From 93fdc1844f1cf1bf808e00507113e24005d9664d Mon Sep 17 00:00:00 2001 From: goyuken Date: Tue, 30 Apr 2013 21:20:11 +0000 Subject: [PATCH] yabause 0.9.12 source release --- yabause/AUTHORS | 36 + yabause/CMakeLists.txt | 35 + yabause/COPYING | 340 + yabause/ChangeLog | 924 + yabause/GOALS | 8 + yabause/INSTALL | 229 + yabause/Makefile.am | 2 + yabause/NEWS | 0 yabause/README | 103 + yabause/README.DC | 85 + yabause/README.LIN | 135 + yabause/README.MAC | 98 + yabause/README.PSP | 802 + yabause/README.QT | 138 + yabause/README.WII | 88 + yabause/README.WIN | 250 + yabause/TODO | 1 + yabause/acinclude.m4 | 34 + yabause/autogen.sh | 2 + yabause/autopackage/default.apspec.in | 57 + yabause/configure.in | 616 + yabause/doc/CMakeLists.txt | 11 + yabause/doc/Doxyfile.in | 1356 ++ yabause/l10n/CMakeLists.txt | 13 + yabause/l10n/Makefile.am | 19 + yabause/l10n/yabause_de.yts | 195 + yabause/l10n/yabause_es.yts | 198 + yabause/l10n/yabause_fr.yts | 276 + yabause/l10n/yabause_it.yts | 198 + yabause/l10n/yabause_lt.yts | 195 + yabause/l10n/yabause_pt.yts | 195 + yabause/l10n/yabause_pt_BR.yts | 274 + yabause/l10n/yabause_sv.yts | 195 + yabause/src/CMakeLists.txt | 492 + yabause/src/Makefile.am | 117 + yabause/src/Makefile.dc | 67 + yabause/src/Makefile.mng | 86 + yabause/src/Makefile.wii | 53 + yabause/src/android/AndroidManifest.xml | 17 + yabause/src/android/CMakeLists.txt | 114 + yabause/src/android/android.cmake | 14 + yabause/src/android/build.xml | 85 + yabause/src/android/jni/Android.mk.in | 14 + yabause/src/android/jni/miniegl.h | 48 + yabause/src/android/jni/sndaudiotrack.c | 221 + yabause/src/android/jni/sndaudiotrack.h | 28 + yabause/src/android/jni/yui.c | 419 + yabause/src/android/project.properties | 11 + .../src/android/res/drawable-hdpi/icon.png | Bin 0 -> 5763 bytes .../src/android/res/drawable-ldpi/icon.png | Bin 0 -> 2540 bytes .../src/android/res/drawable-mdpi/icon.png | Bin 0 -> 3620 bytes yabause/src/android/res/drawable/pad.png | Bin 0 -> 11885 bytes yabause/src/android/res/layout/main.xml | 19 + yabause/src/android/res/menu/emulation.xml | 11 + yabause/src/android/res/values/strings.xml | 4 + .../src/org/yabause/android/Yabause.java | 265 + .../src/org/yabause/android/YabauseView.java | 196 + yabause/src/bios.c | 1869 ++ yabause/src/bios.h | 58 + yabause/src/c68k/CMakeLists.txt | 30 + yabause/src/c68k/Makefile.am | 11 + yabause/src/c68k/c68k.c | 311 + yabause/src/c68k/c68k.h | 213 + yabause/src/c68k/c68k_ini.inc | 0 yabause/src/c68k/c68k_op0.inc | 8330 +++++++ yabause/src/c68k/c68k_op1.inc | 5629 +++++ yabause/src/c68k/c68k_op2.inc | 6254 +++++ yabause/src/c68k/c68k_op3.inc | 6254 +++++ yabause/src/c68k/c68k_op4.inc | 7508 ++++++ yabause/src/c68k/c68k_op5.inc | 8265 +++++++ yabause/src/c68k/c68k_op6.inc | 4454 ++++ yabause/src/c68k/c68k_op7.inc | 2058 ++ yabause/src/c68k/c68k_op8.inc | 6117 +++++ yabause/src/c68k/c68k_op9.inc | 5950 +++++ yabause/src/c68k/c68k_opA.inc | 4118 ++++ yabause/src/c68k/c68k_opB.inc | 5970 +++++ yabause/src/c68k/c68k_opC.inc | 5383 +++++ yabause/src/c68k/c68k_opD.inc | 5950 +++++ yabause/src/c68k/c68k_opE.inc | 6130 +++++ yabause/src/c68k/c68k_opF.inc | 4118 ++++ yabause/src/c68k/c68kexec.c | 335 + yabause/src/c68k/c68kmac.inc | 307 + yabause/src/c68k/configure.in | 18 + yabause/src/c68k/gen68k.c | 3820 +++ yabause/src/c68k/gen68k.h | 68 + yabause/src/c68k/gen68k.inc | 1649 ++ yabause/src/carbon/CMakeLists.txt | 36 + yabause/src/carbon/Makefile.am | 23 + .../carbon/Yabause.app/Contents/Info.plist | 22 + .../Resources/cpustatus.nib/classes.nib | 4 + .../Contents/Resources/cpustatus.nib/info.nib | 67 + .../Resources/cpustatus.nib/objects.xib | 1229 + .../Resources/load_dialog.nib/classes.nib | 8 + .../Resources/load_dialog.nib/info.nib | 18 + .../Resources/load_dialog.nib/objects.xib | 63 + .../Contents/Resources/menu.nib/classes.nib | 8 + .../Contents/Resources/menu.nib/info.nib | 18 + .../Contents/Resources/menu.nib/objects.xib | Bin 0 -> 10392 bytes .../Resources/preferences.nib/classes.nib | 4 + .../Resources/preferences.nib/info.nib | 18 + .../Resources/preferences.nib/objects.xib | 775 + .../Contents/Resources/yabause.icns | Bin 0 -> 45411 bytes yabause/src/carbon/build_dmg.sh | 33 + yabause/src/carbon/cpustatus.c | 206 + yabause/src/carbon/cpustatus.h | 27 + yabause/src/carbon/main.c | 609 + yabause/src/carbon/settings.c | 491 + yabause/src/carbon/settings.h | 49 + yabause/src/cd-freebsd.c | 166 + yabause/src/cd-linux.c | 182 + yabause/src/cd-macosx.c | 227 + yabause/src/cd-netbsd.c | 167 + yabause/src/cd-windows.c | 364 + yabause/src/cdbase.c | 512 + yabause/src/cdbase.h | 51 + yabause/src/cheat.c | 383 + yabause/src/cheat.h | 60 + yabause/src/cocoa/CMake-Info.plist | 32 + yabause/src/cocoa/CMakeLists.txt | 73 + .../src/cocoa/English.lproj/InfoPlist.strings | 1 + yabause/src/cocoa/English.lproj/MainMenu.xib | 6148 +++++ yabause/src/cocoa/PerCocoa.h | 32 + yabause/src/cocoa/PerCocoa.m | 168 + yabause/src/cocoa/Yabause-Info.plist | 34 + yabause/src/cocoa/Yabause.icns | Bin 0 -> 175924 bytes .../cocoa/Yabause.xcodeproj/project.pbxproj | 808 + yabause/src/cocoa/YabauseButtonFormatter.h | 40 + yabause/src/cocoa/YabauseButtonFormatter.m | 112 + yabause/src/cocoa/YabauseController.h | 69 + yabause/src/cocoa/YabauseController.m | 379 + yabause/src/cocoa/YabauseGLView.h | 48 + yabause/src/cocoa/YabauseGLView.m | 217 + yabause/src/cocoa/YabausePrefsController.h | 80 + yabause/src/cocoa/YabausePrefsController.m | 449 + yabause/src/cocoa/main.m | 99 + yabause/src/cocoa/resources/controller.png | Bin 0 -> 96999 bytes yabause/src/cocoa/vidgcd.c | 3274 +++ yabause/src/cocoa/vidgcd.h | 29 + yabause/src/coffelf.c | 388 + yabause/src/coffelf.h | 27 + yabause/src/config.h.in | 3 + yabause/src/core.h | 295 + yabause/src/cs0.c | 1504 ++ yabause/src/cs0.h | 78 + yabause/src/cs1.c | 97 + yabause/src/cs1.h | 34 + yabause/src/cs2.c | 3720 +++ yabause/src/cs2.h | 359 + yabause/src/debug.c | 186 + yabause/src/debug.h | 88 + yabause/src/dreamcast/CMakeLists.txt | 29 + yabause/src/dreamcast/Makefile.am | 1 + yabause/src/dreamcast/cd.s | 234 + yabause/src/dreamcast/dreamcast.cmake | 22 + yabause/src/dreamcast/localtime.c | 106 + yabause/src/dreamcast/localtime.h | 3 + yabause/src/dreamcast/perdc.c | 145 + yabause/src/dreamcast/perdc.h | 29 + yabause/src/dreamcast/sh2rec/sh2exec.s | 100 + yabause/src/dreamcast/sh2rec/sh2rec.c | 2908 +++ yabause/src/dreamcast/sh2rec/sh2rec.h | 50 + yabause/src/dreamcast/sh2rec/sh2rec_htab.c | 157 + yabause/src/dreamcast/sh2rec/sh2rec_htab.h | 35 + yabause/src/dreamcast/sh2rec/sh2rec_mem.c | 222 + yabause/src/dreamcast/sh2rec/sh2rec_mem.h | 43 + yabause/src/dreamcast/viddc.c | 2836 +++ yabause/src/dreamcast/viddc.h | 28 + yabause/src/dreamcast/yui.c | 234 + yabause/src/dx.h | 43 + yabause/src/error.c | 115 + yabause/src/error.h | 39 + yabause/src/font.h | 1301 ++ yabause/src/gameshw/dsplist.txt | 64 + yabause/src/gtk/CMakeLists.txt | 65 + yabause/src/gtk/Makefile.am | 31 + yabause/src/gtk/doc/Makefile.am | 1 + yabause/src/gtk/doc/yabause.1 | 93 + yabause/src/gtk/gtk-compat.c | 33 + yabause/src/gtk/gtk-compat.h | 29 + yabause/src/gtk/gtkglwidget.c | 318 + yabause/src/gtk/gtkglwidget.h | 67 + yabause/src/gtk/main.c | 624 + yabause/src/gtk/menu.c | 218 + yabause/src/gtk/pergtk.c | 92 + yabause/src/gtk/pergtk.h | 29 + yabause/src/gtk/settings.c | 569 + yabause/src/gtk/settings.h | 51 + yabause/src/gtk/yabause.desktop.in | 8 + yabause/src/gtk/yabause.png | Bin 0 -> 2544 bytes yabause/src/gtk/yuicheckbutton.c | 132 + yabause/src/gtk/yuicheckbutton.h | 62 + yabause/src/gtk/yuifileentry.c | 167 + yabause/src/gtk/yuifileentry.h | 68 + yabause/src/gtk/yuiinputentry.c | 244 + yabause/src/gtk/yuiinputentry.h | 59 + yabause/src/gtk/yuim68k.c | 382 + yabause/src/gtk/yuim68k.h | 72 + yabause/src/gtk/yuimem.c | 301 + yabause/src/gtk/yuimem.h | 72 + yabause/src/gtk/yuipage.c | 95 + yabause/src/gtk/yuipage.h | 63 + yabause/src/gtk/yuirange.c | 152 + yabause/src/gtk/yuirange.h | 72 + yabause/src/gtk/yuiresolution.c | 170 + yabause/src/gtk/yuiresolution.h | 63 + yabause/src/gtk/yuiscreenshot.c | 127 + yabause/src/gtk/yuiscreenshot.h | 56 + yabause/src/gtk/yuiscsp.c | 159 + yabause/src/gtk/yuiscsp.h | 71 + yabause/src/gtk/yuiscudsp.c | 425 + yabause/src/gtk/yuiscudsp.h | 72 + yabause/src/gtk/yuish.c | 1102 + yabause/src/gtk/yuish.h | 90 + yabause/src/gtk/yuitransfer.c | 284 + yabause/src/gtk/yuitransfer.h | 70 + yabause/src/gtk/yuivdp1.c | 289 + yabause/src/gtk/yuivdp1.h | 77 + yabause/src/gtk/yuivdp2.c | 318 + yabause/src/gtk/yuivdp2.h | 72 + yabause/src/gtk/yuiviewer.c | 166 + yabause/src/gtk/yuiviewer.h | 59 + yabause/src/gtk/yuiwindow.c | 373 + yabause/src/gtk/yuiwindow.h | 94 + yabause/src/logo.bmp | Bin 0 -> 3126 bytes yabause/src/logo.png | Bin 0 -> 2544 bytes yabause/src/logo.svg | 201 + yabause/src/m68kc68k.c | 190 + yabause/src/m68kc68k.h | 27 + yabause/src/m68kcore.c | 167 + yabause/src/m68kcore.h | 76 + yabause/src/m68kd.c | 1327 ++ yabause/src/m68kd.h | 27 + yabause/src/m68kq68.c | 534 + yabause/src/macjoy.c | 516 + yabause/src/macjoy.h | 101 + yabause/src/memory.c | 1684 ++ yabause/src/memory.h | 403 + yabause/src/movie.c | 465 + yabause/src/movie.h | 86 + yabause/src/netlink.c | 854 + yabause/src/netlink.h | 64 + yabause/src/osdcore.c | 399 + yabause/src/osdcore.h | 82 + yabause/src/perdx.c | 1301 ++ yabause/src/perdx.h | 59 + yabause/src/peripheral.c | 724 + yabause/src/peripheral.h | 221 + yabause/src/perlinuxjoy.c | 136 + yabause/src/perlinuxjoy.h | 33 + yabause/src/permacjoy.c | 245 + yabause/src/permacjoy.h | 29 + yabause/src/persdljoy.c | 279 + yabause/src/persdljoy.h | 32 + yabause/src/profile.c | 188 + yabause/src/profile.h | 65 + yabause/src/psp/Makefile.am | 82 + yabause/src/psp/common.h | 258 + yabause/src/psp/config.c | 842 + yabause/src/psp/config.h | 144 + yabause/src/psp/control.c | 139 + yabause/src/psp/control.h | 79 + yabause/src/psp/display.c | 603 + yabause/src/psp/display.h | 213 + yabause/src/psp/filesel.c | 521 + yabause/src/psp/filesel.h | 114 + yabause/src/psp/font.c | 389 + yabause/src/psp/font.h | 64 + yabause/src/psp/gu.c | 488 + yabause/src/psp/gu.h | 1006 + yabause/src/psp/icache-funcs-2450.patch | 60 + yabause/src/psp/init.c | 281 + yabause/src/psp/init.h | 60 + yabause/src/psp/localtime.c | 157 + yabause/src/psp/localtime.h | 76 + yabause/src/psp/main.c | 265 + yabause/src/psp/me-sectend.c | 5 + yabause/src/psp/me-sectstart.c | 5 + yabause/src/psp/me-test.c | 1468 ++ yabause/src/psp/me-utility.c | 136 + yabause/src/psp/me-utility.h | 105 + yabause/src/psp/me.c | 936 + yabause/src/psp/me.exp | 19 + yabause/src/psp/me.h | 231 + yabause/src/psp/menu.c | 2527 ++ yabause/src/psp/menu.h | 79 + yabause/src/psp/misc.c | 119 + yabause/src/psp/misc.h | 104 + yabause/src/psp/osk.c | 479 + yabause/src/psp/osk.h | 124 + yabause/src/psp/profile.c | 233 + yabause/src/psp/profile.h | 106 + yabause/src/psp/psp-cd.c | 952 + yabause/src/psp/psp-cd.h | 46 + yabause/src/psp/psp-logo.png | Bin 0 -> 7371 bytes yabause/src/psp/psp-m68k.c | 630 + yabause/src/psp/psp-m68k.h | 46 + yabause/src/psp/psp-per.c | 162 + yabause/src/psp/psp-per.h | 46 + yabause/src/psp/psp-sh2.c | 670 + yabause/src/psp/psp-sh2.h | 46 + yabause/src/psp/psp-sound.c | 720 + yabause/src/psp/psp-sound.h | 80 + yabause/src/psp/psp-video-bitmap.c | 314 + yabause/src/psp/psp-video-internal.h | 431 + yabause/src/psp/psp-video-rotate.c | 1688 ++ yabause/src/psp/psp-video-tilemap.c | 551 + yabause/src/psp/psp-video-tweaks.c | 1884 ++ yabause/src/psp/psp-video.c | 2160 ++ yabause/src/psp/psp-video.h | 154 + yabause/src/psp/rtl-internal.h | 712 + yabause/src/psp/rtl-mips.c | 4819 ++++ yabause/src/psp/rtl-mips.h | 341 + yabause/src/psp/rtl.c | 1274 + yabause/src/psp/rtl.h | 314 + yabause/src/psp/rtlexec.c | 747 + yabause/src/psp/rtlinsn.c | 935 + yabause/src/psp/rtlopt.c | 897 + yabause/src/psp/rtlunit.c | 287 + yabause/src/psp/satopt-sh2.c | 4019 ++++ yabause/src/psp/satopt-sh2.h | 61 + yabause/src/psp/sh2-core.i | 4008 ++++ yabause/src/psp/sh2-internal.h | 967 + yabause/src/psp/sh2-interpret.c | 498 + yabause/src/psp/sh2-opcodeinfo.c | 1088 + yabause/src/psp/sh2-optimize.c | 1185 + yabause/src/psp/sh2.c | 7714 +++++++ yabause/src/psp/sh2.h | 738 + yabause/src/psp/sys.c | 331 + yabause/src/psp/sys.h | 108 + yabause/src/psp/texcache.c | 2021 ++ yabause/src/psp/texcache.h | 141 + yabause/src/psp/threads.c | 227 + yabause/src/psp/timing.c | 145 + yabause/src/psp/timing.h | 80 + yabause/src/psp/yui.c | 147 + yabause/src/q68/q68-const.h | 217 + yabause/src/q68/q68-core.c | 2563 +++ yabause/src/q68/q68-disasm.c | 836 + yabause/src/q68/q68-internal.h | 644 + yabause/src/q68/q68-jit-psp.S | 2876 +++ yabause/src/q68/q68-jit-psp.h | 593 + yabause/src/q68/q68-jit-x86.S | 2800 +++ yabause/src/q68/q68-jit-x86.h | 453 + yabause/src/q68/q68-jit.c | 3722 +++ yabause/src/q68/q68-jit.h | 81 + yabause/src/q68/q68.c | 324 + yabause/src/q68/q68.h | 295 + yabause/src/qt/Arguments.cpp | 209 + yabause/src/qt/Arguments.h | 11 + yabause/src/qt/CMakeLists.txt | 215 + yabause/src/qt/CommonDialogs.cpp | 45 + yabause/src/qt/CommonDialogs.h | 38 + yabause/src/qt/Makefile.am | 58 + yabause/src/qt/PerQt.c | 63 + yabause/src/qt/PerQt.h | 30 + yabause/src/qt/QtYabause.cpp | 433 + yabause/src/qt/QtYabause.h | 114 + yabause/src/qt/Settings.cpp | 138 + yabause/src/qt/Settings.h | 57 + yabause/src/qt/VolatileSettings.cpp | 27 + yabause/src/qt/VolatileSettings.h | 23 + yabause/src/qt/YabauseGL.cpp | 53 + yabause/src/qt/YabauseGL.h | 52 + yabause/src/qt/YabauseSoftGL.cpp | 71 + yabause/src/qt/YabauseThread.cpp | 270 + yabause/src/qt/YabauseThread.h | 70 + yabause/src/qt/doc/Makefile.am | 1 + yabause/src/qt/doc/yabause.1 | 98 + yabause/src/qt/main.cpp | 55 + .../src/qt/mkspecs/win32-osx-g++/qmake.conf | 122 + .../qt/mkspecs/win32-osx-g++/qplatformdefs.h | 161 + .../src/qt/mkspecs/win32-x11-g++/qmake.conf | 101 + .../qt/mkspecs/win32-x11-g++/qplatformdefs.h | 161 + .../src/qt/resources/icons/actions/about.png | Bin 0 -> 1171 bytes .../qt/resources/icons/actions/backup_ram.png | Bin 0 -> 1312 bytes .../resources/icons/actions/button_cancel.png | Bin 0 -> 656 bytes .../qt/resources/icons/actions/button_ok.png | Bin 0 -> 764 bytes yabause/src/qt/resources/icons/actions/cd.png | Bin 0 -> 1311 bytes .../resources/icons/actions/cheat_search.png | Bin 0 -> 1399 bytes .../src/qt/resources/icons/actions/cheats.png | Bin 0 -> 1364 bytes .../qt/resources/icons/actions/debug_cpu.png | Bin 0 -> 1288 bytes .../icons/actions/emu-compatibility.png | Bin 0 -> 4217 bytes .../src/qt/resources/icons/actions/fps.png | Bin 0 -> 412 bytes .../icons/actions/frame_skipping.png | Bin 0 -> 1375 bytes .../qt/resources/icons/actions/fullscreen.png | Bin 0 -> 769 bytes .../src/qt/resources/icons/actions/iso.png | Bin 0 -> 598 bytes .../src/qt/resources/icons/actions/layer.png | Bin 0 -> 770 bytes .../qt/resources/icons/actions/load_state.png | Bin 0 -> 744 bytes .../src/qt/resources/icons/actions/log.png | Bin 0 -> 522 bytes .../src/qt/resources/icons/actions/mute.png | Bin 0 -> 4373 bytes .../resources/icons/actions/no_fullscreen.png | Bin 0 -> 771 bytes .../qt/resources/icons/actions/open_file.png | Bin 0 -> 1105 bytes .../src/qt/resources/icons/actions/pause.png | Bin 0 -> 1157 bytes .../src/qt/resources/icons/actions/play.png | Bin 0 -> 1258 bytes .../src/qt/resources/icons/actions/quit.png | Bin 0 -> 1166 bytes .../src/qt/resources/icons/actions/reset.png | Bin 0 -> 1377 bytes .../qt/resources/icons/actions/save_state.png | Bin 0 -> 794 bytes .../qt/resources/icons/actions/screenshot.png | Bin 0 -> 1438 bytes .../qt/resources/icons/actions/settings.png | Bin 0 -> 3768 bytes .../src/qt/resources/icons/actions/sound.png | Bin 0 -> 4306 bytes .../qt/resources/icons/actions/transfert.png | Bin 0 -> 1129 bytes .../src/qt/resources/icons/actions/video.png | Bin 0 -> 1120 bytes yabause/src/qt/resources/icons/controller.png | Bin 0 -> 96999 bytes yabause/src/qt/resources/icons/made.png | Bin 0 -> 10576 bytes yabause/src/qt/resources/icons/yabause.icns | Bin 0 -> 45411 bytes yabause/src/qt/resources/icons/yabause.png | Bin 0 -> 2544 bytes yabause/src/qt/resources/icons/yabause.rc | 1 + yabause/src/qt/resources/resources.qrc | 39 + yabause/src/qt/scripts/yabause_dmg.sh | 70 + yabause/src/qt/scripts/yabause_setup.iss | 54 + yabause/src/qt/translations/yabause_fr.ts | 1165 + yabause/src/qt/translations/yabause_pt.ts | 1171 + yabause/src/qt/ui/UIAbout.cpp | 37 + yabause/src/qt/ui/UIAbout.h | 34 + yabause/src/qt/ui/UIAbout.ui | 117 + yabause/src/qt/ui/UIBackupRam.cpp | 153 + yabause/src/qt/ui/UIBackupRam.h | 41 + yabause/src/qt/ui/UIBackupRam.ui | 190 + yabause/src/qt/ui/UICheatAR.cpp | 29 + yabause/src/qt/ui/UICheatAR.h | 32 + yabause/src/qt/ui/UICheatAR.ui | 110 + yabause/src/qt/ui/UICheatRaw.cpp | 43 + yabause/src/qt/ui/UICheatRaw.h | 39 + yabause/src/qt/ui/UICheatRaw.ui | 145 + yabause/src/qt/ui/UICheatSearch.cpp | 273 + yabause/src/qt/ui/UICheatSearch.h | 54 + yabause/src/qt/ui/UICheatSearch.ui | 237 + yabause/src/qt/ui/UICheats.cpp | 227 + yabause/src/qt/ui/UICheats.h | 50 + yabause/src/qt/ui/UICheats.ui | 178 + yabause/src/qt/ui/UIDebugCPU.cpp | 340 + yabause/src/qt/ui/UIDebugCPU.h | 82 + yabause/src/qt/ui/UIDebugCPU.ui | 502 + yabause/src/qt/ui/UIDebugM68K.cpp | 209 + yabause/src/qt/ui/UIDebugM68K.h | 45 + yabause/src/qt/ui/UIDebugSCSP.cpp | 92 + yabause/src/qt/ui/UIDebugSCSP.h | 39 + yabause/src/qt/ui/UIDebugSCSP.ui | 149 + yabause/src/qt/ui/UIDebugSCUDSP.cpp | 225 + yabause/src/qt/ui/UIDebugSCUDSP.h | 49 + yabause/src/qt/ui/UIDebugSH2.cpp | 257 + yabause/src/qt/ui/UIDebugSH2.h | 46 + yabause/src/qt/ui/UIDebugVDP1.cpp | 110 + yabause/src/qt/ui/UIDebugVDP1.h | 42 + yabause/src/qt/ui/UIDebugVDP1.ui | 172 + yabause/src/qt/ui/UIDebugVDP2.cpp | 66 + yabause/src/qt/ui/UIDebugVDP2.h | 39 + yabause/src/qt/ui/UIDebugVDP2.ui | 216 + yabause/src/qt/ui/UIDebugVDP2Viewer.cpp | 88 + yabause/src/qt/ui/UIDebugVDP2Viewer.h | 39 + yabause/src/qt/ui/UIDebugVDP2Viewer.ui | 86 + yabause/src/qt/ui/UIDisasm.cpp | 169 + yabause/src/qt/ui/UIDisasm.h | 59 + yabause/src/qt/ui/UIHexEditor.cpp | 165 + yabause/src/qt/ui/UIHexEditor.h | 64 + yabause/src/qt/ui/UIHexInput.cpp | 50 + yabause/src/qt/ui/UIHexInput.h | 95 + yabause/src/qt/ui/UIHexInput.ui | 77 + yabause/src/qt/ui/UIMemoryEditor.cpp | 33 + yabause/src/qt/ui/UIMemoryEditor.h | 38 + yabause/src/qt/ui/UIMemoryEditor.ui | 122 + yabause/src/qt/ui/UIMemoryTransfer.cpp | 147 + yabause/src/qt/ui/UIMemoryTransfer.h | 45 + yabause/src/qt/ui/UIMemoryTransfer.ui | 131 + yabause/src/qt/ui/UIPadSetting.cpp | 206 + yabause/src/qt/ui/UIPadSetting.h | 58 + yabause/src/qt/ui/UIPadSetting.ui | 355 + yabause/src/qt/ui/UIPortManager.cpp | 175 + yabause/src/qt/ui/UIPortManager.h | 50 + yabause/src/qt/ui/UIPortManager.ui | 391 + yabause/src/qt/ui/UISettings.cpp | 319 + yabause/src/qt/ui/UISettings.h | 48 + yabause/src/qt/ui/UISettings.ui | 720 + yabause/src/qt/ui/UIWaitInput.cpp | 83 + yabause/src/qt/ui/UIWaitInput.h | 45 + yabause/src/qt/ui/UIWaitInput.ui | 57 + yabause/src/qt/ui/UIYabause.cpp | 650 + yabause/src/qt/ui/UIYabause.h | 145 + yabause/src/qt/ui/UIYabause.ui | 941 + yabause/src/qt/yabause.desktop.in | 8 + yabause/src/qt/yabause.pro.in | 149 + yabause/src/scsp.c | 4449 ++++ yabause/src/scsp.h | 145 + yabause/src/scsp2.c | 3560 +++ yabause/src/scsp2.h | 162 + yabause/src/scu.c | 2545 ++ yabause/src/scu.h | 312 + yabause/src/sh2_dynarec/assem_arm.c | 3343 +++ yabause/src/sh2_dynarec/assem_arm.h | 46 + yabause/src/sh2_dynarec/assem_x64.c | 3510 +++ yabause/src/sh2_dynarec/assem_x64.h | 35 + yabause/src/sh2_dynarec/assem_x86.c | 3330 +++ yabause/src/sh2_dynarec/assem_x86.h | 31 + yabause/src/sh2_dynarec/linkage_arm.s | 982 + yabause/src/sh2_dynarec/linkage_x64.s | 749 + yabause/src/sh2_dynarec/linkage_x86.s | 745 + yabause/src/sh2_dynarec/sh2_dynarec.c | 8442 +++++++ yabause/src/sh2_dynarec/sh2_dynarec.h | 10 + yabause/src/sh2core.c | 1935 ++ yabause/src/sh2core.h | 469 + yabause/src/sh2d.c | 291 + yabause/src/sh2d.h | 27 + yabause/src/sh2idle.c | 668 + yabause/src/sh2idle.h | 26 + yabause/src/sh2int.c | 3057 +++ yabause/src/sh2int.h | 73 + yabause/src/sh2trace.c | 274 + yabause/src/sh2trace.h | 33 + yabause/src/smpc.c | 812 + yabause/src/smpc.h | 103 + yabause/src/sndal.c | 341 + yabause/src/sndal.h | 27 + yabause/src/snddummy.c | 122 + yabause/src/snddx.c | 298 + yabause/src/snddx.h | 25 + yabause/src/sndmac.c | 348 + yabause/src/sndmac.h | 27 + yabause/src/sndsdl.c | 256 + yabause/src/sndsdl.h | 26 + yabause/src/sndwav.c | 202 + yabause/src/thr-dummy.c | 36 + yabause/src/thr-linux.c | 100 + yabause/src/thr-macosx.c | 128 + yabause/src/threads.h | 62 + yabause/src/titan/titan.c | 269 + yabause/src/titan/titan.h | 46 + yabause/src/tools/cdtest.c | 229 + yabause/src/vdp1.c | 1529 ++ yabause/src/vdp1.h | 169 + yabause/src/vdp2.c | 1072 + yabause/src/vdp2.h | 403 + yabause/src/vdp2debug.c | 1673 ++ yabause/src/vdp2debug.h | 33 + yabause/src/vidogl.c | 4313 ++++ yabause/src/vidogl.h | 32 + yabause/src/vidshared.c | 621 + yabause/src/vidshared.h | 962 + yabause/src/vidsoft.c | 3269 +++ yabause/src/vidsoft.h | 33 + yabause/src/wii/CMakeLists.txt | 12 + yabause/src/wii/Makefile.am | 5 + yabause/src/wii/keys.h | 120 + yabause/src/wii/menu.h | 19212 ++++++++++++++++ yabause/src/wii/perwii.c | 326 + yabause/src/wii/perwii.h | 37 + yabause/src/wii/sndwii.c | 213 + yabause/src/wii/sndwii.h | 25 + yabause/src/wii/wii.cmake | 13 + yabause/src/wii/yui.c | 427 + yabause/src/windows/68klib.vcproj | 571 + yabause/src/windows/CMakeLists.txt | 75 + yabause/src/windows/Installer/COPYING.rtf | 127 + .../src/windows/Installer/Installer.vcproj | 186 + .../src/windows/Installer/NSISScript.rules | 19 + .../windows/Installer/Yabause Uninstall.bat | 2 + yabause/src/windows/Installer/installer.bmp | Bin 0 -> 105054 bytes yabause/src/windows/Installer/installer.nsi | 120 + yabause/src/windows/Makefile.am | 21 + yabause/src/windows/aviout.c | 457 + yabause/src/windows/aviout.h | 18 + yabause/src/windows/bup-manager.c | 221 + yabause/src/windows/cd.h | 55 + yabause/src/windows/cheats.c | 834 + yabause/src/windows/cheats.h | 24 + yabause/src/windows/cpudebug/debug-68k.c | 317 + yabause/src/windows/cpudebug/debug-scsp.c | 160 + yabause/src/windows/cpudebug/debug-scu.c | 306 + yabause/src/windows/cpudebug/debug-sh2.c | 485 + yabause/src/windows/cpudebug/debug-smpc.c | 110 + yabause/src/windows/cpudebug/debug-vdp1.c | 194 + yabause/src/windows/cpudebug/debug-vdp2.c | 297 + yabause/src/windows/cpudebug/yuidebug.c | 917 + yabause/src/windows/cpudebug/yuidebug.h | 65 + yabause/src/windows/custctl.c | 90 + yabause/src/windows/custctl.h | 43 + yabause/src/windows/disasm.c | 304 + yabause/src/windows/disasm.h | 35 + yabause/src/windows/fakeddk.h | 53 + yabause/src/windows/gen68k.vcproj | 528 + yabause/src/windows/glext.h | 11486 +++++++++ yabause/src/windows/hexedit.c | 873 + yabause/src/windows/hexedit.h | 40 + yabause/src/windows/hotkey.cpp | 0 yabause/src/windows/hotkey.h | 0 yabause/src/windows/inc.rules | 19 + yabause/src/windows/logo.ico | Bin 0 -> 2238 bytes yabause/src/windows/ram_search.cpp | 1990 ++ yabause/src/windows/ram_search.h | 70 + yabause/src/windows/ramwatch.cpp | 1257 + yabause/src/windows/ramwatch.h | 60 + yabause/src/windows/resource.h | 464 + yabause/src/windows/resource.rc | 1253 + yabause/src/windows/settings/settings-basic.c | 733 + yabause/src/windows/settings/settings-input.c | 616 + yabause/src/windows/settings/settings-log.c | 240 + .../src/windows/settings/settings-netlink.c | 87 + yabause/src/windows/settings/settings-sound.c | 126 + yabause/src/windows/settings/settings-video.c | 266 + yabause/src/windows/settings/settings.c | 221 + yabause/src/windows/settings/settings.h | 115 + yabause/src/windows/windows.cmake | 12 + yabause/src/windows/yabause.sln | 96 + yabause/src/windows/yabause.vcproj | 1133 + yabause/src/windows/yabause_logo.bmp | Bin 0 -> 122934 bytes yabause/src/windows/yui.cpp | 2202 ++ yabause/src/windows/yuiwin.h | 26 + yabause/src/yabause.c | 977 + yabause/src/yabause.h | 104 + yabause/src/ygl.c | 1869 ++ yabause/src/ygl.h | 264 + yabause/src/yglshader.c | 618 + yabause/src/yui.h | 256 + 612 files changed, 362092 insertions(+) create mode 100644 yabause/AUTHORS create mode 100644 yabause/CMakeLists.txt create mode 100644 yabause/COPYING create mode 100644 yabause/ChangeLog create mode 100644 yabause/GOALS create mode 100644 yabause/INSTALL create mode 100644 yabause/Makefile.am create mode 100644 yabause/NEWS create mode 100644 yabause/README create mode 100644 yabause/README.DC create mode 100644 yabause/README.LIN create mode 100644 yabause/README.MAC create mode 100644 yabause/README.PSP create mode 100644 yabause/README.QT create mode 100644 yabause/README.WII create mode 100644 yabause/README.WIN create mode 100644 yabause/TODO create mode 100644 yabause/acinclude.m4 create mode 100644 yabause/autogen.sh create mode 100644 yabause/autopackage/default.apspec.in create mode 100644 yabause/configure.in create mode 100644 yabause/doc/CMakeLists.txt create mode 100644 yabause/doc/Doxyfile.in create mode 100644 yabause/l10n/CMakeLists.txt create mode 100644 yabause/l10n/Makefile.am create mode 100644 yabause/l10n/yabause_de.yts create mode 100644 yabause/l10n/yabause_es.yts create mode 100644 yabause/l10n/yabause_fr.yts create mode 100644 yabause/l10n/yabause_it.yts create mode 100644 yabause/l10n/yabause_lt.yts create mode 100644 yabause/l10n/yabause_pt.yts create mode 100644 yabause/l10n/yabause_pt_BR.yts create mode 100644 yabause/l10n/yabause_sv.yts create mode 100644 yabause/src/CMakeLists.txt create mode 100644 yabause/src/Makefile.am create mode 100644 yabause/src/Makefile.dc create mode 100644 yabause/src/Makefile.mng create mode 100644 yabause/src/Makefile.wii create mode 100644 yabause/src/android/AndroidManifest.xml create mode 100644 yabause/src/android/CMakeLists.txt create mode 100644 yabause/src/android/android.cmake create mode 100644 yabause/src/android/build.xml create mode 100644 yabause/src/android/jni/Android.mk.in create mode 100644 yabause/src/android/jni/miniegl.h create mode 100644 yabause/src/android/jni/sndaudiotrack.c create mode 100644 yabause/src/android/jni/sndaudiotrack.h create mode 100644 yabause/src/android/jni/yui.c create mode 100644 yabause/src/android/project.properties create mode 100644 yabause/src/android/res/drawable-hdpi/icon.png create mode 100644 yabause/src/android/res/drawable-ldpi/icon.png create mode 100644 yabause/src/android/res/drawable-mdpi/icon.png create mode 100644 yabause/src/android/res/drawable/pad.png create mode 100644 yabause/src/android/res/layout/main.xml create mode 100644 yabause/src/android/res/menu/emulation.xml create mode 100644 yabause/src/android/res/values/strings.xml create mode 100644 yabause/src/android/src/org/yabause/android/Yabause.java create mode 100644 yabause/src/android/src/org/yabause/android/YabauseView.java create mode 100644 yabause/src/bios.c create mode 100644 yabause/src/bios.h create mode 100644 yabause/src/c68k/CMakeLists.txt create mode 100644 yabause/src/c68k/Makefile.am create mode 100644 yabause/src/c68k/c68k.c create mode 100644 yabause/src/c68k/c68k.h create mode 100644 yabause/src/c68k/c68k_ini.inc create mode 100644 yabause/src/c68k/c68k_op0.inc create mode 100644 yabause/src/c68k/c68k_op1.inc create mode 100644 yabause/src/c68k/c68k_op2.inc create mode 100644 yabause/src/c68k/c68k_op3.inc create mode 100644 yabause/src/c68k/c68k_op4.inc create mode 100644 yabause/src/c68k/c68k_op5.inc create mode 100644 yabause/src/c68k/c68k_op6.inc create mode 100644 yabause/src/c68k/c68k_op7.inc create mode 100644 yabause/src/c68k/c68k_op8.inc create mode 100644 yabause/src/c68k/c68k_op9.inc create mode 100644 yabause/src/c68k/c68k_opA.inc create mode 100644 yabause/src/c68k/c68k_opB.inc create mode 100644 yabause/src/c68k/c68k_opC.inc create mode 100644 yabause/src/c68k/c68k_opD.inc create mode 100644 yabause/src/c68k/c68k_opE.inc create mode 100644 yabause/src/c68k/c68k_opF.inc create mode 100644 yabause/src/c68k/c68kexec.c create mode 100644 yabause/src/c68k/c68kmac.inc create mode 100644 yabause/src/c68k/configure.in create mode 100644 yabause/src/c68k/gen68k.c create mode 100644 yabause/src/c68k/gen68k.h create mode 100644 yabause/src/c68k/gen68k.inc create mode 100644 yabause/src/carbon/CMakeLists.txt create mode 100644 yabause/src/carbon/Makefile.am create mode 100644 yabause/src/carbon/Yabause.app/Contents/Info.plist create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/classes.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/info.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/objects.xib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/classes.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/info.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/objects.xib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/classes.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/info.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/objects.xib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/classes.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/info.nib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/objects.xib create mode 100644 yabause/src/carbon/Yabause.app/Contents/Resources/yabause.icns create mode 100644 yabause/src/carbon/build_dmg.sh create mode 100644 yabause/src/carbon/cpustatus.c create mode 100644 yabause/src/carbon/cpustatus.h create mode 100644 yabause/src/carbon/main.c create mode 100644 yabause/src/carbon/settings.c create mode 100644 yabause/src/carbon/settings.h create mode 100644 yabause/src/cd-freebsd.c create mode 100644 yabause/src/cd-linux.c create mode 100644 yabause/src/cd-macosx.c create mode 100644 yabause/src/cd-netbsd.c create mode 100644 yabause/src/cd-windows.c create mode 100644 yabause/src/cdbase.c create mode 100644 yabause/src/cdbase.h create mode 100644 yabause/src/cheat.c create mode 100644 yabause/src/cheat.h create mode 100644 yabause/src/cocoa/CMake-Info.plist create mode 100644 yabause/src/cocoa/CMakeLists.txt create mode 100644 yabause/src/cocoa/English.lproj/InfoPlist.strings create mode 100644 yabause/src/cocoa/English.lproj/MainMenu.xib create mode 100644 yabause/src/cocoa/PerCocoa.h create mode 100644 yabause/src/cocoa/PerCocoa.m create mode 100644 yabause/src/cocoa/Yabause-Info.plist create mode 100644 yabause/src/cocoa/Yabause.icns create mode 100644 yabause/src/cocoa/Yabause.xcodeproj/project.pbxproj create mode 100644 yabause/src/cocoa/YabauseButtonFormatter.h create mode 100644 yabause/src/cocoa/YabauseButtonFormatter.m create mode 100644 yabause/src/cocoa/YabauseController.h create mode 100644 yabause/src/cocoa/YabauseController.m create mode 100644 yabause/src/cocoa/YabauseGLView.h create mode 100644 yabause/src/cocoa/YabauseGLView.m create mode 100644 yabause/src/cocoa/YabausePrefsController.h create mode 100644 yabause/src/cocoa/YabausePrefsController.m create mode 100644 yabause/src/cocoa/main.m create mode 100644 yabause/src/cocoa/resources/controller.png create mode 100644 yabause/src/cocoa/vidgcd.c create mode 100644 yabause/src/cocoa/vidgcd.h create mode 100644 yabause/src/coffelf.c create mode 100644 yabause/src/coffelf.h create mode 100644 yabause/src/config.h.in create mode 100644 yabause/src/core.h create mode 100644 yabause/src/cs0.c create mode 100644 yabause/src/cs0.h create mode 100644 yabause/src/cs1.c create mode 100644 yabause/src/cs1.h create mode 100644 yabause/src/cs2.c create mode 100644 yabause/src/cs2.h create mode 100644 yabause/src/debug.c create mode 100644 yabause/src/debug.h create mode 100644 yabause/src/dreamcast/CMakeLists.txt create mode 100644 yabause/src/dreamcast/Makefile.am create mode 100644 yabause/src/dreamcast/cd.s create mode 100644 yabause/src/dreamcast/dreamcast.cmake create mode 100644 yabause/src/dreamcast/localtime.c create mode 100644 yabause/src/dreamcast/localtime.h create mode 100644 yabause/src/dreamcast/perdc.c create mode 100644 yabause/src/dreamcast/perdc.h create mode 100644 yabause/src/dreamcast/sh2rec/sh2exec.s create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec.c create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec.h create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec_htab.c create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec_htab.h create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec_mem.c create mode 100644 yabause/src/dreamcast/sh2rec/sh2rec_mem.h create mode 100644 yabause/src/dreamcast/viddc.c create mode 100644 yabause/src/dreamcast/viddc.h create mode 100644 yabause/src/dreamcast/yui.c create mode 100644 yabause/src/dx.h create mode 100644 yabause/src/error.c create mode 100644 yabause/src/error.h create mode 100644 yabause/src/font.h create mode 100644 yabause/src/gameshw/dsplist.txt create mode 100644 yabause/src/gtk/CMakeLists.txt create mode 100644 yabause/src/gtk/Makefile.am create mode 100644 yabause/src/gtk/doc/Makefile.am create mode 100644 yabause/src/gtk/doc/yabause.1 create mode 100644 yabause/src/gtk/gtk-compat.c create mode 100644 yabause/src/gtk/gtk-compat.h create mode 100644 yabause/src/gtk/gtkglwidget.c create mode 100644 yabause/src/gtk/gtkglwidget.h create mode 100644 yabause/src/gtk/main.c create mode 100644 yabause/src/gtk/menu.c create mode 100644 yabause/src/gtk/pergtk.c create mode 100644 yabause/src/gtk/pergtk.h create mode 100644 yabause/src/gtk/settings.c create mode 100644 yabause/src/gtk/settings.h create mode 100644 yabause/src/gtk/yabause.desktop.in create mode 100644 yabause/src/gtk/yabause.png create mode 100644 yabause/src/gtk/yuicheckbutton.c create mode 100644 yabause/src/gtk/yuicheckbutton.h create mode 100644 yabause/src/gtk/yuifileentry.c create mode 100644 yabause/src/gtk/yuifileentry.h create mode 100644 yabause/src/gtk/yuiinputentry.c create mode 100644 yabause/src/gtk/yuiinputentry.h create mode 100644 yabause/src/gtk/yuim68k.c create mode 100644 yabause/src/gtk/yuim68k.h create mode 100644 yabause/src/gtk/yuimem.c create mode 100644 yabause/src/gtk/yuimem.h create mode 100644 yabause/src/gtk/yuipage.c create mode 100644 yabause/src/gtk/yuipage.h create mode 100644 yabause/src/gtk/yuirange.c create mode 100644 yabause/src/gtk/yuirange.h create mode 100644 yabause/src/gtk/yuiresolution.c create mode 100644 yabause/src/gtk/yuiresolution.h create mode 100644 yabause/src/gtk/yuiscreenshot.c create mode 100644 yabause/src/gtk/yuiscreenshot.h create mode 100644 yabause/src/gtk/yuiscsp.c create mode 100644 yabause/src/gtk/yuiscsp.h create mode 100644 yabause/src/gtk/yuiscudsp.c create mode 100644 yabause/src/gtk/yuiscudsp.h create mode 100644 yabause/src/gtk/yuish.c create mode 100644 yabause/src/gtk/yuish.h create mode 100644 yabause/src/gtk/yuitransfer.c create mode 100644 yabause/src/gtk/yuitransfer.h create mode 100644 yabause/src/gtk/yuivdp1.c create mode 100644 yabause/src/gtk/yuivdp1.h create mode 100644 yabause/src/gtk/yuivdp2.c create mode 100644 yabause/src/gtk/yuivdp2.h create mode 100644 yabause/src/gtk/yuiviewer.c create mode 100644 yabause/src/gtk/yuiviewer.h create mode 100644 yabause/src/gtk/yuiwindow.c create mode 100644 yabause/src/gtk/yuiwindow.h create mode 100644 yabause/src/logo.bmp create mode 100644 yabause/src/logo.png create mode 100644 yabause/src/logo.svg create mode 100644 yabause/src/m68kc68k.c create mode 100644 yabause/src/m68kc68k.h create mode 100644 yabause/src/m68kcore.c create mode 100644 yabause/src/m68kcore.h create mode 100644 yabause/src/m68kd.c create mode 100644 yabause/src/m68kd.h create mode 100644 yabause/src/m68kq68.c create mode 100644 yabause/src/macjoy.c create mode 100644 yabause/src/macjoy.h create mode 100644 yabause/src/memory.c create mode 100644 yabause/src/memory.h create mode 100644 yabause/src/movie.c create mode 100644 yabause/src/movie.h create mode 100644 yabause/src/netlink.c create mode 100644 yabause/src/netlink.h create mode 100644 yabause/src/osdcore.c create mode 100644 yabause/src/osdcore.h create mode 100644 yabause/src/perdx.c create mode 100644 yabause/src/perdx.h create mode 100644 yabause/src/peripheral.c create mode 100644 yabause/src/peripheral.h create mode 100644 yabause/src/perlinuxjoy.c create mode 100644 yabause/src/perlinuxjoy.h create mode 100644 yabause/src/permacjoy.c create mode 100644 yabause/src/permacjoy.h create mode 100644 yabause/src/persdljoy.c create mode 100644 yabause/src/persdljoy.h create mode 100644 yabause/src/profile.c create mode 100644 yabause/src/profile.h create mode 100644 yabause/src/psp/Makefile.am create mode 100644 yabause/src/psp/common.h create mode 100644 yabause/src/psp/config.c create mode 100644 yabause/src/psp/config.h create mode 100644 yabause/src/psp/control.c create mode 100644 yabause/src/psp/control.h create mode 100644 yabause/src/psp/display.c create mode 100644 yabause/src/psp/display.h create mode 100644 yabause/src/psp/filesel.c create mode 100644 yabause/src/psp/filesel.h create mode 100644 yabause/src/psp/font.c create mode 100644 yabause/src/psp/font.h create mode 100644 yabause/src/psp/gu.c create mode 100644 yabause/src/psp/gu.h create mode 100644 yabause/src/psp/icache-funcs-2450.patch create mode 100644 yabause/src/psp/init.c create mode 100644 yabause/src/psp/init.h create mode 100644 yabause/src/psp/localtime.c create mode 100644 yabause/src/psp/localtime.h create mode 100644 yabause/src/psp/main.c create mode 100644 yabause/src/psp/me-sectend.c create mode 100644 yabause/src/psp/me-sectstart.c create mode 100644 yabause/src/psp/me-test.c create mode 100644 yabause/src/psp/me-utility.c create mode 100644 yabause/src/psp/me-utility.h create mode 100644 yabause/src/psp/me.c create mode 100644 yabause/src/psp/me.exp create mode 100644 yabause/src/psp/me.h create mode 100644 yabause/src/psp/menu.c create mode 100644 yabause/src/psp/menu.h create mode 100644 yabause/src/psp/misc.c create mode 100644 yabause/src/psp/misc.h create mode 100644 yabause/src/psp/osk.c create mode 100644 yabause/src/psp/osk.h create mode 100644 yabause/src/psp/profile.c create mode 100644 yabause/src/psp/profile.h create mode 100644 yabause/src/psp/psp-cd.c create mode 100644 yabause/src/psp/psp-cd.h create mode 100644 yabause/src/psp/psp-logo.png create mode 100644 yabause/src/psp/psp-m68k.c create mode 100644 yabause/src/psp/psp-m68k.h create mode 100644 yabause/src/psp/psp-per.c create mode 100644 yabause/src/psp/psp-per.h create mode 100644 yabause/src/psp/psp-sh2.c create mode 100644 yabause/src/psp/psp-sh2.h create mode 100644 yabause/src/psp/psp-sound.c create mode 100644 yabause/src/psp/psp-sound.h create mode 100644 yabause/src/psp/psp-video-bitmap.c create mode 100644 yabause/src/psp/psp-video-internal.h create mode 100644 yabause/src/psp/psp-video-rotate.c create mode 100644 yabause/src/psp/psp-video-tilemap.c create mode 100644 yabause/src/psp/psp-video-tweaks.c create mode 100644 yabause/src/psp/psp-video.c create mode 100644 yabause/src/psp/psp-video.h create mode 100644 yabause/src/psp/rtl-internal.h create mode 100644 yabause/src/psp/rtl-mips.c create mode 100644 yabause/src/psp/rtl-mips.h create mode 100644 yabause/src/psp/rtl.c create mode 100644 yabause/src/psp/rtl.h create mode 100644 yabause/src/psp/rtlexec.c create mode 100644 yabause/src/psp/rtlinsn.c create mode 100644 yabause/src/psp/rtlopt.c create mode 100644 yabause/src/psp/rtlunit.c create mode 100644 yabause/src/psp/satopt-sh2.c create mode 100644 yabause/src/psp/satopt-sh2.h create mode 100644 yabause/src/psp/sh2-core.i create mode 100644 yabause/src/psp/sh2-internal.h create mode 100644 yabause/src/psp/sh2-interpret.c create mode 100644 yabause/src/psp/sh2-opcodeinfo.c create mode 100644 yabause/src/psp/sh2-optimize.c create mode 100644 yabause/src/psp/sh2.c create mode 100644 yabause/src/psp/sh2.h create mode 100644 yabause/src/psp/sys.c create mode 100644 yabause/src/psp/sys.h create mode 100644 yabause/src/psp/texcache.c create mode 100644 yabause/src/psp/texcache.h create mode 100644 yabause/src/psp/threads.c create mode 100644 yabause/src/psp/timing.c create mode 100644 yabause/src/psp/timing.h create mode 100644 yabause/src/psp/yui.c create mode 100644 yabause/src/q68/q68-const.h create mode 100644 yabause/src/q68/q68-core.c create mode 100644 yabause/src/q68/q68-disasm.c create mode 100644 yabause/src/q68/q68-internal.h create mode 100644 yabause/src/q68/q68-jit-psp.S create mode 100644 yabause/src/q68/q68-jit-psp.h create mode 100644 yabause/src/q68/q68-jit-x86.S create mode 100644 yabause/src/q68/q68-jit-x86.h create mode 100644 yabause/src/q68/q68-jit.c create mode 100644 yabause/src/q68/q68-jit.h create mode 100644 yabause/src/q68/q68.c create mode 100644 yabause/src/q68/q68.h create mode 100644 yabause/src/qt/Arguments.cpp create mode 100644 yabause/src/qt/Arguments.h create mode 100644 yabause/src/qt/CMakeLists.txt create mode 100644 yabause/src/qt/CommonDialogs.cpp create mode 100644 yabause/src/qt/CommonDialogs.h create mode 100644 yabause/src/qt/Makefile.am create mode 100644 yabause/src/qt/PerQt.c create mode 100644 yabause/src/qt/PerQt.h create mode 100644 yabause/src/qt/QtYabause.cpp create mode 100644 yabause/src/qt/QtYabause.h create mode 100644 yabause/src/qt/Settings.cpp create mode 100644 yabause/src/qt/Settings.h create mode 100644 yabause/src/qt/VolatileSettings.cpp create mode 100644 yabause/src/qt/VolatileSettings.h create mode 100644 yabause/src/qt/YabauseGL.cpp create mode 100644 yabause/src/qt/YabauseGL.h create mode 100644 yabause/src/qt/YabauseSoftGL.cpp create mode 100644 yabause/src/qt/YabauseThread.cpp create mode 100644 yabause/src/qt/YabauseThread.h create mode 100644 yabause/src/qt/doc/Makefile.am create mode 100644 yabause/src/qt/doc/yabause.1 create mode 100644 yabause/src/qt/main.cpp create mode 100644 yabause/src/qt/mkspecs/win32-osx-g++/qmake.conf create mode 100644 yabause/src/qt/mkspecs/win32-osx-g++/qplatformdefs.h create mode 100644 yabause/src/qt/mkspecs/win32-x11-g++/qmake.conf create mode 100644 yabause/src/qt/mkspecs/win32-x11-g++/qplatformdefs.h create mode 100644 yabause/src/qt/resources/icons/actions/about.png create mode 100644 yabause/src/qt/resources/icons/actions/backup_ram.png create mode 100644 yabause/src/qt/resources/icons/actions/button_cancel.png create mode 100644 yabause/src/qt/resources/icons/actions/button_ok.png create mode 100644 yabause/src/qt/resources/icons/actions/cd.png create mode 100644 yabause/src/qt/resources/icons/actions/cheat_search.png create mode 100644 yabause/src/qt/resources/icons/actions/cheats.png create mode 100644 yabause/src/qt/resources/icons/actions/debug_cpu.png create mode 100644 yabause/src/qt/resources/icons/actions/emu-compatibility.png create mode 100644 yabause/src/qt/resources/icons/actions/fps.png create mode 100644 yabause/src/qt/resources/icons/actions/frame_skipping.png create mode 100644 yabause/src/qt/resources/icons/actions/fullscreen.png create mode 100644 yabause/src/qt/resources/icons/actions/iso.png create mode 100644 yabause/src/qt/resources/icons/actions/layer.png create mode 100644 yabause/src/qt/resources/icons/actions/load_state.png create mode 100644 yabause/src/qt/resources/icons/actions/log.png create mode 100644 yabause/src/qt/resources/icons/actions/mute.png create mode 100644 yabause/src/qt/resources/icons/actions/no_fullscreen.png create mode 100644 yabause/src/qt/resources/icons/actions/open_file.png create mode 100644 yabause/src/qt/resources/icons/actions/pause.png create mode 100644 yabause/src/qt/resources/icons/actions/play.png create mode 100644 yabause/src/qt/resources/icons/actions/quit.png create mode 100644 yabause/src/qt/resources/icons/actions/reset.png create mode 100644 yabause/src/qt/resources/icons/actions/save_state.png create mode 100644 yabause/src/qt/resources/icons/actions/screenshot.png create mode 100644 yabause/src/qt/resources/icons/actions/settings.png create mode 100644 yabause/src/qt/resources/icons/actions/sound.png create mode 100644 yabause/src/qt/resources/icons/actions/transfert.png create mode 100644 yabause/src/qt/resources/icons/actions/video.png create mode 100644 yabause/src/qt/resources/icons/controller.png create mode 100644 yabause/src/qt/resources/icons/made.png create mode 100644 yabause/src/qt/resources/icons/yabause.icns create mode 100644 yabause/src/qt/resources/icons/yabause.png create mode 100644 yabause/src/qt/resources/icons/yabause.rc create mode 100644 yabause/src/qt/resources/resources.qrc create mode 100644 yabause/src/qt/scripts/yabause_dmg.sh create mode 100644 yabause/src/qt/scripts/yabause_setup.iss create mode 100644 yabause/src/qt/translations/yabause_fr.ts create mode 100644 yabause/src/qt/translations/yabause_pt.ts create mode 100644 yabause/src/qt/ui/UIAbout.cpp create mode 100644 yabause/src/qt/ui/UIAbout.h create mode 100644 yabause/src/qt/ui/UIAbout.ui create mode 100644 yabause/src/qt/ui/UIBackupRam.cpp create mode 100644 yabause/src/qt/ui/UIBackupRam.h create mode 100644 yabause/src/qt/ui/UIBackupRam.ui create mode 100644 yabause/src/qt/ui/UICheatAR.cpp create mode 100644 yabause/src/qt/ui/UICheatAR.h create mode 100644 yabause/src/qt/ui/UICheatAR.ui create mode 100644 yabause/src/qt/ui/UICheatRaw.cpp create mode 100644 yabause/src/qt/ui/UICheatRaw.h create mode 100644 yabause/src/qt/ui/UICheatRaw.ui create mode 100644 yabause/src/qt/ui/UICheatSearch.cpp create mode 100644 yabause/src/qt/ui/UICheatSearch.h create mode 100644 yabause/src/qt/ui/UICheatSearch.ui create mode 100644 yabause/src/qt/ui/UICheats.cpp create mode 100644 yabause/src/qt/ui/UICheats.h create mode 100644 yabause/src/qt/ui/UICheats.ui create mode 100644 yabause/src/qt/ui/UIDebugCPU.cpp create mode 100644 yabause/src/qt/ui/UIDebugCPU.h create mode 100644 yabause/src/qt/ui/UIDebugCPU.ui create mode 100644 yabause/src/qt/ui/UIDebugM68K.cpp create mode 100644 yabause/src/qt/ui/UIDebugM68K.h create mode 100644 yabause/src/qt/ui/UIDebugSCSP.cpp create mode 100644 yabause/src/qt/ui/UIDebugSCSP.h create mode 100644 yabause/src/qt/ui/UIDebugSCSP.ui create mode 100644 yabause/src/qt/ui/UIDebugSCUDSP.cpp create mode 100644 yabause/src/qt/ui/UIDebugSCUDSP.h create mode 100644 yabause/src/qt/ui/UIDebugSH2.cpp create mode 100644 yabause/src/qt/ui/UIDebugSH2.h create mode 100644 yabause/src/qt/ui/UIDebugVDP1.cpp create mode 100644 yabause/src/qt/ui/UIDebugVDP1.h create mode 100644 yabause/src/qt/ui/UIDebugVDP1.ui create mode 100644 yabause/src/qt/ui/UIDebugVDP2.cpp create mode 100644 yabause/src/qt/ui/UIDebugVDP2.h create mode 100644 yabause/src/qt/ui/UIDebugVDP2.ui create mode 100644 yabause/src/qt/ui/UIDebugVDP2Viewer.cpp create mode 100644 yabause/src/qt/ui/UIDebugVDP2Viewer.h create mode 100644 yabause/src/qt/ui/UIDebugVDP2Viewer.ui create mode 100644 yabause/src/qt/ui/UIDisasm.cpp create mode 100644 yabause/src/qt/ui/UIDisasm.h create mode 100644 yabause/src/qt/ui/UIHexEditor.cpp create mode 100644 yabause/src/qt/ui/UIHexEditor.h create mode 100644 yabause/src/qt/ui/UIHexInput.cpp create mode 100644 yabause/src/qt/ui/UIHexInput.h create mode 100644 yabause/src/qt/ui/UIHexInput.ui create mode 100644 yabause/src/qt/ui/UIMemoryEditor.cpp create mode 100644 yabause/src/qt/ui/UIMemoryEditor.h create mode 100644 yabause/src/qt/ui/UIMemoryEditor.ui create mode 100644 yabause/src/qt/ui/UIMemoryTransfer.cpp create mode 100644 yabause/src/qt/ui/UIMemoryTransfer.h create mode 100644 yabause/src/qt/ui/UIMemoryTransfer.ui create mode 100644 yabause/src/qt/ui/UIPadSetting.cpp create mode 100644 yabause/src/qt/ui/UIPadSetting.h create mode 100644 yabause/src/qt/ui/UIPadSetting.ui create mode 100644 yabause/src/qt/ui/UIPortManager.cpp create mode 100644 yabause/src/qt/ui/UIPortManager.h create mode 100644 yabause/src/qt/ui/UIPortManager.ui create mode 100644 yabause/src/qt/ui/UISettings.cpp create mode 100644 yabause/src/qt/ui/UISettings.h create mode 100644 yabause/src/qt/ui/UISettings.ui create mode 100644 yabause/src/qt/ui/UIWaitInput.cpp create mode 100644 yabause/src/qt/ui/UIWaitInput.h create mode 100644 yabause/src/qt/ui/UIWaitInput.ui create mode 100644 yabause/src/qt/ui/UIYabause.cpp create mode 100644 yabause/src/qt/ui/UIYabause.h create mode 100644 yabause/src/qt/ui/UIYabause.ui create mode 100644 yabause/src/qt/yabause.desktop.in create mode 100644 yabause/src/qt/yabause.pro.in create mode 100644 yabause/src/scsp.c create mode 100644 yabause/src/scsp.h create mode 100644 yabause/src/scsp2.c create mode 100644 yabause/src/scsp2.h create mode 100644 yabause/src/scu.c create mode 100644 yabause/src/scu.h create mode 100644 yabause/src/sh2_dynarec/assem_arm.c create mode 100644 yabause/src/sh2_dynarec/assem_arm.h create mode 100644 yabause/src/sh2_dynarec/assem_x64.c create mode 100644 yabause/src/sh2_dynarec/assem_x64.h create mode 100644 yabause/src/sh2_dynarec/assem_x86.c create mode 100644 yabause/src/sh2_dynarec/assem_x86.h create mode 100644 yabause/src/sh2_dynarec/linkage_arm.s create mode 100644 yabause/src/sh2_dynarec/linkage_x64.s create mode 100644 yabause/src/sh2_dynarec/linkage_x86.s create mode 100644 yabause/src/sh2_dynarec/sh2_dynarec.c create mode 100644 yabause/src/sh2_dynarec/sh2_dynarec.h create mode 100644 yabause/src/sh2core.c create mode 100644 yabause/src/sh2core.h create mode 100644 yabause/src/sh2d.c create mode 100644 yabause/src/sh2d.h create mode 100644 yabause/src/sh2idle.c create mode 100644 yabause/src/sh2idle.h create mode 100644 yabause/src/sh2int.c create mode 100644 yabause/src/sh2int.h create mode 100644 yabause/src/sh2trace.c create mode 100644 yabause/src/sh2trace.h create mode 100644 yabause/src/smpc.c create mode 100644 yabause/src/smpc.h create mode 100644 yabause/src/sndal.c create mode 100644 yabause/src/sndal.h create mode 100644 yabause/src/snddummy.c create mode 100644 yabause/src/snddx.c create mode 100644 yabause/src/snddx.h create mode 100644 yabause/src/sndmac.c create mode 100644 yabause/src/sndmac.h create mode 100644 yabause/src/sndsdl.c create mode 100644 yabause/src/sndsdl.h create mode 100644 yabause/src/sndwav.c create mode 100644 yabause/src/thr-dummy.c create mode 100644 yabause/src/thr-linux.c create mode 100644 yabause/src/thr-macosx.c create mode 100644 yabause/src/threads.h create mode 100644 yabause/src/titan/titan.c create mode 100644 yabause/src/titan/titan.h create mode 100644 yabause/src/tools/cdtest.c create mode 100644 yabause/src/vdp1.c create mode 100644 yabause/src/vdp1.h create mode 100644 yabause/src/vdp2.c create mode 100644 yabause/src/vdp2.h create mode 100644 yabause/src/vdp2debug.c create mode 100644 yabause/src/vdp2debug.h create mode 100644 yabause/src/vidogl.c create mode 100644 yabause/src/vidogl.h create mode 100644 yabause/src/vidshared.c create mode 100644 yabause/src/vidshared.h create mode 100644 yabause/src/vidsoft.c create mode 100644 yabause/src/vidsoft.h create mode 100644 yabause/src/wii/CMakeLists.txt create mode 100644 yabause/src/wii/Makefile.am create mode 100644 yabause/src/wii/keys.h create mode 100644 yabause/src/wii/menu.h create mode 100644 yabause/src/wii/perwii.c create mode 100644 yabause/src/wii/perwii.h create mode 100644 yabause/src/wii/sndwii.c create mode 100644 yabause/src/wii/sndwii.h create mode 100644 yabause/src/wii/wii.cmake create mode 100644 yabause/src/wii/yui.c create mode 100644 yabause/src/windows/68klib.vcproj create mode 100644 yabause/src/windows/CMakeLists.txt create mode 100644 yabause/src/windows/Installer/COPYING.rtf create mode 100644 yabause/src/windows/Installer/Installer.vcproj create mode 100644 yabause/src/windows/Installer/NSISScript.rules create mode 100644 yabause/src/windows/Installer/Yabause Uninstall.bat create mode 100644 yabause/src/windows/Installer/installer.bmp create mode 100644 yabause/src/windows/Installer/installer.nsi create mode 100644 yabause/src/windows/Makefile.am create mode 100644 yabause/src/windows/aviout.c create mode 100644 yabause/src/windows/aviout.h create mode 100644 yabause/src/windows/bup-manager.c create mode 100644 yabause/src/windows/cd.h create mode 100644 yabause/src/windows/cheats.c create mode 100644 yabause/src/windows/cheats.h create mode 100644 yabause/src/windows/cpudebug/debug-68k.c create mode 100644 yabause/src/windows/cpudebug/debug-scsp.c create mode 100644 yabause/src/windows/cpudebug/debug-scu.c create mode 100644 yabause/src/windows/cpudebug/debug-sh2.c create mode 100644 yabause/src/windows/cpudebug/debug-smpc.c create mode 100644 yabause/src/windows/cpudebug/debug-vdp1.c create mode 100644 yabause/src/windows/cpudebug/debug-vdp2.c create mode 100644 yabause/src/windows/cpudebug/yuidebug.c create mode 100644 yabause/src/windows/cpudebug/yuidebug.h create mode 100644 yabause/src/windows/custctl.c create mode 100644 yabause/src/windows/custctl.h create mode 100644 yabause/src/windows/disasm.c create mode 100644 yabause/src/windows/disasm.h create mode 100644 yabause/src/windows/fakeddk.h create mode 100644 yabause/src/windows/gen68k.vcproj create mode 100644 yabause/src/windows/glext.h create mode 100644 yabause/src/windows/hexedit.c create mode 100644 yabause/src/windows/hexedit.h create mode 100644 yabause/src/windows/hotkey.cpp create mode 100644 yabause/src/windows/hotkey.h create mode 100644 yabause/src/windows/inc.rules create mode 100644 yabause/src/windows/logo.ico create mode 100644 yabause/src/windows/ram_search.cpp create mode 100644 yabause/src/windows/ram_search.h create mode 100644 yabause/src/windows/ramwatch.cpp create mode 100644 yabause/src/windows/ramwatch.h create mode 100644 yabause/src/windows/resource.h create mode 100644 yabause/src/windows/resource.rc create mode 100644 yabause/src/windows/settings/settings-basic.c create mode 100644 yabause/src/windows/settings/settings-input.c create mode 100644 yabause/src/windows/settings/settings-log.c create mode 100644 yabause/src/windows/settings/settings-netlink.c create mode 100644 yabause/src/windows/settings/settings-sound.c create mode 100644 yabause/src/windows/settings/settings-video.c create mode 100644 yabause/src/windows/settings/settings.c create mode 100644 yabause/src/windows/settings/settings.h create mode 100644 yabause/src/windows/windows.cmake create mode 100644 yabause/src/windows/yabause.sln create mode 100644 yabause/src/windows/yabause.vcproj create mode 100644 yabause/src/windows/yabause_logo.bmp create mode 100644 yabause/src/windows/yui.cpp create mode 100644 yabause/src/windows/yuiwin.h create mode 100644 yabause/src/yabause.c create mode 100644 yabause/src/yabause.h create mode 100644 yabause/src/ygl.c create mode 100644 yabause/src/ygl.h create mode 100644 yabause/src/yglshader.c create mode 100644 yabause/src/yui.h diff --git a/yabause/AUTHORS b/yabause/AUTHORS new file mode 100644 index 0000000000..c9230113fc --- /dev/null +++ b/yabause/AUTHORS @@ -0,0 +1,36 @@ +Current team +------------ +Theo Berkau +Guillaume Duhamel +Filipe Azevedo (pasnox) +Lawrence Sebald (BlueCrab) + +Contributors +------------ +Anders Montonen (antime) +Andrew Church +Ari64 +Michele Balistreri +Richard Dolinsky (menace690) +Lucas Newman (Adam Green) +Joost Peters +Jerome Vernet +Takashi Kiyohara +Weston Yager +Ex-Cyber +Romulo Fernandes (nightz) +pa__ +Fabien Coulon +trap15 +devmiyax + +Translators +----------- +Benjamin Siskoo +Felipe2 + +(people we grabbed code from) +------------ +Stephane Dallongeville - c68k and scsp +Patrick Kooman - profiler +Bart Trzynadlowski - sh2 disassembler diff --git a/yabause/CMakeLists.txt b/yabause/CMakeLists.txt new file mode 100644 index 0000000000..c5e3c2d0e6 --- /dev/null +++ b/yabause/CMakeLists.txt @@ -0,0 +1,35 @@ +project(yabause) + +cmake_minimum_required(VERSION 2.8) + +set(YAB_PACKAGE yabause) +set(YAB_VERSION_MAJOR 0) +set(YAB_VERSION_MINOR 9) +set(YAB_VERSION_PATCH 12) +set(YAB_VERSION "${YAB_VERSION_MAJOR}.${YAB_VERSION_MINOR}.${YAB_VERSION_PATCH}") + +set(CPACK_SOURCE_GENERATOR TGZ) +set(CPACK_PACKAGE_VERSION_MAJOR ${YAB_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${YAB_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${YAB_VERSION_PATCH}) +set(CPACK_PACKAGE_VENDOR "Yabause team") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "yabause-${YAB_VERSION}") + +if (APPLE) + set(CPACK_GENERATOR DragNDrop) + set(CPACK_PACKAGE_FILE_NAME yabause-${YAB_VERSION}-mac) +endif () + +if (WIN32) + SET(CPACK_NSIS_INSTALLED_ICON_NAME yabause.exe) + set(CPACK_NSIS_MENU_LINKS yabause.exe;Yabause) + set(CPACK_NSIS_URL_INFO_ABOUT "http://yabause.org") + set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") +endif () + +include(CPack) + +add_subdirectory(doc) +add_subdirectory(l10n) +add_subdirectory(src) diff --git a/yabause/COPYING b/yabause/COPYING new file mode 100644 index 0000000000..41aa952bd8 --- /dev/null +++ b/yabause/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/yabause/ChangeLog b/yabause/ChangeLog new file mode 100644 index 0000000000..14cab09654 --- /dev/null +++ b/yabause/ChangeLog @@ -0,0 +1,924 @@ +0.9.11 -> 0.9.12 + general: + - Fixes to the dynamic recompiler (Ari64) + - Added ARMv5 support to the dynarec (Ari64) + - New OSD system (Guillaume) + - Added "built-in" DDK to make it easier to compile on Windows (Guillaume) + sound: + - Improvements / Fixes in both SCSP and SCSP2 (Cwiiis) + video: + - Major improvements to the OpenGL renderer (Devmiyax) + - Major improvements to the software renderer (Guillaume) + - Some fixes to register emulation (Guillaume) + - Improvements to line drawing functions in the software renderer (Cwiiis) + - Fixed endianess bugs (Guillaume) + cocoa port: + - Added "load image" feature (BlueCrab) + - Fixed the resize bug (BlueCrab) + qt port: + - Added shortcuts to toggle vdp2 layers (Benjamin Siskoo) + - Fixed the "mute sound" feature (Guillaume) + - It's now possible to compile the Qt port in "full software" mode (Guillaume) + - Added an autostart option, disabled by default (Guillaume) + - Now using a XDG compliant location for config file (Guillaume) + - Added a debugger to the Qt port (CyberWarriorX) + - DirectX cores can now be used in Qt port (CyberWarriorX) + - Cheat search function (CyberWarriorX) + - Option to show/hide menu and toolbar (Guillaume) + - Close button in pad settings (guillaume) +0.9.10 -> 0.9.11 + general: + - Now using CMake as the default build system. + For now, autotools based build and "custom" build systems are still supported. + - New Cocoa port + - Added a dynamic recompiling SH2 core for x86 and ARM + - New SCSP implementation + - Major update of the software renderer from the yabause-rr team + - Added an option to allow to execute from the cache + - Improvements to the OpenGL renderer + carbon port: + - Improvements + gtk port: + - Added command line option to enable/disable frame skipping / limiting. + - Added frame skipping/limiting configuration in settings. + - Added --autoload command line option + - Vdp2 layers can be toggled from the Vdp2 debug window + psp port: + - Added support for Media Engine CPU + - Improvements to the PSP port + qt port: + - Added command line support + wii port: + - Merged some stuff from the wiibrew fork, mostly related to SH2 emulation + windows port: + - Fixed the XBox controller driver + - Fixed the "open iso then cancel bug" +0.9.9 -> 0.9.10 + scsp/68k: + - Added code to make SCSP emulation frame-accurate (optional, + enabled with --enable-scsp-frame-accurate configure switch). + - Added a new 68000 emulation core. + software video core: + - Added line scroll emulation. + - Improved user clipping. + - Added some basic vertical scroll emulation, enough to get + Sonic Jam working. + gtk port + - Gtk port is now compiling on Mac OS X. + - Fixed full software screenshots. + - Fixed store function in transfer dialog. + windows port: + - Added 12 player support. + - Fixed a bug that was causing the memory transfer dialog + to register the wrong filename after pressing "Browse". + - Fixed bugs in Goto Address dialog. + - Fixed a bug that was causing the vdp2 viewer dialog + to register the wrong filename after pressing "Browse". + - Added MD saving in SCU DSP debug dialog. + - Added new Ram Watch dialog. + - Added video recording feature. + - Added move recording feature. + general: + - Added Lithuanian translation. + - New sound core using OpenAL. + - Added joystick core for Mac OS X. + - Added a joystick core for Linux. + - Added a PSP port. + - Added support for loading ELF binaries. + - Now using gettimeofday when available for better resolution. + - Fixed save states. +0.9.8 -> 0.9.9 + opengl video core: + - Fixed a bug that was causing some games to + crash (albert odyssey, dragon ball, etc.) + gtk port: + - Automatic detection of current locale. + qt port: + - Added support of DESTDIR and --program-prefix + - Automatic detection of current locale. + - Added support for multiple players. + windows port: + - Fixed crash when going into settings. + - Fixed mouse wheel usage in disassembler. + - Rewrote as an unicode application. + - Fixed the key configuration problem. + - Fixed joypad support. + - Partial fix for mouse wheel and slider problem. + - Fixed fullscreen bug. + general: + - Hooks for renaming .desktop on installation. + - .yts file are now installed. + - Fixed parallel builds. + - SDL peripheral core now handles all connected + joysticks. +0.9.7 -> 0.9.8 + vdp2: + - Fixed a bug in software renderer with rotating + backgrounds. + opengl video core: + - Added gouraud shading and mesh processing. + This is not enabled by default. + software video core: + - Fixed user clipping. + gtk port: + - Added mouse support. + - Configuration dialog now displays key names instead + of values. Also made it so each different configuration + is saved. This broke compatibility with old .ini files. + windows port: + - Support for spaces in filenames when using CLI. + - Added mouse support. + - Added cheat search. + general: + - Added mouse emulation. + - Added de, es, it, pt-br and sv translations + - Support for "out of src" build. + - Fixed compilation for non supported platforms. + For instance this should fix compilation on dragonfly bsd. + Fixed compilation on GNU/Hurd too. +0.9.6 -> 0.9.7 + vdp1: + - Added clipping for line-based drawing to software renderer. + vdp2: + - Toggling a screen is now core independent. + - Added per-character priority to software renderer. + gtk port: + - Fixed fullscreen setting and added a keep ratio one. + - Fixed a bug in the vdp2 debugger that was causing the emu to crash. + - Full software mode can be compiled again. + - Fixed segfault when taking screenshots in full software mode. + - Fixed default value for region. + - Window position is now saved and restored when re-opening the emu. + - Fixed a problem when changing input cores. + qt port: + - Improved compilation process: make (un)install now works. + - Fix crash when configuring input while using translated version. + windows port: + - Changed resolution list generation so it adds the resolution to the list, + regardless of whether it supports 60 hz or not. + - Fixed error when trying to add blank cheat code. + - Fixed all code that allowed the user to choose filename for saving so it + automatically places a default extension. + - Save and Clear buttons are now enabled when loading a cheat file. + - Fixed a bug with AR code adding where it was tracking the wrong edit + window. + - Fixed a bug when adding raw cheat codes. + - Fixed bugs in vdp1 debugger. + - Fixed a bug where saving/loading a save state and an error occured would + cause sound looping. + - Scroll bar in memory editor now works properly when you move the thumb + control. + - Added support for x64 builds in Visual Studio. + general: + - Fixed a bug that was causing older save states to fail. +0.9.5 -> 0.9.6 + sdl joystick core: + - Fixed it... + software video core: + - Improvements and bug fixes. + carbon port: + - Added detection of sdk in the build process. + - Changed the cd core so that the first device found is used. + Users shouldn't have anything to set up when using cd device now. + gtk port: + - Tagged more strings to be translatable. + - Fixed bugs when setting a resolution in settings. + - Fixed controller settings so keys can now be configured even if + emulation is not started. + qt port: + - Removed libsjw core. + wii port: + - Updated to use the last devkitppc. + - Added support for classic controller and for wiimote, disabled + keyboard support. + windows port: + - Added command line support. + general: + - Updated copyright for some files where it was missing or + inaccurate. + - Fixes and improvements to the build process: fixed cross compilation + of Qt port, added Wii port support, found a better way to "trigger" + compilation of gen68k, fixed a bug when calling the sub-configure, + .inc files are now cleaned, added MINI18N variable support, forwarded + distclean rule to qt makefiles, configure now make sure the compiler + is a cross compiler when cross compiling + - Added a workaround for the "limits.h" problem... now distros should + fix their headers... + - Fixed the .desktop files for linux (gtk + qt ports) + - Added translation files for fr and pt in the repository. +0.9.4 -> 0.9.5 + 68k: + - Added 1010 and 1111 line emulator support. + cd block: + - Reworked bin/cue support. Reading should be a lot more accurate + now on tracks 2 and greater. + emulated bios: + - Fixed a bug in BupGetDate year calculation. + - Fixed a bug where interrupt mask wasn't being set correctly when + using emulated bios. + smpc: + - Added support for SMPC NMIREQ command. + - Added reset button emulation. + software video core: + - Improved software renderer: window, line scroll, mosaic are now + available and color offset and scroll screen has been fixed. + gtk port: + - Tagged most of gtk port strings to be translatable. + qt port: + - Added ability to specify address where binaries are loaded when + using command line. + - Other bug fixes. + wii port: + - Added support for bios and game loading from sd card. + - Added sound support. + - Added usb keyboard support. + windows port: + - Added pause emulation function. + - Other bug fixes. + dreamcast port: + - Rewrote all of the Dreamcast CD Interface in hand-optimized + assembly. + - Enabled use of the emulated bios if there is no saturn.bin on + the CD. + general: + - Updated peripheral interface so both ports can now be used and + multiple pads can now be connected to each port. + - Added translation support through mini18n library. +0.9.3 -> 0.9.4 + scsp: + - Fixed a timer bug. + - Fixed a bug with mcire word writes. + - Added wave file output core to available sound cores. + - Fixed a bug in total level attenuation. + - Fixed a bug in EG. + gtk port: + - Redesigned memory dump window. + - Redesigned SH2 debug window. + - Other bug fixes. + qt port: + - Added initial support. It should be pretty much on par with the gtk port. + wii port: + - Added initial support. + windows port: + - Fixed a bug where emulation wasn't paused when save/load state as was + selected from the menu. + - Changed disassembler so it can scroll up and down. + - Tweaked error messages so it doesn't report invalid opcode errors when + running the fast interpreter. + - Added SCSP common control register debug info to SCSP debug dialog + - Other bug fixes. + general: + - Added a few internal tweaks that should yield some performance gains. + - Added support for saving and loading cheats. +0.9.2 -> 0.9.3 + cart: + - Fixed a couple of bugs with Netlink emulation. + cd block: + - Tweaked error handling for cue files so it's more helpful to the user. + scu: + - Fixed a bug in DSP MVI instruction. + - Fixed a bug with DSP Program Ram Address. + - Fixed ALU behaviour on NOP. + - Other bug fixes. + vdp2: + - Fixed a bug where coefficient reading wasn't making sure reads weren't + going out of bounds. + - Tweaked frame-skipping so it only skips if frame time is faster/slower + than a 1/2 a frame. The results are much better now. + - Added general VDP2 debug info functionality. + - Added partial end code support to VDP1 texture debugging. + opengl video core: + - Fixed a bug in 16 BPP sprites where pixels 0x0001-0x7FFF weren't + transparent when transparency was enabled. + gtk port: + - Redesigned the window so each part can now be resized. + - Added a toolbar and removed the buttons. + - The sprite list now displays texture thumbnails. + - Added tooltips to "run" and "pause" buttons. + - Redesigned VDP2 debug window. + windows port: + - Fixed a bug that was causing Yabause to crash when run for the first time. + - Added screen capture. + - Reworked Input dialog so it'll allow for more than one peripheral(in the + future). + - Added a bunch of tools tips for basic and input settings. + - Fixed a bug that was causing wrong VDP1 command information to sometimes + be displayed. + - Other bug fixes. + - Fixed a bug that was causing the wrong breakpoint to be removed from the + breakpoint list. + - Text length is now limited correctly in breakpoint edit text controls. + general: + - Tweaked memory breakpoints so that regardless of whether you're using + cached or cache-through addresses variations of an address, it'll still + detect and break when the memory is accessed. + - Other bug fixes. +0.9.1 -> 0.9.2 + cd block: + - Fixed a bug in periodic timing. Most movies should play correctly now. + - Other bug fixes + scsp: + - Fixed a bug that was causing reversed panning. + - Fixed a bug in SCSP slot debug stats. + sh2: + - Fixed a bug that caused Yabause to crash when fetching instructions from + some areas. + vdp2: + - Fixed undocumented plane size setting when debugging vdp2 + - Special Color Calculation mode added to vdp2 debugging + opengl video core: + - Added the eight missing sprite types in Vdp1ReadPriority. + software video core: + - Fixed a bug where Polygons that used non-RGB values had the wrong + priority. + - Fixed a bug that was causing some scrolling issues. + gtk port: + - CD, sound, and video cores can now be changed without restarting the + emulator. + - Added basic support for save states. + windows port: + - Fixed compilation with MSYS. + - Changed SCSP debug dialog so it allows for individual slot saving. + - Fixed a bug when using goto address in memory editor. + - Fixed a bug where Yabause crashed when joystick was unplugged. + - Added memory search support. + - Fixed cheat dialog. Codes should show up after re-opening it. + general: + - Fixed some bugs where vdp1/vdp2 layers wouldn't be drawn after switching + video cores. + - Fixed a bug when switching between opengl and software video cores. + - Added memory search function. +0.9.0 -> 0.9.1 + scsp: + - Fixed slot pitch LFO. Amplitude LFO is probably more accurate now too. + emulated bios: + - Added Backup RAM manager functions. + opengl video core: + - Fixed a bug with VDP2 2x2 plane size rotation screens. + - Optimized tile mode rotation screens + - Added support for VDP1 polyline. + software video core: + - FPS display now working. + - Added support for VDP2 rotation without coefficient tables. + - Fixed a bug in VDP2 24 BPP bitmap mode. + - Fixed several clipping bugs in Normal and Scaled Sprites. + - Fixed a bug with VDP2 2x2 plane size rotation screens. + - Optimized tile mode rotation screens. + linux port: + - Cursor now disappears after 2 seconds of inactivity in the gtk port. + macos port: + - New high resolution icon. + - Add some missing OS X application property list keys. + windows port: + - Fixed window position bug. + - Other bug fixes. + general: + - Tweaked frame timing code so it's more accurate. + - Re-implemented save states. + - Some internal changes do so that sound, video, and cd cores can be changed + at runtime. +0.8.6 -> 0.9.0 + opengl video core: + - Added support for VDP1 line draw. + - Added support for VDP2 Rotation with coefficient tables. + - Other bug fixes. + software video core: + - Added support for VDP1 frame buffer switching. + - Added support for VDP2 Rotation with coefficient tables. + - Fixed a bug in frame buffer erasing. + - Other bug fixes. + linux port: + - Fixed a bug on 64 bits CPU. + - Hanged the location of the ini file to conform to XDG specification. + - Removed some old useless code. + - Added a "subclass" to GtkDrawingArea so sprite textures and screenshots + can now be saved through a popup menu. + macos port: + - Added fullscreen support. + - Added graphics layer toggling. + windows port: + - Fixed a stack corruption bug in DirectInput code. + - Fixed(hopefully this time) the joystick centering bug. +0.8.5 -> 0.8.6 + 68k: + - Fixed a bug which caused the emulator to crash if 68k execution jumped to + an invalid address. + scsp: + - Fixed a bug where the slot buffer pointers weren't set correctly. + - Added a function for debugging SCSP registers + vdp1: + - MODR returns the correct version number now. + - Fixed a bug that caused Local Coordinates, etc. commands to not get executed + correctly. + software video core: + - Added vdp2 horizontal flip for cell mode. + linux port: + - Improved vdp1 window a bit. + - Updated website url. + - Some cleanups + macos port: + - Added browse buttons for some settings. + - Added universal build support. + - Emulation loop was optimized. + - Fixed bug when "Run" is selected from the menu. + - Audio is now muted when emulator is paused. + - Fixed Backup RAM saving. + - Fixed a bug that was causing filenames to be parsed wrong. + - Other bug fixes and cleanups. + windows port: + - msys compiling is now fixed. + - Windows position is now saved when program exits. + - Fixed sound volume adjustment. Should be more accurate now. + - Fixed centering bug on joysticks. + - Fixed POV hat diagonals. + - Sound is now muted in the about dialog. + - Other bug fixes. + general: + - Added COFF file support. +0.8.0 -> 0.8.5 + scsp: + - Added functions for dumping individual slots to wav files. + scu: + - Fixed SCU execution speed + sh2: + - Added DVDNTL/DVDNTH mirrors + - Added overflow interrupt + vdp1: + - Added function for displaying vdp1 textures for debugging + - Other bug fixes + vdp2: + - Added more RBG0 debug info + 68k: + - Added a core system for m68k and a c68k core interface. + - Added a dummy m68k core based on old yabause code, working enough + to boot the bios. + emulated bios: + - Registers are now reset correctly + - Fixed bug in BiosSetSh2Interrupt + - Added Read/Write Save support + - Added undocumented CD Authentication function + opengl video core: + - RBG0 bug fixes + - Rotation Screen improvements + software video core: + - Added 32 BPP cell draw mode + bsd port: + - Added support for OpenBSD + linux port: + - Fixed the segfault that occured when opening the preferences dialog. + - Added texture display in vdp1 debug dialog + - Other GUI improvements + macos port: + - Added browse button for bios setting + - Other bug fixes + windows port: + - Fixed a bug that was causing sound to not work on some people's computers. + - Added texture display in vdp1 debug dialog + - Added window/full screen resizing + - Added full screen on startup + - Settings changed to use tabs instead of what was previously used + - Other bug fixes + - Logging now is done to a logging window when DEBUG is defined while + compiling. + - Added cheat dialog + - Added memory editor + - Added Visual C++ project file + general: + - Added Cheat support. Largely untested. +0.7.2 -> 0.8.0 + cart: + - Moved Netlink code to its own file: netlink.c + - Improved Netlink AT command handling. Most games using the X-Band software + should work now. + - Fixed a number of bugs that were causing strange behaviour in Netlink + emulation. + - Added Modem states. Online Mode is now handled correctly. + - Added Networking code that allows two Yabause instances to communicate + with each other. Still somewhat buggy. + cd block: + - Fixed an issue where games that didn't specify an index along with the + track when playing cd audio didn't work correctly. + vdp1: + - Code cleanups. + vdp2: + - Code cleanups. + - Adjusted frameskip code so it skips up to a maximum of 9 frames at a time. + direct sound core: + - Fixed a bug that was screwing up the buffer position. Now it's almost + perfect(at the very least there's no clicks or pops anymore). + sdl sound core: + - Fixed a bug that was screwing up the buffer position. Now it's almost + perfect(at the very least there's no clicks or pops anymore). + software video core: + - Polygon drawing improvements + - Removed the silly y-axis clipping technique + - Added a filter for clipping detection + - Added vdp1 "end codes" in textures, but didn't find a game that use it + yet, please report bugs. + - Code Cleanups + - Fixed a potential bug in polygons + - Fixed a bug in polygon clipping + linux port: + - Code cleanups + - Changed a few things in configure script to fix compilation problems when + OpenGL and/or gtkglext were not present. + - Added a log popup window. + - Added a screenshot window on gtk port. + - Fixed Pause/Screenshot bug. + - Removed the "Keep ratio" setting as it can't be done in gtk and + replaced it by a "Fullscreen" setting. + - Added a yabause entry in gnome and KDE application menus + - Changed configure script so it fails on linux if --with-opengl is used + and gtlglext is not installed. + dreamcast port: + - Compiles and runs again. + - Added Normal Sprite support. + - Added Distorted Sprite support. + - Added Scaled Sprite support. + - Added in YabauseGetTicks support. + - Ported VDP2 portion of software renderer. + - Added new cd core. + - Added very simple GUI. + - Other bug fixes. + netbsd port: + - Added patch to get yabause working on netbsd with cd support thanks to + Takashi Kyohara. + windows port: + - Added pad configuration(first pad only). + - Added support for gamepads/joysticks. + - Removed duplicate cd code. + - Added a separate thread for cd access. SPTICDGetStatus is the only + function making use of it for now. + - Fixed fullscreen bug + - Added dialog and settings saving/loading for Netlink stuff(disabled for now). + - Other bug fixes. + general: + - Commited mac port fix by Antime. + - Coordinate Increment Registers are now set to 1 when using the quick load + function. It seems there's at least one game out there that doesn't want + to set it. + - Improved Backup Ram bios emulation functions. The only functions that + still aren't functioning correctly are Bup Write, Bup Read, Bup Verify, + and Bup Set/Get Date. So still no saving, but at least there's no errors + when running games now. +0.7.1 -> 0.7.2 + cart: + - A few Netlink changes(still doesn't work). + cd block: + - CD Block play disc command fixes and improvements. Play Modes now handled correctly. + - Added correct Repeat counter support. + - CD audio data is now written to its own buffer, which is then played by the SCSP. + scsp: + - CD audio data is now played by the SCSP. EFSDL and EFPAN support still needs to be added. + opengl video core: + - glutInit is now called before any other glut function(except for on the windows port). + software video core: + - Added normal sprite flipping(copied from scaled sprites). + - Corrected a bug with 8 bpp color calculation in scaled and distorted sprites. + - Fixed a bug that caused duplicated textures in 8 bpp regular sprites. + - Distorted sprites made safer (won't read outside the texture) + - Fixed transparency for distorted sprites. + - Fixed scaled sprites bug in zoom points modes two points mode and C point + upper than A. + - Fixed a bug that was causing sprite priority problems. + linux port: + - Fixed a gtk warning. + - Added Joystick support. + - Added a test in configuration dialog so input tab is displayed only when + emulation is initialized. + - Added NTSC/PAL setting + - Input settings are now disabled when PERCore isn't initialized. + - Added a sound setting tab. + macos port: + - Added code to handle settings (everything should be working now, except + the "browse" buttons). + - Controls are now using the new Per* functions. + - Fixed some bugs in combo boxes. + windows port: + - EC Compatibility list link added to help menu. + - Fixed an issue where default values weren't set correctly when yabause.ini + wasn't present. + - Fixed an issue where Yabause would go into an endless loop if bios path + was incorrect. + - DirectX error messages now return more info when there's an error. + - Fixed an issue where people without hardware sound buffers on their + sound card would have problems trying to run with sound. + - Fixed some inaccurate information in the README.WIN file. + - Fixed cut-off text in Memory Transfer dialog. + - All dialog windows can now be closed using the X icon in the top-right + corner. + general: + - Fixed an issue where in certain cases Yabause would crash when sound + settings were altered. + - Some useless files were removed. + - Moved SDL detection in "global" part of configure script as it may be used + by all ports. + - Fixed a weird issue where a few functions were trying to return a value + when they obviously can't(How come GNU C compilers won't detect this?). + - Fixed a number of things that were causing compilation issues in VC++(VC++ + still doesn't completely compile Yabause yet). + - Configure now checks if c99 variadic macros are available. +0.7.0 -> 0.7.1 + opengl video core: + - Added polygons that use a palette. + software video core: + - Added scaled sprites with clipping and flipping. + - Full screen mode now working correctly. + - Added correct support for vdp2 resolutions other than 320x224. + - Fixed compilation issue on big endian systems. + - Added function to software renderer for fetching width/height of the display buffer + - Memory leak when clearing VDP1 frame buffer fixed. + linux port: + - Added autostart and fullscreen command line switches. + - Fixed a bug that was causing the emulator to sometimes start in using PAL + timing. + - Added an option to choose the peripheral interface at configure time. + - Started to move the gtk controls code into a proper peripheral core. + - Added code so software renderer can be used without OpenGL. + - Added --without-opengl switch to configure script to prevent OpenGL + detection. + - Resizing is now enabled when using software renderer and opengl. + macos port: + - Fixed a bug that was causing the emulator to sometimes start in using PAL + timing. + - Some fixes to carbon interface (preferences should works now). + windows port: + - Fixed a bug that was causing the emulator to sometimes start in using PAL + timing. + - Added shortcuts to the Yabause website, forum, donation page, and the + submit bug page to the main menu. + - Added About dialog. + general: + - Fixed a potential issue when enabling/disabling auto frameskipping. +0.6.0 -> 0.7.0 + cart: + - Added Action Replay flash emulation. + cd block: + - Fixed Read Directory/Change Directory commands. This fixes Duke Nukem 3D + and a few others that have Netlink support. + - Audio data is no longer stored when read by the cd block. This fixes + Guardian Heroes. + - other bug fixes. + scsp: + - Added function that allows developers to get easy to read information on + the requested scsp sound slot. + - Fixed a bug where the phase wasn't getting updated if DISDL was set to 0. + This fixes Falcom Classics, Nadesico, and many other games using ADX. + - Fixed a bug that was causing OCT with a setting of 0x8 to play at the + wrong octave. + - Fixed a bug that was causing King of Fighters 95(and possibly others) to + go into an endless loop. + scu: + - Improved SCU interrupt handling. + sh2: + - Fixed a bug in exts.b opcode. + - Corrected some bugs in sh2idle + - SCI emulation improvements + smpc: + - Added proper DOTSEL reporting. + - Region settings are now properly preserved. + - Changed region autodetection so it defaults to the japanese region if + it can't autodetect. + 68k: + - Fixed a few bugs. + vdp2: + - Debug info bug fixes + - Implemented one mode of external HV latching. This fixes King of Fighters + 95. + - External latch and sync flags are now cleared on TVSTAT reading. + - Added speed throttle(basically skips 6 frame draws). + - Added long writes for VCSTA, LSTA0, and LSTA1 registers. + software video core: + - Rewrote it so it's no longer dependent on SDL. + - Added NBG2/NBG3 support. + - Added tile mode rendering. + - Added frame buffer emulation. + - Added normal sprite drawing. + - Changed Normal Sprite drawing so that Scaled Sprite and Distorted Sprite + functions can use it too. + - Added some support for Scaled/Distorted Sprites. + - Added VDP1 Polyline and Line drawing to Software renderer. + - Fixed a bunch of bugs. + opengl video core: + - Fixed a few issues with OpenGL initialization. + - Fixed a window/fullscreen bug. + - Added a smart Line Scroll/Vertical Cell Scroll interpreter. + - Changed Color Offset so it uses the same method as the Software renderer. + - Fixed Rotation Table reading. + - Fixed a bug in VIDOGLVdp1PolylineDraw where coordinate reads were writing + to invalid areas. + linux port: + - Removed some useless debug messages and fixed the "quit" menu entry. + - Added vdp1 debug dialog in new gtk interface. + - Added dialog for sh2, video core switching. + - Added reset menu entry. + - Added about dialog. + - Added MSH2 and SSH2 debug dialogs to the GTK interface. + - Added transfer dialog to the new gtk ui. + - Added empty Memory Dump dialog. + - Added the dialog box for scsp + - Added shortcut F7 for command Step + - Added support for memory breakpoints in sh2 debug dialog + - Sound is now muted when emulation is paused (in gtk interface). + - The window data is now saved while emulation is paused. + - Screenshot function added. + macos port: + - Added carbon interface + - Can now build .dmg image from .app directory + - Other improvements + windows port: + - Added SCSP Debug Dialog. + - Added Reset option to menu. + - Now uses DirectInput and DirectSound instead of SDL. + - Added dialog for video, sound and input core switching. + - Fixed window/fullscreen switching. + - Added support for memory breakpoints in sh2 debug dialog. + - Sound volume can now be adjusted in the settings dialog. + - Sound is now muted when dialog window has focus. + - Auto frameskip can be be enabled via video settings menu. + - Other bug fixes. + general: + - Better handling of NULL string when opening a file + - Fixed a few memory leaks + - ISO support fixes + - PAL support added + - Fixed v-blank timing + - Added auto frameskipping(still not working correctly) + - Improved sound buffering + - Fixed handling of invalid SH2 opcodes + - Dummy sound core bug fixes + - Fixed some warnings + - Added experimental bios emulation + - Added memory breakpoints + - Added a function to the sound cores for setting the volume. +0.5.0 -> 0.6.0 + cart: + - accesses to Netlink addresses when Netlink was not present was causing + errors, this has been fixed. + scu: + - fixed DSP debugging. + - fixed a Timer 0 bug. Fixes Shining the Holy Ark. + sh2: + - added SH2 idle detection. Speed should be significantly faster. + - separated original core(now the "debug interpreter core") from the core + with idle detection. + - sh2 cores are now selectable. + 68k: + - added 68k disassembler. + - fixed some warnings. + vdp1: + - added debugging functions. + - fixed bug that was causing garbage graphics in Albert Odyssey. + - fixed bug that was causing graphics in Legend of Oasis to not get drawn. + - other bug fixes. + vdp2: + - fixed a few priority bugs. + - added initial special priority emulation. + general: + - added fullscreen and fixed resize in Windows. Still needs quite a bit of + work. + - changed event handling a bit. Gained quite a bit of speed from it. + - fixed some Mac OS X port bugs. + - fixed some Dreamcast port bugs. + - added proper Linux gui. + - Fixed YGL initialization. + - fixed some Windows ports bugs + - other bug fixes. +0.0.7 -> 0.5.0 + cd block: + - bug fixes. + - improved timing. + cart: + - added Action Replay emulation. + - added 8/32 Mbit dram emulation. + - added 4/8/32 Mbit backup ram emulation. + - added 16 Mbit rom emulation. + - added very early Netlink emulation. + scsp: + - added Stephane Dallongeville's SCSP's core. Thanks again Stef! + - fixed a couple of bugs that were causing movies to lock up. + 68k: + - added Stephane Dallongeville's 68k's core. Thanks again Stef! + - fixed a few endian related bugs. + - added debugger(still need disassembler though). + scu: + - added dsp emulation. + - added dsp debugger. + - added indirect dma emulation. + - added timer0 emulation. + - bug fixes. + smpc: + - added very basic SH2 direct peripheral mode. + - added clock change commands. + - added slave sh2 off/on commands. + - fixed intback command timing. + - bug fixes. + sh2: + - added FRT, WDT, and partial UBC emulation. + - fixed a couple of opcode bugs. + - re-added debugger. + - added some early dynarec code. + vdp1: + - added sprite priorities. + - added color offset. + - bug fixes. + vdp2: + - added basic rbg0 emulation(no rotation, etc.). + - added backscreen emulation. + - added caching. + - added color offset. + - added video mode changing. + - added screen scrolling. + - fix caching bug. + - other bug fixes. + - added early software video rendering. It's still pretty much unuseable at this point. +general: + - added binary execution. + - rewrote entire code in C for portability and speed. + - fixed a number of configure bugs, added a few more command-line options. + - fixed code so it's 64-bit friendly. + - added iso and bin/cue files support. + - changed several parts of yabause to allow for multiple implementations of video, sound, and peripheral code.. + - added save states(currently broken unfortunately). + +0.0.6 -> 0.0.7 + cd block: + - added cd interface for porters. + - whole bunch of cd commands were added. Most games should now + start to boot. + - added region auto-detection. + mpeg card: + - added basic emulation. + - added mpeg rom loading support. + scsp: + - bug fixes. + scu: + - bug fixes. + smpc: + - bug fixes. + superh: + - fixed dma. + - lots of other bugfixes. + - opcode optimizations. + vdp1: + - added sprite caching. + - added scaled sprites. + - added sprite color modes 0, 1, 2, 3, 4. + - macosx color bug fixed. + - bug fixes. + vdp2: + - macosx color bug fixed. + - bug fixes. + general: + - added fps counter. + - switched to OpenGL, removed SDL_gfx. + - yui interface added. Now each port should be able to provide + a nice custom ui. + - threads removed, program should be more stable now. + - added save ram loading ability. + +0.0.5 -> 0.0.6 + scu + - added direct dma. + superh + - added division unit. + - fixed endianess issue. + vdp2 + - added NBG3. + - fixed color bug. + +0.0.4 -> 0.0.5 + vdp2: + - lot of work, the vdp2 is now capable of + displaying the set-clock screen of the + bios. + monitor/debugger: + - added memory dump possibility. + +0.0.2 -> 0.0.4 + monitor/debugger: + - added debugging possibility, can now pause/resume emulation + and execute instructions step by step; + - opcodes are disassembled interactively. + general: + - early emulations of different cpu/onchip modules: scu, vdp1 + and dmac; + - translate most of the code from french to english; + - added synchronisation between processors; + - yabause is now using SDL, remove all fork/ipc code and use + SDL_Thread instead. + +0.0.1 -> 0.0.2 + sh2: + - "mull" is now decoded; + - changed the way the opcodes are decoded, now using a table with pointers + to function, should be faster. + intc: + - now tests if the interrupt level is correct before accepting one. + vdp2: + - early emulation, just throws an interrupt every half-frame. + general: + - vdp1 and vdp2 are now synchronized with the master sh; + - fixed some memory bug, all the shared memory allocated is de-allocated; + - now using configure/make, should be more portable; + - modified things to be more c++ and less linux/c. diff --git a/yabause/GOALS b/yabause/GOALS new file mode 100644 index 0000000000..e2d43585ac --- /dev/null +++ b/yabause/GOALS @@ -0,0 +1,8 @@ +Goals for a 1.0.0 release +-------------------------- +-Full "stock" saturn emulation. At the very least it should be "feature complete"(most major bugs should be fixed at this point too) +-Any external carts or peripheral do not need to be fully emulated at this point +-Switchable SH2 Dynarec/Interpreter cores +-Full debugging support +-Speed should be high at this point(It's too tough ironing out those bugs with Yabause at its current state) +-Save States diff --git a/yabause/INSTALL b/yabause/INSTALL new file mode 100644 index 0000000000..a4b34144dc --- /dev/null +++ b/yabause/INSTALL @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/yabause/Makefile.am b/yabause/Makefile.am new file mode 100644 index 0000000000..f6f08ec7c3 --- /dev/null +++ b/yabause/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src l10n +EXTRA_DIST = README.DC README.LIN README.MAC README.WIN GOALS README.QT README.WII README.PSP diff --git a/yabause/NEWS b/yabause/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/yabause/README b/yabause/README new file mode 100644 index 0000000000..7a7d0c9f98 --- /dev/null +++ b/yabause/README @@ -0,0 +1,103 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + ____________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Introduction.............................................20 +2) Staff/Thanks.............................................39 +3) Contact information......................................75 +4) Disclaimer...............................................86 + + +1 Introduction________________________________________________ + +Yabause is a Sega Saturn emulator under GNU GPL. + +Yabause can boot Saturn bios and games, some of those games +are playable. + +For installation/how to use information, check the ports +specific README files: + + * README.DC for the dreamcast port + * README.LIN for the linux port + * README.MAC for the mac port + * README.PSP for the PSP port + * README.QT for the crossplatform Qt 4 port + * README.WII for the WII port + * README.WIN for the windows port + + +2 Staff/Thanks________________________________________________ + +See the AUTHORS file for team members list. + +Thanks to: + + * Runik (author of Saturnin), for all his help, especially + on the vdp2. + http://saturnin.consollection.com + + * Fabien Autrel (author of Satourne), + for letting me browse his sources. + http://satourne.consollection.com + + * Chuck Mason (author of semu), + for releasing semu sources and for his handy debugger. + + * Romain Vallet + for winning the "Find a name for my emu" contest, for + his contribution to this README and for this nice web + site he did. + http://romanito.free.fr/ + + * Stefano and all of segadev for their help. + + * Josquin Debaz + for writing the man page. + + * Bart Trzynadlowski + for his disassembler. + + * Charles MacDonald + for his saturn sample programs, docs, and being a major + source for saturn information. + + +3 Contact information_________________________________________ + +E-mail: guillaume@yabause.org +Web: http://yabause.org +IRC: irc://irc.freenode.net/yabause + +Please don't ask for roms, bios files or any other copyrighted +stuff. Please use the forum when you have any questions if +possible. + + +4 Disclaimer__________________________________________________ + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This program is distributed in the hope that it will be +useful,but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301 USA + +See the GNU General Public License details in COPYING. diff --git a/yabause/README.DC b/yabause/README.DC new file mode 100644 index 0000000000..87e238d9f8 --- /dev/null +++ b/yabause/README.DC @@ -0,0 +1,85 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + ____________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Introduction.............................................20 +2) Compiling instructions...................................31 +2) How to use Yabause.......................................48 +4) Final Thoughts...........................................71 + + +1 Introduction________________________________________________ + +This file documents the Dreamcast version only, for general +information check the README file. Note, that if you are just +a casual user, and not a developer, you probably won't have +to worry about the Compiling instructions below. Pick up with +the section "How to use Yabause" to learn how to make a CD +image you can burn to use Yabause (or use a program like +Selfboot). + + +2 Compiling instructions______________________________________ + +Yabause is written in C using KallistiOS. So, in order to +compile it, you need a working sh-elf targetted C compiler, +such as gcc and a working KallistiOS environment: + + * http://gamedev.allusion.net + +Once KallistiOS is set up, you should be ready to build +Yabause. + +Uncompress the Yabause source archive, move to the newly +created directory, type "cd src", then "make -f Makefile.dc", +it will generate one binary: "yabause.bin" in the "src" +directory. + + +3 How to use Yabause__________________________________________ + +Before using Yabause, you need to build a CD with a few files +in the correct places. In order to build a CD with the +compiled binary, you must first scramble the binary. This can +be done with the scramble utility which can be obtained from +http://mc.pp.se/dc/files/scramble.c . This will have to be +compiled with your native compiler. Once you run the scramble +program on the yabause.bin file created in section 2 above, +you will have a binary file suitable for booting on a real +Dreamcast. + +Once you have your scrambled binary, you may choose to include +a Sega Saturn BIOS image on your disc as well. As of 0.9.5, +this is not a requirement for the Dreamcast port, but if you +choose to use an actual BIOS image, it must be put on the +root of the CD and named "saturn.bin". + +From this point, follow your favorite guide for how to build +a selfbooting CD out of plain files. I use the one available +at http://mc.pp.se/dc/cdr.html . + + +4 Final Thoughts______________________________________________ + +The Dreamcast port of Yabause is quite slow. I have done very +little in the way of optimizing any of the core of Yabause +toward the Dreamcast (there is quite a bit that could be done +when time allows). Do not expect the Dreamcast port of +Yabause to run your favorite Sega Saturn games at any sort of +playable speed for now (or the near future). As Yabause +matures, and I have more time to work on it, hopefully speed +will improve. For now, think of it as a fancy tech demo. + +Note that in 0.9.5, a small bit of assembly has appeared in +the Dreamcast port. Hopefully as time goes on more of the +Dreamcast specific code will be rewritten in assembly for at +least some small speed increase. diff --git a/yabause/README.LIN b/yabause/README.LIN new file mode 100644 index 0000000000..0b65079552 --- /dev/null +++ b/yabause/README.LIN @@ -0,0 +1,135 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + ____________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Introduction.............................................19 +2) Compiling instructions...................................25 +3) How to use Yabause......................................102 + + +1 Introduction________________________________________________ + +This file documents the gtk version only, for general +information check the README file. + + +2 Compiling instructions______________________________________ + +The Gtk+ port of Yabause is written in C and depends on the +Gtk+ library (thus the name). The recommended setup of the +Gtk+ port is to link it against OpenGL and gtkglext libraries, +but this is not mandatory; see "Full Software mode" for +further instructions. + +Yabause currently provides two build system, a legacy build +process using the autotools and a newer build process using +CMake. + + +2.1 Recommended setup_________________________________________ + +You need a working C compiler, such as gcc and the above +libraries runtime and development packages: + + * http://www.gtk.org + + * http://gtkglext.sourceforge.net + + * OpenGL should be included with your compiler, if it isn't, + check on your distribution's website for links. + + * http://www.cmake.org, you'll need a CMake version >= 2.8 + +With those libraries, you'll get a working Yabause, but with +some restrictions: + + * No sound + + * No translations + + * Depending on your OS, keyboard input only + +You may want to install some optional dependencies for a +better experience. + + +2.2 Optional libraries________________________________________ + +Yabause can use a number of optional libraries: + + * SDL: provides sound and joystick support + http://www.libsdl.org/ + + * OpenAL: provides sound support + + * mini18n: provides translation support + + +2.3 Compiling_________________________________________________ + +For the build process, we recommend using two directories: one +for the Yabause sources (SOURCES) and one for the build (BUILD) + +Uncompress the Yabause source archive into the $SOURCES dir +and create the $BUILD directory. + +Move to the build directory and type "cmake $SOURCES" then +"make" it will generate one program: "yabause" in the "src/gtk" +directory. + +You can even type "make install" to install that program on +your system (in /usr/local/ by default), but we don't support +desinstalling it. + + +2.4 Full Software mode________________________________________ + +The Gtk+ supports building without OpenGL support. + +cmake -DYAB_WANT_OPENGL=NO $SOURCES +make + + +3 How to use Yabause__________________________________________ + +Before using Yabause, you need to configure a few things in +the Preferences dialog (Yabause>Preferences). + + +3.1 Configuration_____________________________________________ + +First, set the BIOS path. +Yabause can run some games without a BIOS, but most of them +needs it. If you want to use the emulated BIOS, just let the +BIOS entry blank. + +Next, set the cdrom device. +It can be a cd device, an iso or a cue file. Set the cd type +accordingly. + +The last thing you have to configure is the keys. + +Once eveything is set, you can start emulation with the +"Yabause>run" entry. + + +3.2 Command line arguments____________________________________ + +-b (or --bios=) + Specify bios file. +-c (or --cdrom=) + Specify cd device. You can know which file is used as cd + device by looking in /etc/fstab. It is commonly something + like /dev/hdc or /dev/hdd for IDE devices and /dev/scd0 + for SCSI devices. +-i (or --iso=) + Specify iso file. diff --git a/yabause/README.MAC b/yabause/README.MAC new file mode 100644 index 0000000000..bd8b2f25b8 --- /dev/null +++ b/yabause/README.MAC @@ -0,0 +1,98 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + ____________________________________ + Copyright (c) 2002-2012 Yabause team + + +1) Introduction.............................................20 +2) Compiling instructions...................................26 +3) How to use Yabause.......................................46 +4) Known Issues.............................................92 + + +1 Introduction________________________________________________ + +This file documents the mac version only, for general +information check the README file. + + +2 Compiling instructions______________________________________ + +Yabause is written in C and Objective C using the Cocoa, +IOKit, OpenGL, and CoreAudio frameworks. All of these +frameworks should be installed by default on your Mac OS X +system. You must have the Mac OS X 10.6 SDK installed by the +Xcode installer in order to build Yabause. In addition, you +will need at least Xcode 3.2. + +Once you have Xcode installed, you should be ready to build +Yabause. + +Uncompress the Yabause source archive, and open the +Yabause.xcodeproj in the src/cocoa directory. From there it +should be as easy as hitting the Build or Build and Run +button in Xcode. This should generate a Yabause.app file +that can be run just like any other application bundle on +Mac OS X. + + +3 How to use Yabause__________________________________________ + +Before using Yabause, you need to configure a few things in +the Preferences dialog (Yabause > Preferences). + + +3.1 Configuration_____________________________________________ + +First, set the BIOS path. +Yabause can run some games without a BIOS, but many of them +need it. If you want to use the emulated BIOS, select the +checkbox for that. + +Next, set up the video and sound cores. For the video core, +you have 4 options: +1. OpenGL Hardware Video Core - Potentially the fastest video + core choice, at least with a discrete video card. However, + it is also the least accurate. +2. Software Video Core - The most accurate video core you can + use, but also the slowest. +3. Grand Central Dispatch Software Core - A multithreaded + version of the Software Video Core. On a multi-core system + this should be significantly faster than the Software core + with a similar accuracy level. +4. Disable Video - Does exactly what it sounds like. + +For the sound core, you only have two options: +1. Core Audio Sound Core - The default sound core. Select + this one if you want sound. +2. Disable Sound - Does exactly what it sounds like. + +Next, set up keys for input. Go to the Input tab, and +configure each button (at least on Controller 1). For the +moment, this is limited to keyboard input only. + +There are other options you can configure as well in here, +including BRAM (for saving), a MPEG ROM (for games that use +the VideoCD/MPEG card), and a cartridge for the cartridge +port on the Saturn. + +Once eveything is set, you can start emulation with the +"File > Run BIOS", "File > Run CDROM" or "File > Run Image" +menu options. Don't use the Run BIOS entry if you're using +BIOS emulation. + + +4 Known Issues________________________________________________ + +When running in GDB, you should not use fullscreen mode. If +Yabause crashes while running under GDB in fullscreen mode, +you will probably get stuck with no way to exit. This should +only affect developers and shouldn't ever be an issue for +normal users. diff --git a/yabause/README.PSP b/yabause/README.PSP new file mode 100644 index 0000000000..0c85aaf21f --- /dev/null +++ b/yabause/README.PSP @@ -0,0 +1,802 @@ +PSP-Specific Yabause Documentation +================================== + +Important notice +---------------- +PSP support for Yabause is experimental; please be aware that some things +may not work well (or at all). + +Unlike Yabause 0.9.10, this version of Yabause now works on all PSPs, +including the original PSP-1000 ("Phat"). However, some games may run +more slowly on PSP Phats because of the limited amount of memory available +for caching dynamically-translated program code. + + +Installing from a binary distribution +------------------------------------- +The yabause-X.Y.Z.zip archive contains a "PSP" directory (folder); copy +this into the root directory of your Memory Stick. (On Windows, for +example, your Memory Stick might show up as the drive F: -- in this case, +drag the "PSP" folder from the ZIP archive onto the "F:" drive icon in +Windows Explorer.) + +The "PSP" directory contains a directory called "GAME", which in turn +contains a directory called "YABAUSE". Inside the "YABAUSE" directory are +two files named "EBOOT.PBP" and "ME.PRX"; these are the program files used +by Yabause, like .EXE and .DLL files on Windows. You'll also need to copy +your CD image and other data files to this directory on your Memory Stick +(see below). + +Once you've copied Yabause to your Memory Stick, skip to "How to use +Yabause" below. + + +Installing from source +---------------------- +To build Yabause for PSP from the source code, you'll need a recent (at +least SVN r2450(*)) copy of the unofficial PSP SDK from http://ps2dev.org, +along with the toolchain from the same site; Gentoo Linux users can also +download a Portage overlay from http://achurch.org/portage-psp.tar.bz2 and +"emerge pspsdk". Ensure that the PSP toolchain (psp-gcc) and tools +(psp-prxgen, etc.) are in your $PATH, then configure Yabause with: + + ./configure --host=psp [options...] + +(*) Note that the PSP SDK headers and libraries are, at least through + r2493, missing some functions required by Yabause. If you get errors + about the functions sceKernelIcacheInvalidateAll or + sceKernelIcacheInvalidateRange, apply the patch found in + src/psp/icache-funcs-2450.patch to the PSP SDK source, recompile and + reinstall it, then rebuild Yabause. This patch is already included if + you build the SDK from the Gentoo Portage overlay. + +You can ignore the warning about the --build option that appears when you +start the configure script. You may also see a warning about "using cross +tools not prefixed with host triplet"; you can usually ignore this as well, +but if you get strange build errors related to libraries like SDL or +OpenGL, try disabling the optional libraries with the options +"--without-sdl" and "--without-opengl". + +The following additional options can be used when configuring for PSP: + + --enable-psp-debug + Enables printing of debug messages to standard error. + + --enable-psp-profile + Enables printing of profiling statistics to standard error. + By default, statistics are output every 100 frames; edit + src/psp/main.c to change this. Note that profiling has a + significant impact on emulation speed. + + --with-psp-me-test + Builds an additional program, "me-test.prx", which tests the + functionality of the Media Engine access library included with + Yabause. Only useful for debugging or extending the library. + +Note that if you build with optimization disabled (-O0) or at too low a +level, you may get compilation errors in src/psp/satopt-sh2.c. -O3 is +recommended; set this flag in the CFLAGS environment variable before +running the "configure" script. For example, if you use the "bash" shell: + + CFLAGS=-O3 ./configure --host=psp [options...] + +After the configure script completes, run "make" to build Yabause. The +build process will create the EBOOT.PBP and me.prx (note that the latter +is lowercase) files in the src/psp/ subdirectory; create a directory for +Yabause under /PSP/GAME on your memory stick (e.g. /PSP/GAME/YABAUSE) and +copy the files there. + + +How to use Yabause (PSP-specific notes) +--------------------------------------- +All files you intend to use with Yabause (BIOS images, CD images, backup +RAM images) must be stored in the same directory as the EBOOT.PBP and +ME.PRX files mentioned above. The default filenames used by Yabause are +as follows: + + BIOS.BIN -- BIOS image + CD.ISO -- CD image (can also be a *.CUE file) + BACKUP.BIN -- Backup RAM image (will be created if it does not exist) + +You can choose other files from the Yabause configuration menu, which is +displayed the first time you start Yabause and can also be brought up at +any time by pressing the Select button; see below for details. If you do +not already have a backup RAM image, just leave the backup RAM filename at +its default setting, and the file will be created the first time backup RAM +is saved. + +The directional pad and analog stick can both be used to emulate the +Saturn controller's directional pad. The default button controls are as +follows: + + Start -- Start + A -- Cross + B -- Circle + C -- (unassigned) + X -- Square + Y -- Triangle + Z -- (unassigned) + L -- L + R -- R + +Button controls can be changed via the configuration menu. + + +The Yabause PSP configuration menu +---------------------------------- +When you first run Yabause, the configuration menu will be displayed, +allowing you to choose the CD image you want to run and configure other +Yabause options. You can also press Select while the emulator is running +to bring up the menu; the emulator will remain paused while you have the +menu open. + +The main menu contains six options: + + * "Configure general options..." + + This opens a submenu with the following options: + + * "Start emulator immediately" + + When enabled, the emulator will start running immediately when + you load Yabause, instead of showing the configuration menu. + + * "Select BIOS/CD/backup files..." + + This opens a submenu which allows you to select the files + containing the BIOS image, CD image, and backup data you want + to use. Selecting one of the three options will open a file + selector, allowing you to choose any file in the Yabause + directory on your Memory Stick. + + Note that changing any of the files will reset the emulator. + + * "Auto-save backup RAM" + + When enabled, automatically saves the contents of backup RAM to + your Memory Stick whenever you save your game in the emulator. + The emulator will display "Backup RAM saved." on the screen for + a short time when an autosave occurs. Note that the emulator + may pause for a fraction of a second while autosaving. This + option is enabled by default. + + Be aware that backup RAM is _not_ saved to the Memory Stick + when you quit Yabause; if you disable this option, you need to + manually save it using the "Save backup RAM now" option when + appropriate. + + * "Save backup RAM now" + + Immediately saves the contents of backup RAM to your Memory + Stick. If you have auto-save disabled, you should use this + option to save backup RAM before quitting Yabause. + + * "Save backup RAM as..." + + Allows you to enter a new filename (using the PSP's built-in + on-screen keyboard) for the backup RAM save file. This can be + useful if you want to keep separate backup RAM files for + different games, or if you want to save more slots than a game + normally allows. Yabause will immediately save backup RAM to + the filename you enter, and will also use that filename when + later auto-saving backup RAM (or when you manually use "Save + backup RAM now"). However, the new filename will only be used + until you quit Yabause, unless you select "Save options" on the + main menu. + + Note that the emulator will _not_ be reset when you use this + option, so you can feel free to select it while playing a game. + (However, don't select it while the game is in the middle of + loading or saving, as this can corrupt backup RAM -- just as if + you tried to remove the PSP's Memory Stick while saving a game + on your PSP.) + + NOTE: For reasons currently unknown, the top part of the + on-screen keyboard display may flicker or appear corrupted. + However, text can be entered as usual. + + * "Configure controller buttons..." + + This opens a submenu which allows you to configure which PSP button + corresponds to which button on the emulated Saturn controller. + Pressing one of the Circle, Cross, Triangle, or Square buttons on + the PSP will assign that button to the currently selected Saturn + controller button. The PSP's Start, L, and R buttons are always + assigned to the same-named buttons on the Saturn controller, and + cannot be changed. + + Since both the Circle and Cross buttons are used for button + assignment, the Start button is used to return to the main menu. + + * "Configure video options..." + + This opens a submenu with the following options: + + * "Use hardware video renderer" / "Use software video renderer" + + These options allow you to choose between the PSP-specific + hardware renderer and the default software renderer built into + Yabause for displaying Saturn graphics. The hardware renderer + is significantly faster; for simple 2-D graphics, it can run at + a full 60fps without frame skipping (if the game program itself + can be emulated quickly enough). However, a number of more + complex graphics features are not supported, so if a game does + not display correctly, try using the software renderer instead. + + The selected renderer can be changed while the emulator is + running without disturbing your game in progress. However, + changing the renderer may cause the screen to blank out or + display corrupted graphics for a short time. + + * "Configure hardware rendering settings..." + + This option opens another submenu which allows you to change + certain aspects of the hardware video renderer's behavior: + + * "Aggressively cache pixel data" + + When enabled, Yabause will try to store a copy of all + graphic data in the PSP's native pixel format, to speed up + drawing. However, Yabause may not always notice when the + data is changed, causing incorrect graphics to appear. + (This can be fixed by disabling the option, exiting the + menu for a moment, then re-enabling the option.) When + disabled, all graphics are redrawn from the Saturn data + every frame. This option is enabled by default. + + * "Smooth textures and sprites" + + When enabled, smoothing (antialiasing) is applied to all + 3-D textures and sprites drawn on the screen. This can + make 3-D environments look smoother than on a real Saturn, + but it will also cause zoomed sprites to look blurry, which + may not be the game's intended behavior. + + * "Smooth high-resolution graphics" + + When enabled, high-resolution graphics (which ordinarly + would not fit on the PSP's screen) are displayed by + averaging adjacent pixels to give a smoother look to the + display; this can particularly help in reading small text + on a high-resolution screen. However, this smoothing is + significantly slower than the default method of just + skipping every second pixel. + + * "Enable rotated/distorted graphics" + + Selects whether to display rotated or distorted graphics + at all. Most such graphics cannot be rendered by the + PSP's hardware, so Yabause has to draw them in software, + which can be a major source of slowdown. Disabling this + option will turn such graphics off entirely. This option + is enabled by default. + + * "Optimize rotated/distorted graphics" + + When enabled, Yabause will try to detect certain types of + rotated or distorted graphics which can be approximated by + PSP hardware operations such as 3D transformations, and use + the PSP's hardware to draw them quickly. However, this + will often result in graphics that look different from the + game as played on an actual Saturn, so this option can be + used to disable the optimizations and draw the graphcs more + accurately (at the expense of speed). This option is + enabled by default. + + Note that none of the above options have any effect when the + software video renderer is in use. + + * "Configure frame-skip settings..." + + This option opens another submenu which allows you to configure + the hardware renderer's frame-skip behavior: + + * "Frame-skip mode" + + This option is intended to allow you to switch between + manual setting and automatic adjustment of frame-skip + parameters. However, automatic mode is not yet + implemented, so always leave this set on "Manual". + + * "Number of frames to skip" + + In Manual mode, sets the number of frames to skip for every + frame drawn. 0 means "draw every frame", 1 means "draw + every second frame" (skip 1 frame for every frame drawn), + and so on. + + * "Limit to 30fps for interlaced display" + + Always skip at least one frame when drawing interlaced + (high-resolution) screens. Has no effect unless the number + of frames to skip is set to zero. This option is enabled + by default. + + * "Halve framerate for rotated backgrounds" + + Reduce the frame rate by half (in other words, skip every + second frame that would otherwise be drawn) when rotated or + distorted background graphics are displayed. Since rotation + and distortion take a long time to process on the PSP, this + option can help keep games playable even when they make use + of these Saturn hardware features. This option is enabled + by default. + + Note that this option does not apply to rotated or + distorted graphics which are displayed using an optimized + algorithm (see the "Optimize rotated/distorted graphics" + option above). + + Frame skipping is not supported by the software renderer, so + none of these options will have any effect when the software + renderer is in use. + + * "Show FPS" + + When enabled, the emulator's current speed in emulated frames per + second (FPS) will be displayed in the upper-right corner of the + screen as "FPS: XX.X (Y/Z)". The number "XX.X" is the average + frame rate, calculated from the last few seconds of emulation; + "Y" shows the number of Saturn frames emulated since the previous + frame was shown, while "Z" is the actual time that passed in + 60ths of a second. (Thus, the instantaneous frame rate can be + calculated as (Y/Z)*60.) + + This option has no effect when the software renderer is in use. + + * "Configure advanced settings..." + + This opens a submenu with the following options: + + * "Use SH-2 recompiler" + + This option allows you to choose between the default SH-2 core, + which recompiles Saturn SH-2 code into native MIPS code for the + PSP, and the SH-2 interpreter built into Yabause. The SH-2 + interpreter is much slower, often by an order of magnitude or + more, so there is generally no reason to disable this option + unless you suspect a bug in the recompiler. + + Note that changing this option will reset the emulator. As with + "Reset emulator" on the main menu, you must hold L and R while + changing this option to avoid an accidental reset. + + * "Select SH-2 optimizations..." + + This option opens up another submenu which allows you to turn on + or off certain optimizations used by the SH-2 recompiler. These + are shortcuts taken by the recompiler to allow games to run more + quickly, but in rare cases they can cause games to misbehave or + even crash. If a game doesn't work correctly, turning one or + more of these options off may fix it. + + These options can be changed while the emulator is running + without disturbing your game in progress. However, changing them + causes the emulator to clear out any recompiled code it has in + memory, so the game may run slowly for a short time after exiting + the menu as the emulator recompiles SH-2 code using the new + options. + + All optimizations are enabled by default. + + * "Configure Media Engine options..." + + This option opens up another submenu with options for + configuring the Media Engine: + + * "Use Media Engine for emulation" + + Enables the use of the PSP's Media Engine CPU to handle part + of the emulation in parallel with the main CPU. This can + provide a moderate boost to emulation speed; however, since + the Media Engine is not designed for this sort of parallel + processing, some games may behave incorrectly or even crash. + As such, this option is still considered experimental; use + it at your own risk. + + IMPORTANT: It is not currently possible to suspend the PSP + while the Media Engine is in use. If you start Yabause with + the Media Engine enabled, the "suspend" function of the + PSP's power switch will be disabled, so you must save your + game inside the emulator and exit Yabause before putting the + PSP into suspend mode. + + This option only takes effect when Yabause is started, so if + you change it, make sure you select "Save options" in the + main menu and then quit and restart Yabause. + + * "Cache writeback frequency" + + Sets the frequency at which the main CPU and Media Engine + caches are synchronized, relative to the frequency of code + execution on the Media Engine. The default frequency of 1/1 + is safest; lower frequencies (1/2, 1/4, and so on) can + increase emulation speed, but are also more likely to cause + sound glitches, crashes, or other incorrect behavior + depending on the particular game. However, adjusting the + size of the write-through region (see below) can mitigate + these problems for some games. + + Naturally, this option has no effect if the Media Engine is + not being used for emulation. + + * "Sound RAM write-through region" + + Sets the size of the region at the beginning of sound RAM + which is written through the PSP's cache. Writing through + the cache is an order of magnitude slower than normal + operation, so setting this to a large value can slow down + games significantly. However, most games only use a small + portion of sound RAM for communication with the sound CPU, + so by tuning this value appropriately, you may be able to + reduce the cache writeback frequency (see above) while still + getting stable operation. From experimentation, a value of + 2k seems to work well for some games. + + Naturally, this option has no effect if the Media Engine is + not being used for emulation. + + * "Use more precise emulation timing" + + When enabled, the emulator will keep the various parts of the + emulated Saturn hardware more precisely in sync with each other. + This carries a noticeable speed penalty, but some games may + require this more precise timing to work correctly. + + * "Sync audio output to emulation" + + When enabled, the emulator will synchronize audio output with + the rest of the emulation. In general, this improves audio/video + synchronization but causes more frequent audio dropouts (or + "popping") when the emulator runs more slowly than real time. + However, the exact effect of this option can vary: + + - When disabled, the audio can get ahead of the video if the + emulator is running slowly; this can be seen, for example, + in the Saturn BIOS startup animation. On the other hand, + game code that uses the audio output speed for timing (such + as the movie player in Panzer Dragoon Saga) can actually run + faster with synchronization disabled. MIDI-style background + music will also play more smoothly, though of course the + music tempo will slow down depending on the emulation speed. + + - When enabled, the audio output will match the output of a + real Saturn much more closely. In particular, this option + is needed to avoid popping in streamed audio such as Red + Book audio tracks when the emulator runs at full speed + (60fps). On the flip side, the audio will momentarily drop + out (as described above) whenever the emulator takes more + than 1/60th of a second to process an emulated frame. + + This option is enabled by default. + + * "Sync Saturn clock to emulation" + + When enabled, the Saturn's internal clock is synchronized with + the emulation, rather than following real time regardless of + emulation speed. If the emulator is running slow, for example, + this option will slow the Saturn's clock down to match the speed + at which the emulator is running. This option is enabled by + default. + + * "Always start from 1998-01-01 12:00" + + When enabled, the Saturn's internal clock will always be + initialized to 12:00 noon on January 1, 1998, rather than the + current time when the emulator starts. When used with the clock + sync option above, this is useful in debugging because it ensures + a consistent environment each time the emulator is started. + Outside of debugging, however, there is usually no reason to + enable this option. + + * "Save options" + + Save the current settings, so Yabause will use them automatically the + next time you start it up. + + * "Reset emulator" + + Reset the emulator, as though you had pressed the Saturn's RESET + button. To avoid accidentally resetting the emulator, you must hold + the PSP's L and R buttons while selecting this option. + +Pressing Select on any menu screen will exit the menu and return to the +Saturn emulation. + + +Troubleshooting +--------------- +Q: "My game runs too slowly!" + +A: C'est la vie. The PSP is unfortunately just not powerful enough to + emulate the Saturn at full speed (see "Technical notes" below for the + gory details). Here are some things you can do to improve the speed of + the emulator: + + * Make sure you are using the hardware video renderer (in the + "Configure video options" menu) and the SH-2 recompiler (in the + "Configure advanced settings" menu). + + * Under "Configure video options" / "Configure hardware rendering" + settings", turn off "Enable rotated/distorted graphics". A single + distorted background can take the equivalent of 2 to 3 frames at + 60fps to render on the PSP. + + * Under "Configure video options" / "Configure frame-skip settings", + set the frame-skip mode to manual and increase the number of frames + to skip. (Many games only run at 30 frames per second, so using a + frame-skip count of 1 won't actually make a visible difference + compared to a count of 0.) + + * Under "Configure advanced emulation options" / "Select SH-2 + optimizations", make sure all optimizations are enabled. + + * Under "Configure advanced emulation options", if "Use more precise + emulation timing" is disabled, try enabling it. (This may cause + the game to freeze or crash, however.) + + * Try turning on the "Use Media Engine for emulation" option in the + "Configure advanced emulation options" menu, but note that this + option is experimental and may cause your game to misbehave or even + crash. + + * If the Media Engine is enabled, try lowering the cache writeback + frequency in the "advanced emulation options" menu. Typically, + 1/4 to 1/8 will provide a noticeable speed increase over 1/1, while + 1/16 and lower are not likely to have much effect. + +Q: "My game suddenly froze!" + +A: Try pressing Select to open the Yabause menu. + + * If the menu doesn't open, then either you've hit a bug in Yabause, + or the SH-2 optimizer has caused the program to misbehave. Restart + Yabause, then go to the "Configure advanced emulation options" / + "Select SH-2 optimizations" and disable all of the options there. + If that fixes the problem, you can then try turning the options on + one by one to find the one that caused the crash (you may need to + repeat whatever actions you performed in the game in order to + determine whether the crash occurs or not), and disable only that + option to keep the emulator running as fast as possible. + + * If the menu does open, then one likely cause is a timing issue; + this can be seen, for example, when starting Dead or Alive with the + "Use more precise emulation timing" option disabled. Try enabling + this option under the "Configure advanced emulation options" menu + and resetting the emulator to see if it fixes the problem. + + In either of the above cases, it's also possible that the game itself + has a bug. Look in FAQs or other online resources and see if any + similar problems have been reported. + + +Technical notes +--------------- +The Saturn, like the PSOne, is only one step down in power from the PSP +itself, so full-speed emulation is a fairly difficult proposition from the +outset. To make matters worse, the Saturn's architecture is about as +different from the PSP as two modern computer architectures can be: +different primary CPUs (SH-2 versus MIPS Allegrex), big-endian byte order +(Saturn) versus little-endian (PSP), tile-based graphics (Saturn) versus +texture-based graphics (PSP), and so on. As such, Yabause must take a +number of shortcuts to make games even somewhat playable. + +<<< SH-2 emulation >>> + +Emulation of the Saturn's two SH-2 CPUs in particular is problematic. +These processors run at either 26 or 28 MHz, and they use a RISC-like +instruction set in which most instructions execute in one clock cycle, so +in a worst-case scenario Yabause would need to process 56 million SH-2 +instructions per second--on top of sound, video, and other hardware +emulation--to maintain full speed. But the PSP's single(*) Allegrex CPU +runs at a maximum of 333MHz, meaning that the SH-2 emulator must be able to +execute each instruction (including accessing the register file, swapping +byte order in memory accesses, updating the SH-2 clock cycle counter, and +so on) within at most 6 native clock cycles for full-speed emulation. In +fact, the demands of emulating the other Saturn hardware reduce this to +something closer to 4 native clock cycles. + +(*) The PSP actually has a second CPU, the Media Engine, but limitations + of the PSP architecture make it unsuitable for use as a full-fledged + second processor. See below for details. + +With these limitations, interpreted execution of SH-2 code is out of the +question--merely looking up the instruction handler would exhaust the +instruction's quota of execution time. For this reason, the PSP port uses +a dynamic translator to convert blocks of SH-2 code into blocks of native +MIPS code. When the emulator encounters a block of SH-2 code for the first +time, it scans through the block, generating equivalent native code for the +block which is then executed directly on the native CPU. This naturally +causes the emulator to pause for a short time when it encounters a lot of +new code at once, such as when loading a new part of a game from CD; this +is the price that must be paid for the speed of native code execution. + +Even with this dynamic translation, however, there are still a number of +hurdles to fast emulation. For example: + +* Every time the end of a code block is reached, the emulator must look up + the next block to execute. This lookup consumes precious cycles which do + not directly correspond to SH-2 instruction emulation (around 35 cycles + per lookup in the current version). + + In order to streamline code translation and increase the optimizability + of individual blocks, the dynamic translator tends to choose minimally- + sized blocks for translation. Tests showed that this was an improvement + over an older algorithm that used larger blocks, but the resulting + overhead of block lookups imposes a limit on execution speed for certain + types of code, particularly algorithms which rely heavily on subroutine + calls. + + At the other end of the spectrum, one might consider modifying a true + compiler like GCC to accept SH-2 instructions as input, then running + each code block through the compiler itself to generate native code. + This could undoubtedly produce efficient output with larger blocks, but + it would also impose significant additional overhead when translating. + +* The SH-2 is unable to load arbitrary constants into registers, instead + using PC-relative accesses to load values outside the range of a MOV #imm + instruction from memory. However, Saturn programs also use PC-relative + accesses for function-local static variables, meaning that there is no + general way to tell whether a given value is actually a constant or + merely a variable that may be modified elsewhere. + + This presents a particular problem in optimizing memory accesses, since + if a pointer loaded from a PC-relative address is not known to be + constant, the translated code must incur the overhead of checking the + pointer's value every time the block is executed. The SH-2 core includes + an optional optimization, SH2_OPTIMIZE_LOCAL_POINTERS, which takes the + stance that all such pointers either are constant or will always point + within the same memory region (high system RAM, VDP2 RAM, etc.). This + optimization shows a marked improvement in execution speed in some cases, + but any code which violates the assumption above will cause the emulator + to crash. + +* Some games make use of self-modifying code, presumably in an attempt to + increase execution speed; one example can be found in the "light ray" + animation used in Panzer Dragoon Saga when obtaining an item. Naturally, + the use of self-modifying code has a severe impact on execution time in a + dynamic translation environment, as each modification requires every + block containing the modified instruction to be retranslated. (A similar + effect can be seen on modern x86-family CPUs, which internally translate + x86 instructions to native micro-ops for execution; self-modifying code + can slow down the processor by an order of magnitude or more.) + + The SH-2 core attempts to detect frequently modified instructions and + pass them directly to the interpreter to avoid the overhead of repeated + translation, but there is unfortunately no true solution to the problem + other than rewriting the relevant part of the game program itself. + +* Memory accesses are difficult to implement efficiently; in fact, the SH-2 + emulator devotes over 1,000 lines of source code to handling load and + store operations, independently of the memory access handlers in the + Yabause core. The current implementation is able to handle accesses to + true RAM fairly quickly, but any access which falls back to the default + MappedMemory*() handlers incurs a significant access penalty (typically + 20-30 cycles plus any handling needed for the specific address). + + This is most obvious while loading data from the emulated CD, since the + game program must access a hardware register in a loop while waiting for + the CD data to be loaded, and additionally some games read CD data + directly out of the CD data register rather than using DMA to load the + data into memory. Currently, the only way to speed up such code blocks + is through handwritten translation (see src/psp/satopt-sh2.c). + +Patches to either speed up specific games or to improve the translation +algorithm generally are of course welcome. + +<<< Use of the Media Engine >>> + +Aside from the two SH-2 cores, a third major consumer of CPU time is the +SCSP, the Saturn's sound processor, and particularly the MC68EC000 +("68k") CPU used therein. While most games don't run particularly complex +code on the 68k, it is nonetheless a proper CPU in its own right, and +requires a fair amount of time to emulate; multi-channel FM background +music takes time to generate as well. Currently, the PSP port of Yabause +has the ability to make use of the PSP's Media Engine CPU to process 68k +instructions and audio generation in parallel with the rest of the +emulation, but this use of the Media Engine is a considerable departure +from Sony's design and thus a risky endeavor. + +The primary difficulty with using the ME as a "second core" in the sense +of the multi-core processors used in PCs is that of cache coherency. +Unlike generic multiprocessor or multi-core systems, the PSP's two CPUs +do not implement cache coherency; this means that neither CPU knows what +the other CPU has in its cache, and one CPU may inadvertently clobber the +other's changes, causing stores to memory to get lost. As an example, +consider these two simple loops, operating in parallel on a two-element +array initialized to {1,1} that resides in a single cache line: + + Core 1 Core 2 + ------ ------ + for (;;) { for (;;) { + array[0] += array[1]; array[1] += array[0]; + } } + +This illustrates two problems caused by the lack of cache coherency: + +* On a cache-coherent (or single-core) system, the two array elements + will increase unpredictably as each loop sees the updated value stored + by the other loop. On the PSP, however, both elements will increase + monotonically; once each CPU loads the cache line, it never sees any + stores performed by the other CPU, because accesses to the array always + hit the cache. + +* On a cache-coherent system, if the cache line is flushed to memory, it + will always contain the current values of both array elements. On the + PSP, however, the array element _not_ updated by the flushing CPU will + be written with the same value it had when the cache line was loaded + by that CPU. In particular, if the other CPU had already flushed the + cache line, that change will be clobbered--for example (here "SC" is + the main CPU and "ME" is the Media Engine): + + Time Operation SC cache ME cache Memory Desired + ---- ---------- -------- -------- ------ ------- + T1 Initialize {1,1} {1,1} {1,1} {1,1} + T2 SC flush {A,1} {1,B} {A,1} {A,B} + T3 ME flush {C,1} {1,D} {1,D} {C,D} + + Note that at no time after initialization are the contents of memory + correct, and in particular, the value "A" written by the SC is lost + when the ME flushes {1,D} from its cache, even though the ME loop + never actually modified that array element. + +In order for Yabause to have even a hope of stable operation, therefore, +the use of both CPUs' caches must be carefully controlled to avoid data +loss. + +When use of the Media Engine is enabled, the following steps are taken +to avoid data corruption due to the lack of cache coherency: + +* SCSP state variables used for inter-thread communication are divided into + separate, 64-byte (cache-line) aligned data sections, based on which + thread (the main Yabause thread, running on the SC, or the SCSP thread, + running on the ME) writes to them. + +* SCSP state variables are accessed using uncached (0x4nnnnnnn) addresses + in two cases: when _reading_ data written by the other CPU (to avoid an + old value getting stuck in the cache), and when _writing_ data which is + also written by the other CPU (to avoid the cache line clobbering problem + described above). + +* Sound RAM is accessed _with_ caching (except in one case described + below), because forcing every sound RAM access through an uncached + pointer causes significant slowdown. Instead, cached CPU data is written + back to RAM at strategic points. + +* The SC's data cache is flushed (written back and invalidated) immediately + before waiting for the SCSP thread to finish processing, e.g. for + ScspReset(). The data cache is written back on every ScspExec() call + (though the writeback frequency may be reduced through the configuration + menu), but it is _not_ flushed for performance reasons; instead, sound + RAM read accesses from the SC are made through uncached addresses, as + with SCSP state variables above. + +* The ME's data cache is flushed after each iteration of the SCSP thread + loop. This flushing is not coded directly into scsp.c, but instead + takes place in the YabThreadYield() and YabThreadSleep() implementations. + (These functions are naturally meaningless on the ME, but since the SCSP + thread calls one or the other at the end of each loop, it's a convenient + place to flush the cache.) + +* The 68k state block, along with dynamically-generated native code when + dynamic translation is enabled, is stored in a separately allocated pool + and managed with custom memory allocation functions (local_malloc() and + friends in psp-m68k.c), since the standard memory management functions + are not designed to work with the ME and would likely cause a crash due + to cache desynchronization. + +In general, using the ME provides a moderate speed improvement (10-15%) to +overall emulation speed. There are, however, some cases in which the lack +of cache coherency could cause games to misbehave or even crash Yabause: + +* If a game writes (from the SH-2) to a portion of sound RAM containing 68k + program code while the 68k is executing, the 68k may execute incorrect + code, or the dynamic translation memory pool may be corrupted. Normally, + games should only load code while the 68k is stopped, but there may be + cases when the SH-2 writes to a variable in sound RAM which is located in + the same region as 68k code, thus triggering this issue. + +* Games which rely on the precise relative timing of the SH-2 and 68k + processors are likely to fail in any multithreaded emulator, but are more + likely to fail when using the ME due to delays in data being written out + from the data caches. diff --git a/yabause/README.QT b/yabause/README.QT new file mode 100644 index 0000000000..d3e813c6cd --- /dev/null +++ b/yabause/README.QT @@ -0,0 +1,138 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + _________________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Compiling instructions...................................20 +2) How to use Yabause.......................................82 +3) Contact information.....................................105 +4) Disclaimer..............................................121 + + +1 Compiling instructions______________________________________ + +Yabause is written in C using the OpenGL library, so you need a working C compiler(such as gcc) and +these libraries, runtime and development packages: + + * OpenGL should be included with your compiler, if it isn't, + check on your compiler's website for links. + +Yabause can also use the SDL library: + + * http://www.libsdl.org/ + +Yabause can also use the GLUT library: + + * Check google, I haven't been able to find a good + source for it. + +Once these libraries installed, you should be ready to +install Yabause. + +Building the Qt 4 port require the Qt 4.3.x libraries too, it's a crossplatform C++ library for various all days crossplatform work ( gui, threads ... ). +I used the version 4.3.4 version for the developement and tests, so please don't report bugs if you are not using at least this version. + + * http://www.trolltech.com/products/qt + +Compiling for Windows : using mingw/cygwin__________________________________ + +All you have to do now is now is go into your mingw/cygwin +shell environment, go into the directory where you extracted +yabause, and type: "./configure --enable-newperinterface ---with-port=qt". Once that's done(and there +was no errors), type: "make". It should now take some time to +compile so go grab yourself a sandwich or beer - whatever suits +your fancy and it should be done in a few minutes. Now all you +have to do is type "./src/qt/release/yabause" in order to run it. + +Compiling for Mac OS X : using gcc__________________________________________ + +Uncompress the Yabause source archive, move to the newly +created directory, type "./configure --enable-newperinterface --disable-dependency-tracking --disable-shared --with-port=qt", then "make", +it will generate a bundle: "Yabause.app" in the "src/qt" +directory. +You can then use Yabause by opening the bundle with: +"open Yabause.app" or with the Mac OS X finder. + +Compiling for *Nix/Linux/BSD ? : using gcc__________________________________ + +Uncompress the Yabause source archive, move to the newly +created directory, type "./configure --enable-newperinterface ---with-port=qt", then "make", +it will generate one program: "yabause" in the "src/qt" +directory. + + +You can also pass the --enable-debug option to the configure script, Yabause +will then print debug messages in a special dock window. +(Use the --help flag for a complete list of options.) + +You can even type "make install" to install that program on +your system (in /usr/local/ by default), then uninstalling is +done by typing "make uninstall". + +You can try to read other specific README files for more information. + +2 How to use Yabause__________________________________________ + +Before using Yabause, you need to configure a few things in +the Preferences dialog (File>Settings). + + +2.1 Configuration_____________________________________________ + +First, set the BIOS path. +Yabause can run some games without a BIOS, but most of them +needs it. If you want to use the emulated BIOS, just let the +BIOS entry blank. + +Next, set the cdrom device. +It can be a cd device, an iso or a cue file. Set the cd type +accordingly. + +The last thing you have to configure is the keys. + +Once eveything is set, you can start emulation with the +"Emulation>Run" entry. + + +3 Contact information_________________________________________ + +General inquiries should go to: +E-mail: guillaume.duhamel@gmail.com +E-mail: cwx@cyberwarriorx.com + +Qt Port-related inquiries should go to: +E-mail: pasnox@yabause.org + +Web: http://yabause.org + +Please don't ask for roms, bios files or any other copyrighted +stuff. Please use the forum when you have any questions if +possible. + + +4 Disclaimer__________________________________________________ + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This program is distributed in the hope that it will be +useful,but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301 USA + +See the GNU General Public License details in COPYING. diff --git a/yabause/README.WII b/yabause/README.WII new file mode 100644 index 0000000000..cc57112344 --- /dev/null +++ b/yabause/README.WII @@ -0,0 +1,88 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + _________________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Special Notice...........................................21 +2) Compiling instructions...................................28 +3) How to use Yabause.......................................50 +4) Contact information......................................55 +5) Disclaimer...............................................71 + + +1 Special Notice______________________________________________ + +Please note that the Wii port is in alpha status, and won't be +officially supported until a later date. If you would like to +contribute, please contact us(see the contact information +section in README). + +2 Compiling instructions______________________________________ + +Yabause is written in C using the devkitPro packages. You can +download it from: + +http://www.devkitpro.org + +Once this is installed, you should be ready to install Yabause. + +To compile, either use the Makefile.wii file in the src +subdirectory to compile it(e.g. make -f Makefile.wii) or you can +use configure in the main directory as follows: + +./configure --build=i686-pc-linux-gnu --host=powerpc-gekko + +Once that's done, type "make". It should now take some time to +compile so go grab yourself a sandwich or beer - whatever suits +your fancy and it should be done in a few minutes. You should +now have a file called "yabause.elf". Now all you have to do is +load it on your Wii(which is something we won't cover here). + + +3 How to use Yabause__________________________________________ + +To be finished later. + + +4 Contact information_________________________________________ + +General inquiries should go to: +E-mail: guillaume@yabause.org +E-mail: cwx@cyberwarriorx.com + +Windows Port-related inquiries should go to: +E-mail: cwx@cyberwarriorx.com + +Web: http://yabause.org + +Please don't ask for roms, bios files or any other copyrighted +stuff. Please use the forum when you have any questions if +possible. + + +5 Disclaimer__________________________________________________ + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This program is distributed in the hope that it will be +useful,but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301 USA + +See the GNU General Public License details in COPYING. diff --git a/yabause/README.WIN b/yabause/README.WIN new file mode 100644 index 0000000000..f028197b3d --- /dev/null +++ b/yabause/README.WIN @@ -0,0 +1,250 @@ + _ _ + / \_/ \ ___ _ ____ + \ /___ ___ / || | __ / \ ____ + \ // || \ / || | \ \\ \_// \ + / // || // _ || |__\ \\ \ __/ + \_// _ || \\_/ \_||______/ \ \\ \__ + \_/ \_||___/ \____/ \____\ + Yet Another Buggy And Uncomplete Saturn Emulator + + _________________________________________ + Copyright (c) 2002-2011 Yabause team + + +1) Compiling instructions...................................20 +2) How to use Yabause.......................................68 +3) Contact information.....................................217 +4) Disclaimer..............................................233 + + +1 Compiling instructions______________________________________ + +Yabause is written in C using the DirectX 8.0, OpenGL, GLUT, and +mini18n libraries, so you need a working C compiler(such as gcc) +and these libraries, runtime and development packages: + + * You can find DirectX headers and libraries(for mingw) at + http://alleg.sourceforge.net/wip.html as the file + "dx80_mgw.zip". The actual runtime libraries(or + headers/libraries for Visual C++) can be gotten from + http://www.microsoft.com/DirectX + + * OpenGL should be included with your compiler, if it isn't, + check on your compiler's website for links. + + * Check google for GLUT. I haven't been able to find a good + source for it. + + * You can get mini18n from Yabause's sourceforge download page + here: http://sourceforge.net/project/showfiles.php?group_id=89991&package_id=304859 + +Once these libraries installed, you should be ready to +install Yabause. + +Compiling using mingw/cygwin__________________________________ + +All you have to do now is now is go into your mingw/cygwin +shell environment, go into the directory where you extracted +yabause, and type: "./configure". Once that's done(and there +was no errors), type: "make". It should now take some time to +compile so go grab yourself a sandwich or beer - whatever suits +your fancy and it should be done in a few minutes. Now all you +have to do is type "./src/yabause" in order to run it. + +Compiling using Visual C++____________________________________ + +Make sure you have the latest DirectX SDK and DDK installed. You +can get both of them from Microsoft's website. + +Load up IDE that comes with Visual C++/Visual Studio, go into the +file menu, open an existing project. Go into the yabause's +src/windows directory and open yabause.sln. Now all you have +to do is build it like any other Visual C++ project. + +You can compile for either x86 or x64(for those using Windows XP +x64 or Vista x64. + + +2 How to use Yabause__________________________________________ + +While not necessarily needed, it is recommended you get a Saturn +ROM BIOS image. Please don't ask us where to get one. + +Execute "yabause". The program will open a settings window. + +Basic Settings________________________________________________ + +The Disc Type setting allows you to choose whether you'd like to +use a real cdrom or a cdrom image of the game you're trying to +run. + +The Cue/Iso File setting allows you to specify the location +of your Saturn game's cdrom image. + +The Drive Letter setting is for you to be able to choose which +cdrom drive you want yabause to use when trying to boot a game. + +The SH2 Core setting is for you to be able to choose which SH2 +Core to use. Unless you're a developer, chances are, you should +leave it as the default: "Fast Interpreter". + +The Region setting allows you to choose which region of game +you'll be booting. In most cases, it's best to leave it as +"Auto-detect". + +The Bios ROM File setting allows you to specify the location +of your Saturn ROM BIOS image. If you leave it blank, yabause +will try to emulate the bios instead. It's better to specify +a ROM BIOS image if you can since the emulated bios isn't +100% perfect and may not work with your games. + +The Backup RAM File setting allows you to specify the location +of the Backup RAM file. This file allows yabause to store and +load save games. + +The MPEG ROM File setting allows you to specify the location +of a MPEG Card's ROM image. While not necessary, it does allow +you to test out the saturn's vcd capabilities. + +The Cartridge Type setting allows you to choose which type of +external cartridge to emulate. Some carts also require you to +supply a rom filename, or a new filename for the emulator to +write to. You can enter that information in the field below it. + +When you're done, just click on the "OK" button. If the bios +location was specified correctly, emulation should start and +you will see a brief animation of the saturn logo being formed. + +Special Note: Some settings require a restart of the program. + +There's also settings specifically for video, sound, and input. + +Video Settings________________________________________________ + +If you click on the "Video" tab another list of settings is +displayed. You can set the Video Core to either do hardware +rendering using OpenGL, software renderer(uses OpenGL the final +draw though), or disable drawing completely with the "None" +option. You can also "Enable Auto Frame-skipping" which basically +tries to skip rendering video frames if emulation is lagging in +an attempt to speed things up. + +The Full Screen on startup setting allows you to set Yabause to +run using the full screen when started. You can also change what +resolution is used while in full screen. + +The custom window size setting allows you to set the size of the +video display for yabause. + +Sound Settings________________________________________________ + +If you click on the "Sound" tab another list of settings is +displayed. You can set the Sound Core to either do sound mixing +using DirectX Sound or disable sound completely with the "None" +option. You can also adjust the sound volume using the volume +slider underneath. + +Input Settings________________________________________________ + +If you click on the "Input" tab another list of settings is +displayed. Here you can choose which peripheral(s) emulate. If +you press "Config" another window will pop up. Here can set which +device you'd like to use at the top of the window. Control +settings can be changed by clicking on the equivalent button, and +then when a new window pops up that says "waiting for input..." +press a key/button and that will set the new setting for that +control. + +Log Settings__________________________________________________ + +If you've compiled your own copy of Yabause with the processor +define DEBUG, another tab will be available called "Log". This +allows you to control whether or not the program should be +logging emulation output using the "Enable Logging" setting. Log +Type tells the program whether it should write the output to a +file, or to a separate window so you can monitor the output while +you're running the program. + +Here are the default key mappings(they may be subject to change): +Up arrow - Up +Left arrow - Left +Down arrow - Down +right arrow - Right +k - A button +l - B button +m - C button +u - X button +i - Y button +o - Z button +x - Left Trigger +z - Right Trigger +j - Start button +q - Quit program +F1 - Toggle FPS display +Alt-Enter - Toggle fullscreen/window mode +` - Enable Speed Throttle +1 - Toggle VDP2 NBG0 display +2 - Toggle VDP2 NBG1 display +3 - Toggle VDP2 NBG2 display +4 - Toggle VDP2 NBG3 display +5 - Toggle VDP2 RBG0 display +6 - Toggle VDP1 display +F2 - Load State from slot 1 +F3 - Load State from slot 2 +F4 - Load State from slot 3 +F5 - Load State from slot 4 +F6 - Load State from slot 5 +F7 - Load State from slot 6 +F8 - Load State from slot 7 +F9 - Load State from slot 8 +F10 - Load State from slot 9 +Shift-F2 - Save State to slot 1 +Shift-F3 - Save State to slot 2 +Shift-F4 - Save State to slot 3 +Shift-F5 - Save State to slot 4 +Shift-F6 - Save State to slot 5 +Shift-F7 - Save State to slot 6 +Shift-F8 - Save State to slot 7 +Shift-F9 - Save State to slot 8 +Shift-F10 - Save State to slot 9 + +Command-line Options__________________________________________ + +You can also run the program using command-line options. To see a +full list, run "yabause --help" in the command prompt. + + +3 Contact information_________________________________________ + +General inquiries should go to: +E-mail: guillaume@yabause.org +E-mail: cwx@cyberwarriorx.com + +Windows Port-related inquiries should go to: +E-mail: cwx@cyberwarriorx.com + +Web: http://yabause.org + +Please don't ask for roms, bios files or any other copyrighted +stuff. Please use the forum when you have any questions if +possible. + + +4 Disclaimer__________________________________________________ + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This program is distributed in the hope that it will be +useful,but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301 USA + +See the GNU General Public License details in COPYING. diff --git a/yabause/TODO b/yabause/TODO new file mode 100644 index 0000000000..ee1f06f2f7 --- /dev/null +++ b/yabause/TODO @@ -0,0 +1 @@ +Everything moved to sourceforge trackers. diff --git a/yabause/acinclude.m4 b/yabause/acinclude.m4 new file mode 100644 index 0000000000..099f02511c --- /dev/null +++ b/yabause/acinclude.m4 @@ -0,0 +1,34 @@ +AC_DEFUN([YAB_CHECK_HOST_TOOLS], + [ + AC_CHECK_TOOLS([$1], [$2]) + if test `expr x$[$1] : x$host_alias` -eq 0 ; then + [$1]="" + fi + ]) + +AC_DEFUN([YAB_DEP_DISABLED], + [ + depdisabled=no + for i in $ac_configure_args ; do + if test $i = "'--disable-dependency-tracking'" ; then + depdisabled=yes + fi + done + if test "$depdisabled" = "no" ; then + AC_MSG_ERROR([You must disable dependency tracking +run the configure script again with --disable-dependency-tracking]) + fi + ]) + +AC_DEFUN([YAB_LINK_MINI18N], + [ + AC_ARG_ENABLE(static-mini18n, + AC_HELP_STRING(--enable-static-mini18n, Use a static dependency on mini18n), + [use_static_mini18n=$enableval]) + if test "x$use_static_mini18n" = "xyes" ; then + LIBS="-Wl,-Bstatic -lmini18n -Wl,-Bdynamic $LIBS" + else + LIBS="-lmini18n $LIBS" + fi + AC_DEFINE(HAVE_LIBMINI18N) + ]) diff --git a/yabause/autogen.sh b/yabause/autogen.sh new file mode 100644 index 0000000000..83236d797c --- /dev/null +++ b/yabause/autogen.sh @@ -0,0 +1,2 @@ +aclocal && autoconf && automake --add-missing --copy +cd src/c68k && aclocal && autoconf && automake diff --git a/yabause/autopackage/default.apspec.in b/yabause/autopackage/default.apspec.in new file mode 100644 index 0000000000..84a5d043c8 --- /dev/null +++ b/yabause/autopackage/default.apspec.in @@ -0,0 +1,57 @@ +# Copyright 2006 Guillaume Duhamel +# Copyright 2006 Fabien Coulon +# +# This file is part of Yabause. +# +# Yabause is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Yabause is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Yabause; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# -*-shell-script-*- + +[Meta] +RootName: @yabause.org/yabause:$SOFTWAREVERSION +DisplayName: Yabause Sega Saturn Emulator +ShortName: yabause +Maintainer: Guillaume Duhamel +Packager: Guillaume Duhamel +Summary: Yabause is a Sega Saturn emulator. +URL: http://yabause.org/ +License: GNU General Public License, Version 2 +SoftwareVersion: @VERSION@ +AutopackageTarget: 1.0 + +[Description] +This is a Sega Saturn emulator. + +[BuildPrepare] +prepareBuild --enable-static-mini18n CFLAGS='-D_FORTIFY_SOURCE=0' + +[BuildUnprepare] +unprepareBuild + +[Imports] +echo '*' | import + +[Prepare] +# Dependency checking +require @gtk.org/gtk 2.4 + +[Install] +# Put your installation script here +installExe bin/yabause +installDesktop "Game" share/applications/yabause.desktop +installData share/yabause + +[Uninstall] +# Usually just the following line is enough to uninstall everything +uninstallFromLog diff --git a/yabause/configure.in b/yabause/configure.in new file mode 100644 index 0000000000..4777423ece --- /dev/null +++ b/yabause/configure.in @@ -0,0 +1,616 @@ +AC_INIT(yabause, 0.9.10) + +if test "x$host_alias" = "xpowerpc-gekko" ; then + config_guess_sucks=$host_alias + host_alias=powerpc +elif test "x$host_alias" = "xpsp" ; then + config_guess_sucks=$host_alias + host_alias=mips +fi + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +if test ! "x$config_guess_sucks" = "x" ; then + host_alias=$config_guess_sucks +fi + +# hack to reset host_alias when we're not cross compiling +if test "x$host_alias" = "x$build_alias"; then + host_alias= +fi + +AM_INIT_AUTOMAKE([1.8.0]) + +AC_PROG_RANLIB + +# Check for --host=psp now because we need to get the PSP SDK directory and +# set linker flags/libraries +if test "x$host_alias" = "xpsp" ; then + AC_MSG_CHECKING([for PSPSDK]) + if test -z "$PSPSDK"; then + saved_IFS=$IFS + IFS=$PATH_SEPARATOR + for dir in $PATH; do + IFS=$saved_IFS + test -z "$dir" && dir=. + if test -x "$dir/psp-config"; then + PSPSDK=`"$dir/psp-config" -p` + test -n "$PSPSDK" && break + fi + done + IFS=$saved_IFS + fi + if test -n "$PSPSDK"; then + AC_MSG_RESULT([$PSPSDK]) + else + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please set the PSPSDK variable]) + fi + CFLAGS="-G0 -falign-functions=16 -I$PSPSDK/include -DNO_CLI $CFLAGS" + LDFLAGS="-specs=$PSPSDK/lib/prxspecs -Wl,-q,-T$PSPSDK/lib/linkfile.prx -L$PSPSDK/lib $LDFLAGS" + LIBS="$LIBS -lm -lc -lpspaudio -lpspctrl -lpspdisplay -lpspgu -lpspge -lpsppower -lpsputility -lpspuser" +fi + +AC_PROG_CC + +if test `expr x$CC : x$host_alias` -eq 0 ; then + AC_MSG_ERROR([$CC is not a cross compiler and we're cross-compiling.]) +fi + +AC_PROG_CPP +AC_PROG_INSTALL + +AC_LANG(C) +AC_LANG(C++) + +AC_C_BIGENDIAN + +AM_PROG_CC_C_O +AM_PROG_AS + +# Check what kind of CPU we're running on +case "$target_cpu" in + x86|i?86) yabause_cpu=x86; AC_DEFINE(CPU_X86);; + x86_64|amd64) yabause_cpu=x64; AC_DEFINE(CPU_X64);; + armv7*) yabause_cpu=arm; AC_DEFINE(CPU_ARM);; + *) if test "$host_alias" = psp; then + yabause_cpu=psp; AC_DEFINE(CPU_PSP) + else + yabause_cpu=unknown + fi;; +esac + + +################################################################################# +# # +# phase 1, we're checking for things that could be used by Yabause library # +# # +################################################################################# + +# checking for gettimeofday +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_FUNCS([gettimeofday]) + +# checking for floorf (C99 single-precision math) +OLDLIBS="$LIBS" +LIBS="$LIBS -lm" +AC_CHECK_FUNCS([floorf]) +LIBS="$OLDLIBS" + +# checking for mini18n +if test ! "x$MINI18N" = "x" ; then + OLDCPPFLAGS="$CPPFLAGS" + OLDLDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS -I$MINI18N/include" + LDFLAGS="$LDFLAGS -L$MINI18N/lib" + + AC_CHECK_LIB(mini18n, mini18n, [YAB_LINK_MINI18N], [ + CPPFLAGS="$OLDCPPFLAGS" + LDFLAGS="$OLDLDFLAGS" + ]) +else + AC_CHECK_LIB(mini18n, mini18n, [YAB_LINK_MINI18N]) +fi + +# checking for variadic macros +AC_MSG_CHECKING([[whether the compiled supports c99 variadic macros]]) +AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[#define MACRO(...) puts(__VA_ARGS__)]], [[MACRO("foo");]]), + AC_DEFINE(HAVE_C99_VARIADIC_MACROS) + AC_MSG_RESULT(yes), AC_MSG_RESULT(no)) + +# checking for SDL (can be used for sound and input) +use_sdl=yes +AC_ARG_WITH(sdl, AC_HELP_STRING(--without-sdl, don't use SDL), [use_sdl=$withval]) + +if test x$use_sdl = xyes ; then + case $host in + *darwin*) + OLDLDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -framework SDL" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + int t(void) { return 0; } + ]],[[ + int foo = t(); + ]])],[AC_DEFINE(HAVE_LIBSDL) + SDL_LIBS="-framework SDL"], []) + LDFLAGS="$OLDLDFLAGS" + ;; + *) + YAB_CHECK_HOST_TOOLS(HAVE_LIBSDL, [sdl-config sdl11-config]) + + if test ! x$HAVE_LIBSDL = x ; then + SDL_CFLAGS=`$HAVE_LIBSDL --cflags` + SDL_LIBS=`$HAVE_LIBSDL --libs` + AC_DEFINE(HAVE_LIBSDL) + fi + ;; + esac + + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +fi + +# checking for OpenGL (most ports needs it for video) +use_opengl=yes +AC_ARG_WITH(opengl, AC_HELP_STRING(--without-opengl, don't use OpenGL), [use_opengl=$withval]) + +if test x$use_opengl = xyes ; then + case $host in + *darwin*) + LIBS="$LIBS -framework OpenGL" + AC_DEFINE(HAVE_LIBGL) + ;; + *cygwin* | *mingw32*) + YAB_LIBS="$YAB_LIBS -lopengl32 -lglut32" + AC_DEFINE(HAVE_LIBGL) + ;; + *linux* | *bsd*) + AC_PATH_XTRA + LIBS="$LIBS $X_LIBS" + CFLAGS="$CFLAGS $X_CFLAGS" + + AC_CHECK_LIB(GL, glEnable, [ + LIBS="$LIBS -lGL" + AC_DEFINE(HAVE_LIBGL) + ],, $LIBS) + AC_CHECK_LIB(glut, glutGetModifiers,[ + LIBS="$LIBS -lglut" + AC_DEFINE(HAVE_LIBGLUT)],, $LIBS) + AC_CHECK_FUNC(glXGetProcAddress, AC_DEFINE(HAVE_GLXGETPROCADDRESS)) + ;; + *) + AC_CHECK_LIB(GL, glEnable, [ + LIBS="$LIBS -lGL" + AC_DEFINE(HAVE_LIBGL) + ],, $LIBS) + AC_CHECK_LIB(glut, glutGetModifiers,[ + LIBS="$LIBS -lglut" + AC_DEFINE(HAVE_LIBGLUT)],, $LIBS) + ;; + esac +fi + +# checking for OpenAL (can be used for sound) +use_openal=yes +AC_ARG_WITH(openal, AC_HELP_STRING(--without-openal, "don't use OpenAL"), [use_openal=$withval]) + +if test x$use_openal = xyes ; then + case $host in + *darwin*) + LIBS="$LIBS -framework OpenAL" + AC_DEFINE(HAVE_LIBAL) + ;; + *mingw32*) + # The OpenAL sound code uses Pthreads at the moment, so MinGW + # won't work right now. + ;; + *) + AC_CHECK_LIB(pthread, main) + AC_CHECK_LIB(openal, alBufferData, [ + LIBS="$LIBS -lopenal" + AC_DEFINE(HAVE_LIBAL) + ],, $LIBS) + ;; + esac +fi + +# platform-specific features +case $host in + *darwin*) + yabause_arch=macosx + AC_DEFINE([ARCH_IS_MACOSX]) + LIBS="$LIBS -framework CoreFoundation -framework IOKit" + major=`expr $host_os : "darwin\(@<:@^.@:>@*\)"` + if test $major -ge 7 ; then + sdkversion=0 + sdkfile="" + for i in /Developer/SDKs/MacOSX10.*.sdk; do + j=`expr $i : "/Developer/SDKs/MacOSX10.\(.\).*.sdk"` + if test $j -gt $sdkversion ; then + sdkversion=$j + sdkfile=$i + fi + done + AC_ARG_WITH([sdk], AC_HELP_STRING(--with-sdk, [choose your sdk (macosx only)]), [sdkfile=$withval]) + + YAB_DEP_DISABLED + + CFLAGS="$CFLAGS -mmacosx-version-min=10.3 -isysroot $sdkfile -arch i386 -arch ppc" + LDFLAGS="$LDFLAGS -Wl,-macosx_version_min,10.3 -arch i386 -arch ppc" + AC_DEFINE(MAC_OS_X_VERSION_MAX_ALLOWED, MAC_OS_X_VERSION_10_3) + fi + ;; + *linux*) + yabause_arch=linux + LIBS="$LIBS -lm" + AC_DEFINE([ARCH_IS_LINUX]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = CDSL_CURRENT;]])], + [], + [AC_DEFINE(LINUX_CDROM_H_IS_BROKEN)]) + ;; + *cygwin*) + yabause_arch=windows + AC_DEFINE([ARCH_IS_WINDOWS]) + AC_DEFINE(_WIN32_IE, 0x0500) + ;; + *mingw32*) + yabause_arch=windows + AC_DEFINE([ARCH_IS_WINDOWS]) + AC_CHECK_HEADERS("wnaspi32.h", [], [], [#include ]) + AC_DEFINE(_WIN32_IE, 0x0500) + ;; + *freebsd*) + yabause_arch=freebsd + AC_DEFINE([ARCH_IS_FREEBSD]) + ;; + *netbsd* | *openbsd*) + yabause_arch=netbsd + AC_DEFINE([ARCH_IS_NETBSD]) + ;; + *) + case $host_alias in + psp) + yabause_arch=psp + ;; + *) + yabause_arch="." + AC_DEFINE(UNKNOWN_ARCH) + ;; + esac + ;; +esac + +# users can turn c68k compilation off (forced off on PSP) +if test "x$yabause_arch" = "xpsp"; then + compile_c68k=no +else + compile_c68k=yes +fi +AC_ARG_WITH(c68k, AC_HELP_STRING(--without-c68k, don't compile C68k), [compile_c68k=$withval]) +if test x$compile_c68k = xyes ; then + if test "x$yabause_arch" = "xpsp"; then + AC_MSG_ERROR([c68k is not supported on PSP]) + fi + AC_DEFINE(HAVE_C68K) +fi +AM_CONDITIONAL(COMPILE_C68K, test x$compile_c68k = xyes) + +# Q68 emulator is optional (but required on PSP) +if test "x$yabause_arch" = "xpsp"; then + compile_q68=yes +else + compile_q68=no +fi +AC_ARG_WITH(q68, AC_HELP_STRING(--with-q68, [include Q68 68k emulator (requires a C99-compliant compiler like GCC)]), [compile_q68=$withval]) +if test "x$compile_q68" = "xyes"; then + AC_DEFINE(HAVE_Q68) +elif test "x$yabause_arch" = "xpsp"; then + AC_MSG_ERROR([Q68 is required on PSP]) +fi +AM_CONDITIONAL(COMPILE_Q68, test "x$compile_q68" = "xyes") + +# JIT for Q68 can be disabled (and is automatically disabled on unsupported +# systems) +q68_use_jit=maybe +AC_ARG_ENABLE(q68-jit, AC_HELP_STRING(--disable-q68-jit, [disable dynamic (Just-In-Time) translation for Q68]), [q68_use_jit=$enableval]) +case $yabause_cpu in + x86|x64|psp) + if test "x$q68_use_jit" = "xmaybe"; then + q68_use_jit=yes + fi + ;; + *) + if test "x$q68_use_jit" = "xyes"; then + AC_MSG_ERROR([Q68 dynamic translation is not supported on this CPU]); + elif test "x$q68_use_jit" = "xmaybe"; then + AC_MSG_WARN([Disabling Q68 dynamic translation (not supported on this CPU)]); + fi + ;; +esac +if test "x$q68_use_jit" = "xyes"; then + AC_DEFINE(Q68_USE_JIT) +fi +AM_CONDITIONAL([Q68_USE_JIT], test "x$q68_use_jit" = "xyes") + +# Allow disabling of dynarec +AC_ARG_ENABLE(dynarec, AC_HELP_STRING(--disable-dynarec, [Disable dynarec core]), [], [use_dynarec=yes]) + +if test "x$use_dynarec" = "xyes"; then + AC_DEFINE(USE_DYNAREC) +fi + +AM_CONDITIONAL([USE_DYNAREC], test "x$use_dynarec" = "xyes") + +################################################################################# +# # +# phase 2, we're done with Yabause library, now we're tring to configure ports # +# # +################################################################################# + +# qt +AC_PATH_PROGS(HAVE_QMAKE, [qmake-qt4 qmake]) + +if test ! x$HAVE_QMAKE = x ; then + yabause_available_yuis="qt $yabause_available_yuis" +fi + +# gtk +want_gtk=yes +AC_ARG_WITH(gtk, AC_HELP_STRING(--without-gtk, don't try to configure the gtk port), [want_gtk=$withval]) + +YAB_CHECK_HOST_TOOLS(HAVE_PKG, [pkg-config]) +if test ! x$HAVE_PKG = x ; then + if test "x$want_gtk" = "xyes" && `$HAVE_PKG gtk+-2.0` ; then + if test "x$use_opengl" = "xyes" ; then + if `$HAVE_PKG gtkglext-1.0` ; then + yabause_available_yuis="gtk $yabause_available_yuis" + YUI_gtk_CFLAGS=`$HAVE_PKG gtkglext-1.0 --cflags` + YUI_gtk_LIBS=`$HAVE_PKG gtkglext-1.0 --libs` + AC_DEFINE(HAVE_LIBGTKGLEXT) + else + AC_MSG_NOTICE([Found OpenGL and Gtk+ but not libgtkglext.]) + AC_MSG_NOTICE([You can either:]) + AC_MSG_NOTICE([- install libgtkglext to compile a gtk port with OpenGL support]) + AC_MSG_NOTICE([- re-run configure with --without-opengl flag to compile a gtk port without OpenGL support]) + AC_MSG_NOTICE([- re-run configure with --without-gtk flag to disable gtk port compilation]) + AC_MSG_ERROR([Can't go further, please install libgtkglext or re-run configure with --without-opengl or --without-gtk]) + fi + else + yabause_available_yuis="gtk $yabause_available_yuis" + YUI_gtk_CFLAGS=`$HAVE_PKG gtk+-2.0 --cflags` + YUI_gtk_LIBS=`$HAVE_PKG gtk+-2.0 --libs` + fi + fi +fi + +# carbon +OLDLDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -framework Carbon" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + int t(void) { return 0; } + ]],[[ + int foo = t(); + ]])],[YUI_carbon_LIBS="-framework Carbon -framework AGL" + yabause_available_yuis="carbon $yabause_available_yuis"], []) +LDFLAGS="$OLDLDFLAGS" + +# windows +YAB_CHECK_HOST_TOOLS(WINDRES, [windres]) +AC_CHECK_HEADER([windows.h], [yabause_available_yuis="windows $yabause_available_yuis"], []) + +# wii +if test "x$host_alias" = "xpowerpc-gekko" ; then + if test \( "x$LIBOGC" = "x" \) -a \( ! "x$DEVKITPRO" = "x" \) ; then + LIBOGC="$DEVKITPRO/libogc" + fi + if test "x$LIBOGC" = "x" ; then + AC_MSG_ERROR([Please set the LIBOGC variable]) + else + CPPFLAGS="-I$LIBOGC/include $CPPFLAGS" + LDFLAGS="-L$LIBOGC/lib/wii $LDFLAGS" + CFLAGS="-mrvl -mcpu=750 -meabi -mhard-float $CFLAGS" + LIBS="-lfat -lwiiuse -lbte -logc -lm $LIBS" + fi + + AC_DEFINE(GEKKO) + + yabause_available_yuis="wii" +fi + +# PSP +if test "x$host_alias" = "xpsp" ; then + AC_DEFINE(PSP) + yabause_available_yuis="psp" +fi + +# adding . as a fallback when no other port is available +yabause_available_yuis="$yabause_available_yuis ." + +yabause_yui=`echo $yabause_available_yuis | cut -d\ -f1` + +AC_ARG_WITH([port], AC_HELP_STRING(--with-port, choose your port), [yabause_manual_yui=$withval]) +for yabause_available_yui in $yabause_available_yuis; do + if test x$yabause_available_yui = x$yabause_manual_yui; then + yabause_yui=$yabause_manual_yui + fi +done + +if ! test "x$yabause_yui" = "x." ; then + eval YAB_CFLAGS=\$YUI_${yabause_yui}_CFLAGS + eval YAB_LIBS=\$YUI_${yabause_yui}_LIBS + AC_SUBST(YAB_CFLAGS) + AC_SUBST(YAB_LIBS) +fi + +AC_SUBST(yabause_yui) + + +AC_ARG_ENABLE(debug, AC_HELP_STRING(--enable-debug, enable general debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(DEBUG) + fi]) +AC_ARG_ENABLE(vdp1-debug, AC_HELP_STRING(--enable-vdp1-debug, enable vdp1 debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(VDP1_DEBUG) + fi]) +AC_ARG_ENABLE(vdp2-debug, AC_HELP_STRING(--enable-vdp2-debug, enable vdp2 debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(VDP2_DEBUG) + fi]) +AC_ARG_ENABLE(cd-debug, AC_HELP_STRING(--enable-cd-debug, enable cdblock debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(CDDEBUG) + fi]) +AC_ARG_ENABLE(smpc-debug, AC_HELP_STRING(--enable-smpc-debug, enable smpc debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(SMPC_DEBUG) + fi]) +AC_ARG_ENABLE(scsp-debug, AC_HELP_STRING(--enable-scsp-debug, enable scsp debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(SCSP_DEBUG) + fi]) +AC_ARG_ENABLE(idle-debug, AC_HELP_STRING(--enable-idle-debug, enable idle cpu debug information) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(IDLE_DETECT_VERBOSE) + fi]) +AC_ARG_ENABLE(mic-shaders, AC_HELP_STRING(--enable-mic-shaders, enable OpenGL shaders for gouraud and mesh) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(USEMICSHADERS) + fi]) +AC_ARG_ENABLE(network, AC_HELP_STRING(--enable-network, enable network) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(USESOCKET) + fi]) +AC_ARG_ENABLE(perkeyname, AC_HELP_STRING(--enable-perkeyname, use peripheral key name callback) , + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(PERKEYNAME) + fi]) +AC_ARG_ENABLE(exec-from-cache, AC_HELP_STRING(--enable-exec-from-cache, [allow code execution from 0xC0000000]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(EXEC_FROM_CACHE) + fi]) +AC_ARG_ENABLE(optimized-dma, AC_HELP_STRING(--enable-optimized-dma, [use optimized DMA when possible]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(OPTIMIZED_DMA) + fi]) +AC_ARG_ENABLE(new-scsp, AC_HELP_STRING(--enable-new-scsp, [enable experimental new SCSP implementation]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE(USE_SCSP2) + fi]) +AM_CONDITIONAL([USE_SCSP2], [test "${enable_new_scsp}" = "yes"]) + + +#### PSP options + +AC_ARG_ENABLE(psp-debug, AC_HELP_STRING(--enable-psp-debug, [enable PSP debugging output]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE([PSP_DEBUG]) + fi]) + +AC_ARG_ENABLE(psp-profile, AC_HELP_STRING(--enable-psp-profile, [enable profiling on PSP port]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE([SYS_PROFILE_H], ["psp/profile.h"]) + fi]) + +AC_ARG_WITH(psp-me-test, AC_HELP_STRING(--with-psp-me-test, [build ME library test program])) +AM_CONDITIONAL([BUILD_ME_TEST], [test "${with_psp_me_test}" = "yes"]) + +AC_ARG_ENABLE(debug-psp-sh2, AC_HELP_STRING(--enable-debug-psp-sh2, [include PSP SH-2 core for testing]), + [if test "x$enableval" = "xyes" ; then + AC_DEFINE([TEST_PSP_SH2]) + fi]) +AM_CONDITIONAL([TEST_PSP_SH2], [test "${enable_debug_psp_sh2}" = "yes"]) + +### End PSP options + +AC_CONFIG_FILES([Makefile + l10n/Makefile + doc/Doxyfile + src/Makefile + src/carbon/Makefile + src/dreamcast/Makefile + src/gtk/Makefile + src/gtk/doc/Makefile + src/psp/Makefile + src/qt/Makefile + src/qt/yabause.pro + src/qt/doc/Makefile + src/wii/Makefile + src/windows/Makefile + autopackage/default.apspec +]) +if test x$yabause_yui = xqt ; then + case $host in + *mingw*) + case $build in + *linux*) + qmake_spec="-win32 -spec mkspecs/win32-x11-g++" + ;; + *darwin*) + qmake_spec="-win32 -spec mkspecs/win32-osx-g++" + ;; + *) + if test "x$cross_compiling" = "xyes" ; then + AC_MSG_ERROR([cross-compiling $host port on $build is not supported yet]) + fi + ;; + esac + ;; + *darwin*) + case $build in + *darwin*) + qmake_spec="-spec macx-g++" + ;; + *) + AC_MSG_ERROR([cross-compiling $host port on $build is not supported yet]) + ;; + esac + ;; + *) + if test "x$cross_compiling" = "xyes" ; then + AC_MSG_ERROR([cross-compiling $host port on $build is not supported yet]) + fi + ;; + esac + AC_CONFIG_FILES([src/qt/Makefile.qmake:src/qt/yabause.pro], + [( cd src/qt && $QMAKE yabause.pro $QMAKE_SPEC -o Makefile.qmake )], + [QMAKE=$HAVE_QMAKE QMAKE_SPEC="$qmake_spec"]) +fi + +AC_CONFIG_COMMANDS([src/c68k/Makefile], [( cd src/c68k/ && $CONFIG_SHELL ${ac_srcdir}/configure )]) + +AM_CONDITIONAL([YUI_IS_CARBON], [test ${yabause_yui} = "carbon"]) +AM_CONDITIONAL([YUI_IS_DREAMCAST], [test ${yabause_yui} = "dreamcast"]) +AM_CONDITIONAL([YUI_IS_GTK], [test ${yabause_yui} = "gtk"]) +AM_CONDITIONAL([YUI_IS_PSP], [test ${yabause_yui} = "psp"]) +AM_CONDITIONAL([YUI_IS_QT], [test ${yabause_yui} = "qt"]) +AM_CONDITIONAL([YUI_IS_WII], [test ${yabause_yui} = "wii"]) +AM_CONDITIONAL([YUI_IS_WINDOWS], [test ${yabause_yui} = "windows"]) + +AM_CONDITIONAL([ARCH_IS_FREEBSD], [test ${yabause_arch} = "freebsd"]) +AM_CONDITIONAL([ARCH_IS_LINUX], [test ${yabause_arch} = "linux"]) +AM_CONDITIONAL([ARCH_IS_MACOSX], [test ${yabause_arch} = "macosx"]) +AM_CONDITIONAL([ARCH_IS_NETBSD], [test ${yabause_arch} = "netbsd"]) +AM_CONDITIONAL([ARCH_IS_WINDOWS], [test ${yabause_arch} = "windows"]) + +AM_CONDITIONAL([CPU_IS_ARM], [test ${yabause_cpu} = "arm"]) +AM_CONDITIONAL([CPU_IS_X86], [test ${yabause_cpu} = "x86"]) +AM_CONDITIONAL([CPU_IS_X64], [test ${yabause_cpu} = "x64"]) +AM_CONDITIONAL([CPU_IS_PSP], [test ${yabause_cpu} = "psp"]) + +AC_OUTPUT + +echo "==================" +echo "WARNING" +echo +echo "Compiling Yabause with autootols is deprecated" +echo +echo "Please use CMake instead" +echo +echo "==================" +echo "configure report" +echo +echo "available ports: $yabause_available_yuis" +echo "selected port: $yabause_yui" +echo "==================" diff --git a/yabause/doc/CMakeLists.txt b/yabause/doc/CMakeLists.txt new file mode 100644 index 0000000000..ebd9fec1f3 --- /dev/null +++ b/yabause/doc/CMakeLists.txt @@ -0,0 +1,11 @@ +project(yabause-doc) + +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation with Doxygen" VERBATIM + ) +endif(DOXYGEN_FOUND) diff --git a/yabause/doc/Doxyfile.in b/yabause/doc/Doxyfile.in new file mode 100644 index 0000000000..2f1988b034 --- /dev/null +++ b/yabause/doc/Doxyfile.in @@ -0,0 +1,1356 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Yabause + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@/.. + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @DOXYGEN_DOT_FOUND@ + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/yabause/l10n/CMakeLists.txt b/yabause/l10n/CMakeLists.txt new file mode 100644 index 0000000000..c18f927dbf --- /dev/null +++ b/yabause/l10n/CMakeLists.txt @@ -0,0 +1,13 @@ +project(yabause-l10n) + +set(LANGS de es fr it lt pt pt_BR sv) + +if (UNIX AND NOT APPLE) + foreach(LANG ${LANGS}) + install(FILES "yabause_${LANG}.yts" DESTINATION "share/yabause/yts" RENAME "${LANG}.yts") + endforeach() +elseif (WIN32) + foreach(LANG ${LANGS}) + install(FILES "yabause_${LANG}.yts" DESTINATION "trans" RENAME "${LANG}.yts") + endforeach() +endif () diff --git a/yabause/l10n/Makefile.am b/yabause/l10n/Makefile.am new file mode 100644 index 0000000000..4375084617 --- /dev/null +++ b/yabause/l10n/Makefile.am @@ -0,0 +1,19 @@ +LANGS=de es fr it lt pt pt_BR sv + +dist-hook: + @for l in $(LANGS) ; do \ + cp -p "$(srcdir)/$(PACKAGE)_$$l.yts" "$(distdir)/$(PACKAGE)_$$l.yts" ; \ + done + +install-data-hook: + test -z "$(DESTDIR)$(datadir)/$(PACKAGE)/yts" || $(MKDIR_P) "$(DESTDIR)$(datadir)/$(PACKAGE)/yts" + @for l in $(LANGS) ; do \ + echo " $(INSTALL_DATA) $(PACKAGE)_$$l.yts $(DESTDIR)$(datadir)/$(PACKAGE)/yts/$$l.yts" ; \ + $(INSTALL_DATA) "$(srcdir)/$(PACKAGE)_$$l.yts" "$(DESTDIR)$(datadir)/$(PACKAGE)/yts/$$l.yts" ; \ + done + +uninstall-hook: + @for l in $(LANGS) ; do \ + echo " rm -f $(DESTDIR)$(datadir)/$(PACKAGE)/yts/$$l.yts" ; \ + rm -f "$(DESTDIR)$(datadir)/$(PACKAGE)/yts/$$l.yts" ; \ + done diff --git a/yabause/l10n/yabause_de.yts b/yabause/l10n/yabause_de.yts new file mode 100644 index 0000000000..c75de6a581 --- /dev/null +++ b/yabause/l10n/yabause_de.yts @@ -0,0 +1,195 @@ +%1/%2 blocks free|%1/%2 blöcke frei +%1 Images (*.%2)|Bilder %1 (*.%2) +503/512 blocks free|503/512 blöcke frei +510/512 blocks free|510/512 blöcke frei +About...|Über... +&About...|&Ü Über... +About|Über +&Action Replay|&Action Replay +Action Replay Code :|Action Replay Code : +Add Action Replay Code|Action Replay Code zufügen +Add Codes...|Codes zufügen... +Add Raw Memory Code|Raw Memory Code zufügen +Address :|Adresse : +Advanced|Sonstiges +| +Are you sure you want to delete '%1' ?|Sind Sie sicher, dass Sie '%1' löschen möchten ? +Are you sure you want to format '%1' ?|Sind Sie sicher, dass Sie '%1' formatieren möchten ? +Backup Manager|Backup Manager +Backup Manager...|Backup Manager... +&Backup Ram Manager|&Backup Ram Manager +Backup Ram Manager|Backup Ram Manager +Bios|Bios +Block Size :|Blockgröße : +&Browse|&Browse +Browse|Suchen +Byte Write|Byte schreiben +Cancel|Abbrechen +Cannot initialize|Initialisierung fehlgeschlagen +&Capture Screen|&Capture Screen +Cart/Memory|Cartridge/Speicher +Cartridge|Cartridge +CD Images (*.iso *.cue *.bin)|CD Images (*.iso *.cue *.bin) +Cd-Rom|Cd-Rom +&Cheats|&Cheats +Cheats|Cheats +Cheats File|Cheatdatei +&Cheat List|&Cheat List +Cheats List|Cheatliste +Cheats List...|Cheatliste... +Cheat Type|Cheattyp +Choose a cdrom drive/mount point|CD-Rom Laufwerk/Mount-Punkt auswählen +Choose a cheat file to open|Cheatdatei zum öffnen auswählen +Choose a cheat file to save to|Cheatdatei zum speichern auswählen +Choose a file to save your state|Datei zum speichern des Status auswählen +Choose a location for your screenshot|Speicherort für das Bildschirmfoto auswählen +&Clear|&Leeren +Close|Schliessen +Code|Code +Comment :|Kommentar : +Data Size :|Datengrösse : +_Debug|_Debug +&Debug|&Debug +&Delete|&Löschen +Delete|Löschen +Description|Beschreibung +Description :|Beschreibung : +Device List|Geräteliste +Disabled|Deaktiviert +Down|Runter +Download|Herunterladen +Emu-Compatibility|Emu-Kombatibilität +Emulation|Emulation +End Address:|Adressende +Enable|Aktivieren +Enabled|Aktiviert +English|Englisch +&File|&Datei +File|Datei +File :|File : +File Name :|Dateiname : +File transfer|Dateiübertragung +WARNING: Master Codes are NOT supported.|WARNUNG :Mastercodes werden NICHT unterstützt. +Format|Formatieren +FPS|BPS +Frame Skip/Limiter|Frames überspringen/limitieren +French|Französisch +From|Von +From File|Von Datei +From File...|Von Datei... +&Fullscreen|&Vollbild +Fullscreen|Vollbild +General|Allgemein +German|Deutsch +Hard Reset|Hard Reset +Height|Höhe +_Help|_Hilfe +&Help|&Hilfe +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=de +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Eingabe +Invalid Address|Ungültige Adresse +Invalid Value|Ungültiger Wert +Italian|Italienisch +Japanese|Japanisch +Language :|Sprache : +&Layer|&Schicht +Layer|Schicht +Left|Links +Left trigger|Linker Trigger +Load as executable|Als ausführbar laden +Load|Laden +&Load From File|&Von Datei laden +Load State|Spielstand laden +Load State As|Spielstand laden als +Log|Protokoll +Long Write|Long schreiben +M68K|M68K +Memory dump|Speicherabzug +Memory Dump|Speicherabzug +Memory Editor|Memory Editor +&Memory Transfer|&Memory Transfer +Memory Transfer|Memory Transfer +Memory|Speicher +Mpeg ROM|Mpeg ROM +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Ok|Ok +&OK|&OK +Open CD Rom|CD-Rom öffnen +Open CD Rom...|CD-Rom öffnen... +Open ISO|ISO öffnen +Open ISO...|ISO öffnen... +&Pause|&Pause +Pause|Pause +&Quit|&Schliessen +Quit|Schliessen +&Raw Memory Address|&Raw Speicheradresse +RBG0|RBG0 +Region|Region +&Reset|&Zurücksetzen +Reset|Zurücksetzen +Resolution|Auflösung +Right|Rechts +Right trigger|Rechter Trigger +R&un|S&tarten +Run|Starten +Save Information|Informationen speichern +Save List|Liste speichern +Save|Speichern +Save State|Status speichern +Save State As|Save State As +Save States|Stati speichern +&Save To File|&In Datei speichern +Screenshot|Bilschirmfoto +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Wählen Sie die Statusdatei aus +Select your iso/cue/bin file|Wählen Sie Ihre iso/cue/bin-Datei +&Settings...|Einstellungen... +Settings|Einstellungen +SH2 Interpreter|SH2 Interpreter +Sound Core|Sound Kern +Sound|Sound +Spanish|Spanisch +SSH2|SSH2 +Start|Start +Start Address:|Adressanfang +Status|Status +Store|Sichern +To File|in Datei +To File...|in Datei... +toolBar|Werkzeugleiste +Tools|Tools +To|zu +Transfer|Transferieren +Translation|Übersetzung +Unable to add code|Code konnte nicht zugefügt werden +Unable to change description|Beschreibung konnte nicht geändert werden +Unable to open file for loading|Datei konnte nicht geöffnet werden +Unable to open file for saving|Datei konnte nicht geschrieben werden +Unable to remove code|Code konnte nicht entfernt werden +Unknow (%1)|Unbekannt (%1) +Up|Hoch +Upload|Upload +Value :|Wert : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Video Kern +Video Driver|Video Treiber +Video Format|Video Format +Video|Video +_View|_Sicht +&View|&Sicht +Waiting Input...|Warte auf EIngaben... +Width|Breite +Word Write|Word schreiben +Yabause Cheat Files (*.yct);;All Files (*)|Yabause Cheat Dateien (*ycf);;Alle Dateien (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO +Yabause Qt GUI|Qt Grafik-Interface für Yabause +Yabause Save State (*.yss)|Yabause Speicherstände (*.yss) diff --git a/yabause/l10n/yabause_es.yts b/yabause/l10n/yabause_es.yts new file mode 100644 index 0000000000..aa10478704 --- /dev/null +++ b/yabause/l10n/yabause_es.yts @@ -0,0 +1,198 @@ +%1/%2 blocks free|%1/%2 bloques libres +%1 Images (*.%2)|%1 Imágenes (*.%2) +503/512 blocks free|503/512 bloques libres +510/512 blocks free|510/512 bloques libres +About...|Acerca de... +&About...|&Acerca de... +About|Acerca de +&Action Replay|&Action Replay +Action Replay Code :|Código de Action Replay +Add Action Replay Code|Añadir Código de Action Replay +Add Codes...|Añadir Códigos... +Add Raw Memory Code|Añadir código dirección de memoria +Address :|Dirección : +Advanced|Avanzado +| +Are you sure you want to delete '%1' ?|¿Estás seguro de borrar '%1' ? +Are you sure you want to format '%1' ?|¿Estás seguro de formatear '%1' ? +Backup Manager|Gestor de memoria de backup +Backup Manager...|Gestor de memoria de backup... +&Backup Ram Manager|Gestor de RAM de &backup +Backup Ram Manager|Gestor de RAM de backup +Bios|Bios +Block Size :|Tamaño de bloque: +&Browse|&Examinar +Browse|Examinar +Byte Write|Escritura de byte +Cancel|Cancelar +Cannot initialize|Imposible iniciar +&Capture Screen|&Capturar pantalla +Cart/Memory|Cartucho/Memoria +Cartridge|Cartucho +CD Images (*.iso *.cue *.bin)|Imágenes de CD (*.iso *.cue *.bin) +Cd-Rom|Cd-Rom +&Cheats|&Trucos +Cheats|Trucos +Cheats File|Archivo de trucos +&Cheat List|Listado de &Trucos +Cheats List|Lista de trucos +Cheats List...|Lista de trucos... +Cheat Type|Tipo de truco +Choose a cdrom drive/mount point|Selecciona una unidad de cdrom/punto de montaje +Choose a cheat file to open|Selecciona un archivo de trucos para abrir +Choose a cheat file to save to|Selecciona el archivo de trucos para guardar +Choose a file to save your state|Selecciona un archivo para guardar tu estado +Choose a location for your screenshot|Selecciona ubicación para tu captura +&Clear|&Limpiar +Close|Cerrar +Code|Código +Comment :|Comentario : +Data Size :|Tamaño de datos : +_Debug|_Debug +&Debug|&Debug +&Delete|&Borrar +Delete|Borrar +Description|Descripción +Description :|Descripción : +Device List|Lista de Dispositivos +Disabled|Desactivado +Down|Abajo +Download|Descargar +Emu-Compatibility|Compatibilidad del emulador +Emulation|Emulación +End Address:|Dirección final: +Enable|Activar +Enabled|Activado +English|Inglés +&File|&Archivo +File|Archivo +File :|Archivo: +File Name :|Nombre de archivo : +File transfer|Transferencia de archivo +WARNING: Master Codes are NOT supported.|AVISO: Códigos Maestros NO soportados. +Format|Formato +FPS|FPS +Frame Skip/Limiter|Salto de cuadros/Limitador +French|Francés +From|De +From File|De archivo +From File...|De archivo... +&Fullscreen|&Pantalla completa +Fullscreen|Pantalla completa +General|General +German|Alemán +Hard Reset|Reiniciar en frío +Height|Altura +_Help|_Ayuda +&Help|&Ayuda +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Entrada +Invalid Address|Dirección inválida +Invalid Value|Valor inválido +Italian|Italiano +Japanese|Japonés +Language :|Idioma : +&Layer|&Capa +Layer|Capa +Left|Izquierda +Left trigger|Gatillo izquierdo +Load as executable|Cargar como ejecutable +Load|Cargar +&Load From File|&Cargar desde archivo +Load State|Cargar estado +Load State As|Cargar estado como +Log|Log +Long Write|Escritura larga +M68K|M68K +Memory dump|Volcado de memoria +Memory Dump|Volcado de Memoria +Memory Editor|Editor de memoria +&Memory Transfer|Transferir &memoria +Memory Transfer|Transferir memoria +Memory|Memoria +Mpeg ROM|ROM Mpeg +Mouse|Ratón +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +None|Ninguno +Ok|Ok +&OK|&OK +Open CD Rom|Abrir CD Rom +Open CD Rom...|Abrir CD Rom... +Open ISO|Abrir ISO +Open ISO...|Abrir ISO... +Pad|Pad +&Pause|&Pausar +Pause|Pausar +&Quit|&Sair +Quit|Sair +&Raw Memory Address|&Dirección de memoria Raw +RBG0|RBG0 +Region|Región +&Reset|&Resetear +Reset|Resetear +Resolution|Resolución +Right|Derecha +Right trigger|Gatillo derecho +R&un|E&jecutar +Run|Ejecutar +Save Information|Guardar informes +Save List|Lista de salvaguardados +Save|Salvar +Save State|Guardar Estado +Save State As|Guardar estado como +Save States|Guardar estados +&Save To File|&Guardar a archivo +Screenshot|Captura de pantalla +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Selecciona el archivo para cargar tu estado +Select your iso/cue/bin file|Selecciona tu archivo iso/cue/bin +&Settings...|&Configuración... +Settings|Configuración +SH2 Interpreter|Intérprete SH2 +Sound Core|Núcleo de sonido +Sound|Sonido +Spanish|Español +SSH2|SSH2 +Start|Iniciar +Start Address:|Dirección inicial: +Status|Estado +Store|Almacenar +To File|A archivo +To File...|A archivo... +toolBar|Barra de herramientas +Tools|Herramientas +To|Para +Transfer|Transferencia +Translation|Traducción +Unable to add code|Imposible añadir código +Unable to change description|Imposible cambiar descripción +Unable to open file for loading|Imposible abrir archivo para cargar +Unable to open file for saving|Imposible abrir archivo para guardar +Unable to remove code|Imposible eliminar código +Unknow (%1)|Desconocido (%1) +Up|Arriba +Upload|Subir +Value :|Valor : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Núcleo de Vídeo +Video Driver|Driver de Vídeo +Video Format|Formato de Vídeo +Video|Vídeo +_View|_Ver +&View|&Ver +Waiting Input...|Esperando Entrada de Datos... +Width|Ancho +Word Write|Escribir WORD +Yabause Cheat Files (*.yct);;All Files (*)|Archivos de trucos para Yabause (*.yct);;Todos los archivos (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Interfaz gráfica QT
Basada en Yabause 0.9.5
http://yabause.org
El equipo de Yabause
Filipe AZEVEDO +Yabause Qt GUI|Interfaz gráfica Qt +Yabause Save State (*.yss)|Salvaguardados de Yabause (*.yss) diff --git a/yabause/l10n/yabause_fr.yts b/yabause/l10n/yabause_fr.yts new file mode 100644 index 0000000000..799bd817ed --- /dev/null +++ b/yabause/l10n/yabause_fr.yts @@ -0,0 +1,276 @@ +%1/%2 blocks free|%1/%2 blocs libre +%1 Images (*.%2)|Images %1 (*.%2) +503/512 blocks free|503/512 blocs libre +510/512 blocks free|510/512 blocs libre +About...|A Propos De... +&About...|&A Propos De... +About|A Propos De +&Action Replay|&Action Replay +Action Replay Code :|Code Action Replay : +Add Action Replay Code|Ajouter un Code Action Replay +Add Cheat|Ajouter un Cheat +Add Codes...|Ajouter un Code... +Add Raw Memory Code|Ajouter un Code Mémoire Brut +Address|Adresse +Address :|Adresse : +Advanced|Avancé +Always|Toujours +Asia (NTSC)|Asie (NTSC) +Asia (PAL)|Asie (PAL) +Are you sure you want to delete '%1' ?|Etes vous sûr de vouloir supprimer '%1' ? +Are you sure you want to format '%1' ?|Etes vous sûr de vouloir formater '%1' ? +Auto-detect|Détection Auto +Autostart|Démarrage Automatique +Backup Manager|Gestion des Sauvegardes +&Backup Manager...|&Gestion des Sauvegardes... +Backup Manager...|Gestion des Sauvegardes... +&Backup Ram Manager|&Gestion des Sauvegardes +Backup Ram Manager|Gestion des Sauvegardes +Bios|Bios : +Block Size :|Taille du Bloc : +&Browse|&Parcourir +Browse|Parcourir +Byte Write|Ecrire un Byte +&Cancel|&Annuler +Cancel|Annuler +Cannot initialize|Ne peut pas Initialiser +Cannot initialize Windows SPTI Driver|Ne peut pas initialiser le pilote SPTI +&Capture Screen|&Capture d'Ecran +Cart/Memory|Cartouche/Mémoire +Cartridge|Cartouche : +CD Images (*.iso *.cue *.bin)|Images CD (*.iso *.cue *.bin) +Cd-Rom|CD-Rom : +Central/South America (NTSC)|Amérique du Sud/Centrale (NTSC) +Central/South America (PAL)|Amérique du Sud/Centrale (PAL) +&Cheats|&Cheats +Cheats|Cheats +Cheats File|Fichier Cheat +&Cheat List|Liste de &Cheat +Cheats List|Liste de Cheat +&Cheats List...|Liste de &Cheat... +Cheats List...|Liste de Cheat... +Cheat &Search...|&Rechercher un Cheat... +Cheat Search|Rechercher un Cheat +Cheat Type|Type de Cheat +Choose a cdrom drive/mount point|Choisir un lecteur CD ou un point de montage +Choose a cheat file to open|Choisir un fichier de Cheat à ouvrir +Choose a cheat file to save to|Choisir un fichier de Cheat à enregistrer +Choose a file to save your state|Choisir un fichier où enregistrer l'état +Choose a location for your screenshot|Choisir un nom de fichier pour votre capture d'écran +&Clear|&Effacer +Clear configuration|Vider la Configuration +&Close|&Fermer +Close|Fermer +Code|Code +Comment :|Commentaire : +Compare Type|Type de Comparaison +Data Size|Taille Donnée +Data Size :|Taille des Données : +Data Type|Type Donnée +_Debug|_Débug +&Debug|&Débug +&Delete|&Effacer +Delete|Effacer +Description|Description +Description :|Description : +Device List|Liste des Périphériques +Disabled|Désactivé +Down|Bas +Download|Télécharger +Dummy CD Drive|Lecteur de CD Factice +Dummy Input Interface|Interface Contrôleur Factice +Dummy OSD Interface|Interface OSD Factice +Dummy Sound Interface|Interface Son Factice +Dummy Video Interface|Interface Vidéo Factice +Edit configuration|Editer la Configuration +Emu-Compatibility|Emu-Compatibility +&Emulation|&Emulation +Emulation|Emulation +End Address:|Adresse de Fin : +Enable|Activer +Enable Frame Skip/Limiter|Active le Saut d'Image/Limitateur +Enabled|Activé +English|Anglais +Europe + others (PAL)|Europe + Autres (PAL) +Exact|Identique +&File|&Fichier +File|Fichier +File :|Fichier : +File:|Fichier : +File Name :|Nom du Fichier : +File transfer|Transfert de Fichier +WARNING: Master Codes are NOT supported.|AVERTISSEMENT : Les Codes Maîtres NE SONT PAS supportés. +Format|Formater +FPS|FPS +Frame Skip/Limiter|Saut d'Image/Limitateur +&Frame Skip/Limiter|&Saut d'Image/Limitateur +French|Français +From|A Partir De +From File|A Partir d'un Fichier +From File...|A Partir d'un Fichier... +&Fullscreen|&Plein Ecran +Fullscreen|Plein Ecran +General|Général +German|Allemand +Glut OSD Interface|Interface OSD GLut +Greater then|Plus grand que +Hard Reset|Redémarrage Matériel +Height|Hauteur : +_Help|_Aide +&Help|&Aide +Hide menubar|Cacher la Barre de Menu +Hide toolbar|Cacher la Barre d'Outils +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=fr +http://www.monkeystudio.org|http://www.monkeystudio.org +Information...|Information... +Input|Contrôleur +Invalid Address|Adresse Invalide +Invalid Value|Valeur Invalide +ISO-File Virtual Drive|Lecteur de Fichier ISO Virtuel +Italian|Italien +Japan (NTSC)|Japon (NTSC) +Japanese|Japonais +Korea (NTSC)|Corée (NTSC) +Language :|Langage : +&Layer|&Couche +Layer|Couche +Left|Gauche +Left trigger|Gâchette Gauche +Less than|Moins que +Linux CD Drive|Lecteur CD Linux +Load as executable|Charger en tant qu'Executable +Load|Charger +&Load From File|&Charger à Partir d'un Fichier +L&oad State|&Charger un Etat +Load State|Charger l'Etat +Load State As|Charger l'Etat sous +&Log|&Journal +Log|Journal +Long Write|Ecrire un Long +M68K|M68K +&Master SH2|&Master SH2 +Master SH2|Master SH2 +Memory dump|Copier la mémoire +Memory Dump|Copier la Mémoire +Memory &Editor|&Editeur de Mémoire +Memory Editor|Editeur de Mémoire +&Memory Transfer|&Transfert de Mémoire +Memory Transfer|Transfert de Mémoire +Memory|Mémoire : +Mpeg ROM|ROM MPEG : +Mouse|Souris +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Never|Jamais +None|Aucun +North America (NTSC)|Amérique du Nord (NTSC) +Ok|Ok +&OK|&OK +On fullscreen|Plein Ecran +Open CD Rom|Ouvrir un CD ROM +Open &CD Rom...|Ouvrir un &CD Rom... +Open CD Rom...|Ouvrir un CD ROM... +Open ISO|Ouvrir un Fichier ISO +Open &ISO...|Ouvrir un &ISO... +Open ISO...|Ouvrir un Fichier ISO... +OpenGL Video Interface|Interface Vidéo OpenGL +OSD Core|Noyau OSD +Pad|Manette +Pad Configuration|Configuration de la Manette +&Pause|&Pause +Pause|Pause +Qt Keyboard Input Interface|Interface Qt du Clavier +&Quit|&Quitter +Quit|Quitter +&Raw Memory Address|Adresse Mémoire &Brut +RBG0|RBG0 +Region|Région : +&Reset|&Redémarrer +Reset|Redémarrer +Resolution|Résolution : +Right|Droite +Right trigger|Gâchette Droite +R&un|L&ancer +Run|Lancer +Save Information|Informations sur la Sauvegarde +Save List|Liste des Sauvegardes +Save|Sauvegarder +S&ave State|S&auvegarder un Etat +Save State|Sauvegarder l'Etat +Save State As|Sauvegarder l'Etat sous +Save States|Sauvegarde d'Etat : +&Save To File|&Sauvegarder Vers le Fichier +Sc&reenshot|Copie d'Ec&ran +Screenshot|Capture d'Ecran +SCSP|SCSP +SCU-DSP|SCU-DSP +SDL Joystick Interface|Interface SDL du Joystick +SDL Sound Interface|Interface SDL du Son +Search|Rechercher +Search/Add Cheats|Rechercher/Ajouter un Cheat +Search Value:|Rechercher une Valeur : +Select a file to load your state|Choisissez un fichier pour charger l'état +Select your iso/cue/bin file|Choisissez votre fichier iso/cue/bin +Set PC to Start Address|Sélection PC vers l'Adresse de Démarrage +&Settings...|&Paramétres... +Settings|Paramétres +SH2 Debugger Interpreter|Interpréteur de Débugeur SH2 +SH2 Dynamic Recompiler|Recompileur Dynamique SH2 +SH2 Interpreter|Interpréteur SH2 : +Show FPS|Afficher les FPS +&Slave SH2|&Slave SH2 +Slave SH2|Slave SH2 +Software Video Interface|Interface Vidéo Logiciel +Sound Core|Noyau Son : +Sound|Son +Spanish|Espagnol +SSH2|SSH2 +Signed|Signé +Software OSD Interface|Interface OSD Logiciel +Start|Démarrer +Start Address:|Adresse de Début : +Status|Statut +Store|Stocker +To File|Vers un Fichier +To File...|Vers un Fichier... +toolBar|Barre d'Outils +&Tools|Ou&tils +Tools|Outils +To|Vers +&Transfer|&Transfert +Transfer|Transfert +Translation|Traduction : +Unable to add code|Ne peut pas ajouter de Code +Unable to change description|Ne peut pas changer la Description +Unable to open file for loading|Ne peut pas ouvrir le fichier à Charger +Unable to open file for saving|Ne peut pas ouvrir le fichier à sauvegarder +Unable to remove code|Ne peut pas enlever le Code +Unknow (%1)|Inconnu (%1) +Unsigned|Non Signé +Up|Haut +Upload|Upload +Value|Valeur +Value :|Valeur : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Noyau Vidéo : +Video Driver|Pilote Vidéo : +Video Format|Format Vidéo : +Video|Vidéo +_View|_Visualiser +&View|&Visualiser +View|Visualiser +Waiting Input...|En Attente du Contrôleur +Width|Largeur : +Windows SPTI Driver|Pilote Windows SPTI +Word Write|Ecrire un Word +Yabause Cheat Files (*.yct);;All Files (*)|Fichiers de Cheat Yabause (*ycf);;Tous les Fichiers (*) +Yabause is not initialized, can't manage backup ram.|Yabause n'est pas initialisé. Ne peut pas gérer la sauvegarde. +Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Basé sur Yabause %1 :
http://yabause.org
L'équipe de Yabause :
Filipe AZEVEDO +Yabause Qt GUI|Interface Graphique Qt pour Yabause +Yabause Save State (*.yss)|Sauvegarde d'État Yabause (*.yss) diff --git a/yabause/l10n/yabause_it.yts b/yabause/l10n/yabause_it.yts new file mode 100644 index 0000000000..89b201c316 --- /dev/null +++ b/yabause/l10n/yabause_it.yts @@ -0,0 +1,198 @@ +%1/%2 blocks free|%1%2 blocchi liberi +%1 Images (*.%2)|%1 Immagini (*.%2) +503/512 blocks free|503/512 blocchi liberi +510/512 blocks free|510/512 blocchi liberi +About...|Informazioni... +&About...|&Informazioni... +About|Informazioni +&Action Replay|&Action Replay +Action Replay Code :|Codice Action Replay +Add Action Replay Code|Aggiungi codice Action Replay +Add Codes...|Aggiungi codici.... +Add Raw Memory Code|Aggiungi Raw Memory Code +Address :|Indirizzo: +Advanced|Avanzate +|http://www.monkeystudio.org"> +Are you sure you want to delete '%1' ?|Sicuro di voler cancellare '%1'? +Are you sure you want to format '%1' ?|Sicuro di voler formattare '%1'? +Backup Manager|Gestione Backup +Backup Manager...|Gestione Backup... +&Backup Ram Manager|&Gestione Backup RAM +Backup Ram Manager|Gestione Backup RAM +Bios|BIOS +Block Size :|Dimensioni Blocco: +&Browse|&Sfoglia +Browse|Sfoglia +Byte Write|Scrivi Byte +Cancel|Annulla +Cannot initialize|Impossibile Inizializzare +&Capture Screen|&Cattura Schermo +Cart/Memory|Cart/Memoria +Cartridge|Cartuccia +CD Images (*.iso *.cue *.bin)|Immagine CD (*.iso *.cue *.bin) +Cd-Rom|Cd-Rom +&Cheats|&Cheats +Cheats|Cheats +Cheats File|File Cheats +&Cheat List|Lista &Cheat +Cheats List|Lista Cheat +Cheats List...|Lista Cheat... +Cheat Type|Tipo di Cheat +Choose a cdrom drive/mount point|Choose a cdrom drive/mount point +Choose a cheat file to open|Selezionare un file cheat da aprire +Choose a cheat file to save to|Selezionare un file cheat per salvare +Choose a file to save your state|Selezionare un file per salvare lo stato +Choose a location for your screenshot|Selezionare una directory per gli screenshoot +&Clear|&Svuota +Close|Chiudi +Code|Codice +Comment :|Commento: +Data Size :|Dimensioni Dati: +_Debug|_Debug +&Debug|&Debug +&Delete|&Elimina +Delete|Elimina +Description|Descrizione +Description :|Descrizione: +Device List|Lista periferiche +Disabled|Disattivo +Down|Giù +Download|Download +Emu-Compatibility|Compatibilità Emulatore +Emulation|Emulazione +End Address:|Indirizzo Finale: +Enable|Abilita +Enabled|Abilitato +English|Inglese +&File|&File +File|File +File :|File: +File Name :|Nome file: +File transfer|Trasferimento file +WARNING: Master Codes are NOT supported.|ATTENZIONE: I Codici Master NON sono supportati. +Format|Formato +FPS|FPS +Frame Skip/Limiter|Limitatore frame +French|Francese +From|Da +From File|Dal file +From File...|Dal file... +&Fullscreen|&Schermo Intero +Fullscreen|Schermo Intero +General|Generale +German|Tedesco +Hard Reset|Hard Reset +Height|Altezza +_Help|_Aiuto +&Help|&Aiuto +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=it +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Input +Invalid Address|Indirizzo non valido +Invalid Value|Valore non valido +Italian|Italiano +Japanese|Giapponese +Language :|Lingua: +&Layer|&Layer +Layer|Layer +Left|Sinistra +Left trigger|Left trigger +Load as executable|Carica come eseguibile +Load|Carica +&Load From File|Carica da&l file +Load State|Carica stato +Load State As|Carica stato come +Log|Log +Long Write|Scrivi Log +M68K|M68K +Memory dump|Dump Memoria +Memory Dump|Dump Memoria +Memory Editor|Editor Memoria +&Memory Transfer|Trasferimento &Memoria +Memory Transfer|Trasferimento Memoria +Memory|Memoria +Mpeg ROM|Mpeg ROM +Mouse|Mouse +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +None|Nessuno +Ok|Ok +&OK|&OK +Open CD Rom|Apri CD Rom +Open CD Rom...|Apri CD Rom... +Open ISO|Apri ISO +Open ISO...|Apri ISO... +Pad|Pad +&Pause|&Pausa +Pause|Pausa +&Quit|&Esci +Quit|Esci +&Raw Memory Address|Indirizzo Grezzo Memoria +RBG0|RBG0 +Region|Regione +&Reset|&Reset +Reset|Reset +Resolution|Risoluzione +Right|Destra +Right trigger|Right trigger +R&un|Eseg&ui +Run|Esegui +Save Information|Salva Informazioni +Save List|Salva lista +Save|Salva +Save State|Salva stato +Save State As|Salva stato come +Save States|Salva stati +&Save To File|$Salva su file +Screenshot|Screenshot +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Selezionare un file per caricare il tuo stato +Select your iso/cue/bin file|Selezionare un file iso/cue/bin +&Settings...|Impo&stazioni... +Settings|Impostazioni +SH2 Interpreter|Interprete SH2 +Sound Core|Sistema Audio +Sound|Suono +Spanish|Spagnolo +SSH2|SSH2 +Start|Inizia +Start Address:|Indirizzo Iniziale: +Status|Stato +Store|Store +To File|Nel file +To File...|Nel file... +toolBar|Barra degli Strumenti +Tools|Strumenti +To|A +Transfer|Trasferimento +Translation|Traduzione +Unable to add code|Impossibile aggiungere il codice +Unable to change description|Impossibile cambiare la descrizione +Unable to open file for loading|Impossibile aprire il file per il caricamento +Unable to open file for saving|Impossibile aprire il file per il salvataggio +Unable to remove code|Impossibile rimuovere il codice +Unknow (%1)|Sconosciuto (%1) +Up|Su +Upload|Upload +Value :|Valore: +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Sistema Video +Video Driver|Driver Video +Video Format|Formato Video +Video|Video +_View|_Visualizza +&View|&Visualizza +Waiting Input...|In attesa di input... +Width|Larghezza +Word Write|Word Write +Yabause Cheat Files (*.yct);;All Files (*)|Yabause File Cheat (*.yct);;Tutti i file (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Interfaccia Qt Yabause
Basata su Yabause 0.9.5
http://yabause.org">http://yabause.org
Il Team Yabause
mailto:pasnox@gmail.com">Filipe AZEVEDO +Yabause Qt GUI|Interfaccia Qt Yabause +Yabause Save State (*.yss)|Yabause Salvataggio Stato (*.yss) diff --git a/yabause/l10n/yabause_lt.yts b/yabause/l10n/yabause_lt.yts new file mode 100644 index 0000000000..4679856916 --- /dev/null +++ b/yabause/l10n/yabause_lt.yts @@ -0,0 +1,195 @@ +%1/%2 blocks free|%1/%2 blokų laisvų +%1 Images (*.%2)|%1 Images (*.%2) +503/512 blocks free|503/512 blokų laisvų +510/512 blocks free|510/512 blokų laisvų +About...|Apie... +&About...|&Apie... +About|Apie +&Action Replay|&Veiksmo pakartojimas +Action Replay Code :|Veiksmo pakartojimo kodas: +Add Action Replay Code|Pridėti veiksmo pakartojimo kodą: +Add Codes...|Pridėti kodų... +Add Raw Memory Code|Pridėti "Raw" atminties kodą +Address :|Adresas : +Advanced|Papildoma +|http://www.monkeystudio.org"> +Are you sure you want to delete '%1' ?|Ar tu įsitikinęs,kad nori ištrinti '%1' ? +Are you sure you want to format '%1' ?|Ar tu įsitikinęs,kad nori suformatuoti '%1' ? +Backup Manager|Atsarginių failų tvarkyklė +Backup Manager...|Atsarginių failų tvarkyklė... +&Backup Ram Manager|&Atsarginės darbinės atminties tvarkyklė +Backup Ram Manager|Atsarginės darbinės atminties tvarkyklė +Bios|Bios'as +Block Size :|Bloko dydis: +&Browse|&Naršyti +Browse|Browse +Byte Write|Byte Write +Cancel|Atšaukti +Cannot initialize|Negalima pradėti +&Capture Screen|&Nufotografuoti ekraną +Cart/Memory|Darbinė atmintis +Cartridge|Disketė +CD Images (*.iso *.cue *.bin)|CD atvaizdai (*.iso *.cue *.bin) +Cd-Rom|CD-ROM +&Cheats|&Kodai +Cheats|Kodai +Cheats File|Kodų failas +&Cheat List|&Kodų sąrašas +Cheats List|Kodų sąrašas +Cheats List...|Kodų sąrašas... +Cheat Type|Kodų tipas +Choose a cdrom drive/mount point|Pasirink CD-ROM prijungimo tašką +Choose a cheat file to open|Pasirink kodų failą +Choose a cheat file to save to|Pasirink kodų failą išsaugojimui į +Choose a file to save your state|Pasirink failą išsaugoti žaidimui +Choose a location for your screenshot|Pasirink vietą žaidimo ekranvaizdžiui +&Clear|&Išvalyti +Close|Uždaryti +Code|Kodas +Comment :|Komentaras: +Data Size :|Duomenų dydis: +_Debug|_Taisyti klaidas +&Debug|&Klaidų taisymas +&Delete|&Trinti +Delete|Trinti +Description|Aprašymas +Description :|Aprašymas : +Device List|Įrenginių sąrašas +Disabled|Neįgalinta +Down|Žemyn +Download|Atsisiųsti +Emu-Compatibility|Emuliatoriaus suderinamumas +Emulation|Emuliacija +End Address:|End Adress: +Enable|Įgalinti +Enabled|Įgalinta +English|Anglų +&File|&Failas +File|Failas +File :|Failas +File Name :|Failo pavadinimas: +File transfer|Failo perkėlimas +WARNING: Master Codes are NOT supported.|WARNING: Master Codes are NOT supported. +Format|Formatuoti +FPS|KPS +Frame Skip/Limiter|Kadrų praleidimas +French|Prancūzų +From|Iš +From File|Iš failo +From File...|Iš failo... +&Fullscreen|&Pilnas ekranas +Fullscreen|Pilnas ekranas +General|Pagrindiniai +German|Vokiečių +Hard Reset|"Gilus" perkrovimas +Height|Aukštis +_Help|_Pagalba +&Help|&Pagalba +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Įvedimas +Invalid Address|Neteisingas adresas +Invalid Value|Neteisinga vertė +Italian|Italų +Japanese|Japonų +Language :|Kalba : +&Layer|&Sluoksniai +Layer|Sluoksniai +Left|Kairė +Left trigger|Kairysis mygtukas +Load as executable|Įkrauti kaip paleidžiamąjį failą +Load|Įkrauti +&Load From File|&Įkelti iš failo +Load State|Įkelti išsaugotą žaidimą +Load State As|Išsaugoti žaidimą kaip +Log|Log'as +Long Write|Long Write +M68K|M68K +Memory dump|Atminties "išmetimas" +Memory Dump|Atminties "išmetimas" +Memory Editor|Atminties tvarkymas +&Memory Transfer|&Atminties pervedimas +Memory Transfer|Atminties pervedimas +Memory|Atmintis +Mpeg ROM|Mpeg ROM'as +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Ok|Gerai +&OK|&Gerai +Open CD Rom|Atverti CD-ROM'ą +Open CD Rom...|Atverti CD-ROM'ą... +Open ISO|Atverti ISO +Open ISO...|Atverti ISO... +&Pause|&Pauzė +Pause|Pauzė +&Quit|&Išeiti +Quit|Išeiti +&Raw Memory Address|&Priėjimas prie Raw atminties +RBG0|RBG0 +Region|Regionas +&Reset|&Perkrauti +Reset|Perkrauti +Resolution|Raiška +Right|Dešinė +Right trigger|Dešinysis mygtukas +R&un|&Leisti +Run|Leisti +Save Information|Išsaugoti informaciją +Save List|Išsaugojimų sąrašas +Save|Išsaugoti +Save State|Išsaugoti žaidimą +Save State As|Išsaugoti žaidimą kaip +Save States|Žaidimų išsaugojimo failai +&Save To File|&Išsaugoti į failą +Screenshot|Ekrano nuotrauka +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Pasirink žaidimo išsaugojimo failą įkėlimui +Select your iso/cue/bin file|Pasirink iso/cue/bin failą +&Settings...|&Nuostatos +Settings|Nuostatos +SH2 Interpreter|SH2 Interpretatorius +Sound Core|Garso įranga +Sound|Garsas +Spanish|Ispanų +SSH2|SSH2 +Start|Pradėti +Start Address:|Pradžios adresas: +Status|Statusas +Store|Saugoti +To File|Į failą +To File...|Į failą... +toolBar|Įrankų juosta +Tools|Įrankiai +To|Į +Transfer|Pervedimas +Translation|Vertimas +Unable to add code|Neįmanoma pridėti kodo +Unable to change description|Neįmanoma keisti aprašymo +Unable to open file for loading|Neįmanoma atverti failo įkrovimui +Unable to open file for saving|Neįmanoma atverti failo saugojimui +Unable to remove code|Neįmanoma pašalinti kodo +Unknow (%1)|Nežinoma (%1) +Up|Aukštyn +Upload|Įkrauti +Value :|Vertė: +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Video įranga +Video Driver|Video tvarkyklė +Video Format|Video formatas +Video|Video +_View|_Žiūrėti +&View|&Žiūrėti +Waiting Input...|Laukiama įvedimo +Width|Plotis +Word Write|Žodžių rašymas +Yabause Cheat Files (*.yct);;All Files (*)|Yabause kodų failai (*.yct);;Visi failai (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Paremtas pagal Yabause 0.9.5
http://yabause.org">http://yabause.org
Yabause komanda
mailto:pasnox@gmail.com">Filipe AZEVEDO +Yabause Qt GUI|Yabause Qt grafinė sąsaja +Yabause Save State (*.yss)|Yabause žaidimo išsaugojimo failas (*.yss) diff --git a/yabause/l10n/yabause_pt.yts b/yabause/l10n/yabause_pt.yts new file mode 100644 index 0000000000..634124c088 --- /dev/null +++ b/yabause/l10n/yabause_pt.yts @@ -0,0 +1,195 @@ +%1/%2 blocks free|%1/%2 blocos livres +%1 Images (*.%2)|%1 Images (*.%2) +503/512 blocks free|503/512 blocos livres +510/512 blocks free|510/512 blocos livres +About...|Sobre... +&About...|&Sobre... +About|Sobre +&Action Replay|&Action Replay +Action Replay Code :|Código do Action Replay +Add Action Replay Code|Adicionar Código do Action Replay +Add Codes...|Adicionar Códigos... +Add Raw Memory Code|Adicionar o Endereço Original da Memória +Address :|Endereço : +Advanced|Avançado +| +Are you sure you want to delete '%1' ?|Você tem certeza de que deseja excluir '%1' ? +Are you sure you want to format '%1' ?|Você tem certeza de que deseja formatar '%1' ? +Backup Manager|Gerenciador de Backups +Backup Manager...|Gerenciador de Backups... +&Backup Ram Manager|&Gerenciador da Ram de Backup +Backup Ram Manager|Gerenciador da Ram de Backup +Bios|Bios +Block Size :|Tamanho dos Blocos : +&Browse|&Procurar +Browse|Procurar +Byte Write|Gravar Byte +Cancel|Cancelar +Cannot initialize|Não pode inicializar +&Capture Screen|&Capturar Tela +Cart/Memory|Cartucho/Memória +Cartridge|Cartucho +CD Images (*.iso *.cue *.bin)|Imagens de CD (*.iso *.cue *.bin) +Cd-Rom|CD-Rom +&Cheats|&Trapaças +Cheats|Trapaças +Cheats File|Arquivo de Trapaças +&Cheat List|Lista de &Trapaças +Cheats List|Lista de Trapaças +Cheats List...|Lista de Trapaças +Cheat Type|Tipo de Trapaça +Choose a cdrom drive/mount point|Escolha um leitor de CD ou um ponto de montagem +Choose a cheat file to open|Escolha um arquivo de trapaças para abrir +Choose a cheat file to save to|Escolha um arquivo de trapaças para salvar +Choose a file to save your state|Escolha um arquivo para salvar seu estado +Choose a location for your screenshot|Escolha um local para sua captura de tela +&Clear|&Limpar +Close|Fechar +Code|Código +Comment :|Comentário : +Data Size :|Tamanho dos Dados : +_Debug|_Debug +&Debug|&Debug +&Delete|&Excluir +Delete|Excluir +Description|Descrição +Description :|Descrição : +Device List|Lista de Dispositivos +Disabled|Desativado +Down|Para baixo +Download|Baixar +Emu-Compatibility|Compatibilidade com o Emu +Emulation|Emulação +End Address:|Endereço Final: +Enable|Ativar +Enabled|Ativado +English|Inglês +&File|&Arquivo +File|Arquivo +File :|Arquivo : +File Name :|Nome do Arquivo : +File transfer|Transferência de arquivo +WARNING: Master Codes are NOT supported.|AVISO: Códigos Mestres NÃO são suportados. +Format|Formato +FPS|QPS +Frame Skip/Limiter|Pulo de quadros/Limitador +French|Francês +From|De +From File|Do Arquivo +From File...|Do Arquivo... +&Fullscreen|Tela cheia +Fullscreen|Tela cheia +General|Geral +German|Alemão +Hard Reset|Reiniciar Hardware +Height|Altura +_Help|_Ajuda +&Help|&Ajuda +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Controles +Invalid Address|Endereço Inválido +Invalid Value|Valor Inválido +Italian|Italiano +Japanese|Japonês +Language :|Idioma : +&Layer|&Camada +Layer|Camada +Left|Esquerda +Left trigger|Gatilho esquerdo +Load as executable|Carregar como executável +Load|Carregar +&Load From File|&Carregar Do Arquivo +Load State|Carregar Estado +Load State As|Carregar Estado como +Log|Log +Long Write|Escrita Longa +M68K|M68K +Memory dump|Dump de memória +Memory Dump|Dump de Memória +Memory Editor|Editor de Memória +&Memory Transfer|&Transferência de Memória +Memory Transfer|Transferência de Memória +Memory|Memória +Mpeg ROM|ROM Mpeg +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Ok|Ok +&OK|&OK +Open CD Rom|Abrir CD Rom +Open CD Rom...|Abrir CD Rom... +Open ISO|Abrir ISO +Open ISO...|Abrir ISO... +&Pause|&Pausar +Pause|Pausar +&Quit|&Sair +Quit|Sair +&Raw Memory Address|&Endereço Original da Memória +RBG0|RBG0 +Region|Região +&Reset|&Reiniciar +Reset|Reiniciar +Resolution|Resolução +Right|Direita +Right trigger|Gatilho direito +R&un|Exec&utar +Run|Executar +Save Information|Informações sobre os Saves +Save List|Lista dos Saves +Save|Salvar +Save State|Salvar Estado +Save State As|Salvar Estado Como +Save States|Salvar Estado +&Save To File|&Salvar Para Arquivo +Screenshot|Captura de tela +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Escolha um arquivo para carregar seu estado +Select your iso/cue/bin file|Escolha seu arquivo iso/cue/bin +&Settings...|Configuraçõe&s... +Settings|Configurações +SH2 Interpreter|Interpretador SH2 +Sound Core|Núcleo de Som +Sound|Som +Spanish|Espanhol +SSH2|SSH2 +Start|Iniciar +Start Address:|Endereço Inicial: +Status|Estado +Store|Armazenar +To File|Para Arquivo +To File...|Para arquivo... +toolBar|Barra de ferramentas +Tools|Ferramentas +To|Para +Transfer|Transferência +Translation|Tradução +Unable to add code|Não foi possível adicionar código +Unable to change description|Não foi possível modificar a descrição +Unable to open file for loading|Não foi possível carregar o arquivo +Unable to open file for saving|Não foi possível salvar o arquivo +Unable to remove code|Não foi possível remover o código +Unknow (%1)|Desconh (%1) +Up|Para cima +Upload|Subir +Value :|Valor : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Núcleo de Vídeo +Video Driver|Driver de Vídeo +Video Format|Formato do Vídeo +Video|Vídeo +_View|_Visualizar +&View|&Visualizar +Waiting Input...|Aguardando Entrada de Dados... +Width|Largura +Word Write|Escrita Word +Yabause Cheat Files (*.yct);;All Files (*)|Arquivos de trapaça Yabause (*.yct);;Todos os arquivos (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Gui Qt do Yabause
Baseada no Yabause 0.9.5
http://yabause.org
O Time do Yabause
Filipe AZEVEDO +Yabause Qt GUI|Interface Gráfica do Usuário em Qt do Yabause +Yabause Save State (*.yss)|Estado Salvo do Yabause diff --git a/yabause/l10n/yabause_pt_BR.yts b/yabause/l10n/yabause_pt_BR.yts new file mode 100644 index 0000000000..2843809790 --- /dev/null +++ b/yabause/l10n/yabause_pt_BR.yts @@ -0,0 +1,274 @@ +%1/%2 blocks free|%1/%2 blocos livres +%1 Images (*.%2)|%1 Imagens (*.%2) +503/512 blocks free|503/512 blocos livres +510/512 blocks free|510/512 blocos livres +About...|Sobre... +&About...|&Sobre... +About|Sobre +&Action Replay|&Action Replay +Action Replay Code :|Código do Action Replay +Add Action Replay Code|Adicionar Código do Action Replay +Add Cheat|Adicionar Trapaça +Add Codes...|Adicionar Códigos... +Add Raw Memory Code|Adicionar o Código da Memória Original +Address|Endereço +Address :|Endereço : +Advanced|Avançado +Always|Sempre +Asia (NTSC)|Ásia (NTSC) +Asia (PAL)|Ásia (PAL) +Are you sure you want to delete '%1' ?|Você tem certeza que você quer apagar '%1' ? +Are you sure you want to format '%1' ?|Você tem certeza que você quer formatar '%1' ? +Auto-detect|Auto-detectar +Autostart|Auto-iniciar +Backup Manager|Gerenciador de Backups +&Backup Manager...|&Gerenciador de Backups... +Backup Manager...|Gerenciador de Backups... +&Backup Ram Manager|&Gerenciador da Ram de Backup +Backup Ram Manager|Gerenciador da Ram de Backup +Bios|Bios +Block Size :|Tamanho dos Blocos : +&Browse|&Explorar +Browse|Explorar +Byte Write|Gravação do Byte +&Cancel|&Cancelar +Cancel|Cancelar +Cannot initialize|Não consegue inicializar +&Capture Screen|&Capturar Tela +Cart/Memory|Cartucho/Memória +Cartridge|Cartucho +CD Images (*.iso *.cue *.bin)|Imagens de CD (*.iso *.cue *.bin) +Cd-Rom|Cd-Rom +Central/South America (NTSC)|América Central/do Sul (NTSC) +Central/South America (PAL)|América Central/do Sul (PAL) +&Cheats|&Códigos +Cheats|Trapaças +Cheats File|Arquivo das Trapaças +&Cheat List|Lista de &Trapaças +Cheats List|Lista de Trapaças +&Cheats List...|&Lista de Trapaças... +Cheats List...|Lista de Trapaças... +Cheat &Search...|Busca de &Trapaças... +Cheat Search|Busca de Trapaças +Cheat Type|Tipo de Trapaça +Choose a cdrom drive/mount point|Escolha um drive de cdrom/ponto de montagem +Choose a cheat file to open|Escolha um arquivo de códigos para abrir +Choose a cheat file to save to|Escolha um arquivo de códigos para salvar +Choose a file to save your state|Escolha um arquivo para salvar seu estado +Choose a location for your screenshot|Escolha um local para seu screenshot +&Clear|&Limpar +Clear configuration|Limpar configuração +&Close|&Fechar +Close|Fechar +Code|Código +Comment :|Comentário : +Compare Type|Tipo de Comparação +Data Size|Tamanho dos Dados +Data Size :|Tamanho dos Dados : +Data Type|Tipo de Dados +_Debug|_Debug +&Debug|&Debug +&Delete|&Apagar +Delete|Apagar +Description|Descrição +Description :|Descrição : +Device List|Lista de Dispositivos +Disabled|Desativado +Down|Pra baixo +Download|Baixar +Dummy CD Drive|Imitação do Drive de CD +Dummy Input Interface|Imitação da Interface de Entrada de Dados +Dummy OSD Interface|Imitação da Interface OSD +Dummy Sound Interface|Imitação da Interface de Som +Dummy Video Interface|Imitação da Interface de Vídeo +Edit configuration|Editar configuração +Emu-Compatibility|Compatibilidade com o Emu +&Emulation|&Emulação +Emulation|Emulação +End Address:|Endereço Final: +Enable|Ativar +Enable Frame Skip/Limiter|Ativar o Frame Skip/Limitador +Enabled|Ativado +English|Inglês +Europe + others (PAL)|Europa + outros (PAL) +Exact|Exato +&File|&Arquivo +File|Arquivo +File :|Arquivo : +File:|Arquivo: +File Name :|Nome do Arquivo : +File transfer|Transferência de arquivo +WARNING: Master Codes are NOT supported.|AVISO: Códigos Mestres NÃO são suportados. +Format|Formato +FPS|FPS +Frame Skip/Limiter|Frame Skip/Limitador +&Frame Skip/Limiter|&Frame Skip/Limitador +French|Francês +From|De +From File|Do Arquivo +From File...|Do Arquivo... +&Fullscreen|&Tela cheia +Fullscreen|Tela cheia +General|Geral +German|Alemão +Glut OSD Interface|Interface OSD do Glut +Greater then|Maior do que +Hard Reset|Reset Rígido +Height|Altura +_Help|_Ajuda +&Help|&Ajuda +Hide menubar|Esconder a barra do menu +Hide toolbar|Esconder a barra de ferramentas +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk +http://www.monkeystudio.org|http://www.monkeystudio.org +Information...|Informação... +Input|Controles +Invalid Address|Endereço Inválido +Invalid Value|Valor Inválido +ISO-File Virtual Drive|Drive Virtual do Arquivo-ISO +Italian|Italiano +Japan (NTSC)|Japão (NTSC) +Japanese|Japonês +Korea (NTSC)|Coréia (NTSC) +Language :|Idioma : +&Layer|&Camada +Layer|Camada +Left|Esquerda +Left trigger|Gatilho esquerdo +Less than|Menos do que +Linux CD Drive|Drive de CD do Linux +Load as executable|Carregar como executável +Load|Carregar +&Load From File|&Carregar Do Arquivo +L&oad State|C&arregar o State +Load State|Carregar State +Load State As|Carregar State Como +&Log|&Log +Log|Log +Long Write|Gravação Longa +M68K|M68K +&Master SH2|&SH2 Mestre +Master SH2|SH2 Mestre +Memory dump|Dump da memória +Memory Dump|Dump da Memória +Memory &Editor|Editor de &Memória +Memory Editor|Editor de Memória +&Memory Transfer|&Transferência de Memória +Memory Transfer|Transferência de Memória +Memory|Memória +Mpeg ROM|ROM Mpeg +Mouse|Mouse +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Never|Nunca +None|Nenhum +North America (NTSC)|América do Norte (NTSC) +Ok|Ok +&OK|&OK +On fullscreen|Na tela cheia +Open CD Rom|Abrir o CD Rom +Open &CD Rom...|Abrir &CD Rom... +Open CD Rom...|Abrir o CD Rom... +Open ISO|Abrir a ISO +Open &ISO...|Abrir &ISO... +Open ISO...|Abrir a ISO... +OpenGL Video Interface|Interface de Vídeo do OpenGL +OSD Core|Núcleo do OSD +Pad|Pad +&Pause|&Pausar +Pause|Pausar +Qt Keyboard Input Interface|Interface da Entrada de Dados do Teclado do Qt +&Quit|&Sair +Quit|Sair +&Raw Memory Address|&Endereço da Memória Original +RBG0|RBG0 +Region|Região +&Reset|&Resetar +Reset|Resetar +Resolution|Resolução +Right|Direita +Right trigger|Gatilho direito +R&un|E&xecutar +Run|Executar +Save Information|Informações sobre os Saves +Save List|Lista dos Saves +Save|Salvar +S&ave State|S&ave State +Save State|Salvar o State +Save State As|Salvar o State Como +Save States|Salvar os States +&Save To File|&Salvar para o Arquivo +Sc&reenshot|Sc&reenshot +Screenshot|Screenshot +SCSP|SCSP +SCU-DSP|SCU-DSP +SDL Joystick Interface|Interface SDL do Joystick +SDL Sound Interface|Interface de Som SDL +Search|Busca +Search/Add Cheats|Procurar/Adicionar Trapaças +Search Value:|Procurar Valor: +Select a file to load your state|Selecione um arquivo para carregar seu state +Select your iso/cue/bin file|Selecione seu arquivo iso/cue/bin +Set PC to Start Address|Configurar o PC pra Iniciar no Endereço +&Settings...|&Configurações... +Settings|Configurações +SH2 Debugger Interpreter|Interpretador do Debugger SH2 +SH2 Dynamic Recompiler|Recompilador Dinâmico do SH2 +SH2 Interpreter|Interpretador SH2 +Show FPS|Mostrar FPS +&Slave SH2|&SH2 Escravo +Slave SH2|SH2 Escravo +Software Video Interface|Interface de Vídeo do Software +Sound Core|Núcleo do Som +Sound|Som +Spanish|Espanhol +SSH2|SSH2 +Signed|Assinado +Software OSD Interface|Interface OSD do Software +Start|Iniciar +Start Address:|Endereço Inicial: +Status|Status +Store|Armazenar +To File|Para o Arquivo +To File...|Para o Arquivo... +toolBar|Barra de ferramentas +&Tools|&Ferramentas +Tools|Ferramentas +To|Para +&Transfer|&Transferência +Transfer|Transferência +Translation|Tradução +Unable to add code|Incapaz de adicionar o código +Unable to change description|Incapaz de mudar a descrição +Unable to open file for loading|Incapaz de abrir o arquivo para carregar +Unable to open file for saving|Incapaz de abrir o arquivo para salvar +Unable to remove code|Incapaz de remover o código +Unknow (%1)|(%1) Desconhecido +Unsigned|Não Assinado +Up|Para cima +Upload|Upload +Value|Valor +Value :|Valor : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Núcleo do Vídeo +Video Driver|Driver do Vídeo +Video Format|Formato do Vídeo +Video|Vídeo +_View|_Visualizar +&View|&Visualizar +View|Visualizar +Waiting Input...|Esperando a Entrada dos Dados... +Width|Largura +Windows SPTI Driver|Drive SPTI do Windows +Word Write|Gravação das Palavras +Yabause Cheat Files (*.yct);;All Files (*)|Arquivos das Trapaças do Yabause (*.yct);;Todos os Arquivos (*) +Yabause is not initialized, can't manage backup ram.|O Yabause não está inicializado, não pode gerenciar a ram do backup. +Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Gui Qt do Yabause
Baseada no Yabause %1
http://yabause.org
O Time Yabause
Filipe AZEVEDO +Yabause Qt GUI|GUI Qt do Yabause +Yabause Save State (*.yss)|Save State do Yabause (*.yss) diff --git a/yabause/l10n/yabause_sv.yts b/yabause/l10n/yabause_sv.yts new file mode 100644 index 0000000000..f2afe08443 --- /dev/null +++ b/yabause/l10n/yabause_sv.yts @@ -0,0 +1,195 @@ +%1/%2 blocks free|%1/%2 block fria +%1 Images (*.%2)|%1 Avbilder (*.%2) +503/512 blocks free|503/512 block fria +510/512 blocks free|510/512 block fria +About...|Om... +&About...|&Om... +About|Om +&Action Replay|&Action Replay +Action Replay Code :|Action Replay-kod +Add Action Replay Code|Lägg till en Action Replay-kod +Add Codes...|Lägg till kod... +Add Raw Memory Code|Lägg till råminneskod +Address :|Adress : +Advanced|Avancerat +| +Are you sure you want to delete '%1' ?|Är du säker på att du vill ta bort '%1' ? +Are you sure you want to format '%1' ?|Är du säker på att du vill formatera '%1' ? +Backup Manager|Backuphanterare +Backup Manager...|Backuphanterare... +&Backup Ram Manager|&Backup Ram Manager +Backup Ram Manager|Backup Ram Manager +Bios|Bios +Block Size :|Blockstorlek +&Browse|&Bläddra +Browse|Bläddra +Byte Write|Byteskrivning +Cancel|Avbryt +Cannot initialize|Kan inte initialisera +&Capture Screen|&Skärmdump +Cart/Memory|Kassett/Minne +Cartridge|Kassett +CD Images (*.iso *.cue *.bin)|CD-avbild (*.iso *.cue *.bin) +Cd-Rom|CD-ROM +&Cheats|&Fusk +Cheats|Fusk +Cheats File|Fuskfil +&Cheat List|&Lista över fusk +Cheats List|Lista över fusk +Cheats List...|Lista över fusk... +Cheat Type|Typ av fusk +Choose a cdrom drive/mount point|Välj en CD-läsare eller en monteringspunkt +Choose a cheat file to open|Välj fuskfil att öppna +Choose a cheat file to save to|Välj fuskfil att spara till +Choose a file to save your state|Välj en fil att spara ditt läge till +Choose a location for your screenshot|Välj plats att spara skärmdump till +&Clear|&Rensa +Close|Stäng +Code|Kod +Comment :|Kommentar : +Data Size :|Datastorlek : +_Debug|_Debug +&Debug|%Debug +&Delete|&Ta bort +Delete|Ta bort +Description|Beskrivning +Description :|Beskrivning : +Device List|Lista över enheter +Disabled|Inaktiverad +Down|Ner +Download|Nerladdning +Emu-Compatibility|Emu-kompatibilitet +Emulation|Emulering +End Address:|Slutadress +Enable|Aktivera +Enabled|Aktiverad +English|Engelska +&File|&Fil +File|Fil +File :|File : +File Name :|Filnamn : +File transfer|Filöverföring +WARNING: Master Codes are NOT supported.|VARNING: Master Codes stöds INTE +Format|Formatera +FPS|FPS +Frame Skip/Limiter|Frame Skip/Limiter +French|Franska +From|Från +From File|Från fil +From File...|Från fil... +&Fullscreen|&Helskärm +Fullscreen|Helskärm +General|Allmänt +German|Tyska +Hard Reset|Hård reset +Height|Höjd +_Help|_Hjälp +&Help|&Hjälp +http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk +http://www.monkeystudio.org|http://www.monkeystudio.org +Input|Kontroller +Invalid Address|Ogiltig adress +Invalid Value|Ogiltigt värde +Italian|Italienska +Japanese|Japanska +Language :|Språk : +&Layer|&Lager +Layer|Lager +Left|Vänster +Left trigger|Vänster avtryckare +Load as executable|Ladda som exekverbar +Load|Ladda +&Load From File|Ladda från fil +Load State|Ladda läge +Load State As|Ladda läge som +Log|Logg +Long Write|Lång skrivning +M68K|M68K +Memory dump|Minnesdump +Memory Dump|Minnesdump +Memory Editor|Minnesredigerare +&Memory Transfer|&Minnesöverföring +Memory Transfer|Minnesöverföring +Memory|Minne +Mpeg ROM|MPEG ROM +MSH2|MSH2 +NBG0|NBG0 +NBG1|NBG1 +NBG2|NBG2 +NBG3|NBG3 +Ok|OK +&OK|&OK +Open CD Rom|Öppna CD-ROM +Open CD Rom...|Öppna CD-ROM... +Open ISO|Öppna ISO +Open ISO...|Öppna ISO... +&Pause|&Paus +Pause|Paus +&Quit|&Avsluta +Quit|Avsluta +&Raw Memory Address|&Rå minnesadress +RBG0|RBG0 +Region|Region +&Reset|&Reset +Reset|Reset +Resolution|Upplösning +Right|Höger +Right trigger|Höger avtryckare +R&un|&Kör +Run|Kör +Save Information|Spara information +Save List|Spara lista +Save|Spara +Save State|Spara läge +Save State As|Spara läge som +Save States|Spara lägen +&Save To File|&Spara till fil +Screenshot|Skärmdump +SCSP|SCSP +SCU-DSP|SCU-DSP +Select a file to load your state|Välj fil för att ladda ditt läge +Select your iso/cue/bin file|Välj din iso/cue/bin-fil +&Settings...|&Inställningar... +Settings|Inställningar +SH2 Interpreter|SH2-tolkare +Sound Core|Ljudgränssnitt +Sound|Ljud +Spanish|Spanska +SSH2|SSH2 +Start|Start +Start Address:|Startadress +Status|Status +Store|Lagra +To File|Till fil +To File...|Till fil... +toolBar|Verktygsfält +Tools|Verktyg +To|Till +Transfer|Överföring +Translation|Översättning +Unable to add code|Kan inte lägga till kod +Unable to change description|Kan inte ändra beskrivning +Unable to open file for loading|Kan inte öppna fil för att ladda +Unable to open file for saving|Kan inte öppna fil för att spara +Unable to remove code|Kan inte ta bort kod +Unknow (%1)|Okänt (%1) +Up|Upp +Upload|Uppladdning +Value :|Värde : +Vdp1|Vdp1 +VDP1|VDP1 +Vdp2|Vdp2 +VDP2|VDP2 +Video Core|Videogränssnitt +Video Driver|Videodrivrutin +Video Format|Videoformat +Video|Video +_View|_Visa +&View|&Visa +Waiting Input...|Väntar på input... +Width|Bredd +Word Write|Ordskrivning +Yabause Cheat Files (*.yct);;All Files (*)|Yabause fuskfil (*.yct);;Alla filer (*) +Yabause Qt Gui
Based on Yabause 0.9.5
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt GUI
Baserat på Yabause 0.9.5
http://yabause.org
Yabause-teamet
Filipe AZEVEDO +Yabause Qt GUI|Yabause Qt GUI +Yabause Save State (*.yss)|Yabause Sparat Läge (*.yss) diff --git a/yabause/src/CMakeLists.txt b/yabause/src/CMakeLists.txt new file mode 100644 index 0000000000..a825f2f0a2 --- /dev/null +++ b/yabause/src/CMakeLists.txt @@ -0,0 +1,492 @@ +project(yabause) + +include(CheckFunctionExists) +include(CheckIncludeFile) + +set(yabause_SOURCES + bios.c + cdbase.c cheat.c coffelf.c cs0.c cs1.c cs2.c + debug.c + error.c + m68kcore.c m68kd.c memory.c movie.c + netlink.c + osdcore.c + peripheral.c profile.c + scu.c sh2core.c sh2d.c sh2idle.c sh2int.c sh2trace.c smpc.c snddummy.c + titan/titan.c + vdp1.c vdp2.c vdp2debug.c vidogl.c vidshared.c vidsoft.c + yabause.c ygl.c yglshader.c) + +# new SCSP +option(YAB_USE_SCSP2 "Use the new SCSP implementation.") +if (YAB_USE_SCSP2) + add_definitions(-DUSE_SCSP2=1) + set(yabause_SOURCES ${yabause_SOURCES} scsp2.c) +else() + set(yabause_SOURCES ${yabause_SOURCES} scsp.c) +endif() + +# disable strdup warning in MSVC +if (MSVC) + add_definitions(/wd4996) +endif () + +# math library +if (UNIX) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} "m") +endif() + +# Bigendian +include(TestBigEndian) +test_big_endian(WORDS_BIGENDIAN) +if (WORDS_BIGENDIAN) + add_definitions(-DWORDS_BIGENDIAN=1) +endif (WORDS_BIGENDIAN) + +include(CheckCSourceCompiles) + +# variadic macros +check_c_source_compiles("#define MACRO(...) puts(__VA_ARGS__) + int main(int argc, char ** argv) { MACRO(\"foo\"); }" + VARIADIC_MACROS_OK) +if (VARIADIC_MACROS_OK) + add_definitions(-DHAVE_C99_VARIADIC_MACROS=1) +endif (VARIADIC_MACROS_OK) + +# gettimeofday +check_function_exists(gettimeofday GETTIMEOFDAY_OK) +if (GETTIMEOFDAY_OK) + add_definitions(-DHAVE_GETTIMEOFDAY=1) +endif () + +# floorf +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-lm") +check_function_exists(floorf FLOORF_OK) +if (FLOORF_OK) + add_definitions(-DHAVE_FLOORF=1) +endif () + +# sys/time.h +check_include_file("sys/time.h" SYSTIME_OK) +if (SYSTIME_OK) + add_definitions(-DHAVE_SYS_TIME_H=1) +endif() + +# Find stdint.h +check_include_file("stdint.h" STDINT_H_FOUND) +if (STDINT_H_FOUND) + add_definitions(-DHAVE_STDINT_H=1) +endif() + +# OpenGL +option(YAB_WANT_OPENGL "use OpenGL for video output (most ports require it)" ON) +if (YAB_WANT_OPENGL) + include(FindOpenGL) + if (OPENGL_FOUND) + add_definitions(-DHAVE_LIBGL=1) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${OPENGL_LIBRARIES}) + + include(FindGLUT) + if (GLUT_FOUND) + include_directories(${GLUT_INCLUDE_DIR}) + add_definitions(-DHAVE_LIBGLUT=1) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${GLUT_LIBRARIES}) + endif() + + # glXGetProcAddress + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${OPENGL_LIBRARIES}) + check_function_exists(glXGetProcAddress GLXGETPROCADDRESS_OK) + if (GLXGETPROCADDRESS_OK) + add_definitions(-DHAVE_GLXGETPROCADDRESS=1) + endif() + endif(OPENGL_FOUND) +endif (YAB_WANT_OPENGL) + +# SDL +option(YAB_WANT_SDL "use SDL cores if available" ON) +if (YAB_WANT_SDL) + include(FindSDL) + if (SDL_FOUND) + add_definitions(-DHAVE_LIBSDL=1) + include_directories(${SDL_INCLUDE_DIR}) + set(yabause_SOURCES ${yabause_SOURCES} persdljoy.c sndsdl.c) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${SDL_LIBRARY}) + endif (SDL_FOUND) +endif (YAB_WANT_SDL) + +# OpenAL +option(YAB_WANT_OPENAL "use OpenAL sound core if available" ON) +if (YAB_WANT_OPENAL) + include(FindOpenAL) + if (OPENAL_FOUND) + find_package(Threads) + add_definitions(-DHAVE_LIBAL=1) + include_directories(${OPENAL_INCLUDE_DIR}) + set(yabause_SOURCES ${yabause_SOURCES} sndal.c) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${OPENAL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) + endif (OPENAL_FOUND) +endif (YAB_WANT_OPENAL) + +# mini18n +find_path(MINI18N_INCLUDE_DIR mini18n.h) +find_library(MINI18N_LIBRARY mini18n) +if (NOT MINI18N_INCLUDE_DIR STREQUAL "MINI18N_INCLUDE_DIR-NOTFOUND" AND NOT MINI18N_LIBRARY STREQUAL "MINI18N_LIBRARY-NOTFOUND") + set(MINI18N_FOUND TRUE) + include_directories(${MINI18N_INCLUDE_DIR}) + add_definitions(-DHAVE_LIBMINI18N=1) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${MINI18N_LIBRARY}) +endif (NOT MINI18N_INCLUDE_DIR STREQUAL "MINI18N_INCLUDE_DIR-NOTFOUND" AND NOT MINI18N_LIBRARY STREQUAL "MINI18N_LIBRARY-NOTFOUND") + +if (MINI18N_FOUND) + if (UNIX) + add_definitions(-DYTSDIR=\"${CMAKE_INSTALL_PREFIX}/share/${YAB_PACKAGE}/yts\") + elseif (WIN32) + add_definitions(-DYTSDIR=\"trans\") + endif() +endif() + +# APPLE // not necessary mac os x, but i don't care ;) +if (APPLE) + FIND_LIBRARY(COREFOUNDATION_LIBRARY NAMES CoreFoundation ) + FIND_LIBRARY(IOKIT_LIBRARY NAMES IOKit ) + set(yabause_SOURCES ${yabause_SOURCES} macjoy.c permacjoy.c cd-macosx.c sndmac.c) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${COREFOUNDATION_LIBRARY} ${IOKIT_LIBRARY}) + + check_function_exists(glBindRenderbuffer HAVE_FBO) + if (HAVE_FBO) + add_definitions(-DHAVE_FBO=1) + endif() +endif (APPLE) + +# Visual Studio +if (MSVC) + # Find DDK + if (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") + set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") + elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/6000/") + set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/6000/") + elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.0/") + set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.0/") + endif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") + + add_definitions(-DHAVE_C99_VARIADIC_MACROS -D_CRT_SECURE_NO_WARNINGS -DC68K_NO_JUMP_TABLE + -D_UNICODE -DUNICODE) +endif (MSVC) + +# Windows ddk +if (WIN32) + option(YAB_WANT_DDK "Use the real DDK instead of the built-in one") + if(YAB_WANT_DDK) + # Find ntddcdrm.h + find_path(ntddcdrm_INCLUDE_DIR ntddcdrm.h + PATHS "${DDK_DIR}" "${DDK_DIR}/inc" PATH_SUFFIXES ddk api) + + if (ntddcdrm_INCLUDE_DIR) + include_directories(${ntddcdrm_INCLUDE_DIR}) + message(STATUS "Found ntddcdrm.h: ${ntddcdrm_INCLUDE_DIR}") + add_definitions(-DHAVE_NTDDCDRM=1) + else (ntddcdrm_INCLUDE_DIR) + message(STATUS "Could not find ntddcdrm.h") + endif (ntddcdrm_INCLUDE_DIR) + endif(YAB_WANT_DDK) + + set(yabause_SOURCES ${yabause_SOURCES} cd-windows.c) + + option(YAB_WANT_DIRECTSOUND "use DirectX sound core if available") + option(YAB_WANT_DIRECTINPUT "use DirectX input core if available") + + if (YAB_WANT_DIRECTSOUND OR YAB_WANT_DIRECTINPUT) + find_path(DirectX_INCLUDE_DIR dxerr9.h "$ENV{DXSDK_DIR}/Include") + if (NOT DirectX_INCLUDE_DIR) + find_path(DirectX_INCLUDE_DIR "dxerr.h" "$ENV{DXSDK_DIR}/Include") + if (DirectX_INCLUDE_DIR) + set(DXERRH_IS_BROKEN 1 CACHE INTERNAL "dxerr is broken") + endif (DirectX_INCLUDE_DIR) + endif(NOT DirectX_INCLUDE_DIR) + + find_library(DirectX_GUID_LIBRARY dxguid "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + if (YAB_WANT_DIRECTINPUT) + find_library(DirectX_INPUT8_LIBRARY dinput8 "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + endif(YAB_WANT_DIRECTINPUT) + if (YAB_WANT_DIRECTSOUND) + find_library(DirectX_SOUND_LIBRARY dsound "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + endif(YAB_WANT_DIRECTSOUND) + + if (DXERRH_IS_BROKEN) + find_library(DirectX_ERR_LIBRARY dxerr "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + elseif(MINGW) + find_library(DirectX_ERR_LIBRARY dxerr8 "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + else() + find_library(DirectX_ERR_LIBRARY dxerr9 "$ENV{DXSDK_DIR}/Lib/x86" "$ENV{DXSDK_DIR}/Lib") + endif() + + if (DirectX_INCLUDE_DIR AND DirectX_GUID_LIBRARY AND DirectX_ERR_LIBRARY) + set(DIRECTX_FOUND "found") + include_directories(${DirectX_INCLUDE_DIR}) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_GUID_LIBRARY} ${DirectX_ERR_LIBRARY}) + + if (DirectX_SOUND_LIBRARY AND DirectX_INPUT8_LIBRARY) + add_definitions(-DHAVE_DIRECTINPUT) + set(yabause_SOURCES ${yabause_SOURCES} snddx.c) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_INPUT8_LIBRARY}) + endif (DirectX_SOUND_LIBRARY AND DirectX_INPUT8_LIBRARY) + if (YAB_WANT_DIRECTSOUND AND DirectX_SOUND_LIBRARY) + add_definitions(-DHAVE_DIRECTSOUND) + set(yabause_SOURCES ${yabause_SOURCES} perdx.c) + set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_SOUND_LIBRARY}) + endif (YAB_WANT_DIRECTSOUND AND DirectX_SOUND_LIBRARY) + + if (DXERRH_IS_BROKEN) + add_definitions(-DDXERRH_IS_BROKEN) + message(STATUS "Using work-around for dxerr.h") + endif(DXERRH_IS_BROKEN) + endif (DirectX_INCLUDE_DIR AND DirectX_GUID_LIBRARY AND DirectX_ERR_LIBRARY) + endif (YAB_WANT_DIRECTSOUND OR YAB_WANT_DIRECTINPUT) +endif (WIN32) + +if (WII) + set(CMAKE_C_FLAGS "-mrvl -mcpu=750 -meabi -mhard-float") + add_definitions(-DGEKKO=1) + # that shouldn't be hardcoded, either use an ENV variable or try to detect it... + include_directories(/opt/devkitpro/libogc/include/) +endif() + +option(YAB_WANT_ARM7 "Build a binary with arm7 support") + +# SH2 dynamic recompiler +message(STATUS "CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}") +message(STATUS "CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +option(SH2_DYNAREC "SH2 dynamic recompiler" ON) +if (SH2_DYNAREC) + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") + enable_language(ASM-ATT) + set(yabause_SOURCES ${yabause_SOURCES} + sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x86.s) + set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + add_definitions(-DSH2_DYNAREC=1) + endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + enable_language(ASM-ATT) + set(yabause_SOURCES ${yabause_SOURCES} + sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x64.s) + set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + add_definitions(-DSH2_DYNAREC=1) + endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") + enable_language(ASM-ATT) + set(yabause_SOURCES ${yabause_SOURCES} + sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) + set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + add_definitions(-DSH2_DYNAREC=1) + endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") + enable_language(ASM-ATT) + set(yabause_SOURCES ${yabause_SOURCES} + sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) + set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + add_definitions(-DSH2_DYNAREC=1 -DHAVE_ARMv6=1 -DHAVE_ARMv7=1) + endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") + if (ANDROID) + enable_language(ASM-ATT) + set(yabause_SOURCES ${yabause_SOURCES} + sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) + set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + add_definitions(-DSH2_DYNAREC=1) + add_definitions(-DANDROID=1) + if (YAB_WANT_ARM7) + add_definitions(-DHAVE_ARMv6=1 -DHAVE_ARMv7=1) + endif() + endif () + endif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") +endif (SH2_DYNAREC) + +# c68k +option(YAB_WANT_C68K "enable c68k compilation" ON) +if (YAB_WANT_C68K) + include(ExternalProject) + ExternalProject_Add(c68kinc + DOWNLOAD_COMMAND "" + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/c68k + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + INSTALL_COMMAND "" + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/c68k + ) + + add_definitions(-DHAVE_C68K=1) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/c68k) + set(yabause_SOURCES ${yabause_SOURCES} c68k/c68kexec.c c68k/c68k.c c68k/gen68k.c m68kc68k.c) + if (MSVC) + set_source_files_properties(c68k/c68kexec.c PROPERTIES COMPILE_FLAGS "/Od /wd4146") + else() + set_source_files_properties(c68k/c68kexec.c PROPERTIES COMPILE_FLAGS "-O0") + endif() +endif(YAB_WANT_C68K) + +# q68 +option(YAB_WANT_Q68 "enable q68 compilation" OFF) +if (YAB_WANT_Q68) + add_definitions(-DHAVE_Q68=1) + set(yabause_SOURCES ${yabause_SOURCES} + m68kq68.c q68/q68.c q68/q68-core.c q68/q68-disasm.c + q68/q68-const.h q68/q68.h q68/q68-internal.h q68/q68-jit.h q68/q68-jit-psp.h q68/q68-jit-x86.h) +endif() + +# *DEBUG +set(YAB_DEBUG "" CACHE STRING "List of enabled debug information") +foreach(DEBUG IN LISTS YAB_DEBUG) + if (${DEBUG} STREQUAL "main") + add_definitions(-DDEBUG=1) + elseif (${DEBUG} STREQUAL "cd") + add_definitions(-DCDDEBUG=1) + elseif (${DEBUG} STREQUAL "idle") + add_definitions(-DIDLE_DETECT_VERBOSE=1) + else (${DEBUG} STREQUAL "main") + string(TOUPPER ${DEBUG} UPDEBUG) + add_definitions(-D${UPDEBUG}_DEBUG=1) + endif (${DEBUG} STREQUAL "main") +endforeach(DEBUG) + +# Network +option(YAB_NETWORK "Enable network") +if (YAB_NETWORK) + add_definitions(-DUSESOCKET=1) +endif() + +# Peripheral key name +option(YAB_PERKEYNAME "Try to display key names instead of cryptic values" OFF) +if (YAB_PERKEYNAME) + add_definitions(-DPERKEYNAME=1) +endif() + +option(YAB_PORT_OSD "Let ports provides their own OSD core list" OFF) +if (YAB_PORT_OSD) + add_definitions(-DYAB_PORT_OSD=1) +endif() + +# Exec from cache +option(YAB_EXEC_FROM_CACHE "Allow code execution from 0xC0000000" OFF) +if (YAB_EXEC_FROM_CACHE) + add_definitions(-DEXEC_FROM_CACHE=1) +endif() + +# Optimized DMA +option(YAB_OPTIMIZED_DMA "Use optimized DMA when possible" OFF) +if (YAB_OPTIMIZED_DMA) + add_definitions(-DOPTIMIZED_DMA=1) +endif() + +# Yabause Arch +if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + add_definitions(-DARCH_IS_MACOSX=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + add_definitions(-DARCH_IS_FREEBSD=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c cd-freebsd.c) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + add_definitions(-DARCH_IS_LINUX=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-linux.c cd-linux.c) + + check_include_file("linux/joystick.h" LINUX_HAS_JOYSTICK) + if (LINUX_HAS_JOYSTICK) + set(yabause_SOURCES ${yabause_SOURCES} perlinuxjoy.c) + endif() + + check_c_source_compiles(" + #include + int main(int argc, char ** argv) { int i = CDSL_CURRENT; } + " LINUX_CDROM_H_OK) + if (NOT LINUX_CDROM_H_OK) + add_definitions(-DLINUX_CDROM_H_IS_BROKEN) + endif (NOT LINUX_CDROM_H_OK) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") + add_definitions(-DARCH_IS_NETBSD=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c cd-netbsd.c) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") + add_definitions(-DARCH_IS_NETBSD=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c cd-netbsd.c) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") + add_definitions(-DARCH_IS_WINDOWS=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c) +else () + add_definitions(-DUNKNOWN_ARCH=1) + set(yabause_SOURCES ${yabause_SOURCES} thr-dummy.c) +endif () + +set(YAB_OPTIMIZATION "-O3" CACHE STRING "Override optimization level") + +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION} -march=i686 -msse") +endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") +endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") +endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") +endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") +if(ANDROID) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") +endif() + +# Warnings defined to know when we're breaking compilation with MSVC +if (CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wdeclaration-after-statement") +endif () + +if (MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4018 /wd4244") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") +endif () + +add_definitions(-DPACKAGE=\"${YAB_PACKAGE}\") +add_definitions(-DVERSION=\"${YAB_VERSION}\") + +add_library(yabause ${yabause_SOURCES}) + +if (YAB_WANT_C68K) + add_dependencies(yabause c68kinc) +endif(YAB_WANT_C68K) + +macro(yab_port_start) + if (YAB_PORT_BUILT AND NOT YAB_MULTIBUILD) + return() + endif () +endmacro(yab_port_start) + +macro(yab_port_stop) + set(YAB_PORT_BUILT TRUE PARENT_SCOPE) +endmacro(yab_port_stop) + +macro(yab_port_success YAB_TARGET) + if (NOT YAB_MULTIBUILD) + set_target_properties(${YAB_TARGET} PROPERTIES OUTPUT_NAME yabause) + set(YAB_PORT_NAME "yabause") + else () + set(YAB_PORT_NAME ${YAB_TARGET}) + endif () + set(YAB_PORT_BUILT TRUE PARENT_SCOPE) +endmacro(yab_port_success) + +set(YAB_MAN_DIR "share/man") +if (NOT $ENV{PKGMANDIR} STREQUAL "") + set(YAB_MAN_DIR $ENV{PKGMANDIR}) +endif () + +option(YAB_MULTIBUILD "Choose wether to build all ports or only a single one") +set(YAB_PORT_BUILT FALSE) +set(YAB_PORTS "gtk;qt;windows;dreamcast;wii;carbon;cocoa" CACHE STRING "List of ports to build") +foreach(PORT IN LISTS YAB_PORTS) + add_subdirectory(${PORT}) +endforeach(PORT) + +# this is stupid, but CMake automatic definitions are based on variables... +if (YAB_WANT_C68K) + set(HAVE_C68K ON) +endif() +if (YAB_WANT_Q68) + set(HAVE_Q68 ON) +endif() +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/yabause/src/Makefile.am b/yabause/src/Makefile.am new file mode 100644 index 0000000000..d7c32ca174 --- /dev/null +++ b/yabause/src/Makefile.am @@ -0,0 +1,117 @@ +EXTRA_DIST = Makefile.dc Makefile.mng logo.bmp logo.png Makefile.wii logo.svg + +if COMPILE_C68K +SUBDIRS = c68k . $(YUI_SUBDIR) +else +SUBDIRS = . $(YUI_SUBDIR) +endif +if YUI_IS_CARBON +YUI_SUBDIR=carbon +endif +if YUI_IS_DREAMCAST +YUI_SUBDIR=dreamcast +endif +if YUI_IS_GTK +YUI_SUBDIR=gtk +endif +if YUI_IS_PSP +YUI_SUBDIR=psp +endif +if YUI_IS_QT +YUI_SUBDIR=qt +endif +if YUI_IS_WII +YUI_SUBDIR=wii +endif +if YUI_IS_WINDOWS +YUI_SUBDIR=windows +endif + +noinst_LIBRARIES = libyabause.a +libyabause_a_SOURCES = \ + bios.c bios.h cdbase.c cdbase.h cheat.c cheat.h coffelf.c coffelf.h \ + core.h cs0.c cs0.h cs1.c cs1.h cs2.c cs2.h \ + debug.c debug.h error.c error.h memory.c memory.h \ + m68kcore.c m68kcore.h m68kd.c m68kd.h movie.c movie.h \ + netlink.c netlink.h \ + osdcore.c osdcore.h \ + peripheral.h peripheral.c \ + persdljoy.c persdljoy.h profile.c profile.h \ + scu.c scu.h sh2core.c sh2core.h sh2d.c sh2d.h \ + sh2idle.c sh2idle.h sh2int.c sh2int.h sh2trace.c sh2trace.h \ + smpc.c smpc.h sndal.c sndal.h snddummy.c sndsdl.c sndsdl.h sndwav.c \ + threads.h titan/titan.c titan/titan.h vdp1.c vdp1.h vdp2.c vdp2.h \ + vdp2debug.c vdp2debug.h vidogl.c vidogl.h vidshared.c vidshared.h \ + vidsoft.c vidsoft.h yabause.c yabause.h ygl.h ygl.c yglshader.c yui.h + +if USE_SCSP2 +libyabause_a_SOURCES += scsp2.c scsp2.h +else +libyabause_a_SOURCES += scsp.c scsp.h +endif + +if COMPILE_C68K +libyabause_a_SOURCES += c68k/c68kexec.c c68k/c68k.c c68k/gen68k.c m68kc68k.c m68kc68k.h +c68kexec.o: c68k/c68kexec.c + $(COMPILE) -Ic68k -O0 $(srcdir)/c68k/c68kexec.c -c -o c68kexec.o +endif + +if COMPILE_Q68 +libyabause_a_SOURCES += m68kq68.c q68/q68.c q68/q68-core.c q68/q68-disasm.c \ + q68/q68-const.h q68/q68.h q68/q68-internal.h q68/q68-jit.h q68/q68-jit-psp.h q68/q68-jit-x86.h +if Q68_USE_JIT +libyabause_a_SOURCES += q68/q68-jit.c +if CPU_IS_X86 +libyabause_a_SOURCES += q68/q68-jit-x86.S +endif +if CPU_IS_X64 +libyabause_a_SOURCES += q68/q68-jit-x86.S +endif +if CPU_IS_PSP +libyabause_a_SOURCES += q68/q68-jit-psp.S +endif +endif +endif + +if ARCH_IS_FREEBSD +libyabause_a_SOURCES += cd-freebsd.c thr-dummy.c +endif +if ARCH_IS_LINUX +libyabause_a_SOURCES += cd-linux.c perlinuxjoy.c perlinuxjoy.h thr-linux.c +if USE_DYNAREC +if CPU_IS_X64 +libyabause_a_SOURCES += sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x64.s +AM_CFLAGS = -DSH2_DYNAREC=1 +endif +if CPU_IS_X86 +libyabause_a_SOURCES += sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x86.s +AM_CFLAGS = -DSH2_DYNAREC=1 +endif +if CPU_IS_ARM +libyabause_a_SOURCES += sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s +AM_CFLAGS = -DSH2_DYNAREC=1 -mcpu=cortex-a8 -mfpu=vfp -mfloat-abi=softfp +endif +endif +endif +if ARCH_IS_MACOSX +libyabause_a_SOURCES += cd-macosx.c macjoy.c macjoy.h permacjoy.c permacjoy.h thr-dummy.c +endif +if ARCH_IS_NETBSD +libyabause_a_SOURCES += cd-netbsd.c thr-dummy.c +endif +if ARCH_IS_WINDOWS +libyabause_a_SOURCES += cd-windows.c thr-dummy.c +endif +if YUI_IS_DREAMCAST +libyabause_a_SOURCES += thr-dummy.c +endif +if YUI_IS_WII +libyabause_a_SOURCES += thr-dummy.c +endif + +if TEST_PSP_SH2 +libyabause_a_SOURCES += psp/psp-sh2.c psp/rtl.c psp/rtlexec.c psp/rtlinsn.c \ + psp/rtlopt.c psp/rtlunit.c psp/satopt-sh2.c psp/sh2.c \ + psp/sh2-interpret.c psp/sh2-opcodeinfo.c \ + psp/sh2-optimize.c +endif diff --git a/yabause/src/Makefile.dc b/yabause/src/Makefile.dc new file mode 100644 index 0000000000..5110a387fb --- /dev/null +++ b/yabause/src/Makefile.dc @@ -0,0 +1,67 @@ +# Makefile.dc +# Dreamcast Makefile +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Lawrence Sebald +# Based on KOS makefiles (C) by Dan Potter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +all: yabause.bin + +include $(KOS_BASE)/Makefile.rules + +KOS_CFLAGS += -I. -DDEBUG -DNO_CLI -DVERSION="0.9.11" +KOS_ASFLAGS += -g + +OBJS = bios.o cdbase.o cheat.o cs0.o cs1.o cs2.o debug.o error.o m68kd.o \ + memory.o netlink.o peripheral.o profile.o scsp.o scu.o sh2core.o sh2idle.o \ + sh2int.o sh2d.o smpc.o vdp1.o vdp2.o yabause.o m68kcore.o coffelf.o \ + m68kc68k.o movie.o snddummy.o +C68K_OBJS = c68k/c68k.o c68k/c68kexec.o c68k/gen68k.o +ARCH_OBJS = dreamcast/yui.o dreamcast/perdc.o dreamcast/viddc.o \ + dreamcast/localtime.o dreamcast/cd.o dreamcast/sh2rec/sh2rec.o \ + dreamcast/sh2rec/sh2rec_htab.o dreamcast/sh2rec/sh2exec.o \ + dreamcast/sh2rec/sh2rec_mem.o + +c68k/c68kexec.o: c68k/gen68k + +c68k/gen68k: c68k/c68kexec.c c68k/c68k.c c68k/gen68k.c + $(CC) $(CFLAGS) -DC68K_GEN -o $@ $^ + cd c68k && ./gen68k + +yabause.elf: $(OBJS) $(ARCH_OBJS) $(C68K_OBJS) + kos-cc -o $@ $^ -lm + +yabause.bin: yabause.elf + $(KOS_OBJCOPY) -R .stack -O binary yabause.elf yabause.bin + +cdtest.elf: dreamcast/cd.o tools/cdtest.o + kos-cc -o $@ $^ + +cdtest.bin: cdtest.elf + $(KOS_OBJCOPY) -R .stack -O binary cdtest.elf cdtest.bin + +run: yabause.bin + $(KOS_LOADER) yabause.bin + +clean: + rm -f $(OBJS) $(ARCH_OBJS) + rm -f tools/cdtest.o + rm -f yabause.elf + rm -f cdtest.elf + rm -f cdtest.bin + +distclean: clean + rm -f yabause.bin diff --git a/yabause/src/Makefile.mng b/yabause/src/Makefile.mng new file mode 100644 index 0000000000..a637e62614 --- /dev/null +++ b/yabause/src/Makefile.mng @@ -0,0 +1,86 @@ +# Makefile.mng +# Mingw makefile +# Copyright (C) 2002, 2003, 2004 Lawrence Sebald +# Copyright (C) 2004-2007 Theo Berkau +# Based on KOS makefiles (C) by Dan Potter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TARGET = yabause.exe +CC = gcc +RC = windres +#DDEFINES=-DDEBUG +VERSION=0.9.10 +DDEFINES= +VDEFINES=-DPACKAGE=\"yabause-win\" -DVERSION=\"$(VERSION)\" -DHAVE_LIBGLUT -DHAVE_LIBGL \ + -D_WIN32_IE=0x0500 -D_UNICODE -DUNICODE -DHAVE_LIBMINI18N +CFLAGS=$(VDEFINES) $(DDEFINES) -O3 -fomit-frame-pointer -fno-strict-aliasing -Wall +#CFLAGS=$(VDEFINES) $(DDEFINES) -O3 -fno-strict-aliasing -Wall -g +CFLAGS2= -fomit-frame-pointer -fno-strict-aliasing -Wall +#CFLAGS2= -fno-strict-aliasing -Wall -g +LDFLAGS=-lmingw32 -lopengl32 -lglut32 -lkernel32 -lgdi32 -lcomctl32 -lcomdlg32 -lwinmm \ + -lws2_32 -ldxguid -ldinput8 -ldxerr8 -ldsound -lmini18n -lvfw32 -mwindows -mthreads +OBJS = bios.o cdbase.o cheat.o coffelf.o cs0.o cs1.o cs2.o debug.o error.o m68kcore.o \ + m68kc68k.o m68kd.o memory.o movie.o netlink.o peripheral.o profile.o scsp.o scu.o sh2core.o \ + sh2idle.o sh2int.o sh2d.o smpc.o vdp1.o vdp2.o vdp2debug.o vidogl.o vidshared.o \ + vidsoft.o yabause.o ygl.o cd-windows.o c68k/c68k.o c68k/c68kexec.o c68k/gen68k.o \ + windows/perdx.o windows/snddx.o windows/yui.o windows/cheats.o windows/resource.o \ + windows/custctl.o windows/disasm.o windows/hexedit.o windows/cpudebug/yuidebug.o \ + windows/cpudebug/debug-68k.o windows/cpudebug/debug-scsp.o windows/cpudebug/debug-scu.o \ + windows/cpudebug/debug-sh2.o windows/cpudebug/debug-smpc.o windows/cpudebug/debug-vdp1.o \ + windows/cpudebug/debug-vdp2.o windows/settings/settings.o windows/settings/settings-basic.o \ + windows/settings/settings-input.o windows/settings/settings-log.o windows/settings/settings-netlink.o \ + windows/settings/settings-sound.o windows/settings/settings-video.o windows\bup-manager.o windows\aviout.o windows\ramwatch.o + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +windows/resource.o: windows/resource.rc windows/resource.h + cd windows && $(RC) $(VDEFINES) $(DDEFINES) resource.rc -o resource.o + cd .. +clean: + rm -f $(OBJS) + rm -f $(TARGET) + +installer: + makensis "/XOutFile ..\..\yabause-$(VERSION)-win.exe" windows\installer\installer.nsi + +release: + zip -9 yabause-$(VERSION)-win.zip * -i *.dll -i yabause.exe + cd .. && zip -9 src\yabause-$(VERSION)-win.zip * -i AUTHORS -i Changelog -i COPYING -i README -i README.WIN + +cdbase.o: cdbase.h +cs0.o: cs0.h core.h +cs1.o: cs1.h core.h +debug.o: debug.h +memory.o: debug.h memory.h +scu.o: scu.h debug.h core.h +sh2core.o: sh2core.h core.h +sh2int.o: sh2core.h sh2int.h core.h +vdp1.o: vdp1.h debug.h memory.h +c68k/c68k.o: c68k/c68k.h +c68k/c68kexec.o: c68k/c68k.h c68k/c68k_ini.inc + $(CC) $(CFLAGS2) -c c68k/c68kexec.c -o $@ +c68k/c68k_ini.inc: c68k/gen68k.exe +c68k/gen68k.exe: c68k/c68kexec.c c68k/c68k.c c68k/gen68k.c + $(CC) $(CFLAGS) -DC68K_GEN -o $@ $^ + cd c68k && gen68k + + diff --git a/yabause/src/Makefile.wii b/yabause/src/Makefile.wii new file mode 100644 index 0000000000..028d7c10d9 --- /dev/null +++ b/yabause/src/Makefile.wii @@ -0,0 +1,53 @@ +# Makefile.wii +# Wii makefile +# Copyright (C) 2002, 2003, 2004 Lawrence Sebald +# Copyright (C) 2004-2008 Theo Berkau +# Based on KOS makefiles (C) by Dan Potter +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC) +endif + +include $(DEVKITPPC)/wii_rules + +TARGET = yabause.elf +DDEFINES= +VDEFINES=-DPACKAGE=\"yabause-wii\" -DVERSION=\"0.9.6\" -DWORDS_BIGENDIAN -DREENTRANT_SYSCALLS_PROVIDED +MACHDEP = -DGEKKO -mcpu=750 -meabi -mhard-float +CFLAGS=$(VDEFINES) $(DDEFINES) -g -O2 -mrvl -Wall $(MACHDEP) -I$(LIBOGC_INC) +LDFLAGS= -g $(MACHDEP) -mrvl -Wl,-Map,$(notdir $@).map +LIBS := -lfat -lwiiuse -lbte -logc -lm +OBJS = bios.o cdbase.o cheat.o coffelf.o cs0.o cs1.o cs2.o debug.o \ +error.o m68kcore.o m68kc68k.o m68kd.o memory.o netlink.o peripheral.o \ +profile.o scsp.o scu.o sh2core.o sh2idle.o sh2int.o sh2d.o smpc.o vdp1.o \ +vdp2.o vidshared.o vidsoft.o yabause.o ygl.o c68k/c68k.o c68k/c68kexec.o \ +wii/yui.o wii/perwii.o wii/sndwii.o +DEPSDIR = $(CURDIR) +export LD := $(CC) +export LIBPATHS := -L$(LIBOGC_LIB) + +all: $(TARGET) + +$(TARGET): $(OBJS) + +clean: + rm -f $(OBJS) + rm -f $(TARGET) + +c68k/c68kexec.o: + $(CC) $(VDEFINES) $(DDEFINES) -mrvl -Wall $(MACHDEP) -I$(LIBOGC_INC) -c c68k/c68kexec.c -o $@ diff --git a/yabause/src/android/AndroidManifest.xml b/yabause/src/android/AndroidManifest.xml new file mode 100644 index 0000000000..15ad60ee95 --- /dev/null +++ b/yabause/src/android/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/yabause/src/android/CMakeLists.txt b/yabause/src/android/CMakeLists.txt new file mode 100644 index 0000000000..4e10702f76 --- /dev/null +++ b/yabause/src/android/CMakeLists.txt @@ -0,0 +1,114 @@ +find_program(NDK_BUILD ndk-build) + +if(NOT NDK_BUILD) + message(FATAL_ERROR "ndk build not found, bye") +endif() + +find_program(SDK_ANDROID android) + +if(NOT SDK_ANDROID) + message(FATAL_ERROR "sdk android tool not found, bye") +endif() + +if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + set(yabause_android_SHADOW + AndroidManifest.xml + project.properties + jni/yui.c + jni/miniegl.h + jni/sndaudiotrack.c + jni/sndaudiotrack.h + src/org/yabause/android/Yabause.java + src/org/yabause/android/YabauseView.java + res/drawable/pad.png + res/drawable-hdpi/icon.png + res/drawable-ldpi/icon.png + res/drawable-mdpi/icon.png + res/layout/main.xml + res/menu/emulation.xml + res/values/strings.xml + ) + + foreach(item IN LISTS yabause_android_SHADOW) + message(STATUS ${item}) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}" + ) + endforeach() +endif() + +set(YABAUSE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/jni/Android.mk.in + ${CMAKE_CURRENT_BINARY_DIR}/jni/Android.mk + @ONLY +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/local.properties" + COMMAND ${SDK_ANDROID} update project -p "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" + "${CMAKE_CURRENT_BINARY_DIR}/project.properties" + "${CMAKE_CURRENT_BINARY_DIR}/jni/Android.mk" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a" + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/../libyabause.a ${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a + DEPENDS yabause "${CMAKE_CURRENT_BINARY_DIR}/../config.h" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/local.properties" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" + COMMAND "${NDK_BUILD}" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/yui.c" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/miniegl.h" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/sndaudiotrack.c" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/sndaudiotrack.h" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +set(yabause_android_RES + "${CMAKE_CURRENT_BINARY_DIR}/res/drawable/pad.png" + "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-hdpi/icon.png" + "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-ldpi/icon.png" + "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-mdpi/icon.png" + "${CMAKE_CURRENT_BINARY_DIR}/res/layout/main.xml" + "${CMAKE_CURRENT_BINARY_DIR}/res/menu/emulation.xml" + "${CMAKE_CURRENT_BINARY_DIR}/res/values/strings.xml" +) +set(yabause_android_SRC + "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/Yabause.java" + "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabauseView.java" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-debug.apk" + COMMAND "ant" ARGS "debug" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" + DEPENDS ${yabause_android_SRC} + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" + DEPENDS ${yabause_android_RES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-release-unsigned.apk" + COMMAND "ant" ARGS "release" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" + DEPENDS ${yabause_android_SRC} + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" + DEPENDS ${yabause_android_RES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_custom_target(yabause-android ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-release-unsigned.apk") +else() + add_custom_target(yabause-android ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-debug.apk") +endif() diff --git a/yabause/src/android/android.cmake b/yabause/src/android/android.cmake new file mode 100644 index 0000000000..d5640bad92 --- /dev/null +++ b/yabause/src/android/android.cmake @@ -0,0 +1,14 @@ +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_VERSION 1) + +SET(CMAKE_C_COMPILER arm-linux-androideabi-gcc) +SET(CMAKE_CXX_COMPILER arm-linux-androideabi-g++) +SET(CMAKE_ASM-ATT_COMPILER arm-linux-androideabi-as) + +SET(CMAKE_FIND_ROOT_PATH /home/guillaume/projects/android/toolchain/sysroot/usr/) + +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +SET(ANDROID ON) diff --git a/yabause/src/android/build.xml b/yabause/src/android/build.xml new file mode 100644 index 0000000000..ecb9624556 --- /dev/null +++ b/yabause/src/android/build.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yabause/src/android/jni/Android.mk.in b/yabause/src/android/jni/Android.mk.in new file mode 100644 index 0000000000..6cfe1fbde7 --- /dev/null +++ b/yabause/src/android/jni/Android.mk.in @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := yabause +LOCAL_SRC_FILES := yui.c sndaudiotrack.c +LOCAL_STATIC_LIBRARIES := yabause-prebuilt +LOCAL_LDLIBS := -llog -ljnigraphics -lGLESv1_CM +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := yabause-prebuilt +LOCAL_SRC_FILES := libyabause.a +LOCAL_EXPORT_C_INCLUDES := @YABAUSE_INCLUDE_DIR@ +include $(PREBUILT_STATIC_LIBRARY) diff --git a/yabause/src/android/jni/miniegl.h b/yabause/src/android/jni/miniegl.h new file mode 100644 index 0000000000..a1a11b9f75 --- /dev/null +++ b/yabause/src/android/jni/miniegl.h @@ -0,0 +1,48 @@ +#ifndef _MINIEGL_ +#define _MINIEGL_ + +typedef int EGLint; +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void *EGLConfig; +typedef void *EGLContext; +typedef void *EGLDisplay; +typedef void *EGLSurface; +typedef void *EGLClientBuffer; + +/* EGL aliases */ +#define EGL_FALSE 0 +#define EGL_TRUE 1 + +/* Out-of-band handle values */ +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_SURFACE ((EGLSurface)0) + +/* GetCurrentSurface targets */ +#define EGL_DRAW 0x3059 +#define EGL_READ 0x305A + +/* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */ +#define EGL_HEIGHT 0x3056 +#define EGL_WIDTH 0x3057 + +/* QueryString targets */ +#define EGL_VENDOR 0x3053 +#define EGL_VERSION 0x3054 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CLIENT_APIS 0x308D + +EGLContext (*eglGetCurrentContext)(void); +EGLSurface (*eglGetCurrentSurface)(EGLint readdraw); +EGLDisplay (*eglGetCurrentDisplay)(void); +EGLBoolean (*eglQuerySurface)(EGLDisplay dpy, EGLSurface surface,EGLint attribute, EGLint *value); +EGLBoolean (*eglSwapInterval)(EGLDisplay dpy, EGLint interval); +EGLBoolean (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw,EGLSurface read, EGLContext ctx); +EGLBoolean (*eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); +const char * (*eglQueryString)(EGLDisplay dpy, EGLint name); +EGLint (*eglGetError)(void); + + +#endif // _MINIEGL_ \ No newline at end of file diff --git a/yabause/src/android/jni/sndaudiotrack.c b/yabause/src/android/jni/sndaudiotrack.c new file mode 100644 index 0000000000..3232062216 --- /dev/null +++ b/yabause/src/android/jni/sndaudiotrack.c @@ -0,0 +1,221 @@ +/* Copyright 2012 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "sndaudiotrack.h" +#include "debug.h" + +static int SNDAudioTrackInit(void); +static void SNDAudioTrackDeInit(void); +static int SNDAudioTrackReset(void); +static int SNDAudioTrackChangeVideoFormat(int vertfreq); +static void SNDAudioTrackUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); +static u32 SNDAudioTrackGetAudioSpace(void); +static void SNDAudioTrackMuteAudio(void); +static void SNDAudioTrackUnMuteAudio(void); +static void SNDAudioTrackSetVolume(int volume); + +SoundInterface_struct SNDAudioTrack = { +SNDCORE_AUDIOTRACK, +"Audio Track Sound Interface", +SNDAudioTrackInit, +SNDAudioTrackDeInit, +SNDAudioTrackReset, +SNDAudioTrackChangeVideoFormat, +SNDAudioTrackUpdateAudio, +SNDAudioTrackGetAudioSpace, +SNDAudioTrackMuteAudio, +SNDAudioTrackUnMuteAudio, +SNDAudioTrackSetVolume +}; + +extern JavaVM * yvm; + +jobject gtrack = NULL; + +jclass cAudioTrack = NULL; + +jmethodID mWrite = NULL; + +int mbufferSizeInBytes; + +static u16 *stereodata16; +static u8 soundvolume; +static u8 soundmaxvolume; +static u8 soundbufsize; +static int soundoffset; + +////////////////////////////////////////////////////////////////////////////// + +static int SNDAudioTrackInit(void) +{ + int sampleRateInHz = 44100; + int channelConfig = 12; //AudioFormat.CHANNEL_OUT_STEREO + int audioFormat = 2; //AudioFormat.ENCODING_PCM_16BIT + JNIEnv * env; + jobject mtrack = NULL; + jmethodID mPlay = NULL; + jmethodID mGetMinBufferSize = NULL; + jmethodID mAudioTrack = NULL; + + if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) + return -1; + + cAudioTrack = (*env)->FindClass(env, "android/media/AudioTrack"); + + mAudioTrack = (*env)->GetMethodID(env, cAudioTrack, "", "(IIIIII)V"); + + mWrite = (*env)->GetMethodID(env, cAudioTrack, "write", "([BII)I"); + + mPlay = (*env)->GetMethodID(env, cAudioTrack, "play", "()V"); + + mGetMinBufferSize = (*env)->GetStaticMethodID(env, cAudioTrack, "getMinBufferSize", "(III)I"); + + mbufferSizeInBytes = (*env)->CallStaticIntMethod(env, cAudioTrack, mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat); + + mtrack = (*env)->NewObject(env, cAudioTrack, mAudioTrack, 3 /* STREAM_MUSIC */, sampleRateInHz, channelConfig, audioFormat, mbufferSizeInBytes, 1 /* MODE_STREAM */); + + gtrack = (*env)->NewGlobalRef(env, mtrack); + + (*env)->CallNonvirtualVoidMethod(env, gtrack, cAudioTrack, mPlay); + + if ((stereodata16 = (u16 *)malloc(2 * mbufferSizeInBytes)) == NULL) + return -1; + memset(stereodata16, 0, soundbufsize); + + soundvolume = 100; + soundmaxvolume = 100; + soundbufsize = 85; + soundoffset = 0; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void SNDAudioTrackDeInit(void) +{ + JNIEnv * env; + + if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) + return; + + free(stereodata16); + stereodata16 = NULL; + + (*env)->DeleteGlobalRef(env, gtrack); +} + +////////////////////////////////////////////////////////////////////////////// + +static int SNDAudioTrackReset(void) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int SNDAudioTrackChangeVideoFormat(int vertfreq) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void sdlConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len) { + u32 i; + + for (i = 0; i < len; i++) + { + // Left Channel + *srcL = ( *srcL *soundvolume ) / soundmaxvolume; + if (*srcL > 0x7FFF) *dst = 0x7FFF; + else if (*srcL < -0x8000) *dst = -0x8000; + else *dst = *srcL; + srcL++; + dst++; + // Right Channel + *srcR = ( *srcR *soundvolume ) / soundmaxvolume; + if (*srcR > 0x7FFF) *dst = 0x7FFF; + else if (*srcR < -0x8000) *dst = -0x8000; + else *dst = *srcR; + srcR++; + dst++; + } +} + +static void SNDAudioTrackUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples) +{ + u32 copy1size=0; + + copy1size = (num_samples * sizeof(s16) * 2); + + sdlConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)(((u8 *)stereodata16)+soundoffset), copy1size / sizeof(s16) / 2); + + soundoffset += copy1size; + + if (soundoffset > mbufferSizeInBytes) { + JNIEnv * env; + if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) + return; + + jshortArray array = (*env)->NewShortArray(env, soundoffset); + if(array) { + (*env)->SetShortArrayRegion(env, array, 0, soundoffset, stereodata16); + } + + (*env)->CallNonvirtualIntMethod(env, gtrack, cAudioTrack, mWrite, array, 0, soundoffset); + + soundoffset = 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 SNDAudioTrackGetAudioSpace(void) +{ + static int i = 0; + i++; + if (i == 55) { + i = 0; + return mbufferSizeInBytes; + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void SNDAudioTrackMuteAudio(void) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void SNDAudioTrackUnMuteAudio(void) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void SNDAudioTrackSetVolume(int volume) +{ +} diff --git a/yabause/src/android/jni/sndaudiotrack.h b/yabause/src/android/jni/sndaudiotrack.h new file mode 100644 index 0000000000..b912d52749 --- /dev/null +++ b/yabause/src/android/jni/sndaudiotrack.h @@ -0,0 +1,28 @@ +/* Copyright 2012 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SNDAUDIOTRACK_H +#define SNDAUDIOTRACK_H + +#define SNDCORE_AUDIOTRACK 4 + +#include "scsp.h" + +extern SoundInterface_struct SNDAudioTrack; +#endif diff --git a/yabause/src/android/jni/yui.c b/yabause/src/android/jni/yui.c new file mode 100644 index 0000000000..2d7dfc806e --- /dev/null +++ b/yabause/src/android/jni/yui.c @@ -0,0 +1,419 @@ +/* Copyright 2011 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "../../config.h" +#include "yabause.h" +#include "scsp.h" +#include "vidsoft.h" +#include "peripheral.h" +#include "m68kcore.h" +#include "sh2core.h" +#include "sh2int.h" +#include "cdbase.h" +#include "cs2.h" +#include "debug.h" + +#include +#include + +#define _ANDROID_2_2_ +#ifdef _ANDROID_2_2_ +#include "miniegl.h" +#else +#include +#endif + +#include +#include +#include + +#include "sndaudiotrack.h" + +JavaVM * yvm; +static jobject yabause; +static jobject ybitmap; + +static char biospath[256] = "/mnt/sdcard/jap.rom"; +static char cdpath[256] = "\0"; +static char buppath[256] = "\0"; +static char mpegpath[256] = "\0"; +static char cartpath[256] = "\0"; + +EGLDisplay g_Display = EGL_NO_DISPLAY; +EGLSurface g_Surface = EGL_NO_SURFACE; +EGLContext g_Context = EGL_NO_CONTEXT; +GLuint g_FrameBuffer = 0; +GLuint g_VertexBuffer = 0; +int g_buf_width = -1; +int g_buf_height = -1; +pthread_mutex_t g_mtxGlLock = PTHREAD_MUTEX_INITIALIZER; +float vertices [] = { + 0, 0, 0, 0, + 320, 0, 0, 0, + 320, 224, 0, 0, + 0, 224, 0, 0 +}; + + +M68K_struct * M68KCoreList[] = { +&M68KDummy, +#ifdef HAVE_C68K +&M68KC68K, +#endif +#ifdef HAVE_Q68 +&M68KQ68, +#endif +NULL +}; + +SH2Interface_struct *SH2CoreList[] = { +&SH2Interpreter, +&SH2DebugInterpreter, +#ifdef SH2_DYNAREC +&SH2Dynarec, +#endif +NULL +}; + +PerInterface_struct *PERCoreList[] = { +&PERDummy, +NULL +}; + +CDInterface *CDCoreList[] = { +&DummyCD, +&ISOCD, +NULL +}; + +SoundInterface_struct *SNDCoreList[] = { +&SNDDummy, +&SNDAudioTrack, +NULL +}; + +VideoInterface_struct *VIDCoreList[] = { +&VIDDummy, +&VIDSoft, +NULL +}; + + +#define LOG_TAG "yabause" + +/* Override printf for debug*/ +int printf( const char * fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + int result = __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, fmt, ap); + va_end(ap); + return result; +} + +void YuiErrorMsg(const char *string) +{ + jclass yclass; + jmethodID errorMsg; + jstring message; + JNIEnv * env; + if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) + return; + + yclass = (*env)->GetObjectClass(env, yabause); + errorMsg = (*env)->GetMethodID(env, yclass, "errorMsg", "(Ljava/lang/String;)V"); + message = (*env)->NewStringUTF(env, string); + (*env)->CallVoidMethod(env, yabause, errorMsg, message); +} + +void YuiSwapBuffers(void) +{ + int buf_width, buf_height; + int error; + + + pthread_mutex_lock(&g_mtxGlLock); + if( g_Display == EGL_NO_DISPLAY ) + { + pthread_mutex_unlock(&g_mtxGlLock); + return; + } + + if( eglMakeCurrent(g_Display,g_Surface,g_Surface,g_Context) == EGL_FALSE ) + { + printf( "eglMakeCurrent fail %04x",eglGetError()); + pthread_mutex_unlock(&g_mtxGlLock); + return; + } + + glClearColor( 0.0f,0.0f,0.0f,1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + + if( g_FrameBuffer == 0 ) + { + glEnable(GL_TEXTURE_2D); + glGenTextures(1,&g_FrameBuffer); + glBindTexture(GL_TEXTURE_2D, g_FrameBuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + error = glGetError(); + if( error != GL_NO_ERROR ) + { + printf("gl error %d", error ); + return; + } + }else{ + glBindTexture(GL_TEXTURE_2D, g_FrameBuffer); + } + + + VIDCore->GetGlSize(&buf_width, &buf_height); + glTexSubImage2D(GL_TEXTURE_2D, 0,0,0,buf_width,buf_height,GL_RGBA,GL_UNSIGNED_BYTE,dispbuffer); + + + if( g_VertexBuffer == 0 ) + { + glGenBuffers(1, &g_VertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, g_VertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),vertices,GL_STATIC_DRAW); + error = glGetError(); + if( error != GL_NO_ERROR ) + { + printf("gl error %d", error ); + return; + } + }else{ + glBindBuffer(GL_ARRAY_BUFFER, g_VertexBuffer); + } + + if( buf_width != g_buf_width || buf_height != g_buf_height ) + { + vertices[6]=vertices[10]=(float)buf_width/1024.f; + vertices[11]=vertices[15]=(float)buf_height/1024.f; + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),vertices,GL_STATIC_DRAW); + glVertexPointer(2, GL_FLOAT, sizeof(float)*4, 0); + glTexCoordPointer(2, GL_FLOAT, sizeof(float)*4, (void*)(sizeof(float)*2)); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + g_buf_width = buf_width; + g_buf_height = buf_height; + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + eglSwapBuffers(g_Display,g_Surface); + + pthread_mutex_unlock(&g_mtxGlLock); +} + +int Java_org_yabause_android_YabauseRunnable_initViewport( int width, int height) +{ + int swidth; + int sheight; + int error; + char * buf; + + g_Display = eglGetCurrentDisplay(); + g_Surface = eglGetCurrentSurface(EGL_READ); + g_Context = eglGetCurrentContext(); + + eglQuerySurface(g_Display,g_Surface,EGL_WIDTH,&swidth); + eglQuerySurface(g_Display,g_Surface,EGL_HEIGHT,&sheight); + + glViewport(0,0,swidth,sheight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, 320, 224, 0, 1, 0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + printf(glGetString(GL_VENDOR)); + printf(glGetString(GL_RENDERER)); + printf(glGetString(GL_VERSION)); + printf(glGetString(GL_EXTENSIONS)); + printf(eglQueryString(g_Display,EGL_EXTENSIONS)); + eglSwapInterval(g_Display,0); + eglMakeCurrent(g_Display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); + return 0; +} + +#ifdef _ANDROID_2_2_ +int initEGLFunc() +{ + void * handle; + char *error; + + handle = dlopen("libEGL.so",RTLD_LAZY); + if( handle == NULL ) + { + printf(dlerror()); + return -1; + } + + eglGetCurrentDisplay = dlsym(handle, "eglGetCurrentDisplay"); + if( eglGetCurrentDisplay == NULL){ printf(dlerror()); return -1; } + + eglGetCurrentSurface = dlsym(handle, "eglGetCurrentSurface"); + if( eglGetCurrentSurface == NULL){ printf(dlerror()); return -1; } + + eglGetCurrentContext = dlsym(handle, "eglGetCurrentContext"); + if( eglGetCurrentContext == NULL){ printf(dlerror()); return -1; } + + eglQuerySurface = dlsym(handle, "eglQuerySurface"); + if( eglQuerySurface == NULL){ printf(dlerror()); return -1; } + + eglSwapInterval = dlsym(handle, "eglSwapInterval"); + if( eglSwapInterval == NULL){ printf(dlerror()); return -1; } + + eglMakeCurrent = dlsym(handle, "eglMakeCurrent"); + if( eglMakeCurrent == NULL){ printf(dlerror()); return -1; } + + eglSwapBuffers = dlsym(handle, "eglSwapBuffers"); + if( eglSwapBuffers == NULL){ printf(dlerror()); return -1; } + + eglQueryString = dlsym(handle, "eglQueryString"); + if( eglQueryString == NULL){ printf(dlerror()); return -1; } + + eglGetError = dlsym(handle, "eglGetError"); + if( eglGetError == NULL){ printf(dlerror()); return -1; } + + return 0; +} +#else +int initEGLFunc() +{ + return 0; +} +#endif + +int Java_org_yabause_android_YabauseRunnable_lockGL() +{ + pthread_mutex_lock(&g_mtxGlLock); +} + +int Java_org_yabause_android_YabauseRunnable_unlockGL() +{ + pthread_mutex_unlock(&g_mtxGlLock); +} + + +jint +Java_org_yabause_android_YabauseRunnable_init( JNIEnv* env, jobject obj, jobject yab, jobject bitmap ) +{ + yabauseinit_struct yinit; + int res; + void * padbits; + + if( initEGLFunc() == -1 ) return -1; + + yabause = (*env)->NewGlobalRef(env, yab); + ybitmap = (*env)->NewGlobalRef(env, bitmap); + + yinit.m68kcoretype = M68KCORE_C68K; + yinit.percoretype = PERCORE_DUMMY; +#ifdef SH2_DYNAREC + yinit.sh2coretype = 2; +#else + yinit.sh2coretype = SH2CORE_DEFAULT; +#endif + yinit.vidcoretype = VIDCORE_SOFT; + yinit.sndcoretype = SNDCORE_AUDIOTRACK; + yinit.cdcoretype = CDCORE_DEFAULT; + yinit.carttype = CART_NONE; + yinit.regionid = 0; + yinit.biospath = biospath; + yinit.cdpath = cdpath; + yinit.buppath = buppath; + yinit.mpegpath = mpegpath; + yinit.cartpath = cartpath; + yinit.videoformattype = VIDEOFORMATTYPE_NTSC; + + res = YabauseInit(&yinit); + + PerPortReset(); + padbits = PerPadAdd(&PORTDATA1); + PerSetKey(1, PERPAD_LEFT, padbits); + PerSetKey(4, PERPAD_UP, padbits); + PerSetKey(6, PERPAD_DOWN, padbits); + PerSetKey(9, PERPAD_RIGHT, padbits); + + ScspSetFrameAccurate(1); + + return res; +} + +void +Java_org_yabause_android_YabauseRunnable_deinit( JNIEnv* env ) +{ + YabauseDeInit(); +} + +void +Java_org_yabause_android_YabauseRunnable_exec( JNIEnv* env ) +{ + YabauseExec(); +} + +void +Java_org_yabause_android_YabauseRunnable_press( JNIEnv* env, jobject obj, jint key ) +{ + PerKeyDown(key); +} + +void +Java_org_yabause_android_YabauseRunnable_release( JNIEnv* env, jobject obj, jint key ) +{ + PerKeyUp(key); +} + +void log_callback(char * message) +{ + __android_log_print(ANDROID_LOG_INFO, "yabause", "%s", message); +} + +jint JNI_OnLoad(JavaVM * vm, void * reserved) +{ + JNIEnv * env; + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) + return -1; + + yvm = vm; + + LogStart(); + LogChangeOutput(DEBUG_CALLBACK, (char *) log_callback); + + return JNI_VERSION_1_6; +} + diff --git a/yabause/src/android/project.properties b/yabause/src/android/project.properties new file mode 100644 index 0000000000..ea89160e01 --- /dev/null +++ b/yabause/src/android/project.properties @@ -0,0 +1,11 @@ +# 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, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/yabause/src/android/res/drawable-hdpi/icon.png b/yabause/src/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8a878c20d75c8f338af612d1e569797460be2bba GIT binary patch literal 5763 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∈8ETJ z8*Ecqaq<4Gt*t14Jj%YHC6ZY@@{+~hBnYZsZjElCj8z_0NJ3~UrBoqGF_%0yuB1<< zS?~g%;*Ta) z?!gdgo&@z%=3XmJmnlkLw~k-KI(?@Q&3i?~Gn@2l0h*oBkEEkTKBB*MpY$`}IgbN^ P-)Io9x?+QZS?K=&xKbFx literal 0 HcmV?d00001 diff --git a/yabause/src/android/res/layout/main.xml b/yabause/src/android/res/layout/main.xml new file mode 100644 index 0000000000..ed24490ae9 --- /dev/null +++ b/yabause/src/android/res/layout/main.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/yabause/src/android/res/menu/emulation.xml b/yabause/src/android/res/menu/emulation.xml new file mode 100644 index 0000000000..4bc79a0e2c --- /dev/null +++ b/yabause/src/android/res/menu/emulation.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/yabause/src/android/res/values/strings.xml b/yabause/src/android/res/values/strings.xml new file mode 100644 index 0000000000..a9a427a080 --- /dev/null +++ b/yabause/src/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Yabause + diff --git a/yabause/src/android/src/org/yabause/android/Yabause.java b/yabause/src/android/src/org/yabause/android/Yabause.java new file mode 100644 index 0000000000..ec9f5eb6ba --- /dev/null +++ b/yabause/src/android/src/org/yabause/android/Yabause.java @@ -0,0 +1,265 @@ +/* Copyright 2011 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package org.yabause.android; + +import java.lang.Runnable; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.graphics.Bitmap; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuInflater; +import android.app.Dialog; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.DialogInterface; +import org.yabause.android.YabauseView; +import android.widget.ImageView; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; + +class InputHandler extends Handler { + private YabauseRunnable yr; + + public InputHandler(YabauseRunnable yr) { + this.yr = yr; + } + + public void handleMessage(Message msg) { + if (msg.arg1 == 1) { + yr.press(msg.arg2); + } else if (msg.arg1 == 2) { + yr.release(msg.arg2); + } + } +} + +class YabauseRunnable implements Runnable +{ + public static native int init(Yabause yabause, Bitmap bitmap); + public static native void deinit(); + public static native void exec(); + public static native void press(int key); + public static native void release(int key); + public static native int initViewport( int width, int hieght); + public static native int drawScreen(); + public static native int lockGL(); + public static native int unlockGL(); + + private boolean inited; + private boolean paused; + public InputHandler handler; + + public YabauseRunnable(Yabause yabause, Bitmap bitmap) + { + handler = new InputHandler(this); + int ok = init(yabause, bitmap); + inited = (ok == 0); + } + + public void pause() + { + Log.v("Yabause", "pause... should really pause emulation now..."); + paused = true; + } + + public void resume() + { + Log.v("Yabause", "resuming emulation..."); + paused = false; + handler.post(this); + } + + public void destroy() + { + Log.v("Yabause", "destroying yabause..."); + inited = false; + deinit(); + } + + public void run() + { + if (inited && (! paused)) + { + exec(); + + handler.post(this); + } + } + + public boolean paused() + { + return paused; + } +} + +class YabauseHandler extends Handler { + private Yabause yabause; + + public YabauseHandler(Yabause yabause) { + this.yabause = yabause; + } + + public void handleMessage(Message msg) { + yabause.showDialog(msg.what, msg.getData()); + } +} + +public class Yabause extends Activity implements OnTouchListener +{ + private static final String TAG = "Yabause"; + private YabauseRunnable yabauseThread; + private YabauseHandler handler; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + YabauseView view = (YabauseView) findViewById(R.id.yabause_view); + handler = new YabauseHandler(this); + yabauseThread = new YabauseRunnable(this,null); + view.setYabauseRunnable(yabauseThread); + + ImageView pad = (ImageView) findViewById(R.id.yabause_pad); + pad.setOnTouchListener(this); + } + + @Override + public void onPause() + { + super.onPause(); + Log.v(TAG, "pause... should pause emulation..."); + yabauseThread.pause(); + } + + @Override + public void onResume() + { + super.onResume(); + Log.v(TAG, "resume... should resume emulation..."); + yabauseThread.resume(); + } + + @Override + public void onDestroy() + { + super.onDestroy(); + Log.v(TAG, "this is the end..."); + yabauseThread.destroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.emulation, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.pause: + yabauseThread.pause(); + return true; + case R.id.quit: + this.finish(); + return true; + case R.id.resume: + yabauseThread.resume(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (yabauseThread.paused()) { + menu.setGroupVisible(R.id.paused, true); + menu.setGroupVisible(R.id.running, false); + } else { + menu.setGroupVisible(R.id.paused, false); + menu.setGroupVisible(R.id.running, true); + } + return true; + } + + @Override + public Dialog onCreateDialog(int id, Bundle args) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(args.getString("message")) + .setCancelable(false) + .setNegativeButton("Exit", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Yabause.this.finish(); + } + }) + .setPositiveButton("Ignore", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + return alert; + } + + public boolean onTouch(View v, MotionEvent event) { + int action = event.getActionMasked(); + float x = event.getX(); + float y = event.getY(); + int keyx = (int) ((x - 10) / 30); + int keyy = (int) ((y - 10) / 30); + int key = (keyx << 2) | keyy; + int keya = 0; + if (action == event.ACTION_DOWN) { + keya = 1; + } else if (action == event.ACTION_UP) { + keya = 2; + } + + Message message = handler.obtainMessage(); + message.arg1 = keya; + message.arg2 = key; + yabauseThread.handler.sendMessage(message); + + return true; + } + + private void errorMsg(String msg) { + Message message = handler.obtainMessage(); + Bundle bundle = new Bundle(); + bundle.putString("message", msg); + message.setData(bundle); + handler.sendMessage(message); + } + + static { + System.loadLibrary("yabause"); + } +} diff --git a/yabause/src/android/src/org/yabause/android/YabauseView.java b/yabause/src/android/src/org/yabause/android/YabauseView.java new file mode 100644 index 0000000000..0b114e7e41 --- /dev/null +++ b/yabause/src/android/src/org/yabause/android/YabauseView.java @@ -0,0 +1,196 @@ +/* Copyright 2011 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package org.yabause.android; + +import java.lang.Runnable; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.view.View; + +class YabauseView extends SurfaceView implements Callback, View.OnKeyListener, View.OnTouchListener{ + private static String TAG = "YabauseView"; + private static final boolean DEBUG = false; + + private int axisX = 0; + private int axisY = 0; + + public boolean[] pointers = new boolean[256]; + public int[] pointerX = new int[256]; + public int[] pointerY = new int[256]; + + private YabauseRunnable _Runnable = null; + + private EGLContext mEglContext; + private EGLDisplay mEglDisplay; + private EGLSurface mEglSurface; + private EGLConfig mEglConfig; + + + public YabauseView(Context context, AttributeSet attrs) { + super(context,attrs); + init(false, 0, 0); + } + + public YabauseView(Context context) { + super(context); + init(false, 0, 0); + } + + public YabauseView(Context context, boolean translucent, int depth, int stencil) { + super(context); + init(translucent, depth, stencil); + } + + public void setYabauseRunnable( YabauseRunnable runnable ) + { + _Runnable = runnable; + } + + private void init(boolean translucent, int depth, int stencil) { + + setFocusable( true ); + setFocusableInTouchMode( true ); + requestFocus(); + setOnKeyListener( this ); + setOnTouchListener( this ); + + getHolder().addCallback(this); + getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU); + initGLES(); + + } + + private boolean initGLES(){ + + EGL10 egl = (EGL10)EGLContext.getEGL(); + + mEglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if( mEglDisplay == EGL10.EGL_NO_DISPLAY ){ + Log.e(TAG, "Fail to get Display"); + return false; + } + + int[] version = new int[2]; + if( !egl.eglInitialize(mEglDisplay, version) ){ + Log.e(TAG, "Fail to eglInitialize"); + return false; + } + + int[] configSpec = { + EGL10.EGL_NONE + }; + EGLConfig[] configs = new EGLConfig[1]; + + int[] numConfigs = new int[1]; + if( !egl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, numConfigs) ){ + Log.e(TAG, "Fail to Choose Config"); + return false; + } + mEglConfig = configs[0]; + + mEglContext = egl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null); + if( mEglContext == EGL10.EGL_NO_CONTEXT ){ + Log.e(TAG, "Fail to Create OpenGL Context"); + return false; + } + return true; + } + + + private boolean createSurface(){ + EGL10 egl = (EGL10)EGLContext.getEGL(); + mEglSurface = egl.eglCreateWindowSurface(mEglDisplay, mEglConfig, getHolder(), null); + if( mEglSurface == EGL10.EGL_NO_SURFACE ){ + return false; + } + return true; + } + + private void endGLES(){ + EGL10 egl = (EGL10)EGLContext.getEGL(); + if( mEglSurface != null){ + //レンダリングコンテキストとの結びつけは解除 + egl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + + egl.eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = null; + } + + if( mEglContext != null ){ + egl.eglDestroyContext(mEglDisplay, mEglContext); + mEglContext = null; + } + + if( mEglDisplay != null){ + egl.eglTerminate(mEglDisplay); + mEglDisplay = null; + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + + EGL10 egl = (EGL10)EGLContext.getEGL(); + + YabauseRunnable.lockGL(); + egl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); + YabauseRunnable.initViewport(width, height); + YabauseRunnable.unlockGL(); + + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if( !createSurface() ){ + Log.e(TAG, "Fail to Creat4e Surface"); + return ; + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + + } + + // Key events + public boolean onKey( View v, int keyCode, KeyEvent event ) + { + return false; + } + + public boolean onTouch( View v, MotionEvent event ) + { + return true; + } + +} diff --git a/yabause/src/bios.c b/yabause/src/bios.c new file mode 100644 index 0000000000..1d7032ab54 --- /dev/null +++ b/yabause/src/bios.c @@ -0,0 +1,1869 @@ +/* Copyright 2006-2007 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "memory.h" +#include "cs0.h" +#include "debug.h" +#include "sh2core.h" +#include "bios.h" +#include "smpc.h" + +static u8 sh2masklist[0x20] = { +0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80, +0x80, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, +0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, +0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70 +}; + +static u32 scumasklist[0x20] = { +0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, +0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, +0xFFFFFF80, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, +0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, +0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, +0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, +0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, +0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00, 0xFFFFFE00 +}; + +u32 interruptlist[2][0x80]; + +////////////////////////////////////////////////////////////////////////////// + +void BiosInit(void) +{ + int i; + + // Setup vectors + MappedMemoryWriteLong(0x06000600, 0x002B0009); // rte, nop + MappedMemoryWriteLong(0x06000604, 0xE0F0600C); // mov #0xF0, r0; extu.b r0, r0 + MappedMemoryWriteLong(0x06000608, 0x400E8BFE); // ldc r0, sr; bf + MappedMemoryWriteLong(0x0600060C, 0x00090009); // nop + MappedMemoryWriteLong(0x06000610, 0x000B0009); // rts, nop + + for (i = 0; i < 0x200; i+=4) + { + MappedMemoryWriteLong(0x06000000+i, 0x06000600); + MappedMemoryWriteLong(0x06000400+i, 0x06000600); + interruptlist[0][i >> 2] = 0x06000600; + interruptlist[1][i >> 2] = 0x06000600; + } + + MappedMemoryWriteLong(0x06000010, 0x06000604); + MappedMemoryWriteLong(0x06000018, 0x06000604); + MappedMemoryWriteLong(0x06000024, 0x06000604); + MappedMemoryWriteLong(0x06000028, 0x06000604); + interruptlist[0][4] = 0x06000604; + interruptlist[0][6] = 0x06000604; + interruptlist[0][9] = 0x06000604; + interruptlist[0][10] = 0x06000604; + + MappedMemoryWriteLong(0x06000410, 0x06000604); + MappedMemoryWriteLong(0x06000418, 0x06000604); + MappedMemoryWriteLong(0x06000424, 0x06000604); + MappedMemoryWriteLong(0x06000428, 0x06000604); + interruptlist[1][4] = 0x06000604; + interruptlist[1][6] = 0x06000604; + interruptlist[1][9] = 0x06000604; + interruptlist[1][10] = 0x06000604; + + // Scu Interrupts + for (i = 0; i < 0x38; i+=4) + { + MappedMemoryWriteLong(0x06000100+i, 0x00000400+i); + interruptlist[0][0x40+(i >> 2)] = 0x00000400+i; + } + + for (i = 0; i < 0x40; i+=4) + { + MappedMemoryWriteLong(0x06000140+i, 0x00000440+i); + interruptlist[0][0x50+(i >> 2)] = 0x00000440+i; + } + + for (i = 0; i < 0x100; i+=4) + MappedMemoryWriteLong(0x06000A00+i, 0x06000610); + + // Setup Bios Functions + MappedMemoryWriteLong(0x06000210, 0x00000210); + MappedMemoryWriteLong(0x0600026C, 0x0000026C); + MappedMemoryWriteLong(0x06000274, 0x00000274); + MappedMemoryWriteLong(0x06000280, 0x00000280); + MappedMemoryWriteLong(0x0600029C, 0x0000029C); + MappedMemoryWriteLong(0x060002DC, 0x000002DC); + MappedMemoryWriteLong(0x06000300, 0x00000300); + MappedMemoryWriteLong(0x06000304, 0x00000304); + MappedMemoryWriteLong(0x06000310, 0x00000310); + MappedMemoryWriteLong(0x06000314, 0x00000314); + MappedMemoryWriteLong(0x06000320, 0x00000320); + MappedMemoryWriteLong(0x06000324, 0x00000000); + MappedMemoryWriteLong(0x06000330, 0x00000330); + MappedMemoryWriteLong(0x06000334, 0x00000334); + MappedMemoryWriteLong(0x06000340, 0x00000340); + MappedMemoryWriteLong(0x06000344, 0x00000344); + MappedMemoryWriteLong(0x06000348, 0xFFFFFFFF); + MappedMemoryWriteLong(0x06000354, 0x00000000); + MappedMemoryWriteLong(0x06000358, 0x00000358); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosSetScuInterrupt(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosSetScuInterrupt. vector = %02X, func = %08X\n", sh->regs.R[4], sh->regs.R[5]); + + if (sh->regs.R[5] == 0) + { + MappedMemoryWriteLong(0x06000900+(sh->regs.R[4] << 2), 0x06000610); + sh->cycles += 8; + } + else + { + MappedMemoryWriteLong(0x06000900+(sh->regs.R[4] << 2), sh->regs.R[5]); + sh->cycles += 9; + } + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosGetScuInterrupt(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + // check me +// LOG("BiosGetScuInterrupt\n"); + + sh->regs.R[0] = MappedMemoryReadLong(0x06000900+(sh->regs.R[4] << 2)); + sh->cycles += 5; + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosSetSh2Interrupt(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosSetSh2Interrupt\n"); + + if (sh->regs.R[5] == 0) + { + MappedMemoryWriteLong(sh->regs.VBR+(sh->regs.R[4] << 2), interruptlist[sh->isslave][sh->regs.R[4]]); + sh->cycles += 8; + } + else + { + MappedMemoryWriteLong(sh->regs.VBR+(sh->regs.R[4] << 2), sh->regs.R[5]); + sh->cycles += 9; + } + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosGetSh2Interrupt(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + // check me +// LOG("BiosGetSh2Interrupt\n"); + + sh->regs.R[0] = MappedMemoryReadLong(sh->regs.VBR+(sh->regs.R[4] << 2)); + sh->cycles += 5; + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosSetScuInterruptMask(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + // check me + LOG("BiosSetScuInterruptMask\n"); + + MappedMemoryWriteLong(0x06000348, sh->regs.R[4]); + MappedMemoryWriteLong(0x25FE00A0, sh->regs.R[4]); // Interrupt Mask Register + + if (!(sh->regs.R[4] & 0x8000)) // double check this + MappedMemoryWriteLong(0x25FE00A8, 1); // A-bus Interrupt Acknowledge + + sh->cycles += 17; + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosChangeScuInterruptMask(SH2_struct * sh) +{ + u32 newmask; + + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosChangeScuInterruptMask\n"); + + // Read Stored Scu Interrupt Mask, AND it by R4, OR it by R5, then put it back + newmask = (MappedMemoryReadLong(0x06000348) & sh->regs.R[4]) | sh->regs.R[5]; + MappedMemoryWriteLong(0x06000348, newmask); + MappedMemoryWriteLong(0x25FE00A0, newmask); // Interrupt Mask Register + MappedMemoryWriteLong(0x25FE00A4, (u32)(s16)sh->regs.R[4]); // Interrupt Status Register + + if (!(sh->regs.R[4] & 0x8000)) // double check this + MappedMemoryWriteLong(0x25FE00A8, 1); // A-bus Interrupt Acknowledge + + sh->cycles += 20; + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosCDINIT2(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosCDINIT1(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosGetSemaphore(SH2_struct * sh) +{ + u8 temp; + + SH2GetRegisters(sh, &sh->regs); + + // check me + LOG("BiosGetSemaphore\n"); + + if ((temp = MappedMemoryReadByte(0x06000B00 + sh->regs.R[4])) == 0) + sh->regs.R[0] = 1; + else + sh->regs.R[0] = 0; + + temp |= 0x80; + MappedMemoryWriteByte(0x06000B00 + sh->regs.R[4], temp); + + sh->cycles += 11; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosClearSemaphore(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + // check me + LOG("BiosClearSemaphore\n"); + + MappedMemoryWriteByte(0x06000B00 + sh->regs.R[4], 0); + + sh->cycles += 5; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosChangeSystemClock(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosChangeSystemClock\n"); + + MappedMemoryWriteLong(0x06000324, sh->regs.R[4]); + + if (sh->regs.R[4] == 0) + SmpcCKCHG320(); + else + SmpcCKCHG352(); + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosChangeScuInterruptPriority(SH2_struct * sh) +{ + int i; + + SH2GetRegisters(sh, &sh->regs); + + // check me +// LOG("BiosChangeScuInterruptPriority\n"); + + for (i = 0; i < 0x20; i++) + { + scumasklist[i] = MappedMemoryReadLong(sh->regs.R[4]+(i << 2)); + sh2masklist[i] = (scumasklist[i] >> 16); + if (scumasklist[i] & 0x8000) + scumasklist[i] |= 0xFFFF0000; + else + scumasklist[i] &= 0x0000FFFF; + } + + sh->cycles += 186; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosExecuteCDPlayer(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosExecuteCDPlayer\n"); + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosPowerOnMemoryClear(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosPowerOnMemoryClear\n"); + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosCheckMPEGCard(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosCheckMPEGCard\n"); + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 GetDeviceStats(u32 device, u32 *size, u32 *addr, u32 *blocksize) +{ + switch(device) + { + case 0: + *addr = 0x00180000; + *size = 0x8000; + *blocksize = 0x40; + return 0; + case 1: + if ((CartridgeArea->cartid & 0xF0) == 0x20) + { + *addr = 0x04000000; + *size = 0x40000 << (CartridgeArea->cartid & 0x0F); + if (CartridgeArea->cartid == 0x24) + *blocksize = 0x400; + else + *blocksize = 0x200; + + return 0; + } + else + return 1; + default: + *addr = 0; + *size = 0; + *blocksize = 0; + return 1; + } + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static int CheckHeader(UNUSED u32 device) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int CalcSaveSize(u32 tableaddr, int blocksize) +{ + int numblocks=0; + + // Now figure out how many blocks this save is + for(;;) + { + u16 block; + block = (MappedMemoryReadByte(tableaddr) << 8) | MappedMemoryReadByte(tableaddr + 2); + if (block == 0) + break; + tableaddr += 4; + if (((tableaddr-1) & ((blocksize << 1) - 1)) == 0) + tableaddr += 8; + numblocks++; + } + + return numblocks; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 GetFreeSpace(UNUSED u32 device, u32 size, u32 addr, u32 blocksize) +{ + u32 i; + u32 usedblocks=0; + + for (i = ((2 * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + { + // Now figure out how many blocks this save is + usedblocks += (CalcSaveSize(addr+i+0x45, blocksize) + 1); + } + } + + return ((size / blocksize) - 2 - usedblocks); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FindSave(UNUSED u32 device, u32 stringaddr, u32 blockoffset, u32 size, u32 addr, u32 blocksize) +{ + u32 i; + + for (i = ((blockoffset * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + { + int i3; + + // See if string matches, or if there's no string to check, just copy + // the data over + for (i3 = 0; i3 < 11; i3++) + { + u8 data = MappedMemoryReadByte(stringaddr+i3); + + if (MappedMemoryReadByte(addr+i+0x9+(i3*2)) != data) + { + if (data == 0) + // There's no string to match + return ((i / blocksize) >> 1); + else + // No Match, move onto the next block + i3 = 12; + } + else + { + // Match + if (i3 == 10 || data == 0) + return ((i / blocksize) >> 1); + } + } + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FindSave2(UNUSED u32 device, const char *string, u32 blockoffset, u32 size, u32 addr, u32 blocksize) +{ + u32 i; + + for (i = ((blockoffset * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + { + int i3; + + // See if string matches, or if there's no string to check, just copy + // the data over + for (i3 = 0; i3 < 11; i3++) + { + if (MappedMemoryReadByte(addr+i+0x9+(i3*2)) != string[i3]) + { + if (string[i3] == 0) + // There's no string to match + return ((i / blocksize) >> 1); + else + // No Match, move onto the next block + i3 = 12; + } + else + { + // Match + if (i3 == 10 || string[i3] == 0) + return ((i / blocksize) >> 1); + } + } + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void DeleteSave(u32 addr, u32 blockoffset, u32 blocksize) +{ + MappedMemoryWriteByte(addr + (blockoffset * blocksize * 2) + 0x1, 0x00); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 *GetFreeBlocks(u32 addr, u32 blocksize, u32 numblocks, u32 size) +{ + u8 *blocktbl; + u16 *freetbl; + u32 tableaddr; + u32 i; + u32 blockcount=0; + + // Create a table that tells us which blocks are free and used + if ((blocktbl = (u8 *)malloc(sizeof(u8) * (size / blocksize))) == NULL) + return NULL; + + memset(blocktbl, 0, (size / blocksize)); + + for (i = ((2 * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + { + tableaddr = addr+i+0x45; + blocktbl[i / (blocksize << 1)] = 1; + + // Now let's figure out which blocks are used + for(;;) + { + u16 block; + block = (MappedMemoryReadByte(tableaddr) << 8) | MappedMemoryReadByte(tableaddr + 2); + if (block == 0) + break; + tableaddr += 4; + if (((tableaddr-1) & ((blocksize << 1) - 1)) == 0) + tableaddr += 8; + // block is used + blocktbl[block] = 1; + } + } + } + + if ((freetbl = (u16 *)malloc(sizeof(u16) * numblocks)) == NULL) + { + free(blocktbl); + return NULL; + } + + // Find some free blocks for us to use + for (i = 2; i < (size / blocksize); i++) + { + if (blocktbl[i] == 0) + { + freetbl[blockcount] = (u16)i; + blockcount++; + + if (blockcount >= numblocks) + break; + } + } + + // Ok, we're all done + free(blocktbl); + + return freetbl; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 *ReadBlockTable(u32 addr, u32 *tableaddr, int block, int blocksize, int *numblocks, int *blocksread) +{ + u16 *blocktbl; + int i=0; + + tableaddr[0] = addr + (block * blocksize * 2) + 0x45; + blocksread[0]=0; + + // First of all figure out how large of buffer we need + numblocks[0] = CalcSaveSize(tableaddr[0], blocksize); + + // Allocate buffer + if ((blocktbl = (u16 *)malloc(sizeof(u16) * numblocks[0])) == NULL) + return NULL; + + // Now read in the table + for(i = 0; i < numblocks[0]; i++) + { + u16 block; + block = (MappedMemoryReadByte(tableaddr[0]) << 8) | MappedMemoryReadByte(tableaddr[0] + 2); + tableaddr[0] += 4; + + if (((tableaddr[0]-1) & ((blocksize << 1) - 1)) == 0) + { + tableaddr[0] = addr + (blocktbl[blocksread[0]] * blocksize * 2) + 9; + blocksread[0]++; + } + blocktbl[i] = block; + } + + tableaddr[0] += 4; + + return blocktbl; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPInit(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosBUPInit. arg1 = %08X, arg2 = %08X, arg3 = %08X\n", sh->regs.R[4], sh->regs.R[5], sh->regs.R[6]); + + // Setup Function table + MappedMemoryWriteLong(0x06000354, sh->regs.R[5]); + MappedMemoryWriteLong(sh->regs.R[5]+0x00, 0x00000380); + MappedMemoryWriteLong(sh->regs.R[5]+0x04, 0x00000384); + MappedMemoryWriteLong(sh->regs.R[5]+0x08, 0x00000388); + MappedMemoryWriteLong(sh->regs.R[5]+0x0C, 0x0000038C); + MappedMemoryWriteLong(sh->regs.R[5]+0x10, 0x00000390); + MappedMemoryWriteLong(sh->regs.R[5]+0x14, 0x00000394); + MappedMemoryWriteLong(sh->regs.R[5]+0x18, 0x00000398); + MappedMemoryWriteLong(sh->regs.R[5]+0x1C, 0x0000039C); + MappedMemoryWriteLong(sh->regs.R[5]+0x20, 0x000003A0); + MappedMemoryWriteLong(sh->regs.R[5]+0x24, 0x000003A4); + MappedMemoryWriteLong(sh->regs.R[5]+0x28, 0x000003A8); + MappedMemoryWriteLong(sh->regs.R[5]+0x2C, 0x000003AC); + + // Setup Device list + + // First Device + MappedMemoryWriteWord(sh->regs.R[6], 1); // ID + MappedMemoryWriteWord(sh->regs.R[6]+0x2, 1); // Number of partitions + + // Second Device + if ((CartridgeArea->cartid & 0xF0) == 0x20) + { + MappedMemoryWriteWord(sh->regs.R[6]+0x4, 2); // ID + MappedMemoryWriteWord(sh->regs.R[6]+0x6, 1); // Number of partitions + } + else + { + MappedMemoryWriteWord(sh->regs.R[6]+0x4, 0); // ID + MappedMemoryWriteWord(sh->regs.R[6]+0x6, 0); // Number of partitions + } + + // Third Device + MappedMemoryWriteWord(sh->regs.R[6]+0x08, 0); // ID + MappedMemoryWriteWord(sh->regs.R[6]+0x0A, 0); // Number of partitions + + // cycles need to be incremented + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPSelectPartition(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPSelectPartition. PR = %08X\n", sh->regs.PR); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPFormat(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosBUPFormat. PR = %08X\n", sh->regs.PR); + + BupFormat(sh->regs.R[4]); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPStatus(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 ret; + u32 freeblocks=0; + + SH2GetRegisters(sh, &sh->regs); + +// LOG("BiosBUPStatus. arg1 = %d, arg2 = %d, arg3 = %08X, PR = %08X\n", sh->regs.R[4], sh->regs.R[5], sh->regs.R[6], sh->regs.PR); + + // Fill in status variables + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + + // Make sure there's a proper header, and return if there's any other errors + if (ret == 1 || CheckHeader(sh->regs.R[4]) != 0) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + freeblocks = GetFreeSpace(sh->regs.R[4], size, addr, blocksize); + + MappedMemoryWriteLong(sh->regs.R[6], size); // Size of Backup Ram (in bytes) + MappedMemoryWriteLong(sh->regs.R[6]+0x4, size / blocksize); // Size of Backup Ram (in blocks) + MappedMemoryWriteLong(sh->regs.R[6]+0x8, blocksize); // Size of block + MappedMemoryWriteLong(sh->regs.R[6]+0xC, ((blocksize - 6) * freeblocks) - 30); // Free space(in bytes) + MappedMemoryWriteLong(sh->regs.R[6]+0x10, freeblocks); // Free space(in blocks) + MappedMemoryWriteLong(sh->regs.R[6]+0x14, freeblocks); // Not sure, but seems to be the same as Free Space(in blocks) + + // cycles need to be incremented + + sh->regs.R[0] = ret; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPWrite(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 block; + u32 ret; + u32 savesize; + u16 *blocktbl; + u32 workaddr; + u32 blockswritten=0; + u32 datasize; + u32 i; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPWrite. arg1 = %d, arg2 = %08X, arg3 = %08X, arg4 = %d, PR = %08X\n", sh->regs.R[4], sh->regs.R[5], sh->regs.R[6], sh->regs.R[7], sh->regs.PR); + + // Fill in status variables + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + if (ret == 1) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // See if save exists already + if ((block = FindSave(sh->regs.R[4], sh->regs.R[5], 2, size, addr, blocksize)) != 0) + { + // save exists + + // Should we be overwriting it? + if (sh->regs.R[7] != 0) + { + // Nope, let's bail instead + sh->regs.R[0] = 6; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // Delete old save + DeleteSave(addr, block, blocksize); + } + + // Let's figure out how many blocks will be needed for the save + datasize = MappedMemoryReadLong(sh->regs.R[5]+0x1C); + savesize = (datasize + 0x1D) / (blocksize - 6); + if ((datasize + 0x1D) % (blocksize - 6)) + savesize++; + + // Will it blend? Err... fit + if (savesize > GetFreeSpace(sh->regs.R[4], size, addr, blocksize)) + { + // Nope, time to bail + sh->regs.R[0] = 4; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // Find free blocks for the save + if ((blocktbl = GetFreeBlocks(addr, blocksize, savesize, size)) == NULL) + { + // Just return an error that might make sense + sh->regs.R[0] = 8; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // Create save + workaddr = addr + (blocktbl[0] * blocksize * 2); + + MappedMemoryWriteByte(workaddr+0x1, 0x80); + + // Copy over filename + for (i = workaddr+0x9; i < ((workaddr+0x9) + (11 * 2)); i+=2) + { + MappedMemoryWriteByte(i, MappedMemoryReadByte(sh->regs.R[5])); + sh->regs.R[5]++; + } + + sh->regs.R[5]++; + + // Copy over comment + for (i = workaddr+0x21; i < ((workaddr+0x21) + (10 * 2)); i+=2) + { + MappedMemoryWriteByte(i, MappedMemoryReadByte(sh->regs.R[5])); + sh->regs.R[5]++; + } + + // Copy over language + MappedMemoryWriteByte(workaddr+0x1F, MappedMemoryReadByte(sh->regs.R[5])); + sh->regs.R[5]++; + + sh->regs.R[5]++; + + // Copy over date + for (i = workaddr+0x35; i < ((workaddr+0x35) + (4 * 2)); i+=2) + { + MappedMemoryWriteByte(i, MappedMemoryReadByte(sh->regs.R[5])); + sh->regs.R[5]++; + } + + // Copy over data size + for (i = workaddr+0x3D; i < ((workaddr+0x3D) + (4 * 2)); i+=2) + { + MappedMemoryWriteByte(i, MappedMemoryReadByte(sh->regs.R[5])); + sh->regs.R[5]++; + } + + // write the block table + workaddr += 0x45; + + for (i = 1; i < savesize; i++) + { + MappedMemoryWriteByte(workaddr, (u8)(blocktbl[i] >> 8)); + workaddr+=2; + MappedMemoryWriteByte(workaddr, (u8)blocktbl[i]); + workaddr+=2; + + if (((workaddr-1) & ((blocksize << 1) - 1)) == 0) + { + // Next block + blockswritten++; + workaddr = addr + (blocktbl[blockswritten] * blocksize * 2) + 9; + } + } + + // Write 2 blank bytes so we now how large the table size is next time + MappedMemoryWriteByte(workaddr, 0); + workaddr+=2; + MappedMemoryWriteByte(workaddr, 0); + workaddr+=2; + + // Lastly, write the actual save data + while (datasize > 0) + { + MappedMemoryWriteByte(workaddr, MappedMemoryReadByte(sh->regs.R[6])); + datasize--; + sh->regs.R[6]++; + workaddr+=2; + + if (((workaddr-1) & ((blocksize << 1) - 1)) == 0) + { + // Next block + blockswritten++; + workaddr = addr + (blocktbl[blockswritten] * blocksize * 2) + 9; + } + } + + free(blocktbl); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPRead(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 block; + u32 ret; + u32 tableaddr; + u16 *blocktbl; + int numblocks; + int blocksread; + u32 datasize; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPRead\n", sh->regs.PR); + + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + + if (ret == 1) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // See if save exists + if ((block = FindSave(sh->regs.R[4], sh->regs.R[5], 2, size, addr, blocksize)) == 0) + { + // save doesn't exist + sh->regs.R[0] = 5; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + tableaddr = addr + (block * blocksize * 2) + 0x3D; + datasize = (MappedMemoryReadByte(tableaddr) << 24) | (MappedMemoryReadByte(tableaddr + 2) << 16) | + (MappedMemoryReadByte(tableaddr+4) << 8) | MappedMemoryReadByte(tableaddr + 6); + + // Read in Block Table + if ((blocktbl = ReadBlockTable(addr, &tableaddr, block, blocksize, &numblocks, &blocksread)) == NULL) + { + // Just return an error that might make sense + sh->regs.R[0] = 8; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // Now let's read in the data + while (datasize > 0) + { + MappedMemoryWriteByte(sh->regs.R[6], MappedMemoryReadByte(tableaddr)); + datasize--; + sh->regs.R[6]++; + tableaddr+=2; + + if (((tableaddr-1) & ((blocksize << 1) - 1)) == 0) + { + // Load up the next block + tableaddr = addr + (blocktbl[blocksread] * blocksize * 2) + 9; + blocksread++; + } + } + + free(blocktbl); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPDelete(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 block; + u32 ret; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPDelete. PR = %08X\n", sh->regs.PR); + + // Fill in status variables + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + if (ret == 1) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // See if save exists + if ((block = FindSave(sh->regs.R[4], sh->regs.R[5], 2, size, addr, blocksize)) == 0) + { + // Since the save doesn't exist, let's bail with an error + + sh->regs.R[0] = 5; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + DeleteSave(addr, block, blocksize); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPDirectory(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 ret; + u32 i; + char filename[12]; + u32 blockoffset=2; + + SH2GetRegisters(sh, &sh->regs); + +// int findmatch = MappedMemoryReadByte(sh->regs.R[5]); + + for (i = 0; i < 12; i++) + filename[i] = MappedMemoryReadByte(sh->regs.R[5]+i); + + LOG("BiosBUPDirectory. arg1 = %d, arg2 = %s, arg3 = %08X, arg4 = %08X, PR = %08X\n", sh->regs.R[4], filename, sh->regs.R[6], sh->regs.R[7], sh->regs.PR); + + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + + if (ret == 1) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + for (i = 0; i < sh->regs.R[6]; i++) + { + u32 i4; + u32 datasize=0; + u32 block = FindSave(sh->regs.R[4], sh->regs.R[5], blockoffset, size, addr, blocksize); + + if (block == 0) + break; + + blockoffset = block+1; + + // Alright, we found a match :) Time to copy over some data + block = addr + (blocksize * block * 2); + + // Copy over filename + for (i4 = block+0x9; i4 < ((block+0x9) + (11 * 2)); i4+=2) + { + MappedMemoryWriteByte(sh->regs.R[7], MappedMemoryReadByte(i4)); + sh->regs.R[7]++; + } + MappedMemoryWriteByte(sh->regs.R[7], 0); + sh->regs.R[7]++; + + // Copy over comment + for (i4 = block+0x21; i4 < ((block+0x21) + (10 * 2)); i4+=2) + { + MappedMemoryWriteByte(sh->regs.R[7], MappedMemoryReadByte(i4)); + sh->regs.R[7]++; + } + + // Copy over language + MappedMemoryWriteByte(sh->regs.R[7], MappedMemoryReadByte(block+0x1F)); + sh->regs.R[7]++; + + MappedMemoryWriteByte(sh->regs.R[7], 0); + sh->regs.R[7]++; + + // Copy over date + for (i4 = block+0x35; i4 < ((block+0x35) + (4 * 2)); i4+=2) + { + MappedMemoryWriteByte(sh->regs.R[7], MappedMemoryReadByte(i4)); + sh->regs.R[7]++; + } + + // Copy over data size + for (i4 = block+0x3D; i4 < ((block+0x3D) + (4 * 2)); i4+=2) + { + u8 data; + datasize <<= 8; + data = MappedMemoryReadByte(i4); + MappedMemoryWriteByte(sh->regs.R[7], data); + datasize |= data; + sh->regs.R[7]++; + } + + // Calculate block size from the data size, and then copy it over + MappedMemoryWriteWord(sh->regs.R[7], (u16)(((datasize + 0x1D) / (blocksize - 6)) + 1)); + sh->regs.R[7] += 4; + } + + sh->regs.R[0] = i; // returns the number of successfully read dir entries + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPVerify(SH2_struct * sh) +{ + u32 size; + u32 addr; + u32 blocksize; + u32 block; + u32 ret; + u32 tableaddr; + u32 datasize; + u16 *blocktbl; + int numblocks; + int blocksread; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPVerify. PR = %08X\n", sh->regs.PR); + + ret = GetDeviceStats(sh->regs.R[4], &size, &addr, &blocksize); + + if (ret == 1) + { + // Error + sh->regs.R[0] = ret; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // See if save exists + if ((block = FindSave(sh->regs.R[4], sh->regs.R[5], 2, size, addr, blocksize)) == 0) + { + // Since the save doesn't exist, let's bail with an error + sh->regs.R[0] = 5; // Not found + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + tableaddr = addr + (block * blocksize * 2) + 0x3D; + datasize = (MappedMemoryReadByte(tableaddr) << 24) | (MappedMemoryReadByte(tableaddr + 2) << 16) | + (MappedMemoryReadByte(tableaddr+4) << 8) | MappedMemoryReadByte(tableaddr + 6); + + // Read in Block Table + if ((blocktbl = ReadBlockTable(addr, &tableaddr, block, blocksize, &numblocks, &blocksread)) == NULL) + { + // Just return an error that might make sense + sh->regs.R[0] = 8; // Broken + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + // Now let's read in the data, and check to see if it matches + while (datasize > 0) + { + if (MappedMemoryReadByte(sh->regs.R[6]) != MappedMemoryReadByte(tableaddr)) + { + free(blocktbl); + // Ok, the data doesn't match + sh->regs.R[0] = 7; // No match + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); + return; + } + + datasize--; + sh->regs.R[6]++; + tableaddr+=2; + + if (((tableaddr-1) & ((blocksize << 1) - 1)) == 0) + { + // Load up the next block + tableaddr = addr + (blocktbl[blocksread] * blocksize * 2) + 9; + blocksread++; + } + } + + free(blocktbl); + + sh->regs.R[0] = 0; // returns 0 if there's no error + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void ConvertMonthAndDay(u32 data, u32 monthaddr, u32 dayaddr, int type) +{ + int i; + u16 monthtbl[11] = { 31, 31+28, 31+28+31, 31+28+31+30, 31+28+31+30+31, + 31+28+31+30+31+30, 31+28+31+30+31+30+31, + 31+28+31+30+31+30+31+31, 31+28+31+30+31+30+31+31+30, + 31+28+31+30+31+30+31+31+30+31, + 31+28+31+30+31+30+31+31+30+31+30 }; + + if (data < monthtbl[0]) + { + // Month + MappedMemoryWriteByte(monthaddr, 1); + + // Day + MappedMemoryWriteByte(dayaddr, (u8)(data + 1)); + return; + } + + for (i = 1; i < 11; i++) + { + if (data <= monthtbl[i]) + break; + } + + if (type == 1) + { + // Month + MappedMemoryWriteByte(monthaddr, (u8)(i + 1)); + + // Day + if ((i + 1) == 2) + MappedMemoryWriteByte(dayaddr, (u8)(data - monthtbl[(i - 1)] + 1)); + else + MappedMemoryWriteByte(dayaddr, (u8)(data - monthtbl[(i - 1)])); + } + else + { + // Month + MappedMemoryWriteByte(monthaddr, (u8)(i + 1)); + + // Day + MappedMemoryWriteByte(dayaddr, (u8)(data - monthtbl[(i - 1)] + 1)); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPGetDate(SH2_struct * sh) +{ + u32 date; + u32 div; + u32 yearoffset; + u32 yearremainder; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPGetDate. PR = %08X\n", sh->regs.PR); + + date = sh->regs.R[4]; + + // Time + MappedMemoryWriteByte(sh->regs.R[5]+3, (u8)((date % 0x5A0) / 0x3C)); + + // Minute + MappedMemoryWriteByte(sh->regs.R[5]+4, (u8)(date % 0x3C)); + + div = date / 0x5A0; + + // Week + if (div > 0xAB71) + MappedMemoryWriteByte(sh->regs.R[5]+5, (u8)((div + 1) % 7)); + else + MappedMemoryWriteByte(sh->regs.R[5]+5, (u8)((div + 2) % 7)); + + yearremainder = div % 0x5B5; + + if (yearremainder > 0x16E) + { + yearoffset = (yearremainder - 1) / 0x16D; + ConvertMonthAndDay((yearremainder - 1) % 0x16D, sh->regs.R[5]+1, sh->regs.R[5]+2, 0); + } + else + { + yearoffset = 0; + ConvertMonthAndDay(0, sh->regs.R[5]+1, sh->regs.R[5]+2, 1); + } + + // Year + MappedMemoryWriteByte(sh->regs.R[5], (u8)(((div / 0x5B5) * 4) + yearoffset)); + + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosBUPSetDate(SH2_struct * sh) +{ + u32 date; + u8 data; + u32 remainder; + u16 monthtbl[11] = { 31, 31+28, 31+28+31, 31+28+31+30, 31+28+31+30+31, + 31+28+31+30+31+30, 31+28+31+30+31+30+31, + 31+28+31+30+31+30+31+31, 31+28+31+30+31+30+31+31+30, + 31+28+31+30+31+30+31+31+30+31, + 31+28+31+30+31+30+31+31+30+31+30 }; + + SH2GetRegisters(sh, &sh->regs); + + LOG("BiosBUPSetDate. PR = %08X\n", sh->regs.PR); + + // Year + data = MappedMemoryReadByte(sh->regs.R[4]); + date = (data / 4) * 0x5B5; + remainder = data % 4; + if (remainder) + date += (remainder * 0x16D) + 1; + + // Month + data = MappedMemoryReadByte(sh->regs.R[4]+1); + if (data != 1 && data < 13) + { + date += monthtbl[data - 2]; + if (date > 2 && remainder == 0) + date++; + } + + // Day + date += MappedMemoryReadByte(sh->regs.R[4]+2) - 1; + date *= 0x5A0; + + // Hour + date += (MappedMemoryReadByte(sh->regs.R[4]+3) * 0x3C); + + // Minute + date += MappedMemoryReadByte(sh->regs.R[4]+4); + + sh->regs.R[0] = date; + sh->regs.PC = sh->regs.PR; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosHandleScuInterrupt(SH2_struct * sh, int vector) +{ + SH2GetRegisters(sh, &sh->regs); + + // Save R0-R7, PR, GBR, and old Interrupt mask to stack + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[0]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[1]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[2]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[3]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], MappedMemoryReadLong(0x06000348)); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[4]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[5]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[6]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.R[7]); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.PR); + sh->regs.R[15] -= 4; + MappedMemoryWriteLong(sh->regs.R[15], sh->regs.GBR); + + // Set SR according to vector + sh->regs.SR.all = (u32)sh2masklist[vector - 0x40]; + + // Write new Interrupt mask value + MappedMemoryWriteLong(0x06000348, MappedMemoryReadLong(0x06000348) | scumasklist[vector - 0x40]); + MappedMemoryWriteLong(0x25FE00A0, MappedMemoryReadLong(0x06000348) | scumasklist[vector - 0x40]); + + // Set PR to our Interrupt Return handler + sh->regs.PR = 0x00000480; + + // Now execute the interrupt + sh->regs.PC = MappedMemoryReadLong(0x06000900+(vector << 2)); +// LOG("Interrupt PC = %08X. Read from %08X\n", sh->regs.PC, 0x06000900+(vector << 2)); + + sh->cycles += 33; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosHandleScuInterruptReturn(SH2_struct * sh) +{ + u32 oldmask; + + SH2GetRegisters(sh, &sh->regs); + + // Restore R0-R7, PR, GBR, and old Interrupt mask from stack + sh->regs.GBR = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.PR = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[7] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[6] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[5] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[4] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + // Return SR back to normal + sh->regs.SR.all = 0xF0; + oldmask = MappedMemoryReadLong(sh->regs.R[15]); + MappedMemoryWriteLong(0x06000348, oldmask); + MappedMemoryWriteLong(0x25FE00A0, oldmask); + sh->regs.R[15] += 4; + sh->regs.R[3] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[2] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[1] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.R[0] = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + + sh->regs.PC = MappedMemoryReadLong(sh->regs.R[15]); + sh->regs.R[15] += 4; + sh->regs.SR.all = MappedMemoryReadLong(sh->regs.R[15]) & 0x000003F3; + sh->regs.R[15] += 4; + + sh->cycles += 24; + SH2SetRegisters(sh, &sh->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +int FASTCALL BiosHandleFunc(SH2_struct * sh) +{ + SH2GetRegisters(sh, &sh->regs); + + // Let's see if it's a bios function + switch((sh->regs.PC - 0x200) >> 2) + { + case 0x04: // 0x06000210 + BiosPowerOnMemoryClear(sh); + break; + case 0x1B: // 0x0600026C + BiosExecuteCDPlayer(sh); + break; + case 0x1D: // 0x06000274 + BiosCheckMPEGCard(sh); + break; + case 0x20: // 0x06000280 + BiosChangeScuInterruptPriority(sh); + break; + case 0x27: // 0x0600029C + BiosCDINIT2(sh); + break; + case 0x37: // 0x060002DC + BiosCDINIT1(sh); + break; + case 0x40: // 0x06000300 + BiosSetScuInterrupt(sh); + break; + case 0x41: // 0x06000304 + BiosGetScuInterrupt(sh); + break; + case 0x44: // 0x06000310 + BiosSetSh2Interrupt(sh); + break; + case 0x45: // 0x06000314 + BiosGetSh2Interrupt(sh); + break; + case 0x48: // 0x06000320 + BiosChangeSystemClock(sh); + break; + case 0x4C: // 0x06000330 + BiosGetSemaphore(sh); + break; + case 0x4D: // 0x06000334 + BiosClearSemaphore(sh); + break; + case 0x50: // 0x06000340 + BiosSetScuInterruptMask(sh); + break; + case 0x51: // 0x06000344 + BiosChangeScuInterruptMask(sh); + break; + case 0x56: // 0x06000358 + BiosBUPInit(sh); + break; + case 0x60: // 0x06000380 + break; + case 0x61: // 0x06000384 + BiosBUPSelectPartition(sh); + break; + case 0x62: // 0x06000388 + BiosBUPFormat(sh); + break; + case 0x63: // 0x0600038C + BiosBUPStatus(sh); + break; + case 0x64: // 0x06000390 + BiosBUPWrite(sh); + break; + case 0x65: // 0x06000394 + BiosBUPRead(sh); + break; + case 0x66: // 0x06000398 + BiosBUPDelete(sh); + break; + case 0x67: // 0x0600039C + BiosBUPDirectory(sh); + break; + case 0x68: // 0x060003A0 + BiosBUPVerify(sh); + break; + case 0x69: // 0x060003A4 + BiosBUPGetDate(sh); + break; + case 0x6A: // 0x060003A8 + BiosBUPSetDate(sh); + break; + case 0x6B: // 0x060003AC + break; + case 0x80: // Interrupt Handler + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + BiosHandleScuInterrupt(sh, (sh->regs.PC - 0x300) >> 2); + break; + case 0xA0: // Interrupt Handler Return + BiosHandleScuInterruptReturn(sh); + break; + default: + return 0; + } + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +deviceinfo_struct *BupGetDeviceList(int *numdevices) +{ + deviceinfo_struct *device; + int devicecount=1; + + if ((CartridgeArea->cartid & 0xF0) == 0x20) + devicecount++; + + if ((device = (deviceinfo_struct *)malloc(devicecount * sizeof(deviceinfo_struct))) == NULL) + { + *numdevices = 0; + return NULL; + } + + *numdevices = devicecount; + + device[0].id = 0; + sprintf(device[0].name, "Internal Backup RAM"); + + if ((CartridgeArea->cartid & 0xF0) == 0x20) + { + device[1].id = 1; + sprintf(device[1].name, "%d Mbit Backup RAM Cartridge", 1 << ((CartridgeArea->cartid & 0xF)+1)); + } + + // For now it's only internal backup ram and cartridge, no floppy :( +// device[2].id = 2; +// sprintf(device[1].name, "Floppy Disk Drive"); + + return device; +} + +////////////////////////////////////////////////////////////////////////////// + +int BupGetStats(u32 device, u32 *freespace, u32 *maxspace) +{ + u32 ret; + u32 size; + u32 addr; + u32 blocksize; + + ret = GetDeviceStats(device, &size, &addr, &blocksize); + + // Make sure there's a proper header, and return if there's any other errors + if (ret == 1 || CheckHeader(device) != 0) + return 0; + + *maxspace = size / blocksize; + *freespace = GetFreeSpace(device, size, addr, blocksize); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +saveinfo_struct *BupGetSaveList(u32 device, int *numsaves) +{ + u32 ret; + u32 size; + u32 addr; + u32 blocksize; + saveinfo_struct *save; + int savecount=0; + u32 i, j; + u32 workaddr; + + ret = GetDeviceStats(device, &size, &addr, &blocksize); + + // Make sure there's a proper header, and return if there's any other errors + if (ret == 1 || CheckHeader(device) != 0) + { + *numsaves = 0; + return NULL; + } + + for (i = ((2 * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + savecount++; + } + + if ((save = (saveinfo_struct *)malloc(savecount * sizeof(saveinfo_struct))) == NULL) + { + *numsaves = 0; + return NULL; + } + + *numsaves = savecount; + + savecount = 0; + + for (i = ((2 * blocksize) << 1); i < (size << 1); i += (blocksize << 1)) + { + // Find a block with the start of a save + if (((s8)MappedMemoryReadByte(addr + i + 1)) < 0) + { + workaddr = addr + i; + + // Copy over filename + for (j = 0; j < 11; j++) + save[savecount].filename[j] = MappedMemoryReadByte(workaddr+0x9+(j * 2)); + save[savecount].filename[11] = '\0'; + + // Copy over comment + for (j = 0; j < 10; j++) + save[savecount].comment[j] = MappedMemoryReadByte(workaddr+0x21+(j * 2)); + save[savecount].comment[10] = '\0'; + + // Copy over language + save[savecount].language = MappedMemoryReadByte(workaddr+0x1F); + + // Copy over Date(fix me) + save[savecount].year = 0; + save[savecount].month = 0; + save[savecount].day = 0; + save[savecount].hour = 0; + save[savecount].minute = 0; + save[savecount].week = 0; + + // Copy over data size + save[savecount].datasize = (MappedMemoryReadByte(workaddr+0x3D) << 24) | + (MappedMemoryReadByte(workaddr+0x3F) << 16) | + (MappedMemoryReadByte(workaddr+0x41) << 8) | + MappedMemoryReadByte(workaddr+0x43); + + // Calculate size in blocks + save[savecount].blocksize = CalcSaveSize(workaddr+0x45, blocksize) + 1; + savecount++; + } + } + + return save; +} + +////////////////////////////////////////////////////////////////////////////// + +int BupDeleteSave(u32 device, const char *savename) +{ + u32 ret; + u32 size; + u32 addr; + u32 blocksize; + u32 block; + + ret = GetDeviceStats(device, &size, &addr, &blocksize); + + // Make sure there's a proper header, and return if there's any other errors + if (ret == 1 || CheckHeader(device) != 0) + return -1; + + // Let's find and delete the save game + if ((block = FindSave2(device, savename, 2, size, addr, blocksize)) != 0) + { + // Delete old save + DeleteSave(addr, block, blocksize); + return 0; + } + + return -2; +} + +////////////////////////////////////////////////////////////////////////////// + +void BupFormat(u32 device) +{ + switch (device) + { + case 0: + FormatBackupRam(BupRam, 0x10000); + break; + case 1: + if ((CartridgeArea->cartid & 0xF0) == 0x20) + { + switch (CartridgeArea->cartid & 0xF) + { + case 1: + FormatBackupRam(CartridgeArea->bupram, 0x100000); + break; + case 2: + FormatBackupRam(CartridgeArea->bupram, 0x200000); + break; + case 3: + FormatBackupRam(CartridgeArea->bupram, 0x400000); + break; + case 4: + FormatBackupRam(CartridgeArea->bupram, 0x800000); + break; + default: break; + } + } + break; + case 2: + LOG("Formatting FDD not supported\n"); + default: break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +int BupCopySave(UNUSED u32 srcdevice, UNUSED u32 dstdevice, UNUSED const char *savename) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int BupImportSave(UNUSED u32 device, const char *filename) +{ + FILE *fp; + u32 filesize; + u8 *buffer; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + // Calculate file size + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if ((buffer = (u8 *)malloc(filesize)) == NULL) + { + fclose(fp); + return -2; + } + + fread((void *)buffer, 1, filesize, fp); + fclose(fp); + + // Write save here + + free(buffer); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int BupExportSave(UNUSED u32 device, UNUSED const char *savename, UNUSED const char *filename) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/bios.h b/yabause/src/bios.h new file mode 100644 index 0000000000..fbf21dca61 --- /dev/null +++ b/yabause/src/bios.h @@ -0,0 +1,58 @@ +/* Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef BIOS_H +#define BIOS_H + +#include "sh2core.h" + +typedef struct +{ + char filename[12]; + char comment[11]; + u8 language; + u8 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 week; + u32 datasize; + u16 blocksize; +} saveinfo_struct; + +typedef struct +{ + u8 id; + char name[32]; +} deviceinfo_struct; + +void BiosInit(void); +int FASTCALL BiosHandleFunc(SH2_struct * sh); + +deviceinfo_struct *BupGetDeviceList(int *numdevices); +int BupGetStats(u32 device, u32 *freespace, u32 *maxspace); +saveinfo_struct *BupGetSaveList(u32 device, int *numsaves); +int BupDeleteSave(u32 device, const char *savename); +void BupFormat(u32 device); +int BupCopySave(u32 srcdevice, u32 dstdevice, const char *savename); +int BupImportSave(u32 device, const char *filename); +int BupExportSave(u32 device, const char *savename, const char *filename); +#endif + diff --git a/yabause/src/c68k/CMakeLists.txt b/yabause/src/c68k/CMakeLists.txt new file mode 100644 index 0000000000..6267fd8de1 --- /dev/null +++ b/yabause/src/c68k/CMakeLists.txt @@ -0,0 +1,30 @@ +project(gen68k) + +cmake_minimum_required(VERSION 2.6) + +include(CheckCSourceCompiles) + +# variadic macros +check_c_source_compiles("#define MACRO(...) puts(__VA_ARGS__) + int main(int argc, char ** argv) { MACRO(\"foo\"); }" + VARIADIC_MACROS_OK) +if (VARIADIC_MACROS_OK) + add_definitions(-DHAVE_C99_VARIADIC_MACROS=1) +endif (VARIADIC_MACROS_OK) + +set(gen68k_SOURCES c68kexec.c c68k.c gen68k.c) + +add_definitions(-DC68K_GEN) +if (MSVC) + add_definitions(-DC68K_NO_JUMP_TABLE) +endif (MSVC) + +add_executable(gen68k ${gen68k_SOURCES}) + +execute_process(COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gen68k) + +set(GEN68K_INC c68k_ini.inc c68k_op0.inc c68k_op1.inc c68k_op2.inc c68k_op3.inc c68k_op4.inc c68k_op5.inc c68k_op6.inc c68k_op7.inc c68k_op8.inc c68k_op9.inc c68k_opA.inc c68k_opB.inc c68k_opC.inc c68k_opD.inc c68k_opE.inc c68k_opF.inc) + +add_custom_command(OUTPUT ${GEN68K_INC} COMMAND gen68k DEPENDS gen68k) + +add_custom_target(c68kinc ALL DEPENDS ${GEN68K_INC}) diff --git a/yabause/src/c68k/Makefile.am b/yabause/src/c68k/Makefile.am new file mode 100644 index 0000000000..f4519b2048 --- /dev/null +++ b/yabause/src/c68k/Makefile.am @@ -0,0 +1,11 @@ +EXTRA_DIST = c68k.h c68kmac.inc gen68k.h gen68k.inc +noinst_PROGRAMS = gen68k +gen68k_SOURCES = c68kexec.c c68k.c gen68k.c +gen68k_CFLAGS = -DC68K_GEN +gen68k_LDFLAGS = -DC68K_GEN + +all-local: gen68k$(EXEEXT) + ./gen68k$(EXEEXT) + +clean-local: + -rm *_*.inc diff --git a/yabause/src/c68k/c68k.c b/yabause/src/c68k/c68k.c new file mode 100644 index 0000000000..eb77fb4605 --- /dev/null +++ b/yabause/src/c68k/c68k.c @@ -0,0 +1,311 @@ +/* Copyright 2003-2004 Stephane Dallongeville + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/********************************************************************************* + * + * C68K (68000 CPU emulator) version 0.80 + * Compiled with Dev-C++ + * Copyright 2003-2004 Stephane Dallongeville + * + ********************************************************************************/ + +#include +#include + +#include "c68k.h" + +// shared global variable +////////////////////////// + +c68k_struc C68K; + +// include macro file +////////////////////// + +#include "c68kmac.inc" + +// prototype +///////////// + +u32 FASTCALL C68k_Read_Dummy(const u32 adr); +void FASTCALL C68k_Write_Dummy(const u32 adr, u32 data); + +u32 C68k_Read_Byte(c68k_struc *cpu, u32 adr); +u32 C68k_Read_Word(c68k_struc *cpu, u32 adr); +u32 C68k_Read_Long(c68k_struc *cpu, u32 adr); +void C68k_Write_Byte(c68k_struc *cpu, u32 adr, u32 data); +void C68k_Write_Word(c68k_struc *cpu, u32 adr, u32 data); +void C68k_Write_Long(c68k_struc *cpu, u32 adr, u32 data); + +s32 FASTCALL C68k_Interrupt_Ack_Dummy(s32 level); +void FASTCALL C68k_Reset_Dummy(void); + +// core main functions +/////////////////////// + +void C68k_Init(c68k_struc *cpu, C68K_INT_CALLBACK *int_cb) +{ + memset(cpu, 0, sizeof(c68k_struc)); + + C68k_Set_ReadB(cpu, C68k_Read_Dummy); + C68k_Set_ReadW(cpu, C68k_Read_Dummy); + + C68k_Set_WriteB(cpu, C68k_Write_Dummy); + C68k_Set_WriteW(cpu, C68k_Write_Dummy); + + if (int_cb) cpu->Interrupt_CallBack = int_cb; + else cpu->Interrupt_CallBack = C68k_Interrupt_Ack_Dummy; + cpu->Reset_CallBack = C68k_Reset_Dummy; + + // used to init JumpTable + cpu->Status |= C68K_DISABLE; + C68k_Exec(cpu, 0); + + cpu->Status &= ~C68K_DISABLE; +} + +s32 FASTCALL C68k_Reset(c68k_struc *cpu) +{ + memset(cpu, 0, ((u8 *)&(cpu->dirty1)) - ((u8 *)&(cpu->D[0]))); + + cpu->flag_notZ = 1; + cpu->flag_I = 7; + cpu->flag_S = C68K_SR_S; + + cpu->A[7] = C68k_Read_Long(cpu, 0); + C68k_Set_PC(cpu, C68k_Read_Long(cpu, 4)); + + return cpu->Status; +} + +///////////////////////////////// + +void FASTCALL C68k_Set_IRQ(c68k_struc *cpu, s32 level) +{ + cpu->IRQLine = level; + if (cpu->Status & C68K_RUNNING) + { + cpu->CycleSup = cpu->CycleIO; + cpu->CycleIO = 0; + } + cpu->Status &= ~(C68K_HALTED | C68K_WAITING); +} + +///////////////////////////////// + +s32 FASTCALL C68k_Get_CycleToDo(c68k_struc *cpu) +{ + if (!(cpu->Status & C68K_RUNNING)) return -1; + + return cpu->CycleToDo; +} + +s32 FASTCALL C68k_Get_CycleRemaining(c68k_struc *cpu) +{ + if (!(cpu->Status & C68K_RUNNING)) return -1; + + return (cpu->CycleIO + cpu->CycleSup); +} + +s32 FASTCALL C68k_Get_CycleDone(c68k_struc *cpu) +{ + if (!(cpu->Status & C68K_RUNNING)) return -1; + + return (cpu->CycleToDo - (cpu->CycleIO + cpu->CycleSup)); +} + +void FASTCALL C68k_Release_Cycle(c68k_struc *cpu) +{ + if (cpu->Status & C68K_RUNNING) cpu->CycleIO = cpu->CycleSup = 0; +} + +void FASTCALL C68k_Add_Cycle(c68k_struc *cpu, s32 cycle) +{ + if (cpu->Status & C68K_RUNNING) cpu->CycleIO -= cycle; +} + +// Read / Write dummy functions +//////////////////////////////// + +u32 FASTCALL C68k_Read_Dummy(UNUSED const u32 adr) +{ + return 0; +} + +void FASTCALL C68k_Write_Dummy(UNUSED const u32 adr, UNUSED u32 data) +{ + +} + +s32 FASTCALL C68k_Interrupt_Ack_Dummy(s32 level) +{ + // return vector + return (C68K_INTERRUPT_AUTOVECTOR_EX + level); +} + +void FASTCALL C68k_Reset_Dummy(void) +{ + +} + +// Read / Write core functions +/////////////////////////////// + +u32 C68k_Read_Byte(c68k_struc *cpu, u32 adr) +{ + return cpu->Read_Byte(adr); +} + +u32 C68k_Read_Word(c68k_struc *cpu, u32 adr) +{ + return cpu->Read_Word(adr); +} + +u32 C68k_Read_Long(c68k_struc *cpu, u32 adr) +{ +#ifdef C68K_BIG_ENDIAN + return (cpu->Read_Word(adr) << 16) | (cpu->Read_Word(adr + 2) & 0xFFFF); +#else + return (cpu->Read_Word(adr) << 16) | (cpu->Read_Word(adr + 2) & 0xFFFF); +#endif +} + +void C68k_Write_Byte(c68k_struc *cpu, u32 adr, u32 data) +{ + cpu->Write_Byte(adr, data); +} + +void C68k_Write_Word(c68k_struc *cpu, u32 adr, u32 data) +{ + cpu->Write_Word(adr, data); +} + +void C68k_Write_Long(c68k_struc *cpu, u32 adr, u32 data) +{ +#ifdef C68K_BIG_ENDIAN + cpu->Write_Word(adr, data >> 16); + cpu->Write_Word(adr + 2, data & 0xFFFF); +#else + cpu->Write_Word(adr, data >> 16); + cpu->Write_Word(adr + 2, data & 0xFFFF); +#endif +} + +// setting core functions +////////////////////////// + +void C68k_Set_Fetch(c68k_struc *cpu, u32 low_adr, u32 high_adr, pointer fetch_adr) +{ + u32 i, j; + + i = (low_adr >> C68K_FETCH_SFT) & C68K_FETCH_MASK; + j = (high_adr >> C68K_FETCH_SFT) & C68K_FETCH_MASK; + fetch_adr -= i << C68K_FETCH_SFT; + while (i <= j) cpu->Fetch[i++] = fetch_adr; +} + +void C68k_Set_ReadB(c68k_struc *cpu, C68K_READ *Func) +{ + cpu->Read_Byte = Func; +} + +void C68k_Set_ReadW(c68k_struc *cpu, C68K_READ *Func) +{ + cpu->Read_Word = Func; +} + +void C68k_Set_WriteB(c68k_struc *cpu, C68K_WRITE *Func) +{ + cpu->Write_Byte = Func; +} + +void C68k_Set_WriteW(c68k_struc *cpu, C68K_WRITE *Func) +{ + cpu->Write_Word = Func; +} + +// externals main functions +//////////////////////////// + +u32 C68k_Get_DReg(c68k_struc *cpu, u32 num) +{ + return cpu->D[num]; +} + +u32 C68k_Get_AReg(c68k_struc *cpu, u32 num) +{ + return cpu->A[num]; +} + +u32 C68k_Get_PC(c68k_struc *cpu) +{ + return (cpu->PC - cpu->BasePC); +} + +u32 C68k_Get_SR(c68k_struc *cpu) +{ + c68k_struc *CPU = cpu; + return GET_SR; +} + +u32 C68k_Get_USP(c68k_struc *cpu) +{ + if (cpu->flag_S) return cpu->USP; + else return cpu->A[7]; +} + +u32 C68k_Get_MSP(c68k_struc *cpu) +{ + if (cpu->flag_S) return cpu->A[7]; + else return cpu->USP; +} + +void C68k_Set_DReg(c68k_struc *cpu, u32 num, u32 val) +{ + cpu->D[num] = val; +} + +void C68k_Set_AReg(c68k_struc *cpu, u32 num, u32 val) +{ + cpu->A[num] = val; +} + +void C68k_Set_PC(c68k_struc *cpu, u32 val) +{ + cpu->BasePC = cpu->Fetch[(val >> C68K_FETCH_SFT) & C68K_FETCH_MASK]; + cpu->PC = val + cpu->BasePC; +} + +void C68k_Set_SR(c68k_struc *cpu, u32 val) +{ + c68k_struc *CPU = cpu; + SET_SR(val); +} + +void C68k_Set_USP(c68k_struc *cpu, u32 val) +{ + if (cpu->flag_S) cpu->USP = val; + else cpu->A[7] = val; +} + +void C68k_Set_MSP(c68k_struc *cpu, u32 val) +{ + if (cpu->flag_S) cpu->A[7] = val; + else cpu->USP = val; +} diff --git a/yabause/src/c68k/c68k.h b/yabause/src/c68k/c68k.h new file mode 100644 index 0000000000..a8297f4494 --- /dev/null +++ b/yabause/src/c68k/c68k.h @@ -0,0 +1,213 @@ +/* Copyright 2003-2004 Stephane Dallongeville + Copyright 2004 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/********************************************************************************* + * C68K.H : + * + * C68K include file + * + ********************************************************************************/ + +#ifndef _C68K_H_ +#define _C68K_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../core.h" + +// setting +/////////// + +//#define NEOCD_HLE + +//#define C68K_GEN +#define C68K_BYTE_SWAP_OPT + +#ifdef WORDS_BIGENDIAN +#define C68K_BIG_ENDIAN +#endif + +#ifdef C68K_BIG_ENDIAN + #define BYTE_OFF 3 + #define WORD_OFF 1 +#else + #define BYTE_OFF 0 + #define WORD_OFF 0 +#endif + +//#define C68K_NO_JUMP_TABLE +//#define C68K_DEBUG +#define C68K_TAS_CAN_SET_MEMORY +//#define C68K_CONST_JUMP_TABLE +//#define C68K_AUTOVECTOR_CALLBACK + +// 68K core types definitions +////////////////////////////// + +#define C68K_FETCH_BITS 8 // [4-12] default = 8 +#define C68K_ADR_BITS 24 + +#define C68K_FETCH_SFT (C68K_ADR_BITS - C68K_FETCH_BITS) +#define C68K_FETCH_BANK (1 << C68K_FETCH_BITS) +#define C68K_FETCH_MASK (C68K_FETCH_BANK - 1) + +#define C68K_SR_C_SFT 8 +#define C68K_SR_V_SFT 7 +#define C68K_SR_Z_SFT 0 +#define C68K_SR_N_SFT 7 +#define C68K_SR_X_SFT 8 + +#define C68K_SR_S_SFT 13 + +#define C68K_SR_C (1 << C68K_SR_C_SFT) +#define C68K_SR_V (1 << C68K_SR_V_SFT) +#define C68K_SR_Z 0 +#define C68K_SR_N (1 << C68K_SR_N_SFT) +#define C68K_SR_X (1 << C68K_SR_X_SFT) + +#define C68K_SR_S (1 << C68K_SR_S_SFT) + +#define C68K_CCR_MASK 0x1F +#define C68K_SR_MASK (0x2700 | C68K_CCR_MASK) + +// exception defines taken from musashi core +#define C68K_RESET_EX 1 +#define C68K_BUS_ERROR_EX 2 +#define C68K_ADDRESS_ERROR_EX 3 +#define C68K_ILLEGAL_INSTRUCTION_EX 4 +#define C68K_ZERO_DIVIDE_EX 5 +#define C68K_CHK_EX 6 +#define C68K_TRAPV_EX 7 +#define C68K_PRIVILEGE_VIOLATION_EX 8 +#define C68K_TRACE_EX 9 +#define C68K_1010_EX 10 +#define C68K_1111_EX 11 +#define C68K_FORMAT_ERROR_EX 14 +#define C68K_UNINITIALIZED_INTERRUPT_EX 15 +#define C68K_SPURIOUS_INTERRUPT_EX 24 +#define C68K_INTERRUPT_AUTOVECTOR_EX 24 +#define C68K_TRAP_BASE_EX 32 + +#define C68K_INT_ACK_AUTOVECTOR -1 + +#define C68K_RUNNING 0x01 +#define C68K_HALTED 0x02 +#define C68K_WAITING 0x04 +#define C68K_DISABLE 0x10 +#define C68K_FAULTED 0x40 + +typedef u32 FASTCALL C68K_READ(const u32 adr); +typedef void FASTCALL C68K_WRITE(const u32 adr, u32 data); + +typedef s32 FASTCALL C68K_INT_CALLBACK(s32 level); +typedef void FASTCALL C68K_RESET_CALLBACK(void); + +typedef struct { + u32 D[8]; // 32 bytes aligned + u32 A[8]; // 16 bytes aligned + + u32 flag_C; // 32 bytes aligned + u32 flag_V; + u32 flag_notZ; + u32 flag_N; + + u32 flag_X; // 16 bytes aligned + u32 flag_I; + u32 flag_S; + + u32 USP; + + pointer PC; // 32 bytes aligned + pointer BasePC; + u32 Status; + s32 IRQLine; + + s32 CycleToDo; // 16 bytes aligned + s32 CycleIO; + s32 CycleSup; + u32 dirty1; + + C68K_READ *Read_Byte; // 32 bytes aligned + C68K_READ *Read_Word; + + C68K_WRITE *Write_Byte; + C68K_WRITE *Write_Word; + + C68K_INT_CALLBACK *Interrupt_CallBack; // 16 bytes aligned + C68K_RESET_CALLBACK *Reset_CallBack; + + pointer Fetch[C68K_FETCH_BANK]; // 32 bytes aligned +} c68k_struc; + + +// 68K core var declaration +//////////////////////////// + +extern c68k_struc C68K; + + +// 68K core function declaration +///////////////////////////////// + +void C68k_Init(c68k_struc *cpu, C68K_INT_CALLBACK *int_cb); + +s32 FASTCALL C68k_Reset(c68k_struc *cpu); + +// if < 0 --> error (cpu state returned) +// if >= 0 --> number of extras cycles done +s32 FASTCALL C68k_Exec(c68k_struc *cpu, s32 cycle); + +void FASTCALL C68k_Set_IRQ(c68k_struc *cpu, s32 level); + +s32 FASTCALL C68k_Get_CycleToDo(c68k_struc *cpu); +s32 FASTCALL C68k_Get_CycleRemaining(c68k_struc *cpu); +s32 FASTCALL C68k_Get_CycleDone(c68k_struc *cpu); +void FASTCALL C68k_Release_Cycle(c68k_struc *cpu); +void FASTCALL C68k_Add_Cycle(c68k_struc *cpu, s32 cycle); + +void C68k_Set_Fetch(c68k_struc *cpu, u32 low_adr, u32 high_adr, pointer fetch_adr); + +void C68k_Set_ReadB(c68k_struc *cpu, C68K_READ *Func); +void C68k_Set_ReadW(c68k_struc *cpu, C68K_READ *Func); +void C68k_Set_WriteB(c68k_struc *cpu, C68K_WRITE *Func); +void C68k_Set_WriteW(c68k_struc *cpu, C68K_WRITE *Func); + +u32 C68k_Get_DReg(c68k_struc *cpu, u32 num); +u32 C68k_Get_AReg(c68k_struc *cpu, u32 num); +u32 C68k_Get_PC(c68k_struc *cpu); +u32 C68k_Get_SR(c68k_struc *cpu); +u32 C68k_Get_USP(c68k_struc *cpu); +u32 C68k_Get_MSP(c68k_struc *cpu); + +void C68k_Set_DReg(c68k_struc *cpu, u32 num, u32 val); +void C68k_Set_AReg(c68k_struc *cpu, u32 num, u32 val); +void C68k_Set_PC(c68k_struc *cpu, u32 val); +void C68k_Set_SR(c68k_struc *cpu, u32 val); +void C68k_Set_USP(c68k_struc *cpu, u32 val); +void C68k_Set_MSP(c68k_struc *cpu, u32 val); + +#ifdef __cplusplus +} +#endif + +#endif // _C68K_H_ + diff --git a/yabause/src/c68k/c68k_ini.inc b/yabause/src/c68k/c68k_ini.inc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/yabause/src/c68k/c68k_op0.inc b/yabause/src/c68k/c68k_op0.inc new file mode 100644 index 0000000000..59e79c2169 --- /dev/null +++ b/yabause/src/c68k/c68k_op0.inc @@ -0,0 +1,8330 @@ +case 0x0001: +case 0x0002: +case 0x0003: +case 0x0004: +case 0x0005: +case 0x0006: +case 0x0007: + +// ORI +case 0x0000: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0011: +case 0x0012: +case 0x0013: +case 0x0014: +case 0x0015: +case 0x0016: +case 0x0017: + +// ORI +case 0x0010: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0019: +case 0x001A: +case 0x001B: +case 0x001C: +case 0x001D: +case 0x001E: + +// ORI +case 0x0018: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0021: +case 0x0022: +case 0x0023: +case 0x0024: +case 0x0025: +case 0x0026: + +// ORI +case 0x0020: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0029: +case 0x002A: +case 0x002B: +case 0x002C: +case 0x002D: +case 0x002E: +case 0x002F: + +// ORI +case 0x0028: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0031: +case 0x0032: +case 0x0033: +case 0x0034: +case 0x0035: +case 0x0036: +case 0x0037: + +// ORI +case 0x0030: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// ORI +case 0x0038: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// ORI +case 0x0039: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// ORI +case 0x001F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// ORI +case 0x0027: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0041: +case 0x0042: +case 0x0043: +case 0x0044: +case 0x0045: +case 0x0046: +case 0x0047: + +// ORI +case 0x0040: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0051: +case 0x0052: +case 0x0053: +case 0x0054: +case 0x0055: +case 0x0056: +case 0x0057: + +// ORI +case 0x0050: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0059: +case 0x005A: +case 0x005B: +case 0x005C: +case 0x005D: +case 0x005E: + +// ORI +case 0x0058: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0061: +case 0x0062: +case 0x0063: +case 0x0064: +case 0x0065: +case 0x0066: + +// ORI +case 0x0060: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0069: +case 0x006A: +case 0x006B: +case 0x006C: +case 0x006D: +case 0x006E: +case 0x006F: + +// ORI +case 0x0068: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x0071: +case 0x0072: +case 0x0073: +case 0x0074: +case 0x0075: +case 0x0076: +case 0x0077: + +// ORI +case 0x0070: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// ORI +case 0x0078: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ORI +case 0x0079: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// ORI +case 0x005F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ORI +case 0x0067: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0081: +case 0x0082: +case 0x0083: +case 0x0084: +case 0x0085: +case 0x0086: +case 0x0087: + +// ORI +case 0x0080: +{ + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(16) +case 0x0091: +case 0x0092: +case 0x0093: +case 0x0094: +case 0x0095: +case 0x0096: +case 0x0097: + +// ORI +case 0x0090: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0099: +case 0x009A: +case 0x009B: +case 0x009C: +case 0x009D: +case 0x009E: + +// ORI +case 0x0098: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x00A1: +case 0x00A2: +case 0x00A3: +case 0x00A4: +case 0x00A5: +case 0x00A6: + +// ORI +case 0x00A0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x00A9: +case 0x00AA: +case 0x00AB: +case 0x00AC: +case 0x00AD: +case 0x00AE: +case 0x00AF: + +// ORI +case 0x00A8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x00B1: +case 0x00B2: +case 0x00B3: +case 0x00B4: +case 0x00B5: +case 0x00B6: +case 0x00B7: + +// ORI +case 0x00B0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// ORI +case 0x00B8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// ORI +case 0x00B9: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// ORI +case 0x009F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// ORI +case 0x00A7: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// ORICCR +case 0x003C: +{ + u32 res; + res = FETCH_BYTE & C68K_CCR_MASK; + PC += 2; + res |= GET_CCR; + SET_CCR(res) +} +RET(20) + +// ORISR +case 0x007C: +{ + u32 res; + if (CPU->flag_S) + { + res = FETCH_WORD & C68K_SR_MASK; + PC += 2; + res |= GET_SR; + SET_SR(res) + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; +case 0x0201: +case 0x0202: +case 0x0203: +case 0x0204: +case 0x0205: +case 0x0206: +case 0x0207: + +// ANDI +case 0x0200: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0211: +case 0x0212: +case 0x0213: +case 0x0214: +case 0x0215: +case 0x0216: +case 0x0217: + +// ANDI +case 0x0210: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0219: +case 0x021A: +case 0x021B: +case 0x021C: +case 0x021D: +case 0x021E: + +// ANDI +case 0x0218: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0221: +case 0x0222: +case 0x0223: +case 0x0224: +case 0x0225: +case 0x0226: + +// ANDI +case 0x0220: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0229: +case 0x022A: +case 0x022B: +case 0x022C: +case 0x022D: +case 0x022E: +case 0x022F: + +// ANDI +case 0x0228: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0231: +case 0x0232: +case 0x0233: +case 0x0234: +case 0x0235: +case 0x0236: +case 0x0237: + +// ANDI +case 0x0230: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// ANDI +case 0x0238: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// ANDI +case 0x0239: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// ANDI +case 0x021F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// ANDI +case 0x0227: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0241: +case 0x0242: +case 0x0243: +case 0x0244: +case 0x0245: +case 0x0246: +case 0x0247: + +// ANDI +case 0x0240: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0251: +case 0x0252: +case 0x0253: +case 0x0254: +case 0x0255: +case 0x0256: +case 0x0257: + +// ANDI +case 0x0250: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0259: +case 0x025A: +case 0x025B: +case 0x025C: +case 0x025D: +case 0x025E: + +// ANDI +case 0x0258: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0261: +case 0x0262: +case 0x0263: +case 0x0264: +case 0x0265: +case 0x0266: + +// ANDI +case 0x0260: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0269: +case 0x026A: +case 0x026B: +case 0x026C: +case 0x026D: +case 0x026E: +case 0x026F: + +// ANDI +case 0x0268: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x0271: +case 0x0272: +case 0x0273: +case 0x0274: +case 0x0275: +case 0x0276: +case 0x0277: + +// ANDI +case 0x0270: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// ANDI +case 0x0278: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ANDI +case 0x0279: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// ANDI +case 0x025F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ANDI +case 0x0267: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0281: +case 0x0282: +case 0x0283: +case 0x0284: +case 0x0285: +case 0x0286: +case 0x0287: + +// ANDI +case 0x0280: +{ + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(16) +case 0x0291: +case 0x0292: +case 0x0293: +case 0x0294: +case 0x0295: +case 0x0296: +case 0x0297: + +// ANDI +case 0x0290: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0299: +case 0x029A: +case 0x029B: +case 0x029C: +case 0x029D: +case 0x029E: + +// ANDI +case 0x0298: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x02A1: +case 0x02A2: +case 0x02A3: +case 0x02A4: +case 0x02A5: +case 0x02A6: + +// ANDI +case 0x02A0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x02A9: +case 0x02AA: +case 0x02AB: +case 0x02AC: +case 0x02AD: +case 0x02AE: +case 0x02AF: + +// ANDI +case 0x02A8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x02B1: +case 0x02B2: +case 0x02B3: +case 0x02B4: +case 0x02B5: +case 0x02B6: +case 0x02B7: + +// ANDI +case 0x02B0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// ANDI +case 0x02B8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// ANDI +case 0x02B9: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// ANDI +case 0x029F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// ANDI +case 0x02A7: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// ANDICCR +case 0x023C: +{ + u32 res; + res = FETCH_BYTE & C68K_CCR_MASK; + PC += 2; + res &= GET_CCR; + SET_CCR(res) +} +RET(20) + +// ANDISR +case 0x027C: +{ + u32 res; + if (CPU->flag_S) + { + res = FETCH_WORD & C68K_SR_MASK; + PC += 2; + res &= GET_SR; + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; +case 0x0A01: +case 0x0A02: +case 0x0A03: +case 0x0A04: +case 0x0A05: +case 0x0A06: +case 0x0A07: + +// EORI +case 0x0A00: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0A11: +case 0x0A12: +case 0x0A13: +case 0x0A14: +case 0x0A15: +case 0x0A16: +case 0x0A17: + +// EORI +case 0x0A10: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0A19: +case 0x0A1A: +case 0x0A1B: +case 0x0A1C: +case 0x0A1D: +case 0x0A1E: + +// EORI +case 0x0A18: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0A21: +case 0x0A22: +case 0x0A23: +case 0x0A24: +case 0x0A25: +case 0x0A26: + +// EORI +case 0x0A20: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0A29: +case 0x0A2A: +case 0x0A2B: +case 0x0A2C: +case 0x0A2D: +case 0x0A2E: +case 0x0A2F: + +// EORI +case 0x0A28: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0A31: +case 0x0A32: +case 0x0A33: +case 0x0A34: +case 0x0A35: +case 0x0A36: +case 0x0A37: + +// EORI +case 0x0A30: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// EORI +case 0x0A38: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// EORI +case 0x0A39: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// EORI +case 0x0A1F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// EORI +case 0x0A27: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0A41: +case 0x0A42: +case 0x0A43: +case 0x0A44: +case 0x0A45: +case 0x0A46: +case 0x0A47: + +// EORI +case 0x0A40: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0A51: +case 0x0A52: +case 0x0A53: +case 0x0A54: +case 0x0A55: +case 0x0A56: +case 0x0A57: + +// EORI +case 0x0A50: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0A59: +case 0x0A5A: +case 0x0A5B: +case 0x0A5C: +case 0x0A5D: +case 0x0A5E: + +// EORI +case 0x0A58: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0A61: +case 0x0A62: +case 0x0A63: +case 0x0A64: +case 0x0A65: +case 0x0A66: + +// EORI +case 0x0A60: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0A69: +case 0x0A6A: +case 0x0A6B: +case 0x0A6C: +case 0x0A6D: +case 0x0A6E: +case 0x0A6F: + +// EORI +case 0x0A68: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x0A71: +case 0x0A72: +case 0x0A73: +case 0x0A74: +case 0x0A75: +case 0x0A76: +case 0x0A77: + +// EORI +case 0x0A70: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// EORI +case 0x0A78: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// EORI +case 0x0A79: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// EORI +case 0x0A5F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// EORI +case 0x0A67: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0A81: +case 0x0A82: +case 0x0A83: +case 0x0A84: +case 0x0A85: +case 0x0A86: +case 0x0A87: + +// EORI +case 0x0A80: +{ + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(16) +case 0x0A91: +case 0x0A92: +case 0x0A93: +case 0x0A94: +case 0x0A95: +case 0x0A96: +case 0x0A97: + +// EORI +case 0x0A90: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0A99: +case 0x0A9A: +case 0x0A9B: +case 0x0A9C: +case 0x0A9D: +case 0x0A9E: + +// EORI +case 0x0A98: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0AA1: +case 0x0AA2: +case 0x0AA3: +case 0x0AA4: +case 0x0AA5: +case 0x0AA6: + +// EORI +case 0x0AA0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x0AA9: +case 0x0AAA: +case 0x0AAB: +case 0x0AAC: +case 0x0AAD: +case 0x0AAE: +case 0x0AAF: + +// EORI +case 0x0AA8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x0AB1: +case 0x0AB2: +case 0x0AB3: +case 0x0AB4: +case 0x0AB5: +case 0x0AB6: +case 0x0AB7: + +// EORI +case 0x0AB0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// EORI +case 0x0AB8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// EORI +case 0x0AB9: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// EORI +case 0x0A9F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// EORI +case 0x0AA7: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// EORICCR +case 0x0A3C: +{ + u32 res; + res = FETCH_BYTE & C68K_CCR_MASK; + PC += 2; + res ^= GET_CCR; + SET_CCR(res) +} +RET(20) + +// EORISR +case 0x0A7C: +{ + u32 res; + if (CPU->flag_S) + { + res = FETCH_WORD & C68K_SR_MASK; + PC += 2; + res ^= GET_SR; + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; +case 0x0401: +case 0x0402: +case 0x0403: +case 0x0404: +case 0x0405: +case 0x0406: +case 0x0407: + +// SUBI +case 0x0400: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0411: +case 0x0412: +case 0x0413: +case 0x0414: +case 0x0415: +case 0x0416: +case 0x0417: + +// SUBI +case 0x0410: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0419: +case 0x041A: +case 0x041B: +case 0x041C: +case 0x041D: +case 0x041E: + +// SUBI +case 0x0418: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0421: +case 0x0422: +case 0x0423: +case 0x0424: +case 0x0425: +case 0x0426: + +// SUBI +case 0x0420: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0429: +case 0x042A: +case 0x042B: +case 0x042C: +case 0x042D: +case 0x042E: +case 0x042F: + +// SUBI +case 0x0428: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0431: +case 0x0432: +case 0x0433: +case 0x0434: +case 0x0435: +case 0x0436: +case 0x0437: + +// SUBI +case 0x0430: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// SUBI +case 0x0438: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// SUBI +case 0x0439: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// SUBI +case 0x041F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// SUBI +case 0x0427: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0441: +case 0x0442: +case 0x0443: +case 0x0444: +case 0x0445: +case 0x0446: +case 0x0447: + +// SUBI +case 0x0440: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0451: +case 0x0452: +case 0x0453: +case 0x0454: +case 0x0455: +case 0x0456: +case 0x0457: + +// SUBI +case 0x0450: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0459: +case 0x045A: +case 0x045B: +case 0x045C: +case 0x045D: +case 0x045E: + +// SUBI +case 0x0458: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0461: +case 0x0462: +case 0x0463: +case 0x0464: +case 0x0465: +case 0x0466: + +// SUBI +case 0x0460: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0469: +case 0x046A: +case 0x046B: +case 0x046C: +case 0x046D: +case 0x046E: +case 0x046F: + +// SUBI +case 0x0468: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x0471: +case 0x0472: +case 0x0473: +case 0x0474: +case 0x0475: +case 0x0476: +case 0x0477: + +// SUBI +case 0x0470: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// SUBI +case 0x0478: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// SUBI +case 0x0479: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// SUBI +case 0x045F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// SUBI +case 0x0467: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0481: +case 0x0482: +case 0x0483: +case 0x0484: +case 0x0485: +case 0x0486: +case 0x0487: + +// SUBI +case 0x0480: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(16) +case 0x0491: +case 0x0492: +case 0x0493: +case 0x0494: +case 0x0495: +case 0x0496: +case 0x0497: + +// SUBI +case 0x0490: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0499: +case 0x049A: +case 0x049B: +case 0x049C: +case 0x049D: +case 0x049E: + +// SUBI +case 0x0498: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x04A1: +case 0x04A2: +case 0x04A3: +case 0x04A4: +case 0x04A5: +case 0x04A6: + +// SUBI +case 0x04A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x04A9: +case 0x04AA: +case 0x04AB: +case 0x04AC: +case 0x04AD: +case 0x04AE: +case 0x04AF: + +// SUBI +case 0x04A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x04B1: +case 0x04B2: +case 0x04B3: +case 0x04B4: +case 0x04B5: +case 0x04B6: +case 0x04B7: + +// SUBI +case 0x04B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// SUBI +case 0x04B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// SUBI +case 0x04B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// SUBI +case 0x049F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// SUBI +case 0x04A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x0601: +case 0x0602: +case 0x0603: +case 0x0604: +case 0x0605: +case 0x0606: +case 0x0607: + +// ADDI +case 0x0600: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0611: +case 0x0612: +case 0x0613: +case 0x0614: +case 0x0615: +case 0x0616: +case 0x0617: + +// ADDI +case 0x0610: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0619: +case 0x061A: +case 0x061B: +case 0x061C: +case 0x061D: +case 0x061E: + +// ADDI +case 0x0618: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0621: +case 0x0622: +case 0x0623: +case 0x0624: +case 0x0625: +case 0x0626: + +// ADDI +case 0x0620: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0629: +case 0x062A: +case 0x062B: +case 0x062C: +case 0x062D: +case 0x062E: +case 0x062F: + +// ADDI +case 0x0628: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0631: +case 0x0632: +case 0x0633: +case 0x0634: +case 0x0635: +case 0x0636: +case 0x0637: + +// ADDI +case 0x0630: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// ADDI +case 0x0638: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// ADDI +case 0x0639: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// ADDI +case 0x061F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// ADDI +case 0x0627: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0641: +case 0x0642: +case 0x0643: +case 0x0644: +case 0x0645: +case 0x0646: +case 0x0647: + +// ADDI +case 0x0640: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0651: +case 0x0652: +case 0x0653: +case 0x0654: +case 0x0655: +case 0x0656: +case 0x0657: + +// ADDI +case 0x0650: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0659: +case 0x065A: +case 0x065B: +case 0x065C: +case 0x065D: +case 0x065E: + +// ADDI +case 0x0658: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x0661: +case 0x0662: +case 0x0663: +case 0x0664: +case 0x0665: +case 0x0666: + +// ADDI +case 0x0660: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0669: +case 0x066A: +case 0x066B: +case 0x066C: +case 0x066D: +case 0x066E: +case 0x066F: + +// ADDI +case 0x0668: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x0671: +case 0x0672: +case 0x0673: +case 0x0674: +case 0x0675: +case 0x0676: +case 0x0677: + +// ADDI +case 0x0670: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// ADDI +case 0x0678: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ADDI +case 0x0679: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// ADDI +case 0x065F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ADDI +case 0x0667: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x0681: +case 0x0682: +case 0x0683: +case 0x0684: +case 0x0685: +case 0x0686: +case 0x0687: + +// ADDI +case 0x0680: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(16) +case 0x0691: +case 0x0692: +case 0x0693: +case 0x0694: +case 0x0695: +case 0x0696: +case 0x0697: + +// ADDI +case 0x0690: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x0699: +case 0x069A: +case 0x069B: +case 0x069C: +case 0x069D: +case 0x069E: + +// ADDI +case 0x0698: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x06A1: +case 0x06A2: +case 0x06A3: +case 0x06A4: +case 0x06A5: +case 0x06A6: + +// ADDI +case 0x06A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x06A9: +case 0x06AA: +case 0x06AB: +case 0x06AC: +case 0x06AD: +case 0x06AE: +case 0x06AF: + +// ADDI +case 0x06A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x06B1: +case 0x06B2: +case 0x06B3: +case 0x06B4: +case 0x06B5: +case 0x06B6: +case 0x06B7: + +// ADDI +case 0x06B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// ADDI +case 0x06B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// ADDI +case 0x06B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// ADDI +case 0x069F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// ADDI +case 0x06A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x0C01: +case 0x0C02: +case 0x0C03: +case 0x0C04: +case 0x0C05: +case 0x0C06: +case 0x0C07: + +// CMPI +case 0x0C00: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; +} +RET(8) +case 0x0C11: +case 0x0C12: +case 0x0C13: +case 0x0C14: +case 0x0C15: +case 0x0C16: +case 0x0C17: + +// CMPI +case 0x0C10: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0x0C19: +case 0x0C1A: +case 0x0C1B: +case 0x0C1C: +case 0x0C1D: +case 0x0C1E: + +// CMPI +case 0x0C18: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0x0C21: +case 0x0C22: +case 0x0C23: +case 0x0C24: +case 0x0C25: +case 0x0C26: + +// CMPI +case 0x0C20: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(14) +case 0x0C29: +case 0x0C2A: +case 0x0C2B: +case 0x0C2C: +case 0x0C2D: +case 0x0C2E: +case 0x0C2F: + +// CMPI +case 0x0C28: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(16) +case 0x0C31: +case 0x0C32: +case 0x0C33: +case 0x0C34: +case 0x0C35: +case 0x0C36: +case 0x0C37: + +// CMPI +case 0x0C30: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(18) + +// CMPI +case 0x0C38: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(16) + +// CMPI +case 0x0C39: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(20) + +// CMPI +case 0x0C1F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) + +// CMPI +case 0x0C27: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(14) +case 0x0C41: +case 0x0C42: +case 0x0C43: +case 0x0C44: +case 0x0C45: +case 0x0C46: +case 0x0C47: + +// CMPI +case 0x0C40: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; +} +RET(8) +case 0x0C51: +case 0x0C52: +case 0x0C53: +case 0x0C54: +case 0x0C55: +case 0x0C56: +case 0x0C57: + +// CMPI +case 0x0C50: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0x0C59: +case 0x0C5A: +case 0x0C5B: +case 0x0C5C: +case 0x0C5D: +case 0x0C5E: + +// CMPI +case 0x0C58: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0x0C61: +case 0x0C62: +case 0x0C63: +case 0x0C64: +case 0x0C65: +case 0x0C66: + +// CMPI +case 0x0C60: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(14) +case 0x0C69: +case 0x0C6A: +case 0x0C6B: +case 0x0C6C: +case 0x0C6D: +case 0x0C6E: +case 0x0C6F: + +// CMPI +case 0x0C68: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(16) +case 0x0C71: +case 0x0C72: +case 0x0C73: +case 0x0C74: +case 0x0C75: +case 0x0C76: +case 0x0C77: + +// CMPI +case 0x0C70: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(18) + +// CMPI +case 0x0C78: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(16) + +// CMPI +case 0x0C79: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(20) + +// CMPI +case 0x0C5F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) + +// CMPI +case 0x0C67: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(14) +case 0x0C81: +case 0x0C82: +case 0x0C83: +case 0x0C84: +case 0x0C85: +case 0x0C86: +case 0x0C87: + +// CMPI +case 0x0C80: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(14) +case 0x0C91: +case 0x0C92: +case 0x0C93: +case 0x0C94: +case 0x0C95: +case 0x0C96: +case 0x0C97: + +// CMPI +case 0x0C90: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0x0C99: +case 0x0C9A: +case 0x0C9B: +case 0x0C9C: +case 0x0C9D: +case 0x0C9E: + +// CMPI +case 0x0C98: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0x0CA1: +case 0x0CA2: +case 0x0CA3: +case 0x0CA4: +case 0x0CA5: +case 0x0CA6: + +// CMPI +case 0x0CA0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(22) +case 0x0CA9: +case 0x0CAA: +case 0x0CAB: +case 0x0CAC: +case 0x0CAD: +case 0x0CAE: +case 0x0CAF: + +// CMPI +case 0x0CA8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(24) +case 0x0CB1: +case 0x0CB2: +case 0x0CB3: +case 0x0CB4: +case 0x0CB5: +case 0x0CB6: +case 0x0CB7: + +// CMPI +case 0x0CB0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(26) + +// CMPI +case 0x0CB8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(24) + +// CMPI +case 0x0CB9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(28) + +// CMPI +case 0x0C9F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) + +// CMPI +case 0x0CA7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(22) +case 0x0801: +case 0x0802: +case 0x0803: +case 0x0804: +case 0x0805: +case 0x0806: +case 0x0807: + +// BTSTn +case 0x0800: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; +} +RET(10) +case 0x0811: +case 0x0812: +case 0x0813: +case 0x0814: +case 0x0815: +case 0x0816: +case 0x0817: + +// BTSTn +case 0x0810: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) +case 0x0819: +case 0x081A: +case 0x081B: +case 0x081C: +case 0x081D: +case 0x081E: + +// BTSTn +case 0x0818: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) +case 0x0821: +case 0x0822: +case 0x0823: +case 0x0824: +case 0x0825: +case 0x0826: + +// BTSTn +case 0x0820: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(14) +case 0x0829: +case 0x082A: +case 0x082B: +case 0x082C: +case 0x082D: +case 0x082E: +case 0x082F: + +// BTSTn +case 0x0828: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(16) +case 0x0831: +case 0x0832: +case 0x0833: +case 0x0834: +case 0x0835: +case 0x0836: +case 0x0837: + +// BTSTn +case 0x0830: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(18) + +// BTSTn +case 0x0838: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(16) + +// BTSTn +case 0x0839: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(20) + +// BTSTn +case 0x083A: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(16) + +// BTSTn +case 0x083B: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(18) + +// BTSTn +case 0x081F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) + +// BTSTn +case 0x0827: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(14) +case 0x0841: +case 0x0842: +case 0x0843: +case 0x0844: +case 0x0845: +case 0x0846: +case 0x0847: + +// BCHGn +case 0x0840: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res ^= src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(12) +case 0x0851: +case 0x0852: +case 0x0853: +case 0x0854: +case 0x0855: +case 0x0856: +case 0x0857: + +// BCHGn +case 0x0850: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0859: +case 0x085A: +case 0x085B: +case 0x085C: +case 0x085D: +case 0x085E: + +// BCHGn +case 0x0858: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0861: +case 0x0862: +case 0x0863: +case 0x0864: +case 0x0865: +case 0x0866: + +// BCHGn +case 0x0860: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0869: +case 0x086A: +case 0x086B: +case 0x086C: +case 0x086D: +case 0x086E: +case 0x086F: + +// BCHGn +case 0x0868: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x0871: +case 0x0872: +case 0x0873: +case 0x0874: +case 0x0875: +case 0x0876: +case 0x0877: + +// BCHGn +case 0x0870: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// BCHGn +case 0x0878: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// BCHGn +case 0x0879: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// BCHGn +case 0x085F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// BCHGn +case 0x0867: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0881: +case 0x0882: +case 0x0883: +case 0x0884: +case 0x0885: +case 0x0886: +case 0x0887: + +// BCLRn +case 0x0880: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res &= ~src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(14) +case 0x0891: +case 0x0892: +case 0x0893: +case 0x0894: +case 0x0895: +case 0x0896: +case 0x0897: + +// BCLRn +case 0x0890: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0899: +case 0x089A: +case 0x089B: +case 0x089C: +case 0x089D: +case 0x089E: + +// BCLRn +case 0x0898: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x08A1: +case 0x08A2: +case 0x08A3: +case 0x08A4: +case 0x08A5: +case 0x08A6: + +// BCLRn +case 0x08A0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x08A9: +case 0x08AA: +case 0x08AB: +case 0x08AC: +case 0x08AD: +case 0x08AE: +case 0x08AF: + +// BCLRn +case 0x08A8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x08B1: +case 0x08B2: +case 0x08B3: +case 0x08B4: +case 0x08B5: +case 0x08B6: +case 0x08B7: + +// BCLRn +case 0x08B0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// BCLRn +case 0x08B8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// BCLRn +case 0x08B9: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// BCLRn +case 0x089F: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// BCLRn +case 0x08A7: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x08C1: +case 0x08C2: +case 0x08C3: +case 0x08C4: +case 0x08C5: +case 0x08C6: +case 0x08C7: + +// BSETn +case 0x08C0: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res |= src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(12) +case 0x08D1: +case 0x08D2: +case 0x08D3: +case 0x08D4: +case 0x08D5: +case 0x08D6: +case 0x08D7: + +// BSETn +case 0x08D0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x08D9: +case 0x08DA: +case 0x08DB: +case 0x08DC: +case 0x08DD: +case 0x08DE: + +// BSETn +case 0x08D8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x08E1: +case 0x08E2: +case 0x08E3: +case 0x08E4: +case 0x08E5: +case 0x08E6: + +// BSETn +case 0x08E0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x08E9: +case 0x08EA: +case 0x08EB: +case 0x08EC: +case 0x08ED: +case 0x08EE: +case 0x08EF: + +// BSETn +case 0x08E8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x08F1: +case 0x08F2: +case 0x08F3: +case 0x08F4: +case 0x08F5: +case 0x08F6: +case 0x08F7: + +// BSETn +case 0x08F0: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// BSETn +case 0x08F8: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// BSETn +case 0x08F9: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// BSETn +case 0x08DF: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// BSETn +case 0x08E7: +{ + u32 adr; + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0300: +case 0x0500: +case 0x0700: +case 0x0900: +case 0x0B00: +case 0x0D00: +case 0x0F00: +case 0x0101: +case 0x0301: +case 0x0501: +case 0x0701: +case 0x0901: +case 0x0B01: +case 0x0D01: +case 0x0F01: +case 0x0102: +case 0x0302: +case 0x0502: +case 0x0702: +case 0x0902: +case 0x0B02: +case 0x0D02: +case 0x0F02: +case 0x0103: +case 0x0303: +case 0x0503: +case 0x0703: +case 0x0903: +case 0x0B03: +case 0x0D03: +case 0x0F03: +case 0x0104: +case 0x0304: +case 0x0504: +case 0x0704: +case 0x0904: +case 0x0B04: +case 0x0D04: +case 0x0F04: +case 0x0105: +case 0x0305: +case 0x0505: +case 0x0705: +case 0x0905: +case 0x0B05: +case 0x0D05: +case 0x0F05: +case 0x0106: +case 0x0306: +case 0x0506: +case 0x0706: +case 0x0906: +case 0x0B06: +case 0x0D06: +case 0x0F06: +case 0x0107: +case 0x0307: +case 0x0507: +case 0x0707: +case 0x0907: +case 0x0B07: +case 0x0D07: +case 0x0F07: + +// BTST +case 0x0100: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; +} +RET(6) +case 0x0310: +case 0x0510: +case 0x0710: +case 0x0910: +case 0x0B10: +case 0x0D10: +case 0x0F10: +case 0x0111: +case 0x0311: +case 0x0511: +case 0x0711: +case 0x0911: +case 0x0B11: +case 0x0D11: +case 0x0F11: +case 0x0112: +case 0x0312: +case 0x0512: +case 0x0712: +case 0x0912: +case 0x0B12: +case 0x0D12: +case 0x0F12: +case 0x0113: +case 0x0313: +case 0x0513: +case 0x0713: +case 0x0913: +case 0x0B13: +case 0x0D13: +case 0x0F13: +case 0x0114: +case 0x0314: +case 0x0514: +case 0x0714: +case 0x0914: +case 0x0B14: +case 0x0D14: +case 0x0F14: +case 0x0115: +case 0x0315: +case 0x0515: +case 0x0715: +case 0x0915: +case 0x0B15: +case 0x0D15: +case 0x0F15: +case 0x0116: +case 0x0316: +case 0x0516: +case 0x0716: +case 0x0916: +case 0x0B16: +case 0x0D16: +case 0x0F16: +case 0x0117: +case 0x0317: +case 0x0517: +case 0x0717: +case 0x0917: +case 0x0B17: +case 0x0D17: +case 0x0F17: + +// BTST +case 0x0110: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(8) +case 0x0318: +case 0x0518: +case 0x0718: +case 0x0918: +case 0x0B18: +case 0x0D18: +case 0x0F18: +case 0x0119: +case 0x0319: +case 0x0519: +case 0x0719: +case 0x0919: +case 0x0B19: +case 0x0D19: +case 0x0F19: +case 0x011A: +case 0x031A: +case 0x051A: +case 0x071A: +case 0x091A: +case 0x0B1A: +case 0x0D1A: +case 0x0F1A: +case 0x011B: +case 0x031B: +case 0x051B: +case 0x071B: +case 0x091B: +case 0x0B1B: +case 0x0D1B: +case 0x0F1B: +case 0x011C: +case 0x031C: +case 0x051C: +case 0x071C: +case 0x091C: +case 0x0B1C: +case 0x0D1C: +case 0x0F1C: +case 0x011D: +case 0x031D: +case 0x051D: +case 0x071D: +case 0x091D: +case 0x0B1D: +case 0x0D1D: +case 0x0F1D: +case 0x011E: +case 0x031E: +case 0x051E: +case 0x071E: +case 0x091E: +case 0x0B1E: +case 0x0D1E: +case 0x0F1E: + +// BTST +case 0x0118: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(8) +case 0x0320: +case 0x0520: +case 0x0720: +case 0x0920: +case 0x0B20: +case 0x0D20: +case 0x0F20: +case 0x0121: +case 0x0321: +case 0x0521: +case 0x0721: +case 0x0921: +case 0x0B21: +case 0x0D21: +case 0x0F21: +case 0x0122: +case 0x0322: +case 0x0522: +case 0x0722: +case 0x0922: +case 0x0B22: +case 0x0D22: +case 0x0F22: +case 0x0123: +case 0x0323: +case 0x0523: +case 0x0723: +case 0x0923: +case 0x0B23: +case 0x0D23: +case 0x0F23: +case 0x0124: +case 0x0324: +case 0x0524: +case 0x0724: +case 0x0924: +case 0x0B24: +case 0x0D24: +case 0x0F24: +case 0x0125: +case 0x0325: +case 0x0525: +case 0x0725: +case 0x0925: +case 0x0B25: +case 0x0D25: +case 0x0F25: +case 0x0126: +case 0x0326: +case 0x0526: +case 0x0726: +case 0x0926: +case 0x0B26: +case 0x0D26: +case 0x0F26: + +// BTST +case 0x0120: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(10) +case 0x0328: +case 0x0528: +case 0x0728: +case 0x0928: +case 0x0B28: +case 0x0D28: +case 0x0F28: +case 0x0129: +case 0x0329: +case 0x0529: +case 0x0729: +case 0x0929: +case 0x0B29: +case 0x0D29: +case 0x0F29: +case 0x012A: +case 0x032A: +case 0x052A: +case 0x072A: +case 0x092A: +case 0x0B2A: +case 0x0D2A: +case 0x0F2A: +case 0x012B: +case 0x032B: +case 0x052B: +case 0x072B: +case 0x092B: +case 0x0B2B: +case 0x0D2B: +case 0x0F2B: +case 0x012C: +case 0x032C: +case 0x052C: +case 0x072C: +case 0x092C: +case 0x0B2C: +case 0x0D2C: +case 0x0F2C: +case 0x012D: +case 0x032D: +case 0x052D: +case 0x072D: +case 0x092D: +case 0x0B2D: +case 0x0D2D: +case 0x0F2D: +case 0x012E: +case 0x032E: +case 0x052E: +case 0x072E: +case 0x092E: +case 0x0B2E: +case 0x0D2E: +case 0x0F2E: +case 0x012F: +case 0x032F: +case 0x052F: +case 0x072F: +case 0x092F: +case 0x0B2F: +case 0x0D2F: +case 0x0F2F: + +// BTST +case 0x0128: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) +case 0x0330: +case 0x0530: +case 0x0730: +case 0x0930: +case 0x0B30: +case 0x0D30: +case 0x0F30: +case 0x0131: +case 0x0331: +case 0x0531: +case 0x0731: +case 0x0931: +case 0x0B31: +case 0x0D31: +case 0x0F31: +case 0x0132: +case 0x0332: +case 0x0532: +case 0x0732: +case 0x0932: +case 0x0B32: +case 0x0D32: +case 0x0F32: +case 0x0133: +case 0x0333: +case 0x0533: +case 0x0733: +case 0x0933: +case 0x0B33: +case 0x0D33: +case 0x0F33: +case 0x0134: +case 0x0334: +case 0x0534: +case 0x0734: +case 0x0934: +case 0x0B34: +case 0x0D34: +case 0x0F34: +case 0x0135: +case 0x0335: +case 0x0535: +case 0x0735: +case 0x0935: +case 0x0B35: +case 0x0D35: +case 0x0F35: +case 0x0136: +case 0x0336: +case 0x0536: +case 0x0736: +case 0x0936: +case 0x0B36: +case 0x0D36: +case 0x0F36: +case 0x0137: +case 0x0337: +case 0x0537: +case 0x0737: +case 0x0937: +case 0x0B37: +case 0x0D37: +case 0x0F37: + +// BTST +case 0x0130: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(14) +case 0x0338: +case 0x0538: +case 0x0738: +case 0x0938: +case 0x0B38: +case 0x0D38: +case 0x0F38: + +// BTST +case 0x0138: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) +case 0x0339: +case 0x0539: +case 0x0739: +case 0x0939: +case 0x0B39: +case 0x0D39: +case 0x0F39: + +// BTST +case 0x0139: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(16) +case 0x033A: +case 0x053A: +case 0x073A: +case 0x093A: +case 0x0B3A: +case 0x0D3A: +case 0x0F3A: + +// BTST +case 0x013A: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(12) +case 0x033B: +case 0x053B: +case 0x073B: +case 0x093B: +case 0x0B3B: +case 0x0D3B: +case 0x0F3B: + +// BTST +case 0x013B: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(14) +case 0x033C: +case 0x053C: +case 0x073C: +case 0x093C: +case 0x0B3C: +case 0x0D3C: +case 0x0F3C: + +// BTST +case 0x013C: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + res = FETCH_BYTE; + PC += 2; + CPU->flag_notZ = res & src; +} +RET(8) +case 0x031F: +case 0x051F: +case 0x071F: +case 0x091F: +case 0x0B1F: +case 0x0D1F: +case 0x0F1F: + +// BTST +case 0x011F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(8) +case 0x0327: +case 0x0527: +case 0x0727: +case 0x0927: +case 0x0B27: +case 0x0D27: +case 0x0F27: + +// BTST +case 0x0127: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + POST_IO +} +RET(10) +case 0x0340: +case 0x0540: +case 0x0740: +case 0x0940: +case 0x0B40: +case 0x0D40: +case 0x0F40: +case 0x0141: +case 0x0341: +case 0x0541: +case 0x0741: +case 0x0941: +case 0x0B41: +case 0x0D41: +case 0x0F41: +case 0x0142: +case 0x0342: +case 0x0542: +case 0x0742: +case 0x0942: +case 0x0B42: +case 0x0D42: +case 0x0F42: +case 0x0143: +case 0x0343: +case 0x0543: +case 0x0743: +case 0x0943: +case 0x0B43: +case 0x0D43: +case 0x0F43: +case 0x0144: +case 0x0344: +case 0x0544: +case 0x0744: +case 0x0944: +case 0x0B44: +case 0x0D44: +case 0x0F44: +case 0x0145: +case 0x0345: +case 0x0545: +case 0x0745: +case 0x0945: +case 0x0B45: +case 0x0D45: +case 0x0F45: +case 0x0146: +case 0x0346: +case 0x0546: +case 0x0746: +case 0x0946: +case 0x0B46: +case 0x0D46: +case 0x0F46: +case 0x0147: +case 0x0347: +case 0x0547: +case 0x0747: +case 0x0947: +case 0x0B47: +case 0x0D47: +case 0x0F47: + +// BCHG +case 0x0140: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res ^= src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x0350: +case 0x0550: +case 0x0750: +case 0x0950: +case 0x0B50: +case 0x0D50: +case 0x0F50: +case 0x0151: +case 0x0351: +case 0x0551: +case 0x0751: +case 0x0951: +case 0x0B51: +case 0x0D51: +case 0x0F51: +case 0x0152: +case 0x0352: +case 0x0552: +case 0x0752: +case 0x0952: +case 0x0B52: +case 0x0D52: +case 0x0F52: +case 0x0153: +case 0x0353: +case 0x0553: +case 0x0753: +case 0x0953: +case 0x0B53: +case 0x0D53: +case 0x0F53: +case 0x0154: +case 0x0354: +case 0x0554: +case 0x0754: +case 0x0954: +case 0x0B54: +case 0x0D54: +case 0x0F54: +case 0x0155: +case 0x0355: +case 0x0555: +case 0x0755: +case 0x0955: +case 0x0B55: +case 0x0D55: +case 0x0F55: +case 0x0156: +case 0x0356: +case 0x0556: +case 0x0756: +case 0x0956: +case 0x0B56: +case 0x0D56: +case 0x0F56: +case 0x0157: +case 0x0357: +case 0x0557: +case 0x0757: +case 0x0957: +case 0x0B57: +case 0x0D57: +case 0x0F57: + +// BCHG +case 0x0150: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x0358: +case 0x0558: +case 0x0758: +case 0x0958: +case 0x0B58: +case 0x0D58: +case 0x0F58: +case 0x0159: +case 0x0359: +case 0x0559: +case 0x0759: +case 0x0959: +case 0x0B59: +case 0x0D59: +case 0x0F59: +case 0x015A: +case 0x035A: +case 0x055A: +case 0x075A: +case 0x095A: +case 0x0B5A: +case 0x0D5A: +case 0x0F5A: +case 0x015B: +case 0x035B: +case 0x055B: +case 0x075B: +case 0x095B: +case 0x0B5B: +case 0x0D5B: +case 0x0F5B: +case 0x015C: +case 0x035C: +case 0x055C: +case 0x075C: +case 0x095C: +case 0x0B5C: +case 0x0D5C: +case 0x0F5C: +case 0x015D: +case 0x035D: +case 0x055D: +case 0x075D: +case 0x095D: +case 0x0B5D: +case 0x0D5D: +case 0x0F5D: +case 0x015E: +case 0x035E: +case 0x055E: +case 0x075E: +case 0x095E: +case 0x0B5E: +case 0x0D5E: +case 0x0F5E: + +// BCHG +case 0x0158: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x0360: +case 0x0560: +case 0x0760: +case 0x0960: +case 0x0B60: +case 0x0D60: +case 0x0F60: +case 0x0161: +case 0x0361: +case 0x0561: +case 0x0761: +case 0x0961: +case 0x0B61: +case 0x0D61: +case 0x0F61: +case 0x0162: +case 0x0362: +case 0x0562: +case 0x0762: +case 0x0962: +case 0x0B62: +case 0x0D62: +case 0x0F62: +case 0x0163: +case 0x0363: +case 0x0563: +case 0x0763: +case 0x0963: +case 0x0B63: +case 0x0D63: +case 0x0F63: +case 0x0164: +case 0x0364: +case 0x0564: +case 0x0764: +case 0x0964: +case 0x0B64: +case 0x0D64: +case 0x0F64: +case 0x0165: +case 0x0365: +case 0x0565: +case 0x0765: +case 0x0965: +case 0x0B65: +case 0x0D65: +case 0x0F65: +case 0x0166: +case 0x0366: +case 0x0566: +case 0x0766: +case 0x0966: +case 0x0B66: +case 0x0D66: +case 0x0F66: + +// BCHG +case 0x0160: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x0368: +case 0x0568: +case 0x0768: +case 0x0968: +case 0x0B68: +case 0x0D68: +case 0x0F68: +case 0x0169: +case 0x0369: +case 0x0569: +case 0x0769: +case 0x0969: +case 0x0B69: +case 0x0D69: +case 0x0F69: +case 0x016A: +case 0x036A: +case 0x056A: +case 0x076A: +case 0x096A: +case 0x0B6A: +case 0x0D6A: +case 0x0F6A: +case 0x016B: +case 0x036B: +case 0x056B: +case 0x076B: +case 0x096B: +case 0x0B6B: +case 0x0D6B: +case 0x0F6B: +case 0x016C: +case 0x036C: +case 0x056C: +case 0x076C: +case 0x096C: +case 0x0B6C: +case 0x0D6C: +case 0x0F6C: +case 0x016D: +case 0x036D: +case 0x056D: +case 0x076D: +case 0x096D: +case 0x0B6D: +case 0x0D6D: +case 0x0F6D: +case 0x016E: +case 0x036E: +case 0x056E: +case 0x076E: +case 0x096E: +case 0x0B6E: +case 0x0D6E: +case 0x0F6E: +case 0x016F: +case 0x036F: +case 0x056F: +case 0x076F: +case 0x096F: +case 0x0B6F: +case 0x0D6F: +case 0x0F6F: + +// BCHG +case 0x0168: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0370: +case 0x0570: +case 0x0770: +case 0x0970: +case 0x0B70: +case 0x0D70: +case 0x0F70: +case 0x0171: +case 0x0371: +case 0x0571: +case 0x0771: +case 0x0971: +case 0x0B71: +case 0x0D71: +case 0x0F71: +case 0x0172: +case 0x0372: +case 0x0572: +case 0x0772: +case 0x0972: +case 0x0B72: +case 0x0D72: +case 0x0F72: +case 0x0173: +case 0x0373: +case 0x0573: +case 0x0773: +case 0x0973: +case 0x0B73: +case 0x0D73: +case 0x0F73: +case 0x0174: +case 0x0374: +case 0x0574: +case 0x0774: +case 0x0974: +case 0x0B74: +case 0x0D74: +case 0x0F74: +case 0x0175: +case 0x0375: +case 0x0575: +case 0x0775: +case 0x0975: +case 0x0B75: +case 0x0D75: +case 0x0F75: +case 0x0176: +case 0x0376: +case 0x0576: +case 0x0776: +case 0x0976: +case 0x0B76: +case 0x0D76: +case 0x0F76: +case 0x0177: +case 0x0377: +case 0x0577: +case 0x0777: +case 0x0977: +case 0x0B77: +case 0x0D77: +case 0x0F77: + +// BCHG +case 0x0170: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x0378: +case 0x0578: +case 0x0778: +case 0x0978: +case 0x0B78: +case 0x0D78: +case 0x0F78: + +// BCHG +case 0x0178: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x0379: +case 0x0579: +case 0x0779: +case 0x0979: +case 0x0B79: +case 0x0D79: +case 0x0F79: + +// BCHG +case 0x0179: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x035F: +case 0x055F: +case 0x075F: +case 0x095F: +case 0x0B5F: +case 0x0D5F: +case 0x0F5F: + +// BCHG +case 0x015F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x0367: +case 0x0567: +case 0x0767: +case 0x0967: +case 0x0B67: +case 0x0D67: +case 0x0F67: + +// BCHG +case 0x0167: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res ^= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x0380: +case 0x0580: +case 0x0780: +case 0x0980: +case 0x0B80: +case 0x0D80: +case 0x0F80: +case 0x0181: +case 0x0381: +case 0x0581: +case 0x0781: +case 0x0981: +case 0x0B81: +case 0x0D81: +case 0x0F81: +case 0x0182: +case 0x0382: +case 0x0582: +case 0x0782: +case 0x0982: +case 0x0B82: +case 0x0D82: +case 0x0F82: +case 0x0183: +case 0x0383: +case 0x0583: +case 0x0783: +case 0x0983: +case 0x0B83: +case 0x0D83: +case 0x0F83: +case 0x0184: +case 0x0384: +case 0x0584: +case 0x0784: +case 0x0984: +case 0x0B84: +case 0x0D84: +case 0x0F84: +case 0x0185: +case 0x0385: +case 0x0585: +case 0x0785: +case 0x0985: +case 0x0B85: +case 0x0D85: +case 0x0F85: +case 0x0186: +case 0x0386: +case 0x0586: +case 0x0786: +case 0x0986: +case 0x0B86: +case 0x0D86: +case 0x0F86: +case 0x0187: +case 0x0387: +case 0x0587: +case 0x0787: +case 0x0987: +case 0x0B87: +case 0x0D87: +case 0x0F87: + +// BCLR +case 0x0180: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res &= ~src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(10) +case 0x0390: +case 0x0590: +case 0x0790: +case 0x0990: +case 0x0B90: +case 0x0D90: +case 0x0F90: +case 0x0191: +case 0x0391: +case 0x0591: +case 0x0791: +case 0x0991: +case 0x0B91: +case 0x0D91: +case 0x0F91: +case 0x0192: +case 0x0392: +case 0x0592: +case 0x0792: +case 0x0992: +case 0x0B92: +case 0x0D92: +case 0x0F92: +case 0x0193: +case 0x0393: +case 0x0593: +case 0x0793: +case 0x0993: +case 0x0B93: +case 0x0D93: +case 0x0F93: +case 0x0194: +case 0x0394: +case 0x0594: +case 0x0794: +case 0x0994: +case 0x0B94: +case 0x0D94: +case 0x0F94: +case 0x0195: +case 0x0395: +case 0x0595: +case 0x0795: +case 0x0995: +case 0x0B95: +case 0x0D95: +case 0x0F95: +case 0x0196: +case 0x0396: +case 0x0596: +case 0x0796: +case 0x0996: +case 0x0B96: +case 0x0D96: +case 0x0F96: +case 0x0197: +case 0x0397: +case 0x0597: +case 0x0797: +case 0x0997: +case 0x0B97: +case 0x0D97: +case 0x0F97: + +// BCLR +case 0x0190: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x0398: +case 0x0598: +case 0x0798: +case 0x0998: +case 0x0B98: +case 0x0D98: +case 0x0F98: +case 0x0199: +case 0x0399: +case 0x0599: +case 0x0799: +case 0x0999: +case 0x0B99: +case 0x0D99: +case 0x0F99: +case 0x019A: +case 0x039A: +case 0x059A: +case 0x079A: +case 0x099A: +case 0x0B9A: +case 0x0D9A: +case 0x0F9A: +case 0x019B: +case 0x039B: +case 0x059B: +case 0x079B: +case 0x099B: +case 0x0B9B: +case 0x0D9B: +case 0x0F9B: +case 0x019C: +case 0x039C: +case 0x059C: +case 0x079C: +case 0x099C: +case 0x0B9C: +case 0x0D9C: +case 0x0F9C: +case 0x019D: +case 0x039D: +case 0x059D: +case 0x079D: +case 0x099D: +case 0x0B9D: +case 0x0D9D: +case 0x0F9D: +case 0x019E: +case 0x039E: +case 0x059E: +case 0x079E: +case 0x099E: +case 0x0B9E: +case 0x0D9E: +case 0x0F9E: + +// BCLR +case 0x0198: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x03A0: +case 0x05A0: +case 0x07A0: +case 0x09A0: +case 0x0BA0: +case 0x0DA0: +case 0x0FA0: +case 0x01A1: +case 0x03A1: +case 0x05A1: +case 0x07A1: +case 0x09A1: +case 0x0BA1: +case 0x0DA1: +case 0x0FA1: +case 0x01A2: +case 0x03A2: +case 0x05A2: +case 0x07A2: +case 0x09A2: +case 0x0BA2: +case 0x0DA2: +case 0x0FA2: +case 0x01A3: +case 0x03A3: +case 0x05A3: +case 0x07A3: +case 0x09A3: +case 0x0BA3: +case 0x0DA3: +case 0x0FA3: +case 0x01A4: +case 0x03A4: +case 0x05A4: +case 0x07A4: +case 0x09A4: +case 0x0BA4: +case 0x0DA4: +case 0x0FA4: +case 0x01A5: +case 0x03A5: +case 0x05A5: +case 0x07A5: +case 0x09A5: +case 0x0BA5: +case 0x0DA5: +case 0x0FA5: +case 0x01A6: +case 0x03A6: +case 0x05A6: +case 0x07A6: +case 0x09A6: +case 0x0BA6: +case 0x0DA6: +case 0x0FA6: + +// BCLR +case 0x01A0: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x03A8: +case 0x05A8: +case 0x07A8: +case 0x09A8: +case 0x0BA8: +case 0x0DA8: +case 0x0FA8: +case 0x01A9: +case 0x03A9: +case 0x05A9: +case 0x07A9: +case 0x09A9: +case 0x0BA9: +case 0x0DA9: +case 0x0FA9: +case 0x01AA: +case 0x03AA: +case 0x05AA: +case 0x07AA: +case 0x09AA: +case 0x0BAA: +case 0x0DAA: +case 0x0FAA: +case 0x01AB: +case 0x03AB: +case 0x05AB: +case 0x07AB: +case 0x09AB: +case 0x0BAB: +case 0x0DAB: +case 0x0FAB: +case 0x01AC: +case 0x03AC: +case 0x05AC: +case 0x07AC: +case 0x09AC: +case 0x0BAC: +case 0x0DAC: +case 0x0FAC: +case 0x01AD: +case 0x03AD: +case 0x05AD: +case 0x07AD: +case 0x09AD: +case 0x0BAD: +case 0x0DAD: +case 0x0FAD: +case 0x01AE: +case 0x03AE: +case 0x05AE: +case 0x07AE: +case 0x09AE: +case 0x0BAE: +case 0x0DAE: +case 0x0FAE: +case 0x01AF: +case 0x03AF: +case 0x05AF: +case 0x07AF: +case 0x09AF: +case 0x0BAF: +case 0x0DAF: +case 0x0FAF: + +// BCLR +case 0x01A8: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x03B0: +case 0x05B0: +case 0x07B0: +case 0x09B0: +case 0x0BB0: +case 0x0DB0: +case 0x0FB0: +case 0x01B1: +case 0x03B1: +case 0x05B1: +case 0x07B1: +case 0x09B1: +case 0x0BB1: +case 0x0DB1: +case 0x0FB1: +case 0x01B2: +case 0x03B2: +case 0x05B2: +case 0x07B2: +case 0x09B2: +case 0x0BB2: +case 0x0DB2: +case 0x0FB2: +case 0x01B3: +case 0x03B3: +case 0x05B3: +case 0x07B3: +case 0x09B3: +case 0x0BB3: +case 0x0DB3: +case 0x0FB3: +case 0x01B4: +case 0x03B4: +case 0x05B4: +case 0x07B4: +case 0x09B4: +case 0x0BB4: +case 0x0DB4: +case 0x0FB4: +case 0x01B5: +case 0x03B5: +case 0x05B5: +case 0x07B5: +case 0x09B5: +case 0x0BB5: +case 0x0DB5: +case 0x0FB5: +case 0x01B6: +case 0x03B6: +case 0x05B6: +case 0x07B6: +case 0x09B6: +case 0x0BB6: +case 0x0DB6: +case 0x0FB6: +case 0x01B7: +case 0x03B7: +case 0x05B7: +case 0x07B7: +case 0x09B7: +case 0x0BB7: +case 0x0DB7: +case 0x0FB7: + +// BCLR +case 0x01B0: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x03B8: +case 0x05B8: +case 0x07B8: +case 0x09B8: +case 0x0BB8: +case 0x0DB8: +case 0x0FB8: + +// BCLR +case 0x01B8: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x03B9: +case 0x05B9: +case 0x07B9: +case 0x09B9: +case 0x0BB9: +case 0x0DB9: +case 0x0FB9: + +// BCLR +case 0x01B9: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x039F: +case 0x059F: +case 0x079F: +case 0x099F: +case 0x0B9F: +case 0x0D9F: +case 0x0F9F: + +// BCLR +case 0x019F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x03A7: +case 0x05A7: +case 0x07A7: +case 0x09A7: +case 0x0BA7: +case 0x0DA7: +case 0x0FA7: + +// BCLR +case 0x01A7: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res &= ~src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x03C0: +case 0x05C0: +case 0x07C0: +case 0x09C0: +case 0x0BC0: +case 0x0DC0: +case 0x0FC0: +case 0x01C1: +case 0x03C1: +case 0x05C1: +case 0x07C1: +case 0x09C1: +case 0x0BC1: +case 0x0DC1: +case 0x0FC1: +case 0x01C2: +case 0x03C2: +case 0x05C2: +case 0x07C2: +case 0x09C2: +case 0x0BC2: +case 0x0DC2: +case 0x0FC2: +case 0x01C3: +case 0x03C3: +case 0x05C3: +case 0x07C3: +case 0x09C3: +case 0x0BC3: +case 0x0DC3: +case 0x0FC3: +case 0x01C4: +case 0x03C4: +case 0x05C4: +case 0x07C4: +case 0x09C4: +case 0x0BC4: +case 0x0DC4: +case 0x0FC4: +case 0x01C5: +case 0x03C5: +case 0x05C5: +case 0x07C5: +case 0x09C5: +case 0x0BC5: +case 0x0DC5: +case 0x0FC5: +case 0x01C6: +case 0x03C6: +case 0x05C6: +case 0x07C6: +case 0x09C6: +case 0x0BC6: +case 0x0DC6: +case 0x0FC6: +case 0x01C7: +case 0x03C7: +case 0x05C7: +case 0x07C7: +case 0x09C7: +case 0x0BC7: +case 0x0DC7: +case 0x0FC7: + +// BSET +case 0x01C0: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 31); + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_notZ = res & src; + res |= src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x03D0: +case 0x05D0: +case 0x07D0: +case 0x09D0: +case 0x0BD0: +case 0x0DD0: +case 0x0FD0: +case 0x01D1: +case 0x03D1: +case 0x05D1: +case 0x07D1: +case 0x09D1: +case 0x0BD1: +case 0x0DD1: +case 0x0FD1: +case 0x01D2: +case 0x03D2: +case 0x05D2: +case 0x07D2: +case 0x09D2: +case 0x0BD2: +case 0x0DD2: +case 0x0FD2: +case 0x01D3: +case 0x03D3: +case 0x05D3: +case 0x07D3: +case 0x09D3: +case 0x0BD3: +case 0x0DD3: +case 0x0FD3: +case 0x01D4: +case 0x03D4: +case 0x05D4: +case 0x07D4: +case 0x09D4: +case 0x0BD4: +case 0x0DD4: +case 0x0FD4: +case 0x01D5: +case 0x03D5: +case 0x05D5: +case 0x07D5: +case 0x09D5: +case 0x0BD5: +case 0x0DD5: +case 0x0FD5: +case 0x01D6: +case 0x03D6: +case 0x05D6: +case 0x07D6: +case 0x09D6: +case 0x0BD6: +case 0x0DD6: +case 0x0FD6: +case 0x01D7: +case 0x03D7: +case 0x05D7: +case 0x07D7: +case 0x09D7: +case 0x0BD7: +case 0x0DD7: +case 0x0FD7: + +// BSET +case 0x01D0: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x03D8: +case 0x05D8: +case 0x07D8: +case 0x09D8: +case 0x0BD8: +case 0x0DD8: +case 0x0FD8: +case 0x01D9: +case 0x03D9: +case 0x05D9: +case 0x07D9: +case 0x09D9: +case 0x0BD9: +case 0x0DD9: +case 0x0FD9: +case 0x01DA: +case 0x03DA: +case 0x05DA: +case 0x07DA: +case 0x09DA: +case 0x0BDA: +case 0x0DDA: +case 0x0FDA: +case 0x01DB: +case 0x03DB: +case 0x05DB: +case 0x07DB: +case 0x09DB: +case 0x0BDB: +case 0x0DDB: +case 0x0FDB: +case 0x01DC: +case 0x03DC: +case 0x05DC: +case 0x07DC: +case 0x09DC: +case 0x0BDC: +case 0x0DDC: +case 0x0FDC: +case 0x01DD: +case 0x03DD: +case 0x05DD: +case 0x07DD: +case 0x09DD: +case 0x0BDD: +case 0x0DDD: +case 0x0FDD: +case 0x01DE: +case 0x03DE: +case 0x05DE: +case 0x07DE: +case 0x09DE: +case 0x0BDE: +case 0x0DDE: +case 0x0FDE: + +// BSET +case 0x01D8: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x03E0: +case 0x05E0: +case 0x07E0: +case 0x09E0: +case 0x0BE0: +case 0x0DE0: +case 0x0FE0: +case 0x01E1: +case 0x03E1: +case 0x05E1: +case 0x07E1: +case 0x09E1: +case 0x0BE1: +case 0x0DE1: +case 0x0FE1: +case 0x01E2: +case 0x03E2: +case 0x05E2: +case 0x07E2: +case 0x09E2: +case 0x0BE2: +case 0x0DE2: +case 0x0FE2: +case 0x01E3: +case 0x03E3: +case 0x05E3: +case 0x07E3: +case 0x09E3: +case 0x0BE3: +case 0x0DE3: +case 0x0FE3: +case 0x01E4: +case 0x03E4: +case 0x05E4: +case 0x07E4: +case 0x09E4: +case 0x0BE4: +case 0x0DE4: +case 0x0FE4: +case 0x01E5: +case 0x03E5: +case 0x05E5: +case 0x07E5: +case 0x09E5: +case 0x0BE5: +case 0x0DE5: +case 0x0FE5: +case 0x01E6: +case 0x03E6: +case 0x05E6: +case 0x07E6: +case 0x09E6: +case 0x0BE6: +case 0x0DE6: +case 0x0FE6: + +// BSET +case 0x01E0: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x03E8: +case 0x05E8: +case 0x07E8: +case 0x09E8: +case 0x0BE8: +case 0x0DE8: +case 0x0FE8: +case 0x01E9: +case 0x03E9: +case 0x05E9: +case 0x07E9: +case 0x09E9: +case 0x0BE9: +case 0x0DE9: +case 0x0FE9: +case 0x01EA: +case 0x03EA: +case 0x05EA: +case 0x07EA: +case 0x09EA: +case 0x0BEA: +case 0x0DEA: +case 0x0FEA: +case 0x01EB: +case 0x03EB: +case 0x05EB: +case 0x07EB: +case 0x09EB: +case 0x0BEB: +case 0x0DEB: +case 0x0FEB: +case 0x01EC: +case 0x03EC: +case 0x05EC: +case 0x07EC: +case 0x09EC: +case 0x0BEC: +case 0x0DEC: +case 0x0FEC: +case 0x01ED: +case 0x03ED: +case 0x05ED: +case 0x07ED: +case 0x09ED: +case 0x0BED: +case 0x0DED: +case 0x0FED: +case 0x01EE: +case 0x03EE: +case 0x05EE: +case 0x07EE: +case 0x09EE: +case 0x0BEE: +case 0x0DEE: +case 0x0FEE: +case 0x01EF: +case 0x03EF: +case 0x05EF: +case 0x07EF: +case 0x09EF: +case 0x0BEF: +case 0x0DEF: +case 0x0FEF: + +// BSET +case 0x01E8: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x03F0: +case 0x05F0: +case 0x07F0: +case 0x09F0: +case 0x0BF0: +case 0x0DF0: +case 0x0FF0: +case 0x01F1: +case 0x03F1: +case 0x05F1: +case 0x07F1: +case 0x09F1: +case 0x0BF1: +case 0x0DF1: +case 0x0FF1: +case 0x01F2: +case 0x03F2: +case 0x05F2: +case 0x07F2: +case 0x09F2: +case 0x0BF2: +case 0x0DF2: +case 0x0FF2: +case 0x01F3: +case 0x03F3: +case 0x05F3: +case 0x07F3: +case 0x09F3: +case 0x0BF3: +case 0x0DF3: +case 0x0FF3: +case 0x01F4: +case 0x03F4: +case 0x05F4: +case 0x07F4: +case 0x09F4: +case 0x0BF4: +case 0x0DF4: +case 0x0FF4: +case 0x01F5: +case 0x03F5: +case 0x05F5: +case 0x07F5: +case 0x09F5: +case 0x0BF5: +case 0x0DF5: +case 0x0FF5: +case 0x01F6: +case 0x03F6: +case 0x05F6: +case 0x07F6: +case 0x09F6: +case 0x0BF6: +case 0x0DF6: +case 0x0FF6: +case 0x01F7: +case 0x03F7: +case 0x05F7: +case 0x07F7: +case 0x09F7: +case 0x0BF7: +case 0x0DF7: +case 0x0FF7: + +// BSET +case 0x01F0: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x03F8: +case 0x05F8: +case 0x07F8: +case 0x09F8: +case 0x0BF8: +case 0x0DF8: +case 0x0FF8: + +// BSET +case 0x01F8: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x03F9: +case 0x05F9: +case 0x07F9: +case 0x09F9: +case 0x0BF9: +case 0x0DF9: +case 0x0FF9: + +// BSET +case 0x01F9: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x03DF: +case 0x05DF: +case 0x07DF: +case 0x09DF: +case 0x0BDF: +case 0x0DDF: +case 0x0FDF: + +// BSET +case 0x01DF: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x03E7: +case 0x05E7: +case 0x07E7: +case 0x09E7: +case 0x0BE7: +case 0x0DE7: +case 0x0FE7: + +// BSET +case 0x01E7: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + src = 1 << (src & 7); + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_notZ = res & src; + res |= src; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x0308: +case 0x0508: +case 0x0708: +case 0x0908: +case 0x0B08: +case 0x0D08: +case 0x0F08: +case 0x0109: +case 0x0309: +case 0x0509: +case 0x0709: +case 0x0909: +case 0x0B09: +case 0x0D09: +case 0x0F09: +case 0x010A: +case 0x030A: +case 0x050A: +case 0x070A: +case 0x090A: +case 0x0B0A: +case 0x0D0A: +case 0x0F0A: +case 0x010B: +case 0x030B: +case 0x050B: +case 0x070B: +case 0x090B: +case 0x0B0B: +case 0x0D0B: +case 0x0F0B: +case 0x010C: +case 0x030C: +case 0x050C: +case 0x070C: +case 0x090C: +case 0x0B0C: +case 0x0D0C: +case 0x0F0C: +case 0x010D: +case 0x030D: +case 0x050D: +case 0x070D: +case 0x090D: +case 0x0B0D: +case 0x0D0D: +case 0x0F0D: +case 0x010E: +case 0x030E: +case 0x050E: +case 0x070E: +case 0x090E: +case 0x0B0E: +case 0x0D0E: +case 0x0F0E: +case 0x010F: +case 0x030F: +case 0x050F: +case 0x070F: +case 0x090F: +case 0x0B0F: +case 0x0D0F: +case 0x0F0F: + +// MOVEPWaD +case 0x0108: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr + 0, res) + READ_BYTE_F(adr + 2, src) + *(u16*)(&CPU->D[(Opcode >> 9) & 7]) = (res << 8) | src; + POST_IO +} +RET(24) +case 0x0348: +case 0x0548: +case 0x0748: +case 0x0948: +case 0x0B48: +case 0x0D48: +case 0x0F48: +case 0x0149: +case 0x0349: +case 0x0549: +case 0x0749: +case 0x0949: +case 0x0B49: +case 0x0D49: +case 0x0F49: +case 0x014A: +case 0x034A: +case 0x054A: +case 0x074A: +case 0x094A: +case 0x0B4A: +case 0x0D4A: +case 0x0F4A: +case 0x014B: +case 0x034B: +case 0x054B: +case 0x074B: +case 0x094B: +case 0x0B4B: +case 0x0D4B: +case 0x0F4B: +case 0x014C: +case 0x034C: +case 0x054C: +case 0x074C: +case 0x094C: +case 0x0B4C: +case 0x0D4C: +case 0x0F4C: +case 0x014D: +case 0x034D: +case 0x054D: +case 0x074D: +case 0x094D: +case 0x0B4D: +case 0x0D4D: +case 0x0F4D: +case 0x014E: +case 0x034E: +case 0x054E: +case 0x074E: +case 0x094E: +case 0x0B4E: +case 0x0D4E: +case 0x0F4E: +case 0x014F: +case 0x034F: +case 0x054F: +case 0x074F: +case 0x094F: +case 0x0B4F: +case 0x0D4F: +case 0x0F4F: + +// MOVEPLaD +case 0x0148: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res <<= 24; + adr += 2; + READ_BYTE_F(adr, src) + res |= src << 16; + adr += 2; + READ_BYTE_F(adr, src) + res |= src << 8; + adr += 2; + READ_BYTE_F(adr, src) + CPU->D[(Opcode >> 9) & 7] = res | src; + POST_IO +} +RET(32) +case 0x0388: +case 0x0588: +case 0x0788: +case 0x0988: +case 0x0B88: +case 0x0D88: +case 0x0F88: +case 0x0189: +case 0x0389: +case 0x0589: +case 0x0789: +case 0x0989: +case 0x0B89: +case 0x0D89: +case 0x0F89: +case 0x018A: +case 0x038A: +case 0x058A: +case 0x078A: +case 0x098A: +case 0x0B8A: +case 0x0D8A: +case 0x0F8A: +case 0x018B: +case 0x038B: +case 0x058B: +case 0x078B: +case 0x098B: +case 0x0B8B: +case 0x0D8B: +case 0x0F8B: +case 0x018C: +case 0x038C: +case 0x058C: +case 0x078C: +case 0x098C: +case 0x0B8C: +case 0x0D8C: +case 0x0F8C: +case 0x018D: +case 0x038D: +case 0x058D: +case 0x078D: +case 0x098D: +case 0x0B8D: +case 0x0D8D: +case 0x0F8D: +case 0x018E: +case 0x038E: +case 0x058E: +case 0x078E: +case 0x098E: +case 0x0B8E: +case 0x0D8E: +case 0x0F8E: +case 0x018F: +case 0x038F: +case 0x058F: +case 0x078F: +case 0x098F: +case 0x0B8F: +case 0x0D8F: +case 0x0F8F: + +// MOVEPWDa +case 0x0188: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr + 0, res >> 8) + WRITE_BYTE_F(adr + 2, res >> 0) + POST_IO +} +RET(24) +case 0x03C8: +case 0x05C8: +case 0x07C8: +case 0x09C8: +case 0x0BC8: +case 0x0DC8: +case 0x0FC8: +case 0x01C9: +case 0x03C9: +case 0x05C9: +case 0x07C9: +case 0x09C9: +case 0x0BC9: +case 0x0DC9: +case 0x0FC9: +case 0x01CA: +case 0x03CA: +case 0x05CA: +case 0x07CA: +case 0x09CA: +case 0x0BCA: +case 0x0DCA: +case 0x0FCA: +case 0x01CB: +case 0x03CB: +case 0x05CB: +case 0x07CB: +case 0x09CB: +case 0x0BCB: +case 0x0DCB: +case 0x0FCB: +case 0x01CC: +case 0x03CC: +case 0x05CC: +case 0x07CC: +case 0x09CC: +case 0x0BCC: +case 0x0DCC: +case 0x0FCC: +case 0x01CD: +case 0x03CD: +case 0x05CD: +case 0x07CD: +case 0x09CD: +case 0x0BCD: +case 0x0DCD: +case 0x0FCD: +case 0x01CE: +case 0x03CE: +case 0x05CE: +case 0x07CE: +case 0x09CE: +case 0x0BCE: +case 0x0DCE: +case 0x0FCE: +case 0x01CF: +case 0x03CF: +case 0x05CF: +case 0x07CF: +case 0x09CF: +case 0x0BCF: +case 0x0DCF: +case 0x0FCF: + +// MOVEPLDa +case 0x01C8: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res >> 24) + adr += 2; + WRITE_BYTE_F(adr, res >> 16) + adr += 2; + WRITE_BYTE_F(adr, res >> 8) + adr += 2; + WRITE_BYTE_F(adr, res >> 0) + POST_IO +} +RET(32) diff --git a/yabause/src/c68k/c68k_op1.inc b/yabause/src/c68k/c68k_op1.inc new file mode 100644 index 0000000000..93694ed9db --- /dev/null +++ b/yabause/src/c68k/c68k_op1.inc @@ -0,0 +1,5629 @@ +case 0x1200: +case 0x1400: +case 0x1600: +case 0x1800: +case 0x1A00: +case 0x1C00: +case 0x1E00: +case 0x1001: +case 0x1201: +case 0x1401: +case 0x1601: +case 0x1801: +case 0x1A01: +case 0x1C01: +case 0x1E01: +case 0x1002: +case 0x1202: +case 0x1402: +case 0x1602: +case 0x1802: +case 0x1A02: +case 0x1C02: +case 0x1E02: +case 0x1003: +case 0x1203: +case 0x1403: +case 0x1603: +case 0x1803: +case 0x1A03: +case 0x1C03: +case 0x1E03: +case 0x1004: +case 0x1204: +case 0x1404: +case 0x1604: +case 0x1804: +case 0x1A04: +case 0x1C04: +case 0x1E04: +case 0x1005: +case 0x1205: +case 0x1405: +case 0x1605: +case 0x1805: +case 0x1A05: +case 0x1C05: +case 0x1E05: +case 0x1006: +case 0x1206: +case 0x1406: +case 0x1606: +case 0x1806: +case 0x1A06: +case 0x1C06: +case 0x1E06: +case 0x1007: +case 0x1207: +case 0x1407: +case 0x1607: +case 0x1807: +case 0x1A07: +case 0x1C07: +case 0x1E07: + +// MOVEB +case 0x1000: +{ + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x1280: +case 0x1480: +case 0x1680: +case 0x1880: +case 0x1A80: +case 0x1C80: +case 0x1E80: +case 0x1081: +case 0x1281: +case 0x1481: +case 0x1681: +case 0x1881: +case 0x1A81: +case 0x1C81: +case 0x1E81: +case 0x1082: +case 0x1282: +case 0x1482: +case 0x1682: +case 0x1882: +case 0x1A82: +case 0x1C82: +case 0x1E82: +case 0x1083: +case 0x1283: +case 0x1483: +case 0x1683: +case 0x1883: +case 0x1A83: +case 0x1C83: +case 0x1E83: +case 0x1084: +case 0x1284: +case 0x1484: +case 0x1684: +case 0x1884: +case 0x1A84: +case 0x1C84: +case 0x1E84: +case 0x1085: +case 0x1285: +case 0x1485: +case 0x1685: +case 0x1885: +case 0x1A85: +case 0x1C85: +case 0x1E85: +case 0x1086: +case 0x1286: +case 0x1486: +case 0x1686: +case 0x1886: +case 0x1A86: +case 0x1C86: +case 0x1E86: +case 0x1087: +case 0x1287: +case 0x1487: +case 0x1687: +case 0x1887: +case 0x1A87: +case 0x1C87: +case 0x1E87: + +// MOVEB +case 0x1080: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x12C0: +case 0x14C0: +case 0x16C0: +case 0x18C0: +case 0x1AC0: +case 0x1CC0: +case 0x10C1: +case 0x12C1: +case 0x14C1: +case 0x16C1: +case 0x18C1: +case 0x1AC1: +case 0x1CC1: +case 0x10C2: +case 0x12C2: +case 0x14C2: +case 0x16C2: +case 0x18C2: +case 0x1AC2: +case 0x1CC2: +case 0x10C3: +case 0x12C3: +case 0x14C3: +case 0x16C3: +case 0x18C3: +case 0x1AC3: +case 0x1CC3: +case 0x10C4: +case 0x12C4: +case 0x14C4: +case 0x16C4: +case 0x18C4: +case 0x1AC4: +case 0x1CC4: +case 0x10C5: +case 0x12C5: +case 0x14C5: +case 0x16C5: +case 0x18C5: +case 0x1AC5: +case 0x1CC5: +case 0x10C6: +case 0x12C6: +case 0x14C6: +case 0x16C6: +case 0x18C6: +case 0x1AC6: +case 0x1CC6: +case 0x10C7: +case 0x12C7: +case 0x14C7: +case 0x16C7: +case 0x18C7: +case 0x1AC7: +case 0x1CC7: + +// MOVEB +case 0x10C0: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1300: +case 0x1500: +case 0x1700: +case 0x1900: +case 0x1B00: +case 0x1D00: +case 0x1101: +case 0x1301: +case 0x1501: +case 0x1701: +case 0x1901: +case 0x1B01: +case 0x1D01: +case 0x1102: +case 0x1302: +case 0x1502: +case 0x1702: +case 0x1902: +case 0x1B02: +case 0x1D02: +case 0x1103: +case 0x1303: +case 0x1503: +case 0x1703: +case 0x1903: +case 0x1B03: +case 0x1D03: +case 0x1104: +case 0x1304: +case 0x1504: +case 0x1704: +case 0x1904: +case 0x1B04: +case 0x1D04: +case 0x1105: +case 0x1305: +case 0x1505: +case 0x1705: +case 0x1905: +case 0x1B05: +case 0x1D05: +case 0x1106: +case 0x1306: +case 0x1506: +case 0x1706: +case 0x1906: +case 0x1B06: +case 0x1D06: +case 0x1107: +case 0x1307: +case 0x1507: +case 0x1707: +case 0x1907: +case 0x1B07: +case 0x1D07: + +// MOVEB +case 0x1100: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1340: +case 0x1540: +case 0x1740: +case 0x1940: +case 0x1B40: +case 0x1D40: +case 0x1F40: +case 0x1141: +case 0x1341: +case 0x1541: +case 0x1741: +case 0x1941: +case 0x1B41: +case 0x1D41: +case 0x1F41: +case 0x1142: +case 0x1342: +case 0x1542: +case 0x1742: +case 0x1942: +case 0x1B42: +case 0x1D42: +case 0x1F42: +case 0x1143: +case 0x1343: +case 0x1543: +case 0x1743: +case 0x1943: +case 0x1B43: +case 0x1D43: +case 0x1F43: +case 0x1144: +case 0x1344: +case 0x1544: +case 0x1744: +case 0x1944: +case 0x1B44: +case 0x1D44: +case 0x1F44: +case 0x1145: +case 0x1345: +case 0x1545: +case 0x1745: +case 0x1945: +case 0x1B45: +case 0x1D45: +case 0x1F45: +case 0x1146: +case 0x1346: +case 0x1546: +case 0x1746: +case 0x1946: +case 0x1B46: +case 0x1D46: +case 0x1F46: +case 0x1147: +case 0x1347: +case 0x1547: +case 0x1747: +case 0x1947: +case 0x1B47: +case 0x1D47: +case 0x1F47: + +// MOVEB +case 0x1140: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1380: +case 0x1580: +case 0x1780: +case 0x1980: +case 0x1B80: +case 0x1D80: +case 0x1F80: +case 0x1181: +case 0x1381: +case 0x1581: +case 0x1781: +case 0x1981: +case 0x1B81: +case 0x1D81: +case 0x1F81: +case 0x1182: +case 0x1382: +case 0x1582: +case 0x1782: +case 0x1982: +case 0x1B82: +case 0x1D82: +case 0x1F82: +case 0x1183: +case 0x1383: +case 0x1583: +case 0x1783: +case 0x1983: +case 0x1B83: +case 0x1D83: +case 0x1F83: +case 0x1184: +case 0x1384: +case 0x1584: +case 0x1784: +case 0x1984: +case 0x1B84: +case 0x1D84: +case 0x1F84: +case 0x1185: +case 0x1385: +case 0x1585: +case 0x1785: +case 0x1985: +case 0x1B85: +case 0x1D85: +case 0x1F85: +case 0x1186: +case 0x1386: +case 0x1586: +case 0x1786: +case 0x1986: +case 0x1B86: +case 0x1D86: +case 0x1F86: +case 0x1187: +case 0x1387: +case 0x1587: +case 0x1787: +case 0x1987: +case 0x1B87: +case 0x1D87: +case 0x1F87: + +// MOVEB +case 0x1180: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x11C1: +case 0x11C2: +case 0x11C3: +case 0x11C4: +case 0x11C5: +case 0x11C6: +case 0x11C7: + +// MOVEB +case 0x11C0: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x13C1: +case 0x13C2: +case 0x13C3: +case 0x13C4: +case 0x13C5: +case 0x13C6: +case 0x13C7: + +// MOVEB +case 0x13C0: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1EC1: +case 0x1EC2: +case 0x1EC3: +case 0x1EC4: +case 0x1EC5: +case 0x1EC6: +case 0x1EC7: + +// MOVEB +case 0x1EC0: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1F01: +case 0x1F02: +case 0x1F03: +case 0x1F04: +case 0x1F05: +case 0x1F06: +case 0x1F07: + +// MOVEB +case 0x1F00: +{ + u32 adr; + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1208: +case 0x1408: +case 0x1608: +case 0x1808: +case 0x1A08: +case 0x1C08: +case 0x1E08: +case 0x1009: +case 0x1209: +case 0x1409: +case 0x1609: +case 0x1809: +case 0x1A09: +case 0x1C09: +case 0x1E09: +case 0x100A: +case 0x120A: +case 0x140A: +case 0x160A: +case 0x180A: +case 0x1A0A: +case 0x1C0A: +case 0x1E0A: +case 0x100B: +case 0x120B: +case 0x140B: +case 0x160B: +case 0x180B: +case 0x1A0B: +case 0x1C0B: +case 0x1E0B: +case 0x100C: +case 0x120C: +case 0x140C: +case 0x160C: +case 0x180C: +case 0x1A0C: +case 0x1C0C: +case 0x1E0C: +case 0x100D: +case 0x120D: +case 0x140D: +case 0x160D: +case 0x180D: +case 0x1A0D: +case 0x1C0D: +case 0x1E0D: +case 0x100E: +case 0x120E: +case 0x140E: +case 0x160E: +case 0x180E: +case 0x1A0E: +case 0x1C0E: +case 0x1E0E: +case 0x100F: +case 0x120F: +case 0x140F: +case 0x160F: +case 0x180F: +case 0x1A0F: +case 0x1C0F: +case 0x1E0F: + +// MOVEB +case 0x1008: +{ + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x1288: +case 0x1488: +case 0x1688: +case 0x1888: +case 0x1A88: +case 0x1C88: +case 0x1E88: +case 0x1089: +case 0x1289: +case 0x1489: +case 0x1689: +case 0x1889: +case 0x1A89: +case 0x1C89: +case 0x1E89: +case 0x108A: +case 0x128A: +case 0x148A: +case 0x168A: +case 0x188A: +case 0x1A8A: +case 0x1C8A: +case 0x1E8A: +case 0x108B: +case 0x128B: +case 0x148B: +case 0x168B: +case 0x188B: +case 0x1A8B: +case 0x1C8B: +case 0x1E8B: +case 0x108C: +case 0x128C: +case 0x148C: +case 0x168C: +case 0x188C: +case 0x1A8C: +case 0x1C8C: +case 0x1E8C: +case 0x108D: +case 0x128D: +case 0x148D: +case 0x168D: +case 0x188D: +case 0x1A8D: +case 0x1C8D: +case 0x1E8D: +case 0x108E: +case 0x128E: +case 0x148E: +case 0x168E: +case 0x188E: +case 0x1A8E: +case 0x1C8E: +case 0x1E8E: +case 0x108F: +case 0x128F: +case 0x148F: +case 0x168F: +case 0x188F: +case 0x1A8F: +case 0x1C8F: +case 0x1E8F: + +// MOVEB +case 0x1088: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x12C8: +case 0x14C8: +case 0x16C8: +case 0x18C8: +case 0x1AC8: +case 0x1CC8: +case 0x10C9: +case 0x12C9: +case 0x14C9: +case 0x16C9: +case 0x18C9: +case 0x1AC9: +case 0x1CC9: +case 0x10CA: +case 0x12CA: +case 0x14CA: +case 0x16CA: +case 0x18CA: +case 0x1ACA: +case 0x1CCA: +case 0x10CB: +case 0x12CB: +case 0x14CB: +case 0x16CB: +case 0x18CB: +case 0x1ACB: +case 0x1CCB: +case 0x10CC: +case 0x12CC: +case 0x14CC: +case 0x16CC: +case 0x18CC: +case 0x1ACC: +case 0x1CCC: +case 0x10CD: +case 0x12CD: +case 0x14CD: +case 0x16CD: +case 0x18CD: +case 0x1ACD: +case 0x1CCD: +case 0x10CE: +case 0x12CE: +case 0x14CE: +case 0x16CE: +case 0x18CE: +case 0x1ACE: +case 0x1CCE: +case 0x10CF: +case 0x12CF: +case 0x14CF: +case 0x16CF: +case 0x18CF: +case 0x1ACF: +case 0x1CCF: + +// MOVEB +case 0x10C8: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1308: +case 0x1508: +case 0x1708: +case 0x1908: +case 0x1B08: +case 0x1D08: +case 0x1109: +case 0x1309: +case 0x1509: +case 0x1709: +case 0x1909: +case 0x1B09: +case 0x1D09: +case 0x110A: +case 0x130A: +case 0x150A: +case 0x170A: +case 0x190A: +case 0x1B0A: +case 0x1D0A: +case 0x110B: +case 0x130B: +case 0x150B: +case 0x170B: +case 0x190B: +case 0x1B0B: +case 0x1D0B: +case 0x110C: +case 0x130C: +case 0x150C: +case 0x170C: +case 0x190C: +case 0x1B0C: +case 0x1D0C: +case 0x110D: +case 0x130D: +case 0x150D: +case 0x170D: +case 0x190D: +case 0x1B0D: +case 0x1D0D: +case 0x110E: +case 0x130E: +case 0x150E: +case 0x170E: +case 0x190E: +case 0x1B0E: +case 0x1D0E: +case 0x110F: +case 0x130F: +case 0x150F: +case 0x170F: +case 0x190F: +case 0x1B0F: +case 0x1D0F: + +// MOVEB +case 0x1108: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1348: +case 0x1548: +case 0x1748: +case 0x1948: +case 0x1B48: +case 0x1D48: +case 0x1F48: +case 0x1149: +case 0x1349: +case 0x1549: +case 0x1749: +case 0x1949: +case 0x1B49: +case 0x1D49: +case 0x1F49: +case 0x114A: +case 0x134A: +case 0x154A: +case 0x174A: +case 0x194A: +case 0x1B4A: +case 0x1D4A: +case 0x1F4A: +case 0x114B: +case 0x134B: +case 0x154B: +case 0x174B: +case 0x194B: +case 0x1B4B: +case 0x1D4B: +case 0x1F4B: +case 0x114C: +case 0x134C: +case 0x154C: +case 0x174C: +case 0x194C: +case 0x1B4C: +case 0x1D4C: +case 0x1F4C: +case 0x114D: +case 0x134D: +case 0x154D: +case 0x174D: +case 0x194D: +case 0x1B4D: +case 0x1D4D: +case 0x1F4D: +case 0x114E: +case 0x134E: +case 0x154E: +case 0x174E: +case 0x194E: +case 0x1B4E: +case 0x1D4E: +case 0x1F4E: +case 0x114F: +case 0x134F: +case 0x154F: +case 0x174F: +case 0x194F: +case 0x1B4F: +case 0x1D4F: +case 0x1F4F: + +// MOVEB +case 0x1148: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1388: +case 0x1588: +case 0x1788: +case 0x1988: +case 0x1B88: +case 0x1D88: +case 0x1F88: +case 0x1189: +case 0x1389: +case 0x1589: +case 0x1789: +case 0x1989: +case 0x1B89: +case 0x1D89: +case 0x1F89: +case 0x118A: +case 0x138A: +case 0x158A: +case 0x178A: +case 0x198A: +case 0x1B8A: +case 0x1D8A: +case 0x1F8A: +case 0x118B: +case 0x138B: +case 0x158B: +case 0x178B: +case 0x198B: +case 0x1B8B: +case 0x1D8B: +case 0x1F8B: +case 0x118C: +case 0x138C: +case 0x158C: +case 0x178C: +case 0x198C: +case 0x1B8C: +case 0x1D8C: +case 0x1F8C: +case 0x118D: +case 0x138D: +case 0x158D: +case 0x178D: +case 0x198D: +case 0x1B8D: +case 0x1D8D: +case 0x1F8D: +case 0x118E: +case 0x138E: +case 0x158E: +case 0x178E: +case 0x198E: +case 0x1B8E: +case 0x1D8E: +case 0x1F8E: +case 0x118F: +case 0x138F: +case 0x158F: +case 0x178F: +case 0x198F: +case 0x1B8F: +case 0x1D8F: +case 0x1F8F: + +// MOVEB +case 0x1188: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x11C9: +case 0x11CA: +case 0x11CB: +case 0x11CC: +case 0x11CD: +case 0x11CE: +case 0x11CF: + +// MOVEB +case 0x11C8: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x13C9: +case 0x13CA: +case 0x13CB: +case 0x13CC: +case 0x13CD: +case 0x13CE: +case 0x13CF: + +// MOVEB +case 0x13C8: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1EC9: +case 0x1ECA: +case 0x1ECB: +case 0x1ECC: +case 0x1ECD: +case 0x1ECE: +case 0x1ECF: + +// MOVEB +case 0x1EC8: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1F09: +case 0x1F0A: +case 0x1F0B: +case 0x1F0C: +case 0x1F0D: +case 0x1F0E: +case 0x1F0F: + +// MOVEB +case 0x1F08: +{ + u32 adr; + u32 res; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x1210: +case 0x1410: +case 0x1610: +case 0x1810: +case 0x1A10: +case 0x1C10: +case 0x1E10: +case 0x1011: +case 0x1211: +case 0x1411: +case 0x1611: +case 0x1811: +case 0x1A11: +case 0x1C11: +case 0x1E11: +case 0x1012: +case 0x1212: +case 0x1412: +case 0x1612: +case 0x1812: +case 0x1A12: +case 0x1C12: +case 0x1E12: +case 0x1013: +case 0x1213: +case 0x1413: +case 0x1613: +case 0x1813: +case 0x1A13: +case 0x1C13: +case 0x1E13: +case 0x1014: +case 0x1214: +case 0x1414: +case 0x1614: +case 0x1814: +case 0x1A14: +case 0x1C14: +case 0x1E14: +case 0x1015: +case 0x1215: +case 0x1415: +case 0x1615: +case 0x1815: +case 0x1A15: +case 0x1C15: +case 0x1E15: +case 0x1016: +case 0x1216: +case 0x1416: +case 0x1616: +case 0x1816: +case 0x1A16: +case 0x1C16: +case 0x1E16: +case 0x1017: +case 0x1217: +case 0x1417: +case 0x1617: +case 0x1817: +case 0x1A17: +case 0x1C17: +case 0x1E17: + +// MOVEB +case 0x1010: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x1290: +case 0x1490: +case 0x1690: +case 0x1890: +case 0x1A90: +case 0x1C90: +case 0x1E90: +case 0x1091: +case 0x1291: +case 0x1491: +case 0x1691: +case 0x1891: +case 0x1A91: +case 0x1C91: +case 0x1E91: +case 0x1092: +case 0x1292: +case 0x1492: +case 0x1692: +case 0x1892: +case 0x1A92: +case 0x1C92: +case 0x1E92: +case 0x1093: +case 0x1293: +case 0x1493: +case 0x1693: +case 0x1893: +case 0x1A93: +case 0x1C93: +case 0x1E93: +case 0x1094: +case 0x1294: +case 0x1494: +case 0x1694: +case 0x1894: +case 0x1A94: +case 0x1C94: +case 0x1E94: +case 0x1095: +case 0x1295: +case 0x1495: +case 0x1695: +case 0x1895: +case 0x1A95: +case 0x1C95: +case 0x1E95: +case 0x1096: +case 0x1296: +case 0x1496: +case 0x1696: +case 0x1896: +case 0x1A96: +case 0x1C96: +case 0x1E96: +case 0x1097: +case 0x1297: +case 0x1497: +case 0x1697: +case 0x1897: +case 0x1A97: +case 0x1C97: +case 0x1E97: + +// MOVEB +case 0x1090: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x12D0: +case 0x14D0: +case 0x16D0: +case 0x18D0: +case 0x1AD0: +case 0x1CD0: +case 0x10D1: +case 0x12D1: +case 0x14D1: +case 0x16D1: +case 0x18D1: +case 0x1AD1: +case 0x1CD1: +case 0x10D2: +case 0x12D2: +case 0x14D2: +case 0x16D2: +case 0x18D2: +case 0x1AD2: +case 0x1CD2: +case 0x10D3: +case 0x12D3: +case 0x14D3: +case 0x16D3: +case 0x18D3: +case 0x1AD3: +case 0x1CD3: +case 0x10D4: +case 0x12D4: +case 0x14D4: +case 0x16D4: +case 0x18D4: +case 0x1AD4: +case 0x1CD4: +case 0x10D5: +case 0x12D5: +case 0x14D5: +case 0x16D5: +case 0x18D5: +case 0x1AD5: +case 0x1CD5: +case 0x10D6: +case 0x12D6: +case 0x14D6: +case 0x16D6: +case 0x18D6: +case 0x1AD6: +case 0x1CD6: +case 0x10D7: +case 0x12D7: +case 0x14D7: +case 0x16D7: +case 0x18D7: +case 0x1AD7: +case 0x1CD7: + +// MOVEB +case 0x10D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1310: +case 0x1510: +case 0x1710: +case 0x1910: +case 0x1B10: +case 0x1D10: +case 0x1111: +case 0x1311: +case 0x1511: +case 0x1711: +case 0x1911: +case 0x1B11: +case 0x1D11: +case 0x1112: +case 0x1312: +case 0x1512: +case 0x1712: +case 0x1912: +case 0x1B12: +case 0x1D12: +case 0x1113: +case 0x1313: +case 0x1513: +case 0x1713: +case 0x1913: +case 0x1B13: +case 0x1D13: +case 0x1114: +case 0x1314: +case 0x1514: +case 0x1714: +case 0x1914: +case 0x1B14: +case 0x1D14: +case 0x1115: +case 0x1315: +case 0x1515: +case 0x1715: +case 0x1915: +case 0x1B15: +case 0x1D15: +case 0x1116: +case 0x1316: +case 0x1516: +case 0x1716: +case 0x1916: +case 0x1B16: +case 0x1D16: +case 0x1117: +case 0x1317: +case 0x1517: +case 0x1717: +case 0x1917: +case 0x1B17: +case 0x1D17: + +// MOVEB +case 0x1110: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1350: +case 0x1550: +case 0x1750: +case 0x1950: +case 0x1B50: +case 0x1D50: +case 0x1F50: +case 0x1151: +case 0x1351: +case 0x1551: +case 0x1751: +case 0x1951: +case 0x1B51: +case 0x1D51: +case 0x1F51: +case 0x1152: +case 0x1352: +case 0x1552: +case 0x1752: +case 0x1952: +case 0x1B52: +case 0x1D52: +case 0x1F52: +case 0x1153: +case 0x1353: +case 0x1553: +case 0x1753: +case 0x1953: +case 0x1B53: +case 0x1D53: +case 0x1F53: +case 0x1154: +case 0x1354: +case 0x1554: +case 0x1754: +case 0x1954: +case 0x1B54: +case 0x1D54: +case 0x1F54: +case 0x1155: +case 0x1355: +case 0x1555: +case 0x1755: +case 0x1955: +case 0x1B55: +case 0x1D55: +case 0x1F55: +case 0x1156: +case 0x1356: +case 0x1556: +case 0x1756: +case 0x1956: +case 0x1B56: +case 0x1D56: +case 0x1F56: +case 0x1157: +case 0x1357: +case 0x1557: +case 0x1757: +case 0x1957: +case 0x1B57: +case 0x1D57: +case 0x1F57: + +// MOVEB +case 0x1150: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1390: +case 0x1590: +case 0x1790: +case 0x1990: +case 0x1B90: +case 0x1D90: +case 0x1F90: +case 0x1191: +case 0x1391: +case 0x1591: +case 0x1791: +case 0x1991: +case 0x1B91: +case 0x1D91: +case 0x1F91: +case 0x1192: +case 0x1392: +case 0x1592: +case 0x1792: +case 0x1992: +case 0x1B92: +case 0x1D92: +case 0x1F92: +case 0x1193: +case 0x1393: +case 0x1593: +case 0x1793: +case 0x1993: +case 0x1B93: +case 0x1D93: +case 0x1F93: +case 0x1194: +case 0x1394: +case 0x1594: +case 0x1794: +case 0x1994: +case 0x1B94: +case 0x1D94: +case 0x1F94: +case 0x1195: +case 0x1395: +case 0x1595: +case 0x1795: +case 0x1995: +case 0x1B95: +case 0x1D95: +case 0x1F95: +case 0x1196: +case 0x1396: +case 0x1596: +case 0x1796: +case 0x1996: +case 0x1B96: +case 0x1D96: +case 0x1F96: +case 0x1197: +case 0x1397: +case 0x1597: +case 0x1797: +case 0x1997: +case 0x1B97: +case 0x1D97: +case 0x1F97: + +// MOVEB +case 0x1190: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x11D1: +case 0x11D2: +case 0x11D3: +case 0x11D4: +case 0x11D5: +case 0x11D6: +case 0x11D7: + +// MOVEB +case 0x11D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x13D1: +case 0x13D2: +case 0x13D3: +case 0x13D4: +case 0x13D5: +case 0x13D6: +case 0x13D7: + +// MOVEB +case 0x13D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x1ED1: +case 0x1ED2: +case 0x1ED3: +case 0x1ED4: +case 0x1ED5: +case 0x1ED6: +case 0x1ED7: + +// MOVEB +case 0x1ED0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1F11: +case 0x1F12: +case 0x1F13: +case 0x1F14: +case 0x1F15: +case 0x1F16: +case 0x1F17: + +// MOVEB +case 0x1F10: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1218: +case 0x1418: +case 0x1618: +case 0x1818: +case 0x1A18: +case 0x1C18: +case 0x1E18: +case 0x1019: +case 0x1219: +case 0x1419: +case 0x1619: +case 0x1819: +case 0x1A19: +case 0x1C19: +case 0x1E19: +case 0x101A: +case 0x121A: +case 0x141A: +case 0x161A: +case 0x181A: +case 0x1A1A: +case 0x1C1A: +case 0x1E1A: +case 0x101B: +case 0x121B: +case 0x141B: +case 0x161B: +case 0x181B: +case 0x1A1B: +case 0x1C1B: +case 0x1E1B: +case 0x101C: +case 0x121C: +case 0x141C: +case 0x161C: +case 0x181C: +case 0x1A1C: +case 0x1C1C: +case 0x1E1C: +case 0x101D: +case 0x121D: +case 0x141D: +case 0x161D: +case 0x181D: +case 0x1A1D: +case 0x1C1D: +case 0x1E1D: +case 0x101E: +case 0x121E: +case 0x141E: +case 0x161E: +case 0x181E: +case 0x1A1E: +case 0x1C1E: +case 0x1E1E: + +// MOVEB +case 0x1018: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x1298: +case 0x1498: +case 0x1698: +case 0x1898: +case 0x1A98: +case 0x1C98: +case 0x1E98: +case 0x1099: +case 0x1299: +case 0x1499: +case 0x1699: +case 0x1899: +case 0x1A99: +case 0x1C99: +case 0x1E99: +case 0x109A: +case 0x129A: +case 0x149A: +case 0x169A: +case 0x189A: +case 0x1A9A: +case 0x1C9A: +case 0x1E9A: +case 0x109B: +case 0x129B: +case 0x149B: +case 0x169B: +case 0x189B: +case 0x1A9B: +case 0x1C9B: +case 0x1E9B: +case 0x109C: +case 0x129C: +case 0x149C: +case 0x169C: +case 0x189C: +case 0x1A9C: +case 0x1C9C: +case 0x1E9C: +case 0x109D: +case 0x129D: +case 0x149D: +case 0x169D: +case 0x189D: +case 0x1A9D: +case 0x1C9D: +case 0x1E9D: +case 0x109E: +case 0x129E: +case 0x149E: +case 0x169E: +case 0x189E: +case 0x1A9E: +case 0x1C9E: +case 0x1E9E: + +// MOVEB +case 0x1098: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x12D8: +case 0x14D8: +case 0x16D8: +case 0x18D8: +case 0x1AD8: +case 0x1CD8: +case 0x10D9: +case 0x12D9: +case 0x14D9: +case 0x16D9: +case 0x18D9: +case 0x1AD9: +case 0x1CD9: +case 0x10DA: +case 0x12DA: +case 0x14DA: +case 0x16DA: +case 0x18DA: +case 0x1ADA: +case 0x1CDA: +case 0x10DB: +case 0x12DB: +case 0x14DB: +case 0x16DB: +case 0x18DB: +case 0x1ADB: +case 0x1CDB: +case 0x10DC: +case 0x12DC: +case 0x14DC: +case 0x16DC: +case 0x18DC: +case 0x1ADC: +case 0x1CDC: +case 0x10DD: +case 0x12DD: +case 0x14DD: +case 0x16DD: +case 0x18DD: +case 0x1ADD: +case 0x1CDD: +case 0x10DE: +case 0x12DE: +case 0x14DE: +case 0x16DE: +case 0x18DE: +case 0x1ADE: +case 0x1CDE: + +// MOVEB +case 0x10D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1318: +case 0x1518: +case 0x1718: +case 0x1918: +case 0x1B18: +case 0x1D18: +case 0x1119: +case 0x1319: +case 0x1519: +case 0x1719: +case 0x1919: +case 0x1B19: +case 0x1D19: +case 0x111A: +case 0x131A: +case 0x151A: +case 0x171A: +case 0x191A: +case 0x1B1A: +case 0x1D1A: +case 0x111B: +case 0x131B: +case 0x151B: +case 0x171B: +case 0x191B: +case 0x1B1B: +case 0x1D1B: +case 0x111C: +case 0x131C: +case 0x151C: +case 0x171C: +case 0x191C: +case 0x1B1C: +case 0x1D1C: +case 0x111D: +case 0x131D: +case 0x151D: +case 0x171D: +case 0x191D: +case 0x1B1D: +case 0x1D1D: +case 0x111E: +case 0x131E: +case 0x151E: +case 0x171E: +case 0x191E: +case 0x1B1E: +case 0x1D1E: + +// MOVEB +case 0x1118: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1358: +case 0x1558: +case 0x1758: +case 0x1958: +case 0x1B58: +case 0x1D58: +case 0x1F58: +case 0x1159: +case 0x1359: +case 0x1559: +case 0x1759: +case 0x1959: +case 0x1B59: +case 0x1D59: +case 0x1F59: +case 0x115A: +case 0x135A: +case 0x155A: +case 0x175A: +case 0x195A: +case 0x1B5A: +case 0x1D5A: +case 0x1F5A: +case 0x115B: +case 0x135B: +case 0x155B: +case 0x175B: +case 0x195B: +case 0x1B5B: +case 0x1D5B: +case 0x1F5B: +case 0x115C: +case 0x135C: +case 0x155C: +case 0x175C: +case 0x195C: +case 0x1B5C: +case 0x1D5C: +case 0x1F5C: +case 0x115D: +case 0x135D: +case 0x155D: +case 0x175D: +case 0x195D: +case 0x1B5D: +case 0x1D5D: +case 0x1F5D: +case 0x115E: +case 0x135E: +case 0x155E: +case 0x175E: +case 0x195E: +case 0x1B5E: +case 0x1D5E: +case 0x1F5E: + +// MOVEB +case 0x1158: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1398: +case 0x1598: +case 0x1798: +case 0x1998: +case 0x1B98: +case 0x1D98: +case 0x1F98: +case 0x1199: +case 0x1399: +case 0x1599: +case 0x1799: +case 0x1999: +case 0x1B99: +case 0x1D99: +case 0x1F99: +case 0x119A: +case 0x139A: +case 0x159A: +case 0x179A: +case 0x199A: +case 0x1B9A: +case 0x1D9A: +case 0x1F9A: +case 0x119B: +case 0x139B: +case 0x159B: +case 0x179B: +case 0x199B: +case 0x1B9B: +case 0x1D9B: +case 0x1F9B: +case 0x119C: +case 0x139C: +case 0x159C: +case 0x179C: +case 0x199C: +case 0x1B9C: +case 0x1D9C: +case 0x1F9C: +case 0x119D: +case 0x139D: +case 0x159D: +case 0x179D: +case 0x199D: +case 0x1B9D: +case 0x1D9D: +case 0x1F9D: +case 0x119E: +case 0x139E: +case 0x159E: +case 0x179E: +case 0x199E: +case 0x1B9E: +case 0x1D9E: +case 0x1F9E: + +// MOVEB +case 0x1198: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x11D9: +case 0x11DA: +case 0x11DB: +case 0x11DC: +case 0x11DD: +case 0x11DE: + +// MOVEB +case 0x11D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x13D9: +case 0x13DA: +case 0x13DB: +case 0x13DC: +case 0x13DD: +case 0x13DE: + +// MOVEB +case 0x13D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x1ED9: +case 0x1EDA: +case 0x1EDB: +case 0x1EDC: +case 0x1EDD: +case 0x1EDE: + +// MOVEB +case 0x1ED8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1F19: +case 0x1F1A: +case 0x1F1B: +case 0x1F1C: +case 0x1F1D: +case 0x1F1E: + +// MOVEB +case 0x1F18: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1220: +case 0x1420: +case 0x1620: +case 0x1820: +case 0x1A20: +case 0x1C20: +case 0x1E20: +case 0x1021: +case 0x1221: +case 0x1421: +case 0x1621: +case 0x1821: +case 0x1A21: +case 0x1C21: +case 0x1E21: +case 0x1022: +case 0x1222: +case 0x1422: +case 0x1622: +case 0x1822: +case 0x1A22: +case 0x1C22: +case 0x1E22: +case 0x1023: +case 0x1223: +case 0x1423: +case 0x1623: +case 0x1823: +case 0x1A23: +case 0x1C23: +case 0x1E23: +case 0x1024: +case 0x1224: +case 0x1424: +case 0x1624: +case 0x1824: +case 0x1A24: +case 0x1C24: +case 0x1E24: +case 0x1025: +case 0x1225: +case 0x1425: +case 0x1625: +case 0x1825: +case 0x1A25: +case 0x1C25: +case 0x1E25: +case 0x1026: +case 0x1226: +case 0x1426: +case 0x1626: +case 0x1826: +case 0x1A26: +case 0x1C26: +case 0x1E26: + +// MOVEB +case 0x1020: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x12A0: +case 0x14A0: +case 0x16A0: +case 0x18A0: +case 0x1AA0: +case 0x1CA0: +case 0x1EA0: +case 0x10A1: +case 0x12A1: +case 0x14A1: +case 0x16A1: +case 0x18A1: +case 0x1AA1: +case 0x1CA1: +case 0x1EA1: +case 0x10A2: +case 0x12A2: +case 0x14A2: +case 0x16A2: +case 0x18A2: +case 0x1AA2: +case 0x1CA2: +case 0x1EA2: +case 0x10A3: +case 0x12A3: +case 0x14A3: +case 0x16A3: +case 0x18A3: +case 0x1AA3: +case 0x1CA3: +case 0x1EA3: +case 0x10A4: +case 0x12A4: +case 0x14A4: +case 0x16A4: +case 0x18A4: +case 0x1AA4: +case 0x1CA4: +case 0x1EA4: +case 0x10A5: +case 0x12A5: +case 0x14A5: +case 0x16A5: +case 0x18A5: +case 0x1AA5: +case 0x1CA5: +case 0x1EA5: +case 0x10A6: +case 0x12A6: +case 0x14A6: +case 0x16A6: +case 0x18A6: +case 0x1AA6: +case 0x1CA6: +case 0x1EA6: + +// MOVEB +case 0x10A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x12E0: +case 0x14E0: +case 0x16E0: +case 0x18E0: +case 0x1AE0: +case 0x1CE0: +case 0x10E1: +case 0x12E1: +case 0x14E1: +case 0x16E1: +case 0x18E1: +case 0x1AE1: +case 0x1CE1: +case 0x10E2: +case 0x12E2: +case 0x14E2: +case 0x16E2: +case 0x18E2: +case 0x1AE2: +case 0x1CE2: +case 0x10E3: +case 0x12E3: +case 0x14E3: +case 0x16E3: +case 0x18E3: +case 0x1AE3: +case 0x1CE3: +case 0x10E4: +case 0x12E4: +case 0x14E4: +case 0x16E4: +case 0x18E4: +case 0x1AE4: +case 0x1CE4: +case 0x10E5: +case 0x12E5: +case 0x14E5: +case 0x16E5: +case 0x18E5: +case 0x1AE5: +case 0x1CE5: +case 0x10E6: +case 0x12E6: +case 0x14E6: +case 0x16E6: +case 0x18E6: +case 0x1AE6: +case 0x1CE6: + +// MOVEB +case 0x10E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1320: +case 0x1520: +case 0x1720: +case 0x1920: +case 0x1B20: +case 0x1D20: +case 0x1121: +case 0x1321: +case 0x1521: +case 0x1721: +case 0x1921: +case 0x1B21: +case 0x1D21: +case 0x1122: +case 0x1322: +case 0x1522: +case 0x1722: +case 0x1922: +case 0x1B22: +case 0x1D22: +case 0x1123: +case 0x1323: +case 0x1523: +case 0x1723: +case 0x1923: +case 0x1B23: +case 0x1D23: +case 0x1124: +case 0x1324: +case 0x1524: +case 0x1724: +case 0x1924: +case 0x1B24: +case 0x1D24: +case 0x1125: +case 0x1325: +case 0x1525: +case 0x1725: +case 0x1925: +case 0x1B25: +case 0x1D25: +case 0x1126: +case 0x1326: +case 0x1526: +case 0x1726: +case 0x1926: +case 0x1B26: +case 0x1D26: + +// MOVEB +case 0x1120: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1360: +case 0x1560: +case 0x1760: +case 0x1960: +case 0x1B60: +case 0x1D60: +case 0x1F60: +case 0x1161: +case 0x1361: +case 0x1561: +case 0x1761: +case 0x1961: +case 0x1B61: +case 0x1D61: +case 0x1F61: +case 0x1162: +case 0x1362: +case 0x1562: +case 0x1762: +case 0x1962: +case 0x1B62: +case 0x1D62: +case 0x1F62: +case 0x1163: +case 0x1363: +case 0x1563: +case 0x1763: +case 0x1963: +case 0x1B63: +case 0x1D63: +case 0x1F63: +case 0x1164: +case 0x1364: +case 0x1564: +case 0x1764: +case 0x1964: +case 0x1B64: +case 0x1D64: +case 0x1F64: +case 0x1165: +case 0x1365: +case 0x1565: +case 0x1765: +case 0x1965: +case 0x1B65: +case 0x1D65: +case 0x1F65: +case 0x1166: +case 0x1366: +case 0x1566: +case 0x1766: +case 0x1966: +case 0x1B66: +case 0x1D66: +case 0x1F66: + +// MOVEB +case 0x1160: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x13A0: +case 0x15A0: +case 0x17A0: +case 0x19A0: +case 0x1BA0: +case 0x1DA0: +case 0x1FA0: +case 0x11A1: +case 0x13A1: +case 0x15A1: +case 0x17A1: +case 0x19A1: +case 0x1BA1: +case 0x1DA1: +case 0x1FA1: +case 0x11A2: +case 0x13A2: +case 0x15A2: +case 0x17A2: +case 0x19A2: +case 0x1BA2: +case 0x1DA2: +case 0x1FA2: +case 0x11A3: +case 0x13A3: +case 0x15A3: +case 0x17A3: +case 0x19A3: +case 0x1BA3: +case 0x1DA3: +case 0x1FA3: +case 0x11A4: +case 0x13A4: +case 0x15A4: +case 0x17A4: +case 0x19A4: +case 0x1BA4: +case 0x1DA4: +case 0x1FA4: +case 0x11A5: +case 0x13A5: +case 0x15A5: +case 0x17A5: +case 0x19A5: +case 0x1BA5: +case 0x1DA5: +case 0x1FA5: +case 0x11A6: +case 0x13A6: +case 0x15A6: +case 0x17A6: +case 0x19A6: +case 0x1BA6: +case 0x1DA6: +case 0x1FA6: + +// MOVEB +case 0x11A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x11E1: +case 0x11E2: +case 0x11E3: +case 0x11E4: +case 0x11E5: +case 0x11E6: + +// MOVEB +case 0x11E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x13E1: +case 0x13E2: +case 0x13E3: +case 0x13E4: +case 0x13E5: +case 0x13E6: + +// MOVEB +case 0x13E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) +case 0x1EE1: +case 0x1EE2: +case 0x1EE3: +case 0x1EE4: +case 0x1EE5: +case 0x1EE6: + +// MOVEB +case 0x1EE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1F21: +case 0x1F22: +case 0x1F23: +case 0x1F24: +case 0x1F25: +case 0x1F26: + +// MOVEB +case 0x1F20: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1228: +case 0x1428: +case 0x1628: +case 0x1828: +case 0x1A28: +case 0x1C28: +case 0x1E28: +case 0x1029: +case 0x1229: +case 0x1429: +case 0x1629: +case 0x1829: +case 0x1A29: +case 0x1C29: +case 0x1E29: +case 0x102A: +case 0x122A: +case 0x142A: +case 0x162A: +case 0x182A: +case 0x1A2A: +case 0x1C2A: +case 0x1E2A: +case 0x102B: +case 0x122B: +case 0x142B: +case 0x162B: +case 0x182B: +case 0x1A2B: +case 0x1C2B: +case 0x1E2B: +case 0x102C: +case 0x122C: +case 0x142C: +case 0x162C: +case 0x182C: +case 0x1A2C: +case 0x1C2C: +case 0x1E2C: +case 0x102D: +case 0x122D: +case 0x142D: +case 0x162D: +case 0x182D: +case 0x1A2D: +case 0x1C2D: +case 0x1E2D: +case 0x102E: +case 0x122E: +case 0x142E: +case 0x162E: +case 0x182E: +case 0x1A2E: +case 0x1C2E: +case 0x1E2E: +case 0x102F: +case 0x122F: +case 0x142F: +case 0x162F: +case 0x182F: +case 0x1A2F: +case 0x1C2F: +case 0x1E2F: + +// MOVEB +case 0x1028: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x12A8: +case 0x14A8: +case 0x16A8: +case 0x18A8: +case 0x1AA8: +case 0x1CA8: +case 0x1EA8: +case 0x10A9: +case 0x12A9: +case 0x14A9: +case 0x16A9: +case 0x18A9: +case 0x1AA9: +case 0x1CA9: +case 0x1EA9: +case 0x10AA: +case 0x12AA: +case 0x14AA: +case 0x16AA: +case 0x18AA: +case 0x1AAA: +case 0x1CAA: +case 0x1EAA: +case 0x10AB: +case 0x12AB: +case 0x14AB: +case 0x16AB: +case 0x18AB: +case 0x1AAB: +case 0x1CAB: +case 0x1EAB: +case 0x10AC: +case 0x12AC: +case 0x14AC: +case 0x16AC: +case 0x18AC: +case 0x1AAC: +case 0x1CAC: +case 0x1EAC: +case 0x10AD: +case 0x12AD: +case 0x14AD: +case 0x16AD: +case 0x18AD: +case 0x1AAD: +case 0x1CAD: +case 0x1EAD: +case 0x10AE: +case 0x12AE: +case 0x14AE: +case 0x16AE: +case 0x18AE: +case 0x1AAE: +case 0x1CAE: +case 0x1EAE: +case 0x10AF: +case 0x12AF: +case 0x14AF: +case 0x16AF: +case 0x18AF: +case 0x1AAF: +case 0x1CAF: +case 0x1EAF: + +// MOVEB +case 0x10A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x12E8: +case 0x14E8: +case 0x16E8: +case 0x18E8: +case 0x1AE8: +case 0x1CE8: +case 0x10E9: +case 0x12E9: +case 0x14E9: +case 0x16E9: +case 0x18E9: +case 0x1AE9: +case 0x1CE9: +case 0x10EA: +case 0x12EA: +case 0x14EA: +case 0x16EA: +case 0x18EA: +case 0x1AEA: +case 0x1CEA: +case 0x10EB: +case 0x12EB: +case 0x14EB: +case 0x16EB: +case 0x18EB: +case 0x1AEB: +case 0x1CEB: +case 0x10EC: +case 0x12EC: +case 0x14EC: +case 0x16EC: +case 0x18EC: +case 0x1AEC: +case 0x1CEC: +case 0x10ED: +case 0x12ED: +case 0x14ED: +case 0x16ED: +case 0x18ED: +case 0x1AED: +case 0x1CED: +case 0x10EE: +case 0x12EE: +case 0x14EE: +case 0x16EE: +case 0x18EE: +case 0x1AEE: +case 0x1CEE: +case 0x10EF: +case 0x12EF: +case 0x14EF: +case 0x16EF: +case 0x18EF: +case 0x1AEF: +case 0x1CEF: + +// MOVEB +case 0x10E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1328: +case 0x1528: +case 0x1728: +case 0x1928: +case 0x1B28: +case 0x1D28: +case 0x1129: +case 0x1329: +case 0x1529: +case 0x1729: +case 0x1929: +case 0x1B29: +case 0x1D29: +case 0x112A: +case 0x132A: +case 0x152A: +case 0x172A: +case 0x192A: +case 0x1B2A: +case 0x1D2A: +case 0x112B: +case 0x132B: +case 0x152B: +case 0x172B: +case 0x192B: +case 0x1B2B: +case 0x1D2B: +case 0x112C: +case 0x132C: +case 0x152C: +case 0x172C: +case 0x192C: +case 0x1B2C: +case 0x1D2C: +case 0x112D: +case 0x132D: +case 0x152D: +case 0x172D: +case 0x192D: +case 0x1B2D: +case 0x1D2D: +case 0x112E: +case 0x132E: +case 0x152E: +case 0x172E: +case 0x192E: +case 0x1B2E: +case 0x1D2E: +case 0x112F: +case 0x132F: +case 0x152F: +case 0x172F: +case 0x192F: +case 0x1B2F: +case 0x1D2F: + +// MOVEB +case 0x1128: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1368: +case 0x1568: +case 0x1768: +case 0x1968: +case 0x1B68: +case 0x1D68: +case 0x1F68: +case 0x1169: +case 0x1369: +case 0x1569: +case 0x1769: +case 0x1969: +case 0x1B69: +case 0x1D69: +case 0x1F69: +case 0x116A: +case 0x136A: +case 0x156A: +case 0x176A: +case 0x196A: +case 0x1B6A: +case 0x1D6A: +case 0x1F6A: +case 0x116B: +case 0x136B: +case 0x156B: +case 0x176B: +case 0x196B: +case 0x1B6B: +case 0x1D6B: +case 0x1F6B: +case 0x116C: +case 0x136C: +case 0x156C: +case 0x176C: +case 0x196C: +case 0x1B6C: +case 0x1D6C: +case 0x1F6C: +case 0x116D: +case 0x136D: +case 0x156D: +case 0x176D: +case 0x196D: +case 0x1B6D: +case 0x1D6D: +case 0x1F6D: +case 0x116E: +case 0x136E: +case 0x156E: +case 0x176E: +case 0x196E: +case 0x1B6E: +case 0x1D6E: +case 0x1F6E: +case 0x116F: +case 0x136F: +case 0x156F: +case 0x176F: +case 0x196F: +case 0x1B6F: +case 0x1D6F: +case 0x1F6F: + +// MOVEB +case 0x1168: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x13A8: +case 0x15A8: +case 0x17A8: +case 0x19A8: +case 0x1BA8: +case 0x1DA8: +case 0x1FA8: +case 0x11A9: +case 0x13A9: +case 0x15A9: +case 0x17A9: +case 0x19A9: +case 0x1BA9: +case 0x1DA9: +case 0x1FA9: +case 0x11AA: +case 0x13AA: +case 0x15AA: +case 0x17AA: +case 0x19AA: +case 0x1BAA: +case 0x1DAA: +case 0x1FAA: +case 0x11AB: +case 0x13AB: +case 0x15AB: +case 0x17AB: +case 0x19AB: +case 0x1BAB: +case 0x1DAB: +case 0x1FAB: +case 0x11AC: +case 0x13AC: +case 0x15AC: +case 0x17AC: +case 0x19AC: +case 0x1BAC: +case 0x1DAC: +case 0x1FAC: +case 0x11AD: +case 0x13AD: +case 0x15AD: +case 0x17AD: +case 0x19AD: +case 0x1BAD: +case 0x1DAD: +case 0x1FAD: +case 0x11AE: +case 0x13AE: +case 0x15AE: +case 0x17AE: +case 0x19AE: +case 0x1BAE: +case 0x1DAE: +case 0x1FAE: +case 0x11AF: +case 0x13AF: +case 0x15AF: +case 0x17AF: +case 0x19AF: +case 0x1BAF: +case 0x1DAF: +case 0x1FAF: + +// MOVEB +case 0x11A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) +case 0x11E9: +case 0x11EA: +case 0x11EB: +case 0x11EC: +case 0x11ED: +case 0x11EE: +case 0x11EF: + +// MOVEB +case 0x11E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x13E9: +case 0x13EA: +case 0x13EB: +case 0x13EC: +case 0x13ED: +case 0x13EE: +case 0x13EF: + +// MOVEB +case 0x13E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) +case 0x1EE9: +case 0x1EEA: +case 0x1EEB: +case 0x1EEC: +case 0x1EED: +case 0x1EEE: +case 0x1EEF: + +// MOVEB +case 0x1EE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1F29: +case 0x1F2A: +case 0x1F2B: +case 0x1F2C: +case 0x1F2D: +case 0x1F2E: +case 0x1F2F: + +// MOVEB +case 0x1F28: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1230: +case 0x1430: +case 0x1630: +case 0x1830: +case 0x1A30: +case 0x1C30: +case 0x1E30: +case 0x1031: +case 0x1231: +case 0x1431: +case 0x1631: +case 0x1831: +case 0x1A31: +case 0x1C31: +case 0x1E31: +case 0x1032: +case 0x1232: +case 0x1432: +case 0x1632: +case 0x1832: +case 0x1A32: +case 0x1C32: +case 0x1E32: +case 0x1033: +case 0x1233: +case 0x1433: +case 0x1633: +case 0x1833: +case 0x1A33: +case 0x1C33: +case 0x1E33: +case 0x1034: +case 0x1234: +case 0x1434: +case 0x1634: +case 0x1834: +case 0x1A34: +case 0x1C34: +case 0x1E34: +case 0x1035: +case 0x1235: +case 0x1435: +case 0x1635: +case 0x1835: +case 0x1A35: +case 0x1C35: +case 0x1E35: +case 0x1036: +case 0x1236: +case 0x1436: +case 0x1636: +case 0x1836: +case 0x1A36: +case 0x1C36: +case 0x1E36: +case 0x1037: +case 0x1237: +case 0x1437: +case 0x1637: +case 0x1837: +case 0x1A37: +case 0x1C37: +case 0x1E37: + +// MOVEB +case 0x1030: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x12B0: +case 0x14B0: +case 0x16B0: +case 0x18B0: +case 0x1AB0: +case 0x1CB0: +case 0x1EB0: +case 0x10B1: +case 0x12B1: +case 0x14B1: +case 0x16B1: +case 0x18B1: +case 0x1AB1: +case 0x1CB1: +case 0x1EB1: +case 0x10B2: +case 0x12B2: +case 0x14B2: +case 0x16B2: +case 0x18B2: +case 0x1AB2: +case 0x1CB2: +case 0x1EB2: +case 0x10B3: +case 0x12B3: +case 0x14B3: +case 0x16B3: +case 0x18B3: +case 0x1AB3: +case 0x1CB3: +case 0x1EB3: +case 0x10B4: +case 0x12B4: +case 0x14B4: +case 0x16B4: +case 0x18B4: +case 0x1AB4: +case 0x1CB4: +case 0x1EB4: +case 0x10B5: +case 0x12B5: +case 0x14B5: +case 0x16B5: +case 0x18B5: +case 0x1AB5: +case 0x1CB5: +case 0x1EB5: +case 0x10B6: +case 0x12B6: +case 0x14B6: +case 0x16B6: +case 0x18B6: +case 0x1AB6: +case 0x1CB6: +case 0x1EB6: +case 0x10B7: +case 0x12B7: +case 0x14B7: +case 0x16B7: +case 0x18B7: +case 0x1AB7: +case 0x1CB7: +case 0x1EB7: + +// MOVEB +case 0x10B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x12F0: +case 0x14F0: +case 0x16F0: +case 0x18F0: +case 0x1AF0: +case 0x1CF0: +case 0x10F1: +case 0x12F1: +case 0x14F1: +case 0x16F1: +case 0x18F1: +case 0x1AF1: +case 0x1CF1: +case 0x10F2: +case 0x12F2: +case 0x14F2: +case 0x16F2: +case 0x18F2: +case 0x1AF2: +case 0x1CF2: +case 0x10F3: +case 0x12F3: +case 0x14F3: +case 0x16F3: +case 0x18F3: +case 0x1AF3: +case 0x1CF3: +case 0x10F4: +case 0x12F4: +case 0x14F4: +case 0x16F4: +case 0x18F4: +case 0x1AF4: +case 0x1CF4: +case 0x10F5: +case 0x12F5: +case 0x14F5: +case 0x16F5: +case 0x18F5: +case 0x1AF5: +case 0x1CF5: +case 0x10F6: +case 0x12F6: +case 0x14F6: +case 0x16F6: +case 0x18F6: +case 0x1AF6: +case 0x1CF6: +case 0x10F7: +case 0x12F7: +case 0x14F7: +case 0x16F7: +case 0x18F7: +case 0x1AF7: +case 0x1CF7: + +// MOVEB +case 0x10F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x1330: +case 0x1530: +case 0x1730: +case 0x1930: +case 0x1B30: +case 0x1D30: +case 0x1131: +case 0x1331: +case 0x1531: +case 0x1731: +case 0x1931: +case 0x1B31: +case 0x1D31: +case 0x1132: +case 0x1332: +case 0x1532: +case 0x1732: +case 0x1932: +case 0x1B32: +case 0x1D32: +case 0x1133: +case 0x1333: +case 0x1533: +case 0x1733: +case 0x1933: +case 0x1B33: +case 0x1D33: +case 0x1134: +case 0x1334: +case 0x1534: +case 0x1734: +case 0x1934: +case 0x1B34: +case 0x1D34: +case 0x1135: +case 0x1335: +case 0x1535: +case 0x1735: +case 0x1935: +case 0x1B35: +case 0x1D35: +case 0x1136: +case 0x1336: +case 0x1536: +case 0x1736: +case 0x1936: +case 0x1B36: +case 0x1D36: +case 0x1137: +case 0x1337: +case 0x1537: +case 0x1737: +case 0x1937: +case 0x1B37: +case 0x1D37: + +// MOVEB +case 0x1130: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x1370: +case 0x1570: +case 0x1770: +case 0x1970: +case 0x1B70: +case 0x1D70: +case 0x1F70: +case 0x1171: +case 0x1371: +case 0x1571: +case 0x1771: +case 0x1971: +case 0x1B71: +case 0x1D71: +case 0x1F71: +case 0x1172: +case 0x1372: +case 0x1572: +case 0x1772: +case 0x1972: +case 0x1B72: +case 0x1D72: +case 0x1F72: +case 0x1173: +case 0x1373: +case 0x1573: +case 0x1773: +case 0x1973: +case 0x1B73: +case 0x1D73: +case 0x1F73: +case 0x1174: +case 0x1374: +case 0x1574: +case 0x1774: +case 0x1974: +case 0x1B74: +case 0x1D74: +case 0x1F74: +case 0x1175: +case 0x1375: +case 0x1575: +case 0x1775: +case 0x1975: +case 0x1B75: +case 0x1D75: +case 0x1F75: +case 0x1176: +case 0x1376: +case 0x1576: +case 0x1776: +case 0x1976: +case 0x1B76: +case 0x1D76: +case 0x1F76: +case 0x1177: +case 0x1377: +case 0x1577: +case 0x1777: +case 0x1977: +case 0x1B77: +case 0x1D77: +case 0x1F77: + +// MOVEB +case 0x1170: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) +case 0x13B0: +case 0x15B0: +case 0x17B0: +case 0x19B0: +case 0x1BB0: +case 0x1DB0: +case 0x1FB0: +case 0x11B1: +case 0x13B1: +case 0x15B1: +case 0x17B1: +case 0x19B1: +case 0x1BB1: +case 0x1DB1: +case 0x1FB1: +case 0x11B2: +case 0x13B2: +case 0x15B2: +case 0x17B2: +case 0x19B2: +case 0x1BB2: +case 0x1DB2: +case 0x1FB2: +case 0x11B3: +case 0x13B3: +case 0x15B3: +case 0x17B3: +case 0x19B3: +case 0x1BB3: +case 0x1DB3: +case 0x1FB3: +case 0x11B4: +case 0x13B4: +case 0x15B4: +case 0x17B4: +case 0x19B4: +case 0x1BB4: +case 0x1DB4: +case 0x1FB4: +case 0x11B5: +case 0x13B5: +case 0x15B5: +case 0x17B5: +case 0x19B5: +case 0x1BB5: +case 0x1DB5: +case 0x1FB5: +case 0x11B6: +case 0x13B6: +case 0x15B6: +case 0x17B6: +case 0x19B6: +case 0x1BB6: +case 0x1DB6: +case 0x1FB6: +case 0x11B7: +case 0x13B7: +case 0x15B7: +case 0x17B7: +case 0x19B7: +case 0x1BB7: +case 0x1DB7: +case 0x1FB7: + +// MOVEB +case 0x11B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) +case 0x11F1: +case 0x11F2: +case 0x11F3: +case 0x11F4: +case 0x11F5: +case 0x11F6: +case 0x11F7: + +// MOVEB +case 0x11F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) +case 0x13F1: +case 0x13F2: +case 0x13F3: +case 0x13F4: +case 0x13F5: +case 0x13F6: +case 0x13F7: + +// MOVEB +case 0x13F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(26) +case 0x1EF1: +case 0x1EF2: +case 0x1EF3: +case 0x1EF4: +case 0x1EF5: +case 0x1EF6: +case 0x1EF7: + +// MOVEB +case 0x1EF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x1F31: +case 0x1F32: +case 0x1F33: +case 0x1F34: +case 0x1F35: +case 0x1F36: +case 0x1F37: + +// MOVEB +case 0x1F30: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x1238: +case 0x1438: +case 0x1638: +case 0x1838: +case 0x1A38: +case 0x1C38: +case 0x1E38: + +// MOVEB +case 0x1038: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x12B8: +case 0x14B8: +case 0x16B8: +case 0x18B8: +case 0x1AB8: +case 0x1CB8: +case 0x1EB8: + +// MOVEB +case 0x10B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x12F8: +case 0x14F8: +case 0x16F8: +case 0x18F8: +case 0x1AF8: +case 0x1CF8: + +// MOVEB +case 0x10F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1338: +case 0x1538: +case 0x1738: +case 0x1938: +case 0x1B38: +case 0x1D38: + +// MOVEB +case 0x1138: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1378: +case 0x1578: +case 0x1778: +case 0x1978: +case 0x1B78: +case 0x1D78: +case 0x1F78: + +// MOVEB +case 0x1178: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x13B8: +case 0x15B8: +case 0x17B8: +case 0x19B8: +case 0x1BB8: +case 0x1DB8: +case 0x1FB8: + +// MOVEB +case 0x11B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// MOVEB +case 0x11F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x13F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// MOVEB +case 0x1EF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// MOVEB +case 0x1F38: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x1239: +case 0x1439: +case 0x1639: +case 0x1839: +case 0x1A39: +case 0x1C39: +case 0x1E39: + +// MOVEB +case 0x1039: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x12B9: +case 0x14B9: +case 0x16B9: +case 0x18B9: +case 0x1AB9: +case 0x1CB9: +case 0x1EB9: + +// MOVEB +case 0x10B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x12F9: +case 0x14F9: +case 0x16F9: +case 0x18F9: +case 0x1AF9: +case 0x1CF9: + +// MOVEB +case 0x10F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x1339: +case 0x1539: +case 0x1739: +case 0x1939: +case 0x1B39: +case 0x1D39: + +// MOVEB +case 0x1139: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x1379: +case 0x1579: +case 0x1779: +case 0x1979: +case 0x1B79: +case 0x1D79: +case 0x1F79: + +// MOVEB +case 0x1179: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) +case 0x13B9: +case 0x15B9: +case 0x17B9: +case 0x19B9: +case 0x1BB9: +case 0x1DB9: +case 0x1FB9: + +// MOVEB +case 0x11B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(26) + +// MOVEB +case 0x11F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// MOVEB +case 0x13F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(28) + +// MOVEB +case 0x1EF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x1F39: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x123A: +case 0x143A: +case 0x163A: +case 0x183A: +case 0x1A3A: +case 0x1C3A: +case 0x1E3A: + +// MOVEB +case 0x103A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x12BA: +case 0x14BA: +case 0x16BA: +case 0x18BA: +case 0x1ABA: +case 0x1CBA: +case 0x1EBA: + +// MOVEB +case 0x10BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x12FA: +case 0x14FA: +case 0x16FA: +case 0x18FA: +case 0x1AFA: +case 0x1CFA: + +// MOVEB +case 0x10FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x133A: +case 0x153A: +case 0x173A: +case 0x193A: +case 0x1B3A: +case 0x1D3A: + +// MOVEB +case 0x113A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x137A: +case 0x157A: +case 0x177A: +case 0x197A: +case 0x1B7A: +case 0x1D7A: +case 0x1F7A: + +// MOVEB +case 0x117A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x13BA: +case 0x15BA: +case 0x17BA: +case 0x19BA: +case 0x1BBA: +case 0x1DBA: +case 0x1FBA: + +// MOVEB +case 0x11BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// MOVEB +case 0x11FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x13FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// MOVEB +case 0x1EFA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// MOVEB +case 0x1F3A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x123B: +case 0x143B: +case 0x163B: +case 0x183B: +case 0x1A3B: +case 0x1C3B: +case 0x1E3B: + +// MOVEB +case 0x103B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x12BB: +case 0x14BB: +case 0x16BB: +case 0x18BB: +case 0x1ABB: +case 0x1CBB: +case 0x1EBB: + +// MOVEB +case 0x10BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x12FB: +case 0x14FB: +case 0x16FB: +case 0x18FB: +case 0x1AFB: +case 0x1CFB: + +// MOVEB +case 0x10FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x133B: +case 0x153B: +case 0x173B: +case 0x193B: +case 0x1B3B: +case 0x1D3B: + +// MOVEB +case 0x113B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x137B: +case 0x157B: +case 0x177B: +case 0x197B: +case 0x1B7B: +case 0x1D7B: +case 0x1F7B: + +// MOVEB +case 0x117B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) +case 0x13BB: +case 0x15BB: +case 0x17BB: +case 0x19BB: +case 0x1BBB: +case 0x1DBB: +case 0x1FBB: + +// MOVEB +case 0x11BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(24) + +// MOVEB +case 0x11FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// MOVEB +case 0x13FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(26) + +// MOVEB +case 0x1EFB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// MOVEB +case 0x1F3B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x123C: +case 0x143C: +case 0x163C: +case 0x183C: +case 0x1A3C: +case 0x1C3C: +case 0x1E3C: + +// MOVEB +case 0x103C: +{ + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x12BC: +case 0x14BC: +case 0x16BC: +case 0x18BC: +case 0x1ABC: +case 0x1CBC: +case 0x1EBC: + +// MOVEB +case 0x10BC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x12FC: +case 0x14FC: +case 0x16FC: +case 0x18FC: +case 0x1AFC: +case 0x1CFC: + +// MOVEB +case 0x10FC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x133C: +case 0x153C: +case 0x173C: +case 0x193C: +case 0x1B3C: +case 0x1D3C: + +// MOVEB +case 0x113C: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x137C: +case 0x157C: +case 0x177C: +case 0x197C: +case 0x1B7C: +case 0x1D7C: +case 0x1F7C: + +// MOVEB +case 0x117C: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x13BC: +case 0x15BC: +case 0x17BC: +case 0x19BC: +case 0x1BBC: +case 0x1DBC: +case 0x1FBC: + +// MOVEB +case 0x11BC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// MOVEB +case 0x11FC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// MOVEB +case 0x13FC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x1EFC: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// MOVEB +case 0x1F3C: +{ + u32 adr; + u32 res; + res = FETCH_BYTE; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x121F: +case 0x141F: +case 0x161F: +case 0x181F: +case 0x1A1F: +case 0x1C1F: +case 0x1E1F: + +// MOVEB +case 0x101F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x129F: +case 0x149F: +case 0x169F: +case 0x189F: +case 0x1A9F: +case 0x1C9F: +case 0x1E9F: + +// MOVEB +case 0x109F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x12DF: +case 0x14DF: +case 0x16DF: +case 0x18DF: +case 0x1ADF: +case 0x1CDF: + +// MOVEB +case 0x10DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x131F: +case 0x151F: +case 0x171F: +case 0x191F: +case 0x1B1F: +case 0x1D1F: + +// MOVEB +case 0x111F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x135F: +case 0x155F: +case 0x175F: +case 0x195F: +case 0x1B5F: +case 0x1D5F: +case 0x1F5F: + +// MOVEB +case 0x115F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x139F: +case 0x159F: +case 0x179F: +case 0x199F: +case 0x1B9F: +case 0x1D9F: +case 0x1F9F: + +// MOVEB +case 0x119F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// MOVEB +case 0x11DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// MOVEB +case 0x13DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x1EDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// MOVEB +case 0x1F1F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x1227: +case 0x1427: +case 0x1627: +case 0x1827: +case 0x1A27: +case 0x1C27: +case 0x1E27: + +// MOVEB +case 0x1027: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x12A7: +case 0x14A7: +case 0x16A7: +case 0x18A7: +case 0x1AA7: +case 0x1CA7: +case 0x1EA7: + +// MOVEB +case 0x10A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x12E7: +case 0x14E7: +case 0x16E7: +case 0x18E7: +case 0x1AE7: +case 0x1CE7: + +// MOVEB +case 0x10E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1327: +case 0x1527: +case 0x1727: +case 0x1927: +case 0x1B27: +case 0x1D27: + +// MOVEB +case 0x1127: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x1367: +case 0x1567: +case 0x1767: +case 0x1967: +case 0x1B67: +case 0x1D67: +case 0x1F67: + +// MOVEB +case 0x1167: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x13A7: +case 0x15A7: +case 0x17A7: +case 0x19A7: +case 0x1BA7: +case 0x1DA7: +case 0x1FA7: + +// MOVEB +case 0x11A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// MOVEB +case 0x11E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// MOVEB +case 0x13E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(22) + +// MOVEB +case 0x1EE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) + +// MOVEB +case 0x1F27: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) diff --git a/yabause/src/c68k/c68k_op2.inc b/yabause/src/c68k/c68k_op2.inc new file mode 100644 index 0000000000..3228cd2dc4 --- /dev/null +++ b/yabause/src/c68k/c68k_op2.inc @@ -0,0 +1,6254 @@ +case 0x2200: +case 0x2400: +case 0x2600: +case 0x2800: +case 0x2A00: +case 0x2C00: +case 0x2E00: +case 0x2001: +case 0x2201: +case 0x2401: +case 0x2601: +case 0x2801: +case 0x2A01: +case 0x2C01: +case 0x2E01: +case 0x2002: +case 0x2202: +case 0x2402: +case 0x2602: +case 0x2802: +case 0x2A02: +case 0x2C02: +case 0x2E02: +case 0x2003: +case 0x2203: +case 0x2403: +case 0x2603: +case 0x2803: +case 0x2A03: +case 0x2C03: +case 0x2E03: +case 0x2004: +case 0x2204: +case 0x2404: +case 0x2604: +case 0x2804: +case 0x2A04: +case 0x2C04: +case 0x2E04: +case 0x2005: +case 0x2205: +case 0x2405: +case 0x2605: +case 0x2805: +case 0x2A05: +case 0x2C05: +case 0x2E05: +case 0x2006: +case 0x2206: +case 0x2406: +case 0x2606: +case 0x2806: +case 0x2A06: +case 0x2C06: +case 0x2E06: +case 0x2007: +case 0x2207: +case 0x2407: +case 0x2607: +case 0x2807: +case 0x2A07: +case 0x2C07: +case 0x2E07: + +// MOVEL +case 0x2000: +{ + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x2280: +case 0x2480: +case 0x2680: +case 0x2880: +case 0x2A80: +case 0x2C80: +case 0x2E80: +case 0x2081: +case 0x2281: +case 0x2481: +case 0x2681: +case 0x2881: +case 0x2A81: +case 0x2C81: +case 0x2E81: +case 0x2082: +case 0x2282: +case 0x2482: +case 0x2682: +case 0x2882: +case 0x2A82: +case 0x2C82: +case 0x2E82: +case 0x2083: +case 0x2283: +case 0x2483: +case 0x2683: +case 0x2883: +case 0x2A83: +case 0x2C83: +case 0x2E83: +case 0x2084: +case 0x2284: +case 0x2484: +case 0x2684: +case 0x2884: +case 0x2A84: +case 0x2C84: +case 0x2E84: +case 0x2085: +case 0x2285: +case 0x2485: +case 0x2685: +case 0x2885: +case 0x2A85: +case 0x2C85: +case 0x2E85: +case 0x2086: +case 0x2286: +case 0x2486: +case 0x2686: +case 0x2886: +case 0x2A86: +case 0x2C86: +case 0x2E86: +case 0x2087: +case 0x2287: +case 0x2487: +case 0x2687: +case 0x2887: +case 0x2A87: +case 0x2C87: +case 0x2E87: + +// MOVEL +case 0x2080: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x22C0: +case 0x24C0: +case 0x26C0: +case 0x28C0: +case 0x2AC0: +case 0x2CC0: +case 0x20C1: +case 0x22C1: +case 0x24C1: +case 0x26C1: +case 0x28C1: +case 0x2AC1: +case 0x2CC1: +case 0x20C2: +case 0x22C2: +case 0x24C2: +case 0x26C2: +case 0x28C2: +case 0x2AC2: +case 0x2CC2: +case 0x20C3: +case 0x22C3: +case 0x24C3: +case 0x26C3: +case 0x28C3: +case 0x2AC3: +case 0x2CC3: +case 0x20C4: +case 0x22C4: +case 0x24C4: +case 0x26C4: +case 0x28C4: +case 0x2AC4: +case 0x2CC4: +case 0x20C5: +case 0x22C5: +case 0x24C5: +case 0x26C5: +case 0x28C5: +case 0x2AC5: +case 0x2CC5: +case 0x20C6: +case 0x22C6: +case 0x24C6: +case 0x26C6: +case 0x28C6: +case 0x2AC6: +case 0x2CC6: +case 0x20C7: +case 0x22C7: +case 0x24C7: +case 0x26C7: +case 0x28C7: +case 0x2AC7: +case 0x2CC7: + +// MOVEL +case 0x20C0: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2300: +case 0x2500: +case 0x2700: +case 0x2900: +case 0x2B00: +case 0x2D00: +case 0x2101: +case 0x2301: +case 0x2501: +case 0x2701: +case 0x2901: +case 0x2B01: +case 0x2D01: +case 0x2102: +case 0x2302: +case 0x2502: +case 0x2702: +case 0x2902: +case 0x2B02: +case 0x2D02: +case 0x2103: +case 0x2303: +case 0x2503: +case 0x2703: +case 0x2903: +case 0x2B03: +case 0x2D03: +case 0x2104: +case 0x2304: +case 0x2504: +case 0x2704: +case 0x2904: +case 0x2B04: +case 0x2D04: +case 0x2105: +case 0x2305: +case 0x2505: +case 0x2705: +case 0x2905: +case 0x2B05: +case 0x2D05: +case 0x2106: +case 0x2306: +case 0x2506: +case 0x2706: +case 0x2906: +case 0x2B06: +case 0x2D06: +case 0x2107: +case 0x2307: +case 0x2507: +case 0x2707: +case 0x2907: +case 0x2B07: +case 0x2D07: + +// MOVEL +case 0x2100: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2340: +case 0x2540: +case 0x2740: +case 0x2940: +case 0x2B40: +case 0x2D40: +case 0x2F40: +case 0x2141: +case 0x2341: +case 0x2541: +case 0x2741: +case 0x2941: +case 0x2B41: +case 0x2D41: +case 0x2F41: +case 0x2142: +case 0x2342: +case 0x2542: +case 0x2742: +case 0x2942: +case 0x2B42: +case 0x2D42: +case 0x2F42: +case 0x2143: +case 0x2343: +case 0x2543: +case 0x2743: +case 0x2943: +case 0x2B43: +case 0x2D43: +case 0x2F43: +case 0x2144: +case 0x2344: +case 0x2544: +case 0x2744: +case 0x2944: +case 0x2B44: +case 0x2D44: +case 0x2F44: +case 0x2145: +case 0x2345: +case 0x2545: +case 0x2745: +case 0x2945: +case 0x2B45: +case 0x2D45: +case 0x2F45: +case 0x2146: +case 0x2346: +case 0x2546: +case 0x2746: +case 0x2946: +case 0x2B46: +case 0x2D46: +case 0x2F46: +case 0x2147: +case 0x2347: +case 0x2547: +case 0x2747: +case 0x2947: +case 0x2B47: +case 0x2D47: +case 0x2F47: + +// MOVEL +case 0x2140: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(16) +case 0x2380: +case 0x2580: +case 0x2780: +case 0x2980: +case 0x2B80: +case 0x2D80: +case 0x2F80: +case 0x2181: +case 0x2381: +case 0x2581: +case 0x2781: +case 0x2981: +case 0x2B81: +case 0x2D81: +case 0x2F81: +case 0x2182: +case 0x2382: +case 0x2582: +case 0x2782: +case 0x2982: +case 0x2B82: +case 0x2D82: +case 0x2F82: +case 0x2183: +case 0x2383: +case 0x2583: +case 0x2783: +case 0x2983: +case 0x2B83: +case 0x2D83: +case 0x2F83: +case 0x2184: +case 0x2384: +case 0x2584: +case 0x2784: +case 0x2984: +case 0x2B84: +case 0x2D84: +case 0x2F84: +case 0x2185: +case 0x2385: +case 0x2585: +case 0x2785: +case 0x2985: +case 0x2B85: +case 0x2D85: +case 0x2F85: +case 0x2186: +case 0x2386: +case 0x2586: +case 0x2786: +case 0x2986: +case 0x2B86: +case 0x2D86: +case 0x2F86: +case 0x2187: +case 0x2387: +case 0x2587: +case 0x2787: +case 0x2987: +case 0x2B87: +case 0x2D87: +case 0x2F87: + +// MOVEL +case 0x2180: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(18) +case 0x21C1: +case 0x21C2: +case 0x21C3: +case 0x21C4: +case 0x21C5: +case 0x21C6: +case 0x21C7: + +// MOVEL +case 0x21C0: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(16) +case 0x23C1: +case 0x23C2: +case 0x23C3: +case 0x23C4: +case 0x23C5: +case 0x23C6: +case 0x23C7: + +// MOVEL +case 0x23C0: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2EC1: +case 0x2EC2: +case 0x2EC3: +case 0x2EC4: +case 0x2EC5: +case 0x2EC6: +case 0x2EC7: + +// MOVEL +case 0x2EC0: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2F01: +case 0x2F02: +case 0x2F03: +case 0x2F04: +case 0x2F05: +case 0x2F06: +case 0x2F07: + +// MOVEL +case 0x2F00: +{ + u32 adr; + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2208: +case 0x2408: +case 0x2608: +case 0x2808: +case 0x2A08: +case 0x2C08: +case 0x2E08: +case 0x2009: +case 0x2209: +case 0x2409: +case 0x2609: +case 0x2809: +case 0x2A09: +case 0x2C09: +case 0x2E09: +case 0x200A: +case 0x220A: +case 0x240A: +case 0x260A: +case 0x280A: +case 0x2A0A: +case 0x2C0A: +case 0x2E0A: +case 0x200B: +case 0x220B: +case 0x240B: +case 0x260B: +case 0x280B: +case 0x2A0B: +case 0x2C0B: +case 0x2E0B: +case 0x200C: +case 0x220C: +case 0x240C: +case 0x260C: +case 0x280C: +case 0x2A0C: +case 0x2C0C: +case 0x2E0C: +case 0x200D: +case 0x220D: +case 0x240D: +case 0x260D: +case 0x280D: +case 0x2A0D: +case 0x2C0D: +case 0x2E0D: +case 0x200E: +case 0x220E: +case 0x240E: +case 0x260E: +case 0x280E: +case 0x2A0E: +case 0x2C0E: +case 0x2E0E: +case 0x200F: +case 0x220F: +case 0x240F: +case 0x260F: +case 0x280F: +case 0x2A0F: +case 0x2C0F: +case 0x2E0F: + +// MOVEL +case 0x2008: +{ + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x2288: +case 0x2488: +case 0x2688: +case 0x2888: +case 0x2A88: +case 0x2C88: +case 0x2E88: +case 0x2089: +case 0x2289: +case 0x2489: +case 0x2689: +case 0x2889: +case 0x2A89: +case 0x2C89: +case 0x2E89: +case 0x208A: +case 0x228A: +case 0x248A: +case 0x268A: +case 0x288A: +case 0x2A8A: +case 0x2C8A: +case 0x2E8A: +case 0x208B: +case 0x228B: +case 0x248B: +case 0x268B: +case 0x288B: +case 0x2A8B: +case 0x2C8B: +case 0x2E8B: +case 0x208C: +case 0x228C: +case 0x248C: +case 0x268C: +case 0x288C: +case 0x2A8C: +case 0x2C8C: +case 0x2E8C: +case 0x208D: +case 0x228D: +case 0x248D: +case 0x268D: +case 0x288D: +case 0x2A8D: +case 0x2C8D: +case 0x2E8D: +case 0x208E: +case 0x228E: +case 0x248E: +case 0x268E: +case 0x288E: +case 0x2A8E: +case 0x2C8E: +case 0x2E8E: +case 0x208F: +case 0x228F: +case 0x248F: +case 0x268F: +case 0x288F: +case 0x2A8F: +case 0x2C8F: +case 0x2E8F: + +// MOVEL +case 0x2088: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x22C8: +case 0x24C8: +case 0x26C8: +case 0x28C8: +case 0x2AC8: +case 0x2CC8: +case 0x20C9: +case 0x22C9: +case 0x24C9: +case 0x26C9: +case 0x28C9: +case 0x2AC9: +case 0x2CC9: +case 0x20CA: +case 0x22CA: +case 0x24CA: +case 0x26CA: +case 0x28CA: +case 0x2ACA: +case 0x2CCA: +case 0x20CB: +case 0x22CB: +case 0x24CB: +case 0x26CB: +case 0x28CB: +case 0x2ACB: +case 0x2CCB: +case 0x20CC: +case 0x22CC: +case 0x24CC: +case 0x26CC: +case 0x28CC: +case 0x2ACC: +case 0x2CCC: +case 0x20CD: +case 0x22CD: +case 0x24CD: +case 0x26CD: +case 0x28CD: +case 0x2ACD: +case 0x2CCD: +case 0x20CE: +case 0x22CE: +case 0x24CE: +case 0x26CE: +case 0x28CE: +case 0x2ACE: +case 0x2CCE: +case 0x20CF: +case 0x22CF: +case 0x24CF: +case 0x26CF: +case 0x28CF: +case 0x2ACF: +case 0x2CCF: + +// MOVEL +case 0x20C8: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2308: +case 0x2508: +case 0x2708: +case 0x2908: +case 0x2B08: +case 0x2D08: +case 0x2109: +case 0x2309: +case 0x2509: +case 0x2709: +case 0x2909: +case 0x2B09: +case 0x2D09: +case 0x210A: +case 0x230A: +case 0x250A: +case 0x270A: +case 0x290A: +case 0x2B0A: +case 0x2D0A: +case 0x210B: +case 0x230B: +case 0x250B: +case 0x270B: +case 0x290B: +case 0x2B0B: +case 0x2D0B: +case 0x210C: +case 0x230C: +case 0x250C: +case 0x270C: +case 0x290C: +case 0x2B0C: +case 0x2D0C: +case 0x210D: +case 0x230D: +case 0x250D: +case 0x270D: +case 0x290D: +case 0x2B0D: +case 0x2D0D: +case 0x210E: +case 0x230E: +case 0x250E: +case 0x270E: +case 0x290E: +case 0x2B0E: +case 0x2D0E: +case 0x210F: +case 0x230F: +case 0x250F: +case 0x270F: +case 0x290F: +case 0x2B0F: +case 0x2D0F: + +// MOVEL +case 0x2108: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2348: +case 0x2548: +case 0x2748: +case 0x2948: +case 0x2B48: +case 0x2D48: +case 0x2F48: +case 0x2149: +case 0x2349: +case 0x2549: +case 0x2749: +case 0x2949: +case 0x2B49: +case 0x2D49: +case 0x2F49: +case 0x214A: +case 0x234A: +case 0x254A: +case 0x274A: +case 0x294A: +case 0x2B4A: +case 0x2D4A: +case 0x2F4A: +case 0x214B: +case 0x234B: +case 0x254B: +case 0x274B: +case 0x294B: +case 0x2B4B: +case 0x2D4B: +case 0x2F4B: +case 0x214C: +case 0x234C: +case 0x254C: +case 0x274C: +case 0x294C: +case 0x2B4C: +case 0x2D4C: +case 0x2F4C: +case 0x214D: +case 0x234D: +case 0x254D: +case 0x274D: +case 0x294D: +case 0x2B4D: +case 0x2D4D: +case 0x2F4D: +case 0x214E: +case 0x234E: +case 0x254E: +case 0x274E: +case 0x294E: +case 0x2B4E: +case 0x2D4E: +case 0x2F4E: +case 0x214F: +case 0x234F: +case 0x254F: +case 0x274F: +case 0x294F: +case 0x2B4F: +case 0x2D4F: +case 0x2F4F: + +// MOVEL +case 0x2148: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(16) +case 0x2388: +case 0x2588: +case 0x2788: +case 0x2988: +case 0x2B88: +case 0x2D88: +case 0x2F88: +case 0x2189: +case 0x2389: +case 0x2589: +case 0x2789: +case 0x2989: +case 0x2B89: +case 0x2D89: +case 0x2F89: +case 0x218A: +case 0x238A: +case 0x258A: +case 0x278A: +case 0x298A: +case 0x2B8A: +case 0x2D8A: +case 0x2F8A: +case 0x218B: +case 0x238B: +case 0x258B: +case 0x278B: +case 0x298B: +case 0x2B8B: +case 0x2D8B: +case 0x2F8B: +case 0x218C: +case 0x238C: +case 0x258C: +case 0x278C: +case 0x298C: +case 0x2B8C: +case 0x2D8C: +case 0x2F8C: +case 0x218D: +case 0x238D: +case 0x258D: +case 0x278D: +case 0x298D: +case 0x2B8D: +case 0x2D8D: +case 0x2F8D: +case 0x218E: +case 0x238E: +case 0x258E: +case 0x278E: +case 0x298E: +case 0x2B8E: +case 0x2D8E: +case 0x2F8E: +case 0x218F: +case 0x238F: +case 0x258F: +case 0x278F: +case 0x298F: +case 0x2B8F: +case 0x2D8F: +case 0x2F8F: + +// MOVEL +case 0x2188: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(18) +case 0x21C9: +case 0x21CA: +case 0x21CB: +case 0x21CC: +case 0x21CD: +case 0x21CE: +case 0x21CF: + +// MOVEL +case 0x21C8: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(16) +case 0x23C9: +case 0x23CA: +case 0x23CB: +case 0x23CC: +case 0x23CD: +case 0x23CE: +case 0x23CF: + +// MOVEL +case 0x23C8: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2EC9: +case 0x2ECA: +case 0x2ECB: +case 0x2ECC: +case 0x2ECD: +case 0x2ECE: +case 0x2ECF: + +// MOVEL +case 0x2EC8: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2F09: +case 0x2F0A: +case 0x2F0B: +case 0x2F0C: +case 0x2F0D: +case 0x2F0E: +case 0x2F0F: + +// MOVEL +case 0x2F08: +{ + u32 adr; + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(12) +case 0x2210: +case 0x2410: +case 0x2610: +case 0x2810: +case 0x2A10: +case 0x2C10: +case 0x2E10: +case 0x2011: +case 0x2211: +case 0x2411: +case 0x2611: +case 0x2811: +case 0x2A11: +case 0x2C11: +case 0x2E11: +case 0x2012: +case 0x2212: +case 0x2412: +case 0x2612: +case 0x2812: +case 0x2A12: +case 0x2C12: +case 0x2E12: +case 0x2013: +case 0x2213: +case 0x2413: +case 0x2613: +case 0x2813: +case 0x2A13: +case 0x2C13: +case 0x2E13: +case 0x2014: +case 0x2214: +case 0x2414: +case 0x2614: +case 0x2814: +case 0x2A14: +case 0x2C14: +case 0x2E14: +case 0x2015: +case 0x2215: +case 0x2415: +case 0x2615: +case 0x2815: +case 0x2A15: +case 0x2C15: +case 0x2E15: +case 0x2016: +case 0x2216: +case 0x2416: +case 0x2616: +case 0x2816: +case 0x2A16: +case 0x2C16: +case 0x2E16: +case 0x2017: +case 0x2217: +case 0x2417: +case 0x2617: +case 0x2817: +case 0x2A17: +case 0x2C17: +case 0x2E17: + +// MOVEL +case 0x2010: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x2290: +case 0x2490: +case 0x2690: +case 0x2890: +case 0x2A90: +case 0x2C90: +case 0x2E90: +case 0x2091: +case 0x2291: +case 0x2491: +case 0x2691: +case 0x2891: +case 0x2A91: +case 0x2C91: +case 0x2E91: +case 0x2092: +case 0x2292: +case 0x2492: +case 0x2692: +case 0x2892: +case 0x2A92: +case 0x2C92: +case 0x2E92: +case 0x2093: +case 0x2293: +case 0x2493: +case 0x2693: +case 0x2893: +case 0x2A93: +case 0x2C93: +case 0x2E93: +case 0x2094: +case 0x2294: +case 0x2494: +case 0x2694: +case 0x2894: +case 0x2A94: +case 0x2C94: +case 0x2E94: +case 0x2095: +case 0x2295: +case 0x2495: +case 0x2695: +case 0x2895: +case 0x2A95: +case 0x2C95: +case 0x2E95: +case 0x2096: +case 0x2296: +case 0x2496: +case 0x2696: +case 0x2896: +case 0x2A96: +case 0x2C96: +case 0x2E96: +case 0x2097: +case 0x2297: +case 0x2497: +case 0x2697: +case 0x2897: +case 0x2A97: +case 0x2C97: +case 0x2E97: + +// MOVEL +case 0x2090: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x22D0: +case 0x24D0: +case 0x26D0: +case 0x28D0: +case 0x2AD0: +case 0x2CD0: +case 0x20D1: +case 0x22D1: +case 0x24D1: +case 0x26D1: +case 0x28D1: +case 0x2AD1: +case 0x2CD1: +case 0x20D2: +case 0x22D2: +case 0x24D2: +case 0x26D2: +case 0x28D2: +case 0x2AD2: +case 0x2CD2: +case 0x20D3: +case 0x22D3: +case 0x24D3: +case 0x26D3: +case 0x28D3: +case 0x2AD3: +case 0x2CD3: +case 0x20D4: +case 0x22D4: +case 0x24D4: +case 0x26D4: +case 0x28D4: +case 0x2AD4: +case 0x2CD4: +case 0x20D5: +case 0x22D5: +case 0x24D5: +case 0x26D5: +case 0x28D5: +case 0x2AD5: +case 0x2CD5: +case 0x20D6: +case 0x22D6: +case 0x24D6: +case 0x26D6: +case 0x28D6: +case 0x2AD6: +case 0x2CD6: +case 0x20D7: +case 0x22D7: +case 0x24D7: +case 0x26D7: +case 0x28D7: +case 0x2AD7: +case 0x2CD7: + +// MOVEL +case 0x20D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2310: +case 0x2510: +case 0x2710: +case 0x2910: +case 0x2B10: +case 0x2D10: +case 0x2111: +case 0x2311: +case 0x2511: +case 0x2711: +case 0x2911: +case 0x2B11: +case 0x2D11: +case 0x2112: +case 0x2312: +case 0x2512: +case 0x2712: +case 0x2912: +case 0x2B12: +case 0x2D12: +case 0x2113: +case 0x2313: +case 0x2513: +case 0x2713: +case 0x2913: +case 0x2B13: +case 0x2D13: +case 0x2114: +case 0x2314: +case 0x2514: +case 0x2714: +case 0x2914: +case 0x2B14: +case 0x2D14: +case 0x2115: +case 0x2315: +case 0x2515: +case 0x2715: +case 0x2915: +case 0x2B15: +case 0x2D15: +case 0x2116: +case 0x2316: +case 0x2516: +case 0x2716: +case 0x2916: +case 0x2B16: +case 0x2D16: +case 0x2117: +case 0x2317: +case 0x2517: +case 0x2717: +case 0x2917: +case 0x2B17: +case 0x2D17: + +// MOVEL +case 0x2110: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2350: +case 0x2550: +case 0x2750: +case 0x2950: +case 0x2B50: +case 0x2D50: +case 0x2F50: +case 0x2151: +case 0x2351: +case 0x2551: +case 0x2751: +case 0x2951: +case 0x2B51: +case 0x2D51: +case 0x2F51: +case 0x2152: +case 0x2352: +case 0x2552: +case 0x2752: +case 0x2952: +case 0x2B52: +case 0x2D52: +case 0x2F52: +case 0x2153: +case 0x2353: +case 0x2553: +case 0x2753: +case 0x2953: +case 0x2B53: +case 0x2D53: +case 0x2F53: +case 0x2154: +case 0x2354: +case 0x2554: +case 0x2754: +case 0x2954: +case 0x2B54: +case 0x2D54: +case 0x2F54: +case 0x2155: +case 0x2355: +case 0x2555: +case 0x2755: +case 0x2955: +case 0x2B55: +case 0x2D55: +case 0x2F55: +case 0x2156: +case 0x2356: +case 0x2556: +case 0x2756: +case 0x2956: +case 0x2B56: +case 0x2D56: +case 0x2F56: +case 0x2157: +case 0x2357: +case 0x2557: +case 0x2757: +case 0x2957: +case 0x2B57: +case 0x2D57: +case 0x2F57: + +// MOVEL +case 0x2150: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2390: +case 0x2590: +case 0x2790: +case 0x2990: +case 0x2B90: +case 0x2D90: +case 0x2F90: +case 0x2191: +case 0x2391: +case 0x2591: +case 0x2791: +case 0x2991: +case 0x2B91: +case 0x2D91: +case 0x2F91: +case 0x2192: +case 0x2392: +case 0x2592: +case 0x2792: +case 0x2992: +case 0x2B92: +case 0x2D92: +case 0x2F92: +case 0x2193: +case 0x2393: +case 0x2593: +case 0x2793: +case 0x2993: +case 0x2B93: +case 0x2D93: +case 0x2F93: +case 0x2194: +case 0x2394: +case 0x2594: +case 0x2794: +case 0x2994: +case 0x2B94: +case 0x2D94: +case 0x2F94: +case 0x2195: +case 0x2395: +case 0x2595: +case 0x2795: +case 0x2995: +case 0x2B95: +case 0x2D95: +case 0x2F95: +case 0x2196: +case 0x2396: +case 0x2596: +case 0x2796: +case 0x2996: +case 0x2B96: +case 0x2D96: +case 0x2F96: +case 0x2197: +case 0x2397: +case 0x2597: +case 0x2797: +case 0x2997: +case 0x2B97: +case 0x2D97: +case 0x2F97: + +// MOVEL +case 0x2190: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x21D1: +case 0x21D2: +case 0x21D3: +case 0x21D4: +case 0x21D5: +case 0x21D6: +case 0x21D7: + +// MOVEL +case 0x21D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x23D1: +case 0x23D2: +case 0x23D3: +case 0x23D4: +case 0x23D5: +case 0x23D6: +case 0x23D7: + +// MOVEL +case 0x23D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x2ED1: +case 0x2ED2: +case 0x2ED3: +case 0x2ED4: +case 0x2ED5: +case 0x2ED6: +case 0x2ED7: + +// MOVEL +case 0x2ED0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2F11: +case 0x2F12: +case 0x2F13: +case 0x2F14: +case 0x2F15: +case 0x2F16: +case 0x2F17: + +// MOVEL +case 0x2F10: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2218: +case 0x2418: +case 0x2618: +case 0x2818: +case 0x2A18: +case 0x2C18: +case 0x2E18: +case 0x2019: +case 0x2219: +case 0x2419: +case 0x2619: +case 0x2819: +case 0x2A19: +case 0x2C19: +case 0x2E19: +case 0x201A: +case 0x221A: +case 0x241A: +case 0x261A: +case 0x281A: +case 0x2A1A: +case 0x2C1A: +case 0x2E1A: +case 0x201B: +case 0x221B: +case 0x241B: +case 0x261B: +case 0x281B: +case 0x2A1B: +case 0x2C1B: +case 0x2E1B: +case 0x201C: +case 0x221C: +case 0x241C: +case 0x261C: +case 0x281C: +case 0x2A1C: +case 0x2C1C: +case 0x2E1C: +case 0x201D: +case 0x221D: +case 0x241D: +case 0x261D: +case 0x281D: +case 0x2A1D: +case 0x2C1D: +case 0x2E1D: +case 0x201E: +case 0x221E: +case 0x241E: +case 0x261E: +case 0x281E: +case 0x2A1E: +case 0x2C1E: +case 0x2E1E: + +// MOVEL +case 0x2018: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x2298: +case 0x2498: +case 0x2698: +case 0x2898: +case 0x2A98: +case 0x2C98: +case 0x2E98: +case 0x2099: +case 0x2299: +case 0x2499: +case 0x2699: +case 0x2899: +case 0x2A99: +case 0x2C99: +case 0x2E99: +case 0x209A: +case 0x229A: +case 0x249A: +case 0x269A: +case 0x289A: +case 0x2A9A: +case 0x2C9A: +case 0x2E9A: +case 0x209B: +case 0x229B: +case 0x249B: +case 0x269B: +case 0x289B: +case 0x2A9B: +case 0x2C9B: +case 0x2E9B: +case 0x209C: +case 0x229C: +case 0x249C: +case 0x269C: +case 0x289C: +case 0x2A9C: +case 0x2C9C: +case 0x2E9C: +case 0x209D: +case 0x229D: +case 0x249D: +case 0x269D: +case 0x289D: +case 0x2A9D: +case 0x2C9D: +case 0x2E9D: +case 0x209E: +case 0x229E: +case 0x249E: +case 0x269E: +case 0x289E: +case 0x2A9E: +case 0x2C9E: +case 0x2E9E: + +// MOVEL +case 0x2098: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x22D8: +case 0x24D8: +case 0x26D8: +case 0x28D8: +case 0x2AD8: +case 0x2CD8: +case 0x20D9: +case 0x22D9: +case 0x24D9: +case 0x26D9: +case 0x28D9: +case 0x2AD9: +case 0x2CD9: +case 0x20DA: +case 0x22DA: +case 0x24DA: +case 0x26DA: +case 0x28DA: +case 0x2ADA: +case 0x2CDA: +case 0x20DB: +case 0x22DB: +case 0x24DB: +case 0x26DB: +case 0x28DB: +case 0x2ADB: +case 0x2CDB: +case 0x20DC: +case 0x22DC: +case 0x24DC: +case 0x26DC: +case 0x28DC: +case 0x2ADC: +case 0x2CDC: +case 0x20DD: +case 0x22DD: +case 0x24DD: +case 0x26DD: +case 0x28DD: +case 0x2ADD: +case 0x2CDD: +case 0x20DE: +case 0x22DE: +case 0x24DE: +case 0x26DE: +case 0x28DE: +case 0x2ADE: +case 0x2CDE: + +// MOVEL +case 0x20D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2318: +case 0x2518: +case 0x2718: +case 0x2918: +case 0x2B18: +case 0x2D18: +case 0x2119: +case 0x2319: +case 0x2519: +case 0x2719: +case 0x2919: +case 0x2B19: +case 0x2D19: +case 0x211A: +case 0x231A: +case 0x251A: +case 0x271A: +case 0x291A: +case 0x2B1A: +case 0x2D1A: +case 0x211B: +case 0x231B: +case 0x251B: +case 0x271B: +case 0x291B: +case 0x2B1B: +case 0x2D1B: +case 0x211C: +case 0x231C: +case 0x251C: +case 0x271C: +case 0x291C: +case 0x2B1C: +case 0x2D1C: +case 0x211D: +case 0x231D: +case 0x251D: +case 0x271D: +case 0x291D: +case 0x2B1D: +case 0x2D1D: +case 0x211E: +case 0x231E: +case 0x251E: +case 0x271E: +case 0x291E: +case 0x2B1E: +case 0x2D1E: + +// MOVEL +case 0x2118: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2358: +case 0x2558: +case 0x2758: +case 0x2958: +case 0x2B58: +case 0x2D58: +case 0x2F58: +case 0x2159: +case 0x2359: +case 0x2559: +case 0x2759: +case 0x2959: +case 0x2B59: +case 0x2D59: +case 0x2F59: +case 0x215A: +case 0x235A: +case 0x255A: +case 0x275A: +case 0x295A: +case 0x2B5A: +case 0x2D5A: +case 0x2F5A: +case 0x215B: +case 0x235B: +case 0x255B: +case 0x275B: +case 0x295B: +case 0x2B5B: +case 0x2D5B: +case 0x2F5B: +case 0x215C: +case 0x235C: +case 0x255C: +case 0x275C: +case 0x295C: +case 0x2B5C: +case 0x2D5C: +case 0x2F5C: +case 0x215D: +case 0x235D: +case 0x255D: +case 0x275D: +case 0x295D: +case 0x2B5D: +case 0x2D5D: +case 0x2F5D: +case 0x215E: +case 0x235E: +case 0x255E: +case 0x275E: +case 0x295E: +case 0x2B5E: +case 0x2D5E: +case 0x2F5E: + +// MOVEL +case 0x2158: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2398: +case 0x2598: +case 0x2798: +case 0x2998: +case 0x2B98: +case 0x2D98: +case 0x2F98: +case 0x2199: +case 0x2399: +case 0x2599: +case 0x2799: +case 0x2999: +case 0x2B99: +case 0x2D99: +case 0x2F99: +case 0x219A: +case 0x239A: +case 0x259A: +case 0x279A: +case 0x299A: +case 0x2B9A: +case 0x2D9A: +case 0x2F9A: +case 0x219B: +case 0x239B: +case 0x259B: +case 0x279B: +case 0x299B: +case 0x2B9B: +case 0x2D9B: +case 0x2F9B: +case 0x219C: +case 0x239C: +case 0x259C: +case 0x279C: +case 0x299C: +case 0x2B9C: +case 0x2D9C: +case 0x2F9C: +case 0x219D: +case 0x239D: +case 0x259D: +case 0x279D: +case 0x299D: +case 0x2B9D: +case 0x2D9D: +case 0x2F9D: +case 0x219E: +case 0x239E: +case 0x259E: +case 0x279E: +case 0x299E: +case 0x2B9E: +case 0x2D9E: +case 0x2F9E: + +// MOVEL +case 0x2198: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x21D9: +case 0x21DA: +case 0x21DB: +case 0x21DC: +case 0x21DD: +case 0x21DE: + +// MOVEL +case 0x21D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x23D9: +case 0x23DA: +case 0x23DB: +case 0x23DC: +case 0x23DD: +case 0x23DE: + +// MOVEL +case 0x23D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x2ED9: +case 0x2EDA: +case 0x2EDB: +case 0x2EDC: +case 0x2EDD: +case 0x2EDE: + +// MOVEL +case 0x2ED8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2F19: +case 0x2F1A: +case 0x2F1B: +case 0x2F1C: +case 0x2F1D: +case 0x2F1E: + +// MOVEL +case 0x2F18: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2220: +case 0x2420: +case 0x2620: +case 0x2820: +case 0x2A20: +case 0x2C20: +case 0x2E20: +case 0x2021: +case 0x2221: +case 0x2421: +case 0x2621: +case 0x2821: +case 0x2A21: +case 0x2C21: +case 0x2E21: +case 0x2022: +case 0x2222: +case 0x2422: +case 0x2622: +case 0x2822: +case 0x2A22: +case 0x2C22: +case 0x2E22: +case 0x2023: +case 0x2223: +case 0x2423: +case 0x2623: +case 0x2823: +case 0x2A23: +case 0x2C23: +case 0x2E23: +case 0x2024: +case 0x2224: +case 0x2424: +case 0x2624: +case 0x2824: +case 0x2A24: +case 0x2C24: +case 0x2E24: +case 0x2025: +case 0x2225: +case 0x2425: +case 0x2625: +case 0x2825: +case 0x2A25: +case 0x2C25: +case 0x2E25: +case 0x2026: +case 0x2226: +case 0x2426: +case 0x2626: +case 0x2826: +case 0x2A26: +case 0x2C26: +case 0x2E26: + +// MOVEL +case 0x2020: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x22A0: +case 0x24A0: +case 0x26A0: +case 0x28A0: +case 0x2AA0: +case 0x2CA0: +case 0x2EA0: +case 0x20A1: +case 0x22A1: +case 0x24A1: +case 0x26A1: +case 0x28A1: +case 0x2AA1: +case 0x2CA1: +case 0x2EA1: +case 0x20A2: +case 0x22A2: +case 0x24A2: +case 0x26A2: +case 0x28A2: +case 0x2AA2: +case 0x2CA2: +case 0x2EA2: +case 0x20A3: +case 0x22A3: +case 0x24A3: +case 0x26A3: +case 0x28A3: +case 0x2AA3: +case 0x2CA3: +case 0x2EA3: +case 0x20A4: +case 0x22A4: +case 0x24A4: +case 0x26A4: +case 0x28A4: +case 0x2AA4: +case 0x2CA4: +case 0x2EA4: +case 0x20A5: +case 0x22A5: +case 0x24A5: +case 0x26A5: +case 0x28A5: +case 0x2AA5: +case 0x2CA5: +case 0x2EA5: +case 0x20A6: +case 0x22A6: +case 0x24A6: +case 0x26A6: +case 0x28A6: +case 0x2AA6: +case 0x2CA6: +case 0x2EA6: + +// MOVEL +case 0x20A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x22E0: +case 0x24E0: +case 0x26E0: +case 0x28E0: +case 0x2AE0: +case 0x2CE0: +case 0x20E1: +case 0x22E1: +case 0x24E1: +case 0x26E1: +case 0x28E1: +case 0x2AE1: +case 0x2CE1: +case 0x20E2: +case 0x22E2: +case 0x24E2: +case 0x26E2: +case 0x28E2: +case 0x2AE2: +case 0x2CE2: +case 0x20E3: +case 0x22E3: +case 0x24E3: +case 0x26E3: +case 0x28E3: +case 0x2AE3: +case 0x2CE3: +case 0x20E4: +case 0x22E4: +case 0x24E4: +case 0x26E4: +case 0x28E4: +case 0x2AE4: +case 0x2CE4: +case 0x20E5: +case 0x22E5: +case 0x24E5: +case 0x26E5: +case 0x28E5: +case 0x2AE5: +case 0x2CE5: +case 0x20E6: +case 0x22E6: +case 0x24E6: +case 0x26E6: +case 0x28E6: +case 0x2AE6: +case 0x2CE6: + +// MOVEL +case 0x20E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2320: +case 0x2520: +case 0x2720: +case 0x2920: +case 0x2B20: +case 0x2D20: +case 0x2121: +case 0x2321: +case 0x2521: +case 0x2721: +case 0x2921: +case 0x2B21: +case 0x2D21: +case 0x2122: +case 0x2322: +case 0x2522: +case 0x2722: +case 0x2922: +case 0x2B22: +case 0x2D22: +case 0x2123: +case 0x2323: +case 0x2523: +case 0x2723: +case 0x2923: +case 0x2B23: +case 0x2D23: +case 0x2124: +case 0x2324: +case 0x2524: +case 0x2724: +case 0x2924: +case 0x2B24: +case 0x2D24: +case 0x2125: +case 0x2325: +case 0x2525: +case 0x2725: +case 0x2925: +case 0x2B25: +case 0x2D25: +case 0x2126: +case 0x2326: +case 0x2526: +case 0x2726: +case 0x2926: +case 0x2B26: +case 0x2D26: + +// MOVEL +case 0x2120: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2360: +case 0x2560: +case 0x2760: +case 0x2960: +case 0x2B60: +case 0x2D60: +case 0x2F60: +case 0x2161: +case 0x2361: +case 0x2561: +case 0x2761: +case 0x2961: +case 0x2B61: +case 0x2D61: +case 0x2F61: +case 0x2162: +case 0x2362: +case 0x2562: +case 0x2762: +case 0x2962: +case 0x2B62: +case 0x2D62: +case 0x2F62: +case 0x2163: +case 0x2363: +case 0x2563: +case 0x2763: +case 0x2963: +case 0x2B63: +case 0x2D63: +case 0x2F63: +case 0x2164: +case 0x2364: +case 0x2564: +case 0x2764: +case 0x2964: +case 0x2B64: +case 0x2D64: +case 0x2F64: +case 0x2165: +case 0x2365: +case 0x2565: +case 0x2765: +case 0x2965: +case 0x2B65: +case 0x2D65: +case 0x2F65: +case 0x2166: +case 0x2366: +case 0x2566: +case 0x2766: +case 0x2966: +case 0x2B66: +case 0x2D66: +case 0x2F66: + +// MOVEL +case 0x2160: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x23A0: +case 0x25A0: +case 0x27A0: +case 0x29A0: +case 0x2BA0: +case 0x2DA0: +case 0x2FA0: +case 0x21A1: +case 0x23A1: +case 0x25A1: +case 0x27A1: +case 0x29A1: +case 0x2BA1: +case 0x2DA1: +case 0x2FA1: +case 0x21A2: +case 0x23A2: +case 0x25A2: +case 0x27A2: +case 0x29A2: +case 0x2BA2: +case 0x2DA2: +case 0x2FA2: +case 0x21A3: +case 0x23A3: +case 0x25A3: +case 0x27A3: +case 0x29A3: +case 0x2BA3: +case 0x2DA3: +case 0x2FA3: +case 0x21A4: +case 0x23A4: +case 0x25A4: +case 0x27A4: +case 0x29A4: +case 0x2BA4: +case 0x2DA4: +case 0x2FA4: +case 0x21A5: +case 0x23A5: +case 0x25A5: +case 0x27A5: +case 0x29A5: +case 0x2BA5: +case 0x2DA5: +case 0x2FA5: +case 0x21A6: +case 0x23A6: +case 0x25A6: +case 0x27A6: +case 0x29A6: +case 0x2BA6: +case 0x2DA6: +case 0x2FA6: + +// MOVEL +case 0x21A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x21E1: +case 0x21E2: +case 0x21E3: +case 0x21E4: +case 0x21E5: +case 0x21E6: + +// MOVEL +case 0x21E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x23E1: +case 0x23E2: +case 0x23E3: +case 0x23E4: +case 0x23E5: +case 0x23E6: + +// MOVEL +case 0x23E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x2EE1: +case 0x2EE2: +case 0x2EE3: +case 0x2EE4: +case 0x2EE5: +case 0x2EE6: + +// MOVEL +case 0x2EE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2F21: +case 0x2F22: +case 0x2F23: +case 0x2F24: +case 0x2F25: +case 0x2F26: + +// MOVEL +case 0x2F20: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2228: +case 0x2428: +case 0x2628: +case 0x2828: +case 0x2A28: +case 0x2C28: +case 0x2E28: +case 0x2029: +case 0x2229: +case 0x2429: +case 0x2629: +case 0x2829: +case 0x2A29: +case 0x2C29: +case 0x2E29: +case 0x202A: +case 0x222A: +case 0x242A: +case 0x262A: +case 0x282A: +case 0x2A2A: +case 0x2C2A: +case 0x2E2A: +case 0x202B: +case 0x222B: +case 0x242B: +case 0x262B: +case 0x282B: +case 0x2A2B: +case 0x2C2B: +case 0x2E2B: +case 0x202C: +case 0x222C: +case 0x242C: +case 0x262C: +case 0x282C: +case 0x2A2C: +case 0x2C2C: +case 0x2E2C: +case 0x202D: +case 0x222D: +case 0x242D: +case 0x262D: +case 0x282D: +case 0x2A2D: +case 0x2C2D: +case 0x2E2D: +case 0x202E: +case 0x222E: +case 0x242E: +case 0x262E: +case 0x282E: +case 0x2A2E: +case 0x2C2E: +case 0x2E2E: +case 0x202F: +case 0x222F: +case 0x242F: +case 0x262F: +case 0x282F: +case 0x2A2F: +case 0x2C2F: +case 0x2E2F: + +// MOVEL +case 0x2028: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x22A8: +case 0x24A8: +case 0x26A8: +case 0x28A8: +case 0x2AA8: +case 0x2CA8: +case 0x2EA8: +case 0x20A9: +case 0x22A9: +case 0x24A9: +case 0x26A9: +case 0x28A9: +case 0x2AA9: +case 0x2CA9: +case 0x2EA9: +case 0x20AA: +case 0x22AA: +case 0x24AA: +case 0x26AA: +case 0x28AA: +case 0x2AAA: +case 0x2CAA: +case 0x2EAA: +case 0x20AB: +case 0x22AB: +case 0x24AB: +case 0x26AB: +case 0x28AB: +case 0x2AAB: +case 0x2CAB: +case 0x2EAB: +case 0x20AC: +case 0x22AC: +case 0x24AC: +case 0x26AC: +case 0x28AC: +case 0x2AAC: +case 0x2CAC: +case 0x2EAC: +case 0x20AD: +case 0x22AD: +case 0x24AD: +case 0x26AD: +case 0x28AD: +case 0x2AAD: +case 0x2CAD: +case 0x2EAD: +case 0x20AE: +case 0x22AE: +case 0x24AE: +case 0x26AE: +case 0x28AE: +case 0x2AAE: +case 0x2CAE: +case 0x2EAE: +case 0x20AF: +case 0x22AF: +case 0x24AF: +case 0x26AF: +case 0x28AF: +case 0x2AAF: +case 0x2CAF: +case 0x2EAF: + +// MOVEL +case 0x20A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x22E8: +case 0x24E8: +case 0x26E8: +case 0x28E8: +case 0x2AE8: +case 0x2CE8: +case 0x20E9: +case 0x22E9: +case 0x24E9: +case 0x26E9: +case 0x28E9: +case 0x2AE9: +case 0x2CE9: +case 0x20EA: +case 0x22EA: +case 0x24EA: +case 0x26EA: +case 0x28EA: +case 0x2AEA: +case 0x2CEA: +case 0x20EB: +case 0x22EB: +case 0x24EB: +case 0x26EB: +case 0x28EB: +case 0x2AEB: +case 0x2CEB: +case 0x20EC: +case 0x22EC: +case 0x24EC: +case 0x26EC: +case 0x28EC: +case 0x2AEC: +case 0x2CEC: +case 0x20ED: +case 0x22ED: +case 0x24ED: +case 0x26ED: +case 0x28ED: +case 0x2AED: +case 0x2CED: +case 0x20EE: +case 0x22EE: +case 0x24EE: +case 0x26EE: +case 0x28EE: +case 0x2AEE: +case 0x2CEE: +case 0x20EF: +case 0x22EF: +case 0x24EF: +case 0x26EF: +case 0x28EF: +case 0x2AEF: +case 0x2CEF: + +// MOVEL +case 0x20E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2328: +case 0x2528: +case 0x2728: +case 0x2928: +case 0x2B28: +case 0x2D28: +case 0x2129: +case 0x2329: +case 0x2529: +case 0x2729: +case 0x2929: +case 0x2B29: +case 0x2D29: +case 0x212A: +case 0x232A: +case 0x252A: +case 0x272A: +case 0x292A: +case 0x2B2A: +case 0x2D2A: +case 0x212B: +case 0x232B: +case 0x252B: +case 0x272B: +case 0x292B: +case 0x2B2B: +case 0x2D2B: +case 0x212C: +case 0x232C: +case 0x252C: +case 0x272C: +case 0x292C: +case 0x2B2C: +case 0x2D2C: +case 0x212D: +case 0x232D: +case 0x252D: +case 0x272D: +case 0x292D: +case 0x2B2D: +case 0x2D2D: +case 0x212E: +case 0x232E: +case 0x252E: +case 0x272E: +case 0x292E: +case 0x2B2E: +case 0x2D2E: +case 0x212F: +case 0x232F: +case 0x252F: +case 0x272F: +case 0x292F: +case 0x2B2F: +case 0x2D2F: + +// MOVEL +case 0x2128: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2368: +case 0x2568: +case 0x2768: +case 0x2968: +case 0x2B68: +case 0x2D68: +case 0x2F68: +case 0x2169: +case 0x2369: +case 0x2569: +case 0x2769: +case 0x2969: +case 0x2B69: +case 0x2D69: +case 0x2F69: +case 0x216A: +case 0x236A: +case 0x256A: +case 0x276A: +case 0x296A: +case 0x2B6A: +case 0x2D6A: +case 0x2F6A: +case 0x216B: +case 0x236B: +case 0x256B: +case 0x276B: +case 0x296B: +case 0x2B6B: +case 0x2D6B: +case 0x2F6B: +case 0x216C: +case 0x236C: +case 0x256C: +case 0x276C: +case 0x296C: +case 0x2B6C: +case 0x2D6C: +case 0x2F6C: +case 0x216D: +case 0x236D: +case 0x256D: +case 0x276D: +case 0x296D: +case 0x2B6D: +case 0x2D6D: +case 0x2F6D: +case 0x216E: +case 0x236E: +case 0x256E: +case 0x276E: +case 0x296E: +case 0x2B6E: +case 0x2D6E: +case 0x2F6E: +case 0x216F: +case 0x236F: +case 0x256F: +case 0x276F: +case 0x296F: +case 0x2B6F: +case 0x2D6F: +case 0x2F6F: + +// MOVEL +case 0x2168: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x23A8: +case 0x25A8: +case 0x27A8: +case 0x29A8: +case 0x2BA8: +case 0x2DA8: +case 0x2FA8: +case 0x21A9: +case 0x23A9: +case 0x25A9: +case 0x27A9: +case 0x29A9: +case 0x2BA9: +case 0x2DA9: +case 0x2FA9: +case 0x21AA: +case 0x23AA: +case 0x25AA: +case 0x27AA: +case 0x29AA: +case 0x2BAA: +case 0x2DAA: +case 0x2FAA: +case 0x21AB: +case 0x23AB: +case 0x25AB: +case 0x27AB: +case 0x29AB: +case 0x2BAB: +case 0x2DAB: +case 0x2FAB: +case 0x21AC: +case 0x23AC: +case 0x25AC: +case 0x27AC: +case 0x29AC: +case 0x2BAC: +case 0x2DAC: +case 0x2FAC: +case 0x21AD: +case 0x23AD: +case 0x25AD: +case 0x27AD: +case 0x29AD: +case 0x2BAD: +case 0x2DAD: +case 0x2FAD: +case 0x21AE: +case 0x23AE: +case 0x25AE: +case 0x27AE: +case 0x29AE: +case 0x2BAE: +case 0x2DAE: +case 0x2FAE: +case 0x21AF: +case 0x23AF: +case 0x25AF: +case 0x27AF: +case 0x29AF: +case 0x2BAF: +case 0x2DAF: +case 0x2FAF: + +// MOVEL +case 0x21A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x21E9: +case 0x21EA: +case 0x21EB: +case 0x21EC: +case 0x21ED: +case 0x21EE: +case 0x21EF: + +// MOVEL +case 0x21E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x23E9: +case 0x23EA: +case 0x23EB: +case 0x23EC: +case 0x23ED: +case 0x23EE: +case 0x23EF: + +// MOVEL +case 0x23E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x2EE9: +case 0x2EEA: +case 0x2EEB: +case 0x2EEC: +case 0x2EED: +case 0x2EEE: +case 0x2EEF: + +// MOVEL +case 0x2EE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2F29: +case 0x2F2A: +case 0x2F2B: +case 0x2F2C: +case 0x2F2D: +case 0x2F2E: +case 0x2F2F: + +// MOVEL +case 0x2F28: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2230: +case 0x2430: +case 0x2630: +case 0x2830: +case 0x2A30: +case 0x2C30: +case 0x2E30: +case 0x2031: +case 0x2231: +case 0x2431: +case 0x2631: +case 0x2831: +case 0x2A31: +case 0x2C31: +case 0x2E31: +case 0x2032: +case 0x2232: +case 0x2432: +case 0x2632: +case 0x2832: +case 0x2A32: +case 0x2C32: +case 0x2E32: +case 0x2033: +case 0x2233: +case 0x2433: +case 0x2633: +case 0x2833: +case 0x2A33: +case 0x2C33: +case 0x2E33: +case 0x2034: +case 0x2234: +case 0x2434: +case 0x2634: +case 0x2834: +case 0x2A34: +case 0x2C34: +case 0x2E34: +case 0x2035: +case 0x2235: +case 0x2435: +case 0x2635: +case 0x2835: +case 0x2A35: +case 0x2C35: +case 0x2E35: +case 0x2036: +case 0x2236: +case 0x2436: +case 0x2636: +case 0x2836: +case 0x2A36: +case 0x2C36: +case 0x2E36: +case 0x2037: +case 0x2237: +case 0x2437: +case 0x2637: +case 0x2837: +case 0x2A37: +case 0x2C37: +case 0x2E37: + +// MOVEL +case 0x2030: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x22B0: +case 0x24B0: +case 0x26B0: +case 0x28B0: +case 0x2AB0: +case 0x2CB0: +case 0x2EB0: +case 0x20B1: +case 0x22B1: +case 0x24B1: +case 0x26B1: +case 0x28B1: +case 0x2AB1: +case 0x2CB1: +case 0x2EB1: +case 0x20B2: +case 0x22B2: +case 0x24B2: +case 0x26B2: +case 0x28B2: +case 0x2AB2: +case 0x2CB2: +case 0x2EB2: +case 0x20B3: +case 0x22B3: +case 0x24B3: +case 0x26B3: +case 0x28B3: +case 0x2AB3: +case 0x2CB3: +case 0x2EB3: +case 0x20B4: +case 0x22B4: +case 0x24B4: +case 0x26B4: +case 0x28B4: +case 0x2AB4: +case 0x2CB4: +case 0x2EB4: +case 0x20B5: +case 0x22B5: +case 0x24B5: +case 0x26B5: +case 0x28B5: +case 0x2AB5: +case 0x2CB5: +case 0x2EB5: +case 0x20B6: +case 0x22B6: +case 0x24B6: +case 0x26B6: +case 0x28B6: +case 0x2AB6: +case 0x2CB6: +case 0x2EB6: +case 0x20B7: +case 0x22B7: +case 0x24B7: +case 0x26B7: +case 0x28B7: +case 0x2AB7: +case 0x2CB7: +case 0x2EB7: + +// MOVEL +case 0x20B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x22F0: +case 0x24F0: +case 0x26F0: +case 0x28F0: +case 0x2AF0: +case 0x2CF0: +case 0x20F1: +case 0x22F1: +case 0x24F1: +case 0x26F1: +case 0x28F1: +case 0x2AF1: +case 0x2CF1: +case 0x20F2: +case 0x22F2: +case 0x24F2: +case 0x26F2: +case 0x28F2: +case 0x2AF2: +case 0x2CF2: +case 0x20F3: +case 0x22F3: +case 0x24F3: +case 0x26F3: +case 0x28F3: +case 0x2AF3: +case 0x2CF3: +case 0x20F4: +case 0x22F4: +case 0x24F4: +case 0x26F4: +case 0x28F4: +case 0x2AF4: +case 0x2CF4: +case 0x20F5: +case 0x22F5: +case 0x24F5: +case 0x26F5: +case 0x28F5: +case 0x2AF5: +case 0x2CF5: +case 0x20F6: +case 0x22F6: +case 0x24F6: +case 0x26F6: +case 0x28F6: +case 0x2AF6: +case 0x2CF6: +case 0x20F7: +case 0x22F7: +case 0x24F7: +case 0x26F7: +case 0x28F7: +case 0x2AF7: +case 0x2CF7: + +// MOVEL +case 0x20F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x2330: +case 0x2530: +case 0x2730: +case 0x2930: +case 0x2B30: +case 0x2D30: +case 0x2131: +case 0x2331: +case 0x2531: +case 0x2731: +case 0x2931: +case 0x2B31: +case 0x2D31: +case 0x2132: +case 0x2332: +case 0x2532: +case 0x2732: +case 0x2932: +case 0x2B32: +case 0x2D32: +case 0x2133: +case 0x2333: +case 0x2533: +case 0x2733: +case 0x2933: +case 0x2B33: +case 0x2D33: +case 0x2134: +case 0x2334: +case 0x2534: +case 0x2734: +case 0x2934: +case 0x2B34: +case 0x2D34: +case 0x2135: +case 0x2335: +case 0x2535: +case 0x2735: +case 0x2935: +case 0x2B35: +case 0x2D35: +case 0x2136: +case 0x2336: +case 0x2536: +case 0x2736: +case 0x2936: +case 0x2B36: +case 0x2D36: +case 0x2137: +case 0x2337: +case 0x2537: +case 0x2737: +case 0x2937: +case 0x2B37: +case 0x2D37: + +// MOVEL +case 0x2130: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x2370: +case 0x2570: +case 0x2770: +case 0x2970: +case 0x2B70: +case 0x2D70: +case 0x2F70: +case 0x2171: +case 0x2371: +case 0x2571: +case 0x2771: +case 0x2971: +case 0x2B71: +case 0x2D71: +case 0x2F71: +case 0x2172: +case 0x2372: +case 0x2572: +case 0x2772: +case 0x2972: +case 0x2B72: +case 0x2D72: +case 0x2F72: +case 0x2173: +case 0x2373: +case 0x2573: +case 0x2773: +case 0x2973: +case 0x2B73: +case 0x2D73: +case 0x2F73: +case 0x2174: +case 0x2374: +case 0x2574: +case 0x2774: +case 0x2974: +case 0x2B74: +case 0x2D74: +case 0x2F74: +case 0x2175: +case 0x2375: +case 0x2575: +case 0x2775: +case 0x2975: +case 0x2B75: +case 0x2D75: +case 0x2F75: +case 0x2176: +case 0x2376: +case 0x2576: +case 0x2776: +case 0x2976: +case 0x2B76: +case 0x2D76: +case 0x2F76: +case 0x2177: +case 0x2377: +case 0x2577: +case 0x2777: +case 0x2977: +case 0x2B77: +case 0x2D77: +case 0x2F77: + +// MOVEL +case 0x2170: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x23B0: +case 0x25B0: +case 0x27B0: +case 0x29B0: +case 0x2BB0: +case 0x2DB0: +case 0x2FB0: +case 0x21B1: +case 0x23B1: +case 0x25B1: +case 0x27B1: +case 0x29B1: +case 0x2BB1: +case 0x2DB1: +case 0x2FB1: +case 0x21B2: +case 0x23B2: +case 0x25B2: +case 0x27B2: +case 0x29B2: +case 0x2BB2: +case 0x2DB2: +case 0x2FB2: +case 0x21B3: +case 0x23B3: +case 0x25B3: +case 0x27B3: +case 0x29B3: +case 0x2BB3: +case 0x2DB3: +case 0x2FB3: +case 0x21B4: +case 0x23B4: +case 0x25B4: +case 0x27B4: +case 0x29B4: +case 0x2BB4: +case 0x2DB4: +case 0x2FB4: +case 0x21B5: +case 0x23B5: +case 0x25B5: +case 0x27B5: +case 0x29B5: +case 0x2BB5: +case 0x2DB5: +case 0x2FB5: +case 0x21B6: +case 0x23B6: +case 0x25B6: +case 0x27B6: +case 0x29B6: +case 0x2BB6: +case 0x2DB6: +case 0x2FB6: +case 0x21B7: +case 0x23B7: +case 0x25B7: +case 0x27B7: +case 0x29B7: +case 0x2BB7: +case 0x2DB7: +case 0x2FB7: + +// MOVEL +case 0x21B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x21F1: +case 0x21F2: +case 0x21F3: +case 0x21F4: +case 0x21F5: +case 0x21F6: +case 0x21F7: + +// MOVEL +case 0x21F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x23F1: +case 0x23F2: +case 0x23F3: +case 0x23F4: +case 0x23F5: +case 0x23F6: +case 0x23F7: + +// MOVEL +case 0x23F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) +case 0x2EF1: +case 0x2EF2: +case 0x2EF3: +case 0x2EF4: +case 0x2EF5: +case 0x2EF6: +case 0x2EF7: + +// MOVEL +case 0x2EF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x2F31: +case 0x2F32: +case 0x2F33: +case 0x2F34: +case 0x2F35: +case 0x2F36: +case 0x2F37: + +// MOVEL +case 0x2F30: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x2238: +case 0x2438: +case 0x2638: +case 0x2838: +case 0x2A38: +case 0x2C38: +case 0x2E38: + +// MOVEL +case 0x2038: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x22B8: +case 0x24B8: +case 0x26B8: +case 0x28B8: +case 0x2AB8: +case 0x2CB8: +case 0x2EB8: + +// MOVEL +case 0x20B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x22F8: +case 0x24F8: +case 0x26F8: +case 0x28F8: +case 0x2AF8: +case 0x2CF8: + +// MOVEL +case 0x20F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2338: +case 0x2538: +case 0x2738: +case 0x2938: +case 0x2B38: +case 0x2D38: + +// MOVEL +case 0x2138: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2378: +case 0x2578: +case 0x2778: +case 0x2978: +case 0x2B78: +case 0x2D78: +case 0x2F78: + +// MOVEL +case 0x2178: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x23B8: +case 0x25B8: +case 0x27B8: +case 0x29B8: +case 0x2BB8: +case 0x2DB8: +case 0x2FB8: + +// MOVEL +case 0x21B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// MOVEL +case 0x21F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x23F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// MOVEL +case 0x2EF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// MOVEL +case 0x2F38: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x2239: +case 0x2439: +case 0x2639: +case 0x2839: +case 0x2A39: +case 0x2C39: +case 0x2E39: + +// MOVEL +case 0x2039: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x22B9: +case 0x24B9: +case 0x26B9: +case 0x28B9: +case 0x2AB9: +case 0x2CB9: +case 0x2EB9: + +// MOVEL +case 0x20B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x22F9: +case 0x24F9: +case 0x26F9: +case 0x28F9: +case 0x2AF9: +case 0x2CF9: + +// MOVEL +case 0x20F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x2339: +case 0x2539: +case 0x2739: +case 0x2939: +case 0x2B39: +case 0x2D39: + +// MOVEL +case 0x2139: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x2379: +case 0x2579: +case 0x2779: +case 0x2979: +case 0x2B79: +case 0x2D79: +case 0x2F79: + +// MOVEL +case 0x2179: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) +case 0x23B9: +case 0x25B9: +case 0x27B9: +case 0x29B9: +case 0x2BB9: +case 0x2DB9: +case 0x2FB9: + +// MOVEL +case 0x21B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// MOVEL +case 0x21F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// MOVEL +case 0x23F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(36) + +// MOVEL +case 0x2EF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x2F39: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x223A: +case 0x243A: +case 0x263A: +case 0x283A: +case 0x2A3A: +case 0x2C3A: +case 0x2E3A: + +// MOVEL +case 0x203A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x22BA: +case 0x24BA: +case 0x26BA: +case 0x28BA: +case 0x2ABA: +case 0x2CBA: +case 0x2EBA: + +// MOVEL +case 0x20BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x22FA: +case 0x24FA: +case 0x26FA: +case 0x28FA: +case 0x2AFA: +case 0x2CFA: + +// MOVEL +case 0x20FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x233A: +case 0x253A: +case 0x273A: +case 0x293A: +case 0x2B3A: +case 0x2D3A: + +// MOVEL +case 0x213A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x237A: +case 0x257A: +case 0x277A: +case 0x297A: +case 0x2B7A: +case 0x2D7A: +case 0x2F7A: + +// MOVEL +case 0x217A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x23BA: +case 0x25BA: +case 0x27BA: +case 0x29BA: +case 0x2BBA: +case 0x2DBA: +case 0x2FBA: + +// MOVEL +case 0x21BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// MOVEL +case 0x21FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x23FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// MOVEL +case 0x2EFA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// MOVEL +case 0x2F3A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x223B: +case 0x243B: +case 0x263B: +case 0x283B: +case 0x2A3B: +case 0x2C3B: +case 0x2E3B: + +// MOVEL +case 0x203B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x22BB: +case 0x24BB: +case 0x26BB: +case 0x28BB: +case 0x2ABB: +case 0x2CBB: +case 0x2EBB: + +// MOVEL +case 0x20BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x22FB: +case 0x24FB: +case 0x26FB: +case 0x28FB: +case 0x2AFB: +case 0x2CFB: + +// MOVEL +case 0x20FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x233B: +case 0x253B: +case 0x273B: +case 0x293B: +case 0x2B3B: +case 0x2D3B: + +// MOVEL +case 0x213B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x237B: +case 0x257B: +case 0x277B: +case 0x297B: +case 0x2B7B: +case 0x2D7B: +case 0x2F7B: + +// MOVEL +case 0x217B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x23BB: +case 0x25BB: +case 0x27BB: +case 0x29BB: +case 0x2BBB: +case 0x2DBB: +case 0x2FBB: + +// MOVEL +case 0x21BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(32) + +// MOVEL +case 0x21FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// MOVEL +case 0x23FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(34) + +// MOVEL +case 0x2EFB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// MOVEL +case 0x2F3B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x223C: +case 0x243C: +case 0x263C: +case 0x283C: +case 0x2A3C: +case 0x2C3C: +case 0x2E3C: + +// MOVEL +case 0x203C: +{ + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(12) +case 0x22BC: +case 0x24BC: +case 0x26BC: +case 0x28BC: +case 0x2ABC: +case 0x2CBC: +case 0x2EBC: + +// MOVEL +case 0x20BC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x22FC: +case 0x24FC: +case 0x26FC: +case 0x28FC: +case 0x2AFC: +case 0x2CFC: + +// MOVEL +case 0x20FC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x233C: +case 0x253C: +case 0x273C: +case 0x293C: +case 0x2B3C: +case 0x2D3C: + +// MOVEL +case 0x213C: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x237C: +case 0x257C: +case 0x277C: +case 0x297C: +case 0x2B7C: +case 0x2D7C: +case 0x2F7C: + +// MOVEL +case 0x217C: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x23BC: +case 0x25BC: +case 0x27BC: +case 0x29BC: +case 0x2BBC: +case 0x2DBC: +case 0x2FBC: + +// MOVEL +case 0x21BC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// MOVEL +case 0x21FC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// MOVEL +case 0x23FC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x2EFC: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// MOVEL +case 0x2F3C: +{ + u32 adr; + u32 res; + res = FETCH_LONG; + PC += 4; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x221F: +case 0x241F: +case 0x261F: +case 0x281F: +case 0x2A1F: +case 0x2C1F: +case 0x2E1F: + +// MOVEL +case 0x201F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x229F: +case 0x249F: +case 0x269F: +case 0x289F: +case 0x2A9F: +case 0x2C9F: +case 0x2E9F: + +// MOVEL +case 0x209F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x22DF: +case 0x24DF: +case 0x26DF: +case 0x28DF: +case 0x2ADF: +case 0x2CDF: + +// MOVEL +case 0x20DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x231F: +case 0x251F: +case 0x271F: +case 0x291F: +case 0x2B1F: +case 0x2D1F: + +// MOVEL +case 0x211F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x235F: +case 0x255F: +case 0x275F: +case 0x295F: +case 0x2B5F: +case 0x2D5F: +case 0x2F5F: + +// MOVEL +case 0x215F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x239F: +case 0x259F: +case 0x279F: +case 0x299F: +case 0x2B9F: +case 0x2D9F: +case 0x2F9F: + +// MOVEL +case 0x219F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// MOVEL +case 0x21DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// MOVEL +case 0x23DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x2EDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// MOVEL +case 0x2F1F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x2227: +case 0x2427: +case 0x2627: +case 0x2827: +case 0x2A27: +case 0x2C27: +case 0x2E27: + +// MOVEL +case 0x2027: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x22A7: +case 0x24A7: +case 0x26A7: +case 0x28A7: +case 0x2AA7: +case 0x2CA7: +case 0x2EA7: + +// MOVEL +case 0x20A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x22E7: +case 0x24E7: +case 0x26E7: +case 0x28E7: +case 0x2AE7: +case 0x2CE7: + +// MOVEL +case 0x20E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2327: +case 0x2527: +case 0x2727: +case 0x2927: +case 0x2B27: +case 0x2D27: + +// MOVEL +case 0x2127: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2367: +case 0x2567: +case 0x2767: +case 0x2967: +case 0x2B67: +case 0x2D67: +case 0x2F67: + +// MOVEL +case 0x2167: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x23A7: +case 0x25A7: +case 0x27A7: +case 0x29A7: +case 0x2BA7: +case 0x2DA7: +case 0x2FA7: + +// MOVEL +case 0x21A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// MOVEL +case 0x21E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// MOVEL +case 0x23E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// MOVEL +case 0x2EE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7]; + CPU->A[7] += 4; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) + +// MOVEL +case 0x2F27: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x2240: +case 0x2440: +case 0x2640: +case 0x2840: +case 0x2A40: +case 0x2C40: +case 0x2E40: +case 0x2041: +case 0x2241: +case 0x2441: +case 0x2641: +case 0x2841: +case 0x2A41: +case 0x2C41: +case 0x2E41: +case 0x2042: +case 0x2242: +case 0x2442: +case 0x2642: +case 0x2842: +case 0x2A42: +case 0x2C42: +case 0x2E42: +case 0x2043: +case 0x2243: +case 0x2443: +case 0x2643: +case 0x2843: +case 0x2A43: +case 0x2C43: +case 0x2E43: +case 0x2044: +case 0x2244: +case 0x2444: +case 0x2644: +case 0x2844: +case 0x2A44: +case 0x2C44: +case 0x2E44: +case 0x2045: +case 0x2245: +case 0x2445: +case 0x2645: +case 0x2845: +case 0x2A45: +case 0x2C45: +case 0x2E45: +case 0x2046: +case 0x2246: +case 0x2446: +case 0x2646: +case 0x2846: +case 0x2A46: +case 0x2C46: +case 0x2E46: +case 0x2047: +case 0x2247: +case 0x2447: +case 0x2647: +case 0x2847: +case 0x2A47: +case 0x2C47: +case 0x2E47: + +// MOVEAL +case 0x2040: +{ + u32 res; + res = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(4) +case 0x2248: +case 0x2448: +case 0x2648: +case 0x2848: +case 0x2A48: +case 0x2C48: +case 0x2E48: +case 0x2049: +case 0x2249: +case 0x2449: +case 0x2649: +case 0x2849: +case 0x2A49: +case 0x2C49: +case 0x2E49: +case 0x204A: +case 0x224A: +case 0x244A: +case 0x264A: +case 0x284A: +case 0x2A4A: +case 0x2C4A: +case 0x2E4A: +case 0x204B: +case 0x224B: +case 0x244B: +case 0x264B: +case 0x284B: +case 0x2A4B: +case 0x2C4B: +case 0x2E4B: +case 0x204C: +case 0x224C: +case 0x244C: +case 0x264C: +case 0x284C: +case 0x2A4C: +case 0x2C4C: +case 0x2E4C: +case 0x204D: +case 0x224D: +case 0x244D: +case 0x264D: +case 0x284D: +case 0x2A4D: +case 0x2C4D: +case 0x2E4D: +case 0x204E: +case 0x224E: +case 0x244E: +case 0x264E: +case 0x284E: +case 0x2A4E: +case 0x2C4E: +case 0x2E4E: +case 0x204F: +case 0x224F: +case 0x244F: +case 0x264F: +case 0x284F: +case 0x2A4F: +case 0x2C4F: +case 0x2E4F: + +// MOVEAL +case 0x2048: +{ + u32 res; + res = (s32)(s32)CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(4) +case 0x2250: +case 0x2450: +case 0x2650: +case 0x2850: +case 0x2A50: +case 0x2C50: +case 0x2E50: +case 0x2051: +case 0x2251: +case 0x2451: +case 0x2651: +case 0x2851: +case 0x2A51: +case 0x2C51: +case 0x2E51: +case 0x2052: +case 0x2252: +case 0x2452: +case 0x2652: +case 0x2852: +case 0x2A52: +case 0x2C52: +case 0x2E52: +case 0x2053: +case 0x2253: +case 0x2453: +case 0x2653: +case 0x2853: +case 0x2A53: +case 0x2C53: +case 0x2E53: +case 0x2054: +case 0x2254: +case 0x2454: +case 0x2654: +case 0x2854: +case 0x2A54: +case 0x2C54: +case 0x2E54: +case 0x2055: +case 0x2255: +case 0x2455: +case 0x2655: +case 0x2855: +case 0x2A55: +case 0x2C55: +case 0x2E55: +case 0x2056: +case 0x2256: +case 0x2456: +case 0x2656: +case 0x2856: +case 0x2A56: +case 0x2C56: +case 0x2E56: +case 0x2057: +case 0x2257: +case 0x2457: +case 0x2657: +case 0x2857: +case 0x2A57: +case 0x2C57: +case 0x2E57: + +// MOVEAL +case 0x2050: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x2258: +case 0x2458: +case 0x2658: +case 0x2858: +case 0x2A58: +case 0x2C58: +case 0x2E58: +case 0x2059: +case 0x2259: +case 0x2459: +case 0x2659: +case 0x2859: +case 0x2A59: +case 0x2C59: +case 0x2E59: +case 0x205A: +case 0x225A: +case 0x245A: +case 0x265A: +case 0x285A: +case 0x2A5A: +case 0x2C5A: +case 0x2E5A: +case 0x205B: +case 0x225B: +case 0x245B: +case 0x265B: +case 0x285B: +case 0x2A5B: +case 0x2C5B: +case 0x2E5B: +case 0x205C: +case 0x225C: +case 0x245C: +case 0x265C: +case 0x285C: +case 0x2A5C: +case 0x2C5C: +case 0x2E5C: +case 0x205D: +case 0x225D: +case 0x245D: +case 0x265D: +case 0x285D: +case 0x2A5D: +case 0x2C5D: +case 0x2E5D: +case 0x205E: +case 0x225E: +case 0x245E: +case 0x265E: +case 0x285E: +case 0x2A5E: +case 0x2C5E: +case 0x2E5E: + +// MOVEAL +case 0x2058: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x2260: +case 0x2460: +case 0x2660: +case 0x2860: +case 0x2A60: +case 0x2C60: +case 0x2E60: +case 0x2061: +case 0x2261: +case 0x2461: +case 0x2661: +case 0x2861: +case 0x2A61: +case 0x2C61: +case 0x2E61: +case 0x2062: +case 0x2262: +case 0x2462: +case 0x2662: +case 0x2862: +case 0x2A62: +case 0x2C62: +case 0x2E62: +case 0x2063: +case 0x2263: +case 0x2463: +case 0x2663: +case 0x2863: +case 0x2A63: +case 0x2C63: +case 0x2E63: +case 0x2064: +case 0x2264: +case 0x2464: +case 0x2664: +case 0x2864: +case 0x2A64: +case 0x2C64: +case 0x2E64: +case 0x2065: +case 0x2265: +case 0x2465: +case 0x2665: +case 0x2865: +case 0x2A65: +case 0x2C65: +case 0x2E65: +case 0x2066: +case 0x2266: +case 0x2466: +case 0x2666: +case 0x2866: +case 0x2A66: +case 0x2C66: +case 0x2E66: + +// MOVEAL +case 0x2060: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0x2268: +case 0x2468: +case 0x2668: +case 0x2868: +case 0x2A68: +case 0x2C68: +case 0x2E68: +case 0x2069: +case 0x2269: +case 0x2469: +case 0x2669: +case 0x2869: +case 0x2A69: +case 0x2C69: +case 0x2E69: +case 0x206A: +case 0x226A: +case 0x246A: +case 0x266A: +case 0x286A: +case 0x2A6A: +case 0x2C6A: +case 0x2E6A: +case 0x206B: +case 0x226B: +case 0x246B: +case 0x266B: +case 0x286B: +case 0x2A6B: +case 0x2C6B: +case 0x2E6B: +case 0x206C: +case 0x226C: +case 0x246C: +case 0x266C: +case 0x286C: +case 0x2A6C: +case 0x2C6C: +case 0x2E6C: +case 0x206D: +case 0x226D: +case 0x246D: +case 0x266D: +case 0x286D: +case 0x2A6D: +case 0x2C6D: +case 0x2E6D: +case 0x206E: +case 0x226E: +case 0x246E: +case 0x266E: +case 0x286E: +case 0x2A6E: +case 0x2C6E: +case 0x2E6E: +case 0x206F: +case 0x226F: +case 0x246F: +case 0x266F: +case 0x286F: +case 0x2A6F: +case 0x2C6F: +case 0x2E6F: + +// MOVEAL +case 0x2068: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x2270: +case 0x2470: +case 0x2670: +case 0x2870: +case 0x2A70: +case 0x2C70: +case 0x2E70: +case 0x2071: +case 0x2271: +case 0x2471: +case 0x2671: +case 0x2871: +case 0x2A71: +case 0x2C71: +case 0x2E71: +case 0x2072: +case 0x2272: +case 0x2472: +case 0x2672: +case 0x2872: +case 0x2A72: +case 0x2C72: +case 0x2E72: +case 0x2073: +case 0x2273: +case 0x2473: +case 0x2673: +case 0x2873: +case 0x2A73: +case 0x2C73: +case 0x2E73: +case 0x2074: +case 0x2274: +case 0x2474: +case 0x2674: +case 0x2874: +case 0x2A74: +case 0x2C74: +case 0x2E74: +case 0x2075: +case 0x2275: +case 0x2475: +case 0x2675: +case 0x2875: +case 0x2A75: +case 0x2C75: +case 0x2E75: +case 0x2076: +case 0x2276: +case 0x2476: +case 0x2676: +case 0x2876: +case 0x2A76: +case 0x2C76: +case 0x2E76: +case 0x2077: +case 0x2277: +case 0x2477: +case 0x2677: +case 0x2877: +case 0x2A77: +case 0x2C77: +case 0x2E77: + +// MOVEAL +case 0x2070: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0x2278: +case 0x2478: +case 0x2678: +case 0x2878: +case 0x2A78: +case 0x2C78: +case 0x2E78: + +// MOVEAL +case 0x2078: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x2279: +case 0x2479: +case 0x2679: +case 0x2879: +case 0x2A79: +case 0x2C79: +case 0x2E79: + +// MOVEAL +case 0x2079: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0x227A: +case 0x247A: +case 0x267A: +case 0x287A: +case 0x2A7A: +case 0x2C7A: +case 0x2E7A: + +// MOVEAL +case 0x207A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x227B: +case 0x247B: +case 0x267B: +case 0x287B: +case 0x2A7B: +case 0x2C7B: +case 0x2E7B: + +// MOVEAL +case 0x207B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0x227C: +case 0x247C: +case 0x267C: +case 0x287C: +case 0x2A7C: +case 0x2C7C: +case 0x2E7C: + +// MOVEAL +case 0x207C: +{ + u32 res; + res = (s32)(s32)FETCH_LONG; + PC += 4; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) +case 0x225F: +case 0x245F: +case 0x265F: +case 0x285F: +case 0x2A5F: +case 0x2C5F: +case 0x2E5F: + +// MOVEAL +case 0x205F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x2267: +case 0x2467: +case 0x2667: +case 0x2867: +case 0x2A67: +case 0x2C67: +case 0x2E67: + +// MOVEAL +case 0x2067: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READSX_LONG_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) diff --git a/yabause/src/c68k/c68k_op3.inc b/yabause/src/c68k/c68k_op3.inc new file mode 100644 index 0000000000..f1bbdc58ad --- /dev/null +++ b/yabause/src/c68k/c68k_op3.inc @@ -0,0 +1,6254 @@ +case 0x3200: +case 0x3400: +case 0x3600: +case 0x3800: +case 0x3A00: +case 0x3C00: +case 0x3E00: +case 0x3001: +case 0x3201: +case 0x3401: +case 0x3601: +case 0x3801: +case 0x3A01: +case 0x3C01: +case 0x3E01: +case 0x3002: +case 0x3202: +case 0x3402: +case 0x3602: +case 0x3802: +case 0x3A02: +case 0x3C02: +case 0x3E02: +case 0x3003: +case 0x3203: +case 0x3403: +case 0x3603: +case 0x3803: +case 0x3A03: +case 0x3C03: +case 0x3E03: +case 0x3004: +case 0x3204: +case 0x3404: +case 0x3604: +case 0x3804: +case 0x3A04: +case 0x3C04: +case 0x3E04: +case 0x3005: +case 0x3205: +case 0x3405: +case 0x3605: +case 0x3805: +case 0x3A05: +case 0x3C05: +case 0x3E05: +case 0x3006: +case 0x3206: +case 0x3406: +case 0x3606: +case 0x3806: +case 0x3A06: +case 0x3C06: +case 0x3E06: +case 0x3007: +case 0x3207: +case 0x3407: +case 0x3607: +case 0x3807: +case 0x3A07: +case 0x3C07: +case 0x3E07: + +// MOVEW +case 0x3000: +{ + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x3280: +case 0x3480: +case 0x3680: +case 0x3880: +case 0x3A80: +case 0x3C80: +case 0x3E80: +case 0x3081: +case 0x3281: +case 0x3481: +case 0x3681: +case 0x3881: +case 0x3A81: +case 0x3C81: +case 0x3E81: +case 0x3082: +case 0x3282: +case 0x3482: +case 0x3682: +case 0x3882: +case 0x3A82: +case 0x3C82: +case 0x3E82: +case 0x3083: +case 0x3283: +case 0x3483: +case 0x3683: +case 0x3883: +case 0x3A83: +case 0x3C83: +case 0x3E83: +case 0x3084: +case 0x3284: +case 0x3484: +case 0x3684: +case 0x3884: +case 0x3A84: +case 0x3C84: +case 0x3E84: +case 0x3085: +case 0x3285: +case 0x3485: +case 0x3685: +case 0x3885: +case 0x3A85: +case 0x3C85: +case 0x3E85: +case 0x3086: +case 0x3286: +case 0x3486: +case 0x3686: +case 0x3886: +case 0x3A86: +case 0x3C86: +case 0x3E86: +case 0x3087: +case 0x3287: +case 0x3487: +case 0x3687: +case 0x3887: +case 0x3A87: +case 0x3C87: +case 0x3E87: + +// MOVEW +case 0x3080: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x32C0: +case 0x34C0: +case 0x36C0: +case 0x38C0: +case 0x3AC0: +case 0x3CC0: +case 0x30C1: +case 0x32C1: +case 0x34C1: +case 0x36C1: +case 0x38C1: +case 0x3AC1: +case 0x3CC1: +case 0x30C2: +case 0x32C2: +case 0x34C2: +case 0x36C2: +case 0x38C2: +case 0x3AC2: +case 0x3CC2: +case 0x30C3: +case 0x32C3: +case 0x34C3: +case 0x36C3: +case 0x38C3: +case 0x3AC3: +case 0x3CC3: +case 0x30C4: +case 0x32C4: +case 0x34C4: +case 0x36C4: +case 0x38C4: +case 0x3AC4: +case 0x3CC4: +case 0x30C5: +case 0x32C5: +case 0x34C5: +case 0x36C5: +case 0x38C5: +case 0x3AC5: +case 0x3CC5: +case 0x30C6: +case 0x32C6: +case 0x34C6: +case 0x36C6: +case 0x38C6: +case 0x3AC6: +case 0x3CC6: +case 0x30C7: +case 0x32C7: +case 0x34C7: +case 0x36C7: +case 0x38C7: +case 0x3AC7: +case 0x3CC7: + +// MOVEW +case 0x30C0: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3300: +case 0x3500: +case 0x3700: +case 0x3900: +case 0x3B00: +case 0x3D00: +case 0x3101: +case 0x3301: +case 0x3501: +case 0x3701: +case 0x3901: +case 0x3B01: +case 0x3D01: +case 0x3102: +case 0x3302: +case 0x3502: +case 0x3702: +case 0x3902: +case 0x3B02: +case 0x3D02: +case 0x3103: +case 0x3303: +case 0x3503: +case 0x3703: +case 0x3903: +case 0x3B03: +case 0x3D03: +case 0x3104: +case 0x3304: +case 0x3504: +case 0x3704: +case 0x3904: +case 0x3B04: +case 0x3D04: +case 0x3105: +case 0x3305: +case 0x3505: +case 0x3705: +case 0x3905: +case 0x3B05: +case 0x3D05: +case 0x3106: +case 0x3306: +case 0x3506: +case 0x3706: +case 0x3906: +case 0x3B06: +case 0x3D06: +case 0x3107: +case 0x3307: +case 0x3507: +case 0x3707: +case 0x3907: +case 0x3B07: +case 0x3D07: + +// MOVEW +case 0x3100: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3340: +case 0x3540: +case 0x3740: +case 0x3940: +case 0x3B40: +case 0x3D40: +case 0x3F40: +case 0x3141: +case 0x3341: +case 0x3541: +case 0x3741: +case 0x3941: +case 0x3B41: +case 0x3D41: +case 0x3F41: +case 0x3142: +case 0x3342: +case 0x3542: +case 0x3742: +case 0x3942: +case 0x3B42: +case 0x3D42: +case 0x3F42: +case 0x3143: +case 0x3343: +case 0x3543: +case 0x3743: +case 0x3943: +case 0x3B43: +case 0x3D43: +case 0x3F43: +case 0x3144: +case 0x3344: +case 0x3544: +case 0x3744: +case 0x3944: +case 0x3B44: +case 0x3D44: +case 0x3F44: +case 0x3145: +case 0x3345: +case 0x3545: +case 0x3745: +case 0x3945: +case 0x3B45: +case 0x3D45: +case 0x3F45: +case 0x3146: +case 0x3346: +case 0x3546: +case 0x3746: +case 0x3946: +case 0x3B46: +case 0x3D46: +case 0x3F46: +case 0x3147: +case 0x3347: +case 0x3547: +case 0x3747: +case 0x3947: +case 0x3B47: +case 0x3D47: +case 0x3F47: + +// MOVEW +case 0x3140: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3380: +case 0x3580: +case 0x3780: +case 0x3980: +case 0x3B80: +case 0x3D80: +case 0x3F80: +case 0x3181: +case 0x3381: +case 0x3581: +case 0x3781: +case 0x3981: +case 0x3B81: +case 0x3D81: +case 0x3F81: +case 0x3182: +case 0x3382: +case 0x3582: +case 0x3782: +case 0x3982: +case 0x3B82: +case 0x3D82: +case 0x3F82: +case 0x3183: +case 0x3383: +case 0x3583: +case 0x3783: +case 0x3983: +case 0x3B83: +case 0x3D83: +case 0x3F83: +case 0x3184: +case 0x3384: +case 0x3584: +case 0x3784: +case 0x3984: +case 0x3B84: +case 0x3D84: +case 0x3F84: +case 0x3185: +case 0x3385: +case 0x3585: +case 0x3785: +case 0x3985: +case 0x3B85: +case 0x3D85: +case 0x3F85: +case 0x3186: +case 0x3386: +case 0x3586: +case 0x3786: +case 0x3986: +case 0x3B86: +case 0x3D86: +case 0x3F86: +case 0x3187: +case 0x3387: +case 0x3587: +case 0x3787: +case 0x3987: +case 0x3B87: +case 0x3D87: +case 0x3F87: + +// MOVEW +case 0x3180: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x31C1: +case 0x31C2: +case 0x31C3: +case 0x31C4: +case 0x31C5: +case 0x31C6: +case 0x31C7: + +// MOVEW +case 0x31C0: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x33C1: +case 0x33C2: +case 0x33C3: +case 0x33C4: +case 0x33C5: +case 0x33C6: +case 0x33C7: + +// MOVEW +case 0x33C0: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3EC1: +case 0x3EC2: +case 0x3EC3: +case 0x3EC4: +case 0x3EC5: +case 0x3EC6: +case 0x3EC7: + +// MOVEW +case 0x3EC0: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3F01: +case 0x3F02: +case 0x3F03: +case 0x3F04: +case 0x3F05: +case 0x3F06: +case 0x3F07: + +// MOVEW +case 0x3F00: +{ + u32 adr; + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3208: +case 0x3408: +case 0x3608: +case 0x3808: +case 0x3A08: +case 0x3C08: +case 0x3E08: +case 0x3009: +case 0x3209: +case 0x3409: +case 0x3609: +case 0x3809: +case 0x3A09: +case 0x3C09: +case 0x3E09: +case 0x300A: +case 0x320A: +case 0x340A: +case 0x360A: +case 0x380A: +case 0x3A0A: +case 0x3C0A: +case 0x3E0A: +case 0x300B: +case 0x320B: +case 0x340B: +case 0x360B: +case 0x380B: +case 0x3A0B: +case 0x3C0B: +case 0x3E0B: +case 0x300C: +case 0x320C: +case 0x340C: +case 0x360C: +case 0x380C: +case 0x3A0C: +case 0x3C0C: +case 0x3E0C: +case 0x300D: +case 0x320D: +case 0x340D: +case 0x360D: +case 0x380D: +case 0x3A0D: +case 0x3C0D: +case 0x3E0D: +case 0x300E: +case 0x320E: +case 0x340E: +case 0x360E: +case 0x380E: +case 0x3A0E: +case 0x3C0E: +case 0x3E0E: +case 0x300F: +case 0x320F: +case 0x340F: +case 0x360F: +case 0x380F: +case 0x3A0F: +case 0x3C0F: +case 0x3E0F: + +// MOVEW +case 0x3008: +{ + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x3288: +case 0x3488: +case 0x3688: +case 0x3888: +case 0x3A88: +case 0x3C88: +case 0x3E88: +case 0x3089: +case 0x3289: +case 0x3489: +case 0x3689: +case 0x3889: +case 0x3A89: +case 0x3C89: +case 0x3E89: +case 0x308A: +case 0x328A: +case 0x348A: +case 0x368A: +case 0x388A: +case 0x3A8A: +case 0x3C8A: +case 0x3E8A: +case 0x308B: +case 0x328B: +case 0x348B: +case 0x368B: +case 0x388B: +case 0x3A8B: +case 0x3C8B: +case 0x3E8B: +case 0x308C: +case 0x328C: +case 0x348C: +case 0x368C: +case 0x388C: +case 0x3A8C: +case 0x3C8C: +case 0x3E8C: +case 0x308D: +case 0x328D: +case 0x348D: +case 0x368D: +case 0x388D: +case 0x3A8D: +case 0x3C8D: +case 0x3E8D: +case 0x308E: +case 0x328E: +case 0x348E: +case 0x368E: +case 0x388E: +case 0x3A8E: +case 0x3C8E: +case 0x3E8E: +case 0x308F: +case 0x328F: +case 0x348F: +case 0x368F: +case 0x388F: +case 0x3A8F: +case 0x3C8F: +case 0x3E8F: + +// MOVEW +case 0x3088: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x32C8: +case 0x34C8: +case 0x36C8: +case 0x38C8: +case 0x3AC8: +case 0x3CC8: +case 0x30C9: +case 0x32C9: +case 0x34C9: +case 0x36C9: +case 0x38C9: +case 0x3AC9: +case 0x3CC9: +case 0x30CA: +case 0x32CA: +case 0x34CA: +case 0x36CA: +case 0x38CA: +case 0x3ACA: +case 0x3CCA: +case 0x30CB: +case 0x32CB: +case 0x34CB: +case 0x36CB: +case 0x38CB: +case 0x3ACB: +case 0x3CCB: +case 0x30CC: +case 0x32CC: +case 0x34CC: +case 0x36CC: +case 0x38CC: +case 0x3ACC: +case 0x3CCC: +case 0x30CD: +case 0x32CD: +case 0x34CD: +case 0x36CD: +case 0x38CD: +case 0x3ACD: +case 0x3CCD: +case 0x30CE: +case 0x32CE: +case 0x34CE: +case 0x36CE: +case 0x38CE: +case 0x3ACE: +case 0x3CCE: +case 0x30CF: +case 0x32CF: +case 0x34CF: +case 0x36CF: +case 0x38CF: +case 0x3ACF: +case 0x3CCF: + +// MOVEW +case 0x30C8: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3308: +case 0x3508: +case 0x3708: +case 0x3908: +case 0x3B08: +case 0x3D08: +case 0x3109: +case 0x3309: +case 0x3509: +case 0x3709: +case 0x3909: +case 0x3B09: +case 0x3D09: +case 0x310A: +case 0x330A: +case 0x350A: +case 0x370A: +case 0x390A: +case 0x3B0A: +case 0x3D0A: +case 0x310B: +case 0x330B: +case 0x350B: +case 0x370B: +case 0x390B: +case 0x3B0B: +case 0x3D0B: +case 0x310C: +case 0x330C: +case 0x350C: +case 0x370C: +case 0x390C: +case 0x3B0C: +case 0x3D0C: +case 0x310D: +case 0x330D: +case 0x350D: +case 0x370D: +case 0x390D: +case 0x3B0D: +case 0x3D0D: +case 0x310E: +case 0x330E: +case 0x350E: +case 0x370E: +case 0x390E: +case 0x3B0E: +case 0x3D0E: +case 0x310F: +case 0x330F: +case 0x350F: +case 0x370F: +case 0x390F: +case 0x3B0F: +case 0x3D0F: + +// MOVEW +case 0x3108: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3348: +case 0x3548: +case 0x3748: +case 0x3948: +case 0x3B48: +case 0x3D48: +case 0x3F48: +case 0x3149: +case 0x3349: +case 0x3549: +case 0x3749: +case 0x3949: +case 0x3B49: +case 0x3D49: +case 0x3F49: +case 0x314A: +case 0x334A: +case 0x354A: +case 0x374A: +case 0x394A: +case 0x3B4A: +case 0x3D4A: +case 0x3F4A: +case 0x314B: +case 0x334B: +case 0x354B: +case 0x374B: +case 0x394B: +case 0x3B4B: +case 0x3D4B: +case 0x3F4B: +case 0x314C: +case 0x334C: +case 0x354C: +case 0x374C: +case 0x394C: +case 0x3B4C: +case 0x3D4C: +case 0x3F4C: +case 0x314D: +case 0x334D: +case 0x354D: +case 0x374D: +case 0x394D: +case 0x3B4D: +case 0x3D4D: +case 0x3F4D: +case 0x314E: +case 0x334E: +case 0x354E: +case 0x374E: +case 0x394E: +case 0x3B4E: +case 0x3D4E: +case 0x3F4E: +case 0x314F: +case 0x334F: +case 0x354F: +case 0x374F: +case 0x394F: +case 0x3B4F: +case 0x3D4F: +case 0x3F4F: + +// MOVEW +case 0x3148: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3388: +case 0x3588: +case 0x3788: +case 0x3988: +case 0x3B88: +case 0x3D88: +case 0x3F88: +case 0x3189: +case 0x3389: +case 0x3589: +case 0x3789: +case 0x3989: +case 0x3B89: +case 0x3D89: +case 0x3F89: +case 0x318A: +case 0x338A: +case 0x358A: +case 0x378A: +case 0x398A: +case 0x3B8A: +case 0x3D8A: +case 0x3F8A: +case 0x318B: +case 0x338B: +case 0x358B: +case 0x378B: +case 0x398B: +case 0x3B8B: +case 0x3D8B: +case 0x3F8B: +case 0x318C: +case 0x338C: +case 0x358C: +case 0x378C: +case 0x398C: +case 0x3B8C: +case 0x3D8C: +case 0x3F8C: +case 0x318D: +case 0x338D: +case 0x358D: +case 0x378D: +case 0x398D: +case 0x3B8D: +case 0x3D8D: +case 0x3F8D: +case 0x318E: +case 0x338E: +case 0x358E: +case 0x378E: +case 0x398E: +case 0x3B8E: +case 0x3D8E: +case 0x3F8E: +case 0x318F: +case 0x338F: +case 0x358F: +case 0x378F: +case 0x398F: +case 0x3B8F: +case 0x3D8F: +case 0x3F8F: + +// MOVEW +case 0x3188: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x31C9: +case 0x31CA: +case 0x31CB: +case 0x31CC: +case 0x31CD: +case 0x31CE: +case 0x31CF: + +// MOVEW +case 0x31C8: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x33C9: +case 0x33CA: +case 0x33CB: +case 0x33CC: +case 0x33CD: +case 0x33CE: +case 0x33CF: + +// MOVEW +case 0x33C8: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3EC9: +case 0x3ECA: +case 0x3ECB: +case 0x3ECC: +case 0x3ECD: +case 0x3ECE: +case 0x3ECF: + +// MOVEW +case 0x3EC8: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3F09: +case 0x3F0A: +case 0x3F0B: +case 0x3F0C: +case 0x3F0D: +case 0x3F0E: +case 0x3F0F: + +// MOVEW +case 0x3F08: +{ + u32 adr; + u32 res; + res = (u16)CPU->A[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(8) +case 0x3210: +case 0x3410: +case 0x3610: +case 0x3810: +case 0x3A10: +case 0x3C10: +case 0x3E10: +case 0x3011: +case 0x3211: +case 0x3411: +case 0x3611: +case 0x3811: +case 0x3A11: +case 0x3C11: +case 0x3E11: +case 0x3012: +case 0x3212: +case 0x3412: +case 0x3612: +case 0x3812: +case 0x3A12: +case 0x3C12: +case 0x3E12: +case 0x3013: +case 0x3213: +case 0x3413: +case 0x3613: +case 0x3813: +case 0x3A13: +case 0x3C13: +case 0x3E13: +case 0x3014: +case 0x3214: +case 0x3414: +case 0x3614: +case 0x3814: +case 0x3A14: +case 0x3C14: +case 0x3E14: +case 0x3015: +case 0x3215: +case 0x3415: +case 0x3615: +case 0x3815: +case 0x3A15: +case 0x3C15: +case 0x3E15: +case 0x3016: +case 0x3216: +case 0x3416: +case 0x3616: +case 0x3816: +case 0x3A16: +case 0x3C16: +case 0x3E16: +case 0x3017: +case 0x3217: +case 0x3417: +case 0x3617: +case 0x3817: +case 0x3A17: +case 0x3C17: +case 0x3E17: + +// MOVEW +case 0x3010: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x3290: +case 0x3490: +case 0x3690: +case 0x3890: +case 0x3A90: +case 0x3C90: +case 0x3E90: +case 0x3091: +case 0x3291: +case 0x3491: +case 0x3691: +case 0x3891: +case 0x3A91: +case 0x3C91: +case 0x3E91: +case 0x3092: +case 0x3292: +case 0x3492: +case 0x3692: +case 0x3892: +case 0x3A92: +case 0x3C92: +case 0x3E92: +case 0x3093: +case 0x3293: +case 0x3493: +case 0x3693: +case 0x3893: +case 0x3A93: +case 0x3C93: +case 0x3E93: +case 0x3094: +case 0x3294: +case 0x3494: +case 0x3694: +case 0x3894: +case 0x3A94: +case 0x3C94: +case 0x3E94: +case 0x3095: +case 0x3295: +case 0x3495: +case 0x3695: +case 0x3895: +case 0x3A95: +case 0x3C95: +case 0x3E95: +case 0x3096: +case 0x3296: +case 0x3496: +case 0x3696: +case 0x3896: +case 0x3A96: +case 0x3C96: +case 0x3E96: +case 0x3097: +case 0x3297: +case 0x3497: +case 0x3697: +case 0x3897: +case 0x3A97: +case 0x3C97: +case 0x3E97: + +// MOVEW +case 0x3090: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x32D0: +case 0x34D0: +case 0x36D0: +case 0x38D0: +case 0x3AD0: +case 0x3CD0: +case 0x30D1: +case 0x32D1: +case 0x34D1: +case 0x36D1: +case 0x38D1: +case 0x3AD1: +case 0x3CD1: +case 0x30D2: +case 0x32D2: +case 0x34D2: +case 0x36D2: +case 0x38D2: +case 0x3AD2: +case 0x3CD2: +case 0x30D3: +case 0x32D3: +case 0x34D3: +case 0x36D3: +case 0x38D3: +case 0x3AD3: +case 0x3CD3: +case 0x30D4: +case 0x32D4: +case 0x34D4: +case 0x36D4: +case 0x38D4: +case 0x3AD4: +case 0x3CD4: +case 0x30D5: +case 0x32D5: +case 0x34D5: +case 0x36D5: +case 0x38D5: +case 0x3AD5: +case 0x3CD5: +case 0x30D6: +case 0x32D6: +case 0x34D6: +case 0x36D6: +case 0x38D6: +case 0x3AD6: +case 0x3CD6: +case 0x30D7: +case 0x32D7: +case 0x34D7: +case 0x36D7: +case 0x38D7: +case 0x3AD7: +case 0x3CD7: + +// MOVEW +case 0x30D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3310: +case 0x3510: +case 0x3710: +case 0x3910: +case 0x3B10: +case 0x3D10: +case 0x3111: +case 0x3311: +case 0x3511: +case 0x3711: +case 0x3911: +case 0x3B11: +case 0x3D11: +case 0x3112: +case 0x3312: +case 0x3512: +case 0x3712: +case 0x3912: +case 0x3B12: +case 0x3D12: +case 0x3113: +case 0x3313: +case 0x3513: +case 0x3713: +case 0x3913: +case 0x3B13: +case 0x3D13: +case 0x3114: +case 0x3314: +case 0x3514: +case 0x3714: +case 0x3914: +case 0x3B14: +case 0x3D14: +case 0x3115: +case 0x3315: +case 0x3515: +case 0x3715: +case 0x3915: +case 0x3B15: +case 0x3D15: +case 0x3116: +case 0x3316: +case 0x3516: +case 0x3716: +case 0x3916: +case 0x3B16: +case 0x3D16: +case 0x3117: +case 0x3317: +case 0x3517: +case 0x3717: +case 0x3917: +case 0x3B17: +case 0x3D17: + +// MOVEW +case 0x3110: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3350: +case 0x3550: +case 0x3750: +case 0x3950: +case 0x3B50: +case 0x3D50: +case 0x3F50: +case 0x3151: +case 0x3351: +case 0x3551: +case 0x3751: +case 0x3951: +case 0x3B51: +case 0x3D51: +case 0x3F51: +case 0x3152: +case 0x3352: +case 0x3552: +case 0x3752: +case 0x3952: +case 0x3B52: +case 0x3D52: +case 0x3F52: +case 0x3153: +case 0x3353: +case 0x3553: +case 0x3753: +case 0x3953: +case 0x3B53: +case 0x3D53: +case 0x3F53: +case 0x3154: +case 0x3354: +case 0x3554: +case 0x3754: +case 0x3954: +case 0x3B54: +case 0x3D54: +case 0x3F54: +case 0x3155: +case 0x3355: +case 0x3555: +case 0x3755: +case 0x3955: +case 0x3B55: +case 0x3D55: +case 0x3F55: +case 0x3156: +case 0x3356: +case 0x3556: +case 0x3756: +case 0x3956: +case 0x3B56: +case 0x3D56: +case 0x3F56: +case 0x3157: +case 0x3357: +case 0x3557: +case 0x3757: +case 0x3957: +case 0x3B57: +case 0x3D57: +case 0x3F57: + +// MOVEW +case 0x3150: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3390: +case 0x3590: +case 0x3790: +case 0x3990: +case 0x3B90: +case 0x3D90: +case 0x3F90: +case 0x3191: +case 0x3391: +case 0x3591: +case 0x3791: +case 0x3991: +case 0x3B91: +case 0x3D91: +case 0x3F91: +case 0x3192: +case 0x3392: +case 0x3592: +case 0x3792: +case 0x3992: +case 0x3B92: +case 0x3D92: +case 0x3F92: +case 0x3193: +case 0x3393: +case 0x3593: +case 0x3793: +case 0x3993: +case 0x3B93: +case 0x3D93: +case 0x3F93: +case 0x3194: +case 0x3394: +case 0x3594: +case 0x3794: +case 0x3994: +case 0x3B94: +case 0x3D94: +case 0x3F94: +case 0x3195: +case 0x3395: +case 0x3595: +case 0x3795: +case 0x3995: +case 0x3B95: +case 0x3D95: +case 0x3F95: +case 0x3196: +case 0x3396: +case 0x3596: +case 0x3796: +case 0x3996: +case 0x3B96: +case 0x3D96: +case 0x3F96: +case 0x3197: +case 0x3397: +case 0x3597: +case 0x3797: +case 0x3997: +case 0x3B97: +case 0x3D97: +case 0x3F97: + +// MOVEW +case 0x3190: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x31D1: +case 0x31D2: +case 0x31D3: +case 0x31D4: +case 0x31D5: +case 0x31D6: +case 0x31D7: + +// MOVEW +case 0x31D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x33D1: +case 0x33D2: +case 0x33D3: +case 0x33D4: +case 0x33D5: +case 0x33D6: +case 0x33D7: + +// MOVEW +case 0x33D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x3ED1: +case 0x3ED2: +case 0x3ED3: +case 0x3ED4: +case 0x3ED5: +case 0x3ED6: +case 0x3ED7: + +// MOVEW +case 0x3ED0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3F11: +case 0x3F12: +case 0x3F13: +case 0x3F14: +case 0x3F15: +case 0x3F16: +case 0x3F17: + +// MOVEW +case 0x3F10: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3218: +case 0x3418: +case 0x3618: +case 0x3818: +case 0x3A18: +case 0x3C18: +case 0x3E18: +case 0x3019: +case 0x3219: +case 0x3419: +case 0x3619: +case 0x3819: +case 0x3A19: +case 0x3C19: +case 0x3E19: +case 0x301A: +case 0x321A: +case 0x341A: +case 0x361A: +case 0x381A: +case 0x3A1A: +case 0x3C1A: +case 0x3E1A: +case 0x301B: +case 0x321B: +case 0x341B: +case 0x361B: +case 0x381B: +case 0x3A1B: +case 0x3C1B: +case 0x3E1B: +case 0x301C: +case 0x321C: +case 0x341C: +case 0x361C: +case 0x381C: +case 0x3A1C: +case 0x3C1C: +case 0x3E1C: +case 0x301D: +case 0x321D: +case 0x341D: +case 0x361D: +case 0x381D: +case 0x3A1D: +case 0x3C1D: +case 0x3E1D: +case 0x301E: +case 0x321E: +case 0x341E: +case 0x361E: +case 0x381E: +case 0x3A1E: +case 0x3C1E: +case 0x3E1E: + +// MOVEW +case 0x3018: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x3298: +case 0x3498: +case 0x3698: +case 0x3898: +case 0x3A98: +case 0x3C98: +case 0x3E98: +case 0x3099: +case 0x3299: +case 0x3499: +case 0x3699: +case 0x3899: +case 0x3A99: +case 0x3C99: +case 0x3E99: +case 0x309A: +case 0x329A: +case 0x349A: +case 0x369A: +case 0x389A: +case 0x3A9A: +case 0x3C9A: +case 0x3E9A: +case 0x309B: +case 0x329B: +case 0x349B: +case 0x369B: +case 0x389B: +case 0x3A9B: +case 0x3C9B: +case 0x3E9B: +case 0x309C: +case 0x329C: +case 0x349C: +case 0x369C: +case 0x389C: +case 0x3A9C: +case 0x3C9C: +case 0x3E9C: +case 0x309D: +case 0x329D: +case 0x349D: +case 0x369D: +case 0x389D: +case 0x3A9D: +case 0x3C9D: +case 0x3E9D: +case 0x309E: +case 0x329E: +case 0x349E: +case 0x369E: +case 0x389E: +case 0x3A9E: +case 0x3C9E: +case 0x3E9E: + +// MOVEW +case 0x3098: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x32D8: +case 0x34D8: +case 0x36D8: +case 0x38D8: +case 0x3AD8: +case 0x3CD8: +case 0x30D9: +case 0x32D9: +case 0x34D9: +case 0x36D9: +case 0x38D9: +case 0x3AD9: +case 0x3CD9: +case 0x30DA: +case 0x32DA: +case 0x34DA: +case 0x36DA: +case 0x38DA: +case 0x3ADA: +case 0x3CDA: +case 0x30DB: +case 0x32DB: +case 0x34DB: +case 0x36DB: +case 0x38DB: +case 0x3ADB: +case 0x3CDB: +case 0x30DC: +case 0x32DC: +case 0x34DC: +case 0x36DC: +case 0x38DC: +case 0x3ADC: +case 0x3CDC: +case 0x30DD: +case 0x32DD: +case 0x34DD: +case 0x36DD: +case 0x38DD: +case 0x3ADD: +case 0x3CDD: +case 0x30DE: +case 0x32DE: +case 0x34DE: +case 0x36DE: +case 0x38DE: +case 0x3ADE: +case 0x3CDE: + +// MOVEW +case 0x30D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3318: +case 0x3518: +case 0x3718: +case 0x3918: +case 0x3B18: +case 0x3D18: +case 0x3119: +case 0x3319: +case 0x3519: +case 0x3719: +case 0x3919: +case 0x3B19: +case 0x3D19: +case 0x311A: +case 0x331A: +case 0x351A: +case 0x371A: +case 0x391A: +case 0x3B1A: +case 0x3D1A: +case 0x311B: +case 0x331B: +case 0x351B: +case 0x371B: +case 0x391B: +case 0x3B1B: +case 0x3D1B: +case 0x311C: +case 0x331C: +case 0x351C: +case 0x371C: +case 0x391C: +case 0x3B1C: +case 0x3D1C: +case 0x311D: +case 0x331D: +case 0x351D: +case 0x371D: +case 0x391D: +case 0x3B1D: +case 0x3D1D: +case 0x311E: +case 0x331E: +case 0x351E: +case 0x371E: +case 0x391E: +case 0x3B1E: +case 0x3D1E: + +// MOVEW +case 0x3118: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3358: +case 0x3558: +case 0x3758: +case 0x3958: +case 0x3B58: +case 0x3D58: +case 0x3F58: +case 0x3159: +case 0x3359: +case 0x3559: +case 0x3759: +case 0x3959: +case 0x3B59: +case 0x3D59: +case 0x3F59: +case 0x315A: +case 0x335A: +case 0x355A: +case 0x375A: +case 0x395A: +case 0x3B5A: +case 0x3D5A: +case 0x3F5A: +case 0x315B: +case 0x335B: +case 0x355B: +case 0x375B: +case 0x395B: +case 0x3B5B: +case 0x3D5B: +case 0x3F5B: +case 0x315C: +case 0x335C: +case 0x355C: +case 0x375C: +case 0x395C: +case 0x3B5C: +case 0x3D5C: +case 0x3F5C: +case 0x315D: +case 0x335D: +case 0x355D: +case 0x375D: +case 0x395D: +case 0x3B5D: +case 0x3D5D: +case 0x3F5D: +case 0x315E: +case 0x335E: +case 0x355E: +case 0x375E: +case 0x395E: +case 0x3B5E: +case 0x3D5E: +case 0x3F5E: + +// MOVEW +case 0x3158: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3398: +case 0x3598: +case 0x3798: +case 0x3998: +case 0x3B98: +case 0x3D98: +case 0x3F98: +case 0x3199: +case 0x3399: +case 0x3599: +case 0x3799: +case 0x3999: +case 0x3B99: +case 0x3D99: +case 0x3F99: +case 0x319A: +case 0x339A: +case 0x359A: +case 0x379A: +case 0x399A: +case 0x3B9A: +case 0x3D9A: +case 0x3F9A: +case 0x319B: +case 0x339B: +case 0x359B: +case 0x379B: +case 0x399B: +case 0x3B9B: +case 0x3D9B: +case 0x3F9B: +case 0x319C: +case 0x339C: +case 0x359C: +case 0x379C: +case 0x399C: +case 0x3B9C: +case 0x3D9C: +case 0x3F9C: +case 0x319D: +case 0x339D: +case 0x359D: +case 0x379D: +case 0x399D: +case 0x3B9D: +case 0x3D9D: +case 0x3F9D: +case 0x319E: +case 0x339E: +case 0x359E: +case 0x379E: +case 0x399E: +case 0x3B9E: +case 0x3D9E: +case 0x3F9E: + +// MOVEW +case 0x3198: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x31D9: +case 0x31DA: +case 0x31DB: +case 0x31DC: +case 0x31DD: +case 0x31DE: + +// MOVEW +case 0x31D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x33D9: +case 0x33DA: +case 0x33DB: +case 0x33DC: +case 0x33DD: +case 0x33DE: + +// MOVEW +case 0x33D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x3ED9: +case 0x3EDA: +case 0x3EDB: +case 0x3EDC: +case 0x3EDD: +case 0x3EDE: + +// MOVEW +case 0x3ED8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3F19: +case 0x3F1A: +case 0x3F1B: +case 0x3F1C: +case 0x3F1D: +case 0x3F1E: + +// MOVEW +case 0x3F18: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3220: +case 0x3420: +case 0x3620: +case 0x3820: +case 0x3A20: +case 0x3C20: +case 0x3E20: +case 0x3021: +case 0x3221: +case 0x3421: +case 0x3621: +case 0x3821: +case 0x3A21: +case 0x3C21: +case 0x3E21: +case 0x3022: +case 0x3222: +case 0x3422: +case 0x3622: +case 0x3822: +case 0x3A22: +case 0x3C22: +case 0x3E22: +case 0x3023: +case 0x3223: +case 0x3423: +case 0x3623: +case 0x3823: +case 0x3A23: +case 0x3C23: +case 0x3E23: +case 0x3024: +case 0x3224: +case 0x3424: +case 0x3624: +case 0x3824: +case 0x3A24: +case 0x3C24: +case 0x3E24: +case 0x3025: +case 0x3225: +case 0x3425: +case 0x3625: +case 0x3825: +case 0x3A25: +case 0x3C25: +case 0x3E25: +case 0x3026: +case 0x3226: +case 0x3426: +case 0x3626: +case 0x3826: +case 0x3A26: +case 0x3C26: +case 0x3E26: + +// MOVEW +case 0x3020: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x32A0: +case 0x34A0: +case 0x36A0: +case 0x38A0: +case 0x3AA0: +case 0x3CA0: +case 0x3EA0: +case 0x30A1: +case 0x32A1: +case 0x34A1: +case 0x36A1: +case 0x38A1: +case 0x3AA1: +case 0x3CA1: +case 0x3EA1: +case 0x30A2: +case 0x32A2: +case 0x34A2: +case 0x36A2: +case 0x38A2: +case 0x3AA2: +case 0x3CA2: +case 0x3EA2: +case 0x30A3: +case 0x32A3: +case 0x34A3: +case 0x36A3: +case 0x38A3: +case 0x3AA3: +case 0x3CA3: +case 0x3EA3: +case 0x30A4: +case 0x32A4: +case 0x34A4: +case 0x36A4: +case 0x38A4: +case 0x3AA4: +case 0x3CA4: +case 0x3EA4: +case 0x30A5: +case 0x32A5: +case 0x34A5: +case 0x36A5: +case 0x38A5: +case 0x3AA5: +case 0x3CA5: +case 0x3EA5: +case 0x30A6: +case 0x32A6: +case 0x34A6: +case 0x36A6: +case 0x38A6: +case 0x3AA6: +case 0x3CA6: +case 0x3EA6: + +// MOVEW +case 0x30A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x32E0: +case 0x34E0: +case 0x36E0: +case 0x38E0: +case 0x3AE0: +case 0x3CE0: +case 0x30E1: +case 0x32E1: +case 0x34E1: +case 0x36E1: +case 0x38E1: +case 0x3AE1: +case 0x3CE1: +case 0x30E2: +case 0x32E2: +case 0x34E2: +case 0x36E2: +case 0x38E2: +case 0x3AE2: +case 0x3CE2: +case 0x30E3: +case 0x32E3: +case 0x34E3: +case 0x36E3: +case 0x38E3: +case 0x3AE3: +case 0x3CE3: +case 0x30E4: +case 0x32E4: +case 0x34E4: +case 0x36E4: +case 0x38E4: +case 0x3AE4: +case 0x3CE4: +case 0x30E5: +case 0x32E5: +case 0x34E5: +case 0x36E5: +case 0x38E5: +case 0x3AE5: +case 0x3CE5: +case 0x30E6: +case 0x32E6: +case 0x34E6: +case 0x36E6: +case 0x38E6: +case 0x3AE6: +case 0x3CE6: + +// MOVEW +case 0x30E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3320: +case 0x3520: +case 0x3720: +case 0x3920: +case 0x3B20: +case 0x3D20: +case 0x3121: +case 0x3321: +case 0x3521: +case 0x3721: +case 0x3921: +case 0x3B21: +case 0x3D21: +case 0x3122: +case 0x3322: +case 0x3522: +case 0x3722: +case 0x3922: +case 0x3B22: +case 0x3D22: +case 0x3123: +case 0x3323: +case 0x3523: +case 0x3723: +case 0x3923: +case 0x3B23: +case 0x3D23: +case 0x3124: +case 0x3324: +case 0x3524: +case 0x3724: +case 0x3924: +case 0x3B24: +case 0x3D24: +case 0x3125: +case 0x3325: +case 0x3525: +case 0x3725: +case 0x3925: +case 0x3B25: +case 0x3D25: +case 0x3126: +case 0x3326: +case 0x3526: +case 0x3726: +case 0x3926: +case 0x3B26: +case 0x3D26: + +// MOVEW +case 0x3120: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3360: +case 0x3560: +case 0x3760: +case 0x3960: +case 0x3B60: +case 0x3D60: +case 0x3F60: +case 0x3161: +case 0x3361: +case 0x3561: +case 0x3761: +case 0x3961: +case 0x3B61: +case 0x3D61: +case 0x3F61: +case 0x3162: +case 0x3362: +case 0x3562: +case 0x3762: +case 0x3962: +case 0x3B62: +case 0x3D62: +case 0x3F62: +case 0x3163: +case 0x3363: +case 0x3563: +case 0x3763: +case 0x3963: +case 0x3B63: +case 0x3D63: +case 0x3F63: +case 0x3164: +case 0x3364: +case 0x3564: +case 0x3764: +case 0x3964: +case 0x3B64: +case 0x3D64: +case 0x3F64: +case 0x3165: +case 0x3365: +case 0x3565: +case 0x3765: +case 0x3965: +case 0x3B65: +case 0x3D65: +case 0x3F65: +case 0x3166: +case 0x3366: +case 0x3566: +case 0x3766: +case 0x3966: +case 0x3B66: +case 0x3D66: +case 0x3F66: + +// MOVEW +case 0x3160: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x33A0: +case 0x35A0: +case 0x37A0: +case 0x39A0: +case 0x3BA0: +case 0x3DA0: +case 0x3FA0: +case 0x31A1: +case 0x33A1: +case 0x35A1: +case 0x37A1: +case 0x39A1: +case 0x3BA1: +case 0x3DA1: +case 0x3FA1: +case 0x31A2: +case 0x33A2: +case 0x35A2: +case 0x37A2: +case 0x39A2: +case 0x3BA2: +case 0x3DA2: +case 0x3FA2: +case 0x31A3: +case 0x33A3: +case 0x35A3: +case 0x37A3: +case 0x39A3: +case 0x3BA3: +case 0x3DA3: +case 0x3FA3: +case 0x31A4: +case 0x33A4: +case 0x35A4: +case 0x37A4: +case 0x39A4: +case 0x3BA4: +case 0x3DA4: +case 0x3FA4: +case 0x31A5: +case 0x33A5: +case 0x35A5: +case 0x37A5: +case 0x39A5: +case 0x3BA5: +case 0x3DA5: +case 0x3FA5: +case 0x31A6: +case 0x33A6: +case 0x35A6: +case 0x37A6: +case 0x39A6: +case 0x3BA6: +case 0x3DA6: +case 0x3FA6: + +// MOVEW +case 0x31A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x31E1: +case 0x31E2: +case 0x31E3: +case 0x31E4: +case 0x31E5: +case 0x31E6: + +// MOVEW +case 0x31E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x33E1: +case 0x33E2: +case 0x33E3: +case 0x33E4: +case 0x33E5: +case 0x33E6: + +// MOVEW +case 0x33E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) +case 0x3EE1: +case 0x3EE2: +case 0x3EE3: +case 0x3EE4: +case 0x3EE5: +case 0x3EE6: + +// MOVEW +case 0x3EE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3F21: +case 0x3F22: +case 0x3F23: +case 0x3F24: +case 0x3F25: +case 0x3F26: + +// MOVEW +case 0x3F20: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3228: +case 0x3428: +case 0x3628: +case 0x3828: +case 0x3A28: +case 0x3C28: +case 0x3E28: +case 0x3029: +case 0x3229: +case 0x3429: +case 0x3629: +case 0x3829: +case 0x3A29: +case 0x3C29: +case 0x3E29: +case 0x302A: +case 0x322A: +case 0x342A: +case 0x362A: +case 0x382A: +case 0x3A2A: +case 0x3C2A: +case 0x3E2A: +case 0x302B: +case 0x322B: +case 0x342B: +case 0x362B: +case 0x382B: +case 0x3A2B: +case 0x3C2B: +case 0x3E2B: +case 0x302C: +case 0x322C: +case 0x342C: +case 0x362C: +case 0x382C: +case 0x3A2C: +case 0x3C2C: +case 0x3E2C: +case 0x302D: +case 0x322D: +case 0x342D: +case 0x362D: +case 0x382D: +case 0x3A2D: +case 0x3C2D: +case 0x3E2D: +case 0x302E: +case 0x322E: +case 0x342E: +case 0x362E: +case 0x382E: +case 0x3A2E: +case 0x3C2E: +case 0x3E2E: +case 0x302F: +case 0x322F: +case 0x342F: +case 0x362F: +case 0x382F: +case 0x3A2F: +case 0x3C2F: +case 0x3E2F: + +// MOVEW +case 0x3028: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x32A8: +case 0x34A8: +case 0x36A8: +case 0x38A8: +case 0x3AA8: +case 0x3CA8: +case 0x3EA8: +case 0x30A9: +case 0x32A9: +case 0x34A9: +case 0x36A9: +case 0x38A9: +case 0x3AA9: +case 0x3CA9: +case 0x3EA9: +case 0x30AA: +case 0x32AA: +case 0x34AA: +case 0x36AA: +case 0x38AA: +case 0x3AAA: +case 0x3CAA: +case 0x3EAA: +case 0x30AB: +case 0x32AB: +case 0x34AB: +case 0x36AB: +case 0x38AB: +case 0x3AAB: +case 0x3CAB: +case 0x3EAB: +case 0x30AC: +case 0x32AC: +case 0x34AC: +case 0x36AC: +case 0x38AC: +case 0x3AAC: +case 0x3CAC: +case 0x3EAC: +case 0x30AD: +case 0x32AD: +case 0x34AD: +case 0x36AD: +case 0x38AD: +case 0x3AAD: +case 0x3CAD: +case 0x3EAD: +case 0x30AE: +case 0x32AE: +case 0x34AE: +case 0x36AE: +case 0x38AE: +case 0x3AAE: +case 0x3CAE: +case 0x3EAE: +case 0x30AF: +case 0x32AF: +case 0x34AF: +case 0x36AF: +case 0x38AF: +case 0x3AAF: +case 0x3CAF: +case 0x3EAF: + +// MOVEW +case 0x30A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x32E8: +case 0x34E8: +case 0x36E8: +case 0x38E8: +case 0x3AE8: +case 0x3CE8: +case 0x30E9: +case 0x32E9: +case 0x34E9: +case 0x36E9: +case 0x38E9: +case 0x3AE9: +case 0x3CE9: +case 0x30EA: +case 0x32EA: +case 0x34EA: +case 0x36EA: +case 0x38EA: +case 0x3AEA: +case 0x3CEA: +case 0x30EB: +case 0x32EB: +case 0x34EB: +case 0x36EB: +case 0x38EB: +case 0x3AEB: +case 0x3CEB: +case 0x30EC: +case 0x32EC: +case 0x34EC: +case 0x36EC: +case 0x38EC: +case 0x3AEC: +case 0x3CEC: +case 0x30ED: +case 0x32ED: +case 0x34ED: +case 0x36ED: +case 0x38ED: +case 0x3AED: +case 0x3CED: +case 0x30EE: +case 0x32EE: +case 0x34EE: +case 0x36EE: +case 0x38EE: +case 0x3AEE: +case 0x3CEE: +case 0x30EF: +case 0x32EF: +case 0x34EF: +case 0x36EF: +case 0x38EF: +case 0x3AEF: +case 0x3CEF: + +// MOVEW +case 0x30E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3328: +case 0x3528: +case 0x3728: +case 0x3928: +case 0x3B28: +case 0x3D28: +case 0x3129: +case 0x3329: +case 0x3529: +case 0x3729: +case 0x3929: +case 0x3B29: +case 0x3D29: +case 0x312A: +case 0x332A: +case 0x352A: +case 0x372A: +case 0x392A: +case 0x3B2A: +case 0x3D2A: +case 0x312B: +case 0x332B: +case 0x352B: +case 0x372B: +case 0x392B: +case 0x3B2B: +case 0x3D2B: +case 0x312C: +case 0x332C: +case 0x352C: +case 0x372C: +case 0x392C: +case 0x3B2C: +case 0x3D2C: +case 0x312D: +case 0x332D: +case 0x352D: +case 0x372D: +case 0x392D: +case 0x3B2D: +case 0x3D2D: +case 0x312E: +case 0x332E: +case 0x352E: +case 0x372E: +case 0x392E: +case 0x3B2E: +case 0x3D2E: +case 0x312F: +case 0x332F: +case 0x352F: +case 0x372F: +case 0x392F: +case 0x3B2F: +case 0x3D2F: + +// MOVEW +case 0x3128: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3368: +case 0x3568: +case 0x3768: +case 0x3968: +case 0x3B68: +case 0x3D68: +case 0x3F68: +case 0x3169: +case 0x3369: +case 0x3569: +case 0x3769: +case 0x3969: +case 0x3B69: +case 0x3D69: +case 0x3F69: +case 0x316A: +case 0x336A: +case 0x356A: +case 0x376A: +case 0x396A: +case 0x3B6A: +case 0x3D6A: +case 0x3F6A: +case 0x316B: +case 0x336B: +case 0x356B: +case 0x376B: +case 0x396B: +case 0x3B6B: +case 0x3D6B: +case 0x3F6B: +case 0x316C: +case 0x336C: +case 0x356C: +case 0x376C: +case 0x396C: +case 0x3B6C: +case 0x3D6C: +case 0x3F6C: +case 0x316D: +case 0x336D: +case 0x356D: +case 0x376D: +case 0x396D: +case 0x3B6D: +case 0x3D6D: +case 0x3F6D: +case 0x316E: +case 0x336E: +case 0x356E: +case 0x376E: +case 0x396E: +case 0x3B6E: +case 0x3D6E: +case 0x3F6E: +case 0x316F: +case 0x336F: +case 0x356F: +case 0x376F: +case 0x396F: +case 0x3B6F: +case 0x3D6F: +case 0x3F6F: + +// MOVEW +case 0x3168: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x33A8: +case 0x35A8: +case 0x37A8: +case 0x39A8: +case 0x3BA8: +case 0x3DA8: +case 0x3FA8: +case 0x31A9: +case 0x33A9: +case 0x35A9: +case 0x37A9: +case 0x39A9: +case 0x3BA9: +case 0x3DA9: +case 0x3FA9: +case 0x31AA: +case 0x33AA: +case 0x35AA: +case 0x37AA: +case 0x39AA: +case 0x3BAA: +case 0x3DAA: +case 0x3FAA: +case 0x31AB: +case 0x33AB: +case 0x35AB: +case 0x37AB: +case 0x39AB: +case 0x3BAB: +case 0x3DAB: +case 0x3FAB: +case 0x31AC: +case 0x33AC: +case 0x35AC: +case 0x37AC: +case 0x39AC: +case 0x3BAC: +case 0x3DAC: +case 0x3FAC: +case 0x31AD: +case 0x33AD: +case 0x35AD: +case 0x37AD: +case 0x39AD: +case 0x3BAD: +case 0x3DAD: +case 0x3FAD: +case 0x31AE: +case 0x33AE: +case 0x35AE: +case 0x37AE: +case 0x39AE: +case 0x3BAE: +case 0x3DAE: +case 0x3FAE: +case 0x31AF: +case 0x33AF: +case 0x35AF: +case 0x37AF: +case 0x39AF: +case 0x3BAF: +case 0x3DAF: +case 0x3FAF: + +// MOVEW +case 0x31A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) +case 0x31E9: +case 0x31EA: +case 0x31EB: +case 0x31EC: +case 0x31ED: +case 0x31EE: +case 0x31EF: + +// MOVEW +case 0x31E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x33E9: +case 0x33EA: +case 0x33EB: +case 0x33EC: +case 0x33ED: +case 0x33EE: +case 0x33EF: + +// MOVEW +case 0x33E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) +case 0x3EE9: +case 0x3EEA: +case 0x3EEB: +case 0x3EEC: +case 0x3EED: +case 0x3EEE: +case 0x3EEF: + +// MOVEW +case 0x3EE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3F29: +case 0x3F2A: +case 0x3F2B: +case 0x3F2C: +case 0x3F2D: +case 0x3F2E: +case 0x3F2F: + +// MOVEW +case 0x3F28: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3230: +case 0x3430: +case 0x3630: +case 0x3830: +case 0x3A30: +case 0x3C30: +case 0x3E30: +case 0x3031: +case 0x3231: +case 0x3431: +case 0x3631: +case 0x3831: +case 0x3A31: +case 0x3C31: +case 0x3E31: +case 0x3032: +case 0x3232: +case 0x3432: +case 0x3632: +case 0x3832: +case 0x3A32: +case 0x3C32: +case 0x3E32: +case 0x3033: +case 0x3233: +case 0x3433: +case 0x3633: +case 0x3833: +case 0x3A33: +case 0x3C33: +case 0x3E33: +case 0x3034: +case 0x3234: +case 0x3434: +case 0x3634: +case 0x3834: +case 0x3A34: +case 0x3C34: +case 0x3E34: +case 0x3035: +case 0x3235: +case 0x3435: +case 0x3635: +case 0x3835: +case 0x3A35: +case 0x3C35: +case 0x3E35: +case 0x3036: +case 0x3236: +case 0x3436: +case 0x3636: +case 0x3836: +case 0x3A36: +case 0x3C36: +case 0x3E36: +case 0x3037: +case 0x3237: +case 0x3437: +case 0x3637: +case 0x3837: +case 0x3A37: +case 0x3C37: +case 0x3E37: + +// MOVEW +case 0x3030: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x32B0: +case 0x34B0: +case 0x36B0: +case 0x38B0: +case 0x3AB0: +case 0x3CB0: +case 0x3EB0: +case 0x30B1: +case 0x32B1: +case 0x34B1: +case 0x36B1: +case 0x38B1: +case 0x3AB1: +case 0x3CB1: +case 0x3EB1: +case 0x30B2: +case 0x32B2: +case 0x34B2: +case 0x36B2: +case 0x38B2: +case 0x3AB2: +case 0x3CB2: +case 0x3EB2: +case 0x30B3: +case 0x32B3: +case 0x34B3: +case 0x36B3: +case 0x38B3: +case 0x3AB3: +case 0x3CB3: +case 0x3EB3: +case 0x30B4: +case 0x32B4: +case 0x34B4: +case 0x36B4: +case 0x38B4: +case 0x3AB4: +case 0x3CB4: +case 0x3EB4: +case 0x30B5: +case 0x32B5: +case 0x34B5: +case 0x36B5: +case 0x38B5: +case 0x3AB5: +case 0x3CB5: +case 0x3EB5: +case 0x30B6: +case 0x32B6: +case 0x34B6: +case 0x36B6: +case 0x38B6: +case 0x3AB6: +case 0x3CB6: +case 0x3EB6: +case 0x30B7: +case 0x32B7: +case 0x34B7: +case 0x36B7: +case 0x38B7: +case 0x3AB7: +case 0x3CB7: +case 0x3EB7: + +// MOVEW +case 0x30B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x32F0: +case 0x34F0: +case 0x36F0: +case 0x38F0: +case 0x3AF0: +case 0x3CF0: +case 0x30F1: +case 0x32F1: +case 0x34F1: +case 0x36F1: +case 0x38F1: +case 0x3AF1: +case 0x3CF1: +case 0x30F2: +case 0x32F2: +case 0x34F2: +case 0x36F2: +case 0x38F2: +case 0x3AF2: +case 0x3CF2: +case 0x30F3: +case 0x32F3: +case 0x34F3: +case 0x36F3: +case 0x38F3: +case 0x3AF3: +case 0x3CF3: +case 0x30F4: +case 0x32F4: +case 0x34F4: +case 0x36F4: +case 0x38F4: +case 0x3AF4: +case 0x3CF4: +case 0x30F5: +case 0x32F5: +case 0x34F5: +case 0x36F5: +case 0x38F5: +case 0x3AF5: +case 0x3CF5: +case 0x30F6: +case 0x32F6: +case 0x34F6: +case 0x36F6: +case 0x38F6: +case 0x3AF6: +case 0x3CF6: +case 0x30F7: +case 0x32F7: +case 0x34F7: +case 0x36F7: +case 0x38F7: +case 0x3AF7: +case 0x3CF7: + +// MOVEW +case 0x30F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x3330: +case 0x3530: +case 0x3730: +case 0x3930: +case 0x3B30: +case 0x3D30: +case 0x3131: +case 0x3331: +case 0x3531: +case 0x3731: +case 0x3931: +case 0x3B31: +case 0x3D31: +case 0x3132: +case 0x3332: +case 0x3532: +case 0x3732: +case 0x3932: +case 0x3B32: +case 0x3D32: +case 0x3133: +case 0x3333: +case 0x3533: +case 0x3733: +case 0x3933: +case 0x3B33: +case 0x3D33: +case 0x3134: +case 0x3334: +case 0x3534: +case 0x3734: +case 0x3934: +case 0x3B34: +case 0x3D34: +case 0x3135: +case 0x3335: +case 0x3535: +case 0x3735: +case 0x3935: +case 0x3B35: +case 0x3D35: +case 0x3136: +case 0x3336: +case 0x3536: +case 0x3736: +case 0x3936: +case 0x3B36: +case 0x3D36: +case 0x3137: +case 0x3337: +case 0x3537: +case 0x3737: +case 0x3937: +case 0x3B37: +case 0x3D37: + +// MOVEW +case 0x3130: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x3370: +case 0x3570: +case 0x3770: +case 0x3970: +case 0x3B70: +case 0x3D70: +case 0x3F70: +case 0x3171: +case 0x3371: +case 0x3571: +case 0x3771: +case 0x3971: +case 0x3B71: +case 0x3D71: +case 0x3F71: +case 0x3172: +case 0x3372: +case 0x3572: +case 0x3772: +case 0x3972: +case 0x3B72: +case 0x3D72: +case 0x3F72: +case 0x3173: +case 0x3373: +case 0x3573: +case 0x3773: +case 0x3973: +case 0x3B73: +case 0x3D73: +case 0x3F73: +case 0x3174: +case 0x3374: +case 0x3574: +case 0x3774: +case 0x3974: +case 0x3B74: +case 0x3D74: +case 0x3F74: +case 0x3175: +case 0x3375: +case 0x3575: +case 0x3775: +case 0x3975: +case 0x3B75: +case 0x3D75: +case 0x3F75: +case 0x3176: +case 0x3376: +case 0x3576: +case 0x3776: +case 0x3976: +case 0x3B76: +case 0x3D76: +case 0x3F76: +case 0x3177: +case 0x3377: +case 0x3577: +case 0x3777: +case 0x3977: +case 0x3B77: +case 0x3D77: +case 0x3F77: + +// MOVEW +case 0x3170: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) +case 0x33B0: +case 0x35B0: +case 0x37B0: +case 0x39B0: +case 0x3BB0: +case 0x3DB0: +case 0x3FB0: +case 0x31B1: +case 0x33B1: +case 0x35B1: +case 0x37B1: +case 0x39B1: +case 0x3BB1: +case 0x3DB1: +case 0x3FB1: +case 0x31B2: +case 0x33B2: +case 0x35B2: +case 0x37B2: +case 0x39B2: +case 0x3BB2: +case 0x3DB2: +case 0x3FB2: +case 0x31B3: +case 0x33B3: +case 0x35B3: +case 0x37B3: +case 0x39B3: +case 0x3BB3: +case 0x3DB3: +case 0x3FB3: +case 0x31B4: +case 0x33B4: +case 0x35B4: +case 0x37B4: +case 0x39B4: +case 0x3BB4: +case 0x3DB4: +case 0x3FB4: +case 0x31B5: +case 0x33B5: +case 0x35B5: +case 0x37B5: +case 0x39B5: +case 0x3BB5: +case 0x3DB5: +case 0x3FB5: +case 0x31B6: +case 0x33B6: +case 0x35B6: +case 0x37B6: +case 0x39B6: +case 0x3BB6: +case 0x3DB6: +case 0x3FB6: +case 0x31B7: +case 0x33B7: +case 0x35B7: +case 0x37B7: +case 0x39B7: +case 0x3BB7: +case 0x3DB7: +case 0x3FB7: + +// MOVEW +case 0x31B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) +case 0x31F1: +case 0x31F2: +case 0x31F3: +case 0x31F4: +case 0x31F5: +case 0x31F6: +case 0x31F7: + +// MOVEW +case 0x31F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) +case 0x33F1: +case 0x33F2: +case 0x33F3: +case 0x33F4: +case 0x33F5: +case 0x33F6: +case 0x33F7: + +// MOVEW +case 0x33F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(26) +case 0x3EF1: +case 0x3EF2: +case 0x3EF3: +case 0x3EF4: +case 0x3EF5: +case 0x3EF6: +case 0x3EF7: + +// MOVEW +case 0x3EF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x3F31: +case 0x3F32: +case 0x3F33: +case 0x3F34: +case 0x3F35: +case 0x3F36: +case 0x3F37: + +// MOVEW +case 0x3F30: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x3238: +case 0x3438: +case 0x3638: +case 0x3838: +case 0x3A38: +case 0x3C38: +case 0x3E38: + +// MOVEW +case 0x3038: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x32B8: +case 0x34B8: +case 0x36B8: +case 0x38B8: +case 0x3AB8: +case 0x3CB8: +case 0x3EB8: + +// MOVEW +case 0x30B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x32F8: +case 0x34F8: +case 0x36F8: +case 0x38F8: +case 0x3AF8: +case 0x3CF8: + +// MOVEW +case 0x30F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3338: +case 0x3538: +case 0x3738: +case 0x3938: +case 0x3B38: +case 0x3D38: + +// MOVEW +case 0x3138: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3378: +case 0x3578: +case 0x3778: +case 0x3978: +case 0x3B78: +case 0x3D78: +case 0x3F78: + +// MOVEW +case 0x3178: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x33B8: +case 0x35B8: +case 0x37B8: +case 0x39B8: +case 0x3BB8: +case 0x3DB8: +case 0x3FB8: + +// MOVEW +case 0x31B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// MOVEW +case 0x31F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x33F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// MOVEW +case 0x3EF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// MOVEW +case 0x3F38: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x3239: +case 0x3439: +case 0x3639: +case 0x3839: +case 0x3A39: +case 0x3C39: +case 0x3E39: + +// MOVEW +case 0x3039: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x32B9: +case 0x34B9: +case 0x36B9: +case 0x38B9: +case 0x3AB9: +case 0x3CB9: +case 0x3EB9: + +// MOVEW +case 0x30B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x32F9: +case 0x34F9: +case 0x36F9: +case 0x38F9: +case 0x3AF9: +case 0x3CF9: + +// MOVEW +case 0x30F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x3339: +case 0x3539: +case 0x3739: +case 0x3939: +case 0x3B39: +case 0x3D39: + +// MOVEW +case 0x3139: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x3379: +case 0x3579: +case 0x3779: +case 0x3979: +case 0x3B79: +case 0x3D79: +case 0x3F79: + +// MOVEW +case 0x3179: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) +case 0x33B9: +case 0x35B9: +case 0x37B9: +case 0x39B9: +case 0x3BB9: +case 0x3DB9: +case 0x3FB9: + +// MOVEW +case 0x31B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(26) + +// MOVEW +case 0x31F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// MOVEW +case 0x33F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(28) + +// MOVEW +case 0x3EF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x3F39: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x323A: +case 0x343A: +case 0x363A: +case 0x383A: +case 0x3A3A: +case 0x3C3A: +case 0x3E3A: + +// MOVEW +case 0x303A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x32BA: +case 0x34BA: +case 0x36BA: +case 0x38BA: +case 0x3ABA: +case 0x3CBA: +case 0x3EBA: + +// MOVEW +case 0x30BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x32FA: +case 0x34FA: +case 0x36FA: +case 0x38FA: +case 0x3AFA: +case 0x3CFA: + +// MOVEW +case 0x30FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x333A: +case 0x353A: +case 0x373A: +case 0x393A: +case 0x3B3A: +case 0x3D3A: + +// MOVEW +case 0x313A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x337A: +case 0x357A: +case 0x377A: +case 0x397A: +case 0x3B7A: +case 0x3D7A: +case 0x3F7A: + +// MOVEW +case 0x317A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x33BA: +case 0x35BA: +case 0x37BA: +case 0x39BA: +case 0x3BBA: +case 0x3DBA: +case 0x3FBA: + +// MOVEW +case 0x31BA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// MOVEW +case 0x31FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x33FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// MOVEW +case 0x3EFA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// MOVEW +case 0x3F3A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x323B: +case 0x343B: +case 0x363B: +case 0x383B: +case 0x3A3B: +case 0x3C3B: +case 0x3E3B: + +// MOVEW +case 0x303B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x32BB: +case 0x34BB: +case 0x36BB: +case 0x38BB: +case 0x3ABB: +case 0x3CBB: +case 0x3EBB: + +// MOVEW +case 0x30BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x32FB: +case 0x34FB: +case 0x36FB: +case 0x38FB: +case 0x3AFB: +case 0x3CFB: + +// MOVEW +case 0x30FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x333B: +case 0x353B: +case 0x373B: +case 0x393B: +case 0x3B3B: +case 0x3D3B: + +// MOVEW +case 0x313B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x337B: +case 0x357B: +case 0x377B: +case 0x397B: +case 0x3B7B: +case 0x3D7B: +case 0x3F7B: + +// MOVEW +case 0x317B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) +case 0x33BB: +case 0x35BB: +case 0x37BB: +case 0x39BB: +case 0x3BBB: +case 0x3DBB: +case 0x3FBB: + +// MOVEW +case 0x31BB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(24) + +// MOVEW +case 0x31FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// MOVEW +case 0x33FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(26) + +// MOVEW +case 0x3EFB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// MOVEW +case 0x3F3B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x323C: +case 0x343C: +case 0x363C: +case 0x383C: +case 0x3A3C: +case 0x3C3C: +case 0x3E3C: + +// MOVEW +case 0x303C: +{ + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x32BC: +case 0x34BC: +case 0x36BC: +case 0x38BC: +case 0x3ABC: +case 0x3CBC: +case 0x3EBC: + +// MOVEW +case 0x30BC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x32FC: +case 0x34FC: +case 0x36FC: +case 0x38FC: +case 0x3AFC: +case 0x3CFC: + +// MOVEW +case 0x30FC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x333C: +case 0x353C: +case 0x373C: +case 0x393C: +case 0x3B3C: +case 0x3D3C: + +// MOVEW +case 0x313C: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x337C: +case 0x357C: +case 0x377C: +case 0x397C: +case 0x3B7C: +case 0x3D7C: +case 0x3F7C: + +// MOVEW +case 0x317C: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x33BC: +case 0x35BC: +case 0x37BC: +case 0x39BC: +case 0x3BBC: +case 0x3DBC: +case 0x3FBC: + +// MOVEW +case 0x31BC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// MOVEW +case 0x31FC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// MOVEW +case 0x33FC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x3EFC: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// MOVEW +case 0x3F3C: +{ + u32 adr; + u32 res; + res = FETCH_WORD; + PC += 2; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x321F: +case 0x341F: +case 0x361F: +case 0x381F: +case 0x3A1F: +case 0x3C1F: +case 0x3E1F: + +// MOVEW +case 0x301F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x329F: +case 0x349F: +case 0x369F: +case 0x389F: +case 0x3A9F: +case 0x3C9F: +case 0x3E9F: + +// MOVEW +case 0x309F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x32DF: +case 0x34DF: +case 0x36DF: +case 0x38DF: +case 0x3ADF: +case 0x3CDF: + +// MOVEW +case 0x30DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x331F: +case 0x351F: +case 0x371F: +case 0x391F: +case 0x3B1F: +case 0x3D1F: + +// MOVEW +case 0x311F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x335F: +case 0x355F: +case 0x375F: +case 0x395F: +case 0x3B5F: +case 0x3D5F: +case 0x3F5F: + +// MOVEW +case 0x315F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x339F: +case 0x359F: +case 0x379F: +case 0x399F: +case 0x3B9F: +case 0x3D9F: +case 0x3F9F: + +// MOVEW +case 0x319F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// MOVEW +case 0x31DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// MOVEW +case 0x33DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x3EDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// MOVEW +case 0x3F1F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x3227: +case 0x3427: +case 0x3627: +case 0x3827: +case 0x3A27: +case 0x3C27: +case 0x3E27: + +// MOVEW +case 0x3027: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x32A7: +case 0x34A7: +case 0x36A7: +case 0x38A7: +case 0x3AA7: +case 0x3CA7: +case 0x3EA7: + +// MOVEW +case 0x30A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x32E7: +case 0x34E7: +case 0x36E7: +case 0x38E7: +case 0x3AE7: +case 0x3CE7: + +// MOVEW +case 0x30E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3327: +case 0x3527: +case 0x3727: +case 0x3927: +case 0x3B27: +case 0x3D27: + +// MOVEW +case 0x3127: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3367: +case 0x3567: +case 0x3767: +case 0x3967: +case 0x3B67: +case 0x3D67: +case 0x3F67: + +// MOVEW +case 0x3167: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x33A7: +case 0x35A7: +case 0x37A7: +case 0x39A7: +case 0x3BA7: +case 0x3DA7: +case 0x3FA7: + +// MOVEW +case 0x31A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[(Opcode >> 9) & 7]; + DECODE_EXT_WORD + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVEW +case 0x31E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// MOVEW +case 0x33E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = (s32)FETCH_LONG; + PC += 4; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(22) + +// MOVEW +case 0x3EE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7]; + CPU->A[7] += 2; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) + +// MOVEW +case 0x3F27: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x3240: +case 0x3440: +case 0x3640: +case 0x3840: +case 0x3A40: +case 0x3C40: +case 0x3E40: +case 0x3041: +case 0x3241: +case 0x3441: +case 0x3641: +case 0x3841: +case 0x3A41: +case 0x3C41: +case 0x3E41: +case 0x3042: +case 0x3242: +case 0x3442: +case 0x3642: +case 0x3842: +case 0x3A42: +case 0x3C42: +case 0x3E42: +case 0x3043: +case 0x3243: +case 0x3443: +case 0x3643: +case 0x3843: +case 0x3A43: +case 0x3C43: +case 0x3E43: +case 0x3044: +case 0x3244: +case 0x3444: +case 0x3644: +case 0x3844: +case 0x3A44: +case 0x3C44: +case 0x3E44: +case 0x3045: +case 0x3245: +case 0x3445: +case 0x3645: +case 0x3845: +case 0x3A45: +case 0x3C45: +case 0x3E45: +case 0x3046: +case 0x3246: +case 0x3446: +case 0x3646: +case 0x3846: +case 0x3A46: +case 0x3C46: +case 0x3E46: +case 0x3047: +case 0x3247: +case 0x3447: +case 0x3647: +case 0x3847: +case 0x3A47: +case 0x3C47: +case 0x3E47: + +// MOVEAW +case 0x3040: +{ + u32 res; + res = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(4) +case 0x3248: +case 0x3448: +case 0x3648: +case 0x3848: +case 0x3A48: +case 0x3C48: +case 0x3E48: +case 0x3049: +case 0x3249: +case 0x3449: +case 0x3649: +case 0x3849: +case 0x3A49: +case 0x3C49: +case 0x3E49: +case 0x304A: +case 0x324A: +case 0x344A: +case 0x364A: +case 0x384A: +case 0x3A4A: +case 0x3C4A: +case 0x3E4A: +case 0x304B: +case 0x324B: +case 0x344B: +case 0x364B: +case 0x384B: +case 0x3A4B: +case 0x3C4B: +case 0x3E4B: +case 0x304C: +case 0x324C: +case 0x344C: +case 0x364C: +case 0x384C: +case 0x3A4C: +case 0x3C4C: +case 0x3E4C: +case 0x304D: +case 0x324D: +case 0x344D: +case 0x364D: +case 0x384D: +case 0x3A4D: +case 0x3C4D: +case 0x3E4D: +case 0x304E: +case 0x324E: +case 0x344E: +case 0x364E: +case 0x384E: +case 0x3A4E: +case 0x3C4E: +case 0x3E4E: +case 0x304F: +case 0x324F: +case 0x344F: +case 0x364F: +case 0x384F: +case 0x3A4F: +case 0x3C4F: +case 0x3E4F: + +// MOVEAW +case 0x3048: +{ + u32 res; + res = (s32)(s16)CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(4) +case 0x3250: +case 0x3450: +case 0x3650: +case 0x3850: +case 0x3A50: +case 0x3C50: +case 0x3E50: +case 0x3051: +case 0x3251: +case 0x3451: +case 0x3651: +case 0x3851: +case 0x3A51: +case 0x3C51: +case 0x3E51: +case 0x3052: +case 0x3252: +case 0x3452: +case 0x3652: +case 0x3852: +case 0x3A52: +case 0x3C52: +case 0x3E52: +case 0x3053: +case 0x3253: +case 0x3453: +case 0x3653: +case 0x3853: +case 0x3A53: +case 0x3C53: +case 0x3E53: +case 0x3054: +case 0x3254: +case 0x3454: +case 0x3654: +case 0x3854: +case 0x3A54: +case 0x3C54: +case 0x3E54: +case 0x3055: +case 0x3255: +case 0x3455: +case 0x3655: +case 0x3855: +case 0x3A55: +case 0x3C55: +case 0x3E55: +case 0x3056: +case 0x3256: +case 0x3456: +case 0x3656: +case 0x3856: +case 0x3A56: +case 0x3C56: +case 0x3E56: +case 0x3057: +case 0x3257: +case 0x3457: +case 0x3657: +case 0x3857: +case 0x3A57: +case 0x3C57: +case 0x3E57: + +// MOVEAW +case 0x3050: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(8) +case 0x3258: +case 0x3458: +case 0x3658: +case 0x3858: +case 0x3A58: +case 0x3C58: +case 0x3E58: +case 0x3059: +case 0x3259: +case 0x3459: +case 0x3659: +case 0x3859: +case 0x3A59: +case 0x3C59: +case 0x3E59: +case 0x305A: +case 0x325A: +case 0x345A: +case 0x365A: +case 0x385A: +case 0x3A5A: +case 0x3C5A: +case 0x3E5A: +case 0x305B: +case 0x325B: +case 0x345B: +case 0x365B: +case 0x385B: +case 0x3A5B: +case 0x3C5B: +case 0x3E5B: +case 0x305C: +case 0x325C: +case 0x345C: +case 0x365C: +case 0x385C: +case 0x3A5C: +case 0x3C5C: +case 0x3E5C: +case 0x305D: +case 0x325D: +case 0x345D: +case 0x365D: +case 0x385D: +case 0x3A5D: +case 0x3C5D: +case 0x3E5D: +case 0x305E: +case 0x325E: +case 0x345E: +case 0x365E: +case 0x385E: +case 0x3A5E: +case 0x3C5E: +case 0x3E5E: + +// MOVEAW +case 0x3058: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(8) +case 0x3260: +case 0x3460: +case 0x3660: +case 0x3860: +case 0x3A60: +case 0x3C60: +case 0x3E60: +case 0x3061: +case 0x3261: +case 0x3461: +case 0x3661: +case 0x3861: +case 0x3A61: +case 0x3C61: +case 0x3E61: +case 0x3062: +case 0x3262: +case 0x3462: +case 0x3662: +case 0x3862: +case 0x3A62: +case 0x3C62: +case 0x3E62: +case 0x3063: +case 0x3263: +case 0x3463: +case 0x3663: +case 0x3863: +case 0x3A63: +case 0x3C63: +case 0x3E63: +case 0x3064: +case 0x3264: +case 0x3464: +case 0x3664: +case 0x3864: +case 0x3A64: +case 0x3C64: +case 0x3E64: +case 0x3065: +case 0x3265: +case 0x3465: +case 0x3665: +case 0x3865: +case 0x3A65: +case 0x3C65: +case 0x3E65: +case 0x3066: +case 0x3266: +case 0x3466: +case 0x3666: +case 0x3866: +case 0x3A66: +case 0x3C66: +case 0x3E66: + +// MOVEAW +case 0x3060: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(10) +case 0x3268: +case 0x3468: +case 0x3668: +case 0x3868: +case 0x3A68: +case 0x3C68: +case 0x3E68: +case 0x3069: +case 0x3269: +case 0x3469: +case 0x3669: +case 0x3869: +case 0x3A69: +case 0x3C69: +case 0x3E69: +case 0x306A: +case 0x326A: +case 0x346A: +case 0x366A: +case 0x386A: +case 0x3A6A: +case 0x3C6A: +case 0x3E6A: +case 0x306B: +case 0x326B: +case 0x346B: +case 0x366B: +case 0x386B: +case 0x3A6B: +case 0x3C6B: +case 0x3E6B: +case 0x306C: +case 0x326C: +case 0x346C: +case 0x366C: +case 0x386C: +case 0x3A6C: +case 0x3C6C: +case 0x3E6C: +case 0x306D: +case 0x326D: +case 0x346D: +case 0x366D: +case 0x386D: +case 0x3A6D: +case 0x3C6D: +case 0x3E6D: +case 0x306E: +case 0x326E: +case 0x346E: +case 0x366E: +case 0x386E: +case 0x3A6E: +case 0x3C6E: +case 0x3E6E: +case 0x306F: +case 0x326F: +case 0x346F: +case 0x366F: +case 0x386F: +case 0x3A6F: +case 0x3C6F: +case 0x3E6F: + +// MOVEAW +case 0x3068: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x3270: +case 0x3470: +case 0x3670: +case 0x3870: +case 0x3A70: +case 0x3C70: +case 0x3E70: +case 0x3071: +case 0x3271: +case 0x3471: +case 0x3671: +case 0x3871: +case 0x3A71: +case 0x3C71: +case 0x3E71: +case 0x3072: +case 0x3272: +case 0x3472: +case 0x3672: +case 0x3872: +case 0x3A72: +case 0x3C72: +case 0x3E72: +case 0x3073: +case 0x3273: +case 0x3473: +case 0x3673: +case 0x3873: +case 0x3A73: +case 0x3C73: +case 0x3E73: +case 0x3074: +case 0x3274: +case 0x3474: +case 0x3674: +case 0x3874: +case 0x3A74: +case 0x3C74: +case 0x3E74: +case 0x3075: +case 0x3275: +case 0x3475: +case 0x3675: +case 0x3875: +case 0x3A75: +case 0x3C75: +case 0x3E75: +case 0x3076: +case 0x3276: +case 0x3476: +case 0x3676: +case 0x3876: +case 0x3A76: +case 0x3C76: +case 0x3E76: +case 0x3077: +case 0x3277: +case 0x3477: +case 0x3677: +case 0x3877: +case 0x3A77: +case 0x3C77: +case 0x3E77: + +// MOVEAW +case 0x3070: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0x3278: +case 0x3478: +case 0x3678: +case 0x3878: +case 0x3A78: +case 0x3C78: +case 0x3E78: + +// MOVEAW +case 0x3078: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x3279: +case 0x3479: +case 0x3679: +case 0x3879: +case 0x3A79: +case 0x3C79: +case 0x3E79: + +// MOVEAW +case 0x3079: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x327A: +case 0x347A: +case 0x367A: +case 0x387A: +case 0x3A7A: +case 0x3C7A: +case 0x3E7A: + +// MOVEAW +case 0x307A: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x327B: +case 0x347B: +case 0x367B: +case 0x387B: +case 0x3A7B: +case 0x3C7B: +case 0x3E7B: + +// MOVEAW +case 0x307B: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0x327C: +case 0x347C: +case 0x367C: +case 0x387C: +case 0x3A7C: +case 0x3C7C: +case 0x3E7C: + +// MOVEAW +case 0x307C: +{ + u32 res; + res = (s32)(s16)FETCH_WORD; + PC += 2; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x325F: +case 0x345F: +case 0x365F: +case 0x385F: +case 0x3A5F: +case 0x3C5F: +case 0x3E5F: + +// MOVEAW +case 0x305F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(8) +case 0x3267: +case 0x3467: +case 0x3667: +case 0x3867: +case 0x3A67: +case 0x3C67: +case 0x3E67: + +// MOVEAW +case 0x3067: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, res) + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(10) diff --git a/yabause/src/c68k/c68k_op4.inc b/yabause/src/c68k/c68k_op4.inc new file mode 100644 index 0000000000..156122a392 --- /dev/null +++ b/yabause/src/c68k/c68k_op4.inc @@ -0,0 +1,7508 @@ +case 0x4001: +case 0x4002: +case 0x4003: +case 0x4004: +case 0x4005: +case 0x4006: +case 0x4007: + +// NEGX +case 0x4000: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4011: +case 0x4012: +case 0x4013: +case 0x4014: +case 0x4015: +case 0x4016: +case 0x4017: + +// NEGX +case 0x4010: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4019: +case 0x401A: +case 0x401B: +case 0x401C: +case 0x401D: +case 0x401E: + +// NEGX +case 0x4018: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4021: +case 0x4022: +case 0x4023: +case 0x4024: +case 0x4025: +case 0x4026: + +// NEGX +case 0x4020: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4029: +case 0x402A: +case 0x402B: +case 0x402C: +case 0x402D: +case 0x402E: +case 0x402F: + +// NEGX +case 0x4028: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x4031: +case 0x4032: +case 0x4033: +case 0x4034: +case 0x4035: +case 0x4036: +case 0x4037: + +// NEGX +case 0x4030: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// NEGX +case 0x4038: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// NEGX +case 0x4039: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// NEGX +case 0x401F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// NEGX +case 0x4027: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4041: +case 0x4042: +case 0x4043: +case 0x4044: +case 0x4045: +case 0x4046: +case 0x4047: + +// NEGX +case 0x4040: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4051: +case 0x4052: +case 0x4053: +case 0x4054: +case 0x4055: +case 0x4056: +case 0x4057: + +// NEGX +case 0x4050: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4059: +case 0x405A: +case 0x405B: +case 0x405C: +case 0x405D: +case 0x405E: + +// NEGX +case 0x4058: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4061: +case 0x4062: +case 0x4063: +case 0x4064: +case 0x4065: +case 0x4066: + +// NEGX +case 0x4060: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4069: +case 0x406A: +case 0x406B: +case 0x406C: +case 0x406D: +case 0x406E: +case 0x406F: + +// NEGX +case 0x4068: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x4071: +case 0x4072: +case 0x4073: +case 0x4074: +case 0x4075: +case 0x4076: +case 0x4077: + +// NEGX +case 0x4070: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// NEGX +case 0x4078: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// NEGX +case 0x4079: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// NEGX +case 0x405F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// NEGX +case 0x4067: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4081: +case 0x4082: +case 0x4083: +case 0x4084: +case 0x4085: +case 0x4086: +case 0x4087: + +// NEGX +case 0x4080: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0x4091: +case 0x4092: +case 0x4093: +case 0x4094: +case 0x4095: +case 0x4096: +case 0x4097: + +// NEGX +case 0x4090: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x4099: +case 0x409A: +case 0x409B: +case 0x409C: +case 0x409D: +case 0x409E: + +// NEGX +case 0x4098: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x40A1: +case 0x40A2: +case 0x40A3: +case 0x40A4: +case 0x40A5: +case 0x40A6: + +// NEGX +case 0x40A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x40A9: +case 0x40AA: +case 0x40AB: +case 0x40AC: +case 0x40AD: +case 0x40AE: +case 0x40AF: + +// NEGX +case 0x40A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x40B1: +case 0x40B2: +case 0x40B3: +case 0x40B4: +case 0x40B5: +case 0x40B6: +case 0x40B7: + +// NEGX +case 0x40B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// NEGX +case 0x40B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// NEGX +case 0x40B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// NEGX +case 0x409F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// NEGX +case 0x40A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = -src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x4201: +case 0x4202: +case 0x4203: +case 0x4204: +case 0x4205: +case 0x4206: +case 0x4207: + +// CLR +case 0x4200: +{ + u32 res; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4211: +case 0x4212: +case 0x4213: +case 0x4214: +case 0x4215: +case 0x4216: +case 0x4217: + +// CLR +case 0x4210: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4219: +case 0x421A: +case 0x421B: +case 0x421C: +case 0x421D: +case 0x421E: + +// CLR +case 0x4218: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4221: +case 0x4222: +case 0x4223: +case 0x4224: +case 0x4225: +case 0x4226: + +// CLR +case 0x4220: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4229: +case 0x422A: +case 0x422B: +case 0x422C: +case 0x422D: +case 0x422E: +case 0x422F: + +// CLR +case 0x4228: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x4231: +case 0x4232: +case 0x4233: +case 0x4234: +case 0x4235: +case 0x4236: +case 0x4237: + +// CLR +case 0x4230: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// CLR +case 0x4238: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// CLR +case 0x4239: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// CLR +case 0x421F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// CLR +case 0x4227: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4241: +case 0x4242: +case 0x4243: +case 0x4244: +case 0x4245: +case 0x4246: +case 0x4247: + +// CLR +case 0x4240: +{ + u32 res; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4251: +case 0x4252: +case 0x4253: +case 0x4254: +case 0x4255: +case 0x4256: +case 0x4257: + +// CLR +case 0x4250: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4259: +case 0x425A: +case 0x425B: +case 0x425C: +case 0x425D: +case 0x425E: + +// CLR +case 0x4258: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4261: +case 0x4262: +case 0x4263: +case 0x4264: +case 0x4265: +case 0x4266: + +// CLR +case 0x4260: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4269: +case 0x426A: +case 0x426B: +case 0x426C: +case 0x426D: +case 0x426E: +case 0x426F: + +// CLR +case 0x4268: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x4271: +case 0x4272: +case 0x4273: +case 0x4274: +case 0x4275: +case 0x4276: +case 0x4277: + +// CLR +case 0x4270: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// CLR +case 0x4278: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// CLR +case 0x4279: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// CLR +case 0x425F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// CLR +case 0x4267: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4281: +case 0x4282: +case 0x4283: +case 0x4284: +case 0x4285: +case 0x4286: +case 0x4287: + +// CLR +case 0x4280: +{ + u32 res; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0x4291: +case 0x4292: +case 0x4293: +case 0x4294: +case 0x4295: +case 0x4296: +case 0x4297: + +// CLR +case 0x4290: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x4299: +case 0x429A: +case 0x429B: +case 0x429C: +case 0x429D: +case 0x429E: + +// CLR +case 0x4298: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x42A1: +case 0x42A2: +case 0x42A3: +case 0x42A4: +case 0x42A5: +case 0x42A6: + +// CLR +case 0x42A0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x42A9: +case 0x42AA: +case 0x42AB: +case 0x42AC: +case 0x42AD: +case 0x42AE: +case 0x42AF: + +// CLR +case 0x42A8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x42B1: +case 0x42B2: +case 0x42B3: +case 0x42B4: +case 0x42B5: +case 0x42B6: +case 0x42B7: + +// CLR +case 0x42B0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// CLR +case 0x42B8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// CLR +case 0x42B9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// CLR +case 0x429F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// CLR +case 0x42A7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + res = 0; + CPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0; + PRE_IO + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x4401: +case 0x4402: +case 0x4403: +case 0x4404: +case 0x4405: +case 0x4406: +case 0x4407: + +// NEG +case 0x4400: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4411: +case 0x4412: +case 0x4413: +case 0x4414: +case 0x4415: +case 0x4416: +case 0x4417: + +// NEG +case 0x4410: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4419: +case 0x441A: +case 0x441B: +case 0x441C: +case 0x441D: +case 0x441E: + +// NEG +case 0x4418: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4421: +case 0x4422: +case 0x4423: +case 0x4424: +case 0x4425: +case 0x4426: + +// NEG +case 0x4420: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4429: +case 0x442A: +case 0x442B: +case 0x442C: +case 0x442D: +case 0x442E: +case 0x442F: + +// NEG +case 0x4428: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x4431: +case 0x4432: +case 0x4433: +case 0x4434: +case 0x4435: +case 0x4436: +case 0x4437: + +// NEG +case 0x4430: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// NEG +case 0x4438: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// NEG +case 0x4439: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// NEG +case 0x441F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// NEG +case 0x4427: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = -src; + CPU->flag_V = res & src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4441: +case 0x4442: +case 0x4443: +case 0x4444: +case 0x4445: +case 0x4446: +case 0x4447: + +// NEG +case 0x4440: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4451: +case 0x4452: +case 0x4453: +case 0x4454: +case 0x4455: +case 0x4456: +case 0x4457: + +// NEG +case 0x4450: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4459: +case 0x445A: +case 0x445B: +case 0x445C: +case 0x445D: +case 0x445E: + +// NEG +case 0x4458: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4461: +case 0x4462: +case 0x4463: +case 0x4464: +case 0x4465: +case 0x4466: + +// NEG +case 0x4460: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4469: +case 0x446A: +case 0x446B: +case 0x446C: +case 0x446D: +case 0x446E: +case 0x446F: + +// NEG +case 0x4468: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x4471: +case 0x4472: +case 0x4473: +case 0x4474: +case 0x4475: +case 0x4476: +case 0x4477: + +// NEG +case 0x4470: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// NEG +case 0x4478: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// NEG +case 0x4479: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// NEG +case 0x445F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// NEG +case 0x4467: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = -src; + CPU->flag_V = (res & src) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4481: +case 0x4482: +case 0x4483: +case 0x4484: +case 0x4485: +case 0x4486: +case 0x4487: + +// NEG +case 0x4480: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0x4491: +case 0x4492: +case 0x4493: +case 0x4494: +case 0x4495: +case 0x4496: +case 0x4497: + +// NEG +case 0x4490: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x4499: +case 0x449A: +case 0x449B: +case 0x449C: +case 0x449D: +case 0x449E: + +// NEG +case 0x4498: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x44A1: +case 0x44A2: +case 0x44A3: +case 0x44A4: +case 0x44A5: +case 0x44A6: + +// NEG +case 0x44A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x44A9: +case 0x44AA: +case 0x44AB: +case 0x44AC: +case 0x44AD: +case 0x44AE: +case 0x44AF: + +// NEG +case 0x44A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x44B1: +case 0x44B2: +case 0x44B3: +case 0x44B4: +case 0x44B5: +case 0x44B6: +case 0x44B7: + +// NEG +case 0x44B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// NEG +case 0x44B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// NEG +case 0x44B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// NEG +case 0x449F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// NEG +case 0x44A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = -src; + CPU->flag_notZ = res; + CPU->flag_V = (res & src) >> 24; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x4601: +case 0x4602: +case 0x4603: +case 0x4604: +case 0x4605: +case 0x4606: +case 0x4607: + +// NOT +case 0x4600: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4611: +case 0x4612: +case 0x4613: +case 0x4614: +case 0x4615: +case 0x4616: +case 0x4617: + +// NOT +case 0x4610: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4619: +case 0x461A: +case 0x461B: +case 0x461C: +case 0x461D: +case 0x461E: + +// NOT +case 0x4618: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4621: +case 0x4622: +case 0x4623: +case 0x4624: +case 0x4625: +case 0x4626: + +// NOT +case 0x4620: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4629: +case 0x462A: +case 0x462B: +case 0x462C: +case 0x462D: +case 0x462E: +case 0x462F: + +// NOT +case 0x4628: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x4631: +case 0x4632: +case 0x4633: +case 0x4634: +case 0x4635: +case 0x4636: +case 0x4637: + +// NOT +case 0x4630: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// NOT +case 0x4638: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// NOT +case 0x4639: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) + +// NOT +case 0x461F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// NOT +case 0x4627: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_N = res; + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x4641: +case 0x4642: +case 0x4643: +case 0x4644: +case 0x4645: +case 0x4646: +case 0x4647: + +// NOT +case 0x4640: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4651: +case 0x4652: +case 0x4653: +case 0x4654: +case 0x4655: +case 0x4656: +case 0x4657: + +// NOT +case 0x4650: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4659: +case 0x465A: +case 0x465B: +case 0x465C: +case 0x465D: +case 0x465E: + +// NOT +case 0x4658: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x4661: +case 0x4662: +case 0x4663: +case 0x4664: +case 0x4665: +case 0x4666: + +// NOT +case 0x4660: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4669: +case 0x466A: +case 0x466B: +case 0x466C: +case 0x466D: +case 0x466E: +case 0x466F: + +// NOT +case 0x4668: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x4671: +case 0x4672: +case 0x4673: +case 0x4674: +case 0x4675: +case 0x4676: +case 0x4677: + +// NOT +case 0x4670: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// NOT +case 0x4678: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// NOT +case 0x4679: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// NOT +case 0x465F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// NOT +case 0x4667: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res & 0xFFFF; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x4681: +case 0x4682: +case 0x4683: +case 0x4684: +case 0x4685: +case 0x4686: +case 0x4687: + +// NOT +case 0x4680: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0x4691: +case 0x4692: +case 0x4693: +case 0x4694: +case 0x4695: +case 0x4696: +case 0x4697: + +// NOT +case 0x4690: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x4699: +case 0x469A: +case 0x469B: +case 0x469C: +case 0x469D: +case 0x469E: + +// NOT +case 0x4698: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x46A1: +case 0x46A2: +case 0x46A3: +case 0x46A4: +case 0x46A5: +case 0x46A6: + +// NOT +case 0x46A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x46A9: +case 0x46AA: +case 0x46AB: +case 0x46AC: +case 0x46AD: +case 0x46AE: +case 0x46AF: + +// NOT +case 0x46A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x46B1: +case 0x46B2: +case 0x46B3: +case 0x46B4: +case 0x46B5: +case 0x46B6: +case 0x46B7: + +// NOT +case 0x46B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) + +// NOT +case 0x46B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) + +// NOT +case 0x46B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) + +// NOT +case 0x469F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) + +// NOT +case 0x46A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = ~src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x40C1: +case 0x40C2: +case 0x40C3: +case 0x40C4: +case 0x40C5: +case 0x40C6: +case 0x40C7: + +// MOVESRa +case 0x40C0: +{ + u32 res; + res = GET_SR; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0x40D1: +case 0x40D2: +case 0x40D3: +case 0x40D4: +case 0x40D5: +case 0x40D6: +case 0x40D7: + +// MOVESRa +case 0x40D0: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x40D9: +case 0x40DA: +case 0x40DB: +case 0x40DC: +case 0x40DD: +case 0x40DE: + +// MOVESRa +case 0x40D8: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x40E1: +case 0x40E2: +case 0x40E3: +case 0x40E4: +case 0x40E5: +case 0x40E6: + +// MOVESRa +case 0x40E0: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x40E9: +case 0x40EA: +case 0x40EB: +case 0x40EC: +case 0x40ED: +case 0x40EE: +case 0x40EF: + +// MOVESRa +case 0x40E8: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x40F1: +case 0x40F2: +case 0x40F3: +case 0x40F4: +case 0x40F5: +case 0x40F6: +case 0x40F7: + +// MOVESRa +case 0x40F0: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// MOVESRa +case 0x40F8: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// MOVESRa +case 0x40F9: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// MOVESRa +case 0x40DF: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// MOVESRa +case 0x40E7: +{ + u32 adr; + u32 res; + res = GET_SR; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x44C1: +case 0x44C2: +case 0x44C3: +case 0x44C4: +case 0x44C5: +case 0x44C6: +case 0x44C7: + +// MOVEaCCR +case 0x44C0: +{ + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + SET_CCR(res) +} +RET(12) +case 0x44D1: +case 0x44D2: +case 0x44D3: +case 0x44D4: +case 0x44D5: +case 0x44D6: +case 0x44D7: + +// MOVEaCCR +case 0x44D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(16) +case 0x44D9: +case 0x44DA: +case 0x44DB: +case 0x44DC: +case 0x44DD: +case 0x44DE: + +// MOVEaCCR +case 0x44D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(16) +case 0x44E1: +case 0x44E2: +case 0x44E3: +case 0x44E4: +case 0x44E5: +case 0x44E6: + +// MOVEaCCR +case 0x44E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(18) +case 0x44E9: +case 0x44EA: +case 0x44EB: +case 0x44EC: +case 0x44ED: +case 0x44EE: +case 0x44EF: + +// MOVEaCCR +case 0x44E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(20) +case 0x44F1: +case 0x44F2: +case 0x44F3: +case 0x44F4: +case 0x44F5: +case 0x44F6: +case 0x44F7: + +// MOVEaCCR +case 0x44F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(22) + +// MOVEaCCR +case 0x44F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(20) + +// MOVEaCCR +case 0x44F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(24) + +// MOVEaCCR +case 0x44FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(20) + +// MOVEaCCR +case 0x44FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(22) + +// MOVEaCCR +case 0x44FC: +{ + u32 res; + res = FETCH_WORD; + PC += 2; + SET_CCR(res) +} +RET(16) + +// MOVEaCCR +case 0x44DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(16) + +// MOVEaCCR +case 0x44E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + SET_CCR(res) + POST_IO +} +RET(18) +case 0x46C1: +case 0x46C2: +case 0x46C3: +case 0x46C4: +case 0x46C5: +case 0x46C6: +case 0x46C7: + +// MOVEaSR +case 0x46C0: +{ + u32 res; + if (CPU->flag_S) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 12; +goto C68k_Exec_End; +case 0x46D1: +case 0x46D2: +case 0x46D3: +case 0x46D4: +case 0x46D5: +case 0x46D6: +case 0x46D7: + +// MOVEaSR +case 0x46D0: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 16; +goto C68k_Exec_End; +case 0x46D9: +case 0x46DA: +case 0x46DB: +case 0x46DC: +case 0x46DD: +case 0x46DE: + +// MOVEaSR +case 0x46D8: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 16; +goto C68k_Exec_End; +case 0x46E1: +case 0x46E2: +case 0x46E3: +case 0x46E4: +case 0x46E5: +case 0x46E6: + +// MOVEaSR +case 0x46E0: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 18; +goto C68k_Exec_End; +case 0x46E9: +case 0x46EA: +case 0x46EB: +case 0x46EC: +case 0x46ED: +case 0x46EE: +case 0x46EF: + +// MOVEaSR +case 0x46E8: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; +case 0x46F1: +case 0x46F2: +case 0x46F3: +case 0x46F4: +case 0x46F5: +case 0x46F6: +case 0x46F7: + +// MOVEaSR +case 0x46F0: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 22; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46F8: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46F9: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 24; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46FA: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46FB: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 22; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46FC: +{ + u32 res; + if (CPU->flag_S) + { + res = FETCH_WORD; + PC += 2; + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 16; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46DF: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 16; +goto C68k_Exec_End; + +// MOVEaSR +case 0x46E7: +{ + u32 adr; + u32 res; + if (CPU->flag_S) + { + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + } + else + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } +} +POST_IO +CCnt -= 18; +goto C68k_Exec_End; +case 0x4801: +case 0x4802: +case 0x4803: +case 0x4804: +case 0x4805: +case 0x4806: +case 0x4807: + +// NBCD +case 0x4800: +{ + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; +} +RET(6) +case 0x4811: +case 0x4812: +case 0x4813: +case 0x4814: +case 0x4815: +case 0x4816: +case 0x4817: + +// NBCD +case 0x4810: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(12) +case 0x4819: +case 0x481A: +case 0x481B: +case 0x481C: +case 0x481D: +case 0x481E: + +// NBCD +case 0x4818: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(12) +case 0x4821: +case 0x4822: +case 0x4823: +case 0x4824: +case 0x4825: +case 0x4826: + +// NBCD +case 0x4820: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(14) +case 0x4829: +case 0x482A: +case 0x482B: +case 0x482C: +case 0x482D: +case 0x482E: +case 0x482F: + +// NBCD +case 0x4828: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(16) +case 0x4831: +case 0x4832: +case 0x4833: +case 0x4834: +case 0x4835: +case 0x4836: +case 0x4837: + +// NBCD +case 0x4830: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(18) + +// NBCD +case 0x4838: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(16) + +// NBCD +case 0x4839: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(20) + +// NBCD +case 0x481F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(12) + +// NBCD +case 0x4827: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + + if (res != 0x9a) + { + if ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10; + res &= 0xFF; + WRITE_BYTE_F(adr, res) + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = res; + POST_IO +} +RET(14) +case 0x4851: +case 0x4852: +case 0x4853: +case 0x4854: +case 0x4855: +case 0x4856: +case 0x4857: + +// PEA +case 0x4850: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(12) +case 0x4869: +case 0x486A: +case 0x486B: +case 0x486C: +case 0x486D: +case 0x486E: +case 0x486F: + +// PEA +case 0x4868: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(16) +case 0x4871: +case 0x4872: +case 0x4873: +case 0x4874: +case 0x4875: +case 0x4876: +case 0x4877: + +// PEA +case 0x4870: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(20) + +// PEA +case 0x4878: +{ + u32 adr; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(16) + +// PEA +case 0x4879: +{ + u32 adr; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(20) + +// PEA +case 0x487A: +{ + u32 adr; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(16) + +// PEA +case 0x487B: +{ + u32 adr; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + PUSH_32_F(adr) + POST_IO +} +RET(20) +case 0x4841: +case 0x4842: +case 0x4843: +case 0x4844: +case 0x4845: +case 0x4846: +case 0x4847: + +// SWAP +case 0x4840: +{ + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + res = (res >> 16) | (res << 16); + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4891: +case 0x4892: +case 0x4893: +case 0x4894: +case 0x4895: +case 0x4896: +case 0x4897: + +// MOVEMRa +case 0x4890: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_WORD_F(adr, *(u16*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(12) +case 0x48A1: +case 0x48A2: +case 0x48A3: +case 0x48A4: +case 0x48A5: +case 0x48A6: + +// MOVEMRa +case 0x48A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->A[7]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + adr -= 2; + WRITE_WORD_F(adr, *(u16*)src) + } + src -= 4; + } while (res >>= 1); + CPU->A[(Opcode >> 0) & 7] = adr; + POST_IO + CCnt -= (dst - adr) * 2; +} +RET(8) +case 0x48A9: +case 0x48AA: +case 0x48AB: +case 0x48AC: +case 0x48AD: +case 0x48AE: +case 0x48AF: + +// MOVEMRa +case 0x48A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_WORD_F(adr, *(u16*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(20) +case 0x48B1: +case 0x48B2: +case 0x48B3: +case 0x48B4: +case 0x48B5: +case 0x48B6: +case 0x48B7: + +// MOVEMRa +case 0x48B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_WORD_F(adr, *(u16*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) + +// MOVEMRa +case 0x48B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_WORD_F(adr, *(u16*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(20) + +// MOVEMRa +case 0x48B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_WORD_F(adr, *(u16*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMRa +case 0x48A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + src = (pointer)(&CPU->A[7]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + adr -= 2; + WRITE_WORD_F(adr, *(u16*)src) + } + src -= 4; + } while (res >>= 1); + CPU->A[7] = adr; + POST_IO + CCnt -= (dst - adr) * 2; +} +RET(8) +case 0x48D1: +case 0x48D2: +case 0x48D3: +case 0x48D4: +case 0x48D5: +case 0x48D6: +case 0x48D7: + +// MOVEMRa +case 0x48D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(16) +case 0x48E1: +case 0x48E2: +case 0x48E3: +case 0x48E4: +case 0x48E5: +case 0x48E6: + +// MOVEMRa +case 0x48E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->A[7]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + adr -= 4; + WRITE_LONG_DEC_F(adr, *(u32*)src) + } + src -= 4; + } while (res >>= 1); + CPU->A[(Opcode >> 0) & 7] = adr; + POST_IO + CCnt -= (dst - adr) * 2; +} +RET(8) +case 0x48E9: +case 0x48EA: +case 0x48EB: +case 0x48EC: +case 0x48ED: +case 0x48EE: +case 0x48EF: + +// MOVEMRa +case 0x48E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) +case 0x48F1: +case 0x48F2: +case 0x48F3: +case 0x48F4: +case 0x48F5: +case 0x48F6: +case 0x48F7: + +// MOVEMRa +case 0x48F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMRa +case 0x48F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) + +// MOVEMRa +case 0x48F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + WRITE_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(32) + +// MOVEMRa +case 0x48E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + src = (pointer)(&CPU->A[7]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + adr -= 4; + WRITE_LONG_DEC_F(adr, *(u32*)src) + } + src -= 4; + } while (res >>= 1); + CPU->A[7] = adr; + POST_IO + CCnt -= (dst - adr) * 2; +} +RET(8) +case 0x4881: +case 0x4882: +case 0x4883: +case 0x4884: +case 0x4885: +case 0x4886: +case 0x4887: + +// EXT +case 0x4880: +{ + u32 res; + res = (s32)(s8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x48C1: +case 0x48C2: +case 0x48C3: +case 0x48C4: +case 0x48C5: +case 0x48C6: +case 0x48C7: + +// EXT +case 0x48C0: +{ + u32 res; + res = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4A01: +case 0x4A02: +case 0x4A03: +case 0x4A04: +case 0x4A05: +case 0x4A06: +case 0x4A07: + +// TST +case 0x4A00: +{ + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; +} +RET(4) +case 0x4A11: +case 0x4A12: +case 0x4A13: +case 0x4A14: +case 0x4A15: +case 0x4A16: +case 0x4A17: + +// TST +case 0x4A10: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(8) +case 0x4A19: +case 0x4A1A: +case 0x4A1B: +case 0x4A1C: +case 0x4A1D: +case 0x4A1E: + +// TST +case 0x4A18: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(8) +case 0x4A21: +case 0x4A22: +case 0x4A23: +case 0x4A24: +case 0x4A25: +case 0x4A26: + +// TST +case 0x4A20: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(10) +case 0x4A29: +case 0x4A2A: +case 0x4A2B: +case 0x4A2C: +case 0x4A2D: +case 0x4A2E: +case 0x4A2F: + +// TST +case 0x4A28: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(12) +case 0x4A31: +case 0x4A32: +case 0x4A33: +case 0x4A34: +case 0x4A35: +case 0x4A36: +case 0x4A37: + +// TST +case 0x4A30: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(14) + +// TST +case 0x4A38: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(12) + +// TST +case 0x4A39: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(16) + +// TST +case 0x4A1F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(8) + +// TST +case 0x4A27: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + POST_IO +} +RET(10) +case 0x4A41: +case 0x4A42: +case 0x4A43: +case 0x4A44: +case 0x4A45: +case 0x4A46: +case 0x4A47: + +// TST +case 0x4A40: +{ + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; +} +RET(4) +case 0x4A51: +case 0x4A52: +case 0x4A53: +case 0x4A54: +case 0x4A55: +case 0x4A56: +case 0x4A57: + +// TST +case 0x4A50: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(8) +case 0x4A59: +case 0x4A5A: +case 0x4A5B: +case 0x4A5C: +case 0x4A5D: +case 0x4A5E: + +// TST +case 0x4A58: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(8) +case 0x4A61: +case 0x4A62: +case 0x4A63: +case 0x4A64: +case 0x4A65: +case 0x4A66: + +// TST +case 0x4A60: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(10) +case 0x4A69: +case 0x4A6A: +case 0x4A6B: +case 0x4A6C: +case 0x4A6D: +case 0x4A6E: +case 0x4A6F: + +// TST +case 0x4A68: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(12) +case 0x4A71: +case 0x4A72: +case 0x4A73: +case 0x4A74: +case 0x4A75: +case 0x4A76: +case 0x4A77: + +// TST +case 0x4A70: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(14) + +// TST +case 0x4A78: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(12) + +// TST +case 0x4A79: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(16) + +// TST +case 0x4A5F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(8) + +// TST +case 0x4A67: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + POST_IO +} +RET(10) +case 0x4A81: +case 0x4A82: +case 0x4A83: +case 0x4A84: +case 0x4A85: +case 0x4A86: +case 0x4A87: + +// TST +case 0x4A80: +{ + u32 res; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; +} +RET(4) +case 0x4A91: +case 0x4A92: +case 0x4A93: +case 0x4A94: +case 0x4A95: +case 0x4A96: +case 0x4A97: + +// TST +case 0x4A90: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(12) +case 0x4A99: +case 0x4A9A: +case 0x4A9B: +case 0x4A9C: +case 0x4A9D: +case 0x4A9E: + +// TST +case 0x4A98: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(12) +case 0x4AA1: +case 0x4AA2: +case 0x4AA3: +case 0x4AA4: +case 0x4AA5: +case 0x4AA6: + +// TST +case 0x4AA0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0x4AA9: +case 0x4AAA: +case 0x4AAB: +case 0x4AAC: +case 0x4AAD: +case 0x4AAE: +case 0x4AAF: + +// TST +case 0x4AA8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0x4AB1: +case 0x4AB2: +case 0x4AB3: +case 0x4AB4: +case 0x4AB5: +case 0x4AB6: +case 0x4AB7: + +// TST +case 0x4AB0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) + +// TST +case 0x4AB8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) + +// TST +case 0x4AB9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) + +// TST +case 0x4A9F: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(12) + +// TST +case 0x4AA7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0x4AC1: +case 0x4AC2: +case 0x4AC3: +case 0x4AC4: +case 0x4AC5: +case 0x4AC6: +case 0x4AC7: + +// TAS +case 0x4AC0: +{ + u32 res; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x4AD1: +case 0x4AD2: +case 0x4AD3: +case 0x4AD4: +case 0x4AD5: +case 0x4AD6: +case 0x4AD7: + +// TAS +case 0x4AD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x4AD9: +case 0x4ADA: +case 0x4ADB: +case 0x4ADC: +case 0x4ADD: +case 0x4ADE: + +// TAS +case 0x4AD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) +case 0x4AE1: +case 0x4AE2: +case 0x4AE3: +case 0x4AE4: +case 0x4AE5: +case 0x4AE6: + +// TAS +case 0x4AE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(10) +case 0x4AE9: +case 0x4AEA: +case 0x4AEB: +case 0x4AEC: +case 0x4AED: +case 0x4AEE: +case 0x4AEF: + +// TAS +case 0x4AE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x4AF1: +case 0x4AF2: +case 0x4AF3: +case 0x4AF4: +case 0x4AF5: +case 0x4AF6: +case 0x4AF7: + +// TAS +case 0x4AF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) + +// TAS +case 0x4AF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) + +// TAS +case 0x4AF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) + +// TAS +case 0x4ADF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(8) + +// TAS +case 0x4AE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + res |= 0x80; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(10) + +// ILLEGAL +case 0x4AFC: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ILLEGAL_INSTRUCTION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO +} +RET(4) +case 0x4C91: +case 0x4C92: +case 0x4C93: +case 0x4C94: +case 0x4C95: +case 0x4C96: +case 0x4C97: + +// MOVEMaR +case 0x4C90: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(16) +case 0x4C99: +case 0x4C9A: +case 0x4C9B: +case 0x4C9C: +case 0x4C9D: +case 0x4C9E: + +// MOVEMaR +case 0x4C98: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + CPU->A[(Opcode >> 0) & 7] = adr; + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(12) +case 0x4CA9: +case 0x4CAA: +case 0x4CAB: +case 0x4CAC: +case 0x4CAD: +case 0x4CAE: +case 0x4CAF: + +// MOVEMaR +case 0x4CA8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) +case 0x4CB1: +case 0x4CB2: +case 0x4CB3: +case 0x4CB4: +case 0x4CB5: +case 0x4CB6: +case 0x4CB7: + +// MOVEMaR +case 0x4CB0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMaR +case 0x4CB8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) + +// MOVEMaR +case 0x4CB9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(32) + +// MOVEMaR +case 0x4CBA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(24) + +// MOVEMaR +case 0x4CBB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMaR +case 0x4C9F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READSX_WORD_F(adr, *(s32*)src) + adr += 2; + } + src += 4; + } while (res >>= 1); + CPU->A[7] = adr; + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(12) +case 0x4CD1: +case 0x4CD2: +case 0x4CD3: +case 0x4CD4: +case 0x4CD5: +case 0x4CD6: +case 0x4CD7: + +// MOVEMaR +case 0x4CD0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(20) +case 0x4CD9: +case 0x4CDA: +case 0x4CDB: +case 0x4CDC: +case 0x4CDD: +case 0x4CDE: + +// MOVEMaR +case 0x4CD8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + CPU->A[(Opcode >> 0) & 7] = adr; + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(12) +case 0x4CE9: +case 0x4CEA: +case 0x4CEB: +case 0x4CEC: +case 0x4CED: +case 0x4CEE: +case 0x4CEF: + +// MOVEMaR +case 0x4CE8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) +case 0x4CF1: +case 0x4CF2: +case 0x4CF3: +case 0x4CF4: +case 0x4CF5: +case 0x4CF6: +case 0x4CF7: + +// MOVEMaR +case 0x4CF0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(32) + +// MOVEMaR +case 0x4CF8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMaR +case 0x4CF9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (s32)FETCH_LONG; + PC += 4; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(36) + +// MOVEMaR +case 0x4CFA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(28) + +// MOVEMaR +case 0x4CFB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(32) + +// MOVEMaR +case 0x4CDF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + res = FETCH_WORD; + PC += 2; + adr = CPU->A[7]; + src = (pointer)(&CPU->D[0]); + dst = adr; + PRE_IO + do + { + if (res & 1) + { + READ_LONG_F(adr, *(u32*)src) + adr += 4; + } + src += 4; + } while (res >>= 1); + CPU->A[7] = adr; + POST_IO + CCnt -= (adr - dst) * 2; +} +RET(12) +case 0x4E41: +case 0x4E42: +case 0x4E43: +case 0x4E44: +case 0x4E45: +case 0x4E46: +case 0x4E47: +case 0x4E48: +case 0x4E49: +case 0x4E4A: +case 0x4E4B: +case 0x4E4C: +case 0x4E4D: +case 0x4E4E: +case 0x4E4F: + +// TRAP +case 0x4E40: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_TRAP_BASE_EX + (Opcode & 0xF); + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO +} +RET(4) +case 0x4E51: +case 0x4E52: +case 0x4E53: +case 0x4E54: +case 0x4E55: +case 0x4E56: + +// LINK +case 0x4E50: +{ + u32 res; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + PRE_IO + PUSH_32_F(res) + res = CPU->A[7]; + CPU->A[(Opcode >> 0) & 7] = res; + CPU->A[7] += (s32)(s16)FETCH_WORD; + PC += 2; + POST_IO +} +RET(16) + +// LINKA7 +case 0x4E57: +{ + CPU->A[7] -= 4; + PRE_IO + WRITE_LONG_DEC_F(CPU->A[7], CPU->A[7]) + CPU->A[7] += (s32)(s16)FETCH_WORD; + PC += 2; + POST_IO +} +RET(16) +case 0x4E59: +case 0x4E5A: +case 0x4E5B: +case 0x4E5C: +case 0x4E5D: +case 0x4E5E: + +// ULNK +case 0x4E58: +{ + u32 res; + pointer src; + src = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->A[7] = src + 4; + PRE_IO + READ_LONG_F(src, res) + CPU->A[(Opcode >> 0) & 7] = res; + POST_IO +} +RET(12) + +// ULNKA7 +case 0x4E5F: +{ + PRE_IO + READ_LONG_F(CPU->A[7], CPU->A[7]) + POST_IO +} +RET(12) +case 0x4E61: +case 0x4E62: +case 0x4E63: +case 0x4E64: +case 0x4E65: +case 0x4E66: +case 0x4E67: + +// MOVEAUSP +case 0x4E60: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(4) + } + res = (u32)CPU->A[(Opcode >> 0) & 7]; + CPU->USP = res; +} +RET(4) +case 0x4E69: +case 0x4E6A: +case 0x4E6B: +case 0x4E6C: +case 0x4E6D: +case 0x4E6E: +case 0x4E6F: + +// MOVEUSPA +case 0x4E68: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(4) + } + res = CPU->USP; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(4) + +// RESET +case 0x4E70: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(4) + } + PRE_IO + CPU->Reset_CallBack(); + POST_IO +} +RET(132) + +// NOP +case 0x4E71: +{ +} +RET(4) + +// STOP +case 0x4E72: +{ + u32 res; + if (!CPU->flag_S) + { + PC += 2; + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(4) + } + res = FETCH_WORD & C68K_SR_MASK; + PC += 2; + SET_SR(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } + CPU->Status |= C68K_HALTED; + CCnt = 0; +} +CCnt -= 4; +goto C68k_Exec_End; + +// RTE +case 0x4E73: +{ + u32 res; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + res = C68K_PRIVILEGE_VIOLATION_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(4) + } + PRE_IO + POP_16_F(res) + SET_SR(res) + POP_32_F(res) + SET_PC(res) + if (!CPU->flag_S) + { + res = CPU->A[7]; + CPU->A[7] = CPU->USP; + CPU->USP = res; + } +} +POST_IO +CCnt -= 20; +goto C68k_Exec_End; + +// RTS +case 0x4E75: +{ + u32 res; + PRE_IO + POP_32_F(res) + SET_PC(res) + POST_IO +} +RET(16) + +// TRAPV +case 0x4E76: +{ + u32 res; + if (CPU->flag_V & 0x80) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_TRAPV_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(4) + +// RTR +case 0x4E77: +{ + u32 res; + PRE_IO + POP_16_F(res) + SET_CCR(res) + POP_32_F(res) + SET_PC(res) + POST_IO +} +RET(20) +case 0x4E91: +case 0x4E92: +case 0x4E93: +case 0x4E94: +case 0x4E95: +case 0x4E96: +case 0x4E97: + +// JSR +case 0x4E90: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(16) +case 0x4EA9: +case 0x4EAA: +case 0x4EAB: +case 0x4EAC: +case 0x4EAD: +case 0x4EAE: +case 0x4EAF: + +// JSR +case 0x4EA8: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(18) +case 0x4EB1: +case 0x4EB2: +case 0x4EB3: +case 0x4EB4: +case 0x4EB5: +case 0x4EB6: +case 0x4EB7: + +// JSR +case 0x4EB0: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(22) + +// JSR +case 0x4EB8: +{ + u32 adr; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(18) + +// JSR +case 0x4EB9: +{ + u32 adr; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(20) + +// JSR +case 0x4EBA: +{ + u32 adr; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(18) + +// JSR +case 0x4EBB: +{ + u32 adr; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + SET_PC(adr) + POST_IO +} +RET(22) +case 0x4ED1: +case 0x4ED2: +case 0x4ED3: +case 0x4ED4: +case 0x4ED5: +case 0x4ED6: +case 0x4ED7: + +// JMP +case 0x4ED0: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + SET_PC(adr) +} +RET(8) +case 0x4EE9: +case 0x4EEA: +case 0x4EEB: +case 0x4EEC: +case 0x4EED: +case 0x4EEE: +case 0x4EEF: + +// JMP +case 0x4EE8: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + SET_PC(adr) +} +RET(10) +case 0x4EF1: +case 0x4EF2: +case 0x4EF3: +case 0x4EF4: +case 0x4EF5: +case 0x4EF6: +case 0x4EF7: + +// JMP +case 0x4EF0: +{ + u32 adr; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + SET_PC(adr) +} +RET(14) + +// JMP +case 0x4EF8: +{ + u32 adr; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + SET_PC(adr) +} +RET(10) + +// JMP +case 0x4EF9: +{ + u32 adr; + adr = (s32)FETCH_LONG; + PC += 4; + SET_PC(adr) +} +RET(12) + +// JMP +case 0x4EFA: +{ + u32 adr; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + SET_PC(adr) +} +RET(10) + +// JMP +case 0x4EFB: +{ + u32 adr; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + SET_PC(adr) +} +RET(14) +case 0x4380: +case 0x4580: +case 0x4780: +case 0x4980: +case 0x4B80: +case 0x4D80: +case 0x4F80: +case 0x4181: +case 0x4381: +case 0x4581: +case 0x4781: +case 0x4981: +case 0x4B81: +case 0x4D81: +case 0x4F81: +case 0x4182: +case 0x4382: +case 0x4582: +case 0x4782: +case 0x4982: +case 0x4B82: +case 0x4D82: +case 0x4F82: +case 0x4183: +case 0x4383: +case 0x4583: +case 0x4783: +case 0x4983: +case 0x4B83: +case 0x4D83: +case 0x4F83: +case 0x4184: +case 0x4384: +case 0x4584: +case 0x4784: +case 0x4984: +case 0x4B84: +case 0x4D84: +case 0x4F84: +case 0x4185: +case 0x4385: +case 0x4585: +case 0x4785: +case 0x4985: +case 0x4B85: +case 0x4D85: +case 0x4F85: +case 0x4186: +case 0x4386: +case 0x4586: +case 0x4786: +case 0x4986: +case 0x4B86: +case 0x4D86: +case 0x4F86: +case 0x4187: +case 0x4387: +case 0x4587: +case 0x4787: +case 0x4987: +case 0x4B87: +case 0x4D87: +case 0x4F87: + +// CHK +case 0x4180: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(10) +case 0x4390: +case 0x4590: +case 0x4790: +case 0x4990: +case 0x4B90: +case 0x4D90: +case 0x4F90: +case 0x4191: +case 0x4391: +case 0x4591: +case 0x4791: +case 0x4991: +case 0x4B91: +case 0x4D91: +case 0x4F91: +case 0x4192: +case 0x4392: +case 0x4592: +case 0x4792: +case 0x4992: +case 0x4B92: +case 0x4D92: +case 0x4F92: +case 0x4193: +case 0x4393: +case 0x4593: +case 0x4793: +case 0x4993: +case 0x4B93: +case 0x4D93: +case 0x4F93: +case 0x4194: +case 0x4394: +case 0x4594: +case 0x4794: +case 0x4994: +case 0x4B94: +case 0x4D94: +case 0x4F94: +case 0x4195: +case 0x4395: +case 0x4595: +case 0x4795: +case 0x4995: +case 0x4B95: +case 0x4D95: +case 0x4F95: +case 0x4196: +case 0x4396: +case 0x4596: +case 0x4796: +case 0x4996: +case 0x4B96: +case 0x4D96: +case 0x4F96: +case 0x4197: +case 0x4397: +case 0x4597: +case 0x4797: +case 0x4997: +case 0x4B97: +case 0x4D97: +case 0x4F97: + +// CHK +case 0x4190: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(14) +case 0x4398: +case 0x4598: +case 0x4798: +case 0x4998: +case 0x4B98: +case 0x4D98: +case 0x4F98: +case 0x4199: +case 0x4399: +case 0x4599: +case 0x4799: +case 0x4999: +case 0x4B99: +case 0x4D99: +case 0x4F99: +case 0x419A: +case 0x439A: +case 0x459A: +case 0x479A: +case 0x499A: +case 0x4B9A: +case 0x4D9A: +case 0x4F9A: +case 0x419B: +case 0x439B: +case 0x459B: +case 0x479B: +case 0x499B: +case 0x4B9B: +case 0x4D9B: +case 0x4F9B: +case 0x419C: +case 0x439C: +case 0x459C: +case 0x479C: +case 0x499C: +case 0x4B9C: +case 0x4D9C: +case 0x4F9C: +case 0x419D: +case 0x439D: +case 0x459D: +case 0x479D: +case 0x499D: +case 0x4B9D: +case 0x4D9D: +case 0x4F9D: +case 0x419E: +case 0x439E: +case 0x459E: +case 0x479E: +case 0x499E: +case 0x4B9E: +case 0x4D9E: +case 0x4F9E: + +// CHK +case 0x4198: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(14) +case 0x43A0: +case 0x45A0: +case 0x47A0: +case 0x49A0: +case 0x4BA0: +case 0x4DA0: +case 0x4FA0: +case 0x41A1: +case 0x43A1: +case 0x45A1: +case 0x47A1: +case 0x49A1: +case 0x4BA1: +case 0x4DA1: +case 0x4FA1: +case 0x41A2: +case 0x43A2: +case 0x45A2: +case 0x47A2: +case 0x49A2: +case 0x4BA2: +case 0x4DA2: +case 0x4FA2: +case 0x41A3: +case 0x43A3: +case 0x45A3: +case 0x47A3: +case 0x49A3: +case 0x4BA3: +case 0x4DA3: +case 0x4FA3: +case 0x41A4: +case 0x43A4: +case 0x45A4: +case 0x47A4: +case 0x49A4: +case 0x4BA4: +case 0x4DA4: +case 0x4FA4: +case 0x41A5: +case 0x43A5: +case 0x45A5: +case 0x47A5: +case 0x49A5: +case 0x4BA5: +case 0x4DA5: +case 0x4FA5: +case 0x41A6: +case 0x43A6: +case 0x45A6: +case 0x47A6: +case 0x49A6: +case 0x4BA6: +case 0x4DA6: +case 0x4FA6: + +// CHK +case 0x41A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(16) +case 0x43A8: +case 0x45A8: +case 0x47A8: +case 0x49A8: +case 0x4BA8: +case 0x4DA8: +case 0x4FA8: +case 0x41A9: +case 0x43A9: +case 0x45A9: +case 0x47A9: +case 0x49A9: +case 0x4BA9: +case 0x4DA9: +case 0x4FA9: +case 0x41AA: +case 0x43AA: +case 0x45AA: +case 0x47AA: +case 0x49AA: +case 0x4BAA: +case 0x4DAA: +case 0x4FAA: +case 0x41AB: +case 0x43AB: +case 0x45AB: +case 0x47AB: +case 0x49AB: +case 0x4BAB: +case 0x4DAB: +case 0x4FAB: +case 0x41AC: +case 0x43AC: +case 0x45AC: +case 0x47AC: +case 0x49AC: +case 0x4BAC: +case 0x4DAC: +case 0x4FAC: +case 0x41AD: +case 0x43AD: +case 0x45AD: +case 0x47AD: +case 0x49AD: +case 0x4BAD: +case 0x4DAD: +case 0x4FAD: +case 0x41AE: +case 0x43AE: +case 0x45AE: +case 0x47AE: +case 0x49AE: +case 0x4BAE: +case 0x4DAE: +case 0x4FAE: +case 0x41AF: +case 0x43AF: +case 0x45AF: +case 0x47AF: +case 0x49AF: +case 0x4BAF: +case 0x4DAF: +case 0x4FAF: + +// CHK +case 0x41A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(18) +case 0x43B0: +case 0x45B0: +case 0x47B0: +case 0x49B0: +case 0x4BB0: +case 0x4DB0: +case 0x4FB0: +case 0x41B1: +case 0x43B1: +case 0x45B1: +case 0x47B1: +case 0x49B1: +case 0x4BB1: +case 0x4DB1: +case 0x4FB1: +case 0x41B2: +case 0x43B2: +case 0x45B2: +case 0x47B2: +case 0x49B2: +case 0x4BB2: +case 0x4DB2: +case 0x4FB2: +case 0x41B3: +case 0x43B3: +case 0x45B3: +case 0x47B3: +case 0x49B3: +case 0x4BB3: +case 0x4DB3: +case 0x4FB3: +case 0x41B4: +case 0x43B4: +case 0x45B4: +case 0x47B4: +case 0x49B4: +case 0x4BB4: +case 0x4DB4: +case 0x4FB4: +case 0x41B5: +case 0x43B5: +case 0x45B5: +case 0x47B5: +case 0x49B5: +case 0x4BB5: +case 0x4DB5: +case 0x4FB5: +case 0x41B6: +case 0x43B6: +case 0x45B6: +case 0x47B6: +case 0x49B6: +case 0x4BB6: +case 0x4DB6: +case 0x4FB6: +case 0x41B7: +case 0x43B7: +case 0x45B7: +case 0x47B7: +case 0x49B7: +case 0x4BB7: +case 0x4DB7: +case 0x4FB7: + +// CHK +case 0x41B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(20) +case 0x43B8: +case 0x45B8: +case 0x47B8: +case 0x49B8: +case 0x4BB8: +case 0x4DB8: +case 0x4FB8: + +// CHK +case 0x41B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(18) +case 0x43B9: +case 0x45B9: +case 0x47B9: +case 0x49B9: +case 0x4BB9: +case 0x4DB9: +case 0x4FB9: + +// CHK +case 0x41B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(22) +case 0x43BA: +case 0x45BA: +case 0x47BA: +case 0x49BA: +case 0x4BBA: +case 0x4DBA: +case 0x4FBA: + +// CHK +case 0x41BA: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(18) +case 0x43BB: +case 0x45BB: +case 0x47BB: +case 0x49BB: +case 0x4BBB: +case 0x4DBB: +case 0x4FBB: + +// CHK +case 0x41BB: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(20) +case 0x43BC: +case 0x45BC: +case 0x47BC: +case 0x49BC: +case 0x4BBC: +case 0x4DBC: +case 0x4FBC: + +// CHK +case 0x41BC: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(14) +case 0x439F: +case 0x459F: +case 0x479F: +case 0x499F: +case 0x4B9F: +case 0x4D9F: +case 0x4F9F: + +// CHK +case 0x419F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(14) +case 0x43A7: +case 0x45A7: +case 0x47A7: +case 0x49A7: +case 0x4BA7: +case 0x4DA7: +case 0x4FA7: + +// CHK +case 0x41A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + if (((s32)res < 0) || (res > src)) + { + CPU->flag_N = res >> 8; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_CHK_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + } + POST_IO +} +RET(16) +case 0x43D0: +case 0x45D0: +case 0x47D0: +case 0x49D0: +case 0x4BD0: +case 0x4DD0: +case 0x4FD0: +case 0x41D1: +case 0x43D1: +case 0x45D1: +case 0x47D1: +case 0x49D1: +case 0x4BD1: +case 0x4DD1: +case 0x4FD1: +case 0x41D2: +case 0x43D2: +case 0x45D2: +case 0x47D2: +case 0x49D2: +case 0x4BD2: +case 0x4DD2: +case 0x4FD2: +case 0x41D3: +case 0x43D3: +case 0x45D3: +case 0x47D3: +case 0x49D3: +case 0x4BD3: +case 0x4DD3: +case 0x4FD3: +case 0x41D4: +case 0x43D4: +case 0x45D4: +case 0x47D4: +case 0x49D4: +case 0x4BD4: +case 0x4DD4: +case 0x4FD4: +case 0x41D5: +case 0x43D5: +case 0x45D5: +case 0x47D5: +case 0x49D5: +case 0x4BD5: +case 0x4DD5: +case 0x4FD5: +case 0x41D6: +case 0x43D6: +case 0x45D6: +case 0x47D6: +case 0x49D6: +case 0x4BD6: +case 0x4DD6: +case 0x4FD6: +case 0x41D7: +case 0x43D7: +case 0x45D7: +case 0x47D7: +case 0x49D7: +case 0x4BD7: +case 0x4DD7: +case 0x4FD7: + +// LEA +case 0x41D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(4) +case 0x43E8: +case 0x45E8: +case 0x47E8: +case 0x49E8: +case 0x4BE8: +case 0x4DE8: +case 0x4FE8: +case 0x41E9: +case 0x43E9: +case 0x45E9: +case 0x47E9: +case 0x49E9: +case 0x4BE9: +case 0x4DE9: +case 0x4FE9: +case 0x41EA: +case 0x43EA: +case 0x45EA: +case 0x47EA: +case 0x49EA: +case 0x4BEA: +case 0x4DEA: +case 0x4FEA: +case 0x41EB: +case 0x43EB: +case 0x45EB: +case 0x47EB: +case 0x49EB: +case 0x4BEB: +case 0x4DEB: +case 0x4FEB: +case 0x41EC: +case 0x43EC: +case 0x45EC: +case 0x47EC: +case 0x49EC: +case 0x4BEC: +case 0x4DEC: +case 0x4FEC: +case 0x41ED: +case 0x43ED: +case 0x45ED: +case 0x47ED: +case 0x49ED: +case 0x4BED: +case 0x4DED: +case 0x4FED: +case 0x41EE: +case 0x43EE: +case 0x45EE: +case 0x47EE: +case 0x49EE: +case 0x4BEE: +case 0x4DEE: +case 0x4FEE: +case 0x41EF: +case 0x43EF: +case 0x45EF: +case 0x47EF: +case 0x49EF: +case 0x4BEF: +case 0x4DEF: +case 0x4FEF: + +// LEA +case 0x41E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x43F0: +case 0x45F0: +case 0x47F0: +case 0x49F0: +case 0x4BF0: +case 0x4DF0: +case 0x4FF0: +case 0x41F1: +case 0x43F1: +case 0x45F1: +case 0x47F1: +case 0x49F1: +case 0x4BF1: +case 0x4DF1: +case 0x4FF1: +case 0x41F2: +case 0x43F2: +case 0x45F2: +case 0x47F2: +case 0x49F2: +case 0x4BF2: +case 0x4DF2: +case 0x4FF2: +case 0x41F3: +case 0x43F3: +case 0x45F3: +case 0x47F3: +case 0x49F3: +case 0x4BF3: +case 0x4DF3: +case 0x4FF3: +case 0x41F4: +case 0x43F4: +case 0x45F4: +case 0x47F4: +case 0x49F4: +case 0x4BF4: +case 0x4DF4: +case 0x4FF4: +case 0x41F5: +case 0x43F5: +case 0x45F5: +case 0x47F5: +case 0x49F5: +case 0x4BF5: +case 0x4DF5: +case 0x4FF5: +case 0x41F6: +case 0x43F6: +case 0x45F6: +case 0x47F6: +case 0x49F6: +case 0x4BF6: +case 0x4DF6: +case 0x4FF6: +case 0x41F7: +case 0x43F7: +case 0x45F7: +case 0x47F7: +case 0x49F7: +case 0x4BF7: +case 0x4DF7: +case 0x4FF7: + +// LEA +case 0x41F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) +case 0x43F8: +case 0x45F8: +case 0x47F8: +case 0x49F8: +case 0x4BF8: +case 0x4DF8: +case 0x4FF8: + +// LEA +case 0x41F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x43F9: +case 0x45F9: +case 0x47F9: +case 0x49F9: +case 0x4BF9: +case 0x4DF9: +case 0x4FF9: + +// LEA +case 0x41F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) +case 0x43FA: +case 0x45FA: +case 0x47FA: +case 0x49FA: +case 0x4BFA: +case 0x4DFA: +case 0x4FFA: + +// LEA +case 0x41FA: +{ + u32 adr; + u32 res; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x43FB: +case 0x45FB: +case 0x47FB: +case 0x49FB: +case 0x4BFB: +case 0x4DFB: +case 0x4FFB: + +// LEA +case 0x41FB: +{ + u32 adr; + u32 res; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + res = adr; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) diff --git a/yabause/src/c68k/c68k_op5.inc b/yabause/src/c68k/c68k_op5.inc new file mode 100644 index 0000000000..f11d085b15 --- /dev/null +++ b/yabause/src/c68k/c68k_op5.inc @@ -0,0 +1,8265 @@ +case 0x50C1: +case 0x50C2: +case 0x50C3: +case 0x50C4: +case 0x50C5: +case 0x50C6: +case 0x50C7: + +// STCC +case 0x50C0: +{ + u32 res; + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) +} +case 0x51C1: +case 0x51C2: +case 0x51C3: +case 0x51C4: +case 0x51C5: +case 0x51C6: +case 0x51C7: + +// STCC +case 0x51C0: +{ + u32 res; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x52C1: +case 0x52C2: +case 0x52C3: +case 0x52C4: +case 0x52C5: +case 0x52C6: +case 0x52C7: + +// STCC +case 0x52C0: +{ + u32 res; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x53C1: +case 0x53C2: +case 0x53C3: +case 0x53C4: +case 0x53C5: +case 0x53C6: +case 0x53C7: + +// STCC +case 0x53C0: +{ + u32 res; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x54C1: +case 0x54C2: +case 0x54C3: +case 0x54C4: +case 0x54C5: +case 0x54C6: +case 0x54C7: + +// STCC +case 0x54C0: +{ + u32 res; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x55C1: +case 0x55C2: +case 0x55C3: +case 0x55C4: +case 0x55C5: +case 0x55C6: +case 0x55C7: + +// STCC +case 0x55C0: +{ + u32 res; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x56C1: +case 0x56C2: +case 0x56C3: +case 0x56C4: +case 0x56C5: +case 0x56C6: +case 0x56C7: + +// STCC +case 0x56C0: +{ + u32 res; + if (CPU->flag_notZ) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x57C1: +case 0x57C2: +case 0x57C3: +case 0x57C4: +case 0x57C5: +case 0x57C6: +case 0x57C7: + +// STCC +case 0x57C0: +{ + u32 res; + if (!CPU->flag_notZ) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x58C1: +case 0x58C2: +case 0x58C3: +case 0x58C4: +case 0x58C5: +case 0x58C6: +case 0x58C7: + +// STCC +case 0x58C0: +{ + u32 res; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x59C1: +case 0x59C2: +case 0x59C3: +case 0x59C4: +case 0x59C5: +case 0x59C6: +case 0x59C7: + +// STCC +case 0x59C0: +{ + u32 res; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5AC1: +case 0x5AC2: +case 0x5AC3: +case 0x5AC4: +case 0x5AC5: +case 0x5AC6: +case 0x5AC7: + +// STCC +case 0x5AC0: +{ + u32 res; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5BC1: +case 0x5BC2: +case 0x5BC3: +case 0x5BC4: +case 0x5BC5: +case 0x5BC6: +case 0x5BC7: + +// STCC +case 0x5BC0: +{ + u32 res; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5CC1: +case 0x5CC2: +case 0x5CC3: +case 0x5CC4: +case 0x5CC5: +case 0x5CC6: +case 0x5CC7: + +// STCC +case 0x5CC0: +{ + u32 res; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5DC1: +case 0x5DC2: +case 0x5DC3: +case 0x5DC4: +case 0x5DC5: +case 0x5DC6: +case 0x5DC7: + +// STCC +case 0x5DC0: +{ + u32 res; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5EC1: +case 0x5EC2: +case 0x5EC3: +case 0x5EC4: +case 0x5EC5: +case 0x5EC6: +case 0x5EC7: + +// STCC +case 0x5EC0: +{ + u32 res; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x5FC1: +case 0x5FC2: +case 0x5FC3: +case 0x5FC4: +case 0x5FC5: +case 0x5FC6: +case 0x5FC7: + +// STCC +case 0x5FC0: +{ + u32 res; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(4) +} +case 0x50D1: +case 0x50D2: +case 0x50D3: +case 0x50D4: +case 0x50D5: +case 0x50D6: +case 0x50D7: + +// STCC +case 0x50D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x51D1: +case 0x51D2: +case 0x51D3: +case 0x51D4: +case 0x51D5: +case 0x51D6: +case 0x51D7: + +// STCC +case 0x51D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x52D1: +case 0x52D2: +case 0x52D3: +case 0x52D4: +case 0x52D5: +case 0x52D6: +case 0x52D7: + +// STCC +case 0x52D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x53D1: +case 0x53D2: +case 0x53D3: +case 0x53D4: +case 0x53D5: +case 0x53D6: +case 0x53D7: + +// STCC +case 0x53D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x54D1: +case 0x54D2: +case 0x54D3: +case 0x54D4: +case 0x54D5: +case 0x54D6: +case 0x54D7: + +// STCC +case 0x54D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x55D1: +case 0x55D2: +case 0x55D3: +case 0x55D4: +case 0x55D5: +case 0x55D6: +case 0x55D7: + +// STCC +case 0x55D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x56D1: +case 0x56D2: +case 0x56D3: +case 0x56D4: +case 0x56D5: +case 0x56D6: +case 0x56D7: + +// STCC +case 0x56D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x57D1: +case 0x57D2: +case 0x57D3: +case 0x57D4: +case 0x57D5: +case 0x57D6: +case 0x57D7: + +// STCC +case 0x57D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x58D1: +case 0x58D2: +case 0x58D3: +case 0x58D4: +case 0x58D5: +case 0x58D6: +case 0x58D7: + +// STCC +case 0x58D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x59D1: +case 0x59D2: +case 0x59D3: +case 0x59D4: +case 0x59D5: +case 0x59D6: +case 0x59D7: + +// STCC +case 0x59D0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5AD1: +case 0x5AD2: +case 0x5AD3: +case 0x5AD4: +case 0x5AD5: +case 0x5AD6: +case 0x5AD7: + +// STCC +case 0x5AD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5BD1: +case 0x5BD2: +case 0x5BD3: +case 0x5BD4: +case 0x5BD5: +case 0x5BD6: +case 0x5BD7: + +// STCC +case 0x5BD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5CD1: +case 0x5CD2: +case 0x5CD3: +case 0x5CD4: +case 0x5CD5: +case 0x5CD6: +case 0x5CD7: + +// STCC +case 0x5CD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5DD1: +case 0x5DD2: +case 0x5DD3: +case 0x5DD4: +case 0x5DD5: +case 0x5DD6: +case 0x5DD7: + +// STCC +case 0x5DD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5ED1: +case 0x5ED2: +case 0x5ED3: +case 0x5ED4: +case 0x5ED5: +case 0x5ED6: +case 0x5ED7: + +// STCC +case 0x5ED0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5FD1: +case 0x5FD2: +case 0x5FD3: +case 0x5FD4: +case 0x5FD5: +case 0x5FD6: +case 0x5FD7: + +// STCC +case 0x5FD0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x50D9: +case 0x50DA: +case 0x50DB: +case 0x50DC: +case 0x50DD: +case 0x50DE: + +// STCC +case 0x50D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x51D9: +case 0x51DA: +case 0x51DB: +case 0x51DC: +case 0x51DD: +case 0x51DE: + +// STCC +case 0x51D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x52D9: +case 0x52DA: +case 0x52DB: +case 0x52DC: +case 0x52DD: +case 0x52DE: + +// STCC +case 0x52D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x53D9: +case 0x53DA: +case 0x53DB: +case 0x53DC: +case 0x53DD: +case 0x53DE: + +// STCC +case 0x53D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x54D9: +case 0x54DA: +case 0x54DB: +case 0x54DC: +case 0x54DD: +case 0x54DE: + +// STCC +case 0x54D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x55D9: +case 0x55DA: +case 0x55DB: +case 0x55DC: +case 0x55DD: +case 0x55DE: + +// STCC +case 0x55D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x56D9: +case 0x56DA: +case 0x56DB: +case 0x56DC: +case 0x56DD: +case 0x56DE: + +// STCC +case 0x56D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x57D9: +case 0x57DA: +case 0x57DB: +case 0x57DC: +case 0x57DD: +case 0x57DE: + +// STCC +case 0x57D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x58D9: +case 0x58DA: +case 0x58DB: +case 0x58DC: +case 0x58DD: +case 0x58DE: + +// STCC +case 0x58D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x59D9: +case 0x59DA: +case 0x59DB: +case 0x59DC: +case 0x59DD: +case 0x59DE: + +// STCC +case 0x59D8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5AD9: +case 0x5ADA: +case 0x5ADB: +case 0x5ADC: +case 0x5ADD: +case 0x5ADE: + +// STCC +case 0x5AD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5BD9: +case 0x5BDA: +case 0x5BDB: +case 0x5BDC: +case 0x5BDD: +case 0x5BDE: + +// STCC +case 0x5BD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5CD9: +case 0x5CDA: +case 0x5CDB: +case 0x5CDC: +case 0x5CDD: +case 0x5CDE: + +// STCC +case 0x5CD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5DD9: +case 0x5DDA: +case 0x5DDB: +case 0x5DDC: +case 0x5DDD: +case 0x5DDE: + +// STCC +case 0x5DD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5ED9: +case 0x5EDA: +case 0x5EDB: +case 0x5EDC: +case 0x5EDD: +case 0x5EDE: + +// STCC +case 0x5ED8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x5FD9: +case 0x5FDA: +case 0x5FDB: +case 0x5FDC: +case 0x5FDD: +case 0x5FDE: + +// STCC +case 0x5FD8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} +case 0x50E1: +case 0x50E2: +case 0x50E3: +case 0x50E4: +case 0x50E5: +case 0x50E6: + +// STCC +case 0x50E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x51E1: +case 0x51E2: +case 0x51E3: +case 0x51E4: +case 0x51E5: +case 0x51E6: + +// STCC +case 0x51E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x52E1: +case 0x52E2: +case 0x52E3: +case 0x52E4: +case 0x52E5: +case 0x52E6: + +// STCC +case 0x52E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x53E1: +case 0x53E2: +case 0x53E3: +case 0x53E4: +case 0x53E5: +case 0x53E6: + +// STCC +case 0x53E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x54E1: +case 0x54E2: +case 0x54E3: +case 0x54E4: +case 0x54E5: +case 0x54E6: + +// STCC +case 0x54E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x55E1: +case 0x55E2: +case 0x55E3: +case 0x55E4: +case 0x55E5: +case 0x55E6: + +// STCC +case 0x55E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x56E1: +case 0x56E2: +case 0x56E3: +case 0x56E4: +case 0x56E5: +case 0x56E6: + +// STCC +case 0x56E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x57E1: +case 0x57E2: +case 0x57E3: +case 0x57E4: +case 0x57E5: +case 0x57E6: + +// STCC +case 0x57E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x58E1: +case 0x58E2: +case 0x58E3: +case 0x58E4: +case 0x58E5: +case 0x58E6: + +// STCC +case 0x58E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x59E1: +case 0x59E2: +case 0x59E3: +case 0x59E4: +case 0x59E5: +case 0x59E6: + +// STCC +case 0x59E0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5AE1: +case 0x5AE2: +case 0x5AE3: +case 0x5AE4: +case 0x5AE5: +case 0x5AE6: + +// STCC +case 0x5AE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5BE1: +case 0x5BE2: +case 0x5BE3: +case 0x5BE4: +case 0x5BE5: +case 0x5BE6: + +// STCC +case 0x5BE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5CE1: +case 0x5CE2: +case 0x5CE3: +case 0x5CE4: +case 0x5CE5: +case 0x5CE6: + +// STCC +case 0x5CE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5DE1: +case 0x5DE2: +case 0x5DE3: +case 0x5DE4: +case 0x5DE5: +case 0x5DE6: + +// STCC +case 0x5DE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5EE1: +case 0x5EE2: +case 0x5EE3: +case 0x5EE4: +case 0x5EE5: +case 0x5EE6: + +// STCC +case 0x5EE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x5FE1: +case 0x5FE2: +case 0x5FE3: +case 0x5FE4: +case 0x5FE5: +case 0x5FE6: + +// STCC +case 0x5FE0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x50E9: +case 0x50EA: +case 0x50EB: +case 0x50EC: +case 0x50ED: +case 0x50EE: +case 0x50EF: + +// STCC +case 0x50E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x51E9: +case 0x51EA: +case 0x51EB: +case 0x51EC: +case 0x51ED: +case 0x51EE: +case 0x51EF: + +// STCC +case 0x51E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x52E9: +case 0x52EA: +case 0x52EB: +case 0x52EC: +case 0x52ED: +case 0x52EE: +case 0x52EF: + +// STCC +case 0x52E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x53E9: +case 0x53EA: +case 0x53EB: +case 0x53EC: +case 0x53ED: +case 0x53EE: +case 0x53EF: + +// STCC +case 0x53E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x54E9: +case 0x54EA: +case 0x54EB: +case 0x54EC: +case 0x54ED: +case 0x54EE: +case 0x54EF: + +// STCC +case 0x54E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x55E9: +case 0x55EA: +case 0x55EB: +case 0x55EC: +case 0x55ED: +case 0x55EE: +case 0x55EF: + +// STCC +case 0x55E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x56E9: +case 0x56EA: +case 0x56EB: +case 0x56EC: +case 0x56ED: +case 0x56EE: +case 0x56EF: + +// STCC +case 0x56E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x57E9: +case 0x57EA: +case 0x57EB: +case 0x57EC: +case 0x57ED: +case 0x57EE: +case 0x57EF: + +// STCC +case 0x57E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x58E9: +case 0x58EA: +case 0x58EB: +case 0x58EC: +case 0x58ED: +case 0x58EE: +case 0x58EF: + +// STCC +case 0x58E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x59E9: +case 0x59EA: +case 0x59EB: +case 0x59EC: +case 0x59ED: +case 0x59EE: +case 0x59EF: + +// STCC +case 0x59E8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5AE9: +case 0x5AEA: +case 0x5AEB: +case 0x5AEC: +case 0x5AED: +case 0x5AEE: +case 0x5AEF: + +// STCC +case 0x5AE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5BE9: +case 0x5BEA: +case 0x5BEB: +case 0x5BEC: +case 0x5BED: +case 0x5BEE: +case 0x5BEF: + +// STCC +case 0x5BE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5CE9: +case 0x5CEA: +case 0x5CEB: +case 0x5CEC: +case 0x5CED: +case 0x5CEE: +case 0x5CEF: + +// STCC +case 0x5CE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5DE9: +case 0x5DEA: +case 0x5DEB: +case 0x5DEC: +case 0x5DED: +case 0x5DEE: +case 0x5DEF: + +// STCC +case 0x5DE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5EE9: +case 0x5EEA: +case 0x5EEB: +case 0x5EEC: +case 0x5EED: +case 0x5EEE: +case 0x5EEF: + +// STCC +case 0x5EE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x5FE9: +case 0x5FEA: +case 0x5FEB: +case 0x5FEC: +case 0x5FED: +case 0x5FEE: +case 0x5FEF: + +// STCC +case 0x5FE8: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} +case 0x50F1: +case 0x50F2: +case 0x50F3: +case 0x50F4: +case 0x50F5: +case 0x50F6: +case 0x50F7: + +// STCC +case 0x50F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x51F1: +case 0x51F2: +case 0x51F3: +case 0x51F4: +case 0x51F5: +case 0x51F6: +case 0x51F7: + +// STCC +case 0x51F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x52F1: +case 0x52F2: +case 0x52F3: +case 0x52F4: +case 0x52F5: +case 0x52F6: +case 0x52F7: + +// STCC +case 0x52F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x53F1: +case 0x53F2: +case 0x53F3: +case 0x53F4: +case 0x53F5: +case 0x53F6: +case 0x53F7: + +// STCC +case 0x53F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x54F1: +case 0x54F2: +case 0x54F3: +case 0x54F4: +case 0x54F5: +case 0x54F6: +case 0x54F7: + +// STCC +case 0x54F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x55F1: +case 0x55F2: +case 0x55F3: +case 0x55F4: +case 0x55F5: +case 0x55F6: +case 0x55F7: + +// STCC +case 0x55F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x56F1: +case 0x56F2: +case 0x56F3: +case 0x56F4: +case 0x56F5: +case 0x56F6: +case 0x56F7: + +// STCC +case 0x56F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x57F1: +case 0x57F2: +case 0x57F3: +case 0x57F4: +case 0x57F5: +case 0x57F6: +case 0x57F7: + +// STCC +case 0x57F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x58F1: +case 0x58F2: +case 0x58F3: +case 0x58F4: +case 0x58F5: +case 0x58F6: +case 0x58F7: + +// STCC +case 0x58F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x59F1: +case 0x59F2: +case 0x59F3: +case 0x59F4: +case 0x59F5: +case 0x59F6: +case 0x59F7: + +// STCC +case 0x59F0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5AF1: +case 0x5AF2: +case 0x5AF3: +case 0x5AF4: +case 0x5AF5: +case 0x5AF6: +case 0x5AF7: + +// STCC +case 0x5AF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5BF1: +case 0x5BF2: +case 0x5BF3: +case 0x5BF4: +case 0x5BF5: +case 0x5BF6: +case 0x5BF7: + +// STCC +case 0x5BF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5CF1: +case 0x5CF2: +case 0x5CF3: +case 0x5CF4: +case 0x5CF5: +case 0x5CF6: +case 0x5CF7: + +// STCC +case 0x5CF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5DF1: +case 0x5DF2: +case 0x5DF3: +case 0x5DF4: +case 0x5DF5: +case 0x5DF6: +case 0x5DF7: + +// STCC +case 0x5DF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5EF1: +case 0x5EF2: +case 0x5EF3: +case 0x5EF4: +case 0x5EF5: +case 0x5EF6: +case 0x5EF7: + +// STCC +case 0x5EF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} +case 0x5FF1: +case 0x5FF2: +case 0x5FF3: +case 0x5FF4: +case 0x5FF5: +case 0x5FF6: +case 0x5FF7: + +// STCC +case 0x5FF0: +{ + u32 adr; + u32 res; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(18) +} + +// STCC +case 0x50F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x51F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x52F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x53F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x54F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x55F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x56F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x57F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x58F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x59F8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5AF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5BF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5CF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5DF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5EF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x5FF8: +{ + u32 adr; + u32 res; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(16) +} + +// STCC +case 0x50F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x51F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x52F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x53F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x54F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x55F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x56F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x57F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x58F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x59F9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5AF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5BF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5CF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5DF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5EF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x5FF9: +{ + u32 adr; + u32 res; + adr = (s32)FETCH_LONG; + PC += 4; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(20) +} + +// STCC +case 0x50DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x51DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x52DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x53DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x54DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x55DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x56DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x57DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x58DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x59DF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5ADF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5BDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5CDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5DDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5EDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x5FDF: +{ + u32 adr; + u32 res; + adr = CPU->A[7]; + CPU->A[7] += 2; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(12) +} + +// STCC +case 0x50E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x51E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x52E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x53E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x54E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (!(CPU->flag_C & 0x100)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x55E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_C & 0x100) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x56E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x57E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (!CPU->flag_notZ) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x58E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (!(CPU->flag_V & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x59E7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_V & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5AE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (!(CPU->flag_N & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5BE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_N & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5CE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5DE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5EE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} + +// STCC +case 0x5FE7: +{ + u32 adr; + u32 res; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = 0xFF; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) + } + res = 0; + PRE_IO + WRITE_BYTE_F(adr, res) + POST_IO + RET(14) +} +case 0x50C9: +case 0x50CA: +case 0x50CB: +case 0x50CC: +case 0x50CD: +case 0x50CE: +case 0x50CF: + +// DBCC +case 0x50C8: +{ + PC += 2; +} +RET(12) +case 0x51C9: +case 0x51CA: +case 0x51CB: +case 0x51CC: +case 0x51CD: +case 0x51CE: +case 0x51CF: + +// DBCC +case 0x51C8: +{ + u32 res; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(14) +case 0x52C9: +case 0x52CA: +case 0x52CB: +case 0x52CC: +case 0x52CD: +case 0x52CE: +case 0x52CF: + +// DBCC +case 0x52C8: +{ + u32 res; + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x53C9: +case 0x53CA: +case 0x53CB: +case 0x53CC: +case 0x53CD: +case 0x53CE: +case 0x53CF: + +// DBCC +case 0x53C8: +{ + u32 res; + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x54C9: +case 0x54CA: +case 0x54CB: +case 0x54CC: +case 0x54CD: +case 0x54CE: +case 0x54CF: + +// DBCC +case 0x54C8: +{ + u32 res; + if (CPU->flag_C & 0x100) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x55C9: +case 0x55CA: +case 0x55CB: +case 0x55CC: +case 0x55CD: +case 0x55CE: +case 0x55CF: + +// DBCC +case 0x55C8: +{ + u32 res; + if (!(CPU->flag_C & 0x100)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x56C9: +case 0x56CA: +case 0x56CB: +case 0x56CC: +case 0x56CD: +case 0x56CE: +case 0x56CF: + +// DBCC +case 0x56C8: +{ + u32 res; + if (!CPU->flag_notZ) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x57C9: +case 0x57CA: +case 0x57CB: +case 0x57CC: +case 0x57CD: +case 0x57CE: +case 0x57CF: + +// DBCC +case 0x57C8: +{ + u32 res; + if (CPU->flag_notZ) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x58C9: +case 0x58CA: +case 0x58CB: +case 0x58CC: +case 0x58CD: +case 0x58CE: +case 0x58CF: + +// DBCC +case 0x58C8: +{ + u32 res; + if (CPU->flag_V & 0x80) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x59C9: +case 0x59CA: +case 0x59CB: +case 0x59CC: +case 0x59CD: +case 0x59CE: +case 0x59CF: + +// DBCC +case 0x59C8: +{ + u32 res; + if (!(CPU->flag_V & 0x80)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5AC9: +case 0x5ACA: +case 0x5ACB: +case 0x5ACC: +case 0x5ACD: +case 0x5ACE: +case 0x5ACF: + +// DBCC +case 0x5AC8: +{ + u32 res; + if (CPU->flag_N & 0x80) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5BC9: +case 0x5BCA: +case 0x5BCB: +case 0x5BCC: +case 0x5BCD: +case 0x5BCE: +case 0x5BCF: + +// DBCC +case 0x5BC8: +{ + u32 res; + if (!(CPU->flag_N & 0x80)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5CC9: +case 0x5CCA: +case 0x5CCB: +case 0x5CCC: +case 0x5CCD: +case 0x5CCE: +case 0x5CCF: + +// DBCC +case 0x5CC8: +{ + u32 res; + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5DC9: +case 0x5DCA: +case 0x5DCB: +case 0x5DCC: +case 0x5DCD: +case 0x5DCE: +case 0x5DCF: + +// DBCC +case 0x5DC8: +{ + u32 res; + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5EC9: +case 0x5ECA: +case 0x5ECB: +case 0x5ECC: +case 0x5ECD: +case 0x5ECE: +case 0x5ECF: + +// DBCC +case 0x5EC8: +{ + u32 res; + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5FC9: +case 0x5FCA: +case 0x5FCB: +case 0x5FCC: +case 0x5FCD: +case 0x5FCE: +case 0x5FCF: + +// DBCC +case 0x5FC8: +{ + u32 res; + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res--; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + if ((s32)res != -1) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + } + else + { + PC += 2; + RET(12) + } + PC += 2; +} +RET(14) +case 0x5200: +case 0x5400: +case 0x5600: +case 0x5800: +case 0x5A00: +case 0x5C00: +case 0x5E00: +case 0x5001: +case 0x5201: +case 0x5401: +case 0x5601: +case 0x5801: +case 0x5A01: +case 0x5C01: +case 0x5E01: +case 0x5002: +case 0x5202: +case 0x5402: +case 0x5602: +case 0x5802: +case 0x5A02: +case 0x5C02: +case 0x5E02: +case 0x5003: +case 0x5203: +case 0x5403: +case 0x5603: +case 0x5803: +case 0x5A03: +case 0x5C03: +case 0x5E03: +case 0x5004: +case 0x5204: +case 0x5404: +case 0x5604: +case 0x5804: +case 0x5A04: +case 0x5C04: +case 0x5E04: +case 0x5005: +case 0x5205: +case 0x5405: +case 0x5605: +case 0x5805: +case 0x5A05: +case 0x5C05: +case 0x5E05: +case 0x5006: +case 0x5206: +case 0x5406: +case 0x5606: +case 0x5806: +case 0x5A06: +case 0x5C06: +case 0x5E06: +case 0x5007: +case 0x5207: +case 0x5407: +case 0x5607: +case 0x5807: +case 0x5A07: +case 0x5C07: +case 0x5E07: + +// ADDQ +case 0x5000: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u8)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x5210: +case 0x5410: +case 0x5610: +case 0x5810: +case 0x5A10: +case 0x5C10: +case 0x5E10: +case 0x5011: +case 0x5211: +case 0x5411: +case 0x5611: +case 0x5811: +case 0x5A11: +case 0x5C11: +case 0x5E11: +case 0x5012: +case 0x5212: +case 0x5412: +case 0x5612: +case 0x5812: +case 0x5A12: +case 0x5C12: +case 0x5E12: +case 0x5013: +case 0x5213: +case 0x5413: +case 0x5613: +case 0x5813: +case 0x5A13: +case 0x5C13: +case 0x5E13: +case 0x5014: +case 0x5214: +case 0x5414: +case 0x5614: +case 0x5814: +case 0x5A14: +case 0x5C14: +case 0x5E14: +case 0x5015: +case 0x5215: +case 0x5415: +case 0x5615: +case 0x5815: +case 0x5A15: +case 0x5C15: +case 0x5E15: +case 0x5016: +case 0x5216: +case 0x5416: +case 0x5616: +case 0x5816: +case 0x5A16: +case 0x5C16: +case 0x5E16: +case 0x5017: +case 0x5217: +case 0x5417: +case 0x5617: +case 0x5817: +case 0x5A17: +case 0x5C17: +case 0x5E17: + +// ADDQ +case 0x5010: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5218: +case 0x5418: +case 0x5618: +case 0x5818: +case 0x5A18: +case 0x5C18: +case 0x5E18: +case 0x5019: +case 0x5219: +case 0x5419: +case 0x5619: +case 0x5819: +case 0x5A19: +case 0x5C19: +case 0x5E19: +case 0x501A: +case 0x521A: +case 0x541A: +case 0x561A: +case 0x581A: +case 0x5A1A: +case 0x5C1A: +case 0x5E1A: +case 0x501B: +case 0x521B: +case 0x541B: +case 0x561B: +case 0x581B: +case 0x5A1B: +case 0x5C1B: +case 0x5E1B: +case 0x501C: +case 0x521C: +case 0x541C: +case 0x561C: +case 0x581C: +case 0x5A1C: +case 0x5C1C: +case 0x5E1C: +case 0x501D: +case 0x521D: +case 0x541D: +case 0x561D: +case 0x581D: +case 0x5A1D: +case 0x5C1D: +case 0x5E1D: +case 0x501E: +case 0x521E: +case 0x541E: +case 0x561E: +case 0x581E: +case 0x5A1E: +case 0x5C1E: +case 0x5E1E: + +// ADDQ +case 0x5018: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5220: +case 0x5420: +case 0x5620: +case 0x5820: +case 0x5A20: +case 0x5C20: +case 0x5E20: +case 0x5021: +case 0x5221: +case 0x5421: +case 0x5621: +case 0x5821: +case 0x5A21: +case 0x5C21: +case 0x5E21: +case 0x5022: +case 0x5222: +case 0x5422: +case 0x5622: +case 0x5822: +case 0x5A22: +case 0x5C22: +case 0x5E22: +case 0x5023: +case 0x5223: +case 0x5423: +case 0x5623: +case 0x5823: +case 0x5A23: +case 0x5C23: +case 0x5E23: +case 0x5024: +case 0x5224: +case 0x5424: +case 0x5624: +case 0x5824: +case 0x5A24: +case 0x5C24: +case 0x5E24: +case 0x5025: +case 0x5225: +case 0x5425: +case 0x5625: +case 0x5825: +case 0x5A25: +case 0x5C25: +case 0x5E25: +case 0x5026: +case 0x5226: +case 0x5426: +case 0x5626: +case 0x5826: +case 0x5A26: +case 0x5C26: +case 0x5E26: + +// ADDQ +case 0x5020: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x5228: +case 0x5428: +case 0x5628: +case 0x5828: +case 0x5A28: +case 0x5C28: +case 0x5E28: +case 0x5029: +case 0x5229: +case 0x5429: +case 0x5629: +case 0x5829: +case 0x5A29: +case 0x5C29: +case 0x5E29: +case 0x502A: +case 0x522A: +case 0x542A: +case 0x562A: +case 0x582A: +case 0x5A2A: +case 0x5C2A: +case 0x5E2A: +case 0x502B: +case 0x522B: +case 0x542B: +case 0x562B: +case 0x582B: +case 0x5A2B: +case 0x5C2B: +case 0x5E2B: +case 0x502C: +case 0x522C: +case 0x542C: +case 0x562C: +case 0x582C: +case 0x5A2C: +case 0x5C2C: +case 0x5E2C: +case 0x502D: +case 0x522D: +case 0x542D: +case 0x562D: +case 0x582D: +case 0x5A2D: +case 0x5C2D: +case 0x5E2D: +case 0x502E: +case 0x522E: +case 0x542E: +case 0x562E: +case 0x582E: +case 0x5A2E: +case 0x5C2E: +case 0x5E2E: +case 0x502F: +case 0x522F: +case 0x542F: +case 0x562F: +case 0x582F: +case 0x5A2F: +case 0x5C2F: +case 0x5E2F: + +// ADDQ +case 0x5028: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x5230: +case 0x5430: +case 0x5630: +case 0x5830: +case 0x5A30: +case 0x5C30: +case 0x5E30: +case 0x5031: +case 0x5231: +case 0x5431: +case 0x5631: +case 0x5831: +case 0x5A31: +case 0x5C31: +case 0x5E31: +case 0x5032: +case 0x5232: +case 0x5432: +case 0x5632: +case 0x5832: +case 0x5A32: +case 0x5C32: +case 0x5E32: +case 0x5033: +case 0x5233: +case 0x5433: +case 0x5633: +case 0x5833: +case 0x5A33: +case 0x5C33: +case 0x5E33: +case 0x5034: +case 0x5234: +case 0x5434: +case 0x5634: +case 0x5834: +case 0x5A34: +case 0x5C34: +case 0x5E34: +case 0x5035: +case 0x5235: +case 0x5435: +case 0x5635: +case 0x5835: +case 0x5A35: +case 0x5C35: +case 0x5E35: +case 0x5036: +case 0x5236: +case 0x5436: +case 0x5636: +case 0x5836: +case 0x5A36: +case 0x5C36: +case 0x5E36: +case 0x5037: +case 0x5237: +case 0x5437: +case 0x5637: +case 0x5837: +case 0x5A37: +case 0x5C37: +case 0x5E37: + +// ADDQ +case 0x5030: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x5238: +case 0x5438: +case 0x5638: +case 0x5838: +case 0x5A38: +case 0x5C38: +case 0x5E38: + +// ADDQ +case 0x5038: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x5239: +case 0x5439: +case 0x5639: +case 0x5839: +case 0x5A39: +case 0x5C39: +case 0x5E39: + +// ADDQ +case 0x5039: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x521F: +case 0x541F: +case 0x561F: +case 0x581F: +case 0x5A1F: +case 0x5C1F: +case 0x5E1F: + +// ADDQ +case 0x501F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5227: +case 0x5427: +case 0x5627: +case 0x5827: +case 0x5A27: +case 0x5C27: +case 0x5E27: + +// ADDQ +case 0x5027: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x5240: +case 0x5440: +case 0x5640: +case 0x5840: +case 0x5A40: +case 0x5C40: +case 0x5E40: +case 0x5041: +case 0x5241: +case 0x5441: +case 0x5641: +case 0x5841: +case 0x5A41: +case 0x5C41: +case 0x5E41: +case 0x5042: +case 0x5242: +case 0x5442: +case 0x5642: +case 0x5842: +case 0x5A42: +case 0x5C42: +case 0x5E42: +case 0x5043: +case 0x5243: +case 0x5443: +case 0x5643: +case 0x5843: +case 0x5A43: +case 0x5C43: +case 0x5E43: +case 0x5044: +case 0x5244: +case 0x5444: +case 0x5644: +case 0x5844: +case 0x5A44: +case 0x5C44: +case 0x5E44: +case 0x5045: +case 0x5245: +case 0x5445: +case 0x5645: +case 0x5845: +case 0x5A45: +case 0x5C45: +case 0x5E45: +case 0x5046: +case 0x5246: +case 0x5446: +case 0x5646: +case 0x5846: +case 0x5A46: +case 0x5C46: +case 0x5E46: +case 0x5047: +case 0x5247: +case 0x5447: +case 0x5647: +case 0x5847: +case 0x5A47: +case 0x5C47: +case 0x5E47: + +// ADDQ +case 0x5040: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u16)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x5248: +case 0x5448: +case 0x5648: +case 0x5848: +case 0x5A48: +case 0x5C48: +case 0x5E48: +case 0x5049: +case 0x5249: +case 0x5449: +case 0x5649: +case 0x5849: +case 0x5A49: +case 0x5C49: +case 0x5E49: +case 0x504A: +case 0x524A: +case 0x544A: +case 0x564A: +case 0x584A: +case 0x5A4A: +case 0x5C4A: +case 0x5E4A: +case 0x504B: +case 0x524B: +case 0x544B: +case 0x564B: +case 0x584B: +case 0x5A4B: +case 0x5C4B: +case 0x5E4B: +case 0x504C: +case 0x524C: +case 0x544C: +case 0x564C: +case 0x584C: +case 0x5A4C: +case 0x5C4C: +case 0x5E4C: +case 0x504D: +case 0x524D: +case 0x544D: +case 0x564D: +case 0x584D: +case 0x5A4D: +case 0x5C4D: +case 0x5E4D: +case 0x504E: +case 0x524E: +case 0x544E: +case 0x564E: +case 0x584E: +case 0x5A4E: +case 0x5C4E: +case 0x5E4E: +case 0x504F: +case 0x524F: +case 0x544F: +case 0x564F: +case 0x584F: +case 0x5A4F: +case 0x5C4F: +case 0x5E4F: + +// ADDQ +case 0x5048: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->A[(Opcode >> 0) & 7]; + res = dst + src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(8) +case 0x5250: +case 0x5450: +case 0x5650: +case 0x5850: +case 0x5A50: +case 0x5C50: +case 0x5E50: +case 0x5051: +case 0x5251: +case 0x5451: +case 0x5651: +case 0x5851: +case 0x5A51: +case 0x5C51: +case 0x5E51: +case 0x5052: +case 0x5252: +case 0x5452: +case 0x5652: +case 0x5852: +case 0x5A52: +case 0x5C52: +case 0x5E52: +case 0x5053: +case 0x5253: +case 0x5453: +case 0x5653: +case 0x5853: +case 0x5A53: +case 0x5C53: +case 0x5E53: +case 0x5054: +case 0x5254: +case 0x5454: +case 0x5654: +case 0x5854: +case 0x5A54: +case 0x5C54: +case 0x5E54: +case 0x5055: +case 0x5255: +case 0x5455: +case 0x5655: +case 0x5855: +case 0x5A55: +case 0x5C55: +case 0x5E55: +case 0x5056: +case 0x5256: +case 0x5456: +case 0x5656: +case 0x5856: +case 0x5A56: +case 0x5C56: +case 0x5E56: +case 0x5057: +case 0x5257: +case 0x5457: +case 0x5657: +case 0x5857: +case 0x5A57: +case 0x5C57: +case 0x5E57: + +// ADDQ +case 0x5050: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5258: +case 0x5458: +case 0x5658: +case 0x5858: +case 0x5A58: +case 0x5C58: +case 0x5E58: +case 0x5059: +case 0x5259: +case 0x5459: +case 0x5659: +case 0x5859: +case 0x5A59: +case 0x5C59: +case 0x5E59: +case 0x505A: +case 0x525A: +case 0x545A: +case 0x565A: +case 0x585A: +case 0x5A5A: +case 0x5C5A: +case 0x5E5A: +case 0x505B: +case 0x525B: +case 0x545B: +case 0x565B: +case 0x585B: +case 0x5A5B: +case 0x5C5B: +case 0x5E5B: +case 0x505C: +case 0x525C: +case 0x545C: +case 0x565C: +case 0x585C: +case 0x5A5C: +case 0x5C5C: +case 0x5E5C: +case 0x505D: +case 0x525D: +case 0x545D: +case 0x565D: +case 0x585D: +case 0x5A5D: +case 0x5C5D: +case 0x5E5D: +case 0x505E: +case 0x525E: +case 0x545E: +case 0x565E: +case 0x585E: +case 0x5A5E: +case 0x5C5E: +case 0x5E5E: + +// ADDQ +case 0x5058: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5260: +case 0x5460: +case 0x5660: +case 0x5860: +case 0x5A60: +case 0x5C60: +case 0x5E60: +case 0x5061: +case 0x5261: +case 0x5461: +case 0x5661: +case 0x5861: +case 0x5A61: +case 0x5C61: +case 0x5E61: +case 0x5062: +case 0x5262: +case 0x5462: +case 0x5662: +case 0x5862: +case 0x5A62: +case 0x5C62: +case 0x5E62: +case 0x5063: +case 0x5263: +case 0x5463: +case 0x5663: +case 0x5863: +case 0x5A63: +case 0x5C63: +case 0x5E63: +case 0x5064: +case 0x5264: +case 0x5464: +case 0x5664: +case 0x5864: +case 0x5A64: +case 0x5C64: +case 0x5E64: +case 0x5065: +case 0x5265: +case 0x5465: +case 0x5665: +case 0x5865: +case 0x5A65: +case 0x5C65: +case 0x5E65: +case 0x5066: +case 0x5266: +case 0x5466: +case 0x5666: +case 0x5866: +case 0x5A66: +case 0x5C66: +case 0x5E66: + +// ADDQ +case 0x5060: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x5268: +case 0x5468: +case 0x5668: +case 0x5868: +case 0x5A68: +case 0x5C68: +case 0x5E68: +case 0x5069: +case 0x5269: +case 0x5469: +case 0x5669: +case 0x5869: +case 0x5A69: +case 0x5C69: +case 0x5E69: +case 0x506A: +case 0x526A: +case 0x546A: +case 0x566A: +case 0x586A: +case 0x5A6A: +case 0x5C6A: +case 0x5E6A: +case 0x506B: +case 0x526B: +case 0x546B: +case 0x566B: +case 0x586B: +case 0x5A6B: +case 0x5C6B: +case 0x5E6B: +case 0x506C: +case 0x526C: +case 0x546C: +case 0x566C: +case 0x586C: +case 0x5A6C: +case 0x5C6C: +case 0x5E6C: +case 0x506D: +case 0x526D: +case 0x546D: +case 0x566D: +case 0x586D: +case 0x5A6D: +case 0x5C6D: +case 0x5E6D: +case 0x506E: +case 0x526E: +case 0x546E: +case 0x566E: +case 0x586E: +case 0x5A6E: +case 0x5C6E: +case 0x5E6E: +case 0x506F: +case 0x526F: +case 0x546F: +case 0x566F: +case 0x586F: +case 0x5A6F: +case 0x5C6F: +case 0x5E6F: + +// ADDQ +case 0x5068: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x5270: +case 0x5470: +case 0x5670: +case 0x5870: +case 0x5A70: +case 0x5C70: +case 0x5E70: +case 0x5071: +case 0x5271: +case 0x5471: +case 0x5671: +case 0x5871: +case 0x5A71: +case 0x5C71: +case 0x5E71: +case 0x5072: +case 0x5272: +case 0x5472: +case 0x5672: +case 0x5872: +case 0x5A72: +case 0x5C72: +case 0x5E72: +case 0x5073: +case 0x5273: +case 0x5473: +case 0x5673: +case 0x5873: +case 0x5A73: +case 0x5C73: +case 0x5E73: +case 0x5074: +case 0x5274: +case 0x5474: +case 0x5674: +case 0x5874: +case 0x5A74: +case 0x5C74: +case 0x5E74: +case 0x5075: +case 0x5275: +case 0x5475: +case 0x5675: +case 0x5875: +case 0x5A75: +case 0x5C75: +case 0x5E75: +case 0x5076: +case 0x5276: +case 0x5476: +case 0x5676: +case 0x5876: +case 0x5A76: +case 0x5C76: +case 0x5E76: +case 0x5077: +case 0x5277: +case 0x5477: +case 0x5677: +case 0x5877: +case 0x5A77: +case 0x5C77: +case 0x5E77: + +// ADDQ +case 0x5070: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x5278: +case 0x5478: +case 0x5678: +case 0x5878: +case 0x5A78: +case 0x5C78: +case 0x5E78: + +// ADDQ +case 0x5078: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x5279: +case 0x5479: +case 0x5679: +case 0x5879: +case 0x5A79: +case 0x5C79: +case 0x5E79: + +// ADDQ +case 0x5079: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x525F: +case 0x545F: +case 0x565F: +case 0x585F: +case 0x5A5F: +case 0x5C5F: +case 0x5E5F: + +// ADDQ +case 0x505F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5267: +case 0x5467: +case 0x5667: +case 0x5867: +case 0x5A67: +case 0x5C67: +case 0x5E67: + +// ADDQ +case 0x5067: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x5280: +case 0x5480: +case 0x5680: +case 0x5880: +case 0x5A80: +case 0x5C80: +case 0x5E80: +case 0x5081: +case 0x5281: +case 0x5481: +case 0x5681: +case 0x5881: +case 0x5A81: +case 0x5C81: +case 0x5E81: +case 0x5082: +case 0x5282: +case 0x5482: +case 0x5682: +case 0x5882: +case 0x5A82: +case 0x5C82: +case 0x5E82: +case 0x5083: +case 0x5283: +case 0x5483: +case 0x5683: +case 0x5883: +case 0x5A83: +case 0x5C83: +case 0x5E83: +case 0x5084: +case 0x5284: +case 0x5484: +case 0x5684: +case 0x5884: +case 0x5A84: +case 0x5C84: +case 0x5E84: +case 0x5085: +case 0x5285: +case 0x5485: +case 0x5685: +case 0x5885: +case 0x5A85: +case 0x5C85: +case 0x5E85: +case 0x5086: +case 0x5286: +case 0x5486: +case 0x5686: +case 0x5886: +case 0x5A86: +case 0x5C86: +case 0x5E86: +case 0x5087: +case 0x5287: +case 0x5487: +case 0x5687: +case 0x5887: +case 0x5A87: +case 0x5C87: +case 0x5E87: + +// ADDQ +case 0x5080: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->D[(Opcode >> 0) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x5288: +case 0x5488: +case 0x5688: +case 0x5888: +case 0x5A88: +case 0x5C88: +case 0x5E88: +case 0x5089: +case 0x5289: +case 0x5489: +case 0x5689: +case 0x5889: +case 0x5A89: +case 0x5C89: +case 0x5E89: +case 0x508A: +case 0x528A: +case 0x548A: +case 0x568A: +case 0x588A: +case 0x5A8A: +case 0x5C8A: +case 0x5E8A: +case 0x508B: +case 0x528B: +case 0x548B: +case 0x568B: +case 0x588B: +case 0x5A8B: +case 0x5C8B: +case 0x5E8B: +case 0x508C: +case 0x528C: +case 0x548C: +case 0x568C: +case 0x588C: +case 0x5A8C: +case 0x5C8C: +case 0x5E8C: +case 0x508D: +case 0x528D: +case 0x548D: +case 0x568D: +case 0x588D: +case 0x5A8D: +case 0x5C8D: +case 0x5E8D: +case 0x508E: +case 0x528E: +case 0x548E: +case 0x568E: +case 0x588E: +case 0x5A8E: +case 0x5C8E: +case 0x5E8E: +case 0x508F: +case 0x528F: +case 0x548F: +case 0x568F: +case 0x588F: +case 0x5A8F: +case 0x5C8F: +case 0x5E8F: + +// ADDQ +case 0x5088: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->A[(Opcode >> 0) & 7]; + res = dst + src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(8) +case 0x5290: +case 0x5490: +case 0x5690: +case 0x5890: +case 0x5A90: +case 0x5C90: +case 0x5E90: +case 0x5091: +case 0x5291: +case 0x5491: +case 0x5691: +case 0x5891: +case 0x5A91: +case 0x5C91: +case 0x5E91: +case 0x5092: +case 0x5292: +case 0x5492: +case 0x5692: +case 0x5892: +case 0x5A92: +case 0x5C92: +case 0x5E92: +case 0x5093: +case 0x5293: +case 0x5493: +case 0x5693: +case 0x5893: +case 0x5A93: +case 0x5C93: +case 0x5E93: +case 0x5094: +case 0x5294: +case 0x5494: +case 0x5694: +case 0x5894: +case 0x5A94: +case 0x5C94: +case 0x5E94: +case 0x5095: +case 0x5295: +case 0x5495: +case 0x5695: +case 0x5895: +case 0x5A95: +case 0x5C95: +case 0x5E95: +case 0x5096: +case 0x5296: +case 0x5496: +case 0x5696: +case 0x5896: +case 0x5A96: +case 0x5C96: +case 0x5E96: +case 0x5097: +case 0x5297: +case 0x5497: +case 0x5697: +case 0x5897: +case 0x5A97: +case 0x5C97: +case 0x5E97: + +// ADDQ +case 0x5090: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x5298: +case 0x5498: +case 0x5698: +case 0x5898: +case 0x5A98: +case 0x5C98: +case 0x5E98: +case 0x5099: +case 0x5299: +case 0x5499: +case 0x5699: +case 0x5899: +case 0x5A99: +case 0x5C99: +case 0x5E99: +case 0x509A: +case 0x529A: +case 0x549A: +case 0x569A: +case 0x589A: +case 0x5A9A: +case 0x5C9A: +case 0x5E9A: +case 0x509B: +case 0x529B: +case 0x549B: +case 0x569B: +case 0x589B: +case 0x5A9B: +case 0x5C9B: +case 0x5E9B: +case 0x509C: +case 0x529C: +case 0x549C: +case 0x569C: +case 0x589C: +case 0x5A9C: +case 0x5C9C: +case 0x5E9C: +case 0x509D: +case 0x529D: +case 0x549D: +case 0x569D: +case 0x589D: +case 0x5A9D: +case 0x5C9D: +case 0x5E9D: +case 0x509E: +case 0x529E: +case 0x549E: +case 0x569E: +case 0x589E: +case 0x5A9E: +case 0x5C9E: +case 0x5E9E: + +// ADDQ +case 0x5098: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x52A0: +case 0x54A0: +case 0x56A0: +case 0x58A0: +case 0x5AA0: +case 0x5CA0: +case 0x5EA0: +case 0x50A1: +case 0x52A1: +case 0x54A1: +case 0x56A1: +case 0x58A1: +case 0x5AA1: +case 0x5CA1: +case 0x5EA1: +case 0x50A2: +case 0x52A2: +case 0x54A2: +case 0x56A2: +case 0x58A2: +case 0x5AA2: +case 0x5CA2: +case 0x5EA2: +case 0x50A3: +case 0x52A3: +case 0x54A3: +case 0x56A3: +case 0x58A3: +case 0x5AA3: +case 0x5CA3: +case 0x5EA3: +case 0x50A4: +case 0x52A4: +case 0x54A4: +case 0x56A4: +case 0x58A4: +case 0x5AA4: +case 0x5CA4: +case 0x5EA4: +case 0x50A5: +case 0x52A5: +case 0x54A5: +case 0x56A5: +case 0x58A5: +case 0x5AA5: +case 0x5CA5: +case 0x5EA5: +case 0x50A6: +case 0x52A6: +case 0x54A6: +case 0x56A6: +case 0x58A6: +case 0x5AA6: +case 0x5CA6: +case 0x5EA6: + +// ADDQ +case 0x50A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x52A8: +case 0x54A8: +case 0x56A8: +case 0x58A8: +case 0x5AA8: +case 0x5CA8: +case 0x5EA8: +case 0x50A9: +case 0x52A9: +case 0x54A9: +case 0x56A9: +case 0x58A9: +case 0x5AA9: +case 0x5CA9: +case 0x5EA9: +case 0x50AA: +case 0x52AA: +case 0x54AA: +case 0x56AA: +case 0x58AA: +case 0x5AAA: +case 0x5CAA: +case 0x5EAA: +case 0x50AB: +case 0x52AB: +case 0x54AB: +case 0x56AB: +case 0x58AB: +case 0x5AAB: +case 0x5CAB: +case 0x5EAB: +case 0x50AC: +case 0x52AC: +case 0x54AC: +case 0x56AC: +case 0x58AC: +case 0x5AAC: +case 0x5CAC: +case 0x5EAC: +case 0x50AD: +case 0x52AD: +case 0x54AD: +case 0x56AD: +case 0x58AD: +case 0x5AAD: +case 0x5CAD: +case 0x5EAD: +case 0x50AE: +case 0x52AE: +case 0x54AE: +case 0x56AE: +case 0x58AE: +case 0x5AAE: +case 0x5CAE: +case 0x5EAE: +case 0x50AF: +case 0x52AF: +case 0x54AF: +case 0x56AF: +case 0x58AF: +case 0x5AAF: +case 0x5CAF: +case 0x5EAF: + +// ADDQ +case 0x50A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x52B0: +case 0x54B0: +case 0x56B0: +case 0x58B0: +case 0x5AB0: +case 0x5CB0: +case 0x5EB0: +case 0x50B1: +case 0x52B1: +case 0x54B1: +case 0x56B1: +case 0x58B1: +case 0x5AB1: +case 0x5CB1: +case 0x5EB1: +case 0x50B2: +case 0x52B2: +case 0x54B2: +case 0x56B2: +case 0x58B2: +case 0x5AB2: +case 0x5CB2: +case 0x5EB2: +case 0x50B3: +case 0x52B3: +case 0x54B3: +case 0x56B3: +case 0x58B3: +case 0x5AB3: +case 0x5CB3: +case 0x5EB3: +case 0x50B4: +case 0x52B4: +case 0x54B4: +case 0x56B4: +case 0x58B4: +case 0x5AB4: +case 0x5CB4: +case 0x5EB4: +case 0x50B5: +case 0x52B5: +case 0x54B5: +case 0x56B5: +case 0x58B5: +case 0x5AB5: +case 0x5CB5: +case 0x5EB5: +case 0x50B6: +case 0x52B6: +case 0x54B6: +case 0x56B6: +case 0x58B6: +case 0x5AB6: +case 0x5CB6: +case 0x5EB6: +case 0x50B7: +case 0x52B7: +case 0x54B7: +case 0x56B7: +case 0x58B7: +case 0x5AB7: +case 0x5CB7: +case 0x5EB7: + +// ADDQ +case 0x50B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x52B8: +case 0x54B8: +case 0x56B8: +case 0x58B8: +case 0x5AB8: +case 0x5CB8: +case 0x5EB8: + +// ADDQ +case 0x50B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x52B9: +case 0x54B9: +case 0x56B9: +case 0x58B9: +case 0x5AB9: +case 0x5CB9: +case 0x5EB9: + +// ADDQ +case 0x50B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x529F: +case 0x549F: +case 0x569F: +case 0x589F: +case 0x5A9F: +case 0x5C9F: +case 0x5E9F: + +// ADDQ +case 0x509F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x52A7: +case 0x54A7: +case 0x56A7: +case 0x58A7: +case 0x5AA7: +case 0x5CA7: +case 0x5EA7: + +// ADDQ +case 0x50A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x5300: +case 0x5500: +case 0x5700: +case 0x5900: +case 0x5B00: +case 0x5D00: +case 0x5F00: +case 0x5101: +case 0x5301: +case 0x5501: +case 0x5701: +case 0x5901: +case 0x5B01: +case 0x5D01: +case 0x5F01: +case 0x5102: +case 0x5302: +case 0x5502: +case 0x5702: +case 0x5902: +case 0x5B02: +case 0x5D02: +case 0x5F02: +case 0x5103: +case 0x5303: +case 0x5503: +case 0x5703: +case 0x5903: +case 0x5B03: +case 0x5D03: +case 0x5F03: +case 0x5104: +case 0x5304: +case 0x5504: +case 0x5704: +case 0x5904: +case 0x5B04: +case 0x5D04: +case 0x5F04: +case 0x5105: +case 0x5305: +case 0x5505: +case 0x5705: +case 0x5905: +case 0x5B05: +case 0x5D05: +case 0x5F05: +case 0x5106: +case 0x5306: +case 0x5506: +case 0x5706: +case 0x5906: +case 0x5B06: +case 0x5D06: +case 0x5F06: +case 0x5107: +case 0x5307: +case 0x5507: +case 0x5707: +case 0x5907: +case 0x5B07: +case 0x5D07: +case 0x5F07: + +// SUBQ +case 0x5100: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u8)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x5310: +case 0x5510: +case 0x5710: +case 0x5910: +case 0x5B10: +case 0x5D10: +case 0x5F10: +case 0x5111: +case 0x5311: +case 0x5511: +case 0x5711: +case 0x5911: +case 0x5B11: +case 0x5D11: +case 0x5F11: +case 0x5112: +case 0x5312: +case 0x5512: +case 0x5712: +case 0x5912: +case 0x5B12: +case 0x5D12: +case 0x5F12: +case 0x5113: +case 0x5313: +case 0x5513: +case 0x5713: +case 0x5913: +case 0x5B13: +case 0x5D13: +case 0x5F13: +case 0x5114: +case 0x5314: +case 0x5514: +case 0x5714: +case 0x5914: +case 0x5B14: +case 0x5D14: +case 0x5F14: +case 0x5115: +case 0x5315: +case 0x5515: +case 0x5715: +case 0x5915: +case 0x5B15: +case 0x5D15: +case 0x5F15: +case 0x5116: +case 0x5316: +case 0x5516: +case 0x5716: +case 0x5916: +case 0x5B16: +case 0x5D16: +case 0x5F16: +case 0x5117: +case 0x5317: +case 0x5517: +case 0x5717: +case 0x5917: +case 0x5B17: +case 0x5D17: +case 0x5F17: + +// SUBQ +case 0x5110: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5318: +case 0x5518: +case 0x5718: +case 0x5918: +case 0x5B18: +case 0x5D18: +case 0x5F18: +case 0x5119: +case 0x5319: +case 0x5519: +case 0x5719: +case 0x5919: +case 0x5B19: +case 0x5D19: +case 0x5F19: +case 0x511A: +case 0x531A: +case 0x551A: +case 0x571A: +case 0x591A: +case 0x5B1A: +case 0x5D1A: +case 0x5F1A: +case 0x511B: +case 0x531B: +case 0x551B: +case 0x571B: +case 0x591B: +case 0x5B1B: +case 0x5D1B: +case 0x5F1B: +case 0x511C: +case 0x531C: +case 0x551C: +case 0x571C: +case 0x591C: +case 0x5B1C: +case 0x5D1C: +case 0x5F1C: +case 0x511D: +case 0x531D: +case 0x551D: +case 0x571D: +case 0x591D: +case 0x5B1D: +case 0x5D1D: +case 0x5F1D: +case 0x511E: +case 0x531E: +case 0x551E: +case 0x571E: +case 0x591E: +case 0x5B1E: +case 0x5D1E: +case 0x5F1E: + +// SUBQ +case 0x5118: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5320: +case 0x5520: +case 0x5720: +case 0x5920: +case 0x5B20: +case 0x5D20: +case 0x5F20: +case 0x5121: +case 0x5321: +case 0x5521: +case 0x5721: +case 0x5921: +case 0x5B21: +case 0x5D21: +case 0x5F21: +case 0x5122: +case 0x5322: +case 0x5522: +case 0x5722: +case 0x5922: +case 0x5B22: +case 0x5D22: +case 0x5F22: +case 0x5123: +case 0x5323: +case 0x5523: +case 0x5723: +case 0x5923: +case 0x5B23: +case 0x5D23: +case 0x5F23: +case 0x5124: +case 0x5324: +case 0x5524: +case 0x5724: +case 0x5924: +case 0x5B24: +case 0x5D24: +case 0x5F24: +case 0x5125: +case 0x5325: +case 0x5525: +case 0x5725: +case 0x5925: +case 0x5B25: +case 0x5D25: +case 0x5F25: +case 0x5126: +case 0x5326: +case 0x5526: +case 0x5726: +case 0x5926: +case 0x5B26: +case 0x5D26: +case 0x5F26: + +// SUBQ +case 0x5120: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x5328: +case 0x5528: +case 0x5728: +case 0x5928: +case 0x5B28: +case 0x5D28: +case 0x5F28: +case 0x5129: +case 0x5329: +case 0x5529: +case 0x5729: +case 0x5929: +case 0x5B29: +case 0x5D29: +case 0x5F29: +case 0x512A: +case 0x532A: +case 0x552A: +case 0x572A: +case 0x592A: +case 0x5B2A: +case 0x5D2A: +case 0x5F2A: +case 0x512B: +case 0x532B: +case 0x552B: +case 0x572B: +case 0x592B: +case 0x5B2B: +case 0x5D2B: +case 0x5F2B: +case 0x512C: +case 0x532C: +case 0x552C: +case 0x572C: +case 0x592C: +case 0x5B2C: +case 0x5D2C: +case 0x5F2C: +case 0x512D: +case 0x532D: +case 0x552D: +case 0x572D: +case 0x592D: +case 0x5B2D: +case 0x5D2D: +case 0x5F2D: +case 0x512E: +case 0x532E: +case 0x552E: +case 0x572E: +case 0x592E: +case 0x5B2E: +case 0x5D2E: +case 0x5F2E: +case 0x512F: +case 0x532F: +case 0x552F: +case 0x572F: +case 0x592F: +case 0x5B2F: +case 0x5D2F: +case 0x5F2F: + +// SUBQ +case 0x5128: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x5330: +case 0x5530: +case 0x5730: +case 0x5930: +case 0x5B30: +case 0x5D30: +case 0x5F30: +case 0x5131: +case 0x5331: +case 0x5531: +case 0x5731: +case 0x5931: +case 0x5B31: +case 0x5D31: +case 0x5F31: +case 0x5132: +case 0x5332: +case 0x5532: +case 0x5732: +case 0x5932: +case 0x5B32: +case 0x5D32: +case 0x5F32: +case 0x5133: +case 0x5333: +case 0x5533: +case 0x5733: +case 0x5933: +case 0x5B33: +case 0x5D33: +case 0x5F33: +case 0x5134: +case 0x5334: +case 0x5534: +case 0x5734: +case 0x5934: +case 0x5B34: +case 0x5D34: +case 0x5F34: +case 0x5135: +case 0x5335: +case 0x5535: +case 0x5735: +case 0x5935: +case 0x5B35: +case 0x5D35: +case 0x5F35: +case 0x5136: +case 0x5336: +case 0x5536: +case 0x5736: +case 0x5936: +case 0x5B36: +case 0x5D36: +case 0x5F36: +case 0x5137: +case 0x5337: +case 0x5537: +case 0x5737: +case 0x5937: +case 0x5B37: +case 0x5D37: +case 0x5F37: + +// SUBQ +case 0x5130: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x5338: +case 0x5538: +case 0x5738: +case 0x5938: +case 0x5B38: +case 0x5D38: +case 0x5F38: + +// SUBQ +case 0x5138: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x5339: +case 0x5539: +case 0x5739: +case 0x5939: +case 0x5B39: +case 0x5D39: +case 0x5F39: + +// SUBQ +case 0x5139: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x531F: +case 0x551F: +case 0x571F: +case 0x591F: +case 0x5B1F: +case 0x5D1F: +case 0x5F1F: + +// SUBQ +case 0x511F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x5327: +case 0x5527: +case 0x5727: +case 0x5927: +case 0x5B27: +case 0x5D27: +case 0x5F27: + +// SUBQ +case 0x5127: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x5340: +case 0x5540: +case 0x5740: +case 0x5940: +case 0x5B40: +case 0x5D40: +case 0x5F40: +case 0x5141: +case 0x5341: +case 0x5541: +case 0x5741: +case 0x5941: +case 0x5B41: +case 0x5D41: +case 0x5F41: +case 0x5142: +case 0x5342: +case 0x5542: +case 0x5742: +case 0x5942: +case 0x5B42: +case 0x5D42: +case 0x5F42: +case 0x5143: +case 0x5343: +case 0x5543: +case 0x5743: +case 0x5943: +case 0x5B43: +case 0x5D43: +case 0x5F43: +case 0x5144: +case 0x5344: +case 0x5544: +case 0x5744: +case 0x5944: +case 0x5B44: +case 0x5D44: +case 0x5F44: +case 0x5145: +case 0x5345: +case 0x5545: +case 0x5745: +case 0x5945: +case 0x5B45: +case 0x5D45: +case 0x5F45: +case 0x5146: +case 0x5346: +case 0x5546: +case 0x5746: +case 0x5946: +case 0x5B46: +case 0x5D46: +case 0x5F46: +case 0x5147: +case 0x5347: +case 0x5547: +case 0x5747: +case 0x5947: +case 0x5B47: +case 0x5D47: +case 0x5F47: + +// SUBQ +case 0x5140: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u16)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0x5348: +case 0x5548: +case 0x5748: +case 0x5948: +case 0x5B48: +case 0x5D48: +case 0x5F48: +case 0x5149: +case 0x5349: +case 0x5549: +case 0x5749: +case 0x5949: +case 0x5B49: +case 0x5D49: +case 0x5F49: +case 0x514A: +case 0x534A: +case 0x554A: +case 0x574A: +case 0x594A: +case 0x5B4A: +case 0x5D4A: +case 0x5F4A: +case 0x514B: +case 0x534B: +case 0x554B: +case 0x574B: +case 0x594B: +case 0x5B4B: +case 0x5D4B: +case 0x5F4B: +case 0x514C: +case 0x534C: +case 0x554C: +case 0x574C: +case 0x594C: +case 0x5B4C: +case 0x5D4C: +case 0x5F4C: +case 0x514D: +case 0x534D: +case 0x554D: +case 0x574D: +case 0x594D: +case 0x5B4D: +case 0x5D4D: +case 0x5F4D: +case 0x514E: +case 0x534E: +case 0x554E: +case 0x574E: +case 0x594E: +case 0x5B4E: +case 0x5D4E: +case 0x5F4E: +case 0x514F: +case 0x534F: +case 0x554F: +case 0x574F: +case 0x594F: +case 0x5B4F: +case 0x5D4F: +case 0x5F4F: + +// SUBQ +case 0x5148: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->A[(Opcode >> 0) & 7]; + res = dst - src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(8) +case 0x5350: +case 0x5550: +case 0x5750: +case 0x5950: +case 0x5B50: +case 0x5D50: +case 0x5F50: +case 0x5151: +case 0x5351: +case 0x5551: +case 0x5751: +case 0x5951: +case 0x5B51: +case 0x5D51: +case 0x5F51: +case 0x5152: +case 0x5352: +case 0x5552: +case 0x5752: +case 0x5952: +case 0x5B52: +case 0x5D52: +case 0x5F52: +case 0x5153: +case 0x5353: +case 0x5553: +case 0x5753: +case 0x5953: +case 0x5B53: +case 0x5D53: +case 0x5F53: +case 0x5154: +case 0x5354: +case 0x5554: +case 0x5754: +case 0x5954: +case 0x5B54: +case 0x5D54: +case 0x5F54: +case 0x5155: +case 0x5355: +case 0x5555: +case 0x5755: +case 0x5955: +case 0x5B55: +case 0x5D55: +case 0x5F55: +case 0x5156: +case 0x5356: +case 0x5556: +case 0x5756: +case 0x5956: +case 0x5B56: +case 0x5D56: +case 0x5F56: +case 0x5157: +case 0x5357: +case 0x5557: +case 0x5757: +case 0x5957: +case 0x5B57: +case 0x5D57: +case 0x5F57: + +// SUBQ +case 0x5150: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5358: +case 0x5558: +case 0x5758: +case 0x5958: +case 0x5B58: +case 0x5D58: +case 0x5F58: +case 0x5159: +case 0x5359: +case 0x5559: +case 0x5759: +case 0x5959: +case 0x5B59: +case 0x5D59: +case 0x5F59: +case 0x515A: +case 0x535A: +case 0x555A: +case 0x575A: +case 0x595A: +case 0x5B5A: +case 0x5D5A: +case 0x5F5A: +case 0x515B: +case 0x535B: +case 0x555B: +case 0x575B: +case 0x595B: +case 0x5B5B: +case 0x5D5B: +case 0x5F5B: +case 0x515C: +case 0x535C: +case 0x555C: +case 0x575C: +case 0x595C: +case 0x5B5C: +case 0x5D5C: +case 0x5F5C: +case 0x515D: +case 0x535D: +case 0x555D: +case 0x575D: +case 0x595D: +case 0x5B5D: +case 0x5D5D: +case 0x5F5D: +case 0x515E: +case 0x535E: +case 0x555E: +case 0x575E: +case 0x595E: +case 0x5B5E: +case 0x5D5E: +case 0x5F5E: + +// SUBQ +case 0x5158: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5360: +case 0x5560: +case 0x5760: +case 0x5960: +case 0x5B60: +case 0x5D60: +case 0x5F60: +case 0x5161: +case 0x5361: +case 0x5561: +case 0x5761: +case 0x5961: +case 0x5B61: +case 0x5D61: +case 0x5F61: +case 0x5162: +case 0x5362: +case 0x5562: +case 0x5762: +case 0x5962: +case 0x5B62: +case 0x5D62: +case 0x5F62: +case 0x5163: +case 0x5363: +case 0x5563: +case 0x5763: +case 0x5963: +case 0x5B63: +case 0x5D63: +case 0x5F63: +case 0x5164: +case 0x5364: +case 0x5564: +case 0x5764: +case 0x5964: +case 0x5B64: +case 0x5D64: +case 0x5F64: +case 0x5165: +case 0x5365: +case 0x5565: +case 0x5765: +case 0x5965: +case 0x5B65: +case 0x5D65: +case 0x5F65: +case 0x5166: +case 0x5366: +case 0x5566: +case 0x5766: +case 0x5966: +case 0x5B66: +case 0x5D66: +case 0x5F66: + +// SUBQ +case 0x5160: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x5368: +case 0x5568: +case 0x5768: +case 0x5968: +case 0x5B68: +case 0x5D68: +case 0x5F68: +case 0x5169: +case 0x5369: +case 0x5569: +case 0x5769: +case 0x5969: +case 0x5B69: +case 0x5D69: +case 0x5F69: +case 0x516A: +case 0x536A: +case 0x556A: +case 0x576A: +case 0x596A: +case 0x5B6A: +case 0x5D6A: +case 0x5F6A: +case 0x516B: +case 0x536B: +case 0x556B: +case 0x576B: +case 0x596B: +case 0x5B6B: +case 0x5D6B: +case 0x5F6B: +case 0x516C: +case 0x536C: +case 0x556C: +case 0x576C: +case 0x596C: +case 0x5B6C: +case 0x5D6C: +case 0x5F6C: +case 0x516D: +case 0x536D: +case 0x556D: +case 0x576D: +case 0x596D: +case 0x5B6D: +case 0x5D6D: +case 0x5F6D: +case 0x516E: +case 0x536E: +case 0x556E: +case 0x576E: +case 0x596E: +case 0x5B6E: +case 0x5D6E: +case 0x5F6E: +case 0x516F: +case 0x536F: +case 0x556F: +case 0x576F: +case 0x596F: +case 0x5B6F: +case 0x5D6F: +case 0x5F6F: + +// SUBQ +case 0x5168: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x5370: +case 0x5570: +case 0x5770: +case 0x5970: +case 0x5B70: +case 0x5D70: +case 0x5F70: +case 0x5171: +case 0x5371: +case 0x5571: +case 0x5771: +case 0x5971: +case 0x5B71: +case 0x5D71: +case 0x5F71: +case 0x5172: +case 0x5372: +case 0x5572: +case 0x5772: +case 0x5972: +case 0x5B72: +case 0x5D72: +case 0x5F72: +case 0x5173: +case 0x5373: +case 0x5573: +case 0x5773: +case 0x5973: +case 0x5B73: +case 0x5D73: +case 0x5F73: +case 0x5174: +case 0x5374: +case 0x5574: +case 0x5774: +case 0x5974: +case 0x5B74: +case 0x5D74: +case 0x5F74: +case 0x5175: +case 0x5375: +case 0x5575: +case 0x5775: +case 0x5975: +case 0x5B75: +case 0x5D75: +case 0x5F75: +case 0x5176: +case 0x5376: +case 0x5576: +case 0x5776: +case 0x5976: +case 0x5B76: +case 0x5D76: +case 0x5F76: +case 0x5177: +case 0x5377: +case 0x5577: +case 0x5777: +case 0x5977: +case 0x5B77: +case 0x5D77: +case 0x5F77: + +// SUBQ +case 0x5170: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x5378: +case 0x5578: +case 0x5778: +case 0x5978: +case 0x5B78: +case 0x5D78: +case 0x5F78: + +// SUBQ +case 0x5178: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x5379: +case 0x5579: +case 0x5779: +case 0x5979: +case 0x5B79: +case 0x5D79: +case 0x5F79: + +// SUBQ +case 0x5179: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x535F: +case 0x555F: +case 0x575F: +case 0x595F: +case 0x5B5F: +case 0x5D5F: +case 0x5F5F: + +// SUBQ +case 0x515F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x5367: +case 0x5567: +case 0x5767: +case 0x5967: +case 0x5B67: +case 0x5D67: +case 0x5F67: + +// SUBQ +case 0x5167: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x5380: +case 0x5580: +case 0x5780: +case 0x5980: +case 0x5B80: +case 0x5D80: +case 0x5F80: +case 0x5181: +case 0x5381: +case 0x5581: +case 0x5781: +case 0x5981: +case 0x5B81: +case 0x5D81: +case 0x5F81: +case 0x5182: +case 0x5382: +case 0x5582: +case 0x5782: +case 0x5982: +case 0x5B82: +case 0x5D82: +case 0x5F82: +case 0x5183: +case 0x5383: +case 0x5583: +case 0x5783: +case 0x5983: +case 0x5B83: +case 0x5D83: +case 0x5F83: +case 0x5184: +case 0x5384: +case 0x5584: +case 0x5784: +case 0x5984: +case 0x5B84: +case 0x5D84: +case 0x5F84: +case 0x5185: +case 0x5385: +case 0x5585: +case 0x5785: +case 0x5985: +case 0x5B85: +case 0x5D85: +case 0x5F85: +case 0x5186: +case 0x5386: +case 0x5586: +case 0x5786: +case 0x5986: +case 0x5B86: +case 0x5D86: +case 0x5F86: +case 0x5187: +case 0x5387: +case 0x5587: +case 0x5787: +case 0x5987: +case 0x5B87: +case 0x5D87: +case 0x5F87: + +// SUBQ +case 0x5180: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->D[(Opcode >> 0) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0x5388: +case 0x5588: +case 0x5788: +case 0x5988: +case 0x5B88: +case 0x5D88: +case 0x5F88: +case 0x5189: +case 0x5389: +case 0x5589: +case 0x5789: +case 0x5989: +case 0x5B89: +case 0x5D89: +case 0x5F89: +case 0x518A: +case 0x538A: +case 0x558A: +case 0x578A: +case 0x598A: +case 0x5B8A: +case 0x5D8A: +case 0x5F8A: +case 0x518B: +case 0x538B: +case 0x558B: +case 0x578B: +case 0x598B: +case 0x5B8B: +case 0x5D8B: +case 0x5F8B: +case 0x518C: +case 0x538C: +case 0x558C: +case 0x578C: +case 0x598C: +case 0x5B8C: +case 0x5D8C: +case 0x5F8C: +case 0x518D: +case 0x538D: +case 0x558D: +case 0x578D: +case 0x598D: +case 0x5B8D: +case 0x5D8D: +case 0x5F8D: +case 0x518E: +case 0x538E: +case 0x558E: +case 0x578E: +case 0x598E: +case 0x5B8E: +case 0x5D8E: +case 0x5F8E: +case 0x518F: +case 0x538F: +case 0x558F: +case 0x578F: +case 0x598F: +case 0x5B8F: +case 0x5D8F: +case 0x5F8F: + +// SUBQ +case 0x5188: +{ + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + dst = (u32)CPU->A[(Opcode >> 0) & 7]; + res = dst - src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(8) +case 0x5390: +case 0x5590: +case 0x5790: +case 0x5990: +case 0x5B90: +case 0x5D90: +case 0x5F90: +case 0x5191: +case 0x5391: +case 0x5591: +case 0x5791: +case 0x5991: +case 0x5B91: +case 0x5D91: +case 0x5F91: +case 0x5192: +case 0x5392: +case 0x5592: +case 0x5792: +case 0x5992: +case 0x5B92: +case 0x5D92: +case 0x5F92: +case 0x5193: +case 0x5393: +case 0x5593: +case 0x5793: +case 0x5993: +case 0x5B93: +case 0x5D93: +case 0x5F93: +case 0x5194: +case 0x5394: +case 0x5594: +case 0x5794: +case 0x5994: +case 0x5B94: +case 0x5D94: +case 0x5F94: +case 0x5195: +case 0x5395: +case 0x5595: +case 0x5795: +case 0x5995: +case 0x5B95: +case 0x5D95: +case 0x5F95: +case 0x5196: +case 0x5396: +case 0x5596: +case 0x5796: +case 0x5996: +case 0x5B96: +case 0x5D96: +case 0x5F96: +case 0x5197: +case 0x5397: +case 0x5597: +case 0x5797: +case 0x5997: +case 0x5B97: +case 0x5D97: +case 0x5F97: + +// SUBQ +case 0x5190: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x5398: +case 0x5598: +case 0x5798: +case 0x5998: +case 0x5B98: +case 0x5D98: +case 0x5F98: +case 0x5199: +case 0x5399: +case 0x5599: +case 0x5799: +case 0x5999: +case 0x5B99: +case 0x5D99: +case 0x5F99: +case 0x519A: +case 0x539A: +case 0x559A: +case 0x579A: +case 0x599A: +case 0x5B9A: +case 0x5D9A: +case 0x5F9A: +case 0x519B: +case 0x539B: +case 0x559B: +case 0x579B: +case 0x599B: +case 0x5B9B: +case 0x5D9B: +case 0x5F9B: +case 0x519C: +case 0x539C: +case 0x559C: +case 0x579C: +case 0x599C: +case 0x5B9C: +case 0x5D9C: +case 0x5F9C: +case 0x519D: +case 0x539D: +case 0x559D: +case 0x579D: +case 0x599D: +case 0x5B9D: +case 0x5D9D: +case 0x5F9D: +case 0x519E: +case 0x539E: +case 0x559E: +case 0x579E: +case 0x599E: +case 0x5B9E: +case 0x5D9E: +case 0x5F9E: + +// SUBQ +case 0x5198: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x53A0: +case 0x55A0: +case 0x57A0: +case 0x59A0: +case 0x5BA0: +case 0x5DA0: +case 0x5FA0: +case 0x51A1: +case 0x53A1: +case 0x55A1: +case 0x57A1: +case 0x59A1: +case 0x5BA1: +case 0x5DA1: +case 0x5FA1: +case 0x51A2: +case 0x53A2: +case 0x55A2: +case 0x57A2: +case 0x59A2: +case 0x5BA2: +case 0x5DA2: +case 0x5FA2: +case 0x51A3: +case 0x53A3: +case 0x55A3: +case 0x57A3: +case 0x59A3: +case 0x5BA3: +case 0x5DA3: +case 0x5FA3: +case 0x51A4: +case 0x53A4: +case 0x55A4: +case 0x57A4: +case 0x59A4: +case 0x5BA4: +case 0x5DA4: +case 0x5FA4: +case 0x51A5: +case 0x53A5: +case 0x55A5: +case 0x57A5: +case 0x59A5: +case 0x5BA5: +case 0x5DA5: +case 0x5FA5: +case 0x51A6: +case 0x53A6: +case 0x55A6: +case 0x57A6: +case 0x59A6: +case 0x5BA6: +case 0x5DA6: +case 0x5FA6: + +// SUBQ +case 0x51A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x53A8: +case 0x55A8: +case 0x57A8: +case 0x59A8: +case 0x5BA8: +case 0x5DA8: +case 0x5FA8: +case 0x51A9: +case 0x53A9: +case 0x55A9: +case 0x57A9: +case 0x59A9: +case 0x5BA9: +case 0x5DA9: +case 0x5FA9: +case 0x51AA: +case 0x53AA: +case 0x55AA: +case 0x57AA: +case 0x59AA: +case 0x5BAA: +case 0x5DAA: +case 0x5FAA: +case 0x51AB: +case 0x53AB: +case 0x55AB: +case 0x57AB: +case 0x59AB: +case 0x5BAB: +case 0x5DAB: +case 0x5FAB: +case 0x51AC: +case 0x53AC: +case 0x55AC: +case 0x57AC: +case 0x59AC: +case 0x5BAC: +case 0x5DAC: +case 0x5FAC: +case 0x51AD: +case 0x53AD: +case 0x55AD: +case 0x57AD: +case 0x59AD: +case 0x5BAD: +case 0x5DAD: +case 0x5FAD: +case 0x51AE: +case 0x53AE: +case 0x55AE: +case 0x57AE: +case 0x59AE: +case 0x5BAE: +case 0x5DAE: +case 0x5FAE: +case 0x51AF: +case 0x53AF: +case 0x55AF: +case 0x57AF: +case 0x59AF: +case 0x5BAF: +case 0x5DAF: +case 0x5FAF: + +// SUBQ +case 0x51A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x53B0: +case 0x55B0: +case 0x57B0: +case 0x59B0: +case 0x5BB0: +case 0x5DB0: +case 0x5FB0: +case 0x51B1: +case 0x53B1: +case 0x55B1: +case 0x57B1: +case 0x59B1: +case 0x5BB1: +case 0x5DB1: +case 0x5FB1: +case 0x51B2: +case 0x53B2: +case 0x55B2: +case 0x57B2: +case 0x59B2: +case 0x5BB2: +case 0x5DB2: +case 0x5FB2: +case 0x51B3: +case 0x53B3: +case 0x55B3: +case 0x57B3: +case 0x59B3: +case 0x5BB3: +case 0x5DB3: +case 0x5FB3: +case 0x51B4: +case 0x53B4: +case 0x55B4: +case 0x57B4: +case 0x59B4: +case 0x5BB4: +case 0x5DB4: +case 0x5FB4: +case 0x51B5: +case 0x53B5: +case 0x55B5: +case 0x57B5: +case 0x59B5: +case 0x5BB5: +case 0x5DB5: +case 0x5FB5: +case 0x51B6: +case 0x53B6: +case 0x55B6: +case 0x57B6: +case 0x59B6: +case 0x5BB6: +case 0x5DB6: +case 0x5FB6: +case 0x51B7: +case 0x53B7: +case 0x55B7: +case 0x57B7: +case 0x59B7: +case 0x5BB7: +case 0x5DB7: +case 0x5FB7: + +// SUBQ +case 0x51B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x53B8: +case 0x55B8: +case 0x57B8: +case 0x59B8: +case 0x5BB8: +case 0x5DB8: +case 0x5FB8: + +// SUBQ +case 0x51B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x53B9: +case 0x55B9: +case 0x57B9: +case 0x59B9: +case 0x5BB9: +case 0x5DB9: +case 0x5FB9: + +// SUBQ +case 0x51B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x539F: +case 0x559F: +case 0x579F: +case 0x599F: +case 0x5B9F: +case 0x5D9F: +case 0x5F9F: + +// SUBQ +case 0x519F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x53A7: +case 0x55A7: +case 0x57A7: +case 0x59A7: +case 0x5BA7: +case 0x5DA7: +case 0x5FA7: + +// SUBQ +case 0x51A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (((Opcode >> 9) - 1) & 7) + 1; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) diff --git a/yabause/src/c68k/c68k_op6.inc b/yabause/src/c68k/c68k_op6.inc new file mode 100644 index 0000000000..d5b94b5317 --- /dev/null +++ b/yabause/src/c68k/c68k_op6.inc @@ -0,0 +1,4454 @@ +case 0x6202: +case 0x6203: +case 0x6204: +case 0x6205: +case 0x6206: +case 0x6207: +case 0x6208: +case 0x6209: +case 0x620A: +case 0x620B: +case 0x620C: +case 0x620D: +case 0x620E: +case 0x620F: +case 0x6210: +case 0x6211: +case 0x6212: +case 0x6213: +case 0x6214: +case 0x6215: +case 0x6216: +case 0x6217: +case 0x6218: +case 0x6219: +case 0x621A: +case 0x621B: +case 0x621C: +case 0x621D: +case 0x621E: +case 0x621F: +case 0x6220: +case 0x6221: +case 0x6222: +case 0x6223: +case 0x6224: +case 0x6225: +case 0x6226: +case 0x6227: +case 0x6228: +case 0x6229: +case 0x622A: +case 0x622B: +case 0x622C: +case 0x622D: +case 0x622E: +case 0x622F: +case 0x6230: +case 0x6231: +case 0x6232: +case 0x6233: +case 0x6234: +case 0x6235: +case 0x6236: +case 0x6237: +case 0x6238: +case 0x6239: +case 0x623A: +case 0x623B: +case 0x623C: +case 0x623D: +case 0x623E: +case 0x623F: +case 0x6240: +case 0x6241: +case 0x6242: +case 0x6243: +case 0x6244: +case 0x6245: +case 0x6246: +case 0x6247: +case 0x6248: +case 0x6249: +case 0x624A: +case 0x624B: +case 0x624C: +case 0x624D: +case 0x624E: +case 0x624F: +case 0x6250: +case 0x6251: +case 0x6252: +case 0x6253: +case 0x6254: +case 0x6255: +case 0x6256: +case 0x6257: +case 0x6258: +case 0x6259: +case 0x625A: +case 0x625B: +case 0x625C: +case 0x625D: +case 0x625E: +case 0x625F: +case 0x6260: +case 0x6261: +case 0x6262: +case 0x6263: +case 0x6264: +case 0x6265: +case 0x6266: +case 0x6267: +case 0x6268: +case 0x6269: +case 0x626A: +case 0x626B: +case 0x626C: +case 0x626D: +case 0x626E: +case 0x626F: +case 0x6270: +case 0x6271: +case 0x6272: +case 0x6273: +case 0x6274: +case 0x6275: +case 0x6276: +case 0x6277: +case 0x6278: +case 0x6279: +case 0x627A: +case 0x627B: +case 0x627C: +case 0x627D: +case 0x627E: +case 0x627F: +case 0x6280: +case 0x6281: +case 0x6282: +case 0x6283: +case 0x6284: +case 0x6285: +case 0x6286: +case 0x6287: +case 0x6288: +case 0x6289: +case 0x628A: +case 0x628B: +case 0x628C: +case 0x628D: +case 0x628E: +case 0x628F: +case 0x6290: +case 0x6291: +case 0x6292: +case 0x6293: +case 0x6294: +case 0x6295: +case 0x6296: +case 0x6297: +case 0x6298: +case 0x6299: +case 0x629A: +case 0x629B: +case 0x629C: +case 0x629D: +case 0x629E: +case 0x629F: +case 0x62A0: +case 0x62A1: +case 0x62A2: +case 0x62A3: +case 0x62A4: +case 0x62A5: +case 0x62A6: +case 0x62A7: +case 0x62A8: +case 0x62A9: +case 0x62AA: +case 0x62AB: +case 0x62AC: +case 0x62AD: +case 0x62AE: +case 0x62AF: +case 0x62B0: +case 0x62B1: +case 0x62B2: +case 0x62B3: +case 0x62B4: +case 0x62B5: +case 0x62B6: +case 0x62B7: +case 0x62B8: +case 0x62B9: +case 0x62BA: +case 0x62BB: +case 0x62BC: +case 0x62BD: +case 0x62BE: +case 0x62BF: +case 0x62C0: +case 0x62C1: +case 0x62C2: +case 0x62C3: +case 0x62C4: +case 0x62C5: +case 0x62C6: +case 0x62C7: +case 0x62C8: +case 0x62C9: +case 0x62CA: +case 0x62CB: +case 0x62CC: +case 0x62CD: +case 0x62CE: +case 0x62CF: +case 0x62D0: +case 0x62D1: +case 0x62D2: +case 0x62D3: +case 0x62D4: +case 0x62D5: +case 0x62D6: +case 0x62D7: +case 0x62D8: +case 0x62D9: +case 0x62DA: +case 0x62DB: +case 0x62DC: +case 0x62DD: +case 0x62DE: +case 0x62DF: +case 0x62E0: +case 0x62E1: +case 0x62E2: +case 0x62E3: +case 0x62E4: +case 0x62E5: +case 0x62E6: +case 0x62E7: +case 0x62E8: +case 0x62E9: +case 0x62EA: +case 0x62EB: +case 0x62EC: +case 0x62ED: +case 0x62EE: +case 0x62EF: +case 0x62F0: +case 0x62F1: +case 0x62F2: +case 0x62F3: +case 0x62F4: +case 0x62F5: +case 0x62F6: +case 0x62F7: +case 0x62F8: +case 0x62F9: +case 0x62FA: +case 0x62FB: +case 0x62FC: +case 0x62FD: +case 0x62FE: +case 0x62FF: + +// BCC +case 0x6201: +{ + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6302: +case 0x6303: +case 0x6304: +case 0x6305: +case 0x6306: +case 0x6307: +case 0x6308: +case 0x6309: +case 0x630A: +case 0x630B: +case 0x630C: +case 0x630D: +case 0x630E: +case 0x630F: +case 0x6310: +case 0x6311: +case 0x6312: +case 0x6313: +case 0x6314: +case 0x6315: +case 0x6316: +case 0x6317: +case 0x6318: +case 0x6319: +case 0x631A: +case 0x631B: +case 0x631C: +case 0x631D: +case 0x631E: +case 0x631F: +case 0x6320: +case 0x6321: +case 0x6322: +case 0x6323: +case 0x6324: +case 0x6325: +case 0x6326: +case 0x6327: +case 0x6328: +case 0x6329: +case 0x632A: +case 0x632B: +case 0x632C: +case 0x632D: +case 0x632E: +case 0x632F: +case 0x6330: +case 0x6331: +case 0x6332: +case 0x6333: +case 0x6334: +case 0x6335: +case 0x6336: +case 0x6337: +case 0x6338: +case 0x6339: +case 0x633A: +case 0x633B: +case 0x633C: +case 0x633D: +case 0x633E: +case 0x633F: +case 0x6340: +case 0x6341: +case 0x6342: +case 0x6343: +case 0x6344: +case 0x6345: +case 0x6346: +case 0x6347: +case 0x6348: +case 0x6349: +case 0x634A: +case 0x634B: +case 0x634C: +case 0x634D: +case 0x634E: +case 0x634F: +case 0x6350: +case 0x6351: +case 0x6352: +case 0x6353: +case 0x6354: +case 0x6355: +case 0x6356: +case 0x6357: +case 0x6358: +case 0x6359: +case 0x635A: +case 0x635B: +case 0x635C: +case 0x635D: +case 0x635E: +case 0x635F: +case 0x6360: +case 0x6361: +case 0x6362: +case 0x6363: +case 0x6364: +case 0x6365: +case 0x6366: +case 0x6367: +case 0x6368: +case 0x6369: +case 0x636A: +case 0x636B: +case 0x636C: +case 0x636D: +case 0x636E: +case 0x636F: +case 0x6370: +case 0x6371: +case 0x6372: +case 0x6373: +case 0x6374: +case 0x6375: +case 0x6376: +case 0x6377: +case 0x6378: +case 0x6379: +case 0x637A: +case 0x637B: +case 0x637C: +case 0x637D: +case 0x637E: +case 0x637F: +case 0x6380: +case 0x6381: +case 0x6382: +case 0x6383: +case 0x6384: +case 0x6385: +case 0x6386: +case 0x6387: +case 0x6388: +case 0x6389: +case 0x638A: +case 0x638B: +case 0x638C: +case 0x638D: +case 0x638E: +case 0x638F: +case 0x6390: +case 0x6391: +case 0x6392: +case 0x6393: +case 0x6394: +case 0x6395: +case 0x6396: +case 0x6397: +case 0x6398: +case 0x6399: +case 0x639A: +case 0x639B: +case 0x639C: +case 0x639D: +case 0x639E: +case 0x639F: +case 0x63A0: +case 0x63A1: +case 0x63A2: +case 0x63A3: +case 0x63A4: +case 0x63A5: +case 0x63A6: +case 0x63A7: +case 0x63A8: +case 0x63A9: +case 0x63AA: +case 0x63AB: +case 0x63AC: +case 0x63AD: +case 0x63AE: +case 0x63AF: +case 0x63B0: +case 0x63B1: +case 0x63B2: +case 0x63B3: +case 0x63B4: +case 0x63B5: +case 0x63B6: +case 0x63B7: +case 0x63B8: +case 0x63B9: +case 0x63BA: +case 0x63BB: +case 0x63BC: +case 0x63BD: +case 0x63BE: +case 0x63BF: +case 0x63C0: +case 0x63C1: +case 0x63C2: +case 0x63C3: +case 0x63C4: +case 0x63C5: +case 0x63C6: +case 0x63C7: +case 0x63C8: +case 0x63C9: +case 0x63CA: +case 0x63CB: +case 0x63CC: +case 0x63CD: +case 0x63CE: +case 0x63CF: +case 0x63D0: +case 0x63D1: +case 0x63D2: +case 0x63D3: +case 0x63D4: +case 0x63D5: +case 0x63D6: +case 0x63D7: +case 0x63D8: +case 0x63D9: +case 0x63DA: +case 0x63DB: +case 0x63DC: +case 0x63DD: +case 0x63DE: +case 0x63DF: +case 0x63E0: +case 0x63E1: +case 0x63E2: +case 0x63E3: +case 0x63E4: +case 0x63E5: +case 0x63E6: +case 0x63E7: +case 0x63E8: +case 0x63E9: +case 0x63EA: +case 0x63EB: +case 0x63EC: +case 0x63ED: +case 0x63EE: +case 0x63EF: +case 0x63F0: +case 0x63F1: +case 0x63F2: +case 0x63F3: +case 0x63F4: +case 0x63F5: +case 0x63F6: +case 0x63F7: +case 0x63F8: +case 0x63F9: +case 0x63FA: +case 0x63FB: +case 0x63FC: +case 0x63FD: +case 0x63FE: +case 0x63FF: + +// BCC +case 0x6301: +{ + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6402: +case 0x6403: +case 0x6404: +case 0x6405: +case 0x6406: +case 0x6407: +case 0x6408: +case 0x6409: +case 0x640A: +case 0x640B: +case 0x640C: +case 0x640D: +case 0x640E: +case 0x640F: +case 0x6410: +case 0x6411: +case 0x6412: +case 0x6413: +case 0x6414: +case 0x6415: +case 0x6416: +case 0x6417: +case 0x6418: +case 0x6419: +case 0x641A: +case 0x641B: +case 0x641C: +case 0x641D: +case 0x641E: +case 0x641F: +case 0x6420: +case 0x6421: +case 0x6422: +case 0x6423: +case 0x6424: +case 0x6425: +case 0x6426: +case 0x6427: +case 0x6428: +case 0x6429: +case 0x642A: +case 0x642B: +case 0x642C: +case 0x642D: +case 0x642E: +case 0x642F: +case 0x6430: +case 0x6431: +case 0x6432: +case 0x6433: +case 0x6434: +case 0x6435: +case 0x6436: +case 0x6437: +case 0x6438: +case 0x6439: +case 0x643A: +case 0x643B: +case 0x643C: +case 0x643D: +case 0x643E: +case 0x643F: +case 0x6440: +case 0x6441: +case 0x6442: +case 0x6443: +case 0x6444: +case 0x6445: +case 0x6446: +case 0x6447: +case 0x6448: +case 0x6449: +case 0x644A: +case 0x644B: +case 0x644C: +case 0x644D: +case 0x644E: +case 0x644F: +case 0x6450: +case 0x6451: +case 0x6452: +case 0x6453: +case 0x6454: +case 0x6455: +case 0x6456: +case 0x6457: +case 0x6458: +case 0x6459: +case 0x645A: +case 0x645B: +case 0x645C: +case 0x645D: +case 0x645E: +case 0x645F: +case 0x6460: +case 0x6461: +case 0x6462: +case 0x6463: +case 0x6464: +case 0x6465: +case 0x6466: +case 0x6467: +case 0x6468: +case 0x6469: +case 0x646A: +case 0x646B: +case 0x646C: +case 0x646D: +case 0x646E: +case 0x646F: +case 0x6470: +case 0x6471: +case 0x6472: +case 0x6473: +case 0x6474: +case 0x6475: +case 0x6476: +case 0x6477: +case 0x6478: +case 0x6479: +case 0x647A: +case 0x647B: +case 0x647C: +case 0x647D: +case 0x647E: +case 0x647F: +case 0x6480: +case 0x6481: +case 0x6482: +case 0x6483: +case 0x6484: +case 0x6485: +case 0x6486: +case 0x6487: +case 0x6488: +case 0x6489: +case 0x648A: +case 0x648B: +case 0x648C: +case 0x648D: +case 0x648E: +case 0x648F: +case 0x6490: +case 0x6491: +case 0x6492: +case 0x6493: +case 0x6494: +case 0x6495: +case 0x6496: +case 0x6497: +case 0x6498: +case 0x6499: +case 0x649A: +case 0x649B: +case 0x649C: +case 0x649D: +case 0x649E: +case 0x649F: +case 0x64A0: +case 0x64A1: +case 0x64A2: +case 0x64A3: +case 0x64A4: +case 0x64A5: +case 0x64A6: +case 0x64A7: +case 0x64A8: +case 0x64A9: +case 0x64AA: +case 0x64AB: +case 0x64AC: +case 0x64AD: +case 0x64AE: +case 0x64AF: +case 0x64B0: +case 0x64B1: +case 0x64B2: +case 0x64B3: +case 0x64B4: +case 0x64B5: +case 0x64B6: +case 0x64B7: +case 0x64B8: +case 0x64B9: +case 0x64BA: +case 0x64BB: +case 0x64BC: +case 0x64BD: +case 0x64BE: +case 0x64BF: +case 0x64C0: +case 0x64C1: +case 0x64C2: +case 0x64C3: +case 0x64C4: +case 0x64C5: +case 0x64C6: +case 0x64C7: +case 0x64C8: +case 0x64C9: +case 0x64CA: +case 0x64CB: +case 0x64CC: +case 0x64CD: +case 0x64CE: +case 0x64CF: +case 0x64D0: +case 0x64D1: +case 0x64D2: +case 0x64D3: +case 0x64D4: +case 0x64D5: +case 0x64D6: +case 0x64D7: +case 0x64D8: +case 0x64D9: +case 0x64DA: +case 0x64DB: +case 0x64DC: +case 0x64DD: +case 0x64DE: +case 0x64DF: +case 0x64E0: +case 0x64E1: +case 0x64E2: +case 0x64E3: +case 0x64E4: +case 0x64E5: +case 0x64E6: +case 0x64E7: +case 0x64E8: +case 0x64E9: +case 0x64EA: +case 0x64EB: +case 0x64EC: +case 0x64ED: +case 0x64EE: +case 0x64EF: +case 0x64F0: +case 0x64F1: +case 0x64F2: +case 0x64F3: +case 0x64F4: +case 0x64F5: +case 0x64F6: +case 0x64F7: +case 0x64F8: +case 0x64F9: +case 0x64FA: +case 0x64FB: +case 0x64FC: +case 0x64FD: +case 0x64FE: +case 0x64FF: + +// BCC +case 0x6401: +{ + if (!(CPU->flag_C & 0x100)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6502: +case 0x6503: +case 0x6504: +case 0x6505: +case 0x6506: +case 0x6507: +case 0x6508: +case 0x6509: +case 0x650A: +case 0x650B: +case 0x650C: +case 0x650D: +case 0x650E: +case 0x650F: +case 0x6510: +case 0x6511: +case 0x6512: +case 0x6513: +case 0x6514: +case 0x6515: +case 0x6516: +case 0x6517: +case 0x6518: +case 0x6519: +case 0x651A: +case 0x651B: +case 0x651C: +case 0x651D: +case 0x651E: +case 0x651F: +case 0x6520: +case 0x6521: +case 0x6522: +case 0x6523: +case 0x6524: +case 0x6525: +case 0x6526: +case 0x6527: +case 0x6528: +case 0x6529: +case 0x652A: +case 0x652B: +case 0x652C: +case 0x652D: +case 0x652E: +case 0x652F: +case 0x6530: +case 0x6531: +case 0x6532: +case 0x6533: +case 0x6534: +case 0x6535: +case 0x6536: +case 0x6537: +case 0x6538: +case 0x6539: +case 0x653A: +case 0x653B: +case 0x653C: +case 0x653D: +case 0x653E: +case 0x653F: +case 0x6540: +case 0x6541: +case 0x6542: +case 0x6543: +case 0x6544: +case 0x6545: +case 0x6546: +case 0x6547: +case 0x6548: +case 0x6549: +case 0x654A: +case 0x654B: +case 0x654C: +case 0x654D: +case 0x654E: +case 0x654F: +case 0x6550: +case 0x6551: +case 0x6552: +case 0x6553: +case 0x6554: +case 0x6555: +case 0x6556: +case 0x6557: +case 0x6558: +case 0x6559: +case 0x655A: +case 0x655B: +case 0x655C: +case 0x655D: +case 0x655E: +case 0x655F: +case 0x6560: +case 0x6561: +case 0x6562: +case 0x6563: +case 0x6564: +case 0x6565: +case 0x6566: +case 0x6567: +case 0x6568: +case 0x6569: +case 0x656A: +case 0x656B: +case 0x656C: +case 0x656D: +case 0x656E: +case 0x656F: +case 0x6570: +case 0x6571: +case 0x6572: +case 0x6573: +case 0x6574: +case 0x6575: +case 0x6576: +case 0x6577: +case 0x6578: +case 0x6579: +case 0x657A: +case 0x657B: +case 0x657C: +case 0x657D: +case 0x657E: +case 0x657F: +case 0x6580: +case 0x6581: +case 0x6582: +case 0x6583: +case 0x6584: +case 0x6585: +case 0x6586: +case 0x6587: +case 0x6588: +case 0x6589: +case 0x658A: +case 0x658B: +case 0x658C: +case 0x658D: +case 0x658E: +case 0x658F: +case 0x6590: +case 0x6591: +case 0x6592: +case 0x6593: +case 0x6594: +case 0x6595: +case 0x6596: +case 0x6597: +case 0x6598: +case 0x6599: +case 0x659A: +case 0x659B: +case 0x659C: +case 0x659D: +case 0x659E: +case 0x659F: +case 0x65A0: +case 0x65A1: +case 0x65A2: +case 0x65A3: +case 0x65A4: +case 0x65A5: +case 0x65A6: +case 0x65A7: +case 0x65A8: +case 0x65A9: +case 0x65AA: +case 0x65AB: +case 0x65AC: +case 0x65AD: +case 0x65AE: +case 0x65AF: +case 0x65B0: +case 0x65B1: +case 0x65B2: +case 0x65B3: +case 0x65B4: +case 0x65B5: +case 0x65B6: +case 0x65B7: +case 0x65B8: +case 0x65B9: +case 0x65BA: +case 0x65BB: +case 0x65BC: +case 0x65BD: +case 0x65BE: +case 0x65BF: +case 0x65C0: +case 0x65C1: +case 0x65C2: +case 0x65C3: +case 0x65C4: +case 0x65C5: +case 0x65C6: +case 0x65C7: +case 0x65C8: +case 0x65C9: +case 0x65CA: +case 0x65CB: +case 0x65CC: +case 0x65CD: +case 0x65CE: +case 0x65CF: +case 0x65D0: +case 0x65D1: +case 0x65D2: +case 0x65D3: +case 0x65D4: +case 0x65D5: +case 0x65D6: +case 0x65D7: +case 0x65D8: +case 0x65D9: +case 0x65DA: +case 0x65DB: +case 0x65DC: +case 0x65DD: +case 0x65DE: +case 0x65DF: +case 0x65E0: +case 0x65E1: +case 0x65E2: +case 0x65E3: +case 0x65E4: +case 0x65E5: +case 0x65E6: +case 0x65E7: +case 0x65E8: +case 0x65E9: +case 0x65EA: +case 0x65EB: +case 0x65EC: +case 0x65ED: +case 0x65EE: +case 0x65EF: +case 0x65F0: +case 0x65F1: +case 0x65F2: +case 0x65F3: +case 0x65F4: +case 0x65F5: +case 0x65F6: +case 0x65F7: +case 0x65F8: +case 0x65F9: +case 0x65FA: +case 0x65FB: +case 0x65FC: +case 0x65FD: +case 0x65FE: +case 0x65FF: + +// BCC +case 0x6501: +{ + if (CPU->flag_C & 0x100) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6602: +case 0x6603: +case 0x6604: +case 0x6605: +case 0x6606: +case 0x6607: +case 0x6608: +case 0x6609: +case 0x660A: +case 0x660B: +case 0x660C: +case 0x660D: +case 0x660E: +case 0x660F: +case 0x6610: +case 0x6611: +case 0x6612: +case 0x6613: +case 0x6614: +case 0x6615: +case 0x6616: +case 0x6617: +case 0x6618: +case 0x6619: +case 0x661A: +case 0x661B: +case 0x661C: +case 0x661D: +case 0x661E: +case 0x661F: +case 0x6620: +case 0x6621: +case 0x6622: +case 0x6623: +case 0x6624: +case 0x6625: +case 0x6626: +case 0x6627: +case 0x6628: +case 0x6629: +case 0x662A: +case 0x662B: +case 0x662C: +case 0x662D: +case 0x662E: +case 0x662F: +case 0x6630: +case 0x6631: +case 0x6632: +case 0x6633: +case 0x6634: +case 0x6635: +case 0x6636: +case 0x6637: +case 0x6638: +case 0x6639: +case 0x663A: +case 0x663B: +case 0x663C: +case 0x663D: +case 0x663E: +case 0x663F: +case 0x6640: +case 0x6641: +case 0x6642: +case 0x6643: +case 0x6644: +case 0x6645: +case 0x6646: +case 0x6647: +case 0x6648: +case 0x6649: +case 0x664A: +case 0x664B: +case 0x664C: +case 0x664D: +case 0x664E: +case 0x664F: +case 0x6650: +case 0x6651: +case 0x6652: +case 0x6653: +case 0x6654: +case 0x6655: +case 0x6656: +case 0x6657: +case 0x6658: +case 0x6659: +case 0x665A: +case 0x665B: +case 0x665C: +case 0x665D: +case 0x665E: +case 0x665F: +case 0x6660: +case 0x6661: +case 0x6662: +case 0x6663: +case 0x6664: +case 0x6665: +case 0x6666: +case 0x6667: +case 0x6668: +case 0x6669: +case 0x666A: +case 0x666B: +case 0x666C: +case 0x666D: +case 0x666E: +case 0x666F: +case 0x6670: +case 0x6671: +case 0x6672: +case 0x6673: +case 0x6674: +case 0x6675: +case 0x6676: +case 0x6677: +case 0x6678: +case 0x6679: +case 0x667A: +case 0x667B: +case 0x667C: +case 0x667D: +case 0x667E: +case 0x667F: +case 0x6680: +case 0x6681: +case 0x6682: +case 0x6683: +case 0x6684: +case 0x6685: +case 0x6686: +case 0x6687: +case 0x6688: +case 0x6689: +case 0x668A: +case 0x668B: +case 0x668C: +case 0x668D: +case 0x668E: +case 0x668F: +case 0x6690: +case 0x6691: +case 0x6692: +case 0x6693: +case 0x6694: +case 0x6695: +case 0x6696: +case 0x6697: +case 0x6698: +case 0x6699: +case 0x669A: +case 0x669B: +case 0x669C: +case 0x669D: +case 0x669E: +case 0x669F: +case 0x66A0: +case 0x66A1: +case 0x66A2: +case 0x66A3: +case 0x66A4: +case 0x66A5: +case 0x66A6: +case 0x66A7: +case 0x66A8: +case 0x66A9: +case 0x66AA: +case 0x66AB: +case 0x66AC: +case 0x66AD: +case 0x66AE: +case 0x66AF: +case 0x66B0: +case 0x66B1: +case 0x66B2: +case 0x66B3: +case 0x66B4: +case 0x66B5: +case 0x66B6: +case 0x66B7: +case 0x66B8: +case 0x66B9: +case 0x66BA: +case 0x66BB: +case 0x66BC: +case 0x66BD: +case 0x66BE: +case 0x66BF: +case 0x66C0: +case 0x66C1: +case 0x66C2: +case 0x66C3: +case 0x66C4: +case 0x66C5: +case 0x66C6: +case 0x66C7: +case 0x66C8: +case 0x66C9: +case 0x66CA: +case 0x66CB: +case 0x66CC: +case 0x66CD: +case 0x66CE: +case 0x66CF: +case 0x66D0: +case 0x66D1: +case 0x66D2: +case 0x66D3: +case 0x66D4: +case 0x66D5: +case 0x66D6: +case 0x66D7: +case 0x66D8: +case 0x66D9: +case 0x66DA: +case 0x66DB: +case 0x66DC: +case 0x66DD: +case 0x66DE: +case 0x66DF: +case 0x66E0: +case 0x66E1: +case 0x66E2: +case 0x66E3: +case 0x66E4: +case 0x66E5: +case 0x66E6: +case 0x66E7: +case 0x66E8: +case 0x66E9: +case 0x66EA: +case 0x66EB: +case 0x66EC: +case 0x66ED: +case 0x66EE: +case 0x66EF: +case 0x66F0: +case 0x66F1: +case 0x66F2: +case 0x66F3: +case 0x66F4: +case 0x66F5: +case 0x66F6: +case 0x66F7: +case 0x66F8: +case 0x66F9: +case 0x66FA: +case 0x66FB: +case 0x66FC: +case 0x66FD: +case 0x66FE: +case 0x66FF: + +// BCC +case 0x6601: +{ + if (CPU->flag_notZ) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6702: +case 0x6703: +case 0x6704: +case 0x6705: +case 0x6706: +case 0x6707: +case 0x6708: +case 0x6709: +case 0x670A: +case 0x670B: +case 0x670C: +case 0x670D: +case 0x670E: +case 0x670F: +case 0x6710: +case 0x6711: +case 0x6712: +case 0x6713: +case 0x6714: +case 0x6715: +case 0x6716: +case 0x6717: +case 0x6718: +case 0x6719: +case 0x671A: +case 0x671B: +case 0x671C: +case 0x671D: +case 0x671E: +case 0x671F: +case 0x6720: +case 0x6721: +case 0x6722: +case 0x6723: +case 0x6724: +case 0x6725: +case 0x6726: +case 0x6727: +case 0x6728: +case 0x6729: +case 0x672A: +case 0x672B: +case 0x672C: +case 0x672D: +case 0x672E: +case 0x672F: +case 0x6730: +case 0x6731: +case 0x6732: +case 0x6733: +case 0x6734: +case 0x6735: +case 0x6736: +case 0x6737: +case 0x6738: +case 0x6739: +case 0x673A: +case 0x673B: +case 0x673C: +case 0x673D: +case 0x673E: +case 0x673F: +case 0x6740: +case 0x6741: +case 0x6742: +case 0x6743: +case 0x6744: +case 0x6745: +case 0x6746: +case 0x6747: +case 0x6748: +case 0x6749: +case 0x674A: +case 0x674B: +case 0x674C: +case 0x674D: +case 0x674E: +case 0x674F: +case 0x6750: +case 0x6751: +case 0x6752: +case 0x6753: +case 0x6754: +case 0x6755: +case 0x6756: +case 0x6757: +case 0x6758: +case 0x6759: +case 0x675A: +case 0x675B: +case 0x675C: +case 0x675D: +case 0x675E: +case 0x675F: +case 0x6760: +case 0x6761: +case 0x6762: +case 0x6763: +case 0x6764: +case 0x6765: +case 0x6766: +case 0x6767: +case 0x6768: +case 0x6769: +case 0x676A: +case 0x676B: +case 0x676C: +case 0x676D: +case 0x676E: +case 0x676F: +case 0x6770: +case 0x6771: +case 0x6772: +case 0x6773: +case 0x6774: +case 0x6775: +case 0x6776: +case 0x6777: +case 0x6778: +case 0x6779: +case 0x677A: +case 0x677B: +case 0x677C: +case 0x677D: +case 0x677E: +case 0x677F: +case 0x6780: +case 0x6781: +case 0x6782: +case 0x6783: +case 0x6784: +case 0x6785: +case 0x6786: +case 0x6787: +case 0x6788: +case 0x6789: +case 0x678A: +case 0x678B: +case 0x678C: +case 0x678D: +case 0x678E: +case 0x678F: +case 0x6790: +case 0x6791: +case 0x6792: +case 0x6793: +case 0x6794: +case 0x6795: +case 0x6796: +case 0x6797: +case 0x6798: +case 0x6799: +case 0x679A: +case 0x679B: +case 0x679C: +case 0x679D: +case 0x679E: +case 0x679F: +case 0x67A0: +case 0x67A1: +case 0x67A2: +case 0x67A3: +case 0x67A4: +case 0x67A5: +case 0x67A6: +case 0x67A7: +case 0x67A8: +case 0x67A9: +case 0x67AA: +case 0x67AB: +case 0x67AC: +case 0x67AD: +case 0x67AE: +case 0x67AF: +case 0x67B0: +case 0x67B1: +case 0x67B2: +case 0x67B3: +case 0x67B4: +case 0x67B5: +case 0x67B6: +case 0x67B7: +case 0x67B8: +case 0x67B9: +case 0x67BA: +case 0x67BB: +case 0x67BC: +case 0x67BD: +case 0x67BE: +case 0x67BF: +case 0x67C0: +case 0x67C1: +case 0x67C2: +case 0x67C3: +case 0x67C4: +case 0x67C5: +case 0x67C6: +case 0x67C7: +case 0x67C8: +case 0x67C9: +case 0x67CA: +case 0x67CB: +case 0x67CC: +case 0x67CD: +case 0x67CE: +case 0x67CF: +case 0x67D0: +case 0x67D1: +case 0x67D2: +case 0x67D3: +case 0x67D4: +case 0x67D5: +case 0x67D6: +case 0x67D7: +case 0x67D8: +case 0x67D9: +case 0x67DA: +case 0x67DB: +case 0x67DC: +case 0x67DD: +case 0x67DE: +case 0x67DF: +case 0x67E0: +case 0x67E1: +case 0x67E2: +case 0x67E3: +case 0x67E4: +case 0x67E5: +case 0x67E6: +case 0x67E7: +case 0x67E8: +case 0x67E9: +case 0x67EA: +case 0x67EB: +case 0x67EC: +case 0x67ED: +case 0x67EE: +case 0x67EF: +case 0x67F0: +case 0x67F1: +case 0x67F2: +case 0x67F3: +case 0x67F4: +case 0x67F5: +case 0x67F6: +case 0x67F7: +case 0x67F8: +case 0x67F9: +case 0x67FA: +case 0x67FB: +case 0x67FC: +case 0x67FD: +case 0x67FE: +case 0x67FF: + +// BCC +case 0x6701: +{ + if (!CPU->flag_notZ) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6802: +case 0x6803: +case 0x6804: +case 0x6805: +case 0x6806: +case 0x6807: +case 0x6808: +case 0x6809: +case 0x680A: +case 0x680B: +case 0x680C: +case 0x680D: +case 0x680E: +case 0x680F: +case 0x6810: +case 0x6811: +case 0x6812: +case 0x6813: +case 0x6814: +case 0x6815: +case 0x6816: +case 0x6817: +case 0x6818: +case 0x6819: +case 0x681A: +case 0x681B: +case 0x681C: +case 0x681D: +case 0x681E: +case 0x681F: +case 0x6820: +case 0x6821: +case 0x6822: +case 0x6823: +case 0x6824: +case 0x6825: +case 0x6826: +case 0x6827: +case 0x6828: +case 0x6829: +case 0x682A: +case 0x682B: +case 0x682C: +case 0x682D: +case 0x682E: +case 0x682F: +case 0x6830: +case 0x6831: +case 0x6832: +case 0x6833: +case 0x6834: +case 0x6835: +case 0x6836: +case 0x6837: +case 0x6838: +case 0x6839: +case 0x683A: +case 0x683B: +case 0x683C: +case 0x683D: +case 0x683E: +case 0x683F: +case 0x6840: +case 0x6841: +case 0x6842: +case 0x6843: +case 0x6844: +case 0x6845: +case 0x6846: +case 0x6847: +case 0x6848: +case 0x6849: +case 0x684A: +case 0x684B: +case 0x684C: +case 0x684D: +case 0x684E: +case 0x684F: +case 0x6850: +case 0x6851: +case 0x6852: +case 0x6853: +case 0x6854: +case 0x6855: +case 0x6856: +case 0x6857: +case 0x6858: +case 0x6859: +case 0x685A: +case 0x685B: +case 0x685C: +case 0x685D: +case 0x685E: +case 0x685F: +case 0x6860: +case 0x6861: +case 0x6862: +case 0x6863: +case 0x6864: +case 0x6865: +case 0x6866: +case 0x6867: +case 0x6868: +case 0x6869: +case 0x686A: +case 0x686B: +case 0x686C: +case 0x686D: +case 0x686E: +case 0x686F: +case 0x6870: +case 0x6871: +case 0x6872: +case 0x6873: +case 0x6874: +case 0x6875: +case 0x6876: +case 0x6877: +case 0x6878: +case 0x6879: +case 0x687A: +case 0x687B: +case 0x687C: +case 0x687D: +case 0x687E: +case 0x687F: +case 0x6880: +case 0x6881: +case 0x6882: +case 0x6883: +case 0x6884: +case 0x6885: +case 0x6886: +case 0x6887: +case 0x6888: +case 0x6889: +case 0x688A: +case 0x688B: +case 0x688C: +case 0x688D: +case 0x688E: +case 0x688F: +case 0x6890: +case 0x6891: +case 0x6892: +case 0x6893: +case 0x6894: +case 0x6895: +case 0x6896: +case 0x6897: +case 0x6898: +case 0x6899: +case 0x689A: +case 0x689B: +case 0x689C: +case 0x689D: +case 0x689E: +case 0x689F: +case 0x68A0: +case 0x68A1: +case 0x68A2: +case 0x68A3: +case 0x68A4: +case 0x68A5: +case 0x68A6: +case 0x68A7: +case 0x68A8: +case 0x68A9: +case 0x68AA: +case 0x68AB: +case 0x68AC: +case 0x68AD: +case 0x68AE: +case 0x68AF: +case 0x68B0: +case 0x68B1: +case 0x68B2: +case 0x68B3: +case 0x68B4: +case 0x68B5: +case 0x68B6: +case 0x68B7: +case 0x68B8: +case 0x68B9: +case 0x68BA: +case 0x68BB: +case 0x68BC: +case 0x68BD: +case 0x68BE: +case 0x68BF: +case 0x68C0: +case 0x68C1: +case 0x68C2: +case 0x68C3: +case 0x68C4: +case 0x68C5: +case 0x68C6: +case 0x68C7: +case 0x68C8: +case 0x68C9: +case 0x68CA: +case 0x68CB: +case 0x68CC: +case 0x68CD: +case 0x68CE: +case 0x68CF: +case 0x68D0: +case 0x68D1: +case 0x68D2: +case 0x68D3: +case 0x68D4: +case 0x68D5: +case 0x68D6: +case 0x68D7: +case 0x68D8: +case 0x68D9: +case 0x68DA: +case 0x68DB: +case 0x68DC: +case 0x68DD: +case 0x68DE: +case 0x68DF: +case 0x68E0: +case 0x68E1: +case 0x68E2: +case 0x68E3: +case 0x68E4: +case 0x68E5: +case 0x68E6: +case 0x68E7: +case 0x68E8: +case 0x68E9: +case 0x68EA: +case 0x68EB: +case 0x68EC: +case 0x68ED: +case 0x68EE: +case 0x68EF: +case 0x68F0: +case 0x68F1: +case 0x68F2: +case 0x68F3: +case 0x68F4: +case 0x68F5: +case 0x68F6: +case 0x68F7: +case 0x68F8: +case 0x68F9: +case 0x68FA: +case 0x68FB: +case 0x68FC: +case 0x68FD: +case 0x68FE: +case 0x68FF: + +// BCC +case 0x6801: +{ + if (!(CPU->flag_V & 0x80)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6902: +case 0x6903: +case 0x6904: +case 0x6905: +case 0x6906: +case 0x6907: +case 0x6908: +case 0x6909: +case 0x690A: +case 0x690B: +case 0x690C: +case 0x690D: +case 0x690E: +case 0x690F: +case 0x6910: +case 0x6911: +case 0x6912: +case 0x6913: +case 0x6914: +case 0x6915: +case 0x6916: +case 0x6917: +case 0x6918: +case 0x6919: +case 0x691A: +case 0x691B: +case 0x691C: +case 0x691D: +case 0x691E: +case 0x691F: +case 0x6920: +case 0x6921: +case 0x6922: +case 0x6923: +case 0x6924: +case 0x6925: +case 0x6926: +case 0x6927: +case 0x6928: +case 0x6929: +case 0x692A: +case 0x692B: +case 0x692C: +case 0x692D: +case 0x692E: +case 0x692F: +case 0x6930: +case 0x6931: +case 0x6932: +case 0x6933: +case 0x6934: +case 0x6935: +case 0x6936: +case 0x6937: +case 0x6938: +case 0x6939: +case 0x693A: +case 0x693B: +case 0x693C: +case 0x693D: +case 0x693E: +case 0x693F: +case 0x6940: +case 0x6941: +case 0x6942: +case 0x6943: +case 0x6944: +case 0x6945: +case 0x6946: +case 0x6947: +case 0x6948: +case 0x6949: +case 0x694A: +case 0x694B: +case 0x694C: +case 0x694D: +case 0x694E: +case 0x694F: +case 0x6950: +case 0x6951: +case 0x6952: +case 0x6953: +case 0x6954: +case 0x6955: +case 0x6956: +case 0x6957: +case 0x6958: +case 0x6959: +case 0x695A: +case 0x695B: +case 0x695C: +case 0x695D: +case 0x695E: +case 0x695F: +case 0x6960: +case 0x6961: +case 0x6962: +case 0x6963: +case 0x6964: +case 0x6965: +case 0x6966: +case 0x6967: +case 0x6968: +case 0x6969: +case 0x696A: +case 0x696B: +case 0x696C: +case 0x696D: +case 0x696E: +case 0x696F: +case 0x6970: +case 0x6971: +case 0x6972: +case 0x6973: +case 0x6974: +case 0x6975: +case 0x6976: +case 0x6977: +case 0x6978: +case 0x6979: +case 0x697A: +case 0x697B: +case 0x697C: +case 0x697D: +case 0x697E: +case 0x697F: +case 0x6980: +case 0x6981: +case 0x6982: +case 0x6983: +case 0x6984: +case 0x6985: +case 0x6986: +case 0x6987: +case 0x6988: +case 0x6989: +case 0x698A: +case 0x698B: +case 0x698C: +case 0x698D: +case 0x698E: +case 0x698F: +case 0x6990: +case 0x6991: +case 0x6992: +case 0x6993: +case 0x6994: +case 0x6995: +case 0x6996: +case 0x6997: +case 0x6998: +case 0x6999: +case 0x699A: +case 0x699B: +case 0x699C: +case 0x699D: +case 0x699E: +case 0x699F: +case 0x69A0: +case 0x69A1: +case 0x69A2: +case 0x69A3: +case 0x69A4: +case 0x69A5: +case 0x69A6: +case 0x69A7: +case 0x69A8: +case 0x69A9: +case 0x69AA: +case 0x69AB: +case 0x69AC: +case 0x69AD: +case 0x69AE: +case 0x69AF: +case 0x69B0: +case 0x69B1: +case 0x69B2: +case 0x69B3: +case 0x69B4: +case 0x69B5: +case 0x69B6: +case 0x69B7: +case 0x69B8: +case 0x69B9: +case 0x69BA: +case 0x69BB: +case 0x69BC: +case 0x69BD: +case 0x69BE: +case 0x69BF: +case 0x69C0: +case 0x69C1: +case 0x69C2: +case 0x69C3: +case 0x69C4: +case 0x69C5: +case 0x69C6: +case 0x69C7: +case 0x69C8: +case 0x69C9: +case 0x69CA: +case 0x69CB: +case 0x69CC: +case 0x69CD: +case 0x69CE: +case 0x69CF: +case 0x69D0: +case 0x69D1: +case 0x69D2: +case 0x69D3: +case 0x69D4: +case 0x69D5: +case 0x69D6: +case 0x69D7: +case 0x69D8: +case 0x69D9: +case 0x69DA: +case 0x69DB: +case 0x69DC: +case 0x69DD: +case 0x69DE: +case 0x69DF: +case 0x69E0: +case 0x69E1: +case 0x69E2: +case 0x69E3: +case 0x69E4: +case 0x69E5: +case 0x69E6: +case 0x69E7: +case 0x69E8: +case 0x69E9: +case 0x69EA: +case 0x69EB: +case 0x69EC: +case 0x69ED: +case 0x69EE: +case 0x69EF: +case 0x69F0: +case 0x69F1: +case 0x69F2: +case 0x69F3: +case 0x69F4: +case 0x69F5: +case 0x69F6: +case 0x69F7: +case 0x69F8: +case 0x69F9: +case 0x69FA: +case 0x69FB: +case 0x69FC: +case 0x69FD: +case 0x69FE: +case 0x69FF: + +// BCC +case 0x6901: +{ + if (CPU->flag_V & 0x80) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6A02: +case 0x6A03: +case 0x6A04: +case 0x6A05: +case 0x6A06: +case 0x6A07: +case 0x6A08: +case 0x6A09: +case 0x6A0A: +case 0x6A0B: +case 0x6A0C: +case 0x6A0D: +case 0x6A0E: +case 0x6A0F: +case 0x6A10: +case 0x6A11: +case 0x6A12: +case 0x6A13: +case 0x6A14: +case 0x6A15: +case 0x6A16: +case 0x6A17: +case 0x6A18: +case 0x6A19: +case 0x6A1A: +case 0x6A1B: +case 0x6A1C: +case 0x6A1D: +case 0x6A1E: +case 0x6A1F: +case 0x6A20: +case 0x6A21: +case 0x6A22: +case 0x6A23: +case 0x6A24: +case 0x6A25: +case 0x6A26: +case 0x6A27: +case 0x6A28: +case 0x6A29: +case 0x6A2A: +case 0x6A2B: +case 0x6A2C: +case 0x6A2D: +case 0x6A2E: +case 0x6A2F: +case 0x6A30: +case 0x6A31: +case 0x6A32: +case 0x6A33: +case 0x6A34: +case 0x6A35: +case 0x6A36: +case 0x6A37: +case 0x6A38: +case 0x6A39: +case 0x6A3A: +case 0x6A3B: +case 0x6A3C: +case 0x6A3D: +case 0x6A3E: +case 0x6A3F: +case 0x6A40: +case 0x6A41: +case 0x6A42: +case 0x6A43: +case 0x6A44: +case 0x6A45: +case 0x6A46: +case 0x6A47: +case 0x6A48: +case 0x6A49: +case 0x6A4A: +case 0x6A4B: +case 0x6A4C: +case 0x6A4D: +case 0x6A4E: +case 0x6A4F: +case 0x6A50: +case 0x6A51: +case 0x6A52: +case 0x6A53: +case 0x6A54: +case 0x6A55: +case 0x6A56: +case 0x6A57: +case 0x6A58: +case 0x6A59: +case 0x6A5A: +case 0x6A5B: +case 0x6A5C: +case 0x6A5D: +case 0x6A5E: +case 0x6A5F: +case 0x6A60: +case 0x6A61: +case 0x6A62: +case 0x6A63: +case 0x6A64: +case 0x6A65: +case 0x6A66: +case 0x6A67: +case 0x6A68: +case 0x6A69: +case 0x6A6A: +case 0x6A6B: +case 0x6A6C: +case 0x6A6D: +case 0x6A6E: +case 0x6A6F: +case 0x6A70: +case 0x6A71: +case 0x6A72: +case 0x6A73: +case 0x6A74: +case 0x6A75: +case 0x6A76: +case 0x6A77: +case 0x6A78: +case 0x6A79: +case 0x6A7A: +case 0x6A7B: +case 0x6A7C: +case 0x6A7D: +case 0x6A7E: +case 0x6A7F: +case 0x6A80: +case 0x6A81: +case 0x6A82: +case 0x6A83: +case 0x6A84: +case 0x6A85: +case 0x6A86: +case 0x6A87: +case 0x6A88: +case 0x6A89: +case 0x6A8A: +case 0x6A8B: +case 0x6A8C: +case 0x6A8D: +case 0x6A8E: +case 0x6A8F: +case 0x6A90: +case 0x6A91: +case 0x6A92: +case 0x6A93: +case 0x6A94: +case 0x6A95: +case 0x6A96: +case 0x6A97: +case 0x6A98: +case 0x6A99: +case 0x6A9A: +case 0x6A9B: +case 0x6A9C: +case 0x6A9D: +case 0x6A9E: +case 0x6A9F: +case 0x6AA0: +case 0x6AA1: +case 0x6AA2: +case 0x6AA3: +case 0x6AA4: +case 0x6AA5: +case 0x6AA6: +case 0x6AA7: +case 0x6AA8: +case 0x6AA9: +case 0x6AAA: +case 0x6AAB: +case 0x6AAC: +case 0x6AAD: +case 0x6AAE: +case 0x6AAF: +case 0x6AB0: +case 0x6AB1: +case 0x6AB2: +case 0x6AB3: +case 0x6AB4: +case 0x6AB5: +case 0x6AB6: +case 0x6AB7: +case 0x6AB8: +case 0x6AB9: +case 0x6ABA: +case 0x6ABB: +case 0x6ABC: +case 0x6ABD: +case 0x6ABE: +case 0x6ABF: +case 0x6AC0: +case 0x6AC1: +case 0x6AC2: +case 0x6AC3: +case 0x6AC4: +case 0x6AC5: +case 0x6AC6: +case 0x6AC7: +case 0x6AC8: +case 0x6AC9: +case 0x6ACA: +case 0x6ACB: +case 0x6ACC: +case 0x6ACD: +case 0x6ACE: +case 0x6ACF: +case 0x6AD0: +case 0x6AD1: +case 0x6AD2: +case 0x6AD3: +case 0x6AD4: +case 0x6AD5: +case 0x6AD6: +case 0x6AD7: +case 0x6AD8: +case 0x6AD9: +case 0x6ADA: +case 0x6ADB: +case 0x6ADC: +case 0x6ADD: +case 0x6ADE: +case 0x6ADF: +case 0x6AE0: +case 0x6AE1: +case 0x6AE2: +case 0x6AE3: +case 0x6AE4: +case 0x6AE5: +case 0x6AE6: +case 0x6AE7: +case 0x6AE8: +case 0x6AE9: +case 0x6AEA: +case 0x6AEB: +case 0x6AEC: +case 0x6AED: +case 0x6AEE: +case 0x6AEF: +case 0x6AF0: +case 0x6AF1: +case 0x6AF2: +case 0x6AF3: +case 0x6AF4: +case 0x6AF5: +case 0x6AF6: +case 0x6AF7: +case 0x6AF8: +case 0x6AF9: +case 0x6AFA: +case 0x6AFB: +case 0x6AFC: +case 0x6AFD: +case 0x6AFE: +case 0x6AFF: + +// BCC +case 0x6A01: +{ + if (!(CPU->flag_N & 0x80)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6B02: +case 0x6B03: +case 0x6B04: +case 0x6B05: +case 0x6B06: +case 0x6B07: +case 0x6B08: +case 0x6B09: +case 0x6B0A: +case 0x6B0B: +case 0x6B0C: +case 0x6B0D: +case 0x6B0E: +case 0x6B0F: +case 0x6B10: +case 0x6B11: +case 0x6B12: +case 0x6B13: +case 0x6B14: +case 0x6B15: +case 0x6B16: +case 0x6B17: +case 0x6B18: +case 0x6B19: +case 0x6B1A: +case 0x6B1B: +case 0x6B1C: +case 0x6B1D: +case 0x6B1E: +case 0x6B1F: +case 0x6B20: +case 0x6B21: +case 0x6B22: +case 0x6B23: +case 0x6B24: +case 0x6B25: +case 0x6B26: +case 0x6B27: +case 0x6B28: +case 0x6B29: +case 0x6B2A: +case 0x6B2B: +case 0x6B2C: +case 0x6B2D: +case 0x6B2E: +case 0x6B2F: +case 0x6B30: +case 0x6B31: +case 0x6B32: +case 0x6B33: +case 0x6B34: +case 0x6B35: +case 0x6B36: +case 0x6B37: +case 0x6B38: +case 0x6B39: +case 0x6B3A: +case 0x6B3B: +case 0x6B3C: +case 0x6B3D: +case 0x6B3E: +case 0x6B3F: +case 0x6B40: +case 0x6B41: +case 0x6B42: +case 0x6B43: +case 0x6B44: +case 0x6B45: +case 0x6B46: +case 0x6B47: +case 0x6B48: +case 0x6B49: +case 0x6B4A: +case 0x6B4B: +case 0x6B4C: +case 0x6B4D: +case 0x6B4E: +case 0x6B4F: +case 0x6B50: +case 0x6B51: +case 0x6B52: +case 0x6B53: +case 0x6B54: +case 0x6B55: +case 0x6B56: +case 0x6B57: +case 0x6B58: +case 0x6B59: +case 0x6B5A: +case 0x6B5B: +case 0x6B5C: +case 0x6B5D: +case 0x6B5E: +case 0x6B5F: +case 0x6B60: +case 0x6B61: +case 0x6B62: +case 0x6B63: +case 0x6B64: +case 0x6B65: +case 0x6B66: +case 0x6B67: +case 0x6B68: +case 0x6B69: +case 0x6B6A: +case 0x6B6B: +case 0x6B6C: +case 0x6B6D: +case 0x6B6E: +case 0x6B6F: +case 0x6B70: +case 0x6B71: +case 0x6B72: +case 0x6B73: +case 0x6B74: +case 0x6B75: +case 0x6B76: +case 0x6B77: +case 0x6B78: +case 0x6B79: +case 0x6B7A: +case 0x6B7B: +case 0x6B7C: +case 0x6B7D: +case 0x6B7E: +case 0x6B7F: +case 0x6B80: +case 0x6B81: +case 0x6B82: +case 0x6B83: +case 0x6B84: +case 0x6B85: +case 0x6B86: +case 0x6B87: +case 0x6B88: +case 0x6B89: +case 0x6B8A: +case 0x6B8B: +case 0x6B8C: +case 0x6B8D: +case 0x6B8E: +case 0x6B8F: +case 0x6B90: +case 0x6B91: +case 0x6B92: +case 0x6B93: +case 0x6B94: +case 0x6B95: +case 0x6B96: +case 0x6B97: +case 0x6B98: +case 0x6B99: +case 0x6B9A: +case 0x6B9B: +case 0x6B9C: +case 0x6B9D: +case 0x6B9E: +case 0x6B9F: +case 0x6BA0: +case 0x6BA1: +case 0x6BA2: +case 0x6BA3: +case 0x6BA4: +case 0x6BA5: +case 0x6BA6: +case 0x6BA7: +case 0x6BA8: +case 0x6BA9: +case 0x6BAA: +case 0x6BAB: +case 0x6BAC: +case 0x6BAD: +case 0x6BAE: +case 0x6BAF: +case 0x6BB0: +case 0x6BB1: +case 0x6BB2: +case 0x6BB3: +case 0x6BB4: +case 0x6BB5: +case 0x6BB6: +case 0x6BB7: +case 0x6BB8: +case 0x6BB9: +case 0x6BBA: +case 0x6BBB: +case 0x6BBC: +case 0x6BBD: +case 0x6BBE: +case 0x6BBF: +case 0x6BC0: +case 0x6BC1: +case 0x6BC2: +case 0x6BC3: +case 0x6BC4: +case 0x6BC5: +case 0x6BC6: +case 0x6BC7: +case 0x6BC8: +case 0x6BC9: +case 0x6BCA: +case 0x6BCB: +case 0x6BCC: +case 0x6BCD: +case 0x6BCE: +case 0x6BCF: +case 0x6BD0: +case 0x6BD1: +case 0x6BD2: +case 0x6BD3: +case 0x6BD4: +case 0x6BD5: +case 0x6BD6: +case 0x6BD7: +case 0x6BD8: +case 0x6BD9: +case 0x6BDA: +case 0x6BDB: +case 0x6BDC: +case 0x6BDD: +case 0x6BDE: +case 0x6BDF: +case 0x6BE0: +case 0x6BE1: +case 0x6BE2: +case 0x6BE3: +case 0x6BE4: +case 0x6BE5: +case 0x6BE6: +case 0x6BE7: +case 0x6BE8: +case 0x6BE9: +case 0x6BEA: +case 0x6BEB: +case 0x6BEC: +case 0x6BED: +case 0x6BEE: +case 0x6BEF: +case 0x6BF0: +case 0x6BF1: +case 0x6BF2: +case 0x6BF3: +case 0x6BF4: +case 0x6BF5: +case 0x6BF6: +case 0x6BF7: +case 0x6BF8: +case 0x6BF9: +case 0x6BFA: +case 0x6BFB: +case 0x6BFC: +case 0x6BFD: +case 0x6BFE: +case 0x6BFF: + +// BCC +case 0x6B01: +{ + if (CPU->flag_N & 0x80) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6C02: +case 0x6C03: +case 0x6C04: +case 0x6C05: +case 0x6C06: +case 0x6C07: +case 0x6C08: +case 0x6C09: +case 0x6C0A: +case 0x6C0B: +case 0x6C0C: +case 0x6C0D: +case 0x6C0E: +case 0x6C0F: +case 0x6C10: +case 0x6C11: +case 0x6C12: +case 0x6C13: +case 0x6C14: +case 0x6C15: +case 0x6C16: +case 0x6C17: +case 0x6C18: +case 0x6C19: +case 0x6C1A: +case 0x6C1B: +case 0x6C1C: +case 0x6C1D: +case 0x6C1E: +case 0x6C1F: +case 0x6C20: +case 0x6C21: +case 0x6C22: +case 0x6C23: +case 0x6C24: +case 0x6C25: +case 0x6C26: +case 0x6C27: +case 0x6C28: +case 0x6C29: +case 0x6C2A: +case 0x6C2B: +case 0x6C2C: +case 0x6C2D: +case 0x6C2E: +case 0x6C2F: +case 0x6C30: +case 0x6C31: +case 0x6C32: +case 0x6C33: +case 0x6C34: +case 0x6C35: +case 0x6C36: +case 0x6C37: +case 0x6C38: +case 0x6C39: +case 0x6C3A: +case 0x6C3B: +case 0x6C3C: +case 0x6C3D: +case 0x6C3E: +case 0x6C3F: +case 0x6C40: +case 0x6C41: +case 0x6C42: +case 0x6C43: +case 0x6C44: +case 0x6C45: +case 0x6C46: +case 0x6C47: +case 0x6C48: +case 0x6C49: +case 0x6C4A: +case 0x6C4B: +case 0x6C4C: +case 0x6C4D: +case 0x6C4E: +case 0x6C4F: +case 0x6C50: +case 0x6C51: +case 0x6C52: +case 0x6C53: +case 0x6C54: +case 0x6C55: +case 0x6C56: +case 0x6C57: +case 0x6C58: +case 0x6C59: +case 0x6C5A: +case 0x6C5B: +case 0x6C5C: +case 0x6C5D: +case 0x6C5E: +case 0x6C5F: +case 0x6C60: +case 0x6C61: +case 0x6C62: +case 0x6C63: +case 0x6C64: +case 0x6C65: +case 0x6C66: +case 0x6C67: +case 0x6C68: +case 0x6C69: +case 0x6C6A: +case 0x6C6B: +case 0x6C6C: +case 0x6C6D: +case 0x6C6E: +case 0x6C6F: +case 0x6C70: +case 0x6C71: +case 0x6C72: +case 0x6C73: +case 0x6C74: +case 0x6C75: +case 0x6C76: +case 0x6C77: +case 0x6C78: +case 0x6C79: +case 0x6C7A: +case 0x6C7B: +case 0x6C7C: +case 0x6C7D: +case 0x6C7E: +case 0x6C7F: +case 0x6C80: +case 0x6C81: +case 0x6C82: +case 0x6C83: +case 0x6C84: +case 0x6C85: +case 0x6C86: +case 0x6C87: +case 0x6C88: +case 0x6C89: +case 0x6C8A: +case 0x6C8B: +case 0x6C8C: +case 0x6C8D: +case 0x6C8E: +case 0x6C8F: +case 0x6C90: +case 0x6C91: +case 0x6C92: +case 0x6C93: +case 0x6C94: +case 0x6C95: +case 0x6C96: +case 0x6C97: +case 0x6C98: +case 0x6C99: +case 0x6C9A: +case 0x6C9B: +case 0x6C9C: +case 0x6C9D: +case 0x6C9E: +case 0x6C9F: +case 0x6CA0: +case 0x6CA1: +case 0x6CA2: +case 0x6CA3: +case 0x6CA4: +case 0x6CA5: +case 0x6CA6: +case 0x6CA7: +case 0x6CA8: +case 0x6CA9: +case 0x6CAA: +case 0x6CAB: +case 0x6CAC: +case 0x6CAD: +case 0x6CAE: +case 0x6CAF: +case 0x6CB0: +case 0x6CB1: +case 0x6CB2: +case 0x6CB3: +case 0x6CB4: +case 0x6CB5: +case 0x6CB6: +case 0x6CB7: +case 0x6CB8: +case 0x6CB9: +case 0x6CBA: +case 0x6CBB: +case 0x6CBC: +case 0x6CBD: +case 0x6CBE: +case 0x6CBF: +case 0x6CC0: +case 0x6CC1: +case 0x6CC2: +case 0x6CC3: +case 0x6CC4: +case 0x6CC5: +case 0x6CC6: +case 0x6CC7: +case 0x6CC8: +case 0x6CC9: +case 0x6CCA: +case 0x6CCB: +case 0x6CCC: +case 0x6CCD: +case 0x6CCE: +case 0x6CCF: +case 0x6CD0: +case 0x6CD1: +case 0x6CD2: +case 0x6CD3: +case 0x6CD4: +case 0x6CD5: +case 0x6CD6: +case 0x6CD7: +case 0x6CD8: +case 0x6CD9: +case 0x6CDA: +case 0x6CDB: +case 0x6CDC: +case 0x6CDD: +case 0x6CDE: +case 0x6CDF: +case 0x6CE0: +case 0x6CE1: +case 0x6CE2: +case 0x6CE3: +case 0x6CE4: +case 0x6CE5: +case 0x6CE6: +case 0x6CE7: +case 0x6CE8: +case 0x6CE9: +case 0x6CEA: +case 0x6CEB: +case 0x6CEC: +case 0x6CED: +case 0x6CEE: +case 0x6CEF: +case 0x6CF0: +case 0x6CF1: +case 0x6CF2: +case 0x6CF3: +case 0x6CF4: +case 0x6CF5: +case 0x6CF6: +case 0x6CF7: +case 0x6CF8: +case 0x6CF9: +case 0x6CFA: +case 0x6CFB: +case 0x6CFC: +case 0x6CFD: +case 0x6CFE: +case 0x6CFF: + +// BCC +case 0x6C01: +{ + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6D02: +case 0x6D03: +case 0x6D04: +case 0x6D05: +case 0x6D06: +case 0x6D07: +case 0x6D08: +case 0x6D09: +case 0x6D0A: +case 0x6D0B: +case 0x6D0C: +case 0x6D0D: +case 0x6D0E: +case 0x6D0F: +case 0x6D10: +case 0x6D11: +case 0x6D12: +case 0x6D13: +case 0x6D14: +case 0x6D15: +case 0x6D16: +case 0x6D17: +case 0x6D18: +case 0x6D19: +case 0x6D1A: +case 0x6D1B: +case 0x6D1C: +case 0x6D1D: +case 0x6D1E: +case 0x6D1F: +case 0x6D20: +case 0x6D21: +case 0x6D22: +case 0x6D23: +case 0x6D24: +case 0x6D25: +case 0x6D26: +case 0x6D27: +case 0x6D28: +case 0x6D29: +case 0x6D2A: +case 0x6D2B: +case 0x6D2C: +case 0x6D2D: +case 0x6D2E: +case 0x6D2F: +case 0x6D30: +case 0x6D31: +case 0x6D32: +case 0x6D33: +case 0x6D34: +case 0x6D35: +case 0x6D36: +case 0x6D37: +case 0x6D38: +case 0x6D39: +case 0x6D3A: +case 0x6D3B: +case 0x6D3C: +case 0x6D3D: +case 0x6D3E: +case 0x6D3F: +case 0x6D40: +case 0x6D41: +case 0x6D42: +case 0x6D43: +case 0x6D44: +case 0x6D45: +case 0x6D46: +case 0x6D47: +case 0x6D48: +case 0x6D49: +case 0x6D4A: +case 0x6D4B: +case 0x6D4C: +case 0x6D4D: +case 0x6D4E: +case 0x6D4F: +case 0x6D50: +case 0x6D51: +case 0x6D52: +case 0x6D53: +case 0x6D54: +case 0x6D55: +case 0x6D56: +case 0x6D57: +case 0x6D58: +case 0x6D59: +case 0x6D5A: +case 0x6D5B: +case 0x6D5C: +case 0x6D5D: +case 0x6D5E: +case 0x6D5F: +case 0x6D60: +case 0x6D61: +case 0x6D62: +case 0x6D63: +case 0x6D64: +case 0x6D65: +case 0x6D66: +case 0x6D67: +case 0x6D68: +case 0x6D69: +case 0x6D6A: +case 0x6D6B: +case 0x6D6C: +case 0x6D6D: +case 0x6D6E: +case 0x6D6F: +case 0x6D70: +case 0x6D71: +case 0x6D72: +case 0x6D73: +case 0x6D74: +case 0x6D75: +case 0x6D76: +case 0x6D77: +case 0x6D78: +case 0x6D79: +case 0x6D7A: +case 0x6D7B: +case 0x6D7C: +case 0x6D7D: +case 0x6D7E: +case 0x6D7F: +case 0x6D80: +case 0x6D81: +case 0x6D82: +case 0x6D83: +case 0x6D84: +case 0x6D85: +case 0x6D86: +case 0x6D87: +case 0x6D88: +case 0x6D89: +case 0x6D8A: +case 0x6D8B: +case 0x6D8C: +case 0x6D8D: +case 0x6D8E: +case 0x6D8F: +case 0x6D90: +case 0x6D91: +case 0x6D92: +case 0x6D93: +case 0x6D94: +case 0x6D95: +case 0x6D96: +case 0x6D97: +case 0x6D98: +case 0x6D99: +case 0x6D9A: +case 0x6D9B: +case 0x6D9C: +case 0x6D9D: +case 0x6D9E: +case 0x6D9F: +case 0x6DA0: +case 0x6DA1: +case 0x6DA2: +case 0x6DA3: +case 0x6DA4: +case 0x6DA5: +case 0x6DA6: +case 0x6DA7: +case 0x6DA8: +case 0x6DA9: +case 0x6DAA: +case 0x6DAB: +case 0x6DAC: +case 0x6DAD: +case 0x6DAE: +case 0x6DAF: +case 0x6DB0: +case 0x6DB1: +case 0x6DB2: +case 0x6DB3: +case 0x6DB4: +case 0x6DB5: +case 0x6DB6: +case 0x6DB7: +case 0x6DB8: +case 0x6DB9: +case 0x6DBA: +case 0x6DBB: +case 0x6DBC: +case 0x6DBD: +case 0x6DBE: +case 0x6DBF: +case 0x6DC0: +case 0x6DC1: +case 0x6DC2: +case 0x6DC3: +case 0x6DC4: +case 0x6DC5: +case 0x6DC6: +case 0x6DC7: +case 0x6DC8: +case 0x6DC9: +case 0x6DCA: +case 0x6DCB: +case 0x6DCC: +case 0x6DCD: +case 0x6DCE: +case 0x6DCF: +case 0x6DD0: +case 0x6DD1: +case 0x6DD2: +case 0x6DD3: +case 0x6DD4: +case 0x6DD5: +case 0x6DD6: +case 0x6DD7: +case 0x6DD8: +case 0x6DD9: +case 0x6DDA: +case 0x6DDB: +case 0x6DDC: +case 0x6DDD: +case 0x6DDE: +case 0x6DDF: +case 0x6DE0: +case 0x6DE1: +case 0x6DE2: +case 0x6DE3: +case 0x6DE4: +case 0x6DE5: +case 0x6DE6: +case 0x6DE7: +case 0x6DE8: +case 0x6DE9: +case 0x6DEA: +case 0x6DEB: +case 0x6DEC: +case 0x6DED: +case 0x6DEE: +case 0x6DEF: +case 0x6DF0: +case 0x6DF1: +case 0x6DF2: +case 0x6DF3: +case 0x6DF4: +case 0x6DF5: +case 0x6DF6: +case 0x6DF7: +case 0x6DF8: +case 0x6DF9: +case 0x6DFA: +case 0x6DFB: +case 0x6DFC: +case 0x6DFD: +case 0x6DFE: +case 0x6DFF: + +// BCC +case 0x6D01: +{ + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6E02: +case 0x6E03: +case 0x6E04: +case 0x6E05: +case 0x6E06: +case 0x6E07: +case 0x6E08: +case 0x6E09: +case 0x6E0A: +case 0x6E0B: +case 0x6E0C: +case 0x6E0D: +case 0x6E0E: +case 0x6E0F: +case 0x6E10: +case 0x6E11: +case 0x6E12: +case 0x6E13: +case 0x6E14: +case 0x6E15: +case 0x6E16: +case 0x6E17: +case 0x6E18: +case 0x6E19: +case 0x6E1A: +case 0x6E1B: +case 0x6E1C: +case 0x6E1D: +case 0x6E1E: +case 0x6E1F: +case 0x6E20: +case 0x6E21: +case 0x6E22: +case 0x6E23: +case 0x6E24: +case 0x6E25: +case 0x6E26: +case 0x6E27: +case 0x6E28: +case 0x6E29: +case 0x6E2A: +case 0x6E2B: +case 0x6E2C: +case 0x6E2D: +case 0x6E2E: +case 0x6E2F: +case 0x6E30: +case 0x6E31: +case 0x6E32: +case 0x6E33: +case 0x6E34: +case 0x6E35: +case 0x6E36: +case 0x6E37: +case 0x6E38: +case 0x6E39: +case 0x6E3A: +case 0x6E3B: +case 0x6E3C: +case 0x6E3D: +case 0x6E3E: +case 0x6E3F: +case 0x6E40: +case 0x6E41: +case 0x6E42: +case 0x6E43: +case 0x6E44: +case 0x6E45: +case 0x6E46: +case 0x6E47: +case 0x6E48: +case 0x6E49: +case 0x6E4A: +case 0x6E4B: +case 0x6E4C: +case 0x6E4D: +case 0x6E4E: +case 0x6E4F: +case 0x6E50: +case 0x6E51: +case 0x6E52: +case 0x6E53: +case 0x6E54: +case 0x6E55: +case 0x6E56: +case 0x6E57: +case 0x6E58: +case 0x6E59: +case 0x6E5A: +case 0x6E5B: +case 0x6E5C: +case 0x6E5D: +case 0x6E5E: +case 0x6E5F: +case 0x6E60: +case 0x6E61: +case 0x6E62: +case 0x6E63: +case 0x6E64: +case 0x6E65: +case 0x6E66: +case 0x6E67: +case 0x6E68: +case 0x6E69: +case 0x6E6A: +case 0x6E6B: +case 0x6E6C: +case 0x6E6D: +case 0x6E6E: +case 0x6E6F: +case 0x6E70: +case 0x6E71: +case 0x6E72: +case 0x6E73: +case 0x6E74: +case 0x6E75: +case 0x6E76: +case 0x6E77: +case 0x6E78: +case 0x6E79: +case 0x6E7A: +case 0x6E7B: +case 0x6E7C: +case 0x6E7D: +case 0x6E7E: +case 0x6E7F: +case 0x6E80: +case 0x6E81: +case 0x6E82: +case 0x6E83: +case 0x6E84: +case 0x6E85: +case 0x6E86: +case 0x6E87: +case 0x6E88: +case 0x6E89: +case 0x6E8A: +case 0x6E8B: +case 0x6E8C: +case 0x6E8D: +case 0x6E8E: +case 0x6E8F: +case 0x6E90: +case 0x6E91: +case 0x6E92: +case 0x6E93: +case 0x6E94: +case 0x6E95: +case 0x6E96: +case 0x6E97: +case 0x6E98: +case 0x6E99: +case 0x6E9A: +case 0x6E9B: +case 0x6E9C: +case 0x6E9D: +case 0x6E9E: +case 0x6E9F: +case 0x6EA0: +case 0x6EA1: +case 0x6EA2: +case 0x6EA3: +case 0x6EA4: +case 0x6EA5: +case 0x6EA6: +case 0x6EA7: +case 0x6EA8: +case 0x6EA9: +case 0x6EAA: +case 0x6EAB: +case 0x6EAC: +case 0x6EAD: +case 0x6EAE: +case 0x6EAF: +case 0x6EB0: +case 0x6EB1: +case 0x6EB2: +case 0x6EB3: +case 0x6EB4: +case 0x6EB5: +case 0x6EB6: +case 0x6EB7: +case 0x6EB8: +case 0x6EB9: +case 0x6EBA: +case 0x6EBB: +case 0x6EBC: +case 0x6EBD: +case 0x6EBE: +case 0x6EBF: +case 0x6EC0: +case 0x6EC1: +case 0x6EC2: +case 0x6EC3: +case 0x6EC4: +case 0x6EC5: +case 0x6EC6: +case 0x6EC7: +case 0x6EC8: +case 0x6EC9: +case 0x6ECA: +case 0x6ECB: +case 0x6ECC: +case 0x6ECD: +case 0x6ECE: +case 0x6ECF: +case 0x6ED0: +case 0x6ED1: +case 0x6ED2: +case 0x6ED3: +case 0x6ED4: +case 0x6ED5: +case 0x6ED6: +case 0x6ED7: +case 0x6ED8: +case 0x6ED9: +case 0x6EDA: +case 0x6EDB: +case 0x6EDC: +case 0x6EDD: +case 0x6EDE: +case 0x6EDF: +case 0x6EE0: +case 0x6EE1: +case 0x6EE2: +case 0x6EE3: +case 0x6EE4: +case 0x6EE5: +case 0x6EE6: +case 0x6EE7: +case 0x6EE8: +case 0x6EE9: +case 0x6EEA: +case 0x6EEB: +case 0x6EEC: +case 0x6EED: +case 0x6EEE: +case 0x6EEF: +case 0x6EF0: +case 0x6EF1: +case 0x6EF2: +case 0x6EF3: +case 0x6EF4: +case 0x6EF5: +case 0x6EF6: +case 0x6EF7: +case 0x6EF8: +case 0x6EF9: +case 0x6EFA: +case 0x6EFB: +case 0x6EFC: +case 0x6EFD: +case 0x6EFE: +case 0x6EFF: + +// BCC +case 0x6E01: +{ + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) +case 0x6F02: +case 0x6F03: +case 0x6F04: +case 0x6F05: +case 0x6F06: +case 0x6F07: +case 0x6F08: +case 0x6F09: +case 0x6F0A: +case 0x6F0B: +case 0x6F0C: +case 0x6F0D: +case 0x6F0E: +case 0x6F0F: +case 0x6F10: +case 0x6F11: +case 0x6F12: +case 0x6F13: +case 0x6F14: +case 0x6F15: +case 0x6F16: +case 0x6F17: +case 0x6F18: +case 0x6F19: +case 0x6F1A: +case 0x6F1B: +case 0x6F1C: +case 0x6F1D: +case 0x6F1E: +case 0x6F1F: +case 0x6F20: +case 0x6F21: +case 0x6F22: +case 0x6F23: +case 0x6F24: +case 0x6F25: +case 0x6F26: +case 0x6F27: +case 0x6F28: +case 0x6F29: +case 0x6F2A: +case 0x6F2B: +case 0x6F2C: +case 0x6F2D: +case 0x6F2E: +case 0x6F2F: +case 0x6F30: +case 0x6F31: +case 0x6F32: +case 0x6F33: +case 0x6F34: +case 0x6F35: +case 0x6F36: +case 0x6F37: +case 0x6F38: +case 0x6F39: +case 0x6F3A: +case 0x6F3B: +case 0x6F3C: +case 0x6F3D: +case 0x6F3E: +case 0x6F3F: +case 0x6F40: +case 0x6F41: +case 0x6F42: +case 0x6F43: +case 0x6F44: +case 0x6F45: +case 0x6F46: +case 0x6F47: +case 0x6F48: +case 0x6F49: +case 0x6F4A: +case 0x6F4B: +case 0x6F4C: +case 0x6F4D: +case 0x6F4E: +case 0x6F4F: +case 0x6F50: +case 0x6F51: +case 0x6F52: +case 0x6F53: +case 0x6F54: +case 0x6F55: +case 0x6F56: +case 0x6F57: +case 0x6F58: +case 0x6F59: +case 0x6F5A: +case 0x6F5B: +case 0x6F5C: +case 0x6F5D: +case 0x6F5E: +case 0x6F5F: +case 0x6F60: +case 0x6F61: +case 0x6F62: +case 0x6F63: +case 0x6F64: +case 0x6F65: +case 0x6F66: +case 0x6F67: +case 0x6F68: +case 0x6F69: +case 0x6F6A: +case 0x6F6B: +case 0x6F6C: +case 0x6F6D: +case 0x6F6E: +case 0x6F6F: +case 0x6F70: +case 0x6F71: +case 0x6F72: +case 0x6F73: +case 0x6F74: +case 0x6F75: +case 0x6F76: +case 0x6F77: +case 0x6F78: +case 0x6F79: +case 0x6F7A: +case 0x6F7B: +case 0x6F7C: +case 0x6F7D: +case 0x6F7E: +case 0x6F7F: +case 0x6F80: +case 0x6F81: +case 0x6F82: +case 0x6F83: +case 0x6F84: +case 0x6F85: +case 0x6F86: +case 0x6F87: +case 0x6F88: +case 0x6F89: +case 0x6F8A: +case 0x6F8B: +case 0x6F8C: +case 0x6F8D: +case 0x6F8E: +case 0x6F8F: +case 0x6F90: +case 0x6F91: +case 0x6F92: +case 0x6F93: +case 0x6F94: +case 0x6F95: +case 0x6F96: +case 0x6F97: +case 0x6F98: +case 0x6F99: +case 0x6F9A: +case 0x6F9B: +case 0x6F9C: +case 0x6F9D: +case 0x6F9E: +case 0x6F9F: +case 0x6FA0: +case 0x6FA1: +case 0x6FA2: +case 0x6FA3: +case 0x6FA4: +case 0x6FA5: +case 0x6FA6: +case 0x6FA7: +case 0x6FA8: +case 0x6FA9: +case 0x6FAA: +case 0x6FAB: +case 0x6FAC: +case 0x6FAD: +case 0x6FAE: +case 0x6FAF: +case 0x6FB0: +case 0x6FB1: +case 0x6FB2: +case 0x6FB3: +case 0x6FB4: +case 0x6FB5: +case 0x6FB6: +case 0x6FB7: +case 0x6FB8: +case 0x6FB9: +case 0x6FBA: +case 0x6FBB: +case 0x6FBC: +case 0x6FBD: +case 0x6FBE: +case 0x6FBF: +case 0x6FC0: +case 0x6FC1: +case 0x6FC2: +case 0x6FC3: +case 0x6FC4: +case 0x6FC5: +case 0x6FC6: +case 0x6FC7: +case 0x6FC8: +case 0x6FC9: +case 0x6FCA: +case 0x6FCB: +case 0x6FCC: +case 0x6FCD: +case 0x6FCE: +case 0x6FCF: +case 0x6FD0: +case 0x6FD1: +case 0x6FD2: +case 0x6FD3: +case 0x6FD4: +case 0x6FD5: +case 0x6FD6: +case 0x6FD7: +case 0x6FD8: +case 0x6FD9: +case 0x6FDA: +case 0x6FDB: +case 0x6FDC: +case 0x6FDD: +case 0x6FDE: +case 0x6FDF: +case 0x6FE0: +case 0x6FE1: +case 0x6FE2: +case 0x6FE3: +case 0x6FE4: +case 0x6FE5: +case 0x6FE6: +case 0x6FE7: +case 0x6FE8: +case 0x6FE9: +case 0x6FEA: +case 0x6FEB: +case 0x6FEC: +case 0x6FED: +case 0x6FEE: +case 0x6FEF: +case 0x6FF0: +case 0x6FF1: +case 0x6FF2: +case 0x6FF3: +case 0x6FF4: +case 0x6FF5: +case 0x6FF6: +case 0x6FF7: +case 0x6FF8: +case 0x6FF9: +case 0x6FFA: +case 0x6FFB: +case 0x6FFC: +case 0x6FFD: +case 0x6FFE: +case 0x6FFF: + +// BCC +case 0x6F01: +{ + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + PC += (s32)(s8)Opcode; + CCnt -= 2; + } +} +RET(8) + +// BCC16 +case 0x6200: +{ + if (CPU->flag_notZ && (!(CPU->flag_C & 0x100))) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6300: +{ + if ((!CPU->flag_notZ) || (CPU->flag_C & 0x100)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6400: +{ + if (!(CPU->flag_C & 0x100)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6500: +{ + if (CPU->flag_C & 0x100) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6600: +{ + if (CPU->flag_notZ) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6700: +{ + if (!CPU->flag_notZ) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6800: +{ + if (!(CPU->flag_V & 0x80)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6900: +{ + if (CPU->flag_V & 0x80) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6A00: +{ + if (!(CPU->flag_N & 0x80)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6B00: +{ + if (CPU->flag_N & 0x80) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6C00: +{ + if (!((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6D00: +{ + if ((CPU->flag_N ^ CPU->flag_V) & 0x80) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6E00: +{ + if (CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80))) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) + +// BCC16 +case 0x6F00: +{ + if ((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80)) + { + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); + RET(10) + } + PC += 2; +} +RET(12) +case 0x6002: +case 0x6003: +case 0x6004: +case 0x6005: +case 0x6006: +case 0x6007: +case 0x6008: +case 0x6009: +case 0x600A: +case 0x600B: +case 0x600C: +case 0x600D: +case 0x600E: +case 0x600F: +case 0x6010: +case 0x6011: +case 0x6012: +case 0x6013: +case 0x6014: +case 0x6015: +case 0x6016: +case 0x6017: +case 0x6018: +case 0x6019: +case 0x601A: +case 0x601B: +case 0x601C: +case 0x601D: +case 0x601E: +case 0x601F: +case 0x6020: +case 0x6021: +case 0x6022: +case 0x6023: +case 0x6024: +case 0x6025: +case 0x6026: +case 0x6027: +case 0x6028: +case 0x6029: +case 0x602A: +case 0x602B: +case 0x602C: +case 0x602D: +case 0x602E: +case 0x602F: +case 0x6030: +case 0x6031: +case 0x6032: +case 0x6033: +case 0x6034: +case 0x6035: +case 0x6036: +case 0x6037: +case 0x6038: +case 0x6039: +case 0x603A: +case 0x603B: +case 0x603C: +case 0x603D: +case 0x603E: +case 0x603F: +case 0x6040: +case 0x6041: +case 0x6042: +case 0x6043: +case 0x6044: +case 0x6045: +case 0x6046: +case 0x6047: +case 0x6048: +case 0x6049: +case 0x604A: +case 0x604B: +case 0x604C: +case 0x604D: +case 0x604E: +case 0x604F: +case 0x6050: +case 0x6051: +case 0x6052: +case 0x6053: +case 0x6054: +case 0x6055: +case 0x6056: +case 0x6057: +case 0x6058: +case 0x6059: +case 0x605A: +case 0x605B: +case 0x605C: +case 0x605D: +case 0x605E: +case 0x605F: +case 0x6060: +case 0x6061: +case 0x6062: +case 0x6063: +case 0x6064: +case 0x6065: +case 0x6066: +case 0x6067: +case 0x6068: +case 0x6069: +case 0x606A: +case 0x606B: +case 0x606C: +case 0x606D: +case 0x606E: +case 0x606F: +case 0x6070: +case 0x6071: +case 0x6072: +case 0x6073: +case 0x6074: +case 0x6075: +case 0x6076: +case 0x6077: +case 0x6078: +case 0x6079: +case 0x607A: +case 0x607B: +case 0x607C: +case 0x607D: +case 0x607E: +case 0x607F: +case 0x6080: +case 0x6081: +case 0x6082: +case 0x6083: +case 0x6084: +case 0x6085: +case 0x6086: +case 0x6087: +case 0x6088: +case 0x6089: +case 0x608A: +case 0x608B: +case 0x608C: +case 0x608D: +case 0x608E: +case 0x608F: +case 0x6090: +case 0x6091: +case 0x6092: +case 0x6093: +case 0x6094: +case 0x6095: +case 0x6096: +case 0x6097: +case 0x6098: +case 0x6099: +case 0x609A: +case 0x609B: +case 0x609C: +case 0x609D: +case 0x609E: +case 0x609F: +case 0x60A0: +case 0x60A1: +case 0x60A2: +case 0x60A3: +case 0x60A4: +case 0x60A5: +case 0x60A6: +case 0x60A7: +case 0x60A8: +case 0x60A9: +case 0x60AA: +case 0x60AB: +case 0x60AC: +case 0x60AD: +case 0x60AE: +case 0x60AF: +case 0x60B0: +case 0x60B1: +case 0x60B2: +case 0x60B3: +case 0x60B4: +case 0x60B5: +case 0x60B6: +case 0x60B7: +case 0x60B8: +case 0x60B9: +case 0x60BA: +case 0x60BB: +case 0x60BC: +case 0x60BD: +case 0x60BE: +case 0x60BF: +case 0x60C0: +case 0x60C1: +case 0x60C2: +case 0x60C3: +case 0x60C4: +case 0x60C5: +case 0x60C6: +case 0x60C7: +case 0x60C8: +case 0x60C9: +case 0x60CA: +case 0x60CB: +case 0x60CC: +case 0x60CD: +case 0x60CE: +case 0x60CF: +case 0x60D0: +case 0x60D1: +case 0x60D2: +case 0x60D3: +case 0x60D4: +case 0x60D5: +case 0x60D6: +case 0x60D7: +case 0x60D8: +case 0x60D9: +case 0x60DA: +case 0x60DB: +case 0x60DC: +case 0x60DD: +case 0x60DE: +case 0x60DF: +case 0x60E0: +case 0x60E1: +case 0x60E2: +case 0x60E3: +case 0x60E4: +case 0x60E5: +case 0x60E6: +case 0x60E7: +case 0x60E8: +case 0x60E9: +case 0x60EA: +case 0x60EB: +case 0x60EC: +case 0x60ED: +case 0x60EE: +case 0x60EF: +case 0x60F0: +case 0x60F1: +case 0x60F2: +case 0x60F3: +case 0x60F4: +case 0x60F5: +case 0x60F6: +case 0x60F7: +case 0x60F8: +case 0x60F9: +case 0x60FA: +case 0x60FB: +case 0x60FC: +case 0x60FD: +case 0x60FE: +case 0x60FF: + +// BRA +case 0x6001: +{ + PC += (s32)(s8)Opcode; +} +RET(10) + +// BRA16 +case 0x6000: +{ + PC += (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + SET_PC(PC); +} +RET(10) +case 0x6102: +case 0x6103: +case 0x6104: +case 0x6105: +case 0x6106: +case 0x6107: +case 0x6108: +case 0x6109: +case 0x610A: +case 0x610B: +case 0x610C: +case 0x610D: +case 0x610E: +case 0x610F: +case 0x6110: +case 0x6111: +case 0x6112: +case 0x6113: +case 0x6114: +case 0x6115: +case 0x6116: +case 0x6117: +case 0x6118: +case 0x6119: +case 0x611A: +case 0x611B: +case 0x611C: +case 0x611D: +case 0x611E: +case 0x611F: +case 0x6120: +case 0x6121: +case 0x6122: +case 0x6123: +case 0x6124: +case 0x6125: +case 0x6126: +case 0x6127: +case 0x6128: +case 0x6129: +case 0x612A: +case 0x612B: +case 0x612C: +case 0x612D: +case 0x612E: +case 0x612F: +case 0x6130: +case 0x6131: +case 0x6132: +case 0x6133: +case 0x6134: +case 0x6135: +case 0x6136: +case 0x6137: +case 0x6138: +case 0x6139: +case 0x613A: +case 0x613B: +case 0x613C: +case 0x613D: +case 0x613E: +case 0x613F: +case 0x6140: +case 0x6141: +case 0x6142: +case 0x6143: +case 0x6144: +case 0x6145: +case 0x6146: +case 0x6147: +case 0x6148: +case 0x6149: +case 0x614A: +case 0x614B: +case 0x614C: +case 0x614D: +case 0x614E: +case 0x614F: +case 0x6150: +case 0x6151: +case 0x6152: +case 0x6153: +case 0x6154: +case 0x6155: +case 0x6156: +case 0x6157: +case 0x6158: +case 0x6159: +case 0x615A: +case 0x615B: +case 0x615C: +case 0x615D: +case 0x615E: +case 0x615F: +case 0x6160: +case 0x6161: +case 0x6162: +case 0x6163: +case 0x6164: +case 0x6165: +case 0x6166: +case 0x6167: +case 0x6168: +case 0x6169: +case 0x616A: +case 0x616B: +case 0x616C: +case 0x616D: +case 0x616E: +case 0x616F: +case 0x6170: +case 0x6171: +case 0x6172: +case 0x6173: +case 0x6174: +case 0x6175: +case 0x6176: +case 0x6177: +case 0x6178: +case 0x6179: +case 0x617A: +case 0x617B: +case 0x617C: +case 0x617D: +case 0x617E: +case 0x617F: +case 0x6180: +case 0x6181: +case 0x6182: +case 0x6183: +case 0x6184: +case 0x6185: +case 0x6186: +case 0x6187: +case 0x6188: +case 0x6189: +case 0x618A: +case 0x618B: +case 0x618C: +case 0x618D: +case 0x618E: +case 0x618F: +case 0x6190: +case 0x6191: +case 0x6192: +case 0x6193: +case 0x6194: +case 0x6195: +case 0x6196: +case 0x6197: +case 0x6198: +case 0x6199: +case 0x619A: +case 0x619B: +case 0x619C: +case 0x619D: +case 0x619E: +case 0x619F: +case 0x61A0: +case 0x61A1: +case 0x61A2: +case 0x61A3: +case 0x61A4: +case 0x61A5: +case 0x61A6: +case 0x61A7: +case 0x61A8: +case 0x61A9: +case 0x61AA: +case 0x61AB: +case 0x61AC: +case 0x61AD: +case 0x61AE: +case 0x61AF: +case 0x61B0: +case 0x61B1: +case 0x61B2: +case 0x61B3: +case 0x61B4: +case 0x61B5: +case 0x61B6: +case 0x61B7: +case 0x61B8: +case 0x61B9: +case 0x61BA: +case 0x61BB: +case 0x61BC: +case 0x61BD: +case 0x61BE: +case 0x61BF: +case 0x61C0: +case 0x61C1: +case 0x61C2: +case 0x61C3: +case 0x61C4: +case 0x61C5: +case 0x61C6: +case 0x61C7: +case 0x61C8: +case 0x61C9: +case 0x61CA: +case 0x61CB: +case 0x61CC: +case 0x61CD: +case 0x61CE: +case 0x61CF: +case 0x61D0: +case 0x61D1: +case 0x61D2: +case 0x61D3: +case 0x61D4: +case 0x61D5: +case 0x61D6: +case 0x61D7: +case 0x61D8: +case 0x61D9: +case 0x61DA: +case 0x61DB: +case 0x61DC: +case 0x61DD: +case 0x61DE: +case 0x61DF: +case 0x61E0: +case 0x61E1: +case 0x61E2: +case 0x61E3: +case 0x61E4: +case 0x61E5: +case 0x61E6: +case 0x61E7: +case 0x61E8: +case 0x61E9: +case 0x61EA: +case 0x61EB: +case 0x61EC: +case 0x61ED: +case 0x61EE: +case 0x61EF: +case 0x61F0: +case 0x61F1: +case 0x61F2: +case 0x61F3: +case 0x61F4: +case 0x61F5: +case 0x61F6: +case 0x61F7: +case 0x61F8: +case 0x61F9: +case 0x61FA: +case 0x61FB: +case 0x61FC: +case 0x61FD: +case 0x61FE: +case 0x61FF: + +// BSR +case 0x6101: +{ + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PC += (s32)(s8)Opcode; + POST_IO +} +RET(18) + +// BSR16 +case 0x6100: +{ + u32 res; + res = (s32)(s16)FETCH_WORD; + PC -= CPU->BasePC; + PRE_IO + PUSH_32_F(PC + 2) + PC += (s32) res; + SET_PC(PC); + POST_IO +} +RET(18) diff --git a/yabause/src/c68k/c68k_op7.inc b/yabause/src/c68k/c68k_op7.inc new file mode 100644 index 0000000000..ba0b48ba31 --- /dev/null +++ b/yabause/src/c68k/c68k_op7.inc @@ -0,0 +1,2058 @@ +case 0x7001: +case 0x7002: +case 0x7003: +case 0x7004: +case 0x7005: +case 0x7006: +case 0x7007: +case 0x7008: +case 0x7009: +case 0x700A: +case 0x700B: +case 0x700C: +case 0x700D: +case 0x700E: +case 0x700F: +case 0x7010: +case 0x7011: +case 0x7012: +case 0x7013: +case 0x7014: +case 0x7015: +case 0x7016: +case 0x7017: +case 0x7018: +case 0x7019: +case 0x701A: +case 0x701B: +case 0x701C: +case 0x701D: +case 0x701E: +case 0x701F: +case 0x7020: +case 0x7021: +case 0x7022: +case 0x7023: +case 0x7024: +case 0x7025: +case 0x7026: +case 0x7027: +case 0x7028: +case 0x7029: +case 0x702A: +case 0x702B: +case 0x702C: +case 0x702D: +case 0x702E: +case 0x702F: +case 0x7030: +case 0x7031: +case 0x7032: +case 0x7033: +case 0x7034: +case 0x7035: +case 0x7036: +case 0x7037: +case 0x7038: +case 0x7039: +case 0x703A: +case 0x703B: +case 0x703C: +case 0x703D: +case 0x703E: +case 0x703F: +case 0x7040: +case 0x7041: +case 0x7042: +case 0x7043: +case 0x7044: +case 0x7045: +case 0x7046: +case 0x7047: +case 0x7048: +case 0x7049: +case 0x704A: +case 0x704B: +case 0x704C: +case 0x704D: +case 0x704E: +case 0x704F: +case 0x7050: +case 0x7051: +case 0x7052: +case 0x7053: +case 0x7054: +case 0x7055: +case 0x7056: +case 0x7057: +case 0x7058: +case 0x7059: +case 0x705A: +case 0x705B: +case 0x705C: +case 0x705D: +case 0x705E: +case 0x705F: +case 0x7060: +case 0x7061: +case 0x7062: +case 0x7063: +case 0x7064: +case 0x7065: +case 0x7066: +case 0x7067: +case 0x7068: +case 0x7069: +case 0x706A: +case 0x706B: +case 0x706C: +case 0x706D: +case 0x706E: +case 0x706F: +case 0x7070: +case 0x7071: +case 0x7072: +case 0x7073: +case 0x7074: +case 0x7075: +case 0x7076: +case 0x7077: +case 0x7078: +case 0x7079: +case 0x707A: +case 0x707B: +case 0x707C: +case 0x707D: +case 0x707E: +case 0x707F: +case 0x7080: +case 0x7081: +case 0x7082: +case 0x7083: +case 0x7084: +case 0x7085: +case 0x7086: +case 0x7087: +case 0x7088: +case 0x7089: +case 0x708A: +case 0x708B: +case 0x708C: +case 0x708D: +case 0x708E: +case 0x708F: +case 0x7090: +case 0x7091: +case 0x7092: +case 0x7093: +case 0x7094: +case 0x7095: +case 0x7096: +case 0x7097: +case 0x7098: +case 0x7099: +case 0x709A: +case 0x709B: +case 0x709C: +case 0x709D: +case 0x709E: +case 0x709F: +case 0x70A0: +case 0x70A1: +case 0x70A2: +case 0x70A3: +case 0x70A4: +case 0x70A5: +case 0x70A6: +case 0x70A7: +case 0x70A8: +case 0x70A9: +case 0x70AA: +case 0x70AB: +case 0x70AC: +case 0x70AD: +case 0x70AE: +case 0x70AF: +case 0x70B0: +case 0x70B1: +case 0x70B2: +case 0x70B3: +case 0x70B4: +case 0x70B5: +case 0x70B6: +case 0x70B7: +case 0x70B8: +case 0x70B9: +case 0x70BA: +case 0x70BB: +case 0x70BC: +case 0x70BD: +case 0x70BE: +case 0x70BF: +case 0x70C0: +case 0x70C1: +case 0x70C2: +case 0x70C3: +case 0x70C4: +case 0x70C5: +case 0x70C6: +case 0x70C7: +case 0x70C8: +case 0x70C9: +case 0x70CA: +case 0x70CB: +case 0x70CC: +case 0x70CD: +case 0x70CE: +case 0x70CF: +case 0x70D0: +case 0x70D1: +case 0x70D2: +case 0x70D3: +case 0x70D4: +case 0x70D5: +case 0x70D6: +case 0x70D7: +case 0x70D8: +case 0x70D9: +case 0x70DA: +case 0x70DB: +case 0x70DC: +case 0x70DD: +case 0x70DE: +case 0x70DF: +case 0x70E0: +case 0x70E1: +case 0x70E2: +case 0x70E3: +case 0x70E4: +case 0x70E5: +case 0x70E6: +case 0x70E7: +case 0x70E8: +case 0x70E9: +case 0x70EA: +case 0x70EB: +case 0x70EC: +case 0x70ED: +case 0x70EE: +case 0x70EF: +case 0x70F0: +case 0x70F1: +case 0x70F2: +case 0x70F3: +case 0x70F4: +case 0x70F5: +case 0x70F6: +case 0x70F7: +case 0x70F8: +case 0x70F9: +case 0x70FA: +case 0x70FB: +case 0x70FC: +case 0x70FD: +case 0x70FE: +case 0x70FF: +case 0x7200: +case 0x7201: +case 0x7202: +case 0x7203: +case 0x7204: +case 0x7205: +case 0x7206: +case 0x7207: +case 0x7208: +case 0x7209: +case 0x720A: +case 0x720B: +case 0x720C: +case 0x720D: +case 0x720E: +case 0x720F: +case 0x7210: +case 0x7211: +case 0x7212: +case 0x7213: +case 0x7214: +case 0x7215: +case 0x7216: +case 0x7217: +case 0x7218: +case 0x7219: +case 0x721A: +case 0x721B: +case 0x721C: +case 0x721D: +case 0x721E: +case 0x721F: +case 0x7220: +case 0x7221: +case 0x7222: +case 0x7223: +case 0x7224: +case 0x7225: +case 0x7226: +case 0x7227: +case 0x7228: +case 0x7229: +case 0x722A: +case 0x722B: +case 0x722C: +case 0x722D: +case 0x722E: +case 0x722F: +case 0x7230: +case 0x7231: +case 0x7232: +case 0x7233: +case 0x7234: +case 0x7235: +case 0x7236: +case 0x7237: +case 0x7238: +case 0x7239: +case 0x723A: +case 0x723B: +case 0x723C: +case 0x723D: +case 0x723E: +case 0x723F: +case 0x7240: +case 0x7241: +case 0x7242: +case 0x7243: +case 0x7244: +case 0x7245: +case 0x7246: +case 0x7247: +case 0x7248: +case 0x7249: +case 0x724A: +case 0x724B: +case 0x724C: +case 0x724D: +case 0x724E: +case 0x724F: +case 0x7250: +case 0x7251: +case 0x7252: +case 0x7253: +case 0x7254: +case 0x7255: +case 0x7256: +case 0x7257: +case 0x7258: +case 0x7259: +case 0x725A: +case 0x725B: +case 0x725C: +case 0x725D: +case 0x725E: +case 0x725F: +case 0x7260: +case 0x7261: +case 0x7262: +case 0x7263: +case 0x7264: +case 0x7265: +case 0x7266: +case 0x7267: +case 0x7268: +case 0x7269: +case 0x726A: +case 0x726B: +case 0x726C: +case 0x726D: +case 0x726E: +case 0x726F: +case 0x7270: +case 0x7271: +case 0x7272: +case 0x7273: +case 0x7274: +case 0x7275: +case 0x7276: +case 0x7277: +case 0x7278: +case 0x7279: +case 0x727A: +case 0x727B: +case 0x727C: +case 0x727D: +case 0x727E: +case 0x727F: +case 0x7280: +case 0x7281: +case 0x7282: +case 0x7283: +case 0x7284: +case 0x7285: +case 0x7286: +case 0x7287: +case 0x7288: +case 0x7289: +case 0x728A: +case 0x728B: +case 0x728C: +case 0x728D: +case 0x728E: +case 0x728F: +case 0x7290: +case 0x7291: +case 0x7292: +case 0x7293: +case 0x7294: +case 0x7295: +case 0x7296: +case 0x7297: +case 0x7298: +case 0x7299: +case 0x729A: +case 0x729B: +case 0x729C: +case 0x729D: +case 0x729E: +case 0x729F: +case 0x72A0: +case 0x72A1: +case 0x72A2: +case 0x72A3: +case 0x72A4: +case 0x72A5: +case 0x72A6: +case 0x72A7: +case 0x72A8: +case 0x72A9: +case 0x72AA: +case 0x72AB: +case 0x72AC: +case 0x72AD: +case 0x72AE: +case 0x72AF: +case 0x72B0: +case 0x72B1: +case 0x72B2: +case 0x72B3: +case 0x72B4: +case 0x72B5: +case 0x72B6: +case 0x72B7: +case 0x72B8: +case 0x72B9: +case 0x72BA: +case 0x72BB: +case 0x72BC: +case 0x72BD: +case 0x72BE: +case 0x72BF: +case 0x72C0: +case 0x72C1: +case 0x72C2: +case 0x72C3: +case 0x72C4: +case 0x72C5: +case 0x72C6: +case 0x72C7: +case 0x72C8: +case 0x72C9: +case 0x72CA: +case 0x72CB: +case 0x72CC: +case 0x72CD: +case 0x72CE: +case 0x72CF: +case 0x72D0: +case 0x72D1: +case 0x72D2: +case 0x72D3: +case 0x72D4: +case 0x72D5: +case 0x72D6: +case 0x72D7: +case 0x72D8: +case 0x72D9: +case 0x72DA: +case 0x72DB: +case 0x72DC: +case 0x72DD: +case 0x72DE: +case 0x72DF: +case 0x72E0: +case 0x72E1: +case 0x72E2: +case 0x72E3: +case 0x72E4: +case 0x72E5: +case 0x72E6: +case 0x72E7: +case 0x72E8: +case 0x72E9: +case 0x72EA: +case 0x72EB: +case 0x72EC: +case 0x72ED: +case 0x72EE: +case 0x72EF: +case 0x72F0: +case 0x72F1: +case 0x72F2: +case 0x72F3: +case 0x72F4: +case 0x72F5: +case 0x72F6: +case 0x72F7: +case 0x72F8: +case 0x72F9: +case 0x72FA: +case 0x72FB: +case 0x72FC: +case 0x72FD: +case 0x72FE: +case 0x72FF: +case 0x7400: +case 0x7401: +case 0x7402: +case 0x7403: +case 0x7404: +case 0x7405: +case 0x7406: +case 0x7407: +case 0x7408: +case 0x7409: +case 0x740A: +case 0x740B: +case 0x740C: +case 0x740D: +case 0x740E: +case 0x740F: +case 0x7410: +case 0x7411: +case 0x7412: +case 0x7413: +case 0x7414: +case 0x7415: +case 0x7416: +case 0x7417: +case 0x7418: +case 0x7419: +case 0x741A: +case 0x741B: +case 0x741C: +case 0x741D: +case 0x741E: +case 0x741F: +case 0x7420: +case 0x7421: +case 0x7422: +case 0x7423: +case 0x7424: +case 0x7425: +case 0x7426: +case 0x7427: +case 0x7428: +case 0x7429: +case 0x742A: +case 0x742B: +case 0x742C: +case 0x742D: +case 0x742E: +case 0x742F: +case 0x7430: +case 0x7431: +case 0x7432: +case 0x7433: +case 0x7434: +case 0x7435: +case 0x7436: +case 0x7437: +case 0x7438: +case 0x7439: +case 0x743A: +case 0x743B: +case 0x743C: +case 0x743D: +case 0x743E: +case 0x743F: +case 0x7440: +case 0x7441: +case 0x7442: +case 0x7443: +case 0x7444: +case 0x7445: +case 0x7446: +case 0x7447: +case 0x7448: +case 0x7449: +case 0x744A: +case 0x744B: +case 0x744C: +case 0x744D: +case 0x744E: +case 0x744F: +case 0x7450: +case 0x7451: +case 0x7452: +case 0x7453: +case 0x7454: +case 0x7455: +case 0x7456: +case 0x7457: +case 0x7458: +case 0x7459: +case 0x745A: +case 0x745B: +case 0x745C: +case 0x745D: +case 0x745E: +case 0x745F: +case 0x7460: +case 0x7461: +case 0x7462: +case 0x7463: +case 0x7464: +case 0x7465: +case 0x7466: +case 0x7467: +case 0x7468: +case 0x7469: +case 0x746A: +case 0x746B: +case 0x746C: +case 0x746D: +case 0x746E: +case 0x746F: +case 0x7470: +case 0x7471: +case 0x7472: +case 0x7473: +case 0x7474: +case 0x7475: +case 0x7476: +case 0x7477: +case 0x7478: +case 0x7479: +case 0x747A: +case 0x747B: +case 0x747C: +case 0x747D: +case 0x747E: +case 0x747F: +case 0x7480: +case 0x7481: +case 0x7482: +case 0x7483: +case 0x7484: +case 0x7485: +case 0x7486: +case 0x7487: +case 0x7488: +case 0x7489: +case 0x748A: +case 0x748B: +case 0x748C: +case 0x748D: +case 0x748E: +case 0x748F: +case 0x7490: +case 0x7491: +case 0x7492: +case 0x7493: +case 0x7494: +case 0x7495: +case 0x7496: +case 0x7497: +case 0x7498: +case 0x7499: +case 0x749A: +case 0x749B: +case 0x749C: +case 0x749D: +case 0x749E: +case 0x749F: +case 0x74A0: +case 0x74A1: +case 0x74A2: +case 0x74A3: +case 0x74A4: +case 0x74A5: +case 0x74A6: +case 0x74A7: +case 0x74A8: +case 0x74A9: +case 0x74AA: +case 0x74AB: +case 0x74AC: +case 0x74AD: +case 0x74AE: +case 0x74AF: +case 0x74B0: +case 0x74B1: +case 0x74B2: +case 0x74B3: +case 0x74B4: +case 0x74B5: +case 0x74B6: +case 0x74B7: +case 0x74B8: +case 0x74B9: +case 0x74BA: +case 0x74BB: +case 0x74BC: +case 0x74BD: +case 0x74BE: +case 0x74BF: +case 0x74C0: +case 0x74C1: +case 0x74C2: +case 0x74C3: +case 0x74C4: +case 0x74C5: +case 0x74C6: +case 0x74C7: +case 0x74C8: +case 0x74C9: +case 0x74CA: +case 0x74CB: +case 0x74CC: +case 0x74CD: +case 0x74CE: +case 0x74CF: +case 0x74D0: +case 0x74D1: +case 0x74D2: +case 0x74D3: +case 0x74D4: +case 0x74D5: +case 0x74D6: +case 0x74D7: +case 0x74D8: +case 0x74D9: +case 0x74DA: +case 0x74DB: +case 0x74DC: +case 0x74DD: +case 0x74DE: +case 0x74DF: +case 0x74E0: +case 0x74E1: +case 0x74E2: +case 0x74E3: +case 0x74E4: +case 0x74E5: +case 0x74E6: +case 0x74E7: +case 0x74E8: +case 0x74E9: +case 0x74EA: +case 0x74EB: +case 0x74EC: +case 0x74ED: +case 0x74EE: +case 0x74EF: +case 0x74F0: +case 0x74F1: +case 0x74F2: +case 0x74F3: +case 0x74F4: +case 0x74F5: +case 0x74F6: +case 0x74F7: +case 0x74F8: +case 0x74F9: +case 0x74FA: +case 0x74FB: +case 0x74FC: +case 0x74FD: +case 0x74FE: +case 0x74FF: +case 0x7600: +case 0x7601: +case 0x7602: +case 0x7603: +case 0x7604: +case 0x7605: +case 0x7606: +case 0x7607: +case 0x7608: +case 0x7609: +case 0x760A: +case 0x760B: +case 0x760C: +case 0x760D: +case 0x760E: +case 0x760F: +case 0x7610: +case 0x7611: +case 0x7612: +case 0x7613: +case 0x7614: +case 0x7615: +case 0x7616: +case 0x7617: +case 0x7618: +case 0x7619: +case 0x761A: +case 0x761B: +case 0x761C: +case 0x761D: +case 0x761E: +case 0x761F: +case 0x7620: +case 0x7621: +case 0x7622: +case 0x7623: +case 0x7624: +case 0x7625: +case 0x7626: +case 0x7627: +case 0x7628: +case 0x7629: +case 0x762A: +case 0x762B: +case 0x762C: +case 0x762D: +case 0x762E: +case 0x762F: +case 0x7630: +case 0x7631: +case 0x7632: +case 0x7633: +case 0x7634: +case 0x7635: +case 0x7636: +case 0x7637: +case 0x7638: +case 0x7639: +case 0x763A: +case 0x763B: +case 0x763C: +case 0x763D: +case 0x763E: +case 0x763F: +case 0x7640: +case 0x7641: +case 0x7642: +case 0x7643: +case 0x7644: +case 0x7645: +case 0x7646: +case 0x7647: +case 0x7648: +case 0x7649: +case 0x764A: +case 0x764B: +case 0x764C: +case 0x764D: +case 0x764E: +case 0x764F: +case 0x7650: +case 0x7651: +case 0x7652: +case 0x7653: +case 0x7654: +case 0x7655: +case 0x7656: +case 0x7657: +case 0x7658: +case 0x7659: +case 0x765A: +case 0x765B: +case 0x765C: +case 0x765D: +case 0x765E: +case 0x765F: +case 0x7660: +case 0x7661: +case 0x7662: +case 0x7663: +case 0x7664: +case 0x7665: +case 0x7666: +case 0x7667: +case 0x7668: +case 0x7669: +case 0x766A: +case 0x766B: +case 0x766C: +case 0x766D: +case 0x766E: +case 0x766F: +case 0x7670: +case 0x7671: +case 0x7672: +case 0x7673: +case 0x7674: +case 0x7675: +case 0x7676: +case 0x7677: +case 0x7678: +case 0x7679: +case 0x767A: +case 0x767B: +case 0x767C: +case 0x767D: +case 0x767E: +case 0x767F: +case 0x7680: +case 0x7681: +case 0x7682: +case 0x7683: +case 0x7684: +case 0x7685: +case 0x7686: +case 0x7687: +case 0x7688: +case 0x7689: +case 0x768A: +case 0x768B: +case 0x768C: +case 0x768D: +case 0x768E: +case 0x768F: +case 0x7690: +case 0x7691: +case 0x7692: +case 0x7693: +case 0x7694: +case 0x7695: +case 0x7696: +case 0x7697: +case 0x7698: +case 0x7699: +case 0x769A: +case 0x769B: +case 0x769C: +case 0x769D: +case 0x769E: +case 0x769F: +case 0x76A0: +case 0x76A1: +case 0x76A2: +case 0x76A3: +case 0x76A4: +case 0x76A5: +case 0x76A6: +case 0x76A7: +case 0x76A8: +case 0x76A9: +case 0x76AA: +case 0x76AB: +case 0x76AC: +case 0x76AD: +case 0x76AE: +case 0x76AF: +case 0x76B0: +case 0x76B1: +case 0x76B2: +case 0x76B3: +case 0x76B4: +case 0x76B5: +case 0x76B6: +case 0x76B7: +case 0x76B8: +case 0x76B9: +case 0x76BA: +case 0x76BB: +case 0x76BC: +case 0x76BD: +case 0x76BE: +case 0x76BF: +case 0x76C0: +case 0x76C1: +case 0x76C2: +case 0x76C3: +case 0x76C4: +case 0x76C5: +case 0x76C6: +case 0x76C7: +case 0x76C8: +case 0x76C9: +case 0x76CA: +case 0x76CB: +case 0x76CC: +case 0x76CD: +case 0x76CE: +case 0x76CF: +case 0x76D0: +case 0x76D1: +case 0x76D2: +case 0x76D3: +case 0x76D4: +case 0x76D5: +case 0x76D6: +case 0x76D7: +case 0x76D8: +case 0x76D9: +case 0x76DA: +case 0x76DB: +case 0x76DC: +case 0x76DD: +case 0x76DE: +case 0x76DF: +case 0x76E0: +case 0x76E1: +case 0x76E2: +case 0x76E3: +case 0x76E4: +case 0x76E5: +case 0x76E6: +case 0x76E7: +case 0x76E8: +case 0x76E9: +case 0x76EA: +case 0x76EB: +case 0x76EC: +case 0x76ED: +case 0x76EE: +case 0x76EF: +case 0x76F0: +case 0x76F1: +case 0x76F2: +case 0x76F3: +case 0x76F4: +case 0x76F5: +case 0x76F6: +case 0x76F7: +case 0x76F8: +case 0x76F9: +case 0x76FA: +case 0x76FB: +case 0x76FC: +case 0x76FD: +case 0x76FE: +case 0x76FF: +case 0x7800: +case 0x7801: +case 0x7802: +case 0x7803: +case 0x7804: +case 0x7805: +case 0x7806: +case 0x7807: +case 0x7808: +case 0x7809: +case 0x780A: +case 0x780B: +case 0x780C: +case 0x780D: +case 0x780E: +case 0x780F: +case 0x7810: +case 0x7811: +case 0x7812: +case 0x7813: +case 0x7814: +case 0x7815: +case 0x7816: +case 0x7817: +case 0x7818: +case 0x7819: +case 0x781A: +case 0x781B: +case 0x781C: +case 0x781D: +case 0x781E: +case 0x781F: +case 0x7820: +case 0x7821: +case 0x7822: +case 0x7823: +case 0x7824: +case 0x7825: +case 0x7826: +case 0x7827: +case 0x7828: +case 0x7829: +case 0x782A: +case 0x782B: +case 0x782C: +case 0x782D: +case 0x782E: +case 0x782F: +case 0x7830: +case 0x7831: +case 0x7832: +case 0x7833: +case 0x7834: +case 0x7835: +case 0x7836: +case 0x7837: +case 0x7838: +case 0x7839: +case 0x783A: +case 0x783B: +case 0x783C: +case 0x783D: +case 0x783E: +case 0x783F: +case 0x7840: +case 0x7841: +case 0x7842: +case 0x7843: +case 0x7844: +case 0x7845: +case 0x7846: +case 0x7847: +case 0x7848: +case 0x7849: +case 0x784A: +case 0x784B: +case 0x784C: +case 0x784D: +case 0x784E: +case 0x784F: +case 0x7850: +case 0x7851: +case 0x7852: +case 0x7853: +case 0x7854: +case 0x7855: +case 0x7856: +case 0x7857: +case 0x7858: +case 0x7859: +case 0x785A: +case 0x785B: +case 0x785C: +case 0x785D: +case 0x785E: +case 0x785F: +case 0x7860: +case 0x7861: +case 0x7862: +case 0x7863: +case 0x7864: +case 0x7865: +case 0x7866: +case 0x7867: +case 0x7868: +case 0x7869: +case 0x786A: +case 0x786B: +case 0x786C: +case 0x786D: +case 0x786E: +case 0x786F: +case 0x7870: +case 0x7871: +case 0x7872: +case 0x7873: +case 0x7874: +case 0x7875: +case 0x7876: +case 0x7877: +case 0x7878: +case 0x7879: +case 0x787A: +case 0x787B: +case 0x787C: +case 0x787D: +case 0x787E: +case 0x787F: +case 0x7880: +case 0x7881: +case 0x7882: +case 0x7883: +case 0x7884: +case 0x7885: +case 0x7886: +case 0x7887: +case 0x7888: +case 0x7889: +case 0x788A: +case 0x788B: +case 0x788C: +case 0x788D: +case 0x788E: +case 0x788F: +case 0x7890: +case 0x7891: +case 0x7892: +case 0x7893: +case 0x7894: +case 0x7895: +case 0x7896: +case 0x7897: +case 0x7898: +case 0x7899: +case 0x789A: +case 0x789B: +case 0x789C: +case 0x789D: +case 0x789E: +case 0x789F: +case 0x78A0: +case 0x78A1: +case 0x78A2: +case 0x78A3: +case 0x78A4: +case 0x78A5: +case 0x78A6: +case 0x78A7: +case 0x78A8: +case 0x78A9: +case 0x78AA: +case 0x78AB: +case 0x78AC: +case 0x78AD: +case 0x78AE: +case 0x78AF: +case 0x78B0: +case 0x78B1: +case 0x78B2: +case 0x78B3: +case 0x78B4: +case 0x78B5: +case 0x78B6: +case 0x78B7: +case 0x78B8: +case 0x78B9: +case 0x78BA: +case 0x78BB: +case 0x78BC: +case 0x78BD: +case 0x78BE: +case 0x78BF: +case 0x78C0: +case 0x78C1: +case 0x78C2: +case 0x78C3: +case 0x78C4: +case 0x78C5: +case 0x78C6: +case 0x78C7: +case 0x78C8: +case 0x78C9: +case 0x78CA: +case 0x78CB: +case 0x78CC: +case 0x78CD: +case 0x78CE: +case 0x78CF: +case 0x78D0: +case 0x78D1: +case 0x78D2: +case 0x78D3: +case 0x78D4: +case 0x78D5: +case 0x78D6: +case 0x78D7: +case 0x78D8: +case 0x78D9: +case 0x78DA: +case 0x78DB: +case 0x78DC: +case 0x78DD: +case 0x78DE: +case 0x78DF: +case 0x78E0: +case 0x78E1: +case 0x78E2: +case 0x78E3: +case 0x78E4: +case 0x78E5: +case 0x78E6: +case 0x78E7: +case 0x78E8: +case 0x78E9: +case 0x78EA: +case 0x78EB: +case 0x78EC: +case 0x78ED: +case 0x78EE: +case 0x78EF: +case 0x78F0: +case 0x78F1: +case 0x78F2: +case 0x78F3: +case 0x78F4: +case 0x78F5: +case 0x78F6: +case 0x78F7: +case 0x78F8: +case 0x78F9: +case 0x78FA: +case 0x78FB: +case 0x78FC: +case 0x78FD: +case 0x78FE: +case 0x78FF: +case 0x7A00: +case 0x7A01: +case 0x7A02: +case 0x7A03: +case 0x7A04: +case 0x7A05: +case 0x7A06: +case 0x7A07: +case 0x7A08: +case 0x7A09: +case 0x7A0A: +case 0x7A0B: +case 0x7A0C: +case 0x7A0D: +case 0x7A0E: +case 0x7A0F: +case 0x7A10: +case 0x7A11: +case 0x7A12: +case 0x7A13: +case 0x7A14: +case 0x7A15: +case 0x7A16: +case 0x7A17: +case 0x7A18: +case 0x7A19: +case 0x7A1A: +case 0x7A1B: +case 0x7A1C: +case 0x7A1D: +case 0x7A1E: +case 0x7A1F: +case 0x7A20: +case 0x7A21: +case 0x7A22: +case 0x7A23: +case 0x7A24: +case 0x7A25: +case 0x7A26: +case 0x7A27: +case 0x7A28: +case 0x7A29: +case 0x7A2A: +case 0x7A2B: +case 0x7A2C: +case 0x7A2D: +case 0x7A2E: +case 0x7A2F: +case 0x7A30: +case 0x7A31: +case 0x7A32: +case 0x7A33: +case 0x7A34: +case 0x7A35: +case 0x7A36: +case 0x7A37: +case 0x7A38: +case 0x7A39: +case 0x7A3A: +case 0x7A3B: +case 0x7A3C: +case 0x7A3D: +case 0x7A3E: +case 0x7A3F: +case 0x7A40: +case 0x7A41: +case 0x7A42: +case 0x7A43: +case 0x7A44: +case 0x7A45: +case 0x7A46: +case 0x7A47: +case 0x7A48: +case 0x7A49: +case 0x7A4A: +case 0x7A4B: +case 0x7A4C: +case 0x7A4D: +case 0x7A4E: +case 0x7A4F: +case 0x7A50: +case 0x7A51: +case 0x7A52: +case 0x7A53: +case 0x7A54: +case 0x7A55: +case 0x7A56: +case 0x7A57: +case 0x7A58: +case 0x7A59: +case 0x7A5A: +case 0x7A5B: +case 0x7A5C: +case 0x7A5D: +case 0x7A5E: +case 0x7A5F: +case 0x7A60: +case 0x7A61: +case 0x7A62: +case 0x7A63: +case 0x7A64: +case 0x7A65: +case 0x7A66: +case 0x7A67: +case 0x7A68: +case 0x7A69: +case 0x7A6A: +case 0x7A6B: +case 0x7A6C: +case 0x7A6D: +case 0x7A6E: +case 0x7A6F: +case 0x7A70: +case 0x7A71: +case 0x7A72: +case 0x7A73: +case 0x7A74: +case 0x7A75: +case 0x7A76: +case 0x7A77: +case 0x7A78: +case 0x7A79: +case 0x7A7A: +case 0x7A7B: +case 0x7A7C: +case 0x7A7D: +case 0x7A7E: +case 0x7A7F: +case 0x7A80: +case 0x7A81: +case 0x7A82: +case 0x7A83: +case 0x7A84: +case 0x7A85: +case 0x7A86: +case 0x7A87: +case 0x7A88: +case 0x7A89: +case 0x7A8A: +case 0x7A8B: +case 0x7A8C: +case 0x7A8D: +case 0x7A8E: +case 0x7A8F: +case 0x7A90: +case 0x7A91: +case 0x7A92: +case 0x7A93: +case 0x7A94: +case 0x7A95: +case 0x7A96: +case 0x7A97: +case 0x7A98: +case 0x7A99: +case 0x7A9A: +case 0x7A9B: +case 0x7A9C: +case 0x7A9D: +case 0x7A9E: +case 0x7A9F: +case 0x7AA0: +case 0x7AA1: +case 0x7AA2: +case 0x7AA3: +case 0x7AA4: +case 0x7AA5: +case 0x7AA6: +case 0x7AA7: +case 0x7AA8: +case 0x7AA9: +case 0x7AAA: +case 0x7AAB: +case 0x7AAC: +case 0x7AAD: +case 0x7AAE: +case 0x7AAF: +case 0x7AB0: +case 0x7AB1: +case 0x7AB2: +case 0x7AB3: +case 0x7AB4: +case 0x7AB5: +case 0x7AB6: +case 0x7AB7: +case 0x7AB8: +case 0x7AB9: +case 0x7ABA: +case 0x7ABB: +case 0x7ABC: +case 0x7ABD: +case 0x7ABE: +case 0x7ABF: +case 0x7AC0: +case 0x7AC1: +case 0x7AC2: +case 0x7AC3: +case 0x7AC4: +case 0x7AC5: +case 0x7AC6: +case 0x7AC7: +case 0x7AC8: +case 0x7AC9: +case 0x7ACA: +case 0x7ACB: +case 0x7ACC: +case 0x7ACD: +case 0x7ACE: +case 0x7ACF: +case 0x7AD0: +case 0x7AD1: +case 0x7AD2: +case 0x7AD3: +case 0x7AD4: +case 0x7AD5: +case 0x7AD6: +case 0x7AD7: +case 0x7AD8: +case 0x7AD9: +case 0x7ADA: +case 0x7ADB: +case 0x7ADC: +case 0x7ADD: +case 0x7ADE: +case 0x7ADF: +case 0x7AE0: +case 0x7AE1: +case 0x7AE2: +case 0x7AE3: +case 0x7AE4: +case 0x7AE5: +case 0x7AE6: +case 0x7AE7: +case 0x7AE8: +case 0x7AE9: +case 0x7AEA: +case 0x7AEB: +case 0x7AEC: +case 0x7AED: +case 0x7AEE: +case 0x7AEF: +case 0x7AF0: +case 0x7AF1: +case 0x7AF2: +case 0x7AF3: +case 0x7AF4: +case 0x7AF5: +case 0x7AF6: +case 0x7AF7: +case 0x7AF8: +case 0x7AF9: +case 0x7AFA: +case 0x7AFB: +case 0x7AFC: +case 0x7AFD: +case 0x7AFE: +case 0x7AFF: +case 0x7C00: +case 0x7C01: +case 0x7C02: +case 0x7C03: +case 0x7C04: +case 0x7C05: +case 0x7C06: +case 0x7C07: +case 0x7C08: +case 0x7C09: +case 0x7C0A: +case 0x7C0B: +case 0x7C0C: +case 0x7C0D: +case 0x7C0E: +case 0x7C0F: +case 0x7C10: +case 0x7C11: +case 0x7C12: +case 0x7C13: +case 0x7C14: +case 0x7C15: +case 0x7C16: +case 0x7C17: +case 0x7C18: +case 0x7C19: +case 0x7C1A: +case 0x7C1B: +case 0x7C1C: +case 0x7C1D: +case 0x7C1E: +case 0x7C1F: +case 0x7C20: +case 0x7C21: +case 0x7C22: +case 0x7C23: +case 0x7C24: +case 0x7C25: +case 0x7C26: +case 0x7C27: +case 0x7C28: +case 0x7C29: +case 0x7C2A: +case 0x7C2B: +case 0x7C2C: +case 0x7C2D: +case 0x7C2E: +case 0x7C2F: +case 0x7C30: +case 0x7C31: +case 0x7C32: +case 0x7C33: +case 0x7C34: +case 0x7C35: +case 0x7C36: +case 0x7C37: +case 0x7C38: +case 0x7C39: +case 0x7C3A: +case 0x7C3B: +case 0x7C3C: +case 0x7C3D: +case 0x7C3E: +case 0x7C3F: +case 0x7C40: +case 0x7C41: +case 0x7C42: +case 0x7C43: +case 0x7C44: +case 0x7C45: +case 0x7C46: +case 0x7C47: +case 0x7C48: +case 0x7C49: +case 0x7C4A: +case 0x7C4B: +case 0x7C4C: +case 0x7C4D: +case 0x7C4E: +case 0x7C4F: +case 0x7C50: +case 0x7C51: +case 0x7C52: +case 0x7C53: +case 0x7C54: +case 0x7C55: +case 0x7C56: +case 0x7C57: +case 0x7C58: +case 0x7C59: +case 0x7C5A: +case 0x7C5B: +case 0x7C5C: +case 0x7C5D: +case 0x7C5E: +case 0x7C5F: +case 0x7C60: +case 0x7C61: +case 0x7C62: +case 0x7C63: +case 0x7C64: +case 0x7C65: +case 0x7C66: +case 0x7C67: +case 0x7C68: +case 0x7C69: +case 0x7C6A: +case 0x7C6B: +case 0x7C6C: +case 0x7C6D: +case 0x7C6E: +case 0x7C6F: +case 0x7C70: +case 0x7C71: +case 0x7C72: +case 0x7C73: +case 0x7C74: +case 0x7C75: +case 0x7C76: +case 0x7C77: +case 0x7C78: +case 0x7C79: +case 0x7C7A: +case 0x7C7B: +case 0x7C7C: +case 0x7C7D: +case 0x7C7E: +case 0x7C7F: +case 0x7C80: +case 0x7C81: +case 0x7C82: +case 0x7C83: +case 0x7C84: +case 0x7C85: +case 0x7C86: +case 0x7C87: +case 0x7C88: +case 0x7C89: +case 0x7C8A: +case 0x7C8B: +case 0x7C8C: +case 0x7C8D: +case 0x7C8E: +case 0x7C8F: +case 0x7C90: +case 0x7C91: +case 0x7C92: +case 0x7C93: +case 0x7C94: +case 0x7C95: +case 0x7C96: +case 0x7C97: +case 0x7C98: +case 0x7C99: +case 0x7C9A: +case 0x7C9B: +case 0x7C9C: +case 0x7C9D: +case 0x7C9E: +case 0x7C9F: +case 0x7CA0: +case 0x7CA1: +case 0x7CA2: +case 0x7CA3: +case 0x7CA4: +case 0x7CA5: +case 0x7CA6: +case 0x7CA7: +case 0x7CA8: +case 0x7CA9: +case 0x7CAA: +case 0x7CAB: +case 0x7CAC: +case 0x7CAD: +case 0x7CAE: +case 0x7CAF: +case 0x7CB0: +case 0x7CB1: +case 0x7CB2: +case 0x7CB3: +case 0x7CB4: +case 0x7CB5: +case 0x7CB6: +case 0x7CB7: +case 0x7CB8: +case 0x7CB9: +case 0x7CBA: +case 0x7CBB: +case 0x7CBC: +case 0x7CBD: +case 0x7CBE: +case 0x7CBF: +case 0x7CC0: +case 0x7CC1: +case 0x7CC2: +case 0x7CC3: +case 0x7CC4: +case 0x7CC5: +case 0x7CC6: +case 0x7CC7: +case 0x7CC8: +case 0x7CC9: +case 0x7CCA: +case 0x7CCB: +case 0x7CCC: +case 0x7CCD: +case 0x7CCE: +case 0x7CCF: +case 0x7CD0: +case 0x7CD1: +case 0x7CD2: +case 0x7CD3: +case 0x7CD4: +case 0x7CD5: +case 0x7CD6: +case 0x7CD7: +case 0x7CD8: +case 0x7CD9: +case 0x7CDA: +case 0x7CDB: +case 0x7CDC: +case 0x7CDD: +case 0x7CDE: +case 0x7CDF: +case 0x7CE0: +case 0x7CE1: +case 0x7CE2: +case 0x7CE3: +case 0x7CE4: +case 0x7CE5: +case 0x7CE6: +case 0x7CE7: +case 0x7CE8: +case 0x7CE9: +case 0x7CEA: +case 0x7CEB: +case 0x7CEC: +case 0x7CED: +case 0x7CEE: +case 0x7CEF: +case 0x7CF0: +case 0x7CF1: +case 0x7CF2: +case 0x7CF3: +case 0x7CF4: +case 0x7CF5: +case 0x7CF6: +case 0x7CF7: +case 0x7CF8: +case 0x7CF9: +case 0x7CFA: +case 0x7CFB: +case 0x7CFC: +case 0x7CFD: +case 0x7CFE: +case 0x7CFF: +case 0x7E00: +case 0x7E01: +case 0x7E02: +case 0x7E03: +case 0x7E04: +case 0x7E05: +case 0x7E06: +case 0x7E07: +case 0x7E08: +case 0x7E09: +case 0x7E0A: +case 0x7E0B: +case 0x7E0C: +case 0x7E0D: +case 0x7E0E: +case 0x7E0F: +case 0x7E10: +case 0x7E11: +case 0x7E12: +case 0x7E13: +case 0x7E14: +case 0x7E15: +case 0x7E16: +case 0x7E17: +case 0x7E18: +case 0x7E19: +case 0x7E1A: +case 0x7E1B: +case 0x7E1C: +case 0x7E1D: +case 0x7E1E: +case 0x7E1F: +case 0x7E20: +case 0x7E21: +case 0x7E22: +case 0x7E23: +case 0x7E24: +case 0x7E25: +case 0x7E26: +case 0x7E27: +case 0x7E28: +case 0x7E29: +case 0x7E2A: +case 0x7E2B: +case 0x7E2C: +case 0x7E2D: +case 0x7E2E: +case 0x7E2F: +case 0x7E30: +case 0x7E31: +case 0x7E32: +case 0x7E33: +case 0x7E34: +case 0x7E35: +case 0x7E36: +case 0x7E37: +case 0x7E38: +case 0x7E39: +case 0x7E3A: +case 0x7E3B: +case 0x7E3C: +case 0x7E3D: +case 0x7E3E: +case 0x7E3F: +case 0x7E40: +case 0x7E41: +case 0x7E42: +case 0x7E43: +case 0x7E44: +case 0x7E45: +case 0x7E46: +case 0x7E47: +case 0x7E48: +case 0x7E49: +case 0x7E4A: +case 0x7E4B: +case 0x7E4C: +case 0x7E4D: +case 0x7E4E: +case 0x7E4F: +case 0x7E50: +case 0x7E51: +case 0x7E52: +case 0x7E53: +case 0x7E54: +case 0x7E55: +case 0x7E56: +case 0x7E57: +case 0x7E58: +case 0x7E59: +case 0x7E5A: +case 0x7E5B: +case 0x7E5C: +case 0x7E5D: +case 0x7E5E: +case 0x7E5F: +case 0x7E60: +case 0x7E61: +case 0x7E62: +case 0x7E63: +case 0x7E64: +case 0x7E65: +case 0x7E66: +case 0x7E67: +case 0x7E68: +case 0x7E69: +case 0x7E6A: +case 0x7E6B: +case 0x7E6C: +case 0x7E6D: +case 0x7E6E: +case 0x7E6F: +case 0x7E70: +case 0x7E71: +case 0x7E72: +case 0x7E73: +case 0x7E74: +case 0x7E75: +case 0x7E76: +case 0x7E77: +case 0x7E78: +case 0x7E79: +case 0x7E7A: +case 0x7E7B: +case 0x7E7C: +case 0x7E7D: +case 0x7E7E: +case 0x7E7F: +case 0x7E80: +case 0x7E81: +case 0x7E82: +case 0x7E83: +case 0x7E84: +case 0x7E85: +case 0x7E86: +case 0x7E87: +case 0x7E88: +case 0x7E89: +case 0x7E8A: +case 0x7E8B: +case 0x7E8C: +case 0x7E8D: +case 0x7E8E: +case 0x7E8F: +case 0x7E90: +case 0x7E91: +case 0x7E92: +case 0x7E93: +case 0x7E94: +case 0x7E95: +case 0x7E96: +case 0x7E97: +case 0x7E98: +case 0x7E99: +case 0x7E9A: +case 0x7E9B: +case 0x7E9C: +case 0x7E9D: +case 0x7E9E: +case 0x7E9F: +case 0x7EA0: +case 0x7EA1: +case 0x7EA2: +case 0x7EA3: +case 0x7EA4: +case 0x7EA5: +case 0x7EA6: +case 0x7EA7: +case 0x7EA8: +case 0x7EA9: +case 0x7EAA: +case 0x7EAB: +case 0x7EAC: +case 0x7EAD: +case 0x7EAE: +case 0x7EAF: +case 0x7EB0: +case 0x7EB1: +case 0x7EB2: +case 0x7EB3: +case 0x7EB4: +case 0x7EB5: +case 0x7EB6: +case 0x7EB7: +case 0x7EB8: +case 0x7EB9: +case 0x7EBA: +case 0x7EBB: +case 0x7EBC: +case 0x7EBD: +case 0x7EBE: +case 0x7EBF: +case 0x7EC0: +case 0x7EC1: +case 0x7EC2: +case 0x7EC3: +case 0x7EC4: +case 0x7EC5: +case 0x7EC6: +case 0x7EC7: +case 0x7EC8: +case 0x7EC9: +case 0x7ECA: +case 0x7ECB: +case 0x7ECC: +case 0x7ECD: +case 0x7ECE: +case 0x7ECF: +case 0x7ED0: +case 0x7ED1: +case 0x7ED2: +case 0x7ED3: +case 0x7ED4: +case 0x7ED5: +case 0x7ED6: +case 0x7ED7: +case 0x7ED8: +case 0x7ED9: +case 0x7EDA: +case 0x7EDB: +case 0x7EDC: +case 0x7EDD: +case 0x7EDE: +case 0x7EDF: +case 0x7EE0: +case 0x7EE1: +case 0x7EE2: +case 0x7EE3: +case 0x7EE4: +case 0x7EE5: +case 0x7EE6: +case 0x7EE7: +case 0x7EE8: +case 0x7EE9: +case 0x7EEA: +case 0x7EEB: +case 0x7EEC: +case 0x7EED: +case 0x7EEE: +case 0x7EEF: +case 0x7EF0: +case 0x7EF1: +case 0x7EF2: +case 0x7EF3: +case 0x7EF4: +case 0x7EF5: +case 0x7EF6: +case 0x7EF7: +case 0x7EF8: +case 0x7EF9: +case 0x7EFA: +case 0x7EFB: +case 0x7EFC: +case 0x7EFD: +case 0x7EFE: +case 0x7EFF: + +// MOVEQ +case 0x7000: +{ + u32 res; + res = (s32)(s8)Opcode; + CPU->flag_C = CPU->flag_V = 0; + CPU->flag_N = CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) diff --git a/yabause/src/c68k/c68k_op8.inc b/yabause/src/c68k/c68k_op8.inc new file mode 100644 index 0000000000..6b0949d7ef --- /dev/null +++ b/yabause/src/c68k/c68k_op8.inc @@ -0,0 +1,6117 @@ +case 0x8200: +case 0x8400: +case 0x8600: +case 0x8800: +case 0x8A00: +case 0x8C00: +case 0x8E00: +case 0x8001: +case 0x8201: +case 0x8401: +case 0x8601: +case 0x8801: +case 0x8A01: +case 0x8C01: +case 0x8E01: +case 0x8002: +case 0x8202: +case 0x8402: +case 0x8602: +case 0x8802: +case 0x8A02: +case 0x8C02: +case 0x8E02: +case 0x8003: +case 0x8203: +case 0x8403: +case 0x8603: +case 0x8803: +case 0x8A03: +case 0x8C03: +case 0x8E03: +case 0x8004: +case 0x8204: +case 0x8404: +case 0x8604: +case 0x8804: +case 0x8A04: +case 0x8C04: +case 0x8E04: +case 0x8005: +case 0x8205: +case 0x8405: +case 0x8605: +case 0x8805: +case 0x8A05: +case 0x8C05: +case 0x8E05: +case 0x8006: +case 0x8206: +case 0x8406: +case 0x8606: +case 0x8806: +case 0x8A06: +case 0x8C06: +case 0x8E06: +case 0x8007: +case 0x8207: +case 0x8407: +case 0x8607: +case 0x8807: +case 0x8A07: +case 0x8C07: +case 0x8E07: + +// ORaD +case 0x8000: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x8210: +case 0x8410: +case 0x8610: +case 0x8810: +case 0x8A10: +case 0x8C10: +case 0x8E10: +case 0x8011: +case 0x8211: +case 0x8411: +case 0x8611: +case 0x8811: +case 0x8A11: +case 0x8C11: +case 0x8E11: +case 0x8012: +case 0x8212: +case 0x8412: +case 0x8612: +case 0x8812: +case 0x8A12: +case 0x8C12: +case 0x8E12: +case 0x8013: +case 0x8213: +case 0x8413: +case 0x8613: +case 0x8813: +case 0x8A13: +case 0x8C13: +case 0x8E13: +case 0x8014: +case 0x8214: +case 0x8414: +case 0x8614: +case 0x8814: +case 0x8A14: +case 0x8C14: +case 0x8E14: +case 0x8015: +case 0x8215: +case 0x8415: +case 0x8615: +case 0x8815: +case 0x8A15: +case 0x8C15: +case 0x8E15: +case 0x8016: +case 0x8216: +case 0x8416: +case 0x8616: +case 0x8816: +case 0x8A16: +case 0x8C16: +case 0x8E16: +case 0x8017: +case 0x8217: +case 0x8417: +case 0x8617: +case 0x8817: +case 0x8A17: +case 0x8C17: +case 0x8E17: + +// ORaD +case 0x8010: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8218: +case 0x8418: +case 0x8618: +case 0x8818: +case 0x8A18: +case 0x8C18: +case 0x8E18: +case 0x8019: +case 0x8219: +case 0x8419: +case 0x8619: +case 0x8819: +case 0x8A19: +case 0x8C19: +case 0x8E19: +case 0x801A: +case 0x821A: +case 0x841A: +case 0x861A: +case 0x881A: +case 0x8A1A: +case 0x8C1A: +case 0x8E1A: +case 0x801B: +case 0x821B: +case 0x841B: +case 0x861B: +case 0x881B: +case 0x8A1B: +case 0x8C1B: +case 0x8E1B: +case 0x801C: +case 0x821C: +case 0x841C: +case 0x861C: +case 0x881C: +case 0x8A1C: +case 0x8C1C: +case 0x8E1C: +case 0x801D: +case 0x821D: +case 0x841D: +case 0x861D: +case 0x881D: +case 0x8A1D: +case 0x8C1D: +case 0x8E1D: +case 0x801E: +case 0x821E: +case 0x841E: +case 0x861E: +case 0x881E: +case 0x8A1E: +case 0x8C1E: +case 0x8E1E: + +// ORaD +case 0x8018: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8220: +case 0x8420: +case 0x8620: +case 0x8820: +case 0x8A20: +case 0x8C20: +case 0x8E20: +case 0x8021: +case 0x8221: +case 0x8421: +case 0x8621: +case 0x8821: +case 0x8A21: +case 0x8C21: +case 0x8E21: +case 0x8022: +case 0x8222: +case 0x8422: +case 0x8622: +case 0x8822: +case 0x8A22: +case 0x8C22: +case 0x8E22: +case 0x8023: +case 0x8223: +case 0x8423: +case 0x8623: +case 0x8823: +case 0x8A23: +case 0x8C23: +case 0x8E23: +case 0x8024: +case 0x8224: +case 0x8424: +case 0x8624: +case 0x8824: +case 0x8A24: +case 0x8C24: +case 0x8E24: +case 0x8025: +case 0x8225: +case 0x8425: +case 0x8625: +case 0x8825: +case 0x8A25: +case 0x8C25: +case 0x8E25: +case 0x8026: +case 0x8226: +case 0x8426: +case 0x8626: +case 0x8826: +case 0x8A26: +case 0x8C26: +case 0x8E26: + +// ORaD +case 0x8020: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x8228: +case 0x8428: +case 0x8628: +case 0x8828: +case 0x8A28: +case 0x8C28: +case 0x8E28: +case 0x8029: +case 0x8229: +case 0x8429: +case 0x8629: +case 0x8829: +case 0x8A29: +case 0x8C29: +case 0x8E29: +case 0x802A: +case 0x822A: +case 0x842A: +case 0x862A: +case 0x882A: +case 0x8A2A: +case 0x8C2A: +case 0x8E2A: +case 0x802B: +case 0x822B: +case 0x842B: +case 0x862B: +case 0x882B: +case 0x8A2B: +case 0x8C2B: +case 0x8E2B: +case 0x802C: +case 0x822C: +case 0x842C: +case 0x862C: +case 0x882C: +case 0x8A2C: +case 0x8C2C: +case 0x8E2C: +case 0x802D: +case 0x822D: +case 0x842D: +case 0x862D: +case 0x882D: +case 0x8A2D: +case 0x8C2D: +case 0x8E2D: +case 0x802E: +case 0x822E: +case 0x842E: +case 0x862E: +case 0x882E: +case 0x8A2E: +case 0x8C2E: +case 0x8E2E: +case 0x802F: +case 0x822F: +case 0x842F: +case 0x862F: +case 0x882F: +case 0x8A2F: +case 0x8C2F: +case 0x8E2F: + +// ORaD +case 0x8028: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x8230: +case 0x8430: +case 0x8630: +case 0x8830: +case 0x8A30: +case 0x8C30: +case 0x8E30: +case 0x8031: +case 0x8231: +case 0x8431: +case 0x8631: +case 0x8831: +case 0x8A31: +case 0x8C31: +case 0x8E31: +case 0x8032: +case 0x8232: +case 0x8432: +case 0x8632: +case 0x8832: +case 0x8A32: +case 0x8C32: +case 0x8E32: +case 0x8033: +case 0x8233: +case 0x8433: +case 0x8633: +case 0x8833: +case 0x8A33: +case 0x8C33: +case 0x8E33: +case 0x8034: +case 0x8234: +case 0x8434: +case 0x8634: +case 0x8834: +case 0x8A34: +case 0x8C34: +case 0x8E34: +case 0x8035: +case 0x8235: +case 0x8435: +case 0x8635: +case 0x8835: +case 0x8A35: +case 0x8C35: +case 0x8E35: +case 0x8036: +case 0x8236: +case 0x8436: +case 0x8636: +case 0x8836: +case 0x8A36: +case 0x8C36: +case 0x8E36: +case 0x8037: +case 0x8237: +case 0x8437: +case 0x8637: +case 0x8837: +case 0x8A37: +case 0x8C37: +case 0x8E37: + +// ORaD +case 0x8030: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x8238: +case 0x8438: +case 0x8638: +case 0x8838: +case 0x8A38: +case 0x8C38: +case 0x8E38: + +// ORaD +case 0x8038: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x8239: +case 0x8439: +case 0x8639: +case 0x8839: +case 0x8A39: +case 0x8C39: +case 0x8E39: + +// ORaD +case 0x8039: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x823A: +case 0x843A: +case 0x863A: +case 0x883A: +case 0x8A3A: +case 0x8C3A: +case 0x8E3A: + +// ORaD +case 0x803A: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x823B: +case 0x843B: +case 0x863B: +case 0x883B: +case 0x8A3B: +case 0x8C3B: +case 0x8E3B: + +// ORaD +case 0x803B: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x823C: +case 0x843C: +case 0x863C: +case 0x883C: +case 0x8A3C: +case 0x8C3C: +case 0x8E3C: + +// ORaD +case 0x803C: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x821F: +case 0x841F: +case 0x861F: +case 0x881F: +case 0x8A1F: +case 0x8C1F: +case 0x8E1F: + +// ORaD +case 0x801F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8227: +case 0x8427: +case 0x8627: +case 0x8827: +case 0x8A27: +case 0x8C27: +case 0x8E27: + +// ORaD +case 0x8027: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x8240: +case 0x8440: +case 0x8640: +case 0x8840: +case 0x8A40: +case 0x8C40: +case 0x8E40: +case 0x8041: +case 0x8241: +case 0x8441: +case 0x8641: +case 0x8841: +case 0x8A41: +case 0x8C41: +case 0x8E41: +case 0x8042: +case 0x8242: +case 0x8442: +case 0x8642: +case 0x8842: +case 0x8A42: +case 0x8C42: +case 0x8E42: +case 0x8043: +case 0x8243: +case 0x8443: +case 0x8643: +case 0x8843: +case 0x8A43: +case 0x8C43: +case 0x8E43: +case 0x8044: +case 0x8244: +case 0x8444: +case 0x8644: +case 0x8844: +case 0x8A44: +case 0x8C44: +case 0x8E44: +case 0x8045: +case 0x8245: +case 0x8445: +case 0x8645: +case 0x8845: +case 0x8A45: +case 0x8C45: +case 0x8E45: +case 0x8046: +case 0x8246: +case 0x8446: +case 0x8646: +case 0x8846: +case 0x8A46: +case 0x8C46: +case 0x8E46: +case 0x8047: +case 0x8247: +case 0x8447: +case 0x8647: +case 0x8847: +case 0x8A47: +case 0x8C47: +case 0x8E47: + +// ORaD +case 0x8040: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x8250: +case 0x8450: +case 0x8650: +case 0x8850: +case 0x8A50: +case 0x8C50: +case 0x8E50: +case 0x8051: +case 0x8251: +case 0x8451: +case 0x8651: +case 0x8851: +case 0x8A51: +case 0x8C51: +case 0x8E51: +case 0x8052: +case 0x8252: +case 0x8452: +case 0x8652: +case 0x8852: +case 0x8A52: +case 0x8C52: +case 0x8E52: +case 0x8053: +case 0x8253: +case 0x8453: +case 0x8653: +case 0x8853: +case 0x8A53: +case 0x8C53: +case 0x8E53: +case 0x8054: +case 0x8254: +case 0x8454: +case 0x8654: +case 0x8854: +case 0x8A54: +case 0x8C54: +case 0x8E54: +case 0x8055: +case 0x8255: +case 0x8455: +case 0x8655: +case 0x8855: +case 0x8A55: +case 0x8C55: +case 0x8E55: +case 0x8056: +case 0x8256: +case 0x8456: +case 0x8656: +case 0x8856: +case 0x8A56: +case 0x8C56: +case 0x8E56: +case 0x8057: +case 0x8257: +case 0x8457: +case 0x8657: +case 0x8857: +case 0x8A57: +case 0x8C57: +case 0x8E57: + +// ORaD +case 0x8050: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8258: +case 0x8458: +case 0x8658: +case 0x8858: +case 0x8A58: +case 0x8C58: +case 0x8E58: +case 0x8059: +case 0x8259: +case 0x8459: +case 0x8659: +case 0x8859: +case 0x8A59: +case 0x8C59: +case 0x8E59: +case 0x805A: +case 0x825A: +case 0x845A: +case 0x865A: +case 0x885A: +case 0x8A5A: +case 0x8C5A: +case 0x8E5A: +case 0x805B: +case 0x825B: +case 0x845B: +case 0x865B: +case 0x885B: +case 0x8A5B: +case 0x8C5B: +case 0x8E5B: +case 0x805C: +case 0x825C: +case 0x845C: +case 0x865C: +case 0x885C: +case 0x8A5C: +case 0x8C5C: +case 0x8E5C: +case 0x805D: +case 0x825D: +case 0x845D: +case 0x865D: +case 0x885D: +case 0x8A5D: +case 0x8C5D: +case 0x8E5D: +case 0x805E: +case 0x825E: +case 0x845E: +case 0x865E: +case 0x885E: +case 0x8A5E: +case 0x8C5E: +case 0x8E5E: + +// ORaD +case 0x8058: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8260: +case 0x8460: +case 0x8660: +case 0x8860: +case 0x8A60: +case 0x8C60: +case 0x8E60: +case 0x8061: +case 0x8261: +case 0x8461: +case 0x8661: +case 0x8861: +case 0x8A61: +case 0x8C61: +case 0x8E61: +case 0x8062: +case 0x8262: +case 0x8462: +case 0x8662: +case 0x8862: +case 0x8A62: +case 0x8C62: +case 0x8E62: +case 0x8063: +case 0x8263: +case 0x8463: +case 0x8663: +case 0x8863: +case 0x8A63: +case 0x8C63: +case 0x8E63: +case 0x8064: +case 0x8264: +case 0x8464: +case 0x8664: +case 0x8864: +case 0x8A64: +case 0x8C64: +case 0x8E64: +case 0x8065: +case 0x8265: +case 0x8465: +case 0x8665: +case 0x8865: +case 0x8A65: +case 0x8C65: +case 0x8E65: +case 0x8066: +case 0x8266: +case 0x8466: +case 0x8666: +case 0x8866: +case 0x8A66: +case 0x8C66: +case 0x8E66: + +// ORaD +case 0x8060: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x8268: +case 0x8468: +case 0x8668: +case 0x8868: +case 0x8A68: +case 0x8C68: +case 0x8E68: +case 0x8069: +case 0x8269: +case 0x8469: +case 0x8669: +case 0x8869: +case 0x8A69: +case 0x8C69: +case 0x8E69: +case 0x806A: +case 0x826A: +case 0x846A: +case 0x866A: +case 0x886A: +case 0x8A6A: +case 0x8C6A: +case 0x8E6A: +case 0x806B: +case 0x826B: +case 0x846B: +case 0x866B: +case 0x886B: +case 0x8A6B: +case 0x8C6B: +case 0x8E6B: +case 0x806C: +case 0x826C: +case 0x846C: +case 0x866C: +case 0x886C: +case 0x8A6C: +case 0x8C6C: +case 0x8E6C: +case 0x806D: +case 0x826D: +case 0x846D: +case 0x866D: +case 0x886D: +case 0x8A6D: +case 0x8C6D: +case 0x8E6D: +case 0x806E: +case 0x826E: +case 0x846E: +case 0x866E: +case 0x886E: +case 0x8A6E: +case 0x8C6E: +case 0x8E6E: +case 0x806F: +case 0x826F: +case 0x846F: +case 0x866F: +case 0x886F: +case 0x8A6F: +case 0x8C6F: +case 0x8E6F: + +// ORaD +case 0x8068: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x8270: +case 0x8470: +case 0x8670: +case 0x8870: +case 0x8A70: +case 0x8C70: +case 0x8E70: +case 0x8071: +case 0x8271: +case 0x8471: +case 0x8671: +case 0x8871: +case 0x8A71: +case 0x8C71: +case 0x8E71: +case 0x8072: +case 0x8272: +case 0x8472: +case 0x8672: +case 0x8872: +case 0x8A72: +case 0x8C72: +case 0x8E72: +case 0x8073: +case 0x8273: +case 0x8473: +case 0x8673: +case 0x8873: +case 0x8A73: +case 0x8C73: +case 0x8E73: +case 0x8074: +case 0x8274: +case 0x8474: +case 0x8674: +case 0x8874: +case 0x8A74: +case 0x8C74: +case 0x8E74: +case 0x8075: +case 0x8275: +case 0x8475: +case 0x8675: +case 0x8875: +case 0x8A75: +case 0x8C75: +case 0x8E75: +case 0x8076: +case 0x8276: +case 0x8476: +case 0x8676: +case 0x8876: +case 0x8A76: +case 0x8C76: +case 0x8E76: +case 0x8077: +case 0x8277: +case 0x8477: +case 0x8677: +case 0x8877: +case 0x8A77: +case 0x8C77: +case 0x8E77: + +// ORaD +case 0x8070: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x8278: +case 0x8478: +case 0x8678: +case 0x8878: +case 0x8A78: +case 0x8C78: +case 0x8E78: + +// ORaD +case 0x8078: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x8279: +case 0x8479: +case 0x8679: +case 0x8879: +case 0x8A79: +case 0x8C79: +case 0x8E79: + +// ORaD +case 0x8079: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x827A: +case 0x847A: +case 0x867A: +case 0x887A: +case 0x8A7A: +case 0x8C7A: +case 0x8E7A: + +// ORaD +case 0x807A: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x827B: +case 0x847B: +case 0x867B: +case 0x887B: +case 0x8A7B: +case 0x8C7B: +case 0x8E7B: + +// ORaD +case 0x807B: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x827C: +case 0x847C: +case 0x867C: +case 0x887C: +case 0x8A7C: +case 0x8C7C: +case 0x8E7C: + +// ORaD +case 0x807C: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x825F: +case 0x845F: +case 0x865F: +case 0x885F: +case 0x8A5F: +case 0x8C5F: +case 0x8E5F: + +// ORaD +case 0x805F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x8267: +case 0x8467: +case 0x8667: +case 0x8867: +case 0x8A67: +case 0x8C67: +case 0x8E67: + +// ORaD +case 0x8067: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x8280: +case 0x8480: +case 0x8680: +case 0x8880: +case 0x8A80: +case 0x8C80: +case 0x8E80: +case 0x8081: +case 0x8281: +case 0x8481: +case 0x8681: +case 0x8881: +case 0x8A81: +case 0x8C81: +case 0x8E81: +case 0x8082: +case 0x8282: +case 0x8482: +case 0x8682: +case 0x8882: +case 0x8A82: +case 0x8C82: +case 0x8E82: +case 0x8083: +case 0x8283: +case 0x8483: +case 0x8683: +case 0x8883: +case 0x8A83: +case 0x8C83: +case 0x8E83: +case 0x8084: +case 0x8284: +case 0x8484: +case 0x8684: +case 0x8884: +case 0x8A84: +case 0x8C84: +case 0x8E84: +case 0x8085: +case 0x8285: +case 0x8485: +case 0x8685: +case 0x8885: +case 0x8A85: +case 0x8C85: +case 0x8E85: +case 0x8086: +case 0x8286: +case 0x8486: +case 0x8686: +case 0x8886: +case 0x8A86: +case 0x8C86: +case 0x8E86: +case 0x8087: +case 0x8287: +case 0x8487: +case 0x8687: +case 0x8887: +case 0x8A87: +case 0x8C87: +case 0x8E87: + +// ORaD +case 0x8080: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0x8290: +case 0x8490: +case 0x8690: +case 0x8890: +case 0x8A90: +case 0x8C90: +case 0x8E90: +case 0x8091: +case 0x8291: +case 0x8491: +case 0x8691: +case 0x8891: +case 0x8A91: +case 0x8C91: +case 0x8E91: +case 0x8092: +case 0x8292: +case 0x8492: +case 0x8692: +case 0x8892: +case 0x8A92: +case 0x8C92: +case 0x8E92: +case 0x8093: +case 0x8293: +case 0x8493: +case 0x8693: +case 0x8893: +case 0x8A93: +case 0x8C93: +case 0x8E93: +case 0x8094: +case 0x8294: +case 0x8494: +case 0x8694: +case 0x8894: +case 0x8A94: +case 0x8C94: +case 0x8E94: +case 0x8095: +case 0x8295: +case 0x8495: +case 0x8695: +case 0x8895: +case 0x8A95: +case 0x8C95: +case 0x8E95: +case 0x8096: +case 0x8296: +case 0x8496: +case 0x8696: +case 0x8896: +case 0x8A96: +case 0x8C96: +case 0x8E96: +case 0x8097: +case 0x8297: +case 0x8497: +case 0x8697: +case 0x8897: +case 0x8A97: +case 0x8C97: +case 0x8E97: + +// ORaD +case 0x8090: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x8298: +case 0x8498: +case 0x8698: +case 0x8898: +case 0x8A98: +case 0x8C98: +case 0x8E98: +case 0x8099: +case 0x8299: +case 0x8499: +case 0x8699: +case 0x8899: +case 0x8A99: +case 0x8C99: +case 0x8E99: +case 0x809A: +case 0x829A: +case 0x849A: +case 0x869A: +case 0x889A: +case 0x8A9A: +case 0x8C9A: +case 0x8E9A: +case 0x809B: +case 0x829B: +case 0x849B: +case 0x869B: +case 0x889B: +case 0x8A9B: +case 0x8C9B: +case 0x8E9B: +case 0x809C: +case 0x829C: +case 0x849C: +case 0x869C: +case 0x889C: +case 0x8A9C: +case 0x8C9C: +case 0x8E9C: +case 0x809D: +case 0x829D: +case 0x849D: +case 0x869D: +case 0x889D: +case 0x8A9D: +case 0x8C9D: +case 0x8E9D: +case 0x809E: +case 0x829E: +case 0x849E: +case 0x869E: +case 0x889E: +case 0x8A9E: +case 0x8C9E: +case 0x8E9E: + +// ORaD +case 0x8098: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x82A0: +case 0x84A0: +case 0x86A0: +case 0x88A0: +case 0x8AA0: +case 0x8CA0: +case 0x8EA0: +case 0x80A1: +case 0x82A1: +case 0x84A1: +case 0x86A1: +case 0x88A1: +case 0x8AA1: +case 0x8CA1: +case 0x8EA1: +case 0x80A2: +case 0x82A2: +case 0x84A2: +case 0x86A2: +case 0x88A2: +case 0x8AA2: +case 0x8CA2: +case 0x8EA2: +case 0x80A3: +case 0x82A3: +case 0x84A3: +case 0x86A3: +case 0x88A3: +case 0x8AA3: +case 0x8CA3: +case 0x8EA3: +case 0x80A4: +case 0x82A4: +case 0x84A4: +case 0x86A4: +case 0x88A4: +case 0x8AA4: +case 0x8CA4: +case 0x8EA4: +case 0x80A5: +case 0x82A5: +case 0x84A5: +case 0x86A5: +case 0x88A5: +case 0x8AA5: +case 0x8CA5: +case 0x8EA5: +case 0x80A6: +case 0x82A6: +case 0x84A6: +case 0x86A6: +case 0x88A6: +case 0x8AA6: +case 0x8CA6: +case 0x8EA6: + +// ORaD +case 0x80A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x82A8: +case 0x84A8: +case 0x86A8: +case 0x88A8: +case 0x8AA8: +case 0x8CA8: +case 0x8EA8: +case 0x80A9: +case 0x82A9: +case 0x84A9: +case 0x86A9: +case 0x88A9: +case 0x8AA9: +case 0x8CA9: +case 0x8EA9: +case 0x80AA: +case 0x82AA: +case 0x84AA: +case 0x86AA: +case 0x88AA: +case 0x8AAA: +case 0x8CAA: +case 0x8EAA: +case 0x80AB: +case 0x82AB: +case 0x84AB: +case 0x86AB: +case 0x88AB: +case 0x8AAB: +case 0x8CAB: +case 0x8EAB: +case 0x80AC: +case 0x82AC: +case 0x84AC: +case 0x86AC: +case 0x88AC: +case 0x8AAC: +case 0x8CAC: +case 0x8EAC: +case 0x80AD: +case 0x82AD: +case 0x84AD: +case 0x86AD: +case 0x88AD: +case 0x8AAD: +case 0x8CAD: +case 0x8EAD: +case 0x80AE: +case 0x82AE: +case 0x84AE: +case 0x86AE: +case 0x88AE: +case 0x8AAE: +case 0x8CAE: +case 0x8EAE: +case 0x80AF: +case 0x82AF: +case 0x84AF: +case 0x86AF: +case 0x88AF: +case 0x8AAF: +case 0x8CAF: +case 0x8EAF: + +// ORaD +case 0x80A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x82B0: +case 0x84B0: +case 0x86B0: +case 0x88B0: +case 0x8AB0: +case 0x8CB0: +case 0x8EB0: +case 0x80B1: +case 0x82B1: +case 0x84B1: +case 0x86B1: +case 0x88B1: +case 0x8AB1: +case 0x8CB1: +case 0x8EB1: +case 0x80B2: +case 0x82B2: +case 0x84B2: +case 0x86B2: +case 0x88B2: +case 0x8AB2: +case 0x8CB2: +case 0x8EB2: +case 0x80B3: +case 0x82B3: +case 0x84B3: +case 0x86B3: +case 0x88B3: +case 0x8AB3: +case 0x8CB3: +case 0x8EB3: +case 0x80B4: +case 0x82B4: +case 0x84B4: +case 0x86B4: +case 0x88B4: +case 0x8AB4: +case 0x8CB4: +case 0x8EB4: +case 0x80B5: +case 0x82B5: +case 0x84B5: +case 0x86B5: +case 0x88B5: +case 0x8AB5: +case 0x8CB5: +case 0x8EB5: +case 0x80B6: +case 0x82B6: +case 0x84B6: +case 0x86B6: +case 0x88B6: +case 0x8AB6: +case 0x8CB6: +case 0x8EB6: +case 0x80B7: +case 0x82B7: +case 0x84B7: +case 0x86B7: +case 0x88B7: +case 0x8AB7: +case 0x8CB7: +case 0x8EB7: + +// ORaD +case 0x80B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0x82B8: +case 0x84B8: +case 0x86B8: +case 0x88B8: +case 0x8AB8: +case 0x8CB8: +case 0x8EB8: + +// ORaD +case 0x80B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x82B9: +case 0x84B9: +case 0x86B9: +case 0x88B9: +case 0x8AB9: +case 0x8CB9: +case 0x8EB9: + +// ORaD +case 0x80B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(24) +case 0x82BA: +case 0x84BA: +case 0x86BA: +case 0x88BA: +case 0x8ABA: +case 0x8CBA: +case 0x8EBA: + +// ORaD +case 0x80BA: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x82BB: +case 0x84BB: +case 0x86BB: +case 0x88BB: +case 0x8ABB: +case 0x8CBB: +case 0x8EBB: + +// ORaD +case 0x80BB: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0x82BC: +case 0x84BC: +case 0x86BC: +case 0x88BC: +case 0x8ABC: +case 0x8CBC: +case 0x8EBC: + +// ORaD +case 0x80BC: +{ + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(14) +case 0x829F: +case 0x849F: +case 0x869F: +case 0x889F: +case 0x8A9F: +case 0x8C9F: +case 0x8E9F: + +// ORaD +case 0x809F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x82A7: +case 0x84A7: +case 0x86A7: +case 0x88A7: +case 0x8AA7: +case 0x8CA7: +case 0x8EA7: + +// ORaD +case 0x80A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x8310: +case 0x8510: +case 0x8710: +case 0x8910: +case 0x8B10: +case 0x8D10: +case 0x8F10: +case 0x8111: +case 0x8311: +case 0x8511: +case 0x8711: +case 0x8911: +case 0x8B11: +case 0x8D11: +case 0x8F11: +case 0x8112: +case 0x8312: +case 0x8512: +case 0x8712: +case 0x8912: +case 0x8B12: +case 0x8D12: +case 0x8F12: +case 0x8113: +case 0x8313: +case 0x8513: +case 0x8713: +case 0x8913: +case 0x8B13: +case 0x8D13: +case 0x8F13: +case 0x8114: +case 0x8314: +case 0x8514: +case 0x8714: +case 0x8914: +case 0x8B14: +case 0x8D14: +case 0x8F14: +case 0x8115: +case 0x8315: +case 0x8515: +case 0x8715: +case 0x8915: +case 0x8B15: +case 0x8D15: +case 0x8F15: +case 0x8116: +case 0x8316: +case 0x8516: +case 0x8716: +case 0x8916: +case 0x8B16: +case 0x8D16: +case 0x8F16: +case 0x8117: +case 0x8317: +case 0x8517: +case 0x8717: +case 0x8917: +case 0x8B17: +case 0x8D17: +case 0x8F17: + +// ORDa +case 0x8110: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x8318: +case 0x8518: +case 0x8718: +case 0x8918: +case 0x8B18: +case 0x8D18: +case 0x8F18: +case 0x8119: +case 0x8319: +case 0x8519: +case 0x8719: +case 0x8919: +case 0x8B19: +case 0x8D19: +case 0x8F19: +case 0x811A: +case 0x831A: +case 0x851A: +case 0x871A: +case 0x891A: +case 0x8B1A: +case 0x8D1A: +case 0x8F1A: +case 0x811B: +case 0x831B: +case 0x851B: +case 0x871B: +case 0x891B: +case 0x8B1B: +case 0x8D1B: +case 0x8F1B: +case 0x811C: +case 0x831C: +case 0x851C: +case 0x871C: +case 0x891C: +case 0x8B1C: +case 0x8D1C: +case 0x8F1C: +case 0x811D: +case 0x831D: +case 0x851D: +case 0x871D: +case 0x891D: +case 0x8B1D: +case 0x8D1D: +case 0x8F1D: +case 0x811E: +case 0x831E: +case 0x851E: +case 0x871E: +case 0x891E: +case 0x8B1E: +case 0x8D1E: +case 0x8F1E: + +// ORDa +case 0x8118: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x8320: +case 0x8520: +case 0x8720: +case 0x8920: +case 0x8B20: +case 0x8D20: +case 0x8F20: +case 0x8121: +case 0x8321: +case 0x8521: +case 0x8721: +case 0x8921: +case 0x8B21: +case 0x8D21: +case 0x8F21: +case 0x8122: +case 0x8322: +case 0x8522: +case 0x8722: +case 0x8922: +case 0x8B22: +case 0x8D22: +case 0x8F22: +case 0x8123: +case 0x8323: +case 0x8523: +case 0x8723: +case 0x8923: +case 0x8B23: +case 0x8D23: +case 0x8F23: +case 0x8124: +case 0x8324: +case 0x8524: +case 0x8724: +case 0x8924: +case 0x8B24: +case 0x8D24: +case 0x8F24: +case 0x8125: +case 0x8325: +case 0x8525: +case 0x8725: +case 0x8925: +case 0x8B25: +case 0x8D25: +case 0x8F25: +case 0x8126: +case 0x8326: +case 0x8526: +case 0x8726: +case 0x8926: +case 0x8B26: +case 0x8D26: +case 0x8F26: + +// ORDa +case 0x8120: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x8328: +case 0x8528: +case 0x8728: +case 0x8928: +case 0x8B28: +case 0x8D28: +case 0x8F28: +case 0x8129: +case 0x8329: +case 0x8529: +case 0x8729: +case 0x8929: +case 0x8B29: +case 0x8D29: +case 0x8F29: +case 0x812A: +case 0x832A: +case 0x852A: +case 0x872A: +case 0x892A: +case 0x8B2A: +case 0x8D2A: +case 0x8F2A: +case 0x812B: +case 0x832B: +case 0x852B: +case 0x872B: +case 0x892B: +case 0x8B2B: +case 0x8D2B: +case 0x8F2B: +case 0x812C: +case 0x832C: +case 0x852C: +case 0x872C: +case 0x892C: +case 0x8B2C: +case 0x8D2C: +case 0x8F2C: +case 0x812D: +case 0x832D: +case 0x852D: +case 0x872D: +case 0x892D: +case 0x8B2D: +case 0x8D2D: +case 0x8F2D: +case 0x812E: +case 0x832E: +case 0x852E: +case 0x872E: +case 0x892E: +case 0x8B2E: +case 0x8D2E: +case 0x8F2E: +case 0x812F: +case 0x832F: +case 0x852F: +case 0x872F: +case 0x892F: +case 0x8B2F: +case 0x8D2F: +case 0x8F2F: + +// ORDa +case 0x8128: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x8330: +case 0x8530: +case 0x8730: +case 0x8930: +case 0x8B30: +case 0x8D30: +case 0x8F30: +case 0x8131: +case 0x8331: +case 0x8531: +case 0x8731: +case 0x8931: +case 0x8B31: +case 0x8D31: +case 0x8F31: +case 0x8132: +case 0x8332: +case 0x8532: +case 0x8732: +case 0x8932: +case 0x8B32: +case 0x8D32: +case 0x8F32: +case 0x8133: +case 0x8333: +case 0x8533: +case 0x8733: +case 0x8933: +case 0x8B33: +case 0x8D33: +case 0x8F33: +case 0x8134: +case 0x8334: +case 0x8534: +case 0x8734: +case 0x8934: +case 0x8B34: +case 0x8D34: +case 0x8F34: +case 0x8135: +case 0x8335: +case 0x8535: +case 0x8735: +case 0x8935: +case 0x8B35: +case 0x8D35: +case 0x8F35: +case 0x8136: +case 0x8336: +case 0x8536: +case 0x8736: +case 0x8936: +case 0x8B36: +case 0x8D36: +case 0x8F36: +case 0x8137: +case 0x8337: +case 0x8537: +case 0x8737: +case 0x8937: +case 0x8B37: +case 0x8D37: +case 0x8F37: + +// ORDa +case 0x8130: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x8338: +case 0x8538: +case 0x8738: +case 0x8938: +case 0x8B38: +case 0x8D38: +case 0x8F38: + +// ORDa +case 0x8138: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x8339: +case 0x8539: +case 0x8739: +case 0x8939: +case 0x8B39: +case 0x8D39: +case 0x8F39: + +// ORDa +case 0x8139: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x831F: +case 0x851F: +case 0x871F: +case 0x891F: +case 0x8B1F: +case 0x8D1F: +case 0x8F1F: + +// ORDa +case 0x811F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x8327: +case 0x8527: +case 0x8727: +case 0x8927: +case 0x8B27: +case 0x8D27: +case 0x8F27: + +// ORDa +case 0x8127: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x8350: +case 0x8550: +case 0x8750: +case 0x8950: +case 0x8B50: +case 0x8D50: +case 0x8F50: +case 0x8151: +case 0x8351: +case 0x8551: +case 0x8751: +case 0x8951: +case 0x8B51: +case 0x8D51: +case 0x8F51: +case 0x8152: +case 0x8352: +case 0x8552: +case 0x8752: +case 0x8952: +case 0x8B52: +case 0x8D52: +case 0x8F52: +case 0x8153: +case 0x8353: +case 0x8553: +case 0x8753: +case 0x8953: +case 0x8B53: +case 0x8D53: +case 0x8F53: +case 0x8154: +case 0x8354: +case 0x8554: +case 0x8754: +case 0x8954: +case 0x8B54: +case 0x8D54: +case 0x8F54: +case 0x8155: +case 0x8355: +case 0x8555: +case 0x8755: +case 0x8955: +case 0x8B55: +case 0x8D55: +case 0x8F55: +case 0x8156: +case 0x8356: +case 0x8556: +case 0x8756: +case 0x8956: +case 0x8B56: +case 0x8D56: +case 0x8F56: +case 0x8157: +case 0x8357: +case 0x8557: +case 0x8757: +case 0x8957: +case 0x8B57: +case 0x8D57: +case 0x8F57: + +// ORDa +case 0x8150: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x8358: +case 0x8558: +case 0x8758: +case 0x8958: +case 0x8B58: +case 0x8D58: +case 0x8F58: +case 0x8159: +case 0x8359: +case 0x8559: +case 0x8759: +case 0x8959: +case 0x8B59: +case 0x8D59: +case 0x8F59: +case 0x815A: +case 0x835A: +case 0x855A: +case 0x875A: +case 0x895A: +case 0x8B5A: +case 0x8D5A: +case 0x8F5A: +case 0x815B: +case 0x835B: +case 0x855B: +case 0x875B: +case 0x895B: +case 0x8B5B: +case 0x8D5B: +case 0x8F5B: +case 0x815C: +case 0x835C: +case 0x855C: +case 0x875C: +case 0x895C: +case 0x8B5C: +case 0x8D5C: +case 0x8F5C: +case 0x815D: +case 0x835D: +case 0x855D: +case 0x875D: +case 0x895D: +case 0x8B5D: +case 0x8D5D: +case 0x8F5D: +case 0x815E: +case 0x835E: +case 0x855E: +case 0x875E: +case 0x895E: +case 0x8B5E: +case 0x8D5E: +case 0x8F5E: + +// ORDa +case 0x8158: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x8360: +case 0x8560: +case 0x8760: +case 0x8960: +case 0x8B60: +case 0x8D60: +case 0x8F60: +case 0x8161: +case 0x8361: +case 0x8561: +case 0x8761: +case 0x8961: +case 0x8B61: +case 0x8D61: +case 0x8F61: +case 0x8162: +case 0x8362: +case 0x8562: +case 0x8762: +case 0x8962: +case 0x8B62: +case 0x8D62: +case 0x8F62: +case 0x8163: +case 0x8363: +case 0x8563: +case 0x8763: +case 0x8963: +case 0x8B63: +case 0x8D63: +case 0x8F63: +case 0x8164: +case 0x8364: +case 0x8564: +case 0x8764: +case 0x8964: +case 0x8B64: +case 0x8D64: +case 0x8F64: +case 0x8165: +case 0x8365: +case 0x8565: +case 0x8765: +case 0x8965: +case 0x8B65: +case 0x8D65: +case 0x8F65: +case 0x8166: +case 0x8366: +case 0x8566: +case 0x8766: +case 0x8966: +case 0x8B66: +case 0x8D66: +case 0x8F66: + +// ORDa +case 0x8160: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x8368: +case 0x8568: +case 0x8768: +case 0x8968: +case 0x8B68: +case 0x8D68: +case 0x8F68: +case 0x8169: +case 0x8369: +case 0x8569: +case 0x8769: +case 0x8969: +case 0x8B69: +case 0x8D69: +case 0x8F69: +case 0x816A: +case 0x836A: +case 0x856A: +case 0x876A: +case 0x896A: +case 0x8B6A: +case 0x8D6A: +case 0x8F6A: +case 0x816B: +case 0x836B: +case 0x856B: +case 0x876B: +case 0x896B: +case 0x8B6B: +case 0x8D6B: +case 0x8F6B: +case 0x816C: +case 0x836C: +case 0x856C: +case 0x876C: +case 0x896C: +case 0x8B6C: +case 0x8D6C: +case 0x8F6C: +case 0x816D: +case 0x836D: +case 0x856D: +case 0x876D: +case 0x896D: +case 0x8B6D: +case 0x8D6D: +case 0x8F6D: +case 0x816E: +case 0x836E: +case 0x856E: +case 0x876E: +case 0x896E: +case 0x8B6E: +case 0x8D6E: +case 0x8F6E: +case 0x816F: +case 0x836F: +case 0x856F: +case 0x876F: +case 0x896F: +case 0x8B6F: +case 0x8D6F: +case 0x8F6F: + +// ORDa +case 0x8168: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x8370: +case 0x8570: +case 0x8770: +case 0x8970: +case 0x8B70: +case 0x8D70: +case 0x8F70: +case 0x8171: +case 0x8371: +case 0x8571: +case 0x8771: +case 0x8971: +case 0x8B71: +case 0x8D71: +case 0x8F71: +case 0x8172: +case 0x8372: +case 0x8572: +case 0x8772: +case 0x8972: +case 0x8B72: +case 0x8D72: +case 0x8F72: +case 0x8173: +case 0x8373: +case 0x8573: +case 0x8773: +case 0x8973: +case 0x8B73: +case 0x8D73: +case 0x8F73: +case 0x8174: +case 0x8374: +case 0x8574: +case 0x8774: +case 0x8974: +case 0x8B74: +case 0x8D74: +case 0x8F74: +case 0x8175: +case 0x8375: +case 0x8575: +case 0x8775: +case 0x8975: +case 0x8B75: +case 0x8D75: +case 0x8F75: +case 0x8176: +case 0x8376: +case 0x8576: +case 0x8776: +case 0x8976: +case 0x8B76: +case 0x8D76: +case 0x8F76: +case 0x8177: +case 0x8377: +case 0x8577: +case 0x8777: +case 0x8977: +case 0x8B77: +case 0x8D77: +case 0x8F77: + +// ORDa +case 0x8170: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x8378: +case 0x8578: +case 0x8778: +case 0x8978: +case 0x8B78: +case 0x8D78: +case 0x8F78: + +// ORDa +case 0x8178: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x8379: +case 0x8579: +case 0x8779: +case 0x8979: +case 0x8B79: +case 0x8D79: +case 0x8F79: + +// ORDa +case 0x8179: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x835F: +case 0x855F: +case 0x875F: +case 0x895F: +case 0x8B5F: +case 0x8D5F: +case 0x8F5F: + +// ORDa +case 0x815F: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x8367: +case 0x8567: +case 0x8767: +case 0x8967: +case 0x8B67: +case 0x8D67: +case 0x8F67: + +// ORDa +case 0x8167: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x8390: +case 0x8590: +case 0x8790: +case 0x8990: +case 0x8B90: +case 0x8D90: +case 0x8F90: +case 0x8191: +case 0x8391: +case 0x8591: +case 0x8791: +case 0x8991: +case 0x8B91: +case 0x8D91: +case 0x8F91: +case 0x8192: +case 0x8392: +case 0x8592: +case 0x8792: +case 0x8992: +case 0x8B92: +case 0x8D92: +case 0x8F92: +case 0x8193: +case 0x8393: +case 0x8593: +case 0x8793: +case 0x8993: +case 0x8B93: +case 0x8D93: +case 0x8F93: +case 0x8194: +case 0x8394: +case 0x8594: +case 0x8794: +case 0x8994: +case 0x8B94: +case 0x8D94: +case 0x8F94: +case 0x8195: +case 0x8395: +case 0x8595: +case 0x8795: +case 0x8995: +case 0x8B95: +case 0x8D95: +case 0x8F95: +case 0x8196: +case 0x8396: +case 0x8596: +case 0x8796: +case 0x8996: +case 0x8B96: +case 0x8D96: +case 0x8F96: +case 0x8197: +case 0x8397: +case 0x8597: +case 0x8797: +case 0x8997: +case 0x8B97: +case 0x8D97: +case 0x8F97: + +// ORDa +case 0x8190: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x8398: +case 0x8598: +case 0x8798: +case 0x8998: +case 0x8B98: +case 0x8D98: +case 0x8F98: +case 0x8199: +case 0x8399: +case 0x8599: +case 0x8799: +case 0x8999: +case 0x8B99: +case 0x8D99: +case 0x8F99: +case 0x819A: +case 0x839A: +case 0x859A: +case 0x879A: +case 0x899A: +case 0x8B9A: +case 0x8D9A: +case 0x8F9A: +case 0x819B: +case 0x839B: +case 0x859B: +case 0x879B: +case 0x899B: +case 0x8B9B: +case 0x8D9B: +case 0x8F9B: +case 0x819C: +case 0x839C: +case 0x859C: +case 0x879C: +case 0x899C: +case 0x8B9C: +case 0x8D9C: +case 0x8F9C: +case 0x819D: +case 0x839D: +case 0x859D: +case 0x879D: +case 0x899D: +case 0x8B9D: +case 0x8D9D: +case 0x8F9D: +case 0x819E: +case 0x839E: +case 0x859E: +case 0x879E: +case 0x899E: +case 0x8B9E: +case 0x8D9E: +case 0x8F9E: + +// ORDa +case 0x8198: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x83A0: +case 0x85A0: +case 0x87A0: +case 0x89A0: +case 0x8BA0: +case 0x8DA0: +case 0x8FA0: +case 0x81A1: +case 0x83A1: +case 0x85A1: +case 0x87A1: +case 0x89A1: +case 0x8BA1: +case 0x8DA1: +case 0x8FA1: +case 0x81A2: +case 0x83A2: +case 0x85A2: +case 0x87A2: +case 0x89A2: +case 0x8BA2: +case 0x8DA2: +case 0x8FA2: +case 0x81A3: +case 0x83A3: +case 0x85A3: +case 0x87A3: +case 0x89A3: +case 0x8BA3: +case 0x8DA3: +case 0x8FA3: +case 0x81A4: +case 0x83A4: +case 0x85A4: +case 0x87A4: +case 0x89A4: +case 0x8BA4: +case 0x8DA4: +case 0x8FA4: +case 0x81A5: +case 0x83A5: +case 0x85A5: +case 0x87A5: +case 0x89A5: +case 0x8BA5: +case 0x8DA5: +case 0x8FA5: +case 0x81A6: +case 0x83A6: +case 0x85A6: +case 0x87A6: +case 0x89A6: +case 0x8BA6: +case 0x8DA6: +case 0x8FA6: + +// ORDa +case 0x81A0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x83A8: +case 0x85A8: +case 0x87A8: +case 0x89A8: +case 0x8BA8: +case 0x8DA8: +case 0x8FA8: +case 0x81A9: +case 0x83A9: +case 0x85A9: +case 0x87A9: +case 0x89A9: +case 0x8BA9: +case 0x8DA9: +case 0x8FA9: +case 0x81AA: +case 0x83AA: +case 0x85AA: +case 0x87AA: +case 0x89AA: +case 0x8BAA: +case 0x8DAA: +case 0x8FAA: +case 0x81AB: +case 0x83AB: +case 0x85AB: +case 0x87AB: +case 0x89AB: +case 0x8BAB: +case 0x8DAB: +case 0x8FAB: +case 0x81AC: +case 0x83AC: +case 0x85AC: +case 0x87AC: +case 0x89AC: +case 0x8BAC: +case 0x8DAC: +case 0x8FAC: +case 0x81AD: +case 0x83AD: +case 0x85AD: +case 0x87AD: +case 0x89AD: +case 0x8BAD: +case 0x8DAD: +case 0x8FAD: +case 0x81AE: +case 0x83AE: +case 0x85AE: +case 0x87AE: +case 0x89AE: +case 0x8BAE: +case 0x8DAE: +case 0x8FAE: +case 0x81AF: +case 0x83AF: +case 0x85AF: +case 0x87AF: +case 0x89AF: +case 0x8BAF: +case 0x8DAF: +case 0x8FAF: + +// ORDa +case 0x81A8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x83B0: +case 0x85B0: +case 0x87B0: +case 0x89B0: +case 0x8BB0: +case 0x8DB0: +case 0x8FB0: +case 0x81B1: +case 0x83B1: +case 0x85B1: +case 0x87B1: +case 0x89B1: +case 0x8BB1: +case 0x8DB1: +case 0x8FB1: +case 0x81B2: +case 0x83B2: +case 0x85B2: +case 0x87B2: +case 0x89B2: +case 0x8BB2: +case 0x8DB2: +case 0x8FB2: +case 0x81B3: +case 0x83B3: +case 0x85B3: +case 0x87B3: +case 0x89B3: +case 0x8BB3: +case 0x8DB3: +case 0x8FB3: +case 0x81B4: +case 0x83B4: +case 0x85B4: +case 0x87B4: +case 0x89B4: +case 0x8BB4: +case 0x8DB4: +case 0x8FB4: +case 0x81B5: +case 0x83B5: +case 0x85B5: +case 0x87B5: +case 0x89B5: +case 0x8BB5: +case 0x8DB5: +case 0x8FB5: +case 0x81B6: +case 0x83B6: +case 0x85B6: +case 0x87B6: +case 0x89B6: +case 0x8BB6: +case 0x8DB6: +case 0x8FB6: +case 0x81B7: +case 0x83B7: +case 0x85B7: +case 0x87B7: +case 0x89B7: +case 0x8BB7: +case 0x8DB7: +case 0x8FB7: + +// ORDa +case 0x81B0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x83B8: +case 0x85B8: +case 0x87B8: +case 0x89B8: +case 0x8BB8: +case 0x8DB8: +case 0x8FB8: + +// ORDa +case 0x81B8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x83B9: +case 0x85B9: +case 0x87B9: +case 0x89B9: +case 0x8BB9: +case 0x8DB9: +case 0x8FB9: + +// ORDa +case 0x81B9: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x839F: +case 0x859F: +case 0x879F: +case 0x899F: +case 0x8B9F: +case 0x8D9F: +case 0x8F9F: + +// ORDa +case 0x819F: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x83A7: +case 0x85A7: +case 0x87A7: +case 0x89A7: +case 0x8BA7: +case 0x8DA7: +case 0x8FA7: + +// ORDa +case 0x81A7: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res |= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x8300: +case 0x8500: +case 0x8700: +case 0x8900: +case 0x8B00: +case 0x8D00: +case 0x8F00: +case 0x8101: +case 0x8301: +case 0x8501: +case 0x8701: +case 0x8901: +case 0x8B01: +case 0x8D01: +case 0x8F01: +case 0x8102: +case 0x8302: +case 0x8502: +case 0x8702: +case 0x8902: +case 0x8B02: +case 0x8D02: +case 0x8F02: +case 0x8103: +case 0x8303: +case 0x8503: +case 0x8703: +case 0x8903: +case 0x8B03: +case 0x8D03: +case 0x8F03: +case 0x8104: +case 0x8304: +case 0x8504: +case 0x8704: +case 0x8904: +case 0x8B04: +case 0x8D04: +case 0x8F04: +case 0x8105: +case 0x8305: +case 0x8505: +case 0x8705: +case 0x8905: +case 0x8B05: +case 0x8D05: +case 0x8F05: +case 0x8106: +case 0x8306: +case 0x8506: +case 0x8706: +case 0x8906: +case 0x8B06: +case 0x8D06: +case 0x8F06: +case 0x8107: +case 0x8307: +case 0x8507: +case 0x8707: +case 0x8907: +case 0x8B07: +case 0x8D07: +case 0x8F07: + +// SBCD +case 0x8100: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = (dst & 0xF) - (src & 0xF) - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res -= 6; + res += (dst & 0xF0) - (src & 0xF0); + if (res > 0x99) + { + res += 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0x8308: +case 0x8508: +case 0x8708: +case 0x8908: +case 0x8B08: +case 0x8D08: +case 0x8109: +case 0x8309: +case 0x8509: +case 0x8709: +case 0x8909: +case 0x8B09: +case 0x8D09: +case 0x810A: +case 0x830A: +case 0x850A: +case 0x870A: +case 0x890A: +case 0x8B0A: +case 0x8D0A: +case 0x810B: +case 0x830B: +case 0x850B: +case 0x870B: +case 0x890B: +case 0x8B0B: +case 0x8D0B: +case 0x810C: +case 0x830C: +case 0x850C: +case 0x870C: +case 0x890C: +case 0x8B0C: +case 0x8D0C: +case 0x810D: +case 0x830D: +case 0x850D: +case 0x870D: +case 0x890D: +case 0x8B0D: +case 0x8D0D: +case 0x810E: +case 0x830E: +case 0x850E: +case 0x870E: +case 0x890E: +case 0x8B0E: +case 0x8D0E: + +// SBCDM +case 0x8108: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) - (src & 0xF) - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res -= 6; + res += (dst & 0xF0) - (src & 0xF0); + if (res > 0x99) + { + res += 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x830F: +case 0x850F: +case 0x870F: +case 0x890F: +case 0x8B0F: +case 0x8D0F: + +// SBCD7M +case 0x810F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) - (src & 0xF) - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res -= 6; + res += (dst & 0xF0) - (src & 0xF0); + if (res > 0x99) + { + res += 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x8F09: +case 0x8F0A: +case 0x8F0B: +case 0x8F0C: +case 0x8F0D: +case 0x8F0E: + +// SBCDM7 +case 0x8F08: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) - (src & 0xF) - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res -= 6; + res += (dst & 0xF0) - (src & 0xF0); + if (res > 0x99) + { + res += 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// SBCD7M7 +case 0x8F0F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) - (src & 0xF) - ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res -= 6; + res += (dst & 0xF0) - (src & 0xF0); + if (res > 0x99) + { + res += 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x82C0: +case 0x84C0: +case 0x86C0: +case 0x88C0: +case 0x8AC0: +case 0x8CC0: +case 0x8EC0: +case 0x80C1: +case 0x82C1: +case 0x84C1: +case 0x86C1: +case 0x88C1: +case 0x8AC1: +case 0x8CC1: +case 0x8EC1: +case 0x80C2: +case 0x82C2: +case 0x84C2: +case 0x86C2: +case 0x88C2: +case 0x8AC2: +case 0x8CC2: +case 0x8EC2: +case 0x80C3: +case 0x82C3: +case 0x84C3: +case 0x86C3: +case 0x88C3: +case 0x8AC3: +case 0x8CC3: +case 0x8EC3: +case 0x80C4: +case 0x82C4: +case 0x84C4: +case 0x86C4: +case 0x88C4: +case 0x8AC4: +case 0x8CC4: +case 0x8EC4: +case 0x80C5: +case 0x82C5: +case 0x84C5: +case 0x86C5: +case 0x88C5: +case 0x8AC5: +case 0x8CC5: +case 0x8EC5: +case 0x80C6: +case 0x82C6: +case 0x84C6: +case 0x86C6: +case 0x88C6: +case 0x8AC6: +case 0x8CC6: +case 0x8EC6: +case 0x80C7: +case 0x82C7: +case 0x84C7: +case 0x86C7: +case 0x88C7: +case 0x8AC7: +case 0x8CC7: +case 0x8EC7: + +// DIVU +case 0x80C0: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(10) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(70) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(90) +case 0x82D0: +case 0x84D0: +case 0x86D0: +case 0x88D0: +case 0x8AD0: +case 0x8CD0: +case 0x8ED0: +case 0x80D1: +case 0x82D1: +case 0x84D1: +case 0x86D1: +case 0x88D1: +case 0x8AD1: +case 0x8CD1: +case 0x8ED1: +case 0x80D2: +case 0x82D2: +case 0x84D2: +case 0x86D2: +case 0x88D2: +case 0x8AD2: +case 0x8CD2: +case 0x8ED2: +case 0x80D3: +case 0x82D3: +case 0x84D3: +case 0x86D3: +case 0x88D3: +case 0x8AD3: +case 0x8CD3: +case 0x8ED3: +case 0x80D4: +case 0x82D4: +case 0x84D4: +case 0x86D4: +case 0x88D4: +case 0x8AD4: +case 0x8CD4: +case 0x8ED4: +case 0x80D5: +case 0x82D5: +case 0x84D5: +case 0x86D5: +case 0x88D5: +case 0x8AD5: +case 0x8CD5: +case 0x8ED5: +case 0x80D6: +case 0x82D6: +case 0x84D6: +case 0x86D6: +case 0x88D6: +case 0x8AD6: +case 0x8CD6: +case 0x8ED6: +case 0x80D7: +case 0x82D7: +case 0x84D7: +case 0x86D7: +case 0x88D7: +case 0x8AD7: +case 0x8CD7: +case 0x8ED7: + +// DIVU +case 0x80D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(74) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(94) +case 0x82D8: +case 0x84D8: +case 0x86D8: +case 0x88D8: +case 0x8AD8: +case 0x8CD8: +case 0x8ED8: +case 0x80D9: +case 0x82D9: +case 0x84D9: +case 0x86D9: +case 0x88D9: +case 0x8AD9: +case 0x8CD9: +case 0x8ED9: +case 0x80DA: +case 0x82DA: +case 0x84DA: +case 0x86DA: +case 0x88DA: +case 0x8ADA: +case 0x8CDA: +case 0x8EDA: +case 0x80DB: +case 0x82DB: +case 0x84DB: +case 0x86DB: +case 0x88DB: +case 0x8ADB: +case 0x8CDB: +case 0x8EDB: +case 0x80DC: +case 0x82DC: +case 0x84DC: +case 0x86DC: +case 0x88DC: +case 0x8ADC: +case 0x8CDC: +case 0x8EDC: +case 0x80DD: +case 0x82DD: +case 0x84DD: +case 0x86DD: +case 0x88DD: +case 0x8ADD: +case 0x8CDD: +case 0x8EDD: +case 0x80DE: +case 0x82DE: +case 0x84DE: +case 0x86DE: +case 0x88DE: +case 0x8ADE: +case 0x8CDE: +case 0x8EDE: + +// DIVU +case 0x80D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(74) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(94) +case 0x82E0: +case 0x84E0: +case 0x86E0: +case 0x88E0: +case 0x8AE0: +case 0x8CE0: +case 0x8EE0: +case 0x80E1: +case 0x82E1: +case 0x84E1: +case 0x86E1: +case 0x88E1: +case 0x8AE1: +case 0x8CE1: +case 0x8EE1: +case 0x80E2: +case 0x82E2: +case 0x84E2: +case 0x86E2: +case 0x88E2: +case 0x8AE2: +case 0x8CE2: +case 0x8EE2: +case 0x80E3: +case 0x82E3: +case 0x84E3: +case 0x86E3: +case 0x88E3: +case 0x8AE3: +case 0x8CE3: +case 0x8EE3: +case 0x80E4: +case 0x82E4: +case 0x84E4: +case 0x86E4: +case 0x88E4: +case 0x8AE4: +case 0x8CE4: +case 0x8EE4: +case 0x80E5: +case 0x82E5: +case 0x84E5: +case 0x86E5: +case 0x88E5: +case 0x8AE5: +case 0x8CE5: +case 0x8EE5: +case 0x80E6: +case 0x82E6: +case 0x84E6: +case 0x86E6: +case 0x88E6: +case 0x8AE6: +case 0x8CE6: +case 0x8EE6: + +// DIVU +case 0x80E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(16) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(76) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(96) +case 0x82E8: +case 0x84E8: +case 0x86E8: +case 0x88E8: +case 0x8AE8: +case 0x8CE8: +case 0x8EE8: +case 0x80E9: +case 0x82E9: +case 0x84E9: +case 0x86E9: +case 0x88E9: +case 0x8AE9: +case 0x8CE9: +case 0x8EE9: +case 0x80EA: +case 0x82EA: +case 0x84EA: +case 0x86EA: +case 0x88EA: +case 0x8AEA: +case 0x8CEA: +case 0x8EEA: +case 0x80EB: +case 0x82EB: +case 0x84EB: +case 0x86EB: +case 0x88EB: +case 0x8AEB: +case 0x8CEB: +case 0x8EEB: +case 0x80EC: +case 0x82EC: +case 0x84EC: +case 0x86EC: +case 0x88EC: +case 0x8AEC: +case 0x8CEC: +case 0x8EEC: +case 0x80ED: +case 0x82ED: +case 0x84ED: +case 0x86ED: +case 0x88ED: +case 0x8AED: +case 0x8CED: +case 0x8EED: +case 0x80EE: +case 0x82EE: +case 0x84EE: +case 0x86EE: +case 0x88EE: +case 0x8AEE: +case 0x8CEE: +case 0x8EEE: +case 0x80EF: +case 0x82EF: +case 0x84EF: +case 0x86EF: +case 0x88EF: +case 0x8AEF: +case 0x8CEF: +case 0x8EEF: + +// DIVU +case 0x80E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(78) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(98) +case 0x82F0: +case 0x84F0: +case 0x86F0: +case 0x88F0: +case 0x8AF0: +case 0x8CF0: +case 0x8EF0: +case 0x80F1: +case 0x82F1: +case 0x84F1: +case 0x86F1: +case 0x88F1: +case 0x8AF1: +case 0x8CF1: +case 0x8EF1: +case 0x80F2: +case 0x82F2: +case 0x84F2: +case 0x86F2: +case 0x88F2: +case 0x8AF2: +case 0x8CF2: +case 0x8EF2: +case 0x80F3: +case 0x82F3: +case 0x84F3: +case 0x86F3: +case 0x88F3: +case 0x8AF3: +case 0x8CF3: +case 0x8EF3: +case 0x80F4: +case 0x82F4: +case 0x84F4: +case 0x86F4: +case 0x88F4: +case 0x8AF4: +case 0x8CF4: +case 0x8EF4: +case 0x80F5: +case 0x82F5: +case 0x84F5: +case 0x86F5: +case 0x88F5: +case 0x8AF5: +case 0x8CF5: +case 0x8EF5: +case 0x80F6: +case 0x82F6: +case 0x84F6: +case 0x86F6: +case 0x88F6: +case 0x8AF6: +case 0x8CF6: +case 0x8EF6: +case 0x80F7: +case 0x82F7: +case 0x84F7: +case 0x86F7: +case 0x88F7: +case 0x8AF7: +case 0x8CF7: +case 0x8EF7: + +// DIVU +case 0x80F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(20) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(80) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(100) +case 0x82F8: +case 0x84F8: +case 0x86F8: +case 0x88F8: +case 0x8AF8: +case 0x8CF8: +case 0x8EF8: + +// DIVU +case 0x80F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(78) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(98) +case 0x82F9: +case 0x84F9: +case 0x86F9: +case 0x88F9: +case 0x8AF9: +case 0x8CF9: +case 0x8EF9: + +// DIVU +case 0x80F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(22) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(82) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(102) +case 0x82FA: +case 0x84FA: +case 0x86FA: +case 0x88FA: +case 0x8AFA: +case 0x8CFA: +case 0x8EFA: + +// DIVU +case 0x80FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(78) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(98) +case 0x82FB: +case 0x84FB: +case 0x86FB: +case 0x88FB: +case 0x8AFB: +case 0x8CFB: +case 0x8EFB: + +// DIVU +case 0x80FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(20) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(80) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(100) +case 0x82FC: +case 0x84FC: +case 0x86FC: +case 0x88FC: +case 0x8AFC: +case 0x8CFC: +case 0x8EFC: + +// DIVU +case 0x80FC: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(74) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(94) +case 0x82DF: +case 0x84DF: +case 0x86DF: +case 0x88DF: +case 0x8ADF: +case 0x8CDF: +case 0x8EDF: + +// DIVU +case 0x80DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(74) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(94) +case 0x82E7: +case 0x84E7: +case 0x86E7: +case 0x88E7: +case 0x8AE7: +case 0x8CE7: +case 0x8EE7: + +// DIVU +case 0x80E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(16) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + { + u32 q, r; + + q = dst / src; + r = dst % src; + + if (q & 0xFFFF0000) + { + CPU->flag_V = C68K_SR_V; + RET(76) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(96) +case 0x83C0: +case 0x85C0: +case 0x87C0: +case 0x89C0: +case 0x8BC0: +case 0x8DC0: +case 0x8FC0: +case 0x81C1: +case 0x83C1: +case 0x85C1: +case 0x87C1: +case 0x89C1: +case 0x8BC1: +case 0x8DC1: +case 0x8FC1: +case 0x81C2: +case 0x83C2: +case 0x85C2: +case 0x87C2: +case 0x89C2: +case 0x8BC2: +case 0x8DC2: +case 0x8FC2: +case 0x81C3: +case 0x83C3: +case 0x85C3: +case 0x87C3: +case 0x89C3: +case 0x8BC3: +case 0x8DC3: +case 0x8FC3: +case 0x81C4: +case 0x83C4: +case 0x85C4: +case 0x87C4: +case 0x89C4: +case 0x8BC4: +case 0x8DC4: +case 0x8FC4: +case 0x81C5: +case 0x83C5: +case 0x85C5: +case 0x87C5: +case 0x89C5: +case 0x8BC5: +case 0x8DC5: +case 0x8FC5: +case 0x81C6: +case 0x83C6: +case 0x85C6: +case 0x87C6: +case 0x89C6: +case 0x8BC6: +case 0x8DC6: +case 0x8FC6: +case 0x81C7: +case 0x83C7: +case 0x85C7: +case 0x87C7: +case 0x89C7: +case 0x8BC7: +case 0x8DC7: +case 0x8FC7: + +// DIVS +case 0x81C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(10) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(50) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(80) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(108) +case 0x83D0: +case 0x85D0: +case 0x87D0: +case 0x89D0: +case 0x8BD0: +case 0x8DD0: +case 0x8FD0: +case 0x81D1: +case 0x83D1: +case 0x85D1: +case 0x87D1: +case 0x89D1: +case 0x8BD1: +case 0x8DD1: +case 0x8FD1: +case 0x81D2: +case 0x83D2: +case 0x85D2: +case 0x87D2: +case 0x89D2: +case 0x8BD2: +case 0x8DD2: +case 0x8FD2: +case 0x81D3: +case 0x83D3: +case 0x85D3: +case 0x87D3: +case 0x89D3: +case 0x8BD3: +case 0x8DD3: +case 0x8FD3: +case 0x81D4: +case 0x83D4: +case 0x85D4: +case 0x87D4: +case 0x89D4: +case 0x8BD4: +case 0x8DD4: +case 0x8FD4: +case 0x81D5: +case 0x83D5: +case 0x85D5: +case 0x87D5: +case 0x89D5: +case 0x8BD5: +case 0x8DD5: +case 0x8FD5: +case 0x81D6: +case 0x83D6: +case 0x85D6: +case 0x87D6: +case 0x89D6: +case 0x8BD6: +case 0x8DD6: +case 0x8FD6: +case 0x81D7: +case 0x83D7: +case 0x85D7: +case 0x87D7: +case 0x89D7: +case 0x8BD7: +case 0x8DD7: +case 0x8FD7: + +// DIVS +case 0x81D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(54) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(84) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(112) +case 0x83D8: +case 0x85D8: +case 0x87D8: +case 0x89D8: +case 0x8BD8: +case 0x8DD8: +case 0x8FD8: +case 0x81D9: +case 0x83D9: +case 0x85D9: +case 0x87D9: +case 0x89D9: +case 0x8BD9: +case 0x8DD9: +case 0x8FD9: +case 0x81DA: +case 0x83DA: +case 0x85DA: +case 0x87DA: +case 0x89DA: +case 0x8BDA: +case 0x8DDA: +case 0x8FDA: +case 0x81DB: +case 0x83DB: +case 0x85DB: +case 0x87DB: +case 0x89DB: +case 0x8BDB: +case 0x8DDB: +case 0x8FDB: +case 0x81DC: +case 0x83DC: +case 0x85DC: +case 0x87DC: +case 0x89DC: +case 0x8BDC: +case 0x8DDC: +case 0x8FDC: +case 0x81DD: +case 0x83DD: +case 0x85DD: +case 0x87DD: +case 0x89DD: +case 0x8BDD: +case 0x8DDD: +case 0x8FDD: +case 0x81DE: +case 0x83DE: +case 0x85DE: +case 0x87DE: +case 0x89DE: +case 0x8BDE: +case 0x8DDE: +case 0x8FDE: + +// DIVS +case 0x81D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(54) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(84) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(112) +case 0x83E0: +case 0x85E0: +case 0x87E0: +case 0x89E0: +case 0x8BE0: +case 0x8DE0: +case 0x8FE0: +case 0x81E1: +case 0x83E1: +case 0x85E1: +case 0x87E1: +case 0x89E1: +case 0x8BE1: +case 0x8DE1: +case 0x8FE1: +case 0x81E2: +case 0x83E2: +case 0x85E2: +case 0x87E2: +case 0x89E2: +case 0x8BE2: +case 0x8DE2: +case 0x8FE2: +case 0x81E3: +case 0x83E3: +case 0x85E3: +case 0x87E3: +case 0x89E3: +case 0x8BE3: +case 0x8DE3: +case 0x8FE3: +case 0x81E4: +case 0x83E4: +case 0x85E4: +case 0x87E4: +case 0x89E4: +case 0x8BE4: +case 0x8DE4: +case 0x8FE4: +case 0x81E5: +case 0x83E5: +case 0x85E5: +case 0x87E5: +case 0x89E5: +case 0x8BE5: +case 0x8DE5: +case 0x8FE5: +case 0x81E6: +case 0x83E6: +case 0x85E6: +case 0x87E6: +case 0x89E6: +case 0x8BE6: +case 0x8DE6: +case 0x8FE6: + +// DIVS +case 0x81E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(16) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(56) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(86) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(114) +case 0x83E8: +case 0x85E8: +case 0x87E8: +case 0x89E8: +case 0x8BE8: +case 0x8DE8: +case 0x8FE8: +case 0x81E9: +case 0x83E9: +case 0x85E9: +case 0x87E9: +case 0x89E9: +case 0x8BE9: +case 0x8DE9: +case 0x8FE9: +case 0x81EA: +case 0x83EA: +case 0x85EA: +case 0x87EA: +case 0x89EA: +case 0x8BEA: +case 0x8DEA: +case 0x8FEA: +case 0x81EB: +case 0x83EB: +case 0x85EB: +case 0x87EB: +case 0x89EB: +case 0x8BEB: +case 0x8DEB: +case 0x8FEB: +case 0x81EC: +case 0x83EC: +case 0x85EC: +case 0x87EC: +case 0x89EC: +case 0x8BEC: +case 0x8DEC: +case 0x8FEC: +case 0x81ED: +case 0x83ED: +case 0x85ED: +case 0x87ED: +case 0x89ED: +case 0x8BED: +case 0x8DED: +case 0x8FED: +case 0x81EE: +case 0x83EE: +case 0x85EE: +case 0x87EE: +case 0x89EE: +case 0x8BEE: +case 0x8DEE: +case 0x8FEE: +case 0x81EF: +case 0x83EF: +case 0x85EF: +case 0x87EF: +case 0x89EF: +case 0x8BEF: +case 0x8DEF: +case 0x8FEF: + +// DIVS +case 0x81E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(58) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(88) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(116) +case 0x83F0: +case 0x85F0: +case 0x87F0: +case 0x89F0: +case 0x8BF0: +case 0x8DF0: +case 0x8FF0: +case 0x81F1: +case 0x83F1: +case 0x85F1: +case 0x87F1: +case 0x89F1: +case 0x8BF1: +case 0x8DF1: +case 0x8FF1: +case 0x81F2: +case 0x83F2: +case 0x85F2: +case 0x87F2: +case 0x89F2: +case 0x8BF2: +case 0x8DF2: +case 0x8FF2: +case 0x81F3: +case 0x83F3: +case 0x85F3: +case 0x87F3: +case 0x89F3: +case 0x8BF3: +case 0x8DF3: +case 0x8FF3: +case 0x81F4: +case 0x83F4: +case 0x85F4: +case 0x87F4: +case 0x89F4: +case 0x8BF4: +case 0x8DF4: +case 0x8FF4: +case 0x81F5: +case 0x83F5: +case 0x85F5: +case 0x87F5: +case 0x89F5: +case 0x8BF5: +case 0x8DF5: +case 0x8FF5: +case 0x81F6: +case 0x83F6: +case 0x85F6: +case 0x87F6: +case 0x89F6: +case 0x8BF6: +case 0x8DF6: +case 0x8FF6: +case 0x81F7: +case 0x83F7: +case 0x85F7: +case 0x87F7: +case 0x89F7: +case 0x8BF7: +case 0x8DF7: +case 0x8FF7: + +// DIVS +case 0x81F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(20) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(60) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(90) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(118) +case 0x83F8: +case 0x85F8: +case 0x87F8: +case 0x89F8: +case 0x8BF8: +case 0x8DF8: +case 0x8FF8: + +// DIVS +case 0x81F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(58) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(88) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(116) +case 0x83F9: +case 0x85F9: +case 0x87F9: +case 0x89F9: +case 0x8BF9: +case 0x8DF9: +case 0x8FF9: + +// DIVS +case 0x81F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(22) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(62) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(92) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(120) +case 0x83FA: +case 0x85FA: +case 0x87FA: +case 0x89FA: +case 0x8BFA: +case 0x8DFA: +case 0x8FFA: + +// DIVS +case 0x81FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(18) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(58) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(88) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(116) +case 0x83FB: +case 0x85FB: +case 0x87FB: +case 0x89FB: +case 0x8BFB: +case 0x8DFB: +case 0x8FFB: + +// DIVS +case 0x81FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(20) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(60) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(90) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(118) +case 0x83FC: +case 0x85FC: +case 0x87FC: +case 0x89FC: +case 0x8BFC: +case 0x8DFC: +case 0x8FFC: + +// DIVS +case 0x81FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)FETCH_WORD; + PC += 2; + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(54) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(84) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(112) +case 0x83DF: +case 0x85DF: +case 0x87DF: +case 0x89DF: +case 0x8BDF: +case 0x8DDF: +case 0x8FDF: + +// DIVS +case 0x81DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(14) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(54) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(84) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(112) +case 0x83E7: +case 0x85E7: +case 0x87E7: +case 0x89E7: +case 0x8BE7: +case 0x8DE7: +case 0x8FE7: + +// DIVS +case 0x81E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + if (src == 0) + { + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_ZERO_DIVIDE_EX; + POST_IO + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO + RET(16) + } + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + if ((dst == 0x80000000) && (src == -1)) + { + CPU->flag_notZ = CPU->flag_N = 0; + CPU->flag_V = CPU->flag_C = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + RET(56) + } + { + s32 q, r; + + q = (s32)dst / (s32)src; + r = (s32)dst % (s32)src; + + if ((q > 0x7FFF) || (q < -0x8000)) + { + CPU->flag_V = C68K_SR_V; + RET(86) + } + q &= 0x0000FFFF; + CPU->flag_notZ = q; + CPU->flag_N = q >> 8; + CPU->flag_V = CPU->flag_C = 0; + res = q | (r << 16); + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + } +} +RET(114) diff --git a/yabause/src/c68k/c68k_op9.inc b/yabause/src/c68k/c68k_op9.inc new file mode 100644 index 0000000000..ef0bdc95c1 --- /dev/null +++ b/yabause/src/c68k/c68k_op9.inc @@ -0,0 +1,5950 @@ +case 0x9200: +case 0x9400: +case 0x9600: +case 0x9800: +case 0x9A00: +case 0x9C00: +case 0x9E00: +case 0x9001: +case 0x9201: +case 0x9401: +case 0x9601: +case 0x9801: +case 0x9A01: +case 0x9C01: +case 0x9E01: +case 0x9002: +case 0x9202: +case 0x9402: +case 0x9602: +case 0x9802: +case 0x9A02: +case 0x9C02: +case 0x9E02: +case 0x9003: +case 0x9203: +case 0x9403: +case 0x9603: +case 0x9803: +case 0x9A03: +case 0x9C03: +case 0x9E03: +case 0x9004: +case 0x9204: +case 0x9404: +case 0x9604: +case 0x9804: +case 0x9A04: +case 0x9C04: +case 0x9E04: +case 0x9005: +case 0x9205: +case 0x9405: +case 0x9605: +case 0x9805: +case 0x9A05: +case 0x9C05: +case 0x9E05: +case 0x9006: +case 0x9206: +case 0x9406: +case 0x9606: +case 0x9806: +case 0x9A06: +case 0x9C06: +case 0x9E06: +case 0x9007: +case 0x9207: +case 0x9407: +case 0x9607: +case 0x9807: +case 0x9A07: +case 0x9C07: +case 0x9E07: + +// SUBaD +case 0x9000: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9208: +case 0x9408: +case 0x9608: +case 0x9808: +case 0x9A08: +case 0x9C08: +case 0x9E08: +case 0x9009: +case 0x9209: +case 0x9409: +case 0x9609: +case 0x9809: +case 0x9A09: +case 0x9C09: +case 0x9E09: +case 0x900A: +case 0x920A: +case 0x940A: +case 0x960A: +case 0x980A: +case 0x9A0A: +case 0x9C0A: +case 0x9E0A: +case 0x900B: +case 0x920B: +case 0x940B: +case 0x960B: +case 0x980B: +case 0x9A0B: +case 0x9C0B: +case 0x9E0B: +case 0x900C: +case 0x920C: +case 0x940C: +case 0x960C: +case 0x980C: +case 0x9A0C: +case 0x9C0C: +case 0x9E0C: +case 0x900D: +case 0x920D: +case 0x940D: +case 0x960D: +case 0x980D: +case 0x9A0D: +case 0x9C0D: +case 0x9E0D: +case 0x900E: +case 0x920E: +case 0x940E: +case 0x960E: +case 0x980E: +case 0x9A0E: +case 0x9C0E: +case 0x9E0E: +case 0x900F: +case 0x920F: +case 0x940F: +case 0x960F: +case 0x980F: +case 0x9A0F: +case 0x9C0F: +case 0x9E0F: + +// SUBaD +case 0x9008: +{ + u32 res; + pointer dst; + pointer src; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9210: +case 0x9410: +case 0x9610: +case 0x9810: +case 0x9A10: +case 0x9C10: +case 0x9E10: +case 0x9011: +case 0x9211: +case 0x9411: +case 0x9611: +case 0x9811: +case 0x9A11: +case 0x9C11: +case 0x9E11: +case 0x9012: +case 0x9212: +case 0x9412: +case 0x9612: +case 0x9812: +case 0x9A12: +case 0x9C12: +case 0x9E12: +case 0x9013: +case 0x9213: +case 0x9413: +case 0x9613: +case 0x9813: +case 0x9A13: +case 0x9C13: +case 0x9E13: +case 0x9014: +case 0x9214: +case 0x9414: +case 0x9614: +case 0x9814: +case 0x9A14: +case 0x9C14: +case 0x9E14: +case 0x9015: +case 0x9215: +case 0x9415: +case 0x9615: +case 0x9815: +case 0x9A15: +case 0x9C15: +case 0x9E15: +case 0x9016: +case 0x9216: +case 0x9416: +case 0x9616: +case 0x9816: +case 0x9A16: +case 0x9C16: +case 0x9E16: +case 0x9017: +case 0x9217: +case 0x9417: +case 0x9617: +case 0x9817: +case 0x9A17: +case 0x9C17: +case 0x9E17: + +// SUBaD +case 0x9010: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9218: +case 0x9418: +case 0x9618: +case 0x9818: +case 0x9A18: +case 0x9C18: +case 0x9E18: +case 0x9019: +case 0x9219: +case 0x9419: +case 0x9619: +case 0x9819: +case 0x9A19: +case 0x9C19: +case 0x9E19: +case 0x901A: +case 0x921A: +case 0x941A: +case 0x961A: +case 0x981A: +case 0x9A1A: +case 0x9C1A: +case 0x9E1A: +case 0x901B: +case 0x921B: +case 0x941B: +case 0x961B: +case 0x981B: +case 0x9A1B: +case 0x9C1B: +case 0x9E1B: +case 0x901C: +case 0x921C: +case 0x941C: +case 0x961C: +case 0x981C: +case 0x9A1C: +case 0x9C1C: +case 0x9E1C: +case 0x901D: +case 0x921D: +case 0x941D: +case 0x961D: +case 0x981D: +case 0x9A1D: +case 0x9C1D: +case 0x9E1D: +case 0x901E: +case 0x921E: +case 0x941E: +case 0x961E: +case 0x981E: +case 0x9A1E: +case 0x9C1E: +case 0x9E1E: + +// SUBaD +case 0x9018: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9220: +case 0x9420: +case 0x9620: +case 0x9820: +case 0x9A20: +case 0x9C20: +case 0x9E20: +case 0x9021: +case 0x9221: +case 0x9421: +case 0x9621: +case 0x9821: +case 0x9A21: +case 0x9C21: +case 0x9E21: +case 0x9022: +case 0x9222: +case 0x9422: +case 0x9622: +case 0x9822: +case 0x9A22: +case 0x9C22: +case 0x9E22: +case 0x9023: +case 0x9223: +case 0x9423: +case 0x9623: +case 0x9823: +case 0x9A23: +case 0x9C23: +case 0x9E23: +case 0x9024: +case 0x9224: +case 0x9424: +case 0x9624: +case 0x9824: +case 0x9A24: +case 0x9C24: +case 0x9E24: +case 0x9025: +case 0x9225: +case 0x9425: +case 0x9625: +case 0x9825: +case 0x9A25: +case 0x9C25: +case 0x9E25: +case 0x9026: +case 0x9226: +case 0x9426: +case 0x9626: +case 0x9826: +case 0x9A26: +case 0x9C26: +case 0x9E26: + +// SUBaD +case 0x9020: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x9228: +case 0x9428: +case 0x9628: +case 0x9828: +case 0x9A28: +case 0x9C28: +case 0x9E28: +case 0x9029: +case 0x9229: +case 0x9429: +case 0x9629: +case 0x9829: +case 0x9A29: +case 0x9C29: +case 0x9E29: +case 0x902A: +case 0x922A: +case 0x942A: +case 0x962A: +case 0x982A: +case 0x9A2A: +case 0x9C2A: +case 0x9E2A: +case 0x902B: +case 0x922B: +case 0x942B: +case 0x962B: +case 0x982B: +case 0x9A2B: +case 0x9C2B: +case 0x9E2B: +case 0x902C: +case 0x922C: +case 0x942C: +case 0x962C: +case 0x982C: +case 0x9A2C: +case 0x9C2C: +case 0x9E2C: +case 0x902D: +case 0x922D: +case 0x942D: +case 0x962D: +case 0x982D: +case 0x9A2D: +case 0x9C2D: +case 0x9E2D: +case 0x902E: +case 0x922E: +case 0x942E: +case 0x962E: +case 0x982E: +case 0x9A2E: +case 0x9C2E: +case 0x9E2E: +case 0x902F: +case 0x922F: +case 0x942F: +case 0x962F: +case 0x982F: +case 0x9A2F: +case 0x9C2F: +case 0x9E2F: + +// SUBaD +case 0x9028: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x9230: +case 0x9430: +case 0x9630: +case 0x9830: +case 0x9A30: +case 0x9C30: +case 0x9E30: +case 0x9031: +case 0x9231: +case 0x9431: +case 0x9631: +case 0x9831: +case 0x9A31: +case 0x9C31: +case 0x9E31: +case 0x9032: +case 0x9232: +case 0x9432: +case 0x9632: +case 0x9832: +case 0x9A32: +case 0x9C32: +case 0x9E32: +case 0x9033: +case 0x9233: +case 0x9433: +case 0x9633: +case 0x9833: +case 0x9A33: +case 0x9C33: +case 0x9E33: +case 0x9034: +case 0x9234: +case 0x9434: +case 0x9634: +case 0x9834: +case 0x9A34: +case 0x9C34: +case 0x9E34: +case 0x9035: +case 0x9235: +case 0x9435: +case 0x9635: +case 0x9835: +case 0x9A35: +case 0x9C35: +case 0x9E35: +case 0x9036: +case 0x9236: +case 0x9436: +case 0x9636: +case 0x9836: +case 0x9A36: +case 0x9C36: +case 0x9E36: +case 0x9037: +case 0x9237: +case 0x9437: +case 0x9637: +case 0x9837: +case 0x9A37: +case 0x9C37: +case 0x9E37: + +// SUBaD +case 0x9030: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x9238: +case 0x9438: +case 0x9638: +case 0x9838: +case 0x9A38: +case 0x9C38: +case 0x9E38: + +// SUBaD +case 0x9038: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x9239: +case 0x9439: +case 0x9639: +case 0x9839: +case 0x9A39: +case 0x9C39: +case 0x9E39: + +// SUBaD +case 0x9039: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x923A: +case 0x943A: +case 0x963A: +case 0x983A: +case 0x9A3A: +case 0x9C3A: +case 0x9E3A: + +// SUBaD +case 0x903A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x923B: +case 0x943B: +case 0x963B: +case 0x983B: +case 0x9A3B: +case 0x9C3B: +case 0x9E3B: + +// SUBaD +case 0x903B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x923C: +case 0x943C: +case 0x963C: +case 0x983C: +case 0x9A3C: +case 0x9C3C: +case 0x9E3C: + +// SUBaD +case 0x903C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x921F: +case 0x941F: +case 0x961F: +case 0x981F: +case 0x9A1F: +case 0x9C1F: +case 0x9E1F: + +// SUBaD +case 0x901F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9227: +case 0x9427: +case 0x9627: +case 0x9827: +case 0x9A27: +case 0x9C27: +case 0x9E27: + +// SUBaD +case 0x9027: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x9240: +case 0x9440: +case 0x9640: +case 0x9840: +case 0x9A40: +case 0x9C40: +case 0x9E40: +case 0x9041: +case 0x9241: +case 0x9441: +case 0x9641: +case 0x9841: +case 0x9A41: +case 0x9C41: +case 0x9E41: +case 0x9042: +case 0x9242: +case 0x9442: +case 0x9642: +case 0x9842: +case 0x9A42: +case 0x9C42: +case 0x9E42: +case 0x9043: +case 0x9243: +case 0x9443: +case 0x9643: +case 0x9843: +case 0x9A43: +case 0x9C43: +case 0x9E43: +case 0x9044: +case 0x9244: +case 0x9444: +case 0x9644: +case 0x9844: +case 0x9A44: +case 0x9C44: +case 0x9E44: +case 0x9045: +case 0x9245: +case 0x9445: +case 0x9645: +case 0x9845: +case 0x9A45: +case 0x9C45: +case 0x9E45: +case 0x9046: +case 0x9246: +case 0x9446: +case 0x9646: +case 0x9846: +case 0x9A46: +case 0x9C46: +case 0x9E46: +case 0x9047: +case 0x9247: +case 0x9447: +case 0x9647: +case 0x9847: +case 0x9A47: +case 0x9C47: +case 0x9E47: + +// SUBaD +case 0x9040: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9248: +case 0x9448: +case 0x9648: +case 0x9848: +case 0x9A48: +case 0x9C48: +case 0x9E48: +case 0x9049: +case 0x9249: +case 0x9449: +case 0x9649: +case 0x9849: +case 0x9A49: +case 0x9C49: +case 0x9E49: +case 0x904A: +case 0x924A: +case 0x944A: +case 0x964A: +case 0x984A: +case 0x9A4A: +case 0x9C4A: +case 0x9E4A: +case 0x904B: +case 0x924B: +case 0x944B: +case 0x964B: +case 0x984B: +case 0x9A4B: +case 0x9C4B: +case 0x9E4B: +case 0x904C: +case 0x924C: +case 0x944C: +case 0x964C: +case 0x984C: +case 0x9A4C: +case 0x9C4C: +case 0x9E4C: +case 0x904D: +case 0x924D: +case 0x944D: +case 0x964D: +case 0x984D: +case 0x9A4D: +case 0x9C4D: +case 0x9E4D: +case 0x904E: +case 0x924E: +case 0x944E: +case 0x964E: +case 0x984E: +case 0x9A4E: +case 0x9C4E: +case 0x9E4E: +case 0x904F: +case 0x924F: +case 0x944F: +case 0x964F: +case 0x984F: +case 0x9A4F: +case 0x9C4F: +case 0x9E4F: + +// SUBaD +case 0x9048: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->A[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9250: +case 0x9450: +case 0x9650: +case 0x9850: +case 0x9A50: +case 0x9C50: +case 0x9E50: +case 0x9051: +case 0x9251: +case 0x9451: +case 0x9651: +case 0x9851: +case 0x9A51: +case 0x9C51: +case 0x9E51: +case 0x9052: +case 0x9252: +case 0x9452: +case 0x9652: +case 0x9852: +case 0x9A52: +case 0x9C52: +case 0x9E52: +case 0x9053: +case 0x9253: +case 0x9453: +case 0x9653: +case 0x9853: +case 0x9A53: +case 0x9C53: +case 0x9E53: +case 0x9054: +case 0x9254: +case 0x9454: +case 0x9654: +case 0x9854: +case 0x9A54: +case 0x9C54: +case 0x9E54: +case 0x9055: +case 0x9255: +case 0x9455: +case 0x9655: +case 0x9855: +case 0x9A55: +case 0x9C55: +case 0x9E55: +case 0x9056: +case 0x9256: +case 0x9456: +case 0x9656: +case 0x9856: +case 0x9A56: +case 0x9C56: +case 0x9E56: +case 0x9057: +case 0x9257: +case 0x9457: +case 0x9657: +case 0x9857: +case 0x9A57: +case 0x9C57: +case 0x9E57: + +// SUBaD +case 0x9050: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9258: +case 0x9458: +case 0x9658: +case 0x9858: +case 0x9A58: +case 0x9C58: +case 0x9E58: +case 0x9059: +case 0x9259: +case 0x9459: +case 0x9659: +case 0x9859: +case 0x9A59: +case 0x9C59: +case 0x9E59: +case 0x905A: +case 0x925A: +case 0x945A: +case 0x965A: +case 0x985A: +case 0x9A5A: +case 0x9C5A: +case 0x9E5A: +case 0x905B: +case 0x925B: +case 0x945B: +case 0x965B: +case 0x985B: +case 0x9A5B: +case 0x9C5B: +case 0x9E5B: +case 0x905C: +case 0x925C: +case 0x945C: +case 0x965C: +case 0x985C: +case 0x9A5C: +case 0x9C5C: +case 0x9E5C: +case 0x905D: +case 0x925D: +case 0x945D: +case 0x965D: +case 0x985D: +case 0x9A5D: +case 0x9C5D: +case 0x9E5D: +case 0x905E: +case 0x925E: +case 0x945E: +case 0x965E: +case 0x985E: +case 0x9A5E: +case 0x9C5E: +case 0x9E5E: + +// SUBaD +case 0x9058: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9260: +case 0x9460: +case 0x9660: +case 0x9860: +case 0x9A60: +case 0x9C60: +case 0x9E60: +case 0x9061: +case 0x9261: +case 0x9461: +case 0x9661: +case 0x9861: +case 0x9A61: +case 0x9C61: +case 0x9E61: +case 0x9062: +case 0x9262: +case 0x9462: +case 0x9662: +case 0x9862: +case 0x9A62: +case 0x9C62: +case 0x9E62: +case 0x9063: +case 0x9263: +case 0x9463: +case 0x9663: +case 0x9863: +case 0x9A63: +case 0x9C63: +case 0x9E63: +case 0x9064: +case 0x9264: +case 0x9464: +case 0x9664: +case 0x9864: +case 0x9A64: +case 0x9C64: +case 0x9E64: +case 0x9065: +case 0x9265: +case 0x9465: +case 0x9665: +case 0x9865: +case 0x9A65: +case 0x9C65: +case 0x9E65: +case 0x9066: +case 0x9266: +case 0x9466: +case 0x9666: +case 0x9866: +case 0x9A66: +case 0x9C66: +case 0x9E66: + +// SUBaD +case 0x9060: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x9268: +case 0x9468: +case 0x9668: +case 0x9868: +case 0x9A68: +case 0x9C68: +case 0x9E68: +case 0x9069: +case 0x9269: +case 0x9469: +case 0x9669: +case 0x9869: +case 0x9A69: +case 0x9C69: +case 0x9E69: +case 0x906A: +case 0x926A: +case 0x946A: +case 0x966A: +case 0x986A: +case 0x9A6A: +case 0x9C6A: +case 0x9E6A: +case 0x906B: +case 0x926B: +case 0x946B: +case 0x966B: +case 0x986B: +case 0x9A6B: +case 0x9C6B: +case 0x9E6B: +case 0x906C: +case 0x926C: +case 0x946C: +case 0x966C: +case 0x986C: +case 0x9A6C: +case 0x9C6C: +case 0x9E6C: +case 0x906D: +case 0x926D: +case 0x946D: +case 0x966D: +case 0x986D: +case 0x9A6D: +case 0x9C6D: +case 0x9E6D: +case 0x906E: +case 0x926E: +case 0x946E: +case 0x966E: +case 0x986E: +case 0x9A6E: +case 0x9C6E: +case 0x9E6E: +case 0x906F: +case 0x926F: +case 0x946F: +case 0x966F: +case 0x986F: +case 0x9A6F: +case 0x9C6F: +case 0x9E6F: + +// SUBaD +case 0x9068: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x9270: +case 0x9470: +case 0x9670: +case 0x9870: +case 0x9A70: +case 0x9C70: +case 0x9E70: +case 0x9071: +case 0x9271: +case 0x9471: +case 0x9671: +case 0x9871: +case 0x9A71: +case 0x9C71: +case 0x9E71: +case 0x9072: +case 0x9272: +case 0x9472: +case 0x9672: +case 0x9872: +case 0x9A72: +case 0x9C72: +case 0x9E72: +case 0x9073: +case 0x9273: +case 0x9473: +case 0x9673: +case 0x9873: +case 0x9A73: +case 0x9C73: +case 0x9E73: +case 0x9074: +case 0x9274: +case 0x9474: +case 0x9674: +case 0x9874: +case 0x9A74: +case 0x9C74: +case 0x9E74: +case 0x9075: +case 0x9275: +case 0x9475: +case 0x9675: +case 0x9875: +case 0x9A75: +case 0x9C75: +case 0x9E75: +case 0x9076: +case 0x9276: +case 0x9476: +case 0x9676: +case 0x9876: +case 0x9A76: +case 0x9C76: +case 0x9E76: +case 0x9077: +case 0x9277: +case 0x9477: +case 0x9677: +case 0x9877: +case 0x9A77: +case 0x9C77: +case 0x9E77: + +// SUBaD +case 0x9070: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x9278: +case 0x9478: +case 0x9678: +case 0x9878: +case 0x9A78: +case 0x9C78: +case 0x9E78: + +// SUBaD +case 0x9078: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x9279: +case 0x9479: +case 0x9679: +case 0x9879: +case 0x9A79: +case 0x9C79: +case 0x9E79: + +// SUBaD +case 0x9079: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x927A: +case 0x947A: +case 0x967A: +case 0x987A: +case 0x9A7A: +case 0x9C7A: +case 0x9E7A: + +// SUBaD +case 0x907A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0x927B: +case 0x947B: +case 0x967B: +case 0x987B: +case 0x9A7B: +case 0x9C7B: +case 0x9E7B: + +// SUBaD +case 0x907B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0x927C: +case 0x947C: +case 0x967C: +case 0x987C: +case 0x9A7C: +case 0x9C7C: +case 0x9E7C: + +// SUBaD +case 0x907C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x925F: +case 0x945F: +case 0x965F: +case 0x985F: +case 0x9A5F: +case 0x9C5F: +case 0x9E5F: + +// SUBaD +case 0x905F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0x9267: +case 0x9467: +case 0x9667: +case 0x9867: +case 0x9A67: +case 0x9C67: +case 0x9E67: + +// SUBaD +case 0x9067: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0x9280: +case 0x9480: +case 0x9680: +case 0x9880: +case 0x9A80: +case 0x9C80: +case 0x9E80: +case 0x9081: +case 0x9281: +case 0x9481: +case 0x9681: +case 0x9881: +case 0x9A81: +case 0x9C81: +case 0x9E81: +case 0x9082: +case 0x9282: +case 0x9482: +case 0x9682: +case 0x9882: +case 0x9A82: +case 0x9C82: +case 0x9E82: +case 0x9083: +case 0x9283: +case 0x9483: +case 0x9683: +case 0x9883: +case 0x9A83: +case 0x9C83: +case 0x9E83: +case 0x9084: +case 0x9284: +case 0x9484: +case 0x9684: +case 0x9884: +case 0x9A84: +case 0x9C84: +case 0x9E84: +case 0x9085: +case 0x9285: +case 0x9485: +case 0x9685: +case 0x9885: +case 0x9A85: +case 0x9C85: +case 0x9E85: +case 0x9086: +case 0x9286: +case 0x9486: +case 0x9686: +case 0x9886: +case 0x9A86: +case 0x9C86: +case 0x9E86: +case 0x9087: +case 0x9287: +case 0x9487: +case 0x9687: +case 0x9887: +case 0x9A87: +case 0x9C87: +case 0x9E87: + +// SUBaD +case 0x9080: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0x9288: +case 0x9488: +case 0x9688: +case 0x9888: +case 0x9A88: +case 0x9C88: +case 0x9E88: +case 0x9089: +case 0x9289: +case 0x9489: +case 0x9689: +case 0x9889: +case 0x9A89: +case 0x9C89: +case 0x9E89: +case 0x908A: +case 0x928A: +case 0x948A: +case 0x968A: +case 0x988A: +case 0x9A8A: +case 0x9C8A: +case 0x9E8A: +case 0x908B: +case 0x928B: +case 0x948B: +case 0x968B: +case 0x988B: +case 0x9A8B: +case 0x9C8B: +case 0x9E8B: +case 0x908C: +case 0x928C: +case 0x948C: +case 0x968C: +case 0x988C: +case 0x9A8C: +case 0x9C8C: +case 0x9E8C: +case 0x908D: +case 0x928D: +case 0x948D: +case 0x968D: +case 0x988D: +case 0x9A8D: +case 0x9C8D: +case 0x9E8D: +case 0x908E: +case 0x928E: +case 0x948E: +case 0x968E: +case 0x988E: +case 0x9A8E: +case 0x9C8E: +case 0x9E8E: +case 0x908F: +case 0x928F: +case 0x948F: +case 0x968F: +case 0x988F: +case 0x9A8F: +case 0x9C8F: +case 0x9E8F: + +// SUBaD +case 0x9088: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0x9290: +case 0x9490: +case 0x9690: +case 0x9890: +case 0x9A90: +case 0x9C90: +case 0x9E90: +case 0x9091: +case 0x9291: +case 0x9491: +case 0x9691: +case 0x9891: +case 0x9A91: +case 0x9C91: +case 0x9E91: +case 0x9092: +case 0x9292: +case 0x9492: +case 0x9692: +case 0x9892: +case 0x9A92: +case 0x9C92: +case 0x9E92: +case 0x9093: +case 0x9293: +case 0x9493: +case 0x9693: +case 0x9893: +case 0x9A93: +case 0x9C93: +case 0x9E93: +case 0x9094: +case 0x9294: +case 0x9494: +case 0x9694: +case 0x9894: +case 0x9A94: +case 0x9C94: +case 0x9E94: +case 0x9095: +case 0x9295: +case 0x9495: +case 0x9695: +case 0x9895: +case 0x9A95: +case 0x9C95: +case 0x9E95: +case 0x9096: +case 0x9296: +case 0x9496: +case 0x9696: +case 0x9896: +case 0x9A96: +case 0x9C96: +case 0x9E96: +case 0x9097: +case 0x9297: +case 0x9497: +case 0x9697: +case 0x9897: +case 0x9A97: +case 0x9C97: +case 0x9E97: + +// SUBaD +case 0x9090: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x9298: +case 0x9498: +case 0x9698: +case 0x9898: +case 0x9A98: +case 0x9C98: +case 0x9E98: +case 0x9099: +case 0x9299: +case 0x9499: +case 0x9699: +case 0x9899: +case 0x9A99: +case 0x9C99: +case 0x9E99: +case 0x909A: +case 0x929A: +case 0x949A: +case 0x969A: +case 0x989A: +case 0x9A9A: +case 0x9C9A: +case 0x9E9A: +case 0x909B: +case 0x929B: +case 0x949B: +case 0x969B: +case 0x989B: +case 0x9A9B: +case 0x9C9B: +case 0x9E9B: +case 0x909C: +case 0x929C: +case 0x949C: +case 0x969C: +case 0x989C: +case 0x9A9C: +case 0x9C9C: +case 0x9E9C: +case 0x909D: +case 0x929D: +case 0x949D: +case 0x969D: +case 0x989D: +case 0x9A9D: +case 0x9C9D: +case 0x9E9D: +case 0x909E: +case 0x929E: +case 0x949E: +case 0x969E: +case 0x989E: +case 0x9A9E: +case 0x9C9E: +case 0x9E9E: + +// SUBaD +case 0x9098: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x92A0: +case 0x94A0: +case 0x96A0: +case 0x98A0: +case 0x9AA0: +case 0x9CA0: +case 0x9EA0: +case 0x90A1: +case 0x92A1: +case 0x94A1: +case 0x96A1: +case 0x98A1: +case 0x9AA1: +case 0x9CA1: +case 0x9EA1: +case 0x90A2: +case 0x92A2: +case 0x94A2: +case 0x96A2: +case 0x98A2: +case 0x9AA2: +case 0x9CA2: +case 0x9EA2: +case 0x90A3: +case 0x92A3: +case 0x94A3: +case 0x96A3: +case 0x98A3: +case 0x9AA3: +case 0x9CA3: +case 0x9EA3: +case 0x90A4: +case 0x92A4: +case 0x94A4: +case 0x96A4: +case 0x98A4: +case 0x9AA4: +case 0x9CA4: +case 0x9EA4: +case 0x90A5: +case 0x92A5: +case 0x94A5: +case 0x96A5: +case 0x98A5: +case 0x9AA5: +case 0x9CA5: +case 0x9EA5: +case 0x90A6: +case 0x92A6: +case 0x94A6: +case 0x96A6: +case 0x98A6: +case 0x9AA6: +case 0x9CA6: +case 0x9EA6: + +// SUBaD +case 0x90A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x92A8: +case 0x94A8: +case 0x96A8: +case 0x98A8: +case 0x9AA8: +case 0x9CA8: +case 0x9EA8: +case 0x90A9: +case 0x92A9: +case 0x94A9: +case 0x96A9: +case 0x98A9: +case 0x9AA9: +case 0x9CA9: +case 0x9EA9: +case 0x90AA: +case 0x92AA: +case 0x94AA: +case 0x96AA: +case 0x98AA: +case 0x9AAA: +case 0x9CAA: +case 0x9EAA: +case 0x90AB: +case 0x92AB: +case 0x94AB: +case 0x96AB: +case 0x98AB: +case 0x9AAB: +case 0x9CAB: +case 0x9EAB: +case 0x90AC: +case 0x92AC: +case 0x94AC: +case 0x96AC: +case 0x98AC: +case 0x9AAC: +case 0x9CAC: +case 0x9EAC: +case 0x90AD: +case 0x92AD: +case 0x94AD: +case 0x96AD: +case 0x98AD: +case 0x9AAD: +case 0x9CAD: +case 0x9EAD: +case 0x90AE: +case 0x92AE: +case 0x94AE: +case 0x96AE: +case 0x98AE: +case 0x9AAE: +case 0x9CAE: +case 0x9EAE: +case 0x90AF: +case 0x92AF: +case 0x94AF: +case 0x96AF: +case 0x98AF: +case 0x9AAF: +case 0x9CAF: +case 0x9EAF: + +// SUBaD +case 0x90A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x92B0: +case 0x94B0: +case 0x96B0: +case 0x98B0: +case 0x9AB0: +case 0x9CB0: +case 0x9EB0: +case 0x90B1: +case 0x92B1: +case 0x94B1: +case 0x96B1: +case 0x98B1: +case 0x9AB1: +case 0x9CB1: +case 0x9EB1: +case 0x90B2: +case 0x92B2: +case 0x94B2: +case 0x96B2: +case 0x98B2: +case 0x9AB2: +case 0x9CB2: +case 0x9EB2: +case 0x90B3: +case 0x92B3: +case 0x94B3: +case 0x96B3: +case 0x98B3: +case 0x9AB3: +case 0x9CB3: +case 0x9EB3: +case 0x90B4: +case 0x92B4: +case 0x94B4: +case 0x96B4: +case 0x98B4: +case 0x9AB4: +case 0x9CB4: +case 0x9EB4: +case 0x90B5: +case 0x92B5: +case 0x94B5: +case 0x96B5: +case 0x98B5: +case 0x9AB5: +case 0x9CB5: +case 0x9EB5: +case 0x90B6: +case 0x92B6: +case 0x94B6: +case 0x96B6: +case 0x98B6: +case 0x9AB6: +case 0x9CB6: +case 0x9EB6: +case 0x90B7: +case 0x92B7: +case 0x94B7: +case 0x96B7: +case 0x98B7: +case 0x9AB7: +case 0x9CB7: +case 0x9EB7: + +// SUBaD +case 0x90B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0x92B8: +case 0x94B8: +case 0x96B8: +case 0x98B8: +case 0x9AB8: +case 0x9CB8: +case 0x9EB8: + +// SUBaD +case 0x90B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x92B9: +case 0x94B9: +case 0x96B9: +case 0x98B9: +case 0x9AB9: +case 0x9CB9: +case 0x9EB9: + +// SUBaD +case 0x90B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(24) +case 0x92BA: +case 0x94BA: +case 0x96BA: +case 0x98BA: +case 0x9ABA: +case 0x9CBA: +case 0x9EBA: + +// SUBaD +case 0x90BA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0x92BB: +case 0x94BB: +case 0x96BB: +case 0x98BB: +case 0x9ABB: +case 0x9CBB: +case 0x9EBB: + +// SUBaD +case 0x90BB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0x92BC: +case 0x94BC: +case 0x96BC: +case 0x98BC: +case 0x9ABC: +case 0x9CBC: +case 0x9EBC: + +// SUBaD +case 0x90BC: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(14) +case 0x929F: +case 0x949F: +case 0x969F: +case 0x989F: +case 0x9A9F: +case 0x9C9F: +case 0x9E9F: + +// SUBaD +case 0x909F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0x92A7: +case 0x94A7: +case 0x96A7: +case 0x98A7: +case 0x9AA7: +case 0x9CA7: +case 0x9EA7: + +// SUBaD +case 0x90A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0x9310: +case 0x9510: +case 0x9710: +case 0x9910: +case 0x9B10: +case 0x9D10: +case 0x9F10: +case 0x9111: +case 0x9311: +case 0x9511: +case 0x9711: +case 0x9911: +case 0x9B11: +case 0x9D11: +case 0x9F11: +case 0x9112: +case 0x9312: +case 0x9512: +case 0x9712: +case 0x9912: +case 0x9B12: +case 0x9D12: +case 0x9F12: +case 0x9113: +case 0x9313: +case 0x9513: +case 0x9713: +case 0x9913: +case 0x9B13: +case 0x9D13: +case 0x9F13: +case 0x9114: +case 0x9314: +case 0x9514: +case 0x9714: +case 0x9914: +case 0x9B14: +case 0x9D14: +case 0x9F14: +case 0x9115: +case 0x9315: +case 0x9515: +case 0x9715: +case 0x9915: +case 0x9B15: +case 0x9D15: +case 0x9F15: +case 0x9116: +case 0x9316: +case 0x9516: +case 0x9716: +case 0x9916: +case 0x9B16: +case 0x9D16: +case 0x9F16: +case 0x9117: +case 0x9317: +case 0x9517: +case 0x9717: +case 0x9917: +case 0x9B17: +case 0x9D17: +case 0x9F17: + +// SUBDa +case 0x9110: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x9318: +case 0x9518: +case 0x9718: +case 0x9918: +case 0x9B18: +case 0x9D18: +case 0x9F18: +case 0x9119: +case 0x9319: +case 0x9519: +case 0x9719: +case 0x9919: +case 0x9B19: +case 0x9D19: +case 0x9F19: +case 0x911A: +case 0x931A: +case 0x951A: +case 0x971A: +case 0x991A: +case 0x9B1A: +case 0x9D1A: +case 0x9F1A: +case 0x911B: +case 0x931B: +case 0x951B: +case 0x971B: +case 0x991B: +case 0x9B1B: +case 0x9D1B: +case 0x9F1B: +case 0x911C: +case 0x931C: +case 0x951C: +case 0x971C: +case 0x991C: +case 0x9B1C: +case 0x9D1C: +case 0x9F1C: +case 0x911D: +case 0x931D: +case 0x951D: +case 0x971D: +case 0x991D: +case 0x9B1D: +case 0x9D1D: +case 0x9F1D: +case 0x911E: +case 0x931E: +case 0x951E: +case 0x971E: +case 0x991E: +case 0x9B1E: +case 0x9D1E: +case 0x9F1E: + +// SUBDa +case 0x9118: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x9320: +case 0x9520: +case 0x9720: +case 0x9920: +case 0x9B20: +case 0x9D20: +case 0x9F20: +case 0x9121: +case 0x9321: +case 0x9521: +case 0x9721: +case 0x9921: +case 0x9B21: +case 0x9D21: +case 0x9F21: +case 0x9122: +case 0x9322: +case 0x9522: +case 0x9722: +case 0x9922: +case 0x9B22: +case 0x9D22: +case 0x9F22: +case 0x9123: +case 0x9323: +case 0x9523: +case 0x9723: +case 0x9923: +case 0x9B23: +case 0x9D23: +case 0x9F23: +case 0x9124: +case 0x9324: +case 0x9524: +case 0x9724: +case 0x9924: +case 0x9B24: +case 0x9D24: +case 0x9F24: +case 0x9125: +case 0x9325: +case 0x9525: +case 0x9725: +case 0x9925: +case 0x9B25: +case 0x9D25: +case 0x9F25: +case 0x9126: +case 0x9326: +case 0x9526: +case 0x9726: +case 0x9926: +case 0x9B26: +case 0x9D26: +case 0x9F26: + +// SUBDa +case 0x9120: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x9328: +case 0x9528: +case 0x9728: +case 0x9928: +case 0x9B28: +case 0x9D28: +case 0x9F28: +case 0x9129: +case 0x9329: +case 0x9529: +case 0x9729: +case 0x9929: +case 0x9B29: +case 0x9D29: +case 0x9F29: +case 0x912A: +case 0x932A: +case 0x952A: +case 0x972A: +case 0x992A: +case 0x9B2A: +case 0x9D2A: +case 0x9F2A: +case 0x912B: +case 0x932B: +case 0x952B: +case 0x972B: +case 0x992B: +case 0x9B2B: +case 0x9D2B: +case 0x9F2B: +case 0x912C: +case 0x932C: +case 0x952C: +case 0x972C: +case 0x992C: +case 0x9B2C: +case 0x9D2C: +case 0x9F2C: +case 0x912D: +case 0x932D: +case 0x952D: +case 0x972D: +case 0x992D: +case 0x9B2D: +case 0x9D2D: +case 0x9F2D: +case 0x912E: +case 0x932E: +case 0x952E: +case 0x972E: +case 0x992E: +case 0x9B2E: +case 0x9D2E: +case 0x9F2E: +case 0x912F: +case 0x932F: +case 0x952F: +case 0x972F: +case 0x992F: +case 0x9B2F: +case 0x9D2F: +case 0x9F2F: + +// SUBDa +case 0x9128: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x9330: +case 0x9530: +case 0x9730: +case 0x9930: +case 0x9B30: +case 0x9D30: +case 0x9F30: +case 0x9131: +case 0x9331: +case 0x9531: +case 0x9731: +case 0x9931: +case 0x9B31: +case 0x9D31: +case 0x9F31: +case 0x9132: +case 0x9332: +case 0x9532: +case 0x9732: +case 0x9932: +case 0x9B32: +case 0x9D32: +case 0x9F32: +case 0x9133: +case 0x9333: +case 0x9533: +case 0x9733: +case 0x9933: +case 0x9B33: +case 0x9D33: +case 0x9F33: +case 0x9134: +case 0x9334: +case 0x9534: +case 0x9734: +case 0x9934: +case 0x9B34: +case 0x9D34: +case 0x9F34: +case 0x9135: +case 0x9335: +case 0x9535: +case 0x9735: +case 0x9935: +case 0x9B35: +case 0x9D35: +case 0x9F35: +case 0x9136: +case 0x9336: +case 0x9536: +case 0x9736: +case 0x9936: +case 0x9B36: +case 0x9D36: +case 0x9F36: +case 0x9137: +case 0x9337: +case 0x9537: +case 0x9737: +case 0x9937: +case 0x9B37: +case 0x9D37: +case 0x9F37: + +// SUBDa +case 0x9130: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x9338: +case 0x9538: +case 0x9738: +case 0x9938: +case 0x9B38: +case 0x9D38: +case 0x9F38: + +// SUBDa +case 0x9138: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0x9339: +case 0x9539: +case 0x9739: +case 0x9939: +case 0x9B39: +case 0x9D39: +case 0x9F39: + +// SUBDa +case 0x9139: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0x931F: +case 0x951F: +case 0x971F: +case 0x991F: +case 0x9B1F: +case 0x9D1F: +case 0x9F1F: + +// SUBDa +case 0x911F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0x9327: +case 0x9527: +case 0x9727: +case 0x9927: +case 0x9B27: +case 0x9D27: +case 0x9F27: + +// SUBDa +case 0x9127: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0x9350: +case 0x9550: +case 0x9750: +case 0x9950: +case 0x9B50: +case 0x9D50: +case 0x9F50: +case 0x9151: +case 0x9351: +case 0x9551: +case 0x9751: +case 0x9951: +case 0x9B51: +case 0x9D51: +case 0x9F51: +case 0x9152: +case 0x9352: +case 0x9552: +case 0x9752: +case 0x9952: +case 0x9B52: +case 0x9D52: +case 0x9F52: +case 0x9153: +case 0x9353: +case 0x9553: +case 0x9753: +case 0x9953: +case 0x9B53: +case 0x9D53: +case 0x9F53: +case 0x9154: +case 0x9354: +case 0x9554: +case 0x9754: +case 0x9954: +case 0x9B54: +case 0x9D54: +case 0x9F54: +case 0x9155: +case 0x9355: +case 0x9555: +case 0x9755: +case 0x9955: +case 0x9B55: +case 0x9D55: +case 0x9F55: +case 0x9156: +case 0x9356: +case 0x9556: +case 0x9756: +case 0x9956: +case 0x9B56: +case 0x9D56: +case 0x9F56: +case 0x9157: +case 0x9357: +case 0x9557: +case 0x9757: +case 0x9957: +case 0x9B57: +case 0x9D57: +case 0x9F57: + +// SUBDa +case 0x9150: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x9358: +case 0x9558: +case 0x9758: +case 0x9958: +case 0x9B58: +case 0x9D58: +case 0x9F58: +case 0x9159: +case 0x9359: +case 0x9559: +case 0x9759: +case 0x9959: +case 0x9B59: +case 0x9D59: +case 0x9F59: +case 0x915A: +case 0x935A: +case 0x955A: +case 0x975A: +case 0x995A: +case 0x9B5A: +case 0x9D5A: +case 0x9F5A: +case 0x915B: +case 0x935B: +case 0x955B: +case 0x975B: +case 0x995B: +case 0x9B5B: +case 0x9D5B: +case 0x9F5B: +case 0x915C: +case 0x935C: +case 0x955C: +case 0x975C: +case 0x995C: +case 0x9B5C: +case 0x9D5C: +case 0x9F5C: +case 0x915D: +case 0x935D: +case 0x955D: +case 0x975D: +case 0x995D: +case 0x9B5D: +case 0x9D5D: +case 0x9F5D: +case 0x915E: +case 0x935E: +case 0x955E: +case 0x975E: +case 0x995E: +case 0x9B5E: +case 0x9D5E: +case 0x9F5E: + +// SUBDa +case 0x9158: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x9360: +case 0x9560: +case 0x9760: +case 0x9960: +case 0x9B60: +case 0x9D60: +case 0x9F60: +case 0x9161: +case 0x9361: +case 0x9561: +case 0x9761: +case 0x9961: +case 0x9B61: +case 0x9D61: +case 0x9F61: +case 0x9162: +case 0x9362: +case 0x9562: +case 0x9762: +case 0x9962: +case 0x9B62: +case 0x9D62: +case 0x9F62: +case 0x9163: +case 0x9363: +case 0x9563: +case 0x9763: +case 0x9963: +case 0x9B63: +case 0x9D63: +case 0x9F63: +case 0x9164: +case 0x9364: +case 0x9564: +case 0x9764: +case 0x9964: +case 0x9B64: +case 0x9D64: +case 0x9F64: +case 0x9165: +case 0x9365: +case 0x9565: +case 0x9765: +case 0x9965: +case 0x9B65: +case 0x9D65: +case 0x9F65: +case 0x9166: +case 0x9366: +case 0x9566: +case 0x9766: +case 0x9966: +case 0x9B66: +case 0x9D66: +case 0x9F66: + +// SUBDa +case 0x9160: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x9368: +case 0x9568: +case 0x9768: +case 0x9968: +case 0x9B68: +case 0x9D68: +case 0x9F68: +case 0x9169: +case 0x9369: +case 0x9569: +case 0x9769: +case 0x9969: +case 0x9B69: +case 0x9D69: +case 0x9F69: +case 0x916A: +case 0x936A: +case 0x956A: +case 0x976A: +case 0x996A: +case 0x9B6A: +case 0x9D6A: +case 0x9F6A: +case 0x916B: +case 0x936B: +case 0x956B: +case 0x976B: +case 0x996B: +case 0x9B6B: +case 0x9D6B: +case 0x9F6B: +case 0x916C: +case 0x936C: +case 0x956C: +case 0x976C: +case 0x996C: +case 0x9B6C: +case 0x9D6C: +case 0x9F6C: +case 0x916D: +case 0x936D: +case 0x956D: +case 0x976D: +case 0x996D: +case 0x9B6D: +case 0x9D6D: +case 0x9F6D: +case 0x916E: +case 0x936E: +case 0x956E: +case 0x976E: +case 0x996E: +case 0x9B6E: +case 0x9D6E: +case 0x9F6E: +case 0x916F: +case 0x936F: +case 0x956F: +case 0x976F: +case 0x996F: +case 0x9B6F: +case 0x9D6F: +case 0x9F6F: + +// SUBDa +case 0x9168: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x9370: +case 0x9570: +case 0x9770: +case 0x9970: +case 0x9B70: +case 0x9D70: +case 0x9F70: +case 0x9171: +case 0x9371: +case 0x9571: +case 0x9771: +case 0x9971: +case 0x9B71: +case 0x9D71: +case 0x9F71: +case 0x9172: +case 0x9372: +case 0x9572: +case 0x9772: +case 0x9972: +case 0x9B72: +case 0x9D72: +case 0x9F72: +case 0x9173: +case 0x9373: +case 0x9573: +case 0x9773: +case 0x9973: +case 0x9B73: +case 0x9D73: +case 0x9F73: +case 0x9174: +case 0x9374: +case 0x9574: +case 0x9774: +case 0x9974: +case 0x9B74: +case 0x9D74: +case 0x9F74: +case 0x9175: +case 0x9375: +case 0x9575: +case 0x9775: +case 0x9975: +case 0x9B75: +case 0x9D75: +case 0x9F75: +case 0x9176: +case 0x9376: +case 0x9576: +case 0x9776: +case 0x9976: +case 0x9B76: +case 0x9D76: +case 0x9F76: +case 0x9177: +case 0x9377: +case 0x9577: +case 0x9777: +case 0x9977: +case 0x9B77: +case 0x9D77: +case 0x9F77: + +// SUBDa +case 0x9170: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x9378: +case 0x9578: +case 0x9778: +case 0x9978: +case 0x9B78: +case 0x9D78: +case 0x9F78: + +// SUBDa +case 0x9178: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0x9379: +case 0x9579: +case 0x9779: +case 0x9979: +case 0x9B79: +case 0x9D79: +case 0x9F79: + +// SUBDa +case 0x9179: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0x935F: +case 0x955F: +case 0x975F: +case 0x995F: +case 0x9B5F: +case 0x9D5F: +case 0x9F5F: + +// SUBDa +case 0x915F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0x9367: +case 0x9567: +case 0x9767: +case 0x9967: +case 0x9B67: +case 0x9D67: +case 0x9F67: + +// SUBDa +case 0x9167: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0x9390: +case 0x9590: +case 0x9790: +case 0x9990: +case 0x9B90: +case 0x9D90: +case 0x9F90: +case 0x9191: +case 0x9391: +case 0x9591: +case 0x9791: +case 0x9991: +case 0x9B91: +case 0x9D91: +case 0x9F91: +case 0x9192: +case 0x9392: +case 0x9592: +case 0x9792: +case 0x9992: +case 0x9B92: +case 0x9D92: +case 0x9F92: +case 0x9193: +case 0x9393: +case 0x9593: +case 0x9793: +case 0x9993: +case 0x9B93: +case 0x9D93: +case 0x9F93: +case 0x9194: +case 0x9394: +case 0x9594: +case 0x9794: +case 0x9994: +case 0x9B94: +case 0x9D94: +case 0x9F94: +case 0x9195: +case 0x9395: +case 0x9595: +case 0x9795: +case 0x9995: +case 0x9B95: +case 0x9D95: +case 0x9F95: +case 0x9196: +case 0x9396: +case 0x9596: +case 0x9796: +case 0x9996: +case 0x9B96: +case 0x9D96: +case 0x9F96: +case 0x9197: +case 0x9397: +case 0x9597: +case 0x9797: +case 0x9997: +case 0x9B97: +case 0x9D97: +case 0x9F97: + +// SUBDa +case 0x9190: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x9398: +case 0x9598: +case 0x9798: +case 0x9998: +case 0x9B98: +case 0x9D98: +case 0x9F98: +case 0x9199: +case 0x9399: +case 0x9599: +case 0x9799: +case 0x9999: +case 0x9B99: +case 0x9D99: +case 0x9F99: +case 0x919A: +case 0x939A: +case 0x959A: +case 0x979A: +case 0x999A: +case 0x9B9A: +case 0x9D9A: +case 0x9F9A: +case 0x919B: +case 0x939B: +case 0x959B: +case 0x979B: +case 0x999B: +case 0x9B9B: +case 0x9D9B: +case 0x9F9B: +case 0x919C: +case 0x939C: +case 0x959C: +case 0x979C: +case 0x999C: +case 0x9B9C: +case 0x9D9C: +case 0x9F9C: +case 0x919D: +case 0x939D: +case 0x959D: +case 0x979D: +case 0x999D: +case 0x9B9D: +case 0x9D9D: +case 0x9F9D: +case 0x919E: +case 0x939E: +case 0x959E: +case 0x979E: +case 0x999E: +case 0x9B9E: +case 0x9D9E: +case 0x9F9E: + +// SUBDa +case 0x9198: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x93A0: +case 0x95A0: +case 0x97A0: +case 0x99A0: +case 0x9BA0: +case 0x9DA0: +case 0x9FA0: +case 0x91A1: +case 0x93A1: +case 0x95A1: +case 0x97A1: +case 0x99A1: +case 0x9BA1: +case 0x9DA1: +case 0x9FA1: +case 0x91A2: +case 0x93A2: +case 0x95A2: +case 0x97A2: +case 0x99A2: +case 0x9BA2: +case 0x9DA2: +case 0x9FA2: +case 0x91A3: +case 0x93A3: +case 0x95A3: +case 0x97A3: +case 0x99A3: +case 0x9BA3: +case 0x9DA3: +case 0x9FA3: +case 0x91A4: +case 0x93A4: +case 0x95A4: +case 0x97A4: +case 0x99A4: +case 0x9BA4: +case 0x9DA4: +case 0x9FA4: +case 0x91A5: +case 0x93A5: +case 0x95A5: +case 0x97A5: +case 0x99A5: +case 0x9BA5: +case 0x9DA5: +case 0x9FA5: +case 0x91A6: +case 0x93A6: +case 0x95A6: +case 0x97A6: +case 0x99A6: +case 0x9BA6: +case 0x9DA6: +case 0x9FA6: + +// SUBDa +case 0x91A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x93A8: +case 0x95A8: +case 0x97A8: +case 0x99A8: +case 0x9BA8: +case 0x9DA8: +case 0x9FA8: +case 0x91A9: +case 0x93A9: +case 0x95A9: +case 0x97A9: +case 0x99A9: +case 0x9BA9: +case 0x9DA9: +case 0x9FA9: +case 0x91AA: +case 0x93AA: +case 0x95AA: +case 0x97AA: +case 0x99AA: +case 0x9BAA: +case 0x9DAA: +case 0x9FAA: +case 0x91AB: +case 0x93AB: +case 0x95AB: +case 0x97AB: +case 0x99AB: +case 0x9BAB: +case 0x9DAB: +case 0x9FAB: +case 0x91AC: +case 0x93AC: +case 0x95AC: +case 0x97AC: +case 0x99AC: +case 0x9BAC: +case 0x9DAC: +case 0x9FAC: +case 0x91AD: +case 0x93AD: +case 0x95AD: +case 0x97AD: +case 0x99AD: +case 0x9BAD: +case 0x9DAD: +case 0x9FAD: +case 0x91AE: +case 0x93AE: +case 0x95AE: +case 0x97AE: +case 0x99AE: +case 0x9BAE: +case 0x9DAE: +case 0x9FAE: +case 0x91AF: +case 0x93AF: +case 0x95AF: +case 0x97AF: +case 0x99AF: +case 0x9BAF: +case 0x9DAF: +case 0x9FAF: + +// SUBDa +case 0x91A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x93B0: +case 0x95B0: +case 0x97B0: +case 0x99B0: +case 0x9BB0: +case 0x9DB0: +case 0x9FB0: +case 0x91B1: +case 0x93B1: +case 0x95B1: +case 0x97B1: +case 0x99B1: +case 0x9BB1: +case 0x9DB1: +case 0x9FB1: +case 0x91B2: +case 0x93B2: +case 0x95B2: +case 0x97B2: +case 0x99B2: +case 0x9BB2: +case 0x9DB2: +case 0x9FB2: +case 0x91B3: +case 0x93B3: +case 0x95B3: +case 0x97B3: +case 0x99B3: +case 0x9BB3: +case 0x9DB3: +case 0x9FB3: +case 0x91B4: +case 0x93B4: +case 0x95B4: +case 0x97B4: +case 0x99B4: +case 0x9BB4: +case 0x9DB4: +case 0x9FB4: +case 0x91B5: +case 0x93B5: +case 0x95B5: +case 0x97B5: +case 0x99B5: +case 0x9BB5: +case 0x9DB5: +case 0x9FB5: +case 0x91B6: +case 0x93B6: +case 0x95B6: +case 0x97B6: +case 0x99B6: +case 0x9BB6: +case 0x9DB6: +case 0x9FB6: +case 0x91B7: +case 0x93B7: +case 0x95B7: +case 0x97B7: +case 0x99B7: +case 0x9BB7: +case 0x9DB7: +case 0x9FB7: + +// SUBDa +case 0x91B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0x93B8: +case 0x95B8: +case 0x97B8: +case 0x99B8: +case 0x9BB8: +case 0x9DB8: +case 0x9FB8: + +// SUBDa +case 0x91B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0x93B9: +case 0x95B9: +case 0x97B9: +case 0x99B9: +case 0x9BB9: +case 0x9DB9: +case 0x9FB9: + +// SUBDa +case 0x91B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0x939F: +case 0x959F: +case 0x979F: +case 0x999F: +case 0x9B9F: +case 0x9D9F: +case 0x9F9F: + +// SUBDa +case 0x919F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0x93A7: +case 0x95A7: +case 0x97A7: +case 0x99A7: +case 0x9BA7: +case 0x9DA7: +case 0x9FA7: + +// SUBDa +case 0x91A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0x9300: +case 0x9500: +case 0x9700: +case 0x9900: +case 0x9B00: +case 0x9D00: +case 0x9F00: +case 0x9101: +case 0x9301: +case 0x9501: +case 0x9701: +case 0x9901: +case 0x9B01: +case 0x9D01: +case 0x9F01: +case 0x9102: +case 0x9302: +case 0x9502: +case 0x9702: +case 0x9902: +case 0x9B02: +case 0x9D02: +case 0x9F02: +case 0x9103: +case 0x9303: +case 0x9503: +case 0x9703: +case 0x9903: +case 0x9B03: +case 0x9D03: +case 0x9F03: +case 0x9104: +case 0x9304: +case 0x9504: +case 0x9704: +case 0x9904: +case 0x9B04: +case 0x9D04: +case 0x9F04: +case 0x9105: +case 0x9305: +case 0x9505: +case 0x9705: +case 0x9905: +case 0x9B05: +case 0x9D05: +case 0x9F05: +case 0x9106: +case 0x9306: +case 0x9506: +case 0x9706: +case 0x9906: +case 0x9B06: +case 0x9D06: +case 0x9F06: +case 0x9107: +case 0x9307: +case 0x9507: +case 0x9707: +case 0x9907: +case 0x9B07: +case 0x9D07: +case 0x9F07: + +// SUBX +case 0x9100: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ |= res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9340: +case 0x9540: +case 0x9740: +case 0x9940: +case 0x9B40: +case 0x9D40: +case 0x9F40: +case 0x9141: +case 0x9341: +case 0x9541: +case 0x9741: +case 0x9941: +case 0x9B41: +case 0x9D41: +case 0x9F41: +case 0x9142: +case 0x9342: +case 0x9542: +case 0x9742: +case 0x9942: +case 0x9B42: +case 0x9D42: +case 0x9F42: +case 0x9143: +case 0x9343: +case 0x9543: +case 0x9743: +case 0x9943: +case 0x9B43: +case 0x9D43: +case 0x9F43: +case 0x9144: +case 0x9344: +case 0x9544: +case 0x9744: +case 0x9944: +case 0x9B44: +case 0x9D44: +case 0x9F44: +case 0x9145: +case 0x9345: +case 0x9545: +case 0x9745: +case 0x9945: +case 0x9B45: +case 0x9D45: +case 0x9F45: +case 0x9146: +case 0x9346: +case 0x9546: +case 0x9746: +case 0x9946: +case 0x9B46: +case 0x9D46: +case 0x9F46: +case 0x9147: +case 0x9347: +case 0x9547: +case 0x9747: +case 0x9947: +case 0x9B47: +case 0x9D47: +case 0x9F47: + +// SUBX +case 0x9140: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0x9380: +case 0x9580: +case 0x9780: +case 0x9980: +case 0x9B80: +case 0x9D80: +case 0x9F80: +case 0x9181: +case 0x9381: +case 0x9581: +case 0x9781: +case 0x9981: +case 0x9B81: +case 0x9D81: +case 0x9F81: +case 0x9182: +case 0x9382: +case 0x9582: +case 0x9782: +case 0x9982: +case 0x9B82: +case 0x9D82: +case 0x9F82: +case 0x9183: +case 0x9383: +case 0x9583: +case 0x9783: +case 0x9983: +case 0x9B83: +case 0x9D83: +case 0x9F83: +case 0x9184: +case 0x9384: +case 0x9584: +case 0x9784: +case 0x9984: +case 0x9B84: +case 0x9D84: +case 0x9F84: +case 0x9185: +case 0x9385: +case 0x9585: +case 0x9785: +case 0x9985: +case 0x9B85: +case 0x9D85: +case 0x9F85: +case 0x9186: +case 0x9386: +case 0x9586: +case 0x9786: +case 0x9986: +case 0x9B86: +case 0x9D86: +case 0x9F86: +case 0x9187: +case 0x9387: +case 0x9587: +case 0x9787: +case 0x9987: +case 0x9B87: +case 0x9D87: +case 0x9F87: + +// SUBX +case 0x9180: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0x9308: +case 0x9508: +case 0x9708: +case 0x9908: +case 0x9B08: +case 0x9D08: +case 0x9109: +case 0x9309: +case 0x9509: +case 0x9709: +case 0x9909: +case 0x9B09: +case 0x9D09: +case 0x910A: +case 0x930A: +case 0x950A: +case 0x970A: +case 0x990A: +case 0x9B0A: +case 0x9D0A: +case 0x910B: +case 0x930B: +case 0x950B: +case 0x970B: +case 0x990B: +case 0x9B0B: +case 0x9D0B: +case 0x910C: +case 0x930C: +case 0x950C: +case 0x970C: +case 0x990C: +case 0x9B0C: +case 0x9D0C: +case 0x910D: +case 0x930D: +case 0x950D: +case 0x970D: +case 0x990D: +case 0x9B0D: +case 0x9D0D: +case 0x910E: +case 0x930E: +case 0x950E: +case 0x970E: +case 0x990E: +case 0x9B0E: +case 0x9D0E: + +// SUBXM +case 0x9108: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x9348: +case 0x9548: +case 0x9748: +case 0x9948: +case 0x9B48: +case 0x9D48: +case 0x9149: +case 0x9349: +case 0x9549: +case 0x9749: +case 0x9949: +case 0x9B49: +case 0x9D49: +case 0x914A: +case 0x934A: +case 0x954A: +case 0x974A: +case 0x994A: +case 0x9B4A: +case 0x9D4A: +case 0x914B: +case 0x934B: +case 0x954B: +case 0x974B: +case 0x994B: +case 0x9B4B: +case 0x9D4B: +case 0x914C: +case 0x934C: +case 0x954C: +case 0x974C: +case 0x994C: +case 0x9B4C: +case 0x9D4C: +case 0x914D: +case 0x934D: +case 0x954D: +case 0x974D: +case 0x994D: +case 0x9B4D: +case 0x9D4D: +case 0x914E: +case 0x934E: +case 0x954E: +case 0x974E: +case 0x994E: +case 0x9B4E: +case 0x9D4E: + +// SUBXM +case 0x9148: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_WORD_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x9388: +case 0x9588: +case 0x9788: +case 0x9988: +case 0x9B88: +case 0x9D88: +case 0x9189: +case 0x9389: +case 0x9589: +case 0x9789: +case 0x9989: +case 0x9B89: +case 0x9D89: +case 0x918A: +case 0x938A: +case 0x958A: +case 0x978A: +case 0x998A: +case 0x9B8A: +case 0x9D8A: +case 0x918B: +case 0x938B: +case 0x958B: +case 0x978B: +case 0x998B: +case 0x9B8B: +case 0x9D8B: +case 0x918C: +case 0x938C: +case 0x958C: +case 0x978C: +case 0x998C: +case 0x9B8C: +case 0x9D8C: +case 0x918D: +case 0x938D: +case 0x958D: +case 0x978D: +case 0x998D: +case 0x9B8D: +case 0x9D8D: +case 0x918E: +case 0x938E: +case 0x958E: +case 0x978E: +case 0x998E: +case 0x9B8E: +case 0x9D8E: + +// SUBXM +case 0x9188: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_LONG_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x930F: +case 0x950F: +case 0x970F: +case 0x990F: +case 0x9B0F: +case 0x9D0F: + +// SUBX7M +case 0x910F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x934F: +case 0x954F: +case 0x974F: +case 0x994F: +case 0x9B4F: +case 0x9D4F: + +// SUBX7M +case 0x914F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_WORD_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x938F: +case 0x958F: +case 0x978F: +case 0x998F: +case 0x9B8F: +case 0x9D8F: + +// SUBX7M +case 0x918F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_LONG_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x9F09: +case 0x9F0A: +case 0x9F0B: +case 0x9F0C: +case 0x9F0D: +case 0x9F0E: + +// SUBXM7 +case 0x9F08: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0x9F49: +case 0x9F4A: +case 0x9F4B: +case 0x9F4C: +case 0x9F4D: +case 0x9F4E: + +// SUBXM7 +case 0x9F48: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_WORD_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0x9F89: +case 0x9F8A: +case 0x9F8B: +case 0x9F8C: +case 0x9F8D: +case 0x9F8E: + +// SUBXM7 +case 0x9F88: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + READ_LONG_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// SUBX7M7 +case 0x9F0F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// SUBX7M7 +case 0x9F4F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_WORD_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// SUBX7M7 +case 0x9F8F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + READ_LONG_F(adr, dst) + res = dst - src - ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0x92C0: +case 0x94C0: +case 0x96C0: +case 0x98C0: +case 0x9AC0: +case 0x9CC0: +case 0x9EC0: +case 0x90C1: +case 0x92C1: +case 0x94C1: +case 0x96C1: +case 0x98C1: +case 0x9AC1: +case 0x9CC1: +case 0x9EC1: +case 0x90C2: +case 0x92C2: +case 0x94C2: +case 0x96C2: +case 0x98C2: +case 0x9AC2: +case 0x9CC2: +case 0x9EC2: +case 0x90C3: +case 0x92C3: +case 0x94C3: +case 0x96C3: +case 0x98C3: +case 0x9AC3: +case 0x9CC3: +case 0x9EC3: +case 0x90C4: +case 0x92C4: +case 0x94C4: +case 0x96C4: +case 0x98C4: +case 0x9AC4: +case 0x9CC4: +case 0x9EC4: +case 0x90C5: +case 0x92C5: +case 0x94C5: +case 0x96C5: +case 0x98C5: +case 0x9AC5: +case 0x9CC5: +case 0x9EC5: +case 0x90C6: +case 0x92C6: +case 0x94C6: +case 0x96C6: +case 0x98C6: +case 0x9AC6: +case 0x9CC6: +case 0x9EC6: +case 0x90C7: +case 0x92C7: +case 0x94C7: +case 0x96C7: +case 0x98C7: +case 0x9AC7: +case 0x9CC7: +case 0x9EC7: + +// SUBA +case 0x90C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x92C8: +case 0x94C8: +case 0x96C8: +case 0x98C8: +case 0x9AC8: +case 0x9CC8: +case 0x9EC8: +case 0x90C9: +case 0x92C9: +case 0x94C9: +case 0x96C9: +case 0x98C9: +case 0x9AC9: +case 0x9CC9: +case 0x9EC9: +case 0x90CA: +case 0x92CA: +case 0x94CA: +case 0x96CA: +case 0x98CA: +case 0x9ACA: +case 0x9CCA: +case 0x9ECA: +case 0x90CB: +case 0x92CB: +case 0x94CB: +case 0x96CB: +case 0x98CB: +case 0x9ACB: +case 0x9CCB: +case 0x9ECB: +case 0x90CC: +case 0x92CC: +case 0x94CC: +case 0x96CC: +case 0x98CC: +case 0x9ACC: +case 0x9CCC: +case 0x9ECC: +case 0x90CD: +case 0x92CD: +case 0x94CD: +case 0x96CD: +case 0x98CD: +case 0x9ACD: +case 0x9CCD: +case 0x9ECD: +case 0x90CE: +case 0x92CE: +case 0x94CE: +case 0x96CE: +case 0x98CE: +case 0x9ACE: +case 0x9CCE: +case 0x9ECE: +case 0x90CF: +case 0x92CF: +case 0x94CF: +case 0x96CF: +case 0x98CF: +case 0x9ACF: +case 0x9CCF: +case 0x9ECF: + +// SUBA +case 0x90C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0x92D0: +case 0x94D0: +case 0x96D0: +case 0x98D0: +case 0x9AD0: +case 0x9CD0: +case 0x9ED0: +case 0x90D1: +case 0x92D1: +case 0x94D1: +case 0x96D1: +case 0x98D1: +case 0x9AD1: +case 0x9CD1: +case 0x9ED1: +case 0x90D2: +case 0x92D2: +case 0x94D2: +case 0x96D2: +case 0x98D2: +case 0x9AD2: +case 0x9CD2: +case 0x9ED2: +case 0x90D3: +case 0x92D3: +case 0x94D3: +case 0x96D3: +case 0x98D3: +case 0x9AD3: +case 0x9CD3: +case 0x9ED3: +case 0x90D4: +case 0x92D4: +case 0x94D4: +case 0x96D4: +case 0x98D4: +case 0x9AD4: +case 0x9CD4: +case 0x9ED4: +case 0x90D5: +case 0x92D5: +case 0x94D5: +case 0x96D5: +case 0x98D5: +case 0x9AD5: +case 0x9CD5: +case 0x9ED5: +case 0x90D6: +case 0x92D6: +case 0x94D6: +case 0x96D6: +case 0x98D6: +case 0x9AD6: +case 0x9CD6: +case 0x9ED6: +case 0x90D7: +case 0x92D7: +case 0x94D7: +case 0x96D7: +case 0x98D7: +case 0x9AD7: +case 0x9CD7: +case 0x9ED7: + +// SUBA +case 0x90D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x92D8: +case 0x94D8: +case 0x96D8: +case 0x98D8: +case 0x9AD8: +case 0x9CD8: +case 0x9ED8: +case 0x90D9: +case 0x92D9: +case 0x94D9: +case 0x96D9: +case 0x98D9: +case 0x9AD9: +case 0x9CD9: +case 0x9ED9: +case 0x90DA: +case 0x92DA: +case 0x94DA: +case 0x96DA: +case 0x98DA: +case 0x9ADA: +case 0x9CDA: +case 0x9EDA: +case 0x90DB: +case 0x92DB: +case 0x94DB: +case 0x96DB: +case 0x98DB: +case 0x9ADB: +case 0x9CDB: +case 0x9EDB: +case 0x90DC: +case 0x92DC: +case 0x94DC: +case 0x96DC: +case 0x98DC: +case 0x9ADC: +case 0x9CDC: +case 0x9EDC: +case 0x90DD: +case 0x92DD: +case 0x94DD: +case 0x96DD: +case 0x98DD: +case 0x9ADD: +case 0x9CDD: +case 0x9EDD: +case 0x90DE: +case 0x92DE: +case 0x94DE: +case 0x96DE: +case 0x98DE: +case 0x9ADE: +case 0x9CDE: +case 0x9EDE: + +// SUBA +case 0x90D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x92E0: +case 0x94E0: +case 0x96E0: +case 0x98E0: +case 0x9AE0: +case 0x9CE0: +case 0x9EE0: +case 0x90E1: +case 0x92E1: +case 0x94E1: +case 0x96E1: +case 0x98E1: +case 0x9AE1: +case 0x9CE1: +case 0x9EE1: +case 0x90E2: +case 0x92E2: +case 0x94E2: +case 0x96E2: +case 0x98E2: +case 0x9AE2: +case 0x9CE2: +case 0x9EE2: +case 0x90E3: +case 0x92E3: +case 0x94E3: +case 0x96E3: +case 0x98E3: +case 0x9AE3: +case 0x9CE3: +case 0x9EE3: +case 0x90E4: +case 0x92E4: +case 0x94E4: +case 0x96E4: +case 0x98E4: +case 0x9AE4: +case 0x9CE4: +case 0x9EE4: +case 0x90E5: +case 0x92E5: +case 0x94E5: +case 0x96E5: +case 0x98E5: +case 0x9AE5: +case 0x9CE5: +case 0x9EE5: +case 0x90E6: +case 0x92E6: +case 0x94E6: +case 0x96E6: +case 0x98E6: +case 0x9AE6: +case 0x9CE6: +case 0x9EE6: + +// SUBA +case 0x90E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0x92E8: +case 0x94E8: +case 0x96E8: +case 0x98E8: +case 0x9AE8: +case 0x9CE8: +case 0x9EE8: +case 0x90E9: +case 0x92E9: +case 0x94E9: +case 0x96E9: +case 0x98E9: +case 0x9AE9: +case 0x9CE9: +case 0x9EE9: +case 0x90EA: +case 0x92EA: +case 0x94EA: +case 0x96EA: +case 0x98EA: +case 0x9AEA: +case 0x9CEA: +case 0x9EEA: +case 0x90EB: +case 0x92EB: +case 0x94EB: +case 0x96EB: +case 0x98EB: +case 0x9AEB: +case 0x9CEB: +case 0x9EEB: +case 0x90EC: +case 0x92EC: +case 0x94EC: +case 0x96EC: +case 0x98EC: +case 0x9AEC: +case 0x9CEC: +case 0x9EEC: +case 0x90ED: +case 0x92ED: +case 0x94ED: +case 0x96ED: +case 0x98ED: +case 0x9AED: +case 0x9CED: +case 0x9EED: +case 0x90EE: +case 0x92EE: +case 0x94EE: +case 0x96EE: +case 0x98EE: +case 0x9AEE: +case 0x9CEE: +case 0x9EEE: +case 0x90EF: +case 0x92EF: +case 0x94EF: +case 0x96EF: +case 0x98EF: +case 0x9AEF: +case 0x9CEF: +case 0x9EEF: + +// SUBA +case 0x90E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x92F0: +case 0x94F0: +case 0x96F0: +case 0x98F0: +case 0x9AF0: +case 0x9CF0: +case 0x9EF0: +case 0x90F1: +case 0x92F1: +case 0x94F1: +case 0x96F1: +case 0x98F1: +case 0x9AF1: +case 0x9CF1: +case 0x9EF1: +case 0x90F2: +case 0x92F2: +case 0x94F2: +case 0x96F2: +case 0x98F2: +case 0x9AF2: +case 0x9CF2: +case 0x9EF2: +case 0x90F3: +case 0x92F3: +case 0x94F3: +case 0x96F3: +case 0x98F3: +case 0x9AF3: +case 0x9CF3: +case 0x9EF3: +case 0x90F4: +case 0x92F4: +case 0x94F4: +case 0x96F4: +case 0x98F4: +case 0x9AF4: +case 0x9CF4: +case 0x9EF4: +case 0x90F5: +case 0x92F5: +case 0x94F5: +case 0x96F5: +case 0x98F5: +case 0x9AF5: +case 0x9CF5: +case 0x9EF5: +case 0x90F6: +case 0x92F6: +case 0x94F6: +case 0x96F6: +case 0x98F6: +case 0x9AF6: +case 0x9CF6: +case 0x9EF6: +case 0x90F7: +case 0x92F7: +case 0x94F7: +case 0x96F7: +case 0x98F7: +case 0x9AF7: +case 0x9CF7: +case 0x9EF7: + +// SUBA +case 0x90F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0x92F8: +case 0x94F8: +case 0x96F8: +case 0x98F8: +case 0x9AF8: +case 0x9CF8: +case 0x9EF8: + +// SUBA +case 0x90F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x92F9: +case 0x94F9: +case 0x96F9: +case 0x98F9: +case 0x9AF9: +case 0x9CF9: +case 0x9EF9: + +// SUBA +case 0x90F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0x92FA: +case 0x94FA: +case 0x96FA: +case 0x98FA: +case 0x9AFA: +case 0x9CFA: +case 0x9EFA: + +// SUBA +case 0x90FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x92FB: +case 0x94FB: +case 0x96FB: +case 0x98FB: +case 0x9AFB: +case 0x9CFB: +case 0x9EFB: + +// SUBA +case 0x90FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0x92FC: +case 0x94FC: +case 0x96FC: +case 0x98FC: +case 0x9AFC: +case 0x9CFC: +case 0x9EFC: + +// SUBA +case 0x90FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)FETCH_WORD; + PC += 2; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) +case 0x92DF: +case 0x94DF: +case 0x96DF: +case 0x98DF: +case 0x9ADF: +case 0x9CDF: +case 0x9EDF: + +// SUBA +case 0x90DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0x92E7: +case 0x94E7: +case 0x96E7: +case 0x98E7: +case 0x9AE7: +case 0x9CE7: +case 0x9EE7: + +// SUBA +case 0x90E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0x93C0: +case 0x95C0: +case 0x97C0: +case 0x99C0: +case 0x9BC0: +case 0x9DC0: +case 0x9FC0: +case 0x91C1: +case 0x93C1: +case 0x95C1: +case 0x97C1: +case 0x99C1: +case 0x9BC1: +case 0x9DC1: +case 0x9FC1: +case 0x91C2: +case 0x93C2: +case 0x95C2: +case 0x97C2: +case 0x99C2: +case 0x9BC2: +case 0x9DC2: +case 0x9FC2: +case 0x91C3: +case 0x93C3: +case 0x95C3: +case 0x97C3: +case 0x99C3: +case 0x9BC3: +case 0x9DC3: +case 0x9FC3: +case 0x91C4: +case 0x93C4: +case 0x95C4: +case 0x97C4: +case 0x99C4: +case 0x9BC4: +case 0x9DC4: +case 0x9FC4: +case 0x91C5: +case 0x93C5: +case 0x95C5: +case 0x97C5: +case 0x99C5: +case 0x9BC5: +case 0x9DC5: +case 0x9FC5: +case 0x91C6: +case 0x93C6: +case 0x95C6: +case 0x97C6: +case 0x99C6: +case 0x9BC6: +case 0x9DC6: +case 0x9FC6: +case 0x91C7: +case 0x93C7: +case 0x95C7: +case 0x97C7: +case 0x99C7: +case 0x9BC7: +case 0x9DC7: +case 0x9FC7: + +// SUBA +case 0x91C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(6) +case 0x93C8: +case 0x95C8: +case 0x97C8: +case 0x99C8: +case 0x9BC8: +case 0x9DC8: +case 0x9FC8: +case 0x91C9: +case 0x93C9: +case 0x95C9: +case 0x97C9: +case 0x99C9: +case 0x9BC9: +case 0x9DC9: +case 0x9FC9: +case 0x91CA: +case 0x93CA: +case 0x95CA: +case 0x97CA: +case 0x99CA: +case 0x9BCA: +case 0x9DCA: +case 0x9FCA: +case 0x91CB: +case 0x93CB: +case 0x95CB: +case 0x97CB: +case 0x99CB: +case 0x9BCB: +case 0x9DCB: +case 0x9FCB: +case 0x91CC: +case 0x93CC: +case 0x95CC: +case 0x97CC: +case 0x99CC: +case 0x9BCC: +case 0x9DCC: +case 0x9FCC: +case 0x91CD: +case 0x93CD: +case 0x95CD: +case 0x97CD: +case 0x99CD: +case 0x9BCD: +case 0x9DCD: +case 0x9FCD: +case 0x91CE: +case 0x93CE: +case 0x95CE: +case 0x97CE: +case 0x99CE: +case 0x9BCE: +case 0x9DCE: +case 0x9FCE: +case 0x91CF: +case 0x93CF: +case 0x95CF: +case 0x97CF: +case 0x99CF: +case 0x9BCF: +case 0x9DCF: +case 0x9FCF: + +// SUBA +case 0x91C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(6) +case 0x93D0: +case 0x95D0: +case 0x97D0: +case 0x99D0: +case 0x9BD0: +case 0x9DD0: +case 0x9FD0: +case 0x91D1: +case 0x93D1: +case 0x95D1: +case 0x97D1: +case 0x99D1: +case 0x9BD1: +case 0x9DD1: +case 0x9FD1: +case 0x91D2: +case 0x93D2: +case 0x95D2: +case 0x97D2: +case 0x99D2: +case 0x9BD2: +case 0x9DD2: +case 0x9FD2: +case 0x91D3: +case 0x93D3: +case 0x95D3: +case 0x97D3: +case 0x99D3: +case 0x9BD3: +case 0x9DD3: +case 0x9FD3: +case 0x91D4: +case 0x93D4: +case 0x95D4: +case 0x97D4: +case 0x99D4: +case 0x9BD4: +case 0x9DD4: +case 0x9FD4: +case 0x91D5: +case 0x93D5: +case 0x95D5: +case 0x97D5: +case 0x99D5: +case 0x9BD5: +case 0x9DD5: +case 0x9FD5: +case 0x91D6: +case 0x93D6: +case 0x95D6: +case 0x97D6: +case 0x99D6: +case 0x9BD6: +case 0x9DD6: +case 0x9FD6: +case 0x91D7: +case 0x93D7: +case 0x95D7: +case 0x97D7: +case 0x99D7: +case 0x9BD7: +case 0x9DD7: +case 0x9FD7: + +// SUBA +case 0x91D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x93D8: +case 0x95D8: +case 0x97D8: +case 0x99D8: +case 0x9BD8: +case 0x9DD8: +case 0x9FD8: +case 0x91D9: +case 0x93D9: +case 0x95D9: +case 0x97D9: +case 0x99D9: +case 0x9BD9: +case 0x9DD9: +case 0x9FD9: +case 0x91DA: +case 0x93DA: +case 0x95DA: +case 0x97DA: +case 0x99DA: +case 0x9BDA: +case 0x9DDA: +case 0x9FDA: +case 0x91DB: +case 0x93DB: +case 0x95DB: +case 0x97DB: +case 0x99DB: +case 0x9BDB: +case 0x9DDB: +case 0x9FDB: +case 0x91DC: +case 0x93DC: +case 0x95DC: +case 0x97DC: +case 0x99DC: +case 0x9BDC: +case 0x9DDC: +case 0x9FDC: +case 0x91DD: +case 0x93DD: +case 0x95DD: +case 0x97DD: +case 0x99DD: +case 0x9BDD: +case 0x9DDD: +case 0x9FDD: +case 0x91DE: +case 0x93DE: +case 0x95DE: +case 0x97DE: +case 0x99DE: +case 0x9BDE: +case 0x9DDE: +case 0x9FDE: + +// SUBA +case 0x91D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x93E0: +case 0x95E0: +case 0x97E0: +case 0x99E0: +case 0x9BE0: +case 0x9DE0: +case 0x9FE0: +case 0x91E1: +case 0x93E1: +case 0x95E1: +case 0x97E1: +case 0x99E1: +case 0x9BE1: +case 0x9DE1: +case 0x9FE1: +case 0x91E2: +case 0x93E2: +case 0x95E2: +case 0x97E2: +case 0x99E2: +case 0x9BE2: +case 0x9DE2: +case 0x9FE2: +case 0x91E3: +case 0x93E3: +case 0x95E3: +case 0x97E3: +case 0x99E3: +case 0x9BE3: +case 0x9DE3: +case 0x9FE3: +case 0x91E4: +case 0x93E4: +case 0x95E4: +case 0x97E4: +case 0x99E4: +case 0x9BE4: +case 0x9DE4: +case 0x9FE4: +case 0x91E5: +case 0x93E5: +case 0x95E5: +case 0x97E5: +case 0x99E5: +case 0x9BE5: +case 0x9DE5: +case 0x9FE5: +case 0x91E6: +case 0x93E6: +case 0x95E6: +case 0x97E6: +case 0x99E6: +case 0x9BE6: +case 0x9DE6: +case 0x9FE6: + +// SUBA +case 0x91E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0x93E8: +case 0x95E8: +case 0x97E8: +case 0x99E8: +case 0x9BE8: +case 0x9DE8: +case 0x9FE8: +case 0x91E9: +case 0x93E9: +case 0x95E9: +case 0x97E9: +case 0x99E9: +case 0x9BE9: +case 0x9DE9: +case 0x9FE9: +case 0x91EA: +case 0x93EA: +case 0x95EA: +case 0x97EA: +case 0x99EA: +case 0x9BEA: +case 0x9DEA: +case 0x9FEA: +case 0x91EB: +case 0x93EB: +case 0x95EB: +case 0x97EB: +case 0x99EB: +case 0x9BEB: +case 0x9DEB: +case 0x9FEB: +case 0x91EC: +case 0x93EC: +case 0x95EC: +case 0x97EC: +case 0x99EC: +case 0x9BEC: +case 0x9DEC: +case 0x9FEC: +case 0x91ED: +case 0x93ED: +case 0x95ED: +case 0x97ED: +case 0x99ED: +case 0x9BED: +case 0x9DED: +case 0x9FED: +case 0x91EE: +case 0x93EE: +case 0x95EE: +case 0x97EE: +case 0x99EE: +case 0x9BEE: +case 0x9DEE: +case 0x9FEE: +case 0x91EF: +case 0x93EF: +case 0x95EF: +case 0x97EF: +case 0x99EF: +case 0x9BEF: +case 0x9DEF: +case 0x9FEF: + +// SUBA +case 0x91E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0x93F0: +case 0x95F0: +case 0x97F0: +case 0x99F0: +case 0x9BF0: +case 0x9DF0: +case 0x9FF0: +case 0x91F1: +case 0x93F1: +case 0x95F1: +case 0x97F1: +case 0x99F1: +case 0x9BF1: +case 0x9DF1: +case 0x9FF1: +case 0x91F2: +case 0x93F2: +case 0x95F2: +case 0x97F2: +case 0x99F2: +case 0x9BF2: +case 0x9DF2: +case 0x9FF2: +case 0x91F3: +case 0x93F3: +case 0x95F3: +case 0x97F3: +case 0x99F3: +case 0x9BF3: +case 0x9DF3: +case 0x9FF3: +case 0x91F4: +case 0x93F4: +case 0x95F4: +case 0x97F4: +case 0x99F4: +case 0x9BF4: +case 0x9DF4: +case 0x9FF4: +case 0x91F5: +case 0x93F5: +case 0x95F5: +case 0x97F5: +case 0x99F5: +case 0x9BF5: +case 0x9DF5: +case 0x9FF5: +case 0x91F6: +case 0x93F6: +case 0x95F6: +case 0x97F6: +case 0x99F6: +case 0x9BF6: +case 0x9DF6: +case 0x9FF6: +case 0x91F7: +case 0x93F7: +case 0x95F7: +case 0x97F7: +case 0x99F7: +case 0x9BF7: +case 0x9DF7: +case 0x9FF7: + +// SUBA +case 0x91F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(22) +case 0x93F8: +case 0x95F8: +case 0x97F8: +case 0x99F8: +case 0x9BF8: +case 0x9DF8: +case 0x9FF8: + +// SUBA +case 0x91F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0x93F9: +case 0x95F9: +case 0x97F9: +case 0x99F9: +case 0x9BF9: +case 0x9DF9: +case 0x9FF9: + +// SUBA +case 0x91F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(24) +case 0x93FA: +case 0x95FA: +case 0x97FA: +case 0x99FA: +case 0x9BFA: +case 0x9DFA: +case 0x9FFA: + +// SUBA +case 0x91FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0x93FB: +case 0x95FB: +case 0x97FB: +case 0x99FB: +case 0x9BFB: +case 0x9DFB: +case 0x9FFB: + +// SUBA +case 0x91FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(22) +case 0x93FC: +case 0x95FC: +case 0x97FC: +case 0x99FC: +case 0x9BFC: +case 0x9DFC: +case 0x9FFC: + +// SUBA +case 0x91FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)FETCH_LONG; + PC += 4; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(14) +case 0x93DF: +case 0x95DF: +case 0x97DF: +case 0x99DF: +case 0x9BDF: +case 0x9DDF: +case 0x9FDF: + +// SUBA +case 0x91DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0x93E7: +case 0x95E7: +case 0x97E7: +case 0x99E7: +case 0x9BE7: +case 0x9DE7: +case 0x9FE7: + +// SUBA +case 0x91E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) diff --git a/yabause/src/c68k/c68k_opA.inc b/yabause/src/c68k/c68k_opA.inc new file mode 100644 index 0000000000..0bf57ce785 --- /dev/null +++ b/yabause/src/c68k/c68k_opA.inc @@ -0,0 +1,4118 @@ +case 0xA001: +case 0xA002: +case 0xA003: +case 0xA004: +case 0xA005: +case 0xA006: +case 0xA007: +case 0xA008: +case 0xA009: +case 0xA00A: +case 0xA00B: +case 0xA00C: +case 0xA00D: +case 0xA00E: +case 0xA00F: +case 0xA010: +case 0xA011: +case 0xA012: +case 0xA013: +case 0xA014: +case 0xA015: +case 0xA016: +case 0xA017: +case 0xA018: +case 0xA019: +case 0xA01A: +case 0xA01B: +case 0xA01C: +case 0xA01D: +case 0xA01E: +case 0xA01F: +case 0xA020: +case 0xA021: +case 0xA022: +case 0xA023: +case 0xA024: +case 0xA025: +case 0xA026: +case 0xA027: +case 0xA028: +case 0xA029: +case 0xA02A: +case 0xA02B: +case 0xA02C: +case 0xA02D: +case 0xA02E: +case 0xA02F: +case 0xA030: +case 0xA031: +case 0xA032: +case 0xA033: +case 0xA034: +case 0xA035: +case 0xA036: +case 0xA037: +case 0xA038: +case 0xA039: +case 0xA03A: +case 0xA03B: +case 0xA03C: +case 0xA03D: +case 0xA03E: +case 0xA03F: +case 0xA040: +case 0xA041: +case 0xA042: +case 0xA043: +case 0xA044: +case 0xA045: +case 0xA046: +case 0xA047: +case 0xA048: +case 0xA049: +case 0xA04A: +case 0xA04B: +case 0xA04C: +case 0xA04D: +case 0xA04E: +case 0xA04F: +case 0xA050: +case 0xA051: +case 0xA052: +case 0xA053: +case 0xA054: +case 0xA055: +case 0xA056: +case 0xA057: +case 0xA058: +case 0xA059: +case 0xA05A: +case 0xA05B: +case 0xA05C: +case 0xA05D: +case 0xA05E: +case 0xA05F: +case 0xA060: +case 0xA061: +case 0xA062: +case 0xA063: +case 0xA064: +case 0xA065: +case 0xA066: +case 0xA067: +case 0xA068: +case 0xA069: +case 0xA06A: +case 0xA06B: +case 0xA06C: +case 0xA06D: +case 0xA06E: +case 0xA06F: +case 0xA070: +case 0xA071: +case 0xA072: +case 0xA073: +case 0xA074: +case 0xA075: +case 0xA076: +case 0xA077: +case 0xA078: +case 0xA079: +case 0xA07A: +case 0xA07B: +case 0xA07C: +case 0xA07D: +case 0xA07E: +case 0xA07F: +case 0xA080: +case 0xA081: +case 0xA082: +case 0xA083: +case 0xA084: +case 0xA085: +case 0xA086: +case 0xA087: +case 0xA088: +case 0xA089: +case 0xA08A: +case 0xA08B: +case 0xA08C: +case 0xA08D: +case 0xA08E: +case 0xA08F: +case 0xA090: +case 0xA091: +case 0xA092: +case 0xA093: +case 0xA094: +case 0xA095: +case 0xA096: +case 0xA097: +case 0xA098: +case 0xA099: +case 0xA09A: +case 0xA09B: +case 0xA09C: +case 0xA09D: +case 0xA09E: +case 0xA09F: +case 0xA0A0: +case 0xA0A1: +case 0xA0A2: +case 0xA0A3: +case 0xA0A4: +case 0xA0A5: +case 0xA0A6: +case 0xA0A7: +case 0xA0A8: +case 0xA0A9: +case 0xA0AA: +case 0xA0AB: +case 0xA0AC: +case 0xA0AD: +case 0xA0AE: +case 0xA0AF: +case 0xA0B0: +case 0xA0B1: +case 0xA0B2: +case 0xA0B3: +case 0xA0B4: +case 0xA0B5: +case 0xA0B6: +case 0xA0B7: +case 0xA0B8: +case 0xA0B9: +case 0xA0BA: +case 0xA0BB: +case 0xA0BC: +case 0xA0BD: +case 0xA0BE: +case 0xA0BF: +case 0xA0C0: +case 0xA0C1: +case 0xA0C2: +case 0xA0C3: +case 0xA0C4: +case 0xA0C5: +case 0xA0C6: +case 0xA0C7: +case 0xA0C8: +case 0xA0C9: +case 0xA0CA: +case 0xA0CB: +case 0xA0CC: +case 0xA0CD: +case 0xA0CE: +case 0xA0CF: +case 0xA0D0: +case 0xA0D1: +case 0xA0D2: +case 0xA0D3: +case 0xA0D4: +case 0xA0D5: +case 0xA0D6: +case 0xA0D7: +case 0xA0D8: +case 0xA0D9: +case 0xA0DA: +case 0xA0DB: +case 0xA0DC: +case 0xA0DD: +case 0xA0DE: +case 0xA0DF: +case 0xA0E0: +case 0xA0E1: +case 0xA0E2: +case 0xA0E3: +case 0xA0E4: +case 0xA0E5: +case 0xA0E6: +case 0xA0E7: +case 0xA0E8: +case 0xA0E9: +case 0xA0EA: +case 0xA0EB: +case 0xA0EC: +case 0xA0ED: +case 0xA0EE: +case 0xA0EF: +case 0xA0F0: +case 0xA0F1: +case 0xA0F2: +case 0xA0F3: +case 0xA0F4: +case 0xA0F5: +case 0xA0F6: +case 0xA0F7: +case 0xA0F8: +case 0xA0F9: +case 0xA0FA: +case 0xA0FB: +case 0xA0FC: +case 0xA0FD: +case 0xA0FE: +case 0xA0FF: +case 0xA100: +case 0xA101: +case 0xA102: +case 0xA103: +case 0xA104: +case 0xA105: +case 0xA106: +case 0xA107: +case 0xA108: +case 0xA109: +case 0xA10A: +case 0xA10B: +case 0xA10C: +case 0xA10D: +case 0xA10E: +case 0xA10F: +case 0xA110: +case 0xA111: +case 0xA112: +case 0xA113: +case 0xA114: +case 0xA115: +case 0xA116: +case 0xA117: +case 0xA118: +case 0xA119: +case 0xA11A: +case 0xA11B: +case 0xA11C: +case 0xA11D: +case 0xA11E: +case 0xA11F: +case 0xA120: +case 0xA121: +case 0xA122: +case 0xA123: +case 0xA124: +case 0xA125: +case 0xA126: +case 0xA127: +case 0xA128: +case 0xA129: +case 0xA12A: +case 0xA12B: +case 0xA12C: +case 0xA12D: +case 0xA12E: +case 0xA12F: +case 0xA130: +case 0xA131: +case 0xA132: +case 0xA133: +case 0xA134: +case 0xA135: +case 0xA136: +case 0xA137: +case 0xA138: +case 0xA139: +case 0xA13A: +case 0xA13B: +case 0xA13C: +case 0xA13D: +case 0xA13E: +case 0xA13F: +case 0xA140: +case 0xA141: +case 0xA142: +case 0xA143: +case 0xA144: +case 0xA145: +case 0xA146: +case 0xA147: +case 0xA148: +case 0xA149: +case 0xA14A: +case 0xA14B: +case 0xA14C: +case 0xA14D: +case 0xA14E: +case 0xA14F: +case 0xA150: +case 0xA151: +case 0xA152: +case 0xA153: +case 0xA154: +case 0xA155: +case 0xA156: +case 0xA157: +case 0xA158: +case 0xA159: +case 0xA15A: +case 0xA15B: +case 0xA15C: +case 0xA15D: +case 0xA15E: +case 0xA15F: +case 0xA160: +case 0xA161: +case 0xA162: +case 0xA163: +case 0xA164: +case 0xA165: +case 0xA166: +case 0xA167: +case 0xA168: +case 0xA169: +case 0xA16A: +case 0xA16B: +case 0xA16C: +case 0xA16D: +case 0xA16E: +case 0xA16F: +case 0xA170: +case 0xA171: +case 0xA172: +case 0xA173: +case 0xA174: +case 0xA175: +case 0xA176: +case 0xA177: +case 0xA178: +case 0xA179: +case 0xA17A: +case 0xA17B: +case 0xA17C: +case 0xA17D: +case 0xA17E: +case 0xA17F: +case 0xA180: +case 0xA181: +case 0xA182: +case 0xA183: +case 0xA184: +case 0xA185: +case 0xA186: +case 0xA187: +case 0xA188: +case 0xA189: +case 0xA18A: +case 0xA18B: +case 0xA18C: +case 0xA18D: +case 0xA18E: +case 0xA18F: +case 0xA190: +case 0xA191: +case 0xA192: +case 0xA193: +case 0xA194: +case 0xA195: +case 0xA196: +case 0xA197: +case 0xA198: +case 0xA199: +case 0xA19A: +case 0xA19B: +case 0xA19C: +case 0xA19D: +case 0xA19E: +case 0xA19F: +case 0xA1A0: +case 0xA1A1: +case 0xA1A2: +case 0xA1A3: +case 0xA1A4: +case 0xA1A5: +case 0xA1A6: +case 0xA1A7: +case 0xA1A8: +case 0xA1A9: +case 0xA1AA: +case 0xA1AB: +case 0xA1AC: +case 0xA1AD: +case 0xA1AE: +case 0xA1AF: +case 0xA1B0: +case 0xA1B1: +case 0xA1B2: +case 0xA1B3: +case 0xA1B4: +case 0xA1B5: +case 0xA1B6: +case 0xA1B7: +case 0xA1B8: +case 0xA1B9: +case 0xA1BA: +case 0xA1BB: +case 0xA1BC: +case 0xA1BD: +case 0xA1BE: +case 0xA1BF: +case 0xA1C0: +case 0xA1C1: +case 0xA1C2: +case 0xA1C3: +case 0xA1C4: +case 0xA1C5: +case 0xA1C6: +case 0xA1C7: +case 0xA1C8: +case 0xA1C9: +case 0xA1CA: +case 0xA1CB: +case 0xA1CC: +case 0xA1CD: +case 0xA1CE: +case 0xA1CF: +case 0xA1D0: +case 0xA1D1: +case 0xA1D2: +case 0xA1D3: +case 0xA1D4: +case 0xA1D5: +case 0xA1D6: +case 0xA1D7: +case 0xA1D8: +case 0xA1D9: +case 0xA1DA: +case 0xA1DB: +case 0xA1DC: +case 0xA1DD: +case 0xA1DE: +case 0xA1DF: +case 0xA1E0: +case 0xA1E1: +case 0xA1E2: +case 0xA1E3: +case 0xA1E4: +case 0xA1E5: +case 0xA1E6: +case 0xA1E7: +case 0xA1E8: +case 0xA1E9: +case 0xA1EA: +case 0xA1EB: +case 0xA1EC: +case 0xA1ED: +case 0xA1EE: +case 0xA1EF: +case 0xA1F0: +case 0xA1F1: +case 0xA1F2: +case 0xA1F3: +case 0xA1F4: +case 0xA1F5: +case 0xA1F6: +case 0xA1F7: +case 0xA1F8: +case 0xA1F9: +case 0xA1FA: +case 0xA1FB: +case 0xA1FC: +case 0xA1FD: +case 0xA1FE: +case 0xA1FF: +case 0xA200: +case 0xA201: +case 0xA202: +case 0xA203: +case 0xA204: +case 0xA205: +case 0xA206: +case 0xA207: +case 0xA208: +case 0xA209: +case 0xA20A: +case 0xA20B: +case 0xA20C: +case 0xA20D: +case 0xA20E: +case 0xA20F: +case 0xA210: +case 0xA211: +case 0xA212: +case 0xA213: +case 0xA214: +case 0xA215: +case 0xA216: +case 0xA217: +case 0xA218: +case 0xA219: +case 0xA21A: +case 0xA21B: +case 0xA21C: +case 0xA21D: +case 0xA21E: +case 0xA21F: +case 0xA220: +case 0xA221: +case 0xA222: +case 0xA223: +case 0xA224: +case 0xA225: +case 0xA226: +case 0xA227: +case 0xA228: +case 0xA229: +case 0xA22A: +case 0xA22B: +case 0xA22C: +case 0xA22D: +case 0xA22E: +case 0xA22F: +case 0xA230: +case 0xA231: +case 0xA232: +case 0xA233: +case 0xA234: +case 0xA235: +case 0xA236: +case 0xA237: +case 0xA238: +case 0xA239: +case 0xA23A: +case 0xA23B: +case 0xA23C: +case 0xA23D: +case 0xA23E: +case 0xA23F: +case 0xA240: +case 0xA241: +case 0xA242: +case 0xA243: +case 0xA244: +case 0xA245: +case 0xA246: +case 0xA247: +case 0xA248: +case 0xA249: +case 0xA24A: +case 0xA24B: +case 0xA24C: +case 0xA24D: +case 0xA24E: +case 0xA24F: +case 0xA250: +case 0xA251: +case 0xA252: +case 0xA253: +case 0xA254: +case 0xA255: +case 0xA256: +case 0xA257: +case 0xA258: +case 0xA259: +case 0xA25A: +case 0xA25B: +case 0xA25C: +case 0xA25D: +case 0xA25E: +case 0xA25F: +case 0xA260: +case 0xA261: +case 0xA262: +case 0xA263: +case 0xA264: +case 0xA265: +case 0xA266: +case 0xA267: +case 0xA268: +case 0xA269: +case 0xA26A: +case 0xA26B: +case 0xA26C: +case 0xA26D: +case 0xA26E: +case 0xA26F: +case 0xA270: +case 0xA271: +case 0xA272: +case 0xA273: +case 0xA274: +case 0xA275: +case 0xA276: +case 0xA277: +case 0xA278: +case 0xA279: +case 0xA27A: +case 0xA27B: +case 0xA27C: +case 0xA27D: +case 0xA27E: +case 0xA27F: +case 0xA280: +case 0xA281: +case 0xA282: +case 0xA283: +case 0xA284: +case 0xA285: +case 0xA286: +case 0xA287: +case 0xA288: +case 0xA289: +case 0xA28A: +case 0xA28B: +case 0xA28C: +case 0xA28D: +case 0xA28E: +case 0xA28F: +case 0xA290: +case 0xA291: +case 0xA292: +case 0xA293: +case 0xA294: +case 0xA295: +case 0xA296: +case 0xA297: +case 0xA298: +case 0xA299: +case 0xA29A: +case 0xA29B: +case 0xA29C: +case 0xA29D: +case 0xA29E: +case 0xA29F: +case 0xA2A0: +case 0xA2A1: +case 0xA2A2: +case 0xA2A3: +case 0xA2A4: +case 0xA2A5: +case 0xA2A6: +case 0xA2A7: +case 0xA2A8: +case 0xA2A9: +case 0xA2AA: +case 0xA2AB: +case 0xA2AC: +case 0xA2AD: +case 0xA2AE: +case 0xA2AF: +case 0xA2B0: +case 0xA2B1: +case 0xA2B2: +case 0xA2B3: +case 0xA2B4: +case 0xA2B5: +case 0xA2B6: +case 0xA2B7: +case 0xA2B8: +case 0xA2B9: +case 0xA2BA: +case 0xA2BB: +case 0xA2BC: +case 0xA2BD: +case 0xA2BE: +case 0xA2BF: +case 0xA2C0: +case 0xA2C1: +case 0xA2C2: +case 0xA2C3: +case 0xA2C4: +case 0xA2C5: +case 0xA2C6: +case 0xA2C7: +case 0xA2C8: +case 0xA2C9: +case 0xA2CA: +case 0xA2CB: +case 0xA2CC: +case 0xA2CD: +case 0xA2CE: +case 0xA2CF: +case 0xA2D0: +case 0xA2D1: +case 0xA2D2: +case 0xA2D3: +case 0xA2D4: +case 0xA2D5: +case 0xA2D6: +case 0xA2D7: +case 0xA2D8: +case 0xA2D9: +case 0xA2DA: +case 0xA2DB: +case 0xA2DC: +case 0xA2DD: +case 0xA2DE: +case 0xA2DF: +case 0xA2E0: +case 0xA2E1: +case 0xA2E2: +case 0xA2E3: +case 0xA2E4: +case 0xA2E5: +case 0xA2E6: +case 0xA2E7: +case 0xA2E8: +case 0xA2E9: +case 0xA2EA: +case 0xA2EB: +case 0xA2EC: +case 0xA2ED: +case 0xA2EE: +case 0xA2EF: +case 0xA2F0: +case 0xA2F1: +case 0xA2F2: +case 0xA2F3: +case 0xA2F4: +case 0xA2F5: +case 0xA2F6: +case 0xA2F7: +case 0xA2F8: +case 0xA2F9: +case 0xA2FA: +case 0xA2FB: +case 0xA2FC: +case 0xA2FD: +case 0xA2FE: +case 0xA2FF: +case 0xA300: +case 0xA301: +case 0xA302: +case 0xA303: +case 0xA304: +case 0xA305: +case 0xA306: +case 0xA307: +case 0xA308: +case 0xA309: +case 0xA30A: +case 0xA30B: +case 0xA30C: +case 0xA30D: +case 0xA30E: +case 0xA30F: +case 0xA310: +case 0xA311: +case 0xA312: +case 0xA313: +case 0xA314: +case 0xA315: +case 0xA316: +case 0xA317: +case 0xA318: +case 0xA319: +case 0xA31A: +case 0xA31B: +case 0xA31C: +case 0xA31D: +case 0xA31E: +case 0xA31F: +case 0xA320: +case 0xA321: +case 0xA322: +case 0xA323: +case 0xA324: +case 0xA325: +case 0xA326: +case 0xA327: +case 0xA328: +case 0xA329: +case 0xA32A: +case 0xA32B: +case 0xA32C: +case 0xA32D: +case 0xA32E: +case 0xA32F: +case 0xA330: +case 0xA331: +case 0xA332: +case 0xA333: +case 0xA334: +case 0xA335: +case 0xA336: +case 0xA337: +case 0xA338: +case 0xA339: +case 0xA33A: +case 0xA33B: +case 0xA33C: +case 0xA33D: +case 0xA33E: +case 0xA33F: +case 0xA340: +case 0xA341: +case 0xA342: +case 0xA343: +case 0xA344: +case 0xA345: +case 0xA346: +case 0xA347: +case 0xA348: +case 0xA349: +case 0xA34A: +case 0xA34B: +case 0xA34C: +case 0xA34D: +case 0xA34E: +case 0xA34F: +case 0xA350: +case 0xA351: +case 0xA352: +case 0xA353: +case 0xA354: +case 0xA355: +case 0xA356: +case 0xA357: +case 0xA358: +case 0xA359: +case 0xA35A: +case 0xA35B: +case 0xA35C: +case 0xA35D: +case 0xA35E: +case 0xA35F: +case 0xA360: +case 0xA361: +case 0xA362: +case 0xA363: +case 0xA364: +case 0xA365: +case 0xA366: +case 0xA367: +case 0xA368: +case 0xA369: +case 0xA36A: +case 0xA36B: +case 0xA36C: +case 0xA36D: +case 0xA36E: +case 0xA36F: +case 0xA370: +case 0xA371: +case 0xA372: +case 0xA373: +case 0xA374: +case 0xA375: +case 0xA376: +case 0xA377: +case 0xA378: +case 0xA379: +case 0xA37A: +case 0xA37B: +case 0xA37C: +case 0xA37D: +case 0xA37E: +case 0xA37F: +case 0xA380: +case 0xA381: +case 0xA382: +case 0xA383: +case 0xA384: +case 0xA385: +case 0xA386: +case 0xA387: +case 0xA388: +case 0xA389: +case 0xA38A: +case 0xA38B: +case 0xA38C: +case 0xA38D: +case 0xA38E: +case 0xA38F: +case 0xA390: +case 0xA391: +case 0xA392: +case 0xA393: +case 0xA394: +case 0xA395: +case 0xA396: +case 0xA397: +case 0xA398: +case 0xA399: +case 0xA39A: +case 0xA39B: +case 0xA39C: +case 0xA39D: +case 0xA39E: +case 0xA39F: +case 0xA3A0: +case 0xA3A1: +case 0xA3A2: +case 0xA3A3: +case 0xA3A4: +case 0xA3A5: +case 0xA3A6: +case 0xA3A7: +case 0xA3A8: +case 0xA3A9: +case 0xA3AA: +case 0xA3AB: +case 0xA3AC: +case 0xA3AD: +case 0xA3AE: +case 0xA3AF: +case 0xA3B0: +case 0xA3B1: +case 0xA3B2: +case 0xA3B3: +case 0xA3B4: +case 0xA3B5: +case 0xA3B6: +case 0xA3B7: +case 0xA3B8: +case 0xA3B9: +case 0xA3BA: +case 0xA3BB: +case 0xA3BC: +case 0xA3BD: +case 0xA3BE: +case 0xA3BF: +case 0xA3C0: +case 0xA3C1: +case 0xA3C2: +case 0xA3C3: +case 0xA3C4: +case 0xA3C5: +case 0xA3C6: +case 0xA3C7: +case 0xA3C8: +case 0xA3C9: +case 0xA3CA: +case 0xA3CB: +case 0xA3CC: +case 0xA3CD: +case 0xA3CE: +case 0xA3CF: +case 0xA3D0: +case 0xA3D1: +case 0xA3D2: +case 0xA3D3: +case 0xA3D4: +case 0xA3D5: +case 0xA3D6: +case 0xA3D7: +case 0xA3D8: +case 0xA3D9: +case 0xA3DA: +case 0xA3DB: +case 0xA3DC: +case 0xA3DD: +case 0xA3DE: +case 0xA3DF: +case 0xA3E0: +case 0xA3E1: +case 0xA3E2: +case 0xA3E3: +case 0xA3E4: +case 0xA3E5: +case 0xA3E6: +case 0xA3E7: +case 0xA3E8: +case 0xA3E9: +case 0xA3EA: +case 0xA3EB: +case 0xA3EC: +case 0xA3ED: +case 0xA3EE: +case 0xA3EF: +case 0xA3F0: +case 0xA3F1: +case 0xA3F2: +case 0xA3F3: +case 0xA3F4: +case 0xA3F5: +case 0xA3F6: +case 0xA3F7: +case 0xA3F8: +case 0xA3F9: +case 0xA3FA: +case 0xA3FB: +case 0xA3FC: +case 0xA3FD: +case 0xA3FE: +case 0xA3FF: +case 0xA400: +case 0xA401: +case 0xA402: +case 0xA403: +case 0xA404: +case 0xA405: +case 0xA406: +case 0xA407: +case 0xA408: +case 0xA409: +case 0xA40A: +case 0xA40B: +case 0xA40C: +case 0xA40D: +case 0xA40E: +case 0xA40F: +case 0xA410: +case 0xA411: +case 0xA412: +case 0xA413: +case 0xA414: +case 0xA415: +case 0xA416: +case 0xA417: +case 0xA418: +case 0xA419: +case 0xA41A: +case 0xA41B: +case 0xA41C: +case 0xA41D: +case 0xA41E: +case 0xA41F: +case 0xA420: +case 0xA421: +case 0xA422: +case 0xA423: +case 0xA424: +case 0xA425: +case 0xA426: +case 0xA427: +case 0xA428: +case 0xA429: +case 0xA42A: +case 0xA42B: +case 0xA42C: +case 0xA42D: +case 0xA42E: +case 0xA42F: +case 0xA430: +case 0xA431: +case 0xA432: +case 0xA433: +case 0xA434: +case 0xA435: +case 0xA436: +case 0xA437: +case 0xA438: +case 0xA439: +case 0xA43A: +case 0xA43B: +case 0xA43C: +case 0xA43D: +case 0xA43E: +case 0xA43F: +case 0xA440: +case 0xA441: +case 0xA442: +case 0xA443: +case 0xA444: +case 0xA445: +case 0xA446: +case 0xA447: +case 0xA448: +case 0xA449: +case 0xA44A: +case 0xA44B: +case 0xA44C: +case 0xA44D: +case 0xA44E: +case 0xA44F: +case 0xA450: +case 0xA451: +case 0xA452: +case 0xA453: +case 0xA454: +case 0xA455: +case 0xA456: +case 0xA457: +case 0xA458: +case 0xA459: +case 0xA45A: +case 0xA45B: +case 0xA45C: +case 0xA45D: +case 0xA45E: +case 0xA45F: +case 0xA460: +case 0xA461: +case 0xA462: +case 0xA463: +case 0xA464: +case 0xA465: +case 0xA466: +case 0xA467: +case 0xA468: +case 0xA469: +case 0xA46A: +case 0xA46B: +case 0xA46C: +case 0xA46D: +case 0xA46E: +case 0xA46F: +case 0xA470: +case 0xA471: +case 0xA472: +case 0xA473: +case 0xA474: +case 0xA475: +case 0xA476: +case 0xA477: +case 0xA478: +case 0xA479: +case 0xA47A: +case 0xA47B: +case 0xA47C: +case 0xA47D: +case 0xA47E: +case 0xA47F: +case 0xA480: +case 0xA481: +case 0xA482: +case 0xA483: +case 0xA484: +case 0xA485: +case 0xA486: +case 0xA487: +case 0xA488: +case 0xA489: +case 0xA48A: +case 0xA48B: +case 0xA48C: +case 0xA48D: +case 0xA48E: +case 0xA48F: +case 0xA490: +case 0xA491: +case 0xA492: +case 0xA493: +case 0xA494: +case 0xA495: +case 0xA496: +case 0xA497: +case 0xA498: +case 0xA499: +case 0xA49A: +case 0xA49B: +case 0xA49C: +case 0xA49D: +case 0xA49E: +case 0xA49F: +case 0xA4A0: +case 0xA4A1: +case 0xA4A2: +case 0xA4A3: +case 0xA4A4: +case 0xA4A5: +case 0xA4A6: +case 0xA4A7: +case 0xA4A8: +case 0xA4A9: +case 0xA4AA: +case 0xA4AB: +case 0xA4AC: +case 0xA4AD: +case 0xA4AE: +case 0xA4AF: +case 0xA4B0: +case 0xA4B1: +case 0xA4B2: +case 0xA4B3: +case 0xA4B4: +case 0xA4B5: +case 0xA4B6: +case 0xA4B7: +case 0xA4B8: +case 0xA4B9: +case 0xA4BA: +case 0xA4BB: +case 0xA4BC: +case 0xA4BD: +case 0xA4BE: +case 0xA4BF: +case 0xA4C0: +case 0xA4C1: +case 0xA4C2: +case 0xA4C3: +case 0xA4C4: +case 0xA4C5: +case 0xA4C6: +case 0xA4C7: +case 0xA4C8: +case 0xA4C9: +case 0xA4CA: +case 0xA4CB: +case 0xA4CC: +case 0xA4CD: +case 0xA4CE: +case 0xA4CF: +case 0xA4D0: +case 0xA4D1: +case 0xA4D2: +case 0xA4D3: +case 0xA4D4: +case 0xA4D5: +case 0xA4D6: +case 0xA4D7: +case 0xA4D8: +case 0xA4D9: +case 0xA4DA: +case 0xA4DB: +case 0xA4DC: +case 0xA4DD: +case 0xA4DE: +case 0xA4DF: +case 0xA4E0: +case 0xA4E1: +case 0xA4E2: +case 0xA4E3: +case 0xA4E4: +case 0xA4E5: +case 0xA4E6: +case 0xA4E7: +case 0xA4E8: +case 0xA4E9: +case 0xA4EA: +case 0xA4EB: +case 0xA4EC: +case 0xA4ED: +case 0xA4EE: +case 0xA4EF: +case 0xA4F0: +case 0xA4F1: +case 0xA4F2: +case 0xA4F3: +case 0xA4F4: +case 0xA4F5: +case 0xA4F6: +case 0xA4F7: +case 0xA4F8: +case 0xA4F9: +case 0xA4FA: +case 0xA4FB: +case 0xA4FC: +case 0xA4FD: +case 0xA4FE: +case 0xA4FF: +case 0xA500: +case 0xA501: +case 0xA502: +case 0xA503: +case 0xA504: +case 0xA505: +case 0xA506: +case 0xA507: +case 0xA508: +case 0xA509: +case 0xA50A: +case 0xA50B: +case 0xA50C: +case 0xA50D: +case 0xA50E: +case 0xA50F: +case 0xA510: +case 0xA511: +case 0xA512: +case 0xA513: +case 0xA514: +case 0xA515: +case 0xA516: +case 0xA517: +case 0xA518: +case 0xA519: +case 0xA51A: +case 0xA51B: +case 0xA51C: +case 0xA51D: +case 0xA51E: +case 0xA51F: +case 0xA520: +case 0xA521: +case 0xA522: +case 0xA523: +case 0xA524: +case 0xA525: +case 0xA526: +case 0xA527: +case 0xA528: +case 0xA529: +case 0xA52A: +case 0xA52B: +case 0xA52C: +case 0xA52D: +case 0xA52E: +case 0xA52F: +case 0xA530: +case 0xA531: +case 0xA532: +case 0xA533: +case 0xA534: +case 0xA535: +case 0xA536: +case 0xA537: +case 0xA538: +case 0xA539: +case 0xA53A: +case 0xA53B: +case 0xA53C: +case 0xA53D: +case 0xA53E: +case 0xA53F: +case 0xA540: +case 0xA541: +case 0xA542: +case 0xA543: +case 0xA544: +case 0xA545: +case 0xA546: +case 0xA547: +case 0xA548: +case 0xA549: +case 0xA54A: +case 0xA54B: +case 0xA54C: +case 0xA54D: +case 0xA54E: +case 0xA54F: +case 0xA550: +case 0xA551: +case 0xA552: +case 0xA553: +case 0xA554: +case 0xA555: +case 0xA556: +case 0xA557: +case 0xA558: +case 0xA559: +case 0xA55A: +case 0xA55B: +case 0xA55C: +case 0xA55D: +case 0xA55E: +case 0xA55F: +case 0xA560: +case 0xA561: +case 0xA562: +case 0xA563: +case 0xA564: +case 0xA565: +case 0xA566: +case 0xA567: +case 0xA568: +case 0xA569: +case 0xA56A: +case 0xA56B: +case 0xA56C: +case 0xA56D: +case 0xA56E: +case 0xA56F: +case 0xA570: +case 0xA571: +case 0xA572: +case 0xA573: +case 0xA574: +case 0xA575: +case 0xA576: +case 0xA577: +case 0xA578: +case 0xA579: +case 0xA57A: +case 0xA57B: +case 0xA57C: +case 0xA57D: +case 0xA57E: +case 0xA57F: +case 0xA580: +case 0xA581: +case 0xA582: +case 0xA583: +case 0xA584: +case 0xA585: +case 0xA586: +case 0xA587: +case 0xA588: +case 0xA589: +case 0xA58A: +case 0xA58B: +case 0xA58C: +case 0xA58D: +case 0xA58E: +case 0xA58F: +case 0xA590: +case 0xA591: +case 0xA592: +case 0xA593: +case 0xA594: +case 0xA595: +case 0xA596: +case 0xA597: +case 0xA598: +case 0xA599: +case 0xA59A: +case 0xA59B: +case 0xA59C: +case 0xA59D: +case 0xA59E: +case 0xA59F: +case 0xA5A0: +case 0xA5A1: +case 0xA5A2: +case 0xA5A3: +case 0xA5A4: +case 0xA5A5: +case 0xA5A6: +case 0xA5A7: +case 0xA5A8: +case 0xA5A9: +case 0xA5AA: +case 0xA5AB: +case 0xA5AC: +case 0xA5AD: +case 0xA5AE: +case 0xA5AF: +case 0xA5B0: +case 0xA5B1: +case 0xA5B2: +case 0xA5B3: +case 0xA5B4: +case 0xA5B5: +case 0xA5B6: +case 0xA5B7: +case 0xA5B8: +case 0xA5B9: +case 0xA5BA: +case 0xA5BB: +case 0xA5BC: +case 0xA5BD: +case 0xA5BE: +case 0xA5BF: +case 0xA5C0: +case 0xA5C1: +case 0xA5C2: +case 0xA5C3: +case 0xA5C4: +case 0xA5C5: +case 0xA5C6: +case 0xA5C7: +case 0xA5C8: +case 0xA5C9: +case 0xA5CA: +case 0xA5CB: +case 0xA5CC: +case 0xA5CD: +case 0xA5CE: +case 0xA5CF: +case 0xA5D0: +case 0xA5D1: +case 0xA5D2: +case 0xA5D3: +case 0xA5D4: +case 0xA5D5: +case 0xA5D6: +case 0xA5D7: +case 0xA5D8: +case 0xA5D9: +case 0xA5DA: +case 0xA5DB: +case 0xA5DC: +case 0xA5DD: +case 0xA5DE: +case 0xA5DF: +case 0xA5E0: +case 0xA5E1: +case 0xA5E2: +case 0xA5E3: +case 0xA5E4: +case 0xA5E5: +case 0xA5E6: +case 0xA5E7: +case 0xA5E8: +case 0xA5E9: +case 0xA5EA: +case 0xA5EB: +case 0xA5EC: +case 0xA5ED: +case 0xA5EE: +case 0xA5EF: +case 0xA5F0: +case 0xA5F1: +case 0xA5F2: +case 0xA5F3: +case 0xA5F4: +case 0xA5F5: +case 0xA5F6: +case 0xA5F7: +case 0xA5F8: +case 0xA5F9: +case 0xA5FA: +case 0xA5FB: +case 0xA5FC: +case 0xA5FD: +case 0xA5FE: +case 0xA5FF: +case 0xA600: +case 0xA601: +case 0xA602: +case 0xA603: +case 0xA604: +case 0xA605: +case 0xA606: +case 0xA607: +case 0xA608: +case 0xA609: +case 0xA60A: +case 0xA60B: +case 0xA60C: +case 0xA60D: +case 0xA60E: +case 0xA60F: +case 0xA610: +case 0xA611: +case 0xA612: +case 0xA613: +case 0xA614: +case 0xA615: +case 0xA616: +case 0xA617: +case 0xA618: +case 0xA619: +case 0xA61A: +case 0xA61B: +case 0xA61C: +case 0xA61D: +case 0xA61E: +case 0xA61F: +case 0xA620: +case 0xA621: +case 0xA622: +case 0xA623: +case 0xA624: +case 0xA625: +case 0xA626: +case 0xA627: +case 0xA628: +case 0xA629: +case 0xA62A: +case 0xA62B: +case 0xA62C: +case 0xA62D: +case 0xA62E: +case 0xA62F: +case 0xA630: +case 0xA631: +case 0xA632: +case 0xA633: +case 0xA634: +case 0xA635: +case 0xA636: +case 0xA637: +case 0xA638: +case 0xA639: +case 0xA63A: +case 0xA63B: +case 0xA63C: +case 0xA63D: +case 0xA63E: +case 0xA63F: +case 0xA640: +case 0xA641: +case 0xA642: +case 0xA643: +case 0xA644: +case 0xA645: +case 0xA646: +case 0xA647: +case 0xA648: +case 0xA649: +case 0xA64A: +case 0xA64B: +case 0xA64C: +case 0xA64D: +case 0xA64E: +case 0xA64F: +case 0xA650: +case 0xA651: +case 0xA652: +case 0xA653: +case 0xA654: +case 0xA655: +case 0xA656: +case 0xA657: +case 0xA658: +case 0xA659: +case 0xA65A: +case 0xA65B: +case 0xA65C: +case 0xA65D: +case 0xA65E: +case 0xA65F: +case 0xA660: +case 0xA661: +case 0xA662: +case 0xA663: +case 0xA664: +case 0xA665: +case 0xA666: +case 0xA667: +case 0xA668: +case 0xA669: +case 0xA66A: +case 0xA66B: +case 0xA66C: +case 0xA66D: +case 0xA66E: +case 0xA66F: +case 0xA670: +case 0xA671: +case 0xA672: +case 0xA673: +case 0xA674: +case 0xA675: +case 0xA676: +case 0xA677: +case 0xA678: +case 0xA679: +case 0xA67A: +case 0xA67B: +case 0xA67C: +case 0xA67D: +case 0xA67E: +case 0xA67F: +case 0xA680: +case 0xA681: +case 0xA682: +case 0xA683: +case 0xA684: +case 0xA685: +case 0xA686: +case 0xA687: +case 0xA688: +case 0xA689: +case 0xA68A: +case 0xA68B: +case 0xA68C: +case 0xA68D: +case 0xA68E: +case 0xA68F: +case 0xA690: +case 0xA691: +case 0xA692: +case 0xA693: +case 0xA694: +case 0xA695: +case 0xA696: +case 0xA697: +case 0xA698: +case 0xA699: +case 0xA69A: +case 0xA69B: +case 0xA69C: +case 0xA69D: +case 0xA69E: +case 0xA69F: +case 0xA6A0: +case 0xA6A1: +case 0xA6A2: +case 0xA6A3: +case 0xA6A4: +case 0xA6A5: +case 0xA6A6: +case 0xA6A7: +case 0xA6A8: +case 0xA6A9: +case 0xA6AA: +case 0xA6AB: +case 0xA6AC: +case 0xA6AD: +case 0xA6AE: +case 0xA6AF: +case 0xA6B0: +case 0xA6B1: +case 0xA6B2: +case 0xA6B3: +case 0xA6B4: +case 0xA6B5: +case 0xA6B6: +case 0xA6B7: +case 0xA6B8: +case 0xA6B9: +case 0xA6BA: +case 0xA6BB: +case 0xA6BC: +case 0xA6BD: +case 0xA6BE: +case 0xA6BF: +case 0xA6C0: +case 0xA6C1: +case 0xA6C2: +case 0xA6C3: +case 0xA6C4: +case 0xA6C5: +case 0xA6C6: +case 0xA6C7: +case 0xA6C8: +case 0xA6C9: +case 0xA6CA: +case 0xA6CB: +case 0xA6CC: +case 0xA6CD: +case 0xA6CE: +case 0xA6CF: +case 0xA6D0: +case 0xA6D1: +case 0xA6D2: +case 0xA6D3: +case 0xA6D4: +case 0xA6D5: +case 0xA6D6: +case 0xA6D7: +case 0xA6D8: +case 0xA6D9: +case 0xA6DA: +case 0xA6DB: +case 0xA6DC: +case 0xA6DD: +case 0xA6DE: +case 0xA6DF: +case 0xA6E0: +case 0xA6E1: +case 0xA6E2: +case 0xA6E3: +case 0xA6E4: +case 0xA6E5: +case 0xA6E6: +case 0xA6E7: +case 0xA6E8: +case 0xA6E9: +case 0xA6EA: +case 0xA6EB: +case 0xA6EC: +case 0xA6ED: +case 0xA6EE: +case 0xA6EF: +case 0xA6F0: +case 0xA6F1: +case 0xA6F2: +case 0xA6F3: +case 0xA6F4: +case 0xA6F5: +case 0xA6F6: +case 0xA6F7: +case 0xA6F8: +case 0xA6F9: +case 0xA6FA: +case 0xA6FB: +case 0xA6FC: +case 0xA6FD: +case 0xA6FE: +case 0xA6FF: +case 0xA700: +case 0xA701: +case 0xA702: +case 0xA703: +case 0xA704: +case 0xA705: +case 0xA706: +case 0xA707: +case 0xA708: +case 0xA709: +case 0xA70A: +case 0xA70B: +case 0xA70C: +case 0xA70D: +case 0xA70E: +case 0xA70F: +case 0xA710: +case 0xA711: +case 0xA712: +case 0xA713: +case 0xA714: +case 0xA715: +case 0xA716: +case 0xA717: +case 0xA718: +case 0xA719: +case 0xA71A: +case 0xA71B: +case 0xA71C: +case 0xA71D: +case 0xA71E: +case 0xA71F: +case 0xA720: +case 0xA721: +case 0xA722: +case 0xA723: +case 0xA724: +case 0xA725: +case 0xA726: +case 0xA727: +case 0xA728: +case 0xA729: +case 0xA72A: +case 0xA72B: +case 0xA72C: +case 0xA72D: +case 0xA72E: +case 0xA72F: +case 0xA730: +case 0xA731: +case 0xA732: +case 0xA733: +case 0xA734: +case 0xA735: +case 0xA736: +case 0xA737: +case 0xA738: +case 0xA739: +case 0xA73A: +case 0xA73B: +case 0xA73C: +case 0xA73D: +case 0xA73E: +case 0xA73F: +case 0xA740: +case 0xA741: +case 0xA742: +case 0xA743: +case 0xA744: +case 0xA745: +case 0xA746: +case 0xA747: +case 0xA748: +case 0xA749: +case 0xA74A: +case 0xA74B: +case 0xA74C: +case 0xA74D: +case 0xA74E: +case 0xA74F: +case 0xA750: +case 0xA751: +case 0xA752: +case 0xA753: +case 0xA754: +case 0xA755: +case 0xA756: +case 0xA757: +case 0xA758: +case 0xA759: +case 0xA75A: +case 0xA75B: +case 0xA75C: +case 0xA75D: +case 0xA75E: +case 0xA75F: +case 0xA760: +case 0xA761: +case 0xA762: +case 0xA763: +case 0xA764: +case 0xA765: +case 0xA766: +case 0xA767: +case 0xA768: +case 0xA769: +case 0xA76A: +case 0xA76B: +case 0xA76C: +case 0xA76D: +case 0xA76E: +case 0xA76F: +case 0xA770: +case 0xA771: +case 0xA772: +case 0xA773: +case 0xA774: +case 0xA775: +case 0xA776: +case 0xA777: +case 0xA778: +case 0xA779: +case 0xA77A: +case 0xA77B: +case 0xA77C: +case 0xA77D: +case 0xA77E: +case 0xA77F: +case 0xA780: +case 0xA781: +case 0xA782: +case 0xA783: +case 0xA784: +case 0xA785: +case 0xA786: +case 0xA787: +case 0xA788: +case 0xA789: +case 0xA78A: +case 0xA78B: +case 0xA78C: +case 0xA78D: +case 0xA78E: +case 0xA78F: +case 0xA790: +case 0xA791: +case 0xA792: +case 0xA793: +case 0xA794: +case 0xA795: +case 0xA796: +case 0xA797: +case 0xA798: +case 0xA799: +case 0xA79A: +case 0xA79B: +case 0xA79C: +case 0xA79D: +case 0xA79E: +case 0xA79F: +case 0xA7A0: +case 0xA7A1: +case 0xA7A2: +case 0xA7A3: +case 0xA7A4: +case 0xA7A5: +case 0xA7A6: +case 0xA7A7: +case 0xA7A8: +case 0xA7A9: +case 0xA7AA: +case 0xA7AB: +case 0xA7AC: +case 0xA7AD: +case 0xA7AE: +case 0xA7AF: +case 0xA7B0: +case 0xA7B1: +case 0xA7B2: +case 0xA7B3: +case 0xA7B4: +case 0xA7B5: +case 0xA7B6: +case 0xA7B7: +case 0xA7B8: +case 0xA7B9: +case 0xA7BA: +case 0xA7BB: +case 0xA7BC: +case 0xA7BD: +case 0xA7BE: +case 0xA7BF: +case 0xA7C0: +case 0xA7C1: +case 0xA7C2: +case 0xA7C3: +case 0xA7C4: +case 0xA7C5: +case 0xA7C6: +case 0xA7C7: +case 0xA7C8: +case 0xA7C9: +case 0xA7CA: +case 0xA7CB: +case 0xA7CC: +case 0xA7CD: +case 0xA7CE: +case 0xA7CF: +case 0xA7D0: +case 0xA7D1: +case 0xA7D2: +case 0xA7D3: +case 0xA7D4: +case 0xA7D5: +case 0xA7D6: +case 0xA7D7: +case 0xA7D8: +case 0xA7D9: +case 0xA7DA: +case 0xA7DB: +case 0xA7DC: +case 0xA7DD: +case 0xA7DE: +case 0xA7DF: +case 0xA7E0: +case 0xA7E1: +case 0xA7E2: +case 0xA7E3: +case 0xA7E4: +case 0xA7E5: +case 0xA7E6: +case 0xA7E7: +case 0xA7E8: +case 0xA7E9: +case 0xA7EA: +case 0xA7EB: +case 0xA7EC: +case 0xA7ED: +case 0xA7EE: +case 0xA7EF: +case 0xA7F0: +case 0xA7F1: +case 0xA7F2: +case 0xA7F3: +case 0xA7F4: +case 0xA7F5: +case 0xA7F6: +case 0xA7F7: +case 0xA7F8: +case 0xA7F9: +case 0xA7FA: +case 0xA7FB: +case 0xA7FC: +case 0xA7FD: +case 0xA7FE: +case 0xA7FF: +case 0xA800: +case 0xA801: +case 0xA802: +case 0xA803: +case 0xA804: +case 0xA805: +case 0xA806: +case 0xA807: +case 0xA808: +case 0xA809: +case 0xA80A: +case 0xA80B: +case 0xA80C: +case 0xA80D: +case 0xA80E: +case 0xA80F: +case 0xA810: +case 0xA811: +case 0xA812: +case 0xA813: +case 0xA814: +case 0xA815: +case 0xA816: +case 0xA817: +case 0xA818: +case 0xA819: +case 0xA81A: +case 0xA81B: +case 0xA81C: +case 0xA81D: +case 0xA81E: +case 0xA81F: +case 0xA820: +case 0xA821: +case 0xA822: +case 0xA823: +case 0xA824: +case 0xA825: +case 0xA826: +case 0xA827: +case 0xA828: +case 0xA829: +case 0xA82A: +case 0xA82B: +case 0xA82C: +case 0xA82D: +case 0xA82E: +case 0xA82F: +case 0xA830: +case 0xA831: +case 0xA832: +case 0xA833: +case 0xA834: +case 0xA835: +case 0xA836: +case 0xA837: +case 0xA838: +case 0xA839: +case 0xA83A: +case 0xA83B: +case 0xA83C: +case 0xA83D: +case 0xA83E: +case 0xA83F: +case 0xA840: +case 0xA841: +case 0xA842: +case 0xA843: +case 0xA844: +case 0xA845: +case 0xA846: +case 0xA847: +case 0xA848: +case 0xA849: +case 0xA84A: +case 0xA84B: +case 0xA84C: +case 0xA84D: +case 0xA84E: +case 0xA84F: +case 0xA850: +case 0xA851: +case 0xA852: +case 0xA853: +case 0xA854: +case 0xA855: +case 0xA856: +case 0xA857: +case 0xA858: +case 0xA859: +case 0xA85A: +case 0xA85B: +case 0xA85C: +case 0xA85D: +case 0xA85E: +case 0xA85F: +case 0xA860: +case 0xA861: +case 0xA862: +case 0xA863: +case 0xA864: +case 0xA865: +case 0xA866: +case 0xA867: +case 0xA868: +case 0xA869: +case 0xA86A: +case 0xA86B: +case 0xA86C: +case 0xA86D: +case 0xA86E: +case 0xA86F: +case 0xA870: +case 0xA871: +case 0xA872: +case 0xA873: +case 0xA874: +case 0xA875: +case 0xA876: +case 0xA877: +case 0xA878: +case 0xA879: +case 0xA87A: +case 0xA87B: +case 0xA87C: +case 0xA87D: +case 0xA87E: +case 0xA87F: +case 0xA880: +case 0xA881: +case 0xA882: +case 0xA883: +case 0xA884: +case 0xA885: +case 0xA886: +case 0xA887: +case 0xA888: +case 0xA889: +case 0xA88A: +case 0xA88B: +case 0xA88C: +case 0xA88D: +case 0xA88E: +case 0xA88F: +case 0xA890: +case 0xA891: +case 0xA892: +case 0xA893: +case 0xA894: +case 0xA895: +case 0xA896: +case 0xA897: +case 0xA898: +case 0xA899: +case 0xA89A: +case 0xA89B: +case 0xA89C: +case 0xA89D: +case 0xA89E: +case 0xA89F: +case 0xA8A0: +case 0xA8A1: +case 0xA8A2: +case 0xA8A3: +case 0xA8A4: +case 0xA8A5: +case 0xA8A6: +case 0xA8A7: +case 0xA8A8: +case 0xA8A9: +case 0xA8AA: +case 0xA8AB: +case 0xA8AC: +case 0xA8AD: +case 0xA8AE: +case 0xA8AF: +case 0xA8B0: +case 0xA8B1: +case 0xA8B2: +case 0xA8B3: +case 0xA8B4: +case 0xA8B5: +case 0xA8B6: +case 0xA8B7: +case 0xA8B8: +case 0xA8B9: +case 0xA8BA: +case 0xA8BB: +case 0xA8BC: +case 0xA8BD: +case 0xA8BE: +case 0xA8BF: +case 0xA8C0: +case 0xA8C1: +case 0xA8C2: +case 0xA8C3: +case 0xA8C4: +case 0xA8C5: +case 0xA8C6: +case 0xA8C7: +case 0xA8C8: +case 0xA8C9: +case 0xA8CA: +case 0xA8CB: +case 0xA8CC: +case 0xA8CD: +case 0xA8CE: +case 0xA8CF: +case 0xA8D0: +case 0xA8D1: +case 0xA8D2: +case 0xA8D3: +case 0xA8D4: +case 0xA8D5: +case 0xA8D6: +case 0xA8D7: +case 0xA8D8: +case 0xA8D9: +case 0xA8DA: +case 0xA8DB: +case 0xA8DC: +case 0xA8DD: +case 0xA8DE: +case 0xA8DF: +case 0xA8E0: +case 0xA8E1: +case 0xA8E2: +case 0xA8E3: +case 0xA8E4: +case 0xA8E5: +case 0xA8E6: +case 0xA8E7: +case 0xA8E8: +case 0xA8E9: +case 0xA8EA: +case 0xA8EB: +case 0xA8EC: +case 0xA8ED: +case 0xA8EE: +case 0xA8EF: +case 0xA8F0: +case 0xA8F1: +case 0xA8F2: +case 0xA8F3: +case 0xA8F4: +case 0xA8F5: +case 0xA8F6: +case 0xA8F7: +case 0xA8F8: +case 0xA8F9: +case 0xA8FA: +case 0xA8FB: +case 0xA8FC: +case 0xA8FD: +case 0xA8FE: +case 0xA8FF: +case 0xA900: +case 0xA901: +case 0xA902: +case 0xA903: +case 0xA904: +case 0xA905: +case 0xA906: +case 0xA907: +case 0xA908: +case 0xA909: +case 0xA90A: +case 0xA90B: +case 0xA90C: +case 0xA90D: +case 0xA90E: +case 0xA90F: +case 0xA910: +case 0xA911: +case 0xA912: +case 0xA913: +case 0xA914: +case 0xA915: +case 0xA916: +case 0xA917: +case 0xA918: +case 0xA919: +case 0xA91A: +case 0xA91B: +case 0xA91C: +case 0xA91D: +case 0xA91E: +case 0xA91F: +case 0xA920: +case 0xA921: +case 0xA922: +case 0xA923: +case 0xA924: +case 0xA925: +case 0xA926: +case 0xA927: +case 0xA928: +case 0xA929: +case 0xA92A: +case 0xA92B: +case 0xA92C: +case 0xA92D: +case 0xA92E: +case 0xA92F: +case 0xA930: +case 0xA931: +case 0xA932: +case 0xA933: +case 0xA934: +case 0xA935: +case 0xA936: +case 0xA937: +case 0xA938: +case 0xA939: +case 0xA93A: +case 0xA93B: +case 0xA93C: +case 0xA93D: +case 0xA93E: +case 0xA93F: +case 0xA940: +case 0xA941: +case 0xA942: +case 0xA943: +case 0xA944: +case 0xA945: +case 0xA946: +case 0xA947: +case 0xA948: +case 0xA949: +case 0xA94A: +case 0xA94B: +case 0xA94C: +case 0xA94D: +case 0xA94E: +case 0xA94F: +case 0xA950: +case 0xA951: +case 0xA952: +case 0xA953: +case 0xA954: +case 0xA955: +case 0xA956: +case 0xA957: +case 0xA958: +case 0xA959: +case 0xA95A: +case 0xA95B: +case 0xA95C: +case 0xA95D: +case 0xA95E: +case 0xA95F: +case 0xA960: +case 0xA961: +case 0xA962: +case 0xA963: +case 0xA964: +case 0xA965: +case 0xA966: +case 0xA967: +case 0xA968: +case 0xA969: +case 0xA96A: +case 0xA96B: +case 0xA96C: +case 0xA96D: +case 0xA96E: +case 0xA96F: +case 0xA970: +case 0xA971: +case 0xA972: +case 0xA973: +case 0xA974: +case 0xA975: +case 0xA976: +case 0xA977: +case 0xA978: +case 0xA979: +case 0xA97A: +case 0xA97B: +case 0xA97C: +case 0xA97D: +case 0xA97E: +case 0xA97F: +case 0xA980: +case 0xA981: +case 0xA982: +case 0xA983: +case 0xA984: +case 0xA985: +case 0xA986: +case 0xA987: +case 0xA988: +case 0xA989: +case 0xA98A: +case 0xA98B: +case 0xA98C: +case 0xA98D: +case 0xA98E: +case 0xA98F: +case 0xA990: +case 0xA991: +case 0xA992: +case 0xA993: +case 0xA994: +case 0xA995: +case 0xA996: +case 0xA997: +case 0xA998: +case 0xA999: +case 0xA99A: +case 0xA99B: +case 0xA99C: +case 0xA99D: +case 0xA99E: +case 0xA99F: +case 0xA9A0: +case 0xA9A1: +case 0xA9A2: +case 0xA9A3: +case 0xA9A4: +case 0xA9A5: +case 0xA9A6: +case 0xA9A7: +case 0xA9A8: +case 0xA9A9: +case 0xA9AA: +case 0xA9AB: +case 0xA9AC: +case 0xA9AD: +case 0xA9AE: +case 0xA9AF: +case 0xA9B0: +case 0xA9B1: +case 0xA9B2: +case 0xA9B3: +case 0xA9B4: +case 0xA9B5: +case 0xA9B6: +case 0xA9B7: +case 0xA9B8: +case 0xA9B9: +case 0xA9BA: +case 0xA9BB: +case 0xA9BC: +case 0xA9BD: +case 0xA9BE: +case 0xA9BF: +case 0xA9C0: +case 0xA9C1: +case 0xA9C2: +case 0xA9C3: +case 0xA9C4: +case 0xA9C5: +case 0xA9C6: +case 0xA9C7: +case 0xA9C8: +case 0xA9C9: +case 0xA9CA: +case 0xA9CB: +case 0xA9CC: +case 0xA9CD: +case 0xA9CE: +case 0xA9CF: +case 0xA9D0: +case 0xA9D1: +case 0xA9D2: +case 0xA9D3: +case 0xA9D4: +case 0xA9D5: +case 0xA9D6: +case 0xA9D7: +case 0xA9D8: +case 0xA9D9: +case 0xA9DA: +case 0xA9DB: +case 0xA9DC: +case 0xA9DD: +case 0xA9DE: +case 0xA9DF: +case 0xA9E0: +case 0xA9E1: +case 0xA9E2: +case 0xA9E3: +case 0xA9E4: +case 0xA9E5: +case 0xA9E6: +case 0xA9E7: +case 0xA9E8: +case 0xA9E9: +case 0xA9EA: +case 0xA9EB: +case 0xA9EC: +case 0xA9ED: +case 0xA9EE: +case 0xA9EF: +case 0xA9F0: +case 0xA9F1: +case 0xA9F2: +case 0xA9F3: +case 0xA9F4: +case 0xA9F5: +case 0xA9F6: +case 0xA9F7: +case 0xA9F8: +case 0xA9F9: +case 0xA9FA: +case 0xA9FB: +case 0xA9FC: +case 0xA9FD: +case 0xA9FE: +case 0xA9FF: +case 0xAA00: +case 0xAA01: +case 0xAA02: +case 0xAA03: +case 0xAA04: +case 0xAA05: +case 0xAA06: +case 0xAA07: +case 0xAA08: +case 0xAA09: +case 0xAA0A: +case 0xAA0B: +case 0xAA0C: +case 0xAA0D: +case 0xAA0E: +case 0xAA0F: +case 0xAA10: +case 0xAA11: +case 0xAA12: +case 0xAA13: +case 0xAA14: +case 0xAA15: +case 0xAA16: +case 0xAA17: +case 0xAA18: +case 0xAA19: +case 0xAA1A: +case 0xAA1B: +case 0xAA1C: +case 0xAA1D: +case 0xAA1E: +case 0xAA1F: +case 0xAA20: +case 0xAA21: +case 0xAA22: +case 0xAA23: +case 0xAA24: +case 0xAA25: +case 0xAA26: +case 0xAA27: +case 0xAA28: +case 0xAA29: +case 0xAA2A: +case 0xAA2B: +case 0xAA2C: +case 0xAA2D: +case 0xAA2E: +case 0xAA2F: +case 0xAA30: +case 0xAA31: +case 0xAA32: +case 0xAA33: +case 0xAA34: +case 0xAA35: +case 0xAA36: +case 0xAA37: +case 0xAA38: +case 0xAA39: +case 0xAA3A: +case 0xAA3B: +case 0xAA3C: +case 0xAA3D: +case 0xAA3E: +case 0xAA3F: +case 0xAA40: +case 0xAA41: +case 0xAA42: +case 0xAA43: +case 0xAA44: +case 0xAA45: +case 0xAA46: +case 0xAA47: +case 0xAA48: +case 0xAA49: +case 0xAA4A: +case 0xAA4B: +case 0xAA4C: +case 0xAA4D: +case 0xAA4E: +case 0xAA4F: +case 0xAA50: +case 0xAA51: +case 0xAA52: +case 0xAA53: +case 0xAA54: +case 0xAA55: +case 0xAA56: +case 0xAA57: +case 0xAA58: +case 0xAA59: +case 0xAA5A: +case 0xAA5B: +case 0xAA5C: +case 0xAA5D: +case 0xAA5E: +case 0xAA5F: +case 0xAA60: +case 0xAA61: +case 0xAA62: +case 0xAA63: +case 0xAA64: +case 0xAA65: +case 0xAA66: +case 0xAA67: +case 0xAA68: +case 0xAA69: +case 0xAA6A: +case 0xAA6B: +case 0xAA6C: +case 0xAA6D: +case 0xAA6E: +case 0xAA6F: +case 0xAA70: +case 0xAA71: +case 0xAA72: +case 0xAA73: +case 0xAA74: +case 0xAA75: +case 0xAA76: +case 0xAA77: +case 0xAA78: +case 0xAA79: +case 0xAA7A: +case 0xAA7B: +case 0xAA7C: +case 0xAA7D: +case 0xAA7E: +case 0xAA7F: +case 0xAA80: +case 0xAA81: +case 0xAA82: +case 0xAA83: +case 0xAA84: +case 0xAA85: +case 0xAA86: +case 0xAA87: +case 0xAA88: +case 0xAA89: +case 0xAA8A: +case 0xAA8B: +case 0xAA8C: +case 0xAA8D: +case 0xAA8E: +case 0xAA8F: +case 0xAA90: +case 0xAA91: +case 0xAA92: +case 0xAA93: +case 0xAA94: +case 0xAA95: +case 0xAA96: +case 0xAA97: +case 0xAA98: +case 0xAA99: +case 0xAA9A: +case 0xAA9B: +case 0xAA9C: +case 0xAA9D: +case 0xAA9E: +case 0xAA9F: +case 0xAAA0: +case 0xAAA1: +case 0xAAA2: +case 0xAAA3: +case 0xAAA4: +case 0xAAA5: +case 0xAAA6: +case 0xAAA7: +case 0xAAA8: +case 0xAAA9: +case 0xAAAA: +case 0xAAAB: +case 0xAAAC: +case 0xAAAD: +case 0xAAAE: +case 0xAAAF: +case 0xAAB0: +case 0xAAB1: +case 0xAAB2: +case 0xAAB3: +case 0xAAB4: +case 0xAAB5: +case 0xAAB6: +case 0xAAB7: +case 0xAAB8: +case 0xAAB9: +case 0xAABA: +case 0xAABB: +case 0xAABC: +case 0xAABD: +case 0xAABE: +case 0xAABF: +case 0xAAC0: +case 0xAAC1: +case 0xAAC2: +case 0xAAC3: +case 0xAAC4: +case 0xAAC5: +case 0xAAC6: +case 0xAAC7: +case 0xAAC8: +case 0xAAC9: +case 0xAACA: +case 0xAACB: +case 0xAACC: +case 0xAACD: +case 0xAACE: +case 0xAACF: +case 0xAAD0: +case 0xAAD1: +case 0xAAD2: +case 0xAAD3: +case 0xAAD4: +case 0xAAD5: +case 0xAAD6: +case 0xAAD7: +case 0xAAD8: +case 0xAAD9: +case 0xAADA: +case 0xAADB: +case 0xAADC: +case 0xAADD: +case 0xAADE: +case 0xAADF: +case 0xAAE0: +case 0xAAE1: +case 0xAAE2: +case 0xAAE3: +case 0xAAE4: +case 0xAAE5: +case 0xAAE6: +case 0xAAE7: +case 0xAAE8: +case 0xAAE9: +case 0xAAEA: +case 0xAAEB: +case 0xAAEC: +case 0xAAED: +case 0xAAEE: +case 0xAAEF: +case 0xAAF0: +case 0xAAF1: +case 0xAAF2: +case 0xAAF3: +case 0xAAF4: +case 0xAAF5: +case 0xAAF6: +case 0xAAF7: +case 0xAAF8: +case 0xAAF9: +case 0xAAFA: +case 0xAAFB: +case 0xAAFC: +case 0xAAFD: +case 0xAAFE: +case 0xAAFF: +case 0xAB00: +case 0xAB01: +case 0xAB02: +case 0xAB03: +case 0xAB04: +case 0xAB05: +case 0xAB06: +case 0xAB07: +case 0xAB08: +case 0xAB09: +case 0xAB0A: +case 0xAB0B: +case 0xAB0C: +case 0xAB0D: +case 0xAB0E: +case 0xAB0F: +case 0xAB10: +case 0xAB11: +case 0xAB12: +case 0xAB13: +case 0xAB14: +case 0xAB15: +case 0xAB16: +case 0xAB17: +case 0xAB18: +case 0xAB19: +case 0xAB1A: +case 0xAB1B: +case 0xAB1C: +case 0xAB1D: +case 0xAB1E: +case 0xAB1F: +case 0xAB20: +case 0xAB21: +case 0xAB22: +case 0xAB23: +case 0xAB24: +case 0xAB25: +case 0xAB26: +case 0xAB27: +case 0xAB28: +case 0xAB29: +case 0xAB2A: +case 0xAB2B: +case 0xAB2C: +case 0xAB2D: +case 0xAB2E: +case 0xAB2F: +case 0xAB30: +case 0xAB31: +case 0xAB32: +case 0xAB33: +case 0xAB34: +case 0xAB35: +case 0xAB36: +case 0xAB37: +case 0xAB38: +case 0xAB39: +case 0xAB3A: +case 0xAB3B: +case 0xAB3C: +case 0xAB3D: +case 0xAB3E: +case 0xAB3F: +case 0xAB40: +case 0xAB41: +case 0xAB42: +case 0xAB43: +case 0xAB44: +case 0xAB45: +case 0xAB46: +case 0xAB47: +case 0xAB48: +case 0xAB49: +case 0xAB4A: +case 0xAB4B: +case 0xAB4C: +case 0xAB4D: +case 0xAB4E: +case 0xAB4F: +case 0xAB50: +case 0xAB51: +case 0xAB52: +case 0xAB53: +case 0xAB54: +case 0xAB55: +case 0xAB56: +case 0xAB57: +case 0xAB58: +case 0xAB59: +case 0xAB5A: +case 0xAB5B: +case 0xAB5C: +case 0xAB5D: +case 0xAB5E: +case 0xAB5F: +case 0xAB60: +case 0xAB61: +case 0xAB62: +case 0xAB63: +case 0xAB64: +case 0xAB65: +case 0xAB66: +case 0xAB67: +case 0xAB68: +case 0xAB69: +case 0xAB6A: +case 0xAB6B: +case 0xAB6C: +case 0xAB6D: +case 0xAB6E: +case 0xAB6F: +case 0xAB70: +case 0xAB71: +case 0xAB72: +case 0xAB73: +case 0xAB74: +case 0xAB75: +case 0xAB76: +case 0xAB77: +case 0xAB78: +case 0xAB79: +case 0xAB7A: +case 0xAB7B: +case 0xAB7C: +case 0xAB7D: +case 0xAB7E: +case 0xAB7F: +case 0xAB80: +case 0xAB81: +case 0xAB82: +case 0xAB83: +case 0xAB84: +case 0xAB85: +case 0xAB86: +case 0xAB87: +case 0xAB88: +case 0xAB89: +case 0xAB8A: +case 0xAB8B: +case 0xAB8C: +case 0xAB8D: +case 0xAB8E: +case 0xAB8F: +case 0xAB90: +case 0xAB91: +case 0xAB92: +case 0xAB93: +case 0xAB94: +case 0xAB95: +case 0xAB96: +case 0xAB97: +case 0xAB98: +case 0xAB99: +case 0xAB9A: +case 0xAB9B: +case 0xAB9C: +case 0xAB9D: +case 0xAB9E: +case 0xAB9F: +case 0xABA0: +case 0xABA1: +case 0xABA2: +case 0xABA3: +case 0xABA4: +case 0xABA5: +case 0xABA6: +case 0xABA7: +case 0xABA8: +case 0xABA9: +case 0xABAA: +case 0xABAB: +case 0xABAC: +case 0xABAD: +case 0xABAE: +case 0xABAF: +case 0xABB0: +case 0xABB1: +case 0xABB2: +case 0xABB3: +case 0xABB4: +case 0xABB5: +case 0xABB6: +case 0xABB7: +case 0xABB8: +case 0xABB9: +case 0xABBA: +case 0xABBB: +case 0xABBC: +case 0xABBD: +case 0xABBE: +case 0xABBF: +case 0xABC0: +case 0xABC1: +case 0xABC2: +case 0xABC3: +case 0xABC4: +case 0xABC5: +case 0xABC6: +case 0xABC7: +case 0xABC8: +case 0xABC9: +case 0xABCA: +case 0xABCB: +case 0xABCC: +case 0xABCD: +case 0xABCE: +case 0xABCF: +case 0xABD0: +case 0xABD1: +case 0xABD2: +case 0xABD3: +case 0xABD4: +case 0xABD5: +case 0xABD6: +case 0xABD7: +case 0xABD8: +case 0xABD9: +case 0xABDA: +case 0xABDB: +case 0xABDC: +case 0xABDD: +case 0xABDE: +case 0xABDF: +case 0xABE0: +case 0xABE1: +case 0xABE2: +case 0xABE3: +case 0xABE4: +case 0xABE5: +case 0xABE6: +case 0xABE7: +case 0xABE8: +case 0xABE9: +case 0xABEA: +case 0xABEB: +case 0xABEC: +case 0xABED: +case 0xABEE: +case 0xABEF: +case 0xABF0: +case 0xABF1: +case 0xABF2: +case 0xABF3: +case 0xABF4: +case 0xABF5: +case 0xABF6: +case 0xABF7: +case 0xABF8: +case 0xABF9: +case 0xABFA: +case 0xABFB: +case 0xABFC: +case 0xABFD: +case 0xABFE: +case 0xABFF: +case 0xAC00: +case 0xAC01: +case 0xAC02: +case 0xAC03: +case 0xAC04: +case 0xAC05: +case 0xAC06: +case 0xAC07: +case 0xAC08: +case 0xAC09: +case 0xAC0A: +case 0xAC0B: +case 0xAC0C: +case 0xAC0D: +case 0xAC0E: +case 0xAC0F: +case 0xAC10: +case 0xAC11: +case 0xAC12: +case 0xAC13: +case 0xAC14: +case 0xAC15: +case 0xAC16: +case 0xAC17: +case 0xAC18: +case 0xAC19: +case 0xAC1A: +case 0xAC1B: +case 0xAC1C: +case 0xAC1D: +case 0xAC1E: +case 0xAC1F: +case 0xAC20: +case 0xAC21: +case 0xAC22: +case 0xAC23: +case 0xAC24: +case 0xAC25: +case 0xAC26: +case 0xAC27: +case 0xAC28: +case 0xAC29: +case 0xAC2A: +case 0xAC2B: +case 0xAC2C: +case 0xAC2D: +case 0xAC2E: +case 0xAC2F: +case 0xAC30: +case 0xAC31: +case 0xAC32: +case 0xAC33: +case 0xAC34: +case 0xAC35: +case 0xAC36: +case 0xAC37: +case 0xAC38: +case 0xAC39: +case 0xAC3A: +case 0xAC3B: +case 0xAC3C: +case 0xAC3D: +case 0xAC3E: +case 0xAC3F: +case 0xAC40: +case 0xAC41: +case 0xAC42: +case 0xAC43: +case 0xAC44: +case 0xAC45: +case 0xAC46: +case 0xAC47: +case 0xAC48: +case 0xAC49: +case 0xAC4A: +case 0xAC4B: +case 0xAC4C: +case 0xAC4D: +case 0xAC4E: +case 0xAC4F: +case 0xAC50: +case 0xAC51: +case 0xAC52: +case 0xAC53: +case 0xAC54: +case 0xAC55: +case 0xAC56: +case 0xAC57: +case 0xAC58: +case 0xAC59: +case 0xAC5A: +case 0xAC5B: +case 0xAC5C: +case 0xAC5D: +case 0xAC5E: +case 0xAC5F: +case 0xAC60: +case 0xAC61: +case 0xAC62: +case 0xAC63: +case 0xAC64: +case 0xAC65: +case 0xAC66: +case 0xAC67: +case 0xAC68: +case 0xAC69: +case 0xAC6A: +case 0xAC6B: +case 0xAC6C: +case 0xAC6D: +case 0xAC6E: +case 0xAC6F: +case 0xAC70: +case 0xAC71: +case 0xAC72: +case 0xAC73: +case 0xAC74: +case 0xAC75: +case 0xAC76: +case 0xAC77: +case 0xAC78: +case 0xAC79: +case 0xAC7A: +case 0xAC7B: +case 0xAC7C: +case 0xAC7D: +case 0xAC7E: +case 0xAC7F: +case 0xAC80: +case 0xAC81: +case 0xAC82: +case 0xAC83: +case 0xAC84: +case 0xAC85: +case 0xAC86: +case 0xAC87: +case 0xAC88: +case 0xAC89: +case 0xAC8A: +case 0xAC8B: +case 0xAC8C: +case 0xAC8D: +case 0xAC8E: +case 0xAC8F: +case 0xAC90: +case 0xAC91: +case 0xAC92: +case 0xAC93: +case 0xAC94: +case 0xAC95: +case 0xAC96: +case 0xAC97: +case 0xAC98: +case 0xAC99: +case 0xAC9A: +case 0xAC9B: +case 0xAC9C: +case 0xAC9D: +case 0xAC9E: +case 0xAC9F: +case 0xACA0: +case 0xACA1: +case 0xACA2: +case 0xACA3: +case 0xACA4: +case 0xACA5: +case 0xACA6: +case 0xACA7: +case 0xACA8: +case 0xACA9: +case 0xACAA: +case 0xACAB: +case 0xACAC: +case 0xACAD: +case 0xACAE: +case 0xACAF: +case 0xACB0: +case 0xACB1: +case 0xACB2: +case 0xACB3: +case 0xACB4: +case 0xACB5: +case 0xACB6: +case 0xACB7: +case 0xACB8: +case 0xACB9: +case 0xACBA: +case 0xACBB: +case 0xACBC: +case 0xACBD: +case 0xACBE: +case 0xACBF: +case 0xACC0: +case 0xACC1: +case 0xACC2: +case 0xACC3: +case 0xACC4: +case 0xACC5: +case 0xACC6: +case 0xACC7: +case 0xACC8: +case 0xACC9: +case 0xACCA: +case 0xACCB: +case 0xACCC: +case 0xACCD: +case 0xACCE: +case 0xACCF: +case 0xACD0: +case 0xACD1: +case 0xACD2: +case 0xACD3: +case 0xACD4: +case 0xACD5: +case 0xACD6: +case 0xACD7: +case 0xACD8: +case 0xACD9: +case 0xACDA: +case 0xACDB: +case 0xACDC: +case 0xACDD: +case 0xACDE: +case 0xACDF: +case 0xACE0: +case 0xACE1: +case 0xACE2: +case 0xACE3: +case 0xACE4: +case 0xACE5: +case 0xACE6: +case 0xACE7: +case 0xACE8: +case 0xACE9: +case 0xACEA: +case 0xACEB: +case 0xACEC: +case 0xACED: +case 0xACEE: +case 0xACEF: +case 0xACF0: +case 0xACF1: +case 0xACF2: +case 0xACF3: +case 0xACF4: +case 0xACF5: +case 0xACF6: +case 0xACF7: +case 0xACF8: +case 0xACF9: +case 0xACFA: +case 0xACFB: +case 0xACFC: +case 0xACFD: +case 0xACFE: +case 0xACFF: +case 0xAD00: +case 0xAD01: +case 0xAD02: +case 0xAD03: +case 0xAD04: +case 0xAD05: +case 0xAD06: +case 0xAD07: +case 0xAD08: +case 0xAD09: +case 0xAD0A: +case 0xAD0B: +case 0xAD0C: +case 0xAD0D: +case 0xAD0E: +case 0xAD0F: +case 0xAD10: +case 0xAD11: +case 0xAD12: +case 0xAD13: +case 0xAD14: +case 0xAD15: +case 0xAD16: +case 0xAD17: +case 0xAD18: +case 0xAD19: +case 0xAD1A: +case 0xAD1B: +case 0xAD1C: +case 0xAD1D: +case 0xAD1E: +case 0xAD1F: +case 0xAD20: +case 0xAD21: +case 0xAD22: +case 0xAD23: +case 0xAD24: +case 0xAD25: +case 0xAD26: +case 0xAD27: +case 0xAD28: +case 0xAD29: +case 0xAD2A: +case 0xAD2B: +case 0xAD2C: +case 0xAD2D: +case 0xAD2E: +case 0xAD2F: +case 0xAD30: +case 0xAD31: +case 0xAD32: +case 0xAD33: +case 0xAD34: +case 0xAD35: +case 0xAD36: +case 0xAD37: +case 0xAD38: +case 0xAD39: +case 0xAD3A: +case 0xAD3B: +case 0xAD3C: +case 0xAD3D: +case 0xAD3E: +case 0xAD3F: +case 0xAD40: +case 0xAD41: +case 0xAD42: +case 0xAD43: +case 0xAD44: +case 0xAD45: +case 0xAD46: +case 0xAD47: +case 0xAD48: +case 0xAD49: +case 0xAD4A: +case 0xAD4B: +case 0xAD4C: +case 0xAD4D: +case 0xAD4E: +case 0xAD4F: +case 0xAD50: +case 0xAD51: +case 0xAD52: +case 0xAD53: +case 0xAD54: +case 0xAD55: +case 0xAD56: +case 0xAD57: +case 0xAD58: +case 0xAD59: +case 0xAD5A: +case 0xAD5B: +case 0xAD5C: +case 0xAD5D: +case 0xAD5E: +case 0xAD5F: +case 0xAD60: +case 0xAD61: +case 0xAD62: +case 0xAD63: +case 0xAD64: +case 0xAD65: +case 0xAD66: +case 0xAD67: +case 0xAD68: +case 0xAD69: +case 0xAD6A: +case 0xAD6B: +case 0xAD6C: +case 0xAD6D: +case 0xAD6E: +case 0xAD6F: +case 0xAD70: +case 0xAD71: +case 0xAD72: +case 0xAD73: +case 0xAD74: +case 0xAD75: +case 0xAD76: +case 0xAD77: +case 0xAD78: +case 0xAD79: +case 0xAD7A: +case 0xAD7B: +case 0xAD7C: +case 0xAD7D: +case 0xAD7E: +case 0xAD7F: +case 0xAD80: +case 0xAD81: +case 0xAD82: +case 0xAD83: +case 0xAD84: +case 0xAD85: +case 0xAD86: +case 0xAD87: +case 0xAD88: +case 0xAD89: +case 0xAD8A: +case 0xAD8B: +case 0xAD8C: +case 0xAD8D: +case 0xAD8E: +case 0xAD8F: +case 0xAD90: +case 0xAD91: +case 0xAD92: +case 0xAD93: +case 0xAD94: +case 0xAD95: +case 0xAD96: +case 0xAD97: +case 0xAD98: +case 0xAD99: +case 0xAD9A: +case 0xAD9B: +case 0xAD9C: +case 0xAD9D: +case 0xAD9E: +case 0xAD9F: +case 0xADA0: +case 0xADA1: +case 0xADA2: +case 0xADA3: +case 0xADA4: +case 0xADA5: +case 0xADA6: +case 0xADA7: +case 0xADA8: +case 0xADA9: +case 0xADAA: +case 0xADAB: +case 0xADAC: +case 0xADAD: +case 0xADAE: +case 0xADAF: +case 0xADB0: +case 0xADB1: +case 0xADB2: +case 0xADB3: +case 0xADB4: +case 0xADB5: +case 0xADB6: +case 0xADB7: +case 0xADB8: +case 0xADB9: +case 0xADBA: +case 0xADBB: +case 0xADBC: +case 0xADBD: +case 0xADBE: +case 0xADBF: +case 0xADC0: +case 0xADC1: +case 0xADC2: +case 0xADC3: +case 0xADC4: +case 0xADC5: +case 0xADC6: +case 0xADC7: +case 0xADC8: +case 0xADC9: +case 0xADCA: +case 0xADCB: +case 0xADCC: +case 0xADCD: +case 0xADCE: +case 0xADCF: +case 0xADD0: +case 0xADD1: +case 0xADD2: +case 0xADD3: +case 0xADD4: +case 0xADD5: +case 0xADD6: +case 0xADD7: +case 0xADD8: +case 0xADD9: +case 0xADDA: +case 0xADDB: +case 0xADDC: +case 0xADDD: +case 0xADDE: +case 0xADDF: +case 0xADE0: +case 0xADE1: +case 0xADE2: +case 0xADE3: +case 0xADE4: +case 0xADE5: +case 0xADE6: +case 0xADE7: +case 0xADE8: +case 0xADE9: +case 0xADEA: +case 0xADEB: +case 0xADEC: +case 0xADED: +case 0xADEE: +case 0xADEF: +case 0xADF0: +case 0xADF1: +case 0xADF2: +case 0xADF3: +case 0xADF4: +case 0xADF5: +case 0xADF6: +case 0xADF7: +case 0xADF8: +case 0xADF9: +case 0xADFA: +case 0xADFB: +case 0xADFC: +case 0xADFD: +case 0xADFE: +case 0xADFF: +case 0xAE00: +case 0xAE01: +case 0xAE02: +case 0xAE03: +case 0xAE04: +case 0xAE05: +case 0xAE06: +case 0xAE07: +case 0xAE08: +case 0xAE09: +case 0xAE0A: +case 0xAE0B: +case 0xAE0C: +case 0xAE0D: +case 0xAE0E: +case 0xAE0F: +case 0xAE10: +case 0xAE11: +case 0xAE12: +case 0xAE13: +case 0xAE14: +case 0xAE15: +case 0xAE16: +case 0xAE17: +case 0xAE18: +case 0xAE19: +case 0xAE1A: +case 0xAE1B: +case 0xAE1C: +case 0xAE1D: +case 0xAE1E: +case 0xAE1F: +case 0xAE20: +case 0xAE21: +case 0xAE22: +case 0xAE23: +case 0xAE24: +case 0xAE25: +case 0xAE26: +case 0xAE27: +case 0xAE28: +case 0xAE29: +case 0xAE2A: +case 0xAE2B: +case 0xAE2C: +case 0xAE2D: +case 0xAE2E: +case 0xAE2F: +case 0xAE30: +case 0xAE31: +case 0xAE32: +case 0xAE33: +case 0xAE34: +case 0xAE35: +case 0xAE36: +case 0xAE37: +case 0xAE38: +case 0xAE39: +case 0xAE3A: +case 0xAE3B: +case 0xAE3C: +case 0xAE3D: +case 0xAE3E: +case 0xAE3F: +case 0xAE40: +case 0xAE41: +case 0xAE42: +case 0xAE43: +case 0xAE44: +case 0xAE45: +case 0xAE46: +case 0xAE47: +case 0xAE48: +case 0xAE49: +case 0xAE4A: +case 0xAE4B: +case 0xAE4C: +case 0xAE4D: +case 0xAE4E: +case 0xAE4F: +case 0xAE50: +case 0xAE51: +case 0xAE52: +case 0xAE53: +case 0xAE54: +case 0xAE55: +case 0xAE56: +case 0xAE57: +case 0xAE58: +case 0xAE59: +case 0xAE5A: +case 0xAE5B: +case 0xAE5C: +case 0xAE5D: +case 0xAE5E: +case 0xAE5F: +case 0xAE60: +case 0xAE61: +case 0xAE62: +case 0xAE63: +case 0xAE64: +case 0xAE65: +case 0xAE66: +case 0xAE67: +case 0xAE68: +case 0xAE69: +case 0xAE6A: +case 0xAE6B: +case 0xAE6C: +case 0xAE6D: +case 0xAE6E: +case 0xAE6F: +case 0xAE70: +case 0xAE71: +case 0xAE72: +case 0xAE73: +case 0xAE74: +case 0xAE75: +case 0xAE76: +case 0xAE77: +case 0xAE78: +case 0xAE79: +case 0xAE7A: +case 0xAE7B: +case 0xAE7C: +case 0xAE7D: +case 0xAE7E: +case 0xAE7F: +case 0xAE80: +case 0xAE81: +case 0xAE82: +case 0xAE83: +case 0xAE84: +case 0xAE85: +case 0xAE86: +case 0xAE87: +case 0xAE88: +case 0xAE89: +case 0xAE8A: +case 0xAE8B: +case 0xAE8C: +case 0xAE8D: +case 0xAE8E: +case 0xAE8F: +case 0xAE90: +case 0xAE91: +case 0xAE92: +case 0xAE93: +case 0xAE94: +case 0xAE95: +case 0xAE96: +case 0xAE97: +case 0xAE98: +case 0xAE99: +case 0xAE9A: +case 0xAE9B: +case 0xAE9C: +case 0xAE9D: +case 0xAE9E: +case 0xAE9F: +case 0xAEA0: +case 0xAEA1: +case 0xAEA2: +case 0xAEA3: +case 0xAEA4: +case 0xAEA5: +case 0xAEA6: +case 0xAEA7: +case 0xAEA8: +case 0xAEA9: +case 0xAEAA: +case 0xAEAB: +case 0xAEAC: +case 0xAEAD: +case 0xAEAE: +case 0xAEAF: +case 0xAEB0: +case 0xAEB1: +case 0xAEB2: +case 0xAEB3: +case 0xAEB4: +case 0xAEB5: +case 0xAEB6: +case 0xAEB7: +case 0xAEB8: +case 0xAEB9: +case 0xAEBA: +case 0xAEBB: +case 0xAEBC: +case 0xAEBD: +case 0xAEBE: +case 0xAEBF: +case 0xAEC0: +case 0xAEC1: +case 0xAEC2: +case 0xAEC3: +case 0xAEC4: +case 0xAEC5: +case 0xAEC6: +case 0xAEC7: +case 0xAEC8: +case 0xAEC9: +case 0xAECA: +case 0xAECB: +case 0xAECC: +case 0xAECD: +case 0xAECE: +case 0xAECF: +case 0xAED0: +case 0xAED1: +case 0xAED2: +case 0xAED3: +case 0xAED4: +case 0xAED5: +case 0xAED6: +case 0xAED7: +case 0xAED8: +case 0xAED9: +case 0xAEDA: +case 0xAEDB: +case 0xAEDC: +case 0xAEDD: +case 0xAEDE: +case 0xAEDF: +case 0xAEE0: +case 0xAEE1: +case 0xAEE2: +case 0xAEE3: +case 0xAEE4: +case 0xAEE5: +case 0xAEE6: +case 0xAEE7: +case 0xAEE8: +case 0xAEE9: +case 0xAEEA: +case 0xAEEB: +case 0xAEEC: +case 0xAEED: +case 0xAEEE: +case 0xAEEF: +case 0xAEF0: +case 0xAEF1: +case 0xAEF2: +case 0xAEF3: +case 0xAEF4: +case 0xAEF5: +case 0xAEF6: +case 0xAEF7: +case 0xAEF8: +case 0xAEF9: +case 0xAEFA: +case 0xAEFB: +case 0xAEFC: +case 0xAEFD: +case 0xAEFE: +case 0xAEFF: +case 0xAF00: +case 0xAF01: +case 0xAF02: +case 0xAF03: +case 0xAF04: +case 0xAF05: +case 0xAF06: +case 0xAF07: +case 0xAF08: +case 0xAF09: +case 0xAF0A: +case 0xAF0B: +case 0xAF0C: +case 0xAF0D: +case 0xAF0E: +case 0xAF0F: +case 0xAF10: +case 0xAF11: +case 0xAF12: +case 0xAF13: +case 0xAF14: +case 0xAF15: +case 0xAF16: +case 0xAF17: +case 0xAF18: +case 0xAF19: +case 0xAF1A: +case 0xAF1B: +case 0xAF1C: +case 0xAF1D: +case 0xAF1E: +case 0xAF1F: +case 0xAF20: +case 0xAF21: +case 0xAF22: +case 0xAF23: +case 0xAF24: +case 0xAF25: +case 0xAF26: +case 0xAF27: +case 0xAF28: +case 0xAF29: +case 0xAF2A: +case 0xAF2B: +case 0xAF2C: +case 0xAF2D: +case 0xAF2E: +case 0xAF2F: +case 0xAF30: +case 0xAF31: +case 0xAF32: +case 0xAF33: +case 0xAF34: +case 0xAF35: +case 0xAF36: +case 0xAF37: +case 0xAF38: +case 0xAF39: +case 0xAF3A: +case 0xAF3B: +case 0xAF3C: +case 0xAF3D: +case 0xAF3E: +case 0xAF3F: +case 0xAF40: +case 0xAF41: +case 0xAF42: +case 0xAF43: +case 0xAF44: +case 0xAF45: +case 0xAF46: +case 0xAF47: +case 0xAF48: +case 0xAF49: +case 0xAF4A: +case 0xAF4B: +case 0xAF4C: +case 0xAF4D: +case 0xAF4E: +case 0xAF4F: +case 0xAF50: +case 0xAF51: +case 0xAF52: +case 0xAF53: +case 0xAF54: +case 0xAF55: +case 0xAF56: +case 0xAF57: +case 0xAF58: +case 0xAF59: +case 0xAF5A: +case 0xAF5B: +case 0xAF5C: +case 0xAF5D: +case 0xAF5E: +case 0xAF5F: +case 0xAF60: +case 0xAF61: +case 0xAF62: +case 0xAF63: +case 0xAF64: +case 0xAF65: +case 0xAF66: +case 0xAF67: +case 0xAF68: +case 0xAF69: +case 0xAF6A: +case 0xAF6B: +case 0xAF6C: +case 0xAF6D: +case 0xAF6E: +case 0xAF6F: +case 0xAF70: +case 0xAF71: +case 0xAF72: +case 0xAF73: +case 0xAF74: +case 0xAF75: +case 0xAF76: +case 0xAF77: +case 0xAF78: +case 0xAF79: +case 0xAF7A: +case 0xAF7B: +case 0xAF7C: +case 0xAF7D: +case 0xAF7E: +case 0xAF7F: +case 0xAF80: +case 0xAF81: +case 0xAF82: +case 0xAF83: +case 0xAF84: +case 0xAF85: +case 0xAF86: +case 0xAF87: +case 0xAF88: +case 0xAF89: +case 0xAF8A: +case 0xAF8B: +case 0xAF8C: +case 0xAF8D: +case 0xAF8E: +case 0xAF8F: +case 0xAF90: +case 0xAF91: +case 0xAF92: +case 0xAF93: +case 0xAF94: +case 0xAF95: +case 0xAF96: +case 0xAF97: +case 0xAF98: +case 0xAF99: +case 0xAF9A: +case 0xAF9B: +case 0xAF9C: +case 0xAF9D: +case 0xAF9E: +case 0xAF9F: +case 0xAFA0: +case 0xAFA1: +case 0xAFA2: +case 0xAFA3: +case 0xAFA4: +case 0xAFA5: +case 0xAFA6: +case 0xAFA7: +case 0xAFA8: +case 0xAFA9: +case 0xAFAA: +case 0xAFAB: +case 0xAFAC: +case 0xAFAD: +case 0xAFAE: +case 0xAFAF: +case 0xAFB0: +case 0xAFB1: +case 0xAFB2: +case 0xAFB3: +case 0xAFB4: +case 0xAFB5: +case 0xAFB6: +case 0xAFB7: +case 0xAFB8: +case 0xAFB9: +case 0xAFBA: +case 0xAFBB: +case 0xAFBC: +case 0xAFBD: +case 0xAFBE: +case 0xAFBF: +case 0xAFC0: +case 0xAFC1: +case 0xAFC2: +case 0xAFC3: +case 0xAFC4: +case 0xAFC5: +case 0xAFC6: +case 0xAFC7: +case 0xAFC8: +case 0xAFC9: +case 0xAFCA: +case 0xAFCB: +case 0xAFCC: +case 0xAFCD: +case 0xAFCE: +case 0xAFCF: +case 0xAFD0: +case 0xAFD1: +case 0xAFD2: +case 0xAFD3: +case 0xAFD4: +case 0xAFD5: +case 0xAFD6: +case 0xAFD7: +case 0xAFD8: +case 0xAFD9: +case 0xAFDA: +case 0xAFDB: +case 0xAFDC: +case 0xAFDD: +case 0xAFDE: +case 0xAFDF: +case 0xAFE0: +case 0xAFE1: +case 0xAFE2: +case 0xAFE3: +case 0xAFE4: +case 0xAFE5: +case 0xAFE6: +case 0xAFE7: +case 0xAFE8: +case 0xAFE9: +case 0xAFEA: +case 0xAFEB: +case 0xAFEC: +case 0xAFED: +case 0xAFEE: +case 0xAFEF: +case 0xAFF0: +case 0xAFF1: +case 0xAFF2: +case 0xAFF3: +case 0xAFF4: +case 0xAFF5: +case 0xAFF6: +case 0xAFF7: +case 0xAFF8: +case 0xAFF9: +case 0xAFFA: +case 0xAFFB: +case 0xAFFC: +case 0xAFFD: +case 0xAFFE: +case 0xAFFF: + +// 1010 +case 0xA000: +{ + u32 res; + PC -= 2; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_1010_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO +} +RET(4) diff --git a/yabause/src/c68k/c68k_opB.inc b/yabause/src/c68k/c68k_opB.inc new file mode 100644 index 0000000000..84175c98a9 --- /dev/null +++ b/yabause/src/c68k/c68k_opB.inc @@ -0,0 +1,5970 @@ +case 0xB200: +case 0xB400: +case 0xB600: +case 0xB800: +case 0xBA00: +case 0xBC00: +case 0xBE00: +case 0xB001: +case 0xB201: +case 0xB401: +case 0xB601: +case 0xB801: +case 0xBA01: +case 0xBC01: +case 0xBE01: +case 0xB002: +case 0xB202: +case 0xB402: +case 0xB602: +case 0xB802: +case 0xBA02: +case 0xBC02: +case 0xBE02: +case 0xB003: +case 0xB203: +case 0xB403: +case 0xB603: +case 0xB803: +case 0xBA03: +case 0xBC03: +case 0xBE03: +case 0xB004: +case 0xB204: +case 0xB404: +case 0xB604: +case 0xB804: +case 0xBA04: +case 0xBC04: +case 0xBE04: +case 0xB005: +case 0xB205: +case 0xB405: +case 0xB605: +case 0xB805: +case 0xBA05: +case 0xBC05: +case 0xBE05: +case 0xB006: +case 0xB206: +case 0xB406: +case 0xB606: +case 0xB806: +case 0xBA06: +case 0xBC06: +case 0xBE06: +case 0xB007: +case 0xB207: +case 0xB407: +case 0xB607: +case 0xB807: +case 0xBA07: +case 0xBC07: +case 0xBE07: + +// CMP +case 0xB000: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; +} +RET(4) +case 0xB208: +case 0xB408: +case 0xB608: +case 0xB808: +case 0xBA08: +case 0xBC08: +case 0xBE08: +case 0xB009: +case 0xB209: +case 0xB409: +case 0xB609: +case 0xB809: +case 0xBA09: +case 0xBC09: +case 0xBE09: +case 0xB00A: +case 0xB20A: +case 0xB40A: +case 0xB60A: +case 0xB80A: +case 0xBA0A: +case 0xBC0A: +case 0xBE0A: +case 0xB00B: +case 0xB20B: +case 0xB40B: +case 0xB60B: +case 0xB80B: +case 0xBA0B: +case 0xBC0B: +case 0xBE0B: +case 0xB00C: +case 0xB20C: +case 0xB40C: +case 0xB60C: +case 0xB80C: +case 0xBA0C: +case 0xBC0C: +case 0xBE0C: +case 0xB00D: +case 0xB20D: +case 0xB40D: +case 0xB60D: +case 0xB80D: +case 0xBA0D: +case 0xBC0D: +case 0xBE0D: +case 0xB00E: +case 0xB20E: +case 0xB40E: +case 0xB60E: +case 0xB80E: +case 0xBA0E: +case 0xBC0E: +case 0xBE0E: +case 0xB00F: +case 0xB20F: +case 0xB40F: +case 0xB60F: +case 0xB80F: +case 0xBA0F: +case 0xBC0F: +case 0xBE0F: + +// CMP +case 0xB008: +{ + u32 res; + pointer dst; + pointer src; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; +} +RET(4) +case 0xB210: +case 0xB410: +case 0xB610: +case 0xB810: +case 0xBA10: +case 0xBC10: +case 0xBE10: +case 0xB011: +case 0xB211: +case 0xB411: +case 0xB611: +case 0xB811: +case 0xBA11: +case 0xBC11: +case 0xBE11: +case 0xB012: +case 0xB212: +case 0xB412: +case 0xB612: +case 0xB812: +case 0xBA12: +case 0xBC12: +case 0xBE12: +case 0xB013: +case 0xB213: +case 0xB413: +case 0xB613: +case 0xB813: +case 0xBA13: +case 0xBC13: +case 0xBE13: +case 0xB014: +case 0xB214: +case 0xB414: +case 0xB614: +case 0xB814: +case 0xBA14: +case 0xBC14: +case 0xBE14: +case 0xB015: +case 0xB215: +case 0xB415: +case 0xB615: +case 0xB815: +case 0xBA15: +case 0xBC15: +case 0xBE15: +case 0xB016: +case 0xB216: +case 0xB416: +case 0xB616: +case 0xB816: +case 0xBA16: +case 0xBC16: +case 0xBE16: +case 0xB017: +case 0xB217: +case 0xB417: +case 0xB617: +case 0xB817: +case 0xBA17: +case 0xBC17: +case 0xBE17: + +// CMP +case 0xB010: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(8) +case 0xB218: +case 0xB418: +case 0xB618: +case 0xB818: +case 0xBA18: +case 0xBC18: +case 0xBE18: +case 0xB019: +case 0xB219: +case 0xB419: +case 0xB619: +case 0xB819: +case 0xBA19: +case 0xBC19: +case 0xBE19: +case 0xB01A: +case 0xB21A: +case 0xB41A: +case 0xB61A: +case 0xB81A: +case 0xBA1A: +case 0xBC1A: +case 0xBE1A: +case 0xB01B: +case 0xB21B: +case 0xB41B: +case 0xB61B: +case 0xB81B: +case 0xBA1B: +case 0xBC1B: +case 0xBE1B: +case 0xB01C: +case 0xB21C: +case 0xB41C: +case 0xB61C: +case 0xB81C: +case 0xBA1C: +case 0xBC1C: +case 0xBE1C: +case 0xB01D: +case 0xB21D: +case 0xB41D: +case 0xB61D: +case 0xB81D: +case 0xBA1D: +case 0xBC1D: +case 0xBE1D: +case 0xB01E: +case 0xB21E: +case 0xB41E: +case 0xB61E: +case 0xB81E: +case 0xBA1E: +case 0xBC1E: +case 0xBE1E: + +// CMP +case 0xB018: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(8) +case 0xB220: +case 0xB420: +case 0xB620: +case 0xB820: +case 0xBA20: +case 0xBC20: +case 0xBE20: +case 0xB021: +case 0xB221: +case 0xB421: +case 0xB621: +case 0xB821: +case 0xBA21: +case 0xBC21: +case 0xBE21: +case 0xB022: +case 0xB222: +case 0xB422: +case 0xB622: +case 0xB822: +case 0xBA22: +case 0xBC22: +case 0xBE22: +case 0xB023: +case 0xB223: +case 0xB423: +case 0xB623: +case 0xB823: +case 0xBA23: +case 0xBC23: +case 0xBE23: +case 0xB024: +case 0xB224: +case 0xB424: +case 0xB624: +case 0xB824: +case 0xBA24: +case 0xBC24: +case 0xBE24: +case 0xB025: +case 0xB225: +case 0xB425: +case 0xB625: +case 0xB825: +case 0xBA25: +case 0xBC25: +case 0xBE25: +case 0xB026: +case 0xB226: +case 0xB426: +case 0xB626: +case 0xB826: +case 0xBA26: +case 0xBC26: +case 0xBE26: + +// CMP +case 0xB020: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(10) +case 0xB228: +case 0xB428: +case 0xB628: +case 0xB828: +case 0xBA28: +case 0xBC28: +case 0xBE28: +case 0xB029: +case 0xB229: +case 0xB429: +case 0xB629: +case 0xB829: +case 0xBA29: +case 0xBC29: +case 0xBE29: +case 0xB02A: +case 0xB22A: +case 0xB42A: +case 0xB62A: +case 0xB82A: +case 0xBA2A: +case 0xBC2A: +case 0xBE2A: +case 0xB02B: +case 0xB22B: +case 0xB42B: +case 0xB62B: +case 0xB82B: +case 0xBA2B: +case 0xBC2B: +case 0xBE2B: +case 0xB02C: +case 0xB22C: +case 0xB42C: +case 0xB62C: +case 0xB82C: +case 0xBA2C: +case 0xBC2C: +case 0xBE2C: +case 0xB02D: +case 0xB22D: +case 0xB42D: +case 0xB62D: +case 0xB82D: +case 0xBA2D: +case 0xBC2D: +case 0xBE2D: +case 0xB02E: +case 0xB22E: +case 0xB42E: +case 0xB62E: +case 0xB82E: +case 0xBA2E: +case 0xBC2E: +case 0xBE2E: +case 0xB02F: +case 0xB22F: +case 0xB42F: +case 0xB62F: +case 0xB82F: +case 0xBA2F: +case 0xBC2F: +case 0xBE2F: + +// CMP +case 0xB028: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xB230: +case 0xB430: +case 0xB630: +case 0xB830: +case 0xBA30: +case 0xBC30: +case 0xBE30: +case 0xB031: +case 0xB231: +case 0xB431: +case 0xB631: +case 0xB831: +case 0xBA31: +case 0xBC31: +case 0xBE31: +case 0xB032: +case 0xB232: +case 0xB432: +case 0xB632: +case 0xB832: +case 0xBA32: +case 0xBC32: +case 0xBE32: +case 0xB033: +case 0xB233: +case 0xB433: +case 0xB633: +case 0xB833: +case 0xBA33: +case 0xBC33: +case 0xBE33: +case 0xB034: +case 0xB234: +case 0xB434: +case 0xB634: +case 0xB834: +case 0xBA34: +case 0xBC34: +case 0xBE34: +case 0xB035: +case 0xB235: +case 0xB435: +case 0xB635: +case 0xB835: +case 0xBA35: +case 0xBC35: +case 0xBE35: +case 0xB036: +case 0xB236: +case 0xB436: +case 0xB636: +case 0xB836: +case 0xBA36: +case 0xBC36: +case 0xBE36: +case 0xB037: +case 0xB237: +case 0xB437: +case 0xB637: +case 0xB837: +case 0xBA37: +case 0xBC37: +case 0xBE37: + +// CMP +case 0xB030: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(14) +case 0xB238: +case 0xB438: +case 0xB638: +case 0xB838: +case 0xBA38: +case 0xBC38: +case 0xBE38: + +// CMP +case 0xB038: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xB239: +case 0xB439: +case 0xB639: +case 0xB839: +case 0xBA39: +case 0xBC39: +case 0xBE39: + +// CMP +case 0xB039: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(16) +case 0xB23A: +case 0xB43A: +case 0xB63A: +case 0xB83A: +case 0xBA3A: +case 0xBC3A: +case 0xBE3A: + +// CMP +case 0xB03A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xB23B: +case 0xB43B: +case 0xB63B: +case 0xB83B: +case 0xBA3B: +case 0xBC3B: +case 0xBE3B: + +// CMP +case 0xB03B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(14) +case 0xB23C: +case 0xB43C: +case 0xB63C: +case 0xB83C: +case 0xBA3C: +case 0xBC3C: +case 0xBE3C: + +// CMP +case 0xB03C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; +} +RET(8) +case 0xB21F: +case 0xB41F: +case 0xB61F: +case 0xB81F: +case 0xBA1F: +case 0xBC1F: +case 0xBE1F: + +// CMP +case 0xB01F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(8) +case 0xB227: +case 0xB427: +case 0xB627: +case 0xB827: +case 0xBA27: +case 0xBC27: +case 0xBE27: + +// CMP +case 0xB027: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(10) +case 0xB240: +case 0xB440: +case 0xB640: +case 0xB840: +case 0xBA40: +case 0xBC40: +case 0xBE40: +case 0xB041: +case 0xB241: +case 0xB441: +case 0xB641: +case 0xB841: +case 0xBA41: +case 0xBC41: +case 0xBE41: +case 0xB042: +case 0xB242: +case 0xB442: +case 0xB642: +case 0xB842: +case 0xBA42: +case 0xBC42: +case 0xBE42: +case 0xB043: +case 0xB243: +case 0xB443: +case 0xB643: +case 0xB843: +case 0xBA43: +case 0xBC43: +case 0xBE43: +case 0xB044: +case 0xB244: +case 0xB444: +case 0xB644: +case 0xB844: +case 0xBA44: +case 0xBC44: +case 0xBE44: +case 0xB045: +case 0xB245: +case 0xB445: +case 0xB645: +case 0xB845: +case 0xBA45: +case 0xBC45: +case 0xBE45: +case 0xB046: +case 0xB246: +case 0xB446: +case 0xB646: +case 0xB846: +case 0xBA46: +case 0xBC46: +case 0xBE46: +case 0xB047: +case 0xB247: +case 0xB447: +case 0xB647: +case 0xB847: +case 0xBA47: +case 0xBC47: +case 0xBE47: + +// CMP +case 0xB040: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; +} +RET(4) +case 0xB248: +case 0xB448: +case 0xB648: +case 0xB848: +case 0xBA48: +case 0xBC48: +case 0xBE48: +case 0xB049: +case 0xB249: +case 0xB449: +case 0xB649: +case 0xB849: +case 0xBA49: +case 0xBC49: +case 0xBE49: +case 0xB04A: +case 0xB24A: +case 0xB44A: +case 0xB64A: +case 0xB84A: +case 0xBA4A: +case 0xBC4A: +case 0xBE4A: +case 0xB04B: +case 0xB24B: +case 0xB44B: +case 0xB64B: +case 0xB84B: +case 0xBA4B: +case 0xBC4B: +case 0xBE4B: +case 0xB04C: +case 0xB24C: +case 0xB44C: +case 0xB64C: +case 0xB84C: +case 0xBA4C: +case 0xBC4C: +case 0xBE4C: +case 0xB04D: +case 0xB24D: +case 0xB44D: +case 0xB64D: +case 0xB84D: +case 0xBA4D: +case 0xBC4D: +case 0xBE4D: +case 0xB04E: +case 0xB24E: +case 0xB44E: +case 0xB64E: +case 0xB84E: +case 0xBA4E: +case 0xBC4E: +case 0xBE4E: +case 0xB04F: +case 0xB24F: +case 0xB44F: +case 0xB64F: +case 0xB84F: +case 0xBA4F: +case 0xBC4F: +case 0xBE4F: + +// CMP +case 0xB048: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->A[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; +} +RET(4) +case 0xB250: +case 0xB450: +case 0xB650: +case 0xB850: +case 0xBA50: +case 0xBC50: +case 0xBE50: +case 0xB051: +case 0xB251: +case 0xB451: +case 0xB651: +case 0xB851: +case 0xBA51: +case 0xBC51: +case 0xBE51: +case 0xB052: +case 0xB252: +case 0xB452: +case 0xB652: +case 0xB852: +case 0xBA52: +case 0xBC52: +case 0xBE52: +case 0xB053: +case 0xB253: +case 0xB453: +case 0xB653: +case 0xB853: +case 0xBA53: +case 0xBC53: +case 0xBE53: +case 0xB054: +case 0xB254: +case 0xB454: +case 0xB654: +case 0xB854: +case 0xBA54: +case 0xBC54: +case 0xBE54: +case 0xB055: +case 0xB255: +case 0xB455: +case 0xB655: +case 0xB855: +case 0xBA55: +case 0xBC55: +case 0xBE55: +case 0xB056: +case 0xB256: +case 0xB456: +case 0xB656: +case 0xB856: +case 0xBA56: +case 0xBC56: +case 0xBE56: +case 0xB057: +case 0xB257: +case 0xB457: +case 0xB657: +case 0xB857: +case 0xBA57: +case 0xBC57: +case 0xBE57: + +// CMP +case 0xB050: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(8) +case 0xB258: +case 0xB458: +case 0xB658: +case 0xB858: +case 0xBA58: +case 0xBC58: +case 0xBE58: +case 0xB059: +case 0xB259: +case 0xB459: +case 0xB659: +case 0xB859: +case 0xBA59: +case 0xBC59: +case 0xBE59: +case 0xB05A: +case 0xB25A: +case 0xB45A: +case 0xB65A: +case 0xB85A: +case 0xBA5A: +case 0xBC5A: +case 0xBE5A: +case 0xB05B: +case 0xB25B: +case 0xB45B: +case 0xB65B: +case 0xB85B: +case 0xBA5B: +case 0xBC5B: +case 0xBE5B: +case 0xB05C: +case 0xB25C: +case 0xB45C: +case 0xB65C: +case 0xB85C: +case 0xBA5C: +case 0xBC5C: +case 0xBE5C: +case 0xB05D: +case 0xB25D: +case 0xB45D: +case 0xB65D: +case 0xB85D: +case 0xBA5D: +case 0xBC5D: +case 0xBE5D: +case 0xB05E: +case 0xB25E: +case 0xB45E: +case 0xB65E: +case 0xB85E: +case 0xBA5E: +case 0xBC5E: +case 0xBE5E: + +// CMP +case 0xB058: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(8) +case 0xB260: +case 0xB460: +case 0xB660: +case 0xB860: +case 0xBA60: +case 0xBC60: +case 0xBE60: +case 0xB061: +case 0xB261: +case 0xB461: +case 0xB661: +case 0xB861: +case 0xBA61: +case 0xBC61: +case 0xBE61: +case 0xB062: +case 0xB262: +case 0xB462: +case 0xB662: +case 0xB862: +case 0xBA62: +case 0xBC62: +case 0xBE62: +case 0xB063: +case 0xB263: +case 0xB463: +case 0xB663: +case 0xB863: +case 0xBA63: +case 0xBC63: +case 0xBE63: +case 0xB064: +case 0xB264: +case 0xB464: +case 0xB664: +case 0xB864: +case 0xBA64: +case 0xBC64: +case 0xBE64: +case 0xB065: +case 0xB265: +case 0xB465: +case 0xB665: +case 0xB865: +case 0xBA65: +case 0xBC65: +case 0xBE65: +case 0xB066: +case 0xB266: +case 0xB466: +case 0xB666: +case 0xB866: +case 0xBA66: +case 0xBC66: +case 0xBE66: + +// CMP +case 0xB060: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(10) +case 0xB268: +case 0xB468: +case 0xB668: +case 0xB868: +case 0xBA68: +case 0xBC68: +case 0xBE68: +case 0xB069: +case 0xB269: +case 0xB469: +case 0xB669: +case 0xB869: +case 0xBA69: +case 0xBC69: +case 0xBE69: +case 0xB06A: +case 0xB26A: +case 0xB46A: +case 0xB66A: +case 0xB86A: +case 0xBA6A: +case 0xBC6A: +case 0xBE6A: +case 0xB06B: +case 0xB26B: +case 0xB46B: +case 0xB66B: +case 0xB86B: +case 0xBA6B: +case 0xBC6B: +case 0xBE6B: +case 0xB06C: +case 0xB26C: +case 0xB46C: +case 0xB66C: +case 0xB86C: +case 0xBA6C: +case 0xBC6C: +case 0xBE6C: +case 0xB06D: +case 0xB26D: +case 0xB46D: +case 0xB66D: +case 0xB86D: +case 0xBA6D: +case 0xBC6D: +case 0xBE6D: +case 0xB06E: +case 0xB26E: +case 0xB46E: +case 0xB66E: +case 0xB86E: +case 0xBA6E: +case 0xBC6E: +case 0xBE6E: +case 0xB06F: +case 0xB26F: +case 0xB46F: +case 0xB66F: +case 0xB86F: +case 0xBA6F: +case 0xBC6F: +case 0xBE6F: + +// CMP +case 0xB068: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xB270: +case 0xB470: +case 0xB670: +case 0xB870: +case 0xBA70: +case 0xBC70: +case 0xBE70: +case 0xB071: +case 0xB271: +case 0xB471: +case 0xB671: +case 0xB871: +case 0xBA71: +case 0xBC71: +case 0xBE71: +case 0xB072: +case 0xB272: +case 0xB472: +case 0xB672: +case 0xB872: +case 0xBA72: +case 0xBC72: +case 0xBE72: +case 0xB073: +case 0xB273: +case 0xB473: +case 0xB673: +case 0xB873: +case 0xBA73: +case 0xBC73: +case 0xBE73: +case 0xB074: +case 0xB274: +case 0xB474: +case 0xB674: +case 0xB874: +case 0xBA74: +case 0xBC74: +case 0xBE74: +case 0xB075: +case 0xB275: +case 0xB475: +case 0xB675: +case 0xB875: +case 0xBA75: +case 0xBC75: +case 0xBE75: +case 0xB076: +case 0xB276: +case 0xB476: +case 0xB676: +case 0xB876: +case 0xBA76: +case 0xBC76: +case 0xBE76: +case 0xB077: +case 0xB277: +case 0xB477: +case 0xB677: +case 0xB877: +case 0xBA77: +case 0xBC77: +case 0xBE77: + +// CMP +case 0xB070: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(14) +case 0xB278: +case 0xB478: +case 0xB678: +case 0xB878: +case 0xBA78: +case 0xBC78: +case 0xBE78: + +// CMP +case 0xB078: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xB279: +case 0xB479: +case 0xB679: +case 0xB879: +case 0xBA79: +case 0xBC79: +case 0xBE79: + +// CMP +case 0xB079: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(16) +case 0xB27A: +case 0xB47A: +case 0xB67A: +case 0xB87A: +case 0xBA7A: +case 0xBC7A: +case 0xBE7A: + +// CMP +case 0xB07A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xB27B: +case 0xB47B: +case 0xB67B: +case 0xB87B: +case 0xBA7B: +case 0xBC7B: +case 0xBE7B: + +// CMP +case 0xB07B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(14) +case 0xB27C: +case 0xB47C: +case 0xB67C: +case 0xB87C: +case 0xBA7C: +case 0xBC7C: +case 0xBE7C: + +// CMP +case 0xB07C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; +} +RET(8) +case 0xB25F: +case 0xB45F: +case 0xB65F: +case 0xB85F: +case 0xBA5F: +case 0xBC5F: +case 0xBE5F: + +// CMP +case 0xB05F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(8) +case 0xB267: +case 0xB467: +case 0xB667: +case 0xB867: +case 0xBA67: +case 0xBC67: +case 0xBE67: + +// CMP +case 0xB067: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(10) +case 0xB280: +case 0xB480: +case 0xB680: +case 0xB880: +case 0xBA80: +case 0xBC80: +case 0xBE80: +case 0xB081: +case 0xB281: +case 0xB481: +case 0xB681: +case 0xB881: +case 0xBA81: +case 0xBC81: +case 0xBE81: +case 0xB082: +case 0xB282: +case 0xB482: +case 0xB682: +case 0xB882: +case 0xBA82: +case 0xBC82: +case 0xBE82: +case 0xB083: +case 0xB283: +case 0xB483: +case 0xB683: +case 0xB883: +case 0xBA83: +case 0xBC83: +case 0xBE83: +case 0xB084: +case 0xB284: +case 0xB484: +case 0xB684: +case 0xB884: +case 0xBA84: +case 0xBC84: +case 0xBE84: +case 0xB085: +case 0xB285: +case 0xB485: +case 0xB685: +case 0xB885: +case 0xBA85: +case 0xBC85: +case 0xBE85: +case 0xB086: +case 0xB286: +case 0xB486: +case 0xB686: +case 0xB886: +case 0xBA86: +case 0xBC86: +case 0xBE86: +case 0xB087: +case 0xB287: +case 0xB487: +case 0xB687: +case 0xB887: +case 0xBA87: +case 0xBC87: +case 0xBE87: + +// CMP +case 0xB080: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB288: +case 0xB488: +case 0xB688: +case 0xB888: +case 0xBA88: +case 0xBC88: +case 0xBE88: +case 0xB089: +case 0xB289: +case 0xB489: +case 0xB689: +case 0xB889: +case 0xBA89: +case 0xBC89: +case 0xBE89: +case 0xB08A: +case 0xB28A: +case 0xB48A: +case 0xB68A: +case 0xB88A: +case 0xBA8A: +case 0xBC8A: +case 0xBE8A: +case 0xB08B: +case 0xB28B: +case 0xB48B: +case 0xB68B: +case 0xB88B: +case 0xBA8B: +case 0xBC8B: +case 0xBE8B: +case 0xB08C: +case 0xB28C: +case 0xB48C: +case 0xB68C: +case 0xB88C: +case 0xBA8C: +case 0xBC8C: +case 0xBE8C: +case 0xB08D: +case 0xB28D: +case 0xB48D: +case 0xB68D: +case 0xB88D: +case 0xBA8D: +case 0xBC8D: +case 0xBE8D: +case 0xB08E: +case 0xB28E: +case 0xB48E: +case 0xB68E: +case 0xB88E: +case 0xBA8E: +case 0xBC8E: +case 0xBE8E: +case 0xB08F: +case 0xB28F: +case 0xB48F: +case 0xB68F: +case 0xB88F: +case 0xBA8F: +case 0xBC8F: +case 0xBE8F: + +// CMP +case 0xB088: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB290: +case 0xB490: +case 0xB690: +case 0xB890: +case 0xBA90: +case 0xBC90: +case 0xBE90: +case 0xB091: +case 0xB291: +case 0xB491: +case 0xB691: +case 0xB891: +case 0xBA91: +case 0xBC91: +case 0xBE91: +case 0xB092: +case 0xB292: +case 0xB492: +case 0xB692: +case 0xB892: +case 0xBA92: +case 0xBC92: +case 0xBE92: +case 0xB093: +case 0xB293: +case 0xB493: +case 0xB693: +case 0xB893: +case 0xBA93: +case 0xBC93: +case 0xBE93: +case 0xB094: +case 0xB294: +case 0xB494: +case 0xB694: +case 0xB894: +case 0xBA94: +case 0xBC94: +case 0xBE94: +case 0xB095: +case 0xB295: +case 0xB495: +case 0xB695: +case 0xB895: +case 0xBA95: +case 0xBC95: +case 0xBE95: +case 0xB096: +case 0xB296: +case 0xB496: +case 0xB696: +case 0xB896: +case 0xBA96: +case 0xBC96: +case 0xBE96: +case 0xB097: +case 0xB297: +case 0xB497: +case 0xB697: +case 0xB897: +case 0xBA97: +case 0xBC97: +case 0xBE97: + +// CMP +case 0xB090: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB298: +case 0xB498: +case 0xB698: +case 0xB898: +case 0xBA98: +case 0xBC98: +case 0xBE98: +case 0xB099: +case 0xB299: +case 0xB499: +case 0xB699: +case 0xB899: +case 0xBA99: +case 0xBC99: +case 0xBE99: +case 0xB09A: +case 0xB29A: +case 0xB49A: +case 0xB69A: +case 0xB89A: +case 0xBA9A: +case 0xBC9A: +case 0xBE9A: +case 0xB09B: +case 0xB29B: +case 0xB49B: +case 0xB69B: +case 0xB89B: +case 0xBA9B: +case 0xBC9B: +case 0xBE9B: +case 0xB09C: +case 0xB29C: +case 0xB49C: +case 0xB69C: +case 0xB89C: +case 0xBA9C: +case 0xBC9C: +case 0xBE9C: +case 0xB09D: +case 0xB29D: +case 0xB49D: +case 0xB69D: +case 0xB89D: +case 0xBA9D: +case 0xBC9D: +case 0xBE9D: +case 0xB09E: +case 0xB29E: +case 0xB49E: +case 0xB69E: +case 0xB89E: +case 0xBA9E: +case 0xBC9E: +case 0xBE9E: + +// CMP +case 0xB098: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB2A0: +case 0xB4A0: +case 0xB6A0: +case 0xB8A0: +case 0xBAA0: +case 0xBCA0: +case 0xBEA0: +case 0xB0A1: +case 0xB2A1: +case 0xB4A1: +case 0xB6A1: +case 0xB8A1: +case 0xBAA1: +case 0xBCA1: +case 0xBEA1: +case 0xB0A2: +case 0xB2A2: +case 0xB4A2: +case 0xB6A2: +case 0xB8A2: +case 0xBAA2: +case 0xBCA2: +case 0xBEA2: +case 0xB0A3: +case 0xB2A3: +case 0xB4A3: +case 0xB6A3: +case 0xB8A3: +case 0xBAA3: +case 0xBCA3: +case 0xBEA3: +case 0xB0A4: +case 0xB2A4: +case 0xB4A4: +case 0xB6A4: +case 0xB8A4: +case 0xBAA4: +case 0xBCA4: +case 0xBEA4: +case 0xB0A5: +case 0xB2A5: +case 0xB4A5: +case 0xB6A5: +case 0xB8A5: +case 0xBAA5: +case 0xBCA5: +case 0xBEA5: +case 0xB0A6: +case 0xB2A6: +case 0xB4A6: +case 0xB6A6: +case 0xB8A6: +case 0xBAA6: +case 0xBCA6: +case 0xBEA6: + +// CMP +case 0xB0A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB2A8: +case 0xB4A8: +case 0xB6A8: +case 0xB8A8: +case 0xBAA8: +case 0xBCA8: +case 0xBEA8: +case 0xB0A9: +case 0xB2A9: +case 0xB4A9: +case 0xB6A9: +case 0xB8A9: +case 0xBAA9: +case 0xBCA9: +case 0xBEA9: +case 0xB0AA: +case 0xB2AA: +case 0xB4AA: +case 0xB6AA: +case 0xB8AA: +case 0xBAAA: +case 0xBCAA: +case 0xBEAA: +case 0xB0AB: +case 0xB2AB: +case 0xB4AB: +case 0xB6AB: +case 0xB8AB: +case 0xBAAB: +case 0xBCAB: +case 0xBEAB: +case 0xB0AC: +case 0xB2AC: +case 0xB4AC: +case 0xB6AC: +case 0xB8AC: +case 0xBAAC: +case 0xBCAC: +case 0xBEAC: +case 0xB0AD: +case 0xB2AD: +case 0xB4AD: +case 0xB6AD: +case 0xB8AD: +case 0xBAAD: +case 0xBCAD: +case 0xBEAD: +case 0xB0AE: +case 0xB2AE: +case 0xB4AE: +case 0xB6AE: +case 0xB8AE: +case 0xBAAE: +case 0xBCAE: +case 0xBEAE: +case 0xB0AF: +case 0xB2AF: +case 0xB4AF: +case 0xB6AF: +case 0xB8AF: +case 0xBAAF: +case 0xBCAF: +case 0xBEAF: + +// CMP +case 0xB0A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB2B0: +case 0xB4B0: +case 0xB6B0: +case 0xB8B0: +case 0xBAB0: +case 0xBCB0: +case 0xBEB0: +case 0xB0B1: +case 0xB2B1: +case 0xB4B1: +case 0xB6B1: +case 0xB8B1: +case 0xBAB1: +case 0xBCB1: +case 0xBEB1: +case 0xB0B2: +case 0xB2B2: +case 0xB4B2: +case 0xB6B2: +case 0xB8B2: +case 0xBAB2: +case 0xBCB2: +case 0xBEB2: +case 0xB0B3: +case 0xB2B3: +case 0xB4B3: +case 0xB6B3: +case 0xB8B3: +case 0xBAB3: +case 0xBCB3: +case 0xBEB3: +case 0xB0B4: +case 0xB2B4: +case 0xB4B4: +case 0xB6B4: +case 0xB8B4: +case 0xBAB4: +case 0xBCB4: +case 0xBEB4: +case 0xB0B5: +case 0xB2B5: +case 0xB4B5: +case 0xB6B5: +case 0xB8B5: +case 0xBAB5: +case 0xBCB5: +case 0xBEB5: +case 0xB0B6: +case 0xB2B6: +case 0xB4B6: +case 0xB6B6: +case 0xB8B6: +case 0xBAB6: +case 0xBCB6: +case 0xBEB6: +case 0xB0B7: +case 0xB2B7: +case 0xB4B7: +case 0xB6B7: +case 0xB8B7: +case 0xBAB7: +case 0xBCB7: +case 0xBEB7: + +// CMP +case 0xB0B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(22) +case 0xB2B8: +case 0xB4B8: +case 0xB6B8: +case 0xB8B8: +case 0xBAB8: +case 0xBCB8: +case 0xBEB8: + +// CMP +case 0xB0B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB2B9: +case 0xB4B9: +case 0xB6B9: +case 0xB8B9: +case 0xBAB9: +case 0xBCB9: +case 0xBEB9: + +// CMP +case 0xB0B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(24) +case 0xB2BA: +case 0xB4BA: +case 0xB6BA: +case 0xB8BA: +case 0xBABA: +case 0xBCBA: +case 0xBEBA: + +// CMP +case 0xB0BA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB2BB: +case 0xB4BB: +case 0xB6BB: +case 0xB8BB: +case 0xBABB: +case 0xBCBB: +case 0xBEBB: + +// CMP +case 0xB0BB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(22) +case 0xB2BC: +case 0xB4BC: +case 0xB6BC: +case 0xB8BC: +case 0xBABC: +case 0xBCBC: +case 0xBEBC: + +// CMP +case 0xB0BC: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(14) +case 0xB29F: +case 0xB49F: +case 0xB69F: +case 0xB89F: +case 0xBA9F: +case 0xBC9F: +case 0xBE9F: + +// CMP +case 0xB09F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB2A7: +case 0xB4A7: +case 0xB6A7: +case 0xB8A7: +case 0xBAA7: +case 0xBCA7: +case 0xBEA7: + +// CMP +case 0xB0A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB308: +case 0xB508: +case 0xB708: +case 0xB908: +case 0xBB08: +case 0xBD08: +case 0xB109: +case 0xB309: +case 0xB509: +case 0xB709: +case 0xB909: +case 0xBB09: +case 0xBD09: +case 0xB10A: +case 0xB30A: +case 0xB50A: +case 0xB70A: +case 0xB90A: +case 0xBB0A: +case 0xBD0A: +case 0xB10B: +case 0xB30B: +case 0xB50B: +case 0xB70B: +case 0xB90B: +case 0xBB0B: +case 0xBD0B: +case 0xB10C: +case 0xB30C: +case 0xB50C: +case 0xB70C: +case 0xB90C: +case 0xBB0C: +case 0xBD0C: +case 0xB10D: +case 0xB30D: +case 0xB50D: +case 0xB70D: +case 0xB90D: +case 0xBB0D: +case 0xBD0D: +case 0xB10E: +case 0xB30E: +case 0xB50E: +case 0xB70E: +case 0xB90E: +case 0xBB0E: +case 0xBD0E: + +// CMPM +case 0xB108: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xB348: +case 0xB548: +case 0xB748: +case 0xB948: +case 0xBB48: +case 0xBD48: +case 0xB149: +case 0xB349: +case 0xB549: +case 0xB749: +case 0xB949: +case 0xBB49: +case 0xBD49: +case 0xB14A: +case 0xB34A: +case 0xB54A: +case 0xB74A: +case 0xB94A: +case 0xBB4A: +case 0xBD4A: +case 0xB14B: +case 0xB34B: +case 0xB54B: +case 0xB74B: +case 0xB94B: +case 0xBB4B: +case 0xBD4B: +case 0xB14C: +case 0xB34C: +case 0xB54C: +case 0xB74C: +case 0xB94C: +case 0xBB4C: +case 0xBD4C: +case 0xB14D: +case 0xB34D: +case 0xB54D: +case 0xB74D: +case 0xB94D: +case 0xBB4D: +case 0xBD4D: +case 0xB14E: +case 0xB34E: +case 0xB54E: +case 0xB74E: +case 0xB94E: +case 0xBB4E: +case 0xBD4E: + +// CMPM +case 0xB148: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xB388: +case 0xB588: +case 0xB788: +case 0xB988: +case 0xBB88: +case 0xBD88: +case 0xB189: +case 0xB389: +case 0xB589: +case 0xB789: +case 0xB989: +case 0xBB89: +case 0xBD89: +case 0xB18A: +case 0xB38A: +case 0xB58A: +case 0xB78A: +case 0xB98A: +case 0xBB8A: +case 0xBD8A: +case 0xB18B: +case 0xB38B: +case 0xB58B: +case 0xB78B: +case 0xB98B: +case 0xBB8B: +case 0xBD8B: +case 0xB18C: +case 0xB38C: +case 0xB58C: +case 0xB78C: +case 0xB98C: +case 0xBB8C: +case 0xBD8C: +case 0xB18D: +case 0xB38D: +case 0xB58D: +case 0xB78D: +case 0xB98D: +case 0xBB8D: +case 0xBD8D: +case 0xB18E: +case 0xB38E: +case 0xB58E: +case 0xB78E: +case 0xB98E: +case 0xBB8E: +case 0xBD8E: + +// CMPM +case 0xB188: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB30F: +case 0xB50F: +case 0xB70F: +case 0xB90F: +case 0xBB0F: +case 0xBD0F: + +// CMP7M +case 0xB10F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 1; + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xB34F: +case 0xB54F: +case 0xB74F: +case 0xB94F: +case 0xBB4F: +case 0xBD4F: + +// CMP7M +case 0xB14F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 2; + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xB38F: +case 0xB58F: +case 0xB78F: +case 0xB98F: +case 0xBB8F: +case 0xBD8F: + +// CMP7M +case 0xB18F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] += 4; + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xBF09: +case 0xBF0A: +case 0xBF0B: +case 0xBF0C: +case 0xBF0D: +case 0xBF0E: + +// CMPM7 +case 0xBF08: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 2; + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) +case 0xBF49: +case 0xBF4A: +case 0xBF4B: +case 0xBF4C: +case 0xBF4D: +case 0xBF4E: + +// CMPM7 +case 0xBF48: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 2; + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) +case 0xBF89: +case 0xBF8A: +case 0xBF8B: +case 0xBF8C: +case 0xBF8D: +case 0xBF8E: + +// CMPM7 +case 0xBF88: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 4; + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) + +// CMP7M7 +case 0xBF0F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 2; + READ_BYTE_F(adr, dst) + res = dst - src; + CPU->flag_N = CPU->flag_C = res; + CPU->flag_V = (src ^ dst) & (res ^ dst); + CPU->flag_notZ = res & 0xFF; + POST_IO +} +RET(12) + +// CMP7M7 +case 0xBF4F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 2; + READ_WORD_F(adr, dst) + res = dst - src; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8; + CPU->flag_N = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + POST_IO +} +RET(12) + +// CMP7M7 +case 0xBF8F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7]; + CPU->A[7] += 4; + READ_LONG_F(adr, dst) + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB300: +case 0xB500: +case 0xB700: +case 0xB900: +case 0xBB00: +case 0xBD00: +case 0xBF00: +case 0xB101: +case 0xB301: +case 0xB501: +case 0xB701: +case 0xB901: +case 0xBB01: +case 0xBD01: +case 0xBF01: +case 0xB102: +case 0xB302: +case 0xB502: +case 0xB702: +case 0xB902: +case 0xBB02: +case 0xBD02: +case 0xBF02: +case 0xB103: +case 0xB303: +case 0xB503: +case 0xB703: +case 0xB903: +case 0xBB03: +case 0xBD03: +case 0xBF03: +case 0xB104: +case 0xB304: +case 0xB504: +case 0xB704: +case 0xB904: +case 0xBB04: +case 0xBD04: +case 0xBF04: +case 0xB105: +case 0xB305: +case 0xB505: +case 0xB705: +case 0xB905: +case 0xBB05: +case 0xBD05: +case 0xBF05: +case 0xB106: +case 0xB306: +case 0xB506: +case 0xB706: +case 0xB906: +case 0xBB06: +case 0xBD06: +case 0xBF06: +case 0xB107: +case 0xB307: +case 0xB507: +case 0xB707: +case 0xB907: +case 0xBB07: +case 0xBD07: +case 0xBF07: + +// EORDa +case 0xB100: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + res = (u8)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0xB310: +case 0xB510: +case 0xB710: +case 0xB910: +case 0xBB10: +case 0xBD10: +case 0xBF10: +case 0xB111: +case 0xB311: +case 0xB511: +case 0xB711: +case 0xB911: +case 0xBB11: +case 0xBD11: +case 0xBF11: +case 0xB112: +case 0xB312: +case 0xB512: +case 0xB712: +case 0xB912: +case 0xBB12: +case 0xBD12: +case 0xBF12: +case 0xB113: +case 0xB313: +case 0xB513: +case 0xB713: +case 0xB913: +case 0xBB13: +case 0xBD13: +case 0xBF13: +case 0xB114: +case 0xB314: +case 0xB514: +case 0xB714: +case 0xB914: +case 0xBB14: +case 0xBD14: +case 0xBF14: +case 0xB115: +case 0xB315: +case 0xB515: +case 0xB715: +case 0xB915: +case 0xBB15: +case 0xBD15: +case 0xBF15: +case 0xB116: +case 0xB316: +case 0xB516: +case 0xB716: +case 0xB916: +case 0xBB16: +case 0xBD16: +case 0xBF16: +case 0xB117: +case 0xB317: +case 0xB517: +case 0xB717: +case 0xB917: +case 0xBB17: +case 0xBD17: +case 0xBF17: + +// EORDa +case 0xB110: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xB318: +case 0xB518: +case 0xB718: +case 0xB918: +case 0xBB18: +case 0xBD18: +case 0xBF18: +case 0xB119: +case 0xB319: +case 0xB519: +case 0xB719: +case 0xB919: +case 0xBB19: +case 0xBD19: +case 0xBF19: +case 0xB11A: +case 0xB31A: +case 0xB51A: +case 0xB71A: +case 0xB91A: +case 0xBB1A: +case 0xBD1A: +case 0xBF1A: +case 0xB11B: +case 0xB31B: +case 0xB51B: +case 0xB71B: +case 0xB91B: +case 0xBB1B: +case 0xBD1B: +case 0xBF1B: +case 0xB11C: +case 0xB31C: +case 0xB51C: +case 0xB71C: +case 0xB91C: +case 0xBB1C: +case 0xBD1C: +case 0xBF1C: +case 0xB11D: +case 0xB31D: +case 0xB51D: +case 0xB71D: +case 0xB91D: +case 0xBB1D: +case 0xBD1D: +case 0xBF1D: +case 0xB11E: +case 0xB31E: +case 0xB51E: +case 0xB71E: +case 0xB91E: +case 0xBB1E: +case 0xBD1E: +case 0xBF1E: + +// EORDa +case 0xB118: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xB320: +case 0xB520: +case 0xB720: +case 0xB920: +case 0xBB20: +case 0xBD20: +case 0xBF20: +case 0xB121: +case 0xB321: +case 0xB521: +case 0xB721: +case 0xB921: +case 0xBB21: +case 0xBD21: +case 0xBF21: +case 0xB122: +case 0xB322: +case 0xB522: +case 0xB722: +case 0xB922: +case 0xBB22: +case 0xBD22: +case 0xBF22: +case 0xB123: +case 0xB323: +case 0xB523: +case 0xB723: +case 0xB923: +case 0xBB23: +case 0xBD23: +case 0xBF23: +case 0xB124: +case 0xB324: +case 0xB524: +case 0xB724: +case 0xB924: +case 0xBB24: +case 0xBD24: +case 0xBF24: +case 0xB125: +case 0xB325: +case 0xB525: +case 0xB725: +case 0xB925: +case 0xBB25: +case 0xBD25: +case 0xBF25: +case 0xB126: +case 0xB326: +case 0xB526: +case 0xB726: +case 0xB926: +case 0xBB26: +case 0xBD26: +case 0xBF26: + +// EORDa +case 0xB120: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xB328: +case 0xB528: +case 0xB728: +case 0xB928: +case 0xBB28: +case 0xBD28: +case 0xBF28: +case 0xB129: +case 0xB329: +case 0xB529: +case 0xB729: +case 0xB929: +case 0xBB29: +case 0xBD29: +case 0xBF29: +case 0xB12A: +case 0xB32A: +case 0xB52A: +case 0xB72A: +case 0xB92A: +case 0xBB2A: +case 0xBD2A: +case 0xBF2A: +case 0xB12B: +case 0xB32B: +case 0xB52B: +case 0xB72B: +case 0xB92B: +case 0xBB2B: +case 0xBD2B: +case 0xBF2B: +case 0xB12C: +case 0xB32C: +case 0xB52C: +case 0xB72C: +case 0xB92C: +case 0xBB2C: +case 0xBD2C: +case 0xBF2C: +case 0xB12D: +case 0xB32D: +case 0xB52D: +case 0xB72D: +case 0xB92D: +case 0xBB2D: +case 0xBD2D: +case 0xBF2D: +case 0xB12E: +case 0xB32E: +case 0xB52E: +case 0xB72E: +case 0xB92E: +case 0xBB2E: +case 0xBD2E: +case 0xBF2E: +case 0xB12F: +case 0xB32F: +case 0xB52F: +case 0xB72F: +case 0xB92F: +case 0xBB2F: +case 0xBD2F: +case 0xBF2F: + +// EORDa +case 0xB128: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xB330: +case 0xB530: +case 0xB730: +case 0xB930: +case 0xBB30: +case 0xBD30: +case 0xBF30: +case 0xB131: +case 0xB331: +case 0xB531: +case 0xB731: +case 0xB931: +case 0xBB31: +case 0xBD31: +case 0xBF31: +case 0xB132: +case 0xB332: +case 0xB532: +case 0xB732: +case 0xB932: +case 0xBB32: +case 0xBD32: +case 0xBF32: +case 0xB133: +case 0xB333: +case 0xB533: +case 0xB733: +case 0xB933: +case 0xBB33: +case 0xBD33: +case 0xBF33: +case 0xB134: +case 0xB334: +case 0xB534: +case 0xB734: +case 0xB934: +case 0xBB34: +case 0xBD34: +case 0xBF34: +case 0xB135: +case 0xB335: +case 0xB535: +case 0xB735: +case 0xB935: +case 0xBB35: +case 0xBD35: +case 0xBF35: +case 0xB136: +case 0xB336: +case 0xB536: +case 0xB736: +case 0xB936: +case 0xBB36: +case 0xBD36: +case 0xBF36: +case 0xB137: +case 0xB337: +case 0xB537: +case 0xB737: +case 0xB937: +case 0xBB37: +case 0xBD37: +case 0xBF37: + +// EORDa +case 0xB130: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xB338: +case 0xB538: +case 0xB738: +case 0xB938: +case 0xBB38: +case 0xBD38: +case 0xBF38: + +// EORDa +case 0xB138: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xB339: +case 0xB539: +case 0xB739: +case 0xB939: +case 0xBB39: +case 0xBD39: +case 0xBF39: + +// EORDa +case 0xB139: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0xB31F: +case 0xB51F: +case 0xB71F: +case 0xB91F: +case 0xBB1F: +case 0xBD1F: +case 0xBF1F: + +// EORDa +case 0xB11F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xB327: +case 0xB527: +case 0xB727: +case 0xB927: +case 0xBB27: +case 0xBD27: +case 0xBF27: + +// EORDa +case 0xB127: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xB340: +case 0xB540: +case 0xB740: +case 0xB940: +case 0xBB40: +case 0xBD40: +case 0xBF40: +case 0xB141: +case 0xB341: +case 0xB541: +case 0xB741: +case 0xB941: +case 0xBB41: +case 0xBD41: +case 0xBF41: +case 0xB142: +case 0xB342: +case 0xB542: +case 0xB742: +case 0xB942: +case 0xBB42: +case 0xBD42: +case 0xBF42: +case 0xB143: +case 0xB343: +case 0xB543: +case 0xB743: +case 0xB943: +case 0xBB43: +case 0xBD43: +case 0xBF43: +case 0xB144: +case 0xB344: +case 0xB544: +case 0xB744: +case 0xB944: +case 0xBB44: +case 0xBD44: +case 0xBF44: +case 0xB145: +case 0xB345: +case 0xB545: +case 0xB745: +case 0xB945: +case 0xBB45: +case 0xBD45: +case 0xBF45: +case 0xB146: +case 0xB346: +case 0xB546: +case 0xB746: +case 0xB946: +case 0xBB46: +case 0xBD46: +case 0xBF46: +case 0xB147: +case 0xB347: +case 0xB547: +case 0xB747: +case 0xB947: +case 0xBB47: +case 0xBD47: +case 0xBF47: + +// EORDa +case 0xB140: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + res = (u16)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(4) +case 0xB350: +case 0xB550: +case 0xB750: +case 0xB950: +case 0xBB50: +case 0xBD50: +case 0xBF50: +case 0xB151: +case 0xB351: +case 0xB551: +case 0xB751: +case 0xB951: +case 0xBB51: +case 0xBD51: +case 0xBF51: +case 0xB152: +case 0xB352: +case 0xB552: +case 0xB752: +case 0xB952: +case 0xBB52: +case 0xBD52: +case 0xBF52: +case 0xB153: +case 0xB353: +case 0xB553: +case 0xB753: +case 0xB953: +case 0xBB53: +case 0xBD53: +case 0xBF53: +case 0xB154: +case 0xB354: +case 0xB554: +case 0xB754: +case 0xB954: +case 0xBB54: +case 0xBD54: +case 0xBF54: +case 0xB155: +case 0xB355: +case 0xB555: +case 0xB755: +case 0xB955: +case 0xBB55: +case 0xBD55: +case 0xBF55: +case 0xB156: +case 0xB356: +case 0xB556: +case 0xB756: +case 0xB956: +case 0xBB56: +case 0xBD56: +case 0xBF56: +case 0xB157: +case 0xB357: +case 0xB557: +case 0xB757: +case 0xB957: +case 0xBB57: +case 0xBD57: +case 0xBF57: + +// EORDa +case 0xB150: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xB358: +case 0xB558: +case 0xB758: +case 0xB958: +case 0xBB58: +case 0xBD58: +case 0xBF58: +case 0xB159: +case 0xB359: +case 0xB559: +case 0xB759: +case 0xB959: +case 0xBB59: +case 0xBD59: +case 0xBF59: +case 0xB15A: +case 0xB35A: +case 0xB55A: +case 0xB75A: +case 0xB95A: +case 0xBB5A: +case 0xBD5A: +case 0xBF5A: +case 0xB15B: +case 0xB35B: +case 0xB55B: +case 0xB75B: +case 0xB95B: +case 0xBB5B: +case 0xBD5B: +case 0xBF5B: +case 0xB15C: +case 0xB35C: +case 0xB55C: +case 0xB75C: +case 0xB95C: +case 0xBB5C: +case 0xBD5C: +case 0xBF5C: +case 0xB15D: +case 0xB35D: +case 0xB55D: +case 0xB75D: +case 0xB95D: +case 0xBB5D: +case 0xBD5D: +case 0xBF5D: +case 0xB15E: +case 0xB35E: +case 0xB55E: +case 0xB75E: +case 0xB95E: +case 0xBB5E: +case 0xBD5E: +case 0xBF5E: + +// EORDa +case 0xB158: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xB360: +case 0xB560: +case 0xB760: +case 0xB960: +case 0xBB60: +case 0xBD60: +case 0xBF60: +case 0xB161: +case 0xB361: +case 0xB561: +case 0xB761: +case 0xB961: +case 0xBB61: +case 0xBD61: +case 0xBF61: +case 0xB162: +case 0xB362: +case 0xB562: +case 0xB762: +case 0xB962: +case 0xBB62: +case 0xBD62: +case 0xBF62: +case 0xB163: +case 0xB363: +case 0xB563: +case 0xB763: +case 0xB963: +case 0xBB63: +case 0xBD63: +case 0xBF63: +case 0xB164: +case 0xB364: +case 0xB564: +case 0xB764: +case 0xB964: +case 0xBB64: +case 0xBD64: +case 0xBF64: +case 0xB165: +case 0xB365: +case 0xB565: +case 0xB765: +case 0xB965: +case 0xBB65: +case 0xBD65: +case 0xBF65: +case 0xB166: +case 0xB366: +case 0xB566: +case 0xB766: +case 0xB966: +case 0xBB66: +case 0xBD66: +case 0xBF66: + +// EORDa +case 0xB160: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xB368: +case 0xB568: +case 0xB768: +case 0xB968: +case 0xBB68: +case 0xBD68: +case 0xBF68: +case 0xB169: +case 0xB369: +case 0xB569: +case 0xB769: +case 0xB969: +case 0xBB69: +case 0xBD69: +case 0xBF69: +case 0xB16A: +case 0xB36A: +case 0xB56A: +case 0xB76A: +case 0xB96A: +case 0xBB6A: +case 0xBD6A: +case 0xBF6A: +case 0xB16B: +case 0xB36B: +case 0xB56B: +case 0xB76B: +case 0xB96B: +case 0xBB6B: +case 0xBD6B: +case 0xBF6B: +case 0xB16C: +case 0xB36C: +case 0xB56C: +case 0xB76C: +case 0xB96C: +case 0xBB6C: +case 0xBD6C: +case 0xBF6C: +case 0xB16D: +case 0xB36D: +case 0xB56D: +case 0xB76D: +case 0xB96D: +case 0xBB6D: +case 0xBD6D: +case 0xBF6D: +case 0xB16E: +case 0xB36E: +case 0xB56E: +case 0xB76E: +case 0xB96E: +case 0xBB6E: +case 0xBD6E: +case 0xBF6E: +case 0xB16F: +case 0xB36F: +case 0xB56F: +case 0xB76F: +case 0xB96F: +case 0xBB6F: +case 0xBD6F: +case 0xBF6F: + +// EORDa +case 0xB168: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xB370: +case 0xB570: +case 0xB770: +case 0xB970: +case 0xBB70: +case 0xBD70: +case 0xBF70: +case 0xB171: +case 0xB371: +case 0xB571: +case 0xB771: +case 0xB971: +case 0xBB71: +case 0xBD71: +case 0xBF71: +case 0xB172: +case 0xB372: +case 0xB572: +case 0xB772: +case 0xB972: +case 0xBB72: +case 0xBD72: +case 0xBF72: +case 0xB173: +case 0xB373: +case 0xB573: +case 0xB773: +case 0xB973: +case 0xBB73: +case 0xBD73: +case 0xBF73: +case 0xB174: +case 0xB374: +case 0xB574: +case 0xB774: +case 0xB974: +case 0xBB74: +case 0xBD74: +case 0xBF74: +case 0xB175: +case 0xB375: +case 0xB575: +case 0xB775: +case 0xB975: +case 0xBB75: +case 0xBD75: +case 0xBF75: +case 0xB176: +case 0xB376: +case 0xB576: +case 0xB776: +case 0xB976: +case 0xBB76: +case 0xBD76: +case 0xBF76: +case 0xB177: +case 0xB377: +case 0xB577: +case 0xB777: +case 0xB977: +case 0xBB77: +case 0xBD77: +case 0xBF77: + +// EORDa +case 0xB170: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xB378: +case 0xB578: +case 0xB778: +case 0xB978: +case 0xBB78: +case 0xBD78: +case 0xBF78: + +// EORDa +case 0xB178: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xB379: +case 0xB579: +case 0xB779: +case 0xB979: +case 0xBB79: +case 0xBD79: +case 0xBF79: + +// EORDa +case 0xB179: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0xB35F: +case 0xB55F: +case 0xB75F: +case 0xB95F: +case 0xBB5F: +case 0xBD5F: +case 0xBF5F: + +// EORDa +case 0xB15F: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xB367: +case 0xB567: +case 0xB767: +case 0xB967: +case 0xBB67: +case 0xBD67: +case 0xBF67: + +// EORDa +case 0xB167: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xB380: +case 0xB580: +case 0xB780: +case 0xB980: +case 0xBB80: +case 0xBD80: +case 0xBF80: +case 0xB181: +case 0xB381: +case 0xB581: +case 0xB781: +case 0xB981: +case 0xBB81: +case 0xBD81: +case 0xBF81: +case 0xB182: +case 0xB382: +case 0xB582: +case 0xB782: +case 0xB982: +case 0xBB82: +case 0xBD82: +case 0xBF82: +case 0xB183: +case 0xB383: +case 0xB583: +case 0xB783: +case 0xB983: +case 0xBB83: +case 0xBD83: +case 0xBF83: +case 0xB184: +case 0xB384: +case 0xB584: +case 0xB784: +case 0xB984: +case 0xBB84: +case 0xBD84: +case 0xBF84: +case 0xB185: +case 0xB385: +case 0xB585: +case 0xB785: +case 0xB985: +case 0xBB85: +case 0xBD85: +case 0xBF85: +case 0xB186: +case 0xB386: +case 0xB586: +case 0xB786: +case 0xB986: +case 0xBB86: +case 0xBD86: +case 0xBF86: +case 0xB187: +case 0xB387: +case 0xB587: +case 0xB787: +case 0xB987: +case 0xBB87: +case 0xBD87: +case 0xBF87: + +// EORDa +case 0xB180: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xB390: +case 0xB590: +case 0xB790: +case 0xB990: +case 0xBB90: +case 0xBD90: +case 0xBF90: +case 0xB191: +case 0xB391: +case 0xB591: +case 0xB791: +case 0xB991: +case 0xBB91: +case 0xBD91: +case 0xBF91: +case 0xB192: +case 0xB392: +case 0xB592: +case 0xB792: +case 0xB992: +case 0xBB92: +case 0xBD92: +case 0xBF92: +case 0xB193: +case 0xB393: +case 0xB593: +case 0xB793: +case 0xB993: +case 0xBB93: +case 0xBD93: +case 0xBF93: +case 0xB194: +case 0xB394: +case 0xB594: +case 0xB794: +case 0xB994: +case 0xBB94: +case 0xBD94: +case 0xBF94: +case 0xB195: +case 0xB395: +case 0xB595: +case 0xB795: +case 0xB995: +case 0xBB95: +case 0xBD95: +case 0xBF95: +case 0xB196: +case 0xB396: +case 0xB596: +case 0xB796: +case 0xB996: +case 0xBB96: +case 0xBD96: +case 0xBF96: +case 0xB197: +case 0xB397: +case 0xB597: +case 0xB797: +case 0xB997: +case 0xBB97: +case 0xBD97: +case 0xBF97: + +// EORDa +case 0xB190: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xB398: +case 0xB598: +case 0xB798: +case 0xB998: +case 0xBB98: +case 0xBD98: +case 0xBF98: +case 0xB199: +case 0xB399: +case 0xB599: +case 0xB799: +case 0xB999: +case 0xBB99: +case 0xBD99: +case 0xBF99: +case 0xB19A: +case 0xB39A: +case 0xB59A: +case 0xB79A: +case 0xB99A: +case 0xBB9A: +case 0xBD9A: +case 0xBF9A: +case 0xB19B: +case 0xB39B: +case 0xB59B: +case 0xB79B: +case 0xB99B: +case 0xBB9B: +case 0xBD9B: +case 0xBF9B: +case 0xB19C: +case 0xB39C: +case 0xB59C: +case 0xB79C: +case 0xB99C: +case 0xBB9C: +case 0xBD9C: +case 0xBF9C: +case 0xB19D: +case 0xB39D: +case 0xB59D: +case 0xB79D: +case 0xB99D: +case 0xBB9D: +case 0xBD9D: +case 0xBF9D: +case 0xB19E: +case 0xB39E: +case 0xB59E: +case 0xB79E: +case 0xB99E: +case 0xBB9E: +case 0xBD9E: +case 0xBF9E: + +// EORDa +case 0xB198: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xB3A0: +case 0xB5A0: +case 0xB7A0: +case 0xB9A0: +case 0xBBA0: +case 0xBDA0: +case 0xBFA0: +case 0xB1A1: +case 0xB3A1: +case 0xB5A1: +case 0xB7A1: +case 0xB9A1: +case 0xBBA1: +case 0xBDA1: +case 0xBFA1: +case 0xB1A2: +case 0xB3A2: +case 0xB5A2: +case 0xB7A2: +case 0xB9A2: +case 0xBBA2: +case 0xBDA2: +case 0xBFA2: +case 0xB1A3: +case 0xB3A3: +case 0xB5A3: +case 0xB7A3: +case 0xB9A3: +case 0xBBA3: +case 0xBDA3: +case 0xBFA3: +case 0xB1A4: +case 0xB3A4: +case 0xB5A4: +case 0xB7A4: +case 0xB9A4: +case 0xBBA4: +case 0xBDA4: +case 0xBFA4: +case 0xB1A5: +case 0xB3A5: +case 0xB5A5: +case 0xB7A5: +case 0xB9A5: +case 0xBBA5: +case 0xBDA5: +case 0xBFA5: +case 0xB1A6: +case 0xB3A6: +case 0xB5A6: +case 0xB7A6: +case 0xB9A6: +case 0xBBA6: +case 0xBDA6: +case 0xBFA6: + +// EORDa +case 0xB1A0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xB3A8: +case 0xB5A8: +case 0xB7A8: +case 0xB9A8: +case 0xBBA8: +case 0xBDA8: +case 0xBFA8: +case 0xB1A9: +case 0xB3A9: +case 0xB5A9: +case 0xB7A9: +case 0xB9A9: +case 0xBBA9: +case 0xBDA9: +case 0xBFA9: +case 0xB1AA: +case 0xB3AA: +case 0xB5AA: +case 0xB7AA: +case 0xB9AA: +case 0xBBAA: +case 0xBDAA: +case 0xBFAA: +case 0xB1AB: +case 0xB3AB: +case 0xB5AB: +case 0xB7AB: +case 0xB9AB: +case 0xBBAB: +case 0xBDAB: +case 0xBFAB: +case 0xB1AC: +case 0xB3AC: +case 0xB5AC: +case 0xB7AC: +case 0xB9AC: +case 0xBBAC: +case 0xBDAC: +case 0xBFAC: +case 0xB1AD: +case 0xB3AD: +case 0xB5AD: +case 0xB7AD: +case 0xB9AD: +case 0xBBAD: +case 0xBDAD: +case 0xBFAD: +case 0xB1AE: +case 0xB3AE: +case 0xB5AE: +case 0xB7AE: +case 0xB9AE: +case 0xBBAE: +case 0xBDAE: +case 0xBFAE: +case 0xB1AF: +case 0xB3AF: +case 0xB5AF: +case 0xB7AF: +case 0xB9AF: +case 0xBBAF: +case 0xBDAF: +case 0xBFAF: + +// EORDa +case 0xB1A8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xB3B0: +case 0xB5B0: +case 0xB7B0: +case 0xB9B0: +case 0xBBB0: +case 0xBDB0: +case 0xBFB0: +case 0xB1B1: +case 0xB3B1: +case 0xB5B1: +case 0xB7B1: +case 0xB9B1: +case 0xBBB1: +case 0xBDB1: +case 0xBFB1: +case 0xB1B2: +case 0xB3B2: +case 0xB5B2: +case 0xB7B2: +case 0xB9B2: +case 0xBBB2: +case 0xBDB2: +case 0xBFB2: +case 0xB1B3: +case 0xB3B3: +case 0xB5B3: +case 0xB7B3: +case 0xB9B3: +case 0xBBB3: +case 0xBDB3: +case 0xBFB3: +case 0xB1B4: +case 0xB3B4: +case 0xB5B4: +case 0xB7B4: +case 0xB9B4: +case 0xBBB4: +case 0xBDB4: +case 0xBFB4: +case 0xB1B5: +case 0xB3B5: +case 0xB5B5: +case 0xB7B5: +case 0xB9B5: +case 0xBBB5: +case 0xBDB5: +case 0xBFB5: +case 0xB1B6: +case 0xB3B6: +case 0xB5B6: +case 0xB7B6: +case 0xB9B6: +case 0xBBB6: +case 0xBDB6: +case 0xBFB6: +case 0xB1B7: +case 0xB3B7: +case 0xB5B7: +case 0xB7B7: +case 0xB9B7: +case 0xBBB7: +case 0xBDB7: +case 0xBFB7: + +// EORDa +case 0xB1B0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0xB3B8: +case 0xB5B8: +case 0xB7B8: +case 0xB9B8: +case 0xBBB8: +case 0xBDB8: +case 0xBFB8: + +// EORDa +case 0xB1B8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xB3B9: +case 0xB5B9: +case 0xB7B9: +case 0xB9B9: +case 0xBBB9: +case 0xBDB9: +case 0xBFB9: + +// EORDa +case 0xB1B9: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0xB39F: +case 0xB59F: +case 0xB79F: +case 0xB99F: +case 0xBB9F: +case 0xBD9F: +case 0xBF9F: + +// EORDa +case 0xB19F: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xB3A7: +case 0xB5A7: +case 0xB7A7: +case 0xB9A7: +case 0xBBA7: +case 0xBDA7: +case 0xBFA7: + +// EORDa +case 0xB1A7: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res ^= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xB2C0: +case 0xB4C0: +case 0xB6C0: +case 0xB8C0: +case 0xBAC0: +case 0xBCC0: +case 0xBEC0: +case 0xB0C1: +case 0xB2C1: +case 0xB4C1: +case 0xB6C1: +case 0xB8C1: +case 0xBAC1: +case 0xBCC1: +case 0xBEC1: +case 0xB0C2: +case 0xB2C2: +case 0xB4C2: +case 0xB6C2: +case 0xB8C2: +case 0xBAC2: +case 0xBCC2: +case 0xBEC2: +case 0xB0C3: +case 0xB2C3: +case 0xB4C3: +case 0xB6C3: +case 0xB8C3: +case 0xBAC3: +case 0xBCC3: +case 0xBEC3: +case 0xB0C4: +case 0xB2C4: +case 0xB4C4: +case 0xB6C4: +case 0xB8C4: +case 0xBAC4: +case 0xBCC4: +case 0xBEC4: +case 0xB0C5: +case 0xB2C5: +case 0xB4C5: +case 0xB6C5: +case 0xB8C5: +case 0xBAC5: +case 0xBCC5: +case 0xBEC5: +case 0xB0C6: +case 0xB2C6: +case 0xB4C6: +case 0xB6C6: +case 0xB8C6: +case 0xBAC6: +case 0xBCC6: +case 0xBEC6: +case 0xB0C7: +case 0xB2C7: +case 0xB4C7: +case 0xB6C7: +case 0xB8C7: +case 0xBAC7: +case 0xBCC7: +case 0xBEC7: + +// CMPA +case 0xB0C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB2C8: +case 0xB4C8: +case 0xB6C8: +case 0xB8C8: +case 0xBAC8: +case 0xBCC8: +case 0xBEC8: +case 0xB0C9: +case 0xB2C9: +case 0xB4C9: +case 0xB6C9: +case 0xB8C9: +case 0xBAC9: +case 0xBCC9: +case 0xBEC9: +case 0xB0CA: +case 0xB2CA: +case 0xB4CA: +case 0xB6CA: +case 0xB8CA: +case 0xBACA: +case 0xBCCA: +case 0xBECA: +case 0xB0CB: +case 0xB2CB: +case 0xB4CB: +case 0xB6CB: +case 0xB8CB: +case 0xBACB: +case 0xBCCB: +case 0xBECB: +case 0xB0CC: +case 0xB2CC: +case 0xB4CC: +case 0xB6CC: +case 0xB8CC: +case 0xBACC: +case 0xBCCC: +case 0xBECC: +case 0xB0CD: +case 0xB2CD: +case 0xB4CD: +case 0xB6CD: +case 0xB8CD: +case 0xBACD: +case 0xBCCD: +case 0xBECD: +case 0xB0CE: +case 0xB2CE: +case 0xB4CE: +case 0xB6CE: +case 0xB8CE: +case 0xBACE: +case 0xBCCE: +case 0xBECE: +case 0xB0CF: +case 0xB2CF: +case 0xB4CF: +case 0xB6CF: +case 0xB8CF: +case 0xBACF: +case 0xBCCF: +case 0xBECF: + +// CMPA +case 0xB0C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB2D0: +case 0xB4D0: +case 0xB6D0: +case 0xB8D0: +case 0xBAD0: +case 0xBCD0: +case 0xBED0: +case 0xB0D1: +case 0xB2D1: +case 0xB4D1: +case 0xB6D1: +case 0xB8D1: +case 0xBAD1: +case 0xBCD1: +case 0xBED1: +case 0xB0D2: +case 0xB2D2: +case 0xB4D2: +case 0xB6D2: +case 0xB8D2: +case 0xBAD2: +case 0xBCD2: +case 0xBED2: +case 0xB0D3: +case 0xB2D3: +case 0xB4D3: +case 0xB6D3: +case 0xB8D3: +case 0xBAD3: +case 0xBCD3: +case 0xBED3: +case 0xB0D4: +case 0xB2D4: +case 0xB4D4: +case 0xB6D4: +case 0xB8D4: +case 0xBAD4: +case 0xBCD4: +case 0xBED4: +case 0xB0D5: +case 0xB2D5: +case 0xB4D5: +case 0xB6D5: +case 0xB8D5: +case 0xBAD5: +case 0xBCD5: +case 0xBED5: +case 0xB0D6: +case 0xB2D6: +case 0xB4D6: +case 0xB6D6: +case 0xB8D6: +case 0xBAD6: +case 0xBCD6: +case 0xBED6: +case 0xB0D7: +case 0xB2D7: +case 0xB4D7: +case 0xB6D7: +case 0xB8D7: +case 0xBAD7: +case 0xBCD7: +case 0xBED7: + +// CMPA +case 0xB0D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(10) +case 0xB2D8: +case 0xB4D8: +case 0xB6D8: +case 0xB8D8: +case 0xBAD8: +case 0xBCD8: +case 0xBED8: +case 0xB0D9: +case 0xB2D9: +case 0xB4D9: +case 0xB6D9: +case 0xB8D9: +case 0xBAD9: +case 0xBCD9: +case 0xBED9: +case 0xB0DA: +case 0xB2DA: +case 0xB4DA: +case 0xB6DA: +case 0xB8DA: +case 0xBADA: +case 0xBCDA: +case 0xBEDA: +case 0xB0DB: +case 0xB2DB: +case 0xB4DB: +case 0xB6DB: +case 0xB8DB: +case 0xBADB: +case 0xBCDB: +case 0xBEDB: +case 0xB0DC: +case 0xB2DC: +case 0xB4DC: +case 0xB6DC: +case 0xB8DC: +case 0xBADC: +case 0xBCDC: +case 0xBEDC: +case 0xB0DD: +case 0xB2DD: +case 0xB4DD: +case 0xB6DD: +case 0xB8DD: +case 0xBADD: +case 0xBCDD: +case 0xBEDD: +case 0xB0DE: +case 0xB2DE: +case 0xB4DE: +case 0xB6DE: +case 0xB8DE: +case 0xBADE: +case 0xBCDE: +case 0xBEDE: + +// CMPA +case 0xB0D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(10) +case 0xB2E0: +case 0xB4E0: +case 0xB6E0: +case 0xB8E0: +case 0xBAE0: +case 0xBCE0: +case 0xBEE0: +case 0xB0E1: +case 0xB2E1: +case 0xB4E1: +case 0xB6E1: +case 0xB8E1: +case 0xBAE1: +case 0xBCE1: +case 0xBEE1: +case 0xB0E2: +case 0xB2E2: +case 0xB4E2: +case 0xB6E2: +case 0xB8E2: +case 0xBAE2: +case 0xBCE2: +case 0xBEE2: +case 0xB0E3: +case 0xB2E3: +case 0xB4E3: +case 0xB6E3: +case 0xB8E3: +case 0xBAE3: +case 0xBCE3: +case 0xBEE3: +case 0xB0E4: +case 0xB2E4: +case 0xB4E4: +case 0xB6E4: +case 0xB8E4: +case 0xBAE4: +case 0xBCE4: +case 0xBEE4: +case 0xB0E5: +case 0xB2E5: +case 0xB4E5: +case 0xB6E5: +case 0xB8E5: +case 0xBAE5: +case 0xBCE5: +case 0xBEE5: +case 0xB0E6: +case 0xB2E6: +case 0xB4E6: +case 0xB6E6: +case 0xB8E6: +case 0xBAE6: +case 0xBCE6: +case 0xBEE6: + +// CMPA +case 0xB0E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(12) +case 0xB2E8: +case 0xB4E8: +case 0xB6E8: +case 0xB8E8: +case 0xBAE8: +case 0xBCE8: +case 0xBEE8: +case 0xB0E9: +case 0xB2E9: +case 0xB4E9: +case 0xB6E9: +case 0xB8E9: +case 0xBAE9: +case 0xBCE9: +case 0xBEE9: +case 0xB0EA: +case 0xB2EA: +case 0xB4EA: +case 0xB6EA: +case 0xB8EA: +case 0xBAEA: +case 0xBCEA: +case 0xBEEA: +case 0xB0EB: +case 0xB2EB: +case 0xB4EB: +case 0xB6EB: +case 0xB8EB: +case 0xBAEB: +case 0xBCEB: +case 0xBEEB: +case 0xB0EC: +case 0xB2EC: +case 0xB4EC: +case 0xB6EC: +case 0xB8EC: +case 0xBAEC: +case 0xBCEC: +case 0xBEEC: +case 0xB0ED: +case 0xB2ED: +case 0xB4ED: +case 0xB6ED: +case 0xB8ED: +case 0xBAED: +case 0xBCED: +case 0xBEED: +case 0xB0EE: +case 0xB2EE: +case 0xB4EE: +case 0xB6EE: +case 0xB8EE: +case 0xBAEE: +case 0xBCEE: +case 0xBEEE: +case 0xB0EF: +case 0xB2EF: +case 0xB4EF: +case 0xB6EF: +case 0xB8EF: +case 0xBAEF: +case 0xBCEF: +case 0xBEEF: + +// CMPA +case 0xB0E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB2F0: +case 0xB4F0: +case 0xB6F0: +case 0xB8F0: +case 0xBAF0: +case 0xBCF0: +case 0xBEF0: +case 0xB0F1: +case 0xB2F1: +case 0xB4F1: +case 0xB6F1: +case 0xB8F1: +case 0xBAF1: +case 0xBCF1: +case 0xBEF1: +case 0xB0F2: +case 0xB2F2: +case 0xB4F2: +case 0xB6F2: +case 0xB8F2: +case 0xBAF2: +case 0xBCF2: +case 0xBEF2: +case 0xB0F3: +case 0xB2F3: +case 0xB4F3: +case 0xB6F3: +case 0xB8F3: +case 0xBAF3: +case 0xBCF3: +case 0xBEF3: +case 0xB0F4: +case 0xB2F4: +case 0xB4F4: +case 0xB6F4: +case 0xB8F4: +case 0xBAF4: +case 0xBCF4: +case 0xBEF4: +case 0xB0F5: +case 0xB2F5: +case 0xB4F5: +case 0xB6F5: +case 0xB8F5: +case 0xBAF5: +case 0xBCF5: +case 0xBEF5: +case 0xB0F6: +case 0xB2F6: +case 0xB4F6: +case 0xB6F6: +case 0xB8F6: +case 0xBAF6: +case 0xBCF6: +case 0xBEF6: +case 0xB0F7: +case 0xB2F7: +case 0xB4F7: +case 0xB6F7: +case 0xB8F7: +case 0xBAF7: +case 0xBCF7: +case 0xBEF7: + +// CMPA +case 0xB0F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB2F8: +case 0xB4F8: +case 0xB6F8: +case 0xB8F8: +case 0xBAF8: +case 0xBCF8: +case 0xBEF8: + +// CMPA +case 0xB0F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB2F9: +case 0xB4F9: +case 0xB6F9: +case 0xB8F9: +case 0xBAF9: +case 0xBCF9: +case 0xBEF9: + +// CMPA +case 0xB0F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB2FA: +case 0xB4FA: +case 0xB6FA: +case 0xB8FA: +case 0xBAFA: +case 0xBCFA: +case 0xBEFA: + +// CMPA +case 0xB0FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB2FB: +case 0xB4FB: +case 0xB6FB: +case 0xB8FB: +case 0xBAFB: +case 0xBCFB: +case 0xBEFB: + +// CMPA +case 0xB0FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB2FC: +case 0xB4FC: +case 0xB6FC: +case 0xB8FC: +case 0xBAFC: +case 0xBCFC: +case 0xBEFC: + +// CMPA +case 0xB0FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)FETCH_WORD; + PC += 2; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(10) +case 0xB2DF: +case 0xB4DF: +case 0xB6DF: +case 0xB8DF: +case 0xBADF: +case 0xBCDF: +case 0xBEDF: + +// CMPA +case 0xB0DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(10) +case 0xB2E7: +case 0xB4E7: +case 0xB6E7: +case 0xB8E7: +case 0xBAE7: +case 0xBCE7: +case 0xBEE7: + +// CMPA +case 0xB0E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(12) +case 0xB3C0: +case 0xB5C0: +case 0xB7C0: +case 0xB9C0: +case 0xBBC0: +case 0xBDC0: +case 0xBFC0: +case 0xB1C1: +case 0xB3C1: +case 0xB5C1: +case 0xB7C1: +case 0xB9C1: +case 0xBBC1: +case 0xBDC1: +case 0xBFC1: +case 0xB1C2: +case 0xB3C2: +case 0xB5C2: +case 0xB7C2: +case 0xB9C2: +case 0xBBC2: +case 0xBDC2: +case 0xBFC2: +case 0xB1C3: +case 0xB3C3: +case 0xB5C3: +case 0xB7C3: +case 0xB9C3: +case 0xBBC3: +case 0xBDC3: +case 0xBFC3: +case 0xB1C4: +case 0xB3C4: +case 0xB5C4: +case 0xB7C4: +case 0xB9C4: +case 0xBBC4: +case 0xBDC4: +case 0xBFC4: +case 0xB1C5: +case 0xB3C5: +case 0xB5C5: +case 0xB7C5: +case 0xB9C5: +case 0xBBC5: +case 0xBDC5: +case 0xBFC5: +case 0xB1C6: +case 0xB3C6: +case 0xB5C6: +case 0xB7C6: +case 0xB9C6: +case 0xBBC6: +case 0xBDC6: +case 0xBFC6: +case 0xB1C7: +case 0xB3C7: +case 0xB5C7: +case 0xB7C7: +case 0xB9C7: +case 0xBBC7: +case 0xBDC7: +case 0xBFC7: + +// CMPA +case 0xB1C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB3C8: +case 0xB5C8: +case 0xB7C8: +case 0xB9C8: +case 0xBBC8: +case 0xBDC8: +case 0xBFC8: +case 0xB1C9: +case 0xB3C9: +case 0xB5C9: +case 0xB7C9: +case 0xB9C9: +case 0xBBC9: +case 0xBDC9: +case 0xBFC9: +case 0xB1CA: +case 0xB3CA: +case 0xB5CA: +case 0xB7CA: +case 0xB9CA: +case 0xBBCA: +case 0xBDCA: +case 0xBFCA: +case 0xB1CB: +case 0xB3CB: +case 0xB5CB: +case 0xB7CB: +case 0xB9CB: +case 0xBBCB: +case 0xBDCB: +case 0xBFCB: +case 0xB1CC: +case 0xB3CC: +case 0xB5CC: +case 0xB7CC: +case 0xB9CC: +case 0xBBCC: +case 0xBDCC: +case 0xBFCC: +case 0xB1CD: +case 0xB3CD: +case 0xB5CD: +case 0xB7CD: +case 0xB9CD: +case 0xBBCD: +case 0xBDCD: +case 0xBFCD: +case 0xB1CE: +case 0xB3CE: +case 0xB5CE: +case 0xB7CE: +case 0xB9CE: +case 0xBBCE: +case 0xBDCE: +case 0xBFCE: +case 0xB1CF: +case 0xB3CF: +case 0xB5CF: +case 0xB7CF: +case 0xB9CF: +case 0xBBCF: +case 0xBDCF: +case 0xBFCF: + +// CMPA +case 0xB1C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(6) +case 0xB3D0: +case 0xB5D0: +case 0xB7D0: +case 0xB9D0: +case 0xBBD0: +case 0xBDD0: +case 0xBFD0: +case 0xB1D1: +case 0xB3D1: +case 0xB5D1: +case 0xB7D1: +case 0xB9D1: +case 0xBBD1: +case 0xBDD1: +case 0xBFD1: +case 0xB1D2: +case 0xB3D2: +case 0xB5D2: +case 0xB7D2: +case 0xB9D2: +case 0xBBD2: +case 0xBDD2: +case 0xBFD2: +case 0xB1D3: +case 0xB3D3: +case 0xB5D3: +case 0xB7D3: +case 0xB9D3: +case 0xBBD3: +case 0xBDD3: +case 0xBFD3: +case 0xB1D4: +case 0xB3D4: +case 0xB5D4: +case 0xB7D4: +case 0xB9D4: +case 0xBBD4: +case 0xBDD4: +case 0xBFD4: +case 0xB1D5: +case 0xB3D5: +case 0xB5D5: +case 0xB7D5: +case 0xB9D5: +case 0xBBD5: +case 0xBDD5: +case 0xBFD5: +case 0xB1D6: +case 0xB3D6: +case 0xB5D6: +case 0xB7D6: +case 0xB9D6: +case 0xBBD6: +case 0xBDD6: +case 0xBFD6: +case 0xB1D7: +case 0xB3D7: +case 0xB5D7: +case 0xB7D7: +case 0xB9D7: +case 0xBBD7: +case 0xBDD7: +case 0xBFD7: + +// CMPA +case 0xB1D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB3D8: +case 0xB5D8: +case 0xB7D8: +case 0xB9D8: +case 0xBBD8: +case 0xBDD8: +case 0xBFD8: +case 0xB1D9: +case 0xB3D9: +case 0xB5D9: +case 0xB7D9: +case 0xB9D9: +case 0xBBD9: +case 0xBDD9: +case 0xBFD9: +case 0xB1DA: +case 0xB3DA: +case 0xB5DA: +case 0xB7DA: +case 0xB9DA: +case 0xBBDA: +case 0xBDDA: +case 0xBFDA: +case 0xB1DB: +case 0xB3DB: +case 0xB5DB: +case 0xB7DB: +case 0xB9DB: +case 0xBBDB: +case 0xBDDB: +case 0xBFDB: +case 0xB1DC: +case 0xB3DC: +case 0xB5DC: +case 0xB7DC: +case 0xB9DC: +case 0xBBDC: +case 0xBDDC: +case 0xBFDC: +case 0xB1DD: +case 0xB3DD: +case 0xB5DD: +case 0xB7DD: +case 0xB9DD: +case 0xBBDD: +case 0xBDDD: +case 0xBFDD: +case 0xB1DE: +case 0xB3DE: +case 0xB5DE: +case 0xB7DE: +case 0xB9DE: +case 0xBBDE: +case 0xBDDE: +case 0xBFDE: + +// CMPA +case 0xB1D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB3E0: +case 0xB5E0: +case 0xB7E0: +case 0xB9E0: +case 0xBBE0: +case 0xBDE0: +case 0xBFE0: +case 0xB1E1: +case 0xB3E1: +case 0xB5E1: +case 0xB7E1: +case 0xB9E1: +case 0xBBE1: +case 0xBDE1: +case 0xBFE1: +case 0xB1E2: +case 0xB3E2: +case 0xB5E2: +case 0xB7E2: +case 0xB9E2: +case 0xBBE2: +case 0xBDE2: +case 0xBFE2: +case 0xB1E3: +case 0xB3E3: +case 0xB5E3: +case 0xB7E3: +case 0xB9E3: +case 0xBBE3: +case 0xBDE3: +case 0xBFE3: +case 0xB1E4: +case 0xB3E4: +case 0xB5E4: +case 0xB7E4: +case 0xB9E4: +case 0xBBE4: +case 0xBDE4: +case 0xBFE4: +case 0xB1E5: +case 0xB3E5: +case 0xB5E5: +case 0xB7E5: +case 0xB9E5: +case 0xBBE5: +case 0xBDE5: +case 0xBFE5: +case 0xB1E6: +case 0xB3E6: +case 0xB5E6: +case 0xB7E6: +case 0xB9E6: +case 0xBBE6: +case 0xBDE6: +case 0xBFE6: + +// CMPA +case 0xB1E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) +case 0xB3E8: +case 0xB5E8: +case 0xB7E8: +case 0xB9E8: +case 0xBBE8: +case 0xBDE8: +case 0xBFE8: +case 0xB1E9: +case 0xB3E9: +case 0xB5E9: +case 0xB7E9: +case 0xB9E9: +case 0xBBE9: +case 0xBDE9: +case 0xBFE9: +case 0xB1EA: +case 0xB3EA: +case 0xB5EA: +case 0xB7EA: +case 0xB9EA: +case 0xBBEA: +case 0xBDEA: +case 0xBFEA: +case 0xB1EB: +case 0xB3EB: +case 0xB5EB: +case 0xB7EB: +case 0xB9EB: +case 0xBBEB: +case 0xBDEB: +case 0xBFEB: +case 0xB1EC: +case 0xB3EC: +case 0xB5EC: +case 0xB7EC: +case 0xB9EC: +case 0xBBEC: +case 0xBDEC: +case 0xBFEC: +case 0xB1ED: +case 0xB3ED: +case 0xB5ED: +case 0xB7ED: +case 0xB9ED: +case 0xBBED: +case 0xBDED: +case 0xBFED: +case 0xB1EE: +case 0xB3EE: +case 0xB5EE: +case 0xB7EE: +case 0xB9EE: +case 0xBBEE: +case 0xBDEE: +case 0xBFEE: +case 0xB1EF: +case 0xB3EF: +case 0xB5EF: +case 0xB7EF: +case 0xB9EF: +case 0xBBEF: +case 0xBDEF: +case 0xBFEF: + +// CMPA +case 0xB1E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB3F0: +case 0xB5F0: +case 0xB7F0: +case 0xB9F0: +case 0xBBF0: +case 0xBDF0: +case 0xBFF0: +case 0xB1F1: +case 0xB3F1: +case 0xB5F1: +case 0xB7F1: +case 0xB9F1: +case 0xBBF1: +case 0xBDF1: +case 0xBFF1: +case 0xB1F2: +case 0xB3F2: +case 0xB5F2: +case 0xB7F2: +case 0xB9F2: +case 0xBBF2: +case 0xBDF2: +case 0xBFF2: +case 0xB1F3: +case 0xB3F3: +case 0xB5F3: +case 0xB7F3: +case 0xB9F3: +case 0xBBF3: +case 0xBDF3: +case 0xBFF3: +case 0xB1F4: +case 0xB3F4: +case 0xB5F4: +case 0xB7F4: +case 0xB9F4: +case 0xBBF4: +case 0xBDF4: +case 0xBFF4: +case 0xB1F5: +case 0xB3F5: +case 0xB5F5: +case 0xB7F5: +case 0xB9F5: +case 0xBBF5: +case 0xBDF5: +case 0xBFF5: +case 0xB1F6: +case 0xB3F6: +case 0xB5F6: +case 0xB7F6: +case 0xB9F6: +case 0xBBF6: +case 0xBDF6: +case 0xBFF6: +case 0xB1F7: +case 0xB3F7: +case 0xB5F7: +case 0xB7F7: +case 0xB9F7: +case 0xBBF7: +case 0xBDF7: +case 0xBFF7: + +// CMPA +case 0xB1F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB3F8: +case 0xB5F8: +case 0xB7F8: +case 0xB9F8: +case 0xBBF8: +case 0xBDF8: +case 0xBFF8: + +// CMPA +case 0xB1F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB3F9: +case 0xB5F9: +case 0xB7F9: +case 0xB9F9: +case 0xBBF9: +case 0xBDF9: +case 0xBFF9: + +// CMPA +case 0xB1F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(22) +case 0xB3FA: +case 0xB5FA: +case 0xB7FA: +case 0xB9FA: +case 0xBBFA: +case 0xBDFA: +case 0xBFFA: + +// CMPA +case 0xB1FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(18) +case 0xB3FB: +case 0xB5FB: +case 0xB7FB: +case 0xB9FB: +case 0xBBFB: +case 0xBDFB: +case 0xBFFB: + +// CMPA +case 0xB1FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(20) +case 0xB3FC: +case 0xB5FC: +case 0xB7FC: +case 0xB9FC: +case 0xBBFC: +case 0xBDFC: +case 0xBFFC: + +// CMPA +case 0xB1FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)FETCH_LONG; + PC += 4; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; +} +RET(14) +case 0xB3DF: +case 0xB5DF: +case 0xB7DF: +case 0xB9DF: +case 0xBBDF: +case 0xBDDF: +case 0xBFDF: + +// CMPA +case 0xB1DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(14) +case 0xB3E7: +case 0xB5E7: +case 0xB7E7: +case 0xB9E7: +case 0xBBE7: +case 0xBDE7: +case 0xBFE7: + +// CMPA +case 0xB1E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst - src; + CPU->flag_notZ = res; + CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23; + CPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24; + CPU->flag_N = res >> 24; + POST_IO +} +RET(16) diff --git a/yabause/src/c68k/c68k_opC.inc b/yabause/src/c68k/c68k_opC.inc new file mode 100644 index 0000000000..df59c4338d --- /dev/null +++ b/yabause/src/c68k/c68k_opC.inc @@ -0,0 +1,5383 @@ +case 0xC200: +case 0xC400: +case 0xC600: +case 0xC800: +case 0xCA00: +case 0xCC00: +case 0xCE00: +case 0xC001: +case 0xC201: +case 0xC401: +case 0xC601: +case 0xC801: +case 0xCA01: +case 0xCC01: +case 0xCE01: +case 0xC002: +case 0xC202: +case 0xC402: +case 0xC602: +case 0xC802: +case 0xCA02: +case 0xCC02: +case 0xCE02: +case 0xC003: +case 0xC203: +case 0xC403: +case 0xC603: +case 0xC803: +case 0xCA03: +case 0xCC03: +case 0xCE03: +case 0xC004: +case 0xC204: +case 0xC404: +case 0xC604: +case 0xC804: +case 0xCA04: +case 0xCC04: +case 0xCE04: +case 0xC005: +case 0xC205: +case 0xC405: +case 0xC605: +case 0xC805: +case 0xCA05: +case 0xCC05: +case 0xCE05: +case 0xC006: +case 0xC206: +case 0xC406: +case 0xC606: +case 0xC806: +case 0xCA06: +case 0xCC06: +case 0xCE06: +case 0xC007: +case 0xC207: +case 0xC407: +case 0xC607: +case 0xC807: +case 0xCA07: +case 0xCC07: +case 0xCE07: + +// ANDaD +case 0xC000: +{ + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xC210: +case 0xC410: +case 0xC610: +case 0xC810: +case 0xCA10: +case 0xCC10: +case 0xCE10: +case 0xC011: +case 0xC211: +case 0xC411: +case 0xC611: +case 0xC811: +case 0xCA11: +case 0xCC11: +case 0xCE11: +case 0xC012: +case 0xC212: +case 0xC412: +case 0xC612: +case 0xC812: +case 0xCA12: +case 0xCC12: +case 0xCE12: +case 0xC013: +case 0xC213: +case 0xC413: +case 0xC613: +case 0xC813: +case 0xCA13: +case 0xCC13: +case 0xCE13: +case 0xC014: +case 0xC214: +case 0xC414: +case 0xC614: +case 0xC814: +case 0xCA14: +case 0xCC14: +case 0xCE14: +case 0xC015: +case 0xC215: +case 0xC415: +case 0xC615: +case 0xC815: +case 0xCA15: +case 0xCC15: +case 0xCE15: +case 0xC016: +case 0xC216: +case 0xC416: +case 0xC616: +case 0xC816: +case 0xCA16: +case 0xCC16: +case 0xCE16: +case 0xC017: +case 0xC217: +case 0xC417: +case 0xC617: +case 0xC817: +case 0xCA17: +case 0xCC17: +case 0xCE17: + +// ANDaD +case 0xC010: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC218: +case 0xC418: +case 0xC618: +case 0xC818: +case 0xCA18: +case 0xCC18: +case 0xCE18: +case 0xC019: +case 0xC219: +case 0xC419: +case 0xC619: +case 0xC819: +case 0xCA19: +case 0xCC19: +case 0xCE19: +case 0xC01A: +case 0xC21A: +case 0xC41A: +case 0xC61A: +case 0xC81A: +case 0xCA1A: +case 0xCC1A: +case 0xCE1A: +case 0xC01B: +case 0xC21B: +case 0xC41B: +case 0xC61B: +case 0xC81B: +case 0xCA1B: +case 0xCC1B: +case 0xCE1B: +case 0xC01C: +case 0xC21C: +case 0xC41C: +case 0xC61C: +case 0xC81C: +case 0xCA1C: +case 0xCC1C: +case 0xCE1C: +case 0xC01D: +case 0xC21D: +case 0xC41D: +case 0xC61D: +case 0xC81D: +case 0xCA1D: +case 0xCC1D: +case 0xCE1D: +case 0xC01E: +case 0xC21E: +case 0xC41E: +case 0xC61E: +case 0xC81E: +case 0xCA1E: +case 0xCC1E: +case 0xCE1E: + +// ANDaD +case 0xC018: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC220: +case 0xC420: +case 0xC620: +case 0xC820: +case 0xCA20: +case 0xCC20: +case 0xCE20: +case 0xC021: +case 0xC221: +case 0xC421: +case 0xC621: +case 0xC821: +case 0xCA21: +case 0xCC21: +case 0xCE21: +case 0xC022: +case 0xC222: +case 0xC422: +case 0xC622: +case 0xC822: +case 0xCA22: +case 0xCC22: +case 0xCE22: +case 0xC023: +case 0xC223: +case 0xC423: +case 0xC623: +case 0xC823: +case 0xCA23: +case 0xCC23: +case 0xCE23: +case 0xC024: +case 0xC224: +case 0xC424: +case 0xC624: +case 0xC824: +case 0xCA24: +case 0xCC24: +case 0xCE24: +case 0xC025: +case 0xC225: +case 0xC425: +case 0xC625: +case 0xC825: +case 0xCA25: +case 0xCC25: +case 0xCE25: +case 0xC026: +case 0xC226: +case 0xC426: +case 0xC626: +case 0xC826: +case 0xCA26: +case 0xCC26: +case 0xCE26: + +// ANDaD +case 0xC020: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xC228: +case 0xC428: +case 0xC628: +case 0xC828: +case 0xCA28: +case 0xCC28: +case 0xCE28: +case 0xC029: +case 0xC229: +case 0xC429: +case 0xC629: +case 0xC829: +case 0xCA29: +case 0xCC29: +case 0xCE29: +case 0xC02A: +case 0xC22A: +case 0xC42A: +case 0xC62A: +case 0xC82A: +case 0xCA2A: +case 0xCC2A: +case 0xCE2A: +case 0xC02B: +case 0xC22B: +case 0xC42B: +case 0xC62B: +case 0xC82B: +case 0xCA2B: +case 0xCC2B: +case 0xCE2B: +case 0xC02C: +case 0xC22C: +case 0xC42C: +case 0xC62C: +case 0xC82C: +case 0xCA2C: +case 0xCC2C: +case 0xCE2C: +case 0xC02D: +case 0xC22D: +case 0xC42D: +case 0xC62D: +case 0xC82D: +case 0xCA2D: +case 0xCC2D: +case 0xCE2D: +case 0xC02E: +case 0xC22E: +case 0xC42E: +case 0xC62E: +case 0xC82E: +case 0xCA2E: +case 0xCC2E: +case 0xCE2E: +case 0xC02F: +case 0xC22F: +case 0xC42F: +case 0xC62F: +case 0xC82F: +case 0xCA2F: +case 0xCC2F: +case 0xCE2F: + +// ANDaD +case 0xC028: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC230: +case 0xC430: +case 0xC630: +case 0xC830: +case 0xCA30: +case 0xCC30: +case 0xCE30: +case 0xC031: +case 0xC231: +case 0xC431: +case 0xC631: +case 0xC831: +case 0xCA31: +case 0xCC31: +case 0xCE31: +case 0xC032: +case 0xC232: +case 0xC432: +case 0xC632: +case 0xC832: +case 0xCA32: +case 0xCC32: +case 0xCE32: +case 0xC033: +case 0xC233: +case 0xC433: +case 0xC633: +case 0xC833: +case 0xCA33: +case 0xCC33: +case 0xCE33: +case 0xC034: +case 0xC234: +case 0xC434: +case 0xC634: +case 0xC834: +case 0xCA34: +case 0xCC34: +case 0xCE34: +case 0xC035: +case 0xC235: +case 0xC435: +case 0xC635: +case 0xC835: +case 0xCA35: +case 0xCC35: +case 0xCE35: +case 0xC036: +case 0xC236: +case 0xC436: +case 0xC636: +case 0xC836: +case 0xCA36: +case 0xCC36: +case 0xCE36: +case 0xC037: +case 0xC237: +case 0xC437: +case 0xC637: +case 0xC837: +case 0xCA37: +case 0xCC37: +case 0xCE37: + +// ANDaD +case 0xC030: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xC238: +case 0xC438: +case 0xC638: +case 0xC838: +case 0xCA38: +case 0xCC38: +case 0xCE38: + +// ANDaD +case 0xC038: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC239: +case 0xC439: +case 0xC639: +case 0xC839: +case 0xCA39: +case 0xCC39: +case 0xCE39: + +// ANDaD +case 0xC039: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xC23A: +case 0xC43A: +case 0xC63A: +case 0xC83A: +case 0xCA3A: +case 0xCC3A: +case 0xCE3A: + +// ANDaD +case 0xC03A: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC23B: +case 0xC43B: +case 0xC63B: +case 0xC83B: +case 0xCA3B: +case 0xCC3B: +case 0xCE3B: + +// ANDaD +case 0xC03B: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xC23C: +case 0xC43C: +case 0xC63C: +case 0xC83C: +case 0xCA3C: +case 0xCC3C: +case 0xCE3C: + +// ANDaD +case 0xC03C: +{ + u32 res; + pointer src; + src = FETCH_BYTE; + PC += 2; + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0xC21F: +case 0xC41F: +case 0xC61F: +case 0xC81F: +case 0xCA1F: +case 0xCC1F: +case 0xCE1F: + +// ANDaD +case 0xC01F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC227: +case 0xC427: +case 0xC627: +case 0xC827: +case 0xCA27: +case 0xCC27: +case 0xCE27: + +// ANDaD +case 0xC027: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + res = (u8)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xC240: +case 0xC440: +case 0xC640: +case 0xC840: +case 0xCA40: +case 0xCC40: +case 0xCE40: +case 0xC041: +case 0xC241: +case 0xC441: +case 0xC641: +case 0xC841: +case 0xCA41: +case 0xCC41: +case 0xCE41: +case 0xC042: +case 0xC242: +case 0xC442: +case 0xC642: +case 0xC842: +case 0xCA42: +case 0xCC42: +case 0xCE42: +case 0xC043: +case 0xC243: +case 0xC443: +case 0xC643: +case 0xC843: +case 0xCA43: +case 0xCC43: +case 0xCE43: +case 0xC044: +case 0xC244: +case 0xC444: +case 0xC644: +case 0xC844: +case 0xCA44: +case 0xCC44: +case 0xCE44: +case 0xC045: +case 0xC245: +case 0xC445: +case 0xC645: +case 0xC845: +case 0xCA45: +case 0xCC45: +case 0xCE45: +case 0xC046: +case 0xC246: +case 0xC446: +case 0xC646: +case 0xC846: +case 0xCA46: +case 0xCC46: +case 0xCE46: +case 0xC047: +case 0xC247: +case 0xC447: +case 0xC647: +case 0xC847: +case 0xCA47: +case 0xCC47: +case 0xCE47: + +// ANDaD +case 0xC040: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xC250: +case 0xC450: +case 0xC650: +case 0xC850: +case 0xCA50: +case 0xCC50: +case 0xCE50: +case 0xC051: +case 0xC251: +case 0xC451: +case 0xC651: +case 0xC851: +case 0xCA51: +case 0xCC51: +case 0xCE51: +case 0xC052: +case 0xC252: +case 0xC452: +case 0xC652: +case 0xC852: +case 0xCA52: +case 0xCC52: +case 0xCE52: +case 0xC053: +case 0xC253: +case 0xC453: +case 0xC653: +case 0xC853: +case 0xCA53: +case 0xCC53: +case 0xCE53: +case 0xC054: +case 0xC254: +case 0xC454: +case 0xC654: +case 0xC854: +case 0xCA54: +case 0xCC54: +case 0xCE54: +case 0xC055: +case 0xC255: +case 0xC455: +case 0xC655: +case 0xC855: +case 0xCA55: +case 0xCC55: +case 0xCE55: +case 0xC056: +case 0xC256: +case 0xC456: +case 0xC656: +case 0xC856: +case 0xCA56: +case 0xCC56: +case 0xCE56: +case 0xC057: +case 0xC257: +case 0xC457: +case 0xC657: +case 0xC857: +case 0xCA57: +case 0xCC57: +case 0xCE57: + +// ANDaD +case 0xC050: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC258: +case 0xC458: +case 0xC658: +case 0xC858: +case 0xCA58: +case 0xCC58: +case 0xCE58: +case 0xC059: +case 0xC259: +case 0xC459: +case 0xC659: +case 0xC859: +case 0xCA59: +case 0xCC59: +case 0xCE59: +case 0xC05A: +case 0xC25A: +case 0xC45A: +case 0xC65A: +case 0xC85A: +case 0xCA5A: +case 0xCC5A: +case 0xCE5A: +case 0xC05B: +case 0xC25B: +case 0xC45B: +case 0xC65B: +case 0xC85B: +case 0xCA5B: +case 0xCC5B: +case 0xCE5B: +case 0xC05C: +case 0xC25C: +case 0xC45C: +case 0xC65C: +case 0xC85C: +case 0xCA5C: +case 0xCC5C: +case 0xCE5C: +case 0xC05D: +case 0xC25D: +case 0xC45D: +case 0xC65D: +case 0xC85D: +case 0xCA5D: +case 0xCC5D: +case 0xCE5D: +case 0xC05E: +case 0xC25E: +case 0xC45E: +case 0xC65E: +case 0xC85E: +case 0xCA5E: +case 0xCC5E: +case 0xCE5E: + +// ANDaD +case 0xC058: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC260: +case 0xC460: +case 0xC660: +case 0xC860: +case 0xCA60: +case 0xCC60: +case 0xCE60: +case 0xC061: +case 0xC261: +case 0xC461: +case 0xC661: +case 0xC861: +case 0xCA61: +case 0xCC61: +case 0xCE61: +case 0xC062: +case 0xC262: +case 0xC462: +case 0xC662: +case 0xC862: +case 0xCA62: +case 0xCC62: +case 0xCE62: +case 0xC063: +case 0xC263: +case 0xC463: +case 0xC663: +case 0xC863: +case 0xCA63: +case 0xCC63: +case 0xCE63: +case 0xC064: +case 0xC264: +case 0xC464: +case 0xC664: +case 0xC864: +case 0xCA64: +case 0xCC64: +case 0xCE64: +case 0xC065: +case 0xC265: +case 0xC465: +case 0xC665: +case 0xC865: +case 0xCA65: +case 0xCC65: +case 0xCE65: +case 0xC066: +case 0xC266: +case 0xC466: +case 0xC666: +case 0xC866: +case 0xCA66: +case 0xCC66: +case 0xCE66: + +// ANDaD +case 0xC060: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xC268: +case 0xC468: +case 0xC668: +case 0xC868: +case 0xCA68: +case 0xCC68: +case 0xCE68: +case 0xC069: +case 0xC269: +case 0xC469: +case 0xC669: +case 0xC869: +case 0xCA69: +case 0xCC69: +case 0xCE69: +case 0xC06A: +case 0xC26A: +case 0xC46A: +case 0xC66A: +case 0xC86A: +case 0xCA6A: +case 0xCC6A: +case 0xCE6A: +case 0xC06B: +case 0xC26B: +case 0xC46B: +case 0xC66B: +case 0xC86B: +case 0xCA6B: +case 0xCC6B: +case 0xCE6B: +case 0xC06C: +case 0xC26C: +case 0xC46C: +case 0xC66C: +case 0xC86C: +case 0xCA6C: +case 0xCC6C: +case 0xCE6C: +case 0xC06D: +case 0xC26D: +case 0xC46D: +case 0xC66D: +case 0xC86D: +case 0xCA6D: +case 0xCC6D: +case 0xCE6D: +case 0xC06E: +case 0xC26E: +case 0xC46E: +case 0xC66E: +case 0xC86E: +case 0xCA6E: +case 0xCC6E: +case 0xCE6E: +case 0xC06F: +case 0xC26F: +case 0xC46F: +case 0xC66F: +case 0xC86F: +case 0xCA6F: +case 0xCC6F: +case 0xCE6F: + +// ANDaD +case 0xC068: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC270: +case 0xC470: +case 0xC670: +case 0xC870: +case 0xCA70: +case 0xCC70: +case 0xCE70: +case 0xC071: +case 0xC271: +case 0xC471: +case 0xC671: +case 0xC871: +case 0xCA71: +case 0xCC71: +case 0xCE71: +case 0xC072: +case 0xC272: +case 0xC472: +case 0xC672: +case 0xC872: +case 0xCA72: +case 0xCC72: +case 0xCE72: +case 0xC073: +case 0xC273: +case 0xC473: +case 0xC673: +case 0xC873: +case 0xCA73: +case 0xCC73: +case 0xCE73: +case 0xC074: +case 0xC274: +case 0xC474: +case 0xC674: +case 0xC874: +case 0xCA74: +case 0xCC74: +case 0xCE74: +case 0xC075: +case 0xC275: +case 0xC475: +case 0xC675: +case 0xC875: +case 0xCA75: +case 0xCC75: +case 0xCE75: +case 0xC076: +case 0xC276: +case 0xC476: +case 0xC676: +case 0xC876: +case 0xCA76: +case 0xCC76: +case 0xCE76: +case 0xC077: +case 0xC277: +case 0xC477: +case 0xC677: +case 0xC877: +case 0xCA77: +case 0xCC77: +case 0xCE77: + +// ANDaD +case 0xC070: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xC278: +case 0xC478: +case 0xC678: +case 0xC878: +case 0xCA78: +case 0xCC78: +case 0xCE78: + +// ANDaD +case 0xC078: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC279: +case 0xC479: +case 0xC679: +case 0xC879: +case 0xCA79: +case 0xCC79: +case 0xCE79: + +// ANDaD +case 0xC079: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xC27A: +case 0xC47A: +case 0xC67A: +case 0xC87A: +case 0xCA7A: +case 0xCC7A: +case 0xCE7A: + +// ANDaD +case 0xC07A: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xC27B: +case 0xC47B: +case 0xC67B: +case 0xC87B: +case 0xCA7B: +case 0xCC7B: +case 0xCE7B: + +// ANDaD +case 0xC07B: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xC27C: +case 0xC47C: +case 0xC67C: +case 0xC87C: +case 0xCA7C: +case 0xCC7C: +case 0xCE7C: + +// ANDaD +case 0xC07C: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0xC25F: +case 0xC45F: +case 0xC65F: +case 0xC85F: +case 0xCA5F: +case 0xCC5F: +case 0xCE5F: + +// ANDaD +case 0xC05F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xC267: +case 0xC467: +case 0xC667: +case 0xC867: +case 0xCA67: +case 0xCC67: +case 0xCE67: + +// ANDaD +case 0xC067: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xC280: +case 0xC480: +case 0xC680: +case 0xC880: +case 0xCA80: +case 0xCC80: +case 0xCE80: +case 0xC081: +case 0xC281: +case 0xC481: +case 0xC681: +case 0xC881: +case 0xCA81: +case 0xCC81: +case 0xCE81: +case 0xC082: +case 0xC282: +case 0xC482: +case 0xC682: +case 0xC882: +case 0xCA82: +case 0xCC82: +case 0xCE82: +case 0xC083: +case 0xC283: +case 0xC483: +case 0xC683: +case 0xC883: +case 0xCA83: +case 0xCC83: +case 0xCE83: +case 0xC084: +case 0xC284: +case 0xC484: +case 0xC684: +case 0xC884: +case 0xCA84: +case 0xCC84: +case 0xCE84: +case 0xC085: +case 0xC285: +case 0xC485: +case 0xC685: +case 0xC885: +case 0xCA85: +case 0xCC85: +case 0xCE85: +case 0xC086: +case 0xC286: +case 0xC486: +case 0xC686: +case 0xC886: +case 0xCA86: +case 0xCC86: +case 0xCE86: +case 0xC087: +case 0xC287: +case 0xC487: +case 0xC687: +case 0xC887: +case 0xCA87: +case 0xCC87: +case 0xCE87: + +// ANDaD +case 0xC080: +{ + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0xC290: +case 0xC490: +case 0xC690: +case 0xC890: +case 0xCA90: +case 0xCC90: +case 0xCE90: +case 0xC091: +case 0xC291: +case 0xC491: +case 0xC691: +case 0xC891: +case 0xCA91: +case 0xCC91: +case 0xCE91: +case 0xC092: +case 0xC292: +case 0xC492: +case 0xC692: +case 0xC892: +case 0xCA92: +case 0xCC92: +case 0xCE92: +case 0xC093: +case 0xC293: +case 0xC493: +case 0xC693: +case 0xC893: +case 0xCA93: +case 0xCC93: +case 0xCE93: +case 0xC094: +case 0xC294: +case 0xC494: +case 0xC694: +case 0xC894: +case 0xCA94: +case 0xCC94: +case 0xCE94: +case 0xC095: +case 0xC295: +case 0xC495: +case 0xC695: +case 0xC895: +case 0xCA95: +case 0xCC95: +case 0xCE95: +case 0xC096: +case 0xC296: +case 0xC496: +case 0xC696: +case 0xC896: +case 0xCA96: +case 0xCC96: +case 0xCE96: +case 0xC097: +case 0xC297: +case 0xC497: +case 0xC697: +case 0xC897: +case 0xCA97: +case 0xCC97: +case 0xCE97: + +// ANDaD +case 0xC090: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xC298: +case 0xC498: +case 0xC698: +case 0xC898: +case 0xCA98: +case 0xCC98: +case 0xCE98: +case 0xC099: +case 0xC299: +case 0xC499: +case 0xC699: +case 0xC899: +case 0xCA99: +case 0xCC99: +case 0xCE99: +case 0xC09A: +case 0xC29A: +case 0xC49A: +case 0xC69A: +case 0xC89A: +case 0xCA9A: +case 0xCC9A: +case 0xCE9A: +case 0xC09B: +case 0xC29B: +case 0xC49B: +case 0xC69B: +case 0xC89B: +case 0xCA9B: +case 0xCC9B: +case 0xCE9B: +case 0xC09C: +case 0xC29C: +case 0xC49C: +case 0xC69C: +case 0xC89C: +case 0xCA9C: +case 0xCC9C: +case 0xCE9C: +case 0xC09D: +case 0xC29D: +case 0xC49D: +case 0xC69D: +case 0xC89D: +case 0xCA9D: +case 0xCC9D: +case 0xCE9D: +case 0xC09E: +case 0xC29E: +case 0xC49E: +case 0xC69E: +case 0xC89E: +case 0xCA9E: +case 0xCC9E: +case 0xCE9E: + +// ANDaD +case 0xC098: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xC2A0: +case 0xC4A0: +case 0xC6A0: +case 0xC8A0: +case 0xCAA0: +case 0xCCA0: +case 0xCEA0: +case 0xC0A1: +case 0xC2A1: +case 0xC4A1: +case 0xC6A1: +case 0xC8A1: +case 0xCAA1: +case 0xCCA1: +case 0xCEA1: +case 0xC0A2: +case 0xC2A2: +case 0xC4A2: +case 0xC6A2: +case 0xC8A2: +case 0xCAA2: +case 0xCCA2: +case 0xCEA2: +case 0xC0A3: +case 0xC2A3: +case 0xC4A3: +case 0xC6A3: +case 0xC8A3: +case 0xCAA3: +case 0xCCA3: +case 0xCEA3: +case 0xC0A4: +case 0xC2A4: +case 0xC4A4: +case 0xC6A4: +case 0xC8A4: +case 0xCAA4: +case 0xCCA4: +case 0xCEA4: +case 0xC0A5: +case 0xC2A5: +case 0xC4A5: +case 0xC6A5: +case 0xC8A5: +case 0xCAA5: +case 0xCCA5: +case 0xCEA5: +case 0xC0A6: +case 0xC2A6: +case 0xC4A6: +case 0xC6A6: +case 0xC8A6: +case 0xCAA6: +case 0xCCA6: +case 0xCEA6: + +// ANDaD +case 0xC0A0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0xC2A8: +case 0xC4A8: +case 0xC6A8: +case 0xC8A8: +case 0xCAA8: +case 0xCCA8: +case 0xCEA8: +case 0xC0A9: +case 0xC2A9: +case 0xC4A9: +case 0xC6A9: +case 0xC8A9: +case 0xCAA9: +case 0xCCA9: +case 0xCEA9: +case 0xC0AA: +case 0xC2AA: +case 0xC4AA: +case 0xC6AA: +case 0xC8AA: +case 0xCAAA: +case 0xCCAA: +case 0xCEAA: +case 0xC0AB: +case 0xC2AB: +case 0xC4AB: +case 0xC6AB: +case 0xC8AB: +case 0xCAAB: +case 0xCCAB: +case 0xCEAB: +case 0xC0AC: +case 0xC2AC: +case 0xC4AC: +case 0xC6AC: +case 0xC8AC: +case 0xCAAC: +case 0xCCAC: +case 0xCEAC: +case 0xC0AD: +case 0xC2AD: +case 0xC4AD: +case 0xC6AD: +case 0xC8AD: +case 0xCAAD: +case 0xCCAD: +case 0xCEAD: +case 0xC0AE: +case 0xC2AE: +case 0xC4AE: +case 0xC6AE: +case 0xC8AE: +case 0xCAAE: +case 0xCCAE: +case 0xCEAE: +case 0xC0AF: +case 0xC2AF: +case 0xC4AF: +case 0xC6AF: +case 0xC8AF: +case 0xCAAF: +case 0xCCAF: +case 0xCEAF: + +// ANDaD +case 0xC0A8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xC2B0: +case 0xC4B0: +case 0xC6B0: +case 0xC8B0: +case 0xCAB0: +case 0xCCB0: +case 0xCEB0: +case 0xC0B1: +case 0xC2B1: +case 0xC4B1: +case 0xC6B1: +case 0xC8B1: +case 0xCAB1: +case 0xCCB1: +case 0xCEB1: +case 0xC0B2: +case 0xC2B2: +case 0xC4B2: +case 0xC6B2: +case 0xC8B2: +case 0xCAB2: +case 0xCCB2: +case 0xCEB2: +case 0xC0B3: +case 0xC2B3: +case 0xC4B3: +case 0xC6B3: +case 0xC8B3: +case 0xCAB3: +case 0xCCB3: +case 0xCEB3: +case 0xC0B4: +case 0xC2B4: +case 0xC4B4: +case 0xC6B4: +case 0xC8B4: +case 0xCAB4: +case 0xCCB4: +case 0xCEB4: +case 0xC0B5: +case 0xC2B5: +case 0xC4B5: +case 0xC6B5: +case 0xC8B5: +case 0xCAB5: +case 0xCCB5: +case 0xCEB5: +case 0xC0B6: +case 0xC2B6: +case 0xC4B6: +case 0xC6B6: +case 0xC8B6: +case 0xCAB6: +case 0xCCB6: +case 0xCEB6: +case 0xC0B7: +case 0xC2B7: +case 0xC4B7: +case 0xC6B7: +case 0xC8B7: +case 0xCAB7: +case 0xCCB7: +case 0xCEB7: + +// ANDaD +case 0xC0B0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0xC2B8: +case 0xC4B8: +case 0xC6B8: +case 0xC8B8: +case 0xCAB8: +case 0xCCB8: +case 0xCEB8: + +// ANDaD +case 0xC0B8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xC2B9: +case 0xC4B9: +case 0xC6B9: +case 0xC8B9: +case 0xCAB9: +case 0xCCB9: +case 0xCEB9: + +// ANDaD +case 0xC0B9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(24) +case 0xC2BA: +case 0xC4BA: +case 0xC6BA: +case 0xC8BA: +case 0xCABA: +case 0xCCBA: +case 0xCEBA: + +// ANDaD +case 0xC0BA: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xC2BB: +case 0xC4BB: +case 0xC6BB: +case 0xC8BB: +case 0xCABB: +case 0xCCBB: +case 0xCEBB: + +// ANDaD +case 0xC0BB: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0xC2BC: +case 0xC4BC: +case 0xC6BC: +case 0xC8BC: +case 0xCABC: +case 0xCCBC: +case 0xCEBC: + +// ANDaD +case 0xC0BC: +{ + u32 res; + pointer src; + src = FETCH_LONG; + PC += 4; + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(14) +case 0xC29F: +case 0xC49F: +case 0xC69F: +case 0xC89F: +case 0xCA9F: +case 0xCC9F: +case 0xCE9F: + +// ANDaD +case 0xC09F: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xC2A7: +case 0xC4A7: +case 0xC6A7: +case 0xC8A7: +case 0xCAA7: +case 0xCCA7: +case 0xCEA7: + +// ANDaD +case 0xC0A7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + res = (u32)CPU->D[(Opcode >> 9) & 7]; + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0xC310: +case 0xC510: +case 0xC710: +case 0xC910: +case 0xCB10: +case 0xCD10: +case 0xCF10: +case 0xC111: +case 0xC311: +case 0xC511: +case 0xC711: +case 0xC911: +case 0xCB11: +case 0xCD11: +case 0xCF11: +case 0xC112: +case 0xC312: +case 0xC512: +case 0xC712: +case 0xC912: +case 0xCB12: +case 0xCD12: +case 0xCF12: +case 0xC113: +case 0xC313: +case 0xC513: +case 0xC713: +case 0xC913: +case 0xCB13: +case 0xCD13: +case 0xCF13: +case 0xC114: +case 0xC314: +case 0xC514: +case 0xC714: +case 0xC914: +case 0xCB14: +case 0xCD14: +case 0xCF14: +case 0xC115: +case 0xC315: +case 0xC515: +case 0xC715: +case 0xC915: +case 0xCB15: +case 0xCD15: +case 0xCF15: +case 0xC116: +case 0xC316: +case 0xC516: +case 0xC716: +case 0xC916: +case 0xCB16: +case 0xCD16: +case 0xCF16: +case 0xC117: +case 0xC317: +case 0xC517: +case 0xC717: +case 0xC917: +case 0xCB17: +case 0xCD17: +case 0xCF17: + +// ANDDa +case 0xC110: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xC318: +case 0xC518: +case 0xC718: +case 0xC918: +case 0xCB18: +case 0xCD18: +case 0xCF18: +case 0xC119: +case 0xC319: +case 0xC519: +case 0xC719: +case 0xC919: +case 0xCB19: +case 0xCD19: +case 0xCF19: +case 0xC11A: +case 0xC31A: +case 0xC51A: +case 0xC71A: +case 0xC91A: +case 0xCB1A: +case 0xCD1A: +case 0xCF1A: +case 0xC11B: +case 0xC31B: +case 0xC51B: +case 0xC71B: +case 0xC91B: +case 0xCB1B: +case 0xCD1B: +case 0xCF1B: +case 0xC11C: +case 0xC31C: +case 0xC51C: +case 0xC71C: +case 0xC91C: +case 0xCB1C: +case 0xCD1C: +case 0xCF1C: +case 0xC11D: +case 0xC31D: +case 0xC51D: +case 0xC71D: +case 0xC91D: +case 0xCB1D: +case 0xCD1D: +case 0xCF1D: +case 0xC11E: +case 0xC31E: +case 0xC51E: +case 0xC71E: +case 0xC91E: +case 0xCB1E: +case 0xCD1E: +case 0xCF1E: + +// ANDDa +case 0xC118: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xC320: +case 0xC520: +case 0xC720: +case 0xC920: +case 0xCB20: +case 0xCD20: +case 0xCF20: +case 0xC121: +case 0xC321: +case 0xC521: +case 0xC721: +case 0xC921: +case 0xCB21: +case 0xCD21: +case 0xCF21: +case 0xC122: +case 0xC322: +case 0xC522: +case 0xC722: +case 0xC922: +case 0xCB22: +case 0xCD22: +case 0xCF22: +case 0xC123: +case 0xC323: +case 0xC523: +case 0xC723: +case 0xC923: +case 0xCB23: +case 0xCD23: +case 0xCF23: +case 0xC124: +case 0xC324: +case 0xC524: +case 0xC724: +case 0xC924: +case 0xCB24: +case 0xCD24: +case 0xCF24: +case 0xC125: +case 0xC325: +case 0xC525: +case 0xC725: +case 0xC925: +case 0xCB25: +case 0xCD25: +case 0xCF25: +case 0xC126: +case 0xC326: +case 0xC526: +case 0xC726: +case 0xC926: +case 0xCB26: +case 0xCD26: +case 0xCF26: + +// ANDDa +case 0xC120: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xC328: +case 0xC528: +case 0xC728: +case 0xC928: +case 0xCB28: +case 0xCD28: +case 0xCF28: +case 0xC129: +case 0xC329: +case 0xC529: +case 0xC729: +case 0xC929: +case 0xCB29: +case 0xCD29: +case 0xCF29: +case 0xC12A: +case 0xC32A: +case 0xC52A: +case 0xC72A: +case 0xC92A: +case 0xCB2A: +case 0xCD2A: +case 0xCF2A: +case 0xC12B: +case 0xC32B: +case 0xC52B: +case 0xC72B: +case 0xC92B: +case 0xCB2B: +case 0xCD2B: +case 0xCF2B: +case 0xC12C: +case 0xC32C: +case 0xC52C: +case 0xC72C: +case 0xC92C: +case 0xCB2C: +case 0xCD2C: +case 0xCF2C: +case 0xC12D: +case 0xC32D: +case 0xC52D: +case 0xC72D: +case 0xC92D: +case 0xCB2D: +case 0xCD2D: +case 0xCF2D: +case 0xC12E: +case 0xC32E: +case 0xC52E: +case 0xC72E: +case 0xC92E: +case 0xCB2E: +case 0xCD2E: +case 0xCF2E: +case 0xC12F: +case 0xC32F: +case 0xC52F: +case 0xC72F: +case 0xC92F: +case 0xCB2F: +case 0xCD2F: +case 0xCF2F: + +// ANDDa +case 0xC128: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xC330: +case 0xC530: +case 0xC730: +case 0xC930: +case 0xCB30: +case 0xCD30: +case 0xCF30: +case 0xC131: +case 0xC331: +case 0xC531: +case 0xC731: +case 0xC931: +case 0xCB31: +case 0xCD31: +case 0xCF31: +case 0xC132: +case 0xC332: +case 0xC532: +case 0xC732: +case 0xC932: +case 0xCB32: +case 0xCD32: +case 0xCF32: +case 0xC133: +case 0xC333: +case 0xC533: +case 0xC733: +case 0xC933: +case 0xCB33: +case 0xCD33: +case 0xCF33: +case 0xC134: +case 0xC334: +case 0xC534: +case 0xC734: +case 0xC934: +case 0xCB34: +case 0xCD34: +case 0xCF34: +case 0xC135: +case 0xC335: +case 0xC535: +case 0xC735: +case 0xC935: +case 0xCB35: +case 0xCD35: +case 0xCF35: +case 0xC136: +case 0xC336: +case 0xC536: +case 0xC736: +case 0xC936: +case 0xCB36: +case 0xCD36: +case 0xCF36: +case 0xC137: +case 0xC337: +case 0xC537: +case 0xC737: +case 0xC937: +case 0xCB37: +case 0xCD37: +case 0xCF37: + +// ANDDa +case 0xC130: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xC338: +case 0xC538: +case 0xC738: +case 0xC938: +case 0xCB38: +case 0xCD38: +case 0xCF38: + +// ANDDa +case 0xC138: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xC339: +case 0xC539: +case 0xC739: +case 0xC939: +case 0xCB39: +case 0xCD39: +case 0xCF39: + +// ANDDa +case 0xC139: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0xC31F: +case 0xC51F: +case 0xC71F: +case 0xC91F: +case 0xCB1F: +case 0xCD1F: +case 0xCF1F: + +// ANDDa +case 0xC11F: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xC327: +case 0xC527: +case 0xC727: +case 0xC927: +case 0xCB27: +case 0xCD27: +case 0xCF27: + +// ANDDa +case 0xC127: +{ + u32 adr; + u32 res; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xC350: +case 0xC550: +case 0xC750: +case 0xC950: +case 0xCB50: +case 0xCD50: +case 0xCF50: +case 0xC151: +case 0xC351: +case 0xC551: +case 0xC751: +case 0xC951: +case 0xCB51: +case 0xCD51: +case 0xCF51: +case 0xC152: +case 0xC352: +case 0xC552: +case 0xC752: +case 0xC952: +case 0xCB52: +case 0xCD52: +case 0xCF52: +case 0xC153: +case 0xC353: +case 0xC553: +case 0xC753: +case 0xC953: +case 0xCB53: +case 0xCD53: +case 0xCF53: +case 0xC154: +case 0xC354: +case 0xC554: +case 0xC754: +case 0xC954: +case 0xCB54: +case 0xCD54: +case 0xCF54: +case 0xC155: +case 0xC355: +case 0xC555: +case 0xC755: +case 0xC955: +case 0xCB55: +case 0xCD55: +case 0xCF55: +case 0xC156: +case 0xC356: +case 0xC556: +case 0xC756: +case 0xC956: +case 0xCB56: +case 0xCD56: +case 0xCF56: +case 0xC157: +case 0xC357: +case 0xC557: +case 0xC757: +case 0xC957: +case 0xCB57: +case 0xCD57: +case 0xCF57: + +// ANDDa +case 0xC150: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xC358: +case 0xC558: +case 0xC758: +case 0xC958: +case 0xCB58: +case 0xCD58: +case 0xCF58: +case 0xC159: +case 0xC359: +case 0xC559: +case 0xC759: +case 0xC959: +case 0xCB59: +case 0xCD59: +case 0xCF59: +case 0xC15A: +case 0xC35A: +case 0xC55A: +case 0xC75A: +case 0xC95A: +case 0xCB5A: +case 0xCD5A: +case 0xCF5A: +case 0xC15B: +case 0xC35B: +case 0xC55B: +case 0xC75B: +case 0xC95B: +case 0xCB5B: +case 0xCD5B: +case 0xCF5B: +case 0xC15C: +case 0xC35C: +case 0xC55C: +case 0xC75C: +case 0xC95C: +case 0xCB5C: +case 0xCD5C: +case 0xCF5C: +case 0xC15D: +case 0xC35D: +case 0xC55D: +case 0xC75D: +case 0xC95D: +case 0xCB5D: +case 0xCD5D: +case 0xCF5D: +case 0xC15E: +case 0xC35E: +case 0xC55E: +case 0xC75E: +case 0xC95E: +case 0xCB5E: +case 0xCD5E: +case 0xCF5E: + +// ANDDa +case 0xC158: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xC360: +case 0xC560: +case 0xC760: +case 0xC960: +case 0xCB60: +case 0xCD60: +case 0xCF60: +case 0xC161: +case 0xC361: +case 0xC561: +case 0xC761: +case 0xC961: +case 0xCB61: +case 0xCD61: +case 0xCF61: +case 0xC162: +case 0xC362: +case 0xC562: +case 0xC762: +case 0xC962: +case 0xCB62: +case 0xCD62: +case 0xCF62: +case 0xC163: +case 0xC363: +case 0xC563: +case 0xC763: +case 0xC963: +case 0xCB63: +case 0xCD63: +case 0xCF63: +case 0xC164: +case 0xC364: +case 0xC564: +case 0xC764: +case 0xC964: +case 0xCB64: +case 0xCD64: +case 0xCF64: +case 0xC165: +case 0xC365: +case 0xC565: +case 0xC765: +case 0xC965: +case 0xCB65: +case 0xCD65: +case 0xCF65: +case 0xC166: +case 0xC366: +case 0xC566: +case 0xC766: +case 0xC966: +case 0xCB66: +case 0xCD66: +case 0xCF66: + +// ANDDa +case 0xC160: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xC368: +case 0xC568: +case 0xC768: +case 0xC968: +case 0xCB68: +case 0xCD68: +case 0xCF68: +case 0xC169: +case 0xC369: +case 0xC569: +case 0xC769: +case 0xC969: +case 0xCB69: +case 0xCD69: +case 0xCF69: +case 0xC16A: +case 0xC36A: +case 0xC56A: +case 0xC76A: +case 0xC96A: +case 0xCB6A: +case 0xCD6A: +case 0xCF6A: +case 0xC16B: +case 0xC36B: +case 0xC56B: +case 0xC76B: +case 0xC96B: +case 0xCB6B: +case 0xCD6B: +case 0xCF6B: +case 0xC16C: +case 0xC36C: +case 0xC56C: +case 0xC76C: +case 0xC96C: +case 0xCB6C: +case 0xCD6C: +case 0xCF6C: +case 0xC16D: +case 0xC36D: +case 0xC56D: +case 0xC76D: +case 0xC96D: +case 0xCB6D: +case 0xCD6D: +case 0xCF6D: +case 0xC16E: +case 0xC36E: +case 0xC56E: +case 0xC76E: +case 0xC96E: +case 0xCB6E: +case 0xCD6E: +case 0xCF6E: +case 0xC16F: +case 0xC36F: +case 0xC56F: +case 0xC76F: +case 0xC96F: +case 0xCB6F: +case 0xCD6F: +case 0xCF6F: + +// ANDDa +case 0xC168: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xC370: +case 0xC570: +case 0xC770: +case 0xC970: +case 0xCB70: +case 0xCD70: +case 0xCF70: +case 0xC171: +case 0xC371: +case 0xC571: +case 0xC771: +case 0xC971: +case 0xCB71: +case 0xCD71: +case 0xCF71: +case 0xC172: +case 0xC372: +case 0xC572: +case 0xC772: +case 0xC972: +case 0xCB72: +case 0xCD72: +case 0xCF72: +case 0xC173: +case 0xC373: +case 0xC573: +case 0xC773: +case 0xC973: +case 0xCB73: +case 0xCD73: +case 0xCF73: +case 0xC174: +case 0xC374: +case 0xC574: +case 0xC774: +case 0xC974: +case 0xCB74: +case 0xCD74: +case 0xCF74: +case 0xC175: +case 0xC375: +case 0xC575: +case 0xC775: +case 0xC975: +case 0xCB75: +case 0xCD75: +case 0xCF75: +case 0xC176: +case 0xC376: +case 0xC576: +case 0xC776: +case 0xC976: +case 0xCB76: +case 0xCD76: +case 0xCF76: +case 0xC177: +case 0xC377: +case 0xC577: +case 0xC777: +case 0xC977: +case 0xCB77: +case 0xCD77: +case 0xCF77: + +// ANDDa +case 0xC170: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xC378: +case 0xC578: +case 0xC778: +case 0xC978: +case 0xCB78: +case 0xCD78: +case 0xCF78: + +// ANDDa +case 0xC178: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xC379: +case 0xC579: +case 0xC779: +case 0xC979: +case 0xCB79: +case 0xCD79: +case 0xCF79: + +// ANDDa +case 0xC179: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0xC35F: +case 0xC55F: +case 0xC75F: +case 0xC95F: +case 0xCB5F: +case 0xCD5F: +case 0xCF5F: + +// ANDDa +case 0xC15F: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xC367: +case 0xC567: +case 0xC767: +case 0xC967: +case 0xCB67: +case 0xCD67: +case 0xCF67: + +// ANDDa +case 0xC167: +{ + u32 adr; + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 8; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xC390: +case 0xC590: +case 0xC790: +case 0xC990: +case 0xCB90: +case 0xCD90: +case 0xCF90: +case 0xC191: +case 0xC391: +case 0xC591: +case 0xC791: +case 0xC991: +case 0xCB91: +case 0xCD91: +case 0xCF91: +case 0xC192: +case 0xC392: +case 0xC592: +case 0xC792: +case 0xC992: +case 0xCB92: +case 0xCD92: +case 0xCF92: +case 0xC193: +case 0xC393: +case 0xC593: +case 0xC793: +case 0xC993: +case 0xCB93: +case 0xCD93: +case 0xCF93: +case 0xC194: +case 0xC394: +case 0xC594: +case 0xC794: +case 0xC994: +case 0xCB94: +case 0xCD94: +case 0xCF94: +case 0xC195: +case 0xC395: +case 0xC595: +case 0xC795: +case 0xC995: +case 0xCB95: +case 0xCD95: +case 0xCF95: +case 0xC196: +case 0xC396: +case 0xC596: +case 0xC796: +case 0xC996: +case 0xCB96: +case 0xCD96: +case 0xCF96: +case 0xC197: +case 0xC397: +case 0xC597: +case 0xC797: +case 0xC997: +case 0xCB97: +case 0xCD97: +case 0xCF97: + +// ANDDa +case 0xC190: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xC398: +case 0xC598: +case 0xC798: +case 0xC998: +case 0xCB98: +case 0xCD98: +case 0xCF98: +case 0xC199: +case 0xC399: +case 0xC599: +case 0xC799: +case 0xC999: +case 0xCB99: +case 0xCD99: +case 0xCF99: +case 0xC19A: +case 0xC39A: +case 0xC59A: +case 0xC79A: +case 0xC99A: +case 0xCB9A: +case 0xCD9A: +case 0xCF9A: +case 0xC19B: +case 0xC39B: +case 0xC59B: +case 0xC79B: +case 0xC99B: +case 0xCB9B: +case 0xCD9B: +case 0xCF9B: +case 0xC19C: +case 0xC39C: +case 0xC59C: +case 0xC79C: +case 0xC99C: +case 0xCB9C: +case 0xCD9C: +case 0xCF9C: +case 0xC19D: +case 0xC39D: +case 0xC59D: +case 0xC79D: +case 0xC99D: +case 0xCB9D: +case 0xCD9D: +case 0xCF9D: +case 0xC19E: +case 0xC39E: +case 0xC59E: +case 0xC79E: +case 0xC99E: +case 0xCB9E: +case 0xCD9E: +case 0xCF9E: + +// ANDDa +case 0xC198: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xC3A0: +case 0xC5A0: +case 0xC7A0: +case 0xC9A0: +case 0xCBA0: +case 0xCDA0: +case 0xCFA0: +case 0xC1A1: +case 0xC3A1: +case 0xC5A1: +case 0xC7A1: +case 0xC9A1: +case 0xCBA1: +case 0xCDA1: +case 0xCFA1: +case 0xC1A2: +case 0xC3A2: +case 0xC5A2: +case 0xC7A2: +case 0xC9A2: +case 0xCBA2: +case 0xCDA2: +case 0xCFA2: +case 0xC1A3: +case 0xC3A3: +case 0xC5A3: +case 0xC7A3: +case 0xC9A3: +case 0xCBA3: +case 0xCDA3: +case 0xCFA3: +case 0xC1A4: +case 0xC3A4: +case 0xC5A4: +case 0xC7A4: +case 0xC9A4: +case 0xCBA4: +case 0xCDA4: +case 0xCFA4: +case 0xC1A5: +case 0xC3A5: +case 0xC5A5: +case 0xC7A5: +case 0xC9A5: +case 0xCBA5: +case 0xCDA5: +case 0xCFA5: +case 0xC1A6: +case 0xC3A6: +case 0xC5A6: +case 0xC7A6: +case 0xC9A6: +case 0xCBA6: +case 0xCDA6: +case 0xCFA6: + +// ANDDa +case 0xC1A0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xC3A8: +case 0xC5A8: +case 0xC7A8: +case 0xC9A8: +case 0xCBA8: +case 0xCDA8: +case 0xCFA8: +case 0xC1A9: +case 0xC3A9: +case 0xC5A9: +case 0xC7A9: +case 0xC9A9: +case 0xCBA9: +case 0xCDA9: +case 0xCFA9: +case 0xC1AA: +case 0xC3AA: +case 0xC5AA: +case 0xC7AA: +case 0xC9AA: +case 0xCBAA: +case 0xCDAA: +case 0xCFAA: +case 0xC1AB: +case 0xC3AB: +case 0xC5AB: +case 0xC7AB: +case 0xC9AB: +case 0xCBAB: +case 0xCDAB: +case 0xCFAB: +case 0xC1AC: +case 0xC3AC: +case 0xC5AC: +case 0xC7AC: +case 0xC9AC: +case 0xCBAC: +case 0xCDAC: +case 0xCFAC: +case 0xC1AD: +case 0xC3AD: +case 0xC5AD: +case 0xC7AD: +case 0xC9AD: +case 0xCBAD: +case 0xCDAD: +case 0xCFAD: +case 0xC1AE: +case 0xC3AE: +case 0xC5AE: +case 0xC7AE: +case 0xC9AE: +case 0xCBAE: +case 0xCDAE: +case 0xCFAE: +case 0xC1AF: +case 0xC3AF: +case 0xC5AF: +case 0xC7AF: +case 0xC9AF: +case 0xCBAF: +case 0xCDAF: +case 0xCFAF: + +// ANDDa +case 0xC1A8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xC3B0: +case 0xC5B0: +case 0xC7B0: +case 0xC9B0: +case 0xCBB0: +case 0xCDB0: +case 0xCFB0: +case 0xC1B1: +case 0xC3B1: +case 0xC5B1: +case 0xC7B1: +case 0xC9B1: +case 0xCBB1: +case 0xCDB1: +case 0xCFB1: +case 0xC1B2: +case 0xC3B2: +case 0xC5B2: +case 0xC7B2: +case 0xC9B2: +case 0xCBB2: +case 0xCDB2: +case 0xCFB2: +case 0xC1B3: +case 0xC3B3: +case 0xC5B3: +case 0xC7B3: +case 0xC9B3: +case 0xCBB3: +case 0xCDB3: +case 0xCFB3: +case 0xC1B4: +case 0xC3B4: +case 0xC5B4: +case 0xC7B4: +case 0xC9B4: +case 0xCBB4: +case 0xCDB4: +case 0xCFB4: +case 0xC1B5: +case 0xC3B5: +case 0xC5B5: +case 0xC7B5: +case 0xC9B5: +case 0xCBB5: +case 0xCDB5: +case 0xCFB5: +case 0xC1B6: +case 0xC3B6: +case 0xC5B6: +case 0xC7B6: +case 0xC9B6: +case 0xCBB6: +case 0xCDB6: +case 0xCFB6: +case 0xC1B7: +case 0xC3B7: +case 0xC5B7: +case 0xC7B7: +case 0xC9B7: +case 0xCBB7: +case 0xCDB7: +case 0xCFB7: + +// ANDDa +case 0xC1B0: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0xC3B8: +case 0xC5B8: +case 0xC7B8: +case 0xC9B8: +case 0xCBB8: +case 0xCDB8: +case 0xCFB8: + +// ANDDa +case 0xC1B8: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xC3B9: +case 0xC5B9: +case 0xC7B9: +case 0xC9B9: +case 0xCBB9: +case 0xCDB9: +case 0xCFB9: + +// ANDDa +case 0xC1B9: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0xC39F: +case 0xC59F: +case 0xC79F: +case 0xC99F: +case 0xCB9F: +case 0xCD9F: +case 0xCF9F: + +// ANDDa +case 0xC19F: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xC3A7: +case 0xC5A7: +case 0xC7A7: +case 0xC9A7: +case 0xCBA7: +case 0xCDA7: +case 0xCFA7: + +// ANDDa +case 0xC1A7: +{ + u32 adr; + u32 res; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, res) + res &= src; + CPU->flag_C = 0; + CPU->flag_V = 0; + CPU->flag_notZ = res; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xC300: +case 0xC500: +case 0xC700: +case 0xC900: +case 0xCB00: +case 0xCD00: +case 0xCF00: +case 0xC101: +case 0xC301: +case 0xC501: +case 0xC701: +case 0xC901: +case 0xCB01: +case 0xCD01: +case 0xCF01: +case 0xC102: +case 0xC302: +case 0xC502: +case 0xC702: +case 0xC902: +case 0xCB02: +case 0xCD02: +case 0xCF02: +case 0xC103: +case 0xC303: +case 0xC503: +case 0xC703: +case 0xC903: +case 0xCB03: +case 0xCD03: +case 0xCF03: +case 0xC104: +case 0xC304: +case 0xC504: +case 0xC704: +case 0xC904: +case 0xCB04: +case 0xCD04: +case 0xCF04: +case 0xC105: +case 0xC305: +case 0xC505: +case 0xC705: +case 0xC905: +case 0xCB05: +case 0xCD05: +case 0xCF05: +case 0xC106: +case 0xC306: +case 0xC506: +case 0xC706: +case 0xC906: +case 0xCB06: +case 0xCD06: +case 0xCF06: +case 0xC107: +case 0xC307: +case 0xC507: +case 0xC707: +case 0xC907: +case 0xCB07: +case 0xCD07: +case 0xCF07: + +// ABCD +case 0xC100: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = (dst & 0xF) + (src & 0xF) + ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res += 6; + res += (dst & 0xF0) + (src & 0xF0); + if (res > 0x99) + { + res -= 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0xC308: +case 0xC508: +case 0xC708: +case 0xC908: +case 0xCB08: +case 0xCD08: +case 0xC109: +case 0xC309: +case 0xC509: +case 0xC709: +case 0xC909: +case 0xCB09: +case 0xCD09: +case 0xC10A: +case 0xC30A: +case 0xC50A: +case 0xC70A: +case 0xC90A: +case 0xCB0A: +case 0xCD0A: +case 0xC10B: +case 0xC30B: +case 0xC50B: +case 0xC70B: +case 0xC90B: +case 0xCB0B: +case 0xCD0B: +case 0xC10C: +case 0xC30C: +case 0xC50C: +case 0xC70C: +case 0xC90C: +case 0xCB0C: +case 0xCD0C: +case 0xC10D: +case 0xC30D: +case 0xC50D: +case 0xC70D: +case 0xC90D: +case 0xCB0D: +case 0xCD0D: +case 0xC10E: +case 0xC30E: +case 0xC50E: +case 0xC70E: +case 0xC90E: +case 0xCB0E: +case 0xCD0E: + +// ABCDM +case 0xC108: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) + (src & 0xF) + ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res += 6; + res += (dst & 0xF0) + (src & 0xF0); + if (res > 0x99) + { + res -= 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xC30F: +case 0xC50F: +case 0xC70F: +case 0xC90F: +case 0xCB0F: +case 0xCD0F: + +// ABCD7M +case 0xC10F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) + (src & 0xF) + ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res += 6; + res += (dst & 0xF0) + (src & 0xF0); + if (res > 0x99) + { + res -= 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xCF09: +case 0xCF0A: +case 0xCF0B: +case 0xCF0C: +case 0xCF0D: +case 0xCF0E: + +// ABCDM7 +case 0xCF08: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) + (src & 0xF) + ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res += 6; + res += (dst & 0xF0) + (src & 0xF0); + if (res > 0x99) + { + res -= 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// ABCD7M7 +case 0xCF0F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = (dst & 0xF) + (src & 0xF) + ((CPU->flag_X >> C68K_SR_X_SFT) & 1); + if (res > 9) res += 6; + res += (dst & 0xF0) + (src & 0xF0); + if (res > 0x99) + { + res -= 0xA0; + CPU->flag_X = CPU->flag_C = C68K_SR_C; + } + else CPU->flag_X = CPU->flag_C = 0; + CPU->flag_notZ |= res & 0xFF; + CPU->flag_N = res; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xC2C0: +case 0xC4C0: +case 0xC6C0: +case 0xC8C0: +case 0xCAC0: +case 0xCCC0: +case 0xCEC0: +case 0xC0C1: +case 0xC2C1: +case 0xC4C1: +case 0xC6C1: +case 0xC8C1: +case 0xCAC1: +case 0xCCC1: +case 0xCEC1: +case 0xC0C2: +case 0xC2C2: +case 0xC4C2: +case 0xC6C2: +case 0xC8C2: +case 0xCAC2: +case 0xCCC2: +case 0xCEC2: +case 0xC0C3: +case 0xC2C3: +case 0xC4C3: +case 0xC6C3: +case 0xC8C3: +case 0xCAC3: +case 0xCCC3: +case 0xCEC3: +case 0xC0C4: +case 0xC2C4: +case 0xC4C4: +case 0xC6C4: +case 0xC8C4: +case 0xCAC4: +case 0xCCC4: +case 0xCEC4: +case 0xC0C5: +case 0xC2C5: +case 0xC4C5: +case 0xC6C5: +case 0xC8C5: +case 0xCAC5: +case 0xCCC5: +case 0xCEC5: +case 0xC0C6: +case 0xC2C6: +case 0xC4C6: +case 0xC6C6: +case 0xC8C6: +case 0xCAC6: +case 0xCCC6: +case 0xCEC6: +case 0xC0C7: +case 0xC2C7: +case 0xC4C7: +case 0xC6C7: +case 0xC8C7: +case 0xCAC7: +case 0xCCC7: +case 0xCEC7: + +// MULU +case 0xC0C0: +{ + u32 res; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(50) +case 0xC2D0: +case 0xC4D0: +case 0xC6D0: +case 0xC8D0: +case 0xCAD0: +case 0xCCD0: +case 0xCED0: +case 0xC0D1: +case 0xC2D1: +case 0xC4D1: +case 0xC6D1: +case 0xC8D1: +case 0xCAD1: +case 0xCCD1: +case 0xCED1: +case 0xC0D2: +case 0xC2D2: +case 0xC4D2: +case 0xC6D2: +case 0xC8D2: +case 0xCAD2: +case 0xCCD2: +case 0xCED2: +case 0xC0D3: +case 0xC2D3: +case 0xC4D3: +case 0xC6D3: +case 0xC8D3: +case 0xCAD3: +case 0xCCD3: +case 0xCED3: +case 0xC0D4: +case 0xC2D4: +case 0xC4D4: +case 0xC6D4: +case 0xC8D4: +case 0xCAD4: +case 0xCCD4: +case 0xCED4: +case 0xC0D5: +case 0xC2D5: +case 0xC4D5: +case 0xC6D5: +case 0xC8D5: +case 0xCAD5: +case 0xCCD5: +case 0xCED5: +case 0xC0D6: +case 0xC2D6: +case 0xC4D6: +case 0xC6D6: +case 0xC8D6: +case 0xCAD6: +case 0xCCD6: +case 0xCED6: +case 0xC0D7: +case 0xC2D7: +case 0xC4D7: +case 0xC6D7: +case 0xC8D7: +case 0xCAD7: +case 0xCCD7: +case 0xCED7: + +// MULU +case 0xC0D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC2D8: +case 0xC4D8: +case 0xC6D8: +case 0xC8D8: +case 0xCAD8: +case 0xCCD8: +case 0xCED8: +case 0xC0D9: +case 0xC2D9: +case 0xC4D9: +case 0xC6D9: +case 0xC8D9: +case 0xCAD9: +case 0xCCD9: +case 0xCED9: +case 0xC0DA: +case 0xC2DA: +case 0xC4DA: +case 0xC6DA: +case 0xC8DA: +case 0xCADA: +case 0xCCDA: +case 0xCEDA: +case 0xC0DB: +case 0xC2DB: +case 0xC4DB: +case 0xC6DB: +case 0xC8DB: +case 0xCADB: +case 0xCCDB: +case 0xCEDB: +case 0xC0DC: +case 0xC2DC: +case 0xC4DC: +case 0xC6DC: +case 0xC8DC: +case 0xCADC: +case 0xCCDC: +case 0xCEDC: +case 0xC0DD: +case 0xC2DD: +case 0xC4DD: +case 0xC6DD: +case 0xC8DD: +case 0xCADD: +case 0xCCDD: +case 0xCEDD: +case 0xC0DE: +case 0xC2DE: +case 0xC4DE: +case 0xC6DE: +case 0xC8DE: +case 0xCADE: +case 0xCCDE: +case 0xCEDE: + +// MULU +case 0xC0D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC2E0: +case 0xC4E0: +case 0xC6E0: +case 0xC8E0: +case 0xCAE0: +case 0xCCE0: +case 0xCEE0: +case 0xC0E1: +case 0xC2E1: +case 0xC4E1: +case 0xC6E1: +case 0xC8E1: +case 0xCAE1: +case 0xCCE1: +case 0xCEE1: +case 0xC0E2: +case 0xC2E2: +case 0xC4E2: +case 0xC6E2: +case 0xC8E2: +case 0xCAE2: +case 0xCCE2: +case 0xCEE2: +case 0xC0E3: +case 0xC2E3: +case 0xC4E3: +case 0xC6E3: +case 0xC8E3: +case 0xCAE3: +case 0xCCE3: +case 0xCEE3: +case 0xC0E4: +case 0xC2E4: +case 0xC4E4: +case 0xC6E4: +case 0xC8E4: +case 0xCAE4: +case 0xCCE4: +case 0xCEE4: +case 0xC0E5: +case 0xC2E5: +case 0xC4E5: +case 0xC6E5: +case 0xC8E5: +case 0xCAE5: +case 0xCCE5: +case 0xCEE5: +case 0xC0E6: +case 0xC2E6: +case 0xC4E6: +case 0xC6E6: +case 0xC8E6: +case 0xCAE6: +case 0xCCE6: +case 0xCEE6: + +// MULU +case 0xC0E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(56) +case 0xC2E8: +case 0xC4E8: +case 0xC6E8: +case 0xC8E8: +case 0xCAE8: +case 0xCCE8: +case 0xCEE8: +case 0xC0E9: +case 0xC2E9: +case 0xC4E9: +case 0xC6E9: +case 0xC8E9: +case 0xCAE9: +case 0xCCE9: +case 0xCEE9: +case 0xC0EA: +case 0xC2EA: +case 0xC4EA: +case 0xC6EA: +case 0xC8EA: +case 0xCAEA: +case 0xCCEA: +case 0xCEEA: +case 0xC0EB: +case 0xC2EB: +case 0xC4EB: +case 0xC6EB: +case 0xC8EB: +case 0xCAEB: +case 0xCCEB: +case 0xCEEB: +case 0xC0EC: +case 0xC2EC: +case 0xC4EC: +case 0xC6EC: +case 0xC8EC: +case 0xCAEC: +case 0xCCEC: +case 0xCEEC: +case 0xC0ED: +case 0xC2ED: +case 0xC4ED: +case 0xC6ED: +case 0xC8ED: +case 0xCAED: +case 0xCCED: +case 0xCEED: +case 0xC0EE: +case 0xC2EE: +case 0xC4EE: +case 0xC6EE: +case 0xC8EE: +case 0xCAEE: +case 0xCCEE: +case 0xCEEE: +case 0xC0EF: +case 0xC2EF: +case 0xC4EF: +case 0xC6EF: +case 0xC8EF: +case 0xCAEF: +case 0xCCEF: +case 0xCEEF: + +// MULU +case 0xC0E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC2F0: +case 0xC4F0: +case 0xC6F0: +case 0xC8F0: +case 0xCAF0: +case 0xCCF0: +case 0xCEF0: +case 0xC0F1: +case 0xC2F1: +case 0xC4F1: +case 0xC6F1: +case 0xC8F1: +case 0xCAF1: +case 0xCCF1: +case 0xCEF1: +case 0xC0F2: +case 0xC2F2: +case 0xC4F2: +case 0xC6F2: +case 0xC8F2: +case 0xCAF2: +case 0xCCF2: +case 0xCEF2: +case 0xC0F3: +case 0xC2F3: +case 0xC4F3: +case 0xC6F3: +case 0xC8F3: +case 0xCAF3: +case 0xCCF3: +case 0xCEF3: +case 0xC0F4: +case 0xC2F4: +case 0xC4F4: +case 0xC6F4: +case 0xC8F4: +case 0xCAF4: +case 0xCCF4: +case 0xCEF4: +case 0xC0F5: +case 0xC2F5: +case 0xC4F5: +case 0xC6F5: +case 0xC8F5: +case 0xCAF5: +case 0xCCF5: +case 0xCEF5: +case 0xC0F6: +case 0xC2F6: +case 0xC4F6: +case 0xC6F6: +case 0xC8F6: +case 0xCAF6: +case 0xCCF6: +case 0xCEF6: +case 0xC0F7: +case 0xC2F7: +case 0xC4F7: +case 0xC6F7: +case 0xC8F7: +case 0xCAF7: +case 0xCCF7: +case 0xCEF7: + +// MULU +case 0xC0F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(60) +case 0xC2F8: +case 0xC4F8: +case 0xC6F8: +case 0xC8F8: +case 0xCAF8: +case 0xCCF8: +case 0xCEF8: + +// MULU +case 0xC0F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC2F9: +case 0xC4F9: +case 0xC6F9: +case 0xC8F9: +case 0xCAF9: +case 0xCCF9: +case 0xCEF9: + +// MULU +case 0xC0F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(62) +case 0xC2FA: +case 0xC4FA: +case 0xC6FA: +case 0xC8FA: +case 0xCAFA: +case 0xCCFA: +case 0xCEFA: + +// MULU +case 0xC0FA: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC2FB: +case 0xC4FB: +case 0xC6FB: +case 0xC8FB: +case 0xCAFB: +case 0xCCFB: +case 0xCEFB: + +// MULU +case 0xC0FB: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(60) +case 0xC2FC: +case 0xC4FC: +case 0xC6FC: +case 0xC8FC: +case 0xCAFC: +case 0xCCFC: +case 0xCEFC: + +// MULU +case 0xC0FC: +{ + u32 res; + pointer src; + src = FETCH_WORD; + PC += 2; + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(54) +case 0xC2DF: +case 0xC4DF: +case 0xC6DF: +case 0xC8DF: +case 0xCADF: +case 0xCCDF: +case 0xCEDF: + +// MULU +case 0xC0DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC2E7: +case 0xC4E7: +case 0xC6E7: +case 0xC8E7: +case 0xCAE7: +case 0xCCE7: +case 0xCEE7: + +// MULU +case 0xC0E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + res = (u16)CPU->D[(Opcode >> 9) & 7]; + res *= src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(56) +case 0xC3C0: +case 0xC5C0: +case 0xC7C0: +case 0xC9C0: +case 0xCBC0: +case 0xCDC0: +case 0xCFC0: +case 0xC1C1: +case 0xC3C1: +case 0xC5C1: +case 0xC7C1: +case 0xC9C1: +case 0xCBC1: +case 0xCDC1: +case 0xCFC1: +case 0xC1C2: +case 0xC3C2: +case 0xC5C2: +case 0xC7C2: +case 0xC9C2: +case 0xCBC2: +case 0xCDC2: +case 0xCFC2: +case 0xC1C3: +case 0xC3C3: +case 0xC5C3: +case 0xC7C3: +case 0xC9C3: +case 0xCBC3: +case 0xCDC3: +case 0xCFC3: +case 0xC1C4: +case 0xC3C4: +case 0xC5C4: +case 0xC7C4: +case 0xC9C4: +case 0xCBC4: +case 0xCDC4: +case 0xCFC4: +case 0xC1C5: +case 0xC3C5: +case 0xC5C5: +case 0xC7C5: +case 0xC9C5: +case 0xCBC5: +case 0xCDC5: +case 0xCFC5: +case 0xC1C6: +case 0xC3C6: +case 0xC5C6: +case 0xC7C6: +case 0xC9C6: +case 0xCBC6: +case 0xCDC6: +case 0xCFC6: +case 0xC1C7: +case 0xC3C7: +case 0xC5C7: +case 0xC7C7: +case 0xC9C7: +case 0xCBC7: +case 0xCDC7: +case 0xCFC7: + +// MULS +case 0xC1C0: +{ + u32 res; + pointer src; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(50) +case 0xC3D0: +case 0xC5D0: +case 0xC7D0: +case 0xC9D0: +case 0xCBD0: +case 0xCDD0: +case 0xCFD0: +case 0xC1D1: +case 0xC3D1: +case 0xC5D1: +case 0xC7D1: +case 0xC9D1: +case 0xCBD1: +case 0xCDD1: +case 0xCFD1: +case 0xC1D2: +case 0xC3D2: +case 0xC5D2: +case 0xC7D2: +case 0xC9D2: +case 0xCBD2: +case 0xCDD2: +case 0xCFD2: +case 0xC1D3: +case 0xC3D3: +case 0xC5D3: +case 0xC7D3: +case 0xC9D3: +case 0xCBD3: +case 0xCDD3: +case 0xCFD3: +case 0xC1D4: +case 0xC3D4: +case 0xC5D4: +case 0xC7D4: +case 0xC9D4: +case 0xCBD4: +case 0xCDD4: +case 0xCFD4: +case 0xC1D5: +case 0xC3D5: +case 0xC5D5: +case 0xC7D5: +case 0xC9D5: +case 0xCBD5: +case 0xCDD5: +case 0xCFD5: +case 0xC1D6: +case 0xC3D6: +case 0xC5D6: +case 0xC7D6: +case 0xC9D6: +case 0xCBD6: +case 0xCDD6: +case 0xCFD6: +case 0xC1D7: +case 0xC3D7: +case 0xC5D7: +case 0xC7D7: +case 0xC9D7: +case 0xCBD7: +case 0xCDD7: +case 0xCFD7: + +// MULS +case 0xC1D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC3D8: +case 0xC5D8: +case 0xC7D8: +case 0xC9D8: +case 0xCBD8: +case 0xCDD8: +case 0xCFD8: +case 0xC1D9: +case 0xC3D9: +case 0xC5D9: +case 0xC7D9: +case 0xC9D9: +case 0xCBD9: +case 0xCDD9: +case 0xCFD9: +case 0xC1DA: +case 0xC3DA: +case 0xC5DA: +case 0xC7DA: +case 0xC9DA: +case 0xCBDA: +case 0xCDDA: +case 0xCFDA: +case 0xC1DB: +case 0xC3DB: +case 0xC5DB: +case 0xC7DB: +case 0xC9DB: +case 0xCBDB: +case 0xCDDB: +case 0xCFDB: +case 0xC1DC: +case 0xC3DC: +case 0xC5DC: +case 0xC7DC: +case 0xC9DC: +case 0xCBDC: +case 0xCDDC: +case 0xCFDC: +case 0xC1DD: +case 0xC3DD: +case 0xC5DD: +case 0xC7DD: +case 0xC9DD: +case 0xCBDD: +case 0xCDDD: +case 0xCFDD: +case 0xC1DE: +case 0xC3DE: +case 0xC5DE: +case 0xC7DE: +case 0xC9DE: +case 0xCBDE: +case 0xCDDE: +case 0xCFDE: + +// MULS +case 0xC1D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC3E0: +case 0xC5E0: +case 0xC7E0: +case 0xC9E0: +case 0xCBE0: +case 0xCDE0: +case 0xCFE0: +case 0xC1E1: +case 0xC3E1: +case 0xC5E1: +case 0xC7E1: +case 0xC9E1: +case 0xCBE1: +case 0xCDE1: +case 0xCFE1: +case 0xC1E2: +case 0xC3E2: +case 0xC5E2: +case 0xC7E2: +case 0xC9E2: +case 0xCBE2: +case 0xCDE2: +case 0xCFE2: +case 0xC1E3: +case 0xC3E3: +case 0xC5E3: +case 0xC7E3: +case 0xC9E3: +case 0xCBE3: +case 0xCDE3: +case 0xCFE3: +case 0xC1E4: +case 0xC3E4: +case 0xC5E4: +case 0xC7E4: +case 0xC9E4: +case 0xCBE4: +case 0xCDE4: +case 0xCFE4: +case 0xC1E5: +case 0xC3E5: +case 0xC5E5: +case 0xC7E5: +case 0xC9E5: +case 0xCBE5: +case 0xCDE5: +case 0xCFE5: +case 0xC1E6: +case 0xC3E6: +case 0xC5E6: +case 0xC7E6: +case 0xC9E6: +case 0xCBE6: +case 0xCDE6: +case 0xCFE6: + +// MULS +case 0xC1E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(56) +case 0xC3E8: +case 0xC5E8: +case 0xC7E8: +case 0xC9E8: +case 0xCBE8: +case 0xCDE8: +case 0xCFE8: +case 0xC1E9: +case 0xC3E9: +case 0xC5E9: +case 0xC7E9: +case 0xC9E9: +case 0xCBE9: +case 0xCDE9: +case 0xCFE9: +case 0xC1EA: +case 0xC3EA: +case 0xC5EA: +case 0xC7EA: +case 0xC9EA: +case 0xCBEA: +case 0xCDEA: +case 0xCFEA: +case 0xC1EB: +case 0xC3EB: +case 0xC5EB: +case 0xC7EB: +case 0xC9EB: +case 0xCBEB: +case 0xCDEB: +case 0xCFEB: +case 0xC1EC: +case 0xC3EC: +case 0xC5EC: +case 0xC7EC: +case 0xC9EC: +case 0xCBEC: +case 0xCDEC: +case 0xCFEC: +case 0xC1ED: +case 0xC3ED: +case 0xC5ED: +case 0xC7ED: +case 0xC9ED: +case 0xCBED: +case 0xCDED: +case 0xCFED: +case 0xC1EE: +case 0xC3EE: +case 0xC5EE: +case 0xC7EE: +case 0xC9EE: +case 0xCBEE: +case 0xCDEE: +case 0xCFEE: +case 0xC1EF: +case 0xC3EF: +case 0xC5EF: +case 0xC7EF: +case 0xC9EF: +case 0xCBEF: +case 0xCDEF: +case 0xCFEF: + +// MULS +case 0xC1E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC3F0: +case 0xC5F0: +case 0xC7F0: +case 0xC9F0: +case 0xCBF0: +case 0xCDF0: +case 0xCFF0: +case 0xC1F1: +case 0xC3F1: +case 0xC5F1: +case 0xC7F1: +case 0xC9F1: +case 0xCBF1: +case 0xCDF1: +case 0xCFF1: +case 0xC1F2: +case 0xC3F2: +case 0xC5F2: +case 0xC7F2: +case 0xC9F2: +case 0xCBF2: +case 0xCDF2: +case 0xCFF2: +case 0xC1F3: +case 0xC3F3: +case 0xC5F3: +case 0xC7F3: +case 0xC9F3: +case 0xCBF3: +case 0xCDF3: +case 0xCFF3: +case 0xC1F4: +case 0xC3F4: +case 0xC5F4: +case 0xC7F4: +case 0xC9F4: +case 0xCBF4: +case 0xCDF4: +case 0xCFF4: +case 0xC1F5: +case 0xC3F5: +case 0xC5F5: +case 0xC7F5: +case 0xC9F5: +case 0xCBF5: +case 0xCDF5: +case 0xCFF5: +case 0xC1F6: +case 0xC3F6: +case 0xC5F6: +case 0xC7F6: +case 0xC9F6: +case 0xCBF6: +case 0xCDF6: +case 0xCFF6: +case 0xC1F7: +case 0xC3F7: +case 0xC5F7: +case 0xC7F7: +case 0xC9F7: +case 0xCBF7: +case 0xCDF7: +case 0xCFF7: + +// MULS +case 0xC1F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(60) +case 0xC3F8: +case 0xC5F8: +case 0xC7F8: +case 0xC9F8: +case 0xCBF8: +case 0xCDF8: +case 0xCFF8: + +// MULS +case 0xC1F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC3F9: +case 0xC5F9: +case 0xC7F9: +case 0xC9F9: +case 0xCBF9: +case 0xCDF9: +case 0xCFF9: + +// MULS +case 0xC1F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(62) +case 0xC3FA: +case 0xC5FA: +case 0xC7FA: +case 0xC9FA: +case 0xCBFA: +case 0xCDFA: +case 0xCFFA: + +// MULS +case 0xC1FA: +{ + u32 adr; + u32 res; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(58) +case 0xC3FB: +case 0xC5FB: +case 0xC7FB: +case 0xC9FB: +case 0xCBFB: +case 0xCDFB: +case 0xCFFB: + +// MULS +case 0xC1FB: +{ + u32 adr; + u32 res; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(60) +case 0xC3FC: +case 0xC5FC: +case 0xC7FC: +case 0xC9FC: +case 0xCBFC: +case 0xCDFC: +case 0xCFFC: + +// MULS +case 0xC1FC: +{ + u32 res; + pointer src; + src = (s32)(s16)FETCH_WORD; + PC += 2; + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(54) +case 0xC3DF: +case 0xC5DF: +case 0xC7DF: +case 0xC9DF: +case 0xCBDF: +case 0xCDDF: +case 0xCFDF: + +// MULS +case 0xC1DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(54) +case 0xC3E7: +case 0xC5E7: +case 0xC7E7: +case 0xC9E7: +case 0xCBE7: +case 0xCDE7: +case 0xCFE7: + +// MULS +case 0xC1E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + res = (s32)(s16)CPU->D[(Opcode >> 9) & 7]; + res *= (s32)src; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + CPU->flag_V = CPU->flag_C = 0; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(56) +case 0xC340: +case 0xC540: +case 0xC740: +case 0xC940: +case 0xCB40: +case 0xCD40: +case 0xCF40: +case 0xC141: +case 0xC341: +case 0xC541: +case 0xC741: +case 0xC941: +case 0xCB41: +case 0xCD41: +case 0xCF41: +case 0xC142: +case 0xC342: +case 0xC542: +case 0xC742: +case 0xC942: +case 0xCB42: +case 0xCD42: +case 0xCF42: +case 0xC143: +case 0xC343: +case 0xC543: +case 0xC743: +case 0xC943: +case 0xCB43: +case 0xCD43: +case 0xCF43: +case 0xC144: +case 0xC344: +case 0xC544: +case 0xC744: +case 0xC944: +case 0xCB44: +case 0xCD44: +case 0xCF44: +case 0xC145: +case 0xC345: +case 0xC545: +case 0xC745: +case 0xC945: +case 0xCB45: +case 0xCD45: +case 0xCF45: +case 0xC146: +case 0xC346: +case 0xC546: +case 0xC746: +case 0xC946: +case 0xCB46: +case 0xCD46: +case 0xCF46: +case 0xC147: +case 0xC347: +case 0xC547: +case 0xC747: +case 0xC947: +case 0xCB47: +case 0xCD47: +case 0xCF47: + +// EXGDD +case 0xC140: +{ + u32 res; + pointer src; + res = (u32)CPU->D[(Opcode >> 0) & 7]; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + res = src; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xC348: +case 0xC548: +case 0xC748: +case 0xC948: +case 0xCB48: +case 0xCD48: +case 0xCF48: +case 0xC149: +case 0xC349: +case 0xC549: +case 0xC749: +case 0xC949: +case 0xCB49: +case 0xCD49: +case 0xCF49: +case 0xC14A: +case 0xC34A: +case 0xC54A: +case 0xC74A: +case 0xC94A: +case 0xCB4A: +case 0xCD4A: +case 0xCF4A: +case 0xC14B: +case 0xC34B: +case 0xC54B: +case 0xC74B: +case 0xC94B: +case 0xCB4B: +case 0xCD4B: +case 0xCF4B: +case 0xC14C: +case 0xC34C: +case 0xC54C: +case 0xC74C: +case 0xC94C: +case 0xCB4C: +case 0xCD4C: +case 0xCF4C: +case 0xC14D: +case 0xC34D: +case 0xC54D: +case 0xC74D: +case 0xC94D: +case 0xCB4D: +case 0xCD4D: +case 0xCF4D: +case 0xC14E: +case 0xC34E: +case 0xC54E: +case 0xC74E: +case 0xC94E: +case 0xCB4E: +case 0xCD4E: +case 0xCF4E: +case 0xC14F: +case 0xC34F: +case 0xC54F: +case 0xC74F: +case 0xC94F: +case 0xCB4F: +case 0xCD4F: +case 0xCF4F: + +// EXGAA +case 0xC148: +{ + u32 res; + pointer src; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + src = (u32)CPU->A[(Opcode >> 9) & 7]; + CPU->A[(Opcode >> 9) & 7] = res; + res = src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(6) +case 0xC388: +case 0xC588: +case 0xC788: +case 0xC988: +case 0xCB88: +case 0xCD88: +case 0xCF88: +case 0xC189: +case 0xC389: +case 0xC589: +case 0xC789: +case 0xC989: +case 0xCB89: +case 0xCD89: +case 0xCF89: +case 0xC18A: +case 0xC38A: +case 0xC58A: +case 0xC78A: +case 0xC98A: +case 0xCB8A: +case 0xCD8A: +case 0xCF8A: +case 0xC18B: +case 0xC38B: +case 0xC58B: +case 0xC78B: +case 0xC98B: +case 0xCB8B: +case 0xCD8B: +case 0xCF8B: +case 0xC18C: +case 0xC38C: +case 0xC58C: +case 0xC78C: +case 0xC98C: +case 0xCB8C: +case 0xCD8C: +case 0xCF8C: +case 0xC18D: +case 0xC38D: +case 0xC58D: +case 0xC78D: +case 0xC98D: +case 0xCB8D: +case 0xCD8D: +case 0xCF8D: +case 0xC18E: +case 0xC38E: +case 0xC58E: +case 0xC78E: +case 0xC98E: +case 0xCB8E: +case 0xCD8E: +case 0xCF8E: +case 0xC18F: +case 0xC38F: +case 0xC58F: +case 0xC78F: +case 0xC98F: +case 0xCB8F: +case 0xCD8F: +case 0xCF8F: + +// EXGAD +case 0xC188: +{ + u32 res; + pointer src; + res = (u32)CPU->A[(Opcode >> 0) & 7]; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + res = src; + CPU->A[(Opcode >> 0) & 7] = res; +} +RET(6) diff --git a/yabause/src/c68k/c68k_opD.inc b/yabause/src/c68k/c68k_opD.inc new file mode 100644 index 0000000000..af269730e8 --- /dev/null +++ b/yabause/src/c68k/c68k_opD.inc @@ -0,0 +1,5950 @@ +case 0xD200: +case 0xD400: +case 0xD600: +case 0xD800: +case 0xDA00: +case 0xDC00: +case 0xDE00: +case 0xD001: +case 0xD201: +case 0xD401: +case 0xD601: +case 0xD801: +case 0xDA01: +case 0xDC01: +case 0xDE01: +case 0xD002: +case 0xD202: +case 0xD402: +case 0xD602: +case 0xD802: +case 0xDA02: +case 0xDC02: +case 0xDE02: +case 0xD003: +case 0xD203: +case 0xD403: +case 0xD603: +case 0xD803: +case 0xDA03: +case 0xDC03: +case 0xDE03: +case 0xD004: +case 0xD204: +case 0xD404: +case 0xD604: +case 0xD804: +case 0xDA04: +case 0xDC04: +case 0xDE04: +case 0xD005: +case 0xD205: +case 0xD405: +case 0xD605: +case 0xD805: +case 0xDA05: +case 0xDC05: +case 0xDE05: +case 0xD006: +case 0xD206: +case 0xD406: +case 0xD606: +case 0xD806: +case 0xDA06: +case 0xDC06: +case 0xDE06: +case 0xD007: +case 0xD207: +case 0xD407: +case 0xD607: +case 0xD807: +case 0xDA07: +case 0xDC07: +case 0xDE07: + +// ADDaD +case 0xD000: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD208: +case 0xD408: +case 0xD608: +case 0xD808: +case 0xDA08: +case 0xDC08: +case 0xDE08: +case 0xD009: +case 0xD209: +case 0xD409: +case 0xD609: +case 0xD809: +case 0xDA09: +case 0xDC09: +case 0xDE09: +case 0xD00A: +case 0xD20A: +case 0xD40A: +case 0xD60A: +case 0xD80A: +case 0xDA0A: +case 0xDC0A: +case 0xDE0A: +case 0xD00B: +case 0xD20B: +case 0xD40B: +case 0xD60B: +case 0xD80B: +case 0xDA0B: +case 0xDC0B: +case 0xDE0B: +case 0xD00C: +case 0xD20C: +case 0xD40C: +case 0xD60C: +case 0xD80C: +case 0xDA0C: +case 0xDC0C: +case 0xDE0C: +case 0xD00D: +case 0xD20D: +case 0xD40D: +case 0xD60D: +case 0xD80D: +case 0xDA0D: +case 0xDC0D: +case 0xDE0D: +case 0xD00E: +case 0xD20E: +case 0xD40E: +case 0xD60E: +case 0xD80E: +case 0xDA0E: +case 0xDC0E: +case 0xDE0E: +case 0xD00F: +case 0xD20F: +case 0xD40F: +case 0xD60F: +case 0xD80F: +case 0xDA0F: +case 0xDC0F: +case 0xDE0F: + +// ADDaD +case 0xD008: +{ + u32 res; + pointer dst; + pointer src; + // can't read byte from Ax registers ! + CPU->Status |= C68K_FAULTED; + CCnt = 0; + goto C68k_Exec_Really_End; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD210: +case 0xD410: +case 0xD610: +case 0xD810: +case 0xDA10: +case 0xDC10: +case 0xDE10: +case 0xD011: +case 0xD211: +case 0xD411: +case 0xD611: +case 0xD811: +case 0xDA11: +case 0xDC11: +case 0xDE11: +case 0xD012: +case 0xD212: +case 0xD412: +case 0xD612: +case 0xD812: +case 0xDA12: +case 0xDC12: +case 0xDE12: +case 0xD013: +case 0xD213: +case 0xD413: +case 0xD613: +case 0xD813: +case 0xDA13: +case 0xDC13: +case 0xDE13: +case 0xD014: +case 0xD214: +case 0xD414: +case 0xD614: +case 0xD814: +case 0xDA14: +case 0xDC14: +case 0xDE14: +case 0xD015: +case 0xD215: +case 0xD415: +case 0xD615: +case 0xD815: +case 0xDA15: +case 0xDC15: +case 0xDE15: +case 0xD016: +case 0xD216: +case 0xD416: +case 0xD616: +case 0xD816: +case 0xDA16: +case 0xDC16: +case 0xDE16: +case 0xD017: +case 0xD217: +case 0xD417: +case 0xD617: +case 0xD817: +case 0xDA17: +case 0xDC17: +case 0xDE17: + +// ADDaD +case 0xD010: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD218: +case 0xD418: +case 0xD618: +case 0xD818: +case 0xDA18: +case 0xDC18: +case 0xDE18: +case 0xD019: +case 0xD219: +case 0xD419: +case 0xD619: +case 0xD819: +case 0xDA19: +case 0xDC19: +case 0xDE19: +case 0xD01A: +case 0xD21A: +case 0xD41A: +case 0xD61A: +case 0xD81A: +case 0xDA1A: +case 0xDC1A: +case 0xDE1A: +case 0xD01B: +case 0xD21B: +case 0xD41B: +case 0xD61B: +case 0xD81B: +case 0xDA1B: +case 0xDC1B: +case 0xDE1B: +case 0xD01C: +case 0xD21C: +case 0xD41C: +case 0xD61C: +case 0xD81C: +case 0xDA1C: +case 0xDC1C: +case 0xDE1C: +case 0xD01D: +case 0xD21D: +case 0xD41D: +case 0xD61D: +case 0xD81D: +case 0xDA1D: +case 0xDC1D: +case 0xDE1D: +case 0xD01E: +case 0xD21E: +case 0xD41E: +case 0xD61E: +case 0xD81E: +case 0xDA1E: +case 0xDC1E: +case 0xDE1E: + +// ADDaD +case 0xD018: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD220: +case 0xD420: +case 0xD620: +case 0xD820: +case 0xDA20: +case 0xDC20: +case 0xDE20: +case 0xD021: +case 0xD221: +case 0xD421: +case 0xD621: +case 0xD821: +case 0xDA21: +case 0xDC21: +case 0xDE21: +case 0xD022: +case 0xD222: +case 0xD422: +case 0xD622: +case 0xD822: +case 0xDA22: +case 0xDC22: +case 0xDE22: +case 0xD023: +case 0xD223: +case 0xD423: +case 0xD623: +case 0xD823: +case 0xDA23: +case 0xDC23: +case 0xDE23: +case 0xD024: +case 0xD224: +case 0xD424: +case 0xD624: +case 0xD824: +case 0xDA24: +case 0xDC24: +case 0xDE24: +case 0xD025: +case 0xD225: +case 0xD425: +case 0xD625: +case 0xD825: +case 0xDA25: +case 0xDC25: +case 0xDE25: +case 0xD026: +case 0xD226: +case 0xD426: +case 0xD626: +case 0xD826: +case 0xDA26: +case 0xDC26: +case 0xDE26: + +// ADDaD +case 0xD020: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xD228: +case 0xD428: +case 0xD628: +case 0xD828: +case 0xDA28: +case 0xDC28: +case 0xDE28: +case 0xD029: +case 0xD229: +case 0xD429: +case 0xD629: +case 0xD829: +case 0xDA29: +case 0xDC29: +case 0xDE29: +case 0xD02A: +case 0xD22A: +case 0xD42A: +case 0xD62A: +case 0xD82A: +case 0xDA2A: +case 0xDC2A: +case 0xDE2A: +case 0xD02B: +case 0xD22B: +case 0xD42B: +case 0xD62B: +case 0xD82B: +case 0xDA2B: +case 0xDC2B: +case 0xDE2B: +case 0xD02C: +case 0xD22C: +case 0xD42C: +case 0xD62C: +case 0xD82C: +case 0xDA2C: +case 0xDC2C: +case 0xDE2C: +case 0xD02D: +case 0xD22D: +case 0xD42D: +case 0xD62D: +case 0xD82D: +case 0xDA2D: +case 0xDC2D: +case 0xDE2D: +case 0xD02E: +case 0xD22E: +case 0xD42E: +case 0xD62E: +case 0xD82E: +case 0xDA2E: +case 0xDC2E: +case 0xDE2E: +case 0xD02F: +case 0xD22F: +case 0xD42F: +case 0xD62F: +case 0xD82F: +case 0xDA2F: +case 0xDC2F: +case 0xDE2F: + +// ADDaD +case 0xD028: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD230: +case 0xD430: +case 0xD630: +case 0xD830: +case 0xDA30: +case 0xDC30: +case 0xDE30: +case 0xD031: +case 0xD231: +case 0xD431: +case 0xD631: +case 0xD831: +case 0xDA31: +case 0xDC31: +case 0xDE31: +case 0xD032: +case 0xD232: +case 0xD432: +case 0xD632: +case 0xD832: +case 0xDA32: +case 0xDC32: +case 0xDE32: +case 0xD033: +case 0xD233: +case 0xD433: +case 0xD633: +case 0xD833: +case 0xDA33: +case 0xDC33: +case 0xDE33: +case 0xD034: +case 0xD234: +case 0xD434: +case 0xD634: +case 0xD834: +case 0xDA34: +case 0xDC34: +case 0xDE34: +case 0xD035: +case 0xD235: +case 0xD435: +case 0xD635: +case 0xD835: +case 0xDA35: +case 0xDC35: +case 0xDE35: +case 0xD036: +case 0xD236: +case 0xD436: +case 0xD636: +case 0xD836: +case 0xDA36: +case 0xDC36: +case 0xDE36: +case 0xD037: +case 0xD237: +case 0xD437: +case 0xD637: +case 0xD837: +case 0xDA37: +case 0xDC37: +case 0xDE37: + +// ADDaD +case 0xD030: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xD238: +case 0xD438: +case 0xD638: +case 0xD838: +case 0xDA38: +case 0xDC38: +case 0xDE38: + +// ADDaD +case 0xD038: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD239: +case 0xD439: +case 0xD639: +case 0xD839: +case 0xDA39: +case 0xDC39: +case 0xDE39: + +// ADDaD +case 0xD039: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xD23A: +case 0xD43A: +case 0xD63A: +case 0xD83A: +case 0xDA3A: +case 0xDC3A: +case 0xDE3A: + +// ADDaD +case 0xD03A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD23B: +case 0xD43B: +case 0xD63B: +case 0xD83B: +case 0xDA3B: +case 0xDC3B: +case 0xDE3B: + +// ADDaD +case 0xD03B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xD23C: +case 0xD43C: +case 0xD63C: +case 0xD83C: +case 0xDA3C: +case 0xDC3C: +case 0xDE3C: + +// ADDaD +case 0xD03C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_BYTE; + PC += 2; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0xD21F: +case 0xD41F: +case 0xD61F: +case 0xD81F: +case 0xDA1F: +case 0xDC1F: +case 0xDE1F: + +// ADDaD +case 0xD01F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD227: +case 0xD427: +case 0xD627: +case 0xD827: +case 0xDA27: +case 0xDC27: +case 0xDE27: + +// ADDaD +case 0xD027: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xD240: +case 0xD440: +case 0xD640: +case 0xD840: +case 0xDA40: +case 0xDC40: +case 0xDE40: +case 0xD041: +case 0xD241: +case 0xD441: +case 0xD641: +case 0xD841: +case 0xDA41: +case 0xDC41: +case 0xDE41: +case 0xD042: +case 0xD242: +case 0xD442: +case 0xD642: +case 0xD842: +case 0xDA42: +case 0xDC42: +case 0xDE42: +case 0xD043: +case 0xD243: +case 0xD443: +case 0xD643: +case 0xD843: +case 0xDA43: +case 0xDC43: +case 0xDE43: +case 0xD044: +case 0xD244: +case 0xD444: +case 0xD644: +case 0xD844: +case 0xDA44: +case 0xDC44: +case 0xDE44: +case 0xD045: +case 0xD245: +case 0xD445: +case 0xD645: +case 0xD845: +case 0xDA45: +case 0xDC45: +case 0xDE45: +case 0xD046: +case 0xD246: +case 0xD446: +case 0xD646: +case 0xD846: +case 0xDA46: +case 0xDC46: +case 0xDE46: +case 0xD047: +case 0xD247: +case 0xD447: +case 0xD647: +case 0xD847: +case 0xDA47: +case 0xDC47: +case 0xDE47: + +// ADDaD +case 0xD040: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD248: +case 0xD448: +case 0xD648: +case 0xD848: +case 0xDA48: +case 0xDC48: +case 0xDE48: +case 0xD049: +case 0xD249: +case 0xD449: +case 0xD649: +case 0xD849: +case 0xDA49: +case 0xDC49: +case 0xDE49: +case 0xD04A: +case 0xD24A: +case 0xD44A: +case 0xD64A: +case 0xD84A: +case 0xDA4A: +case 0xDC4A: +case 0xDE4A: +case 0xD04B: +case 0xD24B: +case 0xD44B: +case 0xD64B: +case 0xD84B: +case 0xDA4B: +case 0xDC4B: +case 0xDE4B: +case 0xD04C: +case 0xD24C: +case 0xD44C: +case 0xD64C: +case 0xD84C: +case 0xDA4C: +case 0xDC4C: +case 0xDE4C: +case 0xD04D: +case 0xD24D: +case 0xD44D: +case 0xD64D: +case 0xD84D: +case 0xDA4D: +case 0xDC4D: +case 0xDE4D: +case 0xD04E: +case 0xD24E: +case 0xD44E: +case 0xD64E: +case 0xD84E: +case 0xDA4E: +case 0xDC4E: +case 0xDE4E: +case 0xD04F: +case 0xD24F: +case 0xD44F: +case 0xD64F: +case 0xD84F: +case 0xDA4F: +case 0xDC4F: +case 0xDE4F: + +// ADDaD +case 0xD048: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->A[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD250: +case 0xD450: +case 0xD650: +case 0xD850: +case 0xDA50: +case 0xDC50: +case 0xDE50: +case 0xD051: +case 0xD251: +case 0xD451: +case 0xD651: +case 0xD851: +case 0xDA51: +case 0xDC51: +case 0xDE51: +case 0xD052: +case 0xD252: +case 0xD452: +case 0xD652: +case 0xD852: +case 0xDA52: +case 0xDC52: +case 0xDE52: +case 0xD053: +case 0xD253: +case 0xD453: +case 0xD653: +case 0xD853: +case 0xDA53: +case 0xDC53: +case 0xDE53: +case 0xD054: +case 0xD254: +case 0xD454: +case 0xD654: +case 0xD854: +case 0xDA54: +case 0xDC54: +case 0xDE54: +case 0xD055: +case 0xD255: +case 0xD455: +case 0xD655: +case 0xD855: +case 0xDA55: +case 0xDC55: +case 0xDE55: +case 0xD056: +case 0xD256: +case 0xD456: +case 0xD656: +case 0xD856: +case 0xDA56: +case 0xDC56: +case 0xDE56: +case 0xD057: +case 0xD257: +case 0xD457: +case 0xD657: +case 0xD857: +case 0xDA57: +case 0xDC57: +case 0xDE57: + +// ADDaD +case 0xD050: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD258: +case 0xD458: +case 0xD658: +case 0xD858: +case 0xDA58: +case 0xDC58: +case 0xDE58: +case 0xD059: +case 0xD259: +case 0xD459: +case 0xD659: +case 0xD859: +case 0xDA59: +case 0xDC59: +case 0xDE59: +case 0xD05A: +case 0xD25A: +case 0xD45A: +case 0xD65A: +case 0xD85A: +case 0xDA5A: +case 0xDC5A: +case 0xDE5A: +case 0xD05B: +case 0xD25B: +case 0xD45B: +case 0xD65B: +case 0xD85B: +case 0xDA5B: +case 0xDC5B: +case 0xDE5B: +case 0xD05C: +case 0xD25C: +case 0xD45C: +case 0xD65C: +case 0xD85C: +case 0xDA5C: +case 0xDC5C: +case 0xDE5C: +case 0xD05D: +case 0xD25D: +case 0xD45D: +case 0xD65D: +case 0xD85D: +case 0xDA5D: +case 0xDC5D: +case 0xDE5D: +case 0xD05E: +case 0xD25E: +case 0xD45E: +case 0xD65E: +case 0xD85E: +case 0xDA5E: +case 0xDC5E: +case 0xDE5E: + +// ADDaD +case 0xD058: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD260: +case 0xD460: +case 0xD660: +case 0xD860: +case 0xDA60: +case 0xDC60: +case 0xDE60: +case 0xD061: +case 0xD261: +case 0xD461: +case 0xD661: +case 0xD861: +case 0xDA61: +case 0xDC61: +case 0xDE61: +case 0xD062: +case 0xD262: +case 0xD462: +case 0xD662: +case 0xD862: +case 0xDA62: +case 0xDC62: +case 0xDE62: +case 0xD063: +case 0xD263: +case 0xD463: +case 0xD663: +case 0xD863: +case 0xDA63: +case 0xDC63: +case 0xDE63: +case 0xD064: +case 0xD264: +case 0xD464: +case 0xD664: +case 0xD864: +case 0xDA64: +case 0xDC64: +case 0xDE64: +case 0xD065: +case 0xD265: +case 0xD465: +case 0xD665: +case 0xD865: +case 0xDA65: +case 0xDC65: +case 0xDE65: +case 0xD066: +case 0xD266: +case 0xD466: +case 0xD666: +case 0xD866: +case 0xDA66: +case 0xDC66: +case 0xDE66: + +// ADDaD +case 0xD060: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xD268: +case 0xD468: +case 0xD668: +case 0xD868: +case 0xDA68: +case 0xDC68: +case 0xDE68: +case 0xD069: +case 0xD269: +case 0xD469: +case 0xD669: +case 0xD869: +case 0xDA69: +case 0xDC69: +case 0xDE69: +case 0xD06A: +case 0xD26A: +case 0xD46A: +case 0xD66A: +case 0xD86A: +case 0xDA6A: +case 0xDC6A: +case 0xDE6A: +case 0xD06B: +case 0xD26B: +case 0xD46B: +case 0xD66B: +case 0xD86B: +case 0xDA6B: +case 0xDC6B: +case 0xDE6B: +case 0xD06C: +case 0xD26C: +case 0xD46C: +case 0xD66C: +case 0xD86C: +case 0xDA6C: +case 0xDC6C: +case 0xDE6C: +case 0xD06D: +case 0xD26D: +case 0xD46D: +case 0xD66D: +case 0xD86D: +case 0xDA6D: +case 0xDC6D: +case 0xDE6D: +case 0xD06E: +case 0xD26E: +case 0xD46E: +case 0xD66E: +case 0xD86E: +case 0xDA6E: +case 0xDC6E: +case 0xDE6E: +case 0xD06F: +case 0xD26F: +case 0xD46F: +case 0xD66F: +case 0xD86F: +case 0xDA6F: +case 0xDC6F: +case 0xDE6F: + +// ADDaD +case 0xD068: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD270: +case 0xD470: +case 0xD670: +case 0xD870: +case 0xDA70: +case 0xDC70: +case 0xDE70: +case 0xD071: +case 0xD271: +case 0xD471: +case 0xD671: +case 0xD871: +case 0xDA71: +case 0xDC71: +case 0xDE71: +case 0xD072: +case 0xD272: +case 0xD472: +case 0xD672: +case 0xD872: +case 0xDA72: +case 0xDC72: +case 0xDE72: +case 0xD073: +case 0xD273: +case 0xD473: +case 0xD673: +case 0xD873: +case 0xDA73: +case 0xDC73: +case 0xDE73: +case 0xD074: +case 0xD274: +case 0xD474: +case 0xD674: +case 0xD874: +case 0xDA74: +case 0xDC74: +case 0xDE74: +case 0xD075: +case 0xD275: +case 0xD475: +case 0xD675: +case 0xD875: +case 0xDA75: +case 0xDC75: +case 0xDE75: +case 0xD076: +case 0xD276: +case 0xD476: +case 0xD676: +case 0xD876: +case 0xDA76: +case 0xDC76: +case 0xDE76: +case 0xD077: +case 0xD277: +case 0xD477: +case 0xD677: +case 0xD877: +case 0xDA77: +case 0xDC77: +case 0xDE77: + +// ADDaD +case 0xD070: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xD278: +case 0xD478: +case 0xD678: +case 0xD878: +case 0xDA78: +case 0xDC78: +case 0xDE78: + +// ADDaD +case 0xD078: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD279: +case 0xD479: +case 0xD679: +case 0xD879: +case 0xDA79: +case 0xDC79: +case 0xDE79: + +// ADDaD +case 0xD079: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xD27A: +case 0xD47A: +case 0xD67A: +case 0xD87A: +case 0xDA7A: +case 0xDC7A: +case 0xDE7A: + +// ADDaD +case 0xD07A: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(12) +case 0xD27B: +case 0xD47B: +case 0xD67B: +case 0xD87B: +case 0xDA7B: +case 0xDC7B: +case 0xDE7B: + +// ADDaD +case 0xD07B: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(14) +case 0xD27C: +case 0xD47C: +case 0xD67C: +case 0xD87C: +case 0xDA7C: +case 0xDC7C: +case 0xDE7C: + +// ADDaD +case 0xD07C: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_WORD; + PC += 2; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0xD25F: +case 0xD45F: +case 0xD65F: +case 0xD85F: +case 0xDA5F: +case 0xDC5F: +case 0xDE5F: + +// ADDaD +case 0xD05F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(8) +case 0xD267: +case 0xD467: +case 0xD667: +case 0xD867: +case 0xDA67: +case 0xDC67: +case 0xDE67: + +// ADDaD +case 0xD067: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(10) +case 0xD280: +case 0xD480: +case 0xD680: +case 0xD880: +case 0xDA80: +case 0xDC80: +case 0xDE80: +case 0xD081: +case 0xD281: +case 0xD481: +case 0xD681: +case 0xD881: +case 0xDA81: +case 0xDC81: +case 0xDE81: +case 0xD082: +case 0xD282: +case 0xD482: +case 0xD682: +case 0xD882: +case 0xDA82: +case 0xDC82: +case 0xDE82: +case 0xD083: +case 0xD283: +case 0xD483: +case 0xD683: +case 0xD883: +case 0xDA83: +case 0xDC83: +case 0xDE83: +case 0xD084: +case 0xD284: +case 0xD484: +case 0xD684: +case 0xD884: +case 0xDA84: +case 0xDC84: +case 0xDE84: +case 0xD085: +case 0xD285: +case 0xD485: +case 0xD685: +case 0xD885: +case 0xDA85: +case 0xDC85: +case 0xDE85: +case 0xD086: +case 0xD286: +case 0xD486: +case 0xD686: +case 0xD886: +case 0xDA86: +case 0xDC86: +case 0xDE86: +case 0xD087: +case 0xD287: +case 0xD487: +case 0xD687: +case 0xD887: +case 0xDA87: +case 0xDC87: +case 0xDE87: + +// ADDaD +case 0xD080: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0xD288: +case 0xD488: +case 0xD688: +case 0xD888: +case 0xDA88: +case 0xDC88: +case 0xDE88: +case 0xD089: +case 0xD289: +case 0xD489: +case 0xD689: +case 0xD889: +case 0xDA89: +case 0xDC89: +case 0xDE89: +case 0xD08A: +case 0xD28A: +case 0xD48A: +case 0xD68A: +case 0xD88A: +case 0xDA8A: +case 0xDC8A: +case 0xDE8A: +case 0xD08B: +case 0xD28B: +case 0xD48B: +case 0xD68B: +case 0xD88B: +case 0xDA8B: +case 0xDC8B: +case 0xDE8B: +case 0xD08C: +case 0xD28C: +case 0xD48C: +case 0xD68C: +case 0xD88C: +case 0xDA8C: +case 0xDC8C: +case 0xDE8C: +case 0xD08D: +case 0xD28D: +case 0xD48D: +case 0xD68D: +case 0xD88D: +case 0xDA8D: +case 0xDC8D: +case 0xDE8D: +case 0xD08E: +case 0xD28E: +case 0xD48E: +case 0xD68E: +case 0xD88E: +case 0xDA8E: +case 0xDC8E: +case 0xDE8E: +case 0xD08F: +case 0xD28F: +case 0xD48F: +case 0xD68F: +case 0xD88F: +case 0xDA8F: +case 0xDC8F: +case 0xDE8F: + +// ADDaD +case 0xD088: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(6) +case 0xD290: +case 0xD490: +case 0xD690: +case 0xD890: +case 0xDA90: +case 0xDC90: +case 0xDE90: +case 0xD091: +case 0xD291: +case 0xD491: +case 0xD691: +case 0xD891: +case 0xDA91: +case 0xDC91: +case 0xDE91: +case 0xD092: +case 0xD292: +case 0xD492: +case 0xD692: +case 0xD892: +case 0xDA92: +case 0xDC92: +case 0xDE92: +case 0xD093: +case 0xD293: +case 0xD493: +case 0xD693: +case 0xD893: +case 0xDA93: +case 0xDC93: +case 0xDE93: +case 0xD094: +case 0xD294: +case 0xD494: +case 0xD694: +case 0xD894: +case 0xDA94: +case 0xDC94: +case 0xDE94: +case 0xD095: +case 0xD295: +case 0xD495: +case 0xD695: +case 0xD895: +case 0xDA95: +case 0xDC95: +case 0xDE95: +case 0xD096: +case 0xD296: +case 0xD496: +case 0xD696: +case 0xD896: +case 0xDA96: +case 0xDC96: +case 0xDE96: +case 0xD097: +case 0xD297: +case 0xD497: +case 0xD697: +case 0xD897: +case 0xDA97: +case 0xDC97: +case 0xDE97: + +// ADDaD +case 0xD090: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xD298: +case 0xD498: +case 0xD698: +case 0xD898: +case 0xDA98: +case 0xDC98: +case 0xDE98: +case 0xD099: +case 0xD299: +case 0xD499: +case 0xD699: +case 0xD899: +case 0xDA99: +case 0xDC99: +case 0xDE99: +case 0xD09A: +case 0xD29A: +case 0xD49A: +case 0xD69A: +case 0xD89A: +case 0xDA9A: +case 0xDC9A: +case 0xDE9A: +case 0xD09B: +case 0xD29B: +case 0xD49B: +case 0xD69B: +case 0xD89B: +case 0xDA9B: +case 0xDC9B: +case 0xDE9B: +case 0xD09C: +case 0xD29C: +case 0xD49C: +case 0xD69C: +case 0xD89C: +case 0xDA9C: +case 0xDC9C: +case 0xDE9C: +case 0xD09D: +case 0xD29D: +case 0xD49D: +case 0xD69D: +case 0xD89D: +case 0xDA9D: +case 0xDC9D: +case 0xDE9D: +case 0xD09E: +case 0xD29E: +case 0xD49E: +case 0xD69E: +case 0xD89E: +case 0xDA9E: +case 0xDC9E: +case 0xDE9E: + +// ADDaD +case 0xD098: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xD2A0: +case 0xD4A0: +case 0xD6A0: +case 0xD8A0: +case 0xDAA0: +case 0xDCA0: +case 0xDEA0: +case 0xD0A1: +case 0xD2A1: +case 0xD4A1: +case 0xD6A1: +case 0xD8A1: +case 0xDAA1: +case 0xDCA1: +case 0xDEA1: +case 0xD0A2: +case 0xD2A2: +case 0xD4A2: +case 0xD6A2: +case 0xD8A2: +case 0xDAA2: +case 0xDCA2: +case 0xDEA2: +case 0xD0A3: +case 0xD2A3: +case 0xD4A3: +case 0xD6A3: +case 0xD8A3: +case 0xDAA3: +case 0xDCA3: +case 0xDEA3: +case 0xD0A4: +case 0xD2A4: +case 0xD4A4: +case 0xD6A4: +case 0xD8A4: +case 0xDAA4: +case 0xDCA4: +case 0xDEA4: +case 0xD0A5: +case 0xD2A5: +case 0xD4A5: +case 0xD6A5: +case 0xD8A5: +case 0xDAA5: +case 0xDCA5: +case 0xDEA5: +case 0xD0A6: +case 0xD2A6: +case 0xD4A6: +case 0xD6A6: +case 0xD8A6: +case 0xDAA6: +case 0xDCA6: +case 0xDEA6: + +// ADDaD +case 0xD0A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0xD2A8: +case 0xD4A8: +case 0xD6A8: +case 0xD8A8: +case 0xDAA8: +case 0xDCA8: +case 0xDEA8: +case 0xD0A9: +case 0xD2A9: +case 0xD4A9: +case 0xD6A9: +case 0xD8A9: +case 0xDAA9: +case 0xDCA9: +case 0xDEA9: +case 0xD0AA: +case 0xD2AA: +case 0xD4AA: +case 0xD6AA: +case 0xD8AA: +case 0xDAAA: +case 0xDCAA: +case 0xDEAA: +case 0xD0AB: +case 0xD2AB: +case 0xD4AB: +case 0xD6AB: +case 0xD8AB: +case 0xDAAB: +case 0xDCAB: +case 0xDEAB: +case 0xD0AC: +case 0xD2AC: +case 0xD4AC: +case 0xD6AC: +case 0xD8AC: +case 0xDAAC: +case 0xDCAC: +case 0xDEAC: +case 0xD0AD: +case 0xD2AD: +case 0xD4AD: +case 0xD6AD: +case 0xD8AD: +case 0xDAAD: +case 0xDCAD: +case 0xDEAD: +case 0xD0AE: +case 0xD2AE: +case 0xD4AE: +case 0xD6AE: +case 0xD8AE: +case 0xDAAE: +case 0xDCAE: +case 0xDEAE: +case 0xD0AF: +case 0xD2AF: +case 0xD4AF: +case 0xD6AF: +case 0xD8AF: +case 0xDAAF: +case 0xDCAF: +case 0xDEAF: + +// ADDaD +case 0xD0A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xD2B0: +case 0xD4B0: +case 0xD6B0: +case 0xD8B0: +case 0xDAB0: +case 0xDCB0: +case 0xDEB0: +case 0xD0B1: +case 0xD2B1: +case 0xD4B1: +case 0xD6B1: +case 0xD8B1: +case 0xDAB1: +case 0xDCB1: +case 0xDEB1: +case 0xD0B2: +case 0xD2B2: +case 0xD4B2: +case 0xD6B2: +case 0xD8B2: +case 0xDAB2: +case 0xDCB2: +case 0xDEB2: +case 0xD0B3: +case 0xD2B3: +case 0xD4B3: +case 0xD6B3: +case 0xD8B3: +case 0xDAB3: +case 0xDCB3: +case 0xDEB3: +case 0xD0B4: +case 0xD2B4: +case 0xD4B4: +case 0xD6B4: +case 0xD8B4: +case 0xDAB4: +case 0xDCB4: +case 0xDEB4: +case 0xD0B5: +case 0xD2B5: +case 0xD4B5: +case 0xD6B5: +case 0xD8B5: +case 0xDAB5: +case 0xDCB5: +case 0xDEB5: +case 0xD0B6: +case 0xD2B6: +case 0xD4B6: +case 0xD6B6: +case 0xD8B6: +case 0xDAB6: +case 0xDCB6: +case 0xDEB6: +case 0xD0B7: +case 0xD2B7: +case 0xD4B7: +case 0xD6B7: +case 0xD8B7: +case 0xDAB7: +case 0xDCB7: +case 0xDEB7: + +// ADDaD +case 0xD0B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0xD2B8: +case 0xD4B8: +case 0xD6B8: +case 0xD8B8: +case 0xDAB8: +case 0xDCB8: +case 0xDEB8: + +// ADDaD +case 0xD0B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xD2B9: +case 0xD4B9: +case 0xD6B9: +case 0xD8B9: +case 0xDAB9: +case 0xDCB9: +case 0xDEB9: + +// ADDaD +case 0xD0B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(24) +case 0xD2BA: +case 0xD4BA: +case 0xD6BA: +case 0xD8BA: +case 0xDABA: +case 0xDCBA: +case 0xDEBA: + +// ADDaD +case 0xD0BA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(20) +case 0xD2BB: +case 0xD4BB: +case 0xD6BB: +case 0xD8BB: +case 0xDABB: +case 0xDCBB: +case 0xDEBB: + +// ADDaD +case 0xD0BB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(22) +case 0xD2BC: +case 0xD4BC: +case 0xD6BC: +case 0xD8BC: +case 0xDABC: +case 0xDCBC: +case 0xDEBC: + +// ADDaD +case 0xD0BC: +{ + u32 res; + pointer dst; + pointer src; + src = FETCH_LONG; + PC += 4; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(14) +case 0xD29F: +case 0xD49F: +case 0xD69F: +case 0xD89F: +case 0xDA9F: +case 0xDC9F: +case 0xDE9F: + +// ADDaD +case 0xD09F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(16) +case 0xD2A7: +case 0xD4A7: +case 0xD6A7: +case 0xD8A7: +case 0xDAA7: +case 0xDCA7: +case 0xDEA7: + +// ADDaD +case 0xD0A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; + POST_IO +} +RET(18) +case 0xD310: +case 0xD510: +case 0xD710: +case 0xD910: +case 0xDB10: +case 0xDD10: +case 0xDF10: +case 0xD111: +case 0xD311: +case 0xD511: +case 0xD711: +case 0xD911: +case 0xDB11: +case 0xDD11: +case 0xDF11: +case 0xD112: +case 0xD312: +case 0xD512: +case 0xD712: +case 0xD912: +case 0xDB12: +case 0xDD12: +case 0xDF12: +case 0xD113: +case 0xD313: +case 0xD513: +case 0xD713: +case 0xD913: +case 0xDB13: +case 0xDD13: +case 0xDF13: +case 0xD114: +case 0xD314: +case 0xD514: +case 0xD714: +case 0xD914: +case 0xDB14: +case 0xDD14: +case 0xDF14: +case 0xD115: +case 0xD315: +case 0xD515: +case 0xD715: +case 0xD915: +case 0xDB15: +case 0xDD15: +case 0xDF15: +case 0xD116: +case 0xD316: +case 0xD516: +case 0xD716: +case 0xD916: +case 0xDB16: +case 0xDD16: +case 0xDF16: +case 0xD117: +case 0xD317: +case 0xD517: +case 0xD717: +case 0xD917: +case 0xDB17: +case 0xDD17: +case 0xDF17: + +// ADDDa +case 0xD110: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xD318: +case 0xD518: +case 0xD718: +case 0xD918: +case 0xDB18: +case 0xDD18: +case 0xDF18: +case 0xD119: +case 0xD319: +case 0xD519: +case 0xD719: +case 0xD919: +case 0xDB19: +case 0xDD19: +case 0xDF19: +case 0xD11A: +case 0xD31A: +case 0xD51A: +case 0xD71A: +case 0xD91A: +case 0xDB1A: +case 0xDD1A: +case 0xDF1A: +case 0xD11B: +case 0xD31B: +case 0xD51B: +case 0xD71B: +case 0xD91B: +case 0xDB1B: +case 0xDD1B: +case 0xDF1B: +case 0xD11C: +case 0xD31C: +case 0xD51C: +case 0xD71C: +case 0xD91C: +case 0xDB1C: +case 0xDD1C: +case 0xDF1C: +case 0xD11D: +case 0xD31D: +case 0xD51D: +case 0xD71D: +case 0xD91D: +case 0xDB1D: +case 0xDD1D: +case 0xDF1D: +case 0xD11E: +case 0xD31E: +case 0xD51E: +case 0xD71E: +case 0xD91E: +case 0xDB1E: +case 0xDD1E: +case 0xDF1E: + +// ADDDa +case 0xD118: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 1; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xD320: +case 0xD520: +case 0xD720: +case 0xD920: +case 0xDB20: +case 0xDD20: +case 0xDF20: +case 0xD121: +case 0xD321: +case 0xD521: +case 0xD721: +case 0xD921: +case 0xDB21: +case 0xDD21: +case 0xDF21: +case 0xD122: +case 0xD322: +case 0xD522: +case 0xD722: +case 0xD922: +case 0xDB22: +case 0xDD22: +case 0xDF22: +case 0xD123: +case 0xD323: +case 0xD523: +case 0xD723: +case 0xD923: +case 0xDB23: +case 0xDD23: +case 0xDF23: +case 0xD124: +case 0xD324: +case 0xD524: +case 0xD724: +case 0xD924: +case 0xDB24: +case 0xDD24: +case 0xDF24: +case 0xD125: +case 0xD325: +case 0xD525: +case 0xD725: +case 0xD925: +case 0xDB25: +case 0xDD25: +case 0xDF25: +case 0xD126: +case 0xD326: +case 0xD526: +case 0xD726: +case 0xD926: +case 0xDB26: +case 0xDD26: +case 0xDF26: + +// ADDDa +case 0xD120: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xD328: +case 0xD528: +case 0xD728: +case 0xD928: +case 0xDB28: +case 0xDD28: +case 0xDF28: +case 0xD129: +case 0xD329: +case 0xD529: +case 0xD729: +case 0xD929: +case 0xDB29: +case 0xDD29: +case 0xDF29: +case 0xD12A: +case 0xD32A: +case 0xD52A: +case 0xD72A: +case 0xD92A: +case 0xDB2A: +case 0xDD2A: +case 0xDF2A: +case 0xD12B: +case 0xD32B: +case 0xD52B: +case 0xD72B: +case 0xD92B: +case 0xDB2B: +case 0xDD2B: +case 0xDF2B: +case 0xD12C: +case 0xD32C: +case 0xD52C: +case 0xD72C: +case 0xD92C: +case 0xDB2C: +case 0xDD2C: +case 0xDF2C: +case 0xD12D: +case 0xD32D: +case 0xD52D: +case 0xD72D: +case 0xD92D: +case 0xDB2D: +case 0xDD2D: +case 0xDF2D: +case 0xD12E: +case 0xD32E: +case 0xD52E: +case 0xD72E: +case 0xD92E: +case 0xDB2E: +case 0xDD2E: +case 0xDF2E: +case 0xD12F: +case 0xD32F: +case 0xD52F: +case 0xD72F: +case 0xD92F: +case 0xDB2F: +case 0xDD2F: +case 0xDF2F: + +// ADDDa +case 0xD128: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xD330: +case 0xD530: +case 0xD730: +case 0xD930: +case 0xDB30: +case 0xDD30: +case 0xDF30: +case 0xD131: +case 0xD331: +case 0xD531: +case 0xD731: +case 0xD931: +case 0xDB31: +case 0xDD31: +case 0xDF31: +case 0xD132: +case 0xD332: +case 0xD532: +case 0xD732: +case 0xD932: +case 0xDB32: +case 0xDD32: +case 0xDF32: +case 0xD133: +case 0xD333: +case 0xD533: +case 0xD733: +case 0xD933: +case 0xDB33: +case 0xDD33: +case 0xDF33: +case 0xD134: +case 0xD334: +case 0xD534: +case 0xD734: +case 0xD934: +case 0xDB34: +case 0xDD34: +case 0xDF34: +case 0xD135: +case 0xD335: +case 0xD535: +case 0xD735: +case 0xD935: +case 0xDB35: +case 0xDD35: +case 0xDF35: +case 0xD136: +case 0xD336: +case 0xD536: +case 0xD736: +case 0xD936: +case 0xDB36: +case 0xDD36: +case 0xDF36: +case 0xD137: +case 0xD337: +case 0xD537: +case 0xD737: +case 0xD937: +case 0xDB37: +case 0xDD37: +case 0xDF37: + +// ADDDa +case 0xD130: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xD338: +case 0xD538: +case 0xD738: +case 0xD938: +case 0xDB38: +case 0xDD38: +case 0xDF38: + +// ADDDa +case 0xD138: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(16) +case 0xD339: +case 0xD539: +case 0xD739: +case 0xD939: +case 0xDB39: +case 0xDD39: +case 0xDF39: + +// ADDDa +case 0xD139: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(20) +case 0xD31F: +case 0xD51F: +case 0xD71F: +case 0xD91F: +case 0xDB1F: +case 0xDD1F: +case 0xDF1F: + +// ADDDa +case 0xD11F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(12) +case 0xD327: +case 0xD527: +case 0xD727: +case 0xD927: +case 0xDB27: +case 0xDD27: +case 0xDF27: + +// ADDDa +case 0xD127: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, dst) + res = dst + src; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ = res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(14) +case 0xD350: +case 0xD550: +case 0xD750: +case 0xD950: +case 0xDB50: +case 0xDD50: +case 0xDF50: +case 0xD151: +case 0xD351: +case 0xD551: +case 0xD751: +case 0xD951: +case 0xDB51: +case 0xDD51: +case 0xDF51: +case 0xD152: +case 0xD352: +case 0xD552: +case 0xD752: +case 0xD952: +case 0xDB52: +case 0xDD52: +case 0xDF52: +case 0xD153: +case 0xD353: +case 0xD553: +case 0xD753: +case 0xD953: +case 0xDB53: +case 0xDD53: +case 0xDF53: +case 0xD154: +case 0xD354: +case 0xD554: +case 0xD754: +case 0xD954: +case 0xDB54: +case 0xDD54: +case 0xDF54: +case 0xD155: +case 0xD355: +case 0xD555: +case 0xD755: +case 0xD955: +case 0xDB55: +case 0xDD55: +case 0xDF55: +case 0xD156: +case 0xD356: +case 0xD556: +case 0xD756: +case 0xD956: +case 0xDB56: +case 0xDD56: +case 0xDF56: +case 0xD157: +case 0xD357: +case 0xD557: +case 0xD757: +case 0xD957: +case 0xDB57: +case 0xDD57: +case 0xDF57: + +// ADDDa +case 0xD150: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xD358: +case 0xD558: +case 0xD758: +case 0xD958: +case 0xDB58: +case 0xDD58: +case 0xDF58: +case 0xD159: +case 0xD359: +case 0xD559: +case 0xD759: +case 0xD959: +case 0xDB59: +case 0xDD59: +case 0xDF59: +case 0xD15A: +case 0xD35A: +case 0xD55A: +case 0xD75A: +case 0xD95A: +case 0xDB5A: +case 0xDD5A: +case 0xDF5A: +case 0xD15B: +case 0xD35B: +case 0xD55B: +case 0xD75B: +case 0xD95B: +case 0xDB5B: +case 0xDD5B: +case 0xDF5B: +case 0xD15C: +case 0xD35C: +case 0xD55C: +case 0xD75C: +case 0xD95C: +case 0xDB5C: +case 0xDD5C: +case 0xDF5C: +case 0xD15D: +case 0xD35D: +case 0xD55D: +case 0xD75D: +case 0xD95D: +case 0xDB5D: +case 0xDD5D: +case 0xDF5D: +case 0xD15E: +case 0xD35E: +case 0xD55E: +case 0xD75E: +case 0xD95E: +case 0xDB5E: +case 0xDD5E: +case 0xDF5E: + +// ADDDa +case 0xD158: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xD360: +case 0xD560: +case 0xD760: +case 0xD960: +case 0xDB60: +case 0xDD60: +case 0xDF60: +case 0xD161: +case 0xD361: +case 0xD561: +case 0xD761: +case 0xD961: +case 0xDB61: +case 0xDD61: +case 0xDF61: +case 0xD162: +case 0xD362: +case 0xD562: +case 0xD762: +case 0xD962: +case 0xDB62: +case 0xDD62: +case 0xDF62: +case 0xD163: +case 0xD363: +case 0xD563: +case 0xD763: +case 0xD963: +case 0xDB63: +case 0xDD63: +case 0xDF63: +case 0xD164: +case 0xD364: +case 0xD564: +case 0xD764: +case 0xD964: +case 0xDB64: +case 0xDD64: +case 0xDF64: +case 0xD165: +case 0xD365: +case 0xD565: +case 0xD765: +case 0xD965: +case 0xDB65: +case 0xDD65: +case 0xDF65: +case 0xD166: +case 0xD366: +case 0xD566: +case 0xD766: +case 0xD966: +case 0xDB66: +case 0xDD66: +case 0xDF66: + +// ADDDa +case 0xD160: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xD368: +case 0xD568: +case 0xD768: +case 0xD968: +case 0xDB68: +case 0xDD68: +case 0xDF68: +case 0xD169: +case 0xD369: +case 0xD569: +case 0xD769: +case 0xD969: +case 0xDB69: +case 0xDD69: +case 0xDF69: +case 0xD16A: +case 0xD36A: +case 0xD56A: +case 0xD76A: +case 0xD96A: +case 0xDB6A: +case 0xDD6A: +case 0xDF6A: +case 0xD16B: +case 0xD36B: +case 0xD56B: +case 0xD76B: +case 0xD96B: +case 0xDB6B: +case 0xDD6B: +case 0xDF6B: +case 0xD16C: +case 0xD36C: +case 0xD56C: +case 0xD76C: +case 0xD96C: +case 0xDB6C: +case 0xDD6C: +case 0xDF6C: +case 0xD16D: +case 0xD36D: +case 0xD56D: +case 0xD76D: +case 0xD96D: +case 0xDB6D: +case 0xDD6D: +case 0xDF6D: +case 0xD16E: +case 0xD36E: +case 0xD56E: +case 0xD76E: +case 0xD96E: +case 0xDB6E: +case 0xDD6E: +case 0xDF6E: +case 0xD16F: +case 0xD36F: +case 0xD56F: +case 0xD76F: +case 0xD96F: +case 0xDB6F: +case 0xDD6F: +case 0xDF6F: + +// ADDDa +case 0xD168: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xD370: +case 0xD570: +case 0xD770: +case 0xD970: +case 0xDB70: +case 0xDD70: +case 0xDF70: +case 0xD171: +case 0xD371: +case 0xD571: +case 0xD771: +case 0xD971: +case 0xDB71: +case 0xDD71: +case 0xDF71: +case 0xD172: +case 0xD372: +case 0xD572: +case 0xD772: +case 0xD972: +case 0xDB72: +case 0xDD72: +case 0xDF72: +case 0xD173: +case 0xD373: +case 0xD573: +case 0xD773: +case 0xD973: +case 0xDB73: +case 0xDD73: +case 0xDF73: +case 0xD174: +case 0xD374: +case 0xD574: +case 0xD774: +case 0xD974: +case 0xDB74: +case 0xDD74: +case 0xDF74: +case 0xD175: +case 0xD375: +case 0xD575: +case 0xD775: +case 0xD975: +case 0xDB75: +case 0xDD75: +case 0xDF75: +case 0xD176: +case 0xD376: +case 0xD576: +case 0xD776: +case 0xD976: +case 0xDB76: +case 0xDD76: +case 0xDF76: +case 0xD177: +case 0xD377: +case 0xD577: +case 0xD777: +case 0xD977: +case 0xDB77: +case 0xDD77: +case 0xDF77: + +// ADDDa +case 0xD170: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xD378: +case 0xD578: +case 0xD778: +case 0xD978: +case 0xDB78: +case 0xDD78: +case 0xDF78: + +// ADDDa +case 0xD178: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xD379: +case 0xD579: +case 0xD779: +case 0xD979: +case 0xDB79: +case 0xDD79: +case 0xDF79: + +// ADDDa +case 0xD179: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) +case 0xD35F: +case 0xD55F: +case 0xD75F: +case 0xD95F: +case 0xDB5F: +case 0xDD5F: +case 0xDF5F: + +// ADDDa +case 0xD15F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xD367: +case 0xD567: +case 0xD767: +case 0xD967: +case 0xDB67: +case 0xDD67: +case 0xDF67: + +// ADDDa +case 0xD167: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, dst) + res = dst + src; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ = res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xD390: +case 0xD590: +case 0xD790: +case 0xD990: +case 0xDB90: +case 0xDD90: +case 0xDF90: +case 0xD191: +case 0xD391: +case 0xD591: +case 0xD791: +case 0xD991: +case 0xDB91: +case 0xDD91: +case 0xDF91: +case 0xD192: +case 0xD392: +case 0xD592: +case 0xD792: +case 0xD992: +case 0xDB92: +case 0xDD92: +case 0xDF92: +case 0xD193: +case 0xD393: +case 0xD593: +case 0xD793: +case 0xD993: +case 0xDB93: +case 0xDD93: +case 0xDF93: +case 0xD194: +case 0xD394: +case 0xD594: +case 0xD794: +case 0xD994: +case 0xDB94: +case 0xDD94: +case 0xDF94: +case 0xD195: +case 0xD395: +case 0xD595: +case 0xD795: +case 0xD995: +case 0xDB95: +case 0xDD95: +case 0xDF95: +case 0xD196: +case 0xD396: +case 0xD596: +case 0xD796: +case 0xD996: +case 0xDB96: +case 0xDD96: +case 0xDF96: +case 0xD197: +case 0xD397: +case 0xD597: +case 0xD797: +case 0xD997: +case 0xDB97: +case 0xDD97: +case 0xDF97: + +// ADDDa +case 0xD190: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xD398: +case 0xD598: +case 0xD798: +case 0xD998: +case 0xDB98: +case 0xDD98: +case 0xDF98: +case 0xD199: +case 0xD399: +case 0xD599: +case 0xD799: +case 0xD999: +case 0xDB99: +case 0xDD99: +case 0xDF99: +case 0xD19A: +case 0xD39A: +case 0xD59A: +case 0xD79A: +case 0xD99A: +case 0xDB9A: +case 0xDD9A: +case 0xDF9A: +case 0xD19B: +case 0xD39B: +case 0xD59B: +case 0xD79B: +case 0xD99B: +case 0xDB9B: +case 0xDD9B: +case 0xDF9B: +case 0xD19C: +case 0xD39C: +case 0xD59C: +case 0xD79C: +case 0xD99C: +case 0xDB9C: +case 0xDD9C: +case 0xDF9C: +case 0xD19D: +case 0xD39D: +case 0xD59D: +case 0xD79D: +case 0xD99D: +case 0xDB9D: +case 0xDD9D: +case 0xDF9D: +case 0xD19E: +case 0xD39E: +case 0xD59E: +case 0xD79E: +case 0xD99E: +case 0xDB9E: +case 0xDD9E: +case 0xDF9E: + +// ADDDa +case 0xD198: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xD3A0: +case 0xD5A0: +case 0xD7A0: +case 0xD9A0: +case 0xDBA0: +case 0xDDA0: +case 0xDFA0: +case 0xD1A1: +case 0xD3A1: +case 0xD5A1: +case 0xD7A1: +case 0xD9A1: +case 0xDBA1: +case 0xDDA1: +case 0xDFA1: +case 0xD1A2: +case 0xD3A2: +case 0xD5A2: +case 0xD7A2: +case 0xD9A2: +case 0xDBA2: +case 0xDDA2: +case 0xDFA2: +case 0xD1A3: +case 0xD3A3: +case 0xD5A3: +case 0xD7A3: +case 0xD9A3: +case 0xDBA3: +case 0xDDA3: +case 0xDFA3: +case 0xD1A4: +case 0xD3A4: +case 0xD5A4: +case 0xD7A4: +case 0xD9A4: +case 0xDBA4: +case 0xDDA4: +case 0xDFA4: +case 0xD1A5: +case 0xD3A5: +case 0xD5A5: +case 0xD7A5: +case 0xD9A5: +case 0xDBA5: +case 0xDDA5: +case 0xDFA5: +case 0xD1A6: +case 0xD3A6: +case 0xD5A6: +case 0xD7A6: +case 0xD9A6: +case 0xDBA6: +case 0xDDA6: +case 0xDFA6: + +// ADDDa +case 0xD1A0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xD3A8: +case 0xD5A8: +case 0xD7A8: +case 0xD9A8: +case 0xDBA8: +case 0xDDA8: +case 0xDFA8: +case 0xD1A9: +case 0xD3A9: +case 0xD5A9: +case 0xD7A9: +case 0xD9A9: +case 0xDBA9: +case 0xDDA9: +case 0xDFA9: +case 0xD1AA: +case 0xD3AA: +case 0xD5AA: +case 0xD7AA: +case 0xD9AA: +case 0xDBAA: +case 0xDDAA: +case 0xDFAA: +case 0xD1AB: +case 0xD3AB: +case 0xD5AB: +case 0xD7AB: +case 0xD9AB: +case 0xDBAB: +case 0xDDAB: +case 0xDFAB: +case 0xD1AC: +case 0xD3AC: +case 0xD5AC: +case 0xD7AC: +case 0xD9AC: +case 0xDBAC: +case 0xDDAC: +case 0xDFAC: +case 0xD1AD: +case 0xD3AD: +case 0xD5AD: +case 0xD7AD: +case 0xD9AD: +case 0xDBAD: +case 0xDDAD: +case 0xDFAD: +case 0xD1AE: +case 0xD3AE: +case 0xD5AE: +case 0xD7AE: +case 0xD9AE: +case 0xDBAE: +case 0xDDAE: +case 0xDFAE: +case 0xD1AF: +case 0xD3AF: +case 0xD5AF: +case 0xD7AF: +case 0xD9AF: +case 0xDBAF: +case 0xDDAF: +case 0xDFAF: + +// ADDDa +case 0xD1A8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xD3B0: +case 0xD5B0: +case 0xD7B0: +case 0xD9B0: +case 0xDBB0: +case 0xDDB0: +case 0xDFB0: +case 0xD1B1: +case 0xD3B1: +case 0xD5B1: +case 0xD7B1: +case 0xD9B1: +case 0xDBB1: +case 0xDDB1: +case 0xDFB1: +case 0xD1B2: +case 0xD3B2: +case 0xD5B2: +case 0xD7B2: +case 0xD9B2: +case 0xDBB2: +case 0xDDB2: +case 0xDFB2: +case 0xD1B3: +case 0xD3B3: +case 0xD5B3: +case 0xD7B3: +case 0xD9B3: +case 0xDBB3: +case 0xDDB3: +case 0xDFB3: +case 0xD1B4: +case 0xD3B4: +case 0xD5B4: +case 0xD7B4: +case 0xD9B4: +case 0xDBB4: +case 0xDDB4: +case 0xDFB4: +case 0xD1B5: +case 0xD3B5: +case 0xD5B5: +case 0xD7B5: +case 0xD9B5: +case 0xDBB5: +case 0xDDB5: +case 0xDFB5: +case 0xD1B6: +case 0xD3B6: +case 0xD5B6: +case 0xD7B6: +case 0xD9B6: +case 0xDBB6: +case 0xDDB6: +case 0xDFB6: +case 0xD1B7: +case 0xD3B7: +case 0xD5B7: +case 0xD7B7: +case 0xD9B7: +case 0xDBB7: +case 0xDDB7: +case 0xDFB7: + +// ADDDa +case 0xD1B0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(26) +case 0xD3B8: +case 0xD5B8: +case 0xD7B8: +case 0xD9B8: +case 0xDBB8: +case 0xDDB8: +case 0xDFB8: + +// ADDDa +case 0xD1B8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(24) +case 0xD3B9: +case 0xD5B9: +case 0xD7B9: +case 0xD9B9: +case 0xDBB9: +case 0xDDB9: +case 0xDFB9: + +// ADDDa +case 0xD1B9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(28) +case 0xD39F: +case 0xD59F: +case 0xD79F: +case 0xD99F: +case 0xDB9F: +case 0xDD9F: +case 0xDF9F: + +// ADDDa +case 0xD19F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(20) +case 0xD3A7: +case 0xD5A7: +case 0xD7A7: +case 0xD9A7: +case 0xDBA7: +case 0xDDA7: +case 0xDFA7: + +// ADDDa +case 0xD1A7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 9) & 7]; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, dst) + res = dst + src; + CPU->flag_notZ = res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(22) +case 0xD300: +case 0xD500: +case 0xD700: +case 0xD900: +case 0xDB00: +case 0xDD00: +case 0xDF00: +case 0xD101: +case 0xD301: +case 0xD501: +case 0xD701: +case 0xD901: +case 0xDB01: +case 0xDD01: +case 0xDF01: +case 0xD102: +case 0xD302: +case 0xD502: +case 0xD702: +case 0xD902: +case 0xDB02: +case 0xDD02: +case 0xDF02: +case 0xD103: +case 0xD303: +case 0xD503: +case 0xD703: +case 0xD903: +case 0xDB03: +case 0xDD03: +case 0xDF03: +case 0xD104: +case 0xD304: +case 0xD504: +case 0xD704: +case 0xD904: +case 0xDB04: +case 0xDD04: +case 0xDF04: +case 0xD105: +case 0xD305: +case 0xD505: +case 0xD705: +case 0xD905: +case 0xDB05: +case 0xDD05: +case 0xDF05: +case 0xD106: +case 0xD306: +case 0xD506: +case 0xD706: +case 0xD906: +case 0xDB06: +case 0xDD06: +case 0xDF06: +case 0xD107: +case 0xD307: +case 0xD507: +case 0xD707: +case 0xD907: +case 0xDB07: +case 0xDD07: +case 0xDF07: + +// ADDX +case 0xD100: +{ + u32 res; + pointer dst; + pointer src; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + dst = (u8)CPU->D[(Opcode >> 9) & 7]; + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ |= res & 0xFF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD340: +case 0xD540: +case 0xD740: +case 0xD940: +case 0xDB40: +case 0xDD40: +case 0xDF40: +case 0xD141: +case 0xD341: +case 0xD541: +case 0xD741: +case 0xD941: +case 0xDB41: +case 0xDD41: +case 0xDF41: +case 0xD142: +case 0xD342: +case 0xD542: +case 0xD742: +case 0xD942: +case 0xDB42: +case 0xDD42: +case 0xDF42: +case 0xD143: +case 0xD343: +case 0xD543: +case 0xD743: +case 0xD943: +case 0xDB43: +case 0xDD43: +case 0xDF43: +case 0xD144: +case 0xD344: +case 0xD544: +case 0xD744: +case 0xD944: +case 0xDB44: +case 0xDD44: +case 0xDF44: +case 0xD145: +case 0xD345: +case 0xD545: +case 0xD745: +case 0xD945: +case 0xDB45: +case 0xDD45: +case 0xDF45: +case 0xD146: +case 0xD346: +case 0xD546: +case 0xD746: +case 0xD946: +case 0xDB46: +case 0xDD46: +case 0xDF46: +case 0xD147: +case 0xD347: +case 0xD547: +case 0xD747: +case 0xD947: +case 0xDB47: +case 0xDD47: +case 0xDF47: + +// ADDX +case 0xD140: +{ + u32 res; + pointer dst; + pointer src; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + dst = (u16)CPU->D[(Opcode >> 9) & 7]; + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(4) +case 0xD380: +case 0xD580: +case 0xD780: +case 0xD980: +case 0xDB80: +case 0xDD80: +case 0xDF80: +case 0xD181: +case 0xD381: +case 0xD581: +case 0xD781: +case 0xD981: +case 0xDB81: +case 0xDD81: +case 0xDF81: +case 0xD182: +case 0xD382: +case 0xD582: +case 0xD782: +case 0xD982: +case 0xDB82: +case 0xDD82: +case 0xDF82: +case 0xD183: +case 0xD383: +case 0xD583: +case 0xD783: +case 0xD983: +case 0xDB83: +case 0xDD83: +case 0xDF83: +case 0xD184: +case 0xD384: +case 0xD584: +case 0xD784: +case 0xD984: +case 0xDB84: +case 0xDD84: +case 0xDF84: +case 0xD185: +case 0xD385: +case 0xD585: +case 0xD785: +case 0xD985: +case 0xDB85: +case 0xDD85: +case 0xDF85: +case 0xD186: +case 0xD386: +case 0xD586: +case 0xD786: +case 0xD986: +case 0xDB86: +case 0xDD86: +case 0xDF86: +case 0xD187: +case 0xD387: +case 0xD587: +case 0xD787: +case 0xD987: +case 0xDB87: +case 0xDD87: +case 0xDF87: + +// ADDX +case 0xD180: +{ + u32 res; + pointer dst; + pointer src; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->D[(Opcode >> 9) & 7]; + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + *((u32*)(&CPU->D[(Opcode >> 9) & 7])) = res; +} +RET(8) +case 0xD308: +case 0xD508: +case 0xD708: +case 0xD908: +case 0xDB08: +case 0xDD08: +case 0xD109: +case 0xD309: +case 0xD509: +case 0xD709: +case 0xD909: +case 0xDB09: +case 0xDD09: +case 0xD10A: +case 0xD30A: +case 0xD50A: +case 0xD70A: +case 0xD90A: +case 0xDB0A: +case 0xDD0A: +case 0xD10B: +case 0xD30B: +case 0xD50B: +case 0xD70B: +case 0xD90B: +case 0xDB0B: +case 0xDD0B: +case 0xD10C: +case 0xD30C: +case 0xD50C: +case 0xD70C: +case 0xD90C: +case 0xDB0C: +case 0xDD0C: +case 0xD10D: +case 0xD30D: +case 0xD50D: +case 0xD70D: +case 0xD90D: +case 0xDB0D: +case 0xDD0D: +case 0xD10E: +case 0xD30E: +case 0xD50E: +case 0xD70E: +case 0xD90E: +case 0xDB0E: +case 0xDD0E: + +// ADDXM +case 0xD108: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xD348: +case 0xD548: +case 0xD748: +case 0xD948: +case 0xDB48: +case 0xDD48: +case 0xD149: +case 0xD349: +case 0xD549: +case 0xD749: +case 0xD949: +case 0xDB49: +case 0xDD49: +case 0xD14A: +case 0xD34A: +case 0xD54A: +case 0xD74A: +case 0xD94A: +case 0xDB4A: +case 0xDD4A: +case 0xD14B: +case 0xD34B: +case 0xD54B: +case 0xD74B: +case 0xD94B: +case 0xDB4B: +case 0xDD4B: +case 0xD14C: +case 0xD34C: +case 0xD54C: +case 0xD74C: +case 0xD94C: +case 0xDB4C: +case 0xDD4C: +case 0xD14D: +case 0xD34D: +case 0xD54D: +case 0xD74D: +case 0xD94D: +case 0xDB4D: +case 0xDD4D: +case 0xD14E: +case 0xD34E: +case 0xD54E: +case 0xD74E: +case 0xD94E: +case 0xDB4E: +case 0xDD4E: + +// ADDXM +case 0xD148: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_WORD_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xD388: +case 0xD588: +case 0xD788: +case 0xD988: +case 0xDB88: +case 0xDD88: +case 0xD189: +case 0xD389: +case 0xD589: +case 0xD789: +case 0xD989: +case 0xDB89: +case 0xDD89: +case 0xD18A: +case 0xD38A: +case 0xD58A: +case 0xD78A: +case 0xD98A: +case 0xDB8A: +case 0xDD8A: +case 0xD18B: +case 0xD38B: +case 0xD58B: +case 0xD78B: +case 0xD98B: +case 0xDB8B: +case 0xDD8B: +case 0xD18C: +case 0xD38C: +case 0xD58C: +case 0xD78C: +case 0xD98C: +case 0xDB8C: +case 0xDD8C: +case 0xD18D: +case 0xD38D: +case 0xD58D: +case 0xD78D: +case 0xD98D: +case 0xDB8D: +case 0xDD8D: +case 0xD18E: +case 0xD38E: +case 0xD58E: +case 0xD78E: +case 0xD98E: +case 0xDB8E: +case 0xDD8E: + +// ADDXM +case 0xD188: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_LONG_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0xD30F: +case 0xD50F: +case 0xD70F: +case 0xD90F: +case 0xDB0F: +case 0xDD0F: + +// ADDX7M +case 0xD10F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 1; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_BYTE_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xD34F: +case 0xD54F: +case 0xD74F: +case 0xD94F: +case 0xDB4F: +case 0xDD4F: + +// ADDX7M +case 0xD14F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 2; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_WORD_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xD38F: +case 0xD58F: +case 0xD78F: +case 0xD98F: +case 0xDB8F: +case 0xDD8F: + +// ADDX7M +case 0xD18F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[(Opcode >> 9) & 7] - 4; + CPU->A[(Opcode >> 9) & 7] = adr; + READ_LONG_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0xDF09: +case 0xDF0A: +case 0xDF0B: +case 0xDF0C: +case 0xDF0D: +case 0xDF0E: + +// ADDXM7 +case 0xDF08: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 1; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) +case 0xDF49: +case 0xDF4A: +case 0xDF4B: +case 0xDF4C: +case 0xDF4D: +case 0xDF4E: + +// ADDXM7 +case 0xDF48: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_WORD_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) +case 0xDF89: +case 0xDF8A: +case 0xDF8B: +case 0xDF8C: +case 0xDF8D: +case 0xDF8E: + +// ADDXM7 +case 0xDF88: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + READ_LONG_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) + +// ADDX7M7 +case 0xDF0F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_BYTE_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_BYTE_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_N = CPU->flag_X = CPU->flag_C = res; + CPU->flag_V = (src ^ res) & (dst ^ res); + CPU->flag_notZ |= res & 0xFF; + WRITE_BYTE_F(adr, res) + POST_IO +} +RET(18) + +// ADDX7M7 +case 0xDF4F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + READ_WORD_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8; + CPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_notZ |= res & 0xFFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ADDX7M7 +case 0xDF8F: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READ_LONG_F(adr, src) + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + READ_LONG_F(adr, dst) + res = dst + src + ((CPU->flag_X >> 8) & 1); + CPU->flag_notZ |= res; + CPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23; + CPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24; + CPU->flag_N = res >> 24; + WRITE_LONG_F(adr, res) + POST_IO +} +RET(30) +case 0xD2C0: +case 0xD4C0: +case 0xD6C0: +case 0xD8C0: +case 0xDAC0: +case 0xDCC0: +case 0xDEC0: +case 0xD0C1: +case 0xD2C1: +case 0xD4C1: +case 0xD6C1: +case 0xD8C1: +case 0xDAC1: +case 0xDCC1: +case 0xDEC1: +case 0xD0C2: +case 0xD2C2: +case 0xD4C2: +case 0xD6C2: +case 0xD8C2: +case 0xDAC2: +case 0xDCC2: +case 0xDEC2: +case 0xD0C3: +case 0xD2C3: +case 0xD4C3: +case 0xD6C3: +case 0xD8C3: +case 0xDAC3: +case 0xDCC3: +case 0xDEC3: +case 0xD0C4: +case 0xD2C4: +case 0xD4C4: +case 0xD6C4: +case 0xD8C4: +case 0xDAC4: +case 0xDCC4: +case 0xDEC4: +case 0xD0C5: +case 0xD2C5: +case 0xD4C5: +case 0xD6C5: +case 0xD8C5: +case 0xDAC5: +case 0xDCC5: +case 0xDEC5: +case 0xD0C6: +case 0xD2C6: +case 0xD4C6: +case 0xD6C6: +case 0xD8C6: +case 0xDAC6: +case 0xDCC6: +case 0xDEC6: +case 0xD0C7: +case 0xD2C7: +case 0xD4C7: +case 0xD6C7: +case 0xD8C7: +case 0xDAC7: +case 0xDCC7: +case 0xDEC7: + +// ADDA +case 0xD0C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0xD2C8: +case 0xD4C8: +case 0xD6C8: +case 0xD8C8: +case 0xDAC8: +case 0xDCC8: +case 0xDEC8: +case 0xD0C9: +case 0xD2C9: +case 0xD4C9: +case 0xD6C9: +case 0xD8C9: +case 0xDAC9: +case 0xDCC9: +case 0xDEC9: +case 0xD0CA: +case 0xD2CA: +case 0xD4CA: +case 0xD6CA: +case 0xD8CA: +case 0xDACA: +case 0xDCCA: +case 0xDECA: +case 0xD0CB: +case 0xD2CB: +case 0xD4CB: +case 0xD6CB: +case 0xD8CB: +case 0xDACB: +case 0xDCCB: +case 0xDECB: +case 0xD0CC: +case 0xD2CC: +case 0xD4CC: +case 0xD6CC: +case 0xD8CC: +case 0xDACC: +case 0xDCCC: +case 0xDECC: +case 0xD0CD: +case 0xD2CD: +case 0xD4CD: +case 0xD6CD: +case 0xD8CD: +case 0xDACD: +case 0xDCCD: +case 0xDECD: +case 0xD0CE: +case 0xD2CE: +case 0xD4CE: +case 0xD6CE: +case 0xD8CE: +case 0xDACE: +case 0xDCCE: +case 0xDECE: +case 0xD0CF: +case 0xD2CF: +case 0xD4CF: +case 0xD6CF: +case 0xD8CF: +case 0xDACF: +case 0xDCCF: +case 0xDECF: + +// ADDA +case 0xD0C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(8) +case 0xD2D0: +case 0xD4D0: +case 0xD6D0: +case 0xD8D0: +case 0xDAD0: +case 0xDCD0: +case 0xDED0: +case 0xD0D1: +case 0xD2D1: +case 0xD4D1: +case 0xD6D1: +case 0xD8D1: +case 0xDAD1: +case 0xDCD1: +case 0xDED1: +case 0xD0D2: +case 0xD2D2: +case 0xD4D2: +case 0xD6D2: +case 0xD8D2: +case 0xDAD2: +case 0xDCD2: +case 0xDED2: +case 0xD0D3: +case 0xD2D3: +case 0xD4D3: +case 0xD6D3: +case 0xD8D3: +case 0xDAD3: +case 0xDCD3: +case 0xDED3: +case 0xD0D4: +case 0xD2D4: +case 0xD4D4: +case 0xD6D4: +case 0xD8D4: +case 0xDAD4: +case 0xDCD4: +case 0xDED4: +case 0xD0D5: +case 0xD2D5: +case 0xD4D5: +case 0xD6D5: +case 0xD8D5: +case 0xDAD5: +case 0xDCD5: +case 0xDED5: +case 0xD0D6: +case 0xD2D6: +case 0xD4D6: +case 0xD6D6: +case 0xD8D6: +case 0xDAD6: +case 0xDCD6: +case 0xDED6: +case 0xD0D7: +case 0xD2D7: +case 0xD4D7: +case 0xD6D7: +case 0xD8D7: +case 0xDAD7: +case 0xDCD7: +case 0xDED7: + +// ADDA +case 0xD0D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0xD2D8: +case 0xD4D8: +case 0xD6D8: +case 0xD8D8: +case 0xDAD8: +case 0xDCD8: +case 0xDED8: +case 0xD0D9: +case 0xD2D9: +case 0xD4D9: +case 0xD6D9: +case 0xD8D9: +case 0xDAD9: +case 0xDCD9: +case 0xDED9: +case 0xD0DA: +case 0xD2DA: +case 0xD4DA: +case 0xD6DA: +case 0xD8DA: +case 0xDADA: +case 0xDCDA: +case 0xDEDA: +case 0xD0DB: +case 0xD2DB: +case 0xD4DB: +case 0xD6DB: +case 0xD8DB: +case 0xDADB: +case 0xDCDB: +case 0xDEDB: +case 0xD0DC: +case 0xD2DC: +case 0xD4DC: +case 0xD6DC: +case 0xD8DC: +case 0xDADC: +case 0xDCDC: +case 0xDEDC: +case 0xD0DD: +case 0xD2DD: +case 0xD4DD: +case 0xD6DD: +case 0xD8DD: +case 0xDADD: +case 0xDCDD: +case 0xDEDD: +case 0xD0DE: +case 0xD2DE: +case 0xD4DE: +case 0xD6DE: +case 0xD8DE: +case 0xDADE: +case 0xDCDE: +case 0xDEDE: + +// ADDA +case 0xD0D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0xD2E0: +case 0xD4E0: +case 0xD6E0: +case 0xD8E0: +case 0xDAE0: +case 0xDCE0: +case 0xDEE0: +case 0xD0E1: +case 0xD2E1: +case 0xD4E1: +case 0xD6E1: +case 0xD8E1: +case 0xDAE1: +case 0xDCE1: +case 0xDEE1: +case 0xD0E2: +case 0xD2E2: +case 0xD4E2: +case 0xD6E2: +case 0xD8E2: +case 0xDAE2: +case 0xDCE2: +case 0xDEE2: +case 0xD0E3: +case 0xD2E3: +case 0xD4E3: +case 0xD6E3: +case 0xD8E3: +case 0xDAE3: +case 0xDCE3: +case 0xDEE3: +case 0xD0E4: +case 0xD2E4: +case 0xD4E4: +case 0xD6E4: +case 0xD8E4: +case 0xDAE4: +case 0xDCE4: +case 0xDEE4: +case 0xD0E5: +case 0xD2E5: +case 0xD4E5: +case 0xD6E5: +case 0xD8E5: +case 0xDAE5: +case 0xDCE5: +case 0xDEE5: +case 0xD0E6: +case 0xD2E6: +case 0xD4E6: +case 0xD6E6: +case 0xD8E6: +case 0xDAE6: +case 0xDCE6: +case 0xDEE6: + +// ADDA +case 0xD0E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0xD2E8: +case 0xD4E8: +case 0xD6E8: +case 0xD8E8: +case 0xDAE8: +case 0xDCE8: +case 0xDEE8: +case 0xD0E9: +case 0xD2E9: +case 0xD4E9: +case 0xD6E9: +case 0xD8E9: +case 0xDAE9: +case 0xDCE9: +case 0xDEE9: +case 0xD0EA: +case 0xD2EA: +case 0xD4EA: +case 0xD6EA: +case 0xD8EA: +case 0xDAEA: +case 0xDCEA: +case 0xDEEA: +case 0xD0EB: +case 0xD2EB: +case 0xD4EB: +case 0xD6EB: +case 0xD8EB: +case 0xDAEB: +case 0xDCEB: +case 0xDEEB: +case 0xD0EC: +case 0xD2EC: +case 0xD4EC: +case 0xD6EC: +case 0xD8EC: +case 0xDAEC: +case 0xDCEC: +case 0xDEEC: +case 0xD0ED: +case 0xD2ED: +case 0xD4ED: +case 0xD6ED: +case 0xD8ED: +case 0xDAED: +case 0xDCED: +case 0xDEED: +case 0xD0EE: +case 0xD2EE: +case 0xD4EE: +case 0xD6EE: +case 0xD8EE: +case 0xDAEE: +case 0xDCEE: +case 0xDEEE: +case 0xD0EF: +case 0xD2EF: +case 0xD4EF: +case 0xD6EF: +case 0xD8EF: +case 0xDAEF: +case 0xDCEF: +case 0xDEEF: + +// ADDA +case 0xD0E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD2F0: +case 0xD4F0: +case 0xD6F0: +case 0xD8F0: +case 0xDAF0: +case 0xDCF0: +case 0xDEF0: +case 0xD0F1: +case 0xD2F1: +case 0xD4F1: +case 0xD6F1: +case 0xD8F1: +case 0xDAF1: +case 0xDCF1: +case 0xDEF1: +case 0xD0F2: +case 0xD2F2: +case 0xD4F2: +case 0xD6F2: +case 0xD8F2: +case 0xDAF2: +case 0xDCF2: +case 0xDEF2: +case 0xD0F3: +case 0xD2F3: +case 0xD4F3: +case 0xD6F3: +case 0xD8F3: +case 0xDAF3: +case 0xDCF3: +case 0xDEF3: +case 0xD0F4: +case 0xD2F4: +case 0xD4F4: +case 0xD6F4: +case 0xD8F4: +case 0xDAF4: +case 0xDCF4: +case 0xDEF4: +case 0xD0F5: +case 0xD2F5: +case 0xD4F5: +case 0xD6F5: +case 0xD8F5: +case 0xDAF5: +case 0xDCF5: +case 0xDEF5: +case 0xD0F6: +case 0xD2F6: +case 0xD4F6: +case 0xD6F6: +case 0xD8F6: +case 0xDAF6: +case 0xDCF6: +case 0xDEF6: +case 0xD0F7: +case 0xD2F7: +case 0xD4F7: +case 0xD6F7: +case 0xD8F7: +case 0xDAF7: +case 0xDCF7: +case 0xDEF7: + +// ADDA +case 0xD0F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0xD2F8: +case 0xD4F8: +case 0xD6F8: +case 0xD8F8: +case 0xDAF8: +case 0xDCF8: +case 0xDEF8: + +// ADDA +case 0xD0F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD2F9: +case 0xD4F9: +case 0xD6F9: +case 0xD8F9: +case 0xDAF9: +case 0xDCF9: +case 0xDEF9: + +// ADDA +case 0xD0F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0xD2FA: +case 0xD4FA: +case 0xD6FA: +case 0xD8FA: +case 0xDAFA: +case 0xDCFA: +case 0xDEFA: + +// ADDA +case 0xD0FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD2FB: +case 0xD4FB: +case 0xD6FB: +case 0xD8FB: +case 0xDAFB: +case 0xDCFB: +case 0xDEFB: + +// ADDA +case 0xD0FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0xD2FC: +case 0xD4FC: +case 0xD6FC: +case 0xD8FC: +case 0xDAFC: +case 0xDCFC: +case 0xDEFC: + +// ADDA +case 0xD0FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s16)FETCH_WORD; + PC += 2; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(12) +case 0xD2DF: +case 0xD4DF: +case 0xD6DF: +case 0xD8DF: +case 0xDADF: +case 0xDCDF: +case 0xDEDF: + +// ADDA +case 0xD0DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(12) +case 0xD2E7: +case 0xD4E7: +case 0xD6E7: +case 0xD8E7: +case 0xDAE7: +case 0xDCE7: +case 0xDEE7: + +// ADDA +case 0xD0E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READSX_WORD_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(14) +case 0xD3C0: +case 0xD5C0: +case 0xD7C0: +case 0xD9C0: +case 0xDBC0: +case 0xDDC0: +case 0xDFC0: +case 0xD1C1: +case 0xD3C1: +case 0xD5C1: +case 0xD7C1: +case 0xD9C1: +case 0xDBC1: +case 0xDDC1: +case 0xDFC1: +case 0xD1C2: +case 0xD3C2: +case 0xD5C2: +case 0xD7C2: +case 0xD9C2: +case 0xDBC2: +case 0xDDC2: +case 0xDFC2: +case 0xD1C3: +case 0xD3C3: +case 0xD5C3: +case 0xD7C3: +case 0xD9C3: +case 0xDBC3: +case 0xDDC3: +case 0xDFC3: +case 0xD1C4: +case 0xD3C4: +case 0xD5C4: +case 0xD7C4: +case 0xD9C4: +case 0xDBC4: +case 0xDDC4: +case 0xDFC4: +case 0xD1C5: +case 0xD3C5: +case 0xD5C5: +case 0xD7C5: +case 0xD9C5: +case 0xDBC5: +case 0xDDC5: +case 0xDFC5: +case 0xD1C6: +case 0xD3C6: +case 0xD5C6: +case 0xD7C6: +case 0xD9C6: +case 0xDBC6: +case 0xDDC6: +case 0xDFC6: +case 0xD1C7: +case 0xD3C7: +case 0xD5C7: +case 0xD7C7: +case 0xD9C7: +case 0xDBC7: +case 0xDDC7: +case 0xDFC7: + +// ADDA +case 0xD1C0: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(6) +case 0xD3C8: +case 0xD5C8: +case 0xD7C8: +case 0xD9C8: +case 0xDBC8: +case 0xDDC8: +case 0xDFC8: +case 0xD1C9: +case 0xD3C9: +case 0xD5C9: +case 0xD7C9: +case 0xD9C9: +case 0xDBC9: +case 0xDDC9: +case 0xDFC9: +case 0xD1CA: +case 0xD3CA: +case 0xD5CA: +case 0xD7CA: +case 0xD9CA: +case 0xDBCA: +case 0xDDCA: +case 0xDFCA: +case 0xD1CB: +case 0xD3CB: +case 0xD5CB: +case 0xD7CB: +case 0xD9CB: +case 0xDBCB: +case 0xDDCB: +case 0xDFCB: +case 0xD1CC: +case 0xD3CC: +case 0xD5CC: +case 0xD7CC: +case 0xD9CC: +case 0xDBCC: +case 0xDDCC: +case 0xDFCC: +case 0xD1CD: +case 0xD3CD: +case 0xD5CD: +case 0xD7CD: +case 0xD9CD: +case 0xDBCD: +case 0xDDCD: +case 0xDFCD: +case 0xD1CE: +case 0xD3CE: +case 0xD5CE: +case 0xD7CE: +case 0xD9CE: +case 0xDBCE: +case 0xDDCE: +case 0xDFCE: +case 0xD1CF: +case 0xD3CF: +case 0xD5CF: +case 0xD7CF: +case 0xD9CF: +case 0xDBCF: +case 0xDDCF: +case 0xDFCF: + +// ADDA +case 0xD1C8: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)CPU->A[(Opcode >> 0) & 7]; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(6) +case 0xD3D0: +case 0xD5D0: +case 0xD7D0: +case 0xD9D0: +case 0xDBD0: +case 0xDDD0: +case 0xDFD0: +case 0xD1D1: +case 0xD3D1: +case 0xD5D1: +case 0xD7D1: +case 0xD9D1: +case 0xDBD1: +case 0xDDD1: +case 0xDFD1: +case 0xD1D2: +case 0xD3D2: +case 0xD5D2: +case 0xD7D2: +case 0xD9D2: +case 0xDBD2: +case 0xDDD2: +case 0xDFD2: +case 0xD1D3: +case 0xD3D3: +case 0xD5D3: +case 0xD7D3: +case 0xD9D3: +case 0xDBD3: +case 0xDDD3: +case 0xDFD3: +case 0xD1D4: +case 0xD3D4: +case 0xD5D4: +case 0xD7D4: +case 0xD9D4: +case 0xDBD4: +case 0xDDD4: +case 0xDFD4: +case 0xD1D5: +case 0xD3D5: +case 0xD5D5: +case 0xD7D5: +case 0xD9D5: +case 0xDBD5: +case 0xDDD5: +case 0xDFD5: +case 0xD1D6: +case 0xD3D6: +case 0xD5D6: +case 0xD7D6: +case 0xD9D6: +case 0xDBD6: +case 0xDDD6: +case 0xDFD6: +case 0xD1D7: +case 0xD3D7: +case 0xD5D7: +case 0xD7D7: +case 0xD9D7: +case 0xDBD7: +case 0xDDD7: +case 0xDFD7: + +// ADDA +case 0xD1D0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD3D8: +case 0xD5D8: +case 0xD7D8: +case 0xD9D8: +case 0xDBD8: +case 0xDDD8: +case 0xDFD8: +case 0xD1D9: +case 0xD3D9: +case 0xD5D9: +case 0xD7D9: +case 0xD9D9: +case 0xDBD9: +case 0xDDD9: +case 0xDFD9: +case 0xD1DA: +case 0xD3DA: +case 0xD5DA: +case 0xD7DA: +case 0xD9DA: +case 0xDBDA: +case 0xDDDA: +case 0xDFDA: +case 0xD1DB: +case 0xD3DB: +case 0xD5DB: +case 0xD7DB: +case 0xD9DB: +case 0xDBDB: +case 0xDDDB: +case 0xDFDB: +case 0xD1DC: +case 0xD3DC: +case 0xD5DC: +case 0xD7DC: +case 0xD9DC: +case 0xDBDC: +case 0xDDDC: +case 0xDFDC: +case 0xD1DD: +case 0xD3DD: +case 0xD5DD: +case 0xD7DD: +case 0xD9DD: +case 0xDBDD: +case 0xDDDD: +case 0xDFDD: +case 0xD1DE: +case 0xD3DE: +case 0xD5DE: +case 0xD7DE: +case 0xD9DE: +case 0xDBDE: +case 0xDDDE: +case 0xDFDE: + +// ADDA +case 0xD1D8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD3E0: +case 0xD5E0: +case 0xD7E0: +case 0xD9E0: +case 0xDBE0: +case 0xDDE0: +case 0xDFE0: +case 0xD1E1: +case 0xD3E1: +case 0xD5E1: +case 0xD7E1: +case 0xD9E1: +case 0xDBE1: +case 0xDDE1: +case 0xDFE1: +case 0xD1E2: +case 0xD3E2: +case 0xD5E2: +case 0xD7E2: +case 0xD9E2: +case 0xDBE2: +case 0xDDE2: +case 0xDFE2: +case 0xD1E3: +case 0xD3E3: +case 0xD5E3: +case 0xD7E3: +case 0xD9E3: +case 0xDBE3: +case 0xDDE3: +case 0xDFE3: +case 0xD1E4: +case 0xD3E4: +case 0xD5E4: +case 0xD7E4: +case 0xD9E4: +case 0xDBE4: +case 0xDDE4: +case 0xDFE4: +case 0xD1E5: +case 0xD3E5: +case 0xD5E5: +case 0xD7E5: +case 0xD9E5: +case 0xDBE5: +case 0xDDE5: +case 0xDFE5: +case 0xD1E6: +case 0xD3E6: +case 0xD5E6: +case 0xD7E6: +case 0xD9E6: +case 0xDBE6: +case 0xDDE6: +case 0xDFE6: + +// ADDA +case 0xD1E0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 4; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) +case 0xD3E8: +case 0xD5E8: +case 0xD7E8: +case 0xD9E8: +case 0xDBE8: +case 0xDDE8: +case 0xDFE8: +case 0xD1E9: +case 0xD3E9: +case 0xD5E9: +case 0xD7E9: +case 0xD9E9: +case 0xDBE9: +case 0xDDE9: +case 0xDFE9: +case 0xD1EA: +case 0xD3EA: +case 0xD5EA: +case 0xD7EA: +case 0xD9EA: +case 0xDBEA: +case 0xDDEA: +case 0xDFEA: +case 0xD1EB: +case 0xD3EB: +case 0xD5EB: +case 0xD7EB: +case 0xD9EB: +case 0xDBEB: +case 0xDDEB: +case 0xDFEB: +case 0xD1EC: +case 0xD3EC: +case 0xD5EC: +case 0xD7EC: +case 0xD9EC: +case 0xDBEC: +case 0xDDEC: +case 0xDFEC: +case 0xD1ED: +case 0xD3ED: +case 0xD5ED: +case 0xD7ED: +case 0xD9ED: +case 0xDBED: +case 0xDDED: +case 0xDFED: +case 0xD1EE: +case 0xD3EE: +case 0xD5EE: +case 0xD7EE: +case 0xD9EE: +case 0xDBEE: +case 0xDDEE: +case 0xDFEE: +case 0xD1EF: +case 0xD3EF: +case 0xD5EF: +case 0xD7EF: +case 0xD9EF: +case 0xDBEF: +case 0xDDEF: +case 0xDFEF: + +// ADDA +case 0xD1E8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0xD3F0: +case 0xD5F0: +case 0xD7F0: +case 0xD9F0: +case 0xDBF0: +case 0xDDF0: +case 0xDFF0: +case 0xD1F1: +case 0xD3F1: +case 0xD5F1: +case 0xD7F1: +case 0xD9F1: +case 0xDBF1: +case 0xDDF1: +case 0xDFF1: +case 0xD1F2: +case 0xD3F2: +case 0xD5F2: +case 0xD7F2: +case 0xD9F2: +case 0xDBF2: +case 0xDDF2: +case 0xDFF2: +case 0xD1F3: +case 0xD3F3: +case 0xD5F3: +case 0xD7F3: +case 0xD9F3: +case 0xDBF3: +case 0xDDF3: +case 0xDFF3: +case 0xD1F4: +case 0xD3F4: +case 0xD5F4: +case 0xD7F4: +case 0xD9F4: +case 0xDBF4: +case 0xDDF4: +case 0xDFF4: +case 0xD1F5: +case 0xD3F5: +case 0xD5F5: +case 0xD7F5: +case 0xD9F5: +case 0xDBF5: +case 0xDDF5: +case 0xDFF5: +case 0xD1F6: +case 0xD3F6: +case 0xD5F6: +case 0xD7F6: +case 0xD9F6: +case 0xDBF6: +case 0xDDF6: +case 0xDFF6: +case 0xD1F7: +case 0xD3F7: +case 0xD5F7: +case 0xD7F7: +case 0xD9F7: +case 0xDBF7: +case 0xDDF7: +case 0xDFF7: + +// ADDA +case 0xD1F0: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(22) +case 0xD3F8: +case 0xD5F8: +case 0xD7F8: +case 0xD9F8: +case 0xDBF8: +case 0xDDF8: +case 0xDFF8: + +// ADDA +case 0xD1F8: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0xD3F9: +case 0xD5F9: +case 0xD7F9: +case 0xD9F9: +case 0xDBF9: +case 0xDDF9: +case 0xDFF9: + +// ADDA +case 0xD1F9: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(24) +case 0xD3FA: +case 0xD5FA: +case 0xD7FA: +case 0xD9FA: +case 0xDBFA: +case 0xDDFA: +case 0xDFFA: + +// ADDA +case 0xD1FA: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(20) +case 0xD3FB: +case 0xD5FB: +case 0xD7FB: +case 0xD9FB: +case 0xDBFB: +case 0xDDFB: +case 0xDFFB: + +// ADDA +case 0xD1FB: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = PC - CPU->BasePC; + DECODE_EXT_WORD + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(22) +case 0xD3FC: +case 0xD5FC: +case 0xD7FC: +case 0xD9FC: +case 0xDBFC: +case 0xDDFC: +case 0xDFFC: + +// ADDA +case 0xD1FC: +{ + u32 res; + pointer dst; + pointer src; + src = (s32)(s32)FETCH_LONG; + PC += 4; + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; +} +RET(14) +case 0xD3DF: +case 0xD5DF: +case 0xD7DF: +case 0xD9DF: +case 0xDBDF: +case 0xDDDF: +case 0xDFDF: + +// ADDA +case 0xD1DF: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 4; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(16) +case 0xD3E7: +case 0xD5E7: +case 0xD7E7: +case 0xD9E7: +case 0xDBE7: +case 0xDDE7: +case 0xDFE7: + +// ADDA +case 0xD1E7: +{ + u32 adr; + u32 res; + pointer dst; + pointer src; + adr = CPU->A[7] - 4; + CPU->A[7] = adr; + PRE_IO + READSX_LONG_F(adr, src) + dst = (u32)CPU->A[(Opcode >> 9) & 7]; + res = dst + src; + CPU->A[(Opcode >> 9) & 7] = res; + POST_IO +} +RET(18) diff --git a/yabause/src/c68k/c68k_opE.inc b/yabause/src/c68k/c68k_opE.inc new file mode 100644 index 0000000000..29c55772fd --- /dev/null +++ b/yabause/src/c68k/c68k_opE.inc @@ -0,0 +1,6130 @@ +case 0xE200: +case 0xE400: +case 0xE600: +case 0xE800: +case 0xEA00: +case 0xEC00: +case 0xEE00: +case 0xE001: +case 0xE201: +case 0xE401: +case 0xE601: +case 0xE801: +case 0xEA01: +case 0xEC01: +case 0xEE01: +case 0xE002: +case 0xE202: +case 0xE402: +case 0xE602: +case 0xE802: +case 0xEA02: +case 0xEC02: +case 0xEE02: +case 0xE003: +case 0xE203: +case 0xE403: +case 0xE603: +case 0xE803: +case 0xEA03: +case 0xEC03: +case 0xEE03: +case 0xE004: +case 0xE204: +case 0xE404: +case 0xE604: +case 0xE804: +case 0xEA04: +case 0xEC04: +case 0xEE04: +case 0xE005: +case 0xE205: +case 0xE405: +case 0xE605: +case 0xE805: +case 0xEA05: +case 0xEC05: +case 0xEE05: +case 0xE006: +case 0xE206: +case 0xE406: +case 0xE606: +case 0xE806: +case 0xEA06: +case 0xEC06: +case 0xEE06: +case 0xE007: +case 0xE207: +case 0xE407: +case 0xE607: +case 0xE807: +case 0xEA07: +case 0xEC07: +case 0xEE07: + +// ASRk +case 0xE000: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (s32)(s8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = ((s32)src) >> sft; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE240: +case 0xE440: +case 0xE640: +case 0xE840: +case 0xEA40: +case 0xEC40: +case 0xEE40: +case 0xE041: +case 0xE241: +case 0xE441: +case 0xE641: +case 0xE841: +case 0xEA41: +case 0xEC41: +case 0xEE41: +case 0xE042: +case 0xE242: +case 0xE442: +case 0xE642: +case 0xE842: +case 0xEA42: +case 0xEC42: +case 0xEE42: +case 0xE043: +case 0xE243: +case 0xE443: +case 0xE643: +case 0xE843: +case 0xEA43: +case 0xEC43: +case 0xEE43: +case 0xE044: +case 0xE244: +case 0xE444: +case 0xE644: +case 0xE844: +case 0xEA44: +case 0xEC44: +case 0xEE44: +case 0xE045: +case 0xE245: +case 0xE445: +case 0xE645: +case 0xE845: +case 0xEA45: +case 0xEC45: +case 0xEE45: +case 0xE046: +case 0xE246: +case 0xE446: +case 0xE646: +case 0xE846: +case 0xEA46: +case 0xEC46: +case 0xEE46: +case 0xE047: +case 0xE247: +case 0xE447: +case 0xE647: +case 0xE847: +case 0xEA47: +case 0xEC47: +case 0xEE47: + +// ASRk +case 0xE040: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = ((s32)src) >> sft; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE280: +case 0xE480: +case 0xE680: +case 0xE880: +case 0xEA80: +case 0xEC80: +case 0xEE80: +case 0xE081: +case 0xE281: +case 0xE481: +case 0xE681: +case 0xE881: +case 0xEA81: +case 0xEC81: +case 0xEE81: +case 0xE082: +case 0xE282: +case 0xE482: +case 0xE682: +case 0xE882: +case 0xEA82: +case 0xEC82: +case 0xEE82: +case 0xE083: +case 0xE283: +case 0xE483: +case 0xE683: +case 0xE883: +case 0xEA83: +case 0xEC83: +case 0xEE83: +case 0xE084: +case 0xE284: +case 0xE484: +case 0xE684: +case 0xE884: +case 0xEA84: +case 0xEC84: +case 0xEE84: +case 0xE085: +case 0xE285: +case 0xE485: +case 0xE685: +case 0xE885: +case 0xEA85: +case 0xEC85: +case 0xEE85: +case 0xE086: +case 0xE286: +case 0xE486: +case 0xE686: +case 0xE886: +case 0xEA86: +case 0xEC86: +case 0xEE86: +case 0xE087: +case 0xE287: +case 0xE487: +case 0xE687: +case 0xE887: +case 0xEA87: +case 0xEC87: +case 0xEE87: + +// ASRk +case 0xE080: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = ((s32)src) >> sft; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE208: +case 0xE408: +case 0xE608: +case 0xE808: +case 0xEA08: +case 0xEC08: +case 0xEE08: +case 0xE009: +case 0xE209: +case 0xE409: +case 0xE609: +case 0xE809: +case 0xEA09: +case 0xEC09: +case 0xEE09: +case 0xE00A: +case 0xE20A: +case 0xE40A: +case 0xE60A: +case 0xE80A: +case 0xEA0A: +case 0xEC0A: +case 0xEE0A: +case 0xE00B: +case 0xE20B: +case 0xE40B: +case 0xE60B: +case 0xE80B: +case 0xEA0B: +case 0xEC0B: +case 0xEE0B: +case 0xE00C: +case 0xE20C: +case 0xE40C: +case 0xE60C: +case 0xE80C: +case 0xEA0C: +case 0xEC0C: +case 0xEE0C: +case 0xE00D: +case 0xE20D: +case 0xE40D: +case 0xE60D: +case 0xE80D: +case 0xEA0D: +case 0xEC0D: +case 0xEE0D: +case 0xE00E: +case 0xE20E: +case 0xE40E: +case 0xE60E: +case 0xE80E: +case 0xEA0E: +case 0xEC0E: +case 0xEE0E: +case 0xE00F: +case 0xE20F: +case 0xE40F: +case 0xE60F: +case 0xE80F: +case 0xEA0F: +case 0xEC0F: +case 0xEE0F: + +// LSRk +case 0xE008: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = src >> sft; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE248: +case 0xE448: +case 0xE648: +case 0xE848: +case 0xEA48: +case 0xEC48: +case 0xEE48: +case 0xE049: +case 0xE249: +case 0xE449: +case 0xE649: +case 0xE849: +case 0xEA49: +case 0xEC49: +case 0xEE49: +case 0xE04A: +case 0xE24A: +case 0xE44A: +case 0xE64A: +case 0xE84A: +case 0xEA4A: +case 0xEC4A: +case 0xEE4A: +case 0xE04B: +case 0xE24B: +case 0xE44B: +case 0xE64B: +case 0xE84B: +case 0xEA4B: +case 0xEC4B: +case 0xEE4B: +case 0xE04C: +case 0xE24C: +case 0xE44C: +case 0xE64C: +case 0xE84C: +case 0xEA4C: +case 0xEC4C: +case 0xEE4C: +case 0xE04D: +case 0xE24D: +case 0xE44D: +case 0xE64D: +case 0xE84D: +case 0xEA4D: +case 0xEC4D: +case 0xEE4D: +case 0xE04E: +case 0xE24E: +case 0xE44E: +case 0xE64E: +case 0xE84E: +case 0xEA4E: +case 0xEC4E: +case 0xEE4E: +case 0xE04F: +case 0xE24F: +case 0xE44F: +case 0xE64F: +case 0xE84F: +case 0xEA4F: +case 0xEC4F: +case 0xEE4F: + +// LSRk +case 0xE048: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = src >> sft; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE288: +case 0xE488: +case 0xE688: +case 0xE888: +case 0xEA88: +case 0xEC88: +case 0xEE88: +case 0xE089: +case 0xE289: +case 0xE489: +case 0xE689: +case 0xE889: +case 0xEA89: +case 0xEC89: +case 0xEE89: +case 0xE08A: +case 0xE28A: +case 0xE48A: +case 0xE68A: +case 0xE88A: +case 0xEA8A: +case 0xEC8A: +case 0xEE8A: +case 0xE08B: +case 0xE28B: +case 0xE48B: +case 0xE68B: +case 0xE88B: +case 0xEA8B: +case 0xEC8B: +case 0xEE8B: +case 0xE08C: +case 0xE28C: +case 0xE48C: +case 0xE68C: +case 0xE88C: +case 0xEA8C: +case 0xEC8C: +case 0xEE8C: +case 0xE08D: +case 0xE28D: +case 0xE48D: +case 0xE68D: +case 0xE88D: +case 0xEA8D: +case 0xEC8D: +case 0xEE8D: +case 0xE08E: +case 0xE28E: +case 0xE48E: +case 0xE68E: +case 0xE88E: +case 0xEA8E: +case 0xEC8E: +case 0xEE8E: +case 0xE08F: +case 0xE28F: +case 0xE48F: +case 0xE68F: +case 0xE88F: +case 0xEA8F: +case 0xEC8F: +case 0xEE8F: + +// LSRk +case 0xE088: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = src >> sft; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE210: +case 0xE410: +case 0xE610: +case 0xE810: +case 0xEA10: +case 0xEC10: +case 0xEE10: +case 0xE011: +case 0xE211: +case 0xE411: +case 0xE611: +case 0xE811: +case 0xEA11: +case 0xEC11: +case 0xEE11: +case 0xE012: +case 0xE212: +case 0xE412: +case 0xE612: +case 0xE812: +case 0xEA12: +case 0xEC12: +case 0xEE12: +case 0xE013: +case 0xE213: +case 0xE413: +case 0xE613: +case 0xE813: +case 0xEA13: +case 0xEC13: +case 0xEE13: +case 0xE014: +case 0xE214: +case 0xE414: +case 0xE614: +case 0xE814: +case 0xEA14: +case 0xEC14: +case 0xEE14: +case 0xE015: +case 0xE215: +case 0xE415: +case 0xE615: +case 0xE815: +case 0xEA15: +case 0xEC15: +case 0xEE15: +case 0xE016: +case 0xE216: +case 0xE416: +case 0xE616: +case 0xE816: +case 0xEA16: +case 0xEC16: +case 0xEE16: +case 0xE017: +case 0xE217: +case 0xE417: +case 0xE617: +case 0xE817: +case 0xEA17: +case 0xEC17: +case 0xEE17: + +// ROXRk +case 0xE010: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + src |= (CPU->flag_X & C68K_SR_X) << 0; + res = (src >> sft) | (src << (9 - sft)); + CPU->flag_X = CPU->flag_C = res >> 0; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE250: +case 0xE450: +case 0xE650: +case 0xE850: +case 0xEA50: +case 0xEC50: +case 0xEE50: +case 0xE051: +case 0xE251: +case 0xE451: +case 0xE651: +case 0xE851: +case 0xEA51: +case 0xEC51: +case 0xEE51: +case 0xE052: +case 0xE252: +case 0xE452: +case 0xE652: +case 0xE852: +case 0xEA52: +case 0xEC52: +case 0xEE52: +case 0xE053: +case 0xE253: +case 0xE453: +case 0xE653: +case 0xE853: +case 0xEA53: +case 0xEC53: +case 0xEE53: +case 0xE054: +case 0xE254: +case 0xE454: +case 0xE654: +case 0xE854: +case 0xEA54: +case 0xEC54: +case 0xEE54: +case 0xE055: +case 0xE255: +case 0xE455: +case 0xE655: +case 0xE855: +case 0xEA55: +case 0xEC55: +case 0xEE55: +case 0xE056: +case 0xE256: +case 0xE456: +case 0xE656: +case 0xE856: +case 0xEA56: +case 0xEC56: +case 0xEE56: +case 0xE057: +case 0xE257: +case 0xE457: +case 0xE657: +case 0xE857: +case 0xEA57: +case 0xEC57: +case 0xEE57: + +// ROXRk +case 0xE050: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + src |= (CPU->flag_X & C68K_SR_X) << 8; + res = (src >> sft) | (src << (17 - sft)); + CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE290: +case 0xE490: +case 0xE690: +case 0xE890: +case 0xEA90: +case 0xEC90: +case 0xEE90: +case 0xE091: +case 0xE291: +case 0xE491: +case 0xE691: +case 0xE891: +case 0xEA91: +case 0xEC91: +case 0xEE91: +case 0xE092: +case 0xE292: +case 0xE492: +case 0xE692: +case 0xE892: +case 0xEA92: +case 0xEC92: +case 0xEE92: +case 0xE093: +case 0xE293: +case 0xE493: +case 0xE693: +case 0xE893: +case 0xEA93: +case 0xEC93: +case 0xEE93: +case 0xE094: +case 0xE294: +case 0xE494: +case 0xE694: +case 0xE894: +case 0xEA94: +case 0xEC94: +case 0xEE94: +case 0xE095: +case 0xE295: +case 0xE495: +case 0xE695: +case 0xE895: +case 0xEA95: +case 0xEC95: +case 0xEE95: +case 0xE096: +case 0xE296: +case 0xE496: +case 0xE696: +case 0xE896: +case 0xEA96: +case 0xEC96: +case 0xEE96: +case 0xE097: +case 0xE297: +case 0xE497: +case 0xE697: +case 0xE897: +case 0xEA97: +case 0xEC97: +case 0xEE97: + +// ROXRk +case 0xE090: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + if (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1))); + else res = (src >> sft) | (src << (33 - sft)) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + sft))); + CPU->flag_X = CPU->flag_C; + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE218: +case 0xE418: +case 0xE618: +case 0xE818: +case 0xEA18: +case 0xEC18: +case 0xEE18: +case 0xE019: +case 0xE219: +case 0xE419: +case 0xE619: +case 0xE819: +case 0xEA19: +case 0xEC19: +case 0xEE19: +case 0xE01A: +case 0xE21A: +case 0xE41A: +case 0xE61A: +case 0xE81A: +case 0xEA1A: +case 0xEC1A: +case 0xEE1A: +case 0xE01B: +case 0xE21B: +case 0xE41B: +case 0xE61B: +case 0xE81B: +case 0xEA1B: +case 0xEC1B: +case 0xEE1B: +case 0xE01C: +case 0xE21C: +case 0xE41C: +case 0xE61C: +case 0xE81C: +case 0xEA1C: +case 0xEC1C: +case 0xEE1C: +case 0xE01D: +case 0xE21D: +case 0xE41D: +case 0xE61D: +case 0xE81D: +case 0xEA1D: +case 0xEC1D: +case 0xEE1D: +case 0xE01E: +case 0xE21E: +case 0xE41E: +case 0xE61E: +case 0xE81E: +case 0xEA1E: +case 0xEC1E: +case 0xEE1E: +case 0xE01F: +case 0xE21F: +case 0xE41F: +case 0xE61F: +case 0xE81F: +case 0xEA1F: +case 0xEC1F: +case 0xEE1F: + +// RORk +case 0xE018: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = (src >> sft) | (src << (8 - sft)); + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE258: +case 0xE458: +case 0xE658: +case 0xE858: +case 0xEA58: +case 0xEC58: +case 0xEE58: +case 0xE059: +case 0xE259: +case 0xE459: +case 0xE659: +case 0xE859: +case 0xEA59: +case 0xEC59: +case 0xEE59: +case 0xE05A: +case 0xE25A: +case 0xE45A: +case 0xE65A: +case 0xE85A: +case 0xEA5A: +case 0xEC5A: +case 0xEE5A: +case 0xE05B: +case 0xE25B: +case 0xE45B: +case 0xE65B: +case 0xE85B: +case 0xEA5B: +case 0xEC5B: +case 0xEE5B: +case 0xE05C: +case 0xE25C: +case 0xE45C: +case 0xE65C: +case 0xE85C: +case 0xEA5C: +case 0xEC5C: +case 0xEE5C: +case 0xE05D: +case 0xE25D: +case 0xE45D: +case 0xE65D: +case 0xE85D: +case 0xEA5D: +case 0xEC5D: +case 0xEE5D: +case 0xE05E: +case 0xE25E: +case 0xE45E: +case 0xE65E: +case 0xE85E: +case 0xEA5E: +case 0xEC5E: +case 0xEE5E: +case 0xE05F: +case 0xE25F: +case 0xE45F: +case 0xE65F: +case 0xE85F: +case 0xEA5F: +case 0xEC5F: +case 0xEE5F: + +// RORk +case 0xE058: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = (src >> sft) | (src << (16 - sft)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE298: +case 0xE498: +case 0xE698: +case 0xE898: +case 0xEA98: +case 0xEC98: +case 0xEE98: +case 0xE099: +case 0xE299: +case 0xE499: +case 0xE699: +case 0xE899: +case 0xEA99: +case 0xEC99: +case 0xEE99: +case 0xE09A: +case 0xE29A: +case 0xE49A: +case 0xE69A: +case 0xE89A: +case 0xEA9A: +case 0xEC9A: +case 0xEE9A: +case 0xE09B: +case 0xE29B: +case 0xE49B: +case 0xE69B: +case 0xE89B: +case 0xEA9B: +case 0xEC9B: +case 0xEE9B: +case 0xE09C: +case 0xE29C: +case 0xE49C: +case 0xE69C: +case 0xE89C: +case 0xEA9C: +case 0xEC9C: +case 0xEE9C: +case 0xE09D: +case 0xE29D: +case 0xE49D: +case 0xE69D: +case 0xE89D: +case 0xEA9D: +case 0xEC9D: +case 0xEE9D: +case 0xE09E: +case 0xE29E: +case 0xE49E: +case 0xE69E: +case 0xE89E: +case 0xEA9E: +case 0xEC9E: +case 0xEE9E: +case 0xE09F: +case 0xE29F: +case 0xE49F: +case 0xE69F: +case 0xE89F: +case 0xEA9F: +case 0xEC9F: +case 0xEE9F: + +// RORk +case 0xE098: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = (src >> sft) | (src << (32 - sft)); + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE300: +case 0xE500: +case 0xE700: +case 0xE900: +case 0xEB00: +case 0xED00: +case 0xEF00: +case 0xE101: +case 0xE301: +case 0xE501: +case 0xE701: +case 0xE901: +case 0xEB01: +case 0xED01: +case 0xEF01: +case 0xE102: +case 0xE302: +case 0xE502: +case 0xE702: +case 0xE902: +case 0xEB02: +case 0xED02: +case 0xEF02: +case 0xE103: +case 0xE303: +case 0xE503: +case 0xE703: +case 0xE903: +case 0xEB03: +case 0xED03: +case 0xEF03: +case 0xE104: +case 0xE304: +case 0xE504: +case 0xE704: +case 0xE904: +case 0xEB04: +case 0xED04: +case 0xEF04: +case 0xE105: +case 0xE305: +case 0xE505: +case 0xE705: +case 0xE905: +case 0xEB05: +case 0xED05: +case 0xEF05: +case 0xE106: +case 0xE306: +case 0xE506: +case 0xE706: +case 0xE906: +case 0xEB06: +case 0xED06: +case 0xEF06: +case 0xE107: +case 0xE307: +case 0xE507: +case 0xE707: +case 0xE907: +case 0xEB07: +case 0xED07: +case 0xEF07: + +// ASLk +case 0xE100: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft < 8) + { + CPU->flag_X = CPU->flag_C = src << (0 + sft); + res = src << sft; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + if ((sft > 7) && (src)) CPU->flag_V = C68K_SR_V; + else + { + u32 msk = (((s32)0x80000000) >> (sft + 24)) & 0x000000FF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } + RET(6) + } + + if (src) CPU->flag_V = C68K_SR_V; + else CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_N = 0; + CPU->flag_notZ = 0; +} +RET(6) +case 0xE340: +case 0xE540: +case 0xE740: +case 0xE940: +case 0xEB40: +case 0xED40: +case 0xEF40: +case 0xE141: +case 0xE341: +case 0xE541: +case 0xE741: +case 0xE941: +case 0xEB41: +case 0xED41: +case 0xEF41: +case 0xE142: +case 0xE342: +case 0xE542: +case 0xE742: +case 0xE942: +case 0xEB42: +case 0xED42: +case 0xEF42: +case 0xE143: +case 0xE343: +case 0xE543: +case 0xE743: +case 0xE943: +case 0xEB43: +case 0xED43: +case 0xEF43: +case 0xE144: +case 0xE344: +case 0xE544: +case 0xE744: +case 0xE944: +case 0xEB44: +case 0xED44: +case 0xEF44: +case 0xE145: +case 0xE345: +case 0xE545: +case 0xE745: +case 0xE945: +case 0xEB45: +case 0xED45: +case 0xEF45: +case 0xE146: +case 0xE346: +case 0xE546: +case 0xE746: +case 0xE946: +case 0xEB46: +case 0xED46: +case 0xEF46: +case 0xE147: +case 0xE347: +case 0xE547: +case 0xE747: +case 0xE947: +case 0xEB47: +case 0xED47: +case 0xEF47: + +// ASLk +case 0xE140: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_X = CPU->flag_C = src >> (8 - sft); + res = src << sft; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + { + u32 msk = (((s32)0x80000000) >> (sft + 16)) & 0x0000FFFF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } +} +RET(6) +case 0xE380: +case 0xE580: +case 0xE780: +case 0xE980: +case 0xEB80: +case 0xED80: +case 0xEF80: +case 0xE181: +case 0xE381: +case 0xE581: +case 0xE781: +case 0xE981: +case 0xEB81: +case 0xED81: +case 0xEF81: +case 0xE182: +case 0xE382: +case 0xE582: +case 0xE782: +case 0xE982: +case 0xEB82: +case 0xED82: +case 0xEF82: +case 0xE183: +case 0xE383: +case 0xE583: +case 0xE783: +case 0xE983: +case 0xEB83: +case 0xED83: +case 0xEF83: +case 0xE184: +case 0xE384: +case 0xE584: +case 0xE784: +case 0xE984: +case 0xEB84: +case 0xED84: +case 0xEF84: +case 0xE185: +case 0xE385: +case 0xE585: +case 0xE785: +case 0xE985: +case 0xEB85: +case 0xED85: +case 0xEF85: +case 0xE186: +case 0xE386: +case 0xE586: +case 0xE786: +case 0xE986: +case 0xEB86: +case 0xED86: +case 0xEF86: +case 0xE187: +case 0xE387: +case 0xE587: +case 0xE787: +case 0xE987: +case 0xEB87: +case 0xED87: +case 0xEF87: + +// ASLk +case 0xE180: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_X = CPU->flag_C = src >> (24 - sft); + res = src << sft; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res & 0xFFFFFFFF; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + { + u32 msk = (((s32)0x80000000) >> (sft + 0)) & 0xFFFFFFFF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } +} +RET(8) +case 0xE308: +case 0xE508: +case 0xE708: +case 0xE908: +case 0xEB08: +case 0xED08: +case 0xEF08: +case 0xE109: +case 0xE309: +case 0xE509: +case 0xE709: +case 0xE909: +case 0xEB09: +case 0xED09: +case 0xEF09: +case 0xE10A: +case 0xE30A: +case 0xE50A: +case 0xE70A: +case 0xE90A: +case 0xEB0A: +case 0xED0A: +case 0xEF0A: +case 0xE10B: +case 0xE30B: +case 0xE50B: +case 0xE70B: +case 0xE90B: +case 0xEB0B: +case 0xED0B: +case 0xEF0B: +case 0xE10C: +case 0xE30C: +case 0xE50C: +case 0xE70C: +case 0xE90C: +case 0xEB0C: +case 0xED0C: +case 0xEF0C: +case 0xE10D: +case 0xE30D: +case 0xE50D: +case 0xE70D: +case 0xE90D: +case 0xEB0D: +case 0xED0D: +case 0xEF0D: +case 0xE10E: +case 0xE30E: +case 0xE50E: +case 0xE70E: +case 0xE90E: +case 0xEB0E: +case 0xED0E: +case 0xEF0E: +case 0xE10F: +case 0xE30F: +case 0xE50F: +case 0xE70F: +case 0xE90F: +case 0xEB0F: +case 0xED0F: +case 0xEF0F: + +// LSLk +case 0xE108: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << (0 + sft); + res = src << sft; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE348: +case 0xE548: +case 0xE748: +case 0xE948: +case 0xEB48: +case 0xED48: +case 0xEF48: +case 0xE149: +case 0xE349: +case 0xE549: +case 0xE749: +case 0xE949: +case 0xEB49: +case 0xED49: +case 0xEF49: +case 0xE14A: +case 0xE34A: +case 0xE54A: +case 0xE74A: +case 0xE94A: +case 0xEB4A: +case 0xED4A: +case 0xEF4A: +case 0xE14B: +case 0xE34B: +case 0xE54B: +case 0xE74B: +case 0xE94B: +case 0xEB4B: +case 0xED4B: +case 0xEF4B: +case 0xE14C: +case 0xE34C: +case 0xE54C: +case 0xE74C: +case 0xE94C: +case 0xEB4C: +case 0xED4C: +case 0xEF4C: +case 0xE14D: +case 0xE34D: +case 0xE54D: +case 0xE74D: +case 0xE94D: +case 0xEB4D: +case 0xED4D: +case 0xEF4D: +case 0xE14E: +case 0xE34E: +case 0xE54E: +case 0xE74E: +case 0xE94E: +case 0xEB4E: +case 0xED4E: +case 0xEF4E: +case 0xE14F: +case 0xE34F: +case 0xE54F: +case 0xE74F: +case 0xE94F: +case 0xEB4F: +case 0xED4F: +case 0xEF4F: + +// LSLk +case 0xE148: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> (8 - sft); + res = src << sft; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE388: +case 0xE588: +case 0xE788: +case 0xE988: +case 0xEB88: +case 0xED88: +case 0xEF88: +case 0xE189: +case 0xE389: +case 0xE589: +case 0xE789: +case 0xE989: +case 0xEB89: +case 0xED89: +case 0xEF89: +case 0xE18A: +case 0xE38A: +case 0xE58A: +case 0xE78A: +case 0xE98A: +case 0xEB8A: +case 0xED8A: +case 0xEF8A: +case 0xE18B: +case 0xE38B: +case 0xE58B: +case 0xE78B: +case 0xE98B: +case 0xEB8B: +case 0xED8B: +case 0xEF8B: +case 0xE18C: +case 0xE38C: +case 0xE58C: +case 0xE78C: +case 0xE98C: +case 0xEB8C: +case 0xED8C: +case 0xEF8C: +case 0xE18D: +case 0xE38D: +case 0xE58D: +case 0xE78D: +case 0xE98D: +case 0xEB8D: +case 0xED8D: +case 0xEF8D: +case 0xE18E: +case 0xE38E: +case 0xE58E: +case 0xE78E: +case 0xE98E: +case 0xEB8E: +case 0xED8E: +case 0xEF8E: +case 0xE18F: +case 0xE38F: +case 0xE58F: +case 0xE78F: +case 0xE98F: +case 0xEB8F: +case 0xED8F: +case 0xEF8F: + +// LSLk +case 0xE188: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> (24 - sft); + res = src << sft; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res & 0xFFFFFFFF; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE310: +case 0xE510: +case 0xE710: +case 0xE910: +case 0xEB10: +case 0xED10: +case 0xEF10: +case 0xE111: +case 0xE311: +case 0xE511: +case 0xE711: +case 0xE911: +case 0xEB11: +case 0xED11: +case 0xEF11: +case 0xE112: +case 0xE312: +case 0xE512: +case 0xE712: +case 0xE912: +case 0xEB12: +case 0xED12: +case 0xEF12: +case 0xE113: +case 0xE313: +case 0xE513: +case 0xE713: +case 0xE913: +case 0xEB13: +case 0xED13: +case 0xEF13: +case 0xE114: +case 0xE314: +case 0xE514: +case 0xE714: +case 0xE914: +case 0xEB14: +case 0xED14: +case 0xEF14: +case 0xE115: +case 0xE315: +case 0xE515: +case 0xE715: +case 0xE915: +case 0xEB15: +case 0xED15: +case 0xEF15: +case 0xE116: +case 0xE316: +case 0xE516: +case 0xE716: +case 0xE916: +case 0xEB16: +case 0xED16: +case 0xEF16: +case 0xE117: +case 0xE317: +case 0xE517: +case 0xE717: +case 0xE917: +case 0xEB17: +case 0xED17: +case 0xEF17: + +// ROXLk +case 0xE110: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + src |= (CPU->flag_X & C68K_SR_X) << 0; + res = (src << sft) | (src >> (9 - sft)); + CPU->flag_X = CPU->flag_C = res >> 0; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE350: +case 0xE550: +case 0xE750: +case 0xE950: +case 0xEB50: +case 0xED50: +case 0xEF50: +case 0xE151: +case 0xE351: +case 0xE551: +case 0xE751: +case 0xE951: +case 0xEB51: +case 0xED51: +case 0xEF51: +case 0xE152: +case 0xE352: +case 0xE552: +case 0xE752: +case 0xE952: +case 0xEB52: +case 0xED52: +case 0xEF52: +case 0xE153: +case 0xE353: +case 0xE553: +case 0xE753: +case 0xE953: +case 0xEB53: +case 0xED53: +case 0xEF53: +case 0xE154: +case 0xE354: +case 0xE554: +case 0xE754: +case 0xE954: +case 0xEB54: +case 0xED54: +case 0xEF54: +case 0xE155: +case 0xE355: +case 0xE555: +case 0xE755: +case 0xE955: +case 0xEB55: +case 0xED55: +case 0xEF55: +case 0xE156: +case 0xE356: +case 0xE556: +case 0xE756: +case 0xE956: +case 0xEB56: +case 0xED56: +case 0xEF56: +case 0xE157: +case 0xE357: +case 0xE557: +case 0xE757: +case 0xE957: +case 0xEB57: +case 0xED57: +case 0xEF57: + +// ROXLk +case 0xE150: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + src |= (CPU->flag_X & C68K_SR_X) << 8; + res = (src << sft) | (src >> (17 - sft)); + CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE390: +case 0xE590: +case 0xE790: +case 0xE990: +case 0xEB90: +case 0xED90: +case 0xEF90: +case 0xE191: +case 0xE391: +case 0xE591: +case 0xE791: +case 0xE991: +case 0xEB91: +case 0xED91: +case 0xEF91: +case 0xE192: +case 0xE392: +case 0xE592: +case 0xE792: +case 0xE992: +case 0xEB92: +case 0xED92: +case 0xEF92: +case 0xE193: +case 0xE393: +case 0xE593: +case 0xE793: +case 0xE993: +case 0xEB93: +case 0xED93: +case 0xEF93: +case 0xE194: +case 0xE394: +case 0xE594: +case 0xE794: +case 0xE994: +case 0xEB94: +case 0xED94: +case 0xEF94: +case 0xE195: +case 0xE395: +case 0xE595: +case 0xE795: +case 0xE995: +case 0xEB95: +case 0xED95: +case 0xEF95: +case 0xE196: +case 0xE396: +case 0xE596: +case 0xE796: +case 0xE996: +case 0xEB96: +case 0xED96: +case 0xEF96: +case 0xE197: +case 0xE397: +case 0xE597: +case 0xE797: +case 0xE997: +case 0xEB97: +case 0xED97: +case 0xEF97: + +// ROXLk +case 0xE190: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_C = src >> ((32 - C68K_SR_C_SFT) - sft); + if (sft == 1) res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - 1)); + else res = (src << sft) | (src >> (33 - sft)) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - sft)); + CPU->flag_X = CPU->flag_C; + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE318: +case 0xE518: +case 0xE718: +case 0xE918: +case 0xEB18: +case 0xED18: +case 0xEF18: +case 0xE119: +case 0xE319: +case 0xE519: +case 0xE719: +case 0xE919: +case 0xEB19: +case 0xED19: +case 0xEF19: +case 0xE11A: +case 0xE31A: +case 0xE51A: +case 0xE71A: +case 0xE91A: +case 0xEB1A: +case 0xED1A: +case 0xEF1A: +case 0xE11B: +case 0xE31B: +case 0xE51B: +case 0xE71B: +case 0xE91B: +case 0xEB1B: +case 0xED1B: +case 0xEF1B: +case 0xE11C: +case 0xE31C: +case 0xE51C: +case 0xE71C: +case 0xE91C: +case 0xEB1C: +case 0xED1C: +case 0xEF1C: +case 0xE11D: +case 0xE31D: +case 0xE51D: +case 0xE71D: +case 0xE91D: +case 0xEB1D: +case 0xED1D: +case 0xEF1D: +case 0xE11E: +case 0xE31E: +case 0xE51E: +case 0xE71E: +case 0xE91E: +case 0xEB1E: +case 0xED1E: +case 0xEF1E: +case 0xE11F: +case 0xE31F: +case 0xE51F: +case 0xE71F: +case 0xE91F: +case 0xEB1F: +case 0xED1F: +case 0xEF1F: + +// ROLk +case 0xE118: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src << (0 + sft); + res = (src << sft) | (src >> (8 - sft)); + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE358: +case 0xE558: +case 0xE758: +case 0xE958: +case 0xEB58: +case 0xED58: +case 0xEF58: +case 0xE159: +case 0xE359: +case 0xE559: +case 0xE759: +case 0xE959: +case 0xEB59: +case 0xED59: +case 0xEF59: +case 0xE15A: +case 0xE35A: +case 0xE55A: +case 0xE75A: +case 0xE95A: +case 0xEB5A: +case 0xED5A: +case 0xEF5A: +case 0xE15B: +case 0xE35B: +case 0xE55B: +case 0xE75B: +case 0xE95B: +case 0xEB5B: +case 0xED5B: +case 0xEF5B: +case 0xE15C: +case 0xE35C: +case 0xE55C: +case 0xE75C: +case 0xE95C: +case 0xEB5C: +case 0xED5C: +case 0xEF5C: +case 0xE15D: +case 0xE35D: +case 0xE55D: +case 0xE75D: +case 0xE95D: +case 0xEB5D: +case 0xED5D: +case 0xEF5D: +case 0xE15E: +case 0xE35E: +case 0xE55E: +case 0xE75E: +case 0xE95E: +case 0xEB5E: +case 0xED5E: +case 0xEF5E: +case 0xE15F: +case 0xE35F: +case 0xE55F: +case 0xE75F: +case 0xE95F: +case 0xEB5F: +case 0xED5F: +case 0xEF5F: + +// ROLk +case 0xE158: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src >> (8 - sft); + res = (src << sft) | (src >> (16 - sft)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(6) +case 0xE398: +case 0xE598: +case 0xE798: +case 0xE998: +case 0xEB98: +case 0xED98: +case 0xEF98: +case 0xE199: +case 0xE399: +case 0xE599: +case 0xE799: +case 0xE999: +case 0xEB99: +case 0xED99: +case 0xEF99: +case 0xE19A: +case 0xE39A: +case 0xE59A: +case 0xE79A: +case 0xE99A: +case 0xEB9A: +case 0xED9A: +case 0xEF9A: +case 0xE19B: +case 0xE39B: +case 0xE59B: +case 0xE79B: +case 0xE99B: +case 0xEB9B: +case 0xED9B: +case 0xEF9B: +case 0xE19C: +case 0xE39C: +case 0xE59C: +case 0xE79C: +case 0xE99C: +case 0xEB9C: +case 0xED9C: +case 0xEF9C: +case 0xE19D: +case 0xE39D: +case 0xE59D: +case 0xE79D: +case 0xE99D: +case 0xEB9D: +case 0xED9D: +case 0xEF9D: +case 0xE19E: +case 0xE39E: +case 0xE59E: +case 0xE79E: +case 0xE99E: +case 0xEB9E: +case 0xED9E: +case 0xEF9E: +case 0xE19F: +case 0xE39F: +case 0xE59F: +case 0xE79F: +case 0xE99F: +case 0xEB9F: +case 0xED9F: +case 0xEF9F: + +// ROLk +case 0xE198: +{ + u32 res; + pointer src; + u32 sft; + + sft = (((Opcode >> 9) - 1) & 7) + 1; + CCnt -= sft * 2; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + CPU->flag_V = 0; + CPU->flag_C = src >> (24 - sft); + res = (src << sft) | (src >> (32 - sft)); + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; +} +RET(8) +case 0xE220: +case 0xE420: +case 0xE620: +case 0xE820: +case 0xEA20: +case 0xEC20: +case 0xEE20: +case 0xE021: +case 0xE221: +case 0xE421: +case 0xE621: +case 0xE821: +case 0xEA21: +case 0xEC21: +case 0xEE21: +case 0xE022: +case 0xE222: +case 0xE422: +case 0xE622: +case 0xE822: +case 0xEA22: +case 0xEC22: +case 0xEE22: +case 0xE023: +case 0xE223: +case 0xE423: +case 0xE623: +case 0xE823: +case 0xEA23: +case 0xEC23: +case 0xEE23: +case 0xE024: +case 0xE224: +case 0xE424: +case 0xE624: +case 0xE824: +case 0xEA24: +case 0xEC24: +case 0xEE24: +case 0xE025: +case 0xE225: +case 0xE425: +case 0xE625: +case 0xE825: +case 0xEA25: +case 0xEC25: +case 0xEE25: +case 0xE026: +case 0xE226: +case 0xE426: +case 0xE626: +case 0xE826: +case 0xEA26: +case 0xEC26: +case 0xEE26: +case 0xE027: +case 0xE227: +case 0xE427: +case 0xE627: +case 0xE827: +case 0xEA27: +case 0xEC27: +case 0xEE27: + +// ASRD +case 0xE020: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (s32)(s8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 8) + { + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = ((s32)src) >> sft; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + if (src & (1 << 7)) + { + CPU->flag_N = C68K_SR_N; + CPU->flag_notZ = 1; + CPU->flag_V = 0; + CPU->flag_C = C68K_SR_C; + CPU->flag_X = C68K_SR_X; + res = 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_X = 0; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE260: +case 0xE460: +case 0xE660: +case 0xE860: +case 0xEA60: +case 0xEC60: +case 0xEE60: +case 0xE061: +case 0xE261: +case 0xE461: +case 0xE661: +case 0xE861: +case 0xEA61: +case 0xEC61: +case 0xEE61: +case 0xE062: +case 0xE262: +case 0xE462: +case 0xE662: +case 0xE862: +case 0xEA62: +case 0xEC62: +case 0xEE62: +case 0xE063: +case 0xE263: +case 0xE463: +case 0xE663: +case 0xE863: +case 0xEA63: +case 0xEC63: +case 0xEE63: +case 0xE064: +case 0xE264: +case 0xE464: +case 0xE664: +case 0xE864: +case 0xEA64: +case 0xEC64: +case 0xEE64: +case 0xE065: +case 0xE265: +case 0xE465: +case 0xE665: +case 0xE865: +case 0xEA65: +case 0xEC65: +case 0xEE65: +case 0xE066: +case 0xE266: +case 0xE466: +case 0xE666: +case 0xE866: +case 0xEA66: +case 0xEC66: +case 0xEE66: +case 0xE067: +case 0xE267: +case 0xE467: +case 0xE667: +case 0xE867: +case 0xEA67: +case 0xEC67: +case 0xEE67: + +// ASRD +case 0xE060: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (s32)(s16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 16) + { + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT; + res = ((s32)src) >> sft; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + if (src & (1 << 15)) + { + CPU->flag_N = C68K_SR_N; + CPU->flag_notZ = 1; + CPU->flag_V = 0; + CPU->flag_C = C68K_SR_C; + CPU->flag_X = C68K_SR_X; + res = 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_X = 0; + res = 0; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE2A0: +case 0xE4A0: +case 0xE6A0: +case 0xE8A0: +case 0xEAA0: +case 0xECA0: +case 0xEEA0: +case 0xE0A1: +case 0xE2A1: +case 0xE4A1: +case 0xE6A1: +case 0xE8A1: +case 0xEAA1: +case 0xECA1: +case 0xEEA1: +case 0xE0A2: +case 0xE2A2: +case 0xE4A2: +case 0xE6A2: +case 0xE8A2: +case 0xEAA2: +case 0xECA2: +case 0xEEA2: +case 0xE0A3: +case 0xE2A3: +case 0xE4A3: +case 0xE6A3: +case 0xE8A3: +case 0xEAA3: +case 0xECA3: +case 0xEEA3: +case 0xE0A4: +case 0xE2A4: +case 0xE4A4: +case 0xE6A4: +case 0xE8A4: +case 0xEAA4: +case 0xECA4: +case 0xEEA4: +case 0xE0A5: +case 0xE2A5: +case 0xE4A5: +case 0xE6A5: +case 0xE8A5: +case 0xEAA5: +case 0xECA5: +case 0xEEA5: +case 0xE0A6: +case 0xE2A6: +case 0xE4A6: +case 0xE6A6: +case 0xE8A6: +case 0xEAA6: +case 0xECA6: +case 0xEEA6: +case 0xE0A7: +case 0xE2A7: +case 0xE4A7: +case 0xE6A7: +case 0xE8A7: +case 0xEAA7: +case 0xECA7: +case 0xEEA7: + +// ASRD +case 0xE0A0: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (s32)(s32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 32) + { + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT; + res = ((s32)src) >> sft; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + if (src & (1 << 31)) + { + CPU->flag_N = C68K_SR_N; + CPU->flag_notZ = 1; + CPU->flag_V = 0; + CPU->flag_C = C68K_SR_C; + CPU->flag_X = C68K_SR_X; + res = 0xFFFFFFFF; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_X = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE228: +case 0xE428: +case 0xE628: +case 0xE828: +case 0xEA28: +case 0xEC28: +case 0xEE28: +case 0xE029: +case 0xE229: +case 0xE429: +case 0xE629: +case 0xE829: +case 0xEA29: +case 0xEC29: +case 0xEE29: +case 0xE02A: +case 0xE22A: +case 0xE42A: +case 0xE62A: +case 0xE82A: +case 0xEA2A: +case 0xEC2A: +case 0xEE2A: +case 0xE02B: +case 0xE22B: +case 0xE42B: +case 0xE62B: +case 0xE82B: +case 0xEA2B: +case 0xEC2B: +case 0xEE2B: +case 0xE02C: +case 0xE22C: +case 0xE42C: +case 0xE62C: +case 0xE82C: +case 0xEA2C: +case 0xEC2C: +case 0xEE2C: +case 0xE02D: +case 0xE22D: +case 0xE42D: +case 0xE62D: +case 0xE82D: +case 0xEA2D: +case 0xEC2D: +case 0xEE2D: +case 0xE02E: +case 0xE22E: +case 0xE42E: +case 0xE62E: +case 0xE82E: +case 0xEA2E: +case 0xEC2E: +case 0xEE2E: +case 0xE02F: +case 0xE22F: +case 0xE42F: +case 0xE62F: +case 0xE82F: +case 0xEA2F: +case 0xEC2F: +case 0xEE2F: + +// LSRD +case 0xE028: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft <= 8) + { + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft); + res = src >> sft; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE268: +case 0xE468: +case 0xE668: +case 0xE868: +case 0xEA68: +case 0xEC68: +case 0xEE68: +case 0xE069: +case 0xE269: +case 0xE469: +case 0xE669: +case 0xE869: +case 0xEA69: +case 0xEC69: +case 0xEE69: +case 0xE06A: +case 0xE26A: +case 0xE46A: +case 0xE66A: +case 0xE86A: +case 0xEA6A: +case 0xEC6A: +case 0xEE6A: +case 0xE06B: +case 0xE26B: +case 0xE46B: +case 0xE66B: +case 0xE86B: +case 0xEA6B: +case 0xEC6B: +case 0xEE6B: +case 0xE06C: +case 0xE26C: +case 0xE46C: +case 0xE66C: +case 0xE86C: +case 0xEA6C: +case 0xEC6C: +case 0xEE6C: +case 0xE06D: +case 0xE26D: +case 0xE46D: +case 0xE66D: +case 0xE86D: +case 0xEA6D: +case 0xEC6D: +case 0xEE6D: +case 0xE06E: +case 0xE26E: +case 0xE46E: +case 0xE66E: +case 0xE86E: +case 0xEA6E: +case 0xEC6E: +case 0xEE6E: +case 0xE06F: +case 0xE26F: +case 0xE46F: +case 0xE66F: +case 0xE86F: +case 0xEA6F: +case 0xEC6F: +case 0xEE6F: + +// LSRD +case 0xE068: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft <= 16) + { + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT; + res = src >> sft; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE2A8: +case 0xE4A8: +case 0xE6A8: +case 0xE8A8: +case 0xEAA8: +case 0xECA8: +case 0xEEA8: +case 0xE0A9: +case 0xE2A9: +case 0xE4A9: +case 0xE6A9: +case 0xE8A9: +case 0xEAA9: +case 0xECA9: +case 0xEEA9: +case 0xE0AA: +case 0xE2AA: +case 0xE4AA: +case 0xE6AA: +case 0xE8AA: +case 0xEAAA: +case 0xECAA: +case 0xEEAA: +case 0xE0AB: +case 0xE2AB: +case 0xE4AB: +case 0xE6AB: +case 0xE8AB: +case 0xEAAB: +case 0xECAB: +case 0xEEAB: +case 0xE0AC: +case 0xE2AC: +case 0xE4AC: +case 0xE6AC: +case 0xE8AC: +case 0xEAAC: +case 0xECAC: +case 0xEEAC: +case 0xE0AD: +case 0xE2AD: +case 0xE4AD: +case 0xE6AD: +case 0xE8AD: +case 0xEAAD: +case 0xECAD: +case 0xEEAD: +case 0xE0AE: +case 0xE2AE: +case 0xE4AE: +case 0xE6AE: +case 0xE8AE: +case 0xEAAE: +case 0xECAE: +case 0xEEAE: +case 0xE0AF: +case 0xE2AF: +case 0xE4AF: +case 0xE6AF: +case 0xE8AF: +case 0xEAAF: +case 0xECAF: +case 0xEEAF: + +// LSRD +case 0xE0A8: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 32) + { + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT; + res = src >> sft; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + if (sft == 32) CPU->flag_C = src >> (31 - C68K_SR_C_SFT); + else CPU->flag_C = 0; + CPU->flag_X = CPU->flag_C; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE230: +case 0xE430: +case 0xE630: +case 0xE830: +case 0xEA30: +case 0xEC30: +case 0xEE30: +case 0xE031: +case 0xE231: +case 0xE431: +case 0xE631: +case 0xE831: +case 0xEA31: +case 0xEC31: +case 0xEE31: +case 0xE032: +case 0xE232: +case 0xE432: +case 0xE632: +case 0xE832: +case 0xEA32: +case 0xEC32: +case 0xEE32: +case 0xE033: +case 0xE233: +case 0xE433: +case 0xE633: +case 0xE833: +case 0xEA33: +case 0xEC33: +case 0xEE33: +case 0xE034: +case 0xE234: +case 0xE434: +case 0xE634: +case 0xE834: +case 0xEA34: +case 0xEC34: +case 0xEE34: +case 0xE035: +case 0xE235: +case 0xE435: +case 0xE635: +case 0xE835: +case 0xEA35: +case 0xEC35: +case 0xEE35: +case 0xE036: +case 0xE236: +case 0xE436: +case 0xE636: +case 0xE836: +case 0xEA36: +case 0xEC36: +case 0xEE36: +case 0xE037: +case 0xE237: +case 0xE437: +case 0xE637: +case 0xE837: +case 0xEA37: +case 0xEC37: +case 0xEE37: + +// ROXRD +case 0xE030: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 9; + + src |= (CPU->flag_X & C68K_SR_X) << 0; + res = (src >> sft) | (src << (9 - sft)); + CPU->flag_X = CPU->flag_C = res >> 0; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE270: +case 0xE470: +case 0xE670: +case 0xE870: +case 0xEA70: +case 0xEC70: +case 0xEE70: +case 0xE071: +case 0xE271: +case 0xE471: +case 0xE671: +case 0xE871: +case 0xEA71: +case 0xEC71: +case 0xEE71: +case 0xE072: +case 0xE272: +case 0xE472: +case 0xE672: +case 0xE872: +case 0xEA72: +case 0xEC72: +case 0xEE72: +case 0xE073: +case 0xE273: +case 0xE473: +case 0xE673: +case 0xE873: +case 0xEA73: +case 0xEC73: +case 0xEE73: +case 0xE074: +case 0xE274: +case 0xE474: +case 0xE674: +case 0xE874: +case 0xEA74: +case 0xEC74: +case 0xEE74: +case 0xE075: +case 0xE275: +case 0xE475: +case 0xE675: +case 0xE875: +case 0xEA75: +case 0xEC75: +case 0xEE75: +case 0xE076: +case 0xE276: +case 0xE476: +case 0xE676: +case 0xE876: +case 0xEA76: +case 0xEC76: +case 0xEE76: +case 0xE077: +case 0xE277: +case 0xE477: +case 0xE677: +case 0xE877: +case 0xEA77: +case 0xEC77: +case 0xEE77: + +// ROXRD +case 0xE070: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 17; + + src |= (CPU->flag_X & C68K_SR_X) << 8; + res = (src >> sft) | (src << (17 - sft)); + CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE2B0: +case 0xE4B0: +case 0xE6B0: +case 0xE8B0: +case 0xEAB0: +case 0xECB0: +case 0xEEB0: +case 0xE0B1: +case 0xE2B1: +case 0xE4B1: +case 0xE6B1: +case 0xE8B1: +case 0xEAB1: +case 0xECB1: +case 0xEEB1: +case 0xE0B2: +case 0xE2B2: +case 0xE4B2: +case 0xE6B2: +case 0xE8B2: +case 0xEAB2: +case 0xECB2: +case 0xEEB2: +case 0xE0B3: +case 0xE2B3: +case 0xE4B3: +case 0xE6B3: +case 0xE8B3: +case 0xEAB3: +case 0xECB3: +case 0xEEB3: +case 0xE0B4: +case 0xE2B4: +case 0xE4B4: +case 0xE6B4: +case 0xE8B4: +case 0xEAB4: +case 0xECB4: +case 0xEEB4: +case 0xE0B5: +case 0xE2B5: +case 0xE4B5: +case 0xE6B5: +case 0xE8B5: +case 0xEAB5: +case 0xECB5: +case 0xEEB5: +case 0xE0B6: +case 0xE2B6: +case 0xE4B6: +case 0xE6B6: +case 0xE8B6: +case 0xEAB6: +case 0xECB6: +case 0xEEB6: +case 0xE0B7: +case 0xE2B7: +case 0xE4B7: +case 0xE6B7: +case 0xE8B7: +case 0xEAB7: +case 0xECB7: +case 0xEEB7: + +// ROXRD +case 0xE0B0: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 33; + + if (sft != 0) + { + if (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1))); + else res = (src >> sft) | (src << (33 - sft)) | (((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1))) >> (sft - 1)); + CPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT; + } + else res = src; + CPU->flag_C = CPU->flag_X; + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE238: +case 0xE438: +case 0xE638: +case 0xE838: +case 0xEA38: +case 0xEC38: +case 0xEE38: +case 0xE039: +case 0xE239: +case 0xE439: +case 0xE639: +case 0xE839: +case 0xEA39: +case 0xEC39: +case 0xEE39: +case 0xE03A: +case 0xE23A: +case 0xE43A: +case 0xE63A: +case 0xE83A: +case 0xEA3A: +case 0xEC3A: +case 0xEE3A: +case 0xE03B: +case 0xE23B: +case 0xE43B: +case 0xE63B: +case 0xE83B: +case 0xEA3B: +case 0xEC3B: +case 0xEE3B: +case 0xE03C: +case 0xE23C: +case 0xE43C: +case 0xE63C: +case 0xE83C: +case 0xEA3C: +case 0xEC3C: +case 0xEE3C: +case 0xE03D: +case 0xE23D: +case 0xE43D: +case 0xE63D: +case 0xE83D: +case 0xEA3D: +case 0xEC3D: +case 0xEE3D: +case 0xE03E: +case 0xE23E: +case 0xE43E: +case 0xE63E: +case 0xE83E: +case 0xEA3E: +case 0xEC3E: +case 0xEE3E: +case 0xE03F: +case 0xE23F: +case 0xE43F: +case 0xE63F: +case 0xE83F: +case 0xEA3F: +case 0xEC3F: +case 0xEE3F: + +// RORD +case 0xE038: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft &= 0x07; + + CPU->flag_C = src << (C68K_SR_C_SFT - ((sft - 1) & 7)); + res = (src >> sft) | (src << (8 - sft)); + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE278: +case 0xE478: +case 0xE678: +case 0xE878: +case 0xEA78: +case 0xEC78: +case 0xEE78: +case 0xE079: +case 0xE279: +case 0xE479: +case 0xE679: +case 0xE879: +case 0xEA79: +case 0xEC79: +case 0xEE79: +case 0xE07A: +case 0xE27A: +case 0xE47A: +case 0xE67A: +case 0xE87A: +case 0xEA7A: +case 0xEC7A: +case 0xEE7A: +case 0xE07B: +case 0xE27B: +case 0xE47B: +case 0xE67B: +case 0xE87B: +case 0xEA7B: +case 0xEC7B: +case 0xEE7B: +case 0xE07C: +case 0xE27C: +case 0xE47C: +case 0xE67C: +case 0xE87C: +case 0xEA7C: +case 0xEC7C: +case 0xEE7C: +case 0xE07D: +case 0xE27D: +case 0xE47D: +case 0xE67D: +case 0xE87D: +case 0xEA7D: +case 0xEC7D: +case 0xEE7D: +case 0xE07E: +case 0xE27E: +case 0xE47E: +case 0xE67E: +case 0xE87E: +case 0xEA7E: +case 0xEC7E: +case 0xEE7E: +case 0xE07F: +case 0xE27F: +case 0xE47F: +case 0xE67F: +case 0xE87F: +case 0xEA7F: +case 0xEC7F: +case 0xEE7F: + +// RORD +case 0xE078: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft &= 0x0F; + + CPU->flag_C = (src >> ((sft - 1) & 15)) << C68K_SR_C_SFT; + res = (src >> sft) | (src << (16 - sft)); + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE2B8: +case 0xE4B8: +case 0xE6B8: +case 0xE8B8: +case 0xEAB8: +case 0xECB8: +case 0xEEB8: +case 0xE0B9: +case 0xE2B9: +case 0xE4B9: +case 0xE6B9: +case 0xE8B9: +case 0xEAB9: +case 0xECB9: +case 0xEEB9: +case 0xE0BA: +case 0xE2BA: +case 0xE4BA: +case 0xE6BA: +case 0xE8BA: +case 0xEABA: +case 0xECBA: +case 0xEEBA: +case 0xE0BB: +case 0xE2BB: +case 0xE4BB: +case 0xE6BB: +case 0xE8BB: +case 0xEABB: +case 0xECBB: +case 0xEEBB: +case 0xE0BC: +case 0xE2BC: +case 0xE4BC: +case 0xE6BC: +case 0xE8BC: +case 0xEABC: +case 0xECBC: +case 0xEEBC: +case 0xE0BD: +case 0xE2BD: +case 0xE4BD: +case 0xE6BD: +case 0xE8BD: +case 0xEABD: +case 0xECBD: +case 0xEEBD: +case 0xE0BE: +case 0xE2BE: +case 0xE4BE: +case 0xE6BE: +case 0xE8BE: +case 0xEABE: +case 0xECBE: +case 0xEEBE: +case 0xE0BF: +case 0xE2BF: +case 0xE4BF: +case 0xE6BF: +case 0xE8BF: +case 0xEABF: +case 0xECBF: +case 0xEEBF: + +// RORD +case 0xE0B8: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft &= 0x1F; + + CPU->flag_C = (src >> ((sft - 1) & 31)) << C68K_SR_C_SFT; + res = (src >> sft) | (src << (32 - sft)); + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE320: +case 0xE520: +case 0xE720: +case 0xE920: +case 0xEB20: +case 0xED20: +case 0xEF20: +case 0xE121: +case 0xE321: +case 0xE521: +case 0xE721: +case 0xE921: +case 0xEB21: +case 0xED21: +case 0xEF21: +case 0xE122: +case 0xE322: +case 0xE522: +case 0xE722: +case 0xE922: +case 0xEB22: +case 0xED22: +case 0xEF22: +case 0xE123: +case 0xE323: +case 0xE523: +case 0xE723: +case 0xE923: +case 0xEB23: +case 0xED23: +case 0xEF23: +case 0xE124: +case 0xE324: +case 0xE524: +case 0xE724: +case 0xE924: +case 0xEB24: +case 0xED24: +case 0xEF24: +case 0xE125: +case 0xE325: +case 0xE525: +case 0xE725: +case 0xE925: +case 0xEB25: +case 0xED25: +case 0xEF25: +case 0xE126: +case 0xE326: +case 0xE526: +case 0xE726: +case 0xE926: +case 0xEB26: +case 0xED26: +case 0xEF26: +case 0xE127: +case 0xE327: +case 0xE527: +case 0xE727: +case 0xE927: +case 0xEB27: +case 0xED27: +case 0xEF27: + +// ASLD +case 0xE120: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 8) + { + CPU->flag_X = CPU->flag_C = (src << sft) >> 0; + res = (src << sft) & 0x000000FF; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + { + u32 msk = (((s32)0x80000000) >> (sft + 24)) & 0x000000FF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } + RET(6) + } + + if (sft == 256) CPU->flag_C = src << C68K_SR_C_SFT; + else CPU->flag_C = 0; + CPU->flag_X = CPU->flag_C; + if (src) CPU->flag_V = C68K_SR_V; + else CPU->flag_V = 0; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE360: +case 0xE560: +case 0xE760: +case 0xE960: +case 0xEB60: +case 0xED60: +case 0xEF60: +case 0xE161: +case 0xE361: +case 0xE561: +case 0xE761: +case 0xE961: +case 0xEB61: +case 0xED61: +case 0xEF61: +case 0xE162: +case 0xE362: +case 0xE562: +case 0xE762: +case 0xE962: +case 0xEB62: +case 0xED62: +case 0xEF62: +case 0xE163: +case 0xE363: +case 0xE563: +case 0xE763: +case 0xE963: +case 0xEB63: +case 0xED63: +case 0xEF63: +case 0xE164: +case 0xE364: +case 0xE564: +case 0xE764: +case 0xE964: +case 0xEB64: +case 0xED64: +case 0xEF64: +case 0xE165: +case 0xE365: +case 0xE565: +case 0xE765: +case 0xE965: +case 0xEB65: +case 0xED65: +case 0xEF65: +case 0xE166: +case 0xE366: +case 0xE566: +case 0xE766: +case 0xE966: +case 0xEB66: +case 0xED66: +case 0xEF66: +case 0xE167: +case 0xE367: +case 0xE567: +case 0xE767: +case 0xE967: +case 0xEB67: +case 0xED67: +case 0xEF67: + +// ASLD +case 0xE160: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 16) + { + CPU->flag_X = CPU->flag_C = (src << sft) >> 8; + res = (src << sft) & 0x0000FFFF; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + { + u32 msk = (((s32)0x80000000) >> (sft + 16)) & 0x0000FFFF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } + RET(6) + } + + if (sft == 65536) CPU->flag_C = src << C68K_SR_C_SFT; + else CPU->flag_C = 0; + CPU->flag_X = CPU->flag_C; + if (src) CPU->flag_V = C68K_SR_V; + else CPU->flag_V = 0; + res = 0; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE3A0: +case 0xE5A0: +case 0xE7A0: +case 0xE9A0: +case 0xEBA0: +case 0xEDA0: +case 0xEFA0: +case 0xE1A1: +case 0xE3A1: +case 0xE5A1: +case 0xE7A1: +case 0xE9A1: +case 0xEBA1: +case 0xEDA1: +case 0xEFA1: +case 0xE1A2: +case 0xE3A2: +case 0xE5A2: +case 0xE7A2: +case 0xE9A2: +case 0xEBA2: +case 0xEDA2: +case 0xEFA2: +case 0xE1A3: +case 0xE3A3: +case 0xE5A3: +case 0xE7A3: +case 0xE9A3: +case 0xEBA3: +case 0xEDA3: +case 0xEFA3: +case 0xE1A4: +case 0xE3A4: +case 0xE5A4: +case 0xE7A4: +case 0xE9A4: +case 0xEBA4: +case 0xEDA4: +case 0xEFA4: +case 0xE1A5: +case 0xE3A5: +case 0xE5A5: +case 0xE7A5: +case 0xE9A5: +case 0xEBA5: +case 0xEDA5: +case 0xEFA5: +case 0xE1A6: +case 0xE3A6: +case 0xE5A6: +case 0xE7A6: +case 0xE9A6: +case 0xEBA6: +case 0xEDA6: +case 0xEFA6: +case 0xE1A7: +case 0xE3A7: +case 0xE5A7: +case 0xE7A7: +case 0xE9A7: +case 0xEBA7: +case 0xEDA7: +case 0xEFA7: + +// ASLD +case 0xE1A0: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 32) + { + CPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT; + res = src << sft; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_V = 0; + { + u32 msk = (((s32)0x80000000) >> (sft + 0)) & 0xFFFFFFFF; + src &= msk; + if ((src) && (src != msk)) CPU->flag_V = C68K_SR_V; + } + RET(8) + } + + if (sft == 0) CPU->flag_C = src << C68K_SR_C_SFT; + else CPU->flag_C = 0; + CPU->flag_X = CPU->flag_C; + if (src) CPU->flag_V = C68K_SR_V; + else CPU->flag_V = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE328: +case 0xE528: +case 0xE728: +case 0xE928: +case 0xEB28: +case 0xED28: +case 0xEF28: +case 0xE129: +case 0xE329: +case 0xE529: +case 0xE729: +case 0xE929: +case 0xEB29: +case 0xED29: +case 0xEF29: +case 0xE12A: +case 0xE32A: +case 0xE52A: +case 0xE72A: +case 0xE92A: +case 0xEB2A: +case 0xED2A: +case 0xEF2A: +case 0xE12B: +case 0xE32B: +case 0xE52B: +case 0xE72B: +case 0xE92B: +case 0xEB2B: +case 0xED2B: +case 0xEF2B: +case 0xE12C: +case 0xE32C: +case 0xE52C: +case 0xE72C: +case 0xE92C: +case 0xEB2C: +case 0xED2C: +case 0xEF2C: +case 0xE12D: +case 0xE32D: +case 0xE52D: +case 0xE72D: +case 0xE92D: +case 0xEB2D: +case 0xED2D: +case 0xEF2D: +case 0xE12E: +case 0xE32E: +case 0xE52E: +case 0xE72E: +case 0xE92E: +case 0xEB2E: +case 0xED2E: +case 0xEF2E: +case 0xE12F: +case 0xE32F: +case 0xE52F: +case 0xE72F: +case 0xE92F: +case 0xEB2F: +case 0xED2F: +case 0xEF2F: + +// LSLD +case 0xE128: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft <= 8) + { + CPU->flag_X = CPU->flag_C = (src << sft) >> 0; + res = (src << sft) & 0x000000FF; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE368: +case 0xE568: +case 0xE768: +case 0xE968: +case 0xEB68: +case 0xED68: +case 0xEF68: +case 0xE169: +case 0xE369: +case 0xE569: +case 0xE769: +case 0xE969: +case 0xEB69: +case 0xED69: +case 0xEF69: +case 0xE16A: +case 0xE36A: +case 0xE56A: +case 0xE76A: +case 0xE96A: +case 0xEB6A: +case 0xED6A: +case 0xEF6A: +case 0xE16B: +case 0xE36B: +case 0xE56B: +case 0xE76B: +case 0xE96B: +case 0xEB6B: +case 0xED6B: +case 0xEF6B: +case 0xE16C: +case 0xE36C: +case 0xE56C: +case 0xE76C: +case 0xE96C: +case 0xEB6C: +case 0xED6C: +case 0xEF6C: +case 0xE16D: +case 0xE36D: +case 0xE56D: +case 0xE76D: +case 0xE96D: +case 0xEB6D: +case 0xED6D: +case 0xEF6D: +case 0xE16E: +case 0xE36E: +case 0xE56E: +case 0xE76E: +case 0xE96E: +case 0xEB6E: +case 0xED6E: +case 0xEF6E: +case 0xE16F: +case 0xE36F: +case 0xE56F: +case 0xE76F: +case 0xE96F: +case 0xEB6F: +case 0xED6F: +case 0xEF6F: + +// LSLD +case 0xE168: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft <= 16) + { + CPU->flag_X = CPU->flag_C = (src << sft) >> 8; + res = (src << sft) & 0x0000FFFF; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_X = CPU->flag_C = 0; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE3A8: +case 0xE5A8: +case 0xE7A8: +case 0xE9A8: +case 0xEBA8: +case 0xEDA8: +case 0xEFA8: +case 0xE1A9: +case 0xE3A9: +case 0xE5A9: +case 0xE7A9: +case 0xE9A9: +case 0xEBA9: +case 0xEDA9: +case 0xEFA9: +case 0xE1AA: +case 0xE3AA: +case 0xE5AA: +case 0xE7AA: +case 0xE9AA: +case 0xEBAA: +case 0xEDAA: +case 0xEFAA: +case 0xE1AB: +case 0xE3AB: +case 0xE5AB: +case 0xE7AB: +case 0xE9AB: +case 0xEBAB: +case 0xEDAB: +case 0xEFAB: +case 0xE1AC: +case 0xE3AC: +case 0xE5AC: +case 0xE7AC: +case 0xE9AC: +case 0xEBAC: +case 0xEDAC: +case 0xEFAC: +case 0xE1AD: +case 0xE3AD: +case 0xE5AD: +case 0xE7AD: +case 0xE9AD: +case 0xEBAD: +case 0xEDAD: +case 0xEFAD: +case 0xE1AE: +case 0xE3AE: +case 0xE5AE: +case 0xE7AE: +case 0xE9AE: +case 0xEBAE: +case 0xEDAE: +case 0xEFAE: +case 0xE1AF: +case 0xE3AF: +case 0xE5AF: +case 0xE7AF: +case 0xE9AF: +case 0xEBAF: +case 0xEDAF: +case 0xEFAF: + +// LSLD +case 0xE1A8: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft < 32) + { + CPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT; + res = src << sft; + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + if (sft == 32) CPU->flag_C = src << C68K_SR_C_SFT; + else CPU->flag_C = 0; + CPU->flag_X = CPU->flag_C; + CPU->flag_N = 0; + CPU->flag_notZ = 0; + CPU->flag_V = 0; + res = 0; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE330: +case 0xE530: +case 0xE730: +case 0xE930: +case 0xEB30: +case 0xED30: +case 0xEF30: +case 0xE131: +case 0xE331: +case 0xE531: +case 0xE731: +case 0xE931: +case 0xEB31: +case 0xED31: +case 0xEF31: +case 0xE132: +case 0xE332: +case 0xE532: +case 0xE732: +case 0xE932: +case 0xEB32: +case 0xED32: +case 0xEF32: +case 0xE133: +case 0xE333: +case 0xE533: +case 0xE733: +case 0xE933: +case 0xEB33: +case 0xED33: +case 0xEF33: +case 0xE134: +case 0xE334: +case 0xE534: +case 0xE734: +case 0xE934: +case 0xEB34: +case 0xED34: +case 0xEF34: +case 0xE135: +case 0xE335: +case 0xE535: +case 0xE735: +case 0xE935: +case 0xEB35: +case 0xED35: +case 0xEF35: +case 0xE136: +case 0xE336: +case 0xE536: +case 0xE736: +case 0xE936: +case 0xEB36: +case 0xED36: +case 0xEF36: +case 0xE137: +case 0xE337: +case 0xE537: +case 0xE737: +case 0xE937: +case 0xEB37: +case 0xED37: +case 0xEF37: + +// ROXLD +case 0xE130: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 9; + + src |= (CPU->flag_X & C68K_SR_X) << 0; + res = (src << sft) | (src >> (9 - sft)); + CPU->flag_X = CPU->flag_C = res >> 0; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res & 0x000000FF; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE370: +case 0xE570: +case 0xE770: +case 0xE970: +case 0xEB70: +case 0xED70: +case 0xEF70: +case 0xE171: +case 0xE371: +case 0xE571: +case 0xE771: +case 0xE971: +case 0xEB71: +case 0xED71: +case 0xEF71: +case 0xE172: +case 0xE372: +case 0xE572: +case 0xE772: +case 0xE972: +case 0xEB72: +case 0xED72: +case 0xEF72: +case 0xE173: +case 0xE373: +case 0xE573: +case 0xE773: +case 0xE973: +case 0xEB73: +case 0xED73: +case 0xEF73: +case 0xE174: +case 0xE374: +case 0xE574: +case 0xE774: +case 0xE974: +case 0xEB74: +case 0xED74: +case 0xEF74: +case 0xE175: +case 0xE375: +case 0xE575: +case 0xE775: +case 0xE975: +case 0xEB75: +case 0xED75: +case 0xEF75: +case 0xE176: +case 0xE376: +case 0xE576: +case 0xE776: +case 0xE976: +case 0xEB76: +case 0xED76: +case 0xEF76: +case 0xE177: +case 0xE377: +case 0xE577: +case 0xE777: +case 0xE977: +case 0xEB77: +case 0xED77: +case 0xEF77: + +// ROXLD +case 0xE170: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 17; + + src |= (CPU->flag_X & C68K_SR_X) << 8; + res = (src << sft) | (src >> (17 - sft)); + CPU->flag_X = CPU->flag_C = res >> 8; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE3B0: +case 0xE5B0: +case 0xE7B0: +case 0xE9B0: +case 0xEBB0: +case 0xEDB0: +case 0xEFB0: +case 0xE1B1: +case 0xE3B1: +case 0xE5B1: +case 0xE7B1: +case 0xE9B1: +case 0xEBB1: +case 0xEDB1: +case 0xEFB1: +case 0xE1B2: +case 0xE3B2: +case 0xE5B2: +case 0xE7B2: +case 0xE9B2: +case 0xEBB2: +case 0xEDB2: +case 0xEFB2: +case 0xE1B3: +case 0xE3B3: +case 0xE5B3: +case 0xE7B3: +case 0xE9B3: +case 0xEBB3: +case 0xEDB3: +case 0xEFB3: +case 0xE1B4: +case 0xE3B4: +case 0xE5B4: +case 0xE7B4: +case 0xE9B4: +case 0xEBB4: +case 0xEDB4: +case 0xEFB4: +case 0xE1B5: +case 0xE3B5: +case 0xE5B5: +case 0xE7B5: +case 0xE9B5: +case 0xEBB5: +case 0xEDB5: +case 0xEFB5: +case 0xE1B6: +case 0xE3B6: +case 0xE5B6: +case 0xE7B6: +case 0xE9B6: +case 0xEBB6: +case 0xEDB6: +case 0xEFB6: +case 0xE1B7: +case 0xE3B7: +case 0xE5B7: +case 0xE7B7: +case 0xE9B7: +case 0xEBB7: +case 0xEDB7: +case 0xEFB7: + +// ROXLD +case 0xE1B0: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + sft %= 33; + + if (sft != 0) + { + if (sft == 1) res = (src << 1) | ((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1); + else res = (src << sft) | (src >> (33 - sft)) | (((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1) << (sft - 1)); + CPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT; + } + else res = src; + CPU->flag_C = CPU->flag_X; + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = CPU->flag_X; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE338: +case 0xE538: +case 0xE738: +case 0xE938: +case 0xEB38: +case 0xED38: +case 0xEF38: +case 0xE139: +case 0xE339: +case 0xE539: +case 0xE739: +case 0xE939: +case 0xEB39: +case 0xED39: +case 0xEF39: +case 0xE13A: +case 0xE33A: +case 0xE53A: +case 0xE73A: +case 0xE93A: +case 0xEB3A: +case 0xED3A: +case 0xEF3A: +case 0xE13B: +case 0xE33B: +case 0xE53B: +case 0xE73B: +case 0xE93B: +case 0xEB3B: +case 0xED3B: +case 0xEF3B: +case 0xE13C: +case 0xE33C: +case 0xE53C: +case 0xE73C: +case 0xE93C: +case 0xEB3C: +case 0xED3C: +case 0xEF3C: +case 0xE13D: +case 0xE33D: +case 0xE53D: +case 0xE73D: +case 0xE93D: +case 0xEB3D: +case 0xED3D: +case 0xEF3D: +case 0xE13E: +case 0xE33E: +case 0xE53E: +case 0xE73E: +case 0xE93E: +case 0xEB3E: +case 0xED3E: +case 0xEF3E: +case 0xE13F: +case 0xE33F: +case 0xE53F: +case 0xE73F: +case 0xE93F: +case 0xEB3F: +case 0xED3F: +case 0xEF3F: + +// ROLD +case 0xE138: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u8)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft &= 0x07) + { + CPU->flag_C = (src << sft) >> 0; + res = ((src << sft) | (src >> (8 - sft))) & 0x000000FF; + CPU->flag_V = 0; + CPU->flag_N = res >> 0; + CPU->flag_notZ = res; + *(BYTE_OFF + (u8*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 0; + CPU->flag_notZ = src; +} +RET(6) +case 0xE378: +case 0xE578: +case 0xE778: +case 0xE978: +case 0xEB78: +case 0xED78: +case 0xEF78: +case 0xE179: +case 0xE379: +case 0xE579: +case 0xE779: +case 0xE979: +case 0xEB79: +case 0xED79: +case 0xEF79: +case 0xE17A: +case 0xE37A: +case 0xE57A: +case 0xE77A: +case 0xE97A: +case 0xEB7A: +case 0xED7A: +case 0xEF7A: +case 0xE17B: +case 0xE37B: +case 0xE57B: +case 0xE77B: +case 0xE97B: +case 0xEB7B: +case 0xED7B: +case 0xEF7B: +case 0xE17C: +case 0xE37C: +case 0xE57C: +case 0xE77C: +case 0xE97C: +case 0xEB7C: +case 0xED7C: +case 0xEF7C: +case 0xE17D: +case 0xE37D: +case 0xE57D: +case 0xE77D: +case 0xE97D: +case 0xEB7D: +case 0xED7D: +case 0xEF7D: +case 0xE17E: +case 0xE37E: +case 0xE57E: +case 0xE77E: +case 0xE97E: +case 0xEB7E: +case 0xED7E: +case 0xEF7E: +case 0xE17F: +case 0xE37F: +case 0xE57F: +case 0xE77F: +case 0xE97F: +case 0xEB7F: +case 0xED7F: +case 0xEF7F: + +// ROLD +case 0xE178: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u16)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft &= 0x0F) + { + CPU->flag_C = (src << sft) >> 8; + res = ((src << sft) | (src >> (16 - sft))) & 0x0000FFFF; + CPU->flag_V = 0; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + *(WORD_OFF + (u16*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; + RET(6) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 8; + CPU->flag_notZ = src; +} +RET(6) +case 0xE3B8: +case 0xE5B8: +case 0xE7B8: +case 0xE9B8: +case 0xEBB8: +case 0xEDB8: +case 0xEFB8: +case 0xE1B9: +case 0xE3B9: +case 0xE5B9: +case 0xE7B9: +case 0xE9B9: +case 0xEBB9: +case 0xEDB9: +case 0xEFB9: +case 0xE1BA: +case 0xE3BA: +case 0xE5BA: +case 0xE7BA: +case 0xE9BA: +case 0xEBBA: +case 0xEDBA: +case 0xEFBA: +case 0xE1BB: +case 0xE3BB: +case 0xE5BB: +case 0xE7BB: +case 0xE9BB: +case 0xEBBB: +case 0xEDBB: +case 0xEFBB: +case 0xE1BC: +case 0xE3BC: +case 0xE5BC: +case 0xE7BC: +case 0xE9BC: +case 0xEBBC: +case 0xEDBC: +case 0xEFBC: +case 0xE1BD: +case 0xE3BD: +case 0xE5BD: +case 0xE7BD: +case 0xE9BD: +case 0xEBBD: +case 0xEDBD: +case 0xEFBD: +case 0xE1BE: +case 0xE3BE: +case 0xE5BE: +case 0xE7BE: +case 0xE9BE: +case 0xEBBE: +case 0xEDBE: +case 0xEFBE: +case 0xE1BF: +case 0xE3BF: +case 0xE5BF: +case 0xE7BF: +case 0xE9BF: +case 0xEBBF: +case 0xEDBF: +case 0xEFBF: + +// ROLD +case 0xE1B8: +{ + u32 res; + pointer src; + u32 sft; + + sft = CPU->D[(Opcode >> 9) & 7] & 0x3F; + src = (u32)CPU->D[(Opcode >> 0) & 7]; + if (sft) + { + CCnt -= sft * 2; + if (sft &= 0x1F) + { + CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT; + res = (src << sft) | (src >> (32 - sft)); + CPU->flag_V = 0; + CPU->flag_N = res >> 24; + CPU->flag_notZ = res; + *((u32*)(&CPU->D[(Opcode >> 0) & 7])) = res; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; + RET(8) + } + + CPU->flag_V = 0; + CPU->flag_C = 0; + CPU->flag_N = src >> 24; + CPU->flag_notZ = src; +} +RET(8) +case 0xE0D1: +case 0xE0D2: +case 0xE0D3: +case 0xE0D4: +case 0xE0D5: +case 0xE0D6: +case 0xE0D7: + +// ASR +case 0xE0D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE0D9: +case 0xE0DA: +case 0xE0DB: +case 0xE0DC: +case 0xE0DD: +case 0xE0DE: + +// ASR +case 0xE0D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE0E1: +case 0xE0E2: +case 0xE0E3: +case 0xE0E4: +case 0xE0E5: +case 0xE0E6: + +// ASR +case 0xE0E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE0E9: +case 0xE0EA: +case 0xE0EB: +case 0xE0EC: +case 0xE0ED: +case 0xE0EE: +case 0xE0EF: + +// ASR +case 0xE0E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE0F1: +case 0xE0F2: +case 0xE0F3: +case 0xE0F4: +case 0xE0F5: +case 0xE0F6: +case 0xE0F7: + +// ASR +case 0xE0F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ASR +case 0xE0F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ASR +case 0xE0F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ASR +case 0xE0DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ASR +case 0xE0E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src & (1 << 15)); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE2D1: +case 0xE2D2: +case 0xE2D3: +case 0xE2D4: +case 0xE2D5: +case 0xE2D6: +case 0xE2D7: + +// LSR +case 0xE2D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE2D9: +case 0xE2DA: +case 0xE2DB: +case 0xE2DC: +case 0xE2DD: +case 0xE2DE: + +// LSR +case 0xE2D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE2E1: +case 0xE2E2: +case 0xE2E3: +case 0xE2E4: +case 0xE2E5: +case 0xE2E6: + +// LSR +case 0xE2E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE2E9: +case 0xE2EA: +case 0xE2EB: +case 0xE2EC: +case 0xE2ED: +case 0xE2EE: +case 0xE2EF: + +// LSR +case 0xE2E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE2F1: +case 0xE2F2: +case 0xE2F3: +case 0xE2F4: +case 0xE2F5: +case 0xE2F6: +case 0xE2F7: + +// LSR +case 0xE2F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// LSR +case 0xE2F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// LSR +case 0xE2F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// LSR +case 0xE2DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// LSR +case 0xE2E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_N = CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT; + res = src >> 1; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE4D1: +case 0xE4D2: +case 0xE4D3: +case 0xE4D4: +case 0xE4D5: +case 0xE4D6: +case 0xE4D7: + +// ROXR +case 0xE4D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE4D9: +case 0xE4DA: +case 0xE4DB: +case 0xE4DC: +case 0xE4DD: +case 0xE4DE: + +// ROXR +case 0xE4D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE4E1: +case 0xE4E2: +case 0xE4E3: +case 0xE4E4: +case 0xE4E5: +case 0xE4E6: + +// ROXR +case 0xE4E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE4E9: +case 0xE4EA: +case 0xE4EB: +case 0xE4EC: +case 0xE4ED: +case 0xE4EE: +case 0xE4EF: + +// ROXR +case 0xE4E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE4F1: +case 0xE4F2: +case 0xE4F3: +case 0xE4F4: +case 0xE4F5: +case 0xE4F6: +case 0xE4F7: + +// ROXR +case 0xE4F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ROXR +case 0xE4F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ROXR +case 0xE4F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ROXR +case 0xE4DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ROXR +case 0xE4E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << 7); + CPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE6D1: +case 0xE6D2: +case 0xE6D3: +case 0xE6D4: +case 0xE6D5: +case 0xE6D6: +case 0xE6D7: + +// ROR +case 0xE6D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE6D9: +case 0xE6DA: +case 0xE6DB: +case 0xE6DC: +case 0xE6DD: +case 0xE6DE: + +// ROR +case 0xE6D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE6E1: +case 0xE6E2: +case 0xE6E3: +case 0xE6E4: +case 0xE6E5: +case 0xE6E6: + +// ROR +case 0xE6E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE6E9: +case 0xE6EA: +case 0xE6EB: +case 0xE6EC: +case 0xE6ED: +case 0xE6EE: +case 0xE6EF: + +// ROR +case 0xE6E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE6F1: +case 0xE6F2: +case 0xE6F3: +case 0xE6F4: +case 0xE6F5: +case 0xE6F6: +case 0xE6F7: + +// ROR +case 0xE6F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ROR +case 0xE6F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ROR +case 0xE6F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ROR +case 0xE6DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ROR +case 0xE6E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src << C68K_SR_C_SFT; + res = (src >> 1) | (src << 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE1D1: +case 0xE1D2: +case 0xE1D3: +case 0xE1D4: +case 0xE1D5: +case 0xE1D6: +case 0xE1D7: + +// ASL +case 0xE1D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE1D9: +case 0xE1DA: +case 0xE1DB: +case 0xE1DC: +case 0xE1DD: +case 0xE1DE: + +// ASL +case 0xE1D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE1E1: +case 0xE1E2: +case 0xE1E3: +case 0xE1E4: +case 0xE1E5: +case 0xE1E6: + +// ASL +case 0xE1E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE1E9: +case 0xE1EA: +case 0xE1EB: +case 0xE1EC: +case 0xE1ED: +case 0xE1EE: +case 0xE1EF: + +// ASL +case 0xE1E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE1F1: +case 0xE1F2: +case 0xE1F3: +case 0xE1F4: +case 0xE1F5: +case 0xE1F6: +case 0xE1F7: + +// ASL +case 0xE1F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ASL +case 0xE1F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ASL +case 0xE1F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ASL +case 0xE1DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ASL +case 0xE1E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_V = (src ^ res) >> 8; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE3D1: +case 0xE3D2: +case 0xE3D3: +case 0xE3D4: +case 0xE3D5: +case 0xE3D6: +case 0xE3D7: + +// LSL +case 0xE3D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE3D9: +case 0xE3DA: +case 0xE3DB: +case 0xE3DC: +case 0xE3DD: +case 0xE3DE: + +// LSL +case 0xE3D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE3E1: +case 0xE3E2: +case 0xE3E3: +case 0xE3E4: +case 0xE3E5: +case 0xE3E6: + +// LSL +case 0xE3E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE3E9: +case 0xE3EA: +case 0xE3EB: +case 0xE3EC: +case 0xE3ED: +case 0xE3EE: +case 0xE3EF: + +// LSL +case 0xE3E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE3F1: +case 0xE3F2: +case 0xE3F3: +case 0xE3F4: +case 0xE3F5: +case 0xE3F6: +case 0xE3F7: + +// LSL +case 0xE3F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// LSL +case 0xE3F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// LSL +case 0xE3F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// LSL +case 0xE3DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// LSL +case 0xE3E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_X = CPU->flag_C = src >> 7; + res = src << 1; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE5D1: +case 0xE5D2: +case 0xE5D3: +case 0xE5D4: +case 0xE5D5: +case 0xE5D6: +case 0xE5D7: + +// ROXL +case 0xE5D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE5D9: +case 0xE5DA: +case 0xE5DB: +case 0xE5DC: +case 0xE5DD: +case 0xE5DE: + +// ROXL +case 0xE5D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE5E1: +case 0xE5E2: +case 0xE5E3: +case 0xE5E4: +case 0xE5E5: +case 0xE5E6: + +// ROXL +case 0xE5E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE5E9: +case 0xE5EA: +case 0xE5EB: +case 0xE5EC: +case 0xE5ED: +case 0xE5EE: +case 0xE5EF: + +// ROXL +case 0xE5E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE5F1: +case 0xE5F2: +case 0xE5F3: +case 0xE5F4: +case 0xE5F5: +case 0xE5F6: +case 0xE5F7: + +// ROXL +case 0xE5F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ROXL +case 0xE5F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ROXL +case 0xE5F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ROXL +case 0xE5DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ROXL +case 0xE5E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> 8); + CPU->flag_X = CPU->flag_C = src >> 7; + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE7D1: +case 0xE7D2: +case 0xE7D3: +case 0xE7D4: +case 0xE7D5: +case 0xE7D6: +case 0xE7D7: + +// ROL +case 0xE7D0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE7D9: +case 0xE7DA: +case 0xE7DB: +case 0xE7DC: +case 0xE7DD: +case 0xE7DE: + +// ROL +case 0xE7D8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + CPU->A[(Opcode >> 0) & 7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) +case 0xE7E1: +case 0xE7E2: +case 0xE7E3: +case 0xE7E4: +case 0xE7E5: +case 0xE7E6: + +// ROL +case 0xE7E0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] - 2; + CPU->A[(Opcode >> 0) & 7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) +case 0xE7E9: +case 0xE7EA: +case 0xE7EB: +case 0xE7EC: +case 0xE7ED: +case 0xE7EE: +case 0xE7EF: + +// ROL +case 0xE7E8: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7] + (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) +case 0xE7F1: +case 0xE7F2: +case 0xE7F3: +case 0xE7F4: +case 0xE7F5: +case 0xE7F6: +case 0xE7F7: + +// ROL +case 0xE7F0: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[(Opcode >> 0) & 7]; + DECODE_EXT_WORD + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(18) + +// ROL +case 0xE7F8: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)(s16)FETCH_WORD; + PC += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(16) + +// ROL +case 0xE7F9: +{ + u32 adr; + u32 res; + pointer src; + adr = (s32)FETCH_LONG; + PC += 4; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(20) + +// ROL +case 0xE7DF: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7]; + CPU->A[7] += 2; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(12) + +// ROL +case 0xE7E7: +{ + u32 adr; + u32 res; + pointer src; + adr = CPU->A[7] - 2; + CPU->A[7] = adr; + PRE_IO + READ_WORD_F(adr, src) + CPU->flag_V = 0; + CPU->flag_C = src >> 7; + res = (src << 1) | (src >> 15); + CPU->flag_N = res >> 8; + CPU->flag_notZ = res & 0x0000FFFF; + WRITE_WORD_F(adr, res) + POST_IO +} +RET(14) diff --git a/yabause/src/c68k/c68k_opF.inc b/yabause/src/c68k/c68k_opF.inc new file mode 100644 index 0000000000..802c9b31b8 --- /dev/null +++ b/yabause/src/c68k/c68k_opF.inc @@ -0,0 +1,4118 @@ +case 0xF001: +case 0xF002: +case 0xF003: +case 0xF004: +case 0xF005: +case 0xF006: +case 0xF007: +case 0xF008: +case 0xF009: +case 0xF00A: +case 0xF00B: +case 0xF00C: +case 0xF00D: +case 0xF00E: +case 0xF00F: +case 0xF010: +case 0xF011: +case 0xF012: +case 0xF013: +case 0xF014: +case 0xF015: +case 0xF016: +case 0xF017: +case 0xF018: +case 0xF019: +case 0xF01A: +case 0xF01B: +case 0xF01C: +case 0xF01D: +case 0xF01E: +case 0xF01F: +case 0xF020: +case 0xF021: +case 0xF022: +case 0xF023: +case 0xF024: +case 0xF025: +case 0xF026: +case 0xF027: +case 0xF028: +case 0xF029: +case 0xF02A: +case 0xF02B: +case 0xF02C: +case 0xF02D: +case 0xF02E: +case 0xF02F: +case 0xF030: +case 0xF031: +case 0xF032: +case 0xF033: +case 0xF034: +case 0xF035: +case 0xF036: +case 0xF037: +case 0xF038: +case 0xF039: +case 0xF03A: +case 0xF03B: +case 0xF03C: +case 0xF03D: +case 0xF03E: +case 0xF03F: +case 0xF040: +case 0xF041: +case 0xF042: +case 0xF043: +case 0xF044: +case 0xF045: +case 0xF046: +case 0xF047: +case 0xF048: +case 0xF049: +case 0xF04A: +case 0xF04B: +case 0xF04C: +case 0xF04D: +case 0xF04E: +case 0xF04F: +case 0xF050: +case 0xF051: +case 0xF052: +case 0xF053: +case 0xF054: +case 0xF055: +case 0xF056: +case 0xF057: +case 0xF058: +case 0xF059: +case 0xF05A: +case 0xF05B: +case 0xF05C: +case 0xF05D: +case 0xF05E: +case 0xF05F: +case 0xF060: +case 0xF061: +case 0xF062: +case 0xF063: +case 0xF064: +case 0xF065: +case 0xF066: +case 0xF067: +case 0xF068: +case 0xF069: +case 0xF06A: +case 0xF06B: +case 0xF06C: +case 0xF06D: +case 0xF06E: +case 0xF06F: +case 0xF070: +case 0xF071: +case 0xF072: +case 0xF073: +case 0xF074: +case 0xF075: +case 0xF076: +case 0xF077: +case 0xF078: +case 0xF079: +case 0xF07A: +case 0xF07B: +case 0xF07C: +case 0xF07D: +case 0xF07E: +case 0xF07F: +case 0xF080: +case 0xF081: +case 0xF082: +case 0xF083: +case 0xF084: +case 0xF085: +case 0xF086: +case 0xF087: +case 0xF088: +case 0xF089: +case 0xF08A: +case 0xF08B: +case 0xF08C: +case 0xF08D: +case 0xF08E: +case 0xF08F: +case 0xF090: +case 0xF091: +case 0xF092: +case 0xF093: +case 0xF094: +case 0xF095: +case 0xF096: +case 0xF097: +case 0xF098: +case 0xF099: +case 0xF09A: +case 0xF09B: +case 0xF09C: +case 0xF09D: +case 0xF09E: +case 0xF09F: +case 0xF0A0: +case 0xF0A1: +case 0xF0A2: +case 0xF0A3: +case 0xF0A4: +case 0xF0A5: +case 0xF0A6: +case 0xF0A7: +case 0xF0A8: +case 0xF0A9: +case 0xF0AA: +case 0xF0AB: +case 0xF0AC: +case 0xF0AD: +case 0xF0AE: +case 0xF0AF: +case 0xF0B0: +case 0xF0B1: +case 0xF0B2: +case 0xF0B3: +case 0xF0B4: +case 0xF0B5: +case 0xF0B6: +case 0xF0B7: +case 0xF0B8: +case 0xF0B9: +case 0xF0BA: +case 0xF0BB: +case 0xF0BC: +case 0xF0BD: +case 0xF0BE: +case 0xF0BF: +case 0xF0C0: +case 0xF0C1: +case 0xF0C2: +case 0xF0C3: +case 0xF0C4: +case 0xF0C5: +case 0xF0C6: +case 0xF0C7: +case 0xF0C8: +case 0xF0C9: +case 0xF0CA: +case 0xF0CB: +case 0xF0CC: +case 0xF0CD: +case 0xF0CE: +case 0xF0CF: +case 0xF0D0: +case 0xF0D1: +case 0xF0D2: +case 0xF0D3: +case 0xF0D4: +case 0xF0D5: +case 0xF0D6: +case 0xF0D7: +case 0xF0D8: +case 0xF0D9: +case 0xF0DA: +case 0xF0DB: +case 0xF0DC: +case 0xF0DD: +case 0xF0DE: +case 0xF0DF: +case 0xF0E0: +case 0xF0E1: +case 0xF0E2: +case 0xF0E3: +case 0xF0E4: +case 0xF0E5: +case 0xF0E6: +case 0xF0E7: +case 0xF0E8: +case 0xF0E9: +case 0xF0EA: +case 0xF0EB: +case 0xF0EC: +case 0xF0ED: +case 0xF0EE: +case 0xF0EF: +case 0xF0F0: +case 0xF0F1: +case 0xF0F2: +case 0xF0F3: +case 0xF0F4: +case 0xF0F5: +case 0xF0F6: +case 0xF0F7: +case 0xF0F8: +case 0xF0F9: +case 0xF0FA: +case 0xF0FB: +case 0xF0FC: +case 0xF0FD: +case 0xF0FE: +case 0xF0FF: +case 0xF100: +case 0xF101: +case 0xF102: +case 0xF103: +case 0xF104: +case 0xF105: +case 0xF106: +case 0xF107: +case 0xF108: +case 0xF109: +case 0xF10A: +case 0xF10B: +case 0xF10C: +case 0xF10D: +case 0xF10E: +case 0xF10F: +case 0xF110: +case 0xF111: +case 0xF112: +case 0xF113: +case 0xF114: +case 0xF115: +case 0xF116: +case 0xF117: +case 0xF118: +case 0xF119: +case 0xF11A: +case 0xF11B: +case 0xF11C: +case 0xF11D: +case 0xF11E: +case 0xF11F: +case 0xF120: +case 0xF121: +case 0xF122: +case 0xF123: +case 0xF124: +case 0xF125: +case 0xF126: +case 0xF127: +case 0xF128: +case 0xF129: +case 0xF12A: +case 0xF12B: +case 0xF12C: +case 0xF12D: +case 0xF12E: +case 0xF12F: +case 0xF130: +case 0xF131: +case 0xF132: +case 0xF133: +case 0xF134: +case 0xF135: +case 0xF136: +case 0xF137: +case 0xF138: +case 0xF139: +case 0xF13A: +case 0xF13B: +case 0xF13C: +case 0xF13D: +case 0xF13E: +case 0xF13F: +case 0xF140: +case 0xF141: +case 0xF142: +case 0xF143: +case 0xF144: +case 0xF145: +case 0xF146: +case 0xF147: +case 0xF148: +case 0xF149: +case 0xF14A: +case 0xF14B: +case 0xF14C: +case 0xF14D: +case 0xF14E: +case 0xF14F: +case 0xF150: +case 0xF151: +case 0xF152: +case 0xF153: +case 0xF154: +case 0xF155: +case 0xF156: +case 0xF157: +case 0xF158: +case 0xF159: +case 0xF15A: +case 0xF15B: +case 0xF15C: +case 0xF15D: +case 0xF15E: +case 0xF15F: +case 0xF160: +case 0xF161: +case 0xF162: +case 0xF163: +case 0xF164: +case 0xF165: +case 0xF166: +case 0xF167: +case 0xF168: +case 0xF169: +case 0xF16A: +case 0xF16B: +case 0xF16C: +case 0xF16D: +case 0xF16E: +case 0xF16F: +case 0xF170: +case 0xF171: +case 0xF172: +case 0xF173: +case 0xF174: +case 0xF175: +case 0xF176: +case 0xF177: +case 0xF178: +case 0xF179: +case 0xF17A: +case 0xF17B: +case 0xF17C: +case 0xF17D: +case 0xF17E: +case 0xF17F: +case 0xF180: +case 0xF181: +case 0xF182: +case 0xF183: +case 0xF184: +case 0xF185: +case 0xF186: +case 0xF187: +case 0xF188: +case 0xF189: +case 0xF18A: +case 0xF18B: +case 0xF18C: +case 0xF18D: +case 0xF18E: +case 0xF18F: +case 0xF190: +case 0xF191: +case 0xF192: +case 0xF193: +case 0xF194: +case 0xF195: +case 0xF196: +case 0xF197: +case 0xF198: +case 0xF199: +case 0xF19A: +case 0xF19B: +case 0xF19C: +case 0xF19D: +case 0xF19E: +case 0xF19F: +case 0xF1A0: +case 0xF1A1: +case 0xF1A2: +case 0xF1A3: +case 0xF1A4: +case 0xF1A5: +case 0xF1A6: +case 0xF1A7: +case 0xF1A8: +case 0xF1A9: +case 0xF1AA: +case 0xF1AB: +case 0xF1AC: +case 0xF1AD: +case 0xF1AE: +case 0xF1AF: +case 0xF1B0: +case 0xF1B1: +case 0xF1B2: +case 0xF1B3: +case 0xF1B4: +case 0xF1B5: +case 0xF1B6: +case 0xF1B7: +case 0xF1B8: +case 0xF1B9: +case 0xF1BA: +case 0xF1BB: +case 0xF1BC: +case 0xF1BD: +case 0xF1BE: +case 0xF1BF: +case 0xF1C0: +case 0xF1C1: +case 0xF1C2: +case 0xF1C3: +case 0xF1C4: +case 0xF1C5: +case 0xF1C6: +case 0xF1C7: +case 0xF1C8: +case 0xF1C9: +case 0xF1CA: +case 0xF1CB: +case 0xF1CC: +case 0xF1CD: +case 0xF1CE: +case 0xF1CF: +case 0xF1D0: +case 0xF1D1: +case 0xF1D2: +case 0xF1D3: +case 0xF1D4: +case 0xF1D5: +case 0xF1D6: +case 0xF1D7: +case 0xF1D8: +case 0xF1D9: +case 0xF1DA: +case 0xF1DB: +case 0xF1DC: +case 0xF1DD: +case 0xF1DE: +case 0xF1DF: +case 0xF1E0: +case 0xF1E1: +case 0xF1E2: +case 0xF1E3: +case 0xF1E4: +case 0xF1E5: +case 0xF1E6: +case 0xF1E7: +case 0xF1E8: +case 0xF1E9: +case 0xF1EA: +case 0xF1EB: +case 0xF1EC: +case 0xF1ED: +case 0xF1EE: +case 0xF1EF: +case 0xF1F0: +case 0xF1F1: +case 0xF1F2: +case 0xF1F3: +case 0xF1F4: +case 0xF1F5: +case 0xF1F6: +case 0xF1F7: +case 0xF1F8: +case 0xF1F9: +case 0xF1FA: +case 0xF1FB: +case 0xF1FC: +case 0xF1FD: +case 0xF1FE: +case 0xF1FF: +case 0xF200: +case 0xF201: +case 0xF202: +case 0xF203: +case 0xF204: +case 0xF205: +case 0xF206: +case 0xF207: +case 0xF208: +case 0xF209: +case 0xF20A: +case 0xF20B: +case 0xF20C: +case 0xF20D: +case 0xF20E: +case 0xF20F: +case 0xF210: +case 0xF211: +case 0xF212: +case 0xF213: +case 0xF214: +case 0xF215: +case 0xF216: +case 0xF217: +case 0xF218: +case 0xF219: +case 0xF21A: +case 0xF21B: +case 0xF21C: +case 0xF21D: +case 0xF21E: +case 0xF21F: +case 0xF220: +case 0xF221: +case 0xF222: +case 0xF223: +case 0xF224: +case 0xF225: +case 0xF226: +case 0xF227: +case 0xF228: +case 0xF229: +case 0xF22A: +case 0xF22B: +case 0xF22C: +case 0xF22D: +case 0xF22E: +case 0xF22F: +case 0xF230: +case 0xF231: +case 0xF232: +case 0xF233: +case 0xF234: +case 0xF235: +case 0xF236: +case 0xF237: +case 0xF238: +case 0xF239: +case 0xF23A: +case 0xF23B: +case 0xF23C: +case 0xF23D: +case 0xF23E: +case 0xF23F: +case 0xF240: +case 0xF241: +case 0xF242: +case 0xF243: +case 0xF244: +case 0xF245: +case 0xF246: +case 0xF247: +case 0xF248: +case 0xF249: +case 0xF24A: +case 0xF24B: +case 0xF24C: +case 0xF24D: +case 0xF24E: +case 0xF24F: +case 0xF250: +case 0xF251: +case 0xF252: +case 0xF253: +case 0xF254: +case 0xF255: +case 0xF256: +case 0xF257: +case 0xF258: +case 0xF259: +case 0xF25A: +case 0xF25B: +case 0xF25C: +case 0xF25D: +case 0xF25E: +case 0xF25F: +case 0xF260: +case 0xF261: +case 0xF262: +case 0xF263: +case 0xF264: +case 0xF265: +case 0xF266: +case 0xF267: +case 0xF268: +case 0xF269: +case 0xF26A: +case 0xF26B: +case 0xF26C: +case 0xF26D: +case 0xF26E: +case 0xF26F: +case 0xF270: +case 0xF271: +case 0xF272: +case 0xF273: +case 0xF274: +case 0xF275: +case 0xF276: +case 0xF277: +case 0xF278: +case 0xF279: +case 0xF27A: +case 0xF27B: +case 0xF27C: +case 0xF27D: +case 0xF27E: +case 0xF27F: +case 0xF280: +case 0xF281: +case 0xF282: +case 0xF283: +case 0xF284: +case 0xF285: +case 0xF286: +case 0xF287: +case 0xF288: +case 0xF289: +case 0xF28A: +case 0xF28B: +case 0xF28C: +case 0xF28D: +case 0xF28E: +case 0xF28F: +case 0xF290: +case 0xF291: +case 0xF292: +case 0xF293: +case 0xF294: +case 0xF295: +case 0xF296: +case 0xF297: +case 0xF298: +case 0xF299: +case 0xF29A: +case 0xF29B: +case 0xF29C: +case 0xF29D: +case 0xF29E: +case 0xF29F: +case 0xF2A0: +case 0xF2A1: +case 0xF2A2: +case 0xF2A3: +case 0xF2A4: +case 0xF2A5: +case 0xF2A6: +case 0xF2A7: +case 0xF2A8: +case 0xF2A9: +case 0xF2AA: +case 0xF2AB: +case 0xF2AC: +case 0xF2AD: +case 0xF2AE: +case 0xF2AF: +case 0xF2B0: +case 0xF2B1: +case 0xF2B2: +case 0xF2B3: +case 0xF2B4: +case 0xF2B5: +case 0xF2B6: +case 0xF2B7: +case 0xF2B8: +case 0xF2B9: +case 0xF2BA: +case 0xF2BB: +case 0xF2BC: +case 0xF2BD: +case 0xF2BE: +case 0xF2BF: +case 0xF2C0: +case 0xF2C1: +case 0xF2C2: +case 0xF2C3: +case 0xF2C4: +case 0xF2C5: +case 0xF2C6: +case 0xF2C7: +case 0xF2C8: +case 0xF2C9: +case 0xF2CA: +case 0xF2CB: +case 0xF2CC: +case 0xF2CD: +case 0xF2CE: +case 0xF2CF: +case 0xF2D0: +case 0xF2D1: +case 0xF2D2: +case 0xF2D3: +case 0xF2D4: +case 0xF2D5: +case 0xF2D6: +case 0xF2D7: +case 0xF2D8: +case 0xF2D9: +case 0xF2DA: +case 0xF2DB: +case 0xF2DC: +case 0xF2DD: +case 0xF2DE: +case 0xF2DF: +case 0xF2E0: +case 0xF2E1: +case 0xF2E2: +case 0xF2E3: +case 0xF2E4: +case 0xF2E5: +case 0xF2E6: +case 0xF2E7: +case 0xF2E8: +case 0xF2E9: +case 0xF2EA: +case 0xF2EB: +case 0xF2EC: +case 0xF2ED: +case 0xF2EE: +case 0xF2EF: +case 0xF2F0: +case 0xF2F1: +case 0xF2F2: +case 0xF2F3: +case 0xF2F4: +case 0xF2F5: +case 0xF2F6: +case 0xF2F7: +case 0xF2F8: +case 0xF2F9: +case 0xF2FA: +case 0xF2FB: +case 0xF2FC: +case 0xF2FD: +case 0xF2FE: +case 0xF2FF: +case 0xF300: +case 0xF301: +case 0xF302: +case 0xF303: +case 0xF304: +case 0xF305: +case 0xF306: +case 0xF307: +case 0xF308: +case 0xF309: +case 0xF30A: +case 0xF30B: +case 0xF30C: +case 0xF30D: +case 0xF30E: +case 0xF30F: +case 0xF310: +case 0xF311: +case 0xF312: +case 0xF313: +case 0xF314: +case 0xF315: +case 0xF316: +case 0xF317: +case 0xF318: +case 0xF319: +case 0xF31A: +case 0xF31B: +case 0xF31C: +case 0xF31D: +case 0xF31E: +case 0xF31F: +case 0xF320: +case 0xF321: +case 0xF322: +case 0xF323: +case 0xF324: +case 0xF325: +case 0xF326: +case 0xF327: +case 0xF328: +case 0xF329: +case 0xF32A: +case 0xF32B: +case 0xF32C: +case 0xF32D: +case 0xF32E: +case 0xF32F: +case 0xF330: +case 0xF331: +case 0xF332: +case 0xF333: +case 0xF334: +case 0xF335: +case 0xF336: +case 0xF337: +case 0xF338: +case 0xF339: +case 0xF33A: +case 0xF33B: +case 0xF33C: +case 0xF33D: +case 0xF33E: +case 0xF33F: +case 0xF340: +case 0xF341: +case 0xF342: +case 0xF343: +case 0xF344: +case 0xF345: +case 0xF346: +case 0xF347: +case 0xF348: +case 0xF349: +case 0xF34A: +case 0xF34B: +case 0xF34C: +case 0xF34D: +case 0xF34E: +case 0xF34F: +case 0xF350: +case 0xF351: +case 0xF352: +case 0xF353: +case 0xF354: +case 0xF355: +case 0xF356: +case 0xF357: +case 0xF358: +case 0xF359: +case 0xF35A: +case 0xF35B: +case 0xF35C: +case 0xF35D: +case 0xF35E: +case 0xF35F: +case 0xF360: +case 0xF361: +case 0xF362: +case 0xF363: +case 0xF364: +case 0xF365: +case 0xF366: +case 0xF367: +case 0xF368: +case 0xF369: +case 0xF36A: +case 0xF36B: +case 0xF36C: +case 0xF36D: +case 0xF36E: +case 0xF36F: +case 0xF370: +case 0xF371: +case 0xF372: +case 0xF373: +case 0xF374: +case 0xF375: +case 0xF376: +case 0xF377: +case 0xF378: +case 0xF379: +case 0xF37A: +case 0xF37B: +case 0xF37C: +case 0xF37D: +case 0xF37E: +case 0xF37F: +case 0xF380: +case 0xF381: +case 0xF382: +case 0xF383: +case 0xF384: +case 0xF385: +case 0xF386: +case 0xF387: +case 0xF388: +case 0xF389: +case 0xF38A: +case 0xF38B: +case 0xF38C: +case 0xF38D: +case 0xF38E: +case 0xF38F: +case 0xF390: +case 0xF391: +case 0xF392: +case 0xF393: +case 0xF394: +case 0xF395: +case 0xF396: +case 0xF397: +case 0xF398: +case 0xF399: +case 0xF39A: +case 0xF39B: +case 0xF39C: +case 0xF39D: +case 0xF39E: +case 0xF39F: +case 0xF3A0: +case 0xF3A1: +case 0xF3A2: +case 0xF3A3: +case 0xF3A4: +case 0xF3A5: +case 0xF3A6: +case 0xF3A7: +case 0xF3A8: +case 0xF3A9: +case 0xF3AA: +case 0xF3AB: +case 0xF3AC: +case 0xF3AD: +case 0xF3AE: +case 0xF3AF: +case 0xF3B0: +case 0xF3B1: +case 0xF3B2: +case 0xF3B3: +case 0xF3B4: +case 0xF3B5: +case 0xF3B6: +case 0xF3B7: +case 0xF3B8: +case 0xF3B9: +case 0xF3BA: +case 0xF3BB: +case 0xF3BC: +case 0xF3BD: +case 0xF3BE: +case 0xF3BF: +case 0xF3C0: +case 0xF3C1: +case 0xF3C2: +case 0xF3C3: +case 0xF3C4: +case 0xF3C5: +case 0xF3C6: +case 0xF3C7: +case 0xF3C8: +case 0xF3C9: +case 0xF3CA: +case 0xF3CB: +case 0xF3CC: +case 0xF3CD: +case 0xF3CE: +case 0xF3CF: +case 0xF3D0: +case 0xF3D1: +case 0xF3D2: +case 0xF3D3: +case 0xF3D4: +case 0xF3D5: +case 0xF3D6: +case 0xF3D7: +case 0xF3D8: +case 0xF3D9: +case 0xF3DA: +case 0xF3DB: +case 0xF3DC: +case 0xF3DD: +case 0xF3DE: +case 0xF3DF: +case 0xF3E0: +case 0xF3E1: +case 0xF3E2: +case 0xF3E3: +case 0xF3E4: +case 0xF3E5: +case 0xF3E6: +case 0xF3E7: +case 0xF3E8: +case 0xF3E9: +case 0xF3EA: +case 0xF3EB: +case 0xF3EC: +case 0xF3ED: +case 0xF3EE: +case 0xF3EF: +case 0xF3F0: +case 0xF3F1: +case 0xF3F2: +case 0xF3F3: +case 0xF3F4: +case 0xF3F5: +case 0xF3F6: +case 0xF3F7: +case 0xF3F8: +case 0xF3F9: +case 0xF3FA: +case 0xF3FB: +case 0xF3FC: +case 0xF3FD: +case 0xF3FE: +case 0xF3FF: +case 0xF400: +case 0xF401: +case 0xF402: +case 0xF403: +case 0xF404: +case 0xF405: +case 0xF406: +case 0xF407: +case 0xF408: +case 0xF409: +case 0xF40A: +case 0xF40B: +case 0xF40C: +case 0xF40D: +case 0xF40E: +case 0xF40F: +case 0xF410: +case 0xF411: +case 0xF412: +case 0xF413: +case 0xF414: +case 0xF415: +case 0xF416: +case 0xF417: +case 0xF418: +case 0xF419: +case 0xF41A: +case 0xF41B: +case 0xF41C: +case 0xF41D: +case 0xF41E: +case 0xF41F: +case 0xF420: +case 0xF421: +case 0xF422: +case 0xF423: +case 0xF424: +case 0xF425: +case 0xF426: +case 0xF427: +case 0xF428: +case 0xF429: +case 0xF42A: +case 0xF42B: +case 0xF42C: +case 0xF42D: +case 0xF42E: +case 0xF42F: +case 0xF430: +case 0xF431: +case 0xF432: +case 0xF433: +case 0xF434: +case 0xF435: +case 0xF436: +case 0xF437: +case 0xF438: +case 0xF439: +case 0xF43A: +case 0xF43B: +case 0xF43C: +case 0xF43D: +case 0xF43E: +case 0xF43F: +case 0xF440: +case 0xF441: +case 0xF442: +case 0xF443: +case 0xF444: +case 0xF445: +case 0xF446: +case 0xF447: +case 0xF448: +case 0xF449: +case 0xF44A: +case 0xF44B: +case 0xF44C: +case 0xF44D: +case 0xF44E: +case 0xF44F: +case 0xF450: +case 0xF451: +case 0xF452: +case 0xF453: +case 0xF454: +case 0xF455: +case 0xF456: +case 0xF457: +case 0xF458: +case 0xF459: +case 0xF45A: +case 0xF45B: +case 0xF45C: +case 0xF45D: +case 0xF45E: +case 0xF45F: +case 0xF460: +case 0xF461: +case 0xF462: +case 0xF463: +case 0xF464: +case 0xF465: +case 0xF466: +case 0xF467: +case 0xF468: +case 0xF469: +case 0xF46A: +case 0xF46B: +case 0xF46C: +case 0xF46D: +case 0xF46E: +case 0xF46F: +case 0xF470: +case 0xF471: +case 0xF472: +case 0xF473: +case 0xF474: +case 0xF475: +case 0xF476: +case 0xF477: +case 0xF478: +case 0xF479: +case 0xF47A: +case 0xF47B: +case 0xF47C: +case 0xF47D: +case 0xF47E: +case 0xF47F: +case 0xF480: +case 0xF481: +case 0xF482: +case 0xF483: +case 0xF484: +case 0xF485: +case 0xF486: +case 0xF487: +case 0xF488: +case 0xF489: +case 0xF48A: +case 0xF48B: +case 0xF48C: +case 0xF48D: +case 0xF48E: +case 0xF48F: +case 0xF490: +case 0xF491: +case 0xF492: +case 0xF493: +case 0xF494: +case 0xF495: +case 0xF496: +case 0xF497: +case 0xF498: +case 0xF499: +case 0xF49A: +case 0xF49B: +case 0xF49C: +case 0xF49D: +case 0xF49E: +case 0xF49F: +case 0xF4A0: +case 0xF4A1: +case 0xF4A2: +case 0xF4A3: +case 0xF4A4: +case 0xF4A5: +case 0xF4A6: +case 0xF4A7: +case 0xF4A8: +case 0xF4A9: +case 0xF4AA: +case 0xF4AB: +case 0xF4AC: +case 0xF4AD: +case 0xF4AE: +case 0xF4AF: +case 0xF4B0: +case 0xF4B1: +case 0xF4B2: +case 0xF4B3: +case 0xF4B4: +case 0xF4B5: +case 0xF4B6: +case 0xF4B7: +case 0xF4B8: +case 0xF4B9: +case 0xF4BA: +case 0xF4BB: +case 0xF4BC: +case 0xF4BD: +case 0xF4BE: +case 0xF4BF: +case 0xF4C0: +case 0xF4C1: +case 0xF4C2: +case 0xF4C3: +case 0xF4C4: +case 0xF4C5: +case 0xF4C6: +case 0xF4C7: +case 0xF4C8: +case 0xF4C9: +case 0xF4CA: +case 0xF4CB: +case 0xF4CC: +case 0xF4CD: +case 0xF4CE: +case 0xF4CF: +case 0xF4D0: +case 0xF4D1: +case 0xF4D2: +case 0xF4D3: +case 0xF4D4: +case 0xF4D5: +case 0xF4D6: +case 0xF4D7: +case 0xF4D8: +case 0xF4D9: +case 0xF4DA: +case 0xF4DB: +case 0xF4DC: +case 0xF4DD: +case 0xF4DE: +case 0xF4DF: +case 0xF4E0: +case 0xF4E1: +case 0xF4E2: +case 0xF4E3: +case 0xF4E4: +case 0xF4E5: +case 0xF4E6: +case 0xF4E7: +case 0xF4E8: +case 0xF4E9: +case 0xF4EA: +case 0xF4EB: +case 0xF4EC: +case 0xF4ED: +case 0xF4EE: +case 0xF4EF: +case 0xF4F0: +case 0xF4F1: +case 0xF4F2: +case 0xF4F3: +case 0xF4F4: +case 0xF4F5: +case 0xF4F6: +case 0xF4F7: +case 0xF4F8: +case 0xF4F9: +case 0xF4FA: +case 0xF4FB: +case 0xF4FC: +case 0xF4FD: +case 0xF4FE: +case 0xF4FF: +case 0xF500: +case 0xF501: +case 0xF502: +case 0xF503: +case 0xF504: +case 0xF505: +case 0xF506: +case 0xF507: +case 0xF508: +case 0xF509: +case 0xF50A: +case 0xF50B: +case 0xF50C: +case 0xF50D: +case 0xF50E: +case 0xF50F: +case 0xF510: +case 0xF511: +case 0xF512: +case 0xF513: +case 0xF514: +case 0xF515: +case 0xF516: +case 0xF517: +case 0xF518: +case 0xF519: +case 0xF51A: +case 0xF51B: +case 0xF51C: +case 0xF51D: +case 0xF51E: +case 0xF51F: +case 0xF520: +case 0xF521: +case 0xF522: +case 0xF523: +case 0xF524: +case 0xF525: +case 0xF526: +case 0xF527: +case 0xF528: +case 0xF529: +case 0xF52A: +case 0xF52B: +case 0xF52C: +case 0xF52D: +case 0xF52E: +case 0xF52F: +case 0xF530: +case 0xF531: +case 0xF532: +case 0xF533: +case 0xF534: +case 0xF535: +case 0xF536: +case 0xF537: +case 0xF538: +case 0xF539: +case 0xF53A: +case 0xF53B: +case 0xF53C: +case 0xF53D: +case 0xF53E: +case 0xF53F: +case 0xF540: +case 0xF541: +case 0xF542: +case 0xF543: +case 0xF544: +case 0xF545: +case 0xF546: +case 0xF547: +case 0xF548: +case 0xF549: +case 0xF54A: +case 0xF54B: +case 0xF54C: +case 0xF54D: +case 0xF54E: +case 0xF54F: +case 0xF550: +case 0xF551: +case 0xF552: +case 0xF553: +case 0xF554: +case 0xF555: +case 0xF556: +case 0xF557: +case 0xF558: +case 0xF559: +case 0xF55A: +case 0xF55B: +case 0xF55C: +case 0xF55D: +case 0xF55E: +case 0xF55F: +case 0xF560: +case 0xF561: +case 0xF562: +case 0xF563: +case 0xF564: +case 0xF565: +case 0xF566: +case 0xF567: +case 0xF568: +case 0xF569: +case 0xF56A: +case 0xF56B: +case 0xF56C: +case 0xF56D: +case 0xF56E: +case 0xF56F: +case 0xF570: +case 0xF571: +case 0xF572: +case 0xF573: +case 0xF574: +case 0xF575: +case 0xF576: +case 0xF577: +case 0xF578: +case 0xF579: +case 0xF57A: +case 0xF57B: +case 0xF57C: +case 0xF57D: +case 0xF57E: +case 0xF57F: +case 0xF580: +case 0xF581: +case 0xF582: +case 0xF583: +case 0xF584: +case 0xF585: +case 0xF586: +case 0xF587: +case 0xF588: +case 0xF589: +case 0xF58A: +case 0xF58B: +case 0xF58C: +case 0xF58D: +case 0xF58E: +case 0xF58F: +case 0xF590: +case 0xF591: +case 0xF592: +case 0xF593: +case 0xF594: +case 0xF595: +case 0xF596: +case 0xF597: +case 0xF598: +case 0xF599: +case 0xF59A: +case 0xF59B: +case 0xF59C: +case 0xF59D: +case 0xF59E: +case 0xF59F: +case 0xF5A0: +case 0xF5A1: +case 0xF5A2: +case 0xF5A3: +case 0xF5A4: +case 0xF5A5: +case 0xF5A6: +case 0xF5A7: +case 0xF5A8: +case 0xF5A9: +case 0xF5AA: +case 0xF5AB: +case 0xF5AC: +case 0xF5AD: +case 0xF5AE: +case 0xF5AF: +case 0xF5B0: +case 0xF5B1: +case 0xF5B2: +case 0xF5B3: +case 0xF5B4: +case 0xF5B5: +case 0xF5B6: +case 0xF5B7: +case 0xF5B8: +case 0xF5B9: +case 0xF5BA: +case 0xF5BB: +case 0xF5BC: +case 0xF5BD: +case 0xF5BE: +case 0xF5BF: +case 0xF5C0: +case 0xF5C1: +case 0xF5C2: +case 0xF5C3: +case 0xF5C4: +case 0xF5C5: +case 0xF5C6: +case 0xF5C7: +case 0xF5C8: +case 0xF5C9: +case 0xF5CA: +case 0xF5CB: +case 0xF5CC: +case 0xF5CD: +case 0xF5CE: +case 0xF5CF: +case 0xF5D0: +case 0xF5D1: +case 0xF5D2: +case 0xF5D3: +case 0xF5D4: +case 0xF5D5: +case 0xF5D6: +case 0xF5D7: +case 0xF5D8: +case 0xF5D9: +case 0xF5DA: +case 0xF5DB: +case 0xF5DC: +case 0xF5DD: +case 0xF5DE: +case 0xF5DF: +case 0xF5E0: +case 0xF5E1: +case 0xF5E2: +case 0xF5E3: +case 0xF5E4: +case 0xF5E5: +case 0xF5E6: +case 0xF5E7: +case 0xF5E8: +case 0xF5E9: +case 0xF5EA: +case 0xF5EB: +case 0xF5EC: +case 0xF5ED: +case 0xF5EE: +case 0xF5EF: +case 0xF5F0: +case 0xF5F1: +case 0xF5F2: +case 0xF5F3: +case 0xF5F4: +case 0xF5F5: +case 0xF5F6: +case 0xF5F7: +case 0xF5F8: +case 0xF5F9: +case 0xF5FA: +case 0xF5FB: +case 0xF5FC: +case 0xF5FD: +case 0xF5FE: +case 0xF5FF: +case 0xF600: +case 0xF601: +case 0xF602: +case 0xF603: +case 0xF604: +case 0xF605: +case 0xF606: +case 0xF607: +case 0xF608: +case 0xF609: +case 0xF60A: +case 0xF60B: +case 0xF60C: +case 0xF60D: +case 0xF60E: +case 0xF60F: +case 0xF610: +case 0xF611: +case 0xF612: +case 0xF613: +case 0xF614: +case 0xF615: +case 0xF616: +case 0xF617: +case 0xF618: +case 0xF619: +case 0xF61A: +case 0xF61B: +case 0xF61C: +case 0xF61D: +case 0xF61E: +case 0xF61F: +case 0xF620: +case 0xF621: +case 0xF622: +case 0xF623: +case 0xF624: +case 0xF625: +case 0xF626: +case 0xF627: +case 0xF628: +case 0xF629: +case 0xF62A: +case 0xF62B: +case 0xF62C: +case 0xF62D: +case 0xF62E: +case 0xF62F: +case 0xF630: +case 0xF631: +case 0xF632: +case 0xF633: +case 0xF634: +case 0xF635: +case 0xF636: +case 0xF637: +case 0xF638: +case 0xF639: +case 0xF63A: +case 0xF63B: +case 0xF63C: +case 0xF63D: +case 0xF63E: +case 0xF63F: +case 0xF640: +case 0xF641: +case 0xF642: +case 0xF643: +case 0xF644: +case 0xF645: +case 0xF646: +case 0xF647: +case 0xF648: +case 0xF649: +case 0xF64A: +case 0xF64B: +case 0xF64C: +case 0xF64D: +case 0xF64E: +case 0xF64F: +case 0xF650: +case 0xF651: +case 0xF652: +case 0xF653: +case 0xF654: +case 0xF655: +case 0xF656: +case 0xF657: +case 0xF658: +case 0xF659: +case 0xF65A: +case 0xF65B: +case 0xF65C: +case 0xF65D: +case 0xF65E: +case 0xF65F: +case 0xF660: +case 0xF661: +case 0xF662: +case 0xF663: +case 0xF664: +case 0xF665: +case 0xF666: +case 0xF667: +case 0xF668: +case 0xF669: +case 0xF66A: +case 0xF66B: +case 0xF66C: +case 0xF66D: +case 0xF66E: +case 0xF66F: +case 0xF670: +case 0xF671: +case 0xF672: +case 0xF673: +case 0xF674: +case 0xF675: +case 0xF676: +case 0xF677: +case 0xF678: +case 0xF679: +case 0xF67A: +case 0xF67B: +case 0xF67C: +case 0xF67D: +case 0xF67E: +case 0xF67F: +case 0xF680: +case 0xF681: +case 0xF682: +case 0xF683: +case 0xF684: +case 0xF685: +case 0xF686: +case 0xF687: +case 0xF688: +case 0xF689: +case 0xF68A: +case 0xF68B: +case 0xF68C: +case 0xF68D: +case 0xF68E: +case 0xF68F: +case 0xF690: +case 0xF691: +case 0xF692: +case 0xF693: +case 0xF694: +case 0xF695: +case 0xF696: +case 0xF697: +case 0xF698: +case 0xF699: +case 0xF69A: +case 0xF69B: +case 0xF69C: +case 0xF69D: +case 0xF69E: +case 0xF69F: +case 0xF6A0: +case 0xF6A1: +case 0xF6A2: +case 0xF6A3: +case 0xF6A4: +case 0xF6A5: +case 0xF6A6: +case 0xF6A7: +case 0xF6A8: +case 0xF6A9: +case 0xF6AA: +case 0xF6AB: +case 0xF6AC: +case 0xF6AD: +case 0xF6AE: +case 0xF6AF: +case 0xF6B0: +case 0xF6B1: +case 0xF6B2: +case 0xF6B3: +case 0xF6B4: +case 0xF6B5: +case 0xF6B6: +case 0xF6B7: +case 0xF6B8: +case 0xF6B9: +case 0xF6BA: +case 0xF6BB: +case 0xF6BC: +case 0xF6BD: +case 0xF6BE: +case 0xF6BF: +case 0xF6C0: +case 0xF6C1: +case 0xF6C2: +case 0xF6C3: +case 0xF6C4: +case 0xF6C5: +case 0xF6C6: +case 0xF6C7: +case 0xF6C8: +case 0xF6C9: +case 0xF6CA: +case 0xF6CB: +case 0xF6CC: +case 0xF6CD: +case 0xF6CE: +case 0xF6CF: +case 0xF6D0: +case 0xF6D1: +case 0xF6D2: +case 0xF6D3: +case 0xF6D4: +case 0xF6D5: +case 0xF6D6: +case 0xF6D7: +case 0xF6D8: +case 0xF6D9: +case 0xF6DA: +case 0xF6DB: +case 0xF6DC: +case 0xF6DD: +case 0xF6DE: +case 0xF6DF: +case 0xF6E0: +case 0xF6E1: +case 0xF6E2: +case 0xF6E3: +case 0xF6E4: +case 0xF6E5: +case 0xF6E6: +case 0xF6E7: +case 0xF6E8: +case 0xF6E9: +case 0xF6EA: +case 0xF6EB: +case 0xF6EC: +case 0xF6ED: +case 0xF6EE: +case 0xF6EF: +case 0xF6F0: +case 0xF6F1: +case 0xF6F2: +case 0xF6F3: +case 0xF6F4: +case 0xF6F5: +case 0xF6F6: +case 0xF6F7: +case 0xF6F8: +case 0xF6F9: +case 0xF6FA: +case 0xF6FB: +case 0xF6FC: +case 0xF6FD: +case 0xF6FE: +case 0xF6FF: +case 0xF700: +case 0xF701: +case 0xF702: +case 0xF703: +case 0xF704: +case 0xF705: +case 0xF706: +case 0xF707: +case 0xF708: +case 0xF709: +case 0xF70A: +case 0xF70B: +case 0xF70C: +case 0xF70D: +case 0xF70E: +case 0xF70F: +case 0xF710: +case 0xF711: +case 0xF712: +case 0xF713: +case 0xF714: +case 0xF715: +case 0xF716: +case 0xF717: +case 0xF718: +case 0xF719: +case 0xF71A: +case 0xF71B: +case 0xF71C: +case 0xF71D: +case 0xF71E: +case 0xF71F: +case 0xF720: +case 0xF721: +case 0xF722: +case 0xF723: +case 0xF724: +case 0xF725: +case 0xF726: +case 0xF727: +case 0xF728: +case 0xF729: +case 0xF72A: +case 0xF72B: +case 0xF72C: +case 0xF72D: +case 0xF72E: +case 0xF72F: +case 0xF730: +case 0xF731: +case 0xF732: +case 0xF733: +case 0xF734: +case 0xF735: +case 0xF736: +case 0xF737: +case 0xF738: +case 0xF739: +case 0xF73A: +case 0xF73B: +case 0xF73C: +case 0xF73D: +case 0xF73E: +case 0xF73F: +case 0xF740: +case 0xF741: +case 0xF742: +case 0xF743: +case 0xF744: +case 0xF745: +case 0xF746: +case 0xF747: +case 0xF748: +case 0xF749: +case 0xF74A: +case 0xF74B: +case 0xF74C: +case 0xF74D: +case 0xF74E: +case 0xF74F: +case 0xF750: +case 0xF751: +case 0xF752: +case 0xF753: +case 0xF754: +case 0xF755: +case 0xF756: +case 0xF757: +case 0xF758: +case 0xF759: +case 0xF75A: +case 0xF75B: +case 0xF75C: +case 0xF75D: +case 0xF75E: +case 0xF75F: +case 0xF760: +case 0xF761: +case 0xF762: +case 0xF763: +case 0xF764: +case 0xF765: +case 0xF766: +case 0xF767: +case 0xF768: +case 0xF769: +case 0xF76A: +case 0xF76B: +case 0xF76C: +case 0xF76D: +case 0xF76E: +case 0xF76F: +case 0xF770: +case 0xF771: +case 0xF772: +case 0xF773: +case 0xF774: +case 0xF775: +case 0xF776: +case 0xF777: +case 0xF778: +case 0xF779: +case 0xF77A: +case 0xF77B: +case 0xF77C: +case 0xF77D: +case 0xF77E: +case 0xF77F: +case 0xF780: +case 0xF781: +case 0xF782: +case 0xF783: +case 0xF784: +case 0xF785: +case 0xF786: +case 0xF787: +case 0xF788: +case 0xF789: +case 0xF78A: +case 0xF78B: +case 0xF78C: +case 0xF78D: +case 0xF78E: +case 0xF78F: +case 0xF790: +case 0xF791: +case 0xF792: +case 0xF793: +case 0xF794: +case 0xF795: +case 0xF796: +case 0xF797: +case 0xF798: +case 0xF799: +case 0xF79A: +case 0xF79B: +case 0xF79C: +case 0xF79D: +case 0xF79E: +case 0xF79F: +case 0xF7A0: +case 0xF7A1: +case 0xF7A2: +case 0xF7A3: +case 0xF7A4: +case 0xF7A5: +case 0xF7A6: +case 0xF7A7: +case 0xF7A8: +case 0xF7A9: +case 0xF7AA: +case 0xF7AB: +case 0xF7AC: +case 0xF7AD: +case 0xF7AE: +case 0xF7AF: +case 0xF7B0: +case 0xF7B1: +case 0xF7B2: +case 0xF7B3: +case 0xF7B4: +case 0xF7B5: +case 0xF7B6: +case 0xF7B7: +case 0xF7B8: +case 0xF7B9: +case 0xF7BA: +case 0xF7BB: +case 0xF7BC: +case 0xF7BD: +case 0xF7BE: +case 0xF7BF: +case 0xF7C0: +case 0xF7C1: +case 0xF7C2: +case 0xF7C3: +case 0xF7C4: +case 0xF7C5: +case 0xF7C6: +case 0xF7C7: +case 0xF7C8: +case 0xF7C9: +case 0xF7CA: +case 0xF7CB: +case 0xF7CC: +case 0xF7CD: +case 0xF7CE: +case 0xF7CF: +case 0xF7D0: +case 0xF7D1: +case 0xF7D2: +case 0xF7D3: +case 0xF7D4: +case 0xF7D5: +case 0xF7D6: +case 0xF7D7: +case 0xF7D8: +case 0xF7D9: +case 0xF7DA: +case 0xF7DB: +case 0xF7DC: +case 0xF7DD: +case 0xF7DE: +case 0xF7DF: +case 0xF7E0: +case 0xF7E1: +case 0xF7E2: +case 0xF7E3: +case 0xF7E4: +case 0xF7E5: +case 0xF7E6: +case 0xF7E7: +case 0xF7E8: +case 0xF7E9: +case 0xF7EA: +case 0xF7EB: +case 0xF7EC: +case 0xF7ED: +case 0xF7EE: +case 0xF7EF: +case 0xF7F0: +case 0xF7F1: +case 0xF7F2: +case 0xF7F3: +case 0xF7F4: +case 0xF7F5: +case 0xF7F6: +case 0xF7F7: +case 0xF7F8: +case 0xF7F9: +case 0xF7FA: +case 0xF7FB: +case 0xF7FC: +case 0xF7FD: +case 0xF7FE: +case 0xF7FF: +case 0xF800: +case 0xF801: +case 0xF802: +case 0xF803: +case 0xF804: +case 0xF805: +case 0xF806: +case 0xF807: +case 0xF808: +case 0xF809: +case 0xF80A: +case 0xF80B: +case 0xF80C: +case 0xF80D: +case 0xF80E: +case 0xF80F: +case 0xF810: +case 0xF811: +case 0xF812: +case 0xF813: +case 0xF814: +case 0xF815: +case 0xF816: +case 0xF817: +case 0xF818: +case 0xF819: +case 0xF81A: +case 0xF81B: +case 0xF81C: +case 0xF81D: +case 0xF81E: +case 0xF81F: +case 0xF820: +case 0xF821: +case 0xF822: +case 0xF823: +case 0xF824: +case 0xF825: +case 0xF826: +case 0xF827: +case 0xF828: +case 0xF829: +case 0xF82A: +case 0xF82B: +case 0xF82C: +case 0xF82D: +case 0xF82E: +case 0xF82F: +case 0xF830: +case 0xF831: +case 0xF832: +case 0xF833: +case 0xF834: +case 0xF835: +case 0xF836: +case 0xF837: +case 0xF838: +case 0xF839: +case 0xF83A: +case 0xF83B: +case 0xF83C: +case 0xF83D: +case 0xF83E: +case 0xF83F: +case 0xF840: +case 0xF841: +case 0xF842: +case 0xF843: +case 0xF844: +case 0xF845: +case 0xF846: +case 0xF847: +case 0xF848: +case 0xF849: +case 0xF84A: +case 0xF84B: +case 0xF84C: +case 0xF84D: +case 0xF84E: +case 0xF84F: +case 0xF850: +case 0xF851: +case 0xF852: +case 0xF853: +case 0xF854: +case 0xF855: +case 0xF856: +case 0xF857: +case 0xF858: +case 0xF859: +case 0xF85A: +case 0xF85B: +case 0xF85C: +case 0xF85D: +case 0xF85E: +case 0xF85F: +case 0xF860: +case 0xF861: +case 0xF862: +case 0xF863: +case 0xF864: +case 0xF865: +case 0xF866: +case 0xF867: +case 0xF868: +case 0xF869: +case 0xF86A: +case 0xF86B: +case 0xF86C: +case 0xF86D: +case 0xF86E: +case 0xF86F: +case 0xF870: +case 0xF871: +case 0xF872: +case 0xF873: +case 0xF874: +case 0xF875: +case 0xF876: +case 0xF877: +case 0xF878: +case 0xF879: +case 0xF87A: +case 0xF87B: +case 0xF87C: +case 0xF87D: +case 0xF87E: +case 0xF87F: +case 0xF880: +case 0xF881: +case 0xF882: +case 0xF883: +case 0xF884: +case 0xF885: +case 0xF886: +case 0xF887: +case 0xF888: +case 0xF889: +case 0xF88A: +case 0xF88B: +case 0xF88C: +case 0xF88D: +case 0xF88E: +case 0xF88F: +case 0xF890: +case 0xF891: +case 0xF892: +case 0xF893: +case 0xF894: +case 0xF895: +case 0xF896: +case 0xF897: +case 0xF898: +case 0xF899: +case 0xF89A: +case 0xF89B: +case 0xF89C: +case 0xF89D: +case 0xF89E: +case 0xF89F: +case 0xF8A0: +case 0xF8A1: +case 0xF8A2: +case 0xF8A3: +case 0xF8A4: +case 0xF8A5: +case 0xF8A6: +case 0xF8A7: +case 0xF8A8: +case 0xF8A9: +case 0xF8AA: +case 0xF8AB: +case 0xF8AC: +case 0xF8AD: +case 0xF8AE: +case 0xF8AF: +case 0xF8B0: +case 0xF8B1: +case 0xF8B2: +case 0xF8B3: +case 0xF8B4: +case 0xF8B5: +case 0xF8B6: +case 0xF8B7: +case 0xF8B8: +case 0xF8B9: +case 0xF8BA: +case 0xF8BB: +case 0xF8BC: +case 0xF8BD: +case 0xF8BE: +case 0xF8BF: +case 0xF8C0: +case 0xF8C1: +case 0xF8C2: +case 0xF8C3: +case 0xF8C4: +case 0xF8C5: +case 0xF8C6: +case 0xF8C7: +case 0xF8C8: +case 0xF8C9: +case 0xF8CA: +case 0xF8CB: +case 0xF8CC: +case 0xF8CD: +case 0xF8CE: +case 0xF8CF: +case 0xF8D0: +case 0xF8D1: +case 0xF8D2: +case 0xF8D3: +case 0xF8D4: +case 0xF8D5: +case 0xF8D6: +case 0xF8D7: +case 0xF8D8: +case 0xF8D9: +case 0xF8DA: +case 0xF8DB: +case 0xF8DC: +case 0xF8DD: +case 0xF8DE: +case 0xF8DF: +case 0xF8E0: +case 0xF8E1: +case 0xF8E2: +case 0xF8E3: +case 0xF8E4: +case 0xF8E5: +case 0xF8E6: +case 0xF8E7: +case 0xF8E8: +case 0xF8E9: +case 0xF8EA: +case 0xF8EB: +case 0xF8EC: +case 0xF8ED: +case 0xF8EE: +case 0xF8EF: +case 0xF8F0: +case 0xF8F1: +case 0xF8F2: +case 0xF8F3: +case 0xF8F4: +case 0xF8F5: +case 0xF8F6: +case 0xF8F7: +case 0xF8F8: +case 0xF8F9: +case 0xF8FA: +case 0xF8FB: +case 0xF8FC: +case 0xF8FD: +case 0xF8FE: +case 0xF8FF: +case 0xF900: +case 0xF901: +case 0xF902: +case 0xF903: +case 0xF904: +case 0xF905: +case 0xF906: +case 0xF907: +case 0xF908: +case 0xF909: +case 0xF90A: +case 0xF90B: +case 0xF90C: +case 0xF90D: +case 0xF90E: +case 0xF90F: +case 0xF910: +case 0xF911: +case 0xF912: +case 0xF913: +case 0xF914: +case 0xF915: +case 0xF916: +case 0xF917: +case 0xF918: +case 0xF919: +case 0xF91A: +case 0xF91B: +case 0xF91C: +case 0xF91D: +case 0xF91E: +case 0xF91F: +case 0xF920: +case 0xF921: +case 0xF922: +case 0xF923: +case 0xF924: +case 0xF925: +case 0xF926: +case 0xF927: +case 0xF928: +case 0xF929: +case 0xF92A: +case 0xF92B: +case 0xF92C: +case 0xF92D: +case 0xF92E: +case 0xF92F: +case 0xF930: +case 0xF931: +case 0xF932: +case 0xF933: +case 0xF934: +case 0xF935: +case 0xF936: +case 0xF937: +case 0xF938: +case 0xF939: +case 0xF93A: +case 0xF93B: +case 0xF93C: +case 0xF93D: +case 0xF93E: +case 0xF93F: +case 0xF940: +case 0xF941: +case 0xF942: +case 0xF943: +case 0xF944: +case 0xF945: +case 0xF946: +case 0xF947: +case 0xF948: +case 0xF949: +case 0xF94A: +case 0xF94B: +case 0xF94C: +case 0xF94D: +case 0xF94E: +case 0xF94F: +case 0xF950: +case 0xF951: +case 0xF952: +case 0xF953: +case 0xF954: +case 0xF955: +case 0xF956: +case 0xF957: +case 0xF958: +case 0xF959: +case 0xF95A: +case 0xF95B: +case 0xF95C: +case 0xF95D: +case 0xF95E: +case 0xF95F: +case 0xF960: +case 0xF961: +case 0xF962: +case 0xF963: +case 0xF964: +case 0xF965: +case 0xF966: +case 0xF967: +case 0xF968: +case 0xF969: +case 0xF96A: +case 0xF96B: +case 0xF96C: +case 0xF96D: +case 0xF96E: +case 0xF96F: +case 0xF970: +case 0xF971: +case 0xF972: +case 0xF973: +case 0xF974: +case 0xF975: +case 0xF976: +case 0xF977: +case 0xF978: +case 0xF979: +case 0xF97A: +case 0xF97B: +case 0xF97C: +case 0xF97D: +case 0xF97E: +case 0xF97F: +case 0xF980: +case 0xF981: +case 0xF982: +case 0xF983: +case 0xF984: +case 0xF985: +case 0xF986: +case 0xF987: +case 0xF988: +case 0xF989: +case 0xF98A: +case 0xF98B: +case 0xF98C: +case 0xF98D: +case 0xF98E: +case 0xF98F: +case 0xF990: +case 0xF991: +case 0xF992: +case 0xF993: +case 0xF994: +case 0xF995: +case 0xF996: +case 0xF997: +case 0xF998: +case 0xF999: +case 0xF99A: +case 0xF99B: +case 0xF99C: +case 0xF99D: +case 0xF99E: +case 0xF99F: +case 0xF9A0: +case 0xF9A1: +case 0xF9A2: +case 0xF9A3: +case 0xF9A4: +case 0xF9A5: +case 0xF9A6: +case 0xF9A7: +case 0xF9A8: +case 0xF9A9: +case 0xF9AA: +case 0xF9AB: +case 0xF9AC: +case 0xF9AD: +case 0xF9AE: +case 0xF9AF: +case 0xF9B0: +case 0xF9B1: +case 0xF9B2: +case 0xF9B3: +case 0xF9B4: +case 0xF9B5: +case 0xF9B6: +case 0xF9B7: +case 0xF9B8: +case 0xF9B9: +case 0xF9BA: +case 0xF9BB: +case 0xF9BC: +case 0xF9BD: +case 0xF9BE: +case 0xF9BF: +case 0xF9C0: +case 0xF9C1: +case 0xF9C2: +case 0xF9C3: +case 0xF9C4: +case 0xF9C5: +case 0xF9C6: +case 0xF9C7: +case 0xF9C8: +case 0xF9C9: +case 0xF9CA: +case 0xF9CB: +case 0xF9CC: +case 0xF9CD: +case 0xF9CE: +case 0xF9CF: +case 0xF9D0: +case 0xF9D1: +case 0xF9D2: +case 0xF9D3: +case 0xF9D4: +case 0xF9D5: +case 0xF9D6: +case 0xF9D7: +case 0xF9D8: +case 0xF9D9: +case 0xF9DA: +case 0xF9DB: +case 0xF9DC: +case 0xF9DD: +case 0xF9DE: +case 0xF9DF: +case 0xF9E0: +case 0xF9E1: +case 0xF9E2: +case 0xF9E3: +case 0xF9E4: +case 0xF9E5: +case 0xF9E6: +case 0xF9E7: +case 0xF9E8: +case 0xF9E9: +case 0xF9EA: +case 0xF9EB: +case 0xF9EC: +case 0xF9ED: +case 0xF9EE: +case 0xF9EF: +case 0xF9F0: +case 0xF9F1: +case 0xF9F2: +case 0xF9F3: +case 0xF9F4: +case 0xF9F5: +case 0xF9F6: +case 0xF9F7: +case 0xF9F8: +case 0xF9F9: +case 0xF9FA: +case 0xF9FB: +case 0xF9FC: +case 0xF9FD: +case 0xF9FE: +case 0xF9FF: +case 0xFA00: +case 0xFA01: +case 0xFA02: +case 0xFA03: +case 0xFA04: +case 0xFA05: +case 0xFA06: +case 0xFA07: +case 0xFA08: +case 0xFA09: +case 0xFA0A: +case 0xFA0B: +case 0xFA0C: +case 0xFA0D: +case 0xFA0E: +case 0xFA0F: +case 0xFA10: +case 0xFA11: +case 0xFA12: +case 0xFA13: +case 0xFA14: +case 0xFA15: +case 0xFA16: +case 0xFA17: +case 0xFA18: +case 0xFA19: +case 0xFA1A: +case 0xFA1B: +case 0xFA1C: +case 0xFA1D: +case 0xFA1E: +case 0xFA1F: +case 0xFA20: +case 0xFA21: +case 0xFA22: +case 0xFA23: +case 0xFA24: +case 0xFA25: +case 0xFA26: +case 0xFA27: +case 0xFA28: +case 0xFA29: +case 0xFA2A: +case 0xFA2B: +case 0xFA2C: +case 0xFA2D: +case 0xFA2E: +case 0xFA2F: +case 0xFA30: +case 0xFA31: +case 0xFA32: +case 0xFA33: +case 0xFA34: +case 0xFA35: +case 0xFA36: +case 0xFA37: +case 0xFA38: +case 0xFA39: +case 0xFA3A: +case 0xFA3B: +case 0xFA3C: +case 0xFA3D: +case 0xFA3E: +case 0xFA3F: +case 0xFA40: +case 0xFA41: +case 0xFA42: +case 0xFA43: +case 0xFA44: +case 0xFA45: +case 0xFA46: +case 0xFA47: +case 0xFA48: +case 0xFA49: +case 0xFA4A: +case 0xFA4B: +case 0xFA4C: +case 0xFA4D: +case 0xFA4E: +case 0xFA4F: +case 0xFA50: +case 0xFA51: +case 0xFA52: +case 0xFA53: +case 0xFA54: +case 0xFA55: +case 0xFA56: +case 0xFA57: +case 0xFA58: +case 0xFA59: +case 0xFA5A: +case 0xFA5B: +case 0xFA5C: +case 0xFA5D: +case 0xFA5E: +case 0xFA5F: +case 0xFA60: +case 0xFA61: +case 0xFA62: +case 0xFA63: +case 0xFA64: +case 0xFA65: +case 0xFA66: +case 0xFA67: +case 0xFA68: +case 0xFA69: +case 0xFA6A: +case 0xFA6B: +case 0xFA6C: +case 0xFA6D: +case 0xFA6E: +case 0xFA6F: +case 0xFA70: +case 0xFA71: +case 0xFA72: +case 0xFA73: +case 0xFA74: +case 0xFA75: +case 0xFA76: +case 0xFA77: +case 0xFA78: +case 0xFA79: +case 0xFA7A: +case 0xFA7B: +case 0xFA7C: +case 0xFA7D: +case 0xFA7E: +case 0xFA7F: +case 0xFA80: +case 0xFA81: +case 0xFA82: +case 0xFA83: +case 0xFA84: +case 0xFA85: +case 0xFA86: +case 0xFA87: +case 0xFA88: +case 0xFA89: +case 0xFA8A: +case 0xFA8B: +case 0xFA8C: +case 0xFA8D: +case 0xFA8E: +case 0xFA8F: +case 0xFA90: +case 0xFA91: +case 0xFA92: +case 0xFA93: +case 0xFA94: +case 0xFA95: +case 0xFA96: +case 0xFA97: +case 0xFA98: +case 0xFA99: +case 0xFA9A: +case 0xFA9B: +case 0xFA9C: +case 0xFA9D: +case 0xFA9E: +case 0xFA9F: +case 0xFAA0: +case 0xFAA1: +case 0xFAA2: +case 0xFAA3: +case 0xFAA4: +case 0xFAA5: +case 0xFAA6: +case 0xFAA7: +case 0xFAA8: +case 0xFAA9: +case 0xFAAA: +case 0xFAAB: +case 0xFAAC: +case 0xFAAD: +case 0xFAAE: +case 0xFAAF: +case 0xFAB0: +case 0xFAB1: +case 0xFAB2: +case 0xFAB3: +case 0xFAB4: +case 0xFAB5: +case 0xFAB6: +case 0xFAB7: +case 0xFAB8: +case 0xFAB9: +case 0xFABA: +case 0xFABB: +case 0xFABC: +case 0xFABD: +case 0xFABE: +case 0xFABF: +case 0xFAC0: +case 0xFAC1: +case 0xFAC2: +case 0xFAC3: +case 0xFAC4: +case 0xFAC5: +case 0xFAC6: +case 0xFAC7: +case 0xFAC8: +case 0xFAC9: +case 0xFACA: +case 0xFACB: +case 0xFACC: +case 0xFACD: +case 0xFACE: +case 0xFACF: +case 0xFAD0: +case 0xFAD1: +case 0xFAD2: +case 0xFAD3: +case 0xFAD4: +case 0xFAD5: +case 0xFAD6: +case 0xFAD7: +case 0xFAD8: +case 0xFAD9: +case 0xFADA: +case 0xFADB: +case 0xFADC: +case 0xFADD: +case 0xFADE: +case 0xFADF: +case 0xFAE0: +case 0xFAE1: +case 0xFAE2: +case 0xFAE3: +case 0xFAE4: +case 0xFAE5: +case 0xFAE6: +case 0xFAE7: +case 0xFAE8: +case 0xFAE9: +case 0xFAEA: +case 0xFAEB: +case 0xFAEC: +case 0xFAED: +case 0xFAEE: +case 0xFAEF: +case 0xFAF0: +case 0xFAF1: +case 0xFAF2: +case 0xFAF3: +case 0xFAF4: +case 0xFAF5: +case 0xFAF6: +case 0xFAF7: +case 0xFAF8: +case 0xFAF9: +case 0xFAFA: +case 0xFAFB: +case 0xFAFC: +case 0xFAFD: +case 0xFAFE: +case 0xFAFF: +case 0xFB00: +case 0xFB01: +case 0xFB02: +case 0xFB03: +case 0xFB04: +case 0xFB05: +case 0xFB06: +case 0xFB07: +case 0xFB08: +case 0xFB09: +case 0xFB0A: +case 0xFB0B: +case 0xFB0C: +case 0xFB0D: +case 0xFB0E: +case 0xFB0F: +case 0xFB10: +case 0xFB11: +case 0xFB12: +case 0xFB13: +case 0xFB14: +case 0xFB15: +case 0xFB16: +case 0xFB17: +case 0xFB18: +case 0xFB19: +case 0xFB1A: +case 0xFB1B: +case 0xFB1C: +case 0xFB1D: +case 0xFB1E: +case 0xFB1F: +case 0xFB20: +case 0xFB21: +case 0xFB22: +case 0xFB23: +case 0xFB24: +case 0xFB25: +case 0xFB26: +case 0xFB27: +case 0xFB28: +case 0xFB29: +case 0xFB2A: +case 0xFB2B: +case 0xFB2C: +case 0xFB2D: +case 0xFB2E: +case 0xFB2F: +case 0xFB30: +case 0xFB31: +case 0xFB32: +case 0xFB33: +case 0xFB34: +case 0xFB35: +case 0xFB36: +case 0xFB37: +case 0xFB38: +case 0xFB39: +case 0xFB3A: +case 0xFB3B: +case 0xFB3C: +case 0xFB3D: +case 0xFB3E: +case 0xFB3F: +case 0xFB40: +case 0xFB41: +case 0xFB42: +case 0xFB43: +case 0xFB44: +case 0xFB45: +case 0xFB46: +case 0xFB47: +case 0xFB48: +case 0xFB49: +case 0xFB4A: +case 0xFB4B: +case 0xFB4C: +case 0xFB4D: +case 0xFB4E: +case 0xFB4F: +case 0xFB50: +case 0xFB51: +case 0xFB52: +case 0xFB53: +case 0xFB54: +case 0xFB55: +case 0xFB56: +case 0xFB57: +case 0xFB58: +case 0xFB59: +case 0xFB5A: +case 0xFB5B: +case 0xFB5C: +case 0xFB5D: +case 0xFB5E: +case 0xFB5F: +case 0xFB60: +case 0xFB61: +case 0xFB62: +case 0xFB63: +case 0xFB64: +case 0xFB65: +case 0xFB66: +case 0xFB67: +case 0xFB68: +case 0xFB69: +case 0xFB6A: +case 0xFB6B: +case 0xFB6C: +case 0xFB6D: +case 0xFB6E: +case 0xFB6F: +case 0xFB70: +case 0xFB71: +case 0xFB72: +case 0xFB73: +case 0xFB74: +case 0xFB75: +case 0xFB76: +case 0xFB77: +case 0xFB78: +case 0xFB79: +case 0xFB7A: +case 0xFB7B: +case 0xFB7C: +case 0xFB7D: +case 0xFB7E: +case 0xFB7F: +case 0xFB80: +case 0xFB81: +case 0xFB82: +case 0xFB83: +case 0xFB84: +case 0xFB85: +case 0xFB86: +case 0xFB87: +case 0xFB88: +case 0xFB89: +case 0xFB8A: +case 0xFB8B: +case 0xFB8C: +case 0xFB8D: +case 0xFB8E: +case 0xFB8F: +case 0xFB90: +case 0xFB91: +case 0xFB92: +case 0xFB93: +case 0xFB94: +case 0xFB95: +case 0xFB96: +case 0xFB97: +case 0xFB98: +case 0xFB99: +case 0xFB9A: +case 0xFB9B: +case 0xFB9C: +case 0xFB9D: +case 0xFB9E: +case 0xFB9F: +case 0xFBA0: +case 0xFBA1: +case 0xFBA2: +case 0xFBA3: +case 0xFBA4: +case 0xFBA5: +case 0xFBA6: +case 0xFBA7: +case 0xFBA8: +case 0xFBA9: +case 0xFBAA: +case 0xFBAB: +case 0xFBAC: +case 0xFBAD: +case 0xFBAE: +case 0xFBAF: +case 0xFBB0: +case 0xFBB1: +case 0xFBB2: +case 0xFBB3: +case 0xFBB4: +case 0xFBB5: +case 0xFBB6: +case 0xFBB7: +case 0xFBB8: +case 0xFBB9: +case 0xFBBA: +case 0xFBBB: +case 0xFBBC: +case 0xFBBD: +case 0xFBBE: +case 0xFBBF: +case 0xFBC0: +case 0xFBC1: +case 0xFBC2: +case 0xFBC3: +case 0xFBC4: +case 0xFBC5: +case 0xFBC6: +case 0xFBC7: +case 0xFBC8: +case 0xFBC9: +case 0xFBCA: +case 0xFBCB: +case 0xFBCC: +case 0xFBCD: +case 0xFBCE: +case 0xFBCF: +case 0xFBD0: +case 0xFBD1: +case 0xFBD2: +case 0xFBD3: +case 0xFBD4: +case 0xFBD5: +case 0xFBD6: +case 0xFBD7: +case 0xFBD8: +case 0xFBD9: +case 0xFBDA: +case 0xFBDB: +case 0xFBDC: +case 0xFBDD: +case 0xFBDE: +case 0xFBDF: +case 0xFBE0: +case 0xFBE1: +case 0xFBE2: +case 0xFBE3: +case 0xFBE4: +case 0xFBE5: +case 0xFBE6: +case 0xFBE7: +case 0xFBE8: +case 0xFBE9: +case 0xFBEA: +case 0xFBEB: +case 0xFBEC: +case 0xFBED: +case 0xFBEE: +case 0xFBEF: +case 0xFBF0: +case 0xFBF1: +case 0xFBF2: +case 0xFBF3: +case 0xFBF4: +case 0xFBF5: +case 0xFBF6: +case 0xFBF7: +case 0xFBF8: +case 0xFBF9: +case 0xFBFA: +case 0xFBFB: +case 0xFBFC: +case 0xFBFD: +case 0xFBFE: +case 0xFBFF: +case 0xFC00: +case 0xFC01: +case 0xFC02: +case 0xFC03: +case 0xFC04: +case 0xFC05: +case 0xFC06: +case 0xFC07: +case 0xFC08: +case 0xFC09: +case 0xFC0A: +case 0xFC0B: +case 0xFC0C: +case 0xFC0D: +case 0xFC0E: +case 0xFC0F: +case 0xFC10: +case 0xFC11: +case 0xFC12: +case 0xFC13: +case 0xFC14: +case 0xFC15: +case 0xFC16: +case 0xFC17: +case 0xFC18: +case 0xFC19: +case 0xFC1A: +case 0xFC1B: +case 0xFC1C: +case 0xFC1D: +case 0xFC1E: +case 0xFC1F: +case 0xFC20: +case 0xFC21: +case 0xFC22: +case 0xFC23: +case 0xFC24: +case 0xFC25: +case 0xFC26: +case 0xFC27: +case 0xFC28: +case 0xFC29: +case 0xFC2A: +case 0xFC2B: +case 0xFC2C: +case 0xFC2D: +case 0xFC2E: +case 0xFC2F: +case 0xFC30: +case 0xFC31: +case 0xFC32: +case 0xFC33: +case 0xFC34: +case 0xFC35: +case 0xFC36: +case 0xFC37: +case 0xFC38: +case 0xFC39: +case 0xFC3A: +case 0xFC3B: +case 0xFC3C: +case 0xFC3D: +case 0xFC3E: +case 0xFC3F: +case 0xFC40: +case 0xFC41: +case 0xFC42: +case 0xFC43: +case 0xFC44: +case 0xFC45: +case 0xFC46: +case 0xFC47: +case 0xFC48: +case 0xFC49: +case 0xFC4A: +case 0xFC4B: +case 0xFC4C: +case 0xFC4D: +case 0xFC4E: +case 0xFC4F: +case 0xFC50: +case 0xFC51: +case 0xFC52: +case 0xFC53: +case 0xFC54: +case 0xFC55: +case 0xFC56: +case 0xFC57: +case 0xFC58: +case 0xFC59: +case 0xFC5A: +case 0xFC5B: +case 0xFC5C: +case 0xFC5D: +case 0xFC5E: +case 0xFC5F: +case 0xFC60: +case 0xFC61: +case 0xFC62: +case 0xFC63: +case 0xFC64: +case 0xFC65: +case 0xFC66: +case 0xFC67: +case 0xFC68: +case 0xFC69: +case 0xFC6A: +case 0xFC6B: +case 0xFC6C: +case 0xFC6D: +case 0xFC6E: +case 0xFC6F: +case 0xFC70: +case 0xFC71: +case 0xFC72: +case 0xFC73: +case 0xFC74: +case 0xFC75: +case 0xFC76: +case 0xFC77: +case 0xFC78: +case 0xFC79: +case 0xFC7A: +case 0xFC7B: +case 0xFC7C: +case 0xFC7D: +case 0xFC7E: +case 0xFC7F: +case 0xFC80: +case 0xFC81: +case 0xFC82: +case 0xFC83: +case 0xFC84: +case 0xFC85: +case 0xFC86: +case 0xFC87: +case 0xFC88: +case 0xFC89: +case 0xFC8A: +case 0xFC8B: +case 0xFC8C: +case 0xFC8D: +case 0xFC8E: +case 0xFC8F: +case 0xFC90: +case 0xFC91: +case 0xFC92: +case 0xFC93: +case 0xFC94: +case 0xFC95: +case 0xFC96: +case 0xFC97: +case 0xFC98: +case 0xFC99: +case 0xFC9A: +case 0xFC9B: +case 0xFC9C: +case 0xFC9D: +case 0xFC9E: +case 0xFC9F: +case 0xFCA0: +case 0xFCA1: +case 0xFCA2: +case 0xFCA3: +case 0xFCA4: +case 0xFCA5: +case 0xFCA6: +case 0xFCA7: +case 0xFCA8: +case 0xFCA9: +case 0xFCAA: +case 0xFCAB: +case 0xFCAC: +case 0xFCAD: +case 0xFCAE: +case 0xFCAF: +case 0xFCB0: +case 0xFCB1: +case 0xFCB2: +case 0xFCB3: +case 0xFCB4: +case 0xFCB5: +case 0xFCB6: +case 0xFCB7: +case 0xFCB8: +case 0xFCB9: +case 0xFCBA: +case 0xFCBB: +case 0xFCBC: +case 0xFCBD: +case 0xFCBE: +case 0xFCBF: +case 0xFCC0: +case 0xFCC1: +case 0xFCC2: +case 0xFCC3: +case 0xFCC4: +case 0xFCC5: +case 0xFCC6: +case 0xFCC7: +case 0xFCC8: +case 0xFCC9: +case 0xFCCA: +case 0xFCCB: +case 0xFCCC: +case 0xFCCD: +case 0xFCCE: +case 0xFCCF: +case 0xFCD0: +case 0xFCD1: +case 0xFCD2: +case 0xFCD3: +case 0xFCD4: +case 0xFCD5: +case 0xFCD6: +case 0xFCD7: +case 0xFCD8: +case 0xFCD9: +case 0xFCDA: +case 0xFCDB: +case 0xFCDC: +case 0xFCDD: +case 0xFCDE: +case 0xFCDF: +case 0xFCE0: +case 0xFCE1: +case 0xFCE2: +case 0xFCE3: +case 0xFCE4: +case 0xFCE5: +case 0xFCE6: +case 0xFCE7: +case 0xFCE8: +case 0xFCE9: +case 0xFCEA: +case 0xFCEB: +case 0xFCEC: +case 0xFCED: +case 0xFCEE: +case 0xFCEF: +case 0xFCF0: +case 0xFCF1: +case 0xFCF2: +case 0xFCF3: +case 0xFCF4: +case 0xFCF5: +case 0xFCF6: +case 0xFCF7: +case 0xFCF8: +case 0xFCF9: +case 0xFCFA: +case 0xFCFB: +case 0xFCFC: +case 0xFCFD: +case 0xFCFE: +case 0xFCFF: +case 0xFD00: +case 0xFD01: +case 0xFD02: +case 0xFD03: +case 0xFD04: +case 0xFD05: +case 0xFD06: +case 0xFD07: +case 0xFD08: +case 0xFD09: +case 0xFD0A: +case 0xFD0B: +case 0xFD0C: +case 0xFD0D: +case 0xFD0E: +case 0xFD0F: +case 0xFD10: +case 0xFD11: +case 0xFD12: +case 0xFD13: +case 0xFD14: +case 0xFD15: +case 0xFD16: +case 0xFD17: +case 0xFD18: +case 0xFD19: +case 0xFD1A: +case 0xFD1B: +case 0xFD1C: +case 0xFD1D: +case 0xFD1E: +case 0xFD1F: +case 0xFD20: +case 0xFD21: +case 0xFD22: +case 0xFD23: +case 0xFD24: +case 0xFD25: +case 0xFD26: +case 0xFD27: +case 0xFD28: +case 0xFD29: +case 0xFD2A: +case 0xFD2B: +case 0xFD2C: +case 0xFD2D: +case 0xFD2E: +case 0xFD2F: +case 0xFD30: +case 0xFD31: +case 0xFD32: +case 0xFD33: +case 0xFD34: +case 0xFD35: +case 0xFD36: +case 0xFD37: +case 0xFD38: +case 0xFD39: +case 0xFD3A: +case 0xFD3B: +case 0xFD3C: +case 0xFD3D: +case 0xFD3E: +case 0xFD3F: +case 0xFD40: +case 0xFD41: +case 0xFD42: +case 0xFD43: +case 0xFD44: +case 0xFD45: +case 0xFD46: +case 0xFD47: +case 0xFD48: +case 0xFD49: +case 0xFD4A: +case 0xFD4B: +case 0xFD4C: +case 0xFD4D: +case 0xFD4E: +case 0xFD4F: +case 0xFD50: +case 0xFD51: +case 0xFD52: +case 0xFD53: +case 0xFD54: +case 0xFD55: +case 0xFD56: +case 0xFD57: +case 0xFD58: +case 0xFD59: +case 0xFD5A: +case 0xFD5B: +case 0xFD5C: +case 0xFD5D: +case 0xFD5E: +case 0xFD5F: +case 0xFD60: +case 0xFD61: +case 0xFD62: +case 0xFD63: +case 0xFD64: +case 0xFD65: +case 0xFD66: +case 0xFD67: +case 0xFD68: +case 0xFD69: +case 0xFD6A: +case 0xFD6B: +case 0xFD6C: +case 0xFD6D: +case 0xFD6E: +case 0xFD6F: +case 0xFD70: +case 0xFD71: +case 0xFD72: +case 0xFD73: +case 0xFD74: +case 0xFD75: +case 0xFD76: +case 0xFD77: +case 0xFD78: +case 0xFD79: +case 0xFD7A: +case 0xFD7B: +case 0xFD7C: +case 0xFD7D: +case 0xFD7E: +case 0xFD7F: +case 0xFD80: +case 0xFD81: +case 0xFD82: +case 0xFD83: +case 0xFD84: +case 0xFD85: +case 0xFD86: +case 0xFD87: +case 0xFD88: +case 0xFD89: +case 0xFD8A: +case 0xFD8B: +case 0xFD8C: +case 0xFD8D: +case 0xFD8E: +case 0xFD8F: +case 0xFD90: +case 0xFD91: +case 0xFD92: +case 0xFD93: +case 0xFD94: +case 0xFD95: +case 0xFD96: +case 0xFD97: +case 0xFD98: +case 0xFD99: +case 0xFD9A: +case 0xFD9B: +case 0xFD9C: +case 0xFD9D: +case 0xFD9E: +case 0xFD9F: +case 0xFDA0: +case 0xFDA1: +case 0xFDA2: +case 0xFDA3: +case 0xFDA4: +case 0xFDA5: +case 0xFDA6: +case 0xFDA7: +case 0xFDA8: +case 0xFDA9: +case 0xFDAA: +case 0xFDAB: +case 0xFDAC: +case 0xFDAD: +case 0xFDAE: +case 0xFDAF: +case 0xFDB0: +case 0xFDB1: +case 0xFDB2: +case 0xFDB3: +case 0xFDB4: +case 0xFDB5: +case 0xFDB6: +case 0xFDB7: +case 0xFDB8: +case 0xFDB9: +case 0xFDBA: +case 0xFDBB: +case 0xFDBC: +case 0xFDBD: +case 0xFDBE: +case 0xFDBF: +case 0xFDC0: +case 0xFDC1: +case 0xFDC2: +case 0xFDC3: +case 0xFDC4: +case 0xFDC5: +case 0xFDC6: +case 0xFDC7: +case 0xFDC8: +case 0xFDC9: +case 0xFDCA: +case 0xFDCB: +case 0xFDCC: +case 0xFDCD: +case 0xFDCE: +case 0xFDCF: +case 0xFDD0: +case 0xFDD1: +case 0xFDD2: +case 0xFDD3: +case 0xFDD4: +case 0xFDD5: +case 0xFDD6: +case 0xFDD7: +case 0xFDD8: +case 0xFDD9: +case 0xFDDA: +case 0xFDDB: +case 0xFDDC: +case 0xFDDD: +case 0xFDDE: +case 0xFDDF: +case 0xFDE0: +case 0xFDE1: +case 0xFDE2: +case 0xFDE3: +case 0xFDE4: +case 0xFDE5: +case 0xFDE6: +case 0xFDE7: +case 0xFDE8: +case 0xFDE9: +case 0xFDEA: +case 0xFDEB: +case 0xFDEC: +case 0xFDED: +case 0xFDEE: +case 0xFDEF: +case 0xFDF0: +case 0xFDF1: +case 0xFDF2: +case 0xFDF3: +case 0xFDF4: +case 0xFDF5: +case 0xFDF6: +case 0xFDF7: +case 0xFDF8: +case 0xFDF9: +case 0xFDFA: +case 0xFDFB: +case 0xFDFC: +case 0xFDFD: +case 0xFDFE: +case 0xFDFF: +case 0xFE00: +case 0xFE01: +case 0xFE02: +case 0xFE03: +case 0xFE04: +case 0xFE05: +case 0xFE06: +case 0xFE07: +case 0xFE08: +case 0xFE09: +case 0xFE0A: +case 0xFE0B: +case 0xFE0C: +case 0xFE0D: +case 0xFE0E: +case 0xFE0F: +case 0xFE10: +case 0xFE11: +case 0xFE12: +case 0xFE13: +case 0xFE14: +case 0xFE15: +case 0xFE16: +case 0xFE17: +case 0xFE18: +case 0xFE19: +case 0xFE1A: +case 0xFE1B: +case 0xFE1C: +case 0xFE1D: +case 0xFE1E: +case 0xFE1F: +case 0xFE20: +case 0xFE21: +case 0xFE22: +case 0xFE23: +case 0xFE24: +case 0xFE25: +case 0xFE26: +case 0xFE27: +case 0xFE28: +case 0xFE29: +case 0xFE2A: +case 0xFE2B: +case 0xFE2C: +case 0xFE2D: +case 0xFE2E: +case 0xFE2F: +case 0xFE30: +case 0xFE31: +case 0xFE32: +case 0xFE33: +case 0xFE34: +case 0xFE35: +case 0xFE36: +case 0xFE37: +case 0xFE38: +case 0xFE39: +case 0xFE3A: +case 0xFE3B: +case 0xFE3C: +case 0xFE3D: +case 0xFE3E: +case 0xFE3F: +case 0xFE40: +case 0xFE41: +case 0xFE42: +case 0xFE43: +case 0xFE44: +case 0xFE45: +case 0xFE46: +case 0xFE47: +case 0xFE48: +case 0xFE49: +case 0xFE4A: +case 0xFE4B: +case 0xFE4C: +case 0xFE4D: +case 0xFE4E: +case 0xFE4F: +case 0xFE50: +case 0xFE51: +case 0xFE52: +case 0xFE53: +case 0xFE54: +case 0xFE55: +case 0xFE56: +case 0xFE57: +case 0xFE58: +case 0xFE59: +case 0xFE5A: +case 0xFE5B: +case 0xFE5C: +case 0xFE5D: +case 0xFE5E: +case 0xFE5F: +case 0xFE60: +case 0xFE61: +case 0xFE62: +case 0xFE63: +case 0xFE64: +case 0xFE65: +case 0xFE66: +case 0xFE67: +case 0xFE68: +case 0xFE69: +case 0xFE6A: +case 0xFE6B: +case 0xFE6C: +case 0xFE6D: +case 0xFE6E: +case 0xFE6F: +case 0xFE70: +case 0xFE71: +case 0xFE72: +case 0xFE73: +case 0xFE74: +case 0xFE75: +case 0xFE76: +case 0xFE77: +case 0xFE78: +case 0xFE79: +case 0xFE7A: +case 0xFE7B: +case 0xFE7C: +case 0xFE7D: +case 0xFE7E: +case 0xFE7F: +case 0xFE80: +case 0xFE81: +case 0xFE82: +case 0xFE83: +case 0xFE84: +case 0xFE85: +case 0xFE86: +case 0xFE87: +case 0xFE88: +case 0xFE89: +case 0xFE8A: +case 0xFE8B: +case 0xFE8C: +case 0xFE8D: +case 0xFE8E: +case 0xFE8F: +case 0xFE90: +case 0xFE91: +case 0xFE92: +case 0xFE93: +case 0xFE94: +case 0xFE95: +case 0xFE96: +case 0xFE97: +case 0xFE98: +case 0xFE99: +case 0xFE9A: +case 0xFE9B: +case 0xFE9C: +case 0xFE9D: +case 0xFE9E: +case 0xFE9F: +case 0xFEA0: +case 0xFEA1: +case 0xFEA2: +case 0xFEA3: +case 0xFEA4: +case 0xFEA5: +case 0xFEA6: +case 0xFEA7: +case 0xFEA8: +case 0xFEA9: +case 0xFEAA: +case 0xFEAB: +case 0xFEAC: +case 0xFEAD: +case 0xFEAE: +case 0xFEAF: +case 0xFEB0: +case 0xFEB1: +case 0xFEB2: +case 0xFEB3: +case 0xFEB4: +case 0xFEB5: +case 0xFEB6: +case 0xFEB7: +case 0xFEB8: +case 0xFEB9: +case 0xFEBA: +case 0xFEBB: +case 0xFEBC: +case 0xFEBD: +case 0xFEBE: +case 0xFEBF: +case 0xFEC0: +case 0xFEC1: +case 0xFEC2: +case 0xFEC3: +case 0xFEC4: +case 0xFEC5: +case 0xFEC6: +case 0xFEC7: +case 0xFEC8: +case 0xFEC9: +case 0xFECA: +case 0xFECB: +case 0xFECC: +case 0xFECD: +case 0xFECE: +case 0xFECF: +case 0xFED0: +case 0xFED1: +case 0xFED2: +case 0xFED3: +case 0xFED4: +case 0xFED5: +case 0xFED6: +case 0xFED7: +case 0xFED8: +case 0xFED9: +case 0xFEDA: +case 0xFEDB: +case 0xFEDC: +case 0xFEDD: +case 0xFEDE: +case 0xFEDF: +case 0xFEE0: +case 0xFEE1: +case 0xFEE2: +case 0xFEE3: +case 0xFEE4: +case 0xFEE5: +case 0xFEE6: +case 0xFEE7: +case 0xFEE8: +case 0xFEE9: +case 0xFEEA: +case 0xFEEB: +case 0xFEEC: +case 0xFEED: +case 0xFEEE: +case 0xFEEF: +case 0xFEF0: +case 0xFEF1: +case 0xFEF2: +case 0xFEF3: +case 0xFEF4: +case 0xFEF5: +case 0xFEF6: +case 0xFEF7: +case 0xFEF8: +case 0xFEF9: +case 0xFEFA: +case 0xFEFB: +case 0xFEFC: +case 0xFEFD: +case 0xFEFE: +case 0xFEFF: +case 0xFF00: +case 0xFF01: +case 0xFF02: +case 0xFF03: +case 0xFF04: +case 0xFF05: +case 0xFF06: +case 0xFF07: +case 0xFF08: +case 0xFF09: +case 0xFF0A: +case 0xFF0B: +case 0xFF0C: +case 0xFF0D: +case 0xFF0E: +case 0xFF0F: +case 0xFF10: +case 0xFF11: +case 0xFF12: +case 0xFF13: +case 0xFF14: +case 0xFF15: +case 0xFF16: +case 0xFF17: +case 0xFF18: +case 0xFF19: +case 0xFF1A: +case 0xFF1B: +case 0xFF1C: +case 0xFF1D: +case 0xFF1E: +case 0xFF1F: +case 0xFF20: +case 0xFF21: +case 0xFF22: +case 0xFF23: +case 0xFF24: +case 0xFF25: +case 0xFF26: +case 0xFF27: +case 0xFF28: +case 0xFF29: +case 0xFF2A: +case 0xFF2B: +case 0xFF2C: +case 0xFF2D: +case 0xFF2E: +case 0xFF2F: +case 0xFF30: +case 0xFF31: +case 0xFF32: +case 0xFF33: +case 0xFF34: +case 0xFF35: +case 0xFF36: +case 0xFF37: +case 0xFF38: +case 0xFF39: +case 0xFF3A: +case 0xFF3B: +case 0xFF3C: +case 0xFF3D: +case 0xFF3E: +case 0xFF3F: +case 0xFF40: +case 0xFF41: +case 0xFF42: +case 0xFF43: +case 0xFF44: +case 0xFF45: +case 0xFF46: +case 0xFF47: +case 0xFF48: +case 0xFF49: +case 0xFF4A: +case 0xFF4B: +case 0xFF4C: +case 0xFF4D: +case 0xFF4E: +case 0xFF4F: +case 0xFF50: +case 0xFF51: +case 0xFF52: +case 0xFF53: +case 0xFF54: +case 0xFF55: +case 0xFF56: +case 0xFF57: +case 0xFF58: +case 0xFF59: +case 0xFF5A: +case 0xFF5B: +case 0xFF5C: +case 0xFF5D: +case 0xFF5E: +case 0xFF5F: +case 0xFF60: +case 0xFF61: +case 0xFF62: +case 0xFF63: +case 0xFF64: +case 0xFF65: +case 0xFF66: +case 0xFF67: +case 0xFF68: +case 0xFF69: +case 0xFF6A: +case 0xFF6B: +case 0xFF6C: +case 0xFF6D: +case 0xFF6E: +case 0xFF6F: +case 0xFF70: +case 0xFF71: +case 0xFF72: +case 0xFF73: +case 0xFF74: +case 0xFF75: +case 0xFF76: +case 0xFF77: +case 0xFF78: +case 0xFF79: +case 0xFF7A: +case 0xFF7B: +case 0xFF7C: +case 0xFF7D: +case 0xFF7E: +case 0xFF7F: +case 0xFF80: +case 0xFF81: +case 0xFF82: +case 0xFF83: +case 0xFF84: +case 0xFF85: +case 0xFF86: +case 0xFF87: +case 0xFF88: +case 0xFF89: +case 0xFF8A: +case 0xFF8B: +case 0xFF8C: +case 0xFF8D: +case 0xFF8E: +case 0xFF8F: +case 0xFF90: +case 0xFF91: +case 0xFF92: +case 0xFF93: +case 0xFF94: +case 0xFF95: +case 0xFF96: +case 0xFF97: +case 0xFF98: +case 0xFF99: +case 0xFF9A: +case 0xFF9B: +case 0xFF9C: +case 0xFF9D: +case 0xFF9E: +case 0xFF9F: +case 0xFFA0: +case 0xFFA1: +case 0xFFA2: +case 0xFFA3: +case 0xFFA4: +case 0xFFA5: +case 0xFFA6: +case 0xFFA7: +case 0xFFA8: +case 0xFFA9: +case 0xFFAA: +case 0xFFAB: +case 0xFFAC: +case 0xFFAD: +case 0xFFAE: +case 0xFFAF: +case 0xFFB0: +case 0xFFB1: +case 0xFFB2: +case 0xFFB3: +case 0xFFB4: +case 0xFFB5: +case 0xFFB6: +case 0xFFB7: +case 0xFFB8: +case 0xFFB9: +case 0xFFBA: +case 0xFFBB: +case 0xFFBC: +case 0xFFBD: +case 0xFFBE: +case 0xFFBF: +case 0xFFC0: +case 0xFFC1: +case 0xFFC2: +case 0xFFC3: +case 0xFFC4: +case 0xFFC5: +case 0xFFC6: +case 0xFFC7: +case 0xFFC8: +case 0xFFC9: +case 0xFFCA: +case 0xFFCB: +case 0xFFCC: +case 0xFFCD: +case 0xFFCE: +case 0xFFCF: +case 0xFFD0: +case 0xFFD1: +case 0xFFD2: +case 0xFFD3: +case 0xFFD4: +case 0xFFD5: +case 0xFFD6: +case 0xFFD7: +case 0xFFD8: +case 0xFFD9: +case 0xFFDA: +case 0xFFDB: +case 0xFFDC: +case 0xFFDD: +case 0xFFDE: +case 0xFFDF: +case 0xFFE0: +case 0xFFE1: +case 0xFFE2: +case 0xFFE3: +case 0xFFE4: +case 0xFFE5: +case 0xFFE6: +case 0xFFE7: +case 0xFFE8: +case 0xFFE9: +case 0xFFEA: +case 0xFFEB: +case 0xFFEC: +case 0xFFED: +case 0xFFEE: +case 0xFFEF: +case 0xFFF0: +case 0xFFF1: +case 0xFFF2: +case 0xFFF3: +case 0xFFF4: +case 0xFFF5: +case 0xFFF6: +case 0xFFF7: +case 0xFFF8: +case 0xFFF9: +case 0xFFFA: +case 0xFFFB: +case 0xFFFC: +case 0xFFFD: +case 0xFFFE: +case 0xFFFF: + +// 1111 +case 0xF000: +{ + u32 res; + PC -= 2; + if (!CPU->flag_S) + { + res = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = res; + } + res = C68K_1111_EX; + CCnt -= c68k_exception_cycle_table[res]; + PRE_IO + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + CPU->flag_S = C68K_SR_S; + READ_LONG_F(res * 4, PC) + SET_PC(PC) + POST_IO +} +RET(4) diff --git a/yabause/src/c68k/c68kexec.c b/yabause/src/c68k/c68kexec.c new file mode 100644 index 0000000000..c9ad703871 --- /dev/null +++ b/yabause/src/c68k/c68kexec.c @@ -0,0 +1,335 @@ +/* Copyright 2003-2004 Stephane Dallongeville + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../core.h" +#include "c68k.h" + +// #define TRACE_WITH_Q68 // Define to use Q68 tracing code to trace insns + // (requires Q68 built in, of course) + +#ifdef NEOCD_HLE +void cdrom_load_files(void); +void neogeo_cdda_control(void); +void neogeo_prio_switch(void); +void neogeo_upload(void); +#endif + +// exception cycle table (taken from musashi core) +static const s32 c68k_exception_cycle_table[256] = +{ + 4, // 0: Reset - Initial Stack Pointer + 4, // 1: Reset - Initial Program Counter + 50, // 2: Bus Error + 50, // 3: Address Error + 34, // 4: Illegal Instruction + 38, // 5: Divide by Zero + 40, // 6: CHK + 34, // 7: TRAPV + 34, // 8: Privilege Violation + 34, // 9: Trace + 4, // 10: + 4, // 11: + 4, // 12: RESERVED + 4, // 13: Coprocessor Protocol Violation + 4, // 14: Format Error + 44, // 15: Uninitialized Interrupt + 4, // 16: RESERVED + 4, // 17: RESERVED + 4, // 18: RESERVED + 4, // 19: RESERVED + 4, // 20: RESERVED + 4, // 21: RESERVED + 4, // 22: RESERVED + 4, // 23: RESERVED + 44, // 24: Spurious Interrupt + 44, // 25: Level 1 Interrupt Autovector + 44, // 26: Level 2 Interrupt Autovector + 44, // 27: Level 3 Interrupt Autovector + 44, // 28: Level 4 Interrupt Autovector + 44, // 29: Level 5 Interrupt Autovector + 44, // 30: Level 6 Interrupt Autovector + 44, // 31: Level 7 Interrupt Autovector + 34, // 32: TRAP #0 + 34, // 33: TRAP #1 + 34, // 34: TRAP #2 + 34, // 35: TRAP #3 + 34, // 36: TRAP #4 + 34, // 37: TRAP #5 + 34, // 38: TRAP #6 + 34, // 39: TRAP #7 + 34, // 40: TRAP #8 + 34, // 41: TRAP #9 + 34, // 42: TRAP #10 + 34, // 43: TRAP #11 + 34, // 44: TRAP #12 + 34, // 45: TRAP #13 + 34, // 46: TRAP #14 + 34, // 47: TRAP #15 + 4, // 48: FP Branch or Set on Unknown Condition + 4, // 49: FP Inexact Result + 4, // 50: FP Divide by Zero + 4, // 51: FP Underflow + 4, // 52: FP Operand Error + 4, // 53: FP Overflow + 4, // 54: FP Signaling NAN + 4, // 55: FP Unimplemented Data Type + 4, // 56: MMU Configuration Error + 4, // 57: MMU Illegal Operation Error + 4, // 58: MMU Access Level Violation Error + 4, // 59: RESERVED + 4, // 60: RESERVED + 4, // 61: RESERVED + 4, // 62: RESERVED + 4, // 63: RESERVED + // 64-255: User Defined + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +}; + +// global variable +/////////////////// + +#ifndef C68K_GEN + +#ifndef C68K_NO_JUMP_TABLE +#ifndef C68K_CONST_JUMP_TABLE +static void *JumpTable[0x10000]; +#endif +#endif + +static u32 C68k_Initialised = 0; + +#endif // C68K_GEN + +#ifdef NEOCD_HLE +extern int img_display; +#endif + +// include macro file +////////////////////// + +#include "c68kmac.inc" + +#ifndef C68K_GEN +# ifdef TRACE_WITH_Q68 +#include "../q68/q68.h" +static c68k_struc *TRACE_CPU; +static uint32_t readw(uint32_t address) { + return TRACE_CPU->Read_Word(address); +} +/* Make our own version of the structure to avoid the overhead of dozens of + * function calls every instruction */ +static struct { + u32 D[8], A[8], PC, SR, USP, SSP, dummy[7]; + void *readb, *readw, *writeb, *writew; +} state = {.readw = readw}; +void TRACE(int PC,c68k_struc *CPU,int Opcode,int CCnt) { + static FILE *f; + if (!f) f = fopen("c68k.log", "w"); + TRACE_CPU = CPU; + memcpy(state.D, CPU->D, 16*4); + state.PC = PC - 2 - CPU->BasePC; + state.SR = GET_SR; + if (f) q68_trace((Q68State *)&state, f, + CPU->CycleToDo - CCnt, CPU->CycleToDo); +} +# endif // TRACE_WITH_Q68 +#endif // !C68K_GEN + +// main exec function +////////////////////// + +s32 FASTCALL C68k_Exec(c68k_struc *cpu, s32 cycle) +{ +#ifndef C68K_GEN +#if 0 + register c68k_struc *CPU asm ("ebx"); + register pointer PC asm ("esi"); + register s32 CCnt asm ("edi"); +// register u32 Opcode asm ("edi"); +// c68k_struc *CPU; +// u32 PC; +// s32 CCnt; + u32 Opcode; +#else +// register c68k_struc *CPU asm ("r10"); +// register u32 PC asm ("r11"); +// register s32 CCnt asm ("r12"); +// register u32 Opcode asm ("r13"); + c68k_struc *CPU; + pointer PC; + s32 CCnt; + u32 Opcode; +#endif +#endif + +#ifndef C68K_GEN + +#ifndef C68K_NO_JUMP_TABLE +#ifdef C68K_CONST_JUMP_TABLE + #include "c68k_ini.inc" +#endif +#else + C68k_Initialised = 1; +#endif + + CPU = cpu; + PC = CPU->PC; + + if (CPU->Status & (C68K_RUNNING | C68K_DISABLE | C68K_FAULTED)) + { +#ifndef C68K_NO_JUMP_TABLE +#ifndef C68K_CONST_JUMP_TABLE + if (!C68k_Initialised) goto C68k_Init; +#endif +#endif + return (CPU->Status | 0x80000000); + } + + if (cycle <= 0) return -cycle; + + CPU->CycleToDo = CCnt = cycle; + +#ifndef C68K_DEBUG + CHECK_INT +#else + { + s32 line, vect; + + line = CPU->IRQLine; + + if ((line == 7) || (line > CPU->flag_I)) + { + PRE_IO + + /* get vector */ + CPU->IRQLine = 0; + vect = CPU->Interrupt_CallBack(line); + if (vect == C68K_INT_ACK_AUTOVECTOR) + vect = C68K_INTERRUPT_AUTOVECTOR_EX + (line & 7); + + /* adjust CCnt */ + CCnt -= c68k_exception_cycle_table[vect]; + + /* swap A7 and USP */ + if (!CPU->flag_S) + { + u32 tmpSP; + + tmpSP = CPU->USP; + CPU->USP = CPU->A[7]; + CPU->A[7] = tmpSP; + } + + /* push PC and SR */ + PUSH_32_F(PC - CPU->BasePC) + PUSH_16_F(GET_SR) + + /* adjust SR */ + CPU->flag_S = C68K_SR_S; + CPU->flag_I = line; + + /* fetch new PC */ + READ_LONG_F(vect * 4, PC) + SET_PC(PC) + + POST_IO + } + } +#endif + + if (CPU->Status & (C68K_HALTED | C68K_WAITING)) return CPU->CycleToDo; + + CPU->CycleSup = 0; + CPU->Status |= C68K_RUNNING; + +#ifndef C68K_DEBUG + NEXT +#else +#ifdef C68K_NO_JUMP_TABLE + NEXT +#else + Opcode = FETCH_WORD; + PC += 2; + goto *JumpTable[Opcode]; +#endif +#endif + +#ifdef C68K_NO_JUMP_TABLE +SwitchTable: + switch(Opcode) + { +#endif + #include "c68k_op0.inc" + #include "c68k_op1.inc" + #include "c68k_op2.inc" + #include "c68k_op3.inc" + #include "c68k_op4.inc" + #include "c68k_op5.inc" + #include "c68k_op6.inc" + #include "c68k_op7.inc" + #include "c68k_op8.inc" + #include "c68k_op9.inc" + #include "c68k_opA.inc" + #include "c68k_opB.inc" + #include "c68k_opC.inc" + #include "c68k_opD.inc" + #include "c68k_opE.inc" + #include "c68k_opF.inc" +#ifdef C68K_NO_JUMP_TABLE + } +#endif + +C68k_Exec_End: + CHECK_INT + if ((CCnt += CPU->CycleSup) > 0) + { + CPU->CycleSup = 0; + NEXT; + } + +C68k_Exec_Really_End: + CPU->Status &= ~C68K_RUNNING; + CPU->PC = PC; + + return (CPU->CycleToDo - CCnt); + +#ifndef C68K_CONST_JUMP_TABLE +#ifndef C68K_NO_JUMP_TABLE +C68k_Init: + { + u32 i, j; + + #include "c68k_ini.inc" + + C68k_Initialised = 1; + } + + return 0; +#endif +#endif +#else + return 0; +#endif +} + diff --git a/yabause/src/c68k/c68kmac.inc b/yabause/src/c68k/c68kmac.inc new file mode 100644 index 0000000000..25767be99b --- /dev/null +++ b/yabause/src/c68k/c68kmac.inc @@ -0,0 +1,307 @@ +/* Copyright 2003-2004 Stephane Dallongeville + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// internals core macros +///////////////////////// + +#define LSL(A, C) ((A) << (C)) +#define LSR(A, C) ((A) >> (C)) + +#define LSR_32(A, C) ((C) < 32 ? (A) >> (C) : 0) +#define LSL_32(A, C) ((C) < 32 ? (A) << (C) : 0) + +#define ROL_8(A, C) (LSL(A, C) | LSR(A, 8-(C))) +#define ROL_9(A, C) (LSL(A, C) | LSR(A, 9-(C))) +#define ROL_16(A, C) (LSL(A, C) | LSR(A, 16-(C))) +#define ROL_17(A, C) (LSL(A, C) | LSR(A, 17-(C))) +#define ROL_32(A, C) (LSL_32(A, C) | LSR_32(A, 32-(C))) +#define ROL_33(A, C) (LSL_32(A, C) | LSR_32(A, 33-(C))) + +#define ROR_8(A, C) (LSR(A, C) | LSL(A, 8-(C))) +#define ROR_9(A, C) (LSR(A, C) | LSL(A, 9-(C))) +#define ROR_16(A, C) (LSR(A, C) | LSL(A, 16-(C))) +#define ROR_17(A, C) (LSR(A, C) | LSL(A, 17-(C))) +#define ROR_32(A, C) (LSR_32(A, C) | LSL_32(A, 32-(C))) +#define ROR_33(A, C) (LSR_32(A, C) | LSL_32(A, 33-(C))) + + +#ifdef TRACE_WITH_Q68 +extern void TRACE(int,c68k_struc*,int,int); +#else +# define TRACE(a,b,c,d) /*nothing*/ +#endif +#ifndef C68K_NO_JUMP_TABLE +#define NEXT \ + PRE_IO \ + Opcode = FETCH_WORD; \ + PC += 2; \ + TRACE(PC, CPU, Opcode, CCnt); \ + goto *JumpTable[Opcode]; +#else +#define NEXT \ + PRE_IO \ + Opcode = FETCH_WORD; \ + PC += 2; \ + TRACE(PC, CPU, Opcode, CCnt); \ + goto SwitchTable; +#endif + +#define RET(A) \ + CCnt -= (A); \ + if (CCnt <= 0) goto C68k_Exec_End; \ + NEXT + +#define SET_PC(A) \ + CPU->BasePC = CPU->Fetch[((A) >> C68K_FETCH_SFT) & C68K_FETCH_MASK]; \ + CPU->BasePC -= (A) & 0xFF000000; \ + PC = (A) + CPU->BasePC; + +#define PRE_IO \ + CPU->CycleIO = CCnt; + +#define POST_IO \ + CCnt = CPU->CycleIO; + +#define READ_BYTE_F(A, D) \ + D = CPU->Read_Byte(A) & 0xFF; + +#define READ_WORD_F(A, D) \ + D = CPU->Read_Word(A) & 0xFFFF; + +#ifdef C68K_BIG_ENDIAN + #define READ_LONG_F(A, D) \ + D = CPU->Read_Word((A)) << 16; \ + D |= CPU->Read_Word((A) + 2) & 0xFFFF; + + #define READ_LONG_DEC_F(A, D) \ + D = CPU->Read_Word((A) + 2) & 0xFFFF; \ + D |= CPU->Read_Word((A)) << 16; +#else + #define READ_LONG_F(A, D) \ + D = CPU->Read_Word((A)) << 16; \ + D |= CPU->Read_Word((A) + 2) & 0xFFFF; + + #define READ_LONG_DEC_F(A, D) \ + D = CPU->Read_Word((A) + 2) & 0xFFFF; \ + D |= CPU->Read_Word((A)) << 16; +#endif + +#define READSX_BYTE_F(A, D) \ + D = (s32)(s8)CPU->Read_Byte(A); + +#define READSX_WORD_F(A, D) \ + D = (s32)(s16)CPU->Read_Word(A); + +#ifdef C68K_BIG_ENDIAN + #define READSX_LONG_F(A, D) \ + D = CPU->Read_Word((A)) << 16; \ + D |= CPU->Read_Word((A) + 2) & 0xFFFF; + + #define READSX_LONG_DEC_F(A, D) \ + D = CPU->Read_Word((A) + 2) & 0xFFFF; \ + D |= CPU->Read_Word((A)) << 16; +#else + #define READSX_LONG_F(A, D) \ + D = CPU->Read_Word((A)) << 16; \ + D |= CPU->Read_Word((A) + 2) & 0xFFFF; + + #define READSX_LONG_DEC_F(A, D) \ + D = CPU->Read_Word((A) + 2) & 0xFFFF; \ + D |= CPU->Read_Word((A)) << 16; +#endif + +#define WRITE_BYTE_F(A, D) \ + CPU->Write_Byte(A, D); + +#define WRITE_WORD_F(A, D) \ + CPU->Write_Word(A, D); + +#ifdef C68K_BIG_ENDIAN + #define WRITE_LONG_F(A, D) \ + CPU->Write_Word((A), (D) >> 16); \ + CPU->Write_Word((A) + 2, (D) & 0xFFFF); + + #define WRITE_LONG_DEC_F(A, D) \ + CPU->Write_Word((A) + 2, (D) & 0xFFFF); \ + CPU->Write_Word((A), (D) >> 16); +#else + #define WRITE_LONG_F(A, D) \ + CPU->Write_Word((A), (D) >> 16); \ + CPU->Write_Word((A) + 2, (D) & 0xFFFF); + + #define WRITE_LONG_DEC_F(A, D) \ + CPU->Write_Word((A) + 2, (D) & 0xFFFF); \ + CPU->Write_Word((A), (D) >> 16); +#endif + +#define PUSH_16_F(D) \ + CPU->Write_Word(CPU->A[7] -= 2, D); \ + +#define POP_16_F(D) \ + D = (u16)CPU->Read_Word(CPU->A[7]); \ + CPU->A[7] += 2; + +#ifdef C68K_BIG_ENDIAN + #define PUSH_32_F(D) \ + CPU->A[7] -= 4; \ + CPU->Write_Word(CPU->A[7] + 2, (D) & 0xFFFF); \ + CPU->Write_Word(CPU->A[7], (D) >> 16); + + #define POP_32_F(D) \ + D = CPU->Read_Word(CPU->A[7]) << 16; \ + D |= CPU->Read_Word(CPU->A[7] + 2) & 0xFFFF; \ + CPU->A[7] += 4; +#else + #define PUSH_32_F(D) \ + CPU->A[7] -= 4; \ + CPU->Write_Word(CPU->A[7] + 2, (D) & 0xFFFF); \ + CPU->Write_Word(CPU->A[7], (D) >> 16); + + #define POP_32_F(D) \ + D = CPU->Read_Word(CPU->A[7]) << 16; \ + D |= CPU->Read_Word(CPU->A[7] + 2) & 0xFFFF; \ + CPU->A[7] += 4; +#endif + +#define FETCH_BYTE \ +((*(u16*)PC) & 0xFF) + +#define FETCH_WORD \ +(*(u16*)PC) + + +#define FETCH_LONG \ +(*(u32*)PC) + +#define DECODE_EXT_WORD \ +{ \ + u32 ext; \ + \ + ext = (*(u16*)PC); \ + PC += 2; \ + \ + adr += (s32)((s8)(ext)); \ + if (ext & 0x0800) adr += (s32) CPU->D[ext >> 12]; \ + else adr += (s32)((s16)(CPU->D[ext >> 12])); \ +} + +#ifndef C68K_BIG_ENDIAN +#ifdef C68K_BYTE_SWAP_OPT + #undef FETCH_LONG + #define FETCH_LONG \ + ((((u32)(*(u16*)PC)) << 16) | ((u32)(*(u16*)(PC + 2)))) +// ((((u32)(*(u8*)(PC + 2))) | (((u32)(*(u8*)(PC + 3))) << 8) | (((u32)(*(u8*)PC)) << 16) | (((u32)(*(u8*)(PC + 1))) << 24))) +#else + #undef FETCH_BYTE + #define FETCH_BYTE \ + (*(u16*)PC) >> 8) + + #undef FETCH_WORD + #define FETCH_WORD \ + ((((u16)(*(u8*)PC)) << 8) | ((u16)(*(u8*)(PC + 1)))) +// ((((u16)(*(u8*)(PC + 1))) | (((u16)(*(u8*)PC)) << 8))) + + #undef FETCH_LONG + #define FETCH_LONG \ + ((((u32)(*(u8*)PC)) << 24) | (((u32)(*(u8*)(PC + 1))) << 16) | (((u32)(*(u8*)(PC + 2))) << 8) | ((u32)(*(u8*)(PC + 3)))) +// ((((u32)(*(u8*)(PC + 3))) | (((u32)(*(u8*)(PC + 2))) << 8) | (((u32)(*(u8*)(PC + 1))) << 16) | (((u32)(*(u8*)PC)) << 24))) + + #undef DECODE_EXT_WORD + #define DECODE_EXT_WORD \ + { \ + u32 ext; \ + \ + ext = (*(u16*)PC); \ + PC += 2; \ + \ + adr += (s32)((s8)(ext >> 8)); \ + if (ext & 0x0008) adr += (s32) CPU->D[(ext >> 4) & 0x000F]; \ + else adr += (s32)((s16)(CPU->D[(ext >> 4) & 0x000F])); \ + } +#endif +#endif + +#define GET_CCR \ + (((CPU->flag_C >> (C68K_SR_C_SFT - 0)) & 1) | \ + ((CPU->flag_V >> (C68K_SR_V_SFT - 1)) & 2) | \ + (((!CPU->flag_notZ) & 1) << 2) | \ + ((CPU->flag_N >> (C68K_SR_N_SFT - 3)) & 8) | \ + ((CPU->flag_X >> (C68K_SR_X_SFT - 4)) & 0x10)) + +#define GET_SR \ + ((CPU->flag_S << 0) | \ + (CPU->flag_I << 8) | \ + GET_CCR) + +#define SET_CCR(A) \ + CPU->flag_C = (A) << (C68K_SR_C_SFT - 0); \ + CPU->flag_V = (A) << (C68K_SR_V_SFT - 1); \ + CPU->flag_notZ = ~(A) & 4; \ + CPU->flag_N = (A) << (C68K_SR_N_SFT - 3); \ + CPU->flag_X = (A) << (C68K_SR_X_SFT - 4); + +#define SET_SR(A) \ + SET_CCR(A) \ + CPU->flag_I = ((A) >> 8) & 7; \ + CPU->flag_S = (A) & C68K_SR_S; + +#define CHECK_INT \ + { \ + s32 line, vect; \ + \ + line = CPU->IRQLine; \ + \ + if ((line == 7) || (line > CPU->flag_I)) \ + { \ + /* get vector */ \ + CPU->IRQLine = 0; \ + vect = CPU->Interrupt_CallBack(line); \ + if (vect == C68K_INT_ACK_AUTOVECTOR) \ + vect = C68K_INTERRUPT_AUTOVECTOR_EX + (line & 7); \ + \ + /* adjust CCnt */ \ + CCnt -= c68k_exception_cycle_table[vect]; \ + \ + /* swap A7 and USP */ \ + if (!CPU->flag_S) \ + { \ + u32 tmpSP; \ + \ + tmpSP = CPU->USP; \ + CPU->USP = CPU->A[7]; \ + CPU->A[7] = tmpSP; \ + } \ + \ + PRE_IO \ + \ + /* push PC and SR */ \ + PUSH_32_F(PC - CPU->BasePC) \ + PUSH_16_F(GET_SR) \ + \ + /* adjust SR */ \ + CPU->flag_S = C68K_SR_S; \ + CPU->flag_I = line; \ + \ + /* fetch new PC */ \ + READ_LONG_F(vect * 4, PC) \ + SET_PC(PC) \ + \ + POST_IO \ + } \ + } diff --git a/yabause/src/c68k/configure.in b/yabause/src/c68k/configure.in new file mode 100644 index 0000000000..ed03d740a2 --- /dev/null +++ b/yabause/src/c68k/configure.in @@ -0,0 +1,18 @@ +AC_INIT(gen68k, 0.9.10) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([1.8.0 foreign]) + +AC_PROG_CC + +AC_LANG(C) + +AC_C_BIGENDIAN + +AM_PROG_CC_C_O + +AC_CONFIG_FILES([Makefile]) + +AC_OUTPUT diff --git a/yabause/src/c68k/gen68k.c b/yabause/src/c68k/gen68k.c new file mode 100644 index 0000000000..1a540598b4 --- /dev/null +++ b/yabause/src/c68k/gen68k.c @@ -0,0 +1,3820 @@ +/* Copyright 2003-2004 Stephane Dallongeville + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/********************************************************************************* + * GEN68K.C : + * + * C68K generator source file + * + ********************************************************************************/ + +#include +#ifdef __WIN32__ +#include +#endif + +#include "../core.h" +#include "c68k.h" +#include "gen68k.h" + +#ifdef C68K_GEN + +#include "gen68k.inc" + +// to do : +// need accurate cycles calculations in MUL and DIV instruction +// some bugs to fix + +// opcode generation function +////////////////////////////// + +static void GenLogicI(char op) +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + if (current_ea != EA_DREG) current_cycle += 4; + switch (current_size) + { + case SIZE_BYTE: + wf_op("\tsrc = FETCH_BYTE;\n"); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_WORD: + wf_op("\tsrc = FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_LONG: + wf_op("\tsrc = FETCH_LONG;\n"); + wf_op("\tPC += 4;\n"); + current_cycle += 8; + break; + } + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // op + wf_op("\tres %c= src;\n", op); + // flag calculation + set_logic_flag(); + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenLogicICCR(char op) +{ + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tres = FETCH_BYTE & C68K_CCR_MASK;\n"); + wf_op("\tPC += 2;\n"); + wf_op("\tres %c= GET_CCR;\n", op); + wf_op("\tSET_CCR(res)\n"); + + terminate_op(20); +} + +static void GenLogicISR(char op) +{ + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tif (CPU->flag_S)\n"); + wf_op("\t{\n"); + wf_op("\t\tres = FETCH_WORD & C68K_SR_MASK;\n"); + wf_op("\t\tPC += 2;\n"); + wf_op("\t\tres %c= GET_SR;\n", op); + wf_op("\t\tSET_SR(res)\n"); + if (op != '|') + { + wf_op("\t\tif (!CPU->flag_S)\n"); + wf_op("\t\t{\n"); + wf_op("\t\t\tres = CPU->A[7];\n"); + wf_op("\t\t\tCPU->A[7] = CPU->USP;\n"); + wf_op("\t\t\tCPU->USP = res;\n"); + wf_op("\t\t}\n"); + } + wf_op("\t}\n"); + wf_op("\telse\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + wf_op("\t}\n"); + + // check for interrupt + fterminate_op(20); +} + +static void GenORI() +{ + GenLogicI('|'); +} + +static void GenORICCR() +{ + GenLogicICCR('|'); +} + +static void GenORISR() +{ + GenLogicISR('|'); +} + +static void GenANDI() +{ + GenLogicI('&'); +} + +static void GenANDICCR() +{ + GenLogicICCR('&'); +} + +static void GenANDISR() +{ + GenLogicISR('&'); +} + +static void GenEORI() +{ + GenLogicI('^'); +} + +static void GenEORICCR() +{ + GenLogicICCR('^'); +} + +static void GenEORISR() +{ + GenLogicISR('^'); +} + +static void GenArithI(char op) +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC | GEN_DST); + else + start_all(GEN_ALL); + + if ((op != ' ') && (current_ea != EA_DREG)) current_cycle += 4; + switch (current_size) + { + case SIZE_BYTE: + wf_op("\tsrc = FETCH_BYTE;\n"); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_WORD: + wf_op("\tsrc = FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_LONG: + wf_op("\tsrc = FETCH_LONG;\n"); + wf_op("\tPC += 4;\n"); + if (op == ' ') + { + if (current_ea == EA_DREG) current_cycle += 6; + else current_cycle += 4; + } else current_cycle += 8; + break; + } + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_dst(current_ea, current_op->reg_sft); + if (op == ' ') + { + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + } + else + { + // op + wf_op("\tres = dst %c src;\n", op); + // flag calculation + if (op == '+') set_add_flag(); + else set_sub_flag(); + // write + _ea_write(current_ea, current_op->reg_sft); + } + + terminate_op(8); +} + +static void GenSUBI() +{ + GenArithI('-'); +} + +static void GenADDI() +{ + GenArithI('+'); +} + +static void GenCMPI() +{ + GenArithI(' '); +} + +static void GenBitsOp(char op, u32 dyn) +{ + // generate jump table & opcode declaration + if (dyn) current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + if (current_ea == EA_DREG) + { + set_current_size(SIZE_LONG); + if ((op == 'c') || (op == ' ')) current_cycle += 2; + } + else set_current_size(SIZE_BYTE); + + // get shift value in src + if (dyn) + { + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + } + else + { + wf_op("\tsrc = FETCH_BYTE;\n"); + wf_op("\tPC += 2;\n"); + current_cycle += 4; + } + wf_op("\tsrc = 1 << (src & %d);\n", current_sft_mask); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // flag calculation + wf_op("\tCPU->flag_notZ = res & src;\n"); + // op + switch(op) + { + case 'c': + wf_op("\tres &= ~src;\n"); + break; + + case 'g': + wf_op("\tres ^= src;\n"); + break; + + case 's': + wf_op("\tres |= src;\n"); + break; + } + // write + if (op != ' ') + { + _ea_write(current_ea, current_op->reg_sft); + current_cycle += 4; + } + + terminate_op(4); +} + +static void GenBTSTn() +{ + GenBitsOp(' ', 0); +} + +static void GenBCHGn() +{ + GenBitsOp('g', 0); +} + +static void GenBCLRn() +{ + GenBitsOp('c', 0); +} + +static void GenBSETn() +{ + GenBitsOp('s', 0); +} + +static void GenBTST() +{ + GenBitsOp(' ', 1); +} + +static void GenBCHG() +{ + GenBitsOp('g', 1); +} + +static void GenBCLR() +{ + GenBitsOp('c', 1); +} + +static void GenBSET() +{ + GenBitsOp('s', 1); +} + +static void GenMOVEPWaD() +{ + // generate jump table & opcode declaration + current_ea = EA_D16A; + current_ea2 = EA_DREG; + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + set_current_size(SIZE_BYTE); + _ea_calc(current_ea, current_op->reg_sft); + mem_op("\tREAD_BYTE_F(adr + 0, res)\n"); + mem_op("\tREAD_BYTE_F(adr + 2, src)\n"); + // write + wf_op("\t*(u16*)(&CPU->D[(Opcode >> %d) & 7]) = (res << 8) | src;\n", current_op->reg2_sft); + + terminate_op(16); +} + +static void GenMOVEPLaD() +{ + // generate jump table & opcode declaration + current_ea = EA_D16A; + current_ea2 = EA_DREG; + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + set_current_size(SIZE_BYTE); + _ea_calc(EA_D16A, current_op->reg_sft); + mem_op("\tREAD_BYTE_F(adr, res)\n"); + wf_op("\tres <<= 24;\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tREAD_BYTE_F(adr, src)\n"); + wf_op("\tres |= src << 16;\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tREAD_BYTE_F(adr, src)\n"); + wf_op("\tres |= src << 8;\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tREAD_BYTE_F(adr, src)\n"); + // write + wf_op("\tCPU->D[(Opcode >> %d) & 7] = res | src;\n", current_op->reg2_sft); + + terminate_op(24); +} + +static void GenMOVEPWDa() +{ + // generate jump table & opcode declaration + current_ea = EA_D16A; + current_ea2 = EA_DREG; + start_all(GEN_ADR | GEN_RES); + + // read + set_current_size(SIZE_LONG); + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read(current_ea2, current_op->reg2_sft); + // write + set_current_size(SIZE_BYTE); + _ea_calc(current_ea, current_op->reg_sft); + mem_op("\tWRITE_BYTE_F(adr + 0, res >> 8)\n"); + mem_op("\tWRITE_BYTE_F(adr + 2, res >> 0)\n"); + + terminate_op(16); +} + +static void GenMOVEPLDa() +{ + // generate jump table & opcode declaration + current_ea = EA_D16A; + current_ea2 = EA_DREG; + start_all(GEN_ADR | GEN_RES); + + // read + set_current_size(SIZE_LONG); + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read(current_ea2, current_op->reg2_sft); + // write + set_current_size(SIZE_BYTE); + _ea_calc(current_ea, current_op->reg_sft); + mem_op("\tWRITE_BYTE_F(adr, res >> 24)\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tWRITE_BYTE_F(adr, res >> 16)\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tWRITE_BYTE_F(adr, res >> 8)\n"); + wf_op("\tadr += 2;\n"); + mem_op("\tWRITE_BYTE_F(adr, res >> 0)\n"); + + terminate_op(24); +} + +static void GenMOVE(u32 size) +{ + set_current_size(size); + + // generate jump table & opcode declaration + if (((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) && + ((current_ea2 == EA_AREG) || (current_ea2 == EA_DREG) || (current_ea2 == EA_IMM))) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // flag calculation + set_logic_flag(); + if ((current_ea2 == EA_ADEC) || (current_ea2 == EA_ADEC7)) current_cycle -= 2; + // write + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(4); +} + +static void GenMOVEB() +{ + GenMOVE(SIZE_BYTE); +} + +static void GenMOVEW() +{ + GenMOVE(SIZE_WORD); +} + +static void GenMOVEL() +{ + GenMOVE(SIZE_LONG); +} + +static void GenMOVEA(u32 size) +{ + set_current_size(size); + + // generate jump table & opcode declaration + current_ea2 = EA_AREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_sx(current_ea, current_op->reg_sft); + // write (dst = Ax) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(4); +} + +static void GenMOVEAW() +{ + GenMOVEA(SIZE_WORD); +} + +static void GenMOVEAL() +{ + GenMOVEA(SIZE_LONG); +} + +static void GenMOVEQ() +{ + u32 base = get_current_opcode_base(); + + // generate jump table + current_ea = EA_DREG; + gen_opjumptable_ext(base, 0x00, 0xFF, 1, base); + + // generate label & declarations + start_op(base, GEN_RES); + + // read + set_current_size(SIZE_BYTE); + wf_op("\tres = (s32)(s8)Opcode;\n"); + // fast flag calculation for moveQ + wf_op("\tCPU->flag_C = CPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_N = CPU->flag_notZ = res;\n"); + // write + set_current_size(SIZE_LONG); + _ea_calc(current_ea, current_op->reg_sft); + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(4); +} + +static void GenSingle(char op) +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) { + if (op == 'c') + start_all(GEN_RES); + else + start_all(GEN_RES | GEN_SRC); + } else { + if (op == 'c') + start_all(GEN_ADR | GEN_RES); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + } + + if (current_size == SIZE_LONG) current_cycle = 6; + else current_cycle= 4; + if (is_ea_memory(current_ea)) current_cycle *= 2; + + // read + _ea_calc(current_ea, current_op->reg_sft); + if (op != 'c') _ea_read_src(current_ea, current_op->reg_sft); + // op + switch (op) + { + case 'x': // negx + wf_op("\tres = -src - ((CPU->flag_X >> 8) & 1);\n"); + break; + + case 'g': // neg + wf_op("\tres = -src;\n"); + break; + + case 'n': // not + wf_op("\tres = ~src;\n"); + break; + + case 'c': // clr + wf_op("\tres = 0;\n"); + break; + } + // flag calculation + switch (op) + { + case 'x': // negx + set_negx_flag(); + break; + + case 'g': // neg + set_neg_flag(); + break; + + case 'n': // not + set_logicl_flag(); + break; + + case 'c': // clr + wf_op("\tCPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0;\n"); + break; + } + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(0); +} + +static void GenCLR() +{ + GenSingle('c'); +} + +static void GenNEGX() +{ + GenSingle('x'); +} + +static void GenNEG() +{ + GenSingle('g'); +} + +static void GenNOT() +{ + GenSingle('n'); +} + +static void GenMOVESRa() +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + wf_op("\tres = GET_SR;\n"); + // write + set_current_size(SIZE_WORD); + if (is_ea_memory(current_ea)) current_cycle += 2; + _ea_calc(current_ea, current_op->reg_sft); + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenMOVEaSR() +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + wf_op("\tif (CPU->flag_S)\n"); + wf_op("\t{\n"); + // read + set_current_size(SIZE_WORD); + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + wf_op("\t\tSET_SR(res)\n"); + wf_op("\t\tif (!CPU->flag_S)\n"); + wf_op("\t\t{\n"); + wf_op("\t\t\tres = CPU->A[7];\n"); + wf_op("\t\t\tCPU->A[7] = CPU->USP;\n"); + wf_op("\t\t\tCPU->USP = res;\n"); + wf_op("\t\t}\n"); + wf_op("\t}\n"); + wf_op("\telse\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + wf_op("\t}\n"); + + // force terminaison to check for interrupt + fterminate_op(12); +} + +static void GenMOVEaCCR() +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + set_current_size(SIZE_WORD); + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // write + wf_op("\tSET_CCR(res)\n"); + + terminate_op(12); +} + +static void GenMOVEAUSP() +{ + current_ea = EA_AREG; + + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + quick_terminate_op(4); + wf_op("\t}\n"); + + // read + set_current_size(SIZE_LONG); + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // write + wf_op("\tCPU->USP = res;\n"); + + terminate_op(4); +} + +static void GenMOVEUSPA() +{ + current_ea = EA_AREG; + + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + quick_terminate_op(4); + wf_op("\t}\n"); + + // read + wf_op("\tres = CPU->USP;\n"); + // write + set_current_size(SIZE_LONG); + _ea_calc(current_ea, current_op->reg_sft); + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(4); +} + +static void GenPEA() +{ + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(0); + else + start_all(GEN_ADR); + + _ea_calc_free(current_ea, current_op->reg_sft); + mem_op("\tPUSH_32_F(adr)\n"); + + terminate_op(lea_pea_cycle_table[current_ea] + 12); +} + +static void GenSWAP() +{ + current_ea = EA_DREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // op + wf_op("\tres = (res >> 16) | (res << 16);\n"); + // flag calculation + set_logic_flag(); + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(4); +} + +static void GenMOVEMaR() +{ + // generate jump table & opcode declaration + start_all(GEN_ALL); + + // get register mask + wf_op("\tres = FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + // get adr + if (current_ea == EA_AINC) wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", current_op->reg_sft); + else if (current_ea == EA_AINC7) wf_op("\tadr = CPU->A[7];\n"); + else _ea_calc(current_ea, current_op->reg_sft); + wf_op("\tsrc = (pointer)(&CPU->D[0]);\n"); + + wf_op("\tdst = adr;\n"); + do_pre_io(); + + wf_op("\tdo\n"); + wf_op("\t{\n"); + wf_op("\t\tif (res & 1)\n"); + wf_op("\t\t{\n"); + + if (current_size == SIZE_WORD) + { + wf_op("\t\t\tREADSX_WORD_F(adr, *(s32*)src)\n"); + wf_op("\t\t\tadr += 2;\n"); + } + else + { + wf_op("\t\t\tREAD_LONG_F(adr, *(u32*)src)\n"); + wf_op("\t\t\tadr += 4;\n"); + } + wf_op("\t\t}\n"); + wf_op("\t\tsrc += 4;\n"); + wf_op("\t} while (res >>= 1);\n"); + + if (current_ea == EA_AINC) wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", current_op->reg_sft); + else if (current_ea == EA_AINC7) wf_op("\tCPU->A[7] = adr;\n"); + adds_CCnt("(adr - dst) * 2"); + + terminate_op(movem_cycle_table[current_ea] + 12); +} + +static void GenMOVEMRa() +{ + // generate jump table & opcode declaration + start_all(GEN_ALL); + + // get register mask + wf_op("\tres = FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + // get adr + if (current_ea == EA_ADEC) wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", current_op->reg_sft); + else if (current_ea == EA_ADEC7) wf_op("\tadr = CPU->A[7];\n"); + else _ea_calc(current_ea, current_op->reg_sft); + if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\tsrc = (pointer)(&CPU->A[7]);\n"); + else wf_op("\tsrc = (pointer)(&CPU->D[0]);\n"); + + wf_op("\tdst = adr;\n"); + do_pre_io(); + + wf_op("\tdo\n"); + wf_op("\t{\n"); + wf_op("\t\tif (res & 1)\n"); + wf_op("\t\t{\n"); + + if (current_size == SIZE_WORD) + { + if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\t\t\tadr -= 2;\n"); + wf_op("\t\t\tWRITE_WORD_F(adr, *(u16*)src)\n"); + if (!((current_ea == EA_ADEC) || (current_ea == EA_ADEC7))) wf_op("\t\t\tadr += 2;\n"); + } + else + { + if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) + { + wf_op("\t\t\tadr -= 4;\n"); + wf_op("\t\t\tWRITE_LONG_DEC_F(adr, *(u32*)src)\n"); + } + else + { + wf_op("\t\t\tWRITE_LONG_F(adr, *(u32*)src)\n"); + wf_op("\t\t\tadr += 4;\n"); + } + } + wf_op("\t\t}\n"); + if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\t\tsrc -= 4;\n"); + else wf_op("\t\tsrc += 4;\n"); + wf_op("\t} while (res >>= 1);\n"); + + if (current_ea == EA_ADEC) wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", current_op->reg_sft); + else if (current_ea == EA_ADEC7) wf_op("\tCPU->A[7] = adr;\n"); + if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) adds_CCnt("(dst - adr) * 2"); + else adds_CCnt("(adr - dst) * 2"); + + terminate_op(movem_cycle_table[current_ea] + 8); +} + +static void GenEXT() +{ + current_ea = EA_DREG; + // generate jump table & opcode declaration + start_all(GEN_RES); + + // read + set_current_size(current_size - 1); + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_sx(current_ea, current_op->reg_sft); + // flag calculation + set_logic_flag(); + // write + set_current_size(current_size + 1); + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(4); +} + +static void GenTST() +{ + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // flag calculation + set_logic_flag(); + + terminate_op(4); +} + +static void GenTAS() +{ + set_current_size(SIZE_BYTE); + + if (is_ea_memory(current_ea)) current_cycle += 6; + + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // flag calculation + set_logic_flag(); +#ifndef C68K_TAS_CAN_SET_MEMORY + if (current_ea < EA_AIND) + { +#endif + // flag calculation + wf_op("\tres |= 0x80;\n"); + // write + _ea_write(current_ea, current_op->reg_sft); +#ifndef C68K_TAS_CAN_SET_MEMORY + } +#endif + + terminate_op(4); +} + +static void GenTRAP() +{ + u32 base; + + base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, (0 << 0), (15 << 0), (1 << 0), base); + + // generate label & declarations + start_op(base, GEN_RES); + + gen_exception("\t", "C68K_TRAP_BASE_EX + (Opcode & 0xF)"); + + terminate_op(4); +} + +static void GenTRAPV() +{ + // generate label & declarations + start_all(GEN_RES); + + wf_op("\tif %s\n", get_cond_as_cond(COND_VS, 0)); + wf_op("\t{\n"); + gen_exception("\t\t", "C68K_TRAPV_EX"); + wf_op("\t}\n"); + + terminate_op(4); +} + +static void GenLINK() +{ + current_ea = EA_AREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(GEN_RES); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // push + mem_op("\tPUSH_32_F(res)\n"); + wf_op("\tres = CPU->A[7];\n"); + // write + _ea_write(current_ea, current_op->reg_sft); + // update SP + wf_op("\tCPU->A[7] += (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + + terminate_op(16); +} + +static void GenLINKA7() +{ + current_ea = EA_AREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(0); + + // push A7 + wf_op("\tCPU->A[7] -= 4;\n"); + mem_op("\tWRITE_LONG_DEC_F(CPU->A[7], CPU->A[7])\n"); + // update A7 + wf_op("\tCPU->A[7] += (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + + terminate_op(16); +} + +static void GenULNK() +{ + current_ea = EA_AREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // pop + wf_op("\tCPU->A[7] = src + 4;\n"); + mem_op("\tREAD_LONG_F(src, res)\n"); + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(12); +} + +static void GenULNKA7() +{ + current_ea = EA_AREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(0); + + mem_op("\tREAD_LONG_F(CPU->A[7], CPU->A[7])\n"); + + terminate_op(12); +} + +static void GenRESET() +{ + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + quick_terminate_op(4); + wf_op("\t}\n"); + + // Reset callback function + mem_op("\tCPU->Reset_CallBack();\n"); + + terminate_op(132); +} + +static void GenLEA() +{ + current_ea2 = EA_AREG; + set_current_size(SIZE_LONG); + // generate jump table & opcode declaration + start_all(GEN_ADR | GEN_RES); + + _ea_calc_free(current_ea, current_op->reg_sft); + wf_op("\tres = adr;\n"); + current_cycle = lea_pea_cycle_table[current_ea]; + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(4); +} + +static void GenNOP() +{ + start_all(0); + terminate_op(4); +} + +static void GenILLEGAL() +{ + start_all(GEN_RES); + + gen_exception("\t\t", "C68K_ILLEGAL_INSTRUCTION_EX"); + + terminate_op(4); +} + +static void GenCHK() +{ + current_ea2 = EA_DREG; + set_current_size(SIZE_WORD); + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read Src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read Dx + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read(current_ea2, current_op->reg2_sft); + + wf_op("\tif (((s32)res < 0) || (res > src))\n"); + wf_op("\t{\n"); + wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + gen_exception("\t\t", "C68K_CHK_EX"); + wf_op("\t}\n"); + + terminate_op(10); +} + +static void GenSTOP() +{ + // generate jump table & opcode declaration + start_all(GEN_RES); + + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + wf_op("\t\tPC += 2;\n"); + gen_privilege_exception("\t\t"); + quick_terminate_op(4); + wf_op("\t}\n"); + + // read & set SR + wf_op("\tres = FETCH_WORD & C68K_SR_MASK;\n"); + wf_op("\tPC += 2;\n"); + wf_op("\tSET_SR(res)\n"); + + // if S flag not set --> we swap stack pointer + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + wf_op("\t\tres = CPU->A[7];\n"); + wf_op("\t\tCPU->A[7] = CPU->USP;\n"); + wf_op("\t\tCPU->USP = res;\n"); + wf_op("\t}\n"); + + wf_op("\tCPU->Status |= C68K_HALTED;\n"); + wf_op("\tCCnt = 0;\n"); + + // force end execution + fterminate_op(4); +} + +static void GenRTE() +{ + start_all(GEN_RES); + + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + gen_privilege_exception("\t\t"); + quick_terminate_op(4); + wf_op("\t}\n"); + + // restore SR and PC + mem_op("\tPOP_16_F(res)\n"); + wf_op("\tSET_SR(res)\n"); + mem_op("\tPOP_32_F(res)\n"); + wf_op("\tSET_PC(res)\n"); + + // if S flag not set --> we swap stack pointer + wf_op("\tif (!CPU->flag_S)\n"); + wf_op("\t{\n"); + wf_op("\t\tres = CPU->A[7];\n"); + wf_op("\t\tCPU->A[7] = CPU->USP;\n"); + wf_op("\t\tCPU->USP = res;\n"); + wf_op("\t}\n"); + + // check for interrupt + fterminate_op(20); +} + +static void GenRTS() +{ + start_all(GEN_RES); + + mem_op("\tPOP_32_F(res)\n"); + wf_op("\tSET_PC(res)\n"); + + terminate_op(16); +} + +static void GenRTR() +{ + start_all(GEN_RES); + + mem_op("\tPOP_16_F(res)\n"); + wf_op("\tSET_CCR(res)\n"); + mem_op("\tPOP_32_F(res)\n"); + wf_op("\tSET_PC(res)\n"); + + terminate_op(20); +} + +static void GenJSR() +{ + start_all(GEN_ADR); + + // get adr + _ea_calc_free(current_ea, current_op->reg_sft); + mem_op("\tPUSH_32_F(PC - CPU->BasePC)\n"); + wf_op("\tSET_PC(adr)\n"); + + terminate_op(jmp_jsr_cycle_table[current_ea] + 12); +} + +static void GenJMP() +{ + start_all(GEN_ADR); + + // get adr + _ea_calc_free(current_ea, current_op->reg_sft); + wf_op("\tSET_PC(adr)\n"); + + terminate_op(jmp_jsr_cycle_table[current_ea] + 4); +} + +static void GenSTCC() +{ + u32 base, cond; + + base = get_current_opcode_base(); + + for(cond = 0; cond < 0x10; cond++) + { + // generate jump table + gen_opjumptable(base + (cond << 8)); + // generate label & declarations + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_op(base + (cond << 8), GEN_RES); + else + start_op(base + (cond << 8), GEN_ADR | GEN_RES); + + set_current_size(SIZE_BYTE); + + if (is_ea_memory(current_ea)) current_cycle += 4; + + // op + _ea_calc(current_ea, current_op->reg_sft); + if ((cond != COND_TR) && (cond != COND_FA)) + { + wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); + wf_op("\t{\n"); + } + if (cond != COND_FA) + { + wf_op("\tres = 0xFF;\n"); + // write + _ea_write(current_ea, current_op->reg_sft); + if (!is_ea_memory(current_ea)) quick_terminate_op(6); + else quick_terminate_op(4); + } + if ((cond != COND_TR) && (cond != COND_FA)) + { + wf_op("\t}\n"); + } + if (cond != COND_TR) + { + wf_op("\tres = 0;\n"); + // write + _ea_write(current_ea, current_op->reg_sft); + quick_terminate_op(4); + } + + wf_op("}\n"); + } +} + +static void GenDBCC() +{ + u32 base, cond; + + base = get_current_opcode_base(); + + current_ea = EA_DREG; + set_current_size(SIZE_WORD); + + for(cond = 0; cond < 0x10; cond++) + { + // generate jump table + gen_opjumptable(base + (cond << 8)); + // generate label & declarations + start_op(base + (cond << 8), cond==COND_TR ? 0 : GEN_RES); + + if (cond != COND_TR) + { + if (cond != COND_FA) + { + wf_op("\tif %s\n", get_cond_as_cond(cond, 1)); + wf_op("\t{\n"); + } + + // read Dx + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // dec Dx + wf_op("\tres--;\n"); + // write Dx + _ea_write(current_ea, current_op->reg_sft); + wf_op("\tif ((s32)res != -1)\n"); + wf_op("\t{\n"); + wf_op("\t\tPC += (s32)(s16)FETCH_WORD;\n"); + // unbase PC + wf_op("\t\tPC -= CPU->BasePC;\n"); + // rebase PC + wf_op("\t\tSET_PC(PC);\n"); + quick_terminate_op(10); + wf_op("\t}\n"); + + if (cond != COND_FA) + { + wf_op("\t}\n"); + wf_op("\telse\n"); + wf_op("\t{\n"); + wf_op("\t\tPC += 2;\n"); + quick_terminate_op(12); + wf_op("\t}\n"); + } + } + + wf_op("\tPC += 2;\n"); + + if (cond == COND_TR) terminate_op(12); + else terminate_op(14); + } +} + +static void GenBCC() +{ + u32 base, cond; + + base = get_current_opcode_base(); + + for(cond = 2; cond < 0x10; cond++) + { + // generate jump table + gen_opjumptable_ext(base + (cond << 8), 0x01, 0xFF, 1, base + (cond << 8) + 0x01); + // generate label & declarations + start_op(base + (cond << 8) + 0x01, 0); + + // op + wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); + wf_op("\t{\n"); + wf_op("\t\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement + add_CCnt(2); + wf_op("\t}\n"); + + terminate_op(8); + } +} + +static void GenBCC16() +{ + u32 base, cond; + + base = get_current_opcode_base(); + + for(cond = 2; cond < 0x10; cond++) + { + // generate jump table + gen_opjumptable(base + (cond << 8)); + // generate label & declarations + start_op(base + (cond << 8), 0); + + // op + wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); + wf_op("\t{\n"); + wf_op("\t\tPC += (s32)(s16)FETCH_WORD;\n"); + // unbase PC + wf_op("\t\tPC -= CPU->BasePC;\n"); + // rebase PC + wf_op("\t\tSET_PC(PC);\n"); + quick_terminate_op(10); + wf_op("\t}\n"); + + wf_op("\tPC += 2;\n"); + + terminate_op(12); + } +} + +static void GenBRA() +{ + u32 base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, 0x01, 0xFF, 1, base + 0x01); + // generate label & declarations + start_op(base + 0x01, 0); + + wf_op("\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement + + terminate_op(10); +} + +static void GenBRA16() +{ + u32 base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable(base + 0x00); + // generate label & declarations + start_op(base + 0x00, 0); + + wf_op("\tPC += (s32)(s16)FETCH_WORD;\n"); + // unbase PC + wf_op("\tPC -= CPU->BasePC;\n"); + // rebase PC + wf_op("\tSET_PC(PC);\n"); + + terminate_op(10); +} + +static void GenBSR() +{ + u32 base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, 0x01, 0xFF, 1, base + 0x01); + // generate label & declarations + start_op(base + 0x01, 0); + + mem_op("\tPUSH_32_F(PC - CPU->BasePC)\n"); + wf_op("\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement + + terminate_op(18); +} + +static void GenBSR16() +{ + u32 base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable(base + 0x00); + // generate label & declarations + start_op(base + 0x00, GEN_RES); + + wf_op("\tres = (s32)(s16)FETCH_WORD;\n"); + // unbase PC + wf_op("\tPC -= CPU->BasePC;\n"); + mem_op("\tPUSH_32_F(PC + 2)\n"); + wf_op("\tPC += (s32) res;\n"); + // rebase PC for 16 bits deplacement + wf_op("\tSET_PC(PC);\n"); + + terminate_op(18); +} + +static void GenArithQ(char op) +{ + u32 base; + + if ((current_ea == EA_AREG) && (current_size == SIZE_BYTE)) return; + + base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, (0 << 9), (7 << 9), (1 << 9), base); + + // generate label & declarations + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_op(base, GEN_DST | GEN_RES | GEN_SRC); + else + start_op(base, GEN_ALL); + + if (current_ea == EA_AREG) set_current_size(SIZE_LONG); + + if (is_ea_memory(current_ea)) current_cycle += 4; + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src + wf_op("\tsrc = (((Opcode >> 9) - 1) & 7) + 1;\n"); + // read dst + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_dst(current_ea, current_op->reg_sft); + // op + wf_op("\tres = dst %c src;\n", op); + // flag calculation + if (current_ea != EA_AREG) + { + if (op == '+') set_add_flag(); + else set_sub_flag(); + } + // write dst + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(4); +} + +static void GenADDQ() +{ + GenArithQ('+'); +} + +static void GenSUBQ() +{ + GenArithQ('-'); +} + +static void GenLogicaD(char op) +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) + { + if (!is_ea_memory(current_ea)) current_cycle += 2; + else current_cycle += 4; + } + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres %c= src;\n", op); + // flag calculation + set_logic_flag(); + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(4); +} + +static void GenLogicDa(char op) +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + // read dst + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // op + wf_op("\tres %c= src;\n", op); + // flag calculation + set_logic_flag(); + // write dst + _ea_write(current_ea, current_op->reg_sft); + + if (current_ea == EA_DREG) terminate_op(4); + else terminate_op(8); +} + +static void GenANDaD() +{ + GenLogicaD('&'); +} + +static void GenANDDa() +{ + GenLogicDa('&'); +} + +static void GenORaD() +{ + GenLogicaD('|'); +} + +static void GenORDa() +{ + GenLogicDa('|'); +} + +static void GenEORDa() +{ + GenLogicDa('^'); +} + +static void GenNBCD() +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES); + else + start_all(GEN_ADR | GEN_RES); + + if (is_ea_memory(current_ea)) current_cycle += 2; + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + + // op + wf_op("\tres = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1);\n"); + wf_op("\n"); + wf_op("\tif (res != 0x9a)\n"); + wf_op("\t{\n"); + wf_op("\t\tif ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10;\n"); + wf_op("\t\tres &= 0xFF;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + // flag calculation + wf_op("\t\tCPU->flag_notZ |= res;\n"); + wf_op("\t\tCPU->flag_X = CPU->flag_C = C68K_SR_C;\n"); + + wf_op("\t}\n"); + wf_op("\telse CPU->flag_X = CPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = res;\n"); + + terminate_op(6); +} + +static void GenBCD(char op) +{ + // op + wf_op("\tres = (dst & 0xF) %c (src & 0xF) %c ((CPU->flag_X >> C68K_SR_X_SFT) & 1);\n", op, op); + wf_op("\tif (res > 9) res %c= 6;\n", op); + wf_op("\tres += (dst & 0xF0) %c (src & 0xF0);\n", op, op); + + // flag calculation + wf_op("\tif (res > 0x99)\n"); + wf_op("\t{\n"); + switch (op) + { + case '+': + wf_op("\t\tres -= 0xA0;\n"); + break; + + case '-': + wf_op("\t\tres += 0xA0;\n"); + break; + } + wf_op("\t\tCPU->flag_X = CPU->flag_C = C68K_SR_C;\n"); + wf_op("\t}\n"); + wf_op("\telse CPU->flag_X = CPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); + wf_op("\tCPU->flag_N = res;\n"); +} + +static void GenxBCD(char op) +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + current_ea = EA_DREG; + current_ea2 = EA_DREG; + start_all(GEN_DST | GEN_RES | GEN_SRC); + + // read src (Dx) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + + // op & flag calculation + GenBCD(op); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(6); +} + +static void GenxBCDM(char op) +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + current_ea = EA_ADEC; + current_ea2 = EA_ADEC; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + + // op & flag calculation + GenBCD(op); + + // write dst (ADEC) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(6); +} + +static void GenxBCD7M(char op) +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + current_ea = EA_ADEC7; + current_ea2 = EA_ADEC; + start_all(GEN_ALL); + + // read src (ADEC7) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + + // op & flag calculation + GenBCD(op); + + // write dst (ADEC) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(6); +} + +static void GenxBCDM7(char op) +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + current_ea = EA_ADEC; + current_ea2 = EA_ADEC7; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC7) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + + // op & flag calculation + GenBCD(op); + + // write dst (ADEC7) + _ea_write(current_ea2, 0); + + terminate_op(6); +} + +static void GenxBCD7M7(char op) +{ + set_current_size(SIZE_BYTE); + + // generate jump table & opcode declaration + current_ea = EA_ADEC7; + current_ea2 = EA_ADEC7; + start_all(GEN_ALL); + + // read src (ADEC7) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC7) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + + // op & flag calculation + GenBCD(op); + + // write dst (ADEC7) + _ea_write(current_ea2, 0); + + terminate_op(6); +} + +static void GenABCD() +{ + GenxBCD('+'); +} + +static void GenABCDM() +{ + GenxBCDM('+'); +} + +static void GenABCD7M() +{ + GenxBCD7M('+'); +} + +static void GenABCDM7() +{ + GenxBCDM7('+'); +} + +static void GenABCD7M7() +{ + GenxBCD7M7('+'); +} + +static void GenSBCD() +{ + GenxBCD('-'); +} + +static void GenSBCDM() +{ + GenxBCDM('-'); +} + +static void GenSBCD7M() +{ + GenxBCD7M('-'); +} + +static void GenSBCDM7() +{ + GenxBCDM7('-'); +} + +static void GenSBCD7M7() +{ + GenxBCD7M7('-'); +} + +static void GenDIVU() +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_DST | GEN_RES | GEN_SRC); + else + start_all(GEN_ALL); + + set_current_size(SIZE_WORD); + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // division by zero + wf_op("\tif (src == 0)\n"); + wf_op("\t{\n"); + gen_exception("\t\t", "C68K_ZERO_DIVIDE_EX"); + quick_terminate_op(10); + wf_op("\t}\n"); + + set_current_size(SIZE_LONG); + + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + + wf_op("\t{\n"); + wf_op("\t\tu32 q, r;\n"); + wf_op("\n"); + wf_op("\t\tq = dst / src;\n"); + wf_op("\t\tr = dst %% src;\n"); + wf_op("\n"); + + wf_op("\t\tif (q & 0xFFFF0000)\n"); + wf_op("\t\t{\n"); + // overflow occured + wf_op("\t\t\tCPU->flag_V = C68K_SR_V;\n"); + quick_terminate_op(70); + wf_op("\t\t}\n"); + + // quotient size = word + set_current_size(SIZE_WORD); + + wf_op("\t\tq &= 0x%.8X;\n", current_bits_mask); + wf_op("\t\tCPU->flag_notZ = q;\n"); + wf_op("\t\tCPU->flag_N = q >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); + + wf_op("\t\tres = q | (r << 16);\n"); + + set_current_size(SIZE_LONG); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + wf_op("\t}\n"); + + // max cycle = 140 + terminate_op(140 - 50); +} + +static void GenDIVS() +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_DST | GEN_RES | GEN_SRC); + else + start_all(GEN_ALL); + + set_current_size(SIZE_WORD); + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src_sx(current_ea, current_op->reg_sft); + + // division by zero + wf_op("\tif (src == 0)\n"); + wf_op("\t{\n"); + gen_exception("\t\t", "C68K_ZERO_DIVIDE_EX"); + quick_terminate_op(10); + wf_op("\t}\n"); + + set_current_size(SIZE_LONG); + + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + + // division by zero + wf_op("\tif ((dst == 0x80000000) && (src == -1))\n"); + wf_op("\t{\n"); + wf_op("\t\tCPU->flag_notZ = CPU->flag_N = 0;\n"); + wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); + wf_op("\t\tres = 0;\n"); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + quick_terminate_op(50); + wf_op("\t}\n"); + + wf_op("\t{\n"); + wf_op("\t\ts32 q, r;\n"); + wf_op("\n"); + wf_op("\t\tq = (s32)dst / (s32)src;\n"); + wf_op("\t\tr = (s32)dst %% (s32)src;\n"); + wf_op("\n"); + + wf_op("\t\tif ((q > 0x7FFF) || (q < -0x8000))\n"); + wf_op("\t\t{\n"); + // overflow occured + wf_op("\t\t\tCPU->flag_V = C68K_SR_V;\n"); + quick_terminate_op(80); + wf_op("\t\t}\n"); + + // quotient size = word + set_current_size(SIZE_WORD); + + wf_op("\t\tq &= 0x%.8X;\n", current_bits_mask); + wf_op("\t\tCPU->flag_notZ = q;\n"); + wf_op("\t\tCPU->flag_N = q >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); + + wf_op("\t\tres = q | (r << 16);\n"); + + set_current_size(SIZE_LONG); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + wf_op("\t}\n"); + + // max cycle = 158 + terminate_op(158 - 50); +} + +static void GenMULU() +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + set_current_size(SIZE_WORD); + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read(current_ea2, current_op->reg2_sft); + + set_current_size(SIZE_LONG); + // op + wf_op("\tres *= src;\n"); + + // flag calculation + wf_op("\tCPU->flag_N = res >> 24;\n"); + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_V = CPU->flag_C = 0;\n"); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + // min cycle = 38; max cycle = 70 + terminate_op(38 + (2 * 6)); +} + +static void GenMULS() +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_RES | GEN_SRC); + else + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + set_current_size(SIZE_WORD); + // read src signed + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src_sx(current_ea, current_op->reg_sft); + // read dst signed (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_sx(current_ea2, current_op->reg2_sft); + + set_current_size(SIZE_LONG); + // op + //wf_op("\t(s32)res *= (s32)src;\n"); + wf_op("\tres *= (s32)src;\n"); // antime fix + + // flag calculation + wf_op("\tCPU->flag_N = res >> 24;\n"); + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_V = CPU->flag_C = 0;\n"); + + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + // min cycle = 38; max cycle = 70 + terminate_op(38 + (2 * 6)); +} + +static void GenArithaD(char op) +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_DST | GEN_RES | GEN_SRC); + else + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) + { + if (!is_ea_memory(current_ea)) current_cycle += 2; + else current_cycle += 4; + } + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + if (op == ' ') + { + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + } + else + { + // op + wf_op("\tres = dst %c src;\n", op); + // flag calculation + if (op == '+') set_add_flag(); + else set_sub_flag(); + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + } + + terminate_op(4); +} + +static void GenArithDa(char op) +{ + // generate jump table & opcode declaration + current_ea2 = EA_DREG; + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + // read dst + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_dst(current_ea, current_op->reg_sft); + // op + wf_op("\tres = dst %c src;\n", op); + // flag calculation + if (op == '+') set_add_flag(); + else set_sub_flag(); + // write dst + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenArithA(char op) +{ + // generate jump table & opcode declaration + current_ea2 = EA_AREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_DST | GEN_RES | GEN_SRC); + else + start_all(GEN_ALL); + + if ((op != ' ') && ((current_size == SIZE_WORD) || (is_ea_memory(current_ea)))) current_cycle += 2; + + // read src + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src_sx(current_ea, current_op->reg_sft); + // read dst (Ax) + set_current_size(SIZE_LONG); + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + if (op == ' ') + { + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + } + else + { + // op + wf_op("\tres = dst %c src;\n", op); + // write dst (Ax) + _ea_write(current_ea2, current_op->reg2_sft); + } + + terminate_op(6); +} + +static void GenArithX(char op) +{ + // generate jump table & opcode declaration + current_ea = EA_DREG; + current_ea2 = EA_DREG; + if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) + start_all(GEN_DST | GEN_RES | GEN_SRC); + else + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (Dx) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (Dx) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); + // flag calculation + if (op == '+') set_addx_flag(); + else set_subx_flag(); + // write dst (Dx) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(4); +} + +static void GenArithXM(char op) +{ + // generate jump table & opcode declaration + current_ea = EA_ADEC; + current_ea2 = EA_ADEC; + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); + // flag calculation + if (op == '+') set_addx_flag(); + else set_subx_flag(); + // write dst (ADEC) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(6); +} + +static void GenArithX7M(char op) +{ + // generate jump table & opcode declaration + current_ea = EA_ADEC7; + current_ea2 = EA_ADEC; + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (ADEC7) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); + // flag calculation + if (op == '+') set_addx_flag(); + else set_subx_flag(); + // write dst (ADEC) + _ea_write(current_ea2, current_op->reg2_sft); + + terminate_op(6); +} + +static void GenArithXM7(char op) +{ + // generate jump table & opcode declaration + current_ea = EA_ADEC; + current_ea2 = EA_ADEC7; + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC7) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + // op + wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); + // flag calculation + if (op == '+') set_addx_flag(); + else set_subx_flag(); + // write dst (ADEC7) + _ea_write(current_ea2, 0); + + terminate_op(6); +} + +static void GenArithX7M7(char op) +{ + // generate jump table & opcode declaration + current_ea = EA_ADEC7; + current_ea2 = EA_ADEC7; + start_all(GEN_ALL); + + if (current_size == SIZE_LONG) current_cycle += 4; + + // read src (ADEC7) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC7) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + // op + wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); + // flag calculation + if (op == '+') set_addx_flag(); + else set_subx_flag(); + // write dst (ADEC7) + _ea_write(current_ea2, 0); + + terminate_op(6); +} + +static void GenADDaD() +{ + GenArithaD('+'); +} + +static void GenADDDa() +{ + GenArithDa('+'); +} + +static void GenADDA() +{ + GenArithA('+'); +} + +static void GenADDX() +{ + GenArithX('+'); +} + +static void GenADDXM() +{ + GenArithXM('+'); +} + +static void GenADDX7M() +{ + GenArithX7M('+'); +} + +static void GenADDXM7() +{ + GenArithXM7('+'); +} + +static void GenADDX7M7() +{ + GenArithX7M7('+'); +} + +static void GenSUBaD() +{ + GenArithaD('-'); +} + +static void GenSUBDa() +{ + GenArithDa('-'); +} + +static void GenSUBA() +{ + GenArithA('-'); +} + +static void GenSUBX() +{ + GenArithX('-'); +} + +static void GenSUBXM() +{ + GenArithXM('-'); +} + +static void GenSUBX7M() +{ + GenArithX7M('-'); +} + +static void GenSUBXM7() +{ + GenArithXM7('-'); +} + +static void GenSUBX7M7() +{ + GenArithX7M7('-'); +} + +static void GenCMP() +{ + GenArithaD(' '); +} + +static void GenCMPA() +{ + GenArithA(' '); +} + +static void GenCMPM() +{ + // generate jump table & opcode declaration + current_ea = EA_AINC; + current_ea2 = EA_AINC; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + + terminate_op(4); + +} + +static void GenCMP7M() +{ + // generate jump table & opcode declaration + current_ea = EA_AINC7; + current_ea2 = EA_AINC; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC) + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_dst(current_ea2, current_op->reg2_sft); + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + + terminate_op(4); + +} + +static void GenCMPM7() +{ + // generate jump table & opcode declaration + current_ea = EA_AINC; + current_ea2 = EA_AINC7; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + // read dst (ADEC) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + + terminate_op(4); + +} + +static void GenCMP7M7() +{ + // generate jump table & opcode declaration + current_ea = EA_AINC7; + current_ea2 = EA_AINC7; + start_all(GEN_ALL); + + // read src (ADEC) + _ea_calc(current_ea, 0); + _ea_read_src(current_ea, 0); + // read dst (ADEC) + _ea_calc(current_ea2, 0); + _ea_read_dst(current_ea2, 0); + // op + wf_op("\tres = dst - src;\n"); + // flag calculation + set_cmp_flag(); + + terminate_op(4); + +} + +static void GenEXGDD() +{ + // generate jump table & opcode declaration + set_current_size(SIZE_LONG); + current_ea = EA_DREG; + current_ea2 = EA_DREG; + start_all(GEN_RES | GEN_SRC); + + // read R1 + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // read R2 + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + // write R1 + _ea_write(current_ea2, current_op->reg2_sft); + wf_op("\tres = src;\n"); + // write R2 + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenEXGAA() +{ + // generate jump table & opcode declaration + set_current_size(SIZE_LONG); + current_ea = EA_AREG; + current_ea2 = EA_AREG; + start_all(GEN_RES | GEN_SRC); + + // read R1 + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // read R2 + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + // write R1 + _ea_write(current_ea2, current_op->reg2_sft); + wf_op("\tres = src;\n"); + // write R2 + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenEXGAD() +{ + // generate jump table & opcode declaration + set_current_size(SIZE_LONG); + current_ea = EA_AREG; + current_ea2 = EA_DREG; + start_all(GEN_RES | GEN_SRC); + + // read R1 + _ea_calc(current_ea, current_op->reg_sft); + _ea_read(current_ea, current_op->reg_sft); + // read R2 + _ea_calc(current_ea2, current_op->reg2_sft); + _ea_read_src(current_ea2, current_op->reg2_sft); + // write R1 + _ea_write(current_ea2, current_op->reg2_sft); + wf_op("\tres = src;\n"); + // write R2 + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenASRk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read (sign extend) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src_sx(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + wf_op("\tres = ((s32)src) >> sft;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenLSRk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_N = CPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + wf_op("\tres = src >> sft;\n"); + wf_op("\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenROXRk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & C / X flags calculation + if (current_size != SIZE_LONG) + { + wf_op("\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + wf_op("\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 2); + wf_op("\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + } + else + { + wf_op("\tCPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + wf_op("\tif (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1)));\n"); + wf_op("\telse res = (src >> sft) | (src << (33 - sft)) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + sft)));\n"); + wf_op("\tCPU->flag_X = CPU->flag_C;\n"); + } + + // V / N / Z flags calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); + else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenRORk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + wf_op("\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 1); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); + else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenASLk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift < size op) ... only for BYTE here + if (current_size == SIZE_BYTE) + { + wf_op("\tif (sft < %d)\n", current_sft_mask + 1); + wf_op("\t{\n"); + } + + // op & flag X, C, N, Z calculation + if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) + wf_op("\t\tCPU->flag_X = CPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + else wf_op("\t\tCPU->flag_X = CPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + wf_op("\t\tres = src << sft;\n"); + wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + // we do V flag calculation at end for a better register usage + wf_op("\t\tCPU->flag_V = 0;\n"); + if (current_size == SIZE_BYTE) + { + wf_op("\t\tif ((sft > %d) && (src)) CPU->flag_V = C68K_SR_V;\n", current_sft_mask); + wf_op("\t\telse\n"); + } + wf_op("\t\t{\n"); + wf_op("\t\t\tu32 msk = (((s32)0x80000000) >> (sft + %d)) & 0x%.8X;\n", 31 - current_sft_mask, current_bits_mask); + wf_op("\t\t\tsrc &= msk;\n"); + wf_op("\t\t\tif ((src) && (src != msk)) CPU->flag_V = C68K_SR_V;\n"); + wf_op("\t\t}\n"); + + if (current_size == SIZE_BYTE) + { + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of shift == size op (sft = 8 for byte operation) + wf_op("\tif (src) CPU->flag_V = C68K_SR_V;\n"); + wf_op("\telse CPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); + + // write + wf_op("\tres = 0;\n"); + _ea_write(current_ea, current_op->reg_sft); + + // others flags + wf_op("\tCPU->flag_N = 0;\n"); + wf_op("\tCPU->flag_notZ = 0;\n"); + } + + terminate_op(6); +} + +static void GenLSLk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) + wf_op("\tCPU->flag_X = CPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + else wf_op("\tCPU->flag_X = CPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + wf_op("\tres = src << sft;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenROXLk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & C / X flags calculation + if (current_size != SIZE_LONG) + { + wf_op("\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + wf_op("\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 2); + wf_op("\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + } + else + { + wf_op("\tCPU->flag_C = src >> ((32 - C68K_SR_C_SFT) - sft);\n"); + wf_op("\tif (sft == 1) res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - 1));\n"); + wf_op("\telse res = (src << sft) | (src >> (33 - sft)) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - sft));\n"); + wf_op("\tCPU->flag_X = CPU->flag_C;\n"); + } + + // V / N / Z flags calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); + else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenROLk() +{ + u32 base; + + current_ea = EA_DREG; // dst = Dx + + base = get_current_opcode_base(); + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); + // generate label & declarations + start_op(base, GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); + adds_CCnt("sft * 2"); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) + wf_op("\tCPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + else wf_op("\tCPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); + wf_op("\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 1); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); + else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(6); +} + +static void GenASRD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read (sign extend) + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src_sx(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + // if (shift < size op) + wf_op("\t\tif (sft < %d)\n", current_sft_mask + 1); + wf_op("\t\t{\n"); + + // op & flag calculation + wf_op("\t\t\tCPU->flag_V = 0;\n"); + if (current_size == SIZE_BYTE) wf_op("\t\t\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + else wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT;\n"); + wf_op("\t\t\tres = ((s32)src) >> sft;\n", szcs); + wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\t\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // special case of shift >= size op + + // if signed + wf_op("\t\tif (src & (1 << %d))\n", current_sft_mask); + wf_op("\t\t{\n"); + + // op & flag calculation + wf_op("\t\t\tCPU->flag_N = C68K_SR_N;\n"); + wf_op("\t\t\tCPU->flag_notZ = 1;\n"); + wf_op("\t\t\tCPU->flag_V = 0;\n"); + wf_op("\t\t\tCPU->flag_C = C68K_SR_C;\n"); + wf_op("\t\t\tCPU->flag_X = C68K_SR_X;\n"); + wf_op("\t\t\tres = 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // if not signed + wf_op("\t\tCPU->flag_N = 0;\n"); + wf_op("\t\tCPU->flag_notZ = 0;\n"); + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tCPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_X = 0;\n"); + wf_op("\t\tres = 0;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenLSRD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + // if (shift <= size op) + if (current_size == SIZE_LONG) wf_op("\t\tif (sft < 32)\n"); + else wf_op("\t\tif (sft <= %d)\n", current_sft_mask + 1); + wf_op("\t\t{\n"); + + // op & flag calculation + wf_op("\t\t\tCPU->flag_N = CPU->flag_V = 0;\n"); + if (current_size == SIZE_BYTE) wf_op("\t\t\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); + else wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT;\n"); + wf_op("\t\t\tres = src >> sft;\n", szcs); + wf_op("\t\t\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // special case of shift > size op + if (current_size == SIZE_LONG) + { + wf_op("\t\tif (sft == 32) CPU->flag_C = src >> (31 - C68K_SR_C_SFT);\n"); + wf_op("\t\telse CPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); + } + else wf_op("\t\tCPU->flag_X = CPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_N = 0;\n"); + wf_op("\t\tCPU->flag_notZ = 0;\n"); + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tres = 0;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenROXRD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + wf_op("\t\tsft %%= %d;\n", current_sft_mask + 2); + wf_op("\n"); + + // op & C / X flag calculation + if (current_size != SIZE_LONG) + { + wf_op("\t\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + wf_op("\t\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 2); + wf_op("\t\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + } + else + { + wf_op("\t\tif (sft != 0)\n"); + wf_op("\t\t{\n"); + wf_op("\t\t\tif (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1)));\n"); + wf_op("\t\t\telse res = (src >> sft) | (src << (33 - sft)) | (((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1))) >> (sft - 1));\n"); + wf_op("\t\t\tCPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT;\n"); + wf_op("\t\t}\n"); + wf_op("\t\telse res = src;\n"); + wf_op("\t\tCPU->flag_C = CPU->flag_X;\n"); + } + + // V / N / Z flag calculation + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); + else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = CPU->flag_X;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenRORD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + wf_op("\t\tsft &= 0x%.2X;\n", current_sft_mask); + wf_op("\t\t\n"); + + // op & flag calculation + if (current_size == SIZE_BYTE) + wf_op("\t\tCPU->flag_C = src << (C68K_SR_C_SFT - ((sft - 1) & 7));\n"); + else + wf_op("\t\tCPU->flag_C = (src >> ((sft - 1) & %d)) << C68K_SR_C_SFT;\n", current_sft_mask); + wf_op("\t\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 1); + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); + else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenASLD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + // if (shift < size op) + wf_op("\t\tif (sft < %d)\n", current_sft_mask + 1); + wf_op("\t\t{\n"); + + // op & flag calculation + if (current_size != SIZE_LONG) + { + wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); + wf_op("\t\t\tres = (src << sft) & 0x%.8X;\n", current_bits_mask); + } + else + { + wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); + wf_op("\t\t\tres = src << sft;\n"); + } + wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\t\tCPU->flag_notZ = res;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + // we do V flag calculation at end for a better register usage + wf_op("\t\t\tCPU->flag_V = 0;\n"); + wf_op("\t\t\t{\n"); + wf_op("\t\t\t\tu32 msk = (((s32)0x80000000) >> (sft + %d)) & 0x%.8X;\n", 31 - current_sft_mask, current_bits_mask); + wf_op("\t\t\t\tsrc &= msk;\n"); + wf_op("\t\t\t\tif ((src) && (src != msk)) CPU->flag_V = C68K_SR_V;\n"); + wf_op("\t\t\t}\n"); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // special case of shift >= size op + wf_op("\t\tif (sft == %d) CPU->flag_C = src << C68K_SR_C_SFT;\n", current_bits_mask + 1); + wf_op("\t\telse CPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); + wf_op("\t\tif (src) CPU->flag_V = C68K_SR_V;\n"); + wf_op("\t\telse CPU->flag_V = 0;\n"); + + wf_op("\t\tres = 0;\n"); + // write + _ea_write(current_ea, current_op->reg_sft); + + // others flags + wf_op("\t\tCPU->flag_N = 0;\n"); + wf_op("\t\tCPU->flag_notZ = 0;\n"); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenLSLD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + // if (shift <= size op) + if (current_size == SIZE_LONG) wf_op("\t\tif (sft < 32)\n"); + else wf_op("\t\tif (sft <= %d)\n", current_sft_mask + 1); + wf_op("\t\t{\n"); + + // op & flag calculation + if (current_size != SIZE_LONG) + { + wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); + wf_op("\t\t\tres = (src << sft) & 0x%.8X;\n", current_bits_mask); + } + else + { + wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); + wf_op("\t\t\tres = src << sft;\n"); + } + wf_op("\t\t\tCPU->flag_V = 0;\n"); + wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\t\tCPU->flag_notZ = res;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // special case of shift > size op + if (current_size == SIZE_LONG) + { + wf_op("\t\tif (sft == 32) CPU->flag_C = src << C68K_SR_C_SFT;\n"); + wf_op("\t\telse CPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); + } + else wf_op("\t\tCPU->flag_X = CPU->flag_C = 0;\n"); + wf_op("\t\tCPU->flag_N = 0;\n"); + wf_op("\t\tCPU->flag_notZ = 0;\n"); + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tres = 0;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenROXLD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + wf_op("\t\tsft %%= %d;\n", current_sft_mask + 2); + wf_op("\n"); + + // op & C/X flags calculation + if (current_size != SIZE_LONG) + { + wf_op("\t\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + wf_op("\t\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 2); + wf_op("\t\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); + } + else + { + wf_op("\t\tif (sft != 0)\n"); + wf_op("\t\t{\n"); + wf_op("\t\t\tif (sft == 1) res = (src << 1) | ((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1);\n"); + wf_op("\t\t\telse res = (src << sft) | (src >> (33 - sft)) | (((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1) << (sft - 1));\n"); + wf_op("\t\t\tCPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT;\n"); + wf_op("\t\t}\n"); + wf_op("\t\telse res = src;\n"); + wf_op("\t\tCPU->flag_C = CPU->flag_X;\n"); + } + + // V / N / Z flags calculation + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); + else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = CPU->flag_X;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenROLD() +{ +// u32 base = get_current_opcode_base(); + + current_ea = EA_DREG; // dst = Dx + + start_all(GEN_RES | GEN_SRC); + + if (current_size == SIZE_LONG) current_cycle += 2; + + wf_op("\tu32 sft;\n"); + wf_op("\n"); + wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // if (shift != 0) + wf_op("\tif (sft)\n"); + wf_op("\t{\n"); + + adds_CCnt("sft * 2"); + + // if ((shift & size op) != 0) + wf_op("\t\tif (sft &= 0x%.2X)\n", current_sft_mask); + wf_op("\t\t{\n"); + + // op & flag calculation + if (current_size != SIZE_LONG) + { + wf_op("\t\t\tCPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); + wf_op("\t\t\tres = ((src << sft) | (src >> (%d - sft))) & 0x%.8X;\n", current_sft_mask + 1, current_bits_mask); + } + else + { + wf_op("\t\t\tCPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); + wf_op("\t\t\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 1); + } + wf_op("\t\t\tCPU->flag_V = 0;\n"); + wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\t\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + quick_terminate_op(6); + wf_op("\t\t}\n"); + wf_op("\n"); + + // special case of ((shift & size op) == 0) + wf_op("\t\tCPU->flag_V = 0;\n"); + wf_op("\t\tCPU->flag_C = src << C68K_SR_C_SFT;\n"); + wf_op("\t\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\t\tCPU->flag_notZ = src;\n"); + + quick_terminate_op(6); + wf_op("\t}\n"); + wf_op("\n"); + + // special case of (shift == 0) + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = src;\n"); + + terminate_op(6); +} + +static void GenASR() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); + wf_op("\tres = (src >> 1) | (src & (1 << %d));\n", current_sft_mask); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenLSR() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_N = CPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); + wf_op("\tres = src >> 1;\n"); + wf_op("\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenROXR() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tres = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << %d);\n", current_sft_mask - C68K_SR_X_SFT); + wf_op("\tCPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res;\n"); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenROR() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = src << C68K_SR_C_SFT;\n"); + wf_op("\tres = (src >> 1) | (src << %d);\n", current_sft_mask); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenASL() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); + wf_op("\tres = src << 1;\n"); + wf_op("\tCPU->flag_V = (src ^ res) >> %d;\n", current_sft_mask - C68K_SR_V_SFT); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenLSL() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); + wf_op("\tres = src << 1;\n"); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenROXL() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tres = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> %d);\n", C68K_SR_X_SFT); + wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void GenROL() +{ + set_current_size(SIZE_WORD); // dst = mem (word operation) + start_all(GEN_ADR | GEN_RES | GEN_SRC); + + // read + _ea_calc(current_ea, current_op->reg_sft); + _ea_read_src(current_ea, current_op->reg_sft); + + // op & flag calculation + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); + wf_op("\tres = (src << 1) | (src >> %d);\n", current_sft_mask); + wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); + wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); + + // write + _ea_write(current_ea, current_op->reg_sft); + + terminate_op(8); +} + +static void Gen1010() +{ + u32 base; + base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0FFF, 0x1, base); + + // generate label & declarations + start_op(base, GEN_RES); + wf_op("\tPC -= 2;\n"); + gen_exception("\t", "C68K_1010_EX"); + terminate_op(4); +} + +static void Gen1111() +{ + u32 base; + base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable_ext(base, 0x0000, 0x0FFF, 0x1, base); + + // generate label & declarations + start_op(base, GEN_RES); + wf_op("\tPC -= 2;\n"); + gen_exception("\t", "C68K_1111_EX"); + terminate_op(4); +} + +#ifdef NEOCD_HLE +static void Gen0xFABE() +{ + start_all(GEN_ALL); + + wf_op("\tneogeo_exit();\n"); + + terminate_op(0); +} + +static void Gen0xFABF() +{ + start_all(GEN_ALL); + + wf_op("\timg_display = 1;\n"); + wf_op("\tcdrom_load_files();\n"); + + terminate_op(0); +} + +static void Gen0xFAC0() +{ + start_all(GEN_ALL); + + wf_op("\timg_display = 0;\n"); + wf_op("\tcdrom_load_files();\n"); + + terminate_op(0); +} + +static void Gen0xFAC1() +{ + start_all(GEN_ALL); + + wf_op("\tneogeo_upload();\n"); + + terminate_op(0); +} + +static void Gen0xFAC2() +{ + start_all(GEN_ALL); + + wf_op("\tneogeo_prio_switch();\n"); + + terminate_op(0); +} + +static void Gen0xFAC3() +{ + start_all(GEN_ALL); + + wf_op("\tneogeo_cdda_control();\n"); + + terminate_op(0); +} +#endif + + +// main function +///////////////// +int main(void) +{ + u32 i; + u32 s; + u32 smax; + + // clear opcode files + for(i = 0; i < 0x10; i++) + { + char fn[16]; + + sprintf(fn, "c68k_op%.1X.inc", (int)i); + opcode_file = fopen(fn, "wt"); + if (opcode_file != NULL) + { + fclose(opcode_file); + opcode_file = NULL; + } + } + + // init opcode jump table + ini_file = fopen("c68k_ini.inc", "wt"); +#ifndef C68K_NO_JUMP_TABLE +#ifdef C68K_CONST_JUMP_TABLE + for(i = 0; i < 0x10000; i++) op_jump_table[i] = OP_ILLEGAL; +#else + // defaut ILLEGAL instruction + gen_jumptable(0x0000, 0x0000, 0xFFFF, 1, 0, 0, 0, 0, 0, 0, 0x4AFC); +#endif +#endif + // generate opcode files + for(i = 0; i < OP_INFO_TABLE_LEN; i++) + { + current_op = &(op_info_table[i]); + if (prepare_generate()) return 1; + + // s = size to start + current_size = 0; + smax = SIZE_LONG; + if (current_op->size_type == 0) smax = 0; + else if (current_op->size_type == 1) current_size = 1; + + for(s = current_size; s <= smax; s++) + { + if (current_op->eam_sft != -1) + { + for(current_ea = 0; current_ea <= EA_ADEC7; current_ea++) + { + if (!has_ea(current_ea)) continue; + current_eam = _ea_to_eamreg(current_ea) >> 3; + current_reg = _ea_to_eamreg(current_ea) & 7; + + if (op_info_table[i].eam2_sft != -1) + { + for(current_ea2 = 0; current_ea2 <= EA_ADEC7; current_ea2++) + { + if (!has_ea2(current_ea2)) continue; + current_eam2 = _ea_to_eamreg(current_ea2) >> 3; + current_reg2 = _ea_to_eamreg(current_ea2) & 7; + + set_current_size(s); + current_op->genfunc(); + } + } + else + { + current_reg2 = 0; + set_current_size(s); + current_op->genfunc(); + } + } + } + else + { + current_reg = 0; + set_current_size(s); + current_op->genfunc(); + } + } + } + + // generate jumptable file +#ifdef C68K_CONST_JUMP_TABLE + if (ini_file != NULL) + { + fprintf(ini_file, "\tstatic const void *JumpTable[0x10000] =\n"); + fprintf(ini_file, "\t{\n"); + + for(i = 0; i < (0x10000 - 4); i += 4) + fprintf(ini_file, "\t\t&&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X,\n", op_jump_table[i + 0], op_jump_table[i + 1], op_jump_table[i + 2], op_jump_table[i + 3]); + fprintf(ini_file, "\t\t&&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X\n", op_jump_table[0xFFFC], op_jump_table[0xFFFD], op_jump_table[0xFFFE], op_jump_table[0xFFFF]); + + fprintf(ini_file, "\t};\n\n"); + } +#endif + + // close handle + if (ini_file != NULL) fclose(ini_file); + if (opcode_file != NULL) fclose(opcode_file); + + return 0; +} +#endif diff --git a/yabause/src/c68k/gen68k.h b/yabause/src/c68k/gen68k.h new file mode 100644 index 0000000000..e8b71d83db --- /dev/null +++ b/yabause/src/c68k/gen68k.h @@ -0,0 +1,68 @@ +/* Copyright 2003-2004 Stephane Dallongeville + Copyright 2004 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/********************************************************************************* + * GEN68K.H : + * + * C68K generator include file + * + ********************************************************************************/ + +#ifndef _GEN68K_H_ +#define _GEN68K_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// setting +/////////// + +// structure definition +//////////////////////// + +typedef struct { + u32 name; + u32 mask; + u32 match; +} c68k_ea_info_struc; + +typedef struct __c68k_op_info_struc { + s8 op_name[8 + 1]; + u16 op_base; + u16 op_mask; + s8 size_type; + s8 size_sft; + s8 eam_sft; + s8 reg_sft; + s8 eam2_sft; + s8 reg2_sft; + s8 ea_supported[12 + 1]; + s8 ea2_supported[12 + 1]; + void (*genfunc)(void); +} c68k_op_info_struc; + + +#ifdef __cplusplus +} +#endif + +#endif // _GEN68K_H_ + diff --git a/yabause/src/c68k/gen68k.inc b/yabause/src/c68k/gen68k.inc new file mode 100644 index 0000000000..483a4f7b07 --- /dev/null +++ b/yabause/src/c68k/gen68k.inc @@ -0,0 +1,1649 @@ +/* Copyright 2003-2004 Stephane Dallongeville + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#define EA_DREG 0 +#define EA_AREG 1 +#define EA_AIND 2 +#define EA_AINC 3 +#define EA_ADEC 4 +#define EA_D16A 5 +#define EA_D8AX 6 +#define EA_A16 7 +#define EA_A32 8 +#define EA_D16P 9 +#define EA_D8PX 10 +#define EA_IMM 11 +#define EA_AINC7 12 +#define EA_ADEC7 13 +#define EA_ILLEGAL 15 + +#define SIZE_BYTE 0 +#define SIZE_WORD 1 +#define SIZE_LONG 2 + +#define COND_TR 0 +#define COND_FA 1 +#define COND_HI 2 +#define COND_LS 3 +#define COND_CC 4 +#define COND_CS 5 +#define COND_NE 6 +#define COND_EQ 7 +#define COND_VC 8 +#define COND_VS 9 +#define COND_PL 10 +#define COND_MI 11 +#define COND_GE 12 +#define COND_LT 13 +#define COND_GT 14 +#define COND_LE 15 + +#define COND_NOT_TR COND_FA +#define COND_NOT_FA COND_TR +#define COND_NOT_HI COND_LS +#define COND_NOT_LS COND_HI +#define COND_NOT_CC COND_CS +#define COND_NOT_CS COND_CC +#define COND_NOT_NE COND_EQ +#define COND_NOT_EQ COND_NE +#define COND_NOT_VC COND_VS +#define COND_NOT_VS COND_VC +#define COND_NOT_PL COND_MI +#define COND_NOT_MI COND_PL +#define COND_NOT_GE COND_LT +#define COND_NOT_LT COND_GE +#define COND_NOT_GT COND_LE +#define COND_NOT_LE COND_GT + +#define OP_ILLEGAL 0x4AFC + +static void GenORI(void); +static void GenORICCR(void); +static void GenORISR(void); +static void GenANDI(void); +static void GenANDICCR(void); +static void GenANDISR(void); +static void GenEORI(void); +static void GenEORICCR(void); +static void GenEORISR(void); +static void GenSUBI(void); +static void GenADDI(void); +static void GenCMPI(void); +static void GenBTSTn(void); +static void GenBCHGn(void); +static void GenBCLRn(void); +static void GenBSETn(void); +static void GenBTST(void); +static void GenBCHG(void); +static void GenBCLR(void); +static void GenBSET(void); +static void GenMOVEPWaD(void); +static void GenMOVEPLaD(void); +static void GenMOVEPWDa(void); +static void GenMOVEPLDa(void); +static void GenMOVEB(void); +static void GenMOVEL(void); +static void GenMOVEW(void); +static void GenMOVEAL(void); +static void GenMOVEAW(void); +static void GenNEGX(void); +static void GenCLR(void); +static void GenNEG(void); +static void GenNOT(void); +static void GenMOVESRa(void); +static void GenMOVEaSR(void); +static void GenMOVEaCCR(void); +static void GenNBCD(void); +static void GenPEA(void); +static void GenSWAP(void); +static void GenMOVEMaR(void); +static void GenEXT(void); +static void GenTST(void); +static void GenTAS(void); +static void GenILLEGAL(void); +static void GenMOVEMRa(void); +static void GenTRAP(void); +static void GenLINK(void); +static void GenLINKA7(void); +static void GenULNK(void); +static void GenULNKA7(void); +static void GenMOVEAUSP(void); +static void GenMOVEUSPA(void); +static void GenRESET(void); +static void GenNOP(void); +static void GenSTOP(void); +static void GenRTE(void); +static void GenRTS(void); +static void GenTRAPV(void); +static void GenRTR(void); +static void GenJSR(void); +static void GenJMP(void); +static void GenCHK(void); +static void GenLEA(void); +static void GenSTCC(void); +static void GenDBCC(void); +static void GenADDQ(void); +static void GenSUBQ(void); +static void GenBCC(void); +static void GenBCC16(void); +static void GenBRA(void); +static void GenBRA16(void); +static void GenBSR(void); +static void GenBSR16(void); +static void GenMOVEQ(void); +static void GenORaD(void); +static void GenORDa(void); +static void GenSBCD(void); +static void GenSBCDM(void); +static void GenSBCD7M(void); +static void GenSBCDM7(void); +static void GenSBCD7M7(void); +static void GenDIVU(void); +static void GenDIVS(void); +static void GenSUBaD(void); +static void GenSUBDa(void); +static void GenSUBX(void); +static void GenSUBXM(void); +static void GenSUBX7M(void); +static void GenSUBXM7(void); +static void GenSUBX7M7(void); +static void GenSUBA(void); +static void GenCMP(void); +static void GenCMPM(void); +static void GenCMP7M(void); +static void GenCMPM7(void); +static void GenCMP7M7(void); +static void GenEORDa(void); +static void GenCMPA(void); +static void GenANDaD(void); +static void GenANDDa(void); +static void GenABCD(void); +static void GenABCDM(void); +static void GenABCD7M(void); +static void GenABCDM7(void); +static void GenABCD7M7(void); +static void GenMULU(void); +static void GenMULS(void); +static void GenEXGDD(void); +static void GenEXGAA(void); +static void GenEXGAD(void); +static void GenADDaD(void); +static void GenADDDa(void); +static void GenADDX(void); +static void GenADDXM(void); +static void GenADDX7M(void); +static void GenADDXM7(void); +static void GenADDX7M7(void); +static void GenADDA(void); +static void GenASRk(void); +static void GenLSRk(void); +static void GenROXRk(void); +static void GenRORk(void); +static void GenASLk(void); +static void GenLSLk(void); +static void GenROXLk(void); +static void GenROLk(void); +static void GenASRD(void); +static void GenLSRD(void); +static void GenROXRD(void); +static void GenRORD(void); +static void GenASLD(void); +static void GenLSLD(void); +static void GenROXLD(void); +static void GenROLD(void); +static void GenASR(void); +static void GenLSR(void); +static void GenROXR(void); +static void GenROR(void); +static void GenASL(void); +static void GenLSL(void); +static void GenROXL(void); +static void GenROL(void); +static void Gen1010(void); +static void Gen1111(void); +#ifdef NEOCD_HLE +static void Gen0xFABE(void); +static void Gen0xFABF(void); +static void Gen0xFAC0(void); +static void Gen0xFAC1(void); +static void Gen0xFAC2(void); +static void Gen0xFAC3(void); +#endif + +#ifdef NEOCD_HLE +#define OP_INFO_TABLE_LEN (142 + 6) +#else +#define OP_INFO_TABLE_LEN 144 +#endif + +static c68k_op_info_struc op_info_table[OP_INFO_TABLE_LEN] = +{ // DAAAAddaaddi DAAAAddaaddi + // iid181318m iid181318m + // siz siz eam ear eam ear nne6A626Pm nne6A626Pm + // opname opbase opmask typ sft 1 1 2 2 dccAX PX dccAX PX GenFunc + { "1010", 0xA000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen1010 }, + { "1111", 0xF000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen1111 }, + { "ORI", 0x0000, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenORI }, + { "ORICCR", 0x003C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenORICCR }, + { "ORISR", 0x007C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenORISR }, + { "ANDI", 0x0200, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenANDI }, + { "ANDICCR", 0x023C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenANDICCR }, + { "ANDISR", 0x027C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenANDISR }, + { "EORI", 0x0A00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenEORI }, + { "EORICCR", 0x0A3C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenEORICCR }, + { "EORISR", 0x0A7C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenEORISR }, + + { "SUBI", 0x0400, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenSUBI }, + { "ADDI", 0x0600, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenADDI }, + { "CMPI", 0x0C00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenCMPI }, + + { "BTSTn", 0x0800, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooooo-", "------------", GenBTSTn }, + { "BCHGn", 0x0840, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBCHGn }, + { "BCLRn", 0x0880, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBCLRn }, + { "BSETn", 0x08C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBSETn }, + + { "BTST", 0x0100, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenBTST }, + { "BCHG", 0x0140, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBCHG }, + { "BCLR", 0x0180, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBCLR }, + { "BSET", 0x01C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBSET }, + + { "MOVEPWaD", 0x0108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPWaD }, + { "MOVEPLaD", 0x0148, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPLaD }, + { "MOVEPWDa", 0x0188, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPWDa }, + { "MOVEPLDa", 0x01C8, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPLDa }, + + { "MOVEB", 0x1000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEB }, + { "MOVEL", 0x2000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEL }, + { "MOVEW", 0x3000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEW }, + { "MOVEAL", 0x2040, 0xF1C0, 0, 0, 3, 0, -1, 9, "oooooooooooo", "------------", GenMOVEAL }, + { "MOVEAW", 0x3040, 0xF1C0, 0, 0, 3, 0, -1, 9, "oooooooooooo", "------------", GenMOVEAW }, + + { "NEGX", 0x4000, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNEGX }, + { "CLR", 0x4200, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenCLR }, + { "NEG", 0x4400, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNEG }, + { "NOT", 0x4600, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNOT }, + + { "MOVESRa", 0x40C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenMOVESRa }, + { "MOVEaCCR", 0x44C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-oooooooooo", "------------", GenMOVEaCCR }, + { "MOVEaSR", 0x46C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-oooooooooo", "------------", GenMOVEaSR }, + + { "NBCD", 0x4800, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNBCD }, + { "PEA", 0x4840, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenPEA }, + { "SWAP", 0x4840, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenSWAP }, + + { "MOVEMRa", 0x4880, 0xFF80, 1, 6, 3, 0, -1, -1, "--o-ooooo---", "------------", GenMOVEMRa }, + { "EXT", 0x4880, 0xFFB8, 1, 6, -1, 0, -1, -1, "------------", "------------", GenEXT }, + { "TST", 0x4A00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenTST }, + { "TAS", 0x4AC0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenTAS }, + { "ILLEGAL", 0x4AFC, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenILLEGAL }, + { "MOVEMaR", 0x4C80, 0xFF80, 1, 6, 3, 0, -1, -1, "--oo-oooooo-", "------------", GenMOVEMaR }, + + { "TRAP", 0x4E40, 0xFFF0, 0, 0, -1, -1, -1, -1, "------------", "------------", GenTRAP }, + { "LINK", 0x4E50, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenLINK }, + { "LINKA7", 0x4E57, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenLINKA7 }, + { "ULNK", 0x4E58, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenULNK }, + { "ULNKA7", 0x4E5F, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenULNKA7 }, + { "MOVEAUSP", 0x4E60, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenMOVEAUSP }, + { "MOVEUSPA", 0x4E68, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenMOVEUSPA }, + + { "RESET", 0x4E70, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRESET }, + { "NOP", 0x4E71, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenNOP }, + { "STOP", 0x4E72, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenSTOP }, + { "RTE", 0x4E73, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTE }, + { "RTS", 0x4E75, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTS }, + { "TRAPV", 0x4E76, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenTRAPV }, + { "RTR", 0x4E77, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTR }, + + { "JSR", 0x4E80, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenJSR }, + { "JMP", 0x4EC0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenJMP }, + + { "CHK", 0x4180, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenCHK }, + { "LEA", 0x41C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "--o--oooooo-", "------------", GenLEA }, + + { "STCC", 0x50C0, 0xF0C0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenSTCC }, + { "DBCC", 0x50C8, 0xF0F8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenDBCC }, + + { "ADDQ", 0x5000, 0xF100, 2, 6, 3, 0, -1, -1, "ooooooooo---", "------------", GenADDQ }, + { "SUBQ", 0x5100, 0xF100, 2, 6, 3, 0, -1, -1, "ooooooooo---", "------------", GenSUBQ }, + + { "BCC", 0x6000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBCC }, + { "BCC16", 0x6000, 0xF0FF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBCC16 }, + { "BRA", 0x6000, 0xFF00, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBRA }, + { "BRA16", 0x6000, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBRA16 }, + { "BSR", 0x6100, 0xFF00, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBSR }, + { "BSR16", 0x6100, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBSR16 }, + + { "MOVEQ", 0x7000, 0xF100, 0, 0, -1, 9, -1, -1, "------------", "------------", GenMOVEQ }, + + { "ORaD", 0x8000, 0xF100, 2, 6, 3, 0, -1, 9, "o-oooooooooo", "------------", GenORaD }, + { "ORDa", 0x8100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenORDa }, + { "SBCD", 0x8100, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD }, + { "SBCDM", 0x8108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCDM }, + { "SBCD7M", 0x810F, 0xF1FF, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD7M }, + { "SBCDM7", 0x8F08, 0xFFF8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCDM7 }, + { "SBCD7M7", 0x8F0F, 0xFFFF, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD7M7 }, + { "DIVU", 0x80C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenDIVU }, + { "DIVS", 0x81C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenDIVS }, + + { "SUBaD", 0x9000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenSUBaD }, + { "SUBDa", 0x9100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenSUBDa }, + { "SUBX", 0x9100, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenSUBX }, + { "SUBXM", 0x9108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenSUBXM }, + { "SUBX7M", 0x910F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenSUBX7M }, + { "SUBXM7", 0x9F08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenSUBXM7 }, + { "SUBX7M7", 0x9F0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenSUBX7M7 }, + { "SUBA", 0x90C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenSUBA }, + + { "CMP", 0xB000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenCMP }, + { "CMPM", 0xB108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenCMPM }, + { "CMP7M", 0xB10F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenCMP7M }, + { "CMPM7", 0xBF08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenCMPM7 }, + { "CMP7M7", 0xBF0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenCMP7M7 }, + { "EORDa", 0xB100, 0xF100, 2, 6, 3, 0, -1, 9, "o-ooooooo---", "------------", GenEORDa }, + { "CMPA", 0xB0C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenCMPA }, + + { "ANDaD", 0xC000, 0xF100, 2, 6, 3, 0, -1, 9, "o-oooooooooo", "------------", GenANDaD }, + { "ANDDa", 0xC100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenANDDa }, + { "ABCD", 0xC100, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenABCD }, + { "ABCDM", 0xC108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenABCDM }, + { "ABCD7M", 0xC10F, 0xF1FF, 0, 0, -1, -1, -1, 9, "------------", "------------", GenABCD7M }, + { "ABCDM7", 0xCF08, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenABCDM7 }, + { "ABCD7M7", 0xCF0F, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenABCD7M7 }, + { "MULU", 0xC0C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenMULU }, + { "MULS", 0xC1C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenMULS }, + { "EXGDD", 0xC140, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGDD }, + { "EXGAA", 0xC148, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGAA }, + { "EXGAD", 0xC188, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGAD }, + + { "ADDaD", 0xD000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenADDaD }, + { "ADDDa", 0xD100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenADDDa }, + { "ADDX", 0xD100, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenADDX }, + { "ADDXM", 0xD108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenADDXM }, + { "ADDX7M", 0xD10F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenADDX7M }, + { "ADDXM7", 0xDF08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenADDXM7 }, + { "ADDX7M7", 0xDF0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenADDX7M7 }, + { "ADDA", 0xD0C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenADDA }, + + { "ASRk", 0xE000, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenASRk }, + { "LSRk", 0xE008, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenLSRk }, + { "ROXRk", 0xE010, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROXRk }, + { "RORk", 0xE018, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenRORk }, + { "ASLk", 0xE100, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenASLk }, + { "LSLk", 0xE108, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenLSLk }, + { "ROXLk", 0xE110, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROXLk }, + { "ROLk", 0xE118, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROLk }, + + { "ASRD", 0xE020, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenASRD }, + { "LSRD", 0xE028, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenLSRD }, + { "ROXRD", 0xE030, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROXRD }, + { "RORD", 0xE038, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenRORD }, + { "ASLD", 0xE120, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenASLD }, + { "LSLD", 0xE128, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenLSLD }, + { "ROXLD", 0xE130, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROXLD }, + { "ROLD", 0xE138, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROLD }, + + { "ASR", 0xE0C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenASR }, + { "LSR", 0xE2C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenLSR }, + { "ROXR", 0xE4C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROXR }, + { "ROR", 0xE6C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROR }, + { "ASL", 0xE1C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenASL }, + { "LSL", 0xE3C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenLSL }, + { "ROXL", 0xE5C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROXL }, + { "ROL", 0xE7C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROL } + +#ifdef NEOCD_HLE + , + { "0xFABE", 0xFABE, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFABE }, + { "0xFABF", 0xFABF, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFABF }, + { "0xFAC0", 0xFAC0, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC0 }, + { "0xFAC1", 0xFAC1, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC1 }, + { "0xFAC2", 0xFAC2, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC2 }, + { "0xFAC3", 0xFAC3, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC3 } +#endif +}; + +#ifndef C68K_NO_JUMP_TABLE +#ifdef C68K_CONST_JUMP_TABLE +static u16 op_jump_table[0x10000]; +#endif +#endif + +// files where code is generated +static FILE* ini_file = NULL; +static FILE* opcode_file = NULL; + +// current generated instruction infos +static c68k_op_info_struc *current_op; +static u32 current_ea; +static u32 current_eam; +static u32 current_reg; +static u32 current_ea2; +static u32 current_eam2; +static u32 current_reg2; +static u32 current_size; +static u32 current_cycle; +static u32 current_io_sav; + +static char current_cond_char[128]; + +static char szc[20]; +static char szcs[20]; +static char szcf[20]; +static char szcsf[20]; + +static u32 current_bits_mask; +static u8 current_sft_mask; + +#define EA_DREG 0 +#define EA_AREG 1 +#define EA_AIND 2 +#define EA_AINC 3 +#define EA_ADEC 4 +#define EA_D16A 5 +#define EA_D8AX 6 +#define EA_A16 7 +#define EA_A32 8 +#define EA_D16P 9 +#define EA_D8PX 10 +#define EA_IMM 11 +#define EA_AINC7 12 +#define EA_ADEC7 13 +#define EA_ILLEGAL 15 + +static const u32 jmp_jsr_cycle_table[16] = +{ + 0, 0, + 4, + 0, + 0, + 6, + 10, + 6, + 8, + 6, + 10, + 0, 0, 0, 0 +}; + +static const u32 lea_pea_cycle_table[16] = +{ + 0, 0, + 0, + 0, + 0, + 4, + 8, + 4, + 8, + 4, + 8, + 0, 0, 0, 0 +}; + +static const u32 movem_cycle_table[16] = +{ + 0, 0, + 0, + 0, + 0, + 4, + 6, + 4, + 8, + 4, + 6, + 0, 0, 0, 0 +}; + +// general emitter function +//////////////////////////// + +static u32 prepare_generate(void) +{ + char filename[32]; + + sprintf(filename, "c68k_op%.1X.inc", (current_op->op_base >> 12) & 0xF); + if (opcode_file != NULL) + { + fclose(opcode_file); + opcode_file = NULL; + } + opcode_file = fopen(filename, "at"); + if (opcode_file == NULL) + { + printf("Can't open %s\n", filename); + return 1; + } + return 0; +} + +static void wf_op(char* fmt, ...) +{ + va_list args; + + if (opcode_file == NULL) return; + + va_start(args, fmt); + vfprintf(opcode_file, fmt, args); + va_end(args); +} + +static void gen_jumptable(u32 base, u32 start1, u32 end1, u32 step1, u32 start2, u32 end2, u32 step2, u32 start3, u32 end3, u32 step3, u32 op) +{ +#ifdef C68K_CONST_JUMP_TABLE + u32 i, j, k; +#endif +#ifdef C68K_NO_JUMP_TABLE + u32 i, j, k; +#endif + + + base &= 0xFFFF; + start1 &= 0xFFFF; + end1 &= 0xFFFF; + step1 &= 0xFFFF; + if (end1 < start1) end1 = start1; + start2 &= 0xFFFF; + end2 &= 0xFFFF; + step2 &= 0xFFFF; + if (end2 < start2) end2 = start2; + op &= 0xFFFF; + +#ifndef C68K_NO_JUMP_TABLE +#ifdef C68K_CONST_JUMP_TABLE + if (step1 == 0) step1 = 1; + if (step2 == 0) step2 = 1; + if (step3 == 0) step3 = 1; + + for(i = start1; i <= end1; i += step1) + for(j = start2; j <= end2; j += step2) + for(k = start3; k <= end3; k += step3) + op_jump_table[base + i + j + k] = op; + +#else + if (ini_file == NULL) return; + + if (start1 != end1) + { + fprintf(ini_file, "\t\tfor(i = 0x%.4X; i <= 0x%.4X; i += 0x%.4X)\n", (int)start1, (int)end1, (int)step1); + if (start2 != end2) + { + fprintf(ini_file, "\t\t\tfor(j = 0x%.4X; j <= 0x%.4X; j += 0x%.4X)\n\t", (int)start2, (int)end2, (int)step2); + if (start3 != end3) + { + fprintf(ini_file, "\t\t\t\tfor(k = 0x%.4X; k <= 0x%.4X; k += 0x%.4X)\n\t", (int)start3, (int)end3, (int)step3); + fprintf(ini_file, "\t\t\t\t\tJumpTable[0x%.4X + i + j + k] = &&OP_0x%.4X;\n", (int)base, (int)op); + } + else fprintf(ini_file, "\t\t\t\tJumpTable[0x%.4X + i + j] = &&OP_0x%.4X;\n", (int)base, (int)op); + } + else fprintf(ini_file, "\t\t\tJumpTable[0x%.4X + i] = &&OP_0x%.4X;\n", (int)base, (int)op); + } + else fprintf(ini_file, "\t\tJumpTable[0x%.4X] = &&OP_0x%.4X;\n", (int)base, (int)op); +#endif +#else + if (step1 == 0) step1 = 1; + if (step2 == 0) step2 = 1; + if (step3 == 0) step3 = 1; + + for(i = start1; i <= end1; i += step1) + for(j = start2; j <= end2; j += step2) + for(k = start3; k <= end3; k += step3) + { + u32 temp=(base + i + j + k); + if (temp != op && temp != 0x4E57 && temp != 0x4E5F) + wf_op("case 0x%.4X:\n", base + i + j + k); + } +#endif +} + +static void gen_opjumptable_ext(u32 base, u32 start3, u32 end3, u32 step3, u32 op) +{ + u32 start1, end1, step1, start2, end2, step2; + + start1 = end1 = step1 = 0; + start2 = end2 = step2 = 0; + + if ((current_op->reg_sft != -1) && (current_ea < 7)) + { + if ((current_ea == EA_AINC) || (current_ea == EA_ADEC)) end1 = 6 << current_op->reg_sft; + else end1 = 7 << current_op->reg_sft; + step1 = 1 << current_op->reg_sft; + } + if ((current_op->reg2_sft != -1) && (current_ea2 < 7)) + { + if ((current_ea2 == EA_AINC) || (current_ea2 == EA_ADEC)) end2 = 6 << current_op->reg2_sft; + else end2 = 7 << current_op->reg2_sft; + step2 = 1 << current_op->reg2_sft; + } + + if (start1 != end1) + { + if (start2 != end2) gen_jumptable(base, start1, end1, step1, start2, end2, step2, start3, end3, step3, op); + else gen_jumptable(base, start1, end1, step1, start3, end3, step3, start2, end2, step2, op); + } + else if (start2 != end2) gen_jumptable(base, start2, end2, step2, start3, end3, step3, start1, end1, step1, op); + else gen_jumptable(base, start3, end3, step3, start2, end2, step2, start1, end1, step1, op); +} + +static void gen_opjumptable(u32 op) +{ + gen_opjumptable_ext(op, 0, 0, 0, op); +} + +#define GEN_ADR 1 +#define GEN_RES 2 +#define GEN_SRC 4 +#define GEN_DST 8 +#define GEN_ALL 15 + +static void start_op(u32 op, int v) +{ + current_io_sav = 0; + current_cycle = 0; + + wf_op("\n// %s\n", current_op->op_name); +#ifndef C68K_NO_JUMP_TABLE + wf_op("OP_0x%.4X:\n", op & 0xFFFF); +#else + wf_op("case 0x%.4X:\n", op & 0xFFFF); +#endif + wf_op("{\n"); + if (v & GEN_ADR) wf_op("\tu32 adr;\n"); + if (v & GEN_RES) wf_op("\tu32 res;\n"); + if (v & GEN_DST) wf_op("\tpointer dst;\n"); + if (v & GEN_SRC) wf_op("\tpointer src;\n"); +} + +static void add_CCnt(u32 cycle) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tCCnt -= %d;\n", cycle); +} + +static void adds_CCnt(char *str) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tCCnt -= %s;\n", str); +} + +#if 0 // FIXME: warning removal +static void sub_CCnt(u32 cycle) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tCCnt += %d;\n", cycle); +} + +static void subs_CCnt(char *str) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tCCnt += %s;\n", str); +} + +static void quick_fterminate_op(u32 cycle) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tCCnt -= %d;\n", current_cycle + cycle); + wf_op("\tgoto C68k_Exec_End;\n"); +} +#endif + +static void fterminate_op(u32 cycle) +{ + wf_op("}\n"); + if (current_io_sav) wf_op("POST_IO\n"); + current_io_sav = 0; + wf_op("CCnt -= %d;\n", current_cycle + cycle); + wf_op("goto C68k_Exec_End;\n"); +} + +static void quick_terminate_op(u32 cycle) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + current_io_sav = 0; + wf_op("\tRET(%d)\n", current_cycle + cycle); +} + +static void terminate_op(u32 cycle) +{ + if (current_io_sav) wf_op("\tPOST_IO\n"); + wf_op("}\n"); + current_io_sav = 0; + wf_op("RET(%d)\n", current_cycle + cycle); +} + +static void do_pre_io(void) +{ + if (!current_io_sav) fprintf(opcode_file, "\tPRE_IO\n"); + current_io_sav = 1; +} + +static void mem_op(char* fmt, ...) +{ + va_list args; + + if (opcode_file == NULL) return; + + do_pre_io(); + + va_start(args, fmt); + vfprintf(opcode_file, fmt, args); + va_end(args); +} + +// flag emitter function +///////////////////////// + +static void set_logic_flag(void) +{ + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_V = 0;\n"); + wf_op("\tCPU->flag_notZ = res;\n"); + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = res;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_N = res >> 8;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_logicl_flag(void) +{ + wf_op("\tCPU->flag_C = 0;\n"); + wf_op("\tCPU->flag_V = 0;\n"); + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = res;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); + wf_op("\tCPU->flag_N = res >> 8;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_add_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n"); + wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n"); + wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_addx_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ |= res;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n"); + wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_sub_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); + wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_subx_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ |= res;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_cmp_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_N = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); + wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); + wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_negx_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_V = res & src;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = (res & src) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ |= res;\n"); + wf_op("\tCPU->flag_V = (res & src) >> 24;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static void set_neg_flag(void) +{ + switch(current_size) + { + case SIZE_BYTE: + wf_op("\tCPU->flag_V = res & src;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); + break; + + case SIZE_WORD: + wf_op("\tCPU->flag_V = (res & src) >> 8;\n"); + wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); + wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); + break; + + case SIZE_LONG: + wf_op("\tCPU->flag_notZ = res;\n"); + wf_op("\tCPU->flag_V = (res & src) >> 24;\n"); + wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); +// wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); + wf_op("\tCPU->flag_N = res >> 24;\n"); + break; + } +} + +static char* get_cond_as_cond(u32 cond, u32 notvar) +{ + if (notvar) cond ^= 1; + + switch(cond) + { + case COND_TR: + sprintf(current_cond_char, "(1)"); + break; + + case COND_FA: + sprintf(current_cond_char, "(0)"); + break; + + case COND_HI: + sprintf(current_cond_char, "(CPU->flag_notZ && (!(CPU->flag_C & 0x100)))"); + break; + + case COND_LS: + sprintf(current_cond_char, "((!CPU->flag_notZ) || (CPU->flag_C & 0x100))"); + break; + + case COND_CC: + sprintf(current_cond_char, "(!(CPU->flag_C & 0x100))"); + break; + + case COND_CS: + sprintf(current_cond_char, "(CPU->flag_C & 0x100)"); + break; + + case COND_NE: + sprintf(current_cond_char, "(CPU->flag_notZ)"); + break; + + case COND_EQ: + sprintf(current_cond_char, "(!CPU->flag_notZ)"); + break; + + case COND_VC: + sprintf(current_cond_char, "(!(CPU->flag_V & 0x80))"); + break; + + case COND_VS: + sprintf(current_cond_char, "(CPU->flag_V & 0x80)"); + break; + + case COND_PL: + sprintf(current_cond_char, "(!(CPU->flag_N & 0x80))"); + break; + + case COND_MI: + sprintf(current_cond_char, "(CPU->flag_N & 0x80)"); + break; + + case COND_GE: + sprintf(current_cond_char, "(!((CPU->flag_N ^ CPU->flag_V) & 0x80))"); + break; + + case COND_LT: + sprintf(current_cond_char, "((CPU->flag_N ^ CPU->flag_V) & 0x80)"); + break; + + case COND_GT: + sprintf(current_cond_char, "(CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80)))"); + break; + + case COND_LE: + sprintf(current_cond_char, "((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80))"); + break; + } + + return current_cond_char; +} + +// effective address related function +////////////////////////////////////// + +static u32 has_ea(u32 ea) +{ + if (ea == EA_AINC7) return (current_op->ea_supported[EA_AINC] == 'o'); + if (ea == EA_ADEC7) return (current_op->ea_supported[EA_ADEC] == 'o'); + if (ea <= EA_IMM) return (current_op->ea_supported[ea] == 'o'); + return 0; +} + +static u32 has_ea2(u32 ea) +{ + if (ea == EA_AINC7) return (current_op->ea2_supported[EA_AINC] == 'o'); + if (ea == EA_ADEC7) return (current_op->ea2_supported[EA_ADEC] == 'o'); + if (ea <= EA_IMM) return (current_op->ea2_supported[ea] == 'o'); + return 0; +} + +#if 0 // FIXME: warning removal +static u32 _eamreg_to_ea(u32 eam, u32 reg) +{ + if ((eam > 7) || (reg > 7)) return EA_ILLEGAL; + if ((eam == 3) && (reg == 7)) return EA_AINC7; + if ((eam == 4) && (reg == 7)) return EA_ADEC7; + if (eam != 7) return eam; + if (reg < 5) return (eam + reg); + return EA_ILLEGAL; +} +#endif + +static u32 _ea_to_eamreg(u32 ea) +{ + if (ea < 7) return (ea << 3) | 0; + if (ea == EA_AINC7) return (EA_AINC << 3) | 7; + if (ea == EA_ADEC7) return (EA_ADEC << 3) | 7; + if (ea <= EA_IMM) return (7 << 3) | (ea - 7); + return (7 << 3) | 7; +} + +static u32 is_ea_memory(u32 ea) +{ + if ((ea > EA_AREG) && (ea != EA_IMM)) return 1; + else return 0; +} + +static void _ea_calc_free(u32 ea, u32 rsft) +{ + u32 step; + + step = 0; + switch (current_size) + { + case SIZE_BYTE: + if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2; + else step = 1; + break; + + case SIZE_WORD: + step = 2; + break; + + case SIZE_LONG: + step = 4; + break; + } + + switch (ea) + { + case EA_DREG: +// wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft); + break; + + case EA_AREG: +// wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft); + break; + + case EA_AIND: + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + break; + + case EA_AINC: + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step); + break; + + case EA_AINC7: + wf_op("\tadr = CPU->A[7];\n"); + wf_op("\tCPU->A[7] += %d;\n", step); + break; + + case EA_ADEC: + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step); + wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft); + break; + + case EA_ADEC7: + wf_op("\tadr = CPU->A[7] - %d;\n", step); + wf_op("\tCPU->A[7] = adr;\n"); + break; + + case EA_D16A: + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft); + wf_op("\tPC += 2;\n"); + break; + + case EA_D8AX: + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + wf_op("\tDECODE_EXT_WORD\n"); + break; + + case EA_A16: + wf_op("\tadr = (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case EA_A32: + wf_op("\tadr = (s32)FETCH_LONG;\n"); + wf_op("\tPC += 4;\n"); + break; + + case EA_D16P: + wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case EA_D8PX: + wf_op("\tadr = PC - CPU->BasePC;\n"); + wf_op("\tDECODE_EXT_WORD\n"); + break; + } +} + +static void _ea_calc(u32 ea, u32 rsft) +{ + u32 step; + u32 cycle_sft; + + step = 0; + cycle_sft = 0; + switch (current_size) + { + case SIZE_BYTE: + if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2; + else step = 1; + break; + + case SIZE_WORD: + step = 2; + break; + + case SIZE_LONG: + cycle_sft = 1; + step = 4; + break; + } + + switch (ea) + { + case EA_DREG: +// wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft); + break; + + case EA_AREG: +// wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft); + break; + + case EA_IMM: + current_cycle += (4 << cycle_sft) + 0; + break; + + case EA_AIND: + current_cycle += (4 << cycle_sft) + 0; + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + break; + + case EA_AINC: + current_cycle += (4 << cycle_sft) + 0; + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step); + break; + + case EA_AINC7: + current_cycle += (4 << cycle_sft) + 0; + wf_op("\tadr = CPU->A[7];\n"); + wf_op("\tCPU->A[7] += %d;\n", step); + break; + + case EA_ADEC: + current_cycle += (4 << cycle_sft) + 2; + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step); + wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft); + break; + + case EA_ADEC7: + current_cycle += (4 << cycle_sft) + 2; + wf_op("\tadr = CPU->A[7] - %d;\n", step); + wf_op("\tCPU->A[7] = adr;\n"); + break; + + case EA_D16A: + current_cycle += (4 << cycle_sft) + 4; + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft); + wf_op("\tPC += 2;\n"); + break; + + case EA_D8AX: + current_cycle += (4 << cycle_sft) + 6; + wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); + wf_op("\tDECODE_EXT_WORD\n"); + break; + + case EA_A16: + current_cycle += (4 << cycle_sft) + 4; + wf_op("\tadr = (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case EA_A32: + current_cycle += (4 << cycle_sft) + 8; + wf_op("\tadr = (s32)FETCH_LONG;\n"); + wf_op("\tPC += 4;\n"); + break; + + case EA_D16P: + current_cycle += (4 << cycle_sft) + 4; + wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n"); + wf_op("\tPC += 2;\n"); + break; + + case EA_D8PX: + current_cycle += (4 << cycle_sft) + 6; + wf_op("\tadr = PC - CPU->BasePC;\n"); + wf_op("\tDECODE_EXT_WORD\n"); + break; + } +} + +static void _ea_read_(u32 ea, u32 rsft, char dest[4]) +{ + char sz[8]; + + switch (current_size) + { + case SIZE_BYTE: + strcpy(sz, "BYTE"); + break; + + case SIZE_WORD: + strcpy(sz, "WORD"); + break; + + case SIZE_LONG: + strcpy(sz, "LONG"); + break; + } + + switch (ea) + { + case EA_DREG: + wf_op("\t%s = (%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szc, rsft); + break; + + case EA_AREG: + if (current_size == SIZE_BYTE) + { + wf_op("\t// can't read byte from Ax registers !\n"); + wf_op("\tCPU->Status |= C68K_FAULTED;\n"); + wf_op("\tCCnt = 0;\n"); + wf_op("\tgoto C68k_Exec_Really_End;\n"); + } + else wf_op("\t%s = (%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szc, rsft); + break; +/* + case EA_DREG: + case EA_AREG: + wf_op("\t%s = %s(adr);\n", dest, szcf); + break; +*/ + case EA_A32: + case EA_D8AX: + case EA_D8PX: + case EA_D16A: + case EA_D16P: + case EA_A16: + case EA_ADEC: + case EA_ADEC7: + case EA_AIND: + case EA_AINC: + case EA_AINC7: + mem_op("\tREAD_%s_F(adr, %s)\n", sz, dest); + break; + + case EA_IMM: + switch (current_size) + { + case SIZE_BYTE: + wf_op("\t%s = FETCH_BYTE;\n", dest); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_WORD: + wf_op("\t%s = FETCH_WORD;\n", dest); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_LONG: + wf_op("\t%s = FETCH_LONG;\n", dest); + wf_op("\tPC += 4;\n"); + break; + } + break; + } +} + +static void _ea_read(u32 ea, u32 rsft) +{ + _ea_read_(ea, rsft, "res"); +} + +static void _ea_read_src(u32 ea, u32 rsft) +{ + _ea_read_(ea, rsft, "src"); +} + +static void _ea_read_dst(u32 ea, u32 rsft) +{ + _ea_read_(ea, rsft, "dst"); +} + +static void _ea_read_sx_(u32 ea, u32 rsft, char dest[4]) +{ + char sz[8]; + + switch (current_size) + { + case SIZE_BYTE: + strcpy(sz, "BYTE"); + break; + + case SIZE_WORD: + strcpy(sz, "WORD"); + break; + + case SIZE_LONG: + strcpy(sz, "LONG"); + break; + } + + switch (ea) + { + case EA_DREG: + wf_op("\t%s = (s32)(%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szcs, rsft); + break; + + case EA_AREG: + if (current_size == SIZE_BYTE) + { + wf_op("\t// can't read byte from Ax registers !\n"); + wf_op("\tCPU->Status |= C68K_FAULTED;\n"); + wf_op("\tCCnt = 0;\n"); + wf_op("\tgoto C68k_Exec_Really_End;\n"); + } + else wf_op("\t%s = (s32)(%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szcs, rsft); + break; +/* + case EA_DREG: + case EA_AREG: + wf_op("\t%s = (s32)(%s(adr));\n", dest, szcsf); + break; +*/ + case EA_A32: + case EA_D8AX: + case EA_D8PX: + case EA_D16A: + case EA_D16P: + case EA_A16: + case EA_ADEC: + case EA_ADEC7: + case EA_AIND: + case EA_AINC: + case EA_AINC7: + mem_op("\tREADSX_%s_F(adr, %s)\n", sz, dest); + break; + + case EA_IMM: + switch (current_size) + { + case SIZE_BYTE: + wf_op("\t%s = (s32)(%s(PC)));\n", dest, szcsf); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_WORD: + wf_op("\t%s = (s32)(%s)FETCH_WORD;\n", dest, szcs); + wf_op("\tPC += 2;\n"); + break; + + case SIZE_LONG: + wf_op("\t%s = (s32)(%s)FETCH_LONG;\n", dest, szcs); + wf_op("\tPC += 4;\n"); + break; + } + break; + } +} + +static void _ea_read_sx(u32 ea, u32 rsft) +{ + _ea_read_sx_(ea, rsft, "res"); +} + +static void _ea_read_src_sx(u32 ea, u32 rsft) +{ + _ea_read_sx_(ea, rsft, "src"); +} + +static void _ea_write(u32 ea, u32 rsft) +{ + char sz[8]; + + switch (current_size) + { + case SIZE_BYTE: + strcpy(sz, "BYTE"); + break; + + case SIZE_WORD: + strcpy(sz, "WORD"); + break; + + case SIZE_LONG: + strcpy(sz, "LONG"); + break; + } + + switch (ea) + { + case EA_DREG: + wf_op("\t%s(&CPU->D[(Opcode >> %d) & 7])) = res;\n", szcf, +rsft); + break; + + case EA_AREG: + // writes in Ax registers are always 32 bits sized + wf_op("\tCPU->A[(Opcode >> %d) & 7] = res;\n", rsft); + break; +/* + case EA_DREG: + case EA_AREG: + wf_op("\t%s(adr) = res;\n", szcf); + break; +*/ + case EA_A32: + case EA_D8AX: + case EA_D8PX: + case EA_D16A: + case EA_D16P: + case EA_A16: + case EA_ADEC: + case EA_ADEC7: + case EA_AIND: + case EA_AINC: + case EA_AINC7: + mem_op("\tWRITE_%s_F(adr, res)\n", sz); + break; + } +} + +// misc function +///////////////// + +static u32 get_current_opcode_base(void) +{ + u32 base; + + base = current_op->op_base; + if (current_op->eam_sft != -1) base += (current_eam & 7) << current_op->eam_sft; + if (current_op->reg_sft != -1) base += (current_reg & 7) << current_op->reg_sft; + if (current_op->eam2_sft != -1) base += (current_eam2 & 7) << current_op->eam2_sft; + if (current_op->reg2_sft != -1) base += (current_reg2 & 7) << current_op->reg2_sft; + if (current_op->size_type == 1) base += (current_size - 1) << current_op->size_sft; + else if (current_op->size_type == 2) base += (current_size & 3) << current_op->size_sft; + + return base; +} + +static void start_all(int v) +{ + u32 base; + + base = get_current_opcode_base(); + + // generate jump table + gen_opjumptable(base); + + // generate label & declarations + start_op(base, v); +} + +static void set_current_size(u32 sz) +{ + current_size = sz; + switch(current_size) + { + case SIZE_BYTE: + current_bits_mask = 0xFF; + current_sft_mask = 7; + strcpy(szc, "u8"); + strcpy(szcf, "*(BYTE_OFF + (u8*)"); + strcpy(szcs, "s8"); + strcpy(szcsf, "*(BYTE_OFF + (s8*)"); + break; + + case SIZE_WORD: + current_bits_mask = 0xFFFF; + current_sft_mask = 15; + strcpy(szc, "u16"); + strcpy(szcf, "*(WORD_OFF + (u16*)"); + strcpy(szcs, "s16"); + strcpy(szcsf, "*(WORD_OFF + (s16*)"); + break; + + case SIZE_LONG: + current_bits_mask = 0xFFFFFFFF; + current_sft_mask = 31; + strcpy(szc, "u32"); + strcpy(szcf, "*((u32*)"); + strcpy(szcs, "s32"); + strcpy(szcsf, "*((s32*)"); + break; + } +} + +// gen privilege exception (happen when S flag is not set) +static void gen_privilege_exception(char *pre) +{ + // swap A7 and USP (because S not set) + wf_op("%sres = CPU->USP;\n", pre); + wf_op("%sCPU->USP = CPU->A[7];\n", pre); + wf_op("%sCPU->A[7] = res;\n", pre); + + // get vector & add cycle + wf_op("%sres = C68K_PRIVILEGE_VIOLATION_EX;\n", pre); + adds_CCnt("c68k_exception_cycle_table[res]"); + + // we will do some mem/io access + do_pre_io(); + + // push PC and SR + mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre); + mem_op("%sPUSH_16_F(GET_SR)\n", pre); + + // adjust SR + wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre); + + // fetch new PC + mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre); + wf_op("%sSET_PC(PC)\n", pre); +} + +static void gen_exception(char *pre, char* exception) +{ + // swap A7 and USP if needed + wf_op("%sif (!CPU->flag_S)\n", pre); + wf_op("%s{\n", pre); + wf_op("%s\tres = CPU->USP;\n", pre); + wf_op("%s\tCPU->USP = CPU->A[7];\n", pre); + wf_op("%s\tCPU->A[7] = res;\n", pre); + wf_op("%s}\n", pre); + + // get vector & add cycle + wf_op("%sres = %s;\n", pre, exception); + adds_CCnt("c68k_exception_cycle_table[res]"); + + // we will do some mem/io access + do_pre_io(); + + // push PC and SR + mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre); + mem_op("%sPUSH_16_F(GET_SR)\n", pre); + + // adjust SR + wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre); + + // fetch new PC + mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre); + wf_op("%sSET_PC(PC)\n", pre); +} + diff --git a/yabause/src/carbon/CMakeLists.txt b/yabause/src/carbon/CMakeLists.txt new file mode 100644 index 0000000000..219de3e075 --- /dev/null +++ b/yabause/src/carbon/CMakeLists.txt @@ -0,0 +1,36 @@ +project(yabause-carbon) + +find_library(CARBON_LIBRARY carbon) + +if (NOT CARBON_LIBRARY) + return() +endif() + +set(yabause_carbon_SOURCES main.c settings.c settings.h cpustatus.c cpustatus.h) + +add_executable(yabause-carbon ${yabause_carbon_SOURCES}) +target_link_libraries(yabause-carbon yabause ${YABAUSE_LIBRARIES} ${CARBON_LIBRARY}) + +if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/Yabause.app" "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Yabause.app" + ) +endif() + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS/yabause" + COMMAND ${CMAKE_COMMAND} -E copy $ "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS/yabause" + DEPENDS yabause-carbon + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS" +) + +add_custom_target(yabause-carbon-app ALL + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS/yabause") diff --git a/yabause/src/carbon/Makefile.am b/yabause/src/carbon/Makefile.am new file mode 100644 index 0000000000..b2822d2096 --- /dev/null +++ b/yabause/src/carbon/Makefile.am @@ -0,0 +1,23 @@ +EXTRA_DIST = build_dmg.sh Yabause.app/Contents/Info.plist Yabause.app/Contents/Resources/yabause.icns \ + Yabause.app/Contents/Resources/cpustatus.nib/classes.nib Yabause.app/Contents/Resources/cpustatus.nib/info.nib \ + Yabause.app/Contents/Resources/cpustatus.nib/objects.xib \ + Yabause.app/Contents/Resources/menu.nib/classes.nib Yabause.app/Contents/Resources/menu.nib/info.nib \ + Yabause.app/Contents/Resources/menu.nib/objects.xib \ + Yabause.app/Contents/Resources/preferences.nib/classes.nib Yabause.app/Contents/Resources/preferences.nib/info.nib \ + Yabause.app/Contents/Resources/preferences.nib/objects.xib +bin_PROGRAMS = yabause +yabause_SOURCES = main.c settings.c settings.h cpustatus.c cpustatus.h +yabause_CFLAGS = $(YAB_CFLAGS) +yabause_LDADD = $(YAB_LIBS) ../libyabause.a + +all-local: + if [ ! -e Yabause.app/Contents/MacOS ]; then mkdir Yabause.app/Contents/MacOS; fi + cp yabause Yabause.app/Contents/MacOS + if [ ! -e Yabause.app/Contents/Frameworks ]; then mkdir Yabause.app/Contents/Frameworks; fi + if [ ! -e Yabause.app/Contents/Frameworks/SDL.framework ]; then \ + if [ -e /Library/Frameworks/SDL.framework ]; then \ + cp -R /Library/Frameworks/SDL.framework Yabause.app/Contents/Frameworks; \ + elif [ -e ~/Library/Frameworks/SDL.framework ]; then \ + cp -R ~/Library/Frameworks/SDL.framework Yabause.app/Contents/Frameworks; \ + fi; \ + fi diff --git a/yabause/src/carbon/Yabause.app/Contents/Info.plist b/yabause/src/carbon/Yabause.app/Contents/Info.plist new file mode 100644 index 0000000000..9e3e0c781f --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleExecutable + yabause + CFBundleIconFile + yabause.icns + CFBundleIdentifier + org.yabause.Yabause + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Yabause + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.9.10 + CFBundleVersion + 0.9.10 + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/classes.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/classes.nib new file mode 100644 index 0000000000..ea58db1189 --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/classes.nib @@ -0,0 +1,4 @@ +{ +IBClasses = (); +IBVersion = 1; +} diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/info.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/info.nib new file mode 100644 index 0000000000..79be5a82ec --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/info.nib @@ -0,0 +1,67 @@ + + + + + IBDocumentLocation + 76 101 356 240 0 0 1280 1002 + IBFramework Version + 446.1 + IBLockedObjects + + 223 + 239 + 240 + 241 + 242 + 243 + 244 + 245 + 232 + 235 + 246 + 247 + 248 + 249 + 250 + 251 + 252 + 253 + 254 + 258 + 259 + 261 + 277 + 296 + 286 + 271 + 267 + 299 + 300 + 306 + 304 + 270 + 308 + 303 + 266 + 298 + 273 + 302 + 274 + 301 + 269 + 311 + 310 + 295 + + IBOldestOS + 1 + IBOpenObjects + + 166 + + IBSystem Version + 8L2127 + targetFramework + IBCarbonFramework + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/objects.xib b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/objects.xib new file mode 100644 index 0000000000..025d5f41b5 --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/cpustatus.nib/objects.xib @@ -0,0 +1,1229 @@ + + + IBCarbonFramework + + NSApplication + + + + 404 468 747 1080 + Processor Status + + 0 0 343 612 + 0 0 612 343 + + + 6 20 323 296 + 20 6 276 317 + + + 36 36 49 73 + 16 30 37 13 + TRUE + 1 + R0 + -2 + + + 57 36 70 73 + 16 51 37 13 + TRUE + 1 + R1 + -2 + + + 78 36 91 73 + 16 72 37 13 + TRUE + 1 + R2 + -2 + + + 99 36 112 73 + 16 93 37 13 + TRUE + 1 + R3 + -2 + + + 120 36 133 73 + 16 114 37 13 + TRUE + 1 + R4 + -2 + + + 141 36 154 73 + 16 135 37 13 + TRUE + 1 + R5 + -2 + + + 162 36 175 73 + 16 156 37 13 + TRUE + 1 + R6 + -2 + + + 183 36 196 73 + 16 177 37 13 + TRUE + 1 + R7 + -2 + + + 36 162 49 199 + 142 30 37 13 + TRUE + 1 + R8 + -2 + + + 57 162 70 199 + 142 51 37 13 + TRUE + 1 + R9 + -2 + + + 78 162 91 199 + 142 72 37 13 + TRUE + 1 + R10 + -2 + + + 99 162 112 199 + 142 93 37 13 + TRUE + 1 + R11 + -2 + + + 120 162 133 199 + 142 114 37 13 + TRUE + 1 + R12 + -2 + + + 141 162 154 199 + 142 135 37 13 + TRUE + 1 + R13 + -2 + + + 162 162 175 199 + 142 156 37 13 + TRUE + 1 + R14 + -2 + + + 183 162 196 199 + 142 177 37 13 + TRUE + 1 + R15 + -2 + + + 38 84 48 151 + 64 32 67 10 + cpus + 1 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 206 36 219 73 + 16 200 37 13 + TRUE + 1 + MACH + -2 + + + 208 84 218 151 + 64 202 67 10 + cpus + 17 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 206 162 219 199 + 142 200 37 13 + TRUE + 1 + MACL + -2 + + + 229 36 242 73 + 16 223 37 13 + TRUE + 1 + GBR + -2 + + + 231 84 241 151 + 64 225 67 10 + cpus + 19 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 229 162 242 199 + 142 223 37 13 + TRUE + 1 + VBR + -2 + + + 252 36 265 73 + 16 246 37 13 + TRUE + 1 + PC + -2 + + + 252 162 265 199 + 142 246 37 13 + TRUE + 1 + PR + -2 + + + 59 84 69 151 + 64 53 67 10 + cpus + 2 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 80 84 90 151 + 64 74 67 10 + cpus + 3 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 101 84 111 151 + 64 95 67 10 + cpus + 4 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 122 84 132 151 + 64 116 67 10 + cpus + 5 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 143 84 153 151 + 64 137 67 10 + cpus + 6 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 164 84 174 151 + 64 158 67 10 + cpus + 7 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 185 84 195 151 + 64 179 67 10 + cpus + 8 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 252 84 262 151 + 64 246 67 10 + cpus + 21 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 38 210 48 277 + 190 32 67 10 + cpus + 9 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 59 210 69 277 + 190 53 67 10 + cpus + 10 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 80 210 90 277 + 190 74 67 10 + cpus + 11 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 101 210 111 277 + 190 95 67 10 + cpus + 12 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 122 210 132 277 + 190 116 67 10 + cpus + 13 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 143 210 153 277 + 190 137 67 10 + cpus + 14 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 164 210 174 277 + 190 158 67 10 + cpus + 15 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 185 210 195 277 + 190 179 67 10 + cpus + 16 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 208 210 218 277 + 190 202 67 10 + cpus + 18 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 231 210 241 277 + 190 225 67 10 + cpus + 20 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 252 210 262 277 + 190 246 67 10 + cpus + 22 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 273 81 286 280 + 61 267 199 13 + TRUE + 1 + Status bits go here + -1 + + + 294 36 307 73 + 16 288 37 13 + TRUE + 1 + SR + -2 + + + 296 84 306 277 + 64 290 193 10 + cpus + 23 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + Master + + + 6 316 323 592 + 316 6 276 317 + + + 36 332 49 369 + 16 30 37 13 + TRUE + 1 + R0 + -2 + + + 57 332 70 369 + 16 51 37 13 + TRUE + 1 + R1 + -2 + + + 78 332 91 369 + 16 72 37 13 + TRUE + 1 + R2 + -2 + + + 99 332 112 369 + 16 93 37 13 + TRUE + 1 + R3 + -2 + + + 120 332 133 369 + 16 114 37 13 + TRUE + 1 + R4 + -2 + + + 141 332 154 369 + 16 135 37 13 + TRUE + 1 + R5 + -2 + + + 162 332 175 369 + 16 156 37 13 + TRUE + 1 + R6 + -2 + + + 183 332 196 369 + 16 177 37 13 + TRUE + 1 + R7 + -2 + + + 36 458 49 495 + 142 30 37 13 + TRUE + 1 + R8 + -2 + + + 57 458 70 495 + 142 51 37 13 + TRUE + 1 + R9 + -2 + + + 78 458 91 495 + 142 72 37 13 + TRUE + 1 + R10 + -2 + + + 99 458 112 495 + 142 93 37 13 + TRUE + 1 + R11 + -2 + + + 120 458 133 495 + 142 114 37 13 + TRUE + 1 + R12 + -2 + + + 141 458 154 495 + 142 135 37 13 + TRUE + 1 + R13 + -2 + + + 162 458 175 495 + 142 156 37 13 + TRUE + 1 + R14 + -2 + + + 183 458 196 495 + 142 177 37 13 + TRUE + 1 + R15 + -2 + + + 38 380 48 447 + 64 32 67 10 + cpus + 31 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 206 332 219 369 + 16 200 37 13 + TRUE + 1 + MACH + -2 + + + 208 380 218 447 + 64 202 67 10 + cpus + 47 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 206 458 219 495 + 142 200 37 13 + TRUE + 1 + MACL + -2 + + + 229 332 242 369 + 16 223 37 13 + TRUE + 1 + GBR + -2 + + + 231 380 241 447 + 64 225 67 10 + cpus + 49 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 229 458 242 495 + 142 223 37 13 + TRUE + 1 + VBR + -2 + + + 252 332 265 369 + 16 246 37 13 + TRUE + 1 + PC + -2 + + + 252 458 265 495 + 142 246 37 13 + TRUE + 1 + PR + -2 + + + 59 380 69 447 + 64 53 67 10 + cpus + 32 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 80 380 90 447 + 64 74 67 10 + cpus + 33 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 101 380 111 447 + 64 95 67 10 + cpus + 34 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 122 380 132 447 + 64 116 67 10 + cpus + 35 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 143 380 153 447 + 64 137 67 10 + cpus + 36 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 164 380 174 447 + 64 158 67 10 + cpus + 37 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 185 380 195 447 + 64 179 67 10 + cpus + 38 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 252 380 262 447 + 64 246 67 10 + cpus + 51 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 38 506 48 573 + 190 32 67 10 + cpus + 39 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 59 506 69 573 + 190 53 67 10 + cpus + 40 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 80 506 90 573 + 190 74 67 10 + cpus + 41 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 101 506 111 573 + 190 95 67 10 + cpus + 42 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 122 506 132 573 + 190 116 67 10 + cpus + 43 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 143 506 153 573 + 190 137 67 10 + cpus + 44 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 164 506 174 573 + 190 158 67 10 + cpus + 45 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 185 506 195 573 + 190 179 67 10 + cpus + 46 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 208 506 218 573 + 190 202 67 10 + cpus + 48 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 231 506 241 573 + 190 225 67 10 + cpus + 50 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 252 506 262 573 + 190 246 67 10 + cpus + 52 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + 273 377 286 576 + 61 267 199 13 + TRUE + 1 + Status bits go here + -1 + + + 294 332 307 369 + 16 288 37 13 + TRUE + 1 + SR + -2 + + + 296 380 306 573 + 64 290 193 10 + cpus + 53 + FALSE + -5 + TRUE + 1 + -1 + TRUE + TRUE + + + Slave + + + + FALSE + FALSE + FALSE + FALSE + FALSE + TRUE + 5 + 5 + FALSE + TRUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File's Owner + + Window + + + 313 + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/classes.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/classes.nib new file mode 100644 index 0000000000..c4b887e72b --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/classes.nib @@ -0,0 +1,8 @@ + + + + + IBVersion + 1 + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/info.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/info.nib new file mode 100644 index 0000000000..44cc4f505c --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 740 + IBOldestOS + 6 + IBOpenObjects + + 167 + + IBSystem Version + 10C540 + targetFramework + IBCarbonFramework + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/objects.xib b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/objects.xib new file mode 100644 index 0000000000..c8fc38239d --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/load_dialog.nib/objects.xib @@ -0,0 +1,63 @@ + + + + + + + Address + 55 20 71 72 + + + conf + 50 + Browse + 20 239 40 309 + + + conf + 1 + 23 23 39 228 + + + FALSE + FALSE + FALSE + FALSE + 5 + Load + + + + + + conf + 2 + 55 83 71 306 + + + + 0 0 94 329 + + 72 111 166 440 + 60 0 778 1280 + + + + + + + + + + + + + + Dialog + + File's Owner + + + IBCarbonFramework + 320 + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/classes.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/classes.nib new file mode 100644 index 0000000000..c4b887e72b --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/classes.nib @@ -0,0 +1,8 @@ + + + + + IBVersion + 1 + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/info.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/info.nib new file mode 100644 index 0000000000..f17a327dc0 --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 740 + IBOldestOS + 6 + IBOpenObjects + + 195 + + IBSystem Version + 10C540 + targetFramework + IBCarbonFramework + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/objects.xib b/yabause/src/carbon/Yabause.app/Contents/Resources/menu.nib/objects.xib new file mode 100644 index 0000000000000000000000000000000000000000..6c19b8b5ad66147f662daa25f8e9fdde318c300a GIT binary patch literal 10392 zcmd5?>u=jO5bxeYH*EicpaS_gM-OK)>ZoWEcMGtlLDCM`r-HVbRIN;T5|zh)zY|4? zqDaZKBggYMi+3XL!MoqxY5eMb5m~o5NyC^N+0JkKwv|#u<|v8@KC&NhYQGAekK^fQ zJfqeuLTP$rpI@CFzhh5M5k+?J+_Fjvf)>D*#4#-hY{7Gqr8HiYkHa}^SFjF>x3kxv zBtZ|>EY1i$vOU+fiWj0NDi~A7BKG4aIy*i=$uuT!G0Dy-Uf33U|LAv44h$(xLUP03 zM?)H6JGekuipPUuDc?W;$&_F4!{pyAyhRZv)DD&k^)!wnj0k^mwwxo1uVAMLvx5Zh zFF#Di15Q=#5vTNFi8<3OUMxW8cJS=kv#-a4Vyz&Be@xN*U-75Zsa|T=^Sh<~Gn`}V z{P>T3MHWd%4apsax@XD1??Ljyqf{$$dtZHu@2r!H54n%iEZw$5-}WHUftf8;>vY-D z-L~XEJYI6&HIV#rT`wMPTk^dZ&i>0Dq+b!aCifw=uCpjgX9>nck*dr3*9x_KlRt&? zIVN^Xp4b8OF zZ$5ldEp3`R3!LP-EBxpbPqQ24K7@pF0JF$p*Qp)A4TFaj$dSODGb+Jc2mr0baV%9Q z_!=je%rKy~OMHD~yPjhY%GVfzJr~fTU}?fQ4geVRX$Ju8G;)CJG^2*zFf1_6RuK4| z92yw01Oy9q36!^iK~L6-EM1D1C_yw%TB7}91Oyps(AE6J!+rV0Qjq3${3}Lt>o_DR zd1$&N+eqSR0NF^(A?u|VD*_bo8c={8Zd1JoaI4B#*9(*q*5n?~GKx3Vkx~?~9k-yP zU;6X(p~3yXLbn_1L@5`xrITO#(}^zvyXLeT-2Qo(qG^PoR@K(EnoUSdFA{tk###D0 zqBtQ?AGdOAVumI;_)>Dv5DZrl97qIaRkc9mOiC%l)ay&YTS_&iax{XHf=xA=+({~G zRC4TCY5>#N;80SrhF~NS7}>x#NDVB-gAEk?zS0K%(?8Gzo zUYKf8bv|e=tc@@I29jzG0J^5cO6OA=6aSY0plN8d1Oc-vESi(5PU&evQ1%5tl+E-3A4y zac4#jjfF#*G|YT4abrx91$o|>5GZ6j#xE*k!}Q7sMxz{KN%ID2+c>!~USZovL61FA zu8Ql|u1Z%L)^K)Z90)#T=v6tvS6UYxNU^PpF4(MGH1SGjBx2y2p<2G3h9+8>lAMtc zU9wJ%v&>$;AXQc9Rc6z0LW8W-lFA94qV`1yg5_ene)%jxbey!R&7o8?PsoH726~G^ z_Cbe2F3lCZ)^w-{=>Q(&a_HSYcZ^N~7l2Gfu-?7!1e8z?=0anGy|(tsu`Ff%QgQ2Rg`yY}|EvJWtJIHE-?OvePEri{cuodv`{b62Je%w}F=se!N&ztIe?8T#D@FtA#9$Lof%hY;z zM{omPYgAwPSAkiAynZ}JEBQ)c#shK6yylj}GDXP^rf(8B?OmLFv4gS>t{&Cn^f)7F ecth~~qbP0&zUN;BvRUyET3&T3_{^}%eg6T$Jqd{b literal 0 HcmV?d00001 diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/classes.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/classes.nib new file mode 100644 index 0000000000..ea58db1189 --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/classes.nib @@ -0,0 +1,4 @@ +{ +IBClasses = (); +IBVersion = 1; +} diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/info.nib b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/info.nib new file mode 100644 index 0000000000..8bec016c9c --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBDocumentLocation + 267 126 356 240 0 0 1280 1002 + IBFramework Version + 446.1 + IBOpenObjects + + 166 + + IBSystem Version + 8P2137 + targetFramework + IBCarbonFramework + + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/objects.xib b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/objects.xib new file mode 100644 index 0000000000..607de62a48 --- /dev/null +++ b/yabause/src/carbon/Yabause.app/Contents/Resources/preferences.nib/objects.xib @@ -0,0 +1,775 @@ + + + IBCarbonFramework + + NSApplication + + + + 81 82 409 437 + Yabause preferences + + 0 0 328 355 + + + 12 -1 328 360 + + + 49 -1 328 360 + + + 50 19 116 340 + + + 81 38 97 243 + conf + 1 + + + 79 254 99 324 + conf + 50 + Browse + + + Bios + + + 124 19 218 340 + + + 155 38 171 243 + conf + 2 + + + 153 254 173 324 + conf + 51 + Browse + + + 182 35 202 324 + conf + 3 + + Popup: + + + Dummy CD + + + ISO or CUE file + + + Cdrom device + + + + + + Cdrom + + + 226 19 290 340 + + + 254 35 274 324 + conf + 4 + + Popup: + + + Auto-detect + + + Japan (NTSC) + + + Asia (NTSC) + + + North America (NTSC) + + + Central/South America (NTSC) + + + Korea (NTSC) + + + Asia (PAL) + + + Europe + others (PAL) + + + Central/South America (PAL) + + + + + + Region + + + tabs + 129 + 2 + + + 49 -1 328 360 + + + 50 19 146 340 + + + 78 35 98 324 + conf + 5 + + Popup: + + + TRUE + Dummy Video Interface + + + OpenGL Video Interface + + + Software Video Interface + + + + + + 112 35 130 196 + conf + 11 + Enable auto frameskip + + + Video core + + + 148 19 242 340 + + + 207 98 223 226 + FALSE + 224 + + + 176 35 192 87 + Width + + + 200 35 216 87 + Height + + + 178 237 192 324 + FALSE + Keep ratio + + + 177 98 193 226 + FALSE + 320 + + + Resolution + + + 244 19 308 340 + + + 272 35 292 324 + conf + 6 + + Popup: + + + Dummy Sound Interface + + + SDL Sound Interface + + + + + + Sound core + + + tabs + 130 + 2 + + + 49 -1 328 360 + + + 50 19 144 340 + + + 81 38 97 243 + conf + 7 + + + 79 254 99 324 + conf + 52 + Browse + + + 108 35 128 324 + conf + 8 + + Popup: + + + None + + + Pro Action Replay + + + 4 Mbit Backup Ram + + + 8 Mbit Backup Ram + + + 16 Mbit Backup Ram + + + 32 Mbit Backup Ram + + + 8 Mbit Dram + + + 32 Mbit Dram + + + Netlink + + + 16 Mbit ROM + + + + + + Cartridge + + + 152 19 218 340 + + + 183 38 199 243 + conf + 9 + + + 181 254 201 324 + conf + 53 + Browse + + + Memory + + + 226 19 292 340 + + + 257 38 273 243 + conf + 10 + + + 255 254 275 324 + conf + 54 + Browse + + + Mpeg ROM + + + tabs + 131 + 2 + + + 49 -1 328 360 + + + 64 19 80 115 + Up + + + 65 126 81 167 + conf + 31 + + + 90 19 106 115 + Right + + + 91 126 107 167 + conf + 32 + + + 116 19 132 115 + Down + + + 117 126 133 167 + conf + 33 + + + 142 19 158 115 + Left + + + 143 126 159 167 + conf + 34 + + + 168 19 184 115 + Right trigger + + + 169 126 185 167 + conf + 35 + + + 194 19 210 115 + Left trigger + + + 195 126 211 167 + conf + 36 + + + 220 19 236 115 + Start + + + 221 126 237 167 + conf + 37 + + + 65 296 81 337 + conf + 38 + + + 64 189 80 285 + A + + + 117 296 133 337 + conf + 40 + + + 142 189 158 285 + X + + + 194 189 210 285 + Z + + + 169 296 185 337 + conf + 42 + + + 195 296 211 337 + conf + 43 + + + 90 189 106 285 + B + + + 116 189 132 285 + C + + + 91 296 107 337 + conf + 39 + + + 143 296 159 337 + conf + 41 + + + 168 189 184 285 + Y + + + 54 178 296 179 + + + tabs + 132 + Input + 2 + + + tabs + 128 + + + contentResID + 0 + tabEnabled + 1 + tabName + General + userPane + + + + contentResID + 0 + tabEnabled + 1 + tabName + Video/Sound + userPane + + + + contentResID + 0 + tabEnabled + 1 + tabName + Memory + userPane + + + + contentResID + 0 + tabEnabled + 1 + tabName + Input + userPane + + + + + + + FALSE + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 23642 -6178 23658 -6082 + Up + + + 23653 -6048 23649 -6053 + + + 23647 -6173 23663 -6077 + Up + + + 23658 -6043 23654 -6048 + + + + + + + + + 23648 -6053 23644 -6058 + + + 23585 -6183 23601 -6087 + Right + + + 23700 -6053 23696 -6058 + + + 23611 -6183 23627 -6087 + Down + + + 23726 -6053 23722 -6058 + + + 23715 -6183 23731 -6087 + Start + + + 23622 -6053 23618 -6058 + + + 23689 -6183 23705 -6087 + Left trigger + + + 23596 -6053 23592 -6058 + + + 23674 -6053 23670 -6058 + + + 23663 -6183 23679 -6087 + Right trigger + + + 23570 -6053 23566 -6058 + + + 23559 -6183 23575 -6087 + Up + + + 23637 -6183 23653 -6087 + Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dialog + + File's Owner + + NSCustomView2 + + + 313 + diff --git a/yabause/src/carbon/Yabause.app/Contents/Resources/yabause.icns b/yabause/src/carbon/Yabause.app/Contents/Resources/yabause.icns new file mode 100644 index 0000000000000000000000000000000000000000..a03911992851cc616b1e2f2c7bcf98407739f43a GIT binary patch literal 45411 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 + +#include "cpustatus.h" +#include "../sh2core.h" + +/* Dialog field IDs */ + +#define SLAVE_ID_OFFSET 30 + +#define R0_ID 1 +#define R1_ID 2 +#define R2_ID 3 +#define R3_ID 4 +#define R4_ID 5 +#define R5_ID 6 +#define R6_ID 7 +#define R7_ID 8 +#define R8_ID 9 +#define R9_ID 10 +#define R10_ID 11 +#define R11_ID 12 +#define R12_ID 13 +#define R13_ID 14 +#define R14_ID 15 +#define R15_ID 16 +#define MACH_ID 17 +#define MACL_ID 18 +#define GBR_ID 19 +#define VBR_ID 20 +#define PC_ID 21 +#define PR_ID 22 +#define SR_ID 23 + +static WindowRef CPUStatusWindow; + +static OSStatus CPUStatusWindowEventHandler(EventHandlerCallRef myHandler, + EventRef theEvent, + void *userData) +{ + OSStatus result = eventNotHandledErr; + WindowRef window; + MenuRef menu; + + switch(GetEventKind(theEvent)) + { + case kEventWindowClose: + GetEventParameter(theEvent, kEventParamDirectObject, typeWindowRef, + 0, sizeof(typeWindowRef), 0, &window); + DisposeWindow(window); + menu = GetMenuRef(1); + ChangeMenuItemAttributes(menu, 4, 0, kMenuItemAttrHidden); + ChangeMenuItemAttributes(menu, 5, kMenuItemAttrHidden, 0); + + result = noErr; + break; + } + + return result; +} + +void ShowCPUStatusWindow(void) +{ + IBNibRef nib; + + EventTypeSpec eventList[] = { {kEventClassWindow, kEventWindowClose} }; + + CreateNibReference(CFSTR("cpustatus"), &nib); + CreateWindowFromNib(nib, CFSTR("Window"), &CPUStatusWindow); + ShowWindow(CPUStatusWindow); + + InstallWindowEventHandler(CPUStatusWindow, + NewEventHandlerUPP(CPUStatusWindowEventHandler), + GetEventTypeCount(eventList), + eventList, CPUStatusWindow, NULL); + + UpdateCPUStatusWindow(); +} + +void HideCPUStatusWindow(void) +{ + DisposeWindow(CPUStatusWindow); +} + +static void SetSRString(u32 SR, CFMutableStringRef s) +{ + int ii; + + for(ii = 0; ii < 32; ii++) + { + if(SR & 0x80000000) + CFStringAppendCString(s, "1", kCFStringEncodingASCII); + else + CFStringAppendCString(s, "0", kCFStringEncodingASCII); + + SR <<= 1; + } +} + +static void SetRegisterValue(const int controlId, CFStringRef s) +{ + ControlID id; + ControlRef control; + + id.signature = 'cpus'; + id.id = controlId; + GetControlByID(CPUStatusWindow, &id, &control); + SetControlData(control, kControlEditTextPart, + kControlEditTextCFStringTag, sizeof(CFStringRef), &s); + +} + +void UpdateCPUStatusWindow(void) +{ + CFStringRef s; + CFMutableStringRef ms; + sh2regs_struct master = {0}; + sh2regs_struct slave = {0}; + int ii = 0; + int srNumber = 0; + + if(MSH2) + SH2GetRegisters(MSH2, &master); + + if(SSH2) + SH2GetRegisters(SSH2, &slave); + + /* Master registers */ + for(ii = 0; ii < 16; ii++) + { + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.R[ii]); + SetRegisterValue(ii+1, s); + } + + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.MACH); + SetRegisterValue(MACH_ID, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.MACL); + SetRegisterValue(MACL_ID, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.GBR); + SetRegisterValue(GBR_ID, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.VBR); + SetRegisterValue(VBR_ID, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.PC); + SetRegisterValue(PC_ID, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), master.PR); + SetRegisterValue(PR_ID, s); + + ms = CFStringCreateMutable(kCFAllocatorDefault, 32); + SetSRString(master.SR.all, ms); + SetRegisterValue(SR_ID, ms); + + /* Slave registers */ + for(ii = 0; ii < 16; ii++) + { + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.R[ii]); + SetRegisterValue(ii+1+SLAVE_ID_OFFSET, s); + } + + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.MACH); + SetRegisterValue(MACH_ID+SLAVE_ID_OFFSET, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.MACL); + SetRegisterValue(MACL_ID+SLAVE_ID_OFFSET, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.GBR); + SetRegisterValue(GBR_ID+SLAVE_ID_OFFSET, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.VBR); + SetRegisterValue(VBR_ID+SLAVE_ID_OFFSET, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.PC); + SetRegisterValue(PC_ID+SLAVE_ID_OFFSET, s); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%x"), slave.PR); + SetRegisterValue(PR_ID+SLAVE_ID_OFFSET, s); + ms = CFStringCreateMutable(kCFAllocatorDefault, 32); + SetSRString(slave.SR.all, ms); + SetRegisterValue(SR_ID+SLAVE_ID_OFFSET, ms); +} diff --git a/yabause/src/carbon/cpustatus.h b/yabause/src/carbon/cpustatus.h new file mode 100644 index 0000000000..58cb7df205 --- /dev/null +++ b/yabause/src/carbon/cpustatus.h @@ -0,0 +1,27 @@ +/* Copyright 2006 Anders Montonen + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CPUSTATUS_H_ +#define CPUSTATUS_H_ + +void ShowCPUStatusWindow(void); +void HideCPUStatusWindow(void); +void UpdateCPUStatusWindow(void); + +#endif /* CPUSTATUS_H_ */ diff --git a/yabause/src/carbon/main.c b/yabause/src/carbon/main.c new file mode 100644 index 0000000000..e89acccf91 --- /dev/null +++ b/yabause/src/carbon/main.c @@ -0,0 +1,609 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Anders Montonen + Copyright 2010 Alex Marshall + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "../core.h" +#include "../memory.h" +#include "settings.h" +#include "cpustatus.h" +#include "../yabause.h" + +#define YUI_MENU_EMULATION 1 +#define YUI_MENU_DEBUG 2 + +#define YUI_COMMAND_RESET 1 +#define YUI_COMMAND_PAUSE 2 +#define YUI_COMMAND_RESUME 3 +#define YUI_COMMAND_SHOW_CPU 4 +#define YUI_COMMAND_HIDE_CPU 5 +#define YUI_COMMAND_TOGGLE_NBG0 6 +#define YUI_COMMAND_TOGGLE_NBG1 7 +#define YUI_COMMAND_TOGGLE_NBG2 8 +#define YUI_COMMAND_TOGGLE_NBG3 9 +#define YUI_COMMAND_TOGGLE_RBG0 10 +#define YUI_COMMAND_TOGGLE_VDP1 11 +#define YUI_COMMAND_TOGGLE_FULLSCREEN 12 +#define YUI_COMMAND_LOAD_BINARY 13 +#define YUI_COMMAND_LOAD_AND_EXECUTE 14 +#define YUI_COMMAND_SAVE_BINARY 15 + +AGLContext myAGLContext = NULL; +WindowRef myWindow = NULL; +yabauseinit_struct yinit; + +M68K_struct * M68KCoreList[] = { +&M68KDummy, +#ifdef HAVE_C68K +&M68KC68K, +#endif +#ifdef HAVE_Q68 +&M68KQ68, +#endif +NULL +}; + +SH2Interface_struct *SH2CoreList[] = { +&SH2Interpreter, +&SH2DebugInterpreter, +NULL +}; + +PerInterface_struct *PERCoreList[] = { +&PERDummy, +NULL +}; + +CDInterface *CDCoreList[] = { +&DummyCD, +&ISOCD, +&ArchCD, +NULL +}; + +SoundInterface_struct *SNDCoreList[] = { +&SNDDummy, +#ifdef HAVE_LIBSDL +&SNDSDL, +#endif +NULL +}; + +VideoInterface_struct *VIDCoreList[] = { +&VIDDummy, +&VIDOGL, +&VIDSoft, +NULL +}; + +static EventLoopTimerRef EventTimer; +int load_file_core(char* file, char* addr, int type); + +void YuiIdle(EventLoopTimerRef a, void * b) +{ + PERCore->HandleEvents(); +} + +void read_settings(void) { + PerPad_struct * pad; + int i; + CFStringRef s; + yinit.percoretype = PERCORE_DUMMY; + yinit.sh2coretype = SH2CORE_INTERPRETER; + yinit.vidcoretype = VIDCORE_OGL; + yinit.m68kcoretype = M68KCORE_C68K; + s = CFPreferencesCopyAppValue(CFSTR("VideoCore"), + kCFPreferencesCurrentApplication); + if (s) + yinit.vidcoretype = CFStringGetIntValue(s) - 1; + yinit.sndcoretype = SNDCORE_DUMMY; + s = CFPreferencesCopyAppValue(CFSTR("SoundCore"), + kCFPreferencesCurrentApplication); + if (s) + yinit.sndcoretype = CFStringGetIntValue(s) - 1; + yinit.cdcoretype = CDCORE_ARCH; + s = CFPreferencesCopyAppValue(CFSTR("CDROMCore"), + kCFPreferencesCurrentApplication); + if (s) + yinit.cdcoretype = CFStringGetIntValue(s) - 1; + yinit.carttype = CART_NONE; + s = CFPreferencesCopyAppValue(CFSTR("CartType"), + kCFPreferencesCurrentApplication); + if (s) + yinit.carttype = CFStringGetIntValue(s) - 1; + yinit.regionid = 0; + s = CFPreferencesCopyAppValue(CFSTR("Region"), + kCFPreferencesCurrentApplication); + if (s) + yinit.regionid = CFStringGetIntValue(s) - 1; + + yinit.biospath = 0; + s = CFPreferencesCopyAppValue(CFSTR("BiosPath"), + kCFPreferencesCurrentApplication); + if (s) + yinit.biospath = strdup(CFStringGetCStringPtr(s, 0)); + yinit.cdpath = 0; + s = CFPreferencesCopyAppValue(CFSTR("CDROMDrive"), + kCFPreferencesCurrentApplication); + if (s) + yinit.cdpath = strdup(CFStringGetCStringPtr(s, 0)); + yinit.buppath = 0; + s = CFPreferencesCopyAppValue(CFSTR("BackupRamPath"), + kCFPreferencesCurrentApplication); + if (s) + yinit.buppath = strdup(CFStringGetCStringPtr(s, 0)); + yinit.mpegpath = 0; + s = CFPreferencesCopyAppValue(CFSTR("MpegRomPath"), + kCFPreferencesCurrentApplication); + if (s) + yinit.mpegpath = strdup(CFStringGetCStringPtr(s, 0)); + yinit.cartpath = 0; + s = CFPreferencesCopyAppValue(CFSTR("CartPath"), + kCFPreferencesCurrentApplication); + if (s) + yinit.cartpath = strdup(CFStringGetCStringPtr(s, 0)); + + yinit.videoformattype = VIDEOFORMATTYPE_NTSC; + + s = CFPreferencesCopyAppValue(CFSTR("AutoFrameSkip"), + kCFPreferencesCurrentApplication); + if (s) + yinit.frameskip = CFStringGetIntValue(s); + + PerPortReset(); + pad = PerPadAdd(&PORTDATA1); + + i = 0; + while(PerPadNames[i]) { + s = CFPreferencesCopyAppValue( + CFStringCreateWithCString(0, PerPadNames[i], 0), + kCFPreferencesCurrentApplication); + if (s) + PerSetKey(CFStringGetIntValue(s), i, pad); + i++; + } +} + +static void YuiPause(const int Pause) +{ + EventTimerInterval Interval; + + if(Pause) + { + Interval = kEventDurationForever; + ScspMuteAudio(SCSP_MUTE_SYSTEM); + } + else + { + Interval = 16*kEventDurationMillisecond; + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + } + + SetEventLoopTimerNextFireTime(EventTimer, Interval); +} + +void YuiRun(void) { + static int FirstRun = 1; + EventLoopTimerUPP myFrameUPP; + + if(FirstRun) + { + myFrameUPP = NewEventLoopTimerUPP(YuiIdle); + InstallEventLoopTimer(GetCurrentEventLoop(), kEventDurationNoWait, + 16*kEventDurationMillisecond, myFrameUPP, NULL, &EventTimer); + FirstRun = 0; + } + else + { + YuiPause(0); + YabauseDeInit(); + } + + read_settings(); + YabauseInit(&yinit); +} + +static void TogglePairedMenuItems(MenuRef menu, MenuItemIndex BaseItemIndex) +{ + MenuItemAttributes ItemAttributes; + + GetMenuItemAttributes(menu, BaseItemIndex, &ItemAttributes); + + if(ItemAttributes & kMenuItemAttrHidden) + { + ChangeMenuItemAttributes(menu, BaseItemIndex, 0, kMenuItemAttrHidden); + ChangeMenuItemAttributes(menu, BaseItemIndex+1, kMenuItemAttrHidden, 0); + } + else + { + ChangeMenuItemAttributes(menu, BaseItemIndex, kMenuItemAttrHidden, 0); + ChangeMenuItemAttributes(menu, BaseItemIndex+1, 0, kMenuItemAttrHidden); + } +} + +OSStatus MyWindowEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) +{ + OSStatus ret = noErr; + MenuRef menu; + + switch(GetEventClass(theEvent)) { + case kEventClassWindow: + switch (GetEventKind (theEvent)) { + case kEventWindowClose: + + YabauseDeInit(); + QuitApplicationEventLoop(); + break; + + case kEventWindowBoundsChanged: + aglUpdateContext(myAGLContext); + { + Rect bounds; + GetEventParameter(theEvent, kEventParamCurrentBounds, + typeQDRectangle, NULL, sizeof(Rect), NULL, &bounds); + glViewport(0, 0, bounds.right - bounds.left, + bounds.bottom - bounds.top); + } + break; + } + break; + case kEventClassCommand: + { + HICommand command; + GetEventParameter(theEvent, kEventParamDirectObject, + typeHICommand, NULL, sizeof(HICommand), NULL, &command); + switch(command.commandID) { + case kHICommandPreferences: + CreateSettingsWindow(); + break; + case kHICommandQuit: + YabauseDeInit(); + QuitApplicationEventLoop(); + break; + case YUI_COMMAND_RESET: + YabauseReset(); + break; + case YUI_COMMAND_PAUSE: + YuiPause(1); + menu = GetMenuRef(YUI_MENU_EMULATION); + TogglePairedMenuItems(menu, 2); + UpdateCPUStatusWindow(); + break; + case YUI_COMMAND_RESUME: + YuiPause(0); + menu = GetMenuRef(YUI_MENU_EMULATION); + TogglePairedMenuItems(menu, 2); + break; + case YUI_COMMAND_SHOW_CPU: + ShowCPUStatusWindow(); + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 1); + break; + case YUI_COMMAND_HIDE_CPU: + HideCPUStatusWindow(); + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 1); + break; + case YUI_COMMAND_TOGGLE_NBG0: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 4); + ToggleNBG0(); + } + break; + case YUI_COMMAND_TOGGLE_NBG1: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 6); + ToggleNBG1(); + } + break; + case YUI_COMMAND_TOGGLE_NBG2: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 8); + ToggleNBG2(); + } + break; + case YUI_COMMAND_TOGGLE_NBG3: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 10); + ToggleNBG3(); + } + break; + case YUI_COMMAND_TOGGLE_RBG0: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 12); + ToggleRBG0(); + } + break; + case YUI_COMMAND_TOGGLE_VDP1: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_DEBUG); + TogglePairedMenuItems(menu, 14); + ToggleVDP1(); + } + break; + case YUI_COMMAND_TOGGLE_FULLSCREEN: + if(VIDCore) + { + menu = GetMenuRef(YUI_MENU_EMULATION); + TogglePairedMenuItems(menu, 5); + ToggleFullScreen(); + } + break; + case YUI_COMMAND_LOAD_BINARY: + CreateLoadWindow(0); + break; + case YUI_COMMAND_LOAD_AND_EXECUTE: + CreateLoadWindow(1); + break; + case YUI_COMMAND_SAVE_BINARY: +// MappedMemorySave(file, address, size); + break; + default: + ret = eventNotHandledErr; + printf("unhandled command\n"); + break; + } + } + break; + + case kEventClassKeyboard: + switch(GetEventKind(theEvent)) { + int i; + UInt32 key; + case kEventRawKeyDown: + GetEventParameter(theEvent, kEventParamKeyCode, + typeUInt32, NULL, sizeof(UInt32), NULL, &key); + PerKeyDown(key); + break; + case kEventRawKeyUp: + GetEventParameter(theEvent, kEventParamKeyCode, + typeUInt32, NULL, sizeof(UInt32), NULL, &key); + PerKeyUp(key); + break; + } + break; + } + + return ret; +} + +static WindowRef CreateMyWindow() { + + WindowRef myWindow; + Rect contentBounds; + + CFStringRef windowTitle = CFSTR("Yabause"); + WindowClass windowClass = kDocumentWindowClass; + WindowAttributes attributes = + kWindowStandardDocumentAttributes | + kWindowStandardHandlerAttribute | + kWindowLiveResizeAttribute; + + EventTypeSpec eventList[] = { + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassCommand, kEventCommandProcess }, + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp } + }; + + SetRect(&contentBounds, 200, 200, 520, 424); + + CreateNewWindow (windowClass, + attributes, + &contentBounds, + &myWindow); + + SetWindowTitleWithCFString (myWindow, windowTitle); + CFRelease(windowTitle); + ShowWindow(myWindow); + + InstallWindowEventHandler(myWindow, + NewEventHandlerUPP (MyWindowEventHandler), + GetEventTypeCount(eventList), + eventList, myWindow, NULL); + return myWindow; +} + +static OSStatus MyAGLReportError (void) { + GLenum err = aglGetError(); + + if (err == AGL_NO_ERROR) + return noErr; + else + return (OSStatus) err; +} + +static OSStatus MySetWindowAsDrawableObject (WindowRef window) +{ + OSStatus err = noErr; + + GLint attributes[] = { AGL_RGBA, + AGL_DOUBLEBUFFER, + AGL_DEPTH_SIZE, 24, + AGL_NONE }; + + AGLPixelFormat myAGLPixelFormat; + + myAGLPixelFormat = aglChoosePixelFormat (NULL, 0, attributes); + + err = MyAGLReportError (); + + if (myAGLPixelFormat) { + myAGLContext = aglCreateContext (myAGLPixelFormat, NULL); + + err = MyAGLReportError (); + aglDestroyPixelFormat(myAGLPixelFormat); + } + + if (! aglSetDrawable (myAGLContext, GetWindowPort (window))) + err = MyAGLReportError (); + + if (!aglSetCurrentContext (myAGLContext)) + err = MyAGLReportError (); + + return err; + +} + +int main(int argc, char* argv[]) { + MenuRef menu; + EventLoopTimerRef nextFrameTimer; + IBNibRef menuNib; + + myWindow = CreateMyWindow(); + MySetWindowAsDrawableObject(myWindow); + + CreateNibReference(CFSTR("menu"), &menuNib); + SetMenuBarFromNib(menuNib, CFSTR("MenuBar")); + + EnableMenuCommand(NULL, kHICommandPreferences); + + read_settings(); + + YuiRun(); + if(argc >= 2) + load_file_core(argv[1], (argc >= 3) ? argv[2] : NULL, 1); + + RunApplicationEventLoop(); + + return 0; +} + +void YuiErrorMsg(const char * string) { + printf("%s\n", string); +} + +void YuiSetVideoAttribute(int type, int val) { +} + +int YuiSetVideoMode(int width, int height, int bpp, int fullscreen) +{ + static CFDictionaryRef oldDisplayMode = 0; + static int oldDisplayModeValid = 0; + + AGLPixelFormat myAGLPixelFormat; + AGLDrawable myDrawable = aglGetDrawable(myAGLContext); + OSStatus err = noErr; + GLint attributesFullscreen[] = { AGL_RGBA, + AGL_FULLSCREEN, + AGL_DOUBLEBUFFER, + AGL_DEPTH_SIZE, 24, + AGL_NONE }; + CGDirectDisplayID displayId = kCGDirectMainDisplay; + + if(myDrawable) + { + if(fullscreen) + { + Rect bounds; + CGPoint point; + CGDisplayCount displayCount; + + GetWindowBounds(myWindow, kWindowGlobalPortRgn, &bounds); + point.x = (float)bounds.left; + point.y = (float)bounds.top; + + CGGetDisplaysWithPoint(point, 1, &displayId, &displayCount); + + CFDictionaryRef refDisplayMode = CGDisplayBestModeForParameters(displayId, + bpp, width, height, NULL); + if(refDisplayMode) + { + GDHandle gdhDisplay; + oldDisplayMode = CGDisplayCurrentMode(displayId); + oldDisplayModeValid = 1; + + aglSetDrawable(myAGLContext, NULL); + aglSetCurrentContext(NULL); + aglDestroyContext(myAGLContext); + myAGLContext = NULL; + + CGCaptureAllDisplays(); + CGDisplaySwitchToMode(displayId, refDisplayMode); + CGDisplayHideCursor(displayId); + + DMGetGDeviceByDisplayID((DisplayIDType)displayId, &gdhDisplay, 0); + + myAGLPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, attributesFullscreen); + if(myAGLPixelFormat) + { + myAGLContext = aglCreateContext(myAGLPixelFormat, NULL); + if(myAGLContext) + { + aglSetCurrentContext(myAGLContext); + aglSetFullScreen(myAGLContext, width, height, 0, 0); + } + + err = MyAGLReportError(); + aglDestroyPixelFormat(myAGLPixelFormat); + } + else + { + err = MyAGLReportError(); + CGReleaseAllDisplays(); + CGDisplayShowCursor(displayId); + } + } + else + { + err = MyAGLReportError(); + } + } + else + { + if(oldDisplayModeValid) + { + oldDisplayModeValid = 0; + + aglSetDrawable(myAGLContext, NULL); + aglSetCurrentContext(NULL); + aglDestroyContext(myAGLContext); + myAGLContext = NULL; + + CGDisplayShowCursor(displayId); + CGDisplaySwitchToMode(displayId, oldDisplayMode); + CGReleaseAllDisplays(); + + MySetWindowAsDrawableObject(myWindow); + } + } + } + + return !(err == noErr); +} + +void YuiSwapBuffers(void) { + aglSwapBuffers(myAGLContext); +} diff --git a/yabause/src/carbon/settings.c b/yabause/src/carbon/settings.c new file mode 100644 index 0000000000..091dfa05ff --- /dev/null +++ b/yabause/src/carbon/settings.c @@ -0,0 +1,491 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Anders Montonen + Copyright 2010 Alex Marshall + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "settings.h" + +#define TAB_ID 128 +#define TAB_SIGNATURE 'tabs' +int tabList[] = {129, 130, 131, 132}; + +int loadtype = 0; +ControlRef oldTab; + +int mystrnlen(char* in, int maxlen) +{ + int len; + for(len = 0; (*in != 0) && (len < maxlen); len++, in++); + return len; +} + +unsigned int mytoi(char* in) +{ + unsigned int out = 0; + int length = 0; + int i; + int format = 0; /* Decimal */ + if((in[0] == '0') && (in[1] == 'x')) { + in += 2; + format = 1; /* Hexadecimal */ + }else if(in[0] == '$') { + in += 1; + format = 1; /* Hexadecimal */ + }else if((in[0] == 'H') && (in[1] == '\'')) { + in += 2; + format = 1; /* Hexadecimal */ + } + length = mystrnlen(in, 11); + for(i = 0; i < length; i++) { + switch(format) { + case 0: /* Decimal */ + out *= 10; + if((in[i] >= '0') && (in[i] <= '9')) + out += in[i] - '0'; + break; + case 1: /* Hexadecimal */ + out <<= 4; + if((in[i] >= '0') && (in[i] <= '9')) + out += in[i] - '0'; + if((in[i] >= 'A') && (in[i] <= 'F')) + out += (in[i] - 'A') + 0xA; + if((in[i] >= 'a') && (in[i] <= 'f')) + out += (in[i] - 'a') + 0xA; + break; + } + } + return out; +} + +void SelectItemOfTabControl(ControlRef tabControl) +{ + ControlRef userPane; + ControlID controlID; + + GetControlID(tabControl, &controlID); + if (controlID.id != TAB_ID) return; + + controlID.signature = TAB_SIGNATURE; + + controlID.id = tabList[GetControlValue(tabControl) - 1]; + GetControlByID(GetControlOwner(tabControl), &controlID, &userPane); + + DisableControl(oldTab); + SetControlVisibility(oldTab, false, false); + EnableControl(userPane); + SetControlVisibility(userPane, true, true); + oldTab = userPane; + + Draw1Control(tabControl); +} + +pascal OSStatus TabEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) +{ + ControlRef control; + + GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, + NULL, sizeof(ControlRef), NULL, &control ); + + SelectItemOfTabControl(control); + + return eventNotHandledErr; +} + +void InstallTabHandler(WindowRef window) +{ + EventTypeSpec controlSpec = { kEventClassControl, kEventControlHit }; + ControlRef tabControl; + ControlID controlID; + int i; + + controlID.signature = TAB_SIGNATURE; + + for(i = 0;i < 4;i++) { + controlID.id = tabList[i]; + GetControlByID(window, &controlID, &tabControl); + DisableControl(tabControl); + SetControlVisibility(tabControl, false, false); + } + + controlID.id = TAB_ID; + GetControlByID(window, &controlID, &tabControl); + + InstallControlEventHandler(tabControl, + NewEventHandlerUPP( TabEventHandler ), + 1, &controlSpec, 0, NULL); + + SetControl32BitValue(tabControl, 1); + + SelectItemOfTabControl(tabControl); +} + +CFStringRef get_settings(WindowRef window, int i) { + ControlID id; + ControlRef control; + CFStringRef s; + + id.signature = 'conf'; + id.id = i; + GetControlByID(window, &id, &control); + GetControlData(control, kControlEditTextPart, + kControlEditTextCFStringTag, sizeof(CFStringRef), &s, NULL); + + return s; +} + +CFStringRef get_settings_c(WindowRef window, int i) { + ControlID id; + ControlRef control; + CFStringRef s; + + id.signature = 'conf'; + id.id = i; + GetControlByID(window, &id, &control); + s = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%d"), GetControl32BitValue(control)); + + return s; +} + +void set_settings(WindowRef window, int i, CFStringRef s) { + ControlID id; + ControlRef control; + + if (s) { + id.signature = 'conf'; + id.id = i; + GetControlByID(window, &id, &control); + SetControlData(control, kControlEditTextPart, + kControlEditTextCFStringTag, sizeof(CFStringRef), &s); + } +} + +void set_settings_c(WindowRef window, int i, CFStringRef s) { + ControlID id; + ControlRef control; + + if (s) { + id.signature = 'conf'; + id.id = i; + GetControlByID(window, &id, &control); + SetControl32BitValue(control, CFStringGetIntValue(s)); + } +} + +void save_settings(WindowRef window) { + PerPad_struct * pad; + int i; + CFStringRef s; + + CFPreferencesSetAppValue(CFSTR("BiosPath"), get_settings(window, 1), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("CDROMDrive"), get_settings(window, 2), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("CDROMCore"), get_settings_c(window, 3), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("Region"), get_settings_c(window, 4), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("VideoCore"), get_settings_c(window, 5), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("SoundCore"), get_settings_c(window, 6), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("CartPath"), get_settings(window, 7), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("CartType"), get_settings_c(window, 8), + kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("BackupRamPath"), + get_settings(window, 9), kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("MpegRomPath"), + get_settings(window, 10), kCFPreferencesCurrentApplication); + CFPreferencesSetAppValue(CFSTR("AutoFrameSkip"), + get_settings_c(window, 11), kCFPreferencesCurrentApplication); + + PerPortReset(); + pad = PerPadAdd(&PORTDATA1); + + i = 0; + while(PerPadNames[i]) { + s = get_settings(window, 31 + i); + CFPreferencesSetAppValue( + CFStringCreateWithCString(0, PerPadNames[i], 0), + s, kCFPreferencesCurrentApplication); + PerSetKey(CFStringGetIntValue(s), i, pad); + i++; + } + + CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); +} + +void load_settings(WindowRef window) { + int i; + + set_settings(window, 1, CFPreferencesCopyAppValue(CFSTR("BiosPath"), + kCFPreferencesCurrentApplication)); + set_settings(window, 2, CFPreferencesCopyAppValue(CFSTR("CDROMDrive"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 3, CFPreferencesCopyAppValue(CFSTR("CDROMCore"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 4, CFPreferencesCopyAppValue(CFSTR("Region"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 5, CFPreferencesCopyAppValue(CFSTR("VideoCore"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 6, CFPreferencesCopyAppValue(CFSTR("SoundCore"), + kCFPreferencesCurrentApplication)); + set_settings(window, 7, CFPreferencesCopyAppValue(CFSTR("CartPath"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 8, CFPreferencesCopyAppValue(CFSTR("CartType"), + kCFPreferencesCurrentApplication)); + set_settings(window, 9, + CFPreferencesCopyAppValue(CFSTR("BackupRamPath"), + kCFPreferencesCurrentApplication)); + set_settings(window, 10, CFPreferencesCopyAppValue(CFSTR("MpegRomPath"), + kCFPreferencesCurrentApplication)); + set_settings_c(window, 11, CFPreferencesCopyAppValue(CFSTR("AutoFrameSkip"), + kCFPreferencesCurrentApplication)); + + i = 0; + while(PerPadNames[i]) { + set_settings(window, 31 + i, CFPreferencesCopyAppValue( + CFStringCreateWithCString(0, PerPadNames[i], 0), + kCFPreferencesCurrentApplication)); + i++; + } +} + +int load_file_core(char* file, char* addr, int type) +{ + unsigned int adr; + int ret = -1; + if(addr == NULL) + adr = 0; + else + adr = mytoi(addr); + switch(type) { + case 0: + ret = MappedMemoryLoad(file, adr); + break; + case 1: + MappedMemoryLoadExec(file, adr); + ret = 0; + break; + } + return ret; +} + +void load_file(WindowRef window, int type) { + char addrbuf[12]; + char filebuf[256]; + int ret = -1; + CFStringGetCString(get_settings(window, 1), filebuf, 256, kCFStringEncodingUTF8); + CFStringGetCString(get_settings(window, 2), addrbuf, 12, kCFStringEncodingUTF8); + ret = load_file_core(filebuf, addrbuf, type); + (void)(ret); /* We need to do something about bad return values... */ +} + +OSStatus SettingsWindowEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) +{ + OSStatus result = eventNotHandledErr; + + switch (GetEventKind (theEvent)) + { + case kEventWindowClose: + { + WindowRef window; + GetEventParameter(theEvent, kEventParamDirectObject, typeWindowRef, + 0, sizeof(typeWindowRef), 0, &window); + + save_settings(window); + + DisposeWindow(window); + } + result = noErr; + break; + + } + + return (result); +} + +OSStatus BrowseHandler(EventHandlerCallRef h, EventRef event, void* data) { + NavDialogRef dialog; + NavDialogCreationOptions options; + + NavGetDefaultDialogCreationOptions(&options); + NavCreateChooseFileDialog(&options, NULL, NULL, NULL, NULL, + NULL, &dialog); + NavDialogRun(dialog); + + if (NavDialogGetUserAction(dialog) == kNavUserActionChoose) { + NavReplyRecord reply; + FSRef fileAsFSRef; + CFURLRef fileAsCFURLRef = NULL; + CFStringRef s; + + NavDialogGetReply(dialog, &reply); + + AEGetNthPtr(&(reply.selection), 1, typeFSRef, + NULL, NULL, &fileAsFSRef, sizeof(FSRef), NULL); + + NavDisposeReply(&reply); + NavDialogDispose(dialog); + + fileAsCFURLRef = CFURLCreateFromFSRef(NULL, &fileAsFSRef); + s = CFURLCopyFileSystemPath(fileAsCFURLRef, kCFURLPOSIXPathStyle); + + CFShow(s); + + SetControlData(data, kControlEditTextPart, + kControlEditTextCFStringTag, sizeof(CFStringRef), &s); + Draw1Control(data); + } + + return noErr; +} + +OSStatus KeyConfigHandler(EventHandlerCallRef h, EventRef event, void* data) { + UInt32 key; + CFStringRef s; + GetEventParameter(event, kEventParamKeyCode, + typeUInt32, NULL, sizeof(UInt32), NULL, &key); + s = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), key); + SetControlData(data, kControlEditTextPart, + kControlEditTextCFStringTag, sizeof(CFStringRef), &s); + Draw1Control(data); + + return noErr; +} + +void InstallBrowseHandler(WindowRef myWindow, const SInt32 ControllerId, + const SInt32 ControlledId) +{ + EventTypeSpec flist[] = { + { kEventClassControl, kEventControlHit } + }; + ControlID Id; + ControlRef Controller, Controlled; + + Id.signature = 'conf'; + Id.id = ControllerId; + GetControlByID(myWindow, &Id, &Controller); + Id.id = ControlledId; + GetControlByID(myWindow, &Id, &Controlled); + InstallControlEventHandler(Controller, NewEventHandlerUPP(BrowseHandler), + GetEventTypeCount(flist), flist, Controlled, NULL); +} + +WindowRef CreateSettingsWindow() { + + WindowRef myWindow; + IBNibRef nib; + + EventTypeSpec eventList[] = { + { kEventClassWindow, kEventWindowClose } + }; + + CreateNibReference(CFSTR("preferences"), &nib); + CreateWindowFromNib(nib, CFSTR("Dialog"), &myWindow); + + load_settings(myWindow); + + InstallTabHandler(myWindow); + + { + int i; + ControlRef control, controlled; + ControlID id; + EventTypeSpec elist[] = { + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp } + }; + + id.signature = 'conf'; + i = 0; + while(PerPadNames[i]) { + id.id = 31 + i; + GetControlByID(myWindow, &id, &control); + + InstallControlEventHandler(control, NewEventHandlerUPP(KeyConfigHandler), + GetEventTypeCount(elist), elist, control, NULL); + i++; + } + + InstallBrowseHandler(myWindow, 50, 1); /* BIOS */ + InstallBrowseHandler(myWindow, 51, 2); /* CDROM */ + InstallBrowseHandler(myWindow, 52, 7); /* Cartridge ROM */ + InstallBrowseHandler(myWindow, 53, 9); /* Memory */ + InstallBrowseHandler(myWindow, 54, 10); /* MPEG ROM */ + } + + ShowWindow(myWindow); + + InstallWindowEventHandler(myWindow, + NewEventHandlerUPP (SettingsWindowEventHandler), + GetEventTypeCount(eventList), + eventList, myWindow, NULL); + + return myWindow; +} + +OSStatus LoadWindowEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) +{ + OSStatus result = eventNotHandledErr; + switch (GetEventKind (theEvent)) + { + case kEventWindowClose: + { + WindowRef window; + GetEventParameter(theEvent, kEventParamDirectObject, typeWindowRef, + 0, sizeof(typeWindowRef), 0, &window); + + load_file(window, loadtype); + + DisposeWindow(window); + } + result = noErr; + break; + } + return (result); +} + +WindowRef CreateLoadWindow(int type) { + WindowRef myWindow; + IBNibRef nib; + int* hack; + EventTypeSpec eventList[] = { + { kEventClassWindow, kEventWindowClose } + }; + + CreateNibReference(CFSTR("load_dialog"), &nib); + CreateWindowFromNib(nib, CFSTR("Dialog"), &myWindow); + + InstallTabHandler(myWindow); + hack = malloc(sizeof(int)); + loadtype = type; + InstallBrowseHandler(myWindow, 50, 1); /* File */ + ShowWindow(myWindow); + + InstallWindowEventHandler(myWindow, + NewEventHandlerUPP (LoadWindowEventHandler), + GetEventTypeCount(eventList), + eventList, myWindow, NULL); + return myWindow; +} diff --git a/yabause/src/carbon/settings.h b/yabause/src/carbon/settings.h new file mode 100644 index 0000000000..37f6522522 --- /dev/null +++ b/yabause/src/carbon/settings.h @@ -0,0 +1,49 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "../yabause.h" +#include "../peripheral.h" +#include "../sh2core.h" +#include "../sh2int.h" +#include "../vidogl.h" +#include "../vidsoft.h" +#include "../scsp.h" +#include "../sndsdl.h" +#include "../cdbase.h" +#include "../cs0.h" +#include "../m68kcore.h" + +extern yabauseinit_struct yinit; + +WindowRef CreateSettingsWindow(); + +typedef struct _YuiAction YuiAction; + +/* +struct _YuiAction { + UInt32 key; + const char * name; + void (*press)(void); + void (*release)(void); +}; + +extern YuiAction key_config[]; +*/ diff --git a/yabause/src/cd-freebsd.c b/yabause/src/cd-freebsd.c new file mode 100644 index 0000000000..9e451b20a0 --- /dev/null +++ b/yabause/src/cd-freebsd.c @@ -0,0 +1,166 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2004-2006 Guillaume Duhamel + Copyright 2005 Joost Peters + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdbase.h" +#include "debug.h" + +static int FreeBSDCDInit(const char *); +static void FreeBSDCDDeInit(void); +static s32 FreeBSDCDReadTOC(u32 *); +static int FreeBSDCDGetStatus(void); +static int FreeBSDCDReadSectorFAD(u32, void *); +static void FreeBSDCDReadAheadFAD(u32); + +CDInterface ArchCD = { + CDCORE_ARCH, + "FreeBSD CD Drive", + FreeBSDCDInit, + FreeBSDCDDeInit, + FreeBSDCDGetStatus, + FreeBSDCDReadTOC, + FreeBSDCDReadSectorFAD, + FreeBSDCDReadAheadFAD, +}; + +static int hCDROM; + +static int FreeBSDCDInit(const char * cdrom_name) { + int bsize = 2352; + + if ((hCDROM = open(cdrom_name, O_RDONLY | O_NONBLOCK)) == -1) { + LOG("CDInit (%s) failed\n", cdrom_name); + return -1; + } + + if (ioctl (hCDROM, CDRIOCSETBLOCKSIZE, &bsize) == -1) { + return -1; + } + + LOG("CDInit (%s) OK\n", cdrom_name); + return 0; +} + +static void FreeBSDCDDeInit(void) { + if (hCDROM != -1) { + close(hCDROM); + } + + LOG("CDDeInit OK\n"); +} + + +static s32 FreeBSDCDReadTOC(u32 * TOC) +{ + int success; + struct ioc_toc_header ctTOC; + struct ioc_read_toc_single_entry ctTOCent; + int i, j; + int add150 = 0; + + ctTOCent.address_format = CD_LBA_FORMAT; + + if (hCDROM != -1) + { + memset(TOC, 0xFF, 0xCC * 2); + memset(&ctTOC, 0xFF, sizeof(struct ioc_toc_header)); + + if (ioctl(hCDROM, CDIOREADTOCHEADER, &ctTOC) == -1) { + return 0; + } + + ctTOCent.track = ctTOC.starting_track; + ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); + if (ctTOCent.entry.addr.lba == 0) add150 = 150; + TOC[0] = ((ctTOCent.entry.control << 28) | + (ctTOCent.entry.addr_type << 24) | + ctTOCent.entry.addr.lba + add150); + + // convert TOC to saturn format + for (i = ctTOC.starting_track + 1; i <= ctTOC.ending_track; i++) + { + ctTOCent.track = i; + ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); + TOC[i - 1] = (ctTOCent.entry.control << 28) | + (ctTOCent.entry.addr_type << 24) | + (ctTOCent.entry.addr.lba + add150); + } + + // Do First, Last, and Lead out sections here + + ctTOCent.track = ctTOC.starting_track; + ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); + TOC[99] = (ctTOCent.entry.control << 28) | + (ctTOCent.entry.addr_type << 24) | + (ctTOC.starting_track << 16); + + ctTOCent.track = ctTOC.ending_track; + ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); + TOC[100] = (ctTOCent.entry.control << 28) | + (ctTOCent.entry.addr_type << 24) | + (ctTOC.starting_track << 16); + + ctTOCent.track = 0xAA; + ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); + TOC[101] = (ctTOCent.entry.control << 28) | + (ctTOCent.entry.addr_type << 24) | + (ctTOCent.entry.addr.lba + add150); + + return (0xCC * 2); + } + + return 0; +} + +static int FreeBSDCDGetStatus(void) { + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // see ../windows/cd.cc for more info + + return 0; +} + +static int FreeBSDCDReadSectorFAD(u32 FAD, void *buffer) { + if (hCDROM != -1) + { + lseek(hCDROM, (FAD - 150) * 2352, SEEK_SET); + read(hCDROM, buffer, 2352); + + return 1; + } + + return 0; +} + +static void FreeBSDCDReadAheadFAD(UNUSED u32 FAD) +{ + // No-op +} diff --git a/yabause/src/cd-linux.c b/yabause/src/cd-linux.c new file mode 100644 index 0000000000..e488c64ce8 --- /dev/null +++ b/yabause/src/cd-linux.c @@ -0,0 +1,182 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2004-2006 Guillaume Duhamel + Copyright 2005 Joost Peters + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX_CDROM_H_IS_BROKEN +#include +#endif + +#include "cdbase.h" +#include "debug.h" + +static int LinuxCDInit(const char *); +static void LinuxCDDeInit(void); +static s32 LinuxCDReadTOC(u32 *); +static int LinuxCDGetStatus(void); +static int LinuxCDReadSectorFAD(u32, void *); +static void LinuxCDReadAheadFAD(u32); + +CDInterface ArchCD = { + CDCORE_ARCH, + "Linux CD Drive", + LinuxCDInit, + LinuxCDDeInit, + LinuxCDGetStatus, + LinuxCDReadTOC, + LinuxCDReadSectorFAD, + LinuxCDReadAheadFAD, +}; + +static int hCDROM; + +static int LinuxCDInit(const char * cdrom_name) { + if ((hCDROM = open(cdrom_name, O_RDONLY | O_NONBLOCK)) == -1) { + return -1; + } + LOG("CDInit (%s) OK\n", cdrom_name); + return 0; +} + +static void LinuxCDDeInit(void) { + if (hCDROM != -1) { + close(hCDROM); + } + + LOG("CDDeInit OK\n"); +} + + +static s32 LinuxCDReadTOC(u32 * TOC) +{ + struct cdrom_tochdr ctTOC; + struct cdrom_tocentry ctTOCent; + int i; + int add150 = 0; + + ctTOCent.cdte_format = CDROM_LBA; + + if (hCDROM != -1) + { + memset(TOC, 0xFF, 0xCC * 2); + memset(&ctTOC, 0xFF, sizeof(struct cdrom_tochdr)); + + if (ioctl(hCDROM, CDROMREADTOCHDR, &ctTOC) == -1) { + return 0; + } + + ctTOCent.cdte_track = ctTOC.cdth_trk0; + ioctl(hCDROM, CDROMREADTOCENTRY, &ctTOCent); + if (ctTOCent.cdte_addr.lba == 0) add150 = 150; + TOC[0] = ((ctTOCent.cdte_ctrl << 28) | + (ctTOCent.cdte_adr << 24) | + (ctTOCent.cdte_addr.lba + add150)); + + // convert TOC to saturn format + for (i = ctTOC.cdth_trk0 + 1; i <= ctTOC.cdth_trk1; i++) + { + ctTOCent.cdte_track = i; + ioctl(hCDROM, CDROMREADTOCENTRY, &ctTOCent); + TOC[i - 1] = (ctTOCent.cdte_ctrl << 28) | + (ctTOCent.cdte_adr << 24) | + (ctTOCent.cdte_addr.lba + add150); + } + + // Do First, Last, and Lead out sections here + + ctTOCent.cdte_track = ctTOC.cdth_trk0; + ioctl(hCDROM, CDROMREADTOCENTRY, &ctTOCent); + TOC[99] = (ctTOCent.cdte_ctrl << 28) | + (ctTOCent.cdte_adr << 24) | + (ctTOC.cdth_trk0 << 16); + + ctTOCent.cdte_track = ctTOC.cdth_trk1; + ioctl(hCDROM, CDROMREADTOCENTRY, &ctTOCent); + TOC[100] = (ctTOCent.cdte_ctrl << 28) | + (ctTOCent.cdte_adr << 24) | + (ctTOC.cdth_trk1 << 16); + + ctTOCent.cdte_track = CDROM_LEADOUT; + ioctl(hCDROM, CDROMREADTOCENTRY, &ctTOCent); + TOC[101] = (ctTOCent.cdte_ctrl << 28) | + (ctTOCent.cdte_adr << 24) | + (ctTOCent.cdte_addr.lba + add150); + + return (0xCC * 2); + } + + return 0; +} + +static int LinuxCDGetStatus(void) { + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // see ../windows/cd.cc for more info + + int ret = ioctl(hCDROM, CDROM_DRIVE_STATUS, CDSL_CURRENT); + switch(ret) { + case CDS_DISC_OK: + return 0; + case CDS_NO_DISC: + return 2; + case CDS_TRAY_OPEN: + return 3; + } + + // guess it's ok to say there's no disc here... + return 2; +} + +static int LinuxCDReadSectorFAD(u32 FAD, void *buffer) { + union { + struct cdrom_msf msf; + char bigbuf[2352]; + } position; + + if (hCDROM != -1) + { + position.msf.cdmsf_min0 = FAD / 4500; + position.msf.cdmsf_sec0 = (FAD % 4500) / 75; + position.msf.cdmsf_frame0 = FAD % 75; + + if (ioctl(hCDROM, CDROMREADRAW, &position) == -1) { + return 0; + } + + memcpy(buffer, position.bigbuf, 2352); + + return 1; + } + + return 0; +} + +static void LinuxCDReadAheadFAD(UNUSED u32 FAD) +{ + // No-op +} diff --git a/yabause/src/cd-macosx.c b/yabause/src/cd-macosx.c new file mode 100644 index 0000000000..67916553b4 --- /dev/null +++ b/yabause/src/cd-macosx.c @@ -0,0 +1,227 @@ +/* Copyright 2004-2005 Lucas Newman + Copyright 2004-2005 Theo Berkau + Copyright 2005 Weston Yager + Copyright 2006-2008 Guillaume Duhamel + Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdbase.h" + +static int MacOSXCDInit(const char *); +static void MacOSXCDDeInit(void); +static int MacOSXCDGetStatus(void); +static s32 MacOSXCDReadTOC(u32 *); +static int MacOSXCDReadSectorFAD(u32, void *); +static void MacOSXCDReadAheadFAD(u32); + +CDInterface ArchCD = { +CDCORE_ARCH, +"MacOSX CD Drive", +MacOSXCDInit, +MacOSXCDDeInit, +MacOSXCDGetStatus, +MacOSXCDReadTOC, +MacOSXCDReadSectorFAD, +MacOSXCDReadAheadFAD, +}; + +static int hCDROM; + +static int MacOSXCDInit(const char * useless_for_now) +{ + CFMutableDictionaryRef classesToMatch; + io_iterator_t mediaIterator; + io_object_t media; + char cdrom_name[ MAXPATHLEN ]; + + classesToMatch = IOServiceMatching(kIOCDMediaClass); + CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue); + IOServiceGetMatchingServices(kIOMasterPortDefault, + classesToMatch, &mediaIterator); + + media = IOIteratorNext(mediaIterator); + + if(media) + { + CFTypeRef path; + + path = IORegistryEntryCreateCFProperty(media, + CFSTR(kIOBSDNameKey), + kCFAllocatorDefault, 0); + + if (path) + { + size_t length; + + strcpy(cdrom_name, _PATH_DEV); + strcat(cdrom_name, "r"); + length = strlen(cdrom_name); + + CFStringGetCString(path, cdrom_name + length, + MAXPATHLEN - length, kCFStringEncodingUTF8); + + CFRelease(path); + } + IOObjectRelease(media); + } + + if ((hCDROM = open(cdrom_name, O_RDONLY)) == -1) + { + return -1; + } + + return 0; +} + +static void MacOSXCDDeInit(void) +{ + if (hCDROM != -1) + { + close(hCDROM); + } +} + +static CDTOC * GetTOCFromCDPath(void) +{ + CFMutableDictionaryRef classesToMatch; + io_iterator_t mediaIterator; + io_object_t media; + CDTOC * TOC; + + classesToMatch = IOServiceMatching(kIOCDMediaClass); + CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue); + IOServiceGetMatchingServices(kIOMasterPortDefault, + classesToMatch, &mediaIterator); + + media = IOIteratorNext(mediaIterator); + + if(media) + { + CFDataRef TOCData = IORegistryEntryCreateCFProperty(media, CFSTR(kIOCDMediaTOCKey), kCFAllocatorDefault, 0); + TOC = malloc(CFDataGetLength(TOCData)); + CFDataGetBytes(TOCData,CFRangeMake(0,CFDataGetLength(TOCData)),(UInt8 *)TOC); + CFRelease(TOCData); + IOObjectRelease(media); + } + + return TOC; +} + +static s32 MacOSXCDReadTOC(u32 *TOC) +{ + int add150 = 150, tracks = 0; + u_char track; + int i, fad = 0; + CDTOC *cdTOC = GetTOCFromCDPath(); + CDTOCDescriptor *pTrackDescriptors; + pTrackDescriptors = cdTOC->descriptors; + + memset(TOC, 0xFF, 0xCC * 2); + + /* Convert TOC to Saturn format */ + for( i = 3; i < CDTOCGetDescriptorCount(cdTOC); i++ ) { + track = pTrackDescriptors[i].point; + fad = CDConvertMSFToLBA(pTrackDescriptors[i].p) + add150; + if ((track > 99) || (track < 1)) + continue; + TOC[i-3] = (pTrackDescriptors[i].control << 28 | pTrackDescriptors[i].adr << 24 | fad); + tracks++; + } + + /* First */ + TOC[99] = pTrackDescriptors[0].control << 28 | pTrackDescriptors[0].adr << 24 | 1 << 16; + /* Last */ + TOC[100] = pTrackDescriptors[1].control << 28 | pTrackDescriptors[1].adr << 24 | tracks << 16; + /* Leadout */ + TOC[101] = pTrackDescriptors[2].control << 28 | pTrackDescriptors[2].adr << 24 | CDConvertMSFToLBA(pTrackDescriptors[2].p) + add150; + + //free(cdTOC); Looks like this is not need, will look into that. + return (0xCC * 2); +} + +static int MacOSXCDGetStatus(void) +{ + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // see ../windows/cd.cc for more info + + //Return that disc is present and spinning. 2 and 3 can't happen in the mac port, i don't understand what "not spinning" is supposed to say + return 0; +} + +static int MacOSXCDReadSectorFAD(u32 FAD, void *buffer) +{ + const int blockSize = 2352; +#ifdef CRAB_REWRITE + const int cacheBlocks = 32; + static u8 cache[blockSize * cacheBlocks]; + static u32 cacheFAD = 0xFFFFFF00; +#endif + + if (hCDROM != -1) + { +#ifdef CRAB_REWRITE + /* See if the block we are looking for is in the cache already... */ + if(FAD < cacheFAD || FAD >= cacheFAD + cacheBlocks) { + /* Cache miss, read some blocks from the cd, then we'll hit the + cache below. */ + if(!pread(hCDROM, cache, blockSize * cacheBlocks, + (FAD - 150) * blockSize)) { + return 0; + } + + cacheFAD = FAD; + } + + /* Cache hit, copy the block out. */ + memcpy(buffer, cache + (blockSize * (FAD - cacheFAD)), blockSize); + return 1; +#else + if (pread(hCDROM, buffer, blockSize, (FAD - 150) * blockSize)) + return true; +#endif + } + + return false; +} + +static void MacOSXCDReadAheadFAD(UNUSED u32 FAD) +{ + // No-op +} diff --git a/yabause/src/cd-netbsd.c b/yabause/src/cd-netbsd.c new file mode 100644 index 0000000000..98a871eb9f --- /dev/null +++ b/yabause/src/cd-netbsd.c @@ -0,0 +1,167 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2004-2005 Guillaume Duhamel + Copyright 2005 Joost Peters + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "cdbase.h" +#include "debug.h" + +static int NetBSDCDInit(const char *); +static void NetBSDCDDeInit(void); +static s32 NetBSDCDReadTOC(u32 *); +static int NetBSDCDGetStatus(void); +static int NetBSDCDReadSectorFAD(u32, void *); +static void NetBSDCDReadAheadFAD(u32); + +CDInterface ArchCD = { + CDCORE_ARCH, + "NetBSD CD Drive", + NetBSDCDInit, + NetBSDCDDeInit, + NetBSDCDGetStatus, + NetBSDCDReadTOC, + NetBSDCDReadSectorFAD, + NetBSDCDReadAheadFAD, +}; + +static int hCDROM; + +static int NetBSDCDInit(const char * cdrom_name) { + if ((hCDROM = open(cdrom_name, O_RDONLY | O_NONBLOCK)) == -1) { + LOG("CDInit (%s) failed\n", cdrom_name); + return -1; + } + + LOG("CDInit (%s) OK\n", cdrom_name); + return 0; +} + +static void NetBSDCDDeInit(void) { + if (hCDROM != -1) { + close(hCDROM); + } + + LOG("CDDeInit OK\n"); +} + + +static s32 NetBSDCDReadTOC(u32 * TOC) +{ + int success; + struct ioc_toc_header ctTOC; + struct ioc_read_toc_entry ctTOCent; + struct cd_toc_entry data; + int i, j; + int add150 = 0; + + ctTOCent.address_format = CD_LBA_FORMAT; + ctTOCent.data_len = sizeof (struct cd_toc_entry); + ctTOCent.data = &data; + + if (hCDROM != -1) + { + memset(TOC, 0xFF, 0xCC * 2); + memset(&ctTOC, 0xFF, sizeof(struct ioc_toc_header)); + + if (ioctl(hCDROM, CDIOREADTOCHEADER, &ctTOC) == -1) { + return 0; + } + + ctTOCent.starting_track = ctTOC.starting_track; + ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); + if (ctTOCent.data->addr.lba == 0) add150 = 150; + TOC[0] = ((ctTOCent.data->control << 28) | + (ctTOCent.data->addr_type << 24) | + ctTOCent.data->addr.lba + add150); + + // convert TOC to saturn format + for (i = ctTOC.starting_track + 1; i <= ctTOC.ending_track; i++) + { + ctTOCent.starting_track = i; + ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); + TOC[i - 1] = (ctTOCent.data->control << 28) | + (ctTOCent.data->addr_type << 24) | + (ctTOCent.data->addr.lba + add150); + } + + // Do First, Last, and Lead out sections here + + ctTOCent.starting_track = ctTOC.starting_track; + ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); + TOC[99] = (ctTOCent.data->control << 28) | + (ctTOCent.data->addr_type << 24) | + (ctTOC.starting_track << 16); + + ctTOCent.starting_track = ctTOC.ending_track; + ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); + TOC[100] = (ctTOCent.data->control << 28) | + (ctTOCent.data->addr_type << 24) | + (ctTOC.starting_track << 16); + + ctTOCent.starting_track = 0xAA; + ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); + TOC[101] = (ctTOCent.data->control << 28) | + (ctTOCent.data->addr_type << 24) | + (ctTOCent.data->addr.lba + add150); + + return (0xCC * 2); + } + + return 0; +} + +static int NetBSDCDGetStatus(void) { + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // see ../windows/cd.cc for more info + + return 0; +} + +static int NetBSDCDReadSectorFAD(u32 FAD, void *buffer) { + static const s8 syncHdr[] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if (hCDROM != -1) + { + memcpy(buffer, syncHdr, sizeof (syncHdr)); + lseek(hCDROM, (FAD - 150) * 2048, SEEK_SET); + read(hCDROM, (char *)buffer + 0x10, 2048); + + return 1; + } + + return 0; +} + +static void NetBSDCDReadAheadFAD(UNUSED u32 FAD) +{ + // No-op +} diff --git a/yabause/src/cd-windows.c b/yabause/src/cd-windows.c new file mode 100644 index 0000000000..e80ab7b326 --- /dev/null +++ b/yabause/src/cd-windows.c @@ -0,0 +1,364 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2005 Joost Peters + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "windows/cd.h" + +////////////////////////////////////////////////////////////////////////////// + +static HANDLE hCDROM; +static SCSI_PASS_THROUGH_DIRECT sptd; +static int KillCDThread=0; +static HANDLE thread_handle=INVALID_HANDLE_VALUE; +static int drivestatus=0; +static DWORD thread_id; + +CDInterface ArchCD = { +CDCORE_SPTI, +"Windows SPTI Driver", +SPTICDInit, +SPTICDDeInit, +SPTICDGetStatus, +SPTICDReadTOC, +SPTICDReadSectorFAD, +SPTICDReadAheadFAD, +}; + +////////////////////////////////////////////////////////////////////////////// +// SPTI Interface +////////////////////////////////////////////////////////////////////////////// + +DWORD WINAPI __stdcall SPTICDThread(void *b); + +int SPTICDInit(const char *cdrom_name) { + char pipe_name[7]; + + sprintf(pipe_name, "\\\\.\\?:"); + pipe_name[4] = cdrom_name[0]; + + if ((hCDROM = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) + { + return -1; + } + + // Setup a separate thread for handling SPTI commands(that way the emulation + // code doesn't have to wait for a response) + KillCDThread=0; + thread_handle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) SPTICDThread,(void *) &KillCDThread,0,&thread_id); + + // Set it to highest priority to avoid breaks +// SetThreadPriority(thread_handle,THREAD_PRIORITY_HIGHEST); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void SPTICDDeInit() { + if (thread_handle != INVALID_HANDLE_VALUE) + { + // Set the flag telling it to stop + KillCDThread=1; + if (WaitForSingleObject(thread_handle,INFINITE) == WAIT_TIMEOUT) + { + // Couldn't close thread cleanly + TerminateThread(thread_handle,0); + } + CloseHandle(thread_handle); + thread_handle = INVALID_HANDLE_VALUE; + } + + CloseHandle(hCDROM); +} + +////////////////////////////////////////////////////////////////////////////// + +DWORD WINAPI __stdcall SPTICDThread(void *b) +{ + while (KillCDThread != 1) + { + DWORD dwBytesReturned; + unsigned char statusbuf[8]; + BOOL success; + + // Check to see if we have any work to do(for now, let's just do a drive + // status check once a second) + + sptd.Length=sizeof(sptd); + sptd.PathId=0; // + sptd.TargetId=0; // Don't need these, they're automatically generated + sptd.Lun=0; // + sptd.CdbLength=12; + sptd.SenseInfoLength=0; // No sense data + sptd.DataIn=SCSI_IOCTL_DATA_IN; + sptd.TimeOutValue=60; // may need to be changed + sptd.DataBuffer=(PVOID)&(statusbuf); + sptd.SenseInfoOffset=0; + sptd.DataTransferLength=8; // may need to change this + + sptd.Cdb[0]=0xBD; // CDB12 code + sptd.Cdb[1]=0; // Reserved + sptd.Cdb[2]=0; // Reserved + sptd.Cdb[3]=0; // Reserved + sptd.Cdb[4]=0; // Reserved + sptd.Cdb[5]=0; // Reserved + sptd.Cdb[6]=0; // Reserved + sptd.Cdb[7]=0; // Reserved + sptd.Cdb[8]=0; // Allocation Length(byte 1) + sptd.Cdb[9]=8; // Allocation Length(byte 2) (may have to change this) + sptd.Cdb[10]=0; // Reserved + sptd.Cdb[11]=0; // Control + sptd.Cdb[12]=0; + sptd.Cdb[13]=0; + sptd.Cdb[14]=0; + sptd.Cdb[15]=0; + + success=DeviceIoControl(hCDROM, IOCTL_SCSI_PASS_THROUGH_DIRECT, + (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), + NULL, 0, &dwBytesReturned, NULL); + + if (success) + { + // Figure out drive status + + // Is door open? + if (statusbuf[1] & 0x10) + drivestatus = 3; + else + { + // Ok, so the door is closed, now is there a disc there? + success = DeviceIoControl(hCDROM, IOCTL_STORAGE_CHECK_VERIFY, + NULL, 0, NULL, 0, &dwBytesReturned, NULL); + if (!success) + drivestatus = 2; + else + drivestatus = 0; + } + } + else + drivestatus = 2; + + Sleep(1000); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int SPTICDGetStatus() { + // This function is called periodically to see what the status of the + // drive is. + // + // Should return one of the following values: + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // + // If you really don't want to bother too much with this function, just + // return status 0. Though it is kind of nice when the bios's cd player, + // etc. recognizes when you've ejected the tray and popped in another disc. +/* + DWORD dwBytesReturned; + unsigned char statusbuf[8]; + BOOL success; + + sptd.Length=sizeof(sptd); + sptd.PathId=0; // + sptd.TargetId=0; // Don't need these, they're automatically generated + sptd.Lun=0; // + sptd.CdbLength=12; + sptd.SenseInfoLength=0; // No sense data + sptd.DataIn=SCSI_IOCTL_DATA_IN; + sptd.TimeOutValue=60; // may need to be changed + sptd.DataBuffer=(PVOID)&(statusbuf); + sptd.SenseInfoOffset=0; + sptd.DataTransferLength=8; // may need to change this + + sptd.Cdb[0]=0xBD; // CDB12 code + sptd.Cdb[1]=0; // Reserved + sptd.Cdb[2]=0; // Reserved + sptd.Cdb[3]=0; // Reserved + sptd.Cdb[4]=0; // Reserved + sptd.Cdb[5]=0; // Reserved + sptd.Cdb[6]=0; // Reserved + sptd.Cdb[7]=0; // Reserved + sptd.Cdb[8]=0; // Allocation Length(byte 1) + sptd.Cdb[9]=8; // Allocation Length(byte 2) (may have to change this) + sptd.Cdb[10]=0; // Reserved + sptd.Cdb[11]=0; // Control + sptd.Cdb[12]=0; + sptd.Cdb[13]=0; + sptd.Cdb[14]=0; + sptd.Cdb[15]=0; + + success=DeviceIoControl(hCDROM, IOCTL_SCSI_PASS_THROUGH_DIRECT, + (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), + NULL, 0, &dwBytesReturned, NULL); + + if (success) + { + // Figure out drive status + + // Is door open? + if (statusbuf[1] & 0x10) return 3; + + // Ok, so the door is closed, now is there a disc there? + success = DeviceIoControl(hCDROM, IOCTL_STORAGE_CHECK_VERIFY, + NULL, 0, NULL, 0, &dwBytesReturned, NULL); + if (!success) + return 2; + + return 0; + } + + return 2; +*/ + return drivestatus; +} + +////////////////////////////////////////////////////////////////////////////// + +#define MSF_TO_FAD(m,s,f) ((m * 4500) + (s * 75) + f) + +s32 SPTICDReadTOC(u32 *TOC) { +// FILE *debugfp; + CDROM_TOC ctTOC; + DWORD dwNotUsed; + int i; + + if (hCDROM != INVALID_HANDLE_VALUE) + { + memset(TOC, 0xFF, 0xCC * 2); + memset(&ctTOC, 0xFF, sizeof(CDROM_TOC)); + + if (DeviceIoControl (hCDROM, IOCTL_CDROM_READ_TOC, + NULL, 0, &ctTOC, sizeof(ctTOC), + &dwNotUsed, NULL) == 0) + { + return 0; + } + + // convert TOC to saturn format + for (i = 0; i < ctTOC.LastTrack; i++) + { + TOC[i] = (ctTOC.TrackData[i].Control << 28) | + (ctTOC.TrackData[i].Adr << 24) | + MSF_TO_FAD(ctTOC.TrackData[i].Address[1], ctTOC.TrackData[i].Address[2], ctTOC.TrackData[i].Address[3]); + } + + // Do First, Last, and Lead out sections here + + TOC[99] = (ctTOC.TrackData[0].Control << 28) | + (ctTOC.TrackData[0].Adr << 24) | + (ctTOC.FirstTrack << 16); + + TOC[100] = (ctTOC.TrackData[ctTOC.LastTrack - 1].Control << 28) | + (ctTOC.TrackData[ctTOC.LastTrack - 1].Adr << 24) | + (ctTOC.LastTrack << 16); + + TOC[101] = (ctTOC.TrackData[ctTOC.LastTrack].Control << 28) | + (ctTOC.TrackData[ctTOC.LastTrack].Adr << 24) | + MSF_TO_FAD(ctTOC.TrackData[ctTOC.LastTrack].Address[1], ctTOC.TrackData[ctTOC.LastTrack].Address[2], ctTOC.TrackData[ctTOC.LastTrack].Address[3]); + +// debugfp = fopen("cot2toc.bin", "wb"); +// fwrite((void *)TOC, 1, 0xCC * 2, debugfp); +// fclose(debugfp); + + return (0xCC * 2); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int SPTICDReadSectorFAD(u32 FAD, void *buffer) { + // This function is supposed to read exactly 1 -RAW- 2352-byte sector at + // the specified FAD address to buffer. Should return true if successful, + // false if there was an error. + // + // Special Note: To convert from FAD to LBA/LSN, minus 150. + // + // The whole process needed to be changed since I need more control over + // sector detection, etc. Not to mention it means less work for the porter + // since they only have to implement raw sector reading as opposed to + // implementing mode 1, mode 2 form1/form2, -and- raw sector reading. + + DWORD dwBytesReturned; + BOOL success; + + sptd.Length=sizeof(sptd); + sptd.PathId=0; // + sptd.TargetId=0; // Don't need these, they're automatically generated + sptd.Lun=0; // + sptd.CdbLength=12; + sptd.SenseInfoLength=0; // No sense data + sptd.DataIn=SCSI_IOCTL_DATA_IN; + sptd.TimeOutValue=60; // may need to be changed + sptd.DataBuffer=(PVOID)buffer; + sptd.SenseInfoOffset=0; + sptd.DataTransferLength=2352; + + sptd.Cdb[0]=0xBE; // CDB12 code + sptd.Cdb[1]=0; // Sector Type, RELADR + FAD -= 150; + sptd.Cdb[2]=(unsigned char)((FAD & 0xFF000000) >> 24); // lba(byte 1) + sptd.Cdb[3]=(unsigned char)((FAD & 0x00FF0000) >> 16); // lba(byte 2) + sptd.Cdb[4]=(unsigned char)((FAD & 0x0000FF00) >> 8); // lba(byte 3) + sptd.Cdb[5]=(unsigned char)(FAD & 0x000000FF); // lba(byte 4) + sptd.Cdb[6]=0; // number of sectors(byte 1) + sptd.Cdb[7]=0; // number of sectors(byte 2) + sptd.Cdb[8]=1; // number of sectors(byte 3) + sptd.Cdb[9]=0xF8; // Sync + All Headers + User data + EDC/ECC + sptd.Cdb[10]=0; + sptd.Cdb[11]=0; + sptd.Cdb[12]=0; + sptd.Cdb[13]=0; + sptd.Cdb[14]=0; + sptd.Cdb[15]=0; + + success=DeviceIoControl(hCDROM, + IOCTL_SCSI_PASS_THROUGH_DIRECT, + (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), + NULL, 0, + &dwBytesReturned, + NULL); + + if (!success) + { + return 0; + } + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +void SPTICDReadAheadFAD(u32 FAD) { + // No-op +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/cdbase.c b/yabause/src/cdbase.c new file mode 100644 index 0000000000..3e3d0dc8ac --- /dev/null +++ b/yabause/src/cdbase.c @@ -0,0 +1,512 @@ +/* Copyright 2004-2008 Theo Berkau + Copyright 2005 Joost Peters + Copyright 2005-2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "cdbase.h" +#include "error.h" +#include "debug.h" + +////////////////////////////////////////////////////////////////////////////// + +// Contains the Dummy and ISO CD Interfaces + +static int DummyCDInit(const char *); +static void DummyCDDeInit(void); +static int DummyCDGetStatus(void); +static s32 DummyCDReadTOC(u32 *); +static int DummyCDReadSectorFAD(u32, void *); +static void DummyCDReadAheadFAD(u32); + +CDInterface DummyCD = { +CDCORE_DUMMY, +"Dummy CD Drive", +DummyCDInit, +DummyCDDeInit, +DummyCDGetStatus, +DummyCDReadTOC, +DummyCDReadSectorFAD, +DummyCDReadAheadFAD, +}; + +static int ISOCDInit(const char *); +static void ISOCDDeInit(void); +static int ISOCDGetStatus(void); +static s32 ISOCDReadTOC(u32 *); +static int ISOCDReadSectorFAD(u32, void *); +static void ISOCDReadAheadFAD(u32); + +CDInterface ISOCD = { +CDCORE_ISO, +"ISO-File Virtual Drive", +ISOCDInit, +ISOCDDeInit, +ISOCDGetStatus, +ISOCDReadTOC, +ISOCDReadSectorFAD, +ISOCDReadAheadFAD, +}; + +////////////////////////////////////////////////////////////////////////////// +// Dummy Interface +////////////////////////////////////////////////////////////////////////////// + +static int DummyCDInit(UNUSED const char *cdrom_name) +{ + // Initialization function. cdrom_name can be whatever you want it to + // be. Obviously with some ports(e.g. the dreamcast port) you probably + // won't even use it. + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void DummyCDDeInit(void) +{ + // Cleanup function. Enough said. +} + +////////////////////////////////////////////////////////////////////////////// + +static int DummyCDGetStatus(void) +{ + // This function is called periodically to see what the status of the + // drive is. + // + // Should return one of the following values: + // 0 - CD Present, disc spinning + // 1 - CD Present, disc not spinning + // 2 - CD not present + // 3 - Tray open + // + // If you really don't want to bother too much with this function, just + // return status 0. Though it is kind of nice when the bios's cd + // player, etc. recognizes when you've ejected the tray and popped in + // another disc. + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static s32 DummyCDReadTOC(UNUSED u32 *TOC) +{ + // The format of TOC is as follows: + // TOC[0] - TOC[98] are meant for tracks 1-99. Each entry has the + // following format: + // bits 0 - 23: track FAD address + // bits 24 - 27: track addr + // bits 28 - 31: track ctrl + // + // Any Unused tracks should be set to 0xFFFFFFFF + // + // TOC[99] - Point A0 information + // Uses the following format: + // bits 0 - 7: PFRAME(should always be 0) + // bits 7 - 15: PSEC(Program area format: 0x00 - CDDA or CDROM, + // 0x10 - CDI, 0x20 - CDROM-XA) + // bits 16 - 23: PMIN(first track's number) + // bits 24 - 27: first track's addr + // bits 28 - 31: first track's ctrl + // + // TOC[100] - Point A1 information + // Uses the following format: + // bits 0 - 7: PFRAME(should always be 0) + // bits 7 - 15: PSEC(should always be 0) + // bits 16 - 23: PMIN(last track's number) + // bits 24 - 27: last track's addr + // bits 28 - 31: last track's ctrl + // + // TOC[101] - Point A2 information + // Uses the following format: + // bits 0 - 23: leadout FAD address + // bits 24 - 27: leadout's addr + // bits 28 - 31: leadout's ctrl + // + // Special Note: To convert from LBA/LSN to FAD, add 150. + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int DummyCDReadSectorFAD(UNUSED u32 FAD, void * buffer) +{ + // This function is supposed to read exactly 1 -RAW- 2352-byte sector + // at the specified FAD address to buffer. Should return true if + // successful, false if there was an error. + // + // Special Note: To convert from FAD to LBA/LSN, minus 150. + // + // The whole process needed to be changed since I need more control + // over sector detection, etc. Not to mention it means less work for + // the porter since they only have to implement raw sector reading as + // opposed to implementing mode 1, mode 2 form1/form2, -and- raw + // sector reading. + + memset(buffer, 0, 2352); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void DummyCDReadAheadFAD(UNUSED u32 FAD) +{ + // This function is called to tell the driver which sector (FAD + // address) is expected to be read next. If the driver supports + // read-ahead, it should start reading the given sector in the + // background while the emulation continues, so that when the + // sector is actually read with ReadSectorFAD() it'll be available + // immediately. (Note that there's no guarantee this sector will + // actually be requested--the emulated CD might be stopped before + // the sector is read, for example.) + // + // This function should NOT block. If the driver can't perform + // asynchronous reads (or you just don't want to bother handling + // them), make this function a no-op and just read sectors + // normally. +} + +////////////////////////////////////////////////////////////////////////////// +// ISO Interface +////////////////////////////////////////////////////////////////////////////// + +static const s8 syncHdr[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; +static FILE *isofile=NULL; +static int isofilesize=0; +static int bytesPerSector = 0; +static int isbincue = 0; +static u32 isoTOC[102]; +static struct +{ + u32 fadstart; + u32 fileoffset; +} isooffsettbl[100]; + +#define MSF_TO_FAD(m,s,f) ((m * 4500) + (s * 75) + f) + +////////////////////////////////////////////////////////////////////////////// + +static int InitBinCue(const char *cuefilename) +{ + u32 size; + char *tempbuffer, *tempbuffer2; + unsigned int tracknum; + unsigned int indexnum, min, sec, frame; + unsigned int pregap=0; + char *p, *p2; + + fseek(isofile, 0, SEEK_END); + size = ftell(isofile); + fseek(isofile, 0, SEEK_SET); + + // Allocate buffer with enough space for reading cue + if ((tempbuffer = (char *)calloc(size, 1)) == NULL) + return -1; + + // Skip image filename + if (fscanf(isofile, "FILE \"%*[^\"]\" %*s\r\n") == EOF) + { + free(tempbuffer); + return -1; + } + + // Time to generate TOC + for (;;) + { + // Retrieve a line in cue + if (fscanf(isofile, "%s", tempbuffer) == EOF) + break; + + // Figure out what it is + if (strncmp(tempbuffer, "TRACK", 5) == 0) + { + // Handle accordingly + if (fscanf(isofile, "%d %[^\r\n]\r\n", &tracknum, tempbuffer) == EOF) + break; + + if (strncmp(tempbuffer, "MODE1", 5) == 0 || + strncmp(tempbuffer, "MODE2", 5) == 0) + { + // Figure out the track sector size + bytesPerSector = atoi(tempbuffer + 6); + + // Update toc entry + isoTOC[tracknum-1] = 0x41000000; + } + else if (strncmp(tempbuffer, "AUDIO", 5) == 0) + { + // fix me + // Update toc entry + isoTOC[tracknum-1] = 0x01000000; + } + } + else if (strncmp(tempbuffer, "INDEX", 5) == 0) + { + // Handle accordingly + + if (fscanf(isofile, "%d %d:%d:%d\r\n", &indexnum, &min, &sec, &frame) == EOF) + break; + + if (indexnum == 1) + { + // Update toc entry + isoTOC[tracknum-1] = (isoTOC[tracknum-1] & 0xFF000000) | (MSF_TO_FAD(min, sec, frame) + pregap + 150); + + isooffsettbl[tracknum-1].fadstart = MSF_TO_FAD(min, sec, frame) + pregap + 150; + isooffsettbl[tracknum-1].fileoffset = pregap + 150; + } + } + else if (strncmp(tempbuffer, "PREGAP", 5) == 0) + { + if (fscanf(isofile, "%d:%d:%d\r\n", &min, &sec, &frame) == EOF) + break; + + pregap += MSF_TO_FAD(min, sec, frame); + } + else if (strncmp(tempbuffer, "POSTGAP", 5) == 0) + { + if (fscanf(isofile, "%d:%d:%d\r\n", &min, &sec, &frame) == EOF) + break; + } + } + + // Go back, retrieve image filename + fseek(isofile, 0, SEEK_SET); + fscanf(isofile, "FILE \"%[^\"]\" %*s\r\n", tempbuffer); + fclose(isofile); + + // Now go and open up the image file, figure out its size, etc. + if ((isofile = fopen(tempbuffer, "rb")) == NULL) + { + // Ok, exact path didn't work. Let's trim the path and try opening the + // file from the same directory as the cue. + + // find the start of filename + p = tempbuffer; + + for (;;) + { + if (strcspn(p, "/\\") == strlen(p)) + break; + + p += strcspn(p, "/\\") + 1; + } + + // append directory of cue file with bin filename + if ((tempbuffer2 = (char *)calloc(strlen(cuefilename) + strlen(p) + 1, 1)) == NULL) + { + free(tempbuffer); + return -1; + } + + // find end of path + p2 = (char *)cuefilename; + + for (;;) + { + if (strcspn(p2, "/\\") == strlen(p2)) + break; + p2 += strcspn(p2, "/\\") + 1; + } + + // Make sure there was at least some kind of path, otherwise our + // second check is pretty useless + if (cuefilename == p2 && tempbuffer == p) + { + free(tempbuffer); + free(tempbuffer2); + return -1; + } + + strncpy(tempbuffer2, cuefilename, p2 - cuefilename); + strcat(tempbuffer2, p); + + // Let's give it another try + isofile = fopen(tempbuffer2, "rb"); + free(tempbuffer2); + + if (isofile == NULL) + { + YabSetError(YAB_ERR_FILENOTFOUND, tempbuffer); + free(tempbuffer); + return -1; + } + } + + // buffer is no longer needed + free(tempbuffer); + + fseek(isofile, 0, SEEK_END); + isofilesize = ftell(isofile); + fseek(isofile, 0, SEEK_SET); + + // Now then, generate rest of TOC + isoTOC[99] = (isoTOC[0] & 0xFF000000) | 0x010000; + isoTOC[100] = (isoTOC[tracknum - 1] & 0xFF000000) | (tracknum << 16); + isoTOC[101] = (isoTOC[tracknum - 1] & 0xFF000000) | ((isofilesize / bytesPerSector) + pregap + 150); + + isooffsettbl[tracknum].fileoffset = 0; + isooffsettbl[tracknum].fadstart = 0xFFFFFFFF; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int ISOCDInit(const char * iso) { + char header[6]; + + memset(isoTOC, 0xFF, 0xCC * 2); + + if (!iso) + return -1; + + if (!(isofile = fopen(iso, "rb"))) + { + YabSetError(YAB_ERR_FILENOTFOUND, (char *)iso); + return -1; + } + + fread((void *)header, 1, 6, isofile); + + // Figure out what kind of image format we're dealing with + if (strncmp(header, "FILE \"", 6) == 0) + { + // It's a BIN/CUE + isbincue = 1; + + // Generate TOC for bin file + if (InitBinCue(iso) != 0) + { + if (isofile) + free(isofile); + return -1; + } + } + else + { + // Assume it's an ISO file + isbincue = 0; + + fseek(isofile, 0, SEEK_END); + isofilesize = ftell(isofile); + + if (0 == (isofilesize % 2048)) + bytesPerSector = 2048; + else if (0 == (isofilesize % 2352)) + bytesPerSector = 2352; + else + { + YabSetError(YAB_ERR_OTHER, "Unsupported CD image!\n"); + + return -1; + } + + // Generate TOC + isoTOC[0] = 0x41000096; + isoTOC[99] = 0x41010000; + isoTOC[100] = 0x41010000; + isoTOC[101] = (0x41 << 24) | (isofilesize / bytesPerSector); //this isn't fully correct, but it does the job for now. + + isooffsettbl[0].fileoffset = 150; + isooffsettbl[0].fadstart = 150; + isooffsettbl[1].fileoffset = 0; + isooffsettbl[1].fadstart = 0xFFFFFFFF; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void ISOCDDeInit(void) { + if (isofile) + { + fclose(isofile); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static int ISOCDGetStatus(void) { + return isofile != NULL ? 0 : 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static s32 ISOCDReadTOC(u32 * TOC) { + memcpy(TOC, isoTOC, 0xCC * 2); + + return (0xCC * 2); +} + +////////////////////////////////////////////////////////////////////////////// + +static int ISOCDReadSectorFAD(u32 FAD, void *buffer) { + int sector; + int i; + + assert(isofile); + + memset(buffer, 0, 2352); + + for (i = 1; i < 100; i++) + { + if (FAD < isooffsettbl[i].fadstart) + { + sector = FAD - isooffsettbl[i-1].fileoffset; + break; + } + } + if (i == 100) { + CDLOG("Warning: Sector not found in track list"); + return 0; + } + + if ((sector * bytesPerSector) >= isofilesize) { + CDLOG("Warning: Trying to read beyond end of CD image! (sector: %d)\n", sector); + return 0; + } + + fseek(isofile, sector * bytesPerSector, SEEK_SET); + + if (2048 == bytesPerSector) { + memcpy(buffer, syncHdr, 12); + fread((char *)buffer + 0x10, bytesPerSector, 1, isofile); + } else { //2352 + fread(buffer, bytesPerSector, 1, isofile); + } + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void ISOCDReadAheadFAD(UNUSED u32 FAD) +{ + // No-op +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/cdbase.h b/yabause/src/cdbase.h new file mode 100644 index 0000000000..624716b4ba --- /dev/null +++ b/yabause/src/cdbase.h @@ -0,0 +1,51 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2005 Joost Peters + Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CDBASE_H +#define CDBASE_H + +#include +#include "core.h" + +#define CDCORE_DEFAULT -1 +#define CDCORE_DUMMY 0 +#define CDCORE_ISO 1 +#define CDCORE_ARCH 2 + +typedef struct +{ + int id; + const char *Name; + int (*Init)(const char *); + void (*DeInit)(void); + int (*GetStatus)(void); + s32 (*ReadTOC)(u32 *TOC); + int (*ReadSectorFAD)(u32 FAD, void *buffer); + void (*ReadAheadFAD)(u32 FAD); +} CDInterface; + +extern CDInterface DummyCD; + +extern CDInterface ISOCD; + +extern CDInterface ArchCD; + +#endif diff --git a/yabause/src/cheat.c b/yabause/src/cheat.c new file mode 100644 index 0000000000..b3e001a715 --- /dev/null +++ b/yabause/src/cheat.c @@ -0,0 +1,383 @@ +/* Copyright 2007-2008 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "cheat.h" +#include "memory.h" +#include "sh2core.h" + +cheatlist_struct *cheatlist=NULL; +int numcheats=0; +int cheatsize; + +#define DoubleWordSwap(x) x = (((x & 0xFF000000) >> 24) + \ + ((x & 0x00FF0000) >> 8) + \ + ((x & 0x0000FF00) << 8) + \ + ((x & 0x000000FF) << 24)); + +////////////////////////////////////////////////////////////////////////////// + +int CheatInit(void) +{ + cheatsize = 10; + if ((cheatlist = (cheatlist_struct *)calloc(cheatsize, sizeof(cheatlist_struct))) == NULL) + return -1; + cheatlist[0].type = CHEATTYPE_NONE; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void CheatDeInit(void) +{ + if (cheatlist) + free(cheatlist); + cheatlist = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatAddCode(int type, u32 addr, u32 val) +{ + cheatlist[numcheats].type = type; + cheatlist[numcheats].addr = addr; + cheatlist[numcheats].val = val; + cheatlist[numcheats].desc = NULL; + cheatlist[numcheats].enable = 1; + numcheats++; + + // Make sure we still have room + if (numcheats >= cheatsize) + { + cheatlist = realloc(cheatlist, sizeof(cheatlist_struct) * (cheatsize * 2)); + cheatsize *= 2; + } + + cheatlist[numcheats].type = CHEATTYPE_NONE; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatAddARCode(const char *code) +{ + unsigned long addr; + unsigned short val; + sscanf(code, "%08lX %04hX", &addr, &val); + switch (addr >> 28) + { + case 0x0: + // One time word write(fix me) + return -1; + case 0x1: + return CheatAddCode(CHEATTYPE_WORDWRITE, addr & 0x0FFFFFFF, val); + case 0x3: + return CheatAddCode(CHEATTYPE_BYTEWRITE, addr & 0x0FFFFFFF, val); + case 0xD: + return CheatAddCode(CHEATTYPE_ENABLE, addr & 0x0FFFFFFF, val); + default: return -1; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int FindCheat(int type, u32 addr, u32 val) +{ + int i; + + for (i = 0; i < numcheats; i++) + { + if (cheatlist[i].type == type && + cheatlist[i].addr == addr && + cheatlist[i].val == val) + return i; + } + + return -1; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatChangeDescription(int type, u32 addr, u32 val, char *desc) +{ + int i; + + if ((i = FindCheat(type, addr, val)) == -1) + // There is no matches, so let's bail + return -1; + + return CheatChangeDescriptionByIndex(i, desc); +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatChangeDescriptionByIndex(int i, char *desc) +{ + // Free old description(if existing) + if (cheatlist[i].desc) + free(cheatlist[i].desc); + + cheatlist[i].desc = strdup(desc); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatRemoveCode(int type, u32 addr, u32 val) +{ + int i; + + if ((i = FindCheat(type, addr, val)) == -1) + // There is no matches, so let's bail + return -1; + + return CheatRemoveCodeByIndex(i); +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatRemoveCodeByIndex(int i) +{ + // If there's a description, free the memory. + if (cheatlist[i].desc) + { + free(cheatlist[i].desc); + cheatlist[i].desc = NULL; + } + + // Move all entries one forward + for (; i < numcheats-1; i++) + memcpy(&cheatlist[i], &cheatlist[i+1], sizeof(cheatlist_struct)); + + numcheats--; + + // Set the last one to type none + cheatlist[numcheats].type = CHEATTYPE_NONE; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatRemoveARCode(const char *code) +{ + unsigned long addr; + unsigned short val; + sscanf(code, "%08lX %04hX", &addr, &val); + + switch (addr >> 28) + { + case 0x1: + return CheatRemoveCode(CHEATTYPE_WORDWRITE, addr & 0x0FFFFFFF, val); + case 0x3: + return CheatRemoveCode(CHEATTYPE_BYTEWRITE, addr & 0x0FFFFFFF, val); + case 0xD: + return CheatRemoveCode(CHEATTYPE_ENABLE, addr & 0x0FFFFFFF, val); + default: return -1; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void CheatClearCodes(void) +{ + while (numcheats > 0) + CheatRemoveCodeByIndex(numcheats-1); +} + +////////////////////////////////////////////////////////////////////////////// + +void CheatEnableCode(int index) +{ + cheatlist[index].enable = 1; +} + +////////////////////////////////////////////////////////////////////////////// + +void CheatDisableCode(int index) +{ + cheatlist[index].enable = 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void CheatDoPatches(void) +{ + int i; + + for (i = 0; ; i++) + { + switch (cheatlist[i].type) + { + case CHEATTYPE_NONE: + return; + case CHEATTYPE_ENABLE: + if (cheatlist[i].enable == 0) + continue; + if (MappedMemoryReadWord(cheatlist[i].addr) != cheatlist[i].val) + return; + break; + case CHEATTYPE_BYTEWRITE: + if (cheatlist[i].enable == 0) + continue; + MappedMemoryWriteByte(cheatlist[i].addr, (u8)cheatlist[i].val); + SH2WriteNotify(cheatlist[i].addr, 1); + break; + case CHEATTYPE_WORDWRITE: + if (cheatlist[i].enable == 0) + continue; + MappedMemoryWriteWord(cheatlist[i].addr, (u16)cheatlist[i].val); + SH2WriteNotify(cheatlist[i].addr, 2); + break; + case CHEATTYPE_LONGWRITE: + if (cheatlist[i].enable == 0) + continue; + MappedMemoryWriteLong(cheatlist[i].addr, cheatlist[i].val); + SH2WriteNotify(cheatlist[i].addr, 4); + break; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +cheatlist_struct *CheatGetList(int *cheatnum) +{ + if (cheatnum == NULL) + return NULL; + + *cheatnum = numcheats; + return cheatlist; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatSave(const char *filename) +{ + FILE *fp; + int i; + int num; + IOCheck_struct check; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "wb")) == NULL) + return -1; + + fprintf(fp, "YCHT"); + num = numcheats; +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(num); +#endif + ywrite(&check, (void *)&num, sizeof(int), 1, fp); + + for(i = 0; i < numcheats; i++) + { + u8 descsize; + cheatlist_struct cheat; + + memcpy(&cheat, &cheatlist[i], sizeof(cheatlist_struct)); +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(cheat.type); + DoubleWordSwap(cheat.addr); + DoubleWordSwap(cheat.val); + DoubleWordSwap(cheat.enable); +#endif + ywrite(&check, (void *)&cheat.type, sizeof(int), 1, fp); + ywrite(&check, (void *)&cheat.addr, sizeof(u32), 1, fp); + ywrite(&check, (void *)&cheat.val, sizeof(u32), 1, fp); + descsize = (u8)strlen(cheatlist[i].desc)+1; + ywrite(&check, (void *)&descsize, sizeof(u8), 1, fp); + ywrite(&check, (void *)cheatlist[i].desc, sizeof(char), descsize, fp); + ywrite(&check, (void *)&cheat.enable, sizeof(int), 1, fp); + } + + fclose (fp); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int CheatLoad(const char *filename) +{ + FILE *fp; + int i; + char id[4]; + char desc[256]; + IOCheck_struct check; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + yread(&check, (void *)id, 1, 4, fp); + if (strncmp(id, "YCHT", 4) != 0) + { + fclose(fp); + return -2; + } + + CheatClearCodes(); + + yread(&check, (void *)&numcheats, sizeof(int), 1, fp); +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(numcheats); +#endif + + if (numcheats >= cheatsize) + { + cheatlist = realloc(cheatlist, sizeof(cheatlist_struct) * (cheatsize * 2)); + memset((void *)cheatlist, 0, sizeof(cheatlist_struct) * (cheatsize * 2)); + cheatsize *= 2; + } + + for(i = 0; i < numcheats; i++) + { + u8 descsize; + + yread(&check, (void *)&cheatlist[i].type, sizeof(int), 1, fp); + yread(&check, (void *)&cheatlist[i].addr, sizeof(u32), 1, fp); + yread(&check, (void *)&cheatlist[i].val, sizeof(u32), 1, fp); + yread(&check, (void *)&descsize, sizeof(u8), 1, fp); + yread(&check, (void *)desc, sizeof(char), descsize, fp); + CheatChangeDescriptionByIndex(i, desc); + yread(&check, (void *)&cheatlist[i].enable, sizeof(int), 1, fp); +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(cheatlist[i].type); + DoubleWordSwap(cheatlist[i].addr); + DoubleWordSwap(cheatlist[i].val); + DoubleWordSwap(cheatlist[i].enable); +#endif + } + + fclose (fp); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/cheat.h b/yabause/src/cheat.h new file mode 100644 index 0000000000..009b2b4449 --- /dev/null +++ b/yabause/src/cheat.h @@ -0,0 +1,60 @@ +/* Copyright 2007 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CHEAT_H +#define CHEAT_H + +#include "core.h" + +enum +{ + CHEATTYPE_NONE=0, + CHEATTYPE_ENABLE, + CHEATTYPE_BYTEWRITE, + CHEATTYPE_WORDWRITE, + CHEATTYPE_LONGWRITE +}; + +typedef struct +{ + int type; + u32 addr; + u32 val; + char *desc; + int enable; +} cheatlist_struct; + +int CheatInit(void); +void CheatDeInit(void); +int CheatAddCode(int type, u32 addr, u32 val); +int CheatAddARCode(const char *code); +int CheatChangeDescription(int type, u32 addr, u32 val, char *desc); +int CheatChangeDescriptionByIndex(int i, char *desc); +int CheatRemoveCode(int type, u32 addr, u32 val); +int CheatRemoveCodeByIndex(int i); +int CheatRemoveARCode(const char *code); +void CheatClearCodes(void); +void CheatEnableCode(int index); +void CheatDisableCode(int index); +void CheatDoPatches(void); +cheatlist_struct *CheatGetList(int *cheatnum); +int CheatSave(const char *filename); +int CheatLoad(const char *filename); + +#endif diff --git a/yabause/src/cocoa/CMake-Info.plist b/yabause/src/cocoa/CMake-Info.plist new file mode 100644 index 0000000000..c045bd04b9 --- /dev/null +++ b/yabause/src/cocoa/CMake-Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Yabause + CFBundleIconFile + Yabause.icns + CFBundleIdentifier + org.yabause.yabause.cocoa + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Yabause + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleShortVersionString + ${YAB_VERSION} + CFBundleVersion + + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSHumanReadableCopyright + Copyright © Yabause Team + + diff --git a/yabause/src/cocoa/CMakeLists.txt b/yabause/src/cocoa/CMakeLists.txt new file mode 100644 index 0000000000..4bfab44374 --- /dev/null +++ b/yabause/src/cocoa/CMakeLists.txt @@ -0,0 +1,73 @@ +project(yabause-cocoa) + +yab_port_start() + +find_library(AUDIO_LIBRARY AudioUnit) + +if (NOT AUDIO_LIBRARY) + return() +endif() + +set(yabause_cocoa_SOURCES + main.m + PerCocoa.m + YabauseButtonFormatter.m + YabauseController.m + YabauseGLView.m + YabausePrefsController.m + vidgcd.c + vidgcd.h +) + +set(yabause_cocoa_XIBS English.lproj/MainMenu) + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/..") + +add_executable(yabause-cocoa MACOSX_BUNDLE ${yabause_cocoa_SOURCES}) +target_link_libraries(yabause-cocoa yabause ${YABAUSE_LIBRARIES} + ${AUDIO_LIBRARY}) + +yab_port_stop() +set_target_properties(yabause-cocoa PROPERTIES OUTPUT_NAME Yabause) + +set_target_properties(yabause-cocoa PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/CMake-Info.plist) + +find_program(IBTOOL ibtool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin") +if (${IBTOOL} STREQUAL "IBTOOL-NOTFOUND") + message(SEND_ERROR "ibtool can not be found and is needed to compile the .xib files. It should have been installed with + the Apple developer tools. The default system paths were searched in addition to ${OSX_DEVELOPER_ROOT}/usr/bin") +endif() + +add_custom_command( + TARGET yabause-cocoa PRE_BUILD + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/MacOS) + +add_custom_command( + TARGET yabause-cocoa PRE_BUILD + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/Resources/English.lproj) + +foreach(xib ${yabause_cocoa_XIBS}) + add_custom_command( + TARGET yabause-cocoa POST_BUILD + COMMAND ${IBTOOL} --errors --warnings --notices + --output-format human-readable-text + --compile ${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/Resources/${xib}.nib + ${CMAKE_CURRENT_SOURCE_DIR}/${xib}.xib + COMMENT "Compiling ${CMAKE_CURRENT_SOURCE_DIR}/${xib}.xib") + +endforeach() + +add_custom_command( + TARGET yabause-cocoa POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Yabause.icns ${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/Resources) + +add_custom_command( + TARGET yabause-cocoa POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/resources/controller.png ${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/Contents/Resources) + +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Yabause.app/" DESTINATION "Yabause.app" USE_SOURCE_PERMISSIONS) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../AUTHORS DESTINATION ".") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../ChangeLog DESTINATION ".") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../COPYING DESTINATION ".") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../README DESTINATION ".") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../README.MAC DESTINATION ".") diff --git a/yabause/src/cocoa/English.lproj/InfoPlist.strings b/yabause/src/cocoa/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000..b92732c79e --- /dev/null +++ b/yabause/src/cocoa/English.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/yabause/src/cocoa/English.lproj/MainMenu.xib b/yabause/src/cocoa/English.lproj/MainMenu.xib new file mode 100644 index 0000000000..ac87a6f20e --- /dev/null +++ b/yabause/src/cocoa/English.lproj/MainMenu.xib @@ -0,0 +1,6148 @@ + + + + 1060 + 12C54 + 851 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 851 + + + YES + + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + Main Menu + + YES + + + Yabause + + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + Yabause + + YES + + + About Yabause + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Services + + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 2147483647 + + + + + + Hide Yabause + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Quit Yabause + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 2147483647 + + + submenuAction: + + File + + YES + + + Run CD-ROM + o + 1048576 + 2147483647 + + + + + + Run Image + O + 1048576 + 2147483647 + + + + + + Run BIOS + o + 1572864 + 2147483647 + + + + + + + + + Emulation + + 2147483647 + + + submenuAction: + + Emulation + + YES + + + Pause + p + 1048576 + 2147483647 + + + + + + Reset + r + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Enable Frameskip + + 2147483647 + 1 + + + + + + + + + View + + 2147483647 + + + submenuAction: + + View + + YES + + + Show Framerate + + 2147483647 + + + 7 + + + + Fullscreen + f + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Layers + + 2147483647 + + + submenuAction: + + Layers + + YES + + + VDP1 + + 2147483647 + 1 + + + 1 + + + + NBG0 + + 2147483647 + 1 + + + 2 + + + + NBG1 + + 2147483647 + 1 + + + 3 + + + + NBG2 + + 2147483647 + 1 + + + 4 + + + + NBG3 + + 2147483647 + 1 + + + 5 + + + + RBG0 + + 2147483647 + 1 + + + 6 + + + + + + + + + + Window + + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bring All to Front + + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + YES + + + Yabause Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 271 + 2 + {{168, 237}, {320, 224}} + 1685585920 + Yabause + NSWindow + + + {1.7976931348623157e+308, 1.7976931348623157e+308} + {320, 224} + + + 274 + + YES + + + 274 + {320, 224} + + YabauseGLView + + + {320, 224} + + {{0, 0}, {1440, 878}} + {320, 246} + {1.7976931348623157e+308, 1.7976931348623157e+308} + YES + + + YabauseController + + + 7 + 2 + {{162, 79}, {484, 400}} + -193462272 + Preferences + NSPanel + + + {1.7976931348623157e+308, 1.7976931348623157e+308} + + + 256 + + YES + + + 12 + {{13, 10}, {458, 384}} + + + YES + + 1 + + + 256 + + YES + + + 12 + + YES + + + 274 + + YES + + + 268 + {{18, 36}, {296, 22}} + + YES + + -1804599231 + 272630784 + + + LucidaGrande + 13 + 1040 + + Not Set + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + 3 + MAA + + + + NO + + + + 268 + {{316, 30}, {96, 32}} + + YES + + 67108864 + 134217728 + Browse + + + -2038284288 + 129 + + + 200 + 25 + + NO + + + + 268 + {{16, 12}, {163, 18}} + + YES + + -2080374784 + 0 + Enable BIOS Emulation + + + 1211912448 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + NO + + + {{1, 1}, {424, 68}} + + + + {{6, 254}, {426, 84}} + + {0, 0} + + 67108864 + 0 + BIOS + + LucidaGrande + 11 + 3088 + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + + 12 + + YES + + + 274 + + YES + + + 268 + {{10, 10}, {262, 26}} + + YES + + -2076180416 + 2048 + + + 109199360 + 129 + + + 400 + 75 + + + Auto-detect + + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Japan (NTSC) + + 2147483647 + + + _popUpItemAction: + 1 + + + + + Asia (NTSC) + + 2147483647 + + + _popUpItemAction: + 2 + + + + + North America (NTSC) + + 2147483647 + + + _popUpItemAction: + 4 + + + + + Central/South America (NTSC) + + 2147483647 + + + _popUpItemAction: + 5 + + + + + Korea (NTSC) + + 2147483647 + + + _popUpItemAction: + 6 + + + + + Asia (PAL) + + 2147483647 + + + _popUpItemAction: + 10 + + + + + Europe/Australia (PAL) + + 2147483647 + + + _popUpItemAction: + 12 + + + + + Central/South America (PAL) + + 2147483647 + + + _popUpItemAction: + 13 + + + + + + 1 + YES + YES + 2 + + NO + + + {{1, 1}, {424, 44}} + + + + {{6, 124}, {426, 60}} + + {0, 0} + + 67108864 + 0 + Region + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + + 12 + + YES + + + 274 + + YES + + + 268 + {{18, 14}, {296, 22}} + + YES + + -1804599231 + 272630784 + + + Not Set + + YES + + + + NO + + + + 268 + {{316, 8}, {96, 32}} + + YES + + 67108864 + 134217728 + Browse + + + -2038284288 + 129 + + + 200 + 25 + + NO + + + {{1, 1}, {424, 46}} + + + + {{6, 188}, {426, 62}} + + {0, 0} + + 67108864 + 0 + MPEG ROM + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + {{10, 33}, {438, 338}} + + + General + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + + + 2 + + + 256 + + YES + + + 12 + + YES + + + 256 + + YES + + + 268 + {{15, 10}, {262, 26}} + + YES + + -2076180416 + 2048 + + + 109199360 + 129 + + + 400 + 75 + + + Software Video Core + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 2 + + + YES + + OtherViews + + YES + + + OpenGL Hardware Video Core + + 1048576 + 2147483647 + + + _popUpItemAction: + 1 + + + + + + Grand Central Dispatch Software Core + + 2147483647 + + + _popUpItemAction: + 10 + + + + + Disable Video + + 2147483647 + + + _popUpItemAction: + + + + + + 1 + 1 + YES + YES + 2 + + NO + + + {{1, 1}, {424, 44}} + + + + {{6, 278}, {426, 60}} + + {0, 0} + + 67108864 + 0 + Video Core + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + + 12 + + YES + + + 256 + + YES + + + 268 + {{15, 10}, {262, 26}} + + YES + + -2076180416 + 2048 + + + 109199360 + 129 + + + 400 + 75 + + + Core Audio Sound Core + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 3 + + + YES + + OtherViews + + YES + + + + Disable Sound + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + + 1 + YES + YES + 2 + + NO + + + {{1, 1}, {424, 44}} + + + + {{6, 214}, {426, 60}} + + {0, 0} + + 67108864 + 0 + Sound Core + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + {{10, 33}, {438, 338}} + + Video/Sound + + + + + Item 2 + + + 256 + + YES + + + 12 + + YES + + + 256 + + YES + + + 268 + {{18, 14}, {296, 22}} + + YES + + -1804599231 + 272630784 + + + Not Set + + YES + + + + NO + + + + 268 + {{316, 8}, {96, 32}} + + YES + + 67108864 + 134217728 + Browse + + + -2038284288 + 129 + + + 200 + 25 + + NO + + + {{1, 1}, {424, 46}} + + + + {{6, 276}, {426, 62}} + + {0, 0} + + 67108864 + 0 + Internal Memory (BRAM) + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + + 12 + + YES + + + 256 + + YES + + + 268 + {{15, 52}, {262, 26}} + + YES + + -2076180416 + 2048 + + + 109199360 + 129 + + + 400 + 75 + + + None + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Pro Action Replay + + 1048576 + 2147483647 + + + _popUpItemAction: + 1 + + + + + Backup RAM (4 Mbit) + + 1048576 + 2147483647 + + + _popUpItemAction: + 2 + + + + + Backup RAM (8 Mbit) + + 2147483647 + + + _popUpItemAction: + 3 + + + + + Backup RAM (16 Mbit) + + 2147483647 + + + _popUpItemAction: + 4 + + + + + Backup RAM (32 Mbit) + + 2147483647 + + + _popUpItemAction: + 5 + + + + + DRAM (8 Mbit) + + 2147483647 + + + _popUpItemAction: + 6 + + + + + DRAM (32 Mbit) + + 2147483647 + + + _popUpItemAction: + 7 + + + + + Netlink + + 2147483647 + + + _popUpItemAction: + 8 + + + + + ROM (16 Mbit) + + 2147483647 + + + _popUpItemAction: + 9 + + + + + + 1 + YES + YES + 2 + + NO + + + + 268 + {{18, 14}, {296, 22}} + + YES + + -1267728319 + 272630784 + + + No file required for the selected cartridge + + YES + + + + NO + + + + 268 + {{316, 8}, {96, 32}} + + YES + + 603979776 + 134217728 + Browse + + + -2038284288 + 129 + + + 200 + 25 + + NO + + + {{1, 1}, {424, 86}} + + + + {{6, 170}, {426, 102}} + + {0, 0} + + 67108864 + 0 + Cartridge + + + + 3 + MCAwLjgwMDAwMDAxMTkAA + + + + 1 + 0 + 2 + NO + + + {{10, 33}, {438, 338}} + + Memory + + + + + Item 3 + + + 256 + + YES + + + 12 + {{10, 7}, {418, 324}} + + + YES + + 1 + + + 256 + + YES + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{14, 14}, {370, 264}} + + YES + + 0 + 33554432 + + NSImage + controller + + 0 + 0 + 3 + NO + + NO + YES + + + + 268 + {{292, 177}, {17, 18}} + + 12 + YES + + -2080374784 + 134217728 + Z + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{263, 164}, {17, 18}} + + 11 + YES + + -2080374784 + 134217728 + Y + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{237, 145}, {17, 18}} + + 10 + YES + + -2080374784 + 134217728 + X + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{278, 130}, {17, 18}} + + 8 + YES + + -2080374784 + 134217728 + B + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{314, 145}, {17, 18}} + + 9 + YES + + -2080374784 + 134217728 + C + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{248, 210}, {67, 18}} + + 4 + YES + + -2080374784 + 134217728 + R Trigger + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{102, 162}, {21, 18}} + + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{103, 117}, {21, 18}} + + 2 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{125, 139}, {21, 18}} + + 1 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{81, 139}, {21, 18}} + + 3 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{81, 210}, {65, 18}} + + 5 + YES + + -2080374784 + 134217728 + L Trigger + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{180, 111}, {38, 18}} + + 6 + YES + + -2080374784 + 134217728 + Start + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{248, 111}, {17, 18}} + + 7 + YES + + -2080374784 + 134217728 + A + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + {{10, 33}, {398, 278}} + + + Controller 1 + + + + + 2 + + + 256 + + YES + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{14, 14}, {370, 264}} + + YES + + 0 + 33554432 + + 0 + 0 + 3 + NO + + NO + YES + + + + 268 + {{292, 177}, {17, 18}} + + 25 + YES + + -2080374784 + 134217728 + Z + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{263, 164}, {17, 18}} + + 24 + YES + + -2080374784 + 134217728 + Y + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{237, 145}, {17, 18}} + + 23 + YES + + -2080374784 + 134217728 + X + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{278, 130}, {17, 18}} + + 21 + YES + + -2080374784 + 134217728 + B + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{314, 145}, {17, 18}} + + 22 + YES + + -2080374784 + 134217728 + C + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{248, 210}, {67, 18}} + + 17 + YES + + -2080374784 + 134217728 + R Trigger + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{102, 162}, {21, 18}} + + 13 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{103, 117}, {21, 18}} + + 15 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{125, 139}, {21, 18}} + + 14 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{81, 139}, {21, 18}} + + 16 + YES + + -2080374784 + 134217728 + + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{81, 210}, {65, 18}} + + 18 + YES + + -2080374784 + 134217728 + L Trigger + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{180, 111}, {38, 18}} + + 19 + YES + + -2080374784 + 134217728 + Start + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + + 268 + {{248, 111}, {17, 18}} + + 20 + YES + + -2080374784 + 134217728 + A + + + -2033434624 + 160 + + + 400 + 75 + + NO + + + {{10, 33}, {398, 278}} + + Controller 2 + + + + + + + 0 + YES + YES + + YES + + + + + {{10, 33}, {438, 338}} + + Input + + + + + + + 0 + YES + YES + + YES + + + + + {484, 400} + + + {{0, 0}, {1440, 878}} + {1.7976931348623157e+308, 1.7976931348623157e+308} + YES + + + YabausePrefsController + + + 17 + 2 + {{162, 364}, {220, 115}} + -461897728 + Button Assignment + NSPanel + + + {1.7976931348623157e+308, 1.7976931348623157e+308} + + + 256 + + YES + + + 268 + {{17, 78}, {186, 17}} + + YES + + 68157504 + 138413056 + Current Assignment: + + + + + 6 + System + controlTextColor + + + + NO + + + + 268 + {{110, 12}, {96, 32}} + + YES + + 67108864 + 134217728 + Cancel + + + -2038284288 + 129 + + Gw + 200 + 25 + + NO + + + + 268 + {{14, 12}, {96, 32}} + + YES + + 67108864 + 134217728 + OK + + + -2038284288 + 129 + + DQ + 200 + 25 + + NO + + + + 268 + {{62, 48}, {96, 22}} + + YES + + -1805647807 + 138414080 + + + + YES + + + + NO + + + {220, 115} + + + {{0, 0}, {1440, 878}} + {1.7976931348623157e+308, 1.7976931348623157e+308} + YES + + + YabauseButtonFormatter + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performZoom: + + + + 240 + + + + showHelp: + + + + 360 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + window + + + + 853 + + + + view + + + + 855 + + + + toggleFullscreen: + + + + 858 + + + + toggle: + + + + 875 + + + + toggle: + + + + 876 + + + + toggle: + + + + 877 + + + + toggle: + + + + 878 + + + + toggle: + + + + 879 + + + + toggle: + + + + 880 + + + + toggle: + + + + 881 + + + + toggleFrameskip: + + + + 883 + + + + runCD: + + + + 884 + + + + runBIOS: + + + + 885 + + + + pause: + + + + 893 + + + + reset: + + + + 894 + + + + delegate + + + + 897 + + + + prefsPane + + + + 1048 + + + + showPreferences: + + + + 1049 + + + + region + + + + 1051 + + + + mpegPath + + + + 1052 + + + + biosPath + + + + 1053 + + + + emulateBios + + + + 1054 + + + + videoCore + + + + 1055 + + + + soundCore + + + + 1056 + + + + cartType + + + + 1058 + + + + cartPath + + + + 1059 + + + + cartBrowse + + + + 1060 + + + + regionSelected: + + + + 1061 + + + + videoCoreSelected: + + + + 1062 + + + + soundCoreSelected: + + + + 1063 + + + + cartSelected: + + + + 1064 + + + + prefs + + + + 1065 + + + + bramBrowse: + + + + 1066 + + + + cartBrowse: + + + + 1067 + + + + biosBrowse: + + + + 1068 + + + + mpegBrowse: + + + + 1069 + + + + delegate + + + + 1070 + + + + bramPath + + + + 1072 + + + + frameskip + + + + 1073 + + + + biosToggle: + + + + 1074 + + + + delegate + + + + 1076 + + + + delegate + + + + 1077 + + + + delegate + + + + 1078 + + + + delegate + + + + 1079 + + + + buttonAssignment + + + + 1115 + + + + buttonSelect: + + + + 1120 + + + + prefsPane + + + + 1121 + + + + buttonSetOk: + + + + 1128 + + + + buttonSetCancel: + + + + 1129 + + + + buttonBox + + + + 1134 + + + + formatter + + + + 1136 + + + + delegate + + + + 1137 + + + + buttonSelect: + + + + 1227 + + + + buttonSelect: + + + + 1228 + + + + buttonSelect: + + + + 1229 + + + + buttonSelect: + + + + 1230 + + + + buttonSelect: + + + + 1231 + + + + buttonSelect: + + + + 1232 + + + + buttonSelect: + + + + 1233 + + + + buttonSelect: + + + + 1234 + + + + buttonSelect: + + + + 1235 + + + + buttonSelect: + + + + 1236 + + + + buttonSelect: + + + + 1237 + + + + buttonSelect: + + + + 1238 + + + + buttonSelect: + + + + 1239 + + + + buttonSelect: + + + + 1240 + + + + buttonSelect: + + + + 1241 + + + + buttonSelect: + + + + 1242 + + + + buttonSelect: + + + + 1244 + + + + buttonSelect: + + + + 1245 + + + + buttonSelect: + + + + 1246 + + + + buttonSelect: + + + + 1247 + + + + buttonSelect: + + + + 1248 + + + + buttonSelect: + + + + 1249 + + + + buttonSelect: + + + + 1250 + + + + buttonSelect: + + + + 1251 + + + + buttonSelect: + + + + 1252 + + + + runISO: + + + + 1254 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 103 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + 72 + + + + + 106 + + + YES + + + + + + 111 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 371 + + + + + 843 + + + YES + + + + + + 844 + + + YES + + + + + + 846 + + + + + 847 + + + YES + + + + + + 848 + + + YES + + + + + + + + + 854 + + + + + 882 + + + + + 856 + + + + + 886 + + + YES + + + + + + 887 + + + YES + + + + + + + + + 864 + + + YES + + + + + + 865 + + + YES + + + + + + + + + + + 866 + + + + + 869 + + + + + 870 + + + + + 871 + + + + + 872 + + + + + 873 + + + + + 859 + + + + + 849 + + + + + 889 + + + + + 890 + + + + + 891 + + + + + 892 + + + + + 899 + + + YES + + + + + + 900 + + + YES + + + + + + 901 + + + YES + + + + + + + + + 902 + + + YES + + + + + + 903 + + + YES + + + + + + 904 + + + YES + + + + + + + 905 + + + YES + + + + + + + + 906 + + + YES + + + + + + + + 907 + + + YES + + + + + + 908 + + + + + 909 + + + YES + + + + + + 910 + + + + + 911 + + + YES + + + + + + 912 + + + YES + + + + + + 913 + + + YES + + + + + + 914 + + + YES + + + + + + + + + + + + + + 917 + + + + + 916 + + + + + 915 + + + + + 918 + + + + + 919 + + + + + 920 + + + + + 921 + + + + + 922 + + + + + 923 + + + + + 924 + + + YES + + + + + + 925 + + + + + 926 + + + YES + + + + + + 927 + + + YES + + + + + + 928 + + + YES + + + + + + 929 + + + YES + + + + + + + + + 930 + + + + + 931 + + + + + 933 + + + YES + + + + + + 934 + + + YES + + + + + + 935 + + + YES + + + + + + 936 + + + YES + + + + + + + 937 + + + + + 938 + + + + + 940 + + + + + 941 + + + YES + + + + + + + 942 + + + YES + + + + + + 945 + + + + + 943 + + + YES + + + + + + 944 + + + + + 946 + + + YES + + + + + + 947 + + + YES + + + + + + + 948 + + + YES + + + + + + + 949 + + + YES + + + + + + 952 + + + + + 950 + + + YES + + + + + + 951 + + + + + 953 + + + YES + + + + + + 954 + + + YES + + + + + + 965 + + + YES + + + + + + + 966 + + + YES + + + + + + 967 + + + YES + + + + + + 968 + + + YES + + + + + + + + + + + + + + + + + + + 969 + + + YES + + + + + + + + + + + + + + + + + + + 972 + + + YES + + + + + + 973 + + + + + 974 + + + YES + + + + + + 975 + + + + + 976 + + + YES + + + + + + 977 + + + + + 978 + + + YES + + + + + + 979 + + + + + 980 + + + YES + + + + + + 981 + + + + + 982 + + + YES + + + + + + 983 + + + + + 984 + + + YES + + + + + + 985 + + + + + 986 + + + YES + + + + + + 987 + + + + + 988 + + + YES + + + + + + 989 + + + + + 990 + + + YES + + + + + + 991 + + + + + 992 + + + YES + + + + + + 993 + + + + + 994 + + + YES + + + + + + 995 + + + + + 996 + + + YES + + + + + + 997 + + + + + 998 + + + YES + + + + + + 999 + + + + + 1000 + + + YES + + + + + + 1027 + + + + + 1028 + + + YES + + + + + + + + 1029 + + + YES + + + + + + 1030 + + + YES + + + + + + 1031 + + + YES + + + + + + + + + + + + + + + 1032 + + + + + 1033 + + + + + 1034 + + + + + 1035 + + + + + 1036 + + + + + 1037 + + + + + 1038 + + + + + 1039 + + + + + 1040 + + + + + 1041 + + + + + 1044 + + + YES + + + + + + 1045 + + + YES + + + + + + 1046 + + + + + 1047 + + + + + 1050 + + + + + 1097 + + + + + 1109 + + + YES + + + + + + 1110 + + + YES + + + + + + + + + 1111 + + + YES + + + + + + 1112 + + + + + 1124 + + + YES + + + + + + 1125 + + + + + 1126 + + + YES + + + + + + 1127 + + + + + 1132 + + + YES + + + + + + 1133 + + + + + 1135 + + + + + 1200 + + + YES + + + + + + 1201 + + + YES + + + + + + 1202 + + + YES + + + + + + 1203 + + + YES + + + + + + 1204 + + + YES + + + + + + 1205 + + + YES + + + + + + 1206 + + + YES + + + + + + 1207 + + + YES + + + + + + 1208 + + + YES + + + + + + 1209 + + + YES + + + + + + 1210 + + + YES + + + + + + 1211 + + + YES + + + + + + 1212 + + + YES + + + + + + 1213 + + + + + 1214 + + + + + 1215 + + + + + 1216 + + + + + 1217 + + + + + 1218 + + + + + 1219 + + + + + 1220 + + + + + 1221 + + + + + 1222 + + + + + 1223 + + + + + 1224 + + + + + 1225 + + + + + 1253 + + + + + + + YES + + YES + -3.IBPluginDependency + 1000.IBPluginDependency + 1027.IBPluginDependency + 1028.IBPluginDependency + 1029.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 1030.IBPluginDependency + 1031.IBEditorWindowLastContentRect + 1031.IBPluginDependency + 1032.IBPluginDependency + 1033.IBPluginDependency + 1034.IBPluginDependency + 1035.IBPluginDependency + 1036.IBPluginDependency + 1037.IBPluginDependency + 1038.IBPluginDependency + 1039.IBPluginDependency + 1040.IBPluginDependency + 1041.IBPluginDependency + 1044.IBPluginDependency + 1045.IBPluginDependency + 1046.IBPluginDependency + 1047.IBPluginDependency + 106.IBEditorWindowLastContentRect + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 1097.IBPluginDependency + 1109.IBEditorWindowLastContentRect + 1109.IBPluginDependency + 1109.IBWindowTemplateEditedContentRect + 1109.NSWindowTemplate.visibleAtLaunch + 111.IBPluginDependency + 111.ImportedFromIB2 + 1110.IBPluginDependency + 1111.IBPluginDependency + 1111.IBViewBoundsToFrameTransform + 1112.IBPluginDependency + 1124.IBPluginDependency + 1124.IBViewBoundsToFrameTransform + 1125.IBPluginDependency + 1126.IBPluginDependency + 1126.IBViewBoundsToFrameTransform + 1127.IBPluginDependency + 1132.IBPluginDependency + 1132.IBViewBoundsToFrameTransform + 1133.IBPluginDependency + 1135.IBPluginDependency + 1200.IBPluginDependency + 1200.IBViewBoundsToFrameTransform + 1201.IBPluginDependency + 1201.IBViewBoundsToFrameTransform + 1202.IBPluginDependency + 1202.IBViewBoundsToFrameTransform + 1203.IBPluginDependency + 1203.IBViewBoundsToFrameTransform + 1204.IBPluginDependency + 1204.IBViewBoundsToFrameTransform + 1205.IBPluginDependency + 1205.IBViewBoundsToFrameTransform + 1206.IBPluginDependency + 1206.IBViewBoundsToFrameTransform + 1207.IBPluginDependency + 1207.IBViewBoundsToFrameTransform + 1208.IBPluginDependency + 1208.IBViewBoundsToFrameTransform + 1209.IBPluginDependency + 1209.IBViewBoundsToFrameTransform + 1210.IBPluginDependency + 1210.IBViewBoundsToFrameTransform + 1211.IBPluginDependency + 1211.IBViewBoundsToFrameTransform + 1212.IBPluginDependency + 1212.IBViewBoundsToFrameTransform + 1213.IBPluginDependency + 1214.IBPluginDependency + 1215.IBPluginDependency + 1216.IBPluginDependency + 1217.IBPluginDependency + 1218.IBPluginDependency + 1219.IBPluginDependency + 1220.IBPluginDependency + 1221.IBPluginDependency + 1222.IBPluginDependency + 1223.IBPluginDependency + 1224.IBPluginDependency + 1225.IBPluginDependency + 1253.IBPluginDependency + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 81.IBEditorWindowLastContentRect + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 83.IBPluginDependency + 83.ImportedFromIB2 + 843.IBEditorWindowLastContentRect + 843.IBPluginDependency + 843.IBWindowTemplateEditedContentRect + 843.NSWindowTemplate.visibleAtLaunch + 843.windowTemplate.hasMinSize + 843.windowTemplate.minSize + 844.IBPluginDependency + 846.IBPluginDependency + 847.IBPluginDependency + 848.IBEditorWindowLastContentRect + 848.IBPluginDependency + 849.IBPluginDependency + 856.IBPluginDependency + 859.IBPluginDependency + 864.IBPluginDependency + 865.IBEditorWindowLastContentRect + 865.IBPluginDependency + 866.IBPluginDependency + 869.IBPluginDependency + 870.IBPluginDependency + 871.IBPluginDependency + 872.IBPluginDependency + 873.IBPluginDependency + 882.IBPluginDependency + 886.IBPluginDependency + 887.IBEditorWindowLastContentRect + 887.IBPluginDependency + 889.IBPluginDependency + 890.IBPluginDependency + 891.IBPluginDependency + 892.IBPluginDependency + 899.IBEditorWindowLastContentRect + 899.IBPluginDependency + 899.IBWindowTemplateEditedContentRect + 899.NSWindowTemplate.visibleAtLaunch + 900.IBPluginDependency + 901.IBAttributePlaceholdersKey + 901.IBPluginDependency + 902.IBPluginDependency + 903.IBPluginDependency + 904.IBPluginDependency + 905.IBPluginDependency + 906.IBPluginDependency + 907.IBPluginDependency + 908.IBPluginDependency + 909.IBPluginDependency + 910.IBPluginDependency + 911.IBPluginDependency + 912.IBPluginDependency + 913.IBPluginDependency + 914.IBEditorWindowLastContentRect + 914.IBPluginDependency + 915.IBPluginDependency + 916.IBPluginDependency + 917.IBPluginDependency + 918.IBPluginDependency + 919.IBPluginDependency + 92.IBPluginDependency + 92.ImportedFromIB2 + 920.IBPluginDependency + 921.IBPluginDependency + 922.IBPluginDependency + 923.IBPluginDependency + 924.IBPluginDependency + 925.IBPluginDependency + 926.IBPluginDependency + 927.IBPluginDependency + 928.IBPluginDependency + 929.IBEditorWindowLastContentRect + 929.IBPluginDependency + 930.IBPluginDependency + 931.IBPluginDependency + 933.IBPluginDependency + 934.IBPluginDependency + 935.IBPluginDependency + 936.IBEditorWindowLastContentRect + 936.IBPluginDependency + 937.IBPluginDependency + 938.IBPluginDependency + 940.IBPluginDependency + 941.IBPluginDependency + 942.IBPluginDependency + 943.IBPluginDependency + 944.IBPluginDependency + 945.IBPluginDependency + 946.IBPluginDependency + 947.IBPluginDependency + 948.IBPluginDependency + 949.IBPluginDependency + 950.IBPluginDependency + 951.IBPluginDependency + 952.IBPluginDependency + 953.IBPluginDependency + 954.IBPluginDependency + 965.IBAttributePlaceholdersKey + 965.IBPluginDependency + 965.IBViewBoundsToFrameTransform + 966.IBPluginDependency + 967.IBPluginDependency + 968.IBPluginDependency + 969.IBPluginDependency + 972.IBPluginDependency + 972.IBViewBoundsToFrameTransform + 973.IBPluginDependency + 974.IBPluginDependency + 974.IBViewBoundsToFrameTransform + 975.IBPluginDependency + 976.IBPluginDependency + 976.IBViewBoundsToFrameTransform + 977.IBPluginDependency + 978.IBPluginDependency + 978.IBViewBoundsToFrameTransform + 979.IBPluginDependency + 980.IBPluginDependency + 980.IBViewBoundsToFrameTransform + 981.IBPluginDependency + 982.IBPluginDependency + 982.IBViewBoundsToFrameTransform + 983.IBPluginDependency + 984.IBPluginDependency + 984.IBViewBoundsToFrameTransform + 985.IBPluginDependency + 986.IBPluginDependency + 986.IBViewBoundsToFrameTransform + 987.IBPluginDependency + 988.IBPluginDependency + 988.IBViewBoundsToFrameTransform + 989.IBPluginDependency + 990.IBPluginDependency + 990.IBViewBoundsToFrameTransform + 991.IBPluginDependency + 992.IBPluginDependency + 992.IBViewBoundsToFrameTransform + 993.IBPluginDependency + 994.IBPluginDependency + 994.IBViewBoundsToFrameTransform + 995.IBPluginDependency + 996.IBPluginDependency + 996.IBViewBoundsToFrameTransform + 997.IBPluginDependency + 998.IBPluginDependency + 998.IBViewBoundsToFrameTransform + 999.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + {{572, 78}, {262, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{768, 368}, {163, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{596, 852}, {216, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + {{477, 639}, {220, 115}} + com.apple.InterfaceBuilder.CocoaPlugin + {{477, 639}, {220, 115}} + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABBiAAAwswAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDAwAAwigAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABB0AAAwigAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCkAAAwogAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDkoAAw0EAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDhAAAwzQAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDbgAAwyEAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDi4AAwxIAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDnYAAwyEAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDeQAAw2IAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCzgAAwzIAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABC0AAAwwUAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABC/AAAwxsAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCpAAAwxsAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCpAAAw2IAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDNQAAwv4AAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDeQAAwv4AAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{697, 318}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{428, 391}, {401, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{11, 977}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{439, 208}, {192, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{519, 328}, {181, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{323, 672}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{257, 623}, {320, 224}} + com.apple.InterfaceBuilder.CocoaPlugin + {{257, 623}, {320, 224}} + + + {320, 224} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{561, 318}, {193, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{828, 218}, {90, 123}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{647, 318}, {182, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{329, 203}, {484, 400}} + com.apple.InterfaceBuilder.CocoaPlugin + {{329, 203}, {484, 400}} + + com.apple.InterfaceBuilder.CocoaPlugin + + InitialTabViewItem + + InitialTabViewItem + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{756, 355}, {262, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{398, 411}, {324, 83}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{608, 620}, {262, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + InitialTabViewItem + + InitialTabViewItem + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABBIAAAw6SAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + AUFgAABBYAAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCzAAAwzIAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCzgAAwwUAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABC+gAAwxsAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCogAAwxsAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABCogAAw2IAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDeAAAw2IAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDNAAAwv4AAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDeAAAwv4AAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDiwAAwxIAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDnQAAwyEAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDbQAAwyEAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDg4AAwzQAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDkgAAw0EAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 1254 + + + + YES + + YabauseButtonFormatter + NSFormatter + + IBProjectSource + YabauseButtonFormatter.h + + + + YabauseController + NSObject + + YES + + YES + pause: + reset: + runBIOS: + runCD: + runISO: + showPreferences: + toggle: + toggleFrameskip: + toggleFullscreen: + + + YES + id + id + id + id + id + id + id + id + id + + + + YES + + YES + pause: + reset: + runBIOS: + runCD: + runISO: + showPreferences: + toggle: + toggleFrameskip: + toggleFullscreen: + + + YES + + pause: + id + + + reset: + id + + + runBIOS: + id + + + runCD: + id + + + runISO: + id + + + showPreferences: + id + + + toggle: + id + + + toggleFrameskip: + id + + + toggleFullscreen: + id + + + + + YES + + YES + frameskip + logView + logWindow + prefs + prefsPane + view + + + YES + NSMenuItem + NSTextView + NSWindow + YabausePrefsController + NSPanel + YabauseGLView + + + + YES + + YES + frameskip + logView + logWindow + prefs + prefsPane + view + + + YES + + frameskip + NSMenuItem + + + logView + NSTextView + + + logWindow + NSWindow + + + prefs + YabausePrefsController + + + prefsPane + NSPanel + + + view + YabauseGLView + + + + + IBProjectSource + YabauseController.h + + + + YabauseGLView + NSOpenGLView + + window + NSWindow + + + window + + window + NSWindow + + + + IBProjectSource + YabauseGLView.h + + + + YabauseGLView + NSOpenGLView + + IBUserSource + + + + + YabausePrefsController + NSObject + + YES + + YES + biosBrowse: + biosToggle: + bramBrowse: + buttonSelect: + buttonSetCancel: + buttonSetOk: + cartBrowse: + cartSelected: + mpegBrowse: + regionSelected: + soundCoreSelected: + videoCoreSelected: + + + YES + id + id + id + id + id + id + id + id + id + id + id + id + + + + YES + + YES + biosBrowse: + biosToggle: + bramBrowse: + buttonSelect: + buttonSetCancel: + buttonSetOk: + cartBrowse: + cartSelected: + mpegBrowse: + regionSelected: + soundCoreSelected: + videoCoreSelected: + + + YES + + biosBrowse: + id + + + biosToggle: + id + + + bramBrowse: + id + + + buttonSelect: + id + + + buttonSetCancel: + id + + + buttonSetOk: + id + + + cartBrowse: + id + + + cartSelected: + id + + + mpegBrowse: + id + + + regionSelected: + id + + + soundCoreSelected: + id + + + videoCoreSelected: + id + + + + + YES + + YES + biosPath + bramPath + buttonAssignment + buttonBox + cartBrowse + cartPath + cartType + emulateBios + mpegPath + prefsPane + region + soundCore + videoCore + + + YES + NSTextField + NSTextField + NSPanel + NSTextField + NSButton + NSTextField + NSPopUpButton + NSButton + NSTextField + NSPanel + NSPopUpButton + NSPopUpButton + NSPopUpButton + + + + YES + + YES + biosPath + bramPath + buttonAssignment + buttonBox + cartBrowse + cartPath + cartType + emulateBios + mpegPath + prefsPane + region + soundCore + videoCore + + + YES + + biosPath + NSTextField + + + bramPath + NSTextField + + + buttonAssignment + NSPanel + + + buttonBox + NSTextField + + + cartBrowse + NSButton + + + cartPath + NSTextField + + + cartType + NSPopUpButton + + + emulateBios + NSButton + + + mpegPath + NSTextField + + + prefsPane + NSPanel + + + region + NSPopUpButton + + + soundCore + NSPopUpButton + + + videoCore + NSPopUpButton + + + + + IBProjectSource + YabausePrefsController.h + + + + YabausePrefsController + NSObject + + IBUserSource + + + + + + YES + + NSActionCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSActionCell.h + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSWindowRestoration.h + + + + NSBox + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSBox.h + + + + NSButton + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSButton.h + + + + NSButtonCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSButtonCell.h + + + + NSCell + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSCell.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSLayoutConstraint.h + + + + NSFontManager + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSImageCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSImageCell.h + + + + NSImageView + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSImageView.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMenuItemCell + NSButtonCell + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItemCell.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSOpenGLView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSOpenGLView.h + + + + NSPanel + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSPanel.h + + + + NSPopUpButton + NSButton + + IBFrameworkSource + AppKit.framework/Headers/NSPopUpButton.h + + + + NSPopUpButtonCell + NSMenuItemCell + + IBFrameworkSource + AppKit.framework/Headers/NSPopUpButtonCell.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSResponder + + + + NSTabView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSTabView.h + + + + NSTabViewItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTabViewItem.h + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSTextField + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSTextField.h + + + + NSTextFieldCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSTextFieldCell.h + + + + NSTextView + NSText + + IBFrameworkSource + AppKit.framework/Headers/NSTextView.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../Yabause.xcodeproj + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + NSSwitch + controller + + + YES + {11, 11} + {10, 3} + {15, 15} + {621, 376} + + + + diff --git a/yabause/src/cocoa/PerCocoa.h b/yabause/src/cocoa/PerCocoa.h new file mode 100644 index 0000000000..568f4442b2 --- /dev/null +++ b/yabause/src/cocoa/PerCocoa.h @@ -0,0 +1,32 @@ +/* Copyright 2010, 2011 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PerCocoa_h +#define PerCocoa_h + +#include "peripheral.h" + +#define PERCORE_COCOA 42 +extern PerInterface_struct PERCocoa; + +/* Update a key mapping */ +void PERCocoaSetKey(u32 key, u8 name, int port); +u32 PERCocoaGetKey(u8 n, int p); + +#endif /* !PerCocoa_h */ diff --git a/yabause/src/cocoa/PerCocoa.m b/yabause/src/cocoa/PerCocoa.m new file mode 100644 index 0000000000..dd6890a569 --- /dev/null +++ b/yabause/src/cocoa/PerCocoa.m @@ -0,0 +1,168 @@ +/* Copyright 2010, 2011 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "PerCocoa.h" +#include "yabause.h" + +#include +#include +#include + +/* Forward Declarations. */ +static int PERCocoaInit(void); +static void PERCocoaDeInit(void); +static int PERCocoaHandleEvents(void); +static void PERCocoaNothing(void); + +static u32 PERCocoaScan(void); +static void PERCocoaFlush(void); + +static PerPad_struct *c1 = NULL, *c2 = NULL; + +PerInterface_struct PERCocoa = { + PERCORE_COCOA, + "Cocoa Keyboard Input Interface", + &PERCocoaInit, + &PERCocoaDeInit, + &PERCocoaHandleEvents, + &PERCocoaNothing, + &PERCocoaScan, + 0, + &PERCocoaFlush +}; + +/* Utility function to check if everything's set up right for a port */ +static BOOL AllSetForPort(int p) { + int i; + NSString *str; + NSUserDefaults *d = [NSUserDefaults standardUserDefaults]; + id val; + Class c = [NSNumber class]; + + for(i = 0; i < 13; ++i) { + str = [NSString stringWithFormat:@"Keyboard %d %s", p, PerPadNames[i]]; + val = [d objectForKey:str]; + + if(!val || ![val isKindOfClass:c]) { + return NO; + } + } + + return YES; +} + +void PERCocoaSetKey(u32 key, u8 n, int p) { + PerPad_struct *c; + NSString *str; + NSUserDefaults *d = [NSUserDefaults standardUserDefaults]; + NSNumber *val = [NSNumber numberWithUnsignedInt:(unsigned int)key]; + + assert(n <= PERPAD_Z); + + /* Build the string we'll look for, and set the value for that key */ + str = [NSString stringWithFormat:@"Pad %d %s", p, PerPadNames[n]]; + [d setObject:val forKey:str]; + [d synchronize]; + + /* Update the mapping if we're running */ + if(p == 0) { + c = c1; + } + else { + c = c2; + } + + /* This will effectively make sure we don't update until we've started. */ + if(c) { + PerSetKey(key, n, c); + } +} + +u32 PERCocoaGetKey(u8 n, int p) { + NSString *str; + id result; + NSUserDefaults *d = [NSUserDefaults standardUserDefaults]; + + assert(n <= PERPAD_Z); + + /* Fetch the key data */ + str = [NSString stringWithFormat:@"Pad %d %s", p, PerPadNames[n]]; + result = [d objectForKey:str]; + + if(result && [result isKindOfClass:[NSNumber class]]) { + return (u32)[result unsignedIntValue]; + } + else { + return (u32)-1; + } +} +static int PERCocoaInit(void) { + /* Fill in pad 1 */ + c1 = PerPadAdd(&PORTDATA1); + + PerSetKey(PERCocoaGetKey(PERPAD_UP, 0), PERPAD_UP, c1); + PerSetKey(PERCocoaGetKey(PERPAD_DOWN, 0), PERPAD_DOWN, c1); + PerSetKey(PERCocoaGetKey(PERPAD_LEFT, 0), PERPAD_LEFT, c1); + PerSetKey(PERCocoaGetKey(PERPAD_RIGHT, 0), PERPAD_RIGHT, c1); + PerSetKey(PERCocoaGetKey(PERPAD_LEFT_TRIGGER, 0), PERPAD_LEFT_TRIGGER, c1); + PerSetKey(PERCocoaGetKey(PERPAD_RIGHT_TRIGGER, 0), PERPAD_RIGHT_TRIGGER, c1); + PerSetKey(PERCocoaGetKey(PERPAD_START, 0), PERPAD_START, c1); + PerSetKey(PERCocoaGetKey(PERPAD_A, 0), PERPAD_A, c1); + PerSetKey(PERCocoaGetKey(PERPAD_B, 0), PERPAD_B, c1); + PerSetKey(PERCocoaGetKey(PERPAD_C, 0), PERPAD_C, c1); + PerSetKey(PERCocoaGetKey(PERPAD_X, 0), PERPAD_X, c1); + PerSetKey(PERCocoaGetKey(PERPAD_Y, 0), PERPAD_Y, c1); + PerSetKey(PERCocoaGetKey(PERPAD_Z, 0), PERPAD_Z, c1); + + /* Fill in pad 2 */ + c2 = PerPadAdd(&PORTDATA2); + + PerSetKey(PERCocoaGetKey(PERPAD_UP, 1), PERPAD_UP, c2); + PerSetKey(PERCocoaGetKey(PERPAD_DOWN, 1), PERPAD_DOWN, c2); + PerSetKey(PERCocoaGetKey(PERPAD_LEFT, 1), PERPAD_LEFT, c2); + PerSetKey(PERCocoaGetKey(PERPAD_RIGHT, 1), PERPAD_RIGHT, c2); + PerSetKey(PERCocoaGetKey(PERPAD_LEFT_TRIGGER, 1), PERPAD_LEFT_TRIGGER, c2); + PerSetKey(PERCocoaGetKey(PERPAD_RIGHT_TRIGGER, 1), PERPAD_RIGHT_TRIGGER, c2); + PerSetKey(PERCocoaGetKey(PERPAD_START, 1), PERPAD_START, c2); + PerSetKey(PERCocoaGetKey(PERPAD_A, 1), PERPAD_A, c2); + PerSetKey(PERCocoaGetKey(PERPAD_B, 1), PERPAD_B, c2); + PerSetKey(PERCocoaGetKey(PERPAD_C, 1), PERPAD_C, c2); + PerSetKey(PERCocoaGetKey(PERPAD_X, 1), PERPAD_X, c2); + PerSetKey(PERCocoaGetKey(PERPAD_Y, 1), PERPAD_Y, c2); + PerSetKey(PERCocoaGetKey(PERPAD_Z, 1), PERPAD_Z, c2); + + return 0; +} + +static void PERCocoaDeInit(void) { +} + +static int PERCocoaHandleEvents(void) { + return YabauseExec(); +} + +static void PERCocoaNothing(void) { +} + +static u32 PERCocoaScan(void) { + return 0; +} + +static void PERCocoaFlush(void) { +} diff --git a/yabause/src/cocoa/Yabause-Info.plist b/yabause/src/cocoa/Yabause-Info.plist new file mode 100644 index 0000000000..d4f860c666 --- /dev/null +++ b/yabause/src/cocoa/Yabause-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + Yabause.icns + CFBundleIdentifier + org.yabause.yabause.cocoa + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleShortVersionString + 0.9.12 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + CFBundleVersion + + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSHumanReadableCopyright + Copyright © Yabause Team + + diff --git a/yabause/src/cocoa/Yabause.icns b/yabause/src/cocoa/Yabause.icns new file mode 100644 index 0000000000000000000000000000000000000000..ee74849a60970f5a263208ceee36427519dfb661 GIT binary patch literal 175924 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 X2$KtEo*QAJyxsh4fFa~%1PJtRD~Ef? literal 0 HcmV?d00001 diff --git a/yabause/src/cocoa/Yabause.xcodeproj/project.pbxproj b/yabause/src/cocoa/Yabause.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..b9bdb37090 --- /dev/null +++ b/yabause/src/cocoa/Yabause.xcodeproj/project.pbxproj @@ -0,0 +1,808 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 2A1145191385E5010087C872 /* YabauseButtonFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1145181385E5010087C872 /* YabauseButtonFormatter.m */; }; + 2A3392711162E100000DA0E9 /* GLUT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A3392701162E100000DA0E9 /* GLUT.framework */; }; + 2A474DF01163A013006993EA /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A474DEF1163A013006993EA /* AudioUnit.framework */; }; + 2A474DF21163A013006993EA /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A474DF11163A013006993EA /* CoreAudio.framework */; }; + 2A7AB7CC117D1C1B00E47298 /* vidsoft.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7AB7CB117D1C1B00E47298 /* vidsoft.c */; }; + 2A7D39D3115BA16100DE3BD1 /* bios.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3987115BA16100DE3BD1 /* bios.c */; }; + 2A7D39D4115BA16100DE3BD1 /* cd-macosx.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3989115BA16100DE3BD1 /* cd-macosx.c */; }; + 2A7D39D5115BA16100DE3BD1 /* cdbase.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D398A115BA16100DE3BD1 /* cdbase.c */; }; + 2A7D39D6115BA16100DE3BD1 /* cheat.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D398C115BA16100DE3BD1 /* cheat.c */; }; + 2A7D39D7115BA16100DE3BD1 /* coffelf.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D398E115BA16100DE3BD1 /* coffelf.c */; }; + 2A7D39D8115BA16100DE3BD1 /* cs0.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3991115BA16100DE3BD1 /* cs0.c */; }; + 2A7D39D9115BA16100DE3BD1 /* cs1.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3993115BA16100DE3BD1 /* cs1.c */; }; + 2A7D39DA115BA16100DE3BD1 /* cs2.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3995115BA16100DE3BD1 /* cs2.c */; }; + 2A7D39DB115BA16100DE3BD1 /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3997115BA16100DE3BD1 /* debug.c */; }; + 2A7D39DC115BA16100DE3BD1 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3999115BA16100DE3BD1 /* error.c */; }; + 2A7D39DD115BA16100DE3BD1 /* m68kc68k.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D399B115BA16100DE3BD1 /* m68kc68k.c */; }; + 2A7D39DE115BA16100DE3BD1 /* m68kcore.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D399D115BA16100DE3BD1 /* m68kcore.c */; }; + 2A7D39DF115BA16100DE3BD1 /* m68kd.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D399F115BA16100DE3BD1 /* m68kd.c */; }; + 2A7D39E1115BA16100DE3BD1 /* macjoy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39A2115BA16100DE3BD1 /* macjoy.c */; }; + 2A7D39E2115BA16100DE3BD1 /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39A4115BA16100DE3BD1 /* memory.c */; }; + 2A7D39E3115BA16100DE3BD1 /* movie.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39A6115BA16100DE3BD1 /* movie.c */; }; + 2A7D39E4115BA16100DE3BD1 /* netlink.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39A8115BA16100DE3BD1 /* netlink.c */; }; + 2A7D39E5115BA16100DE3BD1 /* peripheral.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39AA115BA16100DE3BD1 /* peripheral.c */; }; + 2A7D39E6115BA16100DE3BD1 /* permacjoy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39AC115BA16100DE3BD1 /* permacjoy.c */; }; + 2A7D39E7115BA16100DE3BD1 /* profile.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39AE115BA16100DE3BD1 /* profile.c */; }; + 2A7D39E9115BA16100DE3BD1 /* scu.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39B2115BA16100DE3BD1 /* scu.c */; }; + 2A7D39EA115BA16100DE3BD1 /* sh2core.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39B4115BA16100DE3BD1 /* sh2core.c */; }; + 2A7D39EB115BA16100DE3BD1 /* sh2d.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39B6115BA16100DE3BD1 /* sh2d.c */; }; + 2A7D39EC115BA16100DE3BD1 /* sh2idle.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39B8115BA16100DE3BD1 /* sh2idle.c */; }; + 2A7D39ED115BA16100DE3BD1 /* sh2int.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39BA115BA16100DE3BD1 /* sh2int.c */; }; + 2A7D39EE115BA16100DE3BD1 /* sh2trace.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39BC115BA16100DE3BD1 /* sh2trace.c */; }; + 2A7D39EF115BA16100DE3BD1 /* smpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39BE115BA16100DE3BD1 /* smpc.c */; }; + 2A7D39F1115BA16100DE3BD1 /* vdp1.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39C2115BA16100DE3BD1 /* vdp1.c */; }; + 2A7D39F2115BA16100DE3BD1 /* vdp2.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39C4115BA16100DE3BD1 /* vdp2.c */; }; + 2A7D39F3115BA16100DE3BD1 /* vdp2debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39C6115BA16100DE3BD1 /* vdp2debug.c */; }; + 2A7D39F4115BA16100DE3BD1 /* vidogl.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39C8115BA16100DE3BD1 /* vidogl.c */; }; + 2A7D39F5115BA16100DE3BD1 /* vidshared.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39CA115BA16100DE3BD1 /* vidshared.c */; }; + 2A7D39F6115BA16100DE3BD1 /* vidgcd.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39CC115BA16100DE3BD1 /* vidgcd.c */; }; + 2A7D39F7115BA16100DE3BD1 /* yabause.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39CE115BA16100DE3BD1 /* yabause.c */; }; + 2A7D39F8115BA16100DE3BD1 /* ygl.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D39D0115BA16100DE3BD1 /* ygl.c */; }; + 2A7D3A1E115BA29F00DE3BD1 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A7D3A1D115BA29F00DE3BD1 /* OpenGL.framework */; }; + 2A7D3A31115BA35200DE3BD1 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A7D3A30115BA35200DE3BD1 /* CoreFoundation.framework */; }; + 2A7D3A33115BA35200DE3BD1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A7D3A32115BA35200DE3BD1 /* IOKit.framework */; }; + 2A7D3A48115BA3AF00DE3BD1 /* c68k.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3A41115BA3AF00DE3BD1 /* c68k.c */; }; + 2A7D3A49115BA3AF00DE3BD1 /* c68kexec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3A43115BA3AF00DE3BD1 /* c68kexec.c */; settings = {COMPILER_FLAGS = "-O0"; }; }; + 2A7D3A4B115BA3AF00DE3BD1 /* gen68k.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3A45115BA3AF00DE3BD1 /* gen68k.c */; }; + 2A7D3A5A115BA41200DE3BD1 /* gen68k.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A7D3A45115BA3AF00DE3BD1 /* gen68k.c */; }; + 2A7D3A9B115BAB9E00DE3BD1 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A7D3A99115BAB9E00DE3BD1 /* MainMenu.xib */; }; + 2A7D3AD5115BB01500DE3BD1 /* Yabause.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2A7D3AD4115BB01500DE3BD1 /* Yabause.icns */; }; + 2A82CCA1116171DB00F15B02 /* PerCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A82CCA0116171DB00F15B02 /* PerCocoa.m */; }; + 2A9D7600116645D700A580EB /* sndmac.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A9D75FE116645D700A580EB /* sndmac.c */; }; + 2A9D760E11665F2800A580EB /* controller.png in Resources */ = {isa = PBXBuildFile; fileRef = 2A9D760D11665F2800A580EB /* controller.png */; }; + 2A9D7621116667C300A580EB /* YabausePrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9D761F116667C300A580EB /* YabausePrefsController.m */; }; + 2AD10F9E118BBC850088FB2C /* scsp2.c in Sources */ = {isa = PBXBuildFile; fileRef = 2AD10F97118BBC850088FB2C /* scsp2.c */; }; + 2AD10F9F118BBC850088FB2C /* snddummy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2AD10F99118BBC850088FB2C /* snddummy.c */; }; + 2AD10FA0118BBC850088FB2C /* sndwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 2AD10F9A118BBC850088FB2C /* sndwav.c */; }; + 2AD10FDB118BCFA00088FB2C /* thr-dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2AD10F9B118BBC850088FB2C /* thr-dummy.c */; }; + 2AF0CA51116146A400196BED /* YabauseGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AF0CA4F116146A400196BED /* YabauseGLView.m */; }; + 2AF0CA9911614DD500196BED /* YabauseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AF0CA9811614DD500196BED /* YabauseController.m */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + F07BF9E414A1C2C500775818 /* yglshader.c in Sources */ = {isa = PBXBuildFile; fileRef = F07BF9E314A1C2C500775818 /* yglshader.c */; }; + F07E62CB15054DCC00DF8A4E /* titan.c in Sources */ = {isa = PBXBuildFile; fileRef = F07E62CA15054DCC00DF8A4E /* titan.c */; }; + F0E47FAD156C34EC001A1B51 /* osdcore.c in Sources */ = {isa = PBXBuildFile; fileRef = F0E47FAC156C34EC001A1B51 /* osdcore.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 2A7D3A7A115BA63900DE3BD1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2A7D3A54115BA3E700DE3BD1; + remoteInfo = gen68k; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 2A1145171385E5010087C872 /* YabauseButtonFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YabauseButtonFormatter.h; sourceTree = ""; }; + 2A1145181385E5010087C872 /* YabauseButtonFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YabauseButtonFormatter.m; sourceTree = ""; }; + 2A3392701162E100000DA0E9 /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = System/Library/Frameworks/GLUT.framework; sourceTree = SDKROOT; }; + 2A474DEF1163A013006993EA /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; + 2A474DF11163A013006993EA /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + 2A7AB7CB117D1C1B00E47298 /* vidsoft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vidsoft.c; path = ../vidsoft.c; sourceTree = SOURCE_ROOT; }; + 2A7AB7CE117D1CBE00E47298 /* vidsoft.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vidsoft.h; path = ../vidsoft.h; sourceTree = SOURCE_ROOT; }; + 2A7D3987115BA16100DE3BD1 /* bios.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bios.c; path = ../bios.c; sourceTree = SOURCE_ROOT; }; + 2A7D3988115BA16100DE3BD1 /* bios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bios.h; path = ../bios.h; sourceTree = SOURCE_ROOT; }; + 2A7D3989115BA16100DE3BD1 /* cd-macosx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "cd-macosx.c"; path = "../cd-macosx.c"; sourceTree = SOURCE_ROOT; }; + 2A7D398A115BA16100DE3BD1 /* cdbase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cdbase.c; path = ../cdbase.c; sourceTree = SOURCE_ROOT; }; + 2A7D398B115BA16100DE3BD1 /* cdbase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cdbase.h; path = ../cdbase.h; sourceTree = SOURCE_ROOT; }; + 2A7D398C115BA16100DE3BD1 /* cheat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cheat.c; path = ../cheat.c; sourceTree = SOURCE_ROOT; }; + 2A7D398D115BA16100DE3BD1 /* cheat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cheat.h; path = ../cheat.h; sourceTree = SOURCE_ROOT; }; + 2A7D398E115BA16100DE3BD1 /* coffelf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = coffelf.c; path = ../coffelf.c; sourceTree = SOURCE_ROOT; }; + 2A7D398F115BA16100DE3BD1 /* coffelf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = coffelf.h; path = ../coffelf.h; sourceTree = SOURCE_ROOT; }; + 2A7D3990115BA16100DE3BD1 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = core.h; path = ../core.h; sourceTree = SOURCE_ROOT; }; + 2A7D3991115BA16100DE3BD1 /* cs0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cs0.c; path = ../cs0.c; sourceTree = SOURCE_ROOT; }; + 2A7D3992115BA16100DE3BD1 /* cs0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cs0.h; path = ../cs0.h; sourceTree = SOURCE_ROOT; }; + 2A7D3993115BA16100DE3BD1 /* cs1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cs1.c; path = ../cs1.c; sourceTree = SOURCE_ROOT; }; + 2A7D3994115BA16100DE3BD1 /* cs1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cs1.h; path = ../cs1.h; sourceTree = SOURCE_ROOT; }; + 2A7D3995115BA16100DE3BD1 /* cs2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cs2.c; path = ../cs2.c; sourceTree = SOURCE_ROOT; }; + 2A7D3996115BA16100DE3BD1 /* cs2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cs2.h; path = ../cs2.h; sourceTree = SOURCE_ROOT; }; + 2A7D3997115BA16100DE3BD1 /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = debug.c; path = ../debug.c; sourceTree = SOURCE_ROOT; }; + 2A7D3998115BA16100DE3BD1 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = debug.h; path = ../debug.h; sourceTree = SOURCE_ROOT; }; + 2A7D3999115BA16100DE3BD1 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = error.c; path = ../error.c; sourceTree = SOURCE_ROOT; }; + 2A7D399A115BA16100DE3BD1 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = error.h; path = ../error.h; sourceTree = SOURCE_ROOT; }; + 2A7D399B115BA16100DE3BD1 /* m68kc68k.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m68kc68k.c; path = ../m68kc68k.c; sourceTree = SOURCE_ROOT; }; + 2A7D399C115BA16100DE3BD1 /* m68kc68k.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m68kc68k.h; path = ../m68kc68k.h; sourceTree = SOURCE_ROOT; }; + 2A7D399D115BA16100DE3BD1 /* m68kcore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m68kcore.c; path = ../m68kcore.c; sourceTree = SOURCE_ROOT; }; + 2A7D399E115BA16100DE3BD1 /* m68kcore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m68kcore.h; path = ../m68kcore.h; sourceTree = SOURCE_ROOT; }; + 2A7D399F115BA16100DE3BD1 /* m68kd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m68kd.c; path = ../m68kd.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A0115BA16100DE3BD1 /* m68kd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m68kd.h; path = ../m68kd.h; sourceTree = SOURCE_ROOT; }; + 2A7D39A1115BA16100DE3BD1 /* m68kq68.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m68kq68.c; path = ../m68kq68.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A2115BA16100DE3BD1 /* macjoy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = macjoy.c; path = ../macjoy.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A3115BA16100DE3BD1 /* macjoy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macjoy.h; path = ../macjoy.h; sourceTree = SOURCE_ROOT; }; + 2A7D39A4115BA16100DE3BD1 /* memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = memory.c; path = ../memory.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A5115BA16100DE3BD1 /* memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = memory.h; path = ../memory.h; sourceTree = SOURCE_ROOT; }; + 2A7D39A6115BA16100DE3BD1 /* movie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = movie.c; path = ../movie.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A7115BA16100DE3BD1 /* movie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = movie.h; path = ../movie.h; sourceTree = SOURCE_ROOT; }; + 2A7D39A8115BA16100DE3BD1 /* netlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = netlink.c; path = ../netlink.c; sourceTree = SOURCE_ROOT; }; + 2A7D39A9115BA16100DE3BD1 /* netlink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = netlink.h; path = ../netlink.h; sourceTree = SOURCE_ROOT; }; + 2A7D39AA115BA16100DE3BD1 /* peripheral.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = peripheral.c; path = ../peripheral.c; sourceTree = SOURCE_ROOT; }; + 2A7D39AB115BA16100DE3BD1 /* peripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = peripheral.h; path = ../peripheral.h; sourceTree = SOURCE_ROOT; }; + 2A7D39AC115BA16100DE3BD1 /* permacjoy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = permacjoy.c; path = ../permacjoy.c; sourceTree = SOURCE_ROOT; }; + 2A7D39AD115BA16100DE3BD1 /* permacjoy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = permacjoy.h; path = ../permacjoy.h; sourceTree = SOURCE_ROOT; }; + 2A7D39AE115BA16100DE3BD1 /* profile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = profile.c; path = ../profile.c; sourceTree = SOURCE_ROOT; }; + 2A7D39AF115BA16100DE3BD1 /* profile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = profile.h; path = ../profile.h; sourceTree = SOURCE_ROOT; }; + 2A7D39B0115BA16100DE3BD1 /* scsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scsp.c; path = ../scsp.c; sourceTree = SOURCE_ROOT; }; + 2A7D39B1115BA16100DE3BD1 /* scsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scsp.h; path = ../scsp.h; sourceTree = SOURCE_ROOT; }; + 2A7D39B2115BA16100DE3BD1 /* scu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scu.c; path = ../scu.c; sourceTree = SOURCE_ROOT; }; + 2A7D39B3115BA16100DE3BD1 /* scu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scu.h; path = ../scu.h; sourceTree = SOURCE_ROOT; }; + 2A7D39B4115BA16100DE3BD1 /* sh2core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sh2core.c; path = ../sh2core.c; sourceTree = SOURCE_ROOT; }; + 2A7D39B5115BA16100DE3BD1 /* sh2core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sh2core.h; path = ../sh2core.h; sourceTree = SOURCE_ROOT; }; + 2A7D39B6115BA16100DE3BD1 /* sh2d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sh2d.c; path = ../sh2d.c; sourceTree = SOURCE_ROOT; }; + 2A7D39B7115BA16100DE3BD1 /* sh2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sh2d.h; path = ../sh2d.h; sourceTree = SOURCE_ROOT; }; + 2A7D39B8115BA16100DE3BD1 /* sh2idle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sh2idle.c; path = ../sh2idle.c; sourceTree = SOURCE_ROOT; }; + 2A7D39B9115BA16100DE3BD1 /* sh2idle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sh2idle.h; path = ../sh2idle.h; sourceTree = SOURCE_ROOT; }; + 2A7D39BA115BA16100DE3BD1 /* sh2int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sh2int.c; path = ../sh2int.c; sourceTree = SOURCE_ROOT; }; + 2A7D39BB115BA16100DE3BD1 /* sh2int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sh2int.h; path = ../sh2int.h; sourceTree = SOURCE_ROOT; }; + 2A7D39BC115BA16100DE3BD1 /* sh2trace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sh2trace.c; path = ../sh2trace.c; sourceTree = SOURCE_ROOT; }; + 2A7D39BD115BA16100DE3BD1 /* sh2trace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sh2trace.h; path = ../sh2trace.h; sourceTree = SOURCE_ROOT; }; + 2A7D39BE115BA16100DE3BD1 /* smpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = smpc.c; path = ../smpc.c; sourceTree = SOURCE_ROOT; }; + 2A7D39BF115BA16100DE3BD1 /* smpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = smpc.h; path = ../smpc.h; sourceTree = SOURCE_ROOT; }; + 2A7D39C2115BA16100DE3BD1 /* vdp1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vdp1.c; path = ../vdp1.c; sourceTree = SOURCE_ROOT; }; + 2A7D39C3115BA16100DE3BD1 /* vdp1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vdp1.h; path = ../vdp1.h; sourceTree = SOURCE_ROOT; }; + 2A7D39C4115BA16100DE3BD1 /* vdp2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vdp2.c; path = ../vdp2.c; sourceTree = SOURCE_ROOT; }; + 2A7D39C5115BA16100DE3BD1 /* vdp2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vdp2.h; path = ../vdp2.h; sourceTree = SOURCE_ROOT; }; + 2A7D39C6115BA16100DE3BD1 /* vdp2debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vdp2debug.c; path = ../vdp2debug.c; sourceTree = SOURCE_ROOT; }; + 2A7D39C7115BA16100DE3BD1 /* vdp2debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vdp2debug.h; path = ../vdp2debug.h; sourceTree = SOURCE_ROOT; }; + 2A7D39C8115BA16100DE3BD1 /* vidogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vidogl.c; path = ../vidogl.c; sourceTree = SOURCE_ROOT; }; + 2A7D39C9115BA16100DE3BD1 /* vidogl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vidogl.h; path = ../vidogl.h; sourceTree = SOURCE_ROOT; }; + 2A7D39CA115BA16100DE3BD1 /* vidshared.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vidshared.c; path = ../vidshared.c; sourceTree = SOURCE_ROOT; }; + 2A7D39CB115BA16100DE3BD1 /* vidshared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vidshared.h; path = ../vidshared.h; sourceTree = SOURCE_ROOT; }; + 2A7D39CC115BA16100DE3BD1 /* vidgcd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vidgcd.c; sourceTree = ""; }; + 2A7D39CD115BA16100DE3BD1 /* vidgcd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vidgcd.h; sourceTree = ""; }; + 2A7D39CE115BA16100DE3BD1 /* yabause.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yabause.c; path = ../yabause.c; sourceTree = SOURCE_ROOT; }; + 2A7D39CF115BA16100DE3BD1 /* yabause.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yabause.h; path = ../yabause.h; sourceTree = SOURCE_ROOT; }; + 2A7D39D0115BA16100DE3BD1 /* ygl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ygl.c; path = ../ygl.c; sourceTree = SOURCE_ROOT; }; + 2A7D39D1115BA16100DE3BD1 /* ygl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ygl.h; path = ../ygl.h; sourceTree = SOURCE_ROOT; }; + 2A7D39D2115BA16100DE3BD1 /* yui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yui.h; path = ../yui.h; sourceTree = SOURCE_ROOT; }; + 2A7D3A1D115BA29F00DE3BD1 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 2A7D3A30115BA35200DE3BD1 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 2A7D3A32115BA35200DE3BD1 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 2A7D3A41115BA3AF00DE3BD1 /* c68k.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = c68k.c; path = ../c68k/c68k.c; sourceTree = SOURCE_ROOT; }; + 2A7D3A42115BA3AF00DE3BD1 /* c68k.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = c68k.h; path = ../c68k/c68k.h; sourceTree = SOURCE_ROOT; }; + 2A7D3A43115BA3AF00DE3BD1 /* c68kexec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = c68kexec.c; path = ../c68k/c68kexec.c; sourceTree = SOURCE_ROOT; }; + 2A7D3A44115BA3AF00DE3BD1 /* c68kmac.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = c68kmac.inc; path = ../c68k/c68kmac.inc; sourceTree = SOURCE_ROOT; }; + 2A7D3A45115BA3AF00DE3BD1 /* gen68k.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = gen68k.c; path = ../c68k/gen68k.c; sourceTree = SOURCE_ROOT; }; + 2A7D3A46115BA3AF00DE3BD1 /* gen68k.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gen68k.h; path = ../c68k/gen68k.h; sourceTree = SOURCE_ROOT; }; + 2A7D3A47115BA3AF00DE3BD1 /* gen68k.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = gen68k.inc; path = ../c68k/gen68k.inc; sourceTree = SOURCE_ROOT; }; + 2A7D3A55115BA3E700DE3BD1 /* gen68k */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gen68k; sourceTree = BUILT_PRODUCTS_DIR; }; + 2A7D3A9A115BAB9E00DE3BD1 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; + 2A7D3AD4115BB01500DE3BD1 /* Yabause.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Yabause.icns; sourceTree = ""; }; + 2A82CC9F116171DB00F15B02 /* PerCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerCocoa.h; sourceTree = ""; }; + 2A82CCA0116171DB00F15B02 /* PerCocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PerCocoa.m; sourceTree = ""; }; + 2A9D75FE116645D700A580EB /* sndmac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sndmac.c; path = ../sndmac.c; sourceTree = SOURCE_ROOT; }; + 2A9D75FF116645D700A580EB /* sndmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sndmac.h; path = ../sndmac.h; sourceTree = SOURCE_ROOT; }; + 2A9D760D11665F2800A580EB /* controller.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = controller.png; path = resources/controller.png; sourceTree = ""; }; + 2A9D761F116667C300A580EB /* YabausePrefsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YabausePrefsController.m; sourceTree = ""; }; + 2A9D7620116667C300A580EB /* YabausePrefsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YabausePrefsController.h; sourceTree = ""; }; + 2AD10F97118BBC850088FB2C /* scsp2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scsp2.c; path = ../scsp2.c; sourceTree = SOURCE_ROOT; }; + 2AD10F98118BBC850088FB2C /* scsp2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scsp2.h; path = ../scsp2.h; sourceTree = SOURCE_ROOT; }; + 2AD10F99118BBC850088FB2C /* snddummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = snddummy.c; path = ../snddummy.c; sourceTree = SOURCE_ROOT; }; + 2AD10F9A118BBC850088FB2C /* sndwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sndwav.c; path = ../sndwav.c; sourceTree = SOURCE_ROOT; }; + 2AD10F9B118BBC850088FB2C /* thr-dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "thr-dummy.c"; path = "../thr-dummy.c"; sourceTree = SOURCE_ROOT; }; + 2AD10F9C118BBC850088FB2C /* thr-macosx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "thr-macosx.c"; path = "../thr-macosx.c"; sourceTree = SOURCE_ROOT; }; + 2AD10F9D118BBC850088FB2C /* threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = threads.h; path = ../threads.h; sourceTree = SOURCE_ROOT; }; + 2AF0CA4F116146A400196BED /* YabauseGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YabauseGLView.m; sourceTree = ""; }; + 2AF0CA50116146A400196BED /* YabauseGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YabauseGLView.h; sourceTree = ""; }; + 2AF0CA9711614DD500196BED /* YabauseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YabauseController.h; sourceTree = ""; }; + 2AF0CA9811614DD500196BED /* YabauseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YabauseController.m; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Yabause-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Yabause-Info.plist"; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* Yabause.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Yabause.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F07BF9E314A1C2C500775818 /* yglshader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yglshader.c; path = ../yglshader.c; sourceTree = SOURCE_ROOT; }; + F07E62CA15054DCC00DF8A4E /* titan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = titan.c; path = ../titan/titan.c; sourceTree = SOURCE_ROOT; }; + F07E62CC15054DF500DF8A4E /* titan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = titan.h; path = ../titan/titan.h; sourceTree = SOURCE_ROOT; }; + F0E47FAC156C34EC001A1B51 /* osdcore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = osdcore.c; path = ../osdcore.c; sourceTree = SOURCE_ROOT; }; + F0E47FAE156C34FB001A1B51 /* osdcore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = osdcore.h; path = ../osdcore.h; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2A7D3A53115BA3E700DE3BD1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 2A7D3A1E115BA29F00DE3BD1 /* OpenGL.framework in Frameworks */, + 2A7D3A31115BA35200DE3BD1 /* CoreFoundation.framework in Frameworks */, + 2A7D3A33115BA35200DE3BD1 /* IOKit.framework in Frameworks */, + 2A3392711162E100000DA0E9 /* GLUT.framework in Frameworks */, + 2A474DF01163A013006993EA /* AudioUnit.framework in Frameworks */, + 2A474DF21163A013006993EA /* CoreAudio.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 2A1145171385E5010087C872 /* YabauseButtonFormatter.h */, + 2A1145181385E5010087C872 /* YabauseButtonFormatter.m */, + 2AF0CA9711614DD500196BED /* YabauseController.h */, + 2AF0CA9811614DD500196BED /* YabauseController.m */, + 2AF0CA50116146A400196BED /* YabauseGLView.h */, + 2AF0CA4F116146A400196BED /* YabauseGLView.m */, + 2A9D7620116667C300A580EB /* YabausePrefsController.h */, + 2A9D761F116667C300A580EB /* YabausePrefsController.m */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 2A3392701162E100000DA0E9 /* GLUT.framework */, + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + 2A7D3A1D115BA29F00DE3BD1 /* OpenGL.framework */, + 2A7D3A30115BA35200DE3BD1 /* CoreFoundation.framework */, + 2A7D3A32115BA35200DE3BD1 /* IOKit.framework */, + 2A474DEF1163A013006993EA /* AudioUnit.framework */, + 2A474DF11163A013006993EA /* CoreAudio.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* Yabause.app */, + 2A7D3A55115BA3E700DE3BD1 /* gen68k */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* Yabause */ = { + isa = PBXGroup; + children = ( + 2A7D3972115BA0DD00DE3BD1 /* Emulation Core */, + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = Yabause; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 29B97316FDCFA39411CA2CEA /* main.m */, + 2A82CC9F116171DB00F15B02 /* PerCocoa.h */, + 2A82CCA0116171DB00F15B02 /* PerCocoa.m */, + 2A7D39CD115BA16100DE3BD1 /* vidgcd.h */, + 2A7D39CC115BA16100DE3BD1 /* vidgcd.c */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 2A9D760D11665F2800A580EB /* controller.png */, + 2A7D3AD4115BB01500DE3BD1 /* Yabause.icns */, + 8D1107310486CEB800E47090 /* Yabause-Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + 2A7D3A99115BAB9E00DE3BD1 /* MainMenu.xib */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 2A7D3972115BA0DD00DE3BD1 /* Emulation Core */ = { + isa = PBXGroup; + children = ( + F0E47FAE156C34FB001A1B51 /* osdcore.h */, + F0E47FAC156C34EC001A1B51 /* osdcore.c */, + F07E62CC15054DF500DF8A4E /* titan.h */, + F07E62CA15054DCC00DF8A4E /* titan.c */, + F07BF9E314A1C2C500775818 /* yglshader.c */, + 2A7D3A3E115BA38900DE3BD1 /* C68K */, + 2A7D3987115BA16100DE3BD1 /* bios.c */, + 2A7D3988115BA16100DE3BD1 /* bios.h */, + 2A7D3989115BA16100DE3BD1 /* cd-macosx.c */, + 2A7D398A115BA16100DE3BD1 /* cdbase.c */, + 2A7D398B115BA16100DE3BD1 /* cdbase.h */, + 2A7D398C115BA16100DE3BD1 /* cheat.c */, + 2A7D398D115BA16100DE3BD1 /* cheat.h */, + 2A7D398E115BA16100DE3BD1 /* coffelf.c */, + 2A7D398F115BA16100DE3BD1 /* coffelf.h */, + 2A7D3990115BA16100DE3BD1 /* core.h */, + 2A7D3991115BA16100DE3BD1 /* cs0.c */, + 2A7D3992115BA16100DE3BD1 /* cs0.h */, + 2A7D3993115BA16100DE3BD1 /* cs1.c */, + 2A7D3994115BA16100DE3BD1 /* cs1.h */, + 2A7D3995115BA16100DE3BD1 /* cs2.c */, + 2A7D3996115BA16100DE3BD1 /* cs2.h */, + 2A7D3997115BA16100DE3BD1 /* debug.c */, + 2A7D3998115BA16100DE3BD1 /* debug.h */, + 2A7D3999115BA16100DE3BD1 /* error.c */, + 2A7D399A115BA16100DE3BD1 /* error.h */, + 2A7D399B115BA16100DE3BD1 /* m68kc68k.c */, + 2A7D399C115BA16100DE3BD1 /* m68kc68k.h */, + 2A7D399D115BA16100DE3BD1 /* m68kcore.c */, + 2A7D399E115BA16100DE3BD1 /* m68kcore.h */, + 2A7D399F115BA16100DE3BD1 /* m68kd.c */, + 2A7D39A0115BA16100DE3BD1 /* m68kd.h */, + 2A7D39A1115BA16100DE3BD1 /* m68kq68.c */, + 2A7D39A2115BA16100DE3BD1 /* macjoy.c */, + 2A7D39A3115BA16100DE3BD1 /* macjoy.h */, + 2A7D39A4115BA16100DE3BD1 /* memory.c */, + 2A7D39A5115BA16100DE3BD1 /* memory.h */, + 2A7D39A6115BA16100DE3BD1 /* movie.c */, + 2A7D39A7115BA16100DE3BD1 /* movie.h */, + 2A7D39A8115BA16100DE3BD1 /* netlink.c */, + 2A7D39A9115BA16100DE3BD1 /* netlink.h */, + 2A7D39AA115BA16100DE3BD1 /* peripheral.c */, + 2A7D39AB115BA16100DE3BD1 /* peripheral.h */, + 2A7D39AC115BA16100DE3BD1 /* permacjoy.c */, + 2A7D39AD115BA16100DE3BD1 /* permacjoy.h */, + 2A7D39AE115BA16100DE3BD1 /* profile.c */, + 2A7D39AF115BA16100DE3BD1 /* profile.h */, + 2A7D39B0115BA16100DE3BD1 /* scsp.c */, + 2A7D39B1115BA16100DE3BD1 /* scsp.h */, + 2AD10F97118BBC850088FB2C /* scsp2.c */, + 2AD10F98118BBC850088FB2C /* scsp2.h */, + 2A7D39B2115BA16100DE3BD1 /* scu.c */, + 2A7D39B3115BA16100DE3BD1 /* scu.h */, + 2A7D39B4115BA16100DE3BD1 /* sh2core.c */, + 2A7D39B5115BA16100DE3BD1 /* sh2core.h */, + 2A7D39B6115BA16100DE3BD1 /* sh2d.c */, + 2A7D39B7115BA16100DE3BD1 /* sh2d.h */, + 2A7D39B8115BA16100DE3BD1 /* sh2idle.c */, + 2A7D39B9115BA16100DE3BD1 /* sh2idle.h */, + 2A7D39BA115BA16100DE3BD1 /* sh2int.c */, + 2A7D39BB115BA16100DE3BD1 /* sh2int.h */, + 2A7D39BC115BA16100DE3BD1 /* sh2trace.c */, + 2A7D39BD115BA16100DE3BD1 /* sh2trace.h */, + 2A7D39BE115BA16100DE3BD1 /* smpc.c */, + 2A7D39BF115BA16100DE3BD1 /* smpc.h */, + 2AD10F99118BBC850088FB2C /* snddummy.c */, + 2A9D75FE116645D700A580EB /* sndmac.c */, + 2A9D75FF116645D700A580EB /* sndmac.h */, + 2AD10F9A118BBC850088FB2C /* sndwav.c */, + 2AD10F9B118BBC850088FB2C /* thr-dummy.c */, + 2AD10F9C118BBC850088FB2C /* thr-macosx.c */, + 2AD10F9D118BBC850088FB2C /* threads.h */, + 2A7D39C2115BA16100DE3BD1 /* vdp1.c */, + 2A7D39C3115BA16100DE3BD1 /* vdp1.h */, + 2A7D39C4115BA16100DE3BD1 /* vdp2.c */, + 2A7D39C5115BA16100DE3BD1 /* vdp2.h */, + 2A7D39C6115BA16100DE3BD1 /* vdp2debug.c */, + 2A7D39C7115BA16100DE3BD1 /* vdp2debug.h */, + 2A7D39C8115BA16100DE3BD1 /* vidogl.c */, + 2A7D39C9115BA16100DE3BD1 /* vidogl.h */, + 2A7D39CA115BA16100DE3BD1 /* vidshared.c */, + 2A7D39CB115BA16100DE3BD1 /* vidshared.h */, + 2A7AB7CE117D1CBE00E47298 /* vidsoft.h */, + 2A7AB7CB117D1C1B00E47298 /* vidsoft.c */, + 2A7D39CE115BA16100DE3BD1 /* yabause.c */, + 2A7D39CF115BA16100DE3BD1 /* yabause.h */, + 2A7D39D0115BA16100DE3BD1 /* ygl.c */, + 2A7D39D1115BA16100DE3BD1 /* ygl.h */, + 2A7D39D2115BA16100DE3BD1 /* yui.h */, + ); + name = "Emulation Core"; + sourceTree = ""; + }; + 2A7D3A3E115BA38900DE3BD1 /* C68K */ = { + isa = PBXGroup; + children = ( + 2A7D3A41115BA3AF00DE3BD1 /* c68k.c */, + 2A7D3A42115BA3AF00DE3BD1 /* c68k.h */, + 2A7D3A43115BA3AF00DE3BD1 /* c68kexec.c */, + 2A7D3A44115BA3AF00DE3BD1 /* c68kmac.inc */, + 2A7D3A45115BA3AF00DE3BD1 /* gen68k.c */, + 2A7D3A46115BA3AF00DE3BD1 /* gen68k.h */, + 2A7D3A47115BA3AF00DE3BD1 /* gen68k.inc */, + ); + name = C68K; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2A7D3A54115BA3E700DE3BD1 /* gen68k */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2A7D3A59115BA40C00DE3BD1 /* Build configuration list for PBXNativeTarget "gen68k" */; + buildPhases = ( + 2A7D3A52115BA3E700DE3BD1 /* Sources */, + 2A7D3A53115BA3E700DE3BD1 /* Frameworks */, + 2A7D3A6F115BA5A200DE3BD1 /* Run gen68k */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = gen68k; + productName = gen68k; + productReference = 2A7D3A55115BA3E700DE3BD1 /* gen68k */; + productType = "com.apple.product-type.tool"; + }; + 8D1107260486CEB800E47090 /* Yabause */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Yabause" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 2A7D3A7B115BA63900DE3BD1 /* PBXTargetDependency */, + ); + name = Yabause; + productInstallPath = "$(HOME)/Applications"; + productName = Yabause; + productReference = 8D1107320486CEB800E47090 /* Yabause.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Yabause" */; + compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* Yabause */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2A7D3A54115BA3E700DE3BD1 /* gen68k */, + 8D1107260486CEB800E47090 /* Yabause */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + 2A7D3A9B115BAB9E00DE3BD1 /* MainMenu.xib in Resources */, + 2A7D3AD5115BB01500DE3BD1 /* Yabause.icns in Resources */, + 2A9D760E11665F2800A580EB /* controller.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2A7D3A6F115BA5A200DE3BD1 /* Run gen68k */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/../gen68k/gen68k.c", + "$(PROJECT_DIR)/../gen68k/gen68k.h", + "$(PROJECT_DIR)/../gen68k/gen68k.inc", + "$(PROJECT_DIR)/../gen68k/c68kmac.inc", + "$(PROJECT_DIR)/../gen68k/c68kexec.c", + "$(PROJECT_DIR)/../gen68k/c68k.c", + "$(PROJECT_DIR)/../gen68k/c68k.h", + ); + name = "Run gen68k"; + outputPaths = ( + "$(PROJECT_DIR)/../gen68k/c68k_op0.inc", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd ../c68k\n\"$BUILD_DIR/$CONFIGURATION/gen68k\""; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2A7D3A52115BA3E700DE3BD1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A7D3A5A115BA41200DE3BD1 /* gen68k.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + 2A7D39D3115BA16100DE3BD1 /* bios.c in Sources */, + 2A7D39D4115BA16100DE3BD1 /* cd-macosx.c in Sources */, + 2A7D39D5115BA16100DE3BD1 /* cdbase.c in Sources */, + 2A7D39D6115BA16100DE3BD1 /* cheat.c in Sources */, + 2A7D39D7115BA16100DE3BD1 /* coffelf.c in Sources */, + 2A7D39D8115BA16100DE3BD1 /* cs0.c in Sources */, + 2A7D39D9115BA16100DE3BD1 /* cs1.c in Sources */, + 2A7D39DA115BA16100DE3BD1 /* cs2.c in Sources */, + 2A7D39DB115BA16100DE3BD1 /* debug.c in Sources */, + 2A7D39DC115BA16100DE3BD1 /* error.c in Sources */, + 2A7D39DD115BA16100DE3BD1 /* m68kc68k.c in Sources */, + 2A7D39DE115BA16100DE3BD1 /* m68kcore.c in Sources */, + 2A7D39DF115BA16100DE3BD1 /* m68kd.c in Sources */, + 2A7D39E1115BA16100DE3BD1 /* macjoy.c in Sources */, + 2A7D39E2115BA16100DE3BD1 /* memory.c in Sources */, + 2A7D39E3115BA16100DE3BD1 /* movie.c in Sources */, + 2A7D39E4115BA16100DE3BD1 /* netlink.c in Sources */, + 2A7D39E5115BA16100DE3BD1 /* peripheral.c in Sources */, + 2A7D39E6115BA16100DE3BD1 /* permacjoy.c in Sources */, + 2A7D39E7115BA16100DE3BD1 /* profile.c in Sources */, + 2A7D39E9115BA16100DE3BD1 /* scu.c in Sources */, + 2A7D39EA115BA16100DE3BD1 /* sh2core.c in Sources */, + 2A7D39EB115BA16100DE3BD1 /* sh2d.c in Sources */, + 2A7D39EC115BA16100DE3BD1 /* sh2idle.c in Sources */, + 2A7D39ED115BA16100DE3BD1 /* sh2int.c in Sources */, + 2A7D39EE115BA16100DE3BD1 /* sh2trace.c in Sources */, + 2A7D39EF115BA16100DE3BD1 /* smpc.c in Sources */, + 2A7D39F1115BA16100DE3BD1 /* vdp1.c in Sources */, + 2A7D39F2115BA16100DE3BD1 /* vdp2.c in Sources */, + 2A7D39F3115BA16100DE3BD1 /* vdp2debug.c in Sources */, + 2A7D39F4115BA16100DE3BD1 /* vidogl.c in Sources */, + 2A7D39F5115BA16100DE3BD1 /* vidshared.c in Sources */, + 2A7D39F6115BA16100DE3BD1 /* vidgcd.c in Sources */, + 2A7D39F7115BA16100DE3BD1 /* yabause.c in Sources */, + 2A7D39F8115BA16100DE3BD1 /* ygl.c in Sources */, + 2A7D3A48115BA3AF00DE3BD1 /* c68k.c in Sources */, + 2A7D3A49115BA3AF00DE3BD1 /* c68kexec.c in Sources */, + 2A7D3A4B115BA3AF00DE3BD1 /* gen68k.c in Sources */, + 2AF0CA51116146A400196BED /* YabauseGLView.m in Sources */, + 2AF0CA9911614DD500196BED /* YabauseController.m in Sources */, + 2A82CCA1116171DB00F15B02 /* PerCocoa.m in Sources */, + 2A9D7600116645D700A580EB /* sndmac.c in Sources */, + 2A9D7621116667C300A580EB /* YabausePrefsController.m in Sources */, + 2A7AB7CC117D1C1B00E47298 /* vidsoft.c in Sources */, + 2AD10F9E118BBC850088FB2C /* scsp2.c in Sources */, + 2AD10F9F118BBC850088FB2C /* snddummy.c in Sources */, + 2AD10FA0118BBC850088FB2C /* sndwav.c in Sources */, + 2AD10FDB118BCFA00088FB2C /* thr-dummy.c in Sources */, + 2A1145191385E5010087C872 /* YabauseButtonFormatter.m in Sources */, + F07BF9E414A1C2C500775818 /* yglshader.c in Sources */, + F07E62CB15054DCC00DF8A4E /* titan.c in Sources */, + F0E47FAD156C34EC001A1B51 /* osdcore.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 2A7D3A7B115BA63900DE3BD1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2A7D3A54115BA3E700DE3BD1 /* gen68k */; + targetProxy = 2A7D3A7A115BA63900DE3BD1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 2A7D3A99115BAB9E00DE3BD1 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 2A7D3A9A115BAB9E00DE3BD1 /* English */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 2A7D3A57115BA3E800DE3BD1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = C68K_GEN; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = gen68k; + }; + name = Debug; + }; + 2A7D3A58115BA3E800DE3BD1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = C68K_GEN; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = gen68k; + ZERO_LINK = NO; + }; + name = Release; + }; + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + COPY_PHASE_STRIP = NO; + ENABLE_OPENMP_SUPPORT = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_UNROLL_LOOPS = YES; + GCC_VERSION = 4.2; + INFOPLIST_FILE = "Yabause-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_CFLAGS = "-fnested-functions"; + PRODUCT_NAME = Yabause; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_OPENMP_SUPPORT = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_UNROLL_LOOPS = YES; + GCC_VERSION = 4.2; + INFOPLIST_FILE = "Yabause-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-fnested-functions"; + PRODUCT_NAME = Yabause; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + ENABLE_OPENMP_SUPPORT = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_SCSP2=1", + CRAB_REWRITE, + NO_CLI, + HAVE_LIBGL, + HAVE_LIBGLUT, + HAVE_SYS_TIME_H, + HAVE_GETTIMEOFDAY, + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1)", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1 = "VERSION=\\\"0.9.10\\\""; + GCC_VERSION = 4.2; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.6.sdk"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "USE_SCSP2=1", + CRAB_REWRITE, + NO_CLI, + HAVE_LIBGL, + HAVE_LIBGLUT, + HAVE_SYS_TIME_H, + HAVE_GETTIMEOFDAY, + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1)", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1 = "VERSION=\\\"0.9.10\\\""; + GCC_VERSION = 4.2; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH_ACTUAL)"; + OTHER_CFLAGS = ""; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.6.sdk"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2A7D3A59115BA40C00DE3BD1 /* Build configuration list for PBXNativeTarget "gen68k" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2A7D3A57115BA3E800DE3BD1 /* Debug */, + 2A7D3A58115BA3E800DE3BD1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Yabause" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Yabause" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/yabause/src/cocoa/YabauseButtonFormatter.h b/yabause/src/cocoa/YabauseButtonFormatter.h new file mode 100644 index 0000000000..e283c5b424 --- /dev/null +++ b/yabause/src/cocoa/YabauseButtonFormatter.h @@ -0,0 +1,40 @@ +/* Copyright 2011 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YabauseButtonFormatter_h +#define YabauseButtonFormatter_h + +#import + + +@interface YabauseButtonFormatter : NSFormatter { +} + +- (NSString *)stringForObjectValue:(id)obj; +- (BOOL)getObjectValue:(id *)obj + forString:(NSString *)str + errorDescription:(NSString **)err; + +- (BOOL)control:(NSControl*)control + textView:(NSTextView*)textView + doCommandBySelector:(SEL)commandSelector; + +@end /* @interface YabauseButtonFormatter */ + +#endif /* !YabauseButtonFormatter_h */ diff --git a/yabause/src/cocoa/YabauseButtonFormatter.m b/yabause/src/cocoa/YabauseButtonFormatter.m new file mode 100644 index 0000000000..258846b893 --- /dev/null +++ b/yabause/src/cocoa/YabauseButtonFormatter.m @@ -0,0 +1,112 @@ +/* Copyright 2011 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "YabauseButtonFormatter.h" +#include + +@implementation YabauseButtonFormatter + +- (NSString *)stringForObjectValue:(id)obj +{ + if(![obj isKindOfClass:[NSString class]]) { + return nil; + } + + if([obj length] >= 1) { + return [obj substringToIndex:1]; + } + else { + return [NSString string]; + } +} + +- (BOOL)getObjectValue:(id *)obj + forString:(NSString *)str + errorDescription:(NSString **)err +{ + if(obj) { + if([str length] >= 1) { + *obj = [NSString stringWithString:[str substringToIndex:1]]; + } + else { + *obj = [NSString string]; + } + } + + return YES; +} + +- (BOOL)isPartialStringValid:(NSString **)partialStringPtr + proposedSelectedRange:(NSRangePointer)proposedSelRangePtr + originalString:(NSString *)origString + originalSelectedRange:(NSRange)origSelRange + errorDescription:(NSString **)error +{ + NSString *rs = [[*partialStringPtr substringToIndex:1] uppercaseString]; + + *partialStringPtr = rs; + *proposedSelRangePtr = NSMakeRange(0, [rs length]); + + return NO; +} + +- (BOOL)control:(NSControl*)control + textView:(NSTextView*)textView + doCommandBySelector:(SEL)commandSelector +{ + BOOL result = NO; + + /* handle all the fun special cases... */ + if(commandSelector == @selector(insertNewline:)) { + [textView insertText:@"\u23CE"]; + result = YES; + } + else if(commandSelector == @selector(insertTab:)) { + [textView insertText:@"\u21E5"]; + result = YES; + } + else if(commandSelector == @selector(cancelOperation:)) { + [textView insertText:@"\u241B"]; + result = YES; + } + else if(commandSelector == @selector(deleteBackward:)) { + [textView insertText:@"\u232B"]; + result = YES; + } + else if(commandSelector == @selector(moveLeft:)) { + [textView insertText:@"\u2190"]; + result = YES; + } + else if(commandSelector == @selector(moveUp:)) { + [textView insertText:@"\u2191"]; + result = YES; + } + else if(commandSelector == @selector(moveRight:)) { + [textView insertText:@"\u2192"]; + result = YES; + } + else if(commandSelector == @selector(moveDown:)) { + [textView insertText:@"\u2193"]; + result = YES; + } + + return result; +} + +@end /* @implementation YabauseButtonFormatter */ diff --git a/yabause/src/cocoa/YabauseController.h b/yabause/src/cocoa/YabauseController.h new file mode 100644 index 0000000000..02bebff99e --- /dev/null +++ b/yabause/src/cocoa/YabauseController.h @@ -0,0 +1,69 @@ +/* Copyright 2010, 2012 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YabauseController_h +#define YabauseController_h + +#import + +@class YabauseGLView; +@class YabausePrefsController; + +@interface YabauseController : NSObject { + IBOutlet YabauseGLView *view; + IBOutlet NSPanel *prefsPane; + IBOutlet YabausePrefsController *prefs; + IBOutlet NSMenuItem *frameskip; + IBOutlet NSWindow *logWindow; + IBOutlet NSTextView *logView; + BOOL _running; + BOOL _paused; + NSLock *_runLock; + NSThread *_emuThd; + char *_bramFile; + char *_isoFile; + BOOL _doneExecuting; +} + +- (void)awakeFromNib; +- (void)dealloc; + +/* NSWindow delegate methods */ +- (BOOL)windowShouldClose:(id)sender; + +/* NSApplication delegate methods */ +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app; + +- (IBAction)showPreferences:(id)sender; +- (IBAction)runBIOS:(id)sender; +- (IBAction)runCD:(id)sender; +- (IBAction)runISO:(id)sender; +- (IBAction)toggleFullscreen:(id)sender; +- (IBAction)toggle:(id)sender; +- (IBAction)toggleFrameskip:(id)sender; +- (IBAction)pause:(id)sender; +- (IBAction)reset:(id)sender; + +- (YabauseGLView *)view; + +@end + +extern YabauseController *controller; + +#endif /* !YabauseController_h */ diff --git a/yabause/src/cocoa/YabauseController.m b/yabause/src/cocoa/YabauseController.m new file mode 100644 index 0000000000..313bff1339 --- /dev/null +++ b/yabause/src/cocoa/YabauseController.m @@ -0,0 +1,379 @@ +/* Copyright 2010, 2012 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "YabauseController.h" +#include "YabauseGLView.h" +#include "YabausePrefsController.h" + +#include "vdp1.h" +#include "vdp2.h" +#include "scsp.h" +#include "peripheral.h" +#include "cdbase.h" +#include "yabause.h" +#include "yui.h" +#include "PerCocoa.h" +#include "m68kc68k.h" +#include "cs0.h" + +YabauseController *controller; + +@interface YabauseController (InternalFunctions) +- (void)startEmulationWithCDCore:(int)cdcore CDPath:(const char *)fn; +- (void)emulationThread:(id)ignored; +- (void)terminateEmulation; +@end + +/* Menu Item tags. */ +enum { + tagVDP1 = 1, + tagNBG0 = 2, + tagNBG1 = 3, + tagNBG2 = 4, + tagNBG3 = 5, + tagRBG0 = 6, + tagFPS = 7 +}; + +static void FlipToggle(NSMenuItem *item) { + if([item state] == NSOffState) { + [item setState:NSOnState]; + } + else { + [item setState:NSOffState]; + } +} + +@implementation YabauseController + +- (void)awakeFromNib +{ + NSUserDefaults *p = [NSUserDefaults standardUserDefaults]; + + controller = self; + _running = NO; + _paused = NO; + _runLock = [[NSLock alloc] init]; + _emuThd = nil; + _bramFile = NULL; + _doneExecuting = NO; + + if([p boolForKey:@"Enable Frameskip"]) { + [frameskip setState:NSOnState]; + EnableAutoFrameSkip(); + } + else { + [frameskip setState:NSOffState]; + DisableAutoFrameSkip(); + } +} + +- (void)dealloc +{ + [_runLock release]; + [super dealloc]; +} + +- (BOOL)windowShouldClose:(id)sender +{ + [self terminateEmulation]; + return YES; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app +{ + [self terminateEmulation]; + return NSTerminateNow; +} + +- (IBAction)showPreferences:(id)sender +{ + [prefsPane makeKeyAndOrderFront:self]; +} + +- (IBAction)runBIOS:(id)sender +{ + /* This will simply start up the system with the dummy CD core, so there's + no way it'll actually read that there's a disc to be played. */ + [self startEmulationWithCDCore:CDCORE_DUMMY CDPath:NULL]; +} + +- (IBAction)runCD:(id)sender +{ + [self startEmulationWithCDCore:CDCORE_ARCH CDPath:NULL]; +} + +- (IBAction)runISO:(id)sender +{ + NSOpenPanel *p = [NSOpenPanel openPanel]; + NSArray *types = [NSArray arrayWithObjects:@"iso", @"cue", nil]; + + [p setAllowedFileTypes:types]; + if([p runModal] == NSFileHandlingPanelOKButton) { + NSString *fn = [[[p URLs] objectAtIndex:0] path]; + [self startEmulationWithCDCore:CDCORE_ISO + CDPath:[fn fileSystemRepresentation]]; + } +} + +- (IBAction)toggleFullscreen:(id)sender +{ + /* The view handles any heavy lifting here... */ + [view toggleFullscreen]; +} + +- (IBAction)toggle:(id)sender +{ + /* None of these will work unless we're running... */ + if(!_running) { + return; + } + + /* Flip the checkmark on the button. */ + FlipToggle((NSMenuItem *)sender); + + /* Do whatever this toggle is asking us to do. */ + switch([sender tag]) { + case tagVDP1: + ToggleVDP1(); + break; + + case tagNBG0: + ToggleNBG0(); + break; + + case tagNBG1: + ToggleNBG1(); + break; + + case tagNBG2: + ToggleNBG2(); + break; + + case tagNBG3: + ToggleNBG3(); + break; + + case tagRBG0: + ToggleRBG0(); + break; + + case tagFPS: + ToggleFPS(); + break; + } +} + +- (IBAction)toggleFrameskip:(id)sender +{ + NSUserDefaults *p = [NSUserDefaults standardUserDefaults]; + + if([sender state] == NSOnState) { + DisableAutoFrameSkip(); + [sender setState:NSOffState]; + [p setBool:NO forKey:@"Enable Frameskip"]; + } + else { + EnableAutoFrameSkip(); + [sender setState:NSOnState]; + [p setBool:YES forKey:@"Enable Frameskip"]; + } +} + +- (IBAction)pause:(id)sender +{ + if(_running) { + if(!_paused) { + _paused = YES; + + /* Mute the audio before we actually pause otherwise the user might + not like the result... */ + ScspMuteAudio(SCSP_MUTE_SYSTEM); + [_runLock lock]; + [sender setState:NSOnState]; + } + else { + _paused = NO; + [_runLock unlock]; + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + [sender setState:NSOffState]; + } + } +} + +- (IBAction)reset:(id)sender +{ + if(_running) { + /* Act as if the user pressed the reset button on the console. */ + YabauseResetButton(); + } +} + +- (YabauseGLView *)view +{ + return view; +} + +@end /* @implementation YabauseController */ + +@implementation YabauseController (InternalFunctions) + +- (void)startEmulationWithCDCore:(int)cdcore CDPath:(const char *)fn +{ + if(!_running) { + yabauseinit_struct yinit; + int initok; + NSString *bios = [prefs biosPath]; + NSString *mpeg = [prefs mpegPath]; + NSString *bram = [prefs bramPath]; + NSString *cart = [prefs cartPath]; + + yinit.percoretype = PERCORE_COCOA; + yinit.sh2coretype = SH2CORE_DEFAULT; + yinit.vidcoretype = [prefs videoCore]; + yinit.sndcoretype = [prefs soundCore]; + yinit.m68kcoretype = M68KCORE_C68K; + yinit.cdcoretype = cdcore; + yinit.carttype = [prefs cartType]; + yinit.regionid = [prefs region]; + yinit.biospath = ([bios length] > 0 && ![prefs emulateBios]) ? + [bios UTF8String] : NULL; + yinit.cdpath = fn; + yinit.buppath = NULL; + yinit.mpegpath = ([mpeg length] > 0) ? [mpeg UTF8String] : NULL; + yinit.videoformattype = ([prefs region] < 10) ? VIDEOFORMATTYPE_NTSC : + VIDEOFORMATTYPE_PAL; + yinit.frameskip = [frameskip state] == NSOnState; + yinit.clocksync = 0; + yinit.basetime = 0; + yinit.usethreads = 0; + + /* Set up the internal save ram if specified. */ + if([bram length] > 0) { + const char *tmp = [bram UTF8String]; + yinit.buppath = _bramFile = strdup(tmp); + } + + if(fn) + _isoFile = strdup(fn); + + /* Set up the cartridge stuff based on what was selected. */ + if(yinit.carttype == CART_NETLINK) { + yinit.cartpath = NULL; + yinit.netlinksetting = ([cart length] > 0) ? + [cart UTF8String] : NULL; + } + else { + yinit.cartpath = ([cart length] > 0) ? [cart UTF8String] : NULL; + yinit.netlinksetting = NULL; + } + + if(cdcore == CDCORE_DUMMY && !yinit.biospath) { + NSRunAlertPanel(@"Yabause Error", @"You must specify a BIOS file " + "(and have BIOS emulation disabled) in order to " + "run the BIOS.", @"OK", NULL, NULL); + return; + } + + [[view openGLContext] makeCurrentContext]; + initok = YabauseInit(&yinit); + [NSOpenGLContext clearCurrentContext]; + if (initok != 0) { + return; + } + + YabauseSetDecilineMode(1); + + _running = YES; + _doneExecuting = NO; + + [view showWindow]; + + /* The emulation itself takes place in a separate thread from the main + GUI thread. */ + [NSThread detachNewThreadSelector:@selector(emulationThread:) + toTarget:self + withObject:nil]; + } +} + +- (void)emulationThread:(id)ignored +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + CGLContextObj cxt; + + _emuThd = [NSThread currentThread]; + + /* Make the OpenGL context current for this thread, otherwise we will be + drawing to nothingness. */ + [[view openGLContext] makeCurrentContext]; + + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + + while(_running) { + /* If we get paused from the GUI, we'll end up waiting in this lock + here... Maybe not the most clear way to do it, but it works. */ + [_runLock lock]; + + /* Make sure the main thread doesn't attempt to flip the buffer before + this thread is done rendering. */ + cxt = CGLGetCurrentContext(); + CGLLockContext(cxt); + + /* Shortcut a function call here... We should technically be doing a + PERCore->HandleEvents(), but that function simply calls YabauseExec() + anyway... so cut out the middleman. */ + YabauseExec(); + + CGLUnlockContext(cxt); + [_runLock unlock]; + } + + ScspMuteAudio(SCSP_MUTE_SYSTEM); + + _doneExecuting = YES; + [pool release]; +} + +- (void)terminateEmulation +{ + _running = NO; + + /* Wait for the thread to die, and then clean up after it. */ + if(_emuThd) { + while(!_doneExecuting) { + sched_yield(); + } + + YabauseDeInit(); + + free(_bramFile); + _bramFile = NULL; + free(_isoFile); + _isoFile = NULL; + + _emuThd = nil; + } +} + +@end /* @implementation YabauseController (InternalFunctions) */ diff --git a/yabause/src/cocoa/YabauseGLView.h b/yabause/src/cocoa/YabauseGLView.h new file mode 100644 index 0000000000..110d1317a3 --- /dev/null +++ b/yabause/src/cocoa/YabauseGLView.h @@ -0,0 +1,48 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YabauseGLView_h +#define YabauseGLView_h + +#import +#include +#include + +@interface YabauseGLView : NSOpenGLView { + IBOutlet NSWindow *window; + BOOL _isFullscreen; + NSPoint _mouseLoc; +} + +- (id)initWithFrame:(NSRect)frameRect; + +- (void)toggleFullscreen; + +- (BOOL)acceptsFirstResponder; +- (void)keyDown:(NSEvent *)event; +- (void)keyUp:(NSEvent *)event; + +- (void)showWindow; + +- (void)reshape; +- (void)drawRect:(NSRect)rect; + +@end + +#endif /* !YabauseGLView_h */ diff --git a/yabause/src/cocoa/YabauseGLView.m b/yabause/src/cocoa/YabauseGLView.m new file mode 100644 index 0000000000..3a196ca407 --- /dev/null +++ b/yabause/src/cocoa/YabauseGLView.m @@ -0,0 +1,217 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "YabauseGLView.h" + +#include "peripheral.h" +#include "vdp1.h" + +@interface YabauseGLView (InternalFunctions) +- (NSScreen *)screen; +- (CGDirectDisplayID)screenID; + +/* These are nice to have, but not really necessary to things... */ +- (float)width; +- (float)height; +@end + +@implementation YabauseGLView + +- (id)initWithFrame:(NSRect)frameRect +{ + NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFAWindow, + NSOpenGLPFANoRecovery, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFADepthSize, 32, + NSOpenGLPFADoubleBuffer, + 0 + }; + + NSOpenGLPixelFormat *fmt; + + fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + if(fmt == nil) { + [fmt release]; + return nil; + } + + if(!(self = [super initWithFrame:frameRect pixelFormat:fmt])) { + [fmt release]; + return nil; + } + + _isFullscreen = NO; + + [fmt release]; + + return self; +} + +- (void)toggleFullscreen +{ + CGError err; + CGDisplayFadeReservationToken token; + CGDirectDisplayID d = [self screenID]; + + err = CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, + &token); + + if(err == kCGErrorSuccess) { + CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, 0, 0, 0, 1); + } + + if(!_isFullscreen) { + [self enterFullScreenMode:[self screen] withOptions:nil]; + + /* Hide the cursor, but store its location so we can restore it later. + Also, disassociate the mouse and the cursor position. */ + CGDisplayHideCursor(d); + _mouseLoc = [NSEvent mouseLocation]; + CGDisplayMoveCursorToPoint(d, CGPointZero); + CGAssociateMouseAndMouseCursorPosition(FALSE); + } + else { + CGPoint mousePoint; + int height = CGDisplayPixelsHigh(d); + + mousePoint.x = _mouseLoc.x; + mousePoint.y = height - _mouseLoc.y; + + /* Show the mouse pointer, and reassociate it with the mouse. */ + CGAssociateMouseAndMouseCursorPosition(TRUE); + CGDisplayMoveCursorToPoint(d, mousePoint); + CGDisplayShowCursor(d); + + [self exitFullScreenModeWithOptions:nil]; + [[self window] makeFirstResponder:self]; + } + + if(err == kCGErrorSuccess) { + CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, 0, 0, 0, 0); + CGReleaseDisplayFadeReservation(token); + } + + if(VIDCore) + VIDCore->Resize([self width], [self height], 0); + + _isFullscreen = !_isFullscreen; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)keyDown:(NSEvent *)event +{ + if([[event charactersIgnoringModifiers] length] >= 1) { + PerKeyDown([[event charactersIgnoringModifiers] characterAtIndex:0]); + } +} + +- (void)keyUp:(NSEvent *)event +{ + if([[event charactersIgnoringModifiers] length] >= 1) { + PerKeyUp([[event charactersIgnoringModifiers] characterAtIndex:0]); + } +} + +- (void)showWindow +{ + [window makeKeyAndOrderFront:self]; +} + +- (void)reshape +{ + CGLContextObj cxt = CGLGetCurrentContext(); + + /* Make sure that the emulation thread doesn't attempt to do any OpenGL + calls during the resize event, otherwise one of the two will crash. */ + CGLLockContext(cxt); + + if(VIDCore) + VIDCore->Resize([self width], [self height], 0); + + CGLUnlockContext(cxt); + + [super reshape]; +} + +- (void)drawRect:(NSRect)rect +{ + CGLContextObj cxt = CGLGetCurrentContext(); + + /* Make sure that the emulation thread doesn't attempt to do any OpenGL + calls during the flush to the screen. */ + CGLLockContext(cxt); + [[self openGLContext] flushBuffer]; + CGLUnlockContext(cxt); +} + +@end /* @implementation YabauseGLView */ + +@implementation YabauseGLView (InternalFunctions) + +- (NSScreen *)screen +{ + NSArray *screens = [NSScreen screens]; + NSEnumerator *i = [screens objectEnumerator]; + NSScreen *obj; + NSRect f = [window frame]; + NSRect sf; + + /* Look for the screen that has the main window on it. */ + while((obj = (NSScreen *)[i nextObject])) { + sf = [obj frame]; + + if(f.origin.x >= sf.origin.x && f.origin.y >= sf.origin.y && + f.origin.x <= sf.origin.x + sf.size.width && + f.origin.y <= sf.origin.y + sf.size.height) { + return obj; + } + } + + /* Punt. */ + return [NSScreen mainScreen]; +} + +- (CGDirectDisplayID)screenID +{ + NSScreen *s = [self screen]; + NSDictionary *d = [s deviceDescription]; + NSNumber *n = (NSNumber *)[d objectForKey:@"NSScreenNumber"]; + + return (CGDirectDisplayID)[n unsignedIntValue]; +} + +- (float)width +{ + return [self bounds].size.width; +} + +- (float)height +{ + return [self bounds].size.height; +} + +@end /* @implementation YabauseGLView (InternalFunctions) */ diff --git a/yabause/src/cocoa/YabausePrefsController.h b/yabause/src/cocoa/YabausePrefsController.h new file mode 100644 index 0000000000..d82df96eb1 --- /dev/null +++ b/yabause/src/cocoa/YabausePrefsController.h @@ -0,0 +1,80 @@ +/* Copyright 2010-2011 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YabausePrefsController_h +#define YabausePrefsController_h + +#import + +@interface YabausePrefsController : NSObject { + IBOutlet NSTextField *biosPath; + IBOutlet NSTextField *bramPath; + IBOutlet NSButton *cartBrowse; + IBOutlet NSTextField *cartPath; + IBOutlet NSPopUpButton *cartType; + IBOutlet NSButton *emulateBios; + IBOutlet NSTextField *mpegPath; + IBOutlet NSPopUpButton *region; + IBOutlet NSPopUpButton *soundCore; + IBOutlet NSPopUpButton *videoCore; + IBOutlet NSPanel *prefsPane; + IBOutlet NSPanel *buttonAssignment; + IBOutlet NSTextField *buttonBox; + + int _cartType; + int _region; + int _soundCore; + int _videoCore; + + NSUserDefaults *_prefs; +} + +- (void)awakeFromNib; +- (void)dealloc; + +/* NSTextField delegate methods */ +- (void)controlTextDidEndEditing:(NSNotification *)notification; + +- (IBAction)cartSelected:(id)sender; +- (IBAction)regionSelected:(id)sender; +- (IBAction)soundCoreSelected:(id)sender; +- (IBAction)videoCoreSelected:(id)sender; +- (IBAction)biosBrowse:(id)sender; +- (IBAction)mpegBrowse:(id)sender; +- (IBAction)bramBrowse:(id)sender; +- (IBAction)cartBrowse:(id)sender; +- (IBAction)biosToggle:(id)sender; +- (IBAction)buttonSelect:(id)sender; + +- (IBAction)buttonSetOk:(id)sender; +- (IBAction)buttonSetCancel:(id)sender; + +- (int)cartType; +- (int)region; +- (int)soundCore; +- (int)videoCore; +- (NSString *)biosPath; +- (BOOL)emulateBios; +- (NSString *)mpegPath; +- (NSString *)bramPath; +- (NSString *)cartPath; + +@end + +#endif /* !YabausePrefsController_h */ diff --git a/yabause/src/cocoa/YabausePrefsController.m b/yabause/src/cocoa/YabausePrefsController.m new file mode 100644 index 0000000000..66692a44af --- /dev/null +++ b/yabause/src/cocoa/YabausePrefsController.m @@ -0,0 +1,449 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "YabausePrefsController.h" + +#include "cs0.h" +#include "vidogl.h" +#include "vidsoft.h" +#include "scsp.h" +#include "smpc.h" +#include "sndmac.h" +#include "PerCocoa.h" + +@implementation YabausePrefsController + +- (void)awakeFromNib +{ + _cartType = CART_NONE; + _region = REGION_AUTODETECT; + _soundCore = SNDCORE_MAC; + _videoCore = VIDCORE_SOFT; + + _prefs = [[NSUserDefaults standardUserDefaults] retain]; + + /* Fill in all the settings. */ + if([_prefs objectForKey:@"BIOS Path"]) { + [biosPath setStringValue:[_prefs stringForKey:@"BIOS Path"]]; + } + else { + [_prefs setObject:@"" forKey:@"BIOS Path"]; + } + + if([_prefs objectForKey:@"Emulate BIOS"]) { + [emulateBios setState:[_prefs boolForKey:@"Emulate BIOS"] ? + NSOnState : NSOffState]; + } + else { + [_prefs setBool:YES forKey:@"Emulate BIOS"]; + } + + if([_prefs objectForKey:@"MPEG ROM Path"]) { + [mpegPath setStringValue:[_prefs objectForKey:@"MPEG ROM Path"]]; + } + else { + [_prefs setObject:@"" forKey:@"MPEG ROM Path"]; + } + + if([_prefs objectForKey:@"BRAM Path"]) { + [bramPath setStringValue:[_prefs objectForKey:@"BRAM Path"]]; + } + else { + [_prefs setObject:@"" forKey:@"BRAM Path"]; + } + + if([_prefs objectForKey:@"Cartridge Path"]) { + [cartPath setStringValue:[_prefs objectForKey:@"Cartridge Path"]]; + } + else { + [_prefs setObject:@"" forKey:@"Cartridge Path"]; + } + + if([_prefs objectForKey:@"Cartridge Type"]) { + _cartType = [_prefs integerForKey:@"Cartridge Type"]; + + if(_cartType != CART_NONE && _cartType != CART_DRAM8MBIT && + _cartType != CART_DRAM32MBIT) { + [cartPath setEnabled:YES]; + [cartBrowse setEnabled:YES]; + [[cartPath cell] setPlaceholderString:@"Not Set"]; + } + + [cartType selectItemWithTag:_cartType]; + } + else { + [_prefs setInteger:CART_NONE forKey:@"Cartridge Type"]; + } + + if([_prefs objectForKey:@"Region"]) { + _region = [_prefs integerForKey:@"Region"]; + [region selectItemWithTag:_region]; + } + else { + [_prefs setInteger:REGION_AUTODETECT forKey:@"Region"]; + } + + if([_prefs objectForKey:@"Sound Core"]) { + _soundCore = [_prefs integerForKey:@"Sound Core"]; + [soundCore selectItemWithTag:_soundCore]; + } + else { + [_prefs setInteger:SNDCORE_MAC forKey:@"Sound Core"]; + } + + if([_prefs objectForKey:@"Video Core"]) { + _videoCore = [_prefs integerForKey:@"Video Core"]; + [videoCore selectItemWithTag:_videoCore]; + } + else { + [_prefs setInteger:VIDCORE_OGL forKey:@"Video Core"]; + } + + [_prefs synchronize]; +} + +- (void)dealloc +{ + [_prefs release]; + [super dealloc]; +} + +- (void)controlTextDidEndEditing:(NSNotification *)notification +{ + id obj = [notification object]; + + if(obj == biosPath) { + [_prefs setObject:[biosPath stringValue] forKey:@"BIOS Path"]; + } + else if(obj == bramPath) { + [_prefs setObject:[bramPath stringValue] forKey:@"BRAM Path"]; + } + else if(obj == mpegPath) { + [_prefs setObject:[mpegPath stringValue] forKey:@"MPEG ROM Path"]; + } + else if(obj == cartPath) { + [_prefs setObject:[cartPath stringValue] forKey:@"Cartridge Path"]; + } + + [_prefs synchronize]; +} + +- (IBAction)cartSelected:(id)sender +{ + _cartType = [[sender selectedItem] tag]; + + switch(_cartType) { + case CART_NONE: + case CART_DRAM8MBIT: + case CART_DRAM32MBIT: + [cartPath setEnabled:NO]; + [cartPath setStringValue:@""]; + [cartBrowse setEnabled:NO]; + [[cartPath cell] setPlaceholderString: + @"No file required for the selected cartridge"]; + break; + + default: + [cartPath setEnabled:YES]; + [cartBrowse setEnabled:YES]; + [[cartPath cell] setPlaceholderString:@"Not Set"]; + break; + } + + /* Update the preferences file. */ + [_prefs setObject:[cartPath stringValue] forKey:@"Cartridge Path"]; + [_prefs setInteger:_cartType forKey:@"Cartridge Type"]; + [_prefs synchronize]; +} + +- (IBAction)regionSelected:(id)sender +{ + _region = [[sender selectedItem] tag]; + + /* Update the preferences file. */ + [_prefs setInteger:_region forKey:@"Region"]; + [_prefs synchronize]; +} + +- (IBAction)soundCoreSelected:(id)sender +{ + _soundCore = [[sender selectedItem] tag]; + + /* Update the preferences file. */ + [_prefs setInteger:_soundCore forKey:@"Sound Core"]; + [_prefs synchronize]; +} + +- (IBAction)videoCoreSelected:(id)sender +{ + _videoCore = [[sender selectedItem] tag]; + + /* Update the preferences file. */ + [_prefs setInteger:_videoCore forKey:@"Video Core"]; + [_prefs synchronize]; +} + +- (IBAction)biosBrowse:(id)sender +{ + NSOpenPanel *p = [NSOpenPanel openPanel]; + + [p setTitle:@"Select a Saturn BIOS ROM"]; + + if([p runModal] == NSFileHandlingPanelOKButton) { + [biosPath setStringValue:[[[p URLs] objectAtIndex:0] path]]; + + /* Update the preferences file. */ + [_prefs setObject:[biosPath stringValue] forKey:@"BIOS Path"]; + [_prefs synchronize]; + } +} + +- (IBAction)mpegBrowse:(id)sender +{ + NSOpenPanel *p = [NSOpenPanel openPanel]; + + [p setTitle:@"Select a MPEG ROM"]; + + if([p runModal] == NSFileHandlingPanelOKButton) { + [mpegPath setStringValue:[[[p URLs] objectAtIndex:0] path]]; + + /* Update the preferences file. */ + [_prefs setObject:[mpegPath stringValue] forKey:@"MPEG ROM Path"]; + [_prefs synchronize]; + } +} + +- (IBAction)bramBrowse:(id)sender +{ + NSOpenPanel *p = [NSOpenPanel openPanel]; + + [p setTitle:@"Select a BRAM File"]; + + if([p runModal] == NSFileHandlingPanelOKButton) { + [bramPath setStringValue:[[[p URLs] objectAtIndex:0] path]]; + + /* Update the preferences file. */ + [_prefs setObject:[bramPath stringValue] forKey:@"BRAM Path"]; + [_prefs synchronize]; + } +} + +- (IBAction)cartBrowse:(id)sender +{ + NSOpenPanel *p = [NSOpenPanel openPanel]; + + [p setTitle:@"Select the Cartridge File"]; + + if([p runModal] == NSFileHandlingPanelOKButton) { + [cartPath setStringValue:[[[p URLs] objectAtIndex:0] path]]; + + /* Update the preferences file. */ + [_prefs setObject:[cartPath stringValue] forKey:@"Cartridge Path"]; + [_prefs synchronize]; + } +} + +- (IBAction)biosToggle:(id)sender +{ + /* Update the preferences file. */ + [_prefs setBool:([sender state] == NSOnState) forKey:@"Emulate BIOS"]; + [_prefs synchronize]; +} + +- (IBAction)buttonSelect:(id)sender +{ + NSInteger rv; + NSInteger tag = [sender tag]; + int port = tag > 12 ? 1 : 0; + u8 num = tag > 12 ? tag - 13 : tag; + u32 value = PERCocoaGetKey(num, port); + unichar ch; + + /* Fill in current setting from prefs */ + if(value != (u32)-1) { + switch(value) { + case '\r': + ch = 0x23CE; + break; + + case '\t': + ch = 0x21E5; + break; + + case 27: + ch = 0x241B; + break; + + case 127: + ch = 0x232B; + break; + + case NSLeftArrowFunctionKey: + ch = 0x2190; + break; + + case NSUpArrowFunctionKey: + ch = 0x2191; + break; + + case NSRightArrowFunctionKey: + ch = 0x2192; + break; + + case NSDownArrowFunctionKey: + ch = 0x2193; + break; + + default: + ch = toupper(((int)value)); + } + + [buttonBox setStringValue:[NSString stringWithCharacters:&ch length:1]]; + } + else { + [buttonBox setStringValue:@""]; + } + + /* Open up the sheet and ask for the user's input */ + [NSApp beginSheet:buttonAssignment + modalForWindow:prefsPane + modalDelegate:self + didEndSelector:nil + contextInfo:NULL]; + + rv = [NSApp runModalForWindow:buttonAssignment]; + [NSApp endSheet:buttonAssignment]; + [buttonAssignment orderOut:nil]; + + /* Did the user accept what they put in? */ + if(rv == NSOKButton) { + NSString *s = [buttonBox stringValue]; + u32 val; + + /* This shouldn't happen... */ + if([s length] < 1) { + return; + } + + switch([s characterAtIndex:0]) { + case 0x23CE: /* Return */ + val = '\r'; + break; + + case 0x21E5: /* Tab */ + val = '\t'; + break; + + case 0x241B: /* Escape */ + val = 27; + break; + + case 0x232B: /* Backspace */ + val = 127; + break; + + case 0x2190: /* Left */ + val = NSLeftArrowFunctionKey; + break; + + case 0x2191: /* Up */ + val = NSUpArrowFunctionKey; + break; + + case 0x2192: /* Right */ + val = NSRightArrowFunctionKey; + break; + + case 0x2193: /* Down */ + val = NSDownArrowFunctionKey; + break; + + default: + val = tolower([s characterAtIndex:0]); + } + + /* Update the key mapping, if we're already running. This will also save + the key to the preferences. */ + if(tag > 12) { + PERCocoaSetKey(val, tag - 13, 1); + } + else { + PERCocoaSetKey(val, tag, 0); + } + } +} + +- (IBAction)buttonSetOk:(id)sender +{ + [NSApp stopModalWithCode:NSOKButton]; +} + +- (IBAction)buttonSetCancel:(id)sender +{ + [NSApp stopModalWithCode:NSCancelButton]; +} + +- (int)cartType +{ + return _cartType; +} + +- (int)region +{ + return _region; +} + +- (int)soundCore +{ + return _soundCore; +} + +- (int)videoCore +{ + return _videoCore; +} + +- (NSString *)biosPath +{ + return [biosPath stringValue]; +} + +- (BOOL)emulateBios +{ + return [emulateBios state] == NSOnState; +} + +- (NSString *)mpegPath +{ + return [mpegPath stringValue]; +} + +- (NSString *)bramPath +{ + return [bramPath stringValue]; +} + +- (NSString *)cartPath +{ + return [cartPath stringValue]; +} + +@end /* @implementation YabausePrefsController */ diff --git a/yabause/src/cocoa/main.m b/yabause/src/cocoa/main.m new file mode 100644 index 0000000000..56c0be7036 --- /dev/null +++ b/yabause/src/cocoa/main.m @@ -0,0 +1,99 @@ +/* Copyright 2010, 2012 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#import +#include + +#include "yui.h" +#include "peripheral.h" +#include "m68kcore.h" +#include "m68kc68k.h" +#include "permacjoy.h" +#include "sndmac.h" +#include "vidogl.h" +#include "vidsoft.h" +#include "vidgcd.h" +#include "cs0.h" +#include "vdp2.h" + +#include "PerCocoa.h" +#include "YabauseController.h" +#include "YabauseGLView.h" + +M68K_struct *M68KCoreList[] = { + &M68KDummy, + &M68KC68K, + NULL +}; + +SH2Interface_struct *SH2CoreList[] = { + &SH2Interpreter, + &SH2DebugInterpreter, + NULL +}; + +PerInterface_struct *PERCoreList[] = { + &PERDummy, + &PERCocoa, + &PERMacJoy, + NULL +}; + +CDInterface *CDCoreList[] = { + &DummyCD, + &ISOCD, + &ArchCD, + NULL +}; + +SoundInterface_struct *SNDCoreList[] = { + &SNDDummy, + &SNDMac, + NULL +}; + +VideoInterface_struct *VIDCoreList[] = { + &VIDDummy, + &VIDOGL, + &VIDSoft, + &VIDGCD, + NULL +}; + +void YuiErrorMsg(const char *string) { + NSString *str = [NSString stringWithUTF8String:string]; + dispatch_async(dispatch_get_main_queue(), ^{ + NSRunAlertPanel(@"Yabause Error", str, @"OK", NULL, NULL); + }); +} + +void YuiSetVideoAttribute(int type, int val) { +} + +int YuiSetVideoMode(int width, int height, int bpp, int fullscreen) { + return 0; +} + +void YuiSwapBuffers(void) { + [[controller view] setNeedsDisplay:YES]; +} + +int main(int argc, char *argv[]) { + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/yabause/src/cocoa/resources/controller.png b/yabause/src/cocoa/resources/controller.png new file mode 100644 index 0000000000000000000000000000000000000000..88e2a4344ba1ac6d7ee87abe44781c40ca09706a GIT binary patch literal 96999 zcmcG0WmuF^xa|ZDqaZqTsf5zqQi6h{ATV?d-OV5}fOIO-h=d?YGca^XcMl!X-6eU( zbI(1`{d0cZ=lq&s*w1|5xA(i>wchovJ>lxAuOLL!L;wH)qWJpdTL1v$0002J3GlF= z;9U$5V_$HrWK?7TfRZj@+=xx=-&F4My6&1zmhN7ru5f^ig_9YaQPIKF3jP*uYT@J3 z4VM4_5k879WwgAfcNz$j;cySG`T_*~=2lkiuh3r^uc+v8_`?Z3~+D&)ie z`=6Pd|92E~Ao~9d|KA!j1pV(j`X8_LzdVLu`rmx~*KPa{*ZTh%BcuOcQ;xmF|NA{! z&u*5P!(6kw|Fnk2vwnMDeis17SCGW6SdNhW)a0ay zG6wQ#f(ZhFY@S~rPBwQlCCRVGCqsEq z5R69>pDWXGOh7deP1$?&!jLDiOnRkK^Q6@%%7$vjcjfqpWq;~vMd1g%#wx$9v7c%^ zUf<}29cTBjkHJ#TWpvHP`bFTtH1^?U+{|ZJ+YA1T|5lFYLC^=r@}2;d0aF z2v#6bmxHSn{docygnwUk#V=4tV~_A5UjiSL&5I2Is3g*TgL9DEdwX*g-{5=B;(NBE znBlXQ^78DP#OatOAVfD-(L_g6b99;8cdB=<_V#l7&g1s_uUdN@IkVF4(K5r`MIrZx z!$>E?GPCw}zJa_hT{^_H%@5|BSFx%J(bNfCPhQ0+D@0L#YPSAO$sk~@F4iHpthZDA zGOG0C(XxJ2se6kr8P(p^w=WBm%uQ9rm6aY}cidJhY(}+X7^FUow}q2eMN#puF`m&d zE8#e3WaHov=`_Pef4wWPEP---jx!EJ0xJRQIxg6UEMq!Pt@RRI1$h=b@n z;jBnw)|=)IaK)cT%XCOc2@ekM9&fP=3FR`+*g@0AM=N<9d#GP|d2a4%7WT#^XXoc{ zs{u8F%N^0;5BK%{g>QO!;JPXjHoDXvNx@~@c+IR|<>(jAHpIV@qs_KkX5Q(^?)co+ z!Nv_J613C-a>iR#5zfc6)tU#`sbc`9>SKv8uu0D2PS1;bjcQ)D@-<%XuVxlL9Kx)= zRtn8`vuX77;!9n$+%a_wN>x=>=0=GGP;~Hgh(H98f=HnaAY`_yo~+(Rdv1%0&l$%R z$0gO~hbodQlfL;e<9!o{gpHg~%YpR2y)|y@FL?ZKuXD3=?se8tu71IBD9|ZU{cts- z8DD|SMvNC2C8R$udkc_=mAbvSW`2`s@Vq3c`)-|J(l+=pv{#7C?u^ndRf$TOdP)E? zaIhmwHi`?%jSMDAE-5W8gBTUfykvK|9z3rY`FX(<6%5W4UykB;j8|MAbWzr*#hKW{ zJ_c}3T?3(euw&>k{_f`=*T>;ecZU;(UUNiLGf#LkY^+^O3#pOGmcxVt7rz!XUy&Zj zcp<|(*>8W)54+goxFN3N!*lkUb@9Vy8vT50yY61C2q&S@=y@rp`Fe)l+YirX_Szb~ zXQ!>BTwS}rJk$J4(R9)NL&&;rHBnzW{#*Hdp!7C2OjW0kXHZsZ%h)v=ygV)O_ZuI{ zeZx)BUaej@1|(-uejPx~Va;QwRbesOm9O!WA1~U)sx;7RXYv!5`mfSBAQ537pqik( z)S8%f-zH!?JWzT_Z*w;0>i6TOGKZs;?xxfiewXolOcKvJLmAiaiQYOK-JuoZStOaS z-oHg|JR@fShrDG$5lkZ#c@)t0Q=h5D{N#DGEtF&8Ch}C$gub!>tm~1fW!sn{yH74d z&zI3Y{i%YQVq#)OkC9e@4&hE(^RZGBsZAWXt{Vq}W6P!IUo`JzYG#@{e{G(QW{`Rz zNZK-MD)|Ec0B%o9 zIPvuEcK_~!LF}JutJn-{Z8OPW$AEa1=C?O{UQPN3&EWe6DEq1Mf|q~4;yMdqQ_uIB zni}ygO6h0}kNGIUNa=lm0DzC}l}5zR?1#+VH~|l7hkgftW+BEe(5ut;j;?%UpU~)8 zu+#exl3AvrcWl;e7t2;d(%$rpkAv%By>&^v&(UQ|NTZ zOH5VUX9kTemh8gR{hIX*9jT+PClf@awd&H{NGg8I?}?mWo*=1Vf&lh%8};w%6#yHP zA%Y=`x36WWzdZTE{E&wnav-@ABpn@@!DtTy+^VTt7WsToEimNwz;UcNI;3B3_1l=F z@^ScbEUKdYjitvgwO&f+i_iJfw?L1&m8-N>>Y{WJm!%`@USP!;Dt^dQrH$E*aGYT4 z)PK(VjT6+t((9eh+$-S|v)<*M&e;slrquq?jhb(E!Dsw<2!29xHwqXHA&+K{VRlf5 zDaueuv{m5{^8?mpr~$j{VU@+<+sA3t(O5!F3xl6yV9KE4>d{ zq@>VSG!RocnRo|UES@fhzX?T7)vY4t8vU-$u3~JpsQwcPVv?j?Zq|gGM9&+q_O6cK z6=>i51eC3H$Fs6pW%yXyR*v8T3m#$EkUW!N}L7cCa^uC#o;d5m16HpDCWLJh@Sb%^`MyKeJL`IF{TrCQ2 zFtE_%@1GVm%EG4=#nkHk6p&;3eJ*WUx;#@Q^znwSke$fk744wG4`4cd31VkcegZzm zL3?Lqz=2ZarO&d;L@g@dB3nT`_-a6N$E=wP$#SRXpG>dQMcw9ECQck&Rs6XDdqTrb z3!faSI(~5eLrR39=guVW!MyK&!&cMncGLW`h?|kb#Ke%}0r7pxbJjT$b!QyfFfyPB z`1MS+i}@veF)^FCZqugRGjd>KYqKe&$Nh0&##0fS4Wx-(k=;Zjl+9VvN7u3+b$R-- zO^((v&m)si+>V9-YQK6IyZ(qTWE^an|e7n7} z8O`HN^6*`br_3!}6n$uJQvpDUf>;LhYuz_%#J%>?xiigwN%-5i0y^lDoukY{T%7A| z%)`xFL!MY4ICQO@e*$1^l=9dbbHkDbub7Sc*nc1!BWQ=Yj{v8HazQ> zwNSi@p%b5mKlmH(yH|rh;S`FjW{zi3xr|0u8xLeiUft(Yv=8SR*Hkkh!4hs|vN->< ze|T(wvh2qNJy`2@6Noy@r1jzm$RgGtuW+d!!c{U!z#Ti$t>{C;CwC@~I=pG@2RpXiG_T`0 zmCt2z>Z7kyC{6s)*kr|!?tlCDThmbwyZ^|owv1 zN0IL@196Gp=Dv*lTxMH}$=X@mdaB#Xj-*Z&Vv}H#WizmhD90mbz5hOsN~l&}KzcM& zh28I_CIg4^ZVetW-7T9$WAY4?)}F`8eRG9)I}9A3e%HD%9DeV8?D0g(uWGK^-*uj+ zr6K7VCZr6k+BfZdga#bvL``?@!QpibjH=Q?zv}ik6muh&gWo(;+9%i(?;uTPq_Q&^ z8SvZ43gseWyfXt5p;o7`7=sSjzBia0LpZLS`k|5pZHX2PE9a=fGee{!_M{BiRs3JY ziubzuN~^Kr!-7LT)Y z)=1_!^Q%&bbOvQb$>rrl)9o*Yn~hg@E?64zel0tatG216d{qPsLdG$^U(xzb4T3&1 zGxMHGM(^1)%0fm1{rpsZ{s#MWK(1>e|cA_1qa`Y1)*3S~xW#fu)YB{h6DJVa^x0Z)~!-4K3A>re{FxUie(IY@e{A$vOeV*jQ5({I_bP# zt{OQp8v=D{DV%G%MQEC^$q!|Gl1*XwG-O;JM6H^1_2M@)8-x4PgCkXiw$#N6M;jVt zv)ola?MVw8%A0Z`p0~ddWf7G*|WLqnQ%sbLw-n@)JpX z2($)Y;R!z{F*NXfPjQ)Gs0+AQjE?i2em3*|O0dMIUxmWR`_EG28SN6K20EOH^GA05 zABfZI+uem*&OkK5^yNM4puh*2o_~a!{`SX)zpceFfk2t`yOA*P#c$8dE8aL$)899Q ziNT8j$UsIFVZ_?Y?0AwcA(&1cp1Jm>oq8wIswk?cw;j4$!9lZNM?AO3ZgILz*p?>1 z!B}xc#LT-99u*&&^wpM}T7VdzIWm~>-uY{m1P8N2DG9&J6JH80gNrvcgR$*xA&u;= z`>EQ+20m-#Y7&Qh)e_=jV$hxyDeCitRX6Kvo?Fd2v7$He_G#;DV}O41eu?v3JXwtO zVH5|qgbbL47)P?ThaTJtInhOTJ{q}iWRM;muuR!{@#lA${1ZZA40(MSCxtsJGA*iV z?4n(=_11lxgR+hd&1G`kj0Mxii`t2XJro~T)vF<>Oj_awee?+Dn?#P3qmsMJ68|cP z8MM?lUUP*f8-9Ft0J~E9tT=+P10r@ZqF%fqInzx&d-niWGbZWLVrt?x0cT=d7Q59# zm_VAM)Y-@29N(XgndUx;h;oDZZ4?+0K5N|K3O<$GUDd}aX>iQ>0V zHew|I+s?-__}K;XduiBy^t&7p!N5Tie5kNMGnW5_S083Cy+S z5S`?_NTtlw+8H8-jdgRO0+KjYoMy2Y_74ie&rc~j-?kGd?GxWxJpch{&w>s^&w1=u zGx9PahR%z!!MzU`a<3jABbU*y@9G@RXq$vo0j&UrZm&3%(; zvRAzv<2C2C;OEbcS*#}Jt}m@~#|I~tL6Fnf`hAp-uJoQm$D?>+z}N=S_zkf87g_0) ze2{?nGhNSK%^{rU)2rDjD0LH*N5MxWr1!!wBZqrot)aH{=Mj>CB%2>|#YTUO>G_K} z1859$#pK5EVCUTElcKIa8~SePncGZJLBc_%A9>tyj6MkZr0*d4F-LR7YS zN+M;9JC5+`arFzY{dvi$#CgVe1?Rk!inzLXlPHDH9#UI6SLrahm$7R4a{cA@^jDUU zRQ0|z;rWZJ>9~=rb6pX-QOm*JKoKKW@%a-MTj>4eu;4(!oPPRz_q4z{OYV2}_;ZAk zL|)HG%)Rvhm`t~+a9~C>7Az_xFE?_G{Z>A`y8R}M-I&5gSg^g{{^*GN+a~|DVFcBe_+V>i{ZbtoxG8!#|D42ODC{VDs*yZ;SxpKY)Qu{NM zB-qSYfai!(Bo;PGt{x_e;s{E5XZxrQFUpgzthqlU)o)=e-sqQ{<@b9^&G< zsN7+8o#OPnHgdIe?v9~1p1aeP2Psw=Hmwcj_l3&pB=vTK(_sqi9dKkF{8EoyOfoE>G+c;lPh=Vk0JE&MulHHq7*<*}R4qG5i`{%1Vto8ztE9EE@4-{(uO&+cT{P@P8nrbwb4$4RdsJ&Aw>eJ zmcqAM#W#igSQ2@@_Y7Bz#KL#`qQ%lzBQvmO$N@rId>84BU- z(9A9Jq49{D@2>8DI0(`qboSj}O)~!APYc6R7*Y9`9O%ueInS9S`Ih>nTy8&`)W89> z0Kl5#arh34@~jXkeFUKrej?*s7LL7^0%EBQ4i<1eSZa}tO4Uj0Rt4dx((_x+u_XE} z-x<-S38z>ez&VeGc=hx8(+W@8Okp7LDcD5JExq}&y=*9_L-R5w9O9n!IefItB>}M0 z4N_onnkrxIx!S9{f9}pbS-rNl)``=VW!`GOySv-FIyL>AeDISd+`cc4L zr2~;T`CpkC9a^v|YF?KhZQJi*li`E;?X#?935P;fsnJNCE9E9caVtm2WbKn8Ut=jb z`eQhR)h^6iA5CHU@Z>#2B-*NPZxYO)291>YJ#WrI?R&H)9r{2lTEa1=qHP`Vl#Y6bt1Loph#xS4@ol zsM3Jul9;qH{`j_HbDH&CgF2LUQ|@H zFv>#c*drKH~B@C3xzA z>HSWI)%;F!7#cQIoDieN86Fsz-O>&e+Z%{iw)jkSIW_p<>>D;Qoes!HLUYUP)SsG+ zVpA~VDCdSVm1KhlPU$Gs!$LME&ZCv-(f!U{{^IcCP z2?c;(C!?VTkfOpzR`f`{$`QNoxBr#N^0y0*<3~naIqFt#0+z}6 z@jYQ3-8?mpKL|C0dM#3ULyT|Pja z48xmdulz&x$cQArlNyF}{3b~gW0GP{yvNyVA5H3%4wEL2=%e{}361?VuiMDpwxJ<@*v0RWx?~wv_VNJ7}-t{2wj*+b_Iu zUBF&ky);+if3tlj*^#6$1YLdlsgvH<{xYfYBzFcY_8&g@^yL{Caz8Ufz#S>rUpP7c z9$*?(m-XhA=B?1{=??v2zr`AMSy*b;xe+7pgnpIc>y~K}k((q3?Gp*oafcHeRhHL|)#8z9MN54^?IhdD)JY^6K4?dzPaL&R2ksyNdm(CjCDFu4|t%-FIfe?L9r z*xuXz7dDpD;8#M$1;woQSJox2r^?07hyvVC{V~GQR@#JfQ{6IeU6rtX z(Hq*RJ;by1xV!4LBZpE#(@#tdHui5%en|NN54>;R($es=tv2Vk7v`bIcM(mmsg&-ZBL`jpK84qYLlI^3f_r}87~h9p`A+B z&#I7I5B+WR>)=4u8;OvPj1St9a_x1N^l2qP%P_%;zN!Z8)M3nW^m&GKMrL#ti^<^P z-)6VLYEwUB0dMeehq8YgvlNzRG5o#glzOqxeRsrN@(ru2Yr3pOW^(orvKe3erf~GW z_!D|(Ghgp>(^A}`WIlDzP;f!DR{aIXpR8Ulo?OF;&~ox*_#}r2K|9K3brZHV{or1J zu>O3|)r8F@W^3?Qm1XY(?_y{y;qemYq%0J{G9AvUrPia`L61ZMXAvZ$GoGjb@f50Q{xHXyY`4z2!!JU;jPM zTDUuj|8~7nSXIBu=~R92o0uVz-{c=ek+zuJE@8VMQ&@U<+do_5HtJ0%;KYi`3+gF4 zdTKbRE6B~RRp)EZ{>-ut<`qnFf9aX#wqu3TctL~396$Zyv2MfA%Qajh-{!u+r)hq+ z3FwTt`tbh1pX$UM+>ZUK9O#Ww{1uKcG?$Ja_pTER^OBfKlKAn-%RECv&j%Ax5sg3S zr3YPad=jp9%ck!<>{B3p6X&`VWv6EpAS)x~B)I2}+a_%|)#aXkTc7q(T zl0Y?jFrR|lcFNRm2kjeox%ox6q(X`4Pk8R_{087}vhSG~E6wD6VB42>B=IEI$tNI8YIu zbKl;ZtGlBi5`0Ya&|MtdCRwVG+r6l3NU38LjNnr5UHbT?_}v!0^D!vFLG*qJKk_SS zQ6>GB$>OQhiEga=b%dnXeI7(qnxRvX2XTn~uzJaFUxJDaWt}vK^>#9tI`R*Ph~6o9VXJ-r-%F`tcc+3;}9tw%znYJ`?$hUsiDDgs@`s;W<++cQoYnS-!F{< zW%1n8|c+vZu5LNspT2>1bdIuL+`H&W`&(aO0U27RvpF z(wn!wsEab}1|v5TKZqG6@6gO<@zmFR#h;H0i1>)trZJ~Qe1G8%jjf$M zsycCkv6OpEgNJmR8Ow9%{CYX7-oL@y&JBwNn3xA>(UCePR1~l5GE*KjOrVBp1KpKF zbxtJ&Y;N+tn!p5D!ZN{D!ib7;IcTiyRP@c<{=G143JbX8E|+#qSX-FD6k&63gZ zf%;(5t!mH*BUm-#Ga+FSri&-0^`fJ|&5~+6PNt1g)i2%X&bU8t%J)z7lNaQ(P4Lme z;dLsTtc3~_9t(AgQ_=C-KAInwlno-11^Ct*lNX90VavZyG-T=yJupJP>Dk{2NcQvc z@|YI>HMz>LdcmRataGc>1h{(KtvKR=%nac(%KD9ULX&rqCI^lSjSZ)7j4wwFkM?~k z1~}?06Yzm4mKgWGR9rhTp)R|&9k_}QiB^9858)~6+%4SG?T)RCx&e0Xr7~3}^k7G3 za51~1 zRpP4BJ=|=o@uA}kNG|~l-D~7ayd}%UJDA+f-e}(v+$msLbLGG;SQi_2n04hF(yS2X zcf7?uU28S(gOFA#85Lvf)jjO^22X0RSKC!ROP%ojsT|tr z9HNpas#f6ah%!z+CSNGp$m)s!amb(5Jy84J-5>$qqJikZh(lYumoZk5;1N!)-P^k7 zMdH06d~wAs4)w6&IV)oif1QRROQU+ld2_kQQVV*yHI)IT%#9kZ<0Y`9dIIj+iB&pV zgP1*9H4V2I@wpP91I{iKK@FYWD^p7RdK&mwZh4U3te$d=V_8LN9cuDts=T>#ThKmQ zjJqHqBxK@R+m56W1;@>w;k+jwi|3>vd(J{Jx)9C6W`pp+{IXe+3GPRr++zxIgtrfV*q*t|PE!y!gf)ko)4WPyY{v3M|8 z{ua$vgjg_;~*c*v5?IKQ>=4A`hbUv^?WY{SCWHg&Wu}#h7!;+MokYmZguK(x8eF@iR$JN$5i2<dqv|=5dgPU6kM}UsdZ_RQcJb9F^ z(V?+g*%siK1Fxf3^~4}E-M%ZvmNCS`;G)T}M21ERmDzVjIzIR@rl-OXw~bbE5~Sg;JT1Z8<~senN6eVf~)Z4d>A?g1yzas87iae`Gdqvf37b9>^+i}~II;_>PpS9#0Iv2G$G^l4Rna%o)d zRszGp!%Bw>M>`n&(H{Kk^L;PTEFa#FiDb(lVk+W=84ny=>bR-EF2sZJcz*qu&F)Fn zF6!?yi&n%-6*S!3XB=(WYrGad#eIC+4R6_f76!q)o`)}voAvX}KjLCAr+tGGiDPbF z{JujV>0U7#z`%OT3)cDL>}6xB`vnjgI@EeXPxscn2R*46M?=Gy)=_aG-ltlZodtia z$o*tpkZ4u?jhZs5>a$~Yvv>Ie*&bDPt*KEoW^?)Y5)FzC!sZ=OYQ0xKbczlBj*F!I zn}&6uzH0}S)%&8KKR5Y6LW9#Qj6Yl}UHG6CQ`2@?&9*exeHRJp?9~0gt%)azDY+j= z7bPTfreqnq3iWcVWTRGv%K7f^&SL_vb@F8*+-QQ0Gu&ByKlT^D;0`0-1>aP*SL*tMSM-X*ED( zzvk5JR(92%L-%-glsesKpR;*%FaZv)fn-QM<=S+Wrj@JJYx>Y}kv{d3Mf$~t8r?1uWnn2v>)$%Q;oFlARo$7H(jw0L3Ru)~KtFA497W4J8X%V@iR)R5} zvm!g%MM!P1i=+1bVWTRxAdJL%O{VIx*v!xd6a3fhWy9u|J7cU7t1bp6ktL+OV`F8= z{N4%t&0)F**9fhaO(9@EqdE0!6*DfwysC0Ezp@FxoLV4whuw2b^pkzAUpIF+4EzDS zAif(Vo;>63jLk`&xkkLrzW{T9rFw@qv%Uhd!~12wtF?(G>Vup;EWbW%{TC6ioJ0HP z>ugziFoq@)GRo10BQ)a7qZW) zr}l04*0Fzss(*(C8=u1Dt9*e-9jzJ5+${^HZJRFJcNBcol8FnLOcZRzqT0URSX-rA zox_^VC*lfqMxdJ=BgWtA)K&3b>@IYhKW>OtnYdOGY0wy6u|gZF&#Qc33d zb0fAp9hsK>s1TUe`72lnqI)^NeQITA3tP1UCvs}$_$up3Cq5EReAN3O;vFHZFax)c zm9Undus+T&xH}T-z*~ryABA&o#NaWKm(D?Y4y24~tk0C|ggh2>dNV)CoX(~gMGN}U zp=Q090D1ZvK7_=c4Fh|qfK}LH5ol z5Ui>Zn_xl)8$4$;C|v%a9PX+H8}11o@bx)tQrk&yU0H8)=!f1j;SzsIlB>Le>bDm+;@J~i|5TR{k=B1(D25N~ z_I!g=>Ze+V(@>5t=B=x?a(1U4qQz!6!182+gKIE>)YS$te#g0;t9dB58zK8{$U#OU zZOBB1E`f3G3yzsHl?^hFg3mhrd%j3n6s;&9t|?Yje+{IjfD8!Ckeac&4^`vMCCc>L zc|1&AX;|AqZ@+|2vXy@^9k@gc5AYR_+79+p(=b(;k|C{N8>C&wx?(rBGU*U-sdP+X zxnhPU zz5~3E(*-7(vcpr|Un4oc*s%Kc4@_RZN-QeJWcL&GZ~fh5exO@sNGPH6k+aeyg=QN% zzRml`M3#x5^*aa}Ev?eD-qvc@lHF$B8KV+F1qBHj_%~ReN!JO>k)&G6H%uEQLi*io zpt#OHNOmVF`DRsZhmuA{xNZCC>2j&yxO+-!{iMeIE-PkvU7k*FRqj6>A;g(>kwTZ0 zBLObi5ZXYI-q)km-Oo$k@Fa|vBrS~A%Cic7B#|k4)iA(fG!hdXdn{J;u_JauL2bc` z?+d9R8Q^r*K05{Pmn*07zo5TsupR&9)ix!BO$&k%B#kl-4i0xLL5{L)Ypoz5$Vk0e z4eeMW(`dsJNs2)H2LjuMQO?E$ehrf;PZV*Z0E)%i+nOM@s%!=kz8HAX6c3tvwayyq2JNV320SDZqWN z->u}~voej97b^kfd?b1)eM5SNp{#t1TZ(8-oV5sKT_MX+hD%;FZ~xv$r3?!=3Spl< zkdI-!S3+5b!$)aOn?xeH=o=^!WgI9nx7BT#Wtl)-R%YR8;FHCWSau?vvi|j;LzzAq z^@l{PhIrqF(>ilm%znOJ^yc1v$~ZSRzh7AmNBhUw-K2>e|9fO2vi9R?Y_;YU3ZUqf zCIyt9KQ)9H1$jZb1PY)ad~BUg{d4s9Q_`}^EZGJ~gNUI1gEn;9@RY$0DL_5s!+Z5T zZ)TGx>gFWWoEipI7!2lNCfRpbQeJ2BaD&ZU3al>$x8KN@*6*g^F9$KfYQlOxZt`lG z>ksN>ZRpB4Xy}({@#Do}J?+5|7!RxiN`>i5$8*JTGkTw{a@mrMb#VWvI|WaPfQ0Hh z+4;*hZO5q!jIuv_h=(jqQ}Txu8L!gQ zxCpcT_4g(ifsS`22Fvp0v2!kXF<8Dx_(^752%FMMykXx+LHB30rVAX^R$J`ZCBp;{ z%ExLtYhk2$V86=8sH^T?oXW()N~6C$({J5#bdd^+9glM?hU03&%c*rMA~icZt)27C z`+or~=gaIC6W-Aju}QP{gaqGmx7NPv?Sccl*(%E%wPcGYm8BsaNk(vO33 zLyrd^J$~9kRZ%V^zRm1rD~os*oq#|QLxoNj{NZW{JYSEcw^p?wumS4rBN-~kY+7Gt zI`>oCYfD68dwj7HIlqkHajXvM;~4)6eMvE3V{3akJMdL};lV3YtX37lzVm^vtC0nq z55wLfqgkoUA8*c}YSOk%5j>h@(-a|<5`B4ErO@*)QQB{_I);t-f^X8XL87SM5u6Fx z&Z_99Uz%1<=3#oLWijkdisHPrX-S#b9)3eq+FaQ68F#*Xvhwkbfn4$M&|M{{tX!J2 z-1gM!4}v=Xk?9`^9;Bc^ z?eWrGH5px!T>bR!Z5u}Q_nmr=dNaWCwLX4Yhz>~T`abT!)2PHmD9&h}bTngst!M+$ zt`V7@pJZ=fdHG`hj28yS5RWYZXm@J4AY0fRPOx&RWcd?6b*u8*;}WL4A`;>sRlN_2 z3c3)X$aMJ9$?ugX9M;Dlm3xSC#^{=q)>J~3HooIIAoXr6J1z6qQsFc91uy#Zr|c}L zL+4JzC~g4* z(_&Oc`(h_PM*s%&Tx)r9qg9A29giLcykn$4UVdgrbBxD~hYP|)kx(0%avuA${UOR% ziN{M#O*I0H5R8>m3Z&(xjQ{>AoQNp3qhIU+o1Eg=z`F0Vf^@|*77MA-MMitWh_*U7 zuVa>neDnkTzO=>PAW^y}>oso)=aa)Ku-2{$Ux2>sCy&uZ$YtX*c&$%{@P1zN6`_>a++`ej?JD)C@s(ve<4a>(-i;- zV3PuXHpntdt23728%R*EGga|ew<6ANwEW9Bl0!WF1jn15Xpv)sC}3MJS=i~y767Lg z_Z}fxq3;qa3W0BPHfv+vrJmA$H2CSspyzLs*j4T_o$!nQt;ZAtWpG&d6Sw=#3Tqjc zA&y!gkzM*zB2VATxK5`TXEKZ6j-NVrBbaYvbJiy>P6fZ;wu%C(P3J-&G$Y$BcAa9M zT?a}&*_|MOtzKzvh5ey=#L*Kezmg;*zl(i z%!?!!y=g9RSb;~{CV?J)gpV8$mEvXM$%6rS!HNnhtoJrP1{8j#7UFGwem8G6CILw7 z+<62P^;!JIahzDCLLWv9k51gAcP>Yw5TM%m9-jWohH^)(`zDV(V%GvB=Zr_|YJp^xyfUXp`zy04&2enNT6P+lPjhKA?Qw-gxPL^O1;Z zQO?Y9rGhHMY^H<6&e83r%y$=`-;gr0Hic$ z+xK_zH7V`(v-Yz~3OlT$-+#=|pM^#G%8P(bT>-vj5EM zj1N}H#p?ZpvK>*aL^!Ke*C3T(cTDydD;Q~FljN!ooIe}`0n(UHx`*H6?gp4ip_F=Z z792d>-t=zcfQb+6cRp%AD9lobQ^*YQD7}zSh;|%QUqbHIP=OR+CeLtA%FlG5aS6C_ zFcN*TgTx77Rv~ePy-i$pnLDdEF(z%Xu&WXAIlO=s3-0x+Wgcjf(EgK?9(P^RSNkEB zf83$Z`6mUDVC!YjR|UKn;O({HF4yYc4Wm3EcC8@6o0LvAga^0a==!+(@(1TVKpHu# zsxnH+)YPkz0`ewRFK!ss~$7Y89Dw9`G;ZG?Iwbx<6iJ1cy6A7rg9Q-ASE z$&-WxLB0gWTf%qWFF}j+8SodR}~}zNmM$<6aNy_ zhzm9sKPn8KtkQ%aPlYnsKR+F!02)9qp0_5h8nVt9^?1UDbAvhJPTKOsR=HI&LGM|M3zO`mEUYa-iBKwHT&uvS595halXL==*)uct& z_GS8zd8{ey_GwN?V4}M9WC(`lA;mU>jY5C#2hLvGV1QAPbRo&XFV8zBM%YKgUa+#f zZQeM4+KI(JVkiw+th}8s0l-WE4PZi?PU5|KQ#$txVLG{;zsV-!g3W1Zr-Tz(0wc*> zoB`4}4hE(77}bN=Zqm?x2}$AmbHcF=V|yBb{3TaWw=dlJ0M8@hmnED(9L=?tci;Zk z6Tn_LFE-=r>+2hv%_vS?+M&s1t$$ZF$O`I??xa*6RCo{#pMIbm`_)4IcC~ELzgB8^ zf}Dt0y1(`Xin5Z!WI7)3>xbFoQF0SUNKqmm1H4?uu4p+CpHVHiEyxzATPe*$Y@YyM z{l&SjG5Gc|6F8Upz%qJ-G`|<*sAn~uU8xK*Uq-Mzw<)+0Ii|- z8GKuCDQ2GqeLxqP73shA*&(kYe)jq-RaAC2 ztB0$yR{H9>TvBK0cuwCdD$gRlOl`D~{+H=y7%Ze{e%m(Mus}Nky2=Tjl`YEW2s=im z>eR}CMf#hWv_39Ii#@D;uo&&qDCQE-X{K*D^_KBcR}g?gsxjo?*$8x;3gLJt%K|df zvBfqs=;fUU_ViRqeE+?6G8*hMaD3(C2}ySw5cMLM^!dcqxN-V$+{Mbs_h;dlquuy) zrZzDxC8|Y4kd1oi#yfWT<4C}xJeZ)R-x{zq{wtjQDoviHFdM){{64|Z7$@JS)B6_) zJ?|QH_6S;H??3W*Z&<=v`#yluZ0^WxUV!rGwh9oFfw%FB#~xsHpj5iti#>KQGFX~H zPXrJJFheyb7FB}yw(jxRqOEHijB07aAiiZL1Bny?FYuEeE}q!}9gV;IU8HlFj>+8p z@`xnjpZ}AcPn|nxTLa8p(iYm!a>}Bj#;bR!ueq1ny^O9U;7a3=>z1C(MFJ~HhKleD zv~gy~iEahF%cY;Uc_&C_)L)=&snE8)@4vt&BKT}s-#VZh0bdZ{gGcZIL{K~X?-3u9 zLe^%O#$qD=NI&UyOhEglhVxB%e|y;4VuKkkE?zLE}xLi;Z^ zpg}@UJN5=y#15{VuSK9h#|dtudhb^3=~pME>@)$a;ZlYNps%O*8GC5q$Ng(;GyJZx zvhq}=P}{+-93B99vpF*&L%%6jBrl4q8oBeeGH1nOWJ&at53ZRph>8PDGXWOhdtUMwC zKs*mD&}-6H!z&e*Hk;CO$-4dFvu<2sM+I_}->QSPKJV2!!xaZ?fHu{o#Pj-Rdob#2w$_^go+Cgo;4!MB2apLH%j$UT+F|~*inB65(zFZfV}*b2dx8~A8*f&dF2_sG8rLQFZK|>7WV1nGnVkNNMxImf zPJwrW58Ccs?^sERJ0ex2hlj9cJU>IcRf`UQHO~q>P){d-{xzDQ#4{Qp`T7r0?xR=2 zY=Nd_3~XSeARB~Ujo#!KU_hp0ZA9}+52zUiC*G~12NZ3TeVnW;ubj38+QDUDpQi1{ ztL0)@yTpwa^MA+~tNG2Rl{v!!`uXz4+CM&$)~!`;9P3HJO4opBi6C}0sW?S2s-04} zRwb*z)E(Sbh7>(PufHqnG#wmwi{E<7mwe)e|LpurGPMHz1|KR8>9Gd>RIK0svN+Spc-Q6h& zl7e(djoK*b9-wqdBT^!v)HYx=jPCC4?nV%K_j~_^3p?L?&-pyhai2D_3zRP_NPlrA zCulDsIoUu~*0u5a>1=z$oixtNG7%PPKpC)3KErXCtMCW3JcrAqc`;83n1!1WT~R9h zui_L_7lfhEj55yjG$LA!>Th9iS)DW(E)bP7t6?gSRTHkntGw3!yuQ@rBXVCqU7zMO z;m6cjXC&Q*)B%7-Cq`FP44Hg7Fs6z9o&iR3s)lfCr1h&j;>&gNJelNP#}_v@Kg6Na z+W`uwg<{#$5KpR+Qymm?GjOF8o|cz1PXHMeOQbn_Yi>2muxV~(1(?kE*( z?_?M9ig@)BwXymp30Nk8{LqGxmM~R^KkX4JPma>3=mfzb(nF4V6Z*fY((TBv%NVoC z&IN=IfP?*fPJv&fB{P4f2{1@^!LB^omi?fj14z9M9pd`87a1ZC=nj-`W+8L)X=v0u z&?QU6X+K_-fWr2=c1X`t}45ce{Usec9$<%yqxfT=RQmeJBDDcPp=YS z8+KqgGUQQK*mRb|Pt2$q^%JnvCBobAEVUuI^}*Jf*$3N4@ld3Z3-%@wguyLV z^-x^3{j!;X5JxM6FCX;y@1eHO^x_;1{T~Jom@JRG?qqu_&r75K41};g)PC^woZSis zsh1@Et?}x%k@s~ogQ3W3$|Gi(m7VI~-!qNP!M4wko(o47B(O+n z&{PZVlz)@&pMkqwwtRKztxru}jtME1cY&kgR_qXfBn-fE;)n2gTOaI=aBO~;+h?oq zFojK4h9osjQfACK_ocnm?ZMzix$V3WJRkoX^q(d*;JPYTWBI1#MoEZFej)1R#m>lM zB%sJ(*9SnwJV}(rz-2BKT3%IzOAKH(&9#|9x~$}o7$Vijh!@M%s__Pjz&#)!(vOvP zvXoxoeRtV(*{EYO$#2j~w3j>^LFeJm9gQ--)NRNS{b5RWX7sSJ zi`yz9eVijh&fa#9Nx!F) zhl<8$V&ang%puzabE*6Y^ZvXM*&4Egab_P$jfoe=Xty-jMC28K+wlB8y!3;h^PydT zB8`^IXM(f&JFo9EhB#OUw+k2)t##z7Jbm;{ff8dHzD>HVd!(fD^rl6|1^y`^^;vd=3_#%uZ}BLVIaA?&{4-z~xFtzaGlU{X#~P$9o_^uy+^jeMcp z6qWj)jV_>>qJ4W&GFY3JXZ{s-gonR@&Yh^+L(59OWv-Ri$SF4z;Xqjwyy%^4{87H%&yms^{6eWJrMI@ zWs``qnz#Mlo6lKpKLU`5LS_>~m0->pf{9G1H$rMev+i9k7K>m}SiHl8J6#YdJ{cMr z&^R}#l|k3wbPo@Y!{CXm<;l~D+<(Yo;w$w?BU5#+6Ka%=5!7S#U8m_;3vhsVbpcG_${ax-tX0Tdc&a)5TtcV07>C&B~*H;yo zD%5vFMbh}v04ZeFNOY+_K15T16i`+G!w`YBq%+}Mql-&zOApdjao-t_(Wd~A2fPgN z411q|eUEbxE@ro@!oXQvS?rnacc)2x0D2IC4xoAb3vGf;^u;1LEcE*MpWe4gS^?`% z$zQ)2oxZUd{w^N;5_6rPo6VjDKTNUMGQ#nC7g&}l{+Cn@A2X2>L0&886_=-ki8Su_ zP3il~)h>1xAcO})p)fEi<$mhX-Tf8v3Bw4n>cHs2#*V^1fs^D$WW))qa>pofw$~XI zMdT@0>C+`T0Kz2#d=d<}OnYwp*1Vg1=mJTF{EI?7^ohkt$+Dw`7k*<0EI;}>>ysXS zp>Y@RU?!s;r+%hov__$1TCIdKCAXN;g>qw!_d|W-DWNX_T%D;v7_iAw%1TQ9hD$6^ z{?qiF-Rq0(AuTk*m=XIVvBS^~f|=a{8c4I*8!znZ2=);(LfEJhAjK-+vQYlI7e>S= z&dK;M+1{{VgtN^eU=aYw$5qT*m?o^rEWE@azl9Uw;bHyBzUlw-sL*oJj0O*NiK$Ip zVc*1ZgXd72RiF;FMo8&sLkc2NuNet=ctw+_jj@vGNqKH@Sd zG7`Yx=5S;);<}T5oB<4B2om33UZMN$oSDq;#D8sn(=!<_U^^&&B(2w#Ms;Ib_w7>* zZQIdC`hF>j>8`%Q4(I0FNRgnc&;rz;5ye6YI%|pzx657rz5(7wuKLX+*>`Bw4!iO- zEIMeDCZL55U|5Z&^f&)w04vaNo6Xg?slJim>x)fR*$35qtF99Zi1muV&QCYTrng3S zcnjp(>!}TxtHcsyH)H!)0}XqzI$6L{v%*pM(9jU^x#Y*R6@SBNmw=hZ<*CzAMN&@; zw*OL?b(kWVmv6tL9Z(;?;(C+eq-xMXUA2AVK9^&Rr55wAwcm|%mjL(xv%45v8zfg; zTgJ2^fh~-ZKh0bvD*D|UjpV*kpV)+C<*4t{y=HqkpnQt6ij$U9Y1rGHDjn=U1Su-S zsqj#H@_8+lijS&6MM(Q)WRxjs63y)2@Z+>W%Rjct7{RTSeivBUY&7w<{!K_O zHTTpL0KIGc9{lLMinR?Ueez^8*sGtGZN_OAme1~ruGBxFznJ@eMkOa5R5bF`@BWBM zdc185ekJ>_FJU53kdU%<=#4U|Qzv0DtWBkjW ztaxK_m#1)mB04sY*p1OJPY8dzOrV-JK+3yirYzHTP>5-RbBU38dQc!KrxYRDW+06+ z=DzQZ@$Sc9Q@Zy35zuY4S8O5~OMRD-A!3_SFf3|B|G9k+dacPk{oihS&2fVsD z{|G17Saj=4Qh6s4&m!}QZPff5d>P4Wzx{Y=DN^wY1DE;gJqARjNz47xiO8bgL3y9a z%H4D%;|qJqAjYp~3zC^Q`r6Uj{tCSGjSgnyYP%FPA4i}BZF}R*X75JCq`gQ(#N3s> z^@Mu^hkKo*@CPgUz>le5Zp^o+puP52B`Za)HVzAll{1zUR%G2gARzRu3E&X^7PpP=6iYS2h2w=R z^(+bQ`t6#TS58MqeL-W`^rFrx`W1$mWybXq!Y%o@O~aT%lld1wjOYG`Za~OJ(jLYn z+76c)OKBnEIbXdQ=i+01z9iY2Rc2WK;z0&QT|pgULe2}kq^l5>E1Fhv{iwj|M^rN1 zm^N19jxEy@1i;$*E>nfh;M{|#)tu|r!o&Xqx3bZXqxfCsP~@_AOuwSIGn#|>n21}= z{6T2q?m=3qm=cQu$Pzeh-VqAyQ>gcame1>dP}Y6- z4kv~Z`!xoB^8fUQlb3qqq}pSto0vYiP)q77I`{EE#WZ0bS6B|{C)#+jRNmj*U2 zAger8N?MLKq@HXDV7Tfi$d5nx+w{p zPetp6G13=<(8D^WUgI^r+Ocyvn-V^UWt-oXg=Ti?+FijzRO^(GOrB?8!Zcg66Gy9( zI|=*JsV*M@yS(Z3cj%wdj~axdZ>zyio44eA#Xtsd`reNYqbMabh-Fl1$@AH@95y}arFo({rdLZ`NT%jhqk55J8-jCOQ9z|g4tlGu%}S-`2;Gg3g_ z;o}Kf=kv#UZWEs6-0*8HnzLfpwA%jt8w&F3D=CZQ2$=8jV*R6}P2n7ug2{OYhB!co zN_#V}O;OW}<3n&ct7*Qmk%MRnw|u@CbT*9=E??^Zps8q-=BsX#rgu_r-=y>?68{!c zEc;6#?HgOybwLhJUXh*#=X08PW584tU{=!$Ha$UPUIT>_8FF^Ce?j7dXw!fJ%Wy-{ zQfz7wO`cBF7pW&XZu{9T`r>4}1iOx}fO5|nlO9=PT{OZYk2|~o?xp(epa%LC_5D52 zN|jwDpnYoZ9<9P4RC)HjTBw$VUZhgXlX_Mbz)d)R`^}(}b_Q=VB)&??^xILM9IVKC z0n$W&6LlZvD}OC$>cN3f{m6bV?eTCmBPw;f8kU{0gQgs&3-X-Mci!jJtJCrLW~>am zP>rmkUExT((x*g1USd$!QBb|&1=Lu$Swj4lt7;o0_GAzhZS{+YkZ0h7!N|ACXdKNQ zes^Q>i(5mJ$hdUhWmnY=QzO;yAq|k1;Z9Pgwvy(PPgAaN2n`5J#HLX5XX>VG6MvhD zV4MWI(a;)48PcxGC%yb@YE_?*6j27DofRV-6yLq2MhPQ9m{ngkXlH}v1bnm&D(&j{ z-Y;V)ivdiuq};jY#ZaDcD%@D8_;$_$pkx!%eHaqevbW(E>qw*|R7uk7bVu4RRy&X;?##c5lx% zK9bay@bFt!DS3*M^8SHBNKvG0E?fYoSu{I;j^U<=Dxc5Eqbh?#3veJd(=b_dZUF9> z1OB1T^)^k%`dC5(!#$Qd`7-nRiy5JXg(CI33=zFR z(?Hm#YQZ_Ar^)%?#K`4t2CBtdwo3AWyEA8vCW3_V+u`!Px;OrlUCvz|7Abq_vW)|T zq39tlT%iPdr|36AaQKRuX!J|K>v))&+C_sCVcYm`z1lwWCc_ zrJ@!cZ)859&r@^&bB<=8)G<>y0c4#ZE)oy{>KyFnnlX7FUe2anMVpTWMxM*G(FNs# z_+~xCI?J};4mE&KGN00-pvzr}6aFQ=s9+M^SH9o~YGPw_cH>K$Q`y69AkI#OP2}L$kLD0^EI}@OOu`c`Wtq=!s zrG15_m_@JzhREn{K!)NO*c?8mt#6;Hl-_-cqMH*wq(5^6K&FX*SLqG#Io7}+S2~eO zX77I9y5(?@E1ZTO6eZJpZ2!Wky8HH=G_AZ61L=69`_r%GMv`^ElgtX)kFZs$Xf;q1 zARMKfdpmYQvXk>eM6a6$W{B(X?@s|JJIFWcDbW#0+V^ zx}#jdruiXBku1Y#g@J6!h%w%nYU07++8nTgHSjXT(^trM&hBAhlEz+kem2{_5I7z; zhGGn7-w@u!^Sd-q(F68#B=Hw;xXZv_fKcrGEAiV8M2!3g#D!IAfuAkQIR3~Dt zXlGgH3X`n20#r(ca5)UkYon)5@GNEBm7oNgaQCsZ3LKi0*{i1PmJ z;q)&&uxLL`tp_2VZA4(1w)mecHxr94`&jS%r=f|IKo@~*5ZJ{fcw%>~fOop#GYgot0IucN$p1we{^U+EDd+sqGxOEVdf+wj>@I=QB8F-HJSYoW%+Kmag)n(O zW3sjVlZhMJnJ;I7ST2Vc^3!pdzl?~s&eXNV$Z3&JGnqL}ip%L>dc0m`blQ4^OcwxZ zd6JN?6@?|443*EcdQn^$n_L+kCxy)l*)ruq)8{$0LX(@-70W+H;bI9(y_1X8;ttuP zNpyXql(RRL$H7dVG3%y?42kJ_%!`@I_m^s|74F@B3F^H9*Pi+%Q{Gt4yJxzWf-BII28GD=xcT0ygO1RyH0tL`tELf)17+u0C+NHz8X@ z`59^0B=^1)W;h*IEkTQXaXjj{n9E$6Tme9U0W`56!_J{!BIG06K8F(p9D}YaK%|Zu7jRHPPl)p3i4*J@svZ6Q#jxVuSrx(ghYQDHAQvN+K^D5}I z16^l!O@BdA#HrcB1tE$zyvY0fe>S@q-38M%zKo$|2YG~X#rO6pTlR|HdZY{!Tlg}$ zd~OP#?n3|ky05YZzdhf5PQ~i)b)uRRcsys*JxJSkC|=cQZTFlEg#=xs9Y0ab*HM@} zpd4iBnV!w2kw>YGz70>RP*dZvXDBwyp{j#ZJeh$5T=e@m9~!ZoQ=Mfox0Zv9uYGA@ zC%7`FA>jt-`D3<`9GdFU{lLfP+;rBRvR-rErxo-BzzLfB&)O}!1FM>+UQK z4fBfx?d#_52cSKU)a1rkPQ1@%gCAysvqgHQ&O4s}BTME^CLKnD5P_`zHD~gd^V+^q zcmP!ZL>+S`uBUm~SIIO_$$A!Y&u+$+cC1bH)ydgehDA_F4W61E3kG6?fDv()gk8uf z$>E`x$2<&cs`O!^j;kr*ScvjTAJ)9)t6!;}2db<3=v1Kq*C*jK=Vj@U-;GpvB(yJg z)$3Lgqf|)&YW5cD8aYDZPTyqQ+B-kk$p;rcgxwr!f}@=bTbYeoEdv`gy+zw(JaMEv zU5vtX63#Rcf!TjXqyV$S*^Noh|EK@{xq14bF>y$?f*q*kc}G>;ZL4hR)OxAF|Bhxb*7 z@WvSlkwwPz@`5iaSj6d=rqc7x!LOGU5hpGB%GV^KdXGyp= zX5rSlpzhlS^E z-tA%oHP)A;IsZnH7O;7;TvJ zj%O5BQ&P3XxZ@B-k0sq?Xk7F=Zy^-Cy1M#j9`ST++@D=yk)oW{a?v13p}o&?UbXav zNgx7CY|p?Ep^a^`#}>u|uOFdxh$D2;JploLtIve_iGJn9?EhWf+|Bit&vG=~rWbb9 zRDY3FTG=9DM zCmu6=JUom`7~w8<^7lfg8}iul(05mJ&^0PEDr^)J#3*3nN>?@Qse_v!`n-*&^NJ& znKGk^#LG9&Vd&X2qftwM@286)(PFJiDV3MSn%Q^cX3HJM&qXxl8ZlA!`PI1PcqXw( zl>zpHPgAHtOh*al=&nzFh%@N~TqyB$QG8|9k+EOZ()%z-HC&4-13+TtzfLc$`6ba^Ksfn@c+InHsENKaTTCw{%nF$ppKm(x6^OOyu@}Kiz8IxM4oSsCHwll3<_w9pi46({lYTl!>B* z(1OG*X7C_Z$M*hr=jS$ze=;lsrIZ#iDDF^^k206}q#3Tn)9Dipzg7fm=N8}+`i2(bw@s=?wKf*Nh&h*gd z`0$XUWMP&!*P%m(@1Nc9x37@-YVkRQFW67!GKh##K+Nf%jB6f%_9*i`NXndSB%E=Y zGVEE+A_@tdb(3MPvR_I@hZg*l{JEdV`ARnMd88#bMkri*53bc z1c?-xxP|1;8tGt`Z5G`Uqo?TUk`|iT*7>R8EX_i1c9pfopOM&R@!>!KxzlxFQFy|0 z)0^L!X2p*A)HLa>qFu^{kVGp&QaOQ zmkOD-N1*sxBE@3sS-jc#`T67-(}#VV_Rsjg8Y#-=#|sDkYupuS%+`>XZ&Y9Y%KO-0IVgMC7||F);TI84!!Jc^$PPyX?Zg0!gO7Vo^Oa+uHQfhXkBLft z&_?|A0fx^j0x90lPNzB8>s$qCAUs!DzU6B~629JFu<)a;hKKaQee&)k!Mz<5gHaz?sb^32fSq+jSYzWy88boeFzFKB zb7BZ^!QfEh1M+9lp9!bQFK}$7S^cxvKd)>lgeU9_AM#Pm9{Lv2?RGO-cYijV2H_f##J zVZr-V)%M^!TlHL?4QGq<@${w-LBYWYucdj9TrmE%N_ z#c#X5Gh&petDM0C$8Kmf&vsr&4~D3K(~=%egTTAS(0K{n%ENp`Gta{$0$1=M#6yh$ zwB&9^F$-oq;deDJA^Tij4p?+0ff!eE@)<4-uWvMwAinGFsomB5BqVqq<%+iWen3eC z-ZZXg1fj=I>rs*~2sSR!qvhJFjQk@5dlUV$wlE!_D>NyuCNHaD0e6K^nU`R^?8zk0 z`%vNTN_4#-=Tbi+sX@U9ZL$h4#c%mtzitAs2blswIT;{_lzoHl?hat;oi}{w5LKr~ zwpTr%_r1|3uudS^{cBm>i(?%&4v~!RdPB=Yb!lrN|CjXDqNXLRPYf^K@lJk&u1dS- z+ZC!1*E9vrynLCu@F{j5p-4Dln6a;cwcNqbVD%^ZgMp7{U;iR?Y;Y%Pt@aw9<~s|D z*kU~v+s`5gxs3u1{bB!N}k$!L#84eYzT!>GrxUZAN?tn~n zfw$qF66_()I=(zIFm#HsadU&UY8Lt5Dww_>N->{C2>G{j9}exb#Fi-Jvt)dIGZMdC z{<~>?PWq-FG4x&hBDnuUOi8<^C@wq)kZNVL>O7tY%Y=I(87%4xDYmOP{ ztmb}ueodn7Z~>Gso0GJ5`Kh57yc|{3QXsbbJxo>=MA;8+`NA=t0)~CjR=~Zhww=0> zI$00U%+oxI%u^xjqN;apQn79?XJ7YD5^$G>o45$LT)k7+JvyfW?SG+cE}oJRy}|)2 z0ZcL*AWE}!O>e%I-4h44VXT-bn=1+wI{k*`=I=d%r^{6T8!&}7_)Ru%{ILk;v}ujl z5=HmtGxnp|k93oz{ck+zmqradG{T3Im0tpxFNcopyoyhBEv6DHN9 z_%pN=@vD0l%Yfu&!*WU&vUyv$Qg!(>Ty!%B7kZBt3_e3=s<;3KA0PG&l2bViinvVB ziWltv^bGn{kcQTujMaX8P0yn4My*7nF42(Z`O*-^l$1|w5b^x3n#vvoxIv8;S6(9?2m^A5f zU5lCvsZ>#g-9F!TIchU-=SSjyVUA~S=pzCGq9r4x0$0MTiV!mN~@ zU#(iB;r?=~;eXzEtT(>Lc+-O5??kQ{alG&;(j-9aeZX<5i>h7xq^7uKY5!fqbN?}A z1}IV0rMu{N`Y8_+i;uA#6I|1&1?q~O<9d$_RuwGafNI>367xfqSc!_hZZrZM0dL5l z(?eOJ#XTr+2hdTsuM``?yjql+?<5fCk9j~Fjdg=NXN?X8vBMUs9dz$}@tUTRm(CDy z4=ch7%8dY>8P(T(q9=X~BX2$%&(#W`*M9Jj*YH_^Dr#((24Im&Mm1cvrHs&W$Y0|z z_p18?3_9G#ZxzpvKCzO}3J3*$Az5eufhF2z3S^irDLj~hk~au20$~mtMb&90#)@vE zi4@P^w>&g*ek=+jk~=uv)@$CQzW`>0badvuQqsZX4WY;6jcFJnp`r*U%584Yf{wu+ z^ti76-=TpiP-@g{7el^1uPXn72BqH}_7%G>22jU^PBtjxJjH1NZ3@Z%5Ip3p*f{&7 z^-tk=3pa^o0c{@z>8Qt10h!VX0QE^m`Nlw#(R?t`*KJ&I&uqECnZs0y`{>axA&Y`f zR;R4AA`B}QnYjW~Y1rV~)`EhUdW>`dv-pC9)q0qV2o~3ecT_Pqn8`pZ8`s>#6gRZd z<>w=MhuJc=OHDOV|2t(o%C*<79fDntHEMIAvZQ&F{j2(z&Uc8$H6IU=5-Mowk{vqU zN!~q+$;-^NS6=1W5zi?8jfg>@tg-&jgaDPL`qa5pVfW$MOLn$z(%xPKv1!uqL+k}P z>fOVC`C$?0&0r_TBHtm@z!OH}f|kOX*G9FrX6K9fcAOhRs!N$Y>U9ry2J2H9t^kAG zJc6pp8lo2m`cW?Qc~Y+*I38#H3f+t>KJxhQp*=EdB>0gaj`K}2v2Syyk-Vep< z@MR*^Q+W!zzVO%>t-fg1^J$6s1gaSL-ID*u*U zf`bP13wn@edbp$;x9?BzXChXKu|?5))6xY1)9n@EMudTkpOW{>#w%9W1AqB1n2%^n znbV$naHIo%i5ix&{43Mfw!5dl!LcY!2Nn!m{uG|VA=&52zr39APcojTg5wyg`Kt+7 zPZI(8iTj#m8+jk331N9VmN5C^{8IV-II-DQ$xe(O^4|-}`ONAMk!SM~aA9tYL5@bu zJ6Ty<+85)L5LqT{Yaa$f%S9ZLXQn_1hd4863&+zg$*T7L`dH1{Llk1TlKN(>5-zEq zP6!O-RyU@qGXESh(ZCaT|8x{*Bcl)Jnzm>lMV(nqzHj`M`y%nDkIp57GsPbGiFori ze?>$^KZ>K*!=xV|wY`$2;6_*ic>(-=n6I`pzJZnune!A-MFW0`mK}B$Z5glWFd#$=u11SZxf_I*|pTI+~-mkEsVC*Yhyq+b0lHK-ObkiP4^o_fMG#q#ol zi9tEuD7&sTx64Q0FK!3qt@@wcCX~#7+2Q_dyE4-y^M6Tw#6gDfe#4}jRnTsPOj=B$ z{=}|T<*J}3L!Pe%5PKSC@oPNsb4Nr&f*>b2bw{-$V=LMMD|AnB&9wSQyFi}1MSmSJ z&FIOW&QPGbz;6GOqv(x&;|8mR4@sc_le)KKU6u)e+$;L`_C6p)p#NJ@rb#%&XXIRd zN+#jTV)d{JV1_eld*AHXTqLQnOJ(rs`J?IZ9w%ZCfj9qRV7jur{HXaHDm>fwp7U82 zkl=L~ZldSN?_aCtXcP+?M??>X!H3Bg;6Bot&EXeDJ*u_OV$NQ`a`h@Qu{Zg_^X+Oz zYJcd3KYcLXqt|Pu=ji`pO_D3ROEkUt; zEYQo9=%+d-Qn@j8;w%lYV3d6s+>I6U-#!uTbx4G!UUEUuxC zd|KwUuS{wo1J7DLrq0FgZ|D<4MG$9PYhSPKXT!~juZ6Q0MZ_P1kLEp7M-qwvlosV+ zhNv#Yn0rN9j-#_kqsP zLXKZL+%eXr!*^f2W+Ae2kYD|Ss-zMTqm;sSU2Dqyxq+$y#AN3e!J;N;G(7X+6g?C8 z$ZoOogg&eYlf4{)A-DdSgcFsO*l#SZ^sjilASQ(0Nm~k_-_Y~RDJvCww#*`TmKs1$ z0nj-;Gl_jXofRg0vs)p3dAMfN3H#c2YT1!&iC!Ye&(Op1rve#B+=HAD%rwX*7?&nR zX^-G*BzYgtW*#oIiaIzu$ZY2r!HSmP63cO_Pcy{OoKh+a=YJWxUxbtv6}`fo8-D#J z)l;$H-Wx3E4-ioQ&q?~kDMEWnVB8djBq>EIsCPfxP@9-+zNJEnuL1 z?i=m8APVP9DVG)!gLCen(-9K3q%pfQn9c2HYj}=&Xyw5IkV<=(oD+F5PRK(WM0h`h zt_h8%eOcZIUg(uMDOn+Nw^wuh@bx<^rUy{d6W`2w7y~hAk1qg=Wcu`Ml#I2`r zAM_)XRFfDak;Udfk4U*NA6u(&cRA5=?(z=_pPo+v>d1ULgca7Yp!AJvyEZQyRE#>3 z%nPGU{=^BK1R9B$`EgV^6?52Icke>kb&Hy)CEm#LF|$NtVq)U=V_fz2v#mPrIcEFF zrN!yvHS+S6h!DcQ80$NLaqlA(paaxyPl*^oilBtJORmF9iKB&FshZN#y7k0-v2IJ% z4Jb7-$-nLPn27d$Ple*Qdrfg=HX)lI!f_4~;n2h2h`xHU;W38* zWjd!xiKbDPZo-`;3R09Yb49yreP6%XCm!@GxOHw0F=JfiaBr#!C^j#Y-hU6R#j7%O z5>F85BW#fRYO7197v8K-?>a$xIq6%o(Hi^=|+&okWkBAAG)hB z`HP50DtXXezP4JVDZt6bd?3+3+AGdE?hkjOOWn84_Kn|YLuMpLoAx*+KkX0T#ahO5 zbt?m8Am29=$1wYpz5>Oj)RSiZJdsJAL+TP#|H4(Nf>gq|r65zb4;K7?0Aa~#nrWZ7 z2__ZPvu1QtCZ!#0*$4?|n;>Ik784Je%rrjCp%$+55$|>T4Z?(zFn> zX}00GIYr7ueeneXbHQAO3r5-LtLWv>tXhcb^~>y!WrjRvoe@H80^aKpRcmiv_=t(CN{KmFWx-lcp zBZNETcb|i1*Ivl+a^?)(uh?#a1HXEb^5cY)K2KB6lz5#vE5gqGCTrTq|LjMFk634M z-)t8Wed+=7*+YqY%X5@m3({A`v>CsZ%=Ke)u$=*hJ|hw?WY97~o;s$vdiV6*znQY`{K1~{nEKU^%+|>(KL>mT9q^`&e(b{> zw_uf^*DcKIbhogAfj}~8nL-!FVn(R$LxrYxZbvo$6wA44>{vsdzc>Pl6N425eBA9Y zBkW4Gq(hfON3{;7$%1Tjly#5qBSFcR41NNxnn`T`TkU;6n3I75bjZ5%vi(FrjVkR3 zoh414Dm5tPiT3NP-vWx3*U>1d&eX=cT(E4s82mtM@WBP$s9eY>HE_T2>mJ?$HoS;U zzd57^T7w#2``k}I9t5Gxteel%3pn$^3&qIdL%K@_MGm}elwsUnz`xm55$UD}SGqyc z>wf*?hzdFG8s}U!yYykLE0vwKNx)$L9kCfE26?PjcqyMLCIijIu@+B%-@!%tu4e`UgFj-odi0AFQ@BbN$}NbtoW(h(X|Svlf_z_t6CCDqNJ%zw zJgh$nFiv5yrK6pA-0x7zjks2 z>g*-oqxriO)=Ir3DaL@Q2xr=~njJ-Z@Zlhu+?^b6&Hf~pxhCN_BTU^duS%%9Z*)ey zygccADwxFsE)(f{z84qQF~noy2zPVF$E?QPf6ntgj zd+K0a0sy2z5k?hP0KKa_XLAQKhN^JiSSG!Z+J;%4WA=k>J>!25-t{aWQ_P%DBfU*% z`*0tdg?h{1EnY$E>MHa_#q3;;D9=P2y+WcwzKRF-Bl0e0+Pq&kE#Fu|!Xzax?!@g` z80V2DmqnF6C=E<;-zfo=1#lQj`{goPaIU{7RDk&W3!6lgBSYMlH)r$4sSfLR_?>){ ziOo8+D|Yjs&IDo!=*2)yC5#3$?*>*EhbvE)Gx4YwYy7~9CJ1CQl)gQ!G z-}RBCj=>He&@*l{j;>lSqq<*3ZDUZbsjc_Wd-u;1x_o~+ftKo>&^Z|Txt9rWiIlYc z&LQ~9h;UdSXyDDr3^>l> z7Azl+S!Zx`A^5YOk&2t8OXv!fN~em#TzO_m`Y0l&toT)rY`HPV&2J@%dpLSSGfN9+ zpBy$Kv~S#Rn2Wf?$a*eP=5*(aW3;PH&#YJ)=%mowk`MGCxZDu+MBzYvhm7{ zg=} z-}xAyV=fdF+k3~lJd)WTAh`D0ROnj1;Jk*j!m2WJUgBdUcb=ORw%m&dbff7~U;a>$kExnImMWeH+{UCUWfsGF zApIa8DK1kjmmS@r(uPRTpCOrnUpZ=IVx%4QAN6iz%cG`c#C^B-mim%#*5PH)7b_mq*|!yfQZqfM7>BZZhSo&n2uK-^?K6B;Jp}< zfhVQ<7JFzU5&vfX_=AgH{fg$Lx{S&Oi49;c$Dt-vxN*5<~<#9;`}Aqpy7f0?vM8U7TYVq;Ph$^;D}g zdFM5&4f}(DoAcj5Tb_f}W5*EvW_hEJ7{1MI)l=5@et>#MzYwbD;F|ik^_q546SHzs z2G(}g4olF7zmJE_28lL3p4{I+^IA963@6PYw9wO6a!`Ou2o@7YBICxM;!o$!)RxQq z(bgb}=+P0~CRrsmwkY{PJ{9xCBtU!AD;4Nx@9(s+H$^>-#||b-dZIHj^;$>$!yCUf zJq<|jh)8~y?oYxqB@HT?Z{|nsJEMPM@)^F;&vH2jWWgN7SCsan+z0xGDHc=f%Q!$B zSK>$gT&kHxd;r|MC&k?1Q%)5%PK_KrpW#++N8YBV-S?Pym|1wM-INwS>fP-d-^*+>^Ln@Sl) zUbvagGqy~MMC`zwXi|ich}W7`c=wryyz>|ns-08NYt#}GU=vXQj@_?{&-Ism>xrYry9_Oe z$;+;4VU7tZ+(|t`)VjJ~=p94t1VpB|sGB$@gO4irc3IXZ0qTB^w9FeK@&e~_o|%KFepibRews`5YKQnOzQdp8je13o zjAKOwJc{Eh=ER66EL_@soS8Ydw#aOKyl>?V+Mr)i5q9Rk9rxhza~B+Ys$?_pg+IQt{^5x2k)vCL z1g%&@Pk$Nhpn{ z*%(LlP$^0HhnZIt@aaL}AEl$ApDPuNDJg*+Ek357e7O(b@tbPMxBhc+B77uNTx>jP z$@+s{Wvqtg0ZuUNvqlE~+qKJKJBNdVlj95qoA7QuG0j$_679AajC`vC6$~1%z3U7F3^V+R`u2w z@(Et0^G{J~;qBEu?j_PC-{J3s?8Agmdp?!if|S{RTkJ~Ed)X3A3+~#y%S`Lfd2Ke3 z$s~0^xDRucSrSOiSWpQ6g2i(#gBXh}53qm~VdXx0GF$_E!pgfUEx|qD*9nA7!63`n zv^8#D*Hpi(O#qw%)U8p1EYJ)v9*gDtVWtrMv*|zr9kAVMcPzbal#2T8Vbuu7Z>kT( z$}bH?MLH1>&6gnvWn3S3$_f1eq^e=;{{Trrw!S2vg$3M4RX>OVZ?1hv;MM|_`w0!2 zroGI~sEHkGv9S&F09#AhMM}Vpi?HXZID3V6LW4FO2d>aD-ET+6M!nL2g~=HST|rD+ z9QR|r0bHE6!>k%lCSjvEPMY_H>qXU<6)RS#70XwszP>)PDfE5D(}cTn`HDJs{+t>- zJE$&RzU0T_vE4)H4nP~EqX6Kfi!b8VzxrMM!CVG0dWHf(o?I3 zX>38LcIg0$FJybz3#D`Uw920`iWab7EhMU`=1W-Ee!Yh_9N`^NVn z?O+C{aj8^7l=V|J!h zHSE%S0b*&eY}GPt^>Ty8-8O2Qqzsx&vaz+nq%8=Gn~ef*_Gh+)4FFbiYl~O(J>xNL zaAgJ#SWS6fd=~6lOAv5tW~QylfVXlMDULsCfXs-tuK;Z5OOi#3y01xrR$SI84VI0) zM6hG9`7HL!NoW`g?zrO)b^m?$tJ`nCL#yREhGyn20 z|5EzLfBeS<&^F(eJ$v@3H{N)IWj=~}Ch=Wm3d6f*fDGLDmHqr^JYiX$)c`e@n^gif z$+B@{wxvD7%2UZWHtYvr0^kpe6ytTpOJQZO%9%WpIf_$$$)~{xBxPr~NRxuIu)&oE zlbpAI6q8Q6Up4?RJ^))NRRtY3l=PC~wLzk?JoCqW#vlN&hnXwl+vIGsTJRQ>Si+gj zSnXMDwczSjW7^Vmfi^1B7B~4INVJ<-eHUnxeawm@a_Wo40dEqpv8_c-(MgJ}nj}WP zS!J9RM@U7#$pd7b8`QW&8Mra~RxWvDR%YCCeiG63p&RPTl`B3-aM{*cShh6TwRTkFahP5Y%Iujf8vQJ)T3X0RBha}iL~qx*H_SOU|4T^wJ=3)Vt}-gsL;T`w^f5Z zl=Hjow%gTn-+oRVIeJ9><~P4lzxvg$)Wu5|{RAl-ANrpJ1WnAFtKO{LUf7m0z~l)j z+dAptpqi}4)1kNR((9k-ZT=pZ<_ACcLFw=Q?(a%66LkT!&4WchI6O>D8H)PhZR_jn z`<5(wWQ*xa5XT~>(p8$2WlQoo8%#E)%(7voWEX;us(4_FI|4*-Cjjkj12!8B;JeeO zPtyvjeBHWrRH&E@tu00z=q4?dvvPw(f74jV*c#zXx_6~wq=Dq;%!Zn0kGMwwTx-{^ z4FtkLz!AoVYt!QQELfaYCX)*s^W?&2f^X$4(gmiiI;KsEb#o14PIL+qYqk5Cj4P7B zC<%CDo9fGbm_fNq5AvwWf^(zX^NgYxG<$Lc@W5cq190Bj!mk|X=Cf^OH>p=uWL#a< zeG`+a;o%`awoA%<$XMsNEL&P2Elz>@pot)!DJI;oJvR-5I6XB@0P34heN#R8#FJ{p z$`v%DzBznToj-S;?m3FsU%q^qM2flELW!PBj=(Tjv!0%A)u+b+OlC2rbapP%c5a>e z?svbdf#8$s^*3Huuf6sv0Yx6aupey8tedOd+U-uVZL;pQZ{J1yu2oR}&-C|jt=+hA z;X>v={^LJNfAv>?wE)`Y%Yp{@Z~yjhzRZW7VZ5LlK$omDDg{aH&(M+4Xk8Dhl3fBmRjp_ZcQZ+8)!*VRxC!QsM z8!9Haig(v~&GK(N=eTt&zi4dRd|}$+?zbAIjVFa%=TZis%WaUP1SY1dvr8?~12P8t znXwX4^NMZsV$E3~kE;*;#Iw0(NUm5mE~Fb)1U?9`aU+LY6#J2i(k{?tcN_pV z1g2t!1V9600B{3PgIEaZjtc{SC{aThTUh}VQyj-JBhO{{{^U(@`f1}Oiz(!2A)p})-&ZoGx`NFiN zwVnO!*{<*;l-;4c4P0FhKKP&>Z118;1_osGeVL2{um*sGTDu>7_#wqIojW&J;bO)9 zW%giL%$qzf27BzHg}uUZ^~yEFm=`_QoUvWxp3OIv*Cq+vnuKe#g~WC#MhiAvA8+XT zkQ&tIed_cnVwd>(BwLd)l@Zu}RGV-R=kp}rMU5XgXV_?57iWgHwY95fpLte&^^r&D z*<8Kqv5SWe9#Y4S9@9Ylk{UM?OzCBDxuyA;p)KGzs4%o|-+nc)WPoCLw`|!=W5e=g z%hmV3`#oadUjD^PIRA-5e(=EunP30< z*QLiFe|!P7HTr^wu2HsPmQ4?ykLrPVD;p~t=}QAAnKo`LX~}#9?MDUyv$5b9f6i_tLG3QtRGv~WCOL9W^LCVtTu?`k993JkZBbh`Z&8c87ppHn^kubJk3TQ|oXC@kYr(h_ z0a+QIS?>R#s{SUuk+YFEhFbPnANuwW&%>5n%w(j!)Z6(9Vm2Kykp7f5A z+j9hf?fNy6tYEFn?w)S{JlH1UcQ_Buu8aa`aoDtK;@G0Vo2%3zCiombi?M6Tz!LTB zGk>hstzAdYA7FLg=lj%2eJ`=CZyRbb#qvDI$0sO^2Ip{I1MM9f08w~f@TgZcKKQx~?Z^KS6F%+$%DoOO{ZbQW;N~Ht4Y{d@Sg-%{|kWHjs;}(Z=9~ zSr?v!Jo1%C)U(e%tCr|V1c1%CbLaIyeo`GjcAWILu3o!JvKvyvi`WZ2crMbQs(+xL zVqMp7Sg%&DT&Y1{n|l25C)BD{tJP0_@?&-E_%Zs;sHq7hfm;xB#P$kc0^0#dvfah+ z0T$7BaDHISCNvNmpBPi4VF00{7SffK zG^9tvR#niiFKB!C|La{{({?@cqaXdK^tXTew+o=HQ5X2yUwY{!X4$3(1_qwd54=MP zrOJ#($+*evDw*As22gXocWESHh0%+uDH;UaIJOBL0-abYRJ%HC5Yxtgqho+2;(HPV zdR?wjnjvxUa4zr6!cD4$$|Xc>GEA=vTk0n)O8R zq6tDjc;J8@)X&gh%ki5on*u40D;fZRB{_clxZ1OO4*|Dr+qP1>Uar^g{6F6z#%i+v4P;b5cTOXjaYz&Bru&P8HtH$kEGPH8iEn?bW zZ#U`Z`KZ1}KgGP`-~avJOMmk>e^a`Z>XF`3pzX&${xLdX#(TDz?j=i>{E4fUnj9>p zONrwu*wqvlfF&JJf8sq%J`f-F-O1lEEKtm|G#whajha8uY+e;Odj25pT7$`+Wm zbU{zT8IHBmL1!65J1A# zCr_PJhYlPfpdA$|k+vSEKbMt#G-5I&AgZk~i}Tb!y1VJw?{KNw81TNK%(0B%7uzN8t*wl0=SWcOB$YilH? ztsdJ}!`v~uJt@Kic5dIPHf`EWnV1OdhXl>(Q>T3wX43xXCpt|Qh*1If;kvu9J9qE8 zTfZMtmo8uQU9?iBD2Qp3o&t_7!#M)HVG=6ES;3m2FFETU8>UUh2xpC}jhQuoHJo=? z!cx*wQc3H#)&dY&LzDnp!SgCCU$#;W^e-VjI)r%~Idp{B$hqpHI4%I=;lqb1b`D^A z$-q)tFI+sYCbN?Syzw~$E!Vx|c7tcf;Lf&tG~kOu^2RIlxPINDw>f}aB4i4n?N*Wh zxMe`wM<0Ep-h1yoc8!+6vgwDGvDLRFGh-KOmuy8^HAUkvZL%}k0=hx&QG0=*<8*zF z35Y6l6&2s+0%<`2MY=^NCklRs7tgdgrlKAOf@5HKU?@!L0b^`JwYIc5)wrWTTG)3% zfXx*vCk+YMHn0@cW|F#?ReREsCa_A&;NL1tTcc-=@??ntOMhQKWiL|z_!T{X9y+AX zoH<)DF|D=zA)e~M{sWY_ux9NVa=&iYAaQ8u1|}_kyL9n3ZBc7*$Rwt$4PiG9YpG%`z+)D^RbsrMZ=!HAHZ6eJ4+x^fT7y znnZzXw)sS}TDxv7J-30r0d?*ARsY$terS+~fNdxXLCdUDX4u%CN@*;;cIxl{NVgOC z>vJ!?^iolS^^%>nx&Yedc=`VKzt1dNMn9aDdN%eADWf6jRjGQ-27rC%(lwe!rf3lO zvt6Uq33bC{KYvslnw?026-G@8R|Zueh|9D^0Xq8}Sn7iBBY>1CGg3F2o((rTE6U;E z6(X8Q?T27zlc`l`pSR2~mCqzBek~~Zz-HTIAU7KwJe$Qv^*zWP6$ju1Wt9e%AgULj znh$Ypb0zChZ-5rWvXN$A(PLbf4lJYB32j$Sojy%~tsWC647|sV9jEz}d5WZ^HUgTJdaYuxvq}a|jJs zNRMPFsjlwDdUEPbjL)CHKunwKTIXURSE)Xq);3R+m$H!;X&ZR`il6xkS2mAVF1=A) z7wh;*kBY5H=>Tru*1!1|{q;CPrhfkOpO?P*&2KJ%wuZO>7$)72UwP#fu7EJz+uQq9 z-9VOe3b$n0xO2-GE!HSL%#=jLqIx%D2{D)^`Ef!9Ba((Rxod}q?Rj8#I~(r_AD-7swc}9 z7re0?lwTLS*^A+WkT3ucnEZM^=V}&=2}mHpm;g2q=ZlaTu!QUeXE&+qKJhrli3akS z%dyUpMS8E^W|!XP9qi_ZKm1`4bysf%7wNnQZ9wWTzx=W~d-kla&DP)F|C}8wAsd0c zFp{^ws~6K|yG9xC1i3WB3=jVvvrlH6f-*+Kk`Y`$PX29+W!g1P+4X=iYmjs}*9M1uvmbm^Nd}zR?wV*MdG8B%2i&i)c-ycEPLgs|aX?ub z2@3XkvXlv5e|uZIKUu$a^{N^j8Ewch8h{d#GO*2PpY3{bedFd0KZcR*p|o8#i^gr7 zftfD=EeIJatSGP7r}Z%a+H&u`_ulmT@4sIHK$LoX3!rTd7CgM4{`9Bbc&|uQc4Jpp z*H>KywE0z{by+?N4Jt^6Ax!{h)4S}mmnA;pNPI9LHP>A+Gl|mV35#zFV)bUY7xkQSe*} zT&K`g&MS@rxol4|nZsUsSfO(V4b1+@;Ji`>~y^?Y?~j zkm4G~)!J4pkobUGA2lk2CIvVL1i!P(k`j>gTA20#o9zv#1-!*&&w{{OT&As_ZqlSN zZXEN>1OrNY;r>$MnFc*b0BIDrsH<)G5-k+#>{U77su;MgR;p6rzIL=HkQ&?``8r)h zj3v1Q(Dwi7kFRU{pZ(E~el(47vX(xv0NQF?@Bj`TJcud?%(4~x`}?2JpLgLQ2T5Jn z6%l0d) zdx!rUs#B*y62-SonGRM@og967NbR3AfyX2Wx~RjyC!Lv24KCqY7^%3|GL|B&=zSbE z#|CTCQ1k2%_YAQeaAQ;hWTJi(*X^1Xm}*2YHk(Xaqr|hZJwZEz8;Wfj#AR6$qMCsg z{o}4L?IQQ*^{dzY$ym_BS&e*-EqVe~7%BL6%C@_4z*-y^Y8*D)^<6y^2GZEK{G8p) z{H);`CEJqA(u9=lTB9!GCe9erEZk1V#>f2FMJ6p=woKN-m5F5>FD#SxIPuF&61M8s zFX?0a4@~|qe({T&XFHN+n45X&Rs` z4$Efivn2s){GMmopq--tn~PtMW)dMAYrDrxT{-rb#{^-8*QsREho;@0*J!4 zDh6OnBhrBAc5S?}#wFR=)k&^W0LC^1N-yrFSc>t9aVnM!_syhLL80<~yXZcfNt6VEsEul?GXKt+Z0RRRLiiT+|#bR?xY~{ z&p!DBfrB`|1Arjg3(22+S;;<306Hn)O5?Xt2@zq;T+lPBHY+vG=N#APxd=1FRv8mT z6)xs7TQrWKH&<}r_Su|oR^yp*-+j)N4}#i0dpBIkV75m?CV=U`zhyvMP9J|-DYXFF z=5m3r{KXevq-SbC+@{XX&imOw*qMAT03~Os^~@S|4g3r=SadZ`sVaMx&7zS^BTL{Q z<3FNYo?++CGZ;|Vh@;6!5}?Gki0P5-iGivz_j!bZgePZY zLh-DSUvWMxKAOfvz;0hjBt_<1Tw9W3-fE?)Tm!rutcis%(;t$}m`={JVSkX;*;_@>U-I&S8$%CIGDYQ!=%*G6qWzFw(CpuW|vdq1{OeDEei%dz51@v-rnBFo12?EIM7gnF_|$c1Gbq#vdb5x z1>RhpUxGG2M!^yfj03FryzSreb-`$7sWIoM^P$PgU|@WXSsOL>Y|hseLunX_nGoW* za5>7DloIR9QFds-WOfbtrBztGaxTh;u$B=SgGsMS1WMX)G6IU%W15=J)G?})NsOmtI^jwnZ zW{KCYT~n7YUG|bP+FL6!Rc%0xbC8pSB!DKac=K!kss*NPcBMU{2KcO*dDwL}Fg$$lu*arlvi>Bda)Oej;gRHSiqjRI zfuO{ndV)1+Hzhz}mWNlKM7v}|?7qy*4*EL43{7%{CY?R`t`@*aASFu*@&vV3*JxR< zG)J~+anEAa*Vm`|`}+u-Bf$%qsiO#aF}io~7A#+}Om%lJrm+Ba^u)0f>gwgIfp(ug z8$2h5r)JW`JlkPP_Wh7#gzW{A?MljJ@%P3>`kDc4^WH@|-$5H|vz0V>*{B=b-Q3|^ zS$zD!*`3I70%5dxy;wGxIU42`4I3DwYt&AxAYGeGo`hyQyNY_o)aqxL%9|H*=H@tF zahM`?m}$~Ko1C0bW258h=EzMoqJi2dCml>rry`;ohk;1Kx^avhGMEqm3((dIkxqS2 zpyJ+4i5yKTmp2_glNI2#jN{6KfOmg56&(ujNG`zZ6j;;d{EX)!DSI~ZxNo-5Znaf5 zWSdCB3e6TU`=T&y_0AlPOq5bQ(JIC69}s>sk^7q2{SBxS8+{mZ=ZPDt_Jpk%lt z0B#0ogKM;;oFFDy|M1u^XllY8zI53Vie-dX0`ZY2j-Md*40aP93xF=!&^i`%sIA+# zlGg>c?CRBPp>=HWn+mxWac^uPeEWXLF`PYRyuD=Gq!frKYxS6MkzP`_fQ$52VA`l` zRd;vyBl;nANZ&Z?mdU}4Wj>@oJ&B-hEi9Yu(%@Kyuml8`0hh6OvbLY>0A!3NyjZ!W z<|fq+Lt?UXe8Vm}ug9d7d?^ofP>Ss`B8YH-LR}q!{^Le%oXgF4=i;ewREO6xFG9SY?)+4VblR8!}Qu_3uw)!xVA=T+GZ=B&Bgvu*pXnwqwXX6F4&UWZoiF$ zPw}_U7U{l#OlOwOL0{PS`F<)y?o}UIRPLKuW4lzjkC0t+2;jN~<7(NmB*2bs3u~Qm zr39O)0x(^*Y89<3maiZ|>_wfOesu|W6wudCpFx$0GZYfR@n+a3S1h3TUuZ_Qwg&1$ z#sP3awX!99Rdqko>sk%g!K&hUva1)zdFSnSNHEzKtJUoxrz!y%uJx0TKJjBnY$+w@ zaf>B?{K;D0p(y*#lVrQXjlI|jdHTHIBHg9$-UrYz@z*%83d`Exm2ksf|}luJJR1p+hahAq}Viei%#x+)$C|?jGN;Flef2@Ylv; zs_0!`#jcoDGXqej@w)0Arp6tR)3UX|ZkdIj*fyn6`Sv zv$?l5ahIiVK7l8E+jnkPJM@Y^X4^!#!w}Pk?UpTDrat@VQ#E*YkbnqSG>V7KWewAo zp}4n<7hmDUIhk|M<+2vAoE@gkW+b`99*em$vxa40U_jk{_ucA&2Od;Awr{8F?O3#k z;(Bo%CCB}nG}>rqGzOlc#2QW?XG9X+2LbHn^OXZ^vOr0azZd{Mc#{H!US) zL?Qxj|Ac;h!{CLf1<*D(ONLF8ZfNVey1MS;U`BhEB5Rh(2Eu-HI|-p$mHMQjx-tpE z1=d`@mxjYtL4miUncw(?*Qra2032K&{_#uh^RkTu{Vf9TW&BFb4^Zy|#_z(#3+mkY zb2N)3gIb)f06d`TVfdB|EK$psFQ?cva@xiQ0hz#$G8JE!qAbpg_k1sStSnk!%?9H9 zykNv#gUeijqg#<}o9|3p{9@%ETkZ|y82GZGlNA83JMOwetz5Z+w6m^Vy+$rD*o&RF z?^N%-^PW0;W{}$U>b0xX$rGn2=>QTs%tVu{4zX;O%n)TX7Cpw<46b3)Le;#FtLMf8 zysSmaHXqBjt=rT$zVQw9;DZmU}HqANC9PUB zv)Zt6y$^ucHsM^)4W6qgIaaT}>v?A~f#oAz_eRzWm%nqBZ{$P~v;EifE_(HPUCnbB z>3nC}*fsi)Ze-ou8D;9Q^nJ_hQRy0u1Gth79C6qZ83Ptn*xmKY#)vB@1Z@NN<`l7K z+Hg!2RA$T%&I(7u!p~W+){PlD#6M4;KBWdVU>h47oxx7=g6uP5;2tk>@tZeJr$>pZCcE5y+PT0W!h!~$W^mVdC)?CE%_RX zS>=vxUIH=zDFAGD?7E$5i>&7-Cvp))l#>B}@^vYCnyhcx5cY-Y`#lry`=0Anq%H_-I`i<-Q`bRwxar9F#)8~W1;>_988nAiA ztR>y;+MYY;m8(|_0MAprNh1^hE%g*wNz^Yz1rsYK9`VY2TiGlrczw^v$01Pu^5qp*6?AE$e5a z+ja=z{9Xl=l93%s-$fe4w8_vD*ce%09TTOmKlyd_&2N57gRKFY%$`1dn%us}j~!Ph zPo1LT=7=@rvPnVD6ZZnZ7J}W&mM>H5*RNCS)~!=ZmM$e=_9rj=soHhtUFyXb|Bw3Q z(@!eoL?A)L?$RihO@3ElBTK5Lq_4awH)dA@;&{cdl4a7{gKNMf0Eup#yufkVVSUd| zFbJyU?7AOv{FB>B@Mvqu>A!RO???4EZ>f3iBAw5m4NpVcjoeb*Ngm+XFh=3F)Q5b? z^1)X#Kvv7LxtKP4z>Pa-*%cmU#K4*O64oF~dW?Dk99;LM%X&t8>0*U@+U1^=fRsBR zTsd&3azStrE0xyC!#FVUo;GVaf~i zmuyPCHQH>z#_?r{Tl-T0sa?AWu%Nj8sgozw!Gnj?$rC53ZmKQ66vi51JGhn+`-fzN zg9i@MF*a`8ptfw?LO^cC^5yC;zV{cz?7i~JD|BC^r^Jg7%tvav*{l%-^He5rAr4$UBE7@xM9^#W@$-pPor z`+#lzy7lV$=U*W3gzWc=r%tH@`wyriM~+ZDpR8Y*xX&|y)5kld_lx-4qsNY^ty{OM zEn7AdtMgWIYvx;Kn_&p|`E>~$7(}tK|ze+(hY=_>@=X>^1hzVFS0@sBp z|Ng<{`@lo@t7Auxsi7OgTTPa31D`&3KbqUvb zkcxw={Rd6(rR0Y#YOX)KLw~hFuY1)z7TTTfpe@tg-TknuW80ZGuF_ROC4XuGaB-M6 zIa{e^KT^Ea+0ZSPZLUEZcPhkrz@SUHi#UbZD@&S8sT)H#R9{~|8IidvUOod*1~7%T zq34-=P#dimsQIEZZBcz7P3^UM#-8)hbVV5 z0k-BDz|C`$=KLUU8P^7eZen7bY7PSk+@yH8OlUGfTyPe}(y`|R44VXO8`f`B&piFK z>g(+#Pyu#r-@g6o+}S|_aMF&}(*JouY~blrrwI&#f!ncT2bBXu!os(oeNMgf@_$pT z92T691a4t=!%p@narQr?hZ2%T*cTYKH-Gh}+PGRPpaX|3m(B-pxIMy?tFR?Tzu?VldZGsO(CWgch?9uQ$G2jC9X_7E};h+{)MTe*B) z6IBi&&}QP=ycoA~2`4{`)WoTx{xIBQ!xR@bJ|i!10ZC+f9->?};11e58R zRU=f1_J?v;MH*YDE55I2yQ*X|?2F`Pw}Dk0PlBtmMpT;;9*#%#?|-P@7Sud-kb`sLr?|1v(J^)G*fDkF@DT!0 zvk?P}{Uas}_ZEWQI5+6OA@1$od+t@gd*^p_o&cG$R%TEMLw_~y#HrGh$rg6UF2*@O zJV&s66uVdQwE4!z#))msWpfqc$sQw?Q_U?okB#H8mc~)zvYlobttlVeF~IZNbFt)$ zvAfEBh4m}>%o&)=ICYtgZwnH0ZBqUBefs02dL6`1^!L;A&89UpXv2;E!4H0*R<2x0 z??3x1?=6>F?6i zW(z&8VLvdTVRxFVeX#5dm2#t`gR=fulc|j0t;SXA1Gjud!F2S?nC$Ct z%cd=A-+_I83?A#Z1yx9407akYyjU=@hghj@bo^@qD1*DZNoECef=S+EPTB3vfK6(3 z+Ldzv)}-z!Sl&E?Hgc6uQCp<#jQbw$^qeD+C64{EL!2c0CdUQa?P{2|mHP9Ydi%4u z6|JqU8I3VY0MlpBo>kxb-uDuhGFt#`R-^yg*S@A!tXM(s-+S*pCXc8J!s5k?zZ{fM zVEejljk8(-Sd#B+m0$=1jFGWmtn8lv+| zh?q8&NpvrR#C7AuxK*nk_(cY1jRfH0{w5BHb7f|F1KBjO+B|uaG9X;&yka%(P-feR zaT8$9+pgJB*%l;rps5ZR4_(%DZwy?8O* zm$PRFN%U4G0L-p&@Bjt$?+zb6tTu1nO!slwvgMRLfBDK~I!}a*kc^1sCbex0e+L_i zGKJGc4Sb4Td{|uj+iaLqFS(@20oqKp3x}Lb1!KmU$^dK(+NM0vMk2*ihKVhBT>rc zZ4)j4m}ar~*#tL8>Bvp>`5uNz~^Nb_YS)5q?HkHECy^?J3Bh5i%b_2R$ zsOB)&eSidfFJIR8vPG?4vs$%ka5m7lgwBz#0rpM0NacOWSamwF-Q>8(EV3&zs{PZm z7BPxt;ATL$U{7~b!D%4)kz%5rG0EqqI^Kr8qzD}EEOX6X$ z?WmnC%CYQ{%`+hG0sUQv{>cci#DD(he-@v5>ZuB>u>jgqSWccisXqAN17%pYQhR&* zR^0$s$%F!1pw(8J=Yc*bkTFew7RHQ)WlP3k)na8^atA9J2B_o#i)w6Fnv)M0|3w;{ zjgF4^1_E&>P$uu|>m$KjfHoKx25qQGikT|hFBlluwT*4?+j`E9qW4_}#3Zqh5EMkQ zYe^a0MgVAy2okFWVD)CwY5{z9Mm$&=48A@)ok4YkEcF*JtF%PSinm?Pi%&CgYV0m6 zuf|o{B;BOmfRE&Zf(98^uU#c^+Ova$YGmXlffB@SF$;%nIYA(dWpjbCIIJ6kR9qk1 z`mW9{x<0(WaQ*_>jz(#ZkkvqWE65a~ssiE&7cJ^k*Kb^_aFcS0J69W$Zohm}j`leX zHmjHo1a1u2h&44#syi##dv5t$u2O?fC=r->s_=Xii8%wSO9==J+6-XJ6+CyVA4jPF z!+GTLx!6owklmC5^DcYI3>#wuaje_)x=L?*2w(m3m%q&Dze{s19d*lrHUKu=`F*Jm z)Edp2yX+Flm9s}^cC3kJrZiU?^=2!epz zY*YI4-K|>d{hJK{mvnp1hD09$XpsTn7)ZUWN9grVZ;lTBPZEc*iz*&<0BsspTlt<(-GFiW~HG)T@(dro!o@f2LP}AqZmw z5J78^vLq)bs1INQ#$W@kKCE_Wp)|o8OE)p&0ssU60+0Za;BlkF)@Tdj{%+`J!O0ys z@1dJFd{z?AE+`>^!5g+E;H5$JID{Y%P_HDYI?OIZc(%!Fre9jFStzKp_G_D53T}H4a?@Tf3hlq%|5%vJ#$_6 z>)*VHP4#w#U;gr!>bd8hTL5ioEbQ|9?Qeg}@oX9W(3j~M-R-VyLiuW!iIVQqq?vnZ zv2615pmT^D>lB%dnqentreOqjy-dffTE2XRDwe!Dd;n@#@i((-91Ft+N-AB$D0H-U zsP?vw(2^;$aj9&}GMQxYNOqy+C{``1v`e+mbs-rc9RSw|>>IvEe?SNU02KO?ZyWW% zi~6EBZVXXj^r7J!wC(uhIDxN@k#=Iq(D%6Rs0Z3uPGf>xpcGrG2VoAH2h+vu9D6pf z4VRFz!B$kfTHNg=%ZATeTiSfa5NsBJ(QLGB0teGYzkoY}`J)~s?hBp+stLeNNbc&f zJn5IQyH{q{+s|B%WzeXEYzuzF6)?&f`sJhpmT{?M+nW8D#=OW3_R5syyi#Qp*JfO# zGJCW*GoFn>8<@XB!DIgLH^i191A|?lUD<%G+KG%TS?P*j=W7X3mO+RcJ}Pq!=MeuXsd2)1NMN;50PC3c2xwM>8J-dOTw6i>2w7F zGya{;)VQ|E%m5X$17xBWoHxxTb;q^A8a{^+<6u(0P8Ms#;6rKs`DPQ_R+@8{FK3SW z&r?MQ&a#oKrOmk}Ah!xYOY>aQ0JMz?;9MaVu!~^C@HhGw2Yq7&n44O*lmg%pW^i+O zm;_$o4n>eW>@5KX(somxxGMS|6`{`(!;K>86j*LD^4J~$EaA8SY|wsYXV?9xNxB){nV6-2U_AXJje3)s2v$#x~-QcQW z`6Oryb)#lxN`5p#N;3rzV>YP)8ah0{8eBRw!*8n0EMv4cd~jY(b1|qk^-!Jf~Wd5atTNRSVu2xB-}9uteAa`WLfdm>3}(fs39a zrV65}6jP-KS*)brW?X3PZHrW!1}6lJ3|K?^Wi}jO1z-&;{&wj|rX9~FZ8;@9d7ZVZN6np#K_gTJG!P)l1+ zYBEagQ81*4EA+A-GsKGJ^s_3E7LQor*GEgp2VxG{tm|OjMm;QUZ{%k**(3vx5GJE&# zEn%!d8(siyH5yz2+R%zyySlo*WIrtFLX*s!>%rTWpx3{m=PE){UQcy2|y zxXM(BB1WZFTL-IaQ8pID#Q7l03$uJMZkz$f^gq9X4Y+doin@07D*cXU`DubOS8Z6j z3ni;aG2b7vQu!X#=tJzr^i*Vfut8grEYdV$FmEM5R_nPovJ^;?NlNguNN0dHk7c8>RRXkSC%yX*wi9}Nl)bC} zVP7RxuEfAu&PmHa%ID!K9M9f1dB1Ger~r@m=#Tyjo9W3-=6ApQoth8H2n`L|aC0H^ z@c#Sn`|)hhXVWvA9X3djfXxm-l(k3W)Hq90ZqQ*&L;69IU-r@i=vQ6fnMY>9Xn{=u?Zk7L!3itO!Ig(FQK-@1VLqDTAAnCLoCO zhTCV_+&@1KV`|D6ACK;zHcH<`x_{-_WreYT0I)Gy#6C3T13{o%IkPTK_Hr12O9aHf zAi!)kS-3`$0_Mby#OF!2H7##)GAh~#fL*~c}JlnfUDS`b(Zvu|$=`FxhGrLyWpvzL@0 zL0it~wfPkhOg++~ag9zH8=BF!dCahD=gyx~moH!P-0Fs$$QiK7=Ul5BGOX^XT`|EP z_l;}zF5RBn_1l~H=r_Ol&2*!)Yz+_E(3lP$JP1W=zjT#uh?{gHSRo&Z)L)a?H9RX} zwno-0OJh)~wUm`T;JRYd0^`-F_o6nXyx22AaHn+3^qEYubeHIw2YGOQ!iTx zLC6${O1eEl$0sj@7iA!66o?B^dMH{l-PezBVOv}r$FG`W0_iyreP7&x4aNB8GxPz<8#WqV~oP*-`OxY?sg z;@Q6F;OtfbaB-_B*xxbOLfZ$}BCW8F4qCys;XMzi(j&ov@#rV`JtrCPpc{5RX}d&& zCNH^cUU4L8l)Ej-g6qRQ*|B50TDxJLx_ae`di%G(Q#Y<%r{MZ~@4r`V-L{4L^6t-e zt5c^=sg{~1eviOpPHA@mHg=8T*shs$QupW=J!>>79%ilq++0b4 z1bxRg1MMCxHph?hpvr?Z+f;kfWS0#LbA5)fX0?Jh8F)^%-oL)uSvCez%a$)w+jeXv z`|-iM@2e{s49Q9l(6GaK3=a*f&aO`N@S_j=S(hUtqw3w?y%(C@URhGni&aC7PWhcn zmWn|Nzz@y^fE(b8HSxfpB9!E&ws#aBLv=p$s#$}#wp_Mc8G%ccSsA6xO--UyS)Q%{ zLqWX0hqc<2@2T)uHk<>5(l1@UL^WPdoIFWF=wRtEHZU0F$p8{Ca8GJwLxWT!?DirV zOSzr-yjS19t@`gH245_IwltP^-+h#dor_ev zevh~`Qo1$;hC>gSvEOD=twzO?003*lLqqD?_3Ko~8Uqy!haC@S4?;eGm5ic^4|J88 z!TpA@M68QU*LRf~W6|IsW-ackOz)p>0ByG980rCVt4%~mlTAwk-hw`7`)RWt!4f)| zNI;hKv!q}xD9Oxcy;ZR$n}HQc1{*f5SF2X9QU?zlQU^Xi;1yXng5TKYj$OB_4I4L5 z|LY&<_hZT!q;z(AnU_KLUmla^&>11k6sKwqXY67DW6I};&vEXECj;w-y09KI>VZ1A zKik?oW|iXFT0A#tix1=~D%>Hvwfkxf>5%ux4hOJ5=U2WC35NA|yYyGD2+-E>OGAS;#7%tm*=HQjmV=V9 zo>gqO*)Q|3GB|baTviyX%#2`GnV}1iir)gXNikqoo8v*!7XGXTxHVFp(IhfDd@t?UwwxKe zKt$BFaaNs6qtO))WeaZFzYSZ;E~A-XCWws+3UhGXLwPN60bEp@2m@nrKd%;$C1v8m zBtR;jUn!loOL1%3maW^cmH-dC6k*#qMh*S8x3@>#dDop3KX&ZsadksaikB>1qLwXR z>bDCpU!U*Yr$$FcebzlJd!Mdn%AF_{1^Ov2c+AU|MVumwD&zgKWy|RN5r22(>J`1+ zD6wzwH1zimsJ^~lvfCu{0#_;tm$#TWH&bIaS5688=)L0JfWgabON@A+ z2(F(YDz$U|!UY0uB<`sHVOEJ49#)NKnUTDu0v={)UFFgw0JK4y&1_Jnsc~&olQ}Bm z+2#_Y1%Z^H4b+1&Fy*x~IFokE#UR9WpN26zVW6(o z&*K8JxSz$*bMqN{3SlT!ZO>s7wr<~|)~;Pkf6G`=fHAm90etSb^LDj-{B8V5?#Dl<7=(O+D$AFfF%z!bJe zyM=w@c8#`rux4ieyv$KCgUE#y8(L_)UIo_KWr21_9DG0{F$u{r~lrhUwZX=&VaUtcahGA zGFHB*uI>bzbwg_7hhd{@83*8tv6|2AfW&&aZju&)mjiba7b(-u>>@?yl-Gwr4@Lvq zp(D!si9miB39MA!W^9yz632*v-BX!f)jrF>%?r?-q}k-_P<;&^2x8Hu728cWFyp!(Z`|rQ6 zR;*Z|cJAD%e)hAUQKs?jx8JTFc;EqQr*FLR1_ebkD8exxee_X!{vUt*G1ao&uwjE* zxpE~P_r3StqkZ9Ae*4?sQk&VeYZnbXI2N|szJ0rT@x>Qu@N@NS&jR& znpn3qKd+W`t41Qk_7Egpz^HH9w8@Kq)pm>Pw)*`||J!lfc5Op8(FEe{-@dJeZwwRr zhW-nC=vR@@?++h3toHn2FR@lUdE+ur851^A8TVTvZ8La>5Hho2);Zwl{$Lwe+^xW1CFsaJWrZn&R>27eHIOpbh7lMd_-rL6aZElyHH$ zVg%y4Thah?K?53}6Eg}yz%A^0Vc@yOA3B|6U>MZ#?8kP=xJroxv2HeSli&?T04@U1 zs;k+HO%+ zJ7-3xR@Z2%HB_rKqzp7%0M!NDf`9~`1;7!W(Wjq&s?MH0ODh1^sL6x_h&q1!xLUVv zo!`&ToH;|=aaJSP4t#&;&>^*D%NDhD>sHzaFK81y$-(D{$w6Cr;e{8}jvYJHzJ2@D z>C>kPG@d+pvLdMmpW|38A}bkdS8QRu>cZ9v(B>QK7W7%!j=QUn#JYdQlG?v-KiQ4* z=gzCgpZr>d=qmsgn5e@C537$p{MawH&KaBlM-M&xAnohJ_dgiw=o@9% zf6fP+3_fw~=$~vqndOtD2n#J^EU-g&QUwJj`{H;zBS?6aDzQMtOLLh23BLK%yIstm4) zh|7J7X9d7?*IjoJy9U5@^ypDq(cU1Z;5rtbBiiAfJ$vZ=Bab{nzz$MDVBprQSwn*^ z+Nb<2&IJGuKo1}f@9`Tv8y>tQ6VAuNV7Pbh-im!iwe7gIgp;soX}Lw`8|&u04ppkz z^#)c7F3@8~kEtzNw^YQOqW!@|3V_D87RTAVd6U|H+YT!J{^`e`Q4LVom~!3DoM767 z)>&re{g^6F&9KKVq9LrE`1{h684mi#D}WovyYVpqED?Ws*u>jiA8V?ld@@0GB9(6GrE(0qy+FSw7NmGu~ zN=yiHsksCJ!)6Y$b{p#^0gTvg8oAnF%{CIQ!0I5z&vuoN_i?4FI2 z=$D4wk>iye=NlHcE;2{?`Rvr^jSHWi4=&RA!nC20trw*xqx4gOEhY=0i2z|g%x0p1NxcO!@x_OS$AZ1N#>iH z>gpzfya|G0$MkW>;Vy3PP^(t0qS-N4%zzoNxkQa!Y4$-`ye?2>gKiho#&I4pH7NmC}s`p7{I(_qQS@^1`cg>^XAPoNWb;gTQk79t0a!=8HZ&ECS|Fr0cO=QYjerE z#bx4L=j=b1ivCfmnk+$^LE&}CeB5>St{D@AbsN^H#oddkI>Oo0XVu`~IRa;}E#y%U zv9UQ8C+FIo((ZaEZ&)!s@h#*kJV&m{WVQWWGH9^LXz#Gc9LL6<2Nu3{F;_A)h9~Lh zORRM%Mc!xI=Gc8tvVgWOn`;7Oe!Ee5$q1v2Hx`(-+CUpuLf8oZv&%gtt0CCH%^sBH z!)gSkjRz2WhT;McF1C&1iNJ1Q76stsk`QredvLK;5Ywzwx;jNTRG9aW4ys>E278&kTJVKgx# zwS(7#ejBv^AW`PoG?rnm(Ikuj?-K*ZJ@?#0&kV6-7*z1g4j(>D%$amWa&{>O8~}0v zIwT*!75>R5pHPMz7J#uYed$ZI4dUg%wxK;>l7ZiYjYGyM7&hz^j2oU27(X7c;abIj zhV#L1;bsj3XF(t%Dh4s_x}@_Z)~y;rUVBnrDizIIqWHF2=7xCIM-ClP>(;F$!CU|p z*bTH#uae4yIx8m6qh zY1?t>A>f2Ju3lqjX3iNvurya)o-CP&U?&L~7wS^|evRJdjG6}`yNwRo*w38P9b}6Q zK*Anan1M)}VT-e`FzFA@LXv}gSg^ZI^y-ZQgh+@DNC$Kk+Ws3r8VN4s&4+B1cEXP>J z*{AEfT127iJxQt7Ed?<9(n~K9z-DQlU;XM=6|rcXuz=)&mtTI_AKdU6CJC><{<=TN zGK-Dm1qQmDl!I(k9wc!r3}COl_L?vA!LBj90szWm)w8~6E$-8(n5U$Gs~UDK>^j1j zvT8w|wEM-P>3`YUBqdlzY44uT)k9x-kn9Kcd*b*>$_yPF^*kD_9jz4twEoJW7d~(+ zGi_PRJ<2XjV#JEmR1IN9dB7U<7=!ME^f>w2n-SUawY2Ga*e2U6WV>*K?n6L^XDC5-~$;smUx$-3F?*Ulbx_3ezKR9+lcV3>QGCt9pP}vor6zN6VR*49L9U zZHh`(dH0KjXI+U=X|$gt?#B6GH(a3!K{0sp-q~(kfe$t@L8Ka7WOqI=ZF+)|>1u9n z-e5N@4jPWbNW|4Zn=PhIe$JG%YrvNoFs^O54j#ToKq%ZiQsgkG5`)V~y6#^b7YKjn z)#Ga+umsiuvr+}eO(YqnIPq^b+gxJM20M_=RX+hV0=3(AY*X+4{yla2)M>J79qnyG+p$?xQOQ%h zZj)i!a(E7vA%qw_$shvK(a_pV5@&SXs> z@&;GX{fI50jMe7KLiTakPKc|1^wCG^si&UuuQv&+CXIxQWdjQeEilgTjk8ZXaJCwj zZ7xAtqZXu3s~%~sAgta1u9|GouJwm$+p=cQ!e=w8JBH-yHT-=Po1#mXEATF)K^3_yQs2A&I zv&J^?LYrQ*dJV~{z&;?`7a6-;(g~ZA6wmMw#UdWp6Qe_i4yq$Zj?jBqKbq~c4c=n0 ze;()VWlI{DWHrwczws3RGgL*Tl2vOe7gUE1CB5h3>IT4(-7#!izN}HnY#Y^M)~7l` z^@qpEJ`4@rp!1HZ9+On|<5pqS!k(3#N$%3Pvpp%&?uu=f>%Rx|>m}9TvLB5M+BgXz zs|Vq=cE+b<*6bRAaxj+w%`VQ~C`=m`7&tEcYY+ae*fdubDfR)T4ME*pUs0C$aK*LR zY@6*;#y+7A4=IMh!9mi_f(zCK++2*?bYYsBeo&$AH19EkU%UQi5Ln|j0YLTYtFO}U zQDhkzi;xR})C3H85KxO7%Z9-TfSTj};;#0<9HrZ|0L~g0#MKkP#T8&p3f^3k5neW| z-=H3T)7Mi>%+4A!0sV%x&XdASPl zLhlOl2-r7jIRdz$G?X0xDrsjNn}BF`%$oyKu&~L;PZPAEO&Dz^I<}Rz9(H|vk0A(r z@4ffZ^&y}gzlC(flTSWb$>zlXZg$lOJPWu+ZH+q@d*=dxqOkFtO)|d7OIjeVk&_YZ z?}FkrvD|js?dsWQo>BMQbFb>_>!r`oZ!TWEL^X&WB+Xe-GBuSCr zc59nj)X||Hdgvi_-+lM1vuDpxmM=1R&*;Cox(grMuIl1qTFPUB=Z^KRa zAn1x~W2pmIe47lsv;%r=whc@hW^bH1$*zW=EK(PMYBC{qZ*W;&GUqV6O@qE-P7BvmMvRKCB~L5U#@z3y0tCo zR^NW^IrZQJ531MScwN2n$}3ckj{6<2H~cVKYZj&v7>U z!CZ*vCcO^ZRq7JNcKycuPG*ocY%)S4Gi@?+bY+lMm{hHH(V~)}VCINFZcDPmQ?Mg`TDlj;1PUQyQLnM?I9 zplyzu%f@EeWV?ZYD-13QD*w?R{Sk?>{{HvBr+6HhrR0iXvw<6)5ts&OeBn8`N*{%} zRb8Ur^2R-C+?2W`k4?bnTe35yU)rRE?YCxMzBoY*K0H7Kr4mg0~gK*l@RH!## z#Tmr5MKO+$5P_lj*0;VzLB?R+5I}6Fq`O3SnQf!a6r&_dO$ep}ndd2`Nft#Et`tK1xR`RkyGaim3)+_R!W;|9f-<0!2 zaw(ahsT`*Uw`o3axk_!|miK{OlaT-cC>mkH40nG5(d_i zTn~IUWj~@CrvwgMOH{_U*$i7)_N$C-gJEE{O$u2C$$z-+%Xhx>9hw2dRR{oQ8_cl# z>mQSA8kCGHGsPHBGRh@)-PEX}>n_bczVC38%FZ9A$tD3CRsd#zKWMY@EIN*ai)*GB zhhh~8^11BRiGHA;g(6&mZ z>e@8Eogv~1PINIr3}C{5UA-Hvt5YKOL%K*MSPNpmX#Pvx|Rt_`Wv8i*b{dX#`D5;U${z2eIfaX1Wsos0)e z+CFZzz8gh!xMI3pnf5N*%9C*(`z?EmWu+vI7C>A4g)>KW`_8X&73L07@|TpU?3h6^ zAJzKO>&3RQdlMa*0ay^jW`h*FF|+XkTL#t)fy5HDu^dPoV;VK%aWQTcKvPw|rZt#y zYJVo{cGbjT*@9f2VQx`ZwkQ}$05$|0%X^zdlP&7L-zorYuFoOL280cou8iqgY(%}2 z5yF@oxdP05>F#?pQ0k@jeCp(B3X4E{mQo7!UM9vT$W;oKY}@v2YVqPOwQk*da-r_s zx7Y7)w*BKQZA?}HidZ^?ybi-RO?}U4S_yRJ%36*rfSWb)90cC7Ilq=@)`;fj{KN#0 zb!!qCre21)Qo}cg>0BWtbn?_GHLQUs+_diS*T zpmO$!JH#!bz>98!T zdNkT75QmP<)e-D#3S-;s6^$5)2#B$Qiv#=Z>FKF(EmRYe68C<24>RK@3nVf+aSc<& zHHRC$u!8SNz?+>Uf&m1^7g@D%@z^nSVXl2VyXPXHXzdQ$j)mQ=9aNsZc)ZkY6C*j25EoK^f_fe z1=QIKVzlJKl?h<5I5-~c8`AAvb?$2Eq>|;F0l~>9nym~zOC7Xj{SsL=BkKZe+?Jx; zqfv|-$FX6MJ9g|C4G5BDvn4!S=V3D~^DU*nQ3sK*dl|%@xik#z-^mhbc)l2TWfo-` zELt_cPnvZo-}iL)s3ikS$X;B(c3mAka?A&8bG1xq`v;vvuv%-^t#s7_&K;hb;yNF3EUjzQ`THHl?a&W&+<|ZkDfRh+b!A;a)ZDej4=!^1{MSw0~oW2ZV*Td18ivw>|aE17IwZf&N-v}>}8vI4{HCm z-Ql*5XH7srg9F$Q9*E;IB=wR6iNn0f_6$JM-#egMH7G)xKBH{}+~(cVW&7+*&kF+KkSjsh2*4HtD`>mHjR`fClusNXlN=MwDHnvRsqvVN0MO zD<!U+Pa=XzdMHkpW{48UW!i!mH5c2)fDMNK(4j+=fMBz`K>#fXu*C(l3+1eoD3#Wt zTJ)rZb+NMa3`%}G?ytN-rN-a0Qj63S!x092*AvOP4N@&?p9J zSJi{KU@h+8`$Yt3Erwwk=pRtMeSNC8r&o1#b^0|doAOP5l)XD=K@v~c9rcS#C%aw z3*RGIVQ6@W>ZbzGkB*NjrB@Q|76Q0%qXIy}b^@%UEwX?%pNj;J0BbmZ*90LAhOO4P z$+@s2NfYSO9GiEL#QBW~+6(Xig~yA_1NB zf6LFLR+~$&t(M@=T4`;$on+hW8AOmanv8*yj1w1d$Mv#)8@7S^cwDw84BXnO*fY+n zyk*+;;;m4*5~bTm0kdgb19+Zopb zEuxz^3tfwpvuG6U-~vZ5v~ zG`GA0;GWy9wXKy{6Aa*8or~3d_ui-O)hn3P-Mjyw_Uzq51F+pc?8FqDBS6814I9XH zUdyvf8}QjKC9suj9{?~K$t`Lx=z9QNc@vA=*3w38LH5@mX(wjgz<%QSAe;eq5YLGd zvzVnv*(tOK>4mVLwTmd=^8;9eeYFL1(_+}t@>Dd6gh$-(rH9Ox`*OKU`8`X4xFo_{ z$`g87qAFD^8Y-@>5t%k$qfOuJrLG`wzUi`qx=J+JWh!j(y|CGHPzQrEIUqb1`9^j z<*mQvXJT8pNHCzI0Q1=R7_}g~&Dh5XN-hZcjA!03!T`FGVGD|xiv!l8*vnfTr165m z@yic>S?%0;8$IWX7cP>k5(6t_8qiKzEY;=m;J?e_sCfa>IJ3AcJHzG1CQB1OGbar^ z+J9Sn8@XY7dU~lsz$1@*l|)|OdiyOkcy4fp&9DJJ25`t{pnu^yYSnnGN74eADwelE zKjLZ!X)$BoIb_T;s68wK_Z2n-cFNPCY^jK4w%In$URLj^c5Il-aEaL>B;qh^a^dG;12kzzWgC!_cByx2vvamB*022n zhb%B{34mKmb8~Z_1Sk@SNFZhxk#_+C*9;8XGy4RCXo`?|MvY1bC2E}xk-QuHTMSdgA@}Yjo=KR zQ}n=Epi2|zP5%t8EEz+BVw}F<_%|G-uC}*eGIaWEN~#99uQu{#FNR zazXpM@1Fbhq+zu>d*+Nff8he%U#Q0;aiHXwnArC)XNq~tvO}I+Lpj!wVkuRog4p%j z7%{Wc6!TV`F8UdPIFF|M8R`=QO9oWG25%4C|Dby3-QQE~NCs{$CWb-F(xpoYw59Q! z(*S<5y#Tb~et-eTq$(|z4f|QSY9+~Q4Bfb)CsiY~e;L!|k{oiyrP&Lf4OJ2_V%lIy z!M52Rl&E_r?IM5{*9n$(k!_`nqv8Yvt`>t;vb|CLJ81@RJs7sMZAe;424SH$Ja)O_ z*z+@!9Li*0jJzS21HEJf}UvrI4@7+4TI4d8~DB3XnvteQla-W9Kyg~Bir zTcQU?_JsP^D3~*hdo&hkqg8i!20~@%w9%#_4(~IqBr4E}EsHLf=6V`~CZL?LMZI;pX z>RK7N;kw}xzzSCZ*Ubb&z$I6B5DWrrE@4s$l1C|n>YA#j&EbqU)=6Wa8U)&+m^ZAr z-x!<$D6oQkk?cy+*tKc_+pSu(o!UyVM552Quc#3T_B2ZR#FJ9Un?#)^1KP@J^%dEq zT=|0-H>D)i#xYEiv6Y|=$qgu0jzZ;N{jOZO>Q6#&Z0LGL4YCX@gJPRzlXbK8u1Lb7 zq?{~KHT%Xswr<;^HgDVPGn{<=Ev+r8ySvMPqoSYDIW{_`@8_uM=x8Ul^bdd76V2om zrYa`1K@;t;NoAP#AZP+A`)&}Bt5u(GHW{|rWZIIrL0z9omd(x*4Z8O>ShHn66?QC}E2hm3+UBfM#B(4p7qLVvI*B+pFa?suaFz3MRhp2c!aBPaQAQp; zOTnnAimX!cKEsV6Gvn`yUu2X&AS0s;3?Zl9OA0(m(RLoNS%dB9(W8`bAQM_@d6sEa z(YRF>v_rU_<5uV(xrHX9on^Gyko`!mEu+&`!M0_~Y@5?PTC_o%VcGCHh5dTZy^!;O z+k|0tT+cE{ptl|(wY9iQc`_x#MA86lJ8s*dHgDPFJ&$b8S#iX(!6kpfh1xI;nPbQ!c!VpAsTvz{FC62^8VW#YmBT@;%cr*F@Pg7CAEXyE#sl@FL{ zWASR69Sf^$WPdK(yNq=c`|Qs$cyiHEkK#W08^lSYX;B0GljfwU}>~99(VY%|_f3LAxHDD~M47 z7rTIO+(OWC8Mj*4Hg=P8;Hw>!>2iq%0XN$%i0fxncg%hP)BxN7Uf?!`03`sO1a3BX za{)MXIDSrCmBDpy0s&iuOM+PgW=kd07L-A1Uq2fIFg^%AKY8*bv1_BVdl_OKX##RZT^|;VR|0Ho*#e#lN#&Y@FXl`M#@iOdG(O z1=JlJLf;K)rvPaX-5KaTQWir`7CZg(dF@5pl zk3OL?Q9ZpqB&pHY->cSdTu&Qt1sW{qub=PzTz&fSr&N?Zc6mELv)sJN@X@31jmxc; zgmsGp(CVEbI@^=Kdb4a{pYa&NvRq-~IFI{se6%IOxNN1(gj-`5Shh~ppaQ#%>>lm0 z2WwYZBKxx-1Rcb}@u1D14Q@eBNC;!x<|@7o$3Wr$yGiW^>;fiH<(YUz$ACK&#uT6p z>h2KVJ#yp-B`H9Z60vTGe+y(XEC!9i6kY*1UB)elWrBRO3ut)W3Ah+}Oodo7S`tW15clX|k zxU{6Uu|xxPZiXC8HLP12;4LnItA$}}6g|gk-Jo&78|*X3Rm&J3K8N}L+*D;HD8iNr|NKpCWs>K@H$Biti>({d2DABIdukOQ@>rD+2KTXc`VW429xRx9I{c6JUk z63i00fLj^iVE!xm(a;(#5y^94zK z*lZ&}Ii4|GR}jkuBZB+LgRcF|qu8`CL;FPmYWDpABZc+@mpZG^hb04-Fl_aXUu(4b!)YcPxW+dQ*+GA| zZLlr*WsBuXNhaxulT~tTZ=3{s1{W;WeRqLrO90$*aF5y#MIvH3NJ~|0S6QVXpeGv$ zX9qGv5LAP&TE;D{LELC%9`ZMO1x9D`Jg zd*F)z+#rns#+Krm`uga811twER03*xjAF$&7P9^Jl&lJjK z@4^5;9Pm{QcuR|6OPgIwdz`5U%NEA4vF*k<%vyhTvJ8*o(vxI|Jh`O5f{rCYo%w9B zsw`u*0NMfo8{ecHkgk0r?FSSm#>+6{ z9lU@s5Nt4z{73>R)(Rnz1Z+asQh`u_SrRa-St^(!kU&)ep#tQOEL91ZKou2|B5bkE zc=verMYE4)wC~&N-}zeoNk?bSZ9Pd((v#*M)vfpR)_&dhp7Y)BZ2I*K$mcG(h%G4s zYKsxauqz8+HabuF1;*)y&Y< zqctOweTwIiY)}N~DxH8ZR}i-xo4(~J>EY>e-Y0oNcc|CCQWG)St7aR-Ya5|Ue0ayH zT&wF1Xp8RABBotNgPgn;%NE&|6JC)$4bW^qP7UCAac39{#kSE+TD1FGDBBidk>)B4 z`Yr-)MZ;Er=Y0*{aNb~~kX;IwDZ$(2mtUSSZ%9^v>lAT3U>X4QV9db2;rPf>%*M3M z26jD{rLD6@cO08HgW+NSO5GA=F1y6g_lD*|lxbl%3|td{dSoI4fRTCAeRa=b-i9WF z|Lg1fGWRGN$v11D*4&h3l{PZ7G%L1C_ec&UayKB84aW2AP=dE?uE=q~6>1qW&HAv3 zc-Bx}z#7&=)-mDCnQTLVIc5uVFSLunVga{F-3eVU8@uTtYl|{*?^m6*$A*~87^I9SGzv- zbq`$&+c;j&7u~r8q>+5425gaOD`47i+bAcnB~)xrCluHVuiISNv|sB7_ZBt`2$>}) z;ZTW+N|gnv6m`uuS6r>nnh`E3dpVIdbGkmb8FO zOc*k-4^ZChx-e;Mo6Z7pM~#l7ai&YwxH5}KG!luFP|}z&6PA6 z9URD9pWQv(+4r+>Cz&*SCgv)Rmkq^XOz<3j2Z}F)HN>2=rGjRgGs^+oW`u=IvGA2+ z+9p4Dnq6Jjx^70#WEZT-^_r@&2Um98WCF^}1d^OBtx>;eB$+A!5vs?vSPa{I?wi?mv^N6{3o zr6^WOXO3uvq*1tL-}LKsu%h;T>-Q44L79gbwAsMge0NjxS-AL295Z|Q@%MT?NcC-z zi`0TQov1*AhQ$R&4Gap5FhCpz5*Gm2keLba237?OoPO_gq11+{T0CCI1Z zR^0@Bu10UE0GtIRnQlO)EDReAVC}-^Fnhh?x?Ozf#YtmRW2QfMJigoP zAA2M~sQi~Mtnc8A4as!V_^Og>%pOI?EuvIgiVS9vKip=MXbRQFwQ1m{clE4UrsJPN zi@XwyTcs3p))O%YE;=AngSR5Urrn}8zRkL;serM_$WFk((R~Eti2*|i;09w&28?ld zp~dFKE_wjczMmj11k`4u=Vt@P<$%5@v(HrmF3bnaAIurt0?1*05WLYSdv58_^P{lqjIbl%vG)vZX`52hvNXFhWU$49OiDN8mups(f0HwvSrclGwb)7 zF1*YQ*WHy@UXgw71NZ&vtoWiC%xFX2$s)gzbRUEM_s^ka^ulyz^qQg6+@kZNhDBQxy(@wAF^B3Tw4h zqk9w!w^>G`cJzp zikP-?GEDXEnPox%)EI}LOEq9I;#>j<(;({@I`2EY#YPZXjsh(LSfQkbB1UzgEq|i; z%p1TPU6B|7TWl(h`GQ*=!&>B`#$1H^6>fVZ%-}hE2R@hXdCo^XhVLebBT&O}0XPz< z={dWYfNCk(mTb*6Ovd@8wRH>n{xt7)fOEQY=dR?z2OgYStNd+QoxQ2PvC{>Sr`wRR zOiS(}t*dog=7D9yuP9|PkAt%UsD&gYlmnRN1Ij9``z!q!l`QE|?&D7N*5&{*dzpxbqie!%Z@Q46N;HddT|bQrD`Z?7)dTn z?S{2rakd8U7HE|N)XHVbW|SCkdf-N>oZ$Rooq@#yP~zO7`<*Nq0USY=cO5bR@E*A6 zQKkm3#l`{i3_uL`xz4#+;LaZ3&zMMlS12xPC4;vtD^I`=U8Iw`aDMg)=WgT1wb?mG ziH}Dfeso6s=p=)gcd-bxAgjxOTitXUvf9b`wy6x#n#l}Ou!F44hOFO=b*rmg5{7N5 zV%O$XYGQj1Uhq+hU&&KwGGSE?P{Sgp3QA zt<;B(7vud9a0|Icyyn?V0-$RI=ak3CGAsI+Z1O`u;_5FMa& z3et@NlA}hKbP58aOFDmqfP(0|_g}a_&vVXwU*|g47eRX@V)>IlV3dd1UXJ@PMW3ac zRkhfERAf%BEcFfl2b~o$v*)c0CmUmIa@YU$-gI2GA|$)7nqBlX(L+~fv019&V8mXATW*=esQ#Dd-bKN4?j03cZ*-V%q3TAG7j%+y)&^ccLB%tf!% zE?{D;afzW%2dlFcx_&q7qWq%bmcSxMLIU99KIw~VrW5GhK4T2#So`SrRIKcC zj#^fic8Gd=tQeJCjetRJO!{c*rbGOm-WK)^cqQ8oBU;a+eW zG8utc{|?5FAr+ZN8lbr?d?wFF5>e0j4o#!GG%~&WzC=s1|-jy`sy$E-D5k$Ft`?#r{ixwZ?K1>G>AK`sbLGp&E! zJ;GhD#FVx{d}l1Isi;}{8|+|Z;+3|!yO8qFBDs*KeFt-bSevHY+^63`I7Qot=0om*4YoptE$%e z!xyr+rj2<$Q&Q%c1hRGsx`V4*iwKIz{yQKmjh7YW=_U<@zCQ8%vPXKtc`7|e(3y(o ziANAcfM=fN&e@u;hB5$WCk4+)$BG`@Yo^+k%HKI=$+3tKZ&fwvJk1i(ir96vU72vb zPJe9g{ypoHxQ~*fkfYUI`@?&OanXd+1(jX`)Eh@kGAlCvjw{lA^HWL*&9?Tj>`MN= z0&vBj*#g7ps)n8aVnLldH;dl#_tshJdWqEoe8W(TB6gC|u;vSuLINiY*L&XA`e_eT zEQ&c(xtt~8$b+q|g<|&*Sci(AP*x{X7E5F7Y;1UaG{sdILgs-#0sk0tk*j*_tInC< zwnufpRshG!J&MzTq7q*u`c0aN{hUH#mQg(%DYB02+%3tus0lUK7mM;1sdKkX^cCze zh13f@6m5#5o7w|+>%)|$tClJj9Kj)q5XS%PZpmvIRzd3 zir##77XRl?9*Sl~DVQV~kK^i1Xti#wx8^WWbLU=E*x}%by3opYsHX+!YcJty3s&|k zo?^$882y6-pGk&H%z`QAwbb6a+IY3;$c$s$Vz zzTH=~ZSi;f^M---y@ZBaj5|mZJ_dLKa>scX9fCzo2k67}(T4Hs>j-}f`)~Wl3N%fdVMXI*6!KD2#GXHhKtA|^W#F!K6t^tZtjVmBQ{3T*dFey(UASD&)68N- zfEb$=e6@I};BF@WEw{lR|C8Y*QN@WR(|aXF++VUlb~Nj`5n}aE^Ci4l z3uKPlffHz@=*ves$t-cb6u|O*3dk@ZlEIfa67$SgnIQDpWUcK_{JSDeZKE9jW4Fy` z#;w5rKdl0W`(aHkHdGI>HP+t%cgVp7P~!G&l?&~~;9ODjtzP}`V|HpSGG5YDW@Un6 zaC&7nw}DMLCr8mwVp<&;Njc8{I$^GEX4KLYt=po_7;9$X>S?SK;Vz9HXdzOF*;3{u z-A6btNY*r@6Cf}^>($Aaf4pLnHl9fI{fDHWB|1;R@-LrV?hMDJm#)-aI{$ZZ^ofJG zA+rn+3do^fsy&=O^QaG%@t_eUL@KYnA6=sqi}_B>{}J__BJB$@7{=yYmXh=(1*|xf?3PX!+1oEnkTJ+DDm@r0DY^C3F9#b?n-YG%`%!Q>2m`$*}kN!p6B{vkFYM6#vom= zl6ps=<8kL4TMvzdwP~uu%Dl!Z(1g2<`z2x{NK1qK7Nzj6&CQ${Ca?tcj8oRW3Pz@F z5ETMiO1D(TIpgZ=29|E~DC)kg`pe%;t^m^)-zqljLSW%wEjr6C393|b8_NbxVeAolDb3;cjMf>sWc)Au72pe0c(UY6EJOZg98A;X=4GO;N6)G5k9_TcE_k)- znysfI5We=l0;*qX@gpiY1qVYNk72|5cZw^g9qt0RK7^j5BSASc zp(&lb@(YuKYa90k0)}G?xjI_YD&FME=fU>3A~mj^nV%w7axlbskmuyE(Ce5BbGXUL z*I9BpURD)SfXwB_u<)hF*+hD%g-DQPzR4eJ`$^lQ#YTiy1*dU&Y4$s^UWSwVvh|m~ zLP_;CvEC=dWP7%N{^TUBWzuh(*R=1d8CIUqU3|=?cYBaMYt8;CC!TWxgrdTihwP>u zQXzr@oMo4M;}f3@1+wS6DfHtt3;2Ij5w=@8nAdA?UsTGv|4<)K#{Fx(bq)BAIXjWu zsIS&x4(TSPz{^Ue%2CZWAKYG=Ga1>^thY-#lBVVqyqfhHdq*aGSQtuEeP2;>ncuYA zel*aG(b?$mk@8N9qA-6wlZqWlIA@7D!gxBU+W6WzUbB;2<}3^mL0086xhzTd%V!~Ki1XEgf^pv9wu*D*^GNo>J*YhYfA^An5}&gF z63V$f$;AuOaF)F!-0tjEWim1hSTfh5?Lzk3%Oz*gQZJC0pfRh`mxr!X;7F zXq+x-?_|Q2$(F;%NG6T1 zzyi4*-woL%C}5sLL0o}(P#XF@`PJxW-x&4}fju5VmIA}@Dwug>&ol4V=T zaOGtCyTPr%e8pYvb8E2x=fd}Y3k}8%%zogF%EJ>TD?)t|Lw%}aYm8Z%$1fW>m0DQk z-Nfs)cAsH;l?fB+Hs|*k8j&S#{$;!*Ln8}m&(prtua5`qxbfW&?l8D=wQ2-m${iLt zS}8w`(=}A46TY-gG=jF9YA7Mn9z#h+MT`nn0&5>}q*AAbIG;^?p4`+mo2<8KFsoRQXtBH+$hH3a^{P@C`XQg!(024s!&t&$%2lnZeX;^43+;hc~?YI2$!+&8H zDTO_SpV~?l_K){-$tkH%VTuwtQsj`wb)GF2&%=*XTT=k*YJ~{}%O(7&xaj_n93Q|j z$D#%?{Myw@gzykU2e^OfwcUYs?I258xaesz-g^R&I&i}{Na`Rt zcV+z1ekm=>II-Yhnb3J&6`Uh3N6<~KfC!UBN%JF_UWZAJ`h%#%cj83!?;fU@5c4vz zKJnBnK0#JbxF)7HA-Gc_Emn6Y|835LtbKJjSix91OMk#U?yL_e=(Pi&vlBys@}zU` z`x`-~B(0!o61Qv_nA5s&u(0iF5I6L>^k77;oHxRh=_R(hJnDSsCf$Tub zwqG?}w>c>mDZSGCTyr+Pu2P#AtttvXIc=@eh~+hMQ?;@{7%aIiIa{ai5-+$x#)S4a z`@8o2@^D2na7*8)wI(tRN%-KS7JtT+Aek*K)6Z87ekLdtBO9VnHiZ#5Zk>%MxRO-` zGg(>O59kYSOBM@ts2J6W@uHXscNBlfoBbu=h#cm()YjK1j_!;d5 z5G_3aWhY%f8IsRQ`j(yaZ8W0tlIBt1o=Kp{cw!AtV(IzY`1Yo>L7|$OQRYALr1u-5n4>;dH9by{#2ZNbkN!RjGj{Oq}KHoO4!`)}>l>vlNb zC@fu8;-6~0dQeT0z_tzn2PKfRREt+%{$_L=IcPRu*0D^;+7e1ruVrf$G>oD{Rqe`g|NSi~5xD?MMQ*W7?)O7mRhb!|YMtsk!}LY6A2GWt3Jx;r0WHmkTJ!=Yvff};$SKZI52Pyy~zM85ggnWu?zBTUd@>J_KUui zL7XW+_0f~yow{$~ePiZ{4MpMqB?4NiY^Q}pDcs)M&VAer~ z-_n{^Z16V&k+x$STKhT5%;M4O1>3iOE$bik7^<77yoIk!D@UMS?6EA8Qmyh9lxb!< zZXe0lcRWIBlD;@8F8^eni7~Penr#bcC-!af)b?1(ER2TRnMzeS);~k8Nsy6p8mdTn zi@5>rKF5JgmZ86>Sv3oR03Z&f3YP7)jKCYHW=^zwQ}4$N{tfCA``0tJ$MJ^A&S;Sq z@3EFWl4A4^`-estuFEjkJ><%V30&KJ=HpHG{lZftIK22|yI${H8EE@5c)oP>#^vdE z%QE|C^>#HzO|bJ7*m($i&eb~&GG;E!-kWp_!_%m{4M47jqaSzHSZfutJ}LfF;%K%c ze6EDpf+4nE!)mPBFzf(>h)oXPGTuDuBNsIA?sCp)fNYnv0J08wZ|4>JOE$09mCys< z9PrQ==<%?36s6T}|E1E3F}q0CukiO1#g7_ij1&GO<_;U+`gVEe;}<(YI1dMluYqS6O=I$# z=D28`A4pr)bs=pB=H3#mtTo<)w*m_@Hm8?W$9$&QDV~5!W(6Id$pgz0IM#l~j#zs1 zuLlxIPzKJZNVls<-$Z`6m=fBBAFp-<&kYY&ew@P$dARdLfjz?o=~XBadveLCh{7X$ zh^ZnCtSu}o2!C{s^{nqp8_WW$&1Q~coC!wPtlL$>z6bOJA~Lx6VUdx~-k~DK`-%NO z1IFfXqA`NF4&Bo`kf=FZH8_mbxlj(jFQ^v%)nF=bbc{U@>WOM)k25jvlW@1c@x?;t35ZQeUf;t@*`=Msu=smX zzkX2)pTJWA6x|l;?)&U-FhC(=7HD3oFexkytO70oB;PIN<>gTa8gx~*DTCxf&O1TB zmuBuBum+Y@hZU|Vtkzsx~h1HB|W$!sRAq&rb1^HehCPn?M9sE4G?1Kag;+2+dpbxQ_*BahOw0Tt2poc%RB)E-P!WQ!SmXH@n~jEj+x*FfGVkB>hizAuv6*2G z@Lcgp02dr5#-R?mgoz{g{+Y46XtI$cX@G`N?h%w>lROD4-2K^v&bMow4*w`2^Z{6m z)*)V8ASU=Zuvi8#y;F$WjzK%?mn~66T>#mRD={kB1RjkOWzPz#tnXeXX2|@G_meo- zzMkFvCNtFHj2imwOh+}xzxzpt&>gxit(h18=P$1uqcC0{_P?bjk77lP>O^2{&aL_J zn*qP(gYt?x1h_Zyf)c|nv|OkjDIqo}?=nhFw!4phpE{6HBT}J-MOFt@vMQi6ZtB0E zJlmsp!yo=yA5eAQSj}sjeb|RY*c$6Inn? z>JPXh48|EoyInM@ro83gL!+i=q?hJ0ZdBtkUD^`Iun_3-N>TtLlxW&1fsvvfd zWxHJlZ;&Jkj6yJoVc&7?R)g$w+e^(#jnD7uMF+Ru2peQ2fqe&ujzh7=Q4z*H>97N3g34 zGaQLw^NRO51j$nNnZ0u90C_966&?L9?WawZ!1@azPxYT;_NhW=3R`O4IdAaCWqKQbL2BLp1-UYRg07qzDbp->n<0W%2yo(zd9@GLY+bolAXa!^{+$ z4*{|s)VyHYjPtP!E@{ECw6wLM4pPIofK;DVAr5x~Jw3ASr9}u-dL3it4t0P?jry6t z#fdH#xGckvw0y!KG9YaEMJ%;B4e;N~dWQ)&JzRL-y>e)3J9 zOqJug&*CjQ^4l>H$OVGE&!ra|tb)Jprc_(L8|on2e?Vv9wJUxHC31HY#bVToTywXM4tztOGNkAL}=SOr>P>;JHk3&sfNM z+O$I}uJ*g#ls^@|P1E%dc~@&UPigsUMpT~q{*19MSWel47UJ@At3nkm z^wG_`cI5fnm(_zY(y_aW(`o$MN?d%jT3Zr_;N9Wb%Xuko$8dJx0)Y{E5?F3iPZG=I zzOhiAXA#i6k@j&*+yz*6(|s%FKK*(ASQv(iK_T74euLE^SKFwXVwFWt;iX2n@+3OyB7Xxpn8=3 z6cryI|AqgKZses_3@&aXUdEAjgZW#47AAb~ztq7JSMeV(`We_Wzg`Y8#o()>;gijF zrK@?ng`3x>Jw((_*{q5IXkL%n=U`O&uj&_~>SIm|QvfK*cewd9A2BTa4()kp9N_ zEj0w|`n4|%8ZK<17VFTdLvj(l62(#3N>E7~HVyg?VBtA7WwpkD(7J%#GanWNAQ4z? zwHo}CJh*hMO%){2dwhJXj`#RxTj}onZI@-eD!~jfHsV93h#`e&w2D(;kjMlxIrB&i zO89FK+~W!UygELI!8qsD_2(ktk-q{o9!lt0hIncT=ilZmc%ZNN0T0>~GtL<}joqbr z6V|J67kNv^-{a#F*r+$o$#&-1;O0xbRV;P)aIKqc;Q5xOceTjJ(r;^Z4th%S(y8va zWihHmS*)oA>u;sT24b z=X~V@cuYTlBjiLO;vzoBaX|5=#J9(>8~O0vP8X9_W_21gt;ggj%(T}^*63>_lASXV55`~`gh&%7lA1&Dqb_w$Z zSm@{wU3kSCn-fK@5-CK+bDCPx{m}YKc1s+*67CiPo7Y{5H4W|&W11^5lg~@2L3;Gc-y&}bBT!{B#!!_3@9f?tkcOP7FV=qLx^qsz=GZs(l zjeJGI)Ok9keXj!tDr9fdh&y6&q_h*^{0o5sVmXx{q8&(_&<=I=ifzjiLC|VidG*q3 znUT@P1uLPZ2S{1~$`pd$bbB$#UMp=s93kK`S1cS?fvm3j$Rd#HffRZ;+NsTWHES*F zuZzALnr%^uRSc8vco1Jug>uo`{)E{*M!2fix)z4vh*p!1nP}7Mbon@vgV`7o;XNvN z{h;HkXYg6{v!0L!5?ebfAyVj!+0Q^h7`>9N>3UH(*$Ajn< zb1y52K0Q-(r}q-2!4ty&nk%2Is{aHW=WEvk4JSNb*{H&`EWSD>=|!-Gzq zkj=PB@6HemyaEgTU0J(Z`M~I>6seNq_t_kf>F+G`>OfsFFe8?hmZn4y;bXYtfY`b% zIs83hwDRt$wrb16d4(oM@yi-fA>pHRwT)44!H0=0k%P%0q^ARm9>AvcGV;Ffz|&TJ zL947!AK>ns21|l>A-~w*SUC4S&3lTkiYPwfVL536}wiu!4kz>XUziMxQRsW0LeFQhAU3j3ztOVV)P= zX)CaRS})98)^WSa4wV9f5aDZ4|1Q8CKzQ18`^B);nB11+yl;NaA%cf_Pm*CQW(>H( zvFjsY)cvfXANp8Tj;Omo02mU`)iK^_LX?q~O3*{nm66Sk^H%pjCCj1^In!dOFNfUH z-F}PmXv_w@#57ft!0U5gVr0DzIZJorP>xIT?^@#l`AkXpn)Ha`0{?oY-p;%@=;bbM zllt{jV!|ZZVc3)2{m@2OX~- zNCd~u!>-u0mROHEdEDPX@ zS0^iEbM&p1_tJMMJyzRWp6!fqNIcwztx$oUD#<-MlpM=)oK-9^)*aDjjjD4CnzGwC zVEzB61?tAJI;)k0xo(PINE14zG@gAW6YZ1bzJYh?XO}9T_0cZju*QC0P>qBr8OOY^ zV<$oZrk7zdm9txZ3s6)yK`c)2|$%WRUpT-N) ziHm;&7m5oLG}2B_X-I_uvi`n>HTMPS?1F;)^xcF9O3XMzjRZ0?TWbrQGqn57_`3gT z35L&XD4w~z%~(oK$hK+yn*d%-QbDPd*x7kF*053%#;saooPO0rG<7ixV+SLe5o@?%38UKrBMWTMxRm{fG zhu>4eh)*&^u32VKeMf(xC>5k%ttWX>wfK=mhs_$WijA5uTUuLNamuiJSy;(nZ3Xe94tnBLTBxSBO*sd`7Nf=y6At?11Gp6BO3@^d?Lc|gJn zBX-NK%8gv!p_*jpp^xqkclQ!6vyueklVP7=`!`ls7y1AG#0;_ryU{Y?GzQJ5SCDKnKqgFPk7q#P1vtPWirrGj%8B9J46Obi{IPa;8 zOi5#=P>)S$7N*I1h~~ePh58diM83MxW8r}5e}ak2Dktp1_a*IKb311fG{hTxctU&Z zEn$-*J!VYUD`Cf=8VrnCRALh1#>37sl&nt;A4(}jAK70fu&M5vYA{u96TV_TQID>B z0RgNVr~gumJ#JOC0X#Z?@5EWDFH>2r&G&rWkRP>FDVjw68&8fH@;?6^(x3) zR0$4lvplb!NKQ_64;-_@5@UTU&0jD?#MSW!J!>=k@(_lCj)7Qr9+O4vVf`eUpsGR?b+b&k|aU>ZKwk2GUqda2sVBB#8Kim>i0aiC`|0=YF07(cGQKldlo$AX-+- zcTzH8pRdp8{=mzke=sNEXk~*(H+w8Fe~KGlqA*_wr@d)5{>t$dIH;GE*Tx%>5wHe@ zMoW+O3$~B4;fq$Dh2(2qpUb*2#J=hH{JLW}oK3KsIrW|`8MrAR^y*( z+&dvY@=4_Zo;_`44@4I|@QVZOiihRxo6M#1eDx@9$HhnlTvovguX`dhj&c; zu15hG1pp&HsZH3xzzJiUwe))m2fr8Gx0oz5X?~5qN>F7FJt>+nJsgZ3&{So|2{kL6 zpdV}!b3|0VyejR_Qu#hWk}M}va*hcb%?dEu*7yh`sorv#UT)HMaW8`PDkA;rv91Y8 zr-_ExYPow}9z`#2w*HdP9U&2jwja2(91D?_tq+8I?7YI=$CMnX`1_V+^N;YFlLPI# zrr$f}iZqTk9Ej}U`SNaJJ#$qwx)ZvUaeIFAdjGJ@!cP00S%!3nf`+9@fIOu1SLw5vJ8`W3dbXe-Se#|LnzEn{Kb z%=IeXOy*McDnSA@R;8eyUu;ja zfmQSa&_!ZP0t|HvfC0D3XZRkctf&Pu=jElRmpoSPRL0Tq!rd&@B;73C%typ3Qq}ZK zPl?GMY_=PG0z`#--A+U5cRGOvvg<330%Jxtn#4h1~wIwS+qVpp=^niecvgJ03b*b1t*yWZNTb~hW9A#f2sA(c z#zifLnn7uSba4cum9J5$;zQ4^A!??f$0*o4Fh6>rxv>y1)vIaQ$!e&o624sCG%A|k zj^qjZKbUYI7Y+LM6A>kQj@Z#slFeF>#@m7{{d6~c1^fW&_Dixyhe?6$|96LEwWC2z$&h={{yC)HF9f)cNgDrn)OU+@VF$SY*q)(ORrUDeszf zmSB*t9I0-3T!5sD&}X<-6_0}o2hDYi39-2{mKvy`FYSd~MVnAnyG6okci6;}{{Fxd z3-Z1uo6>jh2W9aUc_S1oUK?gP2fnis>49$2j=Z3Y{XAsM4G%9;5u!A0+*Q7A$!ufM z{SHEGhKGl1<0d{NSVA8(HZ*_>j15Z3gyBkf$%tS3o~Eeo>Sgt-&;REZ)Ek>LV~vVZ z>sx;PVC>3xePnlU6ty(hdDZySr0eFRf3E9Q{CXARFq~gBJpMgO6eaWMA1>xrES+*h zarI{OYoW)7}dfxgR6iSKi;b zDH$DWTnHH0MZQ}DISj4{F>K=iJrdaXFTV{x0F+~U0@|w2TPwc0;v;1FWSjV7V*h3X zBl9h4a$yE2C>`8j;2o-typmH;6u`DZOsB;Wk{`&dahL)=oI>anw1jMO80=|I}e(V zRSNWW-axyTLatYukhliS91t#oXKLpV($}dfYV#}_a-XmVI;tmRuWV8zZ0y`&V!rm` zspyYidxQ?lEPo^8;kmHpO7U*@uj~MtzW?1}$zg*+*52uI>FfM-)b4xGd(y>V6}>j8 zj$d+10^idhXA><0)Sq=0(!afhES3xSG%RLHf4{PeLZr}A5Y%Z+H!*n9e z$#z)NZ-M$*>jCiYMFiCSQQmd?u7i?VA+dPAX%ne90uQ@xqIMk?ZaX9IjmC1<$Z;!J zK(HD1llK4wE#9y1xDZh8kWJ`XMo8%kd848+lL8Cte>Zb>pC11E{cSqCBzb+3*90eblDAL#<7TLWY>nXj>e zL8UTIi83T&+yRFl<435)Jg(Q?{=q|oD6UVMOpLo|gjU_dYt0H6%pn<|YFE};v42?)l_4PB#PRwz zU_4?kH`=6U0UU7Cd^8b3OIrYGd)~)~_Q7Kiw)7h!$8!cB684icu}d1%CYe3ui4ir7 z+8ZWLBWuL22Msi$vPo*?_Q*(4Lf21XX_WZkCsMkv3TEj{i9rZ9xq9r!@J4n5Yy_8S z?9=%5W2c9=%-t5=xWM%UKBwS^mZ$zd_ompVWU{??kb8BbB8SO?iD7}Xrx;D?gv*}* zRXeDGx*cSX1OJJ-oDxoozcdcKPY)HP!IBf;gmb&EM0s}0EV$ml@RRNce}eNc*xbUH zE)hvfA=FZEoKN@+i*!YnW5U^eoJWHeeBd4(o9E^l2Xhv;Bwc0gjwYRQQ}QhbKNiZF z8x|^gn;%gY=wNbwGFY?5w!_e8=GLYKpF}@(uIJr;m+;6FluYG(_V`1XFoW}SR)-rq znQrYU9;z45VM-5>1IPdvzkV~1=?d!zbvMN~o1Vk8`@601UD7!U8uBE1(si=WB-~*a zjc@<`-HPVRQ~6__-hNfU#OnJW6V)k}2+dn_XgFq`4nj!J!@?T*U)&dcy+I}}6d-v2 z&A@)@p_J1quxhvoStwDrz__WhvSu`mI)qpXpo$m{NxJdikachq06k@JK8+YJb6hp% z3a}uq*D{8z#|f9t&*u)i%&XvKY&1?D$|g?U;(HyrsZ3=SVXp$rvRD6`6vtY3z{s)` z{cCdYLyHhQElxvfZtsi*P-iaD`g;|u{$QKZ@UdKFC& ztv?=ABZqRdl2a=l9xv`H%P9L+_SGqR-4fJSOJ|teH1vV4@CIR>D_tXOKlK~4tE^!= z|A}a6Yb8wgkl`|+qU=v(yOW8xe|l#57;BaPWJ!HSUGu%noF_xkol_ODC1x^YNsfn4 z9KoE&JQ&(+z@Vw}$EyWXSnSUZworm(>#K5PKFq>Ew$2^j!*pie%SP1>o=*0j@0@QP5l9O@9d4H0!W<%@@{b1s~E+Cq8{?S)Sz{;Tq>dQDUb0CsN`UO9rf`QY} zZkQrWs#WDoHi225t&43J=(@KXsiY2C-y@wBQAGqxZSKJQn^1#TjrwC&VX8WgiL{N= zC^FH3rB*AY^ZqcoM1$$Bp4w5nb7qB$-+D?0lg1y%G($cf5R)xRM5ysDaDu3hz3W8l z#H|aBnG3ByrA22~5m%3kT>?evMhBszhEpoJE9Li^NT{uaa+L@>3CUOiiv26{oBkp# zhR@8{B0>l5Q6d{xKTLzjn5j^5pT)tE-~d zgEq`%7BHS~#jdX-+l|9vt!idp!xdx(sy=@UUcLC3h*nluAF799J7&y<;u~10F?@jwl&3JMOv!d4kt&%AV`p+jZiLC<7i}YF|2qE&xs4 zZj<}tEzaIzFx)Q8vx962&GLWi4WQ4IWv(Z?9?9E@TjtR2A==a9pwfHtgz__@&uJ;s zlHfVb_r>=#e*|Yx?S+0#;v1H*ylQT^BWscBFjYvro0eYVrE*PP^D@kP_{B@>Y5$qq zEcs%)J$}w4drQHiN6n93(v5lsYW|kpchd%Y*zT=Fe)bdI`<_}^rAh1U6EPBP>Nt5~ z-yRKJ)O1OTCKwRdGJI`_^3;JR_pT6mMwk){=%YOY*u|~nPaq>Sq_%}f=W@-0U+W+6 zP1Qp!d0adPW7gtKRhredS#xMKgC3C^qyy_4xN(rXI7_arkUYfn!m{w04&j`2F!JUs z@vY+B-P*sqdVv7#=(?x)KGzVB0QK{%xpv;W^YpGbd5`^MZf&*CYJ1XbR4Z6fX#Sfi z^$@@#(C)@)O2^40udxR~}141A0q7%>J!lZa2D4F`9{0k}h%9%b76(aVR)EUE{jWMlN~~6EC2&p{ZYb(j2BLZ>I#s zobUFn&%^z^BC?KyXlDgk-4uaXJi}@+B zTJ_TR1cjnX@(3x41Xh#0a z*k0ZHW&=UU_xZ(Pu&dBg&su-8DYKghO+Y-&R(eoyC??RRd5AtOE|_} z*B8pMGjH?{yJTyfI<9PGUHJpK+IWRMAYMitkxe5v0G+ru;AD z(_-y>Azi_*0-%tt@HB}&I_9ruN}a5zf76iSdz$`WbgzS^kMUfD?H7MWf(hz3w5G7s zQ(!b=MZv%FmoUFRIU0j2o}4Ylk^LM6^FIpy3<$X!ToN6RX(tOx#aaa80I_**eiq*w z_a8JWs?_QkI1zmLTQ}@oR@blCF8=?w9;cM!GrXOump^|2Gy(W-m%{_Z?$bsfmpazS z$HPIv<(2)h1UWNmjGU{fjVq=a3<#5OnY5+?GOsV0G6y!TN}xVs+?@KSaQ<#dw)hZW zClp3z7`c|XHsVeQl(wo=yT5Y}&2%EG>^N(e$y-5_XT@vR&$9lk-4o-rkXo%T{+YLb z&MHqn@|XR%1-;#^XqfSK(5~e#`y4ct!{Iff11iv(lELVmBU7Er8=EhuwS6owYHM$*f zaeQt2H$55TqZcdZ+43(wsFvEyRSHTSQr~2E{mza?HU8`uedE<|U^GFZYPpBw@@D^C zCwlnk1m~&v-b?wARo6IULt4{H9pnVAo4C>JDrnlzaP#i&%T2v(dhv6F0KSagu&u)z zed&Bkct?N^zr}sOh$EycS-DA!Cif%mloDnb#W*_IsQ7PWDc%i@eEMev&vpPY&$HQ7 zOTj0prmjPXeP-829#F%#{$Oz{{h5aKlGIAVl-*4wvQB(yF)gCg2)S*vPO~T#W9N&NkM!Y46@iA#+pL=Mum52)*4erV1A=!@h?~|9wE$D$ro82Xh#MP z@^byddgwC;OklffyNqN`8Z|@Jdor))e+GgAiobJQXtA(YLRBCtiK}NnV1`9Z0c>=n zR%P;&)s3y-@~6i`L~t=LHAQD z#mc&uI2JpiJ3|kBi}#?urc7%XBoxajVbB!qZbroRZvL6^Em3o5H690Ech3$jIEY-H zL>?kfd;>udaeOI5A&B9IgDINaDUQrAlr&_9cuUBzw-%_}m1^kIkWPo*TsvHWeB9=xyYRT+X^elj;^b$yuBwsR7dSAq;b)i? zz^b6$p8sGU@L=gq#uAY=&2vz+d8SGPPWa@dEWf@O&kUv+p&g8{b44IVKaf+fD@Fw& zkWr<{?-jj&^hJXLJ1r8P@iF<9-RetaY>?6u%ltx z=EOObxjNq5Mr2;wH^E4omrlLDl`FNwM$%G)w{A83uc^uQKK7rtlI{=u$D+^BzkJpx zFNh6}S5)Sy30C9_ze!atMKnAgM^r4TX8R|*PNw0;sK}I9&ySkt{!Q?y7U3}HL7^jG zQSKki1mxj2$9~gOrh`^^IKLmZV&z2uS^v_P`G^bae3?6CR)roYiiM6V^&Y9!4>^A5 z&jm%_Jww7prt5*q=XkP;6DV^)n*4R%_%BrUdGlJ0uV77v5+?9oTcWJg@eb z0BhZ|@1vc`6=4N4cMWj|l*fr(k%U=Z+IMn>GVhe&=Csf6%naaGe(vf=HoX)+W zF!3f$Y{F*ZQVoAHYV^V$uB@(;@H2PXmnR(?e0VzN$&$nLs@$ayN`WDhC;G7BSbA~%- zGBtt(96mI53do69!EpsG&q)~$UEv6HMIBjt-2jzxx@Yx%`t>ehf}Vp_4u*O~|CUey z7U$>FP5(|&-}W;n)>))}%VW3HfGwe{-h~3L{t50ONfE{FZ}UCGeJ#B{(po5d1!C}K zpk@pa=4`%_DY@n)XA@@Qlr1x8TwPF zdT5?8BNztvmba(=&W3{PpVP%0!Mh9)Z?2N1X{KO4Ek6c#a$gw-y%+}k9Uec~YB`|| zrs2+YWEFjrY!Oh5x_CgsaZn1DialTUcKzsO3(!7eccI?dO&5CN8~N_dG9udD>Fc$p ziU&<aca0Pq`X*TO>->vbl`###t%=N6)EN6nMJjkM>HVoa; zRAw=e$PD6Wz^Z)!06}b0FaMORpTQzFMV_54R0K7e{Na^u1OJWHHQ=aA?L`(e z#RWPn+IjS=8IhXyTEHREQKS4YXQ*KU8NKqNSrT_*$DYmLo8vFH5t|XM zlG1RG$my|SIlaLym3@mRh2Me*xhrY?=gHeXAWu^2b6u&Y^5G8oKBpl`tN3j5-jyzw zDN>|ib<7j-CT-OmHnRY?F+XjGrqm~?bWfg^T@p|9Ap}QpaYONHG9xI+8;Gl@oazML z%*8RPy}oS1C&xnVhaF*E&iC%}DiZNf9P*$nA@us34HJB8xyLw<6j7u?MJ32Kd6G{T zZ*QdkX63>tQuu0g(VWqP)g-#V?tR7|@2VwZEIccO?akGhw^)_wd=0t9`D_(z30G;pzsd7f*(4VOT& zKU7XszWr(Q`22!>kg`69JdG3XM9+^(UF{b8o{CbK zww+Xe9SWt&pKd+!=mnCd_5$ClqYL9avC~pl0fuMn-;J{&6qOEHGQo7zV(Kvw?0LK^ z`x=(~C!-cflx1zlRgU$Q=jr>US2gKXS=&0xX?8W==W_Pu;I-&hV35i?OgT#M(Ryps zsK6T=l9bU&ysj8N$o!a+4PqBYw^*zlZaFvFZ_`@5@TWF+?q z(8jkvNrG7VW{JC5D?%;ytupKT#9VvXl2JW0?Z&7MtO=omBG6)PBACZ^-0_v5J>5c{ zAVaJfM%G6Nr!@A$$j=MatDYDUZ&=vHENO2v{CJr-K!RJII(Bg-q7jfpco&ZL)EH2xb?{cx? zOJ}FjY3D8gZg?)7GU(*fKlDMlJ%-vJ-|YZ5dHh&O(%U!cjFexnPLZ&pun$(imWSY| z__~nvx~6ukWeMK&)SUCYN0RV1z@K0t^X(XhB8iv0Icc^|rGP+izG9J=ySZABC;|Rl zN*?kT_~`u~BP#}i-gorG6;lEKu1`t+#G*AWC-dtKmW92Ye*R@RyT909K;`30R)coS zpdT70fBN*<+b@jTvDhVV0V<*IglF|(1&@iUiP@!g2Xyz?92vf!r{t*7ZtR-j2S{BG=;2jZzMt%$ zp7Cn3k6`KIZd4 z%khl2XuMSE;M|1Fi*|zgNBx$74FhtF<=2=Yjf5`!{L6Ebk}#|of)T8WpW9Uef)=Yg zbt9EH?iwpCqqo_Q=$DFx3~|>^q@80t-QDRZ!veLBu6n5wux7HxR~M}+9A13{TTbVG zkE?ovDRFxD_jq_pO9T%JcMAMFKT1ipQp&nNZL#R?9sixV$wCqSyJ#nx1Wn5Ut0AUu zhvLL@6W`<{1MeB8gn-!+OyA4ft7uzFx>+R3e3%n?5=smpIB9USE;$iPG}*;kn;fmww*&-uje zIqGZm+6$PB8M){pppMr^DlBZ&ET{S@!Qy}xmS?$)oT&7}am-;#uniJq0L1AKhzy0=#i6&0idr2oiO1RYi@@+l zl{h-_v3i@~uj&t6BW|b0%_J}@vAjN7O|bv70bWOvH)!F%s}LiG2YVsge^T@%B7L+% z$%%r{FnyT1qYN+Y!+kD0FwCGM%PjN?OvBYACTqdlVWZupaf$8o#jorSt&3iT=pS|j zg!3uz)E8PweI1T-#G12aWy;=UIv7#I@&9i61aI5_*w++&&Ny1|opEU79;PkE;TSX6 zx0&ji5^bmQIkTZ?KjtpJV-(Szk}YN25Z`2?qSjY!ifHUh1^-p+{v(3g;xmejs}z`* z6j2i59GR&v^E<@IzYU1qL(eU~WC0fU@1zyMkL1?S<>+QaT8L)9d+XWi*>3;^vC4+T z@gU`6d4E$uAH>$+9}9=+y@4lN4Lvd7B8S~e=!>}8;{%m zmbD(%&GWSY{A|I;L7D0X+8MmpI76U$G?6c{!rz~ zwDF{J*ms$BkJxfJ__kE3Rr}ECBvxAYPlsfABY?^OJ(DXt_D$SXtfsDn4+Ax%+5xr(Y)FZzL2 zOnWc%`D^yw%ftCqJNaFPI1gIFp!!k2XYG0@{bZ`mLjVL=ZgN^~E5>RO$*!djqID+< z5JEvvL`?-qGBftOs~Og19;EFsR1_0MH}dnSXl zkNeLG1WeraS%q>ns3yX4+C7*@O_*V5!3j<`Fpx!DYcHRu@6*}z*pT?C;v&Ui%Yo%& zlm=rumWHQ*PkS%=bB02zN3Az9B{q46&yA2H1@`l5YiZ`|{)?ANSNr2vF0G)3gh32s zI83079&aNT>k(+aH<2T*Ki2PE8;Il6ZdZHL(Ojx!)6p9(;JK!Vi$ArPzJx0HAqD+K z+j{n;{Kfsh;roTUpJN(3yw2k*kW2HJnQ)Buf1CW0zr&>tvG~0@u!jA%gbJ(FbDXrx zFkZC5ajN;aPNuvLF{Mw;pIO1D^7XxuQHi78X&u(k)5IcAQHXRHJ#{q&_7)^2R<>s?htSMhux$$buIZ&%a zfnxsC%}c*wQx$>}y!OI4&zsA?eOteW0n$eegU`_kKM5l%UniZ8X}I+xRvbAHMLK0) zS3J=SoNd-gTYj%S+Pc_rn#ZBeU2OKG%uB7)rsZpXMR0LgO+YJ0&DdQ#Vg`{kUU>W! zP?2$Iw-5?SQoY>|F}oj3#PO-J!Ii$0CqS&>7$Nq4Vk;31a(VdzsW!R-eTgBVwU z&B69SGpKHI1ZHS!rFFN4yx>|B0$$5LfUvIEvGrmKjNkBGA@p{h-|D#3a0Qa4Ne`6d1Am| zkIUhaz0u~QoGYr{77>GTV}9$XJ&l%FE866%rXPy)^h6TMaabk9=Tpxn=OJLd3CPS zx=ukDQg4AKpJ@cB+QIZBgwojSd{ETpqvm`aqJPa6ZS6!h#lPXjYH9Ew4jTTvp0=;N zyK_sab+*S3hVRf zChyO#ji#ln{d*ZJ%4=C6We$_SbJGRGfp_Qn3p55U*^op$NQBjN{(fD@Q_)Q%LW6KF zZA0NG`FEX3(A)*3sh^8-==keMdbYntuI65bDJBuY+D>EI{|r9dRs{`z%kM=w!wLrn)24_{_*KU&N-Mu`IzlALiXtU+yZ4C@sX`8MJYr{iKMb8M5$SAz z`An7T+sal)`RXtx@7le{gFNZ&n^q9qD`o~2p-Y!em|)IhG<7Ry66an0cst-$P0H5L zpx0>WED4pq{EwZIlRDWNYwjrxASTU<&zz7zb2O^FcE49Qw(V-FG%MCG)X*QiOc9n` zMWLhc#@?uj3+qIAQkZmui9mZ^4aA8(%hpkNzG}}fRMHKA#Lsd9XK07eikmv=6nVj9 z5DKseiJQU5@VwB=%S($Yx4A_AhL?i$%@lnElfebAq@S@gQ?T{zSuDx`A;(ef^_zR_ zUHjj~e4rxB zCO%(_7jNq-Mu75X3;Jq(E6 zcXzpUIg&3fE=q1R`uDoi8AX`S7O44w4!+T|bhP{|yb04KsKD_$Z39O) zQCFCd&xL(|$CZP89k*J|r_t(G6^XnoTlXF@%)zv6`ivc%rpY(rue6$LE#ysbV7*GC z)pC-p)eH zY2F^Sn!rvu1mp5RPC-j6 zi5huE6ztKg=iXyo;fPb7(~9h3ETR2G{uCh8uGSoq_2aJ8S0YR@D%nUBHdP{Lpnn@m zv#ez~$m_TL3$c}X&$oViMGUDHZ52uffa(^EJT2YbQ3?OjE;l?^-31T*Q`KbRhq?SW zxlL8|-kf#(03qV%o!X`U08n#Az_#S_s4DISUI}0TR{2I?>8k&2+iSiVWj^=E9+0J{ zfWg86da-E)`!}l^`0C>0I`wvy@paEiLmFw})5EojsHG6e+ppSs31!XJiTEWBpUbqW z;9})X(bz-$R3gCG5D`v!PRSsV3Wr?Q&6=ynquH>-PtOk01_{_Mq?x;QZvJh{X5H;( z-PySw)=n#r06{7KxIEyZxT7VX@n$4qEbuxaa6}c)q-5SK$m5x!ug?u?HzM%5lhNcc zblo0+xkuPWjAl?QM-=rSXHKG!U@uWG2jXJoR@?P1_-X8K^1F4Cc{ zE{BdN;a2uD#GxboP_-}QcTQf|<9FZLxy>AqL4ALeuLnYV-C3@M#LZax3+7*IWZ(Q! zdm&>Wk_|zh_y;JOgaJ1AxN@5eBV0Z&1q zh+&VY&H;)`GmgiLWYOJcrQ!-<&LZ;#@~?{oYmy}p7Q|Bd*_2fXKCR7fi4dN7fSh{o zu}ip*hh;WirtL>0_-qtk=kwZo<7A8~BYX9NC8EGh=l0Um+a zdrdHJU~_cx26j@TYCv-yz1oNxQ?HKUFc4d?X#rfeTSVN&_g=g#4t=7;G&Hq^N)e*( zTdM>HKeTB-DU45__QbE^({$LI>3~Rac*jNieaEtJ+!4XmZ3F zkvpQAkK25I4xFN_T$%eT>#b`4Y?PKbF(Smo!jQcwgVnTP-qxM zB^BD7@P}{n6WnCE9Fg(t0YvW3WQC4ISMy?nVou_-USQ+<3P zsMmV%Dm~k5R#F4gAYOk0e{w4MJf$%=porr6+O10F%ds7xK_|Ks_Q0a`P~kLILGhlkW% z$n#K%-$$pW1-JuWWzUbSdw;=`pHD$^q-lY$2YAN=W6Ey5_5j_)b3Ly;fZo9u2tbG9 zILS{?v~a8ga0@QTsc0XFloiPB3v)>Yf2Gz2!Sb}Ap##+0_-7tN3jP(+S~1pub?3~H zgo!t2y5e@&fo<;3DBkg2h!i_WB*SQies+hE9C>LV2ow2<4$b6E`(5dOQEaO5uz{9G zel%`>qql(#@VYi0k{};|Yw0RLxCM$aLx62NiP>sl6w5IzEG$N=%FmddAihEv*O#*%g(KLoiZ=JaTIzO*(0~Ck^q}l@&VQ4e2*9(;ie_=*|*Jg2NBkjI1BFW z+L`ofo{(ndL%iP(R|m6+jS8D}?-en`r?Jm7wV#+8F9=*<&eR6P!F-Q>m!nE5pLhaX zDNY@{Afq23x{mZgK6-)*N5hhIxXjgq%g7-DfzX%PHym!lgzV_Zp!*OpV0lr>VKNyK zA|W6`7>w8mpEmay6p-B*MXJt|_t%95rA`MkPc(L;w`t41^j~0jdj8X2SbjB(By6F& zroE!SqSlq~-PR$#3PGnQSNq}f7j(31wD&7ScV?3>)?Q=GIR^uh1t)fX-`RdOAk$~m zUl|C2qKh_RG5{eEv_K$pxE94Y$~MG1Yz0PS;cit z)jA(2oH(|Mi92yvKXEUgz@}`}*q%Dll7_cyhd^nYmE*er@h|{jknQ# zuyY`~J^Q7Qn6l^5CHUMVeVpjXM5jvW|4M{tl?Iali$_Qc?rIFs0MI z+w;1>sl?ruB#<6o$9N@ow6R73^ADH1d~D9Zq+&Oa4d4j}FF6H+39m>x!-kRPmMb%% zF+n{>oK2y@SnwR^YtO>mRN?aMGOJ0R&Vr6KzHRv{&O`zOrerfnB+dX0Ks^8e#K&fb z@&A^-{W+@5^Q>&Lu}wyYON*OXATp^_b8O8o0a1xqTE@f8^~pmWqXdxSEM_Nb3)=e& z9t!g2eESKgGBoLB{JhdP#yz|H7x^gDaipJkT-OBa4tTW~< zRrQrbRz`EK3`#KXgZLk zONHY!fMRXgNUc+g929_>(Y7F$m@5#+mvC1X{4EGmn2Qs1bWl(}$5Irb0;h)>Z6d3D zC+_{?4k7mK^LolSU!I<($mmglo`-6f>v6(aTJ#DvGij7m=>BBXSxQ7JPn+YTL;Ze_ zjA-8g&`p4*96^vbn=W0EX=(9E-2HW&fXf(wLzXsKD15pOE}{ecLwS-GIW#kN;<3C+ zgAz4(Zi8{ba@MJ$6bV)bvq-6eGVZhK!!W)GgX~y_poNJm5!a^h;^zL1s00{S)E_$L|Om%!L*?hs265E0SgCN zC8QC>F?b$WHtEPgek3gy8K}i&CGe?J6Mvm&n9py!;Tz`QF*+LHlBIaA$615ATXyeT zAGcCG9W$x&Z3*Nym+(w_$KMY$Np*WG`(Z+v-ti>gV zI}~x_12QG+pE7ISGh+Yv{%MNqhq@9PfFrk?WmEa?-6gOQfC{m_igsHuYxidI4rBRd zs@~d7Er=GD|GheqMko}?`$e@@3+S|gzHnR0!?>bK!T7O>L_y(@oz$Z2{iI4dFuvZ~ zx795;U=X7`ZK^oW3n|CN?|F_0>I7{`5_F-~;wt}V7Xo`X?$ZsXw$}mJpJp5!4S7J2VNC3XrJCNqUve%M))UY1QGr9ser+#2(1#*Y} z-li{j2K8uIbI!gdcU}&HnO1Y0O-;8z*Z;ZR3<_Xw%e+R;hXD}^8w8Yc1u3FCrXW#x ztU1O^3^p<*I$hKATRpfuzcBnDrMqg?ySZZ;G|=?4*7p^q$yE4pe74 za28M%pamD0Eifdzl$4h{rN5{s1cq(4n46<55TES8u!3mGDBYu}?z&m0K+zxZFzs5b zc<92d*KjsaHaoCH5C2bStchJO*`*wp)?5UQt{nkEgk0+B`w5R(66U3qJi2iKj47%H zb$#1%GzV$>cfWLRo>V+UR$rBVe2C2Ph)^O@L3}9@RNGJ=a5!A(Rl@fF=estq;hbIXU-hwAT{Vnb;Pc zlv(e>NkjF`gD#_a30xK`xCvEbxtlj|A9;AHEPg zB(yS&8+C(JWC~@+AP@J7%QXX!uL8+Q?La-3cJSoSJNI7Do*o$Pqp1s~JRXVt|fEbP0HuGJjd@*@G6nB|Ti zV@*-PV9$TX6p${$IJ7FdDkONLas8~7!*wJ->Xl%Bq*QVlFY#@3-h$G7|=!lcT2q5iHDg)+!m%cGkz8 zzG`UZN(slU<}xSC&O%!e+u_T%QSMeV;)fh!$%L9|R$DjO2TxOefrc(o4;Ia8im^UP zxrpqrV6A4iP>`dQ5>|1gmFK5Ib9NGh8E2Z6sw1vF7TkRJ(CXaqYUH)K&^P6lPQpb$ zkHqoe7JBaoxHo;lr7$6zOYOdaV04ytJhw=0=d@(c7KAnIg$IIyfA!tsQfHo}kyU{t$<@UZGU(Sw zi)TcLN=6^`&2)?vo8H5WkbvMPZ3&9F#8ZNk|Vhjst zT0Ekb4i~F!hM5zh82nMA>WWbKVpA;`%X=0k?+Z862fc@Xh_RkWjjW1D3@Bqj0b%=k zPAwv*{y9gf0H4OY(bcOHFC6ys>lX7r_n+y;EB`I*u*$>0kwGyCg62=DMI-q0d?P`X z7t!x9v@KdfE>GP^_Q3l$N$%Br9rDTfi2EEPo!Np8<9Oq28!CLWREk1r^Sq zvWAA^Acar3s>=H1VrYQpjoNMF^*54}Kc@E=rn@RKXC%UD^+=6oRe295rQ7TM=DpR3YwSjgsd<*e~WtA%!BadgmGvb+qxITw>eQVQ-0R)8Cj%r zF24Cr*yAL|QZCJ#HJYA!^cUB*y1*jp(evQAr@lz72>AXHi!CzJ_N2T-aLU zd*ztEcF5Zn^M}n_oPcgz0o^wK#W0!c4ze{B|vK@xsV%=1JncgK=;L$t@?Btsp#Z5 z%{>(Z{WV7t%{;o>`SzJ+pDjQsHsWVG_;;~C?LcuTM5f6 zm#82!I6LkHbo((sJy&Ep?M|rV-)MrO(Th6HKhJ*(HooTw)@ZL|2C6G-4vXm>LSDA* zB1w0pVCajq^1$1DYN0xVki{Mc)4y{lfY$I*Pf1){a%3W@e59Ejoa#C%vWZ_74ANwG zj)<1{b0FgH?_Y}k6SOhazhL`gGmF>=|YW z!WMymEsu>=fdwXCe6Z+Zmlg%Nnn-JfXrt@+ST&3m7AosM?IFW^*+`!-%3r?wJAQp% zy`bM|M&rInoah$s7ip1QMM1xuk|!cRxUTuA$>B-+%VzY$vR*!^R*NwgJWMBJ_Y2G8 zjVshQyQUXs5~(MUD(5t-nw}8*gx|Hwj_Ft>BE5*#e{nj+uSyW%abSA_^vsmc0^V?A z$A}GU;%6oi0+tkdpWgR>B}i2&kauW*I|+su^uHyAHG>tZJ&fxIQQVG2=lZu``jG8)m-khKj z|I!Jt#2&j!Lu1jdW}p#_P4bwa4fL_Cq5pU=r-p&(XEpH=?I1`bt6^-LoaL>|RIPry zgtM}T^9ht|>ApQcrKf|q^OKI?DU*o)Cv%%~an|pC;QTf(e}yvN(!Ko|5&9hzi_;Kceo7x z%lPa#c++Qb-y}ls!|v_XvXitZbiR$ho^l^Olr`vL4Pe{kY9zK zS+>v%OXiqkOkMw(eTVV>?t%9O1wKBhl?YDN-tz6yJO1~e5oGvEsW`SEuYG5`=3*VI zki}A8B^%kFLt96(v4!x7k)+SFRMH;soWto-#7FBX1=@QwD|NVN1~n^&oa<+R1&6Uf zDrAW8v};GVF*du=>wdB$_o~(6hv-TSD7L;y(kaHx&&ND$uKWd6HuTegdnh5hka`9I zm+~OG2)zUGI^;i}$n!)m@wyi;rf~?-hrmQAC-B+{9st(T9$n!RT=P0)6>d|pfJ-{m z1T+;M4H;!ZJgwNx8cDwsg8{-eo|A%I$JQ0GDORD~L+k1ln!ylBptt;mjpDjP4qQDT|=i&K-I8w*;I#hTayd4#o0TNIr&4 zwNSu1hx}VBx-v4g7XUFZ5Fb|XkNVa>WTv|>x?+WC{+c6HUm=scn8n;GS(NH5CuR~z zx|3|Lw!IcmK!0H&DweE*J=Ku?o93Yx&BR-KVY$-LmI9em3-Uem=Dgf6Xwtv{&ILtA zz(9y_tnhzbgQ4z85~~obuokby(CF=+w9=4~&Pgy1n#QBJH*q+TnK z4XOKK;x0j^D`ew?HkBIo+R4O$GL15k_zj$FD3?~KUZP+4GaEUj{(q{xQr=NTWXjn6 zRO|$Y5G?m%s0aeTvs0-#w_jD*XFE^ zfMFcuAUhYA|15$BT08w(CtM_eTX^I{H9(IdYMai_yqg!M>vrO&-bEV2H#c~7LepA zjnwVQXDl7=7!4M9H|OSlV*FS={xOQ{Kiiq;hvN72PpW9`7Isudl6u8W4jYW0z7~oY zjs(7*&zz&NijptQscC55Tv`+v#$GDC8QH?QoqIUr!=Hh?lCS;W*~SB&+z9#qzXo8O z+EeoXJ}6fafWjV*{?EUK)cxNd)PV5#e{b}^a*Vvuf1mV!KG*;9F^_Gd{%fTkuJnI6 fmtNw(Kr}FDyt1SN!;Vf11iVz0G!@Gf%tHPT>=rmL literal 0 HcmV?d00001 diff --git a/yabause/src/cocoa/vidgcd.c b/yabause/src/cocoa/vidgcd.c new file mode 100644 index 0000000000..311f987123 --- /dev/null +++ b/yabause/src/cocoa/vidgcd.c @@ -0,0 +1,3274 @@ +/* Copyright 2003-2004 Guillaume Duhamel + Copyright 2004-2008 Theo Berkau + Copyright 2006 Fabien Coulon + Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "vidgcd.h" +#include "vidshared.h" +#include "debug.h" +#include "vdp2.h" + +#ifdef HAVE_LIBGL +#define USE_OPENGL +#endif + +#ifdef USE_OPENGL +#include "ygl.h" +#endif + +#include "yui.h" + +#include +#include +#include + +#if defined WORDS_BIGENDIAN +static INLINE u32 COLSAT2YAB16(int priority,u32 temp) { return (priority | (temp & 0x7C00) << 1 | (temp & 0x3E0) << 14 | (temp & 0x1F) << 27); } +static INLINE u32 COLSAT2YAB32(int priority,u32 temp) { return (((temp & 0xFF) << 24) | ((temp & 0xFF00) << 8) | ((temp & 0xFF0000) >> 8) | priority); } +static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2) { return (((temp2 & 0xFF) << 24) | ((temp2 & 0xFF00) << 8) | ((temp1 & 0xFF) << 8) | priority); } +static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel) { return (pixel | 0xFF); } +#else +static INLINE u32 COLSAT2YAB16(int priority,u32 temp) { return (priority << 24 | (temp & 0x1F) << 3 | (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9); } +static INLINE u32 COLSAT2YAB32(int priority, u32 temp) { return (priority << 24 | (temp & 0xFF0000) | (temp & 0xFF00) | (temp & 0xFF)); } +static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2) { return (priority << 24 | ((temp1 & 0xFF) << 16) | (temp2 & 0xFF00) | (temp2 & 0xFF)); } +static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel) { return (0xFF000000 | pixel); } +#endif + +#define COLOR_ADDt(b) (b>0xFF?0xFF:(b<0?0:b)) +#define COLOR_ADDb(b1,b2) COLOR_ADDt((signed) (b1) + (b2)) +#ifdef WORDS_BIGENDIAN +#define COLOR_ADD(l,r,g,b) (COLOR_ADDb(l & 0xFF, r) << 24) | \ + (COLOR_ADDb((l >> 8) & 0xFF, g) << 16) | \ + (COLOR_ADDb((l >> 16) & 0xFF, b) << 8) | \ + ((l >> 24) & 0xFF) +#else +#define COLOR_ADD(l,r,g,b) COLOR_ADDb((l & 0xFF), r) | \ + (COLOR_ADDb((l >> 8) & 0xFF, g) << 8) | \ + (COLOR_ADDb((l >> 16) & 0xFF, b) << 16) | \ + (l & 0xFF000000) +#endif + +static void PushUserClipping(int mode); +static void PopUserClipping(void); + +int VIDGCDInit(void); +void VIDGCDDeInit(void); +void VIDGCDResize(unsigned int, unsigned int, int); +int VIDGCDIsFullscreen(void); +int VIDGCDVdp1Reset(void); +void VIDGCDVdp1DrawStart(void); +void VIDGCDVdp1DrawEnd(void); +void VIDGCDVdp1NormalSpriteDraw(void); +void VIDGCDVdp1ScaledSpriteDraw(void); +void VIDGCDVdp1DistortedSpriteDraw(void); +void VIDGCDVdp1PolygonDraw(void); +void VIDGCDVdp1PolylineDraw(void); +void VIDGCDVdp1LineDraw(void); +void VIDGCDVdp1UserClipping(void); +void VIDGCDVdp1SystemClipping(void); +void VIDGCDVdp1LocalCoordinate(void); +int VIDGCDVdp2Reset(void); +void VIDGCDVdp2DrawStart(void); +void VIDGCDVdp2DrawEnd(void); +void VIDGCDVdp2DrawScreens(void); +void VIDGCDVdp2SetResolution(u16 TVMD); +void FASTCALL VIDGCDVdp2SetPriorityNBG0(int priority); +void FASTCALL VIDGCDVdp2SetPriorityNBG1(int priority); +void FASTCALL VIDGCDVdp2SetPriorityNBG2(int priority); +void FASTCALL VIDGCDVdp2SetPriorityNBG3(int priority); +void FASTCALL VIDGCDVdp2SetPriorityRBG0(int priority); +void VIDGCDOnScreenDebugMessage(char *string, ...); +void VIDGCDGetGlSize(int *width, int *height); +void VIDGCDVdp1SwapFrameBuffer(void); +void VIDGCDVdp1EraseFrameBuffer(void); + +VideoInterface_struct VIDGCD = { +VIDCORE_GCD, +"Grand Central Dispatch Software Video Interface", +VIDGCDInit, +VIDGCDDeInit, +VIDGCDResize, +VIDGCDIsFullscreen, +VIDGCDVdp1Reset, +VIDGCDVdp1DrawStart, +VIDGCDVdp1DrawEnd, +VIDGCDVdp1NormalSpriteDraw, +VIDGCDVdp1ScaledSpriteDraw, +VIDGCDVdp1DistortedSpriteDraw, +//for the actual hardware, polygons are essentially identical to distorted sprites +//the actual hardware draws using diagonal lines, which is why using half-transparent processing +//on distorted sprites and polygons is not recommended since the hardware overdraws to prevent gaps +//thus, with half-transparent processing some pixels will be processed more than once, producing moire patterns in the drawn shapes +VIDGCDVdp1DistortedSpriteDraw, +VIDGCDVdp1PolylineDraw, +VIDGCDVdp1LineDraw, +VIDGCDVdp1UserClipping, +VIDGCDVdp1SystemClipping, +VIDGCDVdp1LocalCoordinate, +VIDGCDVdp2Reset, +VIDGCDVdp2DrawStart, +VIDGCDVdp2DrawEnd, +VIDGCDVdp2DrawScreens, +VIDGCDVdp2SetResolution, +VIDGCDVdp2SetPriorityNBG0, +VIDGCDVdp2SetPriorityNBG1, +VIDGCDVdp2SetPriorityNBG2, +VIDGCDVdp2SetPriorityNBG3, +VIDGCDVdp2SetPriorityRBG0, +VIDGCDOnScreenDebugMessage, +VIDGCDGetGlSize, +}; + +static u32 *dispbuffer=NULL; +static u8 *vdp1framebuffer[2]= { NULL, NULL }; +static u8 *vdp1frontframebuffer; +static u8 *vdp1backframebuffer; +static u32 *vdp2framebuffer=NULL; + +static int vdp1width; +static int vdp1height; +static int vdp1clipxstart; +static int vdp1clipxend; +static int vdp1clipystart; +static int vdp1clipyend; +static int vdp1pixelsize; +static int vdp1spritetype; +int vdp2width; +int vdp2height; +static int nbg0priority=0; +static int nbg1priority=0; +static int nbg2priority=0; +static int nbg3priority=0; +static int rbg0priority=0; +#ifdef USE_OPENGL +static int outputwidth; +static int outputheight; +#endif +static int resxratio; +static int resyratio; + +static char message[512]; +static int msglength; + +typedef struct { s16 x; s16 y; } vdp1vertex; + +typedef struct +{ + int pagepixelwh, pagepixelwh_bits, pagepixelwh_mask; + int planepixelwidth, planepixelwidth_bits, planepixelwidth_mask; + int planepixelheight, planepixelheight_bits, planepixelheight_mask; + int screenwidth; + int screenheight; + int oldcellx, oldcelly, oldcellcheck; + int xmask, ymask; + u32 planetbl[16]; +} screeninfo_struct; + +struct { + vdp2draw_struct info; + u8 prioritytable[8]; + u32 coloroffset; + int islinewindow; + clipping_struct clip[2]; + u32 linewnd0addr, linewnd1addr; + int priosused[8]; +} vdp1draw_info; + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void vdp2putpixel32(s32 x, s32 y, u32 color, int priority) +{ + vdp2framebuffer[(y * vdp2width) + x] = COLSAT2YAB32(priority, color); +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u8 Vdp2GetPixelPriority(u32 pixel) +{ +#if defined WORDS_BIGENDIAN + return pixel; +#else + return pixel >> 24; +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void puthline16(s32 x, s32 y, s32 width, u16 color, int priority) +{ + u32 *buffer = vdp2framebuffer + (y * vdp2width) + x; + u32 dot=COLSAT2YAB16(priority, color); + int i; + + for (i = 0; i < width; i++) + buffer[i] = dot; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 FASTCALL Vdp2ColorRamGetColor(u32 addr) +{ + switch(Vdp2Internal.ColorMode) + { + case 0: + { + u32 tmp; + addr <<= 1; + tmp = T2ReadWord(Vdp2ColorRam, addr & 0xFFF); + return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)); + } + case 1: + { + u32 tmp; + addr <<= 1; + tmp = T2ReadWord(Vdp2ColorRam, addr & 0xFFF); + return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)); + } + case 2: + { + addr <<= 2; + return T2ReadLong(Vdp2ColorRam, addr & 0xFFF); + } + default: break; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void Vdp2PatternAddr(vdp2draw_struct *info) +{ + switch(info->patterndatasize) + { + case 1: + { + u16 tmp = T1ReadWord(Vdp2Ram, info->addr); + + info->addr += 2; + info->specialfunction = (info->supplementdata >> 9) & 0x1; + + switch(info->colornumber) + { + case 0: // in 16 colors + info->paladdr = ((tmp & 0xF000) >> 8) | ((info->supplementdata & 0xE0) << 3); + break; + default: // not in 16 colors + info->paladdr = (tmp & 0x7000) >> 4; + break; + } + + switch(info->auxmode) + { + case 0: + info->flipfunction = (tmp & 0xC00) >> 10; + + switch(info->patternwh) + { + case 1: + info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10); + break; + case 2: + info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10); + break; + } + break; + case 1: + info->flipfunction = 0; + + switch(info->patternwh) + { + case 1: + info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10); + break; + case 2: + info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10); + break; + } + break; + } + + break; + } + case 2: { + u16 tmp1 = T1ReadWord(Vdp2Ram, info->addr); + u16 tmp2 = T1ReadWord(Vdp2Ram, info->addr+2); + info->addr += 4; + info->charaddr = tmp2 & 0x7FFF; + info->flipfunction = (tmp1 & 0xC000) >> 14; + info->paladdr = (tmp1 & 0x7F) << 4; + info->specialfunction = (tmp1 & 0x2000) >> 13; + break; + } + } + + if (!(Vdp2Regs->VRSIZE & 0x8000)) + info->charaddr &= 0x3FFF; + + info->charaddr *= 0x20; // selon Runik + if (info->specialprimode == 1) { + info->priority = (info->priority & 0xE) | (info->specialfunction & 1); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 FASTCALL DoNothing(UNUSED void *info, u32 pixel) +{ + return pixel; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 FASTCALL DoColorOffset(void *info, u32 pixel) +{ + return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, + ((vdp2draw_struct *)info)->cog, + ((vdp2draw_struct *)info)->cob); +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 FASTCALL DoColorCalc(void *info, u32 pixel) +{ +#if 0 + u8 oldr, oldg, oldb; + u8 r, g, b; + u32 oldpixel = 0x00FFFFFF; // fix me + + static int topratio[32] = { + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + }; + static int bottomratio[32] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + }; + + // separate color components for top and second pixel + r = (pixel & 0xFF) * topratio[((vdp2draw_struct *)info)->alpha] >> 5; + g = ((pixel >> 8) & 0xFF) * topratio[((vdp2draw_struct *)info)->alpha] >> 5; + b = ((pixel >> 16) & 0xFF) * topratio[((vdp2draw_struct *)info)->alpha] >> 5; + +#ifdef WORDS_BIGENDIAN + oldr = ((oldpixel >> 24) & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; + oldg = ((oldpixel >> 16) & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; + oldb = ((oldpixel >> 8) & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; +#else + oldr = (oldpixel & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; + oldg = ((oldpixel >> 8) & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; + oldb = ((oldpixel >> 16) & 0xFF) * bottomratio[((vdp2draw_struct *)info)->alpha] >> 5; +#endif + + // add color components and reform the pixel + pixel = ((b + oldb) << 16) | ((g + oldg) << 8) | (r + oldr); +#endif + return pixel; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 FASTCALL DoColorCalcWithColorOffset(void *info, u32 pixel) +{ + pixel = DoColorCalc(info, pixel); + + return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, + ((vdp2draw_struct *)info)->cog, + ((vdp2draw_struct *)info)->cob); +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void ReadVdp2ColorOffset(vdp2draw_struct *info, int clofmask, int ccmask) +{ + if (Vdp2Regs->CLOFEN & clofmask) + { + // color offset enable + if (Vdp2Regs->CLOFSL & clofmask) + { + // color offset B + info->cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info->cor |= 0xFFFFFF00; + + info->cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info->cog |= 0xFFFFFF00; + + info->cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info->cob |= 0xFFFFFF00; + } + else + { + // color offset A + info->cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info->cor |= 0xFFFFFF00; + + info->cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info->cog |= 0xFFFFFF00; + + info->cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info->cob |= 0xFFFFFF00; + } + + if (info->cor == 0 && info->cog == 0 && info->cob == 0) + { + if (Vdp2Regs->CCCTL & ccmask) + info->PostPixelFetchCalc = &DoColorCalc; + else + info->PostPixelFetchCalc = &DoNothing; + } + else + { + if (Vdp2Regs->CCCTL & ccmask) + info->PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info->PostPixelFetchCalc = &DoColorOffset; + } + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & ccmask) + info->PostPixelFetchCalc = &DoColorCalc; + else + info->PostPixelFetchCalc = &DoNothing; + } + +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE int Vdp2FetchPixel(vdp2draw_struct *info, int x, int y, u32 *color) +{ + u32 dot; + + switch(info->colornumber) + { + case 0: // 4 BPP + dot = T1ReadByte(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) / 2) & 0x7FFFF)); + if (!(x & 0x1)) dot >>= 4; + if (!(dot & 0xF) && info->transparencyenable) return 0; + else + { + *color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | (dot & 0xF))); + return 1; + } + case 1: // 8 BPP + dot = T1ReadByte(Vdp2Ram, ((info->charaddr + (y * info->cellw) + x) & 0x7FFFF)); + if (!(dot & 0xFF) && info->transparencyenable) return 0; + else + { + *color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | (dot & 0xFF))); + return 1; + } + case 2: // 16 BPP(palette) + dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF)); + if ((dot == 0) && info->transparencyenable) return 0; + else + { + *color = Vdp2ColorRamGetColor(info->coloroffset + dot); + return 1; + } + case 3: // 16 BPP(RGB) + dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF)); + if (!(dot & 0x8000) && info->transparencyenable) return 0; + else + { + *color = COLSAT2YAB16(0, dot); + return 1; + } + case 4: // 32 BPP + dot = T1ReadLong(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 4) & 0x7FFFF)); + if (!(dot & 0x80000000) && info->transparencyenable) return 0; + else + { + *color = COLSAT2YAB32(0, dot); + return 1; + } + default: + return 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE int TestWindow(int wctl, int enablemask, int inoutmask, clipping_struct *clip, int x, int y) +{ + if (wctl & enablemask) + { + if (wctl & inoutmask) + { + // Draw inside of window + if (x < clip->xstart || x > clip->xend || + y < clip->ystart || y > clip->yend) + return 0; + } + else + { + // Draw outside of window + if (x >= clip->xstart && x <= clip->xend && + y >= clip->ystart && y <= clip->yend) + return 0; + + //it seems to overflow vertically on hardware + if(clip->yend > vdp2height && (x >= clip->xstart && x <= clip->xend )) + return 0; + } + } + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void GeneratePlaneAddrTable(vdp2draw_struct *info, u32 *planetbl) +{ + int i; + + for (i = 0; i < (info->mapwh*info->mapwh); i++) + { + info->PlaneAddr(info, i); + planetbl[i] = info->addr; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void FASTCALL Vdp2MapCalcXY(vdp2draw_struct *info, int *x, int *y, + screeninfo_struct *sinfo) +{ + int planenum; + const int pagesize_bits=info->pagewh_bits*2; + const int cellwh=(2 + info->patternwh); + + const int check = ((y[0] >> cellwh) << 16) | (x[0] >> cellwh); + //if ((x[0] >> cellwh) != sinfo->oldcellx || (y[0] >> cellwh) != sinfo->oldcelly) + if(check != sinfo->oldcellcheck) + { + sinfo->oldcellx = x[0] >> cellwh; + sinfo->oldcelly = y[0] >> cellwh; + sinfo->oldcellcheck = (sinfo->oldcelly << 16) | sinfo->oldcellx; + + // Calculate which plane we're dealing with + planenum = ((y[0] >> sinfo->planepixelheight_bits) * info->mapwh) + (x[0] >> sinfo->planepixelwidth_bits); + x[0] = (x[0] & sinfo->planepixelwidth_mask); + y[0] = (y[0] & sinfo->planepixelheight_mask); + + // Fetch and decode pattern name data + info->addr = sinfo->planetbl[planenum]; + + // Figure out which page it's on(if plane size is not 1x1) + info->addr += (( ((y[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) << info->planew_bits) + + ( (x[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) + + (((y[0] & sinfo->pagepixelwh_mask) >> cellwh) << info->pagewh_bits) + + ((x[0] & sinfo->pagepixelwh_mask) >> cellwh)) << (info->patterndatasize_bits+1); + + Vdp2PatternAddr(info); // Heh, this could be optimized + } + + // Figure out which pixel in the tile we want + if (info->patternwh == 1) + { + x[0] &= 8-1; + y[0] &= 8-1; + + switch(info->flipfunction & 0x3) + { + case 0: //none + break; + case 1: //horizontal flip + x[0] = 8 - 1 - x[0]; + break; + case 2: // vertical flip + y[0] = 8 - 1 - y[0]; + break; + case 3: //flip both + x[0] = 8 - 1 - x[0]; + y[0] = 8 - 1 - y[0]; + break; + } + } + else + { + if (info->flipfunction) + { + y[0] &= 16 - 1; + if (info->flipfunction & 0x2) + { + if (!(y[0] & 8)) + y[0] = 8 - 1 - y[0] + 16; + else + y[0] = 16 - 1 - y[0]; + } + else if (y[0] & 8) + y[0] += 8; + + if (info->flipfunction & 0x1) + { + if (!(x[0] & 8)) + y[0] += 8; + + x[0] &= 8-1; + x[0] = 8 - 1 - x[0]; + } + else if (x[0] & 8) + { + y[0] += 8; + x[0] &= 8-1; + } + else + x[0] &= 8-1; + } + else + { + y[0] &= 16 - 1; + + if (y[0] & 8) + y[0] += 8; + if (x[0] & 8) + y[0] += 8; + x[0] &= 8-1; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void SetupScreenVars(vdp2draw_struct *info, screeninfo_struct *sinfo) +{ + if (!info->isbitmap) + { + sinfo->pagepixelwh=64*8; + sinfo->pagepixelwh_bits = 9; + sinfo->pagepixelwh_mask = 511; + + sinfo->planepixelwidth=info->planew*sinfo->pagepixelwh; + sinfo->planepixelwidth_bits = 8+info->planew; + sinfo->planepixelwidth_mask = (1<<(sinfo->planepixelwidth_bits))-1; + + sinfo->planepixelheight=info->planeh*sinfo->pagepixelwh; + sinfo->planepixelheight_bits = 8+info->planeh; + sinfo->planepixelheight_mask = (1<<(sinfo->planepixelheight_bits))-1; + + sinfo->screenwidth=info->mapwh*sinfo->planepixelwidth; + sinfo->screenheight=info->mapwh*sinfo->planepixelheight; + sinfo->oldcellx=-1; + sinfo->oldcelly=-1; + sinfo->xmask = sinfo->screenwidth-1; + sinfo->ymask = sinfo->screenheight-1; + GeneratePlaneAddrTable(info, sinfo->planetbl); + } + else + { + sinfo->pagepixelwh = 0; + sinfo->pagepixelwh_bits = 0; + sinfo->pagepixelwh_mask = 0; + sinfo->planepixelwidth=0; + sinfo->planepixelwidth_bits=0; + sinfo->planepixelwidth_mask=0; + sinfo->planepixelheight=0; + sinfo->planepixelheight_bits=0; + sinfo->planepixelheight_mask=0; + sinfo->screenwidth=0; + sinfo->screenheight=0; + sinfo->oldcellx=0; + sinfo->oldcelly=0; + sinfo->oldcellcheck=0; + sinfo->xmask = info->cellw-1; + sinfo->ymask = info->cellh-1; + } +} + +////////////////////////////////////////////////////////////////////////////// + +/* FIXME: This function will not work on Big Endian systems. Since its only here + in vidgcd, and the only thing that this vidcore will work on right now is + Mac OS X 10.6 (which is only available for Little Endian systems), its ok for + the time being... */ +static INLINE u32 FASTCALL Vdp2Blend(vdp2draw_struct *info, u32 src, u32 dst) { + u32 alpha, s1, s2, d1, d2, o1, o2, pri; + + pri = Vdp2GetPixelPriority(dst); + + /* If we're not doing color calculation on this plane, then there's no need + to do any blending... */ + if(info->PostPixelFetchCalc != &DoColorCalc && + info->PostPixelFetchCalc != &DoColorCalcWithColorOffset) { + /* If the old pixel is of higher priority, use it. */ + if(pri > info->priority) { + return dst; + } + + return src; + } + + if(pri < info->priority) { + pri = info->priority; + } + + alpha = 255 - (((info->alpha) << 3) + 0x07); + + /* Magic alpha blending! */ + s1 = src & 0x00FF00FF; + s2 = src & 0x0000FF00; + d1 = dst & 0x00FF00FF; + d2 = dst & 0x0000FF00; + + o1 = (d1 + (((s1 - d1) * alpha) >> 8)) & 0x00FF00FF; + o2 = (d2 + (((s2 - d2) * alpha) >> 8)) & 0x0000FF00; + + return o1 | o2 | (pri << 24); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL GCDVdp2DrawScroll(vdp2draw_struct *_info, u32 *_textdata, int width, int height) +{ + int i, j; + clipping_struct _clip[2], _clip1, _clip2; + u32 _linewnd0addr, _linewnd1addr; + screeninfo_struct _sinfo; + int scrollx, scrolly; + int *mosaic_y, *mosaic_x; + int linescrollmult = (_info->islinescroll & 1) + + ((_info->islinescroll & 2) >> 1) + ((_info->islinescroll & 4) >> 2); + + _info->coordincx *= (float)resxratio; + _info->coordincy *= (float)resyratio; + + SetupScreenVars(_info, &_sinfo); + + scrollx = _info->x; + scrolly = _info->y; + + _clip[0].xstart = _clip[0].ystart = _clip[0].xend = _clip[0].yend = 0; + _clip[1].xstart = _clip[1].ystart = _clip[1].xend = _clip[1].yend = 0; + ReadWindowData(_info->wctl, _clip); + _clip1 = _clip[0]; + _clip2 = _clip[1]; + _linewnd0addr = _linewnd1addr = 0; + ReadLineWindowData(&_info->islinewindow, _info->wctl, &_linewnd0addr, &_linewnd1addr); + + { + static int tables_initialized = 0; + static int mosaic_table[16][1024]; + if(!tables_initialized) + { + tables_initialized = 1; + for(i=0;i<16;i++) + { + int m = i+1; + for(j=0;j<1024;j++) + mosaic_table[i][j] = j/m*m; + } + } + mosaic_x = mosaic_table[_info->mosaicxmask-1]; + mosaic_y = mosaic_table[_info->mosaicymask-1]; + } + + dispatch_apply(height, dispatch_get_global_queue(2, 0), ^(size_t j) { + int x, y, i; + u32 *textdata = _textdata + (j * width); + u32 linewnd0addr = _linewnd0addr, linewnd1addr = _linewnd1addr; + screeninfo_struct sinfo = _sinfo; + clipping_struct clip[2] = { _clip1, _clip2 }; + vdp2draw_struct inf = *_info; + vdp2draw_struct *info = &inf; + int Y; + int linescrollx = 0; + + // if line window is enabled, adjust clipping values + if(info->islinewindow) { + // Fetch new xstart and xend values from table + if (info->islinewindow & 0x1) + { + // Window 0 + linewnd0addr = _linewnd0addr + j * 4; + clip[0].xstart = (T1ReadWord(Vdp2Ram, linewnd0addr) & 0x3FF) >> 1; // fix me + linewnd0addr += 2; + clip[0].xend = (T1ReadWord(Vdp2Ram, linewnd0addr) & 0x3FF) >> 1; // fix me + } + if (info->islinewindow & 0x2) + { + // Window 1 + linewnd1addr = _linewnd1addr + j * 4; + clip[1].xstart = (T1ReadWord(Vdp2Ram, linewnd1addr) & 0x3FF) >> 1; // fix me + linewnd1addr += 2; + clip[1].xend = (T1ReadWord(Vdp2Ram, linewnd1addr) & 0x3FF) >> 1; // fix me + } + } + + // precalculate the coordinate for the line(it's faster) and do line + // scroll + if (info->islinescroll) + { + /* Figure out where we actually are in the line scroll table. */ + info->linescrolltbl += linescrollmult * (j << 2); + + if (info->islinescroll & 0x1) + { + linescrollx = (T1ReadLong(Vdp2Ram, info->linescrolltbl) >> 16) & 0x7FF; + info->linescrolltbl += 4; + } + if (info->islinescroll & 0x2) + { + info->y = (T1ReadWord(Vdp2Ram, info->linescrolltbl) & 0x7FF) + scrolly; + info->linescrolltbl += 4; + y = info->y; + } + else + //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j))); + y = info->y + info->coordincy*mosaic_y[j]; + if (info->islinescroll & 0x4) + { + info->coordincx = (T1ReadLong(Vdp2Ram, info->linescrolltbl) & 0x7FF00) / (float)65536.0; + info->linescrolltbl += 4; + } + } + else + //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j))); + y = info->y + info->coordincy * mosaic_y[j]; + + y &= sinfo.ymask; + + if (info->isverticalscroll) + { + // this is *wrong*, vertical scroll use a different value per cell + // info->verticalscrolltbl should be incremented by info->verticalscrollinc + // each time there's a cell change and reseted at the end of the line... + // or something like that :) + y += T1ReadLong(Vdp2Ram, info->verticalscrolltbl) >> 16; + y &= 0x1FF; + } + + Y=y; + + for (i = 0; i < width; i++) + { + u32 color; + + // See if screen position is clipped, if it isn't, continue + // AND window logic + if(!TestWindow(info->wctl, 0x2, 0x1, &clip[0], i, j) && !TestWindow(info->wctl, 0x8, 0x4, &clip[1], i, j) && (info->wctl & 0x80) == 0x80) + { + textdata++; + continue; + } + //OR window logic + else if ((info->wctl & 0x80) == 0) + { + if (!TestWindow(info->wctl, 0x2, 0x1, &clip[0], i, j)) + { + textdata++; + continue; + } + + // Window 1 + if (!TestWindow(info->wctl, 0x8, 0x4, &clip[1], i,j)) + { + textdata++; + continue; + } + } + + //x = info->x+((int)(info->coordincx*(float)((info->mosaicxmask > 1) ? (i / info->mosaicxmask * info->mosaicxmask) : i))); + x = info->x + mosaic_x[i]*info->coordincx; + x &= sinfo.xmask; + + if (linescrollx) { + x += linescrollx; + x &= 0x3FF; + } + + if (!info->isbitmap) + { + // Tile + y=Y; + // need to calculate this without history! + Vdp2MapCalcXY(info, &x, &y, &sinfo); + } + + // If priority of screen is less than current top pixel and per + // pixel priority isn't used, skip it +#ifndef CRAB_REWRITE + if (Vdp2GetPixelPriority(textdata[0]) > info->priority) + { + textdata++; + continue; + } +#endif + + // Fetch Pixel, if it isn't transparent, continue + if (!Vdp2FetchPixel(info, x, y, &color)) + { + textdata++; + continue; + } + + // check special priority somewhere here + + // Apply color offset and color calculation/special color calculation + // and then continue. + // We almost need to know well ahead of time what the top + // and second pixel is in order to work this. + +#ifndef CRAB_REWRITE + textdata[0] = COLSAT2YAB32(info->priority, info->PostPixelFetchCalc(info, color)); + textdata++; +#else + color = COLSAT2YAB32(info->priority, info->PostPixelFetchCalc(info, color)); + *textdata++ = Vdp2Blend(info, color, *textdata); +#endif + } + }); +} + +////////////////////////////////////////////////////////////////////////////// + +static void SetupRotationInfo(vdp2draw_struct *info, vdp2rotationparameterfp_struct *p) +{ + if (info->rotatenum == 0) + { + Vdp2ReadRotationTableFP(0, p); + info->PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; + } + else + { + Vdp2ReadRotationTableFP(1, &p[1]); + info->PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL Vdp2DrawRotationFP(vdp2draw_struct *info, vdp2rotationparameterfp_struct *parameter) +{ + int i, j; + int x, y; + screeninfo_struct sinfo; + vdp2rotationparameterfp_struct *p=¶meter[info->rotatenum]; + + SetupRotationInfo(info, parameter); + + if (!p->coefenab) + { + fixed32 xmul, ymul, C, F; + + // Since coefficients aren't being used, we can simplify the drawing process + if (IsScreenRotatedFP(p)) + { + // No rotation + info->x = touint(mulfixed(p->kx, (p->Xst - p->Px)) + p->Px + p->Mx); + info->y = touint(mulfixed(p->ky, (p->Yst - p->Py)) + p->Py + p->My); + info->coordincx = tofloat(p->kx); + info->coordincy = tofloat(p->ky); + } + else + { + u32 *textdata=vdp2framebuffer; + + GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F); + + // Do simple rotation + CalculateRotationValuesFP(p); + + SetupScreenVars(info, &sinfo); + + for (j = 0; j < vdp2height; j++) + { + for (i = 0; i < vdp2width; i++) + { + u32 color; + + x = GenerateRotatedXPosFP(p, i, xmul, ymul, C) & sinfo.xmask; + y = GenerateRotatedYPosFP(p, i, xmul, ymul, F) & sinfo.ymask; + xmul += p->deltaXst; + + // Convert coordinates into graphics + if (!info->isbitmap) + { + // Tile + Vdp2MapCalcXY(info, &x, &y, &sinfo); + } + + // If priority of screen is less than current top pixel and per + // pixel priority isn't used, skip it + if (Vdp2GetPixelPriority(textdata[0]) > info->priority) + { + textdata++; + continue; + } + + // Fetch pixel + if (!Vdp2FetchPixel(info, x, y, &color)) + { + textdata++; + continue; + } + + textdata[0] = COLSAT2YAB32(info->priority, info->PostPixelFetchCalc(info, color)); + textdata++; + } + ymul += p->deltaYst; + } + + return; + } + } + else + { + fixed32 xmul, ymul, C, F; + fixed32 coefx, coefy; + u32 *textdata=vdp2framebuffer; + + GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F); + + // Rotation using Coefficient Tables(now this stuff just gets wacky. It + // has to be done in software, no exceptions) + CalculateRotationValuesFP(p); + + SetupScreenVars(info, &sinfo); + coefx = coefy = 0; + + for (j = 0; j < vdp2height; j++) + { + if (p->deltaKAx == 0) + { + Vdp2ReadCoefficientFP(p, + p->coeftbladdr + + touint(coefy) * + p->coefdatasize); + } + + for (i = 0; i < vdp2width; i++) + { + u32 color; + + if (p->deltaKAx != 0) + { + Vdp2ReadCoefficientFP(p, + p->coeftbladdr + + toint(coefy + coefx) * + p->coefdatasize); + coefx += p->deltaKAx; + } + + if (p->msb) + { + textdata++; + continue; + } + + x = GenerateRotatedXPosFP(p, i, xmul, ymul, C) & sinfo.xmask; + y = GenerateRotatedYPosFP(p, i, xmul, ymul, F) & sinfo.ymask; + xmul += p->deltaXst; + + // Convert coordinates into graphics + if (!info->isbitmap) + { + // Tile + Vdp2MapCalcXY(info, &x, &y, &sinfo); + } + + // If priority of screen is less than current top pixel and per + // pixel priority isn't used, skip it + if (Vdp2GetPixelPriority(textdata[0]) > info->priority) + { + textdata++; + continue; + } + + // Fetch pixel + if (!Vdp2FetchPixel(info, x, y, &color)) + { + textdata++; + continue; + } + + textdata[0] = COLSAT2YAB32(info->priority, info->PostPixelFetchCalc(info, color)); + textdata++; + } + ymul += p->deltaYst; + coefx = 0; + coefy += p->deltaKAst; + } + return; + } + + GCDVdp2DrawScroll(info, vdp2framebuffer, vdp2width, vdp2height); +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawBackScreen(void) +{ + int i; + + // Only draw black if TVMD's DISP and BDCLMD bits are cleared + if ((Vdp2Regs->TVMD & 0x8000) == 0 && (Vdp2Regs->TVMD & 0x100) == 0) + { + // Draw Black + for (i = 0; i < (vdp2width * vdp2height); i++) + vdp2framebuffer[i] = 0; + } + else + { + // Draw Back Screen + u32 scrAddr; + u16 dot; + + if (Vdp2Regs->VRSIZE & 0x8000) + scrAddr = (((Vdp2Regs->BKTAU & 0x7) << 16) | Vdp2Regs->BKTAL) * 2; + else + scrAddr = (((Vdp2Regs->BKTAU & 0x3) << 16) | Vdp2Regs->BKTAL) * 2; + + if (Vdp2Regs->BKTAU & 0x8000) + { + // Per Line + for (i = 0; i < vdp2height; i++) + { + dot = T1ReadWord(Vdp2Ram, scrAddr); + scrAddr += 2; + + puthline16(0, i, vdp2width, dot, 0); + } + } + else + { + // Single Color + dot = T1ReadWord(Vdp2Ram, scrAddr); + + for (i = 0; i < (vdp2width * vdp2height); i++) + vdp2framebuffer[i] = COLSAT2YAB16(0, dot); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawNBG0(void) +{ + vdp2draw_struct info; + vdp2rotationparameterfp_struct parameter[2]; + + if (Vdp2Regs->BGON & 0x20) + { + // RBG1 mode + info.enable = Vdp2Regs->BGON & 0x20; + + // Read in Parameter B + Vdp2ReadRotationTableFP(1, ¶meter[1]); + + if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) + { + // Bitmap Mode + ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); + + info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; + info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + // Tile Mode + info.mapwh = 4; + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); + ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); + } + + info.rotatenum = 1; + info.rotatemode = 0; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; + } + else if (Vdp2Regs->BGON & 0x1) + { + // NBG0 mode + info.enable = Vdp2Regs->BGON & 0x1; + + if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) + { + // Bitmap Mode + ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); + + info.x = Vdp2Regs->SCXIN0 & 0x7FF; + info.y = Vdp2Regs->SCYIN0 & 0x7FF; + + info.charaddr = (Vdp2Regs->MPOFN & 0x7) * 0x20000; + info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + // Tile Mode + info.mapwh = 2; + + ReadPlaneSize(&info, Vdp2Regs->PLSZ); + + info.x = Vdp2Regs->SCXIN0 & 0x7FF; + info.y = Vdp2Regs->SCYIN0 & 0x7FF; + ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); + } + + info.coordincx = (Vdp2Regs->ZMXN0.all & 0x7FF00) / (float) 65536; + info.coordincy = (Vdp2Regs->ZMYN0.all & 0x7FF00) / (float) 65536; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG0PlaneAddr; + } + else + // Not enabled + return; + + info.transparencyenable = !(Vdp2Regs->BGON & 0x100); + info.specialprimode = Vdp2Regs->SFPRMD & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLA & 0x70) >> 4; + + if (Vdp2Regs->CCCTL & 0x1) + info.alpha = Vdp2Regs->CCRNA & 0x1F; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x7) << 8; + ReadVdp2ColorOffset(&info, 0x1, 0x1); + info.priority = nbg0priority; + + if (!(info.enable & Vdp2External.disptoggle)) + return; + + ReadMosaicData(&info, 0x1); + ReadLineScrollData(&info, Vdp2Regs->SCRCTL & 0xFF, Vdp2Regs->LSTA0.all); + if (Vdp2Regs->SCRCTL & 1) + { + info.isverticalscroll = 1; + info.verticalscrolltbl = (Vdp2Regs->VCSTA.all & 0x7FFFE) << 1; + if (Vdp2Regs->SCRCTL & 0x100) + info.verticalscrollinc = 8; + else + info.verticalscrollinc = 4; + } + else + info.isverticalscroll = 0; + info.wctl = Vdp2Regs->WCTLA; + + if (info.enable == 1) + { + // NBG0 draw + GCDVdp2DrawScroll(&info, vdp2framebuffer, vdp2width, vdp2height); + } + else + { + // RBG1 draw + Vdp2DrawRotationFP(&info, parameter); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawNBG1(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x2; + info.transparencyenable = !(Vdp2Regs->BGON & 0x200); + info.specialprimode = (Vdp2Regs->SFPRMD >> 2) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; + + if((info.isbitmap = Vdp2Regs->CHCTLA & 0x200) != 0) + { + ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 10, 0x3); + + info.x = Vdp2Regs->SCXIN1 & 0x7FF; + info.y = Vdp2Regs->SCYIN1 & 0x7FF; + + info.charaddr = ((Vdp2Regs->MPOFN & 0x70) >> 4) * 0x20000; + info.paladdr = Vdp2Regs->BMPNA & 0x700; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + info.mapwh = 2; + + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 2); + + info.x = Vdp2Regs->SCXIN1 & 0x7FF; + info.y = Vdp2Regs->SCYIN1 & 0x7FF; + + ReadPatternData(&info, Vdp2Regs->PNCN1, Vdp2Regs->CHCTLA & 0x100); + } + + if (Vdp2Regs->CCCTL & 0x2) + info.alpha = (Vdp2Regs->CCRNA >> 8) & 0x1F; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x70) << 4; + ReadVdp2ColorOffset(&info, 0x2, 0x2); + info.coordincx = (Vdp2Regs->ZMXN1.all & 0x7FF00) / (float) 65536; + info.coordincy = (Vdp2Regs->ZMYN1.all & 0x7FF00) / (float) 65536; + + info.priority = nbg1priority; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG1PlaneAddr; + + if (!(info.enable & Vdp2External.disptoggle)) + return; + + ReadMosaicData(&info, 0x2); + ReadLineScrollData(&info, Vdp2Regs->SCRCTL >> 8, Vdp2Regs->LSTA1.all); + if (Vdp2Regs->SCRCTL & 0x100) + { + info.isverticalscroll = 1; + if (Vdp2Regs->SCRCTL & 0x1) + { + info.verticalscrolltbl = 4 + ((Vdp2Regs->VCSTA.all & 0x7FFFE) << 1); + info.verticalscrollinc = 8; + } + else + { + info.verticalscrolltbl = (Vdp2Regs->VCSTA.all & 0x7FFFE) << 1; + info.verticalscrollinc = 4; + } + } + else + info.isverticalscroll = 0; + info.wctl = Vdp2Regs->WCTLA >> 8; + + GCDVdp2DrawScroll(&info, vdp2framebuffer, vdp2width, vdp2height); +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawNBG2(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x4; + info.transparencyenable = !(Vdp2Regs->BGON & 0x400); + info.specialprimode = (Vdp2Regs->SFPRMD >> 4) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLB & 0x2) >> 1; + info.mapwh = 2; + + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 4); + info.x = Vdp2Regs->SCXN2 & 0x7FF; + info.y = Vdp2Regs->SCYN2 & 0x7FF; + ReadPatternData(&info, Vdp2Regs->PNCN2, Vdp2Regs->CHCTLB & 0x1); + + if (Vdp2Regs->CCCTL & 0x4) + info.alpha = Vdp2Regs->CCRNB & 0x1F; + + info.coloroffset = Vdp2Regs->CRAOFA & 0x700; + ReadVdp2ColorOffset(&info, 0x4, 0x4); + info.coordincx = info.coordincy = 1; + + info.priority = nbg2priority; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG2PlaneAddr; + + if (!(info.enable & Vdp2External.disptoggle)) + return; + + ReadMosaicData(&info, 0x4); + info.islinescroll = 0; + info.isverticalscroll = 0; + info.wctl = Vdp2Regs->WCTLB; + info.isbitmap = 0; + + GCDVdp2DrawScroll(&info, vdp2framebuffer, vdp2width, vdp2height); +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawNBG3(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x8; + info.transparencyenable = !(Vdp2Regs->BGON & 0x800); + info.specialprimode = (Vdp2Regs->SFPRMD >> 6) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLB & 0x20) >> 5; + + info.mapwh = 2; + + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 6); + info.x = Vdp2Regs->SCXN3 & 0x7FF; + info.y = Vdp2Regs->SCYN3 & 0x7FF; + ReadPatternData(&info, Vdp2Regs->PNCN3, Vdp2Regs->CHCTLB & 0x10); + + if (Vdp2Regs->CCCTL & 0x8) + info.alpha = (Vdp2Regs->CCRNB >> 8) & 0x1F; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x7000) >> 4; + ReadVdp2ColorOffset(&info, 0x8, 0x8); + info.coordincx = info.coordincy = 1; + + info.priority = nbg3priority; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG3PlaneAddr; + + if (!(info.enable & Vdp2External.disptoggle)) + return; + + ReadMosaicData(&info, 0x8); + info.islinescroll = 0; + info.isverticalscroll = 0; + info.wctl = Vdp2Regs->WCTLB >> 8; + info.isbitmap = 0; + + GCDVdp2DrawScroll(&info, vdp2framebuffer, vdp2width, vdp2height); +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawRBG0(void) +{ + vdp2draw_struct info; + vdp2rotationparameterfp_struct parameter[2]; + + info.enable = Vdp2Regs->BGON & 0x10; + info.priority = rbg0priority; + if (!(info.enable & Vdp2External.disptoggle)) + return; + info.transparencyenable = !(Vdp2Regs->BGON & 0x1000); + info.specialprimode = (Vdp2Regs->SFPRMD >> 8) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLB & 0x7000) >> 12; + + // Figure out which Rotation Parameter we're using + switch (Vdp2Regs->RPMD & 0x3) + { + case 0: + // Parameter A + info.rotatenum = 0; + info.rotatemode = 0; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; + break; + case 1: + // Parameter B + info.rotatenum = 1; + info.rotatemode = 0; + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; + break; + case 2: + // Parameter A+B switched via coefficients + case 3: + // Parameter A+B switched via rotation parameter window + default: + info.rotatenum = 0; + info.rotatemode = 1 + (Vdp2Regs->RPMD & 0x1); + info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; + break; + } + + Vdp2ReadRotationTableFP(info.rotatenum, ¶meter[info.rotatenum]); + + if((info.isbitmap = Vdp2Regs->CHCTLB & 0x200) != 0) + { + // Bitmap Mode + ReadBitmapSize(&info, Vdp2Regs->CHCTLB >> 10, 0x1); + + if (info.rotatenum == 0) + // Parameter A + info.charaddr = (Vdp2Regs->MPOFR & 0x7) * 0x20000; + else + // Parameter B + info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; + + info.paladdr = (Vdp2Regs->BMPNB & 0x7) << 8; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + // Tile Mode + info.mapwh = 4; + + if (info.rotatenum == 0) + // Parameter A + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 8); + else + // Parameter B + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); + + ReadPatternData(&info, Vdp2Regs->PNCR, Vdp2Regs->CHCTLB & 0x100); + } + + if (Vdp2Regs->CCCTL & 0x10) + info.alpha = Vdp2Regs->CCRR & 0x1F; + + info.coloroffset = (Vdp2Regs->CRAOFB & 0x7) << 8; + + ReadVdp2ColorOffset(&info, 0x10, 0x10); + info.coordincx = info.coordincy = 1; + + ReadMosaicData(&info, 0x10); + info.islinescroll = 0; + info.isverticalscroll = 0; + info.wctl = Vdp2Regs->WCTLC; + + Vdp2DrawRotationFP(&info, parameter); +} + +////////////////////////////////////////////////////////////////////////////// + +int VIDGCDInit(void) +{ + // Initialize output buffer + if ((dispbuffer = (u32 *)calloc(sizeof(u32), 704 * 512)) == NULL) + return -1; + + // Initialize VDP1 framebuffer 1 + if ((vdp1framebuffer[0] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL) + return -1; + + // Initialize VDP1 framebuffer 2 + if ((vdp1framebuffer[1] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL) + return -1; + + // Initialize VDP2 framebuffer + if ((vdp2framebuffer = (u32 *)calloc(sizeof(u32), 704 * 512)) == NULL) + return -1; + + vdp1backframebuffer = vdp1framebuffer[0]; + vdp1frontframebuffer = vdp1framebuffer[1]; + vdp2width = 320; + vdp2height = 224; + +#ifdef USE_OPENGL + YuiSetVideoAttribute(DOUBLEBUFFER, 1); + + if (!YglScreenInit(8, 8, 8, 24)) + { + if (!YglScreenInit(4, 4, 4, 24)) + { + if (!YglScreenInit(5, 6, 5, 16)) + { + YuiErrorMsg("Couldn't set GL mode\n"); + return -1; + } + } + } + + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 320, 224, 0, 1, 0); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glOrtho(-320, 320, -224, 224, 1, 0); + outputwidth = 320; + outputheight = 224; + msglength = 0; +#endif + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDDeInit(void) +{ + if (dispbuffer) + { + free(dispbuffer); + dispbuffer = NULL; + } + + if (vdp1framebuffer[0]) + free(vdp1framebuffer[0]); + + if (vdp1framebuffer[1]) + free(vdp1framebuffer[1]); + + if (vdp2framebuffer) + free(vdp2framebuffer); +} + +////////////////////////////////////////////////////////////////////////////// + +static int IsFullscreen = 0; + +void VIDGCDResize(unsigned int w, unsigned int h, int on) +{ +#ifdef USE_OPENGL + IsFullscreen = on; + + if (on) + YuiSetVideoMode(w, h, 32, 1); + else + YuiSetVideoMode(w, h, 32, 0); + + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, h, 0, 1, 0); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glOrtho(-w, w, -h, h, 1, 0); + + glViewport(0, 0, w, h); + outputwidth = w; + outputheight = h; +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +int VIDGCDIsFullscreen(void) { + return IsFullscreen; +} + +////////////////////////////////////////////////////////////////////////////// + +int VIDGCDVdp1Reset(void) +{ + vdp1clipxstart = 0; + vdp1clipxend = 512; + vdp1clipystart = 0; + vdp1clipyend = 256; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1DrawStart(void) +{ + if (Vdp1Regs->TVMR & 0x1) + { + if (Vdp1Regs->TVMR & 0x2) + { + // Rotation 8-bit + vdp1width = 512; + vdp1height = 512; + } + else + { + // Normal 8-bit + vdp1width = 1024; + vdp1width = 256; + } + + vdp1pixelsize = 1; + } + else + { + // Rotation/Normal 16-bit + vdp1width = 512; + vdp1height = 256; + vdp1pixelsize = 2; + } + + VIDGCDVdp1EraseFrameBuffer(); +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1DrawEnd(void) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE u16 Vdp1ReadPattern16( u32 base, u32 offset ) { + + u16 dot = T1ReadByte(Vdp1Ram, ( base + (offset>>1)) & 0x7FFFF); + if ((offset & 0x1) == 0) dot >>= 4; // Even pixel + else dot &= 0xF; // Odd pixel + return dot; +} + +static INLINE u16 Vdp1ReadPattern64( u32 base, u32 offset ) { + + return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0x3F; +} + +static INLINE u16 Vdp1ReadPattern128( u32 base, u32 offset ) { + + return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0x7F; +} + +static INLINE u16 Vdp1ReadPattern256( u32 base, u32 offset ) { + + return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0xFF; +} + +static INLINE u16 Vdp1ReadPattern64k( u32 base, u32 offset ) { + + return T1ReadWord(Vdp1Ram, ( base + 2*offset) & 0x7FFFF); +} + +//////////////////////////////////////////////////////////////////////////////// + +static INLINE u32 alphablend16(u32 d, u32 s, u32 level) +{ + int r,g,b,sr,sg,sb,dr,dg,db; + + int invlevel = 256-level; + sr = s & 0x001f; dr = d & 0x001f; + r = (sr*level + dr*invlevel)>>8; r&= 0x1f; + sg = s & 0x03e0; dg = d & 0x03e0; + g = (sg*level + dg*invlevel)>>8; g&= 0x03e0; + sb = s & 0x7c00; db = d & 0x7c00; + b = (sb*level + db*invlevel)>>8; b&= 0x7c00; + return r|g|b; +} + +typedef struct _COLOR_PARAMS +{ + double r,g,b; +} COLOR_PARAMS; + +COLOR_PARAMS leftColumnColor; + +vdp1cmd_struct cmd; + +int currentPixel; +int currentPixelIsVisible; +int characterWidth; +int characterHeight; + +static int getpixel(int linenumber, int currentlineindex) { + + u32 characterAddress; + u32 colorlut; + u16 colorbank; + u8 SPD; + int endcode; + int endcodesEnabled; + int untexturedColor = 0; + int isTextured = 1; + int currentShape = cmd.CMDCTRL & 0x7; + int flip; + + characterAddress = cmd.CMDSRCA << 3; + colorbank = cmd.CMDCOLR; + colorlut = (u32)colorbank << 3; + SPD = ((cmd.CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent) + endcodesEnabled = (( cmd.CMDPMOD & 0x80) == 0 )?1:0; + flip = (cmd.CMDCTRL & 0x30) >> 4; + + //4 polygon, 5 polyline or 6 line + if(currentShape == 4 || currentShape == 5 || currentShape == 6) { + isTextured = 0; + untexturedColor = cmd.CMDCOLR; + } + + switch( flip ) { + case 1: + // Horizontal flipping + currentlineindex = characterWidth - currentlineindex-1; + break; + case 2: + // Vertical flipping + linenumber = characterHeight - linenumber-1; + + break; + case 3: + // Horizontal/Vertical flipping + linenumber = characterHeight - linenumber-1; + currentlineindex = characterWidth - currentlineindex-1; + break; + } + + switch ((cmd.CMDPMOD >> 3) & 0x7) + { + case 0x0: //4bpp bank + endcode = 0xf; + currentPixel = Vdp1ReadPattern16( characterAddress + (linenumber*(characterWidth>>1)), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + return 1; + if (!((currentPixel == 0) && !SPD)) + currentPixel = colorbank | currentPixel; + currentPixelIsVisible = 0xf; + break; + + case 0x1://4bpp lut + endcode = 0xf; + currentPixel = Vdp1ReadPattern16( characterAddress + (linenumber*(characterWidth>>1)), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + return 1; + if (!(currentPixel == 0 && !SPD)) + currentPixel = T1ReadWord(Vdp1Ram, (currentPixel * 2 + colorlut) & 0x7FFFF); + currentPixelIsVisible = 0xffff; + break; + case 0x2://8pp bank (64 color) + //is there a hardware bug with endcodes in this color mode? + //there are white lines around some characters in scud + //using an endcode of 63 eliminates the white lines + //but also causes some dropout due to endcodes being triggered that aren't triggered on hardware + //the closest thing i can do to match the hardware is make all pixels with color index 63 transparent + //this needs more hardware testing + + endcode = 63; + currentPixel = Vdp1ReadPattern64( characterAddress + (linenumber*(characterWidth)), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + currentPixel = 0; + // return 1; + if (!((currentPixel == 0) && !SPD)) + currentPixel = colorbank | currentPixel; + currentPixelIsVisible = 0x3f; + break; + case 0x3://128 color + endcode = 0xff; + currentPixel = Vdp1ReadPattern128( characterAddress + (linenumber*characterWidth), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + return 1; + if (!((currentPixel == 0) && !SPD)) + currentPixel = colorbank | currentPixel; + currentPixelIsVisible = 0x7f; + break; + case 0x4://256 color + endcode = 0xff; + currentPixel = Vdp1ReadPattern256( characterAddress + (linenumber*characterWidth), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + return 1; + currentPixelIsVisible = 0xff; + if (!((currentPixel == 0) && !SPD)) + currentPixel = colorbank | currentPixel; + break; + case 0x5://16bpp bank + endcode = 0x7fff; + currentPixel = Vdp1ReadPattern64k( characterAddress + (linenumber*characterWidth*2), currentlineindex ); + if(isTextured && endcodesEnabled && currentPixel == endcode) + return 1; + currentPixelIsVisible = 0xffff; + break; + } + + if(!isTextured) + currentPixel = untexturedColor; + + //force the MSB to be on if MSBON is set + currentPixel |= cmd.CMDPMOD & (1 << 15); + + return 0; +} + +static int gouraudAdjust( int color, int tableValue ) +{ + color += (tableValue - 0x10); + + if ( color < 0 ) color = 0; + if ( color > 0x1f ) color = 0x1f; + + return color; +} + +static void putpixel(int x, int y) { + + u16* iPix = &((u16 *)vdp1backframebuffer)[(y * vdp1width) + x]; + int mesh = cmd.CMDPMOD & 0x0100; + int SPD = ((cmd.CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent) + int currentShape = cmd.CMDCTRL & 0x7; + int isTextured=1; + + if(mesh && (x^y)&1) + return; + + if(currentShape == 4 || currentShape == 5 || currentShape == 6) + isTextured = 0; + + if (cmd.CMDPMOD & 0x0400) PushUserClipping((cmd.CMDPMOD >> 9) & 0x1); + + if (x >= vdp1clipxstart && + x < vdp1clipxend && + y >= vdp1clipystart && + y < vdp1clipyend) + {} + else + return; + + if (cmd.CMDPMOD & 0x0400) PopUserClipping(); + + + if ( SPD || (currentPixel & currentPixelIsVisible)) + { + switch( cmd.CMDPMOD & 0x7 )//we want bits 0,1,2 + { + case 0: // replace + if (!((currentPixel == 0) && !SPD)) + *(iPix) = currentPixel; + break; + case 1: // shadow, TODO + *(iPix) = currentPixel; + break; + case 2: // half luminance + *(iPix) = ((currentPixel & ~0x8421) >> 1) | (1 << 15); + break; + case 3: // half transparent + if ( *(iPix) & (1 << 15) )//only if MSB of framebuffer data is set + *(iPix) = alphablend16( *(iPix), currentPixel, (1 << 7) ) | (1 << 15); + else + *(iPix) = currentPixel; + break; + case 4: //gouraud + #define COLOR(r,g,b) (((r)&0x1F)|(((g)&0x1F)<<5)|(((b)&0x1F)<<10) |0x8000 ) + + //handle the special case demonstrated in the sgl chrome demo + //if we are in a paletted bank mode and the other two colors are unused, adjust the index value instead of rgb + if( + (((cmd.CMDPMOD >> 3) & 0x7) != 5) && + (((cmd.CMDPMOD >> 3) & 0x7) != 1) && + (int)leftColumnColor.g == 16 && + (int)leftColumnColor.b == 16) + { + int c = (int)(leftColumnColor.r-0x10); + if(c < 0) c = 0; + currentPixel = currentPixel+c; + *(iPix) = currentPixel; + break; + } + *(iPix) = COLOR( + gouraudAdjust( + currentPixel&0x001F, + (int)leftColumnColor.r), + + gouraudAdjust( + (currentPixel&0x03e0) >> 5, + (int)leftColumnColor.g), + + gouraudAdjust( + (currentPixel&0x7c00) >> 10, + (int)leftColumnColor.b) + ); + break; + default: + *(iPix) = alphablend16( COLOR((int)leftColumnColor.r,(int)leftColumnColor.g, (int)leftColumnColor.b), currentPixel, (1 << 7) ) | (1 << 15); + break; + } + + if(*iPix & 0x8000) { + vdp1draw_info.priosused[vdp1draw_info.prioritytable[0]] = 1; + } + else if(*iPix) { + u16 p = *iPix; + int s, prio, c; + + Vdp1ProcessSpritePixel(vdp1spritetype, &p, &s, &prio, &c); + vdp1draw_info.priosused[prio] = 1; + } + } +} + +//TODO consolidate the following 3 functions +static int bresenham( int x1, int y1, int x2, int y2, int x[], int y[]) +{ + int dx, dy, xf, yf, a, b, c, i; + + if (x2>x1) { + dx = x2-x1; + xf = 1; + } + else { + dx = x1-x2; + xf = -1; + } + + if (y2>y1) { + dy = y2-y1; + yf = 1; + } + else { + dy = y1-y2; + yf = -1; + } + + //burning rangers tries to draw huge shapes + //this will at least let it run + if(dx > 999 || dy > 999) + return INT_MAX; + + if (dx>dy) { + a = dy+dy; + c = a-dx; + b = c-dx; + for (i=0;i<=dx;i++) { + x[i] = x1; y[i] = y1; + x1 += xf; + if (c<0) { + c += a; + } + else { + c += b; + y1 += yf; + } + } + return dx+1; + } + else { + a = dx+dx; + c = a-dy; + b = c-dy; + for (i=0;i<=dy;i++) { + x[i] = x1; y[i] = y1; + y1 += yf; + if (c<0) { + c += a; + } + else { + c += b; + x1 += xf; + } + } + return dy+1; + } +} + +static int DrawLine( int x1, int y1, int x2, int y2, double linenumber, double texturestep, double xredstep, double xgreenstep, double xbluestep) +{ + int dx, dy, xf, yf, a, b, c, i; + int endcodesdetected=0; + int previousStep = 123456789; + + if (x2>x1) { + dx = x2-x1; + xf = 1; + } + else { + dx = x1-x2; + xf = -1; + } + + if (y2>y1) { + dy = y2-y1; + yf = 1; + } + else { + dy = y1-y2; + yf = -1; + } + + if (dx>dy) { + a = dy+dy; + c = a-dx; + b = c-dx; + for (i=0;i<=dx;i++) { + leftColumnColor.r+=xredstep; + leftColumnColor.g+=xgreenstep; + leftColumnColor.b+=xbluestep; + + if(getpixel(linenumber,(int)i*texturestep)) { + if(currentPixel != previousStep) { + previousStep = (int)i*texturestep; + endcodesdetected++; + } + } + else + putpixel(x1,y1); + + previousStep = currentPixel; + + if(endcodesdetected==2) + break; + + x1 += xf; + if (c<0) { + c += a; + } + else { + getpixel(linenumber,(int)i*texturestep); + putpixel(x1,y1); + c += b; + y1 += yf; +/* + //same as sega's way, but just move the code down here instead + //and use the pixel we already have instead of the next one + if(xf>1&&yf>1) putpixel(x1,y1-1); //case 1 + if(xf<1&&yf<1) putpixel(x1,y1+1); //case 2 + if(xf<1&&yf>1) putpixel(x1+1,y1); //case 7 + if(xf>1&&yf<1) putpixel(x1-1,y1); //case 8*/ + } + } + return dx+1; + } + else { + a = dx+dx; + c = a-dy; + b = c-dy; + for (i=0;i<=dy;i++) { + leftColumnColor.r+=xredstep; + leftColumnColor.g+=xgreenstep; + leftColumnColor.b+=xbluestep; + + if(getpixel(linenumber,(int)i*texturestep)) { + if(currentPixel != previousStep) { + previousStep = (int)i*texturestep; + endcodesdetected++; + } + } + else + putpixel(x1,y1); + + previousStep = currentPixel; + + if(endcodesdetected==2) + break; + + y1 += yf; + if (c<0) { + c += a; + } + else { + getpixel(linenumber,(int)i*texturestep); + putpixel(x1,y1); + c += b; + x1 += xf; +/* + if(xf>1&&yf>1) putpixel(x1,y1-1); //case 3 + if(xf<1&&yf<1) putpixel(x1,y1+1); //case 4 + if(xf<1&&yf>1) putpixel(x1+1,y1); //case 5 + if(xf>1&&yf<1) putpixel(x1-1,y1); //case 6*/ + + } + } + return dy+1; + } +} + +static int getlinelength(int x1, int y1, int x2, int y2) { + int dx, dy, xf, yf, a, b, c, i; + + if (x2>x1) { + dx = x2-x1; + xf = 1; + } + else { + dx = x1-x2; + xf = -1; + } + + if (y2>y1) { + dy = y2-y1; + yf = 1; + } + else { + dy = y1-y2; + yf = -1; + } + + if (dx>dy) { + a = dy+dy; + c = a-dx; + b = c-dx; + for (i=0;i<=dx;i++) { + + x1 += xf; + if (c<0) { + c += a; + } + else { + c += b; + y1 += yf; + } + } + return dx+1; + } + else { + a = dx+dx; + c = a-dy; + b = c-dy; + for (i=0;i<=dy;i++) { + y1 += yf; + if (c<0) { + c += a; + } + else { + c += b; + x1 += xf; + } + } + return dy+1; + } +} + +static INLINE double interpolate(double start, double end, int numberofsteps) { + + double stepvalue = 0; + + if(numberofsteps == 0) + return 1; + + stepvalue = (end - start) / numberofsteps; + + return stepvalue; +} + +typedef union _COLOR { // xbgr x555 + struct { +#ifdef WORDS_BIGENDIAN + u16 x:1; + u16 b:5; + u16 g:5; + u16 r:5; +#else + u16 r:5; + u16 g:5; + u16 b:5; + u16 x:1; +#endif + }; + u16 value; +} COLOR; + + +COLOR gouraudA; +COLOR gouraudB; +COLOR gouraudC; +COLOR gouraudD; + +static void gouraudTable(void) +{ + int gouraudTableAddress; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + gouraudTableAddress = (((unsigned int)cmd.CMDGRDA) << 3); + + gouraudA.value = T1ReadWord(Vdp1Ram,gouraudTableAddress); + gouraudB.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+2); + gouraudC.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+4); + gouraudD.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+6); +} + +int xleft[1000]; +int yleft[1000]; +int xright[1000]; +int yright[1000]; + +//a real vdp1 draws with arbitrary lines +//this is why endcodes are possible +//this is also the reason why half-transparent shading causes moire patterns +//and the reason why gouraud shading can be applied to a single line draw command +static void drawQuad(s32 tl_x, s32 tl_y, s32 bl_x, s32 bl_y, s32 tr_x, s32 tr_y, s32 br_x, s32 br_y){ + + int totalleft; + int totalright; + int total; + int i; + + COLOR_PARAMS topLeftToBottomLeftColorStep = {0,0,0}, topRightToBottomRightColorStep = {0,0,0}; + + //how quickly we step through the line arrays + double leftLineStep = 1; + double rightLineStep = 1; + + //a lookup table for the gouraud colors + COLOR colors[4]; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + characterWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + characterHeight = cmd.CMDSIZE & 0xFF; + + totalleft = bresenham(tl_x,tl_y,bl_x,bl_y,xleft,yleft); + totalright = bresenham(tr_x,tr_y,br_x,br_y,xright,yright); + + //just for now since burning rangers will freeze up trying to draw huge shapes + if(totalleft == INT_MAX || totalright == INT_MAX) + return; + + total = totalleft > totalright ? totalleft : totalright; + + + if(cmd.CMDPMOD & (1 << 2)) { + + gouraudTable(); + + { colors[0] = gouraudA; colors[1] = gouraudD; colors[2] = gouraudB; colors[3] = gouraudC; } + + topLeftToBottomLeftColorStep.r = interpolate(colors[0].r,colors[1].r,total); + topLeftToBottomLeftColorStep.g = interpolate(colors[0].g,colors[1].g,total); + topLeftToBottomLeftColorStep.b = interpolate(colors[0].b,colors[1].b,total); + + topRightToBottomRightColorStep.r = interpolate(colors[2].r,colors[3].r,total); + topRightToBottomRightColorStep.g = interpolate(colors[2].g,colors[3].g,total); + topRightToBottomRightColorStep.b = interpolate(colors[2].b,colors[3].b,total); + } + + //we have to step the equivalent of less than one pixel on the shorter side + //to make sure textures stretch properly and the shape is correct + if(total == totalleft && totalleft != totalright) { + //left side is larger + leftLineStep = 1; + rightLineStep = (double)totalright / totalleft; + } + else if(totalleft != totalright){ + //right side is larger + rightLineStep = 1; + leftLineStep = (double)totalleft / totalright; + } + + for(i = 0; i < total; i++) { + + int xlinelength; + + double xtexturestep; + double ytexturestep; + + COLOR_PARAMS rightColumnColor; + + COLOR_PARAMS leftToRightStep = {0,0,0}; + + //get the length of the line we are about to draw + xlinelength = getlinelength( + xleft[(int)(i*leftLineStep)], + yleft[(int)(i*leftLineStep)], + xright[(int)(i*rightLineStep)], + yright[(int)(i*rightLineStep)]); + + //so from 0 to the width of the texture / the length of the line is how far we need to step + xtexturestep=interpolate(0,characterWidth,xlinelength); + + //now we need to interpolate the y texture coordinate across multiple lines + ytexturestep=interpolate(0,characterHeight,total); + + //gouraud interpolation + if(cmd.CMDPMOD & (1 << 2)) { + + //for each new line we need to step once more through each column + //and add the orignal color + the number of steps taken times the step value to the bottom of the shape + //to get the current colors to use to interpolate across the line + + leftColumnColor.r = colors[0].r +(topLeftToBottomLeftColorStep.r*i); + leftColumnColor.g = colors[0].g +(topLeftToBottomLeftColorStep.g*i); + leftColumnColor.b = colors[0].b +(topLeftToBottomLeftColorStep.b*i); + + rightColumnColor.r = colors[2].r +(topRightToBottomRightColorStep.r*i); + rightColumnColor.g = colors[2].g +(topRightToBottomRightColorStep.g*i); + rightColumnColor.b = colors[2].b +(topRightToBottomRightColorStep.b*i); + + //interpolate colors across to get the right step values + leftToRightStep.r = interpolate(leftColumnColor.r,rightColumnColor.r,xlinelength); + leftToRightStep.g = interpolate(leftColumnColor.g,rightColumnColor.g,xlinelength); + leftToRightStep.b = interpolate(leftColumnColor.b,rightColumnColor.b,xlinelength); + } + + DrawLine( + xleft[(int)(i*leftLineStep)], + yleft[(int)(i*leftLineStep)], + xright[(int)(i*rightLineStep)], + yright[(int)(i*rightLineStep)], + ytexturestep*i, + xtexturestep, + leftToRightStep.r, + leftToRightStep.g, + leftToRightStep.b + ); + } +} + +void VIDGCDVdp1NormalSpriteDraw() { + + s16 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty; + int spriteWidth; + int spriteHeight; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + topLeftx = cmd.CMDXA + Vdp1Regs->localX; + topLefty = cmd.CMDYA + Vdp1Regs->localY; + spriteWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + spriteHeight = cmd.CMDSIZE & 0xFF; + + topRightx = topLeftx + (spriteWidth - 1); + topRighty = topLefty; + bottomRightx = topLeftx + (spriteWidth - 1); + bottomRighty = topLefty + (spriteHeight - 1); + bottomLeftx = topLeftx; + bottomLefty = topLefty + (spriteHeight - 1); + + drawQuad(topLeftx,topLefty,bottomLeftx,bottomLefty,topRightx,topRighty,bottomRightx,bottomRighty); +} + +void VIDGCDVdp1ScaledSpriteDraw(){ + + s32 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty; + int spriteWidth; + int spriteHeight; + int x0,y0,x1,y1; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + x0 = cmd.CMDXA + Vdp1Regs->localX; + y0 = cmd.CMDYA + Vdp1Regs->localY; + + switch ((cmd.CMDCTRL >> 8) & 0xF) + { + case 0x0: // Only two coordinates + default: + x1 = ((int)cmd.CMDXC) - x0 + Vdp1Regs->localX + 1; + y1 = ((int)cmd.CMDYC) - y0 + Vdp1Regs->localY + 1; + break; + case 0x5: // Upper-left + x1 = ((int)cmd.CMDXB) + 1; + y1 = ((int)cmd.CMDYB) + 1; + break; + case 0x6: // Upper-Center + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1/2; + x1++; + y1++; + break; + case 0x7: // Upper-Right + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1; + x1++; + y1++; + break; + case 0x9: // Center-left + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + y0 = y0 - y1/2; + x1++; + y1++; + break; + case 0xA: // Center-center + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1/2; + y0 = y0 - y1/2; + x1++; + y1++; + break; + case 0xB: // Center-right + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1; + y0 = y0 - y1/2; + x1++; + y1++; + break; + case 0xD: // Lower-left + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + y0 = y0 - y1; + x1++; + y1++; + break; + case 0xE: // Lower-center + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1/2; + y0 = y0 - y1; + x1++; + y1++; + break; + case 0xF: // Lower-right + x1 = ((int)cmd.CMDXB); + y1 = ((int)cmd.CMDYB); + x0 = x0 - x1; + y0 = y0 - y1; + x1++; + y1++; + break; + } + + spriteWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + spriteHeight = cmd.CMDSIZE & 0xFF; + + topLeftx = x0; + topLefty = y0; + + topRightx = x1 + x0 - 1; + topRighty = topLefty; + + bottomRightx = x1 + x0 - 1; + bottomRighty = y1 + y0 - 1; + + bottomLeftx = topLeftx; + bottomLefty = y1 + y0 - 1; + + drawQuad(topLeftx,topLefty,bottomLeftx,bottomLefty,topRightx,topRighty,bottomRightx,bottomRighty); +} + +void VIDGCDVdp1DistortedSpriteDraw() { + + s32 xa,ya,xb,yb,xc,yc,xd,yd; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + xa = (s32)(cmd.CMDXA + Vdp1Regs->localX); + ya = (s32)(cmd.CMDYA + Vdp1Regs->localY); + + xb = (s32)(cmd.CMDXB + Vdp1Regs->localX); + yb = (s32)(cmd.CMDYB + Vdp1Regs->localY); + + xc = (s32)(cmd.CMDXC + Vdp1Regs->localX); + yc = (s32)(cmd.CMDYC + Vdp1Regs->localY); + + xd = (s32)(cmd.CMDXD + Vdp1Regs->localX); + yd = (s32)(cmd.CMDYD + Vdp1Regs->localY); + + drawQuad(xa,ya,xd,yd,xb,yb,xc,yc); +} + +static void gouraudLineSetup(double * redstep, double * greenstep, double * bluestep, int length, COLOR table1, COLOR table2) { + + gouraudTable(); + + *redstep =interpolate(table1.r,table2.r,length); + *greenstep =interpolate(table1.g,table2.g,length); + *bluestep =interpolate(table1.b,table2.b,length); + + leftColumnColor.r = table1.r; + leftColumnColor.g = table1.g; + leftColumnColor.b = table1.b; +} + +void VIDGCDVdp1PolylineDraw(void) +{ + int X[4]; + int Y[4]; + double redstep = 0, greenstep = 0, bluestep = 0; + int length; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + X[0] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C)); + Y[0] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E)); + X[1] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10)); + Y[1] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12)); + X[2] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14)); + Y[2] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16)); + X[3] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18)); + Y[3] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A)); + + length = getlinelength(X[0], Y[0], X[1], Y[1]); + gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudA, gouraudB); + DrawLine(X[0], Y[0], X[1], Y[1], 0,0,redstep,greenstep,bluestep); + + length = getlinelength(X[1], Y[1], X[2], Y[2]); + gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudB, gouraudC); + DrawLine(X[1], Y[1], X[2], Y[2], 0,0,redstep,greenstep,bluestep); + + length = getlinelength(X[2], Y[2], X[3], Y[3]); + gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudD, gouraudC); + DrawLine(X[3], Y[3], X[2], Y[2], 0,0,redstep,greenstep,bluestep); + + length = getlinelength(X[3], Y[3], X[0], Y[0]); + gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudA,gouraudD); + DrawLine(X[0], Y[0], X[3], Y[3], 0,0,redstep,greenstep,bluestep); +} + +void VIDGCDVdp1LineDraw(void) +{ + int x1, y1, x2, y2; + double redstep = 0, greenstep = 0, bluestep = 0; + int length; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + x1 = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C)); + y1 = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E)); + x2 = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10)); + y2 = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12)); + + length = getlinelength(x1, y1, x2, y2); + gouraudLineSetup(&redstep,&bluestep,&greenstep,length, gouraudA, gouraudB); + DrawLine(x1, y1, x2, y2, 0,0,redstep,greenstep,bluestep); +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1UserClipping(void) +{ + Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); + Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); + Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); + +#if 0 + vdp1clipxstart = Vdp1Regs->userclipX1; + vdp1clipxend = Vdp1Regs->userclipX2; + vdp1clipystart = Vdp1Regs->userclipY1; + vdp1clipyend = Vdp1Regs->userclipY2; + + // This needs work + if (vdp1clipxstart > Vdp1Regs->systemclipX1) + vdp1clipxstart = Vdp1Regs->userclipX1; + else + vdp1clipxstart = Vdp1Regs->systemclipX1; + + if (vdp1clipxend < Vdp1Regs->systemclipX2) + vdp1clipxend = Vdp1Regs->userclipX2; + else + vdp1clipxend = Vdp1Regs->systemclipX2; + + if (vdp1clipystart > Vdp1Regs->systemclipY1) + vdp1clipystart = Vdp1Regs->userclipY1; + else + vdp1clipystart = Vdp1Regs->systemclipY1; + + if (vdp1clipyend < Vdp1Regs->systemclipY2) + vdp1clipyend = Vdp1Regs->userclipY2; + else + vdp1clipyend = Vdp1Regs->systemclipY2; +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +static void PushUserClipping(int mode) +{ + if (mode == 1) + { + VDP1LOG("User clipping mode 1 not implemented\n"); + return; + } + + vdp1clipxstart = Vdp1Regs->userclipX1; + vdp1clipxend = Vdp1Regs->userclipX2; + vdp1clipystart = Vdp1Regs->userclipY1; + vdp1clipyend = Vdp1Regs->userclipY2; + + // This needs work + if (vdp1clipxstart > Vdp1Regs->systemclipX1) + vdp1clipxstart = Vdp1Regs->userclipX1; + else + vdp1clipxstart = Vdp1Regs->systemclipX1; + + if (vdp1clipxend < Vdp1Regs->systemclipX2) + vdp1clipxend = Vdp1Regs->userclipX2; + else + vdp1clipxend = Vdp1Regs->systemclipX2; + + if (vdp1clipystart > Vdp1Regs->systemclipY1) + vdp1clipystart = Vdp1Regs->userclipY1; + else + vdp1clipystart = Vdp1Regs->systemclipY1; + + if (vdp1clipyend < Vdp1Regs->systemclipY2) + vdp1clipyend = Vdp1Regs->userclipY2; + else + vdp1clipyend = Vdp1Regs->systemclipY2; +} + +////////////////////////////////////////////////////////////////////////////// + +static void PopUserClipping(void) +{ + vdp1clipxstart = Vdp1Regs->systemclipX1; + vdp1clipxend = Vdp1Regs->systemclipX2; + vdp1clipystart = Vdp1Regs->systemclipY1; + vdp1clipyend = Vdp1Regs->systemclipY2; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1SystemClipping(void) +{ + Vdp1Regs->systemclipX1 = 0; + Vdp1Regs->systemclipY1 = 0; + Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); + + vdp1clipxstart = Vdp1Regs->systemclipX1; + vdp1clipxend = Vdp1Regs->systemclipX2; + vdp1clipystart = Vdp1Regs->systemclipY1; + vdp1clipyend = Vdp1Regs->systemclipY2; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1LocalCoordinate(void) +{ + Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); + Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); +} + +////////////////////////////////////////////////////////////////////////////// + +int VIDGCDVdp2Reset(void) +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp2DrawStart(void) +{ + int wctl; + Vdp2DrawBackScreen(); + + vdp1draw_info.prioritytable[0] = Vdp2Regs->PRISA & 0x7; + vdp1draw_info.prioritytable[1] = (Vdp2Regs->PRISA >> 8) & 0x7; + vdp1draw_info.prioritytable[2] = Vdp2Regs->PRISB & 0x7; + vdp1draw_info.prioritytable[3] = (Vdp2Regs->PRISB >> 8) & 0x7; + vdp1draw_info.prioritytable[4] = Vdp2Regs->PRISC & 0x7; + vdp1draw_info.prioritytable[5] = (Vdp2Regs->PRISC >> 8) & 0x7; + vdp1draw_info.prioritytable[6] = Vdp2Regs->PRISD & 0x7; + vdp1draw_info.prioritytable[7] = (Vdp2Regs->PRISD >> 8) & 0x7; + + vdp1draw_info.coloroffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + vdp1spritetype = Vdp2Regs->SPCTL & 0xF; + + if(Vdp2Regs->CLOFEN & 0x40) { + // color offset enable + if(Vdp2Regs->CLOFSL & 0x40) { + // color offset B + vdp1draw_info.info.cor = Vdp2Regs->COBR & 0xFF; + if(Vdp2Regs->COBR & 0x100) + vdp1draw_info.info.cor |= 0xFFFFFF00; + + vdp1draw_info.info.cog = Vdp2Regs->COBG & 0xFF; + if(Vdp2Regs->COBG & 0x100) + vdp1draw_info.info.cog |= 0xFFFFFF00; + + vdp1draw_info.info.cob = Vdp2Regs->COBB & 0xFF; + if(Vdp2Regs->COBB & 0x100) + vdp1draw_info.info.cob |= 0xFFFFFF00; + } + else { + // color offset A + vdp1draw_info.info.cor = Vdp2Regs->COAR & 0xFF; + if(Vdp2Regs->COAR & 0x100) + vdp1draw_info.info.cor |= 0xFFFFFF00; + + vdp1draw_info.info.cog = Vdp2Regs->COAG & 0xFF; + if(Vdp2Regs->COAG & 0x100) + vdp1draw_info.info.cog |= 0xFFFFFF00; + + vdp1draw_info.info.cob = Vdp2Regs->COAB & 0xFF; + if(Vdp2Regs->COAB & 0x100) + vdp1draw_info.info.cob |= 0xFFFFFF00; + } + + if(vdp1draw_info.info.cor == 0 && vdp1draw_info.info.cog == 0 && vdp1draw_info.info.cob == 0) { + if(Vdp2Regs->CCCTL & 0x40) + vdp1draw_info.info.PostPixelFetchCalc = &DoColorCalc; + else + vdp1draw_info.info.PostPixelFetchCalc = &DoNothing; + } + else { + if(Vdp2Regs->CCCTL & 0x40) + vdp1draw_info.info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + vdp1draw_info.info.PostPixelFetchCalc = &DoColorOffset; + } + } + else { // color offset disable + if(Vdp2Regs->CCCTL & 0x40) + vdp1draw_info.info.PostPixelFetchCalc = &DoColorCalc; + else + vdp1draw_info.info.PostPixelFetchCalc = &DoNothing; + } + + wctl = Vdp2Regs->WCTLC >> 8; + vdp1draw_info.clip[0].xstart = vdp1draw_info.clip[0].ystart = 0; + vdp1draw_info.clip[0].xend = vdp1draw_info.clip[0].yend = 0; + vdp1draw_info.clip[1].xstart = vdp1draw_info.clip[1].ystart = 0; + vdp1draw_info.clip[1].xend = vdp1draw_info.clip[1].yend = 0; + ReadWindowData(wctl, vdp1draw_info.clip); + vdp1draw_info.linewnd0addr = vdp1draw_info.linewnd1addr = 0; + ReadLineWindowData(&vdp1draw_info.islinewindow, wctl, &vdp1draw_info.linewnd0addr, &vdp1draw_info.linewnd1addr); +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp2DrawEnd(void) +{ +#ifndef CRAB_REWRITE + int i, i2; + u16 pixel; + u8 prioritytable[8]; + u32 vdp1coloroffset; + int colormode = Vdp2Regs->SPCTL & 0x20; + vdp2draw_struct info; + u32 *dst=dispbuffer; + u32 *vdp2src=vdp2framebuffer; + int islinewindow; + clipping_struct clip[2]; + u32 linewnd0addr, linewnd1addr; + int wctl; + + // Figure out whether to draw vdp1 framebuffer or vdp2 framebuffer pixels + // based on priority + if (Vdp1External.disptoggle) + { + prioritytable[0] = Vdp2Regs->PRISA & 0x7; + prioritytable[1] = (Vdp2Regs->PRISA >> 8) & 0x7; + prioritytable[2] = Vdp2Regs->PRISB & 0x7; + prioritytable[3] = (Vdp2Regs->PRISB >> 8) & 0x7; + prioritytable[4] = Vdp2Regs->PRISC & 0x7; + prioritytable[5] = (Vdp2Regs->PRISC >> 8) & 0x7; + prioritytable[6] = Vdp2Regs->PRISD & 0x7; + prioritytable[7] = (Vdp2Regs->PRISD >> 8) & 0x7; + + vdp1coloroffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + vdp1spritetype = Vdp2Regs->SPCTL & 0xF; + + if (Vdp2Regs->CLOFEN & 0x40) + { + // color offset enable + if (Vdp2Regs->CLOFSL & 0x40) + { + // color offset B + info.cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info.cob |= 0xFFFFFF00; + } + else + { + // color offset A + info.cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info.cob |= 0xFFFFFF00; + } + + if (info.cor == 0 && info.cog == 0 && info.cob == 0) + { + if (Vdp2Regs->CCCTL & 0x40) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + else + { + if (Vdp2Regs->CCCTL & 0x40) + info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info.PostPixelFetchCalc = &DoColorOffset; + } + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & 0x40) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + + wctl = Vdp2Regs->WCTLC >> 8; + clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0; + clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0; + ReadWindowData(wctl, clip); + linewnd0addr = linewnd1addr = 0; + ReadLineWindowData(&islinewindow, wctl, &linewnd0addr, &linewnd1addr); + + for (i2 = 0; i2 < vdp2height; i2++) + { + ReadLineWindowClip(islinewindow, clip, &linewnd0addr, &linewnd1addr); + + for (i = 0; i < vdp2width; i++) + { + // See if screen position is clipped, if it isn't, continue + // Window 0 + if (!TestWindow(wctl, 0x2, 0x1, &clip[0], i, i2)) + { + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + vdp2src++; + dst++; + continue; + } + + // Window 1 + if (!TestWindow(wctl, 0x8, 0x4, &clip[1], i, i2)) + { + vdp2src++; + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + dst++; + continue; + } + + if (vdp1pixelsize == 2) + { + // 16-bit pixel size + pixel = ((u16 *)vdp1frontframebuffer)[(i2 * vdp1width) + i]; + + if (pixel == 0) + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + else if (pixel & 0x8000 && colormode) + { + // 16 BPP + if (prioritytable[0] >= Vdp2GetPixelPriority(vdp2src[0])) + { + // if pixel is 0x8000, only draw pixel if sprite window + // is disabled/sprite type 2-7. sprite types 0 and 1 are + // -always- drawn and sprite types 8-F are always + // transparent. + if (pixel != 0x8000 || vdp1spritetype < 2 || (vdp1spritetype < 8 && !(Vdp2Regs->SPCTL & 0x10))) + dst[0] = info.PostPixelFetchCalc(&info, COLSAT2YAB16(0xFF, pixel)); + else + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + } + else + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + } + else + { + // Color bank + int priority; + int shadow; + int colorcalc; + priority = 0; // Avoid compiler warning + Vdp1ProcessSpritePixel(vdp1spritetype, &pixel, &shadow, &priority, &colorcalc); + if (prioritytable[priority] >= Vdp2GetPixelPriority(vdp2src[0])) + dst[0] = info.PostPixelFetchCalc(&info, COLSAT2YAB32(0xFF, Vdp2ColorRamGetColor(vdp1coloroffset + pixel))); + else + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + } + } + else + { + // 8-bit pixel size + pixel = vdp1frontframebuffer[(i2 * vdp1width) + i]; + + if (pixel == 0) + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + else + { + // Color bank(fix me) + LOG("8-bit Color Bank draw - %02X\n", pixel); + dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + } + } + vdp2src++; + dst++; + } + } + } + else + { + // Render VDP2 only + for (i = 0; i < (vdp2width*vdp2height); i++) + dispbuffer[i] = COLSATSTRIPPRIORITY(vdp2framebuffer[i]); + } +#endif + + VIDGCDVdp1SwapFrameBuffer(); + + if (OSDUseBuffer()) + OSDDisplayMessages(dispbuffer, vdp2width, vdp2height); + +#ifdef USE_OPENGL + glRasterPos2i(0, 0); + glPixelZoom((float)outputwidth / (float)vdp2width, 0 - ((float)outputheight / (float)vdp2height)); +#ifndef CRAB_REWRITE + glDrawPixels(vdp2width, vdp2height, GL_RGBA, GL_UNSIGNED_BYTE, dispbuffer); +#else + glDisable(GL_BLEND); + glDrawPixels(vdp2width, vdp2height, GL_RGBA, GL_UNSIGNED_BYTE, vdp2framebuffer); +#endif + + if (! OSDUseBuffer()) + OSDDisplayMessages(NULL, -1, -1); +#endif + + YuiSwapBuffers(); +} + +/* FIXME: Support Vdp1 8-bit mode sometime... */ +static void Vdp1DrawPriority(int prio) { + int colormode = Vdp2Regs->SPCTL & 0x20; + vdp2draw_struct *info = &vdp1draw_info.info; + int islinewindow = vdp1draw_info.islinewindow; + int wctl= Vdp2Regs->WCTLC >> 8; + + // Figure out whether to draw vdp1 framebuffer or vdp2 framebuffer pixels + // based on priority + if(Vdp1External.disptoggle) { + dispatch_apply(vdp2height, dispatch_get_global_queue(2, 0), ^(size_t i2) { + u16 *fb16 = (u16 *)(vdp1frontframebuffer) + (i2 * vdp1width); + u32 *fb = vdp2framebuffer + (i2 * vdp2width); + int i; + u16 pixel; + u32 linewnd0addr = vdp1draw_info.linewnd0addr; + u32 linewnd1addr = vdp1draw_info.linewnd1addr; + + ReadLineWindowClip(islinewindow, vdp1draw_info.clip, &linewnd0addr, &linewnd1addr); + + for(i = 0; i < vdp2width; ++i, ++fb16, ++fb) { + // See if screen position is clipped, if it isn't, continue + // Window 0 + if(!TestWindow(wctl, 0x2, 0x1, &vdp1draw_info.clip[0], i, i2)) { + continue; + } + + // Window 1 + if(!TestWindow(wctl, 0x8, 0x4, &vdp1draw_info.clip[1], i, i2)) { + continue; + } + + if (vdp1pixelsize == 2) { + // 16-bit pixel size + pixel = *fb16; + + if(pixel & 0x8000 && colormode && vdp1draw_info.prioritytable[0] == prio && + prio >= Vdp2GetPixelPriority(*fb)) { + // 16 BPP + // if pixel is 0x8000, only draw pixel if sprite window + // is disabled/sprite type 2-7. sprite types 0 and 1 are + // -always- drawn and sprite types 8-F are always + // transparent. + if(pixel != 0x8000 || vdp1spritetype < 2 || (vdp1spritetype < 8 && !(Vdp2Regs->SPCTL & 0x10))) + *fb = info->PostPixelFetchCalc(info, COLSAT2YAB16(0xFF, pixel)); + } + else if(pixel) { + // Color bank + int priority; + int shadow; + int colorcalc; + priority = 0; // Avoid compiler warning + Vdp1ProcessSpritePixel(vdp1spritetype, &pixel, &shadow, &priority, &colorcalc); + priority = vdp1draw_info.prioritytable[priority]; + if(priority == prio && priority >= Vdp2GetPixelPriority(*fb)) + *fb = info->PostPixelFetchCalc(info, COLSAT2YAB32(0xFF, Vdp2ColorRamGetColor(vdp1draw_info.coloroffset + pixel))); + } + } + //else + //{ + // // 8-bit pixel size + // pixel = vdp1frontframebuffer[(i2 * vdp1width) + i]; + // + // if (pixel == 0) + // dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + // else + // { + // // Color bank(fix me) + // LOG("8-bit Color Bank draw - %02X\n", pixel); + // dst[0] = COLSATSTRIPPRIORITY(vdp2src[0]); + // } + //} + } + }); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp2DrawScreens(void) +{ + int i; + + /* Draw all the screens, from the lowest priority one forward. */ + for(i = 1; i < 8; ++i) + { + if (nbg3priority == i) + Vdp2DrawNBG3(); + if (nbg2priority == i) + Vdp2DrawNBG2(); + if (nbg1priority == i) + Vdp2DrawNBG1(); + if (nbg0priority == i) + Vdp2DrawNBG0(); + if (rbg0priority == i) + Vdp2DrawRBG0(); + + /* Draw anything in VDP1 that should be shown at this priority level. */ + if(vdp1draw_info.priosused[i]) + Vdp1DrawPriority(i); + vdp1draw_info.priosused[i] = 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp2SetResolution(u16 TVMD) +{ + // This needs some work + + // Horizontal Resolution + switch (TVMD & 0x7) + { + case 0: + vdp2width = 320; + resxratio=1; + break; + case 1: + vdp2width = 352; + resxratio=1; + break; + case 2: // 640 + vdp2width = 320; + resxratio=2; + break; + case 3: // 704 + vdp2width = 352; + resxratio=2; + break; + case 4: + vdp2width = 320; + resxratio=1; + break; + case 5: + vdp2width = 352; + resxratio=1; + break; + case 6: // 640 + vdp2width = 320; + resxratio=2; + break; + case 7: // 704 + vdp2width = 352; + resxratio=2; + break; + } + + // Vertical Resolution + switch ((TVMD >> 4) & 0x3) + { + case 0: + vdp2height = 224; + break; + case 1: + vdp2height = 240; + break; + case 2: + vdp2height = 256; + break; + default: break; + } + resyratio=1; + + // Check for interlace + switch ((TVMD >> 6) & 0x3) + { + case 3: // Double-density Interlace +// vdp2height *= 2; + resyratio=2; + break; + case 2: // Single-density Interlace + case 0: // Non-interlace + default: break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL VIDGCDVdp2SetPriorityNBG0(int priority) +{ + nbg0priority = priority; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL VIDGCDVdp2SetPriorityNBG1(int priority) +{ + nbg1priority = priority; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL VIDGCDVdp2SetPriorityNBG2(int priority) +{ + nbg2priority = priority; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL VIDGCDVdp2SetPriorityNBG3(int priority) +{ + nbg3priority = priority; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL VIDGCDVdp2SetPriorityRBG0(int priority) +{ + rbg0priority = priority; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDOnScreenDebugMessage(char *string, ...) +{ + va_list arglist; + + va_start(arglist, string); + vsprintf(message, string, arglist); + va_end(arglist); + msglength = strlen(message); +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDGetScreenSize(int *width, int *height) +{ + *width = vdp2width; + *height = vdp2height; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1SwapFrameBuffer(void) +{ + u8 *temp = vdp1frontframebuffer; + vdp1frontframebuffer = vdp1backframebuffer; + vdp1backframebuffer = temp; +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDVdp1EraseFrameBuffer(void) +{ + int s, s2; + int w,h; + + h = (Vdp1Regs->EWRR & 0x1FF) + 1; + if (h > vdp1height) h = vdp1height; + w = ((Vdp1Regs->EWRR >> 6) & 0x3F8) + 8; + if (w > vdp1width) w = vdp1width; + + s = Vdp1Regs->EWLR & 0x1FF; + s2 = (Vdp1Regs->EWLR >> 6) & 0x1F8; + + dispatch_apply(h - s, dispatch_get_global_queue(2, 0), ^(size_t i2) { + int i; + i2 += s; + for (i = s2; i < w; i++) { + ((u16 *)vdp1backframebuffer)[(i2 * vdp1width) + i] = Vdp1Regs->EWDR; + } + }); +} + +////////////////////////////////////////////////////////////////////////////// + +void VIDGCDGetGlSize(int *width, int *height) +{ +#ifdef USE_OPENGL + *width = outputwidth; + *height = outputheight; +#else + *width = vdp2width; + *height = vdp2height; +#endif +} diff --git a/yabause/src/cocoa/vidgcd.h b/yabause/src/cocoa/vidgcd.h new file mode 100644 index 0000000000..99ada32a99 --- /dev/null +++ b/yabause/src/cocoa/vidgcd.h @@ -0,0 +1,29 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef VIDGCD_H +#define VIDGCD_H + +#include "vdp1.h" + +#define VIDCORE_GCD 10 + +extern VideoInterface_struct VIDGCD; + +#endif diff --git a/yabause/src/coffelf.c b/yabause/src/coffelf.c new file mode 100644 index 0000000000..54e53c1f3e --- /dev/null +++ b/yabause/src/coffelf.c @@ -0,0 +1,388 @@ +/* Copyright 2007 Theo Berkau + Copyright 2009 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "core.h" +#include "debug.h" +#include "sh2core.h" +#include "yabause.h" +#include "coffelf.h" + +typedef struct +{ + u8 magic[2]; + u16 numsections; + u32 timedate; + u32 symtabptr; + u32 numsymtabs; + u16 optheader; + u16 flags; +} coff_header_struct; + +typedef struct +{ + u8 magic[2]; + u16 versionstamp; + u32 textsize; + u32 datasize; + u32 bsssize; + u32 entrypoint; + u32 textaddr; + u32 dataaddr; +} aout_header_struct; + +typedef struct +{ + s8 name[8]; + u32 physaddr; + u32 virtaddr; + u32 sectionsize; + u32 sectionptr; + u32 relptr; + u32 linenoptr; + u16 numreloc; + u16 numlineno; + u32 flags; +} section_header_struct; + +typedef struct +{ + u8 ident[16]; + u16 type; + u16 machine; + u32 version; + u32 entry; + u32 phdr; + u32 shdr; + u32 flags; + u16 hdrsize; + u16 phdrsize; + u16 phdrcount; + u16 shdrsize; + u16 shdrcount; + u16 shdrstridx; +} elf_header_struct; + +#define ELF_MACHINE_SH 42 + +typedef struct +{ + u32 name; + u32 type; + u32 flags; + u32 addr; + u32 offs; + u32 size; + u32 link; + u32 inf; + u32 align; + u32 esize; +} elf_section_header_struct; + +#define ELF_SECTION_TYPE_NODATA 8 +#define ELF_SECTION_FLAG_ALLOC 2 + +#define WordSwap(x) x = ((x & 0xFF00) >> 8) + ((x & 0x00FF) << 8); +#define DoubleWordSwap(x) x = (((x & 0xFF000000) >> 24) + \ + ((x & 0x00FF0000) >> 8) + \ + ((x & 0x0000FF00) << 8) + \ + ((x & 0x000000FF) << 24)); + +////////////////////////////////////////////////////////////////////////////// + +int MappedMemoryLoadCoff(const char *filename) +{ + coff_header_struct coff_header; + aout_header_struct aout_header; + section_header_struct *section_headers=NULL; + FILE *fp; + u8 *buffer; + u32 i, j; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + fread((void *)&coff_header, sizeof(coff_header), 1, fp); +#ifndef WORDS_BIGENDIAN + WordSwap(coff_header.numsections); + DoubleWordSwap(coff_header.timedate); + DoubleWordSwap(coff_header.timedate); + DoubleWordSwap(coff_header.symtabptr); + DoubleWordSwap(coff_header.numsymtabs); + WordSwap(coff_header.optheader); + WordSwap(coff_header.flags); +#endif + + if (coff_header.magic[0] != 0x05 || coff_header.magic[1] != 0x00 || + coff_header.optheader != sizeof(aout_header)) + { + // Not SH COFF or is missing the optional header + fclose(fp); + return -1; + } + + fread((void *)&aout_header, sizeof(aout_header), 1, fp); +#ifndef WORDS_BIGENDIAN + WordSwap(aout_header.versionstamp); + DoubleWordSwap(aout_header.textsize); + DoubleWordSwap(aout_header.datasize); + DoubleWordSwap(aout_header.bsssize); + DoubleWordSwap(aout_header.entrypoint); + DoubleWordSwap(aout_header.textaddr); + DoubleWordSwap(aout_header.dataaddr); +#endif + + // Read in each section header + if ((section_headers = (section_header_struct *)malloc(sizeof(section_header_struct) * coff_header.numsections)) == NULL) + { + fclose(fp); + return -2; + } + + // read in section headers + for (i = 0; i < coff_header.numsections; i++) + { + fread((void *)§ion_headers[i], sizeof(section_header_struct), 1, fp); +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(section_headers[i].physaddr); + DoubleWordSwap(section_headers[i].virtaddr); + DoubleWordSwap(section_headers[i].sectionsize); + DoubleWordSwap(section_headers[i].sectionptr); + DoubleWordSwap(section_headers[i].relptr); + DoubleWordSwap(section_headers[i].linenoptr); + WordSwap(section_headers[i].numreloc); + WordSwap(section_headers[i].numlineno); + DoubleWordSwap(section_headers[i].flags); +#endif + } + + YabauseResetNoLoad(); + + // Setup the vector table area, etc. + YabauseSpeedySetup(); + + // Read in sections, load them to ram + for (i = 0; i < coff_header.numsections; i++) + { + if (section_headers[i].sectionsize == 0 || + section_headers[i].sectionptr == 0) + // Skip to the next section + continue; + + if ((buffer = (u8 *)malloc(section_headers[i].sectionsize)) == NULL) + { + fclose(fp); + free(section_headers); + return -2; + } + + fseek(fp, section_headers[i].sectionptr, SEEK_SET); + fread((void *)buffer, 1, section_headers[i].sectionsize, fp); + + for (j = 0; j < section_headers[i].sectionsize; j++) + MappedMemoryWriteByte(section_headers[i].physaddr+j, buffer[j]); + SH2WriteNotify(section_headers[i].physaddr, + section_headers[i].sectionsize); + + free(buffer); + } + + // Clean up + free(section_headers); + fclose(fp); + + SH2GetRegisters(MSH2, &MSH2->regs); + MSH2->regs.PC = aout_header.entrypoint; + SH2SetRegisters(MSH2, &MSH2->regs); + return 0; +} + + +////////////////////////////////////////////////////////////////////////////// + +int MappedMemoryLoadElf(const char *filename) +{ + elf_header_struct elf_hdr; + elf_section_header_struct *sections = NULL; + FILE *fp; + u16 i; + u32 j; + u8 *buffer; + + fp = fopen(filename, "rb"); + + if(fp == NULL) + return -1; + + fread(&elf_hdr, sizeof(elf_header_struct), 1, fp); + + if(elf_hdr.ident[0] != 0x7F || elf_hdr.ident[1] != 'E' || + elf_hdr.ident[2] != 'L' || elf_hdr.ident[3] != 'F' || + elf_hdr.ident[4] != 1) + { + /* Doesn't appear to be a valid ELF file. */ + fclose(fp); + return -1; + } + + if(elf_hdr.ident[5] != 2) + { + /* Doesn't appear to be a big-endian file. */ + fclose(fp); + return -1; + } + +#ifndef WORDS_BIGENDIAN + WordSwap(elf_hdr.type); + WordSwap(elf_hdr.machine); + DoubleWordSwap(elf_hdr.version); + DoubleWordSwap(elf_hdr.entry); + DoubleWordSwap(elf_hdr.phdr); + DoubleWordSwap(elf_hdr.shdr); + DoubleWordSwap(elf_hdr.flags); + WordSwap(elf_hdr.hdrsize); + WordSwap(elf_hdr.phdrsize); + WordSwap(elf_hdr.phdrcount); + WordSwap(elf_hdr.shdrsize); + WordSwap(elf_hdr.shdrcount); + WordSwap(elf_hdr.shdrstridx); +#endif + + LOG("Loading ELF file %s\n", filename); + LOG("Type: %d\n", elf_hdr.type); + LOG("Machine code: %d\n", elf_hdr.machine); + LOG("Version: %d\n", elf_hdr.version); + LOG("Entry point: 0x%08X\n", elf_hdr.entry); + LOG("Program header offset: %d\n", elf_hdr.phdr); + LOG("Section header offset: %d\n", elf_hdr.shdr); + LOG("Flags: %d\n", elf_hdr.flags); + LOG("ELF Header Size: %d\n", elf_hdr.hdrsize); + LOG("Program header size: %d\n", elf_hdr.phdrsize); + LOG("Program header count: %d\n", elf_hdr.phdrcount); + LOG("Section header size: %d\n", elf_hdr.shdrsize); + LOG("Section header count: %d\n", elf_hdr.shdrcount); + LOG("String table section: %d\n", elf_hdr.shdrstridx); + + if(elf_hdr.machine != ELF_MACHINE_SH) + { + /* Not a SuperH ELF file. */ + fclose(fp); + return -1; + } + + /* Allocate space for the section headers. */ + sections = + (elf_section_header_struct *)malloc(sizeof(elf_section_header_struct) * + elf_hdr.shdrcount); + if(sections == NULL) + { + fclose(fp); + return -2; + } + + /* Look at the actual section headers. */ + fseek(fp, elf_hdr.shdr, SEEK_SET); + + /* Read in each section header. */ + for(i = 0; i < elf_hdr.shdrcount; ++i) + { + fread(sections + i, sizeof(elf_section_header_struct), 1, fp); +#ifndef WORDS_BIGENDIAN + DoubleWordSwap(sections[i].name); + DoubleWordSwap(sections[i].type); + DoubleWordSwap(sections[i].flags); + DoubleWordSwap(sections[i].addr); + DoubleWordSwap(sections[i].offs); + DoubleWordSwap(sections[i].size); + DoubleWordSwap(sections[i].link); + DoubleWordSwap(sections[i].inf); + DoubleWordSwap(sections[i].align); + DoubleWordSwap(sections[i].esize); +#endif + + LOG("Section header %d:\n", i); + LOG("Name index: %d\n", sections[i].name); + LOG("Type: %d\n", sections[i].type); + LOG("Flags: 0x%X\n", sections[i].flags); + LOG("In-memory address: 0x%08X\n", sections[i].addr); + LOG("In-file offset: %d\n", sections[i].offs); + LOG("Size: %d\n", sections[i].size); + LOG("Link field: %d\n", sections[i].link); + LOG("Info field: %d\n", sections[i].inf); + LOG("Alignment: %d\n", sections[i].align); + LOG("Entry size: %d\n", sections[i].esize); + } + + YabauseResetNoLoad(); + + /* Set up the vector table area, etc. */ + YabauseSpeedySetup(); + + /* Read in the sections and load them to RAM. */ + for(i = 0; i < elf_hdr.shdrcount; ++i) + { + /* Does the header request actual storage for this section? */ + if(sections[i].flags & ELF_SECTION_FLAG_ALLOC) + { + /* Check if the section contains data, or if its just a marker for a + section of zero bytes. */ + if(sections[i].type == ELF_SECTION_TYPE_NODATA) + { + for(j = 0; j < sections[i].size; ++j) + { + MappedMemoryWriteByte(sections[i].addr + j, 0); + } + } + else + { + buffer = (u8 *)malloc(sections[i].size); + + if(buffer == NULL) + { + fclose(fp); + free(sections); + return -2; + } + + fseek(fp, sections[i].offs, SEEK_SET); + fread(buffer, 1, sections[i].size, fp); + + for(j = 0; j < sections[i].size; ++j) + { + MappedMemoryWriteByte(sections[i].addr + j, buffer[j]); + } + + free(buffer); + } + } + } + + /* Clean up. */ + free(sections); + fclose(fp); + + /* Set up our entry point. */ + SH2GetRegisters(MSH2, &MSH2->regs); + MSH2->regs.PC = elf_hdr.entry; + SH2SetRegisters(MSH2, &MSH2->regs); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/coffelf.h b/yabause/src/coffelf.h new file mode 100644 index 0000000000..8085982ba9 --- /dev/null +++ b/yabause/src/coffelf.h @@ -0,0 +1,27 @@ +/* Copyright 2007 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef COFFELF_H +#define COFFELF_H + +int MappedMemoryLoadCoff(const char *filename); +int MappedMemoryLoadElf(const char *filename); + +#endif + diff --git a/yabause/src/config.h.in b/yabause/src/config.h.in new file mode 100644 index 0000000000..afea5ec0a2 --- /dev/null +++ b/yabause/src/config.h.in @@ -0,0 +1,3 @@ +#cmakedefine HAVE_C68K 1 +#cmakedefine HAVE_Q68 1 +#cmakedefine SH2_DYNAREC 1 diff --git a/yabause/src/core.h b/yabause/src/core.h new file mode 100644 index 0000000000..66ef16f380 --- /dev/null +++ b/yabause/src/core.h @@ -0,0 +1,295 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CORE_H +#define CORE_H + +#include +#include + +#ifndef ALIGNED +#ifdef _MSC_VER +#define ALIGNED(x) __declspec(align(x)) +#else +#define ALIGNED(x) __attribute__((aligned(x))) +#endif +#endif + +#ifndef STDCALL +#ifdef _MSC_VER +#define STDCALL __stdcall +#else +#define STDCALL +#endif +#endif + +#ifndef FASTCALL +#ifdef __MINGW32__ +#define FASTCALL __attribute__((fastcall)) +#elif defined (__i386__) +#define FASTCALL __attribute__((regparm(3))) +#else +#define FASTCALL +#endif +#endif + +/* When building multiple arches on OS X you must use the compiler- + provided endian flags instead of the one provided by autoconf */ +#if defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__) + #undef WORDS_BIGENDIAN + #ifdef __BIG_ENDIAN__ + #define WORDS_BIGENDIAN + #endif +#endif + + +#ifndef INLINE +#ifdef _MSC_VER +#define INLINE _inline +#else +#define INLINE inline +#endif +#endif + +#ifdef GEKKO +/* Wii have both stdint.h and "yabause" definitions of fixed +size types */ +#include +typedef unsigned long pointer; + +#else /* ! GEKKO */ + +#ifdef HAVE_STDINT_H + +#include +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +typedef uintptr_t pointer; + +#else // !HAVE_STDINT_H + +typedef unsigned char u8; +typedef unsigned short u16; + +typedef signed char s8; +typedef signed short s16; + +#if defined(__LP64__) +// Generic 64-bit +typedef unsigned int u32; +typedef unsigned long u64; +typedef unsigned long pointer; + +typedef signed int s32; +typedef signed long s64; + +#elif defined(_MSC_VER) +typedef unsigned long u32; +typedef unsigned __int64 u64; +typedef unsigned long long u64; +#ifdef _WIN64 +typedef __int64 pointer; +#else +typedef unsigned long pointer; +#endif + +typedef signed long s32; +typedef __int64 s64; +typedef signed long long s64; + +#else +// 32-bit Linux GCC/MINGW/etc. +typedef unsigned long u32; +typedef unsigned long long u64; +typedef unsigned long pointer; + +typedef signed long s32; +typedef signed long long s64; +#endif + +#endif // !HAVE_STDINT_H + +#endif // !GEKKO + +typedef struct { + unsigned int size; + unsigned int done; +} IOCheck_struct; + +static INLINE void ywrite(IOCheck_struct * check, void * ptr, size_t size, size_t nmemb, FILE * stream) { + check->done += (unsigned int)fwrite(ptr, size, nmemb, stream); + check->size += (unsigned int)nmemb; +} + +static INLINE void yread(IOCheck_struct * check, void * ptr, size_t size, size_t nmemb, FILE * stream) { + check->done += (unsigned int)fread(ptr, size, nmemb, stream); + check->size += (unsigned int)nmemb; +} + +static INLINE int StateWriteHeader(FILE *fp, const char *name, int version) { + IOCheck_struct check; + fprintf(fp, "%s", name); + check.done = 0; + check.size = 0; + ywrite(&check, (void *)&version, sizeof(version), 1, fp); + ywrite(&check, (void *)&version, sizeof(version), 1, fp); // place holder for size + return (check.done == check.size) ? ftell(fp) : -1; +} + +static INLINE int StateFinishHeader(FILE *fp, int offset) { + IOCheck_struct check; + int size = 0; + size = ftell(fp) - offset; + fseek(fp, offset - 4, SEEK_SET); + check.done = 0; + check.size = 0; + ywrite(&check, (void *)&size, sizeof(size), 1, fp); // write true size + fseek(fp, 0, SEEK_END); + return (check.done == check.size) ? (size + 12) : -1; +} + +static INLINE int StateCheckRetrieveHeader(FILE *fp, const char *name, int *version, int *size) { + char id[4]; + size_t ret; + + if ((ret = fread((void *)id, 1, 4, fp)) != 4) + return -1; + + if (strncmp(name, id, 4) != 0) + return -2; + + if ((ret = fread((void *)version, 4, 1, fp)) != 1) + return -1; + + if (fread((void *)size, 4, 1, fp) != 1) + return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +// Terrible, but I'm not sure how to do the equivalent in inline +#ifdef HAVE_C99_VARIADIC_MACROS +#define AddString(s, ...) \ + { \ + sprintf(s, __VA_ARGS__); \ + s += strlen(s); \ + } +#else +#define AddString(s, r...) \ + { \ + sprintf(s, ## r); \ + s += strlen(s); \ + } +#endif + +////////////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_LIBMINI18N +#include "mini18n.h" +#else +#ifndef _ +#define _(a) (a) +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// + +/* Minimum/maximum values */ + +#undef MIN +#undef MAX +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +////////////////////////////////////////////////////////////////////////////// + +/* + * BSWAP16(x) swaps two bytes in a 16-bit value (AABB -> BBAA) or adjacent + * bytes in a 32-bit value (AABBCCDD -> BBAADDCC). + * + * BSWAP32(x) reverses four bytes in a 32-bit value (AABBCCDD -> DDCCBBAA). + * + * WSWAP32(x) swaps two 16-bit words in a 32-bit value (AABBCCDD -> CCDDAABB). + * + * Any of these can be left undefined if there is no platform-specific + * optimization for them; the defaults below will then be used instead. + */ + +#ifdef PSP +# define BSWAP16(x) ((typeof(x)) __builtin_allegrex_wsbh((x))) +# define BSWAP32(x) ((typeof(x)) __builtin_allegrex_wsbw((x))) +# define WSWAP32(x) ((typeof(x)) __builtin_allegrex_rotr((x), 16)) +#endif + +/* Defaults: */ + +#ifndef BSWAP16 +# define BSWAP16(x) (((u32)(x)>>8 & 0x00FF00FF) | ((u32)(x) & 0x00FF00FF) << 8) +#endif +#ifndef BSWAP32 +# define BSWAP32(x) ((u32)(x)>>24 | ((u32)(x)>>8 & 0xFF00) | ((u32)(x) & 0xFF00)<<8 | (u32)(x)<<24) +#endif +#ifndef WSWAP32 +# define WSWAP32(x) ((u32)(x)>>16 | (u32)(x)<<16) +#endif + +////////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUC__ + +#define UNUSED __attribute ((unused)) + +#ifdef DEBUG +#define USED_IF_DEBUG +#else +#define USED_IF_DEBUG __attribute ((unused)) +#endif + +#ifdef SMPC_DEBUG +#define USED_IF_SMPC_DEBUG +#else +#define USED_IF_SMPC_DEBUG __attribute ((unused)) +#endif + +/* LIKELY(x) indicates that x is likely to be true (nonzero); + * UNLIKELY(x) indicates that x is likely to be false (zero). + * Use like: "if (UNLIKELY(a < b)) {...}" */ +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#define UNLIKELY(x) (__builtin_expect(!!(x), 0)) + +#else + +#define UNUSED +#define USED_IF_DEBUG +#define USED_IF_SMPC_DEBUG +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) + +#endif + +#endif diff --git a/yabause/src/cs0.c b/yabause/src/cs0.c new file mode 100644 index 0000000000..2f6ad4d365 --- /dev/null +++ b/yabause/src/cs0.c @@ -0,0 +1,1504 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2006 Ex-Cyber + Copyright 2005 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "cs0.h" +#include "error.h" + +cartridge_struct *CartridgeArea; + +////////////////////////////////////////////////////////////////////////////// +// Dummy/No Cart Functions +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL DummyCs0ReadByte(UNUSED u32 addr) +{ + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL DummyCs0ReadWord(UNUSED u32 addr) +{ + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL DummyCs0ReadLong(UNUSED u32 addr) +{ + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs0WriteByte(UNUSED u32 addr, UNUSED u8 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs0WriteWord(UNUSED u32 addr, UNUSED u16 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs0WriteLong(UNUSED u32 addr, UNUSED u32 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL DummyCs1ReadByte(UNUSED u32 addr) +{ + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL DummyCs1ReadWord(UNUSED u32 addr) +{ + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL DummyCs1ReadLong(UNUSED u32 addr) +{ + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs1WriteByte(UNUSED u32 addr, UNUSED u8 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs1WriteWord(UNUSED u32 addr, UNUSED u16 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs1WriteLong(UNUSED u32 addr, UNUSED u32 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL DummyCs2ReadByte(UNUSED u32 addr) +{ + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL DummyCs2ReadWord(UNUSED u32 addr) +{ + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL DummyCs2ReadLong(UNUSED u32 addr) +{ + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs2WriteByte(UNUSED u32 addr, UNUSED u8 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs2WriteWord(UNUSED u32 addr, UNUSED u16 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DummyCs2WriteLong(UNUSED u32 addr, UNUSED u32 val) +{ +} + +////////////////////////////////////////////////////////////////////////////// +// Action Replay 4M Plus funcions +////////////////////////////////////////////////////////////////////////////// + +typedef enum + { + FL_READ, + FL_SDP, + FL_CMD, + FL_ID, + FL_IDSDP, + FL_IDCMD, + FL_WRITEBUF, + FL_WRITEARRAY + } flashstate; + +u8 flreg0 = 0; +u8 flreg1 = 0; + +flashstate flstate0; +flashstate flstate1; + +u8 flbuf0[128]; +u8 flbuf1[128]; + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL FlashCs0ReadByte(u32 addr) +{ + flashstate* state; + u8* reg; + + if (addr & 1) + { + state = &flstate1; + reg = &flreg1; + } + else + { + state = &flstate0; + reg = &flreg0; + } + + switch (*state) + { + case FL_ID: + case FL_IDSDP: + case FL_IDCMD: + if (addr & 2) return 0xD5; + else return 0x1F; + case FL_WRITEARRAY: *reg ^= 0x02; + case FL_WRITEBUF: return *reg; + case FL_SDP: + case FL_CMD: *state = FL_READ; + case FL_READ: + default: return T2ReadByte(CartridgeArea->rom, addr); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL FlashCs0ReadWord(u32 addr) +{ + return ((u16)(FlashCs0ReadByte(addr) << 8) | (u16)(FlashCs0ReadByte(addr+1))); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL FlashCs0ReadLong(u32 addr) +{ + return ((u32)FlashCs0ReadWord(addr) << 16) |(u32) FlashCs0ReadWord(addr + 2); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL FlashCs0WriteByte(u32 addr, u8 val) +{ + flashstate* state; + u8* reg; + u8* buf; + + if (addr & 1) + { + state = &flstate1; + reg = &flreg1; + buf = flbuf1; + } + else + { + state = &flstate0; + reg = &flreg0; + buf = flbuf0; + } + + switch (*state) + { + case FL_READ: + if (((addr & 0xfffe) == 0xaaaa) && (val == 0xaa)) + *state = FL_SDP; + return; + case FL_WRITEBUF: + buf[(addr >> 1) & 0x7f] = val; + if (((addr >> 1) & 0x7f) == 0x7f) + { + int i; + int j = addr & 0x1; + addr &= 0xffffff00; + for (i = 0; i <= 127; i++) + { + T2WriteByte(CartridgeArea->rom, (addr + i*2 + j), buf[i]); + } + *state = FL_READ; + } + return; + case FL_SDP: + if (((addr & 0xfffe) == 0x5554) && (val == 0x55)) + *state = FL_CMD; + else *state = FL_READ; + return; + case FL_ID: + if (((addr & 0xfffe) == 0xaaaa) && (val == 0xaa)) + *state = FL_IDSDP; + else *state = FL_ID; + return; + case FL_IDSDP: + if (((addr & 0xfffe) == 0x5554) && (val == 0x55)) + *state = FL_READ; + else *state=FL_ID; + return; + case FL_IDCMD: + if (((addr & 0xfffe) == 0xaaaa) && (val == 0xf0)) + *state = FL_READ; + else *state = FL_ID; + return; + case FL_CMD: + if ((addr & 0xfffe) != 0xaaaa) + { + *state = FL_READ; + return; + } + + switch (val) + { + case 0xa0: + *state = FL_WRITEBUF; + return; + case 0x90: + *state = FL_ID; + return; + default: + *state = FL_READ; + return; + } + default: break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL FlashCs0WriteWord(u32 addr, u16 val) +{ + FlashCs0WriteByte(addr, (u8)(val >> 8)); + FlashCs0WriteByte(addr + 1, (u8)(val & 0xff)); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL FlashCs0WriteLong(u32 addr, u32 val) +{ + FlashCs0WriteWord(addr, (u16)(val >> 16)); + FlashCs0WriteWord(addr + 2, (u16)(val & 0xffff)); +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL AR4MCs0ReadByte(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + return FlashCs0ReadByte(addr); +// return biosarea->getByte(addr); +// else // Outport +// fprintf(stderr, "Commlink Outport Byte read\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag read\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Byte read\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Dram area + return T1ReadByte(CartridgeArea->dram, addr & 0x3FFFFF); + default: // The rest doesn't matter + break; + } + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL AR4MCs0ReadWord(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + return FlashCs0ReadWord(addr); +// else // Outport +// fprintf(stderr, "Commlink Outport Word read\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag read\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Word read\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + return T1ReadWord(CartridgeArea->dram, addr & 0x3FFFFF); + case 0x12: + case 0x1E: + if (0x80000) + return 0xFFFD; + break; + case 0x13: + case 0x16: + case 0x17: + case 0x1A: + case 0x1B: + case 0x1F: + return 0xFFFD; + default: // The rest doesn't matter + break; + } + + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL AR4MCs0ReadLong(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + return FlashCs0ReadLong(addr); +// else // Outport +// fprintf(stderr, "Commlink Outport Long read\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag read\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Long read\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + return T1ReadLong(CartridgeArea->dram, addr & 0x3FFFFF); + case 0x12: + case 0x1E: + if (0x80000) + return 0xFFFDFFFD; + break; + case 0x13: + case 0x16: + case 0x17: + case 0x1A: + case 0x1B: + case 0x1F: + return 0xFFFDFFFD; + default: // The rest doesn't matter + break; + } + + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL AR4MCs0WriteByte(u32 addr, u8 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + FlashCs0WriteByte(addr, val); +// else // Outport +// fprintf(stderr, "Commlink Outport byte write\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag write\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Byte write\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteByte(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL AR4MCs0WriteWord(u32 addr, u16 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + FlashCs0WriteWord(addr, val); +// else // Outport +// fprintf(stderr, "Commlink Outport Word write\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag write\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Word write\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteWord(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL AR4MCs0WriteLong(u32 addr, u32 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x00: + { + if ((addr & 0x80000) == 0) // EEPROM + FlashCs0WriteLong(addr, val); +// else // Outport +// fprintf(stderr, "Commlink Outport Long write\n"); + break; + } + case 0x01: + { +// if ((addr & 0x80000) == 0) // Commlink Status flag +// fprintf(stderr, "Commlink Status Flag write\n"); +// else // Inport for Commlink +// fprintf(stderr, "Commlink Inport Long write\n"); + break; + } + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteLong(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// +// 8 Mbit Dram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL DRAM8MBITCs0ReadByte(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + return T1ReadByte(CartridgeArea->dram, addr & 0x7FFFF); + case 0x06: // Dram area + return T1ReadByte(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); + default: // The rest doesn't matter + break; + } + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL DRAM8MBITCs0ReadWord(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + return T1ReadWord(CartridgeArea->dram, addr & 0x7FFFF); + case 0x06: // Dram area + return T1ReadWord(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); + default: // The rest doesn't matter + break; + } + + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL DRAM8MBITCs0ReadLong(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + return T1ReadLong(CartridgeArea->dram, addr & 0x7FFFF); + case 0x06: // Dram area + return T1ReadLong(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); + default: // The rest doesn't matter + break; + } + + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM8MBITCs0WriteByte(u32 addr, u8 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + T1WriteByte(CartridgeArea->dram, addr & 0x7FFFF, val); + break; + case 0x06: // Dram area + T1WriteByte(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM8MBITCs0WriteWord(u32 addr, u16 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + T1WriteWord(CartridgeArea->dram, addr & 0x7FFFF, val); + break; + case 0x06: // Dram area + T1WriteWord(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM8MBITCs0WriteLong(u32 addr, u32 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: // Dram area + T1WriteLong(CartridgeArea->dram, addr & 0x7FFFF, val); + break; + case 0x06: // Dram area + T1WriteLong(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// +// 32 Mbit Dram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL DRAM32MBITCs0ReadByte(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Dram area + return T1ReadByte(CartridgeArea->dram, addr & 0x3FFFFF); + default: // The rest doesn't matter + break; + } + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL DRAM32MBITCs0ReadWord(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + return T1ReadWord(CartridgeArea->dram, addr & 0x3FFFFF); + default: // The rest doesn't matter + break; + } + + return 0xFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL DRAM32MBITCs0ReadLong(u32 addr) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + return T1ReadLong(CartridgeArea->dram, addr & 0x3FFFFF); + default: // The rest doesn't matter + break; + } + + return 0xFFFFFFFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM32MBITCs0WriteByte(u32 addr, u8 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteByte(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM32MBITCs0WriteWord(u32 addr, u16 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteWord(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL DRAM32MBITCs0WriteLong(u32 addr, u32 val) +{ + addr &= 0x1FFFFFF; + + switch (addr >> 20) + { + case 0x04: + case 0x05: + case 0x06: + case 0x07: // Ram cart area + T1WriteLong(CartridgeArea->dram, addr & 0x3FFFFF, val); + break; + default: // The rest doesn't matter + break; + } +} + +////////////////////////////////////////////////////////////////////////////// +// 4 Mbit Backup Ram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BUP4MBITCs1ReadByte(u32 addr) +{ + return T1ReadByte(CartridgeArea->bupram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BUP4MBITCs1ReadWord(u32 addr) +{ + return T1ReadWord(CartridgeArea->bupram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BUP4MBITCs1ReadLong(u32 addr) +{ + return T1ReadLong(CartridgeArea->bupram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP4MBITCs1WriteByte(u32 addr, u8 val) +{ + T1WriteByte(CartridgeArea->bupram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP4MBITCs1WriteWord(u32 addr, u16 val) +{ + T1WriteWord(CartridgeArea->bupram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP4MBITCs1WriteLong(u32 addr, u32 val) +{ + T1WriteLong(CartridgeArea->bupram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// +// 8 Mbit Backup Ram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BUP8MBITCs1ReadByte(u32 addr) +{ + return T1ReadByte(CartridgeArea->bupram, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BUP8MBITCs1ReadWord(u32 addr) +{ + return T1ReadWord(CartridgeArea->bupram, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BUP8MBITCs1ReadLong(u32 addr) +{ + return T1ReadLong(CartridgeArea->bupram, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP8MBITCs1WriteByte(u32 addr, u8 val) +{ + T1WriteByte(CartridgeArea->bupram, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP8MBITCs1WriteWord(u32 addr, u16 val) +{ + T1WriteWord(CartridgeArea->bupram, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP8MBITCs1WriteLong(u32 addr, u32 val) +{ + T1WriteLong(CartridgeArea->bupram, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// +// 16 Mbit Backup Ram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BUP16MBITCs1ReadByte(u32 addr) +{ + return T1ReadByte(CartridgeArea->bupram, addr & 0x3FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BUP16MBITCs1ReadWord(u32 addr) +{ + return T1ReadWord(CartridgeArea->bupram, addr & 0x3FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BUP16MBITCs1ReadLong(u32 addr) +{ + return T1ReadLong(CartridgeArea->bupram, addr & 0x3FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP16MBITCs1WriteByte(u32 addr, u8 val) +{ + T1WriteByte(CartridgeArea->bupram, addr & 0x3FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP16MBITCs1WriteWord(u32 addr, u16 val) +{ + T1WriteWord(CartridgeArea->bupram, addr & 0x3FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP16MBITCs1WriteLong(u32 addr, u32 val) +{ + T1WriteLong(CartridgeArea->bupram, addr & 0x3FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// +// 32 Mbit Backup Ram +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BUP32MBITCs1ReadByte(u32 addr) +{ + return T1ReadByte(CartridgeArea->bupram, addr & 0x7FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BUP32MBITCs1ReadWord(u32 addr) +{ + return T1ReadWord(CartridgeArea->bupram, addr & 0x7FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BUP32MBITCs1ReadLong(u32 addr) +{ + return T1ReadLong(CartridgeArea->bupram, addr & 0x7FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP32MBITCs1WriteByte(u32 addr, u8 val) +{ + T1WriteByte(CartridgeArea->bupram, addr & 0x7FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP32MBITCs1WriteWord(u32 addr, u16 val) +{ + T1WriteWord(CartridgeArea->bupram, addr & 0x7FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BUP32MBITCs1WriteLong(u32 addr, u32 val) +{ + T1WriteLong(CartridgeArea->bupram, addr & 0x7FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// +// 16 Mbit Rom +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL ROM16MBITCs0ReadByte(u32 addr) +{ + return T1ReadByte(CartridgeArea->rom, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL ROM16MBITCs0ReadWord(u32 addr) +{ + return T1ReadWord(CartridgeArea->rom, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL ROM16MBITCs0ReadLong(u32 addr) +{ + return T1ReadLong(CartridgeArea->rom, addr & 0x1FFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL ROM16MBITCs0WriteByte(u32 addr, u8 val) +{ + T1WriteByte(CartridgeArea->rom, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL ROM16MBITCs0WriteWord(u32 addr, u16 val) +{ + T1WriteWord(CartridgeArea->rom, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL ROM16MBITCs0WriteLong(u32 addr, u32 val) +{ + T1WriteLong(CartridgeArea->rom, addr & 0x1FFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// +// Sega Saturn Modem(Japanese) +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL JapModemCs0ReadByte(u32 addr) +{ + if (addr & 0x1) + return 0xA5; + else + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL JapModemCs0ReadWord(UNUSED u32 addr) +{ + return 0xFFA5; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL JapModemCs0ReadLong(UNUSED u32 addr) +{ + return 0xFFA5FFA5; +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL JapModemCs1ReadByte(UNUSED u32 addr) +{ + return 0xA5; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL JapModemCs1ReadWord(UNUSED u32 addr) +{ + return 0xA5A5; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL JapModemCs1ReadLong(UNUSED u32 addr) +{ + return 0xA5A5A5A5; +} + +////////////////////////////////////////////////////////////////////////////// +// General Cart functions +////////////////////////////////////////////////////////////////////////////// + +int CartInit(const char * filename, int type) +{ + if ((CartridgeArea = (cartridge_struct *)calloc(1, sizeof(cartridge_struct))) == NULL) + return -1; + + CartridgeArea->carttype = type; + CartridgeArea->filename = filename; + + switch(type) + { + case CART_PAR: // Action Replay 4M Plus(or equivalent) + { + if ((CartridgeArea->rom = T2MemoryInit(0x40000)) == NULL) + return -1; + + if ((CartridgeArea->dram = T1MemoryInit(0x400000)) == NULL) + return -1; + + // Use 32 Mbit Dram id + CartridgeArea->cartid = 0x5C; + + // Load AR firmware to memory + if (T123Load(CartridgeArea->rom, 0x40000, 2, filename) != 0) + return -1; + flstate0 = FL_READ; + flstate1 = FL_READ; + + // Setup Functions + CartridgeArea->Cs0ReadByte = &AR4MCs0ReadByte; + CartridgeArea->Cs0ReadWord = &AR4MCs0ReadWord; + CartridgeArea->Cs0ReadLong = &AR4MCs0ReadLong; + CartridgeArea->Cs0WriteByte = &AR4MCs0WriteByte; + CartridgeArea->Cs0WriteWord = &AR4MCs0WriteWord; + CartridgeArea->Cs0WriteLong = &AR4MCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; + CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; + CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_BACKUPRAM4MBIT: // 4 Mbit Backup Ram + { + if ((CartridgeArea->bupram = T1MemoryInit(0x100000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x21; + + // Load Backup Ram data from file + if (T123Load(CartridgeArea->bupram, 0x100000, 1, filename) != 0) + FormatBackupRam(CartridgeArea->bupram, 0x100000); + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &BUP4MBITCs1ReadByte; + CartridgeArea->Cs1ReadWord = &BUP4MBITCs1ReadWord; + CartridgeArea->Cs1ReadLong = &BUP4MBITCs1ReadLong; + CartridgeArea->Cs1WriteByte = &BUP4MBITCs1WriteByte; + CartridgeArea->Cs1WriteWord = &BUP4MBITCs1WriteWord; + CartridgeArea->Cs1WriteLong = &BUP4MBITCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + + break; + } + case CART_BACKUPRAM8MBIT: // 8 Mbit Backup Ram + { + if ((CartridgeArea->bupram = T1MemoryInit(0x200000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x22; + + // Load Backup Ram data from file + if (T123Load(CartridgeArea->bupram, 0x200000, 1, filename) != 0) + FormatBackupRam(CartridgeArea->bupram, 0x200000); + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &BUP8MBITCs1ReadByte; + CartridgeArea->Cs1ReadWord = &BUP8MBITCs1ReadWord; + CartridgeArea->Cs1ReadLong = &BUP8MBITCs1ReadLong; + CartridgeArea->Cs1WriteByte = &BUP8MBITCs1WriteByte; + CartridgeArea->Cs1WriteWord = &BUP8MBITCs1WriteWord; + CartridgeArea->Cs1WriteLong = &BUP8MBITCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + + break; + } + case CART_BACKUPRAM16MBIT: // 16 Mbit Backup Ram + { + if ((CartridgeArea->bupram = T1MemoryInit(0x400000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x23; + + // Load Backup Ram data from file + if (T123Load(CartridgeArea->bupram, 0x400000, 1, filename) != 0) + FormatBackupRam(CartridgeArea->bupram, 0x400000); + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &BUP16MBITCs1ReadByte; + CartridgeArea->Cs1ReadWord = &BUP16MBITCs1ReadWord; + CartridgeArea->Cs1ReadLong = &BUP16MBITCs1ReadLong; + CartridgeArea->Cs1WriteByte = &BUP16MBITCs1WriteByte; + CartridgeArea->Cs1WriteWord = &BUP16MBITCs1WriteWord; + CartridgeArea->Cs1WriteLong = &BUP16MBITCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_BACKUPRAM32MBIT: // 32 Mbit Backup Ram + { + if ((CartridgeArea->bupram = T1MemoryInit(0x800000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x24; + + // Load Backup Ram data from file + if (T123Load(CartridgeArea->bupram, 0x800000, 1, filename) != 0) + FormatBackupRam(CartridgeArea->bupram, 0x800000); + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &BUP32MBITCs1ReadByte; + CartridgeArea->Cs1ReadWord = &BUP32MBITCs1ReadWord; + CartridgeArea->Cs1ReadLong = &BUP32MBITCs1ReadLong; + CartridgeArea->Cs1WriteByte = &BUP32MBITCs1WriteByte; + CartridgeArea->Cs1WriteWord = &BUP32MBITCs1WriteWord; + CartridgeArea->Cs1WriteLong = &BUP32MBITCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_DRAM8MBIT: // 8 Mbit Dram Cart + { + if ((CartridgeArea->dram = T1MemoryInit(0x100000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x5A; + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DRAM8MBITCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DRAM8MBITCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DRAM8MBITCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DRAM8MBITCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DRAM8MBITCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DRAM8MBITCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; + CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; + CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_DRAM32MBIT: // 32 Mbit Dram Cart + { + if ((CartridgeArea->dram = T1MemoryInit(0x400000)) == NULL) + return -1; + + CartridgeArea->cartid = 0x5C; + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DRAM32MBITCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DRAM32MBITCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DRAM32MBITCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DRAM32MBITCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DRAM32MBITCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DRAM32MBITCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; + CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; + CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_ROM16MBIT: // 16 Mbit Rom Cart + { + if ((CartridgeArea->rom = T1MemoryInit(0x200000)) == NULL) + return -1; + + CartridgeArea->cartid = 0xFF; // I have no idea what the real id is + + // Load Rom to memory + if (T123Load(CartridgeArea->rom, 0x200000, 1, filename) != 0) + return -1; + + // Setup Functions + CartridgeArea->Cs0ReadByte = &ROM16MBITCs0ReadByte; + CartridgeArea->Cs0ReadWord = &ROM16MBITCs0ReadWord; + CartridgeArea->Cs0ReadLong = &ROM16MBITCs0ReadLong; + CartridgeArea->Cs0WriteByte = &ROM16MBITCs0WriteByte; + CartridgeArea->Cs0WriteWord = &ROM16MBITCs0WriteWord; + CartridgeArea->Cs0WriteLong = &ROM16MBITCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; + CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; + CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + case CART_JAPMODEM: // Sega Saturn Modem(Japanese) + { + CartridgeArea->cartid = 0xFF; + + CartridgeArea->Cs0ReadByte = &JapModemCs0ReadByte; + CartridgeArea->Cs0ReadWord = &JapModemCs0ReadWord; + CartridgeArea->Cs0ReadLong = &JapModemCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &JapModemCs1ReadByte; + CartridgeArea->Cs1ReadWord = &JapModemCs1ReadWord; + CartridgeArea->Cs1ReadLong = &JapModemCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + default: // No Cart + { + CartridgeArea->cartid = 0xFF; + + // Setup Functions + CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; + CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; + CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; + CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; + CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; + CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; + + CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; + CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; + CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; + CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; + CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; + + CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; + CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; + CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; + CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; + CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; + CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; + break; + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void CartDeInit(void) +{ + if (CartridgeArea) + { + if (CartridgeArea->carttype == CART_PAR) + { + if (CartridgeArea->rom) + { + if (T123Save(CartridgeArea->rom, 0x40000, 2, CartridgeArea->filename) != 0) + YabSetError(YAB_ERR_FILEWRITE, (void *)CartridgeArea->filename); + T2MemoryDeInit(CartridgeArea->rom); + } + } + else + { + if (CartridgeArea->rom) + T1MemoryDeInit(CartridgeArea->rom); + } + + if (CartridgeArea->bupram) + { + u32 size=0; + + switch (CartridgeArea->carttype) + { + case CART_BACKUPRAM4MBIT: // 4 Mbit Backup Ram + { + size = 0x100000; + break; + } + case CART_BACKUPRAM8MBIT: // 8 Mbit Backup Ram + { + size = 0x200000; + break; + } + case CART_BACKUPRAM16MBIT: // 16 Mbit Backup Ram + { + size = 0x400000; + break; + } + case CART_BACKUPRAM32MBIT: // 32 Mbit Backup Ram + { + size = 0x800000; + break; + } + } + + if (size != 0) + { + if (T123Save(CartridgeArea->bupram, size, 1, CartridgeArea->filename) != 0) + YabSetError(YAB_ERR_FILEWRITE, (void *)CartridgeArea->filename); + + T1MemoryDeInit(CartridgeArea->bupram); + } + } + + if (CartridgeArea->dram) + T1MemoryDeInit(CartridgeArea->dram); + + free(CartridgeArea); + } + CartridgeArea = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +int CartSaveState(FILE * fp) +{ + int offset; + + offset = StateWriteHeader(fp, "CART", 1); + + // Write cart type + fwrite((void *)&CartridgeArea->carttype, 4, 1, fp); + + // Write the areas associated with the cart type here + + return StateFinishHeader(fp, offset); +} + +////////////////////////////////////////////////////////////////////////////// + +int CartLoadState(FILE * fp, UNUSED int version, int size) +{ + int newtype; + + // Read cart type + fread((void *)&newtype, 4, 1, fp); + + // Check to see if old cart type and new cart type match, if they don't, + // reallocate memory areas + + // Read the areas associated with the cart type here + + return size; +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/cs0.h b/yabause/src/cs0.h new file mode 100644 index 0000000000..c61edd3774 --- /dev/null +++ b/yabause/src/cs0.h @@ -0,0 +1,78 @@ +/* Copyright 2004-2005 Theo Berkau + Copyright 2005 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CS0_H +#define CS0_H + +#include "memory.h" + +#define CART_NONE 0 +#define CART_PAR 1 +#define CART_BACKUPRAM4MBIT 2 +#define CART_BACKUPRAM8MBIT 3 +#define CART_BACKUPRAM16MBIT 4 +#define CART_BACKUPRAM32MBIT 5 +#define CART_DRAM8MBIT 6 +#define CART_DRAM32MBIT 7 +#define CART_NETLINK 8 +#define CART_ROM16MBIT 9 +#define CART_JAPMODEM 10 + +typedef struct +{ + int carttype; + int cartid; + const char *filename; + + u8 FASTCALL (*Cs0ReadByte)(u32 addr); + u16 FASTCALL (*Cs0ReadWord)(u32 addr); + u32 FASTCALL (*Cs0ReadLong)(u32 addr); + void FASTCALL (*Cs0WriteByte)(u32 addr, u8 val); + void FASTCALL (*Cs0WriteWord)(u32 addr, u16 val); + void FASTCALL (*Cs0WriteLong)(u32 addr, u32 val); + + u8 FASTCALL (*Cs1ReadByte)(u32 addr); + u16 FASTCALL (*Cs1ReadWord)(u32 addr); + u32 FASTCALL (*Cs1ReadLong)(u32 addr); + void FASTCALL (*Cs1WriteByte)(u32 addr, u8 val); + void FASTCALL (*Cs1WriteWord)(u32 addr, u16 val); + void FASTCALL (*Cs1WriteLong)(u32 addr, u32 val); + + u8 FASTCALL (*Cs2ReadByte)(u32 addr); + u16 FASTCALL (*Cs2ReadWord)(u32 addr); + u32 FASTCALL (*Cs2ReadLong)(u32 addr); + void FASTCALL (*Cs2WriteByte)(u32 addr, u8 val); + void FASTCALL (*Cs2WriteWord)(u32 addr, u16 val); + void FASTCALL (*Cs2WriteLong)(u32 addr, u32 val); + + void *rom; + void *bupram; + void *dram; +} cartridge_struct; + +extern cartridge_struct *CartridgeArea; + +int CartInit(const char *filename, int); +void CartDeInit(void); + +int CartSaveState(FILE *fp); +int CartLoadState(FILE *fp, int version, int size); + +#endif diff --git a/yabause/src/cs1.c b/yabause/src/cs1.c new file mode 100644 index 0000000000..6461d269da --- /dev/null +++ b/yabause/src/cs1.c @@ -0,0 +1,97 @@ +/* Copyright 2003-2005 Guillaume Duhamel + Copyright 2005 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "cs1.h" +#include "cs0.h" + +////////////////////////////////////////////////////////////////////////////// + +u8 FASTCALL Cs1ReadByte(u32 addr) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFF) + return CartridgeArea->cartid; + + return CartridgeArea->Cs1ReadByte(addr); +} + +////////////////////////////////////////////////////////////////////////////// + +u16 FASTCALL Cs1ReadWord(u32 addr) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFE) + return (0xFF00 | CartridgeArea->cartid); + + return CartridgeArea->Cs1ReadWord(addr); +} + +////////////////////////////////////////////////////////////////////////////// + +u32 FASTCALL Cs1ReadLong(u32 addr) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFC) + return (0xFF00FF00 | (CartridgeArea->cartid << 16) | CartridgeArea->cartid); + + return CartridgeArea->Cs1ReadLong(addr); +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs1WriteByte(u32 addr, u8 val) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFF) + return; + + CartridgeArea->Cs1WriteByte(addr, val); +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs1WriteWord(u32 addr, u16 val) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFE) + return; + + CartridgeArea->Cs1WriteWord(addr, val); +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs1WriteLong(u32 addr, u32 val) +{ + addr &= 0xFFFFFF; + + if (addr == 0xFFFFFC) + return; + + CartridgeArea->Cs1WriteLong(addr, val); +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/cs1.h b/yabause/src/cs1.h new file mode 100644 index 0000000000..2ebdee2e20 --- /dev/null +++ b/yabause/src/cs1.h @@ -0,0 +1,34 @@ +/* Copyright 2003-2005 Guillaume Duhamel + Copyright 2005 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CS1_H +#define CS1_H + +#include "cs0.h" +#include "memory.h" + +u8 FASTCALL Cs1ReadByte(u32); +u16 FASTCALL Cs1ReadWord(u32); +u32 FASTCALL Cs1ReadLong(u32); +void FASTCALL Cs1WriteByte(u32, u8); +void FASTCALL Cs1WriteWord(u32, u16); +void FASTCALL Cs1WriteLong(u32, u32); + +#endif diff --git a/yabause/src/cs2.c b/yabause/src/cs2.c new file mode 100644 index 0000000000..92484ac6de --- /dev/null +++ b/yabause/src/cs2.c @@ -0,0 +1,3720 @@ +/* Copyright 2003 Guillaume Duhamel + Copyright 2004-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "cs2.h" +#include "debug.h" +#include "error.h" +#include "netlink.h" +#include "scsp.h" +#include "scu.h" +#include "smpc.h" +#include "yui.h" + +#define CDB_HIRQ_CMOK 0x0001 +#define CDB_HIRQ_DRDY 0x0002 +#define CDB_HIRQ_CSCT 0x0004 +#define CDB_HIRQ_BFUL 0x0008 +#define CDB_HIRQ_PEND 0x0010 +#define CDB_HIRQ_DCHG 0x0020 +#define CDB_HIRQ_ESEL 0x0040 +#define CDB_HIRQ_EHST 0x0080 +#define CDB_HIRQ_ECPY 0x0100 +#define CDB_HIRQ_EFLS 0x0200 +#define CDB_HIRQ_SCDQ 0x0400 +#define CDB_HIRQ_MPED 0x0800 +#define CDB_HIRQ_MPCM 0x1000 +#define CDB_HIRQ_MPST 0x2000 + +#define CDB_STAT_BUSY 0x00 +#define CDB_STAT_PAUSE 0x01 +#define CDB_STAT_STANDBY 0x02 +#define CDB_STAT_PLAY 0x03 +#define CDB_STAT_SEEK 0x04 +#define CDB_STAT_SCAN 0x05 +#define CDB_STAT_OPEN 0x06 +#define CDB_STAT_NODISC 0x07 +#define CDB_STAT_RETRY 0x08 +#define CDB_STAT_ERROR 0x09 +#define CDB_STAT_FATAL 0x0A +#define CDB_STAT_PERI 0x20 +#define CDB_STAT_TRNS 0x40 +#define CDB_STAT_WAIT 0x80 +#define CDB_STAT_REJECT 0xFF + +#define CDB_PLAYTYPE_SECTOR 0x01 +#define CDB_PLAYTYPE_FILE 0x02 + +Cs2 * Cs2Area = NULL; +ip_struct *cdip = NULL; + +extern CDInterface *CDCoreList[]; + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void doCDReport(u8 status) +{ + Cs2Area->reg.CR1 = (status << 8) | ((Cs2Area->options & 0xF) << 4) | (Cs2Area->repcnt & 0xF); + Cs2Area->reg.CR2 = (Cs2Area->ctrladdr << 8) | Cs2Area->track; + Cs2Area->reg.CR3 = (u16)((Cs2Area->index << 8) | ((Cs2Area->FAD >> 16) & 0xFF)); + Cs2Area->reg.CR4 = (u16) Cs2Area->FAD; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void doMPEGReport(u8 status) +{ + Cs2Area->reg.CR1 = (status << 8) | Cs2Area->actionstatus; + Cs2Area->reg.CR2 = Cs2Area->vcounter; + Cs2Area->reg.CR3 = (Cs2Area->pictureinfo << 8) | Cs2Area->mpegaudiostatus; + Cs2Area->reg.CR4 = Cs2Area->mpegvideostatus; +} + +////////////////////////////////////////////////////////////////////////////// + +u8 FASTCALL Cs2ReadByte(u32 addr) +{ + addr &= 0xFFFFF; // fix me(I should really have proper mapping) + + if(Cs2Area->carttype == CART_NETLINK) + return NetlinkReadByte(addr); + else + { + // only netlink seems to use byte-access + switch (addr) + { + case 0x95001: + case 0x95005: + case 0x95009: + case 0x9500D: + case 0x95011: + case 0x95015: + case 0x95019: + case 0x9501D: + return 0xFF; + default: + break; + } + } + + LOG("Unimplemented cs2 byte read: %08X\n", addr); + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs2WriteByte(u32 addr, u8 val) +{ + addr &= 0xFFFFF; // fix me(I should really have proper mapping) + + if(Cs2Area->carttype == CART_NETLINK) + { + NetlinkWriteByte(addr, val); + return; + } + else + { + // only netlink seems to use byte-access + switch (addr) + { + case 0x2503D: + case 0x95011: + case 0x9501D: + return; + default: + break; + } + } + + LOG("Unimplemented cs2 byte write: %08X\n", addr); +} + +////////////////////////////////////////////////////////////////////////////// + +u16 FASTCALL Cs2ReadWord(u32 addr) { + u16 val = 0; + addr &= 0xFFFFF; // fix me(I should really have proper mapping) + + switch(addr) { + case 0x90008: + case 0x9000A: + val = Cs2Area->reg.HIRQ; + + if (Cs2Area->isbufferfull) + val |= CDB_HIRQ_BFUL; + else + val &= ~CDB_HIRQ_BFUL; + + if (Cs2Area->isdiskchanged) + val |= CDB_HIRQ_DCHG; + else + val &= ~CDB_HIRQ_DCHG; + + if (Cs2Area->isonesectorstored) + val |= CDB_HIRQ_CSCT; + else + val &= ~CDB_HIRQ_CSCT; + + Cs2Area->reg.HIRQ = val; + +// CDLOG("cs2\t: Hirq read, Hirq mask = %x - ret: %x\n", Memory::getWord(0x9000C), val); + return val; + case 0x9000C: + case 0x9000E: return Cs2Area->reg.HIRQMASK; + case 0x90018: + case 0x9001A: return Cs2Area->reg.CR1; + case 0x9001C: + case 0x9001E: return Cs2Area->reg.CR2; + case 0x90020: + case 0x90022: return Cs2Area->reg.CR3; + case 0x90024: + case 0x90026: Cs2Area->_command = 0; + return Cs2Area->reg.CR4; + case 0x90028: + case 0x9002A: return Cs2Area->reg.MPEGRGB; + case 0x98000: + // transfer info + switch (Cs2Area->infotranstype) { + case 0: + // Get Toc Data + if (Cs2Area->transfercount % 4 == 0) + val = (u16)((Cs2Area->TOC[Cs2Area->transfercount >> 2] & 0xFFFF0000) >> 16); + else + val = (u16)Cs2Area->TOC[Cs2Area->transfercount >> 2]; + + Cs2Area->transfercount += 2; + Cs2Area->cdwnum += 2; + + if (Cs2Area->transfercount > (0xCC * 2)) + { + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = -1; + } + break; + case 1: + // Get File Info(1 file info) + val = (Cs2Area->transfileinfo[Cs2Area->transfercount] << 8) | + Cs2Area->transfileinfo[Cs2Area->transfercount + 1]; + Cs2Area->transfercount += 2; + Cs2Area->cdwnum += 2; + + if (Cs2Area->transfercount > (0x6 * 2)) + { + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = -1; + } + + break; + case 2: + // Get File Info(254 file info) + + // Do we need to retrieve the next file info? + if (Cs2Area->transfercount % (0x6 * 2) == 0) { + // yes we do + Cs2SetupFileInfoTransfer(2 + (Cs2Area->transfercount / (0x6 * 2))); + } + + val = (Cs2Area->transfileinfo[Cs2Area->transfercount % (0x6 * 2)] << 8) | + Cs2Area->transfileinfo[Cs2Area->transfercount % (0x6 * 2) + 1]; + + Cs2Area->transfercount += 2; + Cs2Area->cdwnum += 2; + + if (Cs2Area->transfercount > (254 * (0x6 * 2))) + { + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = -1; + } + + break; + default: break; + } + break; + default: + LOG("cs2\t: Undocumented register read %08X\n", addr); +// val = T3ReadWord(Cs2Area->mem, addr); + break; + } + + return val; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs2WriteWord(u32 addr, u16 val) { + addr &= 0xFFFFF; // fix me(I should really have proper mapping) + + switch(addr) { + case 0x90008: + case 0x9000A: + Cs2Area->reg.HIRQ &= val; + return; + case 0x9000C: + case 0x9000E: Cs2Area->reg.HIRQMASK = val; + return; + case 0x90018: + case 0x9001A: Cs2Area->status &= ~CDB_STAT_PERI; + Cs2Area->_command = 1; + Cs2Area->reg.CR1 = val; + return; + case 0x9001C: + case 0x9001E: Cs2Area->reg.CR2 = val; + return; + case 0x90020: + case 0x90022: Cs2Area->reg.CR3 = val; + return; + case 0x90024: + case 0x90026: Cs2Area->reg.CR4 = val; + Cs2SetCommandTiming(Cs2Area->reg.CR1 >> 8); + return; + case 0x90028: + case 0x9002A: Cs2Area->reg.MPEGRGB = val; + return; + default: + LOG("cs2\t:Undocumented register write %08X\n", addr); +// T3WriteWord(Cs2Area->mem, addr, val); + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +u32 FASTCALL Cs2ReadLong(u32 addr) { + s32 i; + u32 val = 0; + addr &= 0xFFFFF; // fix me(I should really have proper mapping) + + switch(addr) { + case 0x90008: + val = Cs2Area->reg.HIRQ; + + if (Cs2Area->isbufferfull) + val |= CDB_HIRQ_BFUL; + else + val &= ~CDB_HIRQ_BFUL; + + if (Cs2Area->isdiskchanged) + val |= CDB_HIRQ_DCHG; + else + val &= ~CDB_HIRQ_DCHG; + + if (Cs2Area->isonesectorstored) + val |= CDB_HIRQ_CSCT; + else + val &= ~CDB_HIRQ_CSCT; + + Cs2Area->reg.HIRQ = (u16)val; + + val |= (val << 16); + return val; + case 0x9000C: return ((Cs2Area->reg.HIRQMASK << 16) | Cs2Area->reg.HIRQMASK); + case 0x90018: return ((Cs2Area->reg.CR1 << 16) | Cs2Area->reg.CR1); + case 0x9001C: return ((Cs2Area->reg.CR2 << 16) | Cs2Area->reg.CR2); + case 0x90020: return ((Cs2Area->reg.CR3 << 16) | Cs2Area->reg.CR3); + case 0x90024: Cs2Area->_command = 0; + return ((Cs2Area->reg.CR4 << 16) | Cs2Area->reg.CR4); + case 0x90028: return ((Cs2Area->reg.MPEGRGB << 16) | Cs2Area->reg.MPEGRGB); + case 0x18000: + // transfer data + if (Cs2Area->datatranstype != -1) + { + // get sector + + // Make sure we still have sectors to transfer + if (Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) + { + // Transfer Data + const u8 *ptr = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; +#ifdef WORDS_BIGENDIAN + val = *((const u32 *) ptr); +#else + val = BSWAP32(*((const u32 *) ptr)); +#endif + + // increment datatransoffset/cdwnum + Cs2Area->cdwnum += 4; + Cs2Area->datatransoffset += 4; + + // Make sure we're not beyond the sector size boundry + if (Cs2Area->datatransoffset >= Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size) + { + Cs2Area->datatransoffset = 0; + Cs2Area->datanumsecttrans++; + } + } + else + { + if (Cs2Area->datatranstype == 2) + { + // Ok, so we don't have any more sectors to + // transfer, might as well delete them all. + + Cs2Area->datatranstype = -1; + + // free blocks + for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) + { + Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); + Cs2Area->datatranspartition->block[i] = NULL; + Cs2Area->datatranspartition->blocknum[i] = 0xFF; + } + + // sort remaining blocks + Cs2SortBlocks(Cs2Area->datatranspartition); + + Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; + Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; + + CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); + } + } + } + break; + default: + LOG("cs2\t: Undocumented register read %08X\n", addr); +// val = T3ReadLong(Cs2Area->mem, addr); + break; + } + + return val; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL Cs2WriteLong(UNUSED u32 addr, UNUSED u32 val) { + LOG("cs2\t: Long writing isn't implemented\n"); +// T3WriteLong(Cs2Area->mem, addr, val); +} + +////////////////////////////////////////////////////////////////////////////// + +/* Copy "count" 32-bit words from the CD buffer to type-1 memory "dest" (a + * native pointer), as though 0x25818000 had been read that many times */ + +void FASTCALL Cs2RapidCopyT1(void *dest, u32 count) +{ + u8 *dest8 = (u8 *) dest; + + if (Cs2Area->datatranstype != -1) + { + // Copy as many sectors as we have left, one sector at a time + + while (count > 0 && Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) + { + const u8 *src = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; + const u32 size = Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size; + const u32 max = size - Cs2Area->datatransoffset; + const u32 copy = (max < count*4) ? max : count*4; + memcpy(dest8, src, copy); + dest8 += copy; + count -= copy/4; + Cs2Area->datatransoffset += copy; + Cs2Area->cdwnum += copy; + + // Update the sector index if we reached the end of the sector + if (Cs2Area->datatransoffset >= size) + { + Cs2Area->datatransoffset = 0; + Cs2Area->datanumsecttrans++; + } + } + + // If we're in delete mode and we read through everything in memory, + // delete the sectors + if (Cs2Area->datatranstype == 2 + && Cs2Area->datanumsecttrans >= Cs2Area->datasectstotrans) + { + u32 i; + + Cs2Area->datatranstype = -1; + + for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) + { + Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); + Cs2Area->datatranspartition->block[i] = NULL; + Cs2Area->datatranspartition->blocknum[i] = 0xFF; + } + + Cs2SortBlocks(Cs2Area->datatranspartition); + + Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; + Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; + + CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); + } + } + + if (count > 0) + { + // We tried to copy more data than was stored, so fill the rest of + // the buffer with dummy data + memset(dest8, 0xCD, count*4); + } +} + +////////////////////////////////////////////////////////////////////////////// + +/* Copy "count" 32-bit words from the CD buffer to type-2 memory "dest" (a + * native pointer), as though 0x25818000 had been read that many times */ + +void FASTCALL Cs2RapidCopyT2(void *dest, u32 count) +{ + u32 *dest32 = (u32 *) dest; + + if (Cs2Area->datatranstype != -1) + { + // Copy as many sectors as we have left, one sector at a time; copy + // four words at a time where possible to improve data parallelism + + while (count > 0 && Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) + { + const u8 *src = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; + const u32 size = Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size; + const u32 max = size - Cs2Area->datatransoffset; + const u32 copy = (max < count*4) ? max : count*4; + u32 i = 0; + if (copy >= 16) { + for (; i < copy-12; i += 16, src += 16, dest32 += 4) + { + u32 word0, word1, word2, word3; +#ifdef WORDS_BIGENDIAN + word0 = ((u32 *)src)[0]; + word1 = ((u32 *)src)[1]; + word2 = ((u32 *)src)[2]; + word3 = ((u32 *)src)[3]; +#else + word0 = BSWAP16(((u32 *)src)[0]); + word1 = BSWAP16(((u32 *)src)[1]); + word2 = BSWAP16(((u32 *)src)[2]); + word3 = BSWAP16(((u32 *)src)[3]); +#endif + dest32[0] = word0; + dest32[1] = word1; + dest32[2] = word2; + dest32[3] = word3; + } + } + for (; i < copy; i += 4, src += 4, dest32++) + { +#ifdef WORDS_BIGENDIAN + *dest32 = *(u32 *)src; +#else + *dest32 = BSWAP16(*(u32 *)src); +#endif + } + count -= copy/4; + Cs2Area->datatransoffset += copy; + Cs2Area->cdwnum += copy; + + if (Cs2Area->datatransoffset >= size) + { + Cs2Area->datatransoffset = 0; + Cs2Area->datanumsecttrans++; + } + } + + if (Cs2Area->datatranstype == 2 + && Cs2Area->datanumsecttrans >= Cs2Area->datasectstotrans) + { + u32 i; + + Cs2Area->datatranstype = -1; + + for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) + { + Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); + Cs2Area->datatranspartition->block[i] = NULL; + Cs2Area->datatranspartition->blocknum[i] = 0xFF; + } + + Cs2SortBlocks(Cs2Area->datatranspartition); + + Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; + Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; + + CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); + } + } + + if (count > 0) + { + memset(dest32, 0xCD, count*4); + } +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2Init(int carttype, int coreid, const char *cdpath, const char *mpegpath, const char *netlinksetting) { + int ret; + + if ((Cs2Area = (Cs2 *) malloc(sizeof(Cs2))) == NULL) + return -1; + memset(Cs2Area, 0, sizeof(*Cs2Area)); + + Cs2Area->carttype = carttype; + Cs2Area->mpegpath = mpegpath; + Cs2Area->cdi=NULL; + + if ((ret = Cs2ChangeCDCore(coreid, cdpath)) != 0) + return ret; + + Cs2Reset(); + + // If Modem is connected, set the registers + if(Cs2Area->carttype == CART_NETLINK) + { + if ((ret = NetlinkInit(netlinksetting)) != 0) + return ret; + } + + if ((cdip = (ip_struct *) calloc(sizeof(ip_struct), 1)) == NULL) + return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2ChangeCDCore(int coreid, const char *cdpath) +{ + int i; + + // Make sure the old core is freed + if (Cs2Area->cdi != NULL) + Cs2Area->cdi->DeInit(); + + // So which core do we want? + if (coreid == CDCORE_DEFAULT) + coreid = 0; // Assume we want the first one + + // Go through core list and find the id + for (i = 0; CDCoreList[i] != NULL; i++) + { + if (CDCoreList[i]->id == coreid) + { + // Set to current core + Cs2Area->cdi = CDCoreList[i]; + break; + } + } + + if (Cs2Area->cdi == NULL) + { + Cs2Area->cdi = &DummyCD; + return -1; + } + + if (Cs2Area->cdi->Init(cdpath) != 0) + { + // This might be helpful. + YabSetError(YAB_ERR_CANNOTINIT, (void *)Cs2Area->cdi->Name); + + // Since it failed, instead of it being fatal, we'll just use the dummy + // core instead + Cs2Area->cdi = &DummyCD; + } + + Cs2Area->isdiskchanged = 1; + Cs2Area->status = CDB_STAT_PAUSE; + SmpcRecheckRegion(); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2DeInit(void) { + if(Cs2Area != NULL) { + if (Cs2Area->cdi != NULL) { + Cs2Area->cdi->DeInit(); + } + + if(Cs2Area->carttype == CART_NETLINK) + NetlinkDeInit(); + + free(Cs2Area); + } + Cs2Area = NULL; + + if (cdip) + free(cdip); + cdip = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2Reset(void) { + u32 i, i2; + + switch (Cs2Area->cdi->GetStatus()) + { + case 0: + case 1: + Cs2Area->status = CDB_STAT_PAUSE; + Cs2Area->FAD = 150; + Cs2Area->options = 0; + Cs2Area->repcnt = 0; + Cs2Area->ctrladdr = 0x41; + Cs2Area->track = 1; + Cs2Area->index = 1; + break; + case 2: + Cs2Area->status = CDB_STAT_NODISC; + + Cs2Area->FAD = 0xFFFFFFFF; + Cs2Area->options = 0xFF; + Cs2Area->repcnt = 0xFF; + Cs2Area->ctrladdr = 0xFF; + Cs2Area->track = 0xFF; + Cs2Area->index = 0xFF; + break; + case 3: + Cs2Area->status = CDB_STAT_OPEN; + + Cs2Area->FAD = 0xFFFFFFFF; + Cs2Area->options = 0xFF; + Cs2Area->repcnt = 0xFF; + Cs2Area->ctrladdr = 0xFF; + Cs2Area->track = 0xFF; + Cs2Area->index = 0xFF; + break; + default: break; + } + + Cs2Area->infotranstype = -1; + Cs2Area->datatranstype = -1; + Cs2Area->transfercount = 0; + Cs2Area->cdwnum = 0; + Cs2Area->getsectsize = Cs2Area->putsectsize = 2048; + Cs2Area->isdiskchanged = 1; + Cs2Area->isbufferfull = 0; + Cs2Area->isonesectorstored = 0; + Cs2Area->isaudio = 0; + + Cs2Area->reg.CR1 = ( 0 <<8) | 'C'; + Cs2Area->reg.CR2 = ('D'<<8) | 'B'; + Cs2Area->reg.CR3 = ('L'<<8) | 'O'; + Cs2Area->reg.CR4 = ('C'<<8) | 'K'; + Cs2Area->reg.HIRQ = 0xFFFF; + Cs2Area->reg.HIRQMASK = 0xFFFF; + + Cs2Area->playFAD = 0xFFFFFFFF; + Cs2Area->playendFAD = 0xFFFFFFFF; + Cs2Area->playtype = 0; + Cs2Area->maxrepeat = 0; + + // set authentication variables to 0(not authenticated) + Cs2Area->satauth = 0; + Cs2Area->mpgauth = 0; + + // clear filter conditions + for (i = 0; i < MAX_SELECTORS; i++) + { + Cs2Area->filter[i].FAD = 0; + Cs2Area->filter[i].range = 0xFFFFFFFF; + Cs2Area->filter[i].mode = 0; + Cs2Area->filter[i].chan = 0; + Cs2Area->filter[i].smmask = 0; + Cs2Area->filter[i].cimask = 0; + Cs2Area->filter[i].fid = 0; + Cs2Area->filter[i].smval = 0; + Cs2Area->filter[i].cival = 0; + Cs2Area->filter[i].condtrue = 0; + Cs2Area->filter[i].condfalse = 0xFF; + } + + // clear partitions + for (i = 0; i < MAX_SELECTORS; i++) + { + Cs2Area->partition[i].size = -1; + Cs2Area->partition[i].numblocks = 0; + + for (i2 = 0; i2 < MAX_BLOCKS; i2++) + { + Cs2Area->partition[i].block[i2] = NULL; + Cs2Area->partition[i].blocknum[i2] = 0xFF; + } + } + + // clear blocks + for (i = 0; i < MAX_BLOCKS; i++) + { + Cs2Area->block[i].size = -1; + memset(Cs2Area->block[i].data, 0, 2352); + } + + Cs2Area->blockfreespace = 200; + + // initialize TOC + memset(Cs2Area->TOC, 0xFF, sizeof(Cs2Area->TOC)); + + // clear filesystem stuff + Cs2Area->curdirsect = 0; + Cs2Area->curdirsize = 0; + Cs2Area->curdirfidoffset = 0; + memset(&Cs2Area->fileinfo, 0, sizeof(Cs2Area->fileinfo)); + Cs2Area->numfiles = 0; + + Cs2Area->lastbuffer = 0xFF; + + Cs2Area->_command = 0; + Cs2Area->_periodiccycles = 0; + Cs2Area->_commandtiming = 0; + Cs2SetTiming(0); + + // MPEG specific stuff + Cs2Area->mpegcon[0].audcon = Cs2Area->mpegcon[0].vidcon = 0x00; + Cs2Area->mpegcon[0].audlay = Cs2Area->mpegcon[0].vidlay = 0x00; + Cs2Area->mpegcon[0].audbufdivnum = Cs2Area->mpegcon[0].vidbufdivnum = 0xFF; + Cs2Area->mpegcon[1].audcon = Cs2Area->mpegcon[1].vidcon = 0x00; + Cs2Area->mpegcon[1].audlay = Cs2Area->mpegcon[1].vidlay = 0x00; + Cs2Area->mpegcon[1].audbufdivnum = Cs2Area->mpegcon[1].vidbufdivnum = 0xFF; + + // should verify the following + Cs2Area->mpegstm[0].audstm = Cs2Area->mpegstm[0].vidstm = 0x00; + Cs2Area->mpegstm[0].audstmid = Cs2Area->mpegstm[0].vidstmid = 0x00; + Cs2Area->mpegstm[0].audchannum = Cs2Area->mpegstm[0].vidchannum = 0x00; + Cs2Area->mpegstm[1].audstm = Cs2Area->mpegstm[1].vidstm = 0x00; + Cs2Area->mpegstm[1].audstmid = Cs2Area->mpegstm[1].vidstmid = 0x00; + Cs2Area->mpegstm[1].audchannum = Cs2Area->mpegstm[1].vidchannum = 0x00; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2Exec(u32 timing) { + Cs2Area->_periodiccycles += timing * 3; + + if (Cs2Area->_commandtiming > 0) + { + if (Cs2Area->_commandtiming < timing) + { + Cs2Execute(); + Cs2Area->_commandtiming = 0; + } + else + Cs2Area->_commandtiming -= timing; + } + + if (Cs2Area->_periodiccycles >= Cs2Area->_periodictiming) + { + Cs2Area->_periodiccycles -= Cs2Area->_periodictiming; + + // Get Drive's current status and compare with old status +// switch(cd->getStatus()) // this shouldn't be called every periodic response + switch(0) + { + case 0: + case 1: + if ((Cs2Area->status & 0xF) == CDB_STAT_NODISC || + (Cs2Area->status & 0xF) == CDB_STAT_OPEN) + { + Cs2Area->status = CDB_STAT_PAUSE; + Cs2Area->isdiskchanged = 1; + } + break; + case 2: + // may need to change this + if ((Cs2Area->status & 0xF) != CDB_STAT_NODISC) + Cs2Area->status = CDB_STAT_NODISC; + break; + case 3: + // may need to change this + if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN) + Cs2Area->status = CDB_STAT_OPEN; + break; + default: break; + } + + switch (Cs2Area->status & 0xF) { + case CDB_STAT_PAUSE: + { +// if (FAD >= playFAD && FAD < playendFAD) +// status = CDB_STAT_PLAY; +// else + break; + } + case CDB_STAT_PLAY: + { + partition_struct * playpartition; + int ret = Cs2ReadFilteredSector(Cs2Area->FAD, &playpartition); + + switch (ret) + { + case 0: + // Sector Read OK + Cs2Area->FAD++; + Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); + + if (playpartition != NULL) + { + // We can use this sector + CDLOG("partition number = %d blocks = %d blockfreespace = %d fad = %x playpartition->size = %x isbufferfull = %x\n", (playpartition - Cs2Area->partition), playpartition->numblocks, Cs2Area->blockfreespace, Cs2Area->FAD, playpartition->size, Cs2Area->isbufferfull); + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CSCT; + Cs2Area->isonesectorstored = 1; + + if (Cs2Area->FAD >= Cs2Area->playendFAD) { + // Make sure we don't have to do a repeat + if (Cs2Area->repcnt >= Cs2Area->maxrepeat) { + // we're done + Cs2Area->status = CDB_STAT_PAUSE; + Cs2SetTiming(0); + Cs2Area->reg.HIRQ |= CDB_HIRQ_PEND; + + if (Cs2Area->playtype == CDB_PLAYTYPE_FILE) + Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS; + + CDLOG("PLAY HAS ENDED\n"); + } + else { + + Cs2Area->FAD = Cs2Area->playFAD; + if (Cs2Area->repcnt < 0xE) + Cs2Area->repcnt++; + Cs2Area->track = Cs2FADToTrack(Cs2Area->FAD); + + CDLOG("PLAY HAS REPEATED\n"); + } + } + if (Cs2Area->isbufferfull) { + CDLOG("BUFFER IS FULL\n"); +// status = CDB_STAT_PAUSE; + } + } + else + { + CDLOG("Sector filtered out\n"); + if (Cs2Area->FAD >= Cs2Area->playendFAD) { + // Make sure we don't have to do a repeat + if (Cs2Area->repcnt >= Cs2Area->maxrepeat) { + // we're done + Cs2Area->status = CDB_STAT_PAUSE; + Cs2SetTiming(0); + Cs2Area->reg.HIRQ |= CDB_HIRQ_PEND; + + if (Cs2Area->playtype == CDB_PLAYTYPE_FILE) + Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS; + + CDLOG("PLAY HAS ENDED\n"); + } + else { + Cs2Area->FAD = Cs2Area->playFAD; + if (Cs2Area->repcnt < 0xE) + Cs2Area->repcnt++; + Cs2Area->track = Cs2FADToTrack(Cs2Area->FAD); + + CDLOG("PLAY HAS REPEATED\n"); + } + } + } + break; + case -1: + // Things weren't setup correctly + break; + case -2: + // Do a read retry + break; + } + + break; + } + case CDB_STAT_SEEK: + break; + case CDB_STAT_SCAN: + break; + case CDB_STAT_RETRY: + break; + default: break; + } + + if (Cs2Area->_command) + return; + + Cs2Area->status |= CDB_STAT_PERI; + + // adjust registers appropriately here(fix me) + doCDReport(Cs2Area->status); + + Cs2Area->reg.HIRQ |= CDB_HIRQ_SCDQ; + } + + if(Cs2Area->carttype == CART_NETLINK) + NetlinkExec(timing); +} + +////////////////////////////////////////////////////////////////////////////// + +/* Returns the number of (emulated) microseconds before the next sector + * will have been completely read in */ +int Cs2GetTimeToNextSector(void) { + if ((Cs2Area->status & 0xF) != CDB_STAT_PLAY) { + return 0; + } else { + // Round up, since the caller wants to know when it'll be safe to check + int time = (Cs2Area->_periodictiming - Cs2Area->_periodiccycles + 2) / 3; + return time<0 ? 0 : time; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2Command(void) { + Cs2Area->_command = 1; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetTiming(int playing) { + if (playing) { + if (Cs2Area->isaudio || Cs2Area->speed1x == 1) + Cs2Area->_periodictiming = 40000; // 13333.333... * 3 + else + Cs2Area->_periodictiming = 20000; // 6666.666... * 3 + } + else { + Cs2Area->_periodictiming = 50000; // 16666.666... * 3 + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetCommandTiming(u8 cmd) { + switch(cmd) { + default: + Cs2Area->_commandtiming = 1; + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2Execute(void) { + u16 instruction = Cs2Area->reg.CR1 >> 8; + + Cs2Area->reg.HIRQ &= ~CDB_HIRQ_CMOK; + + switch (instruction) { + case 0x00: + CDLOG("cs2\t: Command: getStatus\n"); + Cs2GetStatus(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x01: + CDLOG("cs2\t: Command: getHardwareInfo\n"); + Cs2GetHardwareInfo(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x02: + CDLOG("cs2\t: Command: getToc\n"); + Cs2GetToc(); + break; + case 0x03: + CDLOG("cs2\t: Command: getSessionInfo\n"); + Cs2GetSessionInfo(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x04: + CDLOG("cs2\t: Command: initializeCDSystem %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2InitializeCDSystem(); + break; + case 0x06: + CDLOG("cs2\t: Command: endDataTransfer %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2EndDataTransfer(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x10: + CDLOG("cs2\t: Command: playDisc %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2PlayDisc(); + break; + case 0x11: + CDLOG("cs2\t: Command: seekDisc %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2SeekDisc(); + break; + case 0x20: + CDLOG("cs2\t: Command: getSubcodeQRW %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetSubcodeQRW(); + break; + case 0x30: + CDLOG("cs2\t: Command: setCDDeviceConnection %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2SetCDDeviceConnection(); + break; + case 0x32: + CDLOG("cs2\t: Command: getLastBufferDestination %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetLastBufferDestination(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x40: + CDLOG("cs2\t: Command: setFilterRange\n"); + Cs2SetFilterRange(); + break; + case 0x42: + CDLOG("cs2\t: Command: setFilterSubheaderConditions\n"); + Cs2SetFilterSubheaderConditions(); + break; + case 0x43: + CDLOG("cs2\t: Command: getFilterSubheaderConditions\n"); + Cs2GetFilterSubheaderConditions(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x44: + CDLOG("cs2\t: Command: setFilterMode\n"); + Cs2SetFilterMode(); + break; + case 0x45: + CDLOG("cs2\t: Command: getFilterMode\n"); + Cs2GetFilterMode(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x46: + CDLOG("cs2\t: Command: setFilterConnection\n"); + Cs2SetFilterConnection(); + break; + case 0x48: + CDLOG("cs2\t: Command: resetSelector %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2ResetSelector(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x50: + CDLOG("cs2\t: Command: getBufferSize\n"); + Cs2GetBufferSize(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x51: +// CDLOG("cs2\t: Command: getSectorNumber %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetSectorNumber(); +// CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x52: + CDLOG("cs2\t: Command: calculateActualSize %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2CalculateActualSize(); + break; + case 0x53: + CDLOG("cs2\t: Command: getActualSize\n"); + Cs2GetActualSize(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x54: + CDLOG("cs2\t: Command: getSectorInfo %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetSectorInfo(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x60: + CDLOG("cs2\t: Command: setSectorLength %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2SetSectorLength(); + break; + case 0x61: + CDLOG("cs2\t: Command: getSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetSectorData(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x62: + CDLOG("cs2\t: Command: deleteSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2DeleteSectorData(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x63: + CDLOG("cs2\t: Command: getThenDeleteSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetThenDeleteSectorData(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x64: + CDLOG("cs2\t: Command: putSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2PutSectorData(); + break; + case 0x67: + CDLOG("cs2\t: Command: getCopyError\n"); + Cs2GetCopyError(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x70: + CDLOG("cs2\t: Command: changeDirectory %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2ChangeDirectory(); + break; + case 0x71: + CDLOG("cs2\t: Command: readDirectory %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2ReadDirectory(); + break; + case 0x72: + CDLOG("cs2\t: Command: getFileSystemScope\n"); + Cs2GetFileSystemScope(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x73: + CDLOG("cs2\t: Command: getFileInfo %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2GetFileInfo(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x74: + CDLOG("cs2\t: Command: readFile %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2ReadFile(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x75: + CDLOG("cs2\t: Command: abortFile\n"); + Cs2AbortFile(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x90: + CDLOG("cs2\t: Command: mpegGetStatus\n"); + Cs2MpegGetStatus(); + break; + case 0x91: + CDLOG("cs2\t: Command: mpegGetInterrupt\n"); + Cs2MpegGetInterrupt(); + CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + break; + case 0x92: + CDLOG("cs2\t: Command: mpegSetInterruptMask\n"); + Cs2MpegSetInterruptMask(); + break; + case 0x93: + CDLOG("cs2\t: Command: mpegInit\n"); + Cs2MpegInit(); + break; + case 0x94: + CDLOG("cs2\t: Command: mpegSetMode\n"); + Cs2MpegSetMode(); + break; + case 0x95: + CDLOG("cs2\t: Command: mpegPlay\n"); + Cs2MpegPlay(); + break; + case 0x96: + CDLOG("cs2\t: Command: mpegSetDecodingMethod\n"); + Cs2MpegSetDecodingMethod(); + break; + case 0x9A: + CDLOG("cs2\t: Command: mpegSetConnection\n"); + Cs2MpegSetConnection(); + break; + case 0x9B: + CDLOG("cs2\t: Command: mpegGetConnection\n"); + Cs2MpegGetConnection(); + break; + case 0x9D: + CDLOG("cs2\t: Command: mpegSetStream\n"); + Cs2MpegSetStream(); + break; + case 0x9E: + CDLOG("cs2\t: Command: mpegGetStream\n"); + Cs2MpegGetStream(); + break; + case 0xA0: + CDLOG("cs2\t: Command: mpegDisplay\n"); + Cs2MpegDisplay(); + break; + case 0xA1: + CDLOG("cs2\t: Command: mpegSetWindow\n"); + Cs2MpegSetWindow(); + break; + case 0xA2: + CDLOG("cs2\t: Command: mpegSetBorderColor\n"); + Cs2MpegSetBorderColor(); + break; + case 0xA3: + CDLOG("cs2\t: Command: mpegSetFade\n"); + Cs2MpegSetFade(); + break; + case 0xA4: + CDLOG("cs2\t: Command: mpegSetVideoEffects\n"); + Cs2MpegSetVideoEffects(); + break; + case 0xAF: + CDLOG("cs2\t: Command: mpegSetLSI\n"); + Cs2MpegSetLSI(); + break; + case 0xE0: + CDLOG("cs2\t: Command: cmdE0 %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2CmdE0(); + break; + case 0xE1: + CDLOG("cs2\t: Command: cmdE1 %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2CmdE1(); + break; + case 0xE2: + CDLOG("cs2\t: Command: cmdE2 %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); + Cs2CmdE2(); + break; + default: + CDLOG("cs2\t: Command %02x not implemented\n", instruction); + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetStatus(void) { + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetHardwareInfo(void) { + if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && (Cs2Area->status & 0xF) != CDB_STAT_NODISC) + Cs2Area->isdiskchanged = 0; + + Cs2Area->reg.CR1 = Cs2Area->status << 8; + // hardware flags/CD Version + Cs2Area->reg.CR2 = 0x0201; // mpeg card exists + // mpeg version, it actually is required(at least by the bios) + + if (Cs2Area->mpgauth) + Cs2Area->reg.CR3 = 0x1; + else + Cs2Area->reg.CR3 = 0; + + // drive info/revision + Cs2Area->reg.CR4 = 0x0400; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetToc(void) { + Cs2Area->cdi->ReadTOC(Cs2Area->TOC); + + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = 0; + + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0xCC; + Cs2Area->reg.CR3 = 0x0; + Cs2Area->reg.CR4 = 0x0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; + Cs2Area->status = CDB_STAT_PAUSE; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetSessionInfo(void) { + + switch (Cs2Area->reg.CR1 & 0xFF) { + case 0: + Cs2Area->reg.CR3 = (u16)(0x0100 | ((Cs2Area->TOC[101] & 0xFF0000) >> 16)); + Cs2Area->reg.CR4 = (u16)Cs2Area->TOC[101]; + break; + case 1: + Cs2Area->reg.CR3 = 0x0100; // return Session number(high byte)/and first byte of Session lba + Cs2Area->reg.CR4 = 0; // lower word of Session lba + break; + default: + Cs2Area->reg.CR3 = 0xFFFF; + Cs2Area->reg.CR4 = 0xFFFF; + break; + } + + Cs2Area->status = CDB_STAT_PAUSE; + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0; + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2InitializeCDSystem(void) { + u16 val = 0; + u8 initflag = Cs2Area->reg.CR1 & 0xFF; + + if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && (Cs2Area->status & 0xF) != CDB_STAT_NODISC) + { + Cs2Area->status = CDB_STAT_PAUSE; + Cs2Area->FAD = 150; + } + + if (initflag & 0x1) + { + // Reset CD block software + } + + if (initflag & 0x2) + { + // Decode RW subcode + } + + if (initflag & 0x4) + { + // Don't confirm Mode 2 subheader + } + + if (initflag & 0x8) + { + // Retry reading Form 2 sectors + } + + if (initflag & 0x10) + Cs2Area->speed1x = 1; + else + Cs2Area->speed1x = 0; + + val = Cs2Area->reg.HIRQ & 0xFFE5; + Cs2Area->isbufferfull = 0; + + if (Cs2Area->isdiskchanged) + val |= CDB_HIRQ_DCHG; + else + val &= ~CDB_HIRQ_DCHG; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ = val | CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2EndDataTransfer(void) { + s32 i; + if (Cs2Area->cdwnum) + { + Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->cdwnum >> 17) & 0xFF)); + Cs2Area->reg.CR2 = (u16)(Cs2Area->cdwnum >> 1); + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + } + else + { + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0xFF; // FIXME + Cs2Area->reg.CR2 = 0xFFFF; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + } + + // stop any transfers that may be going(this is still probably wrong), and + // set/clear the appropriate flags + + switch (Cs2Area->datatranstype) + { + case 0: + // Get Sector Data + Cs2Area->reg.HIRQ |= CDB_HIRQ_EHST; + break; + case 2: + { + // Get Then Delete Sector + + // Make sure we actually have to free something + if (Cs2Area->datatranspartition->size <= 0) break; + + Cs2Area->datatranstype = -1; + + // free blocks + for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos + Cs2Area->datasectstotrans); i++) + { + Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); + Cs2Area->datatranspartition->block[i] = NULL; + Cs2Area->datatranspartition->blocknum[i] = 0xFF; + } + + // sort remaining blocks + Cs2SortBlocks(Cs2Area->datatranspartition); + + Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; + Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; + + if (Cs2Area->blockfreespace == 200) Cs2Area->isonesectorstored = 0; + + Cs2Area->reg.HIRQ |= CDB_HIRQ_EHST; + break; + } + default: break; + } + + Cs2Area->cdwnum = 0; + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2PlayDisc(void) { + u32 pdspos; + u32 pdepos; + u32 pdpmode; + + // Get all the arguments + pdspos = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; + pdepos = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; + pdpmode = Cs2Area->reg.CR3 >> 8; + + // Convert Start Position to playFAD + if (pdspos == 0xFFFFFF || pdpmode == 0xFF) // This still isn't right + { + // No Change + } + else if (pdspos & 0x800000) + { + // FAD Mode + Cs2Area->playFAD = (pdspos & 0xFFFFF); + + Cs2SetupDefaultPlayStats(Cs2FADToTrack(Cs2Area->playFAD), 0); + + if (!(pdpmode & 0x80)) + // Move pickup to start position + Cs2Area->FAD = Cs2Area->playFAD; + } + else + { + // Track Mode + + // If track == 0, set it to the first available track, or something like that + if (pdspos == 0) + pdspos = 0x0100; + + if (!(pdpmode & 0x80)) + { + Cs2SetupDefaultPlayStats((u8)(pdspos >> 8), 1); + Cs2Area->playFAD = Cs2Area->FAD; + Cs2Area->track = (u8)(pdspos >> 8); + Cs2Area->index = (u8)pdspos; + } + else + { + // Preserve Pickup Position + Cs2SetupDefaultPlayStats((u8)(pdspos >> 8), 0); + } + } + + pdpmode &= 0x7F; + + // Only update max repeat if bits 0-6 aren't all set + if (pdpmode != 0x7F) + Cs2Area->maxrepeat = pdpmode; + + // Convert End Position to playendFAD + if (pdepos == 0xFFFFFF) + { + // No Change + } + else if (pdepos & 0x800000) + { + // FAD Mode + Cs2Area->playendFAD = Cs2Area->playFAD+(pdepos & 0xFFFFF); + } + else if (pdepos != 0) + { + // Track Mode + if ((pdepos & 0xFF) == 0) + Cs2Area->playendFAD = Cs2TrackToFAD((u16)(pdepos | 0x0063)); + else + Cs2Area->playendFAD = Cs2TrackToFAD((u16)pdepos); + } + else + { + // Default Mode + Cs2Area->playendFAD = Cs2TrackToFAD(0xFFFF); + } + + // setup play mode here +#if CDDEBUG + if (pdpmode != 0) + CDLOG("cs2\t: playDisc: Unsupported play mode = %02X\n", pdpmode); +#endif + + Cs2SetTiming(1); + + Cs2Area->status = CDB_STAT_PLAY; + Cs2Area->playtype = CDB_PLAYTYPE_SECTOR; + Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SeekDisc(void) { + if (Cs2Area->reg.CR1 & 0x80) + { + // Seek by FAD + u32 sdFAD; + + sdFAD = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; + + if (sdFAD == 0xFFFFFF) + Cs2Area->status = CDB_STAT_PAUSE; + else + { + CDLOG("cs2\t: seekDisc - FAD Mode not supported\n"); + } + } + else + { + // Were we given a valid track number? + if (Cs2Area->reg.CR2 >> 8) + { + // Seek by index + Cs2Area->status = CDB_STAT_PAUSE; + Cs2SetupDefaultPlayStats((Cs2Area->reg.CR2 >> 8), 1); + Cs2Area->index = Cs2Area->reg.CR2 & 0xFF; + } + else + { + // Error + Cs2Area->status = CDB_STAT_STANDBY; + Cs2Area->options = 0xFF; + Cs2Area->repcnt = 0xFF; + Cs2Area->ctrladdr = 0xFF; + Cs2Area->track = 0xFF; + Cs2Area->index = 0xFF; + Cs2Area->FAD = 0xFFFFFFFF; + } + } + + Cs2SetTiming(0); + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetSubcodeQRW(void) { + // According to Tyranid's doc, the subcode type is stored in the low byte + // of CR2. However, Sega's CDC library writes the type to the low byte + // of CR1. Somehow I'd sooner believe Sega is right. + switch(Cs2Area->reg.CR1 & 0xFF) { + case 0: + // Get Q Channel + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0; + Cs2Area->reg.CR2 = 5; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + + // setup transfer here(fix me) + break; + case 1: + // Get RW Channel + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0; + Cs2Area->reg.CR2 = 12; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + + // setup transfer here(fix me) + break; + default: break; + } + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetCDDeviceConnection(void) { + u32 scdcfilternum; + + scdcfilternum = (Cs2Area->reg.CR3 >> 8); + + if (scdcfilternum == 0xFF) + Cs2Area->outconcddev = NULL; + else if (scdcfilternum < 0x24) + Cs2Area->outconcddev = Cs2Area->filter + scdcfilternum; + + Cs2Area->outconcddevnum = (u8)scdcfilternum; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetLastBufferDestination(void) { + Cs2Area->reg.CR1 = (Cs2Area->status << 8); + Cs2Area->reg.CR2 = 0; + Cs2Area->reg.CR3 = Cs2Area->lastbuffer << 8; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetFilterRange(void) { + u8 sfrfilternum; + + sfrfilternum = Cs2Area->reg.CR3 >> 8; + + Cs2Area->filter[sfrfilternum].FAD = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; + Cs2Area->filter[sfrfilternum].range = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; + + // return default cd stats + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetFilterSubheaderConditions(void) { + u8 sfscfilternum; + + sfscfilternum = Cs2Area->reg.CR3 >> 8; + + Cs2Area->filter[sfscfilternum].chan = Cs2Area->reg.CR1 & 0xFF; + Cs2Area->filter[sfscfilternum].smmask = Cs2Area->reg.CR2 >> 8; + Cs2Area->filter[sfscfilternum].cimask = Cs2Area->reg.CR2 & 0xFF; + Cs2Area->filter[sfscfilternum].fid = Cs2Area->reg.CR3 & 0xFF;; + Cs2Area->filter[sfscfilternum].smval = Cs2Area->reg.CR4 >> 8; + Cs2Area->filter[sfscfilternum].cival = Cs2Area->reg.CR4 & 0xFF; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetFilterSubheaderConditions(void) { + u8 gfscfilternum; + + gfscfilternum = Cs2Area->reg.CR3 >> 8; + + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->filter[gfscfilternum].chan; + Cs2Area->reg.CR2 = (Cs2Area->filter[gfscfilternum].smmask << 8) | Cs2Area->filter[gfscfilternum].cimask; + Cs2Area->reg.CR3 = Cs2Area->filter[gfscfilternum].fid; + Cs2Area->reg.CR4 = (Cs2Area->filter[gfscfilternum].smval << 8) | Cs2Area->filter[gfscfilternum].cival; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetFilterMode(void) { + u8 sfmfilternum; + + sfmfilternum = Cs2Area->reg.CR3 >> 8; + + Cs2Area->filter[sfmfilternum].mode = Cs2Area->reg.CR1 & 0xFF; + + if (Cs2Area->filter[sfmfilternum].mode & 0x80) + { + // Initialize filter conditions + Cs2Area->filter[sfmfilternum].mode = 0; + Cs2Area->filter[sfmfilternum].FAD = 0; + Cs2Area->filter[sfmfilternum].range = 0; + Cs2Area->filter[sfmfilternum].chan = 0; + Cs2Area->filter[sfmfilternum].smmask = 0; + Cs2Area->filter[sfmfilternum].cimask = 0; + Cs2Area->filter[sfmfilternum].smval = 0; + Cs2Area->filter[sfmfilternum].cival = 0; + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetFilterMode(void) { + u8 gfmfilternum; + + gfmfilternum = Cs2Area->reg.CR3 >> 8; + + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->filter[gfmfilternum].mode; + Cs2Area->reg.CR2 = 0; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetFilterConnection(void) { + u8 sfcfilternum; + + sfcfilternum = Cs2Area->reg.CR3 >> 8; + + if (Cs2Area->reg.CR1 & 0x1) + { + // Set connection for true condition + Cs2Area->filter[sfcfilternum].condtrue = Cs2Area->reg.CR2 >> 8; + } + + if (Cs2Area->reg.CR1 & 0x2) + { + // Set connection for false condition + Cs2Area->filter[sfcfilternum].condfalse = Cs2Area->reg.CR2 & 0xFF; + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2ResetSelector(void) { + // still needs a bit of work + u32 i, i2; + + if ((Cs2Area->reg.CR1 & 0xFF) == 0) + { + // Reset specified partition buffer only + u32 rsbufno = Cs2Area->reg.CR3 >> 8; + + // sort remaining blocks + if (rsbufno < MAX_SELECTORS) + { + // clear partition + for (i = 0; i < Cs2Area->partition[rsbufno].numblocks; i++) + { + Cs2FreeBlock(Cs2Area->partition[rsbufno].block[i]); + Cs2Area->partition[rsbufno].block[i] = NULL; + Cs2Area->partition[rsbufno].blocknum[i] = 0xFF; + } + + Cs2Area->partition[rsbufno].size = -1; + Cs2Area->partition[rsbufno].numblocks = 0; + } + + if (Cs2Area->blockfreespace > 0) Cs2Area->isbufferfull = 0; + if (Cs2Area->blockfreespace == 200) Cs2Area->isonesectorstored = 0; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; + return; + } + + // parse flags and reset the specified area(fix me) + if (Cs2Area->reg.CR1 & 0x80) + { + // reset false filter output connections + for (i = 0; i < MAX_SELECTORS; i++) + Cs2Area->filter[i].condfalse = 0xFF; + } + + if (Cs2Area->reg.CR1 & 0x40) + { + // reset true filter output connections + for (i = 0; i < MAX_SELECTORS; i++) + Cs2Area->filter[i].condtrue = (u8)i; + } + + if (Cs2Area->reg.CR1 & 0x10) + { + // reset filter conditions + for (i = 0; i < MAX_SELECTORS; i++) + { + Cs2Area->filter[i].FAD = 0; + Cs2Area->filter[i].range = 0xFFFFFFFF; + Cs2Area->filter[i].mode = 0; + Cs2Area->filter[i].chan = 0; + Cs2Area->filter[i].smmask = 0; + Cs2Area->filter[i].cimask = 0; + Cs2Area->filter[i].fid = 0; + Cs2Area->filter[i].smval = 0; + Cs2Area->filter[i].cival = 0; + } + } + + if (Cs2Area->reg.CR1 & 0x8) + { + // reset partition output connectors + } + + if (Cs2Area->reg.CR1 & 0x4) + { + // reset partitions buffer data + Cs2Area->isbufferfull = 0; + + // clear partitions + for (i = 0; i < MAX_SELECTORS; i++) + { + Cs2Area->partition[i].size = -1; + Cs2Area->partition[i].numblocks = 0; + + for (i2 = 0; i2 < MAX_BLOCKS; i2++) + { + Cs2Area->partition[i].block[i2] = NULL; + Cs2Area->partition[i].blocknum[i2] = 0xFF; + } + } + + // clear blocks + for (i = 0; i < MAX_BLOCKS; i++) + { + Cs2Area->block[i].size = -1; + memset(Cs2Area->block[i].data, 0, 2352); + } + + Cs2Area->isonesectorstored = 0; + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetBufferSize(void) { + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = (u16)Cs2Area->blockfreespace; + Cs2Area->reg.CR3 = MAX_SELECTORS << 8; + Cs2Area->reg.CR4 = MAX_BLOCKS; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetSectorNumber(void) { + u32 gsnbufno; + + gsnbufno = Cs2Area->reg.CR3 >> 8; + + if (Cs2Area->partition[gsnbufno].size == -1) + Cs2Area->reg.CR4 = 0; + else + Cs2Area->reg.CR4 = Cs2Area->partition[gsnbufno].numblocks; + + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2CalculateActualSize(void) { + u16 i; + u32 casbufno; + u16 cassectoffset; + u16 casnumsect; + + cassectoffset = Cs2Area->reg.CR2; + casbufno = Cs2Area->reg.CR3 >> 8; + casnumsect = Cs2Area->reg.CR4; + + if (Cs2Area->partition[casbufno].size != 0) + { + Cs2Area->calcsize = 0; + + for (i = 0; i < casnumsect; i++) + { + if (Cs2Area->partition[casbufno].block[cassectoffset]) + Cs2Area->calcsize += (Cs2Area->partition[casbufno].block[cassectoffset]->size / 2); + } + } + else + Cs2Area->calcsize = 0; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetActualSize(void) { + Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->calcsize >> 16) & 0xFF)); + Cs2Area->reg.CR2 = (u16)Cs2Area->calcsize; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetSectorInfo(void) { + u32 gsisctnum; + u32 gsibufno; + + gsisctnum = Cs2Area->reg.CR2 & 0xFF; + gsibufno = Cs2Area->reg.CR3 >> 8; + if (gsibufno < MAX_SELECTORS) { + if (gsisctnum < Cs2Area->partition[gsibufno].numblocks) { + Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->partition[gsibufno].block[gsisctnum]->FAD >> 16) & 0xFF)); + Cs2Area->reg.CR2 = (u16)Cs2Area->partition[gsibufno].block[gsisctnum]->FAD; + Cs2Area->reg.CR3 = (Cs2Area->partition[gsibufno].block[gsisctnum]->fn << 8) | Cs2Area->partition[gsibufno].block[gsisctnum]->cn; + Cs2Area->reg.CR4 = (Cs2Area->partition[gsibufno].block[gsisctnum]->sm << 8) | Cs2Area->partition[gsibufno].block[gsisctnum]->ci; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; + return; + } + else + { + CDLOG("cs2\t: getSectorInfo: Unsupported Partition Number\n"); + } + } + + Cs2Area->reg.CR1 = (CDB_STAT_REJECT << 8) | (Cs2Area->reg.CR1 & 0xFF); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetSectorLength(void) { + switch (Cs2Area->reg.CR1 & 0xFF) { + case 0: + Cs2Area->getsectsize = 2048; + break; + case 1: + Cs2Area->getsectsize = 2336; + break; + case 2: + Cs2Area->getsectsize = 2340; + break; + case 3: + Cs2Area->getsectsize = 2352; + break; + default: break; + } + + switch (Cs2Area->reg.CR2 >> 8) { + case 0: + Cs2Area->putsectsize = 2048; + break; + case 1: + Cs2Area->putsectsize = 2336; + break; + case 2: + Cs2Area->putsectsize = 2340; + break; + case 3: + Cs2Area->putsectsize = 2352; + break; + default: break; + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE void CalcSectorOffsetNumber(u32 bufno, u32 *sectoffset, u32 *sectnum) +{ + if (*sectoffset == 0xFFFF) + { + // Last sector + CDLOG("FIXME - Sector offset of 0xFFFF not supported\n"); + } + else if (*sectnum == 0xFFFF) + { + // From sectoffset to last sector in partition + *sectnum = Cs2Area->partition[bufno].numblocks - *sectoffset; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetSectorData(void) +{ + u32 gsdsectoffset; + u32 gsdbufno; + u32 gsdsectnum; + + gsdsectoffset = Cs2Area->reg.CR2; + gsdbufno = Cs2Area->reg.CR3 >> 8; + gsdsectnum = Cs2Area->reg.CR4; + + if (gsdbufno >= MAX_SELECTORS) + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + if (Cs2Area->partition[gsdbufno].numblocks == 0) + { + CDLOG("No sectors available\n"); + + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + CalcSectorOffsetNumber(gsdbufno, &gsdsectoffset, &gsdsectnum); + + // Setup Data Transfer + Cs2Area->cdwnum = 0; + Cs2Area->datatranstype = 0; + Cs2Area->datatranspartition = Cs2Area->partition + gsdbufno; + Cs2Area->datatranspartitionnum = (u8)gsdbufno; + Cs2Area->datatransoffset = 0; + Cs2Area->datanumsecttrans = 0; + Cs2Area->datatranssectpos = (u16)gsdsectoffset; + Cs2Area->datasectstotrans = (u16)gsdsectnum; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY | CDB_HIRQ_EHST; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2DeleteSectorData(void) +{ + u32 dsdsectoffset; + u32 dsdbufno; + u32 dsdsectnum; + u32 i; + + dsdsectoffset = Cs2Area->reg.CR2; + dsdbufno = Cs2Area->reg.CR3 >> 8; + dsdsectnum = Cs2Area->reg.CR4; + + if (dsdbufno >= MAX_SELECTORS) + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + if (Cs2Area->partition[dsdbufno].numblocks == 0) + { + CDLOG("No sectors available\n"); + + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + CalcSectorOffsetNumber(dsdbufno, &dsdsectoffset, &dsdsectnum); + + for (i = dsdsectoffset; i < (dsdsectoffset+dsdsectnum); i++) + { + Cs2Area->partition[dsdbufno].size -= Cs2Area->partition[dsdbufno].block[i]->size; + Cs2FreeBlock(Cs2Area->partition[dsdbufno].block[i]); + Cs2Area->partition[dsdbufno].block[i] = NULL; + Cs2Area->partition[dsdbufno].blocknum[i] = 0xFF; + } + + // sort remaining blocks + Cs2SortBlocks(&Cs2Area->partition[dsdbufno]); + + Cs2Area->partition[dsdbufno].numblocks -= (u8)dsdsectnum; + + if (Cs2Area->blockfreespace == 200) + Cs2Area->isonesectorstored = 0; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetThenDeleteSectorData(void) +{ + u32 gtdsdsectoffset; + u32 gtdsdbufno; + u32 gtdsdsectnum; + + gtdsdsectoffset = Cs2Area->reg.CR2; + gtdsdbufno = Cs2Area->reg.CR3 >> 8; + gtdsdsectnum = Cs2Area->reg.CR4; + + if (gtdsdbufno >= MAX_SELECTORS) + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + if (Cs2Area->partition[gtdsdbufno].numblocks == 0) + { + CDLOG("No sectors available\n"); + + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + return; + } + + CalcSectorOffsetNumber(gtdsdbufno, >dsdsectoffset, >dsdsectnum); + + // Setup Data Transfer + Cs2Area->cdwnum = 0; + Cs2Area->datatranstype = 2; + Cs2Area->datatranspartition = Cs2Area->partition + gtdsdbufno; + Cs2Area->datatransoffset = 0; + Cs2Area->datanumsecttrans = 0; + Cs2Area->datatranssectpos = (u16)gtdsdsectoffset; + Cs2Area->datasectstotrans = (u16)gtdsdsectnum; + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY | CDB_HIRQ_EHST; + + return; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2PutSectorData(void) { + u32 psdfiltno; + + psdfiltno = Cs2Area->reg.CR3 >> 8; + + if (psdfiltno < MAX_SELECTORS) + { + // I'm not really sure what I'm supposed to really be doing or returning + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + } + else + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetCopyError(void) { + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2ChangeDirectory(void) { + u32 cdfilternum; + + cdfilternum = (Cs2Area->reg.CR3 >> 8); + + if (cdfilternum == 0xFF) + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; + return; + } + else if (cdfilternum < 0x24) + { + if (Cs2ReadFileSystem(Cs2Area->filter + cdfilternum, ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4, 0) != 0) + { + CDLOG("cs2\t: ReadFileSystem failed\n"); + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; + return; + } + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2ReadDirectory(void) { + u32 rdfilternum; + + rdfilternum = (Cs2Area->reg.CR3 >> 8); + + if (rdfilternum == 0xFF) + { + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; + return; + } + else if (rdfilternum < 0x24) + { + if (Cs2ReadFileSystem(Cs2Area->filter + rdfilternum, ((Cs2Area->reg.CR3 & 0xFF) << 8) | Cs2Area->reg.CR4, 1) != 0) + { + CDLOG("cs2\t: ReadFileSystem failed\n"); + doCDReport(CDB_STAT_REJECT); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; + return; + } + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetFileSystemScope(void) { + // may need to fix this + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = (u16)(Cs2Area->numfiles - 2); + Cs2Area->reg.CR3 = 0x0100; + Cs2Area->reg.CR4 = 0x0002; + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2GetFileInfo(void) { + u32 gfifid; + + gfifid = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; + + if (gfifid == 0xFFFFFF) + { + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = 2; + + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0x05F4; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + } + else + { + Cs2SetupFileInfoTransfer(gfifid); + + Cs2Area->transfercount = 0; + Cs2Area->infotranstype = 1; + + Cs2Area->reg.CR1 = Cs2Area->status << 8; + Cs2Area->reg.CR2 = 0x06; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + } + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2ReadFile(void) { + u32 rfoffset, rffilternum, rffid, rfsize; + + rfoffset = ((Cs2Area->reg.CR1 & 0xFF) << 8) | Cs2Area->reg.CR2; + rffilternum = Cs2Area->reg.CR3 >> 8; + rffid = ((Cs2Area->reg.CR3 & 0xFF) << 8) | Cs2Area->reg.CR4; + rfsize = ((Cs2Area->fileinfo[rffid].size + Cs2Area->getsectsize - 1) / + Cs2Area->getsectsize) - rfoffset; + + Cs2SetupDefaultPlayStats(Cs2FADToTrack(Cs2Area->fileinfo[rffid].lba + rfoffset), 0); + Cs2Area->maxrepeat = 0; + + Cs2Area->playFAD = Cs2Area->FAD = Cs2Area->fileinfo[rffid].lba + rfoffset; + Cs2Area->playendFAD = Cs2Area->playFAD + rfsize; + + Cs2Area->options = 0x8; + + Cs2SetTiming(1); + + Cs2Area->outconcddev = Cs2Area->filter + rffilternum; + + Cs2Area->status = CDB_STAT_PLAY; + Cs2Area->playtype = CDB_PLAYTYPE_FILE; + Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2AbortFile(void) { + if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && + (Cs2Area->status & 0xF) != CDB_STAT_NODISC) + Cs2Area->status = CDB_STAT_PAUSE; + Cs2Area->isonesectorstored = 0; + Cs2Area->datatranstype = -1; + Cs2Area->cdwnum = 0; + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegGetStatus(void) { + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegGetInterrupt(void) { + u32 mgiworkinterrupt; + + // mpeg interrupt should be retrieved here + mgiworkinterrupt = 0; + + // mask interupt + mgiworkinterrupt &= Cs2Area->mpegintmask; + + Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((mgiworkinterrupt >> 16) & 0xFF)); + Cs2Area->reg.CR2 = (u16) mgiworkinterrupt; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetInterruptMask(void) { + Cs2Area->mpegintmask = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegInit(void) { + + if (Cs2Area->mpgauth) + Cs2Area->reg.CR1 = Cs2Area->status << 8; + else + Cs2Area->reg.CR1 = 0xFF00; + + // double-check this + if (Cs2Area->reg.CR2 == 0x0001) // software timer/reset? + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM | CDB_HIRQ_MPED | CDB_HIRQ_MPST; + else + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPED | CDB_HIRQ_MPST; + + Cs2Area->reg.CR2 = 0; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + + // future mpeg-related variables should be initialized here +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetMode(void) { + // fix me + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegPlay(void) { + // fix me + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetDecodingMethod(void) { + // fix me + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetConnection(void) { + int mscnext = (Cs2Area->reg.CR3 >> 8); + + if (mscnext == 0) + { + // Current + Cs2Area->mpegcon[0].audcon = Cs2Area->reg.CR1 & 0xFF; + Cs2Area->mpegcon[0].audlay = Cs2Area->reg.CR2 >> 8; + Cs2Area->mpegcon[0].audbufdivnum = Cs2Area->reg.CR2 & 0xFF; + Cs2Area->mpegcon[0].vidcon = Cs2Area->reg.CR3 & 0xFF; + Cs2Area->mpegcon[0].vidlay = Cs2Area->reg.CR4 >> 8; + Cs2Area->mpegcon[0].vidbufdivnum = Cs2Area->reg.CR4 & 0xFF; + } + else + { + // Next + Cs2Area->mpegcon[1].audcon = Cs2Area->reg.CR1 & 0xFF; + Cs2Area->mpegcon[1].audlay = Cs2Area->reg.CR2 >> 8; + Cs2Area->mpegcon[1].audbufdivnum = Cs2Area->reg.CR2 & 0xFF; + Cs2Area->mpegcon[1].vidcon = Cs2Area->reg.CR3 & 0xFF; + Cs2Area->mpegcon[1].vidlay = Cs2Area->reg.CR4 >> 8; + Cs2Area->mpegcon[1].vidbufdivnum = Cs2Area->reg.CR4 & 0xFF; + } + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegGetConnection(void) { + int mgcnext = (Cs2Area->reg.CR3 >> 8); + + if (mgcnext == 0) + { + // Current + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegcon[0].audcon; + Cs2Area->reg.CR2 = (Cs2Area->mpegcon[0].audlay << 8) | Cs2Area->mpegcon[0].audbufdivnum; + Cs2Area->reg.CR3 = Cs2Area->mpegcon[0].vidcon; + Cs2Area->reg.CR4 = (Cs2Area->mpegcon[0].vidlay << 8) | Cs2Area->mpegcon[0].vidbufdivnum; + } + else + { + // Next + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegcon[1].audcon; + Cs2Area->reg.CR2 = (Cs2Area->mpegcon[1].audlay << 8) | Cs2Area->mpegcon[1].audbufdivnum; + Cs2Area->reg.CR3 = Cs2Area->mpegcon[1].vidcon; + Cs2Area->reg.CR4 = (Cs2Area->mpegcon[1].vidlay << 8) | Cs2Area->mpegcon[1].vidbufdivnum; + } + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetStream(void) { + int mssnext = (Cs2Area->reg.CR3 >> 8); + + if (mssnext == 0) + { + // Current + Cs2Area->mpegstm[0].audstm = Cs2Area->reg.CR1 & 0xFF; + Cs2Area->mpegstm[0].audstmid = Cs2Area->reg.CR2 >> 8; + Cs2Area->mpegstm[0].audchannum = Cs2Area->reg.CR2 & 0xFF; + Cs2Area->mpegstm[0].vidstm = Cs2Area->reg.CR3 & 0xFF; + Cs2Area->mpegstm[0].vidstmid = Cs2Area->reg.CR4 >> 8; + Cs2Area->mpegstm[0].vidchannum = Cs2Area->reg.CR4 & 0xFF; + } + else + { + // Next + Cs2Area->mpegstm[1].audstm = Cs2Area->reg.CR1 & 0xFF; + Cs2Area->mpegstm[1].audstmid = Cs2Area->reg.CR2 >> 8; + Cs2Area->mpegstm[1].audchannum = Cs2Area->reg.CR2 & 0xFF; + Cs2Area->mpegstm[1].vidstm = Cs2Area->reg.CR3 & 0xFF; + Cs2Area->mpegstm[1].vidstmid = Cs2Area->reg.CR4 >> 8; + Cs2Area->mpegstm[1].vidchannum = Cs2Area->reg.CR4 & 0xFF; + } + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegGetStream(void) { + int mgsnext = (Cs2Area->reg.CR3 >> 8); + + if (mgsnext == 0) + { + // Current + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegstm[0].audstm; + Cs2Area->reg.CR2 = (Cs2Area->mpegstm[0].audstmid << 8) | Cs2Area->mpegstm[0].audchannum; + Cs2Area->reg.CR3 = Cs2Area->mpegstm[0].vidstm; + Cs2Area->reg.CR4 = (Cs2Area->mpegstm[0].vidstmid << 8) | Cs2Area->mpegstm[0].vidchannum; + } + else + { + // Next + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegstm[1].audstm; + Cs2Area->reg.CR2 = (Cs2Area->mpegstm[1].audstmid << 8) | Cs2Area->mpegstm[1].audchannum; + Cs2Area->reg.CR3 = Cs2Area->mpegstm[1].vidstm; + Cs2Area->reg.CR4 = (Cs2Area->mpegstm[1].vidstmid << 8) | Cs2Area->mpegstm[1].vidchannum; + } + + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegDisplay(void) { + // fix me(should be setting display setting) + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetWindow(void) { + // fix me(should be setting windows settings) + + // return default mpeg stats + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetBorderColor(void) { + // fix me(should be setting border color) + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetFade(void) { + // fix me(should be setting fade setting) + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetVideoEffects(void) { + // fix me(should be setting video effects settings) + + doMPEGReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2MpegSetLSI(void) { + // fix me(should be setting the LSI, among other things) + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2CmdE0(void) { + int mpegauth; + + mpegauth = Cs2Area->reg.CR2 & 0xFF; + + + if ((Cs2Area->status & 0xF) != CDB_STAT_NODISC && + (Cs2Area->status & 0xF) != CDB_STAT_OPEN) + { + // Set registers all to invalid values(aside from status) + Cs2Area->status = CDB_STAT_BUSY; + + Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0xFF; + Cs2Area->reg.CR2 = 0xFFFF; + Cs2Area->reg.CR3 = 0xFFFF; + Cs2Area->reg.CR4 = 0xFFFF; + + if (mpegauth == 1) + { + Cs2Area->reg.HIRQ |= CDB_HIRQ_MPED; + Cs2Area->mpgauth = 2; + } + else + { + // if authentication passes(obviously it always does), CDB_HIRQ_CSCT is set + Cs2Area->isonesectorstored = 1; + Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS | CDB_HIRQ_CSCT; + Cs2Area->satauth = 4; + } + + // Set registers all back to normal values + + Cs2Area->status = CDB_STAT_PAUSE; + } + else + { + if (mpegauth == 1) + { + Cs2Area->reg.HIRQ |= CDB_HIRQ_MPED; + Cs2Area->mpgauth = 2; + } + else + Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS | CDB_HIRQ_CSCT; + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2CmdE1(void) { + Cs2Area->reg.CR1 = (Cs2Area->status << 8); + if (Cs2Area->reg.CR2) + Cs2Area->reg.CR2 = Cs2Area->mpgauth; + else + Cs2Area->reg.CR2 = Cs2Area->satauth; + Cs2Area->reg.CR3 = 0; + Cs2Area->reg.CR4 = 0; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2CmdE2(void) { + u16 i; + FILE * mpgfp; + partition_struct * mpgpartition; + + // fix me + Cs2Area->mpgauth |= 0x300; + + Cs2Area->outconmpegrom = Cs2Area->filter + 0; + Cs2Area->outconmpegromnum = 0; + + if (Cs2Area->mpegpath && (mpgfp = fopen(Cs2Area->mpegpath, "rb")) != NULL) + { + u32 readoffset = ((Cs2Area->reg.CR1 & 0xFF) << 8) | Cs2Area->reg.CR2; + u16 readsize = Cs2Area->reg.CR4; + + fseek(mpgfp, readoffset * Cs2Area->getsectsize, SEEK_SET); + if ((mpgpartition = Cs2GetPartition(Cs2Area->outconmpegrom)) != NULL && !Cs2Area->isbufferfull) + { + IOCheck_struct check; + mpgpartition->size = 0; + + for (i = 0; i < readsize; i++) + { + mpgpartition->block[mpgpartition->numblocks] = Cs2AllocateBlock(&mpgpartition->blocknum[mpgpartition->numblocks]); + + if (mpgpartition->block[mpgpartition->numblocks] != NULL) { + // read data + yread(&check, (void *)mpgpartition->block[mpgpartition->numblocks]->data, 1, Cs2Area->getsectsize, mpgfp); + + mpgpartition->numblocks++; + mpgpartition->size += Cs2Area->getsectsize; + } + } + + Cs2Area->isonesectorstored = 1; + Cs2Area->reg.HIRQ |= CDB_HIRQ_CSCT; + } + + fclose(mpgfp); + } + + doCDReport(Cs2Area->status); + Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPED; +} + +////////////////////////////////////////////////////////////////////////////// + +u8 Cs2FADToTrack(u32 val) { + int i; + for (i = 0; i < 99; i++) + { + if (Cs2Area->TOC[i] == 0xFFFFFFFF) return 0xFF; + + if (val >= (Cs2Area->TOC[i] & 0xFFFFFF) && val < (Cs2Area->TOC[i + 1] & 0xFFFFFF)) + return (i + 1); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 Cs2TrackToFAD(u16 trackandindex) { + if (trackandindex == 0xFFFF) + // leadout position + return (Cs2Area->TOC[101] & 0x00FFFFFF); + if (trackandindex != 0x0000) + { + // regular track + // (really, we should be fetching subcode q's here) + if ((trackandindex & 0xFF) == 0x01) + // Return Start of Track + return (Cs2Area->TOC[(trackandindex >> 8) - 1] & 0x00FFFFFF); + else if ((trackandindex & 0xFF) == 0x63) + // Return End of Track + return ((Cs2Area->TOC[(trackandindex >> 8)] & 0x00FFFFFF) - 1); + } + + // assume it's leadin + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetupDefaultPlayStats(u8 track_number, int writeFAD) { + if (track_number != 0xFF) + { + Cs2Area->options = 0; + Cs2Area->repcnt = 0; + Cs2Area->ctrladdr = (u8)(Cs2Area->TOC[track_number - 1] >> 24); + Cs2Area->index = 1; + Cs2Area->track = track_number; + if (writeFAD) + Cs2Area->FAD = Cs2Area->TOC[track_number - 1] & 0x00FFFFFF; + } +} + +////////////////////////////////////////////////////////////////////////////// + +block_struct * Cs2AllocateBlock(u8 * blocknum) { + u32 i; + // find a free block + for(i = 0; i < 200; i++) + { + if (Cs2Area->block[i].size == -1) + { + Cs2Area->blockfreespace--; + + if (Cs2Area->blockfreespace <= 0) Cs2Area->isbufferfull = 1; + + Cs2Area->block[i].size = Cs2Area->getsectsize; + + *blocknum = (u8)i; + return (Cs2Area->block + i); + } + } + + Cs2Area->isbufferfull = 1; + + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2FreeBlock(block_struct * blk) { + if (blk == NULL) return; + blk->size = -1; + Cs2Area->blockfreespace++; + Cs2Area->isbufferfull = 0; + Cs2Area->reg.HIRQ &= ~CDB_HIRQ_BFUL; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SortBlocks(partition_struct * part) { + unsigned int from, to; + + for (from = to = 0; from < MAX_BLOCKS; from++) + { + if (part->block[from] != NULL) + { + if (to != from) + { + part->block[to] = part->block[from]; + } + to++; + } + } + + for (; to < MAX_BLOCKS; to++) { + part->block[to] = NULL; + } +} + +////////////////////////////////////////////////////////////////////////////// + +partition_struct * Cs2GetPartition(filter_struct * curfilter) +{ + // go through various filter conditions here(fix me) + + return &Cs2Area->partition[curfilter->condtrue]; +} + +////////////////////////////////////////////////////////////////////////////// + +partition_struct * Cs2FilterData(filter_struct * curfilter, int isaudio) +{ + int condresults = 1; + partition_struct * fltpartition = NULL; + + // fix me, this is pretty bad. Though I guess it's a start + + for (;;) + { + // detect which type of sector we're dealing with + // If it's not mode 2, ignore the subheader conditions + if (Cs2Area->workblock.data[0xF] == 0x02 && !isaudio) + { + // Mode 2 + // go through various subheader filter conditions here(fix me) + + if (curfilter->mode & 0x01) + { + // File Number Check + if (Cs2Area->workblock.fn != curfilter->fid) + condresults = 0; + } + + if (curfilter->mode & 0x02) + { + // Channel Number Check + if (Cs2Area->workblock.cn != curfilter->chan) + condresults = 0; + } + + if (curfilter->mode & 0x04) + { + // Sub Mode Check + if ((Cs2Area->workblock.sm & curfilter->smmask) != curfilter->smval) + condresults = 0; + } + + if (curfilter->mode & 0x08) + { + // Coding Information Check + CDLOG("cs2\t: FilterData: Coding Information Check. Coding Information = %02X. Filter's Coding Information Mask = %02X, Coding Information Value = %02X\n", Cs2Area->workblock.ci, curfilter->cimask, curfilter->cival); + if ((Cs2Area->workblock.ci & curfilter->cimask) != curfilter->cival) + condresults = 0; + } + + if (curfilter->mode & 0x10) + { + // Reverse Subheader Conditions + CDLOG("cs2\t: FilterData: Reverse Subheader Conditions\n"); + condresults ^= 1; + } + } + + if (curfilter->mode & 0x40) + { + // FAD Range Check + if (Cs2Area->workblock.FAD < curfilter->FAD || + Cs2Area->workblock.FAD > (curfilter->FAD+curfilter->range)) + condresults = 0; + } + + if (condresults == 1) + { + Cs2Area->lastbuffer = curfilter->condtrue; + fltpartition = &Cs2Area->partition[curfilter->condtrue]; + break; + } + else + { + Cs2Area->lastbuffer = curfilter->condfalse; + + if (curfilter->condfalse == 0xFF) + return NULL; + // loop and try filter that was connected to the false connector + curfilter = &Cs2Area->filter[curfilter->condfalse]; + } + } + + // Allocate block + fltpartition->block[fltpartition->numblocks] = Cs2AllocateBlock(&fltpartition->blocknum[fltpartition->numblocks]); + + if (fltpartition->block[fltpartition->numblocks] == NULL) + return NULL; + + // Copy workblock settings to allocated block + fltpartition->block[fltpartition->numblocks]->size = Cs2Area->workblock.size; + fltpartition->block[fltpartition->numblocks]->FAD = Cs2Area->workblock.FAD; + fltpartition->block[fltpartition->numblocks]->cn = Cs2Area->workblock.cn; + fltpartition->block[fltpartition->numblocks]->fn = Cs2Area->workblock.fn; + fltpartition->block[fltpartition->numblocks]->sm = Cs2Area->workblock.sm; + fltpartition->block[fltpartition->numblocks]->ci = Cs2Area->workblock.ci; + + // convert raw sector to type specified in getsectsize + switch(Cs2Area->workblock.size) + { + case 2048: // user data only + if (Cs2Area->workblock.data[0xF] == 0x02) + // m2f1 + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data + 24, Cs2Area->workblock.size); + else + // m1 + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data + 16, Cs2Area->workblock.size); + break; + case 2324: // m2f2 user data only + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data + 24, Cs2Area->workblock.size); + break; + case 2336: // m2f2 skip sync+header data + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data + 16, Cs2Area->workblock.size); + break; + case 2340: // m2f2 skip sync data + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data + 12, Cs2Area->workblock.size); + break; + case 2352: // Copy data as is + memcpy(fltpartition->block[fltpartition->numblocks]->data, + Cs2Area->workblock.data, Cs2Area->workblock.size); + break; + default: break; + } + + // Modify Partition values + if (fltpartition->size == -1) fltpartition->size = 0; + fltpartition->size += fltpartition->block[fltpartition->numblocks]->size; + fltpartition->numblocks++; + + return fltpartition; +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2CopyDirRecord(u8 * buffer, dirrec_struct * dirrec) +{ + u8 * temp_pointer; + + temp_pointer = buffer; + + memcpy(&dirrec->recordsize, buffer, sizeof(dirrec->recordsize)); + buffer += sizeof(dirrec->recordsize); + + memcpy(&dirrec->xarecordsize, buffer, sizeof(dirrec->xarecordsize)); + buffer += sizeof(dirrec->xarecordsize); + +#ifdef WORDS_BIGENDIAN + buffer += sizeof(dirrec->lba); + memcpy(&dirrec->lba, buffer, sizeof(dirrec->lba)); + buffer += sizeof(dirrec->lba); +#else + memcpy(&dirrec->lba, buffer, sizeof(dirrec->lba)); + buffer += (sizeof(dirrec->lba) * 2); +#endif + +#ifdef WORDS_BIGENDIAN + buffer += sizeof(dirrec->size); + memcpy(&dirrec->size, buffer, sizeof(dirrec->size)); + buffer += sizeof(dirrec->size); +#else + memcpy(&dirrec->size, buffer, sizeof(dirrec->size)); + buffer += (sizeof(dirrec->size) * 2); +#endif + + dirrec->dateyear = buffer[0]; + dirrec->datemonth = buffer[1]; + dirrec->dateday = buffer[2]; + dirrec->datehour = buffer[3]; + dirrec->dateminute = buffer[4]; + dirrec->datesecond = buffer[5]; + dirrec->gmtoffset = buffer[6]; + buffer += 7; + + dirrec->flags = buffer[0]; + buffer += sizeof(dirrec->flags); + + dirrec->fileunitsize = buffer[0]; + buffer += sizeof(dirrec->fileunitsize); + + dirrec->interleavegapsize = buffer[0]; + buffer += sizeof(dirrec->interleavegapsize); + +#ifdef WORDS_BIGENDIAN + buffer += sizeof(dirrec->volumesequencenumber); + memcpy(&dirrec->volumesequencenumber, buffer, sizeof(dirrec->volumesequencenumber)); + buffer += sizeof(dirrec->volumesequencenumber); +#else + memcpy(&dirrec->volumesequencenumber, buffer, sizeof(dirrec->volumesequencenumber)); + buffer += (sizeof(dirrec->volumesequencenumber) * 2); +#endif + + dirrec->namelength = buffer[0]; + buffer += sizeof(dirrec->namelength); + + memset(dirrec->name, 0, sizeof(dirrec->name)); + memcpy(dirrec->name, buffer, dirrec->namelength); + buffer += dirrec->namelength; + + // handle padding + buffer += (1 - dirrec->namelength % 2); + + memset(&dirrec->xarecord, 0, sizeof(dirrec->xarecord)); + + // sadily, this is the best way I can think of for detecting XA records + + if ((dirrec->recordsize - (buffer - temp_pointer)) == 14) + { + memcpy(&dirrec->xarecord.groupid, buffer, sizeof(dirrec->xarecord.groupid)); + buffer += sizeof(dirrec->xarecord.groupid); + + memcpy(&dirrec->xarecord.userid, buffer, sizeof(dirrec->xarecord.userid)); + buffer += sizeof(dirrec->xarecord.userid); + + memcpy(&dirrec->xarecord.attributes, buffer, sizeof(dirrec->xarecord.attributes)); + buffer += sizeof(dirrec->xarecord.attributes); + +#ifndef WORDS_BIGENDIAN + // byte swap it + dirrec->xarecord.attributes = ((dirrec->xarecord.attributes & 0xFF00) >> 8) + + ((dirrec->xarecord.attributes & 0x00FF) << 8); +#endif + + memcpy(&dirrec->xarecord.signature, buffer, sizeof(dirrec->xarecord.signature)); + buffer += sizeof(dirrec->xarecord.signature); + + memcpy(&dirrec->xarecord.filenumber, buffer, sizeof(dirrec->xarecord.filenumber)); + buffer += sizeof(dirrec->xarecord.filenumber); + + memcpy(dirrec->xarecord.reserved, buffer, sizeof(dirrec->xarecord.reserved)); + buffer += sizeof(dirrec->xarecord.reserved); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2ReadFileSystem(filter_struct * curfilter, u32 fid, int isoffset) +{ + u8 * workbuffer; + u32 i; + dirrec_struct dirrec; + u8 numsectorsleft = 0; + u32 curdirlba = 0; + partition_struct * rfspartition; + u32 blocksectsize = Cs2Area->getsectsize; + + Cs2Area->outconcddev = curfilter; + + if (isoffset) + { + // readDirectory operation + + // make sure we have a valid current directory + if (Cs2Area->curdirsect == 0) + return -1; + + Cs2Area->curdirfidoffset = fid - 2; + curdirlba = Cs2Area->curdirsect; + numsectorsleft = (u8)Cs2Area->curdirsize; + } + else + { + // changeDirectory operation + + if (fid == 0xFFFFFF) + { + // Figure out root directory's location + + // Read sector 16 + if ((rfspartition = Cs2ReadUnFilteredSector(166)) == NULL) + return -2; + + blocksectsize = rfspartition->block[rfspartition->numblocks - 1]->size; + + // Retrieve directory record's lba + Cs2CopyDirRecord(rfspartition->block[rfspartition->numblocks - 1]->data + 0x9C, &dirrec); + + // Free Block + rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; + Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); + rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; + + // Sort remaining blocks + Cs2SortBlocks(rfspartition); + rfspartition->numblocks -= 1; + + curdirlba = Cs2Area->curdirsect = dirrec.lba; + Cs2Area->curdirsize = (dirrec.size / blocksectsize) - 1; + numsectorsleft = (u8)Cs2Area->curdirsize; + Cs2Area->curdirfidoffset = 0; + } + else + { + // Read in new directory record of specified directory + + // make sure we have a valid current directory + if (Cs2Area->curdirsect == 0) + return -1; + + curdirlba = Cs2Area->curdirsect = Cs2Area->fileinfo[fid - Cs2Area->curdirfidoffset].lba - 150; + Cs2Area->curdirsize = (Cs2Area->fileinfo[fid - Cs2Area->curdirfidoffset].size / blocksectsize) - 1; + numsectorsleft = (u8)Cs2Area->curdirsize; + Cs2Area->curdirfidoffset = 0; + } + } + + // Make sure any old records are cleared + memset(Cs2Area->fileinfo, 0, sizeof(dirrec_struct) * MAX_FILES); + + // now read in first sector of directory record + if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) + return -2; + + curdirlba++; + workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; + + // Fill in first two entries of fileinfo + for (i = 0; i < 2; i++) + { + Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + i); + Cs2Area->fileinfo[i].lba += 150; + workbuffer += Cs2Area->fileinfo[i].recordsize; + + if (workbuffer[0] == 0) + { + Cs2Area->numfiles = i; + break; + } + } + + // If doing a ReadDirectory operation, parse sector entries until we've + // found the fid that matches fid + if (isoffset) + { + for (i = 2; i < fid; i++) + { + Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + 2); + workbuffer += Cs2Area->fileinfo[2].recordsize; + + if (workbuffer[0] == 0) + { + if (numsectorsleft > 0) + { + // Free previous read sector + rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; + Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); + rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; + + // Sort remaining blocks + Cs2SortBlocks(rfspartition); + rfspartition->numblocks -= 1; + + // Read in next sector of directory record + if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) + return -2; + + curdirlba++; + + numsectorsleft--; + workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; + } + else + { + break; + } + } + } + } + + // Now generate the last 254 entries(the first two should've already been + // generated earlier) + for (i = 2; i < MAX_FILES; i++) + { + Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + i); + Cs2Area->fileinfo[i].lba += 150; + workbuffer += Cs2Area->fileinfo[i].recordsize; + + if (workbuffer[0] == 0) + { + if (numsectorsleft > 0) + { + // Free previous read sector + rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; + Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); + rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; + + // Sort remaining blocks + Cs2SortBlocks(rfspartition); + rfspartition->numblocks -= 1; + + // Read in next sector of directory record + if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) + return -2; + + curdirlba++; + numsectorsleft--; + workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; + } + else + { + Cs2Area->numfiles = i; + break; + } + } + } + + // Free the remaining sector + rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; + Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); + rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; + + // Sort remaining blocks + Cs2SortBlocks(rfspartition); + rfspartition->numblocks -= 1; + +//#if CDDEBUG +// for (i = 0; i < MAX_FILES; i++) +// { +// CDLOG("fileinfo[%d].name = %s\n", i, Cs2Area->fileinfo[i].name); +// } +//#endif + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void Cs2SetupFileInfoTransfer(u32 fid) { + Cs2Area->transfileinfo[0] = (u8)(Cs2Area->fileinfo[fid].lba >> 24); + Cs2Area->transfileinfo[1] = (u8)(Cs2Area->fileinfo[fid].lba >> 16); + Cs2Area->transfileinfo[2] = (u8)(Cs2Area->fileinfo[fid].lba >> 8); + Cs2Area->transfileinfo[3] = (u8)Cs2Area->fileinfo[fid].lba; + + Cs2Area->transfileinfo[4] = (u8)(Cs2Area->fileinfo[fid].size >> 24); + Cs2Area->transfileinfo[5] = (u8)(Cs2Area->fileinfo[fid].size >> 16); + Cs2Area->transfileinfo[6] = (u8)(Cs2Area->fileinfo[fid].size >> 8); + Cs2Area->transfileinfo[7] = (u8)Cs2Area->fileinfo[fid].size; + + Cs2Area->transfileinfo[8] = Cs2Area->fileinfo[fid].interleavegapsize; + Cs2Area->transfileinfo[9] = Cs2Area->fileinfo[fid].fileunitsize; + Cs2Area->transfileinfo[10] = (u8) fid; + Cs2Area->transfileinfo[11] = Cs2Area->fileinfo[fid].flags; +} + +////////////////////////////////////////////////////////////////////////////// + +partition_struct * Cs2ReadUnFilteredSector(u32 rufsFAD) { + partition_struct * rufspartition; + char syncheader[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00}; + + if ((rufspartition = Cs2GetPartition(Cs2Area->outconcddev)) != NULL && !Cs2Area->isbufferfull) + { + // Allocate Block + rufspartition->block[rufspartition->numblocks] = Cs2AllocateBlock(&rufspartition->blocknum[rufspartition->numblocks]); + + if (rufspartition->block[rufspartition->numblocks] == NULL) + return NULL; + + // read a sector using cd interface function + if (!Cs2Area->cdi->ReadSectorFAD(rufsFAD, Cs2Area->workblock.data)) + return NULL; + + // convert raw sector to type specified in getsectsize + switch(Cs2Area->getsectsize) + { + case 2048: // user data only + if (Cs2Area->workblock.data[0xF] == 0x02) + { + // is it form1/form2 data? + if (!(Cs2Area->workblock.data[0x12] & 0x20)) + { + // form 1 + memcpy(rufspartition->block[rufspartition->numblocks]->data, + Cs2Area->workblock.data + 24, 2048); + Cs2Area->workblock.size = Cs2Area->getsectsize; + } + else + { + // form 2 + memcpy(rufspartition->block[rufspartition->numblocks]->data, + Cs2Area->workblock.data + 24, 2324); + Cs2Area->workblock.size = 2324; + } + } + else + { + memcpy(rufspartition->block[rufspartition->numblocks]->data, + Cs2Area->workblock.data + 16, 2048); + Cs2Area->workblock.size = Cs2Area->getsectsize; + } + break; + case 2336: // skip sync+header data + memcpy(rufspartition->block[rufspartition->numblocks]->data, + Cs2Area->workblock.data + 16, 2336); + Cs2Area->workblock.size = Cs2Area->getsectsize; + break; + case 2340: // skip sync data + memcpy(rufspartition->block[rufspartition->numblocks]->data, + Cs2Area->workblock.data + 12, 2340); + Cs2Area->workblock.size = Cs2Area->getsectsize; + break; + case 2352: // no conversion needed + Cs2Area->workblock.size = Cs2Area->getsectsize; + break; + default: break; + } + + // if mode 2 track, setup the subheader values + if (memcmp(syncheader, Cs2Area->workblock.data, 12) == 0 && + Cs2Area->workblock.data[0xF] == 0x02) + { + rufspartition->block[rufspartition->numblocks]->fn = Cs2Area->workblock.data[0x10]; + rufspartition->block[rufspartition->numblocks]->cn = Cs2Area->workblock.data[0x11]; + rufspartition->block[rufspartition->numblocks]->sm = Cs2Area->workblock.data[0x12]; + rufspartition->block[rufspartition->numblocks]->ci = Cs2Area->workblock.data[0x13]; + } + + Cs2Area->workblock.FAD = rufsFAD; + + // Modify Partition values + if (rufspartition->size == -1) rufspartition->size = 0; + rufspartition->size += rufspartition->block[rufspartition->numblocks]->size; + rufspartition->numblocks++; + + return rufspartition; + } + + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2ReadFilteredSector(u32 rfsFAD, partition_struct **partition) { + char syncheader[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00}; + int isaudio = 0; + + if (Cs2Area->outconcddev != NULL && !Cs2Area->isbufferfull) + { + // read a sector using cd interface function to workblock.data + if (!Cs2Area->cdi->ReadSectorFAD(rfsFAD, Cs2Area->workblock.data)) + { + *partition = NULL; + return -2; + } + + Cs2Area->workblock.size = Cs2Area->getsectsize; + Cs2Area->workblock.FAD = rfsFAD; + + if (memcmp(syncheader, Cs2Area->workblock.data, 12) != 0) isaudio = 1; + + // force 1x speed if reading from an audio track + Cs2Area->isaudio = isaudio; + Cs2SetTiming(1); + + // if mode 2 track, setup the subheader values + if (isaudio) + { + ScspReceiveCDDA(Cs2Area->workblock.data); + *partition = NULL; + return 0; + } + else if (Cs2Area->workblock.data[0xF] == 0x02) + { + // if it's form 2 data the sector size should be 2324 + if (Cs2Area->workblock.data[0x12] & 0x20) Cs2Area->workblock.size = 2324; + + Cs2Area->workblock.fn = Cs2Area->workblock.data[0x10]; + Cs2Area->workblock.cn = Cs2Area->workblock.data[0x11]; + Cs2Area->workblock.sm = Cs2Area->workblock.data[0x12]; + Cs2Area->workblock.ci = Cs2Area->workblock.data[0x13]; + } + + + // pass workblock to filter function(after it identifies partition, + // it should allocate the partition block, setup/change the partition + // values, and copy workblock to the allocated block) + *partition = Cs2FilterData(Cs2Area->outconcddev, isaudio); + return 0; + } + + *partition = NULL; + return -1; +} + +////////////////////////////////////////////////////////////////////////////// + +u8 Cs2GetIP(int autoregion) { + partition_struct * gripartition; + u8 ret = 0; + + Cs2Area->outconcddev = Cs2Area->filter + 0; + Cs2Area->outconcddevnum = 0; + + // read in lba 0/FAD 150 + if ((gripartition = Cs2ReadUnFilteredSector(150)) != NULL) + { + char *buf=(char*)gripartition->block[gripartition->numblocks - 1]->data; + + // Make sure we're dealing with a saturn game + if (memcmp(buf, "SEGA SEGASATURN", 15) == 0) + { + memcpy(cdip->system, buf, 16); + cdip->system[16]='\0'; + memcpy(cdip->company, buf+0x10, 16); + cdip->company[16]='\0'; + sscanf(buf+0x20, "%s", cdip->itemnum); + memcpy(cdip->version, buf+0x2A, 6); + cdip->version[6]='\0'; + sprintf(cdip->date, "%c%c/%c%c/%c%c%c%c", buf[0x34], buf[0x35], buf[0x36], buf[0x37], buf[0x30], buf[0x31], buf[0x32], buf[0x33]); + sscanf(buf+0x38, "%s", cdip->cdinfo); + sscanf(buf+0x40, "%s", cdip->region); + sscanf(buf+0x50, "%s", cdip->peripheral); + memcpy(cdip->gamename, buf+0x60, 112); + cdip->gamename[112]='\0'; +#ifdef WORDS_BIGENDIAN + memcpy(&cdip->ipsize, buf+0xE0, sizeof(u32)); + memcpy(&cdip->msh2stack, buf+0xE8, sizeof(u32)); + memcpy(&cdip->ssh2stack, buf+0xEC, sizeof(u32)); + memcpy(&cdip->firstprogaddr, buf+0xF0, sizeof(u32)); + memcpy(&cdip->firstprogsize, buf+0xF4, sizeof(u32)); +#else + cdip->ipsize = (buf[0xE0] << 24) | (buf[0xE1] << 16) | + (buf[0xE2] << 8) | buf[0xE3]; + cdip->msh2stack = (buf[0xE8] << 24) | (buf[0xE9] << 16) | + (buf[0xEA] << 8) | buf[0xEB]; + cdip->ssh2stack = (buf[0xEC] << 24) | (buf[0xED] << 16) | + (buf[0xEE] << 8) | buf[0xEF]; + cdip->firstprogaddr = (buf[0xF0] << 24) | (buf[0xF1] << 16) | + (buf[0xF2] << 8) | buf[0xF3]; + cdip->firstprogsize = (buf[0xF4] << 24) | (buf[0xF5] << 16) | + (buf[0xF6] << 8) | buf[0xF7]; +#endif + + if (autoregion) + { + // Read first available region, that'll be what we'll use + switch (cdip->region[0]) + { + case 'J': + ret = 1; + break; + case 'T': + ret = 2; + break; + case 'U': + ret = 4; + break; + case 'B': + ret = 5; + break; + case 'K': + ret = 6; + break; + case 'A': + ret = 0xA; + break; + case 'E': + ret = 0xC; + break; + case 'L': + ret = 0xD; + break; + default: break; + } + } + } + + // Free Block + gripartition->size -= gripartition->block[gripartition->numblocks - 1]->size; + Cs2FreeBlock(gripartition->block[gripartition->numblocks - 1]); + gripartition->blocknum[gripartition->numblocks - 1] = 0xFF; + + // Sort remaining blocks + Cs2SortBlocks(gripartition); + gripartition->numblocks -= 1; + } + + return ret; +} + +////////////////////////////////////////////////////////////////////////////// + +u8 Cs2GetRegionID(void) +{ + return Cs2GetIP(1); +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2SaveState(FILE * fp) { + int offset, i; + IOCheck_struct check; + + // This is mostly kludge, but it will have to do until I have time to rewrite it all + + offset = StateWriteHeader(fp, "CS2 ", 2); + + // Write cart type + ywrite(&check, (void *) &Cs2Area->carttype, 4, 1, fp); + + // Write cd block registers + ywrite(&check, (void *) &Cs2Area->reg, sizeof(blockregs_struct), 1, fp); + + // Write current Status variables(needs a rewrite) + ywrite(&check, (void *) &Cs2Area->FAD, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->status, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->options, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->repcnt, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->ctrladdr, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->track, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->index, 1, 1, fp); + + // Write other cd block internal variables + ywrite(&check, (void *) &Cs2Area->satauth, 2, 1, fp); + ywrite(&check, (void *) &Cs2Area->mpgauth, 2, 1, fp); + ywrite(&check, (void *) &Cs2Area->transfercount, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->cdwnum, 4, 1, fp); + ywrite(&check, (void *) Cs2Area->TOC, 4, 102, fp); + ywrite(&check, (void *) &Cs2Area->playFAD, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->playendFAD, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->getsectsize, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->putsectsize, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->calcsize, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->infotranstype, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->datatranstype, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->isonesectorstored, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->isdiskchanged, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->isbufferfull, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->speed1x, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->isaudio, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->transfileinfo, 1, 12, fp); + ywrite(&check, (void *) &Cs2Area->lastbuffer, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->_command, 1, 1, fp); + { + u32 temp = (Cs2Area->_periodictiming + 3) / 3; + ywrite(&check, (void *) &temp, 4, 1, fp); + } + ywrite(&check, (void *) &Cs2Area->_commandtiming, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->outconcddevnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->outconmpegfbnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->outconmpegbufnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->outconmpegromnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->outconhostnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->datatranspartitionnum, 1, 1, fp); + ywrite(&check, (void *) &Cs2Area->datatransoffset, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->datanumsecttrans, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->datatranssectpos, 2, 1, fp); + ywrite(&check, (void *) &Cs2Area->datasectstotrans, 2, 1, fp); + ywrite(&check, (void *) &Cs2Area->blockfreespace, 4, 1, fp); + ywrite(&check, (void *) &Cs2Area->curdirsect, 4, 1, fp); + + // Write CD buffer + ywrite(&check, (void *)Cs2Area->block, sizeof(block_struct), MAX_BLOCKS, fp); + + // Write partition data + for (i = 0; i < MAX_SELECTORS; i++) + { + ywrite(&check, (void *)&Cs2Area->partition[i].size, 4, 1, fp); + ywrite(&check, (void *)Cs2Area->partition[i].blocknum, 1, MAX_BLOCKS, fp); + ywrite(&check, (void *)&Cs2Area->partition[i].numblocks, 1, 1, fp); + } + + // Write filter data + ywrite(&check, (void *)Cs2Area->filter, sizeof(filter_struct), MAX_SELECTORS, fp); + + // Write File Info Table + ywrite(&check, (void *)Cs2Area->fileinfo, sizeof(dirrec_struct), MAX_FILES, fp); + + // Write MPEG card registers here + + // Write current MPEG card status variables + ywrite(&check, (void *)&Cs2Area->actionstatus, 1, 1, fp); + ywrite(&check, (void *)&Cs2Area->pictureinfo, 1, 1, fp); + ywrite(&check, (void *)&Cs2Area->mpegaudiostatus, 1, 1, fp); + ywrite(&check, (void *)&Cs2Area->mpegvideostatus, 2, 1, fp); + ywrite(&check, (void *)&Cs2Area->vcounter, 2, 1, fp); + + // Write other MPEG card internal variables + ywrite(&check, (void *)&Cs2Area->mpegintmask, 4, 1, fp); + ywrite(&check, (void *)Cs2Area->mpegcon, sizeof(mpegcon_struct), 2, fp); + ywrite(&check, (void *)Cs2Area->mpegstm, sizeof(mpegstm_struct), 2, fp); + + return StateFinishHeader(fp, offset); +} + +////////////////////////////////////////////////////////////////////////////// + +int Cs2LoadState(FILE * fp, int version, int size) { + int i, i2; + IOCheck_struct check; + + // This is mostly kludge, but it will have to do until I have time to rewrite it all + + // Read cart type + yread(&check, (void *)&Cs2Area->carttype, 4, 1, fp); + + // Read cd block registers + yread(&check, (void *)&Cs2Area->reg, sizeof(blockregs_struct), 1, fp); + + // Read current Status variables(needs a reRead) + yread(&check, (void *)&Cs2Area->FAD, 4, 1, fp); + yread(&check, (void *)&Cs2Area->status, 1, 1, fp); + yread(&check, (void *)&Cs2Area->options, 1, 1, fp); + yread(&check, (void *)&Cs2Area->repcnt, 1, 1, fp); + yread(&check, (void *)&Cs2Area->ctrladdr, 1, 1, fp); + yread(&check, (void *)&Cs2Area->track, 1, 1, fp); + yread(&check, (void *)&Cs2Area->index, 1, 1, fp); + + // Read other cd block internal variables + yread(&check, (void *)&Cs2Area->satauth, 2, 1, fp); + yread(&check, (void *)&Cs2Area->mpgauth, 2, 1, fp); + yread(&check, (void *)&Cs2Area->transfercount, 4, 1, fp); + yread(&check, (void *)&Cs2Area->cdwnum, 4, 1, fp); + yread(&check, (void *)Cs2Area->TOC, 4, 102, fp); + yread(&check, (void *)&Cs2Area->playFAD, 4, 1, fp); + yread(&check, (void *)&Cs2Area->playendFAD, 4, 1, fp); + yread(&check, (void *)&Cs2Area->getsectsize, 4, 1, fp); + yread(&check, (void *)&Cs2Area->putsectsize, 4, 1, fp); + yread(&check, (void *)&Cs2Area->calcsize, 4, 1, fp); + yread(&check, (void *)&Cs2Area->infotranstype, 4, 1, fp); + yread(&check, (void *)&Cs2Area->datatranstype, 4, 1, fp); + yread(&check, (void *)&Cs2Area->isonesectorstored, 1, 1, fp); + yread(&check, (void *)&Cs2Area->isdiskchanged, 1, 1, fp); + yread(&check, (void *)&Cs2Area->isbufferfull, 1, 1, fp); + yread(&check, (void *)&Cs2Area->speed1x, 1, 1, fp); + if (version > 1) + yread(&check, (void *)&Cs2Area->isaudio, 1, 1, fp); + yread(&check, (void *)&Cs2Area->transfileinfo, 1, 12, fp); + yread(&check, (void *)&Cs2Area->lastbuffer, 1, 1, fp); + yread(&check, (void *)&Cs2Area->_command, 1, 1, fp); + { + u32 temp; + yread(&check, (void *)&temp, 4, 1, fp); + // Derive the actual, accurate value (always a multiple of 10) + Cs2Area->_periodictiming = ((temp * 3) / 10) * 10; + } + yread(&check, (void *)&Cs2Area->_commandtiming, 4, 1, fp); + yread(&check, (void *)&Cs2Area->outconcddevnum, 1, 1, fp); + if (Cs2Area->outconcddevnum == 0xFF) + Cs2Area->outconcddev = NULL; + else + Cs2Area->outconcddev = Cs2Area->filter + Cs2Area->outconcddevnum; + + yread(&check, (void *)&Cs2Area->outconmpegfbnum, 1, 1, fp); + if (Cs2Area->outconmpegfbnum == 0xFF) + Cs2Area->outconmpegfb = NULL; + else + Cs2Area->outconmpegfb = Cs2Area->filter + Cs2Area->outconmpegfbnum; + + yread(&check, (void *)&Cs2Area->outconmpegbufnum, 1, 1, fp); + if (Cs2Area->outconmpegbufnum == 0xFF) + Cs2Area->outconmpegbuf = NULL; + else + Cs2Area->outconmpegbuf = Cs2Area->filter + Cs2Area->outconmpegbufnum; + + yread(&check, (void *)&Cs2Area->outconmpegromnum, 1, 1, fp); + if (Cs2Area->outconmpegromnum == 0xFF) + Cs2Area->outconmpegrom = NULL; + else + Cs2Area->outconmpegrom = Cs2Area->filter + Cs2Area->outconmpegromnum; + + yread(&check, (void *)&Cs2Area->outconhostnum, 1, 1, fp); + if (Cs2Area->outconhostnum == 0xFF) + Cs2Area->outconhost = NULL; + else + Cs2Area->outconhost = Cs2Area->filter + Cs2Area->outconhostnum; + + yread(&check, (void *)&Cs2Area->datatranspartitionnum, 1, 1, fp); + yread(&check, (void *)&Cs2Area->datatransoffset, 4, 1, fp); + yread(&check, (void *)&Cs2Area->datanumsecttrans, 4, 1, fp); + yread(&check, (void *)&Cs2Area->datatranssectpos, 2, 1, fp); + yread(&check, (void *)&Cs2Area->datasectstotrans, 2, 1, fp); + yread(&check, (void *)&Cs2Area->blockfreespace, 4, 1, fp); + yread(&check, (void *)&Cs2Area->curdirsect, 4, 1, fp); + + // Read CD buffer + yread(&check, (void *)Cs2Area->block, sizeof(block_struct), MAX_BLOCKS, fp); + + // Read partition data + for (i = 0; i < MAX_SELECTORS; i++) + { + yread(&check, (void *)&Cs2Area->partition[i].size, 4, 1, fp); + yread(&check, (void *)Cs2Area->partition[i].blocknum, 1, MAX_BLOCKS, fp); + yread(&check, (void *)&Cs2Area->partition[i].numblocks, 1, 1, fp); + + for (i2 = 0; i2 < MAX_BLOCKS; i2++) + { + if (Cs2Area->partition[i].blocknum[i2] == 0xFF) + Cs2Area->partition[i].block[i2] = NULL; + else + Cs2Area->partition[i].block[i2] = Cs2Area->block + Cs2Area->partition[i].blocknum[i2]; + } + } + + // Read filter data + yread(&check, (void *)Cs2Area->filter, sizeof(filter_struct), MAX_SELECTORS, fp); + + // Read File Info Table + yread(&check, (void *)Cs2Area->fileinfo, sizeof(dirrec_struct), MAX_FILES, fp); + + // Read MPEG card registers here + + // Read current MPEG card status variables + yread(&check, (void *)&Cs2Area->actionstatus, 1, 1, fp); + yread(&check, (void *)&Cs2Area->pictureinfo, 1, 1, fp); + yread(&check, (void *)&Cs2Area->mpegaudiostatus, 1, 1, fp); + yread(&check, (void *)&Cs2Area->mpegvideostatus, 2, 1, fp); + yread(&check, (void *)&Cs2Area->vcounter, 2, 1, fp); + + // Read other MPEG card internal variables + yread(&check, (void *)&Cs2Area->mpegintmask, 4, 1, fp); + yread(&check, (void *)Cs2Area->mpegcon, sizeof(mpegcon_struct), 2, fp); + yread(&check, (void *)Cs2Area->mpegstm, sizeof(mpegstm_struct), 2, fp); + + return size; +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/cs2.h b/yabause/src/cs2.h new file mode 100644 index 0000000000..2c0a494242 --- /dev/null +++ b/yabause/src/cs2.h @@ -0,0 +1,359 @@ +/* Copyright 2003 Guillaume Duhamel + Copyright 2004-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CS2_H +#define CS2_H + +#include "memory.h" +#include "cdbase.h" +#include "cs0.h" + +#define MAX_BLOCKS 200 +#define MAX_SELECTORS 24 +#define MAX_FILES 256 + +typedef struct +{ + s32 size; + u32 FAD; + u8 cn; + u8 fn; + u8 sm; + u8 ci; + u8 data[2352]; +} block_struct; + +typedef struct +{ + u32 FAD; + u32 range; + u8 mode; + u8 chan; + u8 smmask; + u8 cimask; + u8 fid; + u8 smval; + u8 cival; + u8 condtrue; + u8 condfalse; +} filter_struct; + +typedef struct +{ + s32 size; + block_struct *block[MAX_BLOCKS]; + u8 blocknum[MAX_BLOCKS]; + u8 numblocks; +} partition_struct; + +typedef struct +{ + u16 groupid; + u16 userid; + u16 attributes; + u16 signature; + u8 filenumber; + u8 reserved[5]; +} xarec_struct; + +typedef struct +{ + u8 recordsize; + u8 xarecordsize; + u32 lba; + u32 size; + u8 dateyear; + u8 datemonth; + u8 dateday; + u8 datehour; + u8 dateminute; + u8 datesecond; + u8 gmtoffset; + u8 flags; + u8 fileunitsize; + u8 interleavegapsize; + u16 volumesequencenumber; + u8 namelength; + char name[32]; + xarec_struct xarecord; +} dirrec_struct; + +typedef struct +{ + u8 audcon; + u8 audlay; + u8 audbufdivnum; + u8 vidcon; + u8 vidlay; + u8 vidbufdivnum; +} mpegcon_struct; + +typedef struct +{ + u8 audstm; + u8 audstmid; + u8 audchannum; + u8 vidstm; + u8 vidstmid; + u8 vidchannum; +} mpegstm_struct; + +typedef struct +{ + u32 DTR; + u16 UNKNOWN; + u16 HIRQ; + u16 HIRQMASK; // Masks bits from HIRQ -only- when generating A-bus interrupts + u16 CR1; + u16 CR2; + u16 CR3; + u16 CR4; + u16 MPEGRGB; +} blockregs_struct; + +typedef struct { + blockregs_struct reg; + u32 FAD; + u8 status; + + // cd specific stats + u8 options; + u8 repcnt; + u8 ctrladdr; + u8 track; + u8 index; + + // mpeg specific stats + u8 actionstatus; + u8 pictureinfo; + u8 mpegaudiostatus; + u16 mpegvideostatus; + u16 vcounter; + + // authentication variables + u16 satauth; + u16 mpgauth; + + // internal varaibles + u32 transfercount; + u32 cdwnum; + u32 TOC[102]; + u32 playFAD; + u32 playendFAD; + unsigned int maxrepeat; + u32 getsectsize; + u32 putsectsize; + u32 calcsize; + s32 infotranstype; + s32 datatranstype; + int isonesectorstored; + int isdiskchanged; + int isbufferfull; + int speed1x; + int isaudio; + u8 transfileinfo[12]; + u8 lastbuffer; + + filter_struct filter[MAX_SELECTORS]; + filter_struct *outconcddev; + filter_struct *outconmpegfb; + filter_struct *outconmpegbuf; + filter_struct *outconmpegrom; + filter_struct *outconhost; + u8 outconcddevnum; + u8 outconmpegfbnum; + u8 outconmpegbufnum; + u8 outconmpegromnum; + u8 outconhostnum; + + partition_struct partition[MAX_SELECTORS]; + + partition_struct *datatranspartition; + u8 datatranspartitionnum; + s32 datatransoffset; + u32 datanumsecttrans; + u16 datatranssectpos; + u16 datasectstotrans; + + u32 blockfreespace; + block_struct block[MAX_BLOCKS]; + block_struct workblock; + + u32 curdirsect; + u32 curdirsize; + u32 curdirfidoffset; + dirrec_struct fileinfo[MAX_FILES]; + u32 numfiles; + + const char *mpegpath; + + u32 mpegintmask; + + mpegcon_struct mpegcon[2]; + mpegstm_struct mpegstm[2]; + + int _command; + u32 _periodiccycles; // microseconds * 3 + u32 _periodictiming; // microseconds * 3 + u32 _commandtiming; + CDInterface * cdi; + + int carttype; + int playtype; +} Cs2; + +typedef struct { + char system[17]; + char company[17]; + char itemnum[11]; + char version[7]; + char date[11]; + char cdinfo[9]; + char region[11]; + char peripheral[17]; + char gamename[113]; + u32 ipsize; + u32 msh2stack; + u32 ssh2stack; + u32 firstprogaddr; + u32 firstprogsize; +} ip_struct; + +extern Cs2 * Cs2Area; +extern ip_struct * cdip; + +int Cs2Init(int, int, const char *, const char *, const char *); +int Cs2ChangeCDCore(int coreid, const char *cdpath); +void Cs2DeInit(void); + +u8 FASTCALL Cs2ReadByte(u32); +u16 FASTCALL Cs2ReadWord(u32); +u32 FASTCALL Cs2ReadLong(u32); +void FASTCALL Cs2WriteByte(u32, u8); +void FASTCALL Cs2WriteWord(u32, u16); +void FASTCALL Cs2WriteLong(u32, u32); + +void FASTCALL Cs2RapidCopyT1(void *dest, u32 count); +void FASTCALL Cs2RapidCopyT2(void *dest, u32 count); + +void Cs2Exec(u32); +int Cs2GetTimeToNextSector(void); +void Cs2Execute(void); +void Cs2Reset(void); +void Cs2SetTiming(int); +void Cs2Command(void); +void Cs2SetCommandTiming(u8 cmd); + +// command name command code +void Cs2GetStatus(void); // 0x00 +void Cs2GetHardwareInfo(void); // 0x01 +void Cs2GetToc(void); // 0x02 +void Cs2GetSessionInfo(void); // 0x03 +void Cs2InitializeCDSystem(void); // 0x04 +// Open Tray // 0x05 +void Cs2EndDataTransfer(void); // 0x06 +void Cs2PlayDisc(void); // 0x10 +void Cs2SeekDisc(void); // 0x11 +// Scan Disc // 0x12 +void Cs2GetSubcodeQRW(void); // 0x20 +void Cs2SetCDDeviceConnection(void); // 0x30 +// get CD Device Connection // 0x31 +void Cs2GetLastBufferDestination(void); // 0x32 +void Cs2SetFilterRange(void); // 0x40 +// get Filter Range // 0x41 +void Cs2SetFilterSubheaderConditions(void);// 0x42 +void Cs2GetFilterSubheaderConditions(void);// 0x43 +void Cs2SetFilterMode(void); // 0x44 +void Cs2GetFilterMode(void); // 0x45 +void Cs2SetFilterConnection(void); // 0x46 +// Get Filter Connection // 0x47 +void Cs2ResetSelector(void); // 0x48 +void Cs2GetBufferSize(void); // 0x50 +void Cs2GetSectorNumber(void); // 0x51 +void Cs2CalculateActualSize(void); // 0x52 +void Cs2GetActualSize(void); // 0x53 +void Cs2GetSectorInfo(void); // 0x54 +void Cs2SetSectorLength(void); // 0x60 +void Cs2GetSectorData(void); // 0x61 +void Cs2DeleteSectorData(void); // 0x62 +void Cs2GetThenDeleteSectorData(void); // 0x63 +void Cs2PutSectorData(void); // 0x64 +// Copy Sector Data // 0x65 +// Move Sector Data // 0x66 +void Cs2GetCopyError(void); // 0x67 +void Cs2ChangeDirectory(void); // 0x70 +void Cs2ReadDirectory(void); // 0x71 +void Cs2GetFileSystemScope(void); // 0x72 +void Cs2GetFileInfo(void); // 0x73 +void Cs2ReadFile(void); // 0x74 +void Cs2AbortFile(void); // 0x75 +void Cs2MpegGetStatus(void); // 0x90 +void Cs2MpegGetInterrupt(void); // 0x91 +void Cs2MpegSetInterruptMask(void); // 0x92 +void Cs2MpegInit(void); // 0x93 +void Cs2MpegSetMode(void); // 0x94 +void Cs2MpegPlay(void); // 0x95 +void Cs2MpegSetDecodingMethod(void); // 0x96 +// MPEG Out Decoding Sync // 0x97 +// MPEG Get Timecode // 0x98 +// MPEG Get Pts // 0x99 +void Cs2MpegSetConnection(void); // 0x9A +void Cs2MpegGetConnection(void); // 0x9B +// MPEG Change Connection // 0x9C +void Cs2MpegSetStream(void); // 0x9D +void Cs2MpegGetStream(void); // 0x9E +// MPEG Get Picture Size // 0x9F +void Cs2MpegDisplay(void); // 0xA0 +void Cs2MpegSetWindow(void); // 0xA1 +void Cs2MpegSetBorderColor(void); // 0xA2 +void Cs2MpegSetFade(void); // 0xA3 +void Cs2MpegSetVideoEffects(void); // 0xA4 +// MPEG Get Image // 0xA5 +// MPEG Set Image // 0xA6 +// MPEG Read Image // 0xA7 +// MPEG Write Image // 0xA8 +// MPEG Read Sector // 0xA9 +// MPEG Write Sector // 0xAA +// MPEG Get LSI // 0xAE +void Cs2MpegSetLSI(void); // 0xAF +void Cs2CmdE0(void); // 0xE0 +void Cs2CmdE1(void); // 0xE1 +void Cs2CmdE2(void); // 0xE2 + +u8 Cs2FADToTrack(u32 val); +u32 Cs2TrackToFAD(u16 trackandindex); +void Cs2SetupDefaultPlayStats(u8 track_number, int writeFAD); +block_struct * Cs2AllocateBlock(u8 * blocknum); +void Cs2FreeBlock(block_struct * blk); +void Cs2SortBlocks(partition_struct * part); +partition_struct * Cs2GetPartition(filter_struct * curfilter); +partition_struct * Cs2FilterData(filter_struct * curfilter, int isaudio); +int Cs2CopyDirRecord(u8 * buffer, dirrec_struct * dirrec); +int Cs2ReadFileSystem(filter_struct * curfilter, u32 fid, int isoffset); +void Cs2SetupFileInfoTransfer(u32 fid); +partition_struct * Cs2ReadUnFilteredSector(u32 rufsFAD); +//partition_struct * Cs2ReadFilteredSector(u32 rfsFAD); +int Cs2ReadFilteredSector(u32 rfsFAD, partition_struct **partition); +u8 Cs2GetIP(int autoregion); +u8 Cs2GetRegionID(void); +int Cs2SaveState(FILE *); +int Cs2LoadState(FILE *, int, int); + +#endif diff --git a/yabause/src/debug.c b/yabause/src/debug.c new file mode 100644 index 0000000000..295b21a00e --- /dev/null +++ b/yabause/src/debug.c @@ -0,0 +1,186 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "debug.h" + +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////// + +Debug * DebugInit(const char * n, DebugOutType t, char * s) { + Debug * d; + + if ((d = (Debug *) malloc(sizeof(Debug))) == NULL) + return NULL; + + d->output_type = t; + + if ((d->name = strdup(n)) == NULL) + { + free(d); + return NULL; + } + + switch(t) { + case DEBUG_STREAM: + d->output.stream = fopen(s, "w"); + break; + case DEBUG_STRING: + d->output.string = s; + break; + case DEBUG_STDOUT: + d->output.stream = stdout; + break; + case DEBUG_STDERR: + d->output.stream = stderr; + break; + case DEBUG_CALLBACK: + d->output.callback = (void (*) (char*))s; + break; + } + + return d; +} + +////////////////////////////////////////////////////////////////////////////// + +void DebugDeInit(Debug * d) { + if (d == NULL) + return; + + switch(d->output_type) { + case DEBUG_STREAM: + if (d->output.stream) + fclose(d->output.stream); + break; + case DEBUG_STRING: + case DEBUG_STDOUT: + case DEBUG_STDERR: + case DEBUG_CALLBACK: + break; + } + if (d->name) + free(d->name); + free(d); +} + +////////////////////////////////////////////////////////////////////////////// + +void DebugChangeOutput(Debug * d, DebugOutType t, char * s) { + if (t != d->output_type) { + if (d->output_type == DEBUG_STREAM) + { + if (d->output.stream) + fclose(d->output.stream); + } + d->output_type = t; + } + switch(t) { + case DEBUG_STREAM: + d->output.stream = fopen(s, "w"); + break; + case DEBUG_STRING: + d->output.string = s; + break; + case DEBUG_CALLBACK: + d->output.callback = (void (*) (char*))s; + break; + case DEBUG_STDOUT: + d->output.stream = stdout; + break; + case DEBUG_STDERR: + d->output.stream = stderr; + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void DebugPrintf(Debug * d, const char * file, u32 line, const char * format, ...) { + va_list l; + static char strtmp[512]; + static int strhash; + + if (d == NULL) + return; + + va_start(l, format); + + switch(d->output_type) { + case DEBUG_STDOUT: + case DEBUG_STDERR: + case DEBUG_STREAM: + if (d->output.stream == NULL) + break; + fprintf(d->output.stream, "%s (%s:%ld): ", d->name, file, (long)line); + vfprintf(d->output.stream, format, l); + break; + case DEBUG_STRING: + { + int i; + if (d->output.string == NULL) + break; + + i = sprintf(d->output.string, "%s (%s:%ld): ", d->name, file, (long)line); + vsprintf(d->output.string + i, format, l); + } + break; + case DEBUG_CALLBACK: + { + int i; + int strnewhash = 0; + i = sprintf(strtmp, "%s (%s:%ld): ", d->name, file, (long)line); + i += vsprintf(strtmp + i, format, l); + for ( ; i>0 ; i-- ) strnewhash += (int)(strtmp[i]); + if ( strnewhash != strhash ) d->output.callback( strtmp ); + strhash = strnewhash; + } + break; + } + + va_end(l); +} + +////////////////////////////////////////////////////////////////////////////// + +Debug * MainLog; + +////////////////////////////////////////////////////////////////////////////// + +void LogStart(void) { + MainLog = DebugInit("main", DEBUG_STDOUT, NULL); +// MainLog = DebugInit("main", DEBUG_STREAM, "stdout.txt"); +} + +////////////////////////////////////////////////////////////////////////////// + +void LogStop(void) { + DebugDeInit(MainLog); + MainLog = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void LogChangeOutput(DebugOutType t, char * s) { + + DebugChangeOutput( MainLog, t, s ); +} diff --git a/yabause/src/debug.h b/yabause/src/debug.h new file mode 100644 index 0000000000..91935b322e --- /dev/null +++ b/yabause/src/debug.h @@ -0,0 +1,88 @@ +/* Copyright 2005-2006 Guillaume Duhamel + Copyright 2005 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef DEBUG_H +#define DEBUG_H + +#include "core.h" +#include + +typedef enum { DEBUG_STRING, DEBUG_STREAM , DEBUG_STDOUT, DEBUG_STDERR, DEBUG_CALLBACK } DebugOutType; + +typedef struct { + DebugOutType output_type; + union { + FILE * stream; + char * string; + void (*callback) (char*); + } output; + char * name; +} Debug; + +Debug * DebugInit(const char *, DebugOutType, char *); +void DebugDeInit(Debug *); + +void DebugChangeOutput(Debug *, DebugOutType, char *); + +void DebugPrintf(Debug *, const char *, u32, const char *, ...); + +extern Debug * MainLog; + +void LogStart(void); +void LogStop(void); +void LogChangeOutput(DebugOutType t, char * s); + +#ifdef DEBUG +#define LOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG(...) +#endif + +#ifdef CDDEBUG +#define CDLOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define CDLOG(...) +#endif + +#ifdef SCSP_DEBUG +#define SCSPLOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define SCSPLOG(...) +#endif + +#ifdef VDP1_DEBUG +#define VDP1LOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define VDP1LOG(...) +#endif + +#ifdef VDP2_DEBUG +#define VDP2LOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define VDP2LOG(...) +#endif + +#ifdef SMPC_DEBUG +#define SMPCLOG(...) DebugPrintf(MainLog, __FILE__, __LINE__, __VA_ARGS__) +#else +#define SMPCLOG(...) +#endif + +#endif diff --git a/yabause/src/dreamcast/CMakeLists.txt b/yabause/src/dreamcast/CMakeLists.txt new file mode 100644 index 0000000000..e76d009e4c --- /dev/null +++ b/yabause/src/dreamcast/CMakeLists.txt @@ -0,0 +1,29 @@ +project(yabause-dc) + +if(NOT dreamcast) + return() +endif(NOT dreamcast) + +enable_language(ASM-ATT) +include_directories(${PORT_INCLUDE_DIRS}) +add_definitions(${PORT_CFLAGS}) + +set(yabause_dc_SOURCES + cd.s + localtime.c + perdc.c + viddc.c + yui.c) + +set(yabause_dc_HEADERS + localtime.h + perdc.h + viddc.h) + +link_directories(..) +add_executable(yabause-dc ${yabause_dc_SOURCES}) +set_target_properties(yabause-dc PROPERTIES OUTPUT_NAME yabause.elf) +target_link_libraries(yabause-dc ${YABAUSE_LIBRARIES}) +target_link_libraries(yabause-dc ${PORT_LIBRARIES}) +target_link_libraries(yabause-dc yabause) +target_link_libraries(yabause-dc m) diff --git a/yabause/src/dreamcast/Makefile.am b/yabause/src/dreamcast/Makefile.am new file mode 100644 index 0000000000..a0bce0a82d --- /dev/null +++ b/yabause/src/dreamcast/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=cd.s localtime.c localtime.h perdc.c perdc.h viddc.c viddc.h yui.c diff --git a/yabause/src/dreamcast/cd.s b/yabause/src/dreamcast/cd.s new file mode 100644 index 0000000000..817d1e93d6 --- /dev/null +++ b/yabause/src/dreamcast/cd.s @@ -0,0 +1,234 @@ +! Copyright 2008 Lawrence Sebald +! +! This file is part of Yabause. +! +! Yabause is free software; you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation; either version 2 of the License, or +! (at your option) any later version. +! +! Yabause is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with Yabause; if not, write to the Free Software +! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +! Ok, so this is the start of me assemblerizing different parts of the core +! of the Dreamcast port of Yabause. I picked the CD core since its one of the +! parts of the emulator that is least likely to change in the future +! (hopefully anyway), and its somewhat simple to start with. + + .file "cd.s" + .little + .text + .align 2 + +! static int DCCDInit(const char *cdrom_name) +! Initialize the GD drive to read 2352 byte sectors. +DCCDInit: + sts.l pr, @-r15 +.do_reinit: + mov.l .cdrom_exec_cmd, r0 + mov #0, r5 + jsr @r0 + mov #24, r4 ! CMD_INIT + cmp/eq #1, r0 ! ERR_NO_DISC + bt .init_return_success + cmp/eq #3, r0 ! ERR_SYS + bt .init_return_error + mov.l .gdc_syscall_vector, r1 + mova .DCCDInitParams, r0 + mov #10, r7 + mov.l @r1, r1 + mov r0, r4 + mov #0, r6 + jsr @r1 + mov #0, r5 + lds.l @r15+, pr + rts + nop +.init_return_success: + lds.l @r15+, pr + rts + mov #0, r0 +.init_return_error: + lds.l @r15+, pr + rts + mov #-1, r0 + .align 4 +.cdrom_exec_cmd: + .long _cdrom_exec_cmd +.DCCDInitParams: + .long 0 ! 0 = set + .long 4096 ! Magic value for RAW sector reads + .long 0x0400 ! Ditto + .long 2352 ! Sector Size? (Maybe not for RAW though?) + +! static int DCCDGetStatus(void) +! Execute the BIOS syscall of the Dreamcast to get the GD drive status, +! translating that into the format expected by the core of Yabause. +DCCDGetStatus: + sts.l pr, @-r15 +.status_startgame: + mov.l .gdc_syscall_vector, r1 + mova .get_status_scratchpad, r0 + mov #4, r7 + mov.l @r1, r1 + mov #0, r5 + mov r0, r4 + jsr @r1 + mov #0, r6 + cmp/eq #2, r0 ! 2 = Disc change error + bt .status_reinit + cmp/eq #0, r0 + bf .status_error +.status_endgame: ! status in 1st entry in scratchpad + mova .get_status_scratchpad, r0 + mov #0x07, r2 + mov.l @r0, r1 + mova .get_status_return_value, r0 + and r2, r1 + add r1, r0 + lds.l @r15+, pr + rts + mov.b @r0, r0 +.status_reinit: + mov.l .get_status_init_func, r0 + jsr @r0 + mov #0, r4 + cmp/eq #0, r0 + bt .status_startgame +.status_error: + lds.l @r15+, pr + rts + mov #2, r0 + + .align 4 +.gdc_syscall_vector: + .long 0x8c0000bc +.get_status_scratchpad: + .long 0 + .long 0 +.get_status_return_value: + .byte 0, 1, 1, 0, 0, 0, 3, 2 +.get_status_init_func: + .long DCCDInit + +! static int DCCDDeInit(void) +! Deinitialize the CD Drive of the Dreamcast (i.e., undo the odd +! initialization stuff that the code does for Yabause). +DCCDDeInit: + mov.l .cdrom_reinit, r0 + sts.l pr, @-r15 + jsr @r0 + nop + lds.l @r15+, pr + rts + nop ! Leave the return value from cdrom_reinit as the return here. + +! static int DCCDReadSectorFAD(u32 FAD, void *buffer) +! Read a single 2352 byte sector from the given position on the disc. +DCCDReadSectorFAD: + sts.l pr, @-r15 + mov r4, r2 + mov.l r4, @-r15 + mov r5, r4 + mov.l .cdrom_read_sectors, r0 + mov.l r5, @-r15 + mov r2, r5 +.read_sector_start: + jsr @r0 + mov #1, r6 + cmp/eq #2, r0 + bt .read_reinit + cmp/eq #0, r0 + add #8, r15 + bf/s .read_error + lds.l @r15+, pr + rts + mov #1, r0 +.read_reinit: + mov.l .DCCDInit, r0 + jsr @r0 + mov #0, r4 + cmp/eq #0, r0 + mov.l @r15, r4 + mov.l .cdrom_read_sectors, r0 + bt/s .read_sector_start + mov.l @(4, r15), r5 + add #8, r15 +.read_error: + rts + mov #0, r0 + +! static int DCCDReadAheadFAD(u32 FAD) +! No-op (for the moment). +DCCDReadAheadFAD: + rts + nop + +! static s32 DCCDReadTOC(u32 *TOC); +! Read the TOC of the CD inserted in the drive. +! Amusingly enough, I just realized that the format that Yabause expects +! and what the Dreamcast spews out are exactly the same. Go figure! +DCCDReadTOC: + sts.l pr, @-r15 + mov.l .cdrom_read_toc, r0 + mov.l r4, @-r15 +.readtoc_start: + jsr @r0 + mov #0, r5 + cmp/eq #2, r0 + bt .readtoc_reinit + cmp/eq #0, r0 + add #4, r15 + bf/s .readtoc_error + mov #0xCC, r0 + lds.l @r15+, pr + extu.b r0, r0 + rts + shll r0 +.readtoc_reinit: + mov.l .DCCDInit, r0 + jsr @r0 + mov #0, r4 + cmp/eq #0, r0 + mov.l @r15, r4 + bt/s .readtoc_start + mov.l .cdrom_read_toc, r0 + add #4, r15 +.readtoc_error: + rts + mov #0, r0 + + .align 4 +.cdrom_reinit: + .long _cdrom_reinit +.cdrom_read_sectors: + .long _cdrom_read_sectors +.DCCDInit: + .long DCCDInit +.cdrom_read_toc: + .long _cdrom_read_toc + + .section .rodata + .align 2 +.CDInterfaceName: + .string "Dreamcast CD Drive" + + .data + .align 4 + .globl _ArchCD + .size _ArchCD, 32 +_ArchCD: + .long 2 + .long .CDInterfaceName + .long DCCDInit + .long DCCDDeInit + .long DCCDGetStatus + .long DCCDReadTOC + .long DCCDReadSectorFAD + .long DCCDReadAheadFAD diff --git a/yabause/src/dreamcast/dreamcast.cmake b/yabause/src/dreamcast/dreamcast.cmake new file mode 100644 index 0000000000..0c39a1b97f --- /dev/null +++ b/yabause/src/dreamcast/dreamcast.cmake @@ -0,0 +1,22 @@ +# CMake toolchain file for building Yabause on the Dreamcast +set(CMAKE_SYSTEM_NAME Generic) + +# Use the gnu_wrappers for the various GNU utilities +set(CMAKE_C_COMPILER kos-cc) +set(CMAKE_CXX_COMPILER kos-c++) +set(CMAKE_ASM_COMPILER kos-as) + +# KOS Sets this nicely for us. +set(CMAKE_FIND_ROOT_PATH $ENV{KOS_CC_BASE}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Set some stuff so that it doesn't complain about the lack of a normal looking +# pthreads flag/library for the compiler. +set(THREADS_HAVE_PTHREAD_ARG 1) +set(CMAKE_HAVE_THREADS_LIBRARY 1) + +# Set a flag so we know we're trying to compile for Dreamcast +set(dreamcast 1) diff --git a/yabause/src/dreamcast/localtime.c b/yabause/src/dreamcast/localtime.c new file mode 100644 index 0000000000..e13d56a031 --- /dev/null +++ b/yabause/src/dreamcast/localtime.c @@ -0,0 +1,106 @@ +/* + * localtime_r.c + * Original Author: Adapted from tzcode maintained by Arthur David Olson. + * + * Converts the calendar time pointed to by tim_p into a broken-down time + * expressed as local time. Returns a pointer to a structure containing the + * broken-down time. + */ + +/* This file was taken from newlib , it's a + * modified version of Arthur David Olsons localtime.c from tzcode which + * is under Public Domain */ + +#include +#include +#include "localtime.h" + +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define YEAR_BASE 1900 +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + +static const int mon_lengths[2][MONSPERYEAR] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + +static const int year_lengths[2] = { + 365, + 366 +} ; + +struct tm * internal_localtime_r(const time_t * tim_p, struct tm *res) +{ + long days, rem; + int y; + int yleap; + const int *ip; + + days = ((long) *tim_p) / SECSPERDAY; + rem = ((long) *tim_p) % SECSPERDAY; + while (rem < 0) + { + rem += SECSPERDAY; + --days; + } + while (rem >= SECSPERDAY) + { + rem -= SECSPERDAY; + ++days; + } + + /* compute hour, min, and sec */ + res->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + res->tm_min = (int) (rem / SECSPERMIN); + res->tm_sec = (int) (rem % SECSPERMIN); + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) + res->tm_wday += DAYSPERWEEK; + + /* compute year & day of year */ + y = EPOCH_YEAR; + if (days >= 0) + { + for (;;) + { + yleap = isleap(y); + if (days < year_lengths[yleap]) + break; + y++; + days -= year_lengths[yleap]; + } + } + else + { + do + { + --y; + yleap = isleap(y); + days += year_lengths[yleap]; + } while (days < 0); + } + + res->tm_year = y - YEAR_BASE; + res->tm_yday = days; + ip = mon_lengths[yleap]; + for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) + days -= ip[res->tm_mon]; + res->tm_mday = days + 1; + + /* set daylight saving time flag */ + res->tm_isdst = -1; + + return (res); +} diff --git a/yabause/src/dreamcast/localtime.h b/yabause/src/dreamcast/localtime.h new file mode 100644 index 0000000000..fb894aad44 --- /dev/null +++ b/yabause/src/dreamcast/localtime.h @@ -0,0 +1,3 @@ +/* internal_localtime_r() function declaration, included by smpc.c */ + +extern struct tm * internal_localtime_r(const time_t * tim_p, struct tm *res); diff --git a/yabause/src/dreamcast/perdc.c b/yabause/src/dreamcast/perdc.c new file mode 100644 index 0000000000..2ebab80ace --- /dev/null +++ b/yabause/src/dreamcast/perdc.c @@ -0,0 +1,145 @@ +/* Copyright 2005-2008 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "perdc.h" +#include "../yabause.h" +#include "../yui.h" +#include "../vdp2.h" + +#include +#include + +int PERDCInit(void); +void PERDCDeInit(void); +int PERDCHandleEvents(void); +void PERDCNothing(void); +u32 PERDCScan(void); + +static PerPad_struct *pad1; + +PerInterface_struct PERDC = { + PERCORE_DC, + "Dreamcast Input Interface", + PERDCInit, + PERDCDeInit, + PERDCHandleEvents, + PERDCNothing, + PERDCScan, + 0, + PERDCNothing +}; + +int PERDCInit(void) { + PerPortReset(); + pad1 = PerPadAdd(&PORTDATA1); + return 0; +} + +void PERDCDeInit(void) { +} + +int PERDCHandleEvents(void) { + maple_device_t *dev; + + dev = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + if(dev != NULL) { + cont_state_t *state = (cont_state_t *) maple_dev_status(dev); + + if(state != NULL) { + if(state->buttons & CONT_DPAD_UP) + *pad1->padbits &= 0xEF; + else + *pad1->padbits |= 0x10; + + if(state->buttons & CONT_DPAD_DOWN) + *pad1->padbits &= 0xDF; + else + *pad1->padbits |= 0x20; + + if(state->buttons & CONT_DPAD_RIGHT) + *pad1->padbits &= 0x7F; + else + *pad1->padbits |= 0x80; + + if(state->buttons & CONT_DPAD_LEFT) + *pad1->padbits &= 0xBF; + else + *pad1->padbits |= 0x40; + + if(state->buttons & CONT_START) + *pad1->padbits &= 0xF7; + else + *pad1->padbits |= 0x08; + + if(state->buttons & CONT_A) + *pad1->padbits &= 0xFB; + else + *pad1->padbits |= 0x04; + + if(state->buttons & CONT_B) + *pad1->padbits &= 0xFE; + else + *pad1->padbits |= 0x01; + + if(state->buttons & CONT_X) + *(pad1->padbits + 1) &= 0xBF; + else + *(pad1->padbits + 1) |= 0x40; + + if(state->buttons & CONT_Y) + *(pad1->padbits + 1) &= 0xDF; + else + *(pad1->padbits + 1) |= 0x20; + + if(state->rtrig > 20) + *(pad1->padbits + 1) &= 0x7F; + else + *(pad1->padbits + 1) |= 0x80; + + if(state->ltrig > 20) + *(pad1->padbits + 1) &= 0xF7; + else + *(pad1->padbits + 1) |= 0x08; + + if(state->joyx > 20) + *pad1->padbits &= 0xFD; + else + *pad1->padbits |= 0x02; + + if(state->joyy > 20) + *(pad1->padbits + 1) &= 0xEF; + else + *(pad1->padbits + 1) |= 0x10; + + } + } + + YabauseExec(); + + return 0; +} + +void PERDCNothing(void) { + /* Nothing */ +} + +u32 PERDCScan(void) { + /* Nothing */ + return 0; +} diff --git a/yabause/src/dreamcast/perdc.h b/yabause/src/dreamcast/perdc.h new file mode 100644 index 0000000000..2dcbe64f41 --- /dev/null +++ b/yabause/src/dreamcast/perdc.h @@ -0,0 +1,29 @@ +/* Copyright 2005 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERDC_H +#define PERDC_H + +#include "../peripheral.h" + +#define PERCORE_DC 2 + +extern PerInterface_struct PERDC; + +#endif diff --git a/yabause/src/dreamcast/sh2rec/sh2exec.s b/yabause/src/dreamcast/sh2rec/sh2exec.s new file mode 100644 index 0000000000..f2ff5f9aaa --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2exec.s @@ -0,0 +1,100 @@ +! Copyright 2010 Lawrence Sebald +! +! This file is part of Yabause. +! +! Yabause is free software; you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation; either version 2 of the License, or +! (at your option) any later version. +! +! Yabause is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with Yabause; if not, write to the Free Software +! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +! Code for calling upon the code generated by sh2rec (SuperH). + + .file "sh2exec.s" + .little + .text + .balign 8 + +! void sh2rec_exec(SH2_struct *cxt, u32 cycles) +! Execute the specified number of cycles on the given SH2 context + .globl _sh2rec_exec +_sh2rec_exec: + stc.l gbr, @-r15 ! Save call-preserved stuff + sts.l mach, @-r15 ! Ditto + sts.l macl, @-r15 ! Ditto + mov.l r8, @-r15 ! Ditto + mov r4, r8 ! Put the SH2 struct in r8 + mov.l r9, @-r15 ! More call-preserved stuff + add #76, r4 ! Point at MACH in the SH2 struct + mov.l r10, @-r15 ! More call-preserved stuff + mov.l r11, @-r15 ! Last one for now... + lds.l @r4+, mach ! Load the SH2 MACH into our MACH + lds.l @r4+, macl ! Ditto for MACL + mov.l checkInterrupts, r0 ! We need to check for interrupts... + mov.l sh2memfuncsptr, r9 ! Memory access function pointer table + sts.l pr, @-r15 ! Helps to know where to go back to + mov r5, r11 ! This is important enough to keep here + mov.l r5, @-r15 ! Save this on the stack too + mov r8, r4 ! We need the original SH2_struct back + jsr @r0 ! Call sh2rec_check_interrupts + ldc r8, gbr ! Put the SH2 struct in gbr (delay slot) + mov.l findBlock, r1 ! Grab the sh2rec_find_block function + mov.l @(88, gbr), r0 ! Grab the PC we are at +.exec_loop: ! This is where the fun is! + jsr @r1 ! Call sh2rec_find_block + mov r0, r4 ! Move the PC to argument 1 (delay slot) + mov.l @r0, r2 ! Grab where the code is + mov.l @(8, r0), r1 ! Figure out the number of cycles used + jsr @r2 ! Call the block + sub r1, r11 ! Chop off the cycles (delay slot) + cmp/pl r11 ! Are we done? + mov.l findBlock, r1 ! Grab the sh2rec_find_block function + bt .exec_loop ! Continue on if needed + ! When we are done, we will be here. + mov.l r0, @(88, gbr) ! Save the next PC value + mov.l @r15+, r5 ! Pop the requested number of cycles + mov r8, r4 ! Keep this for sanity for now + add #84, r8 ! Point just after MACL in SH2 struct + sts.l macl, @-r8 ! Store the SH2 MACL back in the struct + sts.l mach, @-r8 ! Ditto for MACH + lds.l @r15+, pr ! Restore stuff from the stack + sub r11, r5 ! Our counter is negitive, so this works + mov.l cycleOffset, r2 ! Where is the cycles member at? + mov.l @r15+, r11 ! More restoring... + add r2, r4 ! Point r4 at the cycles member + mov.l @r15+, r10 + mov.l @r15+, r9 + mov.l @r15+, r8 + lds.l @r15+, macl + lds.l @r15+, mach + mov.l r5, @r4 ! Save the cycles we spent + rts ! Return to the caller + ldc.l @r15+, gbr ! Last thing to restore (delay slot) + + .balign 4 +sh2memfuncsptr: + .long sh2memfuncs +checkInterrupts: + .long _sh2rec_check_interrupts +findBlock: + .long _sh2rec_find_block +cycleOffset: + .long 5516 + + .data + .balign 4 +sh2memfuncs: + .long _MappedMemoryReadByte + .long _MappedMemoryReadWord + .long _MappedMemoryReadLong + .long _MappedMemoryWriteByte + .long _MappedMemoryWriteWord + .long _MappedMemoryWriteLong diff --git a/yabause/src/dreamcast/sh2rec/sh2rec.c b/yabause/src/dreamcast/sh2rec/sh2rec.c new file mode 100644 index 0000000000..2295da27ed --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec.c @@ -0,0 +1,2908 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* SH2 Dynarec Core (for SH4) */ + +#include +#include +#include +#include + +#include "sh2core.h" +#include "sh2rec.h" +#include "sh2rec_htab.h" +#include "sh2int.h" + +/* Registers */ +#define R0 0 +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +/* Control Registers (use with emitSTC/emitLDC) */ +#define R_SR 0 +#define R_GBR 1 +#define R_VBR 2 + +/* System Registers (use with emitSTS/emitLDS) */ +#define R_MACH 0 +#define R_MACL 1 +#define R_PR 2 + +/* ALU Ops, to be used with the emitALU function */ +#define OP_ADD 0x300C +#define OP_ADDC 0x300E +#define OP_AND 0x2009 +#define OP_EXTSB 0x600E +#define OP_EXTSW 0x600F +#define OP_EXTUB 0x600C +#define OP_EXTUW 0x600D +#define OP_NEG 0x600B +#define OP_NEGC 0x600A +#define OP_NOT 0x6007 +#define OP_OR 0x200B +#define OP_SUB 0x3008 +#define OP_SUBC 0x300A +#define OP_SWAPB 0x6008 +#define OP_SWAPW 0x6009 +#define OP_XOR 0x200A +#define OP_XTRCT 0x200D + +/* Shift/Rotate Ops, to be used with the emitSHIFT function */ +#define OP_ROTCL 0x4024 +#define OP_ROTCR 0x4025 +#define OP_ROTL 0x4004 +#define OP_ROTR 0x4005 +#define OP_SHAL 0x4020 +#define OP_SHAR 0x4021 +#define OP_SHLL 0x4000 +#define OP_SHLR 0x4001 + +/* Comparison Ops, to be used with the emitALU function */ +#define OP_CMPEQ 0x3000 +#define OP_CMPGE 0x3003 +#define OP_CMPGT 0x3007 +#define OP_CMPHI 0x3006 +#define OP_CMPHS 0x3002 +#define OP_CMPSTR 0x200C +#define OP_TST 0x2008 + +/* Multiplication ops, to be used with the emitALU function */ +#define OP_DMULS 0x300D +#define OP_DMULU 0x3005 +#define OP_MULL 0x0007 +#define OP_MULS 0x200F +#define OP_MULU 0x200E + +#ifdef SH2REC__DEBUG +#define EMIT_INST {\ + printf("%s\n", __PRETTY_FUNCTION__); \ + printf("Emitting %04x at %p\n", inst, (void *)b->ptr); \ + *b->ptr++ = inst; \ +} +#else +#define EMIT_INST *b->ptr++ = inst +#endif + +#ifdef SH2REC__DEBUG +#define EMIT_32 {\ + uint32_t *__ptr = (uint32_t *)b->ptr; \ + printf("%s\n", __PRETTY_FUNCTION__); \ + printf("Emitting %08x at %p\n", (unsigned int)v, (void *)__ptr); \ + *__ptr = v; \ + b->ptr += 2; \ +} +#else +#define EMIT_32 uint32_t *__ptr = (uint32_t *)b->ptr; *__ptr = v; b->ptr += 2 +#endif + +static inline void emit16(sh2rec_block_t *b, uint16_t inst) { + EMIT_INST; +} + +static inline void emit32(sh2rec_block_t *b, uint32_t v) { + EMIT_32; +} + +static inline void emitMOV(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x6003 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVWI(sh2rec_block_t *b, int d, int n) { + uint16_t inst = 0x9000 | (n << 8) | (d); + EMIT_INST; +} + +static inline void emitMOVLI(sh2rec_block_t *b, int d, int n) { + uint16_t inst = 0xD000 | (n << 8) | (d); + EMIT_INST; +} + +static inline void emitMOVLS(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x2002 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVLL(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x6002 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVWM(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x2005 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVLM(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x2006 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVLP(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x6006 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMOVI(sh2rec_block_t *b, int imm, int n) { + uint16_t inst = 0xE000 | (n << 8) | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitMOVLL4(sh2rec_block_t *b, int m, int d, int n) { + uint16_t inst = 0x5000 | (n << 8) | (m << 4) | (d); + EMIT_INST; +} + +static inline void emitMOVLS4(sh2rec_block_t *b, int m, int d, int n) { + uint16_t inst = 0x1000 | (n << 8) | (m << 4) | (d); + EMIT_INST; +} + +static inline void emitMOVLLG(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xC600 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitMOVLSG(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xC200 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitMOVT(sh2rec_block_t *b, int n) { + uint16_t inst = 0x0029 | (n << 8); + EMIT_INST; +} + +static inline void emitALU(sh2rec_block_t *b, int m, int n, uint16_t op) { + uint16_t inst = (n << 8) | (m << 4) | op; + EMIT_INST; +} + +static inline void emitSHIFT(sh2rec_block_t *b, int n, uint16_t op) { + uint16_t inst = (n << 8) | op; + EMIT_INST; +} + +static inline void emitADDI(sh2rec_block_t *b, int imm, int n) { + uint16_t inst = 0x7000 | (n << 8) | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitANDI(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xC900 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitORI(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xCB00 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitXORI(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xCA00 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitSHLL2(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4008 | (n << 8); + EMIT_INST; +} + +static inline void emitSHLL8(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4018 | (n << 8); + EMIT_INST; +} + +static inline void emitSHLL16(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4028 | (n << 8); + EMIT_INST; +} + +static inline void emitSHLR2(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4009 | (n << 8); + EMIT_INST; +} + +static inline void emitSHLR8(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4019 | (n << 8); + EMIT_INST; +} + +static inline void emitSHLR16(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4029 | (n << 8); + EMIT_INST; +} + +static inline void emitCMPIM(sh2rec_block_t *b, int imm) { + uint16_t inst = 0x8800 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitCMPPL(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4015 | (n << 8); + EMIT_INST; +} + +static inline void emitCMPPZ(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4011 | (n << 8); + EMIT_INST; +} + +static inline void emitADDV(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x300F | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitSUBV(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x300B | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitLDS(sh2rec_block_t *b, int m, int sr) { + uint16_t inst = 0x400A | (m << 8) | (sr << 4); + EMIT_INST; +} + +static inline void emitSTS(sh2rec_block_t *b, int sr, int n) { + uint16_t inst = 0x000A | (n << 8) | (sr << 4); + EMIT_INST; +} + +static inline void emitLDC(sh2rec_block_t *b, int m, int sr) { + uint16_t inst = 0x400E | (m << 8) | (sr << 4); + EMIT_INST; +} + +static inline void emitSTC(sh2rec_block_t *b, int sr, int n) { + uint16_t inst = 0x0002 | (n << 8) | (sr << 4); + EMIT_INST; +} + +static inline void emitDT(sh2rec_block_t *b, int n) { + uint16_t inst = 0x4010 | (n << 8); + EMIT_INST; +} + +static inline void emitTSTI(sh2rec_block_t *b, int imm) { + uint16_t inst = 0xC800 | (imm & 0xFF); + EMIT_INST; +} + +static inline void emitBRA(sh2rec_block_t *b, int d) { + uint16_t inst = 0xA000 | (d); + EMIT_INST; +} + +static inline void emitDIV0S(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x2007 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitDIV1(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x3004 | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitRTS(sh2rec_block_t *b) { + uint16_t inst = 0x000B; + EMIT_INST; +} + +static inline void emitNOP(sh2rec_block_t *b) { + uint16_t inst = 0x0009; + EMIT_INST; +} + +static inline void emitJSR(sh2rec_block_t *b, int m) { + uint16_t inst = 0x400B | (m << 8); + EMIT_INST; +} + +static inline void emitMACL(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x000F | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitMACW(sh2rec_block_t *b, int m, int n) { + uint16_t inst = 0x400F | (n << 8) | (m << 4); + EMIT_INST; +} + +static inline void emitCLRMAC(sh2rec_block_t *b) { + uint16_t inst = 0x0028; + EMIT_INST; +} + +static inline void emitBF(sh2rec_block_t *b, int disp) { + uint16_t inst = 0x8B00 | (disp & 0xFF); + EMIT_INST; +} + +static inline void emitBT(sh2rec_block_t *b, int disp) { + uint16_t inst = 0x8900 | (disp & 0xFF); + EMIT_INST; +} + +static inline void generateALUOP(uint16_t inst, sh2rec_block_t *b, int op) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, op); /* R2 <- R2 o R3 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ +} + +static inline void generateSHIFT(uint16_t inst, sh2rec_block_t *b, int op) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitSHIFT(b, R2, op); /* R2 <- R2 op */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ +} + +static inline void generateCOMP(uint16_t inst, sh2rec_block_t *b, int op) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R3, R2, op); /* R2 op R3 */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ +} + +static void generateADD(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_ADD); + b->pc += 2; +} + +static void generateADDI(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitADDI(b, imm, R2); /* R2 <- R2 + #imm */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateADDC(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R3, R2, OP_ADDC); /* R2 = R2 + R3 + T (carry to T) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateADDV(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitADDV(b, R3, R2); /* R2 = R2 + R3 (overflow to T Bit) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateAND(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_AND); + b->pc += 2; +} + +static void generateANDI(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ + emitANDI(b, imm); /* R0 <- R0 & #imm */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateANDM(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitANDI(b, imm); /* R0 <- R0 & #imm */ + emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateBF(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_CD(inst); + uint32_t val = b->pc + 2; + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLI(b, 4, R2); /* R2 <- sh2[PC] + 2 */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitMOVI(b, 0, R0); /* R0 <- 0 */ + emitBT(b, 2); /* Branch around the addition if needed */ + emitMOVI(b, disp, R0); /* R0 <- displacement */ + emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ + emitADDI(b, 2, R0); /* R0 <- R0 + 2 */ + emitRTS(b); /* Return to sender! */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + if(((uint32_t)b->ptr) & 0x03) + emit16(b, 0); /* Padding if we need it */ + emit32(b, val); /* The next PC value (if not taken) */ + + b->cycles += 2; /* 2 Cycles (if not taken) */ + /* XXXX: Handle taken case cycle difference */ +} + +static void generateBFS(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_CD(inst); + uint32_t val = b->pc + 4; + int n = (((uint32_t)b->ptr) & 0x03) ? 3 : 4; + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLI(b, n, R2); /* R2 <- sh2[PC] + 4 */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitMOVI(b, 0, R0); /* R0 <- 0 */ + emitBT(b, 1); /* Branch around the addition if needed */ + emitMOVI(b, disp, R0); /* R0 <- displacement */ + emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ + + if(((uint32_t)b->ptr) & 0x03) { + emitBRA(b, 3); /* Branch around the constant */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitBRA(b, 2); /* Branch around the constant */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + } + + emit32(b, val); /* The next PC value (if not taken) */ + emitMOVLM(b, R0, R15); /* Push the next PC on the stack */ + + /* Deal with the delay slot here */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + ++b->cycles; /* 1 Cycle (if not taken) */ + /* XXXX: Handle taken case cycle difference */ +} + +static void generateBRA(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_BCD(inst); + int32_t val; + + if(disp & 0x00000800) { + disp |= 0xFFFFF000; + } + + val = b->pc + 4 + (disp << 1); + + emitMOVLI(b, 1, R2); /* R2 <- sh2[PC] + 4 + disp */ + + if(((uint32_t)b->ptr) & 0x03) { + emitBRA(b, 3); /* Branch around the constant */ + emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitBRA(b, 2); /* Branch around the constant */ + emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ + } + + emit32(b, (uint32_t )val); /* The next PC */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateBRAF(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + uint32_t val = b->pc + 4; + + if(((uint32_t)b->ptr) & 0x03) { + emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] */ + emitBRA(b, 3); /* Branch around the constant */ + emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] */ + emitBRA(b, 2); /* Branch around the constant */ + emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (delay slot) */ + } + + emit32(b, val); /* The value to use as the base for PC */ + emitMOVLM(b, R2, R15); /* Push the next PC */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateBSR(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_BCD(inst); + int32_t val; + int32_t val2 = b->pc + 4; + + if(disp & 0x00000800) { + disp |= 0xFFFFF000; + } + + val = b->pc + 4 + (disp << 1); + + if(((uint32_t)b->ptr) & 0x03) { + emitMOVLI(b, 2, R2); /* R2 <- sh2[PC] + 4 + disp */ + emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ + emitBRA(b, 5); /* Branch around the constant */ + emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitMOVLI(b, 1, R2); /* R2 <- sh2[PC] + 4 + disp */ + emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ + emitBRA(b, 4); /* Branch around the constant */ + emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ + } + + emit32(b, (uint32_t)val); /* The next PC */ + emit32(b, (uint32_t)val2); /* The value for PR */ + emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateBSRF(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + uint32_t val = b->pc + 4; + + emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ + + if(((uint32_t)b->ptr) & 0x03) { + emitBRA(b, 3); /* Branch around the constant */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitBRA(b, 2); /* Branch around the constant */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ + } + + emit32(b, val); /* The value to put in PR */ + emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (branch target) */ + emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ + emitMOVLM(b, R2, R15); /* Push the next PC */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateBT(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_CD(inst); + uint32_t val = b->pc + 2; + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLI(b, 4, R2); /* R2 <- sh2[PC] + 4 */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitMOVI(b, 0, R0); /* R0 <- 0 */ + emitBF(b, 2); /* Branch around the addition if needed */ + emitMOVI(b, disp, R0); /* R0 <- displacement */ + emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ + emitADDI(b, 2, R0); /* R0 <- R0 + 2 */ + emitRTS(b); /* Return to sender! */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + if(((uint32_t)b->ptr) & 0x03) + emit16(b, 0); /* Padding if we need it */ + emit32(b, val); /* The next PC value (if not taken) */ + + b->cycles += 2; /* 2 Cycles (if not taken) */ + /* XXXX: Handle taken case cycle difference */ +} + +static void generateBTS(uint16_t inst, sh2rec_block_t *b) { + int disp = INSTRUCTION_CD(inst); + uint32_t val = b->pc + 4; + int n = (((uint32_t)b->ptr) & 0x03) ? 3 : 4; + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLI(b, n, R2); /* R2 <- sh2[PC] + 2 */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitMOVI(b, 0, R0); /* R0 <- 0 */ + emitBF(b, 1); /* Branch around the addition if needed */ + emitMOVI(b, disp, R0); /* R0 <- displacement */ + emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ + + if(((uint32_t)b->ptr) & 0x03) { + emitBRA(b, 3); /* Branch around the constant */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitBRA(b, 2); /* Branch around the constant */ + emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ + } + + emit32(b, val); /* The next PC value (if not taken) */ + emitMOVLM(b, R0, R15); /* Push the next PC */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + ++b->cycles; /* 1 Cycle (if not taken) */ + /* XXXX: Handle taken case cycle difference */ +} + +static void generateCLRMAC(uint16_t inst, sh2rec_block_t *b) { + emitCLRMAC(b); /* MACL/MACH <- 0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateCLRT(uint16_t inst, sh2rec_block_t *b) { + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVI(b, 0xFE, R1); /* R1 <- 0xFFFFFFFE */ + emitALU(b, R3, R0, OP_AND); /* Clear T bit */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateCMPEQ(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPEQ); + b->pc += 2; +} + +static void generateCMPGE(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPGE); + b->pc += 2; +} + +static void generateCMPGT(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPGT); + b->pc += 2; +} + +static void generateCMPHI(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPHI); + b->pc += 2; +} + +static void generateCMPHS(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPHS); + b->pc += 2; +} + +static void generateCMPSTR(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_CMPSTR); + b->pc += 2; +} + +static void generateCMPPL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitCMPPL(b, R2); /* cmp/pl R2 */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateCMPPZ(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitCMPPZ(b, R2); /* cmp/pz R2 */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateCMPIM(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOV(b, R0, R2); /* R2 <- R0 */ + emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ + emitSHIFT(b, R2, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitCMPIM(b, imm); /* cmp/eq R0, #imm */ + emitSHIFT(b, R2, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOV(b, R2, R0); /* R0 <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateDIV0S(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVI(b, 0x03, R4); /* R4 <- 0x03 */ + emitANDI(b, 0xF2); /* Clear M, Q, and T */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHLL8(b, R4); /* R4 <<= 8 */ + emitSHIFT(b, R0, OP_SHLR); /* Chop off the T from the SH2 reg */ + emitDIV0S(b, R3, R2); /* div0s to grab the M, Q, T bits needed */ + emitSTC(b, R_SR, R5); /* Save SR to R5 */ + emitALU(b, R4, R5, OP_AND); /* Grab M, Q from the SR */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitALU(b, R5, R0, OP_OR); /* Save M, Q into the SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateDIV0U(uint16_t inst, sh2rec_block_t *b) { + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitANDI(b, 0xF2); /* Mask off M, Q, and T bits */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateDIV1(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVI(b, 0x03, R4); /* R4 <- 0x03 */ + emitSHLL8(b, R4); /* R4 <<= 8 */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitMOV(b, R4, R6); /* R6 <- R4 */ + emitALU(b, R0, R6, OP_AND); /* Grab SH2 M and Q bits */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T in place */ + emitSTC(b, R_SR, R5); /* Save SR to R5 */ + emitALU(b, R4, R7, OP_NOT); /* Set up the mask to clear M and Q */ + emitALU(b, R7, R5, OP_AND); /* Clear M, Q */ + emitALU(b, R6, R5, OP_OR); /* Put SH2's M and Q in place */ + emitLDC(b, R5, R_SR); /* Put the modified SR in place */ + emitDIV1(b, R3, R2); /* Do the division! */ + emitSTC(b, R_SR, R5); /* Save updated SR to R5 */ + emitALU(b, R4, R5, OP_AND); /* Grab M and Q from the SR */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitANDI(b, 0xF3); /* Clear M and Q from the SH2 reg */ + emitALU(b, R5, R0, OP_OR); /* Save M and Q into the SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateDMULS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_DMULS); /* MACH/MACL <- (s32)R2 * (s32)R3 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateDMULU(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_DMULU); /* MACH/MACL <- (u32)R2 * (u32)R3 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateDT(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitDT(b, R2); /* R2 = R2 - 1 (T Bit = non-zero) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateEXTSB(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_EXTSB); + b->pc += 2; +} + +static void generateEXTSW(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_EXTSW); + b->pc += 2; +} + +static void generateEXTUB(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_EXTUB); + b->pc += 2; +} + +static void generateEXTUW(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_EXTUW); + b->pc += 2; +} + +static void generateJMP(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* Grab the next PC value */ + emitMOVLM(b, R0, R15); /* Push the next PC on the stack */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateJSR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + uint32_t val = b->pc + 4; + + emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ + + if(((uint32_t)b->ptr) & 0x03) { + emitBRA(b, 3); /* Branch around the constant */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ + emit16(b, 0); /* Padding since we need it */ + } + else { + emitBRA(b, 2); /* Branch around the constant */ + emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ + } + + emit32(b, val); /* The value to put in PR */ + emitMOVLM(b, R2, R15); /* Push the next PC */ + emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateLDCSR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVWI(b, 2, R2); /* R2 <- 0x03F3 */ + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitBRA(b, 1); /* Jump beyond the constant */ + emitALU(b, R2, R0, OP_AND); /* R0 <- R0 & R2 (delay slot) */ + emit16(b, 0x03F3); /* 0x03F3, grabbed by the emitMOVWI */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDCGBR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitMOVLSG(b, 17); /* sh2[GBR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDCVBR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitMOVLSG(b, 18); /* sh2[VBR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDCMSR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVWI(b, 2, R2); /* R2 <- 0x03F3 */ + emitBRA(b, 1); /* Jump beyond the constant */ + emitALU(b, R2, R0, OP_AND); /* R0 <- R0 & R2 (delay slot) */ + emit16(b, 0x03F3); /* 0x03F3, grabbed by the emitMOVWI */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateLDCMGBR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLSG(b, 17); /* sh2[GBR] <- R0 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateLDCMVBR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLSG(b, 18); /* sh2[VBR] <- R0 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateLDSMACH(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitLDS(b, R0, R_MACH); /* MACH <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDSMACL(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitLDS(b, R0, R_MACL); /* MACL <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDSPR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ + emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDSMMACH(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitLDS(b, R0, R_MACH); /* MACH <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDSMMACL(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitLDS(b, R0, R_MACL); /* MACL <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateLDSMPR(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMACL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitADDI(b, 4, R4); /* R4 <- R4 + 4 */ + emitMOVLS4(b, R4, regm, R8); /* sh2[Rm] <- R4 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitADDI(b, -4, R4); /* R4 <- R4 - 4 (delay slot) */ + emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitADDI(b, 4, R4); /* R4 <- R4 + 4 */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitADDI(b, -4, R4); /* R4 <- R4 - 4 (delay slot) */ + emitSTC(b, R_SR, R2); /* R2 <- SR */ + emitMOVI(b, 0xFD, R3); /* R3 <- 0xFFFFFFFD */ + emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitALU(b, R2, R3, OP_AND); /* R3 <- R2 & R3 (Mask out S Bit) */ + emitANDI(b, 0x02); /* R0 <- R0 & 0x02 (S Bit) */ + emitALU(b, R0, R3, OP_OR); /* R3 <- R0 | R3 (Put SH2 S Bit in) */ + emitLDC(b, R3, R_SR); /* SR <- R3 */ + emitMACL(b, R15, R15); /* Perform the MAC.L */ + emitLDC(b, R2, R_SR); /* SR <- R2 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateMACW(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emitADDI(b, 2, R4); /* R4 <- R4 + 2 */ + emitMOVLS4(b, R4, regm, R8); /* sh2[Rm] <- R4 */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitADDI(b, -2, R4); /* R4 <- R4 - 2 (delay slot) */ + emitMOVWM(b, R0, R15); /* Push R0 onto the stack */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emitADDI(b, 2, R4); /* R4 <- R4 + 2 */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitADDI(b, -2, R4); /* R4 <- R4 - 2 (delay slot) */ + emitMOVWM(b, R0, R15); /* Push R0 onto the stack */ + emitSTC(b, R_SR, R2); /* R2 <- SR */ + emitMOVI(b, 0xFD, R3); /* R3 <- 0xFFFFFFFD */ + emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitALU(b, R2, R3, OP_AND); /* R3 <- R2 & R3 (Mask out S Bit) */ + emitANDI(b, 0x02); /* R0 <- R0 & 0x02 (S Bit) */ + emitALU(b, R0, R3, OP_OR); /* R3 <- R0 | R3 (Put SH2 S Bit in) */ + emitLDC(b, R3, R_SR); /* SR <- R3 */ + emitMACW(b, R15, R15); /* Perform the MAC.W */ + emitLDC(b, R2, R_SR); /* SR <- R2 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateMOV(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R2); /* R2 <- sh2[Rm] */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryWriteByte */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryWriteWord */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadByte */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBM(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -1, R4); /* R4 -= 1 */ + emitJSR(b, R0); /* Call MappedMemoryWriteByte */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWM(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -2, R4); /* R4 -= 2 */ + emitJSR(b, R0); /* Call MappedMemoryWriteWord */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLM(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitJSR(b, R0); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBP(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 1, R1); /* R1 <- 1 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadByte */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWP(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 2, R1); /* R1 <- 2 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLP(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVI(b, 4, R1); /* R1 <- 4 */ + emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBS0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitJSR(b, R0); /* Call MappedMemoryWriteByte */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWS0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitJSR(b, R0); /* Call MappedMemoryWriteWord */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLS0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitJSR(b, R0); /* Call MappedMemoryWriteLong */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBL0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitJSR(b, R0); /* Call MappedMemoryReadByte */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWL0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLL0(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVI(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int imm = INSTRUCTION_CD(inst); + + emitMOVI(b, imm, R2); /* R2 <- #imm */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWI(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int imm = INSTRUCTION_CD(inst); + uint32_t addr = b->pc + 4 + (imm << 1); + + if(((uint32_t)b->ptr) & 0x03) { + emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ + emitBRA(b, 2); /* Jump beyond the constant */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emit32(b, addr); /* MOV.W effective address */ + } + else { + emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ + emitBRA(b, 3); /* Jump beyond the constant */ + emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ + emit16(b, 0); /* Padding, for alignment issues */ + emit32(b, addr); /* MOV.W effective address */ + } + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadWord */ + emitNOP(b); /* XXXX: Nothing to put here */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLI(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int imm = INSTRUCTION_CD(inst); + uint32_t addr = ((b->pc + 4) & 0xFFFFFFFC) + (imm << 2); + + if(((uint32_t)b->ptr) & 0x03) { + emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ + emitBRA(b, 2); /* Jump beyond the constant */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emit32(b, addr); /* MOV.L effective address */ + } + else { + emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ + emitBRA(b, 3); /* Jump beyond the constant */ + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emit16(b, 0); /* Padding, for alignment issues */ + emit32(b, addr); /* MOV.L effective address */ + } + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitNOP(b); /* XXXX: Nothing to put here */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBLG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read byte */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWLG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitMOVLL4(b, R9, 1, R1); /* R1 <- MappedMemoryReadWord */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitSHIFT(b, R4, OP_SHLL); /* Double displacement */ + emitJSR(b, R1); /* Call MappedMemoryReadWord */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLLG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitSHLL2(b, R4); /* Quadruple displacement */ + emitJSR(b, R1); /* Call MappedMemoryReadLong */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBSG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWSG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ + emitMOVLL4(b, R9, 4, R1); /* R1 <- MappedMemoryWriteWord */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitSHIFT(b, R4, OP_SHLL); /* Double displacement */ + emitJSR(b, R1); /* Call MappedMemoryWriteWord */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLSG(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ + emitMOVI(b, imm, R4); /* R4 <- Displacement */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitSHLL2(b, R4); /* Quadruple displacement */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBS4(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst); + + emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWS4(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst) << 1; + + emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ + emitMOVLL4(b, R9, 4, R1); /* R1 <- MappedMemoryWriteWord */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryWriteWord */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLS4(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst) << 2; + + emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVBL4(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst); + + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read byte */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVWL4(uint16_t inst, sh2rec_block_t *b) { + int regm = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst) << 1; + + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R9, 1, R1); /* R1 <- MappedMemoryReadWord */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryReadWord */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read word */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVLL4(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + int imm = INSTRUCTION_D(inst) << 2; + + emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ + emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R1); /* Call MappedMemoryReadLong */ + emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVA(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + uint32_t addr = ((b->pc + 4) & 0xFFFFFFFC) + (imm << 2); + + if(((uint32_t)b->ptr) & 0x03) { + emitMOVLI(b, 1, R2); /* R2 <- calculated effective addr */ + emitBRA(b, 2); /* Jump beyond the constant */ + emitMOVLS4(b, R2, 0, R8); /* sh2[R0] <- R2 */ + emit32(b, addr); /* MOVA effective address */ + } + else { + emitMOVLI(b, 1, R2); /* R2 <- calculated effective addr */ + emitBRA(b, 3); /* Jump beyond the constant */ + emitMOVLS4(b, R2, 0, R8); /* sh2[R0] <- R2 */ + emit16(b, 0); /* Padding, for alignment issues */ + emit32(b, addr); /* MOVA effective address */ + } + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMOVT(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitANDI(b, 0x01); /* Grab T Bit */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- T Bit */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMULL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_MULL); /* MACL <- R2 * R3 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateMULS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_MULS); /* MACL <- (s16)R2 * (s16)R3 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateMULU(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_MULU); /* MACL <- (u16)R2 * (u16)R3 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateNEG(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitALU(b, R3, R2, OP_NEG); /* R2 <- 0 - R3 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateNEGC(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R3, R2, OP_NEGC); /* R2 = 0 - R3 - T (borrow to T) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateNOP(uint16_t inst, sh2rec_block_t *b) { + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateNOT(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_NOT); + b->pc += 2; +} + +static void generateOR(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_OR); + b->pc += 2; +} + +static void generateORI(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ + emitORI(b, imm); /* R0 <- R0 | #imm */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateORM(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitORI(b, imm); /* R0 <- R0 | #imm */ + emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateROTCL(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_ROTCL); + b->pc += 2; +} + +static void generateROTCR(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_ROTCR); + b->pc += 2; +} + +static void generateROTL(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_ROTL); + b->pc += 2; +} + +static void generateROTR(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_ROTR); + b->pc += 2; +} + +static void generateRTE(uint16_t inst, sh2rec_block_t *b) { + emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadLong */ + emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] (delay slot) */ + emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ + emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ + emitMOVI(b, 4, R2); /* R2 <- 4 */ + emitALU(b, R2, R4, OP_ADD); /* R4 <- R4 + R2 */ + emitMOVLM(b, R0, R15); /* Push the next PC */ + emitALU(b, R4, R2, OP_ADD); /* R2 <- R4 + R2 */ + emitJSR(b, R1); /* Call MappedMemoryReadLong */ + emitMOVLS4(b, R2, 15, R8); /* sh2[R15] <- R2 (delay slot) */ + emitMOVWI(b, 1, R1); /* R1 <- 0x000003F3 */ + emitBRA(b, 1); /* Branch around the constant */ + emitALU(b, R1, R0, OP_AND); /* R0 <- R0 & R1 */ + emit16(b, 0x03F3); /* Mask for SR register */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 4; /* 4 Cycles */ +} + +static void generateRTS(uint16_t inst, sh2rec_block_t *b) { + emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ + emitMOVLM(b, R0, R15); /* Push the PR on the stack */ + + /* Deal with the delay slot */ + b->pc += 2; + sh2rec_rec_inst(b, 1); + + emitRTS(b); /* Return to sender! */ + emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ + + b->cycles += 2; /* 2 Cycles */ +} + +static void generateSETT(uint16_t inst, sh2rec_block_t *b) { + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitORI(b, 0x01); /* Set T Bit */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHAL(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_SHAL); + b->pc += 2; +} + +static void generateSHAR(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_SHAR); + b->pc += 2; +} + +static void generateSHLL(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_SHLL); + b->pc += 2; +} + +static void generateSHLL2(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLL2(b, R2); /* R2 <- R2 << 2 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHLL8(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLL8(b, R2); /* R2 <- R2 << 8 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHLL16(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLL16(b, R2); /* R2 <- R2 << 16 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHLR(uint16_t inst, sh2rec_block_t *b) { + generateSHIFT(inst, b, OP_SHLR); + b->pc += 2; +} + +static void generateSHLR2(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLR2(b, R2); /* R2 <- R2 >> 2 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHLR8(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLR8(b, R2); /* R2 <- R2 >> 8 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSHLR16(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitSHLR16(b, R2); /* R2 <- R2 >> 16 */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSLEEP(uint16_t inst, sh2rec_block_t *b) { + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateSTCSR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTCGBR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTCVBR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTCMSR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitMOV(b, R0, R5); /* R5 <- R0 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateSTCMGBR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitMOV(b, R0, R5); /* R5 <- R0 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateSTCMVBR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitMOV(b, R0, R5); /* R5 <- R0 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 2; /* 2 Cycles */ + b->pc += 2; +} + +static void generateSTSMACH(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitSTS(b, R_MACH, R0); /* R0 <- MACH */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTSMACL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitSTS(b, R_MACL, R0); /* R0 <- MACL */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTSPR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ + emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTSMMACH(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitSTS(b, R_MACH, R5); /* R5 <- MACH */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTSMMACL(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitSTS(b, R_MACL, R5); /* R5 <- MACL */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSTSMPR(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitADDI(b, -4, R4); /* R4 -= 4 */ + emitMOV(b, R0, R5); /* R5 <- R0 */ + emitJSR(b, R1); /* Call MappedMemoryWriteLong */ + emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSUB(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_SUB); + b->pc += 2; +} + +static void generateSUBC(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R3, R2, OP_SUBC); /* R2 = R2 - R3 - T (borrow to T) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSUBV(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + int regm = INSTRUCTION_C(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ + emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitSUBV(b, R3, R2); /* R2 = R2 - R3 (underflow to T Bit) */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateSWAPB(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_SWAPB); + b->pc += 2; +} + +static void generateSWAPW(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_SWAPW); + b->pc += 2; +} + +static void generateTAS(uint16_t inst, sh2rec_block_t *b) { + int regn = INSTRUCTION_B(inst); + + emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitJSR(b, R0); /* Call MappedMemoryReadByte */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] (delay slot) */ + emitMOV(b, R0, R5); /* R5 <- R0 (byte read) */ + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOVI(b, 0x80, R2); /* R2 <- 0x80 */ + emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R5, R5, OP_TST); /* T <- 1 if byte == 0, 0 otherwise */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitALU(b, R2, R5, OP_OR); /* R5 <- R5 | 0x80 (delay slot) */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 4; /* 4 Cycles */ + b->pc += 2; +} + +static void generateTRAPA(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + int disp = (((uint32_t)(b->ptr)) & 0x03) ? 5 : 6; + uint32_t val = b->pc + 2; + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MemoryMappedWriteLong */ + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitADDI(b, -4, R4); /* R4 <- R4 - 4 */ + emitJSR(b, R1); /* Call MemoryMappedWriteLong */ + emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ + emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ + emitMOVLL4(b, R9, 5, R1); /* R1 <- MemoryMappedWriteLong */ + emitADDI(b, -8, R4); /* R4 <- R4 - 8 */ + emitMOVLI(b, disp, R5); /* R5 <- Updated PC value (to be stacked) */ + emitJSR(b, R1); /* Call MemoryMappedWriteLong */ + emitMOVLS4(b, R4, 15, R8); /* sh2[R15] <- R4 (delay slot) */ + emitMOVI(b, imm, R4); /* R4 <- immediate data */ + emitALU(b, R4, R4, OP_EXTUB); /* Zero-extend R4 */ + emitMOVLL4(b, R9, 2, R1); /* R1 <- MemoryMappedReadLong */ + emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ + emitSHLL2(b, R4); /* R4 <- R4 << 2 */ + emitJSR(b, R1); /* Call MemoryMappedReadLong */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 (delay slot) */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + emitRTS(b); /* Return to sender! */ + emitNOP(b); /* XXXX: Nothing here */ + if(((uint32_t)b->ptr) & 0x03) + emit16(b, 0); /* Padding for the alignment */ + emit32(b, val); /* The PC value to be loaded by the MOVLI */ + + b->cycles += 8; /* 8 Cycles */ +} + +static void generateTST(uint16_t inst, sh2rec_block_t *b) { + generateCOMP(inst, b, OP_TST); + b->pc += 2; +} + +static void generateTSTI(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitMOV(b, R0, R2); /* R2 <- R0 */ + emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ + emitSHIFT(b, R2, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitTSTI(b, imm); /* tst #imm, r0 */ + emitSHIFT(b, R2, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOV(b, R2, R0); /* R0 <- R2 */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateTSTM(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 (delay slot) */ + emitMOV(b, R0, R5); /* R5 <- R0 (byte read) */ + emitMOVI(b, imm, R3); /* R3 <- immediate value */ + emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ + emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ + emitALU(b, R3, R5, OP_TST); /* T <- 1 if (R5 & imm) == 0, 0 otherwise */ + emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ + emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateXOR(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_XOR); + b->pc += 2; +} + +static void generateXORI(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ + emitXORI(b, imm); /* R0 <- R0 ^ #imm */ + emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ + + ++b->cycles; /* 1 Cycle */ + b->pc += 2; +} + +static void generateXORM(uint16_t inst, sh2rec_block_t *b) { + int imm = INSTRUCTION_CD(inst); + + emitSTS(b, R_PR, R10); /* R10 <- PR */ + emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ + emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ + emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ + emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ + emitJSR(b, R1); /* Call MappedMemoryReadByte */ + emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ + emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ + emitXORI(b, imm); /* R0 <- R0 ^ #imm */ + emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ + emitJSR(b, R1); /* Call MappedMemoryWriteByte */ + emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ + emitLDS(b, R10, R_PR); /* PR <- R10 */ + + b->cycles += 3; /* 3 Cycles */ + b->pc += 2; +} + +static void generateXTRCT(uint16_t inst, sh2rec_block_t *b) { + generateALUOP(inst, b, OP_XTRCT); + b->pc += 2; +} + +int sh2rec_rec_inst(sh2rec_block_t *b, int isdelay) { + uint16_t inst = MappedMemoryReadWord(b->pc); + int done = 0; + + switch(INSTRUCTION_A(inst)) { + case 0: + switch(INSTRUCTION_D(inst)) { + case 2: + switch(INSTRUCTION_C(inst)) { + case 0: generateSTCSR(inst, b); break; + case 1: generateSTCGBR(inst, b); break; + case 2: generateSTCVBR(inst, b); break; + default: return -1; + } + break; + + case 3: + switch(INSTRUCTION_C(inst)) { + case 0: generateBSRF(inst, b); done = 1; break; + case 2: generateBRAF(inst, b); done = 1; break; + default: return -1; + } + break; + + case 4: generateMOVBS0(inst, b); break; + case 5: generateMOVWS0(inst, b); break; + case 6: generateMOVLS0(inst, b); break; + case 7: generateMULL(inst, b); break; + case 8: + switch(INSTRUCTION_C(inst)) { + case 0: generateCLRT(inst, b); break; + case 1: generateSETT(inst, b); break; + case 2: generateCLRMAC(inst, b); break; + default: return -1; + } + break; + + case 9: + switch(INSTRUCTION_C(inst)) { + case 0: generateNOP(inst, b); break; + case 1: generateDIV0U(inst, b); break; + case 2: generateMOVT(inst, b); break; + default: return -1; + } + break; + + case 10: + switch(INSTRUCTION_C(inst)) { + case 0: generateSTSMACH(inst, b); break; + case 1: generateSTSMACL(inst, b); break; + case 2: generateSTSPR(inst, b); break; + default: return -1; + } + break; + + case 11: + switch(INSTRUCTION_C(inst)) { + case 0: generateRTS(inst, b); done = 1; break; + case 1: generateSLEEP(inst, b); break; + case 2: generateRTE(inst, b); done = 1; break; + default: return -1; + } + break; + + case 12: generateMOVBL0(inst, b); break; + case 13: generateMOVWL0(inst, b); break; + case 14: generateMOVLL0(inst, b); break; + case 15: generateMACL(inst, b); break; + default: return -1; + } + break; + + case 1: generateMOVLS4(inst, b); break; + case 2: + switch(INSTRUCTION_D(inst)) { + case 0: generateMOVBS(inst, b); break; + case 1: generateMOVWS(inst, b); break; + case 2: generateMOVLS(inst, b); break; + case 4: generateMOVBM(inst, b); break; + case 5: generateMOVWM(inst, b); break; + case 6: generateMOVLM(inst, b); break; + case 7: generateDIV0S(inst, b); break; + case 8: generateTST(inst, b); break; + case 9: generateAND(inst, b); break; + case 10: generateXOR(inst, b); break; + case 11: generateOR(inst, b); break; + case 12: generateCMPSTR(inst, b); break; + case 13: generateXTRCT(inst, b); break; + case 14: generateMULU(inst, b); break; + case 15: generateMULS(inst, b); break; + default: return -1; + } + break; + + case 3: + switch(INSTRUCTION_D(inst)) { + case 0: generateCMPEQ(inst, b); break; + case 2: generateCMPHS(inst, b); break; + case 3: generateCMPGE(inst, b); break; + case 4: generateDIV1(inst, b); break; + case 5: generateDMULU(inst, b); break; + case 6: generateCMPHI(inst, b); break; + case 7: generateCMPGT(inst, b); break; + case 8: generateSUB(inst, b); break; + case 10: generateSUBC(inst, b); break; + case 11: generateSUBV(inst, b); break; + case 12: generateADD(inst, b); break; + case 13: generateDMULS(inst, b); break; + case 14: generateADDC(inst, b); break; + case 15: generateADDV(inst, b); break; + default: return -1; + } + break; + + case 4: + switch(INSTRUCTION_D(inst)) { + case 0: + switch(INSTRUCTION_C(inst)) { + case 0: generateSHLL(inst, b); break; + case 1: generateDT(inst, b); break; + case 2: generateSHAL(inst, b); break; + default: return -1; + } + break; + + case 1: + switch(INSTRUCTION_C(inst)) { + case 0: generateSHLR(inst, b); break; + case 1: generateCMPPZ(inst, b); break; + case 2: generateSHAR(inst, b); break; + default: return -1; + } + break; + + case 2: + switch(INSTRUCTION_C(inst)) { + case 0: generateSTSMMACH(inst, b); break; + case 1: generateSTSMMACL(inst, b); break; + case 2: generateSTSMPR(inst, b); break; + default: return -1; + } + break; + + case 3: + switch(INSTRUCTION_C(inst)) { + case 0: generateSTCMSR(inst, b); break; + case 1: generateSTCMGBR(inst, b); break; + case 2: generateSTCMVBR(inst, b); break; + default: return -1; + } + break; + + case 4: + switch(INSTRUCTION_C(inst)) { + case 0: generateROTL(inst, b); break; + case 2: generateROTCL(inst, b); break; + default: return -1; + } + break; + + case 5: + switch(INSTRUCTION_C(inst)) { + case 0: generateROTR(inst, b); break; + case 1: generateCMPPL(inst, b); break; + case 2: generateROTCR(inst, b); break; + default: return -1; + } + break; + + case 6: + switch(INSTRUCTION_C(inst)) { + case 0: generateLDSMMACH(inst, b); break; + case 1: generateLDSMMACL(inst, b); break; + case 2: generateLDSMPR(inst, b); break; + default: return -1; + } + break; + + case 7: + switch(INSTRUCTION_C(inst)) { + case 0: generateLDCMSR(inst, b); break; + case 1: generateLDCMGBR(inst, b); break; + case 2: generateLDCMVBR(inst, b); break; + default: return -1; + } + break; + + case 8: + switch(INSTRUCTION_C(inst)) { + case 0: generateSHLL2(inst, b); break; + case 1: generateSHLL8(inst, b); break; + case 2: generateSHLL16(inst, b); break; + default: return -1; + } + break; + + case 9: + switch(INSTRUCTION_C(inst)) { + case 0: generateSHLR2(inst, b); break; + case 1: generateSHLR8(inst, b); break; + case 2: generateSHLR16(inst, b); break; + default: return -1; + } + break; + + case 10: + switch(INSTRUCTION_C(inst)) { + case 0: generateLDSMACH(inst, b); break; + case 1: generateLDSMACL(inst, b); break; + case 2: generateLDSPR(inst, b); break; + default: return -1; + } + break; + + case 11: + switch(INSTRUCTION_C(inst)) { + case 0: generateJSR(inst, b); done = 1; break; + case 1: generateTAS(inst, b); break; + case 2: generateJMP(inst, b); done = 1; break; + default: return -1; + } + break; + + case 14: + switch(INSTRUCTION_C(inst)) { + case 0: generateLDCSR(inst, b); break; + case 1: generateLDCGBR(inst, b); break; + case 2: generateLDCVBR(inst, b); break; + default: return -1; + } + break; + + case 15: generateMACW(inst, b); break; + default: return -1; + } + break; + + case 5: generateMOVLL4(inst, b); break; + + case 6: + switch(INSTRUCTION_D(inst)) { + case 0: generateMOVBL(inst, b); break; + case 1: generateMOVWL(inst, b); break; + case 2: generateMOVLL(inst, b); break; + case 3: generateMOV(inst, b); break; + case 4: generateMOVBP(inst, b); break; + case 5: generateMOVWP(inst, b); break; + case 6: generateMOVLP(inst, b); break; + case 7: generateNOT(inst, b); break; + case 8: generateSWAPB(inst, b); break; + case 9: generateSWAPW(inst, b); break; + case 10: generateNEGC(inst, b); break; + case 11: generateNEG(inst, b); break; + case 12: generateEXTUB(inst, b); break; + case 13: generateEXTUW(inst, b); break; + case 14: generateEXTSB(inst, b); break; + case 15: generateEXTSW(inst, b); break; + } + break; + + case 7: generateADDI(inst, b); break; + + case 8: + switch(INSTRUCTION_B(inst)) { + case 0: generateMOVBS4(inst, b); break; + case 1: generateMOVWS4(inst, b); break; + case 4: generateMOVBL4(inst, b); break; + case 5: generateMOVWL4(inst, b); break; + case 8: generateCMPIM(inst, b); break; + case 9: generateBT(inst, b); done = 1; break; + case 11: generateBF(inst, b); done = 1; break; + case 13: generateBTS(inst, b); done = 1; break; + case 15: generateBFS(inst, b); done = 1; break; + default: return -1; + } + break; + + case 9: generateMOVWI(inst, b); break; + case 10: generateBRA(inst, b); done = 1; break; + case 11: generateBSR(inst, b); done = 1; break; + + case 12: + switch(INSTRUCTION_B(inst)) { + case 0: generateMOVBSG(inst, b); break; + case 1: generateMOVWSG(inst, b); break; + case 2: generateMOVLSG(inst, b); break; + case 3: generateTRAPA(inst, b); done = 1; break; + case 4: generateMOVBLG(inst, b); break; + case 5: generateMOVWLG(inst, b); break; + case 6: generateMOVLLG(inst, b); break; + case 7: generateMOVA(inst, b); break; + case 8: generateTSTI(inst, b); break; + case 9: generateANDI(inst, b); break; + case 10: generateXORI(inst, b); break; + case 11: generateORI(inst, b); break; + case 12: generateTSTM(inst, b); break; + case 13: generateANDM(inst, b); break; + case 14: generateXORM(inst, b); break; + case 15: generateORM(inst, b); break; + } + break; + + case 13: generateMOVLI(inst, b); break; + case 14: generateMOVI(inst, b); break; + default: return -1; + } + + return done; +} + +int sh2rec_rec_block(sh2rec_block_t *b) { + int done = 0; + + while(!done) { + done = sh2rec_rec_inst(b, 0); + } + + /* Flush the icache, so we don't execute stale data */ + icache_flush_range((uint32)b->block, ((u32)b->ptr) - ((u32)b->block)); + + return 0; +} + +/* In sh2exec.s */ +extern void sh2rec_exec(SH2_struct *cxt, u32 cycles); + +static int sh2rec_init(void) { + /* Initialize anything important here */ + sh2rec_htab_init(); + return 0; +} + +static void sh2rec_deinit(void) { + /* Clean stuff up here */ + sh2rec_htab_reset(); +} + +static void sh2rec_reset(void) { + /* Reset to a sane state */ + sh2rec_htab_reset(); +} + +/* This function borrowed from the interpreter core */ +void sh2rec_check_interrupts(SH2_struct *c) { + if(c->NumberOfInterrupts != 0) { + if(c->interrupts[c->NumberOfInterrupts-1].level > c->regs.SR.part.I) { + c->regs.R[15] -= 4; + MappedMemoryWriteLong(c->regs.R[15], c->regs.SR.all); + c->regs.R[15] -= 4; + MappedMemoryWriteLong(c->regs.R[15], c->regs.PC); + c->regs.SR.part.I = c->interrupts[c->NumberOfInterrupts - 1].level; + c->regs.PC = MappedMemoryReadLong(c->regs.VBR + (c->interrupts[c->NumberOfInterrupts-1].vector << 2)); + c->NumberOfInterrupts--; + c->isSleeping = 0; + } + } +} + +sh2rec_block_t *sh2rec_find_block(u32 pc) { + sh2rec_block_t *b = sh2rec_htab_lookup(pc); + + if(!b) { + b = sh2rec_htab_block_create(pc, 4096); + sh2rec_rec_block(b); + } + + return b; +} + +SH2Interface_struct SH2Dynarec = { + SH2CORE_DYNAREC, + "SH2 -> SH4 Dynarec", + + sh2rec_init, /* Init */ + sh2rec_deinit, /* DeInit */ + sh2rec_reset, /* Reset */ + sh2rec_exec, /* Exec */ + + SH2InterpreterGetRegisters, /* GetRegisters */ + SH2InterpreterGetGPR, /* GetGPR */ + SH2InterpreterGetSR, /* GetSR */ + SH2InterpreterGetGBR, /* GetGBR */ + SH2InterpreterGetVBR, /* GetVBR */ + SH2InterpreterGetMACH, /* GetMACH */ + SH2InterpreterGetMACL, /* GetMACL */ + SH2InterpreterGetPR, /* GetPR */ + SH2InterpreterGetPC, /* GetPC */ + + SH2InterpreterSetRegisters, /* SetRegisters */ + SH2InterpreterSetGPR, /* SetGPR */ + SH2InterpreterSetSR, /* SetSR */ + SH2InterpreterSetGBR, /* SetGBR */ + SH2InterpreterSetVBR, /* SetVBR */ + SH2InterpreterSetMACH, /* SetMACH */ + SH2InterpreterSetMACL, /* SetMACL */ + SH2InterpreterSetPR, /* SetPR */ + SH2InterpreterSetPC, /* SetPC */ + + SH2InterpreterSendInterrupt, /* SendInterrupt */ + SH2InterpreterGetInterrupts, /* GetInterrupts */ + SH2InterpreterSetInterrupts, /* SetInterrupts */ + + NULL /* WriteNotify */ +}; diff --git a/yabause/src/dreamcast/sh2rec/sh2rec.h b/yabause/src/dreamcast/sh2rec/sh2rec.h new file mode 100644 index 0000000000..cd5a87ac2f --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec.h @@ -0,0 +1,50 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SH2REC_H +#define SH2REC_H + +#define SH2CORE_DYNAREC 10 + +#define INSTRUCTION_A(x) ((x & 0xF000) >> 12) +#define INSTRUCTION_B(x) ((x & 0x0F00) >> 8) +#define INSTRUCTION_C(x) ((x & 0x00F0) >> 4) +#define INSTRUCTION_D(x) (x & 0x000F) +#define INSTRUCTION_CD(x) (x & 0x00FF) +#define INSTRUCTION_BCD(x) (x & 0x0FFF) + +typedef struct sh2rec_block { + u16 *block; + u32 start_pc; + int cycles; + int length; + + u16 *ptr; + u32 pc; +} sh2rec_block_t; + +/* Recompile a single instruction */ +int sh2rec_rec_inst(sh2rec_block_t *b, int isdelay); + +/* Recompile a block at the PC specified in the block */ +int sh2rec_rec_block(sh2rec_block_t *b); + +extern SH2Interface_struct SH2Dynarec; + +#endif /* !SH2REC_H */ diff --git a/yabause/src/dreamcast/sh2rec/sh2rec_htab.c b/yabause/src/dreamcast/sh2rec/sh2rec_htab.c new file mode 100644 index 0000000000..1a0e58b091 --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec_htab.c @@ -0,0 +1,157 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include "core.h" +#include "sh2core.h" +#include "sh2rec.h" +#include "sh2rec_htab.h" +#include "sh2rec_mem.h" + +typedef struct htab_entry { + sh2rec_block_t block; + + struct htab_entry *next; +} htab_entry_t; + +/* The actual hash table. It can't be resized dynamically, and is essentially + just an array of singly-linked lists. */ +static htab_entry_t *table[SH2REC_HTAB_ENTRIES]; + +/* Internal functions */ +static void htab_free_chain(htab_entry_t *ent) { + htab_entry_t *i, *tmp; + + i = ent; + while(i) { + tmp = i->next; + free(i->block.block); + free(i); + i = tmp; + } +} + +/* Hash an address into something slightly nicer to work with. The large + constant in here is about 2^32 / phi (where phi is the golden ratio). Why use + the golden ratio? Because its always fun to use in code. */ +static inline int hash_addr(u32 addr) { + return ((addr ^ 2654435761U) >> 2) & (SH2REC_HTAB_ENTRIES - 1); +} + +/* Public functions */ +void sh2rec_htab_init(void) { + memset(table, 0, sizeof(htab_entry_t *) * SH2REC_HTAB_ENTRIES); +} + +void sh2rec_htab_reset(void) { + int i; + + for(i = 0; i < SH2REC_HTAB_ENTRIES; ++i) { + if(table[i]) { + htab_free_chain(table[i]); + } + } + + memset(table, 0, sizeof(htab_entry_t *) * SH2REC_HTAB_ENTRIES); +} + +sh2rec_block_t *sh2rec_htab_lookup(u32 addr) { + htab_entry_t *i = table[hash_addr(addr)]; + + /* Look through the chain for the entry we're after */ + while(i) { + if(i->block.start_pc == addr) { + return &i->block; + } + + i = i->next; + } + + /* Didn't find it, punt. */ + return NULL; +} + +/* Create a new block assuming an old one does not exist. */ +sh2rec_block_t *sh2rec_htab_block_create(u32 addr, int length) { + uint8_t *ptr; + htab_entry_t *ent; + int index = hash_addr(addr); + + ptr = (uint8_t *)sh2rec_mem_alloc(length + sizeof(htab_entry_t)); + +#ifdef DEBUG + if(!ptr) { + return NULL; + } +#endif + + /* Allocate space for the block */ + ent = (htab_entry_t *)ptr; + ent->block.block = (u16 *)(ptr + sizeof(htab_entry_t)); + + /* Fill in the struct */ + ent->block.start_pc = addr; + ent->block.cycles = 0; + ent->block.pc = addr; + ent->block.length = length; + ent->block.ptr = ent->block.block; + + /* Put the item in the list (puts it at the head of the index in the table + where it would go) */ + ent->next = table[index]; + table[index] = ent; + + return &ent->block; +} + +void sh2rec_htab_block_remove(u32 addr) { + int index = hash_addr(addr); + htab_entry_t *i, *tmp, *last; + + i = table[index]; + last = NULL; + + /* Look through everything for the entry we're supposed to remove */ + while(i) { + tmp = i->next; + + /* Is this the entry we're looking for? */ + if(i->block.start_pc == addr) { + /* Unhook the entry from the list */ + if(last) { + last->next = tmp; + } + else { + table[index] = tmp; + } + + /* Free any memory used by the block */ + sh2rec_mem_free(i); + + return; + } + + last = i; + i = tmp; + } +} diff --git a/yabause/src/dreamcast/sh2rec/sh2rec_htab.h b/yabause/src/dreamcast/sh2rec/sh2rec_htab.h new file mode 100644 index 0000000000..11b096f1b1 --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec_htab.h @@ -0,0 +1,35 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SH2REC_HTAB_H +#define SH2REC_HTAB_H + +/* This MUST be set to a power of two. It is done this way to avoid using a mod + operation to get the entry where the object will go, since division (and thus + modulus) is quite expensive on SuperH. */ +#define SH2REC_HTAB_ENTRIES 4096 + +void sh2rec_htab_init(void); +void sh2rec_htab_reset(void); + +sh2rec_block_t *sh2rec_htab_lookup(u32 addr); +sh2rec_block_t *sh2rec_htab_block_create(u32 addr, int length); +void sh2rec_htab_block_remove(u32 addr); + +#endif /* !SH2REC_HTAB_H */ diff --git a/yabause/src/dreamcast/sh2rec/sh2rec_mem.c b/yabause/src/dreamcast/sh2rec/sh2rec_mem.c new file mode 100644 index 0000000000..8d8c09d47f --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec_mem.c @@ -0,0 +1,222 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "sh2rec_mem.h" + +typedef struct block_s { + uint8_t *ptr; + int size; + struct block_s *prev; + struct block_s *next; +} sh2rec_mem_block; + +typedef struct usedblock_s { + sh2rec_mem_block base; + sh2rec_mem_block *freespace; +} sh2rec_mem_usedblock; + +typedef struct allocblock_s { + struct allocblock_s *next; +} sh2rec_mem_allocblock; + +static sh2rec_mem_block *freeblocks = NULL; +static sh2rec_mem_usedblock *usedblocks = NULL; +static sh2rec_mem_usedblock *usedblocks_tail = NULL; +static sh2rec_mem_allocblock *allocblocks = NULL; + +static int cur_allocation = 0; + +#define BSSIZE (sizeof(sh2rec_mem_allocblock) + sizeof(sh2rec_mem_block) + \ + sizeof(sh2rec_mem_usedblock)) + +int sh2rec_mem_init(void) { + sh2rec_mem_block *initblock; + sh2rec_mem_allocblock *allocblock; + uint8_t *block; + + /* Allocate our initial space for storing rec'd instructions in */ + block = (uint8_t *)malloc(SH2REC_MEM_INITIAL); + +#ifdef DEBUG + if(!block) { + return -1; + } +#endif + + /* Carve our structures out of the beginning of the block */ + allocblock = (sh2rec_mem_allocblock *)block; + initblock = (sh2rec_mem_block *)(block + sizeof(sh2rec_mem_allocblock)); + cur_allocation = SH2REC_MEM_INITIAL; + + /* Fill in the rest of the structs */ + initblock->size = SH2REC_MEM_INITIAL - sizeof(sh2rec_mem_allocblock) - + sizeof(sh2rec_mem_block); + initblock->prev = NULL; + initblock->next = NULL; + + allocblock->next = NULL; + allocblocks = allocblock; + + /* The whole block is free, so put it in the free list */ + freeblocks = initblock; + + return 0; +} + +void sh2rec_mem_shutdown(void) { + sh2rec_mem_allocblock *i, *tmp; + + /* Loop through and free any blocks we allocated */ + i = allocblocks; + while(i) { + tmp = i->next; + free(i); + i = tmp; + } + + /* Clean up the stale pointers */ + allocblocks = NULL; + freeblocks = NULL; + usedblocks = NULL; +} + +void *sh2rec_mem_alloc(int sz) { + sh2rec_mem_block *i; + sh2rec_mem_usedblock *rv; + sh2rec_mem_allocblock *b; + int szlook = sz + SH2REC_MEM_FUDGE + sizeof(sh2rec_mem_usedblock); + uint8_t *block; + + /* Look for a free block of enough size */ + i = freeblocks; + while(i) { + if(i->size >= szlook) { + /* We've found a candidate, so, start working with it */ + rv = (sh2rec_mem_usedblock *)i->ptr; + rv->freespace = i; + rv->base.ptr = i->ptr + sizeof(sh2rec_mem_usedblock); + rv->base.size = sz; + rv->base.prev = (sh2rec_mem_block *)usedblocks_tail; + rv->base.next = NULL; + + /* Update the tail */ + if(usedblocks_tail) { + usedblocks_tail->base.next = (sh2rec_mem_block *)rv; + } + + usedblocks_tail = rv; + + /* The freeblock is now smaller, so reflect that */ + i->size -= sz + sizeof(sh2rec_mem_usedblock); + + return rv; + } + + i = i->next; + } + + /* We didn't find one, so allocate a new block */ + block = malloc(SH2REC_MEM_ALLOCSZ); + +#ifdef DEBUG + if(!block) { + return NULL; + } +#endif + + /* Fill in the allocblock */ + b = (sh2rec_mem_allocblock *)block; + b->next = allocblocks; + allocblocks = b; + + /* Now, create a freeblock, and work from that */ + i = (sh2rec_mem_block *)(block + sizeof(sh2rec_mem_allocblock)); + i->ptr = block + BSSIZE + sz; + i->prev = NULL; + i->next = freeblocks; + i->size = SH2REC_MEM_ALLOCSZ - BSSIZE - sz; + freeblocks = i; + + /* Create the usedblock */ + rv = (sh2rec_mem_usedblock *)(i->ptr - sz); + rv->freespace = i; + rv->base.ptr = i->ptr; + rv->base.size = sz; + rv->base.prev = (sh2rec_mem_block *)usedblocks_tail; + rv->base.next = NULL; + + /* Update the tail */ + if(usedblocks_tail) { + usedblocks_tail->base.next = (sh2rec_mem_block *)rv; + } + + usedblocks_tail = rv; + + /* Keep track of our allocation */ + cur_allocation += SH2REC_MEM_ALLOCSZ; + + return rv; +} + +int sh2rec_mem_expand(void *block, int amt) { + sh2rec_mem_usedblock *b = (sh2rec_mem_usedblock *)block; + + /* If the freeblock has space, allow it */ + if(b->freespace->size > amt) { + b->freespace->size -= amt; + b->base.size += amt; + b->freespace->ptr += amt; + return 1; + } + + return 0; +} + +void sh2rec_mem_free(void *block) { + sh2rec_mem_usedblock *b = (sh2rec_mem_usedblock *)block; + + /* Remove the usedblock from the chain */ + if(b->base.next) { + b->base.next->prev = b->base.prev; + } + + if(b->base.prev) { + b->base.prev->next = b->base.next; + } + + if(b == usedblocks) { + usedblocks = (sh2rec_mem_usedblock *)b->base.next; + } + + if(b == usedblocks_tail) { + usedblocks_tail = (sh2rec_mem_usedblock *)b->base.prev; + } + + /* Treat the usedblock like its a freeblock (it is an extension of the + freeblock), and just link it into the free blocks list */ + b->freespace = NULL; + b->base.next = freeblocks; + b->base.prev = NULL; + b->base.size += sizeof(sh2rec_mem_usedblock) - sizeof(sh2rec_mem_block); + freeblocks = (sh2rec_mem_block *)b; +} diff --git a/yabause/src/dreamcast/sh2rec/sh2rec_mem.h b/yabause/src/dreamcast/sh2rec/sh2rec_mem.h new file mode 100644 index 0000000000..7fb679eb12 --- /dev/null +++ b/yabause/src/dreamcast/sh2rec/sh2rec_mem.h @@ -0,0 +1,43 @@ +/* Copyright 2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SH2REC_MEM_H +#define SH2REC_MEM_H + +/* Initial allocation of memory: 1MB */ +#define SH2REC_MEM_INITIAL (1024 * 1024) + +/* Size of future allocations: 256KB */ +#define SH2REC_MEM_ALLOCSZ (256 * 1024) + +/* Maximum allocation of memory: 2MB */ +#define SH2REC_MEM_MAX (2 * 1024 * 1024) + +/* Fudge factor... make sure at least this much is in any block above the + inital request */ +#define SH2REC_MEM_FUDGE 48 + +int sh2rec_mem_init(void); +void sh2rec_mem_shutdown(void); + +void *sh2rec_mem_alloc(int sz); +int sh2rec_mem_expand(void *block, int amt); +void sh2rec_mem_free(void *block); + +#endif /* !SH2REC_MEM_H */ diff --git a/yabause/src/dreamcast/viddc.c b/yabause/src/dreamcast/viddc.c new file mode 100644 index 0000000000..52ea022f35 --- /dev/null +++ b/yabause/src/dreamcast/viddc.c @@ -0,0 +1,2836 @@ +/* Copyright 2003-2006 Guillaume Duhamel + Copyright 2004-2009 Lawrence Sebald + Copyright 2004-2006 Theo Berkau + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "viddc.h" +#include "../debug.h" +#include "../vdp2.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SAT2YAB1(temp) ((temp & 0x8000) | (temp & 0x1F) << 10 | \ + (temp & 0x3E0) | (temp & 0x7C00) >> 10) +#define SAT2YAB32(alpha, temp) (alpha << 24 | (temp & 0x1F) << 3 | \ + (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9) + +#define SAT2YAB2(dot1, dot2) ((dot1 & 0xF8) << 7 | (dot2 & 0xF800) >> 6 | \ + (dot2 & 0x00F8) >> 3 | 0x8000) +#define SAT2YAB2_32(alpha, dot1, dot2) (alpha << 24 | ((dot1 & 0xFF) << 16) | \ + (dot2 & 0xFF00) | (dot2 & 0xFF)) + +#define COLOR_ADDt32(b) (b > 0xFF ? 0xFF : (b < 0 ? 0 : b)) +#define COLOR_ADDb32(b1,b2) COLOR_ADDt32((signed) (b1) + (b2)) + +#define COLOR_ADD32(l,r,g,b) COLOR_ADDb32((l & 0xFF), r) | \ + (COLOR_ADDb32((l >> 8) & 0xFF, g) << 8) | \ + (COLOR_ADDb32((l >> 16) & 0xFF, b) << 16) | \ + (l & 0xFF000000) + +#define COLOR_ADDt(b) (b > 0xF8 ? 0xF8 : (b < 0x08 ? 0 : b)) +#define COLOR_ADDb(b1,b2) COLOR_ADDt((signed) (b1) + (b2)) +#define COLOR_ADD(l,r,g,b) ((COLOR_ADDb((l >> 7) & 0xF8, \ + r) & 0xF8) << 7) | \ + ((COLOR_ADDb((l >> 2) & 0xF8, \ + g) & 0xF8) << 2) | \ + ((COLOR_ADDb((l << 3) & 0xF8, \ + b) & 0xF8) >> 3) | \ + (l & 0x8000) + + +static pvr_init_params_t pvr_params = { + /* Enable Opaque, Translucent, and Punch-Thru polygons with binsize 16 */ + { PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_16, PVR_BINSIZE_0, + PVR_BINSIZE_16 }, + /* 512KB Vertex Buffer */ + 512 * 1024, + /* DMA Enabled */ + 1, + /* FSAA Disabled */ + 0 +}; + +struct sprite_info { + uint32 pvr_base; + uint32 vdp1_base; + float uf, vf; + int w, h; +}; + +typedef struct { + int cellw, cellh; + int flipfunction; + int priority; + + int mapwh; + int planew, planeh; + int pagewh; + int patternwh; + int patterndatasize; + int specialfunction; + u32 addr, charaddr, paladdr; + int colornumber; + int isbitmap; + u16 supplementdata; + int auxmode; + int enable; + int x, y; + int alpha; + int coloroffset; + int transparencyenable; + int specialprimode; + + s32 cor; + s32 cog; + s32 cob; + + float coordincx, coordincy; + void (* PlaneAddr)(void *, int); + u16 (* PostPixelFetchCalc)(void *, u16); + int patternpixelwh; + int draww; + int drawh; +} vdp2draw_struct; + +static struct sprite_info cur_spr; + +static struct sprite_info cache[1024]; +int cached_spr = 0; + +/* Polygon Headers */ +static pvr_sprite_hdr_t op_poly_hdr; +static pvr_sprite_hdr_t tr_poly_hdr; +static pvr_sprite_hdr_t tr_sprite_hdr; +static pvr_sprite_hdr_t pt_sprite_hdr; + +/* DMA Vertex Buffers 256KB Each */ +static uint8 vbuf_opaque[1024 * 256] __attribute__((aligned(32))); +static uint8 vbuf_translucent[1024 * 256] __attribute__((aligned(32))); +static uint8 vbuf_punchthru[1024 * 256] __attribute__((aligned(32))); + +/* VDP2 Framebuffer */ +static uint16 *vdp2_fb; +static int vdp2_fbnum = 0; +static uint16 vdp2_fbs[2][512 * 256] __attribute__((aligned(32))); +static uint8 vdp2_prio[352][240]; +static semaphore_t *dmadone; + +static pvr_ptr_t vdp2_tex; +static uint32 cur_vdp2; + +/* Priority levels, sprites drawn last get drawn on top */ +static float priority_levels[8]; + +/* Texture space for VDP1 sprites */ +static pvr_ptr_t tex_space; +static uint32 cur_addr; + +/* Misc parameters */ +static int vdp1cor = 0; +static int vdp1cog = 0; +static int vdp1cob = 0; + +static int nbg0priority = 0; +static int nbg1priority = 0; +static int nbg2priority = 0; +static int nbg3priority = 0; +static int rbg0priority = 0; + +static int vdp2width = 320; +static int vdp2height = 224; + +/* Frame counter */ +static time_t lastup; +static int framecount; + +static int power_of_two(int num) { + int ret = 8; + + while(ret < num) + ret <<= 1; + + return ret; +} + +static inline void vdp2putpixel(s32 x, s32 y, u16 color, int priority) { + vdp2_fb[(y * 512) + x] = color; + vdp2_prio[x][y] = (uint8) priority; +} + +static u32 Vdp2ColorRamGetColor32(u32 colorindex, int alpha) { + switch(Vdp2Internal.ColorMode) { + case 0: + case 1: + { + u32 tmp; + colorindex <<= 1; + tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); + return SAT2YAB32(alpha, tmp); + } + case 2: + { + u32 tmp1, tmp2; + colorindex <<= 2; + colorindex &= 0xFFF; + tmp1 = T2ReadWord(Vdp2ColorRam, colorindex); + tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); + return SAT2YAB2_32(alpha, tmp1, tmp2); + } + default: + break; + } + + return 0; +} + +static uint16 Vdp2ColorRamGetColor(u32 colorindex) { + u16 tmp; + + switch(Vdp2Internal.ColorMode) { + case 0: + case 1: + { + colorindex <<= 1; + tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); + return SAT2YAB1(tmp) | 0x8000; + } + case 2: + { + u16 tmp2; + colorindex <<= 2; + colorindex &= 0xFFF; + tmp = T2ReadWord(Vdp2ColorRam, colorindex); + tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); + return SAT2YAB2(tmp, tmp2) | 0x8000; + } + default: + break; + } + + return 0; +} + +static int Vdp1ReadTexture(vdp1cmd_struct *cmd, pvr_sprite_hdr_t *hdr) { + u32 charAddr = cmd->CMDSRCA << 3; + uint16 dot, dot2; + int queuepos = 0; + uint32 *store_queue; + uint32 cur_base; + u8 SPD = ((cmd->CMDPMOD & 0x40) != 0); + int k; + + int wi = power_of_two(cur_spr.w); + int he = power_of_two(cur_spr.h); + + for(k = 0; k < cached_spr; ++k) { + if(cache[k].vdp1_base == charAddr) { + if(cache[k].w == cur_spr.w && cache[k].h == cur_spr.h) { + cur_base = cache[k].pvr_base; + goto fillHeader; + } + } + } + + cur_base = cur_addr; + + /* Set up both Store Queues for transfer to VRAM */ + QACR0 = 0x00000004; + QACR1 = 0x00000004; + + switch((cmd->CMDPMOD >> 3) & 0x07) { + case 0: + { + // 4 bpp Bank mode + u16 temp; + u32 colorBank = cmd->CMDCOLR; + u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + int i, j; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); + + if(((dot & 0xF) == 0) && !SPD) dot2 = 0; + else { + temp = Vdp2ColorRamGetColor(((dot & 0x0F) | colorBank) + + colorOffset); + dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + if(((dot >> 4) == 0) && !SPD) dot = 0; + else { + temp = Vdp2ColorRamGetColor(((dot >> 4) | colorBank) + + colorOffset); + dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + ++charAddr; + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + case 1: + { + // 4 bpp LUT mode + u16 temp; + u32 colorLut = cmd->CMDCOLR * 8; + int i, j; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); + + if(((dot & 0xF) == 0) && !SPD) dot2 = 0; + else { + temp = T1ReadWord(Vdp1Ram, ((dot & 0xF) * 2 + + colorLut) & 0x7FFFF); + + if(temp & 0x8000) + dot2 = COLOR_ADD(SAT2YAB1(temp), vdp1cor, vdp1cog, + vdp1cob); + else + dot2 = COLOR_ADD(Vdp2ColorRamGetColor(temp), + vdp1cor, vdp1cog, vdp1cob); + } + + if(((dot >> 4) == 0) && !SPD) dot = 0; + else { + temp = T1ReadWord(Vdp1Ram, ((dot >> 4) * 2 + colorLut) & + 0x7FFFF); + if (temp & 0x8000) + dot = COLOR_ADD(SAT2YAB1(temp), vdp1cor, vdp1cog, + vdp1cob); + else + dot = COLOR_ADD(Vdp2ColorRamGetColor(temp), vdp1cor, + vdp1cog, vdp1cob); + } + + ++charAddr; + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + case 2: + { + // 8 bpp (64 color) Bank mode + int i, j; + u32 colorBank = cmd->CMDCOLR; + u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + u16 temp; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x3F; + dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF) & 0x3F; + charAddr = charAddr + 2; + + if(dot || SPD) { + temp = Vdp2ColorRamGetColor((dot | colorBank) + + colorOffset); + dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + if(dot2 || SPD) { + temp = Vdp2ColorRamGetColor((dot2 | colorBank) + + colorOffset); + dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + case 3: + { + // 8 bpp (128 color) Bank mode + int i, j; + u32 colorBank = cmd->CMDCOLR; + u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + u16 temp; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x7F; + dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF) & 0x7F; + charAddr = charAddr + 2; + + if(dot || SPD) { + temp = Vdp2ColorRamGetColor((dot | colorBank) + + colorOffset); + dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + if(dot2 || SPD) { + temp = Vdp2ColorRamGetColor((dot2 | colorBank) + + colorOffset); + dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + case 4: + { + // 8 bpp (256 color) Bank mode + int i, j; + u32 colorBank = cmd->CMDCOLR; + u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; + u16 temp; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); + dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF); + charAddr = charAddr + 2; + + if(dot || SPD) { + temp = Vdp2ColorRamGetColor((dot | colorBank) + + colorOffset); + dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + if(dot2 || SPD) { + temp = Vdp2ColorRamGetColor((dot2 | colorBank) + + colorOffset); + dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); + } + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + case 5: + { + // 16 bpp Bank mode + int i, j; + + for(i = 0; i < cur_spr.h; ++i) { + store_queue = (uint32 *) (0xE0000000 | + (cur_addr & 0x03FFFFE0)); + + for(j = 0; j < cur_spr.w; j += 2) { + dot = T1ReadWord(Vdp1Ram, charAddr & 0x7FFFF); + dot2 = T1ReadWord(Vdp1Ram, (charAddr + 2) & 0x7FFFF); + charAddr = charAddr + 4; + + if(dot || SPD) + dot = COLOR_ADD(SAT2YAB1(dot), vdp1cor, vdp1cog, + vdp1cob); + + if(dot2 || SPD) + dot2 = COLOR_ADD(SAT2YAB1(dot2), vdp1cor, vdp1cog, + vdp1cob); + + store_queue[queuepos++] = dot | (dot2 << 16); + + if(queuepos == 8) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + store_queue += 8; + } + } + + if(queuepos) { + asm("pref @%0" : : "r"(store_queue)); + queuepos = 0; + } + + cur_addr += wi * 2; + } + break; + } + + default: + VDP1LOG("Unimplemented sprite color mode: %X\n", + (cmd->CMDPMOD >> 3) & 0x7); + return 0; + } + + if(cached_spr < 1023) { + cache[cached_spr].vdp1_base = cmd->CMDSRCA << 3; + cache[cached_spr].pvr_base = cur_base; + cache[cached_spr].w = cur_spr.w; + cache[cached_spr].h = cur_spr.h; + + cached_spr++; + } + +fillHeader: + + cur_spr.uf = (float) cur_spr.w / wi; + cur_spr.vf = (float) cur_spr.h / he; + + hdr->mode2 &= (~(PVR_TA_PM2_USIZE_MASK | PVR_TA_PM2_VSIZE_MASK)); + + switch (wi) { + case 8: break; + case 16: hdr->mode2 |= (1 << PVR_TA_PM2_USIZE_SHIFT); break; + case 32: hdr->mode2 |= (2 << PVR_TA_PM2_USIZE_SHIFT); break; + case 64: hdr->mode2 |= (3 << PVR_TA_PM2_USIZE_SHIFT); break; + case 128: hdr->mode2 |= (4 << PVR_TA_PM2_USIZE_SHIFT); break; + case 256: hdr->mode2 |= (5 << PVR_TA_PM2_USIZE_SHIFT); break; + case 512: hdr->mode2 |= (6 << PVR_TA_PM2_USIZE_SHIFT); break; + case 1024: hdr->mode2 |= (7 << PVR_TA_PM2_USIZE_SHIFT); break; + default: assert_msg(0, "Invalid texture U size"); break; + } + + switch (he) { + case 8: break; + case 16: hdr->mode2 |= (1 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 32: hdr->mode2 |= (2 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 64: hdr->mode2 |= (3 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 128: hdr->mode2 |= (4 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 256: hdr->mode2 |= (5 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 512: hdr->mode2 |= (6 << PVR_TA_PM2_VSIZE_SHIFT); break; + case 1024: hdr->mode2 |= (7 << PVR_TA_PM2_VSIZE_SHIFT); break; + default: assert_msg(0, "Invalid texture V size"); break; + } + + hdr->mode3 = ((cur_base & 0x00FFFFF8) >> 3) | (PVR_TXRFMT_NONTWIDDLED); + + /* Make sure everything is aligned nicely... */ + cur_addr = (cur_addr & 0x03FFFFE0) + 0x20; + + return 1; +} + +static u8 Vdp1ReadPriority(vdp1cmd_struct *cmd) { + u8 SPCLMD = Vdp2Regs->SPCTL; + u8 sprite_register; + u8 *sprprilist = (u8 *)&Vdp2Regs->PRISA; + + if ((SPCLMD & 0x20) && (cmd->CMDCOLR & 0x8000)) { + // RGB data, use register 0 + return Vdp2Regs->PRISA & 0x07; + } + else { + u8 sprite_type = SPCLMD & 0x0F; + switch(sprite_type) { + case 0: + sprite_register = ((cmd->CMDCOLR & 0x8000) | + (~cmd->CMDCOLR & 0x4000)) >> 14; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 1: + sprite_register = ((cmd->CMDCOLR & 0xC000) | + (~cmd->CMDCOLR & 0x2000)) >> 13; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 3: + sprite_register = ((cmd->CMDCOLR & 0x4000) | + (~cmd->CMDCOLR & 0x2000)) >> 13; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 4: + sprite_register = ((cmd->CMDCOLR & 0x4000) | + (~cmd->CMDCOLR & 0x2000)) >> 13; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 5: + sprite_register = ((cmd->CMDCOLR & 0x6000) | + (~cmd->CMDCOLR & 0x1000)) >> 12; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 6: + sprite_register = ((cmd->CMDCOLR & 0x6000) | + (~cmd->CMDCOLR & 0x1000)) >> 12; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + case 7: + sprite_register = ((cmd->CMDCOLR & 0x6000) | + (~cmd->CMDCOLR & 0x1000)) >> 12; + return sprprilist[sprite_register ^ 1] & 0x07; + break; + default: + VDP1LOG("sprite type %d not implemented\n", sprite_type); + return 0x07; + break; + } + } +} + +/* This has all been imported from the vidsoft.c file. It will be updated, + hopefully (roughly) synchronized with updates to it */ + +////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int xstart, ystart; + int xend, yend; + int pixeloffset; + int lineincrement; +} clipping_struct; + +static void inline HandleClipping(vdp2draw_struct *info, clipping_struct *clip) +{ + clip->pixeloffset=0; + clip->lineincrement=0; + + // Handle clipping(both window and screen clipping) + if (info->x < 0) + { + clip->xstart = 0; + clip->xend = (info->x+info->cellw); + clip->pixeloffset = 0 - info->x; + clip->lineincrement = 0 - info->x; + } + else + { + clip->xstart = info->x; + + if ((info->x+info->cellw) > vdp2width) + { + clip->xend = vdp2width; + clip->lineincrement = (info->x+info->cellw) - vdp2width; + } + else + clip->xend = (info->x+info->cellw); + } + + if (info->y < 0) + { + clip->ystart = 0; + clip->yend = (info->y+info->cellh); + clip->pixeloffset = (info->cellw * (0 - info->y)) + clip->pixeloffset; + } + else + { + clip->ystart = info->y; + + if ((info->y+info->cellh) >= vdp2height) + clip->yend = vdp2height; + else + clip->yend = (info->y+info->cellh); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Vdp2DrawScrollBitmap(vdp2draw_struct *info) +{ + int i, i2; + clipping_struct clip; + + HandleClipping(info, &clip); + + switch (info->colornumber) + { + case 0: // 4 BPP(16 colors) + // fix me + printf("vdp2 bitmap 4 bpp draw\n"); + return; + case 1: // 8 BPP(256 colors) + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u16 color = T1ReadByte(Vdp2Ram, info->charaddr); + info->charaddr++; + + if (color == 0 && info->transparencyenable) { + vdp2putpixel(i2, i, 0, info->priority); + } + else { + color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | color)); + vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color) | 0x8000, info->priority); + } + } + + info->charaddr += clip.lineincrement; + } + + return; + case 2: + printf("vdp2 bitmap 16bpp palette draw\n"); + break; + case 3: // 15 BPP + clip.pixeloffset *= 2; + clip.lineincrement *= 2; + + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u16 color = T1ReadWord(Vdp2Ram, info->charaddr); + info->charaddr += 2; + + if ((color & 0x8000) == 0 && info->transparencyenable) + vdp2_fb[(i * vdp2width) + i2] = 0; + else + vdp2_fb[(i * vdp2width) + i2] = info->PostPixelFetchCalc(info, SAT2YAB1(color)) | 0x8000; + + vdp2_prio[i][i2] = info->priority; + } + + info->charaddr += clip.lineincrement; + } + + return; + case 4: // 24 BPP + clip.pixeloffset *= 4; + clip.lineincrement *= 4; + + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u32 color = T1ReadLong(Vdp2Ram, info->charaddr); + info->charaddr += 4; + + if ((color & 0x80000000) == 0 && info->transparencyenable) + vdp2putpixel(i2, i, 0, info->priority); + else { + u16 dot = ((color & 0xF80000) >> 19 | + (color & 0x00F800) >> 6 | + (color & 0x0000F8) << 7 | 0x8000); + vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, dot), info->priority); + } + } + + info->charaddr += clip.lineincrement; + + } + return; + default: break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +#define Vdp2DrawCell4bpp(mask, shift) \ + if ((dot & mask) == 0 && info->transparencyenable) { \ + vdp2putpixel(i2, i, 0, info->priority); \ + } \ + else \ + { \ + color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | ((dot & mask) >> shift))); \ + vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); \ + } + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawCell(vdp2draw_struct *info) +{ + u32 color; + int i, i2; + clipping_struct clip; + u32 newcharaddr; + + HandleClipping(info, &clip); + + if (info->flipfunction & 0x1) + { + // Horizontal flip + } + + if (info->flipfunction & 0x2) + { + // Vertical flip + // clip.pixeloffset = (info.w * info.h) - clip.pixeloffset; + // clip.lineincrement = 0 - clip.lineincrement; + } + + switch(info->colornumber) + { + case 0: // 4 BPP + if ((clip.lineincrement | clip.pixeloffset) == 0) + { + for (i = clip.ystart; i < clip.yend; i++) + { + u32 dot; + u16 color; + + i2 = clip.xstart; + + // Fetch Pixel 1/2/3/4/5/6/7/8 + dot = T1ReadLong(Vdp2Ram, info->charaddr); + info->charaddr+=4; + + // Draw 8 Pixels + Vdp2DrawCell4bpp(0xF0000000, 28) + i2++; + Vdp2DrawCell4bpp(0x0F000000, 24) + i2++; + Vdp2DrawCell4bpp(0x00F00000, 20) + i2++; + Vdp2DrawCell4bpp(0x000F0000, 16) + i2++; + Vdp2DrawCell4bpp(0x0000F000, 12) + i2++; + Vdp2DrawCell4bpp(0x00000F00, 8) + i2++; + Vdp2DrawCell4bpp(0x000000F0, 4) + i2++; + Vdp2DrawCell4bpp(0x0000000F, 0) + i2++; + } + } + else + { + u8 dot; + + newcharaddr = info->charaddr + ((info->cellw * info->cellh) >> 1); + + info->charaddr <<= 1; + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + dot = T1ReadByte(Vdp2Ram, info->charaddr >> 1); + info->charaddr++; + + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u32 color; + + // Draw two pixels + if(info->charaddr & 0x1) + { + Vdp2DrawCell4bpp(0xF0, 4) + info->charaddr++; + } + else + { + Vdp2DrawCell4bpp(0x0F, 0) + dot = T1ReadByte(Vdp2Ram, info->charaddr >> 1); + info->charaddr++; + } + } + info->charaddr += clip.lineincrement; + } + + info->charaddr = newcharaddr; + } + break; + case 1: // 8 BPP + newcharaddr = info->charaddr + (info->cellw * info->cellh); + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u16 color = T1ReadByte(Vdp2Ram, info->charaddr); + info->charaddr++; + + if (color == 0 && info->transparencyenable) { + vdp2putpixel(i2, i, 0, info->priority); + } + else { + color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | color)); + vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); + } + } + + info->charaddr += clip.lineincrement; + } + + info->charaddr = newcharaddr; + + break; + case 2: // 16 BPP(palette) + printf("vdp2 cell draw 16bpp palette\n"); + break; + + case 3: // 16 BPP(RGB) + printf("vdp2 cell draw 16bpp\n"); + break; + case 4: // 32 BPP + newcharaddr = info->charaddr + (info->cellw * info->cellh); + info->charaddr += clip.pixeloffset; + + for (i = clip.ystart; i < clip.yend; i++) + { + for (i2 = clip.xstart; i2 < clip.xend; i2++) + { + u16 dot1, dot2; + dot1 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); + info->charaddr += 2; + dot2 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); + info->charaddr += 2; + + if (!(dot1 & 0x8000) && info->transparencyenable) + continue; + + color = SAT2YAB2(dot1, dot2); + vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); + } + + info->charaddr += clip.lineincrement; + } + + info->charaddr = newcharaddr; + + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawPattern(vdp2draw_struct *info) +{ + // if (info->specialprimode == 1) + // tile.priority = (info->priority & 0xFFFFFFFE) | info->specialfunction; + // else + // tile.priority = info->priority; + + switch(info->patternwh) + { + case 1: + Vdp2DrawCell(info); + info->x += 8; + info->y += 8; + break; + case 2: + Vdp2DrawCell(info); + info->x += 8; + Vdp2DrawCell(info); + info->x -= 8; + info->y += 8; + Vdp2DrawCell(info); + info->x += 8; + Vdp2DrawCell(info); + info->x += 8; + info->y += 8; + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2PatternAddr(vdp2draw_struct *info) +{ + switch(info->patterndatasize) + { + case 1: + { + u16 tmp = T1ReadWord(Vdp2Ram, info->addr); + + info->addr += 2; + info->specialfunction = (info->supplementdata >> 9) & 0x1; + + switch(info->colornumber) + { + case 0: // in 16 colors + info->paladdr = ((tmp & 0xF000) >> 8) | ((info->supplementdata & 0xE0) << 3); + break; + default: // not in 16 colors + info->paladdr = (tmp & 0x7000) >> 4; + break; + } + + switch(info->auxmode) + { + case 0: + info->flipfunction = (tmp & 0xC00) >> 10; + + switch(info->patternwh) + { + case 1: + info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10); + break; + case 2: + info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10); + break; + } + break; + case 1: + info->flipfunction = 0; + + switch(info->patternwh) + { + case 1: + info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10); + break; + case 2: + info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10); + break; + } + break; + } + + break; + } + case 2: { + u16 tmp1 = T1ReadWord(Vdp2Ram, info->addr); + u16 tmp2 = T1ReadWord(Vdp2Ram, info->addr+2); + info->addr += 4; + info->charaddr = tmp2 & 0x7FFF; + info->flipfunction = (tmp1 & 0xC000) >> 14; + info->paladdr = (tmp1 & 0x7F) << 4; + info->specialfunction = (tmp1 & 0x2000) >> 13; + break; + } + } + + if (!(Vdp2Regs->VRSIZE & 0x8000)) + info->charaddr &= 0x3FFF; + + info->charaddr *= 0x20; // selon Runik +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawPage(vdp2draw_struct *info) +{ + int X, Y; + int i, j; + + X = info->x; + for(i = 0;i < info->pagewh;i++) + { + Y = info->y; + info->x = X; + for(j = 0;j < info->pagewh;j++) + { + info->y = Y; + if ((info->x >= -info->patternpixelwh) && + (info->y >= -info->patternpixelwh) && + (info->x <= info->draww) && + (info->y <= info->drawh)) + { + Vdp2PatternAddr(info); + Vdp2DrawPattern(info); + } + else + { + info->addr += info->patterndatasize * 2; + info->x += info->patternpixelwh; + info->y += info->patternpixelwh; + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawPlane(vdp2draw_struct *info) +{ + int X, Y; + int i, j; + + X = info->x; + for(i = 0;i < info->planeh;i++) + { + Y = info->y; + info->x = X; + for(j = 0;j < info->planew;j++) + { + info->y = Y; + Vdp2DrawPage(info); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2DrawMap(vdp2draw_struct *info) +{ + int i, j; + int X, Y; + u32 lastplane; + + X = info->x; + lastplane=0xFFFFFFFF; + + info->patternpixelwh = 8 * info->patternwh; + info->draww = (int)((float)vdp2width / info->coordincx); + info->drawh = (int)((float)vdp2height / info->coordincy); + + for(i = 0;i < info->mapwh;i++) + { + Y = info->y; + info->x = X; + for(j = 0;j < info->mapwh;j++) + { + info->y = Y; + info->PlaneAddr(info, info->mapwh * i + j); + if (info->addr != lastplane) + { + Vdp2DrawPlane(info); + lastplane = info->addr; + } + } + } +} + +static int VIDDCInit(void) { + pvr_sprite_cxt_t op_poly_cxt, tr_poly_cxt; + pvr_sprite_cxt_t pt_sprite_cxt, tr_sprite_cxt; + + vid_set_mode(DM_320x240, PM_RGB565); + + if(pvr_init(&pvr_params)) { + fprintf(stderr, "VIDDCInit() - error initializing PVR\n"); + return -1; + } + + dmadone = sem_create(1); + + pvr_set_vertbuf(PVR_LIST_OP_POLY, vbuf_opaque, 1024 * 256); + pvr_set_vertbuf(PVR_LIST_TR_POLY, vbuf_translucent, 1024 * 256); + pvr_set_vertbuf(PVR_LIST_PT_POLY, vbuf_punchthru, 1024 * 256); + + tex_space = pvr_mem_malloc(1024 * 1024 * 2); + vdp2_tex = pvr_mem_malloc(512 * 256 * 4 * 2); + cur_addr = (uint32)tex_space; + + printf("PVR Memory Available: %lu\n", pvr_mem_available()); + + sq_set(tex_space, 0xFF, 1024 * 1024 * 2); + + pvr_sprite_cxt_col(&op_poly_cxt, PVR_LIST_OP_POLY); + pvr_sprite_cxt_col(&tr_poly_cxt, PVR_LIST_TR_POLY); + + op_poly_cxt.gen.culling = PVR_CULLING_NONE; + tr_poly_cxt.gen.culling = PVR_CULLING_NONE; + + pvr_sprite_compile(&op_poly_hdr, &op_poly_cxt); + pvr_sprite_compile(&tr_poly_hdr, &tr_poly_cxt); + + pvr_sprite_cxt_txr(&tr_sprite_cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_ARGB1555 | + PVR_TXRFMT_NONTWIDDLED, 1024, 1024, tex_space, + PVR_FILTER_NONE); + pvr_sprite_cxt_txr(&pt_sprite_cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_ARGB1555 | + PVR_TXRFMT_NONTWIDDLED, 1024, 1024, tex_space, + PVR_FILTER_NONE); + + pt_sprite_cxt.gen.culling = PVR_CULLING_NONE; + tr_sprite_cxt.gen.culling = PVR_CULLING_NONE; + + pvr_sprite_compile(&tr_sprite_hdr, &tr_sprite_cxt); + pvr_sprite_compile(&pt_sprite_hdr, &pt_sprite_cxt); + + tr_sprite_hdr.argb = PVR_PACK_COLOR(0.5f, 1.0f, 1.0f, 1.0f); + + priority_levels[0] = 0.0f; + priority_levels[1] = 1.0f; + priority_levels[2] = 2.0f; + priority_levels[3] = 3.0f; + priority_levels[4] = 4.0f; + priority_levels[5] = 5.0f; + priority_levels[6] = 6.0f; + priority_levels[7] = 7.0f; + + framecount = 0; + lastup = time(NULL); + + return 0; +} + +static void VIDDCDeInit(void) { + pvr_set_vertbuf(PVR_LIST_OP_POLY, NULL, 0); + pvr_set_vertbuf(PVR_LIST_TR_POLY, NULL, 0); + pvr_set_vertbuf(PVR_LIST_PT_POLY, NULL, 0); + + pvr_mem_free(tex_space); + sem_destroy(dmadone); + + pvr_shutdown(); + vid_set_mode(DM_640x480, PM_RGB565); +} + +static void VIDDCResize(unsigned int w, unsigned int h, int unused) { +} + +static int VIDDCIsFullscreen(void) { + return 1; +} + +static int VIDDCVdp1Reset(void) { + return 0; +} + +static void VIDDCVdp1DrawStart(void) { + if(Vdp2Regs->CLOFEN & 0x40) { + // color offset enable + if(Vdp2Regs->CLOFSL & 0x40) { + // color offset B + vdp1cor = Vdp2Regs->COBR & 0xFF; + if(Vdp2Regs->COBR & 0x100) + vdp1cor |= 0xFFFFFF00; + + vdp1cog = Vdp2Regs->COBG & 0xFF; + if(Vdp2Regs->COBG & 0x100) + vdp1cog |= 0xFFFFFF00; + + vdp1cob = Vdp2Regs->COBB & 0xFF; + if(Vdp2Regs->COBB & 0x100) + vdp1cob |= 0xFFFFFF00; + } + else { + // color offset A + vdp1cor = Vdp2Regs->COAR & 0xFF; + if(Vdp2Regs->COAR & 0x100) + vdp1cor |= 0xFFFFFF00; + + vdp1cog = Vdp2Regs->COAG & 0xFF; + if(Vdp2Regs->COAG & 0x100) + vdp1cog |= 0xFFFFFF00; + + vdp1cob = Vdp2Regs->COAB & 0xFF; + if(Vdp2Regs->COAB & 0x100) + vdp1cob |= 0xFFFFFF00; + } + } + else // color offset disable + vdp1cor = vdp1cog = vdp1cob = 0; +} + +static void VIDDCVdp1DrawEnd(void) { + cached_spr = 0; + priority_levels[0] = 0.0f; + priority_levels[1] = 1.0f; + priority_levels[2] = 2.0f; + priority_levels[3] = 3.0f; + priority_levels[4] = 4.0f; + priority_levels[5] = 5.0f; + priority_levels[6] = 6.0f; + priority_levels[7] = 7.0f; +} + +static void VIDDCVdp1NormalSpriteDraw(void) { + int x, y, num; + u8 z; + vdp1cmd_struct cmd; + pvr_sprite_txr_t sprite; + pvr_list_t list; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + x = Vdp1Regs->localX + cmd.CMDXA; + y = Vdp1Regs->localY + cmd.CMDYA; + cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) << 3; + cur_spr.h = cmd.CMDSIZE & 0xFF; + + if ((cmd.CMDPMOD & 0x07) == 0x03) { + list = PVR_LIST_TR_POLY; + num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + else { + num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); + list = PVR_LIST_PT_POLY; + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + + z = Vdp1ReadPriority(&cmd); + + sprite.flags = PVR_CMD_VERTEX_EOL; + sprite.ax = x; + sprite.ay = y; + sprite.az = priority_levels[z]; + + sprite.bx = x + cur_spr.w; + sprite.by = y; + sprite.bz = priority_levels[z]; + + sprite.cx = x + cur_spr.w; + sprite.cy = y + cur_spr.h; + sprite.cz = priority_levels[z]; + + sprite.dx = x; + sprite.dy = y + cur_spr.h; + + sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); + pvr_list_prim(list, &sprite, sizeof(sprite)); + + priority_levels[z] += 0.000001f; +} + +static void VIDDCVdp1ScaledSpriteDraw(void) { + vdp1cmd_struct cmd; + s16 rw = 0, rh = 0; + s16 x, y; + u8 z; + pvr_sprite_txr_t sprite; + pvr_list_t list; + int num; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + x = cmd.CMDXA + Vdp1Regs->localX; + y = cmd.CMDYA + Vdp1Regs->localY; + cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + cur_spr.h = cmd.CMDSIZE & 0xFF; + + if((cmd.CMDPMOD & 0x07) == 0x03) { + list = PVR_LIST_TR_POLY; + num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + else { + num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); + list = PVR_LIST_PT_POLY; + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + + // Setup Zoom Point + switch ((cmd.CMDCTRL & 0xF00) >> 8) { + case 0x0: // Only two coordinates + rw = cmd.CMDXC - x + Vdp1Regs->localX + 1; + rh = cmd.CMDYC - y + Vdp1Regs->localY + 1; + break; + case 0x5: // Upper-left + rw = cmd.CMDXB + 1; + rh = cmd.CMDYB + 1; + break; + case 0x6: // Upper-Center + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw / 2; + ++rw; + ++rh; + break; + case 0x7: // Upper-Right + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw; + ++rw; + ++rh; + break; + case 0x9: // Center-left + rw = cmd.CMDXB; + rh = cmd.CMDYB; + y = y - rh / 2; + ++rw; + ++rh; + break; + case 0xA: // Center-center + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw / 2; + y = y - rh / 2; + ++rw; + ++rh; + break; + case 0xB: // Center-right + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw; + y = y - rh / 2; + ++rw; + ++rh; + break; + case 0xD: // Lower-left + rw = cmd.CMDXB; + rh = cmd.CMDYB; + y = y - rh; + ++rw; + ++rh; + break; + case 0xE: // Lower-center + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw / 2; + y = y - rh; + ++rw; + ++rh; + break; + case 0xF: // Lower-right + rw = cmd.CMDXB; + rh = cmd.CMDYB; + x = x - rw; + y = y - rh; + ++rw; + ++rh; + break; + default: + break; + } + + z = Vdp1ReadPriority(&cmd); + + sprite.flags = PVR_CMD_VERTEX_EOL; + sprite.ax = x; + sprite.ay = y; + sprite.az = priority_levels[z]; + + sprite.bx = x + rw; + sprite.by = y; + sprite.bz = priority_levels[z]; + + sprite.cx = x + rw; + sprite.cy = y + rh; + sprite.cz = priority_levels[z]; + + sprite.dx = x; + sprite.dy = y + rh; + + sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); + pvr_list_prim(list, &sprite, sizeof(sprite)); + + priority_levels[z] += 0.000001f; +} + +static void VIDDCVdp1DistortedSpriteDraw(void) { + vdp1cmd_struct cmd; + u8 z; + pvr_sprite_txr_t sprite; + pvr_list_t list; + int num; + + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + cur_spr.h = cmd.CMDSIZE & 0xFF; + + if((cmd.CMDPMOD & 0x7) == 0x3) { + list = PVR_LIST_TR_POLY; + num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + else { + num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); + list = PVR_LIST_PT_POLY; + + if(num == 0) + return; + else + pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, + sizeof(pvr_sprite_hdr_t)); + } + + z = Vdp1ReadPriority(&cmd); + + sprite.flags = PVR_CMD_VERTEX_EOL; + sprite.ax = cmd.CMDXA + Vdp1Regs->localX; + sprite.ay = cmd.CMDYA + Vdp1Regs->localY; + sprite.az = priority_levels[z]; + + sprite.bx = cmd.CMDXB + Vdp1Regs->localX + 1; + sprite.by = cmd.CMDYB + Vdp1Regs->localY; + sprite.bz = priority_levels[z]; + + sprite.cx = cmd.CMDXC + Vdp1Regs->localX + 1; + sprite.cy = cmd.CMDYC + Vdp1Regs->localY + 1; + sprite.cz = priority_levels[z]; + + sprite.dx = cmd.CMDXD + Vdp1Regs->localX; + sprite.dy = cmd.CMDYD + Vdp1Regs->localY + 1; + + sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); + sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), + ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); + pvr_list_prim(list, &sprite, sizeof(sprite)); + + priority_levels[z] += 0.000001f; +} + +static void VIDDCVdp1PolygonDraw(void) { + s16 X[4]; + s16 Y[4]; + u16 color; + u16 CMDPMOD; + u8 alpha, z; + pvr_list_t list; + pvr_sprite_col_t spr; + pvr_sprite_hdr_t *hdr; + + X[0] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); + Y[0] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); + X[1] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10); + Y[1] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12); + X[2] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Y[2] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); + X[3] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18); + Y[3] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A); + + color = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x06); + CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x04); + + /* Don't bother rendering completely transparent polygons */ + if((!(color & 0x8000) && !(CMDPMOD & 0x0040)) || !color) { + return; + } + + if((CMDPMOD & 0x0007) == 0x0003) { + alpha = 0x80; + list = PVR_LIST_TR_POLY; + hdr = &tr_poly_hdr; + } + else { + alpha = 0xFF; + list = PVR_LIST_OP_POLY; + hdr = &op_poly_hdr; + } + + if(color & 0x8000) { + hdr->argb = COLOR_ADD32(SAT2YAB32(alpha, color), vdp1cor, vdp1cog, + vdp1cob); + } + else { + hdr->argb = COLOR_ADD32(Vdp2ColorRamGetColor32(color, alpha), vdp1cor, + vdp1cog, vdp1cob); + } + + pvr_list_prim(list, hdr, sizeof(pvr_sprite_hdr_t)); + + z = Vdp2Regs->PRISA & 0x07; + + spr.flags = PVR_CMD_VERTEX_EOL; + spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; + spr.az = spr.bz = spr.cz = priority_levels[z]; + + spr.ax = X[0]; + spr.ay = Y[0]; + spr.bx = X[1]; + spr.by = Y[1]; + spr.cx = X[2]; + spr.cy = Y[2]; + spr.dx = X[3]; + spr.dy = Y[3]; + + pvr_list_prim(list, &spr, sizeof(pvr_sprite_col_t)); + + priority_levels[z] += 0.000001f; +} + +static void VIDDCVdp1PolylineDraw(void) { +} + +static void VIDDCVdp1LineDraw(void) { +} + +static void VIDDCVdp1UserClipping(void) { + Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); + Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); + Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); +} + +static void VIDDCVdp1SystemClipping(void) { + Vdp1Regs->systemclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); + Vdp1Regs->systemclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); + Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); +} + +static void VIDDCVdp1LocalCoordinate(void) { + Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); + Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 DoNothing(void *info, u16 pixel) +{ + return pixel; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 DoColorOffset(void *info, u16 pixel) +{ + return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, + ((vdp2draw_struct *)info)->cog, + ((vdp2draw_struct *)info)->cob); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 DoColorCalc(void *info, u16 pixel) +{ + // should be doing color calculation here + return pixel; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 DoColorCalcWithColorOffset(void *info, u16 pixel) +{ + // should be doing color calculation here + + return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, + ((vdp2draw_struct *)info)->cog, + ((vdp2draw_struct *)info)->cob); +} + +static void Vdp2DrawBackScreen() { + u32 scrAddr; + u16 dot; + pvr_sprite_col_t spr; + + if(Vdp2Regs->VRSIZE & 0x8000) + scrAddr = (((Vdp2Regs->BKTAU & 0x07) << 16) | Vdp2Regs->BKTAL) << 1; + else + scrAddr = (((Vdp2Regs->BKTAU & 0x03) << 16) | Vdp2Regs->BKTAL) << 1; + + if(Vdp2Regs->BKTAU & 0x8000) { + int i; + + for(i = 0; i < vdp2height; ++i) { + dot = T1ReadWord(Vdp2Ram, scrAddr); + scrAddr += 2; + + op_poly_hdr.argb = SAT2YAB32(0xFF, dot); + pvr_list_prim(PVR_LIST_OP_POLY, &op_poly_hdr, + sizeof(pvr_sprite_hdr_t)); + + spr.flags = PVR_CMD_VERTEX_EOL; + spr.ax = 0.0f; + spr.ay = i + 1; + spr.az = 0.1f; + spr.bx = 0.0f; + spr.by = i; + spr.bz = 0.1f; + spr.cx = vdp2width; + spr.cy = i; + spr.cz = 0.1f; + spr.dx = vdp2width; + spr.dy = i + 1; + spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; + pvr_list_prim(PVR_LIST_OP_POLY, &spr, sizeof(pvr_sprite_col_t)); + } + } + else { + dot = T1ReadWord(Vdp2Ram, scrAddr); + + op_poly_hdr.argb = SAT2YAB32(0xFF, dot); + pvr_list_prim(PVR_LIST_OP_POLY, &op_poly_hdr, sizeof(pvr_sprite_hdr_t)); + + spr.flags = PVR_CMD_VERTEX_EOL; + spr.ax = 0.0f; + spr.ay = vdp2height; + spr.az = 0.1f; + spr.bx = 0.0f; + spr.by = 0.0f; + spr.bz = 0.1f; + spr.cx = vdp2width; + spr.cy = 0.0f; + spr.cz = 0.1f; + spr.dx = vdp2width; + spr.dy = vdp2height; + spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; + pvr_list_prim(PVR_LIST_OP_POLY, &spr, sizeof(pvr_sprite_col_t)); + } +} + +static void Vdp2DrawLineColorScreen() { +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2NBG0PlaneAddr(vdp2draw_struct *info, int i) +{ + u32 offset = (Vdp2Regs->MPOFN & 0x7) << 6; + u32 tmp=0; + int deca; + int multi; + + switch(i) + { + case 0: + tmp = offset | (Vdp2Regs->MPABN0 & 0xFF); + break; + case 1: + tmp = offset | (Vdp2Regs->MPABN0 >> 8); + break; + case 2: + tmp = offset | (Vdp2Regs->MPCDN0 & 0xFF); + break; + case 3: + tmp = offset | (Vdp2Regs->MPCDN0 >> 8); + break; + } + + deca = info->planeh + info->planew - 2; + multi = info->planeh * info->planew; + + //if (Vdp2Regs->VRSIZE & 0x8000) + //{ + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); + else + info->addr = (tmp >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); + } + /*} + else + { + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); + } + }*/ +} + +////////////////////////////////////////////////////////////////////////////// + +static int Vdp2DrawNBG0(void) +{ + vdp2draw_struct info; + + /* FIXME should start by checking if it's a normal + * or rotate scroll screen + */ + info.enable = Vdp2Regs->BGON & 0x1; + + if (!(info.enable & Vdp2External.disptoggle)) + return 0; + + info.transparencyenable = !(Vdp2Regs->BGON & 0x100); + info.specialprimode = Vdp2Regs->SFPRMD & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLA & 0x70) >> 4; + + if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) + { + // Bitmap Mode + + switch((Vdp2Regs->CHCTLA & 0xC) >> 2) + { + case 0: info.cellw = 512; + info.cellh = 256; + break; + case 1: info.cellw = 512; + info.cellh = 512; + break; + case 2: info.cellw = 1024; + info.cellh = 256; + break; + case 3: info.cellw = 1024; + info.cellh = 512; + break; + } + + info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % info.cellw); + info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % info.cellh); + + info.charaddr = (Vdp2Regs->MPOFN & 0x7) * 0x20000; + info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + // Tile Mode + info.mapwh = 2; + + switch(Vdp2Regs->PLSZ & 0x3) + { + case 0: + info.planew = info.planeh = 1; + break; + case 1: + info.planew = 2; + info.planeh = 1; + break; + case 3: + info.planew = info.planeh = 2; + break; + default: // Not sure what 0x2 does + info.planew = info.planeh = 1; + break; + } + + info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % (512 * info.planew)); + info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % (512 * info.planeh)); + + if(Vdp2Regs->PNCN0 & 0x8000) + info.patterndatasize = 1; + else + info.patterndatasize = 2; + + if(Vdp2Regs->CHCTLA & 0x1) + info.patternwh = 2; + else + info.patternwh = 1; + + info.pagewh = 64/info.patternwh; + info.cellw = info.cellh = 8; + info.supplementdata = Vdp2Regs->PNCN0 & 0x3FF; + info.auxmode = (Vdp2Regs->PNCN0 & 0x4000) >> 14; + } + + if (Vdp2Regs->CCCTL & 0x1) + info.alpha = ((~Vdp2Regs->CCRNA & 0x1F) << 3) + 0x7; + else + info.alpha = 0xFF; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x7) << 8; + + if (Vdp2Regs->CLOFEN & 0x1) + { + // color offset enable + if (Vdp2Regs->CLOFSL & 0x1) + { + // color offset B + info.cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info.cob |= 0xFFFFFF00; + } + else + { + // color offset A + info.cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info.cob |= 0xFFFFFF00; + } + + if (Vdp2Regs->CCCTL & 0x1) + info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info.PostPixelFetchCalc = &DoColorOffset; + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & 0x1) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + + info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN0.all & 0x7FF00); + info.coordincy = (float) 65536 / (Vdp2Regs->ZMYN0.all & 0x7FF00); + + info.priority = nbg0priority; + info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG0PlaneAddr; + + if (info.isbitmap) + Vdp2DrawScrollBitmap(&info); + else + Vdp2DrawMap(&info); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2NBG1PlaneAddr(vdp2draw_struct *info, int i) +{ + u32 offset = (Vdp2Regs->MPOFN & 0x70) << 2; + u32 tmp=0; + int deca; + int multi; + + switch(i) + { + case 0: + tmp = offset | (Vdp2Regs->MPABN1 & 0xFF); + break; + case 1: + tmp = offset | (Vdp2Regs->MPABN1 >> 8); + break; + case 2: + tmp = offset | (Vdp2Regs->MPCDN1 & 0xFF); + break; + case 3: + tmp = offset | (Vdp2Regs->MPCDN1 >> 8); + break; + } + + deca = info->planeh + info->planew - 2; + multi = info->planeh * info->planew; + + //if (Vdp2Regs->VRSIZE & 0x8000) + //{ + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); + else + info->addr = (tmp >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); + } + /*} + else + { + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); + } + }*/ +} + +////////////////////////////////////////////////////////////////////////////// + +static int Vdp2DrawNBG1(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x2; + + if (!(info.enable & Vdp2External.disptoggle)) + return 0; + + info.transparencyenable = !(Vdp2Regs->BGON & 0x200); + info.specialprimode = (Vdp2Regs->SFPRMD >> 2) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; + + if((info.isbitmap = Vdp2Regs->CHCTLA & 0x200) != 0) + { + switch((Vdp2Regs->CHCTLA & 0xC00) >> 10) + { + case 0: info.cellw = 512; + info.cellh = 256; + break; + case 1: info.cellw = 512; + info.cellh = 512; + break; + case 2: info.cellw = 1024; + info.cellh = 256; + break; + case 3: info.cellw = 1024; + info.cellh = 512; + break; + } + + info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % info.cellw); + info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % info.cellh); + + info.charaddr = ((Vdp2Regs->MPOFN & 0x70) >> 4) * 0x20000; + info.paladdr = Vdp2Regs->BMPNA & 0x700; + info.flipfunction = 0; + info.specialfunction = 0; + } + else + { + info.mapwh = 2; + + switch((Vdp2Regs->PLSZ & 0xC) >> 2) + { + case 0: + info.planew = info.planeh = 1; + break; + case 1: + info.planew = 2; + info.planeh = 1; + break; + case 3: + info.planew = info.planeh = 2; + break; + default: // Not sure what 0x2 does + info.planew = info.planeh = 1; + break; + } + + info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % (512 * info.planew)); + info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % (512 * info.planeh)); + + if(Vdp2Regs->PNCN1 & 0x8000) + info.patterndatasize = 1; + else + info.patterndatasize = 2; + + if(Vdp2Regs->CHCTLA & 0x100) + info.patternwh = 2; + else + info.patternwh = 1; + + info.pagewh = 64/info.patternwh; + info.cellw = info.cellh = 8; + info.supplementdata = Vdp2Regs->PNCN1 & 0x3FF; + info.auxmode = (Vdp2Regs->PNCN1 & 0x4000) >> 14; + } + + if (Vdp2Regs->CCCTL & 0x2) + info.alpha = ((~Vdp2Regs->CCRNA & 0x1F00) >> 5) + 0x7; + else + info.alpha = 0xFF; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x70) << 4; + + if (Vdp2Regs->CLOFEN & 0x2) + { + // color offset enable + if (Vdp2Regs->CLOFSL & 0x2) + { + // color offset B + info.cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info.cob |= 0xFFFFFF00; + } + else + { + // color offset A + info.cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info.cob |= 0xFFFFFF00; + } + + if (Vdp2Regs->CCCTL & 0x2) + info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info.PostPixelFetchCalc = &DoColorOffset; + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & 0x2) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + + info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00); + info.coordincy = (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00); + + info.priority = nbg1priority; + info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG1PlaneAddr; + + if (info.isbitmap) + { + Vdp2DrawScrollBitmap(&info); +/* + // Handle Scroll Wrapping(Let's see if we even need do to it to begin + // with) + if (info.x < (vdp2width - info.cellw)) + { + info.vertices[0] = (info.x+info.cellw) * info.coordincx; + info.vertices[2] = (info.x + (info.cellw<<1)) * info.coordincx; + info.vertices[4] = (info.x + (info.cellw<<1)) * info.coordincx; + info.vertices[6] = (info.x+info.cellw) * info.coordincx; + + YglCachedQuad((YglSprite *)&info, tmp); + + if (info.y < (vdp2height - info.cellh)) + { + info.vertices[1] = (info.y+info.cellh) * info.coordincy; + info.vertices[3] = (info.y + (info.cellh<<1)) * info.coordincy; + info.vertices[5] = (info.y + (info.cellh<<1)) * info.coordincy; + info.vertices[7] = (info.y+info.cellh) * info.coordincy; + + YglCachedQuad((YglSprite *)&info, tmp); + } + } + else if (info.y < (vdp2height - info.cellh)) + { + info.vertices[1] = (info.y+info.cellh) * info.coordincy; + info.vertices[3] = (info.y + (info.cellh<<1)) * info.coordincy; + info.vertices[5] = (info.y + (info.cellh<<1)) * info.coordincy; + info.vertices[7] = (info.y+info.cellh) * info.coordincy; + + YglCachedQuad((YglSprite *)&info, tmp); + } +*/ + } + else + Vdp2DrawMap(&info); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2NBG2PlaneAddr(vdp2draw_struct *info, int i) +{ + u32 offset = (Vdp2Regs->MPOFN & 0x700) >> 2; + u32 tmp=0; + int deca; + int multi; + + switch(i) + { + case 0: + tmp = offset | (Vdp2Regs->MPABN2 & 0xFF); + break; + case 1: + tmp = offset | (Vdp2Regs->MPABN2 >> 8); + break; + case 2: + tmp = offset | (Vdp2Regs->MPCDN2 & 0xFF); + break; + case 3: + tmp = offset | (Vdp2Regs->MPCDN2 >> 8); + break; + } + + deca = info->planeh + info->planew - 2; + multi = info->planeh * info->planew; + + //if (Vdp2Regs->VRSIZE & 0x8000) + //{ + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); + else + info->addr = (tmp >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); + } + /*} + else + { + if (info->patterndatasize == 1) + { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); + } + else + { + if (info->patternwh == 1) + info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); + } + }*/ +} + +////////////////////////////////////////////////////////////////////////////// + +static int Vdp2DrawNBG2(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x4; + + if (!(info.enable & Vdp2External.disptoggle)) + return 0; + + info.transparencyenable = !(Vdp2Regs->BGON & 0x400); + info.specialprimode = (Vdp2Regs->SFPRMD >> 4) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLB & 0x2) >> 1; + info.mapwh = 2; + + switch((Vdp2Regs->PLSZ & 0x30) >> 4) + { + case 0: + info.planew = info.planeh = 1; + break; + case 1: + info.planew = 2; + info.planeh = 1; + break; + case 3: + info.planew = info.planeh = 2; + break; + default: // Not sure what 0x2 does + info.planew = info.planeh = 1; + break; + } + info.x = - ((Vdp2Regs->SCXN2 & 0x7FF) % (512 * info.planew)); + info.y = - ((Vdp2Regs->SCYN2 & 0x7FF) % (512 * info.planeh)); + + if(Vdp2Regs->PNCN2 & 0x8000) + info.patterndatasize = 1; + else + info.patterndatasize = 2; + + if(Vdp2Regs->CHCTLB & 0x1) + info.patternwh = 2; + else + info.patternwh = 1; + + info.pagewh = 64/info.patternwh; + info.cellw = info.cellh = 8; + info.supplementdata = Vdp2Regs->PNCN2 & 0x3FF; + info.auxmode = (Vdp2Regs->PNCN2 & 0x4000) >> 14; + + if (Vdp2Regs->CCCTL & 0x4) + info.alpha = ((~Vdp2Regs->CCRNB & 0x1F) << 3) + 0x7; + else + info.alpha = 0xFF; + + info.coloroffset = Vdp2Regs->CRAOFA & 0x700; + + if (Vdp2Regs->CLOFEN & 0x4) + { + // color offset enable + if (Vdp2Regs->CLOFSL & 0x4) + { + // color offset B + info.cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info.cob |= 0xFFFFFF00; + } + else + { + // color offset A + info.cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info.cob |= 0xFFFFFF00; + } + + if (Vdp2Regs->CCCTL & 0x4) + info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info.PostPixelFetchCalc = &DoColorOffset; + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & 0x4) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + + info.coordincx = info.coordincy = 1; + + info.priority = nbg2priority; + info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG2PlaneAddr; + + Vdp2DrawMap(&info); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void Vdp2NBG3PlaneAddr(vdp2draw_struct *info, int i) +{ + u32 offset = (Vdp2Regs->MPOFN & 0x7000) >> 6; + u32 tmp=0; + int deca; + int multi; + + switch(i) + { + case 0: + tmp = offset | (Vdp2Regs->MPABN3 & 0xFF); + break; + case 1: + tmp = offset | (Vdp2Regs->MPABN3 >> 8); + break; + case 2: + tmp = offset | (Vdp2Regs->MPCDN3 & 0xFF); + break; + case 3: + tmp = offset | (Vdp2Regs->MPCDN3 >> 8); + break; + } + + deca = info->planeh + info->planew - 2; + multi = info->planeh * info->planew; + + //if (Vdp2Regs->VRSIZE & 0x8000) { + if (info->patterndatasize == 1) { + if (info->patternwh == 1) + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); + else + info->addr = (tmp >> deca) * (multi * 0x800); + } + else { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); + } + /*} + else { + if (info->patterndatasize == 1) { + if (info->patternwh == 1) + info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); + else + info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); + } + else { + if (info->patternwh == 1) + info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); + else + info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); + } + }*/ +} + +////////////////////////////////////////////////////////////////////////////// + +static int Vdp2DrawNBG3(void) +{ + vdp2draw_struct info; + + info.enable = Vdp2Regs->BGON & 0x8; + + if (!(info.enable & Vdp2External.disptoggle)) + return 0; + + info.transparencyenable = !(Vdp2Regs->BGON & 0x800); + info.specialprimode = (Vdp2Regs->SFPRMD >> 6) & 0x3; + + info.colornumber = (Vdp2Regs->CHCTLB & 0x20) >> 5; + + info.mapwh = 2; + + switch((Vdp2Regs->PLSZ & 0xC0) >> 6) + { + case 0: + info.planew = info.planeh = 1; + break; + case 1: + info.planew = 2; + info.planeh = 1; + break; + case 3: + info.planew = info.planeh = 2; + break; + default: // Not sure what 0x2 does + info.planew = info.planeh = 1; + break; + } + info.x = - ((Vdp2Regs->SCXN3 & 0x7FF) % (512 * info.planew)); + info.y = - ((Vdp2Regs->SCYN3 & 0x7FF) % (512 * info.planeh)); + + if(Vdp2Regs->PNCN3 & 0x8000) + info.patterndatasize = 1; + else + info.patterndatasize = 2; + + if(Vdp2Regs->CHCTLB & 0x10) + info.patternwh = 2; + else + info.patternwh = 1; + + info.pagewh = 64/info.patternwh; + info.cellw = info.cellh = 8; + info.supplementdata = Vdp2Regs->PNCN3 & 0x3FF; + info.auxmode = (Vdp2Regs->PNCN3 & 0x4000) >> 14; + + if (Vdp2Regs->CCCTL & 0x8) + info.alpha = ((~Vdp2Regs->CCRNB & 0x1F00) >> 5) + 0x7; + else + info.alpha = 0xFF; + + info.coloroffset = (Vdp2Regs->CRAOFA & 0x7000) >> 4; + + if (Vdp2Regs->CLOFEN & 0x8) + { + // color offset enable + if (Vdp2Regs->CLOFSL & 0x8) + { + // color offset B + info.cor = Vdp2Regs->COBR & 0xFF; + if (Vdp2Regs->COBR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COBG & 0xFF; + if (Vdp2Regs->COBG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COBB & 0xFF; + if (Vdp2Regs->COBB & 0x100) + info.cob |= 0xFFFFFF00; + } + else + { + // color offset A + info.cor = Vdp2Regs->COAR & 0xFF; + if (Vdp2Regs->COAR & 0x100) + info.cor |= 0xFFFFFF00; + + info.cog = Vdp2Regs->COAG & 0xFF; + if (Vdp2Regs->COAG & 0x100) + info.cog |= 0xFFFFFF00; + + info.cob = Vdp2Regs->COAB & 0xFF; + if (Vdp2Regs->COAB & 0x100) + info.cob |= 0xFFFFFF00; + } + + if (Vdp2Regs->CCCTL & 0x8) + info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; + else + info.PostPixelFetchCalc = &DoColorOffset; + } + else // color offset disable + { + if (Vdp2Regs->CCCTL & 0x8) + info.PostPixelFetchCalc = &DoColorCalc; + else + info.PostPixelFetchCalc = &DoNothing; + } + + info.coordincx = info.coordincy = 1; + + info.priority = nbg3priority; + info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG3PlaneAddr; + + Vdp2DrawMap(&info); + + return 1; +} + +static int VIDDCVdp2Reset(void) { + return 0; +} + +static void VIDDCVdp2DrawStart(void) { + cur_addr = (uint32) tex_space; + cur_vdp2 = (uint32) vdp2_tex; + + pvr_wait_ready(); + pvr_scene_begin(); + + Vdp2DrawBackScreen(); + Vdp2DrawLineColorScreen(); +} + +static void VIDDCVdp2DrawEnd(void) { + /* Make sure we don't have any texture dma still going on... */ + sem_wait(dmadone); + sem_signal(dmadone); + + pvr_scene_finish(); + + ++framecount; + + if(lastup + 10 <= time(NULL)) { + printf("%d frames in %d seconds FPS: %f\n", framecount, time(NULL) - + lastup, ((float)(framecount)) / (time(NULL) - lastup)); + framecount = 0; + lastup = time(NULL); + } +} + +static void dma_callback(ptr_t data __attribute__((unused))) { + sem_signal(dmadone); +} + +static void Vdp2Draw(int priority) { + pvr_sprite_txr_t sprite; + + pt_sprite_hdr.mode2 &= (~(PVR_TA_PM2_USIZE_MASK | PVR_TA_PM2_VSIZE_MASK)); + pt_sprite_hdr.mode2 |= (6 << PVR_TA_PM2_USIZE_SHIFT) | + (5 << PVR_TA_PM2_VSIZE_SHIFT); + pt_sprite_hdr.mode3 = ((cur_vdp2 & 0x00FFFFF8) >> 3) | + (PVR_TXRFMT_NONTWIDDLED); + + pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, sizeof(pvr_sprite_hdr_t)); + + sprite.flags = PVR_CMD_VERTEX_EOL; + sprite.ax = 0; + sprite.ay = 0; + sprite.az = priority_levels[priority]; + + sprite.bx = vdp2width; + sprite.by = 0; + sprite.bz = priority_levels[priority]; + + sprite.cx = vdp2width; + sprite.cy = vdp2height; + sprite.cz = priority_levels[priority]; + + sprite.dx = 0; + sprite.dy = vdp2height; + + sprite.auv = PVR_PACK_16BIT_UV(0.0f, 0.0f); + sprite.buv = PVR_PACK_16BIT_UV(vdp2width / 512.0f, 0.0f); + sprite.cuv = PVR_PACK_16BIT_UV(vdp2width / 512.0f, vdp2height / 256.0f); + pvr_list_prim(PVR_LIST_PT_POLY, &sprite, sizeof(pvr_sprite_txr_t)); + + priority_levels[priority] += 0.000001f; +} + +static void VIDDCVdp2DrawScreens(void) { + int i; + + vdp2_fb = vdp2_fbs[0]; + vdp2_fbnum = 0; + + for(i = 1; i < 8; i++) { + if(nbg3priority == i) { + if(Vdp2DrawNBG3()) { + dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); + sem_wait(dmadone); + + pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, + 0, dma_callback, 0); + + Vdp2Draw(i); + + cur_vdp2 += 512 * 256 * 2; + vdp2_fbnum ^= 1; + vdp2_fb = vdp2_fbs[vdp2_fbnum]; + } + } + if(nbg2priority == i) { + if(Vdp2DrawNBG2()) { + dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); + sem_wait(dmadone); + + pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, + 0, dma_callback, 0); + + Vdp2Draw(i); + + cur_vdp2 += 512 * 256 * 2; + vdp2_fbnum ^= 1; + vdp2_fb = vdp2_fbs[vdp2_fbnum]; + } + } + if(nbg1priority == i) { + if(Vdp2DrawNBG1()) { + dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); + sem_wait(dmadone); + + pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, + 0, dma_callback, 0); + + Vdp2Draw(i); + + cur_vdp2 += 512 * 256 * 2; + vdp2_fbnum ^= 1; + vdp2_fb = vdp2_fbs[vdp2_fbnum]; + } + } + if(nbg0priority == i) { + if(Vdp2DrawNBG0()) { + dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); + sem_wait(dmadone); + + pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, + 0, dma_callback, 0); + + Vdp2Draw(i); + + cur_vdp2 += 512 * 256 * 2; + vdp2_fbnum ^= 1; + vdp2_fb = vdp2_fbs[vdp2_fbnum]; + } + } +// if (rbg0priority == i) +// Vdp2DrawRBG0(); + } +} + +static void VIDDCVdp2SetResolution(u16 TVMD) { + int w = 0, h = 0; + + switch(TVMD & 0x03) { + case 0: + w = 320; + break; + case 1: + w = 352; + break; + case 2: + w = 640; + break; + case 3: + w = 704; + break; + } + + switch((TVMD >> 4) & 0x03) { + case 0: + h = 224; + break; + case 1: + h = 240; + break; + case 2: + h = 256; + break; + } + + switch((TVMD >> 6) & 0x03) { + case 2: + case 3: + h <<= 1; + default: + break; + } + + vdp2width = w; + vdp2height = h; + + if(w > 352 || h > 256) { + printf("Unsupported resolution set %d x %d\n", w, h); + printf("Bailing out!\n"); + exit(-1); + } +} + +static void VIDDCVdp2SetPriorityNBG0(int priority) { + nbg0priority = priority; +} + +static void VIDDCVdp2SetPriorityNBG1(int priority) { + nbg1priority = priority; +} + +static void VIDDCVdp2SetPriorityNBG2(int priority) { + nbg2priority = priority; +} + +static void VIDDCVdp2SetPriorityNBG3(int priority) { + nbg3priority = priority; +} + +static void VIDDCVdp2SetPriorityRBG0(int priority) { + rbg0priority = priority; +} + +VideoInterface_struct VIDDC = { + VIDCORE_DC, + "Dreamcast PVR Video Interface", + VIDDCInit, + VIDDCDeInit, + VIDDCResize, + VIDDCIsFullscreen, + VIDDCVdp1Reset, + VIDDCVdp1DrawStart, + VIDDCVdp1DrawEnd, + VIDDCVdp1NormalSpriteDraw, + VIDDCVdp1ScaledSpriteDraw, + VIDDCVdp1DistortedSpriteDraw, + VIDDCVdp1PolygonDraw, + VIDDCVdp1PolylineDraw, + VIDDCVdp1LineDraw, + VIDDCVdp1UserClipping, + VIDDCVdp1SystemClipping, + VIDDCVdp1LocalCoordinate, + VIDDCVdp2Reset, + VIDDCVdp2DrawStart, + VIDDCVdp2DrawEnd, + VIDDCVdp2DrawScreens, + VIDDCVdp2SetResolution, + VIDDCVdp2SetPriorityNBG0, + VIDDCVdp2SetPriorityNBG1, + VIDDCVdp2SetPriorityNBG2, + VIDDCVdp2SetPriorityNBG3, + VIDDCVdp2SetPriorityRBG0 +}; diff --git a/yabause/src/dreamcast/viddc.h b/yabause/src/dreamcast/viddc.h new file mode 100644 index 0000000000..e824200944 --- /dev/null +++ b/yabause/src/dreamcast/viddc.h @@ -0,0 +1,28 @@ +/* Copyright 2005 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef VIDDC_H +#define VIDDC_H + +#include "../vdp1.h" + +#define VIDCORE_DC 3 +extern VideoInterface_struct VIDDC; + +#endif diff --git a/yabause/src/dreamcast/yui.c b/yabause/src/dreamcast/yui.c new file mode 100644 index 0000000000..17762e09f2 --- /dev/null +++ b/yabause/src/dreamcast/yui.c @@ -0,0 +1,234 @@ +/* Copyright 2003 Guillaume Duhamel + Copyright 2004-2010 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../yui.h" +#include "../peripheral.h" +#include "../cs0.h" +#include "../m68kcore.h" +#include "../m68kc68k.h" +#include "perdc.h" +#include "viddc.h" +#include "sh2rec/sh2rec.h" + +SH2Interface_struct *SH2CoreList[] = { + &SH2Interpreter, + &SH2Dynarec, + NULL +}; + +PerInterface_struct *PERCoreList[] = { + &PERDC, + NULL +}; + +CDInterface *CDCoreList[] = { + &ArchCD, + &DummyCD, + NULL +}; + +SoundInterface_struct *SNDCoreList[] = { + &SNDDummy, + NULL +}; + +VideoInterface_struct *VIDCoreList[] = { + &VIDDummy, + &VIDDC, + NULL +}; + +M68K_struct * M68KCoreList[] = { + &M68KDummy, + &M68KC68K, +#ifdef HAVE_Q68 + &M68KQ68, +#endif + NULL +}; + +static const char *bios = "/ram/saturn.bin"; +static int emulate_bios = 0; + +int YuiInit(int sh2core) { + yabauseinit_struct yinit; + + yinit.percoretype = PERCORE_DC; + yinit.sh2coretype = sh2core; + yinit.vidcoretype = VIDCORE_DC; + yinit.m68kcoretype = M68KCORE_C68K; + yinit.sndcoretype = SNDCORE_DUMMY; + yinit.cdcoretype = CDCORE_ARCH; + yinit.carttype = CART_NONE; + yinit.regionid = REGION_AUTODETECT; + yinit.biospath = emulate_bios ? NULL : bios; + yinit.cdpath = NULL; + yinit.buppath = NULL; + yinit.mpegpath = NULL; + yinit.cartpath = NULL; + yinit.frameskip = 0; + yinit.videoformattype = VIDEOFORMATTYPE_NTSC; + yinit.clocksync = 0; + yinit.basetime = 0; + + if(YabauseInit(&yinit) != 0) + return -1; + + for(;;) { + PERCore->HandleEvents(); + } + + return 0; +} + +void YuiErrorMsg(const char *error_text) { + fprintf(stderr, "Error: %s\n", error_text); + arch_exit(); +} + +void YuiSwapBuffers(void) { + /* Nothing here. */ +} + +int DoGui() { + struct coord { + int x; + int y; + }; + + struct coord snowflakes[1024]; + int i; + int offset; + int start_pressed = 0; + int phase = 0; + int core = SH2CORE_INTERPRETER; + + srand(time(NULL)); + + for(i = 0; i < 1024; ++i) { + snowflakes[i].x = (rand() % 640); + snowflakes[i].y = -(rand() % 480); + } + + while(!start_pressed) { + offset = 64 * 640 + 64; /* 64 pixels in from the left, 64 down */ + + bfont_draw_str(vram_s + offset, 640, 0, "Yabause " VERSION); + offset += 640 * 128; + + if(phase == 0) { + FILE *fp; + + fp = fopen("/cd/saturn.bin", "r"); + if(fp) { + fclose(fp); + + fs_copy("/cd/saturn.bin", bios); + phase = 1; + continue; + } + + bfont_draw_str(vram_s + offset, 640, 0, + "Please insert a CD containing the Saturn BIOS"); + offset += 640 * 24; + bfont_draw_str(vram_s + offset, 640, 0, + "on the root of the disc, named saturn.bin."); + offset += 640 * 48; + bfont_draw_str(vram_s + offset, 640, 0, + "Or, to use the BIOS emulation feature, insert"); + offset += 640 * 24; + bfont_draw_str(vram_s + offset, 640, 0, + "a Sega Saturn CD and press Start."); + } + else { + bfont_draw_str(vram_s + offset, 640, 0, + "Please insert a Sega Saturn CD"); + offset += 640 * 24; + bfont_draw_str(vram_s + offset, 640, 0, "and press start."); + } + + for(i = 0; i < 1024; ++i) { + int dx = 1 - (rand() % 3); + + if(snowflakes[i].y >= 0) + vram_s[640 * snowflakes[i].y + snowflakes[i].x] = 0x0000; + + snowflakes[i].x += dx; + snowflakes[i].y += 1; + + if(snowflakes[i].x < 0) + snowflakes[i].x = 639; + else if(snowflakes[i].x > 639) + snowflakes[i].x = 0; + + if(snowflakes[i].y > 479) + snowflakes[i].y = 0; + + if(snowflakes[i].y >= 0) + vram_s[640 * snowflakes[i].y + snowflakes[i].x] = 0xD555; + } + + MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) + if(st->buttons & CONT_START) { + if(phase == 0) { + emulate_bios = 1; + } + + start_pressed = 1; + } + + if(st->buttons & CONT_Y) { + core = SH2CORE_DYNAREC; + + if(phase == 0) { + emulate_bios = 1; + } + + start_pressed = 1; + } + MAPLE_FOREACH_END() + + vid_waitvbl(); + vid_flip(1); + } + + return core; +} + +int main(int argc, char *argv[]) { + int core; + + printf("...\n"); + + bfont_set_encoding(BFONT_CODE_ISO8859_1); + core = DoGui(); + YuiInit(core); + + return 0; +} diff --git a/yabause/src/dx.h b/yabause/src/dx.h new file mode 100644 index 0000000000..c52e0b4540 --- /dev/null +++ b/yabause/src/dx.h @@ -0,0 +1,43 @@ +/* Copyright 2008 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef DX_H +#define DX_H + +#ifdef __MINGW32__ +// I have to do this because for some reason because the dxerr8.h header is fubared +const char* __stdcall DXGetErrorString8A(HRESULT hr); +#define DXGetErrorString8 DXGetErrorString8A +const char* __stdcall DXGetErrorDescription8A(HRESULT hr); +#define DXGetErrorDescription8 DXGetErrorDescription8A +#else +#ifndef DXERRH_IS_BROKEN +#include + +#define DXGetErrorString8 DXGetErrorString9A +#define DXGetErrorDescription8 DXGetErrorDescription9A +#else +#include + +#define DXGetErrorString8 DXGetErrorStringA +#define DXGetErrorDescription8 DXGetErrorDescriptionA + +#endif +#endif + +#endif diff --git a/yabause/src/error.c b/yabause/src/error.c new file mode 100644 index 0000000000..8dd4beba92 --- /dev/null +++ b/yabause/src/error.c @@ -0,0 +1,115 @@ +/* Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "error.h" +#include "yui.h" + +////////////////////////////////////////////////////////////////////////////// + +static void AllocAmendPrintString(const char *string1, const char *string2) +{ + char *string; + + if ((string = (char *)malloc(strlen(string1) + strlen(string2) + 2)) == NULL) + return; + + sprintf(string, "%s%s\n", string1, string2); + YuiErrorMsg(string); + + free(string); +} + +////////////////////////////////////////////////////////////////////////////// + +void YabSetError(int type, const void *extra) +{ + char tempstr[512]; + SH2_struct *sh; + + switch (type) + { + case YAB_ERR_FILENOTFOUND: + AllocAmendPrintString(_("File not found: "), extra); + break; + case YAB_ERR_MEMORYALLOC: + YuiErrorMsg(_("Error allocating memory\n")); + break; + case YAB_ERR_FILEREAD: + AllocAmendPrintString(_("Error reading file: "), extra); + break; + case YAB_ERR_FILEWRITE: + AllocAmendPrintString(_("Error writing file: "), extra); + break; + case YAB_ERR_CANNOTINIT: + AllocAmendPrintString(_("Cannot initialize "), extra); + break; + case YAB_ERR_SH2INVALIDOPCODE: + sh = (SH2_struct *)extra; + SH2GetRegisters(sh, &sh->regs); + sprintf(tempstr, "%s SH2 invalid opcode\n\n" + "R0 = %08lX\tR12 = %08lX\n" + "R1 = %08lX\tR13 = %08lX\n" + "R2 = %08lX\tR14 = %08lX\n" + "R3 = %08lX\tR15 = %08lX\n" + "R4 = %08lX\tSR = %08lX\n" + "R5 = %08lX\tGBR = %08lX\n" + "R6 = %08lX\tVBR = %08lX\n" + "R7 = %08lX\tMACH = %08lX\n" + "R8 = %08lX\tMACL = %08lX\n" + "R9 = %08lX\tPR = %08lX\n" + "R10 = %08lX\tPC = %08lX\n" + "R11 = %08lX\n", sh->isslave ? "Slave" : "Master", + (long)sh->regs.R[0], (long)sh->regs.R[12], + (long)sh->regs.R[1], (long)sh->regs.R[13], + (long)sh->regs.R[2], (long)sh->regs.R[14], + (long)sh->regs.R[3], (long)sh->regs.R[15], + (long)sh->regs.R[4], (long)sh->regs.SR.all, + (long)sh->regs.R[5], (long)sh->regs.GBR, + (long)sh->regs.R[6], (long)sh->regs.VBR, + (long)sh->regs.R[7], (long)sh->regs.MACH, + (long)sh->regs.R[8], (long)sh->regs.MACL, + (long)sh->regs.R[9], (long)sh->regs.PR, + (long)sh->regs.R[10], (long)sh->regs.PC, + (long)sh->regs.R[11]); + YuiErrorMsg(tempstr); + break; + case YAB_ERR_SH2READ: + YuiErrorMsg(_("SH2 read error\n")); // fix me + break; + case YAB_ERR_SH2WRITE: + YuiErrorMsg(_("SH2 write error\n")); // fix me + break; + case YAB_ERR_SDL: + AllocAmendPrintString(_("SDL Error: "), extra); + break; + case YAB_ERR_OTHER: + YuiErrorMsg((char *)extra); + break; + case YAB_ERR_UNKNOWN: + default: + YuiErrorMsg(_("Unknown error occurred\n")); + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/error.h b/yabause/src/error.h new file mode 100644 index 0000000000..6d18c330e0 --- /dev/null +++ b/yabause/src/error.h @@ -0,0 +1,39 @@ +/* Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ERROR_H +#define ERROR_H + +#define YAB_ERR_UNKNOWN 0 +#define YAB_ERR_FILENOTFOUND 1 +#define YAB_ERR_MEMORYALLOC 2 +#define YAB_ERR_FILEREAD 3 +#define YAB_ERR_FILEWRITE 4 +#define YAB_ERR_CANNOTINIT 5 + +#define YAB_ERR_SH2INVALIDOPCODE 6 +#define YAB_ERR_SH2READ 7 +#define YAB_ERR_SH2WRITE 8 + +#define YAB_ERR_SDL 9 + +#define YAB_ERR_OTHER 10 + +void YabSetError(int type, const void *extra); +#endif diff --git a/yabause/src/font.h b/yabause/src/font.h new file mode 100644 index 0000000000..7ce4910aa6 --- /dev/null +++ b/yabause/src/font.h @@ -0,0 +1,1301 @@ +/* Copyright 2012 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +static const char * font[] = { +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" .. ", +" .##.", +" .##. ", +" .##. ", +" .##. ", +" .##. ", +" .##. ", +" .##. ", +".##. ", +" .. ", +" ..... ", +" .#####. ", +".#######.", +".##...##.", +".##.#.##.", +".##.#.##.", +".##...##.", +".#######.", +" .#####. ", +" ..... ", +" .... ", +" .####. ", +" .####. ", +" ..##. ", +" .##. ", +" .##. ", +" ...##.. ", +".#######.", +".#######.", +" ....... ", +" ...... ", +".######. ", +".#######.", +" .....##.", +".#######.", +".######. ", +".##..... ", +".#######.", +".#######.", +" ....... ", +" ....... ", +".#######.", +".#######.", +" .....##.", +".#######.", +".#######.", +" .....##.", +".#######.", +".#######.", +" ....... ", +" .. ", +".##. ", +".##... ", +".##.##. ", +".##.##.. ", +".#######.", +".#######.", +" ...##.. ", +" .##. ", +" .. ", +" ....... ", +".#######.", +".#######.", +".##..... ", +".######. ", +".#######.", +" .....##.", +".#######.", +".######. ", +" ...... ", +" ...... ", +" .######.", +".#######.", +".##..... ", +".######. ", +".#######.", +".##...##.", +".#######.", +" .#####. ", +" ..... ", +" ....... ", +".#######.", +".#######.", +" .....##.", +" .##.", +" .##.", +" .##.", +" .##.", +" .##.", +" .. ", +" ..... ", +" .#####. ", +".#######.", +".##...##.", +" .######.", +".######. ", +".##...##.", +".#######.", +" .#####. ", +" ..... ", +" ..... ", +" .#####. ", +".#######.", +".##...##.", +".#######.", +" .######.", +" .....##.", +".#######.", +".#######.", +" ....... ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ..... ", +" .#####. ", +".#######.", +".##...##.", +".#######.", +".#######.", +".##...##.", +".##. .##.", +".##. .##.", +" .. .. ", +" ...... ", +".######. ", +".#######.", +".##...##.", +".######. ", +".######. ", +".##...##.", +".#######.", +".######. ", +" ...... ", +" ....... ", +".#######.", +".#######.", +".##..... ", +".##. ", +".##. ", +".##..... ", +".#######.", +".#######.", +" ....... ", +" ...... ", +".######. ", +".#######.", +".##...##.", +".##. .##.", +".##. .##.", +".##...##.", +".#######.", +".######. ", +" ...... ", +" ....... ", +".#######.", +".#######.", +".##..... ", +".####. ", +".####. ", +".##..... ", +".#######.", +".#######.", +" ....... ", +" ....... ", +".#######.", +".#######.", +".##..... ", +".####. ", +".####. ", +".##.. ", +".##. ", +".##. ", +" .. ", +" ....... ", +".#######.", +".#######.", +".##..... ", +".##..###.", +".##..###.", +".##...##.", +".#######.", +".#######.", +" ....... ", +" .. .. ", +".##. .##.", +".##. .##.", +".##...##.", +".#######.", +".#######.", +".##...##.", +".##. .##.", +".##. .##.", +" .. .. ", +" ....... ", +".#######.", +".#######.", +" ..###.. ", +" .###. ", +" .###. ", +" ..###.. ", +".#######.", +".#######.", +" ....... ", +" ....... ", +".#######.", +".#######.", +" ....###.", +" .###.", +" .. .###.", +".##..###.", +".#######.", +" ..####. ", +" .... ", +" .. .. ", +".##. .##.", +".##. .##.", +".##..###.", +".######. ", +".#####. ", +".##.###. ", +".##..###.", +".##. .##.", +" .. .. ", +" .. ", +".##. ", +".##. ", +".##. ", +".##. ", +".##. ", +".##..... ", +".#######.", +".#######.", +" ....... ", +" .. .. ", +".##. .##.", +".###.###.", +".#######.", +".##.#.##.", +".##...##.", +".##. .##.", +".##. .##.", +".##. .##.", +" .. .. ", +" .. .. ", +".##. .##.", +".###..##.", +".####.##.", +".##.####.", +".##..###.", +".##. .##.", +".##. .##.", +".##. .##.", +" .. .. ", +" ... ", +" .###. ", +" .#####. ", +".###.###.", +".##. .##.", +".##. .##.", +".###.###.", +" .#####. ", +" .###. ", +" ... ", +" ...... ", +".######. ", +".#######. ", +".##...##.", +".#######.", +".######. ", +".##.... ", +".##. ", +".##. ", +" .. ", +" ... ", +" .###. ", +" .#####. ", +".##...##.", +".##...##.", +".##.#.##.", +".##..###.", +" .######.", +" .###.#.", +" ... . ", +" ...... ", +".######. ", +".#######.", +".##...##.", +".#######.", +".######. ", +".##..###.", +".##. .##.", +".##. .##.", +" .. .. ", +" ..... ", +" .#####. ", +".######. ", +".##.... ", +".######. ", +".######. ", +" ....##. ", +".######. ", +".#####. ", +" ..... ", +" ....... ", +".#######.", +".#######.", +" ..###.. ", +" .###. ", +" .###. ", +" .###. ", +" .###. ", +" .###. ", +" ... ", +" .. .. ", +".##. .##.", +".##. .##.", +".##. .##.", +".##. .##.", +".##. .##.", +".##...##.", +".#######.", +" .#####. ", +" ..... ", +" .. .. ", +".##. .##.", +".##. .##.", +".##. .##.", +" .##.##. ", +" .##.##. ", +" .##.##. ", +" .###. ", +" .###. ", +" ... ", +" .. .. ", +".##...##.", +".##.#.##.", +".##.#.##.", +" .#####. ", +" .#####. ", +" .#####. ", +" .###. ", +" .#.#. ", +" . . ", +" .. .. ", +".##. .##.", +".##. .##.", +".##. .##.", +" .##.##. ", +" .###. ", +" .##.##. ", +".##. .##.", +".##. .##.", +" .. .. ", +" .. .. ", +".##. .##.", +".##. .##.", +".##...##.", +".#######.", +".#######.", +" .....##.", +".#######.", +".#######.", +" ....... ", +" ....... ", +".#######.", +".#######.", +" ....##. ", +" .##. ", +" .##. ", +" .##.... ", +".#######.", +".#######.", +" ....... ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +}; diff --git a/yabause/src/gameshw/dsplist.txt b/yabause/src/gameshw/dsplist.txt new file mode 100644 index 0000000000..b1b5eea808 --- /dev/null +++ b/yabause/src/gameshw/dsplist.txt @@ -0,0 +1,64 @@ +Games using SCU dsp +-------------------- +Battle Monsters +Blazing Tornado +Blue Chicago Blues - J.B.Harold +Blue Seed +Bubble Symphony +Capcom Generations 1 +Capcom Generations 4 +Cotton Boomerang +Crimewave +Daisuki +Dark Savior +Dead or Alive +Don Pachi +Doom +Dragon Ball Z Shinbuthoden +DX Jinsei Game - The Game of Life +Fatal Fury 3 +Find Love 2 +?Gal Panic SS +Galaxy Force 2(sega ages) +Grandia +Grandia: Digital Museum +GT24 +Guardian Force +Guyferd +Jissen Pachinko Hisshouhou! Twin +Lunar 2: Eternal Blue +Mega Man X4 +Momotaroudouchuuki +Nadesico +Mighty Hits +Planet Joker +Psychic Killer Taromaru +Real Bout FF +Real Bout FF Special +Samurai Shodown RPG +Sega Ages Memorial Selection - Vol. 1 +?Shining Force 3 Scenario 1 +Shining Force 3 Scenario 2 +Shining Force 3 Scenario 3 +Sky Target +Slam Dunk - I love Basketball (heavy usage) +Slayers Royal +Sonic R +Stakes Winner SS +Steeldom +?Street Fighter Collection Disc 1 +Super Real Mahjong P7 +Sword & Sorcery +Tenga Seiha +?Tengai Makyou Dai Yon no Mokujiroku - The Apocalypse IV +Toshinden Ura +The Yakuyaken Special +?Thunderforce Gold Pack 1 +Victory Boxing +Virtua Racing +Virtua Fighter Remix +Winter Heat +Wonder 3 +Worms +Yellow Brick Road +Zero4 Champ DooZy-J Type-R diff --git a/yabause/src/gtk/CMakeLists.txt b/yabause/src/gtk/CMakeLists.txt new file mode 100644 index 0000000000..c0d2e4354d --- /dev/null +++ b/yabause/src/gtk/CMakeLists.txt @@ -0,0 +1,65 @@ +project(yabause-gtk) + +yab_port_start() + +find_package(GTK2 2.10 COMPONENTS gtk) + +if (NOT GTK2_FOUND) + return() +endif (NOT GTK2_FOUND) + +set(PORT_INCLUDE_DIRS ${GTK2_INCLUDE_DIRS}) +set(PORT_LIBRARIES ${GTK2_LIBRARIES}) + +if (OPENGL_FOUND) + find_path(GDKGLEXT_CONFIG_INCLUDE_DIR gdkglext-config.h PATHS ${CMAKE_SYSTEM_PREFIX_PATH} PATH_SUFFIXES lib/gtkglext-1.0/include) + find_path(GTKGLEXT_INCLUDE_DIR gtk/gtkgl.h PATH_SUFFIXES gtkglext-1.0) + find_library(GDKGLEXT_LIBRARY gdkglext-x11-1.0) + find_library(GTKGLEXT_LIBRARY gtkglext-x11-1.0) + if (NOT GDKGLEXT_CONFIG_INCLUDE_DIR OR NOT GTKGLEXT_INCLUDE_DIR OR NOT GDKGLEXT_LIBRARY OR NOT GTKGLEXT_LIBRARY) + message(STATUS "Found OpenGL and Gtk+ but not libgtkglext, skipping Gtk+ port.") + return() + endif () + set(PORT_INCLUDE_DIRS ${PORT_INCLUDE_DIRS} ${GTKGLEXT_INCLUDE_DIR} ${GDKGLEXT_CONFIG_INCLUDE_DIR}) + set(PORT_LIBRARIES ${PORT_LIBRARIES} ${GTKGLEXT_LIBRARY} ${GDKGLEXT_LIBRARY}) + add_definitions(-DHAVE_LIBGTKGLEXT=1) +endif (OPENGL_FOUND) +include_directories(${PORT_INCLUDE_DIRS}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/include/gdk-pixbuf-2.0") + +set(yabause_gtk_SOURCES + gtk-compat.c + gtkglwidget.c + main.c + menu.c + pergtk.c + settings.c + yuicheckbutton.c + yuifileentry.c + yuiinputentry.c + yuim68k.c + yuimem.c + yuipage.c + yuirange.c + yuiresolution.c + yuiscreenshot.c + yuiscsp.c + yuiscudsp.c + yuish.c + yuitransfer.c + yuivdp1.c + yuivdp2.c + yuiviewer.c + yuiwindow.c) + +add_executable(yabause-gtk ${yabause_gtk_SOURCES}) +target_link_libraries(yabause-gtk yabause ${YABAUSE_LIBRARIES} ${PORT_LIBRARIES}) + +yab_port_success(yabause-gtk) +configure_file(yabause.desktop.in ${YAB_PORT_NAME}.desktop) + +install(TARGETS yabause-gtk DESTINATION "bin") +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${YAB_PORT_NAME}.desktop DESTINATION "share/applications") +install(FILES "doc/yabause.1" DESTINATION "${YAB_MAN_DIR}/man1" RENAME "${YAB_PORT_NAME}.1") +install(FILES "yabause.png" DESTINATION "share/pixmaps") diff --git a/yabause/src/gtk/Makefile.am b/yabause/src/gtk/Makefile.am new file mode 100644 index 0000000000..d1686ebd9f --- /dev/null +++ b/yabause/src/gtk/Makefile.am @@ -0,0 +1,31 @@ +SUBDIRS = doc +Applicationsdir = $(datadir)/applications +pixmapdir = $(datadir)/pixmaps +pixmap_DATA = yabause.png +bin_PROGRAMS = yabause +EXTRA_DIST = yabause.png + +yabause_SOURCES = gtkglwidget.c gtkglwidget.h main.c settings.c menu.c gtk-compat.c gtk-compat.h \ + yuifileentry.c yuifileentry.h yuirange.c yuirange.h yuiinputentry.c yuiinputentry.h yuipage.c yuipage.h \ + yuiresolution.c yuiresolution.h yuiwindow.c yuiwindow.h yuivdp1.c yuivdp1.h yuivdp2.c yuivdp2.h \ + yuiscsp.c yuiscsp.h pergtk.c pergtk.h yuiscreenshot.c yuiscreenshot.h \ + yuish.c yuish.h yuitransfer.c yuitransfer.h yuim68k.c yuim68k.h yuiscudsp.c yuiscudsp.h yuimem.c yuimem.h \ + yuiviewer.c yuiviewer.h settings.h \ + yuicheckbutton.c yuicheckbutton.h +yabause_CFLAGS = $(YAB_CFLAGS) +yabause_LDADD = ../libyabause.a $(YAB_LIBS) +yabause_CPPFLAGS = -DYTSDIR=\"$(datadir)/$(PACKAGE)/yts\" + +if ARCH_IS_LINUX +if USE_DYNAREC +if CPU_IS_X64 +yabause_CFLAGS += -DSH2_DYNAREC=1 +endif +if CPU_IS_X86 +yabause_CFLAGS += -DSH2_DYNAREC=1 +endif +if CPU_IS_ARM +yabause_CFLAGS += -DSH2_DYNAREC=1 -mcpu=cortex-a8 -mfpu=vfp -mfloat-abi=softfp +endif +endif +endif diff --git a/yabause/src/gtk/doc/Makefile.am b/yabause/src/gtk/doc/Makefile.am new file mode 100644 index 0000000000..327b7de40f --- /dev/null +++ b/yabause/src/gtk/doc/Makefile.am @@ -0,0 +1 @@ +dist_man_MANS = yabause.1 diff --git a/yabause/src/gtk/doc/yabause.1 b/yabause/src/gtk/doc/yabause.1 new file mode 100644 index 0000000000..6d0c781e9d --- /dev/null +++ b/yabause/src/gtk/doc/yabause.1 @@ -0,0 +1,93 @@ +.TH YABAUSE 1 "April 16, 2010" "yabause-0.9.11" +.SH NAME +yabause \- Yet Another Buggy And Uncomplete Saturn Emulator +.SH SYNOPSIS +.B yabause +[ \fB\-afh\fP ] [ \fB\-ns\fP ] [ \fB\-b \fP ] [ \fB\-c \fP ] [ \fB\-i \fP ] +[ \fB\-\-binary=[:

]\fP ] +.SH DESCRIPTION +\fBYabause\fP is a Sega Saturn emulator. \fBYabause\fP needs either a bios file, a game or a binary to run. +Games can be loaded from a real cd device or from dump files. +.SH OPTIONS +.TP +.BI \-a +.TP +.BI \-\-autostart +Automatically start emulation. +.TP +.BI \-\-autoframeskip=0|1 +Enable or disable auto frame skipping / limiting. +.TP +.BI \-\-autoload +Automatically start emulation and load a save state. +.TP +.BI \-b +.TP +.BI \-\-bios +Choose a bios file. +.TP +.BI \-\-binary +Use a binary file. Content of the file will be loaded to 0x06004000 and execution will start from that address. +You can provide an alternative address to load and run the binary with the \-\-binary=:
syntax. +When using this option, emulation is automatically started and you shouldn't use it in cunjunction with \-a. +This option is intended for homebrew developers wanting to test their programs in \fBYabause\fP. +.TP +.BI \-c +.TP +.BI \-\-cdrom +Choose the cdrom device. +.TP +.BI \-f +.TP +.BI \-\-fullscreen +Start the emulator in fullscreen. +.TP +.BI \-h +Display a short help text. +.TP +.BI \-i +.TP +.BI \-\-iso +Choose a dump file. The dump can be either in iso or bin/cue file format. +.TP +.BI \-ns +.TP +.BI \-\-nosound +Turns sound off. This option actually set the sound core to the dummy one. +.SH FILES +.TP +\fB\fR +An executable for the Saturn, usually with a BIN extension. +.TP +\fB\fR +A Saturn ROM BIOS image. +.TP +\fB\fR +A CDROM device file. +.TP +\fB\fR +A Saturn game dump. +.SH AUTHORS +Copyright (c) 2002-2010 Yabause Team + +Web: http://yabause.org + +Please don't ask for roms, bios files or any other +copyrighted stuff. +.SH COPYRIGHT +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +This program is distributed in the hope that it will be +useful,but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301 USA + +See the GNU General Public License details in COPYING. diff --git a/yabause/src/gtk/gtk-compat.c b/yabause/src/gtk/gtk-compat.c new file mode 100644 index 0000000000..3fa155c3a7 --- /dev/null +++ b/yabause/src/gtk/gtk-compat.c @@ -0,0 +1,33 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "gtk-compat.h" + +#if !GLIB_CHECK_VERSION(2, 8, 0) +gboolean g_file_set_contents(const gchar * filename, const gchar * contents, gssize len, GError ** error) { + FILE * file = fopen(filename, "w"); + + if (len == -1) + fprintf(file, "%s", contents); + else + fwrite(contents, 1, len, file); + + fclose(file); +} +#endif diff --git a/yabause/src/gtk/gtk-compat.h b/yabause/src/gtk/gtk-compat.h new file mode 100644 index 0000000000..f18a74c156 --- /dev/null +++ b/yabause/src/gtk/gtk-compat.h @@ -0,0 +1,29 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_GTK_COMPAT_H +#define YUI_GTK_COMPAT_H + +#include + +#if ((GLIB_MAJOR_VERSION < 2) || ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 8))) +gboolean g_file_set_contents(const gchar *, const gchar *, gssize, GError **); +#endif + +#endif diff --git a/yabause/src/gtk/gtkglwidget.c b/yabause/src/gtk/gtkglwidget.c new file mode 100644 index 0000000000..85cdc2db02 --- /dev/null +++ b/yabause/src/gtk/gtkglwidget.c @@ -0,0 +1,318 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "gtkglwidget.h" +#ifdef HAVE_LIBGTKGLEXT +#include +#endif +#include "../vidsoft.h" +#include "../peripheral.h" + +#define X_NOSCALE 160 +#define Y_NOSCALE 120 + +static void yui_gl_class_init (YuiGlClass * klass); +static void yui_gl_init (YuiGl * yfe); +static gboolean yui_gl_resize (GtkWidget *w,GdkEventConfigure *event, gpointer data); + +void yui_gl_draw(YuiGl * glxarea) { +#ifdef HAVE_LIBGTKGLEXT + GdkGLContext *glcontext = gtk_widget_get_gl_context (GTK_WIDGET(glxarea)); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (GTK_WIDGET(glxarea)); + + if (!gdk_gl_drawable_make_current (gldrawable, glcontext)) { + g_print("Cannot set gl drawable current\n"); + return; + } + + gdk_gl_drawable_swap_buffers(gldrawable); +#else + int buf_width, buf_height; + GdkPixbuf * pixbuf, * scaledpixbuf; + + VIDCore->GetGlSize( &buf_width, &buf_height ); + glxarea->pixels_width = GTK_WIDGET(glxarea)->allocation.width; + glxarea->pixels_height = GTK_WIDGET(glxarea)->allocation.height; + glxarea->pixels_rowstride = glxarea->pixels_width * 4; + glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; + + if (dispbuffer == NULL) return; + + pixbuf = gdk_pixbuf_new_from_data((const guchar *) dispbuffer, GDK_COLORSPACE_RGB, TRUE, 8, + buf_width, buf_height, buf_width*4, NULL, NULL); + + if (( glxarea->pixels_width < buf_width + X_NOSCALE )&&( glxarea->pixels_height < buf_height + Y_NOSCALE )) { + + gdk_draw_pixbuf(GTK_WIDGET(glxarea)->window, NULL, pixbuf, 0, 0, + (glxarea->pixels_width-buf_width)/2, (glxarea->pixels_height-buf_height)/2, + buf_width, buf_height, GDK_RGB_DITHER_NONE, 0, 0); + } else { + + scaledpixbuf = gdk_pixbuf_scale_simple(pixbuf, + glxarea->pixels_width, glxarea->pixels_height, GDK_INTERP_NEAREST ); + gdk_draw_pixbuf(GTK_WIDGET(glxarea)->window, NULL, + scaledpixbuf, 0, 0, 0, 0, glxarea->pixels_width, glxarea->pixels_height, + GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref(scaledpixbuf); + } + g_object_unref(pixbuf); +#endif + glxarea->is_init = 1; +} + +void yui_gl_draw_pause(YuiGl * glxarea) { +#ifdef HAVE_LIBGTKGLEXT + if (glxarea->pixels) { + /* The "correct" raster position would be (0, height) but it's not a + * valid position, so I have to use this hack... found here: + * http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/ */ + glRasterPos2i(0, 0); + glBitmap(0, 0, 0, 0, 0, - glxarea->pixels_height, NULL); + glPixelZoom(1, 1); + glDrawPixels(glxarea->pixels_width, glxarea->pixels_height, GL_RGB, GL_UNSIGNED_BYTE, glxarea->pixels); + yui_gl_draw(glxarea); + } else { + gdk_draw_rectangle(GTK_WIDGET(glxarea)->window, GTK_WIDGET(glxarea)->style->bg_gc[GTK_WIDGET_STATE (glxarea)], + TRUE, 0, 0, GTK_WIDGET(glxarea)->allocation.width, GTK_WIDGET(glxarea)->allocation.height); + } +#else + if (dispbuffer) + yui_gl_draw(glxarea); +#endif +} + +static gboolean yui_gl_resize(GtkWidget *w,GdkEventConfigure *event, gpointer data) { +#ifdef HAVE_LIBGTKGLEXT + GdkGLContext *glcontext = gtk_widget_get_gl_context (w); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (w); + + if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) + return FALSE; + + glViewport(0, 0, event->width, event->height); + if ( YUI_GL(w)->is_init ) VIDCore->Resize(event->width, event->height, FALSE ); +#endif + return FALSE; +} + +int beforehiding = 0; + +static gboolean gonna_hide(gpointer data) { + beforehiding--; + + if (beforehiding == 0) { + static char source_data[] = { 0 }; + static char mask_data[] = { 0 }; + + GdkCursor *cursor; + GdkPixmap *source, *mask; + GdkColor fg = { 0, 65535, 65535, 65535 }; + GdkColor bg = { 0, 0, 0, 0 }; + + source = gdk_bitmap_create_from_data(NULL, source_data, 1, 1); + mask = gdk_bitmap_create_from_data(NULL, mask_data, 1, 1); + cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 1, 1); + gdk_pixmap_unref(source); + gdk_pixmap_unref(mask); + + gdk_window_set_cursor(GTK_WIDGET(data)->window, cursor); + + return FALSE; + } else { + return TRUE; + } +} + +extern void * padbits; +extern GKeyFile * keyfile; +int oldx = 0; +int oldy = 0; + +static gboolean yui_gl_hide_cursor(GtkWidget * widget, GdkEventMotion * event, gpointer user_data) { + if (PerGetId(padbits) == PERMOUSE) { + int x = event->x; + int y = event->y; + double speed = g_key_file_get_double(keyfile, "General", "MouseSpeed", NULL); + + PerMouseMove(padbits, speed * (x - oldx), -speed * (y - oldy)); + oldx = x; + oldy = y; + } + + if (beforehiding == 0) { + gdk_window_set_cursor(widget->window, NULL); + g_timeout_add(1000, gonna_hide, widget); + } + + beforehiding = 2; + + return FALSE; +} + +static gboolean yui_gl_button_press(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { + if (PerGetId(padbits) == PERMOUSE) { + switch(event->button) { + case 1: + PerMouseLeftPressed(padbits); + break; + case 2: + PerMouseMiddlePressed(padbits); + break; + case 3: + PerMouseRightPressed(padbits); + break; + } + } + return FALSE; +} + +static gboolean yui_gl_button_release(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { + if (PerGetId(padbits) == PERMOUSE) { + switch(event->button) { + case 1: + PerMouseLeftReleased(padbits); + break; + case 2: + PerMouseMiddleReleased(padbits); + break; + case 3: + PerMouseRightReleased(padbits); + break; + } + } + return FALSE; +} + +GtkWidget * yui_gl_new(void) { + GtkWidget * drawingArea; +#ifdef HAVE_LIBGTKGLEXT + int attribs[] = { + GDK_GL_RGBA, + GDK_GL_RED_SIZE, 1, + GDK_GL_GREEN_SIZE, 1, + GDK_GL_BLUE_SIZE, 1, + + GDK_GL_DOUBLEBUFFER, + + GDK_GL_DEPTH_SIZE ,1, + GDK_GL_STENCIL_SIZE ,8, + GDK_GL_ATTRIB_LIST_NONE + }; +#endif + + drawingArea = GTK_WIDGET(g_object_new(yui_gl_get_type(), NULL)); + YUI_GL(drawingArea)->is_init = 0; + +#ifdef HAVE_LIBGTKGLEXT + gtk_widget_set_gl_capability(drawingArea, gdk_gl_config_new(attribs), NULL, TRUE, GDK_GL_RGBA_TYPE); +#endif + + g_signal_connect (GTK_OBJECT(drawingArea),"configure_event", GTK_SIGNAL_FUNC(yui_gl_resize),0); + + gtk_widget_set_events(drawingArea, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + g_signal_connect(GTK_OBJECT(drawingArea), "motion-notify-event", GTK_SIGNAL_FUNC(yui_gl_hide_cursor),0); + g_signal_connect(GTK_OBJECT(drawingArea), "button-press-event", GTK_SIGNAL_FUNC(yui_gl_button_press),0); + g_signal_connect(GTK_OBJECT(drawingArea), "button-release-event", GTK_SIGNAL_FUNC(yui_gl_button_release),0); + + return drawingArea; +} + +void yui_gl_dump_screen(YuiGl * glxarea) { +#ifdef HAVE_LIBGTKGLEXT + int size; + + glxarea->pixels_width = GTK_WIDGET(glxarea)->allocation.width; + glxarea->pixels_height = GTK_WIDGET(glxarea)->allocation.height; + glxarea->pixels_rowstride = glxarea->pixels_width * 3; + glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; + + size = glxarea->pixels_rowstride * glxarea->pixels_height; + + if (glxarea->pixels) free(glxarea->pixels); + glxarea->pixels = malloc(sizeof(GLubyte) * size); + if (glxarea->pixels == NULL) return; + + glReadPixels(0, 0, glxarea->pixels_width, glxarea->pixels_height, GL_RGB, GL_UNSIGNED_BYTE, glxarea->pixels); +#else + int buf_width, buf_height; + int i, j; + int size; + int cur = 0; + u8 * pixels; + u8 * buffer; + + VIDCore->GetGlSize( &buf_width, &buf_height ); + size = buf_width * buf_height * 3; + + glxarea->pixels_width = buf_width; + glxarea->pixels_height = buf_height; + glxarea->pixels_rowstride = glxarea->pixels_width * 3; + glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; + + if (! glxarea->pixels) glxarea->pixels = malloc(sizeof(u8) * size); + + pixels = (u8 *)glxarea->pixels; + pixels += size - (buf_width * 3); + buffer = (u8 *)dispbuffer; + + for(i = 0;i < buf_height;i++) { + for(j = 0;j < buf_width;j++) { + *pixels++ = buffer[cur]; + *pixels++ = buffer[cur + 1]; + *pixels++ = buffer[cur + 2]; + cur += 4; + } + pixels -= buf_width * 6; + } +#endif +} + +GType yui_gl_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiGlClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_gl_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiGl), + 0, + (GInstanceInitFunc) yui_gl_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "YuiGl", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_gl_class_init (UNUSED YuiGlClass * klass) { +} + +static void yui_gl_init (YuiGl * y) { + y->pixels = NULL; +} diff --git a/yabause/src/gtk/gtkglwidget.h b/yabause/src/gtk/gtkglwidget.h new file mode 100644 index 0000000000..481b1844fa --- /dev/null +++ b/yabause/src/gtk/gtkglwidget.h @@ -0,0 +1,67 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_GL_H +#define YUI_GL_H + +#include + +#ifdef HAVE_LIBGTKGLEXT +#include +#include +#endif + +G_BEGIN_DECLS + +#define YUI_GL_TYPE (yui_gl_get_type ()) +#define YUI_GL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_GL_TYPE, YuiGl)) +#define YUI_GL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_GL_TYPE, YuiGlClass)) +#define IS_YUI_GL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_GL_TYPE)) +#define IS_YUI_GL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_GL_TYPE)) + +typedef struct _YuiGl YuiGl; +typedef struct _YuiGlClass YuiGlClass; + +struct _YuiGl +{ + GtkDrawingArea hbox; + + guint * pixels; + gint pixels_width; + gint pixels_height; + gint pixels_rowstride; + gint is_init; +}; + +struct _YuiGlClass +{ + GtkDrawingAreaClass parent_class; +}; + +GType yui_gl_get_type (void); +GtkWidget * yui_gl_new (void); + +void yui_gl_draw (YuiGl *); +void yui_gl_draw_pause (YuiGl *); +void yui_gl_dump_screen (YuiGl *); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/main.c b/yabause/src/gtk/main.c new file mode 100644 index 0000000000..3749abfa84 --- /dev/null +++ b/yabause/src/gtk/main.c @@ -0,0 +1,624 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Fabien Coulon + Copyright 2005 Joost Peters + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "gtkglwidget.h" +#include "yuiwindow.h" + +#include "../yabause.h" +#include "../yui.h" +#include "../peripheral.h" +#include "../sh2core.h" +#include "../sh2int.h" +#include "../vidogl.h" +#include "../vidsoft.h" +#include "../cs0.h" +#include "../cs2.h" +#include "../cdbase.h" +#include "../scsp.h" +#include "../sndsdl.h" +#include "../sndal.h" +#include "../persdljoy.h" +#ifdef ARCH_IS_LINUX +#include "../perlinuxjoy.h" +#endif +#include "../debug.h" +#include "../m68kcore.h" +#include "../m68kc68k.h" +#include "pergtk.h" +#include "../psp/psp-sh2.h" + +#include "settings.h" + +static char biospath[256] = "\0"; +static char cdpath[256] = "\0"; +static char buppath[256] = "\0"; +static char mpegpath[256] = "\0"; +static char cartpath[256] = "\0"; + +M68K_struct * M68KCoreList[] = { +&M68KDummy, +#ifdef HAVE_C68K +&M68KC68K, +#endif +#ifdef HAVE_Q68 +&M68KQ68, +#endif +NULL +}; + +SH2Interface_struct *SH2CoreList[] = { +&SH2Interpreter, +&SH2DebugInterpreter, +#ifdef TEST_PSP_SH2 +&SH2PSP, +#endif +#ifdef SH2_DYNAREC +&SH2Dynarec, +#endif +NULL +}; + +PerInterface_struct *PERCoreList[] = { +&PERDummy, +&PERGTK, +#ifdef HAVE_LIBSDL +&PERSDLJoy, +#endif +#ifdef ARCH_IS_LINUX +&PERLinuxJoy, +#endif +NULL +}; + +CDInterface *CDCoreList[] = { +&DummyCD, +&ISOCD, +#ifndef UNKNOWN_ARCH +&ArchCD, +#endif +NULL +}; + +SoundInterface_struct *SNDCoreList[] = { +&SNDDummy, +#ifdef HAVE_LIBSDL +&SNDSDL, +#endif +#ifdef HAVE_LIBAL +&SNDAL, +#endif +NULL +}; + +VideoInterface_struct *VIDCoreList[] = { +&VIDDummy, +#ifdef HAVE_LIBGTKGLEXT +&VIDOGL, +#endif +&VIDSoft, +NULL +}; + +#ifdef YAB_PORT_OSD +OSD_struct *OSDCoreList[] = { +&OSDDummy, +#ifdef HAVE_LIBGLUT +&OSDGlut, +#endif +&OSDSoft, +NULL +}; +#endif + +GtkWidget * yui; +GKeyFile * keyfile; +yabauseinit_struct yinit; + +static int yui_main(gpointer data) { + PERCore->HandleEvents(); + return TRUE; +} + +static GtkWidget * yui_new(void) { + yui = yui_window_new(NULL, G_CALLBACK(YabauseInit), &yinit, yui_main, G_CALLBACK(YabauseReset)); + + gtk_widget_show(yui); + + return yui; +} + +static void yui_settings_init(void) { + yinit.m68kcoretype = M68KCORE_C68K; + yinit.percoretype = PERCORE_GTK; + yinit.sh2coretype = SH2CORE_DEFAULT; +#ifdef HAVE_LIBGTKGLEXT + yinit.vidcoretype = VIDCORE_OGL; +#else + yinit.vidcoretype = VIDCORE_SOFT; +#endif + yinit.sndcoretype = SNDCORE_DUMMY; + yinit.cdcoretype = CDCORE_DEFAULT; + yinit.carttype = CART_NONE; + yinit.regionid = 0; + yinit.biospath = biospath; + yinit.cdpath = cdpath; + yinit.buppath = buppath; + yinit.mpegpath = mpegpath; + yinit.cartpath = cartpath; + yinit.videoformattype = VIDEOFORMATTYPE_NTSC; + yinit.osdcoretype = OSDCORE_DEFAULT; +} + +gchar * inifile; + +static int safe_strcmp(const char * s1, const char * s2) { + if (s1) { + if (s2) { + return strcmp(s1, s2); + } else { + return 1; + } + } else { + if (s2) { + return -1; + } else { + return 0; + } + } +} + +extern void * padbits; +void * padbits; + +static gboolean yui_settings_load(void) { + int i, tmp; + long tmptime; + gchar * stmp; + gboolean mustRestart = FALSE; + + g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); + + /* bios */ + stmp = g_key_file_get_value(keyfile, "General", "BiosPath", 0); + if (stmp && !*stmp) stmp = NULL; + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.biospath)) { + mustRestart = TRUE; + } + if (stmp) { + g_strlcpy(biospath, stmp, 256); + yinit.biospath = biospath; + } + else yinit.biospath = NULL; + + /* cd core */ + stmp = g_key_file_get_value(keyfile, "General", "CDROMDrive", 0); + if (stmp && !*stmp) stmp = NULL; + if((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.cdpath)) { + Cs2ChangeCDCore(yinit.cdcoretype, stmp); + } + if (stmp) g_strlcpy(cdpath, stmp, 256); + + tmp = yinit.cdcoretype; + yinit.cdcoretype = g_key_file_get_integer(keyfile, "General", "CDROMCore", 0); + if((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.cdcoretype)) { + Cs2ChangeCDCore(yinit.cdcoretype, yinit.cdpath); + } + + /* region */ + { + char * region = g_key_file_get_value(keyfile, "General", "Region", 0); + tmp = yinit.regionid; + if ((region == 0) || !strcmp(region, "Auto")) { + yinit.regionid = 0; + } else { + switch(region[0]) { + case 'J': yinit.regionid = 1; break; + case 'T': yinit.regionid = 2; break; + case 'U': yinit.regionid = 4; break; + case 'B': yinit.regionid = 5; break; + case 'K': yinit.regionid = 6; break; + case 'A': yinit.regionid = 0xA; break; + case 'E': yinit.regionid = 0xC; break; + case 'L': yinit.regionid = 0xD; break; + } + } + + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.regionid)) { + mustRestart = TRUE; + } + } + + /* cart */ + stmp = g_key_file_get_value(keyfile, "General", "CartPath", 0); + if (stmp && !*stmp) stmp = NULL; + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.cartpath)) { + mustRestart = TRUE; + } + if (stmp) { + g_strlcpy(cartpath, stmp, 256); + yinit.cartpath = cartpath; + } + else yinit.cartpath = NULL; + + tmp = yinit.carttype; + yinit.carttype = g_key_file_get_integer(keyfile, "General", "CartType", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.carttype)) { + mustRestart = TRUE; + } + + /* backup ram */ + stmp = g_key_file_get_value(keyfile, "General", "BackupRamPath", 0); + if (stmp && !*stmp) stmp = NULL; + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.buppath)) { + mustRestart = TRUE; + } + if (stmp) { + g_strlcpy(buppath, stmp, 256); + yinit.buppath = buppath; + } + else yinit.buppath = NULL; + + /* mpeg rom */ + stmp = g_key_file_get_value(keyfile, "General", "MpegRomPath", 0); + if (stmp && !*stmp) stmp = NULL; + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.mpegpath)) { + mustRestart = TRUE; + } + if (stmp) { + g_strlcpy(mpegpath, stmp, 256); + yinit.mpegpath = mpegpath; + } + else yinit.mpegpath = NULL; + + /* sh2 */ + tmp = yinit.sh2coretype; + yinit.sh2coretype = g_key_file_get_integer(keyfile, "General", "SH2Int", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.sh2coretype)) { + mustRestart = TRUE; + } + + /* m68k */ + tmp = yinit.m68kcoretype; + yinit.m68kcoretype = g_key_file_get_integer(keyfile, "General", "M68kInt", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.m68kcoretype)) { + mustRestart = TRUE; + } + + /* video core */ + tmp = yinit.vidcoretype; + yinit.vidcoretype = g_key_file_get_integer(keyfile, "General", "VideoCore", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.vidcoretype)) { + VideoChangeCore(yinit.vidcoretype); + VIDCore->Resize( + GTK_WIDGET(YUI_WINDOW(yui)->area)->allocation.width, + GTK_WIDGET(YUI_WINDOW(yui)->area)->allocation.height, + FALSE); + } + + /* osd core */ + tmp = yinit.osdcoretype; + yinit.osdcoretype = g_key_file_get_integer(keyfile, "General", "OSDCore", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.osdcoretype)) { + OSDChangeCore(yinit.osdcoretype); + } + + /* sound core */ + tmp = yinit.sndcoretype; + yinit.sndcoretype = g_key_file_get_integer(keyfile, "General", "SoundCore", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.sndcoretype)) { + ScspChangeSoundCore(yinit.sndcoretype); + } + + ScspSetVolume(g_key_file_get_integer(keyfile, "General", "Volume", NULL)); + + /* peripheral core */ + yinit.percoretype = g_key_file_get_integer(keyfile, "General", "PerCore", 0); + + /* audio sync */ + tmp = g_key_file_get_boolean(keyfile, "General", "AudioSync", 0); + ScspSetFrameAccurate(tmp); + + /* clock sync */ + tmp = yinit.clocksync; + yinit.clocksync = g_key_file_get_boolean(keyfile, "General", "ClockSync", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.clocksync)) { + mustRestart = TRUE; + } + tmptime = yinit.basetime; + tmp = g_key_file_get_boolean(keyfile, "General", "FixedBaseTime", 0); + if (tmp && yinit.clocksync) { + /* Find timestamp of 1998-01-01 12:00 in the local time zone */ + time_t utc = 883656000; // 1998-01-01 12:00 UTC + struct tm tm; + long local, noon; + localtime_r(&utc, &tm); + local = tm.tm_hour*3600 + tm.tm_min*60 + tm.tm_sec; + if (tm.tm_mday == 2) // 1998-01-02 + local += 86400; + else if (tm.tm_mday == 31) // 1997-12-31 + local -= 86400; + noon = 12*3600 + 0*60 + 0; + yinit.basetime = (long)utc + (noon - local); + } else { + yinit.basetime = 0; + } + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmptime != yinit.basetime)) { + mustRestart = TRUE; + } + + /* threads */ + tmp = g_key_file_get_boolean(keyfile, "General", "UseThreads", 0); + if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.usethreads)) { + mustRestart = TRUE; + } + yinit.usethreads = tmp; + + PerInit(yinit.percoretype); + + PerPortReset(); + switch(g_key_file_get_integer(keyfile, "General", "PerType", NULL)) + { + case PERMOUSE: + padbits = PerMouseAdd(&PORTDATA1); + i = 0; + + while(PerMouseNames[i]) { + char tmp[100]; + u32 key; + sprintf(tmp, "Mouse.%s.1", PerMouseNames[i]); + key = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); + PerSetKey(key, i + 13, padbits); + i++; + } + break; + case PERPAD: + default: + padbits = PerPadAdd(&PORTDATA1); + i = 0; + + while(PerPadNames[i]) { + char tmp[100]; + u32 key; + sprintf(tmp, "Pad.%s.1", PerPadNames[i]); + key = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); + PerSetKey(key, i, padbits); + i++; + } + } + + yui_resize(g_key_file_get_integer(keyfile, "General", "Width", 0), + g_key_file_get_integer(keyfile, "General", "Height", 0), + g_key_file_get_integer(keyfile, "General", "Fullscreen", 0)); + + yinit.videoformattype = g_key_file_get_integer(keyfile, "General", "VideoFormat", 0); + + yui_window_set_frameskip(YUI_WINDOW(yui), g_key_file_get_integer(keyfile, "General", "Frameskip", NULL)); + + return mustRestart; +} + +void YuiErrorMsg(const char * string) { + GtkWidget* warningDlg = gtk_message_dialog_new (GTK_WINDOW(yui), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, string, NULL); + gtk_dialog_run (GTK_DIALOG(warningDlg)); + gtk_widget_destroy (warningDlg); +} + +int main(int argc, char *argv[]) { +#ifndef NO_CLI + int i; +#endif + LogStart(); + LogChangeOutput( DEBUG_STDERR, NULL ); + inifile = g_build_filename(g_get_user_config_dir(), "yabause", "gtk", "yabause.ini", NULL); + + if (! g_file_test(inifile, G_FILE_TEST_EXISTS)) { + // no inifile found, but it could be in the old location + gchar * oldinifile = g_build_filename(g_get_user_config_dir(), "yabause.ini", NULL); + + // we need to create the directory for the new file anyways + gchar * xdgpath = g_build_filename(g_get_user_config_dir(), "yabause", "gtk", NULL); + + if (! g_file_test(xdgpath, G_FILE_TEST_EXISTS)) + g_mkdir_with_parents(xdgpath, 0755); + g_free(xdgpath); + + if (g_file_test(oldinifile, G_FILE_TEST_EXISTS)) { + // ok, we found an old .ini file, let's copy the content + gchar * data; + + g_file_get_contents(oldinifile, &data, NULL, NULL); + g_file_set_contents(inifile, data, -1, NULL); + } + + g_free(oldinifile); + } + + keyfile = g_key_file_new(); + + gtk_init(&argc, &argv); +#ifdef HAVE_LIBGTKGLEXT + gtk_gl_init(&argc, &argv); +#endif + + yui_settings_init(); + +#ifdef HAVE_LIBMINI18N + mini18n_set_domain(YTSDIR); + g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); + mini18n_set_locale(g_key_file_get_value(keyfile, "General", "TranslationPath", NULL)); +#endif + + yui = yui_new(); + + yui_settings_load(); + + gtk_window_move(GTK_WINDOW(yui), g_key_file_get_integer(keyfile, "Gtk", "X", 0), g_key_file_get_integer(keyfile, "Gtk", "Y", 0)); + +#ifndef NO_CLI + //handle command line arguments + for (i = 1; i < argc; ++i) { + if (argv[i]) { + //show usage + if (0 == strcmp(argv[i], "-h") || 0 == strcmp(argv[i], "-?") || 0 == strcmp(argv[i], "--help")) { + print_usage(argv[0]); + return 0; + } + + //set bios + if (0 == strcmp(argv[i], "-b") && argv[i + 1]) { + g_strlcpy(biospath, argv[i + 1], 256); + yinit.biospath = biospath; + } else if (strstr(argv[i], "--bios=")) { + g_strlcpy(biospath, argv[i] + strlen("--bios="), 256); + yinit.biospath = biospath; + } + //set iso + else if (0 == strcmp(argv[i], "-i") && argv[i + 1]) { + g_strlcpy(cdpath, argv[i + 1], 256); + yinit.cdcoretype = 1; + } else if (strstr(argv[i], "--iso=")) { + g_strlcpy(cdpath, argv[i] + strlen("--iso="), 256); + yinit.cdcoretype = 1; + } + //set cdrom + else if (0 == strcmp(argv[i], "-c") && argv[i + 1]) { + g_strlcpy(cdpath, argv[i + 1], 256); + yinit.cdcoretype = 2; + } else if (strstr(argv[i], "--cdrom=")) { + g_strlcpy(cdpath, argv[i] + strlen("--cdrom="), 256); + yinit.cdcoretype = 2; + } + // Set sound + else if (strcmp(argv[i], "-ns") == 0 || strcmp(argv[i], "--nosound") == 0) { + yinit.sndcoretype = 0; + } + // Autoload + else if (strcmp(argv[i], "--autoload") == 0) { + yui_window_start(YUI_WINDOW(yui)); + YuiLoadState(); + yui_window_run(YUI_WINDOW(yui)); + } + // Autostart + else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--autostart") == 0) { + yui_window_run(YUI_WINDOW(yui)); + } + // Fullscreen + else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) { + yui_window_set_fullscreen(YUI_WINDOW(yui), TRUE); + } + // Auto frame skip + else if (strstr(argv[i], "--autoframeskip=")) { + int fscount; + int fsenable; + fscount = sscanf(argv[i] + strlen("--autoframeskip="), "%d", &fsenable); + if (fscount > 0) + yui_window_set_frameskip(YUI_WINDOW(yui), fsenable); + } + // Binary + else if (strstr(argv[i], "--binary=")) { + char binname[1024]; + unsigned int binaddress; + int bincount; + + bincount = sscanf(argv[i] + strlen("--binary="), "%[^:]:%x", binname, &binaddress); + if (bincount > 0) { + if (bincount < 2) binaddress = 0x06004000; + + yui_window_run(YUI_WINDOW(yui)); + MappedMemoryLoadExec(binname, binaddress); + } + } + } + } +#endif + + gtk_main (); + + YabauseDeInit(); + LogStop(); + + return 0; +} + +void YuiSetVideoAttribute(int type, int val) { +} + +int YuiSetVideoMode(int width, int height, int bpp, int fullscreen) { + return 0; +} + +void YuiSwapBuffers(void) { + yui_window_update(YUI_WINDOW(yui)); +} + +void yui_conf(void) { + gint result; + GtkWidget * dialog; + + dialog = create_dialog1(); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + switch(result) { + case GTK_RESPONSE_OK: + { + gboolean mustRestart; + g_file_set_contents(inifile, g_key_file_to_data(keyfile, 0, 0), -1, 0); + mustRestart = yui_settings_load(); + if (mustRestart) { + GtkWidget* warningDlg = gtk_message_dialog_new (GTK_WINDOW(yui), + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + "You must restart Yabause before the changes take effect."); + + gtk_dialog_run (GTK_DIALOG(warningDlg)); + gtk_widget_destroy (warningDlg); + } + break; + } + case GTK_RESPONSE_CANCEL: + g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); + break; + } +} + +void yui_resize(guint width, guint height, gboolean fullscreen) { + if (width <= 0) width = 320; + if (height <= 0) height = 224; + + if (g_key_file_get_integer(keyfile, "General", "KeepRatio", 0)) + { + GdkGeometry geometry; + geometry.min_aspect = (float) width / height; + geometry.max_aspect = (float) width / height; + gtk_window_set_geometry_hints(GTK_WINDOW(yui), YUI_WINDOW(yui)->area, &geometry, GDK_HINT_ASPECT); + } + else + { + gtk_window_set_geometry_hints(GTK_WINDOW(yui), YUI_WINDOW(yui)->area, NULL, 0); + } + + gtk_window_resize(GTK_WINDOW(yui), width, height + YUI_WINDOW(yui)->menu->allocation.height); + + yui_window_set_fullscreen(YUI_WINDOW(yui), fullscreen); +} diff --git a/yabause/src/gtk/menu.c b/yabause/src/gtk/menu.c new file mode 100644 index 0000000000..8ecd9cffbf --- /dev/null +++ b/yabause/src/gtk/menu.c @@ -0,0 +1,218 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "settings.h" +#include "yuiwindow.h" +#include "yuivdp1.h" +#include "yuivdp2.h" +#include "yuish.h" +#include "yuitransfer.h" +#include "yuim68k.h" +#include "yuiscudsp.h" +#include "yuiscsp.h" +#include "yuimem.h" +#include "yuiscreenshot.h" + +static void openAboutDialog(GtkWidget * w, gpointer data) { + gtk_show_about_dialog(data, + "name", "Yabause", + "version", VERSION, + "website", "http://yabause.org", + NULL); +} + +void YuiSaveState(void) { + char * dir = g_key_file_get_value(keyfile, "General", "StatePath", NULL); + + YabSaveStateSlot(dir, 1); +} + +void YuiLoadState(void) { + char * dir = g_key_file_get_value(keyfile, "General", "StatePath", NULL); + + YabLoadStateSlot(dir, 1); +} + +GtkWidget* create_menu(YuiWindow * window1) { + GtkWidget *menubar1; + GtkWidget *menuitem1; + GtkWidget *menuitem1_menu; + GtkWidget *new1; + GtkWidget *view1; + GtkWidget *view1_menu; + GtkWidget *fps1; + GtkWidget *frameLimiter; + GtkWidget *layer1; + GtkWidget *layer1_menu; + GtkWidget *log; + GtkWidget *menuitem3; + GtkWidget *menuitem3_menu; + GtkWidget *msh; + GtkWidget *ssh; + GtkWidget *vdp2; + GtkWidget *vdp1; + GtkWidget *m68k; + GtkWidget *scudsp; + GtkWidget *scsp; + GtkWidget *menuitem4; + GtkWidget *menuitem4_menu; + GtkWidget *about1; + GtkWidget *transfer; + GtkWidget *memory; + GtkWidget *screenshot; + + menubar1 = gtk_menu_bar_new (); + + menuitem1 = gtk_menu_item_new_with_mnemonic ("_Yabause"); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem1); + + menuitem1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem1), menuitem1_menu); + + new1 = gtk_image_menu_item_new_from_stock("gtk-preferences", NULL); + g_signal_connect(new1, "activate", yui_conf, 0); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), new1); + + gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "run"))); + gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "pause"))); + gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "reset"))); + + transfer = gtk_menu_item_new_with_mnemonic (_("Transfer")); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), transfer); + g_signal_connect_swapped(transfer, "activate", G_CALLBACK(yui_transfer_new), window1); + + screenshot = gtk_menu_item_new_with_mnemonic (_("Screenshot")); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), screenshot); + g_signal_connect_swapped(screenshot, "activate", G_CALLBACK(yui_screenshot_new), window1); + + frameLimiter = gtk_check_menu_item_new_with_mnemonic (_("Frame Skip/Limiter")); + { + GtkAction * action = gtk_action_group_get_action(window1->action_group, "frameskip"); + gtk_action_connect_proxy(action, frameLimiter); + } + gtk_container_add (GTK_CONTAINER (menuitem1_menu), frameLimiter); + + { + GtkWidget * savestate_menu; + GtkWidget * savestate; + GtkWidget * savestate_save; + GtkWidget * savestate_load; + + savestate = gtk_menu_item_new_with_mnemonic(_("Save State")); + gtk_container_add(GTK_CONTAINER(menuitem1_menu), savestate); + + savestate_menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(savestate), savestate_menu); + + savestate_save = gtk_menu_item_new_with_mnemonic(_("Save")); + gtk_container_add(GTK_CONTAINER(savestate_menu), savestate_save); + g_signal_connect_swapped(savestate_save, "activate", G_CALLBACK(YuiSaveState), NULL); + + savestate_load = gtk_menu_item_new_with_mnemonic(_("Load")); + gtk_container_add(GTK_CONTAINER(savestate_menu), savestate_load); + g_signal_connect_swapped(savestate_load, "activate", G_CALLBACK(YuiLoadState), NULL); + } + + gtk_container_add (GTK_CONTAINER (menuitem1_menu), gtk_separator_menu_item_new ()); + + gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "quit"))); + + view1 = gtk_menu_item_new_with_mnemonic (_("_View")); + gtk_container_add (GTK_CONTAINER (menubar1), view1); + + view1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (view1), view1_menu); + + fps1 = gtk_check_menu_item_new_with_mnemonic (_("FPS")); + g_signal_connect(fps1, "activate", G_CALLBACK(ToggleFPS), NULL); + gtk_container_add (GTK_CONTAINER (view1_menu), fps1); + + layer1 = gtk_menu_item_new_with_mnemonic (_("Layer")); + gtk_container_add (GTK_CONTAINER (view1_menu), layer1); + + layer1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (layer1), layer1_menu); + + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_vdp1"))); + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg0"))); + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg1"))); + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg2"))); + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg3"))); + gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_rbg0"))); + + gtk_container_add(GTK_CONTAINER(view1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "fullscreen"))); + + log = gtk_menu_item_new_with_mnemonic (_("Log")); + g_signal_connect_swapped(log, "activate", G_CALLBACK(yui_window_show_log), window1); + gtk_container_add(GTK_CONTAINER(view1_menu), log); + + menuitem3 = gtk_menu_item_new_with_mnemonic (_("_Debug")); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem3); + + menuitem3_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem3), menuitem3_menu); + + msh = gtk_menu_item_new_with_mnemonic ("MSH2"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), msh); + g_signal_connect_swapped(msh, "activate", G_CALLBACK(yui_msh_new), window1); + + ssh = gtk_menu_item_new_with_mnemonic ("SSH2"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), ssh); + g_signal_connect_swapped(ssh, "activate", G_CALLBACK(yui_ssh_new), window1); + + vdp2 = gtk_menu_item_new_with_mnemonic ("Vdp1"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), vdp2); + g_signal_connect_swapped(vdp2, "activate", G_CALLBACK(yui_vdp1_new), window1); + + vdp1 = gtk_menu_item_new_with_mnemonic ("Vdp2"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), vdp1); + g_signal_connect_swapped(vdp1, "activate", G_CALLBACK(yui_vdp2_new), window1); + + m68k = gtk_menu_item_new_with_mnemonic ("M68K"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), m68k); + g_signal_connect_swapped(m68k, "activate", G_CALLBACK(yui_m68k_new), window1); + + scudsp = gtk_menu_item_new_with_mnemonic ("SCU-DSP"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), scudsp); + g_signal_connect_swapped(scudsp, "activate", G_CALLBACK(yui_scudsp_new), window1); + + scsp = gtk_menu_item_new_with_mnemonic ("SCSP"); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), scsp); + g_signal_connect_swapped(scsp, "activate", G_CALLBACK(yui_scsp_new), window1); + + gtk_container_add (GTK_CONTAINER (menuitem3_menu), gtk_separator_menu_item_new ()); + + memory = gtk_menu_item_new_with_mnemonic (_("Memory dump")); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), memory); + g_signal_connect_swapped(memory, "activate", G_CALLBACK(yui_mem_new), window1); + + menuitem4 = gtk_menu_item_new_with_mnemonic (_("_Help")); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem4); + + menuitem4_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem4), menuitem4_menu); + + about1 = gtk_image_menu_item_new_from_stock ("gtk-about", NULL); + gtk_container_add (GTK_CONTAINER (menuitem4_menu), about1); + g_signal_connect(about1, "activate", G_CALLBACK(openAboutDialog), window1); + + return menubar1; +} + diff --git a/yabause/src/gtk/pergtk.c b/yabause/src/gtk/pergtk.c new file mode 100644 index 0000000000..734674bf14 --- /dev/null +++ b/yabause/src/gtk/pergtk.c @@ -0,0 +1,92 @@ +/* Copyright 2005-2006 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "pergtk.h" +#include +#include "../yabause.h" +#include "../vdp2.h" +#include "../yui.h" + +int PERGTKInit(void); +void PERGTKDeInit(void); +int PERGTKHandleEvents(void); +void PERGTKNothing(void); + +u32 PERGTKScan(void); +void PERGTKFlush(void); +void PERGTKKeyName(u32 key, char * name, int size); + +PerInterface_struct PERGTK = { +PERCORE_GTK, +"GTK Input Interface", +PERGTKInit, +PERGTKDeInit, +PERGTKHandleEvents, +PERGTKNothing, +PERGTKScan, +0, +PERGTKFlush +#ifdef PERKEYNAME +,PERGTKKeyName +#endif +}; + +////////////////////////////////////////////////////////////////////////////// + +int PERGTKInit(void) { + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERGTKDeInit(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +void PERGTKNothing(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +int PERGTKHandleEvents(void) { + YabauseExec(); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 PERGTKScan(void) { + g_print("this is wrong, the gtk peripheral can't scan\n"); + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERGTKFlush(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +void PERGTKKeyName(u32 key, char * name, int size) +{ + g_strlcpy(name, gdk_keyval_name(key), size); +} diff --git a/yabause/src/gtk/pergtk.h b/yabause/src/gtk/pergtk.h new file mode 100644 index 0000000000..0d441196f9 --- /dev/null +++ b/yabause/src/gtk/pergtk.h @@ -0,0 +1,29 @@ +/* Copyright 2005 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERGTK_H +#define PERGTK_H + +#include "../peripheral.h" + +#define PERCORE_GTK 2 + +extern PerInterface_struct PERGTK; + +#endif diff --git a/yabause/src/gtk/settings.c b/yabause/src/gtk/settings.c new file mode 100644 index 0000000000..4af555def1 --- /dev/null +++ b/yabause/src/gtk/settings.c @@ -0,0 +1,569 @@ +/* Copyright 2006-2011 Guillaume Duhamel + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "settings.h" + +#include "yuicheckbutton.h" +#include "yuifileentry.h" +#include "yuirange.h" +#include "yuipage.h" +#include "yuiinputentry.h" +#include "yuiresolution.h" + +#include "../scsp.h" + +typedef struct { + int id; + const char *Name; +} GenericInterface_struct; + +void cores_to_range(void * pointer, YuiRangeItem ** items) { + GenericInterface_struct ** cores; + GenericInterface_struct * core; + int i; + + if (*items != NULL) return; + + cores = pointer; + + i = 0; + core = cores[i]; + while(core) { + i++; + core = cores[i]; + } + *items = malloc(sizeof(YuiRangeItem) * (i + 1)); + i = 0; + core = cores[i]; + while(core) { + char buffer[1024]; + (*items)[i].name = strdup(core->Name); + sprintf(buffer, "%d", core->id); + (*items)[i].value = strdup(buffer); + i++; + core = cores[i]; + } + (*items)[i].name = NULL; + (*items)[i].value = NULL; +} + +extern CDInterface *SH2CoreList[]; +extern CDInterface *M68KCoreList[]; +extern CDInterface *CDCoreList[]; +extern CDInterface *VIDCoreList[]; +extern CDInterface *OSDCoreList[]; +extern CDInterface *SNDCoreList[]; +extern CDInterface *PERCoreList[]; + +YuiRangeItem * sh2interpreters = NULL; +YuiRangeItem * m68kinterpreters = NULL; +YuiRangeItem * cdcores = NULL; +YuiRangeItem * vidcores = NULL; +YuiRangeItem * osdcores = NULL; +YuiRangeItem * sndcores = NULL; +YuiRangeItem * percores = NULL; + +YuiRangeItem regions[] = { + { "Auto" , "Auto-detect" }, + { "J" , "Japan (NTSC)" }, + { "T", "Asia (NTSC)" }, + { "U", "North America (NTSC)" }, + { "B", "Central/South America (NTSC)" }, + { "K", "Korea (NTSC)" }, + { "A", "Asia (PAL)" }, + { "E", "Europe + others (PAL)" }, + { "L", "Central/South America (PAL)" }, + { 0, 0 } +}; + +YuiRangeItem carttypes[] = { + { "0", "None" }, + { "1", "Pro Action Replay" }, + { "2", "4 Mbit Backup Ram" }, + { "3", "8 Mbit Backup Ram" }, + { "4", "16 Mbit Backup Ram" }, + { "5", "32 Mbit Backup Ram" }, + { "6", "8 Mbit Dram" }, + { "7", "32 Mbit Dram" }, + { "8", "Netlink" }, + { "9", "16 Mbit ROM" }, + { 0, 0 } +}; + +const gchar * keys1[] = { "Up", "Right", "Down", "Left", "R", "L", 0 }; +const gchar * keys2[] = { "A", "B", "C", "X", "Y", "Z", "Start", 0 }; + +YuiRangeItem vidformats[] = { + { "0", "NTSC" }, + { "1", "PAL" }, + { 0, 0 } +}; + +YuiRangeItem mousepercores[] = { + { "2", "Gtk Input Interface" }, + { 0, 0 } +}; + +static void hide_show_cart_path(YuiRange * instance, gpointer data) { + gint i = yui_range_get_active(instance); + + if (i == 8) { + gtk_widget_hide(data); + } else { + gtk_widget_show(data); + } +} + +static void hide_show_netlink(YuiRange * instance, gpointer data) { + gint i = yui_range_get_active(instance); + + if (i != 8) { + gtk_widget_hide(data); + } else { + gtk_widget_show(data); + } +} + +static void percore_changed(GtkWidget * widget, gpointer data) { + const char * core_s = percores[gtk_combo_box_get_active(GTK_COMBO_BOX(widget))].value; + GList * entrylist = data; + int core; + sscanf(core_s, "%d", &core); + + PerDeInit(); + PerInit(core); + + while(entrylist) { + yui_input_entry_update(entrylist->data); + entrylist = g_list_next(entrylist); + } +} + +static void pertype_display_pad(GtkWidget * box) +{ + GtkWidget * table4, * table5; + GtkWidget * box_percore = gtk_vbox_new(FALSE, 10); + GtkWidget * select_percore = yui_range_new(keyfile, "General", "PerCore", percores); + GList * entrylist = NULL; + + gtk_container_set_border_width(GTK_CONTAINER(select_percore), 0); + + gtk_container_set_border_width(GTK_CONTAINER(box_percore), 10); + + gtk_box_pack_start(GTK_BOX (box_percore), select_percore, FALSE, FALSE, 0); + + table4 = yui_input_entry_new(keyfile, "Pad", keys1); + entrylist = g_list_append(entrylist, table4); + + gtk_box_pack_start (GTK_BOX (box_percore), table4, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (box), box_percore, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new(), TRUE, TRUE, 0); + + table5 = yui_input_entry_new(keyfile, "Pad", keys2); + entrylist = g_list_append(entrylist, table5); + + gtk_container_set_border_width(GTK_CONTAINER(table5), 10); + + gtk_box_pack_start (GTK_BOX (box), table5, TRUE, TRUE, 0); + + g_signal_connect(GTK_COMBO_BOX(YUI_RANGE(select_percore)->combo), "changed", G_CALLBACK(percore_changed), entrylist); + + gtk_widget_show_all(box); +} + +static void mouse_speed_change(GtkWidget * range, gpointer data) +{ + g_key_file_set_double(keyfile, "General", "MouseSpeed", gtk_range_get_value(GTK_RANGE(range))); +} + +static void pertype_display_mouse(GtkWidget * box) +{ + GtkWidget * scale; + GtkWidget * table5; + GtkWidget * box_percore = gtk_vbox_new(FALSE, 10); + GtkWidget * select_percore = yui_range_new(keyfile, "General", "MousePerCore", mousepercores); + GList * entrylist = NULL; + + gtk_container_set_border_width(GTK_CONTAINER(select_percore), 0); + gtk_container_set_border_width(GTK_CONTAINER(box_percore), 10); + gtk_box_pack_start(GTK_BOX (box_percore), select_percore, FALSE, FALSE, 0); + + scale = gtk_hscale_new_with_range(0, 10, 0.1); + gtk_range_set_value(GTK_RANGE(scale), g_key_file_get_double(keyfile, "General", "MouseSpeed", NULL)); + g_signal_connect(scale, "value-changed", G_CALLBACK(mouse_speed_change), NULL); + gtk_box_pack_start(GTK_BOX (box_percore), scale, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (box), box_percore, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new(), TRUE, TRUE, 0); + + table5 = yui_input_entry_new(keyfile, "Mouse", PerMouseNames); + entrylist = g_list_append(entrylist, table5); + gtk_container_set_border_width(GTK_CONTAINER(table5), 10); + gtk_box_pack_start (GTK_BOX (box), table5, TRUE, TRUE, 0); + + g_signal_connect(GTK_COMBO_BOX(YUI_RANGE(select_percore)->combo), "changed", G_CALLBACK(percore_changed), entrylist); + gtk_widget_show_all(box); +} + +static void pertype_changed(GtkWidget * widget, gpointer data) { + GtkTreePath * path; + gchar * strpath; + int i; + GtkWidget * box = data; + GList * children; + GtkWidget * child; + + children = gtk_container_get_children(GTK_CONTAINER(box)); + for(i = 1;i < 4;i++) { + child = g_list_nth_data(children, i); + if (child != NULL) gtk_widget_destroy(child); + } + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); + + if (path) { + int i; + + strpath = gtk_tree_path_to_string(path); + sscanf(strpath, "%d", &i); + + switch(i) { + case 0: + g_key_file_set_integer(keyfile, "General", "PerType", PERPAD); + pertype_display_pad(box); + break; + case 1: + g_key_file_set_integer(keyfile, "General", "PerType", PERMOUSE); + pertype_display_mouse(box); + break; + } + + g_free(strpath); + gtk_tree_path_free(path); + } +} + +static void frameskip_toggled(GtkWidget * widget, gpointer data) { + g_key_file_set_integer(keyfile, "General", "Frameskip", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); +} + +static void disable_enable_audio_sync(YuiCheckButton *audiosync) { + ScspSetFrameAccurate(yui_check_button_get_active(audiosync)); +} + +static void disable_enable_fixed_base_time(YuiCheckButton *clocksync, YuiCheckButton *fixedbasetime) { + gtk_widget_set_sensitive(GTK_WIDGET(fixedbasetime), + yui_check_button_get_active(clocksync)); +} + +static void volume_changed(GtkRange * range, gpointer data) { + g_key_file_set_integer(keyfile, "General", "Volume", (int) gtk_range_get_value(range)); +} + +GtkWidget* create_dialog1(void) { + GtkWidget *dialog1; + GtkWidget *notebook1; + GtkWidget *vbox17; + GtkWidget *hbox22; + GtkWidget *button11; + GtkWidget *button12; + GtkWidget * general, * video_sound, * cart_memory, *advanced, * sound; + GtkWidget * box; + u8 perid; + + cores_to_range(SH2CoreList, &sh2interpreters); + cores_to_range(M68KCoreList, &m68kinterpreters); + cores_to_range(CDCoreList, &cdcores); + cores_to_range(VIDCoreList, &vidcores); + cores_to_range(OSDCoreList, &osdcores); + cores_to_range(SNDCoreList, &sndcores); + cores_to_range(PERCoreList, &percores); + + dialog1 = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dialog1), "Yabause configuration"); + gtk_window_set_icon_name (GTK_WINDOW (dialog1), "gtk-preferences"); + gtk_window_set_type_hint (GTK_WINDOW (dialog1), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_resizable(GTK_WINDOW(dialog1), FALSE); + + notebook1 = gtk_notebook_new (); + gtk_widget_show(notebook1); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog1)->vbox), notebook1, TRUE, TRUE, 0); + + /* + * General configuration + */ + general = yui_page_new(keyfile); + + box = yui_page_add(YUI_PAGE(general), _("Bios")); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "BiosPath", YUI_FILE_ENTRY_BROWSE, NULL)); + + box = yui_page_add(YUI_PAGE(general), _("Cdrom")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "CDROMCore", cdcores)); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "CDROMDrive", YUI_FILE_ENTRY_BROWSE, NULL)); + + box = yui_page_add(YUI_PAGE(general), _("Save States")); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "StatePath", YUI_FILE_ENTRY_BROWSE | YUI_FILE_ENTRY_DIRECTORY, NULL)); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), general, gtk_label_new (_("General"))); + gtk_widget_show_all(general); + + /* + * Video configuration + */ + video_sound = yui_page_new(keyfile); + + box = yui_page_add(YUI_PAGE(video_sound), _("Video Core")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "VideoCore", vidcores)); + +#ifdef YAB_PORT_OSD + box = yui_page_add(YUI_PAGE(video_sound), _("OSD Core")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "OSDCore", osdcores)); +#endif + + box = yui_page_add(YUI_PAGE(video_sound), _("Resolution")); + gtk_container_add(GTK_CONTAINER(box), yui_resolution_new(keyfile, "General")); + + box = yui_page_add(YUI_PAGE(video_sound), _("Video Format")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "VideoFormat", vidformats)); + + box = yui_page_add(YUI_PAGE(video_sound), _("Frame Skip/Limiter")); + { + GtkWidget * frameskip = gtk_check_button_new_with_label("Enable frame skipping/limiting"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frameskip), g_key_file_get_integer(keyfile, "General", "Frameskip", NULL)); + gtk_container_set_border_width(GTK_CONTAINER(frameskip), 10); + g_signal_connect(frameskip, "toggled", G_CALLBACK(frameskip_toggled), NULL); + gtk_container_add(GTK_CONTAINER(box), frameskip); + } + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), video_sound, gtk_label_new (_("Video"))); + gtk_widget_show_all(video_sound); + + /* + * Sound configuration + */ + sound = yui_page_new(keyfile); + + box = yui_page_add(YUI_PAGE(sound), _("Sound Core")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "SoundCore", sndcores)); + + { + GtkWidget * volume; + + box = yui_page_add(YUI_PAGE(sound), _("Volume")); + gtk_container_set_border_width(GTK_CONTAINER(box), 10); + volume = gtk_hscale_new_with_range(0, 100, 1); + gtk_range_set_value(GTK_RANGE(volume), g_key_file_get_integer(keyfile, "General", "Volume", NULL)); + g_signal_connect(volume, "value-changed", G_CALLBACK(volume_changed), NULL); + gtk_container_add(GTK_CONTAINER(box), volume); + } + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), sound, gtk_label_new (_("Sound"))); + gtk_widget_show_all(sound); + + /* + * Cart/Memory configuration + */ + cart_memory = yui_page_new(keyfile); + + box = yui_page_add(YUI_PAGE(cart_memory), _("Cartridge")); + { + GtkWidget * w1, * w2, * w3; + + w1 = yui_range_new(keyfile, "General", "CartType", carttypes); + gtk_container_add(GTK_CONTAINER(box), w1); + + w2 = yui_file_entry_new(keyfile, "General", "CartPath", YUI_FILE_ENTRY_BROWSE, NULL); + gtk_container_add(GTK_CONTAINER(box), w2); + + w3 = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(w3), yui_file_entry_new(keyfile, "General", "NetlinkHost", 0, "Host"), TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(w3), yui_file_entry_new(keyfile, "General", "NetlinkPort", 0, "Port"), TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(box), w3); + + g_signal_connect(w1, "changed", G_CALLBACK(hide_show_cart_path), w2); + g_signal_connect(w1, "changed", G_CALLBACK(hide_show_netlink), w3); + + box = yui_page_add(YUI_PAGE(cart_memory), _("Memory")); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "BackupRamPath", YUI_FILE_ENTRY_BROWSE, NULL)); + + box = yui_page_add(YUI_PAGE(cart_memory), _("Mpeg ROM")); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "MpegRomPath", YUI_FILE_ENTRY_BROWSE, NULL)); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), cart_memory, gtk_label_new (_("Cart/Memory"))); + gtk_widget_show_all(cart_memory); + + if (yui_range_get_active(YUI_RANGE(w1)) == 8) gtk_widget_hide(w2); + else gtk_widget_hide(w3); + } + + /* + * Input Configuration + */ + vbox17 = gtk_vbox_new (FALSE, 0); + + hbox22 = gtk_hbox_new (FALSE, 0); + + { + GtkWidget * controllerscroll; + GtkTreeStore * controllerlist; + GtkWidget * controllerlistview; + GtkCellRenderer * controllerrenderer; + GtkTreeViewColumn * controllercolumn; + GtkTreeIter iter; + GtkTreePath * path; + + controllerscroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(controllerscroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + controllerlist = gtk_tree_store_new(1, G_TYPE_STRING); + controllerlistview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(controllerlist)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(controllerlistview), FALSE); + + controllerrenderer = gtk_cell_renderer_text_new(); + controllercolumn = gtk_tree_view_column_new_with_attributes("Controller", controllerrenderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (controllerlistview), controllercolumn); + + gtk_tree_store_append(controllerlist, &iter, NULL); + gtk_tree_store_set(controllerlist, &iter, 0, "Pad", -1); + + gtk_tree_store_append(controllerlist, &iter, NULL); + gtk_tree_store_set(controllerlist, &iter, 0, "Mouse", -1); + + gtk_container_add(GTK_CONTAINER(controllerscroll), controllerlistview); + gtk_box_pack_start (GTK_BOX (hbox22), controllerscroll, TRUE, TRUE, 0); + + gtk_tree_view_expand_all(GTK_TREE_VIEW(controllerlistview)); + + g_signal_connect(controllerlistview, "cursor-changed", G_CALLBACK(pertype_changed), hbox22); + perid = g_key_file_get_integer(keyfile, "General", "PerType", NULL); + switch(perid) + { + case PERMOUSE: + path = gtk_tree_path_new_from_string("1"); + break; + case PERPAD: + default: + path = gtk_tree_path_new_from_string("0"); + break; + } + + gtk_tree_view_set_cursor(GTK_TREE_VIEW(controllerlistview), path, NULL, FALSE); + gtk_tree_path_free(path); + } + + gtk_box_pack_start (GTK_BOX (vbox17), hbox22, TRUE, TRUE, 0); + + //pertype_display_pad(hbox22); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), vbox17, gtk_label_new (_("Input"))); + gtk_widget_show_all(vbox17); + + /* + * Advanced configuration + */ + + advanced = yui_page_new(keyfile); + + box = yui_page_add(YUI_PAGE(advanced), _("Region")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "Region", regions)); + + box = yui_page_add(YUI_PAGE(advanced), _("SH2 Interpreter")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "SH2Int", sh2interpreters)); + + box = yui_page_add(YUI_PAGE(advanced), _("M68k Interpreter")); + gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "M68kInt", m68kinterpreters)); + + box = yui_page_add(YUI_PAGE(advanced), _("Audio Sync")); + { + GtkWidget *button = yui_check_button_new( + _("Synchronize audio output with emulation"), + keyfile, "General", "AudioSync" + ); + gtk_container_add(GTK_CONTAINER(box), button); + g_signal_connect(button, "changed", + G_CALLBACK(disable_enable_audio_sync), NULL); + } + + box = yui_page_add(YUI_PAGE(advanced), _("Clock Sync")); + { + GtkWidget *button1, *button2; + + button1 = yui_check_button_new( + _("Synchronize internal clock with emulation"), + keyfile, "General", "ClockSync" + ); + gtk_container_add(GTK_CONTAINER(box), button1); + + button2 = yui_check_button_new( + _("Always start from 1998-01-01 12:00"), + keyfile, "General", "FixedBaseTime" + ); + gtk_container_add(GTK_CONTAINER(box), button2); + if (!yui_check_button_get_active(YUI_CHECK_BUTTON(button1))) + gtk_widget_set_sensitive(button2, FALSE); + + g_signal_connect(button1, "changed", + G_CALLBACK(disable_enable_fixed_base_time), button2); + } + + box = yui_page_add(YUI_PAGE(advanced), _("Threads")); + { + GtkWidget *button = yui_check_button_new( + _("Use multithreaded emulation (EXPERIMENTAL!)"), + keyfile, "General", "UseThreads" + ); + gtk_container_add(GTK_CONTAINER(box), button); + } + +#ifdef HAVE_LIBMINI18N + box = yui_page_add(YUI_PAGE(advanced), _("Translation")); + gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "TranslationPath", YUI_FILE_ENTRY_BROWSE, NULL)); +#endif + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), advanced, gtk_label_new (_("Advanced"))); + gtk_widget_show_all(advanced); + + /* + * Dialog buttons + */ + button11 = gtk_button_new_from_stock ("gtk-cancel"); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), button11, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button11, GTK_CAN_DEFAULT); + gtk_widget_show(button11); + + button12 = gtk_button_new_from_stock ("gtk-ok"); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), button12, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button12, GTK_CAN_DEFAULT); + gtk_widget_show(button12); + + gtk_widget_show(dialog1); + + return dialog1; +} + +void yui_texture_free(guchar *pixels, gpointer data) { + free(pixels); +} diff --git a/yabause/src/gtk/settings.h b/yabause/src/gtk/settings.h new file mode 100644 index 0000000000..a36f146045 --- /dev/null +++ b/yabause/src/gtk/settings.h @@ -0,0 +1,51 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_SETTINGS_H +#define YUI_SETTINGS_H + +#include + +#include "../vdp1.h" +#include "../vdp2.h" +#include "../scsp.h" +#include "../yabause.h" +#include "../cdbase.h" +#include "../peripheral.h" + +#include "yuiwindow.h" + +extern GKeyFile * keyfile; +extern yabauseinit_struct yinit; + +extern void YuiSaveState(void); +extern void YuiLoadState(void); + +GtkWidget* create_dialog1(void); +GtkWidget* create_menu(YuiWindow *); + +void yui_conf(void); +void yui_resize(guint, guint, gboolean); + +void gtk_yui_toggle_fullscreen(void); + +void yui_texture_free(guchar *pixels, gpointer data); + +#endif diff --git a/yabause/src/gtk/yabause.desktop.in b/yabause/src/gtk/yabause.desktop.in new file mode 100644 index 0000000000..2da69a2a6f --- /dev/null +++ b/yabause/src/gtk/yabause.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Yabause (Gtk port) +Comment=Sega Saturn emulator +TryExec=yabause +Exec=@YAB_PORT_NAME@ +Icon=yabause +Categories=GNOME;GTK;Game; diff --git a/yabause/src/gtk/yabause.png b/yabause/src/gtk/yabause.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa6208a0c49c92d1a7bbec6fc3be334dcba8a9c GIT binary patch literal 2544 zcmW+&dpMM78-M3y9A+2^GbS0^M!S@8h-w|)5k914)fN)tIJ+%5B!>p`j%iVpLq3OU znOy0BLU#MMFL^O3D0frjfmh z--+(iX<=E4H97;L@=qB;N`*hHviEn*oZkond~!_mA=Ti+NTt~^%mwE4&w-Sh5}#Q& z5a^5>$4|fYx7nj>bozxhnB;{T#k}bxIjpS1aFGfsM{X`nAO$g1*B2cRS1bJ8cpMaW zvlf%|g50gkw1t2!32_g42I_6|_GB6|C0pL6O#oay1{Bbyb$08GUkm?n25SZQ z?hlVrZqc&A};oQwJ;`30LxyTr(A)Y(?wc-a_{kta0^90`;Km~;Y)|6}LPGV#E_Re2Uv`_Od-0`Igp zI1%+|IggIPYLU|f-zW|H}bGHE&6)hMvJ)YVKLvKUY zn!f)D+E9Wi-T)pZQb22P3KOv6${aGcas~W6gdGav_Bi=yC3OIag>I}NW|d|LPY>UM zx@2@RN4TlCOZ=##dyYKy18`uoO>wpMLw$^*{dUakOG2-$Z7<{?2>f`c*c}3L@-X%V zac%|$e*CL(OdNg8IG5`*n`2LYwv)kuhaKx9^Ah3F<@0XV-TeBk#=i{7PrR`F5IUa7hNq`}0h1I~!a%rOwerxwJJ#c%6D&;ATeeK4mLcb@Z8#=QR?;+7`5uFu zZ_EYvQG~FL*|^(d$%jsYMwshyX7x*bk+84#CF9%gvmd%JM*Q|xI(>RN=0C?jrN-bX z+skMF$wlW#J zpT8J@=lmy?m?=wAA*t?dqhHZdsPp5*s3phT8aJM8rgDu~=0ydtn&tFxelnfX1Wer% z)_#>PA#HEt8qfM6NHnnK2r55!%@@)6>OSSy`K2^3FE8n}UK<-6p5kl@kiR1YegXT= zg)OSqA2Vr{8!T}uCEeu=dg~RNbLh-xz5;&z>KN7c%fO74(!;szEtko5)P5|D}Y~)*E#9&bBY@TFckjL6BFr#LW zyw!ph@RKXpzYb8q4?oYHVi>uQvJuN~g)XxNtY`PJlwa70=0 z-oPP>Qjr?{ZaR`^bn-LFe=#8{ef`5W?@N%+D4$Cp}}z z38r&9uTU(RsA39O=cvS;+qoJ?=B$O?Zq)Ydwa&y9iC?a<|KOEf8;|MPztk75K1OSC zb1lBi=Oq7cll)u!T?SO=`H~P11uG7bq-uDwBL+nxEt0-&F3jUGoq4A&u~@-1;=v^z zX%bek(~(6N(TjTolG%hy3{bwg+iGFAu~Mz@2P!wys%SK~cmx-^>P^pIZvBz$>mlSceZ0OQ(v>)ai`uGHFB=~xK zDBru!Jb?R?eox%4wlpo#pVbb9DXqWuZ)~3NDcJjmesgI5c3EF*#TJnIKUL+3WXo(gKTd5PyTzuiRNfM9mvuH*^ zeqf z%9V=sy%)Y^AN<4o?hbU)D7D(c-U!v^^9zRYK{xHYE*kp5*{(5im6Vphlz%J!tPYwk vIyA!|t9 + +#include "yuicheckbutton.h" + +static void yui_check_button_class_init(YuiCheckButtonClass * klass); +static void yui_check_button_init (YuiCheckButton * ycb); +static void yui_check_button_toggled (GtkToggleButton * button, YuiCheckButton * ycb); + +GType yui_check_button_get_type (void) { + static GType ycb_type = 0; + + if (!ycb_type) + { + static const GTypeInfo ycb_info = + { + sizeof (YuiCheckButtonClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_check_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiCheckButton), + 0, + (GInstanceInitFunc) yui_check_button_init, + NULL, + }; + + ycb_type = g_type_register_static(GTK_TYPE_CHECK_BUTTON, "YuiCheckButton", &ycb_info, 0); + } + + return ycb_type; +} + +#define PROP_KEYFILE 1 +#define PROP_GROUP 2 +#define PROP_KEY 3 + +static void yui_check_button_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) { + switch(property_id) { + case PROP_KEYFILE: + YUI_CHECK_BUTTON(object)->keyfile = g_value_get_pointer(value); + break; + case PROP_GROUP: + YUI_CHECK_BUTTON(object)->group = g_value_get_pointer(value); + break; + case PROP_KEY: + YUI_CHECK_BUTTON(object)->key = g_value_get_pointer(value); + break; + } +} + +static void yui_check_button_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) { +} + +enum { YUI_CHECK_BUTTON_CHANGED_SIGNAL, LAST_SIGNAL }; + +static guint yui_check_button_signals[LAST_SIGNAL] = { 0 }; + +static void yui_check_button_class_init (YuiCheckButtonClass * klass) { + GParamSpec * param; + + G_OBJECT_CLASS(klass)->set_property = yui_check_button_set_property; + G_OBJECT_CLASS(klass)->get_property = yui_check_button_get_property; + + param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); + + param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); + + param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); + + yui_check_button_signals[YUI_CHECK_BUTTON_CHANGED_SIGNAL] = g_signal_new ("changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(YuiCheckButtonClass, yui_check_button_change), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void yui_check_button_init (YuiCheckButton * ycb) { +} + +GtkWidget * yui_check_button_new(const gchar * label, GKeyFile * keyfile, const gchar * group, const gchar * key) { + GtkWidget * button; + YuiCheckButton * ycb; + gboolean current; + + button = GTK_WIDGET(g_object_new(yui_check_button_get_type(), + "label", label, + "key-file", keyfile, "group", group, "key", key, NULL)); + ycb = YUI_CHECK_BUTTON(button); + + gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(ycb), TRUE); + + current = g_key_file_get_boolean(ycb->keyfile, ycb->group, ycb->key, NULL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ycb), current); + + g_signal_connect(GTK_TOGGLE_BUTTON(ycb), "toggled", G_CALLBACK(yui_check_button_toggled), ycb); + + return button; +} + +static void yui_check_button_toggled(GtkToggleButton * button, YuiCheckButton * ycb) { + g_key_file_set_boolean(ycb->keyfile, ycb->group, ycb->key, + gtk_toggle_button_get_active(button)); + g_signal_emit(G_OBJECT(ycb), yui_check_button_signals[YUI_CHECK_BUTTON_CHANGED_SIGNAL], 0); +} + +gboolean yui_check_button_get_active(YuiCheckButton * ycb) { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ycb)); +} diff --git a/yabause/src/gtk/yuicheckbutton.h b/yabause/src/gtk/yuicheckbutton.h new file mode 100644 index 0000000000..3a468bcb41 --- /dev/null +++ b/yabause/src/gtk/yuicheckbutton.h @@ -0,0 +1,62 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_CHECK_BUTTON_H +#define YUI_CHECK_BUTTON_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_CHECK_BUTTON_TYPE (yui_check_button_get_type ()) +#define YUI_CHECK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_CHECK_BUTTON_TYPE, YuiCheckButton)) +#define YUI_CHECK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_CHECK_BUTTON_TYPE, YuiCheckButtonClass)) +#define IS_YUI_CHECK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_CHECK_BUTTON_TYPE)) +#define IS_YUI_CHECK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_CHECK_BUTTON_TYPE)) + +typedef struct _YuiCheckButton YuiCheckButton; +typedef struct _YuiCheckButtonClass YuiCheckButtonClass; + +struct _YuiCheckButton +{ + GtkCheckButton button; + + GKeyFile * keyfile; + gchar * group; + gchar * key; +}; + +struct _YuiCheckButtonClass +{ + GtkCheckButtonClass parent_class; + + void (* yui_check_button_change) (YuiCheckButton * ycb); +}; + +GType yui_check_button_get_type (void); +GtkWidget * yui_check_button_new (const gchar * label, GKeyFile * keyfile, const gchar * group, const gchar * key); +gboolean yui_check_button_get_active (YuiCheckButton * ycb); + +G_END_DECLS + +#endif /* YUI_CHECK_BUTTON_H */ diff --git a/yabause/src/gtk/yuifileentry.c b/yabause/src/gtk/yuifileentry.c new file mode 100644 index 0000000000..11385d7b94 --- /dev/null +++ b/yabause/src/gtk/yuifileentry.c @@ -0,0 +1,167 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "yuifileentry.h" + +static void yui_file_entry_class_init (YuiFileEntryClass * klass); +static void yui_file_entry_init (YuiFileEntry * yfe); +static void yui_file_entry_browse (GtkWidget * widget, gpointer user_data); +static void yui_file_entry_changed (GtkWidget * widget, YuiFileEntry * yfe); + +GType yui_file_entry_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiFileEntryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_file_entry_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiFileEntry), + 0, + (GInstanceInitFunc) yui_file_entry_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_HBOX, "YuiFileEntry", &yfe_info, 0); + } + + return yfe_type; +} + +#define PROP_KEYFILE 1 +#define PROP_GROUP 2 +#define PROP_KEY 3 + +static void yui_file_entry_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) { + switch(property_id) { + case PROP_KEYFILE: + YUI_FILE_ENTRY(object)->keyfile = g_value_get_pointer(value); + break; + case PROP_GROUP: + YUI_FILE_ENTRY(object)->group = g_value_get_pointer(value); + break; + case PROP_KEY: + YUI_FILE_ENTRY(object)->key = g_value_get_pointer(value); + break; + } +} + +static void yui_file_entry_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) { +} + +static void yui_file_entry_class_init (YuiFileEntryClass * klass) { + GParamSpec * param; + + G_OBJECT_CLASS(klass)->set_property = yui_file_entry_set_property; + G_OBJECT_CLASS(klass)->get_property = yui_file_entry_get_property; + + param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); + + param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); + + param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); +} + +static void yui_file_entry_init (YuiFileEntry * yfe) { + gtk_container_set_border_width(GTK_CONTAINER(yfe), 10); +} + +GtkWidget * yui_file_entry_new(GKeyFile * keyfile, const gchar * group, const gchar * key, gint flags, const gchar * label) { + GtkWidget * entry; + YuiFileEntry * yfe; + gchar * entryText; + + entry = GTK_WIDGET(g_object_new(yui_file_entry_get_type(), "spacing", 10, + "key-file", keyfile, "group", group, "key", key, NULL)); + yfe = YUI_FILE_ENTRY(entry); + + yfe->flags = flags; + + if (label) { + gtk_box_pack_start(GTK_BOX(yfe), gtk_label_new_with_mnemonic(label), FALSE, FALSE, 0); + } + + yfe->entry = gtk_entry_new (); + gtk_box_pack_start(GTK_BOX(yfe), yfe->entry, TRUE, TRUE, 0); + + if (flags & YUI_FILE_ENTRY_BROWSE) { + yfe->button = gtk_button_new_with_mnemonic ("Browse"); + g_signal_connect(yfe->button, "clicked", G_CALLBACK(yui_file_entry_browse), yfe); + gtk_box_pack_start(GTK_BOX(yfe), yfe->button, FALSE, FALSE, 0); + } + + entryText = g_key_file_get_value(yfe->keyfile, yfe->group, yfe->key, 0); + if ( !entryText ) entryText = ""; + gtk_entry_set_text(GTK_ENTRY(yfe->entry), entryText ); + g_signal_connect(GTK_ENTRY(yfe->entry), "changed", G_CALLBACK(yui_file_entry_changed), yfe); + + return entry; +} + +static void yui_file_entry_browse(GtkWidget * widget, gpointer user_data) { + GtkWidget * file_selector; + gint result; + const gchar * filename; + YuiFileEntry * yfe = user_data; + GtkFileChooserAction action; + + if (yfe->flags & YUI_FILE_ENTRY_DIRECTORY) { + action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + } else { + action = GTK_FILE_CHOOSER_ACTION_OPEN; + } + + file_selector = gtk_file_chooser_dialog_new ("Please choose a file", NULL, action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + filename = gtk_entry_get_text(GTK_ENTRY(yfe->entry)); + if (filename[0] != '\0') + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_selector), filename); + + gtk_widget_show(file_selector); + + result = gtk_dialog_run(GTK_DIALOG(file_selector)); + + switch(result) { + case GTK_RESPONSE_ACCEPT: + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); + gtk_entry_set_text(GTK_ENTRY(yfe->entry), filename); + break; + case GTK_RESPONSE_CANCEL: + break; + } + + gtk_widget_destroy(file_selector); +} + +static void yui_file_entry_changed(GtkWidget * entry, YuiFileEntry * yfe) { + g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, gtk_entry_get_text(GTK_ENTRY(yfe->entry))); +} diff --git a/yabause/src/gtk/yuifileentry.h b/yabause/src/gtk/yuifileentry.h new file mode 100644 index 0000000000..f223d8851f --- /dev/null +++ b/yabause/src/gtk/yuifileentry.h @@ -0,0 +1,68 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_FILE_ENTRY_H +#define YUI_FILE_ENTRY_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_FILE_ENTRY_TYPE (yui_file_entry_get_type ()) +#define YUI_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_FILE_ENTRY_TYPE, YuiFileEntry)) +#define YUI_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_FILE_ENTRY_TYPE, YuiFileEntryClass)) +#define IS_YUI_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_FILE_ENTRY_TYPE)) +#define IS_YUI_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_FILE_ENTRY_TYPE)) + +#define YUI_FILE_ENTRY_BROWSE 1 +#define YUI_FILE_ENTRY_DIRECTORY 2 + +typedef struct _YuiFileEntry YuiFileEntry; +typedef struct _YuiFileEntryClass YuiFileEntryClass; + +struct _YuiFileEntry +{ + GtkHBox hbox; + + GtkWidget * entry; + GtkWidget * button; + + GKeyFile * keyfile; + gchar * group; + gchar * key; + + int flags; +}; + +struct _YuiFileEntryClass +{ + GtkHBoxClass parent_class; + + void (* yui_file_entry) (YuiFileEntry * yfe); +}; + +GType yui_file_entry_get_type (void); +GtkWidget* yui_file_entry_new (GKeyFile *, const gchar *, const gchar *, gint flags, const gchar * label); + +G_END_DECLS + +#endif /* YUI_FILE_ENTRY_H */ diff --git a/yabause/src/gtk/yuiinputentry.c b/yabause/src/gtk/yuiinputentry.c new file mode 100644 index 0000000000..03a49579e4 --- /dev/null +++ b/yabause/src/gtk/yuiinputentry.c @@ -0,0 +1,244 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include "yuiinputentry.h" +#include "settings.h" + +static void yui_input_entry_class_init (YuiInputEntryClass *klass); +static void yui_input_entry_init (YuiInputEntry *yie); +gboolean yui_input_entry_keypress(GtkWidget * widget, GdkEventKey * event, gpointer name); +gboolean yui_input_entry_focus_in(GtkWidget * widget, GdkEventFocus * event, gpointer user_data); +gboolean yui_input_entry_focus_out(GtkWidget * widget, GdkEventFocus * event, gpointer user_data); + +GType yui_input_entry_get_type (void) { + static GType yie_type = 0; + + if (!yie_type) { + static const GTypeInfo yie_info = { + sizeof (YuiInputEntryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_input_entry_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiInputEntry), + 0, + (GInstanceInitFunc) yui_input_entry_init, + NULL, + }; + + yie_type = g_type_register_static (GTK_TYPE_TABLE, "YuiInputEntry", &yie_info, 0); + } + + return yie_type; +} + +#define PROP_KEYFILE 1 +#define PROP_GROUP 2 + +static void yui_input_entry_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) { + switch(property_id) { + case PROP_KEYFILE: + YUI_INPUT_ENTRY(object)->keyfile = g_value_get_pointer(value); + break; + case PROP_GROUP: + YUI_INPUT_ENTRY(object)->group = g_value_get_pointer(value); + break; + } +} + +static void yui_input_entry_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) { +} + +static void yui_input_entry_class_init (YuiInputEntryClass *klass) { + GParamSpec * param; + + G_OBJECT_CLASS(klass)->set_property = yui_input_entry_set_property; + G_OBJECT_CLASS(klass)->get_property = yui_input_entry_get_property; + + param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); + + param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); +} + +static void yui_input_entry_init(YuiInputEntry *yie) { + gtk_container_set_border_width(GTK_CONTAINER(yie), 0); + gtk_table_set_row_spacings(GTK_TABLE(yie), 10); + gtk_table_set_col_spacings(GTK_TABLE(yie), 10); +} + +GtkWidget* yui_input_entry_new(GKeyFile * keyfile, const gchar * group, const gchar * keys[]) { + GtkWidget * widget; + GtkWidget * label; + GtkWidget * entry; + guint keyName; + int row = 0; + + widget = GTK_WIDGET(g_object_new(yui_input_entry_get_type(), "key-file", keyfile, "group", group, NULL)); + + while(keys[row]) { + char tmp[100]; + gtk_table_resize(GTK_TABLE(widget), row + 1, 2); + label = gtk_label_new(keys[row]); + + gtk_table_attach(GTK_TABLE(widget), label, 0, 1, row , row + 1, + (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + + entry = gtk_entry_new (); + gtk_entry_set_width_chars(GTK_ENTRY(entry), 10); + + sprintf(tmp, "%s.%s.1", group, keys[row]); + keyName = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); + if (keyName > 0) { + char buffer[50]; +#ifdef PERKEYNAME + PERCore->KeyName(keyName, buffer, 50); +#else + sprintf(buffer, "%x", keyName); +#endif + gtk_entry_set_text(GTK_ENTRY(entry), buffer); + } + + if (PERCore) { + //if (PERCore->canScan) + g_signal_connect(entry, "focus-in-event", G_CALLBACK(yui_input_entry_focus_in), (gpointer) keys[row]); + g_signal_connect(entry, "focus-out-event", G_CALLBACK(yui_input_entry_focus_out), NULL); + //else + g_signal_connect(entry, "key-press-event", G_CALLBACK(yui_input_entry_keypress), (gpointer) keys[row]); + } else { + gtk_widget_set_sensitive(entry, FALSE); + } + + gtk_table_attach(GTK_TABLE(widget), entry, 1, 2, row, row + 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); + row++; + } + + return widget; +} + +gboolean yui_input_entry_keypress(GtkWidget * widget, GdkEventKey * event, gpointer name) { + char tmp[100]; + + if (PERCore->canScan) return FALSE; + +#ifdef PERKEYNAME + PERCore->KeyName(event->keyval, tmp, 100); +#else + sprintf(tmp, "%x", event->keyval); +#endif + gtk_entry_set_text(GTK_ENTRY(widget), tmp); + sprintf(tmp, "%s.%s.1", YUI_INPUT_ENTRY(gtk_widget_get_parent(widget))->group, (char *)name); + g_key_file_set_integer(YUI_INPUT_ENTRY(gtk_widget_get_parent(widget))->keyfile, + PERCore->Name, tmp, event->keyval); + + return TRUE; +} + +gboolean is_watching = FALSE; +GtkEntry * entry_hack = NULL; + +static gboolean watch_joy(gpointer name) { + u32 i; + + if (! is_watching) return TRUE; + + if (! PERCore->canScan) { + is_watching = FALSE; + return TRUE; + } + + i = PERCore->Scan(); + + if (i == 0) { + return TRUE; + } else { + char tmp[100]; + + sprintf(tmp, "Pad.%s.1", (char *)name); // should be group.name + g_key_file_set_integer(keyfile, PERCore->Name, tmp, i); +#ifdef PERKEYNAME + PERCore->KeyName(i, tmp, 100); +#else + sprintf(tmp, "%x", (int)i); +#endif + gtk_entry_set_text(entry_hack, tmp); + is_watching = FALSE; + return FALSE; + } +} + +gboolean yui_input_entry_focus_in(GtkWidget * widget, GdkEventFocus * event, gpointer name) { + if (! PERCore->canScan) return TRUE; + + PERCore->Flush(); + entry_hack = GTK_ENTRY(widget); + + if (!is_watching) { + g_timeout_add(100, watch_joy, name); + is_watching = TRUE; + } + + return FALSE; +} + +gboolean yui_input_entry_focus_out(GtkWidget * widget, GdkEventFocus * event, gpointer name) { + is_watching = FALSE; + + return FALSE; +} + +void yui_input_entry_update(YuiInputEntry * yie) { + GList * wlist = gtk_container_get_children(GTK_CONTAINER(yie)); + u32 key; + GtkEntry * entry = NULL; + char tmp[100]; + + while(wlist) { + if (strcmp(gtk_widget_get_name(wlist->data), "GtkEntry") == 0) { + entry = wlist->data; + } + if (strcmp(gtk_widget_get_name(wlist->data), "GtkLabel") == 0) { + sprintf(tmp, "%s.%s.1", yie->group, gtk_label_get_text(wlist->data)); + key = g_key_file_get_integer(yie->keyfile, PERCore->Name, tmp, 0); + if (key > 0) { +#ifdef PERKEYNAME + PERCore->KeyName(key, tmp, 100); +#else + sprintf(tmp, "%x", (int)key); +#endif + gtk_entry_set_text(entry, tmp); + } else { + gtk_entry_set_text(entry, ""); + } + } + wlist = g_list_next(wlist); + } +} diff --git a/yabause/src/gtk/yuiinputentry.h b/yabause/src/gtk/yuiinputentry.h new file mode 100644 index 0000000000..99d391b9f2 --- /dev/null +++ b/yabause/src/gtk/yuiinputentry.h @@ -0,0 +1,59 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_INPUT_ENTRY_H +#define YUI_INPUT_ENTRY_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_INPUT_ENTRY_TYPE (yui_input_entry_get_type ()) +#define YUI_INPUT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_INPUT_ENTRY_TYPE, YuiInputEntry)) +#define YUI_INPUT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_INPUT_ENTRY_TYPE, YuiInputEntryClass)) +#define IS_YUI_INPUT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_INPUT_ENTRY_TYPE)) +#define IS_YUI_INPUT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_INPUT_ENTRY_TYPE)) + +typedef struct _YuiInputEntry YuiInputEntry; +typedef struct _YuiInputEntryClass YuiInputEntryClass; + +struct _YuiInputEntry +{ + GtkTable table; + + GKeyFile * keyfile; + gchar * group; +}; + +struct _YuiInputEntryClass { + GtkTableClass parent_class; + + void (* yui_input_entry) (YuiInputEntry *yie); +}; + +GType yui_input_entry_get_type (void); +GtkWidget* yui_input_entry_new (GKeyFile * keyfile, const gchar * group, const gchar * keys[]); +void yui_input_entry_update (YuiInputEntry *yie); + +G_END_DECLS + +#endif /* __YUI_INPUT_ENTRY_H__ */ diff --git a/yabause/src/gtk/yuim68k.c b/yabause/src/gtk/yuim68k.c new file mode 100644 index 0000000000..1d92d1af8e --- /dev/null +++ b/yabause/src/gtk/yuim68k.c @@ -0,0 +1,382 @@ +/* Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuim68k.h" +#include "../m68kd.h" +#include "../yabause.h" +#include "settings.h" + +static void yui_m68k_class_init (YuiM68kClass * klass); +static void yui_m68k_init (YuiM68k * yfe); +static void yui_m68k_clear(YuiM68k * m68k); +static void yui_m68k_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiM68k *m68k); +static void yui_m68k_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiM68k *m68k); +static void yui_m68k_step( GtkWidget* widget, YuiM68k * m68k ); +static void yui_m68k_breakpoint_handler(u32 addr); + +static YuiM68k *yui_m68k; +static YuiWindow * yui; + +GType yui_m68k_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiM68kClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_m68k_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiM68k), + 0, + (GInstanceInitFunc) yui_m68k_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiM68k", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_m68k_class_init (UNUSED YuiM68kClass * klass) { +} + +static void yui_m68k_init (YuiM68k * m68k) { + gtk_window_set_title(GTK_WINDOW(m68k), "M68K"); + gtk_window_set_resizable( GTK_WINDOW(m68k), FALSE ); + + m68k->vbox = gtk_vbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( m68k->vbox ),4 ); + gtk_container_add (GTK_CONTAINER (m68k), m68k->vbox); + + m68k->hboxmain = gtk_hbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( m68k->hboxmain ),4 ); + gtk_box_pack_start( GTK_BOX( m68k->vbox ), m68k->hboxmain, FALSE, FALSE, 4 ); + + m68k->hbox = gtk_hbutton_box_new(); + gtk_container_set_border_width( GTK_CONTAINER( m68k->hbox ),4 ); + gtk_box_pack_start( GTK_BOX( m68k->vbox ), m68k->hbox, FALSE, FALSE, 4 ); + + m68k->vboxmain = gtk_vbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( m68k->vboxmain ),4 ); + gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->vboxmain, FALSE, FALSE, 4 ); + + /* unassembler frame */ + + m68k->uFrame = gtk_frame_new("Disassembled code"); + gtk_box_pack_start( GTK_BOX( m68k->vboxmain ), m68k->uFrame, FALSE, FALSE, 4 ); + + m68k->uLabel = gtk_label_new(NULL); + gtk_container_add (GTK_CONTAINER (m68k->uFrame), m68k->uLabel ); + + /* Register list */ + + m68k->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); + m68k->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(m68k->regListStore) ); + m68k->regListRenderer1 = gtk_cell_renderer_text_new(); + m68k->regListRenderer2 = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(m68k->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + m68k->regListColumn1 = gtk_tree_view_column_new_with_attributes("Register", m68k->regListRenderer1, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->regList), m68k->regListColumn1); + m68k->regListColumn2 = gtk_tree_view_column_new_with_attributes("Value", m68k->regListRenderer2, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->regList), m68k->regListColumn2); + gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->regList, FALSE, FALSE, 4 ); + g_signal_connect(G_OBJECT(m68k->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_m68k_editedReg), m68k ); + + /* breakpoint list */ + + m68k->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); + m68k->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(m68k->bpListStore) ); + m68k->bpListRenderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(m68k->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + m68k->bpListColumn = gtk_tree_view_column_new_with_attributes("Breakpoints", m68k->bpListRenderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->bpList), m68k->bpListColumn); + gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->bpList, FALSE, FALSE, 4 ); + g_signal_connect(G_OBJECT(m68k->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_m68k_editedBp), m68k ); + + g_signal_connect(G_OBJECT(m68k), "delete-event", GTK_SIGNAL_FUNC(yui_m68k_destroy), NULL); +} + + +GtkWidget * yui_m68k_new(YuiWindow * y) { + GtkWidget * dialog; + GClosure *closureF7; + GtkAccelGroup *accelGroup; + const m68kcodebreakpoint_struct *cbp; + gint i; + yui = y; + + if ( yui_m68k ) return GTK_WIDGET(yui_m68k); + + dialog = GTK_WIDGET(g_object_new(yui_m68k_get_type(), NULL)); + yui_m68k = YUI_M68K(dialog); + + if (!( yui->state & YUI_IS_INIT )) { + yui_window_run(yui); + yui_window_pause(yui); + } + + M68KSetBreakpointCallBack(&yui_m68k_breakpoint_handler); + + for (i = 0; i < 23 ; i++) { + + GtkTreeIter iter; + gtk_list_store_append( GTK_LIST_STORE( yui_m68k->regListStore ), &iter ); + } + + cbp = M68KGetBreakpointList(); + + for (i = 0; i < MAX_BREAKPOINTS ; i++) { + + GtkTreeIter iter; + yui_m68k->cbp[i] = cbp[i].addr; + gtk_list_store_append( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter ); + if (cbp[i].addr != 0xFFFFFFFF) { + + gchar tempstr[20]; + sprintf(tempstr, "%08X", (int)cbp[i].addr); + gtk_list_store_set( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter, 0, tempstr, -1 ); + } else gtk_list_store_set( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter, 0, "", -1 ); + } + + { + GtkWidget * but2, * but3, * but4; + + yui_m68k->buttonStep = gtk_button_new_with_label( "Step [F7]" ); + gtk_box_pack_start( GTK_BOX( yui_m68k->hbox ), yui_m68k->buttonStep, FALSE, FALSE, 2 ); + g_signal_connect( yui_m68k->buttonStep, "clicked", G_CALLBACK(yui_m68k_step), yui_m68k ); + + but2 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); + gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but2, FALSE, FALSE, 2); + + but3 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); + gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but3, FALSE, FALSE, 2); + + but4 = gtk_button_new_from_stock("gtk-close"); + g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_m68k_destroy), dialog); + gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but4, FALSE, FALSE, 2); + } + yui_m68k->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_m68k_update), yui_m68k); + yui_m68k->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_m68k_clear), yui_m68k); + accelGroup = gtk_accel_group_new (); + closureF7 = g_cclosure_new (G_CALLBACK (yui_m68k_step), yui_m68k, NULL); + gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); + gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); + + yui_m68k_update(yui_m68k); + if ( yui->state & YUI_IS_RUNNING ) yui_m68k_clear(yui_m68k); + + gtk_widget_show_all(GTK_WIDGET(yui_m68k)); + + return dialog; +} + +static void yui_m68k_update_reglist( YuiM68k *m68k, m68kregs_struct *regs) { + /* refresh the registery list */ + + GtkTreeIter iter; + char regstr[32]; + char valuestr[32]; + int i; + + for ( i = 0 ; i < 8 ; i++ ) { + + if ( i==0 ) gtk_tree_model_get_iter_first( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); + else gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); + sprintf(regstr, "D%d", i ); + sprintf(valuestr, "%08x", (int)regs->D[i]); + gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); + } + for ( i = 0 ; i < 8 ; i++ ) { + + gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); + sprintf(regstr, "A%d", i ); + sprintf(valuestr, "%08x", (int)regs->A[i]); + gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); + } + + gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); + sprintf(valuestr, "%08x", (int)regs->SR); + gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, "SR", 1, valuestr, -1 ); + + gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); + sprintf(valuestr, "%08x", (int)regs->PC); + gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, "PC", 1, valuestr, -1 ); +} + +static void m68ksetRegister( YuiM68k *m68k, int nReg, u32 value ) { + /* set register number to value in proc */ + + m68kregs_struct m68kregs; + M68KGetRegisters(&m68kregs); + + if ( nReg < 8 ) m68kregs.D[nReg] = value; + else if ( nReg < 16 ) m68kregs.A[nReg-8] = value; + if ( nReg == 16 ) m68kregs.SR = value; + if ( nReg == 17 ) m68kregs.PC = value; + + M68KSetRegisters(&m68kregs); +} + +static void yui_m68k_update_codelist( YuiM68k *m68k, u32 addr) { + /* refrem68k the assembler view. points the line to be highlighted. */ + + int i; + static char tagPC[] = ""; + static char tagEnd[] = "\n"; + char buf[64*24+40]; + char *curs = buf; + char lineBuf[64]; + u8 bOnPC = 0; + u32 offset; + + if ( addr - m68k->lastCode >= 22 ) offset = addr; + else offset = m68k->lastCode; + m68k->lastCode = offset; + + for (i=0; i < 24; i++) { + + if ( offset == addr ) { bOnPC = 1; strcpy( curs, tagPC ); curs += strlen(tagPC); } + offset = M68KDisasm(offset, lineBuf); + strcpy( curs, lineBuf ); + curs += strlen(lineBuf); + if ( bOnPC ) { bOnPC = 0; strcpy( curs, tagEnd ); curs += strlen(tagEnd); } + else { strcpy( curs, "\n" ); curs += 1;} + } + *curs = 0; + gtk_label_set_markup( GTK_LABEL(m68k->uLabel), buf ); +} + +static void yui_m68k_step( GtkWidget* widget, YuiM68k * m68k ) { + + M68KStep(); + yui_window_invalidate( yui ); /* update all dialogs, including us */ +} + +static void yui_m68k_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiM68k *m68k) { + /* registry number value has been set to */ + + GtkTreeIter iter; + char bptext[10]; + char *endptr; + int i = atoi(arg1); + u32 addr; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( m68k->regListStore ), &iter, arg1 ); + addr = strtoul(arg2, &endptr, 16 ); + if ( endptr - arg2 == strlen(arg2) ) { + + sprintf(bptext, "%08X", (int)addr); + m68ksetRegister( m68k, i, addr ); + gtk_list_store_set( GTK_LIST_STORE( m68k->regListStore ), &iter, 1, bptext, -1 ); + } + yui_window_invalidate( yui ); +} + +static void yui_m68k_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiM68k *m68k) { + /* breakpoint has been set to address */ + + GtkTreeIter iter; + char bptext[10]; + char *endptr; + int i = atoi(arg1); + u32 addr; + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( m68k->bpListStore ), &iter, arg1 ); + addr = strtoul(arg2, &endptr, 16 ); + if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; + if ( m68k->cbp[i] != 0xFFFFFFFF) M68KDelCodeBreakpoint(m68k->cbp[i]); + m68k->cbp[i] = 0xFFFFFFFF; + + if ((addr!=0xFFFFFFFF)&&(M68KAddCodeBreakpoint(addr) == 0)) { + + sprintf(bptext, "%08X", (int)addr); + m68k->cbp[i] = addr; + } else strcpy(bptext,""); + gtk_list_store_set( GTK_LIST_STORE( m68k->bpListStore ), &iter, 0, bptext, -1 ); +} + +static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ + + while ( !(yui->state & YUI_IS_RUNNING) ) + if ( gtk_main_iteration() ) return; +} + +static void yui_m68k_breakpoint_handler (u32 addr) { + + yui_window_pause(yui); + { + m68kregs_struct regs; + YuiM68k* m68k = YUI_M68K(yui_m68k_new( yui )); + + M68KGetRegisters(®s); + yui_m68k_update_codelist(m68k,regs.PC); + yui_m68k_update_reglist(m68k,®s); + } + debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ +} + + +void yui_m68k_update(YuiM68k * m68k) { + m68kregs_struct m68kregs; + M68KGetRegisters(&m68kregs); + yui_m68k_update_codelist(m68k,m68kregs.PC); + yui_m68k_update_reglist(m68k, &m68kregs); + gtk_widget_set_sensitive(m68k->uLabel, TRUE); + gtk_widget_set_sensitive(m68k->bpList, TRUE); + gtk_widget_set_sensitive(m68k->regList, TRUE); + gtk_widget_set_sensitive(m68k->buttonStep, TRUE); +} + +void yui_m68k_destroy(YuiM68k * m68k) { + g_signal_handler_disconnect(yui, m68k->running_handler); + g_signal_handler_disconnect(yui, m68k->paused_handler); + + yui_m68k = NULL; + + gtk_widget_destroy(GTK_WIDGET(m68k)); +} + +static void yui_m68k_clear(YuiM68k * m68k) { + + gtk_widget_set_sensitive(m68k->uLabel, FALSE); + gtk_widget_set_sensitive(m68k->bpList, FALSE); + gtk_widget_set_sensitive(m68k->regList, FALSE); + gtk_widget_set_sensitive(m68k->buttonStep, FALSE); +} diff --git a/yabause/src/gtk/yuim68k.h b/yabause/src/gtk/yuim68k.h new file mode 100644 index 0000000000..6b1fae9030 --- /dev/null +++ b/yabause/src/gtk/yuim68k.h @@ -0,0 +1,72 @@ +/* Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_M68K_H +#define YUI_M68K_H + +#include +#include +#include + +#include "../scsp.h" +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_M68K_TYPE (yui_m68k_get_type ()) +#define YUI_M68K(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_M68K_TYPE, YuiM68k)) +#define YUI_M68K_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_M68K_TYPE, YuiM68kClass)) +#define IS_YUI_M68K(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_M68K_TYPE)) +#define IS_YUI_M68K_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_M68K_TYPE)) + +typedef struct _YuiM68k YuiM68k; +typedef struct _YuiM68kClass YuiM68kClass; + +struct _YuiM68k +{ + GtkWindow dialog; + + GtkWidget *vbox, *vboxmain; + GtkWidget *hbox, *hboxmain; + GtkWidget * buttonStep, * buttonStepOver; + GtkWidget * bpList, *regList, *uLabel, *uFrame; + GtkListStore *bpListStore, *regListStore; + GtkCellRenderer *bpListRenderer, *regListRenderer1, *regListRenderer2; + GtkTreeViewColumn *bpListColumn, *regListColumn1, *regListColumn2; + u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ + u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ + gulong paused_handler; + gulong running_handler; +}; + +struct _YuiM68kClass +{ + GtkWindowClass parent_class; + + void (* yui_m68k) (YuiM68k * yv); +}; + +GType yui_m68k_get_type (void); +GtkWidget* yui_m68k_new(YuiWindow * y); +void yui_m68k_update (YuiM68k * m68k); +void yui_m68k_destroy (YuiM68k * sh); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuimem.c b/yabause/src/gtk/yuimem.c new file mode 100644 index 0000000000..6100ca387d --- /dev/null +++ b/yabause/src/gtk/yuimem.c @@ -0,0 +1,301 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuimem.h" +#include "settings.h" + +static void yui_mem_class_init (YuiMemClass * klass); +static void yui_mem_init (YuiMem * yfe); +static void yui_mem_clear (YuiMem * vdp1); +static void yui_mem_address_changed (GtkWidget * w, YuiMem * ym); +static void yui_mem_content_changed ( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiMem *ym); +static gboolean yui_mem_pagedown_pressed (GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym); +static gboolean yui_mem_pageup_pressed (GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym); +static void yui_mem_update (YuiMem * ym); +static void yui_mem_combo_changed(GtkWidget * w, YuiMem * ym); +static void yui_mem_pagedown_clicked (GtkToolButton * button, YuiMem * ym); +static void yui_mem_pageup_clicked (GtkToolButton * button, YuiMem * ym); + +struct { gchar* name; u32 address; } quickAddress[] = + { {"VDP2_VRAM_A0", 0x25E00000 }, + {"VDP2_VRAM_A1", 0x25E20000 }, + {"VDP2_VRAM_B0", 0x25E40000 }, + {"VDP2_VRAM_B1", 0x25E60000 }, + {"VDP2_CRAM", 0x25F00000 }, + {"LWRAM", 0x20200000 }, + {"HWRAM", 0x26000000 }, + {"SpriteVRAM", 0x25C00000 }, + {0, 0 } }; + +GType yui_mem_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiMemClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_mem_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiMem), + 0, + (GInstanceInitFunc) yui_mem_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiMem", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_mem_class_init (UNUSED YuiMemClass * klass) { +} + +static void yui_mem_init (YuiMem * yv) { + GtkWidget * view; + GtkCellRenderer * renderer; + GtkTreeViewColumn * column; + GtkAccelGroup *accelGroup; + GtkToolItem * comboItem, * upbutton, * downbutton; + GtkWidget * testbox, * vbox; + gint i; + + gtk_window_set_title(GTK_WINDOW(yv), "Memory dump"); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(yv), vbox); + + yv->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); + gtk_box_pack_start(GTK_BOX(vbox), yv->toolbar, FALSE, FALSE, 0); + + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), gtk_separator_tool_item_new(), 0); + + comboItem = gtk_tool_item_new(); + gtk_tool_item_set_expand(comboItem, FALSE); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), comboItem, 1); + + downbutton = gtk_tool_button_new_from_stock(GTK_STOCK_GO_DOWN); + g_signal_connect(downbutton, "clicked", G_CALLBACK(yui_mem_pagedown_clicked), yv); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), downbutton, 2); + + upbutton = gtk_tool_button_new_from_stock(GTK_STOCK_GO_UP); + g_signal_connect(upbutton, "clicked", G_CALLBACK(yui_mem_pageup_clicked), yv); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), upbutton, 3); + + yv->quickCombo = gtk_combo_box_entry_new_text(); + + gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(yv->quickCombo))), 8); + + for ( i = 0 ; quickAddress[i].name ; i++ ) + gtk_combo_box_insert_text( GTK_COMBO_BOX( yv->quickCombo ), i, quickAddress[i].name ); + gtk_combo_box_set_active( GTK_COMBO_BOX(yv->quickCombo), 0 ); + g_signal_connect(yv->quickCombo, "changed", G_CALLBACK(yui_mem_combo_changed), yv ); + g_signal_connect(gtk_bin_get_child(GTK_BIN(yv->quickCombo)), "activate", G_CALLBACK(yui_mem_address_changed), yv ); + + testbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(testbox), yv->quickCombo, TRUE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(comboItem), testbox); + + yv->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Address", renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Dump", renderer, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + g_object_set(G_OBJECT(renderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + g_signal_connect(G_OBJECT(renderer), "edited", GTK_SIGNAL_FUNC(yui_mem_content_changed), yv ); + gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0); + + g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_mem_destroy), NULL); + + accelGroup = gtk_accel_group_new (); + gtk_accel_group_connect( accelGroup, GDK_Page_Up, 0, 0, + g_cclosure_new (G_CALLBACK(yui_mem_pageup_pressed), yv, NULL) ); + gtk_accel_group_connect( accelGroup, GDK_Page_Down, 0, 0, + g_cclosure_new (G_CALLBACK(yui_mem_pagedown_pressed), yv, NULL) ); + gtk_window_add_accel_group( GTK_WINDOW( yv ), accelGroup ); + + yv->address = 0; + yv->wLine = 8; + + gtk_window_set_default_size(GTK_WINDOW(yv), 300, -1); +} + +GtkWidget * yui_mem_new(YuiWindow * y) { + GtkWidget * dialog; + YuiMem * yv; + + dialog = GTK_WIDGET(g_object_new(yui_mem_get_type(), NULL)); + yv = YUI_MEM(dialog); + + yv->yui = y; + + if (!( yv->yui->state & YUI_IS_INIT )) { + yui_window_run(yv->yui); + yui_window_pause(yv->yui); + } + + { + GtkToolItem * play_button, * pause_button; + + play_button = gtk_tool_button_new_from_stock("run"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), 0); + + pause_button = gtk_tool_button_new_from_stock("pause"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), 1); + } + + yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_mem_update), yv); + yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_mem_clear), yv); + + if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) + yui_mem_update(yv); + + gtk_widget_show_all(GTK_WIDGET(yv)); + + return dialog; +} + +void yui_mem_destroy(YuiMem * ym) { + g_signal_handler_disconnect(ym->yui, ym->running_handler); + g_signal_handler_disconnect(ym->yui, ym->paused_handler); + + gtk_widget_destroy(GTK_WIDGET(ym)); +} + +static void yui_mem_clear(YuiMem * vdp1) { +} + +static void yui_mem_address_changed(GtkWidget * w, YuiMem * ym) { + sscanf(gtk_entry_get_text(GTK_ENTRY(w)), "%x", &ym->address); + yui_mem_update(ym); +} + +static void yui_mem_combo_changed(GtkWidget * w, YuiMem * ym) { + + gint i = gtk_combo_box_get_active( GTK_COMBO_BOX(w) ); + + if (i > -1) { + ym->address = quickAddress[i].address; + yui_mem_update(ym); + } +} + +static gint hexaDigitToInt( gchar c ) { + + if (( c >= '0' )&&( c <= '9' )) return c-'0'; + if (( c >= 'a' )&&( c <= 'f' )) return c-'a' + 0xA; + if (( c >= 'A' )&&( c <= 'F' )) return c-'A' + 0xA; + return -1; +} + +static gboolean yui_mem_pageup_pressed(GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym) { + + ym->address -= 2*ym->wLine; + yui_mem_update(ym); + + return TRUE; +} + +static gboolean yui_mem_pagedown_pressed(GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym) { + + ym->address += 2*ym->wLine; + yui_mem_update(ym); + + return TRUE; +} + +static void yui_mem_pagedown_clicked (GtkToolButton * button, YuiMem * ym) { + ym->address += 2*ym->wLine; + yui_mem_update(ym); +} + +static void yui_mem_pageup_clicked (GtkToolButton * button, YuiMem * ym) { + ym->address -= 2*ym->wLine; + yui_mem_update(ym); +} + +static void yui_mem_content_changed( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiMem *ym) { + /* dump line has been modified - new content is */ + + GtkTreeIter iter; + gint i = atoi(arg1); + gint j,k; + gchar *curs; + u32 addr = ym->address + i*ym->wLine; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( ym->store ), &iter, arg1 ); + + /* check the format : wLine*2 hexa digits */ + for ( curs = arg2, j=0 ; *curs ; curs++ ) + if ( hexaDigitToInt( *curs ) != -1 ) j++; + + if ( j != ym->wLine * 2 ) return; + + /* convert */ + for ( curs = arg2, k=-1 ; *curs ; curs++ ) { + + if ( hexaDigitToInt( *curs )!=-1 ) { + + if ( k==-1 ) k = hexaDigitToInt( *curs ); + else { MappedMemoryWriteByte( addr++, 16*k + hexaDigitToInt( *curs ) ); k = -1; + } + } + } + yui_window_invalidate(ym->yui); +} + +static void yui_mem_update(YuiMem * ym) { + int i, j; + GtkTreeIter iter; + char address[10]; + char dump[30]; + + gtk_list_store_clear(ym->store); + + for(i = 0;i < 6;i++) { + sprintf(address, "%08x", ym->address + (8 * i)); + for(j = 0;j < 8;j++) { + sprintf(dump + (j * 3), "%02x ", MappedMemoryReadByte(ym->address + (8 * i) + j)); + } + + gtk_list_store_append(ym->store, &iter); + gtk_list_store_set(GTK_LIST_STORE(ym->store ), &iter, 0, address, 1, dump, -1); + } + + sprintf( address, "%08X", ym->address ); + gtk_entry_set_text( GTK_ENTRY(gtk_bin_get_child(GTK_BIN(ym->quickCombo))), address ); +} diff --git a/yabause/src/gtk/yuimem.h b/yabause/src/gtk/yuimem.h new file mode 100644 index 0000000000..c597070b43 --- /dev/null +++ b/yabause/src/gtk/yuimem.h @@ -0,0 +1,72 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_MEM_H +#define YUI_MEM_H + +#include +#include +#include + +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_MEM_TYPE (yui_mem_get_type ()) +#define YUI_MEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_MEM_TYPE, YuiMem)) +#define YUI_MEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_MEM_TYPE, YuiMemClass)) +#define IS_YUI_MEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_MEM_TYPE)) +#define IS_YUI_MEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_MEM_TYPE)) + +typedef struct _YuiMem YuiMem; +typedef struct _YuiMemClass YuiMemClass; + +struct _YuiMem +{ + GtkWindow dialog; + + GtkWidget * toolbar; + + GtkListStore * store; + GtkWidget * quickCombo; + + guint wLine; + guint32 address; + gulong paused_handler; + gulong running_handler; + + YuiWindow * yui; +}; + +struct _YuiMemClass +{ + GtkWindowClass parent_class; + + void (* yui_mem) (YuiMem * yv); +}; + +GType yui_mem_get_type(void); +GtkWidget * yui_mem_new (YuiWindow * yui); +void yui_mem_fill (YuiMem * vdp1); +void yui_mem_destroy (YuiMem * vdp1); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuipage.c b/yabause/src/gtk/yuipage.c new file mode 100644 index 0000000000..2259ed956c --- /dev/null +++ b/yabause/src/gtk/yuipage.c @@ -0,0 +1,95 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "yuifileentry.h" +#include "yuirange.h" +#include "yuiresolution.h" +#include "yuipage.h" +#include "../core.h" + +static void yui_page_class_init (YuiPageClass * klass); +static void yui_page_init (YuiPage * yfe); + +GType yui_page_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiPageClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_page_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiPage), + 0, + (GInstanceInitFunc) yui_page_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_VBOX, "YuiPage", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_page_class_init (UNUSED YuiPageClass * klass) { +} + +static void yui_page_init (UNUSED YuiPage * yp) { +} + +GtkWidget * yui_page_new(GKeyFile * keyfile) { + GtkWidget * widget; + YuiPage * yp; + + widget = GTK_WIDGET(g_object_new(yui_page_get_type(), NULL)); + yp = YUI_PAGE(widget); + + yp->keyfile = keyfile; + + return widget; +} + +GtkWidget * yui_page_add(YuiPage * yp, const gchar * name) { + GtkWidget * label; + GtkWidget * frame; + GtkWidget * box; + gchar buffer[1024]; + + frame = gtk_frame_new(NULL); + + gtk_box_pack_start(GTK_BOX(yp), frame, FALSE, TRUE, 0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); + + box = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), box); + + sprintf(buffer, "%s", name); + + label = gtk_label_new(buffer); + gtk_frame_set_label_widget(GTK_FRAME(frame), label); + gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + + return box; +} diff --git a/yabause/src/gtk/yuipage.h b/yabause/src/gtk/yuipage.h new file mode 100644 index 0000000000..8bd767e7d6 --- /dev/null +++ b/yabause/src/gtk/yuipage.h @@ -0,0 +1,63 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_PAGE_H +#define YUI_PAGE_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_PAGE_TYPE (yui_page_get_type ()) +#define YUI_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_PAGE_TYPE, YuiPage)) +#define YUI_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_PAGE_TYPE, YuiPageClass)) +#define IS_YUI_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_PAGE_TYPE)) +#define IS_YUI_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_PAGE_TYPE)) + +#define YUI_FILE_SETTING 1 +#define YUI_RANGE_SETTING 2 +#define YUI_RESOLUTION_SETTING 3 + +typedef struct _YuiPage YuiPage; +typedef struct _YuiPageClass YuiPageClass; + +struct _YuiPage +{ + GtkVBox vbox; + + GKeyFile * keyfile; +}; + +struct _YuiPageClass +{ + GtkHBoxClass parent_class; + + void (* yui_page) (YuiPage * yfe); +}; + +GType yui_page_get_type (void); +GtkWidget * yui_page_new (GKeyFile * keyfile); + +GtkWidget * yui_page_add (YuiPage * yp, const gchar * name); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuirange.c b/yabause/src/gtk/yuirange.c new file mode 100644 index 0000000000..dc8efeba7d --- /dev/null +++ b/yabause/src/gtk/yuirange.c @@ -0,0 +1,152 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuirange.h" + +static void yui_range_class_init (YuiRangeClass * klass); +static void yui_range_init (YuiRange * yfe); +static void yui_range_changed (GtkWidget * widget, YuiRange * yfe); + +GType yui_range_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiRangeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_range_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiRange), + 0, + (GInstanceInitFunc) yui_range_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_HBOX, "YuiRange", &yfe_info, 0); + } + + return yfe_type; +} + +#define PROP_KEYFILE 1 +#define PROP_GROUP 2 +#define PROP_KEY 3 +#define PROP_ITEMS 4 + +static void yui_range_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) { + switch(property_id) { + case PROP_KEYFILE: + YUI_RANGE(object)->keyfile = g_value_get_pointer(value); + break; + case PROP_GROUP: + YUI_RANGE(object)->group = g_value_get_pointer(value); + break; + case PROP_KEY: + YUI_RANGE(object)->key = g_value_get_pointer(value); + break; + case PROP_ITEMS: + YUI_RANGE(object)->items = g_value_get_pointer(value); + break; + } +} + +static void yui_range_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) { +} + +enum { YUI_RANGE_CHANGED_SIGNAL, LAST_SIGNAL }; + +static guint yui_range_signals[LAST_SIGNAL] = { 0 }; + +static void yui_range_class_init (YuiRangeClass * klass) { + GParamSpec * param; + + G_OBJECT_CLASS(klass)->set_property = yui_range_set_property; + G_OBJECT_CLASS(klass)->get_property = yui_range_get_property; + + param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); + + param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); + + param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); + + param = g_param_spec_pointer("items", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEMS, param); + + yui_range_signals[YUI_RANGE_CHANGED_SIGNAL] = g_signal_new ("changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(YuiRangeClass, yui_range_change), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void yui_range_init (YuiRange * yfe) { + gtk_container_set_border_width(GTK_CONTAINER(yfe), 10); + + yfe->combo = gtk_combo_box_new_text(); + + gtk_box_pack_start(GTK_BOX(yfe), yfe->combo, TRUE, TRUE, 0); +} + +GtkWidget * yui_range_new(GKeyFile * keyfile, const gchar * group, const gchar * key, YuiRangeItem * items) { + GtkWidget * entry; + YuiRange * yfe; + gchar * current; + guint i; + + entry = GTK_WIDGET(g_object_new(yui_range_get_type(), "spacing", 10, + "key-file", keyfile, "group", group, "key", key, "items", items, NULL)); + yfe = YUI_RANGE(entry); + + current = g_key_file_get_value(yfe->keyfile, yfe->group, yfe->key, 0); + i = 0; + while(yfe->items[i].name) { + gtk_combo_box_append_text(GTK_COMBO_BOX(yfe->combo), yfe->items[i].name); + if (current && !strcmp(yfe->items[i].value, current)) + gtk_combo_box_set_active(GTK_COMBO_BOX(yfe->combo), i); + i++; + } + if ( !current ) { + gtk_combo_box_set_active(GTK_COMBO_BOX(yfe->combo), 0); + g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, items[0].value); + } + + g_signal_connect(GTK_COMBO_BOX(yfe->combo), "changed", G_CALLBACK(yui_range_changed), yfe); + + return entry; +} + +static void yui_range_changed(GtkWidget * entry, YuiRange * yfe) { + g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, + yfe->items[gtk_combo_box_get_active(GTK_COMBO_BOX(yfe->combo))].value); + g_signal_emit(G_OBJECT(yfe), yui_range_signals[YUI_RANGE_CHANGED_SIGNAL], 0); +} + +gint yui_range_get_active(YuiRange * range) { + return gtk_combo_box_get_active(GTK_COMBO_BOX(range->combo)); +} diff --git a/yabause/src/gtk/yuirange.h b/yabause/src/gtk/yuirange.h new file mode 100644 index 0000000000..f973c87e4b --- /dev/null +++ b/yabause/src/gtk/yuirange.h @@ -0,0 +1,72 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_RANGE_H +#define YUI_RANGE_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_RANGE_TYPE (yui_range_get_type ()) +#define YUI_RANGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_RANGE_TYPE, YuiRange)) +#define YUI_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_RANGE_TYPE, YuiRangeClass)) +#define IS_YUI_RANGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_RANGE_TYPE)) +#define IS_YUI_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_RANGE_TYPE)) + +typedef struct _YuiRangeItem YuiRangeItem; +typedef struct _YuiRange YuiRange; +typedef struct _YuiRangeClass YuiRangeClass; + +struct _YuiRangeItem +{ + const gchar * value; + const gchar * name; +}; + +struct _YuiRange +{ + GtkHBox hbox; + + GtkWidget * combo; + + GKeyFile * keyfile; + gchar * group; + gchar * key; + + YuiRangeItem * items; +}; + +struct _YuiRangeClass +{ + GtkHBoxClass parent_class; + + void (* yui_range_change) (YuiRange * yfe); +}; + +GType yui_range_get_type (void); +GtkWidget * yui_range_new (GKeyFile * keyfile, const gchar * group, const gchar * key, YuiRangeItem * items); +gint yui_range_get_active (YuiRange * range); + +G_END_DECLS + +#endif /* YUI_RANGE_H */ diff --git a/yabause/src/gtk/yuiresolution.c b/yabause/src/gtk/yuiresolution.c new file mode 100644 index 0000000000..41a7afa6c5 --- /dev/null +++ b/yabause/src/gtk/yuiresolution.c @@ -0,0 +1,170 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "yuiresolution.h" + +static void yui_resolution_class_init (YuiResolutionClass *klass); +static void yui_resolution_init (YuiResolution *yie); +static void yui_resolution_width_changed (GtkWidget * w, YuiResolution * yr); +static void yui_resolution_height_changed (GtkWidget * w, YuiResolution * yr); +static void yui_resolution_options_changed (GtkWidget * w, YuiResolution * yr); + +GType yui_resolution_get_type (void) { + static GType yie_type = 0; + + if (!yie_type) { + static const GTypeInfo yie_info = { + sizeof (YuiResolutionClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_resolution_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiResolution), + 0, + (GInstanceInitFunc) yui_resolution_init, + NULL, + }; + + yie_type = g_type_register_static (GTK_TYPE_HBOX, "YuiResolution", &yie_info, 0); + } + + return yie_type; +} + +#define PROP_KEYFILE 1 +#define PROP_GROUP 2 + +static void yui_resolution_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) { + switch(property_id) { + case PROP_KEYFILE: + YUI_RESOLUTION(object)->keyfile = g_value_get_pointer(value); + break; + case PROP_GROUP: + YUI_RESOLUTION(object)->group = g_value_get_pointer(value); + break; + } +} + +static void yui_resolution_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) { +} + +static void yui_resolution_class_init (YuiResolutionClass *klass) { + GParamSpec * param; + + G_OBJECT_CLASS(klass)->set_property = yui_resolution_set_property; + G_OBJECT_CLASS(klass)->get_property = yui_resolution_get_property; + + param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); + + param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); +} + +static void yui_resolution_init(YuiResolution * yr) { + GtkWidget * label_w; + GtkWidget * label_h; + + gtk_container_set_border_width (GTK_CONTAINER (yr), 10); + + label_w = gtk_label_new ("Width"); + gtk_box_pack_start(GTK_BOX(yr), label_w, TRUE, TRUE, 0); + + yr->entry_w = gtk_entry_new (); + gtk_entry_set_width_chars(GTK_ENTRY(yr->entry_w), 8); + gtk_box_pack_start(GTK_BOX(yr), yr->entry_w, TRUE, TRUE, 0); + + label_h = gtk_label_new ("Height"); + gtk_box_pack_start(GTK_BOX(yr), label_h, TRUE, TRUE, 0); + + yr->entry_h = gtk_entry_new (); + gtk_entry_set_width_chars(GTK_ENTRY(yr->entry_h), 8); + gtk_box_pack_start(GTK_BOX(yr), yr->entry_h, TRUE, TRUE, 0); + + yr->options = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Window"); + gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Fullscreen"); + gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Keep ratio"); + gtk_box_pack_start(GTK_BOX(yr), yr->options, TRUE, TRUE, 0); + + g_signal_connect(yr->entry_w, "changed", G_CALLBACK(yui_resolution_width_changed), yr); + g_signal_connect(yr->entry_h, "changed", G_CALLBACK(yui_resolution_height_changed), yr); + g_signal_connect(yr->options, "changed", G_CALLBACK(yui_resolution_options_changed), yr); +} + +GtkWidget* yui_resolution_new(GKeyFile * keyfile, const gchar * group) { + GtkWidget * widget; + YuiResolution * yr; + gchar *widthText, *heightText; + + widget = GTK_WIDGET(g_object_new(yui_resolution_get_type(), "key-file", keyfile, "group", group, NULL)); + yr = YUI_RESOLUTION(widget); + + widthText = g_key_file_get_value(yr->keyfile, yr->group, "Width", 0); + if ( !widthText ) widthText = ""; + heightText = g_key_file_get_value(yr->keyfile, yr->group, "Height", 0); + if ( !heightText ) heightText = ""; + gtk_entry_set_text(GTK_ENTRY(yr->entry_w), widthText ); + gtk_entry_set_text(GTK_ENTRY(yr->entry_h), heightText ); + if (g_key_file_get_integer(yr->keyfile, yr->group, "Fullscreen", 0) == 1) + gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 1); + else if (g_key_file_get_integer(yr->keyfile, yr->group, "KeepRatio", 0) == 1) + gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 2); + else + gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 0); + + return widget; +} + +static void yui_resolution_width_changed(GtkWidget * w, YuiResolution * yr) { + g_key_file_set_value(yr->keyfile, yr->group, "Width", gtk_entry_get_text(GTK_ENTRY(w))); +} + +static void yui_resolution_height_changed(GtkWidget * w, YuiResolution * yr) { + g_key_file_set_value(yr->keyfile, yr->group, "Height", gtk_entry_get_text(GTK_ENTRY(w))); +} + +static void yui_resolution_options_changed(GtkWidget * w, YuiResolution * yr) { + gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(yr->options)); + switch(active) { + case 0: + g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 0); + g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 0); + break; + case 1: + g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 1); + g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 0); + break; + case 2: + g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 0); + g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 1); + break; + } +} diff --git a/yabause/src/gtk/yuiresolution.h b/yabause/src/gtk/yuiresolution.h new file mode 100644 index 0000000000..ae9088e3e8 --- /dev/null +++ b/yabause/src/gtk/yuiresolution.h @@ -0,0 +1,63 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_RESOLUTION_H +#define YUI_RESOLUTION_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_RESOLUTION_TYPE (yui_resolution_get_type ()) +#define YUI_RESOLUTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_RESOLUTION_TYPE, YuiResolution)) +#define YUI_RESOLUTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_RESOLUTION_TYPE, YuiResolutionClass)) +#define IS_YUI_RESOLUTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_RESOLUTION_TYPE)) +#define IS_YUI_RESOLUTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_RESOLUTION_TYPE)) + + +typedef struct _YuiResolution YuiResolution; +typedef struct _YuiResolutionClass YuiResolutionClass; + +struct _YuiResolution +{ + GtkHBox table; + + GtkWidget * entry_w; + GtkWidget * entry_h; + GtkWidget * options; + + GKeyFile * keyfile; + gchar * group; +}; + +struct _YuiResolutionClass { + GtkHBoxClass parent_class; + + void (* yui_resolution) (YuiResolution *yie); +}; + +GType yui_resolution_get_type (void); +GtkWidget* yui_resolution_new (GKeyFile * keyfile, const gchar * group); + +G_END_DECLS + +#endif /* YUI_RESOLUTION_H */ diff --git a/yabause/src/gtk/yuiscreenshot.c b/yabause/src/gtk/yuiscreenshot.c new file mode 100644 index 0000000000..5f53f1ea24 --- /dev/null +++ b/yabause/src/gtk/yuiscreenshot.c @@ -0,0 +1,127 @@ +/* Copyright 2006-2007 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "yuiscreenshot.h" +#include "gtkglwidget.h" +#include "yuiviewer.h" +#include "../core.h" + +static void yui_screenshot_class_init (YuiScreenshotClass * klass); +static void yui_screenshot_init (YuiScreenshot * yfe); +static void yui_screenshot_update (YuiScreenshot * ys, gpointer data); +static gboolean yui_screenshot_draw(YuiScreenshot * ys); + +GType yui_screenshot_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiScreenshotClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_screenshot_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiScreenshot), + 0, + (GInstanceInitFunc) yui_screenshot_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScreenshot", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_screenshot_class_init (UNUSED YuiScreenshotClass * klass) { +} + +static YuiWindow * yui; + +static void yui_screenshot_init (YuiScreenshot * yv) { + GtkWidget * box; + GtkWidget * button_box; + GtkWidget * button; + + gtk_window_set_title(GTK_WINDOW(yv), "Screenshot"); + gtk_container_set_border_width(GTK_CONTAINER(yv), 4); + + box = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(yv), box); + + yv->image = yui_viewer_new(); + gtk_box_pack_start(GTK_BOX(box), yv->image, FALSE, FALSE, 0); + gtk_widget_set_size_request(GTK_WIDGET(yv->image), 320, 224); + + button_box = gtk_hbutton_box_new(); + gtk_box_pack_start(GTK_BOX(box), button_box, FALSE, FALSE, 0); + + button = gtk_button_new_from_stock(GTK_STOCK_REFRESH); + gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); + g_signal_connect_swapped(button, "clicked", G_CALLBACK(yui_screenshot_update), yv); + + button = gtk_button_new_from_stock(GTK_STOCK_SAVE); + gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); + g_signal_connect_swapped(button, "clicked", G_CALLBACK(yui_viewer_save), yv->image); + + button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); + g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), yv); +} + +GtkWidget * yui_screenshot_new(YuiWindow * y) { + GtkWidget * dialog; + YuiScreenshot * yv; + + yui = y; + + dialog = GTK_WIDGET(g_object_new(yui_screenshot_get_type(), NULL)); + yv = YUI_SCREENSHOT(dialog); + + gtk_widget_show_all(dialog); + + yui_gl_dump_screen(YUI_GL(yui->area)); + yui_screenshot_draw(yv); + + return dialog; +} + +static void yui_screenshot_update(YuiScreenshot * ys, UNUSED gpointer data) { + yui_gl_dump_screen(YUI_GL(yui->area)); + yui_screenshot_draw(ys); +} + +static gboolean yui_screenshot_draw(YuiScreenshot * ys) { + GdkPixbuf * pixbuf, * correct; + + pixbuf = gdk_pixbuf_new_from_data((const guchar *) YUI_GL(yui->area)->pixels, GDK_COLORSPACE_RGB, FALSE, 8, + YUI_GL(yui->area)->pixels_width, YUI_GL(yui->area)->pixels_height, YUI_GL(yui->area)->pixels_rowstride, NULL, NULL); + correct = gdk_pixbuf_flip(pixbuf, FALSE); + + yui_viewer_draw_pixbuf(YUI_VIEWER(ys->image), correct, YUI_GL(yui->area)->pixels_width, YUI_GL(yui->area)->pixels_height); + + g_object_unref(pixbuf); + g_object_unref(correct); + + return TRUE; +} diff --git a/yabause/src/gtk/yuiscreenshot.h b/yabause/src/gtk/yuiscreenshot.h new file mode 100644 index 0000000000..8a820f727d --- /dev/null +++ b/yabause/src/gtk/yuiscreenshot.h @@ -0,0 +1,56 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_SCREENSHOT_H +#define YUI_SCREENSHOT_H + +#include + +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_SCREENSHOT_TYPE (yui_screenshot_get_type ()) +#define YUI_SCREENSHOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCREENSHOT_TYPE, YuiScreenshot)) +#define YUI_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCREENSHOT_TYPE, YuiScreenshotClass)) +#define IS_YUI_SCREENSHOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCREENSHOT_TYPE)) +#define IS_YUI_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCREENSHOT_TYPE)) + +typedef struct _YuiScreenshot YuiScreenshot; +typedef struct _YuiScreenshotClass YuiScreenshotClass; + +struct _YuiScreenshot +{ + GtkWindow dialog; + + GtkWidget * image; +}; + +struct _YuiScreenshotClass +{ + GtkWindowClass parent_class; +}; + +GType yui_screenshot_get_type (void); +GtkWidget * yui_screenshot_new (YuiWindow * yui); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuiscsp.c b/yabause/src/gtk/yuiscsp.c new file mode 100644 index 0000000000..fd74c9f30a --- /dev/null +++ b/yabause/src/gtk/yuiscsp.c @@ -0,0 +1,159 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "yuiscsp.h" +#include "../scsp.h" +#include "../yabause.h" +#include "settings.h" + +static void yui_scsp_class_init (YuiScspClass * klass); +static void yui_scsp_init (YuiScsp * yfe); +static void yui_scsp_spin_cursor_changed(GtkWidget * spin, YuiScsp * scsp); +static void yui_scsp_clear(YuiScsp * scsp); + +GType yui_scsp_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiScspClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_scsp_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiScsp), + 0, + (GInstanceInitFunc) yui_scsp_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScsp", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_scsp_class_init (UNUSED YuiScspClass * klass) { +} + +static void yui_scsp_init (YuiScsp * yv) { + gtk_window_set_title(GTK_WINDOW(yv), "SCSP"); + + yv->vbox = gtk_vbox_new(FALSE, 2); + gtk_container_set_border_width(GTK_CONTAINER(yv->vbox), 4); + gtk_container_add(GTK_CONTAINER(yv), yv->vbox); + + yv->spin = gtk_spin_button_new_with_range(0, 31, 1); + gtk_spin_button_set_range(GTK_SPIN_BUTTON(yv->spin), 0, 31); + gtk_box_pack_start(GTK_BOX(yv->vbox), yv->spin, FALSE, FALSE, 4); + g_signal_connect(G_OBJECT(yv->spin), "value-changed", GTK_SIGNAL_FUNC(yui_scsp_spin_cursor_changed), yv); + + g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_scsp_destroy), NULL); + + { + GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); + GtkWidget * text = gtk_text_view_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); + yv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + gtk_container_add(GTK_CONTAINER(scroll), text); + gtk_box_pack_start(GTK_BOX(yv->vbox), scroll, TRUE, TRUE, 4); + } + + yv->hbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(yv->hbox), 4); + gtk_box_pack_start(GTK_BOX(yv->vbox ), yv->hbox, FALSE, FALSE, 4); + + yv->cursor = 0; +} + +static gulong paused_handler; +static gulong running_handler; +static YuiWindow * yui; + +GtkWidget * yui_scsp_new(YuiWindow * y) { + GtkWidget * dialog; + YuiScsp * yv; + + yui = y; + + dialog = GTK_WIDGET(g_object_new(yui_scsp_get_type(), NULL)); + yv = YUI_SCSP(dialog); + + { + GtkWidget * but2, * but3, * but4; + but2 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); + gtk_box_pack_start(GTK_BOX(yv->hbox), but2, FALSE, FALSE, 2); + + but3 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); + gtk_box_pack_start(GTK_BOX(yv->hbox), but3, FALSE, FALSE, 2); + + but4 = gtk_button_new_from_stock("gtk-close"); + g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_scsp_destroy), dialog); + gtk_box_pack_start(GTK_BOX(yv->hbox), but4, FALSE, FALSE, 2); + } + paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_scsp_update), yv); + running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_scsp_clear), yv); + + if ((yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) + yui_scsp_update(yv); + + gtk_widget_show_all(GTK_WIDGET(yv)); + + return dialog; +} + +void yui_scsp_fill(YuiScsp * scsp) { + gchar nameTemp[1024]; + + yui_scsp_clear(scsp); + + ScspSlotDebugStats(scsp->cursor, nameTemp ); + + gtk_text_buffer_set_text(scsp->buffer, g_strstrip(nameTemp), -1); +} + +static void yui_scsp_spin_cursor_changed(GtkWidget * spin, YuiScsp * scsp) { + scsp->cursor = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); + yui_scsp_fill(scsp); +} + +void yui_scsp_update(YuiScsp * scsp) { + yui_scsp_fill(scsp); +} + +void yui_scsp_destroy(YuiScsp * scsp) { + g_signal_handler_disconnect(yui, running_handler); + g_signal_handler_disconnect(yui, paused_handler); + + gtk_widget_destroy(GTK_WIDGET(scsp)); +} + +static void yui_scsp_clear(YuiScsp * scsp) { + gtk_text_buffer_set_text(scsp->buffer, "", -1); +} diff --git a/yabause/src/gtk/yuiscsp.h b/yabause/src/gtk/yuiscsp.h new file mode 100644 index 0000000000..1c2cb82f54 --- /dev/null +++ b/yabause/src/gtk/yuiscsp.h @@ -0,0 +1,71 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_SCSP_H +#define YUI_SCSP_H + +#include +#include +#include + +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_SCSP_TYPE (yui_scsp_get_type ()) +#define YUI_SCSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCSP_TYPE, YuiScsp)) +#define YUI_SCSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCSP_TYPE, YuiScspClass)) +#define IS_YUI_SCSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCSP_TYPE)) +#define IS_YUI_SCSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCSP_TYPE)) + +typedef struct _YuiScsp YuiScsp; +typedef struct _YuiScspClass YuiScspClass; + +struct _YuiScsp +{ + GtkWindow dialog; + + GtkWidget * vbox; + GtkWidget * hbox; + GtkWidget * commName; + GtkWidget * commDesc; + GtkWidget * spin; + + GtkTextBuffer * buffer; + + gint cursor; +}; + +struct _YuiScspClass +{ + GtkWindowClass parent_class; + + void (* yui_scsp) (YuiScsp * yv); +}; + +GType yui_scsp_get_type (void); +GtkWidget * yui_scsp_new (YuiWindow * yui); +void yui_scsp_fill (YuiScsp * scsp); +void yui_scsp_update (YuiScsp * scsp); +void yui_scsp_destroy (YuiScsp * scsp); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuiscudsp.c b/yabause/src/gtk/yuiscudsp.c new file mode 100644 index 0000000000..3a8f311a50 --- /dev/null +++ b/yabause/src/gtk/yuiscudsp.c @@ -0,0 +1,425 @@ +/* Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuiscudsp.h" +#include "../scu.h" +#include "../yabause.h" +#include "settings.h" + +static void yui_scudsp_class_init (YuiScudspClass * klass); +static void yui_scudsp_init (YuiScudsp * yfe); +static void yui_scudsp_clear(YuiScudsp * scudsp); +static void yui_scudsp_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiScudsp *scudsp); +static void yui_scudsp_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiScudsp *scudsp); +static void yui_scudsp_step( GtkWidget* widget, YuiScudsp * scudsp ); +static void yui_scudsp_breakpoint_handler (u32 addr); + +static YuiScudsp *yui_scudsp; +static YuiWindow * yui; + +GType yui_scudsp_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiScudspClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_scudsp_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiScudsp), + 0, + (GInstanceInitFunc) yui_scudsp_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScudsp", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_scudsp_class_init (UNUSED YuiScudspClass * klass) { +} + +static void yui_scudsp_init (YuiScudsp * scudsp) { + gtk_window_set_title(GTK_WINDOW(scudsp), "SCU-DSP"); + gtk_window_set_resizable( GTK_WINDOW(scudsp), FALSE ); + + scudsp->vbox = gtk_vbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( scudsp->vbox ),4 ); + gtk_container_add (GTK_CONTAINER (scudsp), scudsp->vbox); + + scudsp->hboxmain = gtk_hbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( scudsp->hboxmain ),4 ); + gtk_box_pack_start( GTK_BOX( scudsp->vbox ), scudsp->hboxmain, FALSE, FALSE, 4 ); + + scudsp->hbox = gtk_hbutton_box_new(); + gtk_container_set_border_width( GTK_CONTAINER( scudsp->hbox ),4 ); + gtk_box_pack_start( GTK_BOX( scudsp->vbox ), scudsp->hbox, FALSE, FALSE, 4 ); + + scudsp->vboxmain = gtk_vbox_new(FALSE, 2); + gtk_container_set_border_width( GTK_CONTAINER( scudsp->vboxmain ),4 ); + gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->vboxmain, FALSE, FALSE, 4 ); + + /* unassembler frame */ + + scudsp->uFrame = gtk_frame_new("Disassembled code"); + gtk_box_pack_start( GTK_BOX( scudsp->vboxmain ), scudsp->uFrame, FALSE, FALSE, 4 ); + + scudsp->uLabel = gtk_label_new(NULL); + gtk_container_add (GTK_CONTAINER (scudsp->uFrame), scudsp->uLabel ); + + /* Register list */ + + scudsp->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); + scudsp->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(scudsp->regListStore) ); + scudsp->regListRenderer1 = gtk_cell_renderer_text_new(); + scudsp->regListRenderer2 = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(scudsp->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + scudsp->regListColumn1 = gtk_tree_view_column_new_with_attributes("Register", scudsp->regListRenderer1, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->regList), scudsp->regListColumn1); + scudsp->regListColumn2 = gtk_tree_view_column_new_with_attributes("Value", scudsp->regListRenderer2, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->regList), scudsp->regListColumn2); + gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->regList, FALSE, FALSE, 4 ); + g_signal_connect(G_OBJECT(scudsp->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_scudsp_editedReg), scudsp ); + + /* breakpoint list */ + + scudsp->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); + scudsp->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(scudsp->bpListStore) ); + scudsp->bpListRenderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(scudsp->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + scudsp->bpListColumn = gtk_tree_view_column_new_with_attributes("Breakpoints", scudsp->bpListRenderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->bpList), scudsp->bpListColumn); + gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->bpList, FALSE, FALSE, 4 ); + g_signal_connect(G_OBJECT(scudsp->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_scudsp_editedBp), scudsp ); + + g_signal_connect(G_OBJECT(scudsp), "delete-event", GTK_SIGNAL_FUNC(yui_scudsp_destroy), NULL); +} + + +GtkWidget * yui_scudsp_new(YuiWindow * y) { + GtkWidget * dialog; + GClosure *closureF7; + GtkAccelGroup *accelGroup; + const scucodebreakpoint_struct *cbp; + gint i; + yui = y; + + if ( yui_scudsp ) return GTK_WIDGET(yui_scudsp); + + dialog = GTK_WIDGET(g_object_new(yui_scudsp_get_type(), NULL)); + yui_scudsp = YUI_SCUDSP(dialog); + + if (!( yui->state & YUI_IS_INIT )) { + yui_window_run(yui); + yui_window_pause(yui); + } + + ScuDspSetBreakpointCallBack(&yui_scudsp_breakpoint_handler); + + for (i = 0; i < 23 ; i++) { + + GtkTreeIter iter; + gtk_list_store_append( GTK_LIST_STORE( yui_scudsp->regListStore ), &iter ); + } + + cbp = ScuDspGetBreakpointList(); + + for (i = 0; i < MAX_BREAKPOINTS; i++) { + + GtkTreeIter iter; + yui_scudsp->cbp[i] = cbp[i].addr; + gtk_list_store_append( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter ); + if (cbp[i].addr != 0xFFFFFFFF) { + + gchar tempstr[20]; + sprintf(tempstr, "%08X", (int)cbp[i].addr); + gtk_list_store_set( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter, 0, tempstr, -1 ); + } else gtk_list_store_set( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter, 0, "", -1 ); + } + + { + GtkWidget * but2, * but3, * but4; + + yui_scudsp->buttonStep = gtk_button_new_with_label( "Step [F7]" ); + gtk_box_pack_start( GTK_BOX( yui_scudsp->hbox ), yui_scudsp->buttonStep, FALSE, FALSE, 2 ); + g_signal_connect( yui_scudsp->buttonStep, "clicked", G_CALLBACK(yui_scudsp_step), yui_scudsp ); + + but2 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); + gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but2, FALSE, FALSE, 2); + + but3 = gtk_button_new(); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); + gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but3, FALSE, FALSE, 2); + + but4 = gtk_button_new_from_stock("gtk-close"); + g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_scudsp_destroy), dialog); + gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but4, FALSE, FALSE, 2); + } + yui_scudsp->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_scudsp_update), yui_scudsp); + yui_scudsp->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_scudsp_clear), yui_scudsp); + accelGroup = gtk_accel_group_new (); + closureF7 = g_cclosure_new (G_CALLBACK (yui_scudsp_step), yui_scudsp, NULL); + gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); + gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); + + yui_scudsp_update(yui_scudsp); + if ( yui->state & YUI_IS_RUNNING ) yui_scudsp_clear(yui_scudsp); + + gtk_widget_show_all(GTK_WIDGET(yui_scudsp)); + + return dialog; +} + + +static void yui_scudsp_update_reglist( YuiScudsp *scudsp, scudspregs_struct *regs) { + /* refrescudsp the registery list */ + + GtkTreeIter iter; + char valuestr[32]; + + gtk_tree_model_get_iter_first( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); + sprintf(valuestr, "%d", regs->ProgControlPort.part.PR); + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, "PR", 1, valuestr, -1 ); + + #define SCUDSPUPDATEREGLISTp(rreg,format) \ + gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ + sprintf(valuestr, #format, (int)regs->ProgControlPort.part.rreg); \ + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); + #define SCUDSPUPDATEREGLIST(rreg,format) \ + gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ + sprintf(valuestr, #format, (int)regs->rreg); \ + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); + #define SCUDSPUPDATEREGLISTx(rreg,vreg,format) \ + gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ + sprintf(valuestr, #format, (int)(vreg)); \ + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); + + SCUDSPUPDATEREGLISTp(EP,%d); + SCUDSPUPDATEREGLISTp(T0,%d); + SCUDSPUPDATEREGLISTp(S,%d); + SCUDSPUPDATEREGLISTp(Z,%d); + SCUDSPUPDATEREGLISTp(C,%d); + SCUDSPUPDATEREGLISTp(V,%d); + SCUDSPUPDATEREGLISTp(E,%d); + SCUDSPUPDATEREGLISTp(ES,%d); + SCUDSPUPDATEREGLISTp(EX,%d); + SCUDSPUPDATEREGLISTp(LE,%d); + SCUDSPUPDATEREGLISTp(P,%02X); + SCUDSPUPDATEREGLIST(TOP,%02X); + SCUDSPUPDATEREGLIST(LOP,%02X); + gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); + sprintf(valuestr, "%08X", (int)(((u32)(regs->CT[0]))<<24 | ((u32)(regs->CT[1]))<<16 | ((u32)(regs->CT[2]))<<8 | ((u32)(regs->CT[3]))) ); + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, "CT", 1, valuestr, -1 ); + SCUDSPUPDATEREGLISTx(RA,regs->RA0,%08X); + SCUDSPUPDATEREGLISTx(WA,regs->WA0,%08X); + SCUDSPUPDATEREGLIST(RX,%08X); + SCUDSPUPDATEREGLIST(RY,%08X); + SCUDSPUPDATEREGLISTx(PH,regs->P.part.H & 0xFFFF,%04X); + SCUDSPUPDATEREGLISTx(PL,regs->P.part.L & 0xFFFFFFFF,%08X); + SCUDSPUPDATEREGLISTx(ACH,regs->AC.part.H & 0xFFFF,%04X); + SCUDSPUPDATEREGLISTx(ACL,regs->AC.part.L & 0xFFFFFFFF,%08X); +} + +static void scudspsetRegister( YuiScudsp *scudsp, int nReg, u32 value ) { + /* set register number to value in proc */ + + scudspregs_struct scudspregs; + ScuDspGetRegisters(&scudspregs); + + switch ( nReg ) { + case 0: scudspregs.ProgControlPort.part.PR = value; break; + case 1: scudspregs.ProgControlPort.part.EP = value; break; + case 2: scudspregs.ProgControlPort.part.T0 = value; break; + case 3: scudspregs.ProgControlPort.part.S = value; break; + case 4: scudspregs.ProgControlPort.part.Z = value; break; + case 5: scudspregs.ProgControlPort.part.C = value; break; + case 6: scudspregs.ProgControlPort.part.V = value; break; + case 7: scudspregs.ProgControlPort.part.E = value; break; + case 8: scudspregs.ProgControlPort.part.ES = value; break; + case 9: scudspregs.ProgControlPort.part.EX = value; break; + case 10: scudspregs.ProgControlPort.part.LE = value; break; + case 11: scudspregs.ProgControlPort.part.P = value; break; + case 12: scudspregs.TOP = value; break; + case 13: scudspregs.LOP = value; break; + case 14: + scudspregs.CT[0] = (value>>24) & 0xff; + scudspregs.CT[1] = (value>>16) & 0xff; + scudspregs.CT[2] = (value>>8) & 0xff; + scudspregs.CT[3] = (value) & 0xff; + break; + case 15: scudspregs.RA0 = value; break; + case 16: scudspregs.WA0 = value; break; + case 17: scudspregs.RX = value; break; + case 18: scudspregs.RY = value; break; + case 19: scudspregs.P.part.H = value; break; + case 20: scudspregs.P.part.L = value; break; + case 21: scudspregs.AC.part.H = value; break; + case 22: scudspregs.AC.part.L = value; break; + } + + ScuDspSetRegisters(&scudspregs); +} + +static void yui_scudsp_update_codelist( YuiScudsp *scudsp, u32 addr) { + /* refresh the assembler view. points the line to be highlighted. */ + + int i; + static char tagPC[] = ""; + static char tagEnd[] = "\n"; + char buf[100*24+40]; + char *curs = buf; + char lineBuf[100]; + u32 offset; + + if ( addr - scudsp->lastCode >= 20 ) offset = addr - 8; + else offset = scudsp->lastCode; + scudsp->lastCode = offset; + + for (i=0; i < 24; i++) { + + if ( offset + i == addr ) { strcpy( curs, tagPC ); curs += strlen(tagPC); } + ScuDspDisasm(offset+i, lineBuf); + strcpy( curs, lineBuf ); + curs += strlen(lineBuf); + if ( offset + i == addr ) { strcpy( curs, tagEnd ); curs += strlen(tagEnd); } + else { strcpy( curs, "\n" ); curs += 1;} + } + *curs = 0; + + gtk_label_set_markup( GTK_LABEL(scudsp->uLabel), buf ); +} + +static void yui_scudsp_step( GtkWidget* widget, YuiScudsp * scudsp ) { + + ScuDspStep(); + yui_window_invalidate( yui ); /* update all dialogs, including us */ +} + +static void yui_scudsp_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiScudsp *scudsp) { + /* registry number value has been set to */ + + GtkTreeIter iter; + char bptext[10]; + char *endptr; + int i = atoi(arg1); + u32 addr; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( scudsp->regListStore ), &iter, arg1 ); + addr = strtoul(arg2, &endptr, 16 ); + if ( endptr - arg2 == strlen(arg2) ) { + + sprintf(bptext, "%08X", (int)addr); + scudspsetRegister( scudsp, i, addr ); + gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 1, bptext, -1 ); + } + yui_window_invalidate( yui ); +} + +static void yui_scudsp_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiScudsp *scudsp) { + /* breakpoint has been set to address */ + + GtkTreeIter iter; + char bptext[10]; + char *endptr; + int i = atoi(arg1); + u32 addr; + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( scudsp->bpListStore ), &iter, arg1 ); + addr = strtoul(arg2, &endptr, 16 ); + if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; + if ( scudsp->cbp[i] != 0xFFFFFFFF) ScuDspDelCodeBreakpoint(scudsp->cbp[i]); + scudsp->cbp[i] = 0xFFFFFFFF; + + if ((addr!=0xFFFFFFFF)&&(ScuDspAddCodeBreakpoint(addr) == 0)) { + + sprintf(bptext, "%08X", (int)addr); + scudsp->cbp[i] = addr; + } else strcpy(bptext,""); + gtk_list_store_set( GTK_LIST_STORE( scudsp->bpListStore ), &iter, 0, bptext, -1 ); +} + +static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ + + while ( !(yui->state & YUI_IS_RUNNING) ) + if ( gtk_main_iteration() ) return; +} + +static void yui_scudsp_breakpoint_handler (u32 addr) { + + yui_window_pause(yui); + { + scudspregs_struct scudspregs; + YuiScudsp* scudsp = YUI_SCUDSP(yui_scudsp_new( yui )); + + ScuDspGetRegisters(&scudspregs); + yui_scudsp_update_reglist(scudsp, &scudspregs); + yui_scudsp_update_codelist(scudsp, scudspregs.PC); + } + debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ +} + + +void yui_scudsp_update(YuiScudsp * scudsp) { + scudspregs_struct scudspregs; + ScuDspGetRegisters(&scudspregs); + yui_scudsp_update_codelist(scudsp,scudspregs.PC); + yui_scudsp_update_reglist(scudsp, &scudspregs); + gtk_widget_set_sensitive(scudsp->uLabel, TRUE); + gtk_widget_set_sensitive(scudsp->bpList, TRUE); + gtk_widget_set_sensitive(scudsp->regList, TRUE); + gtk_widget_set_sensitive(scudsp->buttonStep, TRUE); +} + +void yui_scudsp_destroy(YuiScudsp * scudsp) { + g_signal_handler_disconnect(yui, scudsp->running_handler); + g_signal_handler_disconnect(yui, scudsp->paused_handler); + + yui_scudsp = NULL; + + gtk_widget_destroy(GTK_WIDGET(scudsp)); +} + +static void yui_scudsp_clear(YuiScudsp * scudsp) { + + gtk_widget_set_sensitive(scudsp->uLabel, FALSE); + gtk_widget_set_sensitive(scudsp->bpList, FALSE); + gtk_widget_set_sensitive(scudsp->regList, FALSE); + gtk_widget_set_sensitive(scudsp->buttonStep, FALSE); +} diff --git a/yabause/src/gtk/yuiscudsp.h b/yabause/src/gtk/yuiscudsp.h new file mode 100644 index 0000000000..7cdc285264 --- /dev/null +++ b/yabause/src/gtk/yuiscudsp.h @@ -0,0 +1,72 @@ +/* Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_SCUDSP_H +#define YUI_SCUDSP_H + +#include +#include +#include + +#include "../scu.h" +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_SCUDSP_TYPE (yui_scudsp_get_type ()) +#define YUI_SCUDSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCUDSP_TYPE, YuiScudsp)) +#define YUI_SCUDSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCUDSP_TYPE, YuiScudspClass)) +#define IS_YUI_SCUDSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCUDSP_TYPE)) +#define IS_YUI_SCUDSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCUDSP_TYPE)) + +typedef struct _YuiScudsp YuiScudsp; +typedef struct _YuiScudspClass YuiScudspClass; + +struct _YuiScudsp +{ + GtkWindow dialog; + + GtkWidget *vbox, *vboxmain; + GtkWidget *hbox, *hboxmain; + GtkWidget * buttonStep, * buttonStepOver; + GtkWidget * bpList, *regList, *uLabel, *uFrame; + GtkListStore *bpListStore, *regListStore; + GtkCellRenderer *bpListRenderer, *regListRenderer1, *regListRenderer2; + GtkTreeViewColumn *bpListColumn, *regListColumn1, *regListColumn2; + u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ + u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ + gulong paused_handler; + gulong running_handler; +}; + +struct _YuiScudspClass +{ + GtkWindowClass parent_class; + + void (* yui_scudsp) (YuiScudsp * yv); +}; + +GType yui_scudsp_get_type (void); +GtkWidget * yui_scudsp_new(YuiWindow * y); +void yui_scudsp_update(YuiScudsp * scudsp); +void yui_scudsp_destroy(YuiScudsp * scudsp); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuish.c b/yabause/src/gtk/yuish.c new file mode 100644 index 0000000000..0c8dc3cc3e --- /dev/null +++ b/yabause/src/gtk/yuish.c @@ -0,0 +1,1102 @@ +/* Copyright 2005-2006 Fabien Coulon + Copyright 2008 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuish.h" +#include "../sh2core.h" +#include "../sh2d.h" +#include "../yabause.h" +#include "settings.h" + +static void yui_sh_class_init (YuiShClass * klass); +static void yui_sh_init (YuiSh * yfe); +static void yui_sh_clear(YuiSh * sh); +static void yui_sh_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2); +static void yui_sh_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2); +static void yui_sh_editedMbp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2); +static void yui_sh_editedMbpFlags( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2); +static void yui_sh_step( GtkWidget* widget, YuiSh * sh2 ); +static void SH2BreakpointHandler (SH2_struct *context, u32 addr); +static gint yui_sh_popup(GtkWidget * widget, GdkEvent * event, gpointer data); +static gint yui_sh_bp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data); +static gint yui_sh_mbp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data); +static void yui_sh_popup_add_bp(GtkMenuItem * menuitem, gpointer user_data); +static void yui_sh_popup_del_bp(GtkMenuItem * menuitem, gpointer user_data); +static void SH2UpdateBreakpointList(YuiSh * sh2); +static void SH2UpdateMemoryBreakpointList(YuiSh * sh2); +static void yui_sh_bp_add(GtkEntry * entry, gpointer user_data); +static void yui_sh_button_bp_add(GtkWidget * widget, gpointer user_data); +static void yui_sh_mbp_add(GtkEntry * entry, gpointer user_data); +static void yui_sh_mbp_toggle_flag(GtkWidget * menuitem, gpointer user_data); +static void yui_sh_mbp_remove(GtkWidget * menuitem, gpointer user_data); +static void yui_sh_mbp_clear(GtkWidget * menuitem, gpointer user_data); +static void yui_sh_bp_remove(GtkWidget * menuitem, gpointer user_data); +static void yui_sh_bp_clear(GtkWidget * menuitem, gpointer user_data); + +static YuiSh *yui_msh, *yui_ssh; +static YuiWindow * yui; + +GType yui_sh_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiShClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_sh_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiSh), + 0, + (GInstanceInitFunc) yui_sh_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiSh", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_sh_class_init (UNUSED YuiShClass * klass) { +} + +static void yui_sh_init (YuiSh * sh2) { + //GtkWidget *vboxBp; + + sh2->breakpointEnabled = MSH2->breakpointEnabled; + + gtk_window_set_title(GTK_WINDOW(sh2), "SH"); + + sh2->vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER( sh2->vbox ), 0); + gtk_container_add (GTK_CONTAINER (sh2), sh2->vbox); + + sh2->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(sh2->toolbar), GTK_TOOLBAR_ICONS); + gtk_box_pack_start(GTK_BOX(sh2->vbox), sh2->toolbar, FALSE, FALSE, 0); + + sh2->hboxmain = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start( GTK_BOX( sh2->vbox ), sh2->hboxmain, FALSE, FALSE, 0); + + /* Register list */ + + sh2->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); + sh2->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->regListStore) ); + sh2->regListRenderer1 = gtk_cell_renderer_text_new(); + sh2->regListRenderer2 = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(sh2->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + sh2->regListColumn1 = gtk_tree_view_column_new_with_attributes(_("Register"), sh2->regListRenderer1, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->regList), sh2->regListColumn1); + sh2->regListColumn2 = gtk_tree_view_column_new_with_attributes(_("Value"), sh2->regListRenderer2, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->regList), sh2->regListColumn2); + gtk_box_pack_start( GTK_BOX( sh2->hboxmain ), sh2->regList, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(sh2->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_sh_editedReg), sh2 ); + + sh2->store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + sh2->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (sh2->store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(sh2->view), FALSE); + { + GtkWidget * scroll; + GtkCellRenderer *renderer; + GtkCellRenderer *icon; + GtkTreeViewColumn *column; + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + icon = gtk_cell_renderer_pixbuf_new(); + g_object_set(icon, "xalign", 0, NULL); + column = gtk_tree_view_column_new_with_attributes("Icon", icon, "stock-id", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Address", renderer, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); + + column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 2, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); + + gtk_container_add(GTK_CONTAINER(scroll), sh2->view); + gtk_box_pack_start(GTK_BOX(sh2->hboxmain), scroll, TRUE, TRUE, 0); + } + + if (sh2->breakpointEnabled) { + GtkWidget * menu = gtk_menu_new(); + GtkWidget * item; + + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(yui_sh_popup_add_bp), sh2); + + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(yui_sh_popup_del_bp), sh2); + + gtk_widget_show_all(menu); + + g_signal_connect(sh2->view, "button-press-event", G_CALLBACK(yui_sh_popup), menu); + } + + /* breakpoint list */ + + sh2->vboxBp = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start( GTK_BOX( sh2->hboxmain ), sh2->vboxBp, FALSE, FALSE, 0 ); + + sh2->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); + sh2->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->bpListStore) ); + sh2->bpListRenderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(sh2->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + sh2->bpListColumn = gtk_tree_view_column_new_with_attributes("Code breaks", sh2->bpListRenderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->bpList), sh2->bpListColumn); + gtk_box_pack_start( GTK_BOX( sh2->vboxBp ), sh2->bpList, TRUE, TRUE, 0 ); + g_signal_connect(G_OBJECT(sh2->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedBp), sh2 ); + + { + GtkWidget * bp_form_box; + GtkWidget * bp_input; + GtkWidget * bp_add; + + bp_form_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(sh2->vboxBp), bp_form_box, FALSE, FALSE, 0); + + bp_input = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(bp_input), 8); + g_signal_connect(bp_input, "activate", G_CALLBACK(yui_sh_bp_add), sh2); + gtk_box_pack_start(GTK_BOX(bp_form_box), bp_input, TRUE, TRUE, 0); + + bp_add = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(bp_add), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON)); + gtk_button_set_relief(GTK_BUTTON(bp_add), GTK_RELIEF_NONE); + g_signal_connect(bp_add, "clicked", G_CALLBACK(yui_sh_button_bp_add), bp_input); + gtk_box_pack_start(GTK_BOX(bp_form_box), bp_add, FALSE, FALSE, 0); + } + + { + GtkWidget * bp_item; + sh2->bp_menu = gtk_menu_new(); + + bp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->bp_menu), bp_item); + g_signal_connect(bp_item, "activate", G_CALLBACK(yui_sh_bp_remove), sh2); + + bp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->bp_menu), bp_item); + g_signal_connect(bp_item, "activate", G_CALLBACK(yui_sh_bp_clear), sh2); + + gtk_widget_show_all(sh2->bp_menu); + + g_signal_connect(sh2->bpList, "button-press-event", G_CALLBACK(yui_sh_bp_popup), sh2); + } + + /* memory breakpoint list */ + + sh2->mbpListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); + sh2->mbpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->mbpListStore) ); + sh2->mbpListRenderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(sh2->mbpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); + sh2->mbpListColumn = gtk_tree_view_column_new_with_attributes("Memory breaks", sh2->mbpListRenderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->mbpList), sh2->mbpListColumn); + + { + GtkCellRenderer * flags_renderer; + GtkTreeViewColumn * flags_column; + + flags_renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(flags_renderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); + g_signal_connect(G_OBJECT(flags_renderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedMbpFlags), sh2 ); + + flags_column = gtk_tree_view_column_new_with_attributes("Flags", flags_renderer, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->mbpList), flags_column); + } + + gtk_box_pack_start( GTK_BOX( sh2->vboxBp ), sh2->mbpList, TRUE, TRUE, 0 ); + g_signal_connect(G_OBJECT(sh2->mbpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedMbp), sh2 ); + + { + GtkWidget * mbp_item; + + sh2->mbp_menu = gtk_menu_new(); + + sh2->mbp_menu_item[0] = gtk_check_menu_item_new_with_label("Byte read"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[0]); + g_signal_connect(sh2->mbp_menu_item[0], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + sh2->mbp_menu_item[1] = gtk_check_menu_item_new_with_label("Word read"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[1]); + g_signal_connect(sh2->mbp_menu_item[1], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + sh2->mbp_menu_item[2] = gtk_check_menu_item_new_with_label("Long read"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[2]); + g_signal_connect(sh2->mbp_menu_item[2], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + sh2->mbp_menu_item[3] = gtk_check_menu_item_new_with_label("Byte write"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[3]); + g_signal_connect(sh2->mbp_menu_item[3], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + sh2->mbp_menu_item[4] = gtk_check_menu_item_new_with_label("Word write"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[4]); + g_signal_connect(sh2->mbp_menu_item[4], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + sh2->mbp_menu_item[5] = gtk_check_menu_item_new_with_label("Long write"); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[5]); + g_signal_connect(sh2->mbp_menu_item[5], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); + + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), gtk_separator_menu_item_new()); + + mbp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), mbp_item); + g_signal_connect(mbp_item, "activate", G_CALLBACK(yui_sh_mbp_remove), sh2); + + mbp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), mbp_item); + g_signal_connect(mbp_item, "activate", G_CALLBACK(yui_sh_mbp_clear), sh2); + + gtk_widget_show_all(sh2->mbp_menu); + + g_signal_connect(sh2->mbpList, "button-press-event", G_CALLBACK(yui_sh_mbp_popup), sh2); + } + + { + GtkWidget * bp_form_box; + GtkWidget * bp_input; + GtkWidget * bp_add; + + bp_form_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(sh2->vboxBp), bp_form_box, FALSE, FALSE, 0); + + bp_input = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(bp_input), 8); + g_signal_connect(bp_input, "activate", G_CALLBACK(yui_sh_mbp_add), sh2); + gtk_box_pack_start(GTK_BOX(bp_form_box), bp_input, TRUE, TRUE, 0); + + bp_add = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(bp_add), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON)); + gtk_button_set_relief(GTK_BUTTON(bp_add), GTK_RELIEF_NONE); + g_signal_connect(bp_add, "clicked", G_CALLBACK(yui_sh_button_bp_add), bp_input); + gtk_box_pack_start(GTK_BOX(bp_form_box), bp_add, FALSE, FALSE, 0); + } + + g_signal_connect(G_OBJECT(sh2), "delete-event", GTK_SIGNAL_FUNC(yui_sh_destroy), NULL); + + gtk_window_set_default_size(GTK_WINDOW(sh2), 650, -1); +} + + +static GtkWidget * yui_sh_new(YuiWindow * y, gboolean bMaster) { + GtkWidget * dialog; + GClosure *closureF7; //, *closureF8; + GtkAccelGroup *accelGroup; + YuiSh * sh2; + gint i; + yui = y; + + if (!( yui->state & YUI_IS_INIT )) { + yui_window_run(yui); + yui_window_pause(yui); + } + + if ( bMaster && yui_msh ) return GTK_WIDGET(yui_msh); + if ( !bMaster && yui_ssh ) return GTK_WIDGET(yui_ssh); + + dialog = GTK_WIDGET(g_object_new(yui_sh_get_type(), NULL)); + sh2 = YUI_SH(dialog); + + //sh2->breakpointEnabled = MSH2->breakpointEnabled; +/* + if ( !sh2->breakpointEnabled ) + gtk_box_pack_start( GTK_BOX( sh2->vboxmain ), + gtk_label_new("Breakpoints are disabled (fast interpreter)"), FALSE, FALSE, 4 ); +*/ + + sh2->bMaster = bMaster; + sh2->debugsh = bMaster ? MSH2 : SSH2; + + SH2SetBreakpointCallBack(sh2->debugsh, (void (*)(void *, u32))SH2BreakpointHandler); + + gtk_window_set_title(GTK_WINDOW(sh2), bMaster?"Master SH2":"Slave SH2"); + + for (i = 0; i < 23 ; i++) { + + GtkTreeIter iter; + gtk_list_store_append( GTK_LIST_STORE( sh2->regListStore ), &iter ); + } + + SH2UpdateBreakpointList(sh2); + + SH2UpdateMemoryBreakpointList(sh2); + + { + GtkToolItem * play_button, * pause_button; + + play_button = gtk_tool_button_new_from_stock("run"); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), GTK_WIDGET(play_button)); + gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(play_button), 0); + + pause_button = gtk_tool_button_new_from_stock("pause"); + gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), GTK_WIDGET(pause_button)); + gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(pause_button), 1); + + sh2->buttonStep = gtk_tool_button_new_from_stock(GTK_STOCK_GO_DOWN); + g_signal_connect(sh2->buttonStep, "clicked", G_CALLBACK(yui_sh_step), sh2); + gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(sh2->buttonStep), 2); +#if GTK_CHECK_VERSION(2,12,0) + gtk_widget_set_tooltip_text(GTK_WIDGET(sh2->buttonStep), "step"); +#endif + } + + sh2->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_sh_update), sh2); + sh2->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_sh_clear), sh2); + accelGroup = gtk_accel_group_new (); + closureF7 = g_cclosure_new (G_CALLBACK (yui_sh_step), sh2, NULL); + gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); + gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); + + yui_sh_update(sh2); + if ( yui->state & YUI_IS_RUNNING ) yui_sh_clear(sh2); + + gtk_widget_show_all(GTK_WIDGET(sh2)); + if ( !sh2->breakpointEnabled ) { +/* + gtk_widget_hide( sh2->bpList ); + gtk_widget_hide( sh2->mbpList ); +*/ + gtk_widget_hide(sh2->vboxBp); + } + + return dialog; +} + +GtkWidget * yui_msh_new(YuiWindow * y) { + return GTK_WIDGET( yui_msh = YUI_SH(yui_sh_new( y, TRUE )) ); +} + +GtkWidget * yui_ssh_new(YuiWindow * y) { + return GTK_WIDGET( yui_ssh = YUI_SH(yui_sh_new( y, FALSE )) ); +} + +static void SH2UpdateRegList( YuiSh *sh2, sh2regs_struct *regs) { + /* refresh the registery list */ + + GtkTreeIter iter; + char regstr[32]; + char valuestr[32]; + int i; + + for (i = 0; i < 16; i++) { + sprintf(regstr, "R%02d", i); + sprintf(valuestr, "%08X", (int)regs->R[i] ); + if ( !i ) gtk_tree_model_get_iter_first( GTK_TREE_MODEL( sh2->regListStore ), &iter ); + else gtk_tree_model_iter_next( GTK_TREE_MODEL( sh2->regListStore ), &iter ); + gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); + } + + #define SH2UPDATEREGLIST(rreg) \ + gtk_tree_model_iter_next( GTK_TREE_MODEL( sh2->regListStore ), &iter ); \ + sprintf(valuestr, "%08X", (int)regs->rreg); \ + gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); + + SH2UPDATEREGLIST(SR.all); + SH2UPDATEREGLIST(GBR); + SH2UPDATEREGLIST(VBR); + SH2UPDATEREGLIST(MACH); + SH2UPDATEREGLIST(MACL); + SH2UPDATEREGLIST(PR); + SH2UPDATEREGLIST(PC); +} + +static void sh2setRegister( YuiSh *sh2, int nReg, u32 value ) { + /* set register number to value in proc */ + + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + + if ( nReg < 16 ) sh2regs.R[nReg] = value; + switch ( nReg ) { + case 16: sh2regs.SR.all = value; break; + case 17: sh2regs.GBR = value; break; + case 18: sh2regs.VBR = value; break; + case 19: sh2regs.MACH = value; break; + case 20: sh2regs.MACL = value; break; + case 21: sh2regs.PR = value; break; + case 22: sh2regs.PC = value; break; + } + + SH2SetRegisters(sh2->debugsh, &sh2regs); +} + +void SH2UpdateBreakpointList(YuiSh * sh2) { + const codebreakpoint_struct *cbp; + int i; + + gtk_list_store_clear(GTK_LIST_STORE( sh2->bpListStore )); + + cbp = SH2GetBreakpointList(sh2->debugsh); + + for (i = 0; i < MAX_BREAKPOINTS-1; i++) { + + if (cbp[i].addr != 0xFFFFFFFF) { + gchar tempstr[20]; + GtkTreeIter iter; + gtk_list_store_append( GTK_LIST_STORE( sh2->bpListStore ), &iter ); + + sprintf(tempstr, "%08X", (int)cbp[i].addr); + gtk_list_store_set( GTK_LIST_STORE( sh2->bpListStore ), &iter, 0, tempstr, -1 ); + } + } +} + +void SH2UpdateMemoryBreakpointList(YuiSh * sh2) { + const memorybreakpoint_struct *cmbp; + int i; + + gtk_list_store_clear( sh2->mbpListStore ); + + cmbp = SH2GetMemoryBreakpointList(sh2->debugsh); + + for (i = 0; i < MAX_BREAKPOINTS; i++) { + + if (cmbp[i].addr != 0xFFFFFFFF) { + gchar tempstr[30]; + gchar flagstr[30]; + gchar *curs = flagstr; + u32 flags = cmbp[i].flags; + + GtkTreeIter iter; + gtk_list_store_append( GTK_LIST_STORE( sh2->mbpListStore ), &iter ); + + sprintf(tempstr, "%08X", (int)cmbp[i].addr); + if ( flags & BREAK_BYTEREAD ) *(curs++) = 'b'; + if ( flags & BREAK_WORDREAD ) *(curs++) = 'w'; + if ( flags & BREAK_LONGREAD ) *(curs++) = 'l'; + if ( flags & BREAK_BYTEWRITE ) *(curs++) = 'B'; + if ( flags & BREAK_WORDWRITE ) *(curs++) = 'W'; + if ( flags & BREAK_LONGWRITE ) *(curs++) = 'L'; + *curs = 0; + + gtk_list_store_set( GTK_LIST_STORE( sh2->mbpListStore ), &iter, 0, tempstr, -1 ); + gtk_list_store_set( GTK_LIST_STORE( sh2->mbpListStore ), &iter, 1, flagstr, -1 ); + } + } +} + +static void SH2UpdateCodeList( YuiSh *sh2, u32 addr) { + /* refresh the assembler view. points the line to be highlighted. */ + + int i, j; + char lineBuf[64]; + u32 offset; + GtkTreeIter iter; + unsigned int address; + char address_s[20]; + char command_s[64]; + codebreakpoint_struct *cbp; + + gtk_list_store_clear(sh2->store); + + if ( addr - sh2->lastCode >= 20*2 ) offset = addr - (8*2); + else offset = sh2->lastCode; + sh2->lastCode = offset; + + cbp = SH2GetBreakpointList(sh2->debugsh); + + for (i = 0; i < 24; i++) { + SH2Disasm(offset+2*i, MappedMemoryReadWord(offset+2*i), 0, lineBuf); + + sscanf(lineBuf, "0x%8X: %[^\n]", &address, command_s); + sprintf(address_s, "0x%08X", address); + + gtk_list_store_append(sh2->store, &iter); + if ( offset + 2*i == addr ) { + gtk_list_store_set(sh2->store, &iter, 0, GTK_STOCK_GO_FORWARD, -1); + } else { + for (j = 0;j < MAX_BREAKPOINTS - 1;j++) { + if (cbp[j].addr == address) { + gtk_list_store_set(sh2->store, &iter, 0, GTK_STOCK_STOP, -1); + } + } + } + + gtk_list_store_set(sh2->store, &iter, 1, address_s, -1); + + gtk_list_store_set(sh2->store, &iter, 2, command_s, -1); + } +} + +static void yui_sh_step( GtkWidget* widget, YuiSh * sh2 ) { + + SH2Step(sh2->debugsh); + yui_window_invalidate( yui ); /* update all dialogs, including us */ +} + +static void yui_sh_editedReg( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2) { + /* registry number value has been set to */ + + GtkTreeIter iter; + char bptext[10]; + char *endptr; + int i = atoi(arg1); + u32 addr; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->regListStore ), &iter, arg1 ); + addr = strtoul(arg2, &endptr, 16 ); + if ( endptr - arg2 == strlen(arg2) ) { + + sprintf(bptext, "%08X", (int)addr); + sh2setRegister( sh2, i, addr ); + gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 1, bptext, -1 ); + } + yui_window_invalidate( yui ); +} + +static void yui_sh_editedBp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2) { + /* breakpoint has been set to address */ + + GtkTreeIter iter; + char *endptr; + unsigned int addr; + gchar * oldaddr_s; + unsigned int oldaddr; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->bpListStore ), &iter, arg1 ); + + gtk_tree_model_get(GTK_TREE_MODEL( sh2->bpListStore ), &iter, 0, &oldaddr_s, -1); + sscanf(oldaddr_s, "%8X", &oldaddr); + g_free(oldaddr_s); + + SH2DelCodeBreakpoint(sh2->debugsh, oldaddr); + + addr = strtoul(arg2, &endptr, 16 ); + if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; + + if (addr != 0xFFFFFFFF) { + SH2AddCodeBreakpoint(sh2->debugsh, addr); + } + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} + +static void yui_sh_editedMbp( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2) { + /* breakpoint has been set to address */ + + GtkTreeIter iter; + gchar *endptr; + unsigned int addr; + gchar * oldaddr_s, * flags_s; + unsigned int oldaddr; + u32 flags; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->mbpListStore ), &iter, arg1 ); + + gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 0, &oldaddr_s, -1); + sscanf(oldaddr_s, "%8X", &oldaddr); + g_free(oldaddr_s); + + gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 1, &flags_s, -1); + + SH2DelMemoryBreakpoint(sh2->debugsh, oldaddr); + + addr = strtoul(arg2, &endptr, 16 ); + if (!addr) addr = 0xFFFFFFFF; + + if (addr!=0xFFFFFFFF) { + + flags = 0; + endptr = flags_s; + while ( *endptr ) { + + switch (*endptr) { + + case 'b': flags |= BREAK_BYTEREAD; break; + case 'w': flags |= BREAK_WORDREAD; break; + case 'l': flags |= BREAK_LONGREAD; break; + case 'B': flags |= BREAK_BYTEWRITE; break; + case 'W': flags |= BREAK_WORDWRITE; break; + case 'L': flags |= BREAK_LONGWRITE; break; + } + endptr++; + } + + if ( !flags ) flags = BREAK_BYTEREAD|BREAK_WORDREAD|BREAK_LONGREAD|BREAK_BYTEWRITE|BREAK_WORDWRITE|BREAK_LONGWRITE; + SH2AddMemoryBreakpoint(sh2->debugsh, addr, flags); + } + + SH2UpdateMemoryBreakpointList(sh2); +} + +static void yui_sh_editedMbpFlags( GtkCellRendererText *cellrenderertext, + gchar *arg1, + gchar *arg2, + YuiSh *sh2) { + /* breakpoint has been set to address */ + + GtkTreeIter iter; + gchar *endptr; + unsigned int addr; + gchar * addr_s; + u32 flags = 0; + + gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->mbpListStore ), &iter, arg1 ); + + gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 0, &addr_s, -1); + sscanf(addr_s, "%8X", &addr); + g_free(addr_s); + + SH2DelMemoryBreakpoint(sh2->debugsh, addr); + + endptr = arg2; + + while ( *endptr ) { + + switch (*endptr) { + + case 'b': flags |= BREAK_BYTEREAD; break; + case 'w': flags |= BREAK_WORDREAD; break; + case 'l': flags |= BREAK_LONGREAD; break; + case 'B': flags |= BREAK_BYTEWRITE; break; + case 'W': flags |= BREAK_WORDWRITE; break; + case 'L': flags |= BREAK_LONGWRITE; break; + } + endptr++; + } + + SH2AddMemoryBreakpoint(sh2->debugsh, addr, flags); + + SH2UpdateMemoryBreakpointList(sh2); +} + +static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ + + while ( !(yui->state & YUI_IS_RUNNING) ) + if ( gtk_main_iteration() ) return; +} + +static void SH2BreakpointHandler (SH2_struct *context, u32 addr) { + + yui_window_pause(yui); + { + sh2regs_struct sh2regs; + YuiSh* sh2 = YUI_SH(yui_sh_new( yui, context == MSH2 )); + + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateRegList(sh2, &sh2regs); + SH2UpdateCodeList(sh2, sh2regs.PC); + } + debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ +} + + +void yui_sh_update(YuiSh * sh) { + sh2regs_struct sh2regs; + SH2GetRegisters(sh->debugsh, &sh2regs); + SH2UpdateCodeList(sh,sh2regs.PC); + SH2UpdateRegList(sh, &sh2regs); + gtk_widget_set_sensitive(sh->bpList, TRUE); + gtk_widget_set_sensitive(sh->mbpList, TRUE); + gtk_widget_set_sensitive(sh->regList, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(sh->buttonStep), + !sh->debugsh->isIdle && !(( sh->debugsh == SSH2 )&&( !yabsys.IsSSH2Running ))); +} + +void yui_sh_destroy(YuiSh * sh) { + g_signal_handler_disconnect(yui, sh->running_handler); + g_signal_handler_disconnect(yui, sh->paused_handler); + + if ( sh->bMaster ) yui_msh = NULL; + else yui_ssh = NULL; + + gtk_widget_destroy(GTK_WIDGET(sh)); +} + +static void yui_sh_clear(YuiSh * sh) { + + gtk_widget_set_sensitive(sh->bpList, FALSE); + gtk_widget_set_sensitive(sh->mbpList, FALSE); + gtk_widget_set_sensitive(sh->regList, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(sh->buttonStep), FALSE); + + gtk_list_store_clear(sh->store); +} + +gint yui_sh_popup(GtkWidget * widget, GdkEvent * event, gpointer data) +{ + GtkMenu *menu; + GdkEventButton *event_button; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU (data), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + menu = GTK_MENU(data); + + if (event->type == GDK_BUTTON_PRESS) { + event_button = (GdkEventButton *) event; + if (event_button->button == 3) { + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); + } + } + + return FALSE; +} + +void yui_sh_popup_add_bp(GtkMenuItem * menuitem, gpointer user_data) { + YuiSh * sh2 = user_data; + GtkTreeView * view = GTK_TREE_VIEW(sh2->view); + GtkTreeSelection * selection; + GtkTreeModel * model; + GtkTreeIter iter; + gchar * address_s; + unsigned int address; + + selection = gtk_tree_view_get_selection(view); + + gtk_tree_selection_get_selected(selection, &model, &iter); + + gtk_tree_model_get(model, &iter, 1, &address_s, -1); + + sscanf(address_s, "0x%08X", &address); + + SH2AddCodeBreakpoint(sh2->debugsh, address); + + g_free(address_s); + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} + +void yui_sh_popup_del_bp(GtkMenuItem * menuitem, gpointer user_data) { + YuiSh * sh2 = user_data; + GtkTreeView * view = GTK_TREE_VIEW(sh2->view); + GtkTreeSelection * selection; + GtkTreeModel * model; + GtkTreeIter iter; + gchar * address_s; + unsigned int address; + + selection = gtk_tree_view_get_selection(view); + + gtk_tree_selection_get_selected(selection, &model, &iter); + + gtk_tree_model_get(model, &iter, 1, &address_s, -1); + + sscanf(address_s, "0x%08X", &address); + + SH2DelCodeBreakpoint(sh2->debugsh, address); + + g_free(address_s); + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} + +static void yui_sh_bp_add(GtkEntry * entry, gpointer user_data) { + YuiSh * sh2 = user_data; + const gchar * address_s; + unsigned int address; + + address_s = gtk_entry_get_text(entry); + + if (*address_s == 0) return; + + sscanf(address_s, "%8X", &address); + + SH2AddCodeBreakpoint(sh2->debugsh, address); + + gtk_entry_set_text(entry, ""); + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} + +static void yui_sh_button_bp_add(GtkWidget * widget, gpointer user_data) { + g_signal_emit_by_name(user_data, "activate"); +} + +static void yui_sh_mbp_add(GtkEntry * entry, gpointer user_data) { + YuiSh * sh2 = user_data; + const gchar * address_s; + unsigned int address; + + address_s = gtk_entry_get_text(entry); + + if (*address_s == 0) return; + + sscanf(address_s, "%8X", &address); + + SH2AddMemoryBreakpoint(sh2->debugsh, address, BREAK_BYTEREAD|BREAK_WORDREAD|BREAK_LONGREAD|BREAK_BYTEWRITE|BREAK_WORDWRITE|BREAK_LONGWRITE); + + gtk_entry_set_text(entry, ""); + + SH2UpdateMemoryBreakpointList(sh2); +} + +gint yui_sh_mbp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data) +{ + GtkMenu *menu; + GdkEventButton *event_button; + YuiSh * sh2 = data; + GtkTreeView * view; + GtkTreeSelection * selection; + GtkTreeIter iter; + GtkTreeModel * model; + gchar * flags_s; + char *endptr; + int i; + guint signal_id; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + view = GTK_TREE_VIEW(sh2->mbpList); + menu = GTK_MENU(sh2->mbp_menu); + + if (event->type == GDK_BUTTON_PRESS) { + event_button = (GdkEventButton *) event; + if (event_button->button == 3) { + + GtkTreePath *path; + + selection = gtk_tree_view_get_selection(view); + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), event->x, event->y, &path, NULL, NULL, NULL)) { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } + + gtk_tree_selection_get_selected(selection, &model, &iter); + + if (gtk_tree_selection_count_selected_rows(selection) == 0) return FALSE; + + gtk_tree_model_get(model, &iter, 1, &flags_s, -1); + + signal_id = g_signal_lookup("activate", GTK_TYPE_CHECK_MENU_ITEM); + + for(i = 0;i < 6;i++) g_signal_handlers_block_matched(sh2->mbp_menu_item[i], G_SIGNAL_MATCH_DATA, signal_id, 0, NULL, NULL, sh2); + + for(i = 0;i < 6;i++) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[i]), FALSE); + + endptr = flags_s; + while ( *endptr ) { + switch (*endptr) { + + case 'b': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[0]), TRUE); break; + case 'w': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[1]), TRUE); break; + case 'l': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[2]), TRUE); break; + case 'B': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[3]), TRUE); break; + case 'W': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[4]), TRUE); break; + case 'L': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[5]), TRUE); break; + } + endptr++; + } + + for(i = 0;i < 6;i++) g_signal_handlers_unblock_matched(sh2->mbp_menu_item[i], G_SIGNAL_MATCH_DATA, signal_id, 0, NULL, NULL, sh2); + + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); + } + } + + return FALSE; +} + +void yui_sh_mbp_toggle_flag(GtkWidget * menuitem, gpointer user_data) { + GtkTreeSelection * selection; + YuiSh * sh2 = user_data; + GtkTreeIter iter; + GtkTreeModel * model; + gchar * address_s, * flags_s; + unsigned int address; + u32 flags; + GtkTreeView * view; + char *endptr; + + view = GTK_TREE_VIEW(sh2->mbpList); + + selection = gtk_tree_view_get_selection(view); + + gtk_tree_selection_get_selected(selection, &model, &iter); + + gtk_tree_model_get(model, &iter, 0, &address_s, -1); + gtk_tree_model_get(model, &iter, 1, &flags_s, -1); + sscanf(address_s, "%8X", &address); + + SH2DelMemoryBreakpoint(sh2->debugsh, address); + + flags = 0; + endptr = flags_s; + while ( *endptr ) { + switch (*endptr) { + case 'b': flags |= BREAK_BYTEREAD; break; + case 'w': flags |= BREAK_WORDREAD; break; + case 'l': flags |= BREAK_LONGREAD; break; + case 'B': flags |= BREAK_BYTEWRITE; break; + case 'W': flags |= BREAK_WORDWRITE; break; + case 'L': flags |= BREAK_LONGWRITE; break; + } + endptr++; + } + + if (menuitem == sh2->mbp_menu_item[0]) flags = (flags & ~BREAK_BYTEREAD) | (~flags & BREAK_BYTEREAD); + if (menuitem == sh2->mbp_menu_item[1]) flags = (flags & ~BREAK_WORDREAD) | (~flags & BREAK_WORDREAD); + if (menuitem == sh2->mbp_menu_item[2]) flags = (flags & ~BREAK_LONGREAD) | (~flags & BREAK_LONGREAD); + if (menuitem == sh2->mbp_menu_item[3]) flags = (flags & ~BREAK_BYTEWRITE) | (~flags & BREAK_BYTEWRITE); + if (menuitem == sh2->mbp_menu_item[4]) flags = (flags & ~BREAK_WORDWRITE) | (~flags & BREAK_WORDWRITE); + if (menuitem == sh2->mbp_menu_item[5]) flags = (flags & ~BREAK_LONGWRITE) | (~flags & BREAK_LONGWRITE); + + SH2AddMemoryBreakpoint(sh2->debugsh, address, flags); + + SH2UpdateMemoryBreakpointList(sh2); +} + +void yui_sh_mbp_remove(GtkWidget * menuitem, gpointer user_data) { + GtkTreeSelection * selection; + YuiSh * sh2 = user_data; + GtkTreeIter iter; + GtkTreeModel * model; + gchar * address_s; + unsigned int address; + GtkTreeView * view; + + view = GTK_TREE_VIEW(sh2->mbpList); + + selection = gtk_tree_view_get_selection(view); + + gtk_tree_selection_get_selected(selection, &model, &iter); + + gtk_tree_model_get(model, &iter, 0, &address_s, -1); + sscanf(address_s, "%8X", &address); + + SH2DelMemoryBreakpoint(sh2->debugsh, address); + + SH2UpdateMemoryBreakpointList(sh2); +} + +void yui_sh_mbp_clear(GtkWidget * menuitem, gpointer user_data) { + YuiSh * sh2 = user_data; + + SH2ClearMemoryBreakpoints(sh2->debugsh); + + SH2UpdateMemoryBreakpointList(sh2); +} + +gint yui_sh_bp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data) +{ + GtkMenu *menu; + GdkEventButton *event_button; + YuiSh * sh2 = data; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + menu = GTK_MENU(sh2->bp_menu); + + if (event->type == GDK_BUTTON_PRESS) { + event_button = (GdkEventButton *) event; + if (event_button->button == 3) { + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); + } + } + + return FALSE; +} + +void yui_sh_bp_remove(GtkWidget * menuitem, gpointer user_data) { + GtkTreeSelection * selection; + YuiSh * sh2 = user_data; + GtkTreeIter iter; + GtkTreeModel * model; + gchar * address_s; + unsigned int address; + GtkTreeView * view; + + view = GTK_TREE_VIEW(sh2->bpList); + + selection = gtk_tree_view_get_selection(view); + + gtk_tree_selection_get_selected(selection, &model, &iter); + + gtk_tree_model_get(model, &iter, 0, &address_s, -1); + sscanf(address_s, "%8X", &address); + + SH2DelCodeBreakpoint(sh2->debugsh, address); + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} + +void yui_sh_bp_clear(GtkWidget * menuitem, gpointer user_data) { + YuiSh * sh2 = user_data; + + SH2ClearCodeBreakpoints(sh2->debugsh); + + { + sh2regs_struct sh2regs; + SH2GetRegisters(sh2->debugsh, &sh2regs); + SH2UpdateCodeList(sh2,sh2regs.PC); + SH2UpdateBreakpointList(sh2); + } +} diff --git a/yabause/src/gtk/yuish.h b/yabause/src/gtk/yuish.h new file mode 100644 index 0000000000..60b8fe72bd --- /dev/null +++ b/yabause/src/gtk/yuish.h @@ -0,0 +1,90 @@ +/* Copyright 2005-2006 Fabien Coulon + Copyright 2008 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_SH_H +#define YUI_SH_H + +#include +#include +#include + +#include "../sh2core.h" +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_SH_TYPE (yui_sh_get_type ()) +#define YUI_SH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SH_TYPE, YuiSh)) +#define YUI_SH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SH_TYPE, YuiShClass)) +#define IS_YUI_SH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SH_TYPE)) +#define IS_YUI_SH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SH_TYPE)) + +typedef struct _YuiSh YuiSh; +typedef struct _YuiShClass YuiShClass; + +struct _YuiSh +{ + GtkWindow dialog; + + GtkWidget * view; + GtkWidget * toolbar; + GtkWidget *vbox; + GtkWidget *hboxmain; + GtkToolItem * buttonStep; + GtkWidget *bpList, *mbpList, *regList; //, *uLabel, *uFrame; + GtkListStore *bpListStore, *mbpListStore, *regListStore; + GtkCellRenderer *bpListRenderer, *mbpListRenderer, *regListRenderer1, *regListRenderer2; + GtkTreeViewColumn *bpListColumn, *mbpListColumn, *regListColumn1, *regListColumn2; + //u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ + //u32 cmbp[MAX_BREAKPOINTS]; /* the list of memory breakpoint positions, as they can be found in the list widget */ + //u32 mbpFlags[MAX_BREAKPOINTS]; + u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ + SH2_struct *debugsh; + gboolean bMaster; + gboolean breakpointEnabled; + gulong paused_handler; + gulong running_handler; + + GtkListStore * store; + + GtkWidget * bp_menu; + GtkWidget * mbp_menu; + GtkWidget * mbp_menu_item[6]; + + GtkWidget * vboxBp; +}; + +struct _YuiShClass +{ + GtkWindowClass parent_class; + + void (* yui_sh) (YuiSh * yv); +}; + +GType yui_sh_get_type (void); +GtkWidget * yui_msh_new(YuiWindow * y); +GtkWidget * yui_ssh_new(YuiWindow * y); +void yui_sh_fill (YuiSh * sh); +void yui_sh_update (YuiSh * sh); +void yui_sh_destroy (YuiSh * sh); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuitransfer.c b/yabause/src/gtk/yuitransfer.c new file mode 100644 index 0000000000..9373101907 --- /dev/null +++ b/yabause/src/gtk/yuitransfer.c @@ -0,0 +1,284 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "yuitransfer.h" +#include "../core.h" +#include "../memory.h" + +static void yui_transfer_class_init (YuiTransferClass * klass); +static void yui_transfer_init (YuiTransfer * yfe); +static void yui_transfer_browse (GtkWidget * widget, gpointer user_data); +static void yui_transfer_exec (GtkWidget * widget, YuiTransfer * yt); +static void yui_transfer_load (GtkWidget * entry, YuiTransfer * yt); +static void yui_transfer_load_exec (GtkWidget * entry, YuiTransfer * yt); +static void yui_transfer_store (GtkWidget * entry, YuiTransfer * yt); +static void yui_transfer_check (YuiTransfer * yt); + +GType yui_transfer_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiTransferClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_transfer_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiTransfer), + 0, + (GInstanceInitFunc) yui_transfer_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiTransfer", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_transfer_class_init (UNUSED YuiTransferClass * klass) { +} + +static void yui_transfer_init (YuiTransfer * yt) { + GtkWidget *vbox1; + GtkWidget *hbox1; + GtkWidget *label4; + GtkWidget *button1; + GtkWidget *hbox2; + GtkWidget *label2; + GtkWidget *hbuttonbox1; + GtkWidget *button5; + GtkWidget *hbox3; + GSList *radiobutton1_group = NULL; + GtkWidget *radiobutton1; + GtkWidget *radiobutton2; + GtkWidget *radiobutton3; + const char * tmp; + + gtk_window_set_title (GTK_WINDOW (yt), _("File transfer")); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (yt), vbox1); + + hbox1 = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox1), 10); + + tmp = _("File"); + label4 = gtk_label_new (tmp); + gtk_box_pack_start (GTK_BOX (hbox1), label4, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5); + gtk_label_set_width_chars (GTK_LABEL (label4), strlen(tmp)); + + yt->file_entry = gtk_entry_new (); + g_signal_connect_swapped(yt->file_entry, "changed", G_CALLBACK(yui_transfer_check), yt); + gtk_box_pack_start (GTK_BOX (hbox1), yt->file_entry, TRUE, TRUE, 0); + + button1 = gtk_button_new_with_mnemonic (_("Browse")); + g_signal_connect(button1, "clicked", G_CALLBACK(yui_transfer_browse), yt->file_entry); + gtk_box_pack_start (GTK_BOX (hbox1), button1, FALSE, FALSE, 0); + + hbox3 = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox1), hbox3, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox3), 10); + + radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("Load as executable")); + g_signal_connect(radiobutton1, "toggled", G_CALLBACK(yui_transfer_load_exec), yt); + gtk_box_pack_start (GTK_BOX (hbox3), radiobutton1, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton1), radiobutton1_group); + radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1)); + + radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("Load")); + g_signal_connect(radiobutton2, "toggled", G_CALLBACK(yui_transfer_load), yt); + gtk_box_pack_start (GTK_BOX (hbox3), radiobutton2, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group); + radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2)); + + radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("Store")); + g_signal_connect(radiobutton3, "toggled", G_CALLBACK(yui_transfer_store), yt); + gtk_box_pack_start (GTK_BOX (hbox3), radiobutton3, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group); + radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3)); + + hbox2 = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox2), 10); + + tmp = _("From"); + label2 = gtk_label_new (tmp); + gtk_box_pack_start (GTK_BOX (hbox2), label2, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); + gtk_label_set_width_chars (GTK_LABEL (label2), strlen(tmp)); + + yt->from_entry = gtk_entry_new (); + g_signal_connect_swapped(yt->from_entry, "changed", G_CALLBACK(yui_transfer_check), yt); + gtk_box_pack_start (GTK_BOX (hbox2), yt->from_entry, TRUE, TRUE, 0); + + tmp = _("To"); + yt->to_label = gtk_label_new (tmp); + gtk_box_pack_start (GTK_BOX (hbox2), yt->to_label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (yt->to_label), 0, 0.5); + gtk_label_set_width_chars (GTK_LABEL (yt->to_label), strlen(tmp)); + + yt->to_entry = gtk_entry_new (); + g_signal_connect_swapped(yt->to_entry, "changed", G_CALLBACK(yui_transfer_check), yt); + gtk_box_pack_start (GTK_BOX (hbox2), yt->to_entry, TRUE, TRUE, 0); + + hbuttonbox1 = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox1, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 10); + + yt->transfer_button = gtk_button_new_with_mnemonic (_("Transfer")); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), yt->transfer_button); + g_signal_connect(yt->transfer_button, "clicked", G_CALLBACK(yui_transfer_exec), yt); + GTK_WIDGET_SET_FLAGS (yt->transfer_button, GTK_CAN_DEFAULT); + + button5 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), button5); + g_signal_connect_swapped(button5, "clicked", G_CALLBACK(gtk_widget_destroy), yt); + GTK_WIDGET_SET_FLAGS (button5, GTK_CAN_DEFAULT); + + gtk_widget_show_all (GTK_WIDGET(yt)); + + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); + + yt->mode = YUI_TRANSFER_LOAD_EXEC; + +} + +GtkWidget * yui_transfer_new(YuiWindow * yw) { + GtkWidget * entry; + YuiTransfer * yfe; + + entry = GTK_WIDGET(g_object_new(yui_transfer_get_type(), NULL)); + yfe = YUI_TRANSFERT(entry); + + gtk_widget_show_all(entry); + + yui_transfer_check(yfe); + + yui_window_start(yw); + + return entry; +} + +static void yui_transfer_browse(UNUSED GtkWidget * widget, gpointer user_data) { + GtkWidget * file_selector; + gint result; + const gchar * filename; + + file_selector = gtk_file_chooser_dialog_new (_("Please choose a file"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + filename = gtk_entry_get_text(GTK_ENTRY(user_data)); + if (filename[0] != '\0') + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_selector), filename); + + gtk_widget_show(file_selector); + + result = gtk_dialog_run(GTK_DIALOG(file_selector)); + + switch(result) { + case GTK_RESPONSE_ACCEPT: + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); + gtk_entry_set_text(GTK_ENTRY(user_data), filename); + break; + case GTK_RESPONSE_CANCEL: + break; + } + + gtk_widget_destroy(file_selector); +} + +static void yui_transfer_exec(UNUSED GtkWidget * widget, YuiTransfer * yt) { + guint32 from, to; + + switch(yt->mode) { + case YUI_TRANSFER_LOAD: + sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); + MappedMemoryLoad(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from); + break; + case YUI_TRANSFER_LOAD_EXEC: + sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); + MappedMemoryLoadExec(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from); + break; + case YUI_TRANSFER_STORE: + sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); + sscanf(gtk_entry_get_text(GTK_ENTRY(yt->to_entry)), "%x", &to); + MappedMemorySave(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from, to - from); + break; + } + + gtk_widget_destroy(GTK_WIDGET(yt)); +} + +static void yui_transfer_load(GtkWidget * entry, YuiTransfer * yt) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { + yt->mode = YUI_TRANSFER_LOAD; + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); + yui_transfer_check(yt); + } +} + +static void yui_transfer_load_exec(GtkWidget * entry, YuiTransfer * yt) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { + yt->mode = YUI_TRANSFER_LOAD_EXEC; + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); + yui_transfer_check(yt); + } +} + +static void yui_transfer_store(GtkWidget * entry, YuiTransfer * yt) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { + yt->mode = YUI_TRANSFER_STORE; + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), TRUE); + yui_transfer_check(yt); + } +} + +static void yui_transfer_check(YuiTransfer * yt) { + gboolean ok = FALSE; + + if (*gtk_entry_get_text(GTK_ENTRY(yt->file_entry)) != '\0') { + switch(yt->mode) { + case YUI_TRANSFER_LOAD: + case YUI_TRANSFER_LOAD_EXEC: + if (*gtk_entry_get_text(GTK_ENTRY(yt->from_entry)) != '\0') { + ok = TRUE; + } + break; + case YUI_TRANSFER_STORE: + if ((*gtk_entry_get_text(GTK_ENTRY(yt->from_entry)) != '\0') && (*gtk_entry_get_text(GTK_ENTRY(yt->to_entry)) != '\0')) { + ok = TRUE; + } + break; + } + } + + gtk_widget_set_sensitive(yt->transfer_button, ok); +} diff --git a/yabause/src/gtk/yuitransfer.h b/yabause/src/gtk/yuitransfer.h new file mode 100644 index 0000000000..e6af1f1759 --- /dev/null +++ b/yabause/src/gtk/yuitransfer.h @@ -0,0 +1,70 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_TRANSFERT_H +#define YUI_TRANSFERT_H + +#include +#include +#include + +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_TRANSFERT_TYPE (yui_transfer_get_type ()) +#define YUI_TRANSFERT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_TRANSFERT_TYPE, YuiTransfer)) +#define YUI_TRANSFERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_TRANSFERT_TYPE, YuiTransferClass)) +#define IS_YUI_TRANSFERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_TRANSFERT_TYPE)) +#define IS_YUI_TRANSFERT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_TRANSFERT_TYPE)) + +#define YUI_TRANSFER_LOAD 1 +#define YUI_TRANSFER_LOAD_EXEC 2 +#define YUI_TRANSFER_STORE 3 + +typedef struct _YuiTransfer YuiTransfer; +typedef struct _YuiTransferClass YuiTransferClass; + +struct _YuiTransfer +{ + GtkWindow window; + + GtkWidget * file_entry; + GtkWidget * from_entry; + GtkWidget * to_label; + GtkWidget * to_entry; + GtkWidget * transfer_button; + + int mode; +}; + +struct _YuiTransferClass +{ + GtkWindowClass parent_class; + + void (* yui_transfer) (YuiTransfer * yfe); +}; + +GType yui_transfer_get_type (void); +GtkWidget* yui_transfer_new (YuiWindow * yw); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuivdp1.c b/yabause/src/gtk/yuivdp1.c new file mode 100644 index 0000000000..4b1e3ea5fa --- /dev/null +++ b/yabause/src/gtk/yuivdp1.c @@ -0,0 +1,289 @@ +/* Copyright 2006-2007 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "yuiviewer.h" +#include "yuivdp1.h" +#include "../vdp1.h" +#include "../yabause.h" +#include "settings.h" + +static void yui_vdp1_class_init (YuiVdp1Class * klass); +static void yui_vdp1_init (YuiVdp1 * yfe); +static void yui_vdp1_view_cursor_changed(GtkWidget * view, YuiVdp1 * vdp1); +static void yui_vdp1_clear(YuiVdp1 * vdp1); +static void yui_vdp1_draw(YuiVdp1 * vdp1); + +GType yui_vdp1_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiVdp1Class), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_vdp1_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiVdp1), + 0, + (GInstanceInitFunc) yui_vdp1_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiVdp1", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_vdp1_class_init (UNUSED YuiVdp1Class * klass) { +} + +static void yui_vdp1_init (YuiVdp1 * yv) { + GtkWidget * hbox, * vbox, * vbox2, * view; + + gtk_window_set_title(GTK_WINDOW(yv), "VDP1"); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 0); + gtk_container_add(GTK_CONTAINER(yv), vbox); + + yv->toolbar = gtk_toolbar_new(); + + gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); + + gtk_box_pack_start(GTK_BOX(vbox), yv->toolbar, FALSE, FALSE, 0); + + hbox = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4); + + yv->store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF); + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); + { + GtkWidget * scroll; + GtkCellRenderer *renderer; + GtkCellRenderer *icon; + GtkTreeViewColumn *column; + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + + icon = gtk_cell_renderer_pixbuf_new(); + g_object_set(icon, "xalign", 0, NULL); + column = gtk_tree_view_column_new_with_attributes("Icon", icon, "pixbuf", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + + gtk_container_add(GTK_CONTAINER(scroll), view); + gtk_paned_pack1(GTK_PANED(hbox), scroll, FALSE, TRUE); + } + g_signal_connect(view, "cursor-changed", G_CALLBACK(yui_vdp1_view_cursor_changed), yv); + + g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_vdp1_destroy), NULL); + + vbox2 = gtk_vpaned_new(); + gtk_paned_pack2(GTK_PANED(hbox), vbox2, TRUE, TRUE); + { + GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); + GtkWidget * text = gtk_text_view_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); + yv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + gtk_container_add(GTK_CONTAINER(scroll), text); + gtk_paned_pack1(GTK_PANED(vbox2), scroll, FALSE, TRUE); + } + yv->image = yui_viewer_new(); + { + GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yv->image); + gtk_paned_pack2(GTK_PANED(vbox2), scroll, TRUE, TRUE); + } + + yv->cursor = 0; + yv->texture = NULL; + + gtk_window_set_default_size(GTK_WINDOW(yv), 500, 450); + + gtk_paned_set_position(GTK_PANED(hbox), 250); + + gtk_paned_set_position(GTK_PANED(vbox2), 200); +} + +GtkWidget * yui_vdp1_new(YuiWindow * y) { + GtkWidget * dialog; + YuiVdp1 * yv; + + dialog = GTK_WIDGET(g_object_new(yui_vdp1_get_type(), NULL)); + yv = YUI_VDP1(dialog); + + yv->yui = y; + + if (!( yv->yui->state & YUI_IS_INIT )) { + yui_window_run(yv->yui); + yui_window_pause(yv->yui); + } + + { + GtkToolItem * play_button, * pause_button; + + play_button = gtk_tool_button_new_from_stock("run"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), -1); + + pause_button = gtk_tool_button_new_from_stock("pause"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), -1); + } + yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_vdp1_update), yv); + yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_vdp1_clear), yv); + + if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) + yui_vdp1_update(yv); + + gtk_widget_show_all(GTK_WIDGET(yv)); + + return dialog; +} + +void yui_vdp1_fill(YuiVdp1 * vdp1) { + gint j; + gchar * string; + gchar nameTemp[1024]; + GtkTreeIter iter; + + yui_vdp1_clear(vdp1); + + j = 0; + + string = Vdp1DebugGetCommandNumberName(j); + while(string && (j < MAX_VDP1_COMMAND)) { + gtk_list_store_append(vdp1->store, &iter); + gtk_list_store_set(vdp1->store, &iter, 0, string, -1); + + { + u32 * icontext; + int wtext, htext; + int rowstride; + GdkPixbuf * pixbuftext, * resized; + float ratio; + + icontext = Vdp1DebugTexture(j, &wtext, &htext); + + if ((icontext != NULL) && (wtext > 0) && (htext > 0)) { + rowstride = wtext * 4; + rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; + pixbuftext = gdk_pixbuf_new_from_data((const guchar *) icontext, GDK_COLORSPACE_RGB, TRUE, 8, + wtext, htext, rowstride, yui_texture_free, NULL); + + ratio = (float) 16 / htext; + if (htext > 16) { + resized = gdk_pixbuf_scale_simple(pixbuftext, wtext * ratio, 16, GDK_INTERP_BILINEAR); + } else { + resized = gdk_pixbuf_scale_simple(pixbuftext, wtext, htext, GDK_INTERP_BILINEAR); + } + + gtk_list_store_set(vdp1->store, &iter, 1, resized, -1); + + g_object_unref(pixbuftext); + g_object_unref(resized); + } + } + + j++; + string = Vdp1DebugGetCommandNumberName(j); + } + + Vdp1DebugCommand(vdp1->cursor, nameTemp); + gtk_text_buffer_set_text(vdp1->buffer, g_strstrip(nameTemp), -1); + vdp1->texture = Vdp1DebugTexture(vdp1->cursor, &vdp1->w, &vdp1->h); + yui_vdp1_draw(vdp1); +} + +static void yui_vdp1_view_cursor_changed(GtkWidget * view, YuiVdp1 * vdp1) { + GtkTreePath * path; + gchar * strpath; + int i; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL); + + if (path) { + gchar nameTemp[1024]; + + yui_viewer_clear(YUI_VIEWER(vdp1->image)); + + strpath = gtk_tree_path_to_string(path); + + sscanf(strpath, "%i", &i); + + vdp1->cursor = i; + + Vdp1DebugCommand(i, nameTemp); + gtk_text_buffer_set_text(vdp1->buffer, g_strstrip(nameTemp), -1); + vdp1->texture = Vdp1DebugTexture(i, &vdp1->w, &vdp1->h); + yui_vdp1_draw(vdp1); + + g_free(strpath); + gtk_tree_path_free(path); + } +} + +void yui_vdp1_update(YuiVdp1 * vdp1) { + gint i; + for(i = 0 ; i < MAX_VDP1_COMMAND ; i++ ) if ( !Vdp1DebugGetCommandNumberName(i)) break; + vdp1->cursor = 0; + yui_vdp1_fill(vdp1); +} + +void yui_vdp1_destroy(YuiVdp1 * vdp1) { + g_signal_handler_disconnect(vdp1->yui, vdp1->running_handler); + g_signal_handler_disconnect(vdp1->yui, vdp1->paused_handler); + + gtk_widget_destroy(GTK_WIDGET(vdp1)); +} + +static void yui_vdp1_clear(YuiVdp1 * vdp1) { + gtk_list_store_clear(vdp1->store); + gtk_text_buffer_set_text(vdp1->buffer, "", -1); + yui_viewer_clear(YUI_VIEWER(vdp1->image)); +} + +static void yui_vdp1_draw(YuiVdp1 * vdp1) { + GdkPixbuf * pixbuf; + int rowstride; + + if ((vdp1->texture != NULL) && (vdp1->w > 0) && (vdp1->h > 0)) { + rowstride = vdp1->w * 4; + rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; + pixbuf = gdk_pixbuf_new_from_data((const guchar *) vdp1->texture, GDK_COLORSPACE_RGB, TRUE, 8, + vdp1->w, vdp1->h, rowstride, yui_texture_free, NULL); + + yui_viewer_draw_pixbuf(YUI_VIEWER(vdp1->image), pixbuf, vdp1->w, vdp1->h); + + g_object_unref(pixbuf); + } +} diff --git a/yabause/src/gtk/yuivdp1.h b/yabause/src/gtk/yuivdp1.h new file mode 100644 index 0000000000..0ecc246595 --- /dev/null +++ b/yabause/src/gtk/yuivdp1.h @@ -0,0 +1,77 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_VDP1_H +#define YUI_VDP1_H + +#include + +#include "yuiwindow.h" +#include "../core.h" + +G_BEGIN_DECLS + +#define YUI_VDP1_TYPE (yui_vdp1_get_type ()) +#define YUI_VDP1(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VDP1_TYPE, YuiVdp1)) +#define YUI_VDP1_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VDP1_TYPE, YuiVdp1Class)) +#define IS_YUI_VDP1(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VDP1_TYPE)) +#define IS_YUI_VDP1_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VDP1_TYPE)) + +#define MAX_VDP1_COMMAND 4000 + +typedef struct _YuiVdp1 YuiVdp1; +typedef struct _YuiVdp1Class YuiVdp1Class; + +struct _YuiVdp1 +{ + GtkWindow dialog; + + GtkWidget * image; + GtkWidget * toolbar; + + GtkListStore * store; + GtkTextBuffer * buffer; + + gint cursor; + u32 * texture; + int w; + int h; + + gulong paused_handler; + gulong running_handler; + YuiWindow * yui; +}; + +struct _YuiVdp1Class +{ + GtkWindowClass parent_class; + + void (* yui_vdp1) (YuiVdp1 * yv); +}; + +GType yui_vdp1_get_type (void); +GtkWidget * yui_vdp1_new (YuiWindow * yui); +void yui_vdp1_fill (YuiVdp1 * vdp1); +void yui_vdp1_update (YuiVdp1 * vdp1); +void yui_vdp1_destroy (YuiVdp1 * vdp1); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuivdp2.c b/yabause/src/gtk/yuivdp2.c new file mode 100644 index 0000000000..46bdc82c54 --- /dev/null +++ b/yabause/src/gtk/yuivdp2.c @@ -0,0 +1,318 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "yuivdp2.h" +#include "../vdp2.h" +#include "../yabause.h" +#include "settings.h" +#include "../vdp2debug.h" +#include "yuiviewer.h" + +static void yui_vdp2_sync(GtkAction * action, YuiVdp2 * yv); +static const char * yui_vdp2_action_names[] = { NULL, "toggle_nbg0", "toggle_nbg1", "toggle_nbg2", "toggle_nbg3", "toggle_rbg0" }; + +static void yui_vdp2_class_init (YuiVdp2Class * klass); +static void yui_vdp2_init (YuiVdp2 * yfe); +static void yui_vdp2_clear(YuiVdp2 * vdp2); +static void yui_vdp2_view_cursor_changed(GtkWidget * view, YuiVdp2 * vdp2); +static void yui_vdp2_draw(YuiVdp2 * vdp2, u32 * texture, int w, int h); + +GType yui_vdp2_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiVdp2Class), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_vdp2_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiVdp2), + 0, + (GInstanceInitFunc) yui_vdp2_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiVdp2", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_vdp2_class_init (UNUSED YuiVdp2Class * klass) { +} + +static void yui_vdp2_toggle(GtkCellRendererToggle * crt, const gchar * path, YuiVdp2 * yv) { + int val; + GtkAction * action = NULL; + + sscanf(path, "%d", &val); + if (! yui_vdp2_action_names[val]) return; + + action = gtk_action_group_get_action(yv->yui->action_group, yui_vdp2_action_names[val]); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), ! gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))); +} + +static void yui_vdp2_sync(GtkAction * action, YuiVdp2 * yv) { + GtkTreeIter iter; + const gchar * name; + + name = gtk_action_get_name(action) + 7; + + if (!strcmp("nbg0", name)) + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "1"); + else if (!strcmp("nbg1", name)) + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "2"); + else if (!strcmp("nbg2", name)) + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "3"); + else if (!strcmp("nbg3", name)) + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "4"); + else if (!strcmp("rbg0", name)) + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "5"); + + gtk_list_store_set(yv->store, &iter, 1, gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)), -1); +} + +static void yui_vdp2_init (YuiVdp2 * yv) { + GtkWidget * text; + GtkWidget * scroll; + GtkWidget * box, * box2; + GtkWidget * hpane; + GtkWidget * view; + const char * screens[] = { "General", "NBG0/RBG1", "NBG1", "NBG2", "NBG3", "RBG0" }; + unsigned int i; + + gtk_window_set_title(GTK_WINDOW(yv), "VDP2"); + + box = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(box), 0); + gtk_container_add(GTK_CONTAINER(yv), box); + + yv->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); + gtk_box_pack_start(GTK_BOX(box), yv->toolbar, FALSE, FALSE, 0); + + hpane = gtk_hpaned_new(); + gtk_container_add(GTK_CONTAINER(box), hpane); + + yv->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN); + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); + { + GtkWidget * scroll; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), TRUE); + g_signal_connect(renderer, "toggled", G_CALLBACK(yui_vdp2_toggle), yv); + column = gtk_tree_view_column_new_with_attributes("Command", renderer, "active", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); + + gtk_container_add(GTK_CONTAINER(scroll), view); + gtk_paned_pack1(GTK_PANED(hpane), scroll, FALSE, TRUE); + } + g_signal_connect(view, "cursor-changed", G_CALLBACK(yui_vdp2_view_cursor_changed), yv); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_paned_pack2(GTK_PANED(hpane), scroll, TRUE, TRUE); + box2 = gtk_vpaned_new(); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box2); + + for(i = 0;i < (sizeof(screens) / sizeof(screens[0]));i++) { + GtkTreeIter iter; + gtk_list_store_append(yv->store, &iter); + gtk_list_store_set(yv->store, &iter, 0, screens[i], -1); + } + + text = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); + { + GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), text); + gtk_paned_pack1(GTK_PANED(box2), scroll, FALSE, TRUE); + } + + yv->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + + yv->image = yui_viewer_new(); + { + GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yv->image); + gtk_paned_pack2(GTK_PANED(box2), scroll, TRUE, TRUE); + } + + gtk_window_set_default_size(GTK_WINDOW(yv), 500, 450); + + gtk_paned_set_position(GTK_PANED(hpane), 120); + + g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_vdp2_destroy), NULL); +} + +GtkWidget * yui_vdp2_new(YuiWindow * y) { + GtkWidget * dialog; + YuiVdp2 * yv; + int i; + + dialog = GTK_WIDGET(g_object_new(yui_vdp2_get_type(), NULL)); + yv = YUI_VDP2(dialog); + + yv->yui = y; + + if (!( yv->yui->state & YUI_IS_INIT )) { + yui_window_run(yv->yui); + yui_window_pause(yv->yui); + } + + { + GtkToolItem * play_button, * pause_button; + + play_button = gtk_tool_button_new_from_stock("run"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), -1); + + pause_button = gtk_tool_button_new_from_stock("pause"); + gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); + gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), -1); + } + yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_vdp2_update), yv); + yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_vdp2_clear), yv); + + for(i = 0;i < (sizeof(yui_vdp2_action_names) / sizeof(yui_vdp2_action_names[0]));i++) { + GtkAction * action; + + if (! yui_vdp2_action_names[i]) continue; + + action = gtk_action_group_get_action(yv->yui->action_group, yui_vdp2_action_names[i]); + yui_vdp2_sync(action, yv); + g_signal_connect(action, "toggled", G_CALLBACK(yui_vdp2_sync), yv); + } + + if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) + yui_vdp2_update(yv); + + gtk_widget_show_all(GTK_WIDGET(yv)); + + return dialog; +} + +void yui_vdp2_update(YuiVdp2 * vdp2) { + gchar nameTemp[VDP2_DEBUG_STRING_SIZE]; + gboolean isscrenabled; + + yui_viewer_clear(YUI_VIEWER(vdp2->image)); + + switch(vdp2->cursor) { + case 0: + Vdp2DebugStatsGeneral(nameTemp, &isscrenabled); + break; + case 1: + Vdp2DebugStatsNBG0(nameTemp, &isscrenabled); + break; + case 2: + Vdp2DebugStatsNBG1(nameTemp, &isscrenabled); + break; + case 3: + Vdp2DebugStatsNBG2(nameTemp, &isscrenabled); + break; + case 4: + Vdp2DebugStatsNBG3(nameTemp, &isscrenabled); + break; + case 5: + Vdp2DebugStatsRBG0(nameTemp, &isscrenabled); + break; + } + + if (vdp2->cursor > 0) { + u32 * texture; + int w, h; + texture = Vdp2DebugTexture(vdp2->cursor - 1, &w, &h); + yui_vdp2_draw(vdp2, texture, w, h); + } + + if (isscrenabled) { + gtk_text_buffer_set_text(vdp2->buffer, nameTemp, -1); + } else { + gtk_text_buffer_set_text(vdp2->buffer, "", -1); + } +} + +void yui_vdp2_destroy(YuiVdp2 * vdp2) { + g_signal_handler_disconnect(vdp2->yui, vdp2->paused_handler); + g_signal_handler_disconnect(vdp2->yui, vdp2->running_handler); + gtk_widget_destroy(GTK_WIDGET(vdp2)); +} + +static void yui_vdp2_clear(YuiVdp2 * vdp2) { + gtk_text_buffer_set_text(vdp2->buffer, "", -1); +} + +void yui_vdp2_view_cursor_changed(GtkWidget * view, YuiVdp2 * vdp2) { + GtkTreePath * path; + gchar * strpath; + int i; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL); + + if (path) { + strpath = gtk_tree_path_to_string(path); + + sscanf(strpath, "%i", &i); + + vdp2->cursor = i; + + yui_vdp2_update(vdp2); + + g_free(strpath); + gtk_tree_path_free(path); + } +} + +static void yui_vdp2_draw(YuiVdp2 * vdp2, u32 * texture, int w, int h) { + GdkPixbuf * pixbuf; + int rowstride; + + if ((texture != NULL) && (w > 0) && (h > 0)) { + rowstride = w * 4; + rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; + pixbuf = gdk_pixbuf_new_from_data((const guchar *) texture, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, rowstride, yui_texture_free, NULL); + + yui_viewer_draw_pixbuf(YUI_VIEWER(vdp2->image), pixbuf, w, h); + + g_object_unref(pixbuf); + } +} diff --git a/yabause/src/gtk/yuivdp2.h b/yabause/src/gtk/yuivdp2.h new file mode 100644 index 0000000000..710d7f4068 --- /dev/null +++ b/yabause/src/gtk/yuivdp2.h @@ -0,0 +1,72 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_VDP2_H +#define YUI_VDP2_H + +#include +#include +#include + +#include "yuiwindow.h" + +G_BEGIN_DECLS + +#define YUI_VDP2_TYPE (yui_vdp2_get_type ()) +#define YUI_VDP2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VDP2_TYPE, YuiVdp2)) +#define YUI_VDP2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VDP2_TYPE, YuiVdp2Class)) +#define IS_YUI_VDP2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VDP2_TYPE)) +#define IS_YUI_VDP2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VDP2_TYPE)) + +typedef struct _YuiVdp2 YuiVdp2; +typedef struct _YuiVdp2Class YuiVdp2Class; + +struct _YuiVdp2 +{ + GtkWindow dialog; + + GtkWidget * image; + GtkTextBuffer * buffer; + GtkWidget * toolbar; + + GtkListStore * store; + + gint cursor; + + gulong paused_handler; + gulong running_handler; + + YuiWindow * yui; +}; + +struct _YuiVdp2Class +{ + GtkWindowClass parent_class; +}; + +GType yui_vdp2_get_type (void); +GtkWidget * yui_vdp2_new (YuiWindow * yui); +void yui_vdp2_fill (YuiVdp2 * vdp2); +void yui_vdp2_update (YuiVdp2 * vdp2); +void yui_vdp2_destroy (YuiVdp2 * vdp2); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuiviewer.c b/yabause/src/gtk/yuiviewer.c new file mode 100644 index 0000000000..e8ac5a07ea --- /dev/null +++ b/yabause/src/gtk/yuiviewer.c @@ -0,0 +1,166 @@ +/* Copyright 2006-2007 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "yuiviewer.h" +#include "gtkglwidget.h" +#include "../core.h" + +static void yui_viewer_class_init (YuiViewerClass * klass); +static void yui_viewer_init (YuiViewer * yfe); +static void yui_viewer_expose (GtkWidget * widget, GdkEventExpose *event, gpointer data); + +GType yui_viewer_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiViewerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_viewer_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiViewer), + 0, + (GInstanceInitFunc) yui_viewer_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "YuiViewer", &yfe_info, 0); + } + + return yfe_type; +} + +static void yui_viewer_class_init (UNUSED YuiViewerClass * klass) { +} + +static gint +my_popup_handler (GtkWidget *widget, GdkEvent *event) +{ + GtkMenu *menu; + GdkEventButton *event_button; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + /* The "widget" is the menu that was supplied when + * * g_signal_connect_swapped() was called. + * */ + menu = GTK_MENU (widget); + + if (event->type == GDK_BUTTON_PRESS) + { + event_button = (GdkEventButton *) event; + if (event_button->button == 3) + { + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, + event_button->button, event_button->time); + return TRUE; + } + } + + return FALSE; +} + +static void yui_viewer_init (YuiViewer * yv) { + GtkWidget * menu; + GtkWidget * item; + + gtk_widget_set_events(GTK_WIDGET(yv), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + menu = gtk_menu_new(); + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + gtk_widget_show_all(menu); + + g_signal_connect_swapped(yv, "button-press-event", G_CALLBACK(my_popup_handler), menu); + g_signal_connect(yv, "expose-event", G_CALLBACK(yui_viewer_expose), NULL); + g_signal_connect_swapped(item, "activate", G_CALLBACK(yui_viewer_save), yv); + + yv->pixbuf = NULL; +} + +GtkWidget * yui_viewer_new(void) { + GtkWidget * dialog; + + dialog = GTK_WIDGET(g_object_new(yui_viewer_get_type(), NULL)); + + return dialog; +} + +static void yui_viewer_expose(GtkWidget * widget, GdkEventExpose *event, gpointer data) { + YuiViewer * yv = YUI_VIEWER(widget); + + if (yv->pixbuf != NULL) { + gdk_draw_pixbuf(widget->window, NULL, yv->pixbuf, 0, 0, 0, 0, yv->w, yv->h, GDK_RGB_DITHER_NONE, 0, 0); + } +} + +void yui_viewer_save(YuiViewer * yv) { + GtkWidget * file_selector; + gint result; + char * filename; + int rowstride; + + file_selector = gtk_file_chooser_dialog_new ("Please choose a file", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + + gtk_widget_show(file_selector); + + result = gtk_dialog_run(GTK_DIALOG(file_selector)); + + switch(result) { + case GTK_RESPONSE_ACCEPT: + rowstride = yv->w * 4; + rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); + + gdk_pixbuf_save(yv->pixbuf, filename, "png", NULL, NULL); + + break; + case GTK_RESPONSE_CANCEL: + break; + } + + gtk_widget_destroy(file_selector); +} + +void yui_viewer_draw_pixbuf(YuiViewer * yv, GdkPixbuf * pixbuf, int w, int h) { + if (yv->pixbuf) { + g_object_unref(yv->pixbuf); + } + yv->pixbuf = g_object_ref(pixbuf); + yv->w = w; + yv->h = h; + gdk_window_clear(GTK_WIDGET(yv)->window); + gtk_widget_queue_draw_area(GTK_WIDGET(yv), 0, 0, w, h); +} + +void yui_viewer_clear(YuiViewer * yv) { + if (GTK_WIDGET(yv)->window != NULL) { + gdk_window_clear(GTK_WIDGET(yv)->window); + } +} diff --git a/yabause/src/gtk/yuiviewer.h b/yabause/src/gtk/yuiviewer.h new file mode 100644 index 0000000000..35b2e92f30 --- /dev/null +++ b/yabause/src/gtk/yuiviewer.h @@ -0,0 +1,59 @@ +/* Copyright 2006 Guillaume Duhamel + Copyright 2005-2006 Fabien Coulon + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_VIEWER_H +#define YUI_VIEWER_H + +#include + +G_BEGIN_DECLS + +#define YUI_VIEWER_TYPE (yui_viewer_get_type ()) +#define YUI_VIEWER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VIEWER_TYPE, YuiViewer)) +#define YUI_VIEWER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VIEWER_TYPE, YuiViewerClass)) +#define IS_YUI_VIEWER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VIEWER_TYPE)) +#define IS_YUI_VIEWER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VIEWER_TYPE)) + +typedef struct _YuiViewer YuiViewer; +typedef struct _YuiViewerClass YuiViewerClass; + +struct _YuiViewer +{ + GtkDrawingArea parent; + + int w; + int h; + GdkPixbuf * pixbuf; +}; + +struct _YuiViewerClass +{ + GtkDrawingAreaClass parent_class; +}; + +GType yui_viewer_get_type (void); +GtkWidget * yui_viewer_new (void); +void yui_viewer_draw_pixbuf(YuiViewer * yv, GdkPixbuf * pixbuf, int w, int h); +void yui_viewer_save (YuiViewer * yv); +void yui_viewer_clear (YuiViewer * yv); + +G_END_DECLS + +#endif diff --git a/yabause/src/gtk/yuiwindow.c b/yabause/src/gtk/yuiwindow.c new file mode 100644 index 0000000000..694484f599 --- /dev/null +++ b/yabause/src/gtk/yuiwindow.c @@ -0,0 +1,373 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "yuiwindow.h" +#include "gtkglwidget.h" +#include "../yabause.h" + +#include "settings.h" + +static void yui_window_class_init (YuiWindowClass * klass); +static void yui_window_init (YuiWindow * yfe); +static gboolean yui_window_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data); +static gboolean yui_window_keyrelease(GtkWidget *widget, GdkEventKey *event, gpointer user_data); +static void yui_window_keep_clean(GtkWidget * widget, GdkEventExpose * event, YuiWindow * yui); +static void yui_window_toggle_fullscreen(GtkWidget * w, YuiWindow * yui); +static void yui_window_toggle_frameskip(GtkWidget * w, YuiWindow * yui); + +static void yui_window_create_actions(YuiWindow * yw) { + GtkAction * action; + GtkToggleAction * taction; + + action = gtk_action_new("run", _("Run"), _("start emulation"), "gtk-media-play"); + gtk_action_group_add_action_with_accel(yw->action_group, action, "r"); + g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_run), yw); + + action = gtk_action_new("pause", _("Pause"), _("pause emulation"), "gtk-media-pause"); + gtk_action_group_add_action_with_accel(yw->action_group, action, "p"); + g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_pause), yw); + + action = gtk_action_new("reset", _("Reset"), _("reset emulation"), NULL); + gtk_action_group_add_action_with_accel(yw->action_group, action, NULL); + g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_reset), yw); + + taction = gtk_toggle_action_new("fullscreen", _("Fullscreen"), NULL, "gtk-fullscreen"); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), "f"); + g_signal_connect(taction, "activate", G_CALLBACK(yui_window_toggle_fullscreen), yw); + + taction = gtk_toggle_action_new("frameskip", _("Frame Skip/Limiter"), NULL, NULL); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(yui_window_toggle_frameskip), yw); + + action = gtk_action_new("quit", _("Quit"), NULL, "gtk-quit"); + gtk_action_group_add_action_with_accel(yw->action_group, action, "q"); + g_signal_connect(action, "activate", G_CALLBACK(gtk_main_quit), yw); + + taction = gtk_toggle_action_new("toggle_vdp1", _("VDP1"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleVDP1), NULL); + + taction = gtk_toggle_action_new("toggle_nbg0", _("NBG0"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG0), NULL); + + taction = gtk_toggle_action_new("toggle_nbg1", _("NBG1"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG1), NULL); + + taction = gtk_toggle_action_new("toggle_nbg2", _("NBG2"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG2), NULL); + + taction = gtk_toggle_action_new("toggle_nbg3", _("NBG3"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG3), NULL); + + taction = gtk_toggle_action_new("toggle_rbg0", _("RBG0"), NULL, NULL); + gtk_toggle_action_set_active(taction, TRUE); + gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); + g_signal_connect(taction, "activate", G_CALLBACK(ToggleRBG0), NULL); +} + +GType yui_window_get_type (void) { + static GType yfe_type = 0; + + if (!yfe_type) + { + static const GTypeInfo yfe_info = + { + sizeof (YuiWindowClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) yui_window_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (YuiWindow), + 0, + (GInstanceInitFunc) yui_window_init, + NULL, + }; + + yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiWindow", &yfe_info, 0); + } + + return yfe_type; +} + +enum { YUI_WINDOW_RUNNING_SIGNAL, YUI_WINDOW_PAUSED_SIGNAL, LAST_SIGNAL }; + +static guint yui_window_signals[LAST_SIGNAL] = { 0, 0 }; + +static void yui_window_class_init (YuiWindowClass * klass) { + yui_window_signals[YUI_WINDOW_RUNNING_SIGNAL] = g_signal_new ("running", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(YuiWindowClass, yui_window_running), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL] = g_signal_new ("paused", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(YuiWindowClass, yui_window_paused), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void yui_set_accel_group(gpointer action, gpointer group) { + gtk_action_set_accel_group(action, group); +} + +static gboolean yui_window_log_delete(GtkWidget *widget, GdkEvent *event, YuiWindow *yw ) { + + yui_window_show_log( yw ); + + return TRUE; /* hide instead of killing */ +} + +extern gchar * inifile; + +static void yui_window_destroy(GtkWidget * window) { + gint x, y; + char buffer[512]; + + gtk_window_get_position(GTK_WINDOW(window), &x, &y); + + sprintf(buffer, "%d", x); + g_key_file_set_value(keyfile, "Gtk", "X", buffer); + sprintf(buffer, "%d", y); + g_key_file_set_value(keyfile, "Gtk", "Y", buffer); + + g_file_set_contents(inifile, g_key_file_to_data(keyfile, 0, 0), -1, 0); + gtk_main_quit(); +} + +static void yui_window_init (YuiWindow * yw) { + GtkAccelGroup * accel_group = gtk_accel_group_new(); + GtkWidget * scroll; + + yw->action_group = gtk_action_group_new("yui"); + yui_window_create_actions(yw); + gtk_action_set_sensitive(gtk_action_group_get_action(yw->action_group, "pause"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(yw->action_group, "reset"), FALSE); + { + GList * list = gtk_action_group_list_actions(yw->action_group); + g_list_foreach(list, yui_set_accel_group, accel_group); + } + gtk_window_add_accel_group(GTK_WINDOW(yw), accel_group); + + { + const gchar * const * data_dir; + gboolean pngfound = FALSE; + gchar * pngfile; + + data_dir = g_get_system_data_dirs(); + while (!pngfound && (*data_dir != NULL)) { + pngfile = g_build_filename(*data_dir, "pixmaps", "yabause.png", NULL); + if (g_file_test(pngfile, G_FILE_TEST_EXISTS)) { + gtk_window_set_icon(GTK_WINDOW(yw), gdk_pixbuf_new_from_file(pngfile, NULL)); + pngfound = TRUE; + } + data_dir++; + } + + if (!pngfound) { + gtk_window_set_icon(GTK_WINDOW(yw), gdk_pixbuf_new_from_file("yabause.png", NULL)); + } + } + + gtk_window_set_title (GTK_WINDOW(yw), "Yabause"); + + yw->box = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(yw), yw->box); + + yw->menu = create_menu(yw); + gtk_box_pack_start(GTK_BOX(yw->box), yw->menu, FALSE, FALSE, 0); + + yw->area = yui_gl_new(); + gtk_box_pack_start(GTK_BOX(yw->box), yw->area, TRUE, TRUE, 0); + gtk_widget_set_size_request(GTK_WIDGET(yw->area), 320, 224); + + g_signal_connect(G_OBJECT(yw), "delete-event", G_CALLBACK(yui_window_destroy), NULL); + g_signal_connect(G_OBJECT(yw), "key-press-event", G_CALLBACK(yui_window_keypress), yw); + g_signal_connect(G_OBJECT(yw), "key-release-event", G_CALLBACK(yui_window_keyrelease), yw); + + yw->logpopup = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title( GTK_WINDOW( yw->logpopup ), "Yabause Logs" ); + gtk_widget_set_size_request( yw->logpopup, 500, 300 ); + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(yw->logpopup), scroll); + g_signal_connect(G_OBJECT(yw->logpopup), "delete-event", G_CALLBACK(yui_window_log_delete), yw); + + yw->log = gtk_text_view_new(); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yw->log); + + gtk_widget_show(yw->box); + gtk_widget_show_all(yw->menu); + gtk_widget_show(yw->area); + + yw->clean_handler = g_signal_connect(yw->area, "expose-event", G_CALLBACK(yui_window_keep_clean), yw); + yw->state = 0; +} + +GtkWidget * yui_window_new(YuiAction * act, GCallback ifunc, gpointer idata, + GSourceFunc rfunc, GCallback resetfunc) { + GtkWidget * widget; + YuiWindow * yw; + + widget = GTK_WIDGET(g_object_new(yui_window_get_type(), NULL)); + yw = YUI_WINDOW(widget); + + yw->actions = act; + yw->init_func = ifunc; + yw->init_data = idata; + yw->run_func = rfunc; + yw->reset_func = resetfunc; + + return widget; +} + +void yui_window_toggle_fullscreen(GtkWidget * w, YuiWindow * yui) { + GtkAction * action = gtk_action_group_get_action(yui->action_group, "fullscreen"); + static unsigned int beforefswidth = 1; + static unsigned int beforefsheight = 1; + + if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))) { + beforefswidth = GTK_WIDGET(yui)->allocation.width; + beforefsheight = GTK_WIDGET(yui)->allocation.height; + gtk_widget_hide(yui->menu); + gtk_window_fullscreen(GTK_WINDOW(yui)); + } else { + gtk_window_unfullscreen(GTK_WINDOW(yui)); + gtk_widget_show(yui->menu); + gtk_window_resize(GTK_WINDOW(yui), beforefswidth, beforefsheight); + } +} + +void yui_window_toggle_frameskip(GtkWidget * w, YuiWindow * yui) { + GtkAction * action = gtk_action_group_get_action(yui->action_group, "frameskip"); + gboolean active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); + + if (active) + EnableAutoFrameSkip (); + else + DisableAutoFrameSkip (); +} + +static gboolean yui_window_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { + PerKeyDown(event->keyval); + + return FALSE; +} + +static gboolean yui_window_keyrelease(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { + PerKeyUp(event->keyval); + + return FALSE; +} + +void yui_window_update(YuiWindow * yui) { + + if (!(yui->state & YUI_IS_RUNNING)) yui_gl_draw_pause(YUI_GL(yui->area)); + else yui_gl_draw(YUI_GL(yui->area)); +} + +void yui_window_log(YuiWindow * yui, const char * message) { + GtkTextBuffer * buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(yui->log)); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, message, -1); +} + +void yui_window_show_log(YuiWindow * yui) { + static int i = 0; + if (i) + gtk_widget_hide(yui->logpopup); + else + gtk_widget_show_all(yui->logpopup); + i = !i; +} + +static void yui_window_keep_clean(GtkWidget * widget, GdkEventExpose * event, YuiWindow * yui) { +#ifdef HAVE_LIBGTKGLEXT + glClear(GL_COLOR_BUFFER_BIT); +#endif + yui_window_update(yui); +} + +void yui_window_start(YuiWindow * yui) { + if ((yui->state & YUI_IS_INIT) == 0) { + if (((int (*)(gpointer)) yui->init_func)(yui->init_data) == 0) { + yui->state |= YUI_IS_INIT; + gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "reset"), TRUE); + VIDCore->Resize(GTK_WIDGET(yui->area)->allocation.width, GTK_WIDGET(yui->area)->allocation.height, FALSE); + } + } +} + +void yui_window_run(YuiWindow * yui) { + yui_window_start(yui); + + if ((yui->state & YUI_IS_INIT) && ((yui->state & YUI_IS_RUNNING) == 0)) { + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + g_idle_add(yui->run_func, GINT_TO_POINTER(1)); + g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_RUNNING_SIGNAL], 0); + yui->state |= YUI_IS_RUNNING; + gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "run"), FALSE); + gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "pause"), TRUE); + } +} + +void yui_window_pause(YuiWindow * yui) { + if (yui->state & YUI_IS_RUNNING) { + yui_gl_dump_screen(YUI_GL(yui->area)); + ScspMuteAudio(SCSP_MUTE_SYSTEM); + g_idle_remove_by_data(GINT_TO_POINTER(1)); + g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL], 0); + yui->state &= ~YUI_IS_RUNNING; + gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "run"), TRUE); + gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "pause"), FALSE); + } +} + +void yui_window_reset(YuiWindow * yui) { + if (yui->state & YUI_IS_INIT) { + yui->reset_func(); + } +} + +void yui_window_invalidate(YuiWindow * yui) { + + /* Emit a pause signal while already in pause means refresh all debug views */ + + if ( !(yui->state & YUI_IS_RUNNING )) + g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL], 0); +} + +void yui_window_set_fullscreen(YuiWindow * yui, gboolean f) { + GtkAction * action = gtk_action_group_get_action(yui->action_group, "fullscreen"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), f); +} + +void yui_window_set_frameskip(YuiWindow * yui, gboolean f) { + GtkAction * action = gtk_action_group_get_action(yui->action_group, "frameskip"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), f); +} diff --git a/yabause/src/gtk/yuiwindow.h b/yabause/src/gtk/yuiwindow.h new file mode 100644 index 0000000000..c0aa0da364 --- /dev/null +++ b/yabause/src/gtk/yuiwindow.h @@ -0,0 +1,94 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef YUI_WINDOW_H +#define YUI_WINDOW_H + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define YUI_WINDOW_TYPE (yui_window_get_type ()) +#define YUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_WINDOW_TYPE, YuiWindow)) +#define YUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_WINDOW_TYPE, YuiWindowClass)) +#define IS_YUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_WINDOW_TYPE)) +#define IS_YUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_WINDOW_TYPE)) + +typedef struct _YuiAction YuiAction; +typedef struct _YuiWindow YuiWindow; +typedef struct _YuiWindowClass YuiWindowClass; + +struct _YuiAction { + guint key; + const char * name; + void (*press)(void); + void (*release)(void); +}; + +#define YUI_IS_INIT 1 +#define YUI_IS_RUNNING 2 + +struct _YuiWindow { + GtkWindow hbox; + + GtkWidget * logpopup; + GtkWidget * box; + GtkWidget * menu; + GtkWidget * area; + GtkWidget * log; + + YuiAction * actions; + gulong clean_handler; + GCallback init_func; + gpointer init_data; + GSourceFunc run_func; + GCallback reset_func; + + guint state; + + GtkActionGroup * action_group; +}; + +struct _YuiWindowClass { + GtkWindowClass parent_class; + + void (* yui_window_running) (YuiWindow * yw); + void (* yui_window_paused) (YuiWindow * yw); +}; + +GType yui_window_get_type (void); +GtkWidget * yui_window_new (YuiAction * act, GCallback ifunc, gpointer idata, + GSourceFunc rfunc, GCallback resetfunc); +void yui_window_update (YuiWindow * yui); +void yui_window_log (YuiWindow * yui, const char * message); +void yui_window_show_log (YuiWindow * yui); +void yui_window_start (YuiWindow * yui); +void yui_window_run (YuiWindow * yui); +void yui_window_pause (YuiWindow * yui); +void yui_window_reset (YuiWindow * yui); +void yui_window_invalidate (YuiWindow * yui); +void yui_window_set_fullscreen(YuiWindow * yui, gboolean f); +void yui_window_set_frameskip(YuiWindow * yui, gboolean f); + +G_END_DECLS + +#endif /* YUI_WINDOW_H */ diff --git a/yabause/src/logo.bmp b/yabause/src/logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5fc521042ab20d3fe214a9764f852ae721813205 GIT binary patch literal 3126 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 vIyA!|t9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Yabause Logo + + + + + + + + + + + + + + + + + + + diff --git a/yabause/src/m68kc68k.c b/yabause/src/m68kc68k.c new file mode 100644 index 0000000000..5ab05390f5 --- /dev/null +++ b/yabause/src/m68kc68k.c @@ -0,0 +1,190 @@ +/* Copyright 2007 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "m68kc68k.h" +#include "c68k/c68k.h" +#include "memory.h" +#include "yabause.h" + +/** + * PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting + * the average time per 68000 clock cycle. (Realtime execution would be + * around 88.5 nsec/cycle.) + */ +// #define PROFILE_68K + + +static u8 *SoundDummy=NULL; + +static int M68KC68KInit(void) { + int i; + + // Setup a 64k buffer filled with invalid 68k instructions to serve + // as a default map + if ((SoundDummy = T2MemoryInit(0x10000)) != NULL) + memset(SoundDummy, 0xFF, 0x10000); + + C68k_Init(&C68K, NULL); // not sure if I need the int callback or not + + for (i = 0x10; i < 0x100; i++) + M68K->SetFetch(i << 16, (i << 16) + 0xFFFF, (pointer)SoundDummy); + + return 0; +} + +static void M68KC68KDeInit(void) { + if (SoundDummy) + T2MemoryDeInit(SoundDummy); + SoundDummy = NULL; +} + +static void M68KC68KReset(void) { + C68k_Reset(&C68K); +} + +static s32 FASTCALL M68KC68KExec(s32 cycle) { +#ifdef PROFILE_68K + static u32 tot_cycles = 0, tot_usec = 0, tot_ticks = 0, last_report = 0; + u32 start, end; + start = (u32) YabauseGetTicks(); + int retval = C68k_Exec(&C68K, cycle); + end = (u32) YabauseGetTicks(); + tot_cycles += cycle; + tot_ticks += end - start; + if (tot_cycles/1000000 > last_report) { + tot_usec += (u64)tot_ticks * 1000000 / yabsys.tickfreq; + tot_ticks = 0; + fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n", + (long)tot_cycles, (double)tot_usec/1000000, + ((double)tot_usec / (double)tot_cycles) * 1000); + last_report = tot_cycles/1000000; + } + return retval; +#else + return C68k_Exec(&C68K, cycle); +#endif +} + +static void M68KC68KSync(void) { +} + +static u32 M68KC68KGetDReg(u32 num) { + return C68k_Get_DReg(&C68K, num); +} + +static u32 M68KC68KGetAReg(u32 num) { + return C68k_Get_AReg(&C68K, num); +} + +static u32 M68KC68KGetPC(void) { + return C68k_Get_PC(&C68K); +} + +static u32 M68KC68KGetSR(void) { + return C68k_Get_SR(&C68K); +} + +static u32 M68KC68KGetUSP(void) { + return C68k_Get_USP(&C68K); +} + +static u32 M68KC68KGetMSP(void) { + return C68k_Get_MSP(&C68K); +} + +static void M68KC68KSetDReg(u32 num, u32 val) { + C68k_Set_DReg(&C68K, num, val); +} + +static void M68KC68KSetAReg(u32 num, u32 val) { + C68k_Set_AReg(&C68K, num, val); +} + +static void M68KC68KSetPC(u32 val) { + C68k_Set_PC(&C68K, val); +} + +static void M68KC68KSetSR(u32 val) { + C68k_Set_SR(&C68K, val); +} + +static void M68KC68KSetUSP(u32 val) { + C68k_Set_USP(&C68K, val); +} + +static void M68KC68KSetMSP(u32 val) { + C68k_Set_MSP(&C68K, val); +} + +static void M68KC68KSetFetch(u32 low_adr, u32 high_adr, pointer fetch_adr) { + C68k_Set_Fetch(&C68K, low_adr, high_adr, fetch_adr); +} + +static void FASTCALL M68KC68KSetIRQ(s32 level) { + C68k_Set_IRQ(&C68K, level); +} + +static void FASTCALL M68KC68KWriteNotify(u32 address, u32 size) { + /* nothing to do */ +} + +static void M68KC68KSetReadB(M68K_READ *Func) { + C68k_Set_ReadB(&C68K, Func); +} + +static void M68KC68KSetReadW(M68K_READ *Func) { + C68k_Set_ReadW(&C68K, Func); +} + +static void M68KC68KSetWriteB(M68K_WRITE *Func) { + C68k_Set_WriteB(&C68K, Func); +} + +static void M68KC68KSetWriteW(M68K_WRITE *Func) { + C68k_Set_WriteW(&C68K, Func); +} + +M68K_struct M68KC68K = { + 1, + "C68k Interface", + M68KC68KInit, + M68KC68KDeInit, + M68KC68KReset, + M68KC68KExec, + M68KC68KSync, + M68KC68KGetDReg, + M68KC68KGetAReg, + M68KC68KGetPC, + M68KC68KGetSR, + M68KC68KGetUSP, + M68KC68KGetMSP, + M68KC68KSetDReg, + M68KC68KSetAReg, + M68KC68KSetPC, + M68KC68KSetSR, + M68KC68KSetUSP, + M68KC68KSetMSP, + M68KC68KSetFetch, + M68KC68KSetIRQ, + M68KC68KWriteNotify, + M68KC68KSetReadB, + M68KC68KSetReadW, + M68KC68KSetWriteB, + M68KC68KSetWriteW +}; diff --git a/yabause/src/m68kc68k.h b/yabause/src/m68kc68k.h new file mode 100644 index 0000000000..a179715918 --- /dev/null +++ b/yabause/src/m68kc68k.h @@ -0,0 +1,27 @@ +/* Copyright 2007 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef M68KC68K_H +#define M68KC68K_H + +#include "m68kcore.h" + +extern M68K_struct M68KC68K; + +#endif diff --git a/yabause/src/m68kcore.c b/yabause/src/m68kcore.c new file mode 100644 index 0000000000..614c8e3562 --- /dev/null +++ b/yabause/src/m68kcore.c @@ -0,0 +1,167 @@ +/* Copyright 2007 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "m68kcore.h" +#include "m68kc68k.h" +#include "memory.h" + +extern u8 * SoundRam; + +M68K_struct * M68K = NULL; + +extern M68K_struct * M68KCoreList[]; + +int M68KInit(int coreid) { + int i; + + M68K = &M68KDummy; + + // Go through core list and find the id + for (i = 0; M68KCoreList[i] != NULL; i++) + { + if (M68KCoreList[i]->id == coreid) + { + // Set to current core + M68K = M68KCoreList[i]; + break; + } + } + + return 0; +} + +static int M68KDummyInit(void) { + return 0; +} + +static void M68KDummyDeInit(void) { +} + +static void M68KDummyReset(void) { +} + +static s32 FASTCALL M68KDummyExec(UNUSED s32 cycle) { + T2WriteWord(SoundRam, 0x700, 0); + T2WriteWord(SoundRam, 0x710, 0); + T2WriteWord(SoundRam, 0x720, 0); + T2WriteWord(SoundRam, 0x730, 0); + T2WriteWord(SoundRam, 0x740, 0); + T2WriteWord(SoundRam, 0x750, 0); + T2WriteWord(SoundRam, 0x760, 0); + T2WriteWord(SoundRam, 0x770, 0); + + T2WriteWord(SoundRam, 0x790, 0); + T2WriteWord(SoundRam, 0x792, 0); + return 0; +} + +static void M68KDummySync(void) { +} + +static u32 M68KDummyGetDReg(UNUSED u32 num) { + return 0; +} + +static u32 M68KDummyGetAReg(UNUSED u32 num) { + return 0; +} + +static u32 M68KDummyGetPC(void) { + return 0; +} + +static u32 M68KDummyGetSR(void) { + return 0; +} + +static u32 M68KDummyGetUSP(void) { + return 0; +} + +static u32 M68KDummyGetMSP(void) { + return 0; +} + +static void M68KDummySetDReg(UNUSED u32 num, UNUSED u32 val) { +} + +static void M68KDummySetAReg(UNUSED u32 num, UNUSED u32 val) { +} + +static void M68KDummySetPC(UNUSED u32 val) { +} + +static void M68KDummySetSR(UNUSED u32 val) { +} + +static void M68KDummySetUSP(UNUSED u32 val) { +} + +static void M68KDummySetMSP(UNUSED u32 val) { +} + +static void M68KDummySetFetch(UNUSED u32 low_adr, UNUSED u32 high_adr, UNUSED pointer fetch_adr) { +} + +static void FASTCALL M68KDummySetIRQ(UNUSED s32 level) { +} + +static void FASTCALL M68KDummyWriteNotify(u32 address, u32 size) { +} + +static void M68KDummySetReadB(UNUSED M68K_READ *Func) { +} + +static void M68KDummySetReadW(UNUSED M68K_READ *Func) { +} + +static void M68KDummySetWriteB(UNUSED M68K_WRITE *Func) { +} + +static void M68KDummySetWriteW(UNUSED M68K_WRITE *Func) { +} + +M68K_struct M68KDummy = { + 0, + "Dummy 68k Interface", + M68KDummyInit, + M68KDummyDeInit, + M68KDummyReset, + M68KDummyExec, + M68KDummySync, + M68KDummyGetDReg, + M68KDummyGetAReg, + M68KDummyGetPC, + M68KDummyGetSR, + M68KDummyGetUSP, + M68KDummyGetMSP, + M68KDummySetDReg, + M68KDummySetAReg, + M68KDummySetPC, + M68KDummySetSR, + M68KDummySetUSP, + M68KDummySetMSP, + M68KDummySetFetch, + M68KDummySetIRQ, + M68KDummyWriteNotify, + M68KDummySetReadB, + M68KDummySetReadW, + M68KDummySetWriteB, + M68KDummySetWriteW +}; diff --git a/yabause/src/m68kcore.h b/yabause/src/m68kcore.h new file mode 100644 index 0000000000..e20f88ab3c --- /dev/null +++ b/yabause/src/m68kcore.h @@ -0,0 +1,76 @@ +/* Copyright 2007 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef M68KCORE_H +#define M68KCORE_H + +#include "core.h" + +#define M68KCORE_DEFAULT -1 +#define M68KCORE_DUMMY 0 +#define M68KCORE_C68K 1 +#define M68KCORE_Q68 2 + +typedef u32 FASTCALL M68K_READ(const u32 adr); +typedef void FASTCALL M68K_WRITE(const u32 adr, u32 data); + +typedef struct { + int id; + const char *Name; + + int (*Init)(void); + void (*DeInit)(void); + void (*Reset)(void); + + s32 FASTCALL (*Exec)(s32 cycle); + void (*Sync)(void); + + u32 (*GetDReg)(u32 num); + u32 (*GetAReg)(u32 num); + u32 (*GetPC)(void); + u32 (*GetSR)(void); + u32 (*GetUSP)(void); + u32 (*GetMSP)(void); + + void (*SetDReg)(u32 num, u32 val); + void (*SetAReg)(u32 num, u32 val); + void (*SetPC)(u32 val); + void (*SetSR)(u32 val); + void (*SetUSP)(u32 val); + void (*SetMSP)(u32 val); + + void (*SetFetch)(u32 low_adr, u32 high_adr, pointer fetch_adr); + void FASTCALL (*SetIRQ)(s32 level); + void FASTCALL (*WriteNotify)(u32 address, u32 size); + + void (*SetReadB)(M68K_READ *Func); + void (*SetReadW)(M68K_READ *Func); + void (*SetWriteB)(M68K_WRITE *Func); + void (*SetWriteW)(M68K_WRITE *Func); +} M68K_struct; + +extern M68K_struct * M68K; + +int M68KInit(int coreid); + +extern M68K_struct M68KDummy; +extern M68K_struct M68KC68K; +extern M68K_struct M68KQ68; + +#endif diff --git a/yabause/src/m68kd.c b/yabause/src/m68kd.c new file mode 100644 index 0000000000..735c2315a6 --- /dev/null +++ b/yabause/src/m68kd.c @@ -0,0 +1,1327 @@ +/* Copyright 2005 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "core.h" +#include "m68kd.h" +#include "scsp.h" // for c68k_word_read() + +typedef struct +{ + u16 mask; + u16 inst; + const char *name; + int (*disasm)(u32, u16, char *); +} m68kdis_struct; + +////////////////////////////////////////////////////////////////////////////// + +static int setsizestr(u16 size, char *outstring) +{ + switch (size & 0x3) + { + case 0x1: + return sprintf(outstring, ".b "); + case 0x3: + return sprintf(outstring, ".w "); + case 0x2: + return sprintf(outstring, ".l "); + default: + return sprintf(outstring, " "); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static int setsizestr2(u16 size, char *outstring) +{ + switch (size & 0x3) + { + case 0x0: + return sprintf(outstring, ".b "); + case 0x1: + return sprintf(outstring, ".w "); + case 0x2: + return sprintf(outstring, ".l "); + default: + return sprintf(outstring, " "); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static int setimmstr(u32 addr, u16 size, int *addsize, char *outstring) +{ + switch (size & 0x3) + { + case 0x0: + *addsize+=2; + return sprintf(outstring, "#0x%X", (unsigned int)(c68k_word_read(addr) & 0xFF)); + case 0x1: + *addsize+=2; + return sprintf(outstring, "#0x%X", (unsigned int)c68k_word_read(addr)); + case 0x2: + *addsize+=4; + return sprintf(outstring, "#0x%X", (unsigned int)((c68k_word_read(addr) << 16) | c68k_word_read(addr+2))); + default: + return 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +static int seteafieldstr(u32 addr, u16 modereg, int *addsize, char *outstring) +{ + switch ((modereg >> 3) & 0x7) + { + case 0x0: + // Dn + return sprintf(outstring, "d%d", modereg & 0x7); + case 0x1: + // An + return sprintf(outstring, "a%d", modereg & 0x7); + case 0x2: + // (An) + return sprintf(outstring, "(a%d)", modereg & 0x7); + case 0x3: + // (An)+ + return sprintf(outstring, "(a%d)+", modereg & 0x7); + case 0x4: + // -(An) + return sprintf(outstring, "-(a%d)", modereg & 0x7); + case 0x5: + // (d16, An) + *addsize += 2; + return sprintf(outstring, "0x%X(a%d)", (unsigned int)c68k_word_read(addr), modereg & 0x7); + case 0x6: + // (d8,An,Xn) + // fix me + *addsize += 2; + return sprintf(outstring, "0x%X(a%d, Xn)", (unsigned int)(c68k_word_read(addr) & 0xFF), modereg & 0x7); + case 0x7: + switch (modereg & 0x7) + { + case 0x0: + // (xxx).W + *addsize += 2; // fix me? + return sprintf(outstring, "(0x%X).w", (unsigned int)c68k_word_read(addr)); + case 0x1: + // (xxx).L + *addsize += 4; // fix me? + return sprintf(outstring, "(0x%X).l", (unsigned int)((c68k_word_read(addr) << 16) | c68k_word_read(addr+2))); + case 0x4: + // # + *addsize += 2; // fix me? + return sprintf(outstring, "#0x%X", (unsigned int)c68k_word_read(addr)); + case 0x2: + // (d16,PC) + *addsize += 2; + return sprintf(outstring, "0x%X(PC)", (unsigned int)c68k_word_read(addr)); + case 0x3: + // (d8,PC,Xn) + // fix me + return 0; + default: break; + } + default: break; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int setcondstr(u16 cond, char *outstring) +{ + switch (cond & 0xF) + { + case 0x0: + // True + return sprintf(outstring, "t "); + case 0x1: + // False + return sprintf(outstring, "f "); + case 0x2: + // High + return sprintf(outstring, "hi"); + case 0x3: + // Low or Same + return sprintf(outstring, "ls"); + case 0x4: + // Carry Clear + return sprintf(outstring, "cc"); + case 0x5: + // Carry Set + return sprintf(outstring, "cs"); + case 0x6: + // Not Equal + return sprintf(outstring, "ne"); + case 0x7: + // Equal + return sprintf(outstring, "eq"); + case 0x8: + // Overflow Clear + return sprintf(outstring, "vc"); + case 0x9: + // Overflow Set + return sprintf(outstring, "vs"); + case 0xA: + // Plus + return sprintf(outstring, "pl"); + case 0xB: + // Minus + return sprintf(outstring, "mi"); + case 0xC: + // Greater or Equal + return sprintf(outstring, "ge"); + case 0xD: + // Less Than + return sprintf(outstring, "lt"); + case 0xE: + // Greater Than + return sprintf(outstring, "gt"); + case 0xF: + // Less or Equal + return sprintf(outstring, "le"); + default: break; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int setbranchdispstr(u32 addr, u16 op, int *addsize, char *outstring) +{ + if ((op & 0xFF) == 0xFF) + { + // 32-bit displacement + *addsize += 4; + return sprintf(outstring, ".l %X", (unsigned int)(addr + ((c68k_word_read(addr) << 16) | c68k_word_read(addr+2)))); + } + else if ((op & 0xFF) == 0x00) + { + // 16-bit displacement + *addsize += 2; + return sprintf(outstring, ".w %X", (unsigned int)((s32)addr + (s32)(s16)c68k_word_read(addr))); + } + + // 8-bit displacement + return sprintf(outstring, ".s %X", (unsigned int)((s32)addr + (s32)(s8)(op & 0xFF))); +} + +////////////////////////////////////////////////////////////////////////////// + +static int disabcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "abcd"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disadd(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "add"); + outstring += setsizestr2(op >> 6, outstring); + outstring += sprintf(outstring, " "); + + if (op & 0x100) + { + // Dn, + outstring += sprintf(outstring, "d%d, ", (op >> 9) & 7); + seteafieldstr(addr+size, op, &size, outstring); + } + else + { + // , Dn + outstring += seteafieldstr(addr+size, op, &size, outstring); + sprintf(outstring, ", d%d", (op >> 9) & 7); + } + + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disadda(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "adda"); + if ((op & 0x1C0) == 0xC0) + outstring += sprintf(outstring, ".w "); + else + outstring += sprintf(outstring, ".l "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disaddi(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "addi"); + outstring += setsizestr2(op >> 6, outstring); + outstring += setimmstr(addr+size, op >> 6, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disaddq(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "addq"); + outstring += setsizestr2(op >> 6, outstring); + outstring += sprintf(outstring, " "); + outstring += sprintf(outstring, "#%d, ", (op >> 9) & 7); // fix me + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disaddx(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "addx"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disand(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "and"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disandi(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "andi"); + outstring += setsizestr2(op >> 6, outstring); + outstring += sprintf(outstring, " "); + outstring += setimmstr(addr+size, op >> 6, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disanditoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "andi to CCR"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disasl(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "asl"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disasr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "asr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbcc(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "b"); + outstring += setcondstr(op >> 8, outstring); + setbranchdispstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbkpt(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bkpt"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbra(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bra"); + setbranchdispstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbchg(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bchg"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbclrd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bclr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbclrs(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bclr "); + outstring += setimmstr(addr+size, 0, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbsetd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bset"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbsets(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bset "); + outstring += setimmstr(addr+size, 0, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbtstd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "btst"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbtsts(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "btst "); + outstring += setimmstr(addr+size, 0, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disbsr(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "bsr"); + setbranchdispstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dischk(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "chk"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disclr(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "clr"); + outstring += setsizestr2((op >> 6), outstring); + outstring += sprintf(outstring, " "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disdbcc(u32 addr, u16 op, char *outstring) +{ + outstring += sprintf(outstring, "db"); + outstring += setcondstr(op >> 8, outstring); + outstring += sprintf(outstring, " "); + sprintf(outstring, " d%d, %X", op & 0x7, (unsigned int)((s32)addr+2+(s32)(s16)c68k_word_read(addr+2))); + return 4; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpb(u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "cmp.b "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpw(u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "cmp.w "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpl(u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "cmp.l "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpaw(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "cmpa.w"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpal(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "cmpa.l"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpi(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "cmpi"); + outstring += setsizestr2((op >> 6), outstring); + outstring += sprintf(outstring, " "); + outstring += setimmstr(addr+size, op >> 6, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disdivs(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "divs.w"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disdivu(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "divu.w"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int discmpm(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "cmpm"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int diseorb(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "eor.b"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int diseorw(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "eor.w"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int diseorl(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "eor.l"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int diseori(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "eori"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int diseoritoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "eori to ccr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disexg(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "exg"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disext(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "ext"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disillegal(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + sprintf(outstring, "illegal"); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disjmp(u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "jmp "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disjsr(u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "jsr "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dislea(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "lea "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dislink(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "link"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dislsl(UNUSED u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "lsl"); + outstring += setsizestr2(op >> 6, outstring); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dislsr(UNUSED u32 addr, u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "lsr"); + outstring += setsizestr2(op >> 6, outstring); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismove(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "move"); + outstring += setsizestr((op >> 12), outstring); + outstring += sprintf(outstring, " "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, ((op >> 3) & 0x38) | ((op >> 9) & 0x7), &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovea(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "movea"); + outstring += setsizestr((op >> 12), outstring); + outstring += seteafieldstr(addr+size, op, &size, outstring); + outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovetoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "move to ccr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovefromsr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "move from sr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovetosr(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "move "); + outstring += seteafieldstr(addr+size, op, &size, outstring); + sprintf(outstring, ", sr"); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovem(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + outstring += sprintf(outstring, "movem"); + // fix me + return 4; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismovep(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "movep"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismoveq(UNUSED u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "moveq #0x%X, d%d", op & 0xFF, (op >> 9) & 0x7); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismuls(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "muls"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dismulu(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "mulu"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disnbcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "nbcd"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disneg(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "neg"); + outstring += setsizestr2((op >> 6), outstring); + outstring += sprintf(outstring, " "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disnegx(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "negx"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disnop(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + sprintf(outstring, "nop"); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disnot(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "not"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disor(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "ori to CCR"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disori(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "ori"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disoritoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "ori to CCR"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dispea(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "pea"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disrol(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "rol"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disror(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "ror"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disroxl(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "roxl"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disroxr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "roxr"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disrtr(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + sprintf(outstring, "rtr"); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disrts(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + sprintf(outstring, "rts"); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissbcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "sbcd"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disscc(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "scc"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissub(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "sub"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissuba(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "suba"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissubi(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "subi"); + outstring += setsizestr2(op >> 6, outstring); + outstring += sprintf(outstring, " "); + outstring += setimmstr(addr+size, op >> 6, &size, outstring); + outstring += sprintf(outstring, ", "); + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissubq(u32 addr, u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "subq"); + outstring += setsizestr2(op >> 6, outstring); + outstring += sprintf(outstring, " #%d, ", (op >> 9) & 7); // fix me + seteafieldstr(addr+size, op, &size, outstring); + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int dissubx(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "subx"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disswap(UNUSED u32 addr, u16 op, char *outstring) +{ + sprintf(outstring, "swap d%d", op & 0x7); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int distas(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "tas"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int distrap(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "trap"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int distrapv(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + sprintf(outstring, "trapv"); + return 2; +} + +////////////////////////////////////////////////////////////////////////////// + +static int distst(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + + outstring += sprintf(outstring, "tst"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static int disunlk(UNUSED u32 addr, UNUSED u16 op, char *outstring) +{ + int size=2; + outstring += sprintf(outstring, "unlk"); + // fix me + return size; +} + +////////////////////////////////////////////////////////////////////////////// + +static m68kdis_struct instruction[] = { + { 0xFFFF, 0x023C, "andi #??, CCR", disanditoccr }, + { 0xFFFF, 0x0A3C, "eori #??, CCR", diseoritoccr }, + { 0xFFFF, 0x4AFC, "illegal", disillegal }, + { 0xFFFF, 0x4E71, "nop", disnop }, + { 0xFFFF, 0x003C, "ori #??, CCR", disoritoccr }, + { 0xFFFF, 0x4E77, "rtr", disrtr }, + { 0xFFFF, 0x4E75, "rts", disrts }, + { 0xFFFF, 0x4E76, "trapv", distrapv }, + { 0xFFF8, 0x4848, "bkpt", disbkpt }, + { 0xFFF8, 0x4E50, "link", dislink }, + { 0xFFF8, 0x4840, "swap", disswap }, + { 0xFFF8, 0x4E58, "unlk", disunlk }, + { 0xFFF0, 0x4E40, "trap", distrap }, + { 0xF1F8, 0xD100, "addx.b", disaddx }, + { 0xF1F8, 0xD140, "addx.w", disaddx }, + { 0xF1F8, 0xD180, "addx.l", disaddx }, + { 0xF1F8, 0xB108, "cmpm.b", discmpm }, + { 0xF1F8, 0xB148, "cmpm.w", discmpm }, + { 0xF1F8, 0xB188, "cmpm.l", discmpm }, + { 0xFFC0, 0xE1C0, "asl", disasl }, + { 0xFFC0, 0xE0C0, "asr", disasr }, + { 0xFFC0, 0x0880, "bclr", disbclrs }, + { 0xFFC0, 0x08C0, "bset", disbsets }, + { 0xFFC0, 0x0800, "btst", disbtsts }, + { 0xFFC0, 0x4EC0, "jmp", disjmp }, + { 0xFFC0, 0x4E80, "jsr", disjsr }, + { 0xFFC0, 0x44C0, "move ??, CCR", dismovetoccr }, + { 0xFFC0, 0x40C0, "move SR, ??", dismovefromsr }, + { 0xFFC0, 0x46C0, "move ??, SR", dismovetosr }, + { 0xFFC0, 0x4800, "nbcd", disnbcd }, + { 0xFFC0, 0x4840, "pea", dispea }, + { 0xFFC0, 0x4AC0, "tas", distas }, + { 0xFE38, 0x4800, "ext", disext }, + { 0xF1F0, 0xC100, "abcd", disabcd }, + { 0xF1F0, 0x8100, "sbcd", dissbcd }, + { 0xF0F8, 0x50C8, "dbcc", disdbcc }, + { 0xFB80, 0x4880, "movem", dismovem }, + { 0xFF00, 0x0600, "addi", disaddi }, + { 0xFF00, 0x0200, "andi", disandi }, + { 0xFF00, 0x6000, "bra", disbra }, + { 0xFF00, 0x6100, "bsr", disbsr }, + { 0xFF00, 0x4200, "clr", disclr }, + { 0xFF00, 0x0C00, "cmpi", discmpi }, + { 0xFF00, 0x0A00, "eori", diseori }, + { 0xFF00, 0x4400, "neg", disneg }, + { 0xFF00, 0x4000, "negx", disnegx }, + { 0xFF00, 0x4600, "not", disnot }, + { 0xFF00, 0x0000, "ori", disori }, + { 0xFF00, 0x0400, "subi", dissubi }, + { 0xFF00, 0x4A00, "tst", distst }, + { 0xF118, 0xE108, "lsl", dislsl }, + { 0xF118, 0xE008, "lsr", dislsr }, + { 0xF118, 0xE118, "rol", disrol }, + { 0xF118, 0xE018, "ror", disror }, + { 0xF118, 0xE110, "roxl", disroxl }, + { 0xF118, 0xE010, "roxr", disroxr }, + { 0xF1C0, 0xD0C0, "adda.w", disadda }, + { 0xF1C0, 0xD1C0, "adda.l", disadda }, + { 0xF1C0, 0x0140, "bchg", disbchg }, + { 0xF1C0, 0x0180, "bclr", disbclrd }, + { 0xF1C0, 0x01C0, "bset", disbsetd }, + { 0xF1C0, 0x0100, "btst", disbtstd }, + { 0xF1C0, 0xB000, "cmp.b", discmpb }, + { 0xF1C0, 0xB040, "cmp.w", discmpw }, + { 0xF1C0, 0xB080, "cmp.l", discmpl }, + { 0xF1C0, 0xB0C0, "cmpa.w", discmpaw }, + { 0xF1C0, 0xB1C0, "cmpa.l", discmpal }, + { 0xF1C0, 0x81C0, "divs.w", disdivs }, + { 0xF1C0, 0x80C0, "divu.w", disdivu }, + { 0xF1C0, 0xB100, "eor.b", diseorb }, + { 0xF1C0, 0xB140, "eor.w", diseorw }, + { 0xF1C0, 0xB180, "eor.l", diseorl }, + { 0xF1C0, 0x41C0, "lea", dislea }, + { 0xF1C0, 0xC1C0, "muls", dismuls }, + { 0xF1C0, 0xC0C0, "mulu", dismulu }, + { 0xF130, 0x9100, "subx", dissubx }, + { 0xF038, 0x0008, "movep", dismovep }, + { 0xF0C0, 0x50C0, "scc", disscc }, + { 0xC1C0, 0x0040, "movea", dismovea }, + { 0xF040, 0x4000, "chk", dischk }, + { 0xF100, 0x5000, "addq", disaddq }, + { 0xF100, 0xC100, "exg", disexg }, + { 0xF100, 0x7000, "moveq", dismoveq }, + { 0xF100, 0x5100, "subq", dissubq }, + { 0xF000, 0xD000, "add", disadd }, // fix me + { 0xF000, 0xC000, "and", disand }, + { 0xF000, 0x6000, "bcc", disbcc }, + { 0xF000, 0x8000, "or", disor }, + { 0xF000, 0x9000, "sub", dissub }, // fix me + { 0xF000, 0x9000, "suba", dissuba }, // fix me + { 0xC000, 0x0000, "move", dismove }, + { 0x0000, 0x0000, NULL, NULL } +}; + +////////////////////////////////////////////////////////////////////////////// + +u32 M68KDisasm(u32 addr, char *outstring) +{ + int i; + + outstring += sprintf(outstring, "%05X: ", (unsigned int)addr); + + for (i = 0; instruction[i].name != NULL; i++) + { + u16 op = (u16)c68k_word_read(addr); + + if ((op & instruction[i].mask) == instruction[i].inst) + { + addr += instruction[i].disasm(addr, op, outstring); + return addr; + } + } + + sprintf(outstring, "unknown"); + return (addr+2); +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/m68kd.h b/yabause/src/m68kd.h new file mode 100644 index 0000000000..16383e901e --- /dev/null +++ b/yabause/src/m68kd.h @@ -0,0 +1,27 @@ +/* Copyright 2004 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef M68KD_H +#define M68KD_H + +#include "core.h" + +u32 M68KDisasm(u32 addr, char *outstring); + +#endif diff --git a/yabause/src/m68kq68.c b/yabause/src/m68kq68.c new file mode 100644 index 0000000000..0f1605112f --- /dev/null +++ b/yabause/src/m68kq68.c @@ -0,0 +1,534 @@ +/* src/m68kpsp.c: Q68 emulator interface + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "yabause.h" +#include "m68kcore.h" + +#include "q68/q68.h" + +/*************************************************************************/ + +/** + * NEED_TRAMPOLINE: Defined on platforms where we need trampoline + * functions to convert read/write calls from the native format to the + * FASTCALL format used by Yabause. + */ +#ifdef CPU_X86 +# define NEED_TRAMPOLINE +#endif + +/** + * PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting + * the average time per 68000 clock cycle. (Realtime execution would be + * around 88.5 nsec/cycle.) + * + * Note that this profiling has an overhead of two YabauseGetTicks() calls + * for each M68K->Exec() call; on PSP, this amounts to about 3.8 usec per + * Exec(), or 52.8 nsec/cycle at 72 cycles/call. + */ +// #define PROFILE_68K + +// #define COUNT_OPCODES // see COUNT_OPCODES in q68-core.c + +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int m68kq68_init(void); +static void m68kq68_deinit(void); +static void m68kq68_reset(void); + +static FASTCALL s32 m68kq68_exec(s32 cycles); +static void m68kq68_sync(void); + +static u32 m68kq68_get_dreg(u32 num); +static u32 m68kq68_get_areg(u32 num); +static u32 m68kq68_get_pc(void); +static u32 m68kq68_get_sr(void); +static u32 m68kq68_get_usp(void); +static u32 m68kq68_get_ssp(void); + +static void m68kq68_set_dreg(u32 num, u32 val); +static void m68kq68_set_areg(u32 num, u32 val); +static void m68kq68_set_pc(u32 val); +static void m68kq68_set_sr(u32 val); +static void m68kq68_set_usp(u32 val); +static void m68kq68_set_ssp(u32 val); + +static FASTCALL void m68kq68_set_irq(s32 level); +static FASTCALL void m68kq68_write_notify(u32 address, u32 size); + +static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr); +static void m68kq68_set_readb(M68K_READ *func); +static void m68kq68_set_readw(M68K_READ *func); +static void m68kq68_set_writeb(M68K_WRITE *func); +static void m68kq68_set_writew(M68K_WRITE *func); + +static uint32_t dummy_read(uint32_t address); +static void dummy_write(uint32_t address, uint32_t data); + +#ifdef NEED_TRAMPOLINE +static uint32_t readb_trampoline(uint32_t address); +static uint32_t readw_trampoline(uint32_t address); +static void writeb_trampoline(uint32_t address, uint32_t data); +static void writew_trampoline(uint32_t address, uint32_t data); +#endif + +/*-----------------------------------------------------------------------*/ + +/* Module interface definition */ + +M68K_struct M68KQ68 = { + .id = M68KCORE_Q68, + .Name = "Q68 68k Emulator Interface", + + .Init = m68kq68_init, + .DeInit = m68kq68_deinit, + .Reset = m68kq68_reset, + + .Exec = m68kq68_exec, + .Sync = m68kq68_sync, + + .GetDReg = m68kq68_get_dreg, + .GetAReg = m68kq68_get_areg, + .GetPC = m68kq68_get_pc, + .GetSR = m68kq68_get_sr, + .GetUSP = m68kq68_get_usp, + .GetMSP = m68kq68_get_ssp, + + .SetDReg = m68kq68_set_dreg, + .SetAReg = m68kq68_set_areg, + .SetPC = m68kq68_set_pc, + .SetSR = m68kq68_set_sr, + .SetUSP = m68kq68_set_usp, + .SetMSP = m68kq68_set_ssp, + + .SetIRQ = m68kq68_set_irq, + .WriteNotify = m68kq68_write_notify, + + .SetFetch = m68kq68_set_fetch, + .SetReadB = m68kq68_set_readb, + .SetReadW = m68kq68_set_readw, + .SetWriteB = m68kq68_set_writeb, + .SetWriteW = m68kq68_set_writew, +}; + +/*-----------------------------------------------------------------------*/ + +/* Virtual processor state block */ + +static Q68State *state; + + +#ifdef NEED_TRAMPOLINE + +/* Read/write functions passed to Set{Read,Write}[BW], called via the + * trampolines */ +static M68K_READ *real_readb, *real_readw; +static M68K_WRITE *real_writeb, *real_writew; + +#endif + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * m68kq68_init: Initialize the virtual processpr. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on failure + */ +static int m68kq68_init(void) +{ + if (!(state = q68_create())) { + return -1; + } + q68_set_irq(state, 0); + q68_set_readb_func(state, dummy_read); + q68_set_readw_func(state, dummy_read); + q68_set_writeb_func(state, dummy_write); + q68_set_writew_func(state, dummy_write); + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_deinit: Destroy the virtual processor. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void m68kq68_deinit(void) +{ + q68_destroy(state); + state = NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_reset: Reset the virtual processor. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void m68kq68_reset(void) +{ + q68_reset(state); +} + +/*************************************************************************/ + +/** + * m68kq68_exec: Execute instructions for the given number of clock cycles. + * + * [Parameters] + * cycles: Number of clock cycles to execute + * [Return value] + * Number of clock cycles actually executed + */ +static FASTCALL s32 m68kq68_exec(s32 cycles) +{ +#ifdef PROFILE_68K + static uint32_t tot_cycles = 0, tot_usec = 0, tot_ticks = 0; + static uint32_t last_report = 0; + uint32_t start, end; + start = (uint32_t) YabauseGetTicks(); + int retval = q68_run(state, cycles); + end = (uint32_t) YabauseGetTicks(); + tot_cycles += cycles; + tot_ticks += end - start; + if (tot_cycles/1000000 > last_report) { + tot_usec += (uint64_t)tot_ticks * 1000000 / yabsys.tickfreq; + tot_ticks = 0; + fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n", + (long)tot_cycles, (double)tot_usec/1000000, + ((double)tot_usec / (double)tot_cycles) * 1000); + last_report = tot_cycles/1000000; +# ifdef COUNT_OPCODES + if (last_report % 100 == 0) { + extern uint32_t q68_ops[128], q68_4xxx_ops[32]; + int i; + fprintf(stderr, "Opcodes per 1M cycles:\n"); + for (i = 0; i < 128; i++) { + fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "", + (long)((q68_ops[i] + last_report/2) / last_report), + i%8==7 ? "\n" : ""); + } + fprintf(stderr, "$4xxx opcodes per 1M cycles:\n"); + for (i = 0; i < 32; i++) { + fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "", + (long)((q68_4xxx_ops[i] + last_report/2) / last_report), + i%8==7 ? "\n" : ""); + } + } +# endif // COUNT_OPCODES + } + return retval; +#else // !PROFILE_68K + return q68_run(state, cycles); +#endif +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_sync: Wait for background execution to finish. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void m68kq68_sync(void) +{ + /* Nothing to do */ +} + +/*************************************************************************/ + +/** + * m68kq68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of + * the specified register. + * + * [Parameters] + * num: Register number (m68kq68_get_dreg(), m68kq68_get_areg() only) + * [Return value] + * None + */ + +static u32 m68kq68_get_dreg(u32 num) +{ + return q68_get_dreg(state, num); +} + +static u32 m68kq68_get_areg(u32 num) +{ + return q68_get_areg(state, num); +} + +static u32 m68kq68_get_pc(void) +{ + return q68_get_pc(state); +} + +static u32 m68kq68_get_sr(void) +{ + return q68_get_sr(state); +} + +static u32 m68kq68_get_usp(void) +{ + return q68_get_usp(state); +} + +static u32 m68kq68_get_ssp(void) +{ + return q68_get_ssp(state); +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified + * register. + * + * [Parameters] + * num: Register number (m68kq68_set_dreg(), m68kq68_set_areg() only) + * val: Value to set + * [Return value] + * None + */ + +static void m68kq68_set_dreg(u32 num, u32 val) +{ + q68_set_dreg(state, num, val); +} + +static void m68kq68_set_areg(u32 num, u32 val) +{ + q68_set_areg(state, num, val); +} + +static void m68kq68_set_pc(u32 val) +{ + q68_set_pc(state, val); +} + +static void m68kq68_set_sr(u32 val) +{ + q68_set_sr(state, val); +} + +static void m68kq68_set_usp(u32 val) +{ + q68_set_usp(state, val); +} + +static void m68kq68_set_ssp(u32 val) +{ + q68_set_ssp(state, val); +} + +/*************************************************************************/ + +/** + * m68kq68_set_irq: Deliver an interrupt to the processor. + * + * [Parameters] + * level: Interrupt level (0-7) + * [Return value] + * None + */ +static FASTCALL void m68kq68_set_irq(s32 level) +{ + q68_set_irq(state, level); +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_write_notify: Inform the 68k emulator that the given address + * range has been modified. + * + * [Parameters] + * address: 68000 address of modified data + * size: Size of modified data in bytes + * [Return value] + * None + */ +static FASTCALL void m68kq68_write_notify(u32 address, u32 size) +{ + q68_touch_memory(state, address, size); +} + +/*************************************************************************/ + +/** + * m68kq68_set_fetch: Set the instruction fetch pointer for a region of + * memory. Not used by Q68. + * + * [Parameters] + * low_addr: Low address of memory region to set + * high_addr: High address of memory region to set + * fetch_addr: Pointer to corresponding memory region (NULL to disable) + * [Return value] + * None + */ +static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr) +{ +} + +/*-----------------------------------------------------------------------*/ + +/** + * m68kq68_set_{readb,readw,writeb,writew}: Set functions for reading or + * writing bytes or words in memory. + * + * [Parameters] + * func: Function to set + * [Return value] + * None + */ + +static void m68kq68_set_readb(M68K_READ *func) +{ +#ifdef NEED_TRAMPOLINE + real_readb = func; + q68_set_readb_func(state, readb_trampoline); +#else + q68_set_readb_func(state, (Q68ReadFunc *)func); +#endif +} + +static void m68kq68_set_readw(M68K_READ *func) +{ +#ifdef NEED_TRAMPOLINE + real_readw = func; + q68_set_readw_func(state, readw_trampoline); +#else + q68_set_readw_func(state, (Q68ReadFunc *)func); +#endif +} + +static void m68kq68_set_writeb(M68K_WRITE *func) +{ +#ifdef NEED_TRAMPOLINE + real_writeb = func; + q68_set_writeb_func(state, writeb_trampoline); +#else + q68_set_writeb_func(state, (Q68WriteFunc *)func); +#endif +} + +static void m68kq68_set_writew(M68K_WRITE *func) +{ +#ifdef NEED_TRAMPOLINE + real_writew = func; + q68_set_writew_func(state, writew_trampoline); +#else + q68_set_writew_func(state, (Q68WriteFunc *)func); +#endif +} + +/*************************************************************************/ + +/** + * dummy_read: Default read function, always returning 0 for any address. + * + * [Parameters] + * address: Address to read from + * [Return value] + * Value read (always zero) + */ +static uint32_t dummy_read(uint32_t address) +{ + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * dummy_write: Default write function, ignoring all writes. + * + * [Parameters] + * address: Address to write to + * data: Value to write + * [Return value] + * None + */ +static void dummy_write(uint32_t address, uint32_t data) +{ +} + +/*-----------------------------------------------------------------------*/ + +#ifdef NEED_TRAMPOLINE + +/** + * read[bw]_trampoline, write[bw]_trampoline: Adjust calling conventions + * between the M68k emulator and Yabause's M68k core. + * + * [Parameters] + * address: Address to read from + * data: Value to write (only for write_trampoline) + * [Return value] + * Value read (only for read_trampoline) + */ + +static uint32_t readb_trampoline(uint32_t address) { + return (*real_readb)(address); +} + +static uint32_t readw_trampoline(uint32_t address) { + return (*real_readw)(address); +} + +static void writeb_trampoline(uint32_t address, uint32_t data) { + return (*real_writeb)(address, data); +} + +static void writew_trampoline(uint32_t address, uint32_t data) { + return (*real_writew)(address, data); +} + +#endif // NEED_TRAMPOLINE + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/macjoy.c b/yabause/src/macjoy.c new file mode 100644 index 0000000000..0d202a50ae --- /dev/null +++ b/yabause/src/macjoy.c @@ -0,0 +1,516 @@ +/* This file was imported form CrabEmu ( http://crabemu.sourceforge.net/ ) -- + A Sega Master System emulator for Mac OS X (among other targets). The rest + of the file is left intact from CrabEmu to make things easier if this were + to be upgraded in the future. */ + +/* + This file is part of CrabEmu. + + Copyright (C) 2008 Lawrence Sebald + + CrabEmu is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + CrabEmu is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CrabEmu; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "macjoy.h" + +static int joy_count = 0; +static joydata_t *joys = NULL; + +static void joy_find_elements(CFMutableDictionaryRef prop, joydata_t *joy); + +/* Compare two buttons for sorting. */ +static int joy_cmp_buttons(const void *e1, const void *e2) { + const joy_elemdata_t *b1 = (const joy_elemdata_t *)e1; + const joy_elemdata_t *b2 = (const joy_elemdata_t *)e2; + + return b1->number < b2->number ? -1 : + (b1->number > b2->number ? 1 : 0); +} + +/* Get the io_iterator_t object needed to iterate through the list of HID + devices. */ +static void joy_get_iterator(mach_port_t port, io_iterator_t *iter) { + CFMutableDictionaryRef d; + IOReturn rv; + + /* Create a matching dictionary that will be used to search the device + tree. */ + if(!(d = IOServiceMatching(kIOHIDDeviceKey))) { + return; + } + + /* Get all matching devices from IOKit. */ + rv = IOServiceGetMatchingServices(port, d, iter); + + if(rv != kIOReturnSuccess || !(*iter)) { + return; + } +} + +/* Create the interface needed to do stuff with the device. */ +static int joy_create_interface(io_object_t hidDevice, joydata_t *joy) { + IOCFPlugInInterface **plugin; + SInt32 score = 0; + + /* Create the plugin that we will use to actually get the device interface + that is needed. */ + if(IOCreatePlugInInterfaceForService(hidDevice, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugin, + &score) != kIOReturnSuccess) { + return 0; + } + + /* Grab the device interface from the plugin. */ + if((*plugin)->QueryInterface(plugin, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), + (LPVOID)&joy->iface) != S_OK) { + return 0; + } + + /* The plugin has done all it needs to, release it. */ + (*plugin)->Release(plugin); + + return 1; +} + +/* Fill in a elemdata_t structure with information about the given physical + element. */ +static void joy_fill_elem(CFTypeRef elem, joy_elemdata_t *ptr) { + CFTypeRef ref; + int num; + + memset(ptr, 0, sizeof(joy_elemdata_t)); + + /* Grab the element cookie. */ + if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementCookieKey)))) { + if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { + ptr->cookie = (IOHIDElementCookie)num; + } + } + + /* Grab the element's minimum value. */ + if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementMinKey)))) { + if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { + ptr->min = num; + } + } + + /* Grab the element's maximum value. */ + if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementMaxKey)))) { + if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { + ptr->max = num; + } + } +} + +/* Callback function to handle each sub-element of the controller class. */ +static void joy_elem_array_hnd(const void *value, void *parameter) { + CFTypeRef elem = (CFTypeRef)value; + CFTypeID t = CFGetTypeID(value); + joydata_t *joy = (joydata_t *)parameter; + long elemType, elemPage, elemUsage; + CFTypeRef type, page, usage; + void *ptr; + int et; + + /* Make sure we're dealing with a dictionary. */ + if(t == CFDictionaryGetTypeID()) { + /* Grab the type of the element. */ + type = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementTypeKey)); + + if(!type) { + return; + } + + /* Grab the HID usage page. */ + page = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementUsagePageKey)); + + if(!page) { + return; + } + + /* Grab the HID usage type. */ + usage = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementUsageKey)); + + if(!usage) { + return; + } + + /* Get the integer values from the data grabbed above. */ + if(!CFNumberGetValue(type, kCFNumberLongType, &elemType)) { + return; + } + + if(!CFNumberGetValue(page, kCFNumberLongType, &elemPage)) { + return; + } + + if(!CFNumberGetValue(usage, kCFNumberLongType, &elemUsage)) { + return; + } + + /* If the element is listed as a button, axis, or misc input, check + what it actually is. */ + if(elemType == kIOHIDElementTypeInput_Button || + elemType == kIOHIDElementTypeInput_Axis || + elemType == kIOHIDElementTypeInput_Misc) { + switch(elemPage) { + case kHIDPage_Button: + ptr = realloc(joy->buttons, (joy->buttons_count + 1) * + sizeof(joy_elemdata_t)); + + if(ptr) { + joy->buttons = (joy_elemdata_t *)ptr; + joy_fill_elem(elem, joy->buttons + joy->buttons_count); + joy->buttons[joy->buttons_count].number = elemUsage; + ++joy->buttons_count; + } + break; + + case kHIDPage_GenericDesktop: + switch(elemUsage) { + case kHIDUsage_GD_X: + et = JOY_TYPE_X_AXIS; + goto axis; + + case kHIDUsage_GD_Y: + et = JOY_TYPE_Y_AXIS; + goto axis; + + case kHIDUsage_GD_Z: + et = JOY_TYPE_Z_AXIS; + goto axis; + + case kHIDUsage_GD_Rx: + et = JOY_TYPE_X2_AXIS; + goto axis; + + case kHIDUsage_GD_Ry: + et = JOY_TYPE_Y2_AXIS; + goto axis; + + case kHIDUsage_GD_Rz: + et = JOY_TYPE_Z2_AXIS; +axis: + ptr = realloc(joy->axes, (joy->axes_count + 1) * + sizeof(joy_elemdata_t)); + + if(ptr) { + joy->axes = (joy_elemdata_t *)ptr; + joy_fill_elem(elem, joy->axes + + joy->axes_count); + joy->axes[joy->axes_count].type = et; + ++joy->axes_count; + } + break; + + case kHIDUsage_GD_Hatswitch: + ptr = realloc(joy->hats, (joy->hats_count + 1) * + sizeof(joy_elemdata_t)); + + if(ptr) { + joy->hats = (joy_elemdata_t *)ptr; + joy_fill_elem(elem, joy->hats + + joy->hats_count); + ++joy->hats_count; + } + break; + } + break; + } + + } + /* If we've found another element array, effectively recurse. */ + else if(elemType == kIOHIDElementTypeCollection) { + joy_find_elements((CFMutableDictionaryRef)elem, joy); + } + } +} + +/* Process all the sub-elements of a given property list. */ +static void joy_find_elements(CFMutableDictionaryRef prop, joydata_t *joy) { + CFTypeRef elem; + CFTypeID type; + + if((elem = CFDictionaryGetValue(prop, CFSTR(kIOHIDElementKey)))) { + type = CFGetTypeID(elem); + + if(type == CFArrayGetTypeID()) { + /* Call our function on each element of the array. */ + CFRange r = { 0, CFArrayGetCount(elem) }; + CFArrayApplyFunction(elem, r, &joy_elem_array_hnd, (void *)joy); + } + } +} + +/* Read the device passed in, and add it to our joystick list if appropriate. */ +static void joy_read_device(io_object_t dev) { + CFMutableDictionaryRef props = 0; + + /* Create a dictionary to read the device's properties. */ + if(IORegistryEntryCreateCFProperties(dev, &props, kCFAllocatorDefault, + kNilOptions) == KERN_SUCCESS) { + CFTypeRef inf; + SInt32 page, usage; + void *ptr; + + /* Grab the primary usage page of the device. */ + inf = CFDictionaryGetValue(props, CFSTR(kIOHIDPrimaryUsagePageKey)); + + if(!inf || !CFNumberGetValue((CFNumberRef)inf, kCFNumberSInt32Type, + &page)) { + goto out; + } + + /* Ignore devices that are not in the Generic Desktop page. */ + if(page != kHIDPage_GenericDesktop) { + goto out; + } + + /* Grab the primary device usage. */ + inf = CFDictionaryGetValue(props, CFSTR(kIOHIDPrimaryUsageKey)); + + if(!inf || !CFNumberGetValue((CFNumberRef)inf, kCFNumberSInt32Type, + &usage)) { + goto out; + } + + /* Ignore devices that are not either a Game Pad or Joystick. */ + if(usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_Joystick) { + goto out; + } + + /* Allocate space for the new joystick structure. */ + ptr = realloc(joys, (joy_count + 1) * sizeof(joydata_t)); + + if(ptr == NULL) { + goto out; + } + + joys = (joydata_t *)ptr; + memset(joys + joy_count, 0, sizeof(joydata_t)); + + /* Grab and store the name of the device. */ + inf = CFDictionaryGetValue(props, CFSTR(kIOHIDProductKey)); + + if(!CFStringGetCString((CFStringRef)inf, joys[joy_count].name, 256, + kCFStringEncodingUTF8)) { + goto out; + } + + /* Create the device interface needed to interact with the device. */ + if(!joy_create_interface(dev, joys + joy_count)) { + goto out; + } + + /* Find all elements of the device. */ + joy_find_elements(props, joys + joy_count); + + qsort(joys[joy_count].buttons, joys[joy_count].buttons_count, + sizeof(joy_elemdata_t), &joy_cmp_buttons); + + ++joy_count; + } + +out: + CFRelease(props); +} + +/* Release the given joystick's interface and clean up any memory used by it. */ +static void joy_release_joystick(joydata_t *joy) { + (*joy->iface)->Release(joy->iface); + + joy->iface = NULL; + + if(joy->buttons) + free(joy->buttons); + + if(joy->axes) + free(joy->axes); + + if(joy->hats) + free(joy->hats); +} + +/* Scan the system for any joysticks connected. */ +int joy_scan_joysticks(void) { + io_iterator_t iter = 0; + io_object_t dev; + + if(joys != NULL) { + return -1; + } + + /* Get the iterator needed for going through the list of devices. */ + joy_get_iterator(kIOMasterPortDefault, &iter); + + if(iter != 0) { + while((dev = IOIteratorNext(iter))) { + joy_read_device(dev); + IOObjectRelease(dev); + } + + /* Release the iterator. */ + IOObjectRelease(iter); + } + + return joy_count; +} + +/* Clean up any data allocated by the program for joysticks. */ +void joy_release_joysticks(void) { + int i; + + for(i = 0; i < joy_count; ++i) { + joy_close_joystick(joys + i); + joy_release_joystick(joys + i); + } + + if(joys != NULL) { + free(joys); + joys = NULL; + } + + joy_count = 0; +} + +/* Get the joystick at a given index in our list of joysticks. */ +joydata_t *joy_get_joystick(int index) { + if(index < 0 || index >= joy_count) { + return NULL; + } + + return &joys[index]; +} + +/* Grab the device for exclusive use by this program. The device must be closed + properly (with the joy_close_joystick function, or you may need to + unplug/replug the joystick to get it to work again). */ +int joy_open_joystick(joydata_t *joy) { + if((*joy->iface)->open(joy->iface, 0)) { + return 0; + } + + joy->open = 1; + + return 1; +} + +/* Close the device and return its resources to the system. */ +int joy_close_joystick(joydata_t *joy) { + IOReturn rv; + + if(!joy->open) { + return 1; + } + + rv = (*joy->iface)->close(joy->iface); + + if(rv == kIOReturnNotOpen) { + /* The device wasn't open so it can't be closed. */ + return 1; + } + else if(rv != kIOReturnSuccess) { + return 0; + } + + joy->open = 0; + + return 1; +} + +/* Read a given element from the joystick. The joystick must be open for this + function to actually do anything useful. */ +int joy_read_element(joydata_t *joy, joy_elemdata_t *elem) { + IOHIDEventStruct ev; + + memset(&ev, 0, sizeof(IOHIDEventStruct)); + + (*joy->iface)->getElementValue(joy->iface, elem->cookie, &ev); + + return ev.value; +} + +/* Read the value of a given button. Returns -1 on failure. */ +int joy_read_button(joydata_t *joy, int num) { + /* Subtract 1 from the number to get the index. */ + --num; + + if(num >= joy->buttons_count) { + return -1; + } + + return joy_read_element(joy, joy->buttons + num); +} + +/* Read the value of a given axis. Returns 0 on failure (or if the axis reports + that its value is 0). */ +int joy_read_axis(joydata_t *joy, int index) { + float value; + + if(index >= joy->axes_count) { + return 0; + } + + value = joy_read_element(joy, joy->axes + index) / + (float)(joy->axes[index].max + 1); + + return (int)(value * 32768); +} + +/* Read the value of a given hat. Returns -1 on failure. */ +int joy_read_hat(joydata_t *joy, int index) { + int value; + + if(index >= joy->hats_count) { + return -1; + } + + value = joy_read_element(joy, joy->hats + index) - joy->hats[index].min; + + /* 4-position hat switch -- Make it look like an 8-position one. */ + if(joy->hats[index].max - joy->hats[index].min + 1 == 4) { + value <<= 1; + } + + switch(value) { + case 0: + return JOY_HAT_UP; + case 1: + return JOY_HAT_UP | JOY_HAT_RIGHT; + case 2: + return JOY_HAT_RIGHT; + case 3: + return JOY_HAT_RIGHT | JOY_HAT_DOWN; + case 4: + return JOY_HAT_DOWN; + case 5: + return JOY_HAT_DOWN | JOY_HAT_LEFT; + case 6: + return JOY_HAT_LEFT; + case 7: + return JOY_HAT_LEFT | JOY_HAT_UP; + default: + return JOY_HAT_CENTER; + } +} diff --git a/yabause/src/macjoy.h b/yabause/src/macjoy.h new file mode 100644 index 0000000000..837296185a --- /dev/null +++ b/yabause/src/macjoy.h @@ -0,0 +1,101 @@ +/* This file was imported form CrabEmu ( http://crabemu.sourceforge.net/ ) -- + A Sega Master System emulator for Mac OS X (among other targets). The rest + of the file is left intact from CrabEmu to make things easier if this were + to be upgraded in the future. */ + +/* + This file is part of CrabEmu. + + Copyright (C) 2008 Lawrence Sebald + + CrabEmu is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + CrabEmu is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CrabEmu; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef JOYSTICK_H +#define JOYSTICK_H + +#include + +/* Values for the joy_elemdata_t type element. */ +#define JOY_TYPE_NOT_APPLICABLE 0 +#define JOY_TYPE_X_AXIS 1 +#define JOY_TYPE_Y_AXIS 2 +#define JOY_TYPE_Z_AXIS 3 +#define JOY_TYPE_X2_AXIS 4 +#define JOY_TYPE_Y2_AXIS 5 +#define JOY_TYPE_Z2_AXIS 6 + +/* Structure for holding basic information about each element. */ +typedef struct joy_elemdata_s { + IOHIDElementCookie cookie; + int type; + int number; + int min; + int max; +} joy_elemdata_t; + +/* Structure for holding information about each joystick connected. */ +typedef struct joydata_s { + IOHIDDeviceInterface **iface; + int open; + int buttons_count; + int axes_count; + int hats_count; + + joy_elemdata_t *buttons; + joy_elemdata_t *axes; + joy_elemdata_t *hats; + + char name[256]; +} joydata_t; + +/* Values for hat switches. ORed together and returned from joy_read_hat. */ +#define JOY_HAT_CENTER 0 +#define JOY_HAT_UP (1 << 0) +#define JOY_HAT_DOWN (1 << 1) +#define JOY_HAT_RIGHT (1 << 2) +#define JOY_HAT_LEFT (1 << 3) + +/* Scan the system for any joysticks connected. */ +int joy_scan_joysticks(void); + +/* Clean up any data allocated by the program for joysticks. */ +void joy_release_joysticks(void); + +/* Get the joystick at a given index in our list of joysticks. */ +joydata_t *joy_get_joystick(int index); + +/* Grab the device for exclusive use by this program. The device must be closed + properly (with the joy_close_joystick function, or you may need to + unplug/replug the joystick to get it to work again). */ +int joy_open_joystick(joydata_t *joy); + +/* Close the device and return its resources to the system. */ +int joy_close_joystick(joydata_t *joy); + +/* Read a given element from the joystick. The joystick must be open for this + function to actually do anything useful. */ +int joy_read_element(joydata_t *joy, joy_elemdata_t *elem); + +/* Read the value of a given button. Returns -1 on failure. */ +int joy_read_button(joydata_t *joy, int num); + +/* Read the value of a given axis. Returns 0 on failure (or if the axis reports + that its value is 0). */ +int joy_read_axis(joydata_t *joy, int index); + +/* Read the value of a given hat. Returns -1 on failure. */ +int joy_read_hat(joydata_t *joy, int index); + +#endif /* !JOYSTICK_H */ diff --git a/yabause/src/memory.c b/yabause/src/memory.c new file mode 100644 index 0000000000..9873117c3e --- /dev/null +++ b/yabause/src/memory.c @@ -0,0 +1,1684 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef PSP // see FIXME in T1MemoryInit() +# include +#endif +#include +#include +#include +#include + +#include "memory.h" +#include "coffelf.h" +#include "cs0.h" +#include "cs1.h" +#include "cs2.h" +#include "debug.h" +#include "error.h" +#include "sh2core.h" +#include "scsp.h" +#include "scu.h" +#include "smpc.h" +#include "vdp1.h" +#include "vdp2.h" +#include "yabause.h" +#include "yui.h" +#include "movie.h" + +#ifdef HAVE_LIBGL +#define USE_OPENGL +#endif + +#ifdef USE_OPENGL +#include "ygl.h" +#endif + +#include "vidsoft.h" +#include "vidogl.h" + +////////////////////////////////////////////////////////////////////////////// + +writebytefunc WriteByteList[0x1000]; +writewordfunc WriteWordList[0x1000]; +writelongfunc WriteLongList[0x1000]; + +readbytefunc ReadByteList[0x1000]; +readwordfunc ReadWordList[0x1000]; +readlongfunc ReadLongList[0x1000]; + +u8 *HighWram; +u8 *LowWram; +u8 *BiosRom; +u8 *BupRam; + +/* This flag is set to 1 on every write to backup RAM. Ports can freely + * check or clear this flag to determine when backup RAM has been written, + * e.g. for implementing autosave of backup RAM. */ +u8 BupRamWritten; + +////////////////////////////////////////////////////////////////////////////// + +u8 * T1MemoryInit(u32 size) +{ +#ifdef PSP // FIXME: could be ported to all arches, but requires stdint.h + // for uintptr_t + u8 * base; + u8 * mem; + + if ((base = calloc((size * sizeof(u8)) + sizeof(u8 *) + 64, 1)) == NULL) + return NULL; + + mem = base + sizeof(u8 *); + mem = mem + (64 - ((uintptr_t) mem & 63)); + *(u8 **)(mem - sizeof(u8 *)) = base; // Save base pointer below memory block + + return mem; +#else + return calloc(size, sizeof(u8)); +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +void T1MemoryDeInit(u8 * mem) +{ +#ifdef PSP + if (mem) + free(*(u8 **)(mem - sizeof(u8 *))); +#else + free(mem); +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +T3Memory * T3MemoryInit(u32 size) +{ + T3Memory * mem; + + if ((mem = (T3Memory *) calloc(1, sizeof(T3Memory))) == NULL) + return NULL; + + if ((mem->base_mem = (u8 *) calloc(size, sizeof(u8))) == NULL) + return NULL; + + mem->mem = mem->base_mem + size; + + return mem; +} + +////////////////////////////////////////////////////////////////////////////// + +void T3MemoryDeInit(T3Memory * mem) +{ + free(mem->base_mem); + free(mem); +} + +////////////////////////////////////////////////////////////////////////////// + +Dummy * DummyInit(UNUSED u32 s) +{ + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void DummyDeInit(UNUSED Dummy * d) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL UnhandledMemoryReadByte(USED_IF_DEBUG u32 addr) +{ + LOG("Unhandled byte read %08X\n", (unsigned int)addr); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL UnhandledMemoryReadWord(USED_IF_DEBUG u32 addr) +{ + LOG("Unhandled word read %08X\n", (unsigned int)addr); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL UnhandledMemoryReadLong(USED_IF_DEBUG u32 addr) +{ + LOG("Unhandled long read %08X\n", (unsigned int)addr); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL UnhandledMemoryWriteByte(USED_IF_DEBUG u32 addr, UNUSED u8 val) +{ + LOG("Unhandled byte write %08X\n", (unsigned int)addr); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL UnhandledMemoryWriteWord(USED_IF_DEBUG u32 addr, UNUSED u16 val) +{ + LOG("Unhandled word write %08X\n", (unsigned int)addr); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL UnhandledMemoryWriteLong(USED_IF_DEBUG u32 addr, UNUSED u32 val) +{ + LOG("Unhandled long write %08X\n", (unsigned int)addr); +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL HighWramMemoryReadByte(u32 addr) +{ + return T2ReadByte(HighWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL HighWramMemoryReadWord(u32 addr) +{ + return T2ReadWord(HighWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL HighWramMemoryReadLong(u32 addr) +{ + return T2ReadLong(HighWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL HighWramMemoryWriteByte(u32 addr, u8 val) +{ + T2WriteByte(HighWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL HighWramMemoryWriteWord(u32 addr, u16 val) +{ + T2WriteWord(HighWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL HighWramMemoryWriteLong(u32 addr, u32 val) +{ + T2WriteLong(HighWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL LowWramMemoryReadByte(u32 addr) +{ + return T2ReadByte(LowWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL LowWramMemoryReadWord(u32 addr) +{ + return T2ReadWord(LowWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL LowWramMemoryReadLong(u32 addr) +{ + return T2ReadLong(LowWram, addr & 0xFFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL LowWramMemoryWriteByte(u32 addr, u8 val) +{ + T2WriteByte(LowWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL LowWramMemoryWriteWord(u32 addr, u16 val) +{ + T2WriteWord(LowWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL LowWramMemoryWriteLong(u32 addr, u32 val) +{ + T2WriteLong(LowWram, addr & 0xFFFFF, val); +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BiosRomMemoryReadByte(u32 addr) +{ + return T2ReadByte(BiosRom, addr & 0x7FFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BiosRomMemoryReadWord(u32 addr) +{ + return T2ReadWord(BiosRom, addr & 0x7FFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BiosRomMemoryReadLong(u32 addr) +{ + return T2ReadLong(BiosRom, addr & 0x7FFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosRomMemoryWriteByte(UNUSED u32 addr, UNUSED u8 val) +{ + // read-only +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosRomMemoryWriteWord(UNUSED u32 addr, UNUSED u16 val) +{ + // read-only +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BiosRomMemoryWriteLong(UNUSED u32 addr, UNUSED u32 val) +{ + // read-only +} + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL BupRamMemoryReadByte(u32 addr) +{ + return T1ReadByte(BupRam, addr & 0xFFFF); +} + +////////////////////////////////////////////////////////////////////////////// + +static u16 FASTCALL BupRamMemoryReadWord(USED_IF_DEBUG u32 addr) +{ + LOG("bup\t: BackupRam read word - %08X\n", addr); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static u32 FASTCALL BupRamMemoryReadLong(USED_IF_DEBUG u32 addr) +{ + LOG("bup\t: BackupRam read long - %08X\n", addr); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BupRamMemoryWriteByte(u32 addr, u8 val) +{ + T1WriteByte(BupRam, (addr & 0xFFFF) | 0x1, val); + BupRamWritten = 1; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BupRamMemoryWriteWord(USED_IF_DEBUG u32 addr, UNUSED u16 val) +{ + LOG("bup\t: BackupRam write word - %08X\n", addr); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL BupRamMemoryWriteLong(USED_IF_DEBUG u32 addr, UNUSED u32 val) +{ + LOG("bup\t: BackupRam write long - %08X\n", addr); +} + +////////////////////////////////////////////////////////////////////////////// + +static void FillMemoryArea(unsigned short start, unsigned short end, + readbytefunc r8func, readwordfunc r16func, + readlongfunc r32func, writebytefunc w8func, + writewordfunc w16func, writelongfunc w32func) +{ + int i; + + for (i=start; i < (end+1); i++) + { + ReadByteList[i] = r8func; + ReadWordList[i] = r16func; + ReadLongList[i] = r32func; + WriteByteList[i] = w8func; + WriteWordList[i] = w16func; + WriteLongList[i] = w32func; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MappedMemoryInit() +{ + // Initialize everyting to unhandled to begin with + FillMemoryArea(0x000, 0xFFF, &UnhandledMemoryReadByte, + &UnhandledMemoryReadWord, + &UnhandledMemoryReadLong, + &UnhandledMemoryWriteByte, + &UnhandledMemoryWriteWord, + &UnhandledMemoryWriteLong); + + // Fill the rest + FillMemoryArea(0x000, 0x00F, &BiosRomMemoryReadByte, + &BiosRomMemoryReadWord, + &BiosRomMemoryReadLong, + &BiosRomMemoryWriteByte, + &BiosRomMemoryWriteWord, + &BiosRomMemoryWriteLong); + FillMemoryArea(0x010, 0x017, &SmpcReadByte, + &SmpcReadWord, + &SmpcReadLong, + &SmpcWriteByte, + &SmpcWriteWord, + &SmpcWriteLong); + FillMemoryArea(0x018, 0x01F, &BupRamMemoryReadByte, + &BupRamMemoryReadWord, + &BupRamMemoryReadLong, + &BupRamMemoryWriteByte, + &BupRamMemoryWriteWord, + &BupRamMemoryWriteLong); + FillMemoryArea(0x020, 0x02F, &LowWramMemoryReadByte, + &LowWramMemoryReadWord, + &LowWramMemoryReadLong, + &LowWramMemoryWriteByte, + &LowWramMemoryWriteWord, + &LowWramMemoryWriteLong); + FillMemoryArea(0x100, 0x17F, &UnhandledMemoryReadByte, + &UnhandledMemoryReadWord, + &UnhandledMemoryReadLong, + &UnhandledMemoryWriteByte, + &SSH2InputCaptureWriteWord, + &UnhandledMemoryWriteLong); + FillMemoryArea(0x180, 0x1FF, &UnhandledMemoryReadByte, + &UnhandledMemoryReadWord, + &UnhandledMemoryReadLong, + &UnhandledMemoryWriteByte, + &MSH2InputCaptureWriteWord, + &UnhandledMemoryWriteLong); + FillMemoryArea(0x200, 0x3FF, CartridgeArea->Cs0ReadByte, + CartridgeArea->Cs0ReadWord, + CartridgeArea->Cs0ReadLong, + CartridgeArea->Cs0WriteByte, + CartridgeArea->Cs0WriteWord, + CartridgeArea->Cs0WriteLong); + FillMemoryArea(0x400, 0x4FF, &Cs1ReadByte, + &Cs1ReadWord, + &Cs1ReadLong, + &Cs1WriteByte, + &Cs1WriteWord, + &Cs1WriteLong); + FillMemoryArea(0x580, 0x58F, &Cs2ReadByte, + &Cs2ReadWord, + &Cs2ReadLong, + &Cs2WriteByte, + &Cs2WriteWord, + &Cs2WriteLong); + FillMemoryArea(0x5A0, 0x5AF, &SoundRamReadByte, + &SoundRamReadWord, + &SoundRamReadLong, + &SoundRamWriteByte, + &SoundRamWriteWord, + &SoundRamWriteLong); + FillMemoryArea(0x5B0, 0x5BF, &scsp_r_b, + &scsp_r_w, + &scsp_r_d, + &scsp_w_b, + &scsp_w_w, + &scsp_w_d); + FillMemoryArea(0x5C0, 0x5C7, &Vdp1RamReadByte, + &Vdp1RamReadWord, + &Vdp1RamReadLong, + &Vdp1RamWriteByte, + &Vdp1RamWriteWord, + &Vdp1RamWriteLong); + FillMemoryArea(0x5C8, 0x5CF, &Vdp1FrameBufferReadByte, + &Vdp1FrameBufferReadWord, + &Vdp1FrameBufferReadLong, + &Vdp1FrameBufferWriteByte, + &Vdp1FrameBufferWriteWord, + &Vdp1FrameBufferWriteLong); + FillMemoryArea(0x5D0, 0x5D7, &Vdp1ReadByte, + &Vdp1ReadWord, + &Vdp1ReadLong, + &Vdp1WriteByte, + &Vdp1WriteWord, + &Vdp1WriteLong); + FillMemoryArea(0x5E0, 0x5EF, &Vdp2RamReadByte, + &Vdp2RamReadWord, + &Vdp2RamReadLong, + &Vdp2RamWriteByte, + &Vdp2RamWriteWord, + &Vdp2RamWriteLong); + FillMemoryArea(0x5F0, 0x5F7, &Vdp2ColorRamReadByte, + &Vdp2ColorRamReadWord, + &Vdp2ColorRamReadLong, + &Vdp2ColorRamWriteByte, + &Vdp2ColorRamWriteWord, + &Vdp2ColorRamWriteLong); + FillMemoryArea(0x5F8, 0x5FB, &Vdp2ReadByte, + &Vdp2ReadWord, + &Vdp2ReadLong, + &Vdp2WriteByte, + &Vdp2WriteWord, + &Vdp2WriteLong); + FillMemoryArea(0x5FE, 0x5FE, &ScuReadByte, + &ScuReadWord, + &ScuReadLong, + &ScuWriteByte, + &ScuWriteWord, + &ScuWriteLong); + FillMemoryArea(0x600, 0x7FF, &HighWramMemoryReadByte, + &HighWramMemoryReadWord, + &HighWramMemoryReadLong, + &HighWramMemoryWriteByte, + &HighWramMemoryWriteWord, + &HighWramMemoryWriteLong); +} + +////////////////////////////////////////////////////////////////////////////// + +u8 FASTCALL MappedMemoryReadByte(u32 addr) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + return ReadByteList[(addr >> 16) & 0xFFF](addr); + } +/* + case 0x2: + { + // Purge Area + break; + } +*/ + case 0x4: + case 0x6: + // Data Array + return DataArrayReadByte(addr); + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + return OnchipReadByte(addr); + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + break; + } + default: + { + return UnhandledMemoryReadByte(addr); + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u16 FASTCALL MappedMemoryReadWord(u32 addr) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + return ReadWordList[(addr >> 16) & 0xFFF](addr); + } +/* + case 0x2: + { + // Purge Area + break; + } +*/ + case 0x4: + case 0x6: + // Data Array + return DataArrayReadWord(addr); + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + return OnchipReadWord(addr); + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + break; + } + default: + { + return UnhandledMemoryReadWord(addr); + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 FASTCALL MappedMemoryReadLong(u32 addr) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + return ReadLongList[(addr >> 16) & 0xFFF](addr); + } +/* + case 0x2: + { + // Purge Area + break; + } +*/ + case 0x3: + { + // Address Array + return AddressArrayReadLong(addr); + } + case 0x4: + case 0x6: + // Data Array + return DataArrayReadLong(addr); + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + return OnchipReadLong(addr); + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + break; + } + default: + { + return UnhandledMemoryReadLong(addr); + } + } + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL MappedMemoryWriteByte(u32 addr, u8 val) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + WriteByteList[(addr >> 16) & 0xFFF](addr, val); + return; + } +/* + case 0x2: + { + // Purge Area + return; + } +*/ + case 0x4: + case 0x6: + // Data Array + DataArrayWriteByte(addr, val); + return; + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + OnchipWriteByte(addr, val); + return; + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + return; + } + default: + { + UnhandledMemoryWriteByte(addr, val); + return; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL MappedMemoryWriteWord(u32 addr, u16 val) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + WriteWordList[(addr >> 16) & 0xFFF](addr, val); + return; + } +/* + case 0x2: + { + // Purge Area + return; + } +*/ + case 0x4: + case 0x6: + // Data Array + DataArrayWriteWord(addr, val); + return; + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + OnchipWriteWord(addr, val); + return; + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + return; + } + default: + { + UnhandledMemoryWriteWord(addr, val); + return; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL MappedMemoryWriteLong(u32 addr, u32 val) +{ + switch (addr >> 29) + { + case 0x0: + case 0x1: + case 0x5: + { + // Cache/Non-Cached + WriteLongList[(addr >> 16) & 0xFFF](addr, val); + return; + } + case 0x2: + { + // Purge Area + return; + } + case 0x3: + { + // Address Array + AddressArrayWriteLong(addr, val); + return; + } + case 0x4: + case 0x6: + // Data Array + DataArrayWriteLong(addr, val); + return; + case 0x7: + { + if (addr >= 0xFFFFFE00) + { + // Onchip modules + addr &= 0x1FF; + OnchipWriteLong(addr, val); + return; + } + else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) + { + // ??? + } + else + { + // Garbage data + } + return; + } + default: + { + UnhandledMemoryWriteLong(addr, val); + return; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +int MappedMemoryLoad(const char *filename, u32 addr) +{ + FILE *fp; + u32 filesize; + u8 *buffer; + u32 i; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + // Calculate file size + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if ((buffer = (u8 *)malloc(filesize)) == NULL) + { + fclose(fp); + return -2; + } + + fread((void *)buffer, 1, filesize, fp); + fclose(fp); + + for (i = 0; i < filesize; i++) + MappedMemoryWriteByte(addr+i, buffer[i]); + + free(buffer); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int MappedMemorySave(const char *filename, u32 addr, u32 size) +{ + FILE *fp; + u8 *buffer; + u32 i; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "wb")) == NULL) + return -1; + + if ((buffer = (u8 *)malloc(size)) == NULL) + { + fclose(fp); + return -2; + } + + for (i = 0; i < size; i++) + buffer[i] = MappedMemoryReadByte(addr+i); + + fwrite((void *)buffer, 1, size, fp); + fclose(fp); + free(buffer); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void MappedMemoryLoadExec(const char *filename, u32 pc) +{ + char *p; + size_t i; + + if ((p = strrchr(filename, '.'))) + { + p = strdup(p); + for (i = 0; i < strlen(p); i++) + p[i] = toupper(p[i]); + if (strcmp(p, ".COF") == 0 || strcmp(p, ".COFF") == 0) + { + MappedMemoryLoadCoff(filename); + free(p); + return; + } + else if(strcmp(p, ".ELF") == 0) + { + MappedMemoryLoadElf(filename); + free(p); + return; + } + + free(p); + } + + YabauseResetNoLoad(); + + // Setup the vector table area, etc. + YabauseSpeedySetup(); + + MappedMemoryLoad(filename, pc); + SH2GetRegisters(MSH2, &MSH2->regs); + MSH2->regs.PC = pc; + SH2SetRegisters(MSH2, &MSH2->regs); +} + +////////////////////////////////////////////////////////////////////////////// + +int LoadBios(const char *filename) +{ + return T123Load(BiosRom, 0x80000, 2, filename); +} + +////////////////////////////////////////////////////////////////////////////// + +int LoadBackupRam(const char *filename) +{ + return T123Load(BupRam, 0x10000, 1, filename); +} + +////////////////////////////////////////////////////////////////////////////// + +void FormatBackupRam(void *mem, u32 size) +{ + int i, i2; + u32 i3; + u8 header[32] = { + 0xFF, 'B', 0xFF, 'a', 0xFF, 'c', 0xFF, 'k', + 0xFF, 'U', 0xFF, 'p', 0xFF, 'R', 0xFF, 'a', + 0xFF, 'm', 0xFF, ' ', 0xFF, 'F', 0xFF, 'o', + 0xFF, 'r', 0xFF, 'm', 0xFF, 'a', 0xFF, 't' + }; + + // Fill in header + for(i2 = 0; i2 < 4; i2++) + for(i = 0; i < 32; i++) + T1WriteByte(mem, (i2 * 32) + i, header[i]); + + // Clear the rest + for(i3 = 0x80; i3 < size; i3+=2) + { + T1WriteByte(mem, i3, 0xFF); + T1WriteByte(mem, i3+1, 0x00); + } +} + +////////////////////////////////////////////////////////////////////////////// + +// FIXME: Here's a (possibly incomplete) list of data that should be added +// to the next version of the save state file: +// yabsys.DecilineStop (new format) +// yabsys.SH2CycleFrac (new field) +// yabsys.DecilineUSed (new field) +// yabsys.UsecFrac (new field) +// [scsp2.c] It would be nice to redo the format entirely because so +// many fields have changed format/size from the old scsp.c +// [scsp2.c] scsp_clock, scsp_clock_frac, ScspState.sample_timer (timing) +// [scsp2.c] cdda_buf, cdda_next_in, cdda_next_out (CDDA buffer) +// [sh2core.c] frc.div changed to frc.shift +// [sh2core.c] wdt probably needs to be written as well + +int YabSaveState(const char *filename) +{ + u32 i; + FILE *fp; + int offset; + IOCheck_struct check; + u8 *buf; + int totalsize; + int outputwidth; + int outputheight; + int movieposition; + int temp; + u32 temp32; + + check.done = 0; + check.size = 0; + + //use a second set of savestates for movies + filename = MakeMovieStateName(filename); + if (!filename) + return -1; + + if ((fp = fopen(filename, "wb")) == NULL) + return -1; + + // Write signature + fprintf(fp, "YSS"); + + // Write endianness byte +#ifdef WORDS_BIGENDIAN + fputc(0x00, fp); +#else + fputc(0x01, fp); +#endif + + // Write version(fix me) + i = 2; + ywrite(&check, (void *)&i, sizeof(i), 1, fp); + + // Skip the next 4 bytes for now + i = 0; + ywrite(&check, (void *)&i, sizeof(i), 1, fp); + + //write frame number + ywrite(&check, (void *)&framecounter, 4, 1, fp); + + //this will be updated with the movie position later + ywrite(&check, (void *)&framecounter, 4, 1, fp); + + // Go through each area and write each state + i += CartSaveState(fp); + i += Cs2SaveState(fp); + i += SH2SaveState(MSH2, fp); + i += SH2SaveState(SSH2, fp); + i += SoundSaveState(fp); + i += ScuSaveState(fp); + i += SmpcSaveState(fp); + i += Vdp1SaveState(fp); + i += Vdp2SaveState(fp); + + offset = StateWriteHeader(fp, "OTHR", 1); + + // Other data + ywrite(&check, (void *)BupRam, 0x10000, 1, fp); // do we really want to save this? + ywrite(&check, (void *)HighWram, 0x100000, 1, fp); + ywrite(&check, (void *)LowWram, 0x100000, 1, fp); + + ywrite(&check, (void *)&yabsys.DecilineCount, sizeof(int), 1, fp); + ywrite(&check, (void *)&yabsys.LineCount, sizeof(int), 1, fp); + ywrite(&check, (void *)&yabsys.VBlankLineCount, sizeof(int), 1, fp); + ywrite(&check, (void *)&yabsys.MaxLineCount, sizeof(int), 1, fp); + temp = yabsys.DecilineStop >> YABSYS_TIMING_BITS; + ywrite(&check, (void *)&temp, sizeof(int), 1, fp); + temp = (yabsys.CurSH2FreqType == CLKTYPE_26MHZ) ? 268 : 286; + ywrite(&check, (void *)&temp, sizeof(int), 1, fp); + temp32 = (yabsys.UsecFrac * temp / 10) >> YABSYS_TIMING_BITS; + ywrite(&check, (void *)&temp32, sizeof(u32), 1, fp); + ywrite(&check, (void *)&yabsys.CurSH2FreqType, sizeof(int), 1, fp); + ywrite(&check, (void *)&yabsys.IsPal, sizeof(int), 1, fp); + + VIDCore->GetGlSize(&outputwidth, &outputheight); + + totalsize=outputwidth * outputheight * sizeof(u32); + + if ((buf = (u8 *)malloc(totalsize)) == NULL) + { + return -2; + } + + YuiSwapBuffers(); + #ifdef USE_OPENGL + glPixelZoom(1,1); + glReadBuffer(GL_BACK); + glReadPixels(0, 0, outputwidth, outputheight, GL_RGBA, GL_UNSIGNED_BYTE, buf); + #endif + YuiSwapBuffers(); + + ywrite(&check, (void *)&outputwidth, sizeof(outputwidth), 1, fp); + ywrite(&check, (void *)&outputheight, sizeof(outputheight), 1, fp); + + ywrite(&check, (void *)buf, totalsize, 1, fp); + + movieposition=ftell(fp); + //write the movie to the end of the savestate + SaveMovieInState(fp, check); + + i += StateFinishHeader(fp, offset); + + // Go back and update size + fseek(fp, 8, SEEK_SET); + ywrite(&check, (void *)&i, sizeof(i), 1, fp); + fseek(fp, 16, SEEK_SET); + ywrite(&check, (void *)&movieposition, sizeof(movieposition), 1, fp); + + fclose(fp); + + OSDPushMessage(OSDMSG_STATUS, 150, "STATE SAVED"); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int YabLoadState(const char *filename) +{ + FILE *fp; + char id[3]; + u8 endian; + int headerversion, version, size, chunksize, headersize; + IOCheck_struct check; + u8* buf; + int totalsize; + int outputwidth; + int outputheight; + int curroutputwidth; + int curroutputheight; + int movieposition; + int temp; + u32 temp32; + + filename = MakeMovieStateName(filename); + if (!filename) + return -1; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + headersize = 0xC; + + // Read signature + yread(&check, (void *)id, 1, 3, fp); + + if (strncmp(id, "YSS", 3) != 0) + { + fclose(fp); + return -2; + } + + // Read header + yread(&check, (void *)&endian, 1, 1, fp); + yread(&check, (void *)&headerversion, 4, 1, fp); + yread(&check, (void *)&size, 4, 1, fp); + switch(headerversion) + { + case 1: + /* This is the "original" version of the format */ + break; + case 2: + /* version 2 adds video recording */ + yread(&check, (void *)&framecounter, 4, 1, fp); + movieposition=ftell(fp); + yread(&check, (void *)&movieposition, 4, 1, fp); + headersize = 0x14; + break; + default: + /* we're trying to open a save state using a future version + * of the YSS format, that won't work, sorry :) */ + fclose(fp); + return -3; + break; + } + +#ifdef WORDS_BIGENDIAN + if (endian == 1) +#else + if (endian == 0) +#endif + { + // should setup reading so it's byte-swapped + YabSetError(YAB_ERR_OTHER, (void *)"Load State byteswapping not supported"); + fclose(fp); + return -3; + } + + // Make sure size variable matches actual size minus header + fseek(fp, 0, SEEK_END); + + if (size != (ftell(fp) - headersize)) + { + fclose(fp); + return -2; + } + fseek(fp, headersize, SEEK_SET); + + // Verify version here + + ScspMuteAudio(SCSP_MUTE_SYSTEM); + + if (StateCheckRetrieveHeader(fp, "CART", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + CartLoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "CS2 ", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + Cs2LoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "MSH2", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + SH2LoadState(MSH2, fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "SSH2", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + SH2LoadState(SSH2, fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "SCSP", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + SoundLoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "SCU ", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + ScuLoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "SMPC", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + SmpcLoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "VDP1", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + Vdp1LoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "VDP2", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + Vdp2LoadState(fp, version, chunksize); + + if (StateCheckRetrieveHeader(fp, "OTHR", &version, &chunksize) != 0) + { + fclose(fp); + // Revert back to old state here + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + return -3; + } + // Other data + yread(&check, (void *)BupRam, 0x10000, 1, fp); + yread(&check, (void *)HighWram, 0x100000, 1, fp); + yread(&check, (void *)LowWram, 0x100000, 1, fp); + + yread(&check, (void *)&yabsys.DecilineCount, sizeof(int), 1, fp); + yread(&check, (void *)&yabsys.LineCount, sizeof(int), 1, fp); + yread(&check, (void *)&yabsys.VBlankLineCount, sizeof(int), 1, fp); + yread(&check, (void *)&yabsys.MaxLineCount, sizeof(int), 1, fp); + yread(&check, (void *)&temp, sizeof(int), 1, fp); + yread(&check, (void *)&temp, sizeof(int), 1, fp); + yread(&check, (void *)&temp32, sizeof(u32), 1, fp); + yread(&check, (void *)&yabsys.CurSH2FreqType, sizeof(int), 1, fp); + yread(&check, (void *)&yabsys.IsPal, sizeof(int), 1, fp); + YabauseChangeTiming(yabsys.CurSH2FreqType); + yabsys.UsecFrac = (temp32 << YABSYS_TIMING_BITS) * temp / 10; + + if (headerversion > 1) { + + yread(&check, (void *)&outputwidth, sizeof(outputwidth), 1, fp); + yread(&check, (void *)&outputheight, sizeof(outputheight), 1, fp); + + totalsize=outputwidth * outputheight * sizeof(u32); + + if ((buf = (u8 *)malloc(totalsize)) == NULL) + { + return -2; + } + + yread(&check, (void *)buf, totalsize, 1, fp); + + YuiSwapBuffers(); + + #ifdef USE_OPENGL + if(VIDCore->id == VIDCORE_SOFT) + glRasterPos2i(0, outputheight); + if(VIDCore->id == VIDCORE_OGL) + glRasterPos2i(0, outputheight/2); + #endif + + VIDCore->GetGlSize(&curroutputwidth, &curroutputheight); + #ifdef USE_OPENGL + glPixelZoom((float)curroutputwidth / (float)outputwidth, ((float)curroutputheight / (float)outputheight)); + glDrawPixels(outputwidth, outputheight, GL_RGBA, GL_UNSIGNED_BYTE, buf); + #endif + YuiSwapBuffers(); + + fseek(fp, movieposition, SEEK_SET); + MovieReadState(fp, filename); + } + + fclose(fp); + + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + + OSDPushMessage(OSDMSG_STATUS, 150, "STATE LOADED"); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int YabSaveStateSlot(const char *dirpath, u8 slot) +{ + char filename[512]; + + if (cdip == NULL) + return -1; + +#ifdef WIN32 + sprintf(filename, "%s\\%s_%03d.yss", dirpath, cdip->itemnum, slot); +#else + sprintf(filename, "%s/%s_%03d.yss", dirpath, cdip->itemnum, slot); +#endif + return YabSaveState(filename); +} + +////////////////////////////////////////////////////////////////////////////// + +int YabLoadStateSlot(const char *dirpath, u8 slot) +{ + char filename[512]; + + if (cdip == NULL) + return -1; + +#ifdef WIN32 + sprintf(filename, "%s\\%s_%03d.yss", dirpath, cdip->itemnum, slot); +#else + sprintf(filename, "%s/%s_%03d.yss", dirpath, cdip->itemnum, slot); +#endif + return YabLoadState(filename); +} + +////////////////////////////////////////////////////////////////////////////// + +static int MappedMemoryAddMatch(u32 addr, u32 val, int searchtype, result_struct *result, u32 *numresults) +{ + result[numresults[0]].addr = addr; + result[numresults[0]].val = val; + numresults[0]++; + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static INLINE int SearchIncrementAndCheckBounds(result_struct *prevresults, + u32 *maxresults, + u32 numresults, u32 *i, + u32 inc, u32 *newaddr, + u32 endaddr) +{ + if (prevresults) + { + if (i[0] >= maxresults[0]) + { + maxresults[0] = numresults; + return 1; + } + newaddr[0] = prevresults[i[0]].addr; + i[0]++; + } + else + { + newaddr[0] = inc; + + if (newaddr[0] > endaddr || numresults >= maxresults[0]) + { + maxresults[0] = numresults; + return 1; + } + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int SearchString(u32 startaddr, u32 endaddr, int searchtype, + const char *searchstr, result_struct *results, + u32 *maxresults) +{ + u8 *buf=NULL; + u32 *buf32=NULL; + u32 buflen=0; + u32 counter; + u32 addr; + u32 numresults=0; + + buflen=(u32)strlen(searchstr); + + if ((buf32=(u32 *)malloc(buflen*sizeof(u32))) == NULL) + return 0; + + buf = (u8 *)buf32; + + // Copy string to buffer + switch (searchtype & 0x70) + { + case SEARCHSTRING: + strcpy((char *)buf, searchstr); + break; + case SEARCHREL8BIT: + case SEARCHREL16BIT: + { + char *text; + char *searchtext=strdup(searchstr); + + // Calculate buffer length and read values into table + buflen = 0; + for (text=strtok((char *)searchtext, " ,"); text != NULL; text=strtok(NULL, " ,")) + { + buf32[buflen] = strtoul(text, NULL, 0); + buflen++; + } + free(searchtext); + + break; + } + } + + addr = startaddr; + counter = 0; + + for (;;) + { + // Fetch byte/word/etc. + switch (searchtype & 0x70) + { + case SEARCHSTRING: + { + u8 val = MappedMemoryReadByte(addr); + addr++; + + if (val == buf[counter]) + { + counter++; + if (counter == buflen) + MappedMemoryAddMatch(addr-buflen, val, searchtype, results, &numresults); + } + else + counter = 0; + break; + } + case SEARCHREL8BIT: + { + int diff; + u32 j; + u8 val2; + u8 val = MappedMemoryReadByte(addr); + + for (j = 1; j < buflen; j++) + { + // grab the next value + val2 = MappedMemoryReadByte(addr+j); + + // figure out the diff + diff = (int)val2 - (int)val; + + // see if there's a match + if (((int)buf32[j] - (int)buf32[j-1]) != diff) + break; + + if (j == (buflen - 1)) + MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); + + val = val2; + } + + addr++; + + break; + } + case SEARCHREL16BIT: + { + int diff; + u32 j; + u16 val2; + u16 val = MappedMemoryReadWord(addr); + + for (j = 1; j < buflen; j++) + { + // grab the next value + val2 = MappedMemoryReadWord(addr+(j*2)); + + // figure out the diff + diff = (int)val2 - (int)val; + + // see if there's a match + if (((int)buf32[j] - (int)buf32[j-1]) != diff) + break; + + if (j == (buflen - 1)) + MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); + + val = val2; + } + + addr+=2; + break; + } + } + + if (addr > endaddr || numresults >= maxresults[0]) + break; + } + + free(buf); + maxresults[0] = numresults; + return 1; +} + +////////////////////////////////////////////////////////////////////////////// + +result_struct *MappedMemorySearch(u32 startaddr, u32 endaddr, int searchtype, + const char *searchstr, + result_struct *prevresults, u32 *maxresults) +{ + u32 i=0; + result_struct *results; + u32 numresults=0; + unsigned long searchval; + int issigned=0; + u32 addr; + + if ((results = (result_struct *)malloc(sizeof(result_struct) * maxresults[0])) == NULL) + return NULL; + + switch (searchtype & 0x70) + { + case SEARCHSTRING: + case SEARCHREL8BIT: + case SEARCHREL16BIT: + { + // String/8-bit relative/16-bit relative search(not supported, yet) + if (SearchString(startaddr, endaddr, searchtype, searchstr, + results, maxresults) == 0) + { + maxresults[0] = 0; + free(results); + return NULL; + } + + return results; + } + case SEARCHHEX: + sscanf(searchstr, "%08lx", &searchval); + break; + case SEARCHUNSIGNED: + searchval = (unsigned long)atoi(searchstr); + issigned = 0; + break; + case SEARCHSIGNED: + searchval = (unsigned long)atoi(searchstr); + issigned = 1; + break; + } + + if (prevresults) + { + addr = prevresults[i].addr; + i++; + } + else + addr = startaddr; + + // Regular value search + for (;;) + { + u32 val=0; + u32 newaddr; + + // Fetch byte/word/etc. + switch (searchtype & 0x3) + { + case SEARCHBYTE: + val = MappedMemoryReadByte(addr); + // sign extend if neccessary + if (issigned) + val = (s8)val; + + if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+1, &newaddr, endaddr)) + return results; + break; + case SEARCHWORD: + val = MappedMemoryReadWord(addr); + // sign extend if neccessary + if (issigned) + val = (s16)val; + + if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+2, &newaddr, endaddr)) + return results; + break; + case SEARCHLONG: + val = MappedMemoryReadLong(addr); + + if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+4, &newaddr, endaddr)) + return results; + break; + default: + maxresults[0] = 0; + if (results) + free(results); + return NULL; + } + + // Do a comparison + switch (searchtype & 0xC) + { + case SEARCHEXACT: + if (val == searchval) + MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); + break; + case SEARCHLESSTHAN: + if ((!issigned && val < searchval) || (issigned && (signed)val < (signed)searchval)) + MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); + break; + case SEARCHGREATERTHAN: + if ((!issigned && val > searchval) || (issigned && (signed)val > (signed)searchval)) + MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); + break; + default: + maxresults[0] = 0; + if (results) + free(results); + return NULL; + } + + addr = newaddr; + } + + maxresults[0] = numresults; + return results; +} diff --git a/yabause/src/memory.h b/yabause/src/memory.h new file mode 100644 index 0000000000..2de4cbdc47 --- /dev/null +++ b/yabause/src/memory.h @@ -0,0 +1,403 @@ +/* Copyright 2005-2006 Guillaume Duhamel + Copyright 2005 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include "core.h" + +/* Type 1 Memory, faster for byte (8 bits) accesses */ + +u8 * T1MemoryInit(u32); +void T1MemoryDeInit(u8 *); + +static INLINE u8 T1ReadByte(u8 * mem, u32 addr) +{ + return mem[addr]; +} + +static INLINE u16 T1ReadWord(u8 * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return *((u16 *) (mem + addr)); +#else + return BSWAP16(*((u16 *) (mem + addr))); +#endif +} + +static INLINE u32 T1ReadLong(u8 * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return *((u32 *) (mem + addr)); +#else + return BSWAP32(*((u32 *) (mem + addr))); +#endif +} + +static INLINE void T1WriteByte(u8 * mem, u32 addr, u8 val) +{ + mem[addr] = val; +} + +static INLINE void T1WriteWord(u8 * mem, u32 addr, u16 val) +{ +#ifdef WORDS_BIGENDIAN + *((u16 *) (mem + addr)) = val; +#else + *((u16 *) (mem + addr)) = BSWAP16(val); +#endif +} + +static INLINE void T1WriteLong(u8 * mem, u32 addr, u32 val) +{ +#ifdef WORDS_BIGENDIAN + *((u32 *) (mem + addr)) = val; +#else + *((u32 *) (mem + addr)) = BSWAP32(val); +#endif +} + +/* Type 2 Memory, faster for word (16 bits) accesses */ + +#define T2MemoryInit(x) (T1MemoryInit(x)) +#define T2MemoryDeInit(x) (T1MemoryDeInit(x)) + +static INLINE u8 T2ReadByte(u8 * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return mem[addr]; +#else + return mem[addr ^ 1]; +#endif +} + +static INLINE u16 T2ReadWord(u8 * mem, u32 addr) +{ + return *((u16 *) (mem + addr)); +} + +static INLINE u32 T2ReadLong(u8 * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return *((u32 *) (mem + addr)); +#else + return WSWAP32(*((u32 *) (mem + addr))); +#endif +} + +static INLINE void T2WriteByte(u8 * mem, u32 addr, u8 val) +{ +#ifdef WORDS_BIGENDIAN + mem[addr] = val; +#else + mem[addr ^ 1] = val; +#endif +} + +static INLINE void T2WriteWord(u8 * mem, u32 addr, u16 val) +{ + *((u16 *) (mem + addr)) = val; +} + +static INLINE void T2WriteLong(u8 * mem, u32 addr, u32 val) +{ +#ifdef WORDS_BIGENDIAN + *((u32 *) (mem + addr)) = val; +#else + *((u32 *) (mem + addr)) = WSWAP32(val); +#endif +} + +/* Type 3 Memory, faster for long (32 bits) accesses */ + +typedef struct +{ + u8 * base_mem; + u8 * mem; +} T3Memory; + +T3Memory * T3MemoryInit(u32); +void T3MemoryDeInit(T3Memory *); + +static INLINE u8 T3ReadByte(T3Memory * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return mem->mem[addr]; +#else + return (mem->mem - addr - 1)[0]; +#endif +} + +static INLINE u16 T3ReadWord(T3Memory * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return *((u16 *) (mem->mem + addr)); +#else + return ((u16 *) (mem->mem - addr - 2))[0]; +#endif +} + +static INLINE u32 T3ReadLong(T3Memory * mem, u32 addr) +{ +#ifdef WORDS_BIGENDIAN + return *((u32 *) (mem->mem + addr)); +#else + return ((u32 *) (mem->mem - addr - 4))[0]; +#endif +} + +static INLINE void T3WriteByte(T3Memory * mem, u32 addr, u8 val) +{ +#ifdef WORDS_BIGENDIAN + mem->mem[addr] = val; +#else + (mem->mem - addr - 1)[0] = val; +#endif +} + +static INLINE void T3WriteWord(T3Memory * mem, u32 addr, u16 val) +{ +#ifdef WORDS_BIGENDIAN + *((u16 *) (mem->mem + addr)) = val; +#else + ((u16 *) (mem->mem - addr - 2))[0] = val; +#endif +} + +static INLINE void T3WriteLong(T3Memory * mem, u32 addr, u32 val) +{ +#ifdef WORDS_BIGENDIAN + *((u32 *) (mem->mem + addr)) = val; +#else + ((u32 *) (mem->mem - addr - 4))[0] = val; +#endif +} + +static INLINE int T123Load(void * mem, u32 size, int type, const char *filename) +{ + FILE *fp; + u32 filesize, filesizecheck; + u8 *buffer; + u32 i; + + if (!filename) + return -1; + + if ((fp = fopen(filename, "rb")) == NULL) + return -1; + + // Calculate file size + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (filesize > size) + return -1; + + if ((buffer = (u8 *)malloc(filesize)) == NULL) + { + fclose(fp); + return -1; + } + + filesizecheck = (u32)fread((void *)buffer, 1, filesize, fp); + fclose(fp); + + if (filesizecheck != filesize) return -1; + + switch (type) + { + case 1: + { + for (i = 0; i < filesize; i++) + T1WriteByte((u8 *) mem, i, buffer[i]); + break; + } + case 2: + { + for (i = 0; i < filesize; i++) + T2WriteByte((u8 *) mem, i, buffer[i]); + break; + } + case 3: + { + for (i = 0; i < filesize; i++) + T3WriteByte((T3Memory *) mem, i, buffer[i]); + break; + } + default: + { + free(buffer); + return -1; + } + } + + free(buffer); + + return 0; +} + +static INLINE int T123Save(void * mem, u32 size, int type, const char *filename) +{ + FILE *fp; + u8 *buffer; + u32 i; + u32 sizecheck; + + if (filename == NULL) + return 0; + + if (filename[0] == 0x00) + return 0; + + if ((buffer = (u8 *)malloc(size)) == NULL) + return -1; + + switch (type) + { + case 1: + { + for (i = 0; i < size; i++) + buffer[i] = T1ReadByte((u8 *) mem, i); + break; + } + case 2: + { + for (i = 0; i < size; i++) + buffer[i] = T2ReadByte((u8 *) mem, i); + break; + } + case 3: + { + for (i = 0; i < size; i++) + buffer[i] = T3ReadByte((T3Memory *) mem, i); + break; + } + default: + { + free(buffer); + return -1; + } + } + + if ((fp = fopen(filename, "wb")) == NULL) + { + free(buffer); + return -1; + } + + sizecheck = (u32)fwrite((void *)buffer, 1, size, fp); + fclose(fp); + free(buffer); + + if (sizecheck != size) return -1; + + return 0; +} + +/* Dummy memory, always returns 0 */ + +typedef void Dummy; + +Dummy * DummyInit(u32); +void DummyDeInit(Dummy *); + +static INLINE u8 DummyReadByte(Dummy UNUSED * d, u32 UNUSED a) { return 0; } +static INLINE u16 DummyReadWord(Dummy UNUSED * d, u32 UNUSED a) { return 0; } +static INLINE u32 DummyReadLong(Dummy UNUSED * d, u32 UNUSED a) { return 0; } + +static INLINE void DummyWriteByte(Dummy UNUSED * d, u32 UNUSED a, u8 UNUSED v) {} +static INLINE void DummyWriteWord(Dummy UNUSED * d, u32 UNUSED a, u16 UNUSED v) {} +static INLINE void DummyWriteLong(Dummy UNUSED * d, u32 UNUSED a, u32 UNUSED v) {} + +void MappedMemoryInit(void); +u8 FASTCALL MappedMemoryReadByte(u32 addr); +u16 FASTCALL MappedMemoryReadWord(u32 addr); +u32 FASTCALL MappedMemoryReadLong(u32 addr); +void FASTCALL MappedMemoryWriteByte(u32 addr, u8 val); +void FASTCALL MappedMemoryWriteWord(u32 addr, u16 val); +void FASTCALL MappedMemoryWriteLong(u32 addr, u32 val); +#ifdef __cplusplus +extern "C" { +#endif +extern u8 *HighWram; +#ifdef __cplusplus +} +#endif +extern u8 *LowWram; +extern u8 *BiosRom; +extern u8 *BupRam; +extern u8 BupRamWritten; + +typedef void (FASTCALL *writebytefunc)(u32, u8); +typedef void (FASTCALL *writewordfunc)(u32, u16); +typedef void (FASTCALL *writelongfunc)(u32, u32); + +typedef u8 (FASTCALL *readbytefunc)(u32); +typedef u16 (FASTCALL *readwordfunc)(u32); +typedef u32 (FASTCALL *readlongfunc)(u32); + +extern writebytefunc WriteByteList[0x1000]; +extern writewordfunc WriteWordList[0x1000]; +extern writelongfunc WriteLongList[0x1000]; + +extern readbytefunc ReadByteList[0x1000]; +extern readwordfunc ReadWordList[0x1000]; +extern readlongfunc ReadLongList[0x1000]; + +typedef struct { +u32 addr; +u32 val; +} result_struct; + +#define SEARCHBYTE 0 +#define SEARCHWORD 1 +#define SEARCHLONG 2 + +#define SEARCHEXACT (0 << 2) +#define SEARCHLESSTHAN (1 << 2) +#define SEARCHGREATERTHAN (2 << 2) + +#define SEARCHUNSIGNED (0 << 4) +#define SEARCHSIGNED (1 << 4) +#define SEARCHHEX (2 << 4) +#define SEARCHSTRING (3 << 4) +#define SEARCHREL8BIT (6 << 4) +#define SEARCHREL16BIT (7 << 4) + +result_struct *MappedMemorySearch(u32 startaddr, u32 endaddr, int searchtype, + const char *searchstr, + result_struct *prevresults, u32 *maxresults); + +int MappedMemoryLoad(const char *filename, u32 addr); +int MappedMemorySave(const char *filename, u32 addr, u32 size); +void MappedMemoryLoadExec(const char *filename, u32 pc); + +int LoadBios(const char *filename); +int LoadBackupRam(const char *filename); +void FormatBackupRam(void *mem, u32 size); + +int YabSaveState(const char *filename); +int YabLoadState(const char *filename); +int YabSaveStateSlot(const char *dirpath, u8 slot); +int YabLoadStateSlot(const char *dirpath, u8 slot); + +#endif diff --git a/yabause/src/movie.c b/yabause/src/movie.c new file mode 100644 index 0000000000..5ca936c116 --- /dev/null +++ b/yabause/src/movie.c @@ -0,0 +1,465 @@ +/* + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "peripheral.h" +#include "scsp.h" +#include "movie.h" +#include "cs2.h" +#include "vdp2.h" // for DisplayMessage() prototype +#include "yabause.h" + +int RecordingFileOpened; +int PlaybackFileOpened; + +struct MovieStruct Movie; + +char MovieStatus[40]; +int movieLoaded = 0; //Boolean value, 1 if a movie is playing or recording + +//Counting +int framecounter; +int lagframecounter; +int LagFrameFlag; +int FrameAdvanceVariable=0; + +int headersize=512; + +////////////////////////////////////////////////////////////////////////////// + +static void ReadHeader(FILE* fp) { + + fseek(fp, 0, SEEK_SET); + + fseek(fp, 172, SEEK_SET); + fread(&Movie.Rerecords, sizeof(Movie.Rerecords), 1, fp); + + fseek(fp, headersize, SEEK_SET); +} + +////////////////////////////////////////////////////////////////////////////// + +static void WriteHeader(FILE* fp) { + + fseek(fp, 0, SEEK_SET); + + fwrite("YMV", sizeof("YMV"), 1, fp); + fwrite(VERSION, sizeof(VERSION), 1, fp); + fwrite(cdip->cdinfo, sizeof(cdip->cdinfo), 1, fp); + fwrite(cdip->itemnum, sizeof(cdip->itemnum), 1, fp); + fwrite(cdip->version, sizeof(cdip->version), 1, fp); + fwrite(cdip->date, sizeof(cdip->date), 1, fp); + fwrite(cdip->gamename, sizeof(cdip->gamename), 1, fp); + fwrite(cdip->region, sizeof(cdip->region), 1, fp); + fwrite(&Movie.Rerecords, sizeof(Movie.Rerecords), 1, fp); + fwrite(&yabsys.emulatebios, sizeof(yabsys.emulatebios), 1, fp); + fwrite(&yabsys.IsPal, sizeof(yabsys.IsPal), 1, fp); + + fseek(fp, headersize, SEEK_SET); +} + +////////////////////////////////////////////////////////////////////////////// + +static void ClearInput(void) { + + //do something.... +} + +////////////////////////////////////////////////////////////////////////////// + +const char* Buttons[8] = {"B", "C", "A", "S", "U", "D", "R", "L"}; +const char* Spaces[8] = {" ", " ", " ", " ", " ", " ", " ", " "}; +const char* Buttons2[8] = {"", "", "", "L", "Z", "Y", "X", "R"}; +const char* Spaces2[8] = {"", "", "", " ", " ", " ", " ", " "}; + +char str[40]; +char InputDisplayString[40]; + +static void SetInputDisplayCharacters(void) { + + int x; + + strcpy(str, ""); + + for (x = 0; x < 8; x++) { + + if(PORTDATA1.data[2] & (1 << x)) { + strcat(str, Spaces[x]); + } + else + strcat(str, Buttons[x]); + + } + + for (x = 0; x < 8; x++) { + + if(PORTDATA1.data[3] & (1 << x)) { + strcat(str, Spaces2[x]); + } + else + strcat(str, Buttons2[x]); + + } + + strcpy(InputDisplayString, str); +} + +////////////////////////////////////////////////////////////////////////////// + +static void IncrementLagAndFrameCounter(void) +{ + if(LagFrameFlag == 1) + lagframecounter++; + + framecounter++; +} + +////////////////////////////////////////////////////////////////////////////// + +int framelength=16; + +void DoMovie(void) { + + int x; + + IncrementLagAndFrameCounter(); + LagFrameFlag=1; + SetInputDisplayCharacters(); + + //Read/Write Controller Data + if(Movie.Status == Recording) { + for (x = 0; x < 8; x++) { + fwrite(&PORTDATA1.data[x], 1, 1, Movie.fp); + } + for (x = 0; x < 8; x++) { + fwrite(&PORTDATA2.data[x], 1, 1, Movie.fp); + } + } + + if(Movie.Status == Playback) { + for (x = 0; x < 8; x++) { + fread(&PORTDATA1.data[x], 1, 1, Movie.fp); + } + for (x = 0; x < 8; x++) { + fread(&PORTDATA2.data[x], 1, 1, Movie.fp); + } + + //if we get to the end of the movie + if(((ftell(Movie.fp)-headersize)/framelength) >= Movie.Frames) { + fclose(Movie.fp); + PlaybackFileOpened=0; + Movie.Status = Stopped; + ClearInput(); + strcpy(MovieStatus, "Playback Stopped"); + } + } + + //Stop Recording/Playback + if(Movie.Status != Recording && RecordingFileOpened) { + fclose(Movie.fp); + RecordingFileOpened=0; + Movie.Status = Stopped; + strcpy(MovieStatus, "Recording Stopped"); + } + + if(Movie.Status != Playback && PlaybackFileOpened && Movie.ReadOnly != 0) { + fclose(Movie.fp); + PlaybackFileOpened=0; + Movie.Status = Stopped; + strcpy(MovieStatus, "Playback Stopped"); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MovieLoadState(const char * filename) { + + + if (Movie.ReadOnly == 1 && Movie.Status == Playback) { + //Movie.Status = Playback; + fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET); + } + + if(Movie.Status == Recording) { + fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET); + Movie.Rerecords++; + } + + if(Movie.Status == Playback && Movie.ReadOnly == 0) { + Movie.Status = Recording; + RecordingFileOpened=1; + strcpy(MovieStatus, "Recording Resumed"); + TruncateMovie(Movie); + fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET); + Movie.Rerecords++; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void TruncateMovie(struct MovieStruct Movie) { + + //when we resume recording, shorten the movie so that there isn't + //potential garbage data at the end + +/*//TODO + struct MovieBufferStruct tempbuffer; + fseek(Movie.fp,0,SEEK_SET); + tempbuffer=ReadMovieIntoABuffer(Movie.fp); + fclose(Movie.fp); + + //clear the file and write it again + Movie.fp=fopen(Movie.filename,"wb"); + fwrite(tempbuffer.data,framelength,framecounter,Movie.fp); + fclose(Movie.fp); + + Movie.fp=fopen(Movie.filename,"r+b"); +*/ +} + +////////////////////////////////////////////////////////////////////////////// + +static int MovieGetSize(FILE* fp) { + int size; + int fpos; + + fpos = ftell(fp);//save current pos + + fseek (fp,0,SEEK_END); + size=ftell(fp); + + Movie.Frames=(size-headersize)/ framelength; + + fseek(fp, fpos, SEEK_SET); //reset back to correct pos + return(size); +} + +////////////////////////////////////////////////////////////////////////////// + +void MovieToggleReadOnly(void) { + + if(Movie.Status == Playback) { + + if(Movie.ReadOnly == 1) + { + Movie.ReadOnly=0; + DisplayMessage("Movie is now read+write."); + } + else + { + Movie.ReadOnly=1; + DisplayMessage("Movie is now read only."); + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +void StopMovie(void) { + + if(Movie.Status == Recording && RecordingFileOpened) { + WriteHeader(Movie.fp); + fclose(Movie.fp); + RecordingFileOpened=0; + Movie.Status = Stopped; + ClearInput(); + strcpy(MovieStatus, "Recording Stopped"); + } + + if(Movie.Status == Playback && PlaybackFileOpened && Movie.ReadOnly != 0) { + fclose(Movie.fp); + PlaybackFileOpened=0; + Movie.Status = Stopped; + ClearInput(); + strcpy(MovieStatus, "Playback Stopped"); + } +} + +////////////////////////////////////////////////////////////////////////////// + +int SaveMovie(const char *filename) { + + char* str=malloc(1024); + + if(Movie.Status == Playback) + StopMovie(); + + if ((Movie.fp = fopen(filename, "w+b")) == NULL) + return -1; + + strcpy(str, filename); + Movie.filename=str; + RecordingFileOpened=1; + framecounter=0; + Movie.Status=Recording; + strcpy(MovieStatus, "Recording Started"); + Movie.Rerecords=0; + WriteHeader(Movie.fp); + YabauseReset(); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int PlayMovie(const char *filename) { + + char* str=malloc(1024); + + if(Movie.Status == Recording) + StopMovie(); + + + if ((Movie.fp = fopen(filename, "r+b")) == NULL) + return -1; + + strcpy(str, filename); + Movie.filename=str; + PlaybackFileOpened=1; + framecounter=0; + Movie.ReadOnly = 1; + Movie.Status=Playback; + Movie.Size = MovieGetSize(Movie.fp); + strcpy(MovieStatus, "Playback Started"); + ReadHeader(Movie.fp); + YabauseReset(); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void SaveMovieInState(FILE* fp, IOCheck_struct check) { + + struct MovieBufferStruct tempbuffer; + + fseek(fp, 0, SEEK_END); + + if(Movie.Status == Recording || Movie.Status == Playback) { + tempbuffer=ReadMovieIntoABuffer(Movie.fp); + + fwrite(&tempbuffer.size, 4, 1, fp); + fwrite(tempbuffer.data, tempbuffer.size, 1, fp); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MovieReadState(FILE* fp, const char * filename) { + + ReadMovieInState(fp); + MovieLoadState(filename);//file pointer and truncation + +} + +void ReadMovieInState(FILE* fp) { + + struct MovieBufferStruct tempbuffer; + int fpos; + + //overwrite the main movie on disk if we are recording or read+write playback + if(Movie.Status == Recording || (Movie.Status == Playback && Movie.ReadOnly == 0)) { + + fpos=ftell(fp);//where we are in the savestate + fread(&tempbuffer.size, 4, 1, fp);//size + if ((tempbuffer.data = (char *)malloc(tempbuffer.size)) == NULL) + { + return; + } + fread(tempbuffer.data, 1, tempbuffer.size, fp);//movie + fseek(fp, fpos, SEEK_SET);//reset savestate position + + rewind(Movie.fp); + fwrite(tempbuffer.data, 1, tempbuffer.size, Movie.fp); + rewind(Movie.fp); + } +} + +////////////////////////////////////////////////////////////////////////////// + +struct MovieBufferStruct ReadMovieIntoABuffer(FILE* fp) { + + int fpos; + struct MovieBufferStruct tempbuffer; + + fpos = ftell(fp);//save current pos + + fseek (fp,0,SEEK_END); + tempbuffer.size=ftell(fp); //get size + rewind(fp); + + tempbuffer.data = (char*) malloc (sizeof(char)*tempbuffer.size); + fread (tempbuffer.data, 1, tempbuffer.size, fp); + + fseek(fp, fpos, SEEK_SET); //reset back to correct pos + return(tempbuffer); +} + +////////////////////////////////////////////////////////////////////////////// + +const char *MakeMovieStateName(const char *filename) { + + static char *retbuf = NULL; // Save the pointer to avoid memory leaks + if(Movie.Status == Recording || Movie.Status == Playback) { + const unsigned long newsize = strlen(filename) + 5 + 1; + free(retbuf); + retbuf = malloc(newsize); + if (!retbuf) { + return NULL; // out of memory + } + sprintf(retbuf, "%smovie", filename); + return retbuf; + } else { + return filename; // unchanged + } + +} + +////////////////////////////////////////////////////////////////////////////// + +//debugging only +void TestWrite(struct MovieBufferStruct tempbuffer) { + + FILE* tempbuffertest; + + tempbuffertest=fopen("rmiab.txt", "wb"); + fwrite (tempbuffer.data, 1, tempbuffer.size, tempbuffertest); + fclose(tempbuffertest); +} + +////////////////////////////////////////////////////////////////////////////// + +void PauseOrUnpause(void) { + + if(FrameAdvanceVariable == RunNormal) { + FrameAdvanceVariable=Paused; + ScspMuteAudio(SCSP_MUTE_SYSTEM); + } + else { + FrameAdvanceVariable=RunNormal; + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + } +} + +////////////////////////////////////////////////////////////////////////////// + +int IsMovieLoaded(void) +{ + if (RecordingFileOpened || PlaybackFileOpened) + return 1; + else + return 0; +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/yabause/src/movie.h b/yabause/src/movie.h new file mode 100644 index 0000000000..ccb70f54cb --- /dev/null +++ b/yabause/src/movie.h @@ -0,0 +1,86 @@ +/* + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MOVIE_H +#define MOVIE_H + +#include "core.h" + +#define Stopped 1 +#define Recording 2 +#define Playback 3 + +#define RunNormal 0 +#define Paused 1 +#define NeedAdvance 2 + +void DoMovie(void); + +struct MovieStruct +{ + int Status; + FILE *fp; + int ReadOnly; + int Rerecords; + int Size; + int Frames; + const char* filename; +}; + +extern struct MovieStruct Movie; + +struct MovieBufferStruct +{ + int size; + char* data; +}; + +struct MovieBufferStruct ReadMovieIntoABuffer(FILE* fp); + +void MovieLoadState(const char * filename); + +void SaveMovieInState(FILE* fp, IOCheck_struct check); +void ReadMovieInState(FILE* fp); + +void TestWrite(struct MovieBufferStruct tempbuffer); + +void MovieToggleReadOnly(void); + +void TruncateMovie(struct MovieStruct Movie); + +void DoFrameAdvance(void); + +int SaveMovie(const char *filename); +int PlayMovie(const char *filename); +void StopMovie(void); + +const char *MakeMovieStateName(const char *filename); + +void MovieReadState(FILE* fp, const char * filename); + +void PauseOrUnpause(void); + +int IsMovieLoaded(void); + +extern int framecounter; +extern int LagFrameFlag; +extern int lagframecounter; +extern char MovieStatus[40]; +extern char InputDisplayString[40]; +extern int FrameAdvanceVariable; +#endif diff --git a/yabause/src/netlink.c b/yabause/src/netlink.c new file mode 100644 index 0000000000..5d857fab85 --- /dev/null +++ b/yabause/src/netlink.c @@ -0,0 +1,854 @@ +/* Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef USESOCKET +#ifdef __MINGW32__ +// I blame mingw for this +#define _WIN32_WINNT 0x501 +#endif +#endif + +#include +#ifdef USESOCKET +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif +#endif +#include "cs2.h" +#include "error.h" +#include "netlink.h" +#include "debug.h" +#include "scu.h" + +Netlink *NetlinkArea = NULL; + +#define NL_RESULTCODE_OK 0 +#define NL_RESULTCODE_CONNECT 1 +#define NL_RESULTCODE_RING 2 +#define NL_RESULTCODE_NOCARRIER 3 +#define NL_RESULTCODE_ERROR 4 +#define NL_RESULTCODE_CONNECT1200 5 +#define NL_RESULTCODE_NODIALTONE 6 +#define NL_RESULTCODE_BUSY 7 +#define NL_RESULTCODE_NOANSWER 8 + +#define CONNECTTYPE_SERVER 0 +#define CONNECTTYPE_CLIENT 1 + +#define CONNECTSTATUS_IDLE 0 +#define CONNECTSTATUS_WAIT 1 +#define CONNECTSTATUS_CONNECT 2 +#define CONNECTSTATUS_CONNECTED 3 + +#define MODEMSTATE_COMMAND 0 +#define MODEMSTATE_ONLINE 1 + +#ifdef USESOCKET +static int NetworkInit(void); +static void NetworkDeInit(void); +static int NetworkConnect(const char *ip, const char *port); +static int NetworkWaitForConnect(const char *port); +static int NetworkSend(const void *buffer, int length); +static int NetworkReceive(void *buffer, int maxlength); + +#ifndef WIN32 +#define closesocket close +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// + +UNUSED static void NetlinkLSRChange(u8 val) +{ + // If IER bit 2 is set and if any of the error or alarms bits are set(and + // they weren't previously), trigger an interrupt + if ((NetlinkArea->reg.IER & 0x4) && ((NetlinkArea->reg.LSR ^ val) & val & 0x1E)) + { + NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x6; + ScuSendExternalInterrupt12(); + } + + NetlinkArea->reg.LSR = val; +} + +////////////////////////////////////////////////////////////////////////////// + +#ifndef USESOCKET +UNUSED +#endif +static void NetlinkMSRChange(u8 set, u8 clear) +{ + u8 change; + + change = ((NetlinkArea->reg.MSR >> 4) ^ set) & set; + change |= (((NetlinkArea->reg.MSR >> 4) ^ 0xFF) ^ clear) & clear; + + // If IER bit 3 is set and CTS/DSR/RI/RLSD changes, trigger interrupt + if ((NetlinkArea->reg.IER & 0x8) && change) + { + NetlinkArea->reg.IIR = NetlinkArea->reg.IIR & 0xF0; + ScuSendExternalInterrupt12(); + } + + NetlinkArea->reg.MSR &= ~(clear << 4); + NetlinkArea->reg.MSR |= (set << 4) | change; +} + +////////////////////////////////////////////////////////////////////////////// + +u8 FASTCALL NetlinkReadByte(u32 addr) +{ + u8 ret; + + switch (addr) + { + case 0x95001: // Receiver Buffer/Divisor Latch Low Byte + { + if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte + return NetlinkArea->reg.DLL; + else // Receiver Buffer + { + if (NetlinkArea->outbuffersize == 0) + return 0x00; + + ret = NetlinkArea->outbuffer[NetlinkArea->outbufferstart]; + NetlinkArea->outbufferstart++; + NetlinkArea->outbuffersize--; + + // If the buffer is empty now, make sure the data available + // bit in LSR is cleared + if (NetlinkArea->outbuffersize == 0) + { + NetlinkArea->outbufferstart = NetlinkArea->outbufferend = 0; + NetlinkArea->reg.LSR &= ~0x01; + } + + // If interrupt has been triggered because of RBR having data, reset it + if ((NetlinkArea->reg.IER & 0x1) && (NetlinkArea->reg.IIR & 0xF) == 0x4) + NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; + + return ret; + } + + return 0; + } + case 0x95009: // Interrupt Identification Register + { + // If interrupt has been triggered because THB is empty, reset it + if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2) + NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; + return NetlinkArea->reg.IIR; + } + case 0x9500D: // Line Control Register + { + return NetlinkArea->reg.LCR; + } + case 0x95011: // Modem Control Register + { + return NetlinkArea->reg.MCR; + } + case 0x95015: // Line Status Register + { + return NetlinkArea->reg.LSR; + } + case 0x95019: // Modem Status Register + { + // If interrupt has been triggered because of MSR change, reset it + if ((NetlinkArea->reg.IER & 0x8) && (NetlinkArea->reg.IIR & 0xF) == 0) + NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; + ret = NetlinkArea->reg.MSR; + NetlinkArea->reg.MSR &= 0xF0; + return ret; + } + case 0x9501D: // Scratch + { + return NetlinkArea->reg.SCR; + } + default: + break; + } + + LOG("Unimplemented Netlink byte read: %08X\n", addr); + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +static void FASTCALL NetlinkDoATResponse(const char *string) +{ + strcpy((char *)&NetlinkArea->outbuffer[NetlinkArea->outbufferend], string); + NetlinkArea->outbufferend += (u32)strlen(string); + NetlinkArea->outbuffersize += (u32)strlen(string); +} + +////////////////////////////////////////////////////////////////////////////// + +static int FASTCALL NetlinkFetchATParameter(u8 val, u32 *offset) +{ + if (val >= '0' && val <= '9') + { + (*offset)++; + return (val - 0x30); + } + else + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void FASTCALL NetlinkWriteByte(u32 addr, u8 val) +{ + switch (addr) + { + case 0x2503D: // ??? + { + return; + } + case 0x95001: // Transmitter Holding Buffer/Divisor Latch Low Byte + { + if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte + { + NetlinkArea->reg.DLL = val; + } + else // Transmitter Holding Buffer + { + NetlinkArea->inbuffer[NetlinkArea->inbufferend] = val; + NetlinkArea->inbufferend++; + NetlinkArea->inbuffersize++; + + // If interrupt has been triggered because THB is empty, reset it + if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2) + NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; + + if (NetlinkArea->modemstate == MODEMSTATE_COMMAND) + { + + if (val == 0x0D && + (strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "AT", 2) == 0 || + strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "at", 2) == 0)) // fix me + { + u32 i=NetlinkArea->inbufferstart+2; + int resultcode=NL_RESULTCODE_OK; + int parameter; + + LOG("Program issued %s\n", NetlinkArea->inbuffer); + + // If echo is enabled, do it + if (NetlinkArea->isechoenab) + NetlinkDoATResponse((char *)NetlinkArea->inbuffer); + + // Handle AT command + while(NetlinkArea->inbuffer[i] != 0xD) + { + switch (toupper(NetlinkArea->inbuffer[i])) + { + case '%': + break; + case '&': + // Figure out second part of command + i++; + + switch (toupper(NetlinkArea->inbuffer[i])) + { + case 'C': + // Data Carrier Detect Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'D': + // Data Terminal Ready Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'F': + // Factory reset + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'K': + // Local Flow Control Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'Q': + // Communications Mode Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'S': + // Data Set Ready Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + default: break; + } + break; + case ')': + case '*': + case ':': + case '?': + case '@': + case '\\': + break; + case 'A': + // Answer Command(no other commands should follow) + break; + case 'D': + // Dial Command + NetlinkArea->connectstatus = CONNECTSTATUS_CONNECT; + + i = NetlinkArea->inbufferend-1; // fix me + break; + case 'E': + // Command State Character Echo Selection + + parameter = NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + + // Parameter can only be 0 or 1 + if (parameter < 2) + NetlinkArea->isechoenab = parameter; + else + resultcode = NL_RESULTCODE_ERROR; + + break; + case 'I': + // Internal Memory Tests + switch(NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i)) + { + case 0: + NetlinkDoATResponse("\r\n28800\r\n"); + break; + default: break; + } + break; + case 'L': + // Speaker Volume Level Selection + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'M': + // Speaker On/Off Selection + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'V': + // Result Code Format Options + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + case 'W': + // Negotiation Progress Message Selection + NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); + break; + default: break; + } + + i++; + } + + switch (resultcode) + { + case NL_RESULTCODE_OK: // OK + NetlinkDoATResponse("\r\nOK\r\n"); + break; + case NL_RESULTCODE_CONNECT: // CONNECT + NetlinkDoATResponse("\r\nCONNECT\r\n"); + break; + case NL_RESULTCODE_RING: // RING + NetlinkDoATResponse("\r\nRING\r\n"); + break; + case NL_RESULTCODE_NOCARRIER: // NO CARRIER + NetlinkDoATResponse("\r\nNO CARRIER\r\n"); + break; + case NL_RESULTCODE_ERROR: // ERROR + NetlinkDoATResponse("\r\nERROR\r\n"); + break; + case NL_RESULTCODE_CONNECT1200: // CONNECT 1200 + NetlinkDoATResponse("\r\nCONNECT 1200\r\n"); + break; + case NL_RESULTCODE_NODIALTONE: // NO DIALTONE + NetlinkDoATResponse("\r\nNO DIALTONE\r\n"); + break; + case NL_RESULTCODE_BUSY: // BUSY + NetlinkDoATResponse("\r\nBUSY\r\n"); + break; + case NL_RESULTCODE_NOANSWER: // NO ANSWER + NetlinkDoATResponse("\r\nNO ANSWER\r\n"); + break; + default: break; + } + + memset(NetlinkArea->inbuffer, 0, NetlinkArea->inbuffersize); + NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; + + if (NetlinkArea->outbuffersize > 0) + { + // Set Data available bit in LSR + NetlinkArea->reg.LSR |= 0x01; + + // Trigger Interrrupt + NetlinkArea->reg.IIR = 0x4; + ScuSendExternalInterrupt12(); + } + } + } + } + + return; + } + case 0x95005: // Interrupt Enable Register/Divisor Latch High Byte + { + if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch High Byte + { + NetlinkArea->reg.DLM = val; + } + else // Interrupt Enable Register + { + NetlinkArea->reg.IER = val; + } + + return; + } + case 0x95009: // FIFO Control Register + { + NetlinkArea->reg.FCR = val; + + if (val & 0x1) + // set FIFO enabled bits + NetlinkArea->reg.IIR |= 0xC0; + else + // clear FIFO enabled bits + NetlinkArea->reg.IIR &= ~0xC0; + + return; + } + case 0x9500D: // Line Control Register + { + NetlinkArea->reg.LCR = val; + return; + } + case 0x95011: // Modem Control Register + { + NetlinkArea->reg.MCR = val; + return; + } + case 0x95019: // Modem Status Register(read-only) + return; + case 0x9501D: // Scratch + { + NetlinkArea->reg.SCR = val; + return; + } + default: + break; + } + + LOG("Unimplemented Netlink byte write: %08X\n", addr); +} + +////////////////////////////////////////////////////////////////////////////// + +int NetlinkInit(const char *setting) +{ + if ((NetlinkArea = malloc(sizeof(Netlink))) == NULL) + { + Cs2Area->carttype = CART_NONE; + YabSetError(YAB_ERR_CANNOTINIT, (void *)"Netlink"); + return 0; + } + + memset(NetlinkArea->inbuffer, 0, NETLINK_BUFFER_SIZE); + memset(NetlinkArea->outbuffer, 0, NETLINK_BUFFER_SIZE); + + NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; + NetlinkArea->outbufferstart = NetlinkArea->outbufferend = NetlinkArea->outbuffersize = 0; + + NetlinkArea->isechoenab = 1; + NetlinkArea->cycles = 0; + NetlinkArea->modemstate = MODEMSTATE_COMMAND; + + NetlinkArea->reg.RBR = 0x00; + NetlinkArea->reg.IER = 0x00; + NetlinkArea->reg.DLL = 0x00; + NetlinkArea->reg.DLM = 0x00; + NetlinkArea->reg.IIR = 0x01; +// NetlinkArea->reg.FCR = 0x??; // have no idea + NetlinkArea->reg.LCR = 0x00; + NetlinkArea->reg.MCR = 0x00; + NetlinkArea->reg.LSR = 0x60; + NetlinkArea->reg.MSR = 0x30; + NetlinkArea->reg.SCR = 0x01; + + if (setting == NULL || strcmp(setting, "") == 0) + { + // Use Loopback ip and port 1337 + sprintf(NetlinkArea->ipstring, "127.0.0.1"); + sprintf(NetlinkArea->portstring, "1337"); + } + else + { + char *p; + p = strchr(setting, '\n'); + if (p == NULL) + { + strcpy(NetlinkArea->ipstring, setting); + sprintf(NetlinkArea->portstring, "1337"); + } + else + { + memcpy(NetlinkArea->ipstring, setting, (int)(p - setting)); + NetlinkArea->ipstring[(p - setting)] = '\0'; + if (strlen(p+1) == 0) + sprintf(NetlinkArea->portstring, "1337"); + else + strcpy(NetlinkArea->portstring, p+1); + } + } + +#ifdef USESOCKET + return NetworkInit(); +#else + return 0; +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +void NetlinkDeInit(void) +{ +#ifdef USESOCKET + NetworkDeInit(); +#endif + + if (NetlinkArea) + free(NetlinkArea); +} + +////////////////////////////////////////////////////////////////////////////// + +void NetlinkExec(u32 timing) +{ + NetlinkArea->cycles += timing; + + if (NetlinkArea->cycles >= 20000) + { + NetlinkArea->cycles -= 20000; + + switch(NetlinkArea->connectstatus) + { + case CONNECTSTATUS_IDLE: + { +#ifdef USESOCKET + if (NetworkWaitForConnect(NetlinkArea->portstring) == 0) + { + NetlinkArea->connectstatus = CONNECTSTATUS_CONNECTED; + NetlinkArea->modemstate = MODEMSTATE_ONLINE; + + // This is probably wrong, but let's give it a try anyways + NetlinkDoATResponse("\r\nRING\r\n\r\nCONNECT\r\n"); + NetlinkMSRChange(0x08, 0x00); + + // Set Data available bit in LSR + NetlinkArea->reg.LSR |= 0x01; + + // Trigger Interrrupt + NetlinkArea->reg.IIR = 0x4; + ScuSendExternalInterrupt12(); + LOG("Connected via idle\n"); + } +#endif + break; + } + case CONNECTSTATUS_CONNECT: + { +#ifdef USESOCKET + if (NetworkConnect(NetlinkArea->ipstring, NetlinkArea->portstring) == 0) + { + NetlinkArea->connectstatus = CONNECTSTATUS_CONNECTED; + NetlinkArea->modemstate = MODEMSTATE_ONLINE; + + NetlinkDoATResponse("\r\nCONNECT\r\n"); + NetlinkMSRChange(0x08, 0x00); + + // Set Data available bit in LSR + NetlinkArea->reg.LSR |= 0x01; + + // Trigger Interrrupt + NetlinkArea->reg.IIR = 0x4; + ScuSendExternalInterrupt12(); + LOG("Connected via connect\n"); + } +#endif + break; + } + case CONNECTSTATUS_CONNECTED: + { +#ifdef USESOCKET + int bytes; + fd_set read_fds; + fd_set write_fds; + struct timeval tv; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + + // Let's see if we can even connect at this point + FD_SET(NetlinkArea->connectsocket, &read_fds); + FD_SET(NetlinkArea->connectsocket, &write_fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(NetlinkArea->connectsocket+1, &read_fds, &write_fds, NULL, &tv) < 1) + { + LOG("select failed\n"); + return; + } + + if (NetlinkArea->modemstate == MODEMSTATE_ONLINE && NetlinkArea->inbuffersize > 0 && FD_ISSET(NetlinkArea->connectsocket, &write_fds)) + { + LOG("Sending to external source..."); + + // Send via network connection + if ((bytes = NetworkSend(&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], NetlinkArea->inbufferend-NetlinkArea->inbufferstart)) >= 0) + { + LOG("Successfully sent %d byte(s)\n", bytes); + if (NetlinkArea->inbufferend > bytes) + { + NetlinkArea->inbufferstart += bytes; + NetlinkArea->inbuffersize -= bytes; + } + else + NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; + } + else + { + LOG("failed.\n"); + } + } + + if (FD_ISSET(NetlinkArea->connectsocket, &read_fds)) + { +// if ((bytes = NetworkReceive(&NetlinkArea->outbuffer[NetlinkArea->outbufferend], NETLINK_BUFFER_SIZE-NetlinkArea->outbufferend)) > 0) + if ((bytes = NetworkReceive(&NetlinkArea->outbuffer[NetlinkArea->outbufferend], 8)) > 0) + { + NetlinkArea->outbufferend += bytes; + NetlinkArea->outbuffersize += bytes; + + NetlinkMSRChange(0x08, 0x00); + + // Set Data available bit in LSR + NetlinkArea->reg.LSR |= 0x01; + + // Trigger Interrrupt + NetlinkArea->reg.IIR = 0x4; + ScuSendExternalInterrupt12(); + LOG("Received %d byte(s) from external source\n", bytes); + } + } +#endif + break; + } + default: break; + } + } +} + +////////////////////////////////////////////////////////////////////////////// +#ifdef USESOCKET + +static int NetworkInit(void) +{ +#ifdef WIN32 + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) + return -1; +#endif + + NetlinkArea->connectsocket = -1; + NetlinkArea->connectstatus = CONNECTSTATUS_IDLE; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int NetworkConnect(const char *ip, const char *port) +{ + struct addrinfo *result = NULL, + hints; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(ip, port, &hints, &result) != 0) + return -1; + + // Create a Socket + if ((NetlinkArea->connectsocket = socket(result->ai_family, result->ai_socktype, + result->ai_protocol)) == -1) + { + freeaddrinfo(result); + return -1; + } + + // Connect to the socket + if (connect(NetlinkArea->connectsocket, result->ai_addr, (int)result->ai_addrlen) == -1) + { + freeaddrinfo(result); + closesocket(NetlinkArea->connectsocket); + NetlinkArea->connectsocket = -1; + return -1; + } + + freeaddrinfo(result); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int NetworkWaitForConnect(const char *port) +{ + struct addrinfo *result = NULL, + hints; + int ListenSocket = -1; + fd_set read_fds; + struct timeval tv; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + if (getaddrinfo(NULL, port, &hints, &result) != 0) + return -1; + + // Create a socket that the client can connect to + if ((ListenSocket = socket(result->ai_family, result->ai_socktype, + result->ai_protocol)) == -1) + { + freeaddrinfo(result); + return -1; + } + + // Setup the listening socket + if (bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen) == -1) + { + freeaddrinfo(result); + closesocket(ListenSocket); + return -1; + } + + freeaddrinfo(result); + + // Shhh... Let's listen + if (listen(ListenSocket, SOMAXCONN) == -1) + { + closesocket(ListenSocket); + return -1; + } + + FD_ZERO(&read_fds); + + // Let's see if we can even connect at this point + FD_SET(ListenSocket, &read_fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(ListenSocket+1, &read_fds, NULL, NULL, &tv) < 1) + { + closesocket(ListenSocket); + return -1; + } + + if (FD_ISSET(ListenSocket, &read_fds)) + { + // Good, time to connect + if ((NetlinkArea->connectsocket = accept(ListenSocket, NULL, NULL)) == -1) + { + closesocket(ListenSocket); + return -1; + } + + // We don't need the listen socket anymore + closesocket(ListenSocket); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +static int NetworkSend(const void *buffer, int length) +{ + int bytessent; + + if ((bytessent = send(NetlinkArea->connectsocket, buffer, length, 0)) == -1) + { + // Fix me, better error handling is needed +// closesocket(NetlinkArea->connectsocket); +// NetlinkArea->connectsocket = -1; + return -1; + } + + return bytessent; +} + +////////////////////////////////////////////////////////////////////////////// + +static int NetworkReceive(void *buffer, int maxlength) +{ + int bytesreceived; + + bytesreceived = recv(NetlinkArea->connectsocket, buffer, maxlength, 0); + + if (bytesreceived == 0) + { + // Fix me, better handling is needed + LOG("Connection closed\n"); + return -1; + } + else if (bytesreceived < 0) + { + // Fix me, better error handling is needed + return -1; + } + + return bytesreceived; +} + +////////////////////////////////////////////////////////////////////////////// + +static void NetworkDeInit(void) +{ + if (NetlinkArea->connectsocket != -1) + closesocket(NetlinkArea->connectsocket); +#ifdef WIN32 + WSACleanup(); +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/yabause/src/netlink.h b/yabause/src/netlink.h new file mode 100644 index 0000000000..38c85aed60 --- /dev/null +++ b/yabause/src/netlink.h @@ -0,0 +1,64 @@ +/* Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef NETLINK_H +#define NETLINK_H + +#define NETLINK_BUFFER_SIZE 1024 + +typedef struct +{ + u8 RBR; + u8 THR; + u8 IER; + u8 DLL; + u8 DLM; + u8 IIR; + u8 FCR; + u8 LCR; + u8 MCR; + u8 LSR; + u8 MSR; + u8 SCR; +} netlinkregs_struct; + +typedef struct { + u8 inbuffer[NETLINK_BUFFER_SIZE]; + u8 outbuffer[NETLINK_BUFFER_SIZE]; + u32 inbufferstart, inbufferend, inbuffersize; + u32 outbufferstart, outbufferend, outbuffersize; + netlinkregs_struct reg; + int isechoenab; + int connectsocket; + int connectstatus; + u32 cycles; + int modemstate; + char ipstring[16]; + char portstring[6]; +} Netlink; + +extern Netlink *NetlinkArea; + +u8 FASTCALL NetlinkReadByte(u32 addr); +void FASTCALL NetlinkWriteByte(u32 addr, u8 val); +int NetlinkInit(const char *settingstring); +void NetlinkDeInit(void); +void NetlinkExec(u32 timing); + +#endif diff --git a/yabause/src/osdcore.c b/yabause/src/osdcore.c new file mode 100644 index 0000000000..5f16d04c23 --- /dev/null +++ b/yabause/src/osdcore.c @@ -0,0 +1,399 @@ +/* Copyright 2012 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "osdcore.h" +#include "vdp1.h" +#include "font.h" + +#include +#include + +#ifndef YAB_PORT_OSD +/* +Heya fellow port developper :) +If you're reading this, that may be because you want to +use your own OSD core in your port and you're about to +do it here... +Don't. +Please define the CPP constant YAB_PORT_OSD and define +your own list of OSD cores in your port. +This definition was added here to avoid breaking "everything" +when we the new OSD system was added. +*/ +OSD_struct *OSDCoreList[] = { +&OSDDummy, +#ifdef HAVE_LIBGLUT +&OSDGlut, +#endif +&OSDSoft, +NULL +}; +#else +extern OSD_struct * OSDCoreList[]; +#endif + +static OSD_struct * OSD = NULL; +static OSDMessage_struct osdmessages[OSDMSG_COUNT]; + +int OSDInit(int coreid) +{ + int i; + + for (i = 0; OSDCoreList[i] != NULL; i++) + { + if (OSDCoreList[i]->id == coreid) + { + OSD = OSDCoreList[i]; + break; + } + } + + if (OSD == NULL) + return -1; + + if (OSD->Init() != 0) + return -1; + + memset(osdmessages, 0, sizeof(osdmessages)); + osdmessages[OSDMSG_FPS].hidden = 1; + osdmessages[OSDMSG_DEBUG].hidden = 1; + + return 0; +} + +void OSDDeInit() { + if (OSD) + OSD->DeInit(); + OSD = NULL; +} + +int OSDChangeCore(int coreid) +{ + int preservefps, fpshidden, dbghidden; + + preservefps = (OSD != NULL); + fpshidden = osdmessages[OSDMSG_FPS].hidden; + dbghidden = osdmessages[OSDMSG_DEBUG].hidden; + + OSDDeInit(); + OSDInit(coreid); + + if (preservefps) + { + osdmessages[OSDMSG_FPS].hidden = fpshidden; + osdmessages[OSDMSG_DEBUG].hidden = dbghidden; + } + + return 0; +} + +void OSDPushMessage(int msgtype, int ttl, const char * format, ...) +{ + va_list arglist; + char message[1024]; + + if (ttl == 0) return; + + va_start(arglist, format); + vsprintf(message, format, arglist); + va_end(arglist); + + osdmessages[msgtype].type = msgtype; + osdmessages[msgtype].message = strdup(message); + osdmessages[msgtype].timetolive = ttl; + osdmessages[msgtype].timeleft = ttl; +} + +int OSDDisplayMessages(u32 * buffer, int w, int h) +{ + int i = 0; + int somethingnew = 0; + + if (OSD == NULL) return somethingnew; + + for(i = 0;i < OSDMSG_COUNT;i++) + if (osdmessages[i].timeleft > 0) + { + if (osdmessages[i].hidden == 0) + { + somethingnew = 1; + OSD->DisplayMessage(osdmessages + i, buffer, w, h); + } + osdmessages[i].timeleft--; + if (osdmessages[i].timeleft == 0) free(osdmessages[i].message); + } + + return somethingnew; +} + +void OSDToggle(int what) +{ + if ((what < 0) || (what >= OSDMSG_COUNT)) return; + + osdmessages[what].hidden = 1 - osdmessages[what].hidden; +} + +int OSDIsVisible(int what) +{ + if ((what < 0) || (what >= OSDMSG_COUNT)) return -1; + + return 1 - osdmessages[what].hidden; +} + +void OSDSetVisible(int what, int visible) +{ + if ((what < 0) || (what >= OSDMSG_COUNT)) return; + + visible = visible == 0 ? 0 : 1; + osdmessages[what].hidden = 1 - visible; +} + +int OSDUseBuffer(void) +{ + if (OSD == NULL) return 0; + + return OSD->UseBuffer(); +} + +void ToggleFPS() +{ + OSDToggle(OSDMSG_FPS); +} + +int GetOSDToggle(void) +{ + return OSDIsVisible(OSDMSG_FPS); +} + +void SetOSDToggle(int toggle) +{ + OSDSetVisible(OSDMSG_FPS, toggle); +} + +void DisplayMessage(const char* str) +{ + OSDPushMessage(OSDMSG_STATUS, 120, str); +} + +static int OSDDummyInit(void); +static void OSDDummyDeInit(void); +static void OSDDummyReset(void); +static void OSDDummyDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h); +static int OSDDummyUseBuffer(void); + +OSD_struct OSDDummy = { + OSDCORE_DUMMY, + "Dummy OSD Interface", + OSDDummyInit, + OSDDummyDeInit, + OSDDummyReset, + OSDDummyDisplayMessage, + OSDDummyUseBuffer, +}; + +int OSDDummyInit(void) +{ + return 0; +} + +void OSDDummyDeInit(void) +{ +} + +void OSDDummyReset(void) +{ +} + +void OSDDummyDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h) +{ +} + +int OSDDummyUseBuffer(void) +{ + return 0; +} + +#ifdef HAVE_LIBGLUT +#ifdef __APPLE__ + #include +#else + #include +#endif + +static int OSDGlutInit(void); +static void OSDGlutDeInit(void); +static void OSDGlutReset(void); +static void OSDGlutDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h); +static int OSDGlutUseBuffer(void); + +OSD_struct OSDGlut = { + OSDCORE_GLUT, + "Glut OSD Interface", + OSDGlutInit, + OSDGlutDeInit, + OSDGlutReset, + OSDGlutDisplayMessage, + OSDGlutUseBuffer +}; + +int OSDGlutInit(void) +{ + int fake_argc = 1; + char * fake_argv[] = { "yabause" }; + static int glutinited = 0; + + if (!glutinited) + { + glutInit(&fake_argc, fake_argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL); + glutinited = 1; + } + + return 0; +} + +void OSDGlutDeInit(void) +{ +} + +void OSDGlutReset(void) +{ +} + +void OSDGlutDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h) +{ + int LeftX=9; + int Width=500; + int TxtY=11; + int Height=13; + int i, msglength; + int vidwidth, vidheight; + + VIDCore->GetGlSize(&vidwidth, &vidheight); + Width = vidwidth - 2 * LeftX; + + switch(message->type) { + case OSDMSG_STATUS: + TxtY = vidheight - (Height + TxtY); + break; + } + + msglength = strlen(message->message); + + glBegin(GL_POLYGON); + glColor3f(0, 0, 0); + glVertex2i(LeftX, TxtY); + glVertex2i(LeftX + Width, TxtY); + glVertex2i(LeftX + Width, TxtY + Height); + glVertex2i(LeftX, TxtY + Height); + glEnd(); + + glColor3f(1.0f, 1.0f, 1.0f); + glRasterPos2i(10, TxtY + 11); + for (i = 0; i < msglength; i++) { + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, message->message[i]); + } + glColor3f(1, 1, 1); +} + +int OSDGlutUseBuffer(void) +{ + return 0; +} +#endif + +static int OSDSoftInit(void); +static void OSDSoftDeInit(void); +static void OSDSoftReset(void); +static void OSDSoftDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h); +static int OSDSoftUseBuffer(void); + +OSD_struct OSDSoft = { + OSDCORE_SOFT, + "Software OSD Interface", + OSDSoftInit, + OSDSoftDeInit, + OSDSoftReset, + OSDSoftDisplayMessage, + OSDSoftUseBuffer +}; + +int OSDSoftInit(void) +{ + return 0; +} + +void OSDSoftDeInit(void) +{ +} + +void OSDSoftReset(void) +{ +} + +void OSDSoftDisplayMessage(OSDMessage_struct * message, u32 * buffer, int w, int h) +{ + int i; + u32 * dot; + char * c; + int loffset = 0; + + if (buffer == NULL) return; + + switch (message->type) + { + case OSDMSG_STATUS: + loffset = h - 48; + break; + } + + c = message->message; + i = 0; + while(*c) + { + if (*c >= 47) + { + int first_line, l, p; + first_line = *c * 10; + for(l = 0;l < 10;l++) + { + for(p = 0;p < 9;p++) + { + if (font[first_line + l][p] == '.') + { + dot = buffer + 20 + ((loffset + l + 20) * w) + (i * 8) + p; + *dot = 0xFF000000; + } + else if (font[first_line + l][p] == '#') + { + dot = buffer + 20 + ((loffset + l + 20) * w) + (i * 8) + p; + *dot = 0xFFFFFFFF; + } + } + } + } + c++; + i++; + } +} + +int OSDSoftUseBuffer(void) +{ + return 1; +} diff --git a/yabause/src/osdcore.h b/yabause/src/osdcore.h new file mode 100644 index 0000000000..d13a633561 --- /dev/null +++ b/yabause/src/osdcore.h @@ -0,0 +1,82 @@ +/* Copyright 2012 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef OSDCORE_H +#define OSDCORE_H + +#include "core.h" + +#define OSDCORE_DUMMY 0 +#define OSDCORE_GLUT 1 +#define OSDCORE_SOFT 2 + +#ifdef HAVE_LIBGLUT + #define OSDCORE_DEFAULT OSDCORE_GLUT +#else + #define OSDCORE_DEFAULT OSDCORE_SOFT +#endif + +#define OSDMSG_FPS 0 +#define OSDMSG_STATUS 1 +#define OSDMSG_DEBUG 2 +#define OSDMSG_COUNT 3 + +typedef struct { + int type; + char * message; + int timetolive; + int timeleft; + int hidden; +} OSDMessage_struct; + +typedef struct { + int id; + const char *Name; + + int (*Init)(void); + void (*DeInit)(void); + void (*Reset)(void); + + void (*DisplayMessage)(OSDMessage_struct * message, u32 * buffer, int w, int h); + int (*UseBuffer)(void); +} OSD_struct; + +int OSDInit(int coreid); +int OSDChangeCore(int coreid); + +void OSDPushMessage(int msgtype, int ttl, const char * message, ...); +int OSDDisplayMessages(u32 * buffer, int w, int h); +void OSDToggle(int what); +int OSDIsVisible(int what); +void OSDSetVisible(int what, int visible); +int OSDUseBuffer(void); + +extern OSD_struct OSDDummy; +#ifdef HAVE_LIBGLUT +extern OSD_struct OSDGlut; +#endif +extern OSD_struct OSDSoft; + +/* defined for backward compatibility (used to be in vdp2.h) */ +void ToggleFPS(void); +int GetOSDToggle(void); +void SetOSDToggle(int toggle); +void DisplayMessage(const char* str); + +#endif diff --git a/yabause/src/perdx.c b/yabause/src/perdx.c new file mode 100644 index 0000000000..b7baa67d7b --- /dev/null +++ b/yabause/src/perdx.c @@ -0,0 +1,1301 @@ +/* Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "debug.h" +#include "peripheral.h" +#include "perdx.h" +#include "vdp1.h" +#include "vdp2.h" +#include "yui.h" +#include "movie.h" + +#define IDD_BUTTONCONFIG 123 +#define IDC_WAITINPUT 1001 +#define IDC_DXDEVICECB 1010 +#define IDC_UPTEXT 1024 +#define IDC_RIGHTTEXT 1025 +#define IDC_DOWNTEXT 1026 +#define IDC_LEFTTEXT 1027 +#define IDC_RTEXT 1028 +#define IDC_LTEXT 1029 +#define IDC_STARTTEXT 1030 +#define IDC_ATEXT 1031 +#define IDC_BTEXT 1032 +#define IDC_CTEXT 1033 +#define IDC_XTEXT 1034 +#define IDC_YTEXT 1035 +#define IDC_ZTEXT 1036 +#define IDC_CUSTOMCANCEL 1037 + +enum { + EMUTYPE_NONE=0, + EMUTYPE_STANDARDPAD, + EMUTYPE_ANALOGPAD, + EMUTYPE_STUNNER, + EMUTYPE_MOUSE, + EMUTYPE_KEYBOARD +}; + +int PERDXInit(void); +void PERDXDeInit(void); +int PERDXHandleEvents(void); +int Check_Skip_Key(); + +PerInterface_struct PERDIRECTX = { +PERCORE_DIRECTX, +"DirectX Input Interface", +PERDXInit, +PERDXDeInit, +PERDXHandleEvents +}; + +LPDIRECTINPUT8 lpDI8 = NULL; +LPDIRECTINPUTDEVICE8 lpDIDevice[256]; // I hope that's enough +GUID GUIDDevice[256]; // I hope that's enough +u32 numguids=0; +u32 numdevices=0; + +u32 numpads=12; +PerPad_struct *pad[12]; +padconf_struct paddevice[12]; +int porttype[2]; + +const char *mouse_names[] = { +"A", +"B", +"C", +"Start", +NULL +}; + +#define TYPE_KEYBOARD 0 +#define TYPE_JOYSTICK 1 +#define TYPE_MOUSE 2 + +#define PAD_DIR_AXISLEFT 0 +#define PAD_DIR_AXISRIGHT 1 +#define PAD_DIR_AXISUP 2 +#define PAD_DIR_AXISDOWN 3 +#define PAD_DIR_POVUP 4 +#define PAD_DIR_POVRIGHT 5 +#define PAD_DIR_POVDOWN 6 +#define PAD_DIR_POVLEFT 7 + +HWND DXGetWindow (); + +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK EnumPeripheralsCallback (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK || + GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_KEYBOARD) + { + if (SUCCEEDED(IDirectInput8_CreateDevice(lpDI8, &lpddi->guidInstance, &lpDIDevice[numdevices], + NULL) )) + numdevices++; + } + + return DIENUM_CONTINUE; +} + +////////////////////////////////////////////////////////////////////////////// + +void LoadDefaultPort1A(void) +{ + porttype[0] = 1; + porttype[1] = 0; + + pad[0] = PerPadAdd(&PORTDATA1); + + PerSetKey(DIK_UP, PERPAD_UP, pad[0]); + PerSetKey(DIK_DOWN, PERPAD_DOWN, pad[0]); + PerSetKey(DIK_LEFT, PERPAD_LEFT, pad[0]); + PerSetKey(DIK_RIGHT, PERPAD_RIGHT, pad[0]); + PerSetKey(DIK_K, PERPAD_A, pad[0]); + PerSetKey(DIK_L, PERPAD_B, pad[0]); + PerSetKey(DIK_M, PERPAD_C, pad[0]); + PerSetKey(DIK_U, PERPAD_X, pad[0]); + PerSetKey(DIK_I, PERPAD_Y, pad[0]); + PerSetKey(DIK_O, PERPAD_Z, pad[0]); + PerSetKey(DIK_X, PERPAD_LEFT_TRIGGER, pad[0]); + PerSetKey(DIK_Z, PERPAD_RIGHT_TRIGGER, pad[0]); + PerSetKey(DIK_J, PERPAD_START, pad[0]); +} + +////////////////////////////////////////////////////////////////////////////// + +int PERDXInit(void) +{ + DIPROPDWORD dipdw; + char tempstr[512]; + HRESULT ret; + + memset(pad, 0, sizeof(pad)); + memset(paddevice, 0, sizeof(paddevice)); + + if (FAILED((ret = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + &IID_IDirectInput8, (LPVOID *)&lpDI8, NULL)) )) + { + sprintf(tempstr, "DirectInput8Create error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); + MessageBox (NULL, _16(tempstr), _16("Error"), MB_OK | MB_ICONINFORMATION); + return -1; + } + + IDirectInput8_EnumDevices(lpDI8, DI8DEVCLASS_ALL, EnumPeripheralsCallback, + NULL, DIEDFL_ATTACHEDONLY); + + if (FAILED((ret = IDirectInput8_CreateDevice(lpDI8, &GUID_SysKeyboard, &lpDIDevice[0], + NULL)) )) + { + sprintf(tempstr, "IDirectInput8_CreateDevice error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); + MessageBox (NULL, _16(tempstr), _16("Error"), MB_OK | MB_ICONINFORMATION); + return -1; + } + + if (FAILED((ret = IDirectInputDevice8_SetDataFormat(lpDIDevice[0], &c_dfDIKeyboard)) )) + { + sprintf(tempstr, "IDirectInputDevice8_SetDataFormat error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); + MessageBox (NULL, _16(tempstr), _16("Error"), MB_OK | MB_ICONINFORMATION); + return -1; + } + + if (FAILED((ret = IDirectInputDevice8_SetCooperativeLevel(lpDIDevice[0], DXGetWindow(), + DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY)) )) + { + sprintf(tempstr, "IDirectInputDevice8_SetCooperativeLevel error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); + MessageBox (NULL, _16(tempstr), _16("Error"), MB_OK | MB_ICONINFORMATION); + return -1; + } + + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = 8; // should be enough + + // Setup Buffered input + if (FAILED((ret = IDirectInputDevice8_SetProperty(lpDIDevice[0], DIPROP_BUFFERSIZE, &dipdw.diph)) )) + { + sprintf(tempstr, "IDirectInputDevice8_SetProperty error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); + MessageBox (NULL, _16(tempstr), _16("Error"), MB_OK | MB_ICONINFORMATION); + return -1; + } + + // Make sure Keyboard is acquired already + IDirectInputDevice8_Acquire(lpDIDevice[0]); + + paddevice[0].lpDIDevice = lpDIDevice[0]; + paddevice[0].type = TYPE_KEYBOARD; + paddevice[0].emulatetype = 1; + + PerPortReset(); + LoadDefaultPort1A(); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void StringToGUID(const char *string, GUID *guid) +{ + int data4[8]; + int i; + + sscanf(string, "%08lX-%04hX-%04hX-%02X%02X%02X%02X%02X%02X%02X%02X", (int *)&guid->Data1, (int *)&guid->Data2, (int *)&guid->Data3, &data4[0], &data4[1], &data4[2], &data4[3], &data4[4], &data4[5], &data4[6], &data4[7]); + for (i = 0; i < 8; i++) + guid->Data4[i] = (BYTE)data4[i]; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDXLoadDevices(char *inifilename) +{ + char tempstr[MAX_PATH]; + char string1[20]; + char string2[20]; + GUID guid; + DIDEVCAPS didc; + u32 i; + int j, i2; + int buttonid; + DIPROPDWORD dipdw; + int id; + DWORD coopflags=DISCL_FOREGROUND | DISCL_NONEXCLUSIVE; + BOOL loaddefault=TRUE; + int numpads; + HRESULT hr; + + if (!PERCore) + return; + PerPortReset(); + memset(pad, 0, sizeof(pad)); + + // Check Connection Type + if (GetPrivateProfileStringA("Input", "Port1Type", "", tempstr, MAX_PATH, inifilename) == 0) + { + // Check if it's using the old ini settings for peripherals + if (GetPrivateProfileStringA("Peripheral1", "GUID", "", tempstr, MAX_PATH, inifilename) != 0) + { + // Convert to the newer type of settings + for (i = 0; i < 2; i++) + { + sprintf(string1, "Port%dType", (int)i+1); + WritePrivateProfileStringA("Input", string1, "1", inifilename); + + sprintf(string1, "Peripheral%d", (int)i+1); + sprintf(string2, "Peripheral%dA", (int)i+1); + + if (GetPrivateProfileStringA(string1, "GUID", "", tempstr, MAX_PATH, inifilename)) + WritePrivateProfileStringA(string2, "GUID", tempstr, inifilename); + + if (GetPrivateProfileStringA(string1, "EmulateType", "", tempstr, MAX_PATH, inifilename)) + WritePrivateProfileStringA(string2, "EmulateType", tempstr, inifilename); + + for (i2 = 0; i2 < 13; i2++) + { + if (GetPrivateProfileStringA(string1, PerPadNames[i2], "", tempstr, MAX_PATH, inifilename)) + WritePrivateProfileStringA(string2, PerPadNames[i2], tempstr, inifilename); + } + } + + // Remove old ini entries + for (i = 0; i < 12; i++) + { + sprintf(string1, "Peripheral%d", (int)i+1); + WritePrivateProfileStringA(string1, NULL, NULL, inifilename); + } + + loaddefault = FALSE; + } + } + else + loaddefault = FALSE; + + if (loaddefault) + { + LoadDefaultPort1A(); + return; + } + + // Load new type settings + for (i = 0; i < 2; i++) + { + sprintf(string1, "Port%dType", (int)i+1); + + if (GetPrivateProfileStringA("Input", string1, "", tempstr, MAX_PATH, inifilename) != 0) + { + porttype[i] = atoi(tempstr); + + switch(porttype[i]) + { + case 1: + numpads = 1; + break; + case 2: + numpads = 6; + break; + default: + numpads = 0; + break; + } + + // Load new type settings + for (j = 0; j < numpads; j++) + { + int padindex=(6*i)+j; + padconf_struct *curdevice=&paddevice[padindex]; + sprintf(string1, "Peripheral%d%C", (int)i+1, 'A' + j); + + // Let's first fetch the guid of the device + if (GetPrivateProfileStringA(string1, "GUID", "", tempstr, MAX_PATH, inifilename) == 0) + continue; + + if (GetPrivateProfileStringA(string1, "EmulateType", "0", string2, MAX_PATH, inifilename)) + { + curdevice->emulatetype = atoi(string2); + if (curdevice->emulatetype == 0) + continue; + } + + if (curdevice->lpDIDevice) + { + // Free the default keyboard, etc. + IDirectInputDevice8_Unacquire(curdevice->lpDIDevice); + IDirectInputDevice8_Release(curdevice->lpDIDevice); + } + + StringToGUID(tempstr, &guid); + + // Ok, now that we've got the GUID of the device, let's set it up + if (FAILED(IDirectInput8_CreateDevice(lpDI8, &guid, &lpDIDevice[padindex], + NULL) )) + { + curdevice->lpDIDevice = NULL; + curdevice->emulatetype = 0; + continue; + } + + curdevice->lpDIDevice = lpDIDevice[padindex]; + + didc.dwSize = sizeof(DIDEVCAPS); + + if (FAILED(IDirectInputDevice8_GetCapabilities(lpDIDevice[padindex], &didc) )) + continue; + + if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_KEYBOARD) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevice[padindex], &c_dfDIKeyboard) )) + continue; + curdevice->type = TYPE_KEYBOARD; + coopflags |= DISCL_NOWINKEY; + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_JOYSTICK) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevice[padindex], &c_dfDIJoystick2) )) + continue; + curdevice->type = TYPE_JOYSTICK; + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_MOUSE) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevice[padindex], &c_dfDIMouse2) )) + continue; + curdevice->type = TYPE_MOUSE; + coopflags = DISCL_FOREGROUND | DISCL_EXCLUSIVE; + } + + hr = IDirectInputDevice8_SetCooperativeLevel(lpDIDevice[i], DXGetWindow(), coopflags); + if (FAILED(hr)) + continue; + + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = 8; // should be enough + + // Setup Buffered input + if (FAILED(IDirectInputDevice8_SetProperty(lpDIDevice[padindex], DIPROP_BUFFERSIZE, &dipdw.diph))) + continue; + + IDirectInputDevice8_Acquire(lpDIDevice[padindex]); + + switch(curdevice->emulatetype) + { + case 1: // Standard Pad + id = PERPAD; + break; + case 2: // Analog Pad + case 3: // Stunner + case 5: // Keyboard + id = 0; + break; + case 4: // Mouse + id = PERMOUSE; + break; + default: break; + } + + // Make sure we're added to the smpc list + if (i == 0) + pad[padindex] = PerAddPeripheral(&PORTDATA1, id); + else + pad[padindex] = PerAddPeripheral(&PORTDATA2, id); + + // Now that we're all setup, let's fetch the controls from the ini + if (curdevice->emulatetype != 3 && + curdevice->emulatetype != 4) + { + for (i2 = 0; i2 < 13; i2++) + { + buttonid = GetPrivateProfileIntA(string1, PerPadNames[i2], 0, inifilename); + PerSetKey(buttonid, i2, pad[padindex]); + } + } + else if (curdevice->emulatetype == 4) + { + for (i2 = 0; i2 < 4; i2++) + { + buttonid = GetPrivateProfileIntA(string1, mouse_names[i2], 0, inifilename); + PerSetKey(buttonid, PERMOUSE_LEFT+i2, pad[padindex]); + } + } + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDXDeInit(void) +{ + u32 i; + + for (i = 0; i < numdevices; i++) + { + if (lpDIDevice[i]) + { + IDirectInputDevice8_Unacquire(lpDIDevice[i]); + IDirectInputDevice8_Release(lpDIDevice[i]); + lpDIDevice[i] = NULL; + } + } + + if (lpDI8) + { + IDirectInput8_Release(lpDI8); + lpDI8 = NULL; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PollKeys(void) +{ + u32 i; + DWORD i2; + DWORD size=8; + DIDEVICEOBJECTDATA didod[8]; + HRESULT hr; + + for (i = 0; i < numpads; i++) + { + if (paddevice[i].lpDIDevice == NULL) + continue; + + hr = IDirectInputDevice8_Poll(paddevice[i].lpDIDevice); + + if (FAILED(hr)) + { + if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) + { + // Make sure device is acquired + while(IDirectInputDevice8_Acquire(paddevice[i].lpDIDevice) == DIERR_INPUTLOST) {} + continue; + } + } + + size = 8; + + // Poll events + if (FAILED(IDirectInputDevice8_GetDeviceData(paddevice[i].lpDIDevice, + sizeof(DIDEVICEOBJECTDATA), didod, &size, 0))) + { + if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) + { + // Make sure device is acquired + while(IDirectInputDevice8_Acquire(paddevice[i].lpDIDevice) == DIERR_INPUTLOST) {} + continue; + } + } + + if (size == 0) + continue; + + switch (paddevice[i].type) + { + case TYPE_KEYBOARD: + // This probably could be optimized + for (i2 = 0; i2 < size; i2++) + { + if (didod[i2].dwData & 0x80) + PerKeyDown(didod[i2].dwOfs); + else + PerKeyUp(didod[i2].dwOfs); + } + break; + case TYPE_JOYSTICK: + { + // This probably could be optimized + for (i2 = 0; i2 < size; i2++) + { + // X Axis + if (didod[i2].dwOfs == DIJOFS_X) + { + if (didod[i2].dwData < 0x3FFF) + { + PerKeyDown(PAD_DIR_AXISLEFT); + PerKeyUp(PAD_DIR_AXISRIGHT); + } + else if (didod[i2].dwData > 0xBFFF) + { + PerKeyDown(PAD_DIR_AXISRIGHT); + PerKeyUp(PAD_DIR_AXISLEFT); + } + else + { + PerKeyUp(PAD_DIR_AXISLEFT); + PerKeyUp(PAD_DIR_AXISRIGHT); + } + } + // Y Axis + else if (didod[i2].dwOfs == DIJOFS_Y) + { + if (didod[i2].dwData < 0x3FFF) + { + PerKeyDown(PAD_DIR_AXISUP); + PerKeyUp(PAD_DIR_AXISDOWN); + } + else if (didod[i2].dwData > 0xBFFF) + { + PerKeyDown(PAD_DIR_AXISDOWN); + PerKeyUp(PAD_DIR_AXISUP); + } + else + { + PerKeyUp(PAD_DIR_AXISUP); + PerKeyUp(PAD_DIR_AXISDOWN); + } + } + else if (didod[i2].dwOfs == DIJOFS_POV(0)) + { + // POV Center + if (LOWORD(didod[i2].dwData) == 0xFFFF) + { + PerKeyUp(PAD_DIR_POVUP); + PerKeyUp(PAD_DIR_POVRIGHT); + PerKeyUp(PAD_DIR_POVDOWN); + PerKeyUp(PAD_DIR_POVLEFT); + } + // POV Up + else if (didod[i2].dwData < 4500) + { + PerKeyDown(PAD_DIR_POVUP); + PerKeyUp(PAD_DIR_POVRIGHT); + PerKeyUp(PAD_DIR_POVLEFT); + } + // POV Up-right + else if (didod[i2].dwData < 9000) + { + PerKeyDown(PAD_DIR_POVUP); + PerKeyDown(PAD_DIR_POVRIGHT); + } + // POV Right + else if (didod[i2].dwData < 13500) + { + PerKeyDown(PAD_DIR_POVRIGHT); + PerKeyUp(PAD_DIR_POVDOWN); + PerKeyUp(PAD_DIR_POVUP); + } + // POV Right-down + else if (didod[i2].dwData < 18000) + { + PerKeyDown(PAD_DIR_POVRIGHT); + PerKeyDown(PAD_DIR_POVDOWN); + } + // POV Down + else if (didod[i2].dwData < 22500) + { + PerKeyDown(PAD_DIR_POVDOWN); + PerKeyUp(PAD_DIR_POVLEFT); + PerKeyUp(PAD_DIR_POVRIGHT); + } + // POV Down-left + else if (didod[i2].dwData < 27000) + { + PerKeyDown(PAD_DIR_POVDOWN); + PerKeyDown(PAD_DIR_POVLEFT); + } + // POV Left + else if (didod[i2].dwData < 31500) + { + PerKeyDown(PAD_DIR_POVLEFT); + PerKeyUp(PAD_DIR_POVUP); + PerKeyUp(PAD_DIR_POVDOWN); + } + // POV Left-up + else if (didod[i2].dwData < 36000) + { + PerKeyDown(PAD_DIR_POVLEFT); + PerKeyDown(PAD_DIR_POVUP); + } + } + else if (didod[i2].dwOfs >= DIJOFS_BUTTON(0) && didod[i2].dwOfs <= DIJOFS_BUTTON(127)) + { + if (didod[i2].dwData & 0x80) + PerKeyDown(didod[i2].dwOfs); + else + PerKeyUp(didod[i2].dwOfs); + } + } + break; + } + case TYPE_MOUSE: + for (i2 = 0; i2 < size; i2++) + { + if (didod[i2].dwOfs == DIMOFS_X) + // X Axis + PerMouseMove((PerMouse_struct *)pad[i], (s32)didod[i2].dwData, 0); + else if (didod[i2].dwOfs == DIMOFS_Y) + // Y Axis + PerMouseMove((PerMouse_struct *)pad[i], 0, 0-(s32)didod[i2].dwData); + else if (didod[i2].dwOfs >= DIMOFS_BUTTON0 && didod[i2].dwOfs <= DIMOFS_BUTTON7) + { + // Mouse Buttons + if (didod[i2].dwData & 0x80) + PerKeyDown(didod[i2].dwOfs-DIMOFS_BUTTON0); + else + PerKeyUp(didod[i2].dwOfs-DIMOFS_BUTTON0); + } + } + break; + default: break; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +int PERDXHandleEvents(void) +{ + PollKeys(); + + if (YabauseExec() != 0) + return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK EnumPeripheralsCallbackGamepad (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK || + GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_KEYBOARD) + { + SendMessage((HWND)pvRef, CB_ADDSTRING, 0, (LPARAM)lpddi->tszInstanceName); + memcpy(&GUIDDevice[numguids], &lpddi->guidInstance, sizeof(GUID)); + numguids++; + } + + return DIENUM_CONTINUE; +} + +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK EnumPeripheralsCallbackKeyboard (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_KEYBOARD) + { + SendMessage((HWND)pvRef, CB_ADDSTRING, 0, (LPARAM)lpddi->tszInstanceName); + memcpy(&GUIDDevice[numguids], &lpddi->guidInstance, sizeof(GUID)); + numguids++; + } + + return DIENUM_CONTINUE; +} + +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK EnumPeripheralsCallbackMouse (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_MOUSE) + { + SendMessage((HWND)pvRef, CB_ADDSTRING, 0, (LPARAM)lpddi->tszInstanceName); + memcpy(&GUIDDevice[numguids], &lpddi->guidInstance, sizeof(GUID)); + numguids++; + } + + return DIENUM_CONTINUE; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDXListDevices(HWND control, int emulatetype) +{ + LPDIRECTINPUT8 lpDI8temp = NULL; + + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + &IID_IDirectInput8, (LPVOID *)&lpDI8temp, NULL))) + return; + + numguids = 0; + + SendMessage(control, CB_RESETCONTENT, 0, 0); + SendMessage(control, CB_ADDSTRING, 0, (LPARAM)_16("None")); + + switch(emulatetype) + { + case EMUTYPE_STANDARDPAD: + case EMUTYPE_ANALOGPAD: + IDirectInput8_EnumDevices(lpDI8temp, DI8DEVCLASS_ALL, EnumPeripheralsCallbackGamepad, + (LPVOID)control, DIEDFL_ATTACHEDONLY); + break; + case EMUTYPE_STUNNER: + case EMUTYPE_MOUSE: + IDirectInput8_EnumDevices(lpDI8temp, DI8DEVCLASS_ALL, EnumPeripheralsCallbackMouse, + (LPVOID)control, DIEDFL_ATTACHEDONLY); + break; + case EMUTYPE_KEYBOARD: + IDirectInput8_EnumDevices(lpDI8temp, DI8DEVCLASS_ALL, EnumPeripheralsCallbackKeyboard, + (LPVOID)control, DIEDFL_ATTACHEDONLY); + break; + default: break; + } + + IDirectInput8_Release(lpDI8temp); +} + +////////////////////////////////////////////////////////////////////////////// + +void ConvertKBIDToName(int buttonid, char *string) +{ + memset(string, 0, MAX_PATH); + + // This fixes some strange inconsistencies + if (buttonid == DIK_PAUSE) + buttonid = DIK_NUMLOCK; + else if (buttonid == DIK_NUMLOCK) + buttonid = DIK_PAUSE; + if (buttonid & 0x80) + buttonid += 0x80; + + GetKeyNameTextA(buttonid << 16, string, MAX_PATH); +} + +////////////////////////////////////////////////////////////////////////////// + +void ConvertJoyIDToName(int buttonid, char *string) +{ + switch (buttonid) + { + case 0x00: + sprintf(string, "Axis Left"); + break; + case 0x01: + sprintf(string, "Axis Right"); + break; + case 0x02: + sprintf(string, "Axis Up"); + break; + case 0x03: + sprintf(string, "Axis Down"); + break; + case 0x04: + sprintf(string, "POV Up"); + break; + case 0x05: + sprintf(string, "POV Right"); + break; + case 0x06: + sprintf(string, "POV Down"); + break; + case 0x07: + sprintf(string, "POV Left"); + break; + default: + if (buttonid >= 0x30) + sprintf(string, "Button %d", buttonid - 0x2F); + break; + } + +} + +////////////////////////////////////////////////////////////////////////////// + +void ConvertMouseIDToName(int buttonid, char *string) +{ + sprintf(string, "Button %d", buttonid+1); +} + +////////////////////////////////////////////////////////////////////////////// + +int PERDXInitControlConfig(HWND hWnd, u8 padnum, int *controlmap, const char *inifilename) +{ + char tempstr[MAX_PATH]; + char string1[20]; + GUID guid; + u32 i; + int idlist[] = { IDC_UPTEXT, IDC_RIGHTTEXT, IDC_DOWNTEXT, IDC_LEFTTEXT, + IDC_RTEXT, IDC_LTEXT, IDC_STARTTEXT, + IDC_ATEXT, IDC_BTEXT, IDC_CTEXT, + IDC_XTEXT, IDC_YTEXT, IDC_ZTEXT + }; + + sprintf(string1, "Peripheral%d%C", ((padnum/6)+1), 'A'+(padnum%6)); + + // Let's first fetch the guid of the device and see if we can get a match + if (GetPrivateProfileStringA(string1, "GUID", "", tempstr, MAX_PATH, inifilename) == 0) + { + if (padnum == 0) + { + // Let's use default values + SendDlgItemMessage(hWnd, IDC_DXDEVICECB, CB_SETCURSEL, 1, 0); + + controlmap[0] = DIK_UP; + controlmap[1] = DIK_RIGHT; + controlmap[2] = DIK_DOWN; + controlmap[3] = DIK_LEFT; + controlmap[4] = DIK_Z; + controlmap[5] = DIK_X; + controlmap[6] = DIK_J; + controlmap[7] = DIK_K; + controlmap[8] = DIK_L; + controlmap[9] = DIK_M; + controlmap[10] = DIK_U; + controlmap[11] = DIK_I; + controlmap[12] = DIK_O; + for (i = 0; i < 13; i++) + { + ConvertKBIDToName(controlmap[i], tempstr); + SetDlgItemText(hWnd, idlist[i], _16(tempstr)); + } + } + else + { + SendDlgItemMessage(hWnd, IDC_DXDEVICECB, CB_SETCURSEL, 0, 0); + return -1; + } + } + else + { + LPDIRECTINPUT8 lpDI8temp = NULL; + LPDIRECTINPUTDEVICE8 lpDIDevicetemp; + DIDEVCAPS didc; + int buttonid; + + StringToGUID(tempstr, &guid); + + // Let's find a match + for (i = 0; i < numguids; i++) + { + if (memcmp(&guid, &GUIDDevice[i], sizeof(GUID)) == 0) + { + SendDlgItemMessage(hWnd, IDC_DXDEVICECB, CB_SETCURSEL, i+1, 0); + break; + } + } + + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + &IID_IDirectInput8, (LPVOID *)&lpDI8temp, NULL))) + return -1; + + if (FAILED(IDirectInput8_CreateDevice(lpDI8temp, &GUIDDevice[i], &lpDIDevicetemp, + NULL))) + { + IDirectInput8_Release(lpDI8temp); + return -1; + } + + didc.dwSize = sizeof(DIDEVCAPS); + + if (FAILED(IDirectInputDevice8_GetCapabilities(lpDIDevicetemp, &didc))) + { + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + return -1; + } + + if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_KEYBOARD) + { + sprintf(string1, "Peripheral%d%C", ((padnum/6)+1), 'A'+(padnum%6)); + + for (i = 0; i < 13; i++) + { + buttonid = GetPrivateProfileIntA(string1, PerPadNames[i], 0, inifilename); + printf("%2d: %d\n", i, buttonid); + controlmap[i] = buttonid; + ConvertKBIDToName(buttonid, tempstr); + SetDlgItemText(hWnd, idlist[i], _16(tempstr)); + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_JOYSTICK) + { + sprintf(string1, "Peripheral%d%C", ((padnum/6)+1), 'A'+(padnum%6)); + + for (i = 0; i < 13; i++) + { + buttonid = GetPrivateProfileIntA(string1, PerPadNames[i], 0, inifilename); + controlmap[i] = buttonid; + ConvertJoyIDToName(buttonid, tempstr); + SetDlgItemText(hWnd, idlist[i], _16(tempstr)); + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_MOUSE) + { + for (i = 0; i < 13; i++) + { + buttonid = GetPrivateProfileIntA(string1, PerPadNames[i], 0, inifilename); + controlmap[i] = buttonid; + ConvertMouseIDToName(buttonid, tempstr); + SetDlgItemText(hWnd, idlist[i], _16(tempstr)); + } + } + + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +DIDEVICEOBJECTDATA nextpress; + +int PERDXFetchNextPress(HWND hWnd, u32 guidnum, char *buttonname) +{ + LPDIRECTINPUT8 lpDI8temp = NULL; + LPDIRECTINPUTDEVICE8 lpDIDevicetemp; + DIDEVCAPS didc; + int buttonid=-1; + + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + &IID_IDirectInput8, (LPVOID *)&lpDI8temp, NULL))) + return -1; + + if (FAILED(IDirectInput8_CreateDevice(lpDI8temp, &GUIDDevice[guidnum], &lpDIDevicetemp, + NULL))) + { + IDirectInput8_Release(lpDI8temp); + return -1; + } + + didc.dwSize = sizeof(DIDEVCAPS); + + if (FAILED(IDirectInputDevice8_GetCapabilities(lpDIDevicetemp, &didc))) + { + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + return -1; + } + + if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_KEYBOARD) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevicetemp, &c_dfDIKeyboard))) + { + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + return -1; + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_JOYSTICK) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevicetemp, &c_dfDIJoystick))) + { + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + return -1; + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_MOUSE) + { + if (FAILED(IDirectInputDevice8_SetDataFormat(lpDIDevicetemp, &c_dfDIMouse2))) + { + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + return -1; + } + } + + if (DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_BUTTONCONFIG), hWnd, (DLGPROC)ButtonConfigDlgProc, (LPARAM)lpDIDevicetemp) == TRUE) + { + // Figure out what kind of code to generate + if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_KEYBOARD) + { + memset(buttonname, 0, MAX_PATH); + buttonid = nextpress.dwOfs; + // This fixes some strange inconsistencies + if (buttonid == DIK_PAUSE) + buttonid = DIK_NUMLOCK; + else if (buttonid == DIK_NUMLOCK) + buttonid = DIK_PAUSE; + if (buttonid & 0x80) + buttonid += 0x80; + + GetKeyNameTextA(buttonid << 16, buttonname, MAX_PATH); + buttonid = nextpress.dwOfs; + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_JOYSTICK) + { + if (nextpress.dwOfs == DIJOFS_X) + { + if (nextpress.dwData <= 0x8000) + { + sprintf(buttonname, "Axis Left"); + buttonid = 0x00; + } + else + { + sprintf(buttonname, "Axis Right"); + buttonid = 0x01; + } + } + else if (nextpress.dwOfs == DIJOFS_Y) + { + if (nextpress.dwData <= 0x8000) + { + sprintf(buttonname, "Axis Up"); + buttonid = 0x02; + } + else + { + sprintf(buttonname, "Axis Down"); + buttonid = 0x03; + } + } + else if (nextpress.dwOfs == DIJOFS_POV(0)) + { + if (nextpress.dwData < 9000) + { + sprintf(buttonname, "POV Up"); + buttonid = 0x04; + } + else if (nextpress.dwData < 18000) + { + sprintf(buttonname, "POV Right"); + buttonid = 0x05; + } + else if (nextpress.dwData < 27000) + { + sprintf(buttonname, "POV Down"); + buttonid = 0x06; + } + else + { + sprintf(buttonname, "POV Left"); + buttonid = 0x07; + } + } + else if (nextpress.dwOfs >= DIJOFS_BUTTON(0) && nextpress.dwOfs <= DIJOFS_BUTTON(127)) + { + sprintf(buttonname, "Button %d", (int)(nextpress.dwOfs - 0x2F)); + buttonid = nextpress.dwOfs; + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_MOUSE) + { + buttonid = nextpress.dwOfs-DIMOFS_BUTTON0; + sprintf(buttonname, "Button %d", buttonid+1); + } + } + + IDirectInputDevice8_Unacquire(lpDIDevicetemp); + IDirectInputDevice8_Release(lpDIDevicetemp); + IDirectInput8_Release(lpDI8temp); + + return buttonid; +} + +////////////////////////////////////////////////////////////////////////////// + +HHOOK hook; + +LRESULT CALLBACK KeyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= HC_ACTION) + return TRUE; + + return CallNextHookEx(hook, code, wParam, lParam); +} + +////////////////////////////////////////////////////////////////////////////// + +LRESULT CALLBACK ButtonConfigDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, + LPARAM lParam) +{ + static LPDIRECTINPUTDEVICE8 lpDIDevicetemp; + DIPROPDWORD dipdw; + HRESULT hr; + DWORD size; + DIDEVICEOBJECTDATA didod[8]; + DWORD i; + DIDEVCAPS didc; + + switch (uMsg) + { + case WM_INITDIALOG: + { + lpDIDevicetemp = (LPDIRECTINPUTDEVICE8)lParam; + + if (FAILED(IDirectInputDevice8_SetCooperativeLevel(lpDIDevicetemp, hDlg, + DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY))) + return FALSE; + + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = 8; // should be enough + + // Setup Buffered input + if (FAILED((hr = IDirectInputDevice8_SetProperty(lpDIDevicetemp, DIPROP_BUFFERSIZE, &dipdw.diph)))) + return FALSE; + + if (!SetTimer(hDlg, 1, 100, NULL)) + return FALSE; + + PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_WAITINPUT), TRUE); + hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardHook, GetModuleHandle(NULL), GetCurrentThreadId()); + return TRUE; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_CUSTOMCANCEL: + { + EndDialog(hDlg, FALSE); + return TRUE; + } + default: break; + } + + break; + } + case WM_TIMER: + { + size = 8; + + if (wParam == 1) + { + memset(&didod, 0, sizeof(DIDEVICEOBJECTDATA) * 8); + + // Let's see if there's any data waiting + hr = IDirectInputDevice8_Poll(lpDIDevicetemp); + + if (FAILED(hr)) + { + if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) + { + // Make sure device is acquired + while(IDirectInputDevice8_Acquire(lpDIDevicetemp) == DIERR_INPUTLOST) {} + return TRUE; + } + } + + // Poll events + if (FAILED(IDirectInputDevice8_GetDeviceData(lpDIDevicetemp, + sizeof(DIDEVICEOBJECTDATA), didod, &size, 0))) + { + if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) + { + // Make sure device is acquired + while(IDirectInputDevice8_Acquire(lpDIDevicetemp) == DIERR_INPUTLOST) {} + return TRUE; + } + } + + didc.dwSize = sizeof(DIDEVCAPS); + + if (FAILED(IDirectInputDevice8_GetCapabilities(lpDIDevicetemp, &didc))) + return TRUE; + + if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_KEYBOARD) + { + for (i = 0; i < size; i++) + { + if (didod[i].dwData & 0x80) + { + // We're done. time to bail + EndDialog(hDlg, TRUE); + memcpy(&nextpress, &didod[i], sizeof(DIDEVICEOBJECTDATA)); + break; + } + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_GAMEPAD || + GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_JOYSTICK) + { + for (i = 0; i < size; i++) + { + if (didod[i].dwOfs == 0 || + didod[i].dwOfs == 4) + { + if (didod[i].dwData <= 0x1000 || + didod[i].dwData >= 0xF000) + { + // We're done. time to bail + EndDialog(hDlg, TRUE); + memcpy(&nextpress, &didod[i], sizeof(DIDEVICEOBJECTDATA)); + break; + } + } + else if (didod[i].dwOfs == 0x20) + { + if (((int)didod[i].dwData) >= 0) + { + // We're done. time to bail + EndDialog(hDlg, TRUE); + memcpy(&nextpress, &didod[i], sizeof(DIDEVICEOBJECTDATA)); + } + } + else if (didod[i].dwOfs >= 0x30) + { + if (didod[i].dwData & 0x80) + { + // We're done. time to bail + EndDialog(hDlg, TRUE); + memcpy(&nextpress, &didod[i], sizeof(DIDEVICEOBJECTDATA)); + break; + } + } + } + } + else if (GET_DIDEVICE_TYPE(didc.dwDevType) == DI8DEVTYPE_MOUSE) + { + for (i = 0; i < size; i++) + { + // Make sure it's a button press + if (didod[i].dwOfs >= DIMOFS_BUTTON0 && didod[i].dwOfs <= DIMOFS_BUTTON7) + { + if (didod[i].dwData & 0x80) + { + EndDialog(hDlg, TRUE); + memcpy(&nextpress, &didod[i], sizeof(DIDEVICEOBJECTDATA)); + break; + } + } + } + } + + return TRUE; + } + + return FALSE; + } + case WM_DESTROY: + { + KillTimer(hDlg, 1); + UnhookWindowsHookEx(hook); + break; + } + } + + return FALSE; +} + +////////////////////////////////////////////////////////////////////////////// + +BOOL PERDXWriteGUID(u32 guidnum, u8 padnum, LPCSTR inifilename) +{ + char string1[20]; + char string2[40]; + sprintf(string1, "Peripheral%d%C", ((padnum/6)+1), 'A'+(padnum%6)); + sprintf(string2, "%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X", (int)GUIDDevice[guidnum].Data1, (int)GUIDDevice[guidnum].Data2, (int)GUIDDevice[guidnum].Data3, (int)GUIDDevice[guidnum].Data4[0], (int)GUIDDevice[guidnum].Data4[1], (int)GUIDDevice[guidnum].Data4[2], (int)GUIDDevice[guidnum].Data4[3], (int)GUIDDevice[guidnum].Data4[4], (int)GUIDDevice[guidnum].Data4[5], (int)GUIDDevice[guidnum].Data4[6], (int)GUIDDevice[guidnum].Data4[7]); + return WritePrivateProfileStringA(string1, "GUID", string2, inifilename); +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/yabause/src/perdx.h b/yabause/src/perdx.h new file mode 100644 index 0000000000..245ad7d360 --- /dev/null +++ b/yabause/src/perdx.h @@ -0,0 +1,59 @@ +/* Copyright 2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERDX_H +#define PERDX_H + +#define DIRECTINPUT_VERSION 0x0800 +#include +#include "dx.h" +#include "peripheral.h" + +#define PERCORE_DIRECTX 2 + +extern PerInterface_struct PERDIRECTX; + +extern GUID GUIDDevice[256]; +extern u32 numguids; +extern const char * pad_names[]; +extern PerPad_struct *pad[12]; +extern u32 numpads; +extern int porttype[2]; + +typedef struct +{ + LPDIRECTINPUTDEVICE8 lpDIDevice; + int type; + int emulatetype; +} padconf_struct; + +extern padconf_struct paddevice[12]; + +LRESULT CALLBACK ButtonConfigDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, + LPARAM lParam); + +void KeyStub(PerPad_struct *pad); +void SetupControlUpDown(u8 padnum, u8 controlcode, void (*downfunc)(PerPad_struct *), void (*upfunc)(PerPad_struct *)); + +void PERDXLoadDevices(char *inifilename); +void PERDXListDevices(HWND control, int emulatetype); +int PERDXInitControlConfig(HWND hWnd, u8 padnum, int *controlmap, const char *inifilename); +int PERDXFetchNextPress(HWND hWnd, u32 guidnum, char *buttonname); +BOOL PERDXWriteGUID(u32 guidnum, u8 padnum, LPCSTR inifilename); +#endif diff --git a/yabause/src/peripheral.c b/yabause/src/peripheral.c new file mode 100644 index 0000000000..e10d1fbbed --- /dev/null +++ b/yabause/src/peripheral.c @@ -0,0 +1,724 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "debug.h" +#include "peripheral.h" + +const char * PerPadNames[] = +{ +"Up", +"Right", +"Down", +"Left", +"R", +"L", +"Start", +"A", +"B", +"C", +"X", +"Y", +"Z", +NULL +}; + +const char * PerMouseNames[] = +{ +"A", +"B", +"C", +"Start", +NULL +}; + +PortData_struct PORTDATA1; +PortData_struct PORTDATA2; + +PerInterface_struct * PERCore = NULL; +extern PerInterface_struct * PERCoreList[]; + +typedef struct { + u8 name; + void (*Press)(void *); + void (*Release)(void *); +} PerBaseConfig_struct; + +typedef struct { + u32 key; + PerBaseConfig_struct * base; + void * controller; +} PerConfig_struct; + +#define PERCALLBACK(func) ((void (*) (void *)) func) + +PerBaseConfig_struct perkeybaseconfig[] = { + { PERPAD_UP, PERCALLBACK(PerPadUpPressed), PERCALLBACK(PerPadUpReleased) }, + { PERPAD_RIGHT, PERCALLBACK(PerPadRightPressed), PERCALLBACK(PerPadRightReleased) }, + { PERPAD_DOWN, PERCALLBACK(PerPadDownPressed), PERCALLBACK(PerPadDownReleased) }, + { PERPAD_LEFT, PERCALLBACK(PerPadLeftPressed), PERCALLBACK(PerPadLeftReleased) }, + { PERPAD_RIGHT_TRIGGER, PERCALLBACK(PerPadRTriggerPressed), PERCALLBACK(PerPadRTriggerReleased) }, + { PERPAD_LEFT_TRIGGER, PERCALLBACK(PerPadLTriggerPressed), PERCALLBACK(PerPadLTriggerReleased) }, + { PERPAD_START, PERCALLBACK(PerPadStartPressed), PERCALLBACK(PerPadStartReleased) }, + { PERPAD_A, PERCALLBACK(PerPadAPressed), PERCALLBACK(PerPadAReleased) }, + { PERPAD_B, PERCALLBACK(PerPadBPressed), PERCALLBACK(PerPadBReleased) }, + { PERPAD_C, PERCALLBACK(PerPadCPressed), PERCALLBACK(PerPadCReleased) }, + { PERPAD_X, PERCALLBACK(PerPadXPressed), PERCALLBACK(PerPadXReleased) }, + { PERPAD_Y, PERCALLBACK(PerPadYPressed), PERCALLBACK(PerPadYReleased) }, + { PERPAD_Z, PERCALLBACK(PerPadZPressed), PERCALLBACK(PerPadZReleased) }, +}; + +PerBaseConfig_struct permousebaseconfig[] = { + { PERMOUSE_LEFT, PERCALLBACK(PerMouseLeftPressed), PERCALLBACK(PerMouseLeftReleased) }, + { PERMOUSE_MIDDLE, PERCALLBACK(PerMouseMiddlePressed), PERCALLBACK(PerMouseMiddleReleased) }, + { PERMOUSE_RIGHT, PERCALLBACK(PerMouseRightPressed), PERCALLBACK(PerMouseRightReleased) }, + { PERMOUSE_START, PERCALLBACK(PerMouseStartPressed), PERCALLBACK(PerMouseStartReleased) }, +}; + +static u32 perkeyconfigsize = 0; +static PerConfig_struct * perkeyconfig = NULL; + +static void PerUpdateConfig(PerBaseConfig_struct * baseconfig, int nelems, void * controller); + +////////////////////////////////////////////////////////////////////////////// + +int PerInit(int coreid) { + int i; + + // So which core do we want? + if (coreid == PERCORE_DEFAULT) + coreid = 0; // Assume we want the first one + + // Go through core list and find the id + for (i = 0; PERCoreList[i] != NULL; i++) + { + if (PERCoreList[i]->id == coreid) + { + // Set to current core + PERCore = PERCoreList[i]; + break; + } + } + + if (PERCore == NULL) + return -1; + + if (PERCore->Init() != 0) + return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerDeInit(void) { + if (PERCore) + PERCore->DeInit(); + PERCore = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadUpPressed(PerPad_struct * pad) { + *pad->padbits &= 0xEF; + SMPCLOG("Up\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadUpReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xEF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadDownPressed(PerPad_struct * pad) { + *pad->padbits &= 0xDF; + SMPCLOG("Down\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadDownReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xDF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadRightPressed(PerPad_struct * pad) { + *pad->padbits &= 0x7F; + SMPCLOG("Right\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadRightReleased(PerPad_struct * pad) { + *pad->padbits |= ~0x7F; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadLeftPressed(PerPad_struct * pad) { + *pad->padbits &= 0xBF; + SMPCLOG("Left\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadLeftReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xBF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadStartPressed(PerPad_struct * pad) { + *pad->padbits &= 0xF7; + SMPCLOG("Start\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadStartReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xF7; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadAPressed(PerPad_struct * pad) { + *pad->padbits &= 0xFB; + SMPCLOG("A\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadAReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xFB; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadBPressed(PerPad_struct * pad) { + *pad->padbits &= 0xFE; + SMPCLOG("B\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadBReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xFE; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadCPressed(PerPad_struct * pad) { + *pad->padbits &= 0xFD; + SMPCLOG("C\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadCReleased(PerPad_struct * pad) { + *pad->padbits |= ~0xFD; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadXPressed(PerPad_struct * pad) { + *(pad->padbits + 1) &= 0xBF; + SMPCLOG("X\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadXReleased(PerPad_struct * pad) { + *(pad->padbits + 1) |= ~0xBF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadYPressed(PerPad_struct * pad) { + *(pad->padbits + 1) &= 0xDF; + SMPCLOG("Y\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadYReleased(PerPad_struct * pad) { + *(pad->padbits + 1) |= ~0xDF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadZPressed(PerPad_struct * pad) { + *(pad->padbits + 1) &= 0xEF; + SMPCLOG("Z\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadZReleased(PerPad_struct * pad) { + *(pad->padbits + 1) |= ~0xEF; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadRTriggerPressed(PerPad_struct * pad) { + *(pad->padbits + 1) &= 0x7F; + SMPCLOG("Right Trigger\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadRTriggerReleased(PerPad_struct * pad) { + *(pad->padbits + 1) |= ~0x7F; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadLTriggerPressed(PerPad_struct * pad) { + *(pad->padbits + 1) &= 0xF7; + SMPCLOG("Left Trigger\n"); +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPadLTriggerReleased(PerPad_struct * pad) { + *(pad->padbits + 1) |= ~0xF7; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseLeftPressed(PerMouse_struct * mouse) { + *(mouse->mousebits) |= 1; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseLeftReleased(PerMouse_struct * mouse) { + *(mouse->mousebits) &= 0xFFFE; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseMiddlePressed(PerMouse_struct * mouse) { + *(mouse->mousebits) |= 4; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseMiddleReleased(PerMouse_struct * mouse) { + *(mouse->mousebits) &= 0xFFFB; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseRightPressed(PerMouse_struct * mouse) { + *(mouse->mousebits) |= 2; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseRightReleased(PerMouse_struct * mouse) { + *(mouse->mousebits) &= 0xFFFD; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseStartPressed(PerMouse_struct * mouse) { + *(mouse->mousebits) |= 8; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseStartReleased(PerMouse_struct * mouse) { + *(mouse->mousebits) &= 0xFFF7; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerMouseMove(PerMouse_struct * mouse, s32 dispx, s32 dispy) +{ + int negx, negy, overflowx, overflowy; + u8 diffx, diffy; + + negx = ((mouse->mousebits[0] >> 4) & 1); + negy = ((mouse->mousebits[0] >> 5) & 1); + overflowx = ((mouse->mousebits[0] >> 6) & 1); + overflowy = ((mouse->mousebits[0] >> 7) & 1); + + if (negx) diffx = ~(mouse->mousebits[1]) & 0xFF; + else diffx = mouse->mousebits[1]; + if (negy) diffy = ~(mouse->mousebits[2]) & 0xFF; + else diffy = mouse->mousebits[2]; + + if (dispx > 0) + { + if (negx) + { + if (dispx - diffx > 0) + { + diffx = dispx - diffx; + negx = 0; + } + else diffx -= -dispx; + } + else diffx += dispx; + } + else + { + if (negx) diffx += -dispx; + else + { + if (diffx + dispx > 0) diffx += dispx; + else + { + diffx = -dispx - diffx; + negx = 1; + } + } + } + + if (dispy > 0) + { + if (negy) + { + if (dispy - diffy > 0) + { + diffy = dispy - diffy; + negy = 0; + } + else diffy -= -dispy; + } + else diffy += dispy; + } + else + { + if (negy) diffy += -dispy; + else + { + if (diffy + dispy > 0) diffy += dispy; + else + { + diffy = -dispy - diffy; + negy = 1; + } + } + } + + mouse->mousebits[0] = (overflowy << 7) | (overflowx << 6) | (negy << 5) | (negx << 4) | (mouse->mousebits[0] & 0x0F); + if (negx) mouse->mousebits[1] = ~(diffx); + else mouse->mousebits[1] = diffx; + if (negy) mouse->mousebits[2] = ~(diffy); + else mouse->mousebits[2] = diffy; +} + +////////////////////////////////////////////////////////////////////////////// + +void * PerAddPeripheral(PortData_struct *port, int perid) +{ + int pernum = port->data[0] & 0xF; + int i; + int peroffset=1; + u8 size; + int current = 1; + void * controller; + + if (pernum == 0xF) + return NULL; + + // if only one peripheral is connected use 0xF0, otherwise use 0x00 or 0x10 + if (pernum == 0) + { + pernum = 1; + port->data[0] = 0xF1; + } + else + { + if (pernum == 1) + { + u8 tmp = peroffset; + tmp += (port->data[peroffset] & 0xF) + 1; + + for(i = 0;i < 5;i++) + port->data[tmp + i] = 0xFF; + } + pernum = 6; + port->data[0] = 0x16; + + // figure out where we're at, then add peripheral id + 1 + current = 0; + size = port->data[peroffset] & 0xF; + while ((current < pernum) && (size != 0xF)) + { + peroffset += size + 1; + current++; + size = port->data[peroffset] & 0xF; + } + + if (current == pernum) + { + return NULL; + } + current++; + } + + port->data[peroffset] = perid; + peroffset++; + + // set peripheral data for peripheral to default values and adjust size + // of port data + switch (perid) + { + case PERPAD: + port->data[peroffset] = 0xFF; + port->data[peroffset+1] = 0xFF; + port->size = peroffset+2; + break; + case PERMOUSE: + port->data[peroffset] = 0; + port->data[peroffset + 1] = 0; + port->data[peroffset + 2] = 0; + port->size = peroffset + 3; + break; + default: break; + } + + { + u8 tmp = peroffset; + tmp += (perid & 0xF); + for(i = 0;i < (pernum - current);i++) + { + port->data[tmp + i] = 0xFF; + port->size++; + } + } + + controller = (port->data + (peroffset - 1)); + switch (perid) + { + case PERPAD: + PerUpdateConfig(perkeybaseconfig, 13, controller); + break; + case PERMOUSE: + PerUpdateConfig(permousebaseconfig, 4, controller); + break; + } + return controller; +} + +////////////////////////////////////////////////////////////////////////////// + +int PerGetId(void * peripheral) +{ + u8 * id = peripheral; + return *id; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerRemovePeripheral(UNUSED PortData_struct *port, UNUSED int removeoffset) +{ + // stub +} + +////////////////////////////////////////////////////////////////////////////// + +void PerFlush(PortData_struct * port) +{ + /* FIXME this function only flush data if there's a mouse connected as + * first peripheral */ + u8 perid = port->data[1]; + if (perid == 0xE3) + { + PerMouse_struct * mouse = (PerMouse_struct *) (port->data + 1); + + mouse->mousebits[0] &= 0x0F; + mouse->mousebits[1] = 0; + mouse->mousebits[2] = 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PerKeyDown(u32 key) +{ + unsigned int i = 0; + + while(i < perkeyconfigsize) + { + if (key == perkeyconfig[i].key) + { + perkeyconfig[i].base->Press(perkeyconfig[i].controller); + } + i++; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PerKeyUp(u32 key) +{ + unsigned int i = 0; + + while(i < perkeyconfigsize) + { + if (key == perkeyconfig[i].key) + { + perkeyconfig[i].base->Release(perkeyconfig[i].controller); + } + i++; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PerSetKey(u32 key, u8 name, void * controller) +{ + unsigned int i = 0; + + while(i < perkeyconfigsize) + { + if ((name == perkeyconfig[i].base->name) && (controller == perkeyconfig[i].controller)) + { + perkeyconfig[i].key = key; + } + i++; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void PerPortReset(void) +{ + PORTDATA1.data[0] = 0xF0; + PORTDATA1.size = 1; + PORTDATA2.data[0] = 0xF0; + PORTDATA2.size = 1; + + perkeyconfigsize = 0; + if (perkeyconfig) + free(perkeyconfig); + perkeyconfig = NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +void PerUpdateConfig(PerBaseConfig_struct * baseconfig, int nelems, void * controller) +{ + u32 oldsize = perkeyconfigsize; + u32 i, j; + + perkeyconfigsize += nelems; + perkeyconfig = realloc(perkeyconfig, perkeyconfigsize * sizeof(PerConfig_struct)); + j = 0; + for(i = oldsize;i < perkeyconfigsize;i++) + { + perkeyconfig[i].base = baseconfig + j; + perkeyconfig[i].controller = controller; + j++; + } +} + +////////////////////////////////////////////////////////////////////////////// + +PerPad_struct * PerPadAdd(PortData_struct * port) +{ + return PerAddPeripheral(port, PERPAD); +} + +////////////////////////////////////////////////////////////////////////////// + +PerMouse_struct * PerMouseAdd(PortData_struct * port) +{ + return PerAddPeripheral(port, PERMOUSE); +} + +////////////////////////////////////////////////////////////////////////////// +// Dummy Interface +////////////////////////////////////////////////////////////////////////////// + +int PERDummyInit(void); +void PERDummyDeInit(void); +int PERDummyHandleEvents(void); +void PERDummyNothing(void); + +//static PortData_struct port1; +//static PortData_struct port2; + +u32 PERDummyScan(void); +void PERDummyFlush(void); +void PERDummyKeyName(u32 key, char * name, int size); + +PerInterface_struct PERDummy = { +PERCORE_DUMMY, +"Dummy Input Interface", +PERDummyInit, +PERDummyDeInit, +PERDummyHandleEvents, +PERDummyNothing, +PERDummyScan, +0, +PERDummyFlush +#ifdef PERKEYNAME +,PERDummyKeyName +#endif +}; + +////////////////////////////////////////////////////////////////////////////// + +int PERDummyInit(void) { + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDummyDeInit(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDummyNothing(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +int PERDummyHandleEvents(void) { + if (YabauseExec() != 0) + return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 PERDummyScan(void) { + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDummyFlush(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +void PERDummyKeyName(UNUSED u32 key, char * name, UNUSED int size) { + *name = 0; +} diff --git a/yabause/src/peripheral.h b/yabause/src/peripheral.h new file mode 100644 index 0000000000..e5737c87fd --- /dev/null +++ b/yabause/src/peripheral.h @@ -0,0 +1,221 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERIPHERAL_H +#define PERIPHERAL_H + +#include "core.h" +#include "smpc.h" +#include "yabause.h" + +/** @defgroup peripheral Peripheral + * + * @brief This module provides two kind of functions + * - peripheral core management functions + * - controller ports management functions + * + * @{ + */ + +#define PERPAD 0x02 +#define PERMOUSE 0xE3 + +#define PERCORE_DEFAULT -1 +#define PERCORE_DUMMY 0 + +extern PortData_struct PORTDATA1; +extern PortData_struct PORTDATA2; + +typedef struct +{ + int id; + const char * Name; + int (*Init)(void); + void (*DeInit)(void); + int (*HandleEvents)(void); + void (*PerSetButtonMapping)(void); + u32 (*Scan)(void); + int canScan; + void (*Flush)(void); +#ifdef PERKEYNAME + void (*KeyName)(u32 key, char * name, int size); +#endif +} PerInterface_struct; + +/** @brief Pointer to the current peripheral core. + * + * You should not set this manually but use + * PerInit() and PerDeInit() instead. */ +extern PerInterface_struct * PERCore; + +extern PerInterface_struct PERDummy; + +/** + * @brief Init a peripheral core + * + * Searches through the PERCoreList array for the given coreid. + * If found, PERCore is set to the address of that core and + * the core's Init function is called. + * + * @param coreid the peripheral core to be used + * @return 0 if core has been inited, -1 otherwise + */ +int PerInit(int coreid); +/** + * @brief De-init a peripheral core + * + * Calls the core's DeInit callback and set PERCore to NULL. + */ +void PerDeInit(void); + +/** @brief Adds a peripheral + * + * You shouldn't directly use this function but + * PerPadAdd() or PerMouseAdd() instead. + */ +void * PerAddPeripheral(PortData_struct *port, int perid); +int PerGetId(void * peripheral); +void PerRemovePeripheral(PortData_struct *port, int removeoffset); +void PerPortReset(void); +/** + * Iterate the list of peripherals connected to a port + * and flush them if necesseray. This is needed for mouses. + */ +void PerFlush(PortData_struct * port); + +void PerKeyDown(u32 key); +void PerKeyUp(u32 key); +void PerSetKey(u32 key, u8 name, void * controller); + +/** @defgroup pad Pad + * + * @{ + */ +#define PERPAD_UP 0 +#define PERPAD_RIGHT 1 +#define PERPAD_DOWN 2 +#define PERPAD_LEFT 3 +#define PERPAD_RIGHT_TRIGGER 4 +#define PERPAD_LEFT_TRIGGER 5 +#define PERPAD_START 6 +#define PERPAD_A 7 +#define PERPAD_B 8 +#define PERPAD_C 9 +#define PERPAD_X 10 +#define PERPAD_Y 11 +#define PERPAD_Z 12 + +extern const char * PerPadNames[14]; + +typedef struct +{ + u8 perid; + u8 padbits[2]; +} PerPad_struct; + +/** @brief Adds a pad to one of the controller ports. + * + * @param port can be either &PORTDATA1 or &PORTDATA2 + * @return pointer to a PerPad_struct or NULL if it fails + * */ +PerPad_struct * PerPadAdd(PortData_struct * port); + +void PerPadUpPressed(PerPad_struct * pad); +void PerPadUpReleased(PerPad_struct * pad); + +void PerPadDownPressed(PerPad_struct * pad); +void PerPadDownReleased(PerPad_struct * pad); + +void PerPadRightPressed(PerPad_struct * pad); +void PerPadRightReleased(PerPad_struct * pad); + +void PerPadLeftPressed(PerPad_struct * pad); +void PerPadLeftReleased(PerPad_struct * pad); + +void PerPadStartPressed(PerPad_struct * pad); +void PerPadStartReleased(PerPad_struct * pad); + +void PerPadAPressed(PerPad_struct * pad); +void PerPadAReleased(PerPad_struct * pad); + +void PerPadBPressed(PerPad_struct * pad); +void PerPadBReleased(PerPad_struct * pad); + +void PerPadCPressed(PerPad_struct * pad); +void PerPadCReleased(PerPad_struct * pad); + +void PerPadXPressed(PerPad_struct * pad); +void PerPadXReleased(PerPad_struct * pad); + +void PerPadYPressed(PerPad_struct * pad); +void PerPadYReleased(PerPad_struct * pad); + +void PerPadZPressed(PerPad_struct * pad); +void PerPadZReleased(PerPad_struct * pad); + +void PerPadRTriggerPressed(PerPad_struct * pad); +void PerPadRTriggerReleased(PerPad_struct * pad); + +void PerPadLTriggerPressed(PerPad_struct * pad); +void PerPadLTriggerReleased(PerPad_struct * pad); +/** @} */ + +/** @defgroup mouse Mouse + * + * @{ + * */ +#define PERMOUSE_LEFT 13 +#define PERMOUSE_MIDDLE 14 +#define PERMOUSE_RIGHT 15 +#define PERMOUSE_START 16 + +extern const char * PerMouseNames[5]; + +typedef struct +{ + u8 perid; + u8 mousebits[3]; +} PerMouse_struct; + +/** @brief Adds a mouse to one of the controller ports. + * + * @param port can be either &PORTDATA1 or &PORTDATA2 + * @return pointer to a PerMouse_struct or NULL if it fails + * */ +PerMouse_struct * PerMouseAdd(PortData_struct * port); + +void PerMouseLeftPressed(PerMouse_struct * mouse); +void PerMouseLeftReleased(PerMouse_struct * mouse); + +void PerMouseMiddlePressed(PerMouse_struct * mouse); +void PerMouseMiddleReleased(PerMouse_struct * mouse); + +void PerMouseRightPressed(PerMouse_struct * mouse); +void PerMouseRightReleased(PerMouse_struct * mouse); + +void PerMouseStartPressed(PerMouse_struct * mouse); +void PerMouseStartReleased(PerMouse_struct * mouse); + +void PerMouseMove(PerMouse_struct * mouse, s32 dispx, s32 dispy); +/** @} */ + +/** @} */ + +#endif diff --git a/yabause/src/perlinuxjoy.c b/yabause/src/perlinuxjoy.c new file mode 100644 index 0000000000..cc0c984acc --- /dev/null +++ b/yabause/src/perlinuxjoy.c @@ -0,0 +1,136 @@ +/* Copyright 2009 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "debug.h" +#include "perlinuxjoy.h" +#include +#include +#include +#include + +int PERLinuxJoyInit(void); +void PERLinuxJoyDeInit(void); +int PERLinuxJoyHandleEvents(void); +void PERLinuxJoyNothing(void); +u32 PERLinuxJoyScan(void); +void PERLinuxJoyFlush(void); +void PERLinuxKeyName(u32 key, char * name, int size); + +PerInterface_struct PERLinuxJoy = { +PERCORE_LINUXJOY, +"Linux Joystick Interface", +PERLinuxJoyInit, +PERLinuxJoyDeInit, +PERLinuxJoyHandleEvents, +PERLinuxJoyNothing, +PERLinuxJoyScan, +1, +PERLinuxJoyFlush +#ifdef PERKEYNAME +,PERLinuxKeyName +#endif +}; + +static int hJOY = -1; + +#define PACKEVENT(evt) ((evt.value < 0 ? 0x10000 : 0) | (evt.type << 8) | (evt.number)) + +////////////////////////////////////////////////////////////////////////////// + +int PERLinuxJoyInit(void) +{ + hJOY = open("/dev/input/js0", O_RDONLY | O_NONBLOCK); + + if (hJOY == -1) return -1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERLinuxJoyDeInit(void) +{ + if (hJOY != -1) close(hJOY); +} + +////////////////////////////////////////////////////////////////////////////// + +void PERLinuxJoyNothing(void) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +int PERLinuxJoyHandleEvents(void) +{ + struct js_event evt; + + if (hJOY == -1) return -1; + + while (read(hJOY, &evt, sizeof(struct js_event)) > 0) + { + if (evt.value != 0) + { + PerKeyDown(PACKEVENT(evt)); + } + else + { + PerKeyUp(PACKEVENT(evt)); + PerKeyUp(0x10000 | PACKEVENT(evt)); + } + } + + // execute yabause + if ( YabauseExec() != 0 ) + { + return -1; + } + + // return success + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 PERLinuxJoyScan(void) { + struct js_event evt; + + if (hJOY == -1) return 0; + + if (read(hJOY, &evt, sizeof(struct js_event)) <= 0) return 0; + + return PACKEVENT(evt); +} + +////////////////////////////////////////////////////////////////////////////// + +void PERLinuxJoyFlush(void) { + struct js_event evt; + + if (hJOY == -1) return; + + while (read(hJOY, &evt, sizeof(struct js_event)) > 0); +} + +////////////////////////////////////////////////////////////////////////////// + +void PERLinuxKeyName(u32 key, char * name, UNUSED int size) +{ + sprintf(name, "%x", (int)key); +} diff --git a/yabause/src/perlinuxjoy.h b/yabause/src/perlinuxjoy.h new file mode 100644 index 0000000000..282eacbab4 --- /dev/null +++ b/yabause/src/perlinuxjoy.h @@ -0,0 +1,33 @@ +/* Copyright 2009 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PERLINUXJOY_H +#define PERLINUXJOY_H + +#include "peripheral.h" + +/** @addtogroup peripheral + * @{ */ +#define PERCORE_LINUXJOY 4 + +extern PerInterface_struct PERLinuxJoy; +/** @} */ + +#endif + diff --git a/yabause/src/permacjoy.c b/yabause/src/permacjoy.c new file mode 100644 index 0000000000..3073bdb6db --- /dev/null +++ b/yabause/src/permacjoy.c @@ -0,0 +1,245 @@ +/* Copyright 2009 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "macjoy.h" +#include "permacjoy.h" +#include "debug.h" + +int PERMacJoyInit(void); +void PERMacJoyDeInit(void); +int PERMacJoyHandleEvents(void); +void PERMacJoyNothing(void); + +u32 PERMacJoyScan(void); +void PERMacJoyFlush(void); +void PERMacJoyKeyName(u32 key, char *name, int size); + +PerInterface_struct PERMacJoy = { + PERCORE_MACJOY, + "Mac OS X (IOKit) Joystick Interface", + PERMacJoyInit, + PERMacJoyDeInit, + PERMacJoyHandleEvents, + PERMacJoyNothing, + PERMacJoyScan, + 1, + PERMacJoyFlush +#ifdef PERKEYNAME + ,PERMacJoyKeyName +#endif +}; + +static int initted = 0; +static int joycount = 0; +static joydata_t **joys = NULL; + +#define AXIS_POSITIVE_VALUE 0x800000 +#define AXIS_NEGATIVE_VALUE 0xC00000 +#define HAT_VALUE 0xA00000 + +#define MIDDLE(x, y) (((x) + (y)) / 2) + +int PERMacJoyInit(void) { + int i; + + /* Don't bother trying to init the thing again. */ + if(initted) + return 0; + + /* Grab the number of joysticks connected to the system. */ + joycount = joy_scan_joysticks(); + if(joycount == -1) { + joycount = 0; + return -1; + } + + joys = (joydata_t **)malloc(sizeof(joydata_t *) * joycount); + if(!joys) { + joycount = 0; + return -1; + } + + /* Grab each joystick and open it. */ + for(i = 0; i < joycount; ++i) { + joys[i] = joy_get_joystick(i); + + if(joys[i] == NULL) + continue; + + if(!joy_open_joystick(joys[i])) { + joys[i] = NULL; + continue; + } + } + + initted = 1; + + return 0; +} + +void PERMacJoyDeInit(void) { + int i; + + if(!initted) + return; + + /* Close each joystick. */ + for(i = 0; i < joycount; ++i) { + joy_close_joystick(joys[i]); + } + + free(joys); + joys = NULL; + joycount = 0; + initted = 0; +} + +int PERMacJoyHandleEvents(void) { + int i, j, k, data; + joydata_t *joy; + + /* Check each joystick. */ + for(i = 0; i < joycount; ++i) { + joy = joys[i]; + + if(!joy) { + continue; + } + + /* Handle each axis. */ + for(j = 0; j < joy->axes_count; ++j) { + int midpoint = MIDDLE(joy->axes[j].min, joy->axes[j].max); + + data = joy_read_axis(joy, j); + + if(joy->axes[j].max > 0 && + data > MIDDLE(midpoint, joy->axes[j].max)) { + PerKeyDown((i << 24) | AXIS_POSITIVE_VALUE | j); + PerKeyUp((i << 24) | AXIS_NEGATIVE_VALUE | j); + } + else if(joy->axes[j].min < 0 && + data < MIDDLE(midpoint, joy->axes[j].min)) { + PerKeyUp((i << 24) | AXIS_POSITIVE_VALUE | j); + PerKeyDown((i << 24) | AXIS_NEGATIVE_VALUE | j); + } + else { + PerKeyUp((i << 24) | AXIS_POSITIVE_VALUE | j); + PerKeyUp((i << 24) | AXIS_NEGATIVE_VALUE | j); + } + } + + /* Handle each button. */ + for(j = 1; j <= joy->buttons_count; ++j) { + data = joy_read_button(joy, j); + + if(data > joy->buttons[j].min) { + PerKeyDown((i << 24) | j); + } + else { + PerKeyUp((i << 24) | j); + } + } + + /* Handle any hats. */ + for(j = 0; j < joy->hats_count; ++j) { + data = joy_read_element(joy, joy->hats + j); + + for(k = joy->hats[j].min; k < joy->hats[j].max; ++k) { + if(data == k) { + PerKeyDown((i << 24) | HAT_VALUE | (k << 8) | j); + } + else { + PerKeyUp((i << 24) | HAT_VALUE | (k << 8) | j); + } + } + } + } + + if(YabauseExec() != 0) { + return -1; + } + + return 0; +} + +void PERMacJoyNothing(void) { + /* Nothing. */ +} + +u32 PERMacJoyScan(void) { + int i, j, k, data; + joydata_t *joy; + + /* Check each joystick. */ + for(i = 0; i < joycount; ++i) { + joy = joys[i]; + + if(!joy) { + continue; + } + + /* Handle each axis. */ + for(j = 0; j < joy->axes_count; ++j) { + int midpoint = MIDDLE(joy->axes[j].min, joy->axes[j].max); + + data = joy_read_axis(joy, j); + + if(joy->axes[j].max > 0 && + data > MIDDLE(midpoint, joy->axes[j].max)) { + return ((i << 24) | AXIS_POSITIVE_VALUE | j); + } + else if(joy->axes[j].min < 0 && + data < MIDDLE(midpoint, joy->axes[j].min)) { + return ((i << 24) | AXIS_NEGATIVE_VALUE | j); + } + } + + /* Handle each button. */ + for(j = 1; j <= joy->buttons_count; ++j) { + data = joy_read_button(joy, j); + + if(data > joy->buttons[j].min) { + return ((i << 24) | j); + } + } + + /* Handle any hats. */ + for(j = 0; j < joy->hats_count; ++j) { + data = joy_read_element(joy, joy->hats + j); + + for(k = joy->hats[j].min; k < joy->hats[j].max; ++k) { + if(data == k) { + return ((i << 24) | HAT_VALUE | (k << 8) | j); + } + } + } + } + + return 0; +} + +void PERMacJoyFlush(void) { + /* Nothing. */ +} + +void PERMacJoyKeyName(u32 key, char *name, int size) { + snprintf(name, size, "%x", (unsigned int)key); +} diff --git a/yabause/src/permacjoy.h b/yabause/src/permacjoy.h new file mode 100644 index 0000000000..91e11591fd --- /dev/null +++ b/yabause/src/permacjoy.h @@ -0,0 +1,29 @@ +/* Copyright 2009 Lawrence Sebald + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERMACJOY_H +#define PERMACJOY_H + +#include "peripheral.h" + +#define PERCORE_MACJOY 5 + +extern PerInterface_struct PERMacJoy; + +#endif /* !PERMACJOY_H */ diff --git a/yabause/src/persdljoy.c b/yabause/src/persdljoy.c new file mode 100644 index 0000000000..701fdfe778 --- /dev/null +++ b/yabause/src/persdljoy.c @@ -0,0 +1,279 @@ +/* Copyright 2005 Guillaume Duhamel + Copyright 2005-2006 Theo Berkau + Copyright 2008 Filipe Azevedo + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef HAVE_LIBSDL +#ifdef __APPLE__ + #include +#else + #include "SDL.h" +#endif + +#include "debug.h" +#include "persdljoy.h" + +#define SDL_MAX_AXIS_VALUE 0x110000 +#define SDL_MIN_AXIS_VALUE 0x100000 +#define SDL_MEDIUM_AXIS_VALUE (int)(32768 / 2) +#define SDL_BUTTON_PRESSED 1 +#define SDL_BUTTON_RELEASED 0 + +int PERSDLJoyInit(void); +void PERSDLJoyDeInit(void); +int PERSDLJoyHandleEvents(void); +void PERSDLJoyNothing(void); + +u32 PERSDLJoyScan(void); +void PERSDLJoyFlush(void); +void PERSDLKeyName(u32 key, char * name, int size); + +PerInterface_struct PERSDLJoy = { +PERCORE_SDLJOY, +"SDL Joystick Interface", +PERSDLJoyInit, +PERSDLJoyDeInit, +PERSDLJoyHandleEvents, +PERSDLJoyNothing, +PERSDLJoyScan, +1, +PERSDLJoyFlush +#ifdef PERKEYNAME +,PERSDLKeyName +#endif +}; + +typedef struct { + SDL_Joystick* mJoystick; + s16* mScanStatus; +} PERSDLJoystick; + +unsigned int SDL_PERCORE_INITIALIZED = 0; +unsigned int SDL_PERCORE_JOYSTICKS_INITIALIZED = 0; +PERSDLJoystick* SDL_PERCORE_JOYSTICKS = 0; + +////////////////////////////////////////////////////////////////////////////// + +int PERSDLJoyInit(void) { + int i, j; + + // does not need init if already done + if ( SDL_PERCORE_INITIALIZED ) + { + return 0; + } + + // init joysticks + if ( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) == -1 ) + { + return -1; + } + + // ignore joysticks event in sdl event loop + SDL_JoystickEventState( SDL_IGNORE ); + + // open joysticks + SDL_PERCORE_JOYSTICKS_INITIALIZED = SDL_NumJoysticks(); + SDL_PERCORE_JOYSTICKS = malloc(sizeof(PERSDLJoystick) * SDL_PERCORE_JOYSTICKS_INITIALIZED); + for ( i = 0; i < SDL_PERCORE_JOYSTICKS_INITIALIZED; i++ ) + { + SDL_Joystick* joy = SDL_JoystickOpen( i ); + + SDL_JoystickUpdate(); + + SDL_PERCORE_JOYSTICKS[ i ].mJoystick = joy; + SDL_PERCORE_JOYSTICKS[ i ].mScanStatus = joy ? malloc(sizeof(s16) * SDL_JoystickNumAxes( joy )) : 0; + + if ( joy ) + { + for ( j = 0; j < SDL_JoystickNumAxes( joy ); j++ ) + { + SDL_PERCORE_JOYSTICKS[ i ].mScanStatus[ j ] = SDL_JoystickGetAxis( joy, j ); + } + } + } + + // success + SDL_PERCORE_INITIALIZED = 1; + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +void PERSDLJoyDeInit(void) { + // close joysticks + if ( SDL_PERCORE_INITIALIZED == 1 ) + { + int i; + for ( i = 0; i < SDL_PERCORE_JOYSTICKS_INITIALIZED; i++ ) + { + if ( SDL_JoystickOpened( i ) ) + { + SDL_JoystickClose( SDL_PERCORE_JOYSTICKS[ i ].mJoystick ); + free( SDL_PERCORE_JOYSTICKS[ i ].mScanStatus ); + } + } + free( SDL_PERCORE_JOYSTICKS ); + } + + SDL_PERCORE_JOYSTICKS_INITIALIZED = 0; + SDL_PERCORE_INITIALIZED = 0; + + // close sdl joysticks + SDL_QuitSubSystem( SDL_INIT_JOYSTICK ); +} + +////////////////////////////////////////////////////////////////////////////// + +void PERSDLJoyNothing(void) { +} + +////////////////////////////////////////////////////////////////////////////// + +int PERSDLJoyHandleEvents(void) { + int joyId; + int i; + SDL_Joystick* joy; + Sint16 cur; + Uint8 buttonState; + + // update joysticks states + SDL_JoystickUpdate(); + + // check each joysticks + for ( joyId = 0; joyId < SDL_PERCORE_JOYSTICKS_INITIALIZED; joyId++ ) + { + joy = SDL_PERCORE_JOYSTICKS[ joyId ].mJoystick; + + if ( !joy ) + { + continue; + } + + // check axis + for ( i = 0; i < SDL_JoystickNumAxes( joy ); i++ ) + { + cur = SDL_JoystickGetAxis( joy, i ); + + if ( cur < -SDL_MEDIUM_AXIS_VALUE ) + { + PerKeyUp( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); + PerKeyDown( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); + } + else if ( cur > SDL_MEDIUM_AXIS_VALUE ) + { + PerKeyUp( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); + PerKeyDown( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); + } + else + { + PerKeyUp( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); + PerKeyUp( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); + } + } + + // check buttons + for ( i = 0; i < SDL_JoystickNumButtons( joy ); i++ ) + { + buttonState = SDL_JoystickGetButton( joy, i ); + + if ( buttonState == SDL_BUTTON_PRESSED ) + { + PerKeyDown( (joyId << 18) | (i +1) ); + } + else if ( buttonState == SDL_BUTTON_RELEASED ) + { + PerKeyUp( (joyId << 18) | (i +1) ); + } + } + } + + // execute yabause + if ( YabauseExec() != 0 ) + { + return -1; + } + + // return success + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +u32 PERSDLJoyScan( void ) { + // init vars + int joyId; + int i; + SDL_Joystick* joy; + Sint16 cur; + + // update joysticks states + SDL_JoystickUpdate(); + + // check each joysticks + for ( joyId = 0; joyId < SDL_PERCORE_JOYSTICKS_INITIALIZED; joyId++ ) + { + joy = SDL_PERCORE_JOYSTICKS[ joyId ].mJoystick; + + if ( !joy ) + { + continue; + } + + // check axis + for ( i = 0; i < SDL_JoystickNumAxes( joy ); i++ ) + { + cur = SDL_JoystickGetAxis( joy, i ); + + if ( cur != SDL_PERCORE_JOYSTICKS[ joyId ].mScanStatus[ i ] ) + { + if ( cur < -SDL_MEDIUM_AXIS_VALUE ) + { + return (joyId << 18) | SDL_MIN_AXIS_VALUE | i; + } + else if ( cur > SDL_MEDIUM_AXIS_VALUE ) + { + return (joyId << 18) | SDL_MAX_AXIS_VALUE | i; + } + } + } + + // check buttons + for ( i = 0; i < SDL_JoystickNumButtons( joy ); i++ ) + { + if ( SDL_JoystickGetButton( joy, i ) == SDL_BUTTON_PRESSED ) + { + return (joyId << 18) | (i +1); + break; + } + } + } + + return 0; +} + +void PERSDLJoyFlush(void) { +} + +void PERSDLKeyName(u32 key, char * name, UNUSED int size) +{ + sprintf(name, "%x", (int)key); +} + +#endif diff --git a/yabause/src/persdljoy.h b/yabause/src/persdljoy.h new file mode 100644 index 0000000000..0a4aecc336 --- /dev/null +++ b/yabause/src/persdljoy.h @@ -0,0 +1,32 @@ +/* Copyright 2006 Guillaume Duhamel + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PERSDLJOY_H +#define PERSDLJOY_H + +#include "peripheral.h" + +/** @addtogroup peripheral + * @{ */ +#define PERCORE_SDLJOY 3 + +extern PerInterface_struct PERSDLJoy; +/** @} */ + +#endif diff --git a/yabause/src/profile.c b/yabause/src/profile.c new file mode 100644 index 0000000000..6b67e99ba6 --- /dev/null +++ b/yabause/src/profile.c @@ -0,0 +1,188 @@ +/* copyright Patrick Kooman, 2002 + + Lightweight C & C++ profiler. Works both in + debug- and release mode. For more info, read: + + http://www.2dgame-tutorial.com/sdl/profile.htm + + You are free to use / modify / re-distribute this code. + + */ +#if !defined(SYS_PROFILE_H) && !defined(DONT_PROFILE) + +#include "profile.h" + +/* The time when the profiler initializes */ +clock_t g_init_time ; +/* Entries */ +entry_t g_tag [NUM_TAGS] ; +/* "high-water-mark" */ +int g_i_hwm = 0 ; +/* Is ProfileInit called? */ +int g_init = 0 ; + +/* Looks up given tag and returns the name, + of 0 if not found. */ +static entry_t* LookupTag (char* str_tag) { + int i ; + for (i = 0; i < g_i_hwm; ++i) { + if (strcmp (g_tag [i].str_name, str_tag) == 0) { + return &g_tag [i] ; + } + } + return 0 ; +} + +/* Checks whether the given tag is already started (nesting). + This is true when an entry with the given name is found, + for which the start_time_t is not 0. */ +static int Nested (char* str_tag) { + int i ; + for (i = 0; i < g_i_hwm; ++i) { + if (strcmp (g_tag [i].str_name, str_tag) == 0 && g_tag [i].start_time > -1) { + /* Already 'running': nested*/ + return 1 ; + } + } + /* Not running: not nested */ + return 0 ; +} + +/* Adds the given tag and return the entry it is stored into */ +static entry_t* AddTag (char* str_tag) { + if (g_i_hwm + 1 == NUM_TAGS) { + /* Full */ + return 0 ; + } + /* Copy the name */ + strcpy (g_tag [g_i_hwm].str_name, str_tag) ; + g_tag [g_i_hwm].start_time = -1 ; + /* Increase the high-water-mark but return the current index */ + return &g_tag [g_i_hwm++] ; +} + +/* Compare function for 'qsort' */ +static int CompareEntries (const void* p_1, const void* p_2) { + entry_t* p_entry1, *p_entry2 ; + /* Cast elements to entry_t type */ + p_entry1 = (entry_t*) p_1 ; + p_entry2 = (entry_t*) p_2 ; + /* Compare */ + return p_entry2->l_total_ms - p_entry1->l_total_ms ; +} + +/* Called on the first start-call. It receives the start-time */ +static void Init (void) { + memset (g_tag, 0, sizeof (g_tag)) ; + /* Retreive the time */ + g_init_time = clock () ; + /* Flag that this function has been called */ + g_init = 1 ; + g_i_hwm = 0 ; +} + +/* Prints profiling statistice to stdout, +sorted by percentage (descending) */ +void ProfilePrint (void) { + int i ; + long l_prof_time ; + if (g_i_hwm == 0) { + fprintf (stdout, "ProfilePrint: nothing to print.\n") ; + return ; + } + /* Retreive the time */ + l_prof_time = clock () - g_init_time ; + if (l_prof_time == 0) { + /* Avoid division by 0 */ + fprintf (stdout, "Warning: nothing to show because timer ran for less than 1 clock-tick.") ; + } + /* Print warnings for tags which are not stopped. */ + for (i = 0; i < g_i_hwm; ++i) { + if (g_tag [i].i_stopped == 0) { + g_tag [i].l_total_ms += clock () - g_tag [i].start_time ; + fprintf (stdout, "Warning: \"%s\" started but not stopped. (Done now, but result may be over-expensive!)\n", g_tag [i].str_name) ; + } + } + /* Sort the array desending */ + qsort (&g_tag, g_i_hwm, sizeof (entry_t), CompareEntries) ; + fprintf (stdout, "Profiler results (descending by percentage):\n\n") ; + for (i = 0; i < g_i_hwm; ++i) { + /* Print statistics */ + fprintf (stdout, "< calls: %2d, total ms: %3d, percentage: %3.1f%% > - \"%s\"\n", + g_tag [i].i_calls, + (int) ((double) g_tag [i].l_total_ms / CLOCKS_PER_SEC * 1000), + (double) g_tag [i].l_total_ms / l_prof_time * 100, + g_tag [i].str_name) ; + } +} + +/* Starts timer for given tag. If it does not exist yet, + it is added. + + Note: 1. The tag may not be nested with the same name + 2. The tag may not equal "" */ +void ProfileStart (char* str_tag) { + entry_t* p_entry ; + /* One the first call, we must initialize the profiler. */ + if (!g_init) { + Init () ; + } + /* Test for "" */ + if (*str_tag == '\0') { + fprintf (stdout, "ERROR in ProfileStart: a tag may not be \"\". Call is denied.") ; + return ; + } + /* Search the entry with the given name */ + p_entry = LookupTag (str_tag) ; + if (!p_entry) { + /* New tag, add it*/ + p_entry = AddTag (str_tag) ; + if (!p_entry) { + fprintf (stdout, "WARNING in ProfileStart: no more space to store the tag (\"%s\"). Increase NUM_TAGS in \"profile.h\". Call is denied.\n", str_tag) ; + return ; + } + } + /* Check for nesting of equal tag.*/ + if (Nested (str_tag)) { + fprintf (stdout, "ERROR in ProfileStart: nesting of equal tags not allowed (\"%s\"). Call is denied.\n", str_tag) ; + return ; + } + /* Increase the number of hits */ + ++p_entry->i_calls ; + /* Set the start time */ + p_entry->start_time = clock () ; + p_entry->i_stopped = 0 ; +} + +/* Stops timer for given tag. Checks for existence. + Adds the time between now and the Start call to the + total time.*/ +void ProfileStop (char* str_tag) { + clock_t end_time ; + entry_t* p_entry ; + /* Test for "" */ + if (*str_tag == '\0') { + fprintf (stdout, "ERROR in ProfileStop: a tag may not be \"\". Call is denied.") ; + return ; + } + /* Check for a existing name */ + p_entry = LookupTag (str_tag) ; + if (!p_entry) { + fprintf (stdout, "WARNING in ProfileStop: tag \"%s\" was never started. Call is denied.\n", str_tag) ; + return ; + } + /* Get the time */ + end_time = clock () ; + p_entry->l_total_ms += end_time - p_entry->start_time ; + /* Reset */ + p_entry->start_time = -1 ; + p_entry->i_stopped = 1 ; +} + +/* Resets the profiler. */ +void ProfileReset (void) { + Init () ; +} + +#endif /* !SYS_PROFILE_H && !DONT_PROFILE */ + diff --git a/yabause/src/profile.h b/yabause/src/profile.h new file mode 100644 index 0000000000..c89529f39c --- /dev/null +++ b/yabause/src/profile.h @@ -0,0 +1,65 @@ +/* copyright Patrick Kooman, 2002 + + Lightweight C & C++ profiler. Works both in + debug- and release mode. For more info, read: + + http://www.2dgame-tutorial.com/sdl/profile.htm + + You are free to use / modify / re-distribute this code. + + */ +#ifndef _PROFILE_H_ +#define _PROFILE_H_ + +#include +#include +#include +#include +#include + +#ifdef DONT_PROFILE +/* Profiling disabled: compiler won't generate machine instructions now. */ +#define PROFILE_START(t) +#define PROFILE_STOP(t) +#define PROFILE_PRINT() +#define PROFILE_RESET() +#else +/* Profiling enabled */ +#define MAX_TAG_LEN 100 +#define NUM_TAGS 100 + +typedef struct { + char str_name [MAX_TAG_LEN] ; + int i_calls ; + clock_t start_time ; + int i_stopped ; + long l_total_ms ; +} entry_t ; + +/* Compiler calls functions now. */ +#define PROFILE_START(t) ProfileStart (t) +#define PROFILE_STOP(t) ProfileStop (t) +#define PROFILE_PRINT() ProfilePrint () +#define PROFILE_RESET() ProfileReset () + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Start timer for given tag */ +void ProfileStart (char* str_tag) ; +/* Stops timer for given tag and add time to total time for this tag */ +void ProfileStop (char* str_tag) ; +/* Prints result to stdout */ +void ProfilePrint (void) ; +/* Resets the profiler. */ +void ProfileReset (void) ; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NO_PROFILE */ + +#endif /* _PROFILE_H_ */ + diff --git a/yabause/src/psp/Makefile.am b/yabause/src/psp/Makefile.am new file mode 100644 index 0000000000..5d95fbd291 --- /dev/null +++ b/yabause/src/psp/Makefile.am @@ -0,0 +1,82 @@ +EXTRA_DIST = icache-funcs-2450.patch me-sectstart.c me-sectend.c \ + psp-logo.png sh2-core.i +CLEANFILES = me.elf me.prx me-exp.c me-test.elf me-test.prx melib.S \ + yabause.elf yabause.prx yabause.sfo EBOOT.PBP + +# yabause.{elf,prx} are only intermediate targets; the final target is +# EBOOT.PBP (but we keep the ELF/PRX files around for testing purposes). +if BUILD_ME_TEST +me_test=me-test.prx +else +me_test= +endif +all-local: EBOOT.PBP me.prx $(me_test) +.SECONDARY: yabause.elf yabause.prx me.elf me-test.elf + +######## + +noinst_PROGRAMS = yabause.elf me.elf me-test.elf + +yabause_elf_SOURCES = config.c control.c display.c filesel.c font.c gu.c \ + init.c localtime.c main.c menu.c misc.c osk.c profile.c \ + psp-cd.c psp-m68k.c psp-per.c psp-sh2.c psp-sound.c \ + psp-video.c psp-video-bitmap.c psp-video-rotate.c \ + psp-video-tilemap.c psp-video-tweaks.c \ + rtl.c rtl-mips.c rtlexec.c rtlinsn.c rtlopt.c rtlunit.c \ + satopt-sh2.c \ + sh2.c sh2-interpret.c sh2-opcodeinfo.c sh2-optimize.c \ + sys.c texcache.c threads.c timing.c yui.c \ + me-utility.c \ + \ + common.h config.h control.h display.h filesel.h font.h \ + gu.h init.h localtime.h menu.h misc.h osk.h profile.h \ + psp-cd.h psp-m68k.h psp-per.h psp-sh2.h psp-sound.h \ + psp-video.h psp-video-internal.h \ + rtl.h rtl-internal.h rtl-mips.h \ + satopt-sh2.h \ + sh2.h sh2-internal.h \ + sys.h texcache.h timing.h \ + me-utility.h +yabause_elf_DEPENDENCIES = me-sectstart.$(OBJEXT) me-sectend.$(OBJEXT) +nodist_yabause_elf_SOURCES = melib.S +yabause_elf_CFLAGS = $(YAB_CFLAGS) -Wpointer-arith -Wshadow +yabause_elf_LDADD = ../libyabause.a $(YAB_LIBS) -lpspsdk +yabause.elf: $(yabause_elf_OBJECTS) $(yabause_elf_DEPENDENCIES) ../libyabause.a + $(yabause_elf_LINK) \ + me-sectstart.$(OBJEXT) \ + $(yabause_elf_OBJECTS) \ + $(yabause_elf_LDADD) \ + $(LIBS) \ + me-sectend.$(OBJEXT) + +me_elf_SOURCES = me.c me.h +nodist_me_elf_SOURCES = me-exp.c +me_elf_CFLAGS = -Wpointer-arith -Wshadow +me_elf_LDFLAGS = -nostartfiles +me_elf_LIBS = -lpspkernel +me.elf: $(me_elf_OBJECTS) + $(me_elf_LINK) $(me_elf_OBJECTS) $(me_elf_LDADD) $(me_elf_LIBS) + +me_test_elf_SOURCES = me-test.c me-utility.c me-utility.h +nodist_me_test_elf_SOURCES = melib.S +me_test_elf_CFLAGS = -Wpointer-arith -Wshadow +me_test_elf_LIBS = -lc -lpspuser +me-test.elf: $(me_test_elf_OBJECTS) + $(me_test_elf_LINK) $(me_test_elf_OBJECTS) $(me_test_elf_LDADD) $(me_test_elf_LIBS) + +######## + +.elf.prx: + psp-fixup-imports $< + psp-prxgen $< $@ + +me-exp.c: me.exp + psp-build-exports -b $< >$@ +melib.S: me.exp + psp-build-exports -s $< + +yabause.sfo: + mksfoex -d MEMSIZE=1 "Yabause $(PACKAGE_VERSION)" $@ + +EBOOT.PBP: psp-logo.png yabause.prx yabause.sfo + pack-pbp $@ yabause.sfo $< NULL NULL NULL NULL yabause.prx NULL diff --git a/yabause/src/psp/common.h b/yabause/src/psp/common.h new file mode 100644 index 0000000000..c3ad5886d0 --- /dev/null +++ b/yabause/src/psp/common.h @@ -0,0 +1,258 @@ +/* src/psp/common.h: Common header for PSP source files + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_COMMON_H +#define PSP_COMMON_H + +/**************************************************************************/ + +/* Various system headers */ + +#include +#include +#include +#define abs builtin_abs // Avoid shadowing warnings for common identifiers +#define div builtin_div +#include +#undef abs +#undef div +#define index builtin_index +#include +#undef index +#define remainder builtin_remainder +#define y0 builtin_y0 +#define y1 builtin_y1 +#include +#undef remainder +#undef y0 +#undef y1 + + +#define u8 pspsdk_u8 // Avoid type collisions with ../core.h +#define s8 pspsdk_s8 +#define u16 pspsdk_u16 +#define s16 pspsdk_s16 +#define u32 pspsdk_u32 +#define s32 pspsdk_s32 +#define u64 pspsdk_u64 +#define s64 pspsdk_s64 + +#ifdef PSP +# include +# include +# include +# include +# include +# include +# include +# include +# define PSP_SYSTEMPARAM_ID_INT_X_IS_CONFIRM 9 // Presumably, anyway +#endif + +#undef u8 +#undef s8 +#undef u16 +#undef s16 +#undef u32 +#undef s32 +#undef u64 +#undef s64 + +/* Helpful hints for GCC */ +#if defined(__GNUC__) && defined(PSP) +extern void sceKernelExitGame(void) __attribute__((noreturn)); +extern int sceKernelExitThread(int status) __attribute__((noreturn)); +extern int sceKernelExitDeleteThread(int status) __attribute__((noreturn)); +#endif + +/**************************************************************************/ + +/* Thread priority constants */ +enum { + THREADPRI_MAIN = 32, + THREADPRI_CD_READ = 25, + THREADPRI_UTILITY = 21, + THREADPRI_SOUND = 20, + THREADPRI_SYSTEM_CB = 15, +}; + +/*----------------------------------*/ + +/* Program directory (determined from argv[0]) */ +extern char progpath[256]; + +/* Saturn control pad handle (set at initialization time, and used by menu + * code to change button assignments) */ +extern void *padbits; + +/* Flag indicating whether the ME is available for use */ +extern int me_available; + +/* Have we successfully initialized the Yabause core? */ +extern int yabause_initted; + +/**************************************************************************/ + +/* Convenience macros (not PSP-related, except DSTART/DEND) */ + +/*----------------------------------*/ + +/* Get the length of an array */ +#define lenof(a) (sizeof((a)) / sizeof((a)[0])) +/* Bound a value between two limits (inclusive) */ +#define bound(x,low,high) __extension__({ \ + typeof(x) __x = (x); \ + typeof(low) __low = (low); \ + typeof(high) __high = (high); \ + __x < __low ? __low : __x > __high ? __high : __x; \ +}) + +/* Get offset of a structure member */ +#undef offsetof +#ifdef __GNUC__ +# define offsetof(type,member) __builtin_offsetof(type,member) +#else +# define offsetof(type,member) ((uintptr_t)&(((type *)0)->member)) +#endif + +/* Declare a function to be constant (i.e. not touching memory) */ +#undef CONST_FUNCTION +#ifdef __GNUC__ +# define CONST_FUNCTION __attribute__((const)) +#else +# define CONST_FUNCTION /*nothing*/ +#endif + +/* Force a function to be inlined if possible (use in place of "inline") */ +#ifdef __GNUC__ +# define ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define ALWAYS_INLINE inline +#endif + +/* Prevent a function from being inlined */ +#ifdef __GNUC__ +# define NOINLINE __attribute__((noinline)) +#else +# define NOINLINE /*nothing*/ +#endif + +/*----------------------------------*/ + +/* Convert a float to an int (optimized for PSP, but with alternate + * versions for testing on other systems) */ + +#ifdef PSP + +#define DEFINE_IFUNC(name,insn) \ +static inline CONST_FUNCTION int32_t name(const float x) { \ + float dummy; \ + int32_t result; \ + asm(insn : [result] "=r" (result), [dummy] "=f" (dummy) : [x] "f" (x)); \ + return result; \ +} + +DEFINE_IFUNC(ifloorf, "floor.w.s %[dummy],%[x]; mfc1 %[result],%[dummy]") +DEFINE_IFUNC(iceilf, "ceil.w.s %[dummy],%[x]; mfc1 %[result],%[dummy]") +DEFINE_IFUNC(itruncf, "trunc.w.s %[dummy],%[x]; mfc1 %[result],%[dummy]") +DEFINE_IFUNC(iroundf, "round.w.s %[dummy],%[x]; mfc1 %[result],%[dummy]") + +#elif HAVE_FLOORF + +static inline CONST_FUNCTION int ifloorf(float x) {return (int)floorf(x);} +static inline CONST_FUNCTION int iceilf (float x) {return (int)ceilf(x);} +static inline CONST_FUNCTION int itruncf(float x) {return (int)truncf(x);} +static inline CONST_FUNCTION int iroundf(float x) {return (int)roundf(x);} + +#else // !PSP && !HAVE_FLOORF --> use double-precision floor() and ceil() + +static inline CONST_FUNCTION int ifloorf(float x) {return (int)floor(x);} +static inline CONST_FUNCTION int iceilf (float x) {return (int)ceil(x);} +static inline CONST_FUNCTION int itruncf(float x) + {return (x)<0 ? (int)-floor(-x) : (int)floor(x);} +static inline CONST_FUNCTION int iroundf(float x) {return (int)floor(x+0.5f);} + +#endif + +/*----------------------------------*/ + +#ifdef PSP_DEBUG + +/* Debug/error message macro. DMSG("message",...) prints to stderr a line + * in the form: + * func_name(file:line): message + * printf()-style format tokens and arguments are allowed, and no newline + * is required at the end. The format string must be a literal string + * constant. Note that we sprintf() into a buffer and write the buffer + * rather than calling fprintf() directly because fprintf() makes multiple + * low-level write calls, which can slow the program down significantly + * depending on PSPlink's responsiveness on the PC host. */ +/* global shared */ char DMSG_buffer[10000]; +#define DMSG(msg,...) do { \ + snprintf(DMSG_buffer, sizeof(DMSG_buffer), "%s(%s:%d): " msg "\n", \ + __FUNCTION__, __FILE__, __LINE__ , ## __VA_ARGS__); \ + fputs(DMSG_buffer, stderr); \ +} while (0) + +/* Timing macro. Start timing with DSTART(); DEND() will then print the + * elapsed time in microseconds. Both must occur at the same level of + * block nesting. */ +#define DSTART() { const uint32_t __start = sceKernelGetSystemTimeLow() +#define DEND() DMSG("time=%u", sceKernelGetSystemTimeLow() - __start); } + +#else // !PSP_DEBUG + +/* Disable debug output */ +#define DMSG(msg,...) /*nothing*/ +#define DSTART() /*nothing*/ +#define DEND() /*nothing*/ + +#endif + +/*----------------------------------*/ + +/* Test a precondition, and perform the given action if it fails */ + +#define PRECOND(condition,fail_action) do { \ + if (UNLIKELY(!(condition))) { \ + DMSG("PRECONDITION FAILED: %s", #condition); \ + fail_action; \ + } \ +} while (0) + +/**************************************************************************/ + +/* Include the Yabause core header for other common definitions/declarations */ + +#include "../core.h" + +/**************************************************************************/ + +#endif // PSP_COMMON_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/config.c b/yabause/src/psp/config.c new file mode 100644 index 0000000000..3fdf998e98 --- /dev/null +++ b/yabause/src/psp/config.c @@ -0,0 +1,842 @@ +/* src/psp/config.c: Configuration data management for PSP + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "config.h" +#include "psp-sh2.h" +#include "psp-video.h" +#include "sh2.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Configuration file name (always stored in program directory) */ +#define PATH_INI "yabause.ini" + +/* Data file paths */ +static char path_bios[256] = "bios.bin"; +static char path_cd[256] = "cd.iso"; +static char path_bup[256] = "backup.bin"; + +/* General settings */ +static int start_in_emu = 0; +static int use_me = 0; +static uint32_t me_writeback_period = 1; +static uint32_t me_uncached_boundary = 0x800; +static int bup_autosave = 1; + +/* Button configuration */ +static uint32_t button[6] = { + [CONFIG_BUTTON_A] = PSP_CTRL_CROSS, + [CONFIG_BUTTON_B] = PSP_CTRL_CIRCLE, + [CONFIG_BUTTON_C] = 0, + [CONFIG_BUTTON_X] = PSP_CTRL_SQUARE, + [CONFIG_BUTTON_Y] = PSP_CTRL_TRIANGLE, + [CONFIG_BUTTON_Z] = 0, +}; + +/* Module selections */ +static int module_sh2 = SH2CORE_PSP; +static int module_video = VIDCORE_PSP; + +/* Display settings */ +static int cache_textures = 1; +static int smooth_textures = 0; +static int smooth_hires = 0; +static int enable_rotate = 1; +static int optimize_rotate = 1; +static int frameskip_auto = 0; +static int frameskip_num = 0; +static int frameskip_interlace = 1; +static int frameskip_rotate = 1; +static int show_fps = 0; + +static uint32_t sh2_optimizations = SH2_OPTIMIZE_ASSUME_SAFE_DIVISION + | SH2_OPTIMIZE_BRANCH_TO_RTS + | SH2_OPTIMIZE_FOLD_SUBROUTINES + | SH2_OPTIMIZE_LOCAL_ACCESSES + | SH2_OPTIMIZE_LOCAL_POINTERS + | SH2_OPTIMIZE_MAC_NOSAT + | SH2_OPTIMIZE_POINTERS + | SH2_OPTIMIZE_POINTERS_MAC + | SH2_OPTIMIZE_STACK; +/* All known optimization flags (so we can leave newly-implemented flags at + * their default values when loading the config file) */ +#define SH2_KNOWN_OPTIMIZATIONS (SH2_OPTIMIZE_ASSUME_SAFE_DIVISION \ + | SH2_OPTIMIZE_BRANCH_TO_RTS \ + | SH2_OPTIMIZE_FOLD_SUBROUTINES \ + | SH2_OPTIMIZE_LOCAL_ACCESSES \ + | SH2_OPTIMIZE_LOCAL_POINTERS \ + | SH2_OPTIMIZE_MAC_NOSAT \ + | SH2_OPTIMIZE_POINTERS \ + | SH2_OPTIMIZE_POINTERS_MAC \ + | SH2_OPTIMIZE_STACK) + +/* Deciline (precise timing) mode flag */ +static int deciline_mode = 0; +/* Audio sync flag */ +static int audio_sync = 1; +/* Clock sync flag */ +static int clock_sync = 1; +/* Start-from-fixed-time flag */ +static int clock_fixed_time = 0; + +/*-----------------------------------------------------------------------*/ + +/* Local function declarations */ + +static int parse_string(const char *file, int line, const char *name, + const char *text, char *buffer, unsigned int bufsize); +static int parse_int(const char *file, int line, const char *name, + const char *text, int *value_ret); +static int parse_uint32(const char *file, int line, const char *name, + const char *text, uint32_t *value_ret); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * config_load: Load configuration data from the configuration file. + * Invalid data is ignored, and options not specified in the configuration + * file are left unchanged. + * + * [Parameters] + * None + * [Return value] + * None + */ +void config_load(void) +{ + /* Open the configuration file, aborting if it's not available */ + FILE *f = fopen(PATH_INI, "r"); + if (!f) { + perror("fopen(" PATH_INI ")"); + return; + } + + /* Read the entire file in at once and close the file pointer */ + char *filebuf; + if (fseek(f, 0, SEEK_END) < 0) { + perror("fseek(SEEK_END)"); + close_and_return: + fclose(f); + return; + } + long filesize = ftell(f); + if (filesize < 0) { + perror("ftell()"); + goto close_and_return; + } + if (fseek(f, 0, SEEK_SET) < 0) { + perror("fseek(SEEK_SET)"); + goto close_and_return; + } + filebuf = malloc(filesize+1); // Leave space for a trailing \0 + if (!filebuf) { + fprintf(stderr, "No memory for config file buffer (%ld bytes)\n", + filesize); + goto close_and_return; + } + if (fread(filebuf, filesize, 1, f) != 1) { + fprintf(stderr, "Failed to read config file\n"); + free(filebuf); + goto close_and_return; + } + fclose(f); + filebuf[filesize] = 0; + + /* Parse each line of the configuration file; lines are of the form + * name=value + * with no spaces permitted on either side of the "=". We take care + * to treat names as case-insensitive and to support any of "\r", + * "\r\n" or "\n" as a line terminator, in case people edit the file + * on their own. */ + + char *s, *eol; + int line; + for (s = filebuf, line = 1; *s; s = eol, line++) { + + eol = s + strcspn(s, "\r\n"); + if (*eol == '\r') { + *eol++ = 0; + } + if (*eol == '\n') { + *eol++ = 0; + } + char *name = s; + char *value = strchr(s, '='); + if (!value) { + fprintf(stderr, "%s:%d: Missing `='\n", PATH_INI, line); + continue; + } + *value++ = 0; + + if (stricmp(name, "path_bios") == 0) { + parse_string(PATH_INI, line, name, value, + path_bios, sizeof(path_bios)); + + } else if (stricmp(name, "path_cd") == 0) { + parse_string(PATH_INI, line, name, value, + path_cd, sizeof(path_cd)); + + } else if (stricmp(name, "path_bup") == 0) { + parse_string(PATH_INI, line, name, value, + path_bup, sizeof(path_bup)); + + } else if (stricmp(name, "start_in_emu") == 0) { + parse_int(PATH_INI, line, name, value, &start_in_emu); + + } else if (stricmp(name, "use_me") == 0) { + parse_int(PATH_INI, line, name, value, &use_me); + + } else if (stricmp(name, "me_writeback_period") == 0) { + parse_uint32(PATH_INI, line, name, value, &me_writeback_period); + if (!me_writeback_period + || (me_writeback_period & (me_writeback_period - 1)) + ) { + fprintf(stderr, "config_load(): Invalid value %u for" + " me_writeback_period (must be a power of 2)\n", + me_writeback_period); + me_writeback_period = 1; + } + + } else if (stricmp(name, "me_uncached_boundary") == 0) { + parse_uint32(PATH_INI, line, name, value, &me_uncached_boundary); + if (me_uncached_boundary > 0x80000) { + me_uncached_boundary = 0x80000; + } + + } else if (stricmp(name, "bup_autosave") == 0) { + parse_int(PATH_INI, line, name, value, &bup_autosave); + + } else if (stricmp(name, "button.A") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_A]); + + } else if (stricmp(name, "button.B") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_B]); + + } else if (stricmp(name, "button.C") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_C]); + + } else if (stricmp(name, "button.X") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_X]); + + } else if (stricmp(name, "button.Y") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_Y]); + + } else if (stricmp(name, "button.Z") == 0) { + parse_uint32(PATH_INI, line, name, value,&button[CONFIG_BUTTON_Z]); + + } else if (stricmp(name, "module_sh2") == 0) { + parse_int(PATH_INI, line, name, value, &module_sh2); + + } else if (stricmp(name, "module_video") == 0) { + parse_int(PATH_INI, line, name, value, &module_video); + + } else if (stricmp(name, "cache_textures") == 0) { + parse_int(PATH_INI, line, name, value, &cache_textures); + + } else if (stricmp(name, "smooth_textures") == 0) { + parse_int(PATH_INI, line, name, value, &smooth_textures); + + } else if (stricmp(name, "smooth_hires") == 0) { + parse_int(PATH_INI, line, name, value, &smooth_hires); + + } else if (stricmp(name, "enable_rotate") == 0) { + parse_int(PATH_INI, line, name, value, &enable_rotate); + + } else if (stricmp(name, "optimize_rotate") == 0) { + parse_int(PATH_INI, line, name, value, &optimize_rotate); + + } else if (stricmp(name, "frameskip_auto") == 0) { + parse_int(PATH_INI, line, name, value, &frameskip_auto); + + } else if (stricmp(name, "frameskip_num") == 0) { + parse_int(PATH_INI, line, name, value, &frameskip_num); + if (frameskip_num < 0) { + frameskip_num = 0; + } else if (frameskip_num > 9) { + frameskip_num = 9; + } + + } else if (stricmp(name, "frameskip_interlace") == 0) { + parse_int(PATH_INI, line, name, value, &frameskip_interlace); + + } else if (stricmp(name, "frameskip_rotate") == 0) { + parse_int(PATH_INI, line, name, value, &frameskip_rotate); + + } else if (stricmp(name, "show_fps") == 0) { + parse_int(PATH_INI, line, name, value, &show_fps); + + } else if (stricmp(name, "sh2_optimizations") == 0) { + uint32_t newval = strtoul(value, &s, 10); + if (*s != '/') { + fprintf(stderr, "%s:%d: Bad format for `%s' value\n", + PATH_INI, line, name); + continue; + } + uint32_t mask = strtoul(s+1, &s, 10); + if (*s) { + fprintf(stderr, "%s:%d: Bad format for `%s' value\n", + PATH_INI, line, name); + continue; + } + sh2_optimizations &= ~mask; + sh2_optimizations |= newval & mask; + + } else if (stricmp(name, "deciline_mode") == 0) { + parse_int(PATH_INI, line, name, value, &deciline_mode); + + } else if (stricmp(name, "audio_sync") == 0) { + parse_int(PATH_INI, line, name, value, &audio_sync); + + } else if (stricmp(name, "clock_sync") == 0) { + parse_int(PATH_INI, line, name, value, &clock_sync); + + } else if (stricmp(name, "clock_fixed_time") == 0) { + parse_int(PATH_INI, line, name, value, &clock_fixed_time); + + } else { + fprintf(stderr, "%s:%d: Unknown configuration variable `%s'\n", + PATH_INI, line, name); + + } + + } // for (s = filebuf, line = 1; *s; s = eol, line++) +} + +/*-----------------------------------------------------------------------*/ + +/** + * config_save: Save the current configuration to the configuration file. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +int config_save(void) +{ + FILE *f = fopen(PATH_INI, "w"); + if (!f) { + perror("fopen(" PATH_INI ")"); + return 0; + } + + if (fprintf(f, "path_bios=%s\n", path_bios ) < 0 + || fprintf(f, "path_cd=%s\n", path_cd ) < 0 + || fprintf(f, "path_bup=%s\n", path_bup ) < 0 + || fprintf(f, "start_in_emu=%d\n", start_in_emu ) < 0 + || fprintf(f, "use_me=%d\n", use_me ) < 0 + || fprintf(f, "me_writeback_period=%u\n", me_writeback_period ) < 0 + || fprintf(f, "me_uncached_boundary=%u\n", me_uncached_boundary ) < 0 + || fprintf(f, "bup_autosave=%d\n", bup_autosave ) < 0 + || fprintf(f, "button.A=%u\n", button[CONFIG_BUTTON_A]) < 0 + || fprintf(f, "button.B=%u\n", button[CONFIG_BUTTON_B]) < 0 + || fprintf(f, "button.C=%u\n", button[CONFIG_BUTTON_C]) < 0 + || fprintf(f, "button.X=%u\n", button[CONFIG_BUTTON_X]) < 0 + || fprintf(f, "button.Y=%u\n", button[CONFIG_BUTTON_Y]) < 0 + || fprintf(f, "button.Z=%u\n", button[CONFIG_BUTTON_Z]) < 0 + || fprintf(f, "module_sh2=%d\n", module_sh2 ) < 0 + || fprintf(f, "module_video=%d\n", module_video ) < 0 + || fprintf(f, "cache_textures=%d\n", cache_textures ) < 0 + || fprintf(f, "smooth_textures=%d\n", smooth_textures ) < 0 + || fprintf(f, "smooth_hires=%d\n", smooth_hires ) < 0 + || fprintf(f, "enable_rotate=%d\n", enable_rotate ) < 0 + || fprintf(f, "optimize_rotate=%d\n", optimize_rotate ) < 0 + || fprintf(f, "frameskip_auto=%d\n", frameskip_auto ) < 0 + || fprintf(f, "frameskip_num=%d\n", frameskip_num ) < 0 + || fprintf(f, "frameskip_interlace=%d\n", frameskip_interlace ) < 0 + || fprintf(f, "frameskip_rotate=%d\n", frameskip_rotate ) < 0 + || fprintf(f, "show_fps=%d\n", show_fps ) < 0 + || fprintf(f, "sh2_optimizations=%u/%u\n", sh2_optimizations, + SH2_KNOWN_OPTIMIZATIONS) < 0 + || fprintf(f, "deciline_mode=%d\n", deciline_mode ) < 0 + || fprintf(f, "audio_sync=%d\n", audio_sync ) < 0 + || fprintf(f, "clock_sync=%d\n", clock_sync ) < 0 + || fprintf(f, "clock_fixed_time=%d\n", clock_fixed_time ) < 0 + ) { + perror("fprintf(" PATH_INI ",...)"); + fclose(f); + return 0; + } + + if (fclose(f) < 0) { + perror("fclose(" PATH_INI ")"); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/** + * config_get_*: Retrieve the current value of a configuration variable. + * + * [Parameters] + * id: Button ID (only for config_get_button()) + * [Return value] + * Current value of configuration variable + */ + +const char *config_get_path_bios(void) +{ + return path_bios; +} + +const char *config_get_path_cd(void) +{ + return path_cd; +} + +const char *config_get_path_bup(void) +{ + return path_bup; +} + +int config_get_start_in_emu(void) +{ + return start_in_emu; +} + +int config_get_use_me(void) +{ + return use_me; +} + +uint32_t config_get_me_writeback_period(void) +{ + return me_writeback_period; +} + +uint32_t config_get_me_uncached_boundary(void) +{ + return me_uncached_boundary; +} + +int config_get_bup_autosave(void) +{ + return bup_autosave; +} + +uint32_t config_get_button(ConfigButtonID id) +{ + PRECOND(id >= CONFIG_BUTTON_A && id <= CONFIG_BUTTON_Z, return 0); + return button[id]; +} + +int config_get_module_sh2(void) +{ + return module_sh2; +} + +int config_get_module_video(void) +{ + return module_video; +} + +int config_get_cache_textures(void) +{ + return cache_textures; +} + +int config_get_smooth_textures(void) +{ + return smooth_textures; +} + +int config_get_smooth_hires(void) +{ + return smooth_hires; +} + +int config_get_enable_rotate(void) +{ + return enable_rotate; +} + +int config_get_optimize_rotate(void) +{ + return optimize_rotate; +} + +int config_get_frameskip_auto(void) +{ + return frameskip_auto; +} + +int config_get_frameskip_num(void) +{ + return frameskip_num; +} + +int config_get_frameskip_interlace(void) +{ + return frameskip_interlace; +} + +int config_get_frameskip_rotate(void) +{ + return frameskip_rotate; +} + +int config_get_show_fps(void) +{ + return show_fps; +} + +uint32_t config_get_sh2_optimizations(void) +{ + return sh2_optimizations; +} + +int config_get_deciline_mode(void) +{ + return deciline_mode; +} + +int config_get_audio_sync(void) +{ + return audio_sync; +} + +int config_get_clock_sync(void) +{ + return clock_sync; +} + +int config_get_clock_fixed_time(void) +{ + return clock_fixed_time; +} + +/*-----------------------------------------------------------------------*/ + +/** + * config_set_*: Set the value of a configuration variable. + * + * [Parameters] + * id: Button ID (only for config_get_button()) + * value: New value for configuration variable + * [Return value] + * Nonzero on success, zero on error + */ + +int config_set_path_bios(const char *value) +{ + PRECOND(value != NULL, return 0); + if (strlen(value) > sizeof(path_bios) - 1) { + fprintf(stderr, "config_set_path_bios(): Value too long (max %d" + " characters): %s\n", sizeof(path_bios) - 1, value); + return 0; + } + strcpy(path_bios, value); // Safe + return 1; +} + +int config_set_path_cd(const char *value) +{ + PRECOND(value != NULL, return 0); + if (strlen(value) > sizeof(path_cd) - 1) { + fprintf(stderr, "config_set_path_cd(): Value too long (max %d" + " characters): %s\n", sizeof(path_cd) - 1, value); + return 0; + } + strcpy(path_cd, value); // Safe + return 1; +} + +int config_set_path_bup(const char *value) +{ + PRECOND(value != NULL, return 0); + if (strlen(value) > sizeof(path_bup) - 1) { + fprintf(stderr, "config_set_path_bup(): Value too long (max %d" + " characters): %s\n", sizeof(path_bup) - 1, value); + return 0; + } + strcpy(path_bup, value); // Safe + return 1; +} + +int config_set_start_in_emu(int value) +{ + start_in_emu = value ? 1 : 0; + return 1; +} + +int config_set_use_me(int value) +{ + use_me = value ? 1 : 0; + return 1; +} + +int config_set_me_writeback_period(uint32_t value) +{ + if (value == 0 || (value & (value-1))) { + fprintf(stderr, "config_set_me_writeback_period(): Invalid period %u" + " (must be a power of 2)\n", value); + return 0; + } + me_writeback_period = value; + return 1; +} + +int config_set_me_uncached_boundary(uint32_t value) +{ + if (value > 0x80000) { + fprintf(stderr, "config_set_me_uncached_boundary(): Invalid boundary" + " %u (maximum %u)\n", value, 0x80000); + return 0; + } + me_uncached_boundary = value; + return 1; +} + +int config_set_bup_autosave(int value) +{ + bup_autosave = value ? 1 : 0; + return 1; +} + +int config_set_button(ConfigButtonID id, uint32_t value) +{ + PRECOND(id >= CONFIG_BUTTON_A && id <= CONFIG_BUTTON_Z, return 0); + button[id] = value; + return 1; +} + +int config_set_module_sh2(int value) +{ + module_sh2 = value; + return 1; +} + +int config_set_module_video(int value) +{ + module_video = value; + return 1; +} + +int config_set_cache_textures(int value) +{ + cache_textures = value ? 1 : 0; + return 1; +} + +int config_set_smooth_textures(int value) +{ + smooth_textures = value ? 1 : 0; + return 1; +} + +int config_set_smooth_hires(int value) +{ + smooth_hires = value ? 1 : 0; + return 1; +} + +int config_set_enable_rotate(int value) +{ + enable_rotate = value ? 1 : 0; + return 1; +} + +int config_set_optimize_rotate(int value) +{ + optimize_rotate = value ? 1 : 0; + return 1; +} + +int config_set_frameskip_auto(int value) +{ + frameskip_auto = value ? 1 : 0; + return 1; +} + +int config_set_frameskip_num(int value) +{ + if (value < 0) { + frameskip_num = 0; + } else if (value > 9) { + frameskip_num = 9; + } else { + frameskip_num = value; + } + return 1; +} + +int config_set_frameskip_interlace(int value) +{ + frameskip_interlace = value ? 1 : 0; + return 1; +} + +int config_set_frameskip_rotate(int value) +{ + frameskip_rotate = value ? 1 : 0; + return 1; +} + +int config_set_show_fps(int value) +{ + show_fps = value ? 1 : 0; + return 1; +} + +int config_set_sh2_optimizations(uint32_t value) +{ + sh2_optimizations = value & SH2_KNOWN_OPTIMIZATIONS; + return 1; +} + +int config_set_deciline_mode(int value) +{ + deciline_mode = value ? 1 : 0; + return 1; +} + +int config_set_audio_sync(int value) +{ + audio_sync = value ? 1 : 0; + return 1; +} + +int config_set_clock_sync(int value) +{ + clock_sync = value ? 1 : 0; + return 1; +} + +int config_set_clock_fixed_time(int value) +{ + clock_fixed_time = value ? 1 : 0; + return 1; +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * parse_string: Parse a string-type configuration entry. + * + * [Parameters] + * file: Name of configuration file (for error messages) + * line: Line number in configuration file (for error messages) + * name: Configuration entry name (for error messages) + * text: Configuration value text + * buffer: Buffer into which to store string value + * bufsize: Size of buffer (maximum string length + 1) + * [Return value] + * Nonzero on success, zero on error + */ +static int parse_string(const char *file, int line, const char *name, + const char *text, char *buffer, unsigned int bufsize) +{ + PRECOND(text != NULL, return 0); + PRECOND(buffer != NULL, return 0); + + if (strlen(text) > bufsize - 1) { + fprintf(stderr, "%s:%d: String for `%s' too long (max %d" + " characters)\n", file, line, name, bufsize - 1); + return 0; + } + strcpy(buffer, text); // Safe + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * parse_int: Parse an integer-type configuration entry. + * + * [Parameters] + * file: Name of configuration file (for error messages) + * line: Line number in configuration file (for error messages) + * name: Configuration entry name (for error messages) + * text: Configuration value text + * value_ret: Pointer to variable into which to store integer value + * [Return value] + * Nonzero on success, zero on error + */ +static int parse_int(const char *file, int line, const char *name, + const char *text, int *value_ret) +{ + PRECOND(text != NULL, return 0); + PRECOND(value_ret != NULL, return 0); + + char *s; + int newval = strtol(text, &s, 10); + if (*s) { + fprintf(stderr, "%s:%d: Value for `%s' must be a number\n", + file, line, name); + return 0; + } + *value_ret = newval; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * parse_uint32: Parse a 32-bit unsigned integer configuration entry. + * + * [Parameters] + * file: Name of configuration file (for error messages) + * line: Line number in configuration file (for error messages) + * name: Configuration entry name (for error messages) + * text: Configuration value text + * value_ret: Pointer to variable into which to store integer value + * [Return value] + * Nonzero on success, zero on error + */ +static int parse_uint32(const char *file, int line, const char *name, + const char *text, uint32_t *value_ret) +{ + PRECOND(text != NULL, return 0); + PRECOND(value_ret != NULL, return 0); + + char *s; + uint32_t newval = strtoul(text, &s, 10); + if (*s) { + fprintf(stderr, "%s:%d: Value for `%s' must be a nonnegative number\n", + file, line, name); + return 0; + } + *value_ret = newval; + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/config.h b/yabause/src/psp/config.h new file mode 100644 index 0000000000..9b55bdbe05 --- /dev/null +++ b/yabause/src/psp/config.h @@ -0,0 +1,144 @@ +/* src/psp/config.h: Header for configuration data management for PSP + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_CONFIG_H +#define PSP_CONFIG_H + +/*************************************************************************/ + +/** + * CONFIG_BUTTON_*: Constants identifying Saturn controller buttons which + * are passed to the config_get_button() and config_set_button() functions. + */ +typedef enum ConfigButtonID_ { + CONFIG_BUTTON_A = 0, + CONFIG_BUTTON_B, + CONFIG_BUTTON_C, + CONFIG_BUTTON_X, + CONFIG_BUTTON_Y, + CONFIG_BUTTON_Z, +} ConfigButtonID; + +/** + * config_load: Load configuration data from the configuration file. + * Invalid data is ignored, and options not specified in the configuration + * file are left unchanged. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void config_load(void); + +/** + * config_save: Save the current configuration to the configuration file. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +extern int config_save(void); + +/** + * config_get_*: Retrieve the current value of a configuration variable. + * + * [Parameters] + * id: Button ID (only for config_get_button()) + * [Return value] + * Current value of configuration variable + */ +extern const char *config_get_path_bios(void); +extern const char *config_get_path_cd(void); +extern const char *config_get_path_bup(void); +extern int config_get_start_in_emu(void); +extern int config_get_use_me(void); +extern uint32_t config_get_me_writeback_period(void); +extern uint32_t config_get_me_uncached_boundary(void); +extern int config_get_bup_autosave(void); +extern uint32_t config_get_button(ConfigButtonID id); +extern int config_get_module_sh2(void); +extern int config_get_module_video(void); +extern int config_get_cache_textures(void); +extern int config_get_smooth_textures(void); +extern int config_get_smooth_hires(void); +extern int config_get_enable_rotate(void); +extern int config_get_optimize_rotate(void); +extern int config_get_frameskip_auto(void); +extern int config_get_frameskip_num(void); +extern int config_get_frameskip_interlace(void); +extern int config_get_frameskip_rotate(void); +extern int config_get_show_fps(void); +extern uint32_t config_get_sh2_optimizations(void); +extern int config_get_deciline_mode(void); +extern int config_get_audio_sync(void); +extern int config_get_clock_sync(void); +extern int config_get_clock_fixed_time(void); + +/** + * config_set_*: Set the value of a configuration variable. + * + * [Parameters] + * id: Button ID (only for config_get_button()) + * value: New value for configuration variable + * [Return value] + * Nonzero on success, zero on error + */ +extern int config_set_path_bios(const char *value); +extern int config_set_path_cd(const char *value); +extern int config_set_path_bup(const char *value); +extern int config_set_start_in_emu(int value); +extern int config_set_use_me(int value); +extern int config_set_me_writeback_period(uint32_t value); +extern int config_set_me_uncached_boundary(uint32_t value); +extern int config_set_bup_autosave(int value); +extern int config_set_button(ConfigButtonID id, uint32_t value); +extern int config_set_module_sh2(int value); +extern int config_set_module_video(int value); +extern int config_set_cache_textures(int value); +extern int config_set_smooth_textures(int value); +extern int config_set_smooth_hires(int value); +extern int config_set_enable_rotate(int value); +extern int config_set_optimize_rotate(int value); +extern int config_set_frameskip_auto(int value); +extern int config_set_frameskip_num(int value); +extern int config_set_frameskip_interlace(int value); +extern int config_set_frameskip_rotate(int value); +extern int config_set_show_fps(int value); +extern int config_set_sh2_optimizations(uint32_t value); +extern int config_set_deciline_mode(int value); +extern int config_set_audio_sync(int value); +extern int config_set_clock_sync(int value); +extern int config_set_clock_fixed_time(int value); + +/*************************************************************************/ + +#endif // PSP_CONFIG_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/control.c b/yabause/src/psp/control.c new file mode 100644 index 0000000000..74867344aa --- /dev/null +++ b/yabause/src/psp/control.c @@ -0,0 +1,139 @@ +/* src/psp/control.c: PSP controller input management routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "control.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Current bitmask of buttons pressed */ +static uint32_t buttons; + +/* Previous value of "buttons" */ +static uint32_t last_buttons; + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * control_init: Initialize the controller input management code. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +int control_init(void) +{ + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); + buttons = last_buttons = 0; + + return 1; +} + +/*************************************************************************/ + +/** + * control_update: Update the current controller status. + * + * [Parameters] + * None + * [Return value] + * None + */ +void control_update(void) +{ + SceCtrlData pad_data; + sceCtrlPeekBufferPositive(&pad_data, 1); + + last_buttons = buttons; + buttons = pad_data.Buttons; + + /* If the directional pad isn't being used, check the analog pad instead */ + if (!(buttons & 0x00F0)) { + if (pad_data.Lx < 32) { + buttons |= PSP_CTRL_LEFT; + } else if (pad_data.Lx >= 224) { + buttons |= PSP_CTRL_RIGHT; + } + if (pad_data.Ly < 32) { + buttons |= PSP_CTRL_UP; + } else if (pad_data.Ly >= 224) { + buttons |= PSP_CTRL_DOWN; + } + } + + /* The OS doesn't seem to reset the screensaver timeout when the + * analog pad is moved, so take care of that ourselves */ + if (pad_data.Lx < 32 || pad_data.Lx >= 224 + || pad_data.Ly < 32 || pad_data.Ly >= 224 + ) { + scePowerTick(0); + } +} + +/*************************************************************************/ + +/** + * control_state: Return the current controller state. + * + * [Parameters] + * None + * [Return value] + * Current controller state (PSP_CTRL_* bitmask of buttons held down) + */ +uint32_t control_state(void) +{ + return buttons; +} + +/*-----------------------------------------------------------------------*/ + +/** + * control_new_buttons: Return any buttons newly pressed in the last call + * to control_update(). + * + * [Parameters] + * None + * [Return value] + * Newly pressed buttons (PSP_CTRL_* bitmask) + */ +uint32_t control_new_buttons(void) +{ + return buttons & ~last_buttons; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/control.h b/yabause/src/psp/control.h new file mode 100644 index 0000000000..e99e84d731 --- /dev/null +++ b/yabause/src/psp/control.h @@ -0,0 +1,79 @@ +/* src/psp/control.c: PSP controller input management routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_CONTROL_H +#define PSP_CONTROL_H + +/*************************************************************************/ + +/** + * control_init: Initialize the controller input management code. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +extern int control_init(void); + +/** + * control_update: Update the current controller status. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void control_update(void); + +/** + * control_state: Return the current controller status. + * + * [Parameters] + * None + * [Return value] + * Current controller status (PSP_CTRL_* bitmask of buttons held down) + */ +extern uint32_t control_state(void); + +/** + * control_new_buttons: Return any buttons newly pressed in the last call + * to control_update(). + * + * [Parameters] + * None + * [Return value] + * Newly pressed buttons (PSP_CTRL_* bitmask) + */ +extern uint32_t control_new_buttons(void); + +/*************************************************************************/ + +#endif // PSP_CONTROL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/display.c b/yabause/src/psp/display.c new file mode 100644 index 0000000000..701461a555 --- /dev/null +++ b/yabause/src/psp/display.c @@ -0,0 +1,603 @@ +/* src/psp/display.c: PSP display management functions + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "display.h" +#include "gu.h" +#include "sys.h" + +/*************************************************************************/ + +/* Effective display size */ +static unsigned int display_width, display_height; + +/* Display mode (pixel format) and bits per pixel */ +static uint8_t display_mode; +static uint8_t display_bpp; + +/* Currently displayed (front) buffer index */ +static uint8_t displayed_surface; + +/* Work (back) buffer index */ +static uint8_t work_surface; + +/* Pointers into VRAM */ +static void *surfaces[2]; // Display buffers +static uint8_t *vram_spare_ptr; // Spare VRAM (above display buffers) +static uint8_t *vram_next_alloc; // Next spare address to allocate +static uint8_t *vram_top; // Top of VRAM + +/* Display list */ +/* Size note: A single VDP2 background layer, if at resolution 704x512, + * can take up to 500k of display list memory to render. */ +static uint8_t __attribute__((aligned(64))) display_list[3*1024*1024]; + +/* Semaphore flag to indicate whether there is a buffer swap pending; while + * true, the main thread must not access any other variables */ +static int swap_pending; + +/* System clock after the sceDisplayWaitVblankStart() starting the last frame*/ +static uint32_t last_frame_start; + +/* Number of frames between the last two calls to sceDisplayWaitVblankStart()*/ +static unsigned int last_frame_length; + +/*************************************************************************/ + +/* Local routine declarations */ + +__attribute__((noreturn)) + static int buffer_swap_thread(SceSize args, void *argp); + +static void do_buffer_swap(void); + +/*************************************************************************/ +/*************************************************************************/ + +/** + * display_init: Initialize the PSP display. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +int display_init(void) +{ + /* Have we already initialized? */ + static int initted = 0; + if (initted) { + return 1; + } + + /* Clear out VRAM */ + memset(sceGeEdramGetAddr(), 0, sceGeEdramGetSize()); + sceKernelDcacheWritebackInvalidateAll(); + + /* Set display mode */ + int32_t res = sceDisplaySetMode(0, DISPLAY_WIDTH, DISPLAY_HEIGHT); + if (res < 0) { + DMSG("sceDisplaySetMode() failed: %s", psp_strerror(res)); + return 0; + } + display_width = DISPLAY_WIDTH; + display_height = DISPLAY_HEIGHT; + display_mode = PSP_DISPLAY_PIXEL_FORMAT_8888; + display_bpp = 32; + + /* Initialize VRAM pointers */ + uint8_t *vram_addr = sceGeEdramGetAddr(); + uint32_t vram_size = sceGeEdramGetSize(); + const uint32_t frame_size = + DISPLAY_STRIDE * DISPLAY_HEIGHT * (display_bpp/8); + int i; + for (i = 0; i < lenof(surfaces); i++) { + surfaces[i] = vram_addr + i*frame_size; + } + vram_spare_ptr = (uint8_t *)(vram_addr + lenof(surfaces)*frame_size); + vram_next_alloc = vram_spare_ptr; + vram_top = vram_addr + vram_size; + displayed_surface = 0; + work_surface = 1; + swap_pending = 0; + + /* Set the currently-displayed buffer */ + sceDisplaySetFrameBuf(surfaces[displayed_surface], DISPLAY_STRIDE, + display_mode, PSP_DISPLAY_SETBUF_IMMEDIATE); + + /* Set up the GU library */ + guInit(); + guStart(GU_DIRECT, display_list); + guDispBuffer(DISPLAY_WIDTH, DISPLAY_HEIGHT, + surfaces[displayed_surface], DISPLAY_STRIDE); + guFinish(); + guSync(0, 0); + + /* Success */ + initted = 1; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_set_size: Set the effective display size. The display will + * be centered on the PSP's screen. + * + * This routine should not be called while drawing a frame (i.e. between + * display_begin_frame() and display_end_frame()). + * + * [Parameters] + * width: Effective display width (pixels) + * height: Effective display height (pixels) + * [Return value] + * None + */ +void display_set_size(int width, int height) +{ + display_width = width; + display_height = height; +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_disp_buffer: Return a pointer to the current displayed (front) + * buffer. + * + * [Parameters] + * None + * [Return value] + * Pointer to the current work buffer + */ +uint32_t *display_disp_buffer(void) +{ + return (uint32_t *)surfaces[displayed_surface] + + ((DISPLAY_HEIGHT - display_height) / 2) * DISPLAY_STRIDE + + ((DISPLAY_WIDTH - display_width ) / 2); +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_work_buffer: Return a pointer to the current work (back) + * buffer. + * + * [Parameters] + * None + * [Return value] + * Pointer to the current work buffer + */ +uint32_t *display_work_buffer(void) +{ + return (uint32_t *)surfaces[work_surface] + + ((DISPLAY_HEIGHT - display_height) / 2) * DISPLAY_STRIDE + + ((DISPLAY_WIDTH - display_width ) / 2); +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_spare_vram: Return a pointer to the VRAM spare area. + * + * [Parameters] + * None + * [Return value] + * Pointer to the VRAM spare area + */ +void *display_spare_vram(void) +{ + return vram_spare_ptr; +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_spare_vram_size: Return the size of the VRAM spare area. + * + * [Parameters] + * None + * [Return value] + * Size of the VRAM spare area, in bytes + */ +uint32_t display_spare_vram_size(void) +{ + return vram_top - vram_spare_ptr; +} + +/*************************************************************************/ + +/** + * display_alloc_vram: Allocate memory from the spare VRAM area, aligned + * to a multiple of 64 bytes. All allocated VRAM will be automatically + * freed at the next call to display_begin_frame(). + * + * [Parameters] + * size: Amount of memory to allocate, in bytes + * [Return value] + * Pointer to allocated memory, or NULL on failure (out of memory) + */ +void *display_alloc_vram(uint32_t size) +{ + if (vram_next_alloc + size > vram_top) { + return NULL; + } + void *ptr = vram_next_alloc; + vram_next_alloc += size; + if ((uintptr_t)vram_next_alloc & 0x3F) { // Make sure it stays aligned + vram_next_alloc += 0x40 - ((uintptr_t)vram_next_alloc & 0x3F); + } + return ptr; +} + +/*************************************************************************/ + +/** + * display_begin_frame: Begin processing for a frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +void display_begin_frame(void) +{ + sceKernelDelayThread(0); // Seems to be needed for the buffer swap to work + while (swap_pending) { + sceKernelDelayThread(100); // 0.1ms + } + + vram_next_alloc = vram_spare_ptr; + + guStart(GU_DIRECT, display_list); + + /* We don't use a depth buffer, so disable depth buffer writing */ + guDepthMask(GU_TRUE); + + /* Clear the work surface--make sure to use the base pointer here, not + * the effective pointer, lest we stomp on spare VRAM while using the + * second buffer */ + guDrawBuffer(GU_PSM_8888, surfaces[work_surface], DISPLAY_STRIDE); + guDisable(GU_SCISSOR_TEST); + guClear(GU_COLOR_BUFFER_BIT); + guCommit(); + + /* Register the effective work surface pointer */ + guDrawBuffer(GU_PSM_8888, display_work_buffer(), DISPLAY_STRIDE); + + /* Set up drawing area parameters (we set the depth parameters too, + * just in case a custom drawing routine wants to use 3D coordinates) */ + guViewport(2048, 2048, display_width, display_height); + guOffset(2048 - display_width/2, 2048 - display_height/2); + guScissor(0, 0, display_width, display_height); + guEnable(GU_SCISSOR_TEST); + guDepthRange(65535, 0); + guDisable(GU_DEPTH_TEST); + +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_end_frame: End processing for the current frame, then swap the + * displayed buffer and work buffer. The current frame will not actually + * be shown until the next vertical blank. + * + * [Parameters] + * None + * [Return value] + * None + */ +void display_end_frame(void) +{ + guFinish(); + swap_pending = 1; + /* Give the new thread a slightly higher priority than us, or else it + * won't actually get a chance to run. */ + if (sys_start_thread("YabauseBufferSwapThread", buffer_swap_thread, + sceKernelGetThreadCurrentPriority() - 1, + 0x1000, 0, NULL) < 0) { + DMSG("Failed to start buffer swap thread"); + swap_pending = 0; + do_buffer_swap(); // Do it ourselves + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_last_frame_length: Returns the length of time the last frame + * was displayed, in hardware frame (1/59.94sec) units. Only valid between + * display_begin_frame() and display_end_frame(). + * + * [Parameters] + * None + * [Return value] + * Length of time the last frame was displayed + */ +unsigned int display_last_frame_length(void) +{ + return last_frame_length; +} + +/*************************************************************************/ + +/** + * display_blit: Draw an image to the display. The image must be in + * native 32bpp format. + * + * [Parameters] + * src: Source image data pointer + * src_width: Width of the source image, in pixels + * src_height: Height of the source image, in pixels + * src_stride: Line length of source image, in pixels + * dest_x: X coordinate at which to display image + * dest_y: Y coordinate at which to display image + * [Return value] + * None + */ +void display_blit(const void *src, int src_width, int src_height, + int src_stride, int dest_x, int dest_y) +{ +#if 1 // Method 1: DMA the data into VRAM (fastest) + + /* Pointer must be 64-byte aligned, so if it's not, adjust the pointer + * and add an offset to the X coordinate */ + const unsigned int xofs = ((uintptr_t)src & 0x3F) / 4; + const uint32_t * const src_aligned = (const uint32_t *)src - xofs; + + guCopyImage(GU_PSM_8888, xofs, 0, src_width, src_height, src_stride, + src_aligned, dest_x, dest_y, DISPLAY_STRIDE, + display_work_buffer()); + +#elif 1 // Method 2: Draw as a texture (slower, but more general) + + /* Pointer must be 64-byte aligned, so if it's not, adjust the pointer + * and add an offset to the X coordinate */ + const unsigned int xofs = ((uintptr_t)src & 0x3F) / 4; + const uint32_t * const src_aligned = (const uint32_t *)src - xofs; + + /* Set up the vertex array (it's faster to draw multiple vertical + * strips than try to blit the whole thing at once) */ + const int nstrips = (src_width + 15) / 16; + struct { + uint16_t u, v; + int16_t x, y, z; + } *vertices = guGetMemory(sizeof(*vertices) * (2*nstrips)); + /* Note that guGetMemory() never fails, so we don't check for failure */ + int i, x; + for (i = 0, x = 0; x < src_width; i += 2, x += 16) { + int thiswidth = 16; + if (x+16 > src_width) { + thiswidth = src_width - x; + } + vertices[i+0].u = xofs + x; + vertices[i+0].v = 0; + vertices[i+0].x = dest_x + x; + vertices[i+0].y = dest_y; + vertices[i+0].z = 0; + vertices[i+1].u = xofs + x + thiswidth; + vertices[i+1].v = src_height; + vertices[i+1].x = dest_x + x + thiswidth; + vertices[i+1].y = dest_y + src_height; + vertices[i+1].z = 0; + } + + /* Draw the array */ + guEnable(GU_TEXTURE_2D); + guTexFilter(GU_NEAREST, GU_NEAREST); + guTexWrap(GU_CLAMP, GU_CLAMP); + guTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + /* Always use a 512x512 texture size (the GE can only handle sizes that + * are powers of 2, and the size itself doesn't seem to affect speed) */ + guTexFlush(); + guTexMode(GU_PSM_8888, 0, 0, 0); + guTexImage(0, 512, 512, src_stride, src_aligned); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2*nstrips, NULL, vertices); + guDisable(GU_TEXTURE_2D); + +#else // Method 3: Copy using the CPU (slowest) + + const uint32_t *src32 = (const uint32_t *)src; + uint32_t *dest32 = display_work_buffer() + dest_y*DISPLAY_STRIDE + dest_x; + int y; + for (y = 0; y < src_height; + y++, src32 += src_stride, dest32 += DISPLAY_STRIDE + ) { + memcpy(dest32, src32, src_width*4); + } + +#endif +} + +/*-----------------------------------------------------------------------*/ + +/** + * display_blit_scaled: Scale and draw an image to the display. The image + * must be in native 32bpp format. + * + * [Parameters] + * src: Source image data pointer + * src_width: Width of the source image, in pixels + * src_height: Height of the source image, in pixels + * src_stride: Line length of source image, in pixels + * dest_x: X coordinate at which to display image + * dest_y: Y coordinate at which to display image + * dest_width: Width of the displayed image, in pixels + * dest_height: Height of the displayed image, in pixels + * [Return value] + * None + */ +void display_blit_scaled(const void *src, int src_width, int src_height, + int src_stride, int dest_x, int dest_y, + int dest_width, int dest_height) +{ + /* Pointer must be 64-byte aligned, so if it's not, adjust the pointer + * and add an offset to the X coordinate */ + const unsigned int xofs = ((uintptr_t)src & 0x3F) / 4; + const uint32_t * const src_aligned = (const uint32_t *)src - xofs; + + /* Set up the vertex array (4 vertices for a 2-triangle strip) */ + struct { + uint16_t u, v; + int16_t x, y, z; + } *vertices = guGetMemory(sizeof(*vertices) * 4); + vertices[0].u = xofs; + vertices[0].v = 0; + vertices[0].x = dest_x; + vertices[0].y = dest_y; + vertices[0].z = 0; + vertices[1].u = xofs; + vertices[1].v = src_height; + vertices[1].x = dest_x; + vertices[1].y = dest_y + dest_height; + vertices[1].z = 0; + vertices[2].u = xofs + src_width; + vertices[2].v = 0; + vertices[2].x = dest_x + dest_width; + vertices[2].y = dest_y; + vertices[2].z = 0; + vertices[3].u = xofs + src_width; + vertices[3].v = src_height; + vertices[3].x = dest_x + dest_width; + vertices[3].y = dest_y + dest_height; + vertices[3].z = 0; + + /* Draw the array */ + guEnable(GU_TEXTURE_2D); + guTexFilter(GU_LINEAR, GU_LINEAR); + guTexWrap(GU_CLAMP, GU_CLAMP); + guTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + guTexFlush(); + guTexMode(GU_PSM_8888, 0, 0, 0); + guTexImage(0, 512, 512, src_stride, src_aligned); + guDrawArray(GU_TRIANGLE_STRIP, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 4, NULL, vertices); + guDisable(GU_TEXTURE_2D); +} + +/*************************************************************************/ + +/** + * display_fill_box: Draw a filled box with a specified color. + * + * [Parameters] + * x1, y1: Upper-left coordinates of box + * x2, y2: Lower-right coordinates of box + * color: Color to fill with (0xAABBGGRR) + * [Return value] + * None + */ +void display_fill_box(int x1, int y1, int x2, int y2, uint32_t color) +{ + struct { + uint32_t color; + int16_t x, y, z, pad; + } *vertices = guGetMemory(sizeof(*vertices) * 2); + vertices[0].color = color; + vertices[0].x = x1; + vertices[0].y = y1; + vertices[0].z = 0; + vertices[1].color = color; + vertices[1].x = x2 + 1; + vertices[1].y = y2 + 1; + vertices[1].z = 0; + + guDrawArray(GU_SPRITES, + GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * buffer_swap_thread: Perform a buffer swap and clear the "swap_pending" + * variable to false. Designed to run as a background thread while the + * main emulation proceeds. + * + * [Parameters] + * args, argp: Thread argument size and pointer (unused) + * [Return value] + * Does not return + */ +static int buffer_swap_thread(SceSize args, void *argp) +{ + do_buffer_swap(); + swap_pending = 0; + sceKernelExitDeleteThread(0); +} + +/*----------------------------------*/ + +/** + * do_buffer_swap: Perform a display buffer swap (call guSync(), swap the + * display and work surfaces, wait for the following vertical blank, and + * calculate the length of time between this newly displayed frame and the + * previous one). Called either from the buffer swap thread or (if the + * swap thread fails to start) from the main thread. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void do_buffer_swap(void) +{ + guSync(0, 0); + sceDisplaySetFrameBuf(surfaces[work_surface], DISPLAY_STRIDE, + display_mode, PSP_DISPLAY_SETBUF_NEXTFRAME); + displayed_surface = work_surface; + work_surface = (work_surface + 1) % lenof(surfaces); + sceDisplayWaitVblankStart(); + + /* Update the frame length variables. If this is the first frame + * we've drawn (signaled by last_frame_start == 0), just set a frame + * length of 1 (1/60 sec) since we have nothing to compare it against. */ + const uint32_t now = sceKernelGetSystemTimeLow(); + const uint32_t last_frame_time = now - last_frame_start; + const uint32_t time_unit = (1001000+30)/60; + if (last_frame_start != 0) { + last_frame_length = (last_frame_time + time_unit/2) / time_unit; + } else { + last_frame_length = 1; + } + /* Make sure we don't accidentally signal the next frame as the + * first frame drawn. */ + last_frame_start = (now != 0) ? now : 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/display.h b/yabause/src/psp/display.h new file mode 100644 index 0000000000..7aa113205e --- /dev/null +++ b/yabause/src/psp/display.h @@ -0,0 +1,213 @@ +/* src/psp/display.h: PSP display management header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_DISPLAY_H +#define PSP_DISPLAY_H + +/*************************************************************************/ + +/* Physical screen layout */ +#define DISPLAY_WIDTH 480 +#define DISPLAY_HEIGHT 272 +#define DISPLAY_STRIDE 512 + +/*-----------------------------------------------------------------------*/ + +/** + * display_init: Initialize the PSP display. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +extern int display_init(void); + +/** + * display_set_size: Set the effective display size. The display will + * be centered on the PSP's screen. + * + * This routine should not be called while drawing a frame (i.e. between + * display_begin_frame() and display_end_frame()). + * + * [Parameters] + * width: Effective display width (pixels) + * height: Effective display height (pixels) + * [Return value] + * None + */ +extern void display_set_size(int width, int height); + +/** + * display_disp_buffer: Return a pointer to the current displayed (front) + * buffer. + * + * [Parameters] + * None + * [Return value] + * Pointer to the current work buffer + */ +extern uint32_t *display_disp_buffer(void); + +/** + * display_work_buffer: Return a pointer to the current work (back) + * buffer. + * + * [Parameters] + * None + * [Return value] + * Pointer to the current work buffer + */ +extern uint32_t *display_work_buffer(void); + +/** + * display_spare_vram: Return a pointer to the VRAM spare area. + * + * [Parameters] + * None + * [Return value] + * Pointer to the VRAM spare area + */ +extern void *display_spare_vram(void); + +/** + * display_spare_vram_size: Return the size of the VRAM spare area. + * + * [Parameters] + * None + * [Return value] + * Size of the VRAM spare area, in bytes + */ +extern uint32_t display_spare_vram_size(void); + +/*----------------------------------*/ + +/** + * display_alloc_vram: Allocate memory from the spare VRAM area, aligned + * to a multiple of 64 bytes. All allocated VRAM will be automatically + * freed at the next call to display_begin_frame(). + * + * [Parameters] + * size: Amount of memory to allocate, in bytes + * [Return value] + * Pointer to allocated memory, or NULL on failure (out of memory) + */ +extern void *display_alloc_vram(uint32_t size); + +/*----------------------------------*/ + +/** + * display_begin_frame: Begin processing for a frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void display_begin_frame(void); + +/** + * display_end_frame: End processing for the current frame, then swap the + * displayed buffer and work buffer. The current frame will not actually + * be shown until the next vertical blank. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void display_end_frame(void); + +/** + * display_last_frame_length: Returns the length of time the last frame + * was displayed, in hardware frame (1/59.94sec) units. Only valid between + * display_begin_frame() and display_end_frame(). + * + * [Parameters] + * None + * [Return value] + * Length of time the last frame was displayed + */ +extern unsigned int display_last_frame_length(void); + +/*----------------------------------*/ + +/** + * display_blit: Draw an image to the display. The image must be in + * native 32bpp format. + * + * [Parameters] + * src: Source image data pointer + * src_width: Width of the source image, in pixels + * src_height: Height of the source image, in pixels + * src_stride: Line length of source image, in pixels + * dest_x: X coordinate at which to display image + * dest_y: Y coordinate at which to display image + * [Return value] + * None + */ +extern void display_blit(const void *src, int src_width, int src_height, + int src_stride, int dest_x, int dest_y); + +/** + * display_blit_scaled: Scale and draw an image to the display. The image + * must be in native 32bpp format. + * + * [Parameters] + * src: Source image data pointer + * src_width: Width of the source image, in pixels + * src_height: Height of the source image, in pixels + * src_stride: Line length of source image, in pixels + * dest_x: X coordinate at which to display image + * dest_y: Y coordinate at which to display image + * dest_width: Width of the displayed image, in pixels + * dest_height: Height of the displayed image, in pixels + * [Return value] + * None + */ +extern void display_blit_scaled(const void *src, int src_width, int src_height, + int src_stride, int dest_x, int dest_y, + int dest_width, int dest_height); + +/** + * display_fill_box: Draw a filled box with a specified color. + * + * [Parameters] + * x1, y1: Upper-left coordinates of box + * x2, y2: Lower-right coordinates of box + * color: Color to fill with (0xAABBGGRR) + * [Return value] + * None + */ +extern void display_fill_box(int x1, int y1, int x2, int y2, uint32_t color); + +/*************************************************************************/ + +#endif // PSP_DISPLAY_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/filesel.c b/yabause/src/psp/filesel.c new file mode 100644 index 0000000000..6e60710242 --- /dev/null +++ b/yabause/src/psp/filesel.c @@ -0,0 +1,521 @@ +/* src/psp/filesel.c: Simple file selector for use with PSP menu + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "display.h" +#include "filesel.h" +#include "font.h" +#include "sys.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* File selector data structure (exported as opaque type FileSelector) */ + +struct FileSelector_ { + char *title; // Window title + + char *dir; // Current directory (note that we don't support + // changing directories at present) + SceUID scandir_thread; // If nonzero, thread ID of directory scan thread; + // if zero, indicates that scan is complete. + /* The file list MUST NOT be accessed by the main thread while + * scandir_thread is nonzero */ + char **file_list; // List of files in the current directory (each + // entry is malloc'd) + int num_files; // Number of files in file_list[]; negative + // indicates an error scanning the directory + + int top_file; // Index of top file shown in window + int selected_file; // Selected file index (negative = cancelled) + int num_lines; // Number of lines visible in file selector window + // (set after first filesel_draw() call) + uint8_t cursor_timer; // Frame counter for cursor flashing + + uint8_t done; // Nonzero when user selects a file or cancels +}; + +/*************************************************************************/ + +/* Display parameters */ + +/* Window size */ +#define WINDOW_WIDTH 360 +#define WINDOW_HEIGHT 208 + +/* Window colors */ +#define BGCOLOR_TITLE 0xFF804060 // Title bar +#define BGCOLOR_LIST 0xFF604040 // File list area +#define BORDER_COLOR_HI 0xFFFFECEC // Border lines (bright) +#define BORDER_COLOR_LO 0xFF000000 // Border lines (shadow) + +/* Cursor parameters */ +#define CURSOR_COLOR 0x80FFECEC +#define CURSOR_PERIOD 60 // frames + +/* Text colors */ +#define TEXTCOLOR_TITLE 0xFFFFECEC +#define TEXTCOLOR_FILE 0xFFFFECEC +#define TEXTCOLOR_DISABLED 0xFF807474 // "Scanning directory...", etc. +#define TEXTCOLOR_ERROR 0xFF5540FF + +/*************************************************************************/ + +/* Local function declarations */ + +static void scandir_thread(SceSize args, void *argp); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * filesel_create: Create a new file selector. + * + * [Parameters] + * title: File selector window title + * dir: Directory to select file from + * [Return value] + * Newly-created file selector, or NULL on error + */ +FileSelector *filesel_create(const char *title, const char *dir) +{ + PRECOND(title != NULL, goto error_return); + PRECOND(dir != NULL, goto error_return); + + FileSelector *filesel = malloc(sizeof(*filesel)); + if (UNLIKELY(!filesel)) { + DMSG("No memory for FileSelector"); + goto error_return; + } + filesel->title = strdup(title); + if (UNLIKELY(!filesel->title)) { + DMSG("No memory for title"); + goto error_free_filesel; + } + filesel->dir = strdup(dir); + if (UNLIKELY(!filesel->dir)) { + DMSG("No memory for current directory"); + goto error_free_title; + } + filesel->scandir_thread = 0; + filesel->file_list = NULL; + filesel->num_files = 0; + filesel->top_file = 0; + filesel->selected_file = 0; + filesel->num_lines = 0; + filesel->cursor_timer = 0; + filesel->done = 0; + + /* We only ever have one file selector open at once, so we just use a + * static thread name */ + int32_t thread = sys_start_thread("YabauseScanDirThread", scandir_thread, + THREADPRI_MAIN+1, 0x1000, + sizeof(filesel), &filesel); + if (thread & 0x80000000) { + DMSG("Failed to start scandir thread: %s", psp_strerror(thread)); + goto error_free_dir; + } + + filesel->scandir_thread = thread; + return filesel; + + error_free_dir: + free(filesel->dir); + error_free_title: + free(filesel->title); + error_free_filesel: + free(filesel); + error_return: + return NULL; +} + +/*************************************************************************/ + +/** + * filesel_process: Process input for a file selector. + * + * [Parameters] + * filesel: File selector + * buttons: Newly-pressed (or repeating) buttons (PSP_CTRL_* bitmask) + * [Return value] + * None + */ +void filesel_process(FileSelector *filesel, uint32_t buttons) +{ + PRECOND(filesel != NULL, return); + + /* If the user wants to cancel, give that top priority */ + if (buttons & PSP_CTRL_CROSS) { + filesel->selected_file = -1; + filesel->done = 1; + return; + } + + /* If the scanning thread is still running, there's nothing to do */ + if (filesel->scandir_thread) { + return; + } + + /* Update the cursor timer (assume we're called once per frame) */ + filesel->cursor_timer = (filesel->cursor_timer + 1) % CURSOR_PERIOD; + + /* If we have a file list, let the user move the cursor around or + * select a file; note that scrolling is handled at drawing time, when + * we know how many lines will fit in the file list display area */ + if (filesel->num_files > 0) { + if (buttons & PSP_CTRL_UP) { + if (filesel->selected_file > 0) { + filesel->selected_file--; + filesel->cursor_timer = 0; + } + } else if (buttons & PSP_CTRL_DOWN) { + if (filesel->selected_file < filesel->num_files - 1) { + filesel->selected_file++; + filesel->cursor_timer = 0; + } + } else if (buttons & PSP_CTRL_LEFT) { + /* Use the number of visible lines calculated in filesel_draw(). + * If we get here on the very first filesel_process() call, + * filesel->num_lines will be zero, so this becomes a no-op. */ + int num_to_scroll = filesel->num_lines; + if (num_to_scroll > filesel->top_file) { + num_to_scroll = filesel->top_file; + } + filesel->top_file -= num_to_scroll; + filesel->selected_file -= num_to_scroll; + filesel->cursor_timer = 0; + } else if (buttons & PSP_CTRL_RIGHT) { + int max_top = filesel->num_files - filesel->num_lines; + if (max_top < 0) { + max_top = 0; + } + int num_to_scroll = filesel->num_lines; + if (num_to_scroll > max_top - filesel->top_file) { + num_to_scroll = max_top - filesel->top_file; + } + filesel->top_file += num_to_scroll; + filesel->selected_file += num_to_scroll; + filesel->cursor_timer = 0; + } else if (buttons & PSP_CTRL_CIRCLE) { + filesel->done = 1; + } + } +} + +/*************************************************************************/ + +/** + * filesel_draw: Draw a file selector. + * + * [Parameters] + * filesel: File selector + * [Return value] + * None + */ +void filesel_draw(FileSelector *filesel) +{ + PRECOND(filesel != NULL, return); + + const int x1 = DISPLAY_WIDTH/2 - WINDOW_WIDTH/2; + const int y1 = DISPLAY_HEIGHT/2 - WINDOW_HEIGHT/2; + const int x2 = x1 + (WINDOW_WIDTH - 1); + const int y2 = y1 + (WINDOW_HEIGHT - 1); + + /* Draw the outside border */ + + display_fill_box(x1, y1, x2-1, y1, BORDER_COLOR_HI); + display_fill_box(x1+1, y1+1, x2, y1+1, BORDER_COLOR_LO); + display_fill_box(x1, y1+1, x1, y2-1, BORDER_COLOR_HI); + display_fill_box(x1+1, y1+2, x1+1, y2, BORDER_COLOR_LO); + display_fill_box(x2-1, y1+1, x2-1, y2-1, BORDER_COLOR_HI); + display_fill_box(x2, y2+2, x2, y2, BORDER_COLOR_LO); + display_fill_box(x1+1, y2-1, x2-2, y2-1, BORDER_COLOR_HI); + display_fill_box(x1, y2, x2-1, y2, BORDER_COLOR_LO); + + /* Draw the title bar */ + + const int title_text_y = y1+4; + const int title_y2 = title_text_y + FONT_HEIGHT + 2; + display_fill_box(x1+1, title_y2, x2-2, title_y2, BORDER_COLOR_HI); + display_fill_box(x1+2, title_y2+1, x2-1, title_y2+1, BORDER_COLOR_LO); + display_fill_box(x1+2, y1+2, x2-2, title_y2-2, BGCOLOR_TITLE); + font_printf((x1+x2)/2, title_text_y, 0, TEXTCOLOR_TITLE, + "%s", filesel->title); + + /* Draw the file list */ + + display_fill_box(x1+2, title_y2+2, x2-2, y2-2, BGCOLOR_LIST); + const int list_x1 = x1 + 4; + const int list_y1 = title_y2 + 4; + const int list_x2 = x2 - 4; + const int list_y2 = y2 - 4; + const int line_height = FONT_HEIGHT + 2; + const int num_lines = ((list_y2+1) - list_y1) / line_height; + filesel->num_lines = num_lines; // Save for reference in filesel_process() + int y = list_y1 + (((list_y2+1)-list_y1) - (num_lines*line_height)) / 2; + if (filesel->scandir_thread) { + font_printf(list_x1, y, -1, TEXTCOLOR_DISABLED, + "(Scanning directory...)"); + } else if (filesel->num_files < 0) { + font_printf(list_x1, y, -1, TEXTCOLOR_ERROR, + "(Error scanning directory!)"); + } else if (filesel->num_files == 0) { + font_printf(list_x1, y, -1, TEXTCOLOR_DISABLED, + "(No files found.)"); + } else { + /* Scroll the list if needed */ + if (filesel->selected_file < filesel->top_file) { + filesel->top_file = filesel->selected_file; + } else if (filesel->selected_file >= filesel->top_file + num_lines) { + filesel->top_file = filesel->selected_file - (num_lines-1); + } + /* List as many files as will fit in the window */ + int i; + for (i = filesel->top_file; + i < filesel->num_files && i < filesel->top_file + num_lines; + i++, y += line_height + ) { + // FIXME: handle overlength filenames in a more general way + const int maxlen = ((list_x2+1) - list_x1) / 6; + if (strlen(filesel->file_list[i]) > maxlen) { + font_printf(list_x1, y, -1, TEXTCOLOR_FILE, + "%.*s...", maxlen-3, filesel->file_list[i]); + } else { + font_printf(list_x1, y, -1, TEXTCOLOR_FILE, + "%s", filesel->file_list[i]); + } + if (filesel->selected_file == i) { + const float cursor_alpha = + (sinf((filesel->cursor_timer / (float)CURSOR_PERIOD) + * (float)M_TWOPI) + 1) / 2; + const uint32_t cursor_alpha_byte = + floorf((CURSOR_COLOR>>24 & 0xFF) * cursor_alpha + 0.5f); + display_fill_box(list_x1-1, y-1, list_x2+1, y+FONT_HEIGHT, + cursor_alpha_byte<<24 + | (CURSOR_COLOR & 0x00FFFFFF)); + } + } + } +} + +/*************************************************************************/ + +/** + * filesel_done: Return whether a file selector's work is done (i.e., + * whether the user has either selected a file or cancelled the selector). + * + * [Parameters] + * filesel: File selector + * [Return value] + * True (nonzero) if any of the following are true: + * - The user selected a file + * - The user cancelled the file selector + * - An error occurred which prevents the file selector's + * processing from continuing normally + * False (zero) if none of the above are true + */ +int filesel_done(FileSelector *filesel) +{ + PRECOND(filesel != NULL, return 1); + + return filesel->done; +} + +/*-----------------------------------------------------------------------*/ + +/** + * filesel_selected_file: Return the name of the file selected by the + * user, if any. + * + * [Parameters] + * filesel: File selector + * [Return value] + * The name of the file selected by the user, or NULL if the user has + * not selected a file (including if the user has cancelled the file + * selector) + */ +const char *filesel_selected_file(FileSelector *filesel) +{ + PRECOND(filesel != NULL, return NULL); + + if (filesel->done && filesel->selected_file >= 0) { + return filesel->file_list[filesel->selected_file]; + } else { + return NULL; + } +} + +/*************************************************************************/ + +/** + * filesel_destroy: Destroy a file selector. Does nothing if filesel==NULL. + * + * [Parameters] + * filesel: File selector to destroy + * [Return value] + * None + */ +void filesel_destroy(FileSelector *filesel) +{ + if (filesel) { + if (filesel->scandir_thread) { + sceKernelTerminateDeleteThread(filesel->scandir_thread); + } + int i; + for (i = 0; i < filesel->num_files; i++) { + free(filesel->file_list[i]); + } + free(filesel->file_list); + free(filesel->dir); + free(filesel->title); + free(filesel); + } +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * scandir_thread: Thread used to scan a directory for files. Updates the + * file_list[] and num_files fields of the passed-in FileSelector structure. + * + * [Parameters] + * args: Parameter size (must be sizeof(FileSelector *)) + * argp: Parameter pointer (must point to a valid FileSelector pointer) + * [Return value] + * Does not return (terminates and deletes thread when finished) + */ +static void scandir_thread(SceSize args, void *argp) +{ + PRECOND(args == sizeof(FileSelector *), goto exit_thread); + PRECOND(argp != NULL, goto exit_thread); + + FileSelector * const filesel = *(FileSelector **)argp; + + + int dirfd = sceIoDopen(filesel->dir); + if (dirfd < 0) { + DMSG("sceIoDopen(%s): %s", filesel->dir, psp_strerror(dirfd)); + goto signal_error; + } + + SceIoDirent dirent; + memset(&dirent, 0, sizeof(dirent)); + int res; + while ((res = sceIoDread(dirfd, &dirent)) > 0) { + + /* Ignore . (current directory) and .. (parent directory) entries */ + if (strcmp(dirent.d_name,".") == 0 || strcmp(dirent.d_name,"..") == 0){ + continue; + } + + /* Ignore all directories, since we don't support changing directory + * (we could subsume the above test into this one, but we leave them + * separate for clarity) */ + char pathbuf[1000]; + if (UNLIKELY(snprintf(pathbuf, sizeof(pathbuf), "%s/%s", filesel->dir, + dirent.d_name) >= sizeof(pathbuf))) { + DMSG("Pathname buffer overflow: %s/%s", filesel->dir, + dirent.d_name); + continue; + } + struct SceIoStat st; + memset(&st, 0, sizeof(st)); + res = sceIoGetstat(pathbuf, &st); + if (UNLIKELY(res < 0)) { + DMSG("sceIoGetstat(%s): %s", pathbuf, psp_strerror(res)); + continue; + } + if (FIO_S_ISDIR(st.st_mode)) { + continue; + } + + /* Make room for the new file in the array */ + char **new_file_list = + realloc(filesel->file_list, + sizeof(*filesel->file_list) * (filesel->num_files + 1)); + if (UNLIKELY(!new_file_list)) { + DMSG("No memory to expand file list to %d files", + filesel->num_files + 1); + goto clear_file_list; + } + filesel->file_list = new_file_list; + char *new_file = strdup(dirent.d_name); + if (UNLIKELY(!new_file)) { + DMSG("No memory to copy filename: %s", dirent.d_name); + goto clear_file_list; + } + + /* Insert the new file into the file list, keeping the list sorted. + * We don't expect to see very many files in a directory, so we + * don't bother with anything more complex than a linear search. */ + int i; + for (i = 0; i < filesel->num_files; i++) { + if (stricmp(new_file, filesel->file_list[i]) < 0) { + break; + } + } + if (i < filesel->num_files) { + memmove(&filesel->file_list[i+1], &filesel->file_list[i], + sizeof(*filesel->file_list) * (filesel->num_files - i)); + } + filesel->file_list[i] = new_file; + filesel->num_files++; + } + + res = sceIoDclose(dirfd); + if (res != 0) { + DMSG("sceIoDclose(%s): %s", filesel->dir, psp_strerror(dirfd)); + /* Not a critical error, so just let it slide */ + } + + filesel->scandir_thread = 0; + goto exit_thread; + + clear_file_list:; + int i; + for (i = 0; i < filesel->num_files; i++) { + free(filesel->file_list[i]); + } + free(filesel->file_list); + filesel->file_list = NULL; + signal_error: + filesel->num_files = -1; + filesel->scandir_thread = 0; + exit_thread: + sceKernelExitDeleteThread(0); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/filesel.h b/yabause/src/psp/filesel.h new file mode 100644 index 0000000000..9175866090 --- /dev/null +++ b/yabause/src/psp/filesel.h @@ -0,0 +1,114 @@ +/* src/psp/filesel.h: PSP file selector header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_FILESEL_H +#define PSP_FILESEL_H + +/*************************************************************************/ + +/* Data type of a file selector instance (contents are opaque) */ +typedef struct FileSelector_ FileSelector; + +/*-----------------------------------------------------------------------*/ + +/** + * filesel_create: Create a new file selector. + * + * [Parameters] + * title: File selector window title + * dir: Directory to select file from + * [Return value] + * Newly-created file selector, or NULL on error + */ +extern FileSelector *filesel_create(const char *title, const char *dir); + +/** + * filesel_process: Process input for a file selector. + * + * [Parameters] + * filesel: File selector + * buttons: Newly-pressed (or repeating) buttons (PSP_CTRL_* bitmask) + * [Return value] + * None + */ +extern void filesel_process(FileSelector *filesel, uint32_t buttons); + +/** + * filesel_draw: Draw a file selector. + * + * [Parameters] + * filesel: File selector + * [Return value] + * None + */ +extern void filesel_draw(FileSelector *filesel); + +/** + * filesel_done: Return whether a file selector's work is done (i.e., + * whether the user has either selected a file or cancelled the selector). + * + * [Parameters] + * filesel: File selector + * [Return value] + * True (nonzero) if any of the following are true: + * - The user selected a file + * - The user cancelled the file selector + * - An error occurred which prevents the file selector's + * processing from continuing normally + * False (zero) if none of the above are true + */ +extern int filesel_done(FileSelector *filesel); + +/** + * filesel_selected_file: Return the name of the file selected by the + * user, if any. + * + * [Parameters] + * filesel: File selector + * [Return value] + * The name of the file selected by the user, or NULL if the user has + * not selected a file (including if the user has cancelled the file + * selector) + */ +extern const char *filesel_selected_file(FileSelector *filesel); + +/** + * filesel_destroy: Destroy a file selector. Does nothing if filesel==NULL. + * + * [Parameters] + * filesel: File selector to destroy + * [Return value] + * None + */ +extern void filesel_destroy(FileSelector *filesel); + +/*************************************************************************/ + +#endif // PSP_FILESEL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/font.c b/yabause/src/psp/font.c new file mode 100644 index 0000000000..4bf141f0fd --- /dev/null +++ b/yabause/src/psp/font.c @@ -0,0 +1,389 @@ +/* src/psp/font.c: Simple font used for PSP menu (public-domain 6x13 font + from X.Org) + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "font.h" +#include "gu.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Character width (the font is fixed-width) */ +#define CHAR_WIDTH 6 + +/* First and last characters in font data */ +#define FONTDATA_FIRSTCHAR 32 +#define FONTDATA_LASTCHAR 127 + +/* Characters (not pixels!) in one row of font data */ +#define FONTDATA_STRIDE 32 + +/* Default character to use for out-of-range characters */ +#define FONTDATA_DEFCHAR '?' + +/* Font data definition (1 byte per pixel, 32 6x13 characters per row). + * This wastes 6 bits per pixel, but it's only 7k anyway so no big deal. */ + +static const __attribute__((aligned(64))) uint8_t fontdata[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1, + 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, + 0,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,0,1,1, + 1,1,1,0,0,1,1,1,0,0,1,1,1,1,1,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,0,0, + 0,0,0,0,0,0,0,0,1,2,0,0,0,1,2,1,2,0,0,1,0,1,0,0,0,1,1,1,1,0,1,0, + 1,0,1,2,0,1,0,0,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,0,1,2, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2, + 0,1,0,1,0,0,0,1,1,2,0,0,1,0,2,2,1,0,0,2,2,2,1,2,0,0,0,1,2,0,1,2, + 2,2,2,2,1,0,2,2,1,0,0,2,2,2,1,2,1,0,2,2,1,0,1,0,2,2,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,1,0,0,0,0,1,0,2,2,1,0, + 0,0,0,0,0,0,0,0,1,2,0,0,0,1,2,1,2,0,0,1,2,1,2,0,1,0,1,2,2,2,0,1, + 0,1,0,2,1,0,1,0,0,0,0,0,1,2,0,0,0,1,0,2,0,0,0,0,0,1,0,0,0,1,1,1, + 0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2, + 1,0,2,0,1,0,1,0,1,2,0,0,1,2,0,0,1,2,0,0,0,1,0,2,0,0,1,1,2,0,1,2, + 0,0,0,0,1,2,0,0,0,2,0,0,0,1,0,2,1,2,0,0,1,2,1,2,0,0,1,2,0,0,1,0, + 0,0,0,0,1,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,1,2,0,0,1,2, + 0,0,0,0,0,0,0,0,1,2,0,0,0,0,2,0,2,0,1,1,1,1,1,0,1,2,1,2,0,0,0,0, + 2,1,2,0,1,2,1,2,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,1,2,0,1,0,1,2, + 1,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0, + 1,2,0,0,1,2,0,2,1,2,0,0,0,2,0,0,1,2,0,0,1,0,2,0,0,1,0,1,2,0,1,2, + 1,1,0,0,1,2,0,0,0,0,0,0,0,1,2,0,1,2,0,0,1,2,1,2,0,0,1,2,0,1,1,1, + 0,0,0,1,1,1,0,0,0,1,0,2,0,0,1,1,1,1,1,0,0,0,0,1,0,0,0,2,0,0,1,2, + 0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,1,2,1,2,2,0,1,1,1,0,0,0,0, + 1,0,2,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,2,0,0,2,1,2, + 0,2,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,2,0, + 1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1,0,2,0,1,1,1,0,0,0,1,2,1,2,0,1,1, + 0,2,1,0,1,1,1,1,0,0,0,0,1,0,2,0,0,1,1,1,0,2,0,1,1,1,1,2,0,0,1,2, + 2,0,0,0,1,2,2,0,1,0,2,0,0,0,0,2,2,2,2,2,0,0,0,0,1,0,0,0,0,1,0,2, + 0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,2,1,0,0,1, + 0,2,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,2,0,0,0,0,2, + 0,0,0,2,1,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,1,0,2,0,0, + 1,2,0,0,1,2,0,0,1,2,0,0,0,0,1,0,2,0,0,0,2,2,1,0,1,0,2,1,2,0,0,2, + 2,0,1,2,1,2,2,2,1,0,0,0,1,2,0,0,1,0,2,2,1,0,0,0,2,2,1,2,0,0,0,2, + 0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1,0,2,0, + 0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,1,2,1,2,2,0,0,1,2,1,2,0,1, + 2,1,0,0,1,2,0,1,1,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,2,0,0,0,0,0, + 0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0, + 1,2,0,0,1,2,0,0,1,2,0,0,0,1,0,2,0,0,0,0,0,0,1,2,1,1,1,1,1,0,0,0, + 0,0,1,2,1,2,0,0,1,2,0,1,0,2,0,0,1,2,0,0,1,2,0,0,0,0,1,2,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,0,0,1,0,2,0,0,0,1,2,0,0, + 0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,2,1,2,0,1,1,1,1,0,2,1,0, + 1,0,1,0,1,2,0,1,2,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,2,0,0,0,0,0, + 0,0,0,0,0,2,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,2,0,0,0, + 0,1,0,1,0,2,0,0,1,2,0,0,1,0,2,0,0,0,1,0,0,0,1,2,0,2,2,1,2,2,1,0, + 0,0,1,2,1,2,0,0,1,2,0,1,2,0,0,0,1,2,0,0,1,2,1,0,0,0,1,2,0,0,1,0, + 0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,2,2,2,2,2,0,1,0,2,0,0,0,0,0,2,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,2,1,2,2,0,1,2, + 0,1,0,2,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,0,0,0,1,1,1,0,0,1,2,0,0,0,0, + 0,0,1,0,2,0,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,1,0,2,0,0,0,1,2,0,0,1, + 1,1,0,2,0,1,1,1,0,2,0,1,2,0,0,0,0,1,1,1,0,2,0,1,1,1,0,2,0,1,1,1, + 0,0,0,0,1,2,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2, + 0,0,2,0,0,0,2,2,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,2,0,0,0,0, + 0,0,0,2,0,0,0,2,2,2,2,2,0,2,2,2,2,2,0,0,2,2,2,0,0,0,0,0,2,0,0,0, + 2,2,2,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,2,2,2,0,0,0,2,2,2,0,0,0,1,2, + 2,0,0,1,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,1,1,1,1,0,0,1,1, + 1,1,1,0,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,1,1,0,0,0,0,1,1, + 1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,1,1,0,0, + 1,1,1,1,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,1,1,1,1,1,0,1,0, + 0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,1, + 1,0,0,1,2,2,2,0,1,0,0,0,0,0,0,0,2,1,2,0,0,0,1,0,0,0,0,0,0,0,0,0, + 1,0,2,2,1,0,0,1,0,1,0,0,0,1,2,2,1,0,1,0,2,2,1,0,0,1,2,2,1,0,1,2, + 2,2,2,2,1,2,2,2,2,2,1,0,2,2,1,0,1,2,0,0,1,2,0,0,1,2,2,0,0,0,0,1, + 2,2,1,2,0,0,1,2,1,2,0,0,0,0,1,2,0,0,1,2,1,1,0,0,1,2,1,0,2,2,1,0, + 1,2,2,2,1,0,1,0,2,2,1,0,1,2,2,2,1,0,1,0,2,2,1,0,0,2,1,2,2,2,1,2, + 0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,0,2,2,2, + 1,2,0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,1,2,0,0,1,0,1,0,0,0,0,0,0,0,0, + 1,2,0,0,1,2,1,0,2,0,1,0,0,1,2,0,1,2,1,2,0,0,0,2,0,1,2,0,1,2,1,2, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,2,1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,0,1,0,2,1,2,0,0,0,0,1,1,0,1,1,2,1,1,2,0,1,2,1,2,0,0,1,2, + 1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,0,2,0,0,1,2,0,0,1,2, + 0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,0,1,0,1,0,2,0,1,0,1,0,2,0,0,0,1, + 0,2,0,1,2,0,0,0,0,1,0,0,0,0,0,0,0,1,2,0,1,0,2,0,1,0,0,0,0,0,0,0, + 1,2,0,1,1,2,1,2,0,0,1,2,0,1,2,0,1,2,1,2,0,0,0,0,0,1,2,0,1,2,1,2, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,1,0,2,0,1,2,0,0,0,0,1,2,1,0,1,2,1,2,1,0,1,2,1,2,0,0,1,2, + 1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,0,0,0,0,1,2,0,0,1,2, + 0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,0,1,2,1,2,0,0,1,2,1,2,0,0,0,0,1, + 2,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0,1,2,0,0,2,0,0,0,2,0,0,0,0,0,0, + 1,2,1,0,1,2,1,2,0,0,1,2,0,1,1,1,0,2,1,2,0,0,0,0,0,1,2,0,1,2,1,1, + 1,1,0,0,1,1,1,1,0,0,1,2,0,0,0,0,1,1,1,1,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,1,0,2,0,0,1,2,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,1,2, + 1,1,1,1,0,2,1,2,0,0,1,2,1,1,1,1,0,2,0,1,1,1,0,0,0,0,1,2,0,0,1,2, + 0,0,1,2,0,1,0,1,0,2,1,2,1,0,1,2,0,0,1,0,2,0,0,0,1,0,2,0,0,0,1,0, + 2,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,1,2,1,2,1,1,1,1,1,2,0,1,2,2,1,0,1,2,0,0,0,0,0,1,2,0,1,2,1,2, + 2,2,2,0,1,2,2,2,2,0,1,2,0,1,1,0,1,2,2,2,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,1,0,0,0,1,2,0,0,0,0,1,2,0,2,1,2,1,2,0,1,1,2,1,2,0,0,1,2, + 1,2,2,2,2,0,1,2,0,0,1,2,1,2,1,2,2,0,0,0,2,2,1,0,0,0,1,2,0,0,1,2, + 0,0,1,2,0,1,2,1,2,0,1,2,1,2,1,2,0,1,0,1,0,0,0,0,1,2,0,0,0,1,0,2, + 0,0,0,1,2,0,0,0,0,0,0,1,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,1,1,0,2,1,2,2,2,1,2,0,1,2,0,1,2,1,2,0,0,0,0,0,1,2,0,1,2,1,2, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,2,1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,0,1,0,0,1,2,0,0,0,0,1,2,0,0,1,2,1,2,0,1,1,2,1,2,0,0,1,2, + 1,2,0,0,0,0,1,2,0,0,1,2,1,2,0,1,0,0,0,0,0,0,1,2,0,0,1,2,0,0,1,2, + 0,0,1,2,0,1,2,1,2,0,1,2,1,2,1,2,0,1,2,1,2,0,0,0,1,2,0,0,0,1,2,0, + 0,0,0,1,2,0,0,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,0,2,2,0,1,2,0,0,1,2,0,1,2,0,1,2,1,2,0,0,1,0,0,1,2,0,1,2,1,2, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,2,1,2,0,0,1,2,0,0,1,2,0,0,1,0,0,1, + 2,0,1,2,0,0,1,0,1,2,0,0,0,0,1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2, + 1,2,0,0,0,0,1,2,1,0,1,2,1,2,0,0,1,0,1,0,0,0,1,2,0,0,1,2,0,0,1,2, + 0,0,1,2,0,0,1,0,2,0,1,2,1,2,1,2,1,0,2,0,1,0,0,0,1,2,0,0,1,0,2,0, + 0,0,0,1,2,0,0,0,0,0,0,0,1,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,1,1,1,0,1,2,0,0,1,2,1,1,1,1,0,2,0,1,1,1,0,2,1,1,1,1,0,2,1,1, + 1,1,1,0,1,2,0,0,0,0,0,1,1,1,0,2,1,2,0,0,1,2,0,1,1,1,0,0,0,1,1,0, + 2,0,1,2,0,0,1,2,1,1,1,1,1,0,1,2,0,0,1,2,1,2,0,0,1,2,0,1,1,1,0,2, + 1,2,0,0,0,0,0,1,1,1,0,2,1,2,0,0,1,2,0,1,1,1,0,2,0,0,1,2,0,0,0,1, + 1,1,0,2,0,0,1,2,0,0,0,1,0,1,0,2,1,2,0,0,1,2,0,0,1,2,0,0,1,1,1,1, + 1,0,0,1,2,0,0,0,0,0,0,0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,2,2,2,0,2,0,0,0,2,0,2,2,2,2,0,0,0,2,2,2,0,0,2,2,2,2,0,0,2, + 2,2,2,2,0,2,0,0,0,0,0,0,2,2,2,0,0,2,0,0,0,2,0,0,2,2,2,0,0,0,2,2, + 0,0,0,2,0,0,0,2,0,2,2,2,2,2,0,2,0,0,0,2,0,2,0,0,0,2,0,0,2,2,2,0, + 0,2,0,0,0,0,0,0,2,2,1,0,0,2,0,0,0,2,0,0,2,2,2,0,0,0,0,2,0,0,0,0, + 2,2,2,0,0,0,0,2,0,0,0,0,2,0,2,0,0,2,0,0,0,2,0,0,0,2,0,0,0,2,2,2, + 2,2,0,1,1,1,0,0,0,0,0,0,0,2,0,1,1,1,2,0,0,0,0,0,0,0,1,1,1,1,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, + 0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,2,2,0,0,1,0,0,0,0,2,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0, + 0,0,0,0,0,1,0,2,1,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,0,0,0,0,0,0,1, + 0,0,1,2,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,0,1,0,1,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0, + 0,0,0,0,0,1,2,0,0,2,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0, + 2,0,1,2,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,1,2,0,1,0,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,2,0,1, + 1,1,0,0,0,1,2,0,0,0,0,1,1,1,0,0,1,2,1,1,0,0,0,1,1,0,0,0,0,0,1,1, + 0,0,1,2,0,1,0,0,0,0,1,2,0,0,1,1,0,1,0,0,1,0,1,1,0,0,0,1,1,1,0,0, + 1,1,1,1,0,0,0,1,1,1,1,0,1,0,1,1,0,0,0,1,1,1,0,0,1,1,1,1,0,0,1,0, + 0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,1, + 1,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,2,0,0,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,2,1,0,1,2,2,2,1,0,1,0,2,2,1,0,1,0,2,2,1,2,1,0, + 2,2,1,0,1,1,1,1,0,0,1,0,2,2,1,0,1,1,0,2,1,0,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,1,0,2,0,0,0,1,2,0,0,1,2,1,0,1,0,1,1,0,2,1,0,1,0,2,2,1,0, + 1,2,2,2,1,0,1,0,2,2,1,2,1,1,0,2,1,0,1,0,2,2,1,0,0,1,2,2,2,0,1,2, + 0,0,1,2,1,2,0,0,1,2,1,2,0,0,1,2,0,1,0,1,0,2,1,2,0,0,1,2,0,2,2,1, + 2,2,1,1,0,2,0,0,0,0,1,2,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,1,2,1,2,0,0,1,2,1,2,0,0,0,2,1,2,0,0,1,2,1,1, + 1,1,1,2,0,1,2,2,2,0,1,2,0,0,1,2,1,2,2,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,1,0,2,0,0,0,0,1,2,0,0,1,2,1,2,1,2,1,2,2,0,1,2,1,2,0,0,1,2, + 1,2,0,0,1,2,1,2,0,0,1,2,1,2,2,0,0,2,0,1,1,0,0,2,0,1,2,0,0,0,1,2, + 0,0,1,2,1,2,0,0,1,2,1,2,1,0,1,2,0,0,1,0,2,0,1,2,0,0,1,2,0,0,1,0, + 2,0,0,2,1,0,0,0,0,0,1,2,0,0,0,0,1,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0,2,2,1,2,1,2,0,0,1,2,1,2,0,0,0,0,1,2,0,0,1,2,1,2, + 2,2,2,2,0,1,2,0,0,0,1,2,0,0,1,2,1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,1,0,0,0,0,0,1,2,0,0,1,2,1,2,1,2,1,2,0,0,1,2,1,2,0,0,1,2, + 1,2,0,0,1,2,1,2,0,0,1,2,1,2,0,0,0,0,0,0,2,1,0,0,0,1,2,0,0,0,1,2, + 0,0,1,2,0,1,0,1,0,2,1,2,1,2,1,2,0,0,1,2,0,0,1,2,0,1,1,2,0,1,0,2, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,2,0,1,1,2,1,2,0,0,1,2,1,2,0,0,1,0,1,2,0,0,1,2,1,2, + 0,0,1,0,0,1,2,0,0,0,0,1,1,1,1,2,1,2,0,0,1,2,0,0,1,2,0,0,0,0,0,1, + 2,0,1,2,0,1,0,0,0,0,1,2,0,0,1,2,1,2,1,2,1,2,0,0,1,2,1,2,0,0,1,2, + 1,1,1,1,0,2,0,1,1,1,1,2,1,2,0,0,0,0,1,0,0,0,1,0,0,1,2,0,1,0,1,2, + 0,1,1,2,0,1,2,1,2,0,1,2,1,2,1,2,0,1,0,1,0,0,0,1,1,0,1,2,1,0,2,0, + 0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,0,1,2,1,1,1,1,0,2,0,1,1,1,0,2,0,1,1,1,1,2,0,1, + 1,1,0,2,0,1,2,0,0,0,0,0,2,2,1,2,1,2,0,0,1,2,0,1,1,1,0,0,1,0,0,1, + 2,0,1,2,0,0,1,0,0,1,1,1,0,0,1,2,0,2,1,2,1,2,0,0,1,2,0,1,1,1,0,2, + 1,2,2,2,2,0,0,0,2,2,1,2,1,2,0,0,0,0,0,1,1,1,0,2,0,0,1,1,0,2,0,1, + 1,0,1,2,0,0,1,0,2,0,0,1,0,1,0,2,1,0,2,0,1,0,0,0,2,2,1,2,1,1,1,1, + 1,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,2,0,2,0,2,2,2,2,0,0,0,2,2,2,0,0,0,2,2,2,2,0,0, + 2,2,2,0,0,0,2,0,0,0,1,0,0,0,1,2,0,2,0,0,0,2,0,0,2,2,2,0,1,2,0,1, + 2,0,0,2,0,0,0,2,0,0,2,2,2,0,0,2,0,0,0,2,0,2,0,0,0,2,0,0,2,2,2,0, + 1,2,0,0,0,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,2,2,2,0,0,0,0,2,2,0,0,0, + 2,2,0,2,0,0,0,2,0,0,0,0,2,0,2,0,0,2,0,0,0,2,1,0,0,0,1,2,0,2,2,2, + 2,2,0,0,0,1,1,0,0,0,0,2,0,0,1,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0, + 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,0,0, + 0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * font_printf: Draw text to the screen at the given position and with the + * given color. The string can contain printf()-style format specifiers. + * + * It is assumed that a display list has been started with guStart() + * before calling this function. + * + * [Parameters] + * x, y: Upper-left coordinates of first character + * align: Horizontal alignment (<0: left, 0: center, >0: right) + * color: Text color (0xAABBGGRR) + * format: Format string for text + * ...: Arguments for format string + * [Return value] + * X coordinate immediately following the last character printed + */ +int font_printf(int x, int y, int align, uint32_t color, + const char *format, ...) +{ + PRECOND(format != NULL, return x); + + /* Generate the text string to output */ + char buf[1000]; // Plenty long enough even if we start offscreen + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + const int nchars = strlen(buf); + + /* Adjust the starting X coordinate based on the alignment */ + if (align == 0) { + x -= (nchars * CHAR_WIDTH + 1) / 2; + } else if (align > 0) { + x -= nchars * CHAR_WIDTH; + } + + /* Generate vertices for each character of the string */ + struct { + uint16_t u, v; + int16_t x, y, z; + } *vertices = guGetMemory(sizeof(*vertices) * (2*nchars)); + int i; + for (i = 0; i < nchars; i++, x += CHAR_WIDTH) { + uint8_t ch = buf[i]; + if (ch < FONTDATA_FIRSTCHAR || ch > FONTDATA_LASTCHAR) { + ch = FONTDATA_DEFCHAR; + } + const unsigned int index = ch - FONTDATA_FIRSTCHAR; + vertices[i*2+0].u = (index % FONTDATA_STRIDE) * CHAR_WIDTH; + vertices[i*2+0].v = (index / FONTDATA_STRIDE) * FONT_HEIGHT; + vertices[i*2+0].x = x; + vertices[i*2+0].y = y; + vertices[i*2+0].z = 0; + vertices[i*2+1].u = (index % FONTDATA_STRIDE + 1) * CHAR_WIDTH; + vertices[i*2+1].v = (index / FONTDATA_STRIDE + 1) * FONT_HEIGHT; + vertices[i*2+1].x = x + CHAR_WIDTH; + vertices[i*2+1].y = y + FONT_HEIGHT; + vertices[i*2+1].z = 0; + } + sceKernelDcacheWritebackInvalidateRange(vertices, + sizeof(*vertices) * (2*nchars)); + + /* Set up drawing parameters and draw the string. Unfortunately, color + * lookup tables have to be 64-byte aligned, so we can potentially + * waste a lot of memory here for just 3 color table entries... */ + uint32_t *clut = guGetMemory(3*4 + 60); + clut = (uint32_t *)(((uintptr_t)clut + 63) & -64); + clut[0] = 0x00000000; + clut[1] = color; + clut[2] = color & 0xFF000000; // Shadow pixels + guClutMode(GU_PSM_8888, 0, 0xFF, 0); + guClutLoad(8/8, clut); // We only use 3 of the 8 colors, but we have to + // send in a whole block + guEnable(GU_TEXTURE_2D); + guTexFlush(); + guTexMode(GU_PSM_T8, 0, 0, 0); + guTexImage(0, 512, 512, (FONTDATA_STRIDE * CHAR_WIDTH), fontdata); + guTexFilter(GU_NEAREST, GU_NEAREST); + guTexWrap(GU_CLAMP, GU_CLAMP); + guTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + guAmbientColor(0xFFFFFFFF); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2*nchars, NULL, vertices); + guDisable(GU_TEXTURE_2D); + + /* Return the next X coordinate for potential further drawing */ + return x; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/font.h b/yabause/src/psp/font.h new file mode 100644 index 0000000000..217132f9d0 --- /dev/null +++ b/yabause/src/psp/font.h @@ -0,0 +1,64 @@ +/* src/psp/font.h: Header for PSP menu font + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_FONT_H +#define PSP_FONT_H + +/*************************************************************************/ + +/* Height of the font, in pixels */ + +#define FONT_HEIGHT 13 + +/*-----------------------------------------------------------------------*/ + +/** + * font_printf: Draw text to the screen at the given position and with the + * given color. The string can contain printf()-style format specifiers. + * + * It is assumed that a display list has been started with sceGuStart() + * before calling this function. + * + * [Parameters] + * x, y: Upper-left coordinates of first character + * align: Horizontal alignment (<0: left, 0: center, >0: right) + * color: Text color (0xAABBGGRR) + * format: Format string for text + * ...: Arguments for format string + * [Return value] + * X coordinate immediately following the last character printed + */ +extern int font_printf(int x, int y, int align, uint32_t color, + const char *format, ...) + __attribute__((format(printf,5,6))); + +/*************************************************************************/ + +#endif // PSP_FONT_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/gu.c b/yabause/src/psp/gu.c new file mode 100644 index 0000000000..936b15093c --- /dev/null +++ b/yabause/src/psp/gu.c @@ -0,0 +1,488 @@ +/* src/psp/gu.c: sceGu substitute library + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "gu.h" + +/*************************************************************************/ + +#ifndef USE_SCEGU // To the end of the file + +/*************************************************************************/ +/************************** Library-local data ***************************/ +/*************************************************************************/ + +/**** Data exported to inline functions in gu.h ****/ + +/* Current display list pointer */ +uint32_t *gu_list; + +/* Scissor status flag and test region (in GE command form) */ +uint32_t gu_scissorcmd_min, gu_scissorcmd_max; +uint8_t gu_scissor_enabled; + +/* Color/stencil (combined) value and depth value for clear operations */ +uint16_t gu_clear_depth; +uint32_t gu_clear_color_stencil; + +/*----------------------------------*/ + +/**** Data used only within this file ****/ + +/* Nonzero if we're writing a sublist, zero if we're writing the main list */ +static uint8_t gu_is_sublist; + +/* Saved display list pointer (during sublist generation, the main list + * pointer is pushed here) */ +static uint32_t *gu_saved_list; + +/* sceGe ID for current display list */ +static uint32_t gu_list_id; + +/*************************************************************************/ +/*********** Library functions (which aren't defined in gu.h) ************/ +/*************************************************************************/ + +/** + * guInit: Initialize the GU library. + * + * [Parameters] + * None + * [Return value] + * None + */ +void guInit(void) +{ + static const uint32_t ge_init_list[] = { + GECMD_VERTEX_POINTER<<24 | 0x000000, + GECMD_INDEX_POINTER <<24 | 0x000000, + GECMD_ADDRESS_BASE <<24 | 0x000000, + GECMD_VERTEX_FORMAT <<24 | 0x000000, + GECMD_UNKNOWN_13 <<24 | 0x000000, + GECMD_DRAWAREA_LOW <<24 | 0x000000, + GECMD_DRAWAREA_HIGH <<24 | 0x000000, + GECMD_ENA_LIGHTING <<24 | 0x000000, + GECMD_ENA_LIGHT0 <<24 | 0x000000, + GECMD_ENA_LIGHT1 <<24 | 0x000000, + GECMD_ENA_LIGHT2 <<24 | 0x000000, + GECMD_ENA_LIGHT3 <<24 | 0x000000, + GECMD_ENA_ZCLIP <<24 | 0x000000, + GECMD_ENA_FACE_CULL <<24 | 0x000000, + GECMD_ENA_TEXTURE <<24 | 0x000000, + GECMD_ENA_FOG <<24 | 0x000000, + GECMD_ENA_DITHER <<24 | 0x000000, + GECMD_ENA_BLEND <<24 | 0x000000, + GECMD_ENA_ALPHA_TEST<<24 | 0x000000, + GECMD_ENA_DEPTH_TEST<<24 | 0x000000, + GECMD_ENA_STENCIL <<24 | 0x000000, + GECMD_ENA_ANTIALIAS <<24 | 0x000000, + GECMD_ENA_PATCH_CULL<<24 | 0x000000, + GECMD_ENA_COLOR_TEST<<24 | 0x000000, + GECMD_ENA_LOGIC_OP <<24 | 0x000000, + GECMD_BONE_OFFSET <<24 | 0x000000, + GECMD_BONE_UPLOAD <<24 | 0x000000, + GECMD_MORPH_0 <<24 | 0x000000, + GECMD_MORPH_1 <<24 | 0x000000, + GECMD_MORPH_2 <<24 | 0x000000, + GECMD_MORPH_3 <<24 | 0x000000, + GECMD_MORPH_4 <<24 | 0x000000, + GECMD_MORPH_5 <<24 | 0x000000, + GECMD_MORPH_6 <<24 | 0x000000, + GECMD_MORPH_7 <<24 | 0x000000, + GECMD_PATCH_SUBDIV <<24 | 0x000000, + GECMD_PATCH_PRIM <<24 | 0x000000, + GECMD_PATCH_FRONT <<24 | 0x000000, + GECMD_MODEL_START <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x3F8000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x3F8000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x3F8000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_MODEL_UPLOAD <<24 | 0x000000, + GECMD_VIEW_START <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x3F8000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x3F8000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x3F8000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_VIEW_UPLOAD <<24 | 0x000000, + GECMD_PROJ_START <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x3F8000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x3F8000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x3F8000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x000000, + GECMD_PROJ_UPLOAD <<24 | 0x3F8000, + GECMD_TEXTURE_START <<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x3F8000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x3F8000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x3F8000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_TEXTURE_UPLOAD<<24 | 0x000000, + GECMD_XSCALE <<24 | 0x000000, + GECMD_YSCALE <<24 | 0x000000, + GECMD_ZSCALE <<24 | 0x000000, + GECMD_XPOS <<24 | 0x000000, + GECMD_YPOS <<24 | 0x000000, + GECMD_ZPOS <<24 | 0x000000, + GECMD_USCALE <<24 | 0x3F8000, + GECMD_VSCALE <<24 | 0x3F8000, + GECMD_UOFFSET <<24 | 0x000000, + GECMD_VOFFSET <<24 | 0x000000, + GECMD_XOFFSET <<24 | 0x000000, + GECMD_YOFFSET <<24 | 0x000000, + GECMD_SHADE_MODE <<24 | 0x000000, + GECMD_REV_NORMALS <<24 | 0x000000, + GECMD_COLOR_MATERIAL<<24 | 0x000000, + GECMD_EMISSIVE_COLOR<<24 | 0x000000, + GECMD_AMBIENT_COLOR <<24 | 0x000000, + GECMD_DIFFUSE_COLOR <<24 | 0x000000, + GECMD_SPECULAR_COLOR<<24 | 0x000000, + GECMD_AMBIENT_ALPHA <<24 | 0x000000, + GECMD_SPECULAR_POWER<<24 | 0x000000, + GECMD_LIGHT_AMBCOLOR<<24 | 0x000000, + GECMD_LIGHT_AMBALPHA<<24 | 0x000000, + GECMD_LIGHT_MODEL <<24 | 0x000000, + GECMD_LIGHT0_TYPE <<24 | 0x000000, + GECMD_LIGHT1_TYPE <<24 | 0x000000, + GECMD_LIGHT2_TYPE <<24 | 0x000000, + GECMD_LIGHT3_TYPE <<24 | 0x000000, + GECMD_LIGHT0_XPOS <<24 | 0x000000, + GECMD_LIGHT0_YPOS <<24 | 0x000000, + GECMD_LIGHT0_ZPOS <<24 | 0x000000, + GECMD_LIGHT1_XPOS <<24 | 0x000000, + GECMD_LIGHT1_YPOS <<24 | 0x000000, + GECMD_LIGHT1_ZPOS <<24 | 0x000000, + GECMD_LIGHT2_XPOS <<24 | 0x000000, + GECMD_LIGHT2_YPOS <<24 | 0x000000, + GECMD_LIGHT2_ZPOS <<24 | 0x000000, + GECMD_LIGHT3_XPOS <<24 | 0x000000, + GECMD_LIGHT3_YPOS <<24 | 0x000000, + GECMD_LIGHT3_ZPOS <<24 | 0x000000, + GECMD_LIGHT0_XDIR <<24 | 0x000000, + GECMD_LIGHT0_YDIR <<24 | 0x000000, + GECMD_LIGHT0_ZDIR <<24 | 0x000000, + GECMD_LIGHT1_XDIR <<24 | 0x000000, + GECMD_LIGHT1_YDIR <<24 | 0x000000, + GECMD_LIGHT1_ZDIR <<24 | 0x000000, + GECMD_LIGHT2_XDIR <<24 | 0x000000, + GECMD_LIGHT2_YDIR <<24 | 0x000000, + GECMD_LIGHT2_ZDIR <<24 | 0x000000, + GECMD_LIGHT3_XDIR <<24 | 0x000000, + GECMD_LIGHT3_YDIR <<24 | 0x000000, + GECMD_LIGHT3_ZDIR <<24 | 0x000000, + GECMD_LIGHT0_CATT <<24 | 0x000000, + GECMD_LIGHT0_LATT <<24 | 0x000000, + GECMD_LIGHT0_QATT <<24 | 0x000000, + GECMD_LIGHT1_CATT <<24 | 0x000000, + GECMD_LIGHT1_LATT <<24 | 0x000000, + GECMD_LIGHT1_QATT <<24 | 0x000000, + GECMD_LIGHT2_CATT <<24 | 0x000000, + GECMD_LIGHT2_LATT <<24 | 0x000000, + GECMD_LIGHT2_QATT <<24 | 0x000000, + GECMD_LIGHT3_CATT <<24 | 0x000000, + GECMD_LIGHT3_LATT <<24 | 0x000000, + GECMD_LIGHT3_QATT <<24 | 0x000000, + GECMD_LIGHT0_SPOTEXP<<24 | 0x000000, + GECMD_LIGHT1_SPOTEXP<<24 | 0x000000, + GECMD_LIGHT2_SPOTEXP<<24 | 0x000000, + GECMD_LIGHT3_SPOTEXP<<24 | 0x000000, + GECMD_LIGHT0_SPOTLIM<<24 | 0x000000, + GECMD_LIGHT1_SPOTLIM<<24 | 0x000000, + GECMD_LIGHT2_SPOTLIM<<24 | 0x000000, + GECMD_LIGHT3_SPOTLIM<<24 | 0x000000, + GECMD_LIGHT0_ACOL <<24 | 0x000000, + GECMD_LIGHT0_DCOL <<24 | 0x000000, + GECMD_LIGHT0_SCOL <<24 | 0x000000, + GECMD_LIGHT1_ACOL <<24 | 0x000000, + GECMD_LIGHT1_DCOL <<24 | 0x000000, + GECMD_LIGHT1_SCOL <<24 | 0x000000, + GECMD_LIGHT2_ACOL <<24 | 0x000000, + GECMD_LIGHT2_DCOL <<24 | 0x000000, + GECMD_LIGHT2_SCOL <<24 | 0x000000, + GECMD_LIGHT3_ACOL <<24 | 0x000000, + GECMD_LIGHT3_DCOL <<24 | 0x000000, + GECMD_LIGHT3_SCOL <<24 | 0x000000, + GECMD_FACE_ORDER <<24 | 0x000000, + GECMD_DRAW_ADDRESS <<24 | 0x000000, + GECMD_DRAW_STRIDE <<24 | 0x000000, + GECMD_DEPTH_ADDRESS <<24 | 0x000000, + GECMD_DEPTH_STRIDE <<24 | 0x000000, + GECMD_TEX0_ADDRESS <<24 | 0x000000, + GECMD_TEX1_ADDRESS <<24 | 0x000000, + GECMD_TEX2_ADDRESS <<24 | 0x000000, + GECMD_TEX3_ADDRESS <<24 | 0x000000, + GECMD_TEX4_ADDRESS <<24 | 0x000000, + GECMD_TEX5_ADDRESS <<24 | 0x000000, + GECMD_TEX6_ADDRESS <<24 | 0x000000, + GECMD_TEX7_ADDRESS <<24 | 0x000000, + GECMD_TEX0_STRIDE <<24 | 0x040004, + GECMD_TEX1_STRIDE <<24 | 0x000000, + GECMD_TEX2_STRIDE <<24 | 0x000000, + GECMD_TEX3_STRIDE <<24 | 0x000000, + GECMD_TEX4_STRIDE <<24 | 0x000000, + GECMD_TEX5_STRIDE <<24 | 0x000000, + GECMD_TEX6_STRIDE <<24 | 0x000000, + GECMD_TEX7_STRIDE <<24 | 0x000000, + GECMD_CLUT_ADDRESS_L<<24 | 0x000000, + GECMD_CLUT_ADDRESS_H<<24 | 0x000000, + GECMD_COPY_S_ADDRESS<<24 | 0x000000, + GECMD_COPY_S_STRIDE <<24 | 0x000000, + GECMD_COPY_D_ADDRESS<<24 | 0x000000, + GECMD_COPY_D_STRIDE <<24 | 0x000000, + GECMD_TEX0_SIZE <<24 | 0x000101, + GECMD_TEX1_SIZE <<24 | 0x000000, + GECMD_TEX2_SIZE <<24 | 0x000000, + GECMD_TEX3_SIZE <<24 | 0x000000, + GECMD_TEX4_SIZE <<24 | 0x000000, + GECMD_TEX5_SIZE <<24 | 0x000000, + GECMD_TEX6_SIZE <<24 | 0x000000, + GECMD_TEX7_SIZE <<24 | 0x000000, + GECMD_TEXTURE_MAP <<24 | 0x000000, + GECMD_TEXTURE_ENVMAP<<24 | 0x000000, + GECMD_TEXTURE_MODE <<24 | 0x000000, + GECMD_TEXTURE_PIXFMT<<24 | 0x000000, + GECMD_CLUT_LOAD <<24 | 0x000000, + GECMD_CLUT_MODE <<24 | 0x000000, + GECMD_TEXTURE_FILTER<<24 | 0x000000, + GECMD_TEXTURE_WRAP <<24 | 0x000000, + GECMD_TEXTURE_BIAS <<24 | 0x000000, + GECMD_TEXTURE_FUNC <<24 | 0x000000, + GECMD_TEXTURE_COLOR <<24 | 0x000000, + GECMD_TEXTURE_FLUSH <<24 | 0x000000, + GECMD_COPY_SYNC <<24 | 0x000000, + GECMD_FOG_LIMIT <<24 | 0x000000, + GECMD_FOG_RANGE <<24 | 0x000000, + GECMD_FOG_COLOR <<24 | 0x000000, + GECMD_TEXTURE_SLOPE <<24 | 0x000000, + GECMD_FRAME_PIXFMT <<24 | 0x000000, + GECMD_CLEAR_MODE <<24 | 0x000000, + GECMD_CLIP_MIN <<24 | 0x000000, + GECMD_CLIP_MAX <<24 | 0x000000, + GECMD_CLIP_NEAR <<24 | 0x000000, + GECMD_CLIP_FAR <<24 | 0x000000, + GECMD_COLORTEST_FUNC<<24 | 0x000000, + GECMD_COLORTEST_REF <<24 | 0x000000, + GECMD_COLORTEST_MASK<<24 | 0x000000, + GECMD_ALPHATEST <<24 | 0x000000, + GECMD_STENCILTEST <<24 | 0x000000, + GECMD_STENCIL_OP <<24 | 0x000000, + GECMD_DEPTHTEST <<24 | 0x000000, + GECMD_BLEND_FUNC <<24 | 0x000000, + GECMD_BLEND_SRCFIX <<24 | 0x000000, + GECMD_BLEND_DSTFIX <<24 | 0x000000, + GECMD_DITHER0 <<24 | 0x000000, + GECMD_DITHER1 <<24 | 0x000000, + GECMD_DITHER2 <<24 | 0x000000, + GECMD_DITHER3 <<24 | 0x000000, + GECMD_LOGIC_OP <<24 | 0x000000, + GECMD_DEPTH_MASK <<24 | 0x000000, + GECMD_COLOR_MASK <<24 | 0x000000, + GECMD_ALPHA_MASK <<24 | 0x000000, + GECMD_COPY_S_POS <<24 | 0x000000, + GECMD_COPY_D_POS <<24 | 0x000000, + GECMD_COPY_SIZE <<24 | 0x000000, + GECMD_UNKNOWN_F0 <<24 | 0x000000, + GECMD_UNKNOWN_F1 <<24 | 0x000000, + GECMD_UNKNOWN_F2 <<24 | 0x000000, + GECMD_UNKNOWN_F3 <<24 | 0x000000, + GECMD_UNKNOWN_F4 <<24 | 0x000000, + GECMD_UNKNOWN_F5 <<24 | 0x000000, + GECMD_UNKNOWN_F6 <<24 | 0x000000, + GECMD_UNKNOWN_F7 <<24 | 0x000000, + GECMD_UNKNOWN_F8 <<24 | 0x000000, + GECMD_UNKNOWN_F9 <<24 | 0x000000, + GECMD_FINISH <<24 | 0x000000, + GECMD_END <<24 | 0x000000, + GECMD_NOP <<24 | 0x000000, + GECMD_NOP <<24 | 0x000000, + }; + + int listid = sceGeListEnQueue(ge_init_list, NULL, 0, 0); + sceGeListSync(listid, PSP_GE_LIST_DONE); +} + +/*************************************************************************/ + +/** + * guStart: Start a new display list. + * + * [Parameters] + * type: List type (GU_DIRECT or GU_CALL) + * list: List pointer + */ +void guStart(const int type, void * const list) +{ + if (type == GU_CALL) { + gu_saved_list = gu_list; + gu_is_sublist = 1; + } else { + gu_is_sublist = 0; + gu_list_id = sceGeListEnQueue(list, list, 0, 0); + } + gu_list = list; +} + +/*-----------------------------------------------------------------------*/ + +/** + * guFinish: End the current display list. + * + * [Parameters] + * None + * [Return value] + * None + */ +void guFinish(void) +{ + if (gu_is_sublist) { + *gu_list = GECMD_RETURN<<24; + gu_list = gu_saved_list; + gu_saved_list = NULL; + gu_is_sublist = 0; + } else { + *gu_list++ = GECMD_FINISH<<24; + *gu_list++ = GECMD_END<<24; + guCommit(); + gu_list = NULL; + } +} + +/*************************************************************************/ + +/** + * guCommit: Commit all previously-added commands to memory and start + * GE processing. Does nothing if the current display list is not a + * GU_DIRECT list. + * + * [Parameters] + * None + * [Return value] + * None + */ +void guCommit(void) +{ + if (gu_list && !gu_is_sublist) { + /* If a partially-used cache line hangs around until the next frame + * and the next frame's display list is different, we could end up + * clobbering the display list with old data, so invalidate all the + * cache lines we touched in addition to writing them back to + * memory. */ + sceKernelDcacheWritebackInvalidateAll(); + sceGeListUpdateStallAddr(gu_list_id, gu_list); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * guSync: Wait for GE processing to complete. + * + * [Parameters] + * mode: Synchronization mode (GU_SYNC_*) + * target: Synchronization target (GU_SYNC_WHAT_*) + * [Return value] + * None + */ +void guSync(const int mode, const int target) +{ + if (gu_list_id) { + if (mode != GU_SYNC_FINISH || target != GU_SYNC_WHAT_DONE) { + return; + } + sceGeDrawSync(PSP_GE_LIST_DONE); + sceGeListDeQueue(gu_list_id); + gu_list_id = 0; + } +} + +/*************************************************************************/ + +/** + * guGetMemory: Allocate a block of memory from the current GU_DIRECT + * list. The allocation size will automatically be rounded up to a + * multiple of 4 bytes. + * + * This function may not be called unless there is a current GU_DIRECT + * list. + * + * [Parameters] + * size: Number of bytes to allocate + */ +void *guGetMemory(const uint32_t size) +{ + uint32_t **list_ref = (gu_is_sublist ? &gu_saved_list : &gu_list); + const uint32_t alloc_words = (size + 3) / 4; + uint32_t * const alloc_ptr = (*list_ref) + 2; + uint32_t * const target = alloc_ptr + alloc_words; + (*list_ref)[0] = GECMD_ADDRESS_BASE<<24 + | ((uint32_t)target & 0xFF000000) >> 8; + (*list_ref)[1] = GECMD_JUMP<<24 + | ((uint32_t)target & 0x00FFFFFF); + (*list_ref) = target; + return alloc_ptr; +} + +/*************************************************************************/ + +#endif // USE_SCEGU + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/gu.h b/yabause/src/psp/gu.h new file mode 100644 index 0000000000..87e42cd178 --- /dev/null +++ b/yabause/src/psp/gu.h @@ -0,0 +1,1006 @@ +/* src/psp/gu.h: sceGu substitute library header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_GU_H +#define PSP_GU_H + +/* + * This library defines a substitute set of functions for the sceGu library + * used for manipulating display lists. These substitute functions are + * designed for maximum efficiency when dynamically generating display + * lists, and improve on the sceGu library in the following ways: + * + * - The sceGu library functions (at least as implemented in PSPSDK r2450) + * are designed to be robust against data corruption by using uncached + * memory accesses, but such accesses naturally slow the program down + * significantly. The functions defined here avoid this slowdown by + * making judicious use of the CPU's cache, allowing the CPU to access + * memory in cache line units instead of individual words. + * + * - The sceGuDrawArray() function triggers the GE to resume processing + * after writing instructions to the display list. In theory, this can + * improve performance by maximizing parallelism between the GE and the + * main Allegrex CPU; however, when constructing dynamic display lists + * which may by necessity include frequent DrawArray() calls, the + * overhead of synchronization far outweighs any performance gain. This + * library defines a guCommit() function allowing the caller to specify + * exactly when to trigger the GE, and does not do so on its own (except + * when calling guFinish() on a GU_DIRECT display list). + * + * - The sceGuDrawArray also requires the vertex type, vertex pointer, + * vertex count, and primitive type to all be specified in a single + * call. This can waste a significant amount of space when generating + * dynamic vertex arrays in which the vertex type is constant but the + * number of vertices cannot be easily computed or stored. This library + * defines guVertexFormat(), guVertexPointer(), and guDrawPrimitive() + * functions which implement each of the separate functions of + * guDrawArray(), so only the necessary functions need to be called. + * + * - Many of the texture-related functions internally call sceGuTexFlush() + * to flush the texture cache. While robust, this can cause significant + * bloat of the resulting display list. This library never calls + * guTexFlush() automatically; it is thus up to the caller to flush the + * texture cache at appropriate points. + * + * - Most of the substitute functions are defined as inline functions in + * this file, allowing the compiler to avoid the overhead of calling a + * subroutine for each graphics operation and to fold constant parameters + * when computing GE instruction words. GCC, at least, can also optimize + * out the updates of the list pointer variable after each instruction, + * saving a significant amount of code. In an optimal case where all + * function parameters are constant, such as + * sceGuTexFilter(GU_NEAREST, GU_NEAREST); + * execution time can be reduced from >20 cycles to 3 (or even 2) cycles + * per call. + * + * Note that this library is only implemented to the extent necessary for + * Yabause, and needs more work to be usable as a general-purpose library. + */ + +/*************************************************************************/ +/*************************************************************************/ + +/** + * USE_SCEGU: When defined, the guXxx() functions declared in this file + * will instead be aliased to the standard sceGuXxx() library functions, + * and custom functions not defined in the sceGu library such as guCommit() + * are turned into no-ops. The sceGu functions are slower than our custom + * functions, so there is normally no reason to define this symbol except + * for benchmarking or testing. + */ +// #define USE_SCEGU + +/*************************************************************************/ +/************************ GE command definitions *************************/ +/*************************************************************************/ + +typedef enum GECommand_ { + GECMD_NOP = 0x00, + GECMD_VERTEX_POINTER= 0x01, + GECMD_INDEX_POINTER = 0x02, + // 0x03 undefined + GECMD_DRAW_PRIMITIVE= 0x04, + GECMD_DRAW_BEZIER = 0x05, + GECMD_DRAW_SPLINE = 0x06, + GECMD_TEST_BBOX = 0x07, + GECMD_JUMP = 0x08, + GECMD_COND_JUMP = 0x09, + GECMD_CALL = 0x0A, + GECMD_RETURN = 0x0B, + GECMD_END = 0x0C, + // 0x0D undefined + GECMD_SIGNAL = 0x0E, + GECMD_FINISH = 0x0F, + + GECMD_ADDRESS_BASE = 0x10, + // 0x11 undefined + GECMD_VERTEX_FORMAT = 0x12, + GECMD_UNKNOWN_13 = 0x13, // psp_doc: Offset Address (BASE) + GECMD_UNKNOWN_14 = 0x14, // psp_doc: Origin Address (BASE) + GECMD_DRAWAREA_LOW = 0x15, + GECMD_DRAWAREA_HIGH = 0x16, + GECMD_ENA_LIGHTING = 0x17, + GECMD_ENA_LIGHT0 = 0x18, + GECMD_ENA_LIGHT1 = 0x19, + GECMD_ENA_LIGHT2 = 0x1A, + GECMD_ENA_LIGHT3 = 0x1B, + GECMD_ENA_ZCLIP = 0x1C, + GECMD_ENA_FACE_CULL = 0x1D, + GECMD_ENA_TEXTURE = 0x1E, + GECMD_ENA_FOG = 0x1F, + + GECMD_ENA_DITHER = 0x20, + GECMD_ENA_BLEND = 0x21, + GECMD_ENA_ALPHA_TEST= 0x22, + GECMD_ENA_DEPTH_TEST= 0x23, + GECMD_ENA_STENCIL = 0x24, + GECMD_ENA_ANTIALIAS = 0x25, + GECMD_ENA_PATCH_CULL= 0x26, + GECMD_ENA_COLOR_TEST= 0x27, + GECMD_ENA_LOGIC_OP = 0x28, + // 0x29 undefined + GECMD_BONE_OFFSET = 0x2A, + GECMD_BONE_UPLOAD = 0x2B, + GECMD_MORPH_0 = 0x2C, + GECMD_MORPH_1 = 0x2D, + GECMD_MORPH_2 = 0x2E, + GECMD_MORPH_3 = 0x2F, + + GECMD_MORPH_4 = 0x30, + GECMD_MORPH_5 = 0x31, + GECMD_MORPH_6 = 0x32, + GECMD_MORPH_7 = 0x33, + // 0x34 undefined + // 0x35 undefined + GECMD_PATCH_SUBDIV = 0x36, + GECMD_PATCH_PRIM = 0x37, + GECMD_PATCH_FRONT = 0x38, + // 0x39 undefined + GECMD_MODEL_START = 0x3A, + GECMD_MODEL_UPLOAD = 0x3B, + GECMD_VIEW_START = 0x3C, + GECMD_VIEW_UPLOAD = 0x3D, + GECMD_PROJ_START = 0x3E, + GECMD_PROJ_UPLOAD = 0x3F, + + GECMD_TEXTURE_START = 0x40, + GECMD_TEXTURE_UPLOAD= 0x41, + GECMD_XSCALE = 0x42, + GECMD_YSCALE = 0x43, + GECMD_ZSCALE = 0x44, + GECMD_XPOS = 0x45, + GECMD_YPOS = 0x46, + GECMD_ZPOS = 0x47, + GECMD_USCALE = 0x48, + GECMD_VSCALE = 0x49, + GECMD_UOFFSET = 0x4A, + GECMD_VOFFSET = 0x4B, + GECMD_XOFFSET = 0x4C, + GECMD_YOFFSET = 0x4D, + // 0x4E undefined + // 0x4F undefined + + GECMD_SHADE_MODE = 0x50, + GECMD_REV_NORMALS = 0x51, + // 0x52 undefined + GECMD_COLOR_MATERIAL= 0x53, + GECMD_EMISSIVE_COLOR= 0x54, + GECMD_AMBIENT_COLOR = 0x55, + GECMD_DIFFUSE_COLOR = 0x56, + GECMD_SPECULAR_COLOR= 0x57, + GECMD_AMBIENT_ALPHA = 0x58, + // 0x59 undefined + // 0x5A undefined + GECMD_SPECULAR_POWER= 0x5B, + GECMD_LIGHT_AMBCOLOR= 0x5C, + GECMD_LIGHT_AMBALPHA= 0x5D, + GECMD_LIGHT_MODEL = 0x5E, + GECMD_LIGHT0_TYPE = 0x5F, + + GECMD_LIGHT1_TYPE = 0x60, + GECMD_LIGHT2_TYPE = 0x61, + GECMD_LIGHT3_TYPE = 0x62, + GECMD_LIGHT0_XPOS = 0x63, + GECMD_LIGHT0_YPOS = 0x64, + GECMD_LIGHT0_ZPOS = 0x65, + GECMD_LIGHT1_XPOS = 0x66, + GECMD_LIGHT1_YPOS = 0x67, + GECMD_LIGHT1_ZPOS = 0x68, + GECMD_LIGHT2_XPOS = 0x69, + GECMD_LIGHT2_YPOS = 0x6A, + GECMD_LIGHT2_ZPOS = 0x6B, + GECMD_LIGHT3_XPOS = 0x6C, + GECMD_LIGHT3_YPOS = 0x6D, + GECMD_LIGHT3_ZPOS = 0x6E, + GECMD_LIGHT0_XDIR = 0x6F, + + GECMD_LIGHT0_YDIR = 0x70, + GECMD_LIGHT0_ZDIR = 0x71, + GECMD_LIGHT1_XDIR = 0x72, + GECMD_LIGHT1_YDIR = 0x73, + GECMD_LIGHT1_ZDIR = 0x74, + GECMD_LIGHT2_XDIR = 0x75, + GECMD_LIGHT2_YDIR = 0x76, + GECMD_LIGHT2_ZDIR = 0x77, + GECMD_LIGHT3_XDIR = 0x78, + GECMD_LIGHT3_YDIR = 0x79, + GECMD_LIGHT3_ZDIR = 0x7A, + GECMD_LIGHT0_CATT = 0x7B, + GECMD_LIGHT0_LATT = 0x7C, + GECMD_LIGHT0_QATT = 0x7D, + GECMD_LIGHT1_CATT = 0x7E, + GECMD_LIGHT1_LATT = 0x7F, + + GECMD_LIGHT1_QATT = 0x80, + GECMD_LIGHT2_CATT = 0x81, + GECMD_LIGHT2_LATT = 0x82, + GECMD_LIGHT2_QATT = 0x83, + GECMD_LIGHT3_CATT = 0x84, + GECMD_LIGHT3_LATT = 0x85, + GECMD_LIGHT3_QATT = 0x86, + GECMD_LIGHT0_SPOTEXP= 0x87, + GECMD_LIGHT1_SPOTEXP= 0x88, + GECMD_LIGHT2_SPOTEXP= 0x89, + GECMD_LIGHT3_SPOTEXP= 0x8A, + GECMD_LIGHT0_SPOTLIM= 0x8B, + GECMD_LIGHT1_SPOTLIM= 0x8C, + GECMD_LIGHT2_SPOTLIM= 0x8D, + GECMD_LIGHT3_SPOTLIM= 0x8E, + GECMD_LIGHT0_ACOL = 0x8F, + + GECMD_LIGHT0_DCOL = 0x90, + GECMD_LIGHT0_SCOL = 0x91, + GECMD_LIGHT1_ACOL = 0x92, + GECMD_LIGHT1_DCOL = 0x93, + GECMD_LIGHT1_SCOL = 0x94, + GECMD_LIGHT2_ACOL = 0x95, + GECMD_LIGHT2_DCOL = 0x96, + GECMD_LIGHT2_SCOL = 0x97, + GECMD_LIGHT3_ACOL = 0x98, + GECMD_LIGHT3_DCOL = 0x99, + GECMD_LIGHT3_SCOL = 0x9A, + GECMD_FACE_ORDER = 0x9B, + GECMD_DRAW_ADDRESS = 0x9C, + GECMD_DRAW_STRIDE = 0x9D, + GECMD_DEPTH_ADDRESS = 0x9E, + GECMD_DEPTH_STRIDE = 0x9F, + + GECMD_TEX0_ADDRESS = 0xA0, + GECMD_TEX1_ADDRESS = 0xA1, + GECMD_TEX2_ADDRESS = 0xA2, + GECMD_TEX3_ADDRESS = 0xA3, + GECMD_TEX4_ADDRESS = 0xA4, + GECMD_TEX5_ADDRESS = 0xA5, + GECMD_TEX6_ADDRESS = 0xA6, + GECMD_TEX7_ADDRESS = 0xA7, + GECMD_TEX0_STRIDE = 0xA8, + GECMD_TEX1_STRIDE = 0xA9, + GECMD_TEX2_STRIDE = 0xAA, + GECMD_TEX3_STRIDE = 0xAB, + GECMD_TEX4_STRIDE = 0xAC, + GECMD_TEX5_STRIDE = 0xAD, + GECMD_TEX6_STRIDE = 0xAE, + GECMD_TEX7_STRIDE = 0xAF, + + GECMD_CLUT_ADDRESS_L= 0xB0, + GECMD_CLUT_ADDRESS_H= 0xB1, + GECMD_COPY_S_ADDRESS= 0xB2, + GECMD_COPY_S_STRIDE = 0xB3, + GECMD_COPY_D_ADDRESS= 0xB4, + GECMD_COPY_D_STRIDE = 0xB5, + // 0xB6 undefined + // 0xB7 undefined + GECMD_TEX0_SIZE = 0xB8, + GECMD_TEX1_SIZE = 0xB9, + GECMD_TEX2_SIZE = 0xBA, + GECMD_TEX3_SIZE = 0xBB, + GECMD_TEX4_SIZE = 0xBC, + GECMD_TEX5_SIZE = 0xBD, + GECMD_TEX6_SIZE = 0xBE, + GECMD_TEX7_SIZE = 0xBF, + + GECMD_TEXTURE_MAP = 0xC0, + GECMD_TEXTURE_ENVMAP= 0xC1, + GECMD_TEXTURE_MODE = 0xC2, + GECMD_TEXTURE_PIXFMT= 0xC3, + GECMD_CLUT_LOAD = 0xC4, + GECMD_CLUT_MODE = 0xC5, + GECMD_TEXTURE_FILTER= 0xC6, + GECMD_TEXTURE_WRAP = 0xC7, + GECMD_TEXTURE_BIAS = 0xC8, + GECMD_TEXTURE_FUNC = 0xC9, + GECMD_TEXTURE_COLOR = 0xCA, + GECMD_TEXTURE_FLUSH = 0xCB, + GECMD_COPY_SYNC = 0xCC, + GECMD_FOG_LIMIT = 0xCD, + GECMD_FOG_RANGE = 0xCE, + GECMD_FOG_COLOR = 0xCF, + + GECMD_TEXTURE_SLOPE = 0xD0, + // 0xD1 undefined + GECMD_FRAME_PIXFMT = 0xD2, + GECMD_CLEAR_MODE = 0xD3, + GECMD_CLIP_MIN = 0xD4, + GECMD_CLIP_MAX = 0xD5, + GECMD_CLIP_NEAR = 0xD6, + GECMD_CLIP_FAR = 0xD7, + GECMD_COLORTEST_FUNC= 0xD8, + GECMD_COLORTEST_REF = 0xD9, + GECMD_COLORTEST_MASK= 0xDA, + GECMD_ALPHATEST = 0xDB, + GECMD_STENCILTEST = 0xDC, + GECMD_STENCIL_OP = 0xDD, + GECMD_DEPTHTEST = 0xDE, + GECMD_BLEND_FUNC = 0xDF, + + GECMD_BLEND_SRCFIX = 0xE0, + GECMD_BLEND_DSTFIX = 0xE1, + GECMD_DITHER0 = 0xE2, + GECMD_DITHER1 = 0xE3, + GECMD_DITHER2 = 0xE4, + GECMD_DITHER3 = 0xE5, + GECMD_LOGIC_OP = 0xE6, + GECMD_DEPTH_MASK = 0xE7, + GECMD_COLOR_MASK = 0xE8, + GECMD_ALPHA_MASK = 0xE9, + GECMD_COPY = 0xEA, + GECMD_COPY_S_POS = 0xEB, + GECMD_COPY_D_POS = 0xEC, + // 0xED undefined + GECMD_COPY_SIZE = 0xEE, + // 0xEF undefined + + GECMD_UNKNOWN_F0 = 0xF0, + GECMD_UNKNOWN_F1 = 0xF1, + GECMD_UNKNOWN_F2 = 0xF2, + GECMD_UNKNOWN_F3 = 0xF3, + GECMD_UNKNOWN_F4 = 0xF4, + GECMD_UNKNOWN_F5 = 0xF5, + GECMD_UNKNOWN_F6 = 0xF6, + GECMD_UNKNOWN_F7 = 0xF7, + GECMD_UNKNOWN_F8 = 0xF8, + GECMD_UNKNOWN_F9 = 0xF9, + // 0xFA..0xFF undefined +} GECommand; + +/*************************************************************************/ +/*********************** Custom library functions ************************/ +/*************************************************************************/ + +#ifndef USE_SCEGU + +/*************************************************************************/ + +/* Force these functions to be inlined to avoid function call overhead */ +#ifdef __GNUC__ +# define inline inline __attribute__((always_inline)) +#endif + +/* Note: we declare "extern uint32_t *gu_list" within each function rather + * than here so that we don't export the declaration to the caller. */ + +/* Macros for splitting an address into high and low parts */ +#define ADDRESS_HI(ptr) (((uint32_t)(ptr) & 0x3F000000) >> 8) +#define ADDRESS_LO(ptr) ((uint32_t)(ptr) & 0x00FFFFFF) + +/* Helper function to extract the high 24 bits of a float */ +static inline uint32_t trim_float(const float value) { + uint32_t result; + asm(".set push; .set noreorder\n" + "mfc1 %[result], %[value]\n" + "nop\n" + "srl %[result], %[result], 8\n" + ".set pop" + : [result] "=r" (result) + : [value] "f" (value) + ); + return result; +} + +/*************************************************************************/ + +/**** Functions defined in gu.c ****/ + +/* Custom function to commit cached data and start GE processing */ +extern void guCommit(void); + +extern void guFinish(void); +extern void *guGetMemory(const uint32_t size); +extern void guInit(void); +extern void guStart(const int type, void * const list); +extern void guSync(const int mode, const int target); + +/*************************************************************************/ + +/**** Inline functions ****/ + +static inline void guAlphaFunc(const int func, const int value, const int mask) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_ALPHATEST<<24 | mask<<16 | value<<8 | func; +} + +static inline void guAmbientColor(const uint32_t color) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_AMBIENT_COLOR<<24 | (color & 0xFFFFFF); + *gu_list++ = GECMD_AMBIENT_ALPHA<<24 | color>>24; +} + +static inline void guBlendFunc(const int func, const int src, const int dest, + const uint32_t srcfix, const uint32_t destfix) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_BLEND_FUNC<<24 | func<<8 | dest<<4 | src; + if (src == GU_FIX) { + *gu_list++ = GECMD_BLEND_SRCFIX<<24 | srcfix; + } + if (dest == GU_FIX) { + *gu_list++ = GECMD_BLEND_DSTFIX<<24 | destfix; + } +} + +static inline void guCallList(const void * const list) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_ADDRESS_BASE<<24 | ADDRESS_HI(list); + *gu_list++ = GECMD_CALL<<24 | ADDRESS_LO(list); +} + +static inline void guClear(const int mode) +{ + extern uint32_t *gu_list; + extern uint32_t gu_clear_color_stencil; + extern uint16_t gu_clear_depth; + struct {uint32_t color, xy, z;} *vertices; + vertices = guGetMemory(sizeof(*vertices) * 2); + vertices[0].color = 0; + vertices[0].xy = 0; + vertices[0].z = gu_clear_depth; + vertices[1].color = gu_clear_color_stencil; + vertices[1].xy = 480 | 272<<16; + vertices[1].z = 0; + *gu_list++ = GECMD_CLEAR_MODE<<24 | mode<<8 | 1; + *gu_list++ = GECMD_ENA_BLEND<<24 | 0; + *gu_list++ = GECMD_VERTEX_FORMAT<<24 + | GU_TRANSFORM_2D | GU_COLOR_8888 | GU_VERTEX_16BIT; + *gu_list++ = GECMD_ADDRESS_BASE<<24 | ADDRESS_HI(vertices); + *gu_list++ = GECMD_VERTEX_POINTER<<24 | ADDRESS_LO(vertices); + *gu_list++ = GECMD_DRAW_PRIMITIVE<<24 | GU_SPRITES<<16 | 2; + *gu_list++ = GECMD_CLEAR_MODE<<24 | 0; +} + +static inline void guClearColor(const uint32_t color) +{ + extern uint32_t gu_clear_color_stencil; + gu_clear_color_stencil = (gu_clear_color_stencil & 0xFF000000) + | (color & 0x00FFFFFF); +} + +static inline void guClearDepth(const unsigned int depth) +{ + extern uint16_t gu_clear_depth; + gu_clear_depth = depth; +} + +static inline void guClearStencil(const unsigned int stencil) +{ + extern uint32_t gu_clear_color_stencil; + gu_clear_color_stencil = (gu_clear_color_stencil & 0xFFFFFF) | stencil<<24; +} + +static inline void guClutLoad(const int count, const void * const address) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_CLUT_ADDRESS_L<<24 | ADDRESS_LO(address); + *gu_list++ = GECMD_CLUT_ADDRESS_H<<24 | ADDRESS_HI(address); + *gu_list++ = GECMD_CLUT_LOAD<<24 | count; +} + +static inline void guClutMode(const int format, const int shift, + const int mask, const int unknown) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_CLUT_MODE<<24 | format | shift<<2 | mask<<8; +} + +static inline void guCopyImage(const int mode, + const int src_x, const int src_y, + const int src_w, const int src_h, + const int src_stride, + const void * const src_ptr, + const int dest_x, const int dest_y, + const int dest_stride, + const void * const dest_ptr) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_COPY_S_ADDRESS<<24 | ADDRESS_LO(src_ptr); + *gu_list++ = GECMD_COPY_S_STRIDE<<24 | ADDRESS_HI(src_ptr) | src_stride; + *gu_list++ = GECMD_COPY_S_POS<<24 | src_y<<10 | src_x; + *gu_list++ = GECMD_COPY_D_ADDRESS<<24 | ADDRESS_LO(dest_ptr); + *gu_list++ = GECMD_COPY_D_STRIDE<<24 | ADDRESS_HI(dest_ptr) | dest_stride; + *gu_list++ = GECMD_COPY_D_POS<<24 | dest_y<<10 | dest_x; + *gu_list++ = GECMD_COPY_SIZE<<24 | (src_h-1)<<10 | (src_w-1); + *gu_list++ = GECMD_COPY<<24 | (mode==GU_PSM_8888 ? 1 : 0); +} + +static inline void guDepthBuffer(const void * const address, const int stride) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_DEPTH_ADDRESS<<24 | ADDRESS_LO(address); + *gu_list++ = GECMD_DEPTH_STRIDE<<24 | ADDRESS_HI(address) | stride; +} + +static inline void guDepthFunc(const int function) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_DEPTHTEST<<24 | function; +} + +static inline void guDepthMask(const int mask) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_DEPTH_MASK<<24 | mask; +} + +static inline void guDepthRange(const int near, const int far) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_ZSCALE<<24 | trim_float((far - near) / 2.0f); + *gu_list++ = GECMD_ZPOS<<24 | trim_float((far + near) / 2.0f); + *gu_list++ = GECMD_CLIP_NEAR<<24 | (near>24; +} + +static inline void guScissor(const int left, const int top, + const int width, const int height) +{ + extern uint32_t *gu_list; + extern uint32_t gu_scissorcmd_min, gu_scissorcmd_max; + extern uint8_t gu_scissor_enabled; + gu_scissorcmd_min = GECMD_CLIP_MIN<<24 | top<<10 | left; + gu_scissorcmd_max = GECMD_CLIP_MAX<<24 | (top+height-1)<<10 + | (left+width-1); + if (gu_scissor_enabled) { + *gu_list++ = gu_scissorcmd_min; + *gu_list++ = gu_scissorcmd_max; + } +} + +/* + * Note: As with sceGuSetMatrix(), 4x3 matrices are laid out in a 4x4 + * array as follows: + * { {_11, _12, _13, _ignored}, + * {_21, _22, _23, _ignored}, + * {_31, _32, _33, _ignored}, + * {_41, _42, _43, _ignored} } + */ +static inline void guSetMatrix(int type, const float *matrix) +{ + extern uint32_t *gu_list; + if (type == GU_MODEL) { + *gu_list++ = GECMD_MODEL_START<<24; + unsigned int i; + for (i = 0; i < 4; i++) { + unsigned int j; + for (j = 0; j < 3; j++) { + *gu_list++ = GECMD_MODEL_UPLOAD<<24 + | trim_float(matrix[i*4+j]); + } + } + } else if (type == GU_VIEW) { + *gu_list++ = GECMD_VIEW_START<<24; + unsigned int i; + for (i = 0; i < 4; i++) { + unsigned int j; + for (j = 0; j < 3; j++) { + *gu_list++ = GECMD_VIEW_UPLOAD<<24 + | trim_float(matrix[i*4+j]); + } + } + } else if (type == GU_PROJECTION) { + *gu_list++ = GECMD_PROJ_START<<24; + unsigned int i; + for (i = 0; i < 4; i++) { + unsigned int j; + for (j = 0; j < 4; j++) { + *gu_list++ = GECMD_PROJ_UPLOAD<<24 + | trim_float(matrix[i*4+j]); + } + } + } else if (type == GU_TEXTURE) { + *gu_list++ = GECMD_TEXTURE_START<<24; + unsigned int i; + for (i = 0; i < 4; i++) { + unsigned int j; + for (j = 0; j < 3; j++) { + *gu_list++ = GECMD_TEXTURE_UPLOAD<<24 + | trim_float(matrix[i*4+j]); + } + } + } +} + +static inline void guShadeModel(const int mode) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_SHADE_MODE<<24 | mode; +} + +static inline void guStencilFunc(const int func, const int ref, const int mask) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_STENCILTEST<<24 | mask<<16 | ref<<8 | func; +} + +static inline void guStencilOp(const int fail, const int zfail, const int zpass) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_STENCIL_OP<<24 | zpass<<16 | zfail<<8 | fail; +} + +static inline void guTexFilter(const int min, const int mag) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_FILTER<<24 | mag<<8 | min; +} + +static inline void guTexFlush(void) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_FLUSH<<24; +} + +static inline void guTexFunc(const int func, const int alpha) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_FUNC<<24 | alpha<<8 | func; +} + +static inline void guTexImage(const int level, const int width, + const int height, const int stride, + const void * const address) +{ + extern uint32_t *gu_list; + const int log2_width = 31 - __builtin_clz(width); + const int log2_height = 31 - __builtin_clz(height); + *gu_list++ = (GECMD_TEX0_ADDRESS+level)<<24 | ADDRESS_LO(address); + *gu_list++ = (GECMD_TEX0_STRIDE+level)<<24 | ADDRESS_HI(address) | stride; + *gu_list++ = (GECMD_TEX0_SIZE+level)<<24 | log2_height<<8 | log2_width; +} + +static inline void guTexLevelMode(const int mode, const float bias) +{ + extern uint32_t *gu_list; + const int bias_int = (int)(bias*16 + 0.5f) & 0xFF; + *gu_list++ = GECMD_TEXTURE_BIAS<<24 | bias_int<<16 | mode; +} + +static inline void guTexMode(const int format, const int mipmaps, + const int unknown, const int swizzle) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_MODE<<24 | (mipmaps ? mipmaps-1 : 0) << 16 + | swizzle; + *gu_list++ = GECMD_TEXTURE_PIXFMT<<24 | format; +} + +static inline void guTexSlope(const float slope) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_SLOPE<<24 | trim_float(slope); +} + +static inline void guTexWrap(const int u_mode, const int v_mode) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_TEXTURE_WRAP<<24 | v_mode<<8 | u_mode; +} + +/* Custom function to set vertex format independently of guDrawArray() */ +static inline void guVertexFormat(const uint32_t format) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_VERTEX_FORMAT<<24 | format; +} + +/* Custom function to set vertex pointer independently of guDrawArray() */ +static inline void guVertexPointer(const void * const address) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_ADDRESS_BASE<<24 | ADDRESS_HI(address); + *gu_list++ = GECMD_VERTEX_POINTER<<24 | ADDRESS_LO(address); +} + +static inline void guViewport(const int cx, const int cy, + const int width, const int height) +{ + extern uint32_t *gu_list; + *gu_list++ = GECMD_XPOS<<24 | trim_float(cx); + *gu_list++ = GECMD_YPOS<<24 | trim_float(cy); + *gu_list++ = GECMD_XSCALE<<24 | trim_float(width/2); + *gu_list++ = GECMD_YSCALE<<24 | trim_float(-height/2); +} + +/*************************************************************************/ + +#undef ADDRESS_HI +#undef ADDRESS_LO + +#ifdef __GNUC__ +# undef inline // Cancel the always_inline definition above +#endif + +#endif // !USE_SCEGU + +/*************************************************************************/ +/************************* sceGu library aliases *************************/ +/*************************************************************************/ + +#ifdef USE_SCEGU + +#define guAlphaFunc(func,value,mask) \ + sceGuAlphaFunc((func), (value), (mask)) +#define guAmbientColor(color) \ + sceGuAmbientColor((color)) +#define guBlendFunc(func,src,dest,srcfix,destfix) \ + sceGuBlendFunc((func), (src), (dest), (srcfix), (destfix)) +#define guCallList(list) \ + sceGuCallList((list)) +#define guClear(mode) \ + sceGuClear((mode)) +#define guClearColor(color) \ + sceGuClearColor((color)) +#define guClearDepth(depth) \ + sceGuClearDepth((depth)) +#define guClearStencil(stencil) \ + sceGuClearStencil((stencil)) +#define guClutLoad(count,address) \ + sceGuClutLoad((count), (address)) +#define guClutMode(format,shift,mask,unknown) \ + sceGuClutMode((format), (shift), (mask), (unknown)) +#define guCopyImage(mode,src_x,src_y,src_w,src_h,src_stride,src_ptr,dest_x,dest_y,dest_stride,dest_ptr) \ + sceGuCopyImage((mode), (src_x), (src_y), (src_w), (src_h), (src_stride), \ + (src_ptr), (dest_x), (dest_y), (dest_stride), (dest_ptr)) +#define guDepthBuffer(address,stride) \ + sceGuDepthBuffer((address), (stride)) +#define guDepthFunc(function) \ + sceGuDepthFunc((function)) +#define guDepthMask(mask) \ + sceGuDepthMask((mask)) +#define guDepthRange(near,far) \ + sceGuDepthRange((near), (far)) +#define guDisable(mode) \ + sceGuDisable((mode)) +#define guDispBuffer(width,height,address,stride) \ + sceGuDispBuffer((width), (height), (address), (stride)) +#define guDrawArray(primitive,vertexfmt,count,indexptr,vertexptr) \ + sceGuDrawArray((primitive), (vertexfmt), (count), (indexptr), (vertexptr)) +#define guDrawBuffer(format,address,stride) \ + sceGuDrawBuffer((format), (address), (stride)) +#define guEnable(mode) \ + sceGuEnable((mode)) +#define guFinish() \ + sceGuFinish() +#define guGetMemory(size) \ + sceGuGetMemory((size)) +#define guInit() \ + sceGuInit() +#define guLogicalOp(op) \ + sceGuLogicalOp((op)) +#define guOffset(xofs,yofs) \ + sceGuOffset((xofs), (yofs)) +#define guPixelMask(mask) \ + sceGuPixelMask((mask)) +#define guScissor(left,top,width,height) \ + sceGuScissor((left), (top), (width), (height)) +#define guSetMatrix(type,matrix) \ + sceGuSetMatrix((type), (const ScePspFMatrix4 *)(matrix)) +#define guShadeModel(mode) \ + sceGuShadeModel((mode)) +#define guStart(type,list) \ + sceGuStart((type), (list)) +#define guStencilFunc(func,ref,mask) \ + sceGuStencilFunc((func), (ref), (mask)) +#define guStencilOp(fail,zfail,zpass) \ + sceGuStencilOp((fail), (zfail), (zpass)) +#define guSync(mode,target) \ + sceGuSync((mode), (target)) +#define guTexFilter(min,mag) \ + sceGuTexFilter((min), (mag)) +#define guTexFlush() \ + sceGuTexFlush() +#define guTexFunc(func,alpha) \ + sceGuTexFunc((func), (alpha)) +#define guTexImage(level,width,height,stride,address) \ + sceGuTexImage((level), (width), (height), (stride), (address)) +#define guTexLevelMode(mode, bias) \ + sceGuTexLevelMode((mode), (bias)) +#define guTexMode(format,mipmaps,unknown,swizzle) \ + sceGuTexMode((format), (mipmaps), (unknown), (swizzle)) +#define guTexSlope(slope) \ + sceGuTexSlope((slope)) +#define guTexWrap(u_mode,v_mode) \ + sceGuTexWrap((u_mode), (v_mode)) +#define guViewport(cx,cy,width,height) \ + sceGuViewport((cx), (cy), (width), (height)) + +/* Custom functions */ +#define guCommit() /* no-op */ +#define guDrawPrimitive(primitive,count) \ + sceGuSendCommandi(GECMD_DRAW_PRIMITIVE, (primitive)<<16 | (count)) +#define guVertexFormat(format) \ + sceGuSendCommandi(GECMD_VERTEX_FORMAT, (format)) +#define guVertexPointer(address) do { \ + const uint32_t __address = (uint32_t)(address); \ + sceGuSendCommandi(GECMD_ADDRESS_BASE, (__address & 0xFF000000) >> 8); \ + sceGuSendCommandi(GECMD_VERTEX_POINTER, __address & 0x00FFFFFF); \ +} while (0) + +#endif // USE_SCEGU + +/*************************************************************************/ +/*************************************************************************/ + +#endif // PSP_GU_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/icache-funcs-2450.patch b/yabause/src/psp/icache-funcs-2450.patch new file mode 100644 index 0000000000..d928c52648 --- /dev/null +++ b/yabause/src/psp/icache-funcs-2450.patch @@ -0,0 +1,60 @@ +diff -urN a/pspsdk-2450/src/user/Makefile.am b/pspsdk-2450/src/user/Makefile.am +--- a/pspsdk-2450/src/user/Makefile.am 2009-01-09 03:15:40 +0900 ++++ b/pspsdk-2450/src/user/Makefile.am 2009-02-10 16:24:00 +0900 +@@ -24,7 +24,7 @@ + + THREADMAN_OBJS = ThreadManForUser_0000.o ThreadManForUser_0001.o ThreadManForUser_0002.o ThreadManForUser_0003.o ThreadManForUser_0004.o ThreadManForUser_0005.o ThreadManForUser_0006.o ThreadManForUser_0007.o ThreadManForUser_0008.o ThreadManForUser_0009.o ThreadManForUser_0010.o ThreadManForUser_0011.o ThreadManForUser_0012.o ThreadManForUser_0013.o ThreadManForUser_0014.o ThreadManForUser_0015.o ThreadManForUser_0016.o ThreadManForUser_0017.o ThreadManForUser_0018.o ThreadManForUser_0019.o ThreadManForUser_0020.o ThreadManForUser_0021.o ThreadManForUser_0022.o ThreadManForUser_0023.o ThreadManForUser_0024.o ThreadManForUser_0025.o ThreadManForUser_0026.o ThreadManForUser_0027.o ThreadManForUser_0028.o ThreadManForUser_0029.o ThreadManForUser_0030.o ThreadManForUser_0031.o ThreadManForUser_0032.o ThreadManForUser_0033.o ThreadManForUser_0034.o ThreadManForUser_0035.o ThreadManForUser_0036.o ThreadManForUser_0037.o ThreadManForUser_0038.o ThreadManForUser_0039.o ThreadManForUser_0040.o ThreadManForUser_0041.o ThreadManForUser_0042.o ThreadManForUser_0043.o ThreadManForUser_0044.o ThreadManForUser_0045.o ThreadManForUser_0046.o ThreadManForUser_0047.o ThreadManForUser_0048.o ThreadManForUser_0049.o ThreadManForUser_0050.o ThreadManForUser_0051.o ThreadManForUser_0052.o ThreadManForUser_0053.o ThreadManForUser_0054.o ThreadManForUser_0055.o ThreadManForUser_0056.o ThreadManForUser_0057.o ThreadManForUser_0058.o ThreadManForUser_0059.o ThreadManForUser_0060.o ThreadManForUser_0061.o ThreadManForUser_0062.o ThreadManForUser_0063.o ThreadManForUser_0064.o ThreadManForUser_0065.o ThreadManForUser_0066.o ThreadManForUser_0067.o ThreadManForUser_0068.o ThreadManForUser_0069.o ThreadManForUser_0070.o ThreadManForUser_0071.o ThreadManForUser_0072.o ThreadManForUser_0073.o ThreadManForUser_0074.o ThreadManForUser_0075.o ThreadManForUser_0076.o ThreadManForUser_0077.o ThreadManForUser_0078.o ThreadManForUser_0079.o ThreadManForUser_0080.o ThreadManForUser_0081.o ThreadManForUser_0082.o ThreadManForUser_0083.o ThreadManForUser_0084.o ThreadManForUser_0085.o ThreadManForUser_0086.o ThreadManForUser_0087.o ThreadManForUser_0088.o ThreadManForUser_0089.o ThreadManForUser_0090.o ThreadManForUser_0091.o ThreadManForUser_0092.o ThreadManForUser_0093.o ThreadManForUser_0094.o ThreadManForUser_0095.o ThreadManForUser_0096.o ThreadManForUser_0097.o ThreadManForUser_0098.o ThreadManForUser_0099.o ThreadManForUser_0100.o ThreadManForUser_0101.o ThreadManForUser_0102.o ThreadManForUser_0103.o ThreadManForUser_0104.o ThreadManForUser_0105.o ThreadManForUser_0106.o ThreadManForUser_0107.o ThreadManForUser_0108.o ThreadManForUser_0109.o ThreadManForUser_0110.o ThreadManForUser_0111.o ThreadManForUser_0112.o ThreadManForUser_0113.o ThreadManForUser_0114.o ThreadManForUser_0115.o ThreadManForUser_0116.o ThreadManForUser_0117.o ThreadManForUser_0118.o ThreadManForUser_0119.o ThreadManForUser_0120.o ThreadManForUser_0121.o ThreadManForUser_0122.o ThreadManForUser_0123.o ThreadManForUser_0124.o ThreadManForUser_0125.o ThreadManForUser_0126.o + +-UTILS_OBJS = UtilsForUser_0000.o UtilsForUser_0001.o UtilsForUser_0002.o UtilsForUser_0003.o UtilsForUser_0004.o UtilsForUser_0005.o UtilsForUser_0006.o UtilsForUser_0007.o UtilsForUser_0008.o UtilsForUser_0009.o UtilsForUser_0010.o UtilsForUser_0011.o UtilsForUser_0012.o UtilsForUser_0013.o UtilsForUser_0014.o UtilsForUser_0015.o UtilsForUser_0016.o UtilsForUser_0017.o UtilsForUser_0018.o UtilsForUser_0019.o UtilsForUser_0020.o UtilsForUser_0021.o UtilsForUser_0022.o UtilsForUser_0023.o UtilsForUser_0024.o ++UTILS_OBJS = UtilsForUser_0000.o UtilsForUser_0001.o UtilsForUser_0002.o UtilsForUser_0003.o UtilsForUser_0004.o UtilsForUser_0005.o UtilsForUser_0006.o UtilsForUser_0007.o UtilsForUser_0008.o UtilsForUser_0009.o UtilsForUser_0010.o UtilsForUser_0011.o UtilsForUser_0012.o UtilsForUser_0013.o UtilsForUser_0014.o UtilsForUser_0015.o UtilsForUser_0016.o UtilsForUser_0017.o UtilsForUser_0018.o UtilsForUser_0019.o UtilsForUser_0020.o UtilsForUser_0021.o UtilsForUser_0022.o UtilsForUser_0023.o UtilsForUser_0024.o UtilsForUser_0025.o UtilsForUser_0026.o + + INTERRUPT_OBJS = InterruptManager_0000.o InterruptManager_0001.o InterruptManager_0002.o InterruptManager_0003.o InterruptManager_0004.o InterruptManager_0005.o InterruptManager_0006.o InterruptManager_0007.o InterruptManager_0008.o InterruptManager_0009.o + +diff -urN a/pspsdk-2450/src/user/UtilsForUser.S b/pspsdk-2450/src/user/UtilsForUser.S +--- a/pspsdk-2450/src/user/UtilsForUser.S 2009-01-09 03:15:40 +0900 ++++ b/pspsdk-2450/src/user/UtilsForUser.S 2009-02-10 16:23:29 +0900 +@@ -77,3 +77,9 @@ + #ifdef F_UtilsForUser_0024 + IMPORT_FUNC "UtilsForUser",0xFB05FAD0,sceKernelIcacheReadTag + #endif ++#ifdef F_UtilsForUser_0025 ++ IMPORT_FUNC "UtilsForUser",0x920F104A,sceKernelIcacheInvalidateAll ++#endif ++#ifdef F_UtilsForUser_0026 ++ IMPORT_FUNC "UtilsForUser",0xC2DF770E,sceKernelIcacheInvalidateRange ++#endif +diff -urN a/pspsdk-2450/src/user/psputils.h b/pspsdk-2450/src/user/psputils.h +--- a/pspsdk-2450/src/user/psputils.h 2009-01-09 03:15:40 +0900 ++++ b/pspsdk-2450/src/user/psputils.h 2009-02-10 16:27:07 +0900 +@@ -58,12 +58,12 @@ + void sceKernelDcacheWritebackInvalidateAll(void); + + /** +- * Write back a range of addresses from data cache to memory ++ * Write back a range of addresses from the data cache to memory + */ + void sceKernelDcacheWritebackRange(const void *p, unsigned int size); + + /** +- * Write back and invalidate a range of addresses in data cache ++ * Write back and invalidate a range of addresses in the data cache + */ + void sceKernelDcacheWritebackInvalidateRange(const void *p, unsigned int size); + +@@ -72,6 +72,16 @@ + */ + void sceKernelDcacheInvalidateRange(const void *p, unsigned int size); + ++/** ++ * Invalidate the instruction cache ++ */ ++void sceKernelIcacheInvalidateAll(void); ++ ++/** ++ * Invalidate a range of addresses in the instruction cache ++ */ ++void sceKernelIcacheInvalidateRange(const void *p, unsigned int size); ++ + /** Structure for holding a mersenne twister context */ + typedef struct _SceKernelUtilsMt19937Context { + unsigned int count; diff --git a/yabause/src/psp/init.c b/yabause/src/psp/init.c new file mode 100644 index 0000000000..92c38ac08c --- /dev/null +++ b/yabause/src/psp/init.c @@ -0,0 +1,281 @@ +/* src/psp/init.c: PSP initialization routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" +#include // For struct tm definition + +#include "../cdbase.h" +#include "../cs0.h" +#include "../m68kcore.h" +#include "../peripheral.h" +#include "../scsp.h" +#include "../sh2core.h" +#include "../sh2int.h" +#include "../vidsoft.h" + +#include "config.h" +#include "control.h" +#include "display.h" +#include "init.h" +#include "localtime.h" +#include "menu.h" +#include "sys.h" +#include "psp-cd.h" +#include "psp-m68k.h" +#include "psp-per.h" +#include "psp-sh2.h" +#include "psp-sound.h" +#include "psp-video.h" + +#include "me.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Interface lists */ + +M68K_struct *M68KCoreList[] = { + /* We only support the ME-enabled Q68 interface */ + &M68KPSP, + NULL +}; + +SH2Interface_struct *SH2CoreList[] = { + &SH2Interpreter, + &SH2DebugInterpreter, + &SH2PSP, + NULL +}; + +PerInterface_struct *PERCoreList[] = { + /* We only support the native interface */ + &PERPSP, + NULL +}; + +CDInterface *CDCoreList[] = { + /* We only support the native interface */ + &CDPSP, + NULL +}; + +SoundInterface_struct *SNDCoreList[] = { + /* We only support the native interface */ + &SNDPSP, + NULL +}; + +VideoInterface_struct *VIDCoreList[] = { + &VIDPSP, + &VIDSoft, + NULL +}; + +/*-----------------------------------------------------------------------*/ + +/* Local routine declarations */ + +static void get_base_directory(const char *argv0, char *buffer, int bufsize); + +/*************************************************************************/ +/************************** Interface routines ***************************/ +/*************************************************************************/ + +/** + * init_psp: Perform PSP-related initialization and command-line option + * parsing. Aborts the program if an error occurs. + * + * [Parameters] + * argc: Command line argument count + * argv: Command line argument vector + * [Return value] + * None + */ +void init_psp(int argc, char **argv) +{ + /* Set the CPU to maximum speed, because boy, do we need it */ + scePowerSetClockFrequency(333, 333, 166); + + /* Determine the program's base directory and change to it */ + get_base_directory(argv[0], progpath, sizeof(progpath)); + if (*progpath) { + sceIoChdir(progpath); + } + + /* Start the system callback monitoring thread */ + if (!sys_setup_callbacks()) { + sceKernelExitGame(); + } + + /* Initialize controller input management */ + if (!control_init()) { + DMSG("Failed to initialize controller"); + sceKernelExitGame(); + } + + /* Initialize the display hardware */ + if (!display_init()) { + DMSG("Failed to initialize display"); + sceKernelExitGame(); + } + + /* Initialize localtime() */ + localtime_init(); + + /* Load the ME access library (if possible) */ + int res = sys_load_module("me.prx", PSP_MEMORY_PARTITION_KERNEL); + if (res < 0) { + DMSG("Failed to load me.prx: %s", psp_strerror(res)); + me_available = 0; + } else { + me_available = 1; + } +} + +/*************************************************************************/ + +/** + * init_yabause: Initialize the emulator core. On error, an appropriate + * error message is registered via menu_set_error(). + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on failure + */ +int init_yabause(void) +{ + yabauseinit_struct yinit; + + /* Set a default error message in case the core doesn't set one */ + menu_set_error("Failed to initialize the emulator!"); + + /* Set up general defaults */ + memset(&yinit, 0, sizeof(yinit)); + yinit.m68kcoretype = M68KCORE_PSP; + yinit.percoretype = PERCORE_PSP; + yinit.sh2coretype = config_get_module_sh2(); + yinit.vidcoretype = config_get_module_video(); + yinit.sndcoretype = SNDCORE_PSP; + yinit.cdcoretype = CDCORE_PSP; + yinit.carttype = CART_NONE; + yinit.regionid = 0; + yinit.biospath = config_get_path_bios(); + yinit.cdpath = config_get_path_cd(); + yinit.buppath = config_get_path_bup(); + yinit.mpegpath = NULL; + yinit.cartpath = NULL; + yinit.videoformattype = VIDEOFORMATTYPE_NTSC; + yinit.clocksync = config_get_clock_sync(); + const time_t basetime = 883656000; // 1998-01-01 12:00 UTC + yinit.basetime = config_get_clock_fixed_time() + ? basetime - localtime_utc_offset() + : 0; + yinit.usethreads = 1; + + /* Initialize controller settings */ + PerInit(yinit.percoretype); + PerPortReset(); + padbits = PerPadAdd(&PORTDATA1); + static const struct { + uint8_t key; // Key index from peripheral.h + uint16_t button; // PSP button index (PSP_CTRL_*) + } buttons[] = { + {PERPAD_UP, PSP_CTRL_UP}, + {PERPAD_RIGHT, PSP_CTRL_RIGHT}, + {PERPAD_DOWN, PSP_CTRL_DOWN}, + {PERPAD_LEFT, PSP_CTRL_LEFT}, + {PERPAD_RIGHT_TRIGGER, PSP_CTRL_RTRIGGER}, + {PERPAD_LEFT_TRIGGER, PSP_CTRL_LTRIGGER}, + {PERPAD_START, PSP_CTRL_START}, + }; + int i; + for (i = 0; i < lenof(buttons); i++) { + PerSetKey(buttons[i].button, buttons[i].key, padbits); + } + PerSetKey(config_get_button(CONFIG_BUTTON_A), PERPAD_A, padbits); + PerSetKey(config_get_button(CONFIG_BUTTON_B), PERPAD_B, padbits); + PerSetKey(config_get_button(CONFIG_BUTTON_C), PERPAD_C, padbits); + PerSetKey(config_get_button(CONFIG_BUTTON_X), PERPAD_X, padbits); + PerSetKey(config_get_button(CONFIG_BUTTON_Y), PERPAD_Y, padbits); + PerSetKey(config_get_button(CONFIG_BUTTON_Z), PERPAD_Z, padbits); + + /* Initialize emulator state */ + if (YabauseInit(&yinit) != 0) { + DMSG("YabauseInit() failed!"); + return 0; + } + YabauseSetDecilineMode(config_get_deciline_mode()); + ScspSetFrameAccurate(config_get_audio_sync()); + ScspUnMuteAudio(SCSP_MUTE_SYSTEM); + + /* Success */ + menu_set_error(NULL); + return 1; +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * get_base_directory: Extract the program's base directory from argv[0]. + * Stores the empty string in the destination buffer if the base directory + * cannot be determined. + * + * [Parameters] + * argv0: Value of argv[0] + * buffer: Buffer to store directory path into + * bufsize: Size of buffer + * [Return value] + * None + */ +static void get_base_directory(const char *argv0, char *buffer, int bufsize) +{ + *buffer = 0; + if (argv0) { + const char *s = strrchr(argv0, '/'); + if (s != NULL) { + int n = snprintf(buffer, bufsize, "%.*s", s - argv0, argv0); + if (n >= bufsize) { + DMSG("argv[0] too long: %s", argv0); + *buffer = 0; + } + } else { + DMSG("argv[0] has no directory: %s", argv0); + } + } else { + DMSG("argv[0] is NULL!"); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/init.h b/yabause/src/psp/init.h new file mode 100644 index 0000000000..5ecf1c47ef --- /dev/null +++ b/yabause/src/psp/init.h @@ -0,0 +1,60 @@ +/* src/psp/init.h: PSP initialization routine header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_INIT_H +#define PSP_INIT_H + +/*************************************************************************/ + +/** + * init_psp: Perform PSP-related initialization and command-line option + * parsing. Aborts the program if an error occurs. + * + * [Parameters] + * argc: Command line argument count + * argv: Command line argument vector + * [Return value] + * None + */ +extern void init_psp(int argc, char **argv); + +/** + * init_yabause: Initialize the emulator core. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on failure + */ +extern int init_yabause(void); + +/*************************************************************************/ + +#endif // PSP_INIT_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/localtime.c b/yabause/src/psp/localtime.c new file mode 100644 index 0000000000..b037ac1a0a --- /dev/null +++ b/yabause/src/psp/localtime.c @@ -0,0 +1,157 @@ +/* src/psp/localtime.c: PSP implementation of localtime() + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" +#include // For struct tm definition + +#include "localtime.h" +#include "sys.h" + +/*************************************************************************/ + +/* Time zone offset from UTC (amount to add to time() result) */ +static int32_t utc_offset; + +/*************************************************************************/ +/*************************************************************************/ + +/** + * localtime_init: Perform initialization required for localtime(). + * Called by PSP initialization code at program startup. + * + * [Parameters] + * None + * [Return value] + * None + */ +void localtime_init(void) +{ + /* Find the PSP's time zone */ + int utc_offset_min; + int32_t result = sceUtilityGetSystemParamInt( + PSP_SYSTEMPARAM_ID_INT_TIMEZONE, &utc_offset_min + ); + if (result == 0) { + utc_offset = utc_offset_min * 60; + } else { + DMSG("Failed to get time zone: %s", psp_strerror(result)); + utc_offset = 0; + } + + /* Check whether daylight saving time is in use */ + int dst; + result = sceUtilityGetSystemParamInt( + PSP_SYSTEMPARAM_ID_INT_DAYLIGHTSAVINGS, &dst + ); + if (result == 0) { + if (dst) { + utc_offset += 60*60; + } + } else { + DMSG("Failed to get DST status: %s", psp_strerror(result)); + } + +} + +/*************************************************************************/ + +/** + * localtime_utc_offset: Return the UTC offset of the current time zone in + * seconds (for example, GMT-1 has a UTC offset of -3600 seconds). + * + * [Parameters] + * None + * [Return value] + * UTC offset in seconds + */ +int32_t localtime_utc_offset(void) +{ + return utc_offset; +} + +/*************************************************************************/ + +/** + * internal_localtime_r: Local, reentrant version of localtime() used by + * ../smpc.c. Breaks down a timestamp integer into Y/M/D h:m:s + * representation. + * + * [Parameters] + * timep: Timestamp to break down + * result: Broken-down time + * [Return value] + * result, or NULL on error + */ +struct tm *internal_localtime_r(const time_t *timep, struct tm *result) +{ + static const uint8_t mdays[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; + + PRECOND(timep != NULL, return NULL); + PRECOND(result != NULL, return NULL); + time_t t = *timep + utc_offset; + + /* Weekday is simple: ((days since 1970/1/1) + Thursday) % 7 */ + result->tm_wday = (t/86400 + 4) % 7; + + /* Calculate year */ + result->tm_year = 70; + int32_t yearsecs = 365*86400; + while (t >= yearsecs) { + t -= yearsecs; + result->tm_year++; + /* Careful here -- tm_year starts at 1900, not 0 */ + int isleap = (result->tm_year%4 == 0 + && (result->tm_year%100 != 0 + || result->tm_year%400 == 100)); + yearsecs = isleap ? 366*86400 : 365*86400; + } + + /* Calculate month */ + int month = 0; + while (t >= mdays[month] * 86400) { + t -= mdays[month] * 86400; + month++; + } + result->tm_mon = month; + + /* The rest is straightforward */ + result->tm_sec = t % 60; + t /= 60; + result->tm_min = t % 60; + t /= 60; + result->tm_hour = t % 24; + t /= 24; + result->tm_mday = t + 1; + + return result; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/localtime.h b/yabause/src/psp/localtime.h new file mode 100644 index 0000000000..e8bf653a48 --- /dev/null +++ b/yabause/src/psp/localtime.h @@ -0,0 +1,76 @@ +/* src/psp/localtime.h: PSP localtime() header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_LOCALTIME_H +#define PSP_LOCALTIME_H + +#include +#include // For struct tm declaration + +/*************************************************************************/ + +/** + * localtime_init: Perform initialization required for localtime(). + * Called by PSP initialization code at program startup. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void localtime_init(void); + +/** + * localtime_utc_offset: Return the UTC offset of the current time zone in + * seconds (for example, GMT-1 has a UTC offset of -3600 seconds). + * + * [Parameters] + * None + * [Return value] + * UTC offset in seconds + */ +extern int32_t localtime_utc_offset(void); + +/** + * internal_localtime_r: Local, reentrant version of localtime() used by + * ../smpc.c. Breaks down a timestamp integer into Y/M/D h:m:s + * representation. + * + * [Parameters] + * timep: Timestamp to break down + * result: Broken-down time + * [Return value] + * result, or NULL on error + */ +extern struct tm *internal_localtime_r(const time_t *timep, struct tm *result); + +/*************************************************************************/ + +#endif // PSP_LOCALTIME_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/main.c b/yabause/src/psp/main.c new file mode 100644 index 0000000000..7e6be65bb1 --- /dev/null +++ b/yabause/src/psp/main.c @@ -0,0 +1,265 @@ +/* src/psp/main.c: PSP entry point and main loop + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../memory.h" +#include "../peripheral.h" + +#include "config.h" +#include "control.h" +#include "init.h" +#include "menu.h" +#include "misc.h" +#include "osk.h" +#include "psp-video.h" +#include "timing.h" + +#ifdef SYS_PROFILE_H +# include "profile.h" // Can only be ours +#endif + +/*************************************************************************/ +/************************ PSP module information *************************/ +/*************************************************************************/ + +#define MODULE_FLAGS 0 +#define MODULE_VERSION 0 +#define MODULE_REVISION 9 + +PSP_MODULE_INFO("Yabause", MODULE_FLAGS, MODULE_VERSION, MODULE_REVISION); +const PSP_MAIN_THREAD_PRIORITY(THREADPRI_MAIN); +const PSP_MAIN_THREAD_STACK_SIZE_KB(128); +const PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER); +const PSP_HEAP_SIZE_KB(-64); // Leave 64k for thread stacks + +/*************************************************************************/ +/****************************** Global data ******************************/ +/*************************************************************************/ + +/* Program directory (determined from argv[0], and exported) */ +char progpath[256]; + +/* Saturn control pad handle (set at initialization time, and used by menu + * code to change button assignments) */ +void *padbits; + +/* Flag indicating whether the ME is available for use */ +int me_available; + +/* Have we successfully initialized the Yabause core? */ +int yabause_initted; + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Flag indicating whether the menu is currently displayed */ +static int in_menu; + +/* Delay timer for backup RAM autosave */ +static int bup_autosave_timer; +#define BUP_AUTOSAVE_DELAY 60 // frames + +/* Timer for autosave overlay message */ +static int bup_autosave_info_timer; +#define BUP_AUTOSAVE_INFO_TIME 300 // frames + +/* Text and color for autosave overlay message */ +#define BUP_AUTOSAVE_INFO_TEXT "Backup RAM saved." +#define BUP_AUTOSAVE_INFO_COLOR 0xFF55FF40 // TEXT_COLOR_OK from menu.c + +/*-----------------------------------------------------------------------*/ + +/* Local routine declarations */ + +static void iterate_main_loop(void); +static void emulate_one_frame(void); +static void check_autosave(void); + +/*************************************************************************/ +/************************** Program entry point **************************/ +/*************************************************************************/ + +/** + * main: Program entry point. Performs initialization and then loops + * indefinitely, running the emulator. + * + * [Parameters] + * argc: Command line argument count + * argv: Command line argument vector + * [Return value] + * Does not return + */ +int main(int argc, char **argv) +{ + in_menu = 0; + bup_autosave_timer = 0; + bup_autosave_info_timer = 0; + + init_psp(argc, argv); + config_load(); + if (!config_get_start_in_emu()) { + /* Don't initialize yet -- the user may need to set filenames first. */ + menu_open(); + in_menu = 1; + } else { + if (init_yabause()) { + yabause_initted = 1; + } else { + /* Start in the menu so the user sees the error message. */ + menu_open(); + in_menu = 1; + } + } + + timing_init(); + for (;;) { + iterate_main_loop(); + } +} + +/*************************************************************************/ +/*********** Main loop iteration routine and helper functions ************/ +/*************************************************************************/ + +/** + * iterate_main_loop: Run one iteration of the main loop. Either emulates + * one Saturn frame or runs the menu for one PSP frame. + * + * [Parameters] + * buttons: Current state of PSP control buttons + * [Return value] + * None + */ +static void iterate_main_loop(void) +{ + timing_sync(); + control_update(); + + if (control_new_buttons() & PSP_CTRL_SELECT) { + if (in_menu) { + if (osk_status()) { + /* If the OSK is active, SELECT is used to switch character + * sets, so we shouldn't respond to it. */ + } else if (!yabause_initted && !init_yabause()) { + /* We failed to start the emulator, so stay in the menu. */ + } else { + yabause_initted = 1; // In case we just successfully initted + menu_close(); + in_menu = 0; + } + } else { + menu_open(); + in_menu = 1; + } + } + + if (in_menu) { + menu_run(); + } else { + emulate_one_frame(); + } +} + +/*************************************************************************/ + +/** + * emulate_one_frame: Run the emulator for one frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void emulate_one_frame(void) +{ + PERCore->HandleEvents(); // Also runs the actual emulation + + check_autosave(); + + /* Reset the screensaver timeout, so people don't have to deal with + * the backlight dimming during FMV */ + scePowerTick(0); + +#ifdef SYS_PROFILE_H // Print out profiling info every 100 frames + static unsigned int frame = 0; + frame++; + if (frame % 100 == 0) { + printf("Profiling statistics at frame %u:\n", frame); + PROFILE_PRINT(); + PROFILE_RESET(); + } +#endif +} + +/*-----------------------------------------------------------------------*/ + +/** + * check_autosave: Check whether to autosave backup RAM and/or display the + * autosave message. Should be called exactly once per emulated frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void check_autosave(void) +{ + if (BupRamWritten) { + /* Wait BUP_AUTOSAVE_DELAY frames from the last write before we + * update the file, so we don't update on every frame while the + * emulated game is saving its data. */ + bup_autosave_timer = BUP_AUTOSAVE_DELAY; + BupRamWritten = 0; + } else if (bup_autosave_timer > 0) { + bup_autosave_timer--; + if (bup_autosave_timer == 0) { + save_backup_ram(); + bup_autosave_info_timer = BUP_AUTOSAVE_INFO_TIME; + } + } + + if (bup_autosave_info_timer > 0) { + uint32_t color; + if (bup_autosave_info_timer >= BUP_AUTOSAVE_INFO_TIME / 2) { + color = BUP_AUTOSAVE_INFO_COLOR; + } else { + const uint32_t alpha = + (255 * bup_autosave_info_timer) / (BUP_AUTOSAVE_INFO_TIME / 2); + color = alpha<<24 | (BUP_AUTOSAVE_INFO_COLOR & 0x00FFFFFF); + } + psp_video_infoline(color, BUP_AUTOSAVE_INFO_TEXT); + bup_autosave_info_timer--; + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/me-sectend.c b/yabause/src/psp/me-sectend.c new file mode 100644 index 0000000000..348897b726 --- /dev/null +++ b/yabause/src/psp/me-sectend.c @@ -0,0 +1,5 @@ +// Define section end symbols for ../scsp.c +__attribute__((section(".meshared.scsp.sc_write"), aligned(64))) + volatile char __scsp_sectend_sc_write; +__attribute__((section(".meshared.scsp.me_write"), aligned(64))) + volatile char __scsp_sectend_me_write; diff --git a/yabause/src/psp/me-sectstart.c b/yabause/src/psp/me-sectstart.c new file mode 100644 index 0000000000..13d3232a09 --- /dev/null +++ b/yabause/src/psp/me-sectstart.c @@ -0,0 +1,5 @@ +// Define section start symbols for ../scsp.c +__attribute__((section(".meshared.scsp.sc_write"), aligned(64))) + volatile char __scsp_sectstart_sc_write[64]; +__attribute__((section(".meshared.scsp.me_write"), aligned(64))) + volatile char __scsp_sectstart_me_write[64]; diff --git a/yabause/src/psp/me-test.c b/yabause/src/psp/me-test.c new file mode 100644 index 0000000000..2ea1ee3b49 --- /dev/null +++ b/yabause/src/psp/me-test.c @@ -0,0 +1,1468 @@ +/* src/psp/me-test.c: Test program for PSP Media Engine access library + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include +/* Helpful hint for GCC */ +extern void sceKernelExitGame(void) __attribute__((noreturn)); + +#include "me.h" +#include "me-utility.h" + +/* Macro to get the length of an array */ +#define lenof(a) (sizeof((a)) / sizeof((a)[0])) + +/* Size of the ME's data cache, in bytes */ +#define ME_DCACHE_SIZE (256*64) // 256 cache lines of 64 bytes each + +/*************************************************************************/ +/************************ PSP module information *************************/ +/*************************************************************************/ + +#define MODULE_FLAGS \ + (PSP_MODULE_USER | PSP_MODULE_SINGLE_LOAD | PSP_MODULE_SINGLE_START) +#define MODULE_VERSION 1 +#define MODULE_REVISION 0 + +PSP_MODULE_INFO("me-test", MODULE_FLAGS, MODULE_VERSION, MODULE_REVISION); +const PSP_MAIN_THREAD_PRIORITY(32); +const PSP_MAIN_THREAD_STACK_SIZE_KB(64); +const PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER); +const PSP_HEAP_SIZE_KB(-64); + +/*************************************************************************/ +/******************* Forward declarations of routines ********************/ +/*************************************************************************/ + +static int test_meStart(void); +static int test_meCall(void); +static int test_mePoll(void); +static int test_meWait(void); +static int test_meResult(void); +static int test_meException(void); +static int test_meStop(void); +static int test_restart(void); +static int test_meIsME_SC(void); +static int test_meIsME_ME(void); +static int test_meInterruptWait(void); +static int test_meInterruptPoll(void); +static int test_meInterruptClear(void); +static int test_icache(void); +static int test_icache_inval(void); +static int test_dcache_read(void); +static int test_dcache_write(void); +static int test_dcache_inval(void); +static int test_dcache_wbinv(void); + +static int mefunc_return_123(void *param); +static int mefunc_store_456(void *param); +static int mefunc_delay_and_store_789(void *param); +static int mefunc_count_forever(void *param); +static int mefunc_address_error(void *param); +static int mefunc_return_IsME(void *param); +static int mefunc_send_interrupt(void *param); +static int mefunc_dcache_read(void *param); +static int mefunc_dcache_write(void *param); +static int mefunc_dcache_inval(void *param); +static int mefunc_dcache_wbinv(void *param); + +static __attribute__((always_inline)) void delay(const unsigned int cycles); + +/*************************************************************************/ +/***************************** List of tests *****************************/ +/*************************************************************************/ + +typedef struct TestInfo_ { + /* Name of this test */ + const char * const name; + + /* Name of a previous test which must have passed in order to run this + * test (or NULL if none) */ + const char * const precondition; + + /* Routine implementing test */ + int (* const routine)(void); + + /* Result of test (nonzero = passed, zero = failed or not executed) */ + int passed; +} TestInfo; + +static TestInfo tests[] = { + {"meStart", NULL, test_meStart}, + {"meCall", "meStart", test_meCall}, + {"mePoll", "meStart", test_mePoll}, + {"meWait", "meStart", test_meWait}, + {"meResult", "meWait", test_meResult}, + {"meException", "meWait", test_meException}, + {"meStop", "meResult", test_meStop}, + {"restart", "meStop", test_restart}, + + {"meIsME-SC", "restart", test_meIsME_SC}, + {"meIsME-ME", "meIsME-SC", test_meIsME_ME}, + + {"meInterruptWait", "restart", test_meInterruptWait}, + {"meInterruptPoll", "meInterruptWait", test_meInterruptPoll}, + {"meInterruptClear", "meInterruptPoll", test_meInterruptClear}, + + {"icache", "restart", test_icache}, + {"icache-inval", "restart", test_icache_inval}, + {"dcache-read", "restart", test_dcache_read}, + {"dcache-write", "restart", test_dcache_write}, + {"dcache-inval", "restart", test_dcache_inval}, + {"dcache-wbinv", "restart", test_dcache_wbinv}, +}; + +/*************************************************************************/ +/****************************** Entry point ******************************/ +/*************************************************************************/ + +/** + * main: Program entry point. Calls each of the individual tests and + * reports on their success or failure. + * + * [Parameters] + * None + * [Return value] + * Does not return + */ +int main(void) +{ + /* Don't buffer stdout (since we print partial lines as we test). */ + setbuf(stdout, NULL); + + /* Wait a moment to let PSPlink print its "module started" line before + * we start outputting anything, and output a blank line so the first + * line doesn't follow the PSPlink prompt. */ + sceKernelDelayThread(100000); + printf("\n"); + + /* Load the ME library. */ + SceKernelLMOption lmopts; + memset(&lmopts, 0, sizeof(lmopts)); + lmopts.size = sizeof(lmopts); + lmopts.mpidtext = PSP_MEMORY_PARTITION_KERNEL; + lmopts.mpiddata = PSP_MEMORY_PARTITION_KERNEL; + lmopts.position = 0; + lmopts.access = 1; + SceUID modid = sceKernelLoadModule("me.prx", 0, &lmopts); + if (modid < 0) { + fprintf(stderr, "Failed to load me.prx: %08X\n", modid); + sceKernelExitGame(); + } + int dummy; + int res = sceKernelStartModule(modid, strlen("me.prx")+1, "me.prx", + &dummy, NULL); + if (res < 0) { + fprintf(stderr, "Failed to start me.prx: %08X\n", res); + sceKernelUnloadModule(modid); + sceKernelExitGame(); + } + + /* Run all tests in order. */ + unsigned int num_passed = 0, num_skipped = 0; + unsigned int i; + for (i = 0; i < lenof(tests); i++) { + printf("%s...", tests[i].name); + int can_run = 1; + if (tests[i].precondition) { + unsigned int precond_index; + for (precond_index = 0; precond_index < i; precond_index++) { + if (strcmp(tests[i].precondition, + tests[precond_index].name) == 0) { + break; + } + } + if (precond_index >= i) { + printf("skipped (precondition \"%s\" not found or not yet" + " run)\n", tests[i].precondition); + can_run = 0; + } else if (!tests[precond_index].passed) { + printf("skipped (precondition \"%s\" failed or was skipped)", + tests[i].precondition); + can_run = 0; + } + } + if (can_run) { + tests[i].passed = (*tests[i].routine)(); + if (tests[i].passed) { + num_passed++; + printf("passed\n"); + } else { + printf("FAILED\n"); + } + } else { + num_skipped++; + } + } + + /* Print a summary of all results. */ + printf("\n"); + if (num_passed == lenof(tests)) { + printf("All tests passed.\n"); + } else { + printf("%u/%u tests passed (%u failed, %u skipped).\n", + num_passed, lenof(tests), + lenof(tests) - num_passed - num_skipped, num_skipped); + } + printf("\n"); + + /* All done. */ + sceKernelExitGame(); +} + +/*************************************************************************/ +/***************************** Test routines *****************************/ +/*************************************************************************/ + +/** + * test_meStart: Test that the meStart() function succeeds on both the + * first call and a subsequent call. + * + * If the test passes, the ME has been started and can be used in + * subsequent tests. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meStart(void) +{ + int res; + + if ((res = meStart()) != 0) { + fprintf(stderr, "meStart() #1 failed: %08X\n", res); + return 0; + } + + /* Wait for the ME to become ready (done with a manual delay loop since + * we don't assume that any other library functions work yet). */ + delay(1000000); + + if ((res = meStart()) != 0) { + fprintf(stderr, "meStart() #2 failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meCall: Test that the meCall() function can be used to execute a + * routine on the ME. + * + * Assumes that test_meStart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meCall(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_store_456, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + /* Since we don't yet assume meWait() to work properly, delay long + * enough for the function to finish executing before we check whether + * it successfully ran. */ + delay(1000000); + if ((res = *bufptr) != 456) { + fprintf(stderr, "Bad value in buffer (expected 456, got %d)", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_mePoll: Test that the mePoll() function can be used to check + * whether the ME is idle. + * + * Assumes that test_meStart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_mePoll(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_delay_and_store_789, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = *bufptr) != 0) { + fprintf(stderr, "Bad value in buffer (expected 0, got %d)", res); + return 0; + } + + if ((res = mePoll()) != ME_ERROR_BUSY) { + if (res == 0) { + fprintf(stderr, "mePoll() #1 returned idle\n"); + } else { + fprintf(stderr, "mePoll() #1 failed: %08X\n", res); + } + return 0; + } + + delay(2000000); + if ((res = *bufptr) != 789) { + fprintf(stderr, "Bad value in buffer (expected 789, got %d)", res); + return 0; + } + + if ((res = mePoll()) != 0) { + fprintf(stderr, "mePoll() #2 failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meWait: Test that the meWait() function can be used to wait for + * the ME to become idle. + * + * Assumes that test_meStart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meWait(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_delay_and_store_789, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = *bufptr) != 0) { + fprintf(stderr, "Bad value in buffer (expected 0, got %d)", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1 failed: %08X\n", res); + return 0; + } + + if ((res = *bufptr) != 789) { + fprintf(stderr, "Bad value in buffer (expected 789, got %d)", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2 failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meResult: Test that the meResult() function can be used to + * retrieve the result of a routine executed on the ME. + * + * Assumes that test_meWait() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meResult(void) +{ + int res; + + if ((res = meCall(mefunc_return_123, NULL)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + if ((res = meResult()) != 123) { + fprintf(stderr, "meResult() gave wrong result (expected 123, got" + " %d)\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meException: Test that the meException() function can be used to + * retrieve the exception status of a routine executed on the ME. + * + * Assumes that test_meWait() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meException(void) +{ + int res; + + if ((res = meCall(mefunc_return_123, NULL)) != 0) { + fprintf(stderr, "meCall() #1 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1 failed: %08X\n", res); + return 0; + } + + if ((res = meException()) != 0) { + fprintf(stderr, "meException() #1 gave wrong result (expected 0, got" + " %d)\n", res); + return 0; + } + + if ((res = meCall(mefunc_address_error, NULL)) != 0) { + fprintf(stderr, "meCall() #2 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2 failed: %08X\n", res); + return 0; + } + + if ((res = meException()) == 0) { + fprintf(stderr, "meException() #2 gave wrong result (expected" + " nonzero, got zero)\n"); + return 0; + } + + uint32_t BadVAddr = 0, Status = 0, Cause = 0, EPC = 0, ErrorEPC = 0; + meExceptionGetData(&BadVAddr, &Status, &Cause, &EPC, &ErrorEPC); + fprintf(stderr, "Exception data:\n"); + fprintf(stderr, " BadVAddr = %08X\n", BadVAddr); + fprintf(stderr, " Status = %08X\n", Status); + fprintf(stderr, " Cause = %08X\n", Cause); + fprintf(stderr, " EPC = %08X\n", EPC); + fprintf(stderr, " ErrorEPC = %08X\n", ErrorEPC); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meStop: Test that the meStop() function can be used to halt the ME. + * + * Assumes that test_meResult() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meStop(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_count_forever, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + delay(1000000); // Let the counter run for a bit + + /* Stopping the ME while a function is executing is technically an + * undefined operation, but since all the function does is increment a + * counter, we assume it's safe. */ + meStop(); + + delay(1000000); // The ME should be stopped already, but wait to be sure + + int last_count = *bufptr; + delay(1000000); + if ((res = *bufptr) != last_count) { + fprintf(stderr, "meStop() failed to stop ME (counter changed from" + " %d to %d)", last_count, res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_restart: Test that the ME can be safely stopped and restarted + * using the meStop() and meStart() functions. + * + * Assumes that test_meStop() has passed and the ME is currently stopped. + * If the test passes, the ME has been restarted and can be used in + * subsequent tests. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_restart(void) +{ + int res; + + if ((res = meStart()) != 0) { + fprintf(stderr, "meStart() #1 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1A failed: %08X\n", res); + return 0; + } + + if ((res = meCall(mefunc_return_123, NULL)) != 0) { + fprintf(stderr, "meCall() #1 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1B failed: %08X\n", res); + return 0; + } + + if ((res = meResult()) != 123) { + fprintf(stderr, "meResult() #1 gave wrong result (expected 123, got" + " %d)\n", res); + return 0; + } + + meStop(); + delay(1000000); // Wait for the system to settle (just in case) + + if ((res = meStart()) != 0) { + fprintf(stderr, "meStart() #2 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2A failed: %08X\n", res); + return 0; + } + + if ((res = meCall(mefunc_return_123, NULL)) != 0) { + fprintf(stderr, "meCall() #2 failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2B failed: %08X\n", res); + return 0; + } + + if ((res = meResult()) != 123) { + fprintf(stderr, "meResult() #2 gave wrong result (expected 123, got" + " %d)\n", res); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/** + * test_meIsME_SC: Test that meUtilityIsME() properly returns zero when + * executing on the SC. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meIsME_SC(void) +{ + int res = meUtilityIsME(); + + if (res != 0) { + fprintf(stderr, "meUtilityIsME() returned nonzero (%d) on SC\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meIsME_ME: Test that meUtilityIsME() properly returns nonzero when + * executing on the ME. + * + * Assumes that test_meIsME() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meIsME_ME(void) +{ + int res; + + if ((res = meCall(mefunc_return_IsME, NULL)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + if ((res = meResult()) == 0) { + fprintf(stderr, "meResult() gave wrong result (expected nonzero," + " got 0)\n"); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/** + * test_meInterruptWait: Test that meUtilitySendInterrupt() can be used by + * ME code to send an interrupt to the main CPU, and that meInterruptWait() + * can be used to wait until the interrupt is received. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meInterruptWait(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_send_interrupt, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = *bufptr) != 0) { + fprintf(stderr, "Bad value in buffer (expected 0, got %d)", res); + return 0; + } + + if ((res = meInterruptWait()) != 0) { + fprintf(stderr, "meInterruptWait() failed: %08X\n", res); + return 0; + } + + if ((res = *bufptr) != 654) { + fprintf(stderr, "Bad value in buffer (expected 654, got %d)", res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meInterruptPoll: Test that meInterruptPoll() can be used to check + * whether an ME interrupt is pending without clearing the interrupt. Also + * check that meInterruptWait() clears the interrupt after waiting for it. + * + * Assumes that test_meInterruptWait() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meInterruptPoll(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_send_interrupt, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = meInterruptPoll()) != ME_ERROR_NO_INTERRUPT) { + fprintf(stderr, "meInterruptPoll() #1 returned bad value (expected" + " %08X, got %08X)\n", ME_ERROR_NO_INTERRUPT, res); + return 0; + } + + delay(2000000); + + if ((res = meInterruptPoll()) != 0) { + fprintf(stderr, "meInterruptPoll() #2 failed: %08X\n", res); + return 0; + } + + if ((res = meInterruptWait()) != 0) { + fprintf(stderr, "meInterruptWait() failed: %08X\n", res); + return 0; + } + + if ((res = meInterruptPoll()) != ME_ERROR_NO_INTERRUPT) { + fprintf(stderr, "meInterruptPoll() #3 returned bad value (expected" + " %08X, got %08X)\n", ME_ERROR_NO_INTERRUPT, res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_meInterruptClear: Test that meInterruptClear() can be used to + * clear a pending ME interrupt. + * + * Assumes that test_meInterruptPoll() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_meInterruptClear(void) +{ + static __attribute__((aligned(64))) int buffer; + volatile int *bufptr = (volatile int *)((uintptr_t)&buffer | 0x40000000); + int res; + + *bufptr = 0; + if ((res = meCall(mefunc_send_interrupt, (void *)bufptr)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + if ((res = meInterruptPoll()) != ME_ERROR_NO_INTERRUPT) { + fprintf(stderr, "meInterruptPoll() #1 returned bad value (expected" + " %08X, got %08X)\n", ME_ERROR_NO_INTERRUPT, res); + return 0; + } + + delay(2000000); + + if ((res = meInterruptPoll()) != 0) { + fprintf(stderr, "meInterruptPoll() #2 failed: %08X\n", res); + return 0; + } + + meInterruptClear(); + + if ((res = meInterruptPoll()) != ME_ERROR_NO_INTERRUPT) { + fprintf(stderr, "meInterruptPoll() #3 returned bad value (expected" + " %08X, got %08X)\n", ME_ERROR_NO_INTERRUPT, res); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/** + * test_icache: Test that the ME's instruction cache operates as expected. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_icache(void) +{ + static __attribute__((aligned(64))) uint32_t insn_buf[2]; + volatile uint32_t *insn_ptr = + (volatile uint32_t *)((uintptr_t)&insn_buf[0] | 0x40000000); + int res; + + insn_ptr[0] = 0x03E00008; // jr $ra + insn_ptr[1] = 0x34021234; // li $v0, 0x1234 + + /* Note that we use insn_buf (the cacheable version) rather than + * insn_ptr (the uncacheable one), because we want the ME to cache the + * instructions and therefore _not_ pick up our subsequent change. */ + if ((res = meCall((void *)insn_buf, NULL)) != 0) { + fprintf(stderr, "meCall() #1 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x1234) { + fprintf(stderr, "meResult() #1 gave wrong result (expected 0x1234," + " got 0x%X)\n", res); + return 0; + } + + insn_ptr[1] = 0x34025678; // li $v0, 0x5678 (not seen by ME) + + if ((res = meCall((void *)insn_buf, NULL)) != 0) { + fprintf(stderr, "meCall() #2 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x1234) { + fprintf(stderr, "meResult() #2 gave wrong result (expected 0x1234," + " got 0x%X)\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_icache_inval: Test that meUtilityIcacheInvalidateAll() can be used + * to invalidate the ME's instruction cache. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_icache_inval(void) +{ + static __attribute__((aligned(64))) uint32_t insn_buf[8]; + volatile uint32_t *insn_ptr = + (volatile uint32_t *)((uintptr_t)insn_buf | 0x40000000); + int res; + + insn_ptr[0] = 0x27BDFFF8; // addiu $sp, $sp, -8 + insn_ptr[1] = 0xAFBF0004; // sw $ra, 4($sp) + insn_ptr[2] = 0x0080F809; // jalr $a0 + insn_ptr[3] = 0x00000000; // nop + insn_ptr[4] = 0x8FBF0004; // lw $ra, 4($sp) + insn_ptr[5] = 0x34024321; // li $v0, 0x4321 + insn_ptr[6] = 0x03E00008; // jr $ra + insn_ptr[7] = 0x27BD0008; // addiu $sp, $sp, 8 + + /* Use mefunc_return_123() as a do-nothing function to take the place of + * meUtilityIcacheInvalidateAll(). */ + if ((res = meCall((void *)insn_buf, mefunc_return_123)) != 0) { + fprintf(stderr, "meCall() #1 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #1 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x4321) { + fprintf(stderr, "meResult() #1 gave wrong result (expected 0x4321," + " got 0x%X)\n", res); + return 0; + } + + /* Change the load instruction from the main CPU, but don't flush the + * ME's cache. */ + + insn_ptr[5] = 0x34025432; // li $v0, 0x5432 (not seen by ME) + + if ((res = meCall((void *)insn_buf, mefunc_return_123)) != 0) { + fprintf(stderr, "meCall() #2 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #2 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x4321) { + fprintf(stderr, "meResult() #2 gave wrong result (expected 0x4321," + " got 0x%X)\n", res); + return 0; + } + + /* Change the load instruction and flush the ME's cache. */ + + insn_ptr[5] = 0x34026543; // li $v0, 0x6543 + + if ((res = meCall((void *)insn_buf, meUtilityIcacheInvalidateAll)) != 0) { + fprintf(stderr, "meCall() #3 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #3 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x6543) { + fprintf(stderr, "meResult() #3 gave wrong result (expected 0x6543," + " got 0x%X)\n", res); + return 0; + } + + /* Test once more to make sure the new instruction is properly cached. */ + + insn_ptr[5] = 0x34027654; // li $v0, 0x7654 (not seen by ME) + + if ((res = meCall((void *)insn_buf, mefunc_return_123)) != 0) { + fprintf(stderr, "meCall() #4 failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() #4 failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0x6543) { + fprintf(stderr, "meResult() #4 gave wrong result (expected 0x6543," + " got 0x%X)\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_dcache_read: Test that the ME's data cache operates as expected + * when reading from the cache. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_dcache_read(void) +{ + static __attribute__((aligned(64))) int buffer[ME_DCACHE_SIZE/4]; + volatile int *bufptr = (volatile int *)((uintptr_t)buffer | 0x40000000); + int res, i; + + for (i = 0; i < lenof(buffer); i++) { + bufptr[i] = 0; + } + + if ((res = meCall(mefunc_dcache_read, buffer)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + /* Wait long enough for the ME to finish reading from the buffer before + * we update it. */ + delay(ME_DCACHE_SIZE*4); // Experimentally determined to be sufficient + for (i = 0; i < lenof(buffer); i++) { + bufptr[i] = 0xFFFFFFFF; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + if ((res = meResult()) != 0) { + fprintf(stderr, "meResult() gave wrong result (expected 0, got %d)\n", + res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_dcache_write: Test that the ME's data cache operates as expected + * when writing to the cache. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_dcache_write(void) +{ + static __attribute__((aligned(64))) int buffer[ME_DCACHE_SIZE/4]; + volatile int *bufptr = (volatile int *)((uintptr_t)buffer | 0x40000000); + int res, i; + + for (i = 0; i < lenof(buffer); i++) { + bufptr[i] = -1; + } + + if ((res = meCall(mefunc_dcache_write, buffer)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + + /* Wait long enough for the ME to finish writing to the buffer before + * we read from it. We don't use meWait() because allowing the + * function to return will flush some lines from the cache when the + * library updates its internal state. */ + delay(ME_DCACHE_SIZE*4); + + res = -1; + for (i = 0; i < lenof(buffer); i++) { + res &= bufptr[i]; + } + if (res != -1) { + fprintf(stderr, "Bad buffer contents (expected -1, got %d)\n", res); + meWait(); + return 0; + } + + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_dcache_inval: Test that meUtilityDcacheInvalidateAll() can be used + * to invalidate the ME's data cache. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_dcache_inval(void) +{ + static __attribute__((aligned(64))) int buffer[ME_DCACHE_SIZE/4]; + volatile int *bufptr = (volatile int *)((uintptr_t)buffer | 0x40000000); + int res, i; + + for (i = 0; i < lenof(buffer); i++) { + bufptr[i] = 0xFFFFFFFF; + } + + if ((res = meCall(mefunc_dcache_inval, buffer)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + res = -1; + for (i = 0; i < lenof(buffer); i++) { + res &= bufptr[i]; + } + if (res != -1) { + fprintf(stderr, "Bad buffer contents (expected -1, got %d)\n", res); + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * test_dcache_wbinv: Test that meUtilityDcacheWritebackInvalidateAll() + * can be used to flush (write back and invalidate) the ME's data cache. + * + * Assumes that test_restart() has passed. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the test passes, zero if it fails + */ +static int test_dcache_wbinv(void) +{ + static __attribute__((aligned(64))) int buffer[ME_DCACHE_SIZE/4]; + volatile int *bufptr = (volatile int *)((uintptr_t)buffer | 0x40000000); + int res, i; + + for (i = 0; i < lenof(buffer); i++) { + bufptr[i] = 0xFFFFFFFF; + } + + if ((res = meCall(mefunc_dcache_wbinv, buffer)) != 0) { + fprintf(stderr, "meCall() failed: %08X\n", res); + return 0; + } + if ((res = meWait()) != 0) { + fprintf(stderr, "meWait() failed: %08X\n", res); + return 0; + } + + res = 0; + for (i = 0; i < lenof(buffer); i++) { + res |= bufptr[i]; + } + if (res != 0) { + fprintf(stderr, "Bad buffer contents (expected 0, got %d)\n", res); + return 0; + } + + + return 1; +} + +/*************************************************************************/ +/********************** Routines executed on the ME **********************/ +/*************************************************************************/ + +/** + * mefunc_return_123: Return 123 to the caller. + * + * [Parameters] + * param: Unused + * [Return value] + * 123 + */ +static int mefunc_return_123(void *param) +{ + return 123; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_store_456: Store 456 in the location pointed to by the parameter. + * + * [Parameters] + * param: Pointer to an int variable to receive 456. + * [Return value] + * 0 + */ +static int mefunc_store_456(void *param) +{ + *(int *)param = 456; + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_delay_and_store_789: Delay for 1M cycles, then Store 789 in the + * location pointed to by the parameter. + * + * [Parameters] + * param: Pointer to an int variable to receive 789 + * [Return value] + * 0 + */ +static int mefunc_delay_and_store_789(void *param) +{ + delay(1000000); + *(int *)param = 789; + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_count_forever: Loop indefinitely, continuously incrementing the + * location pointed to by the parameter. + * + * [Parameters] + * param: Pointer to an int variable to be continuously incremented + * [Return value] + * Does not return + */ +static int mefunc_count_forever(void *param) +{ + volatile int *ptr = (volatile int *)param; + for (;;) { + (*ptr)++; + } + return 0; // Not reached, but avoid a compiler warning +} + +/*************************************************************************/ + +/** + * mefunc_address_error: Trigger an address error by accessing an + * unaligned value. + * + * [Parameters] + * param: Unused + * [Return value] + * Does not return + */ +static int mefunc_address_error(void *param) +{ + int dummy = 321; + return *(int *)((uintptr_t)&dummy | 1); +} + +/*************************************************************************/ + +/** + * mefunc_return_IsME(): Return the value returned by meUtilityIsME(). + * + * [Parameters] + * param: Unused + * [Return value] + * Nonzero + */ +static int mefunc_return_IsME(void *param) +{ + return meUtilityIsME(); +} + +/*************************************************************************/ + +/** + * mefunc_send_interrupt: Wait 1M cycles, then store 654 in the location + * pointed to by the parameter and send an interrupt to the main CPU. + * + * [Parameters] + * param: Pointer to an int variable to receive 654 + * [Return value] + * 0 + */ +static int mefunc_send_interrupt(void *param) +{ + delay(1000000); + *(int *)param = 654; + meUtilitySendInterrupt(); + return 0; +} + +/*************************************************************************/ + +/** + * mefunc_dcache_read: Read ME_DCACHE_SIZE/4 words of memory starting at + * the location pointed to by the parameter, wait 1M cycles, then read the + * same words and return their combined bitwise OR. + * + * [Parameters] + * param: Pointer to a buffer of ME_DCACHE_SIZE/4 32-bit words + * [Return value] + * Bitwise OR of all words in buffer + */ +static int mefunc_dcache_read(void *param) +{ + volatile uint32_t *ptr = (volatile uint32_t *)param; + uint32_t res; + int i; + + for (i = 0; i < ME_DCACHE_SIZE/4; i++) { + (void) ptr[i]; + } + + delay(1000000); + + res = 0; + for (i = 0; i < ME_DCACHE_SIZE/4; i++) { + res |= ptr[i]; + } + return res; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_dcache_write: Write all zeroes to ME_DCACHE_SIZE/4 words of + * memory starting at the location pointed to by the parameter, then wait + * 1M cycles before returning. + * + * [Parameters] + * param: Pointer to a buffer of ME_DCACHE_SIZE/4 32-bit words + * [Return value] + * 0 + */ +static int mefunc_dcache_write(void *param) +{ + volatile uint32_t *ptr = (volatile uint32_t *)param; + int i; + + for (i = 0; i < ME_DCACHE_SIZE/4; i++) { + ptr[i] = 0; + } + + delay(1000000); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_dcache_inval: Write all zeroes to ME_DCACHE_SIZE/4 words of + * memory starting at the location pointed to by the parameter, then + * invalidate the data cache (thus nullifying the writes) and return. + * + * [Parameters] + * param: Pointer to a buffer of ME_DCACHE_SIZE/4 32-bit words + * [Return value] + * 0 + */ +static int mefunc_dcache_inval(void *param) +{ + volatile uint32_t *ptr = (volatile uint32_t *)param; + int i; + + /* Normally, we would have to flush the data cache once to ensure that + * values pushed to the stack (like $ra) were properly stored to memory + * before the invalidate call. In this case, however, we completely + * replace the cache set, so any cached stack values will be implicitly + * flushed by the time we're done. */ + + for (i = 0; i < ME_DCACHE_SIZE/4; i++) { + ptr[i] = 0; + } + + meUtilityDcacheInvalidateAll(); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mefunc_dcache_wbinv: Write all zeroes to ME_DCACHE_SIZE/4 words of + * memory starting at the location pointed to by the parameter, then flush + * the data cache and return. + * + * [Parameters] + * param: Pointer to a buffer of ME_DCACHE_SIZE/4 32-bit words + * [Return value] + * 0 + */ +static int mefunc_dcache_wbinv(void *param) +{ + volatile uint32_t *ptr = (volatile uint32_t *)param; + int i; + + for (i = 0; i < ME_DCACHE_SIZE/4; i++) { + ptr[i] = 0; + } + + meUtilityDcacheWritebackInvalidateAll(); + return 0; +} + +/*************************************************************************/ +/************************ Other utility routines *************************/ +/*************************************************************************/ + +/** + * delay: Delay the calling function for approximately the given number of + * CPU clock cycles. The actual length of the delay may differ from the + * requested length by up to four cycles. + * + * [Parameters] + * cycles: Cycles to delay + * [Return value] + * None + */ +static __attribute__((always_inline)) void delay(const unsigned int cycles) +{ + unsigned int iterations = cycles/4; + asm volatile(" .set push; .set noreorder\n" + "1: bnez %[iterations], 1b\n" + " addiu %[iterations], %[iterations], -1\n" + " .set pop" + : [iterations] "=r" (iterations) + : "0" (iterations) + ); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/me-utility.c b/yabause/src/psp/me-utility.c new file mode 100644 index 0000000000..8731520ffe --- /dev/null +++ b/yabause/src/psp/me-utility.c @@ -0,0 +1,136 @@ +/* src/psp/me-utility.c: PSP Media Engine utility routines + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "me.h" +#include "me-utility.h" + +/*************************************************************************/ +/*************************************************************************/ + +/** + * meUtilityIcacheInvalidateAll: Invalidate all entries in the Media + * Engine's instruction cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meUtilityIcacheInvalidateAll(void) +{ + unsigned int cachesize_bits; + asm volatile("mfc0 %0, $16; ext %0, %0, 9, 3" : "=r" (cachesize_bits)); + const unsigned int cachesize = 4096 << cachesize_bits; + + asm volatile("mtc0 $zero, $28"); // TagLo + asm volatile("mtc0 $zero, $29"); // TagHi + unsigned int i; + for (i = 0; i < cachesize; i += 64) { + asm volatile("cache 0x1, 0(%0)" : : "r" (i)); + asm volatile("cache 0x3, 0(%0)" : : "r" (i)); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * meUtilityDcacheInvalidateAll: Invalidate all entries in the Media + * Engine's data cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meUtilityDcacheInvalidateAll(void) +{ + unsigned int cachesize_bits; + asm volatile("mfc0 %0, $16; ext %0, %0, 6, 3" : "=r" (cachesize_bits)); + const unsigned int cachesize = 4096 << cachesize_bits; + + asm volatile("mtc0 $zero, $28"); // TagLo + asm volatile("mtc0 $zero, $29"); // TagHi + unsigned int i; + for (i = 0; i < cachesize; i += 64) { + asm volatile("cache 0x11, 0(%0)" : : "r" (i)); + asm volatile("cache 0x13, 0(%0)" : : "r" (i)); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * meUtilityDcacheWritebackInvalidateAll: Write back and then invalidate + * all entries in the Media Engine's data cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meUtilityDcacheWritebackInvalidateAll(void) +{ + unsigned int cachesize_bits; + asm volatile("mfc0 %0, $16; ext %0, %0, 6, 3" : "=r" (cachesize_bits)); + const unsigned int cachesize = 4096 << cachesize_bits; + + unsigned int i; + for (i = 0; i < cachesize; i += 64) { + asm volatile("cache 0x14, 0(%0)" : : "r" (i)); + } + asm volatile("sync"); +} + +/*************************************************************************/ + +/** + * meUtilitySendInterrupt: Send an interrupt to the main CPU. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meUtilitySendInterrupt(void) +{ + asm volatile("sync"); + asm volatile("sw %0, 0x44(%1)" : : "r" (1), "r" (0xBC100000)); + asm volatile("sync"); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/me-utility.h b/yabause/src/psp/me-utility.h new file mode 100644 index 0000000000..460f0a60d6 --- /dev/null +++ b/yabause/src/psp/me-utility.h @@ -0,0 +1,105 @@ +/* src/psp/me-utility.h: PSP Media Engine utility routine header + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ME_UTILITY_H +#define ME_UTILITY_H + +/*************************************************************************/ + +/** + * meUtilityIsME: Return whether the current CPU is the ME (nonzero) or + * the SC (zero). + * + * [Parameters] + * None + * [Return value] + * Nonzero if executing on the ME, zero if executing on the SC + */ +static inline int meUtilityIsME(void) +{ + int test; + asm("xor %0, $k0, %1" : "=r" (test) : "r" (ME_K0_MAGIC)); + return test == 0; +} + +/*----------------------------------*/ + +/** + * meUtilityIcacheInvalidateAll: Invalidate all entries in the Media + * Engine's instruction cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meUtilityIcacheInvalidateAll(void); + +/** + * meUtilityDcacheInvalidateAll: Invalidate all entries in the Media + * Engine's data cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meUtilityDcacheInvalidateAll(void); + +/** + * meUtilityDcacheWritebackInvalidateAll: Write back and then invalidate + * all entries in the Media Engine's data cache. + * + * This routine may only be called from code executing on the Media Engine. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meUtilityDcacheWritebackInvalidateAll(void); + +/** + * meUtilitySendInterrupt: Send an interrupt to the main CPU. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meUtilitySendInterrupt(void); + +/*************************************************************************/ + +#endif // ME_UTILITY_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/me.c b/yabause/src/psp/me.c new file mode 100644 index 0000000000..df81d811e3 --- /dev/null +++ b/yabause/src/psp/me.c @@ -0,0 +1,936 @@ +/* src/psp/me.c: PSP Media Engine access library + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * This library provides simple, low-level access to the Media Engine CPU. + * Typical usage is as follows (error checks are omitted): + * + * init() { + * meStart(); + * } + * + * main() { + * meCall(function, parameter); + * meWait(); // or use mePoll() in a loop + * if (meException()) { + * // An exception (address error, etc.) occurred + * } else { + * result = meResult(); + * } + * } + * + * Code running on the Media Engine cannot call any external library + * functions which are gated through the syscall interface (which naturally + * includes all firmware functions). However, a few utility functions are + * available in me-utility.[ch] for operations such as cache management. + * + * This library trusts the caller completely, and runs code on the ME in + * kernel mode. Callers must therefore be careful about errors in the + * functions they execute, since improper writes to hardware registers + * could destroy flash or Memory Stick data or have other catastrophic + * results. + * + * Naturally, this library cannot be used alongside any firmware functions + * which make use of the ME, such as audio or video decoding. It is also + * currently impossible to suspend the PSP with the power switch after + * calling meStart(), even if meStop() is later called to halt ME + * processing. (Further investigation is needed to determine why this + * occurs.) + */ + +#include +extern int sceSysregAvcResetEnable(void); // Missing from pspsysreg.h + +#include "me.h" + +/*************************************************************************/ +/************************ PSP module information *************************/ +/*************************************************************************/ + +#define MODULE_FLAGS \ + (PSP_MODULE_KERNEL | PSP_MODULE_SINGLE_LOAD | PSP_MODULE_SINGLE_START) +#define MODULE_VERSION 1 +#define MODULE_REVISION 1 + +PSP_MODULE_INFO("melib", MODULE_FLAGS, MODULE_VERSION, MODULE_REVISION); + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Local function declarations */ + +static void maybe_raise_exception(void); + +static int interrupt_handler(void); + +extern void me_init, me_init_end; +__attribute__((section(".text.me"), noreturn)) void me_loop(void); +__attribute__((section(".text.me"), noreturn)) void me_exception_finish(void); + +/*************************************************************************/ + +/* Has the ME been started? */ + +static int me_started; + +/* Should ME exceptions be fatal to the caller? */ + +static int exceptions_are_fatal; + +/*----------------------------------*/ + +/* Message block for signaling between the main CPU and Media Engine */ + +typedef struct MEMessageBlock_ { + /* ME idle flag; nonzero indicates that the Media Engine is idle and + * ready to accept a new execute request. Set only by the ME; cleared + * only by the main CPU. */ + int idle; + + /* Execute request flag; nonzero indicates that the execute request + * fields below have been filled in and the ME should start executing + * the given function. Set only by the main CPU; cleared only by the + * ME (except when cleared by the main CPU before starting the ME). */ + int request; + + /* Function to be executed. Written only by the main CPU. */ + int (*function)(void *); + + /* Argument to be passed to function. Written only by the main CPU. */ + void *argument; + + /* Return value of last function executed. Written only by the ME. */ + int result; + + /* Exception flag; nonzero indicates that the last function executed on + * the ME triggered an exception. Set only by the ME; cleared only by + * the main CPU. */ + int exception; +} MEMessageBlock; + +static volatile __attribute__((aligned(64))) + MEMessageBlock message_block_buffer; + +/*----------------------------------*/ + +/* Buffer for storing registers on an ME exception (buffer size must be a + * multiple of the cache line size) */ + +typedef struct MEExceptionRegs_ { + uint32_t r[32]; + uint32_t hi, lo; + uint32_t BadVAddr; + uint32_t Status; + uint32_t Cause; + uint32_t EPC; + uint32_t ErrorEPC; + uint32_t pad[9]; +} MEExceptionRegs; + +static __attribute__((aligned(64),used)) MEExceptionRegs exception_registers; + +/*----------------------------------*/ + +/* Event flag used for catching ME interrupts */ + +static SceUID interrupt_flag; + +/*************************************************************************/ +/************************** Module entry points **************************/ +/*************************************************************************/ + +/** + * module_start: Entry point called when the module is started. + * + * [Parameters] + * None + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +extern int module_start(void); +int module_start(void) +{ + /* Create an event flag for receiving interrupts from the ME. */ + interrupt_flag = sceKernelCreateEventFlag("meInterruptFlag", + PSP_EVENT_WAITMULTIPLE, 0, 0); + if (interrupt_flag < 0) { + return interrupt_flag; + } + + /* Install our interrupt handler (overwriting any handler installed + * by the firmware). */ + sceKernelReleaseIntrHandler(31); + int res = + sceKernelRegisterIntrHandler(31, 2, interrupt_handler, NULL, NULL); + if (res < 0) { + sceKernelDeleteEventFlag(interrupt_flag); + interrupt_flag = 0; + return res; + } + sceKernelEnableIntr(31); + + return 0; +} + +/*************************************************************************/ + +/** + * module_stop: Entry point called when the module is stopped. + * + * [Parameters] + * None + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +extern int module_stop(void); +int module_stop(void) +{ + if (me_started) { + meStop(); + } + + sceKernelDisableIntr(31); + sceKernelReleaseIntrHandler(31); + + sceKernelDeleteEventFlag(interrupt_flag); + interrupt_flag = 0; + + return 0; +} + +/*************************************************************************/ +/************************** Interface routines ***************************/ +/*************************************************************************/ + +/** + * meStart: Start up the Media Engine. This function must be called + * before any other Media Engine operations are performed. + * + * [Parameters] + * None + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +int meStart(void) +{ + uint32_t old_k1; + asm volatile("move %0, $k1; move $k1, $zero" : "=r" (old_k1)); + + if (me_started) { + asm volatile("move $k1, %0" : : "r" (old_k1)); + return 0; + } + + /* We generate this pointer on the fly rather than storing it in a + * static variable because attempting to access that static variable + * would pull in the cache line, which is exactly what we're trying + * to avoid. */ + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + message_block->idle = 0; + message_block->request = 0; + meInterruptClear(); + + /* Roll our own memcpy() to avoid an unneeded libc reference. */ + const uint32_t *src = (const uint32_t *)&me_init; + const uint32_t *src_top = (const uint32_t *)&me_init_end; + uint32_t *dest = (uint32_t *)0xBFC00040; + for (; src < src_top; src++, dest++) { + *dest = *src; + } + + /* Flush the data cache, just to be safe. */ + sceKernelDcacheWritebackInvalidateAll(); + + int res; + if ((res = sceSysregMeResetEnable()) < 0 + || (res = sceSysregMeBusClockEnable()) < 0 + || (res = sceSysregMeResetDisable()) < 0 + ) { + asm volatile("move $k1, %0" : : "r" (old_k1)); + return res; + } + + me_started = 1; + + asm volatile("move $k1, %0" : : "r" (old_k1)); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meStop: Stop the Media Engine. No other Media Engine functions except + * meStart() may be called after this function returns. + * + * If code is currently executing on the Media Engine when this function is + * called, the effect on system state is undefined. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meStop(void) +{ + uint32_t old_k1; + asm volatile("move %0, $k1; move $k1, $zero" : "=r" (old_k1)); + + sceSysregVmeResetEnable(); + sceSysregAvcResetEnable(); + sceSysregMeResetEnable(); + sceSysregMeBusClockDisable(); + + me_started = 0; + + asm volatile("move $k1, %0" : : "r" (old_k1)); +} + +/*************************************************************************/ + +/** + * meCall: Begin executing the given function on the Media Engine. The + * function must not call any firmware functions, whether directly or + * indirectly. This routine may only be called when the Media Engine is + * idle (i.e., when mePoll() returns zero). + * + * [Parameters] + * function: Function pointer + * argument: Optional argument to function + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +int meCall(int (*function)(void *), void *argument) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + if (!message_block->idle || message_block->request) { + return ME_ERROR_BUSY; + } + + message_block->exception = 0; + message_block->function = function; + message_block->argument = argument; + message_block->request = 1; + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * mePoll: Check whether the Media Engine is idle. + * + * [Parameters] + * None + * [Return value] + * Zero if the Media Engine is idle, otherwise an error code (negative) + */ +int mePoll(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + if (!message_block->idle || message_block->request) { + return ME_ERROR_BUSY; + } + maybe_raise_exception(); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meWait: Wait for the Media Engine to become idle. + * + * [Parameters] + * None + * [Return value] + * Zero if the Media Engine has become (or was already) idle, + * otherwise an error code (negative) + */ +int meWait(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + while (!message_block->idle || message_block->request) { /*spin*/ } + maybe_raise_exception(); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meResult: Return the result (return value) of the most recently + * executed function. The result is undefined if the Media Engine has not + * been started or is not idle, if the most recently executed function did + * not return a result (i.e. had a return type of void), or if the most + * recently executed function triggered an exception. + * + * [Parameters] + * None + * [Return value] + * Result of the most recently executed function + */ +int meResult(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + return message_block->result; +} + +/*************************************************************************/ + +/** + * meException: Return whether the most recently executed function + * triggered an exception. The result is undefined if the Media Engine has + * not been started or is not idle. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the most recently executed function triggered an + * exception, else zero + */ +int meException(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + return message_block->exception; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meExceptionGetData: Retrieve CPU status register values related to the + * most recent exception. The values retrieved are undefined in any case + * where meException() returns zero or has an undefined return value. + * + * NULL can be passed for any unneeded register values. + * + * [Parameters] + * BadVAddr_ret: Pointer to variable to receive BadVAddr register value + * Status_ret: Pointer to variable to receive Status register value + * Cause_ret: Pointer to variable to receive Cause register value + * EPC_ret: Pointer to variable to receive EPC register value + * ErrorEPC_ret: Pointer to variable to receive ErrorEPC register value + * [Return value] + * None + */ +void meExceptionGetData(uint32_t *BadVAddr_ret, uint32_t *Status_ret, + uint32_t *Cause_ret, uint32_t *EPC_ret, + uint32_t *ErrorEPC_ret) +{ + if (!me_started) { + return; + } + + if (BadVAddr_ret) { + *BadVAddr_ret = exception_registers.BadVAddr; + } + if (Status_ret) { + *Status_ret = exception_registers.Status; + } + if (Cause_ret) { + *Cause_ret = exception_registers.Cause; + } + if (EPC_ret) { + *EPC_ret = exception_registers.EPC; + } + if (ErrorEPC_ret) { + *ErrorEPC_ret = exception_registers.ErrorEPC; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * meExceptionSetFatal: Set whether an exception on the Media Engine + * should automatically trigger an exception on the main CPU. If enabled, + * any call to mePoll() or meWait() when an exception is pending will cause + * an address error exception to be generated on the main CPU, with + * exception status information stored in a buffer pointed to by $gp: + * 0($gp) = Status + * 4($gp) = Cause + * 8($gp)= EPC or ErrorEPC (depending on the exception type) + * 12($gp) = BadVAddr + * 16($gp) = Media Engine's $sp + * 20($gp) = Media Engine's $gp + * All general-purpose registers other than $sp and $gp, as well as $hi and + * $lo, are copied from the Media Engine. + * + * Unlike other functions in this library, this function can be called even + * when the ME is not running, and it will always succeed. + * + * By default, exceptions are not fatal. + * + * [Parameters] + * fatal: Nonzero to make exceptions fatal, zero to make them nonfatal + * [Return value] + * None + */ +void meExceptionSetFatal(int fatal) +{ + exceptions_are_fatal = (fatal != 0); +} + +/*************************************************************************/ + +/** + * meInterruptPoll: Return whether an interrupt from the Media Engine is + * pending. Any pending interrupt is _not_ cleared. + * + * [Parameters] + * None + * [Return value] + * Zero if an interrupt from the Media Engine is pending, otherwise an + * error code (negative) + */ +int meInterruptPoll(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + uint32_t old_k1; + asm volatile("move %0, $k1; move $k1, $zero" : "=r" (old_k1)); + int res = sceKernelPollEventFlag(interrupt_flag, 1, 0, NULL); + asm volatile("move $k1, %0" : : "r" (old_k1)); + + if (res == SCE_KERNEL_ERROR_EVF_COND) { + return ME_ERROR_NO_INTERRUPT; + } else if (res < 0) { + return res; + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meInterruptWait: Wait for an interrupt from the Media Engine if none is + * already pending, then clear the interrupt and return. + * + * [Parameters] + * None + * [Return value] + * Zero if an interrupt was received from the Media Engine, otherwise + * an error code (negative) + */ +int meInterruptWait(void) +{ + if (!me_started) { + return ME_ERROR_NOT_STARTED; + } + + uint32_t old_k1; + asm volatile("move %0, $k1; move $k1, $zero" : "=r" (old_k1)); + int res = sceKernelWaitEventFlag(interrupt_flag, 1, + PSP_EVENT_WAITCLEAR, NULL, NULL); + asm volatile("move $k1, %0" : : "r" (old_k1)); + + if (res < 0) { + return res; + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * meInterruptClear: Clear any pending Media Engine interrupt. + * + * [Parameters] + * None + * [Return value] + * None + */ +void meInterruptClear(void) +{ + uint32_t old_k1; + asm volatile("move %0, $k1; move $k1, $zero" : "=r" (old_k1)); + /* This function is slightly misnamed--it doesn't clear the bits + * specified by the second parameter, but simply performs a bitwise + * AND between the current flag value and the second parameter. */ + sceKernelClearEventFlag(interrupt_flag, 0); + asm volatile("move $k1, %0" : : "r" (old_k1)); +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * maybe_raise_exception: If fatal exceptions have been requested via + * meExceptionSetFatal() and an ME exception is pending, load registers as + * described in the meExceptionSetFatal() documentation and generate an + * address error exception. + * + * Assumes that the ME is currently idle. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void maybe_raise_exception(void) +{ + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + static uint32_t exception_info[6]; + + if (exceptions_are_fatal && message_block->exception) { + asm volatile(".set push; .set noreorder; .set noat\n" + "move $at, %[exception_registers]\n" + "move $gp, %[exception_info]\n" + "lw $t8, %[Status]($at)\n" + "lw $t9, %[Cause]($at)\n" + "lw $k0, %[EPC]($at)\n" + "andi $v0, $t8, 4\n" + "bnezl $v0, 1f\n" + "lw $k0, %[ErrorEPC]($at)\n" + "1:\n" + "lw $k1, %[BadVAddr]($at)\n" + "lw $v0, 120($at)\n" + "lw $v1, 116($at)\n" + "sw $t8, 0($gp)\n" + "sw $t9, 4($gp)\n" + "sw $k0, 8($gp)\n" + "sw $k1, 12($gp)\n" + "sw $v0, 16($gp)\n" + "sw $v1, 20($gp)\n" + "lw $v0, 128($at)\n" + "lw $v1, 132($at)\n" + "mthi $v0\n" + "mtlo $v1\n" + "lw $v0, 8($at)\n" + "lw $v1, 12($at)\n" + "lw $a0, 16($at)\n" + "lw $a1, 20($at)\n" + "lw $a2, 24($at)\n" + "lw $a3, 28($at)\n" + "lw $t0, 32($at)\n" + "lw $t1, 36($at)\n" + "lw $t2, 40($at)\n" + "lw $t3, 44($at)\n" + "lw $t4, 48($at)\n" + "lw $t5, 52($at)\n" + "lw $t6, 56($at)\n" + "lw $t7, 60($at)\n" + "lw $s0, 64($at)\n" + "lw $s1, 68($at)\n" + "lw $s2, 72($at)\n" + "lw $s3, 76($at)\n" + "lw $s4, 80($at)\n" + "lw $s5, 84($at)\n" + "lw $s6, 88($at)\n" + "lw $s7, 92($at)\n" + "lw $t8, 96($at)\n" + "lw $t9, 100($at)\n" + "lw $k0, 104($at)\n" + "lw $k1, 108($at)\n" + "lw $fp, 120($at)\n" + "lw $ra, 124($at)\n" + "lw $at, 4($at)\n" + "sw $zero, -16162($zero)\n" + ".set pop" + : "=m" (exception_info) + : [exception_info] "r" (&exception_info), + [exception_registers] "r" (&exception_registers), + "m" (exception_registers), + [Status] "i" (offsetof(MEExceptionRegs,Status)), + [Cause] "i" (offsetof(MEExceptionRegs,Cause)), + [BadVAddr] "i" (offsetof(MEExceptionRegs,BadVAddr)), + [EPC] "i" (offsetof(MEExceptionRegs,EPC)), + [ErrorEPC] "i" (offsetof(MEExceptionRegs,ErrorEPC)) + ); + } +} + +/*************************************************************************/ + +/** + * interrupt_handler: Handler for Media Engine interrupts. + * + * [Parameters] + * None + * [Return value] + * Always -1 + */ +static int interrupt_handler(void) +{ + sceKernelSetEventFlag(interrupt_flag, 1); + return -1; +} + +/*************************************************************************/ + +/** + * me_init: Initialization code for the Media Engine. Copied to + * 0xBFC00040 in shared memory space. + */ +asm("\ + .set push; .set noreorder \n\ + \n\ + .globl me_init \n\ + .type me_init, @function \n\ +me_init: \n\ + \n\ + # Set up hardware registers. \n\ + li $k0, 0xBC100000 \n\ + li $v0, 7 \n\ + sw $v0, 0x50($k0) # Enable bus clock \n\ + li $v0, -1 \n\ + sw $v0, 0x04($k0) # Clear pending interrupts \n\ + li $v0, 2 # Assume 64MB for now (watch your pointers!) \n\ + sw $v0, 0x40($k0) # Set memory size \n\ + \n\ + # Clear the caches \n\ + mtc0 $zero, $28 # TagLo \n\ + mtc0 $zero, $29 # TagHi \n\ + mfc0 $v1, $16 # Config \n\ + ext $v0, $v1, 9, 3 # Instruction cache size \n\ + li $a0, 2048 \n\ + sllv $a0, $a0, $v0 \n\ + ext $v0, $v1, 6, 3 # Data cache size \n\ + li $a1, 2048 \n\ + sllv $a1, $a1, $v0 \n\ +1: addiu $a0, $a0, -64 \n\ + bnez $a0, 1b \n\ + cache 0x01, 0($a0) # Shouldn't this be 0x00? \n\ +1: addiu $a1, $a1, -64 \n\ + bnez $a1, 1b \n\ + cache 0x11, 0($a1) \n\ + \n\ + # Enable the FPU (COP1) and clear the interrupt-pending flag. \n\ + li $v0, 0x20000000 \n\ + mtc0 $v0, $12 # Status \n\ + mtc0 $zero, $13 # Cause \n\ + \n\ + # Wait for the hardware to become ready. \n\ + li $k0, 0xBCC00000 \n\ + li $v0, 1 \n\ + sw $v0, 0x10($k0) \n\ +1: lw $v0, 0x10($k0) \n\ + andi $v0, $v0, 1 \n\ + bnez $v0, 1b \n\ + nop \n\ + \n\ + # Set up more hardware registers. \n\ + li $v0, 1 \n\ + sw $v0, 0x70($k0) \n\ + li $v0, 8 \n\ + sw $v0, 0x30($k0) \n\ + li $v0, 2 # Assume 64MB for now (watch your pointers!) \n\ + sw $v0, 0x40($k0) \n\ + sync \n\ + \n\ + # Start the internal clock counter running, in case someone \n\ + # wants to access it. (No Compare interrupts will be generated \n\ + # by default, since Status.IM[7] is cleared above.) \n\ + mtc0 $zero, $9 # Count \n\ + \n\ + # Set the exception handler address. \n\ + lui $v0, %hi(me_exception) \n\ + addiu $v0, %lo(me_exception) \n\ + mtc0 $v0, $25 \n\ + \n\ + # Initialize the stack pointer and call the main loop. \n\ + li $sp, 0x80200000 \n\ + lui $ra, %hi(me_loop) \n\ + addiu $ra, %lo(me_loop) \n\ + jr $ra \n\ + nop \n\ + \n\ + .globl me_init_end \n\ +me_init_end: \n\ + \n\ + .set pop \n\ +"); + +/*-----------------------------------------------------------------------*/ + +/** + * me_loop: Media Engine main loop. Infinitely repeats the cycle of + * waiting for an execute request from the main CPU, then executing the + * requested function. + * + * [Parameters] + * None + * [Return value] + * Does not return + * [Notes] + * This is not declared "static" because the address must be referenced + * by the assembly code in me_init(). + */ +__attribute__((section(".text.me"), noreturn)) void me_loop(void) +{ + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + asm("move $k0, %0" : : "r" (ME_K0_MAGIC)); + + for (;;) { + message_block->idle = 1; + while (!message_block->request) { /*spin*/ } + message_block->idle = 0; + int (*function)(void *) = message_block->function; + void *argument = message_block->argument; + message_block->request = 0; + message_block->result = (*function)(argument); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * me_exception: Media Engine exception handler. Stores exception data in + * the local buffer, then clears the ME stack and restarts the main loop. + */ +asm("\ + .set push; .set noreorder; .set noat \n\ + .pushsection .text.me,\"ax\",@progbits \n\ + .balign 64 \n\ + \n\ + .globl me_exception \n\ + .type me_exception, @function \n\ +me_exception: \n\ + \n\ + # Save $v0 and $v1 so we have some work registers available. \n\ + ctc0 $v0, $4 \n\ + ctc0 $v1, $5 \n\ + \n\ + # Save all registers to the exception register buffer. \n\ + lui $v1, %hi(exception_registers) \n\ + addiu $v1, %lo(exception_registers) \n\ + cache 0x18, 0($v1) \n\ + cache 0x18, 64($v1) \n\ + cache 0x18, 128($v1) \n\ + sw $zero, 0($v1) \n\ + sw $at, 4($v1) \n\ + sw $v0, 8($v1) \n\ + cfc0 $v0, $5 \n\ + sw $v0, 12($v1) \n\ + sw $a0, 16($v1) \n\ + sw $a1, 20($v1) \n\ + sw $a2, 24($v1) \n\ + sw $a3, 28($v1) \n\ + sw $t0, 32($v1) \n\ + sw $t1, 36($v1) \n\ + sw $t2, 40($v1) \n\ + sw $t3, 44($v1) \n\ + sw $t4, 48($v1) \n\ + sw $t5, 52($v1) \n\ + sw $t6, 56($v1) \n\ + sw $t7, 60($v1) \n\ + sw $s0, 64($v1) \n\ + sw $s1, 68($v1) \n\ + sw $s2, 72($v1) \n\ + sw $s3, 76($v1) \n\ + sw $s4, 80($v1) \n\ + sw $s5, 84($v1) \n\ + sw $s6, 88($v1) \n\ + sw $s7, 92($v1) \n\ + sw $t8, 96($v1) \n\ + sw $t9, 100($v1) \n\ + sw $k0, 104($v1) \n\ + sw $k1, 108($v1) \n\ + sw $gp, 112($v1) \n\ + sw $sp, 116($v1) \n\ + sw $fp, 120($v1) \n\ + sw $ra, 124($v1) \n\ + mfhi $v0 \n\ + sw $v0, 128($v1) \n\ + mflo $v0 \n\ + sw $v0, 132($v1) \n\ + mfc0 $v0, $8 \n\ + sw $v0, 136($v1) \n\ + mfc0 $v0, $12 \n\ + sw $v0, 140($v1) \n\ + mfc0 $v0, $13 \n\ + sw $v0, 144($v1) \n\ + mfc0 $v0, $14 \n\ + sw $v0, 148($v1) \n\ + mfc0 $v0, $30 \n\ + sw $v0, 152($v1) \n\ + cache 0x1A, 0($v1) \n\ + cache 0x1A, 64($v1) \n\ + cache 0x1A, 128($v1) \n\ + \n\ + # Run the remainder of the handler (compiled C code). \n\ + j me_exception_finish \n\ + nop \n\ + \n\ + .popsection \n\ + .set pop \n\ +"); + +__attribute__((section(".text.me"), noreturn)) void me_exception_finish(void) +{ + volatile MEMessageBlock * const message_block = (volatile MEMessageBlock *) + ((uintptr_t)&message_block_buffer | 0xA0000000); + + message_block->exception = 1; + asm volatile("li $sp, 0x80200000"); + asm volatile("mtc0 %0, $14; " + "mtc0 %0, $30" : : "r" (me_loop)); + asm volatile("eret"); + for (;;) {} // Unreachable, but tell the compiler we don't return +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/me.exp b/yabause/src/psp/me.exp new file mode 100644 index 0000000000..326592b16d --- /dev/null +++ b/yabause/src/psp/me.exp @@ -0,0 +1,19 @@ +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC_HASH(module_start) +PSP_EXPORT_VAR_HASH(module_info) +PSP_EXPORT_END + +PSP_EXPORT_START(melib, 0, 0x4001) +PSP_EXPORT_FUNC_HASH(meStart) +PSP_EXPORT_FUNC_HASH(meStop) +PSP_EXPORT_FUNC_HASH(meCall) +PSP_EXPORT_FUNC_HASH(mePoll) +PSP_EXPORT_FUNC_HASH(meWait) +PSP_EXPORT_FUNC_HASH(meResult) +PSP_EXPORT_FUNC_HASH(meException) +PSP_EXPORT_FUNC_HASH(meExceptionGetData) +PSP_EXPORT_FUNC_HASH(meExceptionSetFatal) +PSP_EXPORT_FUNC_HASH(meInterruptPoll) +PSP_EXPORT_FUNC_HASH(meInterruptWait) +PSP_EXPORT_FUNC_HASH(meInterruptClear) +PSP_EXPORT_END diff --git a/yabause/src/psp/me.h b/yabause/src/psp/me.h new file mode 100644 index 0000000000..4194350296 --- /dev/null +++ b/yabause/src/psp/me.h @@ -0,0 +1,231 @@ +/* src/psp/me.h: PSP Media Engine access library header + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ME_H +#define ME_H + +/*************************************************************************/ + +/* Error codes specific to Media Engine routines */ + +enum { + /* Media Engine has not been successfully started with meStart() */ + ME_ERROR_NOT_STARTED = 0x90000001, + /* Media Engine is currently executing a function */ + ME_ERROR_BUSY = 0x90000002, + /* No Media Engine interrupt is pending */ + ME_ERROR_NO_INTERRUPT = 0x90000003, +}; + +/*----------------------------------*/ + +/* Magic value stored in $k0 to indicate that code is running on the ME */ + +#define ME_K0_MAGIC 0x3E3E3E3E + +/*************************************************************************/ + +/** + * meStart: Start up the Media Engine. This function must be called + * before any other Media Engine operations are performed. + * + * [Parameters] + * None + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +extern int meStart(void); + +/** + * meStop: Stop the Media Engine. No other Media Engine functions except + * meStart() may be called after this function returns. + * + * If code is currently executing on the Media Engine when this function is + * called, the effect on system state is undefined. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meStop(void); + +/*----------------------------------*/ + +/** + * meCall: Begin executing the given function on the Media Engine. The + * function must not call any firmware functions, whether directly or + * indirectly. This routine may only be called when the Media Engine is + * idle (i.e., when mePoll() returns zero). + * + * [Parameters] + * function: Function pointer + * argument: Optional argument to function + * [Return value] + * Zero on success, otherwise an error code (negative) + */ +extern int meCall(int (*function)(void *), void *argument); + +/** + * mePoll: Check whether the Media Engine is idle. + * + * [Parameters] + * None + * [Return value] + * Zero if the Media Engine is idle, otherwise an error code (negative) + */ +extern int mePoll(void); + +/** + * meWait: Wait for the Media Engine to become idle. + * + * [Parameters] + * None + * [Return value] + * Zero if the Media Engine has become (or was already) idle, + * otherwise an error code (negative) + */ +extern int meWait(void); + +/** + * meResult: Return the result (return value) of the most recently + * executed function. The result is undefined if the Media Engine has not + * been started or is not idle, if the most recently executed function did + * not return a result (i.e. had a return type of void), or if the most + * recently executed function triggered an exception. + * + * [Parameters] + * None + * [Return value] + * Result of the most recently executed function + */ +extern int meResult(void); + +/*----------------------------------*/ + +/** + * meException: Return whether the most recently executed function + * triggered an exception. The result is undefined if the Media Engine has + * not been started or is not idle. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the most recently executed function triggered an + * exception, else zero + */ +extern int meException(void); + +/** + * meExceptionGetData: Retrieve CPU status register values related to the + * most recent exception. The values retrieved are undefined in any case + * where meException() returns zero or has an undefined return value. + * + * NULL can be passed for any unneeded register values. + * + * [Parameters] + * BadVAddr_ret: Pointer to variable to receive BadVAddr register value + * Status_ret: Pointer to variable to receive Status register value + * Cause_ret: Pointer to variable to receive Cause register value + * EPC_ret: Pointer to variable to receive EPC register value + * ErrorEPC_ret: Pointer to variable to receive ErrorEPC register value + * [Return value] + * None + */ +extern void meExceptionGetData(uint32_t *BadVAddr_ret, uint32_t *Status_ret, + uint32_t *Cause_ret, uint32_t *EPC_ret, + uint32_t *ErrorEPC_ret); + +/** + * meExceptionSetFatal: Set whether an exception on the Media Engine + * should automatically trigger an exception on the main CPU. If enabled, + * any call to mePoll() or meWait() when an exception is pending will cause + * an address error exception to be generated on the main CPU, with + * exception status information stored in a buffer pointed to by $gp: + * 0($gp) = Status + * 4($gp) = Cause + * 8($gp)= EPC or ErrorEPC (depending on the exception type) + * 12($gp) = BadVAddr + * 16($gp) = Media Engine's $sp + * 20($gp) = Media Engine's $gp + * All general-purpose registers other than $sp and $gp, as well as $hi and + * $lo, are copied from the Media Engine. + * + * Unlike other functions in this library, this function can be called even + * when the ME is not running, and it will always succeed. + * + * By default, exceptions are not fatal. + * + * [Parameters] + * fatal: Nonzero to make exceptions fatal, zero to make them nonfatal + * [Return value] + * None + */ +extern void meExceptionSetFatal(int fatal); + +/*----------------------------------*/ + +/** + * meInterruptPoll: Return whether an interrupt from the Media Engine is + * pending. Any pending interrupt is _not_ cleared. + * + * [Parameters] + * None + * [Return value] + * Zero if an interrupt from the Media Engine is pending, otherwise an + * error code (negative) + */ +extern int meInterruptPoll(void); + +/** + * meInterruptWait: Wait for an interrupt from the Media Engine if none is + * already pending, then clear the interrupt and return. + * + * [Parameters] + * None + * [Return value] + * Zero if an interrupt was received from the Media Engine, otherwise + * an error code (negative) + */ +extern int meInterruptWait(void); + +/** + * meInterruptClear: Clear any pending Media Engine interrupt. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void meInterruptClear(void); + +/*************************************************************************/ + +#endif // ME_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/menu.c b/yabause/src/psp/menu.c new file mode 100644 index 0000000000..8bc7d5f2d7 --- /dev/null +++ b/yabause/src/psp/menu.c @@ -0,0 +1,2527 @@ +/* src/psp/menu.c: PSP menu interface + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../cs2.h" +#include "../memory.h" +#include "../peripheral.h" +#include "../scsp.h" +#include "../sh2core.h" +#include "../sh2int.h" +#include "../vdp1.h" +#include "../vidsoft.h" +#include "../yabause.h" + +#include "config.h" +#include "control.h" +#include "display.h" +#include "filesel.h" +#include "font.h" +#include "gu.h" +#include "menu.h" +#include "misc.h" +#include "osk.h" +#include "psp-sh2.h" +#include "psp-video.h" +#include "sh2.h" +#include "sys.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Button input state from the last frame */ +static uint32_t last_buttons; + +/* Repeat delay, rate, and status */ +#define REPEAT_DELAY 30 // frames +#define REPEAT_RATE 3 // frames +static uint32_t repeat_buttons; // Button(s) to repeat +static uint8_t repeating; // Nonzero = repeat started +static uint8_t repeat_timer; // Counts down from REPEAT_{DELAY,RATE} + +/*----------------------------------*/ + +/* FIXME: This menu stuff is all messy and copy-pastey because I'm too + lazy to do it properly at the moment. I hate writing UIs. */ + +/* Currently selected menu and menu option */ + +typedef enum MenuIndex_ { + MENU_MAIN = 0, + MENU_GENERAL, + MENU_FILES, + MENU_BUTTON, + MENU_VIDEO, + MENU_RENDER, + MENU_FRAME_SKIP, + MENU_ADVANCED, + MENU_OPTIMIZE, + MENU_MEDIA_ENGINE, + MENU_YESNO, // A yes/no dialog implemented as a "menu" +} MenuIndex; + +static uint8_t cur_menu; + + +typedef enum MainMenuOption_ { + OPT_MAIN_GENERAL = 0, + OPT_MAIN_BUTTON, + OPT_MAIN_VIDEO, + OPT_MAIN_ADVANCED, + OPT_MAIN_SAVE, + OPT_MAIN_RESET, +} MainMenuOption; +#define OPT_MAIN__MAX OPT_MAIN_RESET + +typedef enum GeneralMenuOption_ { + OPT_GENERAL_START_IN_EMU = 0, + OPT_GENERAL_FILES, + OPT_GENERAL_BUP_AUTOSAVE, + OPT_GENERAL_BUP_SAVE_NOW, + OPT_GENERAL_BUP_SAVE_AS, +} GeneralMenuOption; +#define OPT_GENERAL__MAX OPT_GENERAL_BUP_SAVE_AS + +typedef enum FilesMenuOption_ { + OPT_FILES_PATH_BIOS = 0, + OPT_FILES_PATH_CD, + OPT_FILES_PATH_BUP, +} FilesMenuOption; +#define OPT_FILES__MAX OPT_FILES_PATH_BUP + +typedef enum ButtonMenuOption_ { + OPT_BUTTON_A, + OPT_BUTTON_B, + OPT_BUTTON_C, + OPT_BUTTON_X, + OPT_BUTTON_Y, + OPT_BUTTON_Z, +} ButtonMenuOption; +#define OPT_BUTTON__MAX OPT_BUTTON_Z + +typedef enum VideoMenuOption_ { + OPT_VIDEO_HW = 0, + OPT_VIDEO_SW, + OPT_VIDEO_RENDER, + OPT_VIDEO_FRAME_SKIP, + OPT_VIDEO_SHOW_FPS, +} VideoMenuOption; +#define OPT_VIDEO__MAX OPT_VIDEO_SHOW_FPS + +typedef enum RenderMenuOption_ { + OPT_RENDER_CACHE_TEXTURES = 0, + OPT_RENDER_SMOOTH_TEXTURES, + OPT_RENDER_SMOOTH_HIRES, + OPT_RENDER_ENABLE_ROTATE, + OPT_RENDER_OPTIMIZE_ROTATE, +} RenderMenuOption; +#define OPT_RENDER__MAX OPT_RENDER_OPTIMIZE_ROTATE + +typedef enum FrameSkipMenuOption_ { + OPT_FRAME_SKIP_AUTO = 0, + OPT_FRAME_SKIP_NUM, + OPT_FRAME_SKIP_INTERLACE, + OPT_FRAME_SKIP_ROTATE, +} FrameSkipMenuOption; +#define OPT_FRAME_SKIP__MAX OPT_FRAME_SKIP_ROTATE + +typedef enum AdvancedMenuOption_ { + OPT_ADVANCED_SH2_RECOMPILER = 0, + OPT_ADVANCED_SH2_OPTIMIZE, + OPT_ADVANCED_MEDIA_ENGINE, + OPT_ADVANCED_DECILINE_MODE, + OPT_ADVANCED_AUDIO_SYNC, + OPT_ADVANCED_CLOCK_SYNC, + OPT_ADVANCED_CLOCK_FIXED_TIME, +} AdvancedMenuOption; +#define OPT_ADVANCED__MAX OPT_ADVANCED_CLOCK_FIXED_TIME + +typedef enum OptimizeMenuOption_ { + OPT_OPTIMIZE_ASSUME_SAFE_DIVISION = 0, + OPT_OPTIMIZE_FOLD_SUBROUTINES, + OPT_OPTIMIZE_BRANCH_TO_RTS, + OPT_OPTIMIZE_LOCAL_ACCESSES, + OPT_OPTIMIZE_POINTERS, + OPT_OPTIMIZE_POINTERS_MAC, + OPT_OPTIMIZE_LOCAL_POINTERS, + OPT_OPTIMIZE_STACK, +#if 0 // FIXME: out of space on the screen; this should be the least dangerous + OPT_OPTIMIZE_MAC_NOSAT, +#endif +} OptimizeMenuOption; +#define OPT_OPTIMIZE__MAX OPT_OPTIMIZE_STACK + +typedef enum MediaEngineMenuOption_ { + OPT_MEDIA_ENGINE_USE_ME = 0, + OPT_MEDIA_ENGINE_WRITEBACK_PERIOD, + OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY, +} MediaEngineMenuOption; +#define OPT_MEDIA_ENGINE__MAX OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY + +typedef enum YesNoMenuOption_ { + OPT_YESNO_YES = 0, + OPT_YESNO_NO, +} YesNoMenuOption; +#define OPT_YESNO__MAX OPT_YESNO_NO + +static uint8_t cur_option; + + +/* Maximum menu option index for current menu (OPT_*__MAX) */ +static uint8_t max_option; + +/* File selector, if a file selector is currently open */ +static FileSelector *filesel; + +/*----------------------------------*/ + +/* Previous menu and option (and max_option value) for the yes/no dialog */ +static uint8_t yesno_menu, yesno_option, yesno_maxopt; + +/* Prompt string for the yes/no dialog (may include newlines) */ +static char yesno_prompt[1000]; + +/*----------------------------------*/ + +/* Saved pathname for "Save backup RAM as..." option */ +static char *save_as_path; + +/*----------------------------------*/ + +/* Flag: Is X the confirm button? */ +static int x_is_confirm; + +/*----------------------------------*/ + + +/* Background image buffer */ +static void *bgimage; +static void *bgimage_base; // Base (unaligned) pointer for later free()ing + +/* Background color for the menu, overlaid on the current emulation screen + * (0xAABBGGRR) */ +#define MENU_BGCOLOR 0xC0332C2C + +/* Flag indicating whether we should draw the background image (cleared + * when the user requests an emulator reset, to show visually that the + * emulator has in fact been reset) */ +static uint8_t draw_bgimage; + +/* Cursor color, flashing period, and timer */ +#define CURSOR_COLOR 0x80FFECEC +#define CURSOR_PERIOD 60 // frames +static uint8_t cursor_timer; + +/* Default text color */ +#define TEXT_COLOR 0xFFFFECEC + +/* Text color for informational messages */ +#define TEXT_COLOR_INFO 0xFFFF8040 + +/* Text color for "Saved"/"Failed" responses to "Save settings" */ +#define TEXT_COLOR_OK 0xFF55FF40 // Also used for "Reset the emulator" +#define TEXT_COLOR_NG 0xFF5540FF // Also used for warning text + +/* Text color for disabled menu options */ +#define TEXT_COLOR_DISABLED 0xFF807676 + +/* Status line current text, text color, display time, and timer */ +static const char *status_text; +static uint32_t status_color; +#define STATUS_DISPTIME 300 // frames +static uint16_t status_timer; + +/*************************************************************************/ + +/* Local function declarations */ + +static void gen_menu_bg(void); + +static void do_reset(void); + +static uint32_t get_new_buttons(const uint32_t buttons); +static void process_input_menu(const uint32_t buttons, + const uint32_t new_buttons); +static void process_option_main(const uint32_t buttons); +static void process_option_general(const uint32_t buttons); +static void process_option_files(const uint32_t buttons); +static void process_option_button(const uint32_t buttons); +static void process_option_video(const uint32_t buttons); +static void process_option_render(const uint32_t buttons); +static void process_option_frame_skip(const uint32_t buttons); +static void process_option_advanced(const uint32_t buttons); +static void process_option_optimize(const uint32_t buttons); +static void process_option_media_engine(const uint32_t buttons); +static void process_option_yesno(const uint32_t buttons); +static void process_input_filesel(const uint32_t new_buttons); +static void process_osk_result(void); + +static void draw_menu(void); +static const char *cur_option_confirm_text(void); +static void draw_menu_option(int option, int x, int y, const char *format, ...) + __attribute__((format(printf,4,5))); +static void draw_disabled_menu_option(int option, int x, int y, + const char *format, ...) + __attribute__((format(printf,4,5))); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * menu_open: Open the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +void menu_open(void) +{ + last_buttons = control_state(); + repeat_buttons = 0; + + cur_menu = MENU_MAIN; + cur_option = 0; + max_option = OPT_MAIN__MAX; + filesel = NULL; + + int res = sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_X_IS_CONFIRM, + &x_is_confirm); + if (res < 0) { + DMSG("Failed to get X_IS_CONFIRM: %s", psp_strerror(res)); + x_is_confirm = 0; // Default to O + } + draw_bgimage = 1; + cursor_timer = 0; + + display_set_size(DISPLAY_WIDTH, DISPLAY_HEIGHT); + gen_menu_bg(); +} + +/*************************************************************************/ + +/** + * menu_run: Perform a single frame's processing for the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +void menu_run(void) +{ + const uint32_t buttons = control_state(); + + /* Update timers */ + cursor_timer = (cursor_timer + 1) % CURSOR_PERIOD; + if (status_timer > 0) { + status_timer--; + } + + /* Update the on-screen keyboard in case it's active */ + osk_update(); + + /* Check for and process input */ + const uint32_t new_buttons = get_new_buttons(buttons); + if (osk_status()) { + process_osk_result(); + } else { + if (filesel) { + process_input_filesel(new_buttons); + } else { + process_input_menu(buttons, new_buttons); + } + } + + /* Draw the menu (and dim the display if the OSK is active) */ + display_begin_frame(); + draw_menu(); + if (osk_status()) { + display_fill_box(0, 0, DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0xAA000000); + } + display_end_frame(); + sceDisplayWaitVblankStart(); +} + +/*************************************************************************/ + +/** + * menu_close: Close the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +void menu_close(void) +{ + /* If the on-screen keyboard is active, close it */ + + if (osk_status()) { + osk_close(); + while (osk_status()) { + osk_update(); + sceDisplayWaitVblankStart(); + } + } + + /* If there's a file selector running, kill it */ + + if (filesel) { + filesel_destroy(filesel); + filesel = NULL; + } + + /* Make sure to clear both display buffers */ + + display_begin_frame(); + if (draw_bgimage) { + guCopyImage(GU_PSM_8888, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_STRIDE, bgimage, + 0, 0, DISPLAY_STRIDE, display_work_buffer()); + } else { + display_fill_box(0, 0, DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0xFF000000); + } + display_end_frame(); + sceDisplayWaitVblankStart(); + + display_begin_frame(); + if (draw_bgimage) { + guCopyImage(GU_PSM_8888, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_STRIDE, bgimage, + 0, 0, DISPLAY_STRIDE, display_work_buffer()); + } else { + display_fill_box(0, 0, DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0xFF000000); + } + display_end_frame(); + sceDisplayWaitVblankStart(); + + /* Free the background image buffer */ + + free(bgimage_base); + bgimage = bgimage_base = NULL; + + /* Clear the status line so any current message doesn't get held over */ + + status_text = ""; + status_timer = 0; +} + +/*************************************************************************/ + +/** + * menu_set_error: Set an error message to be displayed on the menu + * screen. If message is NULL, any message currently displayed is cleared. + * + * [Parameters] + * message: Message text (NULL to clear current message) + * [Return value] + * None + */ +void menu_set_error(const char *message) +{ + static char buf[100]; // Just in case the message is in a local buffer + + if (message) { + snprintf(buf, sizeof(buf), "%s", message); + status_text = buf; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } else { + status_text = NULL; + status_timer = 0; + } +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * gen_menu_bg: Generate the background image for the menu (a copy of the + * currently-displayed image). + * + * [Parameters] + * None + * [Return value] + * None + */ +static void gen_menu_bg(void) +{ + bgimage_base = malloc((DISPLAY_STRIDE * DISPLAY_HEIGHT * 4) + 63); + if (UNLIKELY(!bgimage_base)) { + DMSG("Out of memory for bgimage"); + bgimage = NULL; + return; + } + bgimage = (void *)(((uintptr_t)bgimage_base + 63) & -64); + memcpy(bgimage, display_disp_buffer(), + DISPLAY_STRIDE * DISPLAY_HEIGHT * 4); +} + +/*************************************************************************/ + +/** + * do_reset: Reset the emulator in response to a user action. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void do_reset(void) +{ + YabauseReset(); + status_text = "Resetting the emulator."; + status_color = TEXT_COLOR_OK; + status_timer = STATUS_DISPTIME; + draw_bgimage = 0; +} + +/*************************************************************************/ + +/** + * get_new_buttons: Return which controller buttons were pressed this + * frame. This may include fake button presses generated by auto-repeat. + * + * If the on-screen keyboard is open, this function always returns zero; + * however, it should still be called every frame to ensure proper + * behavior when the OSK is closed. + * + * [Parameters] + * buttons: Buttons which are currently held down (PSP_CTRL_* bitmask) + * [Return value] + * Buttons which were newly pressed this frame (PSP_CTRL_* bitmask) + */ +static uint32_t get_new_buttons(const uint32_t buttons) +{ + uint32_t new_buttons = ~last_buttons & buttons; + + if (osk_status()) { + new_buttons = 0; + repeat_buttons = 0; + } + + if (new_buttons) { + /* Only allow repeat of up/down/left/right */ + repeat_buttons = new_buttons & (PSP_CTRL_UP | PSP_CTRL_DOWN + | PSP_CTRL_LEFT | PSP_CTRL_RIGHT); + repeating = 0; + repeat_timer = REPEAT_DELAY; + } else if ((buttons & repeat_buttons) != repeat_buttons) { + repeat_buttons = 0; + } + if (repeat_buttons != 0) { + repeat_timer--; + if (repeat_timer == 0) { + new_buttons = repeat_buttons; + repeating = 1; + repeat_timer = REPEAT_RATE; + } + } + + last_buttons = buttons; + return new_buttons; +} + +/*-----------------------------------------------------------------------*/ + +/** + * process_input_menu: Process input directed to the menu (i.e. not a file + * selector. + * + * [Parameters] + * buttons: Buttons which are currently held down (PSP_CTRL_* bitmask) + * new_buttons: Buttons which were pressed this frame (PSP_CTRL_* bitmask) + * [Return value] + * None + */ +static void process_input_menu(const uint32_t buttons, + const uint32_t new_buttons) +{ + const uint32_t confirm_button = + x_is_confirm ? PSP_CTRL_CROSS : PSP_CTRL_CIRCLE; + const uint32_t cancel_button = + x_is_confirm ? PSP_CTRL_CIRCLE : PSP_CTRL_CROSS; + + if (new_buttons & PSP_CTRL_UP) { + if (cur_menu != MENU_YESNO && cur_option > 0) { + cur_option--; + cursor_timer = 0; + } + + } else if (new_buttons & PSP_CTRL_DOWN) { + if (cur_menu != MENU_YESNO && cur_option < max_option) { + cur_option++; + cursor_timer = 0; + } + + } else if (new_buttons & (PSP_CTRL_LEFT | PSP_CTRL_RIGHT)) { + const int dir = (new_buttons & PSP_CTRL_RIGHT) ? 1 : -1; + + if (cur_menu == MENU_FRAME_SKIP + && cur_option == OPT_FRAME_SKIP_NUM + ) { + const int new_num = config_get_frameskip_num() + dir; + if (!config_set_frameskip_num(new_num)) { + status_text = "Failed to change fixed frame skip count!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + + } else if (cur_menu == MENU_MEDIA_ENGINE + && cur_option == OPT_MEDIA_ENGINE_WRITEBACK_PERIOD + ) { + if (me_available && config_get_use_me()) { + const unsigned int period = config_get_me_writeback_period(); + unsigned int new_period = (dir > 0) ? period<<1 : period>>1; + if (new_period < 1) { + new_period = 1; + } else if (new_period > 64) { // Should be more than enough + new_period = 64; + } + if (!config_set_me_writeback_period(new_period)) { + status_text = "Failed to change ME writeback frequency!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + } + + } else if (cur_menu == MENU_MEDIA_ENGINE + && cur_option == OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY + ) { + if (me_available && config_get_use_me()) { + const unsigned int value = config_get_me_uncached_boundary(); + unsigned int new_value = (dir > 0) + ? (value==0 ? 0x400 : value<<1) + : value>>1; + if (new_value < 0x400) { // Shouldn't need to deal with + new_value = 0; // fractions of a kilobyte + } else if (new_value > 0x80000) { + new_value = 0x80000; + } + if (!config_set_me_uncached_boundary(new_value)) { + status_text = "Failed to change uncached boundary address!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + } + + } else if (cur_menu == MENU_YESNO) { + if (dir < 0) { + if (cur_option == OPT_YESNO_NO) { + cur_option = OPT_YESNO_YES; + cursor_timer = 0; + } + } else { + if (cur_option == OPT_YESNO_YES) { + cur_option = OPT_YESNO_NO; + cursor_timer = 0; + } + } + } + + } else if (new_buttons != 0 && cur_menu == MENU_BUTTON) { + if (new_buttons & (PSP_CTRL_CIRCLE | PSP_CTRL_CROSS + | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)) { + process_option_button(buttons); + } else if (new_buttons & PSP_CTRL_START) { + cur_menu = MENU_MAIN; + cur_option = OPT_MAIN_BUTTON; + max_option = OPT_MAIN__MAX; + } + + } else if (new_buttons & confirm_button) { + switch ((MenuIndex)cur_menu) { + case MENU_MAIN: process_option_main(buttons); break; + case MENU_GENERAL: process_option_general(buttons); break; + case MENU_FILES: process_option_files(buttons); break; + case MENU_BUTTON: /* impossible (handled above) */ break; + case MENU_VIDEO: process_option_video(buttons); break; + case MENU_RENDER: process_option_render(buttons); break; + case MENU_FRAME_SKIP: process_option_frame_skip(buttons); break; + case MENU_ADVANCED: process_option_advanced(buttons); break; + case MENU_OPTIMIZE: process_option_optimize(buttons); break; + case MENU_MEDIA_ENGINE:process_option_media_engine(buttons); break; + case MENU_YESNO: process_option_yesno(buttons); break; + } + + } else if (new_buttons & cancel_button) { + if (cur_menu != MENU_MAIN) { + switch ((MenuIndex)cur_menu) { + case MENU_MAIN: + case MENU_BUTTON: + /* Impossible, but included to avoid a compiler warning */ + break; + case MENU_GENERAL: + cur_menu = MENU_MAIN; + cur_option = OPT_MAIN_GENERAL; + max_option = OPT_MAIN__MAX; + break; + case MENU_FILES: + cur_menu = MENU_GENERAL; + cur_option = OPT_GENERAL_FILES; + max_option = OPT_GENERAL__MAX; + break; + case MENU_VIDEO: + cur_menu = MENU_MAIN; + cur_option = OPT_MAIN_VIDEO; + max_option = OPT_MAIN__MAX; + break; + case MENU_RENDER: + cur_menu = MENU_VIDEO; + cur_option = OPT_VIDEO_RENDER; + max_option = OPT_VIDEO__MAX; + break; + case MENU_FRAME_SKIP: + cur_menu = MENU_VIDEO; + cur_option = OPT_VIDEO_FRAME_SKIP; + max_option = OPT_VIDEO__MAX; + break; + case MENU_ADVANCED: + cur_menu = MENU_MAIN; + cur_option = OPT_MAIN_ADVANCED; + max_option = OPT_MAIN__MAX; + break; + case MENU_OPTIMIZE: + cur_menu = MENU_ADVANCED; + cur_option = OPT_ADVANCED_SH2_OPTIMIZE; + max_option = OPT_ADVANCED__MAX; + break; + case MENU_MEDIA_ENGINE: + cur_menu = MENU_ADVANCED; + cur_option = OPT_ADVANCED_MEDIA_ENGINE; + max_option = OPT_ADVANCED__MAX; + break; + case MENU_YESNO: + cur_option = OPT_YESNO_NO; + process_option_yesno(confirm_button); + break; + } + } + + } +} + +/*----------------------------------*/ + +/** + * process_option_*: Process a "confirm" button press on the currently + * selected menu option. Each menu has its own handler function. + * + * [Parameters] + * buttons: Buttons which are currently held down (PSP_CTRL_* bitmask) + * [Return value] + * None + */ + +static void process_option_main(const uint32_t buttons) +{ + switch ((MainMenuOption)cur_option) { + + case OPT_MAIN_GENERAL: + cur_menu = MENU_GENERAL; + cur_option = 0; + max_option = OPT_GENERAL__MAX; + break; + + case OPT_MAIN_BUTTON: + cur_menu = MENU_BUTTON; + cur_option = 0; + max_option = OPT_BUTTON__MAX; + break; + + case OPT_MAIN_VIDEO: + cur_menu = MENU_VIDEO; + cur_option = 0; + max_option = OPT_VIDEO__MAX; + break; + + case OPT_MAIN_ADVANCED: + cur_menu = MENU_ADVANCED; + cur_option = 0; + max_option = OPT_ADVANCED__MAX; + break; + + case OPT_MAIN_SAVE: + if (config_save()) { + status_text = "Settings saved."; + status_color = TEXT_COLOR_OK; + } else { + status_text = "Failed to save settings!"; + status_color = TEXT_COLOR_NG; + } + status_timer = STATUS_DISPTIME; + break; + + case OPT_MAIN_RESET: + if (yabause_initted + && (buttons & PSP_CTRL_LTRIGGER) + && (buttons & PSP_CTRL_RTRIGGER) + ) { + do_reset(); + } + + } +} + +/*----------------------------------*/ + +static void process_option_general(const uint32_t buttons) +{ + switch ((GeneralMenuOption)cur_option) { + + case OPT_GENERAL_START_IN_EMU: + if (!config_set_start_in_emu(!config_get_start_in_emu())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_GENERAL_FILES: + cur_menu = MENU_FILES; + cur_option = 0; + max_option = OPT_FILES__MAX; + break; + + case OPT_GENERAL_BUP_AUTOSAVE: + if (!config_set_bup_autosave(!config_get_bup_autosave())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_GENERAL_BUP_SAVE_NOW: { + if (!yabause_initted) { + break; + } + const char *path = config_get_path_bup(); + if (!path || !*path) { // Check this early just in case + status_text = "No backup RAM file configured!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } else if (!save_backup_ram()) { + status_text = "Error saving backup RAM!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } else { + status_text = "Backup RAM saved."; + status_color = TEXT_COLOR_OK; + status_timer = STATUS_DISPTIME; + } + break; + } // case OPT_GENERAL_BUP_SAVE_NOW + + case OPT_GENERAL_BUP_SAVE_AS: { + if (!yabause_initted) { + break; + } + const unsigned int maxlen = 100; // Reasonable limit + const char *path = config_get_path_bup(); + if (!path) { + path = "backup.bin"; // Just in case + } + if (!osk_open("Enter new backup RAM filename.", path, maxlen)) { + status_text = "Unable to open on-screen keyboard!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + break; + } + /* process_osk_result() will take care of handling the result from + * the OSK and saving the new file. */ + break; + } // case OPT_GENERAL_BUP_SAVE_AS + + } +} + +/*----------------------------------*/ + +static void process_option_files(const uint32_t buttons) +{ + switch ((FilesMenuOption)cur_option) { + + case OPT_FILES_PATH_BIOS: + filesel = filesel_create("Select BIOS image file", progpath); + if (!filesel) { + status_text = "Failed to open file selector!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_FILES_PATH_CD: + filesel = filesel_create("Select CD image file (ISO or CUE)", + progpath); + if (!filesel) { + status_text = "Failed to open file selector!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_FILES_PATH_BUP: + filesel = filesel_create("Select backup RAM image file", + progpath); + if (!filesel) { + status_text = "Failed to open file selector!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + } +} + +/*----------------------------------*/ + +static void process_option_button(const uint32_t buttons) +{ + static const uint8_t perkey_map[] = { + [CONFIG_BUTTON_A] = PERPAD_A, + [CONFIG_BUTTON_B] = PERPAD_B, + [CONFIG_BUTTON_C] = PERPAD_C, + [CONFIG_BUTTON_X] = PERPAD_X, + [CONFIG_BUTTON_Y] = PERPAD_Y, + [CONFIG_BUTTON_Z] = PERPAD_Z, + }; + + if (buttons & (buttons-1)) { + status_text = "Press only one button at a time."; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + return; + } + + const unsigned int saturn_button = + CONFIG_BUTTON_A + (cur_option - OPT_BUTTON_A); + const uint32_t psp_button = buttons; + + /* If this button is currently assigned to something else, swap the + * buttons around. */ + unsigned int other_button; + for (other_button = CONFIG_BUTTON_A; other_button <= CONFIG_BUTTON_Z; + other_button++ + ) { + if (config_get_button(other_button) == psp_button) { + break; + } + } + if (other_button <= CONFIG_BUTTON_Z) { + if (!config_set_button(other_button,config_get_button(saturn_button))){ + status_text = "Failed to reassign button!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + return; + } + PerSetKey(config_get_button(other_button), perkey_map[other_button], + padbits); + } + + if (!config_set_button(saturn_button, psp_button)) { + status_text = "Failed to assign button!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + return; + } + PerSetKey(psp_button, perkey_map[saturn_button], padbits); +} + +/*----------------------------------*/ + +static void process_option_video(const uint32_t buttons) +{ + switch ((VideoMenuOption)cur_option) { + + case OPT_VIDEO_HW: + if (config_get_module_video() != VIDCORE_PSP) { + if (VideoChangeCore(VIDCORE_PSP) < 0 + || !config_set_module_video(VIDCORE_PSP) + ) { + status_text = "Failed to change video interface!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + } + break; + + case OPT_VIDEO_SW: + if (config_get_module_video() != VIDCORE_SOFT) { + if (VideoChangeCore(VIDCORE_SOFT) < 0 + || !config_set_module_video(VIDCORE_SOFT) + ) { + status_text = "Failed to change video interface!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + } + break; + + case OPT_VIDEO_RENDER: + cur_menu = MENU_RENDER; + cur_option = 0; + max_option = OPT_RENDER__MAX; + break; + + case OPT_VIDEO_FRAME_SKIP: + cur_menu = MENU_FRAME_SKIP; + cur_option = 0; + max_option = OPT_FRAME_SKIP__MAX; + break; + + case OPT_VIDEO_SHOW_FPS: + if (!config_set_show_fps(!config_get_show_fps())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + } +} + +/*----------------------------------*/ + +static void process_option_render(const uint32_t buttons) +{ + switch ((RenderMenuOption)cur_option) { + + case OPT_RENDER_CACHE_TEXTURES: + if (!config_set_cache_textures(!config_get_cache_textures())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_RENDER_SMOOTH_TEXTURES: + if (!config_set_smooth_textures(!config_get_smooth_textures())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_RENDER_SMOOTH_HIRES: + if (!config_set_smooth_hires(!config_get_smooth_hires())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_RENDER_ENABLE_ROTATE: + if (!config_set_enable_rotate(!config_get_enable_rotate())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_RENDER_OPTIMIZE_ROTATE: + if (!config_set_optimize_rotate(!config_get_optimize_rotate())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + } +} + +/*----------------------------------*/ + +static void process_option_frame_skip(const uint32_t buttons) +{ + switch ((FrameSkipMenuOption)cur_option) { + + case OPT_FRAME_SKIP_AUTO: + if (!config_set_frameskip_auto(!config_get_frameskip_auto())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_FRAME_SKIP_NUM: + /* Don't do anything for the confirm button */ + break; + + case OPT_FRAME_SKIP_INTERLACE: + if (!config_set_frameskip_interlace(!config_get_frameskip_interlace())){ + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_FRAME_SKIP_ROTATE: + if (!config_set_frameskip_rotate(!config_get_frameskip_rotate())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + } +} + +/*----------------------------------*/ + +static void process_option_advanced(const uint32_t buttons) +{ + switch ((AdvancedMenuOption)cur_option) { + + case OPT_ADVANCED_SH2_RECOMPILER: + if ((buttons & PSP_CTRL_LTRIGGER) && (buttons & PSP_CTRL_RTRIGGER)) { + int new_module; + if (config_get_module_sh2() == SH2CORE_PSP) { + new_module = SH2CORE_INTERPRETER; + } else { + new_module = SH2CORE_PSP; + } + if (!config_set_module_sh2(new_module)) { + status_text = "Failed to change SH-2 core!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + if (yabause_initted) { + SH2DeInit(); + SH2Init(new_module); + do_reset(); + } + } + break; + + case OPT_ADVANCED_SH2_OPTIMIZE: + cur_menu = MENU_OPTIMIZE; + cur_option = 0; + max_option = OPT_OPTIMIZE__MAX; + break; + + case OPT_ADVANCED_MEDIA_ENGINE: + if (me_available) { + cur_menu = MENU_MEDIA_ENGINE; + cur_option = 0; + max_option = OPT_MEDIA_ENGINE__MAX; + } + break; + + case OPT_ADVANCED_DECILINE_MODE: + if (!config_set_deciline_mode(!config_get_deciline_mode())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + YabauseSetDecilineMode(config_get_deciline_mode()); + break; + + case OPT_ADVANCED_AUDIO_SYNC: + if (!config_set_audio_sync(!config_get_audio_sync())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + ScspSetFrameAccurate(config_get_audio_sync()); + break; + + case OPT_ADVANCED_CLOCK_SYNC: + if (!config_set_clock_sync(!config_get_clock_sync())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + case OPT_ADVANCED_CLOCK_FIXED_TIME: + if (!config_set_clock_fixed_time(!config_get_clock_fixed_time())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + break; + + } +} + +/*----------------------------------*/ + +static void process_option_optimize(const uint32_t buttons) +{ + uint32_t optflags = config_get_sh2_optimizations(); + + switch ((OptimizeMenuOption)cur_option) { + case OPT_OPTIMIZE_ASSUME_SAFE_DIVISION: + optflags ^= SH2_OPTIMIZE_ASSUME_SAFE_DIVISION; + break; + case OPT_OPTIMIZE_FOLD_SUBROUTINES: + optflags ^= SH2_OPTIMIZE_FOLD_SUBROUTINES; + break; + case OPT_OPTIMIZE_BRANCH_TO_RTS: + optflags ^= SH2_OPTIMIZE_BRANCH_TO_RTS; + break; + case OPT_OPTIMIZE_LOCAL_ACCESSES: + optflags ^= SH2_OPTIMIZE_LOCAL_ACCESSES; + break; + case OPT_OPTIMIZE_POINTERS: + optflags ^= SH2_OPTIMIZE_POINTERS; + break; + case OPT_OPTIMIZE_POINTERS_MAC: + optflags ^= SH2_OPTIMIZE_POINTERS_MAC; + break; + case OPT_OPTIMIZE_LOCAL_POINTERS: + optflags ^= SH2_OPTIMIZE_LOCAL_POINTERS; + break; + case OPT_OPTIMIZE_STACK: + optflags ^= SH2_OPTIMIZE_STACK; + break; +#if 0 // FIXME: out of space on the screen + case OPT_OPTIMIZE_MAC_NOSAT: + optflags ^= SH2_OPTIMIZE_MAC_NOSAT; + break; +#endif + } + + if (!config_set_sh2_optimizations(optflags)) { + status_text = "Failed to set optimization flags!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + return; + } + sh2_set_optimizations(optflags); +} + +/*----------------------------------*/ + +static void process_option_media_engine(const uint32_t buttons) +{ + switch ((MediaEngineMenuOption)cur_option) { + case OPT_MEDIA_ENGINE_USE_ME: + if (me_available) { + if (!config_set_use_me(!config_get_use_me())) { + status_text = "Failed to change option!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } + } + break; + + case OPT_MEDIA_ENGINE_WRITEBACK_PERIOD: + case OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY: + /* Don't do anything for the confirm button */ + break; + } +} + +/*----------------------------------*/ + +static void process_option_yesno(const uint32_t buttons) +{ + if (yesno_menu == MENU_GENERAL && yesno_option == OPT_GENERAL_BUP_SAVE_AS) { + if (cur_option == OPT_YESNO_YES) { + if (!config_set_path_bup(save_as_path)) { + status_text = "Failed to store new backup RAM filename!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } else if (!save_backup_ram()) { + status_text = "Error saving backup RAM!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + } else { + static char buf[50]; + snprintf(buf, sizeof(buf), "Backup RAM saved to: %.25s%s", + save_as_path, strlen(save_as_path) > 25 ? "..." : ""); + status_text = buf; + status_color = TEXT_COLOR_OK; + status_timer = STATUS_DISPTIME; + } + } + free(save_as_path); + + } else { + DMSG("Invalid previous menu/option: %u/%u", yesno_menu, yesno_option); + } + + cur_menu = yesno_menu; + cur_option = yesno_option; + max_option = yesno_maxopt; +} + +/*-----------------------------------------------------------------------*/ + +/** + * process_input_filesel: Process input directed to a file selector. + * + * [Parameters] + * new_buttons: Buttons which were pressed this frame (PSP_CTRL_* bitmask) + * [Return value] + * None + */ +static void process_input_filesel(const uint32_t new_buttons) +{ + filesel_process(filesel, new_buttons); + + if (filesel_done(filesel)) { + + const char *filename = filesel_selected_file(filesel); + + if (filename) { + + /* We only need to reset if the emulator has already been + * started */ + int need_reset = yabause_initted; + + /* We can only come here from the "Files" menu, so there's no + * need to check cur_menu */ + + switch ((FilesMenuOption)cur_option) { + + case OPT_FILES_PATH_BIOS: + if (strcmp(config_get_path_bios(), filename) == 0) { + need_reset = 0; + } + if (!config_set_path_bios(filename)) { + status_text = "Failed to set BIOS image filename!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + need_reset = 0; + } + if (yabause_initted) { + if (LoadBios(filename) != 0) { + status_text = "Failed to load BIOS image!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + need_reset = 0; + } + } + break; + + case OPT_FILES_PATH_CD: + if (strcmp(config_get_path_cd(), filename) == 0) { + need_reset = 0; + } + if (!config_set_path_cd(filename)) { + status_text = "Failed to set CD image filename!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + need_reset = 0; + } + if (yabause_initted) { + /* Unfortunately, Cs2ChangeCDCore() doesn't return an error + * if the load fails, so we'll just hope it worked. */ + Cs2ChangeCDCore(CDCORE_ISO, filename); + } + break; + + case OPT_FILES_PATH_BUP: + if (strcmp(config_get_path_bup(), filename) == 0) { + need_reset = 0; + } + if (!config_set_path_bup(filename)) { + status_text = + "Failed to set backup RAM image filename!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + need_reset = 0; + } + if (yabause_initted) { + if (LoadBackupRam(filename) != 0) { + status_text = "Failed to load backup RAM image!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + need_reset = 0; + } + } + break; + + default: // impossible + break; + + } + + if (need_reset) { + do_reset(); + } + + } // if (filename) + + filesel_destroy(filesel); + filesel = NULL; + + } // if (filesel_done(filesel)) +} + +/*-----------------------------------------------------------------------*/ + +/** + * process_osk_result: Process the result of input to the on-screen + * keyboard. Assumes the OSK is currently active. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void process_osk_result(void) +{ + PRECOND(osk_status(), return); + + /* The OSK is currently only used for OPT_GENERAL_BUP_SAVE_AS, so we + * don't need to check the current menu or option index. */ + + switch (osk_result()) { + case OSK_RESULT_NONE: + /* We've already requested a close, so nothing else to do. */ + break; + + case OSK_RESULT_ERROR: + status_text = "An error occurred during on-screen keyboard input!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + osk_close(); + break; + + case OSK_RESULT_RUNNING: + break; + + case OSK_RESULT_CANCELLED: + osk_close(); + break; + + case OSK_RESULT_UNCHANGED: + case OSK_RESULT_CHANGED: { + char *path = osk_get_text(); + osk_close(); + if (!path) { + status_text = "An error occurred during on-screen keyboard input!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + break; + } else if (!*path) { + status_text = "No filename was entered!"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + free(path); + break; + } else if (path[strcspn(path,":/\\")]) { + status_text = "These characters cannot be used in a filename: / \\ :"; + status_color = TEXT_COLOR_NG; + status_timer = STATUS_DISPTIME; + free(path); + break; + } + + /* Confirm the action with the user. Default to "Yes" if the file + * does not exist, "No" if it already exists. */ + save_as_path = path; + yesno_menu = cur_menu; + yesno_option = cur_option; + yesno_maxopt = max_option; + cur_menu = MENU_YESNO; + max_option = OPT_YESNO__MAX; + FILE *test = fopen(path, "r"); + if (test) { + fclose(test); + cur_option = OPT_YESNO_NO; + snprintf(yesno_prompt, sizeof(yesno_prompt), + "The file \"%.25s%s\" already exists!\n" + "Do you want to overwrite this file?\n", + path, strlen(path) > 25 ? "..." : ""); + } else { + cur_option = OPT_YESNO_YES; + snprintf(yesno_prompt, sizeof(yesno_prompt), + "Save backup RAM to the file%s\"%.50s%s\"?\n", + strlen(path) < 20 ? " " : "\n", + path, strlen(path) > 50 ? "..." : ""); + } + break; + } + } +} + +/*************************************************************************/ + +/** + * draw_menu: Draw the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void draw_menu(void) +{ + /* Set up basic settings */ + + guDisable(GU_TEXTURE_2D); + guEnable(GU_BLEND); + guBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + + /* Draw the background image, if appropriate, and overlay color */ + + if (bgimage && draw_bgimage) { + guCopyImage(GU_PSM_8888, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_STRIDE, bgimage, + 0, 0, DISPLAY_STRIDE, display_work_buffer()); + } + display_fill_box(0, 0, DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, MENU_BGCOLOR); + + /* Draw the upper and lower info bars */ + + font_printf(DISPLAY_WIDTH/2, 5, 0, TEXT_COLOR_INFO, + "Yabause %s", PACKAGE_VERSION); + display_fill_box(0, 21, DISPLAY_WIDTH - 1, 21, TEXT_COLOR_INFO); + display_fill_box(0, 22, DISPLAY_WIDTH - 1, 22, 0xFF000000); + + const char *confirm_text = + (filesel != NULL) + ? "U/D/L/R: Move cursor O: Select file X: Previous menu" + : cur_option_confirm_text(); + if (x_is_confirm) { + static char swapbuf[100]; + unsigned int i; + for (i = 0; i < sizeof(swapbuf)-1 && confirm_text[i]; i++) { + if (confirm_text[i] == 'O' && confirm_text[i+1] == ':') { + swapbuf[i] = 'X'; + } else if (confirm_text[i] == 'X' && confirm_text[i+1] == ':') { + swapbuf[i] = 'O'; + } else { + swapbuf[i] = confirm_text[i]; + } + } + swapbuf[i] = 0; + confirm_text = swapbuf; + } + font_printf(DISPLAY_WIDTH/2, DISPLAY_HEIGHT - 16, 0, TEXT_COLOR_INFO, + "Select: Exit menu %s", confirm_text); + display_fill_box(0, DISPLAY_HEIGHT - 22, + DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 22, TEXT_COLOR_INFO); + display_fill_box(0, DISPLAY_HEIGHT - 21, + DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 21, 0xFF000000); + if (status_timer > 0) { + const float alpha = status_timer >= STATUS_DISPTIME/2 ? 1.0f : + (status_timer / (float)(STATUS_DISPTIME/2)); + const uint32_t alpha_byte = + floorf((status_color>>24 & 0xFF) * alpha + 0.5f); + font_printf(DISPLAY_WIDTH/2, DISPLAY_HEIGHT - 27 - FONT_HEIGHT, 0, + alpha_byte<<24 | (status_color & 0x00FFFFFF), + "%s", status_text); + } + + /* Draw the menu options and cursor */ + + const int menu_left_edge = 130; + const int line_height = FONT_HEIGHT*5/4; + const int menu_title_y = (DISPLAY_HEIGHT/2 - line_height/2) + - (7*line_height + FONT_HEIGHT) / 2 - 2*line_height; + const int menu_help_y = (DISPLAY_HEIGHT/2 - line_height/2) + - (7*line_height + FONT_HEIGHT) / 2 + 7*line_height; + const int menu_center_y = (menu_title_y + menu_help_y) / 2 + FONT_HEIGHT/2; + int x, y; + + switch ((MenuIndex)cur_menu) { + + case MENU_MAIN: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Yabause Main Menu"); + y = menu_center_y - (5*line_height + line_height/2 + FONT_HEIGHT) / 2; + draw_menu_option(OPT_MAIN_GENERAL, menu_left_edge, y, + "Configure general options..."); + y += line_height; + draw_menu_option(OPT_MAIN_BUTTON, menu_left_edge, y, + "Configure controller buttons..."); + y += line_height; + draw_menu_option(OPT_MAIN_VIDEO, menu_left_edge, y, + "Configure video options..."); + y += line_height; + draw_menu_option(OPT_MAIN_ADVANCED, menu_left_edge, y, + "Configure advanced settings..."); + y += line_height*3/2; + draw_menu_option(OPT_MAIN_SAVE, menu_left_edge, y, "Save settings"); + y += line_height; + if (yabause_initted) { + draw_menu_option(OPT_MAIN_RESET, menu_left_edge, y, + "Reset emulator"); + } else { + draw_disabled_menu_option(OPT_MAIN_RESET, menu_left_edge, y, + "Reset emulator"); + } + y = menu_help_y; + switch ((MainMenuOption)cur_option) { + case OPT_MAIN_GENERAL: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Select data files" + " for use with the emulator, or change"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "other general" + " settings."); + y += line_height; + break; + case OPT_MAIN_BUTTON: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Choose which PSP" + " controls to use for the Saturn"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "controller buttons."); + y += line_height; + break; + case OPT_MAIN_VIDEO: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Choose between" + " hardware and software video rendering,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "or configure display" + " settings."); + y += line_height; + break; + case OPT_MAIN_ADVANCED: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Configure advanced" + " emulation options."); + y += line_height; + break; + case OPT_MAIN_SAVE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Save the current" + " settings, so Yabause will use them"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "automatically the" + " next time you start it up."); + y += line_height; + break; + case OPT_MAIN_RESET: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Reset the emulator," + " as though you had pressed the"); + y += line_height; + x = font_printf(75, y, -1, TEXT_COLOR_INFO, "Saturn's RESET" + " button. "); + font_printf(x, y, -1, TEXT_COLOR_NG, "Hold the L and R buttons"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "while selecting this" + " option."); + break; + } + break; + + case MENU_GENERAL: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure general options"); + y = menu_center_y - (5*line_height + FONT_HEIGHT) / 2; + draw_menu_option(OPT_GENERAL_START_IN_EMU, menu_left_edge, y, + "[%c] Start emulator immediately", + config_get_start_in_emu() ? '*' : ' '); + y += line_height*3/2; + draw_menu_option(OPT_GENERAL_FILES, menu_left_edge, y, + " Select BIOS/CD/backup files..."); + y += line_height*3/2; + draw_menu_option(OPT_GENERAL_BUP_AUTOSAVE, menu_left_edge, y, + "[%c] Auto-save backup RAM", + config_get_bup_autosave() ? '*' : ' '); + y += line_height; + if (yabause_initted) { + draw_menu_option(OPT_GENERAL_BUP_SAVE_NOW, menu_left_edge, y, + " Save backup RAM now"); + } else { + draw_disabled_menu_option(OPT_GENERAL_BUP_SAVE_NOW, menu_left_edge, + y, " Save backup RAM now"); + } + y += line_height; + if (yabause_initted) { + draw_menu_option(OPT_GENERAL_BUP_SAVE_AS, menu_left_edge, y, + " Save backup RAM as..."); + } else { + draw_disabled_menu_option(OPT_GENERAL_BUP_SAVE_AS, menu_left_edge, + y, " Save backup RAM as..."); + } + y = menu_help_y; + switch ((GeneralMenuOption)cur_option) { + case OPT_GENERAL_START_IN_EMU: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Start the Saturn" + " emulation immediately when you start"); + y += line_height; + x = font_printf(75, y, -1, TEXT_COLOR_INFO, "Yabause, rather" + " than displaying this menu. "); + font_printf(x, y, -1, TEXT_COLOR_NG, "Remember"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "to \"Save settings\"" + " after changing this option!"); + y += line_height; + break; + case OPT_GENERAL_FILES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Select the files" + " containing the BIOS image, CD image,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "and backup data you" + " want to use."); + y += line_height; + break; + case OPT_GENERAL_BUP_AUTOSAVE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Save the contents of" + " backup RAM whenever you save your"); + y += line_height; + x = font_printf(75, y, -1, TEXT_COLOR_INFO, "game in the" + " emulator. "); + font_printf(x, y, -1, TEXT_COLOR_NG, "Even if this option is" + " enabled,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "backup RAM is NOT saved" + " when you quit Yabause."); + y += line_height; + break; + case OPT_GENERAL_BUP_SAVE_NOW: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Save the current" + " contents of backup RAM to your"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "Memory Stick."); + y += line_height; + break; + case OPT_GENERAL_BUP_SAVE_AS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Save the current" + " contents of backup RAM in a new file"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "on your Memory Stick." + " The new file will be used for"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "automatic and manual" + " saves until you quit Yabause."); + y += line_height; + break; + } + break; + + case MENU_FILES: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Select BIOS/CD/backup files"); + y = menu_center_y - (2*line_height + FONT_HEIGHT) / 2; + draw_menu_option(OPT_FILES_PATH_BIOS, menu_left_edge, y, + "Select BIOS image (%s)", config_get_path_bios()); + y += line_height; + draw_menu_option(OPT_FILES_PATH_CD, menu_left_edge, y, + "Select CD image (%s)", config_get_path_cd()); + y += line_height; + draw_menu_option(OPT_FILES_PATH_BUP, menu_left_edge, y, + "Select backup RAM file (%s)", config_get_path_bup()); + y = menu_help_y; + switch ((FilesMenuOption)cur_option) { + case OPT_FILES_PATH_BIOS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Select the file" + " containing the Saturn BIOS image."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "Changing this file" + " will reset the emulator."); + y += line_height; + break; + case OPT_FILES_PATH_CD: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Select the file" + " containing the CD image you want to"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "use. This can be either" + " an ISO file or a CUE file."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "Changing this file" + " will reset the emulator."); + y += line_height; + break; + case OPT_FILES_PATH_BUP: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Select the file" + " containing your backup RAM data."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "Changing this file" + " will reset the emulator."); + y += line_height; + break; + } + break; + + case MENU_BUTTON: { + static const char saturn_buttons[] = { + [OPT_BUTTON_A] = 'A', + [OPT_BUTTON_B] = 'B', + [OPT_BUTTON_C] = 'C', + [OPT_BUTTON_X] = 'X', + [OPT_BUTTON_Y] = 'Y', + [OPT_BUTTON_Z] = 'Z', + }; + auto inline const char *psp_button_name(const uint32_t bitmask); + auto inline const char *psp_button_name(const uint32_t bitmask) { + static const char * const psp_button_names[] = { + [31 - __builtin_clz(PSP_CTRL_CIRCLE )] = "Circle", + [31 - __builtin_clz(PSP_CTRL_CROSS )] = "Cross", + [31 - __builtin_clz(PSP_CTRL_TRIANGLE)] = "Triangle", + [31 - __builtin_clz(PSP_CTRL_SQUARE )] = "Square", + }; + const unsigned int index = 31 - __builtin_clz(bitmask); + return (index < lenof(psp_button_names) && psp_button_names[index]) + ? psp_button_names[index] : "(None)"; + } + + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure controller buttons"); + y = menu_center_y - (5*line_height + FONT_HEIGHT) / 2; + unsigned int i; + for (i = OPT_BUTTON_A; i <= OPT_BUTTON_Z; i++) { + const uint32_t psp_button = + config_get_button(CONFIG_BUTTON_A + (i - OPT_BUTTON_A)); + draw_menu_option(i, menu_left_edge, y, "%c --- %s", + saturn_buttons[i], psp_button_name(psp_button)); + y += line_height; + } + y = menu_help_y; + font_printf(75, y, -1, TEXT_COLOR_INFO, "Press one of the" + " Circle/Cross/Triangle/Square buttons"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "to assign that PSP button" + " to the Saturn controller's"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "%c button. (L, R, and Start" + " cannot be reassigned.)", saturn_buttons[cur_option]); + y += line_height; + break; + } // case MENU_BUTTON + + case MENU_VIDEO: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure video options"); + y = menu_center_y - (4*line_height + line_height/2 + FONT_HEIGHT) / 2; + draw_menu_option(OPT_VIDEO_HW, menu_left_edge, y, + "(%c) Use hardware video renderer", + config_get_module_video()==VIDCORE_PSP ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_VIDEO_SW, menu_left_edge, y, + "(%c) Use software video renderer", + config_get_module_video()==VIDCORE_SOFT ? '*' : ' '); + y += line_height*3/2; + draw_menu_option(OPT_VIDEO_RENDER, menu_left_edge, y, + " Configure hardware rendering settings..."); + y += line_height; + draw_menu_option(OPT_VIDEO_FRAME_SKIP, menu_left_edge, y, + " Configure frame-skip settings..."); + y += line_height; + draw_menu_option(OPT_VIDEO_SHOW_FPS, menu_left_edge, y, + "[%c] Show FPS", config_get_show_fps() ? '*' : ' '); + y = menu_help_y; + switch ((VideoMenuOption)cur_option) { + case OPT_VIDEO_HW: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The hardware video" + " renderer is fast, but may not always"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "display accurate" + " graphics."); + y += line_height; + break; + case OPT_VIDEO_SW: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The software video" + " renderer is slow, but more faithful"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "to the actual Saturn" + " display."); + y += line_height; + break; + case OPT_VIDEO_RENDER: + font_printf(75, y, -1, TEXT_COLOR_INFO, "This submenu lets you" + " change certain aspects of the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "hardware video" + " renderer's behavior. The options do not"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "affect the software" + " video renderer."); + y += line_height; + break; + case OPT_VIDEO_FRAME_SKIP: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The hardware renderer" + " can be configured to skip drawing"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "some frames to speed" + " up the emulation. This submenu lets"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "you choose whether to" + " skip frames and how many to skip."); + y += line_height; + break; + case OPT_VIDEO_SHOW_FPS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Display the emulator's" + " current speed in frames per second"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "in the upper-right" + " corner of the screen."); + y += line_height; + break; + } + break; + + case MENU_RENDER: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure hardware rendering settings"); + y = menu_center_y - (4*line_height + FONT_HEIGHT) / 2; + draw_menu_option(OPT_RENDER_CACHE_TEXTURES, menu_left_edge, y, + "[%c] Aggressively cache pixel data", + config_get_cache_textures() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_RENDER_SMOOTH_TEXTURES, menu_left_edge, y, + "[%c] Smooth textures and sprites", + config_get_smooth_textures() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_RENDER_SMOOTH_HIRES, menu_left_edge, y, + "[%c] Smooth high-resolution graphics", + config_get_smooth_hires() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_RENDER_ENABLE_ROTATE, menu_left_edge, y, + "[%c] Enable rotated/distorted graphics", + config_get_enable_rotate() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_RENDER_OPTIMIZE_ROTATE, menu_left_edge, y, + "[%c] Optimize rotated/distorted graphics", + config_get_optimize_rotate() ? '*' : ' '); + y = menu_help_y; + switch ((RenderMenuOption)cur_option) { + case OPT_RENDER_CACHE_TEXTURES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Try to cache native" + " pixel data to speed up drawing."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "This may cause the" + " wrong graphics to be shown in"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "rare cases."); + y += line_height; + break; + case OPT_RENDER_SMOOTH_TEXTURES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Apply smoothing" + " (antialiasing) to textures and sprites."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "This can reduce" + " jaggedness in 3-D environments, but may"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "also blur images" + " originally intended to look pixelated."); + y += line_height; + break; + case OPT_RENDER_SMOOTH_HIRES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Apply smoothing to" + " high-resolution background graphics."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "This can make high" + "-resolution screens look clearer, but"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "may also slow down" + " the emulator."); + y += line_height; + break; + case OPT_RENDER_ENABLE_ROTATE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Rotated or distorted" + " background graphics can slow down"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "the emulator" + " significantly. Disable this option to"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "turn them off."); + y += line_height; + break; + case OPT_RENDER_OPTIMIZE_ROTATE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Some types of rotated" + " graphics can be drawn quickly,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "at the expense of" + " accuracy. Disable this option to"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "always use the" + " accurate (but slow) drawing method."); + y += line_height; + break; + } + break; + + case MENU_FRAME_SKIP: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure frame-skip settings"); + y = menu_center_y - (3*line_height + FONT_HEIGHT) / 2; + draw_menu_option(OPT_FRAME_SKIP_AUTO, menu_left_edge, y, + " Frame-skip mode: [%s]", + config_get_frameskip_auto() ? " Auto " : "Manual"); + y += line_height; + draw_menu_option(OPT_FRAME_SKIP_NUM, menu_left_edge, y, + " Number of frames to skip: [%d]", + config_get_frameskip_num()); + y += line_height; + draw_menu_option(OPT_FRAME_SKIP_INTERLACE, menu_left_edge, y, + "[%c] Limit to 30fps for interlaced display", + config_get_frameskip_interlace() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_FRAME_SKIP_ROTATE, menu_left_edge, y, + "[%c] Halve framerate for rotated backgrounds", + config_get_frameskip_rotate() ? '*' : ' '); + y = menu_help_y; + switch ((FrameSkipMenuOption)cur_option) { + case OPT_FRAME_SKIP_AUTO: + if (config_get_frameskip_auto()) { + font_printf(75, y, -1, TEXT_COLOR_INFO, "In Auto mode, the" + " emulator will automatically choose the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "number of frames to" + " skip depending on the emulation speed."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "Note: Auto mode is" + " not currently implemented."); + y += line_height; + } else { + font_printf(75, y, -1, TEXT_COLOR_INFO, "In Manual mode, the" + " emulator will always skip the number"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "of frames set in" + " this menu."); + y += line_height; + } + break; + case OPT_FRAME_SKIP_NUM: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The number of frames" + " to skip for every frame drawn when"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "Manual mode is" + " selected. 0 means \"draw every frame\","); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "1 means \"draw every" + " second frame\", and so on."); + y += line_height; + break; + case OPT_FRAME_SKIP_INTERLACE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Always skip at least" + " one frame when drawing interlaced"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "(high-resolution)" + " screens. Has no effect unless the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "\"number of frames" + " to skip\" is set to zero."); + y += line_height; + break; + case OPT_FRAME_SKIP_ROTATE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Reduce the frame rate" + " by half when rotated or distorted"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "background graphics are" + " displayed."); + y += line_height; + break; + } + break; + + case MENU_ADVANCED: + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure advanced emulation options"); + y = menu_center_y - (6*(line_height-2) + 2*((line_height-2)/2) + FONT_HEIGHT) / 2; + draw_menu_option(OPT_ADVANCED_SH2_RECOMPILER, menu_left_edge, y, + "[%c] Use SH-2 recompiler", + config_get_module_sh2()==SH2CORE_PSP ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_ADVANCED_SH2_OPTIMIZE, menu_left_edge, y, + " Select SH-2 optimizations..."); + y += (line_height-2)*3/2; + if (me_available) { + draw_menu_option(OPT_ADVANCED_MEDIA_ENGINE, menu_left_edge, y, + " Configure Media Engine options..."); + } else { + draw_disabled_menu_option(OPT_ADVANCED_MEDIA_ENGINE, + menu_left_edge, y, + " Configure Media Engine options..."); + } + y += (line_height-2)*3/2; + draw_menu_option(OPT_ADVANCED_DECILINE_MODE, menu_left_edge, y, + "[%c] Use more precise emulation timing", + config_get_deciline_mode() ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_ADVANCED_AUDIO_SYNC, menu_left_edge, y, + "[%c] Sync audio output to emulation", + config_get_audio_sync() ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_ADVANCED_CLOCK_SYNC, menu_left_edge, y, + "[%c] Sync Saturn clock to emulation", + config_get_clock_sync() ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_ADVANCED_CLOCK_FIXED_TIME, menu_left_edge, y, + "[%c] Always start from 1998-01-01 12:00", + config_get_clock_fixed_time() ? '*' : ' '); + y = menu_help_y; + switch ((AdvancedMenuOption)cur_option) { + case OPT_ADVANCED_SH2_RECOMPILER: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Use the SH-2 recompiler" + " instead of the much slower"); + y += line_height; + x = font_printf(75, y, -1, TEXT_COLOR_INFO, "interpreter. "); + font_printf(x, y, -1, TEXT_COLOR_NG, "Changing this option will" + " reset the emulator."); + y += line_height; + break; + case OPT_ADVANCED_SH2_OPTIMIZE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Enable or disable" + " specific optimizations used by the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "SH-2 recompiler."); + y += line_height; + break; + case OPT_ADVANCED_MEDIA_ENGINE: + if (me_available) { + font_printf(75, y, -1, TEXT_COLOR_INFO, "Enable or disable" + " use of the PSP's Media Engine for"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "emulation, and" + " configure various parameters used by"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "the Media Engine."); + y += line_height; + } else { + font_printf(75, y, -1, TEXT_COLOR_NG, "The Media Engine access" + " library (me.prx) was not found,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "so the Media Engine" + " cannot be used for emulation."); + y += line_height; + } + break; + case OPT_ADVANCED_DECILINE_MODE: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Increase the precision" + " of the emulator's execution timing."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "This slows down the" + " emulation, but may be required for"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "some games to work" + " correctly."); + y += line_height; + break; + case OPT_ADVANCED_AUDIO_SYNC: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Synchronize the" + " generated audio data with the emulation."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "This keeps the audio" + " and video in sync, but causes audio"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "dropouts when the" + " emulator runs slower than real time."); + y += line_height; + break; + case OPT_ADVANCED_CLOCK_SYNC: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Synchronize the" + " Saturn's internal clock with the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "emulation, rather" + " than following real time regardless"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "of emulation speed."); + y += line_height; + break; + case OPT_ADVANCED_CLOCK_FIXED_TIME: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Start the Saturn's" + " internal clock at 1998-01-01 12:00"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "every time the" + " emulator is reset. Generally only"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "useful for debugging."); + y += line_height; + break; + } + break; + + case MENU_OPTIMIZE: { + const uint32_t optflags = config_get_sh2_optimizations(); + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Select SH-2 emulation optimizations"); + y = menu_center_y - (7*(line_height-2) + FONT_HEIGHT) / 2; + draw_menu_option(OPT_OPTIMIZE_ASSUME_SAFE_DIVISION, menu_left_edge, y, + "[%c] Assume safe division operations", + optflags & SH2_OPTIMIZE_ASSUME_SAFE_DIVISION ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_FOLD_SUBROUTINES, menu_left_edge, y, + "[%c] Fold short subroutines into callers", + optflags & SH2_OPTIMIZE_FOLD_SUBROUTINES ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_BRANCH_TO_RTS, menu_left_edge, y, + "[%c] Optimize branch/return pairs", + optflags & SH2_OPTIMIZE_BRANCH_TO_RTS ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_LOCAL_ACCESSES, menu_left_edge, y, + "[%c] Optimize accesses to local data", + optflags & SH2_OPTIMIZE_LOCAL_ACCESSES ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_POINTERS, menu_left_edge, y, + "[%c] Optimize pointer register accesses", + optflags & SH2_OPTIMIZE_POINTERS ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_POINTERS_MAC, menu_left_edge, y, + " [%c] Try harder for MAC instructions", + optflags & SH2_OPTIMIZE_POINTERS_MAC ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_LOCAL_POINTERS, menu_left_edge, y, + " [%c] Assume consistent local pointers", + optflags & SH2_OPTIMIZE_LOCAL_ACCESSES ? '*' : ' '); + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_STACK, menu_left_edge, y, + "[%c] Optimize stack accesses", + optflags & SH2_OPTIMIZE_STACK ? '*' : ' '); +#if 0 // FIXME: out of space on the screen + y += line_height-2; + draw_menu_option(OPT_OPTIMIZE_MAC_NOSAT, menu_left_edge, y, + "[%c] Optimize unsaturated multiplication", + optflags & SH2_OPTIMIZE_MAC_NOSAT ? '*' : ' '); +#endif + y = menu_help_y; + switch ((OptimizeMenuOption)cur_option) { + case OPT_OPTIMIZE_ASSUME_SAFE_DIVISION: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Speed up division" + " operations by assuming that quotients"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "will not overflow" + " and division by zero will not occur."); + y += line_height; + break; + case OPT_OPTIMIZE_FOLD_SUBROUTINES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Fold (inline) short," + " simple subroutines into the"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "routines which call" + " them. May cause crashes"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "with games that use" + " self-modifying code."); + y += line_height; + break; + case OPT_OPTIMIZE_BRANCH_TO_RTS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Speed up branches" + " which target RTS instructions."); + y += line_height; + break; + case OPT_OPTIMIZE_LOCAL_ACCESSES: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Detect and speed up" + " accesses to function-local data."); + y += line_height; + break; + case OPT_OPTIMIZE_POINTERS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Detect SH-2 registers" + " which are used as data pointers,"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "and speed up memory" + " accesses through those registers."); + y += line_height; + break; + case OPT_OPTIMIZE_POINTERS_MAC: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Try harder to optimize" + " pointers to MAC (multiply-and-"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "accumulate) instructions." + " Ineffective unless pointer"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "optimizations are also" + " enabled."); + y += line_height; + break; + case OPT_OPTIMIZE_LOCAL_POINTERS: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Assume that pointers" + " loaded from local data or with a"); + y += line_height-2; + font_printf(75, y, -1, TEXT_COLOR_INFO, "MOVA instruction will" + " always access the same region of"); + y += line_height-2; + font_printf(75, y, -1, TEXT_COLOR_INFO, "memory. Ineffective" + " unless pointer and local data"); + y += line_height-2; + font_printf(75, y, -1, TEXT_COLOR_INFO, "optimizations are also" + " enabled."); + y += line_height-2; + break; + case OPT_OPTIMIZE_STACK: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Speed up accesses to" + " the SH-2 stack."); + y += line_height; + break; +#if 0 // FIXME: out of space on the screen + case OPT_OPTIMIZE_MAC_NOSAT: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Speed up multiply-and" + "-accumulate operations which are"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "known not to use" + " saturation."); + y += line_height; +#endif + } + break; + } + + case MENU_MEDIA_ENGINE: { + font_printf(DISPLAY_WIDTH/2, menu_title_y, 0, TEXT_COLOR_INFO, + "Configure Media Engine options"); + y = menu_center_y - (2*line_height + FONT_HEIGHT) / 2; + draw_menu_option(OPT_MEDIA_ENGINE_USE_ME, menu_left_edge, y, + "[%c] Use Media Engine for emulation", + config_get_use_me() ? '*' : ' '); + y += line_height; + draw_menu_option(OPT_MEDIA_ENGINE_WRITEBACK_PERIOD, menu_left_edge, y, + " Cache writeback frequency: [1/%-2u]", + config_get_me_writeback_period()); + y += line_height; + draw_menu_option(OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY, menu_left_edge, y, + " Sound RAM write-through region: [%3uk]", + config_get_me_uncached_boundary() / 1024); + y = menu_help_y; + switch ((MediaEngineMenuOption)cur_option) { + case OPT_MEDIA_ENGINE_USE_ME: + font_printf(75, y, -1, TEXT_COLOR_INFO, "Use the Media Engine" + " CPU for emulation. This option"); + y += line_height; + x = font_printf(75, y, -1, TEXT_COLOR_INFO, "is "); + x = font_printf(x, y, -1, TEXT_COLOR_NG, "EXPERIMENTAL"); + font_printf(x, y, -1, TEXT_COLOR_INFO, "; see the manual" + " for details."); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_NG, "You must restart" + " Yabause after changing this option."); + y += line_height; + break; + case OPT_MEDIA_ENGINE_WRITEBACK_PERIOD: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The relative" + " frequency of data cache synchronization when"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "using the Media" + " Engine for emulation. 1/1 is safest;"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "lower frequencies" + " may run faster, but may also crash."); + y += line_height; + break; + case OPT_MEDIA_ENGINE_UNCACHED_BOUNDARY: + font_printf(75, y, -1, TEXT_COLOR_INFO, "The size of the" + " region at the beginning of sound RAM"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "which is written" + " through the PSP's cache. Larger values"); + y += line_height; + font_printf(75, y, -1, TEXT_COLOR_INFO, "are safer but slower;" + " 2k should be good for most games."); + y += line_height; + break; + } + break; + } + + case MENU_YESNO: { + int nlines; + char buf[100]; + const char *s, *eol; + for (s = yesno_prompt; *s; s = eol+1) { + eol = s + strcspn(s, "\n"); + nlines++; + } + y = DISPLAY_HEIGHT/2 - ((nlines+1) * line_height + FONT_HEIGHT) / 2; + for (s = yesno_prompt; *s; s = eol+1, y += line_height) { + eol = s + strcspn(s, "\n"); + snprintf(buf, sizeof(buf), "%.*s", eol - s, s); + font_printf(DISPLAY_WIDTH/2, y, 0, TEXT_COLOR_INFO, buf); + } + y += line_height; + draw_menu_option(OPT_YESNO_YES, DISPLAY_WIDTH/2 - 45, y, "Yes"); + draw_menu_option(OPT_YESNO_NO, DISPLAY_WIDTH/2 + 30, y, "No"); + } + + } // switch (cur_menu) + + /* If a file selector is open, draw it */ + + if (filesel) { + filesel_draw(filesel); + } +} + +/*----------------------------------*/ + +/** + * cur_option_confirm_text: Return the text to be displayed for the + * "confirm" button help. + * + * [Parameters] + * None + * [Return value] + * String to display for the "confirm" button help + */ +static const char *cur_option_confirm_text(void) +{ + switch ((MenuIndex)cur_menu) { + case MENU_MAIN: + switch ((MainMenuOption)cur_option) { + case OPT_MAIN_SAVE: + return "O: Save settings"; + case OPT_MAIN_RESET: + return yabause_initted ? "L+R+O: Reset emulator" : ""; + default: + return "O: Enter submenu"; + } + case MENU_GENERAL: + switch ((GeneralMenuOption)cur_option) { + case OPT_GENERAL_FILES: + return "O: Enter submenu X: Return to previous menu"; + case OPT_GENERAL_BUP_SAVE_NOW: + case OPT_GENERAL_BUP_SAVE_AS: + if (yabause_initted) { + return "O: Save backup RAM X: Return to previous menu"; + } else { + return "X: Return to previous menu"; + } + default: + return "O: Toggle on/off X: Return to previous menu"; + } + case MENU_FILES: + return "O: Select file X: Return to previous menu"; + case MENU_BUTTON: + return "O/X/Triangle/Square: Assign button Start: Previous menu"; + case MENU_VIDEO: + switch ((VideoMenuOption)cur_option) { + case OPT_VIDEO_RENDER: + case OPT_VIDEO_FRAME_SKIP: + return "O: Enter submenu X: Return to previous menu"; + case OPT_VIDEO_SHOW_FPS: + return "O: Toggle on/off X: Return to previous menu"; + default: + return "O: Select X: Return to previous menu"; + } + case MENU_RENDER: + return "O: Toggle on/off X: Return to previous menu"; + case MENU_FRAME_SKIP: + switch ((FrameSkipMenuOption)cur_option) { + case OPT_FRAME_SKIP_AUTO: + return "O: Change setting X: Return to previous menu"; + case OPT_FRAME_SKIP_NUM: + return "Left/Right: Change setting X: Return to previous menu"; + default: + return "O: Toggle on/off X: Return to previous menu"; + } + case MENU_ADVANCED: + switch ((AdvancedMenuOption)cur_option) { + case OPT_ADVANCED_SH2_RECOMPILER: + return "L+R+O: Toggle on/off X: Return to previous menu"; + case OPT_ADVANCED_SH2_OPTIMIZE: + return "O: Enter submenu X: Return to previous menu"; + case OPT_ADVANCED_MEDIA_ENGINE: + if (me_available) { + return "O: Enter submenu X: Return to previous menu"; + } else { + return "X: Return to previous menu"; + } + default: + return "O: Toggle on/off X: Return to previous menu"; + } + case MENU_OPTIMIZE: + return "O: Toggle on/off X: Return to previous menu"; + case MENU_MEDIA_ENGINE: + switch ((FrameSkipMenuOption)cur_option) { + case OPT_MEDIA_ENGINE_USE_ME: + return "O: Change setting X: Return to previous menu"; + default: + return "Left/Right: Change setting X: Return to previous menu"; + } + case MENU_YESNO: + return "O: Confirm selection X: Cancel"; + } + DMSG("Invalid menu/option %d/%d", cur_menu, cur_option); + return "O: Confirm"; +} + +/*----------------------------------*/ + +/** + * draw_menu_option: Draw a single menu option. If the option is + * currently selected, also draws the menu cursor. + * + * [Parameters] + * option: Option ID (OPT_*) + * x, y: Text position + * format: Format string for option text + * ...: Format arguments + * [Return value] + * None + */ +static void draw_menu_option(int option, int x, int y, const char *format, ...) +{ + PRECOND(format != NULL, return); + + char buf[1000]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + int x2 = font_printf(x, y, -1, TEXT_COLOR, "%s", buf); + + if (cur_option == option) { + const float cursor_alpha = + (sinf((cursor_timer / (float)CURSOR_PERIOD) * (float)M_TWOPI) + 1) + / 2; + const uint32_t cursor_alpha_byte = + floorf((CURSOR_COLOR>>24 & 0xFF) * cursor_alpha + 0.5f); + display_fill_box(x-2, y-2, x2+1, (y+FONT_HEIGHT)+1, + cursor_alpha_byte<<24 | (CURSOR_COLOR & 0x00FFFFFF)); + } +} + +/*----------------------------------*/ + +/** + * draw_disabled_menu_option: Draw a single disabled menu option. If the + * option is currently selected, also draws the menu cursor. + * + * [Parameters] + * option: Option ID (OPT_*) + * x, y: Text position + * format: Format string for option text + * ...: Format arguments + * [Return value] + * None + */ +static void draw_disabled_menu_option(int option, int x, int y, + const char *format, ...) +{ + PRECOND(format != NULL, return); + + char buf[1000]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + int x2 = font_printf(x, y, -1, TEXT_COLOR_DISABLED, "%s", buf); + + if (cur_option == option) { + const float cursor_alpha = + (sinf((cursor_timer / (float)CURSOR_PERIOD) * (float)M_TWOPI) + 1) + / 2; + const uint32_t cursor_alpha_byte = + floorf((CURSOR_COLOR>>24 & 0xFF) * cursor_alpha + 0.5f); + display_fill_box(x-2, y-2, x2+1, (y+FONT_HEIGHT)+1, + cursor_alpha_byte<<24 | (CURSOR_COLOR & 0x00FFFFFF)); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/menu.h b/yabause/src/psp/menu.h new file mode 100644 index 0000000000..936f685b7a --- /dev/null +++ b/yabause/src/psp/menu.h @@ -0,0 +1,79 @@ +/* src/psp/menu.h: PSP menu interface header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_MENU_H +#define PSP_MENU_H + +/*************************************************************************/ + +/** + * menu_open: Open the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void menu_open(void); + +/** + * menu_run: Perform a single frame's processing for the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void menu_run(void); + +/** + * menu_close: Close the menu interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void menu_close(void); + +/** + * menu_set_error: Set an error message to be displayed on the menu + * screen. If message is NULL, any message currently displayed is cleared. + * + * [Parameters] + * message: Message text (NULL to clear current message) + * [Return value] + * None + */ +extern void menu_set_error(const char *message); + +/*************************************************************************/ + +#endif // PSP_MENU_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/misc.c b/yabause/src/psp/misc.c new file mode 100644 index 0000000000..efe5c7a81a --- /dev/null +++ b/yabause/src/psp/misc.c @@ -0,0 +1,119 @@ +/* src/psp/misc.c: PSP support routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../memory.h" + +#include "config.h" +#include "misc.h" +#include "sys.h" + +/*************************************************************************/ +/************************** Interface routines ***************************/ +/*************************************************************************/ + +/** + * save_backup_ram: Save the contents of backup RAM to the configured + * file. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on failure + */ +extern int save_backup_ram(void) +{ + const char *path = config_get_path_bup(); + if (!path || !*path) { + DMSG("No backup RAM file configured!"); + goto error_return; + } + + /* Lock the power switch while writing so the user (hopefully) can't + * shut the PSP off on us. */ + scePowerLock(0); + + int fd = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0600); + if (fd < 0) { + DMSG("open(%s): %s", path, psp_strerror(fd)); + goto error_unlock_power; + } + + int res = sceIoWrite(fd, BupRam, 0x10000); + if (res != 0x10000) { + DMSG("write(%s): %s", path, psp_strerror(fd)); + sceIoClose(fd); + goto error_unlock_power; + } + + res = sceIoClose(fd); + if (res != 0) { + DMSG("close(%s): %s", path, psp_strerror(fd)); + goto error_unlock_power; + } + + /* All done--don't forget to unlock the power switch before returning! */ + scePowerUnlock(0); + return 1; + + error_unlock_power: + scePowerUnlock(0); + error_return: + return 0; +} + +/*************************************************************************/ + +/** + * psp_writeback_cache_for_scsp: Write back all dirty data from the SC's + * cache for an ScspExec() call, depending on the writeback frequency + * selected by the user. + * + * [Parameters] + * None + * [Return value] + * Nonzero if writeback was executed, zero if writeback was skipped + */ +int psp_writeback_cache_for_scsp(void) +{ + static uint32_t counter; + + counter++; + if (!(counter & (config_get_me_writeback_period() - 1))) { + sceKernelDcacheWritebackAll(); + return 1; + } else { + return 0; + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/misc.h b/yabause/src/psp/misc.h new file mode 100644 index 0000000000..8918620ab5 --- /dev/null +++ b/yabause/src/psp/misc.h @@ -0,0 +1,104 @@ +/* src/psp/misc.h: PSP support routine header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_MISC_H +#define PSP_MISC_H + +/*************************************************************************/ + +/** + * save_backup_ram: Save the contents of backup RAM to the configured + * file. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on failure + */ +extern int save_backup_ram(void); + +/** + * psp_writeback_cache_for_scsp: Write back all dirty data from the SC's + * cache for an ScspExec() call, depending on the writeback frequency + * selected by the user. + * + * [Parameters] + * None + * [Return value] + * Nonzero if writeback was skipped, zero if writeback was executed + */ +extern int psp_writeback_cache_for_scsp(void); + +/*-----------------------------------------------------------------------*/ + +/** + * checksum_fast16, checksum_fast32: Perform a fast checksum of 16-bit or + * 32-bit words in a block by simply summing all words and returning the + * cumulative 32-bit total. + * + * [Parameters] + * ptr: Pointer to memory block to checksum + * count: Number of 16-bit or 32-bit words in block + * [Return value] + * Block checksum + */ +static inline uint32_t checksum_fast16(const uint16_t *ptr, unsigned int count) +{ + uint32_t sum = 0; + for (; count >= 4; count -= 4) { + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + } + while (count--) { + sum += *ptr++; + } + return sum; +} + +static inline uint32_t checksum_fast32(const uint32_t *ptr, unsigned int count) +{ + uint32_t sum = 0; + for (; count >= 4; count -= 4) { + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + } + while (count--) { + sum += *ptr++; + } + return sum; +} + +/*************************************************************************/ + +#endif // PSP_MISC_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/osk.c b/yabause/src/psp/osk.c new file mode 100644 index 0000000000..0e352d5190 --- /dev/null +++ b/yabause/src/psp/osk.c @@ -0,0 +1,479 @@ +/* src/psp/osk.c: PSP on-screen keyboard management + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "osk.h" +#include "sys.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Is the on-screen keyboard active? */ +static uint8_t osk_active; + +/* Has a close been requested? */ +static uint8_t osk_closing; + +/* Parameter block pointer (only valid when osk_active != 0). Pointers to + * subsidiary data structures such as string buffers are stored within the + * appropriate parent data structure. */ +static SceUtilityOskParams *osk_params; + +/*-----------------------------------------------------------------------*/ + +/* Local function declarations. */ +static void reset_osk(void); +static uint16_t *utf8to16(const char *str); +static char *utf16to8(const uint16_t *str); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * osk_open: Open the on-screen keyboard with the given prompt string and + * default text. + * + * [Parameters] + * prompt: Prompt string + * deftext: Default text + * maxlen: Maximum length (number of _characters_, not bytes) of + * entered text, not including the trailing null + * [Return value] + * Nonzero on success, zero on failure + */ +int osk_open(const char *prompt, const char *deftext, unsigned int maxlen) +{ + PRECOND(prompt != NULL, goto error_return); + PRECOND(deftext != NULL, goto error_return); + PRECOND(maxlen > 0, goto error_return); + PRECOND(strlen(deftext) <= maxlen, goto error_return); + + if (osk_active) { + DMSG("Tried to start a second OSK while one was already active!"); + goto error_return; + } + + osk_params = calloc(1, sizeof(*osk_params)); + if (!osk_params) { + DMSG("No memory for osk_params"); + goto error_return; + } + osk_params->base.size = sizeof(*osk_params); + osk_params->base.graphicsThread = THREADPRI_UTILITY + 1; + osk_params->base.accessThread = THREADPRI_UTILITY + 3; + osk_params->base.fontThread = THREADPRI_UTILITY + 2; + osk_params->base.soundThread = THREADPRI_UTILITY; + osk_params->datacount = 1; + + osk_params->data = calloc(1, sizeof(*osk_params->data)); + if (!osk_params->data) { + DMSG("No memory for data"); + goto error_free_osk_params; + } + osk_params->data->language = PSP_UTILITY_OSK_LANGUAGE_ENGLISH; + osk_params->data->inputtype = PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE + | PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE + | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT + | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL; + osk_params->data->lines = 1; + /* The order of these field names is apparently reversed in the current + * (r2493) PSPSDK. Set both to maxlen+1 just to be safe; the null + * terminator in "deftext" will keep the OSK from overrunning the end + * of the default text. */ + osk_params->data->outtextlength = maxlen + 1; + osk_params->data->outtextlimit = maxlen + 1; + + osk_params->data->desc = utf8to16(prompt); + if (!osk_params->data->desc) { + DMSG("No memory for prompt buffer"); + goto error_free_data; + } + + osk_params->data->intext = utf8to16(deftext); + if (!osk_params->data->intext) { + DMSG("No memory for default text buffer"); + goto error_free_desc; + } + + osk_params->data->outtext = malloc(2 * (maxlen + 1)); + if (!osk_params->data->outtext) { + DMSG("No memory for output text buffer"); + goto error_free_intext; + } + + int res = sceUtilityOskInitStart(osk_params); + if (res < 0) { + DMSG("sceUtilityOskInitStart() failed: %s", psp_strerror(res)); + return 0; + } + + osk_active = 1; + osk_closing = 0; + return 1; + + error_free_intext: + free(osk_params->data->intext); + error_free_desc: + free(osk_params->data->desc); + error_free_data: + free(osk_params->data); + error_free_osk_params: + free(osk_params); + osk_params = NULL; + error_return: + return 0; +} + +/*************************************************************************/ + +/** + * osk_update: Update the on-screen keyboard if it is active. Must be + * called once per frame while the on-screen keyboard is active; may be + * called at any other time (the function does nothing in that case). + * + * [Parameters] + * None + * [Return value] + * None + */ +void osk_update(void) +{ + if (osk_active) { + const int status = sceUtilityOskGetStatus(); + if (status == PSP_UTILITY_DIALOG_VISIBLE) { + int res = sceUtilityOskUpdate(1); + if (res < 0) { + DMSG("sceUtilityOskUpdate() failed: %s", psp_strerror(res)); + } + } else if (sceUtilityOskGetStatus() == PSP_UTILITY_DIALOG_QUIT) { + sceUtilityOskShutdownStart(); + } else if (sceUtilityOskGetStatus() == PSP_UTILITY_DIALOG_FINISHED) { + if (osk_closing) { + reset_osk(); + } + } + } +} + +/*************************************************************************/ + +/** + * osk_status: Return whether the on-screen keyboard is currently active. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the on-screen keyboard is active, else zero + */ +int osk_status(void) +{ + return osk_active; +} + +/*************************************************************************/ + +/** + * osk_result: Return the result status from the on-screen keyboard. + * + * [Parameters] + * None + * [Return value] + * Result status (OSK_RESULT_*) + */ +OSKResult osk_result(void) +{ + if (!osk_active || osk_closing) { + return OSK_RESULT_NONE; + } else if (sceUtilityOskGetStatus() != PSP_UTILITY_DIALOG_FINISHED) { + return OSK_RESULT_RUNNING; + } else if (osk_params->data->result == PSP_UTILITY_OSK_RESULT_UNCHANGED) { + return OSK_RESULT_UNCHANGED; + } else if (osk_params->data->result == PSP_UTILITY_OSK_RESULT_CHANGED) { + return OSK_RESULT_CHANGED; + } else if (osk_params->data->result == PSP_UTILITY_OSK_RESULT_CANCELLED) { + return OSK_RESULT_CANCELLED; + } else { + DMSG("Weird result value %d from OSK", osk_params->data->result); + return OSK_RESULT_ERROR; + } +} + +/*************************************************************************/ + +/** + * osk_get_text: Return the text entered by the user from the on-screen + * keyboard in a newly malloc()ed buffer. + * + * [Parameters] + * None + * [Return value] + * Entered text, or NULL if not available (e.g., if the OSK was cancelled) + */ +char *osk_get_text(void) +{ + int result = osk_result(); + if (result != OSK_RESULT_UNCHANGED && result != OSK_RESULT_CHANGED) { + return NULL; + } + + char *text = utf16to8(osk_params->data->outtext); + if (!text) { + DMSG("Failed to convert entered text to UTF-8"); + return NULL; + } + + return text; +} + +/*************************************************************************/ + +/** + * osk_close: Close the on-screen keyboard and discard all associated + * resources (including the entered text). If the on-screen keyboard is + * not active, this function does nothing. + * + * Even after calling this function, the caller MUST continue to call + * osk_update() once per frame until osk_status() returns zero. + * + * [Parameters] + * None + * [Return value] + * None + */ +void osk_close(void) +{ + if (osk_active) { + if (sceUtilityOskGetStatus() == PSP_UTILITY_DIALOG_FINISHED) { + /* Free all resources immediately. */ + reset_osk(); + } else { + /* Request a close, and free resources when the OS is done. */ + sceUtilityOskShutdownStart(); + osk_closing = 1; + } + } +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * reset_osk: Free all local resources and reset the OSK to the inactive + * state. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void reset_osk(void) +{ + PRECOND(osk_active, return); + + free(osk_params->data->outtext); + free(osk_params->data->intext); + free(osk_params->data->desc); + free(osk_params->data); + free(osk_params); + osk_params = NULL; + osk_closing = 0; + osk_active = 0; +} + +/*************************************************************************/ + +/** + * utf8to16, utf16to8: Convert a string between 8-bit and 16-bit Unicode + * formats. The returned string is stored in a newly malloc()ed buffer. + * + * [Parameters] + * str: String to convert + * [Return value] + * Converted string, or NULL on error + * [Notes] + * These functions only support Unicode characters with codepoints in + * the range 0-65535 (16-bit characters). + */ +static uint16_t *utf8to16(const char *str) +{ + /* Allocate a buffer big enough for the longest possible result; we'll + * shrink it if necessary when we're done. */ + const uint32_t bufsize = (strlen(str) + 1) * 2; + uint16_t *out = malloc(bufsize); + if (!out) { + DMSG("Can't allocate %u bytes", bufsize); + return NULL; + } + + /* Convert the string, character by character. */ + uint32_t pos = 0, len = 0; + while (str[pos] != 0) { + const uint8_t ch = (uint8_t)str[pos++]; + if (ch < 0x80) { + out[len++] = ch; + } else if (ch < 0xC0) { + /* Continuation bytes are invalid as the first byte of a UTF-8 + * sequence. */ + DMSG("Invalid continuation byte 0x%02X at offset %u", ch, pos-1); + goto fail; + } else if (ch < 0xE0) { + const uint8_t ch_1 = (uint8_t)str[pos++]; + if (ch_1 < 0x80 || ch_1 >= 0xC0) { + /* The required continuation byte is missing, so treat this + * character as unknown and restart processing on the + * second byte (which we just checked). */ + DMSG("Missing continuation byte at offset %u (got 0x%02X)", + pos-1, ch_1); + goto fail; + } else if (ch < 0xC2) { + /* Characters with codepoints less than 128 must be coded + * using the single-byte format; for example, C1 9C for the + * backslash character (U+005C) is invalid. This is a + * common attack vector against security vulnerabilities, + * so we explicitly disallow such invalid forms. */ + DMSG("Invalid extended form 0x%02X 0x%02X at offset %u", + pos-2, ch, ch_1); + goto fail; + } else { + out[len++] = (ch & 0x1F) << 6 + | (ch_1 & 0x3F) << 0; + } + } else if (ch < 0xF0) { + const uint8_t ch_1 = (uint8_t)str[pos++]; + const uint8_t ch_2 = (uint8_t)str[pos++]; + if (ch_1 < 0x80 || ch_1 >= 0xC0) { + DMSG("Missing continuation byte at offset %u (got 0x%02X)", + pos-2, ch_1); + goto fail; + } else if (ch_2 < 0x80 || ch_2 >= 0xC0) { + DMSG("Missing continuation byte at offset %u (got 0x%02X)", + pos-1, ch_2); + goto fail; + } else if (ch == 0xE0 && ch_1 < 0xA0) { + DMSG("Invalid extended form 0x%02X 0x%02X 0x%02X at offset %u", + pos-3, ch, ch_1, ch_2); + goto fail; + } else { + out[len++] = (ch & 0x0F) << 12 + | (ch_1 & 0x3F) << 6 + | (ch_2 & 0x3F) << 0; + if (out[len-1] >= 0xD800 && out[len-1] < 0xE000) { + DMSG("Invalid surrogate 0x%04X at offset %u", + out[len-1], pos-3); + goto fail; + } + } + } else { + DMSG("Out-of-range codepoint with first byte 0x%02X at offset %u", + ch, pos-1); + goto fail; + } + } + + /* Append a terminating null. */ + out[len++] = 0; + + /* If we ended up with fewer characters than we allocated space for, + * shrink the output buffer before returning it. */ + if (len*2 < bufsize) { + /* This should never fail, but just in case, save the result in a + * temporary variable and check for NULL first. */ + uint16_t *new_out = realloc(out, len*2); + if (new_out) { + out = new_out; + } + } + + return out; + + fail: + free(out); + return NULL; +} + +/*----------------------------------*/ + +static char *utf16to8(const uint16_t *str) +{ + /* strlen() only works on byte streams, so we have to calculate the + * input string length manually. */ + uint32_t str_len = 0; + while (str[str_len] != 0) { + str_len++; + } + + const uint32_t bufsize = str_len*3 + 1; + char *out = malloc(bufsize); + if (!out) { + DMSG("Can't allocate %u bytes", bufsize); + return NULL; + } + + uint32_t pos = 0, len = 0; + while (str[pos] != 0) { + const uint16_t ch = str[pos++]; + if (ch < 0x80) { + out[len++] = ch; + } else if (ch < 0x800) { + out[len++] = 0xC0 | (ch>>6 & 0x1F); + out[len++] = 0x80 | (ch>>0 & 0x3F); + } else if (ch >= 0xD800 && ch < 0xE000) { + DMSG("Surrogate 0x%04X found at offset %u (not supported)", + ch, pos-1); + goto fail; + } else { + out[len++] = 0xE0 | (ch>>12 & 0x0F); + out[len++] = 0x80 | (ch>> 6 & 0x3F); + out[len++] = 0x80 | (ch>> 0 & 0x3F); + } + } + + out[len++] = 0; + + if (len < bufsize) { + char *new_out = realloc(out, len); + if (new_out) { + out = new_out; + } + } + + return out; + + fail: + free(out); + return NULL; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/osk.h b/yabause/src/psp/osk.h new file mode 100644 index 0000000000..342a04fcc5 --- /dev/null +++ b/yabause/src/psp/osk.h @@ -0,0 +1,124 @@ +/* src/psp/osk.h: PSP on-screen keyboard management header + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_OSK_H +#define PSP_OSK_H + +/*************************************************************************/ + +/* Result codes returned from osk_result(). */ + +typedef enum OSKResult_ { + OSK_RESULT_NONE = 0, + OSK_RESULT_RUNNING, + OSK_RESULT_UNCHANGED, + OSK_RESULT_CHANGED, + OSK_RESULT_CANCELLED, + OSK_RESULT_ERROR, +} OSKResult; + +/*-----------------------------------------------------------------------*/ + +/** + * osk_open: Open the on-screen keyboard with the given prompt string and + * default text. + * + * [Parameters] + * prompt: Prompt string + * deftext: Default text + * maxlen: Maximum length (number of _characters_, not bytes) of + * entered text, not including the trailing null + * [Return value] + * Nonzero on success, zero on failure + */ +extern int osk_open(const char *prompt, const char *deftext, + unsigned int maxlen); + +/** + * osk_update: Update the on-screen keyboard if it is active. Must be + * called once per frame while the on-screen keyboard is active; may be + * called at any other time (the function does nothing in that case). + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void osk_update(void); + +/** + * osk_status: Return whether the on-screen keyboard is currently active. + * + * [Parameters] + * None + * [Return value] + * Nonzero if the on-screen keyboard is active, else zero + */ +extern int osk_status(void); + +/** + * osk_result: Return the result status from the on-screen keyboard. + * + * [Parameters] + * None + * [Return value] + * Result status (OSK_RESULT_*) + */ +extern OSKResult osk_result(void); + +/** + * osk_get_text: Return the text entered by the user from the on-screen + * keyboard in a newly malloc()ed buffer. + * + * [Parameters] + * None + * [Return value] + * Entered text, or NULL if not available (e.g., if the OSK was cancelled) + */ +extern char *osk_get_text(void); + +/** + * osk_close: Close the on-screen keyboard and discard all associated + * resources (including the entered text). If the on-screen keyboard is + * not active, this function does nothing. + * + * Even after calling this function, the caller MUST continue to call + * osk_update() once per frame until osk_status() returns zero. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void osk_close(void); + +/*************************************************************************/ + +#endif // PSP_OSK_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/profile.c b/yabause/src/psp/profile.c new file mode 100644 index 0000000000..0e5e4b8746 --- /dev/null +++ b/yabause/src/psp/profile.c @@ -0,0 +1,233 @@ +/* src/psp/profile.c: Profiling support for Yabause core + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef SYS_PROFILE_H // i.e. profiling enabled -- continues to end of file + +#include +#include +#include +#include "profile.h" + +/*************************************************************************/ + +/* Names for each tracking slot */ +const char * const prof_names[PROF__END] = { + [PROF__UNKNOWN ] = "(unknown)", + [PROF_Total_Emulation] = "Total Emulation", + [PROF_MSH2 ] = "MSH2", + [PROF_68K ] = "68K", + [PROF_SCSP ] = "SCSP", + [PROF_SCU ] = "SCU", + [PROF_SSH2 ] = "SSH2", + [PROF_CDB ] = "CDB", + [PROF_CDIO ] = "CDIO", + [PROF_SMPC ] = "SMPC", + [PROF_hblankin ] = "hblankin", + [PROF_hblankout ] = "hblankout", + [PROF_VDP1_VDP2 ] = "VDP1/VDP2", + [PROF_vblankin ] = "vblankin", + [PROF__OVERHEAD ] = "(profiling overhead)", +}; + +/* Latest start time for each tracking slot */ +static uint32_t prof_start[PROF__END]; + +/* Total run time (microseconds) for each tracking slot */ +static uint64_t prof_total[PROF__END]; + +/* Number of calls for each tracking slot */ +static uint32_t prof_calls[PROF__END]; + +/* Approximate profiling overhead per call (microseconds) */ +static double prof_overhead = 0; +/* Profiling overhead per call that appears in slot totals (microseconds) */ +static double visible_overhead = 0; + +/*************************************************************************/ +/*************************************************************************/ + +/** + * psp_profile_reset: Reset all profiling counters. + * + * [Parameters] + * None + * [Return value] + * None + */ +void psp_profile_reset(void) +{ + memset(prof_start, 0, sizeof(prof_start)); + memset(prof_total, 0, sizeof(prof_total)); + memset(prof_calls, 0, sizeof(prof_calls)); +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_profile_start: Start the profile timer for the given slot with the + * given timestamp. + * + * [Parameters] + * slot: Tracking slot (0..PROF__END-1) + * timestamp: 32-bit system timestamp from sceKernelGetSystemTimeLow() + * [Return value] + * None + */ +__attribute__((noinline)) // To avoid throwing off overhead measurements +void psp_profile_start(int slot, uint32_t timestamp) +{ + /* Make sure this runs quickly, since it counts against the slot's run + * time */ + prof_start[slot] = timestamp; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_profile_stop: Stop the profile timer for the given slot, and update + * the call count and total run time. + * + * [Parameters] + * slot: Tracking slot (0..PROF__END-1) + * timestamp: 32-bit system timestamp from sceKernelGetSystemTimeLow() + * [Return value] + * None + * [Notes] + * The run time for a single call must not exceed 2^32-1 microseconds + * (about 71.5 minutes), or the profiling totals will be inaccurate. + */ +__attribute__((noinline)) +void psp_profile_stop(int slot, uint32_t timestamp) +{ + const uint32_t run_time = timestamp - prof_start[slot]; + prof_total[slot] += run_time; + prof_calls[slot]++; +} + +/*************************************************************************/ + +/** + * psp_profile_print: Print profiling statistics. Information for each slot + * with a nonzero call count is printed on one line, sorted in descending + * order by total run time. + * + * [Parameters] + * None + * [Return value] + * None + */ +void psp_profile_print(void) +{ + int order[PROF__END]; + double usec[PROF__END]; + int num_slots = 0; + int i, j; + + /* First calculate the approximate profiling overhead per call, if we + * haven't done so already */ + if (!prof_overhead) { + uint32_t start = sceKernelGetSystemTimeLow(); + for (i = 0; i < 1000; i++) { + psp_profile_start(PROF__OVERHEAD, sceKernelGetSystemTimeLow()); + psp_profile_stop(PROF__OVERHEAD, sceKernelGetSystemTimeLow()); + } + uint32_t end = sceKernelGetSystemTimeLow(); + prof_overhead = (end - start) / 1000.0; + + uint32_t visible_sum = 0; + for (i = 0; i < 1000; i++) { + uint32_t this_time = sceKernelGetSystemTimeLow(); + psp_profile_start(PROF__OVERHEAD, this_time); + visible_sum += sceKernelGetSystemTimeLow() - this_time; + } + visible_overhead = visible_sum / 1000.0; + } + + /* Find which slots need to be printed, and calculate their data along + * with total overhead */ + uint32_t total_calls = 0; + double total_overhead = 0; + for (i = 0; i < PROF__OVERHEAD; i++) { + if (prof_calls[i] > 0) { + order[num_slots++] = i; + if (i == PROF_Total_Emulation) { + /* We'll subtract the total overhead from this value once + * we know it */ + usec[i] = (double)prof_total[i]; + } else { + const double overhead = visible_overhead * prof_calls[i]; + usec[i] = (double)prof_total[i] - overhead; + } + } + total_calls += prof_calls[i]; + total_overhead += prof_overhead * prof_calls[i]; + } + /* Invisible overhead from the PROF_Total_Emulation slot isn't included + * in the first place, so don't try to subtract it out */ + total_overhead -= (prof_overhead - visible_overhead) + * prof_calls[PROF_Total_Emulation]; + usec[PROF_Total_Emulation] -= total_overhead; + + /* Sort the slots by total run time. There aren't many slots, so a + * selection sort will do fine */ + for (i = 0; i < num_slots-1; i++) { + int best = i; + for (j = i+1; j < num_slots; j++) { + if (usec[order[j]] > usec[order[best]]) { + best = j; + } + } + if (best != i) { + const int tmp = order[i]; + order[i] = order[best]; + order[best] = tmp; + } + } + + /* Print out the sorted info, adjusting for overhead */ + printf(" Calls Total (s) usec/call %% of max Name\n"); + const double max_usec = usec[order[0]]; + for (i = 0; i < num_slots; i++) { + const double sec = usec[order[i]] / 1000000.0; + const double avg = (sec / prof_calls[order[i]]) * 1000000.0; + const double pct= (usec[order[i]] / max_usec) * 100.0; + printf("%8u %9.3f %9.2f %5.1f%% %s\n", + prof_calls[order[i]], sec, avg, pct, prof_names[order[i]]); + } + printf("%8u %9.3f %9.2f %5.1f%% %s\n", + total_calls, total_overhead / 1000000.0, + (total_overhead / total_calls), + (total_overhead / max_usec) * 100.0, "(profiling overhead)"); +} + +/*************************************************************************/ +/*************************************************************************/ + +#endif // #ifdef SYS_PROFILE_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/profile.h b/yabause/src/psp/profile.h new file mode 100644 index 0000000000..3a0bc8460f --- /dev/null +++ b/yabause/src/psp/profile.h @@ -0,0 +1,106 @@ +/* src/psp/profile.h: Profiling header for Yabause core + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_PROFILE_H +#define PSP_PROFILE_H + +#include // for sceKernelGetSystemTimeLow() + +/*************************************************************************/ + +/* Tracking slots */ +enum { + PROF__UNKNOWN = 0, // For any non-matching name + PROF_Total_Emulation, + PROF_MSH2, + PROF_68K, + PROF_SCSP, + PROF_SCU, + PROF_SSH2, + PROF_CDB, + PROF_CDIO, + PROF_SMPC, + PROF_hblankin, + PROF_hblankout, + PROF_VDP1_VDP2, + PROF_vblankin, + PROF__OVERHEAD, // Internal use only (for calculating profiling overhead) + PROF__END +}; + +/*-----------------------------------------------------------------------*/ + +/* Helper macro that converts string parameters to PROFILE_{START,STOP}() + * into PROF_* indices. Note that as long as the parameter is a string + * literal, compiler optimization will convert the whole thing into a + * single compile-time constant. */ + +#define PROFILE_INDEX(name) \ + (strcmp((name),"Total Emulation") == 0 ? PROF_Total_Emulation : \ + strcmp((name),"MSH2" ) == 0 ? PROF_MSH2 : \ + strcmp((name),"68K" ) == 0 ? PROF_68K : \ + strcmp((name),"SCSP" ) == 0 ? PROF_SCSP : \ + strcmp((name),"SCU" ) == 0 ? PROF_SCU : \ + strcmp((name),"SSH2" ) == 0 ? PROF_SSH2 : \ + strcmp((name),"CDB" ) == 0 ? PROF_CDB : \ + strcmp((name),"CDIO" ) == 0 ? PROF_CDIO : \ + strcmp((name),"SMPC" ) == 0 ? PROF_SMPC : \ + strcmp((name),"hblankin" ) == 0 ? PROF_hblankin : \ + strcmp((name),"hblankout" ) == 0 ? PROF_hblankout : \ + strcmp((name),"VDP1/VDP2" ) == 0 ? PROF_VDP1_VDP2 : \ + strcmp((name),"vblankin" ) == 0 ? PROF_vblankin : \ + PROF__UNKNOWN) + +/*-----------------------------------------------------------------------*/ + +/* Macros called by the Yabause core. */ + +#define PROFILE_START(name) \ + psp_profile_start(PROFILE_INDEX((name)), sceKernelGetSystemTimeLow()) + +#define PROFILE_STOP(name) \ + psp_profile_stop(PROFILE_INDEX((name)), sceKernelGetSystemTimeLow()) + +#define PROFILE_PRINT() psp_profile_print() + +#define PROFILE_RESET() psp_profile_reset() + +/*-----------------------------------------------------------------------*/ + +/* Function declarations */ + +extern void psp_profile_reset(void); +extern void psp_profile_start(int slot, uint32_t timestamp); +extern void psp_profile_stop(int slot, uint32_t timestamp); +extern void psp_profile_print(void); + +/*************************************************************************/ + +#endif // PSP_PROFILE_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-cd.c b/yabause/src/psp/psp-cd.c new file mode 100644 index 0000000000..da56e64c64 --- /dev/null +++ b/yabause/src/psp/psp-cd.c @@ -0,0 +1,952 @@ +/* src/psp/psp-cd.c: PSP virtual CD interface module + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../cdbase.h" + +#include "psp-cd.h" +#include "sys.h" + +#ifdef SYS_PROFILE_H +# include "profile.h" // Can only be our own +#else +# define DONT_PROFILE +# include "../profile.h" +#endif + +/*************************************************************************/ +/************************* Interface definition **************************/ +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int psp_cd_init(const char *path); +static void psp_cd_deinit(void); +static int psp_cd_get_status(void); +static s32 psp_cd_read_toc(u32 *toc_buffer); +static int psp_cd_read_sector(u32 sector, void *buffer); +static void psp_cd_read_ahead(u32 sector); + +/*-----------------------------------------------------------------------*/ + +/* Module interface definition */ + +CDInterface CDPSP = { + .id = CDCORE_PSP, + .Name = "PSP Virtual CD Interface", + .Init = psp_cd_init, + .DeInit = psp_cd_deinit, + .GetStatus = psp_cd_get_status, + .ReadTOC = psp_cd_read_toc, + .ReadSectorFAD = psp_cd_read_sector, + .ReadAheadFAD = psp_cd_read_ahead, +}; + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* File descriptor for ISO image (0 = none open) */ +static int iso_fd; + +/* Table-of-contents data for ISO image */ +static uint32_t iso_toc[102]; + +/* Sector indices and corresponding file offsets for each track */ +static struct { + uint32_t first_sector; // First sector in track (0 = nonexistent track) + uint32_t last_sector; // Last sector in track (inclusive) + uint32_t file_offset; // File offset of first sector, in bytes + uint32_t sector_size; // Size of each sector in the track, in bytes +} tracks[99]; // tracks[0] = track 1, tracks[1] = track 2, etc. + +/*----------------------------------*/ + +/* Read-ahead buffers and sectors loaded into them. We read multiple + * sectors at once to minimize the overhead from system calls. */ + +#define READ_UNIT 8 // Number of sectors to read at once + +static __attribute__((aligned(16))) + uint8_t readahead_buffer[READ_UNIT*2][2352]; +static uint32_t readahead_sector[READ_UNIT*2]; + +/*----------------------------------*/ + +/* CD read thread ID */ +static int cd_read_thid; + +/* Variables for communication with CD read thread (see cd_read_thread() + * documentation for details) */ +static volatile uint8_t cd_read_idle; +static volatile uint8_t cd_read_requested; +static volatile uint8_t cd_read_terminate; +static volatile uint8_t cd_read_index; +static volatile uint32_t cd_read_sector; + +/*-----------------------------------------------------------------------*/ + +/* Local function declarations */ + +static int examine_iso(int fd); +static int examine_cue(int fd); +static int32_t msf_to_sector(const char *msf); + +static void cd_read_thread(void); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * psp_cd_init: Initialize the virtual CD interface. + * + * [Parameters] + * path: Pathname for physical device to associate with virtual CD drive + * [Return value] + * Zero on success, negative on error + */ +static int psp_cd_init(const char *path) +{ + if (!path || !*path) { + DMSG("No file given, behaving like an empty drive"); + goto error_return; + } + + /* Open the requested file. */ + iso_fd = sceIoOpen(path, PSP_O_RDONLY, 0); + if (iso_fd < 0) { + DMSG("Failed to open %s: %s", path, psp_strerror(iso_fd)); + goto error_return; + } + + /* Is it a CUE file? */ + char buf[4]; + if (sceIoRead(iso_fd, buf, 4) != 4) { + DMSG("Failed to read 4 bytes from %s", path); + goto error_close_iso; + } + const int is_cue = (memcmp(buf, "FILE", 4) == 0); + + /* Record information about the ISO image. */ + memset(iso_toc, 0xFF, sizeof(iso_toc)); + memset(tracks, 0, sizeof(tracks)); + if (is_cue) { + int new_fd = examine_cue(iso_fd); + if (new_fd) { + sceIoClose(iso_fd); + iso_fd = new_fd; + } else { + DMSG("Failed to parse CUE file %s", path); + goto error_close_iso; + } + } else { + if (!examine_iso(iso_fd)) { + DMSG("Failed to examine ISO file %s", path); + goto error_close_iso; + } + } + + /* Start up the CD-reading thread. */ + memset(readahead_sector, 0, sizeof(readahead_sector)); + cd_read_idle = 0; + cd_read_requested = 0; + cd_read_terminate = 0; + cd_read_sector = 0; + cd_read_index = 0; + cd_read_thid = sys_start_thread("YabauseCDReader", cd_read_thread, + THREADPRI_CD_READ, 0x1000, 0, NULL); + if (cd_read_thid < 0) { + DMSG("Failed to start CD reader thread: %s", + psp_strerror(cd_read_thid)); + goto error_close_iso; + } + + /* Success! */ + return 0; + + /* On error, we return success and behave like an empty drive. */ + error_close_iso: + sceIoClose(iso_fd); + error_return: + iso_fd = 0; + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_cd_deinit: Shut down the virtual CD interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_cd_deinit(void) +{ + if (iso_fd) { + cd_read_terminate = 1; + sceKernelWakeupThread(cd_read_thid); + unsigned int tries = 0; + while (!sys_delete_thread_if_stopped(cd_read_thid, NULL)) { + if (++tries > 100) { + DMSG("CD reader thread failed to terminate, killing it"); + sceKernelTerminateDeleteThread(cd_read_thid); + break; + } + sceKernelDelayThread(1000); // 1ms + } + cd_read_thid = 0; + sceIoClose(iso_fd); + iso_fd = 0; + } +} + +/*************************************************************************/ + +/** + * psp_cd_get_status: Return the status of the physical device associated + * with the virtual CD drive. + * + * [Parameters] + * None + * [Return value] + * 0: CD present, disc spinning + * 1: CD present, disc not spinning + * 2: CD not present + * 3: Tray open + */ +static int psp_cd_get_status(void) +{ + return iso_fd ? 0 : 2; +} + +/*************************************************************************/ + +/** + * psp_cd_read_toc: Return the TOC (table of contents) data for the disc + * currently inserted in the virtual CD drive. + * + * [Parameters] + * toc_buffer: Buffer into which TOC data is to be stored + * [Return value] + * Number of bytes returned + */ +static s32 psp_cd_read_toc(u32 *toc) +{ + if (!iso_fd) { + return 0; + } + + memcpy(toc, iso_toc, sizeof(iso_toc)); + return sizeof(iso_toc); +} + +/*************************************************************************/ + +/** + * psp_cd_read_sector: Read a 2352-byte raw sector from the virtual CD. + * + * [Parameters] + * sector: Linear sector number to read + * buffer: Buffer into which sector data is to be stored + * [Return value] + * Nonzero on success, zero on failure + */ +static int psp_cd_read_sector(u32 sector, void *buffer) +{ + if (!iso_fd) { + return 0; + } + + /* If the requested sector is already loaded, just return it. */ + unsigned int index; + for (index = 0; index < lenof(readahead_sector); index++) { + if (readahead_sector[index] == sector) { + memcpy(buffer, readahead_buffer[index], 2352); + return 1; + } + } + + /* The sector might have been in the middle of being read in, so wait + * for any pending read operation to complete and check again. */ + unsigned int tries = 0; + while (!cd_read_idle) { + if (++tries > 100) { + DMSG("Timeout waiting for CD reader to become idle"); + break; + } + sceKernelDelayThread(100); // 0.1ms + } + for (index = 0; index < lenof(readahead_sector); index++) { + if (readahead_sector[index] == sector) { + memcpy(buffer, readahead_buffer[index], 2352); + return 1; + } + } + + /* The sector isn't available after all, so start a new read request + * and wait for it to complete. */ + while (cd_read_requested) { + if (++tries > 100) { + DMSG("Timeout waiting for request"); + return 0; + } + sceKernelDelayThread(100); // 0.1ms + } + cd_read_sector = sector; + cd_read_index = 0; + cd_read_requested = 1; + sceKernelWakeupThread(cd_read_thid); + tries = 0; + while (cd_read_requested || !cd_read_idle) { + if (++tries > 1000) { + DMSG("Timeout waiting for read"); + return 0; + } + sceKernelDelayThread(100); // 0.1ms + } + + /* Ensure the sector was actually loaded, and return it. */ + if (readahead_sector[0] != sector) { + DMSG("Failed to read sector %u", (unsigned int)sector); + return 0; + } + memcpy(buffer, readahead_buffer[0], 2352); + return 1; +} + +/*************************************************************************/ + +/** + * psp_cd_read_ahead: Start an asynchronous read of the given sector from + * the virtual CD. + * + * [Parameters] + * sector: Linear sector number to read + * [Return value] + * 1 on success, 0 on failure + */ +static void psp_cd_read_ahead(u32 sector) +{ + if (!iso_fd) { + return; + } + + /* See whether the requested sector has already been read in. */ + unsigned int index; + for (index = 0; index < lenof(readahead_sector); index++) { + if (readahead_sector[index] == sector) { + break; + } + } + + uint32_t req_sector; // Sector to request + unsigned int req_index; // Target buffer index to request + + if (index < lenof(readahead_sector)) { + /* If the sector has already been read in, we normally don't need + * do anything. But if we're approaching the end of the current + * group of READ_UNIT sector buffers and the following sectors + * haven't yet been read in, start that read now. */ + if (index % READ_UNIT < READ_UNIT/2) { + return; + } + const unsigned int group_index = (index / READ_UNIT) * READ_UNIT; + const unsigned int next_group = + (group_index + READ_UNIT) % lenof(readahead_sector); + const uint32_t next_group_sector = + readahead_sector[group_index + (READ_UNIT-1)]; + if (!next_group_sector) { + return; // We must have reached the end of the track + } + if (readahead_sector[next_group] == next_group_sector) { + return; // Following sectors are already read in + } + req_sector = next_group_sector; + req_index = next_group; + } else { + /* The sector wasn't available, so start a new read operation. */ + req_sector = sector; + req_index = 0; + } + + /* If there's a pending read request, we can't do anything (so as not + * to block). */ + if (cd_read_requested) { + DMSG("Async read in progress, skipping readahead"); + return; + } + + /* Pass the requested sector and buffer index to the read thread. */ + cd_read_sector = req_sector; + cd_read_index = req_index; + cd_read_requested = 1; + sceKernelWakeupThread(cd_read_thid); +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * examine_iso: Examine a raw ISO image and fill in the iso_toc[] and + * tracks[] arrays with its information. + * + * [Parameters] + * fd: File descriptor for ISO image + * [Return value] + * Nonzero on success, zero on error + */ +static int examine_iso(int fd) +{ + /* Retrieve the size of the ISO image. */ + + const uint32_t file_size = sceIoLseek(fd, 0, PSP_SEEK_END); + if ((int32_t)file_size < 0) { + DMSG("Failed to retrieve ISO file size: %s", psp_strerror(file_size)); + return 0; + } else if (file_size == 0) { + DMSG("ISO file is empty!"); + return 0; + } + + /* Guess at the sector size based on the file size. */ + + unsigned int sector_size; + if (file_size % 2048 == 0 && file_size % 2352 == 0) { + /* It could be either 2048 or 2352 bytes per sector. Try and find + * an ISO filesystem header to tell which it is. */ + char buf[8]; + sceIoLseek(fd, 2048*16, PSP_SEEK_SET); + if (sceIoRead(fd, buf, 8) != 8) { + DMSG("Failed to read 8 bytes from offset 2048*16"); + return 0; + } + if (memcmp(buf, "\1CD001\1"/*\0*/, 8) == 0) { + sector_size = 2048; + } else { + sceIoLseek(fd, 2352*16+16, PSP_SEEK_SET); + if (sceIoRead(fd, buf, 8) != 8) { + DMSG("Failed to read 8 bytes from offset 2352*16+16"); + return 0; + } + if (memcmp(buf, "\1CD001\1"/*\0*/, 8) == 0) { + sector_size = 2352; + } else { + DMSG("Can't find an ISO9660 header, assuming 2048-byte" + " sectors"); + sector_size = 2048; + } + } + } else if (file_size % 2048 == 0) { + /* Not a multiple of 2352 bytes, so presumably 2048-byte sectors. */ + sector_size = 2048; + } else if (file_size % 2352 == 0) { + /* Not a multiple of 2048 bytes, so presumably 2352-byte sectors. */ + sector_size = 2352; + } else { + DMSG("Can't figure out sector size, assuming 2048-byte sectors"); + sector_size = 2048; + } + + /* Fill in the TOC and track table. */ + + const uint32_t num_sectors = file_size / sector_size; + + iso_toc[ 0] = 0x41000000 | 150; + iso_toc[ 99] = 0x41010000; + iso_toc[100] = 0x41010100; + iso_toc[101] = 0x41000000 | num_sectors; + + tracks[0].first_sector = 150; + tracks[0].last_sector = 150 + num_sectors - 1; + tracks[0].file_offset = 0; + tracks[0].sector_size = sector_size; + + /* All done! */ + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * examine_cue: Examine a CUE file, open the corresponding ISO image file, + * and fill in the iso_toc[] and tracks[] arrays with the image information. + * + * [Parameters] + * fd: File descriptor for CUE file + * [Return value] + * File descriptor for ISO image (nonzero) on success, zero on error + */ +static int examine_cue(int fd) +{ + /* Load the entire CUE file into memory. */ + + const uint32_t file_size = sceIoLseek(fd, 0, PSP_SEEK_END); + if ((int32_t)file_size < 0) { + DMSG("Failed to retrieve CUE file size: %s", psp_strerror(file_size)); + goto error_return; + } else if (file_size == 0) { + /* This should be impossible, since we read from the file to + * determine that it's a CUE file, but let's play it safe anyway... */ + DMSG("CUE file is empty!"); + goto error_return; + } else if (file_size > 1000000) { + /* Way too big to be a CUE file, so give up rather than trying to + * load tons of data into memory just to find out that it's bad. */ + DMSG("CUE file is too big! (%u bytes)", file_size); + goto error_return; + } + + char *cue_buffer = malloc(file_size); + if (!cue_buffer) { + DMSG("Failed to allocate buffer for CUE file (%u bytes)", file_size); + goto error_return; + } + sceIoLseek(fd, 0, PSP_SEEK_SET); + if (sceIoRead(fd, cue_buffer, file_size) != file_size) { + DMSG("Failed to read CUE file into memory"); + goto error_free_cue_buffer; + } + + /* The first line should be FILE "image-file" [...], giving the + * filename of the corresponding disc image. Strip any directory name + * from the image and attempt to open the file. */ + + char *s, *eol; + int new_fd; + uint32_t image_size; + + eol = cue_buffer + strcspn(cue_buffer, "\r\n"); + if (*eol) { + *eol++ = 0; + } + eol += strspn(eol, "\r\n"); + if (strncmp(cue_buffer, "FILE \"", 6) != 0) { + DMSG("Invalid CUE format: File does not begin with `FILE \"'"); + goto error_free_cue_buffer; + } + char *path = cue_buffer + 6; + s = strchr(path, '"'); + if (!s) { + DMSG("Invalid CUE format: FILE path missing closing quote"); + goto error_free_cue_buffer; + } + *s = 0; + + new_fd = sceIoOpen(path, PSP_O_RDONLY, 0); + if (new_fd < 0) { + DMSG("Failed to open image file %s: %s", path, psp_strerror(new_fd)); + goto error_free_cue_buffer; + } + + image_size = sceIoLseek(new_fd, 0, PSP_SEEK_END); + if ((int32_t)image_size < 0) { + DMSG("Failed to retrieve size of image file %s: %s", path, + psp_strerror(image_size)); + goto error_close_new_fd; + } + + /* Process each remaining line in the CUE file. */ + + int linenum = 1; + unsigned int current_track = 0; + uint32_t pregap_accum = 150; // Accumulated pregap sectors + + while (*eol) { + char *line = eol; + linenum++; + eol += strcspn(eol, "\r\n"); + if (*eol) { + *eol++ = 0; + } + eol += strspn(eol, "\r\n"); + + /* Get the tag, skipping leading spaces; if it's an empty line, + * just skip to the next one. */ + + line += strspn(line, " \t"); + if (!*line) { + continue; + } + char *tag = line; + line += strcspn(line, " \t"); + if (*line) { + *line++ = 0; + } + line += strspn(line, " \t"); + + /* Parse remaining parameters based on the tag type. */ + + if (strcmp(tag, "TRACK") == 0) { + + char *track_str = line; + line += strcspn(line, " \t"); + if (*line) { + *line++ = 0; + } + line += strspn(line, " \t"); + current_track = strtoul(track_str, &s, 10); + if (*s || current_track < 1 || current_track > 99) { + DMSG("Invalid CUE format (line %u): Bad track number %s", + linenum, track_str); + goto error_close_new_fd; + } + + char *mode = line; + if (strcmp(mode, "AUDIO") == 0) { + iso_toc[current_track-1] = 0x01000000; // Sector # comes later + tracks[current_track-1].sector_size = 2352; + } else if (strcmp(mode, "MODE1/2352") == 0 + || strcmp(mode, "MODE2/2352") == 0 + ) { + iso_toc[current_track-1] = 0x41000000; + tracks[current_track-1].sector_size = 2352; + } else if (strcmp(mode, "MODE1/2048") == 0 + || strcmp(mode, "MODE2/2048") == 0 + ) { + iso_toc[current_track-1] = 0x41000000; + tracks[current_track-1].sector_size = 2048; + } + + } else if (strcmp(tag, "INDEX") == 0) { + + if (!current_track) { + DMSG("Invalid CUE format (line %u): INDEX tag with no" + " current track", linenum); + goto error_close_new_fd; + } + + char *index_str = line; + line += strcspn(line, " \t"); + if (*line) { + *line++ = 0; + } + line += strspn(line, " \t"); + const unsigned int index = strtoul(index_str, &s, 10); + if (*s) { + DMSG("Invalid CUE format (line %u): Bad index number %s", + linenum, index_str); + goto error_close_new_fd; + } + if (index != 1) { + continue; // We only care about index #1 + } + + char *msf = line; + const int32_t sector = msf_to_sector(msf); + if (sector < 0) { + DMSG("Invalid CUE format (line %u): Bad MSF string %s", + linenum, msf); + goto error_close_new_fd; + } + + iso_toc[current_track-1] |= sector + pregap_accum; + tracks[current_track-1].first_sector = sector + pregap_accum; + /* Temporarily store the accumulated pregap in last_sector */ + tracks[current_track-1].last_sector = pregap_accum; + + } else if (strcmp(tag, "PREGAP") == 0) { + + if (!current_track) { + DMSG("Invalid CUE format (line %u): PREGAP tag with no" + " current track", linenum); + goto error_close_new_fd; + } + + char *msf = line; + const int32_t pregap_sectors = msf_to_sector(msf); + if (pregap_sectors < 0) { + DMSG("Invalid CUE format (line %u): Bad MSF string %s", + linenum, msf); + goto error_close_new_fd; + } + + pregap_accum += pregap_sectors; + + } else { + + /* Either an invalid tag or one we don't care about, so skip it. */ + + } + + } // while (*eol) + + if (!tracks[0].first_sector) { + DMSG("Invalid CUE file: Track 1 missing"); + goto error_close_new_fd; + } + + /* Fill in the remaining fields in the track table. */ + + if (tracks[0].first_sector != tracks[0].last_sector) { + DMSG("First track does not start at file offset 0, assuming sector" + " size %u for skipped sectors", tracks[0].sector_size); + const uint32_t track1_skipped = + tracks[0].first_sector - tracks[0].last_sector; + tracks[0].file_offset = track1_skipped * tracks[0].sector_size; + } else { + tracks[0].file_offset = 0; + } + + unsigned int track; + for (track = 2; track <= current_track; track++) { + if (!tracks[track-1].first_sector) { + DMSG("Invalid CUE file: Intermediate track %u missing", track); + goto error_close_new_fd; + } + const uint32_t pregap = tracks[track-1].last_sector; + const uint32_t added_pregap = pregap - tracks[track-2].last_sector; + tracks[track-2].last_sector = + tracks[track-1].first_sector - added_pregap - 1; + const uint32_t last_track_sectors = + tracks[track-2].last_sector - tracks[track-2].first_sector + 1; + tracks[track-1].file_offset = + tracks[track-2].file_offset + + (last_track_sectors * tracks[track-2].sector_size); + } + + const uint32_t last_track_size = image_size - tracks[track-2].file_offset; + const uint32_t last_track_sectors = + last_track_size / tracks[track-2].sector_size; + tracks[track-2].last_sector = + tracks[track-2].first_sector + last_track_sectors - 1; + + /* Generate the final TOC entries. */ + + iso_toc[ 99] = (iso_toc[0] & 0xFF000000) | 0x00010000; + iso_toc[100] = (iso_toc[current_track-1] & 0xFF000000) + | (current_track << 16); + iso_toc[101] = (iso_toc[current_track-1] & 0xFF000000) + | (tracks[current_track-1].last_sector + 1); + + /* All done! */ + + free(cue_buffer); + return new_fd; + + + /* Error handling. */ + + error_close_new_fd: + sceIoClose(new_fd); + error_free_cue_buffer: + free(cue_buffer); + error_return: + return 0; +} + +/*----------------------------------*/ + +/** + * msf_to_sector: Convert a time (Minutes:Seconds:Frames) string to the + * corresponding sector index. Helper function for examine_cue(). + * + * [Parameters] + * msf: Time string (MM:SS:FF) + * [Return value] + * Corresponding sector index (nonzero), or negative if string is invalid + */ +static int32_t msf_to_sector(const char *msf) +{ + uint32_t minutes, seconds, frames; + const char *s; + + if (!msf + || (minutes = strtoul(msf, (char **)&s, 10)) > 99 + || *s++ != ':' + || (seconds = strtoul(s, (char **)&s, 10)) > 59 + || *s++ != ':' + || (frames = strtoul(s, (char **)&s, 10)) > 74 + || *s != 0 + ) { + return -1; + } + + return (minutes*60 + seconds)*75 + frames; +} + +/*************************************************************************/ +/**************************** CD read thread *****************************/ +/*************************************************************************/ + +/** + * cd_read_thread: Thread which performs reads from the CD image file as + * requested by the main program. + * + * The main program should follow these steps to request a read: + * 1) Wait for cd_read_requested to become zero. + * 2) Store the desired sector address (150-...) in cd_read_sector. + * 3) Store the desired target readahead buffer index in cd_read_index. + * This value must be a multiple of READ_UNIT. + * 4) Store 1 in cd_read_requested. + * 5) Call sceKernelWakeupThread() to wake up the read thread. + * The main program can determine whether all pending requests have been + * completed by checking cd_read_idle for a nonzero value. + * + * Sectors which have been read in are stored in the read-ahead buffer; + * for any nonzero entry in readahead_sector[], the corresponding entry in + * readahead_buffer[] contains the raw data for that sector (converted, if + * necessary, from the format in the file--i.e., 2048-byte sectors in the + * file are filled out to 2352-byte sectors in readahead_buffer[]). + * + * To terminate the thread, the main program should store 1 in + * cd_read_terminate and wake up the thread. + * + * The file descriptor (iso_fd) and track table (tracks[]) must not be + * modified while the thread is running. + * + * [Parameters] + * None + * [Return value] + * Does not return + */ +static void cd_read_thread(void) +{ + PROFILE_START("CDIO"); + + while (!cd_read_terminate) { + + /* Wait for a trigger write from the main program. */ + while (!cd_read_requested && !cd_read_terminate) { + cd_read_idle = 1; + /* Since the read thread runs at a higher priority than the + * main program thread, there is (barring an OS bug) no danger + * of a race condition causing the read thread to be + * interrupted by the main thread between the while() test and + * this sleep call. */ + PROFILE_STOP("CDIO"); + sceKernelSleepThread(); + PROFILE_START("CDIO"); + } + if (cd_read_terminate) { + break; + } + cd_read_idle = 0; + + /* Save the requested sector number, then clear the request trigger. */ + const uint32_t sector = cd_read_sector; + const unsigned int index = cd_read_index; + cd_read_requested = 0; + + /* Figure out which track the sector is in and retrieve the track + * information. */ + unsigned int track; + for (track = 0; track < lenof(tracks); track++) { + if (sector >= tracks[track].first_sector + && sector <= tracks[track].last_sector + ) { + break; + } + } + if (track >= lenof(tracks)) { + DMSG("Failed to find track for sector %u", (unsigned int)sector); + continue; + } + const uint32_t first_sector = tracks[track].first_sector; + const uint32_t file_offset = tracks[track].file_offset; + const uint32_t sector_size = tracks[track].sector_size; + + /* Clear out all sector information previously stored in this group + * of buffers. */ + unsigned int i; + for (i = index; i < index + READ_UNIT; i++) { + readahead_sector[i] = 0; + } + + /* Check how many sectors we should read from this location (avoid + * reading beyond the end of the track). */ + unsigned int sectors_to_read = READ_UNIT; + if (sectors_to_read > (tracks[track].last_sector+1) - sector) { + sectors_to_read = (tracks[track].last_sector+1) - sector; + } + + /* Seek to the proper location in the disc image. */ + const uint32_t relative_sector = sector - first_sector; + const uint32_t relative_offset = relative_sector * sector_size; + const uint32_t absolute_offset = file_offset + relative_offset; + uint32_t res = sceIoLseek(iso_fd, absolute_offset, PSP_SEEK_SET); + if (res != absolute_offset) { + DMSG("sceIoLseek(%u, %u, SEEK_SET): %s", iso_fd, absolute_offset, + psp_strerror(res)); + continue; + } + + /* Read the sector data from the disc image. */ + if (sector_size == 2352) { + PROFILE_STOP("CDIO"); + res = sceIoRead(iso_fd, readahead_buffer[index], + 2352 * sectors_to_read); + PROFILE_START("CDIO"); + } else if (sector_size == 2048) { + static const uint8_t sector_header[16] = + {0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0}; + memcpy(readahead_buffer[index], sector_header, 16); + PROFILE_STOP("CDIO"); + res = sceIoRead(iso_fd, (uint8_t *)readahead_buffer[index] + 16, + 2048 * sectors_to_read); + PROFILE_START("CDIO"); + for (i = sectors_to_read-1; i >= 1; i--) { + /* We have to move the data first, or the sector header + * could overwrite part of the sector we're moving. */ + memmove((uint8_t *)readahead_buffer[index+i] + 16, + (uint8_t *)readahead_buffer[index] + 16 + 2048*i, + 2048); + memcpy(readahead_buffer[index+i], sector_header, 16); + } + } else { + DMSG("IMPOSSIBLE: bad sector_size %u for track %u", + sector_size, track); + continue; + } + if (res != sector_size * sectors_to_read) { + DMSG("sceIoRead(%u, buffer[%u], %u): %s", iso_fd, index, + sector_size, res < 0 ? psp_strerror(res) : "Short read"); + continue; + } + + /* Fill in readahead_sector[] with the newly-read-in sectors. */ + for (i = 0; i < sectors_to_read; i++) { + readahead_sector[index+i] = sector+i; + } + + } // while (!cd_read_terminate) + + PROFILE_STOP("CDIO"); + sceKernelExitThread(0); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-cd.h b/yabause/src/psp/psp-cd.h new file mode 100644 index 0000000000..fc801aac53 --- /dev/null +++ b/yabause/src/psp/psp-cd.h @@ -0,0 +1,46 @@ +/* src/psp/psp-cd.h: PSP virtual CD interface module header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_CD_H +#define PSP_CD_H + +#include "../cdbase.h" // for CDInterface + +/*************************************************************************/ + +/* Module interface definition */ +extern CDInterface CDPSP; + +/* Unique module ID (must be different from any in ../cdbase.h) */ +#define CDCORE_PSP 0x5CE // "SCE" + +/*************************************************************************/ + +#endif // PSP_CD_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-logo.png b/yabause/src/psp/psp-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c7af604addfa5dcca4c6f482e8e530d203fc7cf8 GIT binary patch literal 7371 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 = NULL; + local_malloc_base->prev = NULL; + local_malloc_base->size = size - sizeof(BlockInfo); + local_malloc_base->free = 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * local_malloc: Allocate a block of local memory. + * + * [Parameters] + * size: Size of memory block to allocate + * [Return value] + * Allocated memory block, or NULL on failure + */ +static void *local_malloc(size_t size) +{ + /* Round the size up to a multiple of sizeof(BlockInfo) for simplicity */ + size = (size + sizeof(BlockInfo)-1) + / sizeof(BlockInfo) * sizeof(BlockInfo); + + /* Find a free block of sufficient size and return it, splitting the + * block if appropriate */ + BlockInfo *block; + for (block = local_malloc_base; block; block = block->next) { + if (!block->free) { + continue; + } + if (block->size >= size) { + void *ptr = (void *)((uintptr_t)block + sizeof(BlockInfo)); + if (block->size > size) { + BlockInfo *split_block = (BlockInfo *)((uintptr_t)ptr + size); + split_block->next = block->next; + if (split_block->next) { + split_block->next->prev = split_block; + } + split_block->prev = block; + block->next = split_block; + split_block->size = block->size - (size + sizeof(BlockInfo)); + split_block->free = 1; + } + block->size = size; + block->free = 0; + return ptr; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * local_realloc: Adjust the size of a block of local memory. + * + * [Parameters] + * ptr: Pointer to memory block to adjust + * size: New size of memory block + * [Return value] + * Allocated memory block, or NULL on failure + */ +static void *local_realloc(void *ptr, size_t size) +{ + if (size == 0) { + local_free(ptr); + return NULL; + } + + if (ptr == NULL) { + return local_malloc(size); + } + + BlockInfo *block = (BlockInfo *)((uintptr_t)ptr - sizeof(BlockInfo)); + const size_t oldsize = block->size; + size = (size + sizeof(BlockInfo)-1) + / sizeof(BlockInfo) * sizeof(BlockInfo); + if (size < oldsize - sizeof(BlockInfo)) { + /* Adjust the block size downward and split off the remaining space + * into a new free block (or add it to the next block, if the next + * block is a free block) */ + block->size = size; + BlockInfo *newblock = (BlockInfo *) + ((uintptr_t)block + sizeof(BlockInfo) + size); + if (block->next && block->next->free) { + newblock->next = block->next->next; + newblock->prev = block; + newblock->size = block->next->size; + newblock->free = 1; + if (newblock->next) { + newblock->next->prev = newblock; + } + block->next = newblock; + newblock->size += oldsize - size; + } else { + newblock->next = block->next; + newblock->prev = block; + if (block->next) { + block->next->prev = newblock; + } + block->next = newblock; + newblock->size = oldsize - size - sizeof(BlockInfo); + newblock->free = 1; + } + return ptr; + } else if (size <= sizeof(BlockInfo)) { + /* No need to adjust anything; just return the same block */ + return ptr; + } else if (block->next && block->next->free + && (sizeof(BlockInfo) + block->next->size) >= size - oldsize) { + /* Append the next block to this one, then resize downward with a + * recursive call */ + block->size += sizeof(BlockInfo) + block->next->size; + block->next = block->next->next; + if (block->next) { + block->next->prev = block; + } + return local_realloc(ptr, size); + } else { + /* No simple path, so alloc/copy/free */ + void *newptr = local_malloc(size); + if (!newptr) { + return NULL; + } + const size_t copysize = (oldsize < size) ? oldsize : size; + memcpy(newptr, ptr, copysize); + local_free(ptr); + return newptr; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * local_free: Free a block of local memory. + * + * [Parameters] + * ptr: Pointer to memory block to free + * [Return value] + * None + */ +static void local_free(void *ptr) +{ + if (ptr != NULL) { + BlockInfo *block = (BlockInfo *)((uintptr_t)ptr - sizeof(BlockInfo)); + block->free = 1; + if (block->prev && block->prev->free) { + block->prev->next = block->next; + if (block->next) { + block->next->prev = block->prev; + } + block->prev->size += block->size + sizeof(BlockInfo); + block = block->prev; + } + if (block->next && block->next->free) { + block->size += block->next->size + sizeof(BlockInfo); + block->next = block->next->next; + if (block->next) { + block->next->prev = block; + } + } + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-m68k.h b/yabause/src/psp/psp-m68k.h new file mode 100644 index 0000000000..86662b676b --- /dev/null +++ b/yabause/src/psp/psp-m68k.h @@ -0,0 +1,46 @@ +/* src/psp/psp-m68k.h: PSP M68k emulator interface module header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_M68K_H +#define PSP_M68K_H + +#include "../m68kcore.h" // for M68K_struct + +/*************************************************************************/ + +/* Module interface definition */ +extern M68K_struct M68KPSP; + +/* Unique module ID (must be different from any in ../m68kcore.h) */ +#define M68KCORE_PSP 0x5CE // "SCE" + +/*************************************************************************/ + +#endif // PSP_M68K_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-per.c b/yabause/src/psp/psp-per.c new file mode 100644 index 0000000000..8560e7816a --- /dev/null +++ b/yabause/src/psp/psp-per.c @@ -0,0 +1,162 @@ +/* src/psp/psp-per.c: PSP peripheral interface module + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../peripheral.h" + +#include "control.h" +#include "psp-per.h" + +/*************************************************************************/ +/************************* Interface definition **************************/ +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int psp_per_init(void); +static void psp_per_deinit(void); +static int psp_per_handle_events(void); +#ifdef PERKEYNAME +static void psp_per_key_name(u32 key, char *name, int size); +#endif + +/*-----------------------------------------------------------------------*/ + +/* Module interface definition */ + +PerInterface_struct PERPSP = { + .id = PERCORE_PSP, + .Name = "PSP Peripheral Interface", + .Init = psp_per_init, + .DeInit = psp_per_deinit, + .HandleEvents = psp_per_handle_events, + .canScan = 0, +#ifdef PERKEYNAME + .KeyName = psp_per_key_name, +#endif +}; + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * psp_per_init: Initialize the peripheral interface. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_per_init(void) +{ + /* Nothing to do */ + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_per_deinit: Shut down the peripheral interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_per_deinit(void) +{ + /* Nothing to do */ +} + +/*************************************************************************/ + +/** + * psp_per_handle_events: Process pending peripheral events, and run one + * iteration of the emulation. + * + * For the PSP, the main loop is located in main.c; we only update the + * current button status here. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_per_handle_events(void) +{ + static uint32_t last_buttons; + + const uint32_t buttons = control_state(); + const uint32_t changed_buttons = buttons ^ last_buttons; + last_buttons = buttons; + + int i; + for (i = 0; i < 16; i++) { + const uint32_t button = 1 << i; + if (changed_buttons & button) { + if (buttons & button) { + PerKeyDown(button); + } else { + PerKeyUp(button); + } + } + } + + YabauseExec(); + return 0; +} + +/*************************************************************************/ + +#ifdef PERKEYNAME + +/** + * psp_per_key_name: Return the name corresponding to a system-dependent + * key value. + * + * [Parameters] + * key: Key value to return name for + * name: Buffer into which name is to be stored + * size: Size of buffer in bytes + * [Return value] + * None + */ +static void psp_per_key_name(u32 key, char *name, int size) +{ + /* Not supported on PSP */ + *name = 0; +} + +#endif // PERKEYNAME + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-per.h b/yabause/src/psp/psp-per.h new file mode 100644 index 0000000000..fa274574b6 --- /dev/null +++ b/yabause/src/psp/psp-per.h @@ -0,0 +1,46 @@ +/* src/psp/psp-per.h: PSP peripheral interface module header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_PER_H +#define PSP_PER_H + +#include "../peripheral.h" // for PerInterface_struct + +/*************************************************************************/ + +/* Module interface definition */ +extern PerInterface_struct PERPSP; + +/* Unique module ID (must be different from any in ../peripheral.h) */ +#define PERCORE_PSP 0x5CE // "SCE" + +/*************************************************************************/ + +#endif // PSP_PER_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-sh2.c b/yabause/src/psp/psp-sh2.c new file mode 100644 index 0000000000..ac972cde01 --- /dev/null +++ b/yabause/src/psp/psp-sh2.c @@ -0,0 +1,670 @@ +/* src/psp/psp-sh2.c: Yabause interface for PSP SH-2 emulator + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../bios.h" +#include "../error.h" +#include "../memory.h" +#include "../sh2core.h" +#include "../sh2trace.h" +#include "../vdp1.h" +#include "../vdp2.h" + +#include "config.h" +#include "psp-sh2.h" +#include "rtl.h" +#include "sh2.h" +#include "sh2-internal.h" // For TRACE, etc. (so we don't call sh2_trace_add_cycles() if not needed) + +#include "satopt-sh2.h" + +/*************************************************************************/ +/************************* Interface definition **************************/ +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int psp_sh2_init(void); +static void psp_sh2_deinit(void); +static void psp_sh2_reset(void); +static FASTCALL void psp_sh2_exec(SH2_struct *state, u32 cycles); + +static void psp_sh2_get_registers(SH2_struct *yabause_state, + sh2regs_struct *regs); +static u32 psp_sh2_get_GPR(SH2_struct *yabause_state, int num); +static u32 psp_sh2_get_SR(SH2_struct *yabause_state); +static u32 psp_sh2_get_GBR(SH2_struct *yabause_state); +static u32 psp_sh2_get_VBR(SH2_struct *yabause_state); +static u32 psp_sh2_get_MACH(SH2_struct *yabause_state); +static u32 psp_sh2_get_MACL(SH2_struct *yabause_state); +static u32 psp_sh2_get_PR(SH2_struct *yabause_state); +static u32 psp_sh2_get_PC(SH2_struct *yabause_state); + +static void psp_sh2_set_registers(SH2_struct *yabause_state, + const sh2regs_struct *regs); +static void psp_sh2_set_GPR(SH2_struct *yabause_state, int num, u32 value); +static void psp_sh2_set_SR(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_GBR(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_VBR(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_MACH(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_MACL(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_PR(SH2_struct *yabause_state, u32 value); +static void psp_sh2_set_PC(SH2_struct *yabause_state, u32 value); + +static void psp_sh2_send_interrupt(SH2_struct *yabause_state, + u8 vector, u8 level); +static int psp_sh2_get_interrupts(SH2_struct *yabause_state, + interrupt_struct interrupts[MAX_INTERRUPTS]); +static void psp_sh2_set_interrupts(SH2_struct *yabause_state, + int num_interrupts, + const interrupt_struct interrupts[MAX_INTERRUPTS]); + +static void psp_sh2_write_notify(u32 start, u32 length); + + +/* Module interface definition */ + +SH2Interface_struct SH2PSP = { + .id = SH2CORE_PSP, + .Name = "PSP SH-2 Core", + + .Init = psp_sh2_init, + .DeInit = psp_sh2_deinit, + .Reset = psp_sh2_reset, + .Exec = psp_sh2_exec, + + .GetRegisters = psp_sh2_get_registers, + .GetGPR = psp_sh2_get_GPR, + .GetSR = psp_sh2_get_SR, + .GetGBR = psp_sh2_get_GBR, + .GetVBR = psp_sh2_get_VBR, + .GetMACH = psp_sh2_get_MACH, + .GetMACL = psp_sh2_get_MACL, + .GetPR = psp_sh2_get_PR, + .GetPC = psp_sh2_get_PC, + + .SetRegisters = psp_sh2_set_registers, + .SetGPR = psp_sh2_set_GPR, + .SetSR = psp_sh2_set_SR, + .SetGBR = psp_sh2_set_GBR, + .SetVBR = psp_sh2_set_VBR, + .SetMACH = psp_sh2_set_MACH, + .SetMACL = psp_sh2_set_MACL, + .SetPR = psp_sh2_set_PR, + .SetPC = psp_sh2_set_PC, + + .SendInterrupt = psp_sh2_send_interrupt, + .GetInterrupts = psp_sh2_get_interrupts, + .SetInterrupts = psp_sh2_set_interrupts, + + .WriteNotify = psp_sh2_write_notify, +}; + +/*************************************************************************/ +/************************ Local data definitions *************************/ +/*************************************************************************/ + +/* Master and slave SH-2 state blocks */ +static SH2State *master_SH2, *slave_SH2; + +/* Write buffers for low and high RAM */ +static void *write_buffer_lram, *write_buffer_hram; + +/*-----------------------------------------------------------------------*/ + +/* Local function declarations */ +static void flush_caches(void *start, uint32_t length); +static void invalid_opcode_handler(SH2State *state, uint32_t PC, + uint16_t opcode); +static FASTCALL void trace_insn_handler(SH2State *state, uint32_t address); + +/*************************************************************************/ +/********************** External interface routines **********************/ +/*************************************************************************/ + +/** + * psp_sh2_init: Initialize the SH-2 core. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_sh2_init(void) +{ + master_SH2 = sh2_create(); + slave_SH2 = sh2_create(); + if (!master_SH2 || !slave_SH2) { + return -1; + } + master_SH2->userdata = MSH2; + slave_SH2->userdata = SSH2; + + write_buffer_lram = sh2_alloc_direct_write_buffer(0x100000); + write_buffer_hram = sh2_alloc_direct_write_buffer(0x100000); + if (UNLIKELY(!write_buffer_lram) || UNLIKELY(!write_buffer_hram)) { + DMSG("WARNING: Failed to allocate RAM write buffers, performance" + " will suffer"); + } + + if (UNLIKELY(!sh2_init())) { + return -1; + } +#ifdef PSP + sh2_set_optimizations(config_get_sh2_optimizations()); + /* If we can allocate >24MB of memory, we must be on a PSP-2000 (Slim) + * or newer model; otherwise, assume we're on a PSP-1000 (Phat) and + * reduce the JIT data size limit to avoid crowding out other data. */ + void *memsize_test = malloc(24*1024*1024 + 1); + if (memsize_test) { + free(memsize_test); + sh2_set_jit_data_limit(20*1000000); + } else { + sh2_set_jit_data_limit(8*1000000); + } +#else // !PSP + sh2_set_optimizations(SH2_OPTIMIZE_ASSUME_SAFE_DIVISION + | SH2_OPTIMIZE_BRANCH_TO_RTS + | SH2_OPTIMIZE_FOLD_SUBROUTINES + | SH2_OPTIMIZE_LOCAL_ACCESSES + | SH2_OPTIMIZE_LOCAL_POINTERS + | SH2_OPTIMIZE_MAC_NOSAT + | SH2_OPTIMIZE_POINTERS + | SH2_OPTIMIZE_POINTERS_MAC + | SH2_OPTIMIZE_STACK); + /* Give the SH-2 core some breathing room for saving RTL blocks */ + sh2_set_jit_data_limit(200*1000000); +#endif + sh2_set_manual_optimization_callback(saturn_optimize_sh2); + sh2_set_cache_flush_callback(flush_caches); + sh2_set_invalid_opcode_callback(invalid_opcode_handler); + sh2_set_trace_insn_callback(trace_insn_handler); + sh2_set_trace_storeb_callback(sh2_trace_writeb); + sh2_set_trace_storew_callback(sh2_trace_writew); + sh2_set_trace_storel_callback(sh2_trace_writel); + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sh2_deinit: Perform cleanup for the SH-2 core. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_sh2_deinit(void) +{ + sh2_destroy(master_SH2); + sh2_destroy(slave_SH2); + + free(write_buffer_lram); + free(write_buffer_hram); + write_buffer_lram = write_buffer_hram = NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sh2_reset: Reset the SH-2 core. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_sh2_reset(void) +{ + /* Sanity checks on pointers -- none of these are actually possible + * on the PSP, but it couldn't hurt to have them for testing this code + * on other platforms. */ + if ((uintptr_t)BiosRom == 0x00000000UL + || (uintptr_t)BiosRom == 0x00080000UL + || (uintptr_t)BiosRom == 0x20000000UL + || (uintptr_t)BiosRom == 0x20080000UL + || (uintptr_t)BiosRom == 0xA0000000UL + || (uintptr_t)BiosRom == 0xA0080000UL + || (uintptr_t)LowWram == 0x00200000UL + || (uintptr_t)LowWram == 0x20200000UL + || (uintptr_t)LowWram == 0xA0200000UL + || ((uintptr_t)HighWram & 0xFE0FFFFF) == 0x06000000UL + || ((uintptr_t)HighWram & 0xFE0FFFFF) == 0x26000000UL + || ((uintptr_t)HighWram & 0xFE0FFFFF) == 0xA6000000UL + ) { + DMSG("WARNING: ROM/RAM located at an inconvenient place;" + " performance will suffer!\nROM=%p LRAM=%p HRAM=%p", + BiosRom, LowWram, HighWram); + } + +#define SET_PAGE(sh2_addr,psp_addr,size,writebuf) do { \ + if (!(psp_addr)) { \ + DMSG("WARNING: %s == NULL", #psp_addr); \ + } else { \ + sh2_set_direct_access((sh2_addr) | 0x00000000, (psp_addr), \ + (size), 1, (writebuf)); \ + sh2_set_direct_access((sh2_addr) | 0x20000000, (psp_addr), \ + (size), 1, (writebuf)); \ + sh2_set_direct_access((sh2_addr) | 0xA0000000, (psp_addr), \ + (size), 1, (writebuf)); \ + } \ +} while (0) +#define SET_EXEC_PAGE(sh2_addr,psp_addr,size) do { \ + if (!(psp_addr)) { \ + DMSG("WARNING: %s == NULL", #psp_addr); \ + } else { \ + sh2_set_direct_access((sh2_addr) | 0x00000000, (psp_addr), \ + (size), 0, 0); \ + sh2_set_direct_access((sh2_addr) | 0x20000000, (psp_addr), \ + (size), 0, 0); \ + sh2_set_direct_access((sh2_addr) | 0xA0000000, (psp_addr), \ + (size), 0, 0); \ + } \ +} while (0) +#define SET_BYTE_PAGE(sh2_addr,psp_addr,size) do { \ + if (!(psp_addr)) { \ + DMSG("WARNING: %s == NULL", #psp_addr); \ + } else { \ + sh2_set_byte_direct_access((sh2_addr) | 0x00000000, (psp_addr), \ + (size)); \ + sh2_set_byte_direct_access((sh2_addr) | 0x20000000, (psp_addr), \ + (size)); \ + sh2_set_byte_direct_access((sh2_addr) | 0xA0000000, (psp_addr), \ + (size)); \ + } \ +} while (0) + + SET_EXEC_PAGE(0x00000000, BiosRom, 0x80000); + SET_EXEC_PAGE(0x00080000, BiosRom, 0x80000); + if (write_buffer_lram) { + SET_PAGE(0x00200000, LowWram, 0x100000, write_buffer_lram); + } + if (write_buffer_hram) { + uint32_t base; + for (base = 0x06000000; base < 0x08000000; base += 0x00100000) { + SET_PAGE(base, HighWram, 0x100000, write_buffer_hram); + } + } + SET_BYTE_PAGE(0x05C00000, Vdp1Ram, 0x80000); + SET_BYTE_PAGE(0x05E00000, Vdp2Ram, 0x80000); + SET_BYTE_PAGE(0x05E80000, Vdp2Ram, 0x80000); + +#undef SET_PAGE +#undef SET_EXEC_PAGE +#undef SET_BYTE_PAGE + + sh2_reset(master_SH2); + sh2_reset(slave_SH2); +} + +/*************************************************************************/ + +/** + * psp_sh2_exec: Execute instructions for the given number of clock cycles. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * cycles: Number of clock cycles to execute + * [Return value] + * None + */ +static FASTCALL void psp_sh2_exec(SH2_struct *yabause_state, u32 cycles) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + + state->cycles = yabause_state->cycles; + +#if defined(TRACE) || defined(TRACE_STEALTH) || defined(TRACE_LITE) + /* Avoid accumulating leftover cycles multiple times, since the trace + * code automatically adds state->cycles to the cycle accumulator when + * printing a trace line */ + sh2_trace_add_cycles(-(state->cycles)); +#endif + sh2_run(state, cycles); +#if defined(TRACE) || defined(TRACE_STEALTH) || defined(TRACE_LITE) + sh2_trace_add_cycles(state->cycles); +#endif + + yabause_state->cycles = state->cycles; +} + +/*************************************************************************/ + +/** + * psp_sh2_get_registers: Retrieve the values of all SH-2 registers. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * regs: Structure to receive register values + * [Return value] + * None + */ +static void psp_sh2_get_registers(SH2_struct *yabause_state, + sh2regs_struct *regs) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + memcpy(regs, state, sizeof(*regs)); +} + +/*----------------------------------*/ + +/** + * psp_sh2_get_{GPR,SR,GBR,VBR,MACH,MACL,PR,PC}: Return the value of the + * named register. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * num: General purpose register number to get (get_GPR() only) + * [Return value] + * Register's value + */ +static u32 psp_sh2_get_GPR(SH2_struct *yabause_state, int num) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->R[num]; +} + +static u32 psp_sh2_get_SR(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->SR; +} + +static u32 psp_sh2_get_GBR(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->GBR; +} + +static u32 psp_sh2_get_VBR(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->VBR; +} + +static u32 psp_sh2_get_MACH(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->MACH; +} + +static u32 psp_sh2_get_MACL(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->MACL; +} + +static u32 psp_sh2_get_PR(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->PR; +} + +static u32 psp_sh2_get_PC(SH2_struct *yabause_state) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + return state->PC; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sh2_set_registers: Set the values of all SH-2 registers. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * regs: Structure containing new values for registers + * [Return value] + * None + */ +static void psp_sh2_set_registers(SH2_struct *yabause_state, + const sh2regs_struct *regs) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + memcpy(state, regs, sizeof(*regs)); +} + +/*----------------------------------*/ + +/** + * psp_sh2_set_{GPR,SR,GBR,VBR,MACH,MACL,PR,PC}: Set the value of the + * named register. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * num: General purpose register number to get (get_GPR() only) + * value: New value for register + * [Return value] + * None + */ +static void psp_sh2_set_GPR(SH2_struct *yabause_state, int num, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->R[num] = value; +} + +static void psp_sh2_set_SR(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->SR = value; +} + +static void psp_sh2_set_GBR(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->GBR = value; +} + +static void psp_sh2_set_VBR(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->VBR = value; +} + +static void psp_sh2_set_MACH(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->MACH = value; +} + +static void psp_sh2_set_MACL(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->MACL = value; +} + +static void psp_sh2_set_PR(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->PR = value; +} + +static void psp_sh2_set_PC(SH2_struct *yabause_state, u32 value) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->PC = value; +} + +/*************************************************************************/ + +/** + * psp_sh2_send_interrupt: Send an interrupt to the given SH-2 processor. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * [Return value] + * None + */ +static void psp_sh2_send_interrupt(SH2_struct *yabause_state, + u8 vector, u8 level) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + if (UNLIKELY(vector > 127)) { + return; + } + sh2_signal_interrupt(state, vector, level); +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sh2_set_interrupts: Set the state of the interrupt stack. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * interrupts: Array to receive interrupt data + * [Return value] + * Number of pending interrupts + */ +static int psp_sh2_get_interrupts(SH2_struct *yabause_state, + interrupt_struct interrupts[MAX_INTERRUPTS]) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + int i; + for (i = 0; i < state->interrupt_stack_top && i < MAX_INTERRUPTS; i++) { + interrupts[i].level = state->interrupt_stack[i].level; + interrupts[i].vector = state->interrupt_stack[i].vector; + } + return state->interrupt_stack_top; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sh2_set_interrupts: Set the state of the interrupt stack. + * + * [Parameters] + * yabause_state: Yabause SH-2 context structure + * num_interrupts: Number of pending interrupts + * interrupts: Array of pending interrupts + * [Return value] + * None + */ +static void psp_sh2_set_interrupts(SH2_struct *yabause_state, + int num_interrupts, + const interrupt_struct interrupts[MAX_INTERRUPTS]) +{ + SH2State *state = (yabause_state == MSH2) ? master_SH2 : slave_SH2; + state->interrupt_stack_top = 0; + int i; + for (i = 0; i < num_interrupts; i++) { + if (UNLIKELY(interrupts[i].vector > 127)) { + return; + } + sh2_signal_interrupt(state, interrupts[i].vector, interrupts[i].level); + } +} + +/*************************************************************************/ + +/** + * psp_sh2_write_notify: Called when an external agent modifies memory. + * + * [Parameters] + * address: Beginning of address range to which data was written + * size: Size of address range to which data was written (in bytes) + * [Return value] + * None + */ +static void psp_sh2_write_notify(u32 address, u32 size) +{ + sh2_write_notify(address, size); +} + +/*************************************************************************/ +/**************************** Local functions ****************************/ +/*************************************************************************/ + +/** + * flush_caches: Callback function to flush the native CPU's caches. + * + * [Parameters] + * start: Pointer to start of range + * length: Length of range in bytes + * [Return value] + * None + */ +static void flush_caches(void *start, uint32_t length) +{ +#ifdef PSP // Protect so we can test this SH-2 core on other platforms + sceKernelDcacheWritebackInvalidateRange(start, length); + sceKernelIcacheInvalidateRange(start, length); +#endif +} + +/*-----------------------------------------------------------------------*/ + +/** + * invalid_opcode_handler: Callback function for invalid opcodes detected + * in the instruction stream. + * + * [Parameters] + * state: Processor state block pointer + * PC: PC at which the invalid instruction was found + * opcode: The invalid opcode itself + * [Return value] + * None + */ +static void invalid_opcode_handler(SH2State *state, uint32_t PC, + uint16_t opcode) +{ + SH2_struct *yabause_state = (SH2_struct *)(state->userdata); + uint32_t saved_PC = state->PC; + state->PC = PC; // Show the proper PC in the error message + yabause_state->instruction = opcode; + YabSetError(YAB_ERR_SH2INVALIDOPCODE, yabause_state); + state->PC = saved_PC; +} + +/*-----------------------------------------------------------------------*/ + +/** + * trace_insn_handler: Callback function for tracing instructions. + * Updates the appropriate Yabause SH2_struct's registers and cycle count, + * then calls out to the common SH-2 tracing functionality. + * + * [Parameters] + * state: Processor state block pointer + * address: Address of instruction to trace + * [Return value] + * None + */ +static FASTCALL void trace_insn_handler(SH2State *state, uint32_t address) +{ + SH2_struct *yabause_state = (SH2_struct *)(state->userdata); + yabause_state->cycles = state->cycles; + sh2_trace(yabause_state, address); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-sh2.h b/yabause/src/psp/psp-sh2.h new file mode 100644 index 0000000000..e69dbddaf0 --- /dev/null +++ b/yabause/src/psp/psp-sh2.h @@ -0,0 +1,46 @@ +/* src/psp/psp-sh2.h: Header for SH-2 emulator for PSP + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_SH2_H +#define PSP_SH2_H + +#include "../sh2core.h" // for SH2Interface_struct + +/*************************************************************************/ + +/* Module interface definition */ +extern SH2Interface_struct SH2PSP; + +/* Unique module ID (must be different from any in ../sh2{core,int}.h) */ +#define SH2CORE_PSP 0x5CE // "SCE" + +/*************************************************************************/ + +#endif // PSP_SH2_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-sound.c b/yabause/src/psp/psp-sound.c new file mode 100644 index 0000000000..c01982266a --- /dev/null +++ b/yabause/src/psp/psp-sound.c @@ -0,0 +1,720 @@ +/* src/psp/psp-sound.c: PSP sound output module + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../memory.h" +#include "../scsp.h" + +#include "config.h" +#include "me.h" +#include "me-utility.h" +#include "sys.h" +#include "psp-sound.h" + +/* Macro for uncached access to variables / structure fields */ +#define UNCACHED(var) (*((typeof(&var))((uint32_t)(&var) | 0x40000000))) + +/*************************************************************************/ +/************************* Configuration options *************************/ +/*************************************************************************/ + +/** + * DUMP_AUDIO: When defined, the program will write a dump of all audio + * data sent to the hardware (except filler data sent when the emulator + * falls behind real time) to "audio.pcm" in the current directory. + */ +// #define DUMP_AUDIO + +/*************************************************************************/ +/************************* Interface definition **************************/ +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int psp_sound_init(void); +static void psp_sound_deinit(void); +static int psp_sound_reset(void); +static int psp_sound_change_video_format(int vertfreq); +static void psp_sound_update_audio(u32 *leftchanbuffer, u32 *rightchanbuffer, + u32 num_samples); +static u32 psp_sound_get_audio_space(void); +static void psp_sound_mute_audio(void); +static void psp_sound_unmute_audio(void); +static void psp_sound_set_volume(int volume); + +/*-----------------------------------------------------------------------*/ + +/* Module interface definition */ + +SoundInterface_struct SNDPSP = { + .id = SNDCORE_PSP, + .Name = "PSP Sound Interface", + .Init = psp_sound_init, + .DeInit = psp_sound_deinit, + .Reset = psp_sound_reset, + .ChangeVideoFormat = psp_sound_change_video_format, + .UpdateAudio = psp_sound_update_audio, + .GetAudioSpace = psp_sound_get_audio_space, + .MuteAudio = psp_sound_mute_audio, + .UnMuteAudio = psp_sound_unmute_audio, + .SetVolume = psp_sound_set_volume, +}; + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Playback rate in Hz (unchangeable) */ +#define PLAYBACK_RATE 44100 + +/* Playback buffer size in samples (larger values = less chance of skipping + * but greater lag) */ +#define BUFFER_SIZE 256 + +/* Number of BUFFER_SIZE-sized audio buffers in the playback buffer (see + * description below) */ +#define NUM_BUFFERS 8 + +/* + * Playback buffer descriptor, implementing a lockless ring buffer with the + * following semantics: + * WRITER (main program): + * (1) Waits for .write_ready[.next_write] to become nonzero. + * (2) Writes BUFFER_SIZE samples of data into .buffer[.next_write]. + * (3) Sets .write_ready[.next_write] to zero. + * (4) Increments .next_write to point to the next audio buffer. + * READER (playback thread): + * (1) Waits for .write_ready[.next_play] to become zero. + * (2) Submits .buffer[.next_play] to the OS (which blocks until the + * previous buffer finishes playing). + * (3) Sets .write_ready[.cur_play] to nonzero. + * (4) Sets .cur_play to .next_play. + * (5) Increments .next_play to point to the next audio buffer. + * + * Note that at least three audio buffers are required in the ring buffer: + * - One currently being played by the hardware. + * - One queued for playback by the hardware. + * - One into which the main program is writing. + * A minimum of at least four buffers is recommended to allow overflow + * room, since the sample generation rate will typically not be locked to + * the hardware playback rate. + */ +typedef struct PSPSoundBufferDesc_ { + __attribute__((aligned(64))) int16_t buffer[NUM_BUFFERS][BUFFER_SIZE*2]; + /* Keep this on its own cache line for uncached access by both SC and ME */ + __attribute__((aligned(64))) + volatile uint8_t write_ready[NUM_BUFFERS]; + // When nonzero, data can be stored in buffer[next_write] + /* Start a new cache line here (these are written by the ME) */ + __attribute__((aligned(64))) + unsigned int next_write; + // Index of next buffer to store data into + unsigned int saved_samples; + // Number of samples accumulated in next_write buffer + /* Start another new cache line here (these are written by the SC) */ + __attribute__((aligned(64))) + int started; // Nonzero if channel is playing + int channel; // Channel number allocated for this buffer + unsigned int cur_play; + // Index of buffer currently being played by the hardware + unsigned int next_play; + // Index of next buffer to submit for playback + /* Internal use: */ + SceUID thread; // Playback thread handle + int stop; // Flag to tell thread to terminate +} PSPSoundBufferDesc; +static PSPSoundBufferDesc stereo_buffer; + +/* Mute flag (used by Mute and UnMute methods) */ +static int muted = 1; + +#ifdef DUMP_AUDIO +/* Audio output file */ +static int dump_fd; +#endif + +/*----------------------------------*/ + +/* Uncached pointer to sound RAM (to save generating it on every access) */ +static uint8_t *SoundRam_uncached; + +/*-----------------------------------------------------------------------*/ + +/* Local function declarations */ + +static FASTCALL u8 psp_SoundRamReadByte(u32 address); +static FASTCALL u16 psp_SoundRamReadWord(u32 address); +static FASTCALL u32 psp_SoundRamReadLong(u32 address); +static FASTCALL void psp_SoundRamWriteByte(u32 address, u8 data); +static FASTCALL void psp_SoundRamWriteWord(u32 address, u16 data); +static FASTCALL void psp_SoundRamWriteLong(u32 address, u32 data); + +static int start_channel(PSPSoundBufferDesc *buffer_desc); +void stop_channel(PSPSoundBufferDesc *buffer_desc); +static int playback_thread(SceSize args, void *argp); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * psp_sound_init: Initialize the sound interface. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_sound_init(void) +{ + if (stereo_buffer.started) { + /* Already initialized! */ + return 0; + } + +#ifdef DUMP_AUDIO + dump_fd = sceIoOpen("audio.pcm", + PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0600); + if (dump_fd < 0) { + DMSG("open(audio.pcm): %s", psp_strerror(dump_fd)); + dump_fd = 0; + } +#endif + + if (!start_channel(&stereo_buffer)) { + DMSG("Failed to start playback"); + return -1; + } + + /* If the Media Engine is in use, reassign the sound RAM access + * functions so we read/write through the cache as appropriate. */ + if (me_available && config_get_use_me()) { + SoundRam_uncached = (uint8_t *)((uintptr_t)SoundRam | 0x40000000); + unsigned int i; + for (i = 0x5A0; i < 0x5B0; i++) { + ReadByteList [i] = psp_SoundRamReadByte; + ReadWordList [i] = psp_SoundRamReadWord; + ReadLongList [i] = psp_SoundRamReadLong; + WriteByteList[i] = psp_SoundRamWriteByte; + WriteWordList[i] = psp_SoundRamWriteWord; + WriteLongList[i] = psp_SoundRamWriteLong; + } + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_deinit: Shut down the sound interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_sound_deinit(void) +{ + stop_channel(&stereo_buffer); + + unsigned int i; + for (i = 0x5A0; i < 0x5B0; i++) { + ReadByteList [i] = SoundRamReadByte; + ReadWordList [i] = SoundRamReadWord; + ReadLongList [i] = SoundRamReadLong; + WriteByteList[i] = SoundRamWriteByte; + WriteWordList[i] = SoundRamWriteWord; + WriteLongList[i] = SoundRamWriteLong; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_reset: Reset the sound interface. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_sound_reset(void) +{ + /* Nothing to do */ + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_change_video_format: Handle a change in the video refresh + * frequency. + * + * [Parameters] + * vertfreq: New refresh frequency (Hz) + * [Return value] + * Zero on success, negative on error + */ +static int psp_sound_change_video_format(int vertfreq) +{ + /* Nothing to do */ + return 0; +} + +/*************************************************************************/ + +/** + * psp_sound_update_audio: Output audio data. + * + * [Parameters] + * leftchanbuffer: Left channel sample array, as _signed_ 16-bit samples + * rightchanbuffer: Right channel sample array, as _signed_ 16-bit samples + * num_samples: Number of samples in sample arrays + * [Return value] + * None + */ +static void psp_sound_update_audio(u32 *leftchanbuffer, u32 *rightchanbuffer, + u32 num_samples) +{ + const unsigned int next_write = stereo_buffer.next_write; + + if (!leftchanbuffer || !rightchanbuffer + || !UNCACHED(stereo_buffer.write_ready[next_write]) + || num_samples == 0 + || num_samples > BUFFER_SIZE - stereo_buffer.saved_samples + ) { + if (!meUtilityIsME()) { // Can't write to stderr on the ME + DMSG("Invalid parameters: %p %p %u (status: wr=%d ss=%d)", + leftchanbuffer, rightchanbuffer, (unsigned int)num_samples, + UNCACHED(stereo_buffer.write_ready[next_write]), + stereo_buffer.saved_samples); + } + return; + } + + const int32_t *in_l = (int32_t *)leftchanbuffer; + const int32_t *in_r = (int32_t *)rightchanbuffer; + int16_t *out = + &stereo_buffer.buffer[next_write][stereo_buffer.saved_samples * 2]; + + uint32_t i; + for (i = 0; i < num_samples; i++) { + const int32_t lval = *in_l++; + const int32_t rval = *in_r++; + *out++ = bound(lval, -0x8000, 0x7FFF); + *out++ = bound(rval, -0x8000, 0x7FFF); + } + + stereo_buffer.saved_samples += num_samples; + if (stereo_buffer.saved_samples >= BUFFER_SIZE) { + if (meUtilityIsME()) { + /* Make sure the playback thread sees all the audio data */ + meUtilityDcacheWritebackInvalidateAll(); + } + UNCACHED(stereo_buffer.write_ready[next_write]) = 0; + stereo_buffer.saved_samples = 0; + stereo_buffer.next_write = (next_write + 1) % NUM_BUFFERS; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_get_audio_space: Return the number of samples immediately + * available for outputting audio data. + * + * [Parameters] + * None + * [Return value] + * Number of samples available + */ +static u32 psp_sound_get_audio_space(void) +{ + if (UNCACHED(stereo_buffer.write_ready[stereo_buffer.next_write])) { + return BUFFER_SIZE - stereo_buffer.saved_samples; + } else { + return 0; + } +} + +/*************************************************************************/ + +/** + * psp_sound_mute_audio: Disable audio output. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_sound_mute_audio(void) +{ + muted = 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_mute_audio: Enable audio output. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_sound_unmute_audio(void) +{ + muted = 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_set_volume: Set the audio output volume. + * + * [Parameters] + * volume: New volume (0-100, 100 = full volume) + * [Return value] + * None + */ +static void psp_sound_set_volume(int volume) +{ + const int pspvol = (PSP_AUDIO_VOLUME_MAX * volume + 50) / 100; + if (stereo_buffer.started) { + sceAudioChangeChannelVolume(stereo_buffer.channel, pspvol, pspvol); + } +} + +/*************************************************************************/ +/********************* PSP-local interface functions *********************/ +/*************************************************************************/ + +/** + * psp_sound_pause: Stop audio output. Called when the system is being + * suspended. + * + * [Parameters] + * None + * [Return value] + * None + */ +void psp_sound_pause(void) +{ + if (stereo_buffer.started) { + sceKernelSuspendThread(stereo_buffer.thread); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_unpause: Resume audio output. Called when the system is + * resuming from a suspend. + * + * [Parameters] + * None + * [Return value] + * None + */ +void psp_sound_unpause(void) +{ + if (stereo_buffer.started) { + sceKernelResumeThread(stereo_buffer.thread); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_exit: Terminate all playback in preparation for exiting. + * + * [Parameters] + * None + * [Return value] + * None + */ +void psp_sound_exit(void) +{ + if (stereo_buffer.started) { + stop_channel(&stereo_buffer); + } +} + +/*************************************************************************/ +/* Sound RAM access functions (for use when the 68k is running on the ME) */ +/*************************************************************************/ + +/** + * psp_SoundRamRead{Byte,Word,Long}: Sound RAM read access functions for + * use when the sound CPU is being emulated on the Media Engine. These + * functions access sound RAM using uncached pointers. + * + * 2Mbit mode (MEM4MB == 0) is not supported by these functions. + * + * [Parameters] + * address: Address to read from + * [Return value] + * Data loaded from given address + */ +static FASTCALL u8 psp_SoundRamReadByte(u32 address) +{ + address &= 0x7FFFF; + return T2ReadByte(SoundRam_uncached, address); +} + +static FASTCALL u16 psp_SoundRamReadWord(u32 address) +{ + address &= 0x7FFFF; + return T2ReadWord(SoundRam_uncached, address); +} + +static FASTCALL u32 psp_SoundRamReadLong(u32 address) +{ + address &= 0x7FFFF; + return T2ReadLong(SoundRam_uncached, address); +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_SoundRamWrite{Byte,Word,Long}: Sound RAM write access functions for + * use when the sound CPU is being emulated on the Media Engine. These + * functions do _not_ access sound RAM using uncached pointers; data is + * assumed to be flushed by the SCSP at periodic intervals. + * + * 2Mbit mode (MEM4MB == 0) is not supported by these functions. + * + * [Parameters] + * address: Address to write to + * data: Data to store + * [Return value] + * None + */ +static FASTCALL void psp_SoundRamWriteByte(u32 address, u8 data) +{ + address &= 0x7FFFF; + if (address < config_get_me_uncached_boundary()) { + T2WriteByte(SoundRam_uncached, address, data); + } else { + T2WriteByte(SoundRam, address, data); + } + M68KWriteNotify(address, 1); +} + +static FASTCALL void psp_SoundRamWriteWord(u32 address, u16 data) +{ + address &= 0x7FFFF; + if (address < config_get_me_uncached_boundary()) { + T2WriteWord(SoundRam_uncached, address, data); + } else { + T2WriteWord(SoundRam, address, data); + } + M68KWriteNotify(address, 2); +} + +static FASTCALL void psp_SoundRamWriteLong(u32 address, u32 data) +{ + address &= 0x7FFFF; + if (address < config_get_me_uncached_boundary()) { + T2WriteLong(SoundRam_uncached, address, data); + } else { + T2WriteLong(SoundRam, address, data); + } + M68KWriteNotify(address, 4); +} + +/*************************************************************************/ +/****************** Low-level audio channel management *******************/ +/*************************************************************************/ + +/** + * start_channel: Allocate a new channel and starts playback. + * + * [Parameters] + * buffer_desc: Playback buffer descriptor + * [Return value] + * Nonzero on success, zero on error + */ +static int start_channel(PSPSoundBufferDesc *buffer_desc) +{ + if (!buffer_desc) { + DMSG("buffer_desc == NULL"); + return 0; + } + if (buffer_desc->started) { + DMSG("Buffer is already started!"); + return 0; + } + + /* Allocate a hardware channel */ + buffer_desc->channel = sceAudioChReserve( + PSP_AUDIO_NEXT_CHANNEL, BUFFER_SIZE, PSP_AUDIO_FORMAT_STEREO + ); + if (buffer_desc->channel < 0) { + DMSG("Failed to allocate channel: %s", + psp_strerror(buffer_desc->channel)); + return 0; + } + + /* Initialize the ring buffer */ + buffer_desc->cur_play = NUM_BUFFERS - 1; + buffer_desc->next_play = 0; + buffer_desc->next_write = 0; + int i; + for (i = 0; i < NUM_BUFFERS; i++) { + buffer_desc->write_ready[i] = 1; + } + buffer_desc->stop = 0; + /* Also write everything out of the cache so it's ready for the ME */ + sceKernelDcacheWritebackAll(); + + /* Start the playback thread */ + char thname[100]; + snprintf(thname, sizeof(thname), "YabauseSoundCh%d", buffer_desc->channel); + SceUID handle = sys_start_thread(thname, playback_thread, + THREADPRI_SOUND, 0x1000, + sizeof(buffer_desc), &buffer_desc); + if (handle < 0) { + DMSG("Failed to create thread: %s", psp_strerror(handle)); + sceAudioChRelease(buffer_desc->channel); + return 0; + } + buffer_desc->thread = handle; + + /* Success */ + buffer_desc->started = 1; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * stop_channel: Stop playback from the given playback buffer. + * + * [Parameters] + * buffer_desc: Playback buffer descriptor + * [Return value] + * None + */ +void stop_channel(PSPSoundBufferDesc *buffer_desc) +{ + if (!buffer_desc) { + DMSG("buffer_desc == NULL"); + return; + } + if (!buffer_desc->started) { + DMSG("Buffer has not been started!"); + return; + } + + /* Signal the thread to stop, then wait for it (if we try to stop the + * thread in the middle of an audio write, we won't be able to free + * the hardware channel) */ + buffer_desc->stop = 1; + int tries; + for (tries = (1000 * (2*BUFFER_SIZE)/PLAYBACK_RATE); tries > 0; tries--) { + if (sys_delete_thread_if_stopped(buffer_desc->thread, NULL)) { + break; + } + sceKernelDelayThread(1000); // Wait for 1ms before trying again + } + + if (!tries) { + /* The thread didn't stop on its own, so terminate it with + * extreme prejudice */ + sceKernelTerminateDeleteThread(buffer_desc->thread); + sceAudioChRelease(buffer_desc->channel); + memset(buffer_desc, 0, sizeof(*buffer_desc)); + } +} + +/*************************************************************************/ + +/** + * playback_thread: Sound playback thread. Continually sends the ring + * buffer data to the OS until signaled to stop. + * + * [Parameters] + * args: Thread argument size + * argp: Thread argument pointer + * [Return value] + * Always zero + */ +static int playback_thread(SceSize args, void *argp) +{ + PSPSoundBufferDesc * const buffer_desc = *(PSPSoundBufferDesc **)argp; + + /* Temporary buffer for dummy audio data when the emulator falls behind + * real time (filled with the last sample sent to avoid clicks). This + * thread is only launched once, so "static" is safe. */ + static uint32_t dummy_buffer[BUFFER_SIZE]; // 1 stereo sample = 32 bits + static uint32_t last_sample; // Last stereo sample played + + while (!buffer_desc->stop) { + const unsigned int next_play = buffer_desc->next_play; +//static int x;int now=sceKernelGetSystemTimeLow();if(now-x>100000){printf("--- audio stat: %u %u %u %u cp=%u np=%u nw=%u\n",UNCACHED(buffer_desc->write_ready[0]),UNCACHED(buffer_desc->write_ready[1]),UNCACHED(buffer_desc->write_ready[2]),UNCACHED(buffer_desc->write_ready[3]),buffer_desc->cur_play,next_play,UNCACHED(buffer_desc->next_write));x=now;} + if (!UNCACHED(buffer_desc->write_ready[next_play])) { // i.e., ready for playback + const void *buffer = buffer_desc->buffer[next_play]; + last_sample = ((const uint32_t *)buffer)[BUFFER_SIZE - 1]; + sceAudioOutputBlocking(buffer_desc->channel, muted ? 0 : 0x8000, + buffer); +#ifdef DUMP_AUDIO + sceIoWrite(dump_fd, buffer, BUFFER_SIZE*4); +#endif + UNCACHED(buffer_desc->write_ready[buffer_desc->cur_play]) = 1; + buffer_desc->cur_play = next_play; + buffer_desc->next_play = (next_play + 1) % NUM_BUFFERS; + } else { + const uint32_t sample = last_sample; // Help out optimizer + uint32_t *ptr32 = dummy_buffer; + unsigned int i; + for (i = 0; i < BUFFER_SIZE; i += 8) { + ptr32[i+0] = sample; + ptr32[i+1] = sample; + ptr32[i+2] = sample; + ptr32[i+3] = sample; + ptr32[i+4] = sample; + ptr32[i+5] = sample; + ptr32[i+6] = sample; + ptr32[i+7] = sample; + } + sceAudioOutputBlocking(buffer_desc->channel, muted ? 0 : 0x8000, + dummy_buffer); + } + } + + sceAudioChRelease(buffer_desc->channel); + memset(buffer_desc, 0, sizeof(*buffer_desc)); + return 0; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-sound.h b/yabause/src/psp/psp-sound.h new file mode 100644 index 0000000000..89b46495d1 --- /dev/null +++ b/yabause/src/psp/psp-sound.h @@ -0,0 +1,80 @@ +/* src/psp/psp-sound.h: PSP sound output module header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_SOUND_H +#define PSP_SOUND_H + +#include "../scsp.h" // for SoundInterface_struct + +/*************************************************************************/ + +/* Module interface definition */ +extern SoundInterface_struct SNDPSP; + +/* Unique module ID (must be different from any in scsp.h) */ +#define SNDCORE_PSP 0x5CE // "SCE" + +/*-----------------------------------------------------------------------*/ + +/** + * psp_sound_pause: Stop audio output. Called when the system is being + * suspended. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void psp_sound_pause(void); + +/** + * psp_sound_unpause: Resume audio output. Called when the system is + * resuming from a suspend. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void psp_sound_unpause(void); + +/** + * psp_sound_exit: Terminate all playback in preparation for exiting. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void psp_sound_exit(void); + +/*************************************************************************/ + +#endif // PSP_SOUND_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video-bitmap.c b/yabause/src/psp/psp-video-bitmap.c new file mode 100644 index 0000000000..1083865437 --- /dev/null +++ b/yabause/src/psp/psp-video-bitmap.c @@ -0,0 +1,314 @@ +/* src/psp/psp-video-bitmap.c: Bitmapped background graphics handling for + PSP video module + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../vidshared.h" + +#include "display.h" +#include "gu.h" +#include "psp-video.h" +#include "psp-video-internal.h" +#include "texcache.h" + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * vdp2_draw_bitmap: Draw a graphics layer bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_bitmap(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Set up vertices */ + VertexUVXYZ *vertices = pspGuGetMemoryMerge(sizeof(*vertices) * 2); + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].x = info->x * info->coordincx; + vertices[0].y = info->y * info->coordincy; + vertices[0].z = 0; + vertices[1].u = info->cellw; + vertices[1].v = info->cellh; + vertices[1].x = (info->x + info->cellw) * info->coordincx; + vertices[1].y = (info->y + info->cellh) * info->coordincy; + vertices[1].z = 0; + + /* FIXME: only very basic clipping processing at the moment; see + * vidsoft.c for more details on how this works */ + if ((info->wctl & 0x3) == 0x3) { + vertices[0].x = clip[0].xstart; + vertices[0].y = clip[0].ystart; + vertices[1].x = clip[0].xend + 1; + vertices[1].y = clip[0].yend + 1; + vertices[1].u = (vertices[1].x - vertices[0].x) / info->coordincx; + vertices[1].v = (vertices[1].y - vertices[0].y) / info->coordincy; + /* Offset the bitmap address appropriately */ + const int bpp = (info->colornumber==4 ? 32 : + info->colornumber>=2 ? 16 : + info->colornumber==1 ? 8 : 4); + const int xofs = clip[0].xstart - info->x; + const int yofs = clip[0].ystart - info->y; + info->charaddr += (yofs * info->cellw + xofs) * bpp / 8; + } + + /* Draw the bitmap */ + texcache_load_bitmap( + info->charaddr, + (vertices[1].u - vertices[0].u + 7) & -8, + vertices[1].v - vertices[0].v, + info->cellw, info->colornumber, info->transparencyenable, + info->coloroffset, info->paladdr << 4, + info->cor, info->cog, info->cob, + 0 // Bitmaps are likely to change, so don't cache them persistently + ); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_draw_bitmap_t8: Draw an 8-bit indexed graphics layer bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_bitmap_t8(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Set up vertices */ + // FIXME: this will break with a final (clipped) width above 512; + // need to split the texture in half in that case + VertexUVXYZ *vertices = pspGuGetMemoryMerge(sizeof(*vertices) * 2); + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].x = info->x * info->coordincx; + vertices[0].y = info->y * info->coordincy; + vertices[0].z = 0; + vertices[1].u = info->cellw; + vertices[1].v = info->cellh; + vertices[1].x = (info->x + info->cellw) * info->coordincx; + vertices[1].y = (info->y + info->cellh) * info->coordincy; + vertices[1].z = 0; + + /* FIXME: only very basic clipping processing at the moment; see + * vidsoft.c for more details on how this works */ + if ((info->wctl & 0x3) == 0x3) { + vertices[0].x = clip[0].xstart; + vertices[0].y = clip[0].ystart; + vertices[1].x = clip[0].xend + 1; + vertices[1].y = clip[0].yend + 1; + vertices[1].u = (vertices[1].x - vertices[0].x) / info->coordincx; + vertices[1].v = (vertices[1].y - vertices[0].y) / info->coordincy; + /* Offset the bitmap address appropriately */ + const int bpp = (info->colornumber==4 ? 32 : + info->colornumber>=2 ? 16 : + info->colornumber==1 ? 8 : 4); + const int xofs = clip[0].xstart - info->x; + const int yofs = clip[0].ystart - info->y; + info->charaddr += (yofs * info->cellw + xofs) * bpp / 8; + } + + /* Set up the color table */ + guClutMode(GU_PSM_8888, 0, 0xFF, 0); + void *ptr = vdp2_gen_t8_clut( + info->coloroffset, info->paladdr<<4, + info->transparencyenable, info->cor, info->cog, info->cob + ); + if (LIKELY(ptr)) { + guClutLoad(256/8, ptr); + } + + /* Draw the bitmap */ + guTexMode(GU_PSM_8888, 0, 0, 0); + guTexMode(GU_PSM_T8, 0, 0, 0); + guTexImage(0, 512, 512, info->cellw, &Vdp2Ram[info->charaddr & 0x7FFFF]); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_draw_bitmap_32: Draw a 32-bit ARGB1888 unscaled graphics layer + * bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_bitmap_32(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Determine the area to be drawn */ + unsigned int x0, y0, width, height; + /* FIXME: only very basic clipping processing at the moment; see + * vidsoft.c for more details on how this works */ + if ((info->wctl & 0x3) == 0x3) { + x0 = clip[0].xstart; + y0 = clip[0].ystart; + width = (clip[0].xend + 1) - x0; + height = (clip[0].yend + 1) - y0; + /* Offset the bitmap address appropriately */ + const int xofs = clip[0].xstart - info->x; + const int yofs = clip[0].ystart - info->y; + info->charaddr += (yofs * info->cellw + xofs) * 4; + } else { + x0 = info->x; + y0 = info->y; + width = info->cellw; + height = info->cellh; + } + + /* Set up vertices (using optimized 64-byte-wide strips) */ + // FIXME: this will work incorrectly on bitmaps wider than 512 pixels, + // if there are any such (VDP2 RAM is only big enough for 512x256) + const uint32_t nverts = ((width+15) / 16) * 2; + VertexUVXYZ *vertices = pspGuGetMemoryMerge(sizeof(*vertices) * nverts); + unsigned int x, i; + for (x = i = 0; x < width; x += 16, i += 2) { + const unsigned int thisw = (width-x > 16 ? 16 : width-x); + vertices[i+0].u = x; + vertices[i+0].v = 0; + vertices[i+0].x = x0 + x; + vertices[i+0].y = y0; + vertices[i+0].z = 0; + vertices[i+1].u = x + thisw; + vertices[i+1].v = height; + vertices[i+1].x = x0 + x + thisw; + vertices[i+1].y = y0 + height; + vertices[i+1].z = 0; + } + + /* Set up GE parameters for drawing */ + guTexFlush(); + guTexMode(GU_PSM_T32, 0, 0, 0); + guTexImage(0, 512, 512, info->cellw, &Vdp2Ram[info->charaddr & 0x7FFFF]); + guAmbientColor(0xFFFFFFFF); + guTexFunc(GU_TFX_REPLACE, 1); + guDisable(GU_BLEND); + + /* If transparency is enabled, set up an offscreen buffer for + * rendering; if we can't (not enough spare VRAM), just draw with + * transparency disabled */ + uint32_t *offscreen_buffer = NULL; + uint32_t offscreen_stride = 0; + if (info->transparencyenable) { + offscreen_stride = (width + 3) & -4; + offscreen_buffer = display_alloc_vram(offscreen_stride * height); + if (offscreen_buffer) { + /* Adjust the draw buffer pointer so we don't have to mess + * with the vertex coordinates */ + const uint32_t offset = + vertices[0].y * offscreen_stride + vertices[0].x; + guDrawBuffer(GU_PSM_8888, + offscreen_buffer - offset, offscreen_stride); + } + } + + /* Draw each of the RGB components independently */ + unsigned int rgb; + for (rgb = 0; rgb < 3; rgb++) { + /* Set up the color table for this component */ + const int ofs = (rgb==0 ? info->cor : rgb==1 ? info->cog : info->cob); + void *clut = vdp2_gen_32_clut(ofs); + if (clut) { + guClutMode(GU_PSM_8888, (3-rgb)*8, 0xFF, 0); + guClutLoad(256/8, clut); + } + + /* Blit this component to the screen. If we're using transparency, + * also clear the alpha byte on the first blit so we only need a + * single "set" operation later. */ + if (offscreen_buffer && rgb == 0) { + guStencilOp(GU_ZERO, GU_ZERO, GU_ZERO); // Clear alpha bytes + guEnable(GU_STENCIL_TEST); + } + guPixelMask(~(0xFF0000FF << (rgb*8))); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts, NULL, vertices); + if (offscreen_buffer && rgb == 0) { + guDisable(GU_STENCIL_TEST); + } + } + guPixelMask(0); + + /* Mask off transparent pixels and blit back to the display buffer + * if appropriate */ + if (offscreen_buffer) { + static const __attribute__((aligned(64))) uint32_t mask_clut[8] = + {0, ~0}; + guClutMode(GU_PSM_8888, 7, 0x1, 0); + guClutLoad(1, mask_clut); + guAlphaFunc(GU_EQUAL, 0xFF, 0xFF); // Only pass non-transparent pixels + guEnable(GU_ALPHA_TEST); + guStencilFunc(GU_ALWAYS, 0xFF, 0xFF); // Set drawn alpha bytes to 255 + guStencilOp(GU_REPLACE, GU_REPLACE, GU_REPLACE); + guEnable(GU_STENCIL_TEST); + guPixelMask(0xFFFFFF); // Only modify the alpha byte + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts, NULL, vertices); + guPixelMask(0); + guStencilOp(GU_KEEP, GU_KEEP, GU_KEEP); + guDisable(GU_STENCIL_TEST); + guDisable(GU_ALPHA_TEST); + + guDrawBuffer(GU_PSM_8888, display_work_buffer(), DISPLAY_STRIDE); + guTexFlush(); + guTexMode(GU_PSM_8888, 0, 0, 0); + guTexImage(0, 512, 512, offscreen_stride, offscreen_buffer); + guTexFunc(GU_TFX_REPLACE, 1); + guEnable(GU_BLEND); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts, NULL, vertices); + } + + /* Turn blending back on before returning (in case we didn't do so for + * transparency handling), since everyone else expects it to be on */ + guEnable(GU_BLEND); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video-internal.h b/yabause/src/psp/psp-video-internal.h new file mode 100644 index 0000000000..5fc4120ffe --- /dev/null +++ b/yabause/src/psp/psp-video-internal.h @@ -0,0 +1,431 @@ +/* src/psp/psp-video-internal.h: Internal header for PSP video module + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_VIDEO_INTERNAL_H +#define PSP_VIDEO_INTERNAL_H + +#include "../vdp1.h" +#include "../vdp2.h" +#include "../vidshared.h" + +/*************************************************************************/ +/************************** Internal constants ***************************/ +/*************************************************************************/ + +/* Graphics layer identifiers. */ + +enum {BG_NBG0 = 0, BG_NBG1, BG_NBG2, BG_NBG3, BG_RBG0}; + +/*************************************************************************/ +/******************* Map drawing routine declarations ********************/ +/*************************************************************************/ + +/** + * vdp2_draw_bitmap: Draw a graphics layer bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_bitmap(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * vdp2_draw_bitmap_t8: Draw an 8-bit indexed graphics layer bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_bitmap_t8(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * vdp2_draw_bitmap_32: Draw a 32-bit ARGB1888 unscaled graphics layer + * bitmap. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_bitmap_32(vdp2draw_struct *info, + const clipping_struct *clip); + +/*----------------------------------*/ + +/** + * vdp2_draw_map_rotated: Draw a rotated graphics layer. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_map_rotated(vdp2draw_struct *info, + const clipping_struct *clip); + +/*----------------------------------*/ + +/** + * vdp2_draw_map_8x8: Draw a graphics layer composed of 8x8 patterns of + * any format. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_map_8x8(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * vdp2_draw_map_8x8_t8: Draw a graphics layer composed of 8-bit indexed + * color 8x8 patterns. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_map_8x8_t8(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * vdp2_draw_map_16x16: Draw a graphics layer composed of 16x16 patterns + * of any format. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_map_16x16(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * vdp2_draw_map_16x16_t8: Draw a graphics layer composed of 8-bit indexed + * color 16x16 patterns. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +extern void vdp2_draw_map_16x16_t8(vdp2draw_struct *info, + const clipping_struct *clip); + +/*************************************************************************/ +/**************** Game-specific optimizations and tweaks *****************/ +/*************************************************************************/ + +/** + * psp_video_apply_tweaks: Apply game-specific optimizations and tweaks + * for faster/better PSP video output. Called at the beginning of drawing + * each frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void psp_video_apply_tweaks(void); + +/*************************************************************************/ + +/** + * CustomDrawRoutine: Type of a custom drawing routine for a graphics + * layer, used with psp_video_set_draw_routine(). + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * Nonzero if the graphics layer was drawn, zero if not + */ +typedef int CustomDrawRoutine(vdp2draw_struct *info, + const clipping_struct *clip); + +/** + * psp_video_set_draw_routine: Set a custom drawing routine for a specific + * graphics layer. If "is_fast" is true when setting a routine for RBG0, + * the frame rate will not be halved regardless of the related setting in + * the configuration menu. + * + * [Parameters] + * layer: Graphics layer (BG_*) + * func: Drawing routine (NULL to clear any previous setting) + * is_fast: For BG_RBG0, indicates whether the routine is fast enough + * to be considered a non-distorted layer for the purposes + * of frame rate adjustment; ignored for other layers + * [Return value] + * None + */ +extern void psp_video_set_draw_routine(int layer, CustomDrawRoutine *func, + int is_fast); + +/*************************************************************************/ +/**************** Other utility routines and declarations ****************/ +/*************************************************************************/ + +/* Displayed width and height */ +extern unsigned int disp_width, disp_height; + +/* Scale (right-shift) applied to X and Y coordinates */ +extern unsigned int disp_xscale, disp_yscale; + +/* Total number of frames to skip before we draw the next one */ +extern unsigned int frames_to_skip; + +/* Number of frames skipped so far since we drew the last one */ +extern unsigned int frames_skipped; + +/* VDP1 color component offset values (-0xFF...+0xFF) */ +extern int32_t vdp1_rofs, vdp1_gofs, vdp1_bofs; + +/*************************************************************************/ + +/** + * vdp2_is_persistent: Return whether the tile at the given address in + * VDP2 RAM is persistently cacheable. + * + * [Parameters] + * address: Tile address in VDP2 RAM + * [Return value] + * Nonzero if tile texture can be persistently cached, else zero + */ +extern int vdp2_is_persistent(uint32_t address); + +/*************************************************************************/ + +/** + * vdp2_calc_pattern_address, vdp2_calc_pattern_address_8x8, + * vdp2_calc_pattern_address_16x16: Calculate the address and associated + * data of the next pattern. The _8x8 and _16x16 versions are for use when + * the size of the pattern (8x8 or 16x16 pixels) is known at calling time. + * + * [Parameters] + * info: Graphics layer data + * [Return value] + * None + */ +static inline void vdp2_calc_pattern_address(vdp2draw_struct *info) +{ + if (info->patterndatasize == 1) { + const uint16_t data = T1ReadWord(Vdp2Ram, info->addr); + /* We don't support per-pixel priority, so don't waste time + * parsing this bit */ + //info->specialfunction = (info->supplementdata >> 9) & 1; + if (info->colornumber == 0) { // 4bpp + info->paladdr = ((data >> 12) & 0xF) + | ((info->supplementdata >> 1) & 0x70); + } else { // >=8bpp + info->paladdr = (data >> 8) & 0x70; + } + if (!info->auxmode) { + info->flipfunction = (data >> 10) & 0x3; + if (info->patternwh == 1) { + info->charaddr = (info->supplementdata & 0x1F) << 10 + | (data & 0x3FF); + } else { + info->charaddr = (info->supplementdata & 0x1C) << 10 + | (data & 0x3FF) << 2 + | (info->supplementdata & 0x3); + } + } else { + info->flipfunction = 0; + if (info->patternwh == 1) { + info->charaddr = (info->supplementdata & 0x1C) << 10 + | (data & 0xFFF); + } else { + info->charaddr = (info->supplementdata & 0x10) << 10 + | (data & 0xFFF) << 2 + | (info->supplementdata & 0x3); + } + } + } else { // patterndatasize == 2 + const uint32_t data = T1ReadLong(Vdp2Ram, info->addr); + info->charaddr = data & 0x7FFF; + info->flipfunction = data >> 30; + info->paladdr = (data >> 16) & 0x007F; + /* Ignored, as above */ + //info->specialfunction = (data1 >> 29) & 1; + } + + /* We don't support 1MB of VDP2 RAM, so always mask off the high bit */ + //if (!(Vdp2Regs->VRSIZE & 0x8000)) { + info->charaddr &= 0x3FFF; + //} + + info->charaddr <<= 5; +} + +/*----------------------------------*/ + +static inline void vdp2_calc_pattern_address_8x8(vdp2draw_struct *info) +{ + if (info->patterndatasize == 1) { + const uint16_t data = T1ReadWord(Vdp2Ram, info->addr); + //info->specialfunction = (info->supplementdata >> 9) & 1; + if (info->colornumber == 0) { // 4bpp + info->paladdr = ((data >> 12) & 0xF) + | ((info->supplementdata >> 1) & 0x70); + } else { // >=8bpp + info->paladdr = (data >> 8) & 0x70; + } + if (!info->auxmode) { + info->flipfunction = (data >> 10) & 0x3; + info->charaddr = (info->supplementdata & 0xF) << 10 + | (data & 0x3FF); + } else { + info->flipfunction = 0; + info->charaddr = (info->supplementdata & 0xC) << 10 + | (data & 0xFFF); + } + } else { // patterndatasize == 2 + const uint32_t data = T1ReadLong(Vdp2Ram, info->addr); + info->charaddr = data & 0x3FFF; + info->flipfunction = data >> 30; + info->paladdr = (data >> 16) & 0x007F; + /* Ignored, as above */ + //info->specialfunction = (data1 >> 29) & 1; + } + + info->charaddr <<= 5; +} + +/*----------------------------------*/ + +static inline void vdp2_calc_pattern_address_16x16(vdp2draw_struct *info) +{ + if (info->patterndatasize == 1) { + const uint16_t data = T1ReadWord(Vdp2Ram, info->addr); + //info->specialfunction = (info->supplementdata >> 9) & 1; + if (info->colornumber == 0) { // 4bpp + info->paladdr = ((data >> 12) & 0xF) + | ((info->supplementdata >> 1) & 0x70); + } else { // >=8bpp + info->paladdr = (data >> 8) & 0x70; + } + if (!info->auxmode) { + info->flipfunction = (data >> 10) & 0x3; + info->charaddr = (info->supplementdata & 0xC) << 10 + | (data & 0x3FF) << 2 + | (info->supplementdata & 0x3); + } else { + info->flipfunction = 0; + info->charaddr = (data & 0xFFF) << 2 + | (info->supplementdata & 0x3); + } + } else { // patterndatasize == 2 + const uint32_t data = T1ReadLong(Vdp2Ram, info->addr); + info->charaddr = data & 0x3FFF; + info->flipfunction = data >> 30; + info->paladdr = (data >> 16) & 0x007F; + /* Ignored, as above */ + //info->specialfunction = (data1 >> 29) & 1; + } + + info->charaddr <<= 5; +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_gen_t8_clut: Generate a 256-color color table for an 8-bit indexed + * tile given the base color index and color offset values. Helper routine + * for the T8 map drawing functions. + * + * [Parameters] + * color_base: Base color index + * color_ofs: Color offset (ORed with pixel value) + * transparent: Nonzero if color index 0 should be transparent + * rofs, gofs, bofs: Red/green/blue adjustment values + * [Return value] + * Allocated color table pointer + */ +static inline uint32_t *vdp2_gen_t8_clut( + int color_base, int color_ofs, int transparent, + int rofs, int gofs, int bofs) +{ + uint32_t *clut = pspGuGetMemoryMerge(256*4 + 60); + clut = (uint32_t *)(((uintptr_t)clut + 63) & -64); // Must be aligned + int i; + for (i = 0; i < 256; i++) { + clut[i] = adjust_color_32_32( + global_clut_32[color_base + (color_ofs | i)], rofs, gofs, bofs + ); + } + if (transparent) { + clut[0] = 0x00000000; + } + return clut; +} + +/*----------------------------------*/ + +/** + * vdp2_gen_32_clut: Generate a 256-color color table for one color + * component of a 32-bit pixel given the color offset value. Helper + * routine for the 32bpp bitmap drawing function. + * + * [Parameters] + * color_base: Base color index + * color_ofs: Color offset (ORed with pixel value) + * transparent: Nonzero if color index 0 should be transparent + * rofs, gofs, bofs: Red/green/blue adjustment values + * [Return value] + * Allocated color table pointer + */ +static inline uint32_t *vdp2_gen_32_clut(int ofs) +{ + uint32_t *clut = pspGuGetMemoryMerge(256*4 + 60); + clut = (uint32_t *)(((uintptr_t)clut + 63) & -64); // Must be aligned + int i; + for (i = 0; i < 256; i++) { + clut[i] = 0xFF000000 | (bound(i+ofs, 0, 255) * 0x010101); + } + return clut; +} + +/*************************************************************************/ +/*************************************************************************/ + +#endif // PSP_VIDEO_INTERNAL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video-rotate.c b/yabause/src/psp/psp-video-rotate.c new file mode 100644 index 0000000000..348e6e2e8e --- /dev/null +++ b/yabause/src/psp/psp-video-rotate.c @@ -0,0 +1,1688 @@ +/* src/psp/psp-video-rotate.c: Rotated background graphics handling for + PSP video module + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../vidshared.h" + +#include "config.h" +#include "gu.h" +#include "psp-video.h" +#include "psp-video-internal.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/** + * USE_FIXED_POINT: If defined, floating-point operations will be replaced + * by signed 16.16 fixed-point operations. This may slightly change + * behavior due to the differing precision of computations. + * + * This currently (2010/3/9) gives an improvement of 5-10% with multiple + * coefficients per row or 3-5% with one coefficient per row. + */ +#define USE_FIXED_POINT + +/*************************************************************************/ + +/**** Floating-point / fixed-point operations ****/ + +#ifdef USE_FIXED_POINT + +# define FIXEDFLOAT int32_t +# define UFIXEDFLOAT uint32_t +# define FIXED_MULT(a,b) ((int32_t)(((int64_t)(a) * (int64_t)(b)) >> 16)) +# define FIXED_TOINT(n) ((n) >> 16) +# define FIXED_TOFLOAT(n) ((n) / 65536.0f) + +#else // !USE_FIXED_POINT + +# define FIXEDFLOAT float +# define UFIXEDFLOAT float +# define FIXED_MULT(a,b) ((a) * (b)) +# define FIXED_TOINT(n) ifloorf((n)) +# define FIXED_TOFLOAT(n) ((n)) + +#endif // USE_FIXED_POINT + +/**** Coordinate transformation parameter structure ****/ + +typedef struct RotationParams_ RotationParams; +struct RotationParams_ { + /* Transformation parameters read from VDP2 RAM (arranged for easy + * VFPU loading, in case that ever becomes useful) */ + __attribute__((aligned(16))) FIXEDFLOAT Xst; + FIXEDFLOAT Yst, Zst, pad0w; + FIXEDFLOAT deltaXst, deltaYst, pad1z, pad1w; + FIXEDFLOAT deltaX, deltaY, pad2z, pad2w; + FIXEDFLOAT A, B, C, pad3w; + FIXEDFLOAT D, E, F, pad4w; + FIXEDFLOAT Px, Py, Pz, pad5w; + FIXEDFLOAT Cx, Cy, Cz, pad6w; + FIXEDFLOAT Mx, My, pad7z, pad7w; + FIXEDFLOAT kx, ky, pad8z, pad8w; //May be updated in coefficient table mode + + /* Computed transformation parameters */ + FIXEDFLOAT Xp, Yp, pad9z, pad9w; //May be updated in coefficient table mode + FIXEDFLOAT mat11, mat12, mat13, mat1_pad; + FIXEDFLOAT mat21, mat22, mat23, mat2_pad; + + /* Coefficient table parameters read from VDP2 RAM */ + UFIXEDFLOAT KAst; + FIXEDFLOAT deltaKAst; + FIXEDFLOAT deltaKAx; + + /* Coefficient table base address and flags */ + uint32_t coeftbladdr; + uint8_t coefenab; + uint8_t coefmode; + uint8_t coefdatasize; // Size of a single coefficient in bytes (2 or 4) + uint8_t coefdatashift; // log2(coefdatasize) + + /* Miscellaneous parameters */ + uint8_t screenover; // FIXME: What is this for? +}; + +/*************************************************************************/ + +/**** Macros for pixel address calculation and pixel getting ****/ + +/** + * INIT_CALC_PIXELNUM: Precompute values used by the CALC_PIXELNUM macro. + */ +#define INIT_CALC_PIXELNUM \ + const int srcx_mask = info->isbitmap \ + ? info->cellw - 1 \ + : (8 * 64 * info->planew * 4) - 1; \ + const int srcy_mask = info->isbitmap \ + ? info->cellh - 1 \ + : (8 * 64 * info->planeh * 4) - 1; \ + const int page_shift = 3 + 6; \ + const int page_mask = (1 << page_shift) - 1; \ + const int tile_shift = (info->patternwh==2) ? 4 : 3; \ + /* Remember the last tile seen, to save the time of looking it up \ + * while we're on nearby pixels */ \ + int last_tilex = -1, last_tiley = -1 + +/*----------------------------------*/ + +/** + * INIT_PAGEMAP: Initialize a page map array for the given parameter + * set (either 0 or 1). pagemap should be declared as: + * uint32_t pagemap[8][8]; + */ +#define INIT_PAGEMAP(pagemap,set) do { \ + int plane_aoffset = ((Vdp2Regs->MPOFR >> ((set) * 4)) & 7) << 6; \ + const uint8_t *plane_map = set ? (const uint8_t *)&Vdp2Regs->MPABRB \ + : (const uint8_t *)&Vdp2Regs->MPABRA;\ + const int plane_bits = info->planew_bits + info->planeh_bits; \ + const int plane_ashift = 11 + (info->patternwh==1 ? 2 : 0) \ + + (info->patterndatasize==2 ? 1 : 0); \ + const int plane_amask = (0xFF >> (plane_ashift - 11)) \ + ^ ((1 << plane_bits) - 1); \ + unsigned int planenum = 0; \ + unsigned int plane_y; \ + for (plane_y = 0; plane_y < 4; plane_y++) { \ + unsigned int plane_x; \ + for (plane_x = 0; plane_x < 4; plane_x++, planenum++) { \ + uint32_t address = \ + ((plane_aoffset | plane_map[planenum]) & plane_amask) \ + << plane_ashift; \ + unsigned int pagenum = 0; \ + unsigned int page_y; \ + for (page_y = 0; page_y < info->planeh; page_y++) { \ + unsigned int page_x; \ + for (page_x = 0; page_x < info->planew; page_x++, pagenum++) {\ + const int tilenum = \ + pagenum << (2*(page_shift-tile_shift)); \ + pagemap[plane_y*info->planeh + page_y] \ + [plane_x*info->planew + page_x] = \ + address + (tilenum << (info->patterndatasize_bits+1));\ + } \ + } \ + } \ + } \ +} while (0) + +/*----------------------------------*/ + +/** + * CALC_TILEADDR_8x8, CALC_TILEADDR_16x16: Calculate the tile data address + * associated with the tile coordinate (tilex,tiley) for 8x8 or 16x16 tiles. + * Helper macros for CALC_PIXELNUM. + */ +#define CALC_TILEADDR_8x8(tilex,tiley,pagemap) do { \ + const int page_x = srcx >> page_shift; \ + const int page_y = srcy >> page_shift; \ + info->addr = pagemap[page_y][page_x]; \ + const int tile_x = (srcx & page_mask) >> 3; \ + const int tile_y = (srcy & page_mask) >> 3; \ + const int tilenum = tile_y << (page_shift - 3) | tile_x; \ + info->addr += tilenum << (info->patterndatasize_bits + 1); \ +} while (0) + +#define CALC_TILEADDR_16x16(tilex,tiley,pagemap) do { \ + const int page_x = srcx >> page_shift; \ + const int page_y = srcy >> page_shift; \ + info->addr = pagemap[page_y][page_x]; \ + const int tile_x = (srcx & page_mask) >> 4; \ + const int tile_y = (srcy & page_mask) >> 4; \ + const int tilenum = tile_y << (page_shift - 4) | tile_x; \ + info->addr += tilenum << (info->patterndatasize_bits + 1); \ +} while (0) + +/*----------------------------------*/ + +/** + * CALC_PIXELNUM: Calculate the pixel index associated with the + * transformed coordinates (srcx,srcy), store it in the variable + * "pixelnum", and update the "info" structure's fields as necessary for + * the associated tile (if the graphics layer is in tilemap mode). + * + * This is implemented as a macro so it can make use of precomputed + * constant values within the calling routine. + * + * Optimization note: The use of nearly identical code in the + * if(tile_shift==4) and else branches may look awkward, but it allows the + * compiler to use known values rather than having to repeatedly check and + * branch on the tile size, giving a speed increase of 3-5%. Greater + * optimization could be achieved by pulling such tests further out of the + * critical path, at the cost of code size explosion; one alternative + * would be dynamic compilation/assembly of rotation code based on the + * graphics layer's parameters. + */ +#define CALC_PIXELNUM(pagemap) do { \ + srcx &= srcx_mask; \ + srcy &= srcy_mask; \ + if (info->isbitmap) { \ + pixelnum = srcy << info->cellw_bits | srcx; \ + } else if (tile_shift == 4) { \ + const int tilex = srcx >> 4; \ + const int tiley = srcy >> 4; \ + if (tilex != last_tilex || tiley != last_tiley) { \ + last_tilex = tilex; \ + last_tiley = tiley; \ + CALC_TILEADDR_16x16(tilex, tiley, pagemap); \ + vdp2_calc_pattern_address_16x16(info); \ + } \ + srcx &= 15; \ + srcy &= 15; \ + if (info->flipfunction & 1) { \ + srcx ^= 15; \ + } \ + if (info->flipfunction & 2) { \ + srcy ^= 15; \ + } \ + pixelnum = (srcy & 8) << (7-3) \ + | (srcx & 8) << (6-3) \ + | (srcy & 7) << 3 \ + | (srcx & 7); \ + } else { /* tile_shift == 3 */ \ + const int tilex = srcx >> 3; \ + const int tiley = srcy >> 3; \ + if (tilex != last_tilex || tiley != last_tiley) { \ + last_tilex = tilex; \ + last_tiley = tiley; \ + CALC_TILEADDR_8x8(tilex, tiley, pagemap); \ + vdp2_calc_pattern_address_8x8(info); \ + } \ + srcx &= 7; \ + srcy &= 7; \ + if (info->flipfunction & 1) { \ + srcx ^= 7; \ + } \ + if (info->flipfunction & 2) { \ + srcy ^= 7; \ + } \ + pixelnum = srcy << 3 | srcx; \ + } \ +} while (0) + +/*-----------------------------------------------------------------------*/ + +/** + * SELECT_GET_PIXEL: Declares "get_pixel" as a function pointer to an + * appropriate pixel-getting function based on the graphics layer's + * parameters. + */ + +#define SELECT_GET_PIXEL \ + const int need_adjust = \ + (info->alpha != 0xFF) || info->cor || info->cog || info->cob; \ + uint32_t (*get_pixel)(vdp2draw_struct *, unsigned int); \ + switch (info->colornumber) { \ + case 0: \ + get_pixel = need_adjust \ + ? rotation_get_pixel_t4_adjust \ + : info->transparencyenable \ + ? rotation_get_pixel_t4_transparent \ + : rotation_get_pixel_t4; \ + break; \ + case 1: \ + get_pixel = need_adjust \ + ? rotation_get_pixel_t8_adjust \ + : info->transparencyenable \ + ? rotation_get_pixel_t8_transparent \ + : rotation_get_pixel_t8; \ + break; \ + case 2: \ + get_pixel = need_adjust \ + ? rotation_get_pixel_t16_adjust \ + : info->transparencyenable \ + ? rotation_get_pixel_t16_transparent \ + : rotation_get_pixel_t16; \ + break; \ + case 3: \ + get_pixel = need_adjust \ + ? rotation_get_pixel_16_adjust \ + : info->transparencyenable \ + ? rotation_get_pixel_16_transparent \ + : rotation_get_pixel_16; \ + break; \ + case 4: \ + get_pixel = need_adjust \ + ? rotation_get_pixel_32_adjust \ + : info->transparencyenable \ + ? rotation_get_pixel_32_transparent \ + : rotation_get_pixel_32; \ + break; \ + default: \ + DMSG("Invalid pixel format %d", info->colornumber); \ + return; \ + } + +/*************************************************************************/ + +/**** Local function declarations ****/ + +static void render_mode0(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2], int which); +static void render_mode0_region(uint32_t *pixelbuf, vdp2draw_struct *info, + RotationParams param_set[2], int which, + unsigned int x0, unsigned int y0, + unsigned int xlim, unsigned int ylim); + +static void render_mode1(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2]); +static int mode1_is_split_screen(const RotationParams param_set[2], + int *top_set_ret, int *switch_y_ret); + +static void render_mode2(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2]); + +static void get_rotation_parameters(vdp2draw_struct *info, int which, + RotationParams *param_ret); + +__attribute__((unused)) +static int get_rotation_coefficient(RotationParams *param, uint32_t address); +static int get_rotation_coefficient_size2_mode0(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size2_mode1(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size2_mode2(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size2_mode3(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size4_mode0(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size4_mode1(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size4_mode2(RotationParams *param, + const void *address); +static int get_rotation_coefficient_size4_mode3(RotationParams *param, + const void *address); + +static void transform_coordinates(const RotationParams *param, int x, int y, + FIXEDFLOAT *srcx_ret, FIXEDFLOAT *srcy_ret); + +static uint32_t rotation_get_pixel_t4(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t8(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t16(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_16(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_32(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t4_transparent(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t8_transparent(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t16_transparent(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_16_transparent(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_32_transparent(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t4_adjust(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t8_adjust(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_t16_adjust(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_16_adjust(vdp2draw_struct *info, + unsigned int pixelnum); +static uint32_t rotation_get_pixel_32_adjust(vdp2draw_struct *info, + unsigned int pixelnum); + +/*-----------------------------------------------------------------------*/ + +/* Table of coefficient-reading functions, indexed by + * [param.coefdatasize==4][param.coefmode] */ + +static int (*get_coef_table[2][4])(RotationParams *, const void *) = { + {get_rotation_coefficient_size2_mode0, + get_rotation_coefficient_size2_mode1, + get_rotation_coefficient_size2_mode2, + get_rotation_coefficient_size2_mode3}, + {get_rotation_coefficient_size4_mode0, + get_rotation_coefficient_size4_mode1, + get_rotation_coefficient_size4_mode2, + get_rotation_coefficient_size4_mode3} +}; + +/*************************************************************************/ +/************************** Interface function ***************************/ +/*************************************************************************/ + +/** + * vdp2_draw_map_rotated: Draw a rotated graphics layer. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_map_rotated(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Parse rotation parameters. */ + + static __attribute__((aligned(16))) RotationParams param_set[2]; + get_rotation_parameters(info, 0, ¶m_set[0]); + get_rotation_parameters(info, 1, ¶m_set[1]); + + /* Allocate a buffer for drawing the texture. Note that we swizzle + * the texture for faster drawing, since it's allocated in system + * RAM (which the GE is slow at accessing). */ + + uint32_t *pixelbuf = guGetMemory(disp_width * disp_height * 4 + 60); + pixelbuf = (uint32_t *)(((uintptr_t)pixelbuf + 63) & -64); + + /* Render all pixels. */ + + switch (info->rotatemode) { + case 0: + render_mode0(pixelbuf, info, clip, param_set, info->rotatenum); + break; + case 1: + render_mode1(pixelbuf, info, clip, param_set); + break; + case 2: + render_mode2(pixelbuf, info, clip, param_set); + break; + } + + /* Set up vertices for optimized GE drawing. */ + + const unsigned int nverts = (disp_width / 16) * 2; + VertexUVXYZ *vertices = guGetMemory(sizeof(*vertices) * nverts); + VertexUVXYZ *vptr; + unsigned int x; + for (x = 0, vptr = vertices; x < disp_width; x += 16, vptr += 2) { + vptr[0].u = x; + vptr[0].v = 0; + vptr[0].x = x >> disp_xscale; + vptr[0].y = 0; + vptr[0].z = 0; + vptr[1].u = x + 16; + vptr[1].v = disp_height; + vptr[1].x = (x + 16) >> disp_xscale; + vptr[1].y = disp_height >> disp_yscale; + vptr[1].z = 0; + } + + /* Send the texture to the GE. */ + + guTexFlush(); + guTexMode(GU_PSM_8888, 0, 0, 1 /*swizzled*/); + if (disp_width <= 512) { + guTexImage(0, 512, 512, disp_width, pixelbuf); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts, NULL, vertices); + } else { + guTexImage(0, 512, 512, disp_width, pixelbuf); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts/2, NULL, vertices); + for (x = disp_width/2, vptr = vertices + nverts/2; x < disp_width; + x += 16, vptr += 2 + ) { + vptr[0].u = x - disp_width/2; + vptr[1].u = (x + 16) - disp_width/2; + } + guTexImage(0, 512, 512, disp_width, pixelbuf + (disp_width/2) * 8); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nverts/2, NULL, vertices + nverts/2); + } + guCommit(); +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * render_mode0: Render a rotated/distorted graphics layer in mode 0 + * (fixed parameter set). + * + * [Parameters] + * pixelbuf: Pointer to output pixel buffer + * info: Graphics layer data + * clip: Clipping window data + * param_set: Array of rotation parameter sets + * which: Which parameter set to use (0 or 1) + * [Return value] + * None + */ +static void render_mode0(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2], int which) +{ + /* + * If the parameter set uses a fixed transformation matrix that doesn't + * actually perform any rotation, we can just call the regular map + * drawing functions with appropriately scaled parameters: + * + * [srcX] ( [screenX]) [kx] [Xp] + * [srcY] = (M [screenY]) * [ky] + [Yp] + * ( [ 1 ]) + * + * [srcX] ([mat11 0 mat13] [screenX]) [kx] [Xp] + * [srcY] = ([ 0 mat22 mat23] [screenY]) * [ky] + [Yp] + * ( [ 1 ]) + * + * srcX = (mat11*screenX + mat13) * kx + Xp + * srcY = (mat22*screenY + mat23) * ky + Yp + * + * srcX = (mat11*kx)*screenX + (mat13*kx + Xp) + * srcY = (mat22*ky)*screenY + (mat23*ky + Yp) + * + * (srcX - (mat13*kx + Xp)) / (mat11*kx) = screenX + * (srcY - (mat23*ky + Yp)) / (mat22*ky) = screenX + * + * screenX = (1/(mat11*kx))*srcX - ((mat13*kx + Xp) / (mat11*kx)) + * screenY = (1/(mat22*ky))*srcX - ((mat23*ky + Yp) / (mat22*ky)) + */ + + RotationParams *param = ¶m_set[which]; + if (!param->coefenab && param->mat12 == 0.0f && param->mat21 == 0.0f) { + const float xmul = + 1 / FIXED_TOFLOAT(FIXED_MULT(param->mat11, param->kx)); + const float ymul = + 1 / FIXED_TOFLOAT(FIXED_MULT(param->mat22, param->ky)); + info->coordincx = xmul; + info->coordincy = ymul; + info->x = -FIXED_TOFLOAT(FIXED_MULT(param->mat13, param->kx) + param->Xp) * xmul; + info->y = -FIXED_TOFLOAT(FIXED_MULT(param->mat23, param->ky) + param->Yp) * ymul; + void (*draw_func)(vdp2draw_struct *info, const clipping_struct *clip); + if (info->isbitmap) { + draw_func = &vdp2_draw_bitmap; + } else if (info->patternwh == 2) { + draw_func = &vdp2_draw_map_16x16; + } else { + draw_func = &vdp2_draw_map_8x8; + } + return (*draw_func)(info, clip); + } + + /* + * There's rotation and/or distortion going on, so we'll have to render + * the image manually. (Sadly, the PSP doesn't have shaders, so we + * can't translate the coefficients into a texture coordinate map and + * render that way.) + */ + + render_mode0_region(pixelbuf, info, param_set, which, + 0, 0, disp_width, disp_height); +} + +/*----------------------------------*/ + +/** + * render_mode0_region: Render the given portion of a rotated/distorted + * graphics layer in mode 0 (fixed parameter set). + * + * [Parameters] + * pixelbuf: Pointer to output pixel buffer + * info: Graphics layer data + * param_set: Array of rotation parameter sets + * which: Which parameter set to use (0 or 1) + * x0, y0: Top-left coordinates of region to render + * xlim, ylim: Bottom-right coordinates of region to render plus one + * [Return value] + * None + */ +static void render_mode0_region(uint32_t *pixelbuf, vdp2draw_struct *info, + RotationParams param_set[2], int which, + unsigned int x0, unsigned int y0, + unsigned int xlim, unsigned int ylim) +{ + RotationParams *param = ¶m_set[which]; + + /* Precalculate tilemap/bitmap coordinate masks, shift counts, and + * tile page addresses. */ + + INIT_CALC_PIXELNUM; + uint32_t pagemap[8][8]; + if (!info->isbitmap) { + INIT_PAGEMAP(pagemap, which); + } + + /* Choose appropriate coefficient-read and pixel-read functions. */ + + int (*get_coef)(RotationParams *, const void *) = + get_coef_table[param->coefdatasize==4][param->coefmode]; + SELECT_GET_PIXEL; + + /* Actually render the graphics layer. */ + + /* These two variables are intentionally float rather than FIXEDFLOAT + * because coef_y can exceed the range of a fixed-point value. */ + float coef_dy = FIXED_TOFLOAT(param->deltaKAst); + float coef_y = y0 * coef_dy; + unsigned int y; + + for (y = y0; y < ylim; y++, coef_y += coef_dy) { + + const uint32_t coef_base = param->coeftbladdr + + (ifloorf(coef_y) << param->coefdatashift); + const uint8_t *coef_baseptr = Vdp2Ram + (coef_base & 0x7FFFF); + uint32_t * const dest_base = pixelbuf + (y/8)*(disp_width*8) + (y%8)*4; + + if (!param->coefenab) { + + /* Constant parameters for the whole screen (FIXME: we could + * draw this in hardware if we had code to rotate vertices) */ + FIXEDFLOAT srcx_f, srcy_f; + transform_coordinates(param, x0, y, &srcx_f, &srcy_f); + const FIXEDFLOAT delta_srcx = FIXED_MULT(param->mat11, param->kx); + const FIXEDFLOAT delta_srcy = FIXED_MULT(param->mat21, param->ky); + unsigned int x; + for (x = x0; x < xlim; + x++, srcx_f += delta_srcx, srcy_f += delta_srcy + ) { + uint32_t *dest = dest_base + (x/4)*32 + x%4; + int srcx = FIXED_TOINT(srcx_f); + int srcy = FIXED_TOINT(srcy_f); + unsigned int pixelnum; + CALC_PIXELNUM(pagemap); + *dest = (*get_pixel)(info, pixelnum); + } + + } else if (param->deltaKAx == 0) { + + /* One coefficient for the whole row */ + if (!(*get_coef)(param, coef_baseptr)) { + /* Empty row */ + uint32_t *dest = dest_base + (x0/4)*32; + unsigned int x = x0; + if (UNLIKELY(x & 3)) { + for (; x & 3; x++) { + dest[x & 3] = 0; + } + dest += 32; + } + for (; x < (xlim & ~3); x += 4, dest += 32) { + dest[0] = dest[1] = dest[2] = dest[3] = 0; + } + for (; x < xlim; x++) { + dest[x & 3] = 0; + } + continue; + } + FIXEDFLOAT srcx_f, srcy_f; + transform_coordinates(param, x0, y, &srcx_f, &srcy_f); + const FIXEDFLOAT delta_srcx = FIXED_MULT(param->mat11, param->kx); + const FIXEDFLOAT delta_srcy = FIXED_MULT(param->mat21, param->ky); + unsigned int x; + for (x = x0; x < xlim; + x++, srcx_f += delta_srcx, srcy_f += delta_srcy + ) { + uint32_t *dest = dest_base + (x/4)*32 + x%4; + int srcx = FIXED_TOINT(srcx_f); + int srcy = FIXED_TOINT(srcy_f); + unsigned int pixelnum; + CALC_PIXELNUM(pagemap); + *dest = (*get_pixel)(info, pixelnum); + } + + } else { // param->coefenab && param->deltaKAx != 0 + + /* Multiple coefficients per row */ + const FIXEDFLOAT coef_dx = param->deltaKAx; + FIXEDFLOAT coef_x = 0; + int last_coef_x = -1; + int empty_pixel = 0; + unsigned int x; + for (x = x0; x < xlim; x++, coef_x += coef_dx) { + uint32_t *dest = dest_base + (x/4)*32 + x%4; + if (FIXED_TOINT(coef_x) != last_coef_x) { + last_coef_x = FIXED_TOINT(coef_x); + const uint8_t *coef_ptr = + coef_baseptr + (last_coef_x << param->coefdatashift); + empty_pixel = !(*get_coef)(param, coef_ptr); + } + if (empty_pixel) { + *dest = 0; + } else { + FIXEDFLOAT srcx_f, srcy_f; + transform_coordinates(param, x, y, &srcx_f, &srcy_f); + int srcx = FIXED_TOINT(srcx_f); + int srcy = FIXED_TOINT(srcy_f); + unsigned int pixelnum; + CALC_PIXELNUM(pagemap); + *dest = (*get_pixel)(info, pixelnum); + } + } + + } // if (!param->coefenab) + + } // for (y = y0; y < ylim; y++, coef_y += coef_dy) +} + +/*-----------------------------------------------------------------------*/ + +/** + * render_mode1: Render a rotated/distorted graphics layer in mode 1 + * (parameter set selected by top bit of coefficient). + * + * [Parameters] + * pixelbuf: Pointer to output pixel buffer + * info: Graphics layer data + * clip: Clipping window data + * param_set: Array of rotation parameter sets + * [Return value] + * None + */ +static void render_mode1(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2]) +{ + /* Set up a second vdp2draw_struct for the second parameter set. */ + + vdp2draw_struct *info0 = info; + vdp2draw_struct info1_buf = *info; + vdp2draw_struct *info1 = &info1_buf; + info1->charaddr = ((Vdp2Regs->MPOFR >> 4) & 7) << 17; + info1->planew_bits = (Vdp2Regs->PLSZ >> 12) & 1; + info1->planeh_bits = (Vdp2Regs->PLSZ >> 13) & 1; + info1->planew = 1 << info->planew_bits; + info1->planeh = 1 << info->planeh_bits; + if (info1->planew != info0->planew || info1->planeh != info0->planeh) { + DMSG("WARNING: mixed plane sizes not supported for RPMD=2" + " (set A: %dx%d, set B: %dx%d)", info0->planew, info0->planeh, + info1->planew, info1->planeh); + } + + /* If set A has one coefficient per row, it might simply be splitting + * the screen into two differently-rendered regions (such as sky and + * ground). We can potentially draw that more efficiently as mode 0. */ + + int top_set, switch_y; + if (mode1_is_split_screen(param_set, &top_set, &switch_y)) { + if (switch_y < 0) { + return render_mode0(pixelbuf, info, clip, param_set, top_set); + } else { + render_mode0_region(pixelbuf, top_set==0 ? info0 : info1, + param_set, top_set, + 0, 0, disp_width, switch_y); + return render_mode0_region(pixelbuf, top_set==0 ? info1 : info0, + param_set, top_set ^ 1, + 0, switch_y, disp_width, disp_height); + } + } + + /* Precalculate tilemap/bitmap coordinate masks, shift counts, and + * tile page addresses. */ + + INIT_CALC_PIXELNUM; + uint32_t pagemap[2][8][8]; + if (!info->isbitmap) { + INIT_PAGEMAP(pagemap[0], 0); + INIT_PAGEMAP(pagemap[1], 1); + } + + /* Choose appropriate coefficient-read and pixel-read functions. */ + + int (*get_coef0)(RotationParams *, const void *) = + get_coef_table[param_set[0].coefdatasize==4][param_set[0].coefmode]; + int (*get_coef1)(RotationParams *, const void *) = + get_coef_table[param_set[1].coefdatasize==4][param_set[1].coefmode]; + SELECT_GET_PIXEL; + + /* Actually render the graphics layer. */ + + /* These are intentionally float rather than FIXEDFLOAT, as in + * render_mode0_region(). */ + float coef0_dy = FIXED_TOFLOAT(param_set[0].deltaKAst); + float coef0_y = 0; + float coef1_dy = FIXED_TOFLOAT(param_set[1].deltaKAst); + float coef1_y = 0; + unsigned int y; + + for (y = 0; y < disp_height; + y++, coef0_y += coef0_dy, coef1_y += coef1_dy + ) { + + const uint32_t coef0_base = param_set[0].coeftbladdr + + (ifloorf(coef0_y) << param_set[0].coefdatashift); + const uint8_t *coef0_baseptr = Vdp2Ram + (coef0_base & 0x7FFFF); + const uint32_t coef1_base = param_set[1].coeftbladdr + + (ifloorf(coef1_y) << param_set[1].coefdatashift); + const uint8_t *coef1_baseptr = Vdp2Ram + (coef1_base & 0x7FFFF); + uint32_t * const dest_base = pixelbuf + (y/8)*(disp_width*8) + (y%8)*4; + + if (param_set[0].deltaKAx == 0 + && (!param_set[1].coefenab || param_set[1].deltaKAx == 0) + ) { + + /* One coefficient for the whole row in both sets */ + int which; + if ((*get_coef0)(¶m_set[0], coef0_baseptr)) { + which = 0; + info = info0; + } else if (!param_set[1].coefenab + || (*get_coef1)(¶m_set[1], coef1_baseptr)) { + which = 1; + info = info1; + } else { + /* Empty row */ + uint32_t *dest = dest_base; + unsigned int x; + for (x = 0; x < disp_width; x += 4, dest += 32) { + dest[0] = dest[1] = dest[2] = dest[3] = 0; + } + continue; + } + RotationParams *param = ¶m_set[which]; + FIXEDFLOAT srcx_f, srcy_f; + transform_coordinates(param, 0, y, &srcx_f, &srcy_f); + const FIXEDFLOAT delta_srcx = FIXED_MULT(param->mat11, param->kx); + const FIXEDFLOAT delta_srcy = FIXED_MULT(param->mat21, param->ky); + unsigned int x; + for (x = 0; x < disp_width; + x++, srcx_f += delta_srcx, srcy_f += delta_srcy + ) { + uint32_t *dest = dest_base + (x/4)*32 + x%4; + int srcx = FIXED_TOINT(srcx_f); + int srcy = FIXED_TOINT(srcy_f); + unsigned int pixelnum; + CALC_PIXELNUM(pagemap[which]); + *dest = (*get_pixel)(info, pixelnum); + } + + } else { + + /* Multiple coefficients per row in one or both sets */ + const FIXEDFLOAT coef0_dx = param_set[0].deltaKAx; + FIXEDFLOAT coef0_x = 0; + const FIXEDFLOAT coef1_dx = param_set[1].deltaKAx; + FIXEDFLOAT coef1_x = 0; + int last_coef0_x = -1; + int last_coef1_x = (param_set[1].coefenab ? -1 : 0); + int have_coef0 = 1; + int have_coef1 = 1; + unsigned int x; + for (x = 0; x < disp_width; + x++, coef0_x += coef0_dx, coef1_x += coef1_dx + ) { + uint32_t *dest = dest_base + (x/4)*32 + x%4; + if (FIXED_TOINT(coef0_x) != last_coef0_x) { + last_coef0_x = FIXED_TOINT(coef0_x); + const uint8_t *coef0_ptr = coef0_baseptr + + (last_coef0_x << param_set[0].coefdatashift); + have_coef0 = (*get_coef0)(¶m_set[0], coef0_ptr); + } + if (FIXED_TOINT(coef1_x) != last_coef1_x) { + last_coef1_x = FIXED_TOINT(coef1_x); + const uint8_t *coef1_ptr = coef1_baseptr + + (last_coef1_x << param_set[1].coefdatashift); + have_coef1 = (*get_coef1)(¶m_set[1], coef1_ptr); + } + int which; + if (have_coef0) { + which = 0; + info = info0; + } else if (have_coef1) { + which = 1; + info = info1; + } else { // Empty pixel + *dest = 0; + continue; + } + RotationParams *param = ¶m_set[which]; + FIXEDFLOAT srcx_f, srcy_f; + transform_coordinates(param, x, y, &srcx_f, &srcy_f); + int srcx = FIXED_TOINT(srcx_f); + int srcy = FIXED_TOINT(srcy_f); + unsigned int pixelnum; + CALC_PIXELNUM(pagemap[which]); + *dest = (*get_pixel)(info, pixelnum); + } + + } + + } // for (y = 0; y < disp_height; y++, coef_y += coef_dy) +} + +/*----------------------------------*/ + +/** + * mode1_is_split_screen: Return whether the parameters and coefficients + * for a mode 1 rotated/distorted graphics layer have the effect of + * splitting the screen into a top and bottom region, each with one of the + * two parameter sets. Parameters which specify a single parameter set for + * the entire screen are considered to be a split screen with an empty + * bottom portion for the purposes of this function. + * + * If this function returns true, *top_set_ret will be set to the index of + * the parameter set used for the top line of the screen, and *switch_y_ret + * will be set to the first line at which the other parameter set is used. + * If the entire screen uses a single parameter set (effectively mode 0), + * *switch_y_ret will be set to -1. + * + * [Parameters] + * param_set: Array of rotation parameter sets + * top_set_ret: Pointer to variable to receive the index of the + * parameter set used on screen line 0 + * switch_y_ret: Pointer to variable to receive the first line at which + * the alternate parameter set is used + * [Return value] + * True (nonzero) if the rotation parameters have the effect of + * splitting the screen, else false (zero) + */ +static int mode1_is_split_screen(const RotationParams param_set[2], + int *top_set_ret, int *switch_y_ret) +{ + /* If parameter set A doesn't have coefficients enabled, set B can + * never be selected, so this is just the same as mode 0 with set A. */ + + if (!param_set[0].coefenab) { + *top_set_ret = 0; + *switch_y_ret = -1; + return 1; + } + + /* If there is more than one coefficient per line, assume that the + * rotation operation is more complex than a simple split-screen effect. */ + + if (param_set[0].deltaKAx != 0) { + return 0; + } + + /* Scan over the set A coefficients (now known to be one per line) and + * see how many times the selected set changes. */ + + int cur_set = -1; + *top_set_ret = -1; + *switch_y_ret = -1; + + /* These are intentionally float rather than FIXEDFLOAT, as in + * render_mode0_region(). */ + float coef0_dy = FIXED_TOFLOAT(param_set[0].deltaKAst); + float coef0_y = 0; + int y; + + for (y = 0; y < disp_height; y++, coef0_y += coef0_dy) { + + const uint32_t coef0_addr = param_set[0].coeftbladdr + + (ifloorf(coef0_y) << param_set[0].coefdatashift); + const uint8_t * const coef0_ptr = Vdp2Ram + (coef0_addr & 0x7FFFF); + /* VDP memory is organized by bytes, so coef0_ptr is now pointing + * to the top byte of the coefficient value regardless of the data + * size setting. */ + const int set = (*(int8_t *)coef0_ptr < 0) ? 1 : 0; + + if (y == 0) { + *top_set_ret = cur_set = set; + } else if (set != cur_set) { + if (*switch_y_ret < 0) { + *switch_y_ret = y; + } else { + /* This is the second change we've seen, so it's not a + * split-screen effect. */ +printf("set change 2 at %d\n",y); + return 0; + } + cur_set = set; + } + + } + + /* If we got this far, it must be a split-screen effect. */ + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * render_mode2: Render a rotated/distorted graphics layer in mode 2 + * (parameter set selected by clipping window). + * + * [Parameters] + * pixelbuf: Pointer to output pixel buffer + * info: Graphics layer data + * clip: Clipping window data + * param_set: Array of rotation parameter sets + * [Return value] + * None + */ +static void render_mode2(uint32_t *pixelbuf, vdp2draw_struct *info, + const clipping_struct *clip, + RotationParams param_set[2]) +{ + /* + * FIXME: This logic gets the Panzer Dragoon Saga name entry screen's + * background to display properly, but since PDS is the only example + * of this mode to which I have access, I have no clue whether this is + * the Right Thing To Do. I'm using the height of the clip window (set + * as <0,0>-<352,224> by the game) based on vidsoft.c comments, but it + * could also be just a top-half/bottom-half-of-screen thing. + */ + + render_mode0_region(pixelbuf, info, param_set, 0, + 0, 0, disp_width, clip[0].yend / 2); + + info->charaddr = ((Vdp2Regs->MPOFR >> 4) & 7) << 17; + info->planew_bits = (Vdp2Regs->PLSZ >> 12) & 1; + info->planeh_bits = (Vdp2Regs->PLSZ >> 13) & 1; + info->planew = 1 << info->planew_bits; + info->planeh = 1 << info->planeh_bits; + render_mode0_region(pixelbuf, info, param_set, 1, + 0, clip[0].yend / 2, disp_width, disp_height); +} + +/*************************************************************************/ + +/** + * get_rotation_parameters: Retrieve the rotation parameters for a rotated + * graphics layer and perform necessary precalculations. + * + * [Parameters] + * info: Graphics layer data + * which: Which parameter set to retrieve (0 or 1) + * param_ret: Pointer to structure to receive parsed parameters + * [Return value] + * None + */ +static void get_rotation_parameters(vdp2draw_struct *info, int which, + RotationParams *param_ret) +{ + uint32_t addr = (Vdp2Regs->RPTA.all << 1) & 0x7FF7C; + unsigned int KTCTL, KTAOF; + if (which == 0) { + KTCTL = Vdp2Regs->KTCTL & 0xFF; + KTAOF = Vdp2Regs->KTAOF & 0xFF; + param_ret->screenover = (Vdp2Regs->PLSZ >> 10) & 3; + } else { + addr |= 0x80; + KTCTL = (Vdp2Regs->KTCTL >> 8) & 0xFF; + KTAOF = (Vdp2Regs->KTAOF >> 8) & 0xFF; + param_ret->screenover = (Vdp2Regs->PLSZ >> 14) & 3; + } + param_ret->coefenab = KTCTL & 1; + param_ret->coefdatashift = (KTCTL & 2) ? 1 : 2; + param_ret->coefdatasize = 1 << param_ret->coefdatashift; + param_ret->coefmode = (KTCTL >> 2) & 3; + param_ret->coeftbladdr = ((KTAOF & 7) << param_ret->coefdatashift) << 16; + +#ifdef USE_FIXED_POINT + #define GET_SHORT(nbits) \ + (addr += 2, (int32_t)((int16_t)T1ReadWord(Vdp2Ram,addr-2) \ + << (16-nbits)) << nbits) + #define GET_SIGNED_FLOAT(nbits) \ + (addr += 4, ((((int32_t)T1ReadLong(Vdp2Ram,addr-4) \ + << (32-nbits)) >> (32-nbits)) & ~0x3F)) + #define GET_UNSIGNED_FLOAT(nbits) \ + (addr += 4, (((uint32_t)T1ReadLong(Vdp2Ram,addr-4) \ + & (0xFFFFFFFFU >> (32-nbits))) & ~0x3F)) +#else // !USE_FIXED_POINT + #define GET_SHORT(nbits) \ + (addr += 2, ((int16_t)T1ReadWord(Vdp2Ram,addr-2) \ + << (16-nbits)) >> (16-nbits)) + #define GET_SIGNED_FLOAT(nbits) \ + (addr += 4, ((((int32_t)T1ReadLong(Vdp2Ram,addr-4) \ + << (32-nbits)) >> (32-nbits)) & ~0x3F) / 65536.0f) + #define GET_UNSIGNED_FLOAT(nbits) \ + (addr += 4, (((uint32_t)T1ReadLong(Vdp2Ram,addr-4) \ + & (0xFFFFFFFFU >> (32-nbits))) & ~0x3F) / 65536.0f) +#endif // !USE_FIXED_POINT + + param_ret->Xst = GET_SIGNED_FLOAT(29); + param_ret->Yst = GET_SIGNED_FLOAT(29); + param_ret->Zst = GET_SIGNED_FLOAT(29); + param_ret->deltaXst = GET_SIGNED_FLOAT(19); + param_ret->deltaYst = GET_SIGNED_FLOAT(19); + param_ret->deltaX = GET_SIGNED_FLOAT(19); + param_ret->deltaY = GET_SIGNED_FLOAT(19); + param_ret->A = GET_SIGNED_FLOAT(20); + param_ret->B = GET_SIGNED_FLOAT(20); + param_ret->C = GET_SIGNED_FLOAT(20); + param_ret->D = GET_SIGNED_FLOAT(20); + param_ret->E = GET_SIGNED_FLOAT(20); + param_ret->F = GET_SIGNED_FLOAT(20); + param_ret->Px = GET_SHORT(14); + param_ret->Py = GET_SHORT(14); + param_ret->Pz = GET_SHORT(14); + addr += 2; + param_ret->Cx = GET_SHORT(14); + param_ret->Cy = GET_SHORT(14); + param_ret->Cz = GET_SHORT(14); + addr += 2; + param_ret->Mx = GET_SIGNED_FLOAT(30); + param_ret->My = GET_SIGNED_FLOAT(30); + param_ret->kx = GET_SIGNED_FLOAT(24); + param_ret->ky = GET_SIGNED_FLOAT(24); + if (param_ret->coefenab) { + param_ret->KAst = GET_UNSIGNED_FLOAT(32); + param_ret->deltaKAst = GET_SIGNED_FLOAT(26); + param_ret->deltaKAx = GET_SIGNED_FLOAT(26); + param_ret->coeftbladdr += + FIXED_TOINT(param_ret->KAst) * param_ret->coefdatasize; + } else { + param_ret->KAst = 0; + param_ret->deltaKAst = 0; + param_ret->deltaKAx = 0; + } + + #undef GET_SHORT + #undef GET_SIGNED_FLOAT + #undef GET_UNSIGNED_FLOAT + + /* + * The coordinate transformation performed for rotated graphics layers + * works out to the following: + * + * [srcX] ( [screenX]) [kx] [Xp] + * [srcY] = (M [screenY]) * [ky] + [Yp] + * ( [ 1 ]) + * + * where the "*" operator is multiplication by components (not matrix + * multiplication), M is the 2x3 constant matrix product: + * + * [A B C] [deltaX deltaXst (Xst - Px)] + * M = [D E F] [deltaY deltaYst (Yst - Py)] + * [ 0 0 (Zst - Pz)] + * + * and is a constant vector computed as: + * + * [Xp] ([A B C] [Px - Cx]) [Cx] [Mx] + * [Yp] = ([D E F] [Py - Cy]) + [Cy] + [My] + * ( [Pz - Cz]) + */ + + param_ret->mat11 = FIXED_MULT(param_ret->A, param_ret->deltaX) + + FIXED_MULT(param_ret->B, param_ret->deltaY); + param_ret->mat12 = FIXED_MULT(param_ret->A, param_ret->deltaXst) + + FIXED_MULT(param_ret->B, param_ret->deltaYst); + param_ret->mat13 = FIXED_MULT(param_ret->A, (param_ret->Xst - param_ret->Px)) + + FIXED_MULT(param_ret->B, (param_ret->Yst - param_ret->Py)) + + FIXED_MULT(param_ret->C, (param_ret->Zst - param_ret->Pz)); + param_ret->mat21 = FIXED_MULT(param_ret->D, param_ret->deltaX) + + FIXED_MULT(param_ret->E, param_ret->deltaY); + param_ret->mat22 = FIXED_MULT(param_ret->D, param_ret->deltaXst) + + FIXED_MULT(param_ret->E, param_ret->deltaYst); + param_ret->mat23 = FIXED_MULT(param_ret->D, (param_ret->Xst - param_ret->Px)) + + FIXED_MULT(param_ret->E, (param_ret->Yst - param_ret->Py)) + + FIXED_MULT(param_ret->F, (param_ret->Zst - param_ret->Pz)); + param_ret->Xp = FIXED_MULT(param_ret->A, (param_ret->Px - param_ret->Cx)) + + FIXED_MULT(param_ret->B, (param_ret->Py - param_ret->Cy)) + + FIXED_MULT(param_ret->C, (param_ret->Pz - param_ret->Cz)) + + param_ret->Cx + + param_ret->Mx; + param_ret->Yp = FIXED_MULT(param_ret->D, (param_ret->Px - param_ret->Cx)) + + FIXED_MULT(param_ret->E, (param_ret->Py - param_ret->Cy)) + + FIXED_MULT(param_ret->F, (param_ret->Pz - param_ret->Cz)) + + param_ret->Cy + + param_ret->My; +} + +/*-----------------------------------------------------------------------*/ + +/** + * get_rotation_coefficient: Retrieve a single rotation coefficient for a + * rotated graphics layer and update the rotation parameter set accordingly. + * + * This function is only to demonstrate the behavior of coefficient + * processing, and is not actually called; the per-mode-and-size optimized + * versions below are used instead. + * + * [Parameters] + * param: Rotation parameter set + * address: Address in VDP2 RAM of coefficient + * [Return value] + * Zero if this pixel is blank, else nonzero + */ +__attribute__((unused)) +static int get_rotation_coefficient(RotationParams *param, uint32_t address) +{ + FIXEDFLOAT value; + if (param->coefdatasize == 2) { + const int16_t data = T1ReadWord(Vdp2Ram, address & 0x7FFFE); + if (data < 0) { + return 0; + } +#ifdef USE_FIXED_POINT + value = (int32_t)(data << 1) << 5; +#else + value = (float)((data << 1) >> 1) / 1024.0f; +#endif + } else { + const int32_t data = T1ReadLong(Vdp2Ram, address & 0x7FFFC); + if (data < 0) { + return 0; + } +#ifdef USE_FIXED_POINT + value = (data << 8) >> 8; +#else + value = (float)((data << 8) >> 8) / 65536.0f; +#endif + } + switch (param->coefmode) { + case 0: param->kx = param->ky = value; break; + case 1: param->kx = value; break; + case 2: param->ky = value; break; + case 3: +#ifdef USE_FIXED_POINT + param->Xp = value << 8; +#else + param->Xp = value * 256.0f; +#endif + break; + } + return 1; +} + +/*----------------------------------*/ + +/** + * get_rotation_coefficient_sizeX_modeY: Retrieve a single rotation + * coefficient for a rotated graphics layer and update the rotation + * parameter set accordingly. Each routine is optimized for the specific + * case of param->coefdatasize == X and param->coefmode == Y. + * + * [Parameters] + * param: Rotation parameter set + * address: Native address of coefficient + * [Return value] + * Zero if this pixel is blank, else nonzero + */ + +static int get_rotation_coefficient_size2_mode0(RotationParams *param, + const void *address) +{ + const int16_t data = BSWAP16(*(const int16_t *)address); + if (data < 0) { + return 0; + } + param->kx = param->ky = +#ifdef USE_FIXED_POINT + (int32_t)(data << 1) << 5; +#else + (float)(data << 1) / 2048.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size2_mode1(RotationParams *param, + const void *address) +{ + const int16_t data = BSWAP16(*(const int16_t *)address); + if (data < 0) { + return 0; + } + param->kx = +#ifdef USE_FIXED_POINT + (int32_t)(data << 1) << 5; +#else + (float)(data << 1) / 2048.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size2_mode2(RotationParams *param, + const void *address) +{ + const int16_t data = BSWAP16(*(const int16_t *)address); + if (data < 0) { + return 0; + } + param->ky = +#ifdef USE_FIXED_POINT + (int32_t)(data << 1) << 5; +#else + (float)(data << 1) / 2048.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size2_mode3(RotationParams *param, + const void *address) +{ + const int16_t data = BSWAP16(*(const int16_t *)address); + if (data < 0) { + return 0; + } + param->Xp = +#ifdef USE_FIXED_POINT + (int32_t)(data << 1) << 13; +#else + (float)(data << 1) / 8.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size4_mode0(RotationParams *param, + const void *address) +{ + const int32_t data = BSWAP32(*(const int32_t *)address); + if (data < 0) { + return 0; + } + param->kx = param->ky = +#ifdef USE_FIXED_POINT + (data << 8) >> 8; +#else + (float)(data << 8) / 16777216.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size4_mode1(RotationParams *param, + const void *address) +{ + const int32_t data = BSWAP32(*(const int32_t *)address); + if (data < 0) { + return 0; + } + param->kx = +#ifdef USE_FIXED_POINT + (data << 8) >> 8; +#else + (float)(data << 8) / 16777216.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size4_mode2(RotationParams *param, + const void *address) +{ + const int32_t data = BSWAP32(*(const int32_t *)address); + if (data < 0) { + return 0; + } + param->ky = +#ifdef USE_FIXED_POINT + (data << 8) >> 8; +#else + (float)(data << 8) / 16777216.0f; +#endif + return 1; +} + +static int get_rotation_coefficient_size4_mode3(RotationParams *param, + const void *address) +{ + const int32_t data = BSWAP32(*(const int32_t *)address); + if (data < 0) { + return 0; + } + param->Xp = +#ifdef USE_FIXED_POINT + data << 8; +#else + (float)(data << 8) / 65536.0f; +#endif + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * transform_coordinates: Transform screen coordinates to source + * coordinates based on the current rotation parameters. + * + * [Parameters] + * param: Rotation parameter set + * x, y: Screen coordinates + * srcx_ret, srcy_ret: Pointers to variables to receive source coordinates + * [Return value] + * None + */ +static void transform_coordinates(const RotationParams *param, int x, int y, + FIXEDFLOAT *srcx_ret, FIXEDFLOAT *srcy_ret) +{ + *srcx_ret = FIXED_MULT(param->mat11*x + param->mat12*y + param->mat13, + param->kx) + param->Xp; + *srcy_ret = FIXED_MULT(param->mat21*x + param->mat22*y + param->mat23, + param->ky) + param->Yp; +} + +/*************************************************************************/ + +/** + * rotation_get_pixel_*: Retrieve a pixel from tiles or bitmaps of various + * pixel formats, with transparency disabled. info->charaddr is assumed to + * contain the offset of the tile or bitmap in VDP2 RAM. + * + * [Parameters] + * info: Graphics layer data + * pixelnum: Index of pixel to retrieve (y*w+x) + * [Return value] + * Pixel value as 0xAABBGGRR + */ + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t4(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum/2; + /* For speed, we assume the tile/bitmap won't wrap around the end of + * VDP2 RAM */ + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t byte = *ptr; + const uint8_t pixel = (pixelnum & 1) ? byte & 0x0F : byte >> 4; + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t8(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum; + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t pixel = *ptr; + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t16(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + const unsigned int colornum = info->coloroffset + pixel; + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_16(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + return 0xFF000000 + | (pixel & 0x7C00) << 9 + | (pixel & 0x03E0) << 6 + | (pixel & 0x001F) << 3; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_32(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*4; + const uint32_t *ptr = (const uint32_t *)(Vdp2Ram + address); + const uint32_t pixel = BSWAP32(*ptr); + return 0xFF000000 | pixel; +} + +/*-----------------------------------------------------------------------*/ + +/** + * rotation_get_pixel_*_transparent: Retrieve a pixel from tiles or + * bitmaps of various pixel formats, with transparency enabled. + * info->charaddr is assumed to contain the offset of the tile or bitmap in + * VDP2 RAM. + * + * [Parameters] + * info: Graphics layer data + * pixelnum: Index of pixel to retrieve (y*w+x) + * [Return value] + * Pixel value as 0xAABBGGRR + */ + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t4_transparent(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum/2; + /* For speed, we assume the tile/bitmap won't wrap around the end of + * VDP2 RAM */ + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t byte = *ptr; + const uint8_t pixel = (pixelnum & 1) ? byte & 0x0F : byte >> 4; + if (!pixel) { + return 0x00000000; + } + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t8_transparent(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum; + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t pixel = *ptr; + if (!pixel) { + return 0x00000000; + } + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t16_transparent(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + if (!pixel) { + return 0x00000000; + } + const unsigned int colornum = info->coloroffset + pixel; + return global_clut_32[colornum & 0x7FF]; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_16_transparent(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + if (!(pixel & 0x8000)) { + return 0x00000000; + } + return 0xFF000000 + | (pixel & 0x7C00) << 9 + | (pixel & 0x03E0) << 6 + | (pixel & 0x001F) << 3; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_32_transparent(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*4; + const uint32_t *ptr = (const uint32_t *)(Vdp2Ram + address); + const uint32_t pixel = BSWAP32(*ptr); + if (!(pixel & 0x80000000)) { + return 0x00000000; + } + return 0xFF000000 | pixel; +} + +/*-----------------------------------------------------------------------*/ + +/** + * rotation_get_pixel_*_adjust: Retrieve a pixel from tiles or bitmaps of + * various pixel formats, and apply alpha and color adjustments. + * info->charaddr is assumed to contain the offset of the tile or bitmap in + * VDP2 RAM. + * + * [Parameters] + * info: Graphics layer data + * pixelnum: Index of pixel to retrieve (y*w+x) + * [Return value] + * Pixel value as 0xAABBGGRR + */ + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t4_adjust(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum/2; + /* For speed, we assume the tile/bitmap won't wrap around the end of + * VDP2 RAM */ + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t byte = *ptr; + const uint8_t pixel = (pixelnum & 1) ? byte & 0x0F : byte >> 4; + if (!pixel && info->transparencyenable) { + return 0x00000000; + } + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return (adjust_color_32_32(global_clut_32[colornum & 0x7FF], + info->cor, info->cog, info->cob) & 0xFFFFFF) + | info->alpha << 24; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t8_adjust(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum; + const uint8_t *ptr = (const uint8_t *)(Vdp2Ram + address); + const uint8_t pixel = *ptr; + if (!pixel && info->transparencyenable) { + return 0x00000000; + } + const unsigned int colornum = + info->coloroffset + (info->paladdr<<4 | pixel); + return (adjust_color_32_32(global_clut_32[colornum & 0x7FF], + info->cor, info->cog, info->cob) & 0xFFFFFF) + | info->alpha << 24; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_t16_adjust(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + if (!pixel && info->transparencyenable) { + return 0x00000000; + } + const unsigned int colornum = info->coloroffset + pixel; + return (adjust_color_32_32(global_clut_32[colornum & 0x7FF], + info->cor, info->cog, info->cob) & 0xFFFFFF) + | info->alpha << 24; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_16_adjust(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*2; + const uint16_t *ptr = (const uint16_t *)(Vdp2Ram + address); + const uint16_t pixel = BSWAP16(*ptr); + if (!(pixel & 0x8000) && info->transparencyenable) { + return 0x00000000; + } + return (adjust_color_16_32(pixel, + info->cor, info->cog, info->cob) & 0xFFFFFF) + | info->alpha << 24; +} + +/*----------------------------------*/ + +static uint32_t rotation_get_pixel_32_adjust(vdp2draw_struct *info, + unsigned int pixelnum) +{ + const uint32_t address = info->charaddr + pixelnum*4; + const uint32_t *ptr = (const uint32_t *)(Vdp2Ram + address); + const uint32_t pixel = BSWAP32(*ptr); + if (!(pixel & 0x80000000) && info->transparencyenable) { + return 0x00000000; + } + return (adjust_color_32_32(pixel, + info->cor, info->cog, info->cob) & 0xFFFFFF) + | info->alpha << 24; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video-tilemap.c b/yabause/src/psp/psp-video-tilemap.c new file mode 100644 index 0000000000..2a83c1b9b9 --- /dev/null +++ b/yabause/src/psp/psp-video-tilemap.c @@ -0,0 +1,551 @@ +/* src/psp/psp-video-tilemap.c: Tile-mapped background graphics handling + for PSP video module + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../vidshared.h" + +#include "display.h" +#include "gu.h" +#include "psp-video.h" +#include "psp-video-internal.h" +#include "texcache.h" + +/*************************************************************************/ +/************************** Tile-drawing macros **************************/ +/*************************************************************************/ + +/* + * Macros to work through the Saturn's remarkably nested tile layout (used + * in map drawing routines). Use like: + * + * TILE_LOOP_BEGIN(tilesize) { + * ... // Draw a single tile based on "info" and "vertices" + * } TILE_LOOP_END; + * + * where "tilesize" is 8 or 16 (info->patternwh * 8). Note that we iterate + * over the highest-level "maps" twice to handle wraparound. + */ + +#define TILE_LOOP_BEGIN(tilesize) \ + int x1start = info->x; \ + unsigned int y1; \ + for (y1 = 0; y1 < info->mapwh*2 && info->y < info->drawh; y1++) { \ + int y2start = info->y; \ + info->x = x1start; \ + unsigned int x1; \ + for (x1 = 0; x1 < info->mapwh*2 && info->x < info->draww; x1++) {\ + info->PlaneAddr(info, ((y1 % info->mapwh) * info->mapwh) \ + + (x1 % info->mapwh)); \ + int x2start = info->x; \ + info->y = y2start; \ + unsigned int y2; \ + for (y2 = 0; y2 < info->planeh; y2++) { \ + int y3start = info->y; \ + info->x = x2start; \ + unsigned int x2; \ + for (x2 = 0; x2 < info->planew; x2++) { \ + int x3start = info->x; \ + info->y = y3start; \ + unsigned int y3; \ + for (y3 = 0; y3 < info->pagewh; \ + y3++, info->y += (tilesize) \ + ) { \ + if (UNLIKELY(info->y <= -(tilesize) \ + || info->y >= info->drawh)) { \ + info->addr += info->patterndatasize * 2 \ + * info->pagewh; \ + continue; \ + } \ + info->x = x3start; \ + unsigned int x3; \ + for (x3 = 0; x3 < info->pagewh; \ + x3++, info->x += (tilesize), \ + info->addr += info->patterndatasize * 2 \ + ) { \ + if (UNLIKELY(info->x <= -(tilesize) \ + || info->x >= info->draww)) { \ + continue; \ + } \ + vdp2_calc_pattern_address(info); + +#define TILE_LOOP_END \ + } /* Inner (pattern) X */ \ + } /* Inner (pattern) Y */ \ + } /* Middle (page) X */ \ + } /* Middle (page) Y */ \ + } /* Outer (plane) X */ \ + } /* Outer (plane) Y */ + +/*-----------------------------------------------------------------------*/ + +/* Additional macros used by tile map drawing routines */ + +/*----------------------------------*/ + +/* Set up the clipping region for the graphics layer; half_height should be + * 1 when rendering T8 tiles with the double-stride, two-pass optimization */ +#define SET_CLIP_REGION(half_height) \ + guScissor(clip->xstart, \ + half_height ? clip->ystart/2 : clip->ystart, \ + clip->xend - clip->xstart, \ + half_height ? clip->yend/2 - clip->ystart/2 \ + : clip->yend - clip->ystart) + +/* Reset the clipping region to default */ +#define UNSET_CLIP_REGION \ + guScissor(0, 0, disp_width, disp_height) + +/*----------------------------------*/ + +/* Declare tiledatasize as the size of a single tile's data in bytes. */ +#define GET_TILEDATASIZE \ + int tiledatasize; \ + switch (info->colornumber) { \ + case 0: /* 4bpp */ tiledatasize = 8*8/2; break; \ + case 1: /* 8bpp */ tiledatasize = 8*8*1; break; \ + case 2: /* 16bpp */ tiledatasize = 8*8*2; break; \ + case 3: /* 16bpp */ tiledatasize = 8*8*2; break; \ + case 4: /* 32bpp */ tiledatasize = 8*8*4; break; \ + default: DMSG("Bad tile pixel type %d", info->colornumber); \ + tiledatasize = 0; break; \ + } + +/* Allocate memory for "vertspertile" vertices per "size"x"size" tile. */ +#define GET_VERTICES(size,vertspertile) \ + /* We add 4 here to handle up to 15 pixels of partial tiles \ + * on each edge of the display area */ \ + const int tilew = info->draww / (size) + 4; \ + const int tileh = info->drawh / (size) + 4; \ + const int nvertices = tilew * tileh * (vertspertile); \ + VertexUVXYZ *vertices = pspGuGetMemoryMerge(sizeof(*vertices) * nvertices); + +/* Initialize variables for 8-bit indexed tile palette handling. There can + * be up to 128 different palettes, selectable on a per-tile basis, so to + * save time, we only create (on the fly) those which are actually used. */ +#define INIT_T8_PALETTE \ + uint32_t *palettes[128]; \ + memset(palettes, 0, sizeof(palettes)); \ + int cur_palette = -1; /* So it's always set the first time */ \ + guClutMode(GU_PSM_8888, 0, 0xFF, 0); + +/* Set the texture pixel format for 8-bit indexed tiles. 16-byte-wide + * textures are effectively swizzled already, so set the swizzled flag for + * whatever speed boost it gives us. */ +#define INIT_T8_TEXTURE \ + guTexMode(GU_PSM_T8, 0, 0, 1); + +/* Set the vertex type for 8-bit indexed tiles. */ +#define INIT_T8_VERTEX \ + guVertexFormat(GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D); + +/* Initialize the work buffer pointers for 8-bit indexed tiles. */ +#define INIT_T8_WORK_BUFFER \ + uint32_t * const work_buffer_0 = display_work_buffer(); \ + uint32_t * const work_buffer_1 = work_buffer_0 + DISPLAY_STRIDE; + +/* Initialize a variable for tracking empty tiles (initialize with an + * impossible address so it matches nothing by default). */ +#define INIT_EMPTY_TILE \ + uint32_t empty_tile_addr = 1; + +/*----------------------------------*/ + +/* Check whether this is a known empty tile, and skip the loop body if so. */ +static const uint8_t CHECK_EMPTY_TILE_bppshift_lut[8] = {2,3,4,4,5,5,5,5}; +#define CHECK_EMPTY_TILE(tilesize) \ + if (info->charaddr == empty_tile_addr) { \ + continue; \ + } \ + if (info->transparencyenable && empty_tile_addr == 1) { \ + const uint32_t bppshift = \ + CHECK_EMPTY_TILE_bppshift_lut[info->colornumber]; \ + const uint32_t tile_nwords = ((tilesize)*(tilesize)/32) << bppshift; \ + empty_tile_addr = info->charaddr; \ + const uint32_t *ptr = (const uint32_t *)&Vdp2Ram[info->charaddr]; \ + const uint32_t *top = ptr + tile_nwords; \ + for (; ptr < top; ptr++) { \ + if (*ptr != 0) { \ + empty_tile_addr = 1; \ + break; \ + } \ + } \ + if (empty_tile_addr != 1) { \ + /* The tile was empty, so we don't need to draw anything */ \ + continue; \ + } \ + } + +/* Declare flip_* and priority with the proper values for "size"x"size" + * tiles (either 8x8 or 16x16). */ +#define GET_FLIP_PRI(size) \ + const int flip_u0 = (info->flipfunction & 1) << ((size)==16 ? 4 : 3); \ + const int flip_u1 = flip_u0 ^ (size); \ + const int flip_v0 = (info->flipfunction & 2) << ((size)==16 ? 3 : 2); \ + const int flip_v1 = flip_v0 ^ (size); \ + int priority; \ + if (info->specialprimode == 1) { \ + priority = (info->priority & ~1) | info->specialfunction; \ + } else { \ + priority = info->priority; \ + } + +/* Declare flip_* and priority for 8-bit indexed tiles. */ +static const int flip_t8_u[4][4] = + { {0,8,8,16}, {8,0,16,8}, {8,16,0,8}, {16,8,8,0} }; +#define GET_FLIP_PRI_T8 \ + const int flip_u0 = flip_t8_u[info->flipfunction][0]; \ + const int flip_u1 = flip_t8_u[info->flipfunction][1]; \ + const int flip_u2 = flip_t8_u[info->flipfunction][2]; \ + const int flip_u3 = flip_t8_u[info->flipfunction][3]; \ + const int flip_v0 = (info->flipfunction & 2) << 1; \ + const int flip_v1 = flip_v0 ^ 4; \ + int priority; \ + if (info->specialprimode == 1) { \ + priority = (info->priority & ~1) | info->specialfunction; \ + } else { \ + priority = info->priority; \ + } + +/* Update the current palette for an 8-bit indexed tile, if necessary. */ +#define UPDATE_T8_PALETTE \ + if (info->paladdr != cur_palette) { \ + cur_palette = info->paladdr; \ + if (UNLIKELY(!palettes[cur_palette])) { \ + palettes[cur_palette] = vdp2_gen_t8_clut( \ + info->coloroffset, info->paladdr<<4, \ + info->transparencyenable, info->cor, info->cog, info->cob \ + ); \ + } \ + if (LIKELY(palettes[cur_palette])) { \ + const uint32_t * const clut = palettes[cur_palette];\ + guClutLoad(256/8, clut); \ + } \ + } + +/* Define 2 vertices for a generic 8x8 or 16x16 tile. */ +#define SET_VERTICES(tilex,tiley,xsize,ysize) \ + vertices[0].u = flip_u0; \ + vertices[0].v = flip_v0; \ + vertices[0].x = (tilex); \ + vertices[0].y = (tiley); \ + vertices[0].z = 0; \ + vertices[1].u = flip_u1; \ + vertices[1].v = flip_v1; \ + vertices[1].x = (tilex) + (xsize); \ + vertices[1].y = (tiley) + (ysize); \ + vertices[1].z = 0; + +/* Define 2 vertices for the even lines of an 8-bit indexed 8x8 tile. */ +#define SET_VERTICES_T8_EVEN(tilex,tiley,xsize,ysize) \ + vertices[0].u = flip_u0; \ + vertices[0].v = yofs + flip_v0; \ + vertices[0].x = (tilex); \ + vertices[0].y = (tiley) / 2; \ + vertices[0].z = 0; \ + vertices[1].u = flip_u1; \ + vertices[1].v = yofs + flip_v1; \ + vertices[1].x = (tilex) + (xsize); \ + vertices[1].y = ((tiley) + (ysize)) / 2; \ + vertices[1].z = 0; + +/* Define 2 vertices for the odd lines of an 8-bit indexed 8x8 tile. */ +#define SET_VERTICES_T8_ODD(tilex,tiley,xsize,ysize) \ + vertices[2].u = flip_u2; \ + vertices[2].v = flip_v0; \ + vertices[2].x = (tilex); \ + vertices[2].y = (tiley) / 2; \ + vertices[2].z = 0; \ + vertices[3].u = flip_u3; \ + vertices[3].v = flip_v1; \ + vertices[3].x = (tilex) + (xsize); \ + vertices[3].y = ((tiley) + (ysize)) / 2; \ + vertices[3].z = 0; + +/* Load the texture pointer for an 8-bit indexed 8x8 tile. */ +#define LOAD_T8_TILE \ + guTexFlush(); \ + guTexImage(0, 512, 512, 16, src); + +/* Set the even-lines work buffer for 8-bit indexed 8x8 tiles. */ +#define SET_T8_BUFFER_0 \ + guDrawBuffer(GU_PSM_8888, work_buffer_0, DISPLAY_STRIDE*2); + +/* Draw the even lines of an 8-bit indexed 8x8 tile */ +#define RENDER_T8_EVEN \ + guVertexPointer(vertices); \ + guDrawPrimitive(GU_SPRITES, 2); + +/* Set the odd-lines work buffer for 8-bit indexed 8x8 tiles. */ +#define SET_T8_BUFFER_1 \ + guDrawBuffer(GU_PSM_8888, work_buffer_1, DISPLAY_STRIDE*2); + +/* Draw the odd lines of an 8-bit indexed 8x8 tile. */ +#define RENDER_T8_ODD \ + guVertexPointer(vertices+2); \ + guDrawPrimitive(GU_SPRITES, 2); + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * vdp2_draw_map_8x8: Draw a graphics layer composed of 8x8 patterns of + * any format. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_map_8x8(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Allocate vertex memory and perform other initialization */ + SET_CLIP_REGION(0); + GET_VERTICES(8, 2); + INIT_EMPTY_TILE; + + /* Loop through tiles */ + TILE_LOOP_BEGIN(8) { + CHECK_EMPTY_TILE(8); + GET_FLIP_PRI(8); + SET_VERTICES(info->x * info->coordincx, info->y * info->coordincy, + 8 * info->coordincx, 8 * info->coordincy); + texcache_load_tile(8, info->charaddr, info->colornumber, + info->transparencyenable, + info->coloroffset, info->paladdr << 4, + info->cor, info->cog, info->cob, + vdp2_is_persistent(info->charaddr)); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); + vertices += 2; + } TILE_LOOP_END; + + /* Reset locally-changed GE settings */ + UNSET_CLIP_REGION; +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_draw_map_8x8_t8: Draw a graphics layer composed of 8-bit indexed + * color 8x8 patterns. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_map_8x8_t8(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Check the current screen mode; if we're in interlaced mode, we can + * cheat by treating each 8x8 tile as a 16x4 texture and drawing only + * the left or right half, thus omitting alternate lines for free. */ + const int interlaced = (disp_height > 272); + + /* Allocate vertex memory and perform other initialization. Note that + * we need 2 sprites to draw each tile if we're not optimizing + * interlaced graphics. */ + SET_CLIP_REGION(!interlaced); + GET_VERTICES(8, interlaced ? 2 : 4); + INIT_T8_PALETTE; + INIT_T8_TEXTURE; + INIT_T8_VERTEX; + INIT_T8_WORK_BUFFER; + INIT_EMPTY_TILE; + + /* Loop through tiles */ + TILE_LOOP_BEGIN(8) { + CHECK_EMPTY_TILE(8); + GET_FLIP_PRI_T8; + UPDATE_T8_PALETTE; + + /* Set up vertices and draw the tile */ + const uint8_t *src = &Vdp2Ram[info->charaddr]; + int yofs = ((uintptr_t)src & 63) / 16; + src = (const uint8_t *)((uintptr_t)src & ~63); + SET_VERTICES_T8_EVEN(info->x * info->coordincx, + info->y * info->coordincy, + 8 * info->coordincx, 8 * info->coordincy); + if (!interlaced) { + SET_VERTICES_T8_ODD(info->x * info->coordincx, + info->y * info->coordincy, + 8 * info->coordincx, 8 * info->coordincy); + } else { + /* We don't modify the work buffer stride in this case, so double + * the Y coordinates (which were set assuming a doubled stride) */ + vertices[0].y *= 2; + vertices[1].y *= 2; + } + LOAD_T8_TILE; + if (!interlaced) { + SET_T8_BUFFER_0; + } + RENDER_T8_EVEN; + if (!interlaced) { + SET_T8_BUFFER_1; + RENDER_T8_ODD; + vertices += 4; + } else { + /* Interlaced, so drop odd lines of tile */ + vertices += 2; + } + } TILE_LOOP_END; + + /* Reset locally-changed GE settings */ + UNSET_CLIP_REGION; + if (!interlaced) { + guDrawBuffer(GU_PSM_8888, work_buffer_0, DISPLAY_STRIDE); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_draw_map_16x16: Draw a graphics layer composed of 16x16 patterns + * of any format. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_map_16x16(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Determine tile data size */ + GET_TILEDATASIZE; + + /* Allocate vertex memory and perform other initialization */ + SET_CLIP_REGION(0); + GET_VERTICES(16, 2); + INIT_EMPTY_TILE; + + /* Loop through tiles */ + TILE_LOOP_BEGIN(16) { + CHECK_EMPTY_TILE(16); + GET_FLIP_PRI(16); + + SET_VERTICES(info->x * info->coordincx, info->y * info->coordincy, + 16 * info->coordincx, 16 * info->coordincy); + texcache_load_tile(16, info->charaddr, info->colornumber, + info->transparencyenable, + info->coloroffset, info->paladdr << 4, + info->cor, info->cog, info->cob, + vdp2_is_persistent(info->charaddr)); + guDrawArray(GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); + vertices += 2; + } TILE_LOOP_END; + + /* Reset locally-changed GE settings */ + UNSET_CLIP_REGION; +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp2_draw_map_16x16_t8: Draw a graphics layer composed of 8-bit indexed + * color 16x16 patterns. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * None + */ +void vdp2_draw_map_16x16_t8(vdp2draw_struct *info, const clipping_struct *clip) +{ + /* Check the current screen mode */ + const int interlaced = (disp_height > 272); + + /* Allocate vertex memory and perform other initialization */ + SET_CLIP_REGION(!interlaced); + GET_VERTICES(8, interlaced ? 2 : 4); + INIT_T8_PALETTE; + INIT_T8_TEXTURE; + INIT_T8_VERTEX; + INIT_T8_WORK_BUFFER; + INIT_EMPTY_TILE; + + /* Loop through tiles */ + TILE_LOOP_BEGIN(16) { + CHECK_EMPTY_TILE(16); + GET_FLIP_PRI_T8; + UPDATE_T8_PALETTE; + + const uint8_t *src = &Vdp2Ram[info->charaddr]; + int yofs = ((uintptr_t)src & 63) / 16; + src = (const uint8_t *)((uintptr_t)src & ~63); + int tilenum; + for (tilenum = 0; tilenum < 4; tilenum++, src += 8*8*1) { + const int tilex = info->x + (8 * ((tilenum % 2) ^ (info->flipfunction & 1))); + const int tiley = info->y + (8 * ((tilenum / 2) ^ ((info->flipfunction & 2) >> 1))); + SET_VERTICES_T8_EVEN(tilex * info->coordincx, + tiley * info->coordincy, + 8 * info->coordincx, 8 * info->coordincy); + if (!interlaced) { + SET_VERTICES_T8_ODD(tilex * info->coordincx, + tiley * info->coordincy, + 8 * info->coordincx, 8 * info->coordincy); + } else { + vertices[0].y *= 2; + vertices[1].y *= 2; + } + + LOAD_T8_TILE; + if (!interlaced) { + SET_T8_BUFFER_0; + } + RENDER_T8_EVEN; + if (!interlaced) { + SET_T8_BUFFER_1; + RENDER_T8_ODD; + vertices += 4; + } else { + vertices += 2; + } + } + } TILE_LOOP_END; + + /* Reset locally-changed GE settings */ + UNSET_CLIP_REGION; + if (!interlaced) { + guDrawBuffer(GU_PSM_8888, work_buffer_0, DISPLAY_STRIDE); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video-tweaks.c b/yabause/src/psp/psp-video-tweaks.c new file mode 100644 index 0000000000..615d005495 --- /dev/null +++ b/yabause/src/psp/psp-video-tweaks.c @@ -0,0 +1,1884 @@ +/* src/psp/psp-video-tweaks.c: Game-specific tweaks for PSP video module + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../memory.h" +#include "../vdp2.h" +#include "../vidshared.h" + +#include "config.h" +#include "gu.h" +#include "psp-video.h" +#include "psp-video-internal.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Local routine declarations (internal helpers are declared in their + * respective sections) */ + +static void Azel_fix_registers(void); +static int Azel_draw_NBG1(vdp2draw_struct *info, + const clipping_struct *clip); +static void Azel_reset_cache(void); +static void Azel_cache_RBG0(void); +static int Azel_draw_RBG0(vdp2draw_struct *info, + const clipping_struct *clip); + +/*************************************************************************/ +/************************** Interface function ***************************/ +/*************************************************************************/ + +/** + * psp_video_apply_tweaks: Apply game-specific optimizations and tweaks + * for faster/better PSP video output. Called at the beginning of drawing + * each frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void psp_video_apply_tweaks(void) +{ + /**** Azel: Panzer Dragoon RPG (JP) ****/ + + if (memcmp(HighWram+0x42F34, "APDNAR\0003", 8) == 0) { + + /* For reasons unknown (but possibly an emulator bug), VDP1 and + * RBG0 are one frame out of sync, with RBG0 lagging behind. + * However, if the game is in 30fps mode and we're also skipping + * one frame (or in fact any number of odd frames) per frame drawn, + * we can hide this lag by always skipping the frame in which the + * VDP1 command table is updated. The game finalizes the command + * list by updating the jump target at VDP1 address 0x00082, so we + * watch for changes and use that to sync the frame skipper. */ + static uint8_t just_adjusted = 0; // Avoid infinite loops, just in case + static uint16_t last_00082 = 0xFFFF; // Impossible value + const uint16_t this_00082 = T1ReadWord(Vdp1Ram, 0x00082); + const int game_framerate = T2ReadLong(HighWram, 0x4BC94); + if (!just_adjusted + && game_framerate == 2 + && this_00082 != last_00082 + && frames_to_skip % 2 == 1 + && frames_skipped % 2 == 1 + ) { + frames_skipped--; + just_adjusted = 1; + } else { + just_adjusted = 0; + } + last_00082 = this_00082; + + /* Fix bogus/suboptimal register settings. */ + Azel_fix_registers(); + + /* Apply the top/bottom black border to NBG1 implemented using + * line scrolling. */ + psp_video_set_draw_routine(BG_NBG1, Azel_draw_NBG1, 0); + + /* Draw sky/ground RBG0 graphics more efficiently, if requested. */ + if (config_get_optimize_rotate() && (Vdp2Regs->BGON & 0x0010)) { + Azel_cache_RBG0(); + psp_video_set_draw_routine(BG_RBG0, Azel_draw_RBG0, 1); + } else { + Azel_reset_cache(); + psp_video_set_draw_routine(BG_RBG0, NULL, 0); + } + + } +} + +/*************************************************************************/ +/**************************** Local functions ****************************/ +/*************************************************************************/ + +/**** Azel: Panzer Dragoon RPG (JP) optimizers ****/ + +/*-----------------------------------------------------------------------*/ + +/* Exported variables for RBG0 slope and first coefficient reciprocal, + * set by the optimized RBG0 coefficient generator in satopt-sh2.c. */ +#define SLOPE_UNSET 1e10f +float psp_video_tweaks_Azel_RBG0_slope = SLOPE_UNSET; +float psp_video_tweaks_Azel_RBG0_first_recip; + +/*----------------------------------*/ + +/* Do we have data for RBG0 cached? */ +static uint8_t Azel_RBG0_cached; + +/* Palette indices (0-7) for sky (flat) and ground (scaled) planes. */ +static uint8_t Azel_sky_palette, Azel_ground_palette; + +/* Does the sky wrap vertically? */ +static uint8_t Azel_sky_wrap_v; + +/* Is the ground texture already reduced by half? */ +static uint8_t Azel_ground_reduced; + +/* VDP2 plane addresses for sky and ground planes. */ +static uint32_t Azel_sky_plane_address, Azel_ground_plane_address; + +/* Checksum for plane data. */ +static uint32_t Azel_plane_data_checksum; + +/* Pixel buffers for cached plane data. */ +static uint8_t *Azel_sky_cache, *Azel_ground_cache; + +/* Data structure used for coordinate calculation. */ +struct Azel_RBG0_coord {int x, y, overdraw_x, overdraw_y;}; + +/*----------------------------------*/ + +static void Azel_cache_plane(uint32_t plane_address, uint32_t tile_base, + uint8_t *dest); +static void Azel_make_mipmap(const uint8_t *in, unsigned int size, + uint8_t *out, unsigned int stride); +static void Azel_get_rotation_matrix(uint32_t address, float matrix[2][3], + float *kx_ret, float *ky_ret, + float *Xp_ret, float *Yp_ret); +static void Azel_transform_coordinates(const float x, const float y, + float *u_ret, float *v_ret, + float M[2][3], + const float kx, const float ky, + const float Xp, const float Yp); +static inline void Azel_compute_switch( + uint32_t coef_base, uint32_t coef_switch_index, + const uint32_t coef_index_UL, const uint32_t coef_index_UR, + const uint32_t coef_index_LL, const uint32_t coef_index_LR, + float coef_dx, float coef_dy, + int *switch_x0, int *switch_y0, int *switch_x1, int *switch_y1); +static inline void Azel_compute_vertices( + int UL_is_sky, int UR_is_sky, int LL_is_sky, int LR_is_sky, + int switch_x0, int switch_y0, int switch_x1, int switch_y1, + struct Azel_RBG0_coord coord[2][5], unsigned int nverts[2]); + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_fix_registers: Fix VDP2 registers which are set improperly (but do + * not exhibit problems on a real Saturn due to hardware idiosyncrasies) or + * inefficiently (so that they waste more resources than necessary). + * + * [Parameters] + * None + * [Return value] + * None + */ +static void Azel_fix_registers(void) +{ + /* Fix bogus sprite alpha setting in the Uru underwater tunnel. */ + if (Vdp2Regs->PNCN1 == 0xC100 + && Vdp2Regs->MPABN1 == 0x0B0B + && Vdp2Regs->MPCDN1 == 0x0B0B + && Vdp2Regs->RPTA.all == 0x14000 + && T1ReadLong(Vdp2Ram, 0x28054) == 0x80640000 + && T1ReadLong(Vdp2Ram, 0x28058) == 0x10000 + && (int32_t)T1ReadLong(Vdp2Ram, 0x20190 + 223*4) < 0 + && Vdp2Regs->SPCTL == 0x1523 + && Vdp2Regs->CCCTL == 0x0053 + && Vdp2Regs->SFCCMD == 0x0008 + && Vdp2Regs->PRISA == 0x0405 + && Vdp2Regs->PRINA == 0x0604 + && Vdp2Regs->CCRSA == 0x000C + && Vdp2Regs->CCRNA == 0x101F + ) { + Vdp2Regs->CCRSA &= 0xFF00; + } + + /* Fix bogus sprite alpha setting in the imperial base. */ + if (Vdp2Regs->BGON == 0x011B + && Vdp2Regs->SFSEL == 0x0002 + && Vdp2Regs->CHCTLA == 0x0101 + && Vdp2Regs->CHCTLB == 0x1100 + && Vdp2Regs->PNCN0 == 0x8080 + && Vdp2Regs->PNCN1 == 0xC100 + && Vdp2Regs->PLSZ == 0x0000 + && Vdp2Regs->MPABN0 == 0x3E3E + && Vdp2Regs->MPABN0 == 0x3E3E + && Vdp2Regs->MPABN1 == 0x0B0B + && Vdp2Regs->MPCDN1 == 0x0B0B + && T1ReadLong(Vdp2Ram, 0x1F000) == 0x02010202 + && T1ReadLong(Vdp2Ram, 0x1008C) == 0x99889999 + && Vdp2Regs->SPCTL == 0x1423 + && (Vdp2Regs->CCCTL & ~0x0010) == 0x0143 + && Vdp2Regs->SFCCMD == 0x0008 + && Vdp2Regs->PRISA == 0x0404 + && (Vdp2Regs->PRINA & ~0x0001) == 0x0604 + && (Vdp2Regs->PRINA & 0xF) == (Vdp2Regs->CCCTL>>4 & 0xF) + && Vdp2Regs->CCRSA == 0x180D + && Vdp2Regs->CCRNA == 0x1017 + ) { + Vdp2Regs->CCRSA &= 0xFF00; + } + + /* Fix missing(?) alpha setting for the NBG0 cloud overlay used in + * Mel-Kava and in Atolm battles. (NBG0 is at the third-highest + * priority level; how does it get color calculation enabled in the + * first place?) */ + if ((Vdp2Regs->BGON & ~0x0100) == 0x001B + && Vdp2Regs->SFSEL == 0x0002 + && Vdp2Regs->CHCTLA == 0x0101 + && Vdp2Regs->CHCTLB == 0x1100 + && (Vdp2Regs->PNCN0 & ~0x0020) == 0x8080 + && Vdp2Regs->PNCN1 == 0xC100 + && Vdp2Regs->PLSZ == 0x0000 + && Vdp2Regs->MPABN0 == 0x3C3C + && Vdp2Regs->MPABN0 == 0x3C3C + && Vdp2Regs->MPABN1 == 0x0B0B + && Vdp2Regs->MPCDN1 == 0x0B0B + && T1ReadLong(Vdp2Ram, 0x1E000) == 0x02010202 + && T1ReadLong(Vdp2Ram, 0x100F8) == 0x11111222 + && (Vdp2Regs->CCCTL & ~0x0010) == 0x0103 + && Vdp2Regs->SFCCMD == 0x0008 + && (Vdp2Regs->PRINA & ~0x0001) == 0x0604 + && (Vdp2Regs->PRINA & 0x1) == (Vdp2Regs->CCCTL>>4 & 0x1) + && Vdp2Regs->CCRNA == 0x1000 + ) { + Vdp2Regs->CCRNA |= 0x0017; + } + + /* Display movies with transparency disabled to improve draw speed. */ + if ((Vdp2Regs->CHCTLA & 0x0070) == 0x0040) { + Vdp2Regs->BGON |= 0x0100; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_draw_NBG1: Draw NBG1, along with any black borders specified by + * the line scroll table. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * Nonzero if the graphics layer was drawn, zero if not + */ +static int Azel_draw_NBG1(vdp2draw_struct *info, + const clipping_struct *clip) +{ + /* First check whether this is an optimizable case, and punt back to + * the default if not. */ + + if ((Vdp2Regs->SCRCTL & 0x3F00) != 0x400) { + return 0; // No black bars to draw + } + if (UNLIKELY(info->isbitmap || info->patternwh != 2)) { + DMSG("Bad NBG1 parameters: isbitmap=%d patternwh=%d", + info->isbitmap, info->patternwh); + return 0; + } + + /* Draw the screen itself as usual. */ + + vdp2_draw_map_16x16(info, clip); + + /* Render black bars for lines scrolled off the screen (i.e., to black) + * in the line scroll table. */ + + const uint32_t address = (Vdp2Regs->LSTA1.all & 0x3FFFE) << 1; + const uint8_t *table = &Vdp2Ram[address]; + + guDisable(GU_TEXTURE_2D); + + int in_black_bar = 0; + unsigned int black_bar_top = 0; + unsigned int y; + for (y = 0; y <= disp_height; y++, table += 4) { // Deliberately "<=" + if (y < disp_height && *table != 0) { + if (!in_black_bar) { + black_bar_top = y; + in_black_bar = 1; + } + } else { + if (in_black_bar) { + struct {uint32_t color; int16_t x, y, z, pad;} *vertices; + vertices = guGetMemory(sizeof(*vertices) * 2); + vertices[0].color = 0xFF000000; + vertices[0].x = 0; + vertices[0].y = black_bar_top >> disp_yscale; + vertices[0].z = 0; + vertices[1].color = 0xFF000000; + vertices[1].x = disp_width >> disp_xscale; + vertices[1].y = y >> disp_yscale; + vertices[1].z = 0; + guDrawArray(GU_SPRITES, + GU_TRANSFORM_2D | GU_COLOR_8888 | GU_VERTEX_16BIT, + 2, NULL, vertices); + in_black_bar = 0; + } + } + } + + guEnable(GU_TEXTURE_2D); + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_reset_cache: Clear all cached RBG0 data. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void Azel_reset_cache(void) +{ + Azel_RBG0_cached = 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_cache_RBG0: Check whether the current RBG0 data matches the cached + * data, and cache it if not. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void Azel_cache_RBG0(void) +{ + /* Look up the graphics layer format and make sure it's what we're + * looking for. */ + + const unsigned int colornumber = Vdp2Regs->CHCTLB>>12 & 0x7; + const unsigned int patternwh = Vdp2Regs->CHCTLB & 0x100 ? 2 : 1; + const unsigned int patterndatasize = Vdp2Regs->PNCR & 0x8000 ? 2 : 4; + const unsigned int auxmode = Vdp2Regs->PNCR & 0x4000 ? 1 : 0; + const unsigned int supplementdata = Vdp2Regs->PNCR & 0x3FF; + const unsigned int planewh_A = Vdp2Regs->PLSZ>>8 & 0x3; + const unsigned int planewh_B = Vdp2Regs->PLSZ>>12 & 0x3; + const unsigned int rpmd = Vdp2Regs->RPMD & 0x3; + const unsigned int raovr = Vdp2Regs->PLSZ>>10 & 0x3; + const unsigned int raopn = Vdp2Regs->OVPNRA; + if (colornumber != 1 || patternwh != 2 || patterndatasize != 2 + || auxmode || planewh_A != 0 || planewh_B != 0 + || (rpmd != 2 && !(rpmd == 0 && raovr == 1 && raopn == 0x5000)) + ) { + DMSG("Wrong RBG0 format for cache: colornumber=%u patternwh=%u" + " patterndatasize=%u auxmode=%u planewh=%u/%u rpmd=%u", + colornumber, patternwh, patterndatasize, auxmode, planewh_A, + planewh_B, rpmd); + Azel_RBG0_cached = 0; + return; + } + + /* Calculate the plane data addresses for the sky and ground planes, + * and check that they haven't changed from the cached values. */ + + const unsigned int plane_size = + (64/patternwh) * (64/patternwh) * patterndatasize; + const unsigned int sky_plane = + (Vdp2Regs->MPOFR>>4 & 7) << 6 | (Vdp2Regs->MPABRB & 0xFF); + const unsigned int ground_plane = + (Vdp2Regs->MPOFR>>0 & 7) << 6 | (Vdp2Regs->MPABRA & 0xFF); + const unsigned int sky_plane_address = sky_plane * plane_size; + const unsigned int ground_plane_address = ground_plane * plane_size; + if (Azel_RBG0_cached + && (sky_plane_address != Azel_sky_plane_address + || ground_plane_address != Azel_ground_plane_address) + ) { + DMSG("Plane addresses changed, now sky=%05X ground=%05X", + sky_plane_address, ground_plane_address); + Azel_RBG0_cached = 0; + } + + /* Calculate a checksum for the plane data and check it against the + * cached checksum. If the plane data matches, we assume the tile + * (pixel) data matches as well. */ + + uint16_t sum1 = 1, sum2 = 0; + const uint16_t *ptr, *top; + for (ptr = (const uint16_t *)&Vdp2Ram[sky_plane_address], + top = ptr + plane_size/2; ptr < top; ptr++ + ) { + const uint16_t data = BSWAP16(*ptr); + sum1 += data; + sum2 += sum1; + } + for (ptr = (const uint16_t *)&Vdp2Ram[ground_plane_address], + top = ptr + plane_size/2; ptr < top; ptr++ + ) { + const uint16_t data = BSWAP16(*ptr); + sum1 += data; + sum2 += sum1; + } + const uint32_t checksum = sum1 | sum2<<16; + if (checksum != Azel_plane_data_checksum) { + DMSG("Plane data checksum changed, now 0x%08X", checksum); + Azel_RBG0_cached = 0; + } + + /* If Azel_RBG0_cached is still set, the current RBG0 data already + * matches the cache, so we don't have to do anything else. */ + + if (Azel_RBG0_cached) { + return; + } + + /* Allocate a cache buffer if we haven't done so yet. (We don't free + * the buffers once we allocate them, on the assumption that only one + * game will be played per Yabause boot.) */ + + if (!Azel_sky_cache) { + uint8_t *base = malloc(63 + 512*512 + + 512*512 + 256*256 + 128*128 + 64*64); + if (!base) { + DMSG("No memory for sky/ground pixel buffers"); + return; + } + uintptr_t base_aligned = ((uintptr_t)base + 63) & -64; + Azel_sky_cache = (uint8_t *)base_aligned; + Azel_ground_cache = (uint8_t *)(base_aligned + 512*512); + } + + /* Cache the graphics data (two 512x512-pixel planes) in + * Azel_{sky,ground}_cache as 512x512 T8 swizzled textures. Also add + * mipmaps for the ground texture to improve drawing performance for + * distant regions. */ + + Azel_cache_plane(sky_plane_address, + (supplementdata & 0xC) << 15 | (supplementdata & 3) << 5, + Azel_sky_cache); + if ((Vdp2Regs->RPMD & 3) == 0 + && Vdp2Regs->MPABRA == 0x0100 + && Vdp2Regs->MPEFRA == 0x0203 + ) { + /* Special case for the dome area of the Uru underground dungeon, + * which is a shrunken 1024x1024 map. We reduce it to 512x512 and + * adjust the scale factors appropriately when drawing. */ + Azel_ground_reduced = 1; + uint8_t *temp = malloc(512*512); + if (!temp) { + DMSG("No temporary memory for reducing dome RBG0"); + return; + } + Azel_cache_plane(ground_plane_address, + (supplementdata & 0xC)<<15 | (supplementdata & 3)<<5, + temp); + Azel_make_mipmap(temp, 512, Azel_ground_cache, 512); + Azel_cache_plane(ground_plane_address + 0x800, + (supplementdata & 0xC)<<15 | (supplementdata & 3)<<5, + temp); + Azel_make_mipmap(temp, 512, Azel_ground_cache + 256*8, 512); + Azel_cache_plane(ground_plane_address + 0x1800, + (supplementdata & 0xC)<<15 | (supplementdata & 3)<<5, + temp); + Azel_make_mipmap(temp, 512, Azel_ground_cache + 512*256, 512); + Azel_cache_plane(ground_plane_address + 0x1000, + (supplementdata & 0xC)<<15 | (supplementdata & 3)<<5, + temp); + Azel_make_mipmap(temp, 512, Azel_ground_cache + 512*256 + 256*8, 512); + free(temp); + } else { + Azel_ground_reduced = 0; + Azel_cache_plane(ground_plane_address, + (supplementdata & 0xC)<<15 | (supplementdata & 3)<<5, + Azel_ground_cache); + } + Azel_make_mipmap(Azel_ground_cache, 512, + Azel_ground_cache + 512*512, 256); + Azel_make_mipmap(Azel_ground_cache + 512*512, 256, + Azel_ground_cache + 512*512 + 256*256, 128); + Azel_make_mipmap(Azel_ground_cache + 512*512 + 256*256, 128, + Azel_ground_cache + 512*512 + 256*256 + 128*128, 64); + + /* Record other data in relevant variables and set the cached flag. */ + + Azel_sky_palette = Vdp2Ram[sky_plane_address] >> 4; + Azel_ground_palette = Vdp2Ram[ground_plane_address] >> 4; + Azel_sky_wrap_v = (Vdp2Regs->MPEFRB == Vdp2Regs->MPABRB); + Azel_sky_plane_address = sky_plane_address; + Azel_ground_plane_address = ground_plane_address; + Azel_plane_data_checksum = checksum; + Azel_RBG0_cached = 1; + psp_video_tweaks_Azel_RBG0_slope = SLOPE_UNSET; +} + +/*----------------------------------*/ + +/** + * Azel_cache_plane: Cache a single plane of graphics data as a 512x512 + * T8-format swizzled texture. + * + * [Parameters] + * plane_address: Address of plane data in VDP2 RAM + * tile_base: Base address of tile (pixel) data in VDP2 RAM + * dest: Pointer to output buffer (512*512 bytes) + * [Return value] + * None + */ +static void Azel_cache_plane(uint32_t plane_address, uint32_t tile_base, + uint8_t *dest) +{ + const uint16_t *src = (const uint16_t *)&Vdp2Ram[plane_address]; + + unsigned int tile_y; + for (tile_y = 0; tile_y < 32; tile_y++) { + uint8_t *out_ptr = &dest[(tile_y * 16) * 512]; + unsigned int tile_x; + for (tile_x = 0; tile_x < 32; tile_x++, src++, out_ptr += 128) { + const uint16_t data = BSWAP16(*src); + unsigned int tile_index = (data & 0x3FF) << 2; + unsigned int flip_x = (data & 0x400) ? 8 : 0; + unsigned int flip_y = (data & 0x800) ? 15 : 0; + const uint8_t *tile_data = &Vdp2Ram[tile_base + (tile_index<<5)]; + unsigned int char_y; + for (char_y = 0; char_y < 2; char_y++) { + unsigned int char_x; + for (char_x = 0; char_x < 2; char_x++) { + unsigned int pixel_y; + for (pixel_y = 0; pixel_y < 8; pixel_y++, tile_data += 8) { + const unsigned int y = (char_y*8 + pixel_y) ^ flip_y; + uint8_t * const y_ptr = + &out_ptr[((y/8) * (512*8)) + ((y%8) * 16) + + ((char_x*8) ^ flip_x)]; + const uint32_t pix0_3 = + ((const uint32_t *)tile_data)[0]; + const uint32_t pix4_7 = + ((const uint32_t *)tile_data)[1]; + if (flip_x) { + ((uint32_t *)y_ptr)[0] = BSWAP32(pix4_7); + ((uint32_t *)y_ptr)[1] = BSWAP32(pix0_3); + } else { + ((uint32_t *)y_ptr)[0] = pix0_3; + ((uint32_t *)y_ptr)[1] = pix4_7; + } + } // pixel_y + } // char_x + } // char_y + } // tile_x + } // tile_y +} + +/*----------------------------------*/ + +/** + * Azel_make_mipmap: Create a half-size mipmap from the given pixel buffer + * by dropping every second pixel. + * + * [Parameters] + * in: Input pixel buffer + * size: Size (width and height) of input pixel buffer + * out: Output pixel buffer + * stride: Line length of output buffer (normally size/2) + * [Return value] + * None + */ +static void Azel_make_mipmap(const uint8_t *in, unsigned int size, + uint8_t *out, unsigned int stride) +{ +#define SHRINK \ + asm(".set push; .set noreorder\n" \ + "srl %[temp], %[a], 16\n" \ + "ins %[a], %[temp], 8, 8\n" \ + "ins %[a], %[b], 16, 8\n" \ + "srl %[b], %[b], 16\n" \ + "ins %[a], %[b], 24, 8\n" \ + "srl %[temp], %[c], 16\n" \ + "ins %[c], %[temp], 8, 8\n" \ + "ins %[c], %[d], 16, 8\n" \ + "srl %[d], %[d], 16\n" \ + "ins %[c], %[d], 24, 8\n" \ + ".set pop" \ + : [a] "=r" (a), [b] "=r" (b), [c] "=r" (c), [d] "=r" (d), \ + [temp] "=&r" (temp) \ + : "0" (a), "1" (b), "2" (c), "3" (d) \ + ) + + unsigned int y; + for (y = 0; y < size; y += 16, in += size*8, out += (stride - size/2)*8) { + unsigned int x; + for (x = 0; x < size; x += 32, in += 128, out += 64) { + unsigned int line; + for (line = 0; line < 4; line++, in += 32, out += 16) { + uint32_t a, b, c, d, temp; + + a = ((const uint32_t *)in)[0]; + b = ((const uint32_t *)in)[1]; + c = ((const uint32_t *)in)[2]; + d = ((const uint32_t *)in)[3]; + SHRINK; + ((uint32_t *)out)[0] = a; + ((uint32_t *)out)[1] = c; + + a = ((const uint32_t *)in)[32]; + b = ((const uint32_t *)in)[33]; + c = ((const uint32_t *)in)[34]; + d = ((const uint32_t *)in)[35]; + SHRINK; + ((uint32_t *)out)[2] = a; + ((uint32_t *)out)[3] = c; + + a = ((const uint32_t *)in)[size*2+0]; + b = ((const uint32_t *)in)[size*2+1]; + c = ((const uint32_t *)in)[size*2+2]; + d = ((const uint32_t *)in)[size*2+3]; + SHRINK; + ((uint32_t *)out)[16] = a; + ((uint32_t *)out)[17] = c; + + a = ((const uint32_t *)in)[size*2+32]; + b = ((const uint32_t *)in)[size*2+33]; + c = ((const uint32_t *)in)[size*2+34]; + d = ((const uint32_t *)in)[size*2+35]; + SHRINK; + ((uint32_t *)out)[18] = a; + ((uint32_t *)out)[19] = c; + } + } + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_draw_RBG0: Draw a sky/ground RBG0 layer from cached data. + * + * [Parameters] + * info: Graphics layer data + * clip: Clipping window data + * [Return value] + * Nonzero if the graphics layer was drawn, zero if not + */ +static int Azel_draw_RBG0(vdp2draw_struct *info, + const clipping_struct *clip) +{ + /* Define a macro to read a coefficient as a signed value, given its + * index (longword offset into VDP2 RAM). coef_mask is defined below. */ + #define READ_COEF(index) \ + ((int32_t)T1ReadLong(Vdp2Ram, (index)*4) & coef_mask) + + if (!Azel_RBG0_cached) { + return 0; + } + + /* Make sure it's an RPMD mode 2 layer with 4-byte, mode-0 coefficients + * for the ground (set A) and no coefficients for the sky (set B). + * However, also allow mode 0 with certain parameter settings used in + * the Uru underground dungeon. */ + + if ((Vdp2Regs->RPMD & 3) != 2 + && !((Vdp2Regs->RPMD & 3) == 0 + && (Vdp2Regs->PLSZ>>10 & 3) == 1 + && Vdp2Regs->OVPNRA == 0x5000) + ) { + DMSG("Can't optimize RBG0 (bad rotatemode=%d)", info->rotatemode); + return 0; + } + const int has_sky = ((Vdp2Regs->RPMD & 3) == 2); + const uint32_t param_address = (Vdp2Regs->RPTA.all << 1) & 0x7FF7C; + const uint32_t coef_base_high = (Vdp2Regs->KTAOF & 1) << 18; + if ((Vdp2Regs->KTCTL & 0x0F0F) != 0x0001) { + DMSG("Can't optimize RBG0 (bad KTCTL=%04X)", Vdp2Regs->KTCTL); + return 0; + } + + /* Load rotation matrix parameters. */ + + float sky_M[2][3], sky_kx, sky_ky, sky_Xp, sky_Yp; + float ground_M[2][3], ground_Xp, ground_Yp; + Azel_get_rotation_matrix(param_address + 0x80, + sky_M, &sky_kx, &sky_ky, &sky_Xp, &sky_Yp); + Azel_get_rotation_matrix(param_address, + ground_M, NULL, NULL, &ground_Xp, &ground_Yp); + + const float coef_base = coef_base_high/4 + + (uint32_t)T1ReadLong(Vdp2Ram, param_address+0x54) / 65536.0f; + const float coef_dy = + ((int32_t)T1ReadLong(Vdp2Ram, param_address+0x58) << 6) / 4194304.0f; + const float coef_dx = + ((int32_t)T1ReadLong(Vdp2Ram, param_address+0x5C) << 6) / 4194304.0f; + const float coef_right_offset = (disp_width - 1) * coef_dx; + + /* Find the beginning and end of the coefficient table, and determine + * where the sky/ground boundary is. For a non-rotated background, + * the table length will simply be the number of lines on the screen, + * but when the background is rotated, we need to check all four + * corners of the screen to find the table's extent. + * + * Coefficient values are treated as signed 8.16 bit fixed point, but + * the program does not clamp its coefficient values properly; as a + * result, the coefficient value can spill over into the sign bit or + * the ignored high bits for lines very close to the horizon. We treat + * these as sky lines in order to avoid mathematical trouble when + * drawing. Due to this, we optimize the (data > 0 && data < 0x800000) + * test into ((unsigned)data < 0x800000) for more efficient processing. + * However, there are some areas (such as Mel-Kava) which intentionally + * store data into the upper 8 bits, which we detect by finding four + * consecutive coefficients with upper 8 bits between 0x01-0x7F and + * lower 24 bits less than 0x800000; in that case we mask off bits + * 24-30 when reading the coefficients. + * + * We also check to see whether the ground coefficients are zero (as + * seems to happen sometimes during a scene change) in order to avoid + * division by zero when rendering. We assume that either all or no + * coefficients are zero, so we only check the first one in the ground + * region. */ + + const float coef_index_UL = coef_base; + const float coef_index_UR = coef_index_UL + coef_right_offset; + const float coef_index_LL = coef_base + (disp_height - 1) * coef_dy; + const float coef_index_LR = coef_index_LL + coef_right_offset; + const uint32_t first_coef_index = + MIN(MIN(ifloorf(coef_index_UL), ifloorf(coef_index_UR)), + MIN(ifloorf(coef_index_LL), ifloorf(coef_index_LR))); + const uint32_t last_coef_index = + MAX(MAX(ifloorf(coef_index_UL), ifloorf(coef_index_UR)), + MAX(ifloorf(coef_index_LL), ifloorf(coef_index_LR))); + const uint32_t num_coefs = last_coef_index + first_coef_index - 1; + + int high_bits_used_count = 0; + int32_t coef_mask = -1; + int ground_is_zero; + unsigned int coef_switch_index; + + retry_coef_scan: + + if ((uint32_t)READ_COEF(first_coef_index) >= 0x800000) { + + ground_is_zero = 0; + unsigned int offset; + for (offset = 0; offset < num_coefs; offset++) { + const uint32_t data = READ_COEF(first_coef_index + offset); + if (coef_mask == -1) { + if ((data>>24 >= 0x01 && data>>24 <= 0x7F) + && (data & 0xFFFFFF) < 0x800000 + ) { + high_bits_used_count++; + if (high_bits_used_count >= 4) { + coef_mask = (int32_t)0x80FFFFFF; + goto retry_coef_scan; + } + } else { + high_bits_used_count = 0; + } + } + if (data < 0x800000) { + ground_is_zero = (data == 0); + break; + } + } + coef_switch_index = first_coef_index + offset; + + } else { + + ground_is_zero = (READ_COEF(first_coef_index) == 0); + unsigned int offset; + for (offset = 0; offset < num_coefs; offset++) { + const uint32_t data = READ_COEF(first_coef_index + offset); + if (data >= 0x800000) { + break; + } + } + coef_switch_index = first_coef_index + offset; + + } + + /* Determine the endpoints of the sky/ground dividing line, if any. */ + + const int has_switch = (coef_switch_index <= last_coef_index); + int switch_x0, switch_y0, switch_x1, switch_y1; + + if (has_switch) { + Azel_compute_switch(coef_base, coef_switch_index, coef_index_UL, + coef_index_UR, coef_index_LL, coef_index_LR, + coef_dx, coef_dy, &switch_x0, &switch_y0, + &switch_x1, &switch_y1); + if (UNLIKELY(switch_x0 < 0 || switch_x1 < 0)) { + DMSG("Failed to find swith line endpoints (base=%05X dx=%.3f" + " dy=%.3f", ifloorf(coef_base)*4, coef_dx, coef_dy); + return 0; + } + } else { // !has_switch + /* Set the switch line at the bottom of the screen for simplicity. */ + switch_x0 = 0; + switch_y0 = disp_height; + switch_x1 = disp_width; + switch_y1 = disp_height; + } // if (has_switch) + + /* Work out the vertex coordinates of the sky and ground regions. + * Depending on where the switch line falls, these could be three-, + * four-, or five-sided polygons. To avoid unnecessary code + * duplication, we first compute the vertices themselves, then choose + * which vertex set (coord[0] or coord[1]) to use for the sky and + * ground regions. + * + * The "overdraw_x" and "overdraw_y" fields are set to either +1 or + * -1 to indicate the direction each coordinate should be shifted for + * overdrawing when rendering the sky region. */ + + struct Azel_RBG0_coord coord[2][5]; + unsigned int nverts[2]; + + const int UL_is_sky = + ((uint32_t)READ_COEF(ifloorf(coef_index_UL)) >= 0x800000); + const int UR_is_sky = + ((uint32_t)READ_COEF(ifloorf(coef_index_UR)) >= 0x800000); + const int LL_is_sky = + ((uint32_t)READ_COEF(ifloorf(coef_index_LL)) >= 0x800000); + const int LR_is_sky = + ((uint32_t)READ_COEF(ifloorf(coef_index_LR)) >= 0x800000); + + Azel_compute_vertices(UL_is_sky, UR_is_sky, LL_is_sky, LR_is_sky, + switch_x0, switch_y0, switch_x1, switch_y1, + coord, nverts); + + const unsigned int sky_coord_set = UL_is_sky ? 0 : 1; + unsigned int ground_coord_set = sky_coord_set ^ 1; // May be changed later + + /* Generate color tables for the two background portions. */ + + void *sky_clut, *ground_clut; + sky_clut = vdp2_gen_t8_clut(Azel_sky_palette << 8, 0, + info->transparencyenable, + info->cor, info->cog, info->cob); + if (!sky_clut) { + DMSG("Failed to generate sky CLUT (palette %u)", Azel_sky_palette); + return 0; + } + ground_clut = vdp2_gen_t8_clut(Azel_ground_palette << 8, 0, + info->transparencyenable, + info->cor, info->cog, info->cob); + if (!ground_clut) { + DMSG("Failed to generate ground CLUT (palette %u)", + Azel_ground_palette); + return 0; + } + + /* Set up a vertex structure for rendering. */ + + struct {float u, v, x, y, z;} *vertices; + const uint32_t vertex_type = GU_TEXTURE_32BITF | GU_VERTEX_32BITF; + + /* Draw the sky (flat) portion of the background. We overdraw by one + * pixel in each direction to avoid the possibility of undrawn pixels + * along the switch (horizon) line, since the ground coordinates are + * adjusted by the Z factor and may end up slightly different from the + * original values in the coord[] array. */ + + if (has_sky && nverts[sky_coord_set] > 0) { + + vertices = guGetMemory(sizeof(*vertices) * nverts[sky_coord_set]); + unsigned int i; + for (i = 0; i < nverts[sky_coord_set]; i++) { + vertices[i].x = + coord[sky_coord_set][i].x + coord[sky_coord_set][i].overdraw_x; + vertices[i].y = + coord[sky_coord_set][i].y + coord[sky_coord_set][i].overdraw_y; + vertices[i].z = 0; + Azel_transform_coordinates(vertices[i].x, vertices[i].y, + &vertices[i].u, &vertices[i].v, + sky_M, sky_kx, sky_ky, sky_Xp, sky_Yp); + /* We deliberately shift the "sky" portion 2 pixels in the + * overdraw direction because there are occasionally + * transparent gaps where the background color shows through + * (e.g. 禁止区域). */ + if (UL_is_sky || UR_is_sky) { + vertices[i].v -= 2; + } else { + vertices[i].v += 2; + } + } + + guClutMode(GU_PSM_8888, 0, 255, 0); + guClutLoad(32, sky_clut); + guTexMode(GU_PSM_T8, 0, 0, 1); + guTexImage(0, 512, 512, 512, Azel_sky_cache); + guTexWrap(GU_REPEAT, Azel_sky_wrap_v ? GU_REPEAT : GU_CLAMP); + guTexFlush(); + guDrawArray(GU_TRIANGLE_STRIP, GU_TRANSFORM_2D | vertex_type, + nverts[sky_coord_set], NULL, vertices); + + } // if (nverts[sky_coord_set] > 0) + + /* Draw the ground (scaled) portion of the background. The scale + * factors in the coefficient table are essentially distance (Z) + * values, so we set up a projection matrix that allows us to draw + * a small number of 3D triangles instead of rendering each line + * individually. For ease of coordinate handling, the projection + * matrix maps 3D coordinate (x,y,1) directly to screen coordinate + * (x+disp_width/2,y+disp_height/2). */ + + guClutMode(GU_PSM_8888, 0, 255, 0); + guClutLoad(32, ground_clut); + guTexMode(GU_PSM_T8, 0, 0, 1); + if ((Vdp2Regs->PLSZ>>10 & 3) == 0) { + guTexWrap(GU_REPEAT, GU_REPEAT); + } else { + guTexWrap(GU_CLAMP, GU_CLAMP); + } + + float Mproj[4][4]; + Mproj[0][0] = 2.0f / disp_width; + Mproj[0][1] = 0; + Mproj[0][2] = 0; + Mproj[0][3] = 0; + Mproj[1][0] = 0; + Mproj[1][1] = -2.0f / disp_height; + Mproj[1][2] = 0; + Mproj[1][3] = 0; + Mproj[2][0] = 0; + Mproj[2][1] = 0; + Mproj[2][2] = 1.0f / 128; // Coefficient value range is [0,128). + Mproj[2][3] = 1; + Mproj[3][0] = 0; + Mproj[3][1] = 0; + Mproj[3][2] = 0; + Mproj[3][3] = 0; + guSetMatrix(GU_PROJECTION, &Mproj[0][0]); + + if (nverts[ground_coord_set] == 0) { + + /* Nothing to do. */ + + } else if (ground_is_zero) { + + /* Degenerate case: ground coefficients are all zero. This + * should only happen while changing scenes, so we don't bother + * drawing anything. */ + + } else if (coef_switch_index == last_coef_index) { + + /* Degenerate case: only one ground coefficient (we also come here + * if we detect a slope of zero). We can't compute appropriate 3D + * coordinates for this case, but since it's effectively flat like + * the sky, just draw it that way. */ + + draw_as_flat:; + const int32_t data = READ_COEF(coef_switch_index); + const float kx = data / 65536.0f; + const float ky = kx; + + vertices = guGetMemory(sizeof(*vertices) * nverts[ground_coord_set]); + unsigned int i; + for (i = 0; i < nverts[ground_coord_set]; i++) { + vertices[i].x = coord[ground_coord_set][i].x; + vertices[i].y = coord[ground_coord_set][i].y; + vertices[i].z = 0; + Azel_transform_coordinates(vertices[i].x, vertices[i].y, + &vertices[i].u, &vertices[i].v, + ground_M, kx, ky, ground_Xp, ground_Yp); + } + if (kx >= 6.0f) { + guTexImage(0, 64, 64, 64, + Azel_ground_cache + 512*512 + 256*256 + 128*128); + } else if (kx >= 3.0f) { + guTexImage(0, 128, 128, 128, Azel_ground_cache + 512*512 + 256*256); + } else if (kx >= 1.5f) { + guTexImage(0, 256, 256, 256, Azel_ground_cache + 512*512); + } else { + guTexImage(0, 512, 512, 512, Azel_ground_cache); + } + guTexFlush(); + guDrawArray(GU_TRIANGLE_STRIP, GU_TRANSFORM_2D | vertex_type, + nverts[ground_coord_set], NULL, vertices); + + } else { + + /* Determine the indices of the first and last ground coefficients. */ + + uint32_t first_ground_index, last_ground_index; + if ((uint32_t)READ_COEF(first_coef_index) < 0x800000) { + first_ground_index = first_coef_index; + if (has_switch) { + last_ground_index = coef_switch_index - 1; + } else { + last_ground_index = last_coef_index; + } + } else { + first_ground_index = coef_switch_index; + last_ground_index = last_coef_index; + } + + /* Compute the approximate plane slope from the average of the + * slopes from the first coefficient to all other coefficients. + * (For certain "ground" textures, like water, the actual + * coefficients are modulated by a small amount to produce a + * "shimmering" effect, so they won't give a single, consistent + * result from one coefficient to the next; thus we take the + * average and use that instead.) + * + * If we have a hint from the satopt-sh2.c optimizer, we use that + * instead; however, it seems to be delayed by a frame, so we use + * a local static variable to accomplish the same thing. */ + + static float slope_hint_delayed = SLOPE_UNSET; + static float first_recip_hint_delayed; + const float slope_hint = slope_hint_delayed; + const float first_recip_hint = first_recip_hint_delayed; + slope_hint_delayed = psp_video_tweaks_Azel_RBG0_slope; + first_recip_hint_delayed = psp_video_tweaks_Azel_RBG0_first_recip; + + float slope = 0; + const int32_t first_data = READ_COEF(first_ground_index); + float first_recip; + if (slope_hint != SLOPE_UNSET && slope_hint_delayed != SLOPE_UNSET) { + slope = slope_hint; + first_recip = first_recip_hint; + if (slope != 0) { + /* This may be slightly off (e.g. due to an out-of-range + * coefficient), so adjust as necessary. */ + float diff; + if (slope > 0) { + diff = first_recip - 65536.0f/first_data; + } else { + const float last_recip = + first_recip + slope * (last_ground_index - first_ground_index); + const float recip_test = + 65536.0f / READ_COEF(last_ground_index); + diff = last_recip - recip_test; + } + if (fabsf(diff) > (slope/2)) { + first_recip -= roundf(diff / fabsf(slope)) * fabsf(slope); + } + } + } else { + first_recip = 65536.0f / first_data; + uint32_t i; + for (i = first_ground_index + 1; i <= last_ground_index; i++) { + const int32_t data = READ_COEF(i); + const float recip = 65536.0f / data; + slope += (recip - first_recip) / (i - first_ground_index); + } + slope /= last_ground_index - first_ground_index; + } + + if (slope == 0) { // Avoid division by zero below. + goto draw_as_flat; + } + + /* Go over the coefficient list and find the variance from the + * linear slope we just derived, to determine whether or not we + * need to apply our own "shimmering" effect. */ + + float variance = 0; + uint32_t index; + for (index = first_ground_index; index <= last_ground_index; index++) { + const int32_t data = READ_COEF(index); + const float recip = 65536.0f / data; + const float expected = + first_recip + slope * (index - first_ground_index); + const float error = (recip - expected) / slope; + variance += error * error; + } + variance /= last_ground_index - first_ground_index + 1; + const int do_shimmer = (variance > 0.01f); + const float shimmer_step = + (sceKernelGetSystemTimeLow() & 0x7FFFFF) / (float)0x800000; + + /* If the entire screen is "ground" and the slope is positive, + * move the switch line to the top of the screen so we draw + * the proper portion as mipmapped. */ + + if (switch_y0 == disp_height && slope > 0) { + switch_y0 = switch_y1 = 0; + } + + /* If the ground plane needs to be split into separate mipmapped + * and normal regions, first draw the distant (mipmapped) part of + * the plane. + * FIXME: This section is making me nauseous, which is a sign that + * it's poorly written and desperately needs refactoring--or perhaps + * a complete rethinking... + */ + + index = (slope > 0) ? first_ground_index : last_ground_index + 1; + int32_t mipmap_scale; + for (mipmap_scale = 8; + mipmap_scale > 1 && (slope > 0 ? index <= last_ground_index + : index > first_ground_index); + mipmap_scale /= 2 + ) { + + /* Find the index of the coefficient at which the mipmap scale + * switches. We normally use mipmaps only at scales above the + * mipmap's level, but we're more aggressive about using + * mipmaps in water areas because we need the speed in Uru. */ + + const float mipmap_scale_recip = + (do_shimmer ? 2.0f : 1.0f) / mipmap_scale; + const int32_t mipmap_switch_offset = + iceilf((mipmap_scale_recip - first_recip) / slope); + if (mipmap_switch_offset < 0) { + continue; + } + uint32_t mipmap_switch_index = + first_ground_index + mipmap_switch_offset; + if (mipmap_switch_index > last_ground_index + 1) { + mipmap_switch_index = last_ground_index + 1; + } + if (slope > 0) { + /* If there's only a small section to draw for this mipmap + * level, skip this iteration and draw it as part of the + * next, since we'll probably get better use of the GE's + * texture cache that way. */ + if (mipmap_switch_index <= index + 4) { + continue; + } + /* If we've covered the entire region with this mipmap + * level--or if this would leave only a small amount to + * draw with the next level, which again would make poor + * use of the texture cache, break out of the loop and let + * the last pass handle it with the already-computed + * vertices. Note that we explicitly don't update "index" + * in this case so the last pass is not skipped. */ + if (mipmap_switch_index > last_ground_index - 4) { + break; + } + } else { // slope < 0 + if (mipmap_switch_index >= index - 4) { + continue; + } + if (index <= first_ground_index + 4) { + index--; + } + } + index = mipmap_switch_index; // Save it for next time around. + + /* Compute switch line coordinates for the mipmap line. */ + + int mipmap_x0, mipmap_y0, mipmap_x1, mipmap_y1; + Azel_compute_switch(coef_base, mipmap_switch_index, coef_index_UL, + coef_index_UR, coef_index_LL, coef_index_LR, + coef_dx, coef_dy, &mipmap_x0, &mipmap_y0, + &mipmap_x1, &mipmap_y1); + + /* Compute vertices for the mipmapped region. This is a + * rectangle if the horizon line is horizontal or vertical, but + * may be a four-, five-, or six-sided polygon if the horizon + * is tilted. */ + + struct {int x, y;} mipmap_coords[6]; + unsigned int mipmap_nverts; + if (coef_dx == 0 || coef_dy == 0) { + mipmap_coords[0].x = switch_x0; + mipmap_coords[0].y = switch_y0; + mipmap_coords[1].x = switch_x1; + mipmap_coords[1].y = switch_y1; + mipmap_coords[2].x = mipmap_x0; + mipmap_coords[2].y = mipmap_y0; + mipmap_coords[3].x = mipmap_x1; + mipmap_coords[3].y = mipmap_y1; + mipmap_nverts = 4; + } else { + /* First determine which line is on top (has smaller + * Y coordinates), to simplify computations. We make use + * of the knowledge that Azel_compute_switch() assigns + * coordinates in the preference order top > left > right + * > bottom edge. Also swap the lower line's coordinates + * if necessary so they are in the same order as the upper + * line; depending on the position of the lines, the + * preference order may result in coordinates being + * swapped. */ + int switch_is_top, invert_second; + if (switch_y0 == 0) { + if (mipmap_y0 != 0) { + switch_is_top = 1; + invert_second = (switch_x1 < switch_x0 + && mipmap_x0 == 0); + } else if (switch_x1 < switch_x0) { + switch_is_top = (switch_x0 < mipmap_x0); + invert_second = 0; + } else { + switch_is_top = (switch_x0 > mipmap_x0); + invert_second = 0; + } + } else if (mipmap_y0 == 0) { + switch_is_top = 0; + invert_second = (mipmap_x1 < mipmap_x0 + && switch_x0 == 0); + } else if (switch_x0 == mipmap_x0) { + switch_is_top = (switch_y0 < mipmap_y0); + invert_second = 0; + } else { + /* One line connects the left and right edges, while + * the other runs from the right edge to the bottom. + * The line connecting the left and right edges is on + * top, and the coordinate order is switched. */ + switch_is_top = (switch_x0 == 0); + invert_second = 1; + } + const int x0 = switch_is_top ? switch_x0 : mipmap_x0; + const int y0 = switch_is_top ? switch_y0 : mipmap_y0; + const int x1 = switch_is_top ? switch_x1 : mipmap_x1; + const int y1 = switch_is_top ? switch_y1 : mipmap_y1; + const int x2 = (invert_second + ? (switch_is_top ? mipmap_x1 : switch_x1) + : (switch_is_top ? mipmap_x0 : switch_x0)); + const int y2 = (invert_second + ? (switch_is_top ? mipmap_y1 : switch_y1) + : (switch_is_top ? mipmap_y0 : switch_y0)); + const int x3 = (invert_second + ? (switch_is_top ? mipmap_x0 : switch_x0) + : (switch_is_top ? mipmap_x1 : switch_x1)); + const int y3 = (invert_second + ? (switch_is_top ? mipmap_y0 : switch_y0) + : (switch_is_top ? mipmap_y1 : switch_y1)); + /* The first two vertices are always the endpoints of the + * top line. Depending on where the region is located on + * the screen, one or two corners may also be included; we + * insert either a corner or an endpoint of the lower line + * as vertices 2 and 3, then add any remaining endpoints as + * vertices 4 and 5 if necessary. */ + mipmap_nverts = 4; + mipmap_coords[0].x = x0; + mipmap_coords[0].y = y0; + mipmap_coords[1].x = x1; + mipmap_coords[1].y = y1; + if ((y0 == 0 && x0 != 0 && x0 != disp_width) && y2 != 0) { + /* Region includes the upper-left or upper-right corner. */ + mipmap_coords[2].y = 0; + if (x2 == 0) { + mipmap_coords[2].x = 0; + } else { + mipmap_coords[2].x = disp_width; + } + mipmap_coords[mipmap_nverts].x = x2; + mipmap_coords[mipmap_nverts].y = y2; + mipmap_nverts++; + if (x1 == 0 && y1 != disp_height && x3 != 0) { + /* Region also includes the lower-left corner. */ + mipmap_coords[3].x = 0; + mipmap_coords[3].y = disp_height; + mipmap_coords[mipmap_nverts].x = x3; + mipmap_coords[mipmap_nverts].y = y3; + mipmap_nverts++; + } else if (x1 == disp_width && y1 != disp_height + && x3 != disp_width) { + /* Region also includes the lower-right corner. */ + mipmap_coords[3].x = disp_width; + mipmap_coords[3].y = disp_height; + mipmap_coords[mipmap_nverts].x = x3; + mipmap_coords[mipmap_nverts].y = y3; + mipmap_nverts++; + } else { + mipmap_coords[3].x = x3; + mipmap_coords[3].y = y3; + } + } else if ((x0 == 0 && y0 != 0 && y0 != disp_height) && x2 != 0) { + /* Region includes the lower-left corner. (In this + * case, the top line slants up-and-right and ends on + * the right edge of the screen.) */ + mipmap_coords[2].x = 0; + mipmap_coords[2].y = disp_height; + mipmap_coords[mipmap_nverts].x = x2; + mipmap_coords[mipmap_nverts].y = y2; + mipmap_nverts++; + mipmap_coords[3].x = x3; + mipmap_coords[3].y = y3; + } else { + /* Endpoint 0 of the horizon and mipmap lines are on + * the same edge of the screen. */ + mipmap_coords[2].x = x2; + mipmap_coords[2].y = y2; + if ((y0 == 0 || x0 == disp_width) + && (x1 == 0 && y1 != disp_height) + && x3 != 0 + ) { + /* Region includes the lower-left corner. */ + mipmap_coords[3].x = 0; + mipmap_coords[3].y = disp_height; + mipmap_coords[mipmap_nverts].x = x3; + mipmap_coords[mipmap_nverts].y = y3; + mipmap_nverts++; + } else if ((y0 == 0 || x0 == 0) + && (x1 == disp_width && y1 != disp_height) + && x3 != disp_width + ) { + /* Region includes the lower-right corner. */ + mipmap_coords[3].x = disp_width; + mipmap_coords[3].y = disp_height; + mipmap_coords[mipmap_nverts].x = x3; + mipmap_coords[mipmap_nverts].y = y3; + mipmap_nverts++; + } else { + mipmap_coords[3].x = x3; + mipmap_coords[3].y = y3; + } + } + } + + /* Draw the mipmapped region computed above. */ + + vertices = guGetMemory(sizeof(*vertices) * mipmap_nverts); + unsigned int i; + for (i = 0; i < mipmap_nverts; i++) { + vertices[i].x = mipmap_coords[i].x; + vertices[i].y = mipmap_coords[i].y; + const float coef_offset = coef_base + + vertices[i].x * coef_dx + + vertices[i].y * coef_dy + - first_ground_index; + vertices[i].z = + 1 / MAX(first_recip + coef_offset * slope, 1/127.5f); + Azel_transform_coordinates(vertices[i].x, vertices[i].y, + &vertices[i].u, &vertices[i].v, + ground_M, + vertices[i].z, vertices[i].z, + ground_Xp, ground_Yp); + vertices[i].u /= 512; + vertices[i].v /= 512; + if (do_shimmer) { + vertices[i].v += 0.01f * sinf(shimmer_step * (2*(float)M_PI)); + } + vertices[i].x = (vertices[i].x - disp_width/2) * vertices[i].z; + vertices[i].y = (vertices[i].y - disp_height/2) * vertices[i].z; + } + const unsigned int texture_size = 512 / mipmap_scale; + const uint8_t *texture_data = Azel_ground_cache; + for (i = 512; i > texture_size; i /= 2) { + texture_data += i * i; + } + guTexImage(0, texture_size, texture_size, texture_size, + texture_data); + guTexFlush(); + guDrawArray(GU_TRIANGLE_STRIP, GU_TRANSFORM_3D | vertex_type, + mipmap_nverts, NULL, vertices); + + /* Recompute the vertex sets based on the mipmap line, so the + * final render iteration (with the full-size ground texture) + * skips the part we just drew. Since Azel_compute_vertices() + * uses the *_is_sky variables to determine whether the top or + * bottom portion is "ground", we need to tweak those variables + * in case the entire screen is "ground" so we get the proper + * region registered. Note that we don't check the actual + * coefficients, because that may give incorrect results due to + * the "shimmering" effect; instead, we compare the coefficient + * indices to the mipmap switch index. */ + + int UL_sky_2, UR_sky_2, LL_sky_2, LR_sky_2; + if (slope > 0) { + UL_sky_2 = (coef_index_UL < mipmap_switch_index); + UR_sky_2 = (coef_index_UR < mipmap_switch_index); + LL_sky_2 = (coef_index_LL < mipmap_switch_index); + LR_sky_2 = (coef_index_LR < mipmap_switch_index); + } else { + UL_sky_2 = (coef_index_UL >= mipmap_switch_index); + UR_sky_2 = (coef_index_UR >= mipmap_switch_index); + LL_sky_2 = (coef_index_LL >= mipmap_switch_index); + LR_sky_2 = (coef_index_LR >= mipmap_switch_index); + } + Azel_compute_vertices(UL_sky_2, UR_sky_2, LL_sky_2, LR_sky_2, + mipmap_x0, mipmap_y0, mipmap_x1, mipmap_y1, + coord, nverts); + ground_coord_set = (UL_sky_2 ? 1 : 0); + + /* Copy the mipmap line coordinates to switch_* for the next + * mipmap loop. */ + + switch_x0 = mipmap_x0; + switch_y0 = mipmap_y0; + switch_x1 = mipmap_x1; + switch_y1 = mipmap_y1; + + } // if using mipmap + + /* Generate and render vertices for the lowest-scale portion of the + * the plane, if any. */ + + if (slope > 0 ? index <= last_ground_index + : index > first_ground_index) { + vertices = guGetMemory(sizeof(*vertices) * nverts[ground_coord_set]); + unsigned int i; + for (i = 0; i < nverts[ground_coord_set]; i++) { + vertices[i].x = coord[ground_coord_set][i].x; + vertices[i].y = coord[ground_coord_set][i].y; + const float coef_offset = coef_base + + vertices[i].x * coef_dx + + vertices[i].y * coef_dy + - first_ground_index; + vertices[i].z = + 1 / MAX(first_recip + coef_offset * slope, 1/127.5f); + Azel_transform_coordinates(vertices[i].x, vertices[i].y, + &vertices[i].u, &vertices[i].v, + ground_M, + vertices[i].z, vertices[i].z, + ground_Xp, ground_Yp); + vertices[i].u /= (Azel_ground_reduced ? 1024 : 512); + vertices[i].v /= (Azel_ground_reduced ? 1024 : 512); + if (do_shimmer) { + /* Fake the "shimmering" effect with a simple sinusoidal + * offset. The PSP doesn't have enough hardware operators + * to do what we really want (which is multiply each + * texture coordinate by a*sin(b*y/z), where a and b are + * constants). */ + vertices[i].v += 0.01f * sinf(shimmer_step * (2*(float)M_PI)); + } + vertices[i].x = (vertices[i].x - disp_width/2) * vertices[i].z; + vertices[i].y = (vertices[i].y - disp_height/2) * vertices[i].z; + } + const unsigned int texture_size = 512 / mipmap_scale; + const uint8_t *texture_data = Azel_ground_cache; + for (i = 512; i > texture_size; i /= 2) { + texture_data += i * i; + } + guTexImage(0, texture_size, texture_size, texture_size, + texture_data); + guTexFlush(); + guDrawArray(GU_TRIANGLE_STRIP, GU_TRANSFORM_3D | vertex_type, + nverts[ground_coord_set], NULL, vertices); + } + + } // if (ground_min == ground_max) + + /* All done. Make sure to reset the wrapping flags since other code + * will expect wraparound to be disabled. */ + + guTexWrap(GU_CLAMP, GU_CLAMP); + return 1; + + #undef READ_COEF +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_get_rotation_matrix: Calculate the rotation matrix and scaling + * parameters for a sky or ground parameter set. Helper function for + * Azel_draw_RBG0(). + * + * [Parameters] + * address: Address of parameter set in VDP2 RAM + * M: Pointer to 2x3 array to receive rotation matrix values + * kx_ret: Pointer to variable to receive X scale value (NULL allowed) + * ky_ret: Pointer to variable to receive Y scale value (NULL allowed) + * Xp_ret: Pointer to variable to receive X offset value + * Yp_ret: Pointer to variable to receive Y offset value + * [Return value] + * None + */ +static void Azel_get_rotation_matrix(uint32_t address, float M[2][3], + float *kx_ret, float *ky_ret, + float *Xp_ret, float *Yp_ret) +{ + /* The GET_* macros are borrowed from psp-video-rotate.c. */ + + #define GET_SHORT(nbits) \ + (address += 2, (int32_t)((int16_t)T1ReadWord(Vdp2Ram,address-2) \ + << (16-nbits)) >> (16-nbits)) + #define GET_SIGNED_FLOAT(nbits) \ + (address += 4, ((((int32_t)T1ReadLong(Vdp2Ram,address-4) \ + << (32-nbits)) >> (32-nbits)) & ~0x3F) / 65536.0f) + #define GET_UNSIGNED_FLOAT(nbits) \ + (address += 4, (((uint32_t)T1ReadLong(Vdp2Ram,address-4) \ + & (0xFFFFFFFFU >> (32-nbits))) & ~0x3F) / 65536.0f) + + const float Xst = GET_SIGNED_FLOAT(29); + const float Yst = GET_SIGNED_FLOAT(29); + const float Zst = GET_SIGNED_FLOAT(29); + const float deltaXst = GET_SIGNED_FLOAT(19); + const float deltaYst = GET_SIGNED_FLOAT(19); + const float deltaX = GET_SIGNED_FLOAT(19); + const float deltaY = GET_SIGNED_FLOAT(19); + const float A = GET_SIGNED_FLOAT(20); + const float B = GET_SIGNED_FLOAT(20); + const float C = GET_SIGNED_FLOAT(20); + const float D = GET_SIGNED_FLOAT(20); + const float E = GET_SIGNED_FLOAT(20); + const float F = GET_SIGNED_FLOAT(20); + const float Px = GET_SHORT(14); + const float Py = GET_SHORT(14); + const float Pz = GET_SHORT(14); + address += 2; + const float Cx = GET_SHORT(14); + const float Cy = GET_SHORT(14); + const float Cz = GET_SHORT(14); + address += 2; + const float Mx = GET_SIGNED_FLOAT(30); + const float My = GET_SIGNED_FLOAT(30); + const float kx = GET_SIGNED_FLOAT(24); + const float ky = GET_SIGNED_FLOAT(24); + + #undef GET_SHORT + #undef GET_SIGNED_FLOAT + #undef GET_UNSIGNED_FLOAT + + M[0][0] = (A * deltaX) + (B * deltaY); + M[0][1] = (A * deltaXst) + (B * deltaYst); + M[0][2] = (A * (Xst - Px)) + (B * (Yst - Py)) + (C * (Zst - Pz)); + M[1][0] = (D * deltaX) + (E * deltaY); + M[1][1] = (D * deltaXst) + (E * deltaYst); + M[1][2] = (D * (Xst - Px)) + (E * (Yst - Py)) + (F * (Zst - Pz)); + *Xp_ret = (A * (Px - Cx)) + (B * (Py - Cy)) + (C * (Pz - Cz)) + Cx + Mx; + *Yp_ret = (D * (Px - Cx)) + (E * (Py - Cy)) + (F * (Pz - Cz)) + Cy + My; + + if (kx_ret) { + *kx_ret = kx; + } + if (ky_ret) { + *ky_ret = ky; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_transform_coordinates: Transform screen to texture coordinates + * given the rotation matrix and scaling parameters for a sky or ground + * parameter set. Helper function for Azel_draw_RBG0(). + * + * [Parameters] + * x, y: Screen coordinates to transform + * u_ret, v_ret: Pointers to variables to receive transformed coordinates + * M: Pointer to 2x3 array containing rotation matrix values + * kx, ky: X/Y scale values + * Xp, Yp: X/Y offset values + * [Return value] + * None + */ +static void Azel_transform_coordinates(const float x, const float y, + float *u_ret, float *v_ret, + float M[2][3], + const float kx, const float ky, + const float Xp, const float Yp) +{ + *u_ret = (M[0][0]*x + M[0][1]*y + M[0][2]) * kx + Xp; + *v_ret = (M[1][0]*x + M[1][1]*y + M[1][2]) * ky + Yp; +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_compute_switch: Compute the endpoints of the switch (horizon) line + * between sky and ground. Helper function for Azel_draw_RBG0(). + * + * [Parameters] + * All parameters are local variables or pointers thereto passed from + * Azel_draw_RBG0() + * [Return value] + * None + */ +static inline void Azel_compute_switch( + uint32_t coef_base, uint32_t coef_switch_index, + const uint32_t coef_index_UL, const uint32_t coef_index_UR, + const uint32_t coef_index_LL, const uint32_t coef_index_LR, + float coef_dx, float coef_dy, + int *switch_x0, int *switch_y0, int *switch_x1, int *switch_y1) +{ + *switch_x0 = *switch_y0 = *switch_x1 = *switch_y1 = -1; + + if (coef_dx == 0) { + + *switch_x0 = 0; + *switch_y0 = iceilf((coef_switch_index - coef_base) / coef_dy); + *switch_x1 = disp_width; + *switch_y1 = *switch_y0; + + } else if (coef_dy == 0) { + + *switch_x0 = iceilf((coef_switch_index - coef_base) / coef_dx); + *switch_y0 = 0; + *switch_x1 = *switch_x0; + *switch_y1 = disp_height; + + } else { // coef_dx != 0 && coef_dy != 0 + + const float top_x = + (int32_t)(coef_switch_index - coef_index_UL) / coef_dx; + if (top_x > -1 && top_x <= disp_width) { + *switch_x0 = iceilf(top_x); + *switch_y0 = 0; + } + const float bottom_x = + (int32_t)(coef_switch_index - coef_index_LL) / coef_dx; + if (bottom_x > -1 && bottom_x <= disp_width) { + *switch_x1 = iceilf(bottom_x); + *switch_y1 = disp_height; + } + const float left_y = + (int32_t)(coef_switch_index - coef_index_UL) / coef_dy; + if (left_y > -1 && left_y <= disp_height) { + if (*switch_x0 < 0) { + *switch_x0 = 0; + *switch_y0 = iceilf(left_y); + } else if (*switch_x1 < 0) { + *switch_x1 = 0; + *switch_y1 = iceilf(left_y); + } + } + const float right_y = + (int32_t)(coef_switch_index - coef_index_UR) / coef_dy; + if (right_y > -1 && right_y <= disp_height) { + if (*switch_x0 < 0) { + *switch_x0 = disp_width; + *switch_y0 = iceilf(right_y); + } else if (*switch_x1 < 0) { + *switch_x1 = disp_width; + *switch_y1 = iceilf(right_y); + } + } + + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * Azel_compute_vertices: Compute the two sets of vertices for drawing + * sky and ground sections of an RBG0 layer. Helper function for + * Azel_draw_RBG0(). + * + * [Parameters] + * All parameters are local variables/arrays passed from Azel_draw_RBG0() + * [Return value] + * None + */ +static inline void Azel_compute_vertices( + int UL_is_sky, int UR_is_sky, int LL_is_sky, int LR_is_sky, + int switch_x0, int switch_y0, int switch_x1, int switch_y1, + struct Azel_RBG0_coord coord[2][5], unsigned int nverts[2]) +{ + coord[0][0].x = 0; + coord[0][0].y = 0; + coord[0][0].overdraw_x = -1; + coord[0][0].overdraw_y = -1; + + if (UR_is_sky == UL_is_sky) { + + coord[0][1].x = disp_width; + coord[0][1].y = 0; + coord[0][1].overdraw_x = +1; + coord[0][1].overdraw_y = -1; + + if (LL_is_sky == LR_is_sky) { + + coord[0][2].x = switch_x0; + coord[0][2].y = switch_y0; + coord[0][2].overdraw_x = -1; + coord[0][2].overdraw_y = +1; + coord[0][3].x = switch_x1; + coord[0][3].y = switch_y1; + coord[0][3].overdraw_x = +1; + coord[0][3].overdraw_y = +1; + nverts[0] = 4; + + if (LL_is_sky == UL_is_sky) { + nverts[1] = 0; + } else { + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = -1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = switch_x1; + coord[1][1].y = switch_y1; + coord[1][1].overdraw_x = +1; + coord[1][1].overdraw_y = -1; + coord[1][2].x = 0; + coord[1][2].y = disp_height; + coord[1][2].overdraw_x = -1; + coord[1][2].overdraw_y = +1; + coord[1][3].x = disp_width; + coord[1][3].y = disp_height; + coord[1][3].overdraw_x = +1; + coord[1][3].overdraw_y = +1; + nverts[1] = 4; + } + + } else if (LL_is_sky == UL_is_sky) { + + coord[0][2].x = 0; + coord[0][2].y = disp_height; + coord[0][2].overdraw_x = -1; + coord[0][2].overdraw_y = +1; + coord[0][3].x = switch_x0; + coord[0][3].y = switch_y0; + coord[0][3].overdraw_x = +1; + coord[0][3].overdraw_y = +1; + coord[0][4].x = switch_x1; + coord[0][4].y = switch_y1; + coord[0][4].overdraw_x = +1; + coord[0][4].overdraw_y = +1; + nverts[0] = 5; + + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = +1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = switch_x1; + coord[1][1].y = switch_y1; + coord[1][1].overdraw_x = -1; + coord[1][1].overdraw_y = +1; + coord[1][2].x = disp_width; + coord[1][2].y = disp_height; + coord[1][3].overdraw_x = +1; + coord[1][3].overdraw_y = +1; + nverts[1] = 3; + + } else { // LR_is_sky == UL_is_sky + + coord[0][2].x = switch_x0; + coord[0][2].y = switch_y0; + coord[0][2].overdraw_x = -1; + coord[0][2].overdraw_y = +1; + coord[0][3].x = disp_width; + coord[0][3].y = disp_height; + coord[0][3].overdraw_x = +1; + coord[0][3].overdraw_y = +1; + coord[0][4].x = switch_x1; + coord[0][4].y = switch_y1; + coord[0][4].overdraw_x = -1; + coord[0][4].overdraw_y = +1; + nverts[0] = 5; + + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = -1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = switch_x1; + coord[1][1].y = switch_y1; + coord[1][1].overdraw_x = +1; + coord[1][1].overdraw_y = +1; + coord[1][2].x = 0; + coord[1][2].y = disp_height; + coord[1][2].overdraw_x = -1; + coord[1][2].overdraw_y = +1; + nverts[1] = 3; + + } + + } else if (LL_is_sky == UL_is_sky) { + + coord[0][1].x = 0; + coord[0][1].y = disp_height; + coord[0][1].overdraw_x = -1; + coord[0][1].overdraw_y = +1; + + if (LR_is_sky != UL_is_sky) { + + coord[0][2].x = switch_x0; + coord[0][2].y = switch_y0; + coord[0][2].overdraw_x = +1; + coord[0][2].overdraw_y = -1; + coord[0][3].x = switch_x1; + coord[0][3].y = switch_y1; + coord[0][3].overdraw_x = +1; + coord[0][3].overdraw_y = +1; + nverts[0] = 4; + + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = -1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = switch_x1; + coord[1][1].y = switch_y1; + coord[1][1].overdraw_x = -1; + coord[1][1].overdraw_y = +1; + coord[1][2].x = disp_width; + coord[1][2].y = 0; + coord[1][2].overdraw_x = +1; + coord[1][2].overdraw_y = -1; + coord[1][3].x = disp_width; + coord[1][3].y = disp_height; + coord[1][3].overdraw_x = +1; + coord[1][3].overdraw_y = +1; + nverts[1] = 4; + + } else { // LR_is_sky == UL_is_sky + + coord[0][2].x = switch_x0; + coord[0][2].y = switch_y0; + coord[0][2].overdraw_x = +1; + coord[0][2].overdraw_y = -1; + coord[0][3].x = disp_width; + coord[0][3].y = disp_height; + coord[0][3].overdraw_x = +1; + coord[0][3].overdraw_y = +1; + coord[0][4].x = switch_x1; + coord[0][4].y = switch_y1; + coord[0][4].overdraw_x = +1; + coord[0][4].overdraw_y = -1; + nverts[0] = 5; + + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = -1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = switch_x1; + coord[1][1].y = switch_y1; + coord[1][1].overdraw_x = +1; + coord[1][1].overdraw_y = +1; + coord[1][2].x = disp_width; + coord[1][2].y = 0; + coord[1][2].overdraw_x = +1; + coord[1][2].overdraw_y = -1; + nverts[1] = 3; + + } + + } else { + + coord[0][1].x = switch_x0; + coord[0][1].y = switch_y0; + coord[0][1].overdraw_x = +1; + coord[0][1].overdraw_y = -1; + coord[0][2].x = switch_x1; + coord[0][2].y = switch_y1; + coord[0][2].overdraw_x = -1; + coord[0][2].overdraw_y = +1; + nverts[0] = 3; + + coord[1][0].x = switch_x0; + coord[1][0].y = switch_y0; + coord[1][0].overdraw_x = -1; + coord[1][0].overdraw_y = -1; + coord[1][1].x = disp_width; + coord[1][1].y = 0; + coord[1][1].overdraw_x = +1; + coord[1][1].overdraw_y = -1; + coord[1][2].x = switch_x1; + coord[1][2].y = switch_y1; + coord[1][2].overdraw_x = -1; + coord[1][2].overdraw_y = -1; + coord[1][3].x = disp_width; + coord[1][3].y = disp_height; + coord[1][3].overdraw_x = +1; + coord[1][3].overdraw_y = +1; + coord[1][4].x = 0; + coord[1][4].y = disp_height; + coord[1][4].overdraw_x = -1; + coord[1][4].overdraw_y = +1; + nverts[1] = 5; + + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video.c b/yabause/src/psp/psp-video.c new file mode 100644 index 0000000000..de30aaa4e3 --- /dev/null +++ b/yabause/src/psp/psp-video.c @@ -0,0 +1,2160 @@ +/* src/psp/psp-video.c: PSP video interface module + Copyright 2009-2010 Andrew Church + Based on src/vidogl.c by Guillaume Duhamel and others + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../vdp1.h" +#include "../vdp2.h" +#include "../vidshared.h" + +#include "config.h" +#include "display.h" +#include "font.h" +#include "gu.h" +#include "misc.h" +#include "psp-video.h" +#include "psp-video-internal.h" +#include "texcache.h" +#include "timing.h" + +/*************************************************************************/ +/************************* Interface definition **************************/ +/*************************************************************************/ + +/* Interface function declarations (must come before interface definition) */ + +static int psp_video_init(void); +static void psp_video_deinit(void); +static void psp_video_resize(unsigned int width, unsigned int height, + int fullscreen); +static int psp_video_is_fullscreen(void); +static void psp_video_debug_message(char *format, ...); + +static int psp_vdp1_reset(void); +static void psp_vdp1_draw_start(void); +static void psp_vdp1_draw_end(void); +static void psp_vdp1_normal_sprite_draw(void); +static void psp_vdp1_scaled_sprite_draw(void); +static void psp_vdp1_distorted_sprite_draw(void); +static void psp_vdp1_polygon_draw(void); +static void psp_vdp1_polyline_draw(void); +static void psp_vdp1_line_draw(void); +static void psp_vdp1_user_clipping(void); +static void psp_vdp1_system_clipping(void); +static void psp_vdp1_local_coordinate(void); + +static int psp_vdp2_reset(void); +static void psp_vdp2_draw_start(void); +static void psp_vdp2_draw_end(void); +static void psp_vdp2_draw_screens(void); +static void psp_vdp2_set_resolution(u16 TVMD); +static void FASTCALL psp_vdp2_set_priority_NBG0(int priority); +static void FASTCALL psp_vdp2_set_priority_NBG1(int priority); +static void FASTCALL psp_vdp2_set_priority_NBG2(int priority); +static void FASTCALL psp_vdp2_set_priority_NBG3(int priority); +static void FASTCALL psp_vdp2_set_priority_RBG0(int priority); + +/*-----------------------------------------------------------------------*/ + +/* Module interface definition */ + +VideoInterface_struct VIDPSP = { + .id = VIDCORE_PSP, + .Name = "PSP Video Interface", + .Init = psp_video_init, + .DeInit = psp_video_deinit, + .Resize = psp_video_resize, + .IsFullscreen = psp_video_is_fullscreen, + .OnScreenDebugMessage = psp_video_debug_message, + + .Vdp1Reset = psp_vdp1_reset, + .Vdp1DrawStart = psp_vdp1_draw_start, + .Vdp1DrawEnd = psp_vdp1_draw_end, + .Vdp1NormalSpriteDraw = psp_vdp1_normal_sprite_draw, + .Vdp1ScaledSpriteDraw = psp_vdp1_scaled_sprite_draw, + .Vdp1DistortedSpriteDraw = psp_vdp1_distorted_sprite_draw, + .Vdp1PolygonDraw = psp_vdp1_polygon_draw, + .Vdp1PolylineDraw = psp_vdp1_polyline_draw, + .Vdp1LineDraw = psp_vdp1_line_draw, + .Vdp1UserClipping = psp_vdp1_user_clipping, + .Vdp1SystemClipping = psp_vdp1_system_clipping, + .Vdp1LocalCoordinate = psp_vdp1_local_coordinate, + + .Vdp2Reset = psp_vdp2_reset, + .Vdp2DrawStart = psp_vdp2_draw_start, + .Vdp2DrawEnd = psp_vdp2_draw_end, + .Vdp2DrawScreens = psp_vdp2_draw_screens, + .Vdp2SetResolution = psp_vdp2_set_resolution, + .Vdp2SetPriorityNBG0 = psp_vdp2_set_priority_NBG0, + .Vdp2SetPriorityNBG1 = psp_vdp2_set_priority_NBG1, + .Vdp2SetPriorityNBG2 = psp_vdp2_set_priority_NBG2, + .Vdp2SetPriorityNBG3 = psp_vdp2_set_priority_NBG3, + .Vdp2SetPriorityRBG0 = psp_vdp2_set_priority_RBG0, +}; + +/*************************************************************************/ +/************************* Global and local data *************************/ +/*************************************************************************/ + +/**** Exported data ****/ + +/* Color table generated from VDP2 color RAM */ +__attribute__((aligned(64))) uint16_t global_clut_16[0x800]; +__attribute__((aligned(64))) uint32_t global_clut_32[0x800]; + +/* Displayed width and height */ +unsigned int disp_width, disp_height; + +/* Scale (right-shift) applied to X and Y coordinates */ +unsigned int disp_xscale, disp_yscale; + +/* Total number of frames to skip before we draw the next one */ +unsigned int frames_to_skip; + +/* Number of frames skipped so far since we drew the last one */ +unsigned int frames_skipped; + +/* VDP1 color component offset values (-0xFF...+0xFF) */ +int32_t vdp1_rofs, vdp1_gofs, vdp1_bofs; + +/*-----------------------------------------------------------------------*/ + +/**** Internal data ****/ + +/*----------------------------------*/ + +/* Pending infoline text (malloc()ed, or NULL if none) and color */ +static char *infoline_text; +static uint32_t infoline_color; + +/*----------------------------------*/ + +/* Current average frame rate (rolling average) */ +static float average_fps; + +/* Flag indicating whether graphics should be drawn this frame */ +static uint8_t draw_graphics; + +/* Background priorities (NBG0, NBG1, NBG2, NBG3, RBG0) */ +static uint8_t bg_priority[5]; + +/*----------------------------------*/ + +/* Custom drawing function specified for each background layer */ +static CustomDrawRoutine *custom_draw_func[5]; + +/* Is the RBG0 drawing function fast enough to consider it a normal layer + * for timing purposes? */ +static uint8_t RBG0_draw_func_is_fast; + +/* Did we draw a slow RBG0 this frame? */ +static uint8_t drew_slow_RBG0; + +/*----------------------------------*/ + +/* Rendering data for sprites, polygons, and lines (a copy of all + * parameters except priority passed to vdp1_render_queue() */ +typedef struct VDP1RenderData_ { + uint32_t texture_key; + int primitive; + int vertex_type; + int count; + const void *indices; + const void *vertices; +} VDP1RenderData; + +/* VDP1 render queues (one for each priority level) */ +typedef struct VDP1RenderQueue_ { + VDP1RenderData *queue; // Array of entries (dynamically expanded) + int size; // Size of queue array, in entries + int len; // Number of entries currently in array +} VDP1RenderQueue; +static VDP1RenderQueue vdp1_queue[8]; + +/* Amount to expand a queue's array when it gets full */ +#define VDP1_QUEUE_EXPAND_SIZE 1000 + +/*----------------------------------*/ + +/* Flags indicating whether each 4k page of VDP1/2 RAM contains any + * persistently-cached texture data */ +static uint8_t vdp1_page_cached[0x80], vdp2_page_cached[0x80]; + +/* Checksum of each VDP1/2 RAM page containing cached texture data */ +static uint32_t vdp1_page_checksum[0x80], vdp2_page_checksum[0x80]; + +/* State of color offset settings at last cache reset */ +static uint32_t vdp1_cached_cofs; +static uint32_t vdp2_cached_cofs_regs; // CLOFEN<<16 | CLOFSL +static uint32_t vdp2_cached_cofs_A, vdp2_cached_cofs_B; + +/*************************************************************************/ + +/**** Local function declarations ****/ + +static int vdp1_is_persistent(vdp1cmd_struct *cmd); +static void vdp1_draw_lines(vdp1cmd_struct *cmd, int poly); +static void vdp1_draw_quad(vdp1cmd_struct *cmd, int textured); +static uint32_t vdp1_convert_color(uint16_t color16, int textured, + unsigned int CMDPMOD); +static uint32_t vdp1_get_cmd_color(vdp1cmd_struct *cmd); +static uint32_t vdp1_get_cmd_color_pri(vdp1cmd_struct *cmd, int textured, + int *priority_ret); +static uint16_t vdp1_process_sprite_color(uint16_t color16, int *priority_ret, + int *alpha_ret); +static uint32_t vdp1_cache_sprite_texture( + vdp1cmd_struct *cmd, int width, int height, int *priority_ret, + int *alpha_ret); +static inline void vdp1_queue_render( + int priority, uint32_t texture_key, int primitive, + int vertex_type, int count, const void *indices, const void *vertices); +static void vdp1_run_queue(int priority); + +static inline void vdp2_get_color_offsets(uint16_t mask, int32_t *rofs_ret, + int32_t *gofs_ret, int32_t *bofs_ret); + +static void vdp2_draw_bg(void); +static void vdp2_draw_graphics(int layer); + +/*************************************************************************/ +/********************** General interface functions **********************/ +/*************************************************************************/ + +/** + * psp_video_init: Initialize the peripheral interface. + * + * [Parameters] + * None + * [Return value] + * Zero on success, negative on error + */ +static int psp_video_init(void) +{ + /* Set some reasonable defaults. */ + disp_width = 320; + disp_height = 224; + disp_xscale = 0; + disp_yscale = 0; + + /* Always draw the first frame. */ + frames_to_skip = 0; + frames_skipped = 0; + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * psp_video_deinit: Shut down the peripheral interface. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_video_deinit(void) +{ + /* We don't implement shutting down, so nothing to do. */ +} + +/*************************************************************************/ + +/** + * psp_video_resize: Resize the display window. A no-op on PSP. + * + * [Parameters] + * width: New window width + * height: New window height + * fullscreen: Nonzero to use fullscreen mode, else zero + * [Return value] + * None + */ +static void psp_video_resize(unsigned int width, unsigned int height, + int fullscreen) +{ +} + +/*************************************************************************/ + +/** + * psp_video_is_fullscreen: Return whether the display is currently in + * fullscreen mode. Always returns true (nonzero) on PSP. + * + * [Parameters] + * None + * [Return value] + * Nonzero if in fullscreen mode, else zero + */ +static int psp_video_is_fullscreen(void) +{ + return 1; +} + +/*************************************************************************/ + +/** + * psp_video_debug_message: Display a debug message on the screen. + * + * [Parameters] + * format: printf()-style format string + * [Return value] + * None + */ +static void psp_video_debug_message(char *format, ...) +{ + /* Not implemented */ +} + +/*************************************************************************/ +/********************* PSP-only interface functions **********************/ +/*************************************************************************/ + +/** + * psp_video_infoline: Display an information line on the bottom of the + * screen. The text will be displayed for one frame only; call this + * function every frame to keep the text visible. + * + * [Parameters] + * color: Text color (0xAABBGGRR) + * text: Text string + * [Return value] + * None + */ +void psp_video_infoline(uint32_t color, const char *text) +{ + infoline_text = strdup(text); + if (UNLIKELY(!infoline_text)) { + DMSG("Failed to strdup(%s)", text); + } + infoline_color = color; +} + +/*************************************************************************/ + +/** + * psp_video_set_draw_routine: Set a custom drawing routine for a specific + * graphics layer. If "is_fast" is true when setting a routine for RBG0, + * the frame rate will not be halved regardless of the related setting in + * the configuration menu. + * + * [Parameters] + * layer: Graphics layer (BG_*) + * func: Drawing routine (NULL to clear any previous setting) + * is_fast: For BG_RBG0, indicates whether the routine is fast enough + * to be considered a non-distorted layer for the purposes + * of frame rate adjustment; ignored for other layers + * [Return value] + * None + */ +void psp_video_set_draw_routine(int layer, CustomDrawRoutine *func, + int is_fast) +{ + PRECOND(layer >= BG_NBG0 && layer <= BG_RBG0, return); + custom_draw_func[layer] = func; + if (layer == BG_RBG0) { + RBG0_draw_func_is_fast = is_fast; + } +} + +/*************************************************************************/ + +/** + * vdp2_is_persistent: Return whether the tile at the given address in + * VDP2 RAM is persistently cacheable. + * + * [Parameters] + * address: Tile address in VDP2 RAM + * [Return value] + * Nonzero if tile texture can be persistently cached, else zero + */ +int vdp2_is_persistent(uint32_t address) +{ + const unsigned int page = address >> 12; + if (!vdp2_page_cached[page]) { + vdp2_page_checksum[page] = + checksum_fast32((const uint32_t *)(Vdp2Ram + (page<<12)), 1024); + vdp2_page_cached[page] = 1; + } + return 1; +} + +/*************************************************************************/ +/******************* VDP1-specific interface functions *******************/ +/*************************************************************************/ + +/** + * psp_vdp1_reset: Reset the VDP1 state. + * + * [Parameters] + * None + * [Return value] + * Unknown (always zero) + */ +static int psp_vdp1_reset(void) +{ + /* Nothing to do. */ + return 0; +} + +/*************************************************************************/ + +/** + * psp_vdp1_draw_start: Prepare for VDP1 drawing. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_draw_start(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + /* Clear out all the rendering queues (just to be safe). */ + int priority; + for (priority = 0; priority < 8; priority++) { + vdp1_queue[priority].len = 0; + } + + /* Get the color offsets. */ + vdp2_get_color_offsets(1<<6, &vdp1_rofs, &vdp1_gofs, &vdp1_bofs); +} + +/*************************************************************************/ + + +/** + * psp_vdp1_draw_end: Finish VDP1 drawing. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_draw_end(void) +{ + /* Nothing to do */ +} + +/*************************************************************************/ + + +/** + * psp_vdp1_normal_sprite_draw: Draw an unscaled rectangular sprite. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_normal_sprite_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + int width = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; + int height = cmd.CMDSIZE & 0xFF; + cmd.CMDXB = cmd.CMDXA + width; cmd.CMDYB = cmd.CMDYA; + cmd.CMDXC = cmd.CMDXA + width; cmd.CMDYC = cmd.CMDYA + height; + cmd.CMDXD = cmd.CMDXA; cmd.CMDYD = cmd.CMDYA + height; + + vdp1_draw_quad(&cmd, 1); +} + +/*************************************************************************/ + +/** + * psp_vdp1_scaled_sprite_draw: Draw a scaled rectangular sprite. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_scaled_sprite_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + + if (!(cmd.CMDCTRL & 0x0F00)) { + /* Size is directly specified. */ + cmd.CMDXC++; cmd.CMDYC++; + cmd.CMDXB = cmd.CMDXC; cmd.CMDYB = cmd.CMDYA; + cmd.CMDXD = cmd.CMDXA; cmd.CMDYD = cmd.CMDYC; + } else { + /* Scale around a particular point (left/top, center, right/bottom). */ + int new_w = cmd.CMDXB + 1; + int new_h = cmd.CMDYB + 1; + if ((cmd.CMDCTRL & 0x300) == 0x200) { + cmd.CMDXA -= cmd.CMDXB / 2; + } else if ((cmd.CMDCTRL & 0x300) == 0x300) { + cmd.CMDXA -= cmd.CMDXB; + } + if ((cmd.CMDCTRL & 0xC00) == 0x800) { + cmd.CMDYA -= cmd.CMDYB / 2; + } else if ((cmd.CMDCTRL & 0xC00) == 0xC00) { + cmd.CMDYA -= cmd.CMDYB; + } + cmd.CMDXB = cmd.CMDXA + new_w; cmd.CMDYB = cmd.CMDYA; + cmd.CMDXC = cmd.CMDXA + new_w; cmd.CMDYC = cmd.CMDYA + new_h; + cmd.CMDXD = cmd.CMDXA; cmd.CMDYD = cmd.CMDYA + new_h; + } + + vdp1_draw_quad(&cmd, 1); +} + +/*************************************************************************/ + +/** + * psp_vdp1_distorted_sprite_draw: Draw a sprite on an arbitrary + * quadrilateral. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_distorted_sprite_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + vdp1_draw_quad(&cmd, 1); +} + +/*************************************************************************/ + +/** + * psp_vdp1_polygon_draw: Draw an untextured quadrilateral. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_polygon_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + vdp1_draw_quad(&cmd, 0); +} + +/*************************************************************************/ + +/** + * psp_vdp1_polyline_draw: Draw four connected lines. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_polyline_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + vdp1_draw_lines(&cmd, 1); +} + +/*************************************************************************/ + +/** + * psp_vdp1_line_draw: Draw a single line. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_line_draw(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + vdp1cmd_struct cmd; + Vdp1ReadCommand(&cmd, Vdp1Regs->addr); + vdp1_draw_lines(&cmd, 0); +} + +/*************************************************************************/ + +/** + * psp_vdp1_user_clipping: Set the user clipping coordinates. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_user_clipping(void) +{ + Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); + Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); + Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); +} + +/*************************************************************************/ + +/** + * psp_vdp1_system_clipping: Set the system clipping coordinates. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_system_clipping(void) +{ + Vdp1Regs->systemclipX1 = 0; + Vdp1Regs->systemclipY1 = 0; + Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); + Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); +} + +/*************************************************************************/ + +/** + * psp_vdp1_local_coordinate: Set coordinate offset values used in drawing + * primitives. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp1_local_coordinate(void) +{ + Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); + Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); +} + +/*************************************************************************/ +/******************* VDP2-specific interface functions *******************/ +/*************************************************************************/ + +/** + * psp_vdp2_reset: Reset the VDP2 state. + * + * [Parameters] + * None + * [Return value] + * Unknown (always zero) + */ +static int psp_vdp2_reset(void) +{ + /* Nothing to do */ + return 0; +} + +/*************************************************************************/ + +/** + * psp_vdp2_draw_start: Begin drawing a video frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp2_draw_start(void) +{ + /* Apply any game-specific optimizations or tweaks. (This may involve + * adjusting the frame-skip variables, so we call it before the + * frame-skip check.) */ + psp_video_apply_tweaks(); + + /* If we're skipping this frame, we don't do anything, not even start + * a new output frame (because that forces a VBlank sync, which may + * waste time if the previous frame completed quickly). */ + if (frames_skipped < frames_to_skip) { + return; + } + + /* Load the global color lookup tables from VDP2 color RAM. */ + const uint16_t *cram = (const uint16_t *)Vdp2ColorRam; + if (Vdp2Internal.ColorMode == 2) { // 32-bit color table + int i; + for (i = 0; i < 0x400; i++) { + uint16_t xb = cram[i*2+0]; + uint16_t gr = cram[i*2+1]; + uint16_t color16 = 0x8000 | (xb<<7 & 0x7C00) + | (gr<<2 & 0x3E0) | (gr>>3 & 0x1F); + uint32_t color32 = 0xFF000000 | xb<<16 | gr; + global_clut_16[i] = color16; + global_clut_16[i+0x400] = color16; + global_clut_32[i] = color32; + global_clut_32[i+0x400] = color32; + } + } else { // 16-bit color table + int i; + for (i = 0; i < 0x800; i++) { + uint16_t color16 = 0x8000 | cram[i]; + uint32_t color32 = 0xFF000000 | (color16 & 0x7C00) << 9 + | (color16 & 0x03E0) << 6 + | (color16 & 0x001F) << 3; + global_clut_16[i] = color16; + global_clut_32[i] = color32; + } + } + + /* Start a new frame. */ + display_set_size(disp_width >> disp_xscale, disp_height >> disp_yscale); + display_begin_frame(); + + /* Clear the texture cache of transient data; also clear persistent + * data if any source RAM or color offsets were changed, or if + * persistent caching is disabled in the first place. */ + const uint32_t vdp1_cofs = (vdp1_rofs & 0x1FF) << 18 + | (vdp1_gofs & 0x1FF) << 9 + | (vdp1_bofs & 0x1FF) << 0; + const uint32_t vdp2_cofs_regs = Vdp2Regs->CLOFEN << 16 | Vdp2Regs->CLOFSL; + const uint32_t vdp2_cofs_A = (Vdp2Regs->COAR & 0x1FF) << 18 + | (Vdp2Regs->COAG & 0x1FF) << 9 + | (Vdp2Regs->COAB & 0x1FF) << 0; + const uint32_t vdp2_cofs_B = (Vdp2Regs->COBR & 0x1FF) << 18 + | (Vdp2Regs->COBG & 0x1FF) << 9 + | (Vdp2Regs->COBB & 0x1FF) << 0; + int need_reset = 0; + if (!config_get_cache_textures()) { + need_reset = 1; + } else if (vdp1_cofs != vdp1_cached_cofs + || vdp2_cofs_regs != vdp2_cached_cofs_regs + || vdp2_cofs_A != vdp2_cached_cofs_A + || vdp2_cofs_B != vdp2_cached_cofs_B) { + DMSG("Color offsets changed, clearing cache"); + need_reset = 1; + } else { + unsigned int page; + for (page = 0; page < 0x80; page++) { + if (vdp1_page_cached[page]) { + const uint32_t sum = + checksum_fast32((const uint32_t *)(Vdp1Ram + (page<<12)), 1024); + if (sum != vdp1_page_checksum[page]) { + DMSG("VDP1 page 0x%05X checksum changed (%08X -> %08X)," + " clearing cache", + page<<12, vdp1_page_checksum[page], sum); + need_reset = 1; + break; + } + } + if (vdp2_page_cached[page]) { + const uint32_t sum = + checksum_fast32((const uint32_t *)(Vdp2Ram + (page<<12)), 1024); + if (sum != vdp2_page_checksum[page]) { + DMSG("VDP2 page 0x%05X checksum changed (%08X -> %08X)," + " clearing cache", + page<<12, vdp2_page_checksum[page], sum); + need_reset = 1; + break; + } + } + } + } + if (need_reset) { + texcache_reset(); + memset(vdp1_page_cached, 0, sizeof(vdp1_page_cached)); + memset(vdp2_page_cached, 0, sizeof(vdp2_page_cached)); + } else { + texcache_clean(); + } + vdp1_cached_cofs = vdp1_cofs; + vdp2_cached_cofs_regs = vdp2_cofs_regs; + vdp2_cached_cofs_A = vdp2_cofs_A; + vdp2_cached_cofs_B = vdp2_cofs_B; + + /* Initialize the render state. */ + guTexFilter(GU_NEAREST, GU_NEAREST); + guTexWrap(GU_CLAMP, GU_CLAMP); + guTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + guBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + guEnable(GU_BLEND); // We treat everything as alpha-enabled + + /* Reset the draw-graphics flag (it will be set by draw_screens() if + * graphics are active). */ + draw_graphics = 0; +} + +/*************************************************************************/ + +/** + * psp_vdp2_draw_end: Finish drawing a video frame. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp2_draw_end(void) +{ + if (frames_skipped >= frames_to_skip) { + + /* Draw all graphics by priority. */ + int priority; + for (priority = 0; priority < 8; priority++) { + /* Draw background graphics first... */ + if (draw_graphics && priority > 0) { + if (bg_priority[BG_NBG3] == priority) { + vdp2_draw_graphics(BG_NBG3); + } + if (bg_priority[BG_NBG2] == priority) { + vdp2_draw_graphics(BG_NBG2); + } + if (bg_priority[BG_NBG1] == priority) { + vdp2_draw_graphics(BG_NBG1); + } + if (bg_priority[BG_NBG0] == priority) { + vdp2_draw_graphics(BG_NBG0); + } + if (bg_priority[BG_RBG0] == priority) { + vdp2_draw_graphics(BG_RBG0); + } + } + /* Then draw sprites on top... */ + vdp1_run_queue(priority); + /* And clear the rendering queue. */ + vdp1_queue[priority].len = 0; + } + + /* Always compute average FPS (even if we're not showing it), so + * the value is accurate as soon as the display is turned on. + * We use a rolling average that decays by 50% every second. */ + unsigned int frame_length = display_last_frame_length(); + if (frame_length == 0) { + frame_length = 1; // Just in case (avoid division by 0) + } + unsigned int frame_count = 1 + frames_skipped; + const float fps = (frame_count*60.0f) / frame_length; + if (!average_fps) { + /* When first starting up, just set the average to the first + * frame's frame rate. */ + average_fps = fps; + } else { + const float weight = powf(2.0f, -(1/fps)); + average_fps = (average_fps * weight) + (fps * (1-weight)); + } + if (config_get_show_fps()) { + unsigned int show_fps = iroundf(average_fps*10); + if (show_fps > 600) { + /* FPS may momentarily exceed 60.0 due to timing jitter, + * but we never show more than 60.0. */ + show_fps = 600; + } + font_printf((disp_width >> disp_xscale) - 2, 2, 1, 0xAAFF8040, + "FPS: %2d.%d (%d/%2d)", show_fps/10, show_fps%10, + frame_count, frame_length); + } + + if (infoline_text) { + font_printf((disp_width >> disp_xscale) / 2, + (disp_height >> disp_yscale) - FONT_HEIGHT - 2, 0, + infoline_color, "%s", infoline_text); + free(infoline_text); + infoline_text = NULL; + } + + display_end_frame(); + + } // if (frames_skipped >= frames_to_skip) + + if (frames_skipped < frames_to_skip) { + frames_skipped++; + timing_skip_next_sync(); // Let the emulation continue uninterrupted + } else { + frames_skipped = 0; + if (config_get_frameskip_auto()) { + // FIXME: auto frame skipping not yet implemented + frames_to_skip = 0; + } else { + frames_to_skip = config_get_frameskip_num(); + } + if (drew_slow_RBG0) { + frames_to_skip += 1 + frames_to_skip; + } + if (disp_height > 272 && frames_to_skip == 0 + && config_get_frameskip_interlace() + ) { + frames_to_skip = 1; + } + drew_slow_RBG0 = 0; + } +} + +/*************************************************************************/ + +/** + * psp_vdp2_draw_screens: Draw the VDP2 background and graphics layers. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void psp_vdp2_draw_screens(void) +{ + if (frames_skipped < frames_to_skip) { + return; + } + + /* Draw the background color(s). */ + vdp2_draw_bg(); + + /* Flag the background graphics to be drawn. */ + draw_graphics = 1; +} + +/*************************************************************************/ + +/** + * psp_vdp2_set_resolution: Change the resolution of the Saturn display. + * + * [Parameters] + * TVMD: New value of the VDP2 TVMD register + * [Return value] + * None + */ +static void psp_vdp2_set_resolution(u16 TVMD) +{ + /* Set the display width from bits 0-1. */ + disp_width = (TVMD & 1) ? 352 : 320; + if (TVMD & 2) { + disp_width *= 2; + } + + /* Set the display height from bits 4-5. Note that 0x30 is an invalid + * value for these bits and should not occur in practice; valid heights + * are 0x00=224, 0x10=240, and (for PAL) 0x20=256. */ + disp_height = 224 + (TVMD & 0x30); + if ((TVMD & 0xC0) == 0xC0) { + disp_height *= 2; // Interlaced mode + } + + /* Hi-res or interlaced displays won't fit on the PSP screen, so cut + * everything in half when using them. */ + disp_xscale = (disp_width > 352); + disp_yscale = (disp_height > 256); +} + +/*************************************************************************/ + +/** + * psp_vdp2_set_priority_{NBG[0-3],RBG0}: Set the priority of the given + * background graphics layer. + * + * [Parameters] + * priority: Priority to set + * [Return value] + * None + */ +static void FASTCALL psp_vdp2_set_priority_NBG0(int priority) +{ + bg_priority[BG_NBG0] = priority; +} + +static void FASTCALL psp_vdp2_set_priority_NBG1(int priority) +{ + bg_priority[BG_NBG1] = priority; +} + +static void FASTCALL psp_vdp2_set_priority_NBG2(int priority) +{ + bg_priority[BG_NBG2] = priority; +} + +static void FASTCALL psp_vdp2_set_priority_NBG3(int priority) +{ + bg_priority[BG_NBG3] = priority; +} + +static void FASTCALL psp_vdp2_set_priority_RBG0(int priority) +{ + bg_priority[BG_RBG0] = priority; +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * vdp1_is_persistent: Return whether the given sprite drawing command + * identifies a texture in a persistently-cacheable area of VDP1 RAM. + * + * [Parameters] + * cmd: VDP1 command structure + * [Return value] + * Nonzero if texture can be persistently cached, else zero + */ +static int vdp1_is_persistent(vdp1cmd_struct *cmd) +{ + const unsigned int first_page = cmd->CMDSRCA >> 9; + const unsigned int width_8 = (cmd->CMDSIZE >> 8) & 0x3F; + const unsigned int height = cmd->CMDSIZE & 0xFF; + const unsigned int last_page = (cmd->CMDSRCA + (width_8 * height)) >> 9; + unsigned int page; + for (page = first_page; page <= last_page; page++) { + if (!vdp1_page_cached[page]) { + vdp1_page_checksum[page] = + checksum_fast32((const uint32_t *)(Vdp1Ram + (page<<12)), 1024); + vdp1_page_cached[page] = 1; + } + } + if ((cmd->CMDPMOD>>3 & 7) == 1) { + page = cmd->CMDCOLR >> 9; + if (!vdp1_page_cached[page]) { + vdp1_page_checksum[page] = + checksum_fast32((const uint32_t *)(Vdp1Ram + (page<<12)), 1024); + vdp1_page_cached[page] = 1; + } + } + return 1; +} + +/*************************************************************************/ + +/** + * vdp1_draw_lines: Draw one or four lines based on the given VDP1 command. + * + * [Parameters] + * cmd: VDP1 command pointer + * poly: Nonzero = draw four connected lines, zero = draw a single line + * [Return value] + * None + */ +static void vdp1_draw_lines(vdp1cmd_struct *cmd, int poly) +{ + /* Get the line color and priority. */ + // FIXME: vidogl.c suggests that the priority processing done for + // sprites and polygons is not done here; is that correct? + const uint32_t color32 = vdp1_get_cmd_color(cmd); + const int priority = Vdp2Regs->PRISA & 0x7; + + /* If it's Gouraud-shaded, pick up the four endpoint colors. (Only + * the first two of these are used for single lines.) */ + uint32_t color_A, color_B, color_C, color_D; + if (cmd->CMDPMOD & 4) { // Gouraud shading bit + const uint32_t alpha = color32 & 0xFF000000; + if (vdp1_rofs | vdp1_gofs | vdp1_bofs) { + unsigned int temp_A, temp_B, temp_C, temp_D; + temp_A = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 0); + temp_B = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 2); + temp_C = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 4); + temp_D = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 6); + color_A = alpha | (adjust_color_16_32(temp_A, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_B = alpha | (adjust_color_16_32(temp_B, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_C = alpha | (adjust_color_16_32(temp_C, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_D = alpha | (adjust_color_16_32(temp_D, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + } else { + unsigned int temp_A, temp_B, temp_C, temp_D; + temp_A = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 0); + temp_B = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 2); + temp_C = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 4); + temp_D = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + 6); + color_A = alpha | (temp_A & 0x7C00) << 9 + | (temp_A & 0x03E0) << 6 + | (temp_A & 0x001F) << 3; + color_B = alpha | (temp_B & 0x7C00) << 9 + | (temp_B & 0x03E0) << 6 + | (temp_B & 0x001F) << 3; + color_C = alpha | (temp_C & 0x7C00) << 9 + | (temp_C & 0x03E0) << 6 + | (temp_C & 0x001F) << 3; + color_D = alpha | (temp_D & 0x7C00) << 9 + | (temp_D & 0x03E0) << 6 + | (temp_D & 0x001F) << 3; + } + } else { + color_A = color_B = color_C = color_D = color32; + } + + /* Set up the vertex array. */ + int nvertices = poly ? 5 : 2; + struct {uint32_t color; int16_t x, y, z, pad;} *vertices; + vertices = pspGuGetMemoryMerge(sizeof(*vertices) * nvertices); + vertices[0].color = color_A; + vertices[0].x = (cmd->CMDXA + Vdp1Regs->localX) >> disp_xscale; + vertices[0].y = (cmd->CMDYA + Vdp1Regs->localY) >> disp_yscale; + vertices[0].z = 0; + vertices[1].color = color_B; + vertices[1].x = (cmd->CMDXB + Vdp1Regs->localX) >> disp_xscale; + vertices[1].y = (cmd->CMDYB + Vdp1Regs->localY) >> disp_xscale; + vertices[1].z = 0; + if (poly) { + vertices[2].color = color_C; + vertices[2].x = (cmd->CMDXC + Vdp1Regs->localX) >> disp_xscale; + vertices[2].y = (cmd->CMDYC + Vdp1Regs->localY) >> disp_yscale; + vertices[2].z = 0; + vertices[3].color = color_D; + vertices[3].x = (cmd->CMDXD + Vdp1Regs->localX) >> disp_xscale; + vertices[3].y = (cmd->CMDYD + Vdp1Regs->localY) >> disp_yscale; + vertices[3].z = 0; + vertices[4] = vertices[0]; + } + + /* Queue the line(s). */ + vdp1_queue_render(priority, 0, GU_LINE_STRIP, + GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + nvertices, NULL, vertices); +} + +/*************************************************************************/ + +/** + * vdp1_draw_quad: Draw a quadrilateral based on the given VDP1 command. + * + * [Parameters] + * cmd: VDP1 command pointer + * textured: Nonzero if the quadrilateral is textured (i.e. a sprite) + * [Return value] + * None + */ +static void vdp1_draw_quad(vdp1cmd_struct *cmd, int textured) +{ + /* Get the width, height, and flip arguments for sprites (unused for + * untextured polygons). */ + + int width, height; + unsigned int gouraud_flip = 0; // XOR bitmask for Gouraud color addresses + if (textured) { + width = ((cmd->CMDSIZE >> 8) & 0x3F) * 8; + height = cmd->CMDSIZE & 0xFF; + if (width == 0 || height == 0) { + return; + } + /* If flipping is specified, swap the relevant coordinates in the + * "cmd" structure; this helps avoid texture glitches when the + * vertex order of a texture is changed (e.g. Panzer Dragoon Saga). + * We use inline assembly so we can load and store in 32-bit units + * without GCC complaining about strict aliasing violations. */ + switch (cmd->CMDCTRL & 0x30) { + case 0x10: { // Flip horizontally + gouraud_flip = 2; + uint32_t tempA, tempB, tempC, tempD; + asm(".set push; .set noreorder\n" + "lw %[tempA], 12(%[cmd])\n" + "lw %[tempB], 16(%[cmd])\n" + "lw %[tempC], 20(%[cmd])\n" + "lw %[tempD], 24(%[cmd])\n" + "sw %[tempA], 16(%[cmd])\n" + "sw %[tempB], 12(%[cmd])\n" + "sw %[tempC], 24(%[cmd])\n" + "sw %[tempD], 20(%[cmd])\n" + ".set pop" + : [tempA] "=&r" (tempA), [tempB] "=&r" (tempB), + [tempC] "=&r" (tempC), [tempD] "=&r" (tempD), + "=m" (cmd->CMDXA), "=m" (cmd->CMDYA), "=m" (cmd->CMDXB), + "=m" (cmd->CMDYB), "=m" (cmd->CMDXC), "=m" (cmd->CMDYC), + "=m" (cmd->CMDXD), "=m" (cmd->CMDYD) + : [cmd] "r" (cmd) + ); + break; + } // case 0x10 + case 0x20: { // Flip vertically + gouraud_flip = 6; + uint32_t tempA, tempB, tempC, tempD; + asm(".set push; .set noreorder\n" + "lw %[tempA], 12(%[cmd])\n" + "lw %[tempB], 16(%[cmd])\n" + "lw %[tempC], 20(%[cmd])\n" + "lw %[tempD], 24(%[cmd])\n" + "sw %[tempA], 24(%[cmd])\n" + "sw %[tempB], 20(%[cmd])\n" + "sw %[tempC], 16(%[cmd])\n" + "sw %[tempD], 12(%[cmd])\n" + ".set pop" + : [tempA] "=&r" (tempA), [tempB] "=&r" (tempB), + [tempC] "=&r" (tempC), [tempD] "=&r" (tempD), + "=m" (cmd->CMDXA), "=m" (cmd->CMDYA), "=m" (cmd->CMDXB), + "=m" (cmd->CMDYB), "=m" (cmd->CMDXC), "=m" (cmd->CMDYC), + "=m" (cmd->CMDXD), "=m" (cmd->CMDYD) + : [cmd] "r" (cmd) + ); + break; + } // case 0x20 + case 0x30: { // Flip horizontally and vertically + gouraud_flip = 4; + uint32_t tempA, tempB, tempC, tempD; + asm(".set push; .set noreorder\n" + "lw %[tempA], 12(%[cmd])\n" + "lw %[tempB], 16(%[cmd])\n" + "lw %[tempC], 20(%[cmd])\n" + "lw %[tempD], 24(%[cmd])\n" + "sw %[tempA], 20(%[cmd])\n" + "sw %[tempB], 24(%[cmd])\n" + "sw %[tempC], 12(%[cmd])\n" + "sw %[tempD], 16(%[cmd])\n" + ".set pop" + : [tempA] "=&r" (tempA), [tempB] "=&r" (tempB), + [tempC] "=&r" (tempC), [tempD] "=&r" (tempD), + "=m" (cmd->CMDXA), "=m" (cmd->CMDYA), "=m" (cmd->CMDXB), + "=m" (cmd->CMDYB), "=m" (cmd->CMDXC), "=m" (cmd->CMDYC), + "=m" (cmd->CMDXD), "=m" (cmd->CMDYD) + : [cmd] "r" (cmd) + ); + break; + } // case 0x30 + } // switch (cmd->CMDCTRL & 0x30) + } else { + width = height = 0; + } + + + /* Get the polygon color and priority, and load the texture if it's + * a sprite. */ + + int priority, sprite_alpha; + uint32_t color32 = vdp1_get_cmd_color_pri(cmd, textured, &priority); + uint32_t texture_key; + if (textured) { + texture_key = vdp1_cache_sprite_texture(cmd, width, height, + &priority, &sprite_alpha); + if (UNLIKELY(!texture_key)) { + DMSG("WARNING: failed to cache texture for A=(%d,%d) B=(%d,%d)" + " C=(%d,%d) D=(%d,%d)", + cmd->CMDXA + Vdp1Regs->localX, cmd->CMDYA + Vdp1Regs->localY, + cmd->CMDXB + Vdp1Regs->localX, cmd->CMDYB + Vdp1Regs->localY, + cmd->CMDXC + Vdp1Regs->localX, cmd->CMDYC + Vdp1Regs->localY, + cmd->CMDXD + Vdp1Regs->localX, cmd->CMDYD + Vdp1Regs->localY); + } + /* Convert alpha to 0-255 */ + sprite_alpha = (sprite_alpha << 3) | (sprite_alpha >> 2); + } else { + texture_key = 0; + sprite_alpha = 0xFF; + } + + /* Apply alpha depending on the color calculation settings. */ + + if (Vdp2Regs->CCCTL & 0x40) { + const unsigned int ref_priority = Vdp2Regs->SPCTL>>8 & 0x7; + switch (Vdp2Regs->SPCTL>>12 & 0x3) { + case 0: + if (priority <= ref_priority) { + color32 = (sprite_alpha << 24) | (color32 & 0x00FFFFFF); + } + break; + case 1: + if (priority == ref_priority) { + color32 = (sprite_alpha << 24) | (color32 & 0x00FFFFFF); + } + break; + case 2: + if (priority >= ref_priority) { + color32 = (sprite_alpha << 24) | (color32 & 0x00FFFFFF); + } + break; + case 3: + /* Alpha blending enabled based on high bit of color value + * (not supported in this renderer) */ + break; + } + } + + /* We don't support mesh shading; treat it as half-alpha instead. */ + + if (cmd->CMDPMOD & 0x100) { // Mesh shading bit + const unsigned int alpha = color32 >> 24; + color32 = ((alpha+1)/2) << 24 | (color32 & 0x00FFFFFF); + } + + /* If it's a Gouraud-shaded polygon, pick up the four corner colors. */ + + uint32_t color_A, color_B, color_C, color_D; + if (cmd->CMDPMOD & 4) { // Gouraud shading bit + const uint32_t alpha = color32 & 0xFF000000; + if (vdp1_rofs | vdp1_gofs | vdp1_bofs) { + unsigned int temp_A, temp_B, temp_C, temp_D; + temp_A = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (0^gouraud_flip)); + temp_B = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (2^gouraud_flip)); + temp_C = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (4^gouraud_flip)); + temp_D = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (6^gouraud_flip)); + color_A = alpha | (adjust_color_16_32(temp_A, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_B = alpha | (adjust_color_16_32(temp_B, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_C = alpha | (adjust_color_16_32(temp_C, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + color_D = alpha | (adjust_color_16_32(temp_D, vdp1_rofs, vdp1_gofs, + vdp1_bofs) & 0x00FFFFFF); + } else { + unsigned int temp_A, temp_B, temp_C, temp_D; + temp_A = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (0^gouraud_flip)); + temp_B = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (2^gouraud_flip)); + temp_C = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (4^gouraud_flip)); + temp_D = T1ReadWord(Vdp1Ram, (cmd->CMDGRDA<<3) + (6^gouraud_flip)); + color_A = alpha | (temp_A & 0x7C00) << 9 + | (temp_A & 0x03E0) << 6 + | (temp_A & 0x001F) << 3; + color_B = alpha | (temp_B & 0x7C00) << 9 + | (temp_B & 0x03E0) << 6 + | (temp_B & 0x001F) << 3; + color_C = alpha | (temp_C & 0x7C00) << 9 + | (temp_C & 0x03E0) << 6 + | (temp_C & 0x001F) << 3; + color_D = alpha | (temp_D & 0x7C00) << 9 + | (temp_D & 0x03E0) << 6 + | (temp_D & 0x001F) << 3; + } + } else { + color_A = color_B = color_C = color_D = color32; + } + + /* Set up the vertex array using a strip of 2 triangles. The Saturn + * coordinate order is A,B,C,D clockwise around the texture, so we flip + * around C and D in our vertex array. For simplicity, we assign both + * the color and U/V coordinates regardless of whether the polygon is + * textured or not; the GE is fast enough that it can handle all the + * processing in time. */ + + struct {int16_t u, v; uint32_t color; int16_t x, y, z, pad;} *vertices; + vertices = pspGuGetMemoryMerge(sizeof(*vertices) * 4); + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].color = color_A; + vertices[0].x = (cmd->CMDXA + Vdp1Regs->localX) >> disp_xscale; + vertices[0].y = (cmd->CMDYA + Vdp1Regs->localY) >> disp_yscale; + vertices[0].z = 0; + vertices[1].u = width; + vertices[1].v = 0; + vertices[1].color = color_B; + vertices[1].x = (cmd->CMDXB + Vdp1Regs->localX) >> disp_xscale; + vertices[1].y = (cmd->CMDYB + Vdp1Regs->localY) >> disp_yscale; + vertices[1].z = 0; + vertices[2].u = 0; + vertices[2].v = height; + vertices[2].color = color_D; + vertices[2].x = (cmd->CMDXD + Vdp1Regs->localX) >> disp_xscale; + vertices[2].y = (cmd->CMDYD + Vdp1Regs->localY) >> disp_yscale; + vertices[2].z = 0; + vertices[3].u = width; + vertices[3].v = height; + vertices[3].color = color_C; + vertices[3].x = (cmd->CMDXC + Vdp1Regs->localX) >> disp_xscale; + vertices[3].y = (cmd->CMDYC + Vdp1Regs->localY) >> disp_yscale; + vertices[3].z = 0; + + /* Queue the draw operation. */ + + vdp1_queue_render(priority, texture_key, + GU_TRIANGLE_STRIP, GU_TEXTURE_16BIT | GU_COLOR_8888 + | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 4, NULL, vertices); +} + +/*************************************************************************/ + +/** + * vdp1_convert_color: Convert a VDP1 16-bit color value and pixel mode to + * a 32-bit color value. Helper function for vdp1_get_cmd_color() and + * vdp1_get_cmd_color_pri(). + * + * [Parameters] + * color16: 16-bit color value + * textured: Nonzero if a textured polygon command, else zero + * CMDPMOD: Value of CMDPMOD field in VDP1 command + * [Return value] + * 32-bit color value + */ +static uint32_t vdp1_convert_color(uint16_t color16, int textured, + unsigned int CMDPMOD) +{ + uint32_t color32; + if (textured) { + color32 = 0xFFFFFF; + } else if (color16 == 0) { + color32 = adjust_color_16_32(0x0000, vdp1_rofs, vdp1_gofs, vdp1_bofs); + return color32 & 0x00FFFFFF; // Transparent regardless of CMDPMOD + } else if (color16 & 0x8000) { + color32 = adjust_color_16_32(color16, vdp1_rofs, vdp1_gofs, vdp1_bofs); + } else { + color32 = adjust_color_32_32(global_clut_32[color16 & 0x7FF], + vdp1_rofs, vdp1_gofs, vdp1_bofs); + } + + switch (CMDPMOD & 7) { + default: // Impossible, but avoid a "function may not return" warning + case 1: // Shadow + return 0x80000000; + case 4 ... 7: // Gouraud shading (handled separately) + case 0: // Replace + return 0xFF000000 | color32; + case 2: // 50% luminance + /* Clever, quick way to divide each component by 2 in one step + * (borrowed from vidsoft.c) */ + return 0xFF000000 | ((color32 & 0xFEFEFE) >> 1); + case 3: // 50% transparency + return 0x80000000 | color32; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp1_get_cmd_color: Return the 32-bit color value specified by a VDP1 + * line command. + * + * [Parameters] + * cmd: VDP1 command pointer + * [Return value] + * 32-bit color value + */ +static uint32_t vdp1_get_cmd_color(vdp1cmd_struct *cmd) +{ + return vdp1_convert_color(cmd->CMDCOLR, 0, cmd->CMDPMOD); +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp1_get_cmd_color_pri: Return the 32-bit color value and priority + * specified by a VDP1 polygon command. + * + * [Parameters] + * cmd: VDP1 command pointer + * textured: Nonzero if the polygon is textured, else zero + * priority_ret: Pointer to variable to receive priority value + * [Return value] + * 32-bit color value + */ +static uint32_t vdp1_get_cmd_color_pri(vdp1cmd_struct *cmd, int textured, + int *priority_ret) +{ + uint16_t color16 = cmd->CMDCOLR; + if (cmd->CMDCOLR & 0x8000) { + *priority_ret = Vdp2Regs->PRISA & 7; + } else { + *priority_ret = 0; // Default if not set by SPCTL + int alpha_unused; // FIXME: is this used by non-sprite quads as well? + vdp1_process_sprite_color(color16, priority_ret, &alpha_unused); + } + return vdp1_convert_color(color16, textured, cmd->CMDPMOD); +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp1_process_sprite_color: Return the color index mask, priority index, + * and alpha (color calculation) index selected by the given color register + * value and the VDP2 SPCTL register. + * + * [Parameters] + * color16: 16-bit color register value + * priority_ret: Pointer to variable to receive priority value + * [Return value] + * Mask to apply to CMDCOLR register + */ +static uint16_t vdp1_process_sprite_color(uint16_t color16, int *priority_ret, + int *alpha_ret) +{ + static const uint8_t priority_shift[16] = + { 14, 13, 14, 13, 13, 12, 12, 12, 7, 7, 6, 0, 7, 7, 6, 0 }; + static const uint8_t priority_mask[16] = + { 3, 7, 1, 3, 3, 7, 7, 7, 1, 1, 3, 0, 1, 1, 3, 0 }; + static const uint8_t alpha_shift[16] = + { 11, 11, 11, 11, 10, 11, 10, 9, 0, 6, 0, 6, 0, 6, 0, 6 }; + static const uint8_t alpha_mask[16] = + { 7, 3, 7, 3, 7, 1, 3, 7, 0, 1, 0, 3, 0, 1, 0, 3 }; + static const uint16_t color_mask[16] = + { 0x7FF, 0x7FF, 0x7FF, 0x7FF, 0x3FF, 0x7FF, 0x3FF, 0x1FF, + 0x7F, 0x3F, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF }; + + const unsigned int type = Vdp2Regs->SPCTL & 0xF; + *priority_ret = (color16 >> priority_shift[type]) & priority_mask[type]; + *alpha_ret = (color16 >> alpha_shift[type]) & alpha_mask[type]; + return color_mask[type]; +} + +/*************************************************************************/ + +/** + * vdp1_cache_sprite_texture: Cache the sprite texture designated by the + * given VDP1 command. + * + * [Parameters] + * cmd: VDP1 command pointer + * width: Sprite width (pixels; passed in to avoid recomputation) + * height: Sprite height (pixels; passed in to avoid recomputation) + * priority_ret: Pointer to variable to receive priority value + * alpha_ret: Pointer to variable to receive alpha value (0-31) + * [Return value] + * Cached texture key, or zero on error + */ +static uint32_t vdp1_cache_sprite_texture( + vdp1cmd_struct *cmd, int width, int height, int *priority_ret, + int *alpha_ret) +{ + uint16_t pixel_mask = 0xFFFF; + int pri_reg = 0, alpha_reg = 0; // Default value + + int is_indexed = 1; + uint16_t color16 = cmd->CMDCOLR; + const int pixfmt = cmd->CMDPMOD>>3 & 7; + if (pixfmt == 5) { + is_indexed = 0; + } else if (pixfmt == 1) { + /* Indirect T4 texture; see whether the first pixel references + * color RAM or uses raw RGB values. */ + const uint32_t addr = cmd->CMDSRCA << 3; + const uint8_t pixel = T1ReadByte(Vdp1Ram, addr) >> 4; + const uint32_t colortable = cmd->CMDCOLR << 3; + const uint16_t value = T1ReadWord(Vdp1Ram, colortable + pixel*2); + if (value & 0x8000) { + is_indexed = 0; + } else { + color16 = value; + } + } + if (is_indexed) { + pixel_mask = vdp1_process_sprite_color(color16, &pri_reg, &alpha_reg); + } + + *priority_ret = ((uint8_t *)&Vdp2Regs->PRISA)[pri_reg] & 0x7; + *alpha_ret = 0x1F - (((uint8_t *)&Vdp2Regs->CCRSA)[alpha_reg] & 0x1F); + + /* Cache the texture data and return the key. */ + + return texcache_cache_sprite(cmd, pixel_mask, width, height, + vdp1_is_persistent(cmd)); +} + +/*************************************************************************/ + +/** + * vdp1_queue_render: Queue a render operation from a VDP1 command. + * + * [Parameters] + * priority: Saturn display priority (0-7) + * texture_key: Texture key for sprites, zero for untextured operations + * primitive, + * vertex_type, + * count, + * indices, + * vertices: Parameters to pass to guDrawArray() + * [Return value] + * None + */ +static inline void vdp1_queue_render( + int priority, uint32_t texture_key, int primitive, + int vertex_type, int count, const void *indices, const void *vertices) +{ + /* Expand the queue if necessary. */ + if (UNLIKELY(vdp1_queue[priority].len >= vdp1_queue[priority].size)) { + const int newsize = vdp1_queue[priority].size + VDP1_QUEUE_EXPAND_SIZE; + VDP1RenderData * const newqueue = realloc(vdp1_queue[priority].queue, + newsize * sizeof(*newqueue)); + if (UNLIKELY(!newqueue)) { + DMSG("Failed to expand priority %d queue to %d entries", + priority, newsize); + return; + } + vdp1_queue[priority].queue = newqueue; + vdp1_queue[priority].size = newsize; + } + + /* Record the data passed in. */ + const int index = vdp1_queue[priority].len++; + VDP1RenderData * const entry = &vdp1_queue[priority].queue[index]; + entry->texture_key = texture_key; + entry->primitive = primitive; + entry->vertex_type = vertex_type; + entry->count = count; + entry->indices = indices; + entry->vertices = vertices; +} + +/*-----------------------------------------------------------------------*/ + +/** + * vdp1_run_queue: Run the rendering queue for the given priority level. + * + * [Parameters] + * priority: Priority level to run + * [Return value] + * None + */ +static void vdp1_run_queue(int priority) +{ + int in_texture_mode; // Remember which mode we're in + VDP1RenderData *entry = vdp1_queue[priority].queue; + VDP1RenderData * const queue_top = entry + vdp1_queue[priority].len; + + if (vdp1_queue[priority].len == 0) { + return; // Nothing to do + } + + guShadeModel(GU_SMOOTH); + guAmbientColor(0xFFFFFFFF); + guTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + if (config_get_smooth_textures()) { + guTexFilter(GU_LINEAR, GU_LINEAR); + } + if (entry->texture_key) { + guEnable(GU_TEXTURE_2D); + in_texture_mode = 1; + } else { + guDisable(GU_TEXTURE_2D); + in_texture_mode = 0; + } + for (; entry < queue_top; entry++) { + if (entry->texture_key) { + texcache_load_sprite(entry->texture_key); + if (!in_texture_mode) { + guEnable(GU_TEXTURE_2D); + in_texture_mode = 1; + } + } else { + if (in_texture_mode) { + guDisable(GU_TEXTURE_2D); + in_texture_mode = 0; + } + } + guDrawArray(entry->primitive, entry->vertex_type, + entry->count, entry->indices, entry->vertices); + } + if (in_texture_mode) { + guDisable(GU_TEXTURE_2D); + } + if (config_get_smooth_textures()) { + guTexFilter(GU_NEAREST, GU_NEAREST); + } + guShadeModel(GU_FLAT); + + guCommit(); +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * vdp2_get_color_offset: Calculate the color offsets to use for the + * specified CLOFEN/CLOFSL bit. + * + * [Parameters] + * mask: 1 << bit number to check + * rofs_ret: Pointer to variable to store red offset in + * gofs_ret: Pointer to variable to store green offset in + * bofs_ret: Pointer to variable to store blue offset in + * [Return value] + * None + */ +static inline void vdp2_get_color_offsets(uint16_t mask, int32_t *rofs_ret, + int32_t *gofs_ret, int32_t *bofs_ret) +{ + if (Vdp2Regs->CLOFEN & mask) { // CoLor OFfset ENable + /* Offsets are 9-bit signed values */ + if (Vdp2Regs->CLOFSL & mask) { // CoLor OFfset SeLect + *rofs_ret = ((int32_t)Vdp2Regs->COBR << 23) >> 23; + *gofs_ret = ((int32_t)Vdp2Regs->COBG << 23) >> 23; + *bofs_ret = ((int32_t)Vdp2Regs->COBB << 23) >> 23; + } else { + *rofs_ret = ((int32_t)Vdp2Regs->COAR << 23) >> 23; + *gofs_ret = ((int32_t)Vdp2Regs->COAG << 23) >> 23; + *bofs_ret = ((int32_t)Vdp2Regs->COAB << 23) >> 23; + } + } else { + /* No color offset */ + *rofs_ret = *gofs_ret = *bofs_ret = 0; + } +} + +/*************************************************************************/ + +/** + * vdp2_draw_bg: Draw the screen background. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void vdp2_draw_bg(void) +{ + uint32_t address = ((Vdp2Regs->BKTAU & 7) << 16 | Vdp2Regs->BKTAL) << 1; + if (!(Vdp2Regs->VRSIZE & 0x8000)) { + address &= 0x7FFFF; + } + + int rofs, gofs, bofs; + vdp2_get_color_offsets(1<<6, &rofs, &gofs, &bofs); + + struct {uint32_t color; int16_t x, y, z, pad;} *vertices; + + if (Vdp2Regs->BKTAU & 0x8000) { + /* Distinct color for each line */ + int num_vertices, y; + if (disp_height > 272) { + /* For interlaced screens, we average the colors of each two + * adjacent lines */ + num_vertices = 2*(disp_height/2); + vertices = pspGuGetMemoryMerge(sizeof(*vertices) * num_vertices); + for (y = 0; y+1 < disp_height; y += 2, address += 4) { + uint16_t rgb0 = T1ReadWord(Vdp2Ram, address); + uint32_t r0 = (rgb0 & 0x001F) << 3; + uint32_t g0 = (rgb0 & 0x03E0) >> 2; + uint32_t b0 = (rgb0 & 0x7C00) >> 7; + uint16_t rgb1 = T1ReadWord(Vdp2Ram, address); + uint32_t r1 = (rgb1 & 0x001F) << 3; + uint32_t g1 = (rgb1 & 0x03E0) >> 2; + uint32_t b1 = (rgb1 & 0x7C00) >> 7; + uint32_t color = bound(((r0+r1+1)/2) + rofs, 0, 255) << 0 + | bound(((g0+g1+1)/2) + gofs, 0, 255) << 8 + | bound(((b0+b1+1)/2) + bofs, 0, 255) << 16 + | 0xFF000000; + vertices[y+0].color = color; + vertices[y+0].x = 0; + vertices[y+0].y = y/2; + vertices[y+0].z = 0; + vertices[y+1].color = color; + vertices[y+1].x = disp_width >> disp_xscale; + vertices[y+1].y = y/2; + vertices[y+1].z = 0; + } + } else { + num_vertices = 2*disp_height; + vertices = pspGuGetMemoryMerge(sizeof(*vertices) * num_vertices); + for (y = 0; y < disp_height; y++, address += 2) { + uint16_t rgb = T1ReadWord(Vdp2Ram, address); + uint32_t r = bound(((rgb & 0x001F) << 3) + rofs, 0, 255); + uint32_t g = bound(((rgb & 0x03E0) >> 2) + gofs, 0, 255); + uint32_t b = bound(((rgb & 0x7C00) >> 7) + bofs, 0, 255); + vertices[y*2+0].color = 0xFF000000 | r | g<<8 | b<<16; + vertices[y*2+0].x = 0; + vertices[y*2+0].y = y; + vertices[y*2+0].z = 0; + vertices[y*2+1].color = 0xFF000000 | r | g<<8 | b<<16; + vertices[y*2+1].x = disp_width >> disp_xscale; + vertices[y*2+1].y = y; + vertices[y*2+1].z = 0; + } + } + guDrawArray(GU_LINES, + GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + num_vertices, NULL, vertices); + guCommit(); + } else { + /* Single color for the whole screen */ + vertices = pspGuGetMemoryMerge(sizeof(*vertices) * 2); + uint16_t rgb = T1ReadWord(Vdp2Ram, address); + uint32_t r = bound(((rgb & 0x001F) << 3) + rofs, 0, 255); + uint32_t g = bound(((rgb & 0x03E0) >> 2) + gofs, 0, 255); + uint32_t b = bound(((rgb & 0x7C00) >> 7) + bofs, 0, 255); + vertices[0].color = 0xFF000000 | r | g<<8 | b<<16; + vertices[0].x = 0; + vertices[0].y = 0; + vertices[0].z = 0; + vertices[1].color = 0xFF000000 | r | g<<8 | b<<16; + vertices[1].x = disp_width >> disp_xscale; + vertices[1].y = disp_height >> disp_yscale; + vertices[1].z = 0; + guDrawArray(GU_SPRITES, + GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, NULL, vertices); + guCommit(); + } +} + +/*************************************************************************/ + +/** + * vdp2_draw_graphics: Draw a single VDP2 background graphics layer. + * + * [Parameters] + * layer: Background graphics layer (BG_* constant) + * [Return value] + * None + */ +static void vdp2_draw_graphics(int layer) +{ + vdp2draw_struct info; + clipping_struct clip[2]; + + /* Is this background layer enabled? */ + if (!(Vdp2Regs->BGON & Vdp2External.disptoggle & (1 << layer))) { + return; + } + if (layer == BG_RBG0 && !config_get_enable_rotate()) { + return; + } + + /* Check whether we should smooth the graphics. */ + const int smooth_hires = + (disp_width > 352 || disp_height > 272) && config_get_smooth_hires(); + + /* Find out whether it's a bitmap or not. */ + switch (layer) { + case BG_NBG0: info.isbitmap = Vdp2Regs->CHCTLA & 0x0002; break; + case BG_NBG1: info.isbitmap = Vdp2Regs->CHCTLA & 0x0200; break; + case BG_RBG0: info.isbitmap = Vdp2Regs->CHCTLB & 0x0200; break; + default: info.isbitmap = 0; break; + } + + /* Determine color-related data. */ + info.transparencyenable = !(Vdp2Regs->BGON & (0x100 << layer)); + /* FIXME: specialprimode is not actually supported by the map drawing + * functions */ + info.specialprimode = (Vdp2Regs->SFPRMD >> (2*layer)) & 3; + switch (layer) { + case BG_NBG0: + info.colornumber = (Vdp2Regs->CHCTLA & 0x0070) >> 4; + break; + case BG_NBG1: + info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; + break; + case BG_NBG2: + info.colornumber = (Vdp2Regs->CHCTLB & 0x0002) >> 1; + break; + case BG_NBG3: + info.colornumber = (Vdp2Regs->CHCTLB & 0x0020) >> 5; + break; + case BG_RBG0: + info.colornumber = (Vdp2Regs->CHCTLB & 0x7000) >> 12; + break; + } + if (Vdp2Regs->CCCTL & (1 << layer)) { + const uint8_t *ptr = (const uint8_t *)&Vdp2Regs->CCRNA; + info.alpha = ((~ptr[layer] & 0x1F) << 3) + 7; + } else { + info.alpha = 0xFF; + } + if (layer == BG_RBG0) { + info.coloroffset = (Vdp2Regs->CRAOFB & 7) << 8; + } else { + info.coloroffset = ((Vdp2Regs->CRAOFA >> (4*layer)) & 7) << 8; + } + vdp2_get_color_offsets(1 << layer, (int32_t *)&info.cor, + (int32_t *)&info.cog, (int32_t *)&info.cob); + + /* Extract rotation information for RBG0. */ + if (layer == BG_RBG0) { + switch (Vdp2Regs->RPMD & 3) { + case 0: + info.rotatenum = 0; + info.rotatemode = 0; + break; + case 1: + info.rotatenum = 1; + info.rotatemode = 0; + break; + case 2: + info.rotatenum = 0; + info.rotatemode = 1; + break; + case 3: + info.rotatenum = 0; + info.rotatemode = 2; + break; + } + } + + /* Determine tilemap/bitmap size and display offset. */ + if (info.isbitmap) { + if (layer == BG_RBG0) { + ReadBitmapSize(&info, Vdp2Regs->CHCTLB >> 10, 0x3); + info.charaddr = ((Vdp2Regs->MPOFR >> (4*info.rotatenum)) & 7) << 17; + info.paladdr = (Vdp2Regs->BMPNB & 0x7) << 4; + } else { + ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> (2 + layer*8), 0x3); + info.charaddr = ((Vdp2Regs->MPOFN >> (4*layer)) & 7) << 17; + info.paladdr = ((Vdp2Regs->BMPNA >> (8*layer)) & 7) << 4; + } + info.flipfunction = 0; + info.specialfunction = 0; + switch (layer) { + case BG_NBG0: + info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % info.cellw); + info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % info.cellh); + break; + case BG_NBG1: + info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % info.cellw); + info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % info.cellh); + break; + case BG_RBG0: + /* Transformation is handled separately; nothing to do here. */ + break; + default: + DMSG("info.isbitmap set for invalid layer %d", layer); + return; + } + } else { + if (layer == BG_RBG0) { + info.mapwh = 4; + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> (8 + 4*info.rotatenum)); + } else { + info.mapwh = 2; + ReadPlaneSize(&info, Vdp2Regs->PLSZ >> (2*layer)); + } + const int scx_mask = (512 * info.planew * info.mapwh) - 1; + const int scy_mask = (512 * info.planeh * info.mapwh) - 1; + switch (layer) { + case BG_NBG0: + info.x = - (Vdp2Regs->SCXIN0 & scx_mask); + info.y = - (Vdp2Regs->SCYIN0 & scy_mask); + ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x0001); + break; + case BG_NBG1: + info.x = - (Vdp2Regs->SCXIN1 & scx_mask); + info.y = - (Vdp2Regs->SCYIN1 & scy_mask); + ReadPatternData(&info, Vdp2Regs->PNCN1, Vdp2Regs->CHCTLA & 0x0100); + break; + case BG_NBG2: + info.x = - (Vdp2Regs->SCXN2 & scx_mask); + info.y = - (Vdp2Regs->SCYN2 & scy_mask); + ReadPatternData(&info, Vdp2Regs->PNCN2, Vdp2Regs->CHCTLB & 0x0001); + break; + case BG_NBG3: + info.x = - (Vdp2Regs->SCXN3 & scx_mask); + info.y = - (Vdp2Regs->SCYN3 & scy_mask); + ReadPatternData(&info, Vdp2Regs->PNCN3, Vdp2Regs->CHCTLB & 0x0010); + break; + case BG_RBG0: + ReadPatternData(&info, Vdp2Regs->PNCR, Vdp2Regs->CHCTLB & 0x0100); + break; + } + } + + /* Determine coordinate scaling. */ + // FIXME: scaled graphics may be distorted because integers are used + // for vertex coordinates + switch (layer) { + case BG_NBG0: + info.coordincx = 65536.0f / (Vdp2Regs->ZMXN0.all & 0x7FF00 ?: 65536); + info.coordincy = 65536.0f / (Vdp2Regs->ZMYN0.all & 0x7FF00 ?: 65536); + break; + case BG_NBG1: + info.coordincx = 65536.0f / (Vdp2Regs->ZMXN1.all & 0x7FF00 ?: 65536); + info.coordincy = 65536.0f / (Vdp2Regs->ZMYN1.all & 0x7FF00 ?: 65536); + break; + default: + info.coordincx = info.coordincy = 1; + break; + } + if (disp_xscale == 1) { + info.coordincx /= 2; + } + if (disp_yscale == 1) { + info.coordincy /= 2; + } + + /* Get clipping data. */ + info.wctl = ((uint8_t *)&Vdp2Regs->WCTLA)[layer]; + clip[0].xstart = 0; clip[0].xend = disp_width; + clip[0].ystart = 0; clip[0].yend = disp_height; + clip[1].xstart = 0; clip[1].xend = disp_width; + clip[1].ystart = 0; clip[1].yend = disp_height; + ReadWindowData(info.wctl, clip); + + /* Check for a zero-size clip window, which some games seem to use to + * temporarily disable a screen. */ + if (clip[0].xstart >= clip[0].xend + || clip[0].ystart >= clip[0].yend + || clip[1].xstart >= clip[1].xend + || clip[1].ystart >= clip[1].yend + ) { + return; + } + + info.priority = bg_priority[layer]; + switch (layer) { + case BG_NBG0: info.PlaneAddr = (void *)Vdp2NBG0PlaneAddr; break; + case BG_NBG1: info.PlaneAddr = (void *)Vdp2NBG1PlaneAddr; break; + case BG_NBG2: info.PlaneAddr = (void *)Vdp2NBG2PlaneAddr; break; + case BG_NBG3: info.PlaneAddr = (void *)Vdp2NBG3PlaneAddr; break; + case BG_RBG0: if (info.rotatenum == 0) { + info.PlaneAddr = (void *)Vdp2ParameterAPlaneAddr; + } else { + info.PlaneAddr = (void *)Vdp2ParameterBPlaneAddr; + } + break; + default: DMSG("No PlaneAddr for layer %d", layer); return; + } + info.patternpixelwh = 8 * info.patternwh; + info.draww = (int)((float)(disp_width >> disp_xscale) / info.coordincx); + info.drawh = (int)((float)(disp_height >> disp_yscale) / info.coordincy); + + /* Set up for rendering. */ + guEnable(GU_TEXTURE_2D); + guAmbientColor(info.alpha<<24 | 0xFFFFFF); + if (smooth_hires) { + guTexFilter(GU_LINEAR, GU_LINEAR); + } + + /* If a custom drawing function has been specified for this layer, call + * it first. */ + int custom_draw_succeeded = 0; + if (custom_draw_func[layer]) { + custom_draw_succeeded = (*custom_draw_func[layer])(&info, clip); + if (custom_draw_succeeded && layer == BG_RBG0) { + drew_slow_RBG0 = !RBG0_draw_func_is_fast; + } + } + + if (!custom_draw_succeeded) { + + /* Select a rendering function based on the tile layout and format. */ + void (*draw_map_func)(vdp2draw_struct *info, + const clipping_struct *clip); + if (layer == BG_RBG0) { + draw_map_func = &vdp2_draw_map_rotated; + } else if (info.isbitmap) { + switch (layer) { + case BG_NBG0: + if ((Vdp2Regs->SCRCTL & 7) == 7) { + DMSG("WARNING: line scrolling not supported"); + } + /* fall through */ + case BG_NBG1: + if (info.colornumber == 1 && !smooth_hires) { + draw_map_func = &vdp2_draw_bitmap_t8; + } else if (info.colornumber == 4 && !smooth_hires + && info.coordincx == 1 && info.coordincy == 1) { + draw_map_func = &vdp2_draw_bitmap_32; + } else { + draw_map_func = &vdp2_draw_bitmap; + } + break; + default: + DMSG("info.isbitmap set for invalid layer %d", layer); + return; + } + } else if (info.patternwh == 2) { + if (info.colornumber == 1 && !smooth_hires) { + draw_map_func = &vdp2_draw_map_16x16_t8; + } else { + draw_map_func = &vdp2_draw_map_16x16; + } + } else { + if (info.colornumber == 1 && !smooth_hires) { + draw_map_func = &vdp2_draw_map_8x8_t8; + } else { + draw_map_func = &vdp2_draw_map_8x8; + } + } + + /* Render the graphics. */ + (*draw_map_func)(&info, clip); + if (layer == BG_RBG0) { + drew_slow_RBG0 = 1; + } + + } // if (!custom_draw_succeeded) + + /* All done. */ + if (smooth_hires) { + guTexFilter(GU_NEAREST, GU_NEAREST); + } + guAmbientColor(0xFFFFFFFF); + guDisable(GU_TEXTURE_2D); + guCommit(); +} + +/*************************************************************************/ +/***** Utility routines exported to background graphics drawing code *****/ +/*************************************************************************/ + +/* Last block allocated with pspGuGetMemoryMerge() */ +static void *merge_last_ptr; +static uint32_t merge_last_size; + +/*-----------------------------------------------------------------------*/ + +/** + * pspGuGetMemoryMerge: Acquire a block of memory from the GE display + * list. Similar to sceGuGetMemory(), but if the most recent display list + * operation was also a pspGuGetMemoryMerge() call, merge the two blocks + * together to avoid long chains of jump instructions in the display list. + * + * [Parameters] + * size: Size of block to allocate, in bytes + * [Return value] + * Allocated block + */ +void *pspGuGetMemoryMerge(uint32_t size) +{ + /* Make sure size is 32-bit aligned. */ + size = (size + 3) & -4; + + /* Start off by allocating the block normally. Ideally, we'd check + * first whether the current list pointer is immediately past the last + * block allocated, but since there's apparently no interface for + * either getting the current pointer or deleting the last instruction, + * we're out of luck and can't save the 8 bytes taken by the jump, even + * if we end up not needing it. */ + void *ptr = guGetMemory(size); + + /* If the pointer we got back is equal to the end of the previously + * allocated block plus 8 bytes (2 GU instructions), we can merge. */ + if ((uint8_t *)ptr == (uint8_t *)merge_last_ptr + merge_last_size + 8) { + /* Make sure the instruction before the last block really is a + * jump instruction before we update it. */ + uint32_t *jump_ptr = (uint32_t *)merge_last_ptr - 1; + if (*jump_ptr >> 24 == 0x08) { + void *block_end = (uint8_t *)ptr + size; + *jump_ptr = 0x08<<24 | ((uintptr_t)block_end & 0xFFFFFF); + merge_last_size = (uint8_t *)block_end - (uint8_t *)merge_last_ptr; + return ptr; + } + } + + /* We couldn't merge, so reset the last-block-allocated variables and + * return the block we allocated above. */ + merge_last_ptr = ptr; + merge_last_size = size; + return ptr; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/psp-video.h b/yabause/src/psp/psp-video.h new file mode 100644 index 0000000000..a9629c5848 --- /dev/null +++ b/yabause/src/psp/psp-video.h @@ -0,0 +1,154 @@ +/* src/psp/psp-video.h: PSP video interface module header + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_VIDEO_H +#define PSP_VIDEO_H + +#include "../vdp1.h" // for VideoInterface_struct + +/*************************************************************************/ +/********* Module interface and global-use routine declarations **********/ +/*************************************************************************/ + +/* Module interface definition */ +extern VideoInterface_struct VIDPSP; + +/* Unique module ID (must be different from any in ../{vdp1,vid*}.h) */ +#define VIDCORE_PSP 0x5CE // "SCE" + +/*************************************************************************/ + +/** + * psp_video_infoline: Display an information line on the bottom of the + * screen. The text will be displayed for one frame only; call this + * function every frame to keep the text visible. + * + * [Parameters] + * color: Text color (0xAABBGGRR) + * text: Text string + * [Return value] + * None + */ +extern void psp_video_infoline(uint32_t color, const char *text); + +/*************************************************************************/ +/************ Internal utility data and routine declarations *************/ +/*************************************************************************/ + +/* Vertex data structure for GU drawing */ + +typedef struct VertexUVXYZ_ { + int16_t u, v; + int16_t x, y, z; +} VertexUVXYZ; + +/*************************************************************************/ + +/** + * global_clut_16, global_clut_32: Global color lookup table (from VDP2 + * color RAM), in 16- and 32-bit formats. Each array is indexed by the + * color index value used in sprites and tiles; when the VDP2 is in 32-bit + * color mode (Vdp2Internal.ColorMode == 2), indices 0x400-0x7FF are a + * copy of 0x000-0x3FF. In all cases, the alpha values are set to full + * (1 or 0xFF). + */ +extern uint16_t global_clut_16[0x800]; +extern uint32_t global_clut_32[0x800]; + +/*-----------------------------------------------------------------------*/ + +/** + * adjust_color_16_32: Adjust the components of a 16-bit color value, + * returning it as a 32-bit color value. + * + * [Parameters] + * color: 16-bit color value (A1B5G5R5) + * rofs: Red component offset + * gofs: Green component offset + * bofs: Blue component offset + * [Return value] + * Converted and djusted 32-bit color value + */ +static inline uint32_t adjust_color_16_32(uint16_t color, int32_t rofs, + int32_t gofs, int32_t bofs) +{ + int32_t r = color<<3 & 0xF8; + int32_t g = color>>2 & 0xF8; + int32_t b = color>>7 & 0xF8; + return bound(r+rofs, 0, 255) << 0 + | bound(g+gofs, 0, 255) << 8 + | bound(b+bofs, 0, 255) << 16 + | (color>>15 ? 0xFF000000 : 0); +} + +/*-----------------------------------------------------------------------*/ + +/** + * adjust_color_32_32: Adjust the components of a 32-bit color value. + * + * [Parameters] + * color: 32-bit color value (ABGR) + * rofs: Red component offset + * gofs: Green component offset + * bofs: Blue component offset + * [Return value] + * Adjusted 32-bit color value + */ +static inline uint32_t adjust_color_32_32(uint32_t color, int32_t rofs, + int32_t gofs, int32_t bofs) +{ + int32_t r = color>> 0 & 0xFF; + int32_t g = color>> 8 & 0xFF; + int32_t b = color>>16 & 0xFF; + return bound(r+rofs, 0, 255) << 0 + | bound(g+gofs, 0, 255) << 8 + | bound(b+bofs, 0, 255) << 16 + | (color & 0xFF000000); +} + +/*************************************************************************/ + +/** + * pspGuGetMemoryMerge: Acquire a block of memory from the GE display + * list. Similar to sceGuGetMemory(), but if the most recent display list + * operation was also a pspGuGetMemoryMerge() call, merge the two blocks + * together to avoid long chains of jump instructions in the display list. + * + * [Parameters] + * size: Size of block to allocate, in bytes + * [Return value] + * Allocated block + */ +void *pspGuGetMemoryMerge(uint32_t size); + +/*************************************************************************/ +/*************************************************************************/ + +#endif // PSP_VIDEO_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtl-internal.h b/yabause/src/psp/rtl-internal.h new file mode 100644 index 0000000000..f08bc6ccce --- /dev/null +++ b/yabause/src/psp/rtl-internal.h @@ -0,0 +1,712 @@ +/* src/psp/rtl-internal.h: Internal-use declarations for RTL + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef RTL_INTERNAL_H +#define RTL_INTERNAL_H + +/*************************************************************************/ +/************************* Configuration options *************************/ +/*************************************************************************/ + +/*============ General options ============*/ + +/** + * INSNS_EXPAND_SIZE: Specifies the number of instructions by which to + * expand a block's instruction array when the array is full. This value + * is also used for the initial size of the array. + */ +#define INSNS_EXPAND_SIZE 1000 + +/** + * UNITS_EXPAND_SIZE: Specifies the number of instructions by which to + * expand a block's instruction array when the array is full. This value + * is also used for the initial size of the array. + */ +#define UNITS_EXPAND_SIZE 100 + +/** + * REGS_EXPAND_SIZE: Specifies the number of register entries by which to + * expand a block's register array when the array is full. This value is + * also used for the initial size of the array. + */ +#define REGS_EXPAND_SIZE 1000 + +/** + * REGS_LIMIT: Specifies the maximum number of registers allowed for a + * single block. Must be no greater than 65535 (because this value must + * fit into a uint16_t). The actual number of available registers is one + * less than this value, since register 0 is never used. + */ +#define REGS_LIMIT 65535 + +/** + * LABELS_EXPAND_SIZE: Specifies the number of entries by which to expand + * a block's label-to-unit mapping array when the array is full. This + * value is also used for the initial size of the array. + */ +#define LABELS_EXPAND_SIZE 100 + +/** + * LABELS_LIMIT: Specifies the maximum number of labels allowed for a + * single block. Must be no greater than 65535 (because this value must + * fit into a uint16_t). The actual number of available labels is one less + * than this value, since label 0 is never used. + */ +#define LABELS_LIMIT 65535 + +/** + * NATIVE_EXPAND_SIZE: Specifies the block size (in bytes) by which to + * expand the native code buffer as necessary when translating. + */ +#define NATIVE_EXPAND_SIZE 8192 + +/*============ MIPS-specific options ============*/ + +/** + * MIPS_FRAME_SIZE: The stack frame size to use in generated native code, + * in bytes. This does not include space reserved for saving registers in + * the function prologue. + */ +#define MIPS_FRAME_SIZE 64 + +/** + * MIPS_OPTIMIZE_MERGE_CONSTANTS: When defined, RTL registers with + * identical constant values whose live ranges overlap will share the same + * hardware register. Hardware registers will also be reused if a value + * loaded for a previously-used constant is still available after the + * constant has died. + */ +#define MIPS_OPTIMIZE_MERGE_CONSTANTS + +/** + * MIPS_OPTIMIZE_IMMEDIATE: When defined, the translator will optimize a + * LOAD_IMM instruction followed by one of: + * ADD, SUB, AND, OR, XOR, SLL, SRL, SRA, SLTU, SLTS + * which uses the LOAD_IMM target as its second operand into the equivalent + * MIPS immediate instruction if the immediate operand is within range and + * is not used elsewhere. Similarly, LOAD_NATIVEADDR followed by a memory + * load or store operation will be optimized if possible to insert the low + * 16 bits of the address into the load/store instruction, allowing the + * base register to be loaded with a single MIPS LUI instruction. + */ +#define MIPS_OPTIMIZE_IMMEDIATE + +/** + * MIPS_OPTIMIZE_ABSOLUTE_CALL: When defined, the translator will optimize + * a LOAD_NATIVEADDR instruction followed by a CALL_NATIVE instruction into + * a MIPS JAL instruction to the address specified by the constant register. + * + * ==== PORTABILITY WARNING ==== + * + * This optimization is NOT guaranteed to be portable across different + * platforms! The MIPS JAL instruction only allows the low 28 bits of the + * target address to be specified, and takes the high bits from the current + * PC. We could theoretically check the target address against the address + * of the JAL instruction when we add it, but since the address of the + * native code block may change as it is expanded, we cannot guarantee at + * the time we add the JAL instruction that the target address is reachable. + * + * That said, on (at least current iterations of) the PSP, the upper bits + * of addresses for both code and data are always zero, so we can safely + * optimize jumps to constant addresses. + */ +#define MIPS_OPTIMIZE_ABSOLUTE_CALL + +/** + * MIPS_OPTIMIZE_DELAY_SLOT: When defined, the translator will move the + * instruction preceding a branch or jump instruction into the branch's + * delay slot, if possible. + */ +#define MIPS_OPTIMIZE_DELAY_SLOT + +/** + * MIPS_OPTIMIZE_BRANCHES: When defined, the translator will perform the + * following optimizations on branches: + * + * - A branch to another (unconditional) branch will be chained through to + * the final branch target, unless that target would be outside the range + * of a branch instruction. + * + * - When a branch has a NOP instruction in its delay slot, the instruction + * at the target address will be copied over the NOP, the branch will be + * changed to a Likely branch if it is conditional (e.g. BEQ becomes + * BEQL), and the branch target will be incremented by one instruction. + * (The optimization is not performed if the branch target is already at + * the positive branch offset limit.) + */ +#define MIPS_OPTIMIZE_BRANCHES + +/** + * MIPS_OPTIMIZE_SCHEDULING: When defined, the translator will attempt to + * reschedule load, multiply, and divide instructions to avoid stalls. + */ +#define MIPS_OPTIMIZE_SCHEDULING + +/** + * MIPS_OPTIMIZE_SEX: When defined, the translator will optimize SLL/SRA + * pairs to SEB or SEH when possible: + * + * LOAD_IMM rD,24 + * SLL rB,rA,rD + * LOAD_IMM rE,24 + * SRA rC,rB,rE --> seb $rC,$rA [assuming rB is otherwise unused] + * + * LOAD_IMM rD,16 + * SLL rB,rA,rD + * LOAD_IMM rE,16 + * SRA rC,rB,rE --> seh $rC,$rA [assuming rB is otherwise unused] + * + * SLLI/SRAI pairs are similarly optimized. + */ +#define MIPS_OPTIMIZE_SEX + +/** + * MIPS_OPTIMIZE_MIN_MAX: When defined, the translator will optimize + * SLTS/SELECT pairs to MIN or MAX when possible: + * + * SLTS rC, rA, rB + * SELECT rD, rA, rB, rC --> min $rD, $rA, $rB + * + * SLTS rC, rA, rB + * SELECT rD, rB, rA, rC --> max $rD, $rA, $rB + * + * (both assuming register rC is otherwise unused). + */ +#define MIPS_OPTIMIZE_MIN_MAX + +/*============ Debugging options ============*/ + +/** + * OPERAND_SANITY_CHECKS: If defined, causes rtl_add_insn() to check that + * register and label operands are within allowable ranges. + * + * This option is meaningless if CHECK_PRECONDITIONS is not defined. + */ +#define OPERAND_SANITY_CHECKS + +/** + * CHECK_PRECONDITIONS: If defined, causes functions to check that their + * preconditions are satisfied and return (with an error if appropriate) + * if not. This can add a significant amount of overhead. + */ +// #define CHECK_PRECONDITIONS + +/** + * RTL_TRACE_GENERATE: Trace the generation of RTL blocks and instructions. + */ +// #define RTL_TRACE_GENERATE + +/** + * RTL_TRACE_EXECUTE: Trace the execution of RTL instructions in + * rtl_execute_block(). + */ +// #define RTL_TRACE_EXECUTE + +/** + * RTL_TRACE_STEALTH_FOR_SH2: Enable SH-2 stealth tracing (see the + * documentation for TRACE_STEALTH in sh2.c). + */ +#define RTL_TRACE_STEALTH_FOR_SH2 + +/*************************************************************************/ +/*************************** Type declarations ***************************/ +/*************************************************************************/ + +#undef mips // Avoid namespace pollution from the compiler on MIPS machines + +/*-----------------------------------------------------------------------*/ + +/** + * RTLInsn: A single platform-neutral (more or less) operation. SH-2 + * instructions are translated into sequences of RTLInsns, which are then + * optimized and retranslated into MIPS instructions. + */ +typedef struct RTLInsn_ { + uint8_t opcode; // Operation code (RTLOpcode) + uint16_t dest; // Destination register + uint16_t src1, src2; // Source registers + union { + uint16_t dest2; // Second output register (for MULU_64, etc.) + uint16_t cond; // Condition register for SELECT + struct { + uint8_t start; // First (lowest) bit number for a bitfield + uint8_t count; // Number of bits for a bitfield + } bitfield; + int16_t offset; // Byte offset for load/store instructions + uint16_t label; // GOTO target label + uint16_t target; // CALL_NATIVE branch target register + uint32_t src_imm; // Source immediate value + uintptr_t src_addr; // Source native address value + }; +} RTLInsn; + +/*----------------------------------*/ + +/** + * RTLRegType: The type (source information) of a register used in an RTL + * block. + */ +typedef enum RTLRegType_ { + RTLREG_UNDEFINED = 0, // Not yet defined to anything + RTLREG_CONSTANT, // Constant value (RTLRegister.value) + RTLREG_PARAMETER, // Function parameter (.param_index) + RTLREG_MEMORY, // Memory reference + RTLREG_RESULT, // Result of an operation on other registers + RTLREG_RESULT_NOFOLD, // Result of an operation (not constant foldable) + RTLREG_UNKNOWN, // Source unknown (e.g. due to reassignment) +} RTLRegType; + +/** + * RTLRegister: Data about registers used in an RTL block. All registers + * are 32 bits wide. + */ +typedef struct RTLRegister_ RTLRegister; +struct RTLRegister_ { + /* Basic register information */ + uint8_t source; // Register source (RTLRegType) + uint8_t live; // Nonzero if this register has been referenced + // (this field is never cleared once set) + uint16_t live_link; // Next register in live list (sorted by birth) + uint32_t birth; // First RTL insn index when register is live + // (if SSA, insn index where it's assigned) + uint32_t death; // Last RTL insn index when register is live + + /* Unique pointer information. The "unique_pointer" field has the + * property that all registers with the same nonzero value for + * "unique_pointer" are native addresses which point to the same region + * of memory, and that region of memory will only be accessed through + * a register with the same "unique_pointer" value. */ + uint16_t unique_pointer; + + /* Register value information */ + union { + uintptr_t value; // Value of register for RTLREG_CONSTANT; + // also used during interpreted execution + unsigned int param_index;// Function parameter idx for RTLREG_PARAMETER + struct { + uint16_t addr_reg; // Register holding address for RTLREG_MEMORY + int16_t offset; // Access offset + uint8_t size; // Access size in bytes (1, 2, 4; or 8 if a + // pointer, regardless of actual size) + uint8_t is_signed; // Nonzero if a signed load, zero if unsigned + } memory; + struct { + uint8_t opcode; // Operation code for RTLREG_RESULT + uint8_t second_res:1; // "Second result" flag (high word of + // MUL[US]_64, remainder of DIVMOD[US]) + uint8_t is_imm:1; // Nonzero if a register-immediate operation + uint16_t src1; // Operand 1 + union { + struct { + uint16_t src2; // Op 2 for register-register operations + union { + uint16_t cond; // Condition register for SELECT + struct { + uint8_t start; // Start bit for bitfields + uint8_t count; // Bit count for bitfields + }; + }; + }; + uint32_t imm; // Operand 2 for register-immediate operations + }; + } result; + }; + + /* The following fields are for use by RTL-to-native translators: */ + uint32_t last_used; // Last insn index where this register was used + uint8_t native_allocated; // Nonzero if a native reg has been allocated + uint8_t native_reg; // Native register allocated for this register + uint8_t frame_allocated; // Nonzero if a frame slot has been allocated + uint8_t frame_slot; // Frame slot allocated for this register + int16_t stack_offset; // Stack offset of this register's frame slot + RTLRegister *next_merged; // Next register in merge chain, or NULL + union { + struct { + /* If nonzero, this field contains the opcode to retrieve the + * register's value from the MIPS HI or LO register (either + * MIPS_MFHI(0) or MIPS_MFLO(0)) */ + uint32_t is_in_hilo; + } mips; + }; +}; + +/*----------------------------------*/ + +/** + * RTLUnit: Information about an basic unit of code (a sequence of + * instructions with one entry point and one exit point). Note that a unit + * can be empty, denoted by last_insn < first_insn, and that last_insn can + * be negative, if first_insn is 0 and the unit is empty. + */ +typedef struct RTLUnit_ { + int32_t first_insn; // block->insns[] index of first insn in unit + int32_t last_insn; // block->insns[] index of last insn in unit + int16_t next_unit; // block->units[] index of next unit in code + // stream (may not be the sequentially next + // unit in the array due to optimization); + // -1 indicates the end of the code stream + int16_t prev_unit; // block->units[] index of previous unit in + // code stream + int16_t entries[8]; // block->units[] indices of dominating units; + // -1 indicates an unused slot. Holes in + // the list are not permitted; for more + // than 8 slots, add a dummy unit on top + // (rtlunit_*() functions handle all this) + int16_t exits[2]; // block->units[] indices of postdominating + // units. A terminating insn can go at + // most two places (conditional GOTO). + + /* These fields are provided as hints to RTL-to-native translators: */ + int16_t next_call_unit; // Next unit with a CALL_NATIVE insn (-1=none) + int16_t prev_call_unit; // Prev. unit with a CALL_NATIVE insn (-1=none) + + /* The following fields are used only by RTL-to-native translators: */ + union { + struct { + /* Register and stack frame state at the beginning of the unit */ + RTLRegister *reg_map[32]; // MIPS-to-RTL register map + RTLRegister *frame_map[MIPS_FRAME_SIZE/4]; // Stack frame reg map + } mips; + }; +} RTLUnit; + +/*----------------------------------*/ + +/** + * RTLBlock: State information used in translating a block of code. The + * RTLBlock type itself is defined in rtl.h. + */ +struct RTLBlock_ { + RTLInsn *insns; // Instruction array + int16_t *insn_unitmap; // Insn-to-unit mapping (used by interpreter) + uint32_t insns_size; // Size of instruction array (entries) + uint32_t num_insns; // Number of instructions actually in array + + RTLUnit *units; // Basic unit array + uint16_t units_size; // Size of unit array (entries) + uint16_t num_units; // Number of units actually in array + uint8_t have_unit; // Nonzero if there is a currently active unit + uint16_t cur_unit; // Current unit index if have_unit != 0 + + int16_t *label_unitmap; // Label-to-unit-index mapping (-1 = unset) + uint16_t labels_size; // Size of label-to-unit map array (entries) + uint16_t next_label; // Next label number to allocate + + RTLRegister *regs; // Register array + uint16_t regs_size; // Size of register array (entries) + uint16_t next_reg; // Next register number to allocate + uint16_t first_live_reg; // First register in live range list + uint16_t last_live_reg; // Last register in live range list + uint16_t unique_pointer_index; // Next value for RTLRegister.unique_pointer + + uint8_t finalized; // Nonzero if block has been finalized + + /* These fields are provided as hints to RTL-to-native translators: */ + int16_t first_call_unit; // First unit with a CALL_NATIVE insn (-1=none) + int16_t last_call_unit; // Last unit with a CALL_NATIVE insn (-1=none) + + /* The following fields are used only by optimization routines: */ + uint8_t *unit_seen; // Array of "seen" flags for all units + // (used by rtlopt_drop_dead_units()) + + /* The following fields are used only by RTL-to-native translators: */ + void *native_buffer; // Native code buffer + uint32_t native_bufsize; // Allocated size of native code buffer + uint32_t native_length; // Length of native code + uint32_t *label_offsets; // Array of native offsets for labels + union { + struct { + uint8_t need_save_ra; // Nonzero = need to save/restore $ra + uint8_t need_chain_at; // Nonzero = need chain-to-$at epilogue + uint8_t frame_used; // Nonzero = 1 or more frame slots used + uint32_t sreg_used; // Bitmask of $sN registers used + uint32_t total_frame_size; // Frame size incl. space for $sN/$ra + RTLRegister *reg_map[32]; // MIPS-to-RTL register map + uint32_t reg_free; // Bitmask of free MIPS registers + uint32_t hi_reg, lo_reg; // RTL registers cached in HI and LO + RTLRegister *frame_map[MIPS_FRAME_SIZE/4]; // Stack frame reg map + uint32_t frame_free[((MIPS_FRAME_SIZE/4)+31)/32]; // Free slot mask + uint32_t unit_start; // Offset of first insn in current unit + struct { + uint8_t is_frame; // 0 = MIPS reg, 1 = frame slot + uint8_t index; // Register or frame slot index + int16_t next; // Next entry index or -1 for EOL + } active_list[32 + MIPS_FRAME_SIZE/4]; + int first_active; // First active entry, or -1 if none + int16_t next_call_unit; // Current position in call_unit chain + uint32_t epilogue_offset; // Start offset of epilogue code + uint32_t chain_offset; // Start offset of chain epilogue code + // (only valid if need_chain_at!=0) + } mips; + }; + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + uint32_t sh2_regcache[23]; // Cached values of SH-2 registers + uint32_t sh2_regcache_mask; // Bitmask of cached registers +#endif +}; + +/*************************************************************************/ +/***************************** Miscellaneous *****************************/ +/*************************************************************************/ + +/* We use the PRECOND() macro from common.h for precondition checking; if + * CHECK_PRECONDITIONS is _not_ defined, then redefine PRECOND() here to do + * nothing. */ + +#ifndef CHECK_PRECONDITIONS +# undef PRECOND +# define PRECOND(condition,fail_action) /*nothing*/ +#endif + +/*************************************************************************/ +/**************** Library-internal function declarations *****************/ +/*************************************************************************/ + +/**** Instruction encoding function declarations ****/ + +/* Internal table used by rtlinsn_make() */ +extern int (* const makefunc_table[])(RTLBlock *, RTLInsn *, unsigned int, + uintptr_t, uint32_t, uint32_t); + +/** + * rtlinsn_make: Fill in an RTLInsn structure based on the opcode stored + * in the structure and the parameters passed to the function. + * + * [Parameters] + * block: RTLBlock containing instruction + * insn: RTLInsn structure to fill in (insn->opcode must be set by caller) + * dest: Destination register for instruction + * src1: First source register or immediate value for instruction + * src2: Second source register or immediate value for instruction + * other: Extra register for instruction + * [Return value] + * Nonzero on success, zero on error + */ +static inline int rtlinsn_make(RTLBlock *block, RTLInsn *insn, + unsigned int dest, uintptr_t src1, + uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(insn->opcode >= RTLOP__FIRST && insn->opcode <= RTLOP__LAST, + return 0); + + /* Keep this check out of PRECOND() to try and avoid crashes even when + * CHECK_PRECONDITIONS is disabled; also, invert the sense and call the + * table function first so the parameter registers or stack frame + * aren't spilled by the DMSG() call in debug mode. */ + if (LIKELY(makefunc_table[insn->opcode])) { + return (*makefunc_table[insn->opcode])(block, insn, + dest, src1, src2, other); + } + DMSG("BUG: missing function for opcode %u", insn->opcode); + return 0; +} + + +/*-----------------------------------------------------------------------*/ + +/**** Optimization function declarations ****/ + +/** + * rtlopt_fold_constants: Perform constant folding on the given RTL block, + * converting instructions that operate on constant operands into load- + * immediate instructions that load the result of the operation. If such + * an operand is not used by any other instruction, the instruction that + * loaded it is changed to a NOP. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +extern int rtlopt_fold_constants(RTLBlock *block); + +/** + * rtlopt_decondition: Perform "deconditioning" of conditional branches + * with constant conditions. For "GOTO_IF_Z (GOTO_IF_NZ) label, rN" where + * rN is type RTLREG_CONSTANT, the instruction is changed to GOTO if the + * value of rN is zero (nonzero) and changed to NOP otherwise. As with + * constant folding, if the condition register is not used anywhere else, + * the register is eliminated and the instruction that loaded it is + * changed to a NOP. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +extern int rtlopt_decondition(RTLBlock *block); + +/** + * rtlopt_drop_dead_units: Search an RTL block for basic units which are + * unreachable via any path from the initial unit and remove them from the + * code stream. All units dominated only by such dead units are + * recursively removed as well. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +extern int rtlopt_drop_dead_units(RTLBlock *block); + +/** + * rtlopt_drop_dead_branches: Search an RTL block for branch instructions + * which branch to the next instruction in the code stream and replace them + * with NOPs. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +extern int rtlopt_drop_dead_branches(RTLBlock *block); + +/*-----------------------------------------------------------------------*/ + +/**** Basic unit processing function declarations ****/ + +/** + * rtlunit_add: Add a new, empty basic unit to the given block + * at the end of the block->units[] array. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on failure + */ +extern int rtlunit_add(RTLBlock *block); + +/** + * rtlunit_add_edge: Add a new edge between two basic units. + * + * [Parameters] + * block: RTL block + * from_index: Index of dominating basic unit (in block->units[]) + * to_index: Index of postdominating basic unit (in block->units[]) + * [Return value] + * Nonzero on success, zero on failure + */ +extern int rtlunit_add_edge(RTLBlock *block, unsigned int from_index, + unsigned int to_index); + +/** + * rtlunit_remove_edge: Remove an edge between two basic units. + * + * [Parameters] + * block: RTL block + * from_index: Index of dominating basic unit (in block->units[]) + * exit_index: Index of exit edge to remove (in units[from_index].exits[]) + * [Return value] + * None + */ +extern void rtlunit_remove_edge(RTLBlock *block, const unsigned int from_index, + unsigned int exit_index); + +/** + * rtlunit_dump_all: Dump a list of all basic units in the block to + * stderr. Intended for debugging. + * + * [Parameters] + * block: RTL block + * tag: Tag to prepend to all lines, or NULL for none + * [Return value] + * None + */ +extern void rtlunit_dump_all(const RTLBlock * const block, const char * const tag); + +/*-----------------------------------------------------------------------*/ + +/**** Architecture-specific translation function declarations ****/ + +/** + * rtl_translate_block_XXX: Translate the given block into native code for + * a particular architecture. + * + * [Parameters] + * block: RTLBlock to translate + * code_ret: Pointer to variable to receive code buffer pointer + * size_ret: Pointer to variable to receive code buffer size (in bytes) + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * On error, *code_ret and *size_ret are not modified. + */ +extern int rtl_translate_block_mips(RTLBlock *block, void **code_ret, + uint32_t *size_ret); + +/*-----------------------------------------------------------------------*/ + +/**** Debugging-related functions ****/ + +#if defined(RTL_TRACE_GENERATE) || defined(RTL_TRACE_EXECUTE) + +/** + * rtl_decode_insn: Decode an RTL instruction into a human-readable + * string. + * + * [Parameters] + * block: RTLBlock containing instruction to decode + * index: Index of instruction to decode + * is_exec: Nonzero if being called from interpreted execution, else zero + * [Return value] + * Human-readable string describing the instruction + * [Notes] + * The returned string is stored in a static buffer which is + * overwritten on each call. + */ +extern const char *rtl_decode_insn(const RTLBlock *block, uint32_t index, int is_exec); + +/** + * rtl_describe_register: Generate a string describing the contents of the + * given RTL register. + * + * [Parameters] + * reg: Register to describe + * is_exec: Nonzero if being called from interpreted execution, else zero + * [Return value] + * Human-readable string describing the register + * [Notes] + * The returned string is stored in a static buffer which is + * overwritten on each call. + */ +extern const char *rtl_describe_register(const RTLRegister *reg, int is_exec); + +#endif // RTL_TRACE_GENERATE || RTL_TRACE_EXECUTE + +/*************************************************************************/ +/*************************************************************************/ + +#endif // RTL_INTERNAL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtl-mips.c b/yabause/src/psp/rtl-mips.c new file mode 100644 index 0000000000..c8025b456c --- /dev/null +++ b/yabause/src/psp/rtl-mips.c @@ -0,0 +1,4819 @@ +/* src/psp/rtl-mips.c: RTL->MIPS translator used in dynamic translation + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "rtl.h" +#include "rtl-internal.h" +#include "rtl-mips.h" + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 +# include "sh2.h" +# include "sh2-internal.h" +#endif + +/*************************************************************************/ +/************************** Local declarations ***************************/ +/*************************************************************************/ + +/* Magic MIPS register index indicating that an RTL register is located in + * the stack frame and has no hardware register assigned */ +#define MIPS_noreg 32 + +/* List of caller-saved and callee-saved registers specified by the MIPS ABI. + * Note that we omit $at and $v1 from the caller-saved register list because + * we never assign them to RTL registers (they're only used to load values + * from the stack frame for use as instruction operands) */ +static const uint8_t caller_saved_regs[] = { + MIPS_v0, + MIPS_a0, MIPS_a1, MIPS_a2, MIPS_a3, + MIPS_t0, MIPS_t1, MIPS_t2, MIPS_t3, MIPS_t4, MIPS_t5, MIPS_t6, MIPS_t7, + MIPS_t8, MIPS_t9, +}; +static const uint8_t callee_saved_regs[] = { + MIPS_s0, MIPS_s1, MIPS_s2, MIPS_s3, MIPS_s4, MIPS_s5, MIPS_s6, + MIPS_s7, MIPS_s8, +}; + +/* Array of flags indicating which registers are caller-saved or callee-saved*/ +static const uint8_t reg_is_caller_saved[] = { + [MIPS_v1] = 1, + [MIPS_a0] = 1, [MIPS_a1] = 1, [MIPS_a2] = 1, [MIPS_a3] = 1, + [MIPS_t0] = 1, [MIPS_t1] = 1, [MIPS_t2] = 1, [MIPS_t3] = 1, + [MIPS_t4] = 1, [MIPS_t5] = 1, [MIPS_t6] = 1, [MIPS_t7] = 1, + [MIPS_t8] = 1, [MIPS_t9] = 1, +}; +static const uint8_t reg_is_callee_saved[] = { + [MIPS_s0] = 1, [MIPS_s1] = 1, [MIPS_s2] = 1, [MIPS_s3] = 1, + [MIPS_s4] = 1, [MIPS_s5] = 1, [MIPS_s6] = 1, [MIPS_s7] = 1, + [MIPS_s8] = 1, +}; + +/* Fake opcodes for branching to labels (to be resolved later) */ +#define __OP_BEQ_LABEL 074 // __OP_BEQ + 070 +#define __OP_BNE_LABEL 075 // __OP_BNE + 070 +#define MIPS_BEQ_LABEL(rs,rt,label) \ + __MIPS_INSN_IMM(BEQ_LABEL, (rs), (rt), (label)) +#define MIPS_BNE_LABEL(rs,rt,label) \ + __MIPS_INSN_IMM(BNE_LABEL, (rs), (rt), (label)) +#define MIPS_BEQZ_LABEL(reg,label) MIPS_BEQ_LABEL((reg), MIPS_zero, (label)) +#define MIPS_BNEZ_LABEL(reg,label) MIPS_BNE_LABEL((reg), MIPS_zero, (label)) +#define MIPS_B_LABEL(label) MIPS_BEQZ_LABEL(MIPS_zero, (label)) + +/* Fake opcode for jumping to the epilogue, returning a constant in $v0 */ +#define MIPS_B_EPILOGUE_RET_CONST __MIPS_SPECIAL(0, 0, 0, 1, JALR) + +/* Fake opcode for jumping to the epilogue, then chaining to the address + * in $at */ +#define MIPS_B_EPILOGUE_CHAIN_AT __MIPS_SPECIAL(0, 0, 0, 2, JALR) + +/*-----------------------------------------------------------------------*/ + +static int allocate_registers(RTLBlock * const block); +static inline int allocate_regs_unit(RTLBlock * const block, + const unsigned int unit_index); +static inline int allocate_regs_insn(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index); +static void add_to_active_list(RTLBlock * const block, const int is_frame, + const unsigned int index); +static void remove_from_active_list(RTLBlock * const block, const int is_frame, + const unsigned int index); +static void clean_active_list(RTLBlock * const block, + const uint32_t clean_insn); +static int allocate_one_register(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index); +static int allocate_one_register_prefer(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index, + const unsigned int prefer_mips); +static int merge_constant_register(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index); +static int allocate_frame_slot(RTLBlock * const block, + const uint32_t insn_index, + const unsigned int reg_index); +#ifdef MIPS_OPTIMIZE_IMMEDIATE +static int optimize_immediate(RTLBlock * const block, + const uint32_t insn_index); +#endif +#ifdef RTL_TRACE_GENERATE +static void print_regmap(RTLBlock * const block); +#endif + +/*----------------------------------*/ + +static int translate_block(RTLBlock * const block); +static inline int translate_unit(RTLBlock * const block, + const unsigned int unit_index); +static inline unsigned int translate_insn(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index); +static int append_alu_1op(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode); +static int append_alu_reg(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode); +static int append_shift_reg(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode); +static int append_mult_div(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode, const int accumulate, + const uint32_t insn_index); + +static int flush_for_call(RTLBlock * const block, const uint32_t insn_index); +static int reload_after_call(RTLBlock * const block, + const uint32_t insn_index); +static int flush_hilo(RTLBlock * const block, const uint32_t insn_index); + +#ifdef MIPS_OPTIMIZE_MIN_MAX +static int optimize_min_max(RTLBlock * const block, const uint32_t insn_index); +#endif + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 +static unsigned int sh2_stealth_trace_insn(RTLBlock * const block, + const uint32_t insn_index); +static unsigned int sh2_stealth_cache_reg(RTLBlock * const block, + const uint32_t insn_index); +static unsigned int sh2_stealth_trace_store(RTLBlock * const block, + const uint32_t insn_index); +#endif + +static int resolve_branches(RTLBlock * const block); + +/*----------------------------------*/ + +static inline int append_insn(RTLBlock * const block, const uint32_t insn); +static int expand_block(RTLBlock * const block); +static uint32_t last_insn(const RTLBlock * const block); +static uint32_t pop_insn(RTLBlock * const block); + +static int append_float(RTLBlock * const block, const uint32_t insn, + const int latency); +static int append_branch(RTLBlock * const block, const uint32_t insn); + +static int append_prologue(RTLBlock * const block); +static int append_epilogue(RTLBlock * const block); + +/*----------------------------------*/ + +#ifdef __GNUC__ +# define CONST_FUNC __attribute__((const)) +#else +# define CONST_FUNC /*nothing*/ +#endif + +static CONST_FUNC inline int insn_rs(const uint32_t opcode); +static CONST_FUNC inline int insn_rt(const uint32_t opcode); +static CONST_FUNC inline int insn_rd(const uint32_t opcode); +static CONST_FUNC inline int insn_imm(const uint32_t opcode); + +static CONST_FUNC inline int insn_is_load(const uint32_t opcode); +static CONST_FUNC inline int insn_is_store(const uint32_t opcode); +static CONST_FUNC inline int insn_is_jump(const uint32_t opcode); +static CONST_FUNC inline int insn_is_branch(const uint32_t opcode); +static CONST_FUNC inline int insn_is_imm(const uint32_t opcode); +static CONST_FUNC inline int insn_is_imm_alu(const uint32_t opcode); +static CONST_FUNC inline int insn_is_special(const uint32_t opcode); +static CONST_FUNC inline int insn_is_regimm(const uint32_t opcode); +static CONST_FUNC inline int insn_is_allegrex(const uint32_t opcode); + +static CONST_FUNC inline uint32_t insn_regs_used(const uint32_t opcode); +static CONST_FUNC inline uint32_t insn_regs_set(const uint32_t opcode); + +/*************************************************************************/ + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + +/* Array for caching unflushed SH-2 register values (this assumes + * singlethreaded execution, but since this is only for debugging anyway + * let's not worry too hard about it) */ + +static uint32_t sh2_regcache[23]; + +/*----------------------------------*/ + +/* Set up common code blocks as constant arrays so we don't bloat the code + * too badly */ + +/* Save all caller-saved MIPS registers and the current state block + * register values on the stack; the state block pointer is assumed to be + * in $at */ +static const uint32_t code_save_regs_state[] = { + /* Add stack space for saving registers */ + MIPS_ADDIU(MIPS_sp, MIPS_sp, -(4*(18+24))), + /* First save all MIPS caller-saved registers */ + MIPS_SW(MIPS_v0, 0, MIPS_sp), + MIPS_SW(MIPS_v1, 4, MIPS_sp), + MIPS_SW(MIPS_a0, 8, MIPS_sp), + MIPS_SW(MIPS_a1, 12, MIPS_sp), + MIPS_SW(MIPS_a2, 16, MIPS_sp), + MIPS_SW(MIPS_a3, 20, MIPS_sp), + MIPS_SW(MIPS_t0, 24, MIPS_sp), + MIPS_SW(MIPS_t1, 28, MIPS_sp), + MIPS_SW(MIPS_t2, 32, MIPS_sp), + MIPS_SW(MIPS_t3, 36, MIPS_sp), + MIPS_SW(MIPS_t4, 40, MIPS_sp), + MIPS_SW(MIPS_t5, 44, MIPS_sp), + MIPS_SW(MIPS_t6, 48, MIPS_sp), + MIPS_SW(MIPS_t7, 52, MIPS_sp), + MIPS_SW(MIPS_t8, 56, MIPS_sp), + MIPS_SW(MIPS_t9, 60, MIPS_sp), + MIPS_MFLO(MIPS_v1), + MIPS_SW(MIPS_v1, 64, MIPS_sp), + MIPS_MFHI(MIPS_v1), + MIPS_SW(MIPS_v1, 68, MIPS_sp), + /* Copy the current register values in the SH-2 state block (which may + * not be up to date) to the stack */ + MIPS_MOVE(MIPS_a0, MIPS_at), + MIPS_ADDIU(MIPS_a1, MIPS_sp, 4*18), + MIPS_ADDIU(MIPS_a2, MIPS_a0, 4*23), + MIPS_LW(MIPS_v1, 0, MIPS_a0), + MIPS_ADDIU(MIPS_a0, MIPS_a0, 4), + MIPS_ADDIU(MIPS_a1, MIPS_a1, 4), + MIPS_BNE(MIPS_a0, MIPS_a2, -4), + MIPS_SW(MIPS_v1, -4, MIPS_a1), + /* Copy the current cycle count to the stack (leaving a copy in $v1) + * and return */ + MIPS_LW(MIPS_v1, offsetof(SH2State,cycles), MIPS_at), + MIPS_JR(MIPS_ra), + MIPS_SW(MIPS_v1, 0, MIPS_a1), +}; + +/* Restore all caller-saved MIPS registers and the current state block + * register values from the stack; the state block pointer is assumed to be + * in $at */ +static const uint32_t code_restore_regs_state[] = { + /* Restore values to the SH-2 state block */ + MIPS_MOVE(MIPS_a0, MIPS_at), + MIPS_ADDIU(MIPS_a1, MIPS_sp, 4*18), + MIPS_ADDIU(MIPS_a2, MIPS_a0, 4*23), + MIPS_LW(MIPS_v1, 0, MIPS_a1), + MIPS_ADDIU(MIPS_a0, MIPS_a0, 4), + MIPS_ADDIU(MIPS_a1, MIPS_a1, 4), + MIPS_BNE(MIPS_a0, MIPS_a2, -4), + MIPS_SW(MIPS_v1, -4, MIPS_a0), + MIPS_LW(MIPS_v1, 0, MIPS_a1), + MIPS_SW(MIPS_v1, offsetof(SH2State,cycles), MIPS_at), + /* Restore all MIPS caller-saved registers */ + MIPS_LW(MIPS_v1, 64, MIPS_sp), + MIPS_MTLO(MIPS_v1), + MIPS_LW(MIPS_v1, 68, MIPS_sp), + MIPS_MTHI(MIPS_v1), + MIPS_LW(MIPS_v0, 0, MIPS_sp), + MIPS_LW(MIPS_v1, 4, MIPS_sp), + MIPS_LW(MIPS_a0, 8, MIPS_sp), + MIPS_LW(MIPS_a1, 12, MIPS_sp), + MIPS_LW(MIPS_a2, 16, MIPS_sp), + MIPS_LW(MIPS_a3, 20, MIPS_sp), + MIPS_LW(MIPS_t0, 24, MIPS_sp), + MIPS_LW(MIPS_t1, 28, MIPS_sp), + MIPS_LW(MIPS_t2, 32, MIPS_sp), + MIPS_LW(MIPS_t3, 36, MIPS_sp), + MIPS_LW(MIPS_t4, 40, MIPS_sp), + MIPS_LW(MIPS_t5, 44, MIPS_sp), + MIPS_LW(MIPS_t6, 48, MIPS_sp), + MIPS_LW(MIPS_t7, 52, MIPS_sp), + MIPS_LW(MIPS_t8, 56, MIPS_sp), + MIPS_LW(MIPS_t9, 60, MIPS_sp), + /* Restore the stack pointer and return */ + MIPS_JR(MIPS_ra), + MIPS_ADDIU(MIPS_sp, MIPS_sp, 4*(18+24)), +}; + +/* Save all caller-saved MIPS registers on the stack */ +static const uint32_t code_save_regs[] = { + /* Add stack space for saving registers */ + MIPS_ADDIU(MIPS_sp, MIPS_sp, -(4*16)), + MIPS_SW(MIPS_v0, 0, MIPS_sp), + MIPS_SW(MIPS_v1, 4, MIPS_sp), + MIPS_SW(MIPS_a0, 8, MIPS_sp), + MIPS_SW(MIPS_a1, 12, MIPS_sp), + MIPS_SW(MIPS_a2, 16, MIPS_sp), + MIPS_SW(MIPS_a3, 20, MIPS_sp), + MIPS_SW(MIPS_t0, 24, MIPS_sp), + MIPS_SW(MIPS_t1, 28, MIPS_sp), + MIPS_SW(MIPS_t2, 32, MIPS_sp), + MIPS_SW(MIPS_t3, 36, MIPS_sp), + MIPS_SW(MIPS_t4, 40, MIPS_sp), + MIPS_SW(MIPS_t5, 44, MIPS_sp), + MIPS_SW(MIPS_t6, 48, MIPS_sp), + MIPS_SW(MIPS_t7, 52, MIPS_sp), + MIPS_SW(MIPS_t8, 56, MIPS_sp), + MIPS_SW(MIPS_t9, 60, MIPS_sp), + MIPS_MFLO(MIPS_v1), + MIPS_SW(MIPS_v1, 64, MIPS_sp), + MIPS_MFHI(MIPS_v1), + MIPS_JR(MIPS_ra), + MIPS_SW(MIPS_v1, 68, MIPS_sp), +}; + +/* Restore all caller-saved MIPS registers from the stack */ +static const uint32_t code_restore_regs[] = { + MIPS_LW(MIPS_v1, 64, MIPS_sp), + MIPS_MTLO(MIPS_v1), + MIPS_LW(MIPS_v1, 68, MIPS_sp), + MIPS_MTHI(MIPS_v1), + MIPS_LW(MIPS_v0, 0, MIPS_sp), + MIPS_LW(MIPS_v1, 4, MIPS_sp), + MIPS_LW(MIPS_a0, 8, MIPS_sp), + MIPS_LW(MIPS_a1, 12, MIPS_sp), + MIPS_LW(MIPS_a2, 16, MIPS_sp), + MIPS_LW(MIPS_a3, 20, MIPS_sp), + MIPS_LW(MIPS_t0, 24, MIPS_sp), + MIPS_LW(MIPS_t1, 28, MIPS_sp), + MIPS_LW(MIPS_t2, 32, MIPS_sp), + MIPS_LW(MIPS_t3, 36, MIPS_sp), + MIPS_LW(MIPS_t4, 40, MIPS_sp), + MIPS_LW(MIPS_t5, 44, MIPS_sp), + MIPS_LW(MIPS_t6, 48, MIPS_sp), + MIPS_LW(MIPS_t7, 52, MIPS_sp), + MIPS_LW(MIPS_t8, 56, MIPS_sp), + MIPS_LW(MIPS_t9, 60, MIPS_sp), + MIPS_JR(MIPS_ra), + MIPS_ADDIU(MIPS_sp, MIPS_sp, 4*16), +}; + +#endif // RTL_TRACE_STEALTH_FOR_SH2 + +/*************************************************************************/ +/*********************** Main translation routine ************************/ +/*************************************************************************/ + +/** + * rtl_translate_block_mips: Translate the given block into MIPS code. + * + * [Parameters] + * block: RTLBlock to translate + * code_ret: Pointer to variable to receive code buffer pointer + * size_ret: Pointer to variable to receive code buffer size (in bytes) + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * On error, *code_ret and *size_ret are not modified. + */ +int rtl_translate_block_mips(RTLBlock *block, void **code_ret, + uint32_t *size_ret) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(code_ret != NULL, return 0); + PRECOND(size_ret != NULL, return 0); + + /* Handle manually-optimized cases specially for increased efficiency */ + if (block->num_insns == 4 + && block->insns[0].opcode == RTLOP_LOAD_PARAM + && block->insns[0].src_imm == 0 + && block->insns[1].opcode == RTLOP_LOAD_ADDR + && block->insns[2].opcode == RTLOP_CALL + && block->insns[2].src1 == block->insns[0].dest + && block->insns[2].target == block->insns[1].dest + && block->insns[3].opcode == RTLOP_RETURN + ) { + const unsigned int length = 16; + uint32_t *code = malloc(length); + if (!code) { + DMSG("No memory for native buffer (%u bytes)", length); + return 0; + } + if ((uintptr_t)code >> 28 == block->insns[1].src_addr >> 28) { + code[0] = MIPS_J((block->insns[1].src_addr & 0x0FFFFFFF) >> 2); + code[1] = MIPS_NOP(); + } else { + code[0] = MIPS_LUI(MIPS_at, block->insns[1].src_addr >> 16); + code[1] = MIPS_ORI(MIPS_at, MIPS_at, + block->insns[1].src_addr & 0xFFFF); + code[2] = MIPS_JR(MIPS_at); + code[3] = MIPS_NOP(); + } + *code_ret = code; + *size_ret = length; + return 1; + } + + /* Initialize translation-specific fields */ + block->native_buffer = NULL; + block->native_bufsize = 0; + block->native_length = 0; + block->label_offsets = NULL; + block->mips.need_save_ra = 0; + block->mips.need_chain_at = 0; + block->mips.frame_used = 0; + block->mips.sreg_used = 0; + + /* Check that the number of labels won't cause problems for our fake + * MIPS instructions */ + if (UNLIKELY(block->next_label > 1<<16)) { + DMSG("%p: Too many labels (%u, max %u)", block, (1<<16) - 1, + block->next_label - 1); + } + + /* Allocate the label map */ + if (block->next_label > 0) { // Should always be true, but just in case + block->label_offsets = + malloc(sizeof(*block->label_offsets) * block->next_label); + if (UNLIKELY(!block->label_offsets)) { + DMSG("No memory for block label offsets (%u bytes)", + sizeof(*block->label_offsets) * block->next_label); + goto fail; + } + memset(block->label_offsets, -1, + sizeof(*block->label_offsets) * block->next_label); + } + + /* Allocate an initial native code buffer for the block */ + block->native_bufsize = NATIVE_EXPAND_SIZE; + block->native_buffer = malloc(block->native_bufsize); + if (UNLIKELY(!block->native_buffer)) { + DMSG("No memory for native buffer (%u bytes)", block->native_bufsize); + goto fail; + } + + /* Allocate MIPS registers (and possibly stack frame locations) for all + * RTL registers, and perform other pre-translation scanning and + * optimization */ + if (UNLIKELY(!allocate_registers(block))) { + goto fail; + } +#ifdef RTL_TRACE_GENERATE + print_regmap(block); +#endif + + /* Translate the RTL instructions into MIPS code */ +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + block->sh2_regcache_mask = 0; +#endif + if (UNLIKELY(!translate_block(block))) { + goto fail; + } + + /* Free the branch label offset table */ + free(block->label_offsets); + block->label_offsets = NULL; + + /* Shrink the buffer down to the actual length before returning it */ + *code_ret = realloc(block->native_buffer, block->native_length); + if (UNLIKELY(!*code_ret)) { + DMSG("realloc() to a smaller size failed?!"); + *code_ret = block->native_buffer; + } + *size_ret = block->native_length; + + /* Make sure we don't accidentally free the native code buffer while + * the caller is using it */ + block->native_buffer = NULL; + block->native_bufsize = 0; + block->native_length = 0; + + /* Success */ + free(block->label_offsets); + block->label_offsets = NULL; + return 1; + + fail: + free(block->native_buffer); + free(block->label_offsets); + block->native_buffer = NULL; + block->native_bufsize = 0; + block->native_length = 0; + block->label_offsets = NULL; + return 0; +} + +/*************************************************************************/ +/************************** Register allocation **************************/ +/*************************************************************************/ + +/** + * allocate_registers: Allocate MIPS registers (and, if necessary, stack + * frame locations) for all RTL registers in the block. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +static int allocate_registers(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + int unit_index; + + /* + * The basic algorithm used for allocating registers is linear scan, as + * described by Poletto and Sarkar. However, since we do not have a + * sorted list of live intervals, we instead iterate through the + * instruction stream, allocating a hardware register (or frame slot) + * for each RTL register the first time it is encountered. + * + * Since live intervals calculated by the core RTL code do not take + * into account backward branches, the register allocation code checks + * each basic unit for entering edges from later units in the code + * stream, and if such a unit is found, updates the live intervals of + * all live registers to extend through the end of that unit (the + * latest unit in code stream order if there is more than one). + * + * The basic algorithm is tweaked as follows for the MIPS CPU: + * + * - Registers which are live over multiple basic units including a + * unit with a subroutine call are preferentially assigned callee- + * saved registers $s0-$s8, while registers which are only live + * within a single basic unit or which do not cross a subroutine + * call are preferentially assigned caller-saved registers $v1, + * $a0-$a3, and $t0-$t9. ($at and $v1 are reserved for loading + * values from the stack frame to be used as instruction + * operands.) However, constants are always assigned caller-saved + * registers, since it is often faster (and never slower) to + * reload them with addiu/ori/lui instructions than to save and + * restore another register on the stack. + * + * - Among caller-saved registers, preference is given to registers + * whose last use was longer ago. This assists instruction + * rescheduling by providing a larger live window for each + * hardware register. Callee-saved registers are allocated in + * numerical order regardless of last use to minimize the number + * of such registers which need to be saved and restored. + * + * - When spilling registers, the register with the shortest usage + * interval is spilled, rather than the one with the longest. + * (Spilling the longest interval first would cause the SH-2 state + * block pointer to be spilled, significantly impacting + * performance.) + * [FIXME: Currently, we always spill the new register.] + */ + + /* Clear the register map, stack frame map, and active register list */ + memset(block->mips.reg_map, 0, sizeof(block->mips.reg_map)); + block->mips.reg_free = ~0; + memset(block->mips.frame_map, 0, sizeof(block->mips.frame_map)); + memset(block->mips.frame_free, ~0, sizeof(block->mips.frame_free)); + block->mips.first_active = -1; + + /* Pick up the first unit with a CALL instruction, so we know where + * we'll need to use callee-saved registers */ + block->mips.next_call_unit = block->first_call_unit; + + /* Allocate registers for each basic unit in code stream order */ + for (unit_index = 0; unit_index >= 0; + unit_index = block->units[unit_index].next_unit + ) { + if (UNLIKELY(!allocate_regs_unit(block, unit_index))) { + return 0; + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * allocate_regs_unit: Allocate MIPS registers for an RTL basic unit. + * + * [Parameters] + * block: RTL block + * unit: Index of basic unit + * [Return value] + * Nonzero on success, zero on error + */ +static int allocate_regs_unit(RTLBlock * const block, + const unsigned int unit_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + RTLUnit * const unit = &block->units[unit_index]; + + /* Copy the current register and stack frame map to the unit structure */ + + memcpy(unit->mips.reg_map, block->mips.reg_map, + sizeof(block->mips.reg_map)); + memcpy(unit->mips.frame_map, block->mips.frame_map, + sizeof(block->mips.frame_map)); + + /* Move to the next call unit if we reached the current one */ + + while (block->mips.next_call_unit >= 0 + && block->mips.next_call_unit <= unit_index + ) { + block->mips.next_call_unit = + block->units[block->mips.next_call_unit].next_call_unit; + } + + /* Scan through RTL instructions and allocate MIPS registers */ + + int32_t insn_index; // Signed so we catch last_insn==-1 properly + for (insn_index = unit->first_insn; insn_index <= unit->last_insn; + insn_index++ + ) { + if (UNLIKELY(!allocate_regs_insn(block, unit_index, insn_index))) { + return 0; + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * allocate_regs_insn: Perform register allocation for registers used in a + * single RTL instruction. + * + * [Parameters] + * block: RTL block + * unit_index: Index of current RTL unit + * insn_index: Index of RTL instruction to allocate registers for + * [Return value] + * Nonzero on success, zero on error + */ +static inline int allocate_regs_insn(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(unit_index < block->num_units, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + RTLInsn * const insn = &block->insns[insn_index]; + + retry: + switch ((RTLOpcode)insn->opcode) { + + case RTLOP_NOP: +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + if (insn->src_imm & 0x80000000) { + /* If we have stealth trace NOPs, we'll be calling subroutines, + * so ensure $ra is saved */ + block->mips.need_save_ra = 1; + } +#endif + return 1; + + case RTLOP_LABEL: + case RTLOP_GOTO: + case RTLOP_RETURN: + return 1; + + case RTLOP_LOAD_IMM: + case RTLOP_LOAD_ADDR: + /* If this register is unused elsewhere, kill the instruction */ + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } +#ifdef MIPS_OPTIMIZE_IMMEDIATE + /* Try to optimize this and the following instruction into a single + * immediate instruction (but only if it's part of the same unit) */ + if (insn_index < block->units[unit_index].last_insn + && optimize_immediate(block, insn_index) + ) { + if (insn->opcode != RTLOP_LOAD_IMM + && insn->opcode != RTLOP_LOAD_ADDR + ) { + goto retry; // Instruction was altered + } + } +#endif + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + if (!block->regs[insn->dest].native_allocated + && merge_constant_register(block, unit_index, insn_index, + insn->dest) + ) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + + case RTLOP_LOAD_PARAM: + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + /* If it's a long-lived register, let allocate_one_register() + * select a callee-saved register; otherwise, try to allocate the + * appropriate parameter register to avoid an unnecessary MOVE */ + if (block->mips.next_call_unit >= 0 + && block->regs[insn->dest].death + >= block->units[block->mips.next_call_unit].first_insn + ) { + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + } else { + return allocate_one_register_prefer( + block, unit_index, insn_index, insn->dest, + MIPS_a0 + block->regs[insn->dest].param_index + ); + } + + case RTLOP_MOVE: + case RTLOP_NOT: + case RTLOP_CLZ: + case RTLOP_CLO: + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: + case RTLOP_ADDI: + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + case RTLOP_SLTSI: + case RTLOP_BFEXT: + case RTLOP_LOAD_BU: + case RTLOP_LOAD_BS: + case RTLOP_LOAD_HU: + case RTLOP_LOAD_HS: + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + if (UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src1))) { + return 0; + } + /* Source registers can be reused as destination registers */ + clean_active_list(block, insn_index+1); + if (insn->opcode == RTLOP_ANDI && insn->src_imm > 0xFFFF + && block->regs[insn->src1].native_reg != MIPS_noreg + ) { + /* This might be handled as "ins dest,$zero,...", so try to + * make the destination use the same register as the source */ + return allocate_one_register_prefer( + block, unit_index, insn_index, insn->dest, + block->regs[insn->src1].native_reg + ); + } else { + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + } + + case RTLOP_STORE_B: + case RTLOP_STORE_H: + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + return allocate_one_register(block, unit_index, insn_index, + insn->src1) + && allocate_one_register(block, unit_index, insn_index, + insn->dest); + + case RTLOP_ADD: + case RTLOP_SUB: + case RTLOP_AND: + case RTLOP_OR: + case RTLOP_XOR: + case RTLOP_SLL: + case RTLOP_SRL: + case RTLOP_SRA: + case RTLOP_ROR: + case RTLOP_SLTU: + case RTLOP_SLTS: + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->src2].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + if (UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src1)) + || UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src2)) + ) { + return 0; + } + clean_active_list(block, insn_index+1); + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + + case RTLOP_SELECT: { + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->src2].last_used = insn_index; + block->regs[insn->cond].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + if (UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src1)) + || UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src2)) + || UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->cond)) + ) { + return 0; + } + clean_active_list(block, insn_index+1); + /* We implement SELECT with a MOVE/MOVZ pair in the general case. + * If the target shares a hardware register with either of the + * source operands, we can drop the MOVE and use a single MOVZ or + * MOVN instead, so try to do that. */ + int desired_reg; + if (((desired_reg = block->regs[insn->src1].native_reg) != MIPS_noreg + && desired_reg != MIPS_zero + && (block->mips.reg_free & (1 << desired_reg))) + || ((desired_reg = block->regs[insn->src2].native_reg) != MIPS_noreg + && desired_reg != MIPS_zero + && (block->mips.reg_free & (1 << desired_reg))) + ) { + return allocate_one_register_prefer(block, unit_index, insn_index, + insn->dest, desired_reg); + } else { + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + } + } + + case RTLOP_BFINS: { + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->src2].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + clean_active_list(block, insn_index); + if (UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src1)) + || UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src2)) + ) { + return 0; + } + clean_active_list(block, insn_index+1); + /* The INS instruction treats its destination (rt) as read-write, + * so try to allocate the destination in the same register as src1. + * If we fail, we'll have to MOVE(dest,src1) at translation time. */ + if (block->regs[insn->src1].native_reg == MIPS_noreg + || block->regs[insn->src1].native_reg == MIPS_zero + ) { + return allocate_one_register(block, unit_index, insn_index, + insn->dest); + } else { + const int desired_reg = block->regs[insn->src1].native_reg; + return allocate_one_register_prefer(block, unit_index, insn_index, + insn->dest, desired_reg); + } + } + + case RTLOP_MULU: + case RTLOP_MULS: + case RTLOP_MADDU: + case RTLOP_MADDS: + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: + if (UNLIKELY((!insn->dest || block->regs[insn->dest].birth == block->regs[insn->dest].death) + && (!insn->dest2 || block->regs[insn->dest2].birth == block->regs[insn->dest2].death)) + ) { + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + return 1; + } + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->src2].last_used = insn_index; + block->regs[insn->dest].last_used = insn_index; + block->regs[insn->dest2].last_used = insn_index; + clean_active_list(block, insn_index); + if (UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src1)) + || UNLIKELY(!allocate_one_register(block, unit_index, insn_index, + insn->src2)) + ) { + return 0; + } + clean_active_list(block, insn_index+1); + return (!insn->dest + || allocate_one_register(block, unit_index, insn_index, + insn->dest)) + && (!insn->dest2 + || allocate_one_register(block, unit_index, insn_index, + insn->dest2)); + + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: + block->regs[insn->src1].last_used = insn_index; + clean_active_list(block, insn_index); + return allocate_one_register(block, unit_index, insn_index, + insn->src1); + + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: + block->regs[insn->src1].last_used = insn_index; + block->regs[insn->src2].last_used = insn_index; + clean_active_list(block, insn_index); + return allocate_one_register(block, unit_index, insn_index, + insn->src1) + && allocate_one_register(block, unit_index, insn_index, + insn->src2); + + case RTLOP_CALL: { + if (UNLIKELY(block->regs[insn->dest].birth == block->regs[insn->dest].death)) { + insn->dest = 0; + /* Execute the call anyway, because it may have side effects */ + } + if (insn->src1) { + block->regs[insn->src1].last_used = insn_index; + } + if (insn->src2) { + block->regs[insn->src2].last_used = insn_index; + } + block->regs[insn->target].last_used = insn_index; + if (insn->dest) { + block->regs[insn->dest].last_used = insn_index; + } + clean_active_list(block, insn_index); + if ((insn->src1 + && UNLIKELY(!allocate_one_register_prefer(block, unit_index, insn_index, insn->src1, MIPS_a0))) + || (insn->src2 + && UNLIKELY(!allocate_one_register_prefer(block, unit_index, insn_index, insn->src2, MIPS_a1))) + || UNLIKELY(!allocate_one_register_prefer(block, unit_index, insn_index, insn->target, MIPS_t9)) + ) { + return 0; + } + clean_active_list(block, insn_index+1); + /* Null out any free caller-saved registers because they'll have + * been clobbered by the call (otherwise we might try to reuse a + * constant value that's no longer valid) */ + unsigned int i; + for (i = 0; i < lenof(caller_saved_regs); i++) { + const unsigned int mips_reg = caller_saved_regs[i]; + if (block->mips.reg_free & (1 << mips_reg)) { + block->mips.reg_map[mips_reg] = NULL; + } + } + if (insn->dest + && UNLIKELY(!allocate_one_register_prefer(block, unit_index, insn_index, insn->dest, MIPS_v0)) + ) { + return 0; + } + /* We'll need to save $ra now */ + block->mips.need_save_ra = 1; + return 1; + } // case RTLOP_CALL + + case RTLOP_RETURN_TO: + block->regs[insn->target].last_used = insn_index; + clean_active_list(block, insn_index); + return allocate_one_register(block, unit_index, insn_index, + insn->target); + + } // switch (insn->opcode) + + DMSG("%p/%u: Invalid RTL opcode %u", block, insn_index, insn->opcode); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * add_to_active_list: Add a register to the active list. + * + * [Parameters] + * block: RTL block + * is_frame: Nonzero if the register is in a frame slot, zero if in a + * hardware register + * index: MIPS register index or frame slot index + * [Return value] + * None + */ +static void add_to_active_list(RTLBlock * const block, const int is_frame, + const unsigned int index) +{ + PRECOND(block != NULL, return); + + const unsigned int list_index = is_frame ? 32+index : index; + block->mips.active_list[list_index].is_frame = is_frame ? 1 : 0; + block->mips.active_list[list_index].index = index; + const uint32_t death = is_frame ? block->mips.frame_map[index]->death + : block->mips.reg_map[index]->death; + + int insert_at = -1; + int next = block->mips.first_active; + while (next >= 0 + && (block->mips.active_list[next].is_frame + ? block->mips.frame_map[block->mips.active_list[next].index]->death + : block->mips.reg_map[block->mips.active_list[next].index]->death) + < death + ) { + insert_at = next; + next = block->mips.active_list[next].next; + } + if (insert_at < 0) { + block->mips.first_active = list_index; + } else { + block->mips.active_list[insert_at].next = list_index; + } + block->mips.active_list[list_index].next = next; +} + +/*----------------------------------*/ + +/** + * remove_from_active_list: Explicitly remove a register from the active + * list. Used when merging constants or updating the death time of an + * active register. This function does NOT set the "free" bit for the + * freed register or frame slot. + * + * [Parameters] + * block: RTL block + * is_frame: Nonzero if the register is in a frame slot, zero if in a + * hardware register + * index: MIPS register index or frame slot index + * [Return value] + * None + */ +static void remove_from_active_list(RTLBlock * const block, const int is_frame, + const unsigned int index) +{ + PRECOND(block != NULL, return); + + const unsigned int list_index = is_frame ? 32+index : index; + + int prev = -1; + int next = block->mips.first_active; + while (next >= 0 && next != list_index) { + prev = next; + next = block->mips.active_list[next].next; + } + if (next >= 0) { + next = block->mips.active_list[next].next; + if (prev < 0) { + block->mips.first_active = next; + } else { + block->mips.active_list[prev].next = next; + } + } +} + +/*----------------------------------*/ + +/** + * clean_active_list: Clean all dead registers from the active list. + * + * [Parameters] + * block: RTL block + * clean_insn: Instruction index for cleaning registers (any register + * with death < clean_insn is cleaned) + * [Return value] + * None + */ +static void clean_active_list(RTLBlock * const block, + const uint32_t clean_insn) +{ + PRECOND(block != NULL, return); + + int first_active = block->mips.first_active; + while (first_active >= 0 + && (block->mips.active_list[first_active].is_frame + ? block->mips.frame_map[block->mips.active_list[first_active].index]->death + : block->mips.reg_map[block->mips.active_list[first_active].index]->death) + < clean_insn + ) { + const unsigned int index = block->mips.active_list[first_active].index; + if (block->mips.active_list[first_active].is_frame) { + block->mips.frame_free[index/32] |= 1 << (index%32); + } else { + block->mips.reg_free |= 1 << index; + } + first_active = block->mips.active_list[first_active].next; + block->mips.first_active = first_active; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * allocate_one_register: Allocate a MIPS register for the given RTL + * register, if it does not have one allocated already, and update the + * current register map. + * + * [Parameters] + * block: RTL block + * unit_index: Index of current basic unit + * insn_index: Index of current instruction + * reg_index: Index of RTL register + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * This routine only maps into the available caller-saved registers, + * $3-$15 and $24-$25 ($v1, $a0-$a3, and $t0-$t9); the callee-saved + * registers ($s0-$s8) are only used when renaming. + */ +static int allocate_one_register(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(reg_index < block->next_reg, return 0); + + RTLRegister * const reg = &block->regs[reg_index]; + + /* If it's already allocated, just return */ + if (reg->native_allocated) { + return 1; + } + + /* If it's a constant zero value, just use $zero */ + if (reg->source == RTLREG_CONSTANT && reg->value == 0) { + reg->native_allocated = 1; + reg->native_reg = MIPS_zero; + /* We don't mark it in the register map or add it to the active + * list since $zero can be shared */ + return 1; + } + + /* Macro to select a register if its last use is the oldest so far */ + #define SELECT_REG_BY_AGE(mips_reg) { \ + const int __mips_reg = (mips_reg); \ + if (block->mips.reg_free & (1 << __mips_reg)) { \ + if (!block->mips.reg_map[__mips_reg]) { \ + best_mips = __mips_reg; \ + break; \ + } else if (block->mips.reg_map[__mips_reg]->death < oldest) { \ + best_mips = __mips_reg; \ + oldest = block->mips.reg_map[__mips_reg]->death; \ + } \ + } \ + } + /* Macro to select a register unconditionally if it's free */ + #define SELECT_REG_BY_FREE(mips_reg) { \ + const int __mips_reg = (mips_reg); \ + if (block->mips.reg_free & (1 << __mips_reg)) { \ + best_mips = __mips_reg; \ + break; \ + } \ + } + + int best_mips = -1; + uint32_t oldest = insn_index; // Last use of best_$mips + unsigned int i; + if (reg->source != RTLREG_CONSTANT + && block->mips.next_call_unit >= 0 + && reg->death >= block->units[block->mips.next_call_unit].first_insn + ) { + /* Prefer a callee-saved register for non-constant, long-lived + * registers */ + for (i = 0; i < lenof(callee_saved_regs); i++) { + SELECT_REG_BY_FREE(callee_saved_regs[i]); + } + if (best_mips < 0) { + for (i = 0; i < lenof(caller_saved_regs); i++) { + SELECT_REG_BY_AGE(caller_saved_regs[i]); + } + } + } else { + /* Prefer a caller-saved register for short-lived registers */ + for (i = 0; i < lenof(caller_saved_regs); i++) { + SELECT_REG_BY_AGE(caller_saved_regs[i]); + } + if (best_mips < 0) { + for (i = 0; i < lenof(callee_saved_regs); i++) { + SELECT_REG_BY_FREE(callee_saved_regs[i]); + } + } + } + + #undef SELECT_REG_BY_AGE + #undef SELECT_REG_BY_FREE + + if (best_mips < 0) { + /* No free registers, so give it a frame slot instead */ + reg->native_allocated = 1; + reg->native_reg = MIPS_noreg; + return allocate_frame_slot(block, insn_index, reg_index); + } + + reg->native_allocated = 1; + reg->native_reg = best_mips; + block->mips.reg_map[best_mips] = &block->regs[reg_index]; + block->mips.reg_free &= ~(1 << best_mips); + if (reg_is_callee_saved[best_mips]) { + block->mips.sreg_used |= 1 << best_mips; + } + add_to_active_list(block, 0, best_mips); + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * allocate_one_register_prefer: Allocate a MIPS register for the given + * RTL register, like allocate_one_register(), but use MIPS register + * "prefer_mips" if possible. (If a MIPS register has already been + * allocated, the allocation is left unchanged.) + * + * [Parameters] + * block: RTL block + * unit_index: Index of current basic unit + * insn_index: Index of current instruction + * reg_index: Index of RTL register + * prefer_mips: MIPS register to prefer + * [Return value] + * Nonzero on success, zero on error + */ +static int allocate_one_register_prefer(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index, + const unsigned int prefer_mips) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(reg_index < block->next_reg, return 0); + PRECOND(prefer_mips < 32, return 0); + + RTLRegister * const reg = &block->regs[reg_index]; + + if (!reg->native_allocated && (block->mips.reg_free & (1<native_allocated = 1; + reg->native_reg = prefer_mips; + block->mips.reg_map[prefer_mips] = &block->regs[reg_index]; + block->mips.reg_free &= ~(1 << prefer_mips); + if (reg_is_callee_saved[prefer_mips]) { + block->mips.sreg_used |= 1 << prefer_mips; + } + add_to_active_list(block, 0, prefer_mips); + return 1; + } + + /* We couldn't get the preferred register, so just allocate as usual */ + return allocate_one_register(block, unit_index, insn_index, reg_index); +} + +/*-----------------------------------------------------------------------*/ + +/** + * merge_constant_register: Look for a MIPS hardware register holding the + * same constant value as the given RTL register; if found, assign that + * MIPS register to the given RTL register as well. + * + * When MIPS_OPTIMIZE_MERGE_CONSTANTS is disabled, this function will not + * search for generic constant registers, but will still assign $zero to + * RTL registers with the constant value 0. + * + * [Parameters] + * block: RTL block + * unit_index: Index of current basic unit + * insn_index: Index of current instruction + * reg_index: Index of RTL register + * [Return value] + * Nonzero if the register was successfully merged with another, else zero + */ +static int merge_constant_register(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index, + const unsigned int reg_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(unit_index < block->num_units, return 0); + PRECOND(reg_index < block->next_reg, return 0); + PRECOND(!block->regs[reg_index].native_allocated, return 0); + + RTLRegister * const reg = &block->regs[reg_index]; + + /* If it's a constant zero value, just use $zero */ + if (reg->source == RTLREG_CONSTANT && reg->value == 0) { + reg->native_allocated = 1; + reg->native_reg = MIPS_zero; + /* We don't mark it in the register map or add it to the active + * list since $zero can be shared */ + return 1; + } + + /* If it's a constant that has the same value as another constant + * that's already been loaded (and if MIPS_OPTIMIZE_MERGE_CONSTANTS + * is enabled), reuse the same hardware register. But don't reuse a + * register that died in a previous basic unit, since it may be on a + * different code path */ +#ifdef MIPS_OPTIMIZE_MERGE_CONSTANTS + if (reg->source == RTLREG_CONSTANT) { + unsigned int mips_reg; + for (mips_reg = MIPS_v1; mips_reg <= MIPS_s8; mips_reg++) { + if (block->mips.reg_map[mips_reg] + && block->mips.reg_map[mips_reg]->death + >= block->units[unit_index].first_insn + && block->mips.reg_map[mips_reg]->source == RTLREG_CONSTANT + && block->mips.reg_map[mips_reg]->value == reg->value + ) { + /* Found a match--assign this MIPS register to the + * current RTL register, and ensure the MIPS register + * stays allocated until all merged registers are dead */ + reg->native_allocated = 1; + reg->native_reg = mips_reg; + if (reg->death < block->mips.reg_map[mips_reg]->death) { + reg->death = block->mips.reg_map[mips_reg]->death; + } + reg->next_merged = block->mips.reg_map[mips_reg]; + block->mips.reg_map[mips_reg] = &block->regs[reg_index]; + RTLRegister *merged_reg = reg; + while ((merged_reg = merged_reg->next_merged) != NULL) { + merged_reg->death = reg->death; + } + if (!(block->mips.reg_free & (1 << mips_reg))) { + /* The register's live interval has changed, so remove + * it from the active list (we'll re-add it below) */ + remove_from_active_list(block, 0, mips_reg); + } + block->mips.reg_free &= ~(1 << mips_reg); + add_to_active_list(block, 0, mips_reg); + return 1; + } + } + } +#endif + + /* Couldn't merge this register into another */ + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * allocate_frame_slot: Allocate a frame slot for the given RTL register, + * if it does not have one allocated already. + * + * [Parameters] + * block: RTL block + * insn_index: Index of current instruction + * reg_index: Index of RTL register + * [Return value] + * Nonzero on success, zero on error + */ +static int allocate_frame_slot(RTLBlock * const block, + const uint32_t insn_index, + const unsigned int reg_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(reg_index < block->next_reg, return 0); + + RTLRegister * const reg = &block->regs[reg_index]; + + if (reg->frame_allocated) { + return 1; + } + + const unsigned int num_slots = MIPS_FRAME_SIZE/4; + unsigned int slot; + for (slot = 0; slot < num_slots; slot++) { + if (block->mips.frame_free[slot/32] & (1 << (slot%32))) { + break; + } + } + if (slot >= num_slots) { + DMSG("%p/%u: No free frame slots for RTL register %u", + block, insn_index, reg_index); + return 0; + } + + reg->frame_allocated = 1; + reg->frame_slot = slot; + reg->stack_offset = slot * 4; + block->mips.frame_map[slot] = &block->regs[reg_index]; + block->mips.frame_free[slot/32] &= ~(1 << (slot%32)); + block->mips.frame_used = 1; + add_to_active_list(block, 1, slot); + return 1; +} + +/*-----------------------------------------------------------------------*/ + +#ifdef MIPS_OPTIMIZE_IMMEDIATE + +/** + * optimize_immediate: Attempt to optimize a sequence of LOAD_IMM or + * LOAD_ADDR followed by an instruction using the loaded constant into a + * sequence which uses fewer MIPS instructions. On success, the RTL + * instruction (and possibly following instructions) will be altered + * appropriately. + * + * The following optimizations are performed when r1 is not used beyond + * the second instruction and (for ALU instructions) the constant value is + * within range of the particular instruction: + * + * LOAD_IMM r1, constant; ALUOP r2, r3, r1 + * --> NOP; ALUOPI r2, r3, constant + * + * LOAD_ADDR r1, address; LOAD_{BU,BS,HU,HS,W} r2, offset(r1) + * --> LOAD_ADDR r1, %hi(address+offset); + * l{bu,b,hu,h,w} r2, %lo(address+offset)(r1) + * + * LOAD_ADDR r1, address; STORE_{B,H,W} offset(r1), r2 + * --> LOAD_ADDR r1, %hi(address+offset); + * s{b,h,w} r2, %lo(address+offset)(r1) + * + * where ALUOP is one of ADD, SUB, AND, OR, XOR, SLL, SRL, SRA, ROR, SLTU, + * or SLTS. + * + * [Parameters] + * block: RTL block being translated + * unit_index: Index of current basic unit + * insn_index: Index of RTL LOAD_IMM or LOAD_ADDR instruction + * [Return value] + * Nonzero if the given RTL instruction was successfully optimized, + * else zero + */ +static int optimize_immediate(RTLBlock * const block, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + PRECOND(block->insns[insn_index].opcode == RTLOP_LOAD_IMM + || block->insns[insn_index].opcode == RTLOP_LOAD_ADDR, + return 0); + PRECOND(block->insns[insn_index].dest != 0 + && block->insns[insn_index].dest < block->next_reg, return 0); + + RTLInsn *insn = &block->insns[insn_index]; + const uint32_t imm_index = insn[0].dest; + const uintptr_t value = block->regs[imm_index].value; + + if (block->regs[imm_index].death > insn_index+1) { + /* Register is used later, so we can't optimize the load out */ + return 0; + } + + switch ((RTLOpcode)insn[1].opcode) { + + case RTLOP_ADD: + if (value + 0x8000 <= 0xFFFF) { + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ADDI; + insn[1].src_imm = value; + return 1; + } else if (insn[1].src1 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ADDI; + insn[1].src1 = insn[1].src2; + insn[1].src_imm = value; + return 1; + } + } + return 0; + + case RTLOP_SUB: + if (value + 0x7FFF <= 0xFFFF && insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ADDI; + insn[1].src_imm = -value; + return 1; + } + return 0; + + case RTLOP_AND: { + int is_insable = 0; // Can we turn it into "ins reg,$zero,..."? + if (value & 1) { + /* Must be 0b000...111 */ + is_insable = ((value & (value+1)) == 0); + } else { + /* Must be 0b111...000 */ + is_insable = ((~value & (~value+1)) == 0); + } + if (value <= 0xFFFF || is_insable) { + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ANDI; + insn[1].src_imm = value; + return 1; + } else if (insn[1].src1 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ANDI; + insn[1].src1 = insn[1].src2; + insn[1].src_imm = value; + return 1; + } + } + return 0; + } // case RTLOP_AND + + case RTLOP_OR: + if (value <= 0xFFFF) { + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ORI; + insn[1].src_imm = value; + return 1; + } else if (insn[1].src1 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_ORI; + insn[1].src1 = insn[1].src2; + insn[1].src_imm = value; + return 1; + } + } + return 0; + + case RTLOP_XOR: + if (value <= 0xFFFF) { + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_XORI; + insn[1].src_imm = value; + return 1; + } else if (insn[1].src1 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_XORI; + insn[1].src1 = insn[1].src2; + insn[1].src_imm = value; + return 1; + } + } + return 0; + + case RTLOP_SLL: + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_SLLI; + insn[1].src_imm = value; + return 1; + } + return 0; + + case RTLOP_SRL: + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_SRLI; + insn[1].src_imm = value; + return 1; + } + return 0; + + case RTLOP_SRA: + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_SRAI; + insn[1].src_imm = value; + return 1; + } + return 0; + + case RTLOP_ROR: + if (insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_RORI; + insn[1].src_imm = value; + return 1; + } + return 0; + + case RTLOP_SLTU: + if (value + 0x8000 <= 0xFFFF && insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_SLTUI; + insn[1].src_imm = value; + return 1; + } + return 0; + + case RTLOP_SLTS: + if (value + 0x8000 <= 0xFFFF && insn[1].src2 == imm_index) { + insn[0].opcode = RTLOP_NOP; + insn[0].src_imm = 0; + insn[1].opcode = RTLOP_SLTSI; + insn[1].src_imm = value; + return 1; + } + return 0; + + /*----------------------------*/ + + case RTLOP_LOAD_BU: + case RTLOP_LOAD_BS: + case RTLOP_LOAD_HU: + case RTLOP_LOAD_HS: + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + if (insn[1].src1 == imm_index) { + const uint32_t address = value + insn[1].offset; + insn[0].src_imm = (address + 0x8000) & 0xFFFF0000; + block->regs[imm_index].value = insn[0].src_imm; + insn[1].offset = (int16_t)(address & 0xFFFF); + return 1; + } + return 0; + + case RTLOP_STORE_B: + case RTLOP_STORE_H: + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + if (insn[1].dest == imm_index) { + const uint32_t address = value + insn[1].offset; + insn[0].src_imm = (address + 0x8000) & 0xFFFF0000; + block->regs[imm_index].value = insn[0].src_imm; + insn[1].offset = (int16_t)(address & 0xFFFF); + return 1; + } + return 0; + + /*----------------------------*/ + + case RTLOP_NOP: + case RTLOP_MOVE: + case RTLOP_SELECT: + case RTLOP_MULU: + case RTLOP_MULS: + case RTLOP_MADDU: + case RTLOP_MADDS: + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: + case RTLOP_NOT: + case RTLOP_CLZ: + case RTLOP_CLO: + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: + case RTLOP_ADDI: + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + case RTLOP_SLTSI: + case RTLOP_BFEXT: + case RTLOP_BFINS: + case RTLOP_LOAD_IMM: + case RTLOP_LOAD_ADDR: + case RTLOP_LOAD_PARAM: + case RTLOP_LABEL: + case RTLOP_GOTO: + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: + case RTLOP_CALL: + case RTLOP_RETURN: + case RTLOP_RETURN_TO: + return 0; + + } // switch (insn[1].opcode) + + DMSG("%p/%u: Invalid RTL opcode %u", block, insn_index, insn[1].opcode); + return 0; +} + +#endif // MIPS_OPTIMIZE_IMMEDIATE + +/*-----------------------------------------------------------------------*/ + +#ifdef RTL_TRACE_GENERATE + +/* Helper macro for print_regmap() to check the status of an instruction + * operand and update the line buffer and register map accordingly */ + +# define CHECK_REG(which) do { \ + const uint32_t __rtl_reg = insn->which; \ + if (__rtl_reg > 0 && __rtl_reg < block->next_reg \ + && block->regs[__rtl_reg].native_allocated \ + && block->regs[__rtl_reg].native_reg != MIPS_noreg \ + ) { \ + const uint32_t __mips_reg = block->regs[__rtl_reg].native_reg; \ + const unsigned int __column = columns[__mips_reg]; \ + if (!__column) { \ + break; \ + } \ + if (block->regs[__rtl_reg].birth == insn_index) { \ + block->mips.reg_map[__mips_reg] = &block->regs[__rtl_reg]; \ + unsigned int __value = __rtl_reg; \ + int __pos = 3; \ + do { \ + linebuf[__column+__pos] = '0' + (__value % 10); \ + __value /= 10; \ + __pos--; \ + } while (__value > 0 && __pos >= 0); \ + if (__pos >= 0) { \ + linebuf[__column+__pos] = 'r'; \ + } \ + print_line = 1; \ + } \ + } \ +} while (0) + +/*----------------------------------*/ + +/** + * print_regmap: Print the state of the RTL-to-MIPS register map over the + * course of the block. + * + * [Parameters] + * block: RTL block + * [Return value] + * None + */ +static void print_regmap(RTLBlock * const block) +{ + PRECOND(block != NULL, return); + + fprintf(stderr, "%p: MIPS register allocation:\n", block); + fprintf(stderr, " | v0 v1: a0 a1 a2 a3:" + " t0 t1 t2 t3: t4 t5 t6 t7: t8 t9\n"); + fprintf(stderr, "-----+--------+----------------+" + "----------------+----------------+--------\n"); + static const char template[75] = + " | : :" + " : : \n"; + static const unsigned int columns[32] = { + 0, 0, 6,10, 15,19,23,27, 32,36,40,44, 49,53,57,61, + 0, 0, 0, 0, 0, 0, 0, 0, 66,70, 0, 0, 0, 0, 0, 0, + }; + + char linebuf[75]; + memcpy(linebuf, template, sizeof(template)); + memset(block->mips.reg_map, 0, sizeof(block->mips.reg_map)); + + uint32_t insn_index; + for (insn_index = 0; insn_index < block->num_insns; insn_index++) { + const RTLInsn * const insn = &block->insns[insn_index]; + + /* Only print lines where registers are born or die */ + int print_line = 0; + + /* Fill in active registers first */ + unsigned int mips_reg; + for (mips_reg = MIPS_v0; mips_reg <= MIPS_t9; + mips_reg = (mips_reg==MIPS_t7 ? MIPS_t8 : mips_reg+1) + ) { + if (block->mips.reg_map[mips_reg] + && block->mips.reg_map[mips_reg]->death >= insn_index + ) { + unsigned int column = columns[mips_reg]; + if (column) { + if (block->mips.reg_map[mips_reg]->death == insn_index) { + block->mips.reg_map[mips_reg] = NULL; + linebuf[column+1] = '-'; + linebuf[column+2] = '-'; + linebuf[column+3] = '-'; + print_line = 1; + } else { + linebuf[column+2] = '|'; + } + } + } + } + + /* For simplicity, don't bother checking opcodes; just look at any + * register index that's in range and report it if it's become live */ + CHECK_REG(dest); + CHECK_REG(src1); + CHECK_REG(src2); + CHECK_REG(dest2); + + /* Print the line (with instruction index) if appropriate */ + if (print_line) { + unsigned int value = insn_index; + int pos = 4; + do { + linebuf[pos] = '0' + (value % 10); + value /= 10; + pos--; + } while (value > 0 && pos >= 0); + fwrite(linebuf, sizeof(linebuf), 1, stderr); + } + + /* Reset for the next line */ + memcpy(linebuf, template, sizeof(template)); + + } // for (insn_index) +} + +#undef CHECK_REG + +#endif // RTL_TRACE_GENERATE + +/*************************************************************************/ +/************************ Instruction translation ************************/ +/*************************************************************************/ + +/** + * APPEND: Append a MIPS instruction word to the native code buffer. + */ +#define APPEND(insn) do { \ + if (UNLIKELY(!append_insn(block, (insn)))) { \ + return 0; \ + } \ +} while (0) + +/** + * APPEND_FLOAT: Append a high-latency instruction (load, multiply or + * divide) to the native code buffer. If possible (and if rescheduling is + * enabled), the instruction is floated up by at most "latency" + * instructions to avoid a stall. + */ +#define APPEND_FLOAT(insn,latency) do { \ + if (UNLIKELY(!append_float(block, (insn), (latency)))) { \ + return 0; \ + } \ +} while (0) + +/** + * APPEND_BRANCH: Append a MIPS branch instruction to the native code buffer. + */ +#define APPEND_BRANCH(insn) do { \ + if (UNLIKELY(!append_branch(block, (insn)))) { \ + return 0; \ + } \ +} while (0) + +/** + * IS_SPILLED: Return nonzero if the given register (by index) is spilled, + * else zero. Assumes the variable "insn_index" is available and valid, + * and assumes that the register has been allocated a MIPS register. + * + * Under the current allocation scheme, simply returns nonzero iff the + * given register is live and does not have a hardware register allocated. + */ +#define IS_SPILLED(reg_index) \ + (block->regs[(reg_index)].birth < insn_index \ + && block->regs[(reg_index)].native_reg == MIPS_noreg) + +/** + * MAP_REGISTER: Look up the MIPS register mapped to the given RTL + * register and store it in a local variable. Pass one of the identifiers + * "dest", "src1", "src2", "dest2", or "target" as the "which" parameter; + * the MIPS register will be stored in the corresponding variable ("dest2" + * and "target" must be declared in any blocks that use them). If the RTL + * register does not have an assigned MIPS register, it is loaded into a + * temporary register. + * + * If the MIPS register must not collide with another register used by the + * same instruction, pass the RTL register index of that register as the + * "avoid" parameter (e.g. insn->src1 or insn->src2). If the register to + * be mapped is currently spilled and loading it would cause the "avoid" + * register to be spilled, it is loaded into $at and left spilled. If + * there is no register to avoid, pass 0 as the "avoid" parameter. + * + * The "reload" parameter indicates whether the register should be reloaded + * if it is not currently active. At the moment, this only applies to + * registers located on the stack. + */ +#define MAP_REGISTER(which,reload,avoid) do { \ + which = __MAP_REGISTER(block, insn->which, (reload), (avoid)); \ + if (UNLIKELY(which < 0)) { \ + return 0; \ + } \ +} while (0) +static int __MAP_REGISTER(RTLBlock * const block, const unsigned int reg_index, + const int reload, const uint32_t avoid_index) +{ + RTLRegister * const reg = &block->regs[reg_index]; + PRECOND(reg->native_allocated, return -1); + int mips_reg = reg->native_reg; + if (mips_reg == MIPS_noreg) { + mips_reg = MIPS_at; + if (block->mips.reg_map[mips_reg] == &block->regs[(avoid_index)]) { + mips_reg = MIPS_v1; + } + PRECOND(reg->frame_allocated, return -1); + if (reload + && UNLIKELY(!append_insn(block, MIPS_LW(mips_reg, reg->stack_offset, + MIPS_sp))) + ) { + return -1; + } + } + /* If it's in HI or LO, pull it out */ + if (reg->mips.is_in_hilo) { + APPEND(reg->mips.is_in_hilo | mips_reg<<11); + reg->mips.is_in_hilo = 0; + /* We don't clear the register from block->mips.{hi,lo}_reg, in + * case a subsequent madd/maddu instruction can make use of it */ + } + /* Update the register map unless we're using $zero */ + if (mips_reg != MIPS_zero) { + block->mips.reg_map[mips_reg] = reg; + } + /* Update the stack frame map if this register has a frame slot */ + if (reg->frame_allocated) { + block->mips.frame_map[reg->frame_slot] = reg; + } + return mips_reg; +} + +/** + * MAYBE_SAVE: Save the given register ("dest" or "dest2") to its frame + * slot if one is assigned. + */ +#define MAYBE_SAVE(reg) do { \ + RTLRegister * const regptr = &block->regs[insn->reg]; \ + if (regptr->frame_allocated) { \ + APPEND(MIPS_SW(reg, regptr->stack_offset, MIPS_sp)); \ + } \ +} while (0) + +/*************************************************************************/ + +/** + * translate_block: Perform the actual translation of an RTL block. + * + * [Parameters] + * block: RTL block to translate + * [Return value] + * Nonzero on success, zero on error + */ +static int translate_block(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + + int32_t i; + + /* Clear translation state */ + memset(block->mips.reg_map, 0, sizeof(block->mips.reg_map)); + block->mips.hi_reg = 0; + block->mips.lo_reg = 0; + memset(block->mips.frame_map, 0, sizeof(block->mips.frame_map)); + + /* Determine the total frame size, including space for saving and + * restoring callee-saved registers on entry/exit as well as for + * saving caller-saved registers around a CALL instruction */ + if (block->mips.need_save_ra || block->mips.frame_used) { + block->mips.total_frame_size = + MIPS_FRAME_SIZE + 4*16 // 16 caller-saved regs (not $at/$v1) + + 4; // $ra + } else { + block->mips.total_frame_size = 0; // No space needed for CALLs + } + for (i = 0; i < lenof(callee_saved_regs); i++) { + const unsigned int mips_reg = callee_saved_regs[i]; + if (block->mips.sreg_used & (1 << mips_reg)) { + block->mips.total_frame_size += 4; + } + } + block->mips.total_frame_size = + (block->mips.total_frame_size + 4) & -8; // Must be a multiple of 8 + + /* Start with the function prologue */ + if (UNLIKELY(!append_prologue(block))) { + return 0; + } + + /* Translate code a unit at a time */ + for (i = 0; i >= 0; i = block->units[i].next_unit) { + if (UNLIKELY(!translate_unit(block, i))) { + return 0; + } + } + + /* Append the function epilogue */ + if (UNLIKELY(!append_epilogue(block))) { + return 0; + } + + /* Resolve branches to labels */ + if (UNLIKELY(!resolve_branches(block))) { + return 0; + } + + /* Finished */ + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * translate_unit: Translate an RTL basic unit into MIPS code. + * + * [Parameters] + * block: RTL block being translated + * unit_index: Index of basic unit to translate + * [Return value] + * Nonzero on success, zero on error + */ +static inline int translate_unit(RTLBlock * const block, + const unsigned int unit_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + PRECOND(unit_index < block->num_units, return 0); + + RTLUnit * const unit = &block->units[unit_index]; + uint32_t i; + + /* Reset the current register map to that at the beginning of the unit + * as determined by flow analysis during register allocation */ + memcpy(block->mips.reg_map, unit->mips.reg_map, sizeof(unit->mips.reg_map)); + + /* Translate each RTL instruction in the unit */ + block->mips.unit_start = block->native_length; + i = unit->first_insn; + while ((int32_t)i <= unit->last_insn) { + const unsigned int num_processed = translate_insn(block, unit_index, i); + if (UNLIKELY(!num_processed)) { + return 0; + } + i += num_processed; + } + + /* Flush out any values cached in HI/LO for safety */ + if (UNLIKELY(!flush_hilo(block, unit->last_insn+1))) { + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * translate_insn: Translate a single RTL instruction into MIPS code. + * Optimization may result in multiple RTL instructions being parsed. + * + * [Parameters] + * block: RTL block being translated + * unit_index: Index of basic unit being translated + * insn_index: Index of RTL instruction to translate + * [Return value] + * Number of RTL instructions processed (nonzero) on success, zero on error + */ +static inline unsigned int translate_insn(RTLBlock * const block, + const unsigned int unit_index, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + const RTLInsn *insn = &block->insns[insn_index]; + int dest, src1, src2; // MIPS register indices (mapped only when needed) + + block->mips.reg_map[MIPS_at] = NULL; // For MAP_REGISTER() + + /*------------------------------*/ + + switch ((RTLOpcode)insn->opcode) { + + case RTLOP_NOP: +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + if (insn->src_imm >> 28 == 0x8 || insn->src_imm >> 28 == 0xA) { + return sh2_stealth_trace_insn(block, insn_index); + } else if (insn->src_imm >> 28 == 0xB) { + return sh2_stealth_cache_reg(block, insn_index); + } else if (insn->src_imm >> 28 == 0xC) { + return sh2_stealth_trace_store(block, insn_index); + } +#endif // RTL_TRACE_STEALTH_FOR_SH2 + if (insn->src_imm != 0) { + /* Debugging stuff from JIT_DEBUG_INSERT_PC (sh2.c) */ + const uint32_t addr = insn->src_imm; + APPEND(MIPS_LUI(MIPS_zero, addr>>16)); + APPEND(MIPS_ORI(MIPS_zero, MIPS_zero, addr & 0xFFFF)); + } + return 1; + + /*----------------------------*/ + + case RTLOP_MOVE: + if (block->regs[insn->src1].mips.is_in_hilo + && block->regs[insn->src1].death == insn_index + ) { + /* Handle this case specially--if we did it the usual way, + * we'd waste a cycle with MFHI/MFLO to src1 followed by a + * MOVE from src1 to dest. */ + MAP_REGISTER(dest, 0, 0); + APPEND(block->regs[insn->src1].mips.is_in_hilo | dest<<11); + block->regs[insn->src1].mips.is_in_hilo = 0; + } else { + MAP_REGISTER(src1, 1, 0); + /* Note: we have to map the output operand _after_ all the input + * operands, or else the output operand will get spilled if it + * shares a hardware register with an input operand. (This isn't + * actually true with the current allocation scheme, since we never + * share hardware registers between multiple live RTL registers, + * but it's applicable in general, and it doesn't hurt anything to + * do it this way.) */ + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_MOVE(dest, src1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SELECT: { + int cond; + int spilled_v1 = 0; + if (block->regs[insn->src1].native_reg != MIPS_noreg) { + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(src2, 1, insn->cond); + MAP_REGISTER(cond, 1, insn->src2); + } else if (block->regs[insn->src2].native_reg != MIPS_noreg) { + MAP_REGISTER(src1, 1, insn->cond); + MAP_REGISTER(src2, 1, 0); + MAP_REGISTER(cond, 1, insn->src1); + } else if (block->regs[insn->cond].native_reg != MIPS_noreg) { + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + MAP_REGISTER(cond, 1, 0); + } else { + DMSG("%p/%u: WARNING: spilling $v1 to reload all 3 SELECT" + " operands", block, insn_index); + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, -8)); + APPEND(MIPS_SW(MIPS_v1, 0, MIPS_sp)); + APPEND(MIPS_LW(MIPS_v1, + 8 + block->regs[insn->cond].stack_offset, MIPS_sp)); + cond = MIPS_v1; + spilled_v1 = 1; + } + MAP_REGISTER(dest, 0, 0); + if (dest == src1) { + APPEND(MIPS_MOVZ(dest, src2, cond)); + } else if (dest == src2) { + APPEND(MIPS_MOVN(dest, src1, cond)); + } else if (dest == cond) { + DMSG("%p/%u: WARNING: dest==cond ($%u) for SELECT, using slow" + " register swap", block, insn_index, dest); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, -8)); + APPEND(MIPS_SW(cond, 0, MIPS_sp)); + APPEND(MIPS_SW(src1, 4, MIPS_sp)); + APPEND(MIPS_MOVE(dest, src1)); + APPEND(MIPS_LW(src1, 0, MIPS_sp)); + APPEND(MIPS_MOVZ(dest, src2, src1)); + APPEND(MIPS_LW(src1, 4, MIPS_sp)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + } else { + APPEND(MIPS_MOVE(dest, src1)); + APPEND(MIPS_MOVZ(dest, src2, cond)); + } + if (spilled_v1) { + APPEND(MIPS_LW(MIPS_v1, 0, MIPS_sp)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + } + MAYBE_SAVE(dest); + return 1; + } + + case RTLOP_ADD: + return append_alu_reg(block, insn, MIPS_ADDU(0, 0, 0)); + + case RTLOP_SUB: + return append_alu_reg(block, insn, MIPS_SUBU(0, 0, 0)); + + case RTLOP_MULU: + return append_mult_div(block, insn, MIPS_MULTU(0, 0), 0, insn_index); + + case RTLOP_MULS: + return append_mult_div(block, insn, MIPS_MULT(0, 0), 0, insn_index); + + case RTLOP_MADDU: + return append_mult_div(block, insn, MIPS_MADDU(0, 0), 1, insn_index); + + case RTLOP_MADDS: + return append_mult_div(block, insn, MIPS_MADD(0, 0), 1, insn_index); + + case RTLOP_DIVMODU: + return append_mult_div(block, insn, MIPS_DIVU(0, 0), 0, insn_index); + + case RTLOP_DIVMODS: + return append_mult_div(block, insn, MIPS_DIV(0, 0), 0, insn_index); + + case RTLOP_AND: + return append_alu_reg(block, insn, MIPS_AND(0, 0, 0)); + + case RTLOP_OR: + return append_alu_reg(block, insn, MIPS_OR(0, 0, 0)); + + case RTLOP_XOR: + return append_alu_reg(block, insn, MIPS_XOR(0, 0, 0)); + + case RTLOP_NOT: + return append_alu_1op(block, insn, MIPS_NOR(0, 0, MIPS_zero)); + + case RTLOP_SLL: + return append_shift_reg(block, insn, MIPS_SLLV(0, 0, 0)); + + case RTLOP_SRL: + return append_shift_reg(block, insn, MIPS_SRLV(0, 0, 0)); + + case RTLOP_SRA: + return append_shift_reg(block, insn, MIPS_SRAV(0, 0, 0)); + + case RTLOP_ROR: + return append_shift_reg(block, insn, MIPS_RORV(0, 0, 0)); + + case RTLOP_CLZ: + return append_alu_1op(block, insn, MIPS_CLZ(0, 0)); + + case RTLOP_CLO: + return append_alu_1op(block, insn, MIPS_CLZ(0, 0)); + + case RTLOP_SLTU: + return append_alu_reg(block, insn, MIPS_SLTU(0, 0, 0)); + + case RTLOP_SLTS: +#ifdef MIPS_OPTIMIZE_MIN_MAX + { + const int num_insns = optimize_min_max(block, insn_index); + if (num_insns) { + return num_insns; + } + } +#endif + return append_alu_reg(block, insn, MIPS_SLT(0, 0, 0)); + + case RTLOP_BSWAPH: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_WSBH(dest, src1)); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_BSWAPW: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_WSBW(dest, src1)); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_HSWAPW: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_ROR(dest, src1, 16)); + MAYBE_SAVE(dest); + return 1; + + /*----------------------------*/ + + case RTLOP_ADDI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm + 0x8000 < 0x10000) { + APPEND(MIPS_ADDIU(dest, src1, insn->src_imm & 0xFFFF)); + } else { + if (insn->src_imm < 0x10000) { + APPEND(MIPS_ORI(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_ADDU(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_ANDI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 0x10000) { + APPEND(MIPS_ANDI(dest, src1, insn->src_imm & 0xFFFF)); + } else if (insn->src_imm == 0xFFFFFFFF) { + if (dest != src1) { + APPEND(MIPS_MOVE(dest, src1)); + } + } else if (((insn->src_imm & 1) + && !(insn->src_imm & (insn->src_imm+1))) + || (!(insn->src_imm & 1) + && !(~insn->src_imm & (~insn->src_imm+1))) + ) { + unsigned int start, count; + if (insn->src_imm & 1) { + count = __builtin_clz(insn->src_imm); + start = 32 - count; + } else { + count = 32 - __builtin_allegrex_clo(insn->src_imm); + start = 0; + } + if (dest != src1) { + APPEND(MIPS_MOVE(dest, src1)); + } + APPEND(MIPS_INS(dest, MIPS_zero, start, count)); + } else { + if (insn->src_imm >= 0xFFFF8000) { + APPEND(MIPS_ADDIU(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_AND(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_ORI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 0x10000) { + APPEND(MIPS_ORI(dest, src1, insn->src_imm & 0xFFFF)); + } else { + if (insn->src_imm >= 0xFFFF8000) { + APPEND(MIPS_ADDIU(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_OR(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_XORI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 0x10000) { + APPEND(MIPS_XORI(dest, src1, insn->src_imm & 0xFFFF)); + } else { + if (insn->src_imm >= 0xFFFF8000) { + APPEND(MIPS_ADDIU(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_XOR(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SLLI: + MAP_REGISTER(src1, 1, 0); +#ifdef MIPS_OPTIMIZE_SEX + if ((insn->src_imm == 16 || insn->src_imm == 24) + && block->regs[insn->dest].death == insn_index+1 + && insn[1].opcode == RTLOP_SRAI + && insn[1].src1 == insn->dest + && insn[1].src_imm == insn->src_imm + ) { + insn++; + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm == 16) { + APPEND(MIPS_SEH(dest, src1)); + } else { + APPEND(MIPS_SEB(dest, src1)); + } + MAYBE_SAVE(dest); + return 2; + } +#endif + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 32) { + APPEND(MIPS_SLL(dest, src1, insn->src_imm)); + } else { + APPEND(MIPS_MOVE(dest, MIPS_zero)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SRLI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 32) { + APPEND(MIPS_SRL(dest, src1, insn->src_imm)); + } else { + APPEND(MIPS_MOVE(dest, MIPS_zero)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SRAI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm < 32) { + APPEND(MIPS_SRA(dest, src1, insn->src_imm)); + } else { + APPEND(MIPS_SRA(dest, src1, 31)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_RORI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_ROR(dest, src1, insn->src_imm & 31)); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SLTUI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm + 0x8000 < 0x10000) { + APPEND(MIPS_SLTIU(dest, src1, insn->src_imm & 0xFFFF)); + } else { + if (insn->src_imm < 0x10000) { + APPEND(MIPS_ORI(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_SLTU(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + case RTLOP_SLTSI: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + if (insn->src_imm + 0x8000 < 0x10000) { + APPEND(MIPS_SLTI(dest, src1, insn->src_imm & 0xFFFF)); + } else { + if (insn->src_imm < 0x10000) { + APPEND(MIPS_ORI(MIPS_v1, MIPS_zero, insn->src_imm & 0xFFFF)); + } else { + APPEND(MIPS_LUI(MIPS_v1, insn->src_imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, insn->src_imm & 0xFFFF)); + } + APPEND(MIPS_SLT(dest, src1, MIPS_v1)); + } + MAYBE_SAVE(dest); + return 1; + + /*----------------------------*/ + + case RTLOP_BFEXT: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_EXT(dest, src1, + insn->bitfield.start, insn->bitfield.count)); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_BFINS: + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(dest, 0, 0); + if (dest != src1) { + APPEND(MIPS_MOVE(dest, src1)); + } + MAP_REGISTER(src2, 1, insn->dest); + APPEND(MIPS_INS(dest, src2, + insn->bitfield.start, insn->bitfield.count)); + MAYBE_SAVE(dest); + return 1; + + /*----------------------------*/ + + case RTLOP_LOAD_IMM: { + const uint32_t value = insn->src_imm; + MAP_REGISTER(dest, 0, 0); + if (value + 0x8000 < 0x10000) { + APPEND(MIPS_ADDIU(dest, MIPS_zero, value)); + } else if (value < 0x10000) { + APPEND(MIPS_ORI(dest, MIPS_zero, value)); + } else { + APPEND(MIPS_LUI(dest, value>>16)); + if (value & 0xFFFF) { + APPEND(MIPS_ORI(dest, dest, value & 0xFFFF)); + } + } + MAYBE_SAVE(dest); + return 1; + } + + case RTLOP_LOAD_ADDR: { + const uint32_t value = insn->src_addr; +#ifdef MIPS_OPTIMIZE_ABSOLUTE_CALL + if (block->regs[insn->dest].source == RTLREG_CONSTANT + && block->regs[insn->dest].birth == insn_index + && block->regs[insn->dest].death == insn_index+1 + && insn[1].opcode == RTLOP_CALL + && insn[1].target == insn->dest + ) { + /* Just skip the instruction; CALL will detect the constant and + * handle it properly */ + return 1; + } +#endif + MAP_REGISTER(dest, 0, 0); + if (value + 0x8000 < 0x10000) { + APPEND(MIPS_ADDIU(dest, MIPS_zero, value)); + } else if (value < 0x10000) { + APPEND(MIPS_ORI(dest, MIPS_zero, value)); + } else { + APPEND(MIPS_LUI(dest, value>>16)); + if (value & 0xFFFF) { + APPEND(MIPS_ORI(dest, dest, value & 0xFFFF)); + } + } + MAYBE_SAVE(dest); + return 1; + } + + case RTLOP_LOAD_PARAM: { + const uint32_t param_index = insn->src_imm; + if (UNLIKELY(param_index > 8)) { + DMSG("Only 8 function parameters supported, index %u is invalid", + param_index); + return 0; + } + const unsigned int param_reg = MIPS_a0 + param_index; + MAP_REGISTER(dest, 0, 0); + if (dest != param_reg) { + APPEND(MIPS_MOVE(dest, param_reg)); + } + MAYBE_SAVE(dest); + return 1; + } + + case RTLOP_LOAD_BU: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND_FLOAT(MIPS_LBU(dest, insn->offset, src1), 3); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_LOAD_BS: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND_FLOAT(MIPS_LB(dest, insn->offset, src1), 3); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_LOAD_HU: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND_FLOAT(MIPS_LHU(dest, insn->offset, src1), 3); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_LOAD_HS: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND_FLOAT(MIPS_LH(dest, insn->offset, src1), 3); + MAYBE_SAVE(dest); + return 1; + + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND_FLOAT(MIPS_LW(dest, insn->offset, src1), 3); + MAYBE_SAVE(dest); + return 1; + + /*----------------------------*/ + + case RTLOP_STORE_B: + /* "dest" is actually a source operand here */ + MAP_REGISTER(src1, 1, insn->dest); + MAP_REGISTER(dest, 1, insn->src1); + APPEND(MIPS_SB(src1, insn->offset, dest)); + return 1; + + case RTLOP_STORE_H: + MAP_REGISTER(src1, 1, insn->dest); + MAP_REGISTER(dest, 1, insn->src1); + APPEND(MIPS_SH(src1, insn->offset, dest)); + return 1; + + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + MAP_REGISTER(src1, 1, insn->dest); + MAP_REGISTER(dest, 1, insn->src1); + APPEND(MIPS_SW(src1, insn->offset, dest)); + return 1; + + /*----------------------------*/ + + case RTLOP_LABEL: + block->label_offsets[insn->label] = block->native_length; + return 1; + + case RTLOP_GOTO: + /* A branch to the following unit is in effect a no-op, so just + * skip it entirely */ + if (block->label_unitmap[insn->label] == block->units[unit_index].next_unit) { + return 1; + } + /* Flush any cached values out of HI or LO */ + if (UNLIKELY(!flush_hilo(block, insn_index))) { + return 0; + } + /* Fill in a fake opcode for now; this will be replaced later with + * a real branch instruction once the offset is known */ + APPEND_BRANCH(MIPS_B_LABEL(insn->label)); + return 1; + + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: { + if (block->label_unitmap[insn->label] == block->units[unit_index].next_unit) { + return 1; + } + MAP_REGISTER(src1, 1, 0); + /* Make sure it doesn't collide with anything we're about to + * reload, and move to $at if so */ + unsigned int target_unit = block->label_unitmap[insn->label]; + if (block->units[target_unit].first_insn < insn_index) { + RTLRegister * const target_reg = + block->units[target_unit].mips.reg_map[src1]; + if (target_reg && target_reg != &block->regs[insn->src1] + && target_reg->death >= insn_index + ) { + APPEND(MIPS_MOVE(MIPS_at, src1)); + src1 = MIPS_at; + } + } + if (UNLIKELY(!flush_hilo(block, insn_index))) { + return 0; + } + if (insn->opcode == RTLOP_GOTO_IF_Z) { + APPEND_BRANCH(MIPS_BEQZ_LABEL(src1, insn->label)); + } else { + APPEND_BRANCH(MIPS_BNEZ_LABEL(src1, insn->label)); + } + return 1; + } // case RTLOP_GOTO_IF_{Z,NZ} + + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: { + if (block->label_unitmap[insn->label] == block->units[unit_index].next_unit) { + return 1; + } + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + unsigned int target_unit = block->label_unitmap[insn->label]; + if (block->units[target_unit].first_insn < insn_index) { + RTLRegister * const target_reg1 = + block->units[target_unit].mips.reg_map[src1]; + if (target_reg1 && target_reg1 != &block->regs[insn->src1] + && target_reg1->death >= insn_index + ) { + if (src2 == MIPS_at) { + APPEND(MIPS_MOVE(MIPS_v1, src1)); + src1 = MIPS_v1; + } else { + APPEND(MIPS_MOVE(MIPS_at, src1)); + src1 = MIPS_at; + } + } + RTLRegister * const target_reg2 = + block->units[target_unit].mips.reg_map[src2]; + if (target_reg2 && target_reg2 != &block->regs[insn->src2] + && target_reg2->death >= insn_index + ) { + if (src1 == MIPS_at) { + APPEND(MIPS_MOVE(MIPS_v1, src2)); + src2 = MIPS_v1; + } else { + APPEND(MIPS_MOVE(MIPS_at, src2)); + src2 = MIPS_at; + } + } + } + if (UNLIKELY(!flush_hilo(block, insn_index))) { + return 0; + } + if (insn->opcode == RTLOP_GOTO_IF_E) { + APPEND_BRANCH(MIPS_BEQ_LABEL(src1, src2, insn->label)); + } else { + APPEND_BRANCH(MIPS_BNE_LABEL(src1, src2, insn->label)); + } + return 1; + } // case RTLOP_GOTO_IF_{E,NE} + + /*----------------------------*/ + + case RTLOP_CALL: { + + /* Map the argument registers, if any */ + src1 = src2 = 0; // Avoid a compiler warning (not actually needed) + if (insn->src1) { + MAP_REGISTER(src1, 1, insn->src2); + if (insn->src2) { + MAP_REGISTER(src2, 1, insn->src1); + } + } + + /* Flush any cached values out of HI or LO */ + if (UNLIKELY(!flush_hilo(block, insn_index))) { + return 0; + } + + /* Flush all live caller-saved registers to the stack */ + if (UNLIKELY(!flush_for_call(block, insn_index))) { + return 0; + } + + /* Map the function address, if necessary */ + int target; +#ifdef MIPS_OPTIMIZE_ABSOLUTE_CALL + if (block->regs[insn->target].source == RTLREG_CONSTANT) { + target = -1; + } else { +#endif + /* Need to avoid collision with both src1 and src2; the + * MAP_REGISTER macro can't handle avoiding two registers at + * once, so we do it manually here, taking advantage of the + * registers reed up by flush_for_call(). This won't actually + * occur in practice as long as MIPS_OPTIMIZE_ABSOLUTE_CALL is + * enabled, since all the calls we make are to known addresses. */ + RTLRegister * const target_reg = &block->regs[insn->target]; + if (target_reg->native_reg == MIPS_noreg + && (src1 == MIPS_v1 || src2 == MIPS_v1) + ) { + PRECOND(target_reg->frame_allocated, return 0); + if (src1 == MIPS_v1) { // Impossible, but just for completeness + if (src2 == MIPS_a0) { + APPEND(MIPS_MOVE(MIPS_a2, src1)); + src1 = MIPS_a2; + } else { + APPEND(MIPS_MOVE(MIPS_a2, src1)); + src1 = MIPS_a2; + } + } else if (src2 == MIPS_v1) { + if (src1 == MIPS_a1) { + APPEND(MIPS_MOVE(MIPS_a2, src2)); + src2 = MIPS_a2; + } else { + APPEND(MIPS_MOVE(MIPS_a1, src2)); + src2 = MIPS_a1; + } + } + APPEND(MIPS_LW(MIPS_v1, target_reg->stack_offset, MIPS_sp)); + target = MIPS_v1; + } else { // No collision + MAP_REGISTER(target, 1, 0); + } +#ifdef MIPS_OPTIMIZE_ABSOLUTE_CALL + } +#endif + + /* Move any parameters to $a0-$a1 */ + if (insn->src1) { + if (insn->src2) { + if (src1 == MIPS_a1) { + if (src2 == MIPS_a0) { + /* Arguments are reversed, so use $at as a + * temporary to exchange them */ + APPEND(MIPS_MOVE(MIPS_at, MIPS_a1)); + src1 = MIPS_at; + } else { + APPEND(MIPS_MOVE(MIPS_a0, MIPS_a1)); + src1 = MIPS_a0; + } + } + if (src2 != MIPS_a1) { + APPEND(MIPS_MOVE(MIPS_a1, src2)); + } + } + if (src1 != MIPS_a0) { + APPEND(MIPS_MOVE(MIPS_a0, src1)); + } + } + + /* Actually call the routine */ +#ifdef MIPS_OPTIMIZE_ABSOLUTE_CALL + if (block->regs[insn->target].source == RTLREG_CONSTANT) { + const uint32_t address = block->regs[insn->target].value; + APPEND_BRANCH(MIPS_JAL((address & 0x0FFFFFFC) >> 2)); + } else { +#endif + APPEND_BRANCH(MIPS_JALR(MIPS_t9)); +#ifdef MIPS_OPTIMIZE_ABSOLUTE_CALL + } +#endif + + /* Copy the return value to the target register, if there is one */ + if (insn->dest) { + MAP_REGISTER(dest, 0, 0); + if (dest != MIPS_v0) { + APPEND(MIPS_MOVE(dest, MIPS_v0)); + } + MAYBE_SAVE(dest); + } + + /* Reload all flushed registers */ + if (UNLIKELY(!reload_after_call(block, insn_index))) { + return 0; + } + + return 1; + } // case RTLOP_CALL + + case RTLOP_RETURN: + if (insn_index == block->units[unit_index].last_insn + && block->units[unit_index].next_unit == -1 + ) { + /* This is the last instruction, so we can just fall through */ + } else { + APPEND_BRANCH(MIPS_B_EPILOGUE_RET_CONST); + } + return 1; + + case RTLOP_RETURN_TO: { + int target; + MAP_REGISTER(target, 1, 0); + if (target != MIPS_at) { + APPEND(MIPS_MOVE(MIPS_at, target)); + } + /* Don't forget to reload parameters. (Since we only use this for + * predicted static branches to other native code, we know that we + * can just pass the first parameter, i.e. the state block pointer, + * along; for a more general implementation, we'd treat this like + * CALL and take parameters.) */ + if (UNLIKELY(block->regs[1].source != RTLREG_PARAMETER) + || UNLIKELY(block->regs[1].param_index != 0) + ) { + DMSG("r1 is not state block, can't handle"); + return 0; + } + if (block->regs[1].native_reg != MIPS_a0) { + APPEND(MIPS_MOVE(MIPS_a0, block->regs[1].native_reg)); + } + APPEND_BRANCH(MIPS_B_EPILOGUE_CHAIN_AT); + block->mips.need_chain_at = 1; + return 1; + } // case RTLOP_RETURN_TO + + } // switch (insn->opcode) + + /*------------------------------*/ + + DMSG("%p/%u: Invalid RTL opcode %u", block, insn_index, insn->opcode); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * append_alu_1op: Append a 1-register-operand ALU instruction to the + * instruction stream. + * + * [Parameters] + * block: RTL block being translated + * insn: RTL instruction being translated + * opcode: Instruction word to add (with rs/rd set to zero) + * [Return value] + * 1 on success, 0 on error + */ +static int append_alu_1op(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode) +{ + PRECOND(block != NULL, return 0); + + int src1, dest; + MAP_REGISTER(src1, 1, 0); + MAP_REGISTER(dest, 0, 0); + APPEND(opcode | src1<<21 | dest<<11); + MAYBE_SAVE(dest); + return 1; +} + +/*----------------------------------*/ + +/** + * append_alu_reg: Append a 2-register-operand ALU instruction (other than + * a shift insturction) to the instruction stream. + * + * [Parameters] + * block: RTL block being translated + * insn: RTL instruction being translated + * opcode: Instruction word to add (with rs/rt/rd set to zero) + * [Return value] + * 1 on success, 0 on error + */ +static int append_alu_reg(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode) +{ + PRECOND(block != NULL, return 0); + + int src1, src2, dest; + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + MAP_REGISTER(dest, 0, 0); + APPEND(opcode | src1<<21 | src2<<16 | dest<<11); + MAYBE_SAVE(dest); + return 1; +} + +/*----------------------------------*/ + +/** + * append_shift_reg: Append a variable-count shift instruction to the + * instruction stream. + * + * [Parameters] + * block: RTL block being translated + * insn: RTL instruction being translated + * opcode: Instruction word to add (with rs/rt/rd set to zero) + * [Return value] + * 1 on success, 0 on error + */ +static int append_shift_reg(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode) +{ + PRECOND(block != NULL, return 0); + + int src1, src2, dest; + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + MAP_REGISTER(dest, 0, 0); + /* Register order is reversed from regular ALU insns: SLLV rd,rt,rs + * (as opposed to ADDU rd,rs,rt) */ + APPEND(opcode | src2<<21 | src1<<16 | dest<<11); + MAYBE_SAVE(dest); + return 1; +} + +/*----------------------------------*/ + +/** + * append_mult_div: Append a multiply or divide instruction to the + * instruction stream. + * + * [Parameters] + * block: RTL block being translated + * insn: RTL instruction being translated + * opcode: Instruction word to add (with rs/rt set to zero) + * accumulate: Nonzero if this is an accumulating instruction (madd/maddu) + * insn_index: Index of instruction being translated + * [Return value] + * 1 on success, 0 on error + */ +static int append_mult_div(RTLBlock * const block, const RTLInsn * const insn, + const uint32_t opcode, const int accumulate, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + + if (accumulate) { + /* If the proper registers are already in HI and LO, we don't need + * to do anything here */ + if (block->mips.hi_reg != insn->dest2 + || block->mips.lo_reg != insn->dest + ) { + /* Need to reload HI and/or LO; don't bother with the case + * where one of the two is correctly loaded, since normally + * both will be preloaded properly */ + flush_hilo(block, insn_index); + int dest2; + MAP_REGISTER(dest2, 1, 0); + APPEND(MIPS_MTHI(dest2)); + int dest; + MAP_REGISTER(dest, 1, 0); + APPEND(MIPS_MTLO(dest)); + } + } else { + /* Make sure anything currently in HI/LO is flushed out before we + * overwrite them */ + flush_hilo(block, insn_index); + } + + int src1, src2; + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + const int float_distance = ((opcode & 0x3E) == __SP_DIV) ? 35 : 6; + APPEND_FLOAT(opcode | src1<<21 | src2<<16, float_distance); + if (insn->dest) { + if (block->regs[insn->dest].frame_allocated) { + /* We have to save it to the stack, so it can't stay in LO */ + int dest; + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_MFLO(dest)); + MAYBE_SAVE(dest); + } else { + /* Leave it in LO for now; we'll extract it when we need it */ + block->regs[insn->dest].mips.is_in_hilo = MIPS_MFLO(0); + block->mips.lo_reg = insn->dest; + } + } + if (insn->dest2) { + if (block->regs[insn->dest2].frame_allocated) { + int dest2; + MAP_REGISTER(dest2, 0, 0); + APPEND(MIPS_MFHI(dest2)); + MAYBE_SAVE(dest2); + } else { + block->regs[insn->dest2].mips.is_in_hilo = MIPS_MFHI(0); + block->mips.hi_reg = insn->dest2; + } + } + return 1; +} + +/*************************************************************************/ + +/** + * flush_for_call: Flush all caller-saved registers to the stack frame in + * preparation for a subroutine call. Registers which are born on or die + * with the current instruction are not saved; the destination register is + * also not saved (normally the destination register will be born with this + * instruction, but if the code is not SSA-form, it may already be live). + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of instruction being translated + * [Return value] + * Nonzero on success, zero on error + */ +static int flush_for_call(RTLBlock * const block, const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + const uint32_t dest = block->insns[insn_index].dest; + + unsigned int push_offset = MIPS_FRAME_SIZE; + unsigned int i; + for (i = 0; i < lenof(caller_saved_regs); i++) { + const unsigned int mips_reg = caller_saved_regs[i]; + RTLRegister * const reg = block->mips.reg_map[mips_reg]; + if (reg + && reg->birth < insn_index + && reg->death > insn_index + && !(dest && reg == &block->regs[dest]) + && reg->source != RTLREG_CONSTANT + ) { + APPEND(MIPS_SW(mips_reg, push_offset, MIPS_sp)); + push_offset += 4; + } + } + + return 1; +} + +/*----------------------------------*/ + +/** + * reload_after_call: Reload necessary caller-saved registers after a + * subroutine call. + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of instruction being translated + * [Return value] + * Nonzero on success, zero on error + */ +static int reload_after_call(RTLBlock * const block, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + const uint32_t dest = block->insns[insn_index].dest; + + unsigned int pop_offset = MIPS_FRAME_SIZE; + unsigned int i; + for (i = 0; i < lenof(caller_saved_regs); i++) { + const unsigned int mips_reg = caller_saved_regs[i]; + RTLRegister * const reg = block->mips.reg_map[mips_reg]; + if (reg + && reg->birth < insn_index + && reg->death > insn_index + && !(dest && reg == &block->regs[dest]) + ) { + if (reg->source == RTLREG_CONSTANT) { + const uint32_t value = reg->value; + if (value + 0x8000 < 0x10000) { + APPEND(MIPS_ADDIU(mips_reg, MIPS_zero, value)); + } else if (value < 0x10000) { + APPEND(MIPS_ORI(mips_reg, MIPS_zero, value)); + } else { + APPEND(MIPS_LUI(mips_reg, value>>16)); + if (value & 0xFFFF) { + APPEND(MIPS_ORI(mips_reg, mips_reg, value & 0xFFFF)); + } + } + } else { + APPEND(MIPS_LW(mips_reg, pop_offset, MIPS_sp)); + pop_offset += 4; + } + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * flush_hilo: Flush any values cached in the MIPS HI or LO registers to + * the appropriate general-purpose register. + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of RTL instruction being translated (if between + * instructions, next instruction to be translated) + * [Return value] + * Nonzero on success, zero on error + */ +static int flush_hilo(RTLBlock * const block, const uint32_t insn_index) +{ + if (block->mips.hi_reg) { + RTLRegister * const reg = &block->regs[block->mips.hi_reg]; + /* If the register was already updated, its is_in_hilo field will + * be zero, so we don't need to update the register ourselves in + * that case; we just clear the register reference from hi_reg. + * Also make sure that we don't try to update a register that's + * already dead. */ + if (reg->mips.is_in_hilo && reg->death >= insn_index) { + PRECOND(reg->mips.is_in_hilo == MIPS_MFHI(0), return 0); + PRECOND(reg->native_allocated, return 0); + PRECOND(reg->native_reg != MIPS_noreg, return 0); + PRECOND(reg->native_reg != MIPS_zero, return 0); + APPEND(reg->mips.is_in_hilo | reg->native_reg<<11); + reg->mips.is_in_hilo = 0; + block->mips.reg_map[reg->native_reg] = reg; + } + block->mips.hi_reg = 0; + } + if (block->mips.lo_reg) { + RTLRegister * const reg = &block->regs[block->mips.lo_reg]; + if (reg->mips.is_in_hilo && reg->death >= insn_index) { + PRECOND(reg->mips.is_in_hilo == MIPS_MFLO(0), return 0); + PRECOND(reg->native_allocated, return 0); + PRECOND(reg->native_reg != MIPS_noreg, return 0); + PRECOND(reg->native_reg != MIPS_zero, return 0); + APPEND(reg->mips.is_in_hilo | reg->native_reg<<11); + reg->mips.is_in_hilo = 0; + block->mips.reg_map[reg->native_reg] = reg; + } + block->mips.lo_reg = 0; + } + return 1; +} + +/*************************************************************************/ + +#ifdef MIPS_OPTIMIZE_MIN_MAX + +/** + * optimize_min_max: Attempt to optimize an SLTS instruction followed by a + * SELECT instruction into a MIPS MIN or MAX instruction. + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of RTL instruction to optimize (must be SLTS) + * [Return value] + * Number of RTL instructions processed (nonzero) if the given RTL + * instruction was successfully optimized into a MIPS MIN or MAX + * instruction, else zero + */ +static int optimize_min_max(RTLBlock * const block, const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + PRECOND(block->insns[insn_index].opcode == RTLOP_SLTS, return 0); + + const RTLInsn *insn = &block->insns[insn_index]; + + if (block->regs[insn->dest].death != insn_index+1 + || insn[1].opcode != RTLOP_SELECT + || insn[1].cond != insn->dest + ) { + return 0; + } else if (insn[1].src1 == insn->src1 && insn[1].src2 == insn->src2) { + insn++; + int src1, src2, dest; + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_MIN(dest, src1, src2)); + return 2; + } else if (insn[1].src1 == insn->src2 && insn[1].src2 == insn->src1) { + insn++; + int src1, src2, dest; + MAP_REGISTER(src1, 1, insn->src2); + MAP_REGISTER(src2, 1, insn->src1); + MAP_REGISTER(dest, 0, 0); + APPEND(MIPS_MAX(dest, src1, src2)); + return 2; + } else { + return 0; + } +} + +#endif // MIPS_OPTIMIZE_MIN_MAX + +/*************************************************************************/ + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + +/*----------------------------------*/ + +/** + * sh2_stealth_trace_insn: Add MIPS code to trace an instruction for SH-2 + * TRACE_STEALTH mode. + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of RTL instruction to translate + * [Return value] + * Number of RTL instructions processed (nonzero) on success, zero on error + */ +static unsigned int sh2_stealth_trace_insn(RTLBlock * const block, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + if (block->regs[1].source != RTLREG_PARAMETER + || block->regs[1].param_index != 0 + ) { + DMSG("r1 is not state block, can't handle"); + return 0; + } + + const RTLInsn *insn = &block->insns[insn_index]; + unsigned int num_insns = 1; // Number of RTL instructions consumed + unsigned int cond_reg = 0; + unsigned int test_bit = 0; + uint32_t op = insn[num_insns++].src_imm; + if (op>>24 == 0x9F) { // Conditional execution + cond_reg = op & 0xFFFF; + test_bit = op>>16 & 1; + op = insn[num_insns++].src_imm; + } + uint32_t cached_cycles = op; + PRECOND(cached_cycles>>24 == 0x98, return 1); + cached_cycles &= 0xFFFF; + uint32_t cached_cycle_reg = insn[num_insns++].src_imm; + PRECOND(cached_cycles>>24 == 0x90, return 1); + cached_cycle_reg &= 0xFFFF; + + /* First save all registers; load the state block pointer into $at in + * the delay slot */ + APPEND(MIPS_JAL(((uintptr_t)code_save_regs_state & 0x0FFFFFFC) >> 2)); + if (IS_SPILLED(1)) { + APPEND(MIPS_LW(MIPS_at, block->regs[1].stack_offset, MIPS_sp)); + } else { + APPEND(MIPS_MOVE(MIPS_at, block->regs[1].native_reg)); + } + + /* Update the state block with cached register values */ + uint32_t mask = block->sh2_regcache_mask; + if (mask) { + const uintptr_t cache_ptr = (uintptr_t)sh2_regcache; + APPEND(MIPS_LUI(MIPS_a0, cache_ptr >> 16)); + APPEND(MIPS_ORI(MIPS_a0, MIPS_a0, cache_ptr & 0xFFFF)); + } + unsigned int sh2_reg; + for (sh2_reg = 0; mask != 0; mask >>= 1, sh2_reg++) { + if (mask & 1) { + APPEND(MIPS_LW(MIPS_v1, 4*sh2_reg, MIPS_a0)); + APPEND(MIPS_SW(MIPS_v1, 4*sh2_reg, MIPS_at)); + } + } + + /* Update the state block's cached cycle count */ + if (cached_cycle_reg) { + RTLRegister * const reg = &block->regs[cached_cycle_reg]; + if (IS_SPILLED(cached_cycle_reg)) { + APPEND(MIPS_LW(MIPS_v1, 4*(18+24) + reg->stack_offset, MIPS_sp)); + } else if (reg->native_reg >= MIPS_v0 && reg->native_reg <= MIPS_t7) { + APPEND(MIPS_LW(MIPS_v1, 4*(reg->native_reg - MIPS_v0), MIPS_sp)); + } else if (reg->native_reg >= MIPS_t8 && reg->native_reg <= MIPS_t9) { + APPEND(MIPS_LW(MIPS_v1, + 4*(reg->native_reg - MIPS_t8 + 14), MIPS_sp)); + } else { + APPEND(MIPS_MOVE(MIPS_v1, reg->native_reg)); + } + } + APPEND(MIPS_ADDI(MIPS_v1, MIPS_v1, cached_cycles)); + APPEND(MIPS_SW(MIPS_v1, offsetof(SH2State,cycles), MIPS_at)); + + /* Call the trace routine (if the instruction isn't nulled out) */ + if (cond_reg) { + if (IS_SPILLED(cond_reg)) { + APPEND(MIPS_LW(MIPS_v1, 4*(18+24) + block->regs[cond_reg].stack_offset, MIPS_sp)); + } else if (block->regs[cond_reg].native_reg >= MIPS_v0 + && block->regs[cond_reg].native_reg <= MIPS_t7) { + APPEND(MIPS_LW(MIPS_v1, 4*(block->regs[cond_reg].native_reg - MIPS_v0), MIPS_sp)); + } else if (block->regs[cond_reg].native_reg >= MIPS_t8 + && block->regs[cond_reg].native_reg <= MIPS_t9) { + APPEND(MIPS_LW(MIPS_v1, 4*(block->regs[cond_reg].native_reg - MIPS_t8 + 14), MIPS_sp)); + } else { + APPEND(MIPS_MOVE(MIPS_v1, block->regs[cond_reg].native_reg)); + } + APPEND(MIPS_XORI(MIPS_v1, MIPS_v1, test_bit)); + APPEND(MIPS_BNEZ(MIPS_v1, 4)); + } + APPEND(MIPS_LUI(MIPS_a1, (insn->src_imm & 0x7FFF0000) >> 16)); + APPEND(MIPS_ORI(MIPS_a1, MIPS_a1, insn->src_imm & 0xFFFF)); + APPEND(MIPS_JAL(((uintptr_t)trace_insn_callback & 0x0FFFFFFC) >> 2)); + APPEND(MIPS_MOVE(MIPS_a0, MIPS_at)); + + /* Restore everything back the way it was */ + APPEND(MIPS_JAL(((uintptr_t)code_restore_regs_state & 0x0FFFFFFC) >> 2)); + if (IS_SPILLED(1)) { + APPEND(MIPS_LW(MIPS_at, + 4*(18+24) + block->regs[1].stack_offset, MIPS_sp)); + } else if (block->regs[1].native_reg >= MIPS_v0 + && block->regs[1].native_reg <= MIPS_t7) { + APPEND(MIPS_LW(MIPS_at, + 4*(block->regs[1].native_reg - MIPS_v0), MIPS_sp)); + } else if (block->regs[1].native_reg >= MIPS_t8 + && block->regs[1].native_reg <= MIPS_t9) { + APPEND(MIPS_LW(MIPS_at, + 4*(block->regs[1].native_reg - MIPS_t8 + 14), MIPS_sp)); + } else { + APPEND(MIPS_MOVE(MIPS_at, block->regs[1].native_reg)); + } + + return num_insns; +} + +/*----------------------------------*/ + +/** + * sh2_stealth_cache_reg: Add MIPS code to cache a register value for + * SH-2 TRACE_STEALTH mode. + * + * [Parameters] + * block: RTL block being translated + * insn_index: Index of RTL instruction to translate + * [Return value] + * Number of RTL instructions processed (nonzero) on success, zero on error + */ +static unsigned int sh2_stealth_cache_reg(RTLBlock * const block, + const uint32_t insn_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + const RTLInsn *insn = &block->insns[insn_index]; + unsigned int num_insns = 1; + uint32_t op = insn->src_imm; + uint32_t offset = 0; + + /* Get the offset if we have one */ + if (op & 0x08000000) { + PRECOND(op>>24 == 0xB8, return 1); + offset = (op & 0xFFFF) << 16; + op = insn[num_insns++].src_imm; + PRECOND(op>>24 == 0xBC, return 1); + offset |= op & 0xFFFF; + op = insn[num_insns++].src_imm; + PRECOND(op>>24 == 0xB0, return 1); + } + + unsigned int sh2_reg = (op >> 16) & 0xFF; + unsigned int rtl_reg = op & 0xFFFF; + + if (rtl_reg) { + /* Store a new value into the cache */ + + const RTLRegister * const reg = &block->regs[rtl_reg]; + unsigned int mips_reg; + if (reg->native_allocated + && reg == block->mips.reg_map[reg->native_reg] + ) { + if (IS_SPILLED(rtl_reg)) { + APPEND(MIPS_LW(MIPS_at, reg->stack_offset, MIPS_sp)); + mips_reg = MIPS_at; + } else if (reg->mips.is_in_hilo) { + APPEND(reg->mips.is_in_hilo | MIPS_at<<11); + mips_reg = MIPS_at; + } else if (reg->native_reg == MIPS_a0) { + APPEND(MIPS_MOVE(MIPS_at, MIPS_a0)); + mips_reg = MIPS_at; + } else { + mips_reg = reg->native_reg; + } + } else { + /* Either the register is already dead and got clobbered, or it + * got eliminated perhaps because the corresponding SH-2 + * register was later overwritten. This is a pain if the + * dead/eliminated register is a result register, but what can + * we do... */ + switch (reg->source) { + + case RTLREG_CONSTANT: + APPEND(MIPS_LUI(MIPS_at, reg->value >> 16)); + APPEND(MIPS_ORI(MIPS_at, MIPS_at, reg->value & 0xFFFF)); + break; + + case RTLREG_MEMORY: { + const RTLRegister * const addr_reg = + &block->regs[reg->memory.addr_reg]; + unsigned int base_reg; + if (addr_reg->native_allocated + && addr_reg==block->mips.reg_map[addr_reg->native_reg] + ) { + base_reg = addr_reg->native_reg; + } else if (addr_reg->source == RTLREG_CONSTANT) { + APPEND(MIPS_LUI(MIPS_at, addr_reg->value>>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_at, MIPS_at, addr_reg->value&0xFFFF)); + base_reg = MIPS_at; + } else { + DMSG("%p/%u: Address register r%u not available for r%u", + block, insn_index, reg->memory.addr_reg, rtl_reg); + break; + } + if (reg->memory.size == 1) { // 8 bits + if (reg->memory.is_signed) { + APPEND(MIPS_LB(MIPS_at, reg->memory.offset, base_reg)); + } else { + APPEND(MIPS_LBU(MIPS_at, reg->memory.offset, base_reg)); + } + } else if (reg->memory.size == 2) { // 16 bits + if (reg->memory.is_signed) { + APPEND(MIPS_LH(MIPS_at, reg->memory.offset, base_reg)); + } else { + APPEND(MIPS_LHU(MIPS_at, reg->memory.offset, base_reg)); + } + } else { // 32 bits + APPEND(MIPS_LW(MIPS_at, reg->memory.offset, base_reg)); + } + break; + } // case RTLREG_MEMORY + + case RTLREG_RESULT: + case RTLREG_RESULT_NOFOLD: { + static const uint32_t mips_opcodes[] = { + [RTLOP_ADD ] = MIPS_ADDU (MIPS_at, 0, 0), + [RTLOP_ADDI ] = MIPS_ADDU (MIPS_at, 0, 0), + [RTLOP_SUB ] = MIPS_SUBU (MIPS_at, 0, 0), + [RTLOP_MULU ] = MIPS_MULTU(0, 0), + [RTLOP_MULS ] = MIPS_MULT (0, 0), + [RTLOP_DIVMODU] = MIPS_DIVU (0, 0), + [RTLOP_DIVMODS] = MIPS_DIV (0, 0), + [RTLOP_AND ] = MIPS_AND (MIPS_at, 0, 0), + [RTLOP_ANDI ] = MIPS_AND (MIPS_at, 0, 0), + [RTLOP_OR ] = MIPS_OR (MIPS_at, 0, 0), + [RTLOP_ORI ] = MIPS_OR (MIPS_at, 0, 0), + [RTLOP_XOR ] = MIPS_XOR (MIPS_at, 0, 0), + [RTLOP_XORI ] = MIPS_XOR (MIPS_at, 0, 0), + [RTLOP_NOT ] = MIPS_NOR (MIPS_at, 0, MIPS_zero), + [RTLOP_SLL ] = MIPS_SLLV (MIPS_at, 0, 0), + [RTLOP_SLLI ] = MIPS_SLLV (MIPS_at, 0, 0), + [RTLOP_SRL ] = MIPS_SRLV (MIPS_at, 0, 0), + [RTLOP_SRLI ] = MIPS_SRLV (MIPS_at, 0, 0), + [RTLOP_SRA ] = MIPS_SRAV (MIPS_at, 0, 0), + [RTLOP_SRAI ] = MIPS_SRAV (MIPS_at, 0, 0), + [RTLOP_ROR ] = MIPS_RORV (MIPS_at, 0, 0), + [RTLOP_RORI ] = MIPS_RORV (MIPS_at, 0, 0), + [RTLOP_CLZ ] = MIPS_CLZ (MIPS_at, 0), + [RTLOP_CLO ] = MIPS_CLO (MIPS_at, 0), + [RTLOP_SLTU ] = MIPS_SLTU (MIPS_at, 0, 0), + [RTLOP_SLTUI ] = MIPS_SLTU (MIPS_at, 0, 0), + [RTLOP_SLTS ] = MIPS_SLT (MIPS_at, 0, 0), + [RTLOP_SLTSI ] = MIPS_SLT (MIPS_at, 0, 0), + [RTLOP_BSWAPH ] = MIPS_WSBH (MIPS_at, 0), + [RTLOP_BSWAPW ] = MIPS_WSBW (MIPS_at, 0), + [RTLOP_HSWAPW ] = MIPS_ROR (MIPS_at, 0, 16), + }; + const RTLRegister *src1_reg, *src2_reg; + src1_reg = &block->regs[reg->result.src1]; + src2_reg = &block->regs[reg->result.src2]; + switch (reg->result.opcode) { + + case RTLOP_MOVE: + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + } else { + APPEND(MIPS_MOVE(MIPS_at, src1_reg->native_reg)); + } + break; + + case RTLOP_NOT: + case RTLOP_CLZ: + case RTLOP_CLO: { + int rs; + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + rs = MIPS_at; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + rs = MIPS_at; + } else { + rs = src1_reg->native_reg; + } + APPEND(mips_opcodes[reg->result.opcode] | rs<<21); + break; + } + + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: { + int rt; + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + rt = MIPS_at; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + rt = MIPS_at; + } else { + rt = src1_reg->native_reg; + } + APPEND(mips_opcodes[reg->result.opcode] | rt<<16); + break; + } + + case RTLOP_ADD: + case RTLOP_SUB: + case RTLOP_AND: + case RTLOP_OR: + case RTLOP_XOR: + case RTLOP_SLL: + case RTLOP_SRL: + case RTLOP_SRA: + case RTLOP_ROR: + case RTLOP_SLTU: + case RTLOP_SLTS: { + unsigned int rs, rt; // src1/src2 MIPS registers + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, src1_reg->stack_offset, MIPS_sp)); + rs = MIPS_at; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + rs = MIPS_at; + } else { + rs = src1_reg->native_reg; + } + if (IS_SPILLED(reg->result.src2)) { + APPEND(MIPS_LW(MIPS_v1, src2_reg->stack_offset, MIPS_sp)); + rt = MIPS_v1; + } else if (src2_reg->mips.is_in_hilo) { + APPEND(src2_reg->mips.is_in_hilo | MIPS_v1<<11); + rt = MIPS_v1; + } else { + rt = src2_reg->native_reg; + } + if ((mips_opcodes[reg->result.opcode] & 0xFC00003F) < 010){ + /* For SLLV/SRLV/SRAV/RORV, rt is the source and + * rs is the shift amount */ + APPEND(mips_opcodes[reg->result.opcode] | rt<<21 | rs<<16); + } else { + APPEND(mips_opcodes[reg->result.opcode] | rs<<21 | rt<<16); + } + break; + } + + case RTLOP_ADDI: + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + case RTLOP_SLTSI: { + unsigned int rs, rt; // src1/src2 MIPS registers + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + rs = MIPS_at; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + rs = MIPS_at; + } else { + rs = src1_reg->native_reg; + } + rt = MIPS_v1; + APPEND(MIPS_LUI(rt, reg->result.imm>>16 & 0xFFFF)); + APPEND(MIPS_ORI(rt, rt, reg->result.imm & 0xFFFF)); + if ((mips_opcodes[reg->result.opcode] & 0xFC00003F) < 010){ + APPEND(mips_opcodes[reg->result.opcode] | rt<<21 | rs<<16); + } else { + APPEND(mips_opcodes[reg->result.opcode] | rs<<21 | rt<<16); + } + break; + } + + case RTLOP_MULU: + case RTLOP_MULS: + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: { + unsigned int rs, rt; + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, -8)); + APPEND(MIPS_MFHI(MIPS_v1)); + APPEND(MIPS_SW(MIPS_v1, 0, MIPS_sp)); + APPEND(MIPS_MFLO(MIPS_v1)); + APPEND(MIPS_SW(MIPS_v1, 4, MIPS_sp)); + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, src1_reg->stack_offset, MIPS_sp)); + rs = MIPS_at; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + rs = MIPS_at; + } else { + rs = src1_reg->native_reg; + } + if (IS_SPILLED(reg->result.src2)) { + APPEND(MIPS_LW(MIPS_v1, src2_reg->stack_offset, MIPS_sp)); + rt = MIPS_v1; + } else if (src2_reg->mips.is_in_hilo) { + APPEND(src2_reg->mips.is_in_hilo | MIPS_v1<<11); + rt = MIPS_v1; + } else { + rt = src2_reg->native_reg; + } + APPEND(mips_opcodes[reg->result.opcode] | rs<<21 | rt<<16); + if (reg->result.second_res) { + APPEND(MIPS_MFHI(MIPS_at)); + } else { + APPEND(MIPS_MFLO(MIPS_at)); + } + APPEND(MIPS_LW(MIPS_v1, 0, MIPS_sp)); + APPEND(MIPS_MTHI(MIPS_v1)); + APPEND(MIPS_LW(MIPS_v1, 4, MIPS_sp)); + APPEND(MIPS_MTLO(MIPS_v1)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + break; + } + + case RTLOP_SELECT: { + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, -8)); + APPEND(MIPS_SW(MIPS_v0, 0, MIPS_sp)); + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + } else { + APPEND(MIPS_MOVE(MIPS_at, src1_reg->native_reg)); + } + int reg_src2, reg_cond; + if (IS_SPILLED(reg->result.src2)) { + APPEND(MIPS_LW(MIPS_v0, + src2_reg->stack_offset, MIPS_sp)); + reg_src2 = MIPS_v0; + } else if (src2_reg->mips.is_in_hilo) { + APPEND(src2_reg->mips.is_in_hilo | MIPS_v0<<11); + reg_src2 = MIPS_v0; + } else { + reg_src2 = src2_reg->native_reg; + } + if (IS_SPILLED(reg->result.cond)) { + APPEND(MIPS_LW(MIPS_v1, block->regs[reg->result.cond].stack_offset, MIPS_sp)); + reg_cond = MIPS_v1; + } else if (block->regs[reg->result.cond].mips.is_in_hilo) { + APPEND(block->regs[reg->result.cond].mips.is_in_hilo + | MIPS_at<<11); + } else { + reg_cond = block->regs[reg->result.cond].native_reg; + } + APPEND(MIPS_MOVZ(MIPS_at, reg_src2, reg_cond)); + APPEND(MIPS_LW(MIPS_v0, 0, MIPS_sp)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + break; + } + + case RTLOP_BFEXT: + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_at, + src1_reg->stack_offset, MIPS_sp)); + APPEND(MIPS_EXT(MIPS_at, MIPS_at, + insn->bitfield.start, + insn->bitfield.count)); + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_at<<11); + } else { + APPEND(MIPS_EXT(MIPS_at, src1_reg->native_reg, + insn->bitfield.start, + insn->bitfield.count)); + } + break; + + case RTLOP_BFINS: { + unsigned int rs; + if (IS_SPILLED(reg->result.src2)) { + APPEND(MIPS_LW(MIPS_at, + src2_reg->stack_offset, MIPS_sp)); + } else if (src2_reg->mips.is_in_hilo) { + APPEND(src2_reg->mips.is_in_hilo | MIPS_at<<11); + } else { + APPEND(MIPS_MOVE(MIPS_at, src2_reg->native_reg)); + } + if (IS_SPILLED(reg->result.src1)) { + APPEND(MIPS_LW(MIPS_v1, src1_reg->stack_offset, MIPS_sp)); + rs = MIPS_v1; + } else if (src1_reg->mips.is_in_hilo) { + APPEND(src1_reg->mips.is_in_hilo | MIPS_v1<<11); + rs = MIPS_v1; + } else { + rs = src1_reg->native_reg; + } + APPEND(MIPS_INS(MIPS_at, rs, insn->bitfield.start, + insn->bitfield.count)); + break; + } + + default: + DMSG("%p/%u: Don't know how to emulate result opcode %u" + " for r%u", block, insn_index, reg->result.opcode, + rtl_reg); +#ifdef PSP_DEBUG + return 0; // This is a bug, so abort +#else + break; +#endif + } + break; + } // case RTLREG_RESULT{,_NOFOLD} + + default: + DMSG("%p/%u: Don't know how to emulate register source %u" + " for r%u", block, insn_index, reg->source, rtl_reg); + /* Let it slide, because there's nothing we can do about it */ + break; + + } // switch (reg->source) + mips_reg = MIPS_at; + } // if live + + if (offset) { + if (offset+0x8000 < 0x10000) { + APPEND(MIPS_ADDIU(MIPS_at, mips_reg, offset)); + } else { + if (offset < 0x10000) { + APPEND(MIPS_ORI(MIPS_v1, MIPS_zero, offset)); + } else { + APPEND(MIPS_LUI(MIPS_v1, offset >> 16)); + APPEND(MIPS_ORI(MIPS_v1, MIPS_v1, offset & 0xFFFF)); + } + APPEND(MIPS_ADDU(MIPS_at, mips_reg, MIPS_v1)); + } + mips_reg = MIPS_at; + } + + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, -8)); + APPEND(MIPS_SW(MIPS_a0, 0, MIPS_sp)); + if (sh2_reg & 0x80) { // Used for SR.T + const uintptr_t cache_ptr = (uintptr_t)&sh2_regcache[16]; + APPEND(MIPS_LUI(MIPS_a0, (cache_ptr + 0x8000) >> 16)); + if (!(block->sh2_regcache_mask & 1<<16)) { + if (block->regs[1].source != RTLREG_PARAMETER + || block->regs[1].param_index != 0 + ) { + DMSG("r1 is not state block, can't handle"); + return 0; + } + if (IS_SPILLED(1)) { + APPEND(MIPS_LW(MIPS_v1, + block->regs[1].stack_offset, MIPS_sp)); + APPEND(MIPS_LW(MIPS_v1, offsetof(SH2State,SR), MIPS_v1)); + } else { + APPEND(MIPS_LW(MIPS_v1, offsetof(SH2State,SR), + block->regs[1].native_reg)); + } + block->sh2_regcache_mask |= 1<<16; + } else { + APPEND(MIPS_LW(MIPS_v1, cache_ptr & 0xFFFF, MIPS_a0)); + } + APPEND(MIPS_INS(MIPS_v1, mips_reg, SR_T_SHIFT, 1)); + APPEND(MIPS_SW(MIPS_v1, cache_ptr & 0xFFFF, MIPS_a0)); + APPEND(MIPS_LW(MIPS_a0, 0, MIPS_sp)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + } else { + const uintptr_t cache_ptr = (uintptr_t)&sh2_regcache[sh2_reg]; + APPEND(MIPS_LUI(MIPS_a0, (cache_ptr + 0x8000) >> 16)); + APPEND(MIPS_SW(mips_reg, cache_ptr & 0xFFFF, MIPS_a0)); + APPEND(MIPS_LW(MIPS_a0, 0, MIPS_sp)); + APPEND(MIPS_ADDIU(MIPS_sp, MIPS_sp, 8)); + block->sh2_regcache_mask |= 1<>16 & 0xFFFF)); + APPEND(MIPS_ORI(MIPS_at, MIPS_at, offset & 0xFFFF)); + const uintptr_t cache_ptr = (uintptr_t)&sh2_regcache[sh2_reg]; + APPEND(MIPS_LUI(MIPS_v1, (cache_ptr + 0x8000) >> 16)); + APPEND(MIPS_SW(MIPS_at, cache_ptr & 0xFFFF, MIPS_v1)); + block->sh2_regcache_mask |= 1<sh2_regcache_mask &= ~(1<insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + PRECOND(insn_index < block->num_insns, return 0); + + const RTLInsn *insn = &block->insns[insn_index]; + unsigned int num_insns = 1; + + APPEND(MIPS_JAL(((uintptr_t)code_save_regs & 0x0FFFFFFC) >> 2)); + APPEND(MIPS_NOP()); + + uintptr_t funcptr; + switch (insn->src_imm & 0xFFFF) { + case 1: funcptr = (uintptr_t)trace_storeb_callback; break; + case 2: funcptr = (uintptr_t)trace_storew_callback; break; + case 4: funcptr = (uintptr_t)trace_storel_callback; break; + default: + DMSG("Invalid store trace type %u", insn->src_imm & 0xFF); + return 0; + } + + uint32_t op = insn[num_insns++].src_imm; + PRECOND(op>>28 == 0xD, return 1); + if (op & 0x08000000) { + PRECOND(op>>24 == 0xD8, return 0); + APPEND(MIPS_LUI(MIPS_a0, op & 0xFFFF)); + op = insn[num_insns++].src_imm; + PRECOND(op>>24 == 0xDC, return 0); + APPEND(MIPS_ORI(MIPS_a0, MIPS_a0, op & 0xFFFF)); + } else { + const uint32_t address_reg = op & 0xFFFF; + const RTLRegister *reg = &block->regs[address_reg]; + if (IS_SPILLED(address_reg)) { + APPEND(MIPS_LW(MIPS_a0, 4*18 + reg->stack_offset, MIPS_sp)); + } else if (reg->mips.is_in_hilo) { + APPEND(reg->mips.is_in_hilo | MIPS_a0<<11); + } else if (reg->native_reg != MIPS_a0) { + APPEND(MIPS_MOVE(MIPS_a0, reg->native_reg)); + } + op = insn[num_insns++].src_imm; + PRECOND(op>>24 == 0xD4, return 1); + APPEND(MIPS_LUI(MIPS_at, op & 0xFFFF)); + op = insn[num_insns++].src_imm; + PRECOND(op>>24 == 0xD6, return 1); + APPEND(MIPS_ORI(MIPS_at, MIPS_at, op & 0xFFFF)); + APPEND(MIPS_ADD(MIPS_a0, MIPS_a0, MIPS_at)); + } + + op = insn[num_insns++].src_imm; + PRECOND(op>>28 == 0xE, return 1); + const uint32_t src_reg = op & 0xFFFF; + const RTLRegister *reg = &block->regs[src_reg]; + APPEND(MIPS_JAL((funcptr & 0x0FFFFFFC) >> 2)); + if (IS_SPILLED(src_reg)) { + APPEND(MIPS_LW(MIPS_a1, 4*18 + reg->stack_offset, MIPS_sp)); + } else if (reg->mips.is_in_hilo) { + APPEND(reg->mips.is_in_hilo | MIPS_a1<<11); + } else if (reg->native_reg == MIPS_a0) { + APPEND(MIPS_LW(MIPS_a1, 8, MIPS_sp)); + } else if (reg->native_reg != MIPS_a1) { + APPEND(MIPS_MOVE(MIPS_a1, reg->native_reg)); + } else { // Don't forget to fill the delay slot! + APPEND(MIPS_NOP()); + } + + APPEND(MIPS_JAL(((uintptr_t)code_restore_regs & 0x0FFFFFFC) >> 2)); + APPEND(MIPS_NOP()); + + return num_insns; +} + +/*----------------------------------*/ + +#endif // RTL_TRACE_STEALTH_FOR_SH2 + +/*************************************************************************/ + +/** + * resolve_branches: Resolve branches to RTL labels and jumps to the + * epilogue within MIPS code. If MIPS_OPTIMIZE_BRANCHES is defined, also + * perform optimizations on branches. + * + * [Parameters] + * block: RTL block to translate + * [Return value] + * Nonzero on success, zero on error + */ +static int resolve_branches(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->label_offsets != NULL, return 0); + + uint32_t *native_ptr = (uint32_t *)block->native_buffer; + uint32_t * const native_top = + (uint32_t *)((uintptr_t)native_ptr + block->native_length); + const uint32_t * const epilogue_ptr = + (uint32_t *)((uintptr_t)native_ptr + block->mips.epilogue_offset); + const uint32_t * const chain_ptr = + (uint32_t *)((uintptr_t)native_ptr + block->mips.chain_offset); + + for (; native_ptr < native_top; native_ptr++) { + uint32_t opcode = *native_ptr; + + if (opcode>>27 == __OP_BEQ_LABEL>>1 /*covers both opcodes*/) { + const unsigned int label = opcode & ((1<<16) - 1); + const uint32_t delay_offset = + (uintptr_t)native_ptr - (uintptr_t)block->native_buffer + 4; + if (UNLIKELY((int32_t)block->label_offsets[label] < 0)) { + DMSG("%p: Label %u not defined at native offset %u", + block, label, delay_offset-4); + return 0; + } + int32_t disp = (block->label_offsets[label] - delay_offset); + if (disp < -0x8000<<2 || disp > 0x7FFF<<2) { + /* FIXME: Technically, we could resolve this by flipping + * the sense of the branch and inserting a jump + * instruction, but that would require another pass after + * the final buffer pointer was known to fill in absolute + * addresses; we could also do a BAL and calculate the + * target address manually. But in any case, the chance + * of hitting this is so slim that we don't worry about it + * for now. */ + DMSG("%p: Displacement to label %u (+0x%X) from +0x%X too" + " large, can't resolve", block, label, + block->label_offsets[label], delay_offset); + return 0; + } + *native_ptr = ((__OP_BEQ>>1) << 27) + | (opcode & 0x07FF0000) // Include the EQ/NE bit + | ((disp>>2) & 0xFFFF); + + } else if (opcode == MIPS_B_EPILOGUE_RET_CONST) { + const int32_t offset = epilogue_ptr - (native_ptr+1); + if (LIKELY(offset <= 0x7FFF)) { // Always positive + *native_ptr = MIPS_B(offset); + } else { + DMSG("WARNING: block too large, using nonportable J"); + const uint32_t j_addr = (uintptr_t)epilogue_ptr & 0x0FFFFFFC; + *native_ptr = MIPS_J(j_addr >> 2); + } + + } else if (opcode == MIPS_B_EPILOGUE_CHAIN_AT) { + const int32_t offset = chain_ptr - (native_ptr+1); + if (LIKELY(offset <= 0x7FFF)) { // Always positive + *native_ptr = MIPS_B(offset); + } else { + DMSG("WARNING: block too large, using nonportable J"); + const uint32_t j_addr = (uintptr_t)chain_ptr & 0x0FFFFFFC; + *native_ptr = MIPS_J(j_addr >> 2); + } + + } + +#ifdef MIPS_OPTIMIZE_BRANCHES + opcode = *native_ptr; // It may have been updated above + if (opcode>>28 == __OP_BEQ>>2 + || (opcode>>26 == __OP_REGIMM + && (insn_rt(opcode) & ~021) == __RI_BLTZ) + ) { + uint32_t likely_opcode; + if (opcode>>16 == MIPS_B(0)>>16) { + /* Unconditional branch, so no need for Likely bit */ + likely_opcode = opcode; + } else if (opcode>>28 == __OP_BEQ>>2) { + likely_opcode = opcode | (uint32_t)020<<26; + } else { // REGIMM insns + likely_opcode = opcode | (uint32_t)002<<16; + } + int32_t offset = (int16_t)(opcode & 0xFFFF); + const uint32_t *target = native_ptr + (1+offset); + unsigned int limit = 16; // Watch out for infinite loops + while ((*target>>16 == MIPS_B(0)>>16 + || *target>>16 == MIPS_B_LABEL(0)>>16 + || *target == MIPS_B_EPILOGUE_RET_CONST) + && (native_ptr[1] == MIPS_NOP() || target[1] == MIPS_NOP()) + && limit > 0 + ) { + limit--; + if (target[1] != MIPS_NOP()) { + native_ptr[1] = target[1]; // Must have been a NOP + opcode = likely_opcode; + } + int32_t new_offset; + if (*target == MIPS_B_EPILOGUE_RET_CONST) { + const uint32_t delay_pos = + (native_ptr+1) - (uint32_t *)block->native_buffer; + new_offset = block->mips.epilogue_offset/4 - delay_pos; + } else if (*target>>16 == MIPS_B_LABEL(0)>>16) { + const unsigned int label = *target & 0xFFFF; + if (UNLIKELY((int32_t)block->label_offsets[label] < 0)) { + /* Just skip out here; the Bxx_LABEL processing + * code will report the error */ + break; + } + const uint32_t delay_pos = + (native_ptr+1) - (uint32_t *)block->native_buffer; + new_offset = block->label_offsets[label]/4 - delay_pos; + } else { + new_offset = offset + (1 + (int16_t)(*target & 0xFFFF)); + } + if (new_offset < -0x8000 || new_offset > 0x7FFF) { + break; + } + offset = new_offset; + *native_ptr = (opcode & 0xFFFF0000) | (offset & 0xFFFF); + target = native_ptr + (1+offset); + } + if (*native_ptr>>16 == MIPS_B(0)>>16 + && (*target>>26 == MIPS_J(0) + || (*target & 0xFC00003F) == MIPS_JR(0)) + && (native_ptr[1] == MIPS_NOP() || target[1] == MIPS_NOP()) + ) { + /* Chain an unconditional branch through to a jump (but not + * JAL/JALR, since we'd get the wrong return address) */ + if (target[1] != MIPS_NOP()) { + native_ptr[1] = target[1]; + } + *native_ptr = *target; + } else if (offset != 0x7FFF + && native_ptr[1] == MIPS_NOP() + && !insn_is_jump(*target) + && !insn_is_branch(*target) + ) { + *native_ptr = + (likely_opcode & 0xFFFF0000) | ((offset+1) & 0xFFFF); + native_ptr[1] = *target; + } + } +#endif + + } // for (; native_ptr < native_top; native_ptr++) + + return 1; +} + +/*************************************************************************/ + +#undef MAP_REGISTER +#undef APPEND + +/*************************************************************************/ +/*********************** Low-level helper routines ***********************/ +/*************************************************************************/ + +/** + * append_insn: Append a MIPS instruction word to the given block. + * + * [Parameters] + * block: RTL block to append to + * insn: MIPS instruction word to append + * [Return value] + * Nonzero on success, zero on error + */ +static inline int append_insn(RTLBlock * const block, const uint32_t insn) +{ + PRECOND(block != NULL, return 0); + + if (UNLIKELY(block->native_length >= block->native_bufsize)) { + if (UNLIKELY(!expand_block(block))) { + return 0; + } + } + + *(uint32_t *)((uint8_t *)block->native_buffer + block->native_length) + = insn; + block->native_length += 4; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * expand_block: Expand the current native code buffer. + * + * [Parameters] + * block: RTL block to expand + * [Return value] + * Nonzero on success, zero on error + */ +static int expand_block(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + + block->native_bufsize = block->native_length + NATIVE_EXPAND_SIZE; + void *new_buffer = realloc(block->native_buffer, block->native_bufsize); + if (UNLIKELY(!new_buffer)) { + DMSG("No memory to expand native buffer to %u bytes", + block->native_bufsize); + return 0; + } + block->native_buffer = new_buffer; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * last_insn: Return the last 32-bit instruction word added to the given + * block. If the block is empty, returns MIPS_NOP(). + * + * [Parameters] + * block: RTL block + * [Return value] + * Last instruction word added + */ +static uint32_t last_insn(const RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->native_buffer != NULL, return 0); + + if (UNLIKELY(block->native_length == 0)) { + return MIPS_NOP(); + } + return *(uint32_t *)((uint8_t *)block->native_buffer + + (block->native_length - 4)); +} + +/*-----------------------------------------------------------------------*/ + +/** + * pop_insn: Return the last 32-bit instruction word added to the given + * block, and remove that word from the end of the block. If the block is + * empty, returns MIPS_NOP() without modifying the block. + * + * [Parameters] + * block: RTL block + * [Return value] + * Last instruction word added + */ +static uint32_t pop_insn(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->native_buffer != NULL, return 0); + + if (UNLIKELY(block->native_length == 0)) { + return MIPS_NOP(); + } + block->native_length -= 4; + return *(uint32_t *)((uint8_t *)block->native_buffer + + block->native_length); +} + +/*************************************************************************/ + +/** + * append_float: Append a high-latency instruction (a load, multiply, or + * divide instruction) to the given block. If possible (and if + * MIPS_OPTIMIZE_SCHEDULING is enabled), float the instruction upwards to + * obtain a distance of at least "latency" instructions before the next + * instruction. Preceding instructions in the same dependency chain are + * also floated upwards to preserve correctness. + * + * [Parameters] + * block: RTL block to append to + * insn: Instruction word to append + * latency: Desired distance to next instruction + * [Return value] + * Nonzero on success, zero on error + */ +static int append_float(RTLBlock * const block, const uint32_t insn, + const int latency) +{ +#ifndef MIPS_OPTIMIZE_SCHEDULING + return append_insn(block, insn); +#else + + PRECOND(block != NULL, return 0); + PRECOND(insn_is_load(insn) + || (insn & 0xFC00003C) == __SP_MULT + || (insn & 0xFC00003E) == __SP_MADD + || (insn & 0xFC00003E) == __SP_MSUB, return 0); + + /* Does the dependency chain ending with the new instruction have a + * load or store instruction? If so, do we know where it points? */ + + int has_mem; + unsigned int unique_pointer; + uint32_t unique_pointer_birth; + if (insn_is_load(insn)) { // We're never called for stores + has_mem = 1; + PRECOND(block->mips.reg_map[insn_rs(insn)] != NULL, return 0); + unique_pointer = block->mips.reg_map[insn_rs(insn)]->unique_pointer; + unique_pointer_birth = block->mips.reg_map[insn_rs(insn)]->birth; + } else { + has_mem = 0; + unique_pointer = 0; + } + + /* First add the instruction to the block. */ + + if (UNLIKELY(!append_insn(block, insn))) { + return 0; + } + + /* Extract registers used and set by this instruction; we'll add to + * these sets all registers used by preceding instructions in the + * dependency chain. */ + + uint32_t regs_used = insn_regs_used(insn); + uint32_t regs_set = insn_regs_set(insn); + + /* Scan backwards, up to the beginning of the unit or preceding branch + * delay slot, to find instructions that can be placed below this one, + * and move them below the current instruction. */ + + uint32_t * const unit_start = (uint32_t *) + ((uint8_t *)block->native_buffer + block->mips.unit_start); + uint32_t *insn_ptr = (uint32_t *) + ((uint8_t *)block->native_buffer + (block->native_length - 4)); + uint32_t * const target = insn_ptr - (latency-1); + uint32_t *search; + + for (search = insn_ptr - 1; search >= unit_start; search--) { + + /* If the preceding instruction is a jump or branch, stop here. + * If this is the first instruction in the unit, the preceding + * instruction can't be a jump or branch (if it was, a delay slot + * instruction would have been added after it at the end of the + * unit). */ + + if (search > unit_start + && (insn_is_jump(search[-1]) || insn_is_branch(search[-1])) + ) { + break; + } + + /* An instruction is independent of a following sequence of + * instructions if that instruction: + * 1) Does not set a register used in the following sequence + * (i.e., the sequence does not depend on the result of the + * instruction). + * 2) Does not set a register set in the following sequence + * (i.e., the sequence does not reuse the instruction's + * result register). + * 3) Does not use a register set in the following sequence + * (i.e., the sequence does not clobber an operand of the + * instruction). + * We explicitly do not move load or store instructions across + * other load or store instructions, so as to maintain ordering of + * memory accesses (otherwise, we might try to load from an address + * before the store that updates it) and to avoid introducing a + * stall by shifting a previous load farther down the code stream. + * However, we _do_ float a load above a preceding store if we know + * that the store and load point to different addresses; i.e., if + * at least one of the registers is a unique pointer, and they do + * not reference the same unique address. */ + + const uint32_t this_used = insn_regs_used(*search); + const uint32_t this_set = insn_regs_set(*search); + int this_is_mem; + if (insn_is_load(*search)) { + this_is_mem = 1; + } else if (insn_is_store(*search)) { + // FIXME: We don't currently save unique pointer data with the + // generated MIPS instructions, so for now we assume that the + // current register is the only one pointing to this unique + // region. This works at the moment because the state block + // pointer is the only register we mark as a unique pointer, + // but could break in other cases. + if (unique_pointer == 0 || insn_rs(insn) != insn_rs(*search)) { + this_is_mem = 1; + } else { + PRECOND(block->mips.reg_map[insn_rs(*search)] != NULL, + return 0); + this_is_mem = (insn_imm(insn) == insn_imm(*search)); + } + } else { + this_is_mem = 0; + } + if ((this_set & (regs_used | regs_set)) != 0 + || (this_used & regs_set) != 0 + || (has_mem && this_is_mem) + ) { + /* Add this register's used and set registers to the dependency + * chain's cumulative set. */ + regs_used |= this_used; + regs_set |= this_set; + /* If it was a load or store instruction, record that fact. */ + if (insn_is_load(*search) || insn_is_store(*search)) { + has_mem = 1; + /* Register assignments may have changed, so we don't know + * for certain what the register pointed to at the time, so + * play it safe. */ + unique_pointer = 0; + } + } else { + /* Move this instruction immediately below the one we added. */ + const uint32_t move_insn = *search; + uint32_t *move_ptr; + for (move_ptr = search; move_ptr < insn_ptr; move_ptr++) { + *move_ptr = *(move_ptr + 1); + } + *move_ptr = move_insn; + /* The instruction we added has now moved one word up. */ + insn_ptr--; + /* If we've achieved the requested distance, stop. */ + if (insn_ptr <= target) { + break; + } + } + + } // for (search = insn_ptr - 1; search >= unit_start; search--) + + return 1; + +#endif // MIPS_OPTIMIZE_SCHEDULING +} + +/*-----------------------------------------------------------------------*/ + +/** + * append_branch: Append a branch instruction to the given block. If + * possible (and if MIPS_OPTIMIZE_DELAY_SLOT is enabled), place the current + * last instruction of the block into the branch's delay slot. + * + * [Parameters] + * block: RTL block to append to + * insn: Branch instruction word to append + * [Return value] + * Nonzero on success, zero on error + */ +static int append_branch(RTLBlock * const block, const uint32_t insn) +{ + PRECOND(block != NULL, return 0); + + uint32_t delay_slot = MIPS_NOP(); + +#ifdef MIPS_OPTIMIZE_DELAY_SLOT + if (block->native_length - 4 >= block->mips.unit_start) { + int can_swap_insns = 0; + const uint32_t prev1_insn = pop_insn(block); + uint32_t prev2_insn; + if (block->native_length - 4 >= block->mips.unit_start) { + prev2_insn = last_insn(block); + } else { + prev2_insn = MIPS_NOP(); + } + if (!insn_is_jump(prev2_insn) && !insn_is_branch(prev2_insn)) { + if (insn == MIPS_B_EPILOGUE_RET_CONST + || insn == MIPS_B_EPILOGUE_CHAIN_AT + || !(insn_regs_set(prev1_insn) & insn_regs_used(insn)) + ) { + can_swap_insns = 1; + } + } + if (can_swap_insns) { + delay_slot = prev1_insn; + } else { + if (UNLIKELY(!append_insn(block, prev1_insn))) { + // Just to be safe... + DMSG("Failed to re-append insn"); + return 0; + } + } + } +#endif + + return append_insn(block, insn) && append_insn(block, delay_slot); +} + +/*************************************************************************/ + +/** + * append_prologue: Append a function prologue to the given block. + * + * [Parameters] + * block: RTL block to append to + * [Return value] + * Nonzero on success, zero on error + */ +static int append_prologue(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + + /* Set up the stack frame (if we need one) */ + if (block->mips.total_frame_size) { + if (UNLIKELY(!append_insn(block, MIPS_ADDIU(MIPS_sp, MIPS_sp, -(block->mips.total_frame_size))))) { + DMSG("Failed to append prologue (stack frame)"); + return 0; + } + } + + /* Save $ra if necessary */ + if (block->mips.need_save_ra + && UNLIKELY(!append_insn(block, MIPS_SW(MIPS_ra, block->mips.total_frame_size - 4, MIPS_sp))) + ) { + DMSG("Failed to append prologue ($ra)"); + return 0; + } + + /* Save all callee-saved registers that we use */ + uint32_t offset = block->mips.total_frame_size - 8; + unsigned int i; + for (i = 0; i < lenof(callee_saved_regs); i++) { + const unsigned int mips_reg = callee_saved_regs[i]; + if (block->mips.sreg_used & (1 << mips_reg)) { + if (UNLIKELY(!append_insn(block, + MIPS_SW(mips_reg, offset, MIPS_sp)))) { + DMSG("Failed to append prologue ($%u)", mips_reg); + return 0; + } + offset -= 4; + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * append_epilogue: Append a function epilogue to the given block. + * + * [Parameters] + * block: RTL block to append to + * [Return value] + * Nonzero on success, zero on error + */ +static int append_epilogue(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + + /* Save the offset to the epilogue code for branch resolution */ + block->mips.epilogue_offset = block->native_length; + + /* Restore the original values of saved registers ($ra and $sN) */ + if (block->mips.need_save_ra + && UNLIKELY(!append_insn(block, MIPS_LW(MIPS_ra, block->mips.total_frame_size - 4, MIPS_sp))) + ) { + DMSG("Failed to append epilogue ($ra)"); + return 0; + } + uint32_t offset = block->mips.total_frame_size - 8; + unsigned int i; + for (i = 0; i < lenof(callee_saved_regs); i++) { + const unsigned int mips_reg = callee_saved_regs[i]; + if (block->mips.sreg_used & (1 << mips_reg)) { + if (UNLIKELY(!append_insn(block, + MIPS_LW(mips_reg, offset, MIPS_sp)))) { + DMSG("Failed to append epilogue ($%u)", mips_reg); + return 0; + } + offset -= 4; + } + } + + /* Free the stack frame (if we allocated one) and return */ + uint32_t final_insn; + if (block->mips.total_frame_size) { + final_insn = + MIPS_ADDIU(MIPS_sp, MIPS_sp, block->mips.total_frame_size); + } else { + final_insn = MIPS_NOP(); + } + if (UNLIKELY(!append_insn(block, MIPS_JR(MIPS_ra))) + || UNLIKELY(!append_insn(block, final_insn)) + ) { + DMSG("Failed to append epilogue (return)"); + return 0; + } + + /* If we need a chain-to-$at epilogue as well, generate that */ + if (block->mips.need_chain_at) { + block->mips.chain_offset = block->native_length; + if (block->mips.need_save_ra + && UNLIKELY(!append_insn(block, MIPS_LW(MIPS_ra, block->mips.total_frame_size - 4, MIPS_sp))) + ) { + DMSG("Failed to append chain epilogue ($ra)"); + return 0; + } + offset = block->mips.total_frame_size - 8; + for (i = 0; i < lenof(callee_saved_regs); i++) { + const unsigned int mips_reg = callee_saved_regs[i]; + if (block->mips.sreg_used & (1 << mips_reg)) { + if (UNLIKELY(!append_insn(block, + MIPS_LW(mips_reg, offset, MIPS_sp)))) { + DMSG("Failed to append chain epilogue ($%u)", mips_reg); + return 0; + } + offset -= 4; + } + } + if (block->mips.total_frame_size) { + final_insn = + MIPS_ADDIU(MIPS_sp, MIPS_sp, block->mips.total_frame_size); + } else { + final_insn = MIPS_NOP(); + } + if (UNLIKELY(!append_insn(block, MIPS_JR(MIPS_at))) + || UNLIKELY(!append_insn(block, final_insn)) + ) { + DMSG("Failed to append chain epilogue (return)"); + return 0; + } + } // if (block->mips.need_chain_at) + + return 1; +} + +/*************************************************************************/ +/****************** MIPS opcode informational functions ******************/ +/*************************************************************************/ + +/** + * insn_rs, insn_rt, insn_rd: Return the register number specified in the + * rs, rt, or rd field of the given instruction, respectively. + * + * [Parameters] + * opcode: Instruction opcode + * [Return value] + * rs, rt, or rd register number (0-31) + */ +static inline int insn_rs(const uint32_t opcode) +{ + return (opcode >> 21) & 0x1F; +} + +static inline int insn_rt(const uint32_t opcode) +{ + return (opcode >> 16) & 0x1F; +} + +static inline int insn_rd(const uint32_t opcode) +{ + return (opcode >> 11) & 0x1F; +} + +/*----------------------------------*/ + +/** + * insn_imm: Return the 16-bit immediate value specified in the given + * instruction. + * + * [Parameters] + * opcode: Instruction opcode + * [Return value] + * 16-bit immediate value as a signed integer + */ +static inline int insn_imm(const uint32_t opcode) +{ + return (int)(int16_t)opcode; +} + +/*************************************************************************/ + +/** + * insn_is_*: Return whether the given instruction is of the type + * specified by the function name: + * insn_is_load -- a load instruction + * insn_is_store -- a store instruction + * insn_is_jump -- a JUMP class instruction (J or JAL) + * insn_is_branch -- an IMM/REGIMM class branch instruction (BEQ, etc.) + * insn_is_imm -- an IMM class instruction + * insn_is_imm_alu -- an IMM class ALU instruction (ADDI, etc.) + * insn_is_special -- a SPECIAL class instruction + * insn_is_regimm -- a REGIMM class instruction (BLTZ, etc.) + * insn_is_allegrex -- an Allegrex-specific instruction (INS, SEB, etc.) + * + * [Parameters] + * opcode: Instruction opcode + * [Return value] + * Nonzero if the instruction is of the named type, else zero + */ +static inline int insn_is_load(const uint32_t opcode) +{ + return opcode>>29 == 4; // We don't use LL, so we don't check for it +} + +static inline int insn_is_store(const uint32_t opcode) +{ + return opcode>>29 == 5; // We don't use SC, so we don't check for it +} + +static inline int insn_is_jump(const uint32_t opcode) +{ + return opcode>>27 == __OP_J>>1 + || (insn_is_special(opcode) && (opcode & 0x3E) == __SP_JR); // JR/JALR +} + +static inline int insn_is_branch(const uint32_t opcode) +{ + return opcode>>28 == __OP_BEQ>>2 + || opcode>>28 == __OP_BEQL>>2 + || opcode>>27 == __OP_BEQ_LABEL>>1 + || insn_is_regimm(opcode); +} + +static inline int insn_is_imm(const uint32_t opcode) +{ + return (opcode>>26 >= __OP_BEQ && opcode>>26 <= __OP_BGTZL) + || (opcode>>27 == __OP_BEQ_LABEL>>1); +} + +static inline int insn_is_imm_alu(const uint32_t opcode) +{ + return opcode>>29 == 1; +} + +static inline int insn_is_special(const uint32_t opcode) +{ + return opcode>>26 == __OP_SPECIAL; +} + +static inline int insn_is_regimm(const uint32_t opcode) +{ + return opcode>>26 == __OP_REGIMM; +} + +static inline int insn_is_allegrex(const uint32_t opcode) +{ + return opcode>>26 == __OP_ALLEGREX; +} + +/*************************************************************************/ + +/** + * insn_regs_used: Return a bitmask of MIPS registers used by the given + * instruction (i.e. the instruction's source registers). $zero is never + * included in the set; bit 0 instead reflects the HI and LO registers. + * + * [Parameters] + * opcode: Instruction opcode + * [Return value] + * Bitmask of registers used by the instruction + */ +static inline uint32_t insn_regs_used(const uint32_t opcode) +{ + uint32_t regs; + int hilo = 0; + + if (insn_is_load(opcode) || insn_is_regimm(opcode) + || insn_is_imm_alu(opcode) + ) { + regs = 1<>26 == __OP_JAL + || (insn_is_regimm(opcode) + && insn_rt(opcode)>>2 == __RI_BLTZAL>>2) + ) { + regs = 1<insns = NULL; + block->insn_unitmap = NULL; + block->insns_size = INSNS_EXPAND_SIZE; + block->num_insns = 0; + + block->units = NULL; + block->units_size = UNITS_EXPAND_SIZE; + block->num_units = 0; + block->have_unit = 0; + block->cur_unit = 0; + + block->label_unitmap = NULL; + block->labels_size = LABELS_EXPAND_SIZE; + block->next_label = 1; + + block->regs = NULL; + block->regs_size = REGS_EXPAND_SIZE; + block->next_reg = 1; + block->first_live_reg = 0; + block->last_live_reg = 0; + block->unique_pointer_index = 1; + + block->finalized = 0; + + block->first_call_unit = -1; + block->last_call_unit = -1; + + block->insns = malloc(sizeof(*block->insns) * block->insns_size); + if (!block->insns) { + DMSG("No memory for %d RTLInsns", block->insns_size); + goto fail; + } + + block->units = malloc(sizeof(*block->units) * block->units_size); + if (!block->units) { + DMSG("No memory for %d RTLUnits", block->units_size); + goto fail; + } + + block->regs = malloc(sizeof(*block->regs) * block->regs_size); + if (!block->regs) { + DMSG("No memory for %d RTLRegisters", block->regs_size); + goto fail; + } + memset(&block->regs[0], 0, sizeof(*block->regs)); + + block->label_unitmap = + malloc(sizeof(*block->label_unitmap) * block->labels_size); + if (!block->label_unitmap) { + DMSG("No memory for %d labels", block->labels_size); + goto fail; + } + block->label_unitmap[0] = -1; + +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] Created new block at %p\n", block); +#endif + return block; + + fail: + free(block->insns); + free(block->units); + free(block->regs); + free(block->label_unitmap); + free(block); + return NULL; +} + +/*************************************************************************/ + +/* rtl_add_insn() helpers -- these move function calls like realloc() out + * of rtl_add_insn() to help the compiler optimize the fast path, by + * avoiding register spillage when it's not necessary. Note that GCC (at + * least version 4.3) will inline these by default, but that actually slows + * down the fast path on at least MIPS due to spillage of the function + * parameter registers, so we force these to be generated as separate + * functions. */ +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static int rtl_add_insn_with_extend(RTLBlock *block, RTLOpcode opcode, + uint32_t dest, uintptr_t src1, + uint32_t src2, uint32_t other); +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static int rtl_add_insn_with_new_unit(RTLBlock *block, RTLOpcode opcode, + uint32_t dest, uintptr_t src1, + uint32_t src2, uint32_t other); + +/*----------------------------------*/ + +/** + * rtl_add_insn: Append an instruction to the given block. The meaning of + * each operand depends on the instruction. + * + * [Parameters] + * block: RTLBlock to append to + * opcode: Instruction opcode (RTLOP_*) + * dest: Destination register for instruction + * src1: First source register or immediate value for instruction + * src2: Second source register or immediate value for instruction + * other: Extra register for instruction + * [Return value] + * Nonzero on success, zero on error + */ +int rtl_add_insn(RTLBlock *block, RTLOpcode opcode, uint32_t dest, + uintptr_t src1, uint32_t src2, uint32_t other) +{ + PRECOND(block != NULL, return 0); + PRECOND(!block->finalized, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(opcode >= RTLOP__FIRST && opcode <= RTLOP__LAST, return 0); + + /* Extend the instruction array if necessary */ + if (UNLIKELY(block->num_insns >= block->insns_size)) { + return rtl_add_insn_with_extend(block, opcode, + dest, src1, src2, other); + } + + /* Create a new basic unit if there's no active one */ + if (UNLIKELY(!block->have_unit)) { + return rtl_add_insn_with_new_unit(block, opcode, + dest, src1, src2, other); + } + + /* Fill in the instruction data */ + RTLInsn * const insn = &block->insns[block->num_insns]; + insn->opcode = opcode; + if (UNLIKELY(!rtlinsn_make(block, insn, dest, src1, src2, other))) { + return 0; + } + +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%5u: %s\n", block, block->num_insns, + rtl_decode_insn(block, block->num_insns, 0)); +#endif + block->num_insns++; + return 1; +} + +/*----------------------------------*/ + +/** + * rtl_add_insn_with_extend: Extend the instruction array and then add a + * new instruction. Called by rtl_add_insn() when the instruction array is + * full upon entry. + */ +static int rtl_add_insn_with_extend(RTLBlock *block, RTLOpcode opcode, + uint32_t dest, uintptr_t src1, + uint32_t src2, uint32_t other) +{ + uint32_t new_insns_size = block->num_insns + INSNS_EXPAND_SIZE; + RTLInsn *new_insns = realloc(block->insns, + sizeof(*block->insns) * new_insns_size); + if (UNLIKELY(!new_insns)) { + DMSG("No memory to expand block %p to %d insns", block, + new_insns_size); + return 0; + } + block->insns = new_insns; + block->insns_size = new_insns_size; + + /* Run back through rtl_add_insn() to handle the rest */ + return rtl_add_insn(block, opcode, dest, src1, src2, other); +} + +/*----------------------------------*/ + +/** + * rtl_add_insn_with_new_unit: Start a new basic unit and then add a new + * instruction. Called by rtl_add_insn() when there is no current basic + * unit upon entry. + */ +static int rtl_add_insn_with_new_unit(RTLBlock *block, RTLOpcode opcode, + uint32_t dest, uintptr_t src1, + uint32_t src2, uint32_t other) +{ + if (UNLIKELY(!rtlunit_add(block))) { + return 0; + } + block->have_unit = 1; + block->cur_unit = block->num_units - 1; + block->units[block->cur_unit].first_insn = block->num_insns; + + /* Run back through rtl_add_insn() to handle the rest */ + return rtl_add_insn(block, opcode, dest, src1, src2, other); +} + +/*-----------------------------------------------------------------------*/ + +/** + * rtl_alloc_register: Allocate a new register for use in the given block. + * The register's value is undefined until it has been used as the + * destination of an instruction. + * + * [Parameters] + * block: RTLBlock to allocate a register for + * [Return value] + * Register number (nonzero) on success, zero on error + */ +unsigned int rtl_alloc_register(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(!block->finalized, return 0); + PRECOND(block->regs != NULL, return 0); + + if (UNLIKELY(block->next_reg >= block->regs_size)) { + if (block->regs_size >= REGS_LIMIT) { + DMSG("Too many registers in block %p (limit %u)", + block, REGS_LIMIT); + return 0; + } + unsigned int new_regs_size; + /* Avoid 16-bit overflow (not that there are any modern machines + * where int is 16 bits, but let's follow the rules anyway) */ + if (block->regs_size > REGS_LIMIT - REGS_EXPAND_SIZE) { + new_regs_size = REGS_LIMIT; + } else { + new_regs_size = block->next_reg + REGS_EXPAND_SIZE; + } + RTLRegister * const new_regs = + realloc(block->regs, sizeof(*block->regs) * new_regs_size); + if (UNLIKELY(!new_regs)) { + DMSG("No memory to expand block %p to %d registers", + block, new_regs_size); + return 0; + } + block->regs = new_regs; + block->regs_size = new_regs_size; + } + + const unsigned int reg_index = block->next_reg++; + memset(&block->regs[reg_index], 0, sizeof(block->regs[reg_index])); + return reg_index; +} + +/*-----------------------------------------------------------------------*/ + +/** + * rtl_register_set_unique_pointer: Mark the given register as being a + * "unique pointer", which points to a region of memory which will never + * be accessed except through this register (or another register copied + * from it). This function must be called after adding the instruction + * which sets the register, and if the register's value is subsequently + * modified, its "unique pointer" status will be cancelled. + * + * [Parameters] + * block: RTLBlock containing register to mark + * regnum: Register number to mark + * [Return value] + * Nonzero on success, zero on error + */ +int rtl_register_set_unique_pointer(RTLBlock *block, uint32_t regnum) +{ + PRECOND(block != NULL, return 0); + PRECOND(!block->finalized, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(regnum != 0 && regnum < block->next_reg, return 0); + + if (block->unique_pointer_index == 0) { // i.e. it wrapped around + DMSG("Unique pointer index overflow at register r%u", regnum); + return 0; + } + block->regs[regnum].unique_pointer = block->unique_pointer_index++; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * rtl_alloc_label: Allocate a new label for use in the given block. + * + * [Parameters] + * block: RTLBlock to allocate a label for + * [Return value] + * Label number (nonzero) on success, zero on error + */ +unsigned int rtl_alloc_label(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(!block->finalized, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + if (UNLIKELY(block->next_label >= block->labels_size)) { + if (block->labels_size >= LABELS_LIMIT) { + DMSG("Too many labels in block %p (limit %u)", + block, LABELS_LIMIT); + return 0; + } + unsigned int new_labels_size; + if (block->labels_size > LABELS_LIMIT - LABELS_EXPAND_SIZE) { + new_labels_size = LABELS_LIMIT; + } else { + new_labels_size = block->next_label + LABELS_EXPAND_SIZE; + } + int16_t * const new_label_unitmap = + realloc(block->label_unitmap, + sizeof(*block->label_unitmap) * new_labels_size); + if (UNLIKELY(!new_label_unitmap)) { + DMSG("No memory to expand block %p to %d labels", block, + new_labels_size); + return 0; + } + block->label_unitmap = new_label_unitmap; + block->labels_size = new_labels_size; + } + + const unsigned int label = block->next_label++; + block->label_unitmap[label] = -1; + return label; +} + +/*************************************************************************/ + +/** + * rtl_finalize_block: Perform housekeeping at the end of the given + * block's translation. rtl_add_insn(), rtl_alloc_register(), and + * rtl_alloc_label() may not be called for a block after calling this + * function on the block. + * + * [Parameters] + * block: RTLBlock to finalize + * [Return value] + * Nonzero on success, zero on error + */ +int rtl_finalize_block(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(!block->finalized, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + /* Terminate the last unit (if there is one) */ + if (block->have_unit) { + block->units[block->cur_unit].last_insn = block->num_insns - 1; + block->have_unit = 0; + } + + /* Add execution graph edges for GOTO instructions */ + if (UNLIKELY(!add_unit_edges(block))) { + return 0; + } + + /* Update live ranges for registers used in loops */ + if (UNLIKELY(!update_live_ranges(block))) { + return 0; + } + + block->finalized = 1; +#ifdef RTL_TRACE_GENERATE + rtlunit_dump_all(block, NULL); + fprintf(stderr, "[RTL] Finalized block at %p\n", block); +#endif + return 1; +} + +/*************************************************************************/ + +/** + * rtl_optimize_block: Perform target-independent optimization on the + * given block. Before calling this function, rtl_finalize_block() must be + * called for the block. + * + * [Parameters] + * block: RTLBlock to optimize + * flags: RTLOPT_* flags indicating which optimizations to perform + * [Return value] + * Nonzero on success, zero on error + */ +int rtl_optimize_block(RTLBlock *block, uint32_t flags) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->finalized, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + if (flags & RTLOPT_FOLD_CONSTANTS) { + if (UNLIKELY(!rtlopt_fold_constants(block))) { + DMSG("Constant folding failed"); + return 0; + } + } + + if (flags & RTLOPT_DECONDITION) { + if (UNLIKELY(!rtlopt_decondition(block))) { + DMSG("Deconditioning failed"); + return 0; + } + } + + if (flags & RTLOPT_DECONDITION) { + if (UNLIKELY(!rtlopt_drop_dead_units(block))) { + DMSG("Dead unit dropping failed"); + return 0; + } + } + +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] Optimized block at %p\n", block); + dump_block(block, "optimize"); +#endif + return 1; +} + +/*************************************************************************/ + +/** + * rtl_translate_block: Translate the given block into native machine code. + * + * [Parameters] + * block: RTLBlock to translate + * code_ret: Pointer to variable to receive code buffer pointer + * size_ret: Pointer to variable to receive code buffer size (in bytes) + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * On error, *code_ret and *size_ret are not modified. + */ +int rtl_translate_block(RTLBlock *block, void **code_ret, uint32_t *size_ret) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->finalized, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(code_ret != NULL, return 0); + PRECOND(size_ret != NULL, return 0); + + int retval; + +#if defined(PSP) + retval = rtl_translate_block_mips(block, code_ret, size_ret); +#else + DMSG("Native code generation is not implemented for this platform"); + retval = 0; +#endif + +#ifdef RTL_TRACE_GENERATE + if (retval) { + fprintf(stderr, "[RTL] Translated block at %p to native code at %p" + " (size %u)\n", block, *code_ret, *size_ret); + } else { + fprintf(stderr, "[RTL] FAILED to translate block at %p\n", block); + } +#endif + return retval; +} + +/*************************************************************************/ + +/** + * rtl_destroy_block: Destroy the given block, freeing any resources it + * used. + * + * [Parameters] + * block: RTLBlock to destroy (if NULL, this function does nothing) + * [Return value] + * None + */ +void rtl_destroy_block(RTLBlock *block) +{ + if (block) { +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] Destroying block at %p\n", block); +#endif + free(block->insns); + free(block->insn_unitmap); + free(block->units); + free(block->regs); + free(block->label_unitmap); + free(block); + } +} + +/*************************************************************************/ +/*********************** Library-internal routines ***********************/ +/*************************************************************************/ + +#if defined(RTL_TRACE_GENERATE) || defined(RTL_TRACE_EXECUTE) + +/** + * rtl_decode_insn: Decode an RTL instruction into a human-readable + * string. + * + * [Parameters] + * block: RTLBlock containing instruction to decode + * index: Index of instruction to decode + * is_exec: Nonzero if being called from interpreted execution, else zero + * [Return value] + * Human-readable string describing the instruction + * [Notes] + * The returned string is stored in a static buffer which is + * overwritten on each call. + */ +const char *rtl_decode_insn(const RTLBlock *block, uint32_t index, int is_exec) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + /* We don't check the index because it'll be out of range when tracing + * code generation */ + + static const char * const opcode_names[] = { + [RTLOP_NOP ] = "NOP", + [RTLOP_MOVE ] = "MOVE", + [RTLOP_SELECT ] = "SELECT", + [RTLOP_ADD ] = "ADD", + [RTLOP_SUB ] = "SUB", + [RTLOP_MULU ] = "MULU", + [RTLOP_MULS ] = "MULS", + [RTLOP_MADDU ] = "MADDU", + [RTLOP_MADDS ] = "MADDS", + [RTLOP_DIVMODU ] = "DIVMODU", + [RTLOP_DIVMODS ] = "DIVMODS", + [RTLOP_AND ] = "AND", + [RTLOP_OR ] = "OR", + [RTLOP_XOR ] = "XOR", + [RTLOP_NOT ] = "NOT", + [RTLOP_SLL ] = "SLL", + [RTLOP_SRL ] = "SRL", + [RTLOP_SRA ] = "SRA", + [RTLOP_ROR ] = "ROR", + [RTLOP_CLZ ] = "CLZ", + [RTLOP_CLO ] = "CLO", + [RTLOP_SLTU ] = "SLTU", + [RTLOP_SLTS ] = "SLTS", + [RTLOP_BSWAPH ] = "BSWAPH", + [RTLOP_BSWAPW ] = "BSWAPW", + [RTLOP_HSWAPW ] = "HSWAPW", + [RTLOP_ADDI ] = "ADDI", + [RTLOP_ANDI ] = "ANDI", + [RTLOP_ORI ] = "ORI", + [RTLOP_XORI ] = "XORI", + [RTLOP_SLLI ] = "SLLI", + [RTLOP_SRLI ] = "SRLI", + [RTLOP_SRAI ] = "SRAI", + [RTLOP_RORI ] = "RORI", + [RTLOP_SLTUI ] = "SLTUI", + [RTLOP_SLTSI ] = "SLTSI", + [RTLOP_BFEXT ] = "BFEXT", + [RTLOP_BFINS ] = "BFINS", + [RTLOP_LOAD_IMM ] = "LOAD_IMM", + [RTLOP_LOAD_ADDR ] = "LOAD_ADDR", + [RTLOP_LOAD_PARAM ] = "LOAD_PARAM", + [RTLOP_LOAD_BS ] = "LOAD_BS", + [RTLOP_LOAD_BU ] = "LOAD_BU", + [RTLOP_LOAD_HS ] = "LOAD_HS", + [RTLOP_LOAD_HU ] = "LOAD_HU", + [RTLOP_LOAD_W ] = "LOAD_W", + [RTLOP_LOAD_PTR ] = "LOAD_PTR", + [RTLOP_STORE_B ] = "STORE_B", + [RTLOP_STORE_H ] = "STORE_H", + [RTLOP_STORE_W ] = "STORE_W", + [RTLOP_STORE_PTR ] = "STORE_PTR", + [RTLOP_LABEL ] = "LABEL", + [RTLOP_GOTO ] = "GOTO", + [RTLOP_GOTO_IF_Z ] = "GOTO_IF_Z", + [RTLOP_GOTO_IF_NZ ] = "GOTO_IF_NZ", + [RTLOP_GOTO_IF_E ] = "GOTO_IF_E", + [RTLOP_GOTO_IF_NE ] = "GOTO_IF_NE", + [RTLOP_CALL ] = "CALL", + [RTLOP_RETURN ] = "RETURN", + [RTLOP_RETURN_TO ] = "RETURN_TO", + }; + + static char buf[500]; + + const RTLInsn * const insn = &block->insns[index]; + const char * const name = opcode_names[insn->opcode]; + const unsigned int dest = insn->dest; + const unsigned int src1 = insn->src1; + const unsigned int src2 = insn->src2; + +#define APPEND_REG_DESC(regnum) \ + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n r%u: %s", \ + (regnum), rtl_describe_register(&block->regs[(regnum)], is_exec)); + + switch ((RTLOpcode)insn->opcode) { + + case RTLOP_NOP: + if (insn->src_imm) { + snprintf(buf, sizeof(buf), "%-11s 0x%X", name, insn->src_imm); + } else { + snprintf(buf, sizeof(buf), "%s", name); + } + return buf; + + case RTLOP_MOVE: + case RTLOP_NOT: + case RTLOP_CLZ: + case RTLOP_CLO: + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u", name, dest, src1); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_SELECT: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u, r%u", name, + dest, src1, src2, insn->cond); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + APPEND_REG_DESC(insn->cond); + return buf; + + case RTLOP_ADD: + case RTLOP_SUB: + case RTLOP_AND: + case RTLOP_OR: + case RTLOP_XOR: + case RTLOP_SLL: + case RTLOP_SRL: + case RTLOP_SRA: + case RTLOP_ROR: + case RTLOP_SLTU: + case RTLOP_SLTS: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u", name, dest, src1, + src2); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + return buf; + + case RTLOP_ADDI: + case RTLOP_SLTSI: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, %d", name, dest, src1, + insn->src_imm); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, %u", name, dest, src1, + insn->src_imm); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, 0x%X", name, dest, src1, + insn->src_imm); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_MULU: + case RTLOP_MULS: + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: + if (dest == 0) { + if (insn->dest2 == 0) { + snprintf(buf, sizeof(buf), "%-11s ---, r%u, r%u, ---", name, + src1, src2); + } else { + snprintf(buf, sizeof(buf), "%-11s ---, r%u, r%u, r%u", name, + src1, src2, insn->dest2); + } + } else { + if (insn->dest2 == 0) { + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u, ---", name, + dest, src1, src2); + } else { + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u, r%u", name, + dest, src1, src2, insn->dest2); + } + } + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + return buf; + + case RTLOP_MADDU: + case RTLOP_MADDS: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u, r%u", name, + dest, src1, src2, insn->dest2); + APPEND_REG_DESC(dest); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + APPEND_REG_DESC(insn->dest2); + return buf; + + case RTLOP_BFEXT: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, %u, %u", name, dest, + src1, insn->bitfield.start, insn->bitfield.count); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_BFINS: + snprintf(buf, sizeof(buf), "%-11s r%u, r%u, r%u, %u, %u", name, dest, + src1, src2, insn->bitfield.start, insn->bitfield.count); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + return buf; + + case RTLOP_LOAD_IMM: + if (insn->src_imm >= 0x10000 && insn->src_imm < 0xFFFF0000) { + snprintf(buf, sizeof(buf), "%-11s r%u, 0x%X", name, dest, + insn->src_imm); + } else { + snprintf(buf, sizeof(buf), "%-11s r%u, %d", name, dest, + (int32_t)insn->src_imm); + } + return buf; + + case RTLOP_LOAD_ADDR: + snprintf(buf, sizeof(buf), "%-11s r%u, 0x%lX", name, dest, + (unsigned long)insn->src_addr); + return buf; + + case RTLOP_LOAD_PARAM: + snprintf(buf, sizeof(buf), "%-11s r%u, params[%u]", name, dest, + insn->src_imm); + return buf; + + case RTLOP_LOAD_BU: + case RTLOP_LOAD_BS: + case RTLOP_LOAD_HU: + case RTLOP_LOAD_HS: + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + snprintf(buf, sizeof(buf), "%-11s r%u, %d(r%u)", name, dest, + insn->offset, src1); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_STORE_B: + case RTLOP_STORE_H: + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + snprintf(buf, sizeof(buf), "%-11s %d(r%u), r%u", name, + insn->offset, dest, src1); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(dest); + return buf; + + case RTLOP_LABEL: + case RTLOP_GOTO: + snprintf(buf, sizeof(buf), "%-11s L%u", name, insn->label); + return buf; + + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: + snprintf(buf, sizeof(buf), "%-11s L%u, r%u", name, insn->label, src1); + APPEND_REG_DESC(src1); + return buf; + + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: + snprintf(buf, sizeof(buf), "%-11s L%u, r%u, r%u", name, insn->label, + src1, src2); + APPEND_REG_DESC(src1); + APPEND_REG_DESC(src2); + return buf; + + case RTLOP_CALL: + if (insn->dest) { + int len = snprintf(buf, sizeof(buf), "%-11s r%u = r%u(", + name, dest, insn->target); + if (src1) { + len += snprintf(buf+len, sizeof(buf)-len, "r%u", src1); + if (src2) { + len += snprintf(buf+len, sizeof(buf)-len, ", r%u", src2); + } + } + snprintf(buf+len, sizeof(buf)-len, ")"); + } else { + int len = snprintf(buf, sizeof(buf), "%-11s r%u(", + name, insn->target); + if (src1) { + len += snprintf(buf+len, sizeof(buf)-len, "r%u", src1); + if (src2) { + len += snprintf(buf+len, sizeof(buf)-len, ", r%u", src2); + } + } + snprintf(buf+len, sizeof(buf)-len, ")"); + } + if (src1) { + APPEND_REG_DESC(src1); + if (src2) { + APPEND_REG_DESC(src2); + } + } + APPEND_REG_DESC(insn->target); + return buf; + + case RTLOP_RETURN: + snprintf(buf, sizeof(buf), "%-11s", name); + return buf; + + case RTLOP_RETURN_TO: + snprintf(buf, sizeof(buf), "%-11s r%u", name, insn->target); + APPEND_REG_DESC(insn->target); + return buf; + + } // switch (insn->opcode) + + snprintf(buf, sizeof(buf), "???"); + return buf; + +#undef APPEND_REG_DESC +} + +/*-----------------------------------------------------------------------*/ + +/** + * rtl_describe_register: Generate a string describing the contents of the + * given RTL register. + * + * [Parameters] + * reg: Register to describe + * is_exec: Nonzero if being called from interpreted execution, else zero + * [Return value] + * Human-readable string describing the register + * [Notes] + * The returned string is stored in a static buffer which is + * overwritten on each call. + */ +const char *rtl_describe_register(const RTLRegister *reg, int is_exec) +{ + PRECOND(reg != NULL, return ""); + + static char buf[100]; + if (is_exec || reg->source == RTLREG_CONSTANT) { + if ((intptr_t)reg->value >= 0x10000 + || (intptr_t)reg->value < -0x10000 + ) { + snprintf(buf, sizeof(buf), "0x%lX", (unsigned long)reg->value); + } else { + snprintf(buf, sizeof(buf), "%d", (int32_t)reg->value); + } + } else if (reg->source == RTLREG_PARAMETER) { + snprintf(buf, sizeof(buf), "param[%u]", reg->param_index); + } else if (reg->source == RTLREG_MEMORY) { + snprintf(buf, sizeof(buf), "(%ssigned) @(%d,r%u).%s", + reg->memory.is_signed ? "" : "un", reg->memory.offset, + reg->memory.addr_reg, + reg->memory.size==1 ? "b" : + reg->memory.size==2 ? "w" : + reg->memory.size==4 ? "l" : "ptr"); + } else if (reg->source == RTLREG_RESULT + || reg->source == RTLREG_RESULT_NOFOLD) { + static const char * const operators[] = { + [RTLOP_ADD ] = "+", + [RTLOP_SUB ] = "-", + [RTLOP_AND ] = "&", + [RTLOP_OR ] = "|", + [RTLOP_XOR ] = "^", + [RTLOP_SLL ] = "<<", + [RTLOP_SRL ] = ">>", + [RTLOP_SRA ] = ">>", + [RTLOP_ROR ] = "ROR", + [RTLOP_CLZ ] = "CLZ", + [RTLOP_CLO ] = "CLO", + [RTLOP_SLTU ] = "<", + [RTLOP_SLTS ] = "<", + [RTLOP_BSWAPH] = "BSWAPH", + [RTLOP_BSWAPW] = "BSWAPW", + [RTLOP_HSWAPW] = "HSWAPW", + [RTLOP_ADDI ] = "+", + [RTLOP_ANDI ] = "&", + [RTLOP_ORI ] = "|", + [RTLOP_XORI ] = "^", + [RTLOP_SLLI ] = "<<", + [RTLOP_SRLI ] = ">>", + [RTLOP_SRAI ] = ">>", + [RTLOP_RORI ] = "ROR", + [RTLOP_SLTUI ] = "<", + [RTLOP_SLTSI ] = "<", + }; + // 0x01: immediate operand is signed, 0x02: display "(signed)" + static const uint8_t is_signed[] = { + [RTLOP_SRA ] = 2, + [RTLOP_SLTS ] = 2, + [RTLOP_ADDI ] = 1, + [RTLOP_SRAI ] = 2, + [RTLOP_SLTSI] = 3, + [RTLOP_SLTUI] = 1, + }; + switch (reg->result.opcode) { + case RTLOP_MOVE: + snprintf(buf, sizeof(buf), "r%u", reg->result.src1); + break; + case RTLOP_SELECT: + snprintf(buf, sizeof(buf), "r%u ? r%u : r%u", reg->result.cond, + reg->result.src1, reg->result.src2); + break; + case RTLOP_NOT: + snprintf(buf, sizeof(buf), "~r%u", reg->result.src1); + break; + case RTLOP_CLZ: + case RTLOP_CLO: + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: + snprintf(buf, sizeof(buf), "%s(r%u)", + operators[reg->result.opcode], reg->result.src1); + break; + case RTLOP_ADD: + case RTLOP_SUB: + case RTLOP_AND: + case RTLOP_OR: + case RTLOP_XOR: + case RTLOP_SLL: + case RTLOP_SRL: + case RTLOP_SRA: + case RTLOP_ROR: + case RTLOP_SLTU: + case RTLOP_SLTS: + snprintf(buf, sizeof(buf), "%sr%u %s r%u", + is_signed[reg->result.opcode] & 2 ? "(signed) " : "", + reg->result.src1, operators[reg->result.opcode], + reg->result.src2); + break; + case RTLOP_ADDI: + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + case RTLOP_SLTSI: + snprintf(buf, sizeof(buf), "%sr%u %s ", + is_signed[reg->result.opcode] & 2 ? "(signed) " : "", + reg->result.src1, operators[reg->result.opcode]); + if ((is_signed[reg->result.opcode] & 1) + && (int32_t)reg->result.imm >= -0x8000 + && (int32_t)reg->result.imm <= 0xFFFF + ) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%d", + (int)reg->result.imm); + } else { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "0x%X", + (int)reg->result.imm); + } + break; + case RTLOP_MULU: + case RTLOP_MULS: + snprintf(buf, sizeof(buf), "%s%sr%u * r%u%s", + reg->result.opcode == RTLOP_MULS ? "(signed) " : "", + reg->result.second_res ? "(" : "", + reg->result.src1, reg->result.src2, + reg->result.second_res ? ") >> 32" : ""); + break; + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: + snprintf(buf, sizeof(buf), "%sr%u %c r%u", + reg->result.opcode == RTLOP_DIVMODS ? "(signed) " : "", + reg->result.src1, reg->result.second_res ? '%' : '/', + reg->result.src2); + break; + case RTLOP_BFEXT: + snprintf(buf, sizeof(buf), "BFEXT(r%u, %u, %u)", + reg->result.src1, reg->result.start, reg->result.count); + break; + case RTLOP_BFINS: + snprintf(buf, sizeof(buf), "BFINS(r%u, r%u, %u, %u)", + reg->result.src1, reg->result.src2, + reg->result.start, reg->result.count); + break; + default: + snprintf(buf, sizeof(buf), "???"); + break; + } // switch (reg->result.opcode) + } else { + snprintf(buf, sizeof(buf), "???"); + } + return buf; +} + +#endif // RTL_TRACE_GENERATE || RTL_TRACE_EXECUTE + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * add_unit_edges: Add edges between basic units for GOTO instructions. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * Execution time is O(n) in the number of basic units. + */ +static int add_unit_edges(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + unsigned int unit_index; + for (unit_index = 0; unit_index < block->num_units; unit_index++) { + RTLUnit * const unit = &block->units[unit_index]; + if (unit->first_insn <= unit->last_insn) { + const RTLInsn * const insn = &block->insns[unit->last_insn]; + if (insn->opcode == RTLOP_GOTO + || insn->opcode == RTLOP_GOTO_IF_Z + || insn->opcode == RTLOP_GOTO_IF_NZ + || insn->opcode == RTLOP_GOTO_IF_E + || insn->opcode == RTLOP_GOTO_IF_NE + ) { + const unsigned int label = insn->label; + if (UNLIKELY(block->label_unitmap[label] < 0)) { + DMSG("%p/%u: GOTO to unknown label %u", block, + unit->last_insn, label); + return 0; + } else if (UNLIKELY(!rtlunit_add_edge(block, unit_index, block->label_unitmap[label]))) { + DMSG("%p: Failed to add edge %u->%u for %s L%u", block, + unit_index, block->label_unitmap[label], + insn->opcode == RTLOP_GOTO ? "GOTO" : + insn->opcode == RTLOP_GOTO_IF_Z ? "GOTO_IF_Z" : + insn->opcode == RTLOP_GOTO_IF_NZ ? "GOTO_IF_NZ" : + insn->opcode == RTLOP_GOTO_IF_E ? "GOTO_IF_E" : + "GOTO_IF_NE", + label); + return 0; + } + } + } + } + + return 1; +} + +/*************************************************************************/ + +/** + * update_live_ranges: Update the live range of any register live at the + * beginning unit targeted by a backward branch so that the register is + * live through all branches that target the unit. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + * [Notes] + * Worst-case execution time is O(n*m) in the number of units (n) and + * the number of registers (m). However, the register scan is only + * required for units targeted by backward branches, and terminates at + * the first register born within or after the targeted unit. + */ +static int update_live_ranges(RTLBlock * const block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + + int unit_index; + for (unit_index = 0; unit_index < block->num_units; unit_index++) { + const RTLUnit * const unit = &block->units[unit_index]; + int latest_entry_unit = -1; + unsigned int i; + for (i = 0; i < lenof(unit->entries) && unit->entries[i] >= 0; i++) { + if (unit->entries[i] > latest_entry_unit) { + latest_entry_unit = unit->entries[i]; + } + } + if (latest_entry_unit >= unit_index + && block->units[latest_entry_unit].last_insn >= 0 // Just in case + ) { + const uint32_t birth_limit = unit->first_insn; + const uint32_t min_death = + block->units[latest_entry_unit].last_insn; + unsigned int reg; + for (reg = block->first_live_reg; + reg != 0 && block->regs[reg].birth < birth_limit; + reg = block->regs[reg].live_link + ) { + if (block->regs[reg].death >= birth_limit + && block->regs[reg].death < min_death + ) { + block->regs[reg].death = min_death; + } + } + } + } + + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +#ifdef RTL_TRACE_GENERATE + +/** + * dump_block: Dump the contents of an RTL block to stderr. + * + * [Parameters] + * block: RTL block + * tag: Tag to prepend to all lines, or NULL for none + * [Return value] + * None + */ +static void dump_block(const RTLBlock * const block, const char * const tag) +{ + PRECOND(block != NULL, return); + + uint32_t insn_index; + for (insn_index = 0; insn_index < block->num_insns; insn_index++) { + fprintf(stderr, "[RTL] %s%s%s%p/%5u: %s\n", + tag ? "[" : "", tag ? tag : "", tag ? "] " : "", + block, insn_index, rtl_decode_insn(block, insn_index, 0)); + } + + rtlunit_dump_all(block, tag); +} + +#endif // RTL_TRACE_GENERATE + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtl.h b/yabause/src/psp/rtl.h new file mode 100644 index 0000000000..aed1d5fabb --- /dev/null +++ b/yabause/src/psp/rtl.h @@ -0,0 +1,314 @@ +/* src/psp/rtl.h: Declarations for register transfer language used in + dynamic translation + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef RTL_H +#define RTL_H + +/*************************************************************************/ + +/** + * RTLBlock: State information used in translating a block of code. + * Opaque to callers. + */ +typedef struct RTLBlock_ RTLBlock; + +/*-----------------------------------------------------------------------*/ + +/** + * RTLOpcode: Enumeration of operations, used as the value of the + * RTLInsn.op field. + */ +typedef enum RTLOpcode_ { + /* Zero is invalid */ + + /* No operation */ + RTLOP_NOP = 1, // [No operation -- note that a value can be given + // in src1 for debugging purposes] + + /* Register-register operations */ + RTLOP_MOVE, // dest = src1 + RTLOP_SELECT, // dest = other ? src1 : src2 + RTLOP_ADD, // dest = src1 + src2 + RTLOP_SUB, // dest = src1 - src2 + RTLOP_MULU, // (uint64_t){other,dest} = + // (unsigned)src1 * (unsigned)src2 + // [upper 32 bits of result stored in "other", + // lower 32 bits of result stored in "dest"; + // either dest or other may be zero = omitted] + RTLOP_MULS, // (int64_t){other,dest} = (signed)src1 * (signed)src2 + // [either dest or other may be zero = omitted] + RTLOP_MADDU, // (uint64_t){other,dest} += + // (unsigned)src1 * (unsigned)src2 + // [other/dest are both inputs and outputs; for + // optimal performance, they should be the same + // registers used as outputs of a MULU/MULS or + // previous MADDU/MADDS insn] + RTLOP_MADDS, // (int64_t){other,dest} += (signed)src1 * (signed)src2 + RTLOP_DIVMODU, // dest = (unsigned)src1 / (unsigned)src2; + // other = (unsigned)src1 % (unsigned)src2 + // [both undefined if src2 == 0] + // [either dest or other may be zero = omitted] + RTLOP_DIVMODS, // dest = (signed)src1 / (signed)src2; + // other = (signed)src1 % (signed)src2 + // [both undefined if src2 == 0] + // [either dest or other may be zero = omitted] + RTLOP_AND, // dest = src1 & src2 + RTLOP_OR, // dest = src1 | src2 + RTLOP_XOR, // dest = src1 ^ src2 + RTLOP_NOT, // dest = ~src1 + RTLOP_SLL, // dest = src1 << src2 [undefined when src2 >= 32] + RTLOP_SRL, // dest = (unsigned)src1 >> src2 + // [undefined when src2 >= 32] + RTLOP_SRA, // dest = (signed)src1 >> src2 + // [undefined when src2 >= 32] + RTLOP_ROR, // dest = src1 ROR (src2 % 32) [in 32 bits] + RTLOP_CLZ, // dest = [number of leading zeros in src1] + RTLOP_CLO, // dest = [number of leading ones in src1] + RTLOP_SLTU, // dest = (unsigned)src1 < (unsigned)src2 ? 1 : 0 + RTLOP_SLTS, // dest = (signed)src1 < (signed)src2 ? 1 : 0 + RTLOP_BSWAPH, // dest = [swap adjacent pairs of bytes in src1] + RTLOP_BSWAPW, // dest = [reverse order of sets of 4 bytes in src1] + RTLOP_HSWAPW, // dest = [swap adjacent pairs of halfword in src1] + + /* Register-immediate operations */ + RTLOP_ADDI, // dest = src1 + IMMEDIATE(src2) + RTLOP_ANDI, // dest = src1 & IMMEDIATE(src2) + RTLOP_ORI, // dest = src1 | IMMEDIATE(src2) + RTLOP_XORI, // dest = src1 ^ IMMEDIATE(src2) + RTLOP_SLLI, // dest = src1 << IMMEDIATE(src2) + RTLOP_SRLI, // dest = (unsigned)src1 >> IMMEDIATE(src2) + RTLOP_SRAI, // dest = (signed)src1 << IMMEDIATE(src2) + RTLOP_RORI, // dest = src1 ROR IMMEDIATE(src2) [in 32 bits] + RTLOP_SLTUI, // dest = (unsigned)src1 < (unsigned)IMMEDIATE(src2) + // ? 1 : 0 + RTLOP_SLTSI, // dest = (signed)src1 < (signed)IMMEDIATE(src2) + // ? 1 : 0 + + /* Bitfield operations ("start" and "count" are encoded in the "other" + * parameter as: other = start | count<<8) */ + RTLOP_BFEXT, // dest = (src1 >> start) & ((1<finalized, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(state != NULL, return 0); + + /* Create the instruction-to-unit lookup table if it hasn't been + * created yet */ + if (UNLIKELY(!block->insn_unitmap)) { + if (UNLIKELY(!create_insn_unitmap(block))) { + DMSG("%p: Failed to create insn-to-unit lookup table", block); + return 0; + } + } + +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + /* Clear the SH-2 register cache bitmask */ + block->sh2_regcache_mask = 0; +#endif + + /* Actually execute instructions */ + uint32_t insn_count = 0; + uint32_t index = 0; + while (index < block->num_insns) { + insn_count++; + if (!rtl_execute_insn(block, &index, state)) { + break; + } + } + return insn_count; +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * create_insn_unitmap: Create a lookup table mapping instruction indices to + * unit indices, used by rtl_execute_block() to find the unit containing + * the first instruction to execute. + * + * [Parameters] + * block: RTLBlock to create table for + * [Return value] + * Nonzero on success, zero on error + */ +static int create_insn_unitmap(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->units != NULL, return 0); + + block->insn_unitmap = + malloc(sizeof(*block->insn_unitmap) * block->num_insns); + if (UNLIKELY(!block->insn_unitmap)) { + DMSG("No memory for insn-to-unit lookup table (%lu bytes)", + (unsigned long)(sizeof(*block->insn_unitmap) * block->num_insns)); + return 0; + } + memset(block->insn_unitmap, -1, + sizeof(*block->insn_unitmap) * block->num_insns); + + unsigned int unit_index; + for (unit_index = 0; unit_index < block->num_units; unit_index++) { + uint32_t insn_index; + for (insn_index = block->units[unit_index].first_insn; + (int32_t)insn_index <= block->units[unit_index].last_insn; + insn_index++ + ) { + block->insn_unitmap[insn_index] = unit_index; + } + } + + return 1; +} + +/*************************************************************************/ + +/** + * rtl_execute_insn: Execute a single RTL instruction from the given + * block. + * + * [Parameters] + * block: RTLBlock to execute + * index_ptr: Pointer to variable holding index of instruction to + * execute; incremented on return to indicate the next + * instruction to execute + * state: SH-2 state block + * [Return value] + * Nonzero to continue execution, zero to terminate execution + */ +static inline int rtl_execute_insn(RTLBlock *block, uint32_t *index_ptr, + void *state) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(index_ptr != NULL, return 0); + PRECOND(*index_ptr < block->num_insns, return 0); + PRECOND(state != NULL, return 0); + +#ifdef RTL_TRACE_EXECUTE + fprintf(stderr, "%p/%u: %s\n", block, *index_ptr, + rtl_decode_insn(block, *index_ptr, 1)); +#endif + + const RTLInsn * const insn = &block->insns[*index_ptr]; + const RTLUnit * const unit = &block->units[block->insn_unitmap[*index_ptr]]; + if (*index_ptr < unit->last_insn) { + (*index_ptr)++; + } else { + int next_unit = unit->next_unit; + while (next_unit >= 0 && block->units[next_unit].first_insn + > block->units[next_unit].last_insn) { + next_unit = block->units[next_unit].next_unit; + } + if (next_unit >= 0) { + *index_ptr = block->units[next_unit].first_insn; + } else { + *index_ptr = block->num_insns; // Terminate execution + } + } + + RTLRegister * const dest = &block->regs[insn->dest]; + const RTLRegister * const src1 = &block->regs[insn->src1]; + const RTLRegister * const src2 = &block->regs[insn->src2]; + + /*------------------------------*/ + + switch ((RTLOpcode)insn->opcode) { + + case RTLOP_NOP: +#ifdef RTL_TRACE_STEALTH_FOR_SH2 + if (insn->src_imm >> 28 == 0x8 || insn->src_imm >> 28 == 0xA) { + /* Trace an instruction */ + + SH2State *sh2 = (SH2State *)state; + uint32_t *sh2_regs = (uint32_t *)&sh2; + uint32_t saveregs[23]; + memcpy(saveregs, sh2_regs, sizeof(saveregs)); + uint32_t mask = block->sh2_regcache_mask; + unsigned int sh2_reg; + for (sh2_reg = 0; mask != 0; mask >>= 1, sh2_reg++) { + if (mask & 1) { + sh2_regs[sh2_reg] = block->sh2_regcache[sh2_reg]; + } + } + uint32_t op = block->insns[(*index_ptr)++].src_imm; + unsigned int do_trace = 1; + if (op>>24 == 0x9F) { // Conditional execution + const unsigned int cond_reg = op & 0xFFFF; + const unsigned int test_bit = op>>16 & 1; + do_trace = (block->regs[cond_reg].value == test_bit); + op = block->insns[(*index_ptr)++].src_imm; + } + uint32_t cached_cycles = op; + PRECOND(cached_cycles>>24 == 0x98, return 1); + cached_cycles &= 0xFFFF; + uint32_t cached_cycle_reg = block->insns[(*index_ptr)++].src_imm; + PRECOND(cached_cycles>>24 == 0x90, return 1); + cached_cycle_reg &= 0xFFFF; + if (cached_cycle_reg) { + cached_cycles += block->regs[cached_cycle_reg].value; + } else { + cached_cycles += sh2->cycles; + } + const uint32_t old_cycles = sh2->cycles; + sh2->cycles = cached_cycles; + if (do_trace) { + (*trace_insn_callback)(sh2, insn->src_imm & 0x7FFFFFFF); + } + sh2->cycles = old_cycles; + memcpy(sh2_regs, saveregs, sizeof(saveregs)); + + } else if (insn->src_imm >> 28 == 0xB) { + /* Record a cached SH-2 register value */ + + uint32_t op = insn->src_imm; + uint32_t offset = 0; + if (op & 0x08000000) { + PRECOND(op>>24 == 0xB8, return 1); + offset = (op & 0xFFFF) << 16; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>24 == 0xBC, return 1); + offset |= op & 0xFFFF; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>24 == 0xB0, return 1); + } + unsigned int sh2_reg = (op >> 16) & 0xFF; + unsigned int rtl_reg = op & 0xFFFF; + if (rtl_reg) { + if (sh2_reg & 0x80) { // Used for SR.T + if (!(block->sh2_regcache_mask & 1<<16)) { + SH2State *sh2 = (SH2State *)state; + block->sh2_regcache[16] = sh2->SR & ~SR_T; + block->sh2_regcache_mask |= 1<<16; + } else { + block->sh2_regcache[16] &= ~SR_T; + } + block->sh2_regcache[16] |= block->regs[rtl_reg].value; + } else { + block->sh2_regcache[sh2_reg] = + block->regs[rtl_reg].value + offset; + block->sh2_regcache_mask |= 1<sh2_regcache[sh2_reg] = offset; + block->sh2_regcache_mask |= 1<sh2_regcache_mask &= ~(1<src_imm >> 28 == 0xC) { + /* Trace a store operation */ + + uint32_t op; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>28 == 0xD, return 1); + uint32_t address; + if (op & 0x08000000) { + PRECOND(op>>24 == 0xD8, return 1); + address = (op & 0xFFFF) << 16; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>24 == 0xDC, return 1); + address |= op & 0xFFFF; + } else { + address = block->regs[op & 0xFFFF].value; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>24 == 0xD4, return 1); + address += (op & 0xFFFF) << 16; + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>24 == 0xD6, return 1); + address += op & 0xFFFF; + } + op = block->insns[(*index_ptr)++].src_imm; + PRECOND(op>>28 == 0xE, return 1); + const uint32_t src = block->regs[op & 0xFFFF].value; + switch (insn->src_imm & 0xFFFF) { + case 1: (*trace_storeb_callback)(address, src); break; + case 2: (*trace_storew_callback)(address, src); break; + case 4: (*trace_storel_callback)(address, src); break; + default: + DMSG("Invalid store trace type %u", insn->src_imm & 0xFF); + break; + } + + } +#endif // RTL_TRACE_STEALTH_FOR_SH2 + return 1; + + /*----------------------------*/ + + case RTLOP_MOVE: + dest->value = src1->value; + return 1; + + case RTLOP_SELECT: + dest->value = + block->regs[insn->cond].value ? src1->value : src2->value; + return 1; + + case RTLOP_ADD: + dest->value = src1->value + (intptr_t)(int32_t)src2->value; + return 1; + + case RTLOP_SUB: + dest->value = src1->value - (intptr_t)(int32_t)src2->value; + return 1; + + case RTLOP_MULU: { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + dest->value = (uint32_t)(src1->value * src2->value); + dest2->value = (uint64_t)((uint64_t)(uint32_t)src1->value + * (uint64_t)(uint32_t)src2->value) >> 32; + return 1; + } + + case RTLOP_MULS: { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + dest->value = (uint32_t)(src1->value * src2->value); + dest2->value = (int64_t)((int64_t)(int32_t)src1->value + * (int64_t)(int32_t)src2->value) >> 32; + return 1; + } + + case RTLOP_MADDU: { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + uint64_t initial = (uint64_t)(uint32_t)dest2->value << 32 + | (uint64_t)(uint32_t)dest->value; + uint64_t product = (uint64_t)(uint32_t)src1->value + * (uint64_t)(uint32_t)src2->value; + uint64_t result = initial + product; + dest->value = (uint32_t)result; + dest2->value = (uint32_t)(result >> 32); + return 1; + } + + case RTLOP_MADDS: { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + int64_t initial = (int64_t)(int32_t)dest2->value << 32 + | (int64_t)(uint32_t)dest->value; // unsigned! + int64_t product = (int64_t)(int32_t)src1->value + * (int64_t)(int32_t)src2->value; + int64_t result = initial + product; + dest->value = (uint32_t)result; + dest2->value = (uint32_t)(result >> 32); + return 1; + } + + case RTLOP_DIVMODU: + if (src2->value) { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + dest->value = (uint32_t)src1->value / (uint32_t)src2->value; + dest2->value = (uint32_t)src1->value % (uint32_t)src2->value; + } + return 1; + + case RTLOP_DIVMODS: + if (src2->value) { + RTLRegister * const dest2 = &block->regs[insn->dest2]; + dest->value = (int32_t)src1->value / (int32_t)src2->value; + dest2->value = (int32_t)src1->value % (int32_t)src2->value; + } + return 1; + + case RTLOP_AND: + dest->value = src1->value & (intptr_t)(int32_t)src2->value; + return 1; + + case RTLOP_OR: + dest->value = src1->value | (uintptr_t)(uint32_t)src2->value; + return 1; + + case RTLOP_XOR: + dest->value = src1->value ^ (uintptr_t)(uint32_t)src2->value; + return 1; + + case RTLOP_NOT: + dest->value = (uint32_t)(~src1->value); + return 1; + + case RTLOP_SLL: + if (src2->value < 32) { + dest->value = (uint32_t)(src1->value << src2->value); + } else { + dest->value = 0; + } + return 1; + + case RTLOP_SRL: + if (src2->value < 32) { + dest->value = (uint32_t)src1->value >> src2->value; + } else { + dest->value = 0; + } + return 1; + + case RTLOP_SRA: + if (src2->value < 32) { + dest->value = (int32_t)src1->value >> src2->value; + } else { + dest->value = (int32_t)src1->value >> 31; + } + return 1; + + case RTLOP_ROR: + if ((src2->value & 31) != 0) { + dest->value = (uint32_t)src1->value >> (src2->value & 31) + | (uint32_t)src1->value << (32 - (src2->value & 31)); + } else { + dest->value = (uint32_t)src1->value; + } + return 1; + + case RTLOP_CLZ: { +#ifdef __GNUC__ + dest->value = __builtin_clz(src1->value); +#else + uint32_t temp = src1->value; + dest->value = 32; + while (temp) { + temp >>= 1; + dest->value--; + } +#endif + return 1; + } + + case RTLOP_CLO: { + uint32_t temp = src1->value; + dest->value = 0; + while ((int32_t)temp < 0) { + temp <<= 1; + dest->value++; + } + return 1; + } + + case RTLOP_SLTU: + dest->value = ((uint32_t)src1->value < (uint32_t)src2->value) ? 1 : 0; + return 1; + + case RTLOP_SLTS: + dest->value = ((int32_t)src1->value < (int32_t)src2->value) ? 1 : 0; + return 1; + + case RTLOP_BSWAPH: + dest->value = ((uint32_t)src1->value & 0xFF00FF00) >> 8 + | ((uint32_t)src1->value & 0x00FF00FF) << 8; + return 1; + + case RTLOP_BSWAPW: + dest->value = ((uint32_t)src1->value & 0xFF000000) >> 24 + | ((uint32_t)src1->value & 0x00FF0000) >> 8 + | ((uint32_t)src1->value & 0x0000FF00) << 8 + | ((uint32_t)src1->value & 0x000000FF) << 24; + return 1; + + case RTLOP_HSWAPW: + dest->value = ((uint32_t)src1->value & 0xFFFF0000) >> 16 + | ((uint32_t)src1->value & 0x0000FFFF) << 16; + return 1; + + /*----------------------------*/ + + case RTLOP_ADDI: + dest->value = src1->value + (intptr_t)(int32_t)insn->src_imm; + return 1; + + case RTLOP_ANDI: + dest->value = src1->value & (intptr_t)(int32_t)insn->src_imm; + return 1; + + case RTLOP_ORI: + dest->value = src1->value | (uintptr_t)(uint32_t)insn->src_imm; + return 1; + + case RTLOP_XORI: + dest->value = src1->value ^ (uintptr_t)(uint32_t)insn->src_imm; + return 1; + + case RTLOP_SLLI: + if (insn->src_imm < 32) { + dest->value = (uint32_t)(src1->value << insn->src_imm); + } else { + dest->value = 0; + } + return 1; + + case RTLOP_SRLI: + if (insn->src_imm < 32) { + dest->value = (uint32_t)src1->value >> insn->src_imm; + } else { + dest->value = 0; + } + return 1; + + case RTLOP_SRAI: + if (insn->src_imm < 32) { + dest->value = (int32_t)src1->value >> insn->src_imm; + } else { + dest->value = (int32_t)src1->value >> 31; + } + return 1; + + case RTLOP_RORI: + if ((insn->src_imm & 31) != 0) { + dest->value = (uint32_t)src1->value >> (insn->src_imm & 31) + | (uint32_t)src1->value << (32 - (insn->src_imm & 31)); + } else { + dest->value = (uint32_t)src1->value; + } + return 1; + + case RTLOP_SLTUI: + dest->value = ((uint32_t)src1->value < (uint32_t)insn->src_imm) ? 1 : 0; + return 1; + + case RTLOP_SLTSI: + dest->value = ((int32_t)src1->value < (int32_t)insn->src_imm) ? 1 : 0; + return 1; + + /*----------------------------*/ + + case RTLOP_BFEXT: + dest->value = ((uint32_t)src1->value >> insn->bitfield.start) + & ((1 << insn->bitfield.count) - 1); + return 1; + + case RTLOP_BFINS: + dest->value = ((uint32_t)src1->value + & ~(((1 << insn->bitfield.count) - 1) + << insn->bitfield.start)) + | (((uint32_t)src2->value + & ((1 << insn->bitfield.count) - 1)) + << insn->bitfield.start); + return 1; + + /*----------------------------*/ + + case RTLOP_LOAD_IMM: + dest->value = insn->src_imm; + return 1; + + case RTLOP_LOAD_ADDR: + dest->value = insn->src_addr; + return 1; + + case RTLOP_LOAD_PARAM: + if (insn->src_imm == 0) { + dest->value = (uintptr_t)state; + } else { + DMSG("LOAD_PARAM for undefined parameter %u", + (unsigned int)insn->src_imm); + dest->value = 0xDEADF00D; + } + return 1; + + case RTLOP_LOAD_BU: + dest->value = *(uint8_t *)(src1->value + insn->offset); + return 1; + + case RTLOP_LOAD_BS: + dest->value = *(int8_t *)(src1->value + insn->offset); + return 1; + + case RTLOP_LOAD_HU: + dest->value = *(uint16_t *)(src1->value + insn->offset); + return 1; + + case RTLOP_LOAD_HS: + dest->value = *(int16_t *)(src1->value + insn->offset); + return 1; + + case RTLOP_LOAD_W: + dest->value = *(uint32_t *)(src1->value + insn->offset); + return 1; + + case RTLOP_LOAD_PTR: + dest->value = *(uintptr_t *)(src1->value + insn->offset); + return 1; + + /*----------------------------*/ + + case RTLOP_STORE_B: + *(uint8_t *)(dest->value + insn->offset) = src1->value; + return 1; + + case RTLOP_STORE_H: + *(uint16_t *)(dest->value + insn->offset) = src1->value; + return 1; + + case RTLOP_STORE_W: + *(uint32_t *)(dest->value + insn->offset) = src1->value; + return 1; + + case RTLOP_STORE_PTR: + *(uintptr_t *)(dest->value + insn->offset) = src1->value; + return 1; + + /*----------------------------*/ + + case RTLOP_LABEL: + return 1; + + case RTLOP_GOTO: + do_goto: + if (insn->label < 1 || insn->label > block->next_label) { + DMSG("%p/%u: label %u out of range", + block, (int)(insn - block->insns), insn->label); + } else if (block->label_unitmap[insn->label] < 0) { + DMSG("%p/%u: label %u not defined", + block, (int)(insn - block->insns), insn->label); + } else { + *index_ptr = + block->units[block->label_unitmap[insn->label]].first_insn; + } + return 1; + + case RTLOP_GOTO_IF_Z: + if (src1->value == 0) { + goto do_goto; + } + return 1; + + case RTLOP_GOTO_IF_NZ: + if (src1->value != 0) { + goto do_goto; + } + return 1; + + case RTLOP_GOTO_IF_E: + if (src1->value == src2->value) { + goto do_goto; + } + return 1; + + case RTLOP_GOTO_IF_NE: + if (src1->value != src2->value) { + goto do_goto; + } + return 1; + + /*----------------------------*/ + + case RTLOP_CALL: { + /* The called function must take pointer-sized parameters and + * return a pointer-sized value. */ + const void *funcptr = (const void *)block->regs[insn->target].value; + if (insn->dest) { + if (insn->src1) { + if (insn->src2) { + FASTCALL uintptr_t (*func)(uintptr_t, uintptr_t) = funcptr; + dest->value = (*func)(src1->value, src2->value); + } else { + FASTCALL uintptr_t (*func)(uintptr_t) = funcptr; + dest->value = (*func)(src1->value); + } + } else { + FASTCALL uintptr_t (*func)(void) = funcptr; + dest->value = (*func)(); + } + } else { + if (insn->src1) { + if (insn->src2) { + FASTCALL void (*func)(uintptr_t, uintptr_t) = funcptr; + (*func)(src1->value, src2->value); + } else { + FASTCALL void (*func)(uintptr_t) = funcptr; + (*func)(src1->value); + } + } else { + FASTCALL void (*func)(void) = funcptr; + (*func)(); + } + } + return 1; + } + + /*----------------------------*/ + + case RTLOP_RETURN: + return 0; + + case RTLOP_RETURN_TO: { + RTLBlock *newblock = (RTLBlock *)block->regs[insn->target].value; + /* Assume we won't fill up the stack doing this... */ + rtl_execute_block(newblock, state); + return 0; + } + + } // switch (insn->opcode) + + /*------------------------------*/ + + DMSG("Block %p index %u: invalid opcode %u", block, *index_ptr, + insn->opcode); + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtlinsn.c b/yabause/src/psp/rtlinsn.c new file mode 100644 index 0000000000..e11dcfb9e4 --- /dev/null +++ b/yabause/src/psp/rtlinsn.c @@ -0,0 +1,935 @@ +/* src/psp/rtlinsn.c: Instruction encoding routines for RTL + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ + +/* + * This source file defines functions for encoding RTL instructions into + * the RTLInsn structure based on the type of instruction (2-operand ALU, + * memory load, and so on). These functions are not exported directly, but + * are instead called through a lookup table by rtlinsn_make(), an inline + * function defined in rtl-internal.h. + * + * Both the inlining of rtlinsn_make() and the requirement of preloading + * the opcode into insn->opcode (and then taking the RTLInsn pointer in + * place of the opcode parameter to rtl_add_insn()) are to help + * optimization of the fast path; for example, on MIPS architectures, + * rtl_add_insn() can directly call the encoding function in this file + * without having to pass through a call to rtlinsn_make() or reload the + * parameter registers ($a0-$t1). + */ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "rtl.h" +#include "rtl-internal.h" + +/*************************************************************************/ +/************************** Local declarations ***************************/ +/*************************************************************************/ + +/* Encoding functions for specific opcode groups */ + +static int make_nop(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_alu_1op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_alu_2op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_alui_2op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_alu_2op_2dest(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_select(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_madd(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_bitfield(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_load_imm(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_load_addr(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_load_param(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_load(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_store(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_label(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_goto(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_goto_cond(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_goto_cond2(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_call(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_return(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); +static int make_return_to(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other); + + +/* Encoding function table */ + +int (* const makefunc_table[])(RTLBlock *, RTLInsn *, unsigned int, + uintptr_t, uint32_t, unsigned int) = { + [RTLOP_NOP ] = make_nop, + [RTLOP_MOVE ] = make_alu_1op, + [RTLOP_SELECT ] = make_select, + [RTLOP_ADD ] = make_alu_2op, + [RTLOP_SUB ] = make_alu_2op, + [RTLOP_MULU ] = make_alu_2op_2dest, + [RTLOP_MULS ] = make_alu_2op_2dest, + [RTLOP_MADDU ] = make_madd, + [RTLOP_MADDS ] = make_madd, + [RTLOP_DIVMODU ] = make_alu_2op_2dest, + [RTLOP_DIVMODS ] = make_alu_2op_2dest, + [RTLOP_AND ] = make_alu_2op, + [RTLOP_OR ] = make_alu_2op, + [RTLOP_XOR ] = make_alu_2op, + [RTLOP_NOT ] = make_alu_1op, + [RTLOP_SLL ] = make_alu_2op, + [RTLOP_SRL ] = make_alu_2op, + [RTLOP_SRA ] = make_alu_2op, + [RTLOP_ROR ] = make_alu_2op, + [RTLOP_CLZ ] = make_alu_1op, + [RTLOP_CLO ] = make_alu_1op, + [RTLOP_SLTU ] = make_alu_2op, + [RTLOP_SLTS ] = make_alu_2op, + [RTLOP_BSWAPH ] = make_alu_1op, + [RTLOP_BSWAPW ] = make_alu_1op, + [RTLOP_HSWAPW ] = make_alu_1op, + [RTLOP_ADDI ] = make_alui_2op, + [RTLOP_ANDI ] = make_alui_2op, + [RTLOP_ORI ] = make_alui_2op, + [RTLOP_XORI ] = make_alui_2op, + [RTLOP_SLLI ] = make_alui_2op, + [RTLOP_SRLI ] = make_alui_2op, + [RTLOP_SRAI ] = make_alui_2op, + [RTLOP_RORI ] = make_alui_2op, + [RTLOP_SLTUI ] = make_alui_2op, + [RTLOP_SLTSI ] = make_alui_2op, + [RTLOP_BFEXT ] = make_bitfield, + [RTLOP_BFINS ] = make_bitfield, + [RTLOP_LOAD_IMM ] = make_load_imm, + [RTLOP_LOAD_ADDR ] = make_load_addr, + [RTLOP_LOAD_PARAM ] = make_load_param, + [RTLOP_LOAD_BS ] = make_load, + [RTLOP_LOAD_BU ] = make_load, + [RTLOP_LOAD_HS ] = make_load, + [RTLOP_LOAD_HU ] = make_load, + [RTLOP_LOAD_W ] = make_load, + [RTLOP_LOAD_PTR ] = make_load, + [RTLOP_STORE_B ] = make_store, + [RTLOP_STORE_H ] = make_store, + [RTLOP_STORE_W ] = make_store, + [RTLOP_STORE_PTR ] = make_store, + [RTLOP_LABEL ] = make_label, + [RTLOP_GOTO ] = make_goto, + [RTLOP_GOTO_IF_Z ] = make_goto_cond, + [RTLOP_GOTO_IF_NZ ] = make_goto_cond, + [RTLOP_GOTO_IF_E ] = make_goto_cond2, + [RTLOP_GOTO_IF_NE ] = make_goto_cond2, + [RTLOP_CALL ] = make_call, + [RTLOP_RETURN ] = make_return, + [RTLOP_RETURN_TO ] = make_return_to, +}; + +/*-----------------------------------------------------------------------*/ + +/* Macro to mark a given register live, updating birth/death fields too + * (requires block and insn_index to be separately defined) */ +#define MARK_LIVE(reg,index) do { \ + RTLRegister * const __reg = (reg); \ + const unsigned int __index = (index); \ + if (!__reg->live) { \ + __reg->live = 1; \ + __reg->birth = insn_index; \ + if (!block->last_live_reg) { \ + block->first_live_reg = __index; \ + } else { \ + block->regs[block->last_live_reg].live_link = __index; \ + } \ + block->last_live_reg = __index; \ + reg->live_link = 0; \ + } \ + __reg->death = insn_index; \ +} while (0) + +/*************************************************************************/ +/************************** Routine definitions **************************/ +/*************************************************************************/ + +/** + * make_nop: Encode a NOP instruction. + */ +static int make_nop(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(insn != NULL, return 0); + + insn->src_imm = src1; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_alu_1op: Encode a 1-operand ALU instruction. + */ +static int make_alu_1op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); +#endif + + insn->dest = dest; + // FIXME: why does GCC waste time with an ANDI temp,arg,0xFFFF for + // stores like this src1 (but not with dest)? + insn->src1 = src1; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + if (insn->opcode == RTLOP_MOVE) { + destreg->unique_pointer = src1reg->unique_pointer; + } else { + destreg->unique_pointer = 0; + } + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 0; + destreg->result.src1 = src1; + destreg->result.src2 = 0; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_alu_2op: Encode a 2-operand ALU instruction. + */ +static int make_alu_2op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(src2 != 0 && src2 < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + destreg->unique_pointer = 0; + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 0; + destreg->result.src1 = src1; + destreg->result.src2 = src2; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + MARK_LIVE(src2reg, src2); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_alui_2op: Encode a 2-operand register-immediate ALU instruction. + */ +static int make_alui_2op(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src_imm = src2; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + destreg->unique_pointer = 0; + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 1; + destreg->result.src1 = src1; + destreg->result.imm = src2; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_alu_2op_2dest: Encode a 2-operand, 2-destination ALU instruction. + */ +static int make_alu_2op_2dest(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(src2 != 0 && src2 < block->next_reg, return 0); + PRECOND(other < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + insn->dest2 = other; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + RTLRegister * const dest2reg = &block->regs[other]; + const uint32_t insn_index = block->num_insns; + if (dest != 0) { + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + destreg->unique_pointer = 0; + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 0; + destreg->result.src1 = src1; + destreg->result.src2 = src2; + MARK_LIVE(destreg, dest); + } + if (other != 0) { + dest2reg->source = dest2reg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + dest2reg->unique_pointer = 0; + dest2reg->result.opcode = insn->opcode; + dest2reg->result.second_res = 1; + dest2reg->result.is_imm = 0; + dest2reg->result.src1 = src1; + dest2reg->result.src2 = src2; + MARK_LIVE(dest2reg, other); + } + MARK_LIVE(src1reg, src1); + MARK_LIVE(src2reg, src2); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_select: Encode a SELECT instruction. + */ +static int make_select(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(src2 != 0 && src2 < block->next_reg, return 0); + PRECOND(other != 0 && other < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + insn->cond = other; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + RTLRegister * const condreg = &block->regs[other]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + destreg->unique_pointer = 0; + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 0; + destreg->result.src1 = src1; + destreg->result.src2 = src2; + destreg->result.cond = other; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + MARK_LIVE(src2reg, src2); + MARK_LIVE(condreg, other); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_madd: Encode an MADDU or MADDS instruction. + */ +static int make_madd(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(src2 != 0 && src2 < block->next_reg, return 0); + PRECOND(other != 0 && other < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + insn->dest2 = other; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + RTLRegister * const dest2reg = &block->regs[other]; + const uint32_t insn_index = block->num_insns; + destreg->source = RTLREG_UNKNOWN; // Always UNKNOWN, since we modify it + destreg->unique_pointer = 0; + MARK_LIVE(destreg, dest); + dest2reg->source = RTLREG_UNKNOWN; + dest2reg->unique_pointer = 0; + MARK_LIVE(dest2reg, other); + MARK_LIVE(src1reg, src1); + MARK_LIVE(src2reg, src2); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_bitfield: Encode a bitfield instruction. + */ +static int make_bitfield(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); + const int start = other & 0xFF; + const int count = (other >> 8) & 0xFF; +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND((insn->opcode != RTLOP_BFINS || src2 != 0) && src2 < block->next_reg, return 0); + PRECOND(start < 32, return 0); + PRECOND(count <= 32 - start, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + insn->bitfield.start = start; + insn->bitfield.count = count; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_RESULT; + destreg->unique_pointer = 0; + destreg->result.opcode = insn->opcode; + destreg->result.second_res = 0; + destreg->result.is_imm = 0; + destreg->result.src1 = src1; + destreg->result.src2 = src2; + destreg->result.start = start; + destreg->result.count = count; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + if (src2) { + MARK_LIVE(src2reg, src2); + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_load_imm: Encode a LOAD_IMM instruction. + */ +static int make_load_imm(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src_imm = src1; + + RTLRegister * const destreg = &block->regs[dest]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_CONSTANT; + destreg->unique_pointer = 0; + destreg->value = src1; + MARK_LIVE(destreg, dest); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_load_addr: Encode a LOAD_ADDR instruction. + */ +static int make_load_addr(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src_addr = src1; + + RTLRegister * const destreg = &block->regs[dest]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_CONSTANT; + destreg->unique_pointer = 0; + destreg->value = src1; + MARK_LIVE(destreg, dest); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_load_param: Encode a LOAD_PARAM instruction. + */ +static int make_load_param(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); +#endif + + insn->dest = dest; + insn->src_imm = src1; + + RTLRegister * const destreg = &block->regs[dest]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_PARAMETER; + destreg->unique_pointer = 0; + destreg->param_index = src1; + MARK_LIVE(destreg, dest); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_load: Encode a memory load instruction. + */ +static int make_load(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND((int)other >= -0x8000 && (int)other <= 0x7FFF, return 0); +#endif + + /* Lookup tables for destreg->memory.{size,is_signed} */ + static const uint8_t size_lookup[] = { + [RTLOP_LOAD_BU ] = 1, [RTLOP_LOAD_BS] = 1, + [RTLOP_LOAD_HU ] = 2, [RTLOP_LOAD_HS] = 2, + [RTLOP_LOAD_W ] = 4, + [RTLOP_LOAD_PTR] = sizeof(void *), + }; + static const uint8_t is_signed_lookup[] = { + [RTLOP_LOAD_BS] = 1, + [RTLOP_LOAD_HS] = 1, + }; + + insn->dest = dest; + insn->src1 = src1; + insn->offset = other; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + const uint32_t insn_index = block->num_insns; + destreg->source = destreg->source ? RTLREG_UNKNOWN : RTLREG_MEMORY; + destreg->unique_pointer = 0; + destreg->memory.addr_reg = src1; + destreg->memory.offset = other; + const unsigned int opcode = insn->opcode; + destreg->memory.size = size_lookup[opcode]; + destreg->memory.is_signed = is_signed_lookup[opcode]; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_store: Encode a memory store instruction. + */ +static int make_store(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest != 0 && dest < block->next_reg, return 0); + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND((int)other >= -0x8000 && (int)other <= 0x7FFF, return 0); +#endif + + insn->dest = dest; + insn->src1 = src1; + insn->offset = other; + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + const uint32_t insn_index = block->num_insns; + MARK_LIVE(destreg, dest); + MARK_LIVE(src1reg, src1); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_label: Encode a LABEL instruction. + */ +static int make_label(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->cur_unit >= 0 && block->cur_unit < block->num_units, + return 0); + PRECOND(block->label_unitmap != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(other != 0 && other < block->next_label, return 0); +#endif + + insn->label = other; + + /* If this is _not_ the first instruction in the current basic + * unit, end it and create a new basic unit starting here, since + * there could potentially be a branch to this location. */ + if (block->units[block->cur_unit].first_insn != block->num_insns) { + block->units[block->cur_unit].last_insn = block->num_insns - 1; + if (UNLIKELY(!rtlunit_add(block))) { + DMSG("%p/%u: Failed to start a new basic unit", + block, block->num_insns); + return 0; + } + const uint32_t new_unit = block->num_units - 1; + if (UNLIKELY(!rtlunit_add_edge(block, block->cur_unit, new_unit))){ + DMSG("%p/%u: Failed to add edge %u->%u", block, + block->num_insns, block->cur_unit, new_unit); + return 0; + } + block->cur_unit = new_unit; + block->units[block->cur_unit].first_insn = block->num_insns; + } + + /* Save the label's unit number in the label-to-unit map */ + block->label_unitmap[insn->label] = block->cur_unit; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_goto: Encode a GOTO instruction. + */ +static int make_goto(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(other != 0 && other < block->next_label, return 0); +#endif + + insn->label = other; + + /* Terminate the current basic unit after this instruction */ + block->units[block->cur_unit].last_insn = block->num_insns; + block->have_unit = 0; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_goto_cond: Encode a GOTO_IF_Z or GOTO_IF_NZ instruction. + */ +static int make_goto_cond(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(other != 0 && other < block->next_label, return 0); +#endif + + insn->src1 = src1; + insn->label = other; + + RTLRegister * const src1reg = &block->regs[src1]; + const uint32_t insn_index = block->num_insns; + MARK_LIVE(src1reg, src1); + + /* Terminate the current basic unit after this instruction, and + * start a new basic unit with an edge connecting from this one */ + block->units[block->cur_unit].last_insn = block->num_insns; + if (UNLIKELY(!rtlunit_add(block))) { + DMSG("%p/%u: Failed to start a new basic unit", + block, block->num_insns); + return 0; + } + const unsigned int new_unit = block->num_units - 1; + if (UNLIKELY(!rtlunit_add_edge(block, block->cur_unit, new_unit))) { + DMSG("%p/%u: Failed to add edge %u->%u", block, block->num_insns, + block->cur_unit, new_unit); + return 0; + } + block->cur_unit = new_unit; + block->units[block->cur_unit].first_insn = block->num_insns + 1; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_goto_cond2: Encode a GOTO_IF_E or GOTO_IF_NE instruction. + */ +static int make_goto_cond2(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(src1 != 0 && src1 < block->next_reg, return 0); + PRECOND(src2 != 0 && src2 < block->next_reg, return 0); + PRECOND(other != 0 && other < block->next_label, return 0); +#endif + + insn->src1 = src1; + insn->src2 = src2; + insn->label = other; + + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + const uint32_t insn_index = block->num_insns; + MARK_LIVE(src1reg, src1); + MARK_LIVE(src2reg, src2); + + block->units[block->cur_unit].last_insn = block->num_insns; + if (UNLIKELY(!rtlunit_add(block))) { + DMSG("%p/%u: Failed to start a new basic unit", + block, block->num_insns); + return 0; + } + const unsigned int new_unit = block->num_units - 1; + if (UNLIKELY(!rtlunit_add_edge(block, block->cur_unit, new_unit))) { + DMSG("%p/%u: Failed to add edge %u->%u", block, block->num_insns, + block->cur_unit, new_unit); + return 0; + } + block->cur_unit = new_unit; + block->units[block->cur_unit].first_insn = block->num_insns + 1; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_call: Encode a CALL instruction. + */ +static int make_call(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); +#ifdef OPERAND_SANITY_CHECKS + PRECOND(dest < block->next_reg, return 0); + PRECOND(!(src1 == 0 && src2 != 0) && src1 < block->next_reg, return 0); + PRECOND(src2 < block->next_reg, return 0); + PRECOND(other != 0 && other < block->next_reg, return 0); +#endif + + RTLRegister * const destreg = &block->regs[dest]; + RTLRegister * const src1reg = &block->regs[src1]; + RTLRegister * const src2reg = &block->regs[src2]; + RTLRegister * const targetreg = &block->regs[other]; + const uint32_t insn_index = block->num_insns; + insn->dest = dest; + insn->src1 = src1; + insn->src2 = src2; + insn->target = other; + if (dest) { + destreg->source = RTLREG_UNKNOWN; + destreg->unique_pointer = 0; + MARK_LIVE(destreg, dest); + } + if (src1) { + MARK_LIVE(src1reg, src1); + } + if (src2) { + MARK_LIVE(src2reg, src2); + } + MARK_LIVE(targetreg, other); + + const int cur_unit = block->cur_unit; + if (block->first_call_unit < 0) { + block->first_call_unit = cur_unit; + } + if (block->last_call_unit < cur_unit) { + if (block->last_call_unit >= 0) { + block->units[block->last_call_unit].next_call_unit = cur_unit; + block->units[cur_unit].prev_call_unit = block->last_call_unit; + } else { + block->units[cur_unit].prev_call_unit = -1; + } + block->last_call_unit = cur_unit; + } + block->units[cur_unit].next_call_unit = -1; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_return: Encode a RETURN instruction. + */ +static int make_return(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); + + /* Terminate the current basic unit, like GOTO */ + block->units[block->cur_unit].last_insn = block->num_insns; + block->have_unit = 0; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * make_return_to: Encode a RETURN_TO instruction. + */ +static int make_return_to(RTLBlock *block, RTLInsn *insn, unsigned int dest, + uintptr_t src1, uint32_t src2, unsigned int other) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(insn != NULL, return 0); + + RTLRegister * const targetreg = &block->regs[other]; + const uint32_t insn_index = block->num_insns; + insn->target = other; + MARK_LIVE(targetreg, other); + + /* Terminate the current basic unit, like GOTO */ + block->units[block->cur_unit].last_insn = block->num_insns; + block->have_unit = 0; + + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtlopt.c b/yabause/src/psp/rtlopt.c new file mode 100644 index 0000000000..1b17745fc8 --- /dev/null +++ b/yabause/src/psp/rtlopt.c @@ -0,0 +1,897 @@ +/* src/psp/rtlopt.c: Optimization processing for RTL + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ + +/* + * This source file contains code for the various transformations applied + * to RTL code to produce more optimized instruction streams. The + * currently implemented transformations are: + * + * Constant folding/propagation (rtlopt_fold_constants) + * ---------------------------------------------------- + * Replaces instructions that operate on constant operands with LOAD_IMM + * (load immediate) instructions that load the result of the operation + * directly into the target register, eliminating the operation itself and + * allowing the constant result to be propagated further. Source operands + * that are not used elsewhere are eliminated entirely, with the + * instructions that loaded them converted to NOPs. + * + * While most constant expressions will be resolved by the source + * translator at RTL generation time, this optimization step allows macros + * in particular to take faster paths when they are called with constant + * parameters. + * + * Deconditioning (rtlopt_decondition) + * ---------------------------------- + * Converts conditional jumps (GOTO_IF_Z, GOTO_IF_NZ, GOTO_IF_E, GOTO_IF_NE) + * whose test result is a constant to either unconditional jumps (GOTO) or + * NOPs, depending on the instruction and the result of the test. + * + * Dead unit removal (rtlopt_drop_dead_units) + * ------------------------------------------ + * Removes unreachable basic units from the RTL code stream. + * + * Useless branch removal (rtlopt_drop_dead_branches) + * -------------------------------------------------- + * Replaces branch instructions that branch to the next instruction in the + * code stream with NOPs. + */ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "rtl.h" +#include "rtl-internal.h" + +/*************************************************************************/ +/************************** Local declarations ***************************/ +/*************************************************************************/ + +static inline int fold_one_register(RTLBlock * const block, + RTLRegister * const reg); +static void maybe_eliminate_folded_register(RTLBlock * const block, + RTLRegister * const reg, + const unsigned int reg_index); + +static void drop_dead_unit(RTLBlock * const block, + const unsigned int unit_index); + +/*************************************************************************/ +/*********************** Library-internal routines ***********************/ +/*************************************************************************/ + +/** + * rtlopt_fold_constants: Perform constant folding on the given RTL block, + * converting instructions that operate on constant operands into load- + * immediate instructions that load the result of the operation. If such + * an operand is not used by any other instruction, the instruction that + * loaded it is changed to a NOP. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +int rtlopt_fold_constants(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + + unsigned int reg_index; + for (reg_index = 1; reg_index < block->next_reg; reg_index++) { + RTLRegister * const reg = &block->regs[reg_index]; + if (reg->live && reg->source == RTLREG_RESULT) { + fold_one_register(block, reg); + } + } + + return 1; +} + +/*************************************************************************/ + +/** + * rtlopt_decondition: Perform "deconditioning" of conditional branches + * with constant conditions. For "GOTO_IF_Z (GOTO_IF_NZ) label, rN" where + * rN is type RTLREG_CONSTANT, the instruction is changed to GOTO if the + * value of rN is zero (nonzero) and changed to NOP otherwise; similarly + * for "GOTO_IF_E (GOTO_IF_NE) label, rX, rY". As with constant folding, + * if a condition register is not used anywhere else, the register is + * eliminated and the instruction that loaded it is changed to a NOP. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +int rtlopt_decondition(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + + /* A conditional branch always ends an basic unit and has two targets, + * so we only need to check the last instruction of each unit with two + * exit edges. */ + + unsigned int unit_index; + for (unit_index = 0; unit_index < block->num_units; unit_index++) { + RTLUnit * const unit = &block->units[unit_index]; + if (unit->exits[1] != -1) { + RTLInsn * const insn = &block->insns[unit->last_insn]; + const RTLOpcode opcode = insn->opcode; + + if (opcode == RTLOP_GOTO_IF_Z || opcode == RTLOP_GOTO_IF_NZ) { + const unsigned int reg_index = insn->src1; + RTLRegister * const condition_reg = &block->regs[reg_index]; + if (condition_reg->source == RTLREG_CONSTANT) { + const uintptr_t condition = condition_reg->value; + const int fallthrough_index = + (unit->exits[0] == unit_index+1) ? 0 : 1; + if ((opcode == RTLOP_GOTO_IF_Z && condition == 0) + || (opcode == RTLOP_GOTO_IF_NZ && condition != 0) + ) { + /* Branch always taken: convert to GOTO */ +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%u: Branch always taken," + " convert to GOTO and drop edge %u->%u\n", + block, unit->last_insn, unit_index, + unit->exits[fallthrough_index]); +#endif + insn->opcode = RTLOP_GOTO; + rtlunit_remove_edge(block, unit_index, + fallthrough_index); + } else { + /* Branch never taken: convert to NOP */ +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%u: Branch never taken," + " convert to NOP and drop edge %u->%u\n", + block, unit->last_insn, unit_index, + unit->exits[fallthrough_index ^ 1]); +#endif + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + rtlunit_remove_edge(block, unit_index, + fallthrough_index ^ 1); + } + if (condition_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, condition_reg, + reg_index); + } + } + + } else if (opcode == RTLOP_GOTO_IF_E || opcode == RTLOP_GOTO_IF_NE){ + const unsigned int src1_index = insn->src1; + const unsigned int src2_index = insn->src1; + RTLRegister * const src1_reg = &block->regs[src1_index]; + RTLRegister * const src2_reg = &block->regs[src2_index]; + if (src1_reg->source == RTLREG_CONSTANT + && src2_reg->source == RTLREG_CONSTANT + ) { + const uintptr_t condition = + src1_reg->value - src2_reg->value; + const int fallthrough_index = + (unit->exits[0] == unit_index+1) ? 0 : 1; + if ((opcode == RTLOP_GOTO_IF_Z && condition == 0) + || (opcode == RTLOP_GOTO_IF_NZ && condition != 0) + ) { + /* Branch always taken: convert to GOTO */ +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%u: Branch always taken," + " convert to GOTO and drop edge %u->%u\n", + block, unit->last_insn, unit_index, + unit->exits[fallthrough_index]); +#endif + insn->opcode = RTLOP_GOTO; + rtlunit_remove_edge(block, unit_index, + fallthrough_index); + } else { + /* Branch never taken: convert to NOP */ +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%u: Branch never taken," + " convert to NOP and drop edge %u->%u\n", + block, unit->last_insn, unit_index, + unit->exits[fallthrough_index ^ 1]); +#endif + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + rtlunit_remove_edge(block, unit_index, + fallthrough_index ^ 1); + } + if (src1_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, src1_reg, + src1_index); + } + if (src2_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, src2_reg, + src2_index); + } + } + + } + } // if (unit->exits[1] != -1) + } // for (unit_index = 0; unit_index < block->num_units; unit_index++) + + return 1; +} + +/*************************************************************************/ + +/** + * rtlopt_drop_dead_units: Search an RTL block for basic units which are + * unreachable via any path from the initial unit and remove them from the + * code stream. All units dominated only by such dead units are + * recursively removed as well. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +int rtlopt_drop_dead_units(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + + /* Allocate and clear a buffer for "seen" flags */ + block->unit_seen = calloc(block->num_units, sizeof(*block->unit_seen)); + if (UNLIKELY(!block->unit_seen)) { + DMSG("Failed to allocate units_seen (%u bytes)", block->num_units); + return 0; + } + + /* Check each unit in sequence (except the initial unit, which is never + * dead even if it has no entry edges) */ + unsigned int unit_index; + for (unit_index = 1; unit_index < block->num_units; unit_index++) { + if (block->units[unit_index].entries[0] < 0) { + drop_dead_unit(block, unit_index); + } + block->unit_seen[unit_index] = 1; + } + + /* Free the "seen" flag buffer before returning (since the core doesn't + * touch this field) */ + free(block->unit_seen); + block->unit_seen = NULL; // Just for safety + + return 1; +} + +/*************************************************************************/ + +/** + * rtlopt_drop_dead_branches: Search an RTL block for branch instructions + * which branch to the next instruction in the code stream and replace them + * with NOPs. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on error + */ +int rtlopt_drop_dead_branches(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->insns != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(block->regs != NULL, return 0); + PRECOND(block->label_unitmap != NULL, return 0); + + unsigned int unit_index; + for (unit_index = 0; unit_index < block->num_units; unit_index++) { + RTLUnit * const unit = &block->units[unit_index]; + if (unit->last_insn >= unit->first_insn) { + RTLInsn * const insn = &block->insns[unit->last_insn]; + const RTLOpcode opcode = insn->opcode; + if ((opcode == RTLOP_GOTO + || opcode == RTLOP_GOTO_IF_Z + || opcode == RTLOP_GOTO_IF_NZ + || opcode == RTLOP_GOTO_IF_E + || opcode == RTLOP_GOTO_IF_NE) + && block->label_unitmap[insn->label] == unit->next_unit + ) { +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p/%u: Dropping branch to next insn\n", + block, unit->last_insn); +#endif + if (opcode == RTLOP_GOTO_IF_Z || opcode == RTLOP_GOTO_IF_NZ) { + RTLRegister *src1_reg = &block->regs[insn->src1]; + if (src1_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, src1_reg, + insn->src1); + } + } else if (opcode == RTLOP_GOTO_IF_E || opcode == RTLOP_GOTO_IF_NE) { + RTLRegister *src1_reg = &block->regs[insn->src1]; + if (src1_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, src1_reg, + insn->src1); + } + RTLRegister *src2_reg = &block->regs[insn->src2]; + if (src2_reg->death == unit->last_insn) { + maybe_eliminate_folded_register(block, src2_reg, + insn->src2); + } + } + insn->opcode = RTLOP_NOP; + insn->src_imm = 0; + } + } + } + + return 1; +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * fold_one_register: Attempt to perform constant folding on a register. + * The register must be of type RTLREG_RESULT. + * + * [Parameters] + * block: RTL block + * reg: Register on which to perform constant folding + * [Return value] + * Nonzero if constant folding was performed, zero otherwise + * [Notes] + * This routine calls itself recursively, but it is declared inline + * anyway to optimize the outer loop in rtlopt_fold_constants(). + */ +static inline int fold_one_register(RTLBlock * const block, + RTLRegister * const reg) +{ + PRECOND(block != NULL, return 0); + PRECOND(reg != NULL, return 0); + PRECOND(reg->live, return 0); + PRECOND(reg->source == RTLREG_RESULT, return 0); + + /* Flag the register as not foldable for the moment (to avoid infinite + * recursion in case invalid code creates register dependency loops */ + + reg->source = RTLREG_RESULT_NOFOLD; + + /* See if the operands are constant, folding them if necessary */ + + RTLRegister * const src1 = &block->regs[reg->result.src1]; + RTLRegister * const src2 = &block->regs[reg->result.src2]; + if (src1->source != RTLREG_CONSTANT + && !(src1->source == RTLREG_RESULT + && fold_one_register(block, src1)) + ) { + return 0; // Operand 1 wasn't constant + } + if (!reg->result.is_imm + && reg->result.src2 != 0 // In case it's a 1-operand instruction + && src2->source != RTLREG_CONSTANT + && !(src2->source == RTLREG_RESULT + && fold_one_register(block, src2)) + ) { + return 0; // Operand 2 wasn't constant + } + if (reg->result.opcode == RTLOP_SELECT) { + RTLRegister * const cond = &block->regs[reg->result.cond]; + if (cond->source != RTLREG_CONSTANT + && !(cond->source == RTLREG_RESULT + && fold_one_register(block, cond)) + ) { + return 0; // Condition operand wasn't constant + } + } + + /* All operands are constants, so perform the operation now and convert + * the register to a constant */ + + uintptr_t result = 0; + switch (reg->result.opcode) { + + case RTLOP_MOVE: + result = src1->value; + break; + + case RTLOP_SELECT: + result = + block->regs[reg->result.cond].value ? src1->value : src2->value; + break; + + case RTLOP_ADD: + result = src1->value + (intptr_t)(int32_t)src2->value; + break; + + case RTLOP_SUB: + result = src1->value - (intptr_t)(int32_t)src2->value; + break; + + case RTLOP_MULU: + if (reg->result.second_res) { + result = ((uint64_t)src1->value * (uint64_t)src2->value) >> 32; + } else { + result = src1->value * src2->value; + } + break; + + case RTLOP_MULS: + if (reg->result.second_res) { + result = ((int64_t)(int32_t)src1->value + * (int64_t)(int32_t)src2->value) >> 32; + } else { + result = src1->value * src2->value; + } + break; + + case RTLOP_DIVMODU: + if (reg->result.second_res) { + result = src2->value ? src1->value % src2->value : 0; + } else { + result = src2->value ? src1->value / src2->value : 0; + } + break; + + case RTLOP_DIVMODS: + if (reg->result.second_res) { + result = src2->value ? (int32_t)src1->value % (int32_t)src2->value + : 0; + } else { + result = src2->value ? (int32_t)src1->value / (int32_t)src2->value + : 0; + } + break; + + case RTLOP_AND: + result = src1->value & (intptr_t)(int32_t)src2->value; + break; + + case RTLOP_OR: + result = src1->value | (uintptr_t)(uint32_t)src2->value; + break; + + case RTLOP_XOR: + result = src1->value ^ (uintptr_t)(uint32_t)src2->value; + break; + + case RTLOP_NOT: + result = ~src1->value; + break; + + case RTLOP_SLL: + result = src1->value << src2->value; + break; + + case RTLOP_SRL: + result = (uint32_t)src1->value >> src2->value; + break; + + case RTLOP_SRA: + result = (int32_t)src1->value >> src2->value; + break; + + case RTLOP_ROR: + if ((src2->value & 31) != 0) { + result = (uint32_t)src1->value >> (src2->value & 31) + | (uint32_t)src1->value << (32 - (src2->value & 31)); + } else { + result = (uint32_t)src1->value; + } + break; + + case RTLOP_CLZ: { +#ifdef __GNUC__ + result = __builtin_clz(src1->value); +#else + uint32_t temp = src1->value; + result = 32; + while (temp) { + temp >>= 1; + result--; + } +#endif // __GNUC__ + break; + } + + case RTLOP_CLO: { + uint32_t temp = src1->value; + result = 0; + while ((int32_t)temp < 0) { + temp <<= 1; + result++; + } + break; + } + + case RTLOP_SLTU: + result = ((uint32_t)src1->value < (uint32_t)src2->value) ? 1 : 0; + break; + + case RTLOP_SLTS: + result = ((int32_t)src1->value < (int32_t)src2->value) ? 1 : 0; + break; + + case RTLOP_BSWAPH: + result = ((uint32_t)src1->value & 0xFF00FF00) >> 8 + | ((uint32_t)src1->value & 0x00FF00FF) << 8; + break; + + case RTLOP_BSWAPW: + result = ((uint32_t)src1->value & 0xFF000000) >> 24 + | ((uint32_t)src1->value & 0x00FF0000) >> 8 + | ((uint32_t)src1->value & 0x0000FF00) << 8 + | ((uint32_t)src1->value & 0x000000FF) << 24; + break; + + case RTLOP_HSWAPW: + result = ((uint32_t)src1->value & 0xFFFF0000) >> 16 + | ((uint32_t)src1->value & 0x0000FFFF) << 16; + break; + + case RTLOP_ADDI: + result = src1->value + reg->result.imm; + break; + + case RTLOP_ANDI: + result = src1->value & reg->result.imm; + break; + + case RTLOP_ORI: + result = src1->value | reg->result.imm; + break; + + case RTLOP_XORI: + result = src1->value ^ reg->result.imm; + break; + + case RTLOP_SLLI: + result = src1->value << reg->result.imm; + break; + + case RTLOP_SRLI: + result = (uint32_t)src1->value >> reg->result.imm; + break; + + case RTLOP_SRAI: + result = (int32_t)src1->value >> reg->result.imm; + break; + + case RTLOP_RORI: + if ((reg->result.imm & 31) != 0) { + result = (uint32_t)src1->value >> (reg->result.imm & 31) + | (uint32_t)src1->value << (32 - (reg->result.imm & 31)); + } else { + result = (uint32_t)src1->value; + } + break; + + case RTLOP_SLTUI: + result = ((uint32_t)src1->value < (uint32_t)reg->result.imm) ? 1 : 0; + break; + + case RTLOP_SLTSI: + result = ((int32_t)src1->value < (int32_t)reg->result.imm) ? 1 : 0; + break; + + case RTLOP_BFEXT: + result = ((uint32_t)src1->value >> reg->result.start) + & ((1 << reg->result.count) - 1); + return 1; + + case RTLOP_BFINS: + result = ((uint32_t)src1->value + & ~(((1 << reg->result.count) - 1) << reg->result.start)) + | (((uint32_t)src2->value & ((1 << reg->result.count) - 1)) + << reg->result.start); + return 1; + + /* The remainder will never appear, but list them individually + * rather than using a default case so the compiler will warn us if + * we add a new opcode but don't include it here */ + case RTLOP_NOP: + case RTLOP_MADDU: + case RTLOP_MADDS: + case RTLOP_LOAD_IMM: + case RTLOP_LOAD_ADDR: + case RTLOP_LOAD_PARAM: + case RTLOP_LOAD_BU: + case RTLOP_LOAD_BS: + case RTLOP_LOAD_HU: + case RTLOP_LOAD_HS: + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + case RTLOP_STORE_B: + case RTLOP_STORE_H: + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + case RTLOP_LABEL: + case RTLOP_GOTO: + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: + case RTLOP_CALL: + case RTLOP_RETURN: + case RTLOP_RETURN_TO: + DMSG("impossible: opcode %u on RESULT register %u", + reg->result.opcode, (unsigned int)(reg - block->regs)); + return 0; + + } // switch (insn->opcode) + +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] Folded r%u to constant value 0x%lX at insn %u\n", + (unsigned int)(reg - block->regs), (unsigned long)result, + reg->birth); +#endif + + /* Update the instruction that set this register (use LOAD_ADDR in case + * the original value was an address) */ + + block->insns[reg->birth].opcode = RTLOP_LOAD_ADDR; + block->insns[reg->birth].src_addr = result; + + /* See whether the source register(s) are used anywhere else, and + * eliminate them if not */ + + if (src1->death == reg->birth) { + maybe_eliminate_folded_register(block, src1, reg->result.src1); + } + if ((!reg->result.is_imm && reg->result.src2 != 0) + && src2->death == reg->birth + ) { + maybe_eliminate_folded_register(block, src2, reg->result.src2); + } + + /* Constant folding was successful */ + + reg->source = RTLREG_CONSTANT; + reg->value = result; + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * maybe_eliminate_folded_register: See whether the given register is no + * longer used after being eliminated from the instruction indexed by + * reg->death. If so, eliminate the register and change the instruction + * indexed by reg->birth to a NOP; otherwise, update reg->death to point to + * the last instruction that still uses the register. + * + * It is assumed that the register is only assigned once, at the + * instruction indexed by reg->birth. + * + * [Parameters] + * block: RTL block + * reg: Register to attempt to eliminate + * reg_index: Index of register in block->regs[] + * [Return value] + * None + */ +static void maybe_eliminate_folded_register(RTLBlock * const block, + RTLRegister * const reg, + const unsigned int reg_index) +{ + PRECOND(block != NULL, return); + PRECOND(reg != NULL, return); + PRECOND(reg->live, return); + + uint32_t insn_index; + for (insn_index = reg->death-1; insn_index > reg->birth; insn_index--) { + const RTLInsn * const insn = &block->insns[insn_index]; + switch (insn->opcode) { + + case RTLOP_NOP: + case RTLOP_LOAD_IMM: + case RTLOP_LOAD_ADDR: + case RTLOP_LOAD_PARAM: + case RTLOP_LABEL: + case RTLOP_GOTO: + case RTLOP_RETURN: + break; + + case RTLOP_MOVE: + case RTLOP_NOT: + case RTLOP_CLZ: + case RTLOP_CLO: + case RTLOP_BSWAPH: + case RTLOP_BSWAPW: + case RTLOP_HSWAPW: + case RTLOP_ADDI: + case RTLOP_ANDI: + case RTLOP_ORI: + case RTLOP_XORI: + case RTLOP_SLLI: + case RTLOP_SRLI: + case RTLOP_SRAI: + case RTLOP_RORI: + case RTLOP_SLTUI: + case RTLOP_SLTSI: + case RTLOP_BFEXT: + case RTLOP_LOAD_BU: + case RTLOP_LOAD_BS: + case RTLOP_LOAD_HU: + case RTLOP_LOAD_HS: + case RTLOP_LOAD_W: + case RTLOP_LOAD_PTR: + case RTLOP_STORE_B: + case RTLOP_STORE_H: + case RTLOP_STORE_W: + case RTLOP_STORE_PTR: + case RTLOP_GOTO_IF_Z: + case RTLOP_GOTO_IF_NZ: + if (insn->src1 == reg_index) { + still_used: +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] maybe_eliminate_folded_register: r%u" + " still used at insn %u\n", reg_index, insn_index); +#endif + reg->death = insn_index; + return; + } + break; + + case RTLOP_ADD: + case RTLOP_SUB: + case RTLOP_MULU: + case RTLOP_MULS: + case RTLOP_DIVMODU: + case RTLOP_DIVMODS: + case RTLOP_AND: + case RTLOP_OR: + case RTLOP_XOR: + case RTLOP_SLL: + case RTLOP_SRL: + case RTLOP_SRA: + case RTLOP_ROR: + case RTLOP_SLTU: + case RTLOP_SLTS: + case RTLOP_BFINS: + case RTLOP_GOTO_IF_E: + case RTLOP_GOTO_IF_NE: + if (insn->src1 == reg_index || insn->src2 == reg_index) { + goto still_used; + } + break; + + case RTLOP_MADDU: + case RTLOP_MADDS: + if (insn->src1 == reg_index || insn->src2 == reg_index + || insn->dest == reg_index || insn->dest2 == reg_index + ) { + goto still_used; + } + break; + + case RTLOP_SELECT: + if (insn->src1 == reg_index || insn->src2 == reg_index + || insn->cond == reg_index + ) { + goto still_used; + } + break; + + case RTLOP_CALL: + if (insn->src1 == reg_index || insn->src2 == reg_index + || insn->target == reg_index + ) { + goto still_used; + } + break; + + case RTLOP_RETURN_TO: + if (insn->target == reg_index) { + goto still_used; + } + break; + + } // switch (opcode) + } // for (insn_index) + + /* If we got this far, nothing else uses the register, so nuke it */ +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] maybe_eliminate_folded_register: r%u no longer" + " used, eliminating\n", reg_index); +#endif + block->insns[reg->birth].opcode = RTLOP_NOP; + block->insns[reg->birth].src_imm = 0; + reg->live = 0; +} + +/*************************************************************************/ + +/** + * drop_dead_unit: Drop a dead basic unit from an RTL block. Recursive + * helper function for rtlopt_drop_dead_units(). + * + * [Parameters] + * block: RTL block + * unit_index: Index of unit in block->units[] + * [Return value] + * None + */ +static void drop_dead_unit(RTLBlock * const block, + const unsigned int unit_index) +{ + PRECOND(block != NULL, return); + PRECOND(block->units != NULL, return); + PRECOND(block->unit_seen != NULL, return); + PRECOND(unit_index < block->num_units, return); + PRECOND(block->units[unit_index].entries[0] < 0, return); + PRECOND(block->units[unit_index].prev_unit >= 0, return); + + RTLUnit * const unit = &block->units[unit_index]; +#ifdef RTL_TRACE_GENERATE + fprintf(stderr, "[RTL] %p: Dropping dead unit %u", block, unit_index); + if (unit->exits[0] < 0) { + fprintf(stderr, " (no exits)\n"); + } else if (unit->exits[1] < 0) { + fprintf(stderr, " (exits: %d)\n", unit->exits[0]); + } else { + fprintf(stderr, " (exits: %d, %d)\n", unit->exits[0], unit->exits[1]); + } +#endif + + block->units[unit->prev_unit].next_unit = unit->next_unit; + if (unit->next_unit >= 0) { + block->units[unit->next_unit].prev_unit = unit->prev_unit; + } + if (unit->prev_call_unit >= 0) { + block->units[unit->prev_call_unit].next_call_unit = + unit->next_call_unit; + } + if (unit->next_call_unit >= 0) { + block->units[unit->next_call_unit].prev_call_unit = + unit->prev_call_unit; + } + + while (unit->exits[0] >= 0) { + const unsigned int to_index = unit->exits[0]; + rtlunit_remove_edge(block, unit_index, 0); + if (block->unit_seen[to_index]) { + /* We already saw this unit and (presumably) skipped it because + * it wasn't dead. Check again now that we've removed this + * edge, and if it's now dead, recursively drop it. There's no + * danger of infinite recursion since any dead block has no + * edges entering into it by definition. */ + if (block->units[to_index].entries[0] < 0) { + drop_dead_unit(block, to_index); + } + } + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/rtlunit.c b/yabause/src/psp/rtlunit.c new file mode 100644 index 0000000000..2eae2fc7be --- /dev/null +++ b/yabause/src/psp/rtlunit.c @@ -0,0 +1,287 @@ +/* src/psp/rtlunit.c: Basic unit processing for RTL + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ + +/* + * This source file contains the logic used to divide a stream of RTL + * instructions into basic units (so-called "basic blocks"--we use the + * term "block" to refer to a sequence of source instructions translated + * into RTL as a group, so the term "units" is used here to differentiate). + * Each basic unit has exactly one entry point and one exit point, though + * there may be multiple paths into or out of the unit. As such, the unit + * can be optimized and translated to native code without the necessity of + * tracking every possible path for reaching each instruction. + */ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "rtl.h" +#include "rtl-internal.h" + +/*************************************************************************/ +/********************** External interface routines **********************/ +/*************************************************************************/ + +/** + * rtlunit_add: Add a new, empty basic unit to the given block + * at the end of the block->units[] array. + * + * [Parameters] + * block: RTL block + * [Return value] + * Nonzero on success, zero on failure + */ +int rtlunit_add(RTLBlock *block) +{ + PRECOND(block != NULL, return 0); + + if (UNLIKELY(block->num_units >= block->units_size)) { + unsigned int new_units_size = block->num_units + UNITS_EXPAND_SIZE; + RTLUnit *new_units = realloc(block->units, + sizeof(*block->units) * new_units_size); + if (UNLIKELY(!new_units)) { + DMSG("No memory to expand block %p to %d units", block, + new_units_size); + return 0; + } + block->units = new_units; + block->units_size = new_units_size; + } + + const unsigned int index = block->num_units++; + if (index > 0) { + block->units[index-1].next_unit = index; + } + block->units[index].first_insn = 0; + block->units[index].last_insn = -1; + block->units[index].next_unit = -1; + block->units[index].prev_unit = index-1; + unsigned int i; + for (i = 0; i < lenof(block->units[index].entries); i++) { + block->units[index].entries[i] = -1; + } + for (i = 0; i < lenof(block->units[index].exits); i++) { + block->units[index].exits[i] = -1; + } + block->units[index].next_call_unit = -1; + block->units[index].prev_call_unit = -1; + + return 1; +} + +/*************************************************************************/ + +/** + * rtlunit_add_edge: Add a new edge between two basic units. + * + * [Parameters] + * block: RTL block + * from_index: Index of dominating basic unit (in block->units[]) + * to_index: Index of postdominating basic unit (in block->units[]) + * [Return value] + * Nonzero on success, zero on failure + */ +int rtlunit_add_edge(RTLBlock *block, unsigned int from_index, + unsigned int to_index) +{ + PRECOND(block != NULL, return 0); + PRECOND(block->units != NULL, return 0); + PRECOND(from_index < block->num_units, return 0); + PRECOND(to_index < block->num_units, return 0); + + unsigned int i; + + /* Find an empty exit slot; also check whether this edge already exists, + * and return successfully (without adding a duplicate edge) if so. + * A unit can never have more than two exits, so bail if we try to add + * a third. */ + for (i = 0; i < lenof(block->units[from_index].exits); i++) { + if (block->units[from_index].exits[i] < 0) { + break; + } else if (block->units[from_index].exits[i] == to_index) { + return 1; + } + } + if (UNLIKELY(i >= lenof(block->units[from_index].exits))) { + DMSG("%p: Too many exits from unit %u", block, from_index); + } + block->units[from_index].exits[i] = to_index; + + /* If we overflow the entry list, add a dummy unit to take some of the + * entries out */ + for (i = 0; i < lenof(block->units[to_index].entries); i++) { + if (block->units[to_index].entries[i] < 0) { + break; + } + } + if (UNLIKELY(i >= lenof(block->units[to_index].entries))) { + /* No room in the entry list, so we need to create a dummy unit */ + if (UNLIKELY(!rtlunit_add(block))) { + DMSG("%p: Failed to add dummy unit", block); + return 0; + } + const unsigned int dummy_unit = block->num_units - 1; + /* Move all the current edges over to the dummy unit */ + for (i = 0; i < lenof(block->units[to_index].entries); i++) { + const unsigned int other_unit = block->units[to_index].entries[i]; + unsigned int j; + for (j = 0; j < lenof(block->units[other_unit].exits); j++) { + if (block->units[other_unit].exits[j] == to_index) { + break; + } + } + if (UNLIKELY(j >= lenof(block->units[other_unit].exits))) { + DMSG("%p: Internal compiler error: edge to unit %u missing" + " from unit %u", block, to_index, other_unit); + return 0; + } + block->units[other_unit].exits[j] = dummy_unit; + block->units[dummy_unit].entries[i] = other_unit; + } + /* Link to the original unit */ + block->units[dummy_unit].exits[0] = to_index; + block->units[dummy_unit].exits[1] = -1; + block->units[to_index].entries[0] = dummy_unit; + /* The new entry will go into the second slot; clear out all + * other edges */ + for (i = lenof(block->units[to_index].entries) - 1; i > 1; i--) { + block->units[to_index].entries[i] = -1; + } + } + block->units[to_index].entries[i] = from_index; + + return 1; +} + +/*************************************************************************/ + +/** + * rtlunit_remove_edge: Remove an edge between two basic units. + * + * [Parameters] + * block: RTL block + * from_index: Index of dominating basic unit (in block->units[]) + * exit_index: Index of exit edge to remove (in units[from_index].exits[]) + * [Return value] + * None + */ +void rtlunit_remove_edge(RTLBlock *block, const unsigned int from_index, + unsigned int exit_index) +{ + PRECOND(block != NULL, return); + PRECOND(block->units != NULL, return); + PRECOND(from_index < block->num_units, return); + PRECOND(exit_index < lenof(block->units[from_index].exits), return); + PRECOND(block->units[from_index].exits[exit_index] >= 0, return); + + RTLUnit * const from_unit = &block->units[from_index]; + const unsigned int to_index = from_unit->exits[exit_index]; + RTLUnit * const to_unit = &block->units[to_index]; + unsigned int entry_index; + + for (; exit_index < lenof(from_unit->exits) - 1; exit_index++) { + from_unit->exits[exit_index] = from_unit->exits[exit_index + 1]; + } + from_unit->exits[lenof(from_unit->exits) - 1] = -1; + + for (entry_index = 0; entry_index < lenof(to_unit->entries); + entry_index++ + ) { + if (to_unit->entries[entry_index] == from_index) { + break; + } + } + if (UNLIKELY(entry_index >= lenof(to_unit->entries))) { + DMSG("BUG: edge %u->%u missing from %u.entries!", + from_index, to_index, to_index); + return; + } + + for (; entry_index < lenof(to_unit->entries) - 1; entry_index++) { + to_unit->entries[entry_index] = to_unit->entries[entry_index + 1]; + } + to_unit->entries[lenof(to_unit->entries) - 1] = -1; +} + +/*************************************************************************/ + +/** + * rtlunit_dump_all: Dump a list of all basic units in the block to + * stderr. Intended for debugging. + * + * [Parameters] + * block: RTL block + * tag: Tag to prepend to all lines, or NULL for none + * [Return value] + * None + */ +void rtlunit_dump_all(const RTLBlock * const block, const char * const tag) +{ + PRECOND(block != NULL, return); + PRECOND(block->units != NULL, return); + + unsigned int i; + for (i = 0; i < block->num_units; i++) { + const RTLUnit * const unit = &block->units[i]; + fprintf(stderr, "[RTL] %s%s%sUnit %4u: ", + tag ? "[" : "", tag ? tag : "", tag ? "] " : "", i); + if (unit->entries[0] < 0) { + fprintf(stderr, ""); + } else { + unsigned int j; + for (j = 0; j < lenof(unit->entries) && unit->entries[j]>=0; j++) { + fprintf(stderr, "%s%u", j==0 ? "" : ",", unit->entries[j]); + } + } + if (unit->first_insn <= unit->last_insn) { + fprintf(stderr, " --> [%d,%d] --> ", + unit->first_insn, unit->last_insn); + } else { + fprintf(stderr, " --> [empty] --> "); + } + if (unit->exits[0] < 0) { + fprintf(stderr, ""); + } else { + unsigned int j; + for (j = 0; j < lenof(unit->exits) && unit->exits[j] >= 0; j++) { + fprintf(stderr, "%s%u", j==0 ? "" : ",", unit->exits[j]); + } + } + fprintf(stderr, "\n"); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/satopt-sh2.c b/yabause/src/psp/satopt-sh2.c new file mode 100644 index 0000000000..5d123adb20 --- /dev/null +++ b/yabause/src/psp/satopt-sh2.c @@ -0,0 +1,4019 @@ +/* src/psp/satopt-sh2.c: Saturn-specific SH-2 optimization routines + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "../core.h" +#include "../cs2.h" +#include "../memory.h" +#include "../scsp.h" +#include "../scu.h" +#include "../sh2core.h" +#include "../vdp1.h" +#include "../vdp2.h" +#include "../yabause.h" + +#include "sh2.h" +#include "sh2-internal.h" + +#include "misc.h" +#include "satopt-sh2.h" + +#ifdef JIT_DEBUG_TRACE +# include "../sh2d.h" +#endif + +/*************************************************************************/ + +/* Shorthand macros for memory access, both saving us from having to write + * out 0xFFFFF all over the place and allowing optimized memory access + * using constant offsets where possible (we assume small constant offsets + * don't fall outside the relevant memory region). Note that offsetted + * byte accesses to 16-bit-organized memory generally will not be optimized + * on little-endian systems, because the XOR has to be applied after adding + * the offset to the base; if the base is known to be 16-bit aligned, it + * may be faster to load a 16-bit word and mask or shift as appropriate. */ + +#define HRAM_PTR(base) ((uint8_t *)&HighWram[(base) & 0xFFFFF]) +#define HRAM_LOADB(base,offset) \ + T2ReadByte(HighWram, ((base) + (offset)) & 0xFFFFF) +#define HRAM_LOADW(base,offset) \ + T2ReadWord(HighWram, ((base) & 0xFFFFF) + (offset)) +#define HRAM_LOADL(base,offset) \ + T2ReadLong(HighWram, ((base) & 0xFFFFF) + (offset)) +#define HRAM_STOREB(base,offset,data) \ + T2WriteByte(HighWram, ((base) + (offset)) & 0xFFFFF, (data)) +#define HRAM_STOREW(base,offset,data) \ + T2WriteWord(HighWram, ((base) & 0xFFFFF) + (offset), (data)) +#define HRAM_STOREL(base,offset,data) \ + T2WriteLong(HighWram, ((base) & 0xFFFFF) + (offset), (data)) + +#define LRAM_PTR(base) ((uint8_t *)&LowWram[(base) & 0xFFFFF]) +#define LRAM_LOADB(base,offset) \ + T2ReadByte(LowWram, ((base) + (offset)) & 0xFFFFF) +#define LRAM_LOADW(base,offset) \ + T2ReadWord(LowWram, ((base) & 0xFFFFF) + (offset)) +#define LRAM_LOADL(base,offset) \ + T2ReadLong(LowWram, ((base) & 0xFFFFF) + (offset)) +#define LRAM_STOREB(base,offset,data) \ + T2WriteByte(LowWram, ((base) + (offset)) & 0xFFFFF, (data)) +#define LRAM_STOREW(base,offset,data) \ + T2WriteWord(LowWram, ((base) & 0xFFFFF) + (offset), (data)) +#define LRAM_STOREL(base,offset,data) \ + T2WriteLong(LowWram, ((base) & 0xFFFFF) + (offset), (data)) + +#define VDP1_PTR(base) ((uint8_t *)&Vdp1Ram[(base) & 0x7FFFF]) +#define VDP1_LOADB(base,offset) \ + T1ReadByte(Vdp1Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP1_LOADW(base,offset) \ + T1ReadWord(Vdp1Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP1_LOADL(base,offset) \ + T1ReadLong(Vdp1Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP1_STOREB(base,offset,data) \ + T1WriteByte(Vdp1Ram, ((base) & 0x7FFFF) + (offset), (data)) +#define VDP1_STOREW(base,offset,data) \ + T1WriteWord(Vdp1Ram, ((base) & 0x7FFFF) + (offset), (data)) +#define VDP1_STOREL(base,offset,data) \ + T1WriteLong(Vdp1Ram, ((base) & 0x7FFFF) + (offset), (data)) + +#define VDP2_PTR(base) ((uint8_t *)&Vdp2Ram[(base) & 0x7FFFF]) +#define VDP2_LOADB(base,offset) \ + T1ReadByte(Vdp2Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP2_LOADW(base,offset) \ + T1ReadWord(Vdp2Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP2_LOADL(base,offset) \ + T1ReadLong(Vdp2Ram, ((base) & 0x7FFFF) + (offset)) +#define VDP2_STOREB(base,offset,data) \ + T1WriteByte(Vdp2Ram, ((base) & 0x7FFFF) + (offset), (data)) +#define VDP2_STOREW(base,offset,data) \ + T1WriteWord(Vdp2Ram, ((base) & 0x7FFFF) + (offset), (data)) +#define VDP2_STOREL(base,offset,data) \ + T1WriteLong(Vdp2Ram, ((base) & 0x7FFFF) + (offset), (data)) + +/* Use these macros instead of [BW]SWAP{16,32} when loading from or + * storing to work RAM or VDP memory, to avoid breaking things on + * big-endian systems. RAM_VDP_SWAPL is for copying longwords directly + * between work RAM and VDP memory, and is one operation faster on + * little-endian machines than VDP_SWAPL(RAM_SWAPL(x)). */ + +#ifdef WORDS_BIGENDIAN +# define RAM_SWAPL(x) (x) +# define VDP_SWAPW(x) (x) +# define VDP_SWAPL(x) (x) +# define RAM_VDP_SWAPL(x) (x) +#else +# define RAM_SWAPL(x) WSWAP32(x) +# define VDP_SWAPW(x) BSWAP16(x) +# define VDP_SWAPL(x) BSWAP32(x) +# define RAM_VDP_SWAPL(x) BSWAP16(x) // BSWAP32(WSWAP32(x)) == BSWAP16(x) +#endif + +/*************************************************************************/ +/********************** Optimization routine table ***********************/ +/*************************************************************************/ + +#ifdef ENABLE_JIT // Through end of file + +/*************************************************************************/ + +/* List of detection and translation routines for special-case blocks */ + +#ifdef PSP +# define ALIGN_OPTIMIZER __attribute__((aligned(64))) +#else +# define ALIGN_OPTIMIZER /*nothing*/ +#endif + +#define DECLARE_OPTIMIZER(name) \ + ALIGN_OPTIMIZER static FASTCALL void name(SH2State *state) + +#define DECLARE_OPTIMIZER_WITH_DETECT(name) \ + static int name##_detect(SH2State *state, uint32_t address, \ + const uint16_t *fetch); \ + DECLARE_OPTIMIZER(name) + +/*----------------------------------*/ + +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_000025AC); +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_00002EFA); +DECLARE_OPTIMIZER (BIOS_00003BC6); +DECLARE_OPTIMIZER (BIOS_06001670); +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_06010D22); +DECLARE_OPTIMIZER (BIOS_060115A4); +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_060115B6); +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_060115D4); +DECLARE_OPTIMIZER (BIOS_0602E364); +DECLARE_OPTIMIZER_WITH_DETECT(BIOS_0602E630); + +/*----------------------------------*/ + +DECLARE_OPTIMIZER_WITH_DETECT(Azel_0600614C); +DECLARE_OPTIMIZER (Azel_060061F0); +DECLARE_OPTIMIZER (Azel_0600C4DC); +DECLARE_OPTIMIZER (Azel_0600C59C); +DECLARE_OPTIMIZER_WITH_DETECT(Azel_0600C5B4); +DECLARE_OPTIMIZER (Azel_0600C5F8); +DECLARE_OPTIMIZER (Azel_0600C690); +DECLARE_OPTIMIZER_WITH_DETECT(Azel_06010F24); +DECLARE_OPTIMIZER (Azel_06014274); +DECLARE_OPTIMIZER (Azel_0601E330); +DECLARE_OPTIMIZER (Azel_0601E910); +DECLARE_OPTIMIZER (Azel_0601E95A); +DECLARE_OPTIMIZER (Azel_0601EC20); +DECLARE_OPTIMIZER (Azel_0601EC3C); +DECLARE_OPTIMIZER (Azel_0601EE60); +DECLARE_OPTIMIZER (Azel_0601EEE8); +DECLARE_OPTIMIZER (Azel_0601F240); +DECLARE_OPTIMIZER (Azel_0601F24C); +DECLARE_OPTIMIZER (Azel_0601F2D6); +DECLARE_OPTIMIZER (Azel_0601F2F2); +DECLARE_OPTIMIZER (Azel_0601F30E); +DECLARE_OPTIMIZER (Azel_0601F3F4); +DECLARE_OPTIMIZER (Azel_0601FB70); +DECLARE_OPTIMIZER (Azel_06022E18); +DECLARE_OPTIMIZER (Azel_06035530); +DECLARE_OPTIMIZER (Azel_06035552); +DECLARE_OPTIMIZER (Azel_0603556C); +DECLARE_OPTIMIZER (Azel_06035A8C); +DECLARE_OPTIMIZER (Azel_06035A9C); +DECLARE_OPTIMIZER (Azel_06035AA0); +DECLARE_OPTIMIZER (Azel_06035B14); +DECLARE_OPTIMIZER (Azel_06035C18); +DECLARE_OPTIMIZER (Azel_06035C3C); +DECLARE_OPTIMIZER (Azel_06035C60); +DECLARE_OPTIMIZER (Azel_06035C84); +DECLARE_OPTIMIZER (Azel_06035C90); +DECLARE_OPTIMIZER (Azel_06035C96); +DECLARE_OPTIMIZER (Azel_06035D24); +DECLARE_OPTIMIZER (Azel_06035D30); +DECLARE_OPTIMIZER (Azel_06035D36); +DECLARE_OPTIMIZER (Azel_06035DD4); +DECLARE_OPTIMIZER (Azel_06035DE0); +DECLARE_OPTIMIZER (Azel_06035DE6); +DECLARE_OPTIMIZER (Azel_06035E70); +DECLARE_OPTIMIZER (Azel_06035EA0); +DECLARE_OPTIMIZER (Azel_06035ED0); +DECLARE_OPTIMIZER (Azel_06035F00); +DECLARE_OPTIMIZER (Azel_06035F04); +DECLARE_OPTIMIZER (Azel_060360F0); +DECLARE_OPTIMIZER_WITH_DETECT(Azel_0603A22C); +DECLARE_OPTIMIZER (Azel_0603A242); +DECLARE_OPTIMIZER (Azel_0603ABE0); +DECLARE_OPTIMIZER (Azel_0603DD6E); + +/*----------------------------------*/ + +static const struct { + + /* Start address applicable to this translation. */ + uint32_t address; + + /* Routine that implements the SH-2 code. If NULL, no optimized + * translation is generated; instead, the hints specified in the + * .hint_* fields are applied to the current block. */ + FASTCALL void (*execute)(SH2State *state); + + /* Routine to detect whether to use this translation; returns the + * number of 16-bit words processed (nonzero) to use it, else zero. + * If NULL, the checksum is checked instead. */ + int (*detect)(SH2State *state, uint32_t address, const uint16_t *fetch); + + /* Checksum and block length (in instructions) if detect == NULL. */ + uint32_t sum; + unsigned int length; + + /* Nonzero if this function is foldable (i.e. does not modify any + * registers other than R0-R7, MACH/MACL, and PC). If not set, the + * function is never folded even if it is detected to be a candidate + * for folding. */ + uint8_t foldable; + + /* Bitmask indicating which general purpose registers should be hinted + * as having a constant value at block start. */ + uint16_t hint_constant_reg; + + /* Bitmask indicating which general purpose registers should be hinted + * as containing a data pointer at block start. */ + uint16_t hint_data_pointer_reg; + + /* Bitmask indicating which of the first 32 instructions in the block + * should be hinted as loading a data pointer. */ + uint32_t hint_data_pointer_load; + +} hand_tuned_table[] = { + + /******** BIOS startup animation ********/ + + {0x00001CFC, BIOS_000025AC, BIOS_000025AC_detect}, // 1.00 JP + {0x000025AC, BIOS_000025AC, BIOS_000025AC_detect}, // 1.01 JP / UE + {0x00002658, BIOS_00002EFA, BIOS_00002EFA_detect}, // 1.00 JP + {0x00002EFA, BIOS_00002EFA, BIOS_00002EFA_detect}, // 1.01 JP / UE + {0x00002D44, BIOS_00003BC6, .sum = 0x1A6ECE, .length = 70}, // 1.00 JP + {0x00003BC6, BIOS_00003BC6, .sum = 0x1A40C9, .length = 71}, // 1.01 JP / UE + {0x06001664, BIOS_06001670, .sum = 0x04A2D6, .length = 13}, // 1.00 JP + {0x06001670, BIOS_06001670, .sum = 0x04A2D6, .length = 13}, // 1.01 JP + {0x06001674, BIOS_06001670, .sum = 0x04A2D6, .length = 13}, // UE + {0x06010D22, BIOS_06010D22, BIOS_06010D22_detect}, // JP + {0x06010D36, BIOS_06010D22, BIOS_06010D22_detect}, // UE + {0x060115C0, BIOS_060115A4, .sum = 0x032D22, .length = 9}, // 1.00 JP + {0x060115A4, BIOS_060115A4, .sum = 0x032D22, .length = 9}, // 1.01 JP + {0x06011654, BIOS_060115A4, .sum = 0x032D22, .length = 9}, // UE + {0x060115D2, BIOS_060115B6, BIOS_060115B6_detect}, // 1.00 JP + {0x060115B6, BIOS_060115B6, BIOS_060115B6_detect}, // 1.01 JP + {0x06011666, BIOS_060115B6, BIOS_060115B6_detect}, // UE + {0x060115F0, BIOS_060115D4, BIOS_060115D4_detect}, // 1.00 JP + {0x060115D4, BIOS_060115D4, BIOS_060115D4_detect}, // 1.01 JP + {0x06011684, BIOS_060115D4, BIOS_060115D4_detect}, // UE + {0x0602DA80, NULL, .sum = 0x0A6FD6, .length = 31, // JP + .hint_data_pointer_load = 1<<20}, + {0x06039A80, NULL, .sum = 0x0A6FD6, .length = 31, // UE + .hint_data_pointer_load = 1<<20}, + {0x0602DABE, NULL, .sum = 0x016AF5, .length = 3, // JP + .hint_data_pointer_reg = 1<<12}, + {0x06039ABE, NULL, .sum = 0x016AF5, .length = 3, // UE + .hint_data_pointer_reg = 1<<12}, + {0x0602DAC4, NULL, .sum = 0x016AF5, .length = 3, // JP + .hint_data_pointer_reg = 1<<12}, + {0x06039AC4, NULL, .sum = 0x016AF5, .length = 3, // UE + .hint_data_pointer_reg = 1<<12}, + {0x0602DACA, NULL, .sum = 0x016AF6, .length = 3, // JP + .hint_data_pointer_reg = 1<<12}, + {0x06039ACA, NULL, .sum = 0x016AF6, .length = 3, // UE + .hint_data_pointer_reg = 1<<12}, + {0x0602DF10, NULL, .sum = 0x04634B, .length = 13, // 1.00 JP + .hint_data_pointer_load = 1<<2}, + {0x0602DF30, NULL, .sum = 0x04634B, .length = 13, // 1.01 JP + .hint_data_pointer_load = 1<<2}, + {0x06039F30, NULL, .sum = 0x04634B, .length = 13, // UE + .hint_data_pointer_load = 1<<2}, + {0x0602E364, BIOS_0602E364, .sum = 0x074A3C, .length = 20, // JP + .foldable = 1}, + {0x0603A364, BIOS_0602E364, .sum = 0x074A3C, .length = 20, // UE + .foldable = 1}, + {0x0602E3B0, NULL, .sum = 0x02AAF2, .length = 7, // JP + .hint_data_pointer_load = 1<<6}, + {0x0603A3B0, NULL, .sum = 0x02AAF2, .length = 7, // UE + .hint_data_pointer_load = 1<<6}, + {0x0602E410, NULL, .sum = 0x0298A3, .length = 5, // JP + .hint_data_pointer_load = 1<<4}, + {0x0603A410, NULL, .sum = 0x0298A3, .length = 5, // UE + .hint_data_pointer_load = 1<<4}, + {0x0602E4B8, NULL, .sum = 0x02984F, .length = 5, // JP + .hint_data_pointer_load = 1<<4}, + {0x0603A4B8, NULL, .sum = 0x02984F, .length = 5, // UE + .hint_data_pointer_load = 1<<4}, + {0x0602E560, NULL, .sum = 0x0297FB, .length = 5, // JP + .hint_data_pointer_load = 1<<4}, + {0x0603A560, NULL, .sum = 0x0297FB, .length = 5, // UE + .hint_data_pointer_load = 1<<4}, + {0x0602E38C, NULL, .sum = 0x04E94B, .length = 18, // JP + .hint_data_pointer_load = 1<<0 | 1<<7}, + {0x0603A38C, NULL, .sum = 0x07294B, .length = 18, // UE + .hint_data_pointer_load = 1<<0 | 1<<7}, + {0x0602E630, BIOS_0602E630, BIOS_0602E630_detect}, // JP + {0x0603A630, BIOS_0602E630, BIOS_0602E630_detect}, // UE + + /******** Azel: Panzer Dragoon RPG (JP) ********/ + + {0x0600614C, Azel_0600614C, Azel_0600614C_detect}, + {0x060061F0, Azel_060061F0, .sum = 0x2FB438, .length = 167, .foldable = 1}, + {0x0600C4DC, Azel_0600C4DC, .sum = 0x155D7C, .length = 64, .foldable = 1}, + {0x0600C59C, Azel_0600C59C, .sum = 0x0C1C5C, .length = 30}, + {0x0600C5B4, Azel_0600C5B4, Azel_0600C5B4_detect}, + {0x0600C5F8, Azel_0600C5F8, .sum = 0x085791, .length = 22, .foldable = 1}, + {0x0600C690, Azel_0600C690, .sum = 0x05E193, .length = 15, .foldable = 1}, + {0x06010F24, Azel_06010F24, Azel_06010F24_detect}, + {0x06010F52, Azel_06010F24, Azel_06010F24_detect}, + {0x06014274, Azel_06014274, .sum = 0x23F1EA, .length = 140, .foldable = 1}, + {0x0601E330, Azel_0601E330, .sum = 0x1F9402, .length = 68}, + {0x0601E910, Azel_0601E910, .sum = 0x0EDD5D, .length = 37}, + {0x0601E95A, Azel_0601E95A, .sum = 0x0EDD2D, .length = 37}, + {0x0601EC20, Azel_0601EC20, .sum = 0x05F967, .length = 14}, + {0x0601EC3C, Azel_0601EC3C, .sum = 0x05F97D, .length = 14}, + {0x0601EE60, Azel_0601EE60, .sum = 0x0958CD, .length = 39}, // MOV R0,R1 + {0x0601EE60, Azel_0601EE60, .sum = 0x0998AD, .length = 39}, // BRA 601F02C + {0x0601EEAE, NULL, .sum = 0x05CD03, .length = 17, + .hint_data_pointer_load = 1<<3}, + {0x0601EED6, NULL, .sum = 0x01C56B, .length = 7, + .hint_data_pointer_reg = 1<<0 | 1<<1}, + {0x0601EEE8, Azel_0601EEE8, .sum = 0x075BB7, .length = 20, .foldable = 1}, + {0x0601F120, NULL, .sum = 0x0988A3, .length = 24, + .hint_data_pointer_load = 1<<17}, + {0x0601F140, NULL, .sum = 0x02D776, .length = 8, + .hint_data_pointer_load = 1<<1}, + {0x0601F150, NULL, .sum = 0x034F6E, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x0601F15E, NULL, .sum = 0x034F70, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x0601F16C, NULL, .sum = 0x029E99, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x0601F190, NULL, .sum = 0x01C56B, .length = 7, + .hint_data_pointer_reg = 1<<0 | 1<<1}, + {0x0601F240, Azel_0601F240, .sum = 0x13322C, .length = 75}, + {0x0601F24C, Azel_0601F24C, .sum = 0x11E92D, .length = 69}, + {0x0601F2D6, Azel_0601F2D6, .sum = 0x05F6D3, .length = 14}, + {0x0601F2F2, Azel_0601F2F2, .sum = 0x05F6E9, .length = 14}, + {0x0601F30E, Azel_0601F30E, .sum = 0x250327, .length = 115}, + {0x0601F3F4, Azel_0601F3F4, .sum = 0x1A4DA5, .length = 84}, + {0x0601FB70, Azel_0601FB70, .sum = 0x095941, .length = 39}, // MOV R0,R1 + {0x0601FB70, Azel_0601FB70, .sum = 0x09995B, .length = 39}, // BRA 601FDB0 + {0x0601FBBE, NULL, .sum = 0x049C2B, .length = 14, + .hint_data_pointer_load = 1<<0}, + {0x0601FC3A, NULL, .sum = 0x060C32, .length = 18, + .hint_data_pointer_reg = 1<<4}, + {0x0601FC6C, Azel_0601EEE8, .sum = 0x075BB7, .length = 20, .foldable = 1}, + {0x0601FDB8, NULL, .sum = 0x091C0C, .length = 55, + .hint_data_pointer_reg = 1<<1}, + {0x0601FEA4, NULL, .sum = 0x084DF0, .length = 20, + .hint_data_pointer_load = 1<<17}, + {0x0601FEC4, NULL, .sum = 0x01822F, .length = 4, + .hint_data_pointer_load = 1<<1}, + {0x0601FECC, NULL, .sum = 0x034F73, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x0601FEDA, NULL, .sum = 0x034F75, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x0601FEE8, NULL, .sum = 0x029E9A, .length = 7, + .hint_data_pointer_load = 1<<0 | 1<<1 | 1<<2}, + {0x06021FA2, NULL, .sum = 0x2A12AF, .length = 136, + .hint_constant_reg = 1<<13}, + {0x06022E18, Azel_06022E18, .sum = 0x09ABCB, .length = 33, .foldable = 1}, + {0x06028EE8, NULL, .sum = 0x1F09D1, .length = 103, + .hint_constant_reg = 1<<10}, + {0x0602F834, NULL, .sum = 0x0676B0, .length = 16, + .hint_data_pointer_load = 1<<11}, + {0x06035530, Azel_06035530, .sum = 0x05ABFA, .length = 17, .foldable = 1}, + {0x06035552, Azel_06035552, .sum = 0x0358EB, .length = 13, .foldable = 1}, + {0x0603556C, Azel_0603556C, .sum = 0x0332E9, .length = 11, .foldable = 1}, + {0x06035A8C, Azel_06035A8C, .sum = 0x0A1ECF, .length = 36, .foldable = 1}, + {0x06035A9C, Azel_06035A9C, .sum = 0x065B51, .length = 28, .foldable = 1}, + {0x06035AA0, Azel_06035AA0, .sum = 0x052642, .length = 26, .foldable = 1}, + {0x06035B14, Azel_06035B14, .sum = 0x05CBE1, .length = 37, .foldable = 1}, + {0x06035C18, Azel_06035C18, .sum = 0x085E83, .length = 18, .foldable = 1}, + {0x06035C3C, Azel_06035C3C, .sum = 0x08595C, .length = 18, .foldable = 1}, + {0x06035C60, Azel_06035C60, .sum = 0x085DB6, .length = 18, .foldable = 1}, + {0x06035C84, Azel_06035C84, .sum = 0x0F24AE, .length = 80, .foldable = 1}, + {0x06035C90, Azel_06035C90, .sum = 0x0C95AD, .length = 74, .foldable = 1}, + {0x06035C96, Azel_06035C96, .sum = 0x0B1711, .length = 71, .foldable = 1}, + {0x06035D24, Azel_06035D24, .sum = 0x12CFBE, .length = 88, .foldable = 1}, + {0x06035D30, Azel_06035D30, .sum = 0x1040BD, .length = 82, .foldable = 1}, + {0x06035D36, Azel_06035D36, .sum = 0x0EC21D, .length = 79, .foldable = 1}, + {0x06035DD4, Azel_06035DD4, .sum = 0x0EAFA6, .length = 78, .foldable = 1}, + {0x06035DE0, Azel_06035DE0, .sum = 0x0C20A5, .length = 72, .foldable = 1}, + {0x06035DE6, Azel_06035DE6, .sum = 0x0AA20A, .length = 69, .foldable = 1}, + {0x06035E70, Azel_06035E70, .sum = 0x042E91, .length = 24, .foldable = 1}, + {0x06035EA0, Azel_06035EA0, .sum = 0x042E97, .length = 24, .foldable = 1}, + {0x06035ED0, Azel_06035ED0, .sum = 0x042E9D, .length = 24, .foldable = 1}, + {0x06035F00, Azel_06035F00, .sum = 0x04AC41, .length = 35, .foldable = 1}, + {0x06035F04, Azel_06035F04, .sum = 0x036FCE, .length = 33, .foldable = 1}, + {0x060360F0, Azel_060360F0, .sum = 0x00D021, .length = 4, .foldable = 1}, + {0x0603A22C, Azel_0603A22C, Azel_0603A22C_detect}, + {0x0603A242, Azel_0603A242, .sum = 0x11703E, .length = 49}, + {0x0603ABE0, Azel_0603ABE0, .sum = 0x0BABA4, .length = 35}, + {0x0603DD6E, Azel_0603DD6E, .sum = 0x0BB96E, .length = 31}, + {0x0605444C, NULL, .sum = 0x102906, .length = 56, + .hint_constant_reg = 1<<12}, + {0x06057450, NULL, .sum = 0x0D4F8B, .length = 37, + .hint_constant_reg = 1<<12}, + {0x06058910, NULL, .sum = 0x0F2F81, .length = 52, + .hint_constant_reg = 1<<12}, + {0x06059068, NULL, .sum = 0x42DE2F, .length = 196, + .hint_constant_reg = 1<<11}, + {0x0605906E, NULL, .sum = 0x40F2FD, .length = 193, + .hint_constant_reg = 1<<11}, + {0x060693AE, NULL, .sum = 0x452D32, .length = 237, + .hint_constant_reg = 1<<14}, + {0x0606B7E4, NULL, .sum = 0x5AFDCE, .length = 292, + .hint_constant_reg = 1<<10}, + {0x0606B898, NULL, .sum = 0x2FFF9D, .length = 155, + .hint_constant_reg = 1<<10 | 1<<12}, + {0x06080280, NULL, .sum = 0x22D6B8, .length = 134, + .hint_constant_reg = 1<<13}, + {0x0608DE80, NULL, .sum = 0x114129, .length = 53, + .hint_constant_reg = 1<<13}, + {0x060A03FE, NULL, .sum = 0x4D786E, .length = 246, + .hint_constant_reg = 1<<10 | 1<<14}, + +}; + +/*************************************************************************/ + +#endif // ENABLE_JIT + +/*************************************************************************/ +/************************** Interface function ***************************/ +/*************************************************************************/ + +/** + * saturn_optimize_sh2: Search for and return, if available, a native + * implementation of the SH-2 routine starting at the given address. + * If for_fold is nonzero, this function returns nonzero and NULL in + * *func_ret to indicate that the routine at the given address should not + * be folded. + * + * [Parameters] + * state: Processor state block pointer + * address: Address from which to translate + * fetch: Pointer corresponding to "address" from which opcodes can + * be fetched + * func_ret: Pointer to variable to receive address of native function + * implementing this routine if return value is nonzero + * for_fold: Nonzero if the callback is being called to look up a + * subroutine for folding, zero if being called for a + * full block translation + * [Return value] + * Length of translated block in instructions (nonzero) if a native + * function is returned, else zero + */ +unsigned int saturn_optimize_sh2(SH2State *state, uint32_t address, + const uint16_t *fetch, + SH2NativeFunctionPointer *func_ret, + int for_fold) +{ +#ifdef ENABLE_JIT + + unsigned int i; + for (i = 0; i < lenof(hand_tuned_table); i++) { + + if (hand_tuned_table[i].address != address) { + continue; + } + + unsigned int num_insns; + if (hand_tuned_table[i].detect) { + num_insns = hand_tuned_table[i].detect(state, address, fetch); + if (!num_insns) { + continue; + } + } else { + num_insns = hand_tuned_table[i].length; + const uint32_t sum = checksum_fast16(fetch, num_insns); + if (sum != hand_tuned_table[i].sum) { + continue; + } + } + + if (hand_tuned_table[i].execute == NULL) { + + if (for_fold) { + /* Tell the caller not to fold this function, because we + * want to apply our optimization hints (which we can't do + * while folding). */ + *func_ret = NULL; + return 1; + } + + uint32_t bitmask; + unsigned int bit; + + for (bitmask = hand_tuned_table[i].hint_constant_reg, bit = 0; + bitmask != 0; + bitmask &= ~(1< bf fetch[12] + && fetch[ 2] == 0xA008 // bra fetch[12] --> nop + && fetch[ 3] == 0x0009 // nop + && fetch[ 4] == 0x62D3 // mov r13, r2 + && fetch[ 5] == 0x32E7 // cmp/gt r14, r2 + && fetch[ 6] == 0x8F02 // bf/s fetch[10] --> bf/s fetch[-2] + && fetch[ 7] == 0x7D01 // add #1, r13 + && fetch[ 8] == 0xA008 // bra fetch[18] + && fetch[ 9] == 0xE000 // mov #0, r0 + && fetch[10] == 0xAFF2 // bra fetch[-2] + && fetch[11] == 0x0009 // nop + ? 12 : 0; +} + +static FASTCALL void BIOS_000025AC(SH2State *state) +{ + if (UNLIKELY(state->R[0])) { + state->SR &= ~SR_T; + state->PC += 2*12; + state->cycles += 5; + return; + } + + state->R[2] = state->R[13]; + state->SR &= ~SR_T; + state->SR |= (state->R[13] > state->R[14]) << SR_T_SHIFT; + if (UNLIKELY(state->R[13]++ > state->R[14])) { + state->R[0] = 0; + state->PC += 2*18; + state->cycles += 11; + return; + } + + if (LIKELY(state->R[13]+15 <= state->R[14])) { + state->R[13] += 15; + state->cycles += 15*76 + 15; + } else { + state->cycles += 15; + } + state->PR = state->PC; + state->PC = state->R[12]; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x2EFA: CD read loop */ + +static int BIOS_00002EFA_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return fetch[0] == 0x6503 // mov r0, r5 + && fetch[1] == 0x3CB3 // cmp/ge r11, r12 + && fetch[2] == 0x8D06 // bt/s fetch[10] + && fetch[3] == 0x64C3 // mov r12, r4 + // fetch[4] == 0x7401 // add #1, r4 -- these two are swapped + // fetch[5] == 0x6352 // mov.l @r5, r3 -- in some BIOS versions + && fetch[6] == 0x2E32 // mov.l r3, @r14 + && fetch[7] == 0x34B3 // cmp/ge r11, r4 + && fetch[8] == 0x8FFA // bf/s fetch[4] + && fetch[9] == 0x7E04 // add #4, r14 + ? 10 : 0; +} + +static FASTCALL void BIOS_00002EFA(SH2State *state) +{ + uint8_t *page_base; + + if (UNLIKELY(state->R[0] != 0x25818000) + || UNLIKELY(!(page_base = direct_pages[state->R[14]>>19])) + ) { + state->R[5] = state->R[0]; + state->PC += 2; + state->cycles += 1; + return; + } + + state->R[5] = state->R[0]; + state->SR |= SR_T; // Always ends up set from here down + + int32_t count = state->R[11]; + int32_t i = state->R[12]; + int32_t left = count - i; + if (UNLIKELY(left <= 0)) { + state->R[4] = i; + state->PC += 2*10; + state->cycles += 5; + return; + } + + uint8_t *ptr = page_base + state->R[14]; + state->R[4] = count; + state->R[14] += left*4; + state->PC += 2*10; + state->cycles += 7*left + (4-1); + + /* Call the copy routine last to avoid unnecessary register saves and + * restores. */ + Cs2RapidCopyT2(ptr, left); +} + +/*-----------------------------------------------------------------------*/ + +/* 0x3BC6: CD status read routine */ + +static FASTCALL void BIOS_00003BC6(SH2State *state) +{ + /* With the current CS2 implementation, this all amounts to a simple + * read of registers CR1-CR4, but let's not depend on that behavior. */ + + state->R[0] = -3; // Default return value (error) + + unsigned int try; + for (try = 0; try < 100; try++, state->cycles += 67) { + const unsigned int CR1 = Cs2ReadWord(0x90018); + const unsigned int CR2 = Cs2ReadWord(0x9001C); + const unsigned int CR3 = Cs2ReadWord(0x90020); + const unsigned int CR4 = Cs2ReadWord(0x90024); + HRAM_STOREW(state->R[4], 0, CR1); + HRAM_STOREW(state->R[4], 2, CR2); + HRAM_STOREW(state->R[4], 4, CR3); + HRAM_STOREW(state->R[4], 6, CR4); + + const unsigned int CR1_test = Cs2ReadWord(0x90018); + const unsigned int CR2_test = Cs2ReadWord(0x9001C); + const unsigned int CR3_test = Cs2ReadWord(0x90020); + const unsigned int CR4_test = Cs2ReadWord(0x90024); + if (CR1_test==CR1 && CR2_test==CR2 && CR3_test==CR3 && CR4_test==CR4) { + state->R[0] = 0; + state->cycles += 65; + break; + } + } + + state->PC = state->PR; + state->cycles += 15 + 12; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6001670: Sound RAM load loop (optimized to avoid slowdowns from ME + * cache issues, since it includes a read-back test) */ + +static FASTCALL void BIOS_06001670(SH2State *state) +{ + const uint32_t data = MappedMemoryReadLong(state->R[1]); + state->R[1] += 4; + MappedMemoryWriteLong(state->R[3], data); + state->R[3] += 4; + state->R[6]--; + if (state->R[6] != 0) { + state->cycles += 50; + } else { + state->PC = state->PR; + state->cycles += 52; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6010D22: 3-D intro animation idle loop */ + +static int BIOS_06010D22_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return (fetch[-2] & 0xF000) == 0xB000 // bsr 0x60101AC [only wastes time] + && fetch[-1] == 0xE41E // mov #30, r4 + && fetch[ 0] == 0xD011 // mov.l @(0x6010D68,pc), r0 + && fetch[ 1] == 0x6001 // mov.w @r0, r0 + && fetch[ 2] == 0x2008 // tst r0, r0 + && fetch[ 3] == 0x8BF9 // bf 0x6010D1E + ? 4 : 0; +} + +static FASTCALL void BIOS_06010D22(SH2State *state) +{ + const uint32_t address = HRAM_LOADL(state->PC & -4, 4 + 0x11*4); + state->R[0] = HRAM_LOADW(address, 0); + if (state->R[0]) { + state->SR &= ~SR_T; + state->cycles = state->cycle_limit; + } else { + state->SR |= SR_T; + state->PC += 8; + state->cycles += 4; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x60115A4, etc.: Routine with multiple JSRs and a looping recursive BSR + * (this helps cover the block switch overhead) */ + +static FASTCALL void BIOS_060115A4(SH2State *state) +{ + state->R[15] -= 12; + HRAM_STOREL(state->R[15], 8, state->R[14]); + HRAM_STOREL(state->R[15], 4, state->R[13]); + HRAM_STOREL(state->R[15], 0, state->PR); + state->R[14] = state->R[4]; + state->PR = state->PC + 9*2; + + state->PC = HRAM_LOADL((state->PC + 3*2) & -4, 4 + 0x24*4); + BIOS_0602E364(state); + + state->PC = HRAM_LOADL(state->R[14], 16); + state->cycles += 11; +} + +/*----------------------------------*/ + +static int BIOS_060115B6_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + const uint32_t jsr_address = HRAM_LOADL(state->PC & -4, 4 + 0x22*4); + return checksum_fast16(fetch, 5) == 0x3081A + && BIOS_0602E630_detect(state, jsr_address, + fetch + ((jsr_address - address) >> 1)) + ? 5 : 0; +} + +static FASTCALL void BIOS_060115B6(SH2State *state) +{ + state->R[4] = state->R[14]; + state->R[13] = 0; + state->PR = state->PC + 15*2; + state->PC = HRAM_LOADL(state->PC & -4, 4 + 0x22*4); + state->cycles += 7; + return BIOS_0602E630(state); +} + +/*----------------------------------*/ + +static int BIOS_060115D4_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return checksum_fast16(fetch-10, 21) == 0x8E03C ? 11 : 0; +} + +static FASTCALL void BIOS_060115D4(SH2State *state) +{ + const uint32_t R14_68_address = HRAM_LOADL(state->R[14], 68); + if (state->R[13] < HRAM_LOADL(R14_68_address, 0)) { + state->R[4] = HRAM_LOADL(R14_68_address, 4 + 4*state->R[13]); + state->R[13]++; + state->PC -= 24*2; // Recursive call to 0x60115A4 (PR is already set) + state->cycles += 19; + } else { + state->PR = HRAM_LOADL(state->R[15], 0); + state->R[13] = HRAM_LOADL(state->R[15], 4); + state->R[14] = HRAM_LOADL(state->R[15], 8); + state->R[15] += 12; + state->PC = HRAM_LOADL((state->PC + 8*2) & -4, 4 + 0x17*4); + state->cycles += 12; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x602E364: Short but difficult-to-optimize data initialization routine */ + +static FASTCALL void BIOS_0602E364(SH2State *state) +{ + const uint32_t r2 = HRAM_LOADL(state->PC & -4, 0*2 + 4 + 0xF*4); + const uint32_t r5 = HRAM_LOADL(state->PC & -4, 2*2 + 4 + 0xF*4); + const uint32_t r0 = HRAM_LOADL(state->PC & -4, 4*2 + 4 + 0xF*4); + const uint32_t r3 = HRAM_LOADW(r2, 0) + 1; + HRAM_STOREW(r2, 0, r3); + const uint32_t r6 = HRAM_LOADL(r0, 0); + const uint32_t r5_new = r5 + r3*48; + HRAM_STOREL(r0, 0, r5_new); + + /* Help out the optimizer by telling it we can load multiple values + * at once. */ + const uint32_t *src = (const uint32_t *)HRAM_PTR(r6); + uint32_t *dest = (uint32_t *)HRAM_PTR(r5_new); + unsigned int i; + for (i = 0; i < 12; i += 4) { + const uint32_t a = src[i+0]; + const uint32_t b = src[i+1]; + const uint32_t c = src[i+2]; + const uint32_t d = src[i+3]; + dest[i+0] = a; + dest[i+1] = b; + dest[i+2] = c; + dest[i+3] = d; + } + + state->PC = state->PR; + state->cycles += 87; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x602E630: Coordinate transformation */ + +static int BIOS_0602E630_is_UE; + +static int BIOS_0602E630_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + if (address == 0x602E630 && checksum_fast16(fetch,612) == 0xA87EE4) { + BIOS_0602E630_is_UE = 0; + return 612; + } + if (address == 0x603A630 && checksum_fast16(fetch,600) == 0xA9F4CC) { + BIOS_0602E630_is_UE = 1; + return 600; + } + return 0; +} + +static FASTCALL void BIOS_0602E630(SH2State *state) +{ + int32_t counter; + + int16_t * const R12_ptr = + (int16_t *)HRAM_PTR(HRAM_LOADL((state->PC + 11*2) & -4, 4 + 0x7B*4)); + int32_t * const R13_ptr = + (int32_t *)HRAM_PTR(HRAM_LOADL((state->PC + 12*2) & -4, 4 + 0x7B*4)); + + const uint32_t M_R7 = HRAM_LOADL((state->PC + 24*2) & -4, 4 + 0x76*4); + const int32_t * const M = (const int32_t *)HRAM_PTR(HRAM_LOADL(M_R7, 0)); + + const uint32_t R5 = HRAM_LOADL(state->R[4], 56) + 4; + counter = (int16_t)HRAM_LOADW(R5, -4); + state->cycles += 30; + + if (counter > 0) { + + /* 0x602E66E */ + + state->cycles += 19 + (counter * /*minimum of*/ 110); + +#ifdef PSP +# define DO_MULT(dest_all,dest_z,index) \ + int32_t dest_all, dest_z; \ + do { \ + int32_t temp_x, temp_y, temp_z; \ + asm(".set push; .set noreorder\n" \ + "lw %[temp_z], %[idx]*16+8(%[M])\n" \ + "lw %[temp_x], %[idx]*16+0(%[M])\n" \ + "lw %[temp_y], %[idx]*16+4(%[M])\n" \ + "ror %[temp_z], %[temp_z], 16\n" \ + "mult %[temp_z], %[in_z]\n" \ + "ror %[temp_x], %[temp_x], 16\n" \ + "ror %[temp_y], %[temp_y], 16\n" \ + "mfhi %[temp_z]\n" \ + "mflo %[dst_z]\n" \ + "madd %[temp_x], %[in_x]\n" \ + "sll %[temp_z], %[temp_z], 16\n" \ + "srl %[dst_z], %[dst_z], 16\n" \ + "lw %[temp_x], %[idx]*16+12(%[M])\n" \ + "madd %[temp_y], %[in_y]\n" \ + "or %[dst_z], %[dst_z], %[temp_z]\n" \ + "ror %[temp_z], %[temp_x], 16\n" \ + "mfhi %[temp_x]\n" \ + "mflo %[temp_y]\n" \ + "sll %[temp_x], %[temp_x], 16\n" \ + "srl %[temp_y], %[temp_y], 16\n" \ + "or %[dst_all], %[temp_x], %[temp_y]\n" \ + "addu %[dst_all], %[dst_all], %[temp_z]\n" \ + ".set pop" \ + : [dst_all] "=r" (dest_all), [dst_z] "=&r" (dest_z), \ + [temp_x] "=&r" (temp_x), [temp_y] "=&r" (temp_y), \ + [temp_z] "=&r" (temp_z) \ + : [M] "r" (M), [idx] "i" (index), [in_x] "r" (in_x), \ + [in_y] "r" (in_y), [in_z] "r" (in_z) \ + : "hi", "lo" \ + ); \ + } while (0) +#else // !PSP +# define GET_M(i) ((int64_t)(int32_t)RAM_SWAPL(M[(i)])) +# define DO_MULT(dest_all,dest_z,index) \ + const int32_t dest_z = ((int64_t)in_z * GET_M((index)*4+2)) >> 16; \ + int32_t dest_all = (((int64_t)in_x * GET_M((index)*4+0) \ + + (int64_t)in_y * GET_M((index)*4+1) \ + + (int64_t)in_z * GET_M((index)*4+2)) >> 16) \ + + GET_M((index)*4+3); +#endif + + const uint32_t testflag = HRAM_LOADL(state->R[4], 20); + const int32_t *in = (const int32_t *)HRAM_PTR(R5); + int32_t *out = R13_ptr; + int16_t *coord_out = R12_ptr; + + do { + const int32_t in_x = RAM_SWAPL(in[0]); + const int32_t in_y = RAM_SWAPL(in[1]); + const int32_t in_z = RAM_SWAPL(in[2]); + + DO_MULT(out_z, zz, 2); + if (out_z < 0 && testflag) { + out_z += out_z >> 3; + } + out[ 2] = RAM_SWAPL(out_z); + out[14] = RAM_SWAPL(out_z - (zz<<1)); + + DO_MULT(out_x, zx, 0); + out[ 0] = RAM_SWAPL(out_x); + out[12] = RAM_SWAPL(out_x - (zx<<1)); + + DO_MULT(out_y, zy, 1); + out[ 1] = RAM_SWAPL(out_y); + out[13] = RAM_SWAPL(out_y - (zy<<1)); + + /* The result gets truncated to 16 bits here, so we don't need + * to worry about the 32->24 bit precision loss with floats. + * (There are only a few pixels out of place during the entire + * animation as a result of rounding error.) */ + const float coord_mult = 192.0f / out_z; + *coord_out++ = (int16_t)ifloorf(out_x * coord_mult); + *coord_out++ = (int16_t)ifloorf(out_y * coord_mult); + + in += 3; + out += 3; + counter -= 2; + } while (counter > 0); + +#undef GET_M +#undef DO_MULT + + } // if (counter > 0) + + /* 0x602E840 */ + + /* Offset for second-half local data accesses */ + const int UE_PC_offset = (BIOS_0602E630_is_UE ? -12*2 : 0); + + const uint32_t R11 = HRAM_LOADL(state->R[4], 64); + counter = (int16_t)HRAM_LOADW(R11, 0); + state->cycles += 19; + + if (counter > 0) { + +#ifdef PSP +# define DOT3_16(v,x,y,z) __extension__({ \ + int32_t __temp1, __temp2, __result; \ + asm(".set push; .set noreorder\n" \ + "lw %[temp1], 0(%[V])\n" \ + "lw %[temp2], 4(%[V])\n" \ + "ror %[temp1], %[temp1], 16\n" \ + "mult %[temp1], %[X]\n" \ + "lw %[temp1], 8(%[V])\n" \ + "ror %[temp2], %[temp2], 16\n" \ + "madd %[temp2], %[Y]\n" \ + "ror %[temp1], %[temp1], 16\n" \ + "madd %[temp1], %[Z]\n" \ + "mflo %[result]\n" \ + "mfhi %[temp1]\n" \ + "sra %[result], %[result], 16\n" \ + "ins %[result], %[temp1], 16, 16\n" \ + ".set pop" \ + : [temp1] "=&r" (__temp1), [temp2] "=&r" (__temp2), \ + [result] "=r" (__result) \ + : [V] "r" (v), [X] "r" (x), [Y] "r" (y), [Z] "r" (z) \ + : "hi", "lo" \ + ); \ + __result; \ +}) +# define DOT3_32(v,x,y,z) __extension__({ \ + int32_t __temp1, __temp2, __result; \ + asm(".set push; .set noreorder\n" \ + "lw %[temp1], 0(%[V])\n" \ + "lw %[temp2], 4(%[V])\n" \ + "ror %[temp1], %[temp1], 16\n" \ + "mult %[temp1], %[X]\n" \ + "lw %[temp1], 8(%[V])\n" \ + "ror %[temp2], %[temp2], 16\n" \ + "madd %[temp2], %[Y]\n" \ + "ror %[temp1], %[temp1], 16\n" \ + "madd %[temp1], %[Z]\n" \ + "mfhi %[result]\n" \ + ".set pop" \ + : [temp1] "=&r" (__temp1), [temp2] "=&r" (__temp2), \ + [result] "=r" (__result) \ + : [V] "r" (v), [X] "r" (x), [Y] "r" (y), [Z] "r" (z) \ + : "hi", "lo" \ + ); \ + __result; \ +}) +#else // !PSP +# define DOT3_16(v,x,y,z) \ + (((int64_t)(int32_t)RAM_SWAPL((v)[0]) * (int64_t)(int32_t)(x) \ + + (int64_t)(int32_t)RAM_SWAPL((v)[1]) * (int64_t)(int32_t)(y) \ + + (int64_t)(int32_t)RAM_SWAPL((v)[2]) * (int64_t)(int32_t)(z)) >> 16) +# define DOT3_32(v,x,y,z) \ + (((int64_t)(int32_t)RAM_SWAPL((v)[0]) * (int64_t)(int32_t)(x) \ + + (int64_t)(int32_t)RAM_SWAPL((v)[1]) * (int64_t)(int32_t)(y) \ + + (int64_t)(int32_t)RAM_SWAPL((v)[2]) * (int64_t)(int32_t)(z)) >> 32) +#endif + + state->cycles += 68 + 95*(counter-2); + + /* 0x602E850 */ + + const int32_t *in = (const int32_t *)(HRAM_PTR(R11) + 28); + const int32_t *coord_in = &R13_ptr[12]; + const uint32_t out_address = + HRAM_LOADL((state->PC + 264*2 + UE_PC_offset) & -4, 4 + 0xA2*4); + int32_t *out = (int32_t *)HRAM_PTR(out_address); + int16_t *coord_out = &R12_ptr[8]; + + const uint16_t *R6_ptr = + (const uint16_t *)(HRAM_PTR(HRAM_LOADL(state->R[4], 60)) + 4); + const uint32_t flag_address = + HRAM_LOADL((state->PC + 348*2 + UE_PC_offset) & -4, 4 + 0x79*4); + int16_t *flag = (int16_t *)HRAM_PTR(flag_address); + + { + const int32_t M_2 = RAM_SWAPL(M[ 2]); + const int32_t M_6 = RAM_SWAPL(M[ 6]); + const int32_t M_10 = RAM_SWAPL(M[10]); + + out[0] = RAM_SWAPL(-M_2); + out[1] = RAM_SWAPL(-M_6); + out[2] = RAM_SWAPL(-M_10); + const int32_t *in0_0 = + (const int32_t *)((uintptr_t)R13_ptr + R6_ptr[3]); + R6_ptr += 10; + const int32_t test_0 = DOT3_32(in0_0, -M_2, -M_6, -M_10); + *flag++ = (test_0 < 0); + out += 3; + counter--; + + out[0] = RAM_SWAPL(M_2); + out[1] = RAM_SWAPL(M_6); + out[2] = RAM_SWAPL(M_10); + const int32_t *in0_1 = + (const int32_t *)((uintptr_t)R13_ptr + R6_ptr[3]); + R6_ptr += 10; + const int32_t test_1 = DOT3_32(in0_1, M_2, M_6, M_10); + *flag++ = (test_1 < 0); + out += 3; + counter--; + } + + do { + const int32_t in_x = RAM_SWAPL(in[0]); + const int32_t in_y = RAM_SWAPL(in[1]); + const int32_t in_z = RAM_SWAPL(in[2]); + + const int32_t out_x = DOT3_16(&M[0], in_x, in_y, in_z); + const int32_t out_y = DOT3_16(&M[4], in_x, in_y, in_z); + const int32_t out_z = DOT3_16(&M[8], in_x, in_y, in_z); + + out[0] = RAM_SWAPL(out_x); + out[1] = RAM_SWAPL(out_y); + out[2] = RAM_SWAPL(out_z); + + const float coord_mult = 192.0f / (int32_t)RAM_SWAPL(coord_in[2]); + *coord_out++ = + (int16_t)ifloorf((int32_t)RAM_SWAPL(coord_in[0]) * coord_mult); + *coord_out++ = + (int16_t)ifloorf((int32_t)RAM_SWAPL(coord_in[1]) * coord_mult); + coord_in += 3; + + const int32_t *in0 = + (const int32_t *)((uintptr_t)R13_ptr + R6_ptr[3]); + R6_ptr += 10; + const int32_t test = DOT3_32(in0, out_x, out_y, out_z); + *flag++ = (test < 0); + + in += 3; + out += 3; + counter--; + } while (counter > 0); + +#undef DOT3_16 +#undef DOT3_32 + + } // if (counter > 0) + + /* 0x602E914 */ + /* Note: At this point, all GPRs except R9, R12, R13, and R15 are dead */ + + const int16_t *flag = (const int16_t *)HRAM_PTR( + HRAM_LOADL((state->PC + 572*2 + UE_PC_offset) & -4, 4 + 0x0F*4)); + const int32_t *R1_ptr = (const int32_t *)HRAM_PTR( + HRAM_LOADL((state->PC + 378*2 + UE_PC_offset) & -4, 4 + 0x6C*4)); + const uint16_t *R6_ptr = (const uint16_t *)HRAM_PTR( + HRAM_LOADL(state->R[4], 60)); + const int32_t *R7_ptr = (const int32_t *)HRAM_PTR( + HRAM_LOADL((state->PC + 371*2 + UE_PC_offset) & -4, 4 + 0x6D*4)); + const uint32_t R9 = HRAM_LOADL((state->PC + 9*2) & -4, 4 + 0x7A*4); + uint16_t *R10_ptr = (uint16_t *)HRAM_PTR( + HRAM_LOADL((state->PC + 370*2 + UE_PC_offset) & -4, 4 + 0x73*4)); + + const int32_t limit = *R6_ptr; + R6_ptr += 2; + state->cycles += 13; + + for (counter = 0; counter < limit; counter++, R7_ptr += 3, R6_ptr += 10) { + + /* 0x602EAA8 */ + + if (!flag[counter]) { + state->cycles += 15; + continue; + } + + /* 0x602E924 */ + +#ifdef PSP + int32_t R2; + { + int32_t temp1, temp2, temp3; + asm(".set push; .set noreorder\n" + "lw %[temp1], 0(%[R7_ptr])\n" + "lw %[temp2], 0(%[R1_ptr])\n" + "lw %[temp3], 4(%[R7_ptr])\n" + "ror %[temp1], %[temp1], 16\n" + "ror %[temp2], %[temp2], 16\n" + "mult %[temp1], %[temp2]\n" + "lw %[temp1], 4(%[R1_ptr])\n" + "lw %[temp2], 8(%[R7_ptr])\n" + "ror %[temp3], %[temp3], 16\n" + "ror %[temp1], %[temp1], 16\n" + "madd %[temp3], %[temp1]\n" + "lw %[temp3], 8(%[R1_ptr])\n" + "ror %[temp2], %[temp2], 16\n" + "ror %[temp3], %[temp3], 16\n" + "madd %[temp2], %[temp3]\n" + "mflo %[temp1]\n" + "mfhi %[temp2]\n" + "sra %[temp1], %[temp1], 16\n" + "addiu %[temp2], %[temp2], 1\n" + "ins %[temp1], %[temp2], 16, 16\n" + "sra %[temp1], %[temp1], 10\n" + "max %[temp1], %[temp1], $zero\n" + "min %[R2], %[temp1], %[cst_127]\n" + ".set pop" + : [R2] "=r" (R2), [temp1] "=&r" (temp1), + [temp2] "=&r" (temp2), [temp3] "=&r" (temp3) + : [R7_ptr] "r" (R7_ptr), [R1_ptr] "r" (R1_ptr), + [cst_127] "r" (127) + : "hi", "lo" + ); + } +#else // !PSP + const int32_t mac = + ((int64_t) (int32_t)RAM_SWAPL(R7_ptr[0]) * (int32_t)RAM_SWAPL(R1_ptr[0]) + + (int64_t) (int32_t)RAM_SWAPL(R7_ptr[1]) * (int32_t)RAM_SWAPL(R1_ptr[1]) + + (int64_t) (int32_t)RAM_SWAPL(R7_ptr[2]) * (int32_t)RAM_SWAPL(R1_ptr[2]) + ) >> 16; + const int32_t R2_temp = (mac + 0x10000) >> 10; + const int32_t R2 = R2_temp < 0 ? 0 : R2_temp > 127 ? 127 : R2_temp; +#endif + const uint32_t R2_tableaddr = RAM_SWAPL(*(const uint32_t *)&R6_ptr[8]); + const uint16_t *R2_table = (const uint16_t *)HRAM_PTR(R2_tableaddr); + + const uint32_t R9_address = HRAM_LOADL(R9, 0); + HRAM_STOREL(R9, 0, R9_address + 48); + HRAM_STOREW(R9_address, 0, *R6_ptr); + HRAM_STOREW(R9_address, 42, R2_table[R2]); + + uint32_t * const R9_data32 = (uint32_t *)(HRAM_PTR(R9_address) + 4); + int32_t R3; + switch (R6_ptr[1] & 0xFF) { + case 0x00: + R9_data32[0] = *(uint32_t *)&R12_ptr[ 0]; + R9_data32[1] = *(uint32_t *)&R12_ptr[ 2]; + R9_data32[2] = *(uint32_t *)&R12_ptr[ 4]; + R9_data32[3] = *(uint32_t *)&R12_ptr[ 6]; + R3 = RAM_SWAPL(R13_ptr[5]) + RAM_SWAPL(R13_ptr[11]); + break; + + case 0x30: + R9_data32[0] = *(uint32_t *)&R12_ptr[ 8]; + R9_data32[1] = *(uint32_t *)&R12_ptr[14]; + R9_data32[2] = *(uint32_t *)&R12_ptr[12]; + R9_data32[3] = *(uint32_t *)&R12_ptr[10]; + R3 = RAM_SWAPL(R13_ptr[17]) + RAM_SWAPL(R13_ptr[23]); + break; + + case 0x60: + R9_data32[0] = *(uint32_t *)&R12_ptr[ 0]; + R9_data32[1] = *(uint32_t *)&R12_ptr[ 8]; + R9_data32[2] = *(uint32_t *)&R12_ptr[10]; + R9_data32[3] = *(uint32_t *)&R12_ptr[ 2]; + R3 = RAM_SWAPL(R13_ptr[5]) + RAM_SWAPL(R13_ptr[14]); + break; + + case 0x90: + R9_data32[0] = *(uint32_t *)&R12_ptr[ 4]; + R9_data32[1] = *(uint32_t *)&R12_ptr[12]; + R9_data32[2] = *(uint32_t *)&R12_ptr[14]; + R9_data32[3] = *(uint32_t *)&R12_ptr[ 6]; + R3 = RAM_SWAPL(R13_ptr[8]) + RAM_SWAPL(R13_ptr[23]); + break; + + case 0xC0: + R9_data32[0] = *(uint32_t *)&R12_ptr[ 2]; + R9_data32[1] = *(uint32_t *)&R12_ptr[10]; + R9_data32[2] = *(uint32_t *)&R12_ptr[12]; + R9_data32[3] = *(uint32_t *)&R12_ptr[ 4]; + R3 = RAM_SWAPL(R13_ptr[5]) + RAM_SWAPL(R13_ptr[20]); + break; + + default: // case 0xF0 + R9_data32[0] = *(uint32_t *)&R12_ptr[ 0]; + R9_data32[1] = *(uint32_t *)&R12_ptr[ 6]; + R9_data32[2] = *(uint32_t *)&R12_ptr[14]; + R9_data32[3] = *(uint32_t *)&R12_ptr[ 8]; + R3 = RAM_SWAPL(R13_ptr[11]) + RAM_SWAPL(R13_ptr[14]); + break; + } + + if (!BIOS_0602E630_is_UE && R3 < -0x30000 && (R6_ptr[1] & 0xFF00)) { + R3 = -R3; + } + R3 >>= 1; + uint32_t *R3_buffer = (uint32_t *)HRAM_PTR( + HRAM_LOADL((state->PC + 558*2 + UE_PC_offset) & -4, 4 + 0x17*4)); + R3_buffer[(*R10_ptr)++] = RAM_SWAPL(R3); + + state->cycles += 39 + /*approximately*/ 54; + } + + /* 0x602EAB8 */ + + state->PC = state->PR; + state->cycles += 10; +} + +/*************************************************************************/ + +/**** Azel: Panzer Dragoon RPG (JP) optimizations ****/ + +/*-----------------------------------------------------------------------*/ + +/* Common color calculation logic used by several routines */ + +static uint32_t Azel_color_calc(const int16_t *local_ptr, + const int16_t *r4_ptr, const int16_t *r5_ptr, + int32_t r, int32_t g, int32_t b) +{ + int32_t dot = r4_ptr[0] * local_ptr[0] + + r4_ptr[1] * local_ptr[1] + + r4_ptr[2] * local_ptr[2]; + if (dot > 0) { + dot >>= 16; + b += dot * r5_ptr[0]; + g += dot * r5_ptr[1]; + r += dot * r5_ptr[2]; + } + return (bound(b, 0, 0x1F00) & 0x1F00) << 2 + | (bound(g, 0, 0x1F00) & 0x1F00) >> 3 + | (bound(r, 0, 0x1F00) ) >> 8; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x600614C: Idle loop with a JSR */ + +static int Azel_0600614C_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return fetch[-2] == 0x4B0B // jsr @r11 + && fetch[-1] == 0x0009 // nop + && fetch[ 0] == 0x60E2 // mov.l @r14, r0 + && fetch[ 1] == 0x2008 // tst r0, r0 + && fetch[ 2] == 0x8BFA // bf fetch[-2] + && state->R[14] == 0x604BC80 + ? 3 : 0; +} + +static FASTCALL void Azel_0600614C(SH2State *state) +{ + /* Help out the optimizer by loading these early. */ + const int32_t cycle_limit = state->cycle_limit; + const uint32_t test = *(uint32_t *)HRAM_PTR(0x604BC80); + const uint32_t r3 = 0x604B68C; // @(0x38,pc=0x600FCB0) +#if defined(__mips__) && !defined(WORDS_BIGENDIAN) + /* Similar to the "volatile" in the 0x600C59C optimizer, we force this + * to be loaded during the load delay slots of "test". */ + const uint32_t r3_HI = (r3 + 0x8000) & 0xF0000; + uint32_t r3_test; + asm("lbu %0, %1(%2)" + : "=r" (r3_test) + : "i" (((r3 & 0xFFFFF) ^ 1) - r3_HI), "r" (HighWram + r3_HI)); +#else + const uint32_t r3_test = HRAM_LOADB(r3, 0); +#endif + + /* For a comparison against zero, we don't need to swap bytes. */ + if (LIKELY(test)) { + + state->cycles = cycle_limit; + + /* 0x600FCB0 almost always returns straight to us, so we implement + * the first part of it here. */ + if (UNLIKELY(r3_test != 0)) { + state->PR = state->PC; + state->PC = 0x600FCB0 + 4*2; + state->R[3] = r3; + } + + } else { + + state->PC += 2*3; + state->cycles += 3; + + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x60061F0: RBG0 parameter generation for sky/ground backgrounds. We + * also store the slope set here in a video tweak parameter, so the code + * doesn't have to re-derive it from the coefficients (which may be mangled + * by the "shimmering" effect added to water). */ + +static FASTCALL void Azel_060061F0(SH2State *state) +{ + int32_t r4 = state->R[4]; + int32_t r5 = state->R[5]; + int32_t delta = state->R[6]; + uint32_t counter = state->R[7]; + const uint32_t out_address = HRAM_LOADL(state->R[15], 0); + int32_t *out = (int32_t *)HRAM_PTR(out_address); + + extern float psp_video_tweaks_Azel_RBG0_slope; + extern float psp_video_tweaks_Azel_RBG0_first_recip; + + if (r4 < 0) { + r4 = -r4; + r5 = -r5; + delta = -delta; + } + + if (r4 != 0) { + psp_video_tweaks_Azel_RBG0_slope = (float)-delta / (float)r4; + } + + const float r4_scaled = r4 * 65536.0f; + const int32_t r4_test = r4 >> 14; // See below for why we use this. + + if (delta == 0) { + /* No horizon scaling is taking place. Note that r5 should be + * checked against zero here (and in similar cases below), but + * we're a bit more conservative in order to avoid FPU overflow + * errors on conversion to integer; since only the low 24 bits of + * the 16.16 fixed point result are significant, this isn't a + * problem in practice--it will only come up in cases where the + * horizon line almost exactly coincides with a screen line. */ + if (r5 > r4_test) { + const int32_t quotient = ifloorf(r4_scaled / (float)r5); + state->PC = state->PR; + state->cycles += 72 + (counter * 5); + for (; counter != 0; counter--, out++) { + *out = RAM_SWAPL(quotient); + } + } else { + state->PC = state->PR; + state->cycles += 30 + (counter * 5); + for (; counter != 0; counter--, out++) { + *out = -1; + } + } + return; + } + + const int32_t r5_final = r5 - (delta * (counter-1)); + + if (delta > 0 ? (r5 <= r4_test) : (r5_final <= r4_test)) { + /* The entire background layer is outside the horizon area. */ + state->PC = state->PR; + state->cycles += (delta > 0 ? 40 : 43) + (counter * 5); + for (; counter != 0; counter--, out++) { + *out = -1; + } + return; + } + + if (delta > 0 && r5_final <= r4_test) { + /* The bottom of the background layer is outside the horizon area. */ + const uint32_t partial_count = + MIN(counter - ((r5 - r4_test) / delta) + 1, counter); + state->cycles += 88 + (partial_count * 5); + counter -= partial_count; + int32_t *out_temp = out + counter; + uint32_t i; + for (i = partial_count; i != 0; i--, out_temp++) { + *out_temp = -1; + } + } else if (delta < 0 && r5 <= r4_test) { + /* The top of the background layer is outside the horizon area. */ + const uint32_t partial_count = + MIN((r5 - r4_test) / delta + 1, counter); + state->cycles += 86 + (partial_count * 5); + r5 -= delta * partial_count; + counter -= partial_count; + uint32_t i; + for (i = partial_count; i != 0; i--, out++) { + *out = -1; + } + } else { + /* The entire background layer is within the horizon area. */ + state->cycles += (delta > 0 ? 39 : 42); + } + + state->PC = state->PR; + state->cycles += 15 + (counter * 32) + (counter%2 ? 10 : 0); + + if (r4 != 0) { + psp_video_tweaks_Azel_RBG0_first_recip = (float)r5 / (float)r4; + } + for (; counter != 0; counter--, out++) { + *out = RAM_SWAPL(ifloorf(r4_scaled / (float)r5)); + r5 -= delta; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x600C4DC: Routine to register a function in the SSH2 call table. This + * could be optimized by the main code with a few hints if it had better + * flow analysis. */ + +static FASTCALL void Azel_0600C4DC(SH2State *state) +{ + const uint32_t base = 0x604B604; + const unsigned int index = HRAM_LOADW(base, 0); + const unsigned int next_index = (index + 1) & 7; + + if (HRAM_LOADL(base, 4 + next_index*16 + 0) != 0) { + MappedMemoryWriteByte(0xFFFFFE11, 0); + } + HRAM_STOREL(base, 4 + index*16 + 4, state->R[4]); + HRAM_STOREL(base, 4 + index*16 + 8, state->R[5]); + HRAM_STOREL(base, 4 + index*16 + 12, state->R[6]); + HRAM_STOREL(base, 4 + index*16 + 0, state->R[7]); + if (index == HRAM_LOADW(base, 2)) { + MappedMemoryWriteWord(0x21000000, 0); + } + HRAM_STOREW(base, 0, next_index); + + state->PC = state->PR; + state->cycles += 47; // Approximate +} + +/*-----------------------------------------------------------------------*/ + +/* 0x600C59C, etc.: SSH2 main loop (idle + function call) */ + +static NOINLINE void Azel_0600C5A4(SH2State *state); +static ALWAYS_INLINE void Azel_0600C5C0( + SH2State *state, const uint32_t *r13_ptr, int extra_cycles); + +/*----------------------------------*/ + +static FASTCALL void Azel_0600C59C(SH2State *state) +{ + int32_t test = (int8_t)(((SH2_struct *)(state->userdata))->onchip.FTCSR); + + /* Again, help out the optimizer. This "volatile" actually speeds + * things up, because it forces the compiler to load this during the + * load delay slots of "test", rather than delaying the load so far + * that the subsequent store to state->cycles ends up stalling. + * (Granted, this is properly an optimizer bug that ought to be fixed + * in GCC. But since GCC's scheduler is so complex it might as well + * be random chaos, what else can we do?) */ + int32_t cycle_limit = +#ifdef __mips__ + *(volatile int32_t *)& +#endif + state->cycle_limit; + + if (LIKELY(test >= 0)) { + state->cycles = cycle_limit; + } else { + state->PR = 0x600C5B4; // Make sure PR is set before 0x600C5C0. + /* We deliberately chain to a separate NOINLINE routine here to + * minimize register saves and restores in the common case above, + * since even setting up and tearing down a stack frame adds a + * significant percentage to the execution time of this function. */ + return Azel_0600C5A4(state); + } +} + +/*----------------------------------*/ + +static NOINLINE void Azel_0600C5A4(SH2State *state) +{ + MappedMemoryWriteByte(0xFFFFFE11, 0); + const unsigned int index = HRAM_LOADW(state->R[13], 2); + const uint32_t *r13_ptr = (const uint32_t *)HRAM_PTR(state->R[13]); + return Azel_0600C5C0(state, &r13_ptr[1 + index*4], 7); +} + +/*----------------------------------*/ + +static int Azel_0600C5B4_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return checksum_fast16(fetch-6, 24) == 0x93C29 ? 24 : 0; +} + +static FASTCALL void Azel_0600C5B4(SH2State *state) +{ + const uint32_t r13 = state->R[13]; + uint32_t *r13_ptr = (uint32_t *)HRAM_PTR(r13); + unsigned int index = HRAM_LOADW(r13, 2); + r13_ptr[1 + index*4] = 0; + index = (index + 1) & 7; + HRAM_STOREW(r13, 2, index); + MappedMemoryWriteWord(0x21800000, 0); + return Azel_0600C5C0(state, &r13_ptr[1 + index*4], 6); +} + +/*----------------------------------*/ + +static ALWAYS_INLINE void Azel_0600C5C0( + SH2State *state, const uint32_t *r13_ptr, int extra_cycles) +{ + const uint32_t func_address = RAM_SWAPL(r13_ptr[0]); + if (func_address) { + state->R[4] = RAM_SWAPL(r13_ptr[1]); + state->R[5] = RAM_SWAPL(r13_ptr[2]); + state->R[6] = RAM_SWAPL(r13_ptr[3]); + /* PR is known to be correct (0x600C5B4) here. */ + state->PC = func_address; + state->cycles += extra_cycles + 19; + } else { + state->PC = 0x600C59C; + state->cycles = state->cycle_limit; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x600C5F8, 0x600C690: Cache flush routines (no-ops in emulation) */ + +static FASTCALL void Azel_0600C5F8(SH2State *state) +{ + state->R[0] = 0x604AEA8 + HRAM_LOADW(0x604B606, 0) * 48; + state->PC = state->PR; + state->cycles += 48; +} + +static FASTCALL void Azel_0600C690(SH2State *state) +{ + state->PC = state->PR; + state->cycles += 11 + (state->R[5] / 16) * 5; // Approximate +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6010F24/0x6010F52: Bitmap copy loop (for movies) */ + +static int Azel_06010F24_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return fetch[ 0] == 0x6693 // mov r9, r6 + && fetch[ 1] == 0x65D3 // mov r13, r5 + && fetch[ 2] == 0x4A0B // jsr @r10 [DMA copy routine] + && fetch[ 3] == 0x64E3 // mov r14, r4 + // fetch[ 4] == 0x53F1 // {mov.l @(4,r15), r3 [counter] | add r8, r14} + // fetch[ 5] == 0x3E8C // {add r8, r14 | mov.l @r15, r3 [counter]} + && fetch[ 6] == 0x73FF // add #-1, r3 + // fetch[ 7] == 0x2338 // {tst r3, r3 | mov.l r3, @r15} + // fetch[ 8] == 0x1F31 // {mov.l r3, @(4,r15) | tst r3, r3} + && fetch[ 9] == 0x8FF5 // bf/s fetch[0] + && fetch[10] == 0x3DCC // add r12, r13 + && state->R[13]>>20 == 0x25E + ? 11 : 0; +} + +static FASTCALL void Azel_06010F24(SH2State *state) +{ + const uint32_t src_addr = state->R[14]; + const uint32_t dest_addr = state->R[13]; + const uint32_t size = state->R[9]; + const uint32_t *src = (const uint32_t *)HRAM_PTR(src_addr); + uint32_t *dest = (uint32_t *)VDP2_PTR(dest_addr); + + uint32_t i; + for (i = 0; i < size; i += 16, src += 4, dest += 4) { + const uint32_t word0 = src[0]; + const uint32_t word1 = src[1]; + const uint32_t word2 = src[2]; + const uint32_t word3 = src[3]; + dest[0] = RAM_VDP_SWAPL(word0); + dest[1] = RAM_VDP_SWAPL(word1); + dest[2] = RAM_VDP_SWAPL(word2); + dest[3] = RAM_VDP_SWAPL(word3); + } + + state->R[14] = src_addr + state->R[8]; + state->R[13] = dest_addr + state->R[12]; + + /* Conveniently, the counter is always stored in R3 when we get here + * (it's first loaded when the loop is executed on the fall-in case) + * and the stack variables are never referenced once their respective + * loops complete, so we don't have to worry about which loop we're in. */ + unsigned int counter = state->R[3]; + counter--; + state->R[3] = counter; + + if (counter != 0) { + state->SR &= ~SR_T; + state->cycles += 290; + } else { + state->SR |= SR_T; + state->PC += 2*11; + state->cycles += 289; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6014274: Calculation routine (possibly for the line scroll table?) */ + +static FASTCALL void Azel_06014274(SH2State *state) +{ + const int32_t * const sin_table = (const int32_t *)LRAM_PTR(0x20216660); + + int32_t *r4_ptr = (int32_t *)HRAM_PTR(state->R[4]); + + int32_t r9 = RAM_SWAPL(r4_ptr[6]); + r4_ptr[6] = RAM_SWAPL(r9 + RAM_SWAPL(r4_ptr[1])); + const int32_t r10 = RAM_SWAPL(r4_ptr[7]); + r4_ptr[7] = RAM_SWAPL(r10 + RAM_SWAPL(r4_ptr[4])); + const int32_t r11 = 176; + const int32_t r5 = RAM_SWAPL(r4_ptr[2]); + const int32_t r6 = RAM_SWAPL(r4_ptr[5]); + const int32_t r7 = RAM_SWAPL(r4_ptr[3]); + r4_ptr = (int32_t *)HRAM_PTR(RAM_SWAPL(r4_ptr[0])); + + unsigned int counter; + for (counter = 224; counter != 0; counter--) { + const int32_t r0 = ((int64_t)r7 * sin_table[r9>>16 & 0xFFF]) >> 16; + /* We drop 2 bits of precision here so we can use a 32/32bit + * integer divide instruction; it shouldn't make a difference in + * the visible result. */ + const int32_t r12 = ((r7 + r0) >> 2) + 0x4000; + const int32_t quotient = 0x40000000 / r12; + r4_ptr[1] = RAM_SWAPL((RAM_SWAPL(r4_ptr[1]) + r6) & 0xFFFFFF); + r4_ptr[2] = RAM_SWAPL(quotient); + r4_ptr[0] = RAM_SWAPL((0x10000 - quotient) * r11 + r10); + r4_ptr += 3; + r9 += r5; + } + + state->PC = state->PR; + state->cycles += 86 + 222*48 + 58; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601E330: Input capture check */ + +static FASTCALL void Azel_0601E330(SH2State *state) +{ + int32_t test = (int8_t)(((SH2_struct *)(state->userdata))->onchip.FTCSR); + const uint32_t param = HRAM_LOADL(0x604AEA4, 0); + if (LIKELY(test >= 0)) { + state->R[5] = param; + state->R[6] = 0x604FCFC; + state->R[7] = 0x604BCB4; + state->PC = 0x601F1E4; + state->cycles += 21; + } else { + const uint32_t old_r4 = state->R[4]; + state->R[4] = param; + + /* 0x600C5D8 */ + const unsigned int index = HRAM_LOADW(0x604B604, 0); + state->R[5] = 0x604AEA8 + index*48; + state->PC = 0x6035AA0; + Azel_06035AA0(state); + + /* 0x601E3A6 */ + state->R[4] = old_r4; + state->R[5] = 0; + state->R[6] = 0; + state->R[7] = 0x601E314; + state->PC = 0x600C4DC; + state->cycles += 16 + 17 + 10; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601E910, etc.: Various varieties of vertex manipulation routines and + * their subfunctions */ + +static void Azel_0601E910_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)); +static void Azel_0601E9A4(SH2State *state, const uint32_t color_data, + int swapflag); +static int Azel_0601EB10(const int16_t *vertex_data, uint32_t color_data, + int base_r, int base_g, int base_b); + +static void Azel_0601EC20_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)); +static void Azel_0601EC58(SH2State *state, int swapflag); +static int Azel_0601ED58(const int16_t *vertex_data, + int base_r, int base_g, int base_b); +static int Azel_0601EDDC(const int16_t *vertex_data, + int base_r, int base_g, int base_b); + +static void Azel_0601F2D6_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)); +static void Azel_0601F49C(SH2State *state, int swapflag); +static int Azel_0601FA68(const int16_t *vertex_data, + int base_r, int base_g, int base_b); +static int Azel_0601FAEC(const int16_t *vertex_data, + int base_r, int base_g, int base_b); + +static int Azel_0601F58A(SH2State *state, uint32_t r11, uint32_t r13); +static int Azel_0601F5A6(SH2State *state, + const int16_t *r10_ptr, const int16_t *r11_ptr, + const int16_t *r12_ptr, const int16_t *r13_ptr); +static int Azel_0601F5D2(SH2State *state); + +static int Azel_0601F5EE(SH2State *state, uint32_t r11, uint32_t r13); + +static ALWAYS_INLINE int Azel_0601F762( + SH2State *state, const uint32_t * const r10_ptr, + const uint32_t * const r11_ptr, const uint32_t * const r12_ptr, + const uint32_t * const r13_ptr, uint32_t * const r14_ptr, + const uint32_t r4_0, const int swapflag); + +static int32_t Azel_0601F824(const int16_t *r6_ptr, uint32_t *r10_ptr, + uint32_t *r11_ptr, uint32_t *r12_ptr, + uint32_t *r13_ptr, uint32_t mask, + int (*clipfunc)(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr)); +static int Azel_0601F93A(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr); +static int Azel_0601F948(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr); +static ALWAYS_INLINE int Azel_0601F950(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr, + const int32_t r8_x, const int32_t x_lim, + int base_cycles); +static int Azel_0601F988(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr); +static int Azel_0601F996(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr); +static ALWAYS_INLINE int Azel_0601F99E(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr, + const int32_t r8_y, const int32_t y_lim, + int base_cycles); +static int Azel_0601F9D6(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr); + +/*----------------------------------*/ + +static FASTCALL void Azel_0601E910(SH2State *state) +{ + return Azel_0601E910_common(state, Azel_0601F58A); +} + +static FASTCALL void Azel_0601E95A(SH2State *state) +{ + return Azel_0601E910_common(state, Azel_0601F5EE); +} + +static FASTCALL void Azel_0601EC20(SH2State *state) +{ + return Azel_0601EC20_common(state, Azel_0601F58A); +} + +static FASTCALL void Azel_0601EC3C(SH2State *state) +{ + return Azel_0601EC20_common(state, Azel_0601F5EE); +} + +static FASTCALL void Azel_0601F2D6(SH2State *state) +{ + return Azel_0601F2D6_common(state, Azel_0601F58A); +} + +static FASTCALL void Azel_0601F2F2(SH2State *state) +{ + return Azel_0601F2D6_common(state, Azel_0601F5EE); +} + +/*----------------------------------*/ + +static void Azel_0601E910_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)) +{ + const uint32_t saved_PR = state->PR; + + uint32_t color_data = HRAM_LOADL(state->R[15], 4); + uint32_t index = 0; + uint32_t r11 = LRAM_LOADL(state->R[4], 0); + uint32_t r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + while (r11 != r13) { + const int swapflag = (*clipfunc)(state, r11, r13); + const int clipped = (swapflag < 0); + if (LRAM_LOADL(color_data, 0) == index) { + if (clipped) { + state->cycles += 25; + } else { + Azel_0601E9A4(state, color_data + 4, swapflag); + state->cycles += 21; + } + color_data += 16; + } else { + if (clipped) { + state->cycles += 19; + } else { + Azel_0601F49C(state, swapflag); + state->cycles += 20; + } + } + index++; + r11 = LRAM_LOADL(state->R[4], 0); + r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + } + + state->PC = saved_PR; + state->cycles += 11; +} + +static void Azel_0601E9A4(SH2State *state, const uint32_t color_data, + int swapflag) +{ + static const uint16_t cycles_used[4][4] = + {{13,13,13,13},{312,319,321,319},{315,322,324,322},{309,317,319,317}}; + + const int16_t *quad_data = (const int16_t *)LRAM_PTR(state->R[4]); + const int quad_type = (quad_data[-6] >> 8) & 3; + uint32_t *gbr_ptr = (uint32_t *)HRAM_PTR(state->GBR); + + state->cycles += cycles_used[quad_type][swapflag]; + + gbr_ptr[0] = RAM_SWAPL(state->R[14] + 32); + + if (quad_type == 0) { + return; + } + + const uint32_t color_addr = RAM_SWAPL(gbr_ptr[4]); + int16_t *out = (int16_t *)VDP1_PTR(color_addr); + gbr_ptr[4] = RAM_SWAPL(color_addr + 8); + VDP1_STOREW(state->R[14], 28, color_addr >> 3); + + const int16_t *base_ptr = + (const int16_t *)(HRAM_PTR(0x601FCB0) + (state->R[1]>>7 & -8)); + const int base_r = base_ptr[0]; + const int base_g = base_ptr[1]; + const int base_b = base_ptr[2]; + int a, b, c, d; + + switch (quad_type) { + case 1: + a = Azel_0601EB10(quad_data, color_data+0, base_r, base_g, base_b); + b = Azel_0601EB10(quad_data, color_data+3, base_r, base_g, base_b); + c = Azel_0601EB10(quad_data, color_data+6, base_r, base_g, base_b); + d = Azel_0601EB10(quad_data, color_data+9, base_r, base_g, base_b); + state->R[4] += 8; + break; + + case 2: + a = Azel_0601EB10(quad_data+0, color_data+0, base_r, base_g, base_b); + b = Azel_0601EB10(quad_data+6, color_data+3, base_r, base_g, base_b); + c = Azel_0601EB10(quad_data+12,color_data+6, base_r, base_g, base_b); + d = Azel_0601EB10(quad_data+18,color_data+9, base_r, base_g, base_b); + state->R[4] += 48; + break; + + default: // case 3 + a = Azel_0601EB10(quad_data+0, color_data+0, base_r, base_g, base_b); + b = Azel_0601EB10(quad_data+3, color_data+3, base_r, base_g, base_b); + c = Azel_0601EB10(quad_data+6, color_data+6, base_r, base_g, base_b); + d = Azel_0601EB10(quad_data+9, color_data+9, base_r, base_g, base_b); + state->R[4] += 24; + break; + } // switch (quad_type) + + out[0 ^ swapflag] = VDP_SWAPW(a); + out[1 ^ swapflag] = VDP_SWAPW(b); + out[2 ^ swapflag] = VDP_SWAPW(c); + out[3 ^ swapflag] = VDP_SWAPW(d); +} + +static int Azel_0601EB10(const int16_t *vertex_data, uint32_t color_data, + int base_r, int base_g, int base_b) +{ + return Azel_color_calc((const int16_t *)HRAM_PTR(0x601FC94), + vertex_data, + (const int16_t *)HRAM_PTR(0x601FCA8), + base_r + ((int8_t)LRAM_LOADB(color_data,2) << 8), + base_g + ((int8_t)LRAM_LOADB(color_data,1) << 8), + base_b + ((int8_t)LRAM_LOADB(color_data,0) << 8)); +} + +/*----------------------------------*/ + +static void Azel_0601EC20_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)) +{ + const uint32_t saved_PR = state->PR; + + uint32_t r11 = LRAM_LOADL(state->R[4], 0); + uint32_t r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + while (r11 != r13) { + const int swapflag = (*clipfunc)(state, r11, r13); + const int clipped = (swapflag < 0); + if (clipped) { + state->cycles += 12; + } else { + Azel_0601EC58(state, swapflag); + state->cycles += 13; + } + r11 = LRAM_LOADL(state->R[4], 0); + r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + } + + state->PC = saved_PR; + state->cycles += 11; +} + +static void Azel_0601EC58(SH2State *state, int swapflag) +{ + static const uint16_t cycles_used[4][4] = + {{10,10,10,10}, {98,98,98,98}, {285,291,293,291}, {266,272,274,272}}; + + const int16_t *quad_data = (const int16_t *)LRAM_PTR(state->R[4]); + const int quad_type = (quad_data[-6] >> 8) & 3; + uint32_t *gbr_ptr = (uint32_t *)HRAM_PTR(state->GBR); + + state->cycles += cycles_used[quad_type][swapflag]; + + gbr_ptr[0] = RAM_SWAPL(state->R[14] - 32); + + if (quad_type == 0) { + return; + } + + const uint32_t color_addr = RAM_SWAPL(gbr_ptr[4]); + int16_t *out = (int16_t *)VDP1_PTR(color_addr); + gbr_ptr[4] = RAM_SWAPL(color_addr - 8); + VDP1_STOREW(state->R[14], 28, color_addr >> 3); + + const int16_t *base_ptr = + (const int16_t *)(HRAM_PTR(0x601EF2C) + (state->R[1]>>7 & -8)); + const int base_r = base_ptr[0]; + const int base_g = base_ptr[1]; + const int base_b = base_ptr[2]; + int a, b, c, d; + + switch (quad_type) { + case 1: + a = b = c = d = Azel_0601ED58(quad_data, base_r, base_g, base_b); + state->R[4] += 8; + break; + + case 2: + a = Azel_0601EDDC(quad_data+ 0, base_r, base_g, base_b); + b = Azel_0601EDDC(quad_data+ 6, base_r, base_g, base_b); + c = Azel_0601EDDC(quad_data+12, base_r, base_g, base_b); + d = Azel_0601EDDC(quad_data+18, base_r, base_g, base_b); + state->R[4] += 48; + break; + + default: // case 3 + a = Azel_0601ED58(quad_data+0, base_r, base_g, base_b); + b = Azel_0601ED58(quad_data+3, base_r, base_g, base_b); + c = Azel_0601ED58(quad_data+6, base_r, base_g, base_b); + d = Azel_0601ED58(quad_data+9, base_r, base_g, base_b); + state->R[4] += 24; + break; + } // switch (quad_type) + + out[0 ^ swapflag] = VDP_SWAPW(a); + out[1 ^ swapflag] = VDP_SWAPW(b); + out[2 ^ swapflag] = VDP_SWAPW(c); + out[3 ^ swapflag] = VDP_SWAPW(d); +} + +static int Azel_0601ED58(const int16_t *vertex_data, + int base_r, int base_g, int base_b) +{ + return Azel_color_calc((const int16_t *)HRAM_PTR(0x601EF10), + vertex_data, + (const int16_t *)HRAM_PTR(0x601EF24), + base_r, base_g, base_b); +} + +static int Azel_0601EDDC(const int16_t *vertex_data, + int base_r, int base_g, int base_b) +{ + return Azel_color_calc((const int16_t *)HRAM_PTR(0x601EF10), + vertex_data, + (const int16_t *)HRAM_PTR(0x601EF24), + base_r + vertex_data[5], + base_g + vertex_data[4], + base_b + vertex_data[3]); +} + +/*----------------------------------*/ + +static void Azel_0601F2D6_common(SH2State *state, + int (*clipfunc)(SH2State *state, uint32_t r11, uint32_t r13)) +{ + const uint32_t saved_PR = state->PR; + + uint32_t r11 = LRAM_LOADL(state->R[4], 0); + uint32_t r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + while (r11 != r13) { + const int swapflag = (*clipfunc)(state, r11, r13); + const int clipped = (swapflag < 0); + if (clipped) { + state->cycles += 12; + } else { + Azel_0601F49C(state, swapflag); + state->cycles += 13; + } + r11 = LRAM_LOADL(state->R[4], 0); + r13 = LRAM_LOADL(state->R[4], 4); + state->R[4] += 8; + } + + state->PC = saved_PR; + state->cycles += 11; +} + +static void Azel_0601F49C(SH2State *state, int swapflag) +{ + static const uint16_t cycles_used[4][4] = + {{10,10,10,10}, {98,98,98,98}, {285,291,293,291}, {266,272,274,272}}; + + const int16_t *quad_data = (const int16_t *)LRAM_PTR(state->R[4]); + const int quad_type = (quad_data[-6] >> 8) & 3; + uint32_t *gbr_ptr = (uint32_t *)HRAM_PTR(state->GBR); + + state->cycles += cycles_used[quad_type][swapflag]; + + gbr_ptr[0] = RAM_SWAPL(state->R[14] + 32); + + if (quad_type == 0) { + return; + } + + const uint32_t color_addr = RAM_SWAPL(gbr_ptr[4]); + int16_t *out = (int16_t *)VDP1_PTR(color_addr); + gbr_ptr[4] = RAM_SWAPL(color_addr + 8); + VDP1_STOREW(state->R[14], 28, color_addr >> 3); + + const int16_t *base_ptr = + (const int16_t *)(HRAM_PTR(0x601FCB0) + (state->R[1]>>7 & -8)); + const int base_r = base_ptr[0]; + const int base_g = base_ptr[1]; + const int base_b = base_ptr[2]; + int a, b, c, d; + + switch (quad_type) { + case 1: + a = b = c = d = Azel_0601FA68(quad_data, base_r, base_g, base_b); + state->R[4] += 8; + break; + + case 2: + a = Azel_0601FAEC(quad_data+ 0, base_r, base_g, base_b); + b = Azel_0601FAEC(quad_data+ 6, base_r, base_g, base_b); + c = Azel_0601FAEC(quad_data+12, base_r, base_g, base_b); + d = Azel_0601FAEC(quad_data+18, base_r, base_g, base_b); + state->R[4] += 48; + break; + + default: // case 3 + a = Azel_0601FA68(quad_data+0, base_r, base_g, base_b); + b = Azel_0601FA68(quad_data+3, base_r, base_g, base_b); + c = Azel_0601FA68(quad_data+6, base_r, base_g, base_b); + d = Azel_0601FA68(quad_data+9, base_r, base_g, base_b); + state->R[4] += 24; + break; + } // switch (quad_type) + + out[0 ^ swapflag] = VDP_SWAPW(a); + out[1 ^ swapflag] = VDP_SWAPW(b); + out[2 ^ swapflag] = VDP_SWAPW(c); + out[3 ^ swapflag] = VDP_SWAPW(d); +} + +static int Azel_0601FA68(const int16_t *vertex_data, + int base_r, int base_g, int base_b) +{ + return Azel_color_calc((const int16_t *)HRAM_PTR(0x601FC94), + vertex_data, + (const int16_t *)HRAM_PTR(0x601FCA8), + base_r, base_g, base_b); +} + +static int Azel_0601FAEC(const int16_t *vertex_data, + int base_r, int base_g, int base_b) +{ + return Azel_color_calc((const int16_t *)HRAM_PTR(0x601FC94), + vertex_data, + (const int16_t *)HRAM_PTR(0x601FCA8), + base_r + vertex_data[5], + base_g + vertex_data[4], + base_b + vertex_data[3]); +} + +/*----------------------------------*/ + +static int Azel_0601F58A(SH2State *state, uint32_t r11, uint32_t r13) +{ + r11 <<= 3; + r13 <<= 3; + const uint32_t r7 = state->R[7]; + const int16_t *r10_ptr = (const int16_t *)HRAM_PTR(r7 + (r11 >> 16)); + const int16_t *r11_ptr = (const int16_t *)HRAM_PTR(r7 + (r11 & 0xFFFF)); + const int16_t *r12_ptr = (const int16_t *)HRAM_PTR(r7 + (r13 >> 16)); + const int16_t *r13_ptr = (const int16_t *)HRAM_PTR(r7 + (r13 & 0xFFFF)); + return Azel_0601F5A6(state, r10_ptr, r11_ptr, r12_ptr, r13_ptr); +} + +static int Azel_0601F5A6(SH2State *state, + const int16_t *r10_ptr, const int16_t *r11_ptr, + const int16_t *r12_ptr, const int16_t *r13_ptr) +{ + const int32_t a = (r13_ptr[1] - r11_ptr[1]) * (r12_ptr[0] - r10_ptr[0]); + const int32_t b = (r12_ptr[1] - r10_ptr[1]) * (r13_ptr[0] - r11_ptr[0]); + if (b > a) { + state->cycles += 35; + return Azel_0601F5D2(state); + } + + const int swapflag = 0; + + state->R[14] = HRAM_LOADL(state->GBR, 0); + uint32_t *r14_ptr = (uint32_t *)VDP1_PTR(state->R[14]); + + r14_ptr[3] = RAM_VDP_SWAPL(*(const uint32_t *)r10_ptr); + r14_ptr[4] = RAM_VDP_SWAPL(*(const uint32_t *)r11_ptr); + r14_ptr[5] = RAM_VDP_SWAPL(*(const uint32_t *)r12_ptr); + r14_ptr[6] = RAM_VDP_SWAPL(*(const uint32_t *)r13_ptr); + + const uint32_t r4_0 = LRAM_LOADL(state->R[4], 0); + + state->cycles += 49; + return Azel_0601F762(state, + (const uint32_t *)r10_ptr, + (const uint32_t *)r11_ptr, + (const uint32_t *)r12_ptr, + (const uint32_t *)r13_ptr, + r14_ptr, r4_0, swapflag); +} + +static int Azel_0601F5D2(SH2State *state) +{ + static const uint8_t r4ofs_cycles[8] = { + 12, 20, 60, 36, // R4 offset (polygon data size) + 10, 12, 15, 15, // Cycle count + }; + const unsigned int quad_type = (LRAM_LOADW(state->R[4], 0) >> 8) & 0x3; + state->R[4] += r4ofs_cycles[quad_type]; + state->cycles += r4ofs_cycles[quad_type+4]; + return -1; +} + +/*----------------------------------*/ + +static int Azel_0601F5EE(SH2State *state, uint32_t r11, uint32_t r13) +{ + uint32_t cycles = state->cycles; + + r11 <<= 5; + r13 <<= 5; + const uint32_t r7 = state->R[7]; + uint32_t *r10_ptr = (uint32_t *)HRAM_PTR(r7 + (r11 >> 16)); + uint32_t *r11_ptr = (uint32_t *)HRAM_PTR(r7 + (r11 & 0xFFFF)); + uint32_t *r12_ptr = (uint32_t *)HRAM_PTR(r7 + (r13 >> 16)); + uint32_t *r13_ptr = (uint32_t *)HRAM_PTR(r7 + (r13 & 0xFFFF)); + + if (r10_ptr[6] & r11_ptr[6] & r12_ptr[6] & r13_ptr[6]) { + state->cycles = cycles + 27; + return Azel_0601F5D2(state); + } + if ((r10_ptr[6] | r11_ptr[6] | r12_ptr[6] | r13_ptr[6]) & RAM_SWAPL(0x20)) { + state->cycles = cycles + 33; + return Azel_0601F5D2(state); + } + if (!(r10_ptr[6] | r11_ptr[6] | r12_ptr[6] | r13_ptr[6])) { + state->cycles = cycles + 35 - 14; + return Azel_0601F5A6(state, + (const int16_t *)r10_ptr, + (const int16_t *)r11_ptr, + (const int16_t *)r12_ptr, + (const int16_t *)r13_ptr); + } + + r10_ptr[7] = r10_ptr[6]; + r11_ptr[7] = r11_ptr[6]; + r12_ptr[7] = r12_ptr[6]; + r13_ptr[7] = r13_ptr[6]; + r10_ptr[4] = RAM_SWAPL((int32_t)((int16_t *)r10_ptr)[0]); + r10_ptr[5] = RAM_SWAPL((int32_t)((int16_t *)r10_ptr)[1]); + r11_ptr[4] = RAM_SWAPL((int32_t)((int16_t *)r11_ptr)[0]); + r11_ptr[5] = RAM_SWAPL((int32_t)((int16_t *)r11_ptr)[1]); + r12_ptr[4] = RAM_SWAPL((int32_t)((int16_t *)r12_ptr)[0]); + r12_ptr[5] = RAM_SWAPL((int32_t)((int16_t *)r12_ptr)[1]); + r13_ptr[4] = RAM_SWAPL((int32_t)((int16_t *)r13_ptr)[0]); + r13_ptr[5] = RAM_SWAPL((int32_t)((int16_t *)r13_ptr)[1]); + + const int16_t * const r6_ptr = (const int16_t *)HRAM_PTR(state->R[6]); + int32_t result = Azel_0601F824(r6_ptr, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + 0x10, Azel_0601F9D6); + cycles += result & 0xFFFF; + if (result < 0) { + state->cycles = cycles + 61; + return Azel_0601F5D2(state); + } + + const int32_t r10_x = RAM_SWAPL(r10_ptr[4]); + const int32_t r11_x = RAM_SWAPL(r11_ptr[4]); + const int32_t r12_x = RAM_SWAPL(r12_ptr[4]); + const int32_t r13_x = RAM_SWAPL(r13_ptr[4]); + const int32_t r10_y = RAM_SWAPL(r10_ptr[5]); + const int32_t r11_y = RAM_SWAPL(r11_ptr[5]); + const int32_t r12_y = RAM_SWAPL(r12_ptr[5]); + const int32_t r13_y = RAM_SWAPL(r13_ptr[5]); + int32_t r0 = (r13_y - r11_y) * (r12_x - r10_x); + int32_t r2 = (r12_y - r10_y) * (r13_x - r11_x); + if (r2 > r0) { + state->cycles = cycles + 79; + return Azel_0601F5D2(state); + } + + result = Azel_0601F824(r6_ptr, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + 0x1, Azel_0601F948); + cycles += result & 0xFFFF; + if (result < 0) { + state->cycles = cycles + 84; + return Azel_0601F5D2(state); + } + result = Azel_0601F824(r6_ptr, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + 0x2, Azel_0601F93A); + cycles += result & 0xFFFF; + if (result < 0) { + state->cycles = cycles + 89; + return Azel_0601F5D2(state); + } + result = Azel_0601F824(r6_ptr, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + 0x4, Azel_0601F996); + cycles += result & 0xFFFF; + if (result < 0) { + state->cycles = cycles + 94; + return Azel_0601F5D2(state); + } + result = Azel_0601F824(r6_ptr, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + 0x8, Azel_0601F988); + cycles += result & 0xFFFF; + if (result < 0) { + state->cycles = cycles + 99; + return Azel_0601F5D2(state); + } + + int swapflag; + uint32_t r4_0 = LRAM_LOADL(state->R[4], 0); + if (r10_ptr[7]) { + if (r11_ptr[7]) { + if (r12_ptr[7]) { + if (r13_ptr[7]) { + swapflag = 0; + cycles += 116; + } else { + uint32_t *temp1, *temp2; + temp1 = r10_ptr; temp2 = r13_ptr; + r10_ptr = temp2; r13_ptr = temp1; + temp1 = r11_ptr; temp2 = r12_ptr; + r11_ptr = temp2; r12_ptr = temp1; + r4_0 ^= 0x20; + swapflag = 3; + cycles += 124; + } + } else { + uint32_t *temp1, *temp2; + temp1 = r10_ptr; temp2 = r12_ptr; + r10_ptr = temp2; r12_ptr = temp1; + temp1 = r11_ptr; temp2 = r13_ptr; + r11_ptr = temp2; r13_ptr = temp1; + r4_0 ^= 0x30; + swapflag = 2; + cycles += 123; + } + } else { + uint32_t *temp1, *temp2; + temp1 = r10_ptr; temp2 = r11_ptr; + r10_ptr = temp2; r11_ptr = temp1; + temp1 = r12_ptr; temp2 = r13_ptr; + r12_ptr = temp2; r13_ptr = temp1; + r4_0 ^= 0x10; + swapflag = 1; + cycles += 120; + } + } else { + swapflag = 0; + cycles += 105; + } + + state->R[14] = HRAM_LOADL(state->GBR, 0); + uint32_t *r14_ptr = (uint32_t *)VDP1_PTR(state->R[14]); + + const uint16_t *r10_ptr16 = (const uint16_t *)r10_ptr; + const uint16_t *r11_ptr16 = (const uint16_t *)r11_ptr; + const uint16_t *r12_ptr16 = (const uint16_t *)r12_ptr; + const uint16_t *r13_ptr16 = (const uint16_t *)r13_ptr; + r14_ptr[3] = VDP_SWAPL(r10_ptr16[9]<<16 | r10_ptr16[11]); + r14_ptr[4] = VDP_SWAPL(r11_ptr16[9]<<16 | r11_ptr16[11]); + r14_ptr[5] = VDP_SWAPL(r12_ptr16[9]<<16 | r12_ptr16[11]); + r14_ptr[6] = VDP_SWAPL(r13_ptr16[9]<<16 | r13_ptr16[11]); + + state->cycles = cycles + 24; + return Azel_0601F762(state, r10_ptr, r11_ptr, r12_ptr, r13_ptr, + r14_ptr, r4_0, swapflag); +} + +/*----------------------------------*/ + +static ALWAYS_INLINE int Azel_0601F762( + SH2State *state, const uint32_t * const r10_ptr, + const uint32_t * const r11_ptr, const uint32_t * const r12_ptr, + const uint32_t * const r13_ptr, uint32_t * const r14_ptr, + const uint32_t r4_0, const int swapflag) +{ + const uint32_t r4_1 = LRAM_LOADL(state->R[4], 4); + const uint32_t r4_2 = LRAM_LOADL(state->R[4], 8); + state->R[4] += 12; + + *(uint16_t *)&r14_ptr[0] = VDP_SWAPW(r4_0 | 0x1000); + r14_ptr[1] = VDP_SWAPL(r4_1); + r14_ptr[2] = VDP_SWAPL(r4_2); + + int32_t r1 = RAM_SWAPL(r10_ptr[1]); + int32_t r2 = RAM_SWAPL(r11_ptr[1]); + int32_t r3 = RAM_SWAPL(r12_ptr[1]); + int32_t r5 = RAM_SWAPL(r13_ptr[1]); + const uint32_t out_addr = HRAM_LOADL(state->GBR, 32); + uint16_t *out_ptr = (uint16_t *)HRAM_PTR(out_addr); + const int32_t mult = HRAM_LOADL(state->R[6], 52); + + switch (r4_0 >> 28) { + case 0: + r1 = MAX(MAX(r1, r2), MAX(r3, r5)); + state->cycles += 21; + l_601F7F4: + r1 = ((int64_t)r1 * (int64_t)mult) >> 32; + if (r1 <= 0) { + r1 = 1; + } + out_ptr[2] = r1; + out_ptr[3] = (uint16_t)(state->R[14] >> 3); + state->cycles += 15; + break; + + default: // case 1 + r1 = MIN(MIN(r1, r2), MIN(r3, r5)); + state->cycles += 21; + goto l_601F7F4; + + case 2: + r2 -= r1; + r3 -= r1; + r5 -= r1; + r2 += r3 + r5; + r1 += r2 >> 2; + state->cycles += 18; + goto l_601F7F4; + + case 3: + r2 -= r1; + r3 -= r1; + r5 -= r1; + r2 += r3 + r5; + r1 += r2 >> 2; + r1 = ((int64_t)r1 * (int64_t)mult) >> 32; + if (r1 <= 0) { + r1 = 1; + } + out_ptr[2] = 0x7FFF; + out_ptr[3] = (uint16_t)(state->R[14] >> 3); + state->cycles += 25; + break; + } // switch (r4_0 >> 28) + + /* 0x601F810 */ + + HRAM_STOREL(state->GBR, 32, out_addr + 8); + HRAM_STOREL(state->GBR, 12, HRAM_LOADL(state->GBR, 12) + 1); + HRAM_STOREL(state->GBR, 28, HRAM_LOADL(state->GBR, 28) + 1); + state->R[1] = r1; + state->cycles += 8 + 11; + return swapflag; +} + +/*----------------------------------*/ + +static int32_t Azel_0601F824(const int16_t *r6_ptr, uint32_t *r10_ptr, + uint32_t *r11_ptr, uint32_t *r12_ptr, + uint32_t *r13_ptr, uint32_t mask, + int (*clipfunc)(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr)) +{ + int32_t cycles = 0; + + /* We save a bit of time by byte-swapping just this value and loading + * the values to check directly from native memory. */ + mask = RAM_SWAPL(mask); + const uint32_t r10_check = r10_ptr[7] & mask; + const uint32_t r11_check = r11_ptr[7] & mask; + const uint32_t r12_check = r12_ptr[7] & mask; + const uint32_t r13_check = r13_ptr[7] & mask; + + if (r13_check) { + if (r12_check) { + cycles += 10; + if (r11_check) { + if (r10_check) { + cycles += 20; + return 0x80000000 | cycles; + } else { + cycles += (*clipfunc)(r11_ptr, r10_ptr, r6_ptr); + cycles += (*clipfunc)(r12_ptr, r10_ptr, r6_ptr); + cycles += (*clipfunc)(r13_ptr, r10_ptr, r6_ptr); + cycles += 27; + return cycles; + } + } else if (r10_check) { + cycles += (*clipfunc)(r10_ptr, r11_ptr, r6_ptr); + cycles += (*clipfunc)(r13_ptr, r11_ptr, r6_ptr); + cycles += (*clipfunc)(r12_ptr, r11_ptr, r6_ptr); + cycles += 28; + return cycles; + } else { + cycles += (*clipfunc)(r13_ptr, r10_ptr, r6_ptr); + cycles += (*clipfunc)(r12_ptr, r11_ptr, r6_ptr); + cycles += 24; + return cycles; + } + } else if (r11_check) { + if (r10_check) { + cycles += (*clipfunc)(r10_ptr, r12_ptr, r6_ptr); + cycles += (*clipfunc)(r13_ptr, r12_ptr, r6_ptr); + cycles += (*clipfunc)(r11_ptr, r12_ptr, r6_ptr); + cycles += 28; + return cycles; + } else { + cycles += 17; + return 0x80000000 | cycles; + } + } else if (r10_check) { + cycles += (*clipfunc)(r10_ptr, r11_ptr, r6_ptr); + cycles += (*clipfunc)(r13_ptr, r12_ptr, r6_ptr); + cycles += 25; + return cycles; + } else { + const int32_t r1 = RAM_SWAPL(r12_ptr[1]); + const int32_t r2 = RAM_SWAPL(r10_ptr[1]); + cycles += (r1 > r2) ? 23 : 21; + cycles += (*clipfunc)(r13_ptr, (r1 > r2) ? r12_ptr : r10_ptr, + r6_ptr); + return cycles; + } + + } else if (r12_check) { + if (r11_check) { + if (r10_check) { + cycles += (*clipfunc)(r11_ptr, r13_ptr, r6_ptr); + cycles += (*clipfunc)(r12_ptr, r13_ptr, r6_ptr); + cycles += (*clipfunc)(r10_ptr, r13_ptr, r6_ptr); + cycles += 28; + return cycles; + } else { + cycles += (*clipfunc)(r11_ptr, r10_ptr, r6_ptr); + cycles += (*clipfunc)(r12_ptr, r13_ptr, r6_ptr); + cycles += 24; + return cycles; + } + } else if (r10_check) { + cycles += 18; + return 0x80000000 | cycles; + } else { + const int32_t r1 = RAM_SWAPL(r13_ptr[1]); + const int32_t r2 = RAM_SWAPL(r11_ptr[1]); + cycles += (r1 > r2) ? 23 : 21; + cycles += (*clipfunc)(r12_ptr, (r1 > r2) ? r13_ptr : r11_ptr, + r6_ptr); + return cycles; + } + + } else if (r11_check) { + if (r10_check) { + cycles += (*clipfunc)(r10_ptr, r13_ptr, r6_ptr); + cycles += (*clipfunc)(r11_ptr, r12_ptr, r6_ptr); + cycles += 25; + return cycles; + } else { + const int32_t r1 = RAM_SWAPL(r10_ptr[1]); + const int32_t r2 = RAM_SWAPL(r12_ptr[1]); + cycles += (r1 > r2) ? 23 : 21; + cycles += (*clipfunc)(r11_ptr, (r1 > r2) ? r10_ptr : r12_ptr, + r6_ptr); + return cycles; + } + + } else if (r10_check) { + const int32_t r1 = RAM_SWAPL(r11_ptr[1]); + const int32_t r2 = RAM_SWAPL(r13_ptr[1]); + cycles += (r1 > r2) ? 24 : 22; + cycles += (*clipfunc)(r10_ptr, (r1 > r2) ? r11_ptr : r13_ptr, r6_ptr); + return cycles; + + } else { + cycles += 15; + return cycles; + } +} + +/*----------------------------------*/ + +static int Azel_0601F93A(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr) +{ + const int32_t r8_x = RAM_SWAPL(r8_ptr[4]); + const int32_t x_max = r6_ptr[3]; + if (r8_x <= x_max) { + return 8; + } + return Azel_0601F950(r8_ptr, r9_ptr, r6_ptr, r8_x, x_max, 6); +} + +static int Azel_0601F948(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr) +{ + const int32_t r8_x = RAM_SWAPL(r8_ptr[4]); + const int32_t x_min = r6_ptr[2]; + if (r8_x >= x_min) { + return 10; + } + return Azel_0601F950(r8_ptr, r9_ptr, r6_ptr, r8_x, x_min, 4); +} + +static ALWAYS_INLINE int Azel_0601F950(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr, + const int32_t r8_x, const int32_t x_lim, + int base_cycles) +{ + r8_ptr[4] = RAM_SWAPL(x_lim); + const int32_t dx_lim = x_lim - r8_x; + const int32_t dx_r9 = RAM_SWAPL(r9_ptr[4]) - r8_x; + int32_t frac8; + if (UNLIKELY(dx_r9 == 0)) { + frac8 = (dx_lim >= 0) ? 0xFF : 0; + } else { + frac8 = bound((dx_lim << 8) / dx_r9, 0, 255); + } + const int32_t r8_y = RAM_SWAPL(r8_ptr[5]); + const int32_t r9_y = RAM_SWAPL(r9_ptr[5]); + const int32_t new_r8_y = (int16_t)(r8_y + (((r9_y - r8_y) * frac8) >> 8)); + r8_ptr[5] = RAM_SWAPL(new_r8_y); + + return 29 + base_cycles; +} + +/*----------------------------------*/ + +static int Azel_0601F988(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr) +{ + const int32_t r8_y = RAM_SWAPL(r8_ptr[5]); + const int32_t y_max = r6_ptr[0]; + if (r8_y <= y_max) { + return 8; + } + return Azel_0601F99E(r8_ptr, r9_ptr, r6_ptr, r8_y, y_max, 6); +} + +static int Azel_0601F996(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr) +{ + const int32_t r8_y = RAM_SWAPL(r8_ptr[5]); + const int32_t y_min = r6_ptr[1]; + if (r8_y >= y_min) { + return 10; + } + return Azel_0601F99E(r8_ptr, r9_ptr, r6_ptr, r8_y, y_min, 4); +} + +static ALWAYS_INLINE int Azel_0601F99E(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr, + const int32_t r8_y, const int32_t y_lim, + int base_cycles) +{ + r8_ptr[5] = RAM_SWAPL(y_lim); + const int32_t dy_lim = y_lim - r8_y; + const int32_t dy_r9 = RAM_SWAPL(r9_ptr[5]) - r8_y; + int32_t frac8; + if (UNLIKELY(dy_r9 == 0)) { + frac8 = (dy_lim >= 0) ? 0xFF : 0; + } else { + frac8 = bound((dy_lim << 8) / dy_r9, 0, 255); + } + const int32_t r8_x = RAM_SWAPL(r8_ptr[4]); + const int32_t r9_x = RAM_SWAPL(r9_ptr[4]); + const int32_t new_r8_x = (int16_t)(r8_x + (((r9_x - r8_x) * frac8) >> 8)); + r8_ptr[4] = RAM_SWAPL(new_r8_x); + + return 29 + base_cycles; +} + +/*----------------------------------*/ + +static int Azel_0601F9D6(uint32_t *r8_ptr, uint32_t *r9_ptr, + const int16_t *r6_ptr) +{ + const int32_t *r6_ptr32 = (const int32_t *)r6_ptr; + int32_t r3 = RAM_SWAPL(r8_ptr[1]); + int32_t r0 = (RAM_SWAPL(r6_ptr32[4]) << 8) - r3; + int32_t r2 = RAM_SWAPL(r9_ptr[1]) - r3; + const float frac = (float)r0 / (float)r2; + r2 = RAM_SWAPL(r9_ptr[2]); + r0 = RAM_SWAPL(r8_ptr[2]); + r2 = r0 + (int32_t)((r2 - r0) * frac); + r3 = RAM_SWAPL(r9_ptr[3]); + r0 = RAM_SWAPL(r8_ptr[3]); + r3 = r0 + (int32_t)((r3 - r0) * frac); + const int32_t mult = RAM_SWAPL(r6_ptr32[12]); + r2 = ((int64_t)r2 * (int64_t)mult) >> 32; + r3 = ((int64_t)r3 * (int64_t)mult) >> 32; + r8_ptr[4] = RAM_SWAPL(r2); + r8_ptr[5] = RAM_SWAPL(r3); + + const uint32_t r1 = (r3 > r6_ptr[4]) << 3 + | (r3 < r6_ptr[5]) << 2 + | (r2 > r6_ptr[7]) << 1 + | (r2 < r6_ptr[6]) << 0; + r8_ptr[7] = RAM_SWAPL(r1); + + return 73; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601EE60: Coordinate transformation routine whose second instruction is + * modified from 0x601F1{20,40}; we detect the change here and hint + * 0x601EE62 as a data pointer to avoid having to repeatedly translate the + * block. Incidentally, 0x601F1{20,40} are themselves called from (among + * other places) 0x601F1{50,5E,6C}, which _push and pop_ the _instruction + * word_. Good grief... */ + +static FASTCALL void Azel_0601EE60(SH2State *state) +{ + const uint16_t insn = HRAM_LOADW(0x601EE62, 0); + if (insn>>12 == 0xA) { // BRA instruction + state->R[0] = 0x601EF10; + state->MACL = state->MACH = 0; + state->PC += 6 + ((int32_t)(insn<<20) >> 19); + state->cycles += 4; + } else { + if (insn != 0x6103) { + DMSG("WARNING: Wrong instruction at 0x601EE62 (%04X)", insn); + } + const int32_t *M_ptr = + state->R[5] & 0x6000000 ? (int32_t *)HRAM_PTR(state->R[5]) + : (int32_t *)LRAM_PTR(state->R[5]); + const int32_t in_x = HRAM_LOADL(0x601EF18, 0); + const int32_t in_y = HRAM_LOADL(0x601EF18, 4); + const int32_t in_z = HRAM_LOADL(0x601EF18, 8); + int16_t out_x = (RAM_SWAPL(M_ptr[ 0]) * in_x + + RAM_SWAPL(M_ptr[ 4]) * in_y + + RAM_SWAPL(M_ptr[ 8]) * in_z) >> 16; + int16_t out_y = (RAM_SWAPL(M_ptr[ 1]) * in_x + + RAM_SWAPL(M_ptr[ 5]) * in_y + + RAM_SWAPL(M_ptr[ 9]) * in_z) >> 16; + int16_t out_z = (RAM_SWAPL(M_ptr[ 2]) * in_x + + RAM_SWAPL(M_ptr[ 6]) * in_y + + RAM_SWAPL(M_ptr[10]) * in_z) >> 16; + HRAM_STOREW(0x601EF10, 0, out_x); + HRAM_STOREW(0x601EF10, 2, out_y); + HRAM_STOREW(0x601EF10, 4, out_z); + state->PC = state->PR; + state->cycles += 58; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601EEE8: Routine that writes a local data array in a loop; optimized + * to prevent JIT overwrite checks from flooding the blacklist array. An + * identical routine is located at 0x601FC6C; this code is used for both. */ + +static FASTCALL void Azel_0601EEE8(SH2State *state) +{ + const int r = (state->R[4]>> 0 & 0xFF) << 8; + const int g = (state->R[4]>> 8 & 0xFF) << 8; + const int b = (state->R[4]>>16 & 0xFF) << 8; + int16_t *out = (int16_t *)HRAM_PTR(((state->PC + 8*2) & -4) + 4 + 0xC*4); + int16_t *top = out + 32*4; + for (; out != top; out += 4) { + out[0] = r; + out[1] = g; + out[2] = b; + } + state->PC = state->PR; + state->cycles += 14 + 32*7; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601F240, 0x601F24C: Calculation function */ + +static ALWAYS_INLINE void Azel_0601F24E(SH2State *state, int32_t r2); + +/*----------------------------------*/ + +static FASTCALL void Azel_0601F240(SH2State *state) +{ + const int32_t r2 = LRAM_LOADL(state->R[4], 0); + state->cycles += 8; + return Azel_0601F24E(state, + ((int64_t)r2 * (int64_t)(int32_t)state->R[8]) >> 16); +} + +static FASTCALL void Azel_0601F24C(SH2State *state) +{ + const int32_t r2 = LRAM_LOADL(state->R[4], 0); + return Azel_0601F24E(state, r2); +} + +static ALWAYS_INLINE void Azel_0601F24E(SH2State *state, int32_t r2) +{ + uint32_t r0; + int32_t r8, r9; + int32_t r1 = HRAM_LOADL(state->R[5], 44); + int32_t r3 = HRAM_LOADL(state->R[6], 20); + if (r1 - r2 >= r3) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 12; + return; + } + int32_t r10 = HRAM_LOADL(state->R[6], 16); + if (r1 + r2 < r10) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 17; + return; + } + r0 = (r1 + r2 >= r3) << 5 + | (r1 - r2 < r10) << 4; + +#if defined(__mips__) + int32_t temp; + asm(".set push; .set noreorder\n" + "lw %[r8], 36(%[r6])\n" + "lw %[r9], 32(%[r6])\n" + "lw %[r3], 28(%[r5])\n" + "ror %[r8], %[r8], 16\n" + "mult %[r8], %[r1]\n" + "ror %[r9], %[r9], 16\n" + "ror %[r3], %[r3], 16\n" + "mflo %[r8]\n" + "mfhi %[temp]\n" + "mult %[r9], %[r2]\n" + "srl %[r8], %[r8], 16\n" + "ins %[r8], %[temp], 16, 16\n" + "mflo %[r9]\n" + "mfhi %[temp]\n" + "srl %[r9], %[r9], 16\n" + "ins %[r9], %[temp], 16, 16\n" + ".set pop" + : [r3] "=&r" (r3), [r8] "=&r" (r8), [r9] "=&r" (r9), + [temp] "=&r" (temp) + : [r5] "r" (HRAM_PTR(state->R[5])), [r6] "r" (HRAM_PTR(state->R[6])), + [r1] "r" (r1), [r2] "r" (r2) + ); +#else + r3 = HRAM_LOADL(state->R[5], 28); + r8 = HRAM_LOADL(state->R[6], 36); + r9 = HRAM_LOADL(state->R[6], 32); + r8 = ((int64_t)r8 * (int64_t)r1) >> 16; + r9 = ((int64_t)r9 * (int64_t)r2) >> 16; +#endif + if (r8 + r9 < r3) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 41; + return; + } + if (r8 + r9 < -r3) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 44; + return; + } + r0 |= (r8 - r9 < r3) << 3 + | (r8 - r9 < -r3) << 2; + +#if defined(__mips__) + asm(".set push; .set noreorder\n" + "lw %[r8], 44(%[r6])\n" + "lw %[r9], 40(%[r6])\n" + "lw %[r3], 12(%[r5])\n" + "ror %[r8], %[r8], 16\n" + "mult %[r8], %[r1]\n" + "ror %[r9], %[r9], 16\n" + "ror %[r3], %[r3], 16\n" + "mflo %[r8]\n" + "mfhi %[temp]\n" + "mult %[r9], %[r2]\n" + "srl %[r8], %[r8], 16\n" + "ins %[r8], %[temp], 16, 16\n" + "mflo %[r9]\n" + "mfhi %[temp]\n" + "srl %[r9], %[r9], 16\n" + "ins %[r9], %[temp], 16, 16\n" + ".set pop" + : [r3] "=&r" (r3), [r8] "=&r" (r8), [r9] "=&r" (r9), + [temp] "=&r" (temp) + : [r5] "r" (HRAM_PTR(state->R[5])), [r6] "r" (HRAM_PTR(state->R[6])), + [r1] "r" (r1), [r2] "r" (r2) + ); +#else + r3 = HRAM_LOADL(state->R[5], 12); + r8 = HRAM_LOADL(state->R[6], 44); + r9 = HRAM_LOADL(state->R[6], 40); + r8 = ((int64_t)r8 * (int64_t)r1) >> 16; + r9 = ((int64_t)r9 * (int64_t)r2) >> 16; +#endif + if (r8 + r9 < r3) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 69; + return; + } + if (r8 + r9 < -r3) { + state->SR |= SR_T; + state->PC = state->PR; + state->cycles += 72; + return; + } + r0 |= (r8 - r9 < r3) << 1 + | (r8 - r9 < -r3) << 0; + + state->R[0] = r0; + state->SR &= ~SR_T; + state->PC = state->PR; + state->cycles += 76; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601F30E: Coordinate transform routine */ + +static FASTCALL void Azel_0601F30E(SH2State *state) +{ + const int32_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int32_t *)HRAM_PTR(state->R[4]) + : (const int32_t *)LRAM_PTR(state->R[4]); + const int32_t *r5_ptr = (const int32_t *)HRAM_PTR(state->R[5]); + const int32_t *r6_ptr = (const int32_t *)HRAM_PTR(state->R[6]); + + /* 0x601F38E */ + + const int32_t r6_x = RAM_SWAPL(r6_ptr[6]); + const int32_t M11 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[0])) >> 12; + const int32_t M12 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[1])) >> 12; + const int32_t M13 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[2])) >> 12; + const int32_t M14 = r6_x * (int32_t)RAM_SWAPL(r5_ptr[3]); + + const int32_t r6_y = -RAM_SWAPL(r6_ptr[7]); + const int32_t M21 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[4])) >> 12; + const int32_t M22 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[5])) >> 12; + const int32_t M23 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[6])) >> 12; + const int32_t M24 = r6_y * (int32_t)RAM_SWAPL(r5_ptr[7]); + + const int32_t M31 = (int32_t)RAM_SWAPL(r5_ptr[ 8]) >> 4; + const int32_t M32 = (int32_t)RAM_SWAPL(r5_ptr[ 9]) >> 4; + const int32_t M33 = (int32_t)RAM_SWAPL(r5_ptr[10]) >> 4; + const int32_t M34 = (int32_t)RAM_SWAPL(r5_ptr[11]) << 8; + + uint32_t counter = RAM_SWAPL(r4_ptr[1]); + const uint32_t in_address = RAM_SWAPL(r4_ptr[2]); + + state->cycles += 111; + + /* 0x601F314 */ + + state->cycles += 8 + 56*counter; + + const int16_t *in = in_address & 0x06000000 + ? (const int16_t *)HRAM_PTR(in_address) + : (const int16_t *)LRAM_PTR(in_address); + int16_t *out = (int16_t *)HRAM_PTR(state->R[7]); + + do { + const int32_t out_x = M11*in[0] + M12*in[1] + M13*in[2] + M14; + const int32_t out_y = M21*in[0] + M22*in[1] + M23*in[2] + M24; + const int32_t out_z = M31*in[0] + M32*in[1] + M33*in[2] + M34; + const float coord_mult = 256.0f / out_z; + out[0] = ifloorf(coord_mult * out_x); + out[1] = ifloorf(coord_mult * out_y); + *(int32_t *)&out[2] = RAM_SWAPL(out_z); + in += 3; + out += 4; + } while (--counter != 0); + + state->R[4] += 12; + state->PC = state->PR; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601F3F4: Coordinate transform routine with boundary checks */ + +static FASTCALL void Azel_0601F3F4(SH2State *state) +{ + const int32_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int32_t *)HRAM_PTR(state->R[4]) + : (const int32_t *)LRAM_PTR(state->R[4]); + const int32_t *r5_ptr = (const int32_t *)HRAM_PTR(state->R[5]); + const int32_t *r6_ptr = (const int32_t *)HRAM_PTR(state->R[6]); + + /* 0x601F38E */ + + const int32_t r6_x = RAM_SWAPL(r6_ptr[6]); + const int32_t M11 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[0])) >> 12; + const int32_t M12 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[1])) >> 12; + const int32_t M13 = (r6_x * (int32_t)RAM_SWAPL(r5_ptr[2])) >> 12; + const int32_t M14 = r6_x * (int32_t)RAM_SWAPL(r5_ptr[3]); + + const int32_t r6_y = -RAM_SWAPL(r6_ptr[7]); + const int32_t M21 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[4])) >> 12; + const int32_t M22 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[5])) >> 12; + const int32_t M23 = (r6_y * (int32_t)RAM_SWAPL(r5_ptr[6])) >> 12; + const int32_t M24 = r6_y * (int32_t)RAM_SWAPL(r5_ptr[7]); + + const int32_t M31 = (int32_t)RAM_SWAPL(r5_ptr[ 8]) >> 4; + const int32_t M32 = (int32_t)RAM_SWAPL(r5_ptr[ 9]) >> 4; + const int32_t M33 = (int32_t)RAM_SWAPL(r5_ptr[10]) >> 4; + const int32_t M34 = (int32_t)RAM_SWAPL(r5_ptr[11]) << 8; + + uint32_t counter = RAM_SWAPL(r4_ptr[1]); + const uint32_t in_address = RAM_SWAPL(r4_ptr[2]); + + state->cycles += 111; + + /* 0x601F3FA */ + + state->cycles += 7 + 63*counter; + + const int16_t *in = in_address & 0x06000000 + ? (const int16_t *)HRAM_PTR(in_address) + : (const int16_t *)LRAM_PTR(in_address); + int16_t *out = (int16_t *)HRAM_PTR(state->R[7]); + + do { + const int32_t out_x = M11*in[0] + M12*in[1] + M13*in[2] + M14; + const int32_t out_y = M21*in[0] + M22*in[1] + M23*in[2] + M24; + const int32_t out_z = M31*in[0] + M32*in[1] + M33*in[2] + M34; + *(int32_t *)&out[2] = RAM_SWAPL(out_z); + *(int32_t *)&out[4] = RAM_SWAPL(out_x); + *(int32_t *)&out[6] = RAM_SWAPL(out_y); + uint32_t clip_flags = (out_z >= RAM_SWAPL(r6_ptr[5]) << 8) << 5 + | (out_z < RAM_SWAPL(r6_ptr[4]) << 8) << 4; + if (!(clip_flags & 0x10)) { + const float coord_mult = 256.0f / out_z; + out[0] = ifloorf(coord_mult * out_x); + out[1] = ifloorf(coord_mult * out_y); + clip_flags |= (out[1] > ((const int16_t *)r6_ptr)[4]) << 3 + | (out[1] < ((const int16_t *)r6_ptr)[5]) << 2 + | (out[0] > ((const int16_t *)r6_ptr)[7]) << 1 + | (out[0] < ((const int16_t *)r6_ptr)[6]) << 0; + state->cycles += 19; + } + *(uint32_t *)&out[12] = RAM_SWAPL(clip_flags); + in += 3; + out += 16; + } while (--counter != 0); + + state->R[4] += 12; + state->PC = state->PR; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x601FB70: Entry to another self-modified routine like 0x601EE60 */ + +static FASTCALL void Azel_0601FB70(SH2State *state) +{ + const uint16_t insn = HRAM_LOADW(0x601FB72, 0); + if (insn>>12 == 0xA) { // BRA instruction + state->R[0] = 0x601FC94; + state->MACL = state->MACH = 0; + state->PC += 6 + ((int32_t)(insn<<20) >> 19); + state->cycles += 4; + } else { + if (insn != 0x6103) { + DMSG("WARNING: Wrong instruction at 0x601FB72 (%04X)", insn); + } + const int32_t *M_ptr = + state->R[5] & 0x6000000 ? (int32_t *)HRAM_PTR(state->R[5]) + : (int32_t *)LRAM_PTR(state->R[5]); + const int32_t in_x = HRAM_LOADL(0x601FC9C, 0); + const int32_t in_y = HRAM_LOADL(0x601FC9C, 4); + const int32_t in_z = HRAM_LOADL(0x601FC9C, 8); + int16_t out_x = (RAM_SWAPL(M_ptr[ 0]) * in_x + + RAM_SWAPL(M_ptr[ 4]) * in_y + + RAM_SWAPL(M_ptr[ 8]) * in_z) >> 16; + int16_t out_y = (RAM_SWAPL(M_ptr[ 1]) * in_x + + RAM_SWAPL(M_ptr[ 5]) * in_y + + RAM_SWAPL(M_ptr[ 9]) * in_z) >> 16; + int16_t out_z = (RAM_SWAPL(M_ptr[ 2]) * in_x + + RAM_SWAPL(M_ptr[ 6]) * in_y + + RAM_SWAPL(M_ptr[10]) * in_z) >> 16; + HRAM_STOREW(0x601FC94, 0, out_x); + HRAM_STOREW(0x601FC94, 2, out_y); + HRAM_STOREW(0x601FC94, 4, out_z); + state->PC = state->PR; + state->cycles += 58; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6022E18: Short but difficult-to-optimize routine */ + +static FASTCALL void Azel_06022E18(SH2State *state) +{ + uint32_t cycles = state->cycles; + + int32_t counter = HRAM_LOADL(state->R[4], 4); + if (counter > 0) { + HRAM_STOREL(state->R[4], 4, counter-1); + state->R[0] = HRAM_LOADL(state->R[4], 8); + state->PC = state->PR; + state->cycles = cycles + 9; + return; + } + + uint32_t index = HRAM_LOADL(state->R[4], 0); + int32_t r0 = (int16_t)LRAM_LOADW(state->R[5], index*2); + const uint32_t limit = state->R[6]; + if (index != 0) { + HRAM_STOREL(state->R[4], 4, (r0 & 15) - 1); + r0 &= -16; + cycles += 5; + } else { + HRAM_STOREL(state->R[4], 4, 0); + r0 <<= 4; + } + HRAM_STOREL(state->R[4], 8, r0); + index++; + if (index >= limit) { + index = 0; + cycles++; + } + HRAM_STOREL(state->R[4], 0, index); + state->R[0] = r0; + state->PC = state->PR; + state->cycles = cycles + 21; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x6035xxx: Mathematical library routines */ + +static FASTCALL void Azel_06035530(SH2State *state) +{ + if (LIKELY(state->R[5] != 0)) { + state->R[0] = + ((int64_t)(int32_t)state->R[4] << 16) / (int32_t)state->R[5]; + state->cycles += 52; + } else { + state->R[0] = 0; + state->cycles += 7; + } + state->PC = state->PR; +} + +static FASTCALL void Azel_06035552(SH2State *state) +{ + if (LIKELY(state->R[6] != 0)) { + state->R[0] = + ((int64_t)(int32_t)state->R[4] * (int64_t)(int32_t)state->R[5]) + / (int32_t)state->R[6]; + } + state->cycles += 15; + state->PC = state->PR; +} + +static FASTCALL void Azel_0603556C(SH2State *state) +{ +#ifdef __mips__ // GCC's optimizer fails yet again... + + asm(".set push; .set noreorder\n" +# ifdef WORDS_BIGENDIAN // Just for completeness + "lh $v0, 18(%[state])\n" + "lh $v1, 22(%[state])\n" +# else + "lh $v0, 16(%[state])\n" + "lh $v1, 20(%[state])\n" +# endif + "lw $a1, 92(%[state])\n" + "lw $a2, 84(%[state])\n" + "mult $v0, $v1\n" + "lw $v1, 24(%[state])\n" + "addiu $a1, $a1, 15\n" + "sw $a1, 92(%[state])\n" + "sw $a2, 88(%[state])\n" + /* 1 cycle wasted */ + "mflo $v0\n" + "div $zero, $v0, $v1\n" // $zero needed to avoid the div-by-zero check + /* Up to 33 cycles wasted (sigh) */ + "lw $v0, 0(%[state])\n" + "bnezl $v1, 1f\n" + "mflo $v0\n" + "1:\n" + /* This is totally evil, but it works as long as GCC doesn't try to + * set up any stack frames on us, and it lets the routine fit in + * one cache line (barely). */ + "jr $ra\n" + "sw $v0, 0(%[state])\n" + ".set pop" + : "=m" (*state) + : [state] "r" (state) + : "v0", "v1", "a1", "a2", "hi", "lo" + ); + +#else // !__mips__ + + if (LIKELY(state->R[6] != 0)) { + state->R[0] = ((int16_t)state->R[4] * (int16_t)state->R[5]) + / (int32_t)state->R[6]; + } + state->cycles += 15; + state->PC = state->PR; + +#endif +} + +/*----------------------------------*/ + +static FASTCALL void Azel_06035A8C(SH2State *state) +{ + const uint32_t ptr = HRAM_LOADL(0x604AEA4, 0); + HRAM_STOREL(0x604AEA4, 0, ptr + 48); + state->R[4] = ptr; + state->R[5] = ptr + 48; + state->cycles += 9; + return Azel_06035AA0(state); +} + +static FASTCALL void Azel_06035A9C(SH2State *state) +{ + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + state->cycles += 2; + return Azel_06035AA0(state); +} + +static FASTCALL void Azel_06035AA0(SH2State *state) +{ + const uint32_t *src = (const uint32_t *)HRAM_PTR(state->R[4]); + uint32_t *dest = (uint32_t *)HRAM_PTR(state->R[5]); + + const uint32_t *src_limit = &src[12]; + for (; src < src_limit; src += 4, dest += 4) { + const uint32_t word0 = src[0]; + const uint32_t word1 = src[1]; + const uint32_t word2 = src[2]; + const uint32_t word3 = src[3]; + dest[0] = word0; + dest[1] = word1; + dest[2] = word2; + dest[3] = word3; + } + + state->R[0] = state->R[5]; + state->PC = state->PR; + state->cycles += 27; +} + +/*----------------------------------*/ + +static ALWAYS_INLINE void Azel_06035B14_F00_common( + SH2State *state, const int32_t *vector, const int32_t *matrix, + int32_t *out_x_ptr, int32_t *out_y_ptr, int32_t *out_z_ptr); + +static FASTCALL void Azel_06035B14(SH2State *state) +{ + const int32_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int32_t *)HRAM_PTR(state->R[4]) + : (const int32_t *)LRAM_PTR(state->R[4]); + int32_t *r5_ptr = (int32_t *)HRAM_PTR(HRAM_LOADL(0x604AEA4, 0)); + + state->PC = state->PR; + state->cycles += 56; + return Azel_06035B14_F00_common(state, r4_ptr, r5_ptr, + &r5_ptr[3], &r5_ptr[7], &r5_ptr[11]); +} + +static FASTCALL void Azel_06035F00(SH2State *state) +{ + const int32_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int32_t *)HRAM_PTR(state->R[4]) + : (const int32_t *)LRAM_PTR(state->R[4]); + int32_t *r5_ptr = (int32_t *)HRAM_PTR(state->R[5]); + const int32_t *r6_ptr = (const int32_t *)HRAM_PTR(HRAM_LOADL(0x604AEA4, 0)); + + state->PC = state->PR; + state->cycles += 54; + return Azel_06035B14_F00_common(state, r4_ptr, r6_ptr, + &r5_ptr[0], &r5_ptr[1], &r5_ptr[2]); +} + +static FASTCALL void Azel_06035F04(SH2State *state) +{ + const int32_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int32_t *)HRAM_PTR(state->R[4]) + : (const int32_t *)LRAM_PTR(state->R[4]); + int32_t *r5_ptr = (int32_t *)HRAM_PTR(state->R[5]); + const int32_t *r6_ptr = (const int32_t *)HRAM_PTR(state->R[6]); + + state->PC = state->PR; + state->cycles += 52; + return Azel_06035B14_F00_common(state, r4_ptr, r6_ptr, + &r5_ptr[0], &r5_ptr[1], &r5_ptr[2]); +} + +static ALWAYS_INLINE void Azel_06035B14_F00_common( + SH2State *state, const int32_t *vector, const int32_t *matrix, + int32_t *out_x_ptr, int32_t *out_y_ptr, int32_t *out_z_ptr) +{ +#ifdef PSP + + int32_t v_0, v_1, v_2, M_0, M_1, M_2, M_3, hi, lo; + asm(".set push; .set noreorder\n" + + "lw %[v_0], 0(%[vector])\n" + "lw %[M_0], 0(%[matrix])\n" + "lw %[v_1], 4(%[vector])\n" + "ror %[v_0], %[v_0], 16\n" + "ror %[M_0], %[M_0], 16\n" + "mult %[v_0], %[M_0]\n" + "lw %[M_1], 4(%[matrix])\n" + "ror %[v_1], %[v_1], 16\n" + "ror %[M_1], %[M_1], 16\n" + "madd %[v_1], %[M_1]\n" + "lw %[v_2], 8(%[vector])\n" + "lw %[M_2], 8(%[matrix])\n" + "lw %[M_3], 12(%[matrix])\n" + "ror %[v_2], %[v_2], 16\n" + "ror %[M_2], %[M_2], 16\n" + "madd %[v_2], %[M_2]\n" + "lw %[M_0], 16(%[matrix])\n" + "lw %[M_1], 20(%[matrix])\n" + "lw %[M_2], 24(%[matrix])\n" + "ror %[M_3], %[M_3], 16\n" + "ror %[M_0], %[M_0], 16\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + + "mult %[v_0], %[M_0]\n" + "ror %[M_1], %[M_1], 16\n" + "srl %[lo], %[lo], 16\n" + "ins %[lo], %[hi], 16, 16\n" + "madd %[v_1], %[M_1]\n" + "ror %[M_2], %[M_2], 16\n" + "addu %[lo], %[M_3], %[lo]\n" + "lw %[M_3], 28(%[matrix])\n" + "ror %[lo], %[lo], 16\n" + "sw %[lo], 0(%[out_x_ptr])\n" + "madd %[v_2], %[M_2]\n" + "lw %[M_0], 32(%[matrix])\n" + "lw %[M_1], 36(%[matrix])\n" + "lw %[M_2], 40(%[matrix])\n" + "ror %[M_3], %[M_3], 16\n" + "ror %[M_0], %[M_0], 16\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + + "mult %[v_0], %[M_0]\n" + "ror %[M_1], %[M_1], 16\n" + "srl %[lo], %[lo], 16\n" + "ins %[lo], %[hi], 16, 16\n" + "madd %[v_1], %[M_1]\n" + "ror %[M_2], %[M_2], 16\n" + "addu %[lo], %[M_3], %[lo]\n" + "lw %[M_3], 44(%[matrix])\n" + "ror %[lo], %[lo], 16\n" + "sw %[lo], 0(%[out_y_ptr])\n" + "madd %[v_2], %[M_2]\n" + "ror %[M_3], %[M_3], 16\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + "srl %[lo], %[lo], 16\n" + "ins %[lo], %[hi], 16, 16\n" + "addu %[lo], %[M_3], %[lo]\n" + "ror %[lo], %[lo], 16\n" + "sw %[lo], 0(%[out_z_ptr])\n" + + ".set pop" + : "=m" (*out_x_ptr), "=m" (*out_y_ptr), "=m" (*out_z_ptr), + [v_0] "=&r" (v_0), [v_1] "=&r" (v_1), [v_2] "=&r" (v_2), + [M_0] "=&r" (M_0), [M_1] "=&r" (M_1), [M_2] "=&r" (M_2), + [M_3] "=&r" (M_3), [hi] "=&r" (hi), [lo] "=&r" (lo) + : [vector] "r" (vector), [matrix] "r" (matrix), + [out_x_ptr] "r" (out_x_ptr), [out_y_ptr] "r" (out_y_ptr), + [out_z_ptr] "r" (out_z_ptr) + : "hi", "lo" + ); + +#else // !PSP + + const int32_t temp0 = + ((int64_t)RAM_SWAPL(vector[0]) * (int64_t)RAM_SWAPL(matrix[0]) + + (int64_t)RAM_SWAPL(vector[1]) * (int64_t)RAM_SWAPL(matrix[1]) + + (int64_t)RAM_SWAPL(vector[2]) * (int64_t)RAM_SWAPL(matrix[2]) + ) >> 16; + *out_x_ptr = RAM_SWAPL(RAM_SWAPL(matrix[3]) + temp0); + + const int32_t temp1 = + ((int64_t)RAM_SWAPL(vector[0]) * (int64_t)RAM_SWAPL(matrix[4]) + + (int64_t)RAM_SWAPL(vector[1]) * (int64_t)RAM_SWAPL(matrix[5]) + + (int64_t)RAM_SWAPL(vector[2]) * (int64_t)RAM_SWAPL(matrix[6]) + ) >> 16; + *out_y_ptr = RAM_SWAPL(RAM_SWAPL(matrix[7]) + temp1); + + const int32_t temp2 = + ((int64_t)RAM_SWAPL(vector[0]) * (int64_t)RAM_SWAPL(matrix[8]) + + (int64_t)RAM_SWAPL(vector[1]) * (int64_t)RAM_SWAPL(matrix[9]) + + (int64_t)RAM_SWAPL(vector[2]) * (int64_t)RAM_SWAPL(matrix[10]) + ) >> 16; + *out_z_ptr = RAM_SWAPL(RAM_SWAPL(matrix[11]) + temp2); + +#endif // PSP +} + +/*----------------------------------*/ + +static ALWAYS_INLINE void Azel_06035xxx_rotate_common( + int angle, int32_t *r5_ptr, unsigned int x_idx, unsigned int y_idx, + int invert); + +static FASTCALL void Azel_06035C18(SH2State *state) +{ + const int16_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int16_t *)HRAM_PTR(state->R[4]) + : (const int16_t *)LRAM_PTR(state->R[4]); + const uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + + if (r4_ptr[4] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[4] & 0xFFF, r5_ptr, 0, 1, 0); + } + if (r4_ptr[2] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[2] & 0xFFF, r5_ptr, 0, 2, 1); + } + if (r4_ptr[0] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[0] & 0xFFF, r5_ptr, 1, 2, 0); + } + + state->PC = state->PR; + state->cycles += 290; +} + +static FASTCALL void Azel_06035C3C(SH2State *state) +{ + const int16_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int16_t *)HRAM_PTR(state->R[4]) + : (const int16_t *)LRAM_PTR(state->R[4]); + const uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + + if (r4_ptr[2] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[2] & 0xFFF, r5_ptr, 0, 2, 1); + } + if (r4_ptr[0] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[0] & 0xFFF, r5_ptr, 1, 2, 0); + } + if (r4_ptr[4] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[4] & 0xFFF, r5_ptr, 0, 1, 0); + } + + state->PC = state->PR; + state->cycles += 290; +} + +static FASTCALL void Azel_06035C60(SH2State *state) +{ + const int16_t *r4_ptr = state->R[4] & 0x06000000 + ? (const int16_t *)HRAM_PTR(state->R[4]) + : (const int16_t *)LRAM_PTR(state->R[4]); + const uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + + if (r4_ptr[2] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[4] & 0xFFF, r5_ptr, 0, 1, 0); + } + if (r4_ptr[1] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[2] & 0xFFF, r5_ptr, 0, 2, 1); + } + if (r4_ptr[0] & 0xFFF) { + Azel_06035xxx_rotate_common(r4_ptr[0] & 0xFFF, r5_ptr, 1, 2, 0); + } + + state->PC = state->PR; + state->cycles += 290; +} + +static FASTCALL void Azel_06035C84(SH2State *state) +{ + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035C96(state); +} + +static FASTCALL void Azel_06035C90(SH2State *state) +{ + state->R[4] >>= 16; + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035C96(state); +} + +static FASTCALL void Azel_06035C96(SH2State *state) +{ + const int32_t angle = state->R[4] & 0xFFF; + int32_t *r5_ptr = + state->R[5] & 0x6000000 ? (int32_t *)HRAM_PTR(state->R[5]) + : (int32_t *)LRAM_PTR(state->R[5]); + Azel_06035xxx_rotate_common(angle, r5_ptr, 1, 2, 0); + state->PC = state->PR; + state->cycles += 88; +} + +static FASTCALL void Azel_06035D24(SH2State *state) +{ + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035D36(state); +} + +static FASTCALL void Azel_06035D30(SH2State *state) +{ + state->R[4] >>= 16; + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035D36(state); +} + +static FASTCALL void Azel_06035D36(SH2State *state) +{ + const int32_t angle = state->R[4] & 0xFFF; + int32_t *r5_ptr = + state->R[5] & 0x6000000 ? (int32_t *)HRAM_PTR(state->R[5]) + : (int32_t *)LRAM_PTR(state->R[5]); + Azel_06035xxx_rotate_common(angle, r5_ptr, 0, 2, 1); + state->PC = state->PR; + state->cycles += 96; +} + +static FASTCALL void Azel_06035DD4(SH2State *state) +{ + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035DE6(state); +} + +static FASTCALL void Azel_06035DE0(SH2State *state) +{ + state->R[4] >>= 16; + state->R[5] = HRAM_LOADL(0x604AEA4, 0); + return Azel_06035DE6(state); +} + +static FASTCALL void Azel_06035DE6(SH2State *state) +{ + const int32_t angle = state->R[4] & 0xFFF; + int32_t *r5_ptr = + state->R[5] & 0x6000000 ? (int32_t *)HRAM_PTR(state->R[5]) + : (int32_t *)LRAM_PTR(state->R[5]); + Azel_06035xxx_rotate_common(angle, r5_ptr, 0, 1, 0); + state->PC = state->PR; + state->cycles += 87; +} + +static ALWAYS_INLINE void Azel_06035xxx_rotate_common( + int angle, int32_t *r5_ptr, unsigned int x_idx, unsigned int y_idx, + int invert) +{ + const int32_t *sin_table = (const int32_t *)LRAM_PTR(0x216660); + const int32_t *cos_table = (const int32_t *)LRAM_PTR(0x217660); + const int32_t sin_angle = invert ? -RAM_SWAPL(sin_table[angle]) + : +RAM_SWAPL(sin_table[angle]); + const int32_t cos_angle = RAM_SWAPL(cos_table[angle]); + int32_t new_x, new_y; + +#ifdef PSP + + int32_t old_x, old_y; + + asm(".set push; .set noreorder\n" + + "lw %[old_x], 0+%[x_ofs](%[r5_ptr])\n" + "lw %[old_y], 0+%[y_ofs](%[r5_ptr])\n" + "ror %[old_x], %[old_x], 16\n" + "mult %[old_x], %[cos_angle]\n" + "ror %[old_y], %[old_y], 16\n" + "madd %[old_y], %[sin_angle]\n" + "mfhi %[new_y]\n" + "mflo %[new_x]\n" + "mult %[old_x], %[nsin_angle]\n" + "lw %[old_x], 16+%[x_ofs](%[r5_ptr])\n" + /* We want ROR(HI<<16 | LO>>16, 16), but that's equivalent + * to (HI & 0xFFFF) | (LO & 0xFFFF0000), which we can do + * with a single INS instruction. */ + "ins %[new_x], %[new_y], 0, 16\n" + "sw %[new_x], 0+%[x_ofs](%[r5_ptr])\n" + "ror %[old_x], %[old_x], 16\n" + "madd %[old_y], %[cos_angle]\n" + "lw %[old_y], 16+%[y_ofs](%[r5_ptr])\n" + "ror %[old_y], %[old_y], 16\n" + "mfhi %[new_x]\n" + "mflo %[new_y]\n" + + "mult %[old_x], %[cos_angle]\n" + "ins %[new_y], %[new_x], 0, 16\n" + "sw %[new_y], 0+%[y_ofs](%[r5_ptr])\n" + "madd %[old_y], %[sin_angle]\n" + "mfhi %[new_y]\n" + "mflo %[new_x]\n" + "mult %[old_x], %[nsin_angle]\n" + "lw %[old_x], 32+%[x_ofs](%[r5_ptr])\n" + "ins %[new_x], %[new_y], 0, 16\n" + "sw %[new_x], 16+%[x_ofs](%[r5_ptr])\n" + "ror %[old_x], %[old_x], 16\n" + "madd %[old_y], %[cos_angle]\n" + "lw %[old_y], 32+%[y_ofs](%[r5_ptr])\n" + "ror %[old_y], %[old_y], 16\n" + "mfhi %[new_x]\n" + "mflo %[new_y]\n" + + "mult %[old_x], %[cos_angle]\n" + "ins %[new_y], %[new_x], 0, 16\n" + "sw %[new_y], 16+%[y_ofs](%[r5_ptr])\n" + "madd %[old_y], %[sin_angle]\n" + "mfhi %[new_y]\n" + "mflo %[new_x]\n" + "mult %[old_x], %[nsin_angle]\n" + "ins %[new_x], %[new_y], 0, 16\n" + "sw %[new_x], 32+%[x_ofs](%[r5_ptr])\n" + "madd %[old_y], %[cos_angle]\n" + "mfhi %[new_x]\n" + "mflo %[new_y]\n" + "ins %[new_y], %[new_x], 0, 16\n" + "sw %[new_y], 32+%[y_ofs](%[r5_ptr])\n" + + ".set pop" + : "=m" (r5_ptr[0*4 + x_idx]), "=m" (r5_ptr[0*4 + y_idx]), + "=m" (r5_ptr[1*4 + x_idx]), "=m" (r5_ptr[1*4 + y_idx]), + "=m" (r5_ptr[2*4 + x_idx]), "=m" (r5_ptr[2*4 + y_idx]), + [new_x] "=&r" (new_x), [new_y] "=&r" (new_y), + [old_x] "=&r" (old_x), [old_y] "=&r" (old_y) + : [sin_angle] "r" (sin_angle), [cos_angle] "r" (cos_angle), + [nsin_angle] "r" (-sin_angle), [r5_ptr] "r" (r5_ptr), + [x_ofs] "i" (x_idx*4), [y_ofs] "i" (y_idx*4) + : "hi", "lo" + ); + +#else // !PSP + + #define DOT2(x1,y1,x2,y2) \ + (((int64_t)(x1) * (int64_t)(x2) + (int64_t)(y1) * (int64_t)(y2)) >> 16) + #define ROTATE(index) \ + new_x = DOT2(RAM_SWAPL(r5_ptr[index*4 + x_idx]), \ + RAM_SWAPL(r5_ptr[index*4 + y_idx]), \ + cos_angle, sin_angle); \ + new_y = DOT2(RAM_SWAPL(r5_ptr[index*4 + x_idx]), \ + RAM_SWAPL(r5_ptr[index*4 + y_idx]), \ + -sin_angle, cos_angle); \ + r5_ptr[index*4 + x_idx] = RAM_SWAPL(new_x); \ + r5_ptr[index*4 + y_idx] = RAM_SWAPL(new_y) + + ROTATE(0); + ROTATE(1); + ROTATE(2); + + #undef DOT2 + #undef ROTATE + +#endif +} + +/*----------------------------------*/ + +static ALWAYS_INLINE void Azel_06035Exx_scale_common( + int32_t scale, int32_t *r5_ptr); + +static FASTCALL void Azel_06035E70(SH2State *state) +{ + uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + Azel_06035Exx_scale_common(state->R[4], r5_ptr+0); + state->PC = state->PR; + state->cycles += 28; +} + +static FASTCALL void Azel_06035EA0(SH2State *state) +{ + uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + Azel_06035Exx_scale_common(state->R[4], r5_ptr+1); + state->PC = state->PR; + state->cycles += 28; +} + +static FASTCALL void Azel_06035ED0(SH2State *state) +{ + uint32_t r5 = HRAM_LOADL(0x604AEA4, 0); + int32_t *r5_ptr = r5 & 0x6000000 ? (int32_t *)HRAM_PTR(r5) + : (int32_t *)LRAM_PTR(r5); + Azel_06035Exx_scale_common(state->R[4], r5_ptr+2); + state->PC = state->PR; + state->cycles += 28; +} + +static ALWAYS_INLINE void Azel_06035Exx_scale_common( + int32_t scale, int32_t *r5_ptr) +{ +#ifdef PSP + + int32_t a, b, c, hi, lo; + + asm(".set push; .set noreorder\n" + + "lw %[a], 0(%[r5_ptr])\n" + "lw %[b], 16(%[r5_ptr])\n" + "lw %[c], 32(%[r5_ptr])\n" + "ror %[a], %[a], 16\n" + "mult %[a], %[scale]\n" + "ror %[b], %[b], 16\n" + "ror %[c], %[c], 16\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + "mult %[b], %[scale]\n" + // As with rotation, we take a shortcut for ROR(HI<<16 | LO>>16, 16). + "ins %[lo], %[hi], 0, 16\n" + "sw %[lo], 0(%[r5_ptr])\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + "mult %[c], %[scale]\n" + "ins %[lo], %[hi], 0, 16\n" + "sw %[lo], 16(%[r5_ptr])\n" + "mfhi %[hi]\n" + "mflo %[lo]\n" + "ins %[lo], %[hi], 0, 16\n" + "sw %[lo], 32(%[r5_ptr])\n" + + ".set pop" + : "=m" (r5_ptr[0]), "=m" (r5_ptr[4]), "=m" (r5_ptr[8]), + [a] "=&r" (a), [b] "=&r" (b), [c] "=&r" (c), + [hi] "=&r" (hi), [lo] "=&r" (lo) + : [scale] "r" (scale), [r5_ptr] "r" (r5_ptr) + : "hi", "lo" + ); + +#else // !PSP + + r5_ptr[0] = RAM_SWAPL(((int64_t)RAM_SWAPL(r5_ptr[0]) * scale) >> 16); + r5_ptr[4] = RAM_SWAPL(((int64_t)RAM_SWAPL(r5_ptr[4]) * scale) >> 16); + r5_ptr[8] = RAM_SWAPL(((int64_t)RAM_SWAPL(r5_ptr[8]) * scale) >> 16); + +#endif +} + +/*-----------------------------------------------------------------------*/ + +/* 0x60360F0: Delay routine (not automatically foldable due to the BF) */ + +static FASTCALL void Azel_060360F0(SH2State *state) +{ + state->PC = state->PR; + state->cycles += state->R[4]*4 + 1; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x603A22C: Wrapper for 0x603A242 that jumps in with a BRA */ + +static int Azel_0603A22C_detect(SH2State *state, uint32_t address, + const uint16_t *fetch) +{ + return fetch[0] == 0xA009 // bra 0x603A242 + && fetch[1] == 0xE700 // mov #0, r7 + ? 2 : 0; +} + +static FASTCALL void Azel_0603A22C(SH2State *state) +{ + state->R[7] = 0; + return Azel_0603A242(state); +} + +/*----------------------------------*/ + +/* 0x603A242: CD command execution routine */ + +static int Azel_0603A242_state = 0; +static int Azel_0603A242_cmd51_count = 0; // Count of sequential 0x51 commands + +static FASTCALL void Azel_0603A242(SH2State *state) +{ + if (Azel_0603A242_state == 0) { + const uint32_t r4 = state->R[4]; + uint16_t *ptr_6053278 = (uint16_t *)HRAM_PTR(0x6053278); + *ptr_6053278 |= Cs2ReadWord(0x90008); + if ((*ptr_6053278 & r4) != r4) { + state->R[0] = -1; + state->PC = state->PR; + state->cycles += 77; + return; + } + if (!(*ptr_6053278 & 1)) { + state->R[0] = -2; + state->PC = state->PR; + state->cycles += 82; + return; + } + + Cs2WriteWord(0x90008, ~(r4 | 1)); + *ptr_6053278 &= ~1; + const uint32_t r5 = state->R[5]; + uintptr_t r5_base = (uintptr_t)direct_pages[r5>>19]; + const uint16_t *r5_ptr = (const uint16_t *)(r5_base + r5); + Cs2WriteWord(0x90018, r5_ptr[0]); + Cs2WriteWord(0x9001C, r5_ptr[1]); + Cs2WriteWord(0x90020, r5_ptr[2]); + Cs2WriteWord(0x90024, r5_ptr[3]); + state->cycles += 88; + Azel_0603A242_state = 1; + if (r5_ptr[0]>>8 == 0x51) { + Azel_0603A242_cmd51_count++; + } else if (r5_ptr[0]>>8 != 0) { // Command 0x00 doesn't reset the count + Azel_0603A242_cmd51_count = 0; + } + return; + } + + if (Azel_0603A242_state == 1) { + uint32_t status = (uint16_t)Cs2ReadWord(0x90008); + if (status & 1) { + state->cycles += 23; + Azel_0603A242_state = 2; + } else { + /* Technically a timeout loop, but we assume no timeouts */ + state->cycles = state->cycle_limit; + } + return; + } + + if (Azel_0603A242_state == 2) { + const uint32_t r6 = state->R[6]; + uintptr_t r6_base = (uintptr_t)direct_pages[r6>>19]; + uint16_t *r6_ptr = (uint16_t *)(r6_base + r6); + const unsigned int CR1 = Cs2ReadWord(0x90018); + const unsigned int CR2 = Cs2ReadWord(0x9001C); + const unsigned int CR3 = Cs2ReadWord(0x90020); + const unsigned int CR4 = Cs2ReadWord(0x90024); + if (Azel_0603A242_cmd51_count >= 0 && CR4 == 0) { + /* We're probably waiting for a sector and it hasn't arrived + * yet, so consume enough cycles to get us to that next sector. + * But be careful we don't wait an extra sector if the current + * sector finished reading between executing the CS2 command + * and retrieving the delay period. */ + const unsigned int usec_left = Cs2GetTimeToNextSector(); + if (usec_left > 0 && usec_left < (1000000/(75*2))*9/10) { + uint32_t cycles_left = 0; + if (yabsys.CurSH2FreqType == CLKTYPE_26MHZ) { + cycles_left = (26847 * usec_left) / 1000; + } else { + cycles_left = (28637 * usec_left) / 1000; + } + state->cycles += cycles_left; + } + } + r6_ptr[0] = CR1; + r6_ptr[1] = CR2; + r6_ptr[2] = CR3; + r6_ptr[3] = CR4; + uint16_t *dest = (uint16_t *)HRAM_PTR(0x605329C); + *((uint8_t *)dest + 1) = CR1>>8; + if (state->R[7]) { + dest[2] = CR1<<8 | CR2>>8; + dest[3] = CR2<<8 | CR3>>8; + dest[4] = CR3 & 0xFF; + dest[5] = CR4; + } + state->R[0] = 0; +#if defined(TRACE) || defined(TRACE_STEALTH) || defined(TRACE_LITE) + state->R[1] = ~0xF0; + state->R[2] = 0; + state->R[3] = ~0xF0; + state->R[4] = state->R[15] - 12; + state->R[5] = 0x605329C; + state->SR &= ~SR_T; +#endif + state->PC = state->PR; + state->cycles += 121; + Azel_0603A242_state = 0; + return; + } +} + +/*-----------------------------------------------------------------------*/ + +/* 0x603ABE0: The sole purpose of this routine seems to be to copy the + * first sample in a streaming audio ring buffer over the last sample. + * I don't know whether this is to work around an idiosyncrasy of the real + * SCSP or what, but it causes glitches when using the ME because we try + * to read the sample from an uncached address after writing it to a + * cached address. In any case, the function (as applied to audio data) + * is meaningless for emulation, so we null it out entirely. */ + +static FASTCALL void Azel_0603ABE0(SH2State *state) +{ + if ((MappedMemoryReadLong(state->R[4]+0xA0) & 0x1FF00000) == 0x05A00000) { + state->PC = state->PR; + state->cycles += 23; + return; + } + /* It's not touching sound RAM, so it must be doing something else. + * Let it run normally. */ + state->R[0] = 0xEF; + state->PC += 2; + state->cycles++; +} + +/*-----------------------------------------------------------------------*/ + +/* 0x603DD6E: CD read routine (actually a generalized copy routine, but + * doesn't seem to be used for anything else) */ + +static FASTCALL void Azel_0603DD6E(SH2State *state) +{ + int32_t len = MappedMemoryReadLong(state->R[15]); + uint32_t dest = state->R[4]; + + if (UNLIKELY(state->R[5] != 1) + || UNLIKELY(state->R[6] != 0x25818000) + || UNLIKELY(state->R[7] != 0) + || UNLIKELY(len <= 0) + || UNLIKELY((len & 3) != 0) + ) { + state->SR &= ~SR_T; + state->SR |= (state->R[4] == 0) << SR_T_SHIFT; + state->PC += 2; + state->cycles += 1; + return; + } + + state->PC = state->PR; + state->cycles += 30 + len*2; + + const uint32_t dest_page = dest>>19; + uint8_t *dest_base; + + dest_base = direct_pages[dest_page]; + if (dest_base) { + Cs2RapidCopyT2(dest_base + dest, len/4); + sh2_write_notify(dest, len); + return; + } + + dest_base = byte_direct_pages[dest_page]; + if (dest_base) { + Cs2RapidCopyT1(dest_base + dest, len/4); + return; + } + + if ((dest & 0x1FF00000) == 0x05A00000) { + Cs2RapidCopyT2(SoundRam + (dest & 0x7FFFF), len/4); + M68KWriteNotify(dest & 0x7FFFF, len); + return; + } + + for (; len > 0; len -= 4, dest += 4) { + const uint32_t word = MappedMemoryReadLong(0x25818000); + MappedMemoryWriteLong(dest, word); + } +} + +/*************************************************************************/ + +#endif // ENABLE_JIT + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/satopt-sh2.h b/yabause/src/psp/satopt-sh2.h new file mode 100644 index 0000000000..3969b1d315 --- /dev/null +++ b/yabause/src/psp/satopt-sh2.h @@ -0,0 +1,61 @@ +/* src/psp/satopt-sh2.h: Saturn-specific SH-2 optimization header + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_SATOPT_SH2_H +#define PSP_SATOPT_SH2_H + +/*************************************************************************/ + +/** + * saturn_optimize_sh2: Search for and return, if available, a native + * implementation of the SH-2 routine starting at the given address. + * + * [Parameters] + * state: Processor state block pointer + * address: Address from which to translate + * fetch: Pointer corresponding to "address" from which opcodes can + * be fetched + * func_ret: Pointer to variable to receive address of native function + * implementing this routine if return value is nonzero + * for_fold: Nonzero if the callback is being called to look up a + * subroutine for folding, zero if being called for a + * full block translation + * [Return value] + * Length of translated block in instructions (nonzero) if optimized + * code was generated, else zero + */ +extern unsigned int saturn_optimize_sh2(SH2State *state, uint32_t address, + const uint16_t *fetch, + SH2NativeFunctionPointer *func_ret, + int for_fold); + +/*************************************************************************/ + +#endif // PSP_SATOPT_SH2_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2-core.i b/yabause/src/psp/sh2-core.i new file mode 100644 index 0000000000..be25488277 --- /dev/null +++ b/yabause/src/psp/sh2-core.i @@ -0,0 +1,4008 @@ +/* src/psp/sh2-core.i: SH-2 instruction decoding core for PSP + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * This file is intended to be #included as part of a source file that + * processes SH-2 instructions. It defines the function: + * static inline unsigned int decode_insn(...) + * which decodes one instruction, performs actions to implement the + * instruction, and returns the instruction's opcode. The parameter list + * is set by the caller using + * #define DECODE_INSN_PARAMS ... + * and should be "void" (without quotes) if no parameters are used. The + * caller may also #define DECODE_INSN_INLINE to any inline-related + * keyword, including ALWAYS_INLINE or NOINLINE; if not defined, no such + * keyword will be used. + * + * The following identifiers must be either defined as macros or passed to + * decode_insn() as parameters: + * uint32_t initial_PC; // Address of the first instruction in the + * // current block, if OPTIMIZE_IDLE is defined + * uint32_t state_reg; // RTL register holding the state block pointer + * uint32_t cur_PC; // Address of the instruction to be decoded + * uint16_t *fetch; // Pointer to instruction to decode (or NULL if + * // direct access is not possible) + * + * Each instruction is decoded into a series of "micro-ops", which should + * be defined as macros before this file is included. See sh2.c or + * sh2-interpret.c for the micro-ops used. + * + * Note that while the decoder generally attempts to maintain SSA (static + * single assignment) form for all registers, optimization of state block + * fields into long-lived registers requires that every path through + * conditional code assign the same register to a given state block field. + * Since a proper SSA implementation could require many SELECT operations + * and temporary registers, leading to inefficient code (the optimization + * of which would be too time-consuming), we intentionally break SSA form + * in such cases, conditionally reassigning new values to state block + * mirror registers. Such cases are noted throughout the code. + */ + +/*************************************************************************/ + +/* Local convenience macros for defining and loading SH-2 registers */ + +#define GET_REG(name,reg,field,mask) \ + DECLARE_REG(name); \ + if ((REG_GETKNOWN((reg)) & (mask)) == (mask)) { \ + ALLOC_REG(name); \ + MOVEI(name, REG_GETVALUE((reg)) & (mask)); \ + } else { \ + LOAD_STATE_ALLOC(name, field); \ + } +#define GET_R0 GET_REG(R0, REG_R(0), R[0], 0xFFFFFFFF) +#define GET_R0_W GET_REG(R0, REG_R(0), R[0], 0xFFFF) +#define GET_R0_B GET_REG(R0, REG_R(0), R[0], 0xFF) +#define GET_R15 GET_REG(R15, REG_R(15), R[15], 0xFFFFFFFF) +#define GET_Rn GET_REG(Rn, REG_R(n), R[n], 0xFFFFFFFF) +#define GET_Rm GET_REG(Rm, REG_R(m), R[m], 0xFFFFFFFF) +#define GET_Rm_W GET_REG(Rm, REG_R(m), R[m], 0xFFFF) +#define GET_Rm_B GET_REG(Rm, REG_R(m), R[m], 0xFF) +#define GET_GBR GET_REG(GBR, REG_GBR, GBR, 0xFFFFFFFF) +/* These are generally unknown, so don't waste time on checking them */ +#define GET_SR FLUSH_STATE_SR_T(); \ + DECLARE_REG(SR); LOAD_STATE_ALLOC(SR, SR) +#define GET_SR_T DECLARE_REG(T); LOAD_STATE_SR_T (T) +#define GET_VBR DECLARE_REG(VBR); LOAD_STATE_ALLOC(VBR, VBR) +#define GET_MACH DECLARE_REG(MACH); LOAD_STATE_ALLOC(MACH, MACH) +#define GET_MACL DECLARE_REG(MACL); LOAD_STATE_ALLOC(MACL, MACL) +#define GET_PR DECLARE_REG(PR); LOAD_STATE_ALLOC(PR, PR) + +/* MACH/MACL may be overwritten in the same register by the MAC.[WL] insns, + * so STS MAC uses these macros to force creation of a new register */ +#define GET_MACH_COPY DEFINE_REG(MACH); LOAD_STATE_COPY(MACH, MACH) +#define GET_MACL_COPY DEFINE_REG(MACL); LOAD_STATE_COPY(MACL, MACL) + +/* Versions used by load/store macros to retain the cached offset */ +#define GET_REG_KEEPOFS(name,field) \ + DECLARE_REG(name); LOAD_STATE_ALLOC_KEEPOFS(name, field) +#define GET_R0_KEEPOFS GET_REG_KEEPOFS(R0, R[0]) +#define GET_R15_KEEPOFS GET_REG_KEEPOFS(R15, R[15]) +#define GET_Rn_KEEPOFS GET_REG_KEEPOFS(Rn, R[n]) +#define GET_Rm_KEEPOFS GET_REG_KEEPOFS(Rm, R[m]) +#define GET_GBR_KEEPOFS GET_REG_KEEPOFS(GBR, GBR) + +/* Local convenience macro for moving a value from one SH-2 register to + * another. If the source uses a fixed RTL register (e.g. due to loop + * optimization) but the destination does not, the destination will end up + * sharing the source register, causing the destination's value to + * improperly change when the source is modified. Pass the relevant SH-2 + * register name (R[m], GBR, etc.) as the macro parameter. */ + +#define COPY_FROM_Rn(dest) \ + DECLARE_REG(value); \ + if (!STATE_CACHE_FIXED_REG_WRITABLE(dest)) { \ + STATE_CACHE_CLEAR_FIXED_REG(dest); \ + } \ + if (STATE_CACHE_FIXED_REG(R[n]) && !STATE_CACHE_FIXED_REG(dest)) { \ + ALLOC_REG(value); \ + LOAD_STATE_COPY(value, R[n]); \ + } else { \ + LOAD_STATE_ALLOC(value, R[n]); \ + } \ + if (offsetof(SH2State,SR) == offsetof(SH2State,dest)) { \ + RESET_STATE_SR_T(); \ + } \ + STORE_STATE(dest, value) + +#define COPY_TO_Rn(src) \ + DECLARE_REG(value); \ + if (offsetof(SH2State,SR) == offsetof(SH2State,src)) { \ + FLUSH_STATE_SR_T(); \ + } \ + if (!STATE_CACHE_FIXED_REG_WRITABLE(R[n])) { \ + STATE_CACHE_CLEAR_FIXED_REG(R[n]); \ + } \ + if (STATE_CACHE_FIXED_REG(src) && !STATE_CACHE_FIXED_REG(R[n])) { \ + ALLOC_REG(value); \ + LOAD_STATE_COPY(value, src); \ + } else { \ + LOAD_STATE_ALLOC(value, src); \ + } \ + STORE_STATE(R[n], value) + +/* Local convenience macro for creating result registers to be stored in a + * state block field */ + +#define DEFINE_RESULT_REG(name,field) \ + DECLARE_REG(name); \ + do { \ + name = STATE_CACHE_FIXED_REG(field); \ + if (name && !STATE_CACHE_FIXED_REG_WRITABLE(field)) { \ + STATE_CACHE_CLEAR_FIXED_REG(field); \ + name = 0; \ + } \ + if (!name) { \ + ALLOC_REG(name); \ + } \ + } while (0) + +/* Local convenience macros for storing values to SH-2 registers */ + +#define SET_R0(reg) STORE_STATE(R[0], (reg)) +#define SET_R15(reg) STORE_STATE(R[15],(reg)) +#define SET_Rn(reg) STORE_STATE(R[n], (reg)) +#define SET_Rm(reg) STORE_STATE(R[m], (reg)) +#define SET_SR(reg) RESET_STATE_SR_T(); STORE_STATE(SR, (reg)) +#define SET_SR_T(reg) STORE_STATE_SR_T ((reg)) +#define SET_GBR(reg) STORE_STATE(GBR, (reg)) +#define SET_VBR(reg) STORE_STATE(VBR, (reg)) +#define SET_MACH(reg) STORE_STATE(MACH, (reg)) +#define SET_MACL(reg) STORE_STATE(MACL, (reg)) +#define SET_PR(reg) STORE_STATE(PR, (reg)) +#define SET_PC(reg) STORE_STATE(PC, (reg)) + +/* Set PC to a known value */ +#define SET_PC_KNOWN(value) STORE_STATE_PC((value)) + +/* Local convenience macros for adding constants to SH-2 registers */ + +#define ADDI_R0(imm) ADDI_STATE(R[0], (imm), R0) +#define ADDI_R15(imm) ADDI_STATE(R[15], (imm), R15) +#define ADDI_Rn(imm) ADDI_STATE(R[n], (imm), Rn) +#define ADDI_Rm(imm) ADDI_STATE(R[m], (imm), Rm) +#define ADDI_R0_NOREG(imm) ADDI_STATE_NOREG(R[0], (imm)) +#define ADDI_R15_NOREG(imm) ADDI_STATE_NOREG(R[15], (imm)) +#define ADDI_Rn_NOREG(imm) ADDI_STATE_NOREG(R[n], (imm)) +#define ADDI_Rm_NOREG(imm) ADDI_STATE_NOREG(R[m], (imm)) + +/* Local convenience macro for updating the cycle count */ + +#define ADD_CYCLES() do { \ + if (cur_cycles > 0) { \ + ADDI_STATE_NOREG(cycles, cur_cycles); \ + cur_cycles = 0; \ + } \ +} while (0) + +/*-----------------------------------------------------------------------*/ + +/* Local convenience macros for loading or storing values with various + * addressing modes */ + +/* Increment/decrement amounts */ +#define INCDEC_B 1 +#define INCDEC_W 2 +#define INCDEC_L 4 + +/*----------------------------------*/ + +#define LOAD_Rm(size,dest) LOAD_disp_Rm(size, dest, 0) + +#define LOAD_disp_Rm(size,dest,offset) \ + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF) { \ + SH2_LOAD_ABS_##size(dest, REG_GETVALUE(REG_R(m)) + (offset)); \ + } else { \ + SH2_LOAD_REG_##size(dest, m, (offset), 0); \ + } + +/* Only treat R0 as an offset if it's "small" -- otherwise we might end up + * treating a small value in Rm as a pointer */ +#define LOAD_R0_Rm(size,dest) \ + if (REG_GETKNOWN(REG_R(0)) == 0xFFFFFFFF \ + && REG_GETVALUE(REG_R(0)) + 0x8000 < 0x10000 \ + ) { \ + SH2_LOAD_REG_##size(dest, m, REG_GETVALUE(REG_R(0)), 0); \ + } else { \ + GET_R0_KEEPOFS; \ + GET_Rm_KEEPOFS; \ + DEFINE_REG(address); \ + ADD(address, R0, Rm); \ + const int32_t offset = STATE_CACHE_OFFSET(R[0]) \ + + STATE_CACHE_OFFSET(R[m]); \ + DECLARE_REG(offset_addr); \ + if (offset) { \ + ALLOC_REG(offset_addr); \ + ADDI(offset_addr, address, offset); \ + } else { \ + offset_addr = address; \ + } \ + SH2_LOAD_##size(dest, offset_addr); \ + } + +#define LOAD_Rm_inc(size,dest) \ + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF) { \ + SH2_LOAD_ABS_##size(dest, REG_GETVALUE(REG_R(m))); \ + ADDI_Rm_NOREG(INCDEC_##size); \ + REG_SETVALUE(REG_R(m), REG_GETVALUE(REG_R(m)) + INCDEC_##size); \ + } else { \ + SH2_LOAD_REG_##size(dest, m, 0, 1); \ + REG_SETKNOWN(REG_R(m), 0); \ + } + +#define LOAD_Rn(size,dest) \ + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { \ + SH2_LOAD_ABS_##size(dest, REG_GETVALUE(REG_R(n))); \ + } else { \ + SH2_LOAD_REG_##size(dest, n, 0, 0); \ + } + +#define LOAD_Rn_inc(size,dest) \ + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { \ + SH2_LOAD_ABS_##size(dest, REG_GETVALUE(REG_R(n))); \ + ADDI_Rn_NOREG(INCDEC_##size); \ + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) + INCDEC_##size); \ + } else { \ + SH2_LOAD_REG_##size(dest, n, 0, 1); \ + REG_SETKNOWN(REG_R(n), 0); \ + } + +#define LOAD_disp_GBR(size,dest,offset) \ + GET_GBR_KEEPOFS; \ + const int32_t final_offset = STATE_CACHE_OFFSET(GBR) + (offset); \ + DECLARE_REG(address); \ + if (final_offset) { \ + ALLOC_REG(address); \ + ADDI(address, GBR, final_offset); \ + } else { \ + address = GBR; \ + } \ + SH2_LOAD_##size(dest, address) + +#define LOAD_R0_GBR(size,dest) \ + DECLARE_REG(address); \ + GET_GBR_KEEPOFS; \ + if (REG_GETKNOWN(REG_R(0)) == 0xFFFFFFFF) { \ + const int32_t offset = STATE_CACHE_OFFSET(GBR) \ + + REG_GETVALUE(REG_R(0)); \ + if (offset) { \ + ALLOC_REG(address); \ + ADDI(address, GBR, offset); \ + } else { \ + address = GBR; \ + } \ + } else { \ + GET_R0_KEEPOFS; \ + DEFINE_REG(temp); \ + ADD(temp, R0, GBR); \ + const int32_t offset = STATE_CACHE_OFFSET(GBR) \ + + STATE_CACHE_OFFSET(R[0]); \ + if (offset) { \ + ALLOC_REG(address); \ + ADDI(address, temp, offset); \ + } else { \ + address = temp; \ + } \ + } \ + SH2_LOAD_##size(dest, address) + +/*----------------------------------*/ + +#define STORE_Rn(size,value) STORE_disp_Rn(size, value, 0) + +#define STORE_disp_Rn(size,value,offset) \ + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { \ + SH2_STORE_ABS_##size(REG_GETVALUE(REG_R(n)) + (offset), value, \ + PTR_ISLOCAL(n)); \ + } else { \ + SH2_STORE_REG_##size(n, value, offset, 0); \ + } + +#define STORE_dec_Rn(size,value) \ + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { \ + ADDI_Rn_NOREG(-INCDEC_##size); \ + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) - INCDEC_##size); \ + SH2_STORE_ABS_##size(REG_GETVALUE(REG_R(n)), value, \ + PTR_ISLOCAL(n)); \ + } else { \ + REG_SETKNOWN(REG_R(n), 0); \ + SH2_STORE_REG_##size(n, value, 0, 1); \ + } + +#define STORE_R0_Rn(size,value) \ + if (REG_GETKNOWN(REG_R(0)) == 0xFFFFFFFF \ + && REG_GETVALUE(REG_R(0)) + 0x8000 < 0x10000 \ + ) { \ + SH2_STORE_REG_##size(n, value, REG_GETVALUE(REG_R(0)), 0); \ + } else { \ + GET_R0_KEEPOFS; \ + GET_Rn_KEEPOFS; \ + DEFINE_REG(address); \ + ADD(address, R0, Rn); \ + const int32_t offset = STATE_CACHE_OFFSET(R[0]) \ + + STATE_CACHE_OFFSET(R[n]); \ + DECLARE_REG(offset_addr); \ + if (offset) { \ + ALLOC_REG(offset_addr); \ + ADDI(offset_addr, address, offset); \ + } else { \ + offset_addr = address; \ + } \ + SH2_STORE_##size(offset_addr, value); \ + } + +#define STORE_disp_Rm(size,value,offset) \ + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF) { \ + SH2_STORE_ABS_##size(REG_GETVALUE(REG_R(m)) + (offset), value, \ + PTR_ISLOCAL(n)); \ + } else { \ + SH2_STORE_REG_##size(m, value, offset, 0); \ + } + +#define STORE_disp_GBR(size,value,offset) \ + GET_GBR_KEEPOFS; \ + const int32_t final_offset = STATE_CACHE_OFFSET(GBR) + (offset); \ + DECLARE_REG(address); \ + if (final_offset) { \ + ALLOC_REG(address); \ + ADDI(address, GBR, final_offset); \ + } else { \ + address = GBR; \ + } \ + SH2_STORE_##size(address, value) + +/* @(R0,GBR) is only used in RMW instructions, so use the saved address */ +#define STORE_SAVED_R0_GBR(size,value) \ + SH2_STORE_##size(address, value) + +/*-----------------------------------------------------------------------*/ + +/* Local convenience macro to take a specified exception (immediate value), + * using the specified register as the return PC. */ + +#define TAKE_EXCEPTION(imm_index,PC_reg) do { \ + /* Push SR and PC */ \ + GET_R15_KEEPOFS; \ + GET_SR; \ + SH2_STORE_REG_L(15, SR, 0, 1); \ + SH2_STORE_REG_L(15, PC_reg, 0, 1); \ + if (REG_GETKNOWN(REG_R(15)) == 0xFFFFFFFF) { \ + REG_SETVALUE(REG_R(15), REG_GETVALUE(REG_R(15)) - 8); \ + } else { \ + REG_SETKNOWN(REG_R(15), 0); \ + } \ + /* Call the exception vector */ \ + DEFINE_REG(target); \ + SH2_LOAD_ABS_L(target, (imm_index) << 2); \ + SET_PC(target); \ + FLUSH_STATE_CACHE(); \ + JUMP(); \ +} while (0) + +/*-----------------------------------------------------------------------*/ + +#ifdef OPTIMIZE_SHIFT_SEQUENCES + +/* + * Local macro to retrieve the next opcode for use with shift optimization; + * the opcode is stored in the variable "next_opcode". If the next opcode + * in the instruction stream is a delayed branch which does not depend on + * the value of the register being shifted (Rn of the current instruction) + * or the T bit set by the shift (if a single-bit shift or rotate), then + * the instruction in the branch's delay slot is retrieved instead. If the + * next opcode cannot be retrieved, either because we've reached the end of + * the block or because we're interpreting, or if the instruction is not in + * directly-accessible memory, zero is stored in next_opcode. + */ + +# define GET_NEXT_OPCODE_FOR_SHIFT_CACHE \ + uint16_t next_opcode; \ + if (fetch && INSN_IS_AVAILABLE(1)) { \ + next_opcode = fetch[1]; \ + if (INSN_IS_AVAILABLE(2)) { \ + int use_delay = 0; \ + if ((next_opcode & 0xF0DF) == 0x0003 \ + || (next_opcode & 0xF0DF) == 0x400B) { \ + /* BSRF/BRAF Rn, JSR/JMP @Rn */ \ + use_delay = ((next_opcode>>8 & 0xF) != n); \ + } else if ((next_opcode & 0xFD00) == 0x8D00) { \ + /* BT/S, BF/S (allow only SHL[LR]#) */ \ + use_delay = ((opcode & 0xF00F) == 0x4008); \ + } else if ((next_opcode & 0xF0DF) == 0x000B \ + || (next_opcode & 0xE000) == 0xA000) { \ + /* RTS/RTE, BRA/BSR */ \ + use_delay = 1; \ + } \ + if (use_delay) { \ + next_opcode = fetch[2]; \ + } \ + } \ + } else { \ + next_opcode = 0; \ + } + +#endif // OPTIMIZE_SHIFT_SEQUENCES + +/*-----------------------------------------------------------------------*/ + +/* + * Debugging macro to print a line the first time an instruction is seen + * (enabled when DEBUG_DECODER_INSN_COVERAGE is defined by the including + * file). A full list of these can be generated with: + * + * grep -F 'DEBUG_PRINT_ONCE("' sh2-core.i | sed 's/^[^"]*"\([^"]*\).*$/\1/' + */ + +#ifdef DEBUG_DECODER_INSN_COVERAGE +# define DEBUG_PRINT_ONCE(insn) do { \ + static int seen = 0; \ + if (!seen) { \ + fprintf(stderr, "%s\n", insn); \ + seen = 1; \ + } \ +} while (0) +#else +# define DEBUG_PRINT_ONCE(insn) /*nothing*/ +#endif + +/*************************************************************************/ + +#ifndef DECODE_INSN_INLINE +# define DECODE_INSN_INLINE /*nothing*/ +#endif + +/** + * decode_insn: Decode a single SH-2 instruction. Implements + * translate_insn() using the shared decoder core. + * + * [Parameters] + * Defined by including file + * [Return value] + * Decoded SH-2 opcode, or value returned by micro-ops + */ +static DECODE_INSN_INLINE unsigned int decode_insn(DECODE_INSN_PARAMS) +{ + uint16_t opcode; + + /* Load the opcode to decode */ + + if (fetch) { + opcode = *fetch; + } else { + opcode = MappedMemoryReadWord(cur_PC); + } + + /* Perform any early processing required */ + + OPCODE_INIT(opcode); + + /* If tracing, trace the instruction (do this after OPCODE_INIT() so + * the JIT branch target label gets added before the trace call) */ + +#ifdef TRACE + DEFINE_REG(trace_PC); + MOVEI(trace_PC, cur_PC); + DEFINE_REG(trace_funcptr); + MOVEA(trace_funcptr, trace_insn_callback); + CALL_NORET(state_reg, trace_PC, trace_funcptr); +#endif + + /* Save and clear the delay flag (this needs to be done here, not at + * the end of the previous iteration, so (1) the translator knows not + * to interrupt the pair of instructions and (2) the delay flag can be + * accessed by OPCODE_INIT() if needed) */ + + const int in_delay = state->delay; + state->delay = 0; + + /* Clear the just-branched flag on every instruction, so + * fall-off-the-end is detected properly; we'll set it later if we + * branch after this instruction */ + + state->just_branched = 0; + + /* Record the number of cycles used by this instruction (any additional + * cycles necessary are added by the individual opcode handlers) */ + + int cur_cycles = 1; + + /**** The Big Honkin' Opcode Switch Begins Here ****/ + + /* Extract these early for convenience */ + const unsigned int n = opcode>>8 & 0xF; + const unsigned int m = opcode>>4 & 0xF; + + /* Note: Mnemonics listed here for some instructions differ slightly + * from the official Hitachi specs. Here, register Rn _always_ refers + * to the register specified by bits 8-11 of the opcode, and register + * Rm _always_ refers to the register specified by bits 4-7 of the + * opcode, regardless of the register's role (source or destination). */ + + switch (opcode>>12 & 0xF) { + + case 0x0: { + switch (opcode & 0xFF) { + + // $0xx2 + case 0x02: { // STC SR,Rn + DEBUG_PRINT_ONCE("STC SR,Rn"); + COPY_TO_Rn(SR); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + case 0x12: { // STC GBR,Rn + DEBUG_PRINT_ONCE("STC GBR,Rn"); + COPY_TO_Rn(GBR); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_GBR)); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_GBR)); + PTR_CLEAR(n); + break; + } + case 0x22: { // STC VBR,Rn + DEBUG_PRINT_ONCE("STC VBR,Rn"); + COPY_TO_Rn(VBR); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xx3 + case 0x03: { // BSRF Rn + DEBUG_PRINT_ONCE("BSRF Rn"); + DEFINE_RESULT_REG(ret_addr, PR); + MOVEI(ret_addr, cur_PC + 4); + SET_PR(ret_addr); + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = (cur_PC + 4) + REG_GETVALUE(REG_R(n)); + } else { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_Rn; + DEFINE_REG(target); + ADD(target, ret_addr, Rn); + state->branch_target_reg = target; + } + state->delay = 1; + cur_cycles += 1; + break; + } + case 0x23: { // BRAF Rn + DEBUG_PRINT_ONCE("BRAF Rn"); +#ifdef OPTIMIZE_VARIABLE_SHIFTS + if (fetch) { + unsigned int Rshift, count_max, Rcount, type; + const unsigned int num_insns = can_optimize_variable_shift( + fetch, cur_PC, &Rcount, &count_max, &Rshift, &type, NULL + ); + if (num_insns) { + state->varshift_target_PC = cur_PC + 2*num_insns; + state->varshift_type = type; + state->varshift_Rcount = Rcount; + state->varshift_max = count_max; + state->varshift_Rshift = Rshift; + } + } +#endif + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = (cur_PC + 4) + REG_GETVALUE(REG_R(n)); + } else { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_Rn; + DEFINE_REG(target); + ADDI(target, Rn, cur_PC + 4); + state->branch_target_reg = target; + } + state->delay = 1; + cur_cycles += 1; + break; + } + + // $0xx4 + case 0x04: case 0x14: case 0x24: case 0x34: + case 0x44: case 0x54: case 0x64: case 0x74: + case 0x84: case 0x94: case 0xA4: case 0xB4: + case 0xC4: case 0xD4: case 0xE4: case 0xF4: { // MOV.B Rm,@(R0,Rn) + DEBUG_PRINT_ONCE("MOV.B Rm,@(R0,Rn)"); + GET_Rm_B; + STORE_R0_Rn(B, Rm); + break; + } + + // $0xx5 + case 0x05: case 0x15: case 0x25: case 0x35: + case 0x45: case 0x55: case 0x65: case 0x75: + case 0x85: case 0x95: case 0xA5: case 0xB5: + case 0xC5: case 0xD5: case 0xE5: case 0xF5: { // MOV.W Rm,@(R0,Rn) + DEBUG_PRINT_ONCE("MOV.W Rm,@(R0,Rn)"); + GET_Rm_W; + STORE_R0_Rn(W, Rm); + break; + } + + // $0xx6 + case 0x06: case 0x16: case 0x26: case 0x36: + case 0x46: case 0x56: case 0x66: case 0x76: + case 0x86: case 0x96: case 0xA6: case 0xB6: + case 0xC6: case 0xD6: case 0xE6: case 0xF6: { // MOV.L Rm,@(R0,Rn) + DEBUG_PRINT_ONCE("MOV.L Rm,@(R0,Rn)"); + GET_Rm; + STORE_R0_Rn(L, Rm); + break; + } + + // $0xx7 + case 0x07: case 0x17: case 0x27: case 0x37: + case 0x47: case 0x57: case 0x67: case 0x77: + case 0x87: case 0x97: case 0xA7: case 0xB7: + case 0xC7: case 0xD7: case 0xE7: case 0xF7: { // MUL.L Rm,Rn + DEBUG_PRINT_ONCE("MUL.L Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, MACL); + MUL(result, Rn, Rm); + SET_MACL(result); + CLEAR_MAC_IS_ZERO(); + cur_cycles += 1; // Minimum number of cycles + break; + } + + // $0xx8 + case 0x08: { // CLRT + DEBUG_PRINT_ONCE("CLRT"); + DEFINE_REG(zero); + MOVEI(zero, 0); + SET_SR_T(zero); + break; + } + case 0x18: { // SETT + DEBUG_PRINT_ONCE("SETT"); + DEFINE_REG(one); + MOVEI(one, 1); + SET_SR_T(one); + break; + } + case 0x28: { // CLRMAC + DEBUG_PRINT_ONCE("CLRMAC"); + DEFINE_RESULT_REG(MACH, MACH); + MOVEI(MACH, 0); + SET_MACH(MACH); + DEFINE_RESULT_REG(MACL, MACL); + MOVEI(MACL, 0); + SET_MACL(MACL); + SET_MAC_IS_ZERO(); + break; + } + + // $0xx9 + case 0x09: { // NOP + DEBUG_PRINT_ONCE("NOP"); + /* No operation */ + break; + } + case 0x19: { // DIV0U + DEBUG_PRINT_ONCE("DIV0U"); + GET_SR; + DEFINE_RESULT_REG(new_SR, SR); + ANDI(new_SR, SR, ~(SR_T | SR_Q | SR_M)); + SET_SR(new_SR); +#ifdef OPTIMIZE_DIVISION + int Rlo = -1, Rhi = -1, Rdiv = -1; // Register numbers + int div_bits = 0; // Number of bits of division to perform + int skip_first_rotcl = 0; // Is the 1st ROTCL skipped? (see below) + /* Don't try to optimize if the DIV0U is sitting in a + * branch delay slot; also, only optimize when we can do + * direct fetching (otherwise our read-aheads might have side + * effects). */ + if (LIKELY(!in_delay) && fetch) { + if ((fetch[1] & 0xF00F) == 0x3004 + && (fetch[2] & 0xF0FF) == 0x4024 + ) { + /* If the value of Rlo is zero, the first ROTCL may be + * omitted (since neither Rlo or SR.T would change). */ +#ifdef __GNUC__ // Avoid a warning if known value optimization is disabled + __attribute__((unused)) +#endif + const int rotcl_Rn = fetch[2]>>8 & 0xF; + if (REG_GETKNOWN(REG_R(rotcl_Rn)) == 0xFFFFFFFF + && REG_GETVALUE(REG_R(rotcl_Rn)) == 0 + ) { + skip_first_rotcl = 1; + } + } + div_bits = can_optimize_div0u(fetch, cur_PC, skip_first_rotcl, + &Rhi, &Rlo, &Rdiv); + } + if (div_bits > 0) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("Optimizing unsigned division at 0x%08X", (int)cur_PC); +# endif + /* Set up optimization flags/data */ + const int is_32bit = (REG_GETKNOWN(Rhi) == 0xFFFFFFFF + && REG_GETVALUE(Rhi) == 0); + const int safe_division = + (optimization_flags & SH2_OPTIMIZE_ASSUME_SAFE_DIVISION); + state->division_target_PC = cur_PC + div_bits*4; + if (!safe_division) { + /* Flush cached data since we jump out from inside the + * runtime conditional. */ + ADD_CYCLES(); + FLUSH_STATE_CACHE(); + } else { + /* No need to flush data because we omit the + * unoptimized code entirely. */ + } + /* Load operands */ + DECLARE_REG(lo); + LOAD_STATE_ALLOC(lo, R[Rlo]); + DECLARE_REG(div); + LOAD_STATE_ALLOC(div, R[Rdiv]); + DECLARE_REG(hi); + LOAD_STATE_ALLOC(hi, R[Rhi]); + /* Define output registers (shared across all cases) */ + DEFINE_REG(quotient); + DEFINE_REG(remainder); + /* If the divide is less than 32 bits, save the low bits of + * the dividend. We don't shift the dividend right yet + * because we have to check Rhi first; the divide will + * still misbehave if Rhi >= div. */ + DECLARE_REG(lo_lowbits); + if (div_bits < 32) { + ALLOC_REG(lo_lowbits); + SLLI(lo_lowbits, lo, 32 - div_bits); + } else { // Not needed, but avoid a compiler warning + lo_lowbits = 0; + } + /* Allocate labels for use in non-optimized cases */ + CREATE_LABEL(div0u_not_safe); + CREATE_LABEL(div0u_not_32bit); + CREATE_LABEL(div0u_done); + if (!safe_division) { + GOTO_IF_Z(div0u_not_safe, div); + } + if (!is_32bit) { + GOTO_IF_NZ(div0u_not_32bit, hi); + } + /* Divide 32 bits by 32 bits, unsigned */ + if (div_bits < 32) { + /* Note: SSA violation */ + SRLI(lo, lo, 32 - div_bits); + } + DIVMODU(quotient, lo, div, remainder); + if (!is_32bit) { + GOTO_LABEL(div0u_done); + DEFINE_LABEL(div0u_not_32bit); + if (!safe_division) { + DEFINE_REG(test_safe); + SLTU(test_safe, hi, div); + GOTO_IF_Z(div0u_not_safe, test_safe); + } + if (div_bits < 32) { + /* Note: SSA violations */ + SRLI(lo, lo, 32 - div_bits); + BFINS(lo, lo, hi, div_bits, 32 - div_bits); + SRLI(hi, hi, 32 - div_bits); + } + /* + * Divide 64 bits by 32 bits, unsigned, when the + * quotient is known to fit in 32 bits. + * + * Here, we use the algorithm followed by GCC (among + * others), which is essentially to perform long + * division in base 2^16 with a normalized divisor, + * i.e. a divisor with the highest bit set. We divide + * the two-"digit" divisor into the upper three + * "digits" of the dividend, then append the final + * "digit" to the remainder and divide again. Since + * operands to a division instruction can only be 32 + * bits, i.e. 2 "digits", we process the divisor one + * "digit" at a time. + * + * To demonstrate, this is how it would work if a word + * consisted of two decimal digits (i.e. 0-99): + * _____ + * 59)5023 + * -50 Step 1: 50/5 = 10 (q1), 10*5 = 50 + * ---- + * 2 Step 2: Carry down the next digit + * Step 3: Is the remainder less than 10*9? + * +59 Step 3A: Yes, so add 59 and decrement q1 + * ---- (q1=9) + * 61 Step 3B: Is the remainder still less than 10*9? + * +59 Step 3C: Yes, so add 59 and decrement q1 again + * ---- (q1=8) (since the divisor is normalized, + * 120 we will never need to add it more + * than twice to avoid borrowing) + * -90 Step 4: Subtract 10*9 + * ---- + * 30 (Repeat steps 1-4) + * -30 Step 5: 30/5 = 6 (q0), 6*5 = 30 + * ---- + * 3 Step 6: Carry down the next (last) digit + * Step 7: Is the remainder less than 6*9? + * +59 Step 7A: Yes, so add 59 and decrement q0 + * ---- (q0=5) + * 62 Step 7B: Is the remainder still less than 6*9? + * Step 7C: No, so skip the add and decrement + * -54 Step 8: Subtract 6*9 + * ---- + * 8 Result: quotient 85, remainder 8 + */ + /* Normalize the divisor and dividend */ + DEFINE_REG(norm_shift); + CLZ(norm_shift, div); + DEFINE_REG(hi_norm_temp1); + SLL(hi_norm_temp1, hi, norm_shift); + DEFINE_REG(imm_32); + MOVEI(imm_32, 32); + DEFINE_REG(norm_invshift); + SUB(norm_invshift, imm_32, norm_shift); + DEFINE_REG(hi_norm_temp2); + SRL(hi_norm_temp2, lo, norm_invshift); + DEFINE_REG(hi_norm_temp3); + OR(hi_norm_temp3, hi_norm_temp1, hi_norm_temp2); + /* Need to SELECT here in case norm_shift==0, because + * shifting by 32 bits (norm_invshift) is undefined */ + DEFINE_REG(hi_norm); + SELECT(hi_norm, hi_norm_temp3, hi, norm_shift); + DEFINE_REG(lo_norm); + SLL(lo_norm, lo, norm_shift); + DEFINE_REG(div_norm); + SLL(div_norm, div, norm_shift); + /* Extract the upper and lower halfwords of the divisor */ + DEFINE_REG(div1); + SRLI(div1, div_norm, 16); + DEFINE_REG(div0); + ANDI(div0, div_norm, 0xFFFF); + /* Divide the upper 48 bits (steps 1-4) */ + DEFINE_REG(q1); + DEFINE_REG(mid_norm); + DIVMODU(q1, hi_norm, div1, mid_norm); // Step 1 + SLLI(mid_norm, mid_norm, 16); + DEFINE_REG(mid_norm_temp); + SRLI(mid_norm_temp, lo_norm, 16); + OR(mid_norm, mid_norm, mid_norm_temp); // Step 2 + DEFINE_REG(q1_div0); + MUL(q1_div0, q1, div0); + CREATE_LABEL(div0u_mid_done); + DEFINE_REG(mid_test1); + SLTU(mid_test1, mid_norm, q1_div0); // Step 3 + GOTO_IF_Z(div0u_mid_done, mid_test1); { + ADD(mid_norm, mid_norm, div_norm); // Step 3A + SUBI(q1, q1, 1); + /* If mid_norm < div_norm, then we overflowed and + * have a virtual 1 in bit position 32, so mid_norm + * must be greater than q1_div0 */ + DEFINE_REG(mid_test2a); + SLTU(mid_test2a, mid_norm, div_norm); // Step 3B + DEFINE_REG(mid_test2b); + SLTU(mid_test2b, mid_norm, q1_div0); + GOTO_IF_NZ(div0u_mid_done, mid_test2a); + GOTO_IF_Z(div0u_mid_done, mid_test2b); { + ADD(mid_norm, mid_norm, div_norm); // Step 3C + SUBI(q1, q1, 1); + } + } DEFINE_LABEL(div0u_mid_done); + SUB(mid_norm, mid_norm, q1_div0); // Step 4 + /* Divide the last 16 bits (steps 5-8) */ + DEFINE_REG(q0); + DEFINE_REG(rem_norm); + DIVMODU(q0, mid_norm, div1, rem_norm); // Step 5 + SLLI(rem_norm, rem_norm, 16); + DEFINE_REG(rem_norm_temp); + ANDI(rem_norm_temp, lo_norm, 0xFFFF); + OR(rem_norm, rem_norm, rem_norm_temp); // Step 6 + DEFINE_REG(q0_div0); + MUL(q0_div0, q0, div0); + CREATE_LABEL(div0u_lo_done); + DEFINE_REG(lo_test1); + SLTU(lo_test1, rem_norm, q0_div0); // Step 7 + GOTO_IF_Z(div0u_lo_done, lo_test1); { + ADD(rem_norm, rem_norm, div_norm); // Step 7A + SUBI(q0, q0, 1); + DEFINE_REG(lo_test2a); + SLTU(lo_test2a, rem_norm, div_norm); // Step 7B + DEFINE_REG(lo_test2b); + SLTU(lo_test2b, rem_norm, q0_div0); + GOTO_IF_NZ(div0u_lo_done, lo_test2a); + GOTO_IF_Z(div0u_lo_done, lo_test2b); { + ADD(rem_norm, rem_norm, div_norm); // Step 7C + SUBI(q0, q0, 1); + } + } DEFINE_LABEL(div0u_lo_done); + SUB(rem_norm, rem_norm, q0_div0); // Step 8 + /* Merge the two quotient halves */ + BFINS(quotient, q0, q1, 16, 16); + /* Un-normalize the remainder */ + SRL(remainder, rem_norm, norm_shift); + DEFINE_LABEL(div0u_done); + } // if (!is_32bit) + /* Process the division result */ + DEFINE_REG(new_T); + ANDI(new_T, quotient, 1); // Low bit to T flag + DEFINE_RESULT_REG(quo_out, R[Rlo]); + if (div_bits < 32) { + DEFINE_REG(quo_temp); + SRLI(quo_temp, quotient, 1); + OR(quo_out, quo_temp, lo_lowbits); + } else { + SRLI(quo_out, quotient, 1); + } + STORE_STATE(R[Rlo], quo_out); + REG_SETKNOWN(REG_R(Rlo), 0); + DEFINE_REG(temp0); + SUB(temp0, remainder, div); + DEFINE_REG(rem_out); + SELECT(rem_out, remainder, temp0, new_T); + STORE_STATE(R[Rhi], rem_out); + REG_SETKNOWN(REG_R(Rhi), 0); + DEFINE_REG(SR_temp); + OR(SR_temp, new_SR, new_T); + DEFINE_REG(new_Q); + XORI(new_Q, new_T, 1); + DEFINE_RESULT_REG(SR_out, SR); + BFINS(SR_out, SR_temp, new_Q, SR_Q_SHIFT, 1); + SET_SR(SR_out); + /* Skip over the step-by-step emulation */ + const unsigned int skipped_insns = + div_bits*2 - (skip_first_rotcl ? 1 : 0); + cur_cycles += skipped_insns; + if (!safe_division) { + ADD_CYCLES(); + SET_PC_KNOWN(cur_PC + (2 + skipped_insns*2)); + state->branch_target = cur_PC + (2 + skipped_insns*2); + FLUSH_STATE_CACHE(); + JUMP_STATIC(); + DEFINE_LABEL(div0u_not_safe); + } else { + INC_PC_BY(skipped_insns*2); + } + } // if (div_bits > 0) +#endif // OPTIMIZE_DIVISION + break; + } // DIV0U + case 0x29: { // MOVT Rn + DEBUG_PRINT_ONCE("MOVT Rn"); + GET_SR_T; + SET_Rn(T); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFE); + REG_SETVALUE(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xxA + case 0x0A: { // STS MACH,Rn + DEBUG_PRINT_ONCE("STS MACH,Rn"); + if (STATE_CACHE_FIXED_REG_WRITABLE(R[n])) { + COPY_TO_Rn(MACH); + } else { + /* Make sure Rn gets a copy of the register so it's not + * altered by subsequent MAC instructions */ + GET_MACH_COPY; + SET_Rn(MACH); + } + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + case 0x1A: { // STS MACL,Rn + DEBUG_PRINT_ONCE("STS MACL,Rn"); + if (STATE_CACHE_FIXED_REG_WRITABLE(R[n])) { + COPY_TO_Rn(MACL); + } else { + GET_MACL_COPY; + SET_Rn(MACL); + } + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + case 0x2A: { // STS PR,Rn + DEBUG_PRINT_ONCE("STS PR,Rn"); + COPY_TO_Rn(PR); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xxB + case 0x0B: { // RTS + DEBUG_PRINT_ONCE("RTS"); + /* We don't do anything if this is an RTS from a folded + * subroutine, since that's handled by our caller. */ + if (!state->folding_subroutine) { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_PR; + state->branch_target_reg = PR; + state->delay = 1; + } + cur_cycles += 1; + break; + } + case 0x1B: { // SLEEP + DEBUG_PRINT_ONCE("SLEEP"); + cur_cycles += 2; + ADD_CYCLES(); + FLUSH_STATE_CACHE(); + DEFINE_REG(check_interrupts_funcptr); + MOVEA(check_interrupts_funcptr, check_interrupts); + DEFINE_REG(result); + CALL(result, state_reg, 0, check_interrupts_funcptr); + CREATE_LABEL(sleep_intr); + GOTO_IF_NZ(sleep_intr, result); { + DEFINE_REG(imm_1); + MOVEI(imm_1, 1); + STORE_STATE_B(asleep, imm_1); + DEFINE_REG(cycle_limit); + LOAD_STATE(cycle_limit, cycle_limit); + STORE_STATE(cycles, cycle_limit); + } DEFINE_LABEL(sleep_intr); + RETURN(); + break; + } + case 0x2B: { // RTE + DEBUG_PRINT_ONCE("RTE"); + GET_R15_KEEPOFS; + DEFINE_REG(new_PC); + SH2_LOAD_REG_L(new_PC, 15, 0, 1); + DEFINE_REG(new_SR); + SH2_LOAD_REG_L(new_SR, 15, 0, 1); + if (REG_GETKNOWN(REG_R(15)) == 0xFFFFFFFF) { + REG_SETVALUE(REG_R(15), REG_GETVALUE(REG_R(15)) + 8); + } else { + REG_SETKNOWN(REG_R(15), 0); + } + REG_SETKNOWN(REG_R(15), REG_GETKNOWN(REG_R(15)) & 7); + state->branch_type = SH2BRTYPE_RTE; + state->branch_target_reg = new_PC; + DEFINE_RESULT_REG(new_SR_3F3, SR); + ANDI(new_SR_3F3, new_SR, 0x3F3); + SET_SR(new_SR_3F3); + DEFINE_REG(imm_1); + MOVEI(imm_1, 1); + state->delay = 1; + cur_cycles += 3; + break; + } + + // $0xxC + case 0x0C: case 0x1C: case 0x2C: case 0x3C: + case 0x4C: case 0x5C: case 0x6C: case 0x7C: + case 0x8C: case 0x9C: case 0xAC: case 0xBC: + case 0xCC: case 0xDC: case 0xEC: case 0xFC: { // MOV.B @(R0,Rm),Rn + DEBUG_PRINT_ONCE("MOV.B @(R0,Rm),Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_R0_Rm(B, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xxD + case 0x0D: case 0x1D: case 0x2D: case 0x3D: + case 0x4D: case 0x5D: case 0x6D: case 0x7D: + case 0x8D: case 0x9D: case 0xAD: case 0xBD: + case 0xCD: case 0xDD: case 0xED: case 0xFD: { // MOV.W @(R0,Rm),Rn + DEBUG_PRINT_ONCE("MOV.W @(R0,Rm),Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_R0_Rm(W, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xxE + case 0x0E: case 0x1E: case 0x2E: case 0x3E: + case 0x4E: case 0x5E: case 0x6E: case 0x7E: + case 0x8E: case 0x9E: case 0xAE: case 0xBE: + case 0xCE: case 0xDE: case 0xEE: case 0xFE: { // MOV.L @(R0,Rm),Rn + DEBUG_PRINT_ONCE("MOV.L @(R0,Rm),Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_R0_Rm(L, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + // $0xxF + case 0x0F: case 0x1F: case 0x2F: case 0x3F: + case 0x4F: case 0x5F: case 0x6F: case 0x7F: + case 0x8F: case 0x9F: case 0xAF: case 0xBF: + case 0xCF: case 0xDF: case 0xEF: case 0xFF: { // MAC.L @Rm+,@Rn+ + DEBUG_PRINT_ONCE("MAC.L @Rm+,@Rn+"); + + /* Load the values */ + DEFINE_REG(value_m); + LOAD_Rm_inc(L, value_m); + DEFINE_REG(value_n); + LOAD_Rn_inc(L, value_n); + + /* Do the actual multiplication and addition */ + if (MAC_IS_ZERO()) { + DEFINE_RESULT_REG(new_MACL, MACL); + DEFINE_RESULT_REG(new_MACH, MACH); + MULS_64(new_MACL, value_m, value_n, new_MACH); + SET_MACL(new_MACL); + SET_MACH(new_MACH); + } else { + GET_MACL; + GET_MACH; + MADDS_64(MACL, value_m, value_n, MACH); + SET_MACL(MACL); + SET_MACH(MACH); + } + + /* Perform saturation if the S flag of SR is set */ + if (!CAN_OMIT_MAC_S_CHECK) { + GET_SR; + DEFINE_REG(saturate); + ANDI(saturate, SR, SR_S); + CREATE_LABEL(macl_nosat); + GOTO_IF_Z(macl_nosat, saturate); { + GET_MACL; + GET_MACH; + DEFINE_REG(saturate_minus); + SLTSI(saturate_minus, MACH, -0x7FFF); + CREATE_LABEL(macl_sat_nominus); + GOTO_IF_Z(macl_sat_nominus, saturate_minus); { + DEFINE_REG(temp); + MOVEI(temp, -0x8000); + /* Note: SSA violations for state block optimization */ + MOVE(MACH, temp); + SET_MACH(MACH); + MOVEI(MACL, 0); + SET_MACL(MACL); + GOTO_LABEL(macl_nosat); + } DEFINE_LABEL(macl_sat_nominus); + DEFINE_REG(satplus_test); + SUBI(satplus_test, MACH, 0x8000); + DEFINE_REG(inv_saturate_plus); // Inverse value + SLTSI(inv_saturate_plus, satplus_test, 0); + GOTO_IF_NZ(macl_nosat, inv_saturate_plus); { + DEFINE_REG(temp1); + MOVEI(temp1, 0x7FFF); + /* Note: SSA violations for state block optimization */ + MOVE(MACH, temp1); + SET_MACH(MACH); + DEFINE_REG(temp2); + MOVEI(temp2, 0xFFFFFFFF); + MOVE(MACL, temp2); + SET_MACL(MACL); + } + } DEFINE_LABEL(macl_nosat); + } // if (!CAN_OMIT_MAC_S_CHECK) + + CLEAR_MAC_IS_ZERO(); + cur_cycles += 2; + break; + } // MAC.L + + default: + goto invalid; + } + break; + } // $0xxx + + case 0x1: { // MOV.L Rm,@(disp,Rn) + DEBUG_PRINT_ONCE("MOV.L Rm,@(disp,Rn)"); + const int disp = (opcode & 0xF) * 4; + GET_Rm; + STORE_disp_Rn(L, Rm, disp); + break; + } // $1xxx + + case 0x2: { + switch (opcode & 0xF) { + + case 0x0: { // MOV.B Rm,@Rn + DEBUG_PRINT_ONCE("MOV.B Rm,@Rn"); + GET_Rm_B; + STORE_Rn(B, Rm); + break; + } + + case 0x1: { // MOV.W Rm,@Rn + DEBUG_PRINT_ONCE("MOV.W Rm,@Rn"); + GET_Rm_W; + STORE_Rn(W, Rm); + break; + } + + case 0x2: { // MOV.L Rm,@Rn + DEBUG_PRINT_ONCE("MOV.L Rm,@Rn"); + GET_Rm; + STORE_Rn(L, Rm); + break; + } + + case 0x4: { // MOV.B Rm,@-Rn + DEBUG_PRINT_ONCE("MOV.B Rm,@-Rn"); + GET_Rm_B; + STORE_dec_Rn(B, Rm); + break; + } + + case 0x5: { // MOV.W Rm,@-Rn + DEBUG_PRINT_ONCE("MOV.W Rm,@-Rn"); + GET_Rm_W; + STORE_dec_Rn(W, Rm); + break; + } + + case 0x6: { // MOV.L Rm,@-Rn + DEBUG_PRINT_ONCE("MOV.L Rm,@-Rn"); + GET_Rm; + STORE_dec_Rn(L, Rm); + break; + } + + case 0x7: { // DIV0S Rm,Rn + DEBUG_PRINT_ONCE("DIV0S Rm,Rn"); + GET_Rm; + DEFINE_REG(new_M); + SRLI(new_M, Rm, 31); + GET_SR; + DEFINE_REG(SR_withM); + BFINS(SR_withM, SR, new_M, SR_M_SHIFT, 1); + GET_Rn; + DEFINE_REG(new_Q); + SRLI(new_Q, Rn, 31); + DEFINE_REG(SR_withQ); + BFINS(SR_withQ, SR_withM, new_Q, SR_Q_SHIFT, 1); + DEFINE_REG(new_T); + XOR(new_T, new_M, new_Q); + DEFINE_RESULT_REG(new_SR, SR); + BFINS(new_SR, SR_withQ, new_T, SR_T_SHIFT, 1); + SET_SR(new_SR); +#ifdef OPTIMIZE_DIVISION + int Rlo, Rhi = n, Rdiv = m; // Register numbers + const int can_optimize = !in_delay && fetch + && can_optimize_div0s(fetch+1, cur_PC+2, Rhi, &Rlo, Rdiv); + if (can_optimize) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("Optimizing signed division at 0x%08X", (int)cur_PC); +# endif + const int safe_division = + (optimization_flags & SH2_OPTIMIZE_ASSUME_SAFE_DIVISION); + state->division_target_PC = cur_PC + 128; + if (!safe_division) { + /* Flush cached data since we jump out from inside the + * runtime conditional */ + ADD_CYCLES(); + FLUSH_STATE_CACHE(); + } else { + /* No need to flush data because we omit the + * unoptimzied code entirely */ + } + /* Load operands */ + DECLARE_REG(lo); + LOAD_STATE_ALLOC(lo, R[Rlo]); + DECLARE_REG(div); + LOAD_STATE_ALLOC(div, R[Rdiv]); + DECLARE_REG(hi); + LOAD_STATE_ALLOC(hi, R[Rhi]); + /* Define output registers (shared across all cases) */ + DEFINE_REG(quotient); + DEFINE_REG(remainder); + /* Allocate a label for use in non-optimized cases */ + CREATE_LABEL(div0s_not_safe); + if (!safe_division) { + /* Make sure the divisor is nonzero (we don't handle + * division by zero here because we'd have to carry lo + * down too far) */ + GOTO_IF_Z(div0s_not_safe, div); + /* Check that the 64-bit value is within the range of + * a signed 32-bit integer. The Hitachi docs suggest + * that 64/32 (or 32/16) signed division isn't + * supported, so don't bother trying to optimize that + * case. Besides, 64/32 division is a pain when you + * only have 32-bit registers (see DIV0U). */ + DEFINE_REG(test_safe); + SRAI(test_safe, lo, 31); // Either 0 or -1 + GOTO_IF_NE(div0s_not_safe, hi, test_safe); + } + /* Divide 32 bits by 32 bits, unsigned */ + DIVMODS(quotient, lo, div, remainder); + /* Process the division result depending on the dividend sign*/ + DEFINE_REG(M_bit); + SRLI(M_bit, div, 31); // Get divisor sign bit (M) + DEFINE_REG(lo_sign); + SRLI(lo_sign, lo, 31); // Get dividend sign bit + /* Note: SSA violations for state block optimization */ + DEFINE_RESULT_REG(quo_out, R[Rlo]); + DEFINE_RESULT_REG(rem_out, R[Rhi]); + DEFINE_RESULT_REG(final_SR, SR); + CREATE_LABEL(div0s_final); + CREATE_LABEL(div0s_neg_dividend); + GOTO_IF_NZ(div0s_neg_dividend, lo_sign); { + /* Positive dividend */ + DEFINE_REG(quo_adj); + SUB(quo_adj, quotient, M_bit); // Adjust quotient + DEFINE_REG(new_T2); + ANDI(new_T2, quo_adj, 1); // Get value of T + DEFINE_REG(new_SR_T); + BFINS(new_SR_T, new_SR, new_T2, SR_T_SHIFT, 1); + SRAI(quo_out, quo_adj, 1); // Set quotient>>1 + DEFINE_REG(temp_Q2); + XOR(temp_Q2, new_T2, M_bit); // Get value of Q + DEFINE_REG(new_Q2); + XORI(new_Q2, temp_Q2, 1); + BFINS(final_SR, new_SR_T, new_Q2, SR_Q_SHIFT, 1); + SET_SR(final_SR); + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_REG(neg_div); + SUB(neg_div, zero, div); // Get abs(divisor) + DEFINE_REG(abs_div); // (remember that M bit is + SELECT(abs_div, neg_div, div, M_bit); // divisor sign) + DEFINE_REG(rem_adj); + SUB(rem_adj, remainder, abs_div); // Adjust remainder + SELECT(rem_out, rem_adj, remainder, new_Q2); + GOTO_LABEL(div0s_final); + } DEFINE_LABEL(div0s_neg_dividend); { + /* Negative dividend */ + DEFINE_REG(M_adjtemp); + SLLI(M_adjtemp, M_bit, 1); // Adjust quotient + DEFINE_REG(M_adjtemp2); + SUBI(M_adjtemp2, M_adjtemp, 1); + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_REG(M_adj); + SELECT(M_adj, M_adjtemp2, zero, remainder); + DEFINE_REG(qtemp); + ADD(qtemp, quotient, M_adj); + DEFINE_REG(quo_adj); + SUB(quo_adj, qtemp, M_bit); + DEFINE_REG(new_T2); + ANDI(new_T2, quo_adj, 1); // Get value of T + DEFINE_REG(new_SR_T); + BFINS(new_SR_T, new_SR, new_T2, SR_T_SHIFT, 1); + SRAI(quo_out, quo_adj, 1); // Set quotient>>1 + DEFINE_REG(temp_Q2); + XOR(temp_Q2, new_T2, M_bit); // Get value of Q + DEFINE_REG(new_Q2); + XORI(new_Q2, temp_Q2, 1); + BFINS(final_SR, new_SR_T, new_Q2, SR_Q_SHIFT, 1); + SET_SR(final_SR); + DEFINE_REG(neg_div); + SUB(neg_div, zero, div); // Get abs(divisor) + DEFINE_REG(abs_div); + SELECT(abs_div, neg_div, div, M_bit); + DEFINE_REG(rem_Q_temp); + SUB(rem_Q_temp, remainder, abs_div); + DEFINE_REG(rem_Q); // Remainder if Q + SELECT(rem_Q, remainder, rem_Q_temp, remainder); + DEFINE_REG(rem_nQ_temp); + ADD(rem_nQ_temp, remainder, abs_div); + DEFINE_REG(rem_nQ); // Remainder if !Q + SELECT(rem_nQ, rem_nQ_temp, remainder, remainder); + SELECT(rem_out, rem_Q, rem_nQ, new_Q2); + } DEFINE_LABEL(div0s_final); + STORE_STATE(R[Rlo], quo_out); + REG_SETKNOWN(REG_R(Rlo), 0); + STORE_STATE(R[Rhi], rem_out); + REG_SETKNOWN(REG_R(Rhi), 0); + /* Skip over the step-by-step emulation */ + cur_cycles += 64; // 1 cycle was already added + if (!safe_division) { + ADD_CYCLES(); + SET_PC_KNOWN(cur_PC + 130); + state->branch_target = cur_PC + 130; + FLUSH_STATE_CACHE(); + JUMP_STATIC(); + DEFINE_LABEL(div0s_not_safe); + } else { + INC_PC_BY(128); // Incremented by another 2 below + } + } // if (can_optimize) +#endif // OPTIMIZE_DIVISION + break; + } // DIV0S + + case 0x8: { // TST Rm,Rn + DEBUG_PRINT_ONCE("TST Rm,Rn"); + GET_Rn; + /* TST Rn,Rn is a common idiom for checking the zeroness of Rn */ + DEFINE_REG(new_T); + if (m == n) { + SEQZ(new_T, Rn); + } else { + GET_Rm; + DEFINE_REG(result); + AND(result, Rn, Rm); + SEQZ(new_T, result); + } + SET_SR_T(new_T); + break; + } + + case 0x9: { // AND Rm,Rn + DEBUG_PRINT_ONCE("AND Rm,Rn"); + if (m == n) { + break; // AND Rn,Rn is a no-op + } + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + AND(result, Rn, Rm); + SET_Rn(result); + /* A bit in the result of an AND operation is known iff (1) the + * bit is known in both operands or (2) the bit is known to be + * 0 in one operand. */ + REG_SETKNOWN(REG_R(n), + (REG_GETKNOWN(REG_R(m)) & REG_GETKNOWN(REG_R(n))) + | (REG_GETKNOWN(REG_R(m)) & ~REG_GETVALUE(REG_R(m))) + | (REG_GETKNOWN(REG_R(n)) & ~REG_GETVALUE(REG_R(n)))); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(m)) & REG_GETVALUE(REG_R(n))); + PTR_CLEAR(n); + break; + } + + case 0xA: { // XOR Rm,Rn + DEBUG_PRINT_ONCE("XOR Rm,Rn"); + if (m == n) { // XOR Rn,Rn is a common idiom for clearing Rn + DEFINE_RESULT_REG(result, R[n]); + MOVEI(result, 0); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + XOR(result, Rn, Rm); + SET_Rn(result); + /* A bit in the result of an XOR operation is known iff the + * bit is known in both operands. */ + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(m)) & REG_GETKNOWN(REG_R(n))); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(m)) ^ REG_GETVALUE(REG_R(n))); + } + PTR_CLEAR(n); + break; + } + + case 0xB: { // OR Rm,Rn + DEBUG_PRINT_ONCE("OR Rm,Rn"); + if (m == n) { + break; // OR Rn,Rn is a no-op + } + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + OR(result, Rn, Rm); + SET_Rn(result); + /* A bit in the result of an OR operation is known iff (1) the + * bit is known in both operands or (2) the bit is known to be + * 1 in one operand. */ + REG_SETKNOWN(REG_R(n), + (REG_GETKNOWN(REG_R(m)) & REG_GETKNOWN(REG_R(n))) + | (REG_GETKNOWN(REG_R(m)) & REG_GETVALUE(REG_R(m))) + | (REG_GETKNOWN(REG_R(n)) & REG_GETVALUE(REG_R(n)))); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(m)) | REG_GETVALUE(REG_R(n))); + PTR_CLEAR(n); + break; + } + + case 0xC: { // CMP/ST Rm,Rn + DEBUG_PRINT_ONCE("CMP/ST Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(temp); + XOR(temp, Rn, Rm); + DEFINE_REG(byte3); + BFEXT(byte3, temp, 24, 8); + DEFINE_REG(cmp3); + SEQZ(cmp3, byte3); + DEFINE_REG(byte2); + BFEXT(byte2, temp, 16, 8); + DEFINE_REG(cmp2); + SEQZ(cmp2, byte2); + DEFINE_REG(cmp23); + OR(cmp23, cmp2, cmp3); + DEFINE_REG(byte1); + BFEXT(byte1, temp, 8, 8); + DEFINE_REG(cmp1); + SEQZ(cmp1, byte1); + DEFINE_REG(byte0); + ANDI(byte0, temp, 0xFF); + DEFINE_REG(cmp0); + SEQZ(cmp0, byte0); + DEFINE_REG(cmp01); + OR(cmp01, cmp0, cmp1); + DEFINE_REG(new_T); + OR(new_T, cmp01, cmp23); + SET_SR_T(new_T); + break; + } + + case 0xD: { // XTRCT Rm,Rn + DEBUG_PRINT_ONCE("XTRCT Rm,Rn"); + GET_Rn; + DEFINE_REG(shift_Rn); + SRLI(shift_Rn, Rn, 16); + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + BFINS(result, shift_Rn, Rm, 16, 16); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(m)) << 16 + | REG_GETKNOWN(REG_R(n)) >> 16); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(m)) << 16 + | REG_GETVALUE(REG_R(n)) >> 16); + PTR_CLEAR(n); + break; + } + + case 0xE: { // MULU.W Rm,Rn + DEBUG_PRINT_ONCE("MULU.W Rm,Rn"); + GET_Rn; + DEFINE_REG(ext_Rn); + ANDI(ext_Rn, Rn, 0xFFFF); + GET_Rm; + DEFINE_REG(ext_Rm); + ANDI(ext_Rm, Rm, 0xFFFF); + DEFINE_RESULT_REG(result, MACL); + MUL(result, ext_Rn, ext_Rm); + SET_MACL(result); + CLEAR_MAC_IS_ZERO(); + break; + } + + case 0xF: { // MULS.W Rm,Rn + DEBUG_PRINT_ONCE("MULS.W Rm,Rn"); + GET_Rn; + DEFINE_REG(temp_Rn); + SLLI(temp_Rn, Rn, 16); + DEFINE_REG(ext_Rn); + SRAI(ext_Rn, temp_Rn, 16); + GET_Rm; + DEFINE_REG(temp_Rm); + SLLI(temp_Rm, Rm, 16); + DEFINE_REG(ext_Rm); + SRAI(ext_Rm, temp_Rm, 16); + DEFINE_RESULT_REG(result, MACL); + MUL(result, ext_Rn, ext_Rm); + SET_MACL(result); + CLEAR_MAC_IS_ZERO(); + break; + } + + default: + goto invalid; + } + break; + } // $2xxx + + case 0x3: { + switch (opcode & 0xF) { + + case 0x0: { // CMP/EQ Rm,Rn + DEBUG_PRINT_ONCE("CMP/EQ Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(test); + XOR(test, Rn, Rm); + DEFINE_REG(new_T); + SEQZ(new_T, test); + SET_SR_T(new_T); + break; + } + + case 0x2: { // CMP/HS Rm,Rn + DEBUG_PRINT_ONCE("CMP/HS Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(test); + SLTU(test, Rn, Rm); + DEFINE_REG(new_T); + XORI(new_T, test, 1); + SET_SR_T(new_T); + break; + } + + case 0x3: { // CMP/GE Rm,Rn + DEBUG_PRINT_ONCE("CMP/GE Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(test); + SLTS(test, Rn, Rm); + DEFINE_REG(new_T); + XORI(new_T, test, 1); + SET_SR_T(new_T); + break; + } + + case 0x4: { // DIV1 Rm,Rn + DEBUG_PRINT_ONCE("DIV1 Rm,Rn"); + GET_Rn; + DEFINE_REG(new_Q); + SRLI(new_Q, Rn, 31); + GET_SR; + DEFINE_REG(old_Q); + BFEXT(old_Q, SR, SR_Q_SHIFT, 1); + DEFINE_REG(old_T); + BFEXT(old_T, SR, SR_T_SHIFT, 1); + DEFINE_REG(temp_Rn); + SLLI(temp_Rn, Rn, 1); + DEFINE_REG(new_Rn); + OR(new_Rn, temp_Rn, old_T); + DEFINE_REG(M_bit); + BFEXT(M_bit, SR, SR_M_SHIFT, 1); + DEFINE_REG(M_xor_Q); + XOR(M_xor_Q, M_bit, old_Q); + GET_Rm; + DEFINE_REG(temp1); + /* Note: SSA violation for state block optimization */ + DEFINE_RESULT_REG(final_Rn, R[n]); + CREATE_LABEL(div1_MeqQ); + CREATE_LABEL(div1_continue); + GOTO_IF_Z(div1_MeqQ, M_xor_Q); { + ADD(final_Rn, new_Rn, Rm); + SLTU(temp1, final_Rn, new_Rn); + GOTO_LABEL(div1_continue); + } DEFINE_LABEL(div1_MeqQ); { + SUB(final_Rn, new_Rn, Rm); + SLTU(temp1, new_Rn, final_Rn); + } DEFINE_LABEL(div1_continue); + SET_Rn(final_Rn); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(temp2); + XOR(temp2, temp1, M_bit); + DEFINE_REG(final_Q); + XOR(final_Q, new_Q, temp2); + DEFINE_REG(SR_withQ); + BFINS(SR_withQ, SR, final_Q, SR_Q_SHIFT, 1); + DEFINE_REG(new_T); + XOR(new_T, final_Q, M_bit); + DEFINE_REG(final_T); + XORI(final_T, new_T, 1); + DEFINE_RESULT_REG(final_SR, SR); + BFINS(final_SR, SR_withQ, final_T, SR_T_SHIFT, 1); + SET_SR(final_SR); +#ifdef OPTIMIZE_DIVISION + if (cur_PC == state->division_target_PC) { + /* Flush cached state block fields here so they don't get + * picked up on the optimized path */ + ADD_CYCLES(); + FLUSH_STATE_CACHE(); + state->division_target_PC = 0; + } +#endif + break; + } // DIV1 Rm,Rn + + case 0x5: { // DMULU.L Rm,Rn + DEBUG_PRINT_ONCE("DMULU.L Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(lo, MACL); + DEFINE_RESULT_REG(hi, MACH); + MULU_64(lo, Rn, Rm, hi); + SET_MACL(lo); + SET_MACH(hi); + CLEAR_MAC_IS_ZERO(); + cur_cycles += 1; // Minimum number of cycles + break; + } + + case 0x6: { // CMP/HI Rm,Rn + DEBUG_PRINT_ONCE("CMP/HI Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(new_T); + SLTU(new_T, Rm, Rn); + SET_SR_T(new_T); + break; + } + + case 0x7: { // CMP/GT Rm,Rn + DEBUG_PRINT_ONCE("CMP/GT Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_REG(new_T); + SLTS(new_T, Rm, Rn); + SET_SR_T(new_T); + break; + } + + case 0x8: { // SUB Rm,Rn + DEBUG_PRINT_ONCE("SUB Rm,Rn"); + if (m == n) { // SUB Rn,Rn is an alternate way to clear Rn + DEFINE_RESULT_REG(result, R[n]); + MOVEI(result, 0); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + PTR_CLEAR(n); + } else if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF) { + /* Optimize subtraction a constants */ + const int32_t Rm_value = REG_GETVALUE(REG_R(m)); + ADDI_Rn_NOREG(-Rm_value); + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) - Rm_value); + } else { + REG_SETKNOWN(REG_R(n), 0); + } + if (PTR_CHECK(m)) { + /* Subtracting a pointer from anything does not leave + * a valid pointer. */ + PTR_CLEAR(n); + } + } else { + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + SUB(result, Rn, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + } + break; + } + + case 0xA: { // SUBC Rm,Rn + DEBUG_PRINT_ONCE("SUBC Rm,Rn"); + GET_SR_T; + if (m == n) { // SUBC Rn,Rn sets Rn to -(SR.T), and is + // commonly used in signed division + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_RESULT_REG(result, R[n]); + SUB(result, zero, T); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + /* T is unchanged */ + } else { + GET_Rn; + GET_Rm; + DEFINE_REG(temp); + SUB(temp, Rn, Rm); + DEFINE_REG(borrow1); + SLTU(borrow1, Rn, temp); + DEFINE_RESULT_REG(result, R[n]); + SUB(result, temp, T); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(borrow2); + SLTU(borrow2, temp, result); + DEFINE_REG(new_T); + OR(new_T, borrow1, borrow2); + SET_SR_T(new_T); + } + break; + } + + case 0xB: { // SUBV Rm,Rn + DEBUG_PRINT_ONCE("SUBV Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + SUB(result, Rn, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(temp0); + XOR(temp0, Rn, Rm); + DEFINE_REG(temp1); + XOR(temp1, Rn, result); + DEFINE_REG(temp2); + AND(temp2, temp0, temp1); + DEFINE_REG(new_T); + SRLI(new_T, temp2, 31); + SET_SR_T(new_T); + break; + } + + case 0xC: { // ADD Rm,Rn + DEBUG_PRINT_ONCE("ADD Rm,Rn"); + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF) { + /* Optimize addition of a constant */ + const int32_t Rm_value = REG_GETVALUE(REG_R(m)); + ADDI_Rn_NOREG(Rm_value); + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) + Rm_value); + } else { + REG_SETKNOWN(REG_R(n), 0); + } + if (PTR_CHECK(m)) { + if (PTR_CHECK(n)) { + PTR_CLEAR(n); + } else { + PTR_COPY(m, n, 1); + } + } + } else if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + /* Treat this like MOV Rm,Rn; ADD old_Rn,Rn and optimize + * as above */ + const int32_t old_Rn_value = REG_GETVALUE(REG_R(n)); + const int old_PTR_CHECK = PTR_CHECK(n); + COPY_TO_Rn(R[m]); + PTR_COPY(m, n, 0); + ADDI_Rn_NOREG(old_Rn_value); + REG_SETKNOWN(REG_R(n), 0); + if (old_PTR_CHECK) { + /* We lost the old pointer data in the PTR_COPY() above, + * so just give up and mark it as not a pointer. + * Hopefully this case won't be too common. */ + PTR_CLEAR(n); + } + } else { // Neither operand is a known constant + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + ADD(result, Rn, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + if (PTR_CHECK(m)) { + if (PTR_CHECK(n)) { + /* Adding a pointer to a pointer is invalid, so + * clear pointer info for the result register. */ + PTR_CLEAR(n); + } else { + /* ADD Rptr,Rn is equivalent to "MOV Rptr,Rn" + * followed by "ADD old_value_of_Rn,Rn", which we + * treat as a pointer result. */ + PTR_COPY(m, n, 1); + } + } + } + break; + } + + case 0xD: { // DMULS.L Rm,Rn + DEBUG_PRINT_ONCE("DMULS.L Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(lo, MACL); + DEFINE_RESULT_REG(hi, MACH); + MULS_64(lo, Rn, Rm, hi); + SET_MACL(lo); + SET_MACH(hi); + CLEAR_MAC_IS_ZERO(); + cur_cycles += 1; // Minimum number of cycles + break; + } + + case 0xE: { // ADDC Rm,Rn + DEBUG_PRINT_ONCE("ADDC Rm,Rn"); + GET_Rn; + GET_Rm; + GET_SR_T; + DEFINE_REG(temp); + ADD(temp, Rn, Rm); + DEFINE_REG(carry1); + SLTU(carry1, temp, Rn); + DEFINE_RESULT_REG(result, R[n]); + ADD(result, temp, T); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(carry2); + SLTU(carry2, result, temp); + DEFINE_REG(new_T); + OR(new_T, carry1, carry2); + SET_SR_T(new_T); + break; + } + + case 0xF: { // ADDV Rm,Rn + DEBUG_PRINT_ONCE("ADDV Rm,Rn"); + GET_Rn; + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + ADD(result, Rn, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(temp0); + XOR(temp0, Rn, Rm); + DEFINE_REG(temp1); + XORI(temp1, temp0, 1); + DEFINE_REG(temp2); + XOR(temp2, Rn, result); + DEFINE_REG(temp3); + AND(temp3, temp1, temp2); + DEFINE_REG(new_T); + SRLI(new_T, temp3, 31); + SET_SR_T(new_T); + break; + } + + default: + goto invalid; + } + break; + } // $3xxx + + case 0x4: { + switch (opcode & 0xFF) { + + // $4xx0 + case 0x00: // SHLL Rn + case 0x20: { // SHAL Rn + DEBUG_PRINT_ONCE("SH[AL]L Rn"); +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + /* + * If the next instruction is either SHLL or SHAL on the + * same register, we can cache the 1-bit shift from this + * instruction and combine it with the next instruction. + * (We can't do so with multi-bit shifts after a single-bit + * shift, because the multi-bit shifts don't set the T bit + * in SR.) If the next instruction is a delayed branch and + * does not reference the shifted register, we check the + * delay slot as well. + * + * If _this_ instruction is in a delay slot, we don't try + * to optimize at all because the next instruction executed + * may not be the next one in the code stream. Note that + * we check state->branch_type rather than state->delay + * because the latter is cleared at the top of this routine. + */ + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if ((next_opcode & 0xFFDF) == (opcode & 0xFFDF)) { + if (CACHED_SHIFT_COUNT() < 32) { + ADD_TO_SHIFT_CACHE(1); + } + break; + } + } + const unsigned int shift_count = CACHED_SHIFT_COUNT() + 1; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int shift_count = 1; +#endif + DEFINE_REG(new_T); + DEFINE_RESULT_REG(result, R[n]); + if (shift_count > 32) { + /* Completely shifted out (including T bit) */ + MOVEI(new_T, 0); + MOVEI(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + GET_Rn; + BFEXT(new_T, Rn, 32 - shift_count, 1); + if (shift_count == 32) { + MOVEI(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + SLLI(result, Rn, shift_count); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) << shift_count + | 0xFFFFFFFFU >> (32-shift_count)); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(n)) << shift_count); + } + } + SET_SR_T(new_T); + SET_Rn(result); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } // SHLL/SHAL + case 0x10: { // DT Rn + DEBUG_PRINT_ONCE("DT Rn"); + GET_Rn; + DEFINE_RESULT_REG(result, R[n]); +#ifdef OPTIMIZE_DELAY + if (fetch && INSN_IS_AVAILABLE(1) && fetch[1]==0x8BFD){ + /* It's a DT/BF loop, so eat as many iterations as we can. + * At most, we consume enough iterations to push us over + * the cycle limit. */ +# ifdef JIT_DEBUG_VERBOSE + DMSG("Found DT/BF delay loop at 0x%08X", (int)cur_PC); +# endif + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF + && REG_GETVALUE(REG_R(n)) > 0 // Just in case + && REG_GETVALUE(REG_R(n)) <= OPTIMIZE_DELAY_OMIT_MAX + ) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("-- Known to be %u cycles, omitting loop", + REG_GETVALUE(REG_R(n))); +# endif + /* The iteration count is known and within the limit + * for omitting the loop code, so just clear the + * register and update the cycle count. */ + cur_cycles += REG_GETVALUE(REG_R(n)) * 4; + MOVEI(result, 0); + SET_Rn(result); + REG_SETVALUE(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(new_T); + MOVEI(new_T, 1); + SET_SR_T(new_T); + INC_PC_BY(2); // Skip over the BF + break; + } + /* Calculate the maximum number of loop iterations we can + * consume with the remaining cycles, but always execute + * at least one iteration. */ + DEFINE_REG(imm_1); + MOVEI(imm_1, 1); + /* cycle_limit generally won't be preloaded here, and we + * don't want to carry a previous use too far (so as not to + * create an unnecessarily long-lived register for what's + * essentially a constant value), so we don't bother trying + * to reuse any previous register with LOAD_STATE_ALLOC(). + * However, we _should_ make use of a fixed register if one + * is available. */ + DECLARE_REG(cycle_limit); + cycle_limit = STATE_CACHE_FIXED_REG(cycle_limit); + if (!cycle_limit) { + ALLOC_REG(cycle_limit); + LOAD_STATE(cycle_limit, cycle_limit); + } + DEFINE_REG(temp); + ADDI(temp, cycle_limit, 4-1); // Round the result up + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(cycles_left); + SUB(cycles_left, temp, cycles); + DEFINE_REG(max_loops_temp); + SRAI(max_loops_temp, cycles_left, 2); // 4 cycles per loop + DEFINE_REG(max_loops_test); + SLTS(max_loops_test, max_loops_temp, imm_1); + DEFINE_REG(max_loops); + SELECT(max_loops, imm_1, max_loops_temp, max_loops_test); + /* Don't consume more iterations than are actually + * available. */ + DEFINE_REG(test); + SLTU(test, Rn, max_loops); + DEFINE_REG(num_loops_temp); + SELECT(num_loops_temp, Rn, max_loops, test); + /* Protect against the case of Rn == 0. That should never + * happen in practice (it would take over 10 minutes to + * complete the loop), but since we're optimizing out + * hundreds of cycles here, we can afford a single extra + * instruction to be safe. */ + DEFINE_REG(num_loops); + SELECT(num_loops, num_loops_temp, max_loops, Rn); + SUB(result, Rn, num_loops); + /* Update the cycle counter; since the final loop will have + * its cycles added separately, we subtract one loop's + * worth of cycles before adding. */ + DEFINE_REG(used_cycles_temp); + SLLI(used_cycles_temp, num_loops, 2); + DEFINE_REG(used_cycles); + SUBI(used_cycles, used_cycles_temp, 4); + DEFINE_REG(final_cycles); + ADD(final_cycles, cycles, used_cycles); + STORE_STATE(cycles, final_cycles); + } else // Not optimizable +#endif // OPTIMIZE_DELAY + SUBI(result, Rn, 1); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + DEFINE_REG(new_T); + SEQZ(new_T, result); + SET_SR_T(new_T); + break; + } // DT + + // $4xx1 + case 0x01: { // SHLR Rn + DEBUG_PRINT_ONCE("SHLR Rn"); +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (next_opcode == opcode) { + if (CACHED_SHIFT_COUNT() < 32) { + ADD_TO_SHIFT_CACHE(1); + } + break; + } + } + const unsigned int shift_count = CACHED_SHIFT_COUNT() + 1; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int shift_count = 1; +#endif + DEFINE_REG(new_T); + DEFINE_RESULT_REG(result, R[n]); + if (shift_count > 32) { + MOVEI(new_T, 0); + MOVE(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + GET_Rn; + BFEXT(new_T, Rn, shift_count - 1, 1); + if (shift_count == 32) { + MOVEI(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + SRLI(result, Rn, shift_count); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) >> shift_count + | 0xFFFFFFFF << (32-shift_count)); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(n)) >> shift_count); + } + } + SET_SR_T(new_T); + SET_Rn(result); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } // SHLR Rn + case 0x11: { // CMP/PZ Rn + DEBUG_PRINT_ONCE("CMP/PZ Rn"); + GET_Rn; + DEFINE_REG(temp); + SLTZ(temp, Rn); + DEFINE_REG(new_T); + XORI(new_T, temp, 1); + SET_SR_T(new_T); + break; + } + case 0x21: { // SHAR Rn + DEBUG_PRINT_ONCE("SHAR Rn"); +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (next_opcode == opcode) { + if (CACHED_SHIFT_COUNT() < 32) { + ADD_TO_SHIFT_CACHE(1); + } + break; + } + } + const unsigned int shift_count = CACHED_SHIFT_COUNT() + 1; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int shift_count = 1; +#endif + GET_Rn; + DEFINE_REG(new_T); + DEFINE_RESULT_REG(result, R[n]); + if (shift_count >= 32) { + SRAI(result, Rn, 31); + ANDI(new_T, result, 1); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), + (int32_t)REG_GETVALUE(REG_R(n)) >> 31); + } else { + BFEXT(new_T, Rn, shift_count - 1, 1); + SRAI(result, Rn, shift_count); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) >> shift_count + | 0xFFFFFFFF << (32-shift_count)); + REG_SETVALUE(REG_R(n), + (int32_t)REG_GETVALUE(REG_R(n))>>shift_count); + } + SET_SR_T(new_T); + SET_Rn(result); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } // SHAR Rn + + // $4xx2 + case 0x02: { // STS.L MACH,@-Rn + DEBUG_PRINT_ONCE("STS.L MACH,@-Rn"); + GET_MACH_COPY; + STORE_dec_Rn(L, MACH); + break; + } + case 0x12: { // STS.L MACL,@-Rn + DEBUG_PRINT_ONCE("STS.L MACL,@-Rn"); + GET_MACL_COPY; + STORE_dec_Rn(L, MACL); + break; + } + case 0x22: { // STS.L PR,@-Rn + DEBUG_PRINT_ONCE("STS.L PR,@-Rn"); + GET_PR; + STORE_dec_Rn(L, PR); + break; + } + + // $4xx3 + case 0x03: { // STC.L SR,@-Rn + DEBUG_PRINT_ONCE("STC.L SR,@-Rn"); + GET_SR; + STORE_dec_Rn(L, SR); + cur_cycles += 1; + break; + } + case 0x13: { // STC.L GBR,@-Rn + DEBUG_PRINT_ONCE("STC.L GBR,@-Rn"); + GET_GBR; + STORE_dec_Rn(L, GBR); + cur_cycles += 1; + break; + } + case 0x23: { // STC.L VBR,@-Rn + DEBUG_PRINT_ONCE("STC.L VBR,@-Rn"); + GET_VBR; + STORE_dec_Rn(L, VBR); + cur_cycles += 1; + break; + } + + // $4xx4 + case 0x04: { // ROTL Rn + DEBUG_PRINT_ONCE("ROTL Rn"); +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (next_opcode == opcode) { + /* Don't worry about overflow; we only look at the + * lower 5 bits of the value */ + ADD_TO_SHIFT_CACHE(1); + break; + } + } + const unsigned int rotate_count = (CACHED_SHIFT_COUNT() + 1) & 31; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int rotate_count = 1; +#endif + GET_Rn; + DEFINE_REG(new_T); + BFEXT(new_T, Rn, (32 - rotate_count) & 31, 1); + SET_SR_T(new_T); + DEFINE_RESULT_REG(result, R[n]); + RORI(result, Rn, (32 - rotate_count) & 31); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) << rotate_count + | REG_GETKNOWN(REG_R(n)) >> (32 - rotate_count)); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(n)) << rotate_count + | REG_GETVALUE(REG_R(n)) >> (32 - rotate_count)); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } + case 0x24: { // ROTCL Rn + DEBUG_PRINT_ONCE("ROTCL Rn"); + GET_SR_T; + GET_Rn; + DEFINE_REG(new_T); + SRLI(new_T, Rn, 31); + SET_SR_T(new_T); + DEFINE_REG(temp); + SLLI(temp, Rn, 1); + DEFINE_RESULT_REG(result, R[n]); + OR(result, temp, T); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(n)) << 1); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) << 1); + PTR_CLEAR(n); + break; + } + + // $4xx5 + case 0x05: { // ROTR Rn + DEBUG_PRINT_ONCE("ROTR Rn"); +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (next_opcode == opcode) { + ADD_TO_SHIFT_CACHE(1); + break; + } + } + const unsigned int rotate_count = (CACHED_SHIFT_COUNT() + 1) & 31; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int rotate_count = 1; +#endif + GET_Rn; + DEFINE_REG(new_T); + BFEXT(new_T, Rn, (rotate_count + 31) & 31, 1); + SET_SR_T(new_T); + DEFINE_RESULT_REG(result, R[n]); + RORI(result, Rn, rotate_count); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) >> rotate_count + | REG_GETKNOWN(REG_R(n)) << (32 - rotate_count)); + REG_SETVALUE(REG_R(n), + REG_GETVALUE(REG_R(n)) >> rotate_count + | REG_GETVALUE(REG_R(n)) << (32 - rotate_count)); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } + case 0x15: { // CMP/PL Rn + DEBUG_PRINT_ONCE("CMP/PL Rn"); + GET_Rn; + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_REG(new_T); + SLTS(new_T, zero, Rn); + SET_SR_T(new_T); + break; + } + case 0x25: { // ROTCR Rn + DEBUG_PRINT_ONCE("ROTCR Rn"); + GET_SR_T; + GET_Rn; + DEFINE_REG(new_T); + ANDI(new_T, Rn, 1); + SET_SR_T(new_T); + DEFINE_REG(temp); + SRLI(temp, Rn, 1); + DEFINE_RESULT_REG(result, R[n]); + BFINS(result, temp, T, 31, 1); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(n)) >> 1); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) >> 1); + PTR_CLEAR(n); + break; + } + + // $4xx6 + case 0x06: { // LDS.L @Rn+,MACH + DEBUG_PRINT_ONCE("LDS.L @Rn+,MACH"); + DEFINE_RESULT_REG(value, MACH); + LOAD_Rn_inc(L, value); + SET_MACH(value); + CLEAR_MAC_IS_ZERO(); + break; + } + case 0x16: { // LDS.L @Rn+,MACL + DEBUG_PRINT_ONCE("LDS.L @Rn+,MACL"); + DEFINE_RESULT_REG(value, MACL); + LOAD_Rn_inc(L, value); + SET_MACL(value); + CLEAR_MAC_IS_ZERO(); + break; + } + case 0x26: { // LDS.L @Rn+,PR + DEBUG_PRINT_ONCE("LDS.L @Rn+,PR"); + DEFINE_RESULT_REG(value, PR); + LOAD_Rn_inc(L, value); + SET_PR(value); + break; + } + + // $4xx7 + case 0x07: { // LDC.L @Rn+,SR + DEBUG_PRINT_ONCE("LDC.L @Rn+,SR"); + DEFINE_REG(value); + LOAD_Rn_inc(L, value); + DEFINE_RESULT_REG(new_SR, SR); + ANDI(new_SR, value, 0x3F3); + SET_SR(new_SR); + cur_cycles += 2; + state->need_interrupt_check = 1; + break; + } + case 0x17: { // LDC.L @Rn+,GBR + DEBUG_PRINT_ONCE("LDC.L @Rn+,GBR"); + DEFINE_RESULT_REG(value, GBR); + LOAD_Rn_inc(L, value); + SET_GBR(value); + REG_SETKNOWN(REG_GBR, 0); + cur_cycles += 2; + break; + } + case 0x27: { // LDC.L @Rn+,VBR + DEBUG_PRINT_ONCE("LDC.L @Rn+,VBR"); + DEFINE_RESULT_REG(value, VBR); + LOAD_Rn_inc(L, value); + SET_VBR(value); + cur_cycles += 2; + break; + } + + // $4xx8 + case 0x08: case 0x18: case 0x28: { + unsigned int this_count; + switch (opcode>>4 & 0xF) { + case 0x0: // SHLL2 Rn + DEBUG_PRINT_ONCE("SHLL2 Rn"); + this_count = 2; + break; + case 0x1: // SHLL8 Rn + DEBUG_PRINT_ONCE("SHLL8 Rn"); + this_count = 8; + break; + case 0x2: // SHLL16 Rn + DEBUG_PRINT_ONCE("SHLL16 Rn"); + this_count = 16; + break; + default: // Not needed, but avoid a compiler warning + this_count = 0; + break; + } +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + /* For multi-bit shifts, we can accept any size shift in + * the same direction on the next instruction. */ + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (((next_opcode & 0xFFCF) == (opcode & 0xFFCF) + && ((next_opcode>>4 & 0xF) != 3)) //Watch out for invalids + || (next_opcode & 0xFFDF) == (opcode & 0xFF07) + ) { + if (CACHED_SHIFT_COUNT() < 32) { + ADD_TO_SHIFT_CACHE(this_count); + } + break; + } + } + const unsigned int shift_count = CACHED_SHIFT_COUNT() + this_count; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int shift_count = this_count; +#endif + DEFINE_RESULT_REG(result, R[n]); + if (shift_count >= 32) { + MOVEI(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + GET_Rn; + SLLI(result, Rn, shift_count); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) << shift_count + | 0xFFFFFFFFU >> (32-shift_count)); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) << shift_count); + } + SET_Rn(result); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } // SHLLx Rn + + // $4xx9 + case 0x09: case 0x19: case 0x29: { + unsigned int this_count; + switch (opcode>>4 & 0xF) { + case 0x0: // SHLR2 Rn + DEBUG_PRINT_ONCE("SHLR2 Rn"); + this_count = 2; + break; + case 0x1: // SHLR8 Rn + DEBUG_PRINT_ONCE("SHLR8 Rn"); + this_count = 8; + break; + case 0x2: // SHLR16 Rn + DEBUG_PRINT_ONCE("SHLR16 Rn"); + this_count = 16; + break; + default: // Not needed, but avoid a compiler warning + this_count = 0; + break; + } +#ifdef OPTIMIZE_SHIFT_SEQUENCES + if (!in_delay && CAN_CACHE_SHIFTS()) { + /* We could theoretically cached SHLRn + SHAR by treating + * the SHAR as an SHLR instead (since the top bit will be + * zero following SHLRn), but since this would require + * extra logic for a case assumed to be extremely rare, + * we let it slide. */ + GET_NEXT_OPCODE_FOR_SHIFT_CACHE; + if (((next_opcode & 0xFFCF) == (opcode & 0xFFCF) + && ((next_opcode>>4 & 0xF) != 3)) //Watch out for invalids + || (next_opcode & 0xFFFF) == (opcode & 0xFF07) + ) { + if (CACHED_SHIFT_COUNT() < 32) { + ADD_TO_SHIFT_CACHE(this_count); + } + break; + } + } + const unsigned int shift_count = CACHED_SHIFT_COUNT() + this_count; +#else // !OPTIMIZE_SHIFT_SEQUENCES + const unsigned int shift_count = this_count; +#endif + DEFINE_RESULT_REG(result, R[n]); + if (shift_count >= 32) { + MOVEI(result, 0); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), 0); + } else { + GET_Rn; + SRLI(result, Rn, shift_count); + REG_SETKNOWN(REG_R(n), + REG_GETKNOWN(REG_R(n)) >> shift_count + | 0xFFFFFFFF << (32-shift_count)); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) >> shift_count); + } + SET_Rn(result); + PTR_CLEAR(n); + CLEAR_SHIFT_CACHE(); + break; + } // SHLRx Rn + + // $4xxA + case 0x0A: { // LDS Rn,MACH + DEBUG_PRINT_ONCE("LDS Rn,MACH"); + /* Make sure we load a _copy_ of Rn, so a subsequent overwrite + * of the register by a MADD instruction doesn't modify the + * value of Rn as well. */ + DEFINE_RESULT_REG(Rn, MACH); + LOAD_STATE_COPY(Rn, R[n]); + SET_MACH(Rn); + CLEAR_MAC_IS_ZERO(); + break; + } + case 0x1A: { // LDS Rn,MACL + DEBUG_PRINT_ONCE("LDS Rn,MACL"); + DEFINE_RESULT_REG(Rn, MACL); + LOAD_STATE_COPY(Rn, R[n]); + SET_MACL(Rn); + CLEAR_MAC_IS_ZERO(); + break; + } + case 0x2A: { // LDS Rn,PR + DEBUG_PRINT_ONCE("LDS Rn,PR"); + COPY_FROM_Rn(PR); + break; + } + + // $4xxB + case 0x0B: { // JSR @Rn + DEBUG_PRINT_ONCE("JSR @Rn"); + DEFINE_RESULT_REG(ret_addr, PR); + MOVEI(ret_addr, cur_PC + 4); + SET_PR(ret_addr); + if (BRANCH_IS_FOLDABLE_SUBROUTINE(cur_PC)) { + state->branch_type = SH2BRTYPE_FOLDED; + state->branch_target = BRANCH_FOLD_TARGET(cur_PC); + state->branch_fold_native = BRANCH_FOLD_NATIVE_FUNC(cur_PC); + } else if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = REG_GETVALUE(REG_R(n)); + } else { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_Rn; + state->branch_target_reg = Rn; + } + state->delay = 1; + cur_cycles += 1; + break; + } + case 0x1B: { // TAS.B @Rn + DEBUG_PRINT_ONCE("TAS.B @Rn"); + DEFINE_REG(value); + LOAD_Rn(B, value); + DEFINE_REG(new_T); + SEQZ(new_T, value); + SET_SR_T(new_T); + DEFINE_REG(new_value); + ORI(new_value, value, 0x80); + STORE_Rn(B, new_value); + cur_cycles += 3; + break; + } + case 0x2B: { // JMP @Rn + DEBUG_PRINT_ONCE("JMP @Rn"); + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = REG_GETVALUE(REG_R(n)); + } else { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_Rn; + state->branch_target_reg = Rn; + } + state->delay = 1; + cur_cycles += 1; + break; + } + + // $4xxE + case 0x0E: { // LDC Rn,SR + DEBUG_PRINT_ONCE("LDC Rn,SR"); + GET_Rn; + DEFINE_RESULT_REG(new_SR, SR); + ANDI(new_SR, Rn, 0x3F3); + SET_SR(new_SR); + state->need_interrupt_check = 1; + break; + } + case 0x1E: { // LDC Rn,GBR + DEBUG_PRINT_ONCE("LDC Rn,GBR"); + COPY_FROM_Rn(GBR); + REG_SETKNOWN(REG_GBR, REG_GETKNOWN(REG_R(n))); + REG_SETVALUE(REG_GBR, REG_GETVALUE(REG_R(n))); + break; + } + case 0x2E: { // LDC Rn,VBR + DEBUG_PRINT_ONCE("LDC Rn,VBR"); + COPY_FROM_Rn(VBR); + break; + } + + // $4xxF + case 0x0F: case 0x1F: case 0x2F: case 0x3F: + case 0x4F: case 0x5F: case 0x6F: case 0x7F: + case 0x8F: case 0x9F: case 0xAF: case 0xBF: + case 0xCF: case 0xDF: case 0xEF: case 0xFF: { // MAC.W @Rm+,@Rn+ + DEBUG_PRINT_ONCE("MAC.W @Rm+,@Rn+"); + + /* Load the values */ + DEFINE_REG(value_m); + LOAD_Rm_inc(W, value_m); + DEFINE_REG(value_n); + LOAD_Rn_inc(W, value_n); + + /* If we're saturating, we may need the old value of MACH/MACL */ + DECLARE_REG(old_MACH); + DECLARE_REG(old_MACL); + if (!CAN_OMIT_MAC_S_CHECK) { + ALLOC_REG(old_MACH); + LOAD_STATE_COPY(old_MACH, MACH); + ALLOC_REG(old_MACL); + LOAD_STATE_COPY(old_MACL, MACL); + } else { // Not needed, but avoid a compiler warning + old_MACH = 0; + old_MACL = 0; + } + + /* Do the actual multiplication and addition */ + if (MAC_IS_ZERO()) { + DEFINE_RESULT_REG(new_MACL, MACL); + DEFINE_RESULT_REG(new_MACH, MACH); + MULS_64(new_MACL, value_m, value_n, new_MACH); + SET_MACL(new_MACL); + SET_MACH(new_MACH); + } else { + GET_MACL; + GET_MACH; + MADDS_64(MACL, value_m, value_n, MACH); + SET_MACL(MACL); + SET_MACH(MACH); + } + + /* Perform saturation if the S flag of SR is set */ + if (!CAN_OMIT_MAC_S_CHECK) { + GET_SR; + DEFINE_REG(saturate); + ANDI(saturate, SR, SR_S); + CREATE_LABEL(macw_nosat); + GOTO_IF_Z(macw_nosat, saturate); { + GET_MACL; + GET_MACH; + DEFINE_REG(value_m_sign); + SLTZ(value_m_sign, value_m); + DEFINE_REG(value_n_sign); + SLTZ(value_n_sign, value_n); + DEFINE_REG(sum_sign); + SLTZ(sum_sign, MACL); // sum < 0 + DEFINE_REG(product_sign); + XOR(product_sign, value_m_sign, value_n_sign); // product < 0 + DEFINE_REG(MACL_sign); + SLTZ(MACL_sign, old_MACL); // MACL < 0 + DEFINE_REG(temp0); + XOR(temp0, product_sign, MACL_sign); + DEFINE_REG(temp1); + XORI(temp1, temp0, 1); // (product<0) == (MACL<0) + DEFINE_REG(temp2); + XOR(temp2, sum_sign, product_sign); // (sum<0) != (product<0) + DEFINE_REG(overflow); + AND(overflow, temp2, temp1); // Nonzero if overflow + DEFINE_REG(overflow_bit); + MOVEI(overflow_bit, 0); + CREATE_LABEL(macw_no_overflow); + GOTO_IF_Z(macw_no_overflow, overflow); { + DEFINE_REG(sat_neg); + MOVEI(sat_neg, 0x80000000); + DEFINE_REG(sat_pos); + NOT(sat_pos, sat_neg); + DEFINE_REG(saturated); + SELECT(saturated, sat_neg, sat_pos, product_sign); + /* Note: SSA violations for state block optimization */ + MOVE(MACL, saturated); + SET_MACL(MACL); + MOVEI(overflow_bit, 1); + } DEFINE_LABEL(macw_no_overflow); + OR(MACH, old_MACH, overflow_bit); + SET_MACH(MACH); + } DEFINE_LABEL(macw_nosat); + } // if (!CAN_OMIT_MAC_S_CHECK) + + CLEAR_MAC_IS_ZERO(); + cur_cycles += 2; + break; + } // MAC.W + + default: + goto invalid; + } + break; + } // $4xxx + + case 0x5: { // MOV.L @(disp,Rm),Rn + DEBUG_PRINT_ONCE("MOV.L @(disp,Rm),Rn"); + const int disp = (opcode & 0xF) * 4; + DEFINE_RESULT_REG(value, R[n]); + LOAD_disp_Rm(L, value, disp); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF && PTR_ISLOCAL(m)) { + PTR_SET_SOURCE(n, REG_GETVALUE(REG_R(m)) + disp); + } + break; + } // $5xxx + + case 0x6: { + switch (opcode & 0xF) { + + case 0x0: { // MOV.B @Rm,Rn + DEBUG_PRINT_ONCE("MOV.B @Rm,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm(B, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0x1: { // MOV.W @Rm,Rn + DEBUG_PRINT_ONCE("MOV.W @Rm,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm(W, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0x2: { // MOV.L @Rm,Rn + DEBUG_PRINT_ONCE("MOV.L @Rm,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm(L, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF && PTR_ISLOCAL(m)) { + PTR_SET_SOURCE(n, REG_GETVALUE(REG_R(m))); + } + break; + } + + case 0x3: { // MOV Rm,Rn + DEBUG_PRINT_ONCE("MOV Rm,Rn"); + if (state->pending_select && !in_delay) { + state->pending_select = 0; + GET_Rm; + GET_Rn; + DEFINE_RESULT_REG(selected, R[n]); + if (state->select_sense) { + /* If select_sense is nonzero, the branch was a BT or BT/S, + * meaning we _skip_ the new value if SR.T is set. */ + SELECT(selected, Rn, Rm, state->branch_cond_reg); + } else { + SELECT(selected, Rm, Rn, state->branch_cond_reg); + } + SET_Rn(selected); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + } else { + COPY_TO_Rn(R[m]); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(m))); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(m))); + PTR_COPY(m, n, 0); + } + break; + } + + case 0x4: { // MOV.B @Rm+,Rn + DEBUG_PRINT_ONCE("MOV.B @Rm+,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm_inc(B, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0x5: { // MOV.W @Rm+,Rn + DEBUG_PRINT_ONCE("MOV.W @Rm+,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm_inc(W, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0x6: { // MOV.L @Rm+,Rn + DEBUG_PRINT_ONCE("MOV.L @Rm+,Rn"); + DEFINE_RESULT_REG(value, R[n]); + LOAD_Rm_inc(L, value); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + if (REG_GETKNOWN(REG_R(m)) == 0xFFFFFFFF && PTR_ISLOCAL(m)) { + PTR_SET_SOURCE(n, REG_GETVALUE(REG_R(m)) - 4); + } + break; + } + + case 0x7: { // NOT Rm,Rn + DEBUG_PRINT_ONCE("NOT Rm,Rn"); + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + NOT(result, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(m))); + REG_SETVALUE(REG_R(n), ~REG_GETVALUE(REG_R(m))); + PTR_CLEAR(n); + break; + } + + case 0x8: { // SWAP.B Rm,Rn + DEBUG_PRINT_ONCE("SWAP.B Rm,Rn"); + GET_Rm; + /* BSWAPH swaps both low and high halfwords, but we want to + * keep the high halfword unchanged */ + DEFINE_REG(temp); + BSWAPH(temp, Rm); + DEFINE_RESULT_REG(result, R[n]); + BFINS(result, Rm, temp, 0, 16); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), (REG_GETKNOWN(REG_R(m)) & 0xFFFF0000) + | (REG_GETKNOWN(REG_R(m)) & 0x0000FF00) >> 8 + | (REG_GETKNOWN(REG_R(m)) & 0x000000FF) << 8); + REG_SETVALUE(REG_R(n), (REG_GETVALUE(REG_R(m)) & 0xFFFF0000) + | (REG_GETVALUE(REG_R(m)) & 0x0000FF00) >> 8 + | (REG_GETVALUE(REG_R(m)) & 0x000000FF) << 8); + PTR_CLEAR(n); + break; + } + + case 0x9: { // SWAP.W Rm,Rn + DEBUG_PRINT_ONCE("SWAP.W Rm,Rn"); + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + HSWAPW(result, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), (REG_GETKNOWN(REG_R(m)) & 0xFFFF0000) >> 16 + | (REG_GETKNOWN(REG_R(m)) & 0x0000FFFF) << 16); + REG_SETVALUE(REG_R(n), (REG_GETVALUE(REG_R(m)) & 0xFFFF0000) >> 16 + | (REG_GETVALUE(REG_R(m)) & 0x0000FFFF) << 16); + PTR_CLEAR(n); + break; + } + + case 0xA: { // NEGC Rm,Rn + DEBUG_PRINT_ONCE("NEGC Rm,Rn"); + GET_SR_T; + GET_Rm; + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_REG(borrow); + SLTU(borrow, zero, Rm); + DEFINE_REG(new_T); + OR(new_T, borrow, T); + SET_SR_T(new_T); + DEFINE_REG(temp); + SUB(temp, zero, Rm); + DEFINE_RESULT_REG(result, R[n]); + SUB(result, temp, T); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0xB: { // NEG Rm,Rn + DEBUG_PRINT_ONCE("NEG Rm,Rn"); + GET_Rm; + DEFINE_REG(zero); + MOVEI(zero, 0); + DEFINE_RESULT_REG(result, R[n]); + SUB(result, zero, Rm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } + + case 0xC: { // EXTU.B Rm,Rn + DEBUG_PRINT_ONCE("EXTU.B Rm,Rn"); + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + ANDI(result, Rm, 0xFF); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(m)) | 0xFFFFFF00); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(m)) & 0x000000FF); + PTR_CLEAR(n); + break; + } + + case 0xD: { // EXTU.W Rm,Rn + DEBUG_PRINT_ONCE("EXTU.W Rm,Rn"); + GET_Rm; + DEFINE_RESULT_REG(result, R[n]); + ANDI(result, Rm, 0xFFFF); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), REG_GETKNOWN(REG_R(m)) | 0xFFFF0000); + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(m)) & 0x0000FFFF); + PTR_CLEAR(n); + break; + } + + case 0xE: { // EXTS.B Rm,Rn + DEBUG_PRINT_ONCE("EXTS.B Rm,Rn"); + GET_Rm; + DEFINE_REG(temp); + SLLI(temp, Rm, 24); + DEFINE_RESULT_REG(result, R[n]); + SRAI(result, temp, 24); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), + (int32_t)(REG_GETKNOWN(REG_R(m)) << 24) >> 24); + REG_SETVALUE(REG_R(n), + (int32_t)(REG_GETVALUE(REG_R(m)) << 24) >> 24); + PTR_CLEAR(n); + break; + } + + case 0xF: { // EXTS.W Rm,Rn + DEBUG_PRINT_ONCE("EXTS.W Rm,Rn"); + GET_Rm; + DEFINE_REG(temp); + SLLI(temp, Rm, 16); + DEFINE_RESULT_REG(result, R[n]); + SRAI(result, temp, 16); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), + (int32_t)(REG_GETKNOWN(REG_R(m)) << 16) >> 16); + REG_SETVALUE(REG_R(n), + (int32_t)(REG_GETVALUE(REG_R(m)) << 16) >> 16); + PTR_CLEAR(n); + break; + } + + default: // impossible, but included for consistency + goto invalid; + } + break; + } // $6xxx + + case 0x7: { // ADD #imm,Rn + DEBUG_PRINT_ONCE("ADD #imm,Rn"); + const int8_t imm = opcode & 0xFF; + if (state->pending_select && !in_delay) { + state->pending_select = 0; + GET_Rn; + DEFINE_REG(result); + ADDI(result, Rn, (int32_t)imm); + DEFINE_RESULT_REG(selected, R[n]); + if (state->select_sense) { + /* If select_sense is nonzero, the branch was a BT or BT/S, + * meaning we _skip_ the new value if SR.T is set. */ + SELECT(selected, Rn, result, state->branch_cond_reg); + } else { + SELECT(selected, result, Rn, state->branch_cond_reg); + } + SET_Rn(selected); + REG_SETKNOWN(REG_R(n), 0); + } else { + ADDI_Rn_NOREG((int32_t)imm); + if (REG_GETKNOWN(REG_R(n)) == 0xFFFFFFFF) { + REG_SETVALUE(REG_R(n), REG_GETVALUE(REG_R(n)) + (int32_t)imm); + } else { + REG_SETKNOWN(REG_R(n), 0); + } + } + break; + } // $7xxx + + case 0x8: { + switch (opcode>>8 & 0xF) { + + case 0x0: { // MOV.B R0,@(disp,Rm) + DEBUG_PRINT_ONCE("MOV.B R0,@(disp,Rm)"); + const int disp = opcode & 0xF; + GET_R0; + STORE_disp_Rm(B, R0, disp); + break; + } + + case 0x1: { // MOV.W R0,@(disp,Rm) + DEBUG_PRINT_ONCE("MOV.W R0,@(disp,Rm)"); + const int disp = (opcode & 0xF) * 2; + GET_R0; + STORE_disp_Rm(W, R0, disp); + break; + } + + case 0x4: { // MOV.B @(disp,Rm),R0 + DEBUG_PRINT_ONCE("MOV.B @(disp,Rm),R0"); + const int disp = opcode & 0xF; + DEFINE_RESULT_REG(value, R[0]); + LOAD_disp_Rm(B, value, disp); + SET_R0(value); + REG_SETKNOWN(REG_R(0), 0); + PTR_CLEAR(0); + break; + } + + case 0x5: { // MOV.W @(disp,Rm),R0 + DEBUG_PRINT_ONCE("MOV.W @(disp,Rm),R0"); + const int disp = (opcode & 0xF) * 2; + DEFINE_RESULT_REG(value, R[0]); + LOAD_disp_Rm(W, value, disp); + SET_R0(value); + REG_SETKNOWN(REG_R(0), 0); + PTR_CLEAR(0); + break; + } + + case 0x8: { // CMP/EQ #imm,R0 + DEBUG_PRINT_ONCE("CMP/EQ #imm,R0"); + const int8_t imm = opcode & 0xFF; + GET_R0; + DEFINE_REG(test); + SUBI(test, R0, (int32_t)imm); + DEFINE_REG(new_T); + SEQZ(new_T, test); + SET_SR_T(new_T); + break; + } + + case 0x9: { // BT label + DEBUG_PRINT_ONCE("BT label"); + GET_SR_T; + if (BRANCH_FALLS_THROUGH(cur_PC) || BRANCH_IS_SELECT(cur_PC)) { + /* We don't need to branch in the native code, and we don't + * need to update PC (because a later instruction will do + * so), but we do need to update the cycle counter + * appropriately. */ + ADD_CYCLES(); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(inc_cycles); + ADDI(inc_cycles, cycles, BRANCH_IS_SELECT(cur_PC) ? 1 : 2); + DEFINE_RESULT_REG(new_cycles, cycles); + SELECT(new_cycles, inc_cycles, cycles, T); + STORE_STATE(cycles, new_cycles); + if (BRANCH_IS_SELECT(cur_PC)) { + state->pending_select = 1; + state->select_sense = 1; + state->branch_cond_reg = T; + } + break; + } + const int disp = ((int32_t)(opcode & 0xFF) << 24) >> 23; + state->branch_type = SH2BRTYPE_BT; + state->branch_targets_rts = BRANCH_TARGETS_RTS(cur_PC); + state->loop_to_jsr = BRANCH_IS_LOOP_TO_JSR(cur_PC); + state->branch_cycles = 2; + if (state->branch_targets_rts) { + GET_PR; + state->branch_target_reg = PR; + state->branch_cycles += 3; + } else if (BRANCH_IS_THREADED(cur_PC)) { + state->branch_target = BRANCH_THREAD_TARGET(cur_PC); + state->branch_cycles += BRANCH_THREAD_COUNT(cur_PC) * 3; + } else { + state->branch_target = (cur_PC + 4) + disp; + } + state->branch_cond_reg = T; + break; + } + + case 0xB: { // BF label + DEBUG_PRINT_ONCE("BF label"); + GET_SR_T; + if (BRANCH_FALLS_THROUGH(cur_PC) || BRANCH_IS_SELECT(cur_PC)) { + ADD_CYCLES(); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(inc_cycles); + ADDI(inc_cycles, cycles, BRANCH_IS_SELECT(cur_PC) ? 1 : 2); + DEFINE_RESULT_REG(new_cycles, cycles); + SELECT(new_cycles, cycles, inc_cycles, T); + STORE_STATE(cycles, new_cycles); + if (BRANCH_IS_SELECT(cur_PC)) { + state->pending_select = 1; + state->select_sense = 0; + state->branch_cond_reg = T; + } + break; + } + const int disp = ((int32_t)(opcode & 0xFF) << 24) >> 23; + state->branch_type = SH2BRTYPE_BF; + state->branch_targets_rts = BRANCH_TARGETS_RTS(cur_PC); + state->loop_to_jsr = BRANCH_IS_LOOP_TO_JSR(cur_PC); + state->branch_cycles = 2; + if (state->branch_targets_rts) { + GET_PR; + state->branch_target_reg = PR; + state->branch_cycles += 3; + } else if (BRANCH_IS_THREADED(cur_PC)) { + state->branch_target = BRANCH_THREAD_TARGET(cur_PC); + state->branch_cycles += BRANCH_THREAD_COUNT(cur_PC) * 3; + } else { + state->branch_target = (cur_PC + 4) + disp; + } + state->branch_cond_reg = T; + break; + } + + case 0xD: { // BT/S label + DEBUG_PRINT_ONCE("BT/S label"); + GET_SR_T; + /* For delayed SELECT branches, the cycle count ends up the + * same whether the branch is taken or not, so we can skip all + * the cycle stuff. */ + if (BRANCH_FALLS_THROUGH(cur_PC)) { + ADD_CYCLES(); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(inc_cycles); + ADDI(inc_cycles, cycles, 1); + DEFINE_RESULT_REG(new_cycles, cycles); + SELECT(new_cycles, inc_cycles, cycles, T); + STORE_STATE(cycles, new_cycles); + break; + } else if (BRANCH_IS_SELECT(cur_PC)) { + state->pending_select = 1; + state->select_sense = 1; + state->branch_cond_reg = T; + state->delay = 1; + break; + } + const int disp = ((int32_t)(opcode & 0xFF) << 24) >> 23; + state->branch_type = SH2BRTYPE_BT_S; + state->branch_targets_rts = BRANCH_TARGETS_RTS(cur_PC); + state->loop_to_jsr = BRANCH_IS_LOOP_TO_JSR(cur_PC); + state->branch_cycles = 1; + if (state->branch_targets_rts) { + GET_PR; + state->branch_target_reg = PR; + state->branch_cycles += 3; + } else if (BRANCH_IS_THREADED(cur_PC)) { + state->branch_target = BRANCH_THREAD_TARGET(cur_PC); + state->branch_cycles += BRANCH_THREAD_COUNT(cur_PC) * 3; + } else { + state->branch_target = (cur_PC + 4) + disp; + } + state->branch_cond_reg = T; + state->delay = 1; + /* Unlike the other delayed branch instructions, we don't add + * the extra cycle for conditional branches until we actually + * branch; this avoids having to add a variable number of + * cycles to the cycle counter and thus breaking compile-time + * accumulation of cycles with OPTIMIZE_CONSTANT_ADDS. */ + break; + } + + case 0xF: { // BF/S label + DEBUG_PRINT_ONCE("BF/S label"); + GET_SR_T; + if (BRANCH_FALLS_THROUGH(cur_PC)) { + ADD_CYCLES(); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(inc_cycles); + ADDI(inc_cycles, cycles, 1); + DEFINE_RESULT_REG(new_cycles, cycles); + SELECT(new_cycles, inc_cycles, cycles, T); + STORE_STATE(cycles, new_cycles); + break; + } else if (BRANCH_IS_SELECT(cur_PC)) { + state->pending_select = 1; + state->select_sense = 0; + state->branch_cond_reg = T; + state->delay = 1; + break; + } + const int disp = ((int32_t)(opcode & 0xFF) << 24) >> 23; + state->branch_type = SH2BRTYPE_BF_S; + state->branch_targets_rts = BRANCH_TARGETS_RTS(cur_PC); + state->loop_to_jsr = BRANCH_IS_LOOP_TO_JSR(cur_PC); + state->branch_cycles = 1; + if (state->branch_targets_rts) { + GET_PR; + state->branch_target_reg = PR; + state->branch_cycles += 3; + } else if (BRANCH_IS_THREADED(cur_PC)) { + state->branch_target = BRANCH_THREAD_TARGET(cur_PC); + state->branch_cycles += BRANCH_THREAD_COUNT(cur_PC) * 3; + } else { + state->branch_target = (cur_PC + 4) + disp; + } + state->branch_cond_reg = T; + state->delay = 1; + break; + } + + default: + goto invalid; + } + break; + } // $8xxx + + case 0x9: { // MOV.W @(disp,PC),Rn + DEBUG_PRINT_ONCE("MOV.W @(disp,PC),Rn"); + const int disp = (opcode & 0xFF) * 2; + const uint32_t address = cur_PC + 4 + disp; + DEFINE_RESULT_REG(value, R[n]); + SH2_LOAD_ABS_W(value, address); + SET_Rn(value); + REG_SETKNOWN(REG_R(n), 0); + PTR_CLEAR(n); + break; + } // $9xxx + + case 0xA: { // BRA label + DEBUG_PRINT_ONCE("BRA label"); + if (BRANCH_FALLS_THROUGH(cur_PC)) { + cur_cycles += 1; + break; + } + if (BRANCH_TARGETS_RTS(cur_PC)) { + state->branch_type = SH2BRTYPE_DYNAMIC; + GET_PR; + state->branch_target_reg = PR; + cur_cycles += 3; + } else { + state->loop_to_jsr = BRANCH_IS_LOOP_TO_JSR(cur_PC); + const int disp = ((int32_t)(opcode & 0xFFF) << 20) >> 19; + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = (cur_PC + 4) + disp; + } + state->delay = 1; + cur_cycles += 1; + break; + } // $Axxx + + case 0xB: { // BSR label + DEBUG_PRINT_ONCE("BSR label"); + const int disp = ((int32_t)(opcode & 0xFFF) << 20) >> 19; + DEFINE_RESULT_REG(ret_addr, PR); + MOVEI(ret_addr, cur_PC + 4); + SET_PR(ret_addr); + if (BRANCH_IS_FOLDABLE_SUBROUTINE(cur_PC)) { + state->branch_type = SH2BRTYPE_FOLDED; + state->branch_target = BRANCH_FOLD_TARGET(cur_PC); + state->branch_fold_native = BRANCH_FOLD_NATIVE_FUNC(cur_PC); + } else { + state->branch_type = SH2BRTYPE_STATIC; + state->branch_target = (cur_PC + 4) + disp; + } + state->delay = 1; + cur_cycles += 1; + break; + } // $Bxxx + + case 0xC: { + const unsigned int imm = opcode & 0xFF; + switch (opcode>>8 & 0xF) { + + case 0x0: { // MOV.B R0,@(disp,GBR) + DEBUG_PRINT_ONCE("MOV.B R0,@(disp,GBR)"); + GET_R0; + STORE_disp_GBR(B, R0, imm); + break; + } + + case 0x1: { // MOV.W R0,@(disp,GBR) + DEBUG_PRINT_ONCE("MOV.W R0,@(disp,GBR)"); + GET_R0; + STORE_disp_GBR(W, R0, imm*2); + break; + } + + case 0x2: { // MOV.L R0,@(disp,GBR) + DEBUG_PRINT_ONCE("MOV.L R0,@(disp,GBR)"); + GET_R0; + STORE_disp_GBR(L, R0, imm*4); + break; + } + + case 0x3: { // TRAPA #imm + DEBUG_PRINT_ONCE("TRAPA #imm"); + cur_cycles += 7; + DEFINE_REG(PC); + MOVEI(PC, cur_PC + 2); + TAKE_EXCEPTION(imm, PC); + break; + } + + case 0x4: { // MOV.B @(disp,GBR),R0 + DEBUG_PRINT_ONCE("MOV.B @(disp,GBR),R0"); + DEFINE_RESULT_REG(value, R[0]); + LOAD_disp_GBR(B, value, imm); + SET_R0(value); + REG_SETKNOWN(REG_R(0), 0); + PTR_CLEAR(0); + break; + } + + case 0x5: { // MOV.W @(disp,GBR),R0 + DEBUG_PRINT_ONCE("MOV.W @(disp,GBR),R0"); + DEFINE_RESULT_REG(value, R[0]); + LOAD_disp_GBR(W, value, imm*2); + SET_R0(value); + REG_SETKNOWN(REG_R(0), 0); + PTR_CLEAR(0); + break; + } + + case 0x6: { // MOV.L @(disp,GBR),R0 + DEBUG_PRINT_ONCE("MOV.L @(disp,GBR),R0"); + DEFINE_RESULT_REG(value, R[0]); + LOAD_disp_GBR(L, value, imm*4); + SET_R0(value); + REG_SETKNOWN(REG_R(0), 0); + PTR_CLEAR(0); + break; + } + + case 0x7: { // MOVA @(disp,PC),R0 + DEBUG_PRINT_ONCE("MOVA @(disp,PC),R0"); + DEFINE_RESULT_REG(address, R[0]); + MOVEI(address, (cur_PC & ~3) + 4 + imm*4); + SET_R0(address); + REG_SETKNOWN(REG_R(0), 0xFFFFFFFF); + REG_SETVALUE(REG_R(0), (cur_PC & ~3) + 4 + imm*4); + /* Technically it's still a pointer, but the former value is + * gone (and we'll be using absolute loads/stores for this + * register anyway), so clear pointer status */ + PTR_CLEAR(0); + PTR_SETLOCAL(0); // Flag to pass to the load/store routines + break; + } + + case 0x8: { // TST #imm,R0 + DEBUG_PRINT_ONCE("TST #imm,R0"); + GET_R0; +#ifdef OPTIMIZE_VARIABLE_SHIFTS + if (LIKELY(!in_delay) && fetch) { + unsigned int count_reg, count_mask, shift_reg, shift_type; + const uint8_t *cycles_array; + const unsigned int num_insns = can_optimize_variable_shift( + fetch, cur_PC, &count_reg, &count_mask, &shift_reg, + &shift_type, &cycles_array + ); + if (num_insns) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("Optimizing variable shift at 0x%08X", (int)cur_PC); +# endif + DECLARE_REG(Rshift); + LOAD_STATE_ALLOC(Rshift, R[shift_reg]); + DEFINE_REG(count); + ANDI(count, R0, count_mask); // count_reg is always 0 + DEFINE_REG(new_T); + DEFINE_RESULT_REG(result, R[shift_reg]); + if (shift_type == 0) { + SRLI(new_T, Rshift, 31); + SLL(result, Rshift, count); + } else { + ANDI(new_T, Rshift, 1); + SRL(result, Rshift, count); + } + STORE_STATE(R[shift_reg], result); + /* SR.T is only set if the shift count is odd */ + GET_SR_T; + DEFINE_REG(test); + ANDI(test, count, 1); + DEFINE_REG(final_T); + SELECT(final_T, new_T, T, test); + SET_SR_T(final_T); + DEFINE_REG(cycles_array_base); + MOVEA(cycles_array_base, ADDR_HI(cycles_array)); + DEFINE_REG(cycles_array_ptr); + ADD(cycles_array_ptr, cycles_array_base, count); + cur_cycles--; // Avoid double-counting this instruction + ADD_CYCLES(); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_REG(cycles_to_add); + LOAD_BU(cycles_to_add, cycles_array_ptr, + ADDR_LO(cycles_array)); + DEFINE_RESULT_REG(new_cycles, cycles); + ADD(new_cycles, cycles, cycles_to_add); + STORE_STATE(cycles, new_cycles); + INC_PC_BY((num_insns - 1) * 2); + break; + } + } +#endif + DEFINE_REG(test); + ANDI(test, R0, imm); + DEFINE_REG(new_T); + SEQZ(new_T, test); + SET_SR_T(new_T); + break; + } + + case 0x9: { // AND #imm,R0 + DEBUG_PRINT_ONCE("AND #imm,R0"); + GET_R0; + DEFINE_RESULT_REG(result, R[0]); + ANDI(result, R0, imm); + SET_R0(result); + REG_SETKNOWN(REG_R(0), REG_GETKNOWN(REG_R(0)) | ~imm); + REG_SETVALUE(REG_R(0), REG_GETVALUE(REG_R(0)) & imm); + break; + } + + case 0xA: { // XOR #imm,R0 + DEBUG_PRINT_ONCE("XOR #imm,R0"); + GET_R0; + DEFINE_RESULT_REG(result, R[0]); + XORI(result, R0, imm); + SET_R0(result); + REG_SETVALUE(REG_R(0), REG_GETVALUE(REG_R(0)) ^ imm); + break; + } + + case 0xB: { // OR #imm,R0 + DEBUG_PRINT_ONCE("OR #imm,R0"); + GET_R0; + DEFINE_RESULT_REG(result, R[0]); + ORI(result, R0, imm); + SET_R0(result); + REG_SETKNOWN(REG_R(0), REG_GETKNOWN(REG_R(0)) | imm); + REG_SETVALUE(REG_R(0), REG_GETVALUE(REG_R(0)) | imm); + break; + } + + case 0xC: { // TST.B #imm,@(R0,GBR) + DEBUG_PRINT_ONCE("TST.B #imm,@(R0,GBR)"); + DEFINE_REG(value); + LOAD_R0_GBR(B, value); + DEFINE_REG(test); + ANDI(test, value, imm); + DEFINE_REG(new_T); + SEQZ(new_T, test); + SET_SR_T(new_T); + break; + } + + case 0xD: { // AND.B #imm,@(R0,GBR) + DEBUG_PRINT_ONCE("AND.B #imm,@(R0,GBR)"); + DEFINE_REG(value); + LOAD_R0_GBR(B, value); + DEFINE_REG(result); + ANDI(result, value, imm); + STORE_SAVED_R0_GBR(B, result); + cur_cycles += 2; + break; + } + + case 0xE: { // XOR.B #imm,@(R0,GBR) + DEBUG_PRINT_ONCE("XOR.B #imm,@(R0,GBR)"); + DEFINE_REG(value); + LOAD_R0_GBR(B, value); + DEFINE_REG(result); + XORI(result, value, imm); + STORE_SAVED_R0_GBR(B, result); + cur_cycles += 2; + break; + } + + case 0xF: { // OR.B #imm,@(R0,GBR) + DEBUG_PRINT_ONCE("OR.B #imm,@(R0,GBR)"); + DEFINE_REG(value); + LOAD_R0_GBR(B, value); + DEFINE_REG(result); + ORI(result, value, imm); + STORE_SAVED_R0_GBR(B, result); + cur_cycles += 2; + break; + } + + default: // impossible, but included for consistency + goto invalid; + } + break; + + } // $Cxxx + + case 0xD: { // MOV.L @(disp,PC),Rn + DEBUG_PRINT_ONCE("MOV.L @(disp,PC),Rn"); + const int disp = (opcode & 0xFF) * 4; + const uint32_t address = (cur_PC & ~3) + 4 + disp; + DEFINE_RESULT_REG(value, R[n]); + SH2_LOAD_ABS_L(value, address); + REG_SETKNOWN(REG_R(n), 0); + SET_Rn(value); + PTR_CLEAR(n); + PTR_SET_SOURCE(n, address); + break; + } // $Dxxx + + case 0xE: { // MOV #imm,Rn + DEBUG_PRINT_ONCE("MOV #imm,Rn"); + const int8_t imm = opcode & 0xFF; + if (state->pending_select && !in_delay) { + state->pending_select = 0; + GET_Rn; + DEFINE_REG(result); + MOVEI(result, (int32_t)imm); + DEFINE_RESULT_REG(selected, R[n]); + if (state->select_sense) { + /* If select_sense is nonzero, the branch was a BT or BT/S, + * meaning we _skip_ the new value if SR.T is set. */ + SELECT(selected, Rn, result, state->branch_cond_reg); + } else { + SELECT(selected, result, Rn, state->branch_cond_reg); + } + SET_Rn(selected); + REG_SETKNOWN(REG_R(n), 0); + } else { + DEFINE_RESULT_REG(result, R[n]); + MOVEI(result, (int32_t)imm); + SET_Rn(result); + REG_SETKNOWN(REG_R(n), 0xFFFFFFFF); + REG_SETVALUE(REG_R(n), (int32_t)imm); + } + PTR_CLEAR(n); + break; + } // $Exxx + + case 0xF: { + goto invalid; + } // $Fxxx + + } + + /**** The Big Honkin' Opcode Switch Ends Here ****/ + + /* Update the PC and cycle count */ + + INC_PC(); + ADD_CYCLES(); + + /* Handle any pending branches */ + + if (UNLIKELY(state->branch_type != SH2BRTYPE_NONE && !state->delay)) { + + int is_idle = 0; + +#ifdef OPTIMIZE_IDLE + if ((state->branch_type == SH2BRTYPE_STATIC + || ((state->branch_type == SH2BRTYPE_BT + || state->branch_type == SH2BRTYPE_BF + || state->branch_type == SH2BRTYPE_BT_S + || state->branch_type == SH2BRTYPE_BF_S) + && !state->branch_targets_rts)) + && state->branch_target >= initial_PC + && state->branch_target < cur_PC + && (cur_PC - state->branch_target) / 2 <= OPTIMIZE_IDLE_MAX_INSNS + && fetch + ) { + const int num_insns = (cur_PC - state->branch_target) / 2; + is_idle = can_optimize_idle((fetch+1) - num_insns, + state->branch_target, num_insns); +# ifdef JIT_DEBUG_VERBOSE + if (is_idle) { + DMSG("Found idle loop at 0x%08X (%d instructions)", + (int)state->branch_target, num_insns); + } +# endif + } +#endif + + switch (state->branch_type) { + + case SH2BRTYPE_NONE: // Avoid a compiler warning + break; + + case SH2BRTYPE_STATIC: { + if (is_idle) { + /* See delay loop handling in DT for why we don't use + * LOAD_STATE_ALLOC here. */ + DECLARE_REG(cycle_limit); + cycle_limit = STATE_CACHE_FIXED_REG(cycle_limit); + if (!cycle_limit) { + ALLOC_REG(cycle_limit); + LOAD_STATE(cycle_limit, cycle_limit); + } + STORE_STATE(cycles, cycle_limit); + } + if (state->loop_to_jsr) { + /* Clear the flag now, or else it'll get picked up if the + * subroutine call is a BSR or the target register is known */ + state->loop_to_jsr = 0; + const uint32_t target = state->branch_target; + RECURSIVE_DECODE(target, 0); + RECURSIVE_DECODE(target+2, 1); + /* The flush/jump will be performed by the subroutine call */ + } else { + SET_PC_KNOWN(state->branch_target); + FLUSH_STATE_CACHE(); + JUMP_STATIC(); + } + state->just_branched = 1; + break; + } + + case SH2BRTYPE_DYNAMIC: { +#ifdef OPTIMIZE_VARIABLE_SHIFTS + /* We still have to jump normally if the branch register is + * outside the shift sequence, so create a label for that */ + CREATE_LABEL(label_do_varshift); + int doing_varshift = 0; + DECLARE_REG(Rcount); + if (state->varshift_target_PC) { + doing_varshift = 1; + DEFINE_REG(test); + LOAD_STATE_ALLOC(Rcount, R[state->varshift_Rcount]); + SLTUI(test, Rcount, + (state->varshift_target_PC + 1) - cur_PC); + GOTO_IF_NZ(label_do_varshift, test); + SAVE_STATE_CACHE(); + } else { // Not needed, but avoid a compiler warning + Rcount = 0; + } +#endif + if (is_idle) { + DECLARE_REG(cycle_limit); + cycle_limit = STATE_CACHE_FIXED_REG(cycle_limit); + if (!cycle_limit) { + ALLOC_REG(cycle_limit); + LOAD_STATE(cycle_limit, cycle_limit); + } + STORE_STATE(cycles, cycle_limit); + } + SET_PC(state->branch_target_reg); + FLUSH_STATE_CACHE(); + JUMP(); +#ifdef OPTIMIZE_VARIABLE_SHIFTS + if (doing_varshift) { + RESTORE_STATE_CACHE(); + DEFINE_LABEL(label_do_varshift); + /* A branch distance of zero means the maximum count */ + DEFINE_REG(count_2_max); + MOVEI(count_2_max, state->varshift_max * 2); + DEFINE_REG(count_2); + SUB(count_2, count_2_max, Rcount); + DEFINE_REG(count); + SRLI(count, count_2, 1); + DECLARE_REG(Rshift); + LOAD_STATE_ALLOC(Rshift, R[state->varshift_Rshift]); + DEFINE_RESULT_REG(new_Rshift, + R[state->varshift_Rshift]); + DEFINE_REG(new_T); + switch (state->varshift_type) { + case 0: { + DEFINE_REG(temp); + SUBI(temp, count, 1); + DEFINE_REG(temp2); + SLL(temp2, Rshift, temp); + SRLI(new_T, temp2, 31); + SLL(new_Rshift, Rshift, count); + break; + } + case 1: { + DEFINE_REG(temp); + SUBI(temp, count, 1); + DEFINE_REG(temp2); + SRL(temp2, Rshift, temp); + ANDI(new_T, temp2, 1); + SRL(new_Rshift, Rshift, count); + break; + } + case 2: { + DEFINE_REG(temp); + SUBI(temp, count, 1); + DEFINE_REG(temp2); + SRA(temp2, Rshift, temp); + ANDI(new_T, temp2, 1); + SRA(new_Rshift, Rshift, count); + break; + } + case 3: { + DEFINE_REG(imm_32); + MOVEI(imm_32, 32); + DEFINE_REG(right_count); + SUB(right_count, imm_32, count); + ROR(new_Rshift, Rshift, right_count); + ANDI(new_T, new_Rshift, 1); + break; + } + case 4: + ROR(new_Rshift, Rshift, count); + SRLI(new_T, new_Rshift, 31); + break; + default: + DMSG("Invalid shift type %u at 0x%X", + state->varshift_type, (unsigned int)cur_PC - 4); + MOVEI(new_T, 0); + break; + } + STORE_STATE(R[state->varshift_Rshift], new_Rshift); + GET_SR_T; + DEFINE_REG(final_T); + SELECT(final_T, new_T, T, count); + SET_SR_T(final_T); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DEFINE_RESULT_REG(new_cycles, cycles); + ADD(new_cycles, cycles, count); + STORE_STATE(cycles, new_cycles); + INC_PC_BY(state->varshift_target_PC - cur_PC); + /* We fall through in this case, so don't set the + * just_branched flag */ + break; + } +#endif // OPTIMIZE_VARIABLE_SHIFTS + state->just_branched = 1; + break; + } // case SH2BRTYPE_DYNAMIC + + case SH2BRTYPE_RTE: { + SET_PC(state->branch_target_reg); + FLUSH_STATE_CACHE(); + DEFINE_REG(check_interrupts_funcptr); + MOVEA(check_interrupts_funcptr, check_interrupts); + CALL_NORET(state_reg, 0, check_interrupts_funcptr); + JUMP(); + break; + } // case SH2BRTYPE_RTE + + case SH2BRTYPE_BT: + case SH2BRTYPE_BF: + case SH2BRTYPE_BT_S: + case SH2BRTYPE_BF_S: { + DECLARE_REG(cycle_limit); + cycle_limit = STATE_CACHE_FIXED_REG(cycle_limit); + if (!cycle_limit) { + ALLOC_REG(cycle_limit); + LOAD_STATE(cycle_limit, cycle_limit); + } + DECLARE_REG(cycles); + LOAD_STATE_ALLOC_KEEPOFS(cycles, cycles); + if (STATE_CACHE_FIXED_REG(SR)) { + FLUSH_STATE_SR_T(); // Avoid needing to flush it twice + } + SAVE_STATE_CACHE(); + CREATE_LABEL(bt_bf_nobranch); + if (state->branch_type == SH2BRTYPE_BT + || state->branch_type == SH2BRTYPE_BT_S + ) { + GOTO_IF_Z(bt_bf_nobranch, state->branch_cond_reg); + } else { + GOTO_IF_NZ(bt_bf_nobranch, state->branch_cond_reg); + } + if (is_idle) { + STORE_STATE(cycles, cycle_limit); + } else { + DEFINE_RESULT_REG(new_cycles, cycles); + ADDI(new_cycles, cycles, + state->branch_cycles + STATE_CACHE_OFFSET(cycles)); + STORE_STATE(cycles, new_cycles); + } + if (state->branch_targets_rts) { + SET_PC(state->branch_target_reg); + FLUSH_STATE_CACHE(); + JUMP(); + } else if (state->loop_to_jsr) { + state->loop_to_jsr = 0; + const uint32_t target = state->branch_target; + RECURSIVE_DECODE(target, 0); + RECURSIVE_DECODE(target+2, 1); + } else { + SET_PC_KNOWN(state->branch_target); + WRITEBACK_STATE_CACHE(); // Avoid stores of fixed registers + JUMP_STATIC(); + } + DEFINE_LABEL(bt_bf_nobranch); + RESTORE_STATE_CACHE(); + /* We don't set state->just_branched here, because the code + * will fall through if the condition isn't met */ + break; + } // case SH2BRTYPE_B{T,F}{,_S} + + case SH2BRTYPE_FOLDED: { + /* Handle cleanup for this instruction first. */ + OPCODE_DONE(opcode); + /* Clear branch_type now (rather than at the end) so the + * recursive decode doesn't see it. */ + state->branch_type = SH2BRTYPE_NONE; + /* If it's a native implementation, just call it (but remember + * to update PC and flush the state block cache first). */ + if (state->branch_fold_native) { + SET_PC_KNOWN(state->branch_target); + FLUSH_STATE_CACHE(); + unsigned int i; + for (i = 0; i < 8; i++) { + PTR_CLEAR(i); + REG_SETKNOWN(REG_R(i), 0); + } + DEFINE_REG(funcptr_reg); + MOVEA(funcptr_reg, state->branch_fold_native); + CALL_NORET(state_reg, 0, funcptr_reg); + } else { + /* Fold in the contents of the called subroutine. We don't + * accept subroutines with branches in the first place, so + * we just recursively decode one instruction at a time + * until we reach RTS, then process the RTS manually, + * decode the delay slot, and start back up with the + * instruction after the BSR/JSR's delay slot. */ + state->folding_subroutine = 1; + uint32_t sub_PC = state->branch_target; + const uint16_t *sub_fetch = + BRANCH_FOLD_TARGET_FETCH(state->branch_target); + for (;;) { + uint16_t sub_opcode; + if (sub_fetch) { + sub_opcode = *sub_fetch++; + } else { + sub_opcode = MappedMemoryReadWord(sub_PC); + } + if (sub_opcode == 0x000B) { // RTS + break; + } + RECURSIVE_DECODE(sub_PC, 0); + sub_PC += 2; + } + RECURSIVE_DECODE(sub_PC, 0); + RECURSIVE_DECODE(sub_PC+2, 1); + state->folding_subroutine = 0; + DEFINE_REG(post_fold_PC); + MOVEI(post_fold_PC, cur_PC); + SET_PC(post_fold_PC); + } // if (state->branch_fold_native) + break; + } // case SH2BRTYPE_FOLDED + + } // switch (state->branch_type) + + state->branch_type = SH2BRTYPE_NONE; +#ifdef OPTIMIZE_VARIABLE_SHIFTS + state->varshift_target_PC = 0; +#endif + state->branch_targets_rts = 0; + state->loop_to_jsr = 0; + + } // if we need to branch + + /* Check for interrupts if necessary */ + + if (state->need_interrupt_check) { + state->need_interrupt_check = 0; + FLUSH_STATE_CACHE(); + DEFINE_REG(check_interrupts_funcptr); + MOVEA(check_interrupts_funcptr, check_interrupts); + DEFINE_REG(result); + CALL(result, state_reg, 0, check_interrupts_funcptr); + CREATE_LABEL(nointr); + GOTO_IF_Z(nointr, result); { + RETURN(); + } DEFINE_LABEL(nointr); + } + + /* Invalid opcode handler (jumped to on detection of an invalid opcode) */ + + if (0) { + invalid: + if (invalid_opcode_callback) { + (*invalid_opcode_callback)(state, cur_PC, opcode); + } + cur_cycles++; + DEFINE_REG(PC); + MOVEI(PC, cur_PC); + INC_PC(); // So we don't get stuck when translating + TAKE_EXCEPTION(state->delay ? 6 : 4, PC); + } + + /* All done; perform any cleanup requested and return the instruction's + * opcode to the caller. */ + + OPCODE_DONE(opcode); + return opcode; + +} // End of decode_insn() + +/*************************************************************************/ + +#undef GET_REG +#undef GET_R0 +#undef GET_R0_W +#undef GET_R0_B +#undef GET_R15 +#undef GET_Rn +#undef GET_Rm +#undef GET_Rm_W +#undef GET_Rm_B +#undef GET_SR +#undef GET_SR_T +#undef GET_GBR +#undef GET_VBR +#undef GET_MACH +#undef GET_MACL +#undef GET_PR +#undef GET_MACH_COPY +#undef GET_MACL_COPY +#undef GET_REG_KEEPOFS +#undef GET_R0_KEEPOFS +#undef GET_R15_KEEPOFS +#undef GET_Rn_KEEPOFS +#undef GET_Rm_KEEPOFS +#undef GET_GBR_KEEPOFS + +#undef COPY_FROM_Rn +#undef COPY_TO_Rn +#undef DEFINE_RESULT_REG + +#undef SET_R0 +#undef SET_R15 +#undef SET_Rn +#undef SET_Rm +#undef SET_SR +#undef SET_SR_T +#undef SET_GBR +#undef SET_VBR +#undef SET_MACH +#undef SET_MACL +#undef SET_PR +#undef SET_PC +#undef SET_PC_KNOWN + +#undef ADDI_R0 +#undef ADDI_R15 +#undef ADDI_Rn +#undef ADDI_Rm +#undef ADDI_R0_NOREG +#undef ADDI_R15_NOREG +#undef ADDI_Rn_NOREG +#undef ADDI_Rm_NOREG +#undef ADD_CYCLES + +#undef INCDEC_B +#undef INCDEC_W +#undef INCDEC_L +#undef LOAD_Rm +#undef LOAD_disp_Rm +#undef LOAD_R0_Rm +#undef LOAD_Rm_inc +#undef LOAD_Rn +#undef LOAD_Rn_inc +#undef LOAD_disp_GBR +#undef LOAD_R0_GBR +#undef STORE_Rn +#undef STORE_disp_Rn +#undef STORE_dec_Rn +#undef STORE_R0_Rn +#undef STORE_disp_Rm +#undef STORE_disp_GBR +#undef STORE_SAVED_R0_GBR + +#undef TAKE_EXCEPTION +#undef GET_NEXT_OPCODE_FOR_SHIFT_CACHE +#undef DEBUG_PRINT_ONCE + +/*************************************************************************/ + +/* + * Local variables: + * mode: c + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2-internal.h b/yabause/src/psp/sh2-internal.h new file mode 100644 index 0000000000..57e31a8fce --- /dev/null +++ b/yabause/src/psp/sh2-internal.h @@ -0,0 +1,967 @@ +/* src/psp/sh2-internal.h: SH-2 emulator internal definitions/declarations + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SH2_INTERNAL_H +#define SH2_INTERNAL_H + +#ifndef SH2_H +# include "sh2.h" +#endif + +/*************************************************************************/ +/************************* Configuration options *************************/ +/*************************************************************************/ + +/*============ Compilation environment settings ============*/ + +/** + * LOG2_SIZEOF_PTR: The base-2 log of the size of a pointer value (i.e. + * sizeof(void *) == 1 << LOG2_SIZEOF_PTR). + */ +#define LOG2_SIZEOF_PTR (sizeof(void *) == 8 ? 3 : 2) + +/*============ General options ============*/ + +/** + * INTERRUPT_STACK_SIZE: Sets the maximum number of interrupts that can + * be stacked. Any interrupts occurring when the stack is full will be + * lost. + */ +#ifndef INTERRUPT_STACK_SIZE +# define INTERRUPT_STACK_SIZE 50 +#endif + +/** + * ENABLE_JIT: When defined, enables the use of dynamic recompilation. + */ +#define ENABLE_JIT + +/** + * JIT_ACCURATE_ACCESS_TIMING: When defined, checks the current cycle + * count against the cycle limit before each load or store operation to + * ensure that external accesses occur at the proper times, as compared to + * interpreted execution. This does not apply to accesses which can be + * proven to be to internal RAM or ROM ($[02]0[026]xxxxx). + */ +// #define JIT_ACCURATE_ACCESS_TIMING + +/** + * JIT_ACCURATE_LDC_SR_TIMING: When defined, prevents interrupts from + * being accepted during the instruction following an LDC ...,SR + * instruction, just like a real SH-2 processor. When not defined, accepts + * interrupts immediately following an LDC ...,SR instruction, which may + * provide better performance depending on the code being executed. + */ +// #define JIT_ACCURATE_LDC_SR_TIMING + +/** + * JIT_ALLOW_DISTANT_BRANCHES: When defined, the translator will scan + * forward past an unconditional branch for branch targets later in the + * SH-2 code and attempt to include them in the same block. Otherwise, + * only a branch targeting the instruction immediately following the + * branch's delay slot (or targeting the delay slot itself) will be + * considered as part of the same block. + */ +#define JIT_ALLOW_DISTANT_BRANCHES + +/** + * JIT_TABLE_SIZE: Specifies the size of the dynamic translation (JIT) + * routine table. The larger the table, the more translated code can be + * retained in memory, but the greater the cost of stores that overwrite + * previously-translated code. This should always be a prime number. + */ +#ifndef JIT_TABLE_SIZE +# define JIT_TABLE_SIZE 4001 +#endif + +/** + * JIT_DATA_LIMIT_DEFAULT: Specifies the default for the maximum total + * size of translated code, in bytes of native code. When this limit is + * reached, the least recently created translations will be purged from + * memory to make room for new translations. This limit can be changed + * dynamically with sh2_set_jit_data_limit(). + */ +#ifndef JIT_DATA_LIMIT_DEFAULT +# define JIT_DATA_LIMIT_DEFAULT 20000000 +#endif + +/** + * JIT_BRANCH_PREDICTION_SLOTS: Specifies the number of dynamic branch + * prediction slots to use for each block of translated code. A larger + * value increases the number of different branch targets which can be + * predicted without having to search for the code in the global + * translation table, but slows down processing of code with varying + * branch targets. Each slot also uses up 16 * JIT_TABLE_SIZE bytes of + * memory in the translation table (on a 32-bit system). + * + * A value of zero disables branch prediction entirely (including optimized + * static branch prediction). + */ +#ifndef JIT_BRANCH_PREDICTION_SLOTS +# define JIT_BRANCH_PREDICTION_SLOTS 3 +#endif + +/** + * JIT_BRANCH_PREDICTION_FLOAT: When defined, causes successfully + * predicted dynamic branches to "float" up the predicted branch table, so + * they can be found more quickly the next time the block is executed. + * Whether this helps or hurts performance depends on the particular code + * being executed; blocks called from a single location many times followed + * by a different single location many times will benefit, while blocks + * which are alternately called by two or more locations will suffer. + * + * This option has no effect (obviously) if the value of + * JIT_BRANCH_PREDICTION_SLOTS is 0 or 1. + */ +#define JIT_BRANCH_PREDICTION_FLOAT + +/** + * JIT_BRANCH_PREDICT_STATIC: When defined, optimizes static branches and + * block termination code to minimize the overhead of jumping from one + * block to another. + * + * This option is ignored if the value of JIT_BRANCH_PREDICTION_SLOTS is 0. + */ +// #define JIT_BRANCH_PREDICT_STATIC + +/** + * JIT_INSNS_PER_BLOCK: Specifies the maximum number of SH-2 instructions + * (or 16-bit words of local data) to translate in a single block. A + * larger value allows larger blocks of code to be translated as a single + * unit, potentially increasing execution speed at the cost of longer + * translation delays. + */ +#ifndef JIT_INSNS_PER_BLOCK +# define JIT_INSNS_PER_BLOCK 512 +#endif + +/** + * JIT_MAX_INSN_GAP: Specifies the maximum number of 16-bit words to skip + * between SH-2 instructions or local data before terminating the block. + * A larger value allows common paths through a block to be translated as a + * single unit and helps detection of local data that does not immediately + * follow the last translated instruction, but may result in some segments + * of code being translated multiple times. + * + * Setting this option to 0 is not recommended when using the runtime- + * selectable OPTIMIZE_LOCAL_ACCESSES optimization, since doing so prevents + * recognition of all local data in blocks that end on a non-32-bit-aligned + * address. + */ +#ifndef JIT_MAX_INSN_GAP +# define JIT_MAX_INSN_GAP 256 +#endif + +/** + * JIT_BTCACHE_SIZE: Specifies the size of the branch target cache. + * A larger cache increases the amount of SH-2 code that can be translated + * as a single block, but increases the time required to translate branch + * instructions. + */ +#ifndef JIT_BTCACHE_SIZE +# define JIT_BTCACHE_SIZE 256 +#endif + +/** + * JIT_UNRES_BRANCH_SIZE: Specifies the size of the unresolved branch + * table. A larger table size increases the complexity of SH-2 code that + * can be translated as a single block, but increases the time required to + * translate all instructions. + */ +#ifndef JIT_UNRES_BRANCH_SIZE +# define JIT_UNRES_BRANCH_SIZE 32 +#endif + +/** + * JIT_PAGE_BITS: Specifies the page size used for checking whether a + * store operation affects a previously-translated block, in powers of two + * (e.g. a value of 8 means a page size of 256 bytes). A larger page size + * decreases the amount of memory needed for the page tables, but increases + * the chance of an ordinary data write triggering an expensive check of + * translated blocks. + */ +#ifndef JIT_PAGE_BITS +# define JIT_PAGE_BITS 8 +#endif + +/** + * JIT_BLACKLIST_SIZE: Specifies the size of the blacklist used for + * tracking regions of memory that should not be translated due to runtime + * modifications by nearby code. A smaller blacklist size increases the + * speed of handling writes to such regions as well as the speed of code + * translation, but also increases the chance of thrashing on the table, + * which can significantly degrade performance. + */ +#ifndef JIT_BLACKLIST_SIZE +# define JIT_BLACKLIST_SIZE 10 +#endif + +/** + * JIT_BLACKLIST_EXPIRE: Specifies the time after which a blacklist entry + * will expire if it has not been written to. The unit is calls to + * jit_exec(), so the optimal value will need to be found through + * experimentation. + */ +#ifndef JIT_BLACKLIST_EXPIRE +# define JIT_BLACKLIST_EXPIRE 1000000 +#endif + +/** + * JIT_PURGE_TABLE_SIZE: Specifies the size of the purge table, which + * holds addresses of SH-2 code blocks whose translations have been purged + * from memory due to failure of optimization preconditions. A larger + * table size slows down the translation of code blocks as well as the + * handling of a purge operation. + * + * This value is also used for the size of the "pending blacklist" table, + * used to track addresses which cause jit_clear_write() faults many times + * in rapid succession so that they can be blacklisted to avoid repeated + * retranslation of the affected blocks. (This latter table is essentially + * the equivalent of the purge table for data accesses.) + */ +#ifndef JIT_PURGE_TABLE_SIZE +# define JIT_PURGE_TABLE_SIZE 16 +#endif + +/** + * JIT_PURGE_THRESHOLD: Specifies the number of purges on a single block + * after which all optimizations which require precondition checks are + * disabled for that block. This prevents blocks which take varying + * parameters (pointers to different memory regions, for example) from + * requiring a retranslation on potentially every call. This value must + * be greater than 1 (a value of 1 may cause incorrect operation). + * + * This value is also used for the blacklisting threshold of the pending + * blacklist table. + */ +#ifndef JIT_PURGE_THRESHOLD +# define JIT_PURGE_THRESHOLD 3 +#endif + +/** + * JIT_PURGE_EXPIRE: Specifies the time after which a purge table entry + * will expire. The unit is calls to jit_exec(), so the optimal value will + * need to be found through experimentation. + * + * This value is also used for the expiration time of entries in the + * pending blacklist table. + */ +#ifndef JIT_PURGE_EXPIRE +# define JIT_PURGE_EXPIRE 100000 +#endif + +/*============ Code generation options ============*/ + +/** + * JIT_USE_RTL_REGIMM: When defined, causes the JIT core to use the RTL + * register-immediate instructions (RTLOP_ADDI, etc.); when not defined, + * the JIT core will instead load immediate operands into registers which + * are then used with the register-register form of the instruction. + * + * For MIPS, the register-immediate instructions are a significant win. + */ +#define JIT_USE_RTL_REGIMM + +/** + * JIT_USE_RTL_BITFIELDS: When defined, causes the JIT core to use the RTL + * bitfield manipulation instructions (RTLOP_BFINS and RTLOP_BFEXT) where + * convenient; when not defined, appropriate combinations of AND, OR, + * and shifts will be used instead. + * + * This is generally a win on any architecture supporting bitfield + * manipulation instructions, including the MIPS Allegrex (PSP) + * architecture. + */ +#define JIT_USE_RTL_BITFIELDS + +/*============ Optimization options ============*/ + +/** + * OPTIMIZE_IDLE: When defined, attempts to find "idle loops", i.e. loops + * which continue indefinitely until some external event occurs, and modify + * their behavior to increase processing speed. Specifically, when an idle + * loop finishes an iteration and branches back to the beginning of the + * loop, the virtual processor will consume all pending execution cycles + * immediately rather than continue executing the loop until the requested + * number of cycles have passed. + * + * This optimization will slightly change execution timing as compared to + * real hardware, since the number of cycles per loop is ignored when + * consuming pending cycles. + */ +#define OPTIMIZE_IDLE + +/** + * OPTIMIZE_IDLE_MAX_INSNS: When OPTIMIZE_IDLE is defined, specifies the + * maximum number of instructions to consider when looking at a single + * potential idle loop. + */ +#ifndef OPTIMIZE_IDLE_MAX_INSNS +# define OPTIMIZE_IDLE_MAX_INSNS 8 +#endif + +/** + * OPTIMIZE_DELAY: When defined, modifies the behavior of delay loops of + * the form + * label: DT Rn + * BF label + * to increase their execution speed. + * + * This optimization alters trace output in TRACE and TRACE_STEALTH modes, + * but does not change TRACE_LITE trace output. + */ +#define OPTIMIZE_DELAY + +/** + * OPTIMIZE_DELAY_OMIT_MAX: Specifies the maximum number of iterations for + * omitting delay loops entirely from the translated code. + * + * When OPTIMIZE_DELAY is defined and a delay loop with a known number of + * iterations is found, if that known number of iterations is no greater + * than this value, the loop will be optimized out completely and replaced + * with code to consume the appropriate number of cycles (4 cycles per + * iteration) and clear the counter register to zero. + */ +#ifndef OPTIMIZE_DELAY_OMIT_MAX +# define OPTIMIZE_DELAY_OMIT_MAX 100 +#endif + +/** + * OPTIMIZE_DIVISION: When defined, attempts to find instruction sequences + * that perform division operations and replace them with native division + * instructions. This can achieve a speed increase of an order of + * magnitude or more with respect to the division operation. + * + * This optimization alters trace output in TRACE and TRACE_STEALTH modes, + * but does not change TRACE_LITE trace output. + */ +#define OPTIMIZE_DIVISION + +/** + * OPTIMIZE_SHIFT_SEQUENCES: When defined, replaces sequences of similar + * shift instructions with a single native shift instruction of the total + * count. The following replacements are performed: + * + * - Zero or more SHLL{2,8,16} Rn followed by one or more SH[AL]L Rn + * ==> SLLI(Rn,Rn,count) and set T + * - One or more SHLL{2,8,16} Rn _not_ followed by SH[AL]L Rn + * ==> SLLI(Rn,Rn,count) + * + * - Zero or more SHLR{2,8,16} Rn followed by one or more SHLR Rn + * ==> SRLI(Rn,Rn,count) and set T + * - One or more SHLR{2,8,16} Rn _not_ followed by SHLR Rn + * ==> SRLI(Rn,Rn,count) + * + * - One or more SHAR Rn ==> SRAI(Rn,Rn,count) + * + * - One or more ROTL Rn ==> RORI(Rn,Rn,(32-count)) + * + * - One or more ROTR Rn ==> RORI(Rn,Rn,count) + * + * This optimization alters trace output in TRACE and TRACE_STEALTH modes, + * but does not change TRACE_LITE trace output. + */ +#define OPTIMIZE_SHIFT_SEQUENCES + +/** + * OPTIMIZE_VARIABLE_SHIFTS: When defined, replaces instruction sequences + * that perform variable-count shifts with shorter equivalent sequences of + * RTL instructions, potentially allowing multiple branch instructions to + * be eliminated. + * + * This optimization alters trace output in TRACE and TRACE_STEALTH modes, + * and since it may eliminate branches, it may alter trace output in + * TRACE_LITE mode as well. + */ +#define OPTIMIZE_VARIABLE_SHIFTS + +/** + * OPTIMIZE_KNOWN_VALUES: When defined, tracks which bits of which + * registers have known values and, when possible, performs calculations + * using those values at translation time rather than runtime. + * + * This optimization can cause the timing of cycle count checks to change, + * and therefore alters trace output in all trace modes. + */ +#define OPTIMIZE_KNOWN_VALUES + +/** + * OPTIMIZE_BRANCH_FALLTHRU: When defined, checks for branches which + * branch to the next instruction to be translated and converts them to + * native no-ops. + * + * This optimization alters trace output in TRACE and TRACE_STEALTH modes, + * but does not change TRACE_LITE trace output. + */ +#define OPTIMIZE_BRANCH_FALLTHRU + +/** + * OPTIMIZE_BRANCH_THREAD: When defined, checks for conditional branches + * which branch to another conditional branch of the same sense and + * "threads" the branch through to the final target. This sort of branch + * chain can arise in long blocks of code due to the limited range of the + * conditional branch instructions (-128...+127 instructions). + * + * This optimization alters trace output in all trace modes. + */ +#define OPTIMIZE_BRANCH_THREAD + +/** + * OPTIMIZE_BRANCH_SELECT: When defined, checks for conditional branches + * whose only use is to choose between one of two values for a register, + * and converts such branches into native SELECT operations. + * + * This optimization alters trace output in TRACE mode. + */ +#define OPTIMIZE_BRANCH_SELECT + +/** + * OPTIMIZE_LOOP_TO_JSR: When defined, checks for backward branches that + * target a subroutine call (JSR, BSR, or BSRF) immediately preceding the + * beginning of the current block, and encodes the subroutine call along + * with its delay slot as part of the backward branch, avoiding the need + * to jump to a separate block just for the subroutine call. + * + * This optimization alters trace output in all trace modes. + */ +#define OPTIMIZE_LOOP_TO_JSR + +/** + * OPTIMIZE_STATE_BLOCK: When defined, attempts to minimize the number of + * state block accesses (loads and stores) by keeping live as long as + * possible each RTL register that holds a state block value. Values are + * flushed to memory when branching, and all cached values are cleared at + * branch targets. + */ +#define OPTIMIZE_STATE_BLOCK + +/** + * OPTIMIZE_CONSTANT_ADDS: When defined, accumulates constants added to or + * subtracted from a register, either immediate values in ADD #imm or + * offsets resulting from postincrement/predecrement memory accesses, and + * attempts to minimize the number of actual ADD instructions used to + * update the register in RTL code. + * + * This optimization relies on the following assumptions: + * + * - Offsets will not cause the final address to cross a page (2^19 byte) + * boundary. + * + * - Offsetted stores will not overwrite any code that a store to the + * non-offsetted address would not overwrite. + * + * If either of these assumptions are violated, the translated code will + * behave incorrectly and may crash the host program. + * + * Depends on OPTIMIZE_STATE_BLOCK; if OPTIMIZE_STATE_BLOCK is not defined, + * this optimization will not take place. + */ +#define OPTIMIZE_CONSTANT_ADDS + +/** + * OPTIMIZE_LOOP_REGISTERS: When defined, attempts to keep SH-2 registers + * and other state block fields used in a loop live in RTL registers for + * the duration of the loop, rather than reloading and flushing on each + * iteration. Registers which are only set within the loop (i.e., whose + * final value does not depend on the value of the register at the + * beginning of the loop) are not treated specially. + * + * Loops which include internal forward branches and other sufficiently + * complex loops will not be optimized. + * + * Depends on OPTIMIZE_STATE_BLOCK; if OPTIMIZE_STATE_BLOCK is not defined, + * this optimization will not take place. + */ +#define OPTIMIZE_LOOP_REGISTERS + +/** + * OPTIMIZE_LOOP_REGISTERS_MAX_REGS: Specifies the maximum number of RTL + * registers to keep live over a loop. Higher values minimize reload + * operations at the RTL generation level, but may increase reloads at the + * native code level due to register pressure. + */ +#ifndef OPTIMIZE_LOOP_REGISTERS_MAX_REGS +# define OPTIMIZE_LOOP_REGISTERS_MAX_REGS 12 +#endif + +/** + * OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD: Specifies the number of + * references to an unoptimizable pointer which will cause the block to be + * terminated immediately before the first access (thus providing another + * chance to optimize the pointer). A value of 1 will be treated as 2; a + * value of 0 disables this check entirely. + */ +#ifndef OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD +# define OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD 3 +#endif + +/** + * OPTIMIZE_FOLD_SUBROUTINES_MAX_LENGTH: Specifies the maximum number of + * instructions in a subroutine (excluding the terminating RTS and its + * delay slot) for the subroutine to qualify as foldable for the + * SH2_OPTIMIZE_FOLD_SUBROUTINES optimization. + */ +#ifndef OPTIMIZE_FOLD_SUBROUTINES_MAX_LENGTH +# define OPTIMIZE_FOLD_SUBROUTINES_MAX_LENGTH 16 +#endif + +/** + * JIT_OPTIMIZE_FLAGS: Specifies the optimizations that should be + * performed on the generated RTL code. See RTLOPT_* in rtl.h for details + * on the available flags. + */ +#ifndef JIT_OPTIMIZE_FLAGS +# define JIT_OPTIMIZE_FLAGS 0 // Optimization doesn't currently win us much +#endif + +/*============ Debugging options ============*/ + +/** + * TRACE: When defined, all instructions and all store operations are + * traced using the functions passed to sh2_trace_insn_callback() and + * sh2_trace_store[bwl]_callback(). + */ +// #define TRACE + +/** + * TRACE_STEALTH: When defined, all instructions and all store operations + * are traced in a way that does not affect the behavior of the generated + * code. Where TRACE inserts RTL instructions to flush cached values and + * call the relevant trace functions (thus updating memory more often than + * usual and potentially hiding bugs), TRACE_STEALTH inserts specially- + * coded NOP instructions that inform the RTL interpreter and native code + * translators about cached values and direct it to call the tracing + * functions itself, thus not affecting the behavior of the RTL code. + * (This requires significantly more overhead than regular tracing with the + * TRACE option.) Note that it is also necessary to define + * RTL_TRACE_STEALTH_FOR_SH2 in rtl-internal.h to enable support for this + * option. + * + * Due to optimization (such as clobbering of dead registers), + * TRACE_STEALTH is likely to not work correctly with native code. + * + * TRACE takes precedent over TRACE_STEALTH; if TRACE is defined, then + * TRACE_STEALTH is ignored and tracing code is added directly to the RTL + * code stream. + */ +// #define TRACE_STEALTH + +/** + * TRACE_LITE: When defined, traces instructions at the rate of one per + * call to sh2_run(). This allows the progress of execution to be + * monitored without the significant overhead imposed by inserting trace + * calls for every instruction in the code stream. + * + * TRACE and TRACE_STEALTH take precedence over TRACE_LITE; if either of + * the former two are defined, TRACE_LITE is ignored rather than causing a + * duplicate trace to be output at the beginning of an sh2_run() call. + */ +// #define TRACE_LITE + +/** + * TRACE_LITE_VERBOSE: When defined and when TRACE_LITE is also enabled, + * additionally traces once per call to jit_exec() (except the first in + * each sh2_run() call, to avoid a double trace). + */ +// #define TRACE_LITE_VERBOSE + +/** + * DEBUG_DECODER_INSN_COVERAGE: When defined, a debug line is printed the + * first time the SH-2 decoder encounters each instruction (specifically, + * each opcode pattern handled by a distinct code block). This can be used + * to check the coverage of test runs. + */ +// #define DEBUG_DECODER_INSN_COVERAGE + +/** + * JIT_DEBUG: When defined, debug messages are output in cases that may + * indicate a problem in the translation or optimization of SH-2 code. + */ +// #define JIT_DEBUG + +/** + * JIT_DEBUG_VERBOSE: When defined, additional debug messages are output + * in certain cases considered useful in fine-tuning the translation and + * optimization. + */ +// #define JIT_DEBUG_VERBOSE + +/** + * JIT_DEBUG_TRACE: When defined, a trace line is printed for each SH-2 + * instruction translated. This option is independent of the other trace + * options. + */ +// #define JIT_DEBUG_TRACE + +/** + * JIT_DEBUG_INSERT_PC: When defined, causes the native code generator to + * insert dummy instructions at the beginning of the code for each SH-2 + * instruction, indicating the SH-2 PC for that instruction. The dummy + * instructions are of the form: + * (RTL) + * nop 0x12345678 + * (MIPS) + * lui $zero, 0x1234 + * ori $zero, $zero, 0x5678 + * for SH-2 PC 0x12345678. + */ +// #define JIT_DEBUG_INSERT_PC + +/** + * JIT_DEBUG_INTERPRET_RTL: When defined, causes the JIT core to execute + * RTL instruction sequences directly rather than translating them into + * MIPS machine code. + * + * This is currently forced on when not compiling on PSP since the only + * available RTL->native translator at the moment is the MIPS translator. + */ +// #define JIT_DEBUG_INTERPRET_RTL + +/** + * JIT_PROFILE: When defined, counts the number of times each code block + * is executed and the time spent in execution. Every JIT_PROFILE_INTERVAL + * SH-2 clock cycles, the first JIT_PROFILE_TOP callees in terms of + * execution time and number of calls are printed. + */ +// #define JIT_PROFILE +#ifndef JIT_PROFILE_INTERVAL +# define JIT_PROFILE_INTERVAL 50000000 +#endif +#ifndef JIT_PROFILE_TOP +# define JIT_PROFILE_TOP 10 +#endif + +/** + * PSP_TIME_TRANSLATION: When defined, calculates the average amount of + * time required to translate a single SH-2 instruction. Only works on the + * PSP. + */ +// #define PSP_TIME_TRANSLATION + +/*************************************************************************/ + +/* Perform sanity checks on configuration options */ + +#if JIT_BRANCH_PREDICTION_SLOTS <= 0 +# undef JIT_BRANCH_PREDICT_STATIC +#endif + +#if JIT_PURGE_THRESHOLD < 2 +# undef JIT_PURGE_THRESHOLD +# define JIT_PURGE_THRESHOLD 2 +#endif + +#ifndef OPTIMIZE_STATE_BLOCK +# undef OPTIMIZE_CONSTANT_ADDS +# undef OPTIMIZE_LOOP_REGISTERS +#endif + +#if !defined(TRACE) || !defined(OPTIMIZE_DIVISION) +# undef TRACE_OPTIMIZED_DIVISION +#endif + +#ifdef TRACE +# undef TRACE_STEALTH +# undef TRACE_LITE +#endif + +#ifdef TRACE_STEALTH +# undef TRACE_LITE +#endif + +#ifndef TRACE_LITE +# undef TRACE_LITE_VERBOSE +#endif + +#ifndef PSP +# define JIT_DEBUG_INTERPRET_RTL +# undef PSP_TIME_TRANSLATION +#endif + +/*************************************************************************/ +/************** Internal-use data and function declarations **************/ +/*************************************************************************/ + +/******** sh2.c ********/ + +/* Bitmask indicating which optional optimizations are enabled */ +extern uint32_t optimization_flags; + +/* Callback function for manual/special-case optimization */ +extern SH2OptimizeCallback *manual_optimization_callback; + +/* Callback function for native CPU cache flushing */ +extern SH2CacheFlushCallback *cache_flush_callback; + +/* Callback function for invalid instructions */ +extern SH2InvalidOpcodeCallback *invalid_opcode_callback; + +/* Callback functions for tracing */ +extern SH2TraceInsnCallback *trace_insn_callback; +extern SH2TraceAccessCallback *trace_storeb_callback; +extern SH2TraceAccessCallback *trace_storew_callback; +extern SH2TraceAccessCallback *trace_storel_callback; + +#ifdef ENABLE_JIT +/* Page tables (exported for use in sh2-optimize.c) */ +extern uint8_t *direct_pages[0x2000]; +extern uint8_t *fetch_pages[0x2000]; +extern uint8_t *byte_direct_pages[0x2000]; +extern uint8_t *direct_jit_pages[0x2000]; +#endif + +/** + * check_interrupts: Check whether there are any pending interrupts, and + * service the highest-priority one if so. + * + * [Parameters] + * state: Processor state block + * [Return value] + * Nonzero if an interrupt was serviced, else zero + */ +extern FASTCALL int check_interrupts(SH2State *state); + +/******** sh2-interpret.c ********/ + +/** + * interpret_insn: Interpret and execute a single SH-2 instruction at the + * current PC. + * + * [Parameters] + * state: SH-2 processor state + * [Return value] + * None + */ +extern void interpret_insn(SH2State *state); + + +/******** sh2-opcodeinfo.c ********/ + +/** + * SH2_OPCODE_INFO_*: Flags used in the get_opcode_info() return value. + * "Rn" always refers to the register specified by bits 8-11 of the opcode, + * and "Rm" always refers to bits 4-7 of the opcode, regardless of the + * labeling used in the official Hitachi specs. + */ +#define SH2_OPCODE_INFO_USES_R0 (1<< 0) // Uses the value of R0 +#define SH2_OPCODE_INFO_USES_Rm (1<< 1) // Uses the value of Rm +#define SH2_OPCODE_INFO_USES_Rn (1<< 2) // Uses the value of Rn +#define SH2_OPCODE_INFO_USES_R15 (1<< 3) // Uses the value of R15 +#define SH2_OPCODE_INFO_SETS_R0 (1<< 4) // Sets the value of R0 + /* 1<< 5 is unused */ +#define SH2_OPCODE_INFO_SETS_Rn (1<< 6) // Sets the value of Rn +#define SH2_OPCODE_INFO_SETS_SR_T (1<< 7) // Sets the value of SR.T +#define SH2_OPCODE_INFO_ACCESSES_GBR (1<< 8) // Accesses memory through GBR +#define SH2_OPCODE_INFO_ACCESSES_Rm (1<< 9) // Accesses memory through Rm +#define SH2_OPCODE_INFO_ACCESSES_Rn (1<<10) // Accesses memory through Rn +#define SH2_OPCODE_INFO_ACCESSES_R15 (1<<11) // Accesses memory through R15 +#define SH2_OPCODE_INFO_ACCESSES_R0_GBR (1<<12) // Accesses memory thru R0+GBR +#define SH2_OPCODE_INFO_ACCESSES_R0_Rm (1<<13) // Accesses memory thru R0+Rm +#define SH2_OPCODE_INFO_ACCESSES_R0_Rn (1<<14) // Accesses memory thru R0+Rn +#define SH2_OPCODE_INFO_ACCESSES_PC (1<<15) // Accesses memory through PC +#define SH2_OPCODE_INFO_ACCESS_IS_STORE (1<<16) // Memory access is a store +#define SH2_OPCODE_INFO_ACCESS_IS_RMW (1<<17) // Read/modify/write operation +#define SH2_OPCODE_INFO_ACCESS_POSTINC (1<<18) // Postincrement access mode +#define SH2_OPCODE_INFO_ACCESS_PREDEC (1<<19) // Predecrement access mode +#define SH2_OPCODE_INFO_ACCESS_SIZE_B (1<<20) // Memory access is size 1 +#define SH2_OPCODE_INFO_ACCESS_SIZE_W (1<<21) // Memory access is size 2 +#define SH2_OPCODE_INFO_ACCESS_SIZE_L (1<<22) // Memory access is size 4 +#define SH2_OPCODE_INFO_ACCESS_SIZE_LL (1<<23) // Memory access is size 8 +#define SH2_OPCODE_INFO_ACCESS_DISP_4 (1<<24) // 4-bit displacement +#define SH2_OPCODE_INFO_ACCESS_DISP_8 (1<<25) // 8-bit displacement +#define SH2_OPCODE_INFO_BRANCH_UNCOND (1<<26) // Branches unconditionally +#define SH2_OPCODE_INFO_BRANCH_COND (1<<27) // Branches conditionally +#define SH2_OPCODE_INFO_BRANCH_DELAYED (1<<28) // Branches after a delay slot +#define SH2_OPCODE_INFO_VALID (1<<31) // Opcode is valid + +/** + * SH2_OPCODE_INFO_ACCESS_SIZE: Returns the size in bytes of an access + * performed by an instruction, given that instruction's get_opcode_info() + * value. + */ +#define SH2_OPCODE_INFO_ACCESS_SIZE(opcode_info) (((opcode_info) >> 20) & 0xF) + +/** + * init_opcode_info: Initialize the opcode_info[] table. Must be called + * before calling get_opcode_info(). + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void init_opcode_info(void); + +/** + * get_opcode_info: Return information about the given opcode. + * + * [Parameters] + * opcode: SH-2 opcode to obtain information about + * [Return value] + * + */ +#ifdef __GNUC__ +__attribute__((const)) +#endif +extern int32_t get_opcode_info(uint16_t opcode); + +/******** sh2-optimize.c ********/ + +#ifdef OPTIMIZE_IDLE + +/** + * can_optimize_idle: Return whether the given sequence of instructions + * forms an "idle loop", in which the processor remains in a constant state + * (or repeating sequence of states) indefinitely until a certain external + * event occurs, such as an interrupt or a change in the value of a memory- + * mapped register. If an idle loop is detected, also return information + * allowing the loop to be translated into a faster sequence of native + * instructions. + * + * The sequence of instructions is assumed to end with a branch instruction + * to the beginning of the sequence (possibly including a delay slot). + * + * [Parameters] + * insn_ptr: Pointer to first instruction + * PC: PC of first instruction + * count: Number of instructions to check + * [Return value] + * Nonzero if the given sequence of SH-2 instructions form an idle + * loop, else zero + */ +extern int can_optimize_idle(const uint16_t *insn_ptr, uint32_t PC, + unsigned int count); + +#endif // OPTIMIZE_IDLE + +#ifdef OPTIMIZE_DELAY + +/** + * can_optimize_delay: Return whether the given sequence of instructions + * forms a "delay loop", in which a counter register is repeatedly + * decremented with the DT instruction until it reaches zero. + * + * The sequence of instructions is assumed to end with a branch instruction + * to the beginning of the sequence (possibly including a delay slot). + * + * [Parameters] + * insn_ptr: Pointer to first instruction + * PC: PC of first instruction + * count: Number of instructions to check + * counter_ret: Pointer to variable to receive counter register index + * if a delay loop (unmodified if not a delay loop) + * [Return value] + * Number of clock cycles taken by the loop (nonzero) if the given + * sequence of SH-2 instructions form a delay loop, else zero + */ +extern int can_optimize_delay(const uint16_t *insn_ptr, uint32_t PC, + unsigned int count, unsigned int *counter_ret); + +#endif // OPTIMIZE_DELAY + +#ifdef OPTIMIZE_DIVISION + +/** + * can_optimize_div0u: Return whether a sequence of instructions starting + * from a DIV0U instruction can be optimized to a native divide operation. + * + * [Parameters] + * insn_ptr: Pointer to DIV0U instruction + * PC: PC of DIV0U instruction + * skip_first_rotcl: Nonzero if the first ROTCL instruction is known to + * be omitted (as may happen if the low word of + * the dividend is known to be zero) + * Rhi_ret: Pointer to variable to receive index of dividend high register + * Rlo_ret: Pointer to variable to receive index of dividend low register + * Rdiv_ret: Pointer to variable to receive index of divisor register + * [Return value] + * Number of bits of division performed by the instructions following + * the DIV0U instruction (1-32), or zero if the following instructions + * do not perform a division operation + */ +extern int can_optimize_div0u(const uint16_t *insn_ptr, uint32_t PC, + int skip_first_rotcl, + int *Rhi_ret, int *Rlo_ret, int *Rdiv_ret); + +/** + * can_optimize_div0s: Return whether a sequence of instructions starting + * from a DIV0S instruction can be optimized to a native divide operation. + * + * [Parameters] + * insn_ptr: Pointer to instruction following DIV0S instruction + * PC: PC of instruction following DIV0S instruction + * Rhi: Index of dividend high register + * Rlo_ret: Pointer to variable to receive index of dividend low register + * Rdiv: Index of divisor register + * [Return value] + * Nonzero if the next 64 SH-2 instructions form a 32-bit division + * operation, else zero + */ +extern int can_optimize_div0s(const uint16_t *insn_ptr, uint32_t PC, + int Rhi, int *Rlo_ret, int Rdiv); + +#endif // OPTIMIZE_DIVISION + +#ifdef OPTIMIZE_VARIABLE_SHIFTS + +/** + * can_optimize_variable_shift: Return whether a sequence of instructions + * can be optimized to a native variable-count shift operation. + * + * [Parameters] + * insn_ptr: Pointer to first instruction + * PC: PC of first instruction + * Rcount_ret: Pointer to variable to receive index of shift count register + * max_ret: Pointer to variable to receive maximum shift count + * Rshift_ret: Pointer to variable to receive index of target register + * type_ret: Pointer to variable to receive: + * 0 if a SHLL/SHAL sequence + * 1 if a SHLR sequence + * 2 if a SHAR sequence + * 3 if a ROTL sequence + * 4 if a ROTR sequence + * cycles_ret: Pointer to variable to receive pointer to an array of + * cycle counts indexed by shift count (unused for some + * types of sequences) + * [Return value] + * Number of instructions consumed (nonzero) if an optimizable sequence + * is found, else zero + */ +extern unsigned int can_optimize_variable_shift( + const uint16_t *insn_ptr, uint32_t PC, unsigned int *Rcount_ret, + unsigned int *max_ret, unsigned int *Rshift_ret, unsigned int *type_ret, + const uint8_t **cycles_ret); + +#endif // OPTIMIZE_VARIABLE_SHIFTS + +/*************************************************************************/ +/*************************************************************************/ + +#endif // SH2_INTERNAL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2-interpret.c b/yabause/src/psp/sh2-interpret.c new file mode 100644 index 0000000000..6866075100 --- /dev/null +++ b/yabause/src/psp/sh2-interpret.c @@ -0,0 +1,498 @@ +/* src/psp/sh2-interpret.c: Instruction interpreter for SH-2 emulator + (mostly for debugging) + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "../sh2core.h" + +#include "sh2.h" +#include "sh2-internal.h" + +/*************************************************************************/ +/***************** SH-2 interpreted execution interface ******************/ +/*************************************************************************/ + +/* Declare a register identifier, but don't allocate a new register for it */ +#define DECLARE_REG(name) uintptr_t name + +/* Allocate a register for a declared identifier */ +#define ALLOC_REG(name) /*nothing*/ + +/* Define a new register (equivalent to DECLARE_REG followed by ALLOC_REG) */ +#define DEFINE_REG(name) uintptr_t name + +/* Register-register operations */ +#define MOVE(dest,src) ((dest) = (src)) +#define SELECT(dest,src1,src2,cond) ((dest) = (cond) ? (src1) : (src2)) +#define ADD(dest,src1,src2) ((dest) = (src1) + (intptr_t)(int32_t)(src2)) +#define SUB(dest,src1,src2) ((dest) = (src1) - (intptr_t)(int32_t)(src2)) +#define MUL(dest,src1,src2) ((dest) = (uint32_t)((src1) * (src2))) +#define MULU_64(dest,src1,src2,dest_hi) do { \ + (dest) = (uint32_t)((src1) * (src2)); \ + (dest_hi) = (uint64_t)((uint64_t)(uint32_t)(src1) \ + * (uint64_t)(uint32_t)(src2)) >> 32; \ +} while (0) +#define MULS_64(dest,src1,src2,dest_hi) do { \ + (dest) = (uint32_t)((src1) * (src2)); \ + (dest_hi) = (int64_t)((int64_t)(int32_t)(src1) \ + * (int64_t)(int32_t)(src2)) >> 32; \ +} while (0) +#define MADDU_64(dest,src1,src2,dest_hi) do { \ + uint64_t initial = (uint64_t)(uint32_t)(dest_hi) << 32 \ + | (uint64_t)(uint32_t)(dest); \ + uint64_t product = (uint64_t)(uint32_t)(src1) \ + * (uint64_t)(uint32_t)(src2); \ + uint64_t result = initial + product; \ + (dest) = (uint32_t)result; \ + (dest_hi) = (uint32_t)(result >> 32); \ +} while (0) +#define MADDS_64(dest,src1,src2,dest_hi) do { \ + uint64_t initial = (int64_t)(int32_t)(dest_hi) << 32 \ + | (int64_t)(uint32_t)(dest); \ + uint64_t product = (int64_t)(int32_t)(src1) \ + * (int64_t)(int32_t)(src2); \ + uint64_t result = initial + product; \ + (dest) = (uint32_t)result; \ + (dest_hi) = (uint32_t)(result >> 32); \ +} while (0) +#define DIVMODU(dest,src1,src2,rem) do { \ + if ((src2) != 0) { \ + (dest) = (uint32_t)(src1) / (uint32_t)(src2); \ + (rem) = (uint32_t)(src1) % (uint32_t)(src2); \ + } else { \ + /* Have to set these to avoid a compiler warning */ \ + (dest) = 0; \ + (rem) = 0; \ + } \ +} while (0) +#define DIVMODS(dest,src1,src2,rem) do { \ + if ((src2) != 0) { \ + (dest) = (int32_t)(src1) / (int32_t)(src2); \ + (rem) = (int32_t)(src1) % (int32_t)(src2); \ + } else { \ + /* Have to set these to avoid a compiler warning */ \ + (dest) = 0; \ + (rem) = 0; \ + } \ +} while (0) +#define AND(dest,src1,src2) ((dest) = (src1) & (intptr_t)(int32_t)(src2)) +#define OR(dest,src1,src2) ((dest) = (src1) | (uintptr_t)(uint32_t)(src2)) +#define XOR(dest,src1,src2) ((dest) = (src1) ^ (uintptr_t)(uint32_t)(src2)) +#define NOT(dest,src) ((dest) = (uint32_t)(~(src))) +#define SLL(dest,src1,src2) ((dest) = (uint32_t)((src1) << (src2))) +#define SRL(dest,src1,src2) ((dest) = (uint32_t)(src1) >> (src2)) +#define SRA(dest,src1,src2) ((dest) = (int32_t)(src1) >> (src2)) +#define ROR(dest,src1,src2) \ + ((dest) = (((src2) & 31) \ + ? (uint32_t)(src1) >> ((src2) & 31) \ + | (uint32_t)(src1) << (31-((src2) & 31)) \ + : (uint32_t)(src1))) +#ifdef __GNUC__ +# define CLZ(dest,src) ((dest) = __builtin_clz((src))) +#else +# define CLZ(dest,src) do { \ + uint32_t __temp = (src); \ + (dest) = 32; \ + while (__temp) { \ + __temp >>= 1; \ + (dest)--; \ + } \ +} while (0) +#endif // __GNUC__ +#define CLO(dest,src) do { \ + uint32_t __temp = (src); \ + (dest) = 0; \ + while ((int32_t)__temp < 0) {\ + __temp <<= 1; \ + (dest)++; \ + } \ +} while (0) +#define SLTU(dest,src1,src2) ((dest) = (uint32_t)(src1) < (uint32_t)(src2)) +#define SLTS(dest,src1,src2) ((dest) = (int32_t)(src1) < (int32_t)(src2)) +#define BSWAPH(dest,src) \ + ((dest) = ((uint32_t)(src) & 0xFF00FF00) >> 8 \ + | ((uint32_t)(src) & 0x00FF00FF) << 8) +#define BSWAPW(dest,src) \ + ((dest) = ((uint32_t)(src) & 0xFF000000) >> 24 \ + | ((uint32_t)(src) & 0x00FF0000) >> 8 \ + | ((uint32_t)(src) & 0x0000FF00) << 8 \ + | ((uint32_t)(src) & 0x000000FF) << 24) +#define HSWAPW(dest,src) \ + ((dest) = ((uint32_t)(src) & 0xFFFF0000) >> 16 \ + | ((uint32_t)(src) & 0x0000FFFF) << 16) + +/* Register-immediate operations */ +#define MOVEI(dest,imm) ((dest) = (imm)) +#define MOVEA(dest,addr) ((dest) = (uintptr_t)(addr)) +#define ADDI(dest,src,imm) ((dest) = (src) + (imm)) +#define SUBI(dest,src,imm) ((dest) = (src) - (imm)) +#define ANDI(dest,src,imm) ((dest) = (uint32_t)((src) & (imm))) +#define ORI(dest,src,imm) ((dest) = (uint32_t)((src) | (imm))) +#define XORI(dest,src,imm) ((dest) = (src) ^ (imm)) +#define SLLI(dest,src,imm) ((dest) = (uint32_t)((src) << (imm))) +#define SRLI(dest,src,imm) ((dest) = (uint32_t)(src) >> (imm)) +#define SRAI(dest,src,imm) ((dest) = (int32_t)(src) >> (imm)) +#define RORI(dest,src,imm) \ + ((dest) = (((imm) & 31) \ + ? (uint32_t)(src) >> ((imm) & 31) \ + | (uint32_t)(src) << (32-((imm) & 31)) \ + : (uint32_t)(src))) +#define SLTUI(dest,src,imm) ((dest) = (uint32_t)(src) < (uint32_t)(imm)) +#define SLTSI(dest,src,imm) ((dest) = (int32_t)(src) < (int32_t)(imm)) + +/* Bitfield operations */ +#define BFEXT(dest,src,start,count) \ + ((dest) = ((uint32_t)(src) >> (start)) & ((1 << (count)) - 1)) +#define BFINS(dest,src1,src2,start,count) \ + ((dest) = ((uint32_t)(src1) & ~(((1 << (count)) - 1) << (start))) \ + | (((uint32_t)(src2) & ((1 << (count)) - 1)) << (start))) + +/* Variants of SLT */ +#define SEQZ(dest,src) SLTUI((dest), (src), 1) +#define SLTZ(dest,src) SLTSI((dest), (src), 0) + +/* Load from or store to memory */ +#define LOAD_BU(dest,address,offset) \ + ((dest) = *(uint8_t *)((address)+(offset))) +#define LOAD_BS(dest,address,offset) \ + ((dest) = *(int8_t *)((address)+(offset))) +#define LOAD_HU(dest,address,offset) \ + ((dest) = *(uint16_t *)((address)+(offset))) +#define LOAD_HS(dest,address,offset) \ + ((dest) = *(int16_t *)((address)+(offset))) +#define LOAD_W(dest,address,offset) \ + ((dest) = *(uint32_t *)((address)+(offset))) +#define LOAD_PTR(dest,address,offset) \ + ((dest) = *(uintptr_t *)((address)+(offset))) +#define STORE_B(address,src,offset) \ + (*(uint8_t *)((address)+(offset)) = (src)) +#define STORE_H(address,src,offset) \ + (*(uint16_t *)((address)+(offset)) = (src)) +#define STORE_W(address,src,offset) \ + (*(uint32_t *)((address)+(offset)) = (src)) +#define STORE_PTR(address,src,offset) \ + (*(uintptr_t *)((address)+(offset)) = (src)) + +/* Load from, store to, or add constants to state block fields */ +#define LOAD_STATE(reg,field) ((reg) = state->field) +#define LOAD_STATE_PTR(reg,field) ((reg) = (uintptr_t)state->field) +#define LOAD_STATE_SR_T(reg) ((reg) = (state->SR & SR_T) >> SR_T_SHIFT) +#define STORE_STATE(field,reg) (state->field = (reg)) +#define STORE_STATE_PC(value) (state->PC = (value)) +#define STORE_STATE_B(field,reg) (state->field = (reg)) +#define STORE_STATE_PTR(field,reg) (state->field = (void *)(reg)) +#define STORE_STATE_SR_T(reg) (state->SR &= ~SR_T, \ + state->SR |= ((reg) & 1) << SR_T_SHIFT) +#define FLUSH_STATE_SR_T() /*nothing*/ +#define RESET_STATE_SR_T() /*nothing*/ +#define ADDI_STATE(field,imm,reg) (state->field = (reg) + (imm)) +#define ADDI_STATE_NOREG(field,imm) (state->field += (imm)) + +/* Load from a state block field, but don't change the state block cache */ +#define LOAD_STATE_COPY(name,field) LOAD_STATE(name,field) + +/* Allocate a new register and load it from the state block, or reuse an + * old register if appropriate */ +#define LOAD_STATE_ALLOC(name,field) ALLOC_REG(name); LOAD_STATE(name,field) + +/* Allocate a new register and load it from the state block, or reuse an + * old register (leaving any offset in the cache) if appropriate */ +#define LOAD_STATE_ALLOC_KEEPOFS(name,field) LOAD_STATE_ALLOC(name,field) + +/* Execute an SH-2 load or store operation (note that size desginations are + * SH-2 style B[yte]/W[ord]/L[ong] rather than RTL B[yte]/H[alfword]/W[ord], + * and all 8- and 16-bit loads are signed) */ + +#define SH2_LOAD_B(dest,address) \ + ((dest) = (int8_t)MappedMemoryReadByte((address))) +#define SH2_LOAD_W(dest,address) \ + ((dest) = (int16_t)MappedMemoryReadWord((address))) +#define SH2_LOAD_L(dest,address) \ + ((dest) = MappedMemoryReadLong((address))) + +#ifdef TRACE +# define LOG_STORE(address,src,type) ((*trace_store##type##_callback)((address), (src))) +#else +# define LOG_STORE(address,src,type) /*nothing*/ +#endif +#define SH2_STORE_B(address,src) do { \ + LOG_STORE((address), (src), b); \ + MappedMemoryWriteByte((address), (src)); \ +} while (0) +#define SH2_STORE_W(address,src) do { \ + LOG_STORE((address), (src), w); \ + MappedMemoryWriteWord((address), (src)); \ +} while (0) +#define SH2_STORE_L(address,src) do { \ + LOG_STORE((address), (src), l); \ + MappedMemoryWriteLong((address), (src)); \ +} while (0) + +/* Execute an SH-2 load or store to a known address */ +#define SH2_LOAD_ABS_B(dest,address) SH2_LOAD_B(dest,address) +#define SH2_LOAD_ABS_W(dest,address) SH2_LOAD_W(dest,address) +#define SH2_LOAD_ABS_L(dest,address) SH2_LOAD_L(dest,address) +#define SH2_STORE_ABS_B(address,src,islocal) SH2_STORE_B(address,src) +#define SH2_STORE_ABS_W(address,src,islocal) SH2_STORE_W(address,src) +#define SH2_STORE_ABS_L(address,src,islocal) SH2_STORE_L(address,src) + +/* Execute an SH-2 load or store through an SH-2 register */ +#define SH2_LOAD_REG_B(dest,sh2reg,offset,postinc) do { \ + SH2_LOAD_B(dest, state->R[sh2reg] + (offset)); \ + if (postinc) { \ + state->R[sh2reg] += 1; \ + } \ +} while (0) +#define SH2_LOAD_REG_W(dest,sh2reg,offset,postinc) do { \ + SH2_LOAD_W(dest, state->R[sh2reg] + (offset)); \ + if (postinc) { \ + state->R[sh2reg] += 2; \ + } \ +} while (0) +#define SH2_LOAD_REG_L(dest,sh2reg,offset,postinc) do { \ + SH2_LOAD_L(dest, state->R[sh2reg] + (offset)); \ + if (postinc) { \ + state->R[sh2reg] += 4; \ + } \ +} while (0) +#define SH2_STORE_REG_B(sh2reg,src,offset,predec) do { \ + if (predec) { \ + state->R[sh2reg] -= 1; \ + } \ + SH2_STORE_B(state->R[sh2reg] + (offset), src); \ +} while (0) +#define SH2_STORE_REG_W(sh2reg,src,offset,predec) do { \ + if (predec) { \ + state->R[sh2reg] -= 2; \ + } \ + SH2_STORE_W(state->R[sh2reg] + (offset), src); \ +} while (0) +#define SH2_STORE_REG_L(sh2reg,src,offset,predec) do { \ + if (predec) { \ + state->R[sh2reg] -= 4; \ + } \ + SH2_STORE_L(state->R[sh2reg] + (offset), src); \ +} while (0) + +/* Branches (within an SH-2 instruction's RTL code) */ +#define CREATE_LABEL(label) /*nothing*/ +#define DEFINE_LABEL(label) label: +#define GOTO_LABEL(label) goto label; +#define GOTO_IF_Z(label,reg) if ((reg) == 0) goto label; +#define GOTO_IF_NZ(label,reg) if ((reg) != 0) goto label; +#define GOTO_IF_E(label,reg1,reg2) if ((reg1) == (reg2)) goto label; +#define GOTO_IF_NE(label,reg1,reg2) if ((reg1) != (reg2)) goto label; + +/* Jumps (to other SH-2 instructions) */ +#define JUMP_STATIC() jumped = 1 +#define JUMP() jumped = 1 + +/* Call to a native subroutine */ +#define CALL(result,arg1,arg2,func) do { \ + FASTCALL uintptr_t (*__func)(uintptr_t,uintptr_t) = (void *)(uintptr_t)(func); \ + (result) = (*__func)((arg1), (arg2)); \ +} while (0) +#define CALL_NORET(arg1,arg2,func) do { \ + FASTCALL void (*__func)(uintptr_t,uintptr_t) = (void *)(uintptr_t)(func); \ + (*__func)((arg1), (arg2)); \ +} while (0) + +/* Return from the current block */ +#define RETURN() return 0 + +/*-----------------------------------------------------------------------*/ + +/* We don't have "registers", so alias state_reg directly to the pointer */ +#define state_reg ((uintptr_t)state) + +/* Access the state block directly for the PC */ +#define cur_PC (state->PC) + +/* No direct fetching */ +#define fetch ((uint16_t *)NULL) // uint16_t * to avoid compiler errors + +/* No pre- or post-decode processing needed */ +#define OPCODE_INIT(opcode) /*nothing*/ +#define OPCODE_DONE(opcode) /*nothing*/ + +/* cur_PC and REG_PC are the same thing, so only need to update one of them + * (but only do so for the default case if we didn't already set the PC via + * a jump) */ +#define INC_PC() do { \ + if (!jumped) { \ + cur_PC += 2; \ + } \ +} while (0) +#define INC_PC_BY(amount) (cur_PC += (amount)) + +/* Return whether the word at "offset" words from the current instruction + * is available for peephole optimization */ +#define INSN_IS_AVAILABLE(offset) 0 + +/* Return the "high" (pointer register) and "low" (load/store offset) parts + * of an address for generating optimal native load/store code */ +#define ADDR_HI(address) ((uintptr_t)address) +#define ADDR_LO(address) 0 + +/* Return whether the saturation check for MAC can be omitted */ +#define CAN_OMIT_MAC_S_CHECK 0 + +/* Get or set whether the MACL/MACH pair is known to be zero */ +#define MAC_IS_ZERO() 0 +#define SET_MAC_IS_ZERO() /*nothing*/ +#define CLEAR_MAC_IS_ZERO() /*nothing*/ + +/* Get, add to, or clear the cached shift count */ +#define CAN_CACHE_SHIFTS() 0 +#define CACHED_SHIFT_COUNT() 0 +#define ADD_TO_SHIFT_CACHE(n) /*nothing*/ +#define CLEAR_SHIFT_CACHE() /*nothing*/ + +/* Get or set register known bits and values */ +#define REG_GETKNOWN(reg) 0 +#define REG_GETVALUE(reg) 0 +#define REG_SETKNOWN(reg,value) /*nothing*/ +#define REG_SETVALUE(reg,value) /*nothing*/ + +/* Track pointer registers */ +#define PTR_ISLOCAL(reg) 0 +#define PTR_SETLOCAL(reg) /*nothing*/ +#define PTR_SET_SOURCE(reg,address) /*nothing*/ +#define PTR_CHECK(reg) 0 +#define PTR_COPY(reg,new,for_add) /*nothing*/ +#define PTR_CLEAR(reg) /*nothing*/ + +/* Save the current cache state */ +#define SAVE_STATE_CACHE() /*nothing*/ +/* Restore the saved cache state */ +#define RESTORE_STATE_CACHE() /*nothing*/ +/* Write back to the state block any cached, dirty state block values + * (but leave them dirty) */ +#define WRITEBACK_STATE_CACHE() /*nothing*/ +/* Flush all cached state block values */ +#define FLUSH_STATE_CACHE() /*nothing*/ +/* Return the cached offset for the given state block field, or 0 if none */ +#define STATE_CACHE_OFFSET(field) 0 +/* Return the fixed RTL register to use for the given state block field, + * or 0 if none */ +#define STATE_CACHE_FIXED_REG(field) 0 +/* Return whether the given state block field has a fixed RTL register that + * can be modified */ +#define STATE_CACHE_FIXED_REG_WRITABLE(field) 0 +/* Clear any fixed RTL register for the given state block field */ +#define STATE_CACHE_CLEAR_FIXED_REG(field) /*nothing*/ + +/* Check the status of a branch instruction */ +#define BRANCH_FALLS_THROUGH(addr) 0 +#define BRANCH_TARGETS_RTS(addr) 0 +#define BRANCH_IS_THREADED(addr) 0 +#define BRANCH_THREAD_TARGET(addr) 0 +#define BRANCH_THREAD_COUNT(addr) 0 +#define BRANCH_IS_SELECT(addr) 0 +#define BRANCH_IS_LOOP_TO_JSR(addr) 0 +#define BRANCH_IS_FOLDABLE_SUBROUTINE(addr) 0 +#define BRANCH_FOLD_TARGET(addr) 0 +#define BRANCH_FOLD_TARGET_FETCH(addr) NULL +#define BRANCH_FOLD_NATIVE_FUNC(addr) NULL + +/*************************************************************************/ + +/** + * decode_insn: Decode a single SH-2 instruction. Implements + * interpret_insn() using the shared decoder core. + * + * [Parameters] + * state: SH-2 processor state + * initial_PC: Equal to state->PC (used by OPTIMIZE_IDLE) + * jumped: Local register tracking whether a jump was performed + * [Return value] + * Decoded SH-2 opcode (not used) + */ +#define DECODE_INSN_INLINE NOINLINE +#define DECODE_INSN_PARAMS \ + SH2State *state, uint32_t initial_PC, int jumped +#define RECURSIVE_DECODE(address,is_last) do { \ + const uint32_t saved_PC = state->PC; \ + state->PC = (address); \ + interpret_insn(state); \ + state->PC = saved_PC; \ +} while (0) +#include "sh2-core.i" + +/*-----------------------------------------------------------------------*/ + +/** + * interpret_insn: Interpret and execute a single SH-2 instruction at the + * current PC. + * + * [Parameters] + * state: SH-2 processor state + * [Return value] + * None + */ +void interpret_insn(SH2State *state) +{ + /* Make sure we're not trying to execute from an odd address */ + if (UNLIKELY(state->PC & 1)) { + /* Push SR and PC */ + state->R[15] -= 4; + MappedMemoryWriteLong(state->R[15], state->SR); + state->R[15] -= 4; + MappedMemoryWriteLong(state->R[15], state->PC); + /* Jump to the instruction address error exception vector (9) */ + state->PC = MappedMemoryReadLong(9<<2); + } + + decode_insn(state, state->PC, 0); + + if (UNLIKELY(state->delay)) { + /* Don't treat the instruction after a not-taken conditional branch + * as a delay slot. (Note that when interpreting, the + * branch_cond_reg field holds the actual value of the condition.) */ + if (!(state->branch_type == SH2BRTYPE_BT_S && !state->branch_cond_reg) + && !(state->branch_type == SH2BRTYPE_BF_S && state->branch_cond_reg) + ) { + /* Make sure we interpret the delay slot immediately, so (1) we + * don't try to translate it as the beginning of a block and + * (2) we don't let any exceptions get in the way (SH7604 + * manual page 75, section 4.6.1: exceptions are not accepted + * when processing a delay slot). */ + decode_insn(state, state->PC, 0); + } + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2-opcodeinfo.c b/yabause/src/psp/sh2-opcodeinfo.c new file mode 100644 index 0000000000..61b497a544 --- /dev/null +++ b/yabause/src/psp/sh2-opcodeinfo.c @@ -0,0 +1,1088 @@ +/* src/psp/sh2-opcodeinfo.c: Information table for SH-2 opcodes + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "sh2.h" +#include "sh2-internal.h" + +/*************************************************************************/ +/************************ Opcode table definition ************************/ +/*************************************************************************/ + +/** + * opcode_info_low: Table of information bits for opcodes with the high + * bit clear; in all such instructions, the "n" field (bits 8-11) is an + * operand or part of an operand. Indexed by bits 0-7 and 12-14 of the + * opcode, i.e. ((opcode & 0x7000) >> 4 | (opcode & 0x00FF)). + */ +static int32_t opcode_info_low[0x800]; + +/** + * opcode_info_high: Table of information bits for opcodes in which the + * "n" field (bits 8-11) is part of the instruction code and the lower 8 + * bits are one or more operands. Indexed by bits 8-14 of the opcode. + */ +static int32_t opcode_info_high[0x80]; + +/*************************************************************************/ +/***************** Opcode table initialization routines ******************/ +/*************************************************************************/ + +/* Forward declarations */ + +static void init_0xxx(void); +static void init_1xxx(void); +static void init_2xxx(void); +static void init_3xxx(void); +static void init_4xxx(void); +static void init_5xxx(void); +static void init_6xxx(void); +static void init_7xxx(void); +static void init_8xxx(void); +static void init_9xxx(void); +static void init_Axxx(void); +static void init_Bxxx(void); +static void init_Cxxx(void); +static void init_Dxxx(void); +static void init_Exxx(void); + +/*************************************************************************/ + +/** + * init_opcode_info: Initialize the opcode_info[] table. Must be called + * before accessing the table. + * + * [Parameters] + * None + * [Return value] + * None + */ +void init_opcode_info(void) +{ + /* First clear the tables (rendering all opcodes invalid)... */ + memset(opcode_info_low, 0, sizeof(opcode_info_low)); + memset(opcode_info_high, 0, sizeof(opcode_info_high)); + + /* ... then fill in the tables by calling subroutines for each opcode + * group. */ + init_0xxx(); + init_1xxx(); + init_2xxx(); + init_3xxx(); + init_4xxx(); + init_5xxx(); + init_6xxx(); + init_7xxx(); + init_8xxx(); + init_9xxx(); + init_Axxx(); + init_Bxxx(); + init_Cxxx(); + init_Dxxx(); + init_Exxx(); + /* 0xFxxx is invalid */ +} + +/*************************************************************************/ + +/** + * init_xxxx: Initialize individual groups of opcodes. + * + * [Parameters] + * None + * [Return value] + * None + */ + +/*-----------------------------------------------------------------------*/ + +static void init_0xxx(void) +{ + unsigned int i; + + /* STC SR,Rn */ + opcode_info_low[0x002] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + /* STC GBR,Rn */ + opcode_info_low[0x012] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + /* STC VBR,Rn */ + opcode_info_low[0x022] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + + /* BSRF Rn */ + opcode_info_low[0x003] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + /* BRAF Rn */ + opcode_info_low[0x023] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + + /* MOV.* Rm,@(R0,Rn) */ + for (i = 0x000; i <= 0x0F0; i += 0x10) { + opcode_info_low[i|0x4] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + opcode_info_low[i|0x5] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_W; + opcode_info_low[i|0x6] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L; + } + + /* MUL.L Rm,Rn */ + for (i = 0x000; i <= 0x0F0; i += 0x10) { + opcode_info_low[i|0x7] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn; + } + + /* CLRT */ + opcode_info_low[0x008] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_SR_T; + /* SETT */ + opcode_info_low[0x018] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_SR_T; + /* CLRMAC */ + opcode_info_low[0x028] = SH2_OPCODE_INFO_VALID; + + /* NOP */ + opcode_info_low[0x009] = SH2_OPCODE_INFO_VALID; + /* DIV0U */ + opcode_info_low[0x019] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_SR_T; + /* MOVT Rn */ + opcode_info_low[0x029] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + + /* STS MACH,Rn */ + opcode_info_low[0x00A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + /* STS MACL,Rn */ + opcode_info_low[0x01A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + /* STS PR,Rn */ + opcode_info_low[0x02A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + + /* RTS */ + opcode_info_low[0x00B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + /* SLEEP */ + opcode_info_low[0x01B] = SH2_OPCODE_INFO_VALID; + /* RTE */ + opcode_info_low[0x02B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R15 + | SH2_OPCODE_INFO_SETS_SR_T + | SH2_OPCODE_INFO_ACCESSES_R15 + | SH2_OPCODE_INFO_ACCESS_SIZE_LL + | SH2_OPCODE_INFO_ACCESS_POSTINC + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + + /* MOV.* @(R0,Rm),Rn */ + for (i = 0x000; i <= 0x0F0; i += 0x10) { + opcode_info_low[i|0xC] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + opcode_info_low[i|0xD] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_W; + opcode_info_low[i|0xE] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_R0_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_L; + } + + /* MAC.L @Rm+,@Rn+ */ + for (i = 0x000; i <= 0x0F0; i += 0x10) { + opcode_info_low[i|0xF] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_1xxx(void) +{ + /* MOV.L Rm,@(disp,Rn) */ + unsigned int i; + for (i = 0x100; i <= 0x1FF; i++) { + opcode_info_low[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_DISP_4; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_2xxx(void) +{ + unsigned int i; + for (i = 0x200; i <= 0x2F0; i += 0x10) { + + /* MOV.* Rm,@Rn */ + opcode_info_low[i|0x0] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + opcode_info_low[i|0x1] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_W; + opcode_info_low[i|0x2] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L; + + /* MOV.* Rm,@-Rn */ + opcode_info_low[i|0x4] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_PREDEC; + opcode_info_low[i|0x5] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_PREDEC; + opcode_info_low[i|0x6] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + + /* DIV0S Rm,Rn */ + opcode_info_low[i|0x7] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* TST Rm,Rn */ + opcode_info_low[i|0x8] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* AND Rm,Rn */ + opcode_info_low[i|0x9] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* XOR Rm,Rn */ + opcode_info_low[i|0xA] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* OR Rm,Rn */ + opcode_info_low[i|0xB] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* CMP/ST Rm,Rn */ + opcode_info_low[i|0xC] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* XTRCT Rm,Rn */ + opcode_info_low[i|0xD] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* MULU.W Rm,Rn */ + opcode_info_low[i|0xE] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn; + + /* MULS.W Rm,Rn */ + opcode_info_low[i|0xF] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_3xxx(void) +{ + unsigned int i; + for (i = 0x300; i <= 0x3F0; i += 0x10) { + + /* CMP/EQ Rm,Rn */ + opcode_info_low[i|0x0] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* CMP/HS Rm,Rn */ + opcode_info_low[i|0x2] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* CMP/GE Rm,Rn */ + opcode_info_low[i|0x3] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* DIV1 Rm,Rn */ + opcode_info_low[i|0x4] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* DMULU.L Rm,Rn */ + opcode_info_low[i|0x5] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn; + + /* CMP/HI Rm,Rn */ + opcode_info_low[i|0x6] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* CMP/GT Rm,Rn */ + opcode_info_low[i|0x7] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* SUB Rm,Rn */ + opcode_info_low[i|0x8] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* SUBC Rm,Rn */ + opcode_info_low[i|0xA] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* SUBV Rm,Rn */ + opcode_info_low[i|0xB] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* ADD Rm,Rn */ + opcode_info_low[i|0xC] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* DMULS.L Rm,Rn */ + opcode_info_low[i|0xD] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn; + + /* ADDC Rm,Rn */ + opcode_info_low[i|0xE] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* ADDV Rm,Rn */ + opcode_info_low[i|0xF] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_4xxx(void) +{ + /* SHLL Rn */ + opcode_info_low[0x400] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* DT Rn */ + opcode_info_low[0x410] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* SHAL Rn */ + opcode_info_low[0x420] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* SHLR Rn */ + opcode_info_low[0x401] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* CMP/PZ Rn */ + opcode_info_low[0x411] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* SHAR Rn */ + opcode_info_low[0x421] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* STS.L MACH,@-Rn */ + opcode_info_low[0x402] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + /* STS.L MACL,@-Rn */ + opcode_info_low[0x412] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + /* STS.L PR,@-Rn */ + opcode_info_low[0x422] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + + /* STC.L SR,@-Rn */ + opcode_info_low[0x403] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + /* STC.L GBR,@-Rn */ + opcode_info_low[0x413] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + /* STC.L VBR,@-Rn */ + opcode_info_low[0x423] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_PREDEC; + + /* ROTL Rn */ + opcode_info_low[0x404] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* ROTCL Rn */ + opcode_info_low[0x424] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* ROTR Rn */ + opcode_info_low[0x405] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* CMP/PL Rn */ + opcode_info_low[0x415] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* ROTCR Rn */ + opcode_info_low[0x425] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* LDS.L @Rn+,MACH */ + opcode_info_low[0x406] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + /* LDS.L @Rn+,MACL */ + opcode_info_low[0x416] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + /* LDS.L @Rn+,PR */ + opcode_info_low[0x426] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + + /* LDC.L @Rn+,SR */ + opcode_info_low[0x407] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + /* LDC.L @Rn+,GBR */ + opcode_info_low[0x417] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + /* LDC.L @Rn+,VBR */ + opcode_info_low[0x427] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + + /* SHLL2 Rn */ + opcode_info_low[0x408] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + /* SHLL8 Rn */ + opcode_info_low[0x418] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + /* SHLL16 Rn */ + opcode_info_low[0x428] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* SHLR2 Rn */ + opcode_info_low[0x409] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + /* SHLR8 Rn */ + opcode_info_low[0x419] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + /* SHLR16 Rn */ + opcode_info_low[0x429] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + + /* LDS Rn,MACH */ + opcode_info_low[0x40A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn; + /* LDS Rn,MACL */ + opcode_info_low[0x41A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn; + /* LDS Rn,PR */ + opcode_info_low[0x42A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn; + + /* JSR @Rn */ + opcode_info_low[0x40B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + /* TAS.B @Rn */ + opcode_info_low[0x41B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_IS_RMW + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + /* JMP @Rn */ + opcode_info_low[0x42B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + + /* LDC Rn,SR */ + opcode_info_low[0x40E] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + /* LDC Rn,GBR */ + opcode_info_low[0x41E] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn; + /* LDC Rn,VBR */ + opcode_info_low[0x42E] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn; + + /* MAC.W @Rm+,@Rn+ */ + unsigned int i; + for (i = 0x400; i <= 0x4F0; i += 0x10) { + opcode_info_low[i|0xF] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESSES_Rn + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_POSTINC; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_5xxx(void) +{ + /* MOV.L @(disp,Rm),Rn */ + unsigned int i; + for (i = 0x500; i <= 0x5FF; i++) { + opcode_info_low[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_DISP_4; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_6xxx(void) +{ + unsigned int i; + for (i = 0x600; i <= 0x6F0; i += 0x10) { + + /* MOV.* @Rm,Rn */ + opcode_info_low[i|0x0] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + opcode_info_low[i|0x1] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_W; + opcode_info_low[i|0x2] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_L; + + /* MOV Rm,Rn */ + opcode_info_low[i|0x3] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + + /* MOV.* @Rm+,Rn */ + opcode_info_low[i|0x4] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_POSTINC; + opcode_info_low[i|0x5] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_POSTINC; + opcode_info_low[i|0x6] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_POSTINC; + + /* NOT Rm,Rn */ + opcode_info_low[i|0x7] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + + /* SWAP.* Rm,Rn */ + opcode_info_low[i|0x8] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + opcode_info_low[i|0x9] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + + /* NEGC Rm,Rn */ + opcode_info_low[i|0xA] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_SETS_SR_T; + + /* NEG Rm,Rn */ + opcode_info_low[i|0xB] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + + /* EXTU.* Rm,Rn */ + opcode_info_low[i|0xC] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + opcode_info_low[i|0xD] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + + /* EXTS.* Rm,Rn */ + opcode_info_low[i|0xE] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + opcode_info_low[i|0xF] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_Rn; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_7xxx(void) +{ + /* ADD #imm,Rn */ + unsigned int i; + for (i = 0x700; i <= 0x7FF; i++) { + opcode_info_low[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rn + | SH2_OPCODE_INFO_SETS_Rn; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_8xxx(void) +{ + /* MOV.B R0,@(disp,Rm) */ + opcode_info_high[0x00] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_DISP_4; + + /* MOV.W R0,@(disp,Rm) */ + opcode_info_high[0x01] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_DISP_4; + + /* MOV.B @(disp,Rm),R0 */ + opcode_info_high[0x04] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_R0 + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_DISP_4; + + /* MOV.W @(disp,Rm),R0 */ + opcode_info_high[0x05] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_Rm + | SH2_OPCODE_INFO_SETS_R0 + | SH2_OPCODE_INFO_ACCESSES_Rm + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_DISP_4; + + /* CMP/EQ #imm,R0 */ + opcode_info_high[0x08] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_SR_T; + + /* BT label */ + opcode_info_high[0x09] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_COND; + + /* BF label */ + opcode_info_high[0x0B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_COND; + + /* BT/S label */ + opcode_info_high[0x0D] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_COND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + + /* BF/S label */ + opcode_info_high[0x0F] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_COND + | SH2_OPCODE_INFO_BRANCH_DELAYED; +} + +/*-----------------------------------------------------------------------*/ + +static void init_9xxx(void) +{ + /* MOV.W @(disp,PC),Rn */ + unsigned int i; + for (i = 0x10; i <= 0x1F; i++) { + opcode_info_high[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_PC + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_DISP_8; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_Axxx(void) +{ + /* BRA label */ + unsigned int i; + for (i = 0x20; i <= 0x2F; i++) { + opcode_info_high[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_Bxxx(void) +{ + /* BRA label */ + unsigned int i; + for (i = 0x30; i <= 0x3F; i++) { + opcode_info_high[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_DELAYED; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_Cxxx(void) +{ + /* MOV.B R0,@(disp,GBR) */ + opcode_info_high[0x40] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* MOV.W R0,@(disp,GBR) */ + opcode_info_high[0x41] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* MOV.L R0,@(disp,GBR) */ + opcode_info_high[0x42] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* TRAPA #imm */ + opcode_info_high[0x43] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R15 + | SH2_OPCODE_INFO_ACCESSES_R15 + | SH2_OPCODE_INFO_ACCESS_IS_STORE + | SH2_OPCODE_INFO_ACCESS_SIZE_LL + | SH2_OPCODE_INFO_ACCESS_PREDEC + | SH2_OPCODE_INFO_BRANCH_UNCOND; + + /* MOV.B @(disp,GBR),R0 */ + opcode_info_high[0x44] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_SIZE_B + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* MOV.W @(disp,GBR),R0 */ + opcode_info_high[0x45] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_SIZE_W + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* MOV.L @(disp,GBR),R0 */ + opcode_info_high[0x46] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_R0 + | SH2_OPCODE_INFO_ACCESSES_GBR + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_DISP_8; + + /* MOVA @(disp,PC),R0 */ + opcode_info_high[0x47] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_R0; + + /* TST #imm,R0 */ + opcode_info_high[0x48] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_SR_T; + + /* AND #imm,R0 */ + opcode_info_high[0x49] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_R0; + + /* XOR #imm,R0 */ + opcode_info_high[0x4A] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_R0; + + /* OR #imm,R0 */ + opcode_info_high[0x4B] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_R0; + + /* TST.B #imm,@(R0,GBR) */ + opcode_info_high[0x4C] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_SETS_SR_T + | SH2_OPCODE_INFO_ACCESSES_R0_GBR + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + + /* AND.B #imm,@(R0,GBR) */ + opcode_info_high[0x4D] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_R0_GBR + | SH2_OPCODE_INFO_ACCESS_IS_RMW + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + + /* XOR.B #imm,@(R0,GBR) */ + opcode_info_high[0x4E] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_R0_GBR + | SH2_OPCODE_INFO_ACCESS_IS_RMW + | SH2_OPCODE_INFO_ACCESS_SIZE_B; + + /* OR.B #imm,@(R0,GBR) */ + opcode_info_high[0x4F] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_USES_R0 + | SH2_OPCODE_INFO_ACCESSES_R0_GBR + | SH2_OPCODE_INFO_ACCESS_IS_RMW + | SH2_OPCODE_INFO_ACCESS_SIZE_B; +} + +/*-----------------------------------------------------------------------*/ + +static void init_Dxxx(void) +{ + /* MOV.L @(disp,PC),Rn */ + unsigned int i; + for (i = 0x50; i <= 0x5F; i++) { + opcode_info_high[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn + | SH2_OPCODE_INFO_ACCESSES_PC + | SH2_OPCODE_INFO_ACCESS_SIZE_L + | SH2_OPCODE_INFO_ACCESS_DISP_8; + } +} + +/*-----------------------------------------------------------------------*/ + +static void init_Exxx(void) +{ + /* MOV #imm,Rn */ + unsigned int i; + for (i = 0x60; i <= 0x6F; i++) { + opcode_info_high[i] = SH2_OPCODE_INFO_VALID + | SH2_OPCODE_INFO_SETS_Rn; + } +} + +/*************************************************************************/ +/********************** Opcode table lookup routine **********************/ +/*************************************************************************/ + +/** + * get_opcode_info: Return information about the given opcode. + * + * [Parameters] + * opcode: SH-2 opcode to obtain information about + * [Return value] + * + */ +int32_t get_opcode_info(uint16_t opcode) +{ + if (opcode & 0x8000) { + return opcode_info_high[(opcode & 0x7F00) >> 8]; + } else { + return opcode_info_low[(opcode & 0x7000) >> 4 | (opcode & 0xFF)]; + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2-optimize.c b/yabause/src/psp/sh2-optimize.c new file mode 100644 index 0000000000..5763fe6ae5 --- /dev/null +++ b/yabause/src/psp/sh2-optimize.c @@ -0,0 +1,1185 @@ +/* src/psp/sh2-optimize.c: Optimization routines for SH-2 emulator + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ +/*************************** Required headers ****************************/ +/*************************************************************************/ + +#include "common.h" + +#include "../core.h" + +#include "rtl.h" +#include "sh2.h" +#include "sh2-internal.h" + +#ifdef JIT_DEBUG_TRACE +# include "../sh2d.h" +#endif + +/*************************************************************************/ +/****************************** Common data ******************************/ +/*************************************************************************/ + +#ifdef OPTIMIZE_IDLE + +/* + * The table below is used by OPTIMIZE_IDLE to find the effects of a given + * SH-2 instruction on machine state. Instruction opcodes are listed + * hierarchically, starting with idle_table_main[], which is indexed by the + * top 4 bits of the opcode (bits 12-15). Each table entry is either a + * pointer to a subtable with a shift count indicating which 4 bits of the + * opcode are used to index the subtable, or an instruction definition + * indicating the effects of the given set of opcodes. + * + * Instruction definitions consist of "used" and "changed" bitmasks, + * indicating which registers are used (read) or changed (written) by the + * instruction, respectively. For example, the instruction ADD Rm,Rn uses + * both registers Rm and Rn and changes register Rn, so it is defined as: + * + * {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn} + * + * IDLE_Rm and IDLE_Rn in this example are pseudo-register flags, and are + * interpreted to mean the registers specified by the "m" and "n" fields of + * the opcode (bits 4-7 and 8-11 respectively). There are also individual + * flags for each of the individually-alterable fields in the SR register + * (T, S, Q, and M); the I field can only be altered by writing SR as a + * whole, and is not used directly by any instruction in any case. + * + * Instructions marked as IDLE_BAD in the "changed" field can never be part + * of an idle loop, either because the opcode group itself is invalid, or + * because the instruction modifies machine state in a fashion which is + * either nonrepeatable (such as postincrement or predecrement memory + * accesses) or not trivially repeatable (such as subroutine calls). + * + * The table also includes an "extra_cycles" field, used to indicate + * instructions which require more than one clock cycle to complete. This + * information is not currently used, but is stored in case it becomes + * useful in the future for computing the duration of a loop. + */ + +/*-----------------------------------------------------------------------*/ + +/* Bit values for register bitmasks */ +#define IDLE_R(n) (1U << (n)) +#define IDLE_SR_T (1U << 16) +#define IDLE_SR_S (1U << 17) +#define IDLE_SR_Q (1U << 18) +#define IDLE_SR_M (1U << 19) +#define IDLE_SR_MQT (IDLE_SR_T | IDLE_SR_Q | IDLE_SR_M) +#define IDLE_SR (IDLE_SR_T | IDLE_SR_S | IDLE_SR_Q | IDLE_SR_M) +#define IDLE_GBR (1U << 20) +#define IDLE_VBR (1U << 21) +#define IDLE_PR (1U << 22) +#define IDLE_MACL (1U << 23) +#define IDLE_MACH (1U << 24) +#define IDLE_MAC (IDLE_MACL | IDLE_MACH) + +/* Value used in IdleInfo.changed field to indicate an instruction which + * can never be part of an idle loop */ +#define IDLE_BAD (1U << 31) + +/* Virtual bits used for the Rn (bits 8-11) and Rm (bits 4-7) fields of + * the instruction; e.g. IDLE_Rn translates to IDLE_R(opcode>>8 & 0xF) */ +#define IDLE_Rn (1U << 30) +#define IDLE_Rm (1U << 29) + +/* Table data structure; fields "used" and "changed" are ignored if + * "subtable" is non-NULL */ +typedef struct IdleInfo_ IdleInfo; +struct IdleInfo_ { + uint32_t used; // Bitmask of registers used by the instruction + uint32_t changed; // Bitmask of registers changed by the instruction + uint8_t extra_cycles; // Clock cycles used by instruction minus 1 + uint8_t next_shift; // Bit position of subtable index (0, 4, or 8) + const IdleInfo *subtable; // NULL if no subtable for this opcode +}; + +/*-----------------------------------------------------------------------*/ + +/* Opcode table for $0xx2 opcodes */ +static const IdleInfo idle_table_0xx2[16] = { + {.used = IDLE_SR, .changed = IDLE_Rn}, // STC SR,Rn + {.used = IDLE_GBR, .changed = IDLE_Rn}, // STC GBR,Rn + {.used = IDLE_VBR, .changed = IDLE_Rn}, // STC VBR,Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $0xx3 opcodes */ +static const IdleInfo idle_table_0xx3[16] = { + {.changed = IDLE_BAD}, // BSRF Rn + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_Rn, .changed = 0, .extra_cycles = 1}, // BRAF Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $0xx8 opcodes */ +static const IdleInfo idle_table_0xx8[16] = { + {.used = 0, .changed = IDLE_SR_T}, // CLRT + {.used = 0, .changed = IDLE_SR_T}, // SETT + {.used = 0, .changed = IDLE_MAC}, // CLRMAC + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $0xx9 opcodes */ +static const IdleInfo idle_table_0xx9[16] = { + {.used = 0, .changed = 0}, // NOP + {.used = 0, .changed = IDLE_SR_MQT}, // DIV0U + {.used = IDLE_SR_T, .changed = IDLE_Rn}, // MOVT Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +static const IdleInfo idle_table_0xxA[16] = { + {.used = IDLE_MACH, .changed = IDLE_Rn}, // STS MACH,Rn + {.used = IDLE_MACL, .changed = IDLE_Rn}, // STS MACL,Rn + {.used = IDLE_PR, .changed = IDLE_Rn}, // STS PR,Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $0xxx opcodes */ +static const IdleInfo idle_table_0xxx[16] = { + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.next_shift = 4, .subtable = idle_table_0xx2}, + {.next_shift = 4, .subtable = idle_table_0xx3}, + + {.changed = IDLE_BAD}, // MOV.B Rm,@(R0,Rn) + {.changed = IDLE_BAD}, // MOV.W Rm,@(R0,Rn) + {.changed = IDLE_BAD}, // MOV.L Rm,@(R0,Rn) + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_MACL, .extra_cycles = 1}, // MUL.L Rm,Rn + + {.next_shift = 4, .subtable = idle_table_0xx8}, + {.next_shift = 4, .subtable = idle_table_0xx9}, + {.next_shift = 4, .subtable = idle_table_0xxA}, + {.changed = IDLE_BAD}, // RTS, SLEEP, RTE + + {.used = IDLE_R(0)|IDLE_Rm, .changed = IDLE_Rn}, // MOV.B @(R0,Rm),Rn + {.used = IDLE_R(0)|IDLE_Rm, .changed = IDLE_Rn}, // MOV.W @(R0,Rm),Rn + {.used = IDLE_R(0)|IDLE_Rm, .changed = IDLE_Rn}, // MOV.L @(R0,Rm),Rn + {.changed = IDLE_BAD}, // MAC.L @Rm+,@Rn+ +}; + +/*----------------------------------*/ + +/* Opcode table for $2xxx opcodes */ +static const IdleInfo idle_table_2xxx[16] = { + {.changed = IDLE_BAD}, // MOV.B Rm,@Rn + {.changed = IDLE_BAD}, // MOV.W Rm,@Rn + {.changed = IDLE_BAD}, // MOV.L Rm,@Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // MOV.B Rm,@-Rn + {.changed = IDLE_BAD}, // MOV.W Rm,@-Rn + {.changed = IDLE_BAD}, // MOV.L Rm,@-Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_MQT}, // DIV0S Rm,Rn + + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // TST Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // AND Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // XOR Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // OR Rm,Rn + + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/ST Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // XTRCT Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_MACL}, // MULU.W Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_MACL}, // MULS.W Rm,Rn +}; + +/*----------------------------------*/ + +/* Opcode table for $3xxx opcodes */ +static const IdleInfo idle_table_3xxx[16] = { + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/EQ Rm,Rn + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/HS Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/GE Rm,Rn + + {.changed = IDLE_BAD}, // DIV1 Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_MAC, .extra_cycles = 1}, // DMULU.L Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/HI Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_SR_T}, // CMP/GT Rm,Rn + + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // SUB Rm,Rn + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_Rm|IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // SUBC Rm,Rn + {.used = IDLE_Rm|IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // SUBV Rm,Rn + + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_Rn}, // ADD Rm,Rn + {.used = IDLE_Rm|IDLE_Rn, .changed = IDLE_MAC, .extra_cycles = 1}, // DMULS.L Rm,Rn + {.used = IDLE_Rm|IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // ADDC Rm,Rn + {.used = IDLE_Rm|IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // ADDV Rm,Rn +}; + +/*----------------------------------*/ + +/* Opcode table for $4xx0 opcodes */ +static const IdleInfo idle_table_4xx0[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // SHLL Rn + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // DT Rn + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // SHAL Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xx1 opcodes */ +static const IdleInfo idle_table_4xx1[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // SHLR Rn + {.used = IDLE_Rn, .changed = IDLE_SR_T}, // CMP/PZ Rn + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // SHAR Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xx4 opcodes */ +static const IdleInfo idle_table_4xx4[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // ROTL Rn + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // ROTCL Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xx5 opcodes */ +static const IdleInfo idle_table_4xx5[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn|IDLE_SR_T}, // ROTR Rn + {.used = IDLE_Rn, .changed = IDLE_SR_T}, // CMP/PL Rn + {.used = IDLE_Rn|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // ROTCR Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xx8 opcodes */ +static const IdleInfo idle_table_4xx8[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLL2 Rn + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLL8 Rn + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLL16 Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xx9 opcodes */ +static const IdleInfo idle_table_4xx9[16] = { + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLR2 Rn + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLR8 Rn + {.used = IDLE_Rn, .changed = IDLE_Rn}, // SHLR16 Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xxA opcodes */ +static const IdleInfo idle_table_4xxA[16] = { + {.used = IDLE_Rn, .changed = IDLE_MACH}, // LDS Rn,MACH + {.used = IDLE_Rn, .changed = IDLE_MACL}, // LDS Rn,MACL + {.used = IDLE_Rn, .changed = IDLE_PR}, // LDS Rn,PR + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xxB opcodes */ +static const IdleInfo idle_table_4xxB[16] = { + {.changed = IDLE_BAD}, // JSR @Rn + {.changed = IDLE_BAD}, // TAS @Rn + {.used = 0, .changed = 0, .extra_cycles = 1}, // JMP @Rn + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xxE opcodes */ +static const IdleInfo idle_table_4xxE[16] = { + {.used = IDLE_Rn, .changed = IDLE_SR}, // LDC Rn,SR + {.used = IDLE_Rn, .changed = IDLE_GBR}, // LDC Rn,GBR + {.used = IDLE_Rn, .changed = IDLE_VBR}, // LDC Rn,VBR + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid +}; + +/* Opcode table for $4xxx opcodes */ +static const IdleInfo idle_table_4xxx[16] = { + {.next_shift = 4, .subtable = idle_table_4xx0}, + {.next_shift = 4, .subtable = idle_table_4xx1}, + {.changed = IDLE_BAD}, // STSL ...,@-Rn + {.changed = IDLE_BAD}, // STCL ...,@-Rn + + {.next_shift = 4, .subtable = idle_table_4xx4}, + {.next_shift = 4, .subtable = idle_table_4xx5}, + {.changed = IDLE_BAD}, // LDSL @Rm+,... + {.changed = IDLE_BAD}, // LDCL @Rm+,... + + {.next_shift = 4, .subtable = idle_table_4xx8}, + {.next_shift = 4, .subtable = idle_table_4xx9}, + {.next_shift = 4, .subtable = idle_table_4xxA}, + {.next_shift = 4, .subtable = idle_table_4xxB}, + + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + {.next_shift = 4, .subtable = idle_table_4xxE}, + {.changed = IDLE_BAD}, // MAC.W @Rm+,@Rn+ +}; + +/*----------------------------------*/ + +/* Opcode table for $6xxx opcodes */ +static const IdleInfo idle_table_6xxx[16] = { + {.used = IDLE_Rm, .changed = IDLE_Rn}, // MOV.B @Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // MOV.W @Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // MOV.L @Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // MOV Rm,Rn + + {.used = IDLE_Rm, .changed = IDLE_Rm|IDLE_Rn}, // MOV.B @Rm+,Rn + {.used = IDLE_Rm, .changed = IDLE_Rm|IDLE_Rn}, // MOV.W @Rm+,Rn + {.used = IDLE_Rm, .changed = IDLE_Rm|IDLE_Rn}, // MOV.L @Rm+,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // NOT Rm,Rn + + {.used = IDLE_Rm, .changed = IDLE_Rn}, // SWAP.B Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // SWAP.W Rm,Rn + {.used = IDLE_Rm|IDLE_SR_T, .changed = IDLE_Rn|IDLE_SR_T}, // NEGC Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // NEG Rm,Rn + + {.used = IDLE_Rm, .changed = IDLE_Rn}, // EXTU.B Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // EXTU.W Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // EXTS.B Rm,Rn + {.used = IDLE_Rm, .changed = IDLE_Rn}, // EXTS.W Rm,Rn +}; + +/*----------------------------------*/ + +/* Opcode table for $8xxx opcodes */ +static const IdleInfo idle_table_8xxx[16] = { + {.changed = IDLE_BAD}, // MOV.B R0,@(disp,Rm) + {.changed = IDLE_BAD}, // MOV.W R0,@(disp,Rm) + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.used = IDLE_Rm, .changed = IDLE_R(0)}, // MOV.B @(disp,Rm),R0 + {.used = IDLE_Rm, .changed = IDLE_R(0)}, // MOV.W @(disp,Rm),R0 + {.changed = IDLE_BAD}, // invalid + {.changed = IDLE_BAD}, // invalid + + {.used = IDLE_R(0), .changed = IDLE_SR_T}, // CMP/EQ #imm,R0 + {.used = IDLE_SR_T, .changed = 0, .extra_cycles = 2}, // BT label + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_SR_T, .changed = 0, .extra_cycles = 2}, // BF label + + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_SR_T, .changed = 0, .extra_cycles = 1}, // BT/S label + {.changed = IDLE_BAD}, // invalid + {.used = IDLE_SR_T, .changed = 0, .extra_cycles = 1}, // BF/S label +}; + +/*----------------------------------*/ + +/* Opcode table for $Cxxx opcodes */ +static const IdleInfo idle_table_Cxxx[16] = { + {.changed = IDLE_BAD}, // MOV.B R0,@(disp,GBR) + {.changed = IDLE_BAD}, // MOV.W R0,@(disp,GBR) + {.changed = IDLE_BAD}, // MOV.L R0,@(disp,GBR) + {.changed = IDLE_BAD}, // TRAPA #imm + + {.used = IDLE_GBR, .changed = IDLE_R(0)}, // MOV.B @(disp,GBR),R0 + {.used = IDLE_GBR, .changed = IDLE_R(0)}, // MOV.W @(disp,GBR),R0 + {.used = IDLE_GBR, .changed = IDLE_R(0)}, // MOV.L @(disp,GBR),R0 + {.used = 0, .changed = IDLE_R(0)}, // MOVA @(disp,PC),R0 + + {.used = IDLE_R(0), .changed = IDLE_SR_T}, // TST #imm,R0 + {.used = IDLE_R(0), .changed = IDLE_R(0)}, // AND #imm,R0 + {.used = IDLE_R(0), .changed = IDLE_R(0)}, // XOR #imm,R0 + {.used = IDLE_R(0), .changed = IDLE_R(0)}, // OR #imm,R0 + + {.used = IDLE_R(0)|IDLE_GBR, .changed = IDLE_SR_T}, // TST #imm,@(R0,GBR) + {.changed = IDLE_BAD}, // AND #imm,@(R0,GBR) + {.changed = IDLE_BAD}, // XOR #imm,@(R0,GBR) + {.changed = IDLE_BAD}, // OR #imm,@(R0,GBR) +}; + +/*----------------------------------*/ + +/* Main opcode table (for bits 12-15) */ +static const IdleInfo idle_table_main[16] = { + {.next_shift = 0, .subtable = idle_table_0xxx}, + {.changed = IDLE_BAD}, // MOV.L Rm,@(disp,Rn) + {.next_shift = 0, .subtable = idle_table_2xxx}, + {.next_shift = 0, .subtable = idle_table_3xxx}, + + {.next_shift = 0, .subtable = idle_table_4xxx}, + {.used = IDLE_Rm, .changed = IDLE_Rn}, // MOV.L @(disp,Rn),Rm + {.next_shift = 0, .subtable = idle_table_6xxx}, + {.changed = IDLE_BAD}, // ADD #imm,Rn + + {.next_shift = 8, .subtable = idle_table_8xxx}, + {.used = 0, .changed = IDLE_Rn}, // MOV.W @(disp,PC),Rn + {.used = 0, .changed = 0, .extra_cycles = 1}, // BRA label + {.changed = IDLE_BAD}, // BSR label + + {.next_shift = 8, .subtable = idle_table_Cxxx}, + {.used = 0, .changed = IDLE_Rn}, // MOV.L @(disp,PC),Rn + {.used = 0, .changed = IDLE_Rn}, // MOV #imm,Rn + {.changed = IDLE_BAD}, // invalid +}; + +/*-----------------------------------------------------------------------*/ + +#endif // OPTIMIZE_IDLE + +/*************************************************************************/ +/************************ Idle loop optimization *************************/ +/*************************************************************************/ + +#ifdef OPTIMIZE_IDLE + +/* + * A loop is "idle" if a single execution of the entire loop, starting from + * the first instruction of the loop and ending immediately before the next + * time the first instruction is executed, has no net effect on the state + * of the system after the first iteration of the loop. Each individual + * instruction may alter system state (and in fact, the program counter + * will change with each instruction executed), but provided that the + * external system state remains constant, the final result of executing N + * loops must be identical to the result of executing N-1 loops for N + * greater than 1. + * + * For example, a loop consisting solely of a branch to the same + * instruction: + * 0x1000: bra 0x1000 + * 0x1002: nop + * would naturally qualify, as would a loop reading from the same memory + * location (here, waiting for a memory address to read as zero): + * 0x1000: mov.l @r0, r1 + * 0x1002: tst r1, r1 + * 0x1004: bf 0x1000 + * + * On the flip side, a loop with a counter: + * 0x1000: dt r0 + * 0x1002: bf 0x1000 + * would _not_ qualify, because the state of the system changes with each + * iteration of the loop (though this particular loop can be optimized + * separately through OPTIMIZE_DELAY). Likewise, a loop containing a store + * to memory: + * 0x1000: mov.l r2, @r3 + * 0x1002: mov.l @r0, r1 + * 0x1004: tst r1, r1 + * 0x1006: bt 0x1000 + * would not qualify, because a store operation is assumed to change system + * state even if the value stored is the same. (Even for ordinary memory, + * in a multiprocessor system like the Saturn memory can be accessed by + * agents other than the processor, and an identical store operation may + * have differing effects from one iteration to the next.) + * + * To determine whether a loop is idle, we check whether all operations + * performed in the loop depend on constant values; in other words, no + * instruction in the loop may modify a register which is used as a source + * for that or an earlier instruction. (The contents of memory are assumed + * to be constant for this purpose, and any side effects of load + * instructions are ignored.) One result of this is that any loop with an + * ALU-type operation other than CMP cannot be considered idle, except as + * discussed below, even in cases where the loop does not in fact have a + * net change in state: + * 0x1000: mov.l @r0+, r1 + * 0x1002: cmp/pl @r1 + * 0x1004: bt 0x100A + * 0x1006: bra 0x1000 + * 0x1008: add #-4, r0 + * 0x100A: ... + * While admittedly somewhat contrived, this example has no net effect on + * system state because R0 is decremented at the end of each loop; but + * because R0 is used both in a postincrement memory access and as the + * target of an ADD, its value cannot be considered a constant, so the + * loop is not treated as idle. (A more sophisticated algorithm could + * track such changes in value and determine their cumulative effect, but + * in most cases the simplistic algorithm we use is sufficient.) + * + * As an exception to the above, if a register is modified before it is + * used by another instruction, the register becomes a "don't care" for + * the remainder of the loop. Thus a loop like: + * 0x1000: mov.b @r0, r1 + * 0x1002: add r2, r1 + * 0x1004: tst r1, r1 + * 0x1006: bt 0x1000 + * qualifies as an idle loop despite the "add" instruction writing to + * the same register it reads from. + */ + +/*-----------------------------------------------------------------------*/ + +/** + * can_optimize_idle: Return whether the given sequence of instructions + * forms an "idle loop", in which the processor remains in a constant state + * (or repeating sequence of states) indefinitely until a certain external + * event occurs, such as an interrupt or a change in the value of a memory- + * mapped register. If an idle loop is detected, also return information + * allowing the loop to be translated into a faster sequence of native + * instructions. + * + * The sequence of instructions is assumed to end with a branch instruction + * to the beginning of the sequence (possibly including a delay slot). + * + * [Parameters] + * insn_ptr: Pointer to first instruction + * PC: PC of first instruction + * count: Number of instructions to check + * [Return value] + * Nonzero if the given sequence of SH-2 instructions form an idle + * loop, else zero + */ +int can_optimize_idle(const uint16_t *insn_ptr, uint32_t PC, + unsigned int count) +{ + uint32_t used_regs; // Which registers have been used so far? + uint32_t dont_care; // Which registers are "don't cares"? + int can_optimize = 1; + unsigned int i; + + /* We detect failure by matching the changed-registers bitmask against + * used_regs; set the initial value so that non-idleable instructions + * are automatically rejected */ + used_regs = IDLE_BAD; + /* Initially, no registers are don't cares */ + dont_care = 0; + + for (i = 0; can_optimize && i < count; i++) { + const uint16_t opcode = *insn_ptr++; + + /* Find the entry for this opcode */ + int shift = 12; + const IdleInfo *table = idle_table_main; + while (table[opcode>>shift & 0xF].subtable) { + int newshift = table[opcode>>shift & 0xF].next_shift; + table = table[opcode>>shift & 0xF].subtable; + shift = newshift; + } + + /* Replace virtual Rn/Rm bits with real registers from opcode */ + uint32_t used = table[opcode>>shift & 0xF].used; + uint32_t changed = table[opcode>>shift & 0xF].changed; + if (used & IDLE_Rn) { + used &= ~IDLE_Rn; + used |= IDLE_R(opcode>>8 & 0xF); + } + if (used & IDLE_Rm) { + used &= ~IDLE_Rm; + used |= IDLE_R(opcode>>4 & 0xF); + } + if (changed & IDLE_Rn) { + changed &= ~IDLE_Rn; + changed |= IDLE_R(opcode>>8 & 0xF); + } + if (changed & IDLE_Rm) { + changed &= ~IDLE_Rm; + changed |= IDLE_R(opcode>>4 & 0xF); + } + + /* Add any registers used by this instruction to used_regs */ + used_regs |= used; + + /* Add any not-yet-used registers modified by this instruction to + * dont_care */ + dont_care |= changed & ~used_regs; + + /* See whether we can still treat this as an idle loop */ + if (changed & ~dont_care & used_regs) { + can_optimize = 0; + } + } + + return can_optimize; +} + +/*-----------------------------------------------------------------------*/ + +#endif // OPTIMIZE_IDLE + +/*************************************************************************/ +/************************* Division optimization *************************/ +/*************************************************************************/ + +#ifdef OPTIMIZE_DIVISION + +/* + * On the SH-2, unsigned 64bit/32bit division follows the sequence: + * DIV0U + * .arepeat 32 ;Repeat 32 times + * ROTCL Rlo + * DIV1 Rdiv,Rhi + * .aendr + * and finishes with the following state changes: + * M = 0 + * T = low bit of quotient + * Q = !T + * Rlo = high 31 bits of quotient in bits 0-30, with 0 in bit 31 + * Rhi = !Q ? remainder : (remainder - Rdiv) + * If the number of repetitions of ROTCL Rlo / DIV1 Rdiv,Rhi is less than + * 32, the result is equivalent to shifting the dividend right (32-N) bits + * where N is the repetition count, except that the low (32-N) bits of the + * dividend remain in the most-significant bits of Rlo. + * + * Signed 64bit/32bit division (where the dividend is a 32-bit signed value + * sign-extended to 64 bits) follows a sequence identical to unsigned + * division except for the first instruction: + * DIV0S Rdiv,Rhi + * .arepeat 32 ;Repeat 32 times + * ROTCL Rlo + * DIV1 Rdiv,Rhi + * .aendr + * and finishes with the following state changes: + * M = sign bit of divisor + * (if dividend >= 0) + * T = low bit of (quotient - M) + * Q = !(T ^ M) + * Rlo = high 31 bits of (quotient - M) in bits 0-30, + * sign-extended to 32 bits + * Rhi = !Q ? remainder : (remainder - abs(Rdiv)) + * (if dividend < 0) + * qtemp = quotient + (remainder ? (M ? 1 : -1) : 0) + * T = low bit of (qtemp - M) + * Q = !(T ^ M) + * Rlo = high 31 bits of (qtemp - M) in bits 0-30, + * sign-extended to 32 bits + * Rhi = !Q ? (remainder == 0 ? remainder : remainder + abs(Rdiv)) + * : (remainder != 0 ? remainder : remainder - abs(Rdiv)) + * + * In all cases, division by zero ends with: + * M = 0 + * T = 1 + * Q = 0 + * Rhi:Rlo = Rlo<<32 | x<<31 | (unsigned)(~Rhi)>>1 + * where x is 0 for unsigned division and the high bit of Rlo for signed + * division. + * + * Both of these sequences can thus be optimized significantly using native + * division instructions (at one point, a factor-of-18 reduction in + * execution speed was measured). + */ + +/*-----------------------------------------------------------------------*/ + +/** + * can_optimize_div0u: Return whether a sequence of instructions starting + * from a DIV0U instruction can be optimized to a native divide operation. + * + * [Parameters] + * insn_ptr: Pointer to DIV0U instruction + * PC: PC of DIV0U instruction + * skip_first_rotcl: Nonzero if the first ROTCL instruction is known to + * be omitted (as may happen if the low word of + * the dividend is known to be zero) + * Rhi_ret: Pointer to variable to receive index of dividend high register + * Rlo_ret: Pointer to variable to receive index of dividend low register + * Rdiv_ret: Pointer to variable to receive index of divisor register + * [Return value] + * Number of bits of division performed by the instructions following + * the DIV0U instruction (1-32), or zero if the following instructions + * do not perform a division operation + */ +int can_optimize_div0u(const uint16_t *insn_ptr, uint32_t PC, + int skip_first_rotcl, + int *Rhi_ret, int *Rlo_ret, int *Rdiv_ret) +{ + uint16_t opcode; // Opcode read from instruction stream + uint16_t rotcl_op = 0, div1_op = 0; // Expected opcodes + int nbits; // Number of bits divided so far + + if (!skip_first_rotcl) { + opcode = *++insn_ptr; + if ((opcode & 0xF0FF) != 0x4024) { // ROTCL Rlo +#ifdef JIT_DEBUG + DMSG("DIV0U optimization failed: PC+2 (0x%08X) is 0x%04X, not" + " ROTCL", PC+2, opcode); +#endif + return 0; + } + *Rlo_ret = opcode>>8 & 0xF; + rotcl_op = opcode; + } + + opcode = *++insn_ptr; + if ((opcode & 0xF00F) != 0x3004) { // DIV1 Rdiv,Rhi +#ifdef JIT_DEBUG + DMSG("DIV0U optimization failed: PC+%d (0x%08X) is 0x%04X, not DIV1", + skip_first_rotcl ? 2 : 4, PC + (skip_first_rotcl ? 2 : 4), + opcode); +#endif + return 0; + } + *Rhi_ret = opcode>>8 & 0xF; + *Rdiv_ret = opcode>>4 & 0xF; + div1_op = opcode; + + if (skip_first_rotcl) { + opcode = insn_ptr[1]; // Don't advance yet--we're just peeking + if ((opcode & 0xF0FF) != 0x4024) { +#ifdef JIT_DEBUG + DMSG("DIV0U optimization failed (with skip_first_rotcl):" + " PC+4 (0x%08X) is 0x%04X, not ROTCL", PC+4, opcode); +#endif + return 0; + } + *Rlo_ret = opcode>>8 & 0xF; + rotcl_op = opcode; + } + + for (nbits = 1; nbits < 32; nbits++) { + opcode = *++insn_ptr; + if (opcode != rotcl_op) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("DIV0U optimization stopped at %d bits: PC+%d (0x%08X) is" + " 0x%04X, not ROTCL R%d", + nbits, (skip_first_rotcl ? 0 : 2) + 4*nbits, + PC + ((skip_first_rotcl ? 0 : 2) + 4*nbits), opcode, + *Rlo_ret); +#endif + break; + } + + opcode = *++insn_ptr; + if (opcode != div1_op) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("DIV0U optimization stopped at %d bits: PC+%d (0x%08X) is" + " 0x%04X, not DIV1 R%d,R%d", + nbits, (skip_first_rotcl ? 2 : 4) + 4*nbits, + PC + ((skip_first_rotcl ? 2 : 4) + 4*nbits), opcode, + *Rdiv_ret, *Rhi_ret); +#endif + break; + } + } // for 32 bits + + return nbits; +} + +/*-----------------------------------------------------------------------*/ + +/** + * can_optimize_div0s: Return whether a sequence of instructions starting + * from a DIV0S instruction can be optimized to a native divide operation. + * + * [Parameters] + * insn_ptr: Pointer to instruction following DIV0S instruction + * PC: PC of instruction following DIV0S instruction + * Rhi: Index of dividend high register + * Rlo_ret: Pointer to variable to receive index of dividend low register + * Rdiv: Index of divisor register + * [Return value] + * Nonzero if the next 64 SH-2 instructions form a 32-bit division + * operation, else zero + */ +int can_optimize_div0s(const uint16_t *insn_ptr, uint32_t PC, + int Rhi, int *Rlo_ret, int Rdiv) +{ + uint16_t rotcl_op = 0, div1_op = 0x3004 | Rhi<<8 | Rdiv<<4; + int can_optimize = 1; + unsigned int i; + + for (i = 0; can_optimize && i < 64; i++) { + uint16_t nextop = *insn_ptr++; + + if (i%2 == 0) { // ROTCL Rlo + + if (i == 0) { + if ((nextop & 0xF0FF) == 0x4024) { + *Rlo_ret = nextop>>8 & 0xF; + rotcl_op = nextop; + } else { + /* Don't complain for the first instruction, since + * DIV0S seems to be put to uses other than division + * as well */ + can_optimize = 0; + } + } else { + if (UNLIKELY(nextop != rotcl_op)) { + DMSG("DIV0S optimization failed: PC+%d (0x%08X) is 0x%04X," + " not ROTCL R%d", 2 + 2*i, PC + 2*i, nextop, + *Rlo_ret); + can_optimize = 0; + } + } + + } else { // DIV1 Rdiv,Rhi + + if (UNLIKELY(nextop != div1_op)) { + DMSG("DIV0S optimization failed: PC+%d (0x%08X) is 0x%04X," + " not DIV1 R%d,R%d", 2 + 2*i, PC + 2*i, nextop, Rdiv, Rhi); + can_optimize = 0; + } + + } + } // for 64 instructions + + return can_optimize; +} + +#endif // OPTIMIZE_DIVISION + +/*************************************************************************/ +/********************** Variable shift optimization **********************/ +/*************************************************************************/ + +#ifdef OPTIMIZE_VARIABLE_SHIFTS + +/* + * The SH-2's repertoire of shift instructions is fairly limited compared + * to modern processors. While the SH-2 has a few multi-bit shift + * instructions (SHLL2, SHLR8, and the like), it cannot shift a register + * by an arbitrary constant, instead requiring a sequence of fixed-sized + * shifts to accomplish the task. More importantly, the SH-2 also cannot + * shift by a variable count specified in another register, meaning that + * programs must use conditional tests or loops instead. Since modern + * processors do have variable-count shift instructions, such tests or + * loops can be significantly shortened, eliminating branches that can + * hurt optimizability of the code. + * + * Currently, the following variable shift/rotate sequences is recognized: + * + * - TST #1, R0 + * BT .+4 + * SHL[LR] Rshift + * TST #2, R0 + * BT .+4 + * SHL[LR]2 Rshift + * TST #4, R0 + * BT .+6 + * SHL[LR]2 Rshift + * SHL[LR]2 Rshift + * [TST #8, R0 + * BT .+4 + * SHL[LR]8 Rshift + * [TST #16, R0 + * BT .+4 + * SHL[LR]16 Rshift]] + * + * - BRAF Rcount_adjusted [Rcount_adjusted = (maximum shift - Rcount) * 2] + * (delay slot) + * SHLL Rshift [or SHAL, SHLR, SHAR, ROTL, ROTR] + * SHLL Rshift + * ... + */ + +/*-----------------------------------------------------------------------*/ + +/** + * can_optimize_variable_shift: Return whether a sequence of instructions + * can be optimized to a native variable-count shift operation. + * + * [Parameters] + * insn_ptr: Pointer to first instruction + * PC: PC of first instruction + * Rcount_ret: Pointer to variable to receive index of shift count register + * max_ret: Pointer to variable to receive maximum shift count + * Rshift_ret: Pointer to variable to receive index of target register + * type_ret: Pointer to variable to receive: + * 0 if a SHLL/SHAL sequence + * 1 if a SHLR sequence + * 2 if a SHAR sequence + * 3 if a ROTL sequence + * 4 if a ROTR sequence + * cycles_ret: Pointer to variable to receive pointer to an array of + * cycle counts indexed by shift count (unused for some + * types of sequences) + * [Return value] + * Number of instructions consumed (nonzero) if an optimizable sequence + * is found, else zero + */ +unsigned int can_optimize_variable_shift( + const uint16_t *insn_ptr, uint32_t PC, unsigned int *Rcount_ret, + unsigned int *max_ret, unsigned int *Rshift_ret, unsigned int *type_ret, + const uint8_t **cycles_ret) +{ + if (insn_ptr[0] == 0xC801 // TST #1, R0 + && insn_ptr[1] == 0x8900 // BT .+4 + && (insn_ptr[2] & 0xF0FE) == 0x4000 // SHL[LR] Rshift + && insn_ptr[3] == 0xC802 // TST #2, R0 + && insn_ptr[4] == 0x8900 // BT .+4 + && insn_ptr[5] == (insn_ptr[2] | 0x0008) // SHL[LR]2 Rshift + && insn_ptr[6] == 0xC804 // TST #4, R0 + && insn_ptr[7] == 0x8901 // BT .+6 + && insn_ptr[8] == (insn_ptr[2] | 0x0008) // SHL[LR]2 Rshift + && insn_ptr[9] == (insn_ptr[2] | 0x0008) // SHL[LR]2 Rshift + ) { + *Rcount_ret = 0; + *Rshift_ret = insn_ptr[2]>>8 & 0xF; + *type_ret = (insn_ptr[2] & 1) ? 1 : 0; + if (insn_ptr[10] == 0xC808 // TST #8, R0 + && insn_ptr[11] == 0x8900 // BT .+4 + && insn_ptr[12] == (insn_ptr[2] | 0x0018) // SHL[LR]8 Rshift + ) { + if (insn_ptr[13] == 0xC810 // TST #16, R0 + && insn_ptr[14] == 0x8900 // BT .+4 + && insn_ptr[15] == (insn_ptr[2] | 0x0028) // SHL[LR]16 Rshift + ) { + *max_ret = 31; + static const uint8_t cycles_array[32] = + {20,19,19,18,20,19,19,18,19,18,18,17,19,18,18,17, + 19,18,18,17,19,18,18,17,18,17,17,16,18,17,17,16}; + *cycles_ret = cycles_array; + return 16; + } else { + *max_ret = 15; + static const uint8_t cycles_array[16] = + {16,15,15,14,16,15,15,14,15,14,14,13,15,14,14,13}; + *cycles_ret = cycles_array; + return 13; + } + } else { + *max_ret = 7; + static const uint8_t cycles_array[8] = {12,11,11,10,12,11,11,10}; + *cycles_ret = cycles_array; + return 10; + } + } + + if ((insn_ptr[0] & 0xF0FF) == 0x0023 // BRAF Rcount_adjusted + && ((insn_ptr[2] & 0xF0DE) == 0x4000 // SH[LA][LR] Rshift + || (insn_ptr[2] & 0xF0FE) == 0x4004) // ROT[LR] Rshift + ) { + unsigned int num_insns = 3; + while (num_insns < 2+32 && insn_ptr[num_insns] == insn_ptr[2]) { + num_insns++; + } + *Rcount_ret = insn_ptr[0]>>8 & 0xF; + *Rshift_ret = insn_ptr[2]>>8 & 0xF; + *max_ret = num_insns - 2; + switch (insn_ptr[2] & 0xFF) { + case 0x00: *type_ret = 0; break; + case 0x20: *type_ret = 0; break; + case 0x01: *type_ret = 1; break; + case 0x21: *type_ret = 2; break; + case 0x04: *type_ret = 3; break; + case 0x05: *type_ret = 4; break; + } + return num_insns; + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +#endif // OPTIMIZE_VARIABLE_SHIFTS + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/sh2.c b/yabause/src/psp/sh2.c new file mode 100644 index 0000000000..2eafcf17ca --- /dev/null +++ b/yabause/src/psp/sh2.c @@ -0,0 +1,7714 @@ +/* src/psp/sh2.c: SH-2 emulator with dynamic translation support + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*************************************************************************/ + +/* + * This SH-2 emulator is designed to execute SH-2 instructions by + * recompiling them into equivalent native machine code instructions, then + * executing that native code directly on the host CPU. + * + * The emulation loop (handled by sh2_run()) proceeds as follows: + * + * 1) If there is no current native block, sh2_run() calls jit_translate() + * to translate a block of SH-2 code beginning at the current PC. + * jit_translate() continues translating through the end of the block + * (determined heuristically) and returns the translated block to be + * executed. + * + * 3A) sh2_run() calls jit_exec(), which in turn calls the native code for + * the current, found, or created translated block. + * + * 3B) If translation failed for some reason, sh2_run() calls + * interpret_insn() to interpret and execute one instruction at the + * current PC, then starts over at step 1. If the failure occurred + * during RTL code generation (i.e., after the SH-2 block size was + * determined), the entire block is blacklisted to avoid repeatedly + * trying to translate the block at each subsequent address. + * + * 4) The translated code returns either when it reaches the end of the + * block, or when the number of cycles executed exceeds the requested + * cycle count. (For efficiency, cycle count checks are only performed + * before backward branches.) + * + * 5A) If the new PC at the end of the translated block is cached as the + * predicted branch target of the block, the current native block is + * set to that block. + * + * 5B) Otherwise, if a translated block exists for the new PC, it is cached + * as the predicted branch target and the current native block is set + * to that block. + * + * 5C) Otherwise, the current native block is cleared. + * + * During native code execution, if a write is made to a region of SH-2 + * memory containing translated blocks, all translated blocks containing + * that address are deleted by calling jit_clear_write(), so the modified + * code can be retranslated. + * + * If an external agent (such as DMA) modifies memory, sh2_write_notify() + * should be called to clear any translations in the affected area. In + * this case, the modified range is expanded to a multiple of + * 1< 0 + /* Predicted dynamic branch target(s) */ + BranchTargetInfo predicted[JIT_BRANCH_PREDICTION_SLOTS]; +#endif + +#ifdef JIT_BRANCH_PREDICT_STATIC + /* Predicted static branch target(s) */ + BranchTargetInfo *static_predict; // Dynamically-allocated array + uint32_t num_static_branches; // Number of entries in array +#endif + +#ifdef JIT_PROFILE + uint32_t call_count; // Number of calls made to this block + uint32_t cycle_count; // Number of SH-2 cycles spent in this block + uint64_t exec_time; // Time spent executing this block (when + // interpreting RTL, # of insns executed) +#endif +}; + +/* Hash function */ +#define JIT_HASH(addr) ((uint32_t)(addr) % JIT_TABLE_SIZE) + +/*-----------------------------------------------------------------------*/ + +/* Hash table of translated routines (shared between virtual processors) */ +static JitEntry jit_table[JIT_TABLE_SIZE]; // Record buffer +static JitEntry *jit_hashchain[JIT_TABLE_SIZE]; // Hash collision chains + +/* Head node for circular linked list of free entries */ +static JitEntry jit_freelist; + +/* Head node for circular linked list of active entries sorted by add + * timestamp; used to quickly find the oldest (least recently added) entry + * when we need to purge entries due to a full table or the data size limit */ +static JitEntry jit_lralist; + +/* Total size of translated data */ +static int32_t jit_total_data; // Signed to protect against underflow from bugs + +/* Size limit for translated data */ +static int32_t jit_data_limit = JIT_DATA_LIMIT_DEFAULT; + +/* Global timestamp for LRU expiration mechanism */ +static uint32_t jit_timestamp; + +/* Address from which data is being read */ +static uint32_t jit_PC; + +/* Flags indicating the status of each word in the current block + * (0: unknown, else a combination of WORD_INFO_* flags) */ +static uint8_t word_info[JIT_INSNS_PER_BLOCK]; +#define WORD_INFO_CODE (1<<0) // 1: this is a code word +#define WORD_INFO_FALLTHRU (1<<1) // 1: branch at this address jumps to the + // next translated instruction +#define WORD_INFO_BRA_RTS (1<<2) // 1: branch at this address jumps to a + // RTS/NOP pair +#define WORD_INFO_THREADED (1<<3) // 1: branch at this address jumps to + // another branch +#define WORD_INFO_SELECT (1<<4) // 1: branch at this address acts like + // a SELECT +#define WORD_INFO_LOOP_JSR (1<<5) // 1: branch at this address targets a + // JSR/BSR/BSRF + delay slot + // immediately preceding the block +#define WORD_INFO_FOLDABLE (1<<6) // 1: BSR/JSR at this address jumps to + // a foldable subroutine + +/* Branch target flag array (indicates which words in the current block + * are targeted by branches within the same block) */ +static uint32_t is_branch_target[(JIT_INSNS_PER_BLOCK+31)/32]; + +/* Subroutine call target array (used for subroutine folding) */ +static uint32_t subroutine_target[JIT_INSNS_PER_BLOCK]; + +/* Native implementation function pointers for folded subroutines */ +static SH2NativeFunctionPointer subroutine_native[JIT_INSNS_PER_BLOCK]; + +/* Threaded branch target array (indicates targets for branches marked with + * WORD_INFO_THREADED) */ +static uint32_t branch_thread_target[JIT_INSNS_PER_BLOCK]; + +/* Branch count for each threaded branch (for updating the cycle count) */ +static uint8_t branch_thread_count[JIT_INSNS_PER_BLOCK]; + +/* Branch target lookup table (indicates where in the native code each + * address is located) */ +static struct { + uint32_t sh2_address; // Address of SH-2 instruction + uint32_t rtl_label; // RTL label number corresponding to instruction +} btcache[JIT_BTCACHE_SIZE]; +static unsigned int btcache_index; // Where to store the next branch target + +/* Unresolved branch list (saves locations and targets of forward branches) */ +static struct { + uint32_t sh2_target; // Branch target (SH-2 address, 0 = unused entry) + uint32_t target_label; // RTL label number to be defined at branch target +} unres_branches[JIT_UNRES_BRANCH_SIZE]; + +/* JIT translation blacklist; note that ranges are always 16-bit aligned */ +static struct { + uint32_t start, end; // Zero in both fields indicates an empty entry + uint32_t timestamp; // jit_timestamp when region was last written to + uint32_t pad; // Pad entry size to 16 bytes +} blacklist[JIT_BLACKLIST_SIZE]; + +/* Table of addresses that may require blacklisting if they cause repeated + * faults (see jit_clear_write()); we use the purge table constants for + * this table because they are used for similar purposes */ +static struct { + uint32_t address; // Access address address (0 = unused) + uint32_t timestamp; // jit_timestamp when entry was added/updated + uint32_t count; // Number of jit_clear_write() faults on this block +} pending_blacklist[JIT_PURGE_TABLE_SIZE]; + +/* JIT purge table */ +static struct { + uint32_t address; // Block starting address (0 = unused) + uint32_t timestamp; // jit_timestamp when entry was added/updated + uint32_t count; // Number of purges on this block +} purge_table[JIT_PURGE_TABLE_SIZE]; + +/*----------------------------------*/ + +/* Should we ignore optimization hints? (Used when checking for + * optimizable subroutines to be folded.) */ +static uint8_t ignore_optimization_hints; + +/*----------------------------------*/ + +/* True if we can optimize out the MAC saturation check in this block + * (i.e. S was clear on entry and has not been modified) */ +static uint8_t can_optimize_mac_nosat; + +/* True if the current block contains any MAC instructions */ +static uint8_t block_contains_mac; + +/*----------------------------------*/ + +/* Bitmask of registers hinted as having constant values on block entry */ +static uint16_t is_constant_entry_reg; + +/*----------------------------------*/ + +#ifdef OPTIMIZE_KNOWN_VALUES + +/* Known state of registers (used for optimization when recompiling) */ +static uint32_t reg_knownbits[17];// 1 in a bit means that bit's value is known +static uint32_t reg_value[17]; // Value of known bits in each register +#define REG_R(n) (n) +#define REG_GBR 16 +/* PC is always constant when translating, and the remaining registers are + * generally unknown, so we don't waste time tracking them. */ + +#endif // OPTIMIZE_KNOWN_VALUES + +/*----------------------------------*/ + +/* Status of each register when viewed as a load/store address */ + +static struct { + + /* + * Status of the register: + * known == 0: Value is not a translation-time constant. + * known != 0: Value is within a known memory region. + * known > 0: Value has not significantly changed since the start + * of the block. + * known < 0: Value has been copied from another register + * pointing to a known memory region, or has been + * loaded from local data. + */ + int8_t known; + + /* + * Nonzero if this register is known to point to only data, and + * therefore the JIT overwrite checks can be skipped. + */ + uint8_t data_only; + + /* + * Type of memory pointed to by the register: + * 1: Arranged by 8-bit bytes. + * 2: Arranged by native 16-bit words. + */ + uint8_t type; + + /* + * For known == 0 && rtl_basereg != 0 && !data_only, nonzero if the + * direct_jit_pages[] entry for this pointer has already been checked, + * else zero. + */ + uint8_t checked_djp; + + /* + * Nonzero if the "check_offset" field is valid. + */ + uint8_t checked; + + /* + * Last offset at which a JIT check was performed. + */ + int16_t check_offset; + + /* + * RTL register containing the base pointer for direct memory access, + * to which the register's value is added to form a native address for + * loads and stores. + * + * For known == 0: + * - rtl_basereg==0 means the register has not yet been used as a + * pointer, or its value has changed since the last such use. + * - rtl_basereg!=0 means the register has already been used as a + * pointer. If the RTL register's value is nonzero, it is a + * native pointer through which accesses can be performed + * directly; if the value is zero, the access must go through + * the fallback functions. + * + * For known != 0: + * - rtl_basereg==0 means the access must go through the fallback + * functions. + * - rtl_basereg!=0 means the access can be performed directly; the + * RTL register holds the base address to be added to the register + * value. + */ + uint16_t rtl_basereg; + + /* + * RTL register containing a native pointer for direct memory access + * (equal to rtl_basereg plus the register's value), or zero if no + * such register has yet been created. Only used if known != 0. + */ + uint16_t rtl_ptrreg; + + /* + * Base pointer for JIT page bitmap. Only used for statically-known, + * 16-bit, non-data pointers (known > 0 && type == 2 && !data_only). + */ + uint8_t *djp_base; + + /* + * Source address if this register was loaded from local data. + */ + uint32_t source; + +} pointer_status[16]; + +/* Register holding the native address corresponding to the stack pointer + * (R15) when SH2_OPTIMIZE_STACK is defined */ +static uint16_t stack_pointer; + +/* Bitmask of general-purpose registers containing values which could be + * pointers (used at scanning time to determine which registers need to be + * checked against the constant memory region used in optimization) */ +static uint16_t pointer_regs; +/* Bitmask of registers which are actually used to access memory */ +static uint16_t pointer_used; + +/* Bitmask of general-purpose registers containing local data addresses */ +static uint16_t pointer_local; + +/* Bitmask of general-purpose registers hinted to contain data pointers */ +static uint16_t is_data_pointer_reg; + +/* Array of flags indicating instructions which load data pointers + * (optimization hint) */ +static uint32_t is_data_pointer_load[(JIT_INSNS_PER_BLOCK+31)/32]; + +/*----------------------------------*/ + +#ifdef OPTIMIZE_STATE_BLOCK + +/* Array indicating which SH-2 state block fields are cached in RTL + * registers; if the rtlreg field is nonzero, the field should be accessed + * through that RTL register rather than being loaded from the state block. + * Indexed by the word offset into SH2State, i.e. offsetof(SH2State,REG) / 4 + * The index for a given structure offset can be found by calling + * state_cache_index(offsetof(...)), which returns -1 if the given field + * cannot be cached. */ +static struct { + uint32_t rtlreg; // RTL register containing the SH-2 reg's current value + int16_t offset; // Accumulated offset from constant additions (always + // zero if !OPTIMIZE_CONSTANT_ADDS) + uint8_t fixed; // Nonzero if we should always use this RTL register + // for this state field (rather than allocating a + // new register for each operation) + uint8_t flush; // Nonzero if we should always flush this cache + // register to memory during cache writeback + // (rather than only when it's marked dirty) +} state_cache[25], + saved_state_cache[25]; // Saved cache state (used by SAVE_STATE_CACHE()) + +/* Flags indicating whether a state block field is dirty (needs to be + * flushed); each bit corresponds to a state_cache[] index */ +static uint32_t state_dirty, saved_state_dirty; + +/* Register containing current value of SR.T, or 0 if none */ +static uint32_t cached_SR_T, saved_cached_SR_T; + +/* Nonzero if SR.T is dirty */ +static uint8_t dirty_SR_T, saved_dirty_SR_T; + +/* Cached known value of state->PC (overrides state_cache if nonzero) */ +static uint32_t cached_PC, saved_cached_PC; + +/* Last value actually stored to state->PC */ +static uint32_t stored_PC, saved_stored_PC; + +/* Function to convert a state block offset to a state_cache[] index */ +#ifdef __GNUC__ +__attribute__((const)) +#endif +static inline int state_cache_index(const uint32_t offset) { + if (offset < 25*4) { + return offset / 4; + } else { + return -1; + } +} + +/* Function to convert a state_cache[] index to a state block offset */ +#ifdef __GNUC__ +__attribute__((const)) +#endif +static inline uint32_t state_cache_field(const int index) { + return index * 4; +} + +#endif // OPTIMIZE_STATE_BLOCK + +/*----------------------------------*/ + +#ifdef OPTIMIZE_LOOP_REGISTERS + +/* Is this block a loop that can be optimized? */ +static uint8_t can_optimize_loop; + +/* Bitmask of registers live in the loop (indexed by state cache index) */ +static uint32_t loop_live_registers; + +/* Bitmask of registers whose initial values are used in the loop, and which + * therefore must be loaded ahead of time (indexed by state cache index) */ +static uint32_t loop_load_registers; + +/* Bitmask of registers changed in the loop (indexed by state cache index) */ +static uint32_t loop_changed_registers; + +/* Bitmask of registers which contain invariant pointers, i.e. pointers + * which are unchanged (or only offset by predecrement/postincrement + * addressing or ADD #imm) throughout the body of a loop */ +static uint32_t loop_invariant_pointers; + +#endif // OPTIMIZE_LOOP_REGISTERS + +/*-----------------------------------------------------------------------*/ + +#endif // ENABLE_JIT + +/*************************************************************************/ + +/* Local function declarations */ + +#ifdef ENABLE_JIT // Through the function declarations + +static JitEntry *jit_find(uint32_t address); +static NOINLINE JitEntry *jit_find_noinline(uint32_t address); + +static NOINLINE JitEntry *jit_translate(SH2State *state, uint32_t address); + +#if defined(PSP) && !defined(JIT_DEBUG_INTERPRET_RTL) +__attribute__((unused)) // We use custom assembly in this case +#endif +static inline void jit_exec(SH2State *state, JitEntry *entry); + +static FASTCALL void jit_clear_write(SH2State *state, uint32_t address); +static void jit_clear_range(uint32_t address, uint32_t size); +static void jit_clear_all(void); +static void jit_blacklist_range(uint32_t start, uint32_t end); + +static FASTCALL void jit_mark_purged(uint32_t address); +static int jit_check_purged(uint32_t address); + +#if JIT_BRANCH_PREDICTION_SLOTS > 0 +static NOINLINE JitEntry *jit_predict_branch(BranchTargetInfo *predicted, + const uint32_t target); +#endif + +#ifdef JIT_PROFILE +static NOINLINE void jit_print_profile(void); +#endif + +static NOINLINE void clear_entry(JitEntry *entry); +static void clear_oldest_entry(void); + +static inline void flush_native_cache(void *start, uint32_t length); + +__attribute__((const)) static inline int timestamp_compare( + uint32_t reference, uint32_t a, uint32_t b); + +/*----------------------------------*/ + +static int translate_block(SH2State *state, JitEntry *entry); +static int setup_pointers(SH2State * const state, JitEntry * const entry, + const unsigned int state_reg, + const int skip_preconditions, + unsigned int * const invalidate_label_ptr); + +static int scan_block(SH2State *state, JitEntry *entry, uint32_t address); +static inline void optimize_pointers( + uint32_t start_address, uint32_t address, unsigned int opcode, + uint32_t opcode_info, uint8_t pointer_map[16]); +static inline void optimize_pointers_mac( + uint32_t address, unsigned int opcode, uint8_t pointer_map[16], + uint32_t last_clrmac, int *stop_ret, int *rollback_ret); +#ifdef OPTIMIZE_LOOP_REGISTERS +static inline void check_loop_registers( + unsigned int opcode, uint32_t opcode_info); +#endif +#ifdef OPTIMIZE_BRANCH_SELECT +static inline int optimize_branch_select( + uint32_t start_address, uint32_t address, const uint16_t *fetch, + unsigned int opcode, uint32_t target); +#endif +static int optimize_fold_subroutine( + SH2State *state, const uint32_t start_address, uint32_t address, + const uint32_t target, const uint16_t delay_slot, uint8_t pointer_map[16], + uint32_t local_constant[16], SH2NativeFunctionPointer *native_ret); + +static int translate_insn(SH2State *state, JitEntry *entry, + unsigned int state_reg, int recursing, int is_last); +#ifdef JIT_BRANCH_PREDICT_STATIC +static int add_static_branch_terminator(JitEntry *entry, + unsigned int state_reg, + uint32_t address, unsigned int index); +#endif +#if JIT_BRANCH_PREDICTION_SLOTS > 0 +static FASTCALL JitEntry *update_branch_target(BranchTargetInfo *bti, + uint32_t address); +#endif + +/*----------------------------------*/ + +static uint32_t btcache_lookup(uint32_t address); +static int record_unresolved_branch(const JitEntry *entry, uint32_t sh2_target, + unsigned int target_label); + +static int writeback_state_cache(const JitEntry *entry, unsigned int state_reg, + int flush_fixed); +static void clear_state_cache(int clear_fixed); + +#endif // ENABLE_JIT + +/*-----------------------------------------------------------------------*/ + +/* Dummy, do-nothing callback function used as a default trace callback + * so that the caller doesn't have to check for a NULL function pointer */ +static void dummy_callback(void) {} + +/*************************************************************************/ +/********************** External interface routines **********************/ +/*************************************************************************/ + +/** + * sh2_init: Initialize the SH-2 core. Must be called before creating any + * virtual processors. + * + * [Parameters] + * None + * [Return value] + * Nonzero on success, zero on error + */ +int sh2_init(void) +{ + /* Set up the opcode information tables */ + init_opcode_info(); + + /* Default to no optimizations; let the caller decide what to enable */ + optimization_flags = 0; + + /* Set the dummy callback function in the trace callback pointers */ + trace_insn_callback = (SH2TraceInsnCallback *)dummy_callback; + trace_storeb_callback = (SH2TraceAccessCallback *)dummy_callback; + trace_storew_callback = (SH2TraceAccessCallback *)dummy_callback; + trace_storel_callback = (SH2TraceAccessCallback *)dummy_callback; + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_set_optimizations, sh2_get_optimizations: Set or retrieve the + * bitmask of currently-enabled optional optimizations, as defined by the + * SH2_OPTIMIZE_* constants. + * + * [Parameters] + * flags: Bitmask of optimizations to enable (sh2_set_optimizations() only) + * [Return value] + * Bitmask of enabled optimizations (sh2_get_optimizations() only) + */ +void sh2_set_optimizations(uint32_t flags) +{ + if (flags != optimization_flags) { +#ifdef ENABLE_JIT + jit_clear_all(); +#endif + optimization_flags = flags; + if (!(optimization_flags & SH2_OPTIMIZE_LOCAL_ACCESSES)) { + optimization_flags &= ~SH2_OPTIMIZE_LOCAL_POINTERS; + } + if (!(optimization_flags & SH2_OPTIMIZE_POINTERS)) { + optimization_flags &= ~SH2_OPTIMIZE_LOCAL_POINTERS; + optimization_flags &= ~SH2_OPTIMIZE_POINTERS_MAC; + } + } +} + +uint32_t sh2_get_optimizations(void) +{ + return optimization_flags; +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_set_jit_data_limit: Set the limit on the total size of translated + * code, in bytes of native code (or bytes of RTL data when using the RTL + * interpreter). Does nothing if dynamic translation is disabled. + * + * [Parameters] + * limit: Total JIT data size limit + * [Return value] + * None + */ +void sh2_set_jit_data_limit(uint32_t limit) +{ +#ifdef ENABLE_JIT + jit_data_limit = limit; +#endif +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_set_manual_optimization_callback: Set a callback function to be + * called when beginning to analyze a new block of SH-2 code. If the + * function returns nonzero, it is assumed to have translated a block + * starting at the given address into an optimized RTL instruction stream, + * and the normal block analysis and translation is skipped. + * + * [Parameters] + * funcptr: Callback function pointer (NULL to unset a previously-set + * function) + * [Return value] + * None + */ +void sh2_set_manual_optimization_callback(SH2OptimizeCallback *funcptr) +{ + manual_optimization_callback = funcptr; +} + +/*----------------------------------*/ + +/** + * sh2_set_cache_flush_callback: Set a callback function to be called when + * a range of addresses should be flushed from the native CPU's caches. + * This is called in preparation for executing a newly-translated block of + * code, so the given range must be flushed from both data and instruction + * caches. + * + * [Parameters] + * funcptr: Callback function pointer (NULL to unset a previously-set + * function) + * [Return value] + * None + */ +void sh2_set_cache_flush_callback(SH2CacheFlushCallback *funcptr) +{ + cache_flush_callback = funcptr; +} + +/*----------------------------------*/ + +/** + * sh2_set_invalid_opcode_callback: Set a callback function to be called + * when the SH-2 decoder encounters an invalid instruction. + * + * [Parameters] + * funcptr: Callback function pointer (NULL to unset a previously-set + * function) + * [Return value] + * None + */ +void sh2_set_invalid_opcode_callback(SH2InvalidOpcodeCallback *funcptr) +{ + invalid_opcode_callback = funcptr; +} + +/*----------------------------------*/ + +/** + * sh2_set_trace_insn_callback: Set a callback function to be used for + * tracing instructions as they are executed. The function is only called + * if tracing is enabled in the SH-2 core. + * + * [Parameters] + * funcptr: Callback function pointer (NULL to unset a previously-set + * function) + * [Return value] + * None + */ +void sh2_set_trace_insn_callback(SH2TraceInsnCallback *funcptr) +{ + trace_insn_callback = + funcptr ? funcptr : (SH2TraceInsnCallback *)dummy_callback; +} + +/*----------------------------------*/ + +/** + * sh2_set_trace_store[bwl]_callback: Set a callback function to be used + * for tracing 1-, 2-, or 4-byte write accesses, respectively. The + * function is only called if tracing is enabled in the SH-2 core. + * + * [Parameters] + * funcptr: Callback function pointer (NULL to unset a previously-set + * function) + * [Return value] + * None + */ +void sh2_set_trace_storeb_callback(SH2TraceAccessCallback *funcptr) +{ + trace_storeb_callback = + funcptr ? funcptr : (SH2TraceAccessCallback *)dummy_callback; +} + +void sh2_set_trace_storew_callback(SH2TraceAccessCallback *funcptr) +{ + trace_storew_callback = + funcptr ? funcptr : (SH2TraceAccessCallback *)dummy_callback; +} + +void sh2_set_trace_storel_callback(SH2TraceAccessCallback *funcptr) +{ + trace_storel_callback = + funcptr ? funcptr : (SH2TraceAccessCallback *)dummy_callback; +} + + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_alloc_direct_write_buffer: Allocate a buffer which can be passed + * as the write_buffer parameter to sh2_set_direct_access(). The buffer + * should be freed with free() when no longer needed, but no earlier than + * calling sh2_destroy() for all active virtual processors. + * + * [Parameters] + * size: Size of SH-2 address region to be marked as directly accessible + * [Return value] + * Buffer pointer, or NULL on error + */ +void *sh2_alloc_direct_write_buffer(uint32_t size) +{ + const uint32_t bufsize = + (size + ((1<> JIT_PAGE_BITS; + void *buffer = calloc(bufsize, 1); + if (UNLIKELY(!buffer)) { + DMSG("Failed to get 0x%X-byte buffer for 0x%X-byte region", + bufsize, size); + return NULL; + } + return buffer; +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_set_direct_access: Mark a region of memory as being directly + * accessible. The data format of the memory region is assumed to be + * sequential 16-bit words in native order. + * + * As memory is internally divided into 512k (2^19) byte pages, both + * sh2_address and size must be aligned to a multiple of 2^19. + * + * Passing a NULL parameter for native_address causes the region to be + * marked as not directly accessible. + * + * [Parameters] + * sh2_address: Base address of region in SH-2 address space + * native_address: Pointer to memory block in native address space + * size: Size of memory block, in bytes + * readable: Nonzero if region is readable, zero if execute-only + * write_buffer: Buffer for checking writes to this region (allocated + * with sh2_alloc_direct_write_buffer()), or NULL to + * indicate that the region is read- or execute-only + * [Return value] + * None + */ +void sh2_set_direct_access(uint32_t sh2_address, void *native_address, + uint32_t size, int readable, void *write_buffer) +{ +#ifdef ENABLE_JIT + if (UNLIKELY(((sh2_address | size) & ((1<<19)-1)) != 0)) { + DMSG("sh2_address (0x%08X) and size (0x%X) must be aligned to a" + " 19-bit page", sh2_address, size); + return; + } + + const unsigned int first_page = sh2_address >> 19; + const unsigned int last_page = ((sh2_address + size) >> 19) - 1; + if (native_address) { + void * const target = + (void *)((uintptr_t)native_address - (first_page << 19)); + void * const jit_target = write_buffer + ? (void *)((uintptr_t)write_buffer + - (first_page << (19 - JIT_PAGE_BITS))) + : NULL; + unsigned int page; + for (page = first_page; page <= last_page; page++) { + fetch_pages[page] = target; + direct_pages[page] = readable ? target : NULL; + direct_jit_pages[page] = jit_target; + } + } else { + unsigned int page; + for (page = first_page; page <= last_page; page++) { + fetch_pages[page] = NULL; + direct_pages[page] = NULL; + direct_jit_pages[page] = NULL; + } + } +#endif // ENABLE_JIT +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_set_byte_direct_access: Mark a region of memory as being directly + * accessible in 8-bit units. + * + * As memory is internally divided into 512k (2^19) byte pages, both + * sh2_address and size must be aligned to a multiple of 2^19. + * + * Passing a NULL parameter for native_address causes the region to be + * marked as not directly accessible. + * + * [Parameters] + * sh2_address: Base address of region in SH-2 address space + * native_address: Pointer to memory block in native address space + * size: Size of memory block, in bytes + * [Return value] + * None + */ +void sh2_set_byte_direct_access(uint32_t sh2_address, void *native_address, + uint32_t size) +{ +#ifdef ENABLE_JIT + if (UNLIKELY(((sh2_address | size) & ((1<<19)-1)) != 0)) { + DMSG("sh2_address (0x%08X) and size (0x%X) must be aligned to a" + " 19-bit page", sh2_address, size); + return; + } + + const unsigned int first_page = sh2_address >> 19; + const unsigned int last_page = ((sh2_address + size) >> 19) - 1; + if (native_address) { + void * const target = + (void *)((uintptr_t)native_address - (first_page << 19)); + unsigned int page; + for (page = first_page; page <= last_page; page++) { + byte_direct_pages[page] = target; + } + } else { + unsigned int page; + for (page = first_page; page <= last_page; page++) { + byte_direct_pages[page] = NULL; + } + } +#endif // ENABLE_JIT +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_optimize_hint_data_pointer_load: Provide a hint to the optimizer + * that the load or ALU instruction at the given address loads a pointer to + * a 16-bit direct-access data region. If pointer optimization (the + * SH2_OPTIMIZE_POINTERS flag) is enabled, accesses through the register so + * loaded will automatically use direct access to SH-2 memory, and will + * bypass the code overwrite checks normally executed for store operations. + * If pointer optimization is disabled, this hint has no effect. + * + * The effect of calling this function from any context other than within + * the manual optimization callback, or of specifying an instruction other + * than MOVA which does not set the register specified by the Rn field of + * the opcode, is undefined. + * + * [Parameters] + * state: Processor state block pointer passed to callback function + * address: Address of load instruction to mark as loading a data pointer + * [Return value] + * None + */ +void sh2_optimize_hint_data_pointer_load(SH2State *state, uint32_t address) +{ + PRECOND(state != NULL, return); + PRECOND(state->translate_entry != NULL, return); + +#ifdef ENABLE_JIT + + if (ignore_optimization_hints) { + return; + } + if (!(optimization_flags & SH2_OPTIMIZE_POINTERS)) { + return; + } + + if (address >= state->translate_entry->sh2_start) { + const unsigned int insn_index = + (address - state->translate_entry->sh2_start) / 2; + if (insn_index < lenof(is_data_pointer_load) * 32) { + is_data_pointer_load[insn_index / 32] |= 1 << (insn_index % 32); + } + } + +#endif // ENABLE_JIT +} + +/*----------------------------------*/ + +/** + * sh2_optimize_hint_data_pointer_register: Provide a hint to the + * optimizer that the given general-purpose register (R0 through R15) + * contains a pointer to a direct-access data region at the start of the + * block, and the memory region pointed to will not change over the life + * of the block. If pointer optimization (the SH2_OPTIMIZE_POINTERS flag) + * is enabled, accesses through that register (as long as the register's + * value is unchanged) will automatically use direct access to SH-2 memory, + * and will bypass the code overwrite checks normally executed for store + * operations; furthermore, the register's value will not be verified for + * address validity at block start time, unlike dynamically-detected + * pointers which are verified each time the block is called. If pointer + * optimization is disabled, this hint has no effect. + * + * The effect of calling this function from any context other than within + * the manual optimization callback is undefined. + * + * [Parameters] + * state: Processor state block pointer passed to callback function + * regnum: General-purpose register to mark as containing a data pointer + * [Return value] + * None + */ +void sh2_optimize_hint_data_pointer_register(SH2State *state, + unsigned int regnum) +{ + PRECOND(state != NULL, return); + PRECOND(state->translate_entry != NULL, return); + PRECOND(regnum < 16, return); + +#ifdef ENABLE_JIT + + if (ignore_optimization_hints) { + return; + } + if (!(optimization_flags & SH2_OPTIMIZE_POINTERS)) { + return; + } + + is_data_pointer_reg |= 1 << regnum; + +#endif // ENABLE_JIT +} + +/*----------------------------------*/ + +/** + * sh2_optimize_hint_constant_register: Provide a hint to the optimizer + * that the given general-purpose register (R0 through R15) contains a + * value which will be constant at block entry for the life of the block. + * Instructions which use the register as a source operand may be optimized + * to use the constant value rather than loading the register, and if + * subroutine folding (the SH2_OPTIMIZE_FOLD_SUBROUTINES flag) is enabled, + * JSRs through that register will be considered for subroutine folding + * using the address contained in that register at translation time. + * + * The effect of calling this function from any context other than within + * the manual optimization callback is undefined. + * + * [Parameters] + * state: Processor state block pointer passed to callback function + * regnum: General-purpose register to mark as containing a data pointer + * [Return value] + * None + */ +void sh2_optimize_hint_constant_register(SH2State *state, unsigned int regnum) +{ + PRECOND(state != NULL, return); + PRECOND(state->translate_entry != NULL, return); + PRECOND(regnum < 16, return); + +#ifdef ENABLE_JIT + + if (ignore_optimization_hints) { + return; + } + + is_constant_entry_reg |= 1 << regnum; + +#endif // ENABLE_JIT +} + +/*************************************************************************/ + +/** + * sh2_create: Create a new virtual SH-2 processor. + * + * [Parameters] + * None + * [Return value] + * SH-2 processor state block (NULL on error) + */ +SH2State *sh2_create(void) +{ + SH2State *state; + + state = malloc(sizeof(*state)); + if (!state) { + DMSG("Out of memory allocating state block"); + goto error_return; + } + state->next = state_list; + state_list = state; + + /* Allocate interrupt stack */ + state->interrupt_stack = + malloc(sizeof(*state->interrupt_stack) * INTERRUPT_STACK_SIZE); + if (!state->interrupt_stack) { + DMSG("Out of memory allocating interrupt stack"); + goto error_free_state; + } + + return state; + + error_free_state: + free(state); + error_return: + return NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_destroy: Destroy a virtual SH-2 processor. + * + * [Parameters] + * state: SH-2 processor state block + * [Return value] + * None + */ +void sh2_destroy(SH2State *state) +{ + if (!state) { + return; + } + + SH2State **prev_ptr; + for (prev_ptr = &state_list; *prev_ptr; prev_ptr = &((*prev_ptr)->next)) { + if ((*prev_ptr) == state) { + break; + } + } + if (!*prev_ptr) { + DMSG("%p not found in state list", state); + return; + } + (*prev_ptr) = state->next; + + free(state->interrupt_stack); + free(state); +} + +/*-----------------------------------------------------------------------*/ + +/** + * sh2_reset: Reset a virtual SH-2 processor. + * + * [Parameters] + * state: SH-2 processor state block + * [Return value] + * None + */ +void sh2_reset(SH2State *state) +{ + state->delay = 0; + state->asleep = 0; + state->need_interrupt_check = 0; + state->interrupt_stack_top = 0; + state->current_entry = NULL; + state->branch_type = SH2BRTYPE_NONE; + state->folding_subroutine = 0; + state->pending_select = 0; + state->just_branched = 0; + state->cached_shift_count = 0; + state->varshift_target_PC = 0; + state->division_target_PC = 0; + +#ifdef ENABLE_JIT + memset(blacklist, 0, sizeof(blacklist)); + memset(pending_blacklist, 0, sizeof(pending_blacklist)); + memset(purge_table, 0, sizeof(purge_table)); + jit_clear_all(); + jit_timestamp = 0; +#endif +} + +/*************************************************************************/ + +/** + * sh2_run: Execute instructions for the given number of clock cycles. + * + * [Parameters] + * state: SH-2 processor state block + * cycles: Number of clock cycles to execute + * [Return value] + * None + */ +#ifdef PSP +__attribute__((aligned(64))) +#endif +void sh2_run(SH2State *state, uint32_t cycles) +{ + /* Update the JIT timestamp counter once per call */ + jit_timestamp++; + + /* Save this in the state block for use by translated code */ + state->cycle_limit = cycles; + + /* Check for interrupts before we start executing (this will wake up + * the processor if necessary) */ + if (UNLIKELY(check_interrupts(state))) { +#ifdef ENABLE_JIT + state->current_entry = jit_find(state->PC); +#endif + } + + /* If the processor is sleeping, consume all remaining cycles */ + if (UNLIKELY(state->asleep)) { + state->cycles = state->cycle_limit; + return; + } + + /* In TRACE_LITE (but not TRACE_LITE_VERBOSE) mode, trace a single + * instruction at the current PC before we start executing */ +#if defined(TRACE_LITE) && !defined(TRACE_LITE_VERBOSE) + (*trace_insn_callback)(state, state->PC); +#endif + + /* Load this for faster access during the loop */ + JitEntry *current_entry = state->current_entry; + + /* Loop until we've consumed the requested number of execution cycles. + * There's no need to check state->delay here, because it's handled + * either at translation time or by interpret_insn() call. */ + + const uint32_t cycle_limit = state->cycle_limit; + +#if !defined(ENABLE_JIT) + + while (state->cycles < cycle_limit) { + interpret_insn(state); + } + +#else // ENABLE_JIT + +# if defined(PSP) && !defined(JIT_DEBUG_INTERPRET_RTL) + + /* GCC's optimizer fails horribly on this loop, so we have no choice + * but to write it ourselves. */ + +# ifdef JIT_PROFILE + uint32_t start_cycles, start; // As in jit_exec() +# endif + + asm(".set push; .set noreorder\n" + + "lw $t0, %[cycles](%[state])\n" + "beqz %[current_entry], 5f\n" + "sltu $v0, $t0, %[cycle_limit]\n" + /* In the non-trace, non-profile case, this NOP aligns the top of + * the loop to a 64-byte (cache-line) boundary, so that the quick- + * predict critical path fits within a single cache line. This + * provides a noticeable (sometimes >5%) boost to performance. */ + "nop\n" + + // Loop top, quick restart point (jit_translate() call is at the end) + "1:\n" + "beqz $v0, 9f\n" + + // Resume point after block translation + "2:\n" +# ifdef TRACE_LITE_VERBOSE + "move $a0, %[state]\n" + "jalr %[trace_insn_callback]\n" + "lw $a1, %[PC](%[state])\n" +# endif + + // jit_exec(state, current_entry); +# ifdef JIT_PROFILE +# ifndef TRACE_LITE_VERBOSE + "nop\n" // Fill branch delay slot from above +# endif + "jal sceKernelGetSystemTimeLow\n" + "lw %[start_cycles], %[cycles](%[state])\n" + "move %[start], $v0\n" +# endif + + "lw $v0, %[native_code](%[current_entry])\n" + "li $v1, 1\n" + "sb $v1, %[running](%[current_entry])\n" + "jalr $v0\n" + "move $a0, %[state]\n" + +# ifdef JIT_PROFILE + "jal sceKernelGetSystemTimeLow\n" + "nop\n" + "lw $v1, %[cycles](%[state])\n" + "lw $a0, %[call_count](%[current_entry])\n" + "lw $a1, %[cycle_count](%[current_entry])\n" + "lw $a2, %[exec_time](%[current_entry])\n" + "lw $a3, %[exec_time]+4(%[current_entry])\n" + "addiu $a0, $a0, 1\n" + "subu $v1, $v1, %[start_cycles]\n" + "addu $a1, $a1, $v1\n" + "subu $v0, $v0, %[start]\n" + "addu $v0, $a2, $v0\n" + "sltu $v1, $v0, $a2\n" + "addu $a3, $a3, $v1\n" + "sw $a0, %[call_count](%[current_entry])\n" + "sw $a1, %[cycle_count](%[current_entry])\n" + "sw $v0, %[exec_time](%[current_entry])\n" + "sw $a3, %[exec_time]+4(%[current_entry])\n" +# endif + + // Preload data used below + "lbu $v0, %[must_clear](%[current_entry])\n" + "lw $t0, %[cycles](%[state])\n" +# if JIT_BRANCH_PREDICTION_SLOTS > 0 + "lw $a1, %[PC](%[state])\n" + "lw $t1, %[predicted_target](%[current_entry])\n" +# endif + + // Clear current_entry->running and check must_clear + "bnez $v0, 8f\n" + "sb $zero, %[running](%[current_entry])\n" + +# if JIT_BRANCH_PREDICTION_SLOTS > 0 + // Check for a quickly-predicted branch + "sltu $v0, $t0, %[cycle_limit]\n" + "beql $a1, $t1, 1b\n" + "lw %[current_entry], %[predicted_entry](%[current_entry])\n" + // Otherwise call jit_predict_branch() to get the next block + "jal %[jit_predict_branch]\n" + "addiu $a0, %[current_entry], %[predicted]\n" +# else + // Branch prediction is disabled, so just look up a block manually + "jal %[jit_find_noinline]\n" + "lw $a0, %[PC](%[state])\n" +# endif + // Loop back if we found a block, else translate it + "3:\n" + "lw $t0, %[cycles](%[state])\n" + "4:\n" + "move %[current_entry], $v0\n" + "bnez %[current_entry], 1b\n" + "sltu $v0, $t0, %[cycle_limit]\n" + + // Cycle check and jit_translate() call for not-found blocks + "5:\n" + "beqz $v0, 9f\n" + "move $a0, %[state]\n" + "6:\n" + "jal %[jit_translate]\n" + "lw $a1, %[PC](%[state])\n" + "bnez $v0, 2b\n" + "move %[current_entry], $v0\n" + + // interpret_insn() call for translation failure + "jal interpret_insn\n" + "move $a0, %[state]\n" + "jal %[jit_find_noinline]\n" + "lw $a0, %[PC](%[state])\n" + "b 4b\n" + "lw $t0, %[cycles](%[state])\n" + + // clear_entry() and jit_find_noinline() for purged blocks + "8:\n" + "jal %[clear_entry]\n" + "move $a0, %[current_entry]\n" + "jal %[jit_find_noinline]\n" + "lw $a0, %[PC](%[state])\n" + "b 4b\n" + "lw $t0, %[cycles](%[state])\n" + + // End of loop + "9:\n" + ".set pop" + : [current_entry] "=r" (current_entry), +# ifdef JIT_PROFILE + [start_cycles] "=&r" (start_cycles), + [start] "=&r" (start), +# endif + "=m" (*state) + : [state] "r" (state), [cycle_limit] "r" (cycle_limit), + "0" (current_entry), +# ifdef TRACE_LITE_VERBOSE + [trace_insn_callback] "r" (trace_insn_callback), +# endif + [PC] "i" (offsetof(SH2State,PC)), + [cycles] "i" (offsetof(SH2State,cycles)), + [native_code] "i" (offsetof(JitEntry,native_code)), + [running] "i" (offsetof(JitEntry,running)), + [must_clear] "i" (offsetof(JitEntry,must_clear)), +# if JIT_BRANCH_PREDICTION_SLOTS > 0 + [predicted] "i" (offsetof(JitEntry,predicted)), + [predicted_target] "i" (offsetof(JitEntry,predicted[0].target)), + [predicted_entry] "i" (offsetof(JitEntry,predicted[0].entry)), +# endif +# ifdef JIT_PROFILE + [call_count] "i" (offsetof(JitEntry,call_count)), + [cycle_count] "i" (offsetof(JitEntry,cycle_count)), + [exec_time] "i" (offsetof(JitEntry,exec_time)), +# endif +# if JIT_BRANCH_PREDICTION_SLOTS > 0 + [jit_predict_branch] "S" (jit_predict_branch), +# endif + [jit_translate] "S" (jit_translate), + [clear_entry] "S" (clear_entry), + [jit_find_noinline] "S" (jit_find_noinline) + : "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", + "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "ra" + ); + +# else // !(PSP && !JIT_DEBUG_INTERPRET_RTL) + + while (state->cycles < cycle_limit) { + + /* If we don't have a current native code block, translate from + * the current address */ + if (UNLIKELY(!current_entry)) { + current_entry = jit_translate(state, state->PC); + } + + /* If we (now) have a native code block, execute from it */ + if (LIKELY(current_entry)) { + quick_restart:; + +# ifdef TRACE_LITE_VERBOSE + (*trace_insn_callback)(state, state->PC); +# endif + jit_exec(state, current_entry); + +# if JIT_BRANCH_PREDICTION_SLOTS > 0 + const uint32_t new_PC = state->PC; + BranchTargetInfo * const predicted = ¤t_entry->predicted[0]; + + if (UNLIKELY(current_entry->must_clear)) { + /* A purge was requested, so clear the just-executed entry */ + clear_entry(current_entry); + current_entry = jit_find_noinline(new_PC); + continue; + } + + /* Even if we're using multiple prediction slots, handle the + * first one specially for increased speed if it's a correct + * prediction; we make the safe (for all practical purposes) + * assumption that the target PC is not zero */ + if (new_PC == predicted->target) { + current_entry = predicted->entry; + if (LIKELY(state->cycles < cycle_limit)) { + goto quick_restart; + } + } else { + current_entry = jit_predict_branch(predicted, new_PC); + } +# else // branch prediction disabled + if (UNLIKELY(current_entry->must_clear)) { + clear_entry(current_entry); + } + current_entry = jit_find_noinline(state->PC); +# endif + + } else { // !current_entry + + /* We couldn't translate the code at this address, so interpret + * it instead */ + interpret_insn(state); + + } // if (current_entry) + + } // while (state->cycles < cycle_limit) + +# endif // PSP && !JIT_DEBUG_INTERPRET_RTL +#endif // ENABLE_JIT + + state->current_entry = current_entry; + +#if defined(ENABLE_JIT) && defined(JIT_PROFILE) + static int cycles_for_profile; + cycles_for_profile += state->cycle_limit; + if (cycles_for_profile >= JIT_PROFILE_INTERVAL) { + cycles_for_profile = 0; + jit_print_profile(); + } // if (cycles_for_profile >= JIT_PROFILE_INTERVAL) +#endif // ENABLE_JIT && JIT_PROFILE +} + +/*************************************************************************/ + +/** + * sh2_signal_interrupt: Signal an interrupt to a virtual SH-2 processor. + * The interrupt is ignored if another interrupt on the same vector has + * already been signalled. + * + * [Parameters] + * state: SH-2 processor state block + * vector: Interrupt vector (0-127) + * level: Interrupt level (0-15, or 16 for NMI) + * [Return value] + * None + */ +void sh2_signal_interrupt(SH2State *state, unsigned int vector, + unsigned int level) +{ + int i; + + if (UNLIKELY(state->interrupt_stack_top >= INTERRUPT_STACK_SIZE)) { + DMSG("WARNING: dropped interrupt <%u,%u> due to full stack", + vector, level); + DMSG("Interrupt stack:"); + for (i = state->interrupt_stack_top - 1; i >= 0; i--) { + DMSG(" <%3u,%2u>", state->interrupt_stack[i].vector, + state->interrupt_stack[i].level); + } + return; + } + + for (i = 0; i < state->interrupt_stack_top; i++) { + if (state->interrupt_stack[i].vector == vector) { + return; // This interrupt is already raised + } + } + + for (i = 0; i < state->interrupt_stack_top; i++) { + if (state->interrupt_stack[i].level > level) { + memmove(&state->interrupt_stack[i+1], &state->interrupt_stack[i], + sizeof(*state->interrupt_stack) + * (state->interrupt_stack_top - i)); + break; + } + } + state->interrupt_stack[i].level = level; + state->interrupt_stack[i].vector = vector; + state->interrupt_stack_top++; +} + +/*************************************************************************/ + +/** + * sh2_write_notify: Called when an external agent modifies memory. + * Used here to clear any JIT translations in the modified range. + * + * [Parameters] + * address: Beginning of address range to which data was written + * size: Size of address range to which data was written (in bytes) + * [Return value] + * None + */ +void sh2_write_notify(uint32_t address, uint32_t size) +{ +#ifdef ENABLE_JIT + jit_clear_range(address, size); +#endif +} + +/*************************************************************************/ +/******************** General-purpose local routines *********************/ +/*************************************************************************/ + +/** + * check_interrupts: Check whether there are any pending interrupts, and + * service the highest-priority one if so. + * + * [Parameters] + * state: Processor state block + * [Return value] + * Nonzero if an interrupt was serviced, else zero + * [Notes] + * This routine is exported for use by sh2-interpret.c. + */ +FASTCALL int check_interrupts(SH2State *state) +{ + if (state->interrupt_stack_top > 0) { + const int level = + state->interrupt_stack[state->interrupt_stack_top-1].level; + if (level > ((state->SR & SR_I) >> SR_I_SHIFT)) { + const int vector = + state->interrupt_stack[state->interrupt_stack_top-1].vector; + state->R[15] -= 4; +#if defined(TRACE) || defined(TRACE_STEALTH) + (*trace_storel_callback)(state->R[15], state->SR); +#endif + MappedMemoryWriteLong(state->R[15], state->SR); + state->R[15] -= 4; +#if defined(TRACE) || defined(TRACE_STEALTH) + (*trace_storel_callback)(state->R[15], state->PC); +#endif + MappedMemoryWriteLong(state->R[15], state->PC); + state->SR &= ~SR_I; + state->SR |= level << SR_I_SHIFT; + state->PC = MappedMemoryReadLong(state->VBR + (vector << 2)); + state->asleep = 0; + state->interrupt_stack_top--; + return 1; + } + } + return 0; +} + +/*************************************************************************/ +/**************** Dynamic translation management routines ****************/ +/*************************************************************************/ + +#ifdef ENABLE_JIT // Through the end of the file + +/*************************************************************************/ + +/** + * jit_find, jit_find_noinline: Find the translated block for a given + * address, if any. jit_find_noinline() is explicitly marked NOINLINE to + * minimize register pressure when called from sh2_run(). + * + * [Parameters] + * address: Start address in SH-2 address space + * [Return value] + * Translated block, or NULL if no such block exists + */ +static JitEntry *jit_find(uint32_t address) +{ + const int hashval = JIT_HASH(address); + JitEntry *entry = jit_hashchain[hashval]; + while (entry) { + if (entry->sh2_start == address) { + return entry; + } + entry = entry->next; + } + return NULL; +} + +static NOINLINE JitEntry *jit_find_noinline(uint32_t address) +{ + return jit_find(address); +} + +/*************************************************************************/ + +/** + * jit_translate: Dynamically translate a block of instructions starting + * at the given address. If a translation already exists for the given + * address, it is cleared. + * + * [Parameters] + * state: SH-2 processor state block + * address: Start address in SH-2 address space + * [Return value] + * Translated block, or NULL on error + */ +static NOINLINE JitEntry *jit_translate(SH2State *state, uint32_t address) +{ + JitEntry *entry; + int index; + +#ifdef PSP_TIME_TRANSLATION + static uint32_t total, count; + const uint32_t a = sceKernelGetSystemTimeLow(); +#endif + + /* Update the timestamp on every call */ + jit_timestamp++; + + /* First check for untranslatable addresses */ + if (UNLIKELY(address == 0)) { + /* We use address 0 to indicate an unused entry, so we can't + * translate from address 0. But this should never happen except + * in pathological cases (or unhandled exceptions), so just punt + * and let the interpreter handle it. */ + return NULL; + } + if (UNLIKELY(address & 1)) { + /* Odd addresses are invalid, so we can't translate them in the + * first place. */ + return NULL; + } + if (UNLIKELY(!fetch_pages[address >> 19])) { + /* Don't try to translate anything from non-directly-accessible + * memory, since we can't track changes to such memory. */ + return NULL; + } + + /* Check whether the starting address is blacklisted */ + for (index = 0; index < lenof(blacklist); index++) { + uint32_t age = jit_timestamp - blacklist[index].timestamp; + if (age >= JIT_BLACKLIST_EXPIRE) { + /* Entry expired, so clear it */ + blacklist[index].start = blacklist[index].end = 0; + continue; + } + if (blacklist[index].start <= address + && address <= blacklist[index].end) { + return NULL; + } + } + + /* Clear out any existing translation; if we've reached the data size + * limit, also evict old entries until we're back under the limit */ + if ((entry = jit_find(address)) != NULL) { + clear_entry(entry); + } + if (UNLIKELY(jit_total_data >= jit_data_limit)) { +#ifdef JIT_DEBUG + DMSG("JIT data size over limit (%u >= %u), clearing entries", + jit_total_data, jit_data_limit); +#endif + while (jit_total_data >= jit_data_limit) { + clear_oldest_entry(); + } + } + + /* Obtain a free entry for this block */ + if (jit_freelist.next == &jit_freelist) { + /* No free entries, so clear the oldest one and use it */ +#ifdef JIT_DEBUG + DMSG("No free slots for code at 0x%08X, clearing oldest", address); +#endif + clear_oldest_entry(); + if (UNLIKELY(jit_freelist.next == &jit_freelist)) { // paranoia + DMSG("BUG: failed to update free list"); + return 0; + } + } + entry = jit_freelist.next; + jit_freelist.next = entry->next; + jit_freelist.next->prev = &jit_freelist; + + /* Store the entry pointer in the state block, for reference by other + * functions */ + state->translate_entry = entry; + + /* Initialize the new entry */ + entry->rtl = rtl_create_block(); + if (!entry->rtl) { + DMSG("No memory for code at 0x%08X", address); + goto fail; + } + const int hashval = JIT_HASH(address); + entry->next = jit_hashchain[hashval]; + if (entry->next) { + entry->next->prev = entry; + } + jit_hashchain[hashval] = entry; + entry->prev = NULL; + entry->lra_next = &jit_lralist; + entry->lra_prev = jit_lralist.lra_prev; + entry->lra_next->lra_prev = entry; + entry->lra_prev->lra_next = entry; + entry->pred_ref_head.next = &entry->pred_ref_head; + entry->pred_ref_head.prev = &entry->pred_ref_head; + entry->sh2_start = address; + entry->sh2_end = 0; + entry->native_code = NULL; + entry->native_length = 0; + entry->timestamp = jit_timestamp; + entry->running = 0; + entry->must_clear = 0; +#if JIT_BRANCH_PREDICTION_SLOTS > 0 + for (index = 0; index < JIT_BRANCH_PREDICTION_SLOTS; index++) { + entry->predicted[index].target = 0; + entry->predicted[index].entry = NULL; + entry->predicted[index].next = NULL; + entry->predicted[index].prev = NULL; + } +#endif +#ifdef JIT_BRANCH_PREDICT_STATIC + entry->static_predict = NULL; + entry->num_static_branches = 0; +#endif +#ifdef JIT_PROFILE + entry->call_count = 0; + entry->cycle_count = 0; + entry->exec_time = 0; +#endif + + /* Perform the SH-2 -> RTL translation */ + if (UNLIKELY(!translate_block(state, entry))) { + DMSG("Failed to translate block at 0x%08X", address); + /* If we found a valid end address for the block, blacklist the + * entire block so we don't waste time trying to translate from + * every successive instruction. Otherwise, just blacklist the + * first address in case we come back here again. */ + if (entry->sh2_end) { + jit_blacklist_range(address, entry->sh2_end); + } else { + jit_blacklist_range(address, address+1); + } + goto clear_and_fail; + } + + /* Translate from RTL to native code */ +#ifndef JIT_DEBUG_INTERPRET_RTL + if (UNLIKELY(!rtl_translate_block(entry->rtl, &entry->native_code, + &entry->native_length))) { + DMSG("Failed to translate block at 0x%08X", entry->sh2_start); + goto clear_and_fail; + } + /* Make sure the new code isn't masked by cached data */ + flush_native_cache(entry->native_code, entry->native_length); + rtl_destroy_block(entry->rtl); + entry->rtl = NULL; +#endif + + /* Update JIT management data */ + uint8_t *jit_base = direct_jit_pages[entry->sh2_start >> 19]; + if (jit_base) { + for (index = entry->sh2_start >> JIT_PAGE_BITS; + index <= entry->sh2_end >> JIT_PAGE_BITS; + index++ + ) { + JIT_PAGE_SET(jit_base, index); + } + } +#ifdef JIT_DEBUG_INTERPRET_RTL + /* Guesstimate how much memory was used (including the insn-to-unit LUT + * later allocated by rtl_execute_block()); we assume average ratios of + * 5:8 insns:regs, and 1:8 insns:units (we don't bother with labels + * since they only require 2 bytes each and are generally less than 10% + * of insns). */ + entry->native_length = ((sizeof(RTLInsn)+2) + + sizeof(RTLRegister)*5/8 + + sizeof(RTLUnit)/8) * entry->rtl->insns_size + + sizeof(RTLBlock); +#endif + jit_total_data += entry->native_length; + +#ifdef PSP_TIME_TRANSLATION + const uint32_t b = sceKernelGetSystemTimeLow(); + total += b-a; + count += ((current_entry->sh2_end + 1) - current_entry->sh2_start) / 2; + DMSG("%u/%u = %.3f us/insn", total, count, (float)total/count); +#endif + + /* All done */ + state->translate_entry = NULL; + return entry; + + /* Error handling */ + clear_and_fail: + clear_entry(entry); + fail: + state->translate_entry = NULL; + return NULL; +} + +/*************************************************************************/ + +/** + * jit_clear_write: Clear any translation which includes the given + * address. Intended to be called from within translated code. + * + * [Parameters] + * state: Processor state block + * address: Address to which data was written + * [Return value] + * None + */ +static FASTCALL void jit_clear_write(SH2State *state, uint32_t address) +{ + int index; + + /* If it's a blacklisted address, we don't need to do anything (since + * the address couldn't have been translated); just update the entry's + * write timestamp and return */ + for (index = 0; index < lenof(blacklist); index++) { + if (blacklist[index].start <= address + && address <= blacklist[index].end) { + blacklist[index].timestamp = jit_timestamp; + return; + } + } + +#ifdef JIT_DEBUG + DMSG("WARNING: jit_clear_write(0x%08X) from PC %08X", address, state->PC); +#endif + + /* Clear any translations on the affected page */ + uint8_t * const jit_base = direct_jit_pages[address >> 19]; + const uint32_t page = address >> JIT_PAGE_BITS; + const uint32_t page_start = address & -(1 << JIT_PAGE_BITS); + const uint32_t page_end = page_start + ((1 << JIT_PAGE_BITS) - 1); + JIT_PAGE_CLEAR(jit_base, page); + int found_entry = 0; // Flag: Did we find a block on this page? + int do_blacklist = 0; // Flag: Blacklist this address? + for (index = 0; index < JIT_TABLE_SIZE; index++) { + if (jit_table[index].sh2_start == 0) { + continue; + } + if (jit_table[index].sh2_start <= page_end + && jit_table[index].sh2_end >= page_start + ) { + found_entry = 1; + if (UNLIKELY(jit_table[index].running)) { + jit_table[index].must_clear = 1; + do_blacklist = 1; + } else { + clear_entry(&jit_table[index]); + } + } + } + + /* If we fault on the same address multiple times in rapid succession, + * blacklist the address even if it wasn't in a running block. (We + * don't blacklist this case on the first fault because it could just + * be overwriting a block of memory with new code.) */ + if (found_entry && !do_blacklist) { + int empty = -1; + unsigned int oldest = 0; + for (index = 0; index < lenof(pending_blacklist); index++) { + if (pending_blacklist[index].address == address) { + if (jit_timestamp - pending_blacklist[index].timestamp + > JIT_PURGE_EXPIRE + ) { + pending_blacklist[index].count = 0; + } + break; + } else if (pending_blacklist[index].address != 0) { + if (pending_blacklist[index].timestamp + < pending_blacklist[oldest].timestamp + ) { + oldest = index; + } + } else if (empty < 0) { + empty = index; + } + } + if (index >= lenof(pending_blacklist)) { + if (empty >= 0) { + index = empty; + } else { + index = oldest; + } + pending_blacklist[index].address = address; + pending_blacklist[index].count = 0; + } + pending_blacklist[index].timestamp = jit_timestamp; + pending_blacklist[index].count++; + if (pending_blacklist[index].count >= JIT_PURGE_THRESHOLD) { +#ifdef JIT_DEBUG + DMSG("Blacklisting 0x%08X due to repeated faults",address); +#endif + do_blacklist = 1; + } + } + + /* Blacklist this address if appropriate. We always keep the blacklist + * entries 16-bit-aligned; since we don't pass the write size in, we + * assume the write is 32-bit-sized if on a 32-bit-aligned address, and + * 16-bit-sized otherwise. */ + if (do_blacklist) { + const uint32_t start = address & ~1; + const uint32_t end = (address & ~3) + 3; + jit_blacklist_range(start, end); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * jit_clear_range: Clear any translation on any page touched by the given + * range of addresses. Intended to be called when an external agent + * modifies memory. + * + * [Parameters] + * address: Beginning of address range to which data was written + * size: Size of address range to which data was written (in bytes) + * [Return value] + * None + */ +static void jit_clear_range(uint32_t address, uint32_t size) +{ + const uint32_t first_page = address >> JIT_PAGE_BITS; + const uint32_t last_page = (address + size - 1) >> JIT_PAGE_BITS; + const uint32_t page_start = first_page << JIT_PAGE_BITS; + const uint32_t page_end = ((last_page+1) << JIT_PAGE_BITS) - 1; + + int page; + for (page = first_page; page <= last_page; page++) { + if (direct_jit_pages[page >> (19-JIT_PAGE_BITS)]) { + JIT_PAGE_CLEAR(direct_jit_pages[page >> (19-JIT_PAGE_BITS)], page); + } + } + + int index; + for (index = 0; index < JIT_TABLE_SIZE; index++) { + if (jit_table[index].sh2_start == 0) { + continue; + } + if (jit_table[index].sh2_start <= page_end + && jit_table[index].sh2_end >= page_start + ) { + clear_entry(&jit_table[index]); + } + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * jit_clear_all: Clear all translations. Intended to be used when + * initializing/resetting the processor or when a change in optimization + * options invalidates existing translations. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void jit_clear_all(void) +{ + int index; + + /* Clear all JIT bitmaps */ + for (index = 0; index < lenof(direct_jit_pages); index++) { + if (direct_jit_pages[index]) { + uintptr_t address = (uintptr_t)direct_jit_pages[index]; + address += index << (19 - JIT_PAGE_BITS); + memset((void *)address, 0, (1<<19) >> JIT_PAGE_BITS); + } + } + + /* Clear all entry data (don't call clear_entry() to avoid wasting time + * updating management data for every single entry) */ + for (index = 0; index < JIT_TABLE_SIZE; index++) { + if (jit_table[index].sh2_start) { + free(jit_table[index].native_code); + jit_table[index].native_code = NULL; + rtl_destroy_block(jit_table[index].rtl); + jit_table[index].rtl = NULL; +#ifdef JIT_BRANCH_PREDICT_STATIC + free(jit_table[index].static_predict); + jit_table[index].static_predict = NULL; +#endif + jit_table[index].sh2_start = 0; + } + jit_table[index].timestamp = 0; + } + for (index = 0; index < JIT_TABLE_SIZE; index++) { + jit_hashchain[index] = NULL; + } + + /* Put every entry in the free list */ + jit_freelist.next = &jit_table[0]; + jit_table[0].prev = &jit_freelist; + for (index = 1; index < JIT_TABLE_SIZE; index++) { + jit_table[index-1].next = &jit_table[index]; + jit_table[index].prev = &jit_table[index-1]; + } + jit_table[JIT_TABLE_SIZE-1].next = &jit_freelist; + jit_freelist.prev = &jit_table[JIT_TABLE_SIZE-1]; + + /* Clear other management data */ + jit_lralist.lra_next = &jit_lralist; + jit_lralist.lra_prev = &jit_lralist; + jit_total_data = 0; + SH2State *state; + for (state = state_list; state; state = state->next) { + state->current_entry = NULL; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * jit_blacklist_range: Add the specified range to the JIT blacklist. + * + * [Parameters] + * start: First byte in range to blacklist + * end: Last byte in range to blacklist + * [Return value] + * None + */ +static void jit_blacklist_range(uint32_t start, uint32_t end) +{ + unsigned int index; + + /* Merge this to the beginning or end of another entry if possible */ + for (index = 0; index < lenof(blacklist); index++) { + if (blacklist[index].start == end+1) { + blacklist[index].start = start; + /* See if there's another one we can join to this one */ + unsigned int index2; + for (index2 = 0; index2 < lenof(blacklist); index2++) { + if (start == blacklist[index2].end+1) { + blacklist[index].start = blacklist[index2].start; + blacklist[index2].start = blacklist[index2].end = 0; + break; + } + } + goto entry_added; + } else if (blacklist[index].end+1 == start) { + blacklist[index].end = end; + unsigned int index2; + for (index2 = 0; index2 < lenof(blacklist); index2++) { + if (end+1 == blacklist[index2].start) { + blacklist[index].end = blacklist[index2].end; + blacklist[index2].start = blacklist[index2].end = 0; + break; + } + } + goto entry_added; + } + } + + /* We didn't find an entry to merge this into, so allocate a new one */ + for (index = 0; index < lenof(blacklist); index++) { + if (!blacklist[index].start && !blacklist[index].end) { + break; + } + } + + /* If the table is full, purge the oldest entry */ + if (index >= lenof(blacklist)) { + unsigned int oldest = 0; + for (index = 1; index < lenof(blacklist); index++) { + if (timestamp_compare(jit_timestamp, + blacklist[index].timestamp, + blacklist[oldest].timestamp) < 0) { + oldest = index; + } + } +#ifdef JIT_DEBUG + DMSG("Blacklist full, purging entry %d (0x%X-0x%X)", + oldest, blacklist[oldest].start, blacklist[oldest].end); +#endif + index = oldest; + } + + /* Fill in the new entry */ + blacklist[index].start = start; + blacklist[index].end = end; + entry_added: + blacklist[index].timestamp = jit_timestamp; +} + +/*************************************************************************/ + +/** + * jit_mark_purged: Mark the given block as purged due to precondition + * failure. + * + * [Parameters] + * address: Block starting address + * [Return value] + * None + */ +static FASTCALL void jit_mark_purged(uint32_t address) +{ + unsigned int i; + unsigned int oldest = 0; // Oldest entry (which will be overwritten) + unsigned int oldest_age = 0; // Age of purge_table[oldest] + +#ifdef JIT_DEBUG + DMSG("WARNING: Purging block 0x%08X", address); +#endif + + for (i = 0; i < lenof(purge_table); i++) { + const uint32_t age = jit_timestamp - purge_table[i].timestamp; + if (!purge_table[i].address) { + /* Empty entry, so treat it as the oldest one for overwriting */ + oldest = i; + oldest_age = JIT_PURGE_EXPIRE; + } else if (age >= JIT_PURGE_EXPIRE) { + /* Entry has expired, so clear it out */ + purge_table[i].address = 0; + oldest = i; + oldest_age = JIT_PURGE_EXPIRE; + } else if (purge_table[i].address == address) { + /* If this block is already in the list (and not expired), + * increment its purge counter and stop immediately */ + purge_table[i].timestamp = jit_timestamp; + purge_table[i].count++; + return; + } else if (age > oldest_age) { + /* If this is the oldest entry in the table, we'll overwrite it */ + oldest = i; + oldest_age = age; + } + } + + /* We didn't find the entry in the table, so overwrite the oldest slot + * (which may be an empty slot) with this block's information */ + purge_table[oldest].address = address; + purge_table[oldest].timestamp = jit_timestamp; + purge_table[oldest].count = 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * jit_check_purged: Check whether the given block has been purged often + * enough to require disabling optimizations with preconditions. + * + * [Parameters] + * address: Block starting address + * [Return value] + * Nonzero if relevant optimizations should be disabled, else zero + */ +static int jit_check_purged(uint32_t address) +{ + unsigned int i; + + for (i = 0; i < lenof(purge_table); i++) { + if (purge_table[i].address == address) { + const uint32_t age = jit_timestamp - purge_table[i].timestamp; + if (age >= JIT_PURGE_EXPIRE) { + purge_table[i].address = 0; + return 0; + } else { + return purge_table[i].count >= JIT_PURGE_THRESHOLD; + } + } + } + + return 0; +} + +/*************************************************************************/ + +#if JIT_BRANCH_PREDICTION_SLOTS > 0 + +/** + * jit_predict_branch: Attempt to predict a branch to "target" using the + * prediction array "predicted", and update the prediction table as + * appropriate. Helper function for sh2_run(), called after the first + * prediction table entry has been checked. + * + * [Parameters] + * target: Target SH-2 address + * predicted: JitEntry.predicted array from current translated block + * [Return value] + * Translated block for target address, or NULL if none was found + */ +static NOINLINE JitEntry *jit_predict_branch(BranchTargetInfo *predicted, + const uint32_t target) +{ +# if JIT_BRANCH_PREDICTION_SLOTS > 1 + /* First see if it's in any slot other than the first */ + unsigned int i; + for (i = 1; i < JIT_BRANCH_PREDICTION_SLOTS; i++) { + if (predicted[i].target != 0 && target == predicted[i].target) { + JitEntry *result = predicted[i].entry; +# ifdef JIT_BRANCH_PREDICTION_FLOAT + /* Shift it up to the previous slot so we can find it faster next + * time. If we have any code that alternates between two branch + * targets, we're going to take a major performance hit... */ + BranchTargetInfo *this_next = predicted[i].next; + BranchTargetInfo *this_prev = predicted[i].prev; + uint32_t this_target = predicted[i].target; + JitEntry *this_entry = predicted[i].entry; + predicted[i].target = predicted[i-1].target; + predicted[i].entry = predicted[i-1].entry; + if (predicted[i].entry) { + predicted[i].next = predicted[i-1].next; + predicted[i].prev = predicted[i-1].prev; + predicted[i].next->prev = &predicted[i]; + predicted[i].prev->next = &predicted[i]; + } + predicted[i-1].next = this_next; + predicted[i-1].prev = this_prev; + predicted[i-1].target = this_target; + predicted[i-1].entry = this_entry; + this_next->prev = &predicted[i-1]; + this_prev->next = &predicted[i-1]; +# endif // JIT_BRANCH_PREDICTION_FLOAT + return result; + } + } + /* Update the first empty slot, or the last slot if none are empty */ + for (i = 0; i < JIT_BRANCH_PREDICTION_SLOTS-1; i++) { + if (predicted[i].target == 0) { + break; + } + } + return update_branch_target(&predicted[i], target); +# else // JIT_BRANCH_PREDICTION_SLOTS == 1 + return update_branch_target(predicted, target); +# endif +} + +#endif // JIT_BRANCH_PREDICTION_SLOTS > 0 + +/*************************************************************************/ + +#ifdef JIT_PROFILE + +/** + * jit_print_profile: Print profiling statistics for translated code. + * + * [Parameters] + * None + * [Return value] + * None + */ +static NOINLINE void jit_print_profile(void) +{ + int top[JIT_PROFILE_TOP]; + unsigned int i; + + printf("\n"); + + printf("================= Top callees by time: =================\n"); + memset(top, -1, sizeof(top)); + for (i = 0; i < lenof(jit_table); i++) { + if (!jit_table[i].sh2_start) { + continue; + } + unsigned int j; + for (j = 0; j < lenof(top); j++) { + if (top[j] < 0 + || jit_table[i].exec_time > jit_table[top[j]].exec_time + ) { + unsigned int k; + for (k = lenof(top)-1; k > j; k--) { + top[k] = top[k-1]; + } + top[j] = i; + break; + } + } + } + printf( +# ifdef JIT_DEBUG_INTERPRET_RTL + "RTL insns" +# else + "Exec time" +# endif + " SH2 cyc. Native/SH2 # calls Block addr\n"); + printf("--------- -------- ---------- ------- ----------\n"); + for (i = 0; i < lenof(top) && top[i] >= 0; i++) { + printf("%9lld %10d %12.3f %9d 0x%08X\n", + (unsigned long long)jit_table[top[i]].exec_time, + jit_table[top[i]].cycle_count, + (double)jit_table[top[i]].exec_time + / (double)jit_table[top[i]].cycle_count, + jit_table[top[i]].call_count, + jit_table[top[i]].sh2_start); + } + printf("========================================================\n"); + + + printf("============== Top callees by call count: ==============\n"); + memset(top, -1, sizeof(top)); + for (i = 0; i < lenof(jit_table); i++) { + if (!jit_table[i].sh2_start) { + continue; + } + unsigned int j; + for (j = 0; j < lenof(top); j++) { + if (top[j] < 0 + || jit_table[i].call_count > jit_table[top[j]].call_count + ) { + unsigned int k; + for (k = lenof(top)-1; k > j; k--) { + top[k] = top[k-1]; + } + top[j] = i; + break; + } + } + } + printf( +# ifdef JIT_DEBUG_INTERPRET_RTL + "RTL insns" +# else + "Exec time" +# endif + " SH2 cyc. Native/SH2 # calls Block addr\n"); + printf("--------- -------- ---------- ------- ----------\n"); + for (i = 0; i < lenof(top) && top[i] >= 0; i++) { + printf("%9lld %10d %12.3f %9d 0x%08X\n", + (unsigned long long)jit_table[top[i]].exec_time, + jit_table[top[i]].cycle_count, + (double)jit_table[top[i]].exec_time + / (double)jit_table[top[i]].cycle_count, + jit_table[top[i]].call_count, + jit_table[top[i]].sh2_start); + } + printf("========================================================\n"); + + for (i = 0; i < lenof(jit_table); i++) { + jit_table[i].call_count = 0; + jit_table[i].cycle_count = 0; + jit_table[i].exec_time = 0; + } + +} + +#endif // JIT_PROFILE + +/*************************************************************************/ +/***************** Dynamic translation utility routines ******************/ +/*************************************************************************/ + +/** + * clear_entry: Clear a specific entry from the JIT table, freeing the + * native code buffer and unlinking the entry from its references. + * + * [Parameters] + * entry: JitEntry structure pointer + * [Return value] + * None + */ +static NOINLINE void clear_entry(JitEntry *entry) +{ + PRECOND(entry != NULL, return); + + /* Make sure the non-active processor isn't trying to use this entry + * (if so, clear it out) */ + SH2State *state; + for (state = state_list; state; state = state->next) { + if (state->current_entry == entry) { + state->current_entry = NULL; + } + } + + /* Free the native code, if any */ + jit_total_data -= entry->native_length; + free(entry->native_code); + entry->native_code = NULL; + + /* Free the RTL block, if any */ + rtl_destroy_block(entry->rtl); + entry->rtl = NULL; + + /* Clear the entry from the predicted branch target of any entries + * referencing it */ + BranchTargetInfo *referrer, *next; + for (referrer = entry->pred_ref_head.next; + referrer != &entry->pred_ref_head; + referrer = next + ) { + next = referrer->next; + referrer->target = 0; + referrer->entry = NULL; + referrer->next = NULL; + referrer->prev = NULL; + } + entry->pred_ref_head.next = &entry->pred_ref_head; + entry->pred_ref_head.prev = &entry->pred_ref_head; + + /* Remove this entry from the reference list of any branch it predicts */ +#if JIT_BRANCH_PREDICTION_SLOTS > 0 + unsigned int i; + for (i = 0; i < JIT_BRANCH_PREDICTION_SLOTS; i++) { + if (entry->predicted[i].entry) { + entry->predicted[i].next->prev = entry->predicted[i].prev; + entry->predicted[i].prev->next = entry->predicted[i].next; + } + } +#endif +#ifdef JIT_BRANCH_PREDICT_STATIC + for (i = 0; i < entry->num_static_branches; i++) { + if (entry->static_predict[i].entry) { + entry->static_predict[i].next->prev = entry->static_predict[i].prev; + entry->static_predict[i].prev->next = entry->static_predict[i].next; + } + } + free(entry->static_predict); + entry->static_predict = NULL; + jit_total_data -= + entry->num_static_branches * sizeof(*entry->static_predict); +#endif + + /* Clear the entry from the hash chain and LRA list */ + if (entry->next) { + entry->next->prev = entry->prev; + } + if (entry->prev) { + entry->prev->next = entry->next; + } else { + jit_hashchain[JIT_HASH(entry->sh2_start)] = entry->next; + } + entry->lra_next->lra_prev = entry->lra_prev; + entry->lra_prev->lra_next = entry->lra_next; + + /* Mark the entry as free */ + entry->sh2_start = 0; + + /* Insert the entry into the free list */ + entry->next = jit_freelist.next; + entry->prev = &jit_freelist; + entry->next->prev = entry; + jit_freelist.next = entry; +} + +/*-----------------------------------------------------------------------*/ + +/** + * clear_oldest_entry: Clear the oldest entry from the JIT table. + * + * [Parameters] + * None + * [Return value] + * None + */ +static void clear_oldest_entry(void) +{ + JitEntry *oldest = jit_lralist.lra_next; + if (LIKELY(oldest != &jit_lralist)) { +#ifdef JIT_DEBUG + DMSG("Clearing oldest entry 0x%08X (age %u)", + oldest->sh2_start, jit_timestamp - oldest->timestamp); +#endif + clear_entry(oldest); + } else { +#ifdef JIT_DEBUG + DMSG("Tried to clear oldest entry from an empty table!"); +#endif + /* We don't call this function unless there's something we need to + * clear out, so if we come here, our internal tables may be + * corrupt. Reset everything to be safe. */ + jit_total_data = 0; + } +} + +/*************************************************************************/ + +/** + * flush_native_cache: Flush a range of addresses from the native CPU's + * caches. + * + * [Parameters] + * start: Pointer to start of range + * length: Length of range in bytes + * [Return value] + * None + */ +static inline void flush_native_cache(void *start, uint32_t length) +{ + if (cache_flush_callback) { + (*cache_flush_callback)(start, length); + } +} + +/*************************************************************************/ + +/** + * timestamp_compare: Compare two timestamps. + * + * [Parameters] + * a, b: Timestamps to compare + * reference: Reference timestamp by which the comparison is made + * [Return value] + * -1 if a < b (i.e. "a is older than b") + * 0 if a == b + * 1 if a > b + */ +__attribute__((const)) static inline int timestamp_compare( + uint32_t reference, uint32_t a, uint32_t b) +{ + const uint32_t age_a = reference - a; + const uint32_t age_b = reference - b; + return age_a > age_b ? -1 : + age_a < age_b ? 1 : 0; +} + +/*************************************************************************/ +/****************** Macros for SH-2 -> RTL translation *******************/ +/*************************************************************************/ + +/* Constants used in SH-2 memory access operations */ +#define SH2_ACCESS_TYPE_B 0 // 1-byte access (signed for loads) +#define SH2_ACCESS_TYPE_W 1 // 2-byte access (signed for loads) +#define SH2_ACCESS_TYPE_L 2 // 4-byte access + +/* Return the "high" (pointer register) and "low" (load/store offset) parts + * of an address for generating optimal native load/store code */ +#ifdef __mips__ +# define ADDR_HI(address) (((uintptr_t)(address) + 0x8000) & 0xFFFF0000) +# define ADDR_LO(address) ((int16_t)((uintptr_t)(address) & 0xFFFF)) +#else +# define ADDR_HI(address) ((uintptr_t)address) +# define ADDR_LO(address) 0 +#endif + +/*-----------------------------------------------------------------------*/ + +/* Basic macro to pass a failure (zero) return value up the call chain */ +#define ASSERT(expr) do { \ + if (UNLIKELY(!(expr))) { \ + return 0; \ + } \ +} while (0) + +/* Basic macro to append an RTL instruction, aborting on error */ +#define APPEND(opcode,dest,src1,src2,other) \ + ASSERT(rtl_add_insn(entry->rtl, RTLOP_##opcode, \ + (dest), (src1), (src2), (other))); + +/*-----------------------------------------------------------------------*/ + +/* Declare a register identifier, but don't allocate a new register for it */ +#define DECLARE_REG(name) uint32_t name + +/* Allocate a register for a declared identifier */ +#define ALLOC_REG(name) ASSERT(name = rtl_alloc_register(entry->rtl)) + +/* Define a new register (equivalent to DECLARE_REG followed by ALLOC_REG) */ +#define DEFINE_REG(name) \ + const uint32_t name = rtl_alloc_register(entry->rtl); \ + if (UNLIKELY(!name)) { \ + return 0; \ + } + +/*-----------------------------------------------------------------------*/ + +/* Register-register operations */ + +#define MOVE(dest,src) APPEND(MOVE, (dest), (src), 0, 0) +#define SELECT(dest,src1,src2,cond) \ + APPEND(SELECT, (dest), (src1), (src2), (cond)) +#define ADD(dest,src1,src2) APPEND(ADD, (dest), (src1), (src2), 0) +#define SUB(dest,src1,src2) APPEND(SUB, (dest), (src1), (src2), 0) +#define MUL(dest,src1,src2) APPEND(MULU, (dest), (src1), (src2), 0) +#define MULU_64(dest,src1,src2,dest_hi) \ + APPEND(MULU, (dest), (src1), (src2), (dest_hi)) +#define MULS_64(dest,src1,src2,dest_hi) \ + APPEND(MULS, (dest), (src1), (src2), (dest_hi)) +#define MADDU_64(dest,src1,src2,dest_hi) \ + APPEND(MADDU, (dest), (src1), (src2), (dest_hi)) +#define MADDS_64(dest,src1,src2,dest_hi) \ + APPEND(MADDS, (dest), (src1), (src2), (dest_hi)) +#define DIVMODU(dest,src1,src2,rem) \ + APPEND(DIVMODU, (dest), (src1), (src2), (rem)) +#define DIVMODS(dest,src1,src2,rem) \ + APPEND(DIVMODS, (dest), (src1), (src2), (rem)) +#define AND(dest,src1,src2) APPEND(AND, (dest), (src1), (src2), 0) +#define OR(dest,src1,src2) APPEND(OR, (dest), (src1), (src2), 0) +#define XOR(dest,src1,src2) APPEND(XOR, (dest), (src1), (src2), 0) +#define NOT(dest,src) APPEND(NOT, (dest), (src), 0, 0) +#define SLL(dest,src1,src2) APPEND(SLL, (dest), (src1), (src2), 0) +#define SRL(dest,src1,src2) APPEND(SRL, (dest), (src1), (src2), 0) +#define SRA(dest,src1,src2) APPEND(SRA, (dest), (src1), (src2), 0) +#define ROR(dest,src1,src2) APPEND(SRL, (dest), (src1), (src2), 0) +#define CLZ(dest,src) APPEND(CLZ, (dest), (src), 0, 0) +#define CLO(dest,src) APPEND(CLO, (dest), (src), 0, 0) +#define SLTU(dest,src1,src2) APPEND(SLTU, (dest), (src1), (src2), 0) +#define SLTS(dest,src1,src2) APPEND(SLTS, (dest), (src1), (src2), 0) +#define BSWAPH(dest,src) APPEND(BSWAPH, (dest), (src), 0, 0) +#define BSWAPW(dest,src) APPEND(BSWAPW, (dest), (src), 0, 0) +#define HSWAPW(dest,src) APPEND(HSWAPW, (dest), (src), 0, 0) + +/*----------------------------------*/ + +/* Register-immediate operations */ + +#define MOVEI(dest,imm) APPEND(LOAD_IMM, (dest), (imm), 0, 0) +#define MOVEA(dest,addr) APPEND(LOAD_ADDR, (dest), \ + (uintptr_t)(addr), 0, 0) +#ifdef JIT_USE_RTL_REGIMM +# define IMMOP(op,dest,src,imm) APPEND(op##I, (dest), (src), (imm), 0) +#else +# define IMMOP(op,dest,src,imm) do { DEFINE_REG(__immreg); \ + APPEND(LOAD_IMM, __immreg, (imm), 0, 0); \ + APPEND(op, (dest), (src), __immreg, 0); \ + } while (0) +#endif +#define ADDI(dest,src,imm) IMMOP(ADD, (dest), (src), (imm)) +#define SUBI(dest,src,imm) IMMOP(ADD, (dest), (src), -(imm)) +#define ANDI(dest,src,imm) IMMOP(AND, (dest), (src), (imm)) +#define ORI(dest,src,imm) IMMOP(OR, (dest), (src), (imm)) +#define XORI(dest,src,imm) IMMOP(XOR, (dest), (src), (imm)) +#define SLLI(dest,src,imm) IMMOP(SLL, (dest), (src), (imm)) +#define SRLI(dest,src,imm) IMMOP(SRL, (dest), (src), (imm)) +#define SRAI(dest,src,imm) IMMOP(SRA, (dest), (src), (imm)) +#define RORI(dest,src,imm) IMMOP(ROR, (dest), (src), (imm)) +#define SLTUI(dest,src,imm) IMMOP(SLTU, (dest), (src), (imm)) +#define SLTSI(dest,src,imm) IMMOP(SLTS, (dest), (src), (imm)) + +/*----------------------------------*/ + +/* Bitfield instructions */ + +#ifdef JIT_USE_RTL_BITFIELDS +# define BFOP(op,dest,src1,src2,start,count) \ + APPEND(BF##op, (dest), (src1), (src2), (start) | (count)<<8) +# define BFEXT(dest,src,start,count) \ + BFOP(EXT, (dest), (src), 0, (start), (count)) +# define BFINS(dest,src1,src2,start,count) \ + BFOP(INS, (dest), (src1), (src2), (start), (count)) +#else +# define BFEXT(dest,src,start,count) do { \ + const uint32_t __dest = (dest); \ + const uint32_t __src = (src); \ + const int __start = (start); \ + const int __count = (count); \ + if (__start == 0) { \ + if (__count == 32) { \ + MOVE(__dest, __src); \ + } else { \ + ANDI(__dest, __src, (1U<<__count) - 1); \ + } \ + } else if (__start + __count == 32) { \ + SRLI(__dest, __src, __start); \ + } else { \ + DEFINE_REG(__temp); \ + SRLI(__temp, __src, (start)); \ + ANDI(__dest, __temp, (1U<<__count) - 1); \ + } \ +} while (0) +# define BFINS(dest,src1,src2,start,count) do { \ + const uint32_t __dest = (dest); \ + const uint32_t __src1 = (src1); \ + const uint32_t __src2 = (src2); \ + const int __start = (start); \ + const int __count = (count); \ + if (__start == 0 && __count == 32) { \ + MOVE(__dest, __src2); \ + } else { \ + const uint32_t __mask = (1U<<__count) - 1; \ + DEFINE_REG(__out2); \ + if (__start == 0) { \ + ANDI(__out2, __src2, __mask); \ + } else { \ + DEFINE_REG(__temp); \ + ANDI(__temp, __src2, __mask); \ + SLLI(__out2, __temp, __start); \ + } \ + DEFINE_REG(__out1); \ + ANDI(__out1, __src1, ~(__mask << __start)); \ + OR(__dest, __out1, __out2); \ + } \ +} while (0) +#endif + +/*----------------------------------*/ + +/* Variants of SLT */ + +#define SEQZ(dest,src) SLTUI((dest), (src), 1) +#define SLTZ(dest,src) SLTSI((dest), (src), 0) + +/*----------------------------------*/ + +/* Load from or store to memory */ + +#define LOAD_BU(dest,address,offset) \ + APPEND(LOAD_BU, (dest), (address), 0, (offset)) +#define LOAD_BS(dest,address,offset) \ + APPEND(LOAD_BS, (dest), (address), 0, (offset)) +#define LOAD_HU(dest,address,offset) \ + APPEND(LOAD_HU, (dest), (address), 0, (offset)) +#define LOAD_HS(dest,address,offset) \ + APPEND(LOAD_HS, (dest), (address), 0, (offset)) +#define LOAD_W(dest,address,offset) \ + APPEND(LOAD_W, (dest), (address), 0, (offset)) +#define LOAD_PTR(dest,address,offset) \ + APPEND(LOAD_PTR, (dest), (address), 0, (offset)) +#define STORE_B(address,src,offset) \ + APPEND(STORE_B, (address), (src), 0, (offset)) +#define STORE_H(address,src,offset) \ + APPEND(STORE_H, (address), (src), 0, (offset)) +#define STORE_W(address,src,offset) \ + APPEND(STORE_W, (address), (src), 0, (offset)) +#define STORE_PTR(address,src,offset) \ + APPEND(STORE_PTR, (address), (src), 0, (offset)) + +/*----------------------------------*/ + +/* Load from, store to, or add constants to state block fields */ + +/* Loads */ + +#define LOAD_STATE(reg,field) \ + ASSERT(__LOAD_STATE(entry, state_reg, (reg), offsetof(SH2State,field))) +static inline int __LOAD_STATE(const JitEntry * const entry, + const uint32_t state_reg, + const uint32_t reg, const uint32_t offset) +{ +#ifdef OPTIMIZE_STATE_BLOCK + const int index = state_cache_index(offset); + if (index >= 0) { + if (state_cache[index].rtlreg) { + if (reg == state_cache[index].rtlreg) { + if (state_cache[index].offset) { + ADDI(state_cache[index].rtlreg, state_cache[index].rtlreg, + state_cache[index].offset); + if (index == 15 && stack_pointer) { + ADDI(stack_pointer, stack_pointer, + state_cache[index].offset); + } + state_cache[index].offset = 0; + } + } else { + if (state_cache[index].offset) { + ADDI(reg, state_cache[index].rtlreg, + state_cache[index].offset); + } else { + MOVE(reg, state_cache[index].rtlreg); + } + } + } else { + LOAD_W(reg, state_reg, offset); + } + if (!state_cache[index].fixed + && !(index == 15 && (optimization_flags & SH2_OPTIMIZE_STACK) + && state_cache[index].rtlreg != 0) + ) { + state_cache[index].rtlreg = reg; + state_cache[index].offset = 0; + } + if (index < 16) { + /* We changed the stored value, so we have to generate a new + * direct pointer on the next access. */ + pointer_status[index].rtl_ptrreg = 0; + } + return 1; + } +#endif + LOAD_W(reg, state_reg, offset); + return 1; +} + +/* Load from a state block field, but don't change the state block cache */ +#define LOAD_STATE_COPY(reg,field) \ + ASSERT(__LOAD_STATE_COPY(entry, state_reg, (reg), offsetof(SH2State,field))) +static inline int __LOAD_STATE_COPY(const JitEntry * const entry, + const uint32_t state_reg, + const uint32_t reg, const uint32_t offset) +{ +#ifdef OPTIMIZE_STATE_BLOCK + const int index = state_cache_index(offset); + if (index >= 0 && state_cache[index].rtlreg) { + PRECOND(reg != state_cache[index].rtlreg, return 0); + if (state_cache[index].offset) { + ADDI(reg, state_cache[index].rtlreg, state_cache[index].offset); + } else { + MOVE(reg, state_cache[index].rtlreg); + } + return 1; + } +#endif + LOAD_W(reg, state_reg, offset); + return 1; +} + +/* Allocate a new register and load it from the state block, or reuse an + * old register if appropriate and if the register is not offsetted. Note + * that a register obtained from this macro MUST NOT be reassigned, since + * it may be shared; if reassignment is necessary, use DEFINE_REG() and + * LOAD_STATE() instead. (The MAC.* instructions are exceptions to this + * rule, since all other users of MACH/MACL use LOAD_STATE_COPY() to avoid + * aliasing the register.) */ +#define LOAD_STATE_ALLOC(reg,field) \ + ASSERT(reg = __LOAD_STATE_ALLOC(entry, state_reg, \ + offsetof(SH2State,field))) +static inline uint32_t __LOAD_STATE_ALLOC(const JitEntry * const entry, + const uint32_t state_reg, + const uint32_t offset) +{ +#ifdef OPTIMIZE_STATE_BLOCK + const int index = state_cache_index(offset); + if (index >= 0 && state_cache[index].rtlreg != 0) { + if (state_cache[index].offset == 0) { + return state_cache[index].rtlreg; + } else if (state_cache[index].fixed) { + ADDI(state_cache[index].rtlreg, state_cache[index].rtlreg, + state_cache[index].offset); + if (index == 15 && stack_pointer) { + ADDI(stack_pointer, stack_pointer, state_cache[index].offset); + } + state_cache[index].offset = 0; + return state_cache[index].rtlreg; + } + } +#endif + DEFINE_REG(reg); + ASSERT(__LOAD_STATE(entry, state_reg, reg, offset)); + return reg; +} + +/* Allocate a new register and load it from the state block, or reuse an + * old register (leaving any offset in the cache) if appropriate. As with + * LOAD_STATE_ALLOC(), a register obtained from this macro MUST NOT be + * reassigned. */ +#define LOAD_STATE_ALLOC_KEEPOFS(reg,field) \ + ASSERT(reg = __LOAD_STATE_ALLOC_KEEPOFS(entry, state_reg, \ + offsetof(SH2State,field))) +static inline uint32_t __LOAD_STATE_ALLOC_KEEPOFS(const JitEntry * const entry, + const uint32_t state_reg, + const uint32_t offset) +{ +#ifdef OPTIMIZE_STATE_BLOCK + const int index = state_cache_index(offset); + if (index >= 0 && state_cache[index].rtlreg != 0) { + return state_cache[index].rtlreg; + } +#endif + DEFINE_REG(reg); + ASSERT(__LOAD_STATE(entry, state_reg, reg, offset)); + return reg; +} + +#define LOAD_STATE_PTR(reg,field) \ + LOAD_PTR((reg), state_reg, offsetof(SH2State,field)) + +#define LOAD_STATE_SR_T(reg) \ + ASSERT(reg = __LOAD_STATE_SR_T(entry, state_reg)) +static inline uint32_t __LOAD_STATE_SR_T(const JitEntry * const entry, + const uint32_t state_reg) +{ +#ifdef OPTIMIZE_STATE_BLOCK + if (cached_SR_T) { + return cached_SR_T; + } +#endif + DECLARE_REG(temp); + LOAD_STATE_ALLOC(temp, SR); + DEFINE_REG(reg); + ANDI(reg, temp, SR_T); +#ifdef OPTIMIZE_STATE_BLOCK + cached_SR_T = reg; +#endif + return reg; +} + +/* Stores */ + +#define STORE_STATE(field,reg) \ + ASSERT(__STORE_STATE(entry, state_reg, offsetof(SH2State,field), (reg))) +static inline int __STORE_STATE(const JitEntry * const entry, + const uint32_t state_reg, + const uint16_t offset, const uint32_t reg) +{ + if (offset == offsetof(SH2State,R[15]) + && (optimization_flags & SH2_OPTIMIZE_STACK) + ) { +#ifdef JIT_DEBUG_VERBOSE + if (pointer_status[15].known) { + DMSG("WARNING: Reassigning stack pointer in OPTIMIZE_STACK mode"); + } +#endif + DEFINE_REG(page); + SRLI(page, reg, 19); + DEFINE_REG(pageofs); + SLLI(pageofs, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, pageofs); + if (!pointer_status[15].known) { + DEFINE_REG(base); + pointer_status[15].known = 1; + pointer_status[15].type = 2; + pointer_status[15].data_only = 1; + pointer_status[15].rtl_basereg = base; + pointer_status[15].rtl_ptrreg = 0; // stack_pointer is used instead + DEFINE_REG(new_sp); + stack_pointer = new_sp; + } + PRECOND(pointer_status[15].rtl_basereg != 0, return 0); + LOAD_PTR(pointer_status[15].rtl_basereg, dp_ptr, + ADDR_LO(direct_pages)); + PRECOND(stack_pointer != 0, return 0); + ADD(stack_pointer, pointer_status[15].rtl_basereg, reg); + } +#ifdef OPTIMIZE_STATE_BLOCK + const int index = state_cache_index(offset); + if (index >= 0) { + if (state_cache[index].fixed) { + if (reg != state_cache[index].rtlreg) { + MOVE(state_cache[index].rtlreg, reg); + } + } else { + state_cache[index].rtlreg = reg; + } + if (index < 16) { + pointer_status[index].rtl_ptrreg = 0; + } + state_cache[index].offset = 0; + if (index == state_cache_index(offsetof(SH2State,PC))) { + cached_PC = 0; + stored_PC = 0; + } + state_dirty |= 1 << index; +# ifdef TRACE_STEALTH + if (index < 23) { + APPEND(NOP, 0, 0xB0000000 | index<<16 | state_cache[index].rtlreg, + 0, 0); + } +# endif + return 1; + } +#endif + STORE_W(state_reg, reg, offset); + return 1; +} + +#define STORE_STATE_PC(value) \ + ASSERT(__STORE_STATE_PC(entry, state_reg, (value))) +static inline int __STORE_STATE_PC(const JitEntry * const entry, + const uint32_t state_reg, + const uint32_t value) +{ +#ifdef OPTIMIZE_STATE_BLOCK + const int index_PC = state_cache_index(offsetof(SH2State,PC)); + if (!state_cache[index_PC].fixed && value != 0) { + if (stored_PC == value) { + /* state->PC is already correct, so just clear the cache */ + if (state_cache[index_PC].rtlreg) { +# ifdef TRACE_STEALTH + APPEND(NOP, 0, 0xB0000000 | index_PC<<16, 0, 0); +# endif + } + cached_PC = 0; + state_cache[index_PC].rtlreg = 0; + state_dirty &= ~(1<> 16), 0, 0); + APPEND(NOP, 0, 0xBC000000 | index_PC<<16 | (value & 0xFFFF), 0, 0); + APPEND(NOP, 0, 0xB0000000 | index_PC<<16, 0, 0); +# endif + cached_PC = value; + state_dirty |= 1<= 0 + && (state_cache[index].rtlreg || !state_cache[index].fixed) + ) { + if (!state_cache[index].rtlreg) { + DEFINE_REG(rtlreg); + LOAD_W(rtlreg, state_reg, offset); + state_cache[index].rtlreg = rtlreg; + state_cache[index].offset = 0; + } + const int32_t new_offset = (int32_t)state_cache[index].offset + imm; + if ((uint32_t)(new_offset + 0x8000) < 0x10000) { + state_cache[index].offset = new_offset; + } else { + if (state_cache[index].fixed) { + ADDI(state_cache[index].rtlreg, state_cache[index].rtlreg, + new_offset); + } else { + DEFINE_REG(newreg); + ADDI(newreg, state_cache[index].rtlreg, new_offset); + state_cache[index].rtlreg = newreg; + if (index < 16) { + pointer_status[index].rtl_ptrreg = 0; + } + } + state_cache[index].offset = 0; + } + state_dirty |= 1 << index; +# ifdef TRACE_STEALTH + if (index < 23) { + uint32_t temp = state_cache[index].offset; + APPEND(NOP, 0, 0xB8000000 | index<<16 | (temp>>16 & 0xFFFF), 0, 0); + APPEND(NOP, 0, 0xBC000000 | index<<16 | (temp>> 0 & 0xFFFF), 0, 0); + APPEND(NOP, 0, 0xB0000000 | index<<16 | state_cache[index].rtlreg, + 0, 0); + } +# endif + return 1; + } +#endif + if (!cur_reg) { + ASSERT(cur_reg = __LOAD_STATE_ALLOC(entry, state_reg, offset)); + } + DEFINE_REG(result); + ADDI(result, cur_reg, imm); + STORE_W(state_reg, result, offset); + if (offset == offsetof(SH2State,R[15]) + && (optimization_flags & SH2_OPTIMIZE_STACK) + && stack_pointer + ) { + ADDI(stack_pointer, stack_pointer, imm); + } + return 1; +} + +/*----------------------------------*/ + +/* Execute an SH-2 load or store operation (note that size desginations are + * SH-2 style B[yte]/W[ord]/L[ong] rather than RTL B[yte]/H[alfword]/W[ord], + * and all 8- and 16-bit loads are signed) */ + +#define SH2_LOAD_B(dest,address) \ + do_load(entry, (dest), (address), SH2_ACCESS_TYPE_B) +#define SH2_LOAD_W(dest,address) \ + do_load(entry, (dest), (address), SH2_ACCESS_TYPE_W) +#define SH2_LOAD_L(dest,address) \ + do_load(entry, (dest), (address), SH2_ACCESS_TYPE_L) +#define SH2_STORE_B(address,src) \ + do_store(entry, (address), (src), SH2_ACCESS_TYPE_B, state_reg) +#define SH2_STORE_W(address,src) \ + do_store(entry, (address), (src), SH2_ACCESS_TYPE_W, state_reg) +#define SH2_STORE_L(address,src) \ + do_store(entry, (address), (src), SH2_ACCESS_TYPE_L, state_reg) + +/*----------------------------------*/ + +/* Execute an SH-2 load or store to a known address */ + +#define SH2_LOAD_ABS_B(dest,address) \ + do_load_abs(entry, (dest), (address), SH2_ACCESS_TYPE_B) +#define SH2_LOAD_ABS_W(dest,address) \ + do_load_abs(entry, (dest), (address), SH2_ACCESS_TYPE_W) +#define SH2_LOAD_ABS_L(dest,address) \ + do_load_abs(entry, (dest), (address), SH2_ACCESS_TYPE_L) +#define SH2_STORE_ABS_B(address,src,islocal) \ + do_store_abs(entry, (address), (src), SH2_ACCESS_TYPE_B, state_reg, \ + (islocal)) +#define SH2_STORE_ABS_W(address,src,islocal) \ + do_store_abs(entry, (address), (src), SH2_ACCESS_TYPE_W, state_reg, \ + (islocal)) +#define SH2_STORE_ABS_L(address,src,islocal) \ + do_store_abs(entry, (address), (src), SH2_ACCESS_TYPE_L, state_reg, \ + (islocal)) + +/*----------------------------------*/ + +/* Execute an SH-2 load or store through an SH-2 register */ + +#define SH2_LOAD_REG_B(dest,sh2reg,offset,postinc) \ + do_load_reg(entry, (dest), (sh2reg), (offset), SH2_ACCESS_TYPE_B, \ + (postinc), state, state_reg) +#define SH2_LOAD_REG_W(dest,sh2reg,offset,postinc) \ + do_load_reg(entry, (dest), (sh2reg), (offset), SH2_ACCESS_TYPE_W, \ + (postinc), state, state_reg) +#define SH2_LOAD_REG_L(dest,sh2reg,offset,postinc) \ + do_load_reg(entry, (dest), (sh2reg), (offset), SH2_ACCESS_TYPE_L, \ + (postinc), state, state_reg) +#define SH2_STORE_REG_B(sh2reg,src,offset,predec) \ + do_store_reg(entry, (sh2reg), (src), (offset), SH2_ACCESS_TYPE_B, \ + (predec), state, state_reg) +#define SH2_STORE_REG_W(sh2reg,src,offset,predec) \ + do_store_reg(entry, (sh2reg), (src), (offset), SH2_ACCESS_TYPE_W, \ + (predec), state, state_reg) +#define SH2_STORE_REG_L(sh2reg,src,offset,predec) \ + do_store_reg(entry, (sh2reg), (src), (offset), SH2_ACCESS_TYPE_L, \ + (predec), state, state_reg) + +/*----------------------------------*/ + +/* Branches (within an SH-2 instruction's RTL code) */ + +#define CREATE_LABEL(label) \ + const uint32_t label = rtl_alloc_label(entry->rtl); \ + if (UNLIKELY(!label)) { \ + return 0; \ + } +#define DEFINE_LABEL(label) APPEND(LABEL, 0, 0, 0, (label)) +#define GOTO_LABEL(label) APPEND(GOTO, 0, 0, 0, (label)) +#define GOTO_IF_Z(label,reg) APPEND(GOTO_IF_Z, 0, (reg), 0, (label)) +#define GOTO_IF_NZ(label,reg) APPEND(GOTO_IF_NZ, 0, (reg), 0, (label)) +#define GOTO_IF_E(label,reg1,reg2) \ + APPEND(GOTO_IF_E, 0, (reg1), (reg2), (label)) +#define GOTO_IF_NE(label,reg1,reg2) \ + APPEND(GOTO_IF_NE, 0, (reg1), (reg2), (label)) + +/*----------------------------------*/ + +/* Jumps (to other SH-2 instructions) */ + +#define JUMP_STATIC() \ + ASSERT(branch_static(state, entry, state_reg)) +#define JUMP() RETURN() + +/* Call to a native subroutine */ +#define CALL(result,arg1,arg2,func) \ + APPEND(CALL, (result), (arg1), (arg2), (func)) +#define CALL_NORET(arg1,arg2,func) \ + APPEND(CALL, 0, (arg1), (arg2), (func)) + +/* Return from the current block */ +#define RETURN() APPEND(RETURN, 0, 0, 0, 0) + +/* Chain to a different native routine */ +#define RETURN_TO(addr) APPEND(RETURN_TO, 0, 0, 0, addr) + +/*-----------------------------------------------------------------------*/ + +/* Use global variables for the PC and cycle count; the initial PC can be + * taken from the JitEntry structure */ +#define initial_PC (entry->sh2_start) +#define cur_PC jit_PC + +/* Pre- and post-decode processing is implemented by separate functions + * defined below */ +#define OPCODE_INIT(opcode) \ + ASSERT(opcode_init(state, entry, state_reg, (opcode), recursing)) +#define OPCODE_DONE(opcode) \ + ASSERT(opcode_done(state, entry, state_reg, (opcode), recursing)) + + +/* Need to update both jit_PC and state->PC */ +#define INC_PC() do { \ + jit_PC += 2; \ + STORE_STATE_PC(jit_PC); \ +} while (0) +#ifdef JIT_DEBUG_TRACE +/* Make sure we trace instructions even if eliminated by optimization */ +# define INC_PC_BY(amount) do { \ + const unsigned int __amount = (amount); \ + unsigned int __i; \ + for (__i = 0; __i < __amount; __i += 2) { \ + jit_PC += 2; \ + char tracebuf[100]; \ + const unsigned int __opcode = MappedMemoryReadWord(jit_PC); \ + SH2Disasm(jit_PC, __opcode, 0, tracebuf); \ + fprintf(stderr, "%08X: %04X %s\n", jit_PC, __opcode, tracebuf+12); \ + } \ + STORE_STATE_PC(jit_PC); \ +} while (0) +#else // !JIT_DEBUG_TRACE +# define INC_PC_BY(amount) do { \ + jit_PC += (amount); \ + STORE_STATE_PC(jit_PC); \ +} while (0) +#endif + +/* Return whether the word at "offset" words from the current instruction + * is available for peephole optimization */ +#define INSN_IS_AVAILABLE(offset) \ + (recursing ? !is_last : \ + jit_PC + (offset)*2 <= entry->sh2_end \ + && (word_info[(jit_PC - entry->sh2_start)/2 + (offset)] & WORD_INFO_CODE)\ + && !(is_branch_target[((jit_PC - entry->sh2_start) / 2 + (offset)) / 32] \ + & (1 << (((jit_PC - entry->sh2_start) / 2 + (offset)) % 32)))) + +/*----------------------------------*/ + +/* Return whether the saturation check for MAC can be omitted */ +#define CAN_OMIT_MAC_S_CHECK (can_optimize_mac_nosat) + +/* Get or set whether the MACL/MACH pair is known to be zero */ +#define MAC_IS_ZERO() (state->mac_is_zero) +#define SET_MAC_IS_ZERO() (state->mac_is_zero = 1) +#define CLEAR_MAC_IS_ZERO() (state->mac_is_zero = 0) + +/*----------------------------------*/ + +/* Get, add to, or clear the cached shift count */ +#define CAN_CACHE_SHIFTS() 1 +#define CACHED_SHIFT_COUNT() (state->cached_shift_count) +#define ADD_TO_SHIFT_CACHE(n) (state->cached_shift_count += (n)) +#define CLEAR_SHIFT_CACHE() (state->cached_shift_count = 0) + +/*----------------------------------*/ + +/* Get or set register known bits and values */ +#ifdef OPTIMIZE_KNOWN_VALUES +# define REG_GETKNOWN(reg) reg_knownbits[(reg)] +# define REG_GETVALUE(reg) reg_value[(reg)] +# define REG_SETKNOWN(reg,known) (reg_knownbits[(reg)] = (known)) +# define REG_SETVALUE(reg,value) (reg_value[(reg)] = (value)) +# define REG_RESETKNOWN() do { \ + unsigned int __i; \ + for (__i = 0; __i < lenof(reg_knownbits); __i++) { \ + reg_knownbits[__i] = 0; \ + } \ +} while (0) +#else +# define REG_GETKNOWN(reg) 0 +# define REG_GETVALUE(reg) 0 +# define REG_SETKNOWN(reg,value) /*nothing*/ +# define REG_SETVALUE(reg,value) /*nothing*/ +# define REG_RESETKNOWN() /*nothing*/ +#endif + +/*----------------------------------*/ + +/* Track pointer registers */ + +/* Check or set the local-pointer flag for a GPR (used to skip JIT overwrite + * checks in do_store_abs()); flag is cleared by PTR_CLEAR() */ +#define PTR_ISLOCAL(reg) (pointer_local & 1<<(reg)) +#define PTR_SETLOCAL(reg) do { \ + if (optimization_flags & SH2_OPTIMIZE_LOCAL_POINTERS) { \ + pointer_local |= 1<<(reg); \ + } \ +} while (0) + +/* Mark a GPR as having taken its value from the given local address */ +#define PTR_SET_SOURCE(reg,address) do { \ + if (optimization_flags & SH2_OPTIMIZE_LOCAL_POINTERS) { \ + pointer_status[(reg)].source = (address); \ + } \ +} while (0) + +/* Return whether the given register is a known pointer */ +#define PTR_CHECK(reg) (pointer_status[(reg)].known != 0 \ + || pointer_status[(reg)].rtl_basereg != 0) + +/* Copy pointer status for a MOVE Rm,Rn or ADD Rm,Rn instruction */ +#define PTR_COPY(reg,new,for_add) do { \ + const unsigned int __reg = (reg); \ + const unsigned int __new = (new); \ + pointer_status[__new] = pointer_status[__reg]; \ + if (pointer_status[__reg].known) { \ + pointer_status[__new].known = -1; \ + } \ + if (for_add) { \ + pointer_status[__new].rtl_ptrreg = 0; \ + } \ + if (PTR_ISLOCAL(__reg)) { \ + pointer_local |= 1<<__new; \ + } else { \ + pointer_local &= ~(1<<__new); \ + } \ +} while (0) + +/* Clear pointer status on a register modification */ +#define PTR_CLEAR(reg) do { \ + const unsigned int __reg = (reg); \ + if (__reg != 15 || !(optimization_flags & SH2_OPTIMIZE_STACK)) { \ + pointer_status[__reg].known = 0; \ + pointer_status[__reg].rtl_basereg = 0; \ + pointer_status[__reg].rtl_ptrreg = 0; \ + pointer_status[__reg].source = 0; \ + pointer_local &= ~(1<<__reg); \ + } \ +} while (0) + +/*----------------------------------*/ + +/* Processor state block caching */ + +/* Save the current cache state */ +#define SAVE_STATE_CACHE() do { \ + memcpy(saved_state_cache, state_cache, sizeof(state_cache));\ + saved_state_dirty = state_dirty; \ + saved_cached_SR_T = cached_SR_T; \ + saved_dirty_SR_T = dirty_SR_T; \ + saved_cached_PC = cached_PC; \ + saved_stored_PC = stored_PC; \ +} while (0) + +/* Restore the saved cache state */ +#define RESTORE_STATE_CACHE() do { \ + RESTORE_STATE_CACHE_APPEND_STEALTH_NOPS(); \ + memcpy(state_cache, saved_state_cache, sizeof(state_cache));\ + state_dirty = saved_state_dirty; \ + cached_SR_T = saved_cached_SR_T; \ + dirty_SR_T = saved_dirty_SR_T; \ + cached_PC = saved_cached_PC; \ + stored_PC = saved_stored_PC; \ +} while (0) +#ifdef TRACE_STEALTH +# define RESTORE_STATE_CACHE_APPEND_STEALTH_NOPS() do { \ + unsigned int __i; \ + for (__i = 0; __i < 23; __i++) { \ + if (__i == 22 && saved_cached_PC != 0) { \ + APPEND(NOP, 0, 0xB8160000 | (saved_cached_PC>>16 & 0xFFFF), 0, 0);\ + APPEND(NOP, 0, 0xBC160000 | (saved_cached_PC>> 0 & 0xFFFF), 0, 0);\ + APPEND(NOP, 0, 0xB0160000, 0, 0); \ + } else if ((saved_state_dirty & (1<<__i)) \ + && (!(state_dirty & (1<<__i)) \ + || saved_state_cache[__i].rtlreg != state_cache[__i].rtlreg \ + || saved_state_cache[__i].offset != state_cache[__i].offset) \ + ) { \ + uint32_t __temp = saved_state_cache[__i].offset; \ + if (__temp) { \ + APPEND(NOP, 0, 0xB8000000 | __i<<16 | (__temp>>16 & 0xFFFF), 0, 0); \ + APPEND(NOP, 0, 0xBC000000 | __i<<16 | (__temp>> 0 & 0xFFFF), 0, 0); \ + } \ + APPEND(NOP, 0, 0xB0000000 | __i<<16 | saved_state_cache[__i].rtlreg, 0, 0); \ + } else if (!(saved_state_dirty & (1<<__i)) && (state_dirty & (1<<__i))) { \ + APPEND(NOP, 0, 0xB0000000 | __i<<16, 0, 0); \ + } \ + } \ + if (saved_dirty_SR_T && (!dirty_SR_T || saved_cached_SR_T != cached_SR_T)) { \ + APPEND(NOP, 0, 0xB0900000 | saved_cached_SR_T, 0, 0); \ + } else if (!saved_dirty_SR_T && dirty_SR_T) { \ + APPEND(NOP, 0, 0xB0900000, 0, 0); \ + } \ +} while (0) +#else +# define RESTORE_STATE_CACHE_APPEND_STEALTH_NOPS() /*nothing*/ +#endif + +#ifndef OPTIMIZE_STATE_BLOCK +# undef SAVE_STATE_CACHE +# undef RESTORE_STATE_CACHE +# define SAVE_STATE_CACHE() /*nothing*/ +# define RESTORE_STATE_CACHE() /*nothing*/ +#endif + +/* Write back to the state block any cached, dirty state block values + * (but leave them dirty) */ +#define WRITEBACK_STATE_CACHE() \ + ASSERT(writeback_state_cache(entry, state_reg, 0)); + +/* Flush any cached state block values */ +#define FLUSH_STATE_CACHE() do { \ + ASSERT(writeback_state_cache(entry, state_reg, 1)); \ + clear_state_cache(0); \ +} while (0) + +/* Return the cached offset for the given state block field, or 0 if none */ +#ifdef OPTIMIZE_CONSTANT_ADDS +# define STATE_CACHE_OFFSET(field) \ + (state_cache_index(offsetof(SH2State,field)) >= 0 \ + ? state_cache[state_cache_index(offsetof(SH2State,field))].offset \ + : 0) +#else +# define STATE_CACHE_OFFSET(field) 0 +#endif + +/* Return the fixed RTL register to use for the given state block field, + * or 0 if none */ +#ifdef OPTIMIZE_STATE_BLOCK +# define STATE_CACHE_FIXED_REG(field) \ + (state_cache_index(offsetof(SH2State,field)) >= 0 \ + && state_cache[state_cache_index(offsetof(SH2State,field))].fixed \ + ? state_cache[state_cache_index(offsetof(SH2State,field))].rtlreg \ + : 0) +#else +# define STATE_CACHE_FIXED_REG(field) 0 +#endif + +/* Return whether the given state block field has a fixed RTL register that + * can be modified */ +#ifdef OPTIMIZE_STATE_BLOCK +# define STATE_CACHE_FIXED_REG_WRITABLE(field) \ + (state_cache_index(offsetof(SH2State,field)) >= 0 \ + ? state_cache[state_cache_index(offsetof(SH2State,field))].fixed \ + && state_cache[state_cache_index(offsetof(SH2State,field))].flush \ + : 0) +#else +# define STATE_CACHE_FIXED_REG_WRITABLE(field) 0 +#endif + +/* Clear any fixed RTL register for the given state block field */ +#ifdef OPTIMIZE_STATE_BLOCK +# define STATE_CACHE_CLEAR_FIXED_REG(field) do { \ + if (state_cache_index(offsetof(SH2State,field)) >= 0) { \ + state_cache[state_cache_index(offsetof(SH2State,field))].fixed = 0; \ + state_cache[state_cache_index(offsetof(SH2State,field))].flush = 0; \ + } \ +} while (0) +#else +# define STATE_CACHE_CLEAR_FIXED_REG(field) /*nothing*/ +#endif + +/*----------------------------------*/ + +/* Check the status of a branch instruction */ + +#define BRANCH_FALLS_THROUGH(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_FALLTHRU \ + : 0) +#define BRANCH_TARGETS_RTS(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_BRA_RTS \ + : 0) + +#define BRANCH_IS_THREADED(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_THREADED \ + : 0) + +#define BRANCH_THREAD_TARGET(addr) \ + (branch_thread_target[((addr) - entry->sh2_start) / 2]) + +#define BRANCH_THREAD_COUNT(addr) \ + (branch_thread_count[((addr) - entry->sh2_start) / 2]) + +#define BRANCH_IS_SELECT(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_SELECT \ + : 0) + +#define BRANCH_IS_LOOP_TO_JSR(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_LOOP_JSR \ + : 0) + +#define BRANCH_IS_FOLDABLE_SUBROUTINE(addr) \ + ((addr) >= entry->sh2_start && (addr) <= entry->sh2_end \ + ? word_info[((addr) - entry->sh2_start) / 2] & WORD_INFO_FOLDABLE \ + : 0) + +#define BRANCH_FOLD_TARGET(addr) \ + (subroutine_target[((addr) - entry->sh2_start) / 2]) + +#define BRANCH_FOLD_TARGET_FETCH(addr) \ + ((const uint16_t *)((uintptr_t)fetch_pages[(addr) >> 19] + (addr))) + +#define BRANCH_FOLD_NATIVE_FUNC(addr) \ + (subroutine_native[((addr) - entry->sh2_start) / 2]) + +/*************************************************************************/ +/************* Helper functions for SH-2 -> RTL translation **************/ +/*************************************************************************/ + +/** + * do_load_common: Generate code for an SH-2 memory load operation common + * to both generic loads and register loads from variable addresses. + * + * [Parameters] + * entry: Block being translated + * dest: RTL register into which value is to be loaded + * address: RTL register holding load address + * offset: Offset to be added to address + * type: Access type (SH2_ACCESS_TYPE_*) + * page_ptr: RTL register holding base pointer for memory page or NULL + * [Return value] + * Nonzero on success, zero on error + */ +static int do_load_common(const JitEntry *entry, uint32_t dest, + uint32_t address, int32_t offset, + unsigned int type, uint32_t page_ptr) +{ + CREATE_LABEL(label_fallback); + CREATE_LABEL(label_done); + + DEFINE_REG(final_ptr); // Move this up to help MIPS delay slot optimization + if (offset < -0x8000 || offset > 0x7FFF) { + /* Memory offsets must be within the range [-0x8000,0x7FFF], so if + * we fall outside that range, add the offset to the address + * separately */ + DEFINE_REG(offset_addr); + ADDI(offset_addr, address, offset); + offset = 0; + ADD(final_ptr, page_ptr, offset_addr); + } else { + ADD(final_ptr, page_ptr, address); + } + + GOTO_IF_Z(label_fallback, page_ptr); { + /* We can access the data directly */ + // FIXME: all the code here assumes little-endian; this won't work + // if we port to a big-endian machine + switch (type) { + case SH2_ACCESS_TYPE_B: { + DEFINE_REG(real_ptr); + int32_t real_offset; + if (offset & 1) { + /* Can't use an immediate offset because we don't know + * whether the source address is odd, so we can't predict + * the effect of the XOR */ + DEFINE_REG(offset_ptr); + ADDI(offset_ptr, final_ptr, offset); + XORI(real_ptr, offset_ptr, 1); + real_offset = 0; + } else { + XORI(real_ptr, final_ptr, 1); + real_offset = offset; + } + LOAD_BS(dest, real_ptr, real_offset); + break; + } + case SH2_ACCESS_TYPE_W: { + LOAD_HS(dest, final_ptr, offset); + break; + } + case SH2_ACCESS_TYPE_L: { + DEFINE_REG(swapped); + LOAD_W(swapped, final_ptr, offset); + HSWAPW(dest, swapped); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + GOTO_LABEL(label_done); + + } DEFINE_LABEL(label_fallback); { + /* Not direct access, so call the fallback routine */ + DECLARE_REG(real_address); + if (offset) { + ALLOC_REG(real_address); + ADDI(real_address, address, offset); + } else { + real_address = address; + } + DEFINE_REG(fallback); + switch (type) { + case SH2_ACCESS_TYPE_B: { + MOVEA(fallback, MappedMemoryReadByte); + DEFINE_REG(retval); + CALL(retval, real_address, 0, fallback); + DEFINE_REG(tempdest); + SLLI(tempdest, retval, 24); + SRAI(dest, tempdest, 24); + break; + } + case SH2_ACCESS_TYPE_W: { + MOVEA(fallback, MappedMemoryReadWord); + DEFINE_REG(retval); + CALL(retval, real_address, 0, fallback); + DEFINE_REG(tempdest); + SLLI(tempdest, retval, 16); + SRAI(dest, tempdest, 16); + break; + } + case SH2_ACCESS_TYPE_L: { + MOVEA(fallback, MappedMemoryReadLong); + CALL(dest, real_address, 0, fallback); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + } DEFINE_LABEL(label_done); + + return 1; +} + +/*----------------------------------*/ + +/** + * do_load: Generate code for an SH-2 memory load operation. + * + * [Parameters] + * entry: Block being translated + * dest: RTL register into which value is to be loaded + * address: RTL register holding load address + * type: Access type (SH2_ACCESS_TYPE_*) + * [Return value] + * Nonzero on success, zero on error + */ +static int do_load(const JitEntry *entry, uint32_t dest, uint32_t address, + unsigned int type) +{ + DEFINE_REG(page); + SRLI(page, address, 19); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(temp); + SLLI(temp, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, temp); + DEFINE_REG(page_ptr); + LOAD_PTR(page_ptr, dp_ptr, ADDR_LO(direct_pages)); + + return do_load_common(entry, dest, address, 0, type, page_ptr); +} + +/*----------------------------------*/ + +/** + * do_load_abs: Generate optimized code for an SH-2 memory load operation + * from a known address. + * + * [Parameters] + * entry: Block being translated + * dest: RTL register into which value is to be loaded + * address: Load address (in SH-2 address space) + * type: Access type (SH2_ACCESS_TYPE_*) + * [Return value] + * Nonzero on success, zero on error + */ +static int do_load_abs(const JitEntry *entry, uint32_t dest, uint32_t address, + unsigned int type) +{ + const uint32_t page = address >> 19; + + if (direct_pages[page]) { + + const uintptr_t real_address = + (uintptr_t)direct_pages[page] + address; + DEFINE_REG(addr_reg); + switch (type) { + case SH2_ACCESS_TYPE_B: { + MOVEA(addr_reg, ADDR_HI(real_address ^ 1)); + LOAD_BS(dest, addr_reg, ADDR_LO(real_address ^ 1)); + break; + } + case SH2_ACCESS_TYPE_W: { + MOVEA(addr_reg, ADDR_HI(real_address)); + LOAD_HS(dest, addr_reg, ADDR_LO(real_address)); + break; + } + case SH2_ACCESS_TYPE_L: { + MOVEA(addr_reg, ADDR_HI(real_address)); + DEFINE_REG(swapped); + LOAD_W(swapped, addr_reg, ADDR_LO(real_address)); + HSWAPW(dest, swapped); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + } else { + + DEFINE_REG(addr_reg); + MOVEA(addr_reg, address); + DEFINE_REG(fallback); + switch (type) { + case SH2_ACCESS_TYPE_B: { + MOVEA(fallback, MappedMemoryReadByte); + DEFINE_REG(retval); + CALL(retval, addr_reg, 0, fallback); + DEFINE_REG(temp); + SLLI(temp, retval, 24); + SRAI(dest, temp, 24); + break; + } + case SH2_ACCESS_TYPE_W: { + MOVEA(fallback, MappedMemoryReadWord); + DEFINE_REG(retval); + CALL(retval, addr_reg, 0, fallback); + DEFINE_REG(temp); + SLLI(temp, retval, 16); + SRAI(dest, temp, 16); + break; + } + case SH2_ACCESS_TYPE_L: { + MOVEA(fallback, MappedMemoryReadLong); + CALL(dest, addr_reg, 0, fallback); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + } + + return 1; +} + +/*----------------------------------*/ + +/** + * do_load_reg: Generate optimized code for an SH-2 memory load operation + * through an SH-2 register. + * + * [Parameters] + * entry: Block being translated + * dest: RTL register into which value is to be loaded + * sh2reg: SH-2 register holding load address (0-15) + * offset: Offset to be added to address + * type: Access type (SH2_ACCESS_TYPE_*) + * postinc: Nonzero if a postincrement access, else zero + * state: SH-2 state block pointer + * state_reg: RTL register holding state block pointer + * [Return value] + * Nonzero on success, zero on error + */ +static int do_load_reg(const JitEntry *entry, uint32_t dest, + unsigned int sh2reg, int32_t offset, unsigned int type, + int postinc, const SH2State *state, + uint32_t state_reg) +{ + const int postinc_size = + !postinc ? 0 : + type==SH2_ACCESS_TYPE_L ? 4 : type==SH2_ACCESS_TYPE_W ? 2 : 1; + +#ifdef OPTIMIZE_STATE_BLOCK + offset += state_cache[state_cache_index(offsetof(SH2State,R[sh2reg]))].offset; +#endif + + if (!pointer_status[sh2reg].known && pointer_status[sh2reg].source != 0) { + const uint32_t address = + MappedMemoryReadLong(pointer_status[sh2reg].source); + pointer_status[sh2reg].known = -1; // Only tentatively known + pointer_status[sh2reg].data_only = 0; + if ((address & 0xDFF00000) == 0x00200000 + || (address & 0xDE000000) == 0x06000000 + ) { + pointer_status[sh2reg].type = 2; + DEFINE_REG(basereg); + MOVEA(basereg, direct_pages[address>>19]); + pointer_status[sh2reg].rtl_basereg = basereg; + } else if ((address & 0xDFF80000) == 0x05C00000 + || (address & 0xDFF00000) == 0x05E00000 + ) { + pointer_status[sh2reg].type = 1; + DEFINE_REG(basereg); + MOVEA(basereg, byte_direct_pages[address>>19]); + pointer_status[sh2reg].rtl_basereg = basereg; + } else { + pointer_status[sh2reg].rtl_basereg = 0; + } + pointer_status[sh2reg].rtl_ptrreg = 0; + } + + if (pointer_status[sh2reg].known) { + + DECLARE_REG(address); + LOAD_STATE_ALLOC_KEEPOFS(address, R[sh2reg]); + + if (pointer_status[sh2reg].rtl_basereg) { + + DECLARE_REG(ptr); + if (sh2reg == 15 && stack_pointer) { + if (offset < -0x8000 || offset > 0x7FFF) { + ALLOC_REG(ptr); + ADDI(ptr, stack_pointer, offset); + offset = 0; + } else { + ptr = stack_pointer; + } + } else { + if (offset < -0x8000 || offset > 0x7FFF) { + DEFINE_REG(offset_addr); + ADDI(offset_addr, address, offset); + offset = 0; + ALLOC_REG(ptr); + ADD(ptr, pointer_status[sh2reg].rtl_basereg, offset_addr); + } else if (pointer_status[sh2reg].rtl_ptrreg) { + ptr = pointer_status[sh2reg].rtl_ptrreg; + } else { + ALLOC_REG(ptr); + ADD(ptr, pointer_status[sh2reg].rtl_basereg, address); + pointer_status[sh2reg].rtl_ptrreg = ptr; + } + } + switch (type) { + case SH2_ACCESS_TYPE_B: { + DECLARE_REG(real_ptr); + int32_t real_offset; + if (sh2reg == 15 && (optimization_flags & SH2_OPTIMIZE_STACK)){ + /* Assume the stack is 32-bit aligned, so we can apply + * the XOR directly to the offset */ + real_ptr = ptr; + real_offset = offset ^ 1; + } else if (pointer_status[sh2reg].type == 1) { + /* No need to modify address for byte-ordered memory */ + real_ptr = ptr; + real_offset = offset; + } else if (offset & 1) { + DEFINE_REG(offset_ptr); + ADDI(offset_ptr, ptr, offset); + ALLOC_REG(real_ptr); + XORI(real_ptr, offset_ptr, 1); + real_offset = 0; + } else { + ALLOC_REG(real_ptr); + XORI(real_ptr, ptr, 1); + real_offset = offset; + } + LOAD_BS(dest, real_ptr, real_offset); + break; + } + case SH2_ACCESS_TYPE_W: { + if (pointer_status[sh2reg].type == 1) { + DEFINE_REG(swapped); + LOAD_HU(swapped, ptr, offset); + DEFINE_REG(temp); + BSWAPH(temp, swapped); + DEFINE_REG(temp2); + SLLI(temp2, temp, 16); + SRAI(dest, temp2, 16); + } else { + LOAD_HS(dest, ptr, offset); + } + break; + } + case SH2_ACCESS_TYPE_L: { + DEFINE_REG(swapped); + LOAD_W(swapped, ptr, offset); + if (pointer_status[sh2reg].type == 1) { + BSWAPW(dest, swapped); + } else { + HSWAPW(dest, swapped); + } + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + } else { // !pointer_status[sh2reg].rtl_basereg + + DECLARE_REG(offset_addr); + if (offset) { + ALLOC_REG(offset_addr); + ADDI(offset_addr, address, offset); + } else { + offset_addr = address; + } + DEFINE_REG(fallback); + switch (type) { + case SH2_ACCESS_TYPE_B: { + MOVEA(fallback, MappedMemoryReadByte); + DEFINE_REG(retval); + CALL(retval, offset_addr, 0, fallback); + DEFINE_REG(temp); + SLLI(temp, retval, 24); + SRAI(dest, temp, 24); + break; + } + case SH2_ACCESS_TYPE_W: { + MOVEA(fallback, MappedMemoryReadWord); + DEFINE_REG(retval); + CALL(retval, offset_addr, 0, fallback); + DEFINE_REG(temp); + SLLI(temp, retval, 16); + SRAI(dest, temp, 16); + break; + } + case SH2_ACCESS_TYPE_L: { + MOVEA(fallback, MappedMemoryReadLong); + CALL(dest, offset_addr, 0, fallback); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + } // if (pointer_status[sh2reg].rtl_basereg) + + } else { // !pointer_status[sh2reg].known + + DECLARE_REG(address); + LOAD_STATE_ALLOC_KEEPOFS(address, R[sh2reg]); + if (optimization_flags & SH2_OPTIMIZE_POINTERS) { + if (!pointer_status[sh2reg].rtl_basereg) { + DEFINE_REG(page); + SRLI(page, address, 19); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(table_offset); + SLLI(table_offset, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, table_offset); + DEFINE_REG(basereg); + LOAD_PTR(basereg, dp_ptr, ADDR_LO(direct_pages)); + pointer_status[sh2reg].rtl_basereg = basereg; + pointer_status[sh2reg].rtl_ptrreg = 0; + pointer_status[sh2reg].checked_djp = 0; + } + ASSERT(do_load_common(entry, dest, address, offset, type, + pointer_status[sh2reg].rtl_basereg)); + } else { + DECLARE_REG(offset_addr); + if (offset) { + ALLOC_REG(offset_addr); + ADDI(offset_addr, address, offset); + } else { + offset_addr = address; + } + ASSERT(do_load(entry, dest, offset_addr, type)); + } + + } + + if (postinc_size) { + ADDI_STATE_NOREG(R[sh2reg], postinc_size); + } + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * log_store: Helper function for do_store() and friends to log a store + * operation. Does nothing if no relevant tracing option is enabled. + * + * [Parameters] + * entry: Block being translated + * address: Register holding SH-2 store address, or store address itself + * src: Register holding value to store + * offset: Offset to be added to address + * type: Access type (SH2_ACCESS_TYPE_[BWL] only) + * is_abs: Nonzero if "address" is the actual store address + * [Return value] + * Nonzero on success, zero on error + */ +static inline int log_store(const JitEntry *entry, uint32_t address, + uint32_t src, int32_t offset, int type, int abs) +{ +#if defined(TRACE) + + static SH2TraceAccessCallback ** const logfunc_ptrs[] = { + [SH2_ACCESS_TYPE_B] = &trace_storeb_callback, + [SH2_ACCESS_TYPE_W] = &trace_storew_callback, + [SH2_ACCESS_TYPE_L] = &trace_storel_callback, + }; + DEFINE_REG(funcptr); + DECLARE_REG(addr_param); + if (abs) { + ALLOC_REG(addr_param); + MOVEI(addr_param, address + offset); + } else if (offset) { + ALLOC_REG(addr_param); + ADDI(addr_param, address, offset); + } else { + addr_param = address; + } + MOVEA(funcptr, *logfunc_ptrs[type]); + CALL_NORET(addr_param, src, funcptr); + +#elif defined(TRACE_STEALTH) + + static const unsigned int storecode[] = { + [SH2_ACCESS_TYPE_B] = 1, + [SH2_ACCESS_TYPE_W] = 2, + [SH2_ACCESS_TYPE_L] = 4, + }; + APPEND(NOP, 0, 0xC0000000 | storecode[type], 0, 0); + if (abs) { + address += offset; + APPEND(NOP, 0, 0xD8000000 | ((address >> 16) & 0xFFFF), 0, 0); + APPEND(NOP, 0, 0xDC000000 | (address & 0xFFFF), 0, 0); + } else { + APPEND(NOP, 0, 0xD0000000 | address, 0, 0); + APPEND(NOP, 0, 0xD4000000 | ((offset >> 16) & 0xFFFF), 0, 0); + APPEND(NOP, 0, 0xD6000000 | (offset & 0xFFFF), 0, 0); + } + APPEND(NOP, 0, 0xE0000000 | src, 0, 0); + +#endif + + return 1; +} + +/*----------------------------------*/ + +/** + * do_store_common: Generate code for an SH-2 memory store operation common + * to both generic loads and register loads from variable addresses. + * + * [Parameters] + * entry: Block being translated + * address: RTL register holding store address + * src: RTL register holding value to be stored + * offset: Offset to be added to address + * type: Access type (SH2_ACCESS_TYPE_[BWL] only) + * state_reg: RTL register holding state block pointer + * page_ptr: RTL register holding base pointer for memory page or NULL + * djppage_ptr: RTL register holding base pointer for JIT flag table, or + * zero to skip JIT write check + * [Return value] + * Nonzero on success, zero on error + */ +static int do_store_common(const JitEntry *entry, uint32_t address, + uint32_t src, int32_t offset, unsigned int type, + uint32_t state_reg, uint32_t page_ptr, + uint32_t djppage_ptr) +{ + CREATE_LABEL(label_fallback); + CREATE_LABEL(label_done); + + DEFINE_REG(final_ptr); // Move this up to help MIPS delay slot optimization + int32_t final_offset; + if (offset < -0x8000 || offset > 0x7FFF) { + DEFINE_REG(offset_addr); + ADDI(offset_addr, address, offset); + ADD(final_ptr, page_ptr, offset_addr); + final_offset = 0; + } else { + ADD(final_ptr, page_ptr, address); + final_offset = offset; + } + + GOTO_IF_Z(label_fallback, page_ptr); { + /* We can write data directly */ + + switch (type) { + case SH2_ACCESS_TYPE_B: { + DEFINE_REG(real_ptr); + int32_t real_offset; + if (final_offset & 1) { + DEFINE_REG(offset_ptr); + ADDI(offset_ptr, final_ptr, final_offset); + XORI(real_ptr, offset_ptr, 1); + real_offset = 0; + } else { + XORI(real_ptr, final_ptr, 1); + real_offset = final_offset; + } + STORE_B(real_ptr, src, real_offset); + break; + } + case SH2_ACCESS_TYPE_W: { + STORE_H(final_ptr, src, final_offset); + break; + } + case SH2_ACCESS_TYPE_L: { + DEFINE_REG(swapped); + HSWAPW(swapped, src); + STORE_W(final_ptr, swapped, final_offset); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + /* Check for modified translations and clear if needed */ + if (djppage_ptr) { + /* Look up the appropriate byte in the table */ + DEFINE_REG(byteofs); + SRLI(byteofs, address, JIT_PAGE_BITS); + DEFINE_REG(byteaddr); + ADD(byteaddr, djppage_ptr, byteofs); + DEFINE_REG(test); + LOAD_BU(test, byteaddr, 0); + /* Does the JIT page contain translations? */ + GOTO_IF_Z(label_done, test); { + /* Clear translations from the JIT page */ + DEFINE_REG(jcw_ptr); + MOVEA(jcw_ptr, jit_clear_write); + CALL_NORET(state_reg, address, jcw_ptr); + } DEFINE_LABEL(label_done); + } + + } DEFINE_LABEL(label_fallback); { + /* Not direct access, so call the fallback routine */ + + DECLARE_REG(real_address); + if (final_offset) { + ALLOC_REG(real_address); + ADDI(real_address, address, final_offset); + } else { + real_address = address; + } + void *funcptr; + switch (type) { + case SH2_ACCESS_TYPE_B: funcptr = MappedMemoryWriteByte; break; + case SH2_ACCESS_TYPE_W: funcptr = MappedMemoryWriteWord; break; + case SH2_ACCESS_TYPE_L: funcptr = MappedMemoryWriteLong; break; + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + DEFINE_REG(fallback); + MOVEA(fallback, funcptr); + CALL_NORET(real_address, src, fallback); + /* No need to check translations if not direct-access */ + + } DEFINE_LABEL(label_done); + + return 1; +} + +/*----------------------------------*/ + +/** + * do_store: Generate code for an SH-2 memory store operation. + * + * [Parameters] + * entry: Block being translated + * address: RTL register holding store address + * src: RTL register holding value to be stored + * type: Access type (SH2_ACCESS_TYPE_[BWL] only) + * state_reg: RTL register holding state block pointer + * [Return value] + * Nonzero on success, zero on error + */ +static int do_store(const JitEntry *entry, uint32_t address, uint32_t src, + unsigned int type, uint32_t state_reg) +{ + CREATE_LABEL(label_fallback); + CREATE_LABEL(label_done); + + /* Log the store if appropriate */ + log_store(entry, address, src, 0, type, 0); + + /* Look up the direct access pointer */ + DEFINE_REG(page); + SRLI(page, address, 19); + DEFINE_REG(table_offset); + SLLI(table_offset, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, table_offset); + DEFINE_REG(page_ptr_temp); + LOAD_PTR(page_ptr_temp, dp_ptr, ADDR_LO(direct_pages)); + /* Also check direct_jit_pages[] to make sure it's writable */ + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_ptr); + ADD(djp_ptr, djp_base, table_offset); + DEFINE_REG(djppage_ptr); + LOAD_PTR(djppage_ptr, djp_ptr, ADDR_LO(direct_jit_pages)); + /* Select to zero if the direct_jit_pages[] entry is zero */ + DEFINE_REG(page_ptr); + SELECT(page_ptr, page_ptr_temp, djppage_ptr, djppage_ptr); + + /* Actually perform the store */ + return do_store_common(entry, address, src, 0, type, state_reg, + page_ptr, djppage_ptr); +} + +/*----------------------------------*/ + +/** + * do_store_abs: Generate optimized code for an SH-2 memory store + * operation to a known address. + * + * [Parameters] + * entry: Block being translated + * address: Store address + * src: RTL register holding value to be stored + * type: Access type (SH2_ACCESS_TYPE_[BWL] only) + * state_reg: RTL register holding state block pointer + * islocal: Nonzero if the address points to local data, else zero + * [Return value] + * Nonzero on success, zero on error + */ +static int do_store_abs(const JitEntry *entry, uint32_t address, uint32_t src, + unsigned int type, uint32_t state_reg, int islocal) +{ + if (!(optimization_flags & SH2_OPTIMIZE_LOCAL_ACCESSES)) { + islocal = 0; // Prevent optimization + } + + log_store(entry, address, src, 0, type, 1); + + const uint32_t page = address >> 19; + + if ((address & 0x1FF00000) != 0 && direct_pages[page]) { + + const uintptr_t real_address = + (uintptr_t)direct_pages[page] + address; + DEFINE_REG(addr_reg); + switch (type) { + case SH2_ACCESS_TYPE_B: { + MOVEA(addr_reg, ADDR_HI(real_address ^ 1)); + STORE_B(addr_reg, src, ADDR_LO(real_address ^ 1)); + break; + } + case SH2_ACCESS_TYPE_W: { + MOVEA(addr_reg, ADDR_HI(real_address)); + STORE_H(addr_reg, src, ADDR_LO(real_address)); + break; + } + case SH2_ACCESS_TYPE_L: { + MOVEA(addr_reg, ADDR_HI(real_address)); + DEFINE_REG(swapped); + HSWAPW(swapped, src); + STORE_W(addr_reg, swapped, ADDR_LO(real_address)); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } +#ifdef JIT_DEBUG_VERBOSE + if (islocal) { + fprintf(stderr, "[OLA] %08X: store to local address 0x%08X\n", + jit_PC, address); + } +#endif + if (!islocal && direct_jit_pages[page]) { + CREATE_LABEL(label_done); + /* Look up the appropriate bit in the direct_jit_pages table */ + const uintptr_t byteaddr_val = (uintptr_t)direct_jit_pages[page] + + (address >> JIT_PAGE_BITS); + DEFINE_REG(byteaddr); + MOVEA(byteaddr, ADDR_HI(byteaddr_val)); + DEFINE_REG(test); + LOAD_BU(test, byteaddr, ADDR_LO(byteaddr_val)); + /* Does the JIT page contain translations? */ + GOTO_IF_Z(label_done, test); { + /* Clear translations from the JIT page */ + DEFINE_REG(sh2_addr_reg); + MOVEI(sh2_addr_reg, address); + DEFINE_REG(jcw_ptr); + MOVEA(jcw_ptr, jit_clear_write); + CALL_NORET(state_reg, sh2_addr_reg, jcw_ptr); + } DEFINE_LABEL(label_done); + } + + } else { + + void *funcptr; + switch (type) { + case SH2_ACCESS_TYPE_B: funcptr = MappedMemoryWriteByte; break; + case SH2_ACCESS_TYPE_W: funcptr = MappedMemoryWriteWord; break; + case SH2_ACCESS_TYPE_L: funcptr = MappedMemoryWriteLong; break; + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + DEFINE_REG(addr_reg); + MOVEI(addr_reg, address); + DEFINE_REG(fallback); + MOVEA(fallback, funcptr); + CALL_NORET(addr_reg, src, fallback); + + } + + return 1; +} + +/*----------------------------------*/ + +/** + * do_store_reg: Generate optimized code for an SH-2 memory store + * operation through an SH-2 register. + * + * [Parameters] + * entry: Block being translated + * sh2reg: SH-2 register holding store address + * src: RTL register holding value to be stored + * offset: Offset to be added to address + * type: Access type (SH2_ACCESS_TYPE_[BWL] only) + * predec: Nonzero if a predecrement access, else zero + * state: SH-2 state block pointer + * state_reg: RTL register holding state block pointer + * [Return value] + * Nonzero on success, zero on error + */ +static int do_store_reg(const JitEntry *entry, unsigned int sh2reg, + uint32_t src, int32_t offset, unsigned int type, + int predec, const SH2State *state, uint32_t state_reg) +{ + /* Half of a JIT page, in bytes (used when deciding whether to perform + * a check for overwrites of translated code) */ + const int32_t half_jit_page = (1U << JIT_PAGE_BITS) / 2; + + const int predec_size = + !predec ? 0 : + type==SH2_ACCESS_TYPE_L ? 4 : + type==SH2_ACCESS_TYPE_W ? 2 : 1; + if (predec_size) { + ADDI_STATE_NOREG(R[sh2reg], -predec_size); + } + +#ifdef OPTIMIZE_STATE_BLOCK + offset += state_cache[state_cache_index(offsetof(SH2State,R[sh2reg]))].offset; +#endif + + if (!pointer_status[sh2reg].known && pointer_status[sh2reg].source != 0) { + const uint32_t address = + MappedMemoryReadLong(pointer_status[sh2reg].source); + pointer_status[sh2reg].known = -1; // Only tentatively known + pointer_status[sh2reg].data_only = 0; + pointer_status[sh2reg].checked = 0; + if ((address & 0xDFF00000) == 0x00200000 + || (address & 0xDE000000) == 0x06000000 + ) { + pointer_status[sh2reg].type = 2; + DEFINE_REG(basereg); + MOVEA(basereg, direct_pages[address>>19]); + pointer_status[sh2reg].rtl_basereg = basereg; + } else if ((address & 0xDFF80000) == 0x05C00000 + || (address & 0xDFF00000) == 0x05E00000 + ) { + pointer_status[sh2reg].type = 1; + DEFINE_REG(basereg); + MOVEA(basereg, byte_direct_pages[address>>19]); + pointer_status[sh2reg].rtl_basereg = basereg; + } else { + pointer_status[sh2reg].rtl_basereg = 0; + } + pointer_status[sh2reg].rtl_ptrreg = 0; + } + + DECLARE_REG(address); + LOAD_STATE_ALLOC_KEEPOFS(address, R[sh2reg]); + + if (pointer_status[sh2reg].known) { + + log_store(entry, address, src, offset, type, 0); + + if (pointer_status[sh2reg].rtl_basereg) { + + DECLARE_REG(ptr); + int32_t ptr_offset; + if (sh2reg == 15 && stack_pointer) { + if (offset < -0x8000 || offset > 0x7FFF) { + ALLOC_REG(ptr); + ADDI(ptr, stack_pointer, offset); + ptr_offset = 0; + } else { + ptr_offset = offset; + ptr = stack_pointer; + } + } else { + if (offset < -0x8000 || offset > 0x7FFF) { + DEFINE_REG(offset_addr); + ADDI(offset_addr, address, offset); + ptr_offset = 0; + ALLOC_REG(ptr); + ADD(ptr, pointer_status[sh2reg].rtl_basereg, offset_addr); + } else { + ptr_offset = offset; + if (pointer_status[sh2reg].rtl_ptrreg) { + ptr = pointer_status[sh2reg].rtl_ptrreg; + } else { + ALLOC_REG(ptr); + ADD(ptr, pointer_status[sh2reg].rtl_basereg, address); + pointer_status[sh2reg].rtl_ptrreg = ptr; + } + } + } + switch (type) { + case SH2_ACCESS_TYPE_B: { + DECLARE_REG(real_ptr); + int32_t real_offset; + if (sh2reg == 15 && (optimization_flags & SH2_OPTIMIZE_STACK)){ + real_ptr = ptr; + real_offset = ptr_offset ^ 1; + } else if (pointer_status[sh2reg].type == 1) { + real_ptr = ptr; + real_offset = ptr_offset; + } else if (offset & 1) { + DEFINE_REG(offset_ptr); + ADDI(offset_ptr, ptr, ptr_offset); + ALLOC_REG(real_ptr); + XORI(real_ptr, offset_ptr, 1); + real_offset = 0; + } else { + ALLOC_REG(real_ptr); + XORI(real_ptr, ptr, 1); + real_offset = ptr_offset; + } + STORE_B(real_ptr, src, real_offset); + break; + } + case SH2_ACCESS_TYPE_W: { + if (pointer_status[sh2reg].type == 1) { + DEFINE_REG(swapped); + BSWAPH(swapped, src); + STORE_H(ptr, swapped, ptr_offset); + } else { + STORE_H(ptr, src, ptr_offset); + } + break; + } + case SH2_ACCESS_TYPE_L: { + DEFINE_REG(swapped); + if (pointer_status[sh2reg].type == 1) { + BSWAPW(swapped, src); + } else { + HSWAPW(swapped, src); + } + STORE_W(ptr, swapped, ptr_offset); + break; + } + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + + /* See if we need to check for overwrites of translated code */ + int need_jit_check = 1; + if (sh2reg == 15 && (optimization_flags & SH2_OPTIMIZE_STACK)) { + need_jit_check = 0; + } else if (pointer_status[sh2reg].data_only) { + need_jit_check = 0; + } else if (pointer_status[sh2reg].type != 2) { + need_jit_check = 0; + } else if (pointer_status[sh2reg].checked + && offset - pointer_status[sh2reg].check_offset > -half_jit_page + && offset - pointer_status[sh2reg].check_offset < half_jit_page + ) { + need_jit_check = 0; + } + + if (need_jit_check) { + /* Obtain the address for checking. If the store offset is + * small, use an offset of zero for checking to save an + * instruction */ + DECLARE_REG(jit_address); + int32_t jit_offset; + if (offset > -half_jit_page && offset < half_jit_page) { + jit_address = address; + jit_offset = 0; + } else { + ALLOC_REG(jit_address); + ADDI(jit_address, address, offset); + jit_offset = offset; + } + /* Load the JIT bitmap address */ + CREATE_LABEL(label_done); + DEFINE_REG(djppage_ptr); + int djppage_ofs; + if (pointer_status[sh2reg].known > 0) { + MOVEA(djppage_ptr, + ADDR_HI(pointer_status[sh2reg].djp_base)); + djppage_ofs = ADDR_LO(pointer_status[sh2reg].djp_base); + } else { + DEFINE_REG(temp1); + SRLI(temp1, jit_address, 19); + DEFINE_REG(djp_offset); + SLLI(djp_offset, temp1, LOG2_SIZEOF_PTR); + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_ptr); + ADD(djp_ptr, djp_base, djp_offset); + LOAD_PTR(djppage_ptr, djp_ptr, ADDR_LO(direct_jit_pages)); + GOTO_IF_Z(label_done, djppage_ptr); + djppage_ofs = 0; + } + /* Look up the appropriate bit in the table */ + DEFINE_REG(byteofs); + SRLI(byteofs, address, JIT_PAGE_BITS); + DEFINE_REG(byteaddr); + ADD(byteaddr, djppage_ptr, byteofs); + DEFINE_REG(test); + LOAD_BU(test, byteaddr, djppage_ofs); + /* Does the JIT page contain translations? */ + GOTO_IF_Z(label_done, test); { + /* Clear translations from the JIT page */ + DEFINE_REG(jcw_ptr); + MOVEA(jcw_ptr, jit_clear_write); + CALL_NORET(state_reg, address, jcw_ptr); + } DEFINE_LABEL(label_done); + /* Mark this register checked for translation overwrites */ + pointer_status[sh2reg].checked = 1; + pointer_status[sh2reg].check_offset = jit_offset; + } // if not an optimized stack access + + } else { // !pointer_status[sh2reg].rtl_basereg + + DECLARE_REG(offset_addr); + if (offset) { + ALLOC_REG(offset_addr); + ADDI(offset_addr, address, offset); + } else { + offset_addr = address; + } + void *funcptr; + switch (type) { + case SH2_ACCESS_TYPE_B: funcptr = MappedMemoryWriteByte; break; + case SH2_ACCESS_TYPE_W: funcptr = MappedMemoryWriteWord; break; + case SH2_ACCESS_TYPE_L: funcptr = MappedMemoryWriteLong; break; + default: + DMSG("0x%08X: BUG: invalid access type %u", jit_PC, type); + return 0; + } + DEFINE_REG(fallback); + MOVEA(fallback, funcptr); + CALL_NORET(offset_addr, src, fallback); + + } // if (pointer_status[sh2reg].rtl_basereg) + + } else { // !pointer_status[sh2reg].known + + if (optimization_flags & SH2_OPTIMIZE_POINTERS) { + log_store(entry, address, src, offset, type, 0); + DECLARE_REG(djppage_ptr); + if (!pointer_status[sh2reg].rtl_basereg) { + DEFINE_REG(page); + SRLI(page, address, 19); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(table_offset); + SLLI(table_offset, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, table_offset); + DEFINE_REG(page_ptr); + LOAD_PTR(page_ptr, dp_ptr, ADDR_LO(direct_pages)); + /* Also check direct_jit_pages[] to make sure it's writable */ + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_ptr); + if (offset > -half_jit_page && offset < half_jit_page) { + /* Check at an offset of zero for efficiency */ + ADD(djp_ptr, djp_base, table_offset); + pointer_status[sh2reg].check_offset = 0; + } else { + DEFINE_REG(djp_address); + ADDI(djp_address, address, offset); + DEFINE_REG(djp_page); + SRLI(djp_page, djp_address, 19); + DEFINE_REG(djp_table_offset); + SLLI(djp_table_offset, djp_page, LOG2_SIZEOF_PTR); + ADD(djp_ptr, djp_base, djp_table_offset); + pointer_status[sh2reg].check_offset = offset; + } + ALLOC_REG(djppage_ptr); + LOAD_PTR(djppage_ptr, djp_ptr, ADDR_LO(direct_jit_pages)); + pointer_status[sh2reg].checked = 1; + /* Select to zero if the direct_jit_pages[] entry is zero */ + DEFINE_REG(basereg); + SELECT(basereg, page_ptr, djppage_ptr, djppage_ptr); + pointer_status[sh2reg].rtl_basereg = basereg; + pointer_status[sh2reg].rtl_ptrreg = 0; + pointer_status[sh2reg].checked_djp = 1; + } else if (!pointer_status[sh2reg].checked_djp) { + /* This register was first used in a load, so we don't yet + * know whether it's writable; check the direct_jit_pages[] + * entry as above */ + DEFINE_REG(page); + SRLI(page, address, 19); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(table_offset); + SLLI(table_offset, page, LOG2_SIZEOF_PTR); + DECLARE_REG(basereg); + basereg = pointer_status[sh2reg].rtl_basereg; + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_ptr); + if (offset > -half_jit_page && offset < half_jit_page) { + ADD(djp_ptr, djp_base, table_offset); + pointer_status[sh2reg].check_offset = 0; + } else { + DEFINE_REG(djp_address); + ADDI(djp_address, address, offset); + DEFINE_REG(djp_page); + SRLI(djp_page, djp_address, 19); + DEFINE_REG(djp_table_offset); + SLLI(djp_table_offset, djp_page, LOG2_SIZEOF_PTR); + ADD(djp_ptr, djp_base, djp_table_offset); + pointer_status[sh2reg].check_offset = offset; + } + ALLOC_REG(djppage_ptr); + LOAD_PTR(djppage_ptr, djp_ptr, ADDR_LO(direct_jit_pages)); + pointer_status[sh2reg].checked = 1; + SELECT(basereg, basereg, djppage_ptr, djppage_ptr); + pointer_status[sh2reg].checked_djp = 1; + } else if (!pointer_status[sh2reg].checked + || offset - pointer_status[sh2reg].check_offset <= -half_jit_page + || offset - pointer_status[sh2reg].check_offset >= half_jit_page + ) { + /* Pointer has not been checked or the last check was at + * least half a JIT page away, so check for JIT overwrites */ + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_address); + ADDI(djp_address, address, offset); + DEFINE_REG(djp_page); + SRLI(djp_page, djp_address, 19); + DEFINE_REG(djp_table_offset); + SLLI(djp_table_offset, djp_page, LOG2_SIZEOF_PTR); + DEFINE_REG(djp_ptr); + ADD(djp_ptr, djp_base, djp_table_offset); + ALLOC_REG(djppage_ptr); + LOAD_PTR(djppage_ptr, djp_ptr, ADDR_LO(direct_jit_pages)); + pointer_status[sh2reg].checked = 1; + pointer_status[sh2reg].check_offset = offset; + } else { + /* We recently checked for JIT overwrites here, so no need + * to do so again */ + djppage_ptr = 0; + } + ASSERT(do_store_common(entry, address, src, offset, type, state_reg, + pointer_status[sh2reg].rtl_basereg, + djppage_ptr)); + } else { + DECLARE_REG(offset_addr); + if (offset) { + ALLOC_REG(offset_addr); + ADDI(offset_addr, address, offset); + } else { + offset_addr = address; + } + ASSERT(do_store(entry, offset_addr, src, type, state_reg)); + } + + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * check_cycles: Add RTL code to commit cycles cached in "cur_cycles", + * check whether we've hit the cycle limit, and terminate execution if so. + * + * [Parameters] + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * [Return value] + * Nonzero on success, zero on error + */ +#if defined(__GNUC__) && !defined(JIT_ACCURATE_ACCESS_TIMING) +__attribute__((unused)) +#endif +static int check_cycles(const JitEntry *entry, uint32_t state_reg) +{ + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DECLARE_REG(cycle_limit); + LOAD_STATE_ALLOC(cycle_limit, cycle_limit); + DEFINE_REG(test); + SLTS(test, cycles, cycle_limit); + CREATE_LABEL(no_interrupt); + GOTO_IF_NZ(no_interrupt, test); { + SAVE_STATE_CACHE(); + FLUSH_STATE_CACHE(); + RETURN(); + RESTORE_STATE_CACHE(); + } DEFINE_LABEL(no_interrupt); + + return 1; +} + +/*----------------------------------*/ + +/** + * check_cycles_and_goto: Add RTL code to commit cycles and check the + * cycle count, jumping to the specified label if the cycle count has not + * reached the limit and terminating execution otherwise. + * + * [Parameters] + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * label: Target label for jump + * [Return value] + * Nonzero on success, zero on error + */ +static int check_cycles_and_goto(const JitEntry *entry, uint32_t state_reg, + uint32_t label) +{ + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DECLARE_REG(cycle_limit); + LOAD_STATE_ALLOC(cycle_limit, cycle_limit); + DEFINE_REG(test); + SLTS(test, cycles, cycle_limit); + GOTO_IF_NZ(label, test); + FLUSH_STATE_CACHE(); + RETURN(); + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * branch_static: Generate code to branch to a static address. The + * address is assumed to be stored in state->branch_target. + * Implements the JUMP_STATIC() macro used by the decoding core. + * + * [Parameters] + * state: Processor state block + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * [Return value] + * Nonzero on success, zero on failure + */ +static int branch_static(SH2State *state, JitEntry *entry, uint32_t state_reg) +{ + if ((state->branch_target < jit_PC + && state->branch_target != entry->sh2_start) + || state->branch_target > entry->sh2_end + ) { + FLUSH_STATE_CACHE(); + RETURN(); + return 1; + } + + uint32_t target_label = btcache_lookup(state->branch_target); + if (!target_label) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("Unresolved branch from %08X to %08X", jit_PC - 2, + (int)state->branch_target); +#endif + target_label = rtl_alloc_label(entry->rtl); + if (UNLIKELY(!target_label)) { +#ifdef JIT_DEBUG + DMSG("Failed to allocate label for unresolved branch at 0x%08X", + jit_PC); +#endif + FLUSH_STATE_CACHE(); + RETURN(); + return 1; + } + if (!record_unresolved_branch(entry, state->branch_target, + target_label)) { + return 0; + } + unsigned int index = (state->branch_target - entry->sh2_start) / 2; + is_branch_target[index/32] |= 1 << (index % 32); + } + + if (state->branch_target < jit_PC) { + check_cycles_and_goto(entry, state_reg, target_label); + } else { + APPEND(GOTO, 0, 0, 0, target_label); + } + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opcode_init: Perform pre-decode processing for an instruction. + * Implements the OPCODE_INIT() macro used by the decoding core. + * + * [Parameters] + * state: Processor state block + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * opcode: SH-2 opcode of instruction being decoded + * recursing: Nonzero if this is a recursive decode, else zero + * [Return value] + * Nonzero on success, zero on failure + */ +static int opcode_init(SH2State *state, JitEntry *entry, uint32_t state_reg, + unsigned int opcode, int recursing) +{ + unsigned int index; + + /* + * (1) If translation tracing is enabled, print a trace line for the + * instruction. + */ +#ifdef JIT_DEBUG_TRACE + char tracebuf[100]; + SH2Disasm(jit_PC, opcode, 0, tracebuf); + fprintf(stderr, "%08X: %04X %s\n", jit_PC, opcode, tracebuf+12); +#endif + + /* + * (2) In JIT_ACCURATE_ACCESS_TIMING mode, determine whether the + * instruction performs a load or store that might not be to + * ROM/RAM, and check the cycle count if so. (This is done before + * the branch target for optimization reasons, since a cycle check + * or unconditional interrupt/termination is always performed on a + * branch.) + */ +#if defined(JIT_ACCURATE_ACCESS_TIMING) + const unsigned int n = opcode>>8 & 0xF; + const unsigned int m = opcode>>4 & 0xF; + int is_pointer; + uint32_t knownbits = 0, value = 0; + if ((opcode & 0xF00E) == 0x0004 || (opcode & 0xF00F) == 0x0006) { + is_pointer = 0; +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(0)] & reg_knownbits[REG_R(n)]; + value = reg_value [REG_R(0)] & reg_value [REG_R(n)]; +# endif + } else if (opcode == 0x002B || (opcode & 0xFF00) == 0xC300) { + is_pointer = PTR_CHECK(15); +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(15)]; + value = reg_value [REG_R(15)]; +# endif + } else if ((opcode & 0xF00E) == 0x000C || (opcode & 0xF00F) == 0x000E) { + is_pointer = 0; +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(0)] & reg_knownbits[REG_R(m)]; + value = reg_value [REG_R(0)] & reg_value [REG_R(m)]; +# endif + } else if ((opcode & 0xB00F) == 0x000F) { + is_pointer = PTR_CHECK(n) && PTR_CHECK(m); +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(n)] & reg_knownbits[REG_R(m)]; + value = reg_value [REG_R(n)] & reg_value [REG_R(m)]; +# endif + } else if ((opcode & 0xF000) == 0x1000 + || (opcode & 0xF00A) == 0x2000 || (opcode & 0xF00B) == 0x2002 + || (opcode & 0xF00A) == 0x4002 || (opcode & 0xF0FF) == 0x401B) { + is_pointer = PTR_CHECK(n); +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(n)]; + value = reg_value [REG_R(n)]; +# endif + } else if ((opcode & 0xF000) == 0x5000 + || (opcode & 0xF00A) == 0x6000 || (opcode & 0xF00B) == 0x6002 + || (opcode & 0xFA00) == 0x8000) { + is_pointer = PTR_CHECK(m); +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_R(m)]; + value = reg_value [REG_R(m)]; +# endif + } else if ((opcode & 0xB000) == 0x9000) { + is_pointer = 0; + knownbits = 0xFFFFFFFF; + value = jit_PC; + } else if ((opcode & 0xFA00) == 0xC000 || (opcode & 0xFB00) == 0xC200) { + is_pointer = 0; +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_GBR]; + value = reg_value [REG_GBR]; +# endif + } else if ((opcode & 0xFC00) == 0xCC00) { + is_pointer = 0; +# ifdef OPTIMIZE_KNOWN_VALUES + knownbits = reg_knownbits[REG_GBR] & reg_knownbits[REG_R(0)]; + value = reg_value [REG_GBR] & reg_value [REG_R(0)]; +# endif + } else { + is_pointer = 1; // i.e. no need to check + } + if (!is_pointer + && ((knownbits & 0xFFF00000) != 0xFFF00000 + || ((value & 0xDFF00000) != 0x00200000 // Don't bother with $Axxxxxxx + && (value & 0xDFF00000) != 0x06000000)) + ) { + /* Exceptions are not accepted when processing a delay slot (SH7604 + * manual page 75, section 4.6.1). For BT/S and BF/S, we have to + * check whether we would actually branch or not to know whether + * the following instruction is considered to be in a delay slot. */ + if (!state->delay) { + check_cycles(entry, state_reg); + } else { // It's a delayed-branch instruction + if (state->branch_type == SH2BRTYPE_BT_S + || state->branch_type == SH2BRTYPE_BF_S + ) { + CREATE_LABEL(no_branch); + if (state->branch_type == SH2BRTYPE_BT_S) { + GOTO_IF_NZ(no_branch, state->branch_cond_reg); + } else { + GOTO_IF_Z(no_branch, state->branch_cond_reg); + } + check_cycles(entry, state_reg); + DEFINE_LABEL(no_branch); + } + } + } +#endif // JIT_ACCURATE_ACCESS_TIMING + + /* + * (4) If this is not a recursive decode and the current address is the + * target of a static branch, reset all known register values and + * pointer base registers, flush the state block register cache, + * and commit pending cycles; then add a label to serve as the + * branch target. + */ + int cached_label = 0; + index = (jit_PC - initial_PC) / 2; + if (!recursing && (is_branch_target[index/32] & (1 << (index % 32)))) { + CLEAR_MAC_IS_ZERO(); +#ifdef JIT_DEBUG + if (UNLIKELY(CACHED_SHIFT_COUNT() != 0)) { + DMSG("0x%08X: WARNING: Shift count (%u) cached over a branch" + " target!", jit_PC, CACHED_SHIFT_COUNT()); + } +#endif + REG_RESETKNOWN(); + unsigned int reg; + for (reg = 0; reg < lenof(pointer_status); reg++) { + /* Known registers were checked at the start of the block, so + * there's no risk of them being undefined due to jumping over + * the code that loads them. However, if a register has been + * copied, the copy may have occurred on a different code + * branch, so mark it as unknown in that case. */ + if (pointer_status[reg].known <= 0) { + pointer_status[reg].known = 0; + pointer_status[reg].rtl_basereg = 0; + pointer_status[reg].source = 0; + } + /* Always reset this to avoid having too many long-lived + * registers (see setup_pointers()); we'll regenerate it as + * needed. */ + pointer_status[reg].rtl_ptrreg = 0; + } + pointer_local = 0; // Known registers never appear here + ASSERT(writeback_state_cache(entry, state_reg, 0)); + clear_state_cache(0); + btcache[btcache_index].sh2_address = jit_PC; + btcache[btcache_index].rtl_label = rtl_alloc_label(entry->rtl); + if (UNLIKELY(!btcache[btcache_index].rtl_label)) { + DMSG("Failed to generate label for branch target at 0x%08X", + jit_PC); + return 0; + } else { + DEFINE_LABEL(btcache[btcache_index].rtl_label); + btcache_index++; + if (UNLIKELY(btcache_index >= lenof(btcache))) { + btcache_index = 0; + } + cached_label = 1; + } + } + + /* + * (5) If this is not a recursive decode and there are any pending + * static branches to the current address, resolve them by defining + * the RTL label they branch to. (However, do _not_ define a label + * if this instruction is in a delay slot, because the RTL code + * implementing the branch will be appended after this instruction, + * which would cause incorrect behavior for other code branching to + * this instruction.) + */ + if (!recursing && !state->delay) { + for (index = 0; index < lenof(unres_branches); index++) { + if (unres_branches[index].sh2_target == jit_PC) { + const uint32_t label = unres_branches[index].target_label; + APPEND(LABEL, 0, 0, 0, label); + unres_branches[index].sh2_target = 0; + if (!cached_label) { + /* Probably won't need it, but cache anyway just in case */ + btcache[btcache_index].sh2_address = jit_PC; + btcache[btcache_index].rtl_label = label; + btcache_index++; + if (UNLIKELY(btcache_index >= lenof(btcache))) { + btcache_index = 0; + } + cached_label = 1; + } + } + } + } + + /* + * (6) If this is not a recursive decode and this instruction is hinted + * as loading a data pointer, set a flag so that we will generate + * RTL to load the base pointer after the instruction completes; + * otherwise, clear the flag. (Note that the hint flag will never + * be set if pointer optimization is disabled, so we don't need to + * check again here.) + */ + index = (jit_PC - initial_PC) / 2; + if (!recursing && (is_data_pointer_load[index/32] & (1 << (index % 32)))) { + state->make_Rn_data_pointer = 1; + } else { + state->make_Rn_data_pointer = 0; + } + + /* + * (7) If enabled, insert a dummy instruction indicating the current + * SH-2 PC. Do this after the label so the instruction isn't + * optimized away as part of a dead code block following a branch. + */ +#ifdef JIT_DEBUG_INSERT_PC + APPEND(NOP, 0, jit_PC, 0, 0); +#endif + + /* + * (8) If TRACE_STEALTH is enabled, insert coded NOPs to inform the + * RTL interpreter of cached values and direct it to trace the + * instruction. + */ +#ifdef TRACE_STEALTH + APPEND(NOP, 0, 0x80000000 | jit_PC, 0, 0); + if (state->pending_select && !state->delay) { + APPEND(NOP, 0, 0x9F000000 | (!state->select_sense) << 16 + | state->branch_cond_reg, 0, 0); + } +# ifdef OPTIMIZE_STATE_BLOCK + APPEND(NOP, 0, 0x98000000 | STATE_CACHE_OFFSET(cycles), 0, 0); + APPEND(NOP, 0, 0x90000000 | state_cache[state_cache_index(offsetof(SH2State,cycles))].rtlreg, 0, 0); +# else + APPEND(NOP, 0, 0x98000000, 0, 0); + APPEND(NOP, 0, 0x90000000, 0, 0); +# endif +#endif + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opcode_done: Perform post-decode processing for an instruction. + * Implements the OPCODE_DONE() macro used by the decoding core. + * + * [Parameters] + * state: Processor state block + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * opcode: SH-2 opcode of instruction being decoded + * recursing: Nonzero if this is a recursive decode, else zero + * [Return value] + * Nonzero on success, zero on failure + */ +static int opcode_done(SH2State *state, JitEntry *entry, uint32_t state_reg, + unsigned int opcode, int recursing) +{ + /* + * (1) If this instruction is hinted as loading a data pointer, + * generate RTL to set up a base pointer register for the SH-2 + * register identified by the Rn field of this instruction (or R0, + * if the instruction is MOVA @(disp,PC),R0). + */ + if (state->make_Rn_data_pointer) { + const unsigned int n = + ((opcode & 0xFF00) == 0xC700) ? 0 : opcode>>8 & 0xF; + + DECLARE_REG(Rn); + LOAD_STATE_ALLOC_KEEPOFS(Rn, R[n]); + DEFINE_REG(page); + SRLI(page, Rn, 19); + DEFINE_REG(dp_base); + MOVEA(dp_base, ADDR_HI(direct_pages)); + DEFINE_REG(table_offset); + SLLI(table_offset, page, LOG2_SIZEOF_PTR); + DEFINE_REG(dp_ptr); + ADD(dp_ptr, dp_base, table_offset); + DEFINE_REG(page_ptr); + LOAD_PTR(page_ptr, dp_ptr, ADDR_LO(direct_pages)); + pointer_status[n].known = -1; + pointer_status[n].type = 2; + pointer_status[n].data_only = 1; + pointer_status[n].rtl_basereg = page_ptr; + } + + /* + * (2) If tracing, write all cached SH-2 registers back to the state + * block so traces show the correct values at each instruction. + */ +#ifdef TRACE + ASSERT(writeback_state_cache(entry, state_reg, 1)); +#endif + + return 1; +} + +/*************************************************************************/ +/***************** SH-2 -> RTL translation main routines *****************/ +/*************************************************************************/ + +/** + * translate_block: Translate a block of SH-2 instructions beginning at + * the address in entry->sh2_start into RTL code. On success, the RTL + * block in entry->rtl is finalized. + * + * [Parameters] + * state: SH-2 processor state + * entry: Block being translated + * [Return value] + * Nonzero on success, zero on error + */ +static int translate_block(SH2State *state, JitEntry *entry) +{ + const uint16_t *fetch_base = + (const uint16_t *)fetch_pages[entry->sh2_start >> 19]; + PRECOND(fetch_base != NULL, return 0); + + unsigned int index; + + /* Clear out translation state. */ + + memset(is_branch_target, 0, sizeof(is_branch_target)); + for (index = 0; index < lenof(btcache); index++) { + btcache[index].sh2_address = 0; + } + btcache_index = 0; + for (index = 0; index < lenof(unres_branches); index++) { + unres_branches[index].sh2_target = 0; + } +#ifdef OPTIMIZE_KNOWN_VALUES + for (index = 0; index < lenof(reg_knownbits); index++) { + reg_knownbits[index] = 0; + reg_value[index] = 0; + } +#endif + ignore_optimization_hints = 0; + is_constant_entry_reg = 0; + memset(pointer_status, 0, sizeof(pointer_status)); + stack_pointer = 0; + pointer_local = 0; + is_data_pointer_reg = 0; + memset(is_data_pointer_load, 0, sizeof(is_data_pointer_load)); +#ifdef OPTIMIZE_STATE_BLOCK + clear_state_cache(1); // Should always be clear here, but just in case + stored_PC = entry->sh2_start; // So loops can skip the store if optimized +#endif + state->branch_type = SH2BRTYPE_NONE; + state->branch_target = 0; + state->branch_target_reg = 0; + state->branch_fold_native = 0; + state->branch_cond_reg = 0; + state->branch_cycles = 0; + state->branch_targets_rts = 0; + state->loop_to_jsr = 0; + state->folding_subroutine = 0; + state->pending_select = 0; + state->select_sense = 0; + state->just_branched = 0; + state->need_interrupt_check = 0; + state->mac_is_zero = 0; + state->cached_shift_count = 0; + state->varshift_target_PC = 0; + state->varshift_type = 0; + state->varshift_Rcount = 0; + state->varshift_max = 0; + state->varshift_Rshift = 0; + state->division_target_PC = 0; + state->div_data.Rquo = 0; + state->div_data.Rrem = 0; + state->div_data.quo = 0; + state->div_data.rem = 0; + state->div_data.SR = 0; + + /* If a manual optimization callback has been provided, check for a + * match at this address, and use the optimized translation if found. + * (This must be done after clearing state, because the callback may + * set optimization hints and return zero.) */ + + if (manual_optimization_callback) { + const uint16_t *fetch = + (const uint16_t *)((uintptr_t)fetch_base + entry->sh2_start); + SH2NativeFunctionPointer hand_tuned_function; + const unsigned int hand_tuned_len = + (*manual_optimization_callback)(state, entry->sh2_start, + fetch, &hand_tuned_function, 0); + if (hand_tuned_len > 0) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("Using hand-tuned code for 0x%08X + %u insns", + entry->sh2_start, hand_tuned_len); +#endif + DEFINE_REG(state_reg); + APPEND(LOAD_PARAM, state_reg, 0, 0, 0); + DEFINE_REG(funcptr_reg); + MOVEA(funcptr_reg, hand_tuned_function); + CALL_NORET(state_reg, 0, funcptr_reg); + RETURN(); + ASSERT(rtl_finalize_block(entry->rtl)); + entry->sh2_end = entry->sh2_start + hand_tuned_len*2 - 1; + return 1; + } + } + + /* If any registers were marked as constant on entry, load their + * values into the known-constant array. */ + +#ifdef OPTIMIZE_KNOWN_VALUES + if (is_constant_entry_reg) { + for (index = 0; index < 16; index++) { + if (is_constant_entry_reg & (1<R[index]; + } + } + } +#endif + + /* Check whether saturation code can potentially be optimized out of + * MAC instructions. */ + + can_optimize_mac_nosat = (optimization_flags & SH2_OPTIMIZE_MAC_NOSAT) + && !(state->SR & SR_S); + + /* Mark which registers are currently valid pointers. Note that we + * currently can't detect ROM pointers, because the pointer analysis + * logic would treat "offset + unknown pointer" as "ROM pointer + + * unknown offset". */ + + pointer_regs = 0; + if (optimization_flags & SH2_OPTIMIZE_POINTERS) { + for (index = 0; index < 16; index++) { + if ((state->R[index] & 0xDFF00000) == 0x00200000 + || (state->R[index] & 0xDE000000) == 0x06000000 + || (state->R[index] & 0xDFF80000) == 0x05C00000 + || (state->R[index] & 0xDFF00000) == 0x05E00000 + ) { + pointer_regs |= 1 << index; + } + } + } + + /* Scan through the SH-2 code to find the end of the block and + * determine what parts of it are translatable code, and to perform + * pre-translation checks for various optimizations. */ + + unsigned int block_len = scan_block(state, entry, entry->sh2_start); + if (!block_len) { + DMSG("Failed to find any translatable instructions at 0x%08X", + entry->sh2_start); + return 0; + } + entry->sh2_end = entry->sh2_start + block_len*2 - 1; + + /* Preload the state block pointer (first function argument) into an + * RTL register, and mark it as a unique pointer. */ + + DEFINE_REG(state_reg); + APPEND(LOAD_PARAM, state_reg, 0, 0, 0); + ASSERT(rtl_register_set_unique_pointer(entry->rtl, state_reg)); + + /* If we have an optimizable loop, prepare RTL registers for all SH-2 + * registers live in the loop as well as the cycle count and limit, up + * to the maximum configured. */ + +#ifdef OPTIMIZE_LOOP_REGISTERS + if (can_optimize_loop) { + loop_live_registers |= + 1<sh2_start, loop_live_registers, + loop_load_registers, loop_changed_registers, + loop_invariant_pointers); +# endif + unsigned int regs_fixed = 0; + for (index = 0; index < lenof(state_cache); index++) { + if (loop_live_registers & 1<= OPTIMIZE_LOOP_REGISTERS_MAX_REGS) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("Out of fixed registers (%u), skipping some", + regs_fixed); +# endif + break; + } + ALLOC_REG(state_cache[index].rtlreg); + state_cache[index].offset = 0; + state_cache[index].fixed = 1; + state_cache[index].flush = + (loop_changed_registers & 1<sh2_start); + + /* If the block contains MACs and the S flag is known not to change + * from block entry to the last MAC, add a check to ensure S is still + * clear at runtime and retranslate the block if not. */ + // FIXME: We assume that in a loop, if S is left clear through the + // last MAC in the loop, it won't be set before the end of the loop + // body. If anybody actually does that, they should be dragged out + // into the street and shot. + + if (!skip_preconditions && can_optimize_mac_nosat && block_contains_mac) { + if (!invalidate_label) { + ASSERT(invalidate_label = rtl_alloc_label(entry->rtl)); + } + DECLARE_REG(SR); + LOAD_STATE_ALLOC(SR, SR); + DEFINE_REG(S); + ANDI(S, SR, SR_S); + GOTO_IF_NZ(invalidate_label, S); + } + + /* If we're optimizing a loop, initialize pointer status and preload + * pointer base addresses before the block-top label. In this case, we + * ignore any pointers which are not loop invariants and look them up + * at runtime instead. */ + // FIXME: I wonder if we have to load the stack pointer dynamically? + // (e.g. if the same code is executed by both MSH2 and SSH2, and SSH2 + // uses $002xxxxx instead of $060xxxxx) + +#ifdef OPTIMIZE_LOOP_REGISTERS + if (can_optimize_loop) { + pointer_used &= loop_invariant_pointers; + ASSERT(setup_pointers(state, entry, state_reg, skip_preconditions, + &invalidate_label)); + } +#endif + + /* Add a label for the special case of branching back to the beginning + * of the block (backward branches normally terminate execution of the + * block). When we're not optimizing loops, this has to be added + * _before_ precondition checks on pointer values so changes in those + * values are properly detected when the code loops back. */ + + CREATE_LABEL(block_start_label); + DEFINE_LABEL(block_start_label); + btcache[btcache_index].sh2_address = entry->sh2_start; + btcache[btcache_index].rtl_label = block_start_label; + btcache_index++; + + /* If we're not optimizing a loop, initialize pointer status and + * preload pointer base addresses after the block-top label. */ + +#ifdef OPTIMIZE_LOOP_REGISTERS + if (!can_optimize_loop) +#endif + ASSERT(setup_pointers(state, entry, state_reg, skip_preconditions, + &invalidate_label)); + + /* Perform the actual translation of SH-2 instructions to RTL code. */ + +#ifdef JIT_DEBUG_VERBOSE + DMSG("Starting translation at %08X", entry->sh2_start); +#endif + jit_PC = entry->sh2_start; + uint32_t next_code_address = jit_PC; // For the fall-off-the-end case + while (jit_PC <= entry->sh2_end) { + index = (jit_PC - entry->sh2_start) / 2; + if (word_info[index] & WORD_INFO_CODE) { + if (UNLIKELY(!translate_insn(state, entry, state_reg, 0, 0))) { + DMSG("Failed to translate instruction at 0x%08X", + entry->sh2_start + index*2); + return 0; + } + next_code_address = jit_PC; + } else { + /* It's not code, so just skip the word */ + jit_PC += 2; + } + } +#ifdef JIT_DEBUG_VERBOSE + DMSG("Translation ended at %08X", jit_PC-2); +#endif + + /* If static branch prediction is enabled, count the number of static + * branches to be predicted and allocate memory for the prediction + * data. The memory used by this data will be included in the total + * data size of the translated block. Otherwise, just define a flag + * indicating whether we need to append a RETURN to terminate the + * block. */ + +#ifdef JIT_BRANCH_PREDICT_STATIC + entry->num_static_branches = state->just_branched ? 0 : 1; + for (index = 0; index < lenof(unres_branches); index++) { + if (unres_branches[index].sh2_target != 0) { + entry->num_static_branches++; + } + } + if (entry->num_static_branches > 0) { + const uint32_t static_predict_size = + entry->num_static_branches * sizeof(BranchTargetInfo); + entry->static_predict = calloc(static_predict_size, 1); + if (UNLIKELY(!entry->static_predict)) { + DMSG("No memory for %u static branch predictions", + entry->num_static_branches); + return 0; + } + jit_total_data += static_predict_size; + } + int branch_num = 0; +#else // !JIT_BRANCH_PREDICT_STATIC + int need_return = 0; +#endif + + /* Flush the state block cache and terminate the code if it can fall + * off the end of the block. */ + + if (!state->just_branched) { + ASSERT(writeback_state_cache(entry, state_reg, 1)); + clear_state_cache(0); +#ifdef JIT_BRANCH_PREDICT_STATIC + ASSERT(add_static_branch_terminator( + entry, state_reg, next_code_address, branch_num++)); +#else + need_return = 1; +#endif + } + + /* Add fallback termination code for unresolved static branches. */ + + for (index = 0; index < lenof(unres_branches); index++) { + if (unres_branches[index].sh2_target != 0) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("FAILED to resolve branch to %08X", + unres_branches[index].sh2_target); +#endif + DEFINE_LABEL(unres_branches[index].target_label); +#ifdef JIT_BRANCH_PREDICT_STATIC + ASSERT(add_static_branch_terminator( + entry, state_reg, unres_branches[index].sh2_target, + branch_num++)); +#else + need_return = 1; +#endif + } + } + + /* If static branch prediction is disabled, add a RETURN if needed for + * falling off the end of the block or terminating unresolved static + * branches. */ + +#ifndef JIT_BRANCH_PREDICT_STATIC + if (need_return) { + RETURN(); + } +#endif + + /* If necessary, add fallback code to invalidate the block if a + * precondition check fails. */ + + if (invalidate_label) { + DEFINE_LABEL(invalidate_label); + DEFINE_REG(imm_sh2_start); + MOVEI(imm_sh2_start, entry->sh2_start); + DEFINE_REG(addr_mark_purged); + MOVEA(addr_mark_purged, jit_mark_purged); + CALL_NORET(imm_sh2_start, 0, addr_mark_purged); + DEFINE_REG(imm_1); + MOVEI(imm_1, 1); + DEFINE_REG(addr_must_clear); + MOVEA(addr_must_clear, &entry->must_clear); + STORE_B(addr_must_clear, imm_1, 0); + RETURN(); + } + + /* Close out the translated block. */ + + if (UNLIKELY(!rtl_finalize_block(entry->rtl))) { + DMSG("Failed to finalize block at 0x%08X", entry->sh2_start); + return 0; + } + if (JIT_OPTIMIZE_FLAGS) { + if (UNLIKELY(!rtl_optimize_block(entry->rtl, JIT_OPTIMIZE_FLAGS))) { +#ifdef JIT_DEBUG + DMSG("Failed to optimize block at 0x%08X", entry->sh2_start); +#endif + /* Block is still usable, so keep going */ + } + } + + /* Clear everything from the cache, including any fixed registers. */ + + clear_state_cache(1); + + /* All done, return success. */ + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * setup_pointers: Initialize the pointer_status[] array and load any + * necessary pointer base registers. Helper function for translate_block(). + * + * [Parameters] + * state: SH-2 processor state + * entry: Block being translated + * skip_preconditions: Nonzero if preconditions should be skipped + * invalidate_label_ptr: Pointer to variable holding label index to + * jump to when the block should be invalidated + * [Return value] + * Nonzero on success, zero on error + */ +static int setup_pointers(SH2State * const state, JitEntry * const entry, + const unsigned int state_reg, + const int skip_preconditions, + unsigned int * const invalidate_label_ptr) +{ + unsigned int index; + + for (index = 0; index < lenof(pointer_status); index++) { + if ((pointer_used & 1<R[index]; + const unsigned int addr_shift = + ((reg_address & 0x0F000000) == 0x05000000) ? 19 : 20; + if (!(index == 15 && (optimization_flags & SH2_OPTIMIZE_STACK)) + && !(is_data_pointer_reg & 1<rtl)); + } + DECLARE_REG(regval); + LOAD_STATE_ALLOC(regval, R[index]); + DEFINE_REG(highbits); + SRLI(highbits, regval, addr_shift); + DEFINE_REG(test); + XORI(test, highbits, reg_address >> addr_shift); + GOTO_IF_NZ(*invalidate_label_ptr, test); + } + pointer_status[index].type = + ((reg_address & 0x0F000000) == 0x05000000) ? 1 : 2; + DEFINE_REG(basereg); + if (pointer_status[index].type == 1) { + MOVEA(basereg, byte_direct_pages[reg_address>>19]); + } else { + MOVEA(basereg, direct_pages[reg_address>>19]); + } + pointer_status[index].rtl_basereg = basereg; + /* We don't immediately load a direct pointer in order to + * minimize the number of long-lived registers (one base + * register instead of lots of direct pointer registers); + * the direct pointer will be created as needed and + * discarded at branch points. */ + pointer_status[index].rtl_ptrreg = 0; + pointer_status[index].djp_base = + direct_jit_pages[reg_address>>19]; + if (index == 15 && (optimization_flags & SH2_OPTIMIZE_STACK)) { + DECLARE_REG(regval); + LOAD_STATE_ALLOC(regval, R[index]); + DEFINE_REG(ptrreg); + ADD(ptrreg, basereg, regval); + stack_pointer = ptrreg; + } + } else { + /* Used as a pointer, but not pointing to a directly- + * accessible memory region, so we'll call the fallback + * functions for accesses through this register. But first + * make sure it really isn't pointing to a memory region + * that can contain code, or else we could overwrite an + * already-translated routine and not notice. */ + if (!*invalidate_label_ptr) { + ASSERT(*invalidate_label_ptr = + rtl_alloc_label(entry->rtl)); + } + DECLARE_REG(regval); + LOAD_STATE_ALLOC(regval, R[index]); + DEFINE_REG(page); + SRLI(page, regval, 19); + DEFINE_REG(page_ofs); + SLLI(page_ofs, page, LOG2_SIZEOF_PTR); + DEFINE_REG(djp_base); + MOVEA(djp_base, ADDR_HI(direct_jit_pages)); + DEFINE_REG(djp_ptr); + ADD(djp_ptr, djp_base, page_ofs); + DEFINE_REG(test); + LOAD_PTR(test, djp_ptr, ADDR_LO(direct_jit_pages)); + GOTO_IF_NZ(*invalidate_label_ptr, test); + pointer_status[index].rtl_basereg = 0; + pointer_status[index].rtl_ptrreg = 0; + } + } else { + pointer_status[index].known = 0; + pointer_status[index].rtl_basereg = 0; + pointer_status[index].rtl_ptrreg = 0; + } + } + + return 1; +} + +/*************************************************************************/ + +/** + * scan_block: Scan through a block of SH-2 code, finding the length of + * the block, recording whether each word in the block is code or data, and + * recording all addresses targeted by branches in the is_branch_target[] + * array. + * + * [Parameters] + * state: Processor state block pointer + * entry: Translated block data structure + * address: Start of block in SH-2 address space + * [Return value] + * Length of block, in instructions + * [Side effects] + * - Fills in word_info[], is_branch_target[], branch_thread_target[], + * and branch_thread_count[] + * - Clears can_optimize_mac_nosat if an unoptimizable MAC is found + * - Sets or clears block_contains_mac appropriately + * - Sets pointer_used to the set of registers used as pointers + * - If OPTIMIZE_LOOP_REGISTERS is defined, sets can_optimize_loop, + * loop_live_registers, loop_load_registers, loop_changed_registers, + * and loop_invariant_pointers appropriately + */ +static int scan_block(SH2State *state, JitEntry *entry, uint32_t address) +{ + const uint32_t start_address = address; // Save the block's start address + + const uint16_t *fetch_base = (const uint16_t *)fetch_pages[address >> 19]; + PRECOND(fetch_base != NULL, return 0); + const uint16_t *fetch = + (const uint16_t *)((uintptr_t)fetch_base + address); + + unsigned int index; + + memset(word_info, 0, sizeof(word_info)); + + /* If we just encountered a branch instruction that branched to an + * address we haven't seen before, remember the branch and target + * addresses so we can mark the branch as fall-through if we don't + * translate anything in between. */ + unsigned int fallthru_target = 0; // Target word_info[] index (0 = none) + unsigned int fallthru_source = 0; // Location of branch (word_info[] index) + + /* Remember whether we've seen something write to SR (we don't clear + * the can-optimize flag unless we see a MAC instruction _after_ a + * write to SR, since the SR write could just be restoring it at the + * end of the subroutine) */ + int sr_was_changed = 0; + /* We haven't seen a MAC instruction yet */ + block_contains_mac = 0; + + /* Remember the last CLRMAC we saw, as well as relevant state + * information at the point of that CLRMAC, so we can roll back if we + * later find an unoptimizable MAC instruction. */ + int last_clrmac = -1; // Word index (negative = none seen) + uint32_t last_clrmac_pointer_used = 0; + uint8_t last_clrmac_comn = 0; // can_optimize_mac_nosat + uint8_t last_clrmac_bcm = 0; // block_contains_mac + + /* Map current register contents to registers at the block entry point, + * so we can track pointer values through MOV instructions. A value of + * 31 is inserted for registers which have been overwritten with other + * values (so we don't attempt to mark the original register indices as + * "used"); a value of 30 indicates that the register is known to be a + * pointer (either by being loaded with a MOVA instruction or from an + * optimization hint) but does not derive from a specific register. */ + uint8_t pointer_map[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + /* Clear the current set of registers used as pointers. */ + pointer_used = 0; + + /* Track which registers have values loaded via MOV @(disp,PC) for + * subroutine folding. A value of 1 (an impossible target address) + * indicates that the register has not been so loaded. We assume that + * any PC-relative load subsequently used in a JSR is a constant. */ + uint32_t local_constant[16] = {1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1}; + for (index = 0; index < 16; index++) { + if (is_constant_entry_reg & (1<R[index]; + } + } + /* We also need to remember the state of each register at every branch + * target; if a particular instruction can be reached from multiple + * sources or does not fall through from the preceding instruction, the + * contents of registers will need to be updated appropriately. */ + static uint32_t local_constant_per_insn[JIT_INSNS_PER_BLOCK][16]; + int is_local_branch = 0; + unsigned int local_branch_target_index = 0; + int local_branch_is_new = 0; + +#ifdef OPTIMIZE_LOOP_REGISTERS + /* Initialize loop optimization tracking state; also keep track of + * whether we've seen a backward branch to the beginning of the block + * and the loop register state at the time of that branch. */ + can_optimize_loop = 1; + loop_live_registers = 0; + loop_load_registers = 0; + loop_changed_registers = 0; + loop_invariant_pointers = 0xFFFF; + int is_loop = 0; + int saw_branch_target = 0; + int at_loop_branch = 0; + uint32_t final_live_registers = 0; + uint32_t final_load_registers = 0; + uint32_t final_changed_registers = 0; + uint32_t final_invariant_pointers = 0; + + /* Remember which registers had been set before the first forward + * branch encountered in the loop. Any registers set by subsequent + * instructions will be added to the preload set even if their initial + * values are not used as source operands within the loop, so the + * corresponding RTL registers have the proper values even if the code + * jumps out from the middle of the loop. */ + uint32_t loop_noload_registers = 0; + int loop_noload_registers_set = 0; + int at_branch = 0; +#endif + + + /* Find the maximum length of this block: either JIT_INSNS_PER_BLOCK or + * the number of instruction words before the next blacklisted region, + * whichever is smaller. */ + + unsigned int max_len = JIT_INSNS_PER_BLOCK; + for (index = 0; index < lenof(blacklist); index++) { + if (blacklist[index].start >= start_address + && blacklist[index].start < (start_address + max_len*2) + ) { + max_len = (blacklist[index].start - start_address) / 2; + } + } + if (UNLIKELY(max_len < 2)) { + DMSG("No translatable block at 0x%X (max_len=%u)", + start_address, max_len); + return 0; + } + + int block_len = 0; + int end_block = 0; + int end_block_next = 0; // For use with LDC ...,SR + int delay = 0; + int at_uncond_branch = 0; + for (; !end_block || delay; address += 2, fetch++, block_len++) { + + /* Save whether or not this is a delay slot, then clear "delay". */ + + const int now_in_delay = delay; + delay = 0; + + /* Move the delayed block-end flag (set when an LDC ...,SR + * instruction is detected) to end_block; if it's set, we'll end + * the block after this instruction. */ + + end_block = end_block_next; + end_block_next = 0; + + /* Get the opcode. */ + + const unsigned int opcode = *fetch; + + /* Retrieve information about the opcode. */ + + const uint32_t opcode_info = get_opcode_info(opcode); + + /* Check whether the opcode is valid, and abort if not. Don't + * include the invalid instruction in the block; if we get there + * and it's still invalid, we'll interpret it (but who knows, maybe + * it'll be modified in the meantime). */ + + if (!(opcode_info & SH2_OPCODE_INFO_VALID)) { + goto abort_scan; + } + + /* Mark this word as code. */ + + word_info[block_len] = WORD_INFO_CODE; + + /* If we're not in a delay slot, check whether we just fell through + * from a branch, and clear the fall-through variables. */ + + int at_fallthru = 0; + const uint32_t saved_fallthru_source = fallthru_source; + const uint32_t saved_fallthru_target = fallthru_target; + if (!now_in_delay && fallthru_target) { + if (block_len == fallthru_target) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("Continuing at %08X from fall-through branch at %08X", + address, start_address + fallthru_source*2); +#endif + /* We don't actually update the word_info[] table until we + * have successfully scanned this instruction. If we + * marked the branch as fall-through here but later decided + * to terminate the block for optimization reasons (e.g. + * unoptimizable pointers) before this instruction, the + * generated RTL would incorrectly set the PC to the + * instruction following the branch instead of the branch + * target. */ + at_fallthru = 1; + } + fallthru_target = fallthru_source = 0; + } + + /* If this is a branch target, update the local constant list used + * by subroutine folding. */ + + if (is_branch_target[block_len/32] & (1 << (block_len%32))) { + unsigned int reg; + for (reg = 0; reg < 16; reg++) { + local_constant[reg] = local_constant_per_insn[block_len][reg]; + } + } + + /* Check for MAC.[WL] optimizations. */ + + if (optimization_flags & SH2_OPTIMIZE_MAC_NOSAT) { + if ((opcode & 0xF0FF) == 0x4007 || (opcode & 0xF0FF) == 0x400E) { + sr_was_changed = 1; + } else if ((opcode & 0xB00F) == 0x000F) { + block_contains_mac = 1; + if (sr_was_changed) { + can_optimize_mac_nosat = 0; // Oh well, tough luck + } + } + } + + /* Record pointer accesses and register modifications for pointer + * optimization. */ + + if (optimization_flags & SH2_OPTIMIZE_POINTERS) { + + optimize_pointers(start_address, address, opcode, opcode_info, + pointer_map); + + /* If we're looking at a MAC instruction beyond the beginning + * of the block, and at least one of the source registers to + * the MAC is an unknown pointer, terminate the block before + * this instruction (or before a preceding CLRMAC if there was + * a recent one) so we can translate the MAC with maximum + * optimization. But don't check this if we're in a delay + * slot, since the delay slot isn't necessarily part of the + * following code stream (and interrupting a block between a + * branch and its delay slot will wreak havoc with processor + * state). */ + if ((optimization_flags & SH2_OPTIMIZE_POINTERS_MAC) + && block_len > 0 + && !now_in_delay + ) { + if ((opcode & 0xB00F) == 0x000F) { + int stop = 0, rollback = 0; + optimize_pointers_mac( + address, opcode, pointer_map, + last_clrmac<0 ? 0 : start_address + last_clrmac*2, + &stop, &rollback + ); + if (stop) { + if (rollback) { + block_len = last_clrmac; + pointer_used = last_clrmac_pointer_used; + can_optimize_mac_nosat = last_clrmac_comn; + block_contains_mac = last_clrmac_bcm; + } + goto abort_scan; + } + } else if (opcode == 0x0028) { + /* Remember this CLRMAC so we can terminate before it + * if we find a MAC with unoptimizable memory accesses */ + last_clrmac = block_len; + last_clrmac_pointer_used = pointer_used; + last_clrmac_comn = can_optimize_mac_nosat; + last_clrmac_bcm = block_contains_mac; + } + } // if ((opt_flags & SH2_OPTIMIZE_POINTERS_MAC) && etc.) + +#if OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD > 0 + /* If this instruction accesses through a non-optimizable + * pointer, and there are more accesses to the same pointer in + * subsequent instructions, terminate the block before this + * instruction. (Again, don't do this if we're in a delay + * slot.) */ + int pointer_reg = -1; + if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rn) { + pointer_reg = opcode>>8 & 0xF; + } else if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rm) { + pointer_reg = opcode>>4 & 0xF; + } + /* First make sure this isn't an instruction that overwrites + * the same register it uses as a pointer. */ + if (((opcode_info & SH2_OPCODE_INFO_SETS_R0) && pointer_reg == 0) + || ((opcode_info & SH2_OPCODE_INFO_SETS_Rn) + && pointer_reg == (opcode>>8 & 0xF)) + ) { + pointer_reg = -1; + } + if (pointer_reg >= 0 + && pointer_map[pointer_reg] == 31 + && !now_in_delay + ) { + unsigned int accesses = 1; + int i; + for (i = 1; i < max_len - block_len; i++) { + const unsigned int opcode2 = fetch[i]; + const unsigned int n = opcode2>>8 & 0xF; + const unsigned int m = opcode2>>8 & 0xF; + const uint32_t info2 = get_opcode_info(opcode2); + int access_reg = -1; + if (info2 & SH2_OPCODE_INFO_ACCESSES_Rn) { + access_reg = n; + } else if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rm) { + access_reg = m; + } + if (access_reg == pointer_reg) { + accesses++; + if (accesses >= OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD) { +# ifdef JIT_DEBUG_VERBOSE + DMSG("Breaking block from %08X at %08X due to" + " unoptimizable pointer in r%d\n", + start_address, address, pointer_reg); +# endif + goto abort_scan; + } + } + if (((info2 & SH2_OPCODE_INFO_SETS_R0) && pointer_reg == 0) + || ((info2 & SH2_OPCODE_INFO_SETS_Rn) && pointer_reg == n) + || (info2 & (SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_COND)) + ) { + /* The pointer register was overwritten, or a + * branch interrupted the block, so stop checking. + * (We stop even for conditional branches, so we + * don't break a block for the first access when + * that access is immediately followed by a check. */ + break; + } + } + } // if (pointer_reg >= 0 && etc.) +#endif // OPTIMIZE_POINTERS_BLOCK_BREAK_THRESHOLD > 0 + + } // if (optimization_flags & SH2_OPTIMIZE_POINTERS) + +#ifdef OPTIMIZE_LOOP_REGISTERS + + /* If this address is a branch target and we're within a loop, the + * loop is too complex to optimize. Of course, we won't know + * whether we're in a loop until we see a branch back to the top, + * so just track that we saw a branch target for now. */ + + if (is_branch_target[block_len/32] & (1 << (block_len%32))) { + saw_branch_target = 1; + } + + /* If this is a potential loop, check for changes to register state. */ + + if (can_optimize_loop) { + check_loop_registers(opcode, opcode_info); + } + +#endif // OPTIMIZE_LOOP_REGISTERS + + /* If the instruction is a static branch within the potential range + * of this block, record the target; also check for optimizations. */ + + if ((opcode & 0xF900) == 0x8900 // B[TF]{,_S} + || (opcode & 0xF000) == 0xA000 // BRA + ) { + int32_t disp; + if ((opcode & 0xF000) == 0xA000) { + disp = opcode & 0xFFF; + disp <<= 20; // Sign-extend + disp >>= 19; // And double + } else { + disp = opcode & 0xFF; + disp <<= 24; + disp >>= 23; + } + uint32_t target = (address + 4) + disp; + +#ifdef OPTIMIZE_BRANCH_THREAD + /* If this is a conditional branch, check whether the target is + * another conditional branch of the same sense, either without + * a delay slot or with a NOP in the delay slot; if so, then + * thread the branch through to the final target. But don't + * thread if this branch has a delay slot whose instruction + * modifies SR.T. */ + if ((opcode & 0xF900) == 0x8900 + && target >= start_address + && target <= start_address + max_len*2 + && (!(opcode & 0x0400) + || !(get_opcode_info(fetch[1]) & SH2_OPCODE_INFO_SETS_SR_T)) + ) { + branch_thread_count[block_len] = 0; + unsigned int target_insn0 = fetch[2+(disp/2)+0]; + unsigned int target_insn1 = fetch[2+(disp/2)+1]; + while ((target_insn0 & 0xFF00) == (opcode & 0xFB00) // B[TF] + || ((target_insn0 & 0xFF00) + == ((opcode & 0xFB00) | 0x0400) // B[TF]/S + && target_insn1 == 0x0009) // NOP + ) { + disp = ((int32_t)target_insn0 << 24) >> 23; + target = target + 4 + disp; +#ifdef JIT_DEBUG_VERBOSE + DMSG("Threading branch from 0x%08X through to 0x%08X", + address, target); +#endif + word_info[block_len] |= WORD_INFO_THREADED; + branch_thread_target[block_len] = target; + branch_thread_count[block_len]++; // Assume no overflow + disp = target - (address + 4); + if (target < start_address + || target >= start_address + max_len*2 + ) { + break; + } + target_insn0 = fetch[2+(disp/2)+0]; + target_insn1 = fetch[2+(disp/2)+1]; + } + } +#endif // OPTIMIZE_BRANCH_THREAD + +#ifdef OPTIMIZE_BRANCH_SELECT + /* If this is a conditional branch, check whether the branch + * acts like an RTL SELECT instruction. To be treated like a + * SELECT, the branch must skip exactly one instruction which + * is not targeted by any other branch and which supports being + * used as a SELECT source in the SH-2 decoder (sh2-core.i). */ + if (target < start_address + max_len*2) { + if (optimize_branch_select(start_address, address, fetch, + opcode, target)) { + goto skip_branch_target_check; + } + } +#endif // OPTIMIZE_BRANCH_SELECT + + /* If enabled, check whether this branch targets a RTS/NOP pair. + * We allow this check even if the target is out of range of + * the block, on the assumption that they are part of the same + * routine. */ + if (optimization_flags & SH2_OPTIMIZE_BRANCH_TO_RTS) { + unsigned int target_insn0 = fetch[2+(disp/2)+0]; + unsigned int target_insn1 = fetch[2+(disp/2)+1]; + if (target_insn0 == 0x000B // RTS + && target_insn1 == 0x0009 // NOP + ) { + word_info[block_len] |= WORD_INFO_BRA_RTS; + goto skip_branch_target_check; + } + } + +#ifdef OPTIMIZE_LOOP_REGISTERS + /* If this is a backward branch to the beginning of the block, + * mark the block as a loop and save loop state after the + * branch's delay slot (if any). But if we've seen a branch + * target before (or at) this instruction, then the loop is too + * complex to optimize, so clear the relevant flag. */ + if (target == start_address) { + if (saw_branch_target) { + can_optimize_loop = 0; + } else { + is_loop = 1; + at_loop_branch = 1; + } + } +#endif + +#ifdef OPTIMIZE_LOOP_TO_JSR + /* If this is a backward branch to two instructions before + * the beginning of the block, record whether the targeted + * instruction is a subroutine call (JSR, BSR, or BSRF). + * In this case, also mark the following instruction as a + * branch target, because we're effectively flipping the + * sense of the branch and skipping the call and delay slot + * on the opposite condition. (If we don't do this, side + * effects of the delay slot such as generating a native + * pointer register for an SH-2 pointer won't be forgotten in + * the fall-through case, causing the fall-through code to use + * uninitialized registers and potentially crash.) */ + if (target == start_address - 4) { + unsigned int target_insn = fetch[2+(disp/2)]; + if ((target_insn & 0xF0FF) == 0x400B // JSR @Rn + || (target_insn & 0xF000) == 0xB000 // BSR label + || (target_insn & 0xF0FF) == 0x0003 // BSRF Rn + ) { + word_info[block_len] |= WORD_INFO_LOOP_JSR; + uint32_t false_target; + if (opcode_info & SH2_OPCODE_INFO_BRANCH_DELAYED) { + false_target = address+2; + } else { + false_target = address+4; + } + const uint32_t target_index = + (false_target - start_address) / 2; + is_local_branch = 1; + local_branch_target_index = target_index; + const uint32_t flag = 1 << (target_index % 32); + local_branch_is_new = !(is_branch_target[index/32] & flag); + is_branch_target[target_index/32] |= flag; + /* We don't set the OPTIMIZE_BRANCH_FALLTHRU variables + * because the branch itself does _not_ fall through; + * this branch target handling is only a hack to avoid + * overoptimization. */ + } + } +#endif + + /* Only record the branch in the lookup table if it's a forward + * branch. We allow backward branches to the beginning of the + * block (index == 0), but we don't add them to the table + * because they're handled specially--we insert a label before + * any precondition checks instead of adding one when we + * translate the instruction itself. */ + if (target > address) { + const uint32_t target_index = (target - start_address) / 2; + if (target_index < lenof(is_branch_target)*32) { + is_local_branch = 1; + local_branch_target_index = target_index; + const uint32_t flag = 1 << (target_index % 32); + if (is_branch_target[target_index/32] & flag) { + /* There's already another branch targeting the + * same instruction. After processing any delay + * slot, we'll need to clear any constants from + * the target's local constant table which differ + * from the current known register values. */ + local_branch_is_new = 0; + } else { + /* This is the first time we've seen this target; + * remember it in case we can fall through. */ +#ifdef OPTIMIZE_BRANCH_FALLTHRU + fallthru_target = target_index; + fallthru_source = block_len; +#endif + local_branch_is_new = 1; + } + is_branch_target[target_index/32] |= flag; + } + } + skip_branch_target_check:; // From branch optimization above + } + + /* If this is a BSR or a JSR to a known target, see whether it can + * be folded into the current block. */ + + if (optimization_flags & SH2_OPTIMIZE_FOLD_SUBROUTINES) { + + /* First check for PC-relative loads so we can track JSR + * target addresses. */ + if ((opcode & 0xB000) == 0x9000) { + const unsigned int n = opcode>>8 & 0xF; + const unsigned int disp = opcode & 0xFF; + if (opcode & 0x4000) { // MOV.L + const unsigned int offset = (address&2 ? 1 : 2) + 2*disp; + local_constant[n] = fetch[offset]<<16 | fetch[offset+1]; + } else { // MOV.W + const unsigned int offset = 2 + disp; + local_constant[n] = fetch[offset]; + } + } else { + if (opcode_info & SH2_OPCODE_INFO_SETS_R0) { + local_constant[0] = 1; + } + if (opcode_info & SH2_OPCODE_INFO_SETS_Rn) { + local_constant[opcode>>8 & 0xF] = 1; + } + if (opcode_info & (SH2_OPCODE_INFO_ACCESS_POSTINC + | SH2_OPCODE_INFO_ACCESS_PREDEC)) { + if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rm) { + local_constant[opcode>>4 & 0xF] = 1; + } + if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rn) { + local_constant[opcode>>8 & 0xF] = 1; + } + if (opcode_info & SH2_OPCODE_INFO_ACCESSES_R15) { + local_constant[15] = 1; + } + } + } + + /* Now see if this is an optimizable call. */ + uint32_t target = 1; // 1 = not a subroutine call or unknown target + if ((opcode & 0xF000) == 0xB000) { // BSR label + int32_t disp = opcode & 0xFFF; + disp <<= 20; // Sign-extend + disp >>= 19; // And double + target = (address + 4) + disp; + } else if ((opcode & 0xF0FF) == 0x400B) { // JSR @Rn + const unsigned int n = opcode>>8 & 0xF; + if (local_constant[n]) { + target = local_constant[n]; + } + } + if (target != 1 + && optimize_fold_subroutine(state, start_address, address, + target, fetch[1], + pointer_map, local_constant, + &subroutine_native[block_len]) + ) { + word_info[block_len] |= WORD_INFO_FOLDABLE; + subroutine_target[block_len] = target; +#ifdef OPTIMIZE_LOOP_REGISTERS + /* Subroutine folding and loop optimization don't currently + * play together well, so disable loop optimization if we + * fold a subroutine into the current block. (FIXME: this + * is a loss if the loop has already ended; better to just + * not fold and break the block instead in that case?) */ + is_loop = 0; + can_optimize_loop = 0; +#endif + } + + } // if (optimization_flags & SH2_OPTIMIZE_FOLD_SUBROUTINES) + + /* Check whether this is an instruction with a delay slot. */ + + delay = ((opcode_info & SH2_OPCODE_INFO_BRANCH_DELAYED) != 0); + + /* Check whether this is an unconditional branch or similar + * instruction; when we get past its delay slot (if applicable), + * we'll check whether to end the block. */ + + at_uncond_branch |= + (((opcode_info & SH2_OPCODE_INFO_BRANCH_UNCOND) + && !(word_info[block_len] & WORD_INFO_FOLDABLE)) + || opcode == 0x001B); // SLEEP + + /* If we have a pending local branch, update the local constant + * table as appropriate. */ + + if (is_local_branch && !delay) { + is_local_branch = 0; + unsigned int reg; + if (local_branch_is_new) { + for (reg = 0; reg < 16; reg++) { + local_constant_per_insn[local_branch_target_index][reg] + = local_constant[reg]; + } + } else { + for (reg = 0; reg < 16; reg++) { + if (local_constant_per_insn[local_branch_target_index][reg] != local_constant[reg]) { + local_constant_per_insn[local_branch_target_index][reg] = 1; + } + } + } + } + + /* If we have a pending branch and we're past its delay slot + * (if applicable), update loop optimization state. */ + +#ifdef OPTIMIZE_LOOP_REGISTERS + at_branch |= + at_uncond_branch || (opcode_info & SH2_OPCODE_INFO_BRANCH_COND); + if (at_branch && !delay) { + at_branch = 0; + if (at_loop_branch) { + final_live_registers = loop_live_registers; + final_load_registers = loop_load_registers; + final_changed_registers = loop_changed_registers; + unsigned int reg; + for (reg = 0; reg < 16; reg++) { + if (pointer_map[reg] != reg) { + loop_invariant_pointers &= ~(1 << reg); + } + } + final_invariant_pointers = loop_invariant_pointers; + at_loop_branch = 0; + } +# if !defined(TRACE) && !defined(TRACE_STEALTH) && !defined(TRACE_LITE) + /* If this is a branch back to the beginning of the loop from + * the middle, followed by more loop body code, then any + * registers first set below this branch will still have the + * proper value on exit from the loop, assuming an exception + * handler interrupting the loop doesn't try to alter or rely + * on the loop's state. But this will cause traces (including + * TRACE_LITE) to report invalid register contents if the + * early branch is taken and stops due to the cycle limit + * before the register has been set at least once, so if we're + * tracing, we make an exception to our "don't change behavior + * with TRACE_STEALTH/TRACE_LITE" rule and set the noload + * register set here. If we're _not_ tracing, we omit this + * "else" and let the noload registers continue to accumulate. */ + else +# endif + if (!loop_noload_registers_set) { + loop_noload_registers = loop_changed_registers; + loop_noload_registers_set = 1; + } + } // if (at_branch && !delay) +#endif // OPTIMIZE_LOOP_REGISTERS + + /* If this is an instruction that loads SR (LDC ...,SR), terminate + * the block after the next instruction (since the SH-2 ignores + * interrupts between LDC and the following instruction--SH7604 + * manual page 76, section 4.6.2) to allow any potentially unmasked + * interrupts to be recognized. While the other LD[CS]/ST[CS] + * instructions also ignore interrupts for the following + * instruction, we don't terminate the block for them anyway, so + * we don't worry about them here. */ + + if ((opcode & 0xF0FF) == 0x4007 || (opcode & 0xF0FF) == 0x400E) { +#ifdef JIT_ACCURATE_LDC_SR_TIMING + if (block_len+1 >= max_len) { + /* We'll hit the block size limit after this instruction, + * so terminate the block _before_ the LDC and include it + * in the next block. If the following instruction is in a + * blacklisted address, we'll end up interpreting the LDC, + * which is fine too. */ + goto abort_scan; + } else { + end_block_next = 1; + } +#else + end_block = 1; +#endif + } + + /* If this instruction is the target of a preceding fall-through + * branch, update the word_info[] and is_branch_target[] tables + * to reflect that. + * + * IMPORTANT: Once this code is executed, it is CRITICAL that this + * instruction be included in the scanned block, or incorrect code + * will result! See the earlier code that sets at_fallthru for an + * explanation. */ + + if (at_fallthru) { +#ifdef JIT_DEBUG_VERBOSE + DMSG("Marking branch at 0x%08X as fall-through", + start_address + saved_fallthru_source*2); +#endif + word_info[saved_fallthru_source] |= WORD_INFO_FALLTHRU; + /* Also clear the branch target flag so we don't set a label + * (which would break the basic unit and cause an optimization + * barrier). */ + const uint32_t flag = 1 << (saved_fallthru_target % 32); + is_branch_target[saved_fallthru_target/32] &= ~flag; + } + + /* If we have a pending unconditional branch and we're past its + * delay slot (if applicable), check whether there are any branch + * targets beyond this address which could be part of the same + * block (i.e. are within the instruction limit as well as + * JIT_MAX_INSN_GAP). If there are, we'll continue translating at + * the first such address; otherwise we'll end the block here. + * (In !JIT_ALLOW_DISTANT_BRANCHES mode, we always stop unless the + * next instruction is a branch target.) */ + + if (at_uncond_branch && !delay && !end_block) { + at_uncond_branch = 0; + index = block_len + 1; + + /* If variable shift optimization is enabled, allow a BRAF + * followed by one or more shift/rotate instructions to fall + * through to the instruction following the shift sequence. */ +#ifdef OPTIMIZE_VARIABLE_SHIFTS + if ((fetch[-1] & 0xF0FF) == 0x0023) { // BRAF + unsigned int next_opcode = fetch[1]; + if ((next_opcode & 0xF0DE) == 0x4000 // SH[LA][LR] + || (next_opcode & 0xF0FE) == 0x4004 // ROT[LR] + ) { + const unsigned int shift_opcode = next_opcode; + do { + address += 2; + fetch++; + block_len++; + next_opcode = fetch[1]; + } while (next_opcode == shift_opcode); + } + } else +#endif + + if (!(is_branch_target[index/32] & (1 << (index % 32)))) { + end_block = 1; +#ifdef JIT_ALLOW_DISTANT_BRANCHES + /* See below for why the "-1" on max_len */ + const uint32_t gap_limit = block_len + JIT_MAX_INSN_GAP; + const uint32_t index_limit = (max_len-1 < gap_limit + ? max_len-1 : gap_limit); + const uint32_t address_limit = + start_address + (index_limit * 2); + const uint32_t first_valid = + (now_in_delay ? address : address+2); + index = (first_valid - start_address) / 2; + /* Search for a non-empty word, then find the first bit set + * in that word */ + uint32_t mask = 0xFFFFFFFF << (index % 32); + index /= 32; + for (; index < lenof(is_branch_target); index++) { + if (is_branch_target[index] & mask) { + break; + } + mask = 0xFFFFFFFF; + } + if (index < lenof(is_branch_target)) { + mask &= is_branch_target[index]; + index *= 32; + while (!(mask & 1)) { + mask >>= 1; + index++; + } + const uint32_t first_target = start_address + index*2; + if (first_target < address_limit) { + int skip_bytes = first_target - (address+2); + if (skip_bytes > 0) { + address += skip_bytes; + fetch += skip_bytes / 2; + block_len += skip_bytes / 2; + } + end_block = 0; + } + } +#endif + } // if next insn is not a branch target + } // if (at_uncond_branch && !delay && !end_block) + + /* Check whether we've hit the instruction limit. To avoid the + * potential for problems arising from an instruction with a delay + * slot crossing the limit, we stop one instruction early; if this + * instruction had a delay slot, the delay slot will still fall + * within the limit. (We could also explicitly check against + * (max_len+delay), but this way is both simpler and marginally + * faster.) However, if we're stopping after the following + * instruction due to an LDC SR, don't break early here. */ + + if (!end_block && !end_block_next && block_len+2 >= max_len) { +#ifdef JIT_DEBUG + DMSG("WARNING: Terminating block 0x%08X after 0x%08X due to" + " instruction limit (%d insns) or blacklist", + start_address, delay ? address+2 : address, max_len); +#endif + end_block = 1; + } + +#ifdef OPTIMIZE_VARIABLE_SHIFTS + + /* If this opcode begins a variable shift instruction sequence, + * skip the remaining instructions in the sequence. (Normally we + * don't alter scanning behavior for optimizations, but since + * variable shift sequences involve branches, we want to avoid + * marking the following instruction as a branch target--thus + * creating an optimization barrier--when the branches will be + * eliminated anyway.) However, we skip sequences starting with + * BRAF, since we handle them separately. */ + + if (!now_in_delay && (opcode & 0xF0FF) != 0x0023) { + unsigned int count_reg, count_max, shift_reg, type; + const uint8_t *cycles; + const unsigned int num_insns = + can_optimize_variable_shift(fetch, address, &count_reg, + &count_max, &shift_reg, &type, + &cycles); + if (num_insns) { + block_len += num_insns - 1; + fetch += num_insns - 1; + address += (num_insns - 1) * 2; + } + } + +#endif + + } // for (; !end_block || delay; address += 2, fetch++, block_len++) + + abort_scan: + + /* Record the final set of entry registers used as pointers. */ + + pointer_used &= 0xFFFF; + +#ifdef OPTIMIZE_LOOP_REGISTERS + /* If this block is a loop, update loop_*_registers with the values + * found at the end of the loop body, so we don't unnecessarily fix + * SH-2 registers that aren't used in the body of the loop; also add + * registers changed after any non-loop branch to the set of registers + * that must be preloaded. Otherwise, there's no point in attempting + * loop optimization, so clear the flag. */ + + if (is_loop) { + loop_live_registers = final_live_registers; + loop_load_registers = final_load_registers; + loop_changed_registers = final_changed_registers; + loop_invariant_pointers = final_invariant_pointers; + if (loop_noload_registers_set) { + loop_load_registers |= + (loop_changed_registers & ~loop_noload_registers); + } + } else { + can_optimize_loop = 0; + } +#endif + + /* All done! */ + return block_len; +} + +/*-----------------------------------------------------------------------*/ + +/** + * optimize_pointers: Check the current instruction to determine whether + * it accesses memory through a pointer register or modifies a register + * assumed to be a pointer. Helper function for scan_block(). + * + * [Parameters] + * start_address: Start address of block, or 1 for subroutine fold checks + * address: Address of current instruction + * opcode: Opcode of current instruction + * opcode_info: Information about current instruction + * pointer_map: pointer_map[] array pointer from scan_block() + * [Return value] + * None + */ +static inline void optimize_pointers( + uint32_t start_address, uint32_t address, unsigned int opcode, + uint32_t opcode_info, uint8_t pointer_map[16]) +{ + const unsigned int index = (address - start_address) / 2; + if (start_address != 1 + && (is_data_pointer_load[index/32] & (1 << (index % 32))) + ) { + + if ((opcode & 0xFF00) == 0xC700) { // MOVA + pointer_map[0] = 30; +#ifdef JIT_DEBUG_VERBOSE + DMSG("%08X marked as data pointer load for R0", address); +#endif + } else { + const unsigned int n = opcode>>8 & 0xF; + pointer_map[n] = 30; +#ifdef JIT_DEBUG + if (!(opcode_info & SH2_OPCODE_INFO_SETS_Rn)) { + DMSG("WARNING: %08X marked as data pointer load but does not" + " set Rn!", address); +# ifdef JIT_DEBUG_VERBOSE + } else { + DMSG("%08X marked as data pointer load for R%u", address, n); +# endif + } +#endif + } + + } else if ((opcode & 0xF00F) == 0x6003) { // MOV Rm,Rn + + const unsigned int n = opcode>>8 & 0xF; + const unsigned int m = opcode>>4 & 0xF; + pointer_map[n] = pointer_map[m]; + + } else if ((opcode & 0xFF00) == 0xC700) { // MOVA @(disp,PC),R0 + + pointer_map[0] = 30; + + } else if ((opcode & 0xF000) == 0x7000) { // ADD #imm,Rn + + /* This only modifies Rn by a small, fixed amount, so assume the + * pointer is still valid and don't clear pointer_map[n]. */ + + } else if ((opcode & 0xF00F) == 0x3008) { // SUB Rm,Rn + + const unsigned int m = opcode>>4 & 0xF; + const unsigned int n = opcode>>8 & 0xF; + if (pointer_map[m] == 31) { + /* If we're subtracting a non-pointer from a pointer, assume + * it's a pointer adjustment that leaves the pointer in Rn + * valid. (If Rn is already not a pointer, we don't need to + * do anything in the first place.) */ + } else { + pointer_map[n] = 31; + } + + } else if ((opcode & 0xF00F) == 0x300C) { // ADD Rm,Rn + + const unsigned int m = opcode>>4 & 0xF; + const unsigned int n = opcode>>8 & 0xF; + if (pointer_map[m] == 31) { + /* Like SUB Rm,Rn, we ignore ADD Rm,Rn where Rm is not a + * pointer on the assumption that it's a pointer adjustment. */ + } else if (pointer_map[n] == 31) { + /* Addition is commutative, unlike subtraction, so we could + * also have the case where Rm is a pointer and Rn is an + * adjustment, leaving a pointer value in Rn. Treat this case + * like MOV Rm,Rn where Rm is a pointer. */ + pointer_map[n] = pointer_map[m]; + } else { + pointer_map[n] = 31; + } + + } else { + + const unsigned int m = opcode>>4 & 0xF; + const unsigned int n = opcode>>8 & 0xF; + + if (opcode_info & SH2_OPCODE_INFO_SETS_R0) { + pointer_map[0] = 31; + } + if (opcode_info & SH2_OPCODE_INFO_ACCESSES_Rm) { + pointer_used |= 1 << pointer_map[m]; + } else if (opcode_info & SH2_OPCODE_INFO_ACCESSES_R0_Rm) { + if (pointer_map[0] == 30 + || (pointer_map[0] != 31 && (pointer_regs & 1<>8 & 0xF; + const int m = opcode>>4 & 0xF; + if (pointer_map[n] == 31 + || (pointer_map[n] != 30 && !(pointer_regs & (1<>8 & 0xF; + const unsigned int Rm = opcode>>4 & 0xF; + /* And other register indices */ + const unsigned int R0 = 0; + const unsigned int R15 = 15; + const unsigned int SR = state_cache_index(offsetof(SH2State,SR)); + const unsigned int GBR = state_cache_index(offsetof(SH2State,GBR)); + const unsigned int VBR = state_cache_index(offsetof(SH2State,VBR)); + const unsigned int MACH= state_cache_index(offsetof(SH2State,MACH)); + const unsigned int MACL= state_cache_index(offsetof(SH2State,MACL)); + const unsigned int PR = state_cache_index(offsetof(SH2State,PR)); + #define SR_GBR_VBR(x) ((x)==0 ? SR : (x)==1 ? GBR : VBR) + #define MACH_MACL_PR(x) ((x)==0 ? MACH : (x)==1 ? MACL : PR) + + if ((opcode & 0xF00F) == 0x0002) { // STC ...,Rn + this_used = 1<> 9) & 3]; +# endif + const uint32_t skip_index = ((target-2) - start_address) / 2; + if (!(is_branch_target[skip_index/32] & (1<<(skip_index%32)))) { + const unsigned int skipped_insn = fetch[(opcode & 0x400) ? 2 : 1]; + if ((skipped_insn & 0xF00F) == 0x6003 // MOV Rm,Rn + || (skipped_insn & 0xF000) == 0x7000 // ADD #imm,Rn + || (skipped_insn & 0xF000) == 0xE000 // MOV #imm,Rn + ) { + word_info[(address - start_address) / 2] |= WORD_INFO_SELECT; +# ifdef JIT_DEBUG_VERBOSE + DMSG("Converted %s to SELECT at %08X", mnemonic, address); +# endif + return 1; +# ifdef JIT_DEBUG_VERBOSE + } else { + DMSG("Failed to convert %s to SELECT at %08X (skipped opcode" + " is %04X)", mnemonic, address, skipped_insn); +# endif + } +# ifdef JIT_DEBUG_VERBOSE + } else { + DMSG("Failed to convert %s to SELECT at %08X (skipped instruction" + " at %08X is a branch target)", mnemonic, address, target-2); +# endif + } + } + return 0; +} + +#endif // OPTIMIZE_BRANCH_SELECT + +/*-----------------------------------------------------------------------*/ + +/** + * optimize_fold_subroutine: Determine whether the code sequence starting + * at the SH-2 address "target" qualifies as a foldable subroutine. If so, + * update the pointer_map[] array with any changes as a result of the + * subroutine. + * + * [Parameters] + * state: Processor state block pointer + * start_address: Start address of block + * address: Address of subroutine call + * target: Target address of subroutine call + * delay_slot: Instruction word in delay slot of subroutine call + * pointer_map: pointer_map[] array pointer from scan_block() + * local_constant: local_constant[] array pointer from scan_block() + * native_ret: Pointer to variable to receive native implementation + * function address if the routine is to be folded + * using a native implementation, NULL if the routine + * is to be folded by translating instructions + * [Return value] + * Nonzero if subroutine is foldable, else zero + */ +static int optimize_fold_subroutine( + SH2State *state, const uint32_t start_address, uint32_t address, + const uint32_t target, const uint16_t delay_slot, uint8_t pointer_map[16], + uint32_t local_constant[16], SH2NativeFunctionPointer *native_ret) +{ + const uint16_t *fetch_base = (const uint16_t *)fetch_pages[target >> 19]; + if (fetch_base == NULL) { + return 0; // Unfetchable, therefore unoptimizable + } + const uint16_t *fetch = + (const uint16_t *)((uintptr_t)fetch_base + target); + const uint16_t * const start = fetch; + const uint16_t * const limit = + fetch + OPTIMIZE_FOLD_SUBROUTINES_MAX_LENGTH + 2; + + /* First see if there's a native implementation which we can call. */ + + if (manual_optimization_callback) { + ignore_optimization_hints = 1; + const unsigned int hand_tuned_len = + (*manual_optimization_callback)(state, target, fetch, + native_ret, 1); + ignore_optimization_hints = 0; + if (hand_tuned_len > 0) { + if (*native_ret == NULL) { + return 0; // Folding was refused by the callback. + } +#ifdef JIT_DEBUG_VERBOSE + DMSG("Using hand-tuned code to fold subroutine at 0x%08X", target); +#endif + /* We assume all of R0-R7 have been destroyed. */ + unsigned int reg; + for (reg = 0; reg < 8; reg++) { + pointer_map[reg] = 31; + local_constant[reg] = 1; + } + return 1; + } + } + + /* Make a local copy of optimization state data, so we can revert if + * we discover that the subroutine can't be optimized. */ + + uint8_t saved_pointer_map[16]; + memcpy(saved_pointer_map, pointer_map, 16); + const uint16_t saved_pointer_used = pointer_used; + + /* Scan the delay slot, then each instruction in the subroutine. + * Branches (or similar instructions) are not permitted in delay + * slots, so we assume the instruction is a simple one. */ + + if (optimization_flags & SH2_OPTIMIZE_POINTERS) { + optimize_pointers(start_address, address+2, delay_slot, + get_opcode_info(delay_slot), pointer_map); + } + + for (address = target; + !(fetch >= start+2 && fetch[-2] == 0x000B); + address += 2, fetch++ + ) { + if (fetch > limit) { + goto fail; // Subroutine is too long + } + const uint16_t opcode = *fetch; + const uint32_t opcode_info = get_opcode_info(opcode); + if (opcode == 0x000B) { // RTS + /* Ignore; we'll catch it after the delay slot */ + } else if (opcode_info & (SH2_OPCODE_INFO_BRANCH_UNCOND + | SH2_OPCODE_INFO_BRANCH_COND)) { + goto fail; // Branches break optimization + } else if ((optimization_flags & SH2_OPTIMIZE_POINTERS_MAC) + && (opcode & 0xB00F) == 0x000F) { // MAC.[WL] + const int n = opcode>>8 & 0xF; + const int m = opcode>>4 & 0xF; + if (pointer_map[n] == 31 + || (pointer_map[n] != 30 && !(pointer_regs & (1<running = 1; + +#ifdef JIT_PROFILE + const uint32_t start_cycles = state->cycles; +#endif + +#ifdef JIT_DEBUG_INTERPRET_RTL + +# ifdef JIT_PROFILE + entry->exec_time += +# endif + rtl_execute_block(entry->rtl, state); + +#else // !JIT_DEBUG_INTERPRET_RTL + + const void (*native_code)(SH2State *state) = entry->native_code; + +# ifdef JIT_PROFILE + /* For systems that have an efficient way to measure execution time + * (such as a performance counter register), we could use that to + * update entry->exec_time. On the PSP, the MIPS Count register is + * only accessible in kernel mode, so we're stuck with using a syscall + * to a kernel routine that costs hundreds of cycles (where a short + * block of code may finish in as few as 10 cycles), and we can't even + * get a cycle-accurate count. Oh well... it's better than nothing. */ +# if defined(PSP) + const uint32_t start = sceKernelGetSystemTimeLow(); +# endif +# endif + (*native_code)(state); +# ifdef JIT_PROFILE +# if defined(PSP) + entry->exec_time += sceKernelGetSystemTimeLow() - start; +# else + entry->exec_time++; // Default is to just count each call as 1 time unit +# endif +# endif + +#endif // JIT_DEBUG_INTERPRET_RTL + +#ifdef JIT_PROFILE + entry->call_count++; + entry->cycle_count += state->cycles - start_cycles; +#endif + + entry->running = 0; +} + +/*************************************************************************/ + +/** + * decode_insn: Decode a single SH-2 instruction. Implements + * translate_insn() using the shared decoder core. + * + * [Parameters] + * fetch: Pointer to instruction to decode + * state: SH-2 processor state + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * recursing: Nonzero if this is a recursive decode, else zero + * is_last: Nonzero if this is the last instruction of a recursive + * decode, else zero + * [Return value] + * Decoded SH-2 opcode (nonzero) on success, zero on error + * [Notes] + * Since the opcode 0x0000 is invalid and scan_block() prevents invalid + * opcodes from being included in a block to be translated, the return + * value of this routine will always be nonzero on success. + */ + +#define DECODE_INSN_INLINE inline +#define DECODE_INSN_PARAMS \ + const uint16_t *fetch, SH2State *state, JitEntry *entry, \ + const unsigned int state_reg, const int recursing, const int is_last + +#ifdef TRACE +# define TRACE_WRITEBACK_FOR_RECURSIVE_DECODE \ + ASSERT(writeback_state_cache(entry, state_reg, 1)) +#else +# define TRACE_WRITEBACK_FOR_RECURSIVE_DECODE /*nothing*/ +#endif +#define RECURSIVE_DECODE(address,is_last) do { \ + const uint32_t saved_PC = jit_PC; \ + jit_PC = (address); \ + if (UNLIKELY(!translate_insn(state, entry, state_reg, 1, (is_last)))) { \ + return 0; \ + } \ + TRACE_WRITEBACK_FOR_RECURSIVE_DECODE; \ + jit_PC = saved_PC; \ +} while (0) + +#include "sh2-core.i" + +/*-----------------------------------------------------------------------*/ + +/** + * translate_insn: Translate a single SH-2 instruction at the current + * translation address into one or more equivalent RTL instructions. + * + * [Parameters] + * state: SH-2 processor state + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * recursing: Nonzero if this is a recursive decode (for inserting + * copies of instructions from other locations), else zero + * is_last: Nonzero if this is the last instruction in a recursively + * decoded sequence, else zero + * [Return value] + * Nonzero on success, zero on error + */ +static int translate_insn(SH2State *state, JitEntry *entry, + unsigned int state_reg, int recursing, int is_last) +{ + /* Get a fetch pointer for the current PC */ + const uint16_t *fetch_base = (const uint16_t *)fetch_pages[cur_PC >> 19]; + PRECOND(fetch_base != NULL, return 0); + const uint16_t *fetch = (const uint16_t *)((uintptr_t)fetch_base + cur_PC); + + /* Process the instruction */ + const unsigned int opcode = + decode_insn(fetch, state, entry, state_reg, recursing, is_last); + if (UNLIKELY(!opcode)) { + return 0; + } + + return 1; +} + +/*************************************************************************/ + +#ifdef JIT_BRANCH_PREDICT_STATIC + +/** + * add_static_branch_terminator: Add a terminator for a static branch when + * using static branch prediction. Code will be added to retrieve or look + * up the target address and branch to it if possible. + * + * [Parameters] + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * address: Branch target address (in SH-2 address space) + * index: entry->static_predict[] index to use for static prediction + * [Return value] + * Nonzero on success, zero on failure + */ +static int add_static_branch_terminator(JitEntry *entry, + unsigned int state_reg, + uint32_t address, unsigned int index) +{ + CREATE_LABEL(label_not_predicted); + CREATE_LABEL(label_return); + + /* Load the address of the BranchTargetInfo we're using */ + DEFINE_REG(bti); + MOVEA(bti, &entry->static_predict[index]); + + /* Load relevant values early to mitigate load stalls (very MIPSy) */ + DEFINE_REG(predicted_entry); + LOAD_PTR(predicted_entry, bti, offsetof(BranchTargetInfo,entry)); + DECLARE_REG(cycles); + LOAD_STATE_ALLOC(cycles, cycles); + DECLARE_REG(cycle_limit); + LOAD_STATE_ALLOC(cycle_limit, cycle_limit); + DEFINE_REG(native_target); + LOAD_PTR(native_target, bti, offsetof(BranchTargetInfo,native_target)); + DEFINE_REG(can_continue); + SLTS(can_continue, cycles, cycle_limit); + + /* If we already have a prediction, jump to it (or return if we're out + * of cycles) */ + GOTO_IF_Z(label_not_predicted, predicted_entry); + STORE_STATE_PTR(current_entry, predicted_entry); + GOTO_IF_Z(label_return, can_continue); + RETURN_TO(native_target); + + /* Look up the translated block for this address, then return. For + * simplicity (and to keep code size down), we don't try too hard to + * optimize this case; we'll make up the time in future runs */ + DEFINE_LABEL(label_not_predicted); + DEFINE_REG(addr_reg); + MOVEI(addr_reg, address); + DEFINE_REG(ptr_update_branch_target); + MOVEA(ptr_update_branch_target, update_branch_target); + DEFINE_REG(new_entry); + CALL(new_entry, bti, addr_reg, ptr_update_branch_target); + STORE_STATE_PTR(current_entry, new_entry); + GOTO_IF_Z(label_return, new_entry); + DEFINE_REG(new_target); +#ifdef JIT_DEBUG_INTERPRET_RTL + LOAD_PTR(new_target, new_entry, offsetof(JitEntry,rtl)); +#else + LOAD_PTR(new_target, new_entry, offsetof(JitEntry,native_code)); +#endif + STORE_PTR(bti, new_target, offsetof(BranchTargetInfo,native_target)); + + DEFINE_LABEL(label_return); + RETURN(); + + clear_state_cache(0); + return 1; +} + +#endif // JIT_BRANCH_PREDICT_STATIC + +/*************************************************************************/ + +#if JIT_BRANCH_PREDICTION_SLOTS > 0 + +/** + * update_branch_target: Find the translated block, if any, corresponding + * to the given address and update the given branch target structure with + * the found block. + * + * [Parameters] + * bti: BranchTargetInfo structure to update + * address: Branch target address + * [Return value] + * Translated block found, or NULL if none was found + */ +static FASTCALL JitEntry *update_branch_target(BranchTargetInfo *bti, + uint32_t address) +{ + JitEntry *new_entry = jit_find(address); + if (new_entry) { + if (bti->entry) { + bti->next->prev = bti->prev; + bti->prev->next = bti->next; + } + bti->target = address; + bti->entry = new_entry; + bti->next = new_entry->pred_ref_head.next; + bti->prev = &new_entry->pred_ref_head; + bti->next->prev = bti; + bti->prev->next = bti; + } + return new_entry; +} + +#endif // JIT_BRANCH_PREDICTION_SLOTS > 0 + +/*************************************************************************/ +/************************ Other utility routines *************************/ +/*************************************************************************/ + +/** + * btcache_lookup: Search the branch target cache for the given SH-2 + * address. + * + * [Parameters] + * address: SH-2 address to search for + * [Return value] + * Corresponding RTL label, or zero if the address could not be found + */ +static uint32_t btcache_lookup(uint32_t address) +{ + /* Search backwards from the current instruction so we can handle short + * loops quickly; note that btcache_index is now pointing to where the + * _next_ instruction will go */ + const int current = (btcache_index + (lenof(btcache)-1)) % lenof(btcache); + int index = current; + do { + if (btcache[index].sh2_address == address) { + return btcache[index].rtl_label; + } + index--; + if (UNLIKELY(index < 0)) { + index = lenof(btcache) - 1; + } + } while (index != current); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * record_unresolved_branch: Record the given branch target and native + * offset in an empty slot in the unresolved branch table. If there are + * no empty slots, purge the oldest (lowest native offset) entry. + * + * [Parameters] + * entry: Block being translated + * sh2_target: Branch target address in SH2 address space + * target_label: RTL label number to be defined at branch target + * [Return value] + * Nonzero on success, zero on error + */ +static int record_unresolved_branch(const JitEntry *entry, uint32_t sh2_target, + unsigned int target_label) +{ + PRECOND(entry != NULL, return 0); + + int oldest = 0; + int i; + for (i = 0; i < lenof(unres_branches); i++) { + if (unres_branches[i].sh2_target == 0) { + oldest = i; + break; + } else if (unres_branches[i].target_label + < unres_branches[oldest].target_label) { + oldest = i; + } + } + if (UNLIKELY(unres_branches[oldest].sh2_target != 0)) { +#ifdef JIT_DEBUG + DMSG("WARNING: Unresolved branch table full, dropping branch to PC" + " 0x%08X (RTL label %u)", unres_branches[oldest].sh2_target, + unres_branches[oldest].target_label); +#endif + /* Add a JUMP fallback for this entry in the middle of the code + * stream, since we have nowhere else to put it; use a temporary + * label to branch past it in the normal code flow */ + DEFINE_REG(temp_reg); + CREATE_LABEL(temp_label); + GOTO_LABEL(temp_label); + DEFINE_LABEL(unres_branches[oldest].target_label); + RETURN(); + DEFINE_LABEL(temp_label); + } + unres_branches[oldest].sh2_target = sh2_target; + unres_branches[oldest].target_label = target_label; + return 1; +} + +/*************************************************************************/ + +/** + * writeback_state_cache: Write all cached state block values to memory. + * Does nothing (and returns success) if OPTIMIZE_STATE_BLOCK is not + * defined. + * + * [Parameters] + * entry: Block being translated + * state_reg: RTL register holding state block pointer + * flush_fixed: Nonzero to flush dirty fixed-register fields to memory + * [Return value] + * Nonzero on success, zero on error + */ +static int writeback_state_cache(const JitEntry *entry, unsigned int state_reg, + int flush_fixed) +{ + PRECOND(entry != NULL, return 0); + +#ifdef OPTIMIZE_STATE_BLOCK + + if (dirty_SR_T) { + const int index_SR = state_cache_index(offsetof(SH2State,SR)); + DECLARE_REG(old_SR); + if (state_cache[index_SR].rtlreg) { + old_SR = state_cache[index_SR].rtlreg; + } else { + ALLOC_REG(old_SR); + LOAD_W(old_SR, state_reg, offsetof(SH2State,SR)); + } + DECLARE_REG(new_SR); + if (state_cache[index_SR].fixed) { + new_SR = old_SR; + } else { + ALLOC_REG(new_SR); + } + BFINS(new_SR, old_SR, cached_SR_T, SR_T_SHIFT, 1); + state_cache[index_SR].rtlreg = new_SR; + state_dirty |= 1 << index_SR; + dirty_SR_T = 0; + } + + unsigned int index; + for (index = 0; index < lenof(state_cache); index++) { + if ((state_dirty & (1 << index)) + || (flush_fixed && state_cache[index].flush) + ) { + DECLARE_REG(rtlreg); + if (index == state_cache_index(offsetof(SH2State,PC)) + && cached_PC != 0 + ) { + ALLOC_REG(rtlreg); + MOVEI(rtlreg, cached_PC); + cached_PC = 0; + state_cache[index].rtlreg = rtlreg; + state_cache[index].offset = 0; + } else if (state_cache[index].offset != 0) { + if (state_cache[index].fixed) { + rtlreg = state_cache[index].rtlreg; + } else { + ALLOC_REG(rtlreg); + } + ADDI(rtlreg, state_cache[index].rtlreg, + state_cache[index].offset); + state_cache[index].rtlreg = rtlreg; + if (index < 16) { + pointer_status[index].rtl_ptrreg = 0; + } + if (index == 15 && stack_pointer) { + ADDI(stack_pointer, stack_pointer, + state_cache[index].offset); + } + state_cache[index].offset = 0; + } else { + rtlreg = state_cache[index].rtlreg; + } + if (!state_cache[index].fixed || flush_fixed) { + STORE_W(state_reg, rtlreg, state_cache_field(index)); + state_dirty &= ~(1 << index); + if (index == state_cache_index(offsetof(SH2State,PC))) { + stored_PC = 0; + } +# ifdef TRACE_STEALTH + if (index < 23) { + APPEND(NOP, 0, 0xB0000000 | index<<16, 0, 0); + } +# endif + } + } + } + +#endif // OPTIMIZE_STATE_BLOCK + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * clear_state_cache: Clear all cached state block registers. Does + * nothing if OPTIMIZE_STATE_BLOCK is not defined. + * + * [Parameters] + * clear_fixed: Nonzero to clear fixed registers from the cache as well, + * zero to leave them in the cache + * [Return value] + * None + */ +static void clear_state_cache(int clear_fixed) +{ +#ifdef OPTIMIZE_STATE_BLOCK + if (clear_fixed) { +# ifdef JIT_DEBUG + if (UNLIKELY(state_dirty) || UNLIKELY(dirty_SR_T)) { + DMSG("WARNING: Clearing dirty state fields! (dirty=%08X SR_T=%d)", + state_dirty, dirty_SR_T); + } +# endif + memset(state_cache, 0, sizeof(state_cache)); + state_dirty = 0; + } else { + unsigned int index; +# ifdef JIT_DEBUG + uint32_t nonfixed = 0; + for (index = 0; index < lenof(state_cache); index++) { + if (!state_cache[index].fixed) { + nonfixed |= 1<>3 & 7) >= 5 ? 0 : (CMDCOLR&0x7FF)<<16) \ + | ((CMDPMOD>>3 & 15) << 27) | 0x80000000) +#define HASHKEY_TEXTURE_TILE(tilesize,address,pixfmt,color_base,color_ofs,transparent) \ + (tilesize>>4 | (address>>3 & ~1) \ + | (pixfmt >= 3 ? 0 : ((color_ofs|color_base)&0x7FF)<<16) \ + | (pixfmt<<27) | (transparent<<30)) +#define HASH_TEXTURE(key) ((key) % TEXCACHE_HASH_SIZE) + +/*************************************************************************/ + +/* Local routine declarations */ + +static TexInfo *alloc_texture(uint32_t key, uint32_t hash, int persistent); +static TexInfo *find_texture(uint32_t key, unsigned int *hash_ret); +static void load_texture(const TexInfo *tex); +static void *alloc_pixdata(uint32_t size); +static void *alloc_vram(uint32_t size); + +/* Force GCC to inline the texture/CLUT caching functions, to save the + * expense of register save/restore and optimize constant cases + * (particularly for tile loading). */ + +__attribute__((always_inline)) static inline int cache_sprite( + TexInfo *tex, uint16_t CMDSRCA, uint16_t CMDPMOD, uint16_t CMDCOLR, + uint16_t pixel_mask, unsigned int width, unsigned int height, int rofs, + int gofs, int bofs, int persistent); +__attribute__((always_inline)) static inline int cache_tile( + TexInfo *tex, uint32_t address, unsigned int width, unsigned int height, + unsigned int stride, int array, int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, int rofs, int gofs, int bofs, + int persistent); + +__attribute__((always_inline)) static inline int alloc_texture_pixels( + TexInfo *tex, unsigned int width, unsigned int height, unsigned int psm, + int persistent); + +__attribute__((always_inline)) static inline int gen_clut( + TexInfo *tex, unsigned int size, uint32_t color_base, uint8_t color_ofs, + int rofs, int gofs, int bofs, int transparent, int endcodes, + int can_shadow, int persistent); +__attribute__((always_inline)) static inline int gen_clut_t4ind( + TexInfo *tex, const uint8_t *color_lut, uint16_t pixel_mask, + int rofs, int gofs, int bofs, int transparent, int endcodes, + int persistent); + +__attribute__((always_inline)) static inline int cache_texture_t4( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride); +__attribute__((always_inline)) static inline int cache_texture_t8( + TexInfo *tex, const uint8_t *src, uint8_t pixmask, + unsigned int width, unsigned int height, unsigned int stride); +__attribute__((always_inline)) static inline int cache_texture_t16( + TexInfo *tex, const uint8_t *src, uint32_t color_base, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent); +__attribute__((always_inline)) static inline int cache_texture_16( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent); +__attribute__((always_inline)) static inline int cache_texture_32( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent); + +/*************************************************************************/ +/************************** Interface routines ***************************/ +/*************************************************************************/ + +/** + * texcache_reset: Reset the texture cache, including all persistent + * textures. + * + * [Parameters] + * None + * [Return value] + * None + */ +void texcache_reset(void) +{ + memset(tex_table_persistent, 0, sizeof(tex_table_persistent)); + memset(tex_table_transient, 0, sizeof(tex_table_transient)); + tex_buffer_nextfree_persistent = 0; + tex_buffer_nextfree_transient = lenof(tex_buffer) - 1; + pixdata_cache_next_alloc = 0; + + unsigned int i; + for (i = 0; i < lenof(clut_cache); i++) { + unsigned int j; + for (j = 0; j < lenof(clut_cache[i]); j++) { + clut_cache[i][j].size = 0; + } + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * texcache_clean: Clean all transient textures from the texture cache. + * Persistent textures are not affected. + * + * [Parameters] + * None + * [Return value] + * None + */ +void texcache_clean(void) +{ + memset(tex_table_transient, 0, sizeof(tex_table_transient)); + tex_buffer_nextfree_transient = lenof(tex_buffer) - 1; + + unsigned int i; + for (i = 0; i < lenof(clut_cache); i++) { + unsigned int j; + for (j = 0; j < lenof(clut_cache[i]); j++) { + clut_cache[i][j].size = 0; + } + } +} + +/*************************************************************************/ + +/** + * texcache_cache_sprite: Cache the given sprite texture if it has not + * been cached already. Returns a texture key for later use in loading the + * texture. The texture width must be a multiple of 8. + * + * [Parameters] + * cmd: VDP1 command structure + * pixel_mask: Mask to apply to paletted pixel data + * width, height: Size of texture (in pixels) + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this frame only + * [Return value] + * Texture key (zero on error) + */ +uint32_t texcache_cache_sprite(const vdp1cmd_struct *cmd, + uint16_t pixel_mask, + unsigned int width, unsigned int height, + int persistent) +{ + const uint32_t tex_key = + HASHKEY_TEXTURE_SPRITE(cmd->CMDSRCA, cmd->CMDPMOD, cmd->CMDCOLR); + uint32_t tex_hash; + TexInfo *tex = find_texture(tex_key, &tex_hash); + + if (tex) { + /* Already cached, so just generate a CLUT if needed and return the + * texture key. */ + if (tex->clut_dynamic) { + const int transparent = !(cmd->CMDPMOD & 0x40); + const int endcodes = !(cmd->CMDPMOD & 0x80); + const uint32_t color_base = (Vdp2Regs->CRAOFB & 0x0070) << 4; + switch (cmd->CMDPMOD>>3 & 7) { + case 0: + gen_clut(tex, 16, (color_base + cmd->CMDCOLR) & 0x7F0, + cmd->CMDCOLR & 0x00F, vdp1_rofs, vdp1_gofs, vdp1_bofs, + transparent, endcodes, + ((cmd->CMDCOLR | 0xF) & pixel_mask) == pixel_mask, 0); + break; + case 1: + gen_clut_t4ind(tex, Vdp1Ram + (cmd->CMDCOLR << 3), pixel_mask, + vdp1_rofs, vdp1_gofs, vdp1_bofs, + transparent, endcodes, 0); + break; + case 2: + gen_clut(tex, 64, (color_base + cmd->CMDCOLR) & 0x7C0, + cmd->CMDCOLR & 0x03F, vdp1_rofs, vdp1_gofs, vdp1_bofs, + transparent, endcodes, + ((cmd->CMDCOLR | 0x3F) & pixel_mask) == pixel_mask, 0); + break; + case 3: + gen_clut(tex, 128, + (color_base + cmd->CMDCOLR) & 0x780, + cmd->CMDCOLR & 0x07F, vdp1_rofs, vdp1_gofs, vdp1_bofs, + transparent, endcodes, + ((cmd->CMDCOLR | 0x7F) & pixel_mask) == pixel_mask, 0); + break; + case 4: + gen_clut(tex, 256, (color_base + cmd->CMDCOLR) & 0x700, + cmd->CMDCOLR & 0x0FF, vdp1_rofs, vdp1_gofs, vdp1_bofs, + transparent, endcodes, + ((cmd->CMDCOLR | 0xFF) & pixel_mask) == pixel_mask, 0); + break; + } + } + return tex_key; + } // if (tex) + + tex = alloc_texture(tex_key, tex_hash, persistent); + if (UNLIKELY(!tex)) { + DMSG("Texture buffer full, can't cache"); + return 0; + } + if (UNLIKELY(!cache_sprite(tex, cmd->CMDSRCA, cmd->CMDPMOD, cmd->CMDCOLR, + pixel_mask, width, height, + vdp1_rofs, vdp1_gofs, vdp1_bofs, persistent))) { + if (persistent) { + tex_table_persistent[tex_hash] = tex->next; + tex_buffer_nextfree_persistent--; + return texcache_cache_sprite(cmd, pixel_mask, width, height, 0); + } else { + return 0; + } + } + + return tex_key; +} + +/*-----------------------------------------------------------------------*/ + +/** + * texcache_load_sprite: Load the sprite texture indicated by the given + * texture key. + * + * [Parameters] + * key: Texture key returned by texcache_cache_sprite + * [Return value] + * None + */ +void texcache_load_sprite(uint32_t key) +{ + TexInfo *tex = find_texture(key, NULL); + if (UNLIKELY(!tex)) { + DMSG("No texture found for key %08X", (int)key); + return; + } + + load_texture(tex); +} + +/*-----------------------------------------------------------------------*/ + +/** + * texcache_load_tile: Load the specified 8x8 or 16x16 tile texture into + * the GE registers for drawing, first caching the texture if necessary. + * + * [Parameters] + * tilesize: Pixel size (width and height) of tile, either 8 or 16 + * address: Tile data address within VDP2 RAM + * pixfmt: Tile pixel format + * transparent: Nonzero if index 0 or alpha 0 should be transparent + * color_base: Color table base (for indexed formats) + * color_ofs: Color table offset (for indexed formats) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this + * frame only + * [Return value] + * None + */ +void texcache_load_tile(int tilesize, uint32_t address, + int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, + int rofs, int gofs, int bofs, int persistent) +{ + const uint32_t tex_key = + HASHKEY_TEXTURE_TILE(tilesize, address, pixfmt, color_base, + color_ofs, transparent); + uint32_t tex_hash; + TexInfo *tex = find_texture(tex_key, &tex_hash); + + if (tex) { + if (tex->clut_dynamic) { + if (pixfmt == 0) { + gen_clut(tex, 16, + (color_base + color_ofs) & 0x7F0, color_ofs & 0x00F, + rofs, gofs, bofs, transparent, 0, 0, 0); + } else { // pixfmt == 1 + gen_clut(tex, 256, + (color_base + color_ofs) & 0x700, color_ofs & 0x0FF, + rofs, gofs, bofs, transparent, 0, 0, 0); + } + } + } else { // !tex + tex = alloc_texture(tex_key, tex_hash, persistent); + if (UNLIKELY(!tex)) { + DMSG("Texture buffer full, can't cache"); + return; + } + int ok; + if (tilesize == 16) { + ok = cache_tile(tex, address, 16, 16, 8, 2, pixfmt, + transparent, color_base, color_ofs, + rofs, gofs, bofs, persistent); + } else { + ok = cache_tile(tex, address, 8, 8, 8, 1, pixfmt, + transparent, color_base, color_ofs, + rofs, gofs, bofs, persistent); + } + if (UNLIKELY(!ok)) { + if (persistent) { + tex_table_persistent[tex_hash] = tex->next; + tex_buffer_nextfree_persistent--; + return texcache_load_tile(tilesize, address, pixfmt, + transparent, color_base, color_ofs, + rofs, gofs, bofs, 0); + } else { + return; + } + } + } // if (tex) + + /* Load the texture data (and color table, if appropriate). */ + load_texture(tex); +} + +/*-----------------------------------------------------------------------*/ + +/** + * texcache_load_bitmap: Load the specified bitmap texture into the GE + * registers for drawing, first caching the texture if necessary. The + * texture width must be a multiple of 8. + * + * [Parameters] + * address: Bitmap data address within VDP2 RAM + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * pixfmt: Bitmap pixel format + * transparent: Nonzero if index 0 or alpha 0 should be transparent + * color_base: Color table base (for indexed formats) + * color_ofs: Color table offset (for indexed formats) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this + * frame only + * [Return value] + * None + */ +void texcache_load_bitmap(uint32_t address, unsigned int width, + unsigned int height, unsigned int stride, + int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, + int rofs, int gofs, int bofs, int persistent) +{ + const uint32_t tex_key = + HASHKEY_TEXTURE_TILE(8, address, pixfmt, color_base, + color_ofs, transparent); + uint32_t tex_hash; + TexInfo *tex = find_texture(tex_key, &tex_hash); + + if (tex) { + if (tex->clut_dynamic) { + if (pixfmt == 0) { + gen_clut(tex, 16, + (color_base + color_ofs) & 0x7F0, color_ofs & 0x00F, + rofs, gofs, bofs, transparent, 0, 0, 0); + } else { // pixfmt == 1 + gen_clut(tex, 256, + (color_base + color_ofs) & 0x700, color_ofs & 0x0FF, + rofs, gofs, bofs, transparent, 0, 0, 0); + } + } + } else { // !tex + tex = alloc_texture(tex_key, tex_hash, persistent); + if (UNLIKELY(!tex)) { + DMSG("Texture buffer full, can't cache"); + return; + } + if (UNLIKELY(!cache_tile(tex, address, width, height, stride, 1, + pixfmt, transparent, color_base, color_ofs, + rofs, gofs, bofs, persistent))) { + if (persistent) { + tex_table_persistent[tex_hash] = tex->next; + tex_buffer_nextfree_persistent--; + return texcache_load_bitmap(address, width, height, stride, + pixfmt, transparent, color_base, + color_ofs, rofs, gofs, bofs, 0); + } else { + return; + } + } + } // if (!tex) + + /* Load the texture data (and color table, if appropriate). */ + load_texture(tex); +} + +/*************************************************************************/ +/**************************** Local routines *****************************/ +/*************************************************************************/ + +/** + * alloc_texture: Allocate a new texture entry and insert it into the hash + * table. The entry's data fields (other than those for hash table + * management) are _not_ initialized. + * + * [Parameters] + * key: Hash key for texture + * hash: Hash index for texture (== HASH_TEXTURE(key), passed in + * separately so precomputed values can be reused) + * persistent: Nonzero if a persistent texture, zero if a transient texture + * [Return value] + * Allocated texture entry, or NULL on failure (table full) + */ +static TexInfo *alloc_texture(uint32_t key, uint32_t hash, int persistent) +{ + if (persistent) { + if (UNLIKELY(tex_buffer_nextfree_persistent > tex_buffer_nextfree_transient)) { + return NULL; + } + TexInfo *tex = &tex_buffer[tex_buffer_nextfree_persistent++]; + tex->next = tex_table_persistent[hash]; + tex_table_persistent[hash] = tex; + tex->key = key; + return tex; + } else { + if (UNLIKELY(tex_buffer_nextfree_transient < tex_buffer_nextfree_persistent)) { + return NULL; + } + TexInfo *tex = &tex_buffer[tex_buffer_nextfree_transient--]; + tex->next = tex_table_transient[hash]; + tex_table_transient[hash] = tex; + tex->key = key; + return tex; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * find_texture: Return the texture corresponding to the given texture + * key, or NULL if no such texture is cached. + * + * [Parameters] + * key: Texture hash key + * hash_ret: Pointer to variable to receive hash index, or NULL if unneeded + * [Return value] + * Cached texture, or NULL if none + */ +static TexInfo *find_texture(uint32_t key, unsigned int *hash_ret) +{ + const uint32_t hash = HASH_TEXTURE(key); + if (hash_ret) { + *hash_ret = hash; + } + + TexInfo *tex; + for (tex = tex_table_persistent[hash]; tex != NULL; tex = tex->next) { + if (tex->key == key) { + return tex; + } + } + for (tex = tex_table_transient[hash]; tex != NULL; tex = tex->next) { + if (tex->key == key) { + return tex; + } + } + return NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * load_texture: Load the given texture into the GE texture registers. + * + * [Parameters] + * tex: Texture to load + * [Return value] + * None + */ +static void load_texture(const TexInfo *tex) +{ + if (tex->clut_address) { + guClutMode(GU_PSM_8888, 0, tex->clut_size-1, 0); + guClutLoad(tex->clut_size/8, tex->clut_address); + } + guTexFlush(); + guTexMode(tex->pixfmt, 0, 0, /*swizzled*/ 1); + guTexImage(0, tex->width, tex->height, tex->stride, tex->vram_address); +} + +/*************************************************************************/ + +/** + * alloc_pixdata: Allocate memory from the persistent pixel data cache + * buffer, returning a pointer to the area in the uncached address region + * (0x4nnnnnnn). + * + * [Parameters] + * size: Amount of memory to allocate, in bytes + * [Return value] + * Pointer to allocated memory, or NULL on failure (out of memory) + */ +static void *alloc_pixdata(uint32_t size) +{ + if (UNLIKELY(pixdata_cache_next_alloc + size > sizeof(pixdata_cache))) { + return NULL; + } + void *ptr = &pixdata_cache[pixdata_cache_next_alloc]; + pixdata_cache_next_alloc += (size + 63) & -64; + return (void *)((uint32_t)ptr | 0x40000000); +} + +/*-----------------------------------------------------------------------*/ + +/** + * alloc_vram: Allocate memory from the spare VRAM area, returning a + * pointer to the area in the uncached address region (0x4nnnnnnn). + * + * [Parameters] + * size: Amount of memory to allocate, in bytes + * [Return value] + * Pointer to allocated memory, or NULL on failure (out of memory) + */ +static void *alloc_vram(uint32_t size) +{ + void *ptr = display_alloc_vram(size); + if (UNLIKELY(!ptr)) { + return NULL; + } + return (void *)((uint32_t)ptr | 0x40000000); +} + +/*************************************************************************/ + +/** + * cache_sprite: Cache a texture from sprite (VDP1) memory. Implements + * caching code for texcache_cache_sprite(). + * + * [Parameters] + * tex: TexInfo structure for caching texture + * CMDSRCA, CMDPMOD, CMDCOLR: Values of like-named fields in VDP1 command + * pixel_mask: Mask to apply to paletted pixel data + * width, height: Size of texture (in pixels) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero if a persistent texture, zero if a + * transient texture + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_sprite( + TexInfo *tex, uint16_t CMDSRCA, uint16_t CMDPMOD, uint16_t CMDCOLR, + uint16_t pixel_mask, unsigned int width, unsigned int height, int rofs, + int gofs, int bofs, int persistent) +{ + const uint32_t address = CMDSRCA << 3; + const int pixfmt = CMDPMOD>>3 & 7; + const int transparent = !(CMDPMOD & 0x40); + const int endcodes = !(CMDPMOD & 0x80); + uint32_t color_base = (Vdp2Regs->CRAOFB & 0x0070) << 4; + + if (UNLIKELY(address + (pixfmt<=1 ? width/2 : pixfmt<=4 ? width : width*2) * height > 0x80000)) { + DMSG("%dx%d texture at 0x%X extends past end of VDP1 RAM" + " and will be incorrectly drawn", width, height, address); + } + + const uint8_t *src = Vdp1Ram + address; + switch (pixfmt) { + case 0: + tex->clut_dynamic = persistent; + CMDCOLR &= pixel_mask; + return gen_clut(tex, 16, + (color_base + CMDCOLR) & 0x7F0, CMDCOLR & 0x00F, + rofs, gofs, bofs, transparent, endcodes, + ((CMDCOLR | 0xF) & pixel_mask) == pixel_mask, 0) + && alloc_texture_pixels(tex, width, height, GU_PSM_T4, persistent) + && cache_texture_t4(tex, src, width, height, width); + case 1: { + const int clut_persistent = + persistent && (int16_t)T1ReadWord(Vdp1Ram, CMDCOLR<<3) < 0; + tex->clut_dynamic = persistent && !clut_persistent; + return gen_clut_t4ind(tex, Vdp1Ram + (CMDCOLR << 3), pixel_mask, + rofs, gofs, bofs, transparent, endcodes, + clut_persistent) + && alloc_texture_pixels(tex, width, height, GU_PSM_T4, persistent) + && cache_texture_t4(tex, src, width, height, width); + } + case 2: + tex->clut_dynamic = persistent; + return gen_clut(tex, 64, + (color_base + CMDCOLR) & 0x7C0, CMDCOLR & 0x03F, + rofs, gofs, bofs, transparent, endcodes, + ((CMDCOLR | 0x3F) & pixel_mask) == pixel_mask, 0) + && alloc_texture_pixels(tex, width, height, GU_PSM_T8, persistent) + && cache_texture_t8(tex, src, 0x3F, width, height, width); + case 3: + tex->clut_dynamic = persistent; + return gen_clut(tex, 128, + (color_base + CMDCOLR) & 0x780, CMDCOLR & 0x07F, + rofs, gofs, bofs, transparent, endcodes, + ((CMDCOLR | 0x7F) & pixel_mask) == pixel_mask, 0) + && alloc_texture_pixels(tex, width, height, GU_PSM_T8, persistent) + && cache_texture_t8(tex, src, 0x7F, width, height, width); + case 4: + tex->clut_dynamic = persistent; + return gen_clut(tex, 256, + (color_base + CMDCOLR) & 0x700, CMDCOLR & 0x0FF, + rofs, gofs, bofs, transparent, endcodes, + ((CMDCOLR | 0xFF) & pixel_mask) == pixel_mask, 0) + && alloc_texture_pixels(tex, width, height, GU_PSM_T8, persistent) + && cache_texture_t8(tex, src, 0xFF, width, height, width); + default: + DMSG("unsupported pixel mode %d, assuming 16-bit", pixfmt); + /* Fall through to... */ + case 5: + tex->clut_dynamic = 0; + return alloc_texture_pixels(tex, width, height, GU_PSM_8888, persistent) + && cache_texture_16(tex, src, width, height, width, + rofs, gofs, bofs, transparent); + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * cache_tile: Cache a texture from tile (VDP2) memory. Implements common + * code for texcache_load_tile() and texcache_load_bitmap(). + * + * [Parameters] + * tex: TexInfo structure for caching texture + * address: Bitmap data address within VDP2 RAM + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * array: Number of VDP2 cells across and down texture + * (2 for 16x16 tiles, 1 otherwise) + * pixfmt: Bitmap pixel format + * transparent: Nonzero if index 0 or alpha 0 should be transparent + * color_base: Color table base (for indexed formats) + * color_ofs: Color table offset (for indexed formats) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero if a persistent texture, zero if a + * transient texture + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_tile( + TexInfo *tex, uint32_t address, unsigned int width, unsigned int height, + unsigned int stride, int array, int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, int rofs, int gofs, int bofs, + int persistent) +{ + if (UNLIKELY(address + (pixfmt==0 ? width/2 : pixfmt==1 ? width*1 : pixfmt<=3 ? width*2 : width*4) * height > 0x80000)) { + DMSG("%dx%d texture at 0x%X extends past end of VDP2 RAM" + " and will be incorrectly drawn", width, height, address); + } + + if (pixfmt == 0) { + tex->clut_dynamic = persistent; + if (!gen_clut(tex, 16, + (color_base + color_ofs) & 0x7F0, color_ofs & 0x00F, + rofs, gofs, bofs, transparent, 0, 0, 0)) { + return 0; + } + } else if (pixfmt == 1) { + tex->clut_dynamic = persistent; + if (!gen_clut(tex, 256, + (color_base + color_ofs) & 0x700, color_ofs & 0x0FF, + rofs, gofs, bofs, transparent, 0, 0, 0)) { + return 0; + } + } else { + tex->clut_dynamic = 0; + } + + const uint8_t *src = Vdp2Ram + address; + + if (array == 2) { + + switch (pixfmt) { + + case 0: { + if (!alloc_texture_pixels(tex, 16, 16, GU_PSM_T4, persistent)) { + return 0; + } + void * const saved_vram_address = tex->vram_address; + + /* Fake out the texture_cache_X() functions so they don't try + * to draw boundary pixels. */ + tex->width = tex->height = 8; + + unsigned int i; + for (i = 0; i < 4; i++, src += 32) { + tex->vram_address = (void *)((uintptr_t)saved_vram_address + + (i%2 ? 4 : 0) + + (i/2 ? 128 : 0)); + if (!cache_texture_t4(tex, src, 8, 8, 8)) { + return 0; + } + } + + tex->width = tex->height = 16; + tex->vram_address = saved_vram_address; + return 1; + } // case 0 + + case 1: { + if (!alloc_texture_pixels(tex, 16, 16, GU_PSM_T8, persistent)) { + return 0; + } + void * const saved_vram_address = tex->vram_address; + tex->width = tex->height = 8; + unsigned int i; + for (i = 0; i < 4; i++, src += 64) { + tex->vram_address = (void *)((uintptr_t)saved_vram_address + + (i%2 ? 8 : 0) + + (i/2 ? 128 : 0)); + if (!cache_texture_t8(tex, src, 0xFF, 8, 8, 8)) { + return 0; + } + } + tex->width = tex->height = 16; + tex->vram_address = saved_vram_address; + return 1; + } // case 1 + + case 2: { + if (!alloc_texture_pixels(tex, 16, 16, GU_PSM_8888, persistent)) { + return 0; + } + void * const saved_vram_address = tex->vram_address; + tex->width = tex->height = 8; + const uint32_t tilesize = (pixfmt < 4) ? 128 : 256; + unsigned int i; + for (i = 0; i < 4; i++, src += tilesize) { + tex->vram_address = (void *)((uintptr_t)saved_vram_address + + (i%2 ? 32 : 0) + + (i/2 ? 512 : 0)); + if (!cache_texture_t16(tex, src, color_base, 8, 8, 8, + rofs, gofs, bofs, transparent)) { + return 0; + } + } + tex->width = tex->height = 16; + tex->vram_address = saved_vram_address; + return 1; + } // case 2 + + case 3: { + if (!alloc_texture_pixels(tex, 16, 16, GU_PSM_8888, persistent)) { + return 0; + } + void * const saved_vram_address = tex->vram_address; + tex->width = tex->height = 8; + const uint32_t tilesize = (pixfmt < 4) ? 128 : 256; + unsigned int i; + for (i = 0; i < 4; i++, src += tilesize) { + tex->vram_address = (void *)((uintptr_t)saved_vram_address + + (i%2 ? 32 : 0) + + (i/2 ? 512 : 0)); + if (!cache_texture_16(tex, src, 8, 8, 8, + rofs, gofs, bofs, transparent)) { + return 0; + } + } + tex->width = tex->height = 16; + tex->vram_address = saved_vram_address; + return 1; + } // case 3 + + case 4: { + if (!alloc_texture_pixels(tex, 16, 16, GU_PSM_8888, persistent)) { + return 0; + } + void * const saved_vram_address = tex->vram_address; + tex->width = tex->height = 8; + const uint32_t tilesize = (pixfmt < 4) ? 128 : 256; + unsigned int i; + for (i = 0; i < 4; i++, src += tilesize) { + tex->vram_address = (void *)((uintptr_t)saved_vram_address + + (i%2 ? 32 : 0) + + (i/2 ? 512 : 0)); + if (!cache_texture_32(tex, src, 8, 8, 8, + rofs, gofs, bofs, transparent)) { + return 0; + } + } + tex->width = tex->height = 16; + tex->vram_address = saved_vram_address; + return 1; + } // case 4 + + default: + DMSG("Invalid tile pixel format %d", pixfmt); + return 0; + + } // switch (pixfmt) + + } else { // array == 1 + + switch (pixfmt) { + case 0: + return alloc_texture_pixels(tex, width, height, GU_PSM_T4, + persistent) + && cache_texture_t4(tex, src, width, height, stride); + case 1: + return alloc_texture_pixels(tex, width, height, GU_PSM_T8, + persistent) + && cache_texture_t8(tex, src, 0xFF, width, height, stride); + case 2: + return alloc_texture_pixels(tex, width, height, GU_PSM_8888, + persistent) + && cache_texture_t16(tex, src, color_base, width, height, + stride, rofs, gofs, bofs, transparent); + case 3: + return alloc_texture_pixels(tex, width, height, GU_PSM_8888, + persistent) + && cache_texture_16(tex, src, width, height, stride, + rofs, gofs, bofs, transparent); + case 4: + return alloc_texture_pixels(tex, width, height, GU_PSM_8888, + persistent) + && cache_texture_32(tex, src, width, height, stride, + rofs, gofs, bofs, transparent); + default: + DMSG("Invalid tile pixel format %d", pixfmt); + return 0; + } + + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * alloc_texture_pixels: Allocate memory for a texture's pixel data, and + * fill in the vram_address, width, height, stride, and pixfmt fields of + * the TexInfo structure. + * + * [Parameters] + * tex: TexInfo structure for texture + * width: Texture width in pixels + * height: Texture height in pixels + * psm: Native texture pixel format (GU_PSM_*) + * persistent: Nonzero if a persistent texture, zero if a transient texture + * [Return value] + * Nonzero on success, zero on error + */ +static inline int alloc_texture_pixels( + TexInfo *tex, unsigned int width, unsigned int height, unsigned int psm, + int persistent) +{ + /* Calculate the power-of-2 sizes we need for registering the texture + * with the GE. */ + unsigned int texwidth = 1 << (32 - __builtin_clz(width-1)); + unsigned int texheight = 1 << (32 - __builtin_clz(height-1)); + + /* If the texture width or height aren't powers of 2, we add an extra + * 1-pixel border on the right and bottom edges to avoid graphics + * glitches resulting from the GE trying to read one pixel beyond the + * edge of the texture data. */ + unsigned int outwidth = width + (width != texwidth ? 1 : 0); + unsigned int outheight = height + (height != texheight ? 1 : 0); + + unsigned int stride, linebytes; + if (psm == GU_PSM_T4) { + stride = (outwidth+31) & -32; + linebytes = stride/2; + } else if (psm == GU_PSM_T8) { + stride = (outwidth+15) & -16; + linebytes = stride; + } else { // Must be GU_PSM_8888, since we don't use any others + stride = (outwidth+3) & -4; + linebytes = stride*4; + } + + const unsigned int size = linebytes * ((outheight+7) & -8); + if (persistent) { + tex->vram_address = alloc_pixdata(size); + } else { + tex->vram_address = alloc_vram(size); + } + if (UNLIKELY(!tex->vram_address)) { + DMSG("%s buffer full, can't cache", + persistent ? "Persistent cache" : "DRAM"); + return 0; + } + + tex->width = texwidth; + tex->height = texheight; + tex->stride = stride; + tex->pixfmt = psm; + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * gen_clut: Generate a color lookup table for a 4-bit or 8-bit indexed + * texture. + * + * [Parameters] + * tex: TexInfo structure for color table + * size: Number of color entries to generate + * color_base: Base color index + * color_ofs: Color index offset ORed together with pixel + * rofs, gofs, bofs: Color offset values for texture + * transparent: Nonzero if pixel value 0 should be transparent + * endcodes: Nonzero if pixel value 0b11...11 should be transparent + * can_shadow: Nonzero if pixel value 0b11...10 is a shadow pixel + * persistent: Nonzero if a persistent texture, zero if a + * transient texture + * [Return value] + * Nonzero on success, zero on error + */ +static inline int gen_clut( + TexInfo *tex, unsigned int size, uint32_t color_base, uint8_t color_ofs, + int rofs, int gofs, int bofs, int transparent, int endcodes, + int can_shadow, int persistent) +{ + tex->clut_size = size; + + if (!endcodes && !transparent && !can_shadow && !color_ofs + && !rofs && !gofs && !bofs + ) { + /* There are no changes to apply to the palette, so just use the + * global CLUT directly. */ + tex->clut_address = &global_clut_32[color_base + color_ofs]; + return 1; + } + + const uint32_t color_set = (rofs & 0x1FF) << 0 + | (gofs & 0x1FF) << 9 + | (bofs & 0x1FF) << 18 + | (transparent ? 1<<27 : 0) + | (endcodes ? 1<<28 : 0); + const unsigned int cache_slot = color_base >> 4; + unsigned int cache_entry; + for (cache_entry = 0; cache_entry < CLUT_ENTRIES; cache_entry++) { + if (clut_cache[cache_slot][cache_entry].size == 0) { + /* This entry is empty, so we'll generate a palette and store + * it here. */ + clut_cache[cache_slot][cache_entry].size = size; + clut_cache[cache_slot][cache_entry].color_ofs = color_ofs; + clut_cache[cache_slot][cache_entry].color_set = color_set; + break; + } else if (clut_cache[cache_slot][cache_entry].size == size + && clut_cache[cache_slot][cache_entry].color_ofs == color_ofs + && clut_cache[cache_slot][cache_entry].color_set == color_set){ + /* Found a match, so return it. */ + tex->clut_address = + clut_cache[cache_slot][cache_entry].clut_address; + return 1; + } + } + if (UNLIKELY(cache_entry >= CLUT_ENTRIES)) { + DMSG("Warning: no free entries for CLUT cache slot 0x%02X", cache_slot); + } + + if (persistent) { + tex->clut_address = alloc_pixdata(size*4); + } else { + tex->clut_address = alloc_vram(size*4); + } + if (UNLIKELY(!tex->clut_address)) { + DMSG("%s buffer full, can't cache CLUT", + persistent ? "Persistent cache" : "VRAM"); + return 0; + } + if (cache_entry < CLUT_ENTRIES) { + clut_cache[cache_slot][cache_entry].clut_address = tex->clut_address; + } + + uint32_t *dest = (uint32_t *)tex->clut_address; + int i; + + /* Apply the color offset values to transparent or shadow pixels as + * well; this prevents dark rims around interpolated textures when + * positive color offsets are applied. */ + const uint32_t transparent_rgb = (rofs>0 ? rofs<< 0 : 0) + | (gofs>0 ? gofs<< 8 : 0) + | (bofs>0 ? bofs<<16 : 0); + + if (transparent) { + *dest++ = 0x00<<24 | transparent_rgb; + i = 1; + } else { + i = 0; + } + + const uint32_t *clut_32 = &global_clut_32[color_base]; + if (rofs | gofs | bofs) { + for (; i < size; i++, dest++) { + uint32_t color = clut_32[i | color_ofs]; + *dest = adjust_color_32_32(color, rofs, gofs, bofs); + } + } else { + for (; i < size; i++, dest++) { + *dest = clut_32[i | color_ofs]; + } + } + + if (endcodes) { + dest[-1] = 0x00<<24 | transparent_rgb; + } + + if (can_shadow) { + dest[-1] = 0x80<<24 | transparent_rgb; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * gen_clut_t4ind: Generate a color lookup table for a 4-bit indirect + * indexed texture. + * + * [Parameters] + * tex: TexInfo structure for color table + * color_lut: Pointer to VDP1 color lookup table + * pixel_mask: Mask to apply to palette indices in lookup table + * rofs, gofs, bofs: Color offset values for texture + * transparent: Nonzero if pixel value 0 should be transparent + * endcodes: Nonzero if pixel value 0b11...11 should be transparent + * persistent: Nonzero if a persistent texture, zero if a + * transient texture + * [Return value] + * Nonzero on success, zero on error + */ +static inline int gen_clut_t4ind( + TexInfo *tex, const uint8_t *color_lut, uint16_t pixel_mask, + int rofs, int gofs, int bofs, int transparent, int endcodes, + int persistent) +{ + tex->clut_size = 16; + + if (persistent) { + tex->clut_address = alloc_pixdata(16*4); + } else { + tex->clut_address = alloc_vram(16*4); + } + if (UNLIKELY(!tex->clut_address)) { + DMSG("%s buffer full, can't cache CLUT", + persistent ? "Persistent cache" : "VRAM"); + return 0; + } + + const uint16_t *src = (const uint16_t *)color_lut; + uint32_t *dest = (uint32_t *)tex->clut_address; + const uint32_t *clut_32 = global_clut_32; + + if (rofs | gofs | bofs) { + + const uint32_t transparent_rgb = (rofs>0 ? rofs<< 0 : 0) + | (gofs>0 ? gofs<< 8 : 0) + | (bofs>0 ? bofs<<16 : 0); + const uint16_t *top; + if (endcodes) { + dest[15] = 0x00<<24 | transparent_rgb; + top = src + 15; + } else { + top = src + 16; + } + if (transparent) { + src++; + *dest++ = 0x00<<24 | transparent_rgb; + } + + for (; src < top; src++, dest++) { + uint16_t color16 = BSWAP16(*src); + if (color16 & 0x8000) { + *dest = adjust_color_16_32(color16, rofs, gofs, bofs); + } else { + color16 &= pixel_mask; + if (color16 == pixel_mask - 1) { + *dest = 0x80<<24 | transparent_rgb; + } else { + *dest = adjust_color_32_32(clut_32[color16], + rofs, gofs, bofs); + } + } + } + + } else { // No color offset + + const uint16_t *top; + if (endcodes) { + dest[15] = 0x00000000; + top = src + 15; + } else { + top = src + 16; + } + if (transparent) { + src++; + *dest++ = 0x00000000; + } + + for (; src < top; src++, dest++) { + uint16_t color16 = BSWAP16(*src); + if (color16 & 0x8000) { + *dest = 0xFF000000 | (color16 & 0x7C00) << 9 + | (color16 & 0x03E0) << 6 + | (color16 & 0x001F) << 3; + } else { + color16 &= pixel_mask; + if (color16 == pixel_mask - 1) { + *dest = 0x80000000; + } else { + *dest = clut_32[color16]; + } + } + } + + } + + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * A note about the texture caching routines-- + * + * While the texture caching logic is divided into five separate caching + * routines (one for each pixel type: 4/8/16-bit indexed and 16/32-bit + * direct color), each with multiple branches conditioned on the texture + * parameters, all of them follow the same basic flow: + * + * for (each row of 16-byte-by-8-line swizzled blocks) { + * for (each swizzled block in the row) { + * for (each of 8 lines in the block) { + * read enough input pixels for 16 output bytes + * convert and store pixels into the output buffer + * } + * } + * if (width is not a power of 2) { + * copy rightmost column one pixel to the right + * } + * } + * if (height is not a power of 2) { + * copy bottommost row one pixel down + * } + * + * The reason for all the repetition is to help the compiler optimize + * specific common cases, particularly for tile caching in which case the + * size parameters are all a constant 8 or 16. + */ + +/*************************************************************************/ + +/** + * cache_texture_t4: Cache a 4-bit indexed texture. + * + * [Parameters] + * tex: TexInfo structure for texture + * src: Pointer to VDP1/VDP2 texture data + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_texture_t4( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride) +{ + const int outwidth = width + (width == tex->width ? 0 : 1); + const int outheight = height + (height == tex->height ? 0 : 1); + + /* Cache the value of this locally so we don't have to load it on + * every loop. */ + const unsigned int tex_stride = tex->stride; + + uint8_t *dest = (uint8_t *)tex->vram_address; + + if (tex_stride == 32) { + + if (width == 8) { + uint8_t *dest_top = dest + (height * 16); + for (; dest != dest_top; src += stride/2, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + ((uint32_t *)dest)[0] = ((src_word0 & 0x0F0F0F0F) << 4) + | ((src_word0 >> 4) & 0x0F0F0F0F); + } + } else if (width == 16) { + uint8_t *dest_top = dest + (height * 16); + for (; dest != dest_top; src += stride/2, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + ((uint32_t *)dest)[0] = ((src_word0 & 0x0F0F0F0F) << 4) + | ((src_word0 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[1] = ((src_word1 & 0x0F0F0F0F) << 4) + | ((src_word1 >> 4) & 0x0F0F0F0F); + } + } else { + uint8_t *dest_top = dest + (height * 16); + for (; dest != dest_top; src += stride/2, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + const uint32_t src_word2 = ((const uint32_t *)src)[2]; + const uint32_t src_word3 = ((const uint32_t *)src)[3]; + ((uint32_t *)dest)[0] = ((src_word0 & 0x0F0F0F0F) << 4) + | ((src_word0 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[1] = ((src_word1 & 0x0F0F0F0F) << 4) + | ((src_word1 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[2] = ((src_word2 & 0x0F0F0F0F) << 4) + | ((src_word2 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[3] = ((src_word3 & 0x0F0F0F0F) << 4) + | ((src_word3 >> 4) & 0x0F0F0F0F); + if (width != 32) { + dest[width/2] = dest[width/2-1] >> 4; // Copy last pixel + } + } + } + + if (outheight > height) { // Copy last line + ((uint32_t *)dest)[0] = ((uint32_t *)dest)[-4]; + ((uint32_t *)dest)[1] = ((uint32_t *)dest)[-3]; + ((uint32_t *)dest)[2] = ((uint32_t *)dest)[-2]; + ((uint32_t *)dest)[3] = ((uint32_t *)dest)[-1]; + } + + } else { // tex_stride > 32 + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*4 - tex_stride/2) { + uint8_t *dest_top = dest + tex_stride*4; + for (; dest != dest_top; src += 16) { + const uint8_t *line_src = src; + uint8_t *line_end = dest + 128; + for (; dest != line_end; line_src += stride/2, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)line_src)[0]; + const uint32_t src_word1 = ((const uint32_t *)line_src)[1]; + const uint32_t src_word2 = ((const uint32_t *)line_src)[2]; + const uint32_t src_word3 = ((const uint32_t *)line_src)[3]; + ((uint32_t *)dest)[0] = ((src_word0 & 0x0F0F0F0F) << 4) + | ((src_word0 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[1] = ((src_word1 & 0x0F0F0F0F) << 4) + | ((src_word1 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[2] = ((src_word2 & 0x0F0F0F0F) << 4) + | ((src_word2 >> 4) & 0x0F0F0F0F); + ((uint32_t *)dest)[3] = ((src_word3 & 0x0F0F0F0F) << 4) + | ((src_word3 >> 4) & 0x0F0F0F0F); + } + } + if (outwidth > width) { + uint8_t *eol_ptr = dest - 128 + ((width/2) & 15); + const int eol_ofs = (width & 31) ? -1 : -128 + 15; + uint8_t *eol_top = eol_ptr + 128; + for (; eol_ptr != eol_top; eol_ptr += 16) { + *eol_ptr = eol_ptr[eol_ofs] >> 4; + } + } + } + + if (outheight > height) { + if ((height & 7) != 0) { + dest = dest - tex_stride*4 + (height & 7)*16; + src = dest - 16; + } else { + src = dest - tex_stride*4 + 7*16; + } + uint8_t *dest_top = dest + tex_stride*4; + for (; dest < dest_top; src += 128, dest += 128) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + const uint32_t src_word2 = ((const uint32_t *)src)[2]; + const uint32_t src_word3 = ((const uint32_t *)src)[3]; + ((uint32_t *)dest)[0] = src_word0; + ((uint32_t *)dest)[1] = src_word1; + ((uint32_t *)dest)[2] = src_word2; + ((uint32_t *)dest)[3] = src_word3; + } + } + + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * cache_texture_t8: Cache an 8-bit indexed texture. + * + * [Parameters] + * tex: TexInfo structure for texture + * src: Pointer to VDP1/VDP2 texture data + * pixmask: Pixel data mask + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_texture_t8( + TexInfo *tex, const uint8_t *src, uint8_t pixmask, + unsigned int width, unsigned int height, unsigned int stride) +{ + const int outwidth = width + (width == tex->width ? 0 : 1); + const int outheight = height + (height == tex->height ? 0 : 1); + + const unsigned int tex_stride = tex->stride; + uint8_t *dest = (uint8_t *)tex->vram_address; + + if (pixmask != 0xFF) { + + const uint32_t pixmask32 = pixmask * 0x01010101; + if (stride == 8) { + unsigned int y; + for (y = 0; y < height; y++, src += 8, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + ((uint32_t *)dest)[0] = src_word0 & pixmask32; + ((uint32_t *)dest)[1] = src_word1 & pixmask32; + } + } else if (stride == 16) { + uint8_t *dest_top = dest + (height * stride); + for (; dest != dest_top; dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + const uint32_t src_word2 = ((const uint32_t *)src)[2]; + const uint32_t src_word3 = ((const uint32_t *)src)[3]; + ((uint32_t *)dest)[0] = src_word0 & pixmask32; + ((uint32_t *)dest)[1] = src_word1 & pixmask32; + ((uint32_t *)dest)[2] = src_word2 & pixmask32; + ((uint32_t *)dest)[3] = src_word3 & pixmask32; + } + } else { // stride > 16 + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*8 - tex_stride) { + uint8_t *dest_top = dest + tex_stride*8; + for (; dest != dest_top; src += 16) { + const uint8_t *line_src = src; + uint8_t *line_end = dest + 128; + for (; dest != line_end; line_src += stride, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)line_src)[0]; + const uint32_t src_word1 = ((const uint32_t *)line_src)[1]; + const uint32_t src_word2 = ((const uint32_t *)line_src)[2]; + const uint32_t src_word3 = ((const uint32_t *)line_src)[3]; + ((uint32_t *)dest)[0] = src_word0 & pixmask32; + ((uint32_t *)dest)[1] = src_word1 & pixmask32; + ((uint32_t *)dest)[2] = src_word2 & pixmask32; + ((uint32_t *)dest)[3] = src_word3 & pixmask32; + } + } + if (outwidth > width) { + uint8_t *eol_ptr = dest - 128 + (width & 15); + const int eol_ofs = (width & 15) ? -1 : -128 + 15; + uint8_t *eol_top = eol_ptr + 128; + for (; eol_ptr != eol_top; eol_ptr += 16) { + *eol_ptr = eol_ptr[eol_ofs]; + } + } + } + } + + } else { // pixmask == 0xFF + + if (stride == 8) { + unsigned int y; + for (y = 0; y < height; y++, src += 8, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + ((uint32_t *)dest)[0] = src_word0; + ((uint32_t *)dest)[1] = src_word1; + } + } else if (stride == 16) { + uint8_t *dest_top = dest + (height * stride); + for (; dest != dest_top; dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + const uint32_t src_word2 = ((const uint32_t *)src)[2]; + const uint32_t src_word3 = ((const uint32_t *)src)[3]; + ((uint32_t *)dest)[0] = src_word0; + ((uint32_t *)dest)[1] = src_word1; + ((uint32_t *)dest)[2] = src_word2; + ((uint32_t *)dest)[3] = src_word3; + } + } else { // stride > 16 + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*8 - tex_stride) { + uint8_t *dest_top = dest + tex_stride*8; + for (; dest != dest_top; src += 16) { + const uint8_t *line_src = src; + uint8_t *line_end = dest + 128; + for (; dest != line_end; line_src += stride, dest += 16) { + const uint32_t src_word0 = ((const uint32_t *)line_src)[0]; + const uint32_t src_word1 = ((const uint32_t *)line_src)[1]; + const uint32_t src_word2 = ((const uint32_t *)line_src)[2]; + const uint32_t src_word3 = ((const uint32_t *)line_src)[3]; + ((uint32_t *)dest)[0] = src_word0; + ((uint32_t *)dest)[1] = src_word1; + ((uint32_t *)dest)[2] = src_word2; + ((uint32_t *)dest)[3] = src_word3; + } + } + if (outwidth > width) { + uint8_t *eol_ptr = dest - 128 + (width & 15); + const int eol_ofs = (width & 15) ? -1 : -128 + 15; + uint8_t *eol_top = eol_ptr + 128; + for (; eol_ptr != eol_top; eol_ptr += 16) { + *eol_ptr = eol_ptr[eol_ofs]; + } + } + } + } + + } // if (pixmask != 0xFF) + + if (outheight > height) { + if (tex_stride == 16) { + ((uint32_t *)dest)[0] = ((uint32_t *)dest)[-4]; + ((uint32_t *)dest)[1] = ((uint32_t *)dest)[-3]; + ((uint32_t *)dest)[2] = ((uint32_t *)dest)[-2]; + ((uint32_t *)dest)[3] = ((uint32_t *)dest)[-1]; + } else { + if ((height & 7) != 0) { + dest = dest - tex_stride*8 + (height & 7)*16; + src = dest - 16; + } else { + src = dest - tex_stride*8 + 7*16; + } + uint8_t *dest_top = dest + tex_stride*8; + for (; dest < dest_top; src += 128, dest += 128) { + const uint32_t src_word0 = ((const uint32_t *)src)[0]; + const uint32_t src_word1 = ((const uint32_t *)src)[1]; + const uint32_t src_word2 = ((const uint32_t *)src)[2]; + const uint32_t src_word3 = ((const uint32_t *)src)[3]; + ((uint32_t *)dest)[0] = src_word0; + ((uint32_t *)dest)[1] = src_word1; + ((uint32_t *)dest)[2] = src_word2; + ((uint32_t *)dest)[3] = src_word3; + } + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * cache_texture_t16: Cache a 16-bit indexed texture. + * + * [Parameters] + * tex: TexInfo structure for texture + * src: Pointer to VDP1/VDP2 texture data + * color_base: Base color index + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * rofs, gofs, bofs: Color offset values for texture + * transparent: Nonzero if pixel value 0 should be transparent + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_texture_t16( + TexInfo *tex, const uint8_t *src, uint32_t color_base, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent) +{ + const int outwidth = width + (width == tex->width ? 0 : 1); + const int outheight = height + (height == tex->height ? 0 : 1); + + const unsigned int tex_stride = tex->stride; + uint32_t *dest = (uint32_t *)tex->vram_address; + const uint32_t *clut_32 = &global_clut_32[color_base]; + + if (rofs | gofs | bofs) { + + const uint32_t transparent_pixel = 0x00000000 + | (rofs>0 ? rofs<< 0 : 0) + | (gofs>0 ? gofs<< 8 : 0) + | (bofs>0 ? bofs<<16 : 0); + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + const uint16_t *line_src = (const uint16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const uint16_t pixel0 = BSWAP16(line_src[0]); + const uint16_t pixel1 = BSWAP16(line_src[1]); + const uint16_t pixel2 = BSWAP16(line_src[2]); + const uint16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = (transparent && !pixel0) ? transparent_pixel + : adjust_color_32_32(clut_32[pixel0 & 0x7FF], + rofs, gofs, bofs); + dest[1] = (transparent && !pixel1) ? transparent_pixel + : adjust_color_32_32(clut_32[pixel1 & 0x7FF], + rofs, gofs, bofs); + dest[2] = (transparent && !pixel2) ? transparent_pixel + : adjust_color_32_32(clut_32[pixel2 & 0x7FF], + rofs, gofs, bofs); + dest[3] = (transparent && !pixel3) ? transparent_pixel + : adjust_color_32_32(clut_32[pixel3 & 0x7FF], + rofs, gofs, bofs); + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else if (transparent) { + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + const uint16_t *line_src = (const uint16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const uint16_t pixel0 = BSWAP16(line_src[0]); + const uint16_t pixel1 = BSWAP16(line_src[1]); + const uint16_t pixel2 = BSWAP16(line_src[2]); + const uint16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = (pixel0 == 0) ? 0 : clut_32[pixel0 & 0x7FF]; + dest[1] = (pixel1 == 0) ? 0 : clut_32[pixel1 & 0x7FF]; + dest[2] = (pixel2 == 0) ? 0 : clut_32[pixel2 & 0x7FF]; + dest[3] = (pixel3 == 0) ? 0 : clut_32[pixel3 & 0x7FF]; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else { // !(rofs | gofs | bofs) && !transparent + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + const uint16_t *line_src = (const uint16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const uint16_t pixel0 = BSWAP16(line_src[0]); + const uint16_t pixel1 = BSWAP16(line_src[1]); + const uint16_t pixel2 = BSWAP16(line_src[2]); + const uint16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = clut_32[pixel0 & 0x7FF]; + dest[1] = clut_32[pixel1 & 0x7FF]; + dest[2] = clut_32[pixel2 & 0x7FF]; + dest[3] = clut_32[pixel3 & 0x7FF]; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } // if (rofs | gofs | bofs) + + if (outheight > height) { + const uint32_t *src32; + if ((height & 7) != 0) { + dest = dest - tex_stride*8 + (height & 7)*4; + src32 = dest - 4; + } else { + src32 = dest - tex_stride*8 + 7*4; + } + uint32_t *dest_top = dest + tex_stride; + for (; dest < dest_top; src32 += 32, dest += 32) { + const uint32_t pixel0 = src32[0]; + const uint32_t pixel1 = src32[1]; + const uint32_t pixel2 = src32[2]; + const uint32_t pixel3 = src32[3]; + dest[0] = pixel0; + dest[1] = pixel1; + dest[2] = pixel2; + dest[3] = pixel3; + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * cache_texture_16: Cache a 16-bit ARGB1555 texture. + * + * [Parameters] + * tex: TexInfo structure for texture + * src: Pointer to VDP1/VDP2 texture data + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * rofs, gofs, bofs: Color offset values for texture + * transparent: Nonzero if alpha-0 pixels should be transparent + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_texture_16( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent) +{ + const int outwidth = width + (width == tex->width ? 0 : 1); + const int outheight = height + (height == tex->height ? 0 : 1); + + const unsigned int tex_stride = tex->stride; + uint32_t *dest = (uint32_t *)tex->vram_address; + + if (rofs | gofs | bofs) { + + const uint32_t transparent_pixel = 0x00000000 + | (rofs>0 ? rofs<< 0 : 0) + | (gofs>0 ? gofs<< 8 : 0) + | (bofs>0 ? bofs<<16 : 0); + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + /* We load these as signed values to simplify checking the + * high (transparency) bit. */ + const int16_t *line_src = (const int16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int16_t pixel0 = BSWAP16(line_src[0]); + const int16_t pixel1 = BSWAP16(line_src[1]); + const int16_t pixel2 = BSWAP16(line_src[2]); + const int16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = (transparent && !(pixel0<0)) ? transparent_pixel + : adjust_color_16_32(pixel0, rofs, gofs, bofs); + dest[1] = (transparent && !(pixel1<0)) ? transparent_pixel + : adjust_color_16_32(pixel1, rofs, gofs, bofs); + dest[2] = (transparent && !(pixel2<0)) ? transparent_pixel + : adjust_color_16_32(pixel2, rofs, gofs, bofs); + dest[3] = (transparent && !(pixel3<0)) ? transparent_pixel + : adjust_color_16_32(pixel3, rofs, gofs, bofs); + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else if (transparent) { + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + const int16_t *line_src = (const int16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int16_t pixel0 = BSWAP16(line_src[0]); + const int16_t pixel1 = BSWAP16(line_src[1]); + const int16_t pixel2 = BSWAP16(line_src[2]); + const int16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = (pixel0 >= 0) ? 0 + : 0xFF000000 | (pixel0 & 0x7C00) << 9 + | (pixel0 & 0x03E0) << 6 + | (pixel0 & 0x001F) << 3; + dest[1] = (pixel1 >= 0) ? 0 + : 0xFF000000 | (pixel1 & 0x7C00) << 9 + | (pixel1 & 0x03E0) << 6 + | (pixel1 & 0x001F) << 3; + dest[2] = (pixel2 >= 0) ? 0 + : 0xFF000000 | (pixel2 & 0x7C00) << 9 + | (pixel2 & 0x03E0) << 6 + | (pixel2 & 0x001F) << 3; + dest[3] = (pixel3 >= 0) ? 0 + : 0xFF000000 | (pixel3 & 0x7C00) << 9 + | (pixel3 & 0x03E0) << 6 + | (pixel3 & 0x001F) << 3; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else { // !(rofs | gofs | bofs) && !transparent + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*16 - width*2, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 8) { + const int16_t *line_src = (const int16_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int16_t pixel0 = BSWAP16(line_src[0]); + const int16_t pixel1 = BSWAP16(line_src[1]); + const int16_t pixel2 = BSWAP16(line_src[2]); + const int16_t pixel3 = BSWAP16(line_src[3]); + dest[0] = 0xFF000000 | (pixel0 & 0x7C00) << 9 + | (pixel0 & 0x03E0) << 6 + | (pixel0 & 0x001F) << 3; + dest[1] = 0xFF000000 | (pixel1 & 0x7C00) << 9 + | (pixel1 & 0x03E0) << 6 + | (pixel1 & 0x001F) << 3; + dest[2] = 0xFF000000 | (pixel2 & 0x7C00) << 9 + | (pixel2 & 0x03E0) << 6 + | (pixel2 & 0x001F) << 3; + dest[3] = 0xFF000000 | (pixel3 & 0x7C00) << 9 + | (pixel3 & 0x03E0) << 6 + | (pixel3 & 0x001F) << 3; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } // if (rofs | gofs | bofs) + + if (outheight > height) { + const uint32_t *src32; + if ((height & 7) != 0) { + dest = dest - tex_stride*8 + (height & 7)*4; + src32 = dest - 4; + } else { + src32 = dest - tex_stride*8 + 7*4; + } + uint32_t *dest_top = dest + tex_stride; + for (; dest < dest_top; src32 += 32, dest += 32) { + const uint32_t pixel0 = src32[0]; + const uint32_t pixel1 = src32[1]; + const uint32_t pixel2 = src32[2]; + const uint32_t pixel3 = src32[3]; + dest[0] = pixel0; + dest[1] = pixel1; + dest[2] = pixel2; + dest[3] = pixel3; + } + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * cache_texture_32: Cache a 32-bit ARGB1888 texture. + * + * [Parameters] + * tex: TexInfo structure for texture + * src: Pointer to VDP1/VDP2 texture data + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * rofs, gofs, bofs: Color offset values for texture + * transparent: Nonzero if alpha-0 pixels should be transparent + * [Return value] + * Nonzero on success, zero on error + */ +static inline int cache_texture_32( + TexInfo *tex, const uint8_t *src, + unsigned int width, unsigned int height, unsigned int stride, + int rofs, int gofs, int bofs, int transparent) +{ + const int outwidth = width + (width == tex->width ? 0 : 1); + const int outheight = height + (height == tex->height ? 0 : 1); + + const unsigned int tex_stride = tex->stride; + uint32_t *dest = (uint32_t *)tex->vram_address; + + if (rofs | gofs | bofs) { + + const uint32_t transparent_pixel = 0x00000000 + | (rofs>0 ? rofs<< 0 : 0) + | (gofs>0 ? gofs<< 8 : 0) + | (bofs>0 ? bofs<<16 : 0); + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*32 - width*4, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 16) { + const int32_t *line_src = (const int32_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int32_t pixel0 = BSWAP32(line_src[0]); + const int32_t pixel1 = BSWAP32(line_src[1]); + const int32_t pixel2 = BSWAP32(line_src[2]); + const int32_t pixel3 = BSWAP32(line_src[3]); + dest[0] = (transparent && !(pixel0<0)) ? transparent_pixel + : adjust_color_32_32(pixel0, rofs, gofs, bofs); + dest[1] = (transparent && !(pixel1<0)) ? transparent_pixel + : adjust_color_32_32(pixel1, rofs, gofs, bofs); + dest[2] = (transparent && !(pixel2<0)) ? transparent_pixel + : adjust_color_32_32(pixel2, rofs, gofs, bofs); + dest[3] = (transparent && !(pixel3<0)) ? transparent_pixel + : adjust_color_32_32(pixel3, rofs, gofs, bofs); + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else if (transparent) { + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*32 - width*4, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 16) { + const int32_t *line_src = (const int32_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int32_t pixel0 = BSWAP32(line_src[0]); + const int32_t pixel1 = BSWAP32(line_src[1]); + const int32_t pixel2 = BSWAP32(line_src[2]); + const int32_t pixel3 = BSWAP32(line_src[3]); + dest[0] = (pixel0 >= 0) ? 0 : 0xFF000000 | pixel0; + dest[1] = (pixel1 >= 0) ? 0 : 0xFF000000 | pixel1; + dest[2] = (pixel2 >= 0) ? 0 : 0xFF000000 | pixel2; + dest[3] = (pixel3 >= 0) ? 0 : 0xFF000000 | pixel3; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } else { // !(rofs | gofs | bofs) && !transparent + + unsigned int y; + for (y = 0; y < height; y += 8, src += stride*32 - width*4, dest += (tex_stride - width)*8) { + uint32_t *dest_top = dest + width*8; + for (; dest != dest_top; src += 16) { + const int32_t *line_src = (const int32_t *)src; + uint32_t *line_end = dest + 32; + for (; dest != line_end; line_src += stride, dest += 4) { + const int32_t pixel0 = BSWAP32(line_src[0]); + const int32_t pixel1 = BSWAP32(line_src[1]); + const int32_t pixel2 = BSWAP32(line_src[2]); + const int32_t pixel3 = BSWAP32(line_src[3]); + dest[0] = 0xFF000000 | pixel0; + dest[1] = 0xFF000000 | pixel1; + dest[2] = 0xFF000000 | pixel2; + dest[3] = 0xFF000000 | pixel3; + } + } + if (outwidth > width) { + int line; + for (line = 0; line < 8; line++) { + dest[line*4] = dest[line*4-32]; + } + } + } + + } // if (rofs | gofs | bofs) + + if (outheight > height) { + const uint32_t *src32; + if ((height & 7) != 0) { + dest = dest - tex_stride*8 + (height & 7)*4; + src32 = dest - 4; + } else { + src32 = dest - tex_stride*8 + 7*4; + } + uint32_t *dest_top = dest + tex_stride; + for (; dest < dest_top; src32 += 32, dest += 32) { + const uint32_t pixel0 = src32[0]; + const uint32_t pixel1 = src32[1]; + const uint32_t pixel2 = src32[2]; + const uint32_t pixel3 = src32[3]; + dest[0] = pixel0; + dest[1] = pixel1; + dest[2] = pixel2; + dest[3] = pixel3; + } + } + + return 1; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/texcache.h b/yabause/src/psp/texcache.h new file mode 100644 index 0000000000..c21bda8f37 --- /dev/null +++ b/yabause/src/psp/texcache.h @@ -0,0 +1,141 @@ +/* src/psp/texcache.h: PSP texture cache management header + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_TEXCACHE_H +#define PSP_TEXCACHE_H + +#include "../vdp1.h" + +/*************************************************************************/ + +/** + * texcache_reset: Reset the texture cache, including all persistent + * textures. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void texcache_reset(void); + +/** + * texcache_clean: Clean all transient textures from the texture cache. + * Persistent textures are not affected. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void texcache_clean(void); + +/** + * texcache_cache_sprite: Cache the given sprite texture if it has not + * been cached already. Returns a texture key for later use in loading the + * texture. The texture width must be a multiple of 8. + * + * [Parameters] + * cmd: VDP1 command structure + * pixel_mask: Mask to apply to paletted pixel data + * width, height: Size of texture (in pixels) + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this frame only + * [Return value] + * Texture key (zero on error) + */ +extern uint32_t texcache_cache_sprite(const vdp1cmd_struct *cmd, + uint16_t pixel_mask, + unsigned int width, unsigned int height, + int persistent); + +/** + * texcache_load_sprite: Load the sprite texture indicated by the given + * texture key. + * + * [Parameters] + * key: Texture key returned by texcache_cache_sprite + * [Return value] + * None + */ +extern void texcache_load_sprite(uint32_t key); + +/** + * texcache_load_tile: Load the specified 8x8 or 16x16 tile texture into + * the GE registers for drawing, first caching the texture if necessary. + * + * [Parameters] + * tilesize: Pixel size (width and height) of tile, either 8 or 16 + * address: Tile data address within VDP2 RAM + * pixfmt: Tile pixel format + * transparent: Nonzero if index 0 or alpha 0 should be transparent + * color_base: Color table base (for indexed formats) + * color_ofs: Color table offset (for indexed formats) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this + * frame only + * [Return value] + * None + */ +extern void texcache_load_tile(int tilesize, uint32_t address, + int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, + int rofs, int gofs, int bofs, int persistent); + +/** + * texcache_load_bitmap: Load the specified bitmap texture into the GE + * registers for drawing, first caching the texture if necessary. The + * texture width must be a multiple of 8. + * + * [Parameters] + * address: Bitmap data address within VDP2 RAM + * width, height: Size of texture (in pixels) + * stride: Line size of source data (in pixels) + * pixfmt: Bitmap pixel format + * transparent: Nonzero if index 0 or alpha 0 should be transparent + * color_base: Color table base (for indexed formats) + * color_ofs: Color table offset (for indexed formats) + * rofs, gofs, bofs: Color offset values for texture + * persistent: Nonzero to cache this texture persistently across + * frames, zero to cache the texture for this + * frame only + * [Return value] + * None + */ +extern void texcache_load_bitmap(uint32_t address, unsigned int width, + unsigned int height, unsigned int stride, + int pixfmt, int transparent, + uint16_t color_base, uint16_t color_ofs, + int rofs, int gofs, int bof, int persistent); + +/*************************************************************************/ + +#endif // PSP_TEXCACHE_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/threads.c b/yabause/src/psp/threads.c new file mode 100644 index 0000000000..71c7042121 --- /dev/null +++ b/yabause/src/psp/threads.c @@ -0,0 +1,227 @@ +/* src/psp/threads.c: Yabause thread management routines + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" +#include "config.h" +#include "me.h" +#include "me-utility.h" +#include "sys.h" + +#include "../threads.h" + +/*************************************************************************/ + +/* Thread handle and setup data for each Yabause subthread */ +static struct { + int32_t handle; + const char * const name; + int priority; + uint32_t stack_size; +} thread_data[YAB_NUM_THREADS] = { + [YAB_THREAD_SCSP] = {.name = "YabauseScspThread", + .priority = THREADPRI_MAIN, + .stack_size = 16384}, // FIXME: could be smaller? +}; + +/*************************************************************************/ +/************************** Interface routines ***************************/ +/*************************************************************************/ + +/** + * YabThreadStart: Start a new thread for the given function. Only one + * thread will be started for each thread ID (YAB_THREAD_*). + * + * [Parameters] + * id: Yabause subthread ID (YAB_THREAD_*) + * func: Function to execute + * [Return value] + * Zero on success, negative on error + */ +int YabThreadStart(unsigned int id, void (*func)(void)) +{ + if (thread_data[id].handle) { + fprintf(stderr, "YabThreadStart: thread %u is already started!\n", id); + return -1; + } + + if (id == YAB_THREAD_SCSP) { // We run the SCSP on the ME + if (!me_available || !config_get_use_me()) { + DMSG("ME not available or disabled by user"); + return -1; + } + sceKernelDcacheWritebackAll(); +#ifdef PSP_DEBUG + meExceptionSetFatal(1); +#endif + int32_t res; + if ((res = meStart()) < 0) { + DMSG("meStart(): %s", psp_strerror(res)); + return -1; + } else if ((res = meWait()) < 0) { + DMSG("meWait(): %s", psp_strerror(res)); + return -1; + } else if ((res = meCall((void *)func, NULL)) < 0) { + DMSG("Failed to start thread %d (%s) on ME: %s", + id, thread_data[id].name, psp_strerror(res)); + return -1; + } + /* Attempting to suspend the PSP will currently cause a crash, + * so prevent suspending entirely. (This is documented in the + * README.PSP file.) */ + scePowerLock(0); + thread_data[id].handle = 1; // Anything nonzero will do + } else { + int32_t res = sys_start_thread(thread_data[id].name, func, + thread_data[id].priority, + thread_data[id].stack_size, 0, NULL); + if (res < 0) { + DMSG("Failed to start thread %d (%s): %s", + id, thread_data[id].name, psp_strerror(res)); + return -1; + } + thread_data[id].handle = res; + } + + return 0; +} + +/*************************************************************************/ + +/** + * YabThreadWait: Wait for the given ID's thread to terminate. Returns + * immediately if no thread has been started on the given ID. + * + * [Parameters] + * id: Yabause subthread ID (YAB_THREAD_*) + * [Return value] + * None + */ +void YabThreadWait(unsigned int id) +{ + if (!thread_data[id].handle) { + return; // Thread wasn't running in the first place + } + + if (id == YAB_THREAD_SCSP) { + sceKernelDcacheWritebackInvalidateAll(); + int32_t res; + if ((res = meWait()) < 0) { + DMSG("meWait(): %s", psp_strerror(res)); + } + } else { + int32_t res; + if ((res = sceKernelWaitThreadEnd(thread_data[id].handle, NULL)) < 0) { + DMSG("WaitThreadEnd(%d): %s", id, psp_strerror(res)); + } + if ((res = sceKernelDeleteThread(thread_data[id].handle)) < 0) { + DMSG("DeleteThread(%d): %s", id, psp_strerror(res)); + } + } + + thread_data[id].handle = 0; +} + +/*************************************************************************/ + +/** + * YabThreadYield: Yield CPU execution to another thread. + * + * [Parameters] + * None + * [Return value] + * None + */ +void YabThreadYield(void) +{ + if (meUtilityIsME()) { + /* On the ME, there's no other thread to yield to, but we do flush + * the cache to ensure the SC can see our updates. */ + meUtilityDcacheWritebackInvalidateAll(); + } else { +#ifdef PSP_DEBUG + if (thread_data[YAB_THREAD_SCSP].handle) { + /* ME is running, so check for ME exceptions */ + (void) mePoll(); + } +#endif + sceKernelDelayThread(0); + } +} + +/*************************************************************************/ + +/** + * YabThreadSleep: Put the current thread to sleep. + * + * [Parameters] + * None + * [Return value] + * None + */ +void YabThreadSleep(void) +{ + if (meUtilityIsME()) { + /* Used in place of YabThreadYield() when the SCSP thread has + * nothing to do; there's no real point in sleeping on the ME, but + * flush the data cache as in Yield(). */ + meUtilityDcacheWritebackInvalidateAll(); + } else { + sceKernelSleepThread(); + } +} + +/*************************************************************************/ + +/** + * YabThreadWake: Wake up the given thread if it is asleep. + * + * [Parameters] + * id: Yabause subthread ID (YAB_THREAD_*) + * [Return value] + * None + */ +void YabThreadWake(unsigned int id) +{ + if (!thread_data[id].handle) { + return; // Thread is not running + } + + if (id == YAB_THREAD_SCSP) { +#ifdef PSP_DEBUG + /* Check for ME exceptions */ + (void) mePoll(); +#endif + } else { + sceKernelWakeupThread(thread_data[id].handle); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/timing.c b/yabause/src/psp/timing.c new file mode 100644 index 0000000000..270aded1f2 --- /dev/null +++ b/yabause/src/psp/timing.c @@ -0,0 +1,145 @@ +/* src/psp/timing.c: Emulation timing logic for PSP + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "timing.h" + +/*************************************************************************/ +/****************************** Local data *******************************/ +/*************************************************************************/ + +/* Length of a single frame in microseconds (32.32 fixed point). */ +#define FRAME_TIME (((1001000LL << 32) + 30) / 60) + +/* Minimum elapsed time since the beginning of the previous frame before + * we consider this the start of a new frame. This is slightly less than + * the actual frame length to account for slight discrepancies in hardware + * timing. */ +#define MIN_WAIT (FRAME_TIME * 9/10) + +/* Microsecond timestamp at the beginning of the previous frame + * (32.32 fixed point). */ +static int64_t last_frame_time; + +/* Flag indicating that the next frame should be synched to real time. + * This is set on the call to timing_init() and also each call to + * timing_sync(), and can be cleared during a frame by a call to + * timing_skip_next_sync(). If the flag is clear on entry to timing_sync(), + * the sync is skipped and the last frame time is incremented by FRAME_TIME + * regardless of the current time. This flag is typically cleared by + * psp-video.c during skipped output frames to let several emulated frames + * execute at once. */ +static int sync_next_frame; + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * timing_init: Initialize the timing functionality at program start. + * Should be called as late as possible in the initialization sequence + * (as close to the start of emulation as possible). + * + * [Parameters] + * None + * [Return value] + * None + */ +void timing_init(void) +{ + sync_next_frame = 1; + sceDisplayWaitVblankStart(); + last_frame_time = (int64_t)sceKernelGetSystemTimeLow() << 32; +} + +/*-----------------------------------------------------------------------*/ + +/** + * timing_sync: Wait until at least one frame has passed since the + * beginning of the previous frame. However, if timing_skip_next_sync() + * was called after the previous call to timing_sync() or timing_init(), + * this function returns immediately, and the next timing_sync() call will + * wait one extra frame past the last synchronization point. (For example, + * calling timing_skip_next_sync() every second frame will cause the + * immediately subsequent timing_sync() to return immediately, and the + * timing_sync() after that to wait until two frames' time has passed since + * the call to timing_sync() preceding the timing_skip_next_sync() call.) + * + * [Parameters] + * None + * [Return value] + * None + */ +void timing_sync(void) +{ + if (sync_next_frame) { + int waited = 0; + int64_t now; + while (now = (int64_t)sceKernelGetSystemTimeLow() << 32, + now - last_frame_time < MIN_WAIT) + { + sceDisplayWaitVblankStart(); + waited = 1; + } + /* Keep the timer aligned as closely as possible to the actual + * vertical blank for smoother timing. */ + if (waited) { + last_frame_time = now; + } else { + do { + last_frame_time += FRAME_TIME; + } while (now - last_frame_time > FRAME_TIME/2); + } + } else { + last_frame_time += FRAME_TIME; + sync_next_frame = 1; + } +} + +/*************************************************************************/ + +/** + * timing_skip_next_sync: Called during a frame to indicate that the + * next call to timing_sync() should not perform a sync. See the + * timing_sync() documentation for details. + * + * [Parameters] + * None + * [Return value] + * None + */ +void timing_skip_next_sync(void) +{ + sync_next_frame = 0; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/timing.h b/yabause/src/psp/timing.h new file mode 100644 index 0000000000..f2df7bba76 --- /dev/null +++ b/yabause/src/psp/timing.h @@ -0,0 +1,80 @@ +/* src/psp/timing.h: Header for emulation timing logic + Copyright 2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PSP_TIMING_H +#define PSP_TIMING_H + +/*************************************************************************/ + +/** + * timing_init: Initialize the timing functionality at program start. + * Should be called as late as possible in the initialization sequence + * (as close to the start of emulation as possible). + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void timing_init(void); + +/** + * timing_sync: Wait until at least one frame has passed since the + * beginning of the previous frame. However, if timing_skip_next_sync() + * was called after the previous call to timing_sync() or timing_init(), + * this function returns immediately, and the next timing_sync() call will + * wait one extra frame past the last synchronization point. (For example, + * calling timing_skip_next_sync() every second frame will cause the + * immediately subsequent timing_sync() to return immediately, and the + * timing_sync() after that to wait until two frames' time has passed since + * the call to timing_sync() preceding the timing_skip_next_sync() call.) + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void timing_sync(void); + +/** + * timing_skip_next_sync: Called during a frame to indicate that the + * next call to timing_sync() should not perform a sync. See the + * timing_sync() documentation for details. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void timing_skip_next_sync(void); + +/*************************************************************************/ + +#endif // PSP_TIMING_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/psp/yui.c b/yabause/src/psp/yui.c new file mode 100644 index 0000000000..b0eb5fc09f --- /dev/null +++ b/yabause/src/psp/yui.c @@ -0,0 +1,147 @@ +/* src/psp/yui.c: Yabause core interface routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "common.h" + +#include "../vidsoft.h" +#include "../yui.h" + +#include "display.h" +#include "menu.h" + +/*************************************************************************/ +/******************** Yabause core interface routines ********************/ +/*************************************************************************/ + +/** + * YuiErrorMsg: Report an error to the user. + * + * [Parameters] + * string: Error message string + * [Return value] + * None + */ +void YuiErrorMsg(const char *string) +{ + PRECOND(string != NULL, return); + +#ifdef PSP_DEBUG + fprintf(stderr, "%s", string); +#endif + + /* Drop any leading/trailing newlines, and convert internal newlines to + * spaces, before passing the message to the menu screen */ + while (*string == '\r' || *string == '\n') { + string++; + } + char buf[100]; + snprintf(buf, sizeof(buf), "%s", string); + int i; + for (i = strlen(buf)-1; i >= 0; i--) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = 0; + } else { + break; + } + } + for (i = 0; buf[i]; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = ' '; + } + } + menu_set_error(buf); +} + +/*************************************************************************/ + +/** + * YuiSwapBuffers: Swap the front and back display buffers. Called by the + * software renderer. + * + * [Parameters] + * None + * [Return value] + * None + */ +void YuiSwapBuffers(void) +{ + if (!dispbuffer) { + return; + } + + /* Calculate display size (shrink interlaced/hi-res displays by half) */ + int width_in, height_in, width_out, height_out; + VIDCore->GetGlSize(&width_in, &height_in); + if (width_in <= DISPLAY_WIDTH) { + width_out = width_in; + } else { + width_out = width_in / 2; + } + if (height_in <= DISPLAY_HEIGHT) { + height_out = height_in; + } else { + height_out = height_in / 2; + } + int x = (DISPLAY_WIDTH - width_out) / 2; + int y = (DISPLAY_HEIGHT - height_out) / 2; + + /* Make sure all video buffer data is flushed to memory and cleared + * from the cache */ + sceKernelDcacheWritebackInvalidateRange(dispbuffer, + width_in * height_in * 4); + + /* Blit the data to the screen */ + display_begin_frame(); + if (width_out == width_in && height_out == height_in) { + display_blit(dispbuffer, width_in, height_in, width_in, x, y); + } else { + /* The PSP can't draw textures larger than 512x512, so if we're + * drawing a high-resolution buffer, split it in half. The height + * will never be greater than 512, so we don't need to check for a + * vertical split. */ + if (width_in > 512) { + const uint32_t *dispbuffer32 = (const uint32_t *)dispbuffer; + display_blit_scaled(dispbuffer32, width_in/2, height_in, + width_in, x, y, width_out/2, height_out); + dispbuffer32 += width_in/2; + x += width_out/2; + display_blit_scaled(dispbuffer32, width_in/2, height_in, + width_in, x, y, width_out/2, height_out); + } else { + display_blit_scaled(dispbuffer, width_in, height_in, width_in, + x, y, width_out, height_out); + } + } + display_end_frame(); + sceDisplayWaitVblankStart(); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-const.h b/yabause/src/q68/q68-const.h new file mode 100644 index 0000000000..9de141f175 --- /dev/null +++ b/yabause/src/q68/q68-const.h @@ -0,0 +1,217 @@ +/* src/q68/q68-const.h: Constants used in MC68000 emulation + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_CONST_H +#define Q68_CONST_H + +/*************************************************************************/ + +/* Configuration constants */ + +/* Maximum size in bytes of a 68k code block for translation */ +#ifndef Q68_JIT_MAX_BLOCK_SIZE +# define Q68_JIT_MAX_BLOCK_SIZE 4096 +#endif + +/* Size of pages used in checking for writes to already-translated code + * (1 page = 1<SR >> SR_I0_SHIFT) & 7) +#define SR_SET_I(state,val) ((state)->SR &= ~(7 << SR_I0_SHIFT), \ + (state)->SR |= ((val) & 7) << SR_I0_SHIFT) + +/*-----------------------------------------------------------------------*/ + +/* Exception numbers */ + +#define EX_INITIAL_SP 0 +#define EX_INITIAL_PC 1 +#define EX_BUS_ERROR 2 +#define EX_ADDRESS_ERROR 3 +#define EX_ILLEGAL_INSTRUCTION 4 +#define EX_DIVIDE_BY_ZERO 5 +#define EX_CHK 6 +#define EX_TRAPV 7 +#define EX_PRIVILEGE_VIOLATION 8 +#define EX_TRACE 9 +#define EX_LINE_1010 10 +#define EX_LINE_1111 11 + +#define EX_SPURIOUS_INTERRUPT 24 +#define EX_LEVEL_1_INTERRUPT 25 +#define EX_LEVEL_2_INTERRUPT 26 +#define EX_LEVEL_3_INTERRUPT 27 +#define EX_LEVEL_4_INTERRUPT 28 +#define EX_LEVEL_5_INTERRUPT 29 +#define EX_LEVEL_6_INTERRUPT 30 +#define EX_LEVEL_7_INTERRUPT 31 +#define EX_TRAP 32 // 16 vectors (EX_TRAP+0 .. EX_TRAP+15) + +/*-----------------------------------------------------------------------*/ + +/* Bits in the fault status word for bus/address error exceptions */ + +#define FAULT_STATUS_IN (1<<3) // Instruction/Not (0 = instruction, 1 = not) +#define FAULT_STATUS_IN_INSN (0) +#define FAULT_STATUS_IN_DATA (FAULT_STATUS_IN) + +#define FAULT_STATUS_RW (1<<4) // Read/Write (0 = write, 1 = read) +#define FAULT_STATUS_RW_READ (FAULT_STATUS_RW) +#define FAULT_STATUS_RW_WRITE (0) + +/*-----------------------------------------------------------------------*/ + +/* Condition codes for conditional instructions */ + +#define COND_T 0 +#define COND_F 1 +#define COND_HI 2 +#define COND_LS 3 +#define COND_CC 4 // also HS +#define COND_CS 5 // also LO +#define COND_NE 6 +#define COND_EQ 7 +#define COND_VC 8 +#define COND_VS 9 +#define COND_PL 10 +#define COND_MI 11 +#define COND_GE 12 +#define COND_LT 13 +#define COND_GT 14 +#define COND_LE 15 + +/*-----------------------------------------------------------------------*/ + +/* Size codes in opcode bits 6-7 */ + +#define SIZE_B 0 +#define SIZE_W 1 +#define SIZE_L 2 + +/* Macro to convert size codes to equivalent byte counts */ +#define SIZE_TO_BYTES(size) ((size) + 1 + ((size) == SIZE_L)) + +/*-----------------------------------------------------------------------*/ + +/* Effective address modes */ + +#define EA_DATA_REG 0 // Data Register Direct +#define EA_ADDRESS_REG 1 // Address Register Direct +#define EA_INDIRECT 2 // Address Register Indirect +#define EA_POSTINCREMENT 3 // Address Register Indirect with Postincrement +#define EA_PREDECREMENT 4 // Address Register Indirect with Predecrement +#define EA_DISPLACEMENT 5 // Address Register Indirect with Displacement +#define EA_INDEX 6 // Address Register Indirect with Index +#define EA_MISC 7 + +#define EA_MISC_ABSOLUTE_W 0 // Absolute Short +#define EA_MISC_ABSOLUTE_L 1 // Absolute Long +#define EA_MISC_PCREL 2 // Program Counter Indirect with Displacement +#define EA_MISC_PCREL_INDEX 3 // Program Counter Indirect with Index +#define EA_MISC_IMMEDIATE 4 // Immediate + +/* Macros to retrieve the mode and register number from an opcode */ +#define EA_MODE(opcode) ((opcode)>>3 & 7) +#define EA_REG(opcode) ((opcode)>>0 & 7) + +/*************************************************************************/ +/*************************************************************************/ + +#endif // Q68_CONST_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-core.c b/yabause/src/q68/q68-core.c new file mode 100644 index 0000000000..b3dc4a8bd7 --- /dev/null +++ b/yabause/src/q68/q68-core.c @@ -0,0 +1,2563 @@ +/* src/q68/q68-core.c: Q68 execution core + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "q68.h" +#include "q68-const.h" +#include "q68-internal.h" + +/* Define this to export two tables of counters: + * uint32_t q68_ops[128]; // Corresponds to opcode_table[] + * uint32_t q68_4xxx_ops[32]; // Corresponds to opcode_4xxx_table[] + * Each entry will be incremented each time the corresponding function is + * called. */ +// #define COUNT_OPCODES + +/*************************************************************************/ + +/* + * The following table maps each 68000 instruction to its implementing + * function in this file: + * + * || Instruction | Func. || Instruction | Func. || Instruction | Func. || + * ++-------------+--------++-------------+--------++-------------+--------++ + * || ABCD | op_BCD || EOR | op_alu || NOT | op4alu || + * || ADD | op_alu || EORI | op_imm || OR | op_alu || + * || ADDA | op_alu || EORI to CCR | op_imm || ORI | op_imm || + * || ADDI | op_imm || EORI to SR | op_imm || ORI to CCR | op_imm || + * || ADDQ | opADSQ || EXG | op_EXG || ORI to SR | op_imm || + * || ADDX | opADSX || EXT | op_EXT || PEA | op_PEA || + * || AND | op_alu || ILLEGAL | op_TAS || RESET | op4E7x || + * || ANDI | op_imm || JMP | opjump || ROL | opshft || + * || ANDI to CCR | op_imm || JSR | opjump || ROR | opshft || + * || ANDI to SR | op_imm || LEA | op_LEA || ROXL | opshft || + * || ASL | opshft || LINK | opLINK || ROXR | opshft || + * || ASR | opshft || LSL | opshft || RTE | op4E7x || + * || Bcc | op_Bcc || LSR | opshft || RTR | op4E7x || + * || BCHG | op_bit || MOVE | opMOVE || RTS | op4E7x || + * || BCLR | op_bit || MOVEA | opMOVE || SBCD | op_BCD || + * || BRA | op_Bcc || MOVE to CCR | opMVSR || Scc | op_Scc || + * || BSET | op_bit || MOVE from SR| opMVSR || STOP | op4E7x || + * || BSR | op_Bcc || MOVE to SR | opMVSR || SUB | op_alu || + * || BTST | op_bit || MOVE USP | opMUSP || SUBA | op_alu || + * || CHK | op_CHK || MOVEM | (*1) || SUBI | op_imm || + * || CLR | op4alu || MOVEP | opMOVP || SUBQ | opADSQ || + * || CMP | op_alu || MOVEQ | opMOVQ || SUBX | opADSX || + * || CMPA | op_alu || MULS | op_MUL || SWAP | opSWAP || + * || CMPI | op_imm || MULU | op_MUL || TAS | op_TAS || + * || CMPM | opCMPM || NBCD | opNBCD || TRAP | opTRAP || + * || DBcc | opDBcc || NEG | op4alu || TRAPV | op4E7x || + * || DIVS | op_DIV || NEGX | op4alu || TST | op4alu || + * || DIVU | op_DIV || NOP | op4E7X || UNLK | opUNLK || + * + * (*1) MOVEM is implemented by two instructions, op_STM (MOVEM reglist,mem) + * and op_LDM (MOVEM mem,reglist). + * + * Cycle counts were taken from: + * http://linas.org/mirrors/www.nvg.ntnu.no/2002.09.16/amiga/MC680x0_Sections/mc68000timing.HTML + */ + +/*************************************************************************/ + +/* Forward declarations for helper functions and instruction implementations */ + +static void set_SR(Q68State *state, uint16_t value); +static inline void check_interrupt(Q68State *state); +static int take_exception(Q68State *state, uint8_t num); +static inline int op_ill(Q68State *state, uint32_t opcode); + +static int ea_resolve(Q68State *state, uint32_t opcode, int size, + int access_type); +/* Note: Allowing these to be inlined actually slows down the code by ~0.5% + * (at least on x86). FIXME: how about PSP? */ +static NOINLINE uint32_t ea_get(Q68State *state, uint32_t opcode, int size, + int is_rmw, int *cycles_ret); +static NOINLINE void ea_set(Q68State *state, uint32_t opcode, int size, + uint32_t data); + +static int op_imm(Q68State *state, uint32_t opcode); +static int op_bit(Q68State *state, uint32_t opcode); +static int opMOVE(Q68State *state, uint32_t opcode); +static int op4xxx(Q68State *state, uint32_t opcode); +static int op_CHK(Q68State *state, uint32_t opcode); +static int op_LEA(Q68State *state, uint32_t opcode); +static int opADSQ(Q68State *state, uint32_t opcode); +static int op_Scc(Q68State *state, uint32_t opcode); +static int opDBcc(Q68State *state, uint32_t opcode); +static int op_Bcc(Q68State *state, uint32_t opcode); +static int opMOVQ(Q68State *state, uint32_t opcode); +static int op_alu(Q68State *state, uint32_t opcode); +static int op_DIV(Q68State *state, uint32_t opcode); +static int opAxxx(Q68State *state, uint32_t opcode); +static int op_MUL(Q68State *state, uint32_t opcode); +static int opshft(Q68State *state, uint32_t opcode); +static int opFxxx(Q68State *state, uint32_t opcode); + +static int op4alu(Q68State *state, uint32_t opcode); +static int opMVSR(Q68State *state, uint32_t opcode); +static int opNBCD(Q68State *state, uint32_t opcode); +static int op_PEA(Q68State *state, uint32_t opcode); +static int opSWAP(Q68State *state, uint32_t opcode); +static int op_TAS(Q68State *state, uint32_t opcode); +static int op_EXT(Q68State *state, uint32_t opcode); +static int op_STM(Q68State *state, uint32_t opcode); +static int op_LDM(Q68State *state, uint32_t opcode); +static int opmisc(Q68State *state, uint32_t opcode); +static int opTRAP(Q68State *state, uint32_t opcode); +static int opLINK(Q68State *state, uint32_t opcode); +static int opUNLK(Q68State *state, uint32_t opcode); +static int opMUSP(Q68State *state, uint32_t opcode); +static int op4E7x(Q68State *state, uint32_t opcode); +static int opjump(Q68State *state, uint32_t opcode); + +static int opMOVP(Q68State *state, uint32_t opcode); +static int opADSX(Q68State *state, uint32_t opcode); +static int op_BCD(Q68State *state, uint32_t opcode); +static int opCMPM(Q68State *state, uint32_t opcode); +static int op_EXG(Q68State *state, uint32_t opcode); + +/*-----------------------------------------------------------------------*/ + +/* Main table of instruction implemenation functions; table index is bits + * 15-12 and 8-6 of the opcode (ABCD ...E FG.. .... -> 0ABC DEFG). */ +static OpcodeFunc * const opcode_table[128] = { + op_imm, op_imm, op_imm, op_imm, op_bit, op_bit, op_bit, op_bit, // 00 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 10 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 20 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 30 + + op4xxx, op4xxx, op4xxx, op4xxx, op_ill, op_ill, op_CHK, op_LEA, // 40 + opADSQ, opADSQ, opADSQ, op_Scc, opADSQ, opADSQ, opADSQ, op_Scc, // 50 + op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, // 60 + opMOVQ, opMOVQ, opMOVQ, opMOVQ, op_ill, op_ill, op_ill, op_ill, // 70 + + op_alu, op_alu, op_alu, op_DIV, op_alu, op_alu, op_alu, op_DIV, // 80 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // 90 + opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, // A0 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // B0 + + op_alu, op_alu, op_alu, op_MUL, op_alu, op_alu, op_alu, op_MUL, // C0 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // D0 + opshft, opshft, opshft, opshft, opshft, opshft, opshft, opshft, // E0 + opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, // F0 +}; + +/* Subtable for instructions in the $4xxx (miscellaneous) group; table index + * is bits 11-9 and 7-6 of the opcode (1000 ABC0 DE.. .... -> 000A BCDE). */ +static OpcodeFunc * const opcode_4xxx_table[32] = { + op4alu, op4alu, op4alu, opMVSR, // 40xx + op4alu, op4alu, op4alu, op_ill, // 42xx + op4alu, op4alu, op4alu, opMVSR, // 44xx + op4alu, op4alu, op4alu, opMVSR, // 46xx + opNBCD, op_PEA, op_STM, op_STM, // 48xx + op4alu, op4alu, op4alu, op_TAS, // 4Axx + op_ill, op_ill, op_LDM, op_LDM, // 4Cxx + op_ill, opmisc, opjump, opjump, // 4Exx +}; + +/* Sub-subtable for instructions in the $4E40-$4E7F range, used by opmisc(); + * index is bits 5-3 of the opcode. */ +static OpcodeFunc * const opcode_4E4x_table[8] = { + opTRAP, opTRAP, opLINK, opUNLK, + opMUSP, opMUSP, op4E7x, op_ill, +}; + +#ifdef COUNT_OPCODES +/* Counters for opcode groups. */ +uint32_t q68_ops[128], q68_4xxx_ops[32]; +#endif + +/*************************************************************************/ +/************************** Interface functions **************************/ +/*************************************************************************/ + +/** + * q68_reset: Reset the virtual processor. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +void q68_reset(Q68State *state) +{ + int i; + for (i = 0; i < 8; i++) { + state->D[i] = 0; + state->A[i] = 0; + } + state->PC = 0; + state->SR = SR_S; + SR_SET_I(state, 7); + state->USP = 0; + state->SSP = 0; + state->current_PC = 0; + state->ea_addr = 0; + state->exception = 0; + state->fault_addr = 0; + state->fault_opcode = 0; + state->fault_status = 0; + state->jit_running = NULL; +#ifdef Q68_USE_JIT + q68_jit_reset(state); +#endif +#ifdef Q68_TRACE + q68_trace_init(state); +#endif + + state->A[7] = READU32(state, 0x000000); + state->PC = READU32(state, 0x000004); + state->halted = 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_run: Execute instructions for the given number of clock cycles. + * + * [Parameters] + * state: Processor state block + * cycles: Number of clock cycles to execute + * [Return value] + * Number of clock cycles executed (may be greater than "cycles") + */ +int q68_run(Q68State *state, int cycles) +{ + /* Check for pending interrupts */ + check_interrupt(state); + + /* Run the virtual processor */ + state->cycles = 0; + while (state->cycles < cycles) { + if (UNLIKELY(state->halted)) { + /* If we're halted, consume all remaining cycles */ + state->cycles = cycles; + break; + } + if (UNLIKELY(state->exception)) { + int exception = state->exception; + state->exception = 0; + state->cycles += take_exception(state, exception); + if (state->cycles >= cycles) { + break; + } + } +#ifdef Q68_USE_JIT + if (!state->jit_running) { + state->jit_running = q68_jit_find(state, state->PC); + if (UNLIKELY(!state->jit_running)) { + state->jit_running = q68_jit_translate(state, state->PC); + } + } + if (state->jit_running) { + q68_jit_run(state, cycles, &state->jit_running); + } else { +#endif +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (UNLIKELY(state->PC & 1)) { + state->fault_addr = state->PC; + state->fault_status = FAULT_STATUS_IN_INSN + | FAULT_STATUS_RW_READ; + state->cycles += take_exception(state, EX_ADDRESS_ERROR); + continue; + } +#endif +#ifdef Q68_TRACE + q68_trace(); +#endif + const unsigned int opcode = IFETCH(state); + state->current_PC = state->PC; +#ifndef Q68_DISABLE_ADDRESS_ERROR + state->fault_opcode = opcode; +#endif + const unsigned int index = (opcode>>9 & 0x78) | (opcode>>6 & 0x07); +#ifdef COUNT_OPCODES + q68_ops[index]++; +#endif + state->cycles += (*opcode_table[index])(state, opcode); +#ifdef Q68_USE_JIT + } +#endif + } // while (state->cycles < cycles && !state->halted) + +#ifdef Q68_TRACE + q68_trace_add_cycles(state->cycles); +#endif + + return state->cycles; +} + +/*************************************************************************/ +/************************** Instruction helpers **************************/ +/*************************************************************************/ + +/** + * set_SR: Set the processor's status register, performing any necessary + * additional actions (such as switching user/supervisor stacks). + * + * [Parameters] + * state: Processor state block + * value: New SR value + * [Return value] + * None + */ +static void set_SR(Q68State *state, uint16_t value) +{ + const uint16_t old_value = state->SR; + state->SR = value; + if ((old_value ^ value) & SR_S) { + if (value & SR_S) { // Switched to supervisor mode + state->USP = state->A[7]; + state->A[7] = state->SSP; + } else { // Switched to user mode + state->SSP = state->A[7]; + state->A[7] = state->USP; + } + } + check_interrupt(state); +} + +/*************************************************************************/ + +/** + * check_interrupt: Check whether an unmasked interrupt is pending, and + * raise the appropriate exception if so. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +static inline void check_interrupt(Q68State *state) +{ + const int irq = state->irq & 7; // Just to be safe + if (UNLIKELY(irq > SR_GET_I(state) + || irq == 7 // Level 7 is the non-maskable interrupt + )) { + if (state->halted != Q68_HALTED_DOUBLE_FAULT) { + state->irq = 0; + state->halted = 0; + state->exception = EX_LEVEL_1_INTERRUPT + (irq-1); + } + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * take_exception: Take an exception. + * + * [Parameters] + * state: Processor state block + * num: Exception number + * [Return value] + * Clock cycles used + */ +static int take_exception(Q68State *state, uint8_t num) +{ + static const int exception_cycles[256] = { + [EX_BUS_ERROR ] = 50, + [EX_ADDRESS_ERROR ] = 50, + [EX_ILLEGAL_INSTRUCTION ] = 34, + [EX_DIVIDE_BY_ZERO ] = 42, + [EX_CHK ] = 44, + [EX_TRAPV ] = 34, + [EX_PRIVILEGE_VIOLATION ] = 34, + [EX_TRACE ] = 34, + [EX_LINE_1010 ] = 34, // These two are assumed to be + [EX_LINE_1111 ] = 34, // equal to ILLEGAL_INSTRUCTION + [EX_SPURIOUS_INTERRUPT ] = 44, + [EX_LEVEL_1_INTERRUPT ] = 44, + [EX_LEVEL_2_INTERRUPT ] = 44, + [EX_LEVEL_3_INTERRUPT ] = 44, + [EX_LEVEL_4_INTERRUPT ] = 44, + [EX_LEVEL_5_INTERRUPT ] = 44, + [EX_LEVEL_6_INTERRUPT ] = 44, + [EX_LEVEL_7_INTERRUPT ] = 44, + [EX_TRAP+ 0 ] = 38, + [EX_TRAP+ 1 ] = 38, + [EX_TRAP+ 2 ] = 38, + [EX_TRAP+ 3 ] = 38, + [EX_TRAP+ 4 ] = 38, + [EX_TRAP+ 5 ] = 38, + [EX_TRAP+ 6 ] = 38, + [EX_TRAP+ 7 ] = 38, + [EX_TRAP+ 8 ] = 38, + [EX_TRAP+ 9 ] = 38, + [EX_TRAP+10 ] = 38, + [EX_TRAP+11 ] = 38, + [EX_TRAP+12 ] = 38, + [EX_TRAP+13 ] = 38, + [EX_TRAP+14 ] = 38, + [EX_TRAP+15 ] = 38, + }; + + /* Clear this out ahead of time in case we hit a double fault */ + state->jit_running = NULL; + + if (!(state->SR & SR_S)) { + state->USP = state->A[7]; + state->A[7] = state->SSP; + } +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->halted = Q68_HALTED_DOUBLE_FAULT; // Oops! + return 0; + } +#endif + PUSH32(state, state->PC); + PUSH16(state, state->SR); + if (num == EX_BUS_ERROR || num == EX_ADDRESS_ERROR) { + PUSH16(state, state->fault_opcode); + PUSH32(state, state->fault_addr); + PUSH16(state, state->fault_status); + } + state->SR |= SR_S; + if (num >= EX_LEVEL_1_INTERRUPT && num <= EX_LEVEL_7_INTERRUPT) { + SR_SET_I(state, (num - EX_LEVEL_1_INTERRUPT) + 1); + } + state->PC = READU32(state, num*4); +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->PC & 1) { + /* FIXME: Does a real 68000 double fault here or just take an + * address error exception? */ + state->halted = Q68_HALTED_DOUBLE_FAULT; + return 0; + } +#endif + return exception_cycles[num]; +} + +/*************************************************************************/ + +/** + * op_ill: Handle a generic illegal opcode. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * [Return value] + * Clock cycles used + */ +static inline int op_ill(Q68State *state, uint32_t opcode) +{ + state->exception = EX_ILLEGAL_INSTRUCTION; + return 0; +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * ea_resolve: Resolve the address for the memory-reference EA indicated + * by opcode[5:0], storing it in state->ea_addr. Behavior is undefined if + * the EA is a direct register reference. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * access_type: Access type (ACCESS_*) + * [Return value] + * Clock cycles used (negative indicates an illegal EA) + */ +static int ea_resolve(Q68State *state, uint32_t opcode, int size, + int access_type) +{ + const unsigned int mode = EA_MODE(opcode); + const unsigned int reg = EA_REG(opcode); + const unsigned int bytes = SIZE_TO_BYTES(size); + + static const int base_cycles[8] = {0, 0, 4, 4, 6, 8, 10, 0}; + int cycles = base_cycles[mode] + (size==SIZE_L ? 4 : 0); + + switch (mode) { + case EA_INDIRECT: + state->ea_addr = state->A[reg]; + break; + case EA_POSTINCREMENT: + state->ea_addr = state->A[reg]; + state->A[reg] += bytes; + if (bytes == 1 && reg == 7) { // A7 must stay even + state->A[reg] += 1; + } + break; + case EA_PREDECREMENT: + if (access_type == ACCESS_WRITE) { + /* 2-cycle penalty not applied to write-only accesses + * (MOVE and MOVEM) */ + cycles -= 2; + } + state->A[reg] -= bytes; + if (bytes == 1 && reg == 7) { // A7 must stay even + state->A[reg] -= 1; + } + state->ea_addr = state->A[reg]; + break; + case EA_DISPLACEMENT: + state->ea_addr = state->A[reg] + (int16_t)IFETCH(state); + break; + case EA_INDEX: { + const uint16_t ext = IFETCH(state); + const unsigned int ireg = ext >> 12; // 0..15 + const int32_t index = (ext & 0x0800) ? (int32_t)state->DA[ireg] + : (int16_t)state->DA[ireg]; + const int32_t disp = (int32_t)((int8_t)ext); + state->ea_addr = state->A[reg] + index + disp; + break; + } + default: /* case EA_MISC */ + switch (reg) { + case EA_MISC_ABSOLUTE_W: + cycles += 8; + state->ea_addr = (int16_t)IFETCH(state); + break; + case EA_MISC_ABSOLUTE_L: + cycles += 12; + state->ea_addr = IFETCH(state) << 16; + state->ea_addr |= (uint16_t)IFETCH(state); + break; + case EA_MISC_PCREL: + if (access_type != ACCESS_READ) { + return -1; + } else { + cycles += 8; + state->ea_addr = state->current_PC + (int16_t)IFETCH(state); + } + break; + case EA_MISC_PCREL_INDEX: + if (access_type != ACCESS_READ) { + return -1; + } else { + cycles += 10; + const uint16_t ext = IFETCH(state); + const unsigned int ireg = ext >> 12; // 0..15 + const int32_t index = (ext & 0x0800) ? (int32_t)state->DA[ireg] + : (int16_t)state->DA[ireg]; + const int32_t disp = (int32_t)((int8_t)ext); + state->ea_addr = state->current_PC + index + disp; + } + break; + case EA_MISC_IMMEDIATE: + if (access_type != ACCESS_READ) { + return -1; + } else { + cycles += 4; + state->ea_addr = state->PC; + if (size == SIZE_B) { + state->ea_addr++; // Point at the lower byte + } + state->PC += (size==SIZE_L ? 4 : 2); + } + break; + default: + return -1; + } + } + return cycles; +} + +/*-----------------------------------------------------------------------*/ + +/** + * ea_get: Read an unsigned value from the EA indicated by opcode[5:0]. + * + * If the EA selector is invalid for the access size and mode, an illegal + * instruction exception is raised. If the EA is a memory reference, the + * size is word or long, and the address is odd, an address error + * exception is raised. In either case, the error is indicated by a + * negative value returned in *cycles_ret. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * is_rmw: Nonzero if the operand will be modified and written back + * cycles_ret: Pointer to variable to receive clock cycles used + * (negative indicates that an exception occurred) + * [Return value] + * Value read (undefined if an exception occurs) + */ +static uint32_t ea_get(Q68State *state, uint32_t opcode, int size, + int is_rmw, int *cycles_ret) +{ + const unsigned int reg = EA_REG(opcode); + switch (EA_MODE(opcode)) { + case EA_DATA_REG: + *cycles_ret = 0; + return size==SIZE_B ? (uint8_t) state->D[reg] : + size==SIZE_W ? (uint16_t)state->D[reg] : state->D[reg]; + case EA_ADDRESS_REG: + if (size == SIZE_B) { + /* An.b not permitted */ + state->exception = EX_ILLEGAL_INSTRUCTION; + *cycles_ret = -1; + return 0; + } else { + *cycles_ret = 0; + return size==SIZE_W ? (uint16_t)state->A[reg] : state->A[reg]; + } + default: { + *cycles_ret = ea_resolve(state, opcode, size, + is_rmw ? ACCESS_MODIFY : ACCESS_READ); + if (*cycles_ret < 0) { + state->exception = EX_ILLEGAL_INSTRUCTION; + return 0; + } + if (size == SIZE_B) { + return READU8(state, state->ea_addr); + } else { +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->ea_addr & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->ea_addr; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + *cycles_ret = -1; + return 0; + } +#endif + return size==SIZE_W ? READU16(state, state->ea_addr) + : READU32(state, state->ea_addr); + } + } + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * ea_set: Update a value at the EA indicated by opcode[5:0]. If the + * EA is a memory reference, uses the previously resolved address in + * state->ea_addr rather than resolving the address again. Behavior is + * undefined if the previous ea_resolve() or ea_get() failed (or if no + * previous call was made). + * + * If the EA is a memory reference, the size is word or long, and the + * address is odd, an address error exception is raised instead. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * data: Value to store + * [Return value] + * None + */ +static void ea_set(Q68State *state, uint32_t opcode, int size, uint32_t data) +{ + const unsigned int reg = EA_REG(opcode); + switch (EA_MODE(opcode)) { + case EA_DATA_REG: + switch (size) { + case SIZE_B: *(BYTE_OFS + (uint8_t *)&state->D[reg]) = data; break; + case SIZE_W: *(WORD_OFS + (uint16_t *)&state->D[reg]) = data; break; + default: state->D[reg] = data; break; + } + return; + case EA_ADDRESS_REG: + if (size == SIZE_W) { + state->A[reg] = (int16_t)data; // Sign-extended for address regs + } else { // must be SIZE_L + state->A[reg] = data; + } + return; + default: { + if (size == SIZE_B) { + WRITE8(state, state->ea_addr, data); + } else { +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->ea_addr & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->ea_addr; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return; + } else +#endif + if (size == SIZE_W) { + WRITE16(state, state->ea_addr, data); + } else { + WRITE32(state, state->ea_addr, data); + } + } + return; + } + } +} + +/*************************************************************************/ +/*********************** Major instruction groups ************************/ +/*************************************************************************/ + +/** + * op_imm: Immediate instructions (format 0000 xxx0 xxxx xxxx). + */ +static int op_imm(Q68State *state, uint32_t opcode) +{ + /* Check for bit-twiddling and illegal opcodes first */ + enum {OR = 0, AND, SUB, ADD, _BIT, EOR, CMP, _ILL} aluop; + aluop = opcode>>9 & 7; + if (aluop == _BIT) { + return op_bit(state, opcode); + } else if (aluop == _ILL) { + return op_ill(state, opcode); + } + + /* Get the instruction size */ + INSN_GET_SIZE; + if (size == 3) { + return op_ill(state, opcode); + } + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + + /* Fetch the immediate value */ + uint32_t imm = (uint16_t)IFETCH(state); + if (size == SIZE_B) { + imm &= 0xFF; + } else if (size == SIZE_L) { + imm = imm<<16 | (uint16_t)IFETCH(state); + } + + /* Fetch the EA operand (which may be SR or CCR) */ + int use_SR; + int cycles; + uint32_t ea_val; + if ((aluop==OR || aluop==AND || aluop==EOR) && (opcode & 0x3F) == 0x3C) { + /* xxxI #imm,SR (or CCR) use the otherwise-invalid form of an + * immediate value destination */ + if (size == SIZE_W && !(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } + use_SR = 1; + cycles = 8; // Total instruction time is 20 cycles + switch (size) { + case SIZE_B: ea_val = state->SR & 0xFF; break; + case SIZE_W: ea_val = state->SR; break; + default: return op_ill(state, opcode); + } + } else { + use_SR = 0; + ea_val = ea_get(state, opcode, size, 1, &cycles); + if (cycles < 0) { + return 0; + } + } + + /* Perform the operation */ + uint32_t result; + if (aluop == ADD || aluop == SUB) { + INSN_CLEAR_XCC(); + } else { + INSN_CLEAR_CC(); + } + switch (aluop) { + case OR: result = ea_val | imm; + break; + case AND: result = ea_val & imm; + break; + case EOR: result = ea_val ^ imm; + break; + case CMP: if (size == SIZE_L) { // CMPI takes less time in most cases + if (EA_MODE(opcode) != EA_DATA_REG) { + cycles -= 8; + } else { + cycles -= 2; + } + } else { + if (EA_MODE(opcode) != EA_DATA_REG) { + cycles -= 4; + } + } + /* fall through to... */ + case SUB: { result = (ea_val - imm) & valuemask; + if (((imm ^ ea_val) & (result ^ ea_val)) >> shift) { + state->SR |= SR_V; + } + if ((int)((imm >> shift) - (ea_val >> shift) + + (result >> shift)) > 0) { + state->SR |= SR_C; + if (aluop != CMP) { + state->SR |= SR_X; + } + } + break; + } + default: // case ADD + result = (ea_val + imm) & valuemask; + if (((ea_val ^ result) & (imm ^ result)) >> shift) { + state->SR |= SR_V; + } + if ((int)((ea_val >> shift) + (imm >> shift) + - (result >> shift)) > 0) { + state->SR |= SR_X | SR_C; + } + break; + } + INSN_SETNZ_SHIFT(result); + + /* Update the EA operand (if not CMPI) */ + if (aluop != CMP) { + if (use_SR) { + if (size == SIZE_W) { + set_SR(state, result); + } else { + state->SR &= 0xFF00; + state->SR |= result; + } + } else { + ea_set(state, opcode, size, result); + } + } + + /* All done */ + return (size==SIZE_L ? 16 : 8) + + (EA_MODE(opcode) == EA_DATA_REG ? 0 : 4) + cycles; +} + +/*************************************************************************/ + +/** + * op_bit: Bit-twiddling instructions (formats 0000 rrr1 xxxx xxxx and + * 0000 1000 xxxx xxxx). + */ +static int op_bit(Q68State *state, uint32_t opcode) +{ + /* Check early for MOVEP (coded as BTST/BCHG/BCLR/BSET Dn,An) */ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + if (opcode & 0x0100) { + return opMOVP(state, opcode); + } else { + return op_ill(state, opcode); + } + } + + enum {BTST = 0, BCHG = 1, BCLR = 2, BSET = 3} op = opcode>>6 & 3; + int cycles; + + /* Get the bit number to operate on */ + unsigned int bitnum; + if (opcode & 0x0100) { + /* Bit number in register */ + INSN_GET_REG; + bitnum = state->D[reg]; + cycles = 0; + } else { + bitnum = IFETCH(state); + cycles = 4; + } + + /* EA operand is 32 bits when coming from a register, 8 when from memory */ + int size; + switch (EA_MODE(opcode)) { + case EA_DATA_REG: + size = SIZE_L; + bitnum %= 32; + break; + default: + size = SIZE_B; + bitnum %= 8; + break; + } + int cycles_tmp; + uint32_t value = ea_get(state, opcode, size, 1, &cycles_tmp); + if (cycles_tmp < 0) { + return 0; + } + cycles += cycles_tmp; + if (size == SIZE_L && (op == BCLR || op == BTST)) { + cycles += 2; + } + + /* Perform the operation */ + if ((value >> bitnum) & 1) { + state->SR &= ~SR_Z; + } else { + state->SR |= SR_Z; + } + switch (op) { + case BTST: /* Nothing to do */ break; + case BCHG: value ^= 1 << bitnum; break; + case BCLR: value &= ~(1 << bitnum); break; + case BSET: value |= 1 << bitnum; break; + } + + /* Update EA operand (if not BTST) */ + if (op != BTST) { + ea_set(state, opcode, size, value); + } + + /* Return cycle count; note that the times for BCHG.L, BCLR.L, and + * BSET.L are maximums (though how they vary is undocumented) */ + return (op==BTST ? 4 : 8) + cycles; +} + +/*************************************************************************/ + +/** + * opMOVE: MOVE.[bwl] instruction (format {01,10,11}xx xxxx xxxx xxxx). + */ +static int opMOVE(Q68State *state, uint32_t opcode) +{ + const int size = (opcode>>12==1 ? SIZE_B : opcode>>12==2 ? SIZE_L : SIZE_W); + + int cycles_src; + const uint32_t data = ea_get(state, opcode, size, 0, &cycles_src); + if (cycles_src < 0) { + return 0; + } + + /* Rearrange the opcode bits so we can pass the destination EA to + * ea_resolve() */ + const uint32_t dummy_opcode = (opcode>>9 & 7) | (opcode>>3 & 0x38); + int cycles_dest; + if (EA_MODE(dummy_opcode) <= EA_ADDRESS_REG) { + cycles_dest = 0; + } else { + cycles_dest = ea_resolve(state, dummy_opcode, size, ACCESS_WRITE); + if (cycles_dest < 0) { + return op_ill(state, opcode); + } + } + + /* Update condition codes if the target is not an address register */ + if (EA_MODE(dummy_opcode) != EA_ADDRESS_REG) { + INSN_CLEAR_CC(); + INSN_SETNZ(size==SIZE_B ? (int8_t)data : + size==SIZE_W ? (int16_t)data : (int32_t)data); + } + + /* Update the destination EA and return */ + ea_set(state, dummy_opcode, size, data); + return 4 + cycles_src + cycles_dest; +} + +/*************************************************************************/ + +/** + * op4xxx: Miscellaneous instructions (format 0100 xxx0 xxxx xxxx). + */ +static int op4xxx(Q68State *state, uint32_t opcode) +{ + const unsigned int index = (opcode>>7 & 0x1C) | (opcode>>6 & 3); +#ifdef COUNT_OPCODES + q68_4xxx_ops[index]++; +#endif + return (*opcode_4xxx_table[index])(state, opcode); +} + +/*************************************************************************/ + +/** + * op_CHK: CHK instruction (format 0100 rrr1 10xx xxxx). + */ +static int op_CHK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + int size = SIZE_W; // Bit 7 == 0 indicates long mode on the 68020 + + int cycles; + int32_t upper; // Yes, it's signed + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + return op_ill(state, opcode); + } + upper = ea_get(state, opcode, size, 0, &cycles); + if (cycles < 0) { + return 0; + } + if (size == SIZE_W) { + upper = (int32_t)(int16_t)upper; + } + + int32_t value; + if (size == SIZE_W) { + value = (int32_t)(int16_t)state->D[reg]; + } else { + value = (int32_t)state->D[reg]; + } + if (value < 0) { + state->SR |= SR_N; + state->exception = EX_CHK; + return cycles; + } else if (value > upper) { + state->SR &= ~SR_N; + state->exception = EX_CHK; + return cycles; + } + + return 10 + cycles; +} + +/*************************************************************************/ + +/** + * op_LEA: LEA instruction (format 0100 rrr1 11xx xxxx). + */ +static int op_LEA(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + + /* Register, predecrement, postincrement, immediate modes are illegal */ + if (EA_MODE(opcode) == EA_DATA_REG + || EA_MODE(opcode) == EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT + || EA_MODE(opcode) == EA_PREDECREMENT + || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) + ) { + return op_ill(state, opcode); + } + + int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles + cycles += 2; + } + state->A[reg] = state->ea_addr; + return cycles; +} + +/*************************************************************************/ + +/** + * opADSQ: ADDQ and SUBQ instructions (format 0101 iiix xxxx xxxx). + */ +static int opADSQ(Q68State *state, uint32_t opcode) +{ + const int is_sub = opcode & 0x0100; + INSN_GET_COUNT; + INSN_GET_SIZE; + if (EA_MODE(opcode) == EA_ADDRESS_REG && size == 1) { + size = 2; // ADDQ.W #imm,An is equivalent to ADDQ.L #imm,An + } + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + int cycles; + uint32_t data = ea_get(state, opcode, size, 1, &cycles); + if (cycles < 0) { + return 0; + } + + uint32_t result; + if (is_sub) { + result = data - count; + } else { + result = data + count; + } + result &= valuemask; + if (EA_MODE(opcode) != EA_ADDRESS_REG) { + INSN_CLEAR_XCC(); + INSN_SETNZ_SHIFT(result); + if ((is_sub ? ~result & data : result & ~data) >> shift) { + state->SR |= SR_V; + } + if ((is_sub ? result & ~data : ~result & data) >> shift) { + state->SR |= SR_X | SR_C; + } + } + + ea_set(state, opcode, size, result); + return (size==SIZE_L || EA_MODE(opcode) == EA_ADDRESS_REG ? 8 : 4) + + (EA_MODE(opcode) >= EA_INDIRECT ? 4 : 0) + cycles; +} + +/*************************************************************************/ + +/** + * op_Scc: Scc instruction (format 0101 cccc 11xx xxxx). + */ +static int op_Scc(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + /* DBcc Dn,disp is coded as Scc An with an extension word */ + return opDBcc(state, opcode); + } + + INSN_GET_COND; + const int is_true = INSN_COND_TRUE(cond); + /* From the cycle counts, it looks like this is a standard read/write + * access rather than a write-only access */ + int cycles; + if (EA_MODE(opcode) == EA_DATA_REG) { + cycles = 0; + } else { + cycles = ea_resolve(state, opcode, SIZE_B, ACCESS_MODIFY); + if (cycles < 0) { + return op_ill(state, opcode); + } + } + ea_set(state, opcode, SIZE_B, is_true ? 0xFF : 0x00); + if (EA_MODE(opcode) == EA_DATA_REG) { + /* Scc Dn is a special case */ + return is_true ? 6 : 4; + } else { + return 8 + cycles; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_DBcc: DBcc instruction (format 0101 cccc 1100 1xxx). + */ +static int opDBcc(Q68State *state, uint32_t opcode) +{ + INSN_GET_COND; + const int is_true = INSN_COND_TRUE(cond); + INSN_GET_REG0; + INSN_GET_IMM16; + if (is_true) { + return 12; + } else if (--(*(WORD_OFS + (int16_t *)&state->D[reg0])) == -1) { + return 14; + } else { + state->PC = state->current_PC + imm16; + return 10; + } +} + +/*************************************************************************/ + +/** + * op_Bcc: Conditional branch instructions (format 0110 cccc dddd dddd). + */ +static int op_Bcc(Q68State *state, uint32_t opcode) +{ + INSN_GET_COND; + INSN_GET_DISP8; + int cycles = 0; + if (disp == 0) { + disp = (int16_t)IFETCH(state); + cycles = 4; + } + if (cond == COND_F) { + /* BF is really BSR */ +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return 0; + } +#endif + PUSH32(state, state->PC); + state->PC = state->current_PC + disp; + return 18; + } else if (INSN_COND_TRUE(cond)) { + state->PC = state->current_PC + disp; + return 10; + } else { + return 8 + cycles; + } +} + +/*************************************************************************/ + +/** + * opMOVQ: MOVEQ instruction (format 0111 rrr0 iiii iiii). + */ +static int opMOVQ(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_IMM8; + state->D[reg] = imm8; + INSN_CLEAR_CC(); + INSN_SETNZ(imm8); + return 4; +} + +/*************************************************************************/ + +/** + * op_alu: Non-immediate ALU instructions (format 1ooo rrrx xxxx xxxx for + * ooo = 000, 001, 011, 100, 101). + */ +static int op_alu(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + + /* Pass off special and invalid instructions early */ + if (size != 3) { + if ((opcode & 0xB130) == 0x9100) { + /* ADDX/SUBX are coded as ADD/SUB.* Dn, */ + return opADSX(state, opcode); + } + if ((opcode & 0xB1F0) == 0x8100) { + /* ABCD/SBCD are coded as AND/OR.b Dn, */ + return op_BCD(state, opcode); + } + if ((opcode & 0xF130) == 0xC100) { + /* EXG is coded as AND.[wl] Dn, */ + return op_EXG(state, opcode); + } + if ((opcode & 0xF130) == 0x8100) { + /* OR.[wl] Dn, is invalid on the 68000 (later PACK/UNPK) */ + return op_ill(state, opcode); + } + if ((opcode & 0xF138) == 0xB108 && (opcode>>6 & 3) != 3) { + /* CMPM is coded as EOR.* Dn, */ + return opCMPM(state, opcode); + } + } + + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + int ea_dest = opcode & 0x100; + int areg_dest = 0; // For ADDA/SUBA/CMPA + enum {OR, AND, EOR, CMP, SUB, ADD} aluop; + + /* Find the instruction for the opcode group */ + switch (opcode>>12) { + case 0x8: aluop = OR; break; + case 0x9: aluop = SUB; break; + case 0xB: aluop = (((opcode>>6)+1) & 7) <= 4 ? CMP : EOR; break; + case 0xC: aluop = AND; break; + default: aluop = ADD; break; // case 0xD + } + + /* Handle the special formats of ADDA/SUBA/CMPA */ + if ((aluop == ADD || aluop == SUB || aluop == CMP) && size == 3) { + size = ea_dest ? SIZE_L : SIZE_W; + ea_dest = 0; + areg_dest = 1; + } + + /* Retrieve the register and EA values */ + uint32_t reg_val = areg_dest ? state->A[reg] : (state->D[reg] & valuemask); + int cycles; + uint32_t ea_val = ea_get(state, opcode, size, ea_dest, &cycles); + if (cycles < 0) { + return 0; + } + if (size == SIZE_L || areg_dest) { + cycles += 4; + } + if (ea_dest) { + cycles += 4; + } else if ((aluop == CMP && areg_dest) + || (size == SIZE_L + && (EA_MODE(opcode) <= EA_ADDRESS_REG + || (EA_MODE(opcode) == EA_MISC + && EA_REG(opcode) == EA_MISC_IMMEDIATE)))) { + cycles -= 2; + } + + /* Perform the actual computation */ + uint32_t result; + if (!areg_dest || aluop == CMP) { + if (aluop == ADD || aluop == SUB) { + INSN_CLEAR_XCC(); + } else { + INSN_CLEAR_CC(); + } + } + switch (aluop) { + case OR: result = reg_val | ea_val; + break; + case AND: result = reg_val & ea_val; + break; + case EOR: result = reg_val ^ ea_val; + break; + case CMP: /* fall through to... */ + case SUB: { uint32_t src, dest; + if (areg_dest) { + /* CMPA/SUBA keep all 32 bits, and SUBA doesn't + * touch flags */ + src = ea_val; + dest = reg_val; + result = reg_val - ea_val; + if (aluop == SUB) { + break; + } + } else { + if (ea_dest) { + src = reg_val; + dest = ea_val; + } else { + src = ea_val; + dest = reg_val; + } + result = (dest - src) & valuemask; + } + if (((src ^ dest) & (result ^ dest)) >> shift) { + state->SR |= SR_V; + } + if ((int)((src >> shift) - (dest >> shift) + + (result >> shift)) > 0) { + state->SR |= SR_C; + if (aluop != CMP) { + state->SR |= SR_X; + } + } + break; + } + default: // case ADD + if (areg_dest) { + /* ADDA keeps all 32 bits and doesn't touch flags */ + result = reg_val + ea_val; + break; + } + result = (reg_val + ea_val) & valuemask; + if (((reg_val ^ result) & (ea_val ^ result)) >> shift) { + state->SR |= SR_V; + } + if ((int)((reg_val >> shift) + (ea_val >> shift) + - (result >> shift)) > 0) { + state->SR |= SR_X | SR_C; + } + break; + } // switch (aluop) + if (!areg_dest || aluop == CMP) { + INSN_SETNZ_SHIFT(result); + } + + /* Store the result in the proper place (if the instruction is not CMP) */ + if (aluop != CMP) { + if (ea_dest) { + ea_set(state, opcode, size, result); + } else if (areg_dest) { + state->A[reg] = result; + } else if (size == SIZE_B) { + *(BYTE_OFS + (uint8_t *)&state->D[reg]) = result; + } else if (size == SIZE_W) { + *(WORD_OFS + (uint16_t *)&state->D[reg]) = result; + } else { // size == SIZE_L + state->D[reg] = result; + } + } + + return 4 + cycles; +} + +/*************************************************************************/ + +/** + * op_DIV: DIVU and DIVS instructions (format 1000 rrrx 11xx xxxx). + */ +static int op_DIV(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + const int sign = opcode & (1<<8); + + state->SR &= ~SR_C; // Always cleared, even on exception + + int cycles; + const uint16_t divisor = ea_get(state, opcode, SIZE_W, 0, &cycles); + if (cycles < 0) { + return 0; + } + if (divisor == 0) { + state->exception = EX_DIVIDE_BY_ZERO; + return cycles; + } + + int32_t quotient, remainder; + if (sign) { + quotient = (int32_t)state->D[reg] / (int16_t)divisor; + remainder = (int32_t)state->D[reg] % (int16_t)divisor; + if (quotient < -0x8000 || quotient > 0x7FFF) { + state->SR |= SR_V; + } else { + state->SR &= ~SR_V; + } + } else { + quotient = state->D[reg] / divisor; + remainder = state->D[reg] % divisor; + if (quotient & 0xFFFF0000) { + state->SR |= SR_V; + } else { + state->SR &= ~SR_V; + } + } + + if (!(state->SR & SR_V)) { + state->D[reg] = (quotient & 0xFFFF) | (remainder << 16); + if (quotient & 0x8000) { + state->SR |= SR_N; + } else { + state->SR &= ~SR_N; + } + if (quotient == 0) { + state->SR |= SR_Z; + } else { + state->SR &= ~SR_Z; + } + } + /* The 68000 docs say that the timing difference between best and + * worst cases is less than 10%, so we just return the worst case */ + return (sign ? 158 : 140) + cycles; +} + +/*************************************************************************/ + +/** + * opAxxx: $Axxx illegal instruction set (format 1010 xxxx xxxx xxxx). + */ +static int opAxxx(Q68State *state, uint32_t opcode) +{ + state->exception = EX_LINE_1010; + return 0; +} + +/*************************************************************************/ + +/** + * op_MUL: MULU and MULS instructions (format 1100 rrrx 11xx xxxx). + */ +static int op_MUL(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + const int sign = opcode & (1<<8); + + int cycles; + const uint16_t data = ea_get(state, opcode, SIZE_W, 0, &cycles); + if (cycles < 0) { + return 0; + } + + if (sign) { + state->D[reg] = (int16_t)state->D[reg] * (int16_t)data; + } else { + state->D[reg] = (uint16_t)state->D[reg] * data; + } + INSN_CLEAR_CC(); + INSN_SETNZ(state->D[reg]); + + /* Precise timing varies with the effective address; the algorithm is + * implemented below for reference, but for typical usage it's probably + * not important to be exact */ +#ifdef MUL_PRECISE_TIMING // not normally defined + if (sign) { + uint32_t temp; + for (temp = (uint32_t)data << 1; temp != 0; temp >>= 1) { + if ((temp & 3) == 1 || (temp & 3) == 2) { + cycles += 2; + } + } + } else { + unsigned int temp; + for (temp = data; temp != 0; temp >>= 1) { + if (temp & 1) { + cycles += 2; + } + } + } + return 38 + cycles; +#else // !MUL_PRECISE_TIMING + return 54 + cycles; +#endif +} + +/*************************************************************************/ + +/** + * opshft: Shift and rotate instructions (format 1110 xxxx xxxx xxxx). + */ +static int opshft(Q68State *state, uint32_t opcode) +{ + const int is_left = opcode & 0x0100; + INSN_GET_SIZE; + INSN_GET_COUNT; + INSN_GET_REG0; + int is_memory; + int type; // Shift/rotate type (0=ASL/ASR, 1=LSL/LSR, ...) + uint32_t data; + int cycles; + + if (size == 3) { + /* Memory shift/rotate */ + is_memory = 1; + if ((opcode & 0x0800) || EA_MODE(opcode) <= EA_ADDRESS_REG) { + return op_ill(state, opcode); + } + size = SIZE_W; + type = opcode>>9 & 3; + count = 1; + data = ea_get(state, opcode, size, 1, &cycles); + if (cycles < 0) { + return 0; + } + } else { + /* Register shift/rotate */ + is_memory = 0; + type = opcode>>3 & 3; + if (opcode & 0x0020) { + INSN_GET_REG; + count = state->D[reg] & 63; + } + data = size==SIZE_B ? (uint8_t) state->D[reg0] : + size==SIZE_W ? (uint16_t)state->D[reg0] : state->D[reg0]; + cycles = 0; + } + cycles += count*2; + + INSN_CLEAR_CC(); + if (count > 0) { + const int nbits = (size==SIZE_B ? 8 : size==SIZE_W ? 16 : 32); + switch (type) { + case 0: // ASL/ASR + state->SR &= ~SR_X; + if (is_left) { + int V = 0, C; + /* Have to shift bit by bit to detect overflow */ + for (; count > 0; count--) { + C = (data >> (nbits-1)) & 1; + data <<= 1; + V |= (C ^ (data >> (nbits-1))) & 1; + } + if (V) { + state->SR |= SR_V; + } + if (C) { + state->SR |= SR_X | SR_C; + } + } else { + if (size == SIZE_B) { // Sign extend if necessary + data = (int8_t)data; + } else if (size == SIZE_W) { + data = (int16_t)data; + } + if (count > nbits) { + count = 32; // Some systems break with a shift count >32 + } + data = (int32_t)data >> (count-1); + if (data & 1) { + state->SR |= SR_X | SR_C; + } + data = (int32_t)data >> 1; + } + break; + case 1: // LSL/LSR + state->SR &= ~SR_X; + if (count > nbits) { + data = 0; + } else if (is_left) { + data <<= count-1; + if ((data >> (nbits-1)) & 1) { + state->SR |= SR_X | SR_C; + } + data <<= 1; + } else { + data = (int32_t)data >> (count-1); + if (data & 1) { + state->SR |= SR_X | SR_C; + } + data = (int32_t)data >> 1; + } + break; + case 2: { // ROXL/ROXR + uint32_t X = (state->SR >> SR_X_SHIFT) & 1; + state->SR &= ~SR_X; + if (is_left) { + for (; count > 0; count--) { + const int new_X = (data >> (nbits-1)) & 1; + data = (data << 1) | X; + X = new_X; + } + } else { + for (; count > 0; count--) { + const int new_X = data & 1; + data = (data >> 1) | (X << (nbits-1)); + X = new_X; + } + } + if (X) { + state->SR |= SR_C | SR_X; + } + break; + } + default: { // (case 3) ROL/ROR + count %= nbits; + if (is_left) { + data = (data << count) | (data >> (nbits - count)); + if ((data >> (nbits-1)) & 1) { + state->SR |= SR_C; + } + data <<= 1; + } else { + data = (data >> count) | (data << (nbits - count)); + if (data & 1) { + state->SR |= SR_C; + } + data = (int32_t)data >> 1; + } + break; + } + } // switch (type) + } else { // count == 0 + if (type == 2 && (state->SR & SR_X)) { + state->SR |= SR_C; + } + } + INSN_SETNZ(size==SIZE_B ? (int8_t) data : + size==SIZE_W ? (int16_t)data : data); + + if (is_memory) { + ea_set(state, opcode, size, data); + } else { + switch (size) { + case SIZE_B: *(BYTE_OFS + (uint8_t *)&state->D[reg0]) = data; break; + case SIZE_W: *(WORD_OFS + (uint16_t *)&state->D[reg0]) = data; break; + default: state->D[reg0] = data; break; + } + } + return (size==SIZE_L ? 8 : 6) + cycles; +} + +/*************************************************************************/ + +/** + * opFxxx: $Fxxx illegal instruction set (format 1111 xxxx xxxx xxxx). + */ +static int opFxxx(Q68State *state, uint32_t opcode) +{ + state->exception = EX_LINE_1111; + return 0; +} + +/*************************************************************************/ +/*********************** $4xxx group instructions ************************/ +/*************************************************************************/ + +/** + * op4alu: Single-operand ALU instructions in the $4xxx opcode range + * (format 0100 ooo0 ssxx xxxx for ooo = 000, 001, 010, 011, 101). + */ +static int op4alu(Q68State *state, uint32_t opcode) +{ + INSN_GET_SIZE; + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + enum {NEGX = 0, CLR = 1, NEG = 2, NOT = 3, TST = 5} aluop; + aluop = opcode>>9 & 7; + + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + /* Retrieve the EA value */ + int cycles; + uint32_t value = ea_get(state, opcode, size, 1, &cycles); + if (cycles < 0) { + return 0; + } + if (aluop != TST) { + if (EA_MODE(opcode) == EA_DATA_REG) { + if (size == SIZE_L) { + cycles += 2; + } + } else { + cycles += (size == SIZE_L) ? 8 : 4; + } + } + + /* Perform the actual computation */ + uint32_t result; + if (aluop == NEGX) { + state->SR &= ~(SR_N | SR_V | SR_C); // Z is never set, only cleared + } else { + INSN_CLEAR_CC(); + } + switch (aluop) { + case NEGX: { int X = (state->SR >> SR_X_SHIFT) & 1; + result = (0 - value - X) & valuemask; + if (result != 0) { + state->SR &= ~SR_Z; + } + goto NEG_common; + } + case NEG: result = (0 - value) & valuemask; + if (result == 0) { + state->SR |= SR_Z; + } else { + state->SR &= ~SR_Z; + } + NEG_common: + if (result >> shift) { + state->SR |= SR_N; + } + if ((value & result) >> shift) { + state->SR |= SR_V; + } + if ((value | result) != 0) { + state->SR |= SR_X | SR_C; + } else { + state->SR &= ~SR_X; + } + break; + case CLR: result = 0; + state->SR |= SR_Z; + break; + case NOT: result = ~value & valuemask; + INSN_SETNZ_SHIFT(result); + break; + default: // case TST + result = value; // Avoid a compiler warning + INSN_SETNZ_SHIFT(value); + break; + } // switch (aluop) + + /* Store the result in the proper place (if the instruction is not TST) */ + if (aluop != TST) { + ea_set(state, opcode, size, result); + } + + return 4 + cycles; +} + +/*************************************************************************/ + +/** + * opMVSR: MOVE to/from SR/CCR instructions (format 0100 0xx0 11xx xxxx). + */ +static int opMVSR(Q68State *state, uint32_t opcode) +{ + int is_CCR; + int ea_dest; + int cycles; + switch (opcode>>9 & 3) { + case 0: // MOVE SR, + is_CCR = 0; + ea_dest = 1; + cycles = (EA_MODE(opcode) == EA_DATA_REG) ? 6 : 8; + break; + case 1: // Undefined (MOVE CCR, on 68010) + return op_ill(state, opcode); + case 2: // MOVE ,CCR + is_CCR = 1; + ea_dest = 0; + cycles = 12; + break; + default: // MOVE ,SR (case 3) + if (!(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } + is_CCR = 0; + ea_dest = 0; + cycles = 12; + break; + } + + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + /* Motorola docs say the address is read before being written, even + * for the SR, format; also, the access size is a word even for + * CCR operations. */ + int cycles_tmp; + uint16_t value = ea_get(state, opcode, SIZE_W, ea_dest, &cycles_tmp); + if (cycles_tmp < 0) { + return 0; + } + cycles += cycles_tmp; + + if (ea_dest) { + uint16_t value = state->SR; + if (is_CCR) { + value &= 0x00FF; + } + ea_set(state, opcode, SIZE_W, value); + } else { + if (!is_CCR) { + set_SR(state, value); + } + } + return cycles; +} + +/*************************************************************************/ + +/** + * opNBCD: NBCD instruction (format 0100 1000 00xx xxxx). + */ +static int opNBCD(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + int cycles; + int value = ea_get(state, opcode, SIZE_B, 1, &cycles); + if (cycles < 0) { + return 0; + } + + int result; + int X = (state->SR >> SR_X_SHIFT) & 1; + state->SR &= ~(SR_X | SR_C); // Z is never set, only cleared + /* Slightly convoluted to match what a real 68000 does (see SBCD) */ + int res_low = 0 - (value & 0x0F) - X; + int borrow = 0; + if (res_low < 0) { + res_low += 10; + borrow = 1<<4; + } + int res_high = 0 - (value & 0xF0) - borrow; + if (res_high < 0) { + res_high += 10<<4; + state->SR |= SR_X | SR_C; + } + result = res_high + res_low; + if (result < 0) { + state->SR |= SR_X | SR_C; + } + result &= 0xFF; + if (result != 0) { + state->SR &= ~SR_Z; + } + + ea_set(state, opcode, SIZE_B, result); + return (EA_MODE(opcode) == EA_DATA_REG ? 6 : 8) + cycles; +} + +/*************************************************************************/ + +/** + * op_PEA: PEA instruction (format 0100 1000 01xx xxxx). + */ +static int op_PEA(Q68State *state, uint32_t opcode) +{ + /* SWAP is coded as PEA Dn */ + if (EA_MODE(opcode) == EA_DATA_REG) { + return opSWAP(state, opcode); + } + + if (EA_MODE(opcode) == EA_DATA_REG + || EA_MODE(opcode) == EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT + || EA_MODE(opcode) == EA_PREDECREMENT + || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) + ) { + return op_ill(state, opcode); + } + + int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles + cycles += 2; + } +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return 0; + } +#endif + PUSH32(state, state->ea_addr); + return 8 + cycles; +} + +/*************************************************************************/ + +/** + * opSWAP: SWAP instruction (format 0100 1000 0100 0rrr). + */ +static int opSWAP(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + state->D[reg0] = state->D[reg0]>>16 | state->D[reg0]<<16; + INSN_CLEAR_CC(); + INSN_SETNZ(state->D[reg0]); + return 4; +} + +/*************************************************************************/ + +/** + * op_TAS: TAS instruction (format 0100 1010 11xx xxxx). Also covers the + * ILLEGAL instruction (format 0100 1010 1111 1100). + */ +static int op_TAS(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + int cycles; + int8_t value = ea_get(state, opcode, SIZE_B, 1, &cycles); + if (cycles < 0) { + /* Note that the ILLEGAL instruction is coded as TAS #imm, so it + * will be rejected as unwriteable by ea_get() */ + return 0; + } + + INSN_CLEAR_CC(); + INSN_SETNZ(value); + ea_set(state, opcode, SIZE_B, value | 0x80); + return (EA_MODE(opcode) == EA_DATA_REG ? 4 : 10) + cycles; +} + +/*************************************************************************/ + +/** + * op_EXT: EXT instruction (format 0100 1000 1s00 0rrr). + */ +static int op_EXT(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + INSN_CLEAR_CC(); + if (opcode & 0x0040) { + int16_t value = (int16_t)state->D[reg0]; + state->D[reg0] = (int32_t)value; + INSN_SETNZ(value); + } else { + int8_t value = (int16_t)state->D[reg0]; + *(WORD_OFS + (int16_t *)&state->D[reg0]) = (int16_t)value; + INSN_SETNZ(value); + } + return 4; +} + +/*************************************************************************/ + +/** + * op_STM: MOVEM reglist, (i.e. STore Multiple) instruction (format + * 0100 1000 1sxx xxxx). + */ +static int op_STM(Q68State *state, uint32_t opcode) +{ + /* EXT.* is coded as MOVEM.* reglist,Dn */ + if (EA_MODE(opcode) == EA_DATA_REG) { + return op_EXT(state, opcode); + } + + unsigned int regmask = IFETCH(state); + int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; + if (EA_MODE(opcode) <= EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT // Not allowed for store + ) { + return op_ill(state, opcode); + } + + /* Avoid modifying the register during address resolution */ + uint16_t safe_ea; + if (EA_MODE(opcode) == EA_PREDECREMENT) { + safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); + } else { + safe_ea = opcode; + } + int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_WRITE); + if (cycles < 0) { + return op_ill(state, opcode); + } +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->ea_addr & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->ea_addr; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return 0; + } +#endif + + if (EA_MODE(opcode) == EA_PREDECREMENT) { + /* Register order is reversed in predecrement mode */ + int reg; + for (reg = 15; reg >= 0; reg--, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + state->ea_addr -= 2; + WRITE16(state, state->ea_addr, state->DA[reg]); + cycles += 4; + } else { + state->ea_addr -= 4; + WRITE32(state, state->ea_addr, state->DA[reg]); + cycles += 8; + } + } + } + state->A[EA_REG(opcode)] = state->ea_addr; + } else { + int reg; + for (reg = 0; reg < 16; reg++, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + WRITE16(state, state->ea_addr, state->DA[reg]); + state->ea_addr += 2; + cycles += 4; + } else { + WRITE32(state, state->ea_addr, state->DA[reg]); + state->ea_addr += 4; + cycles += 8; + } + } + } + } + + return 4 + cycles; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_LDM: MOVEM ,reglist (i.e. LoaD Multiple) instruction (format + * 0100 1100 1sxx xxxx). + */ +static int op_LDM(Q68State *state, uint32_t opcode) +{ + unsigned int regmask = IFETCH(state); + int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; + if (EA_MODE(opcode) <= EA_ADDRESS_REG + || EA_MODE(opcode) == EA_PREDECREMENT // Not allowed for load + ) { + return op_ill(state, opcode); + } + + /* Avoid modifying the register during address resolution */ + uint16_t safe_ea; + if (EA_MODE(opcode) == EA_POSTINCREMENT) { + safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); + } else { + safe_ea = opcode; + } + int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->ea_addr & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->ea_addr; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + return 0; + } +#endif + + int reg; + for (reg = 0; reg < 16; reg++, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + int16_t value = READS16(state, state->ea_addr); + if (reg < 8) { + *(WORD_OFS + (uint16_t *)&state->D[reg]) = value; + } else { + state->A[reg-8] = (int32_t)value; + } + state->ea_addr += 2; + cycles += 4; + } else { + state->DA[reg] = READU32(state, state->ea_addr); + state->ea_addr += 4; + cycles += 8; + } + } + } + if (EA_MODE(opcode) == EA_POSTINCREMENT) { + state->A[EA_REG(opcode)] = state->ea_addr; + } + + return 8 + cycles; +} + +/*************************************************************************/ + +/** + * opmisc: $4xxx-group misc. instructions (format 0100 1110 01xx xxxx). + */ +static int opmisc(Q68State *state, uint32_t opcode) +{ + const unsigned int index = (opcode>>3 & 7); + return (*opcode_4E4x_table[index])(state, opcode); +} + +/*-----------------------------------------------------------------------*/ + +/** + * opTRAP: TRAP #n instruction (format 0100 1110 0100 nnnn). + */ +static int opTRAP(Q68State *state, uint32_t opcode) +{ + state->exception = EX_TRAP + (opcode & 0x000F); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opLINK: LINK instruction (format 0100 1110 0101 0rrr). + */ +static int opLINK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + int16_t disp = IFETCH(state); +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return 0; + } +#endif + PUSH32(state, state->A[reg0]); + state->A[reg0] = state->A[7]; + state->A[7] += disp; + return 16; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opUNLK: UNLK instruction (format 0100 1110 0101 1rrr). + */ +static int opUNLK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + /* FIXME: What happens if A7 is used as the register? I.e. does the + * postincrement happen before or after the value is written to + * the destination register? The Motorola docs could be read + * both ways, so going by the literal operation sequence here. */ + state->A[7] = state->A[reg0]; +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + return 0; + } +#endif + state->A[reg0] = READU32(state, state->A[7]); + state->A[7] += 4; + return 12; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opMUSP: MOVE An,USP and MOVE USP,An instructions (format + * 0100 1110 0110 xrrr). + */ +static int opMUSP(Q68State *state, uint32_t opcode) +{ + if (!(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } + + INSN_GET_REG0; + if (opcode & 0x0008) { + state->USP = state->A[reg0]; + } else { + state->A[reg0] = state->USP; + } + return 4; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op4E7x: Instructions with opcodes $4E70-$4E77 that don't fit anywhere + * else. + */ +static int op4E7x(Q68State *state, uint32_t opcode) +{ + switch (opcode & 7) { + case 0: // $4E70 RESET + if (!(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } + return 132; + case 1: // $4E71 NOP + return 4; + case 2: // $4E72 STOP + if (!(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } + state->halted = 1; + set_SR(state, IFETCH(state)); + return 4; + case 3: { // $4E73 RTE + if (!(state->SR & SR_S)) { + state->exception = EX_PRIVILEGE_VIOLATION; + return 0; + } +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + return 0; + } +#endif + uint16_t new_SR = POP16(state); + state->PC = POP32(state); + set_SR(state, new_SR); + return 20; + } + case 5: // $4E75 RTS +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + return 0; + } +#endif + state->PC = POP32(state); + return 16; + case 6: // $4E76 TRAPV + if (state->SR & SR_V) { + state->exception = EX_TRAPV; + return 0; + } + return 4; + case 7: { // $4E77 RTR +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_READ; + return 0; + } +#endif + state->SR &= 0xFF00; + state->SR |= POP16(state) & 0x00FF; + state->PC = POP32(state); + return 20; + } + default: // $4E74 RTD is 68010 only + return op_ill(state, opcode); + } +} + +/*************************************************************************/ + +/** + * opjump: JSR and JMP instructions (format 0100 1110 1xxx xxxx). + */ +static int opjump(Q68State *state, uint32_t opcode) +{ + int is_jsr = ~opcode & 0x0040; + + /* JMP is essentially identical to LEA PC, and has the same + * constraints. JSR is equivalent to MOVE.L PC,-(A7) followed by a + * JMP to the address. Both use a separate timing table, however. */ + + int cycles; + switch (EA_MODE(opcode)) { + case EA_INDIRECT: + cycles = 8; + break; + case EA_DISPLACEMENT: + cycles = 10; + break; + case EA_INDEX: + cycles = 14; + break; + case EA_MISC: + switch (EA_REG(opcode)) { + case EA_MISC_ABSOLUTE_W: + cycles = 10; + break; + case EA_MISC_ABSOLUTE_L: + cycles = 12; + break; + case EA_MISC_PCREL: + cycles = 10; + break; + case EA_MISC_PCREL_INDEX: + cycles = 14; + break; + default: + return op_ill(state, opcode); + } + break; + default: + return op_ill(state, opcode); + } + + ea_resolve(state, opcode, SIZE_W, ACCESS_READ); // cannot fail + if (is_jsr) { +#ifndef Q68_DISABLE_ADDRESS_ERROR + if (state->A[7] & 1) { + state->exception = EX_ADDRESS_ERROR; + state->fault_addr = state->A[7]; + state->fault_status = FAULT_STATUS_IN_DATA + | FAULT_STATUS_RW_WRITE; + return 0; + } +#endif + cycles += 8; + PUSH32(state, state->PC); + } + state->PC = state->ea_addr; + return cycles; +} + +/*************************************************************************/ +/******************* Other miscellaneous instructions ********************/ +/*************************************************************************/ + +/** + * opMOVP: MOVEP instruction (0000 rrr1 xx00 1rrr). + */ +static int opMOVP(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + int to_memory = opcode & 0x0080; + int is_long = opcode & 0x0040; + int16_t disp = IFETCH(state); + uint32_t addr = state->A[reg0] + disp; + + if (to_memory) { + uint32_t data = state->D[reg]; + if (is_long) { + WRITE8(state, addr+0, data>>24); + WRITE8(state, addr+2, data>>16); + WRITE8(state, addr+4, data>> 8); + WRITE8(state, addr+6, data>> 0); + } else { + WRITE8(state, addr+0, data>> 8); + WRITE8(state, addr+2, data>> 0); + } + } else { + uint32_t data; + if (is_long) { + data = READU8(state, addr+0) << 24; + data |= READU8(state, addr+2) << 16; + data |= READU8(state, addr+4) << 8; + data |= READU8(state, addr+6) << 0; + } else { + data = READU8(state, addr+0) << 8; + data |= READU8(state, addr+2) << 0; + } + state->D[reg] = data; + } + + return is_long ? 24 : 16; +} + +/*************************************************************************/ + +/** + * opADSX: ADDX/SUBX instructions (1x01 rrr1 ss00 xrrr). + */ +static int opADSX(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + INSN_GET_REG0; + const int is_add = opcode & 0x4000; + const int is_memory = opcode & 0x0008; + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + + const uint16_t src_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; + const uint16_t dest_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; + int dummy; + uint32_t src = ea_get(state, src_ea, size, 0, &dummy); + uint32_t dest = ea_get(state, dest_ea, size, 1, &dummy); + + uint32_t result; + int X = (state->SR >> SR_X_SHIFT) & 1; + state->SR &= ~(SR_X | SR_N | SR_V | SR_C); // Z is never set, only cleared + if (is_add) { + result = (dest + src + X) & valuemask; + if (((src ^ result) & (dest ^ result)) >> shift) { + state->SR |= SR_V; + } + if ((int)((src >> shift) + (dest >> shift) - (result >> shift)) > 0) { + state->SR |= SR_X | SR_C; + } + } else { + result = (dest - src - X) & valuemask; + if (((src ^ dest) & (result ^ dest)) >> shift) { + state->SR |= SR_V; + } + if ((int)((src >> shift) - (dest >> shift) + (result >> shift)) > 0) { + state->SR |= SR_X | SR_C; + } + } + if (result >> shift) { + state->SR |= SR_N; + } + if (result != 0) { + state->SR &= ~SR_Z; + } + + ea_set(state, dest_ea, size, result); + return (is_memory ? (size==SIZE_L ? 30 : 18) : (size==SIZE_L ? 8 : 4)); +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_BCD: ABCD/SBCD instructions (1x00 rrr1 0000 xrrr). + */ +static int op_BCD(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + const int is_add = opcode & 0x4000; + const int is_memory = opcode & 0x0008; + + const uint16_t src_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; + const uint16_t dest_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; + int dummy; + uint8_t src = ea_get(state, src_ea, SIZE_B, 0, &dummy); + uint8_t dest = ea_get(state, dest_ea, SIZE_B, 1, &dummy); + + int result; + int X = (state->SR >> SR_X_SHIFT) & 1; + state->SR &= ~(SR_X | SR_C); // Z is never set, only cleared + if (is_add) { + result = (dest & 0x0F) + (src & 0x0F) + X; + if (result >= 10) { + /* This seems to be correct w.r.t. a real 68000 and invalid data + * (e.g. 0x0F + 0x0F): it only checks for a carry of 1 */ + result += 6; + } + result += (dest & 0xF0) + (src & 0xF0); + if (result >= 10<<4) { + result -= 10<<4; + state->SR |= SR_X | SR_C; + } + } else { + /* Slightly convoluted to match what a real 68000 does */ + int res_low = (dest & 0x0F) - (src & 0x0F) - X; + int borrow = 0; + if (res_low < 0) { + res_low += 10; + borrow = 1<<4; + } + int res_high = (dest & 0xF0) - (src & 0xF0) - borrow; + if (res_high < 0) { + res_high += 10<<4; + state->SR |= SR_X | SR_C; + } + result = res_high + res_low; + if (result < 0) { + state->SR |= SR_X | SR_C; + } + } + result &= 0xFF; + if (result != 0) { + state->SR &= ~SR_Z; + } + + ea_set(state, dest_ea, SIZE_B, result); + return is_memory ? 18 : 6; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opCMPM: CMPM instructions (1011 rrr1 ss00 1rrr). + */ +static int opCMPM(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + INSN_GET_REG0; + const int bytes = SIZE_TO_BYTES(size); + const int shift = bytes*8 - 1; + const uint32_t valuemask = ~(~1 << shift); + + const uint16_t src_ea = EA_POSTINCREMENT<<3 | reg0; + const uint16_t dest_ea = EA_POSTINCREMENT<<3 | reg; + int dummy; + uint32_t src = ea_get(state, src_ea, size, 0, &dummy); + uint32_t dest = ea_get(state, dest_ea, size, 0, &dummy); + + uint32_t result = (dest - src) & valuemask; + INSN_CLEAR_XCC(); + INSN_SETNZ_SHIFT(result); + if (((src ^ dest) & (result ^ dest)) >> shift) { + state->SR |= SR_V; + } + if ((int)((src >> shift) - (dest >> shift) + (result >> shift)) > 0) { + state->SR |= SR_C; + } + + return size==SIZE_L ? 20 : 12; +} + +/*************************************************************************/ + +/** + * op_EXG: EXG instruction (1100 rrr1 xx00 1rrr). + */ +static int op_EXG(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + const int mode = opcode & 0xF8; + + if (mode == 0x40) { + const uint32_t tmp = state->D[reg]; + state->D[reg] = state->D[reg0]; + state->D[reg0] = tmp; + } else if (mode == 0x48) { + const uint32_t tmp = state->A[reg]; + state->A[reg] = state->A[reg0]; + state->A[reg0] = tmp; + } else if (mode == 0x88) { + const uint32_t tmp = state->D[reg]; + state->D[reg] = state->A[reg0]; + state->A[reg0] = tmp; + } else { + return op_ill(state, opcode); + } + return 6; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-disasm.c b/yabause/src/q68/q68-disasm.c new file mode 100644 index 0000000000..9aab2ffcf4 --- /dev/null +++ b/yabause/src/q68/q68-disasm.c @@ -0,0 +1,836 @@ +/* src/q68/q68-disasm.c: MC68000 disassembly routines + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "q68.h" +#include "q68-const.h" +#include "q68-internal.h" + +/*************************************************************************/ +/********************** 68k instruction disassembly **********************/ +/*************************************************************************/ + +/* Disassembly table. The first entry matching a given instruction is used. */ + +static const struct { + uint16_t mask, test; // Entry matches when (opcode & mask) == test + const char *format; // Instruction format +} instructions[] = { + + /* ALU immediate */ + + {0xFFFF, 0x003C, "ORI.B #,CCR"}, + {0xFFC0, 0x0000, "ORI.B #,"}, + {0xFFFF, 0x007C, "ORI.W #,SR"}, + {0xFFC0, 0x0040, "ORI.W #,"}, + {0xFFC0, 0x0080, "ORI.L #,"}, + {0xFFFF, 0x023C, "ANDI.B #,CCR"}, + {0xFFC0, 0x0200, "ANDI.B #,"}, + {0xFFFF, 0x027C, "ANDI.W #,SR"}, + {0xFFC0, 0x0240, "ANDI.W #,"}, + {0xFFC0, 0x0280, "ANDI.L #,"}, + {0xFFC0, 0x0400, "SUBI.B #,"}, + {0xFFC0, 0x0440, "SUBI.W #,"}, + {0xFFC0, 0x0480, "SUBI.L #,"}, + {0xFFC0, 0x0600, "ADDI.B #,"}, + {0xFFC0, 0x0640, "ADDI.W #,"}, + {0xFFC0, 0x0680, "ADDI.L #,"}, + {0xFFFF, 0x0A3C, "EORI.B #,CCR"}, + {0xFFC0, 0x0A00, "EORI.B #,"}, + {0xFFFF, 0x0A7C, "EORI.W #,SR"}, + {0xFFC0, 0x0A40, "EORI.W #,"}, + {0xFFC0, 0x0A80, "EORI.L #,"}, + {0xFFC0, 0x0C00, "CMPI.B #,"}, + {0xFFC0, 0x0C40, "CMPI.W #,"}, + {0xFFC0, 0x0C80, "CMPI.L #,"}, + + /* Bit twiddling and MOVEP */ + + {0xF1F8, 0x0108, "MOVEP.W (A),D"}, + {0xF1F8, 0x0148, "MOVEP.L (A),D"}, + {0xF1F8, 0x0188, "MOVEP.W D,(A)"}, + {0xF1F8, 0x01C8, "MOVEP.L D,(A)"}, + {0xFFC0, 0x0800, "BTST #,"}, + {0xFFC0, 0x0840, "BCHG #,"}, + {0xFFC0, 0x0880, "BCLR #,"}, + {0xFFC0, 0x08C0, "BSET #,"}, + {0xF1C0, 0x0100, "BTST D,"}, + {0xF1C0, 0x0140, "BCHG D,"}, + {0xF1C0, 0x0180, "BCLR D,"}, + {0xF1C0, 0x01C0, "BSET D,"}, + + /* MOVE */ + + {0xF1C0, 0x1040, "MOVEA.B ,A"}, + {0xF000, 0x1000, "MOVE.B ,"}, + {0xF1C0, 0x2040, "MOVEA.L ,A"}, + {0xF000, 0x2000, "MOVE.L ,"}, + {0xF1C0, 0x3040, "MOVEA.W ,A"}, + {0xF000, 0x3000, "MOVE.W ,"}, + + /* Miscellaneous */ + + {0xFFC0, 0x4000, "NEGX.B "}, + {0xFFC0, 0x4040, "NEGX.W "}, + {0xFFC0, 0x4080, "NEGX.L "}, + {0xFFC0, 0x40C0, "MOVE.W SR,"}, + + {0xFFC0, 0x4200, "CLR.B "}, + {0xFFC0, 0x4240, "CLR.W "}, + {0xFFC0, 0x4280, "CLR.L "}, + {0xFFC0, 0x42C0, "???"}, + + {0xFFC0, 0x4400, "NEG.B "}, + {0xFFC0, 0x4440, "NEG.W "}, + {0xFFC0, 0x4480, "NEG.L "}, + {0xFFC0, 0x44C0, "MOVE.W CCR,"}, + + {0xFFC0, 0x4600, "NOT.B "}, + {0xFFC0, 0x4640, "NOT.W "}, + {0xFFC0, 0x4680, "NOT.L "}, + {0xFFC0, 0x46C0, "MOVE.W ,SR"}, + + {0xFFF8, 0x4808, "???"}, + {0xFFC0, 0x4800, "NBCD.B "}, + {0xFFF8, 0x4840, "SWAP.W D"}, + {0xFFF8, 0x4848, "???"}, + {0xFFC0, 0x4840, "PEA.L "}, + {0xFFF8, 0x4880, "EXT.W D"}, + {0xFFF8, 0x48A0, "MOVEM.W ,-(A)"}, + {0xFFC0, 0x4880, "MOVEM.W ,"}, + {0xFFF8, 0x48C0, "EXT.L D"}, + {0xFFF8, 0x48E0, "MOVEM.L ,-(A)"}, + {0xFFC0, 0x48C0, "MOVEM.L ,"}, + + {0xFFC0, 0x4A00, "TST.B "}, + {0xFFC0, 0x4A40, "TST.W "}, + {0xFFC0, 0x4A80, "TST.L "}, + {0xFFFF, 0x4AFC, "ILLEGAL"}, + {0xFFC0, 0x4AC0, "TAS "}, + + {0xFFC0, 0x4C80, "MOVEM.W ,"}, + {0xFFC0, 0x4CC0, "MOVEM.L ,"}, + + {0xFFF0, 0x4E40, "TRAP #"}, + {0xFFF8, 0x4E50, "LINK #,A"}, + {0xFFF8, 0x4E58, "UNLK A"}, + {0xFFF8, 0x4E60, "MOVE USP,A"}, + {0xFFF8, 0x4E68, "MOVE A,USP"}, + {0xFFFF, 0x4E70, "RESET"}, + {0xFFFF, 0x4E71, "NOP"}, + {0xFFFF, 0x4E72, "STOP"}, + {0xFFFF, 0x4E73, "RTE"}, + {0xFFFF, 0x4E75, "RTS"}, + {0xFFFF, 0x4E76, "TRAPV"}, + {0xFFFF, 0x4E77, "RTR"}, + {0xFFC0, 0x4E80, "JSR "}, + {0xFFC0, 0x4EC0, "JMP "}, + + {0xF1C0, 0x4180, "CHK.W D,"}, + {0xF1C0, 0x41C0, "LEA.L ,A"}, + + /* ADDQ/SUBQ/Scc/DBcc */ + + {0xF1C0, 0x5000, "ADDQ.B #,"}, + {0xF1C0, 0x5040, "ADDQ.W #,"}, + {0xF1C0, 0x5080, "ADDQ.L #,"}, + {0xF1C0, 0x5100, "SUBQ.B #,"}, + {0xF1C0, 0x5140, "SUBQ.W #,"}, + {0xF1C0, 0x5180, "SUBQ.L #,"}, + {0xFFF8, 0x50C8, "DBT D,"}, + {0xFFC0, 0x50C0, "ST.B "}, + {0xFFF8, 0x51C8, "DBRA D,"}, + {0xFFC0, 0x51C0, "SF.B "}, + {0xFFF8, 0x52C8, "DBHI D,"}, + {0xFFC0, 0x52C0, "SHI.B "}, + {0xFFF8, 0x53C8, "DBLS D,"}, + {0xFFC0, 0x53C0, "SLS.B "}, + {0xFFF8, 0x54C8, "DBCC D,"}, + {0xFFC0, 0x54C0, "SCC.B "}, + {0xFFF8, 0x55C8, "DBCS D,"}, + {0xFFC0, 0x55C0, "SCS.B "}, + {0xFFF8, 0x56C8, "DBNE D,"}, + {0xFFC0, 0x56C0, "SNE.B "}, + {0xFFF8, 0x57C8, "DBEQ D,"}, + {0xFFC0, 0x57C0, "SEQ.B "}, + {0xFFF8, 0x58C8, "DBVC D,"}, + {0xFFC0, 0x58C0, "SVC.B "}, + {0xFFF8, 0x59C8, "DBVS D,"}, + {0xFFC0, 0x59C0, "SVS.B "}, + {0xFFF8, 0x5AC8, "DBPL D,"}, + {0xFFC0, 0x5AC0, "SPL.B "}, + {0xFFF8, 0x5BC8, "DBMI D,"}, + {0xFFC0, 0x5BC0, "SMI.B "}, + {0xFFF8, 0x5CC8, "DBLT D,"}, + {0xFFC0, 0x5CC0, "SLT.B "}, + {0xFFF8, 0x5DC8, "DBGE D,"}, + {0xFFC0, 0x5DC0, "SGE.B "}, + {0xFFF8, 0x5EC8, "DBLE D,"}, + {0xFFC0, 0x5EC0, "SLE.B "}, + {0xFFF8, 0x5FC8, "DBGT D,"}, + {0xFFC0, 0x5FC0, "SGT.B "}, + + /* BRA/BSR/Bcc */ + + {0xFFFF, 0x6000, "BRA.W "}, + {0xFF00, 0x6000, "BRA.S "}, + {0xFFFF, 0x6100, "BSR.W "}, + {0xFF00, 0x6100, "BSR.S "}, + {0xFFFF, 0x6200, "BHI.W "}, + {0xFF00, 0x6200, "BHI.S "}, + {0xFFFF, 0x6300, "BLS.W "}, + {0xFF00, 0x6300, "BLS.S "}, + {0xFFFF, 0x6400, "BCC.W "}, + {0xFF00, 0x6400, "BCC.S "}, + {0xFFFF, 0x6500, "BCS.W "}, + {0xFF00, 0x6500, "BCS.S "}, + {0xFFFF, 0x6600, "BNE.W "}, + {0xFF00, 0x6600, "BNE.S "}, + {0xFFFF, 0x6700, "BEQ.W "}, + {0xFF00, 0x6700, "BEQ.S "}, + {0xFFFF, 0x6800, "BVC.W "}, + {0xFF00, 0x6800, "BVC.S "}, + {0xFFFF, 0x6900, "BVS.W "}, + {0xFF00, 0x6900, "BVS.S "}, + {0xFFFF, 0x6A00, "BPL.W "}, + {0xFF00, 0x6A00, "BPL.S "}, + {0xFFFF, 0x6B00, "BMI.W "}, + {0xFF00, 0x6B00, "BMI.S "}, + {0xFFFF, 0x6C00, "BLT.W "}, + {0xFF00, 0x6C00, "BLT.S "}, + {0xFFFF, 0x6D00, "BGE.W "}, + {0xFF00, 0x6D00, "BGE.S "}, + {0xFFFF, 0x6E00, "BLE.W "}, + {0xFF00, 0x6E00, "BLE.S "}, + {0xFFFF, 0x6F00, "BGT.W "}, + {0xFF00, 0x6F00, "BGT.S "}, + + /* MOVEQ */ + + {0xF100, 0x7000, "MOVEQ #,D"}, + + /* ALU non-immediate,ABCD/SBCD etc. */ + + {0xF1F8, 0x8100, "SBCD.B D,D"}, + {0xF1F8, 0x8108, "SBCD.B -(A),-(A)"}, + {0xF1F0, 0x8140, "???"}, + {0xF1F0, 0x8180, "???"}, + {0xF1C0, 0x8000, "OR.B ,D"}, + {0xF1C0, 0x8040, "OR.W ,D"}, + {0xF1C0, 0x8080, "OR.L ,D"}, + {0xF1C0, 0x80C0, "DIVU ,D"}, + {0xF1C0, 0x8100, "OR.B D,"}, + {0xF1C0, 0x8140, "OR.W D,"}, + {0xF1C0, 0x8180, "OR.L D,"}, + {0xF1C0, 0x81C0, "DIVS ,D"}, + + {0xF1F8, 0x9100, "SUBX.B D,D"}, + {0xF1F8, 0x9108, "SUBX.B -(A),-(A)"}, + {0xF1F8, 0x9140, "SUBX.W D,D"}, + {0xF1F8, 0x9148, "SUBX.W -(A),-(A)"}, + {0xF1F8, 0x9180, "SUBX.L D,D"}, + {0xF1F8, 0x9188, "SUBX.L -(A),-(A)"}, + {0xF1C0, 0x9000, "SUB.B ,D"}, + {0xF1C0, 0x9040, "SUB.W ,D"}, + {0xF1C0, 0x9080, "SUB.L ,D"}, + {0xF1C0, 0x90C0, "SUBA.W ,A"}, + {0xF1C0, 0x9100, "SUB.B D,"}, + {0xF1C0, 0x9140, "SUB.W D,"}, + {0xF1C0, 0x9180, "SUB.L D,"}, + {0xF1C0, 0x91C0, "SUBA.L ,A"}, + + {0xF1F8, 0xB108, "CMPM.B -(A),-(A)"}, + {0xF1F8, 0xB148, "CMPM.W -(A),-(A)"}, + {0xF1F8, 0xB188, "CMPM.L -(A),-(A)"}, + {0xF1C0, 0xB000, "CMP.B ,D"}, + {0xF1C0, 0xB040, "CMP.W ,D"}, + {0xF1C0, 0xB080, "CMP.L ,D"}, + {0xF1C0, 0xB0C0, "CMPA.W ,A"}, + {0xF1C0, 0xB100, "CMP.B D,"}, + {0xF1C0, 0xB140, "CMP.W D,"}, + {0xF1C0, 0xB180, "CMP.L D,"}, + {0xF1C0, 0xB1C0, "CMPA.L ,A"}, + + {0xF1F8, 0xC100, "ABCD.B D,D"}, + {0xF1F8, 0xC108, "ABCD.B -(A),-(A)"}, + {0xF1F8, 0xC140, "EXG.L D,D"}, + {0xF1F8, 0xC148, "EXG.L A,A"}, + {0xF1F8, 0xC180, "???"}, + {0xF1F8, 0xC188, "EXG.L A,D"}, + {0xF1C0, 0xC000, "AND.B ,D"}, + {0xF1C0, 0xC040, "AND.W ,D"}, + {0xF1C0, 0xC080, "AND.L ,D"}, + {0xF1C0, 0xC0C0, "MULU ,D"}, + {0xF1C0, 0xC100, "AND.B D,"}, + {0xF1C0, 0xC140, "AND.W D,"}, + {0xF1C0, 0xC180, "AND.L D,"}, + {0xF1C0, 0xC1C0, "MULS ,D"}, + + {0xF1F8, 0xD100, "ADDX.B D,D"}, + {0xF1F8, 0xD108, "ADDX.B -(A),-(A)"}, + {0xF1F8, 0xD140, "ADDX.W D,D"}, + {0xF1F8, 0xD148, "ADDX.W -(A),-(A)"}, + {0xF1F8, 0xD180, "ADDX.L D,D"}, + {0xF1F8, 0xD188, "ADDX.L -(A),-(A)"}, + {0xF1C0, 0xD000, "ADD.B ,D"}, + {0xF1C0, 0xD040, "ADD.W ,D"}, + {0xF1C0, 0xD080, "ADD.L ,D"}, + {0xF1C0, 0xD0C0, "ADDA.W ,A"}, + {0xF1C0, 0xD100, "ADD.B D,"}, + {0xF1C0, 0xD140, "ADD.W D,"}, + {0xF1C0, 0xD180, "ADD.L D,"}, + {0xF1C0, 0xD1C0, "ADDA.L ,A"}, + + /* Shift/rotate instructions */ + + {0xF1F8, 0xE000, "ASR.B #,D"}, + {0xF1F8, 0xE008, "LSR.B #,D"}, + {0xF1F8, 0xE010, "ROXR.B #,D"}, + {0xF1F8, 0xE018, "ROR.B #,D"}, + {0xF1F8, 0xE020, "ASR.B D,D"}, + {0xF1F8, 0xE028, "LSR.B D,D"}, + {0xF1F8, 0xE030, "ROXR.B D,D"}, + {0xF1F8, 0xE038, "ROR.B D,D"}, + + {0xF1F8, 0xE040, "ASR.W #,D"}, + {0xF1F8, 0xE048, "LSR.W #,D"}, + {0xF1F8, 0xE050, "ROXR.W #,D"}, + {0xF1F8, 0xE058, "ROR.W #,D"}, + {0xF1F8, 0xE060, "ASR.W D,D"}, + {0xF1F8, 0xE068, "LSR.W D,D"}, + {0xF1F8, 0xE070, "ROXR.W D,D"}, + {0xF1F8, 0xE078, "ROR.W D,D"}, + + {0xF1F8, 0xE080, "ASR.L #,D"}, + {0xF1F8, 0xE088, "LSR.L #,D"}, + {0xF1F8, 0xE090, "ROXR.L #,D"}, + {0xF1F8, 0xE098, "ROR.L #,D"}, + {0xF1F8, 0xE0A0, "ASR.L D,D"}, + {0xF1F8, 0xE0A8, "LSR.L D,D"}, + {0xF1F8, 0xE0B0, "ROXR.L D,D"}, + {0xF1F8, 0xE0B8, "ROR.L D,D"}, + + {0xF1F8, 0xE100, "ASL.B #,D"}, + {0xF1F8, 0xE108, "LSL.B #,D"}, + {0xF1F8, 0xE110, "ROXL.B #,D"}, + {0xF1F8, 0xE118, "ROL.B #,D"}, + {0xF1F8, 0xE120, "ASL.B D,D"}, + {0xF1F8, 0xE128, "LSL.B D,D"}, + {0xF1F8, 0xE130, "ROXL.B D,D"}, + {0xF1F8, 0xE138, "ROL.B D,D"}, + + {0xF1F8, 0xE140, "ASL.W #,D"}, + {0xF1F8, 0xE148, "LSL.W #,D"}, + {0xF1F8, 0xE150, "ROXL.W #,D"}, + {0xF1F8, 0xE158, "ROL.W #,D"}, + {0xF1F8, 0xE160, "ASL.W D,D"}, + {0xF1F8, 0xE168, "LSL.W D,D"}, + {0xF1F8, 0xE170, "ROXL.W D,D"}, + {0xF1F8, 0xE178, "ROL.W D,D"}, + + {0xF1F8, 0xE180, "ASL.L #,D"}, + {0xF1F8, 0xE188, "LSL.L #,D"}, + {0xF1F8, 0xE190, "ROXL.L #,D"}, + {0xF1F8, 0xE198, "ROL.L #,D"}, + {0xF1F8, 0xE1A0, "ASL.L D,D"}, + {0xF1F8, 0xE1A8, "LSL.L D,D"}, + {0xF1F8, 0xE1B0, "ROXL.L D,D"}, + {0xF1F8, 0xE1B8, "ROL.L D,D"}, + + {0xFFC0, 0xE0C0, "ASR.W "}, + {0xFFC0, 0xE1C0, "ASL.W "}, + {0xFFC0, 0xE2C0, "LSR.W "}, + {0xFFC0, 0xE3C0, "LSL.W "}, + {0xFFC0, 0xE4C0, "ROXR.W "}, + {0xFFC0, 0xE5C0, "ROXL.W "}, + {0xFFC0, 0xE6C0, "ROR.W "}, + {0xFFC0, 0xE7C0, "ROL.W "}, +}; + +/*************************************************************************/ + +/** + * q68_disassemble: Disassembles the instruction at the given address. + * Returns "???" if the address or opcode is invalid. + * + * [Parameters] + * state: Processor state block + * address: Address of instruction to disassemble + * nwords_ret: Pointer to variable to receive length in words of the + * instruction (NULL permitted) + * [Return value] + * String containined disassembled instruction + * [Notes] + * The returned string is only valid until the next call to this function. + */ +const char *q68_disassemble(Q68State *state, uint32_t address, + int *nwords_ret) +{ + const uint32_t base_address = address; + static char outbuf[1000]; + + if (address % 2 != 0) { // Odd addresses are invalid + if (nwords_ret) { + *nwords_ret = 1; + } + return "???"; + } + + uint16_t opcode = READU16(state, address); + address += 2; + const char *format = NULL; + int i; + for (i = 0; i < lenof(instructions); i++) { + if ((opcode & instructions[i].mask) == instructions[i].test) { + format = instructions[i].format; + break; + } + } + if (!format) { + if (nwords_ret) { + *nwords_ret = 1; + } + return "???"; + } + + int outlen = 0; +#define APPEND_CHAR(ch) do { \ + if (outlen < sizeof(outbuf)-1) { \ + outbuf[outlen++] = (ch); \ + outbuf[outlen] = 0; \ + } \ +} while (0) +#define APPEND(fmt,...) do { \ + outlen += snprintf(&outbuf[outlen], sizeof(outbuf)-outlen, \ + fmt , ## __VA_ARGS__); \ + if (outlen > sizeof(outbuf)-1) { \ + outlen = sizeof(outbuf)-1; \ + } \ +} while (0) + + int inpos = 0; + while (format[inpos] != 0) { + if (format[inpos] == '<') { + char tagbuf[100]; + int end = inpos+1; + for (; format[end] != 0 && format[end] != '>'; end++) { + if (end - (inpos+1) >= sizeof(tagbuf)) { + break; + } + } + memcpy(tagbuf, &format[inpos+1], end - (inpos+1)); + tagbuf[end - (inpos+1)] = 0; + if (format[end] != 0) { + end++; + } + inpos = end; + if (strncmp(tagbuf,"ea",2) == 0) { + int mode, reg; + char size; // 'b', 'w', or 'l' + if (strncmp(tagbuf,"ea2",3) == 0) { // 2nd EA of MOVE insns + mode = opcode>>6 & 7; + reg = opcode>>9 & 7; + size = tagbuf[4]; + } else { + mode = opcode>>3 & 7; + reg = opcode>>0 & 7; + size = tagbuf[3]; + } + switch (mode) { + case 0: + APPEND("D%d", reg); + break; + case 1: + APPEND("A%d", reg); + break; + case 2: + APPEND("(A%d)", reg); + break; + case 3: + APPEND("(A%d)+", reg); + break; + case 4: + APPEND("-(A%d)", reg); + break; + case 5: { + int16_t disp = READS16(state, address); + address += 2; + APPEND("%d(A%d)", disp, reg); + break; + } + case 6: { + uint16_t ext = READU16(state, address); + address += 2; + const int iregtype = ext>>15; + const int ireg = ext>>12 & 7; + const int iregsize = ext>>11; + const int8_t disp = ext & 0xFF; + APPEND("%d(A%d,%c%d.%c)", disp, reg, + iregtype ? 'A' : 'D', ireg, iregsize ? 'l' : 'w'); + break; + } + case 7: + switch (reg) { + case 0: { + const uint16_t abs = READU16(state, address); + address += 2; + APPEND("($%X).w", abs); + break; + } + case 1: { + const uint32_t abs = READU32(state, address); + address += 4; + APPEND("($%X).l", abs); + break; + } + case 2: { + int16_t disp = READS16(state, address); + address += 2; + APPEND("$%X(PC)", (base_address+2) + disp); + break; + } + case 3: { + uint16_t ext = READU16(state, address); + address += 2; + const int iregtype = ext>>15; + const int ireg = ext>>12 & 7; + const int iregsize = ext>>11; + const int8_t disp = ext & 0xFF; + APPEND("$%X(PC,%c%d.%c)", (base_address+2) + disp, + iregtype ? 'A' : 'D', ireg, iregsize ? 'l' : 'w'); + break; + } + case 4: { + uint32_t imm; + if (size == 'l') { + imm = READU32(state, address); + address += 4; + } else { + imm = READU16(state, address); + address += 2; + } + APPEND("#%s%X", imm<10 ? "" : "$", imm); + break; + } + default: + APPEND("???"); + break; + } + } + } else if (strcmp(tagbuf,"reg") == 0) { + APPEND("%d", opcode>>9 & 7); + } else if (strcmp(tagbuf,"reg0") == 0) { + APPEND("%d", opcode>>0 & 7); + } else if (strcmp(tagbuf,"count") == 0) { + APPEND("%d", opcode>>9 & 7 ?: 8); + } else if (strcmp(tagbuf,"trap") == 0) { + APPEND("%d", opcode>>0 & 15); + } else if (strcmp(tagbuf,"quick8") == 0) { + APPEND("%d", (int8_t)(opcode & 0xFF)); + } else if (strncmp(tagbuf,"imm8",4) == 0) { + uint8_t imm8 = READU16(state, address); // Upper 8 bits ignored + imm8 &= 0xFF; + address += 2; + if (tagbuf[4] == 'd') { + APPEND("%d", imm8); + } else if (tagbuf[4] == 'x') { + APPEND("$%02X", imm8); + } else { + APPEND("%s%X", imm8<10 ? "" : "$", imm8); + } + } else if (strncmp(tagbuf,"imm16",5) == 0) { + uint16_t imm16 = READU16(state, address); + address += 2; + if (tagbuf[5] == 'd') { + APPEND("%d", imm16); + } else if (tagbuf[5] == 'x') { + APPEND("$%04X", imm16); + } else { + APPEND("%s%X", imm16<10 ? "" : "$", imm16); + } + } else if (strcmp(tagbuf,"pcrel8") == 0) { + int8_t disp8 = opcode & 0xFF; + APPEND("$%X", (base_address+2) + disp8); + } else if (strcmp(tagbuf,"pcrel16") == 0) { + int16_t disp16 = READS16(state, address); + address += 2; + APPEND("$%X", (base_address+2) + disp16); + } else if (strcmp(tagbuf,"reglist") == 0 + || strcmp(tagbuf,"tsilger") == 0) { + uint16_t reglist = READU16(state, address); + address += 2; + if (strcmp(tagbuf,"tsilger") == 0) { // "reglist" backwards + /* Predecrement-mode register list, so flip it around */ + uint16_t temp = reglist; + reglist = 0; + while (temp) { + reglist <<= 1; + if (temp & 1) { + reglist |= 1; + } + temp >>= 1; + } + } + char listbuf[3*16]; // Buffer for generating register list + unsigned int listlen = 0; // strlen(listbuf) + unsigned int last = 0; // State of the previous bit + unsigned int regnum = 0; // Current register number (0-15) + while (reglist) { + if (reglist & 1) { + if (last) { + if (listlen >= 3 && listbuf[listlen-3] == '-') { + listlen -= 2; + } else { + listbuf[listlen++] = '-'; + } + } else { + if (listlen > 0) { + listbuf[listlen++] = '/'; + } + } + listbuf[listlen++] = regnum<8 ? 'D' : 'A'; + listbuf[listlen++] = '0' + (regnum % 8); + } + last = reglist & 1; + regnum++; + reglist >>= 1; + } + listbuf[listlen] = 0; + APPEND("%s", listbuf); + } else { + APPEND("<%s>", tagbuf); + } + } else { + APPEND_CHAR(format[inpos]); + inpos++; + } + } + + if (nwords_ret) { + *nwords_ret = (address - base_address) / 2; + } + return outbuf; +} + +/*************************************************************************/ +/*********************** Execution tracing support ***********************/ +/*************************************************************************/ + +/* Processor state block to use in tracing */ +static Q68State *state; + +/* File pointer for trace output */ +static FILE *logfile; + +/* Cycle accumulator */ +static uint64_t total_cycles; + +/* Range of cycles to trace */ +static const uint64_t trace_start = 000000000ULL; // First cycle to trace +static const uint64_t trace_stop = 600000000ULL; // Last cycle to trace + 1 + +/*-----------------------------------------------------------------------*/ + +/** + * q68_trace_init: Initialize the tracing code. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +void q68_trace_init(Q68State *state_) +{ + state = state_; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_trace_add_cycles: Add the given number of cycles to the global + * accumulator. + * + * [Parameters] + * cycles: Number of cycles to add + * [Return value] + * None + */ +extern void q68_trace_add_cycles(int32_t cycles) +{ + total_cycles += cycles; +} + +/*-----------------------------------------------------------------------*/ + +#ifdef PSP +/** + * HEXIT: Helper routine for q68_trace() to print a value in hexadecimal. + * See q68_trace() for why we don't just use printf(). + */ +static inline void HEXIT(char * const ptr, uint32_t val, int ndigits) +{ + while (ndigits-- > 0) { + const int digit = val & 0xF; + val >>= 4; + ptr[ndigits] = (digit>9 ? digit+7+'0' : digit+'0'); + } +} +#endif + +/*----------------------------------*/ + +/** + * q68_trace: Output a trace for the instruction at the current PC. + * + * [Parameters] + * None + * [Return value] + * None + */ +void q68_trace(void) +{ + const uint64_t cycles = total_cycles + state->cycles; + + if (cycles < trace_start) { + + /* Before first instruction: do nothing */ + + } else if (cycles >= trace_stop) { + + /* After last instruction: close log file if it's open */ + if (logfile) { +#ifdef __linux__ + pclose(logfile); +#else + fclose(logfile); +#endif + logfile = NULL; + } + + } else { + + if (!logfile) { +#ifdef __linux__ + logfile = popen("gzip -3 >q68.log.gz", "w"); +#else + logfile = fopen("q68.log", "w"); +#endif + if (UNLIKELY(!logfile)) { + perror("Failed to open trace logfile"); + return; + } + setvbuf(logfile, NULL, _IOFBF, 65536); + } + + int nwords = 1, i; + const char *disassembled = q68_disassemble(state, state->PC, &nwords); + +#ifdef PSP // because the cleaner fprintf() version is just too slow + int dislen = strlen(disassembled); + static char buf1[] = + "......: .... .... .... .......................... SR=.... ..... [..........]\n"; + static char buf2[] = + " D: ........ ........ ........ ........ ........ ........ ........ ........\n" + " A: ........ ........ ........ ........ ........ ........ ........ ........\n"; + + if (nwords > 3) { // We can only fit 3 words on the line + nwords = 3; + } + HEXIT(&buf1[0], state->PC, 6); + for (i = 0; i < nwords; i++) { + HEXIT(&buf1[8+5*i], READU16(state, state->PC+2*i), 4); + } + if (i < 3) { + memset(&buf1[8+5*i], ' ', 4+5*(2-i)); + } + if (dislen > 26) { // Pathologically long text needs special handling + fprintf(logfile, "%.22s %-26s SR=%04X %c%c%c%c%c [%10lld]\n", + buf1, disassembled, (int)state->SR, + state->SR & SR_X ? 'X' : '.', state->SR & SR_N ? 'N' : '.', + state->SR & SR_Z ? 'Z' : '.', state->SR & SR_V ? 'V' : '.', + state->SR & SR_C ? 'C' : '.', (unsigned long long)cycles); + } else { + memcpy(&buf1[24], disassembled, dislen); + if (dislen < 26) { + memset(&buf1[24+dislen], ' ', 26-dislen); + } + HEXIT(&buf1[55], state->SR, 4); + buf1[60] = state->SR & SR_X ? 'X' : '.'; + buf1[61] = state->SR & SR_N ? 'N' : '.'; + buf1[62] = state->SR & SR_Z ? 'Z' : '.'; + buf1[63] = state->SR & SR_V ? 'V' : '.'; + buf1[64] = state->SR & SR_C ? 'C' : '.'; + snprintf(&buf1[68], sizeof(buf1)-68, "%10lld]\n", + (unsigned long long)cycles); + fwrite(buf1, 1, strlen(buf1), logfile); + } + for (i = 0; i < 8; i++) { + HEXIT(&buf2[ 7+9*i], state->D[i], 8); + HEXIT(&buf2[86+9*i], state->A[i], 8); + } + fwrite(buf2, 1, sizeof(buf2)-1, logfile); +#else // !PSP + char hexbuf[100]; + int hexlen = 0; + + if (nwords > 3) { // We can only fit 3 words on the line + nwords = 3; + } + for (i = 0; i < nwords && hexlen < sizeof(hexbuf)-5; i++) { + hexlen += snprintf(hexbuf+hexlen, sizeof(hexbuf)-hexlen, + "%s%04X", hexlen==0 ? "" : " ", + (int)READU16(state, state->PC+2*i)); + } + + fprintf(logfile, "%06X: %-14s %-26s SR=%04X %c%c%c%c%c [%10llu]\n" + " D: %08X %08X %08X %08X %08X %08X %08X %08X\n" + " A: %08X %08X %08X %08X %08X %08X %08X %08X\n", + (int)state->PC, hexbuf, disassembled, (int)state->SR, + state->SR & SR_X ? 'X' : '.', state->SR & SR_N ? 'N' : '.', + state->SR & SR_Z ? 'Z' : '.', state->SR & SR_V ? 'V' : '.', + state->SR & SR_C ? 'C' : '.', (unsigned long long)cycles, + (int)state->D[0], (int)state->D[1], (int)state->D[2], + (int)state->D[3], (int)state->D[4], (int)state->D[5], + (int)state->D[6], (int)state->D[7], + (int)state->A[0], (int)state->A[1], (int)state->A[2], + (int)state->A[3], (int)state->A[4], (int)state->A[5], + (int)state->A[6], (int)state->A[7] + ); +#endif // PSP + + } // current_cycles >= trace_start && current_cycles < trace_stop +} + +/*************************************************************************/ +/*************************************************************************/ + + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-internal.h b/yabause/src/q68/q68-internal.h new file mode 100644 index 0000000000..48db390df7 --- /dev/null +++ b/yabause/src/q68/q68-internal.h @@ -0,0 +1,644 @@ +/* src/q68/q68-internal.h: Internal declarations/definitions used by Q68 + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_INTERNAL_H +#define Q68_INTERNAL_H + +#ifndef Q68_CONST_H +# include "q68-const.h" +#endif + +/*************************************************************************/ +/******************** Defines controlling compilation ********************/ +/*************************************************************************/ + +/** + * Q68_USE_JIT: When defined, enables the use of dynamic translation. + * Defining this on a CPU without dynamic translation support will result + * in a compilation error. + * + * See q68-jit.h for additional settings specific to dynamic translation. + */ +// #define Q68_USE_JIT + +/** + * Q68_JIT_OPTIMIZE_FLAGS: When defined, allows the dynamic translator to + * skip setting the condition flags (XNZVC) after an instruction when it + * can prove that doing so will have no effect on program execution (e.g. + * because the following instruction overwrites the flags). This will + * cause an instruction-by-instruction execution trace to differ from an + * actual 68000, though the program behavior will remain unchanged + * (barring any bugs in the optimization). + */ +#define Q68_JIT_OPTIMIZE_FLAGS + +/** + * Q68_JIT_LOOSE_TIMING: When defined, allows the dynamic translator to + * take some leeway in checking execution timing, such that instructions + * may continue to be executed even after the cycle limit has been reached. + * This allows the translated code to execute more quickly, but will + * slightly alter the timing of responses to external events such as + * interrupts. + */ +#define Q68_JIT_LOOSE_TIMING + +/** + * Q68_OPTIMIZE_IDLE: When defined, optimizes certain idle loops to + * improve performance in JIT mode. Enabling this option slightly alters + * execution timing. + */ +#define Q68_OPTIMIZE_IDLE + +/** + * Q68_JIT_VERBOSE: When defined, outputs some status messages considered + * useful in debugging or optimizing the JIT core. + */ +// #define Q68_JIT_VERBOSE + +/** + * Q68_DISABLE_ADDRESS_ERROR: When defined, disables the generation of + * address error exceptions for unaligned word/longword accesses. The + * behavior for such an access will depend on how the host system handles + * unaligned accesses, and may include the host program itself crashing. + */ +// #define Q68_DISABLE_ADDRESS_ERROR + +/** + * Q68_TRACE: When defined, the execution of every instruction will be + * traced to the file "q68.log" in the current directory. (On Linux, the + * trace will be written in compressed form to "q68.log.gz".) + */ +// #define Q68_TRACE + +/*************************************************************************/ +/************************* Generic helper macros *************************/ +/*************************************************************************/ + +/* Offset of a byte and word within a native long */ +#ifdef WORDS_BIGENDIAN +# define BYTE_OFS 3 +# define WORD_OFS 1 +#else +# define BYTE_OFS 0 +# define WORD_OFS 0 +#endif + +/* Return the length of an array */ +#define lenof(a) (sizeof((a)) / sizeof(*(a))) + +/* Compiler attribute to mark functions as not to be inlined */ +#ifdef __GNUC__ +# define NOINLINE __attribute__((noinline)) +#else +# define NOINLINE /*nothing*/ +#endif + +/* Macro to tell the compiler that a certain condition is likely or + * unlikely to occur */ +#ifdef __GNUC__ +# define LIKELY(x) (__builtin_expect(!!(x), 1)) +# define UNLIKELY(x) (__builtin_expect(!!(x), 0)) +#else +# define LIKELY(x) (x) +# define UNLIKELY(x) (x) +#endif + +/* Debug/error message macro. DMSG("message",...) prints to stderr a line + * in the form: + * func_name(file:line): message + * printf()-style format tokens and arguments are allowed, and no newline + * is required at the end. The format string must be a literal string + * constant. */ +#define DMSG(msg,...) \ + fprintf(stderr, "%s(%s:%d): " msg "\n", __FUNCTION__, __FILE__, __LINE__ \ + , ## __VA_ARGS__) + +/*************************************************************************/ +/******************* Processor state block definition ********************/ +/*************************************************************************/ + +typedef struct Q68JitEntry_ Q68JitEntry; // For JIT code +typedef struct Q68JitBlacklist_ Q68JitBlacklist; // For JIT code + +struct Q68State_ { + + /**** Data directly reflecting the current CPU state ****/ + + /* Registers */ + union { + struct { + uint32_t D[8]; + uint32_t A[8]; + }; + uint32_t DA[16]; // For fast accessing (A0 = D8, A1 = D9, etc.) + }; + uint32_t PC; + uint32_t SR; + uint32_t USP, SSP; + + /* "Current PC" for this instruction (address of opcode + 2) */ + uint32_t current_PC; + + /* Effective address used by the current instruction */ + uint32_t ea_addr; + + /* Pending exception, to be taken after the current instruction has + * been processed (zero if none) */ + unsigned int exception; + /* Auxiliary exception data used for a bus error or address error */ + uint32_t fault_addr; + uint16_t fault_opcode; + uint16_t fault_status; + + /* Nonzero if virtual processor is halted (due to either a STOP + * instruction or a double fault); see Q68_HALTED_* */ + unsigned int halted; + + /* Current interrupt request level */ + unsigned int irq; + + /* Number of clock cycles executed so far */ + uint32_t cycles; + + /**** Environment settings ****/ + + /* Native memory allocation functions */ + void *(*malloc_func)(size_t size); + void *(*realloc_func)(void *ptr, size_t size); + void (*free_func)(void *ptr); + + /* 68k memory read/write functions */ + Q68ReadFunc *readb_func, *readw_func; + Q68WriteFunc *writeb_func, *writew_func; + + /* Native cache flushing function (for JIT) */ + void (*jit_flush)(void); + + /**** JIT-related data ****/ + + /* Currently executing JIT block (NULL = none) */ + Q68JitEntry *jit_running; + + /* Nonzero if JIT routine needs to abort because the underlying 68000 + * code was modified (e.g. by a self-modifying routine) */ + unsigned int jit_abort; + + /* Hash table of translated code segments, hashed by 68000 start address */ + Q68JitEntry *jit_table; // Record buffer + Q68JitEntry **jit_hashchain; // Hash collision chains + + /* Total size of translated data */ + int32_t jit_total_data; // Signed to protect against its going negative + + /* Internal timestamp used by JIT LRU logic */ + uint32_t jit_timestamp; + + /* List of self-modifying blocks not to be translated (zero in both + * address fields indicates a free entry) */ + struct { + uint32_t m68k_start; // Code start address in 68000 address space + uint32_t m68k_end; // Code end address in 68000 address space + uint32_t timestamp; // Time this entry was added + } jit_blacklist[Q68_JIT_BLACKLIST_SIZE]; + + /* Nonzero if the current PC is inside a blacklisted block */ + unsigned int jit_in_blist; + + /* When jit_in_blacklist != 0, indicates the relevant jit_blacklist[] + * array entry index */ + unsigned int jit_blist_num; + + /* Call stack for efficient handling of subroutine calls */ + unsigned int jit_callstack_top; // Index of next entry to write + struct { + uint32_t return_PC; // PC on return from subroutine (0 = free) + Q68JitEntry *return_entry; // JIT block containing native code + void *return_native; // Native address to jump to + } jit_callstack[Q68_JIT_CALLSTACK_SIZE]; + + /* Buffer for tracking translated code blocks */ + uint8_t jit_pages[1<<(24-(Q68_JIT_PAGE_BITS+3))]; + +}; + +/*-----------------------------------------------------------------------*/ + +/* Constants used in the Q68State.halted field */ + +enum { + Q68_HALTED_NONE = 0, // Processor is running normally + Q68_HALTED_STOP, // Processor halted because of a STOP instruction + Q68_HALTED_DOUBLE_FAULT, // Processor halted because of a double fault +}; + +/*-----------------------------------------------------------------------*/ + +/* Macros for accessing Q68State.jit_pages[] (page is assumed to be valid) */ + +#define JIT_PAGE_SET(state,page) \ + ((state)->jit_pages[(page)>>3] |= 1<<((page)&7)) +#define JIT_PAGE_CLEAR(state,page) \ + ((state)->jit_pages[(page)>>3] &= ~(1<<((page)&7))) +#define JIT_PAGE_TEST(state,page) \ + ((state)->jit_pages[(page)>>3] & 1<<((page)&7)) + +/*************************************************************************/ +/************************ JIT interface functions ************************/ +/*************************************************************************/ + +/** + * q68_jit_init: Allocate memory for JIT data. Must be called before any + * other JIT function. + * + * [Parameters] + * state: Processor state block + * [Return value] + * Nonzero on success, zero on error + */ +extern int q68_jit_init(Q68State *state); + +/** + * q68_jit_reset: Reset the dynamic translation state, clearing out all + * previously stored data. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +extern void q68_jit_reset(Q68State *state); + +/** + * q68_jit_cleanup: Destroy all JIT-related data. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +extern void q68_jit_cleanup(Q68State *state); + +/** + * q68_jit_translate: Dynamically translate a block of instructions + * starting at the given address. If a translation already exists for the + * given address, it is cleared. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * Translated block to be passed to q68_jit_run(), or NULL on error + */ +extern Q68JitEntry *q68_jit_translate(Q68State *state, uint32_t address); + +/** + * q68_jit_find: Find the translated block for a given address, if any. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * Translated block to be passed to q68_jit_run(), or NULL if no such + * block exists + */ +extern Q68JitEntry *q68_jit_find(Q68State *state, uint32_t address); + +/** + * q68_jit_run: Run translated 68000 code. + * + * [Parameters] + * state: Processor state block + * cycle_limit: Clock cycle limit on execution (code will stop when + * state->cycles >= cycles) + * address_ptr: Pointer to translated block to execute; will be cleared + * to NULL on return if the end of the block was reached + * [Return value] + * None + */ +extern void q68_jit_run(Q68State *state, uint32_t cycle_limit, + Q68JitEntry **entry_ptr); + +/** + * q68_jit_clear: Clear any translation beginning at the given address. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * None + */ +extern void q68_jit_clear(Q68State *state, uint32_t address); + +/** + * q68_jit_clear_page: Clear any translation which occurs in the JIT page + * containing the given address. Intended for use on writes from external + * sources. + * + * [Parameters] + * state: Processor state block + * address: Address to which data was written + * [Return value] + * None + */ +extern void q68_jit_clear_page(Q68State *state, uint32_t address); + +/** + * q68_jit_clear_write: Clear any translation which includes the given + * address, and (if there is at least one such translation) blacklist the + * address from being translated. + * + * [Parameters] + * state: Processor state block + * address: Address to which data was written + * size: Size of data written + * [Return value] + * None + */ +extern void q68_jit_clear_write(Q68State *state, uint32_t address, uint32_t size); + +/*************************************************************************/ +/************************ Internal-use constants *************************/ +/*************************************************************************/ + +/* Effective address types for ea_type() */ +enum { + EA_xREG, // Data/address register + EA_MEM, // Memory reference + EA_IMM, // Immediate value +}; + +/* Effective address access types for ea_resolve() */ +enum { + ACCESS_READ = 0, + ACCESS_WRITE, + ACCESS_MODIFY, // For the read access of a read-write operation +}; + +/*************************************************************************/ +/*************** Opcode implementation function prototype ****************/ +/*************************************************************************/ + +/** + * OpcodeFunc: Type of a function implementing one or a group of + * instructions. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * [Return value] + * Clock cycles used + */ +typedef int OpcodeFunc(Q68State *state, uint32_t opcode); + +/*************************************************************************/ +/******************* Memory access functions (inline) ********************/ +/*************************************************************************/ + +/** + * READ[SU]{8,16,32}: Read a value from memory. + * + * [Parameters] + * state: Processor state block + * addr: Address to read or write + * [Return value] + * Value read + */ + +static inline int32_t READS8(Q68State *state, uint32_t addr) { + return (int8_t) state->readb_func(addr & 0xFFFFFF); +} +static inline uint32_t READU8(Q68State *state, uint32_t addr) { + return state->readb_func(addr & 0xFFFFFF); +} + +static inline int32_t READS16(Q68State *state, uint32_t addr) { + return (int16_t) state->readw_func(addr & 0xFFFFFF); +} +static inline uint32_t READU16(Q68State *state, uint32_t addr) { + return state->readw_func(addr & 0xFFFFFF); +} + +static inline int32_t READS32(Q68State *state, uint32_t addr) { + addr &= 0xFFFFFF; + int32_t value = (int32_t) state->readw_func(addr) << 16; + addr += 2; + addr &= 0xFFFFFF; + value |= state->readw_func(addr); + return value; +} +static inline uint32_t READU32(Q68State *state, uint32_t addr) { + addr &= 0xFFFFFF; + uint32_t value = state->readw_func(addr) << 16; + addr += 2; + addr &= 0xFFFFFF; + value |= state->readw_func(addr); + return value; +} + +/*-----------------------------------------------------------------------*/ + +/** + * WRITE{8,16,32}: Write a value to memory. + * + * [Parameters] + * state: Processor state block + * addr: Address to read or write + * data: Value to write + * [Return value] + * None + */ + +static inline void WRITE8(Q68State *state, uint32_t addr, uint8_t data) { + addr &= 0xFFFFFF; +#ifdef Q68_USE_JIT + if (UNLIKELY(JIT_PAGE_TEST(state, addr >> Q68_JIT_PAGE_BITS))) { + q68_jit_clear_write(state, addr, 1); + } +#endif + state->writeb_func(addr, data); +} + +static inline void WRITE16(Q68State *state, uint32_t addr, uint16_t data) { + addr &= 0xFFFFFF; +#ifdef Q68_USE_JIT + if (UNLIKELY(JIT_PAGE_TEST(state, addr >> Q68_JIT_PAGE_BITS))) { + q68_jit_clear_write(state, addr, 2); + } +#endif + state->writew_func(addr, data); +} + +static inline void WRITE32(Q68State *state, uint32_t addr, uint32_t data) { + WRITE16(state, addr, data>>16); + WRITE16(state, addr+2, data); +} + +/*-----------------------------------------------------------------------*/ + +/** + * IFETCH: Retrieve and return the 16-bit word at the PC, incrementing the + * PC by 2. + * + * [Parameters] + * state: Processor state block + * [Return value] + * 16-bit value read + */ + +static inline uint32_t IFETCH(Q68State *state) { + uint32_t data = READU16(state, state->PC); + state->PC += 2; + return data; +} + +/*-----------------------------------------------------------------------*/ + +/** + * PUSH{16,32}, POP{16,32}: Push values onto or pop them off the stack. + * + * [Parameters] + * state: Processor state block + * data: Value to push (PUSH only) + * [Return value] + * Value popped (unsigned, POP only) + */ + +static inline void PUSH16(Q68State *state, uint16_t data) { + state->A[7] -= 2; + WRITE16(state, state->A[7], data); +} +static inline void PUSH32(Q68State *state, uint32_t data) { + state->A[7] -= 4; + WRITE32(state, state->A[7], data); +} + +static inline uint32_t POP16(Q68State *state) { + const uint32_t data = READU16(state, state->A[7]); + state->A[7] += 2; + return data; +} +static inline uint32_t POP32(Q68State *state) { + const uint32_t data = READU32(state, state->A[7]); + state->A[7] += 4; + return data; +} + +/*************************************************************************/ +/******************* Instruction implementation macros *******************/ +/*************************************************************************/ + +/* INSN_GET_REG: uint32_t reg = opcode[11:9] */ +#define INSN_GET_REG \ + uint32_t reg = (opcode>>9) & 7 + +/* INSN_GET_COUNT: uint32_t count = opcode[11:9] || 8 */ +#define INSN_GET_COUNT \ + uint32_t count = (opcode>>9) & 7 ?: 8 + +/* INSN_GET_COND: uint32_t cond = opcode[11:8] */ +#define INSN_GET_COND \ + uint32_t cond = (opcode>>8) & 15 + +/* INSN_GET_SIZE: uint32_t size = opcode[7:6] */ +#define INSN_GET_SIZE \ + uint32_t size = (opcode>>6) & 3 + +/* INSN_GET_REG0: uint32_t reg0 = opcode[2:0] */ +#define INSN_GET_REG0 \ + uint32_t reg0 = opcode & 7 + +/* INSN_GET_IMM8: const int32_t imm8 = SIGN_EXTEND(opcode[7:0]) */ +#define INSN_GET_IMM8 \ + const int32_t imm8 = (int32_t)(int8_t)opcode + +/* INSN_GET_IMM16: const int32_t imm16 = *++PC; */ +#define INSN_GET_IMM16 \ + const int32_t imm16 = (int16_t)IFETCH(state) + +/* INSN_GET_DISP8: int32_t disp = SIGN_EXTEND(opcode[7:0]) */ +#define INSN_GET_DISP8 \ + int32_t disp = (int32_t)(int8_t)opcode + +/*-----------------------------------------------------------------------*/ + +/* INSN_COND_TRUE: Evaluate whether the given condition code is satisfied. */ +#define INSN_COND_TRUE(cond) __extension__({ \ + const unsigned int __cond = (cond); \ + const unsigned int group = __cond >> 1; \ + const unsigned int onoff = __cond & 1; \ + (group==COND_LS>>1 ? (state->SR & SR_C) >> SR_C_SHIFT \ + | (state->SR & SR_Z) >> SR_Z_SHIFT : \ + group==COND_CS>>1 ? (state->SR & SR_C) >> SR_C_SHIFT : \ + group==COND_EQ>>1 ? (state->SR & SR_Z) >> SR_Z_SHIFT : \ + group==COND_VS>>1 ? (state->SR & SR_V) >> SR_V_SHIFT : \ + group==COND_MI>>1 ? (state->SR & SR_N) >> SR_N_SHIFT : \ + group==COND_LT>>1 ? (state->SR & SR_N) >> SR_N_SHIFT \ + ^ (state->SR & SR_V) >> SR_V_SHIFT : \ + group==COND_LE>>1 ? (state->SR & SR_Z) >> SR_Z_SHIFT \ + | ((state->SR & SR_N) >> SR_N_SHIFT \ + ^ (state->SR & SR_V) >> SR_V_SHIFT) : \ + 0 /* COND_T or COND_F */ \ + ) == onoff; \ +}) + +/*-----------------------------------------------------------------------*/ + +/* INSN_CLEAR_XCC, INSN_CLEAR_CC: Clear all condition codes, or all except + * the X flag. */ +#define INSN_CLEAR_XCC() (state->SR &= ~(SR_X|SR_N|SR_Z|SR_V|SR_C)) +#define INSN_CLEAR_CC() (state->SR &= ~( SR_N|SR_Z|SR_V|SR_C)) + + +/* INSN_SETNZ: Set the N and Z flags according to the given 32-bit result. + * Assumes the flags are currently cleared. */ +#define INSN_SETNZ(result) do { \ + const int32_t __result = (result); \ + if (__result < 0) { \ + state->SR |= SR_N; \ + } else if (__result == 0) { \ + state->SR |= SR_Z; \ + } \ +} while (0) + +/* INSN_SETNZ_SHIFT: Like INSN_SETNZ, but assumes the presence of a "shift" + * variable containing the number of bits of "result" less one. */ +#define INSN_SETNZ_SHIFT(result) do { \ + const int32_t __result = (result); \ + if (__result >> shift) { \ + state->SR |= SR_N; \ + } else if (__result == 0) { \ + state->SR |= SR_Z; \ + } \ +} while (0) + +/*************************************************************************/ +/*************************************************************************/ + +#endif // Q68_INTERNAL_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-jit-psp.S b/yabause/src/q68/q68-jit-psp.S new file mode 100644 index 0000000000..b6a7204320 --- /dev/null +++ b/yabause/src/q68/q68-jit-psp.S @@ -0,0 +1,2876 @@ +/* src/q68/q68-jit-psp.S: PSP dynamic translation implementation for Q68 + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "q68-const.h" + +.set noreorder +.set nomips16 + +#define s8 fp // since binutils doesn't seem to recognize $s8 on its own + +/*************************************************************************/ + +/* + * Register and stack usage is as follows: + * + * $v0 -- result; temporary used by macros; (on return) address to resume + * execution at, or NULL to terminate execution + * $v1 -- temporary + * $a0 -- temporary + * $a1 -- temporary + * $a2 -- temporary + * $a3 -- temporary + * $t0 -- temporary (not used by macros) + * $t1 -- temporary (not used by macros) + * $t2 -- temporary (not used by macros) + * $t3 -- temporary (not used by macros) + * $t4 -- temporary (not used by macros) + * $t5 -- not used + * $t6 -- not used + * $t7 -- operand 2 + * $t8 -- not used + * $t9 -- indirect function call pointer + * $s0 -- Q68State structure pointer + * $s1 -- cycles to execute + * $s2 -- cumulative cycle count + * $s3 -- operand 1; persistent temporary + * $s4 -- PC mirror register + * $s5 -- SR mirror register + * $s6 -- effective address (used instead of state->ea_addr); persistent + * temporary + * $s7 -- value of Q68State.jit_abort + * $s8 -- not used (not saved by JIT_CALL()) + * + * 0($sp) -- temporary (used by READ/WRITE macros to hold addresses) + * 4($sp) -- temporary (used by READ/WRITE macros to hold values) + * 8($sp) -- unused + * 12($sp) -- saved $ra + */ + +/*************************************************************************/ + +/* Label/size/parameter definition macros */ +#define DEFLABEL(name) .globl JIT_PSP_##name; \ + .type JIT_PSP_##name, @function; \ + JIT_PSP_##name: +#define DEFSIZE(name) .globl JIT_PSPSIZE_##name; \ + .type JIT_PSPSIZE_##name, @object; \ + JIT_PSPSIZE_##name: .int . - JIT_PSP_##name +#define DEFPARAM(name,param,label,offset) \ + .globl JIT_PSPPARAM_##name##_##param; \ + .type JIT_PSPPARAM_##name##_##param, @object; \ + JIT_PSPPARAM_##name##_##param: .int label - JIT_PSP_##name + (offset) + +/* Q68State structure offsets */ +Q68State_D = 0 +Q68State_A = 32 +Q68State_PC = 64 +Q68State_SR = 68 +Q68State_USP = 72 +Q68State_SSP = 76 +Q68State_current_PC = 80 +Q68State_ea_addr = 84 +Q68State_exception = 88 +Q68State_fault_addr = 92 +Q68State_fault_opcode = 96 +Q68State_fault_status = 98 +Q68State_halted = 100 +Q68State_irq = 104 +Q68State_cycles = 108 +Q68State_malloc_func = 112 +Q68State_realloc_func = 116 +Q68State_free_func = 120 +Q68State_readb_func = 124 +Q68State_readw_func = 128 +Q68State_writeb_func = 132 +Q68State_writew_func = 136 +Q68State_jit_flush = 140 +Q68State_jit_running = 144 +Q68State_jit_abort = 148 +Q68State_jit_table = 152 +Q68State_jit_hashchain = 156 +Q68State_jit_total_data = 160 +Q68State_jit_timestamp = 164 +Q68State_jit_blacklist = 168 +Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) +Q68State_jit_blist_num = Q68State_jit_in_blist + 4 +Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 +Q68State_jit_callstack = Q68State_jit_callstack_top + 4 +Q68State_jit_pages = Q68State_jit_callstack + (12 * Q68_JIT_CALLSTACK_SIZE) + +/*************************************************************************/ + +/* Shorthand for referencing Q68State fields */ + +#define D0 Q68State_D+0*4($s0) +#define D1 Q68State_D+1*4($s0) +#define D2 Q68State_D+2*4($s0) +#define D3 Q68State_D+3*4($s0) +#define D4 Q68State_D+4*4($s0) +#define D5 Q68State_D+5*4($s0) +#define D6 Q68State_D+6*4($s0) +#define D7 Q68State_D+7*4($s0) + +#define A0 Q68State_A+0*4($s0) +#define A1 Q68State_A+1*4($s0) +#define A2 Q68State_A+2*4($s0) +#define A3 Q68State_A+3*4($s0) +#define A4 Q68State_A+4*4($s0) +#define A5 Q68State_A+5*4($s0) +#define A6 Q68State_A+6*4($s0) +#define A7 Q68State_A+7*4($s0) + +#define PC Q68State_PC($s0) +#define SR Q68State_SR($s0) +#define USP Q68State_USP($s0) +#define SSP Q68State_SSP($s0) + +/*************************************************************************/ + +/* LOAD_DELAY_NOP is used to indicate where a pipeline stall will occur + * due to a dependency on data loaded by a previous instruction. It + * doesn't actually expand to anything, but can be used as a hint for + * optimization. */ + +#define LOAD_DELAY_NOP /*nothing*/ + +/*************************************************************************/ +/************************** Convenience macros ***************************/ +/*************************************************************************/ + +/** + * seqz: seqz rd,rs is a clearer substitute for sltiu rd,rs,1 and sets rd + * to 1 if rs is zero, 0 otherwise. + */ +.macro seqz rd, rs + sltiu \rd, \rs, 1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * snez: sgtz rd,rs is a clearer substitute for sltu rd,zero,rs and sets + * rd to 1 if rs is positive, 0 otherwise. + */ +.macro snez rd, rs + sltu \rd, $zero, \rs +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * sgtz: sgtz rd,rs is a clearer substitute for slt rd,zero,rs and sets + * rd to 1 if rs is positive, 0 otherwise. + */ +.macro sgtz rd, rs + slt \rd, $zero, \rs +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * sltz: sltz rd,rs is a clearer substitute for slti rd,rs,0 and sets rd + * to 1 if rs is negative, 0 otherwise. + */ +.macro sltz rd, rs + slti \rd, \rs, 0 +.endm + +/*************************************************************************/ + +/** + * SETUP: Perform setup required before executing translated code. + */ +.macro SETUP + addiu $sp, $sp, -16 + lw $s5, Q68State_SR($s0) + lw $s4, Q68State_PC($s0) + sw $ra, 12($sp) + move $s7, $zero +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * TERMINATE: Terminate execution of the current block. The emulator will + * resume execution at the address in state->PC. + */ +.macro TERMINATE + lw $ra, 12($sp) + sw $s4, Q68State_PC($s0) + sw $s5, Q68State_SR($s0) + move $v0, $zero + jr $ra + addiu $sp, $sp, 16 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * TERMINATE_CONTINUE: Terminate execution of the current block, returning + * the address of the following native instruction. + */ +.macro TERMINATE_CONTINUE + sw $s4, Q68State_PC($s0) + jal TERMINATE_CONTINUE_get_pc + sw $s5, Q68State_SR($s0) + jr $ra + addiu $sp, $sp, 16 + SETUP +.endm + +/* Helper subroutine to get the PC */ +TERMINATE_CONTINUE_get_pc: + addiu $v0, $ra, 8 // Size of "jr ra; addiu $sp, $sp, 16" + jr $ra + lw $ra, 12($sp) // Preload the saved $ra to avoid a load stall + +/*************************************************************************/ + +/** + * READ{8,16,32}: Read a value from memory. The address to read from is + * taken from $s6; the value read is returned zero-extended in \return. + */ +.macro READ8 return + lw $t9, Q68State_readb_func($s0) + ext $a0, $s6, 0, 24 // Mask off top 8 bits + LOAD_DELAY_NOP + jalr $t9 + nop + andi \return, $v0, 0xFF +.endm + +.macro READ16 return + lw $t9, Q68State_readw_func($s0) + ext $a0, $s6, 0, 24 + LOAD_DELAY_NOP + jalr $t9 + nop + andi \return, $v0, 0xFFFF +.endm + +.macro READ32 return + lw $t9, Q68State_readw_func($s0) + ext $a0, $s6, 0, 24 + LOAD_DELAY_NOP + jalr $t9 // Read high word + sw $a0, 0($sp) + lw $a0, 0($sp) + sll $v0, $v0, 16 + lw $t9, Q68State_readw_func($s0) + sw $v0, 4($sp) + addiu $a0, $a0, 2 + jalr $t9 // Read low word + ext $a0, $a0, 0, 24 // Just in case we're reading from $FFFFFE + lw $v1, 4($sp) + andi $v0, $v0, 0xFFFF + LOAD_DELAY_NOP + or \return, $v0, $v1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * WRITE_CHECK_JIT: Check whether a write of size \nbytes (*bytes*, not + * bits) to the address in $s6 would clobber a page containing already- + * translated blocks, and clear those translations if so. All caller-saved + * registers are destroyed. + * + * Note that this macro uses local label 4. + */ +.macro WRITE_CHECK_JIT nbytes + srl $a0, $s6, Q68_JIT_PAGE_BITS+3 + addu $a0, $a0, $s0 + lbu $v0, Q68State_jit_pages($a0) + ext $a1, $s6, Q68_JIT_PAGE_BITS, 3 + li $v1, 1 + beqz $v0, 4f + sllv $v1, $v1, $a1 + and $v0, $v0, $v1 + beqz $v0, 4f + move $a1, $s6 + li $a2, \nbytes + jal q68_jit_clear_write + move $a0, $s0 + lw $s7, Q68State_jit_abort($s0) +4: +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * WRITE{8,16,32}: Write a value to memory. The address must be stored in + * $s6, and the value to write in $v0. Note that a 32-bit write spanning + * two JIT pages that clobbers the first word of a translated routine + * beginning on the second page will not be detected. + */ +.macro WRITE8 + sw $v0, 4($sp) + WRITE_CHECK_JIT 1 + lw $t9, Q68State_writeb_func($s0) + lw $a1, 4($sp) + LOAD_DELAY_NOP + jalr $t9 + ext $a0, $s6, 0, 24 +.endm + +.macro WRITE16 + sw $v0, 4($sp) + WRITE_CHECK_JIT 2 + lw $t9, Q68State_writew_func($s0) + lw $a1, 4($sp) + LOAD_DELAY_NOP + jalr $t9 + ext $a0, $s6, 0, 24 +.endm + +.macro WRITE32 + sw $v0, 4($sp) + WRITE_CHECK_JIT 4 + lw $t9, Q68State_writew_func($s0) + lw $a1, 4($sp) + ext $a0, $s6, 0, 24 + jalr $t9 // Write high word + srl $a1, $a1, 16 + lw $t9, Q68State_writew_func($s0) + lhu $a1, 4($sp) // Keep only the low 16 bits of the value + addiu $a0, $s6, 2 + jalr $t9 // Write low word + ext $a0, $a0, 0, 24 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * {PUSH,POP}{16,32}: Push or pop values onto or off of the stack. For + * POP, the value popped is zero-extended and returned in \return. + * + * For pushes, it is assumed that the push does not clobber any + * JIT-translated code. + */ +.macro PUSH16 value + lw $a0, A7 + lw $t9, Q68State_writew_func($s0) + move $a1, \value + addiu $a0, $a0, -2 + sw $a0, A7 + jalr $t9 + ext $a0, $a0, 0, 24 +.endm + +.macro PUSH32 value + lw $a0, A7 + lw $t9, Q68State_writew_func($s0) + sw \value, 4($sp) + addiu $a0, $a0, -4 + sw $a0, A7 + ext $a0, $a0, 0, 24 + jalr $t9 // Write high word + srl $a1, \value, 16 + lw $a0, A7 + lw $t9, Q68State_writew_func($s0) + lhu $a1, 4($sp) // Keep only the low 16 bits of \value + addiu $a0, $a0, 2 + jalr $t9 // Write low word + ext $a0, $a0, 0, 24 +.endm + +.macro POP16 return + lw $a0, A7 + lw $t9, Q68State_readw_func($s0) + LOAD_DELAY_NOP + addiu $v0, $a0, 2 + sw $v0, A7 + jalr $t9 + ext $a0, $a0, 0, 24 + andi \return, $v0, 0xFFFF +.endm + +.macro POP32 return + lw $a0, A7 + lw $t9, Q68State_readw_func($s0) + LOAD_DELAY_NOP + addiu $v0, $a0, 4 + sw $v0, A7 + jalr $t9 // Read high word + ext $a0, $a0, 0, 24 + lw $a0, A7 + sll $v0, $v0, 16 + lw $t9, Q68State_readw_func($s0) + sw $v0, 4($sp) + addiu $a0, $a0, -2 // Since it was already incremented by 4 + jalr $t9 // Read low word + ext $a0, $a0, 0, 24 + lw $v1, 4($sp) + andi $v0, $v0, 0xFFFF + LOAD_DELAY_NOP + or \return, $v0, $v1 +.endm + +/*************************************************************************/ + +/** + * SETCC_NZ_[BWL]: Set the N and Z condition codes according to \value. + */ +.macro SETCC_NZ_B value + ext $v1, \value, 7, 1 + ins $s5, $v1, SR_N_SHIFT, 1 + andi $v1, \value, 0xFF + seqz $v1, $v1 + ins $s5, $v1, SR_Z_SHIFT, 1 +.endm + +.macro SETCC_NZ_W value + ext $v1, \value, 15, 1 + ins $s5, $v1, SR_N_SHIFT, 1 + andi $v1, \value, 0xFFFF + seqz $v1, $v1 + ins $s5, $v1, SR_Z_SHIFT, 1 +.endm + +.macro SETCC_NZ_L value + ext $v1, \value, 31, 1 + ins $s5, $v1, SR_N_SHIFT, 1 + seqz $v1, \value + ins $s5, $v1, SR_Z_SHIFT, 1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_NZ00_[BWL]: Set the N and Z condition codes according to \value, + * and clear the V and C condition codes. + */ +.macro SETCC_NZ00_B value + SETCC_NZ_B \value + ins $s5, $zero, 0, 2 +.endm + +.macro SETCC_NZ00_W value + SETCC_NZ_W \value + ins $s5, $zero, 0, 2 +.endm + +.macro SETCC_NZ00_L value + SETCC_NZ_L \value + ins $s5, $zero, 0, 2 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_XNZVC_ADD: Set the condition codes for an ADD operation based on + * the values in the op1, op2, and result registers. + */ +.macro SETCC_XNZVC_ADD nbits + .if \nbits == 8 + SETCC_NZ_B $v0 + .else + .if \nbits == 16 + SETCC_NZ_W $v0 + .else + SETCC_NZ_L $v0 + .endif + .endif + xor $a0, $s3, $v0 + xor $a1, $t7, $v0 + and $v1, $a0, $a1 + ext $v1, $v1, \nbits-1, 1 + ins $s5, $v1, SR_V_SHIFT, 1 + ext $a0, $s3, \nbits-1, 1 + ext $a1, $t7, \nbits-1, 1 + ext $v1, $v0, \nbits-1, 1 + addu $a0, $a0, $a1 + subu $v1, $a0, $v1 + sgtz $v1, $v1 + ins $s5, $v1, SR_C_SHIFT, 1 + ins $s5, $v1, SR_X_SHIFT, 1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_XNZVC_ADDX: Set the condition codes for an ADDX operation based + * on the values in the op1, op2, and result registers. + */ +.macro SETCC_XNZVC_ADDX nbits + // Z is only cleared (never set) by ADDX etc., so we can't use SETNZ + ext $v1, $v0, \nbits-1, 1 + ins $s5, $v1, SR_N_SHIFT, 1 + .if \nbits < 32 + ext $v1, $v0, 0, \nbits + snez $v1, $v1 + .else + snez $v1, $v0 + .endif + sll $v1, $v1, SR_Z_SHIFT + not $v1, $v1 + and $s5, $s5, $v1 + xor $a0, $s3, $v0 + xor $a1, $t7, $v0 + and $v1, $a0, $a1 + ext $v1, $v1, \nbits-1, 1 + ins $s5, $v1, SR_V_SHIFT, 1 + ext $a0, $s3, \nbits-1, 1 + ext $a1, $t7, \nbits-1, 1 + ext $v1, $v0, \nbits-1, 1 + addu $a0, $a0, $a1 + subu $v1, $a0, $v1 + sgtz $v1, $v1 + ins $s5, $v1, SR_C_SHIFT, 1 + ins $s5, $v1, SR_X_SHIFT, 1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_NZVC_SUB, SETCC_XNZVC_SUB: Set the condition codes for a SUB + * operation (excluding or including the X flag) based on the values in the + * op1, op2, and result registers. + */ +.macro SETCC_NZVC_SUB nbits + .if \nbits == 8 + SETCC_NZ_B $v0 + .else + .if \nbits == 16 + SETCC_NZ_W $v0 + .else + SETCC_NZ_L $v0 + .endif + .endif + xor $a0, $s3, $t7 + xor $a1, $v0, $t7 + and $v1, $a0, $a1 + ext $v1, $v1, \nbits-1, 1 + ins $s5, $v1, SR_V_SHIFT, 1 + ext $a0, $s3, \nbits-1, 1 + ext $a1, $t7, \nbits-1, 1 + ext $v1, $v0, \nbits-1, 1 + subu $a0, $a0, $a1 + addu $v1, $a0, $v1 + sgtz $v1, $v1 + ins $s5, $v1, SR_C_SHIFT, 1 +.endm + +.macro SETCC_XNZVC_SUB nbits + SETCC_NZVC_SUB \nbits + ins $s5, $v1, SR_X_SHIFT, 1 +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_XNZVC_SUBX: Set the condition codes for a SUBX operation based on + * the values in the op1, op2, and result registers. + */ +.macro SETCC_XNZVC_SUBX nbits + ext $v1, $v0, \nbits-1, 1 + ins $s5, $v1, SR_N_SHIFT, 1 + .if \nbits < 32 + ext $v1, $v0, 0, \nbits + snez $v1, $v1 + .else + snez $v1, $v0 + .endif + sll $v1, $v1, SR_Z_SHIFT + not $v1, $v1 + and $s5, $s5, $v1 + xor $a0, $s3, $t7 + xor $a1, $v0, $t7 + and $v1, $a0, $a1 + ext $v1, $v1, \nbits-1, 1 + ins $s5, $v1, SR_V_SHIFT, 1 + ext $a0, $s3, \nbits-1, 1 + ext $a1, $t7, \nbits-1, 1 + ext $v1, $v0, \nbits-1, 1 + subu $a0, $a0, $a1 + addu $v1, $a0, $v1 + sgtz $v1, $v1 + ins $s5, $v1, SR_C_SHIFT, 1 + ins $s5, $v1, SR_X_SHIFT, 1 +.endm + +/*************************************************************************/ +/*************************** Local subroutines ***************************/ +/*************************************************************************/ + +/** + * UPDATE_SR: Set the status register according to the value in $v0. + */ +UPDATE_SR: + lw $a0, Q68State_irq($s0) // Load early for IRQ check below + xor $v1, $s5, $v0 + andi $v1, $v1, SR_S // Change in S bit? + beqz $v1, 1f + andi $s5, $v0, 0xFFFF // Zero-extend value into SR mirror register + andi $v1, $v0, SR_S // Which way did it change? + beqz $v1, 0f + lw $a0, A7 + lw $a1, SSP // Into supervisor mode + LOAD_DELAY_NOP + sw $a0, USP + j 1f + sw $a1, A7 +0: lw $a1, USP // Out of supervisor mode + sw $a0, SSP + LOAD_DELAY_NOP + sw $a1, A7 +1: andi $v1, $a0, 7 + slti $a1, $v1, 7 // Check for a pending NMI + beqz $a1, 2f + ext $v0, $v0, SR_I0_SHIFT, 3 + slt $v0, $v0, $v1 // Check for a pending unmasked interrupt + beqz $v0, 3f +2: addiu $a0, $v1, EX_LEVEL_1_INTERRUPT-1 // In a delay slot, but okay + sw $a0, Q68State_exception($s0) + sw $zero, Q68State_irq($s0) + TERMINATE +3: jr $ra + nop + +/*************************************************************************/ +/**************************** Meta-operations ****************************/ +/*************************************************************************/ + +/** + * PROLOGUE: Any prologue necessary at the beginning of the code stream. + */ +DEFLABEL(PROLOGUE) +_PROLOGUE_TOP: + /* Include various exceptional termination code here, so we can + * conditionally branch to it (meaning we can skip the branch in + * 1-2 cycles) instead of having to inverse-branch around an + * unconditional jump (costing 3-4 cycles). */ + b 0f + nop +_PROLOGUE_TERMINATE: // Generic termination (used for CHECK_ABORT) +1: TERMINATE +_PROLOGUE_EXCEPTION: // Exception raised (exception number in $a0) + b 1b + sw $a0, Q68State_exception($s0) +_PROLOGUE_ADDRESS_ERROR_EA: // EA address error (opcode in $a2, status in $a3) + li $v1, EX_ADDRESS_ERROR + sw $v1, Q68State_exception($s0) + sw $s6, Q68State_fault_addr($s0) + sh $a2, Q68State_fault_opcode($s0) + b 1b + sh $a3, Q68State_fault_status($s0) +_PROLOGUE_ADDRESS_ERROR_SP: // Stack address error (address in $a1, + // opcode in $a2, status in $a3) + li $a0, EX_ADDRESS_ERROR + sw $a0, Q68State_exception($s0) + sw $v1, Q68State_fault_addr($s0) + sh $a2, Q68State_fault_opcode($s0) + b 1b + sh $a3, Q68State_fault_status($s0) +0: // Actual setup starts here + SETUP +DEFSIZE(PROLOGUE) + + +/* Offset definitions used for branching to termination code; "branchofs" + * is the offset in _instructions_ (not bytes) from the beginning of the + * named fragment */ +#define DEFOFS(name,branchofs) \ + .globl JIT_PSPOFS_##name; \ + .type JIT_PSPOFS_##name, @object; \ + JIT_PSPOFS_##name: .int _PROLOGUE_##name - _PROLOGUE_TOP - 4*branchofs +DEFOFS(TERMINATE, 0) +DEFOFS(EXCEPTION, 0) +DEFOFS(ADDRESS_ERROR_EA, 2) +DEFOFS(ADDRESS_ERROR_SP, 4) + +/*-----------------------------------------------------------------------*/ + +/** + * EPILOGUE: Any epilogue necessary at the end of the code stream. + */ +DEFLABEL(EPILOGUE) + TERMINATE +DEFSIZE(EPILOGUE) + +/*************************************************************************/ + +/** + * TRACE: Trace the current instruction. + */ +DEFLABEL(TRACE) + lw $s3, Q68State_cycles($s0) + addu $t0, $s3, $s2 + sw $t0, Q68State_cycles($s0) + sw $s4, Q68State_PC($s0) + jal q68_trace + sw $s5, Q68State_SR($s0) + sw $s3, Q68State_cycles($s0) +DEFSIZE(TRACE) + +/*************************************************************************/ + +/** + * ADD_CYCLES: Add the specified number of clock cycles to the cycle count. + * + * [Parameters] + * cycles: Number of clock cycles to add + */ +DEFLABEL(ADD_CYCLES) + addiu $s2, $s2, 1 +9: +DEFSIZE(ADD_CYCLES) +DEFPARAM(ADD_CYCLES, cycles, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_CYCLES: Check whether the clock cycle limit has been reached, and + * interrupt execution if so. + */ +DEFLABEL(CHECK_CYCLES) + slt $v0, $s2, $s1 + bnez $v0, 2f + sw $s4, Q68State_PC($s0) // Can't have JAL in a delay slot + jal CHECK_CYCLES_get_pc // (got to keep up their reputation) + sw $s5, Q68State_SR($s0) +0: jr $ra + addiu $sp, $sp, 16 +1: SETUP +2: +DEFSIZE(CHECK_CYCLES) + +/* Helper subroutine to get the PC (as with TERMINATE_CONTINUE) */ +CHECK_CYCLES_get_pc: + addiu $v0, $ra, 1b-0b + jr $ra + lw $ra, 12($sp) // Preload the saved $ra to avoid a load stall + +/*************************************************************************/ + +/** + * ADVANCE_PC: Add the specified value to the current program counter. + * + * [Parameters] + * value: Amount to add + */ +DEFLABEL(ADVANCE_PC) + addiu $s4, $s4, 1 +9: +DEFSIZE(ADVANCE_PC) +DEFPARAM(ADVANCE_PC, value, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * ADVANCE_PC_CHECK_ABORT: Add the specified value to the current program + * counter, then check the jit_abort flag and abort if necessary. + * + * [Parameters] + * value: Amount to add + * disp_4: (JIT_PSPOFS_TERMINATE - native offset of this fragment) / 4 + */ +DEFLABEL(ADVANCE_PC_CHECK_ABORT) + bnez $s7, .+0x1234 +8: addiu $s4, $s4, 1 +9: +DEFSIZE(ADVANCE_PC_CHECK_ABORT) +DEFPARAM(ADVANCE_PC_CHECK_ABORT, value, 9b, -4) +DEFPARAM(ADVANCE_PC_CHECK_ABORT, disp_4, 8b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ABORT: Check the jit_abort flag and abort if necessary. + * + * [Parameters] + * disp_4: (JIT_PSPOFS_TERMINATE - native offset of this fragment) / 4 + */ +DEFLABEL(CHECK_ABORT) + bnez $s7, .+0x1234 +9: nop +DEFSIZE(CHECK_ABORT) +DEFPARAM(CHECK_ABORT, disp_4, 9b, -4) + +/*************************************************************************/ + +/** + * EXCEPTION: Raise the specified exception. + * + * [Parameters] + * num: Exception number + * disp_4: (JIT_PSPOFS_EXCEPTION - native offset of this fragment) / 4 + */ +DEFLABEL(EXCEPTION) + b .+0x1234 +8: addiu $a0, $zero, 1 +9: +DEFSIZE(EXCEPTION) +DEFPARAM(EXCEPTION, num, 9b, -4) +DEFPARAM(EXCEPTION, disp_4, 8b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ALIGNED_EA: Check whether the previously resolved effective + * address is word-aligned (bit 0 is clear), and raise an address error + * exception if not. + * + * [Parameters] + * opcode: Instruction opcode + * status: Status word for address error exception + * disp_4: (JIT_PSPOFS_ADDRESS_ERROR_EA + * - native offset of this fragment) / 4 + */ +DEFLABEL(CHECK_ALIGNED_EA) + andi $v1, $s6, 1 + ori $a2, $zero, 0x1234 +7: bnezl $v1, .+0x1234 +8: ori $a3, $zero, 0x1234 +9: +DEFSIZE(CHECK_ALIGNED_EA) +DEFPARAM(CHECK_ALIGNED_EA, opcode, 7b, -4) +DEFPARAM(CHECK_ALIGNED_EA, status, 9b, -4) +DEFPARAM(CHECK_ALIGNED_EA, disp_4, 8b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ALIGNED_SP: Check whether the current stack pointer (register A7) + * is word-aligned (bit 0 is clear), and raise an address error exception + * if not. Destroys $v1 and $a0-$a3. + * + * [Parameters] + * opcode: Instruction opcode + * status: Status word for address error exception + * disp_4: (JIT_PSPOFS_ADDRESS_ERROR_SP + * - native offset of this fragment) / 4 + */ +DEFLABEL(CHECK_ALIGNED_SP) + lw $a1, A7 + ori $a2, $zero, 0x1234 +7: LOAD_DELAY_NOP + andi $v1, $a1, 1 + bnez $v1, .+0x1234 +8: ori $a3, $zero, 0x1234 +9: +DEFSIZE(CHECK_ALIGNED_SP) +DEFPARAM(CHECK_ALIGNED_SP, opcode, 7b, -4) +DEFPARAM(CHECK_ALIGNED_SP, status, 9b, -4) +DEFPARAM(CHECK_ALIGNED_SP, disp_4, 8b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_SUPER: Check whether the processor is in supervisor mode, and + * raise a privilege violation exception if not. + * + * [Parameters] + * disp_4: (JIT_PSPOFS_EXCEPTION - native offset of this fragment) / 4 + */ +DEFLABEL(CHECK_SUPER) + andi $v0, $s5, SR_S + beqzl $v0, .+0x1234 +9: li $a0, EX_PRIVILEGE_VIOLATION +DEFSIZE(CHECK_SUPER) +DEFPARAM(CHECK_SUPER, disp_4, 9b, -4) + +/*************************************************************************/ +/********************* Effective address resolution **********************/ +/*************************************************************************/ + +/** + * RESOLVE_INDIRECT: Resolve an address register indirect reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + */ +DEFLABEL(RESOLVE_INDIRECT) + lw $s6, 1($s0) +9: +DEFSIZE(RESOLVE_INDIRECT) +DEFPARAM(RESOLVE_INDIRECT, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_POSTINC: Resolve an address register postincrement reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * size: Size in bytes of the reference + */ +DEFLABEL(RESOLVE_POSTINC) + lw $s6, 1($s0) +7: LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $v0, $s6, 1 +8: sw $v0, 1($s0) +9: +DEFSIZE(RESOLVE_POSTINC) +DEFPARAM(RESOLVE_POSTINC, reg4, 7b, -4) +DEFPARAM(RESOLVE_POSTINC, size, 8b, -4) +DEFPARAM(RESOLVE_POSTINC, reg4_b, 9b, -4) // same as reg4 + +/* For byte-sized (A7)+, make sure A7 stays even */ +DEFLABEL(RESOLVE_POSTINC_A7_B) + lw $s6, A7 + LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, 1 + addiu $v0, $s6, 1 + sw $v0, A7 +DEFSIZE(RESOLVE_POSTINC_A7_B) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_PREDEC: Resolve an address register predecrement reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * nsize: Size in bytes of the reference, negated + */ +DEFLABEL(RESOLVE_PREDEC) + lw $s6, 1($s0) +7: LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, -1 +8: sw $s6, 1($s0) +9: +DEFSIZE(RESOLVE_PREDEC) +DEFPARAM(RESOLVE_PREDEC, reg4, 7b, -4) +DEFPARAM(RESOLVE_PREDEC, nsize, 8b, -4) +DEFPARAM(RESOLVE_PREDEC, reg4_b, 9b, -4) // same as reg4 + +/* For byte-sized -(A7), make sure A7 stays even */ +DEFLABEL(RESOLVE_PREDEC_A7_B) + lw $s6, 1($s0) + LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, -1 + addiu $v0, $s6, -1 + sw $v0, A7 +DEFSIZE(RESOLVE_PREDEC_A7_B) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_DISP: Resolve an address register indirect with displacement + * reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * disp: Displacement + */ +DEFLABEL(RESOLVE_DISP) + lw $s6, 1($s0) +8: LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, 1 +9: +DEFSIZE(RESOLVE_DISP) +DEFPARAM(RESOLVE_DISP, reg4, 8b, -4) +DEFPARAM(RESOLVE_DISP, disp, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_INDEX_[WL]: Resolve an address register indirect with index + * reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * ireg4: Index register number * 4 + * disp: Displacement + */ +DEFLABEL(RESOLVE_INDEX_W) + lw $s6, 1($s0) +7: lh $v0, 1($s0) +8: LOAD_DELAY_NOP + addi $s6, $s6, 1 +9: add $s6, $s6, $v0 +DEFSIZE(RESOLVE_INDEX_W) +DEFPARAM(RESOLVE_INDEX_W, reg4, 7b, -4) +DEFPARAM(RESOLVE_INDEX_W, ireg4, 8b, -4) +DEFPARAM(RESOLVE_INDEX_W, disp, 9b, -4) + +DEFLABEL(RESOLVE_INDEX_L) + lw $s6, 1($s0) +7: lw $v0, 1($s0) +8: LOAD_DELAY_NOP + addi $s6, $s6, 1 +9: add $s6, $s6, $v0 +DEFSIZE(RESOLVE_INDEX_L) +DEFPARAM(RESOLVE_INDEX_L, reg4, 7b, -4) +DEFPARAM(RESOLVE_INDEX_L, ireg4, 8b, -4) +DEFPARAM(RESOLVE_INDEX_L, disp, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_ABSOLUTE: Resolve an absolute short, absolute long, or + * PC-relative reference. + * + * [Parameters] + * addr_hi: Absolute address, high 16 bits + * addr_lo: Absolute address, low 16 bits + */ +DEFLABEL(RESOLVE_ABSOLUTE) + lui $s6, 0x1234 +8: ori $s6, $s6, 0x5678 +9: +DEFSIZE(RESOLVE_ABSOLUTE) +DEFPARAM(RESOLVE_ABSOLUTE, addr_hi, 8b, -4) +DEFPARAM(RESOLVE_ABSOLUTE, addr_lo, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_ABS_INDEX_[WL]: Resolve a PC-relative with index reference. + * + * [Parameters] + * ireg4: Index register number * 4 + * addr_hi: Absolute address, high 16 bits + * addr_lo: Absolute address, low 16 bits + */ +DEFLABEL(RESOLVE_ABS_INDEX_W) + lh $v0, 1($s0) +7: lui $s6, 0x1234 +8: ori $s6, $s6, 0x5678 +9: addu $s6, $s6, $v0 +DEFSIZE(RESOLVE_ABS_INDEX_W) +DEFPARAM(RESOLVE_ABS_INDEX_W, ireg4, 7b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_W, addr_hi, 8b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_W, addr_lo, 9b, -4) + +DEFLABEL(RESOLVE_ABS_INDEX_L) + lh $v0, 1($s0) +7: lui $s6, 0x1234 +8: ori $s6, $s6, 0x5678 +9: addu $s6, $s6, $v0 +DEFSIZE(RESOLVE_ABS_INDEX_L) +DEFPARAM(RESOLVE_ABS_INDEX_L, ireg4, 7b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_L, addr_hi, 8b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_L, addr_lo, 9b, -4) + +/*************************************************************************/ +/*************************** Operand retrieval ***************************/ +/*************************************************************************/ + +/** + * GET_OP1_REGISTER: Get the current value of the given register as + * operand 1. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(GET_OP1_REGISTER) + lw $s3, 1($s0) +9: +DEFSIZE(GET_OP1_REGISTER) +DEFPARAM(GET_OP1_REGISTER, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_EA_[BWL]: Get the value pointed to by the previously resolved + * effective address as operand 1. + */ +DEFLABEL(GET_OP1_EA_B) + READ8 $s3 +DEFSIZE(GET_OP1_EA_B) + +DEFLABEL(GET_OP1_EA_W) + READ16 $s3 +DEFSIZE(GET_OP1_EA_W) + +DEFLABEL(GET_OP1_EA_L) + READ32 $s3 +DEFSIZE(GET_OP1_EA_L) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_IMMED_{16S,16U,16HI,32}: Get an immediate value as operand 1. + * + * [Parameters] + * value_hi: High 16 bits of immediate value + * value_lo: Low 16 bits of immediate value (signed for 16S, else unsigned) + */ +DEFLABEL(GET_OP1_IMMED_16S) + addiu $s3, $zero, 1 +9: +DEFSIZE(GET_OP1_IMMED_16S) +DEFPARAM(GET_OP1_IMMED_16S, value_lo, 9b, -4) + +DEFLABEL(GET_OP1_IMMED_16U) + ori $s3, $zero, 1 +9: +DEFSIZE(GET_OP1_IMMED_16U) +DEFPARAM(GET_OP1_IMMED_16U, value_lo, 9b, -4) + +DEFLABEL(GET_OP1_IMMED_16HI) + lui $s3, 1 +9: +DEFSIZE(GET_OP1_IMMED_16HI) +DEFPARAM(GET_OP1_IMMED_16HI, value_hi, 9b, -4) + +DEFLABEL(GET_OP1_IMMED_32) + lui $s3, 1 +8: ori $s3, $s3, 1 +9: +DEFSIZE(GET_OP1_IMMED_32) +DEFPARAM(GET_OP1_IMMED_32, value_hi, 8b, -4) +DEFPARAM(GET_OP1_IMMED_32, value_lo, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_CCR: Get the current value of CCR as operand 1. + */ +DEFLABEL(GET_OP1_CCR) + andi $s3, $s5, 0xFF +DEFSIZE(GET_OP1_CCR) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_SR: Get the current value of SR as operand 1. + */ +DEFLABEL(GET_OP1_SR) + move $s3, $s5 +DEFSIZE(GET_OP1_SR) + +/*************************************************************************/ + +/** + * GET_OP2_*: Get the same things as above as operand 2. + */ +DEFLABEL(GET_OP2_REGISTER) + lw $t7, 1($s0) +9: +DEFSIZE(GET_OP2_REGISTER) +DEFPARAM(GET_OP2_REGISTER, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_EA_B) + READ8 $t7 +DEFSIZE(GET_OP2_EA_B) + +DEFLABEL(GET_OP2_EA_W) + READ16 $t7 +DEFSIZE(GET_OP2_EA_W) + +DEFLABEL(GET_OP2_EA_L) + READ32 $t7 +DEFSIZE(GET_OP2_EA_L) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_IMMED_16S) + addiu $t7, $zero, 1 +9: +DEFSIZE(GET_OP2_IMMED_16S) +DEFPARAM(GET_OP2_IMMED_16S, value_lo, 9b, -4) + +DEFLABEL(GET_OP2_IMMED_16U) + ori $t7, $zero, 1 +9: +DEFSIZE(GET_OP2_IMMED_16U) +DEFPARAM(GET_OP2_IMMED_16U, value_lo, 9b, -4) + +DEFLABEL(GET_OP2_IMMED_16HI) + lui $t7, 1 +9: +DEFSIZE(GET_OP2_IMMED_16HI) +DEFPARAM(GET_OP2_IMMED_16HI, value_hi, 9b, -4) + +DEFLABEL(GET_OP2_IMMED_32) + lui $t7, 1 +8: ori $t7, $t7, 1 +9: +DEFSIZE(GET_OP2_IMMED_32) +DEFPARAM(GET_OP2_IMMED_32, value_hi, 8b, -4) +DEFPARAM(GET_OP2_IMMED_32, value_lo, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_CCR) + andi $t7, $s5, 0xFF +DEFSIZE(GET_OP2_CCR) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_SR) + move $t7, $s5 +DEFSIZE(GET_OP2_SR) + +/*************************************************************************/ +/**************************** Result storing *****************************/ +/*************************************************************************/ + +/** + * SET_REGISTER_[BWL]: Set the value of the given register to the result + * value. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(SET_REGISTER_B) + sb $v0, 1($s0) +9: +DEFSIZE(SET_REGISTER_B) +DEFPARAM(SET_REGISTER_B, reg4, 9b, -4) + +DEFLABEL(SET_REGISTER_W) + sh $v0, 1($s0) +9: +DEFSIZE(SET_REGISTER_W) +DEFPARAM(SET_REGISTER_W, reg4, 9b, -4) + +DEFLABEL(SET_REGISTER_L) + sw $v0, 1($s0) +9: +DEFSIZE(SET_REGISTER_L) +DEFPARAM(SET_REGISTER_L, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_AREG_W: Set the value of the given address register to the + * sign-extended result value. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(SET_AREG_W) + seh $v1, $v0 + sw $v1, 1($s0) +9: +DEFSIZE(SET_AREG_W) +DEFPARAM(SET_AREG_W, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_EA_[BWL]: Set the value pointed to by the previously resolved + * effective address to the result value. + */ +DEFLABEL(SET_EA_B) + WRITE8 +DEFSIZE(SET_EA_B) + +DEFLABEL(SET_EA_W) + WRITE16 +DEFSIZE(SET_EA_W) + +DEFLABEL(SET_EA_L) + WRITE32 +DEFSIZE(SET_EA_L) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_CCR: Set the condition codes from the result value. + */ +DEFLABEL(SET_CCR) + ins $s5, $v0, 0, 8 +DEFSIZE(SET_CCR) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_SR: Set the status register from the result value. + */ +DEFLABEL(SET_SR) + jal UPDATE_SR + nop +DEFSIZE(SET_SR) + +/*************************************************************************/ +/*************************** Stack operations ****************************/ +/*************************************************************************/ + +/** + * PUSH_L: Push the 32-bit value of operand 1 onto the stack. + */ +DEFLABEL(PUSH_L) + PUSH32 $s3 +DEFSIZE(PUSH_L) + +/*-----------------------------------------------------------------------*/ + +/** + * POP_L: Pop a 32-bit value off the stack into the result register. + */ +DEFLABEL(POP_L) + POP32 $v0 +DEFSIZE(POP_L) + +/*************************************************************************/ +/************************ Condition code setting *************************/ +/*************************************************************************/ + +/** + * SETCC_ADD_[BWL]: Set the condition codes for the result of an ADD + * instruction stored in the result register. + */ +DEFLABEL(SETCC_ADD_B) + SETCC_XNZVC_ADD 8 +DEFSIZE(SETCC_ADD_B) + +DEFLABEL(SETCC_ADD_W) + SETCC_XNZVC_ADD 16 +DEFSIZE(SETCC_ADD_W) + +DEFLABEL(SETCC_ADD_L) + SETCC_XNZVC_ADD 32 +DEFSIZE(SETCC_ADD_L) + +/*************************************************************************/ + +/** + * SETCC_ADDX_[BWL]: Set the condition codes for the result of an ADDX + * instruction stored in the result register. + */ +DEFLABEL(SETCC_ADDX_B) + SETCC_XNZVC_ADDX 8 +DEFSIZE(SETCC_ADDX_B) + +DEFLABEL(SETCC_ADDX_W) + SETCC_XNZVC_ADDX 16 +DEFSIZE(SETCC_ADDX_W) + +DEFLABEL(SETCC_ADDX_L) + SETCC_XNZVC_ADDX 32 +DEFSIZE(SETCC_ADDX_L) + +/*************************************************************************/ + +/** + * SETCC_SUB_[BWL]: Set the condition codes for the result of a SUB + * instruction stored in the result register. + */ +DEFLABEL(SETCC_SUB_B) + SETCC_XNZVC_SUB 8 +DEFSIZE(SETCC_SUB_B) + +DEFLABEL(SETCC_SUB_W) + SETCC_XNZVC_SUB 16 +DEFSIZE(SETCC_SUB_W) + +DEFLABEL(SETCC_SUB_L) + SETCC_XNZVC_SUB 32 +DEFSIZE(SETCC_SUB_L) + +/*************************************************************************/ + +/** + * SETCC_SUBX_[BWL]: Set the condition codes for the result of a SUBX + * instruction stored in the result register. + */ +DEFLABEL(SETCC_SUBX_B) + SETCC_XNZVC_SUBX 8 +DEFSIZE(SETCC_SUBX_B) + +DEFLABEL(SETCC_SUBX_W) + SETCC_XNZVC_SUBX 16 +DEFSIZE(SETCC_SUBX_W) + +DEFLABEL(SETCC_SUBX_L) + SETCC_XNZVC_SUBX 32 +DEFSIZE(SETCC_SUBX_L) + +/*************************************************************************/ + +/** + * SETCC_CMP_[BWL]: Set the condition codes for the result of a CMP + * instruction stored in the result register. The X flag is unmodified. + */ +DEFLABEL(SETCC_CMP_B) + SETCC_NZVC_SUB 8 +DEFSIZE(SETCC_CMP_B) + +DEFLABEL(SETCC_CMP_W) + SETCC_NZVC_SUB 16 +DEFSIZE(SETCC_CMP_W) + +DEFLABEL(SETCC_CMP_L) + SETCC_NZVC_SUB 32 +DEFSIZE(SETCC_CMP_L) + +/*************************************************************************/ + +/** + * SETCC_LOGIC_[BWL]: Set the condition codes for the result of a logical + * instruction (MOVE, AND, OR, EOR) stored in the result register. The X + * flag is unmodified. + */ +DEFLABEL(SETCC_LOGIC_B) + SETCC_NZ00_B $v0 +DEFSIZE(SETCC_LOGIC_B) + +DEFLABEL(SETCC_LOGIC_W) + SETCC_NZ00_W $v0 +DEFSIZE(SETCC_LOGIC_W) + +DEFLABEL(SETCC_LOGIC_L) + SETCC_NZ00_L $v0 +DEFSIZE(SETCC_LOGIC_L) + +/*************************************************************************/ +/*************************** Condition testing ***************************/ +/*************************************************************************/ + +/** + * TEST_*: Check whether a condition is true (based on the current + * condition codes) and set $v1 based on the result (nonzero = true). + */ + +DEFLABEL(TEST_T) + li $v1, 1 +DEFSIZE(TEST_T) + +DEFLABEL(TEST_F) + li $v1, 0 +DEFSIZE(TEST_F) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_HI) + ext $v1, $s5, SR_Z_SHIFT, 1 + ext $a0, $s5, SR_C_SHIFT, 1 + or $v1, $v1, $a0 + xori $v1, $v1, 1 +DEFSIZE(TEST_HI) + +DEFLABEL(TEST_LS) + ext $v1, $s5, SR_Z_SHIFT, 1 + ext $a0, $s5, SR_C_SHIFT, 1 + or $v1, $v1, $a0 +DEFSIZE(TEST_LS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_CC) + ext $v1, $s5, SR_C_SHIFT, 1 + xori $v1, $v1, 1 +DEFSIZE(TEST_CC) + +DEFLABEL(TEST_CS) + ext $v1, $s5, SR_C_SHIFT, 1 +DEFSIZE(TEST_CS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_NE) + ext $v1, $s5, SR_Z_SHIFT, 1 + xori $v1, $v1, 1 +DEFSIZE(TEST_NE) + +DEFLABEL(TEST_EQ) + ext $v1, $s5, SR_Z_SHIFT, 1 +DEFSIZE(TEST_EQ) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_VC) + ext $v1, $s5, SR_V_SHIFT, 1 + xori $v1, $v1, 1 +DEFSIZE(TEST_VC) + +DEFLABEL(TEST_VS) + ext $v1, $s5, SR_V_SHIFT, 1 +DEFSIZE(TEST_VS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_PL) + ext $v1, $s5, SR_N_SHIFT, 1 + xori $v1, $v1, 1 +DEFSIZE(TEST_PL) + +DEFLABEL(TEST_MI) + ext $v1, $s5, SR_N_SHIFT, 1 +DEFSIZE(TEST_MI) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_GE) + ext $v1, $s5, SR_N_SHIFT, 1 + ext $a0, $s5, SR_V_SHIFT, 1 + xor $v1, $v1, $a0 + xori $v1, $v1, 1 +DEFSIZE(TEST_GE) + +DEFLABEL(TEST_LT) + ext $v1, $s5, SR_N_SHIFT, 1 + ext $a0, $s5, SR_V_SHIFT, 1 + xor $v1, $v1, $a0 +DEFSIZE(TEST_LT) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_GT) + ext $v1, $s5, SR_N_SHIFT, 1 + ext $a0, $s5, SR_V_SHIFT, 1 + ext $a1, $s5, SR_Z_SHIFT, 1 + xor $v1, $v1, $a0 + or $v1, $v1, $a1 + xori $v1, $v1, 1 +DEFSIZE(TEST_GT) + +DEFLABEL(TEST_LE) + ext $v1, $s5, SR_N_SHIFT, 1 + ext $a0, $s5, SR_V_SHIFT, 1 + ext $a1, $s5, SR_Z_SHIFT, 1 + xor $v1, $v1, $a0 + or $v1, $v1, $a1 +DEFSIZE(TEST_LE) + +/*************************************************************************/ +/**************************** ALU operations *****************************/ +/*************************************************************************/ + +/** + * MOVE_[BWL]: Evaluate op1, setting the result value for the MOVE + * instruction. + */ +DEFLABEL(MOVE_B) + move $v0, $s3 +DEFSIZE(MOVE_B) + +DEFLABEL(MOVE_W) + move $v0, $s3 +DEFSIZE(MOVE_W) + +DEFLABEL(MOVE_L) + move $v0, $s3 +DEFSIZE(MOVE_L) + +/*************************************************************************/ + +/** + * ADD_[BWL]: Evaluate op2 + op1. + */ +DEFLABEL(ADD_B) + addu $v0, $t7, $s3 +DEFSIZE(ADD_B) + +DEFLABEL(ADD_W) + addu $v0, $t7, $s3 +DEFSIZE(ADD_W) + +DEFLABEL(ADD_L) + addu $v0, $t7, $s3 +DEFSIZE(ADD_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ADDA_W: Sign-extend op1 to 32 bits, then evaluate op2 + op1. + */ +DEFLABEL(ADDA_W) + seh $v1, $s3 + addu $v0, $t7, $v1 +DEFSIZE(ADDA_W) + +/*-----------------------------------------------------------------------*/ + +/** + * ADDX_[BWL]: Evaluate op2 + op1 + X. + */ +DEFLABEL(ADDX_B) + ext $v1, $s5, SR_X_SHIFT, 1 + addu $v0, $t7, $s3 + addu $v0, $v0, $v1 +DEFSIZE(ADDX_B) + +DEFLABEL(ADDX_W) + ext $v1, $s5, SR_X_SHIFT, 1 + addu $v0, $t7, $s3 + addu $v0, $v0, $v1 +DEFSIZE(ADDX_W) + +DEFLABEL(ADDX_L) + ext $v1, $s5, SR_X_SHIFT, 1 + addu $v0, $t7, $s3 + addu $v0, $v0, $v1 +DEFSIZE(ADDX_L) + +/*************************************************************************/ + +/** + * SUB_[BWL]: Evaluate op2 - op1. + */ +DEFLABEL(SUB_B) + subu $v0, $t7, $s3 +DEFSIZE(SUB_B) + +DEFLABEL(SUB_W) + subu $v0, $t7, $s3 +DEFSIZE(SUB_W) + +DEFLABEL(SUB_L) + subu $v0, $t7, $s3 +DEFSIZE(SUB_L) + +/*-----------------------------------------------------------------------*/ + +/** + * SUBA_W: Sign-extend op1 to 32 bits, then evaluate op2 - op1. + */ +DEFLABEL(SUBA_W) + seh $v1, $s3 + subu $v0, $t7, $v1 +DEFSIZE(SUBA_W) + +/*-----------------------------------------------------------------------*/ + +/** + * SUBX_[BWL]: Evaluate op2 - op1 - X. + */ +DEFLABEL(SUBX_B) + ext $v1, $s5, SR_X_SHIFT, 1 + subu $v0, $t7, $s3 + subu $v0, $v0, $v1 +DEFSIZE(SUBX_B) + +DEFLABEL(SUBX_W) + ext $v1, $s5, SR_X_SHIFT, 1 + subu $v0, $t7, $s3 + subu $v0, $v0, $v1 +DEFSIZE(SUBX_W) + +DEFLABEL(SUBX_L) + ext $v1, $s5, SR_X_SHIFT, 1 + subu $v0, $t7, $s3 + subu $v0, $v0, $v1 +DEFSIZE(SUBX_L) + +/*************************************************************************/ + +/** + * MUL[SU]_W: Evaluate op2 * op1 in signed or unsigned context. + */ +DEFLABEL(MULS_W) + seh $v0, $t7 + seh $v1, $s3 + mul $v0, $v1 + mflo $v0 +DEFSIZE(MULS_W) + +DEFLABEL(MULU_W) + andi $v0, $t7, 0xFFFF + andi $v1, $s3, 0xFFFF + multu $v0, $v1 + mflo $v0 +DEFSIZE(MULU_W) + +/*************************************************************************/ + +/** + * DIV[SU]_W: Evaluate op2 / op1 in signed or unsigned context, setting + * the condition codes appropriately. The quotient is stored in the low + * 16 bits, the remainder in the high 16 bits of the result value. On + * overflow, op2 is copied to the result. + */ +DEFLABEL(DIVS_W) + seh $v1, $s3 + /* MIPS doesn't raise an exception on divide-by-zero, so let the + * divider unit do its thing while we check for a zero divisor */ + div $t7, $v1 + bnez $v1, 0f + // The C flag is always cleared, so do it here in the delay slot + ins $s5, $zero, SR_C_SHIFT, 1 + li $a0, EX_DIVIDE_BY_ZERO + sw $a0, Q68State_exception($s0) + TERMINATE +0: mflo $v0 + mfhi $a3 + li $a0, 0x8000 // Overflow check + addu $a0, $v0, $a0 + srl $a0, $a0, 16 + beqz $a0, 1f + sll $a3, $a3, 16 + li $a0, 1 + ins $s5, $a0, SR_V_SHIFT, 1 + j 2f + move $v0, $t7 +1: andi $v0, $v0, 0xFFFF // Need to clear upper bits in case it's negative + SETCC_NZ_W $v0 + ins $s5, $zero, SR_V_SHIFT, 1 + or $v0, $v0, $a3 +2: +DEFSIZE(DIVS_W) + +DEFLABEL(DIVU_W) + andi $v1, $s3, 0xFFFF + divu $t7, $v1 + bnez $v1, 0f + ins $s5, $zero, SR_C_SHIFT, 1 + li $a0, EX_DIVIDE_BY_ZERO + sw $a0, Q68State_exception($s0) + TERMINATE +0: mflo $v0 + mfhi $a3 + srl $a0, $v0, 16 // Overflow check (easier for unsigned quotients) + beqz $a0, 1f + sll $a3, $a3, 16 + li $a0, 1 + ins $s5, $a0, SR_V_SHIFT, 1 + j 2f + move $v0, $t7 +1: SETCC_NZ_W $v0 + ins $s5, $zero, SR_V_SHIFT, 1 + or $v0, $v0, $a3 +2: +DEFSIZE(DIVU_W) + +/*************************************************************************/ + +/** + * AND_[BWL]: Evaluate op2 & op1. + */ +DEFLABEL(AND_B) + and $v0, $t7, $s3 +DEFSIZE(AND_B) + +DEFLABEL(AND_W) + and $v0, $t7, $s3 +DEFSIZE(AND_W) + +DEFLABEL(AND_L) + and $v0, $t7, $s3 +DEFSIZE(AND_L) + +/*************************************************************************/ + +/** + * OR_[BWL]: Evaluate op2 | op1. + */ +DEFLABEL(OR_B) + or $v0, $t7, $s3 +DEFSIZE(OR_B) + +DEFLABEL(OR_W) + or $v0, $t7, $s3 +DEFSIZE(OR_W) + +DEFLABEL(OR_L) + or $v0, $t7, $s3 +DEFSIZE(OR_L) + +/*************************************************************************/ + +/** + * EOR_[BWL]: Evaluate op2 ^ op1. + */ +DEFLABEL(EOR_B) + xor $v0, $t7, $s3 +DEFSIZE(EOR_B) + +DEFLABEL(EOR_W) + xor $v0, $t7, $s3 +DEFSIZE(EOR_W) + +DEFLABEL(EOR_L) + xor $v0, $t7, $s3 +DEFSIZE(EOR_L) + +/*************************************************************************/ + +/** + * EXT_[WL]: Sign-extend op1 from 8 to 16 or from 16 to 32 bits. + */ +DEFLABEL(EXT_W) + seb $v0, $s3 +DEFSIZE(EXT_W) + +DEFLABEL(EXT_L) + seh $v0, $s3 +DEFSIZE(EXT_L) + +/*************************************************************************/ + +/** + * SWAP: Swap the upper and lower 16-bit halves of op1, placing the result + * in the result register. + */ +DEFLABEL(SWAP) + srl $v0, $s3, 16 + ins $v0, $s3, 16, 16 +DEFSIZE(SWAP) + +/*************************************************************************/ +/**************************** BCD operations *****************************/ +/*************************************************************************/ + +/** + * ABCD: Evaluate op2 + op1 + X, treating the operands as binary-coded + * decimal values. + */ +DEFLABEL(ABCD) + ext $t2, $s5, SR_X_SHIFT, 1 + andi $t0, $t7, 0xF + andi $t1, $s3, 0xF + addu $t3, $t0, $t1 + addu $t3, $t3, $t2 + sltiu $v1, $t3, 10 + beqzl $v1, 0f + addiu $t3, $t3, 6 // Skipped if no carry from the units place +0: andi $t0, $t7, 0xF0 + andi $t1, $s3, 0xF0 + addu $t2, $t0, $t1 + addu $v0, $t2, $t3 + sltiu $v1, $v0, 10<<4 + bnezl $v1, 1f + move $a3, $zero // Executed if no carry from the tens place + addiu $v0, $v0, -(10<<4) + li $a3, 1 +1: ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + andi $v1, $v0, 0xFF + snez $v1, $v1 + sll $v1, $v1, SR_Z_SHIFT + not $v1, $v1 + and $s5, $s5, $v1 +DEFSIZE(ABCD) + +/*************************************************************************/ + +/** + * SBCD: Evaluate op2 - op1 - X, treating the operands as binary-coded + * decimal values. + */ +DEFLABEL(SBCD) + ext $t2, $s5, SR_X_SHIFT, 1 + andi $t0, $t7, 0xF + andi $t1, $s3, 0xF + subu $t3, $t0, $t1 + subu $t3, $t3, $t2 + sltz $v1, $t3 + bnezl $v1, 0f + move $t2, $zero // Executed if no borrow from the units place + addiu $t3, $t3, 10 + li $t2, 1<<4 +0: andi $t0, $t7, 0xF0 + andi $t1, $s3, 0xF0 + subu $t4, $t0, $t1 + subu $t4, $t4, $t2 + sltz $v1, $t4 + bnezl $v1, 1f + move $a3, $zero // Executed if no carry from the tens place + addiu $v0, $v0, 10<<4 + li $a3, 1 +1: addu $v0, $t3, $t4 + sltz $v1, $v0 + or $a3, $a3, $v1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + andi $v1, $v0, 0xFF + snez $v1, $v1 + sll $v1, $v1, SR_Z_SHIFT + not $v1, $v1 + and $s5, $s5, $v1 +DEFSIZE(SBCD) + +/*************************************************************************/ +/*********************** Bit-twiddling operations ************************/ +/*************************************************************************/ + +/** + * BTST_[BL]: Evaluate op2 & (1 << op1). The value (1 << op1), where the + * high bits of op1 have been masked to zero, is left in $t0 for use by a + * subsequent BCHG/BCLR/BSET operation. + */ +DEFLABEL(BTST_B) + andi $a0, $s3, 7 + li $t0, 1 + sllv $t0, $t0, $a0 + and $v1, $t7, $t0 + seqz $v1, $v1 + ins $s5, $v1, SR_Z_SHIFT, 1 +DEFSIZE(BTST_B) + +DEFLABEL(BTST_L) + andi $a0, $s3, 31 + li $t0, 1 + sllv $t0, $t0, $a0 + and $v1, $t7, $t0 + seqz $v1, $v1 + ins $s5, $v1, SR_Z_SHIFT, 1 +DEFSIZE(BTST_L) + +/*************************************************************************/ + +/** + * BCHG: Evaluate op2 ^ (1 << op1), where (1 << op1) has already been + * stored in $t0. + */ +DEFLABEL(BCHG) + xor $v0, $t7, $t0 +DEFSIZE(BCHG) + +/*-----------------------------------------------------------------------*/ + +/** + * BCLR: Evaluate op2 & ~(1 << op1), where (1 << op1) has already been + * stored in $t0. + */ +DEFLABEL(BCLR) + not $v1, $t0 + and $v0, $t7, $v1 +DEFSIZE(BCLR) + +/*-----------------------------------------------------------------------*/ + +/** + * BSET: Evaluate op2 | (1 << op1), where (1 << op1) has already been + * stored in $t0. + */ +DEFLABEL(BSET) + or $v0, $t7, $t0 +DEFSIZE(BSET) + +/*************************************************************************/ +/************************ Shift/rotate operations ************************/ +/*************************************************************************/ + +/** + * ASL_[BWL]: Evaluate (signed) op2 << op1. + */ +.macro DEF_ASL nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 // Add 2 clock cycles per shift + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 1f + move $v0, $t7 + // Have to shift bit by bit to detect overflow +0: ext $a3, $v0, \nbits-1, 1 + ext $v1, $v0, \nbits-2, 1 + sll $v0, $v0, 1 + addiu $s3, $s3, -1 + xor $v1, $v1, $a3 + sll $v1, $v1, SR_V_SHIFT + bnez $s3, 0b + or $s5, $s5, $v1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 +1: +.endm + +DEFLABEL(ASL_B) + DEF_ASL 8 + SETCC_NZ_B $v0 +DEFSIZE(ASL_B) + +DEFLABEL(ASL_W) + DEF_ASL 16 + SETCC_NZ_W $v0 +DEFSIZE(ASL_W) + +DEFLABEL(ASL_L) + DEF_ASL 32 + SETCC_NZ_L $v0 +DEFSIZE(ASL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ASR_[BWL]: Evaluate (signed) op2 >> op1. + */ +.macro DEF_ASR nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 3f + move $v0, $t7 + sltiu $v1, $s3, \nbits + bnez $v1, 2f + nop +1: // count >= nbits + sra $v0, $t7, \nbits-1 + andi $a3, $v0, 1 + ins $s5, $a3, SR_C_SHIFT, 1 + j 3f + ins $s5, $a3, SR_X_SHIFT, 1 +2: // count != 0 && count < nbits + addiu $v1, $s3, -1 + srav $v0, $t7, $v1 + andi $a3, $v0, 1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + sra $v0, $v0, 1 +3: // All cases +.endm + +DEFLABEL(ASR_B) + seb $t7 + DEF_ASR 8 + SETCC_NZ_B $v0 +DEFSIZE(ASR_B) + +DEFLABEL(ASR_W) + seh $t7 + DEF_ASR 16 + SETCC_NZ_W $v0 +DEFSIZE(ASR_W) + +DEFLABEL(ASR_L) + DEF_ASR 32 + SETCC_NZ_L $v0 +DEFSIZE(ASR_L) + +/*************************************************************************/ + +/** + * LSL_[BWL]: Evaluate (unsigned) op2 << op1. + */ +.macro DEF_LSL nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 3f + move $v0, $t7 + li $a0, \nbits + sltu $v1, $s3, $a0 + bnez $v1, 2f + sltu $v1, $a0, $s3 + bnez $v1, 1f + nop +0: // count == nbits + ins $s5, $t7, SR_C_SHIFT, 1 + ins $s5, $t7, SR_X_SHIFT, 1 + j 3f + move $v0, $zero +1: // count > nbits + ins $s5, $zero, SR_X_SHIFT, 1 + j 3f + move $v0, $zero +2: // count != 0 && count < nbits + addiu $v1, $s3, -1 + sllv $v0, $t7, $v1 + ext $a3, $v0, \nbits-1, 1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + sll $v0, $v0, 1 +3: // All cases +.endm + +DEFLABEL(LSL_B) + DEF_LSL 8 + SETCC_NZ_B $v0 +DEFSIZE(LSL_B) + +DEFLABEL(LSL_W) + DEF_LSL 16 + SETCC_NZ_W $v0 +DEFSIZE(LSL_W) + +DEFLABEL(LSL_L) + DEF_LSL 32 + SETCC_NZ_L $v0 +DEFSIZE(LSL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * LSR_[BWL]: Evaluate (unsigned) op2 >> op1. + */ +.macro DEF_LSR nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 3f + move $v0, $t7 + li $a0, \nbits + sltu $v1, $s3, $a0 + bnez $v1, 2f + sltu $v1, $a0, $s3 + bnez $v1, 1f + nop +0: // count == nbits + ext $a3, $t7, \nbits-1, 1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + j 3f + move $v0, $zero +1: // count > nbits + ins $s5, $zero, SR_X_SHIFT, 1 + j 3f + move $v0, $zero +2: // count != 0 && count < nbits + addiu $v1, $s3, -1 + srlv $v0, $t7, $v1 + andi $a3, $v0, 1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 + srl $v0, $v0, 1 +3: // All cases +.endm + +DEFLABEL(LSR_B) + andi $t7, $t7, 0xFF + DEF_LSR 8 + SETCC_NZ_B $v0 +DEFSIZE(LSR_B) + +DEFLABEL(LSR_W) + andi $t7, $t7, 0xFFFF + DEF_LSR 16 + SETCC_NZ_W $v0 +DEFSIZE(LSR_W) + +DEFLABEL(LSR_L) + DEF_LSR 32 + SETCC_NZ_L $v0 +DEFSIZE(LSR_L) + +/*************************************************************************/ + +/** + * ROXL_[BWL]: Evaluate op2 ROXL op1. + */ +.macro DEF_ROXL nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ext $a3, $s5, SR_X_SHIFT, 1 + ins $s5, $a3, SR_C_SHIFT, 2 // Clear V while setting C + beqz $s3, 1f + move $v0, $t7 +0: sll $v1, $v0, 1 + ext $a0, $v0, \nbits-1, 1 + or $v0, $v1, $a3 + addiu $s3, $s3, -1 + bnez $s3, 0b + move $a3, $a0 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 +1: +.endm + +DEFLABEL(ROXL_B) + DEF_ROXL 8 + SETCC_NZ_B $v0 +DEFSIZE(ROXL_B) + +DEFLABEL(ROXL_W) + DEF_ROXL 16 + SETCC_NZ_W $v0 +DEFSIZE(ROXL_W) + +DEFLABEL(ROXL_L) + DEF_ROXL 32 + SETCC_NZ_L $v0 +DEFSIZE(ROXL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ROXR_[BWL]: Evaluate op2 ROXR op1. + */ +.macro DEF_ROXR nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ext $a3, $s5, SR_X_SHIFT, 1 + ins $s5, $a3, SR_C_SHIFT, 2 // Clear V while setting C + beqz $s3, 1f + move $v0, $t7 +0: srl $v1, $v0, 1 + ins $v1, $a3, \nbits-1, 1 + andi $a3, $v0, 1 + addiu $s3, $s3, -1 + bnez $s3, 0b + move $v0, $v1 + ins $s5, $a3, SR_C_SHIFT, 1 + ins $s5, $a3, SR_X_SHIFT, 1 +1: +.endm + +DEFLABEL(ROXR_B) + DEF_ROXR 8 + SETCC_NZ_B $v0 +DEFSIZE(ROXR_B) + +DEFLABEL(ROXR_W) + DEF_ROXR 16 + SETCC_NZ_W $v0 +DEFSIZE(ROXR_W) + +DEFLABEL(ROXR_L) + DEF_ROXR 32 + SETCC_NZ_L $v0 +DEFSIZE(ROXR_L) + +/*************************************************************************/ + +/** + * ROL_[BWL]: Evaluate op2 ROL op1. + */ +.macro DEF_ROL nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 3f + move $v0, $t7 + andi $s3, $s3, \nbits-1 + bnez $s3, 2f +1: // count != 0 && count % nbits == 0 + andi $a3, $t7, 1 // Branch delay slot from above (this is safe) + j 3f + ins $s5, $a3, SR_C_SHIFT, 1 +2: // count % nbits != 0 + li $v1, \nbits + sub $v1, $v1, $s3 + sllv $a0, $t7, $s3 + srlv $t0, $t7, $v1 + or $v0, $a0, $t0 + andi $a3, $t7, 1 + ins $s5, $a3, SR_C_SHIFT, 1 +3: // All cases +.endm + +DEFLABEL(ROL_B) + andi $t7, $t7, 0xFF + DEF_ROL 8 + SETCC_NZ_B $v0 +DEFSIZE(ROL_B) + +DEFLABEL(ROL_W) + andi $t7, $t7, 0xFFFF + DEF_ROL 16 + SETCC_NZ_W $v0 +DEFSIZE(ROL_W) + +DEFLABEL(ROL_L) + DEF_ROL 32 + SETCC_NZ_L $v0 +DEFSIZE(ROL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ROR_[BWL]: Evaluate op2 ROR op1. + */ +.macro DEF_ROR nbits + andi $s3, $s3, 0x3F + sll $v1, $s3, 1 + add $s2, $s2, $v1 + ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C + beqz $s3, 3f + move $v0, $t7 + andi $s3, $s3, \nbits-1 + bnez $s3, 2f +1: // count != 0 && count % nbits == 0 + ext $a3, $t7, \nbits-1, 1 // Branch delay slot from above (safe) + j 3f + ins $s5, $a3, SR_C_SHIFT, 1 +2: // count % nbits != 0 + li $v1, \nbits + sub $v1, $v1, $s3 + srlv $a0, $t7, $s3 + sllv $t0, $t7, $v1 + or $v0, $a0, $t0 + ext $a3, $t7, \nbits-1, 1 + ins $s5, $a3, SR_C_SHIFT, 1 +3: // All cases +.endm + +DEFLABEL(ROR_B) + andi $t7, $t7, 0xFF + DEF_ROR 8 + SETCC_NZ_B $v0 +DEFSIZE(ROR_B) + +DEFLABEL(ROR_W) + andi $t7, $t7, 0xFFFF + DEF_ROR 16 + SETCC_NZ_W $v0 +DEFSIZE(ROR_W) + +DEFLABEL(ROR_L) + DEF_ROR 32 + SETCC_NZ_L $v0 +DEFSIZE(ROR_L) + +/*************************************************************************/ +/******************* Conditional and branch operations *******************/ +/*************************************************************************/ + +/** + * Scc: Set the lower 8 bits of the result value to 0xFF if the condition + * is true, 0x00 if false. + */ +DEFLABEL(Scc) + negu $v0, $v1 +DEFSIZE(Scc) + +/*-----------------------------------------------------------------------*/ + +/** + * ADD_CYCLES_Scc_Dn: Add the appropriate number of clock cycles for an + * Scc Dn instruction to the cycle count. + */ +DEFLABEL(ADD_CYCLES_Scc_Dn) + andi $v1, $v0, 2 + addiu $v1, $v1, 4 + addu $s2, $s2, $v1 +DEFSIZE(ADD_CYCLES_Scc_Dn) + +/*************************************************************************/ + +/** + * DBcc: Jump to the specified target address unless the condition is true + * or the lower 16 bits of the given data register, after being decremented, + * are equal to -1. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7) + * target_hi: High 16 bits of target address + * target_lo: Low 16 bits of target address + */ +DEFLABEL(DBcc) + lhu $v0, 1($s0) +6: bnezl $v1, 0f + addiu $s2, $s2, 12 + addiu $v1, $v0, -1 + sh $v1, 1($s0) +7: beqzl $v0, 0f + addiu $s2, $s2, 14 + addiu $s2, $s2, 10 + lui $v0, 0x1234 +8: ori $s4, $v0, 0x5678 +9: TERMINATE +0: +DEFSIZE(DBcc) +DEFPARAM(DBcc, reg4, 6b, -4) +DEFPARAM(DBcc, reg4_b, 7b, -4) // same as reg4 +DEFPARAM(DBcc, target_hi, 8b, -4) +DEFPARAM(DBcc, target_lo, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * DBcc_native: Implement DBcc using a jump within the native code. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7) + * target_hi: High 16 bits of target 68000 address + * target_lo: Low 16 bits of target 68000 address + * native_disp_4_4: (Native displacement from end of this fragment + 4) / 4 + */ +DEFLABEL(DBcc_native) + lhu $v0, 1($s0) +6: bnezl $v1, 0f + addiu $s2, $s2, 12 + addiu $v1, $v0, -1 + sh $v1, 1($s0) +7: beqzl $v0, 0f + addiu $s2, $s2, 14 + addiu $s2, $s2, 10 + lui $v0, 0x1234 +8: ori $s4, $v0, 0x5678 +9: b .+0x1234 +5: nop +0: +DEFSIZE(DBcc_native) +DEFPARAM(DBcc_native, reg4, 6b, -4) +DEFPARAM(DBcc_native, reg4_b, 7b, -4) // same as reg4 +DEFPARAM(DBcc_native, target_hi, 8b, -4) +DEFPARAM(DBcc_native, target_lo, 9b, -4) +DEFPARAM(DBcc_native, native_disp_4_4, 5b, -4) + +/*************************************************************************/ + +/** + * Bcc_common: Jump to the specified target address if the condition is + * true. Used for both interpreted jumps (by branching to TERMINATE) and + * native jumps. + * + * [Parameters] + * target_hi: High 16 bits of target 68000 address + * target_lo: Low 16 bits of target 68000 address + * disp_4: Native displacement / 4 (either JIT_PSPOFS_TERMINATE or + * target address minus address of "nop" following branch) + */ +DEFLABEL(Bcc_common) + beqz $v1, 0f + lui $v0, 0x1234 // In the delay slot, but not a problem +8: ori $s4, $v0, 0x5678 +9: addiu $s2, $s2, 10 + b .+0x1234 +5: nop +0: +DEFSIZE(Bcc_common) +DEFPARAM(Bcc_common, target_hi, 8b, -4) +DEFPARAM(Bcc_common, target_lo, 9b, -4) +DEFPARAM(Bcc_common, disp_4, 5b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * BSR: Push the address of the next instruction onto the stack, then jump + * to the specified target address. + * + * [Parameters] + * return_addr_hi: High 16 bits of return address to push onto the stack + * return_addr_lo: High 16 bits of return address to push onto the stack + * target_hi: High 16 bits of target address + * target_lo: Low 16 bits of target address + */ +DEFLABEL(BSR) + lui $v0, 0x1234 +6: ori $v0, $v0, 0x5678 +7: PUSH32 $v0 + lui $v0, 0x1234 +8: ori $s4, $v0, 0x5678 +9: ori $s2, $s2, 0x8000 // Indicate that this is a BSR/JSR termination + TERMINATE_CONTINUE +DEFSIZE(BSR) +DEFPARAM(BSR, return_addr_hi, 6b, -4) +DEFPARAM(BSR, return_addr_lo, 7b, -4) +DEFPARAM(BSR, target_hi, 8b, -4) +DEFPARAM(BSR, target_lo, 9b, -4) + +/*************************************************************************/ + +/** + * JMP: Jump to the previously resolved effective address. + */ +DEFLABEL(JMP) + move $s4, $s6 + TERMINATE +DEFSIZE(JMP) + +/*-----------------------------------------------------------------------*/ + +/** + * JSR: Push the address of the next instruction onto the stack, then jump + * to the previously resolved effective address. + * + * [Parameters] + * return_addr_hi: High 16 bits of return address to push onto the stack + * return_addr_lo: High 16 bits of return address to push onto the stack + */ +DEFLABEL(JSR) + lui $v0, 0x1234 +8: ori $v0, $v0, 0x5678 +9: PUSH32 $v0 + move $s4, $s6 + ori $s2, $s2, 0x8000 // Indicate that this is a BSR/JSR termination + TERMINATE_CONTINUE +DEFSIZE(JSR) +DEFPARAM(JSR, return_addr_hi, 8b, -4) +DEFPARAM(JSR, return_addr_lo, 9b, -4) + +/*************************************************************************/ +/*********************** MOVEM-related operations ************************/ +/*************************************************************************/ + +/** + * STORE_DEC_[WL]: Decrement state->ea_addr, then store the specified + * register to the resulting location. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(STORE_DEC_W) + lw $v0, 1($s0) +9: addiu $s6, $s6, -2 + LOAD_DELAY_NOP + WRITE16 +DEFSIZE(STORE_DEC_W) +DEFPARAM(STORE_DEC_W, reg4, 9b, -4) + +DEFLABEL(STORE_DEC_L) + lw $v0, 1($s0) +9: addiu $s6, $s6, -4 + LOAD_DELAY_NOP + WRITE32 +DEFSIZE(STORE_DEC_L) +DEFPARAM(STORE_DEC_L, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * STORE_INC_[WL]: Store the specified register to the location indicated + * by state->ea_addr, then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(STORE_INC_W) + lw $v0, 1($s0) +9: LOAD_DELAY_NOP + LOAD_DELAY_NOP + WRITE16 + addiu $s6, $s6, 2 +DEFSIZE(STORE_INC_W) +DEFPARAM(STORE_INC_W, reg4, 9b, -4) + +DEFLABEL(STORE_INC_L) + lw $v0, 1($s0) +9: LOAD_DELAY_NOP + LOAD_DELAY_NOP + WRITE32 + addiu $s6, $s6, 4 +DEFSIZE(STORE_INC_L) +DEFPARAM(STORE_INC_L, reg4, 9b, -4) + +/*************************************************************************/ + +/** + * LOAD_INC_[WL]: Load the specified register from the location indicated + * by state->ea_addr, then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(LOAD_INC_W) + READ16 $v0 + sh $v0, 1($s0) +9: addiu $s6, $s6, 2 +DEFSIZE(LOAD_INC_W) +DEFPARAM(LOAD_INC_W, reg4, 9b, -4) + +DEFLABEL(LOAD_INC_L) + READ32 $v0 + sw $v0, 1($s0) +9: addiu $s6, $s6, 4 +DEFSIZE(LOAD_INC_L) +DEFPARAM(LOAD_INC_L, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * LOADA_INC_W: Load the specified address register from the location + * indicated by state->ea_addr, sign-extending the 16-bit value to 32 bits, + * then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(LOADA_INC_W) + READ16 $v0 + seh $v0, $v0 + sh $v0, 1($s0) +9: addiu $s6, $s6, 2 +DEFSIZE(LOADA_INC_W) +DEFPARAM(LOADA_INC_W, reg4, 9b, -4) + +/*************************************************************************/ + +/** + * MOVEM_WRITEBACK: Store the address in state->ea_addr to the specified + * address register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVEM_WRITEBACK) + sw $s6, 1($s0) +9: +DEFSIZE(MOVEM_WRITEBACK) +DEFPARAM(MOVEM_WRITEBACK, reg4, 9b, -4) + +/*************************************************************************/ +/*********************** Miscellaneous operations ************************/ +/*************************************************************************/ + +/** + * CHK_W: Raise a CHK exception if op1 < 0 or op1 > op2, treating both + * operands as signed 16-bit values. + */ +DEFLABEL(CHK_W) + seh $s3, $s3 + seh $t7, $t7 + bltzl $s3, 0f + ori $s5, $s5, SR_N + slt $v1, $t7, $s3 + beqz $v1, 1f + nop + ins $s5, $zero, SR_N_SHIFT, 1 +0: li $a0, EX_CHK + sw $a0, Q68State_exception($s0) + TERMINATE +1: +DEFSIZE(CHK_W) + +/*************************************************************************/ + +/** + * LEA: Store the previously resolved effective address in the specified + * address register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(LEA) + sw $s6, 1($s0) +9: +DEFSIZE(LEA) +DEFPARAM(LEA, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * PEA: Push the previously resolved effective address onto the stack. + */ +DEFLABEL(PEA) + PUSH32 $s6 +DEFSIZE(PEA) + +/*************************************************************************/ + +/** + * TAS: Test the 8-bit value of op1, setting the condition codes + * appropriately, then calculate op1 | 0x80. + */ +DEFLABEL(TAS) + SETCC_NZ00_B $s3 + ori $v0, $s3, 0x80 +DEFSIZE(TAS) + +/*************************************************************************/ + +/** + * MOVE_FROM_USP: Copy the user stack pointer to the specified register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVE_FROM_USP) + lw $v0, USP + LOAD_DELAY_NOP + LOAD_DELAY_NOP + sw $v0, 1($s0) +9: +DEFSIZE(MOVE_FROM_USP) +DEFPARAM(MOVE_FROM_USP, reg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * MOVE_TO_USP: Copy the specified register to the user stack pointer. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVE_TO_USP) + lw $v0, 1($s0) +9: LOAD_DELAY_NOP + LOAD_DELAY_NOP + sw $v0, USP +DEFSIZE(MOVE_TO_USP) +DEFPARAM(MOVE_TO_USP, reg4, 9b, -4) + +/*************************************************************************/ + +/** + * STOP: Halt the processor. + * + * [Parameters] + * newSR: Value to load into SR + */ +DEFLABEL(STOP) + li $v0, 1 + sw $v0, Q68State_halted($s0) + jal UPDATE_SR + ori $v0, $zero, 0x1234 +9: +DEFSIZE(STOP) +DEFPARAM(STOP, newSR, 9b, -4) + +/*************************************************************************/ + +/** + * TRAPV: Raise a TRAPV exception if the overflow flag is set. + */ +DEFLABEL(TRAPV) + ext $v1, $s5, SR_V_SHIFT, 1 + beqz $v1, 0f + li $a0, EX_TRAPV // In the delay slot, but that's okay + sw $a0, Q68State_exception($s0) + TERMINATE +0: +DEFSIZE(TRAPV) + +/*************************************************************************/ + +/** + * RTS: Pop the PC from the stack. + */ +DEFLABEL(RTS) + POP32 $s4 + ori $s2, $s2, 0xC000 // Indicate that this is an RTS/RTR termination + TERMINATE +DEFSIZE(RTS) + +/*-----------------------------------------------------------------------*/ + +/** + * RTR: Pop the condition codes and PC from the stack. + */ +DEFLABEL(RTR) + POP16 $v0 + ins $s5, $v0, 0, 8 + POP32 $s4 + ori $s2, $s2, 0xC000 // Indicate that this is an RTS/RTR termination + TERMINATE +DEFSIZE(RTR) + +/*-----------------------------------------------------------------------*/ + +/** + * RTE: Pop the status register and PC from the stack. + */ +DEFLABEL(RTE) + POP16 $s3 // Borrow op1, since POP32 will destroy temporary registers + POP32 $s4 + jal UPDATE_SR + move $v0, $s3 + TERMINATE +DEFSIZE(RTE) + +/*************************************************************************/ + +/** + * MOVEP_READ_[WL]: Read a value from memory, skipping every other byte. + * + * [Parameters] + * areg4: Register number * 4 of base address register (32-60 = A0-A7) + * disp: Displacement from base address register + * dreg4: Register number * 4 of data reg. to receive data (0-28 = D0-D7) + */ +DEFLABEL(MOVEP_READ_W) + lw $s6, 1($s0) +7: LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, 1 +8: READ8 $v0 // Byte 1 + addiu $s6, $s6, 2 + ins $s3, $v0, 8, 8 + READ8 $v0 // Byte 0 + ins $s3, $v0, 0, 8 + sh $s3, 1($s0) +9: +DEFSIZE(MOVEP_READ_W) +DEFPARAM(MOVEP_READ_W, areg4, 7b, -4) +DEFPARAM(MOVEP_READ_W, disp, 8b, -4) +DEFPARAM(MOVEP_READ_W, dreg4, 9b, -4) + +DEFLABEL(MOVEP_READ_L) + lw $s6, 1($s0) +7: LOAD_DELAY_NOP + LOAD_DELAY_NOP + addiu $s6, $s6, 1 +8: READ8 $v0 // Byte 3 + addiu $s6, $s6, 2 + ins $s3, $v0, 24, 8 + READ8 $v0 // Byte 2 + addiu $s6, $s6, 2 + ins $s3, $v0, 16, 8 + READ8 $v0 // Byte 1 + addiu $s6, $s6, 2 + ins $s3, $v0, 8, 8 + READ8 $v0 // Byte 0 + ins $s3, $v0, 0, 8 + sh $s3, 1($s0) +9: +DEFSIZE(MOVEP_READ_L) +DEFPARAM(MOVEP_READ_L, areg4, 7b, -4) +DEFPARAM(MOVEP_READ_L, disp, 8b, -4) +DEFPARAM(MOVEP_READ_L, dreg4, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * MOVEP_WRITE_[WL]: Write a value to memory, skipping every other byte. + * + * [Parameters] + * areg4: Register number * 4 of base address register (32-60 = A0-A7) + * disp: Displacement from base address register + * dreg4: Register number * 4 of data reg. containing data (0-28 = D0-D7) + */ +DEFLABEL(MOVEP_WRITE_W) + lw $s6, 1($s0) +7: lw $s3, 1($s0) +9: LOAD_DELAY_NOP + addiu $s6, $s6, 1 +8: ext $v0, $s3, 8, 8 + WRITE8 // Byte 1 + addiu $s6, $s6, 2 + ext $v0, $s3, 0, 8 + WRITE8 // Byte 0 +DEFSIZE(MOVEP_WRITE_W) +DEFPARAM(MOVEP_WRITE_W, areg4, 7b, -4) +DEFPARAM(MOVEP_WRITE_W, disp, 8b, -4) +DEFPARAM(MOVEP_WRITE_W, dreg4, 9b, -4) + +DEFLABEL(MOVEP_WRITE_L) + lw $s6, 1($s0) +7: lw $s3, 1($s0) +9: LOAD_DELAY_NOP + addiu $s6, $s6, 1 +8: ext $v0, $s3, 24, 8 + WRITE8 // Byte 3 + addiu $s6, $s6, 2 + ext $v0, $s3, 16, 8 + WRITE8 // Byte 2 + addiu $s6, $s6, 2 + ext $v0, $s3, 8, 8 + WRITE8 // Byte 1 + addiu $s6, $s6, 2 + ext $v0, $s3, 0, 8 + WRITE8 // Byte 0 +DEFSIZE(MOVEP_WRITE_L) +DEFPARAM(MOVEP_WRITE_L, areg4, 7b, -4) +DEFPARAM(MOVEP_WRITE_L, disp, 8b, -4) +DEFPARAM(MOVEP_WRITE_L, dreg4, 9b, -4) + +/*************************************************************************/ + +/** + * EXG: Exchange the values of two registers. + * + * [Parameters] + * reg1_4: Register number * 4 of first register (0-60 = D0-A7) + * reg2_4: Register number * 4 of second register (0-60 = D0-A7) + */ +DEFLABEL(EXG) + addiu $a0, $s0, 1 +8: lw $v0, 0($a0) + addiu $a1, $s0, 1 +9: lw $v1, 0($a1) + sw $v0, 0($a1) + LOAD_DELAY_NOP + sw $v1, 0($a0) +DEFSIZE(EXG) +DEFPARAM(EXG, reg1_4, 8b, -1) +DEFPARAM(EXG, reg2_4, 9b, -1) + +/*************************************************************************/ +/*************************************************************************/ diff --git a/yabause/src/q68/q68-jit-psp.h b/yabause/src/q68/q68-jit-psp.h new file mode 100644 index 0000000000..be580cddbe --- /dev/null +++ b/yabause/src/q68/q68-jit-psp.h @@ -0,0 +1,593 @@ +/* src/q68/q68-jit-psp.h: PSP dynamic translation header for Q68 + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_JIT_PSP_H +#define Q68_JIT_PSP_H + +#include + +/*************************************************************************/ + +/** + * JIT_CALL: Run translated code from the given (native) address for the + * given number of cycles. + * + * [Parameters] + * state: Processor state block + * cycles: Number of clock cycles to execute + * address_ptr: Pointer to address of native code to execute; must be + * updated on return with the next address to execute + * or NULL if the end of the block was reached + * [Return value] + * Number of clock cycles actually executed + */ +static inline int JIT_CALL(Q68State *state, int cycles, void **address_ptr) +{ + register Q68State *__state asm("s0") = state; + register int __cycles asm("s1") = cycles; + register int __cycles_out asm("s2"); + register void * __address asm("v0") = *address_ptr; + asm(".set push; .set noreorder\n" + "jalr %[address]\n" + "move $s2, $zero\n" + ".set pop" + : "=r" (__cycles), [address] "=r" (__address), "=r" (__cycles_out) + : "r" (__state), "0" (__cycles), "1" (__address) + : "at", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", + "t5", "t6", "t7", "t8", "t9", "s3", "s4", "s5", "s6", "s7", "ra", + "memory" + ); + *address_ptr = __address; + return __cycles_out; +} + +/*************************************************************************/ + +/** + * JIT_FIXUP_BRANCH: Modify a branch instruction at the given offset to + * jump to the given target. + * + * [Parameters] + * entry: Block being translated + * offset: Offset within entry->native_code of branch instruction + * (as returned in *branch_offset EMIT parameter) + * target: Target offset within entry->native_code + * [Return value] + * None + */ +static inline void JIT_FIXUP_BRANCH(Q68JitEntry *entry, uint32_t offset, + uint32_t target) +{ + int32_t disp_4 = (target - (offset + 4)) / 4; + if (disp_4 >= -32768 && disp_4 <= 32767) { + *(int16_t *)((uint8_t *)entry->native_code + offset) = disp_4; + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * The remaining macros are all used to insert a specific operation into + * the native code stream. For simplicity, we define the actual code for + * each operation in a separate assembly file, and use memcpy() to copy + * from the assembled code to the output code stream. The GEN_EMIT macro + * below is used to generate each of the JIT_EMIT_* functions; each + * function JIT_EMIT_xxx copies JIT_PSPSIZE_xxx bytes from JIT_PSP_xxx to + * the code stream, expanding the code buffer if necessary. + */ + +/* Sub-macros: */ +#define GEN_NAMESIZE(name) \ + extern const uint8_t JIT_PSP_##name[]; \ + extern const uint32_t JIT_PSPSIZE_##name; +#define GEN_PARAM(name,param) \ + extern const uint32_t JIT_PSPPARAM_##name##_##param; +#define GEN_FUNC_TOP(name) \ + if (UNLIKELY(entry->native_size - entry->native_length \ + < JIT_PSPSIZE_##name)) { \ + if (!expand_buffer(entry)) { \ + return; \ + } \ + } \ + if (JIT_PSPSIZE_##name > 0) { \ + memcpy((uint8_t *)entry->native_code + entry->native_length, \ + JIT_PSP_##name, JIT_PSPSIZE_##name); \ + } +#define GEN_COPY_PARAM(name,type,param) \ + *(type *)((uint8_t *)entry->native_code + entry->native_length \ + + JIT_PSPPARAM_##name##_##param) = param; +#define GEN_FUNC_BOTTOM(name) \ + entry->native_length += JIT_PSPSIZE_##name; + + +#define GEN_EMIT(name) \ + GEN_NAMESIZE(name) \ + static void JIT_EMIT_##name(Q68JitEntry *entry) { \ + GEN_FUNC_TOP(name) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_1(name,type1,param1) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_2(name,type1,param1,type2,param2) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_3(name,type1,param1,type2,param2,type3,param3) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + GEN_PARAM(name,param3) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2, type3 param3) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_COPY_PARAM(name, type3, param3) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_4(name,type1,param1,type2,param2,type3,param3,type4,param4) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + GEN_PARAM(name,param3) \ + GEN_PARAM(name,param4) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2, type3 param3, type4 param4) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_COPY_PARAM(name, type3, param3) \ + GEN_COPY_PARAM(name, type4, param4) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_5(name,type1,param1,type2,param2,type3,param3,type4,param4,type5,param5) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + GEN_PARAM(name,param3) \ + GEN_PARAM(name,param4) \ + GEN_PARAM(name,param5) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2, type3 param3, \ + type4 param4, type5 param5) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_COPY_PARAM(name, type3, param3) \ + GEN_COPY_PARAM(name, type4, param4) \ + GEN_COPY_PARAM(name, type5, param5) \ + GEN_FUNC_BOTTOM(name) \ + } + + +/* These versions include a hidden "disp_4" parameter, and pass the value of + * disp_4_formula for that parameter. */ + +#define GEN_EMIT_disp(name,disp_4_formula) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,disp_4) \ + static void __real_JIT_EMIT_##name(Q68JitEntry *entry, int16_t disp_4) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, int16_t, disp_4) \ + GEN_FUNC_BOTTOM(name) \ + } \ + static void JIT_EMIT_##name(Q68JitEntry *entry) { \ + __real_JIT_EMIT_##name(entry, (disp_4_formula)); \ + } + +#define GEN_EMIT_1_disp(name,type1,param1,disp_4_formula) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,disp_4) \ + static void __real_JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + int16_t disp_4) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, int16_t, disp_4) \ + GEN_FUNC_BOTTOM(name) \ + } \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ + __real_JIT_EMIT_##name(entry, param1, (disp_4_formula)); \ + } + +#define GEN_EMIT_2_disp(name,type1,param1,type2,param2,disp_4_formula) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + GEN_PARAM(name,disp_4) \ + static void __real_JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2, int16_t disp_4) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_COPY_PARAM(name, int16_t, disp_4) \ + GEN_FUNC_BOTTOM(name) \ + } \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2) { \ + __real_JIT_EMIT_##name(entry, param1, param2, (disp_4_formula)); \ + } + +/*-----------------------------------------------------------------------*/ + +/* Code prologue and epilogue */ +GEN_EMIT(PROLOGUE) +extern const int JIT_PSPOFS_TERMINATE; +extern const int JIT_PSPOFS_EXCEPTION; +extern const int JIT_PSPOFS_ADDRESS_ERROR_EA; +extern const int JIT_PSPOFS_ADDRESS_ERROR_SP; +GEN_EMIT(EPILOGUE) + +#ifdef Q68_TRACE +/* Trace the current instruction */ +GEN_EMIT(TRACE) +#endif + +/* Add the specified number of cycles to the cycle counter */ +GEN_EMIT_1(ADD_CYCLES, int16_t, cycles) + +/* Check the cycle limit and interrupt execution if necessary */ +GEN_EMIT(CHECK_CYCLES) + +/* Add the specified amount to the program counter and/or check whether + * to abort */ +GEN_EMIT_1(ADVANCE_PC, int16_t, value) +GEN_EMIT_1_disp(ADVANCE_PC_CHECK_ABORT, int16_t, value, + (JIT_PSPOFS_TERMINATE - (entry->native_length + 4)) / 4) +GEN_EMIT_disp(CHECK_ABORT, + (JIT_PSPOFS_TERMINATE - (entry->native_length + 4)) / 4) + +/* Exception raising */ +GEN_EMIT_1_disp(EXCEPTION, int16_t, num, + (JIT_PSPOFS_EXCEPTION - (entry->native_length + 4)) / 4) +GEN_EMIT_2_disp(CHECK_ALIGNED_EA, uint16_t, opcode, uint16_t, status, + (JIT_PSPOFS_ADDRESS_ERROR_EA - (entry->native_length + 4)) / 4) +GEN_EMIT_2_disp(CHECK_ALIGNED_SP, uint16_t, opcode, uint16_t, status, + (JIT_PSPOFS_ADDRESS_ERROR_SP - entry->native_length) / 4) +GEN_EMIT_disp(CHECK_SUPER, + (JIT_PSPOFS_EXCEPTION - (entry->native_length + 4)) / 4) + +/*-----------------------------------------------------------------------*/ + +/* Resolve an effective address */ +GEN_EMIT_1(RESOLVE_INDIRECT, int16_t, reg4) +GEN_EMIT_3(RESOLVE_POSTINC, int16_t, reg4, int16_t, size, int16_t, reg4_b) +#define JIT_EMIT_RESOLVE_POSTINC(entry,reg4,size) do { \ + int16_t __reg4 = (reg4); \ + JIT_EMIT_RESOLVE_POSTINC((entry), __reg4, (size), __reg4); \ +} while (0) +GEN_EMIT(RESOLVE_POSTINC_A7_B) +GEN_EMIT_3(RESOLVE_PREDEC, int16_t, reg4, int16_t, nsize, int16_t, reg4_b) +#define JIT_EMIT_RESOLVE_PREDEC(entry,reg4,size) do { \ + int16_t __reg4 = (reg4); \ + JIT_EMIT_RESOLVE_PREDEC((entry), __reg4, -(size), __reg4); \ +} while (0) +GEN_EMIT(RESOLVE_PREDEC_A7_B) +GEN_EMIT_2(RESOLVE_DISP, int16_t, reg4, int16_t, disp) +GEN_EMIT_3(RESOLVE_INDEX_W, int16_t, reg4, int16_t, ireg4, int16_t, disp) +GEN_EMIT_3(RESOLVE_INDEX_L, int16_t, reg4, int16_t, ireg4, int16_t, disp) +GEN_EMIT_2(RESOLVE_ABSOLUTE, uint16_t, addr_hi, uint16_t, addr_lo) +#define JIT_EMIT_RESOLVE_ABSOLUTE(entry,addr) do { \ + uint32_t __addr = (addr); \ + JIT_EMIT_RESOLVE_ABSOLUTE((entry), __addr>>16, __addr & 0xFFFF); \ +} while (0) +GEN_EMIT_3(RESOLVE_ABS_INDEX_W, int16_t, ireg4, uint16_t, addr_hi, + uint16_t, addr_lo) +#define JIT_EMIT_RESOLVE_ABS_INDEX_W(entry,addr,ireg4) do { \ + uint32_t __addr = (addr); \ + JIT_EMIT_RESOLVE_ABS_INDEX_W((entry), (ireg4), __addr>>16, \ + (addr) & 0xFFFF); \ +} while (0) +GEN_EMIT_3(RESOLVE_ABS_INDEX_L, int16_t, ireg4, uint16_t, addr_hi, + uint16_t, addr_lo) +#define JIT_EMIT_RESOLVE_ABS_INDEX_L(entry,addr,ireg4) do { \ + uint32_t __addr = (addr); \ + JIT_EMIT_RESOLVE_ABS_INDEX_L((entry), (ireg4), __addr>>16, \ + __addr & 0xFFFF); \ +} while (0) + +/* Retrieve various things as operand 1 */ +GEN_EMIT_1(GET_OP1_REGISTER, int16_t, reg4) +GEN_EMIT(GET_OP1_EA_B) +GEN_EMIT(GET_OP1_EA_W) +GEN_EMIT(GET_OP1_EA_L) +GEN_EMIT_1(GET_OP1_IMMED_16S, int16_t, value_lo) +GEN_EMIT_1(GET_OP1_IMMED_16U, uint16_t, value_lo) +GEN_EMIT_1(GET_OP1_IMMED_16HI, uint16_t, value_hi) +GEN_EMIT_2(GET_OP1_IMMED_32, uint16_t, value_hi, uint16_t, value_lo) +#define JIT_EMIT_GET_OP1_IMMEDIATE(entry,value) do { \ + Q68JitEntry * const __entry = (entry); \ + const int32_t __value = (value); \ + if (value >= -32768 && value <= 32767) { \ + JIT_EMIT_GET_OP1_IMMED_16S(__entry, __value); \ + } else if (value >= 0 && value <= 65535) { \ + JIT_EMIT_GET_OP1_IMMED_16U(__entry, __value); \ + } else if ((value & 0xFFFF) == 0) { \ + JIT_EMIT_GET_OP1_IMMED_16HI(__entry, __value>>16); \ + } else { \ + JIT_EMIT_GET_OP1_IMMED_32(__entry, __value>>16, __value & 0xFFFF); \ + } \ +} while (0) +GEN_EMIT(GET_OP1_CCR) +GEN_EMIT(GET_OP1_SR) + +/* Retrieve various things as operand 2 */ +GEN_EMIT_1(GET_OP2_REGISTER, int16_t, reg4) +GEN_EMIT(GET_OP2_EA_B) +GEN_EMIT(GET_OP2_EA_W) +GEN_EMIT(GET_OP2_EA_L) +GEN_EMIT_1(GET_OP2_IMMED_16S, int16_t, value_lo) +GEN_EMIT_1(GET_OP2_IMMED_16U, uint16_t, value_lo) +GEN_EMIT_1(GET_OP2_IMMED_16HI, uint16_t, value_hi) +GEN_EMIT_2(GET_OP2_IMMED_32, uint16_t, value_hi, uint16_t, value_lo) +#define JIT_EMIT_GET_OP2_IMMEDIATE(entry,value) do { \ + Q68JitEntry * const __entry = (entry); \ + const int32_t __value = (value); \ + if (value >= -32768 && value <= 32767) { \ + JIT_EMIT_GET_OP2_IMMED_16S(__entry, __value); \ + } else if (value >= 0 && value <= 65535) { \ + JIT_EMIT_GET_OP2_IMMED_16U(__entry, __value); \ + } else if ((value & 0xFFFF) == 0) { \ + JIT_EMIT_GET_OP2_IMMED_16HI(__entry, __value>>16); \ + } else { \ + JIT_EMIT_GET_OP2_IMMED_32(__entry, __value>>16, __value & 0xFFFF); \ + } \ +} while (0) +GEN_EMIT(GET_OP2_CCR) +GEN_EMIT(GET_OP2_SR) + +/* Update various things from result */ +GEN_EMIT_1(SET_REGISTER_B, int16_t, reg4) +GEN_EMIT_1(SET_REGISTER_W, int16_t, reg4) +GEN_EMIT_1(SET_REGISTER_L, int16_t, reg4) +GEN_EMIT_1(SET_AREG_W, int16_t, reg4) +GEN_EMIT(SET_EA_B) +GEN_EMIT(SET_EA_W) +GEN_EMIT(SET_EA_L) +GEN_EMIT(SET_CCR) +GEN_EMIT(SET_SR) + +/* Stack operations */ +GEN_EMIT(PUSH_L) +GEN_EMIT(POP_L) + +/* Condition code setting */ +GEN_EMIT(SETCC_ADD_B) +GEN_EMIT(SETCC_ADD_W) +GEN_EMIT(SETCC_ADD_L) +GEN_EMIT(SETCC_ADDX_B) +GEN_EMIT(SETCC_ADDX_W) +GEN_EMIT(SETCC_ADDX_L) +GEN_EMIT(SETCC_SUB_B) +GEN_EMIT(SETCC_SUB_W) +GEN_EMIT(SETCC_SUB_L) +GEN_EMIT(SETCC_SUBX_B) +GEN_EMIT(SETCC_SUBX_W) +GEN_EMIT(SETCC_SUBX_L) +GEN_EMIT(SETCC_CMP_B) +GEN_EMIT(SETCC_CMP_W) +GEN_EMIT(SETCC_CMP_L) +GEN_EMIT(SETCC_LOGIC_B) +GEN_EMIT(SETCC_LOGIC_W) +GEN_EMIT(SETCC_LOGIC_L) + +/* Condition testing */ +GEN_EMIT(TEST_T) +GEN_EMIT(TEST_F) +GEN_EMIT(TEST_HI) +GEN_EMIT(TEST_LS) +GEN_EMIT(TEST_CC) +GEN_EMIT(TEST_CS) +GEN_EMIT(TEST_NE) +GEN_EMIT(TEST_EQ) +GEN_EMIT(TEST_VC) +GEN_EMIT(TEST_VS) +GEN_EMIT(TEST_PL) +GEN_EMIT(TEST_MI) +GEN_EMIT(TEST_GE) +GEN_EMIT(TEST_LT) +GEN_EMIT(TEST_GT) +GEN_EMIT(TEST_LE) + +/* ALU operations */ +GEN_EMIT(MOVE_B) +GEN_EMIT(MOVE_W) +GEN_EMIT(MOVE_L) +GEN_EMIT(ADD_B) +GEN_EMIT(ADD_W) +GEN_EMIT(ADD_L) +GEN_EMIT(ADDA_W) +GEN_EMIT(ADDX_B) +GEN_EMIT(ADDX_W) +GEN_EMIT(ADDX_L) +GEN_EMIT(SUB_B) +GEN_EMIT(SUB_W) +GEN_EMIT(SUB_L) +GEN_EMIT(SUBA_W) +GEN_EMIT(SUBX_B) +GEN_EMIT(SUBX_W) +GEN_EMIT(SUBX_L) +GEN_EMIT(MULS_W) +GEN_EMIT(MULU_W) +GEN_EMIT(DIVS_W) +GEN_EMIT(DIVU_W) +GEN_EMIT(AND_B) +GEN_EMIT(AND_W) +GEN_EMIT(AND_L) +GEN_EMIT(OR_B) +GEN_EMIT(OR_W) +GEN_EMIT(OR_L) +GEN_EMIT(EOR_B) +GEN_EMIT(EOR_W) +GEN_EMIT(EOR_L) +GEN_EMIT(EXT_W) +GEN_EMIT(EXT_L) +GEN_EMIT(SWAP) + +/* BCD operations */ +GEN_EMIT(ABCD) +GEN_EMIT(SBCD) + +/* Bit-twiddling operations */ +GEN_EMIT(BTST_B) +GEN_EMIT(BTST_L) +GEN_EMIT(BCHG) +GEN_EMIT(BCLR) +GEN_EMIT(BSET) + +/* Shift/rotate operations */ +GEN_EMIT(ASL_B) +GEN_EMIT(ASL_W) +GEN_EMIT(ASL_L) +GEN_EMIT(ASR_B) +GEN_EMIT(ASR_W) +GEN_EMIT(ASR_L) +GEN_EMIT(LSL_B) +GEN_EMIT(LSL_W) +GEN_EMIT(LSL_L) +GEN_EMIT(LSR_B) +GEN_EMIT(LSR_W) +GEN_EMIT(LSR_L) +GEN_EMIT(ROXL_B) +GEN_EMIT(ROXL_W) +GEN_EMIT(ROXL_L) +GEN_EMIT(ROXR_B) +GEN_EMIT(ROXR_W) +GEN_EMIT(ROXR_L) +GEN_EMIT(ROL_B) +GEN_EMIT(ROL_W) +GEN_EMIT(ROL_L) +GEN_EMIT(ROR_B) +GEN_EMIT(ROR_W) +GEN_EMIT(ROR_L) + +/* Conditional and branch operations ("branch_offset" parameter receives + * the native offset of the branch to update when resolving, or -1 if not + * supported) */ +GEN_EMIT(Scc) +GEN_EMIT(ADD_CYCLES_Scc_Dn) +GEN_EMIT_4(DBcc, int16_t, reg4, int16_t, reg4_b, uint16_t, target_hi, + uint16_t, target_lo) +#define JIT_EMIT_DBcc(entry,reg4,target) do { \ + int16_t __reg4 = (reg4); \ + uint32_t __target = (target); \ + JIT_EMIT_DBcc((entry), __reg4, __reg4, __target>>16, __target & 0xFFFF); \ +} while (0) +GEN_EMIT_5(DBcc_native, int16_t, reg4, int16_t, reg4_b, uint16_t, target_hi, + uint16_t, target_lo, int16_t, native_disp_4_4) +#define JIT_EMIT_DBcc_native(entry,reg4,target,offset) do { \ + Q68JitEntry *__entry = (entry); \ + int16_t __reg4 = (reg4); \ + uint32_t __target = (target); \ + int32_t __fragment_end = __entry->native_length + JIT_PSPSIZE_DBcc_native; \ + JIT_EMIT_DBcc_native(__entry, __reg4, __reg4, \ + __target>>16, __target & 0xFFFF, \ + ((offset) - __fragment_end + 4) / 4); \ +} while (0) +GEN_EMIT_3(Bcc_common, uint16_t, target_hi, uint16_t, target_lo, int16_t, disp_4) +static void JIT_EMIT_Bcc(Q68JitEntry *entry, uint32_t target, + int32_t *branch_offset) { + *branch_offset = entry->native_length + JIT_PSPPARAM_Bcc_common_disp_4; + int32_t disp_4 = (JIT_PSPOFS_TERMINATE - (*branch_offset + 4)) / 4; + JIT_EMIT_Bcc_common(entry, target>>16, target & 0xFFFF, disp_4); +} +static void JIT_EMIT_Bcc_native(Q68JitEntry *entry, uint32_t target, + int32_t offset) { + uint32_t branch_offset = + entry->native_length + JIT_PSPPARAM_Bcc_common_disp_4; + int32_t disp_4 = (offset - (branch_offset + 4)) / 4; + /* Displacement is assumed to be within range (+/-128k) */ + JIT_EMIT_Bcc_common(entry, target>>16, target & 0xFFFF, disp_4); +} +GEN_EMIT_4(BSR, uint16_t, return_addr_hi, uint16_t, return_addr_lo, + uint16_t, target_hi, uint16_t, target_lo) +#define JIT_EMIT_BSR(entry,return_addr,target) do { \ + uint32_t __return_addr = (return_addr); \ + uint32_t __target = (target); \ + JIT_EMIT_BSR((entry), __return_addr>>16, __return_addr & 0xFFFF, \ + __target>>16, __target & 0xFFFF); \ +} while (0) +GEN_EMIT(JMP) +GEN_EMIT_2(JSR, uint16_t, return_addr_hi, uint16_t, return_addr_lo) +#define JIT_EMIT_JSR(entry,return_addr) do { \ + uint32_t __return_addr = (return_addr); \ + JIT_EMIT_JSR((entry), __return_addr>>16, __return_addr & 0xFFFF); \ +} while (0) + +/* MOVEM-related operations */ +GEN_EMIT_1(STORE_DEC_W, int16_t, reg4) +GEN_EMIT_1(STORE_DEC_L, int16_t, reg4) +GEN_EMIT_1(STORE_INC_W, int16_t, reg4) +GEN_EMIT_1(STORE_INC_L, int16_t, reg4) +GEN_EMIT_1(LOAD_INC_W, int16_t, reg4) +GEN_EMIT_1(LOAD_INC_L, int16_t, reg4) +GEN_EMIT_1(LOADA_INC_W, int16_t, reg4) +GEN_EMIT_1(MOVEM_WRITEBACK, int16_t, reg4) + +/* Miscellaneous operations */ +GEN_EMIT(CHK_W) +GEN_EMIT_1(LEA, int16_t, reg4) +GEN_EMIT(PEA) +GEN_EMIT(TAS) +GEN_EMIT_1(MOVE_FROM_USP, int16_t, reg4) +GEN_EMIT_1(MOVE_TO_USP, int16_t, reg4) +GEN_EMIT_1(STOP, uint16_t, newSR) +GEN_EMIT(TRAPV) +GEN_EMIT(RTS) +GEN_EMIT(RTR) +GEN_EMIT(RTE) +GEN_EMIT_3(MOVEP_READ_W, int16_t, areg4, int16_t, disp, int16_t, dreg4) +GEN_EMIT_3(MOVEP_READ_L, int16_t, areg4, int16_t, disp, int16_t, dreg4) +GEN_EMIT_3(MOVEP_WRITE_W, int16_t, areg4, int16_t, disp, int16_t, dreg4) +GEN_EMIT_3(MOVEP_WRITE_L, int16_t, areg4, int16_t, disp, int16_t, dreg4) +GEN_EMIT_2(EXG, int16_t, reg1_4, int16_t, reg2_4) + +/*************************************************************************/ + +#endif // Q68_JIT_PSP_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-jit-x86.S b/yabause/src/q68/q68-jit-x86.S new file mode 100644 index 0000000000..d6331dbf20 --- /dev/null +++ b/yabause/src/q68/q68-jit-x86.S @@ -0,0 +1,2800 @@ +/* src/q68/q68-jit-x86.S: x86 (32/64-bit) dynamic translation implementation + for Q68 + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "q68-const.h" + +/*************************************************************************/ + +/* + * Register usage on x86 and x64 is as follows (for x86, read %rXX as %eXX): + * + * %rax -- result, temporary + * %rbx -- Q68State structure pointer + * %rcx -- temporary + * %rdx -- operand 2 + * %rsi -- cumulative cycle count + * %rdi -- operand 1, (on termination) pointer to next address to execute + * + * Additionally, the cycle limit is pushed onto the stack at the start of + * execution. + */ + +/*************************************************************************/ + +/* Define handy macros so we can use the same source on both x86 and x64 */ + +#ifdef CPU_X64 + +/* External routine calling macros (indirect calls only) */ +.macro CALL1 address, arg1 + push %rsi + push %rdi + mov \arg1, %rdi + call \address + pop %rdi + pop %rsi +.endm +.macro CALL2 address, arg1, arg2 + push %rsi + push %rdi + mov \arg1, %rdi + mov \arg2, %rsi + call \address + pop %rdi + pop %rsi +.endm + +/* Label/size/parameter definition macros */ +#define DEFLABEL(name) .globl JIT_X64_##name; JIT_X64_##name: +#define DEFSIZE(name) .globl JIT_X64SIZE_##name; \ + JIT_X64SIZE_##name: .int . - JIT_X64_##name +#define DEFPARAM(name,param,label,offset) \ + .globl JIT_X64PARAM_##name##_##param; \ + JIT_X64PARAM_##name##_##param: \ + .int label - JIT_X64_##name + (offset) + +/* Q68State structure offsets */ +Q68State_D = 0 +Q68State_A = 32 +Q68State_PC = 64 +Q68State_SR = 68 +Q68State_USP = 72 +Q68State_SSP = 76 +Q68State_current_PC = 80 +Q68State_ea_addr = 84 +Q68State_exception = 88 +Q68State_fault_addr = 92 +Q68State_fault_opcode = 96 +Q68State_fault_status = 98 +Q68State_halted = 100 +Q68State_irq = 104 +Q68State_cycles = 108 +Q68State_malloc_func = 112 +Q68State_realloc_func = 120 +Q68State_free_func = 128 +Q68State_readb_func = 136 +Q68State_readw_func = 144 +Q68State_writeb_func = 152 +Q68State_writew_func = 160 +Q68State_jit_flush = 168 +Q68State_jit_running = 176 +Q68State_jit_abort = 184 +Q68State_jit_table = 192 +Q68State_jit_hashchain = 200 +Q68State_jit_total_data = 208 +Q68State_jit_timestamp = 212 +Q68State_jit_blacklist = 216 +Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) +Q68State_jit_blist_num = Q68State_jit_in_blist + 4 +Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 +Q68State_jit_callstack = (Q68State_jit_callstack_top + 7) & ~7 +Q68State_jit_pages = Q68State_jit_callstack + (24 * Q68_JIT_CALLSTACK_SIZE) + +#else // CPU_X86 + +/* Register name translation macros (we don't handle any 64-bit data except + * when protected by #ifdef CPU_X64, so this is safe) */ +#define rax eax +#define rbx ebx +#define rcx ecx +#define rdx edx +#define rsp esp +#define rbp ebp +#define rsi esi +#define rdi edi + +/* External routine calling macros (indirect calls only) */ +.macro CALL1 address, arg1 + push \arg1 + call \address + pop %rcx +.endm +.macro CALL2 address, arg1, arg2 + push \arg2 + push \arg1 + call \address + pop %rcx + pop %rcx +.endm + +/* Label/size/parameter definition macros */ +#define DEFLABEL(name) .globl JIT_X86_##name; JIT_X86_##name: +#define DEFSIZE(name) .globl JIT_X86SIZE_##name; \ + JIT_X86SIZE_##name: .int . - JIT_X86_##name +#define DEFPARAM(name,param,label,offset) \ + .globl JIT_X86PARAM_##name##_##param; \ + JIT_X86PARAM_##name##_##param: \ + .int label - JIT_X86_##name + (offset) + +/* Q68State structure offsets */ +Q68State_D = 0 +Q68State_A = 32 +Q68State_PC = 64 +Q68State_SR = 68 +Q68State_USP = 72 +Q68State_SSP = 76 +Q68State_current_PC = 80 +Q68State_ea_addr = 84 +Q68State_exception = 88 +Q68State_fault_addr = 92 +Q68State_fault_opcode = 96 +Q68State_fault_status = 98 +Q68State_halted = 100 +Q68State_irq = 104 +Q68State_cycles = 108 +Q68State_malloc_func = 112 +Q68State_realloc_func = 116 +Q68State_free_func = 120 +Q68State_readb_func = 124 +Q68State_readw_func = 128 +Q68State_writeb_func = 132 +Q68State_writew_func = 136 +Q68State_jit_flush = 140 +Q68State_jit_running = 144 +Q68State_jit_abort = 148 +Q68State_jit_table = 152 +Q68State_jit_hashchain = 156 +Q68State_jit_total_data = 160 +Q68State_jit_timestamp = 164 +Q68State_jit_blacklist = 168 +Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) +Q68State_jit_blist_num = Q68State_jit_in_blist + 4 +Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 +Q68State_jit_callstack = Q68State_jit_callstack_top + 4 +Q68State_jit_pages = Q68State_jit_callstack + (12 * Q68_JIT_CALLSTACK_SIZE) + +#endif // X64/X86 + +/*************************************************************************/ + +/* Shorthand for referencing Q68State fields */ + +#define D0 Q68State_D+0*4(%rbx) +#define D1 Q68State_D+1*4(%rbx) +#define D2 Q68State_D+2*4(%rbx) +#define D3 Q68State_D+3*4(%rbx) +#define D4 Q68State_D+4*4(%rbx) +#define D5 Q68State_D+5*4(%rbx) +#define D6 Q68State_D+6*4(%rbx) +#define D7 Q68State_D+7*4(%rbx) + +#define A0 Q68State_A+0*4(%rbx) +#define A1 Q68State_A+1*4(%rbx) +#define A2 Q68State_A+2*4(%rbx) +#define A3 Q68State_A+3*4(%rbx) +#define A4 Q68State_A+4*4(%rbx) +#define A5 Q68State_A+5*4(%rbx) +#define A6 Q68State_A+6*4(%rbx) +#define A7 Q68State_A+7*4(%rbx) + +#define PC Q68State_PC(%rbx) +#define SR Q68State_SR(%rbx) +#define USP Q68State_USP(%rbx) +#define SSP Q68State_SSP(%rbx) + +/*************************************************************************/ +/************************** Convenience macros ***************************/ +/*************************************************************************/ + +/** + * READ{8,16,32}: Read a value from memory. The value read is returned + * zero-extended in %eax; the address parameter is destroyed. %rdx may not + * be used as a parameter. + */ +.macro READ8 address + and $0x00FFFFFF, \address + mov Q68State_readb_func(%rbx), %rdx + CALL1 *%rdx, \address + movzx %al, %eax +.endm + +.macro READ16 address + and $0x00FFFFFF, \address + mov Q68State_readw_func(%rbx), %rdx + CALL1 *%rdx, \address + movzx %ax, %eax +.endm + +.macro READ32 address + and $0x00FFFFFF, \address + mov Q68State_readw_func(%rbx), %rdx +#ifdef CPU_X64 + push %rdi + mov \address, %rdi + call *%rdx + push %rax + add $2, %rdi + and $0x00FFFFFF, %rdi + mov Q68State_readw_func(%rbx), %rdx + call *%rdx + pop %rcx + pop %rdi +#else + push \address + call *%rdx + xchg %rax, (%rsp) + addl $2, %rax + push %rax + mov Q68State_readw_func(%rbx), %rdx + call *%rdx + pop %rcx + pop %rcx +#endif + shl $16, %ecx + or %ecx, %eax +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * WRITE_CHECK_JIT: Check whether a write of size \nbytes (*bytes*, not + * bits) to \address would clobber a page containing already-translated + * blocks, and clear those translations if so. The value of \address is + * preserved, but all other caller-saved registers are destroyed. + * + * Note that this macro uses local label 4. + */ +.macro WRITE_CHECK_JIT address, nbytes + push \address + mov \address, %rdx + shr $Q68_JIT_PAGE_BITS+3, %rdx + mov \address, %rcx + shr $Q68_JIT_PAGE_BITS, %rcx + and $7, %cl + mov $1, %al + shl %cl, %al + test %al, Q68State_jit_pages(%rbx,%rdx,1) + jz 4f + /* Have to use an indirect call because the offset for the call + * instruction will change based on where this code is copied */ + mov (%rsp), \address +#ifdef CPU_X64 + mov $q68_jit_clear_write, %r8 + mov $\nbytes, %edx + CALL2 *%r8, %rbx, \address +#else + mov $q68_jit_clear_write, %edx + pushl $\nbytes + CALL2 *%edx, %ebx, \address + pop %ecx +#endif +4: pop \address +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * WRITE{8,16,32}: Write a value to memory. %rdx may not be used as a + * parameter; the address parameter is destroyed. + */ +.macro WRITE8 address, value + and $0x00FFFFFF, \address + push \value + WRITE_CHECK_JIT \address, 1 + pop \value + mov Q68State_writeb_func(%rbx), %rdx + CALL2 *%rdx, \address, \value +.endm + +.macro WRITE16 address, value + and $0x00FFFFFF, \address + push \value + WRITE_CHECK_JIT \address, 2 + pop \value + mov Q68State_writew_func(%rbx), %rdx + CALL2 *%rdx, \address, \value +.endm + +.macro WRITE32 address, value + push \value + push \address + shr $16, \value + WRITE16 \address, \value + pop \address + pop \value + add $2, \address + WRITE16 \address, \value +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * {PUSH,POP}{16,32}: Push or pop values onto or off of the stack. For + * POP, the value popped is zero-extended and returned in %rax; for PUSH, + * register %rax is destroyed before storing. %rdx may not be used as a + * parameter. + */ +.macro PUSH16 value + mov A7, %eax + sub $2, %eax + mov %eax, A7 + WRITE16 %rax, \value +.endm + +.macro PUSH32 value + mov A7, %eax + sub $4, %eax + mov %eax, A7 + WRITE32 %rax, \value +.endm + +.macro POP16 + mov A7, %eax + add $2, A7 + READ16 %rax +.endm + +.macro POP32 + mov A7, %eax + add $4, A7 + READ32 %rax +.endm + +/*************************************************************************/ + +/** + * LDC_FROM_X: Set the x86 carry flag (CF) based on the value of the + * 68000 extend flag (X). The byte register passed in \temp is destroyed. + */ +.macro LDC_FROM_X temp + mov SR, \temp + shr $5, \temp +.endm + +/*************************************************************************/ + +/** + * SETCC_NZ: Set the N and Z condition codes according to the x86 flag bits. + */ +.macro SETCC_NZ + sets %cl + setz %dl + andb $~(SR_N|SR_Z), SR + shl $SR_N_SHIFT, %cl + shl $SR_Z_SHIFT, %dl + or %cl, %dl + or %dl, SR +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_NZ00: Set the N and Z condition codes according to the x86 flag + * bits, and clear the V and C condition codes. + */ +.macro SETCC_NZ00 + sets %cl + setz %dl + andb $~(SR_N|SR_Z|SR_V|SR_C), SR + shl $SR_N_SHIFT, %cl + shl $SR_Z_SHIFT, %dl + or %cl, %dl + or %dl, SR +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_NZVC: Set the N, Z, V, and C condition codes according to the x86 + * flag bits. + */ +.macro SETCC_NZVC + sets %cl + setz %dl + seto %ch + setc %dh + andb $~(SR_N|SR_Z|SR_V|SR_C), SR + shl $SR_N_SHIFT, %cl + shl $SR_Z_SHIFT, %dl + shl $SR_V_SHIFT, %ch + //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift + or %ch, %cl + or %dh, %dl + or %cl, %dl + or %dl, SR +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_XNZVC: Set the N, Z, V, and C condition codes according to the + * x86 flag bits, and sets the X condition code equal to C. + */ +.macro SETCC_XNZVC + sets %cl + setz %dl + seto %ch + setc %dh + andb $~(SR_X|SR_N|SR_Z|SR_V|SR_C), SR + shl $SR_N_SHIFT, %cl + shl $SR_Z_SHIFT, %dl + shl $SR_V_SHIFT, %ch + //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift + or %ch, %cl + or %dh, %dl + shl $(SR_X_SHIFT - SR_C_SHIFT), %dh + or %dh, %dl + or %cl, %dl + or %dl, SR +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * SETCC_XNVC_Z: Set the N, V, and C condition codes according to the x86 + * flag bits; clears the Z condition code if the x86 Z flag is clear, and + * sets the X condition code equal to C. + */ +.macro SETCC_XNVC_Z + sets %cl + setnz %dl + seto %ch + setc %dh + andb $~(SR_X|SR_N|SR_V|SR_C), SR + shl $SR_N_SHIFT, %cl + shl $SR_Z_SHIFT, %dl + shl $SR_V_SHIFT, %ch + //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift + or %ch, %cl + or %dh, %cl + shl $(SR_X_SHIFT - SR_C_SHIFT), %dh + or %dh, %cl + or %cl, SR + not %dl + and %dl, SR +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * UPDATE_SR: Set the status register and condition codes according to + * the value in %ax. + * + * Note that this macro uses local labels 0, 1, 2, and 3. + */ +.macro UPDATE_SR value + movzwl %ax, %ecx + xor SR, %eax + mov %ecx, SR + test $SR_S, %eax // Change in S bit? + jz 1f + test $SR_S, %ecx // Which way did it change? + jz 0f + mov A7, %eax // Into supervisor mode + mov %eax, USP + mov SSP, %eax + mov %eax, A7 + jmp 1f +0: mov A7, %eax // Out of supervisor mode + mov %eax, SSP + mov USP, %eax + mov %eax, A7 +1: mov Q68State_irq(%rbx), %al + and $7, %al + cmp $7, %al + je 2f + and $7, %ch + cmp %al, %ch + jae 3f +2: movzx %al, %eax + add $EX_LEVEL_1_INTERRUPT-1, %eax + mov %eax, Q68State_exception(%rbx) + movl $0, Q68State_irq(%rbx) + TERMINATE +3: +.endm + +/*************************************************************************/ + +/** + * SETUP: Perform setup required before executing translated code. + */ +.macro SETUP + push %rsi + xor %esi, %esi +.endm + +/*-----------------------------------------------------------------------*/ + +/** + * TERMINATE: Terminate execution of the current block. The emulator will + * resume execution at the address in state->PC. + */ +.macro TERMINATE + pop %rax + xor %rdi, %rdi + ret +.endm + +/*************************************************************************/ +/**************************** Meta-operations ****************************/ +/*************************************************************************/ + +/** + * PROLOGUE: Any prologue necessary at the beginning of the code stream. + */ +DEFLABEL(PROLOGUE) + SETUP +DEFSIZE(PROLOGUE) + +/*-----------------------------------------------------------------------*/ + +/** + * EPILOGUE: Any epilogue necessary at the end of the code stream. + */ +DEFLABEL(EPILOGUE) + TERMINATE +DEFSIZE(EPILOGUE) + +/*************************************************************************/ + +/** + * TRACE: Trace the current instruction. + */ +DEFLABEL(TRACE) + mov Q68State_cycles(%rbx), %eax + push %rax + add %esi, %eax + mov %eax, Q68State_cycles(%rbx) +#ifdef CPU_X64 + push %rsi + push %rdi +#endif + mov $q68_trace, %rdx + call *%rdx +#ifdef CPU_X64 + pop %rdi + pop %rsi +#endif + pop %rax + mov %eax, Q68State_cycles(%rbx) +DEFSIZE(TRACE) + +/*************************************************************************/ + +/** + * ADD_CYCLES: Add the specified number of clock cycles to the cycle count. + * + * [Parameters] + * cycles: Number of clock cycles to add + */ +DEFLABEL(ADD_CYCLES) + add $0x12345678, %esi +9: +DEFSIZE(ADD_CYCLES) +DEFPARAM(ADD_CYCLES, cycles, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_CYCLES: Check whether the clock cycle limit has been reached, and + * interrupt execution if so. + */ +DEFLABEL(CHECK_CYCLES) + cmp %esi, (%rsp) + ja 4f + pop %rax + call 1f +0: jmp 2f +1: mov (%rsp), %rdi + ret +2: add $3f-0b, %rdi + ret +3: SETUP +4: +DEFSIZE(CHECK_CYCLES) + +/*************************************************************************/ + +/** + * ADVANCE_PC: Add the specified value to the current program counter. + * + * [Parameters] + * value: Amount to add + */ +DEFLABEL(ADVANCE_PC) + addl $0x12345678, PC +9: +DEFSIZE(ADVANCE_PC) +DEFPARAM(ADVANCE_PC, value, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * ADVANCE_PC_CHECK_ABORT: Add the specified value to the current program + * counter, then check the jit_abort flag and abort if necessary. + * + * [Parameters] + * value: Amount to add + */ +DEFLABEL(ADVANCE_PC_CHECK_ABORT) + addl $0x12345678, PC +9: testb $1, Q68State_jit_abort(%rbx) + jz 0f + TERMINATE +0: +DEFSIZE(ADVANCE_PC_CHECK_ABORT) +DEFPARAM(ADVANCE_PC_CHECK_ABORT, value, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ABORT: Check the jit_abort flag and abort if necessary. + */ +DEFLABEL(CHECK_ABORT) + testb $1, Q68State_jit_abort(%rbx) + jz 0f + TERMINATE +0: +DEFSIZE(CHECK_ABORT) + +/*************************************************************************/ + +/** + * EXCEPTION: Raise the specified exception. + * + * [Parameters] + * num: Exception number + */ +DEFLABEL(EXCEPTION) + movl $0x12345678, Q68State_exception(%rbx) +9: TERMINATE +DEFSIZE(EXCEPTION) +DEFPARAM(EXCEPTION, num, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ALIGNED_EA: Check whether the previously resolved effective + * address is word-aligned (bit 0 is clear), and raise an address error + * exception if not. + * + * [Parameters] + * opcode: Instruction opcode + * status: Status word for address error exception + */ +DEFLABEL(CHECK_ALIGNED_EA) + testl $1, Q68State_ea_addr(%rbx) + jz 0f + movl $EX_ADDRESS_ERROR, Q68State_exception(%rbx) + mov Q68State_ea_addr(%rbx), %eax + mov %eax, Q68State_fault_addr(%rbx) + movw $0x1234, Q68State_fault_opcode(%rbx) +8: movw $0x1234, Q68State_fault_status(%rbx) +9: TERMINATE +0: +DEFSIZE(CHECK_ALIGNED_EA) +DEFPARAM(CHECK_ALIGNED_EA, opcode, 8b, -2) +DEFPARAM(CHECK_ALIGNED_EA, status, 9b, -2) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_ALIGNED_SP: Check whether the current stack pointer (register A7) + * is word-aligned (bit 0 is clear), and raise an address error exception + * if not. + * + * [Parameters] + * opcode: Instruction opcode + * status: Status word for address error exception + */ +DEFLABEL(CHECK_ALIGNED_SP) + testl $1, A7 + jz 0f + movl $EX_ADDRESS_ERROR, Q68State_exception(%rbx) + mov A7, %eax + mov %eax, Q68State_fault_addr(%rbx) + movw $0x1234, Q68State_fault_opcode(%rbx) +8: movw $0x1234, Q68State_fault_status(%rbx) +9: TERMINATE +0: +DEFSIZE(CHECK_ALIGNED_SP) +DEFPARAM(CHECK_ALIGNED_SP, opcode, 8b, -2) +DEFPARAM(CHECK_ALIGNED_SP, status, 9b, -2) + +/*-----------------------------------------------------------------------*/ + +/** + * CHECK_SUPER: Check whether the processor is in supervisor mode, and + * raise a privilege violation exception if not. + */ +DEFLABEL(CHECK_SUPER) + testl $SR_S, SR + jnz 0f + movl $EX_PRIVILEGE_VIOLATION, Q68State_exception(%rbx) + TERMINATE +0: +DEFSIZE(CHECK_SUPER) + +/*************************************************************************/ +/********************* Effective address resolution **********************/ +/*************************************************************************/ + +/** + * RESOLVE_INDIRECT: Resolve an address register indirect reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + */ +DEFLABEL(RESOLVE_INDIRECT) + mov 1(%rbx), %eax +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_INDIRECT) +DEFPARAM(RESOLVE_INDIRECT, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_POSTINC: Resolve an address register postincrement reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * size: Size in bytes of the reference + */ +DEFLABEL(RESOLVE_POSTINC) + lea 1(%rbx), %rcx +8: mov (%rcx), %eax + add $1, (%rcx) +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_POSTINC) +DEFPARAM(RESOLVE_POSTINC, reg4, 8b, -1) +DEFPARAM(RESOLVE_POSTINC, size, 9b, -1) + +/* For byte-sized (A7)+, make sure A7 stays even */ +DEFLABEL(RESOLVE_POSTINC_A7_B) + mov A7, %ecx + lea 1(%ecx), %eax + add $2, A7 + mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_POSTINC_A7_B) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_PREDEC: Resolve an address register predecrement reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * size: Size in bytes of the reference + */ +DEFLABEL(RESOLVE_PREDEC) + lea 1(%rbx), %rcx +8: sub $1, (%rcx) +9: mov (%rcx), %eax + mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_PREDEC) +DEFPARAM(RESOLVE_PREDEC, reg4, 8b, -1) +DEFPARAM(RESOLVE_PREDEC, size, 9b, -1) + +/* For byte-sized -(A7), make sure A7 stays even */ +DEFLABEL(RESOLVE_PREDEC_A7_B) + mov A7, %ecx + lea -1(%ecx), %eax + sub $2, A7 + mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_PREDEC_A7_B) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_DISP: Resolve an address register indirect with displacement + * reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * disp: Displacement + */ +DEFLABEL(RESOLVE_DISP) + mov 1(%rbx), %eax +8: add $0x12345678, %eax +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_DISP) +DEFPARAM(RESOLVE_DISP, reg4, 8b, -1) +DEFPARAM(RESOLVE_DISP, disp, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_INDEX_[WL]: Resolve an address register indirect with index + * reference. + * + * [Parameters] + * reg4: (8+n)*4 for register An + * ireg4: Index register number * 4 + * disp: Displacement + */ +DEFLABEL(RESOLVE_INDEX_W) + mov 1(%rbx), %eax +7: mov 1(%rbx), %ecx +8: movsx %cx, %ecx + lea 1(%eax, %ecx), %eax +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_INDEX_W) +DEFPARAM(RESOLVE_INDEX_W, reg4, 7b, -1) +DEFPARAM(RESOLVE_INDEX_W, ireg4, 8b, -1) +DEFPARAM(RESOLVE_INDEX_W, disp, 9b, -1) + +DEFLABEL(RESOLVE_INDEX_L) + mov 1(%rbx), %eax +7: mov 1(%rbx), %ecx +8: lea 1(%eax, %ecx), %eax +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_INDEX_L) +DEFPARAM(RESOLVE_INDEX_L, reg4, 7b, -1) +DEFPARAM(RESOLVE_INDEX_L, ireg4, 8b, -1) +DEFPARAM(RESOLVE_INDEX_L, disp, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_ABSOLUTE: Resolve an absolute short, absolute long, or + * PC-relative reference. + * + * [Parameters] + * addr: Absolute address + */ +DEFLABEL(RESOLVE_ABSOLUTE) + mov $0x12345678, %eax +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_ABSOLUTE) +DEFPARAM(RESOLVE_ABSOLUTE, addr, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * RESOLVE_ABS_INDEX_[WL]: Resolve a PC-relative with index reference. + * + * [Parameters] + * addr: Absolute address + * ireg4: Index register number * 4 + */ +DEFLABEL(RESOLVE_ABS_INDEX_W) + mov $0x12345678, %eax +8: movswl 1(%rbx), %ecx +9: add %ecx, %eax + mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_ABS_INDEX_W) +DEFPARAM(RESOLVE_ABS_INDEX_W, addr, 8b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_W, ireg4, 9b, -1) + +DEFLABEL(RESOLVE_ABS_INDEX_L) + mov $0x12345678, %eax +8: add 1(%rbx), %ecx +9: mov %eax, Q68State_ea_addr(%rbx) +DEFSIZE(RESOLVE_ABS_INDEX_L) +DEFPARAM(RESOLVE_ABS_INDEX_L, addr, 8b, -4) +DEFPARAM(RESOLVE_ABS_INDEX_L, ireg4, 9b, -1) + +/*************************************************************************/ +/*************************** Operand retrieval ***************************/ +/*************************************************************************/ + +/** + * GET_OP1_REGISTER: Get the current value of the given register as + * operand 1. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(GET_OP1_REGISTER) + mov 1(%rbx), %edi +9: +DEFSIZE(GET_OP1_REGISTER) +DEFPARAM(GET_OP1_REGISTER, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_EA_[BWL]: Get the value pointed to by the previously resolved + * effective address as operand 1. + */ +DEFLABEL(GET_OP1_EA_B) + mov Q68State_ea_addr(%rbx), %eax + READ8 %rax + movzx %al, %edi +DEFSIZE(GET_OP1_EA_B) + +DEFLABEL(GET_OP1_EA_W) + mov Q68State_ea_addr(%rbx), %eax + READ16 %rax + movzx %ax, %edi +DEFSIZE(GET_OP1_EA_W) + +DEFLABEL(GET_OP1_EA_L) + mov Q68State_ea_addr(%rbx), %eax + READ32 %rax + mov %eax, %edi +DEFSIZE(GET_OP1_EA_L) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_IMMEDIATE: Get an immediate value as operand 1. + * + * [Parameters] + * value: Immediate value + */ +DEFLABEL(GET_OP1_IMMEDIATE) + mov $0x12345678, %edi +9: +DEFSIZE(GET_OP1_IMMEDIATE) +DEFPARAM(GET_OP1_IMMEDIATE, value, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_CCR: Get the current value of CCR as operand 1. + */ +DEFLABEL(GET_OP1_CCR) + movzbl SR, %edi +DEFSIZE(GET_OP1_CCR) + +/*-----------------------------------------------------------------------*/ + +/** + * GET_OP1_SR: Get the current value of SR as operand 1. + */ +DEFLABEL(GET_OP1_SR) + mov SR, %edi +DEFSIZE(GET_OP1_SR) + +/*************************************************************************/ + +/** + * GET_OP2_*: Get the same things as above as operand 2. + */ +DEFLABEL(GET_OP2_REGISTER) + mov 1(%rbx), %edx +9: +DEFSIZE(GET_OP2_REGISTER) +DEFPARAM(GET_OP2_REGISTER, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_EA_B) + mov Q68State_ea_addr(%rbx), %eax + READ8 %rax + movzx %al, %edx +DEFSIZE(GET_OP2_EA_B) + +DEFLABEL(GET_OP2_EA_W) + mov Q68State_ea_addr(%rbx), %eax + READ16 %rax + movzx %ax, %edx +DEFSIZE(GET_OP2_EA_W) + +DEFLABEL(GET_OP2_EA_L) + mov Q68State_ea_addr(%rbx), %eax + READ32 %rax + mov %eax, %edx +DEFSIZE(GET_OP2_EA_L) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_IMMEDIATE) + mov $0x12345678, %edx +9: +DEFSIZE(GET_OP2_IMMEDIATE) +DEFPARAM(GET_OP2_IMMEDIATE, value, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_CCR) + movzbl SR, %edx +DEFSIZE(GET_OP2_CCR) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(GET_OP2_SR) + mov SR, %edx +DEFSIZE(GET_OP2_SR) + +/*************************************************************************/ +/**************************** Result storing *****************************/ +/*************************************************************************/ + +/** + * SET_REGISTER_[BWL]: Set the value of the given register to the result + * value. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(SET_REGISTER_B) + mov %al, 1(%rbx) +9: +DEFSIZE(SET_REGISTER_B) +DEFPARAM(SET_REGISTER_B, reg4, 9b, -1) + +DEFLABEL(SET_REGISTER_W) + mov %ax, 1(%rbx) +9: +DEFSIZE(SET_REGISTER_W) +DEFPARAM(SET_REGISTER_W, reg4, 9b, -1) + +DEFLABEL(SET_REGISTER_L) + mov %eax, 1(%rbx) +9: +DEFSIZE(SET_REGISTER_L) +DEFPARAM(SET_REGISTER_L, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_AREG_W: Set the value of the given address register to the + * sign-extended result value. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(SET_AREG_W) + cwde + mov %eax, 1(%rbx) +9: +DEFSIZE(SET_AREG_W) +DEFPARAM(SET_AREG_W, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_EA_[BWL]: Set the value pointed to by the previously resolved + * effective address to the result value. + */ +DEFLABEL(SET_EA_B) + mov Q68State_ea_addr(%rbx), %ecx + WRITE8 %rcx, %rax +DEFSIZE(SET_EA_B) + +DEFLABEL(SET_EA_W) + mov Q68State_ea_addr(%rbx), %ecx + WRITE16 %rcx, %rax +DEFSIZE(SET_EA_W) + +DEFLABEL(SET_EA_L) + mov Q68State_ea_addr(%rbx), %ecx + WRITE32 %rcx, %rax +DEFSIZE(SET_EA_L) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_CCR: Set the condition codes from the result value. + */ +DEFLABEL(SET_CCR) + mov %al, SR // Update low byte only +DEFSIZE(SET_CCR) + +/*-----------------------------------------------------------------------*/ + +/** + * SET_SR: Set the status register from the result value. + */ +DEFLABEL(SET_SR) + UPDATE_SR +DEFSIZE(SET_SR) + +/*************************************************************************/ +/*************************** Stack operations ****************************/ +/*************************************************************************/ + +/** + * PUSH_L: Push the 32-bit value of operand 1 onto the stack. + */ +DEFLABEL(PUSH_L) + PUSH32 %rdi +DEFSIZE(PUSH_L) + +/*-----------------------------------------------------------------------*/ + +/** + * POP_L: Pop a 32-bit value off the stack into the result register. + */ +DEFLABEL(POP_L) + POP32 +DEFSIZE(POP_L) + +/*************************************************************************/ +/************************ Condition code setting *************************/ +/*************************************************************************/ + +/** + * SETCC_ADD_[BWL]: Set the condition codes for the result of an ADD + * instruction stored in the result register. + */ +DEFLABEL(SETCC_ADD_B) + SETCC_XNZVC +DEFSIZE(SETCC_ADD_B) + +DEFLABEL(SETCC_ADD_W) + SETCC_XNZVC +DEFSIZE(SETCC_ADD_W) + +DEFLABEL(SETCC_ADD_L) + SETCC_XNZVC +DEFSIZE(SETCC_ADD_L) + +/*************************************************************************/ + +/** + * SETCC_ADDX_[BWL]: Set the condition codes for the result of an ADDX + * instruction stored in the result register. + */ +DEFLABEL(SETCC_ADDX_B) + SETCC_XNVC_Z +DEFSIZE(SETCC_ADDX_B) + +DEFLABEL(SETCC_ADDX_W) + SETCC_XNVC_Z +DEFSIZE(SETCC_ADDX_W) + +DEFLABEL(SETCC_ADDX_L) + SETCC_XNVC_Z +DEFSIZE(SETCC_ADDX_L) + +/*************************************************************************/ + +/** + * SETCC_SUB_[BWL]: Set the condition codes for the result of a SUB + * instruction stored in the result register. + */ +DEFLABEL(SETCC_SUB_B) + SETCC_XNZVC +DEFSIZE(SETCC_SUB_B) + +DEFLABEL(SETCC_SUB_W) + SETCC_XNZVC +DEFSIZE(SETCC_SUB_W) + +DEFLABEL(SETCC_SUB_L) + SETCC_XNZVC +DEFSIZE(SETCC_SUB_L) + +/*************************************************************************/ + +/** + * SETCC_SUBX_[BWL]: Set the condition codes for the result of a SUBX + * instruction stored in the result register. + */ +DEFLABEL(SETCC_SUBX_B) + SETCC_XNVC_Z +DEFSIZE(SETCC_SUBX_B) + +DEFLABEL(SETCC_SUBX_W) + SETCC_XNVC_Z +DEFSIZE(SETCC_SUBX_W) + +DEFLABEL(SETCC_SUBX_L) + SETCC_XNVC_Z +DEFSIZE(SETCC_SUBX_L) + +/*************************************************************************/ + +/** + * SETCC_CMP_[BWL]: Set the condition codes for the result of a CMP + * instruction stored in the result register. The X flag is unmodified. + */ +DEFLABEL(SETCC_CMP_B) + SETCC_NZVC +DEFSIZE(SETCC_CMP_B) + +DEFLABEL(SETCC_CMP_W) + SETCC_NZVC +DEFSIZE(SETCC_CMP_W) + +DEFLABEL(SETCC_CMP_L) + SETCC_NZVC +DEFSIZE(SETCC_CMP_L) + +/*************************************************************************/ + +/** + * SETCC_LOGIC_[BWL]: Set the condition codes for the result of a logical + * instruction (MOVE, AND, OR, EOR) stored in the result register. The X + * flag is unmodified. + */ +DEFLABEL(SETCC_LOGIC_B) + SETCC_NZ00 +DEFSIZE(SETCC_LOGIC_B) + +DEFLABEL(SETCC_LOGIC_W) + SETCC_NZ00 +DEFSIZE(SETCC_LOGIC_W) + +DEFLABEL(SETCC_LOGIC_L) + SETCC_NZ00 +DEFSIZE(SETCC_LOGIC_L) + +/*************************************************************************/ +/*************************** Condition testing ***************************/ +/*************************************************************************/ + +/** + * TEST_*: Check whether a condition is true (based on the current + * condition codes) and set %al based on the result (nonzero = true). + */ + +DEFLABEL(TEST_T) + mov $1, %al +DEFSIZE(TEST_T) + +DEFLABEL(TEST_F) + mov $0, %al +DEFSIZE(TEST_F) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_HI) + mov SR, %al + mov %al, %cl + shr $SR_Z_SHIFT, %cl + or %cl, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_HI) + +DEFLABEL(TEST_LS) + mov SR, %al + mov %al, %cl + shr $SR_Z_SHIFT, %cl + or %cl, %al + and $1, %al +DEFSIZE(TEST_LS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_CC) + mov SR, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_CC) + +DEFLABEL(TEST_CS) + mov SR, %al + and $1, %al +DEFSIZE(TEST_CS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_NE) + mov SR, %al + shr $SR_Z_SHIFT, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_NE) + +DEFLABEL(TEST_EQ) + mov SR, %al + shr $SR_Z_SHIFT, %al + and $1, %al +DEFSIZE(TEST_EQ) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_VC) + mov SR, %al + shr $SR_V_SHIFT, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_VC) + +DEFLABEL(TEST_VS) + mov SR, %al + shr $SR_V_SHIFT, %al + and $1, %al +DEFSIZE(TEST_VS) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_PL) + mov SR, %al + shr $SR_N_SHIFT, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_PL) + +DEFLABEL(TEST_MI) + mov SR, %al + shr $SR_N_SHIFT, %al + and $1, %al +DEFSIZE(TEST_MI) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_GE) + mov SR, %al + mov %al, %cl + shr $SR_N_SHIFT, %al + shr $SR_V_SHIFT, %cl + xor %cl, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_GE) + +DEFLABEL(TEST_LT) + mov SR, %al + mov %al, %cl + shr $SR_N_SHIFT, %al + shr $SR_V_SHIFT, %cl + xor %cl, %al + and $1, %al +DEFSIZE(TEST_LT) + +/*-----------------------------------------------------------------------*/ + +DEFLABEL(TEST_GT) + mov SR, %al + push %rax + mov %al, %cl + shr $SR_N_SHIFT, %al + shr $SR_V_SHIFT, %cl + xor %al, %cl + pop %rax + shr $SR_Z_SHIFT, %al + or %cl, %al + and $1, %al + xor $1, %al +DEFSIZE(TEST_GT) + +DEFLABEL(TEST_LE) + mov SR, %al + push %rax + mov %al, %cl + shr $SR_N_SHIFT, %al + shr $SR_V_SHIFT, %cl + xor %al, %cl + pop %rax + shr $SR_Z_SHIFT, %al + or %cl, %al + and $1, %al +DEFSIZE(TEST_LE) + +/*************************************************************************/ +/**************************** ALU operations *****************************/ +/*************************************************************************/ + +/** + * MOVE_[BWL]: Evaluate op1, setting the result value for the MOVE + * instruction. + */ +DEFLABEL(MOVE_B) + mov %edi, %eax + test %al, %al +DEFSIZE(MOVE_B) + +DEFLABEL(MOVE_W) + mov %edi, %eax + test %ax, %ax +DEFSIZE(MOVE_W) + +DEFLABEL(MOVE_L) + mov %edi, %eax + test %eax, %eax +DEFSIZE(MOVE_L) + +/*************************************************************************/ + +/** + * ADD_[BWL]: Evaluate op2 + op1. + */ +DEFLABEL(ADD_B) + mov %edi, %eax + add %dl, %al +DEFSIZE(ADD_B) + +DEFLABEL(ADD_W) + mov %edi, %eax + add %dx, %ax +DEFSIZE(ADD_W) + +DEFLABEL(ADD_L) + mov %edi, %eax + add %edx, %eax +DEFSIZE(ADD_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ADDA_W: Sign-extend op1 to 32 bits, then evaluate op2 + op1. + */ +DEFLABEL(ADDA_W) + movsx %di, %edi + mov %edi, %eax + add %edx, %eax +DEFSIZE(ADDA_W) + +/*-----------------------------------------------------------------------*/ + +/** + * ADDX_[BWL]: Evaluate op2 + op1 + X. + */ +DEFLABEL(ADDX_B) + LDC_FROM_X %al + mov %edi, %eax + adc %dl, %al +DEFSIZE(ADDX_B) + +DEFLABEL(ADDX_W) + LDC_FROM_X %al + mov %edi, %eax + adc %dx, %ax +DEFSIZE(ADDX_W) + +DEFLABEL(ADDX_L) + LDC_FROM_X %al + mov %edi, %eax + adc %edx, %eax +DEFSIZE(ADDX_L) + +/*************************************************************************/ + +/** + * SUB_[BWL]: Evaluate op2 - op1. + */ +DEFLABEL(SUB_B) + mov %edi, %eax + xchg %edx, %eax + sub %dl, %al +DEFSIZE(SUB_B) + +DEFLABEL(SUB_W) + mov %edi, %eax + xchg %edx, %eax + sub %dx, %ax +DEFSIZE(SUB_W) + +DEFLABEL(SUB_L) + mov %edi, %eax + xchg %edx, %eax + sub %edx, %eax +DEFSIZE(SUB_L) + +/*-----------------------------------------------------------------------*/ + +/** + * SUBA_W: Sign-extend op1 to 32 bits, then evaluate op2 - op1. + */ +DEFLABEL(SUBA_W) + movsx %di, %edi + mov %edi, %eax + xchg %edx, %eax + sub %edx, %eax +DEFSIZE(SUBA_W) + +/*-----------------------------------------------------------------------*/ + +/** + * SUBX_[BWL]: Evaluate op2 - op1 - X. + */ +DEFLABEL(SUBX_B) + LDC_FROM_X %al + mov %edi, %eax + xchg %edx, %eax + sbb %dl, %al +DEFSIZE(SUBX_B) + +DEFLABEL(SUBX_W) + LDC_FROM_X %al + mov %edi, %eax + xchg %edx, %eax + sbb %dx, %ax +DEFSIZE(SUBX_W) + +DEFLABEL(SUBX_L) + LDC_FROM_X %al + mov %edi, %eax + xchg %edx, %eax + sbb %edx, %eax +DEFSIZE(SUBX_L) + +/*************************************************************************/ + +/** + * MUL[SU]_W: Evaluate op2 * op1 in signed or unsigned context. + */ +DEFLABEL(MULS_W) + movsx %di, %eax + movsx %dx, %edx + imul %edx + test %eax, %eax +DEFSIZE(MULS_W) + +DEFLABEL(MULU_W) + movzx %di, %eax + movzx %dx, %edx + mul %edx + test %eax, %eax +DEFSIZE(MULU_W) + +/*************************************************************************/ + +/** + * DIV[SU]_W: Evaluate op2 / op1 in signed or unsigned context, setting + * the condition codes appropriately. The quotient is stored in the low + * 16 bits, the remainder in the high 16 bits of the result value. On + * overflow, op2 is copied to the result. + */ +DEFLABEL(DIVS_W) + andb $~SR_C, SR + test %di, %di + jnz 0f + movl $EX_DIVIDE_BY_ZERO, Q68State_exception(%rbx) + TERMINATE +0: push %rdx // Save op2 (register value) so we can restore it as the + // result on overflow + mov %edx, %eax + cdq + movsx %di, %edi + idiv %edi + lea 0x8000(%eax), %ecx + test $0xFFFF0000, %ecx + jz 1f + pop %rax + orb $SR_V, SR + jmp 2f +1: pop %rcx + shl $16, %edx + or %edx, %eax + test %ax, %ax + SETCC_NZ + andb $~SR_V, SR +2: +DEFSIZE(DIVS_W) + +DEFLABEL(DIVU_W) + andb $~SR_C, SR + test %di, %di + jnz 0f + movl $EX_DIVIDE_BY_ZERO, Q68State_exception(%rbx) + TERMINATE +0: push %rdx // Save op2 (register value) so we can restore it as the + // result on overflow + mov %edx, %eax + xor %edx, %edx + movsx %di, %edi + div %edi + test $0xFFFF0000, %eax + jz 1f + pop %rax + orb $SR_V, SR + jmp 2f +1: pop %rcx + shl $16, %edx + or %edx, %eax + test %ax, %ax + SETCC_NZ + andb $~SR_V, SR +2: +DEFSIZE(DIVU_W) + +/*************************************************************************/ + +/** + * AND_[BWL]: Evaluate op2 & op1. + */ +DEFLABEL(AND_B) + mov %edi, %eax + and %dl, %al +DEFSIZE(AND_B) + +DEFLABEL(AND_W) + mov %edi, %eax + and %dx, %ax +DEFSIZE(AND_W) + +DEFLABEL(AND_L) + mov %edi, %eax + and %edx, %eax +DEFSIZE(AND_L) + +/*************************************************************************/ + +/** + * OR_[BWL]: Evaluate op2 | op1. + */ +DEFLABEL(OR_B) + mov %edi, %eax + or %dl, %al +DEFSIZE(OR_B) + +DEFLABEL(OR_W) + mov %edi, %eax + or %dx, %ax +DEFSIZE(OR_W) + +DEFLABEL(OR_L) + mov %edi, %eax + or %edx, %eax +DEFSIZE(OR_L) + +/*************************************************************************/ + +/** + * EOR_[BWL]: Evaluate op2 ^ op1. + */ +DEFLABEL(EOR_B) + mov %edi, %eax + xor %dl, %al +DEFSIZE(EOR_B) + +DEFLABEL(EOR_W) + mov %edi, %eax + xor %dx, %ax +DEFSIZE(EOR_W) + +DEFLABEL(EOR_L) + mov %edi, %eax + xor %edx, %eax +DEFSIZE(EOR_L) + +/*************************************************************************/ + +/** + * EXT_[WL]: Sign-extend op1 from 8 to 16 or from 16 to 32 bits. + */ +DEFLABEL(EXT_W) + mov %edi, %eax + movsx %al, %ax + test %ax, %ax +DEFSIZE(EXT_W) + +DEFLABEL(EXT_L) + movsx %di, %eax + test %eax, %eax +DEFSIZE(EXT_L) + +/*************************************************************************/ + +/** + * SWAP: Swap the upper and lower 16-bit halves of op1, placing the result + * in the result register. + */ +DEFLABEL(SWAP) + mov %edi, %eax + rol $16, %eax + test %eax, %eax +DEFSIZE(SWAP) + +/*************************************************************************/ +/**************************** BCD operations *****************************/ +/*************************************************************************/ + +/** + * ABCD: Evaluate op2 + op1 + X, treating the operands as binary-coded + * decimal values. + */ +DEFLABEL(ABCD) + mov %edi, %ecx + mov %edx, %eax + and $0x0F, %ecx + and $0x0F, %eax + add %ecx, %eax + mov SR, %ecx + shr $SR_X_SHIFT, %ecx + and $1, %ecx + add %ecx, %eax + cmp $10, %eax + jb 0f + add $6, %eax +0: and $0xF0, %edi + and $0xF0, %edx + add %edi, %edx + add %edx, %eax + xor %ecx, %ecx + cmp $10<<4, %eax + jb 1f + sub $10<<4, %eax + mov $1, %cl +1: mov %cl, %dl + //shl $SR_C_SHIFT, %cl // Shift count is 0, so omitted + shl $SR_X_SHIFT, %dl + or %cl, %dl + andb $~(SR_X|SR_C), SR + or %dl, SR + test %al, %al + setnz %cl + shl $SR_Z_SHIFT, %cl + not %cl + and %cl, SR +DEFSIZE(ABCD) + +/*************************************************************************/ + +/** + * SBCD: Evaluate op2 - op1 - X, treating the operands as binary-coded + * decimal values. + */ +DEFLABEL(SBCD) + mov %edi, %ecx + mov %edx, %eax + and $0x0F, %ecx + and $0x0F, %eax + sub %ecx, %eax + mov SR, %ecx + shr $SR_X_SHIFT, %ecx + and $1, %ecx + sub %ecx, %eax + xor %ecx, %ecx + test %eax, %eax + jns 0f + add $10, %eax + add $16, %ecx +0: and $0xF0, %edi + and $0xF0, %edx + sub %ecx, %edx + xor %ecx, %ecx + sub %edi, %edx + jns 1f + add $10<<4, %eax + mov $1, %cl +1: add %edx, %eax + jns 2f + mov $1, %cl +2: mov %cl, %dl + //shl $SR_C_SHIFT, %cl // Shift count is 0, so omitted + shl $SR_X_SHIFT, %dl + or %cl, %dl + andb $~(SR_X|SR_C), SR + or %dl, SR + test %al, %al + setnz %cl + shl $SR_Z_SHIFT, %cl + not %cl + and %cl, SR +DEFSIZE(SBCD) + +/*************************************************************************/ +/*********************** Bit-twiddling operations ************************/ +/*************************************************************************/ + +/** + * BTST_[BL]: Evaluate op2 & (1 << op1). The value (1 << op1), where the + * high bits of op1 have been masked to zero, is left in %edi for use by a + * subsequent BCHG/BCLR/BSET operation. + */ +DEFLABEL(BTST_B) + mov %edi, %ecx + and $7, %ecx + mov $1, %edi + shl %cl, %edi + test %edi, %edx + setz %cl + shl $SR_Z_SHIFT, %cl + and $~SR_Z, SR + or %cl, SR +DEFSIZE(BTST_B) + +DEFLABEL(BTST_L) + mov %edi, %ecx + and $31, %ecx + mov $1, %edi + shl %cl, %edi + test %edi, %edx + setz %cl + shl $SR_Z_SHIFT, %cl + and $~SR_Z, SR + or %cl, SR +DEFSIZE(BTST_L) + +/*************************************************************************/ + +/** + * BCHG: Evaluate op2 ^ (1 << op1), where (1 << op1) has already been + * stored in %edi. + */ +DEFLABEL(BCHG) + mov %edx, %eax + xor %edi, %eax +DEFSIZE(BCHG) + +/*-----------------------------------------------------------------------*/ + +/** + * BCLR: Evaluate op2 & ~(1 << op1), where (1 << op1) has already been + * stored in %edi. + */ +DEFLABEL(BCLR) + mov %edx, %eax + not %edi + and %edi, %eax +DEFSIZE(BCLR) + +/*-----------------------------------------------------------------------*/ + +/** + * BSET: Evaluate op2 | (1 << op1), where (1 << op1) has already been + * stored in %edi. + */ +DEFLABEL(BSET) + mov %edx, %eax + or %edi, %eax +DEFSIZE(BSET) + +/*************************************************************************/ +/************************ Shift/rotate operations ************************/ +/*************************************************************************/ + +/** + * SETCC_XC_SHIFT: Set the X and C flags for a shift or rotate instruction. + * The value to set (0 or 1) is passed in %dl; %dh is destroyed. Assumes + * that the X and C flags have already been cleared. + */ +.macro SETCC_XC_SHIFT + mov %dl, %dh + shl $SR_X_SHIFT, %dh + //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted + or %dh, %dl + or %dl, SR +.endm + +/*************************************************************************/ + +/** + * ASL_[BWL]: Evaluate (signed) op2 << op1. + */ +.macro DEF_ASL nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 1f + andb $~SR_X, SR + // Have to shift bit by bit to detect overflow +0: sal $1, \reg + setc %dl + seto %dh + shl $SR_V_SHIFT, %dh + or %dh, SR + loop 0b + SETCC_XC_SHIFT +1: test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ASL_B) + DEF_ASL 8, %al +DEFSIZE(ASL_B) + +DEFLABEL(ASL_W) + DEF_ASL 16, %ax +DEFSIZE(ASL_W) + +DEFLABEL(ASL_L) + DEF_ASL 32, %eax +DEFSIZE(ASL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ASR_[BWL]: Evaluate (signed) op2 >> op1. + */ +.macro DEF_ASR nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 4f + andb $~SR_X, SR + cmp $\nbits, %ecx + jb 2f +1: // count >= nbits + sar $\nbits-1, \reg + mov %al, %dl + and $1, %dl + jmp 3f +2: // 0 < count < nbits + sar %cl, \reg + setc %dl +3: // count != 0 + SETCC_XC_SHIFT +4: // All cases + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ASR_B) + DEF_ASR 8, %al +DEFSIZE(ASR_B) + +DEFLABEL(ASR_W) + DEF_ASR 16, %ax +DEFSIZE(ASR_W) + +DEFLABEL(ASR_L) + DEF_ASR 32, %eax +DEFSIZE(ASR_L) + +/*************************************************************************/ + +/** + * LSL_[BWL]: Evaluate (unsigned) op2 << op1. + */ +.macro DEF_LSL nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 4f + andb $~SR_X, SR + cmp $\nbits, %ecx + jb 2f + ja 1f +0: // count == nbits + and $1, %al + mov %al, %dl + xor \reg, \reg + jmp 3f +1: // count > nbits + xor \reg, \reg + jmp 4f +2: // 0 < count < nbits + sal %cl, \reg + setc %dl +3: // 0 < count <= nbits + SETCC_XC_SHIFT +4: // All cases + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(LSL_B) + DEF_LSL 8, %al +DEFSIZE(LSL_B) + +DEFLABEL(LSL_W) + DEF_LSL 16, %ax +DEFSIZE(LSL_W) + +DEFLABEL(LSL_L) + DEF_LSL 32, %eax +DEFSIZE(LSL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * LSR_[BWL]: Evaluate (unsigned) op2 >> op1. + */ +.macro DEF_LSR nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 4f + andb $~SR_X, SR + cmp $\nbits, %ecx + jb 2f + ja 1f +0: // count == nbits + shl $1, \reg + setc %dl + xor \reg, \reg + jmp 3f +1: // count > nbits + xor \reg, \reg + jmp 4f +2: // 0 < count < nbits + shr %cl, \reg + setc %dl +3: // 0 < count <= nbits + SETCC_XC_SHIFT +4: // All cases + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(LSR_B) + DEF_LSR 8, %al +DEFSIZE(LSR_B) + +DEFLABEL(LSR_W) + DEF_LSR 16, %ax +DEFSIZE(LSR_W) + +DEFLABEL(LSR_L) + DEF_LSR 32, %eax +DEFSIZE(LSR_L) + +/*************************************************************************/ + +/** + * ROXL_[BWL]: Evaluate op2 ROXL op1. + */ +.macro DEF_ROXL nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jnz 0f + mov SR, %dl + shr $SR_X_SHIFT, %dl + and $1, %dl + jmp 2f +0: LDC_FROM_X %dl +1: rcl \reg + loop 1b + setc %dl + andb $~SR_X, SR +2: SETCC_XC_SHIFT + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ROXL_B) + DEF_ROXL 8, %al +DEFSIZE(ROXL_B) + +DEFLABEL(ROXL_W) + DEF_ROXL 16, %ax +DEFSIZE(ROXL_W) + +DEFLABEL(ROXL_L) + DEF_ROXL 32, %eax +DEFSIZE(ROXL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ROXR_[BWL]: Evaluate op2 ROXR op1. + */ +.macro DEF_ROXR nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jnz 0f + mov SR, %dl + shr $SR_X_SHIFT, %dl + and $1, %dl + jmp 2f +0: LDC_FROM_X %dl +1: rcr \reg + loop 1b + setc %dl + andb $~SR_X, SR +2: SETCC_XC_SHIFT + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ROXR_B) + DEF_ROXR 8, %al +DEFSIZE(ROXR_B) + +DEFLABEL(ROXR_W) + DEF_ROXR 16, %ax +DEFSIZE(ROXR_W) + +DEFLABEL(ROXR_L) + DEF_ROXR 32, %eax +DEFSIZE(ROXR_L) + +/*************************************************************************/ + +/** + * ROL_[BWL]: Evaluate op2 ROL op1. + */ +.macro DEF_ROL nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 3f + and $\nbits-1, %ecx + jnz 2f +1: // count != 0 && count % nbits == 0 + mov %al, %dl + and $1, %dl + //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted + or %dl, SR + jmp 3f +2: // count % nbits != 0 + rol %cl, \reg + setc %dl + //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted + or %dl, SR +3: // All cases + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ROL_B) + DEF_ROL 8, %al +DEFSIZE(ROL_B) + +DEFLABEL(ROL_W) + DEF_ROL 16, %ax +DEFSIZE(ROL_W) + +DEFLABEL(ROL_L) + DEF_ROL 32, %eax +DEFSIZE(ROL_L) + +/*-----------------------------------------------------------------------*/ + +/** + * ROR_[BWL]: Evaluate op2 ROR op1. + */ +.macro DEF_ROR nbits, reg + mov %edi, %ecx + mov %edx, %eax + and $0x3F, %ecx + add %ecx, %esi + add %ecx, %esi + andb $~(SR_V|SR_C), SR + test %ecx, %ecx + jz 3f + and $\nbits-1, %ecx + jnz 2f +1: // count != 0 && count % nbits == 0 + mov %eax, %edx + shr $\nbits-1, %edx + and $1, %dl + //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted + or %dl, SR + jmp 3f +2: // count % nbits != 0 + ror %cl, \reg + setc %dl + //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted + or %dl, SR +3: // All cases + test \reg, \reg + SETCC_NZ +.endm + +DEFLABEL(ROR_B) + DEF_ROR 8, %al +DEFSIZE(ROR_B) + +DEFLABEL(ROR_W) + DEF_ROR 16, %ax +DEFSIZE(ROR_W) + +DEFLABEL(ROR_L) + DEF_ROR 32, %eax +DEFSIZE(ROR_L) + +/*************************************************************************/ +/******************* Conditional and branch operations *******************/ +/*************************************************************************/ + +/** + * Scc: Set the lower 8 bits of the result value to 0xFF if the condition + * is true, 0x00 if false. + */ +DEFLABEL(Scc) + neg %al +DEFSIZE(Scc) + +/*-----------------------------------------------------------------------*/ + +/** + * ADD_CYCLES_Scc_Dn: Add the appropriate number of clock cycles for an + * Scc Dn instruction to the cycle count. + */ +DEFLABEL(ADD_CYCLES_Scc_Dn) + movzx %al, %ecx + and $2, %ecx + add $4, %ecx + add %ecx, %esi +DEFSIZE(ADD_CYCLES_Scc_Dn) + +/*************************************************************************/ + +/** + * DBcc: Jump to the specified target address unless the condition is true + * or the lower 16 bits of the given data register, after being decremented, + * are equal to -1. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7) + * target: Target address + */ +DEFLABEL(DBcc) + test %al, %al + jz 0f + add $12, %esi + jmp 2f +0: lea 1(%rbx), %rcx +8: subw $1, (%rcx) + mov (%rcx), %ax + cmp $-1, %ax + jne 1f + add $14, %esi + jmp 2f +1: add $10, %esi + mov $0x12345678, %eax +9: mov %eax, PC + TERMINATE +2: +DEFSIZE(DBcc) +DEFPARAM(DBcc, reg4, 8b, -1) +DEFPARAM(DBcc, target, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * DBcc_native: Implement DBcc using a jump within the native code. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7) + * target: Target 68000 address + * native_disp: Target native displacement from end of this fragment + */ +DEFLABEL(DBcc_native) + test %al, %al + jz 0f + add $12, %esi + jmp 2f +0: lea 1(%rbx), %rcx +8: subw $1, (%rcx) + mov (%rcx), %ax + cmp $-1, %ax + jne 1f + add $14, %esi + jmp 2f +1: add $10, %esi + mov $0x12345678, %eax +9: mov %eax, PC + jmp .+0x12345678 +2: +DEFSIZE(DBcc_native) +DEFPARAM(DBcc_native, reg4, 8b, -1) +DEFPARAM(DBcc_native, target, 9b, -4) +DEFPARAM(DBcc_native, native_disp, 2b, -4) + +/*************************************************************************/ + +/** + * Bcc: Jump to the specified target address if the condition is true. + * + * [Parameters] + * target: Target address + */ +DEFLABEL(Bcc) + test %al, %al + jz 0f + mov $0x12345678, %eax +9: mov %eax, PC + add $10, %esi + TERMINATE +0: +DEFSIZE(Bcc) +DEFPARAM(Bcc, target, 9b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * Bcc_native: Implement Bcc using a jump within the native code. + * + * [Parameters] + * target: Target 68000 address + * native_disp: Target native displacement from end of this fragment + */ +DEFLABEL(Bcc_native) + test %al, %al + jz 0f + mov $0x12345678, %eax +9: mov %eax, PC + add $10, %esi + jmp .+0x12345678 +0: +DEFSIZE(Bcc_native) +DEFPARAM(Bcc_native, target, 9b, -4) +DEFPARAM(Bcc_native, native_disp, 0b, -4) + +/*-----------------------------------------------------------------------*/ + +/** + * BSR: Push the address of the next instruction onto the stack, then jump + * to the specified target address. + * + * [Parameters] + * return_addr: Return address to push onto the stack + * target: Target address + */ +DEFLABEL(BSR) + mov $0x12345678, %ecx +8: PUSH32 %rcx + mov $0x12345678, %eax +9: mov %eax, PC + TERMINATE +DEFSIZE(BSR) +DEFPARAM(BSR, return_addr, 8b, -4) +DEFPARAM(BSR, target, 9b, -4) + +/*************************************************************************/ + +/** + * JMP: Jump to the previously resolved effective address. + */ +DEFLABEL(JMP) + mov Q68State_ea_addr(%rbx), %eax + mov %eax, PC + TERMINATE +DEFSIZE(JMP) + +/*-----------------------------------------------------------------------*/ + +/** + * JSR: Push the address of the next instruction onto the stack, then jump + * to the previously resolved effective address. + * + * [Parameters] + * return_addr: Return address to push onto the stack + */ +DEFLABEL(JSR) + mov $0x12345678, %ecx +9: PUSH32 %rcx + mov Q68State_ea_addr(%rbx), %eax + mov %eax, PC + TERMINATE +DEFSIZE(JSR) +DEFPARAM(JSR, return_addr, 9b, -4) + +/*************************************************************************/ +/*********************** MOVEM-related operations ************************/ +/*************************************************************************/ + +/** + * STORE_DEC_[WL]: Decrement state->ea_addr, then store the specified + * register to the resulting location. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(STORE_DEC_W) + mov Q68State_ea_addr(%rbx), %ecx + sub $2, %ecx + mov %ecx, Q68State_ea_addr(%rbx) + mov 1(%rbx), %eax +9: WRITE16 %rcx, %rax +DEFSIZE(STORE_DEC_W) +DEFPARAM(STORE_DEC_W, reg4, 9b, -1) + +DEFLABEL(STORE_DEC_L) + mov Q68State_ea_addr(%rbx), %ecx + sub $4, %ecx + mov %ecx, Q68State_ea_addr(%rbx) + mov 1(%rbx), %eax +9: WRITE32 %rcx, %rax +DEFSIZE(STORE_DEC_L) +DEFPARAM(STORE_DEC_L, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * STORE_INC_[WL]: Store the specified register to the location indicated + * by state->ea_addr, then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(STORE_INC_W) + mov Q68State_ea_addr(%rbx), %ecx + mov 1(%rbx), %eax +9: WRITE16 %rcx, %rax + add $2, Q68State_ea_addr(%rbx) +DEFSIZE(STORE_INC_W) +DEFPARAM(STORE_INC_W, reg4, 9b, -1) + +DEFLABEL(STORE_INC_L) + mov Q68State_ea_addr(%rbx), %ecx + mov 1(%rbx), %eax +9: WRITE32 %rcx, %rax + add $4, Q68State_ea_addr(%rbx) +DEFSIZE(STORE_INC_L) +DEFPARAM(STORE_INC_L, reg4, 9b, -1) + +/*************************************************************************/ + +/** + * LOAD_INC_[WL]: Load the specified register from the location indicated + * by state->ea_addr, then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) + */ +DEFLABEL(LOAD_INC_W) + mov Q68State_ea_addr(%rbx), %ecx + READ16 %rcx + mov %ax, 1(%rbx) +9: add $2, Q68State_ea_addr(%rbx) +DEFSIZE(LOAD_INC_W) +DEFPARAM(LOAD_INC_W, reg4, 9b, -1) + +DEFLABEL(LOAD_INC_L) + mov Q68State_ea_addr(%rbx), %ecx + READ32 %rcx + mov %eax, 1(%rbx) +9: add $4, Q68State_ea_addr(%rbx) +DEFSIZE(LOAD_INC_L) +DEFPARAM(LOAD_INC_L, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * LOADA_INC_W: Load the specified address register from the location + * indicated by state->ea_addr, sign-extending the 16-bit value to 32 bits, + * then increment state->ea_addr. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(LOADA_INC_W) + mov Q68State_ea_addr(%rbx), %ecx + READ16 %rcx + cwde + mov %eax, 1(%rbx) +9: add $2, Q68State_ea_addr(%rbx) +DEFSIZE(LOADA_INC_W) +DEFPARAM(LOADA_INC_W, reg4, 9b, -1) + +/*************************************************************************/ + +/** + * MOVEM_WRITEBACK: Store the address in state->ea_addr to the specified + * address register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVEM_WRITEBACK) + mov Q68State_ea_addr(%rbx), %ecx + mov %ecx, 1(%rbx) +9: +DEFSIZE(MOVEM_WRITEBACK) +DEFPARAM(MOVEM_WRITEBACK, reg4, 9b, -1) + +/*************************************************************************/ +/*********************** Miscellaneous operations ************************/ +/*************************************************************************/ + +/** + * CHK_W: Raise a CHK exception if op1 < 0 or op1 > op2, treating both + * operands as signed 16-bit values. + */ +DEFLABEL(CHK_W) + test %di, %di + jns 0f + orb $SR_N, SR + jmp 1f +0: cmp %dx, %di + jle 2f + andb $~SR_N, SR +1: movl $EX_CHK, Q68State_exception(%rbx) + TERMINATE +2: +DEFSIZE(CHK_W) + +/*************************************************************************/ + +/** + * LEA: Store the previously resolved effective address in the specified + * address register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(LEA) + mov Q68State_ea_addr(%rbx), %eax + mov %eax, 1(%rbx) +9: +DEFSIZE(LEA) +DEFPARAM(LEA, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * PEA: Push the previously resolved effective address onto the stack. + */ +DEFLABEL(PEA) + mov Q68State_ea_addr(%rbx), %ecx + PUSH32 %rcx +DEFSIZE(PEA) + +/*************************************************************************/ + +/** + * TAS: Test the 8-bit value of op1, setting the condition codes + * appropriately, then calculate op1 | 0x80. + */ +DEFLABEL(TAS) + mov %edi, %eax + test %al, %al + SETCC_NZ00 + or $0x80, %al +DEFSIZE(TAS) + +/*************************************************************************/ + +/** + * MOVE_FROM_USP: Copy the user stack pointer to the specified register. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVE_FROM_USP) + mov USP, %eax + mov %eax, 1(%rbx) +9: +DEFSIZE(MOVE_FROM_USP) +DEFPARAM(MOVE_FROM_USP, reg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * MOVE_TO_USP: Copy the specified register to the user stack pointer. + * + * [Parameters] + * reg4: Register number * 4 (32-60: A0-A7) + */ +DEFLABEL(MOVE_TO_USP) + mov 1(%rbx), %eax +9: mov %eax, USP +DEFSIZE(MOVE_TO_USP) +DEFPARAM(MOVE_TO_USP, reg4, 9b, -1) + +/*************************************************************************/ + +/** + * STOP: Halt the processor. + * + * [Parameters] + * newSR: Value to load into SR + */ +DEFLABEL(STOP) + movl $1, Q68State_halted(%rbx) + mov $0x1234, %ax +9: UPDATE_SR +DEFSIZE(STOP) +DEFPARAM(STOP, newSR, 9b, -2) + +/*************************************************************************/ + +/** + * TRAPV: Raise a TRAPV exception if the overflow flag is set. + */ +DEFLABEL(TRAPV) + testb $SR_V, SR + jz 0f + movl $EX_TRAPV, Q68State_exception(%rbx) + TERMINATE +0: +DEFSIZE(TRAPV) + +/*************************************************************************/ + +/** + * RTS: Pop the PC from the stack. + */ +DEFLABEL(RTS) + POP32 + mov %eax, PC + TERMINATE +DEFSIZE(RTS) + +/*-----------------------------------------------------------------------*/ + +/** + * RTR: Pop the condition codes and PC from the stack. + */ +DEFLABEL(RTR) + POP16 + mov %al, SR // Update low byte only + POP32 + mov %eax, PC + TERMINATE +DEFSIZE(RTR) + +/*-----------------------------------------------------------------------*/ + +/** + * RTE: Pop the status register and PC from the stack. + */ +DEFLABEL(RTE) + POP16 + push %rax + POP32 + mov %eax, PC + pop %rax + UPDATE_SR + TERMINATE +DEFSIZE(RTE) + +/*************************************************************************/ + +/** + * MOVEP_READ_[WL]: Read a value from memory, skipping every other byte. + * + * [Parameters] + * areg4: Register number * 4 of base address register (32-60 = A0-A7) + * disp: Displacement from base address register + * dreg4: Register number * 4 of data reg. to receive data (0-28 = D0-D7) + */ +DEFLABEL(MOVEP_READ_W) + mov 1(%rbx), %ecx +7: add $0x12345678, %ecx +8: push %rcx + READ8 %rcx // Byte 1 + movzx %al, %edi + shl $8, %edi + pop %rcx + add $2, %ecx + READ8 %rcx // Byte 0 + movzx %al, %eax + or %eax, %edi + mov %di, 1(%rbx) +9: +DEFSIZE(MOVEP_READ_W) +DEFPARAM(MOVEP_READ_W, areg4, 7b, -1) +DEFPARAM(MOVEP_READ_W, disp, 8b, -4) +DEFPARAM(MOVEP_READ_W, dreg4, 9b, -1) + +DEFLABEL(MOVEP_READ_L) + mov 1(%rbx), %ecx +7: add $0x12345678, %ecx +8: push %rcx + READ8 %rcx // Byte 3 + movzx %al, %edi + shl $24, %edi + mov (%rsp), %ecx + add $2, %ecx + READ8 %rcx // Byte 2 + movzx %al, %eax + shl $16, %eax + or %eax, %edi + mov (%rsp), %ecx + add $4, %ecx + READ8 %rcx // Byte 1 + movzx %al, %eax + shl $8, %eax + or %eax, %edi + pop %rcx + add $6, %ecx + READ8 %rcx // Byte 0 + movzx %al, %eax + or %eax, %edi + mov %di, 1(%rbx) +9: +DEFSIZE(MOVEP_READ_L) +DEFPARAM(MOVEP_READ_L, areg4, 7b, -1) +DEFPARAM(MOVEP_READ_L, disp, 8b, -4) +DEFPARAM(MOVEP_READ_L, dreg4, 9b, -1) + +/*-----------------------------------------------------------------------*/ + +/** + * MOVEP_WRITE_[WL]: Write a value to memory, skipping every other byte. + * + * [Parameters] + * areg4: Register number * 4 of base address register (32-60 = A0-A7) + * disp: Displacement from base address register + * dreg4: Register number * 4 of data reg. containing data (0-28 = D0-D7) + */ +DEFLABEL(MOVEP_WRITE_W) + mov 1(%rbx), %ecx +7: add $0x12345678, %ecx +8: mov 1(%rbx), %eax +9: push %rcx + push %rax + shr $8, %eax + WRITE8 %rcx, %rax // Byte 1 + pop %rax + pop %rcx + add $2, %ecx + WRITE8 %rcx, %rax // Byte 0 +DEFSIZE(MOVEP_WRITE_W) +DEFPARAM(MOVEP_WRITE_W, areg4, 7b, -1) +DEFPARAM(MOVEP_WRITE_W, disp, 8b, -4) +DEFPARAM(MOVEP_WRITE_W, dreg4, 9b, -1) + +DEFLABEL(MOVEP_WRITE_L) + mov 1(%rbx), %ecx +7: add $0x12345678, %ecx +8: mov 1(%rbx), %eax +9: push %rcx + push %rax + shr $24, %eax + WRITE8 %rcx, %rax // Byte 3 + pop %rax + mov (%rsp), %ecx + add $2, %ecx + push %rax + shr $16, %eax + WRITE8 %rcx, %rax // Byte 2 + pop %rax + mov (%rsp), %ecx + add $4, %ecx + push %rax + shr $8, %eax + WRITE8 %rcx, %rax // Byte 1 + pop %rax + pop %rcx + add $6, %ecx + WRITE8 %rcx, %rax // Byte 0 +DEFSIZE(MOVEP_WRITE_L) +DEFPARAM(MOVEP_WRITE_L, areg4, 7b, -1) +DEFPARAM(MOVEP_WRITE_L, disp, 8b, -4) +DEFPARAM(MOVEP_WRITE_L, dreg4, 9b, -1) + +/*************************************************************************/ + +/** + * EXG: Exchange the values of two registers. + * + * [Parameters] + * reg1_4: Register number * 4 of first register (0-60 = D0-A7) + * reg2_4: Register number * 4 of second register (0-60 = D0-A7) + */ +DEFLABEL(EXG) + lea 1(%rbx), %ecx +8: lea 1(%rbx), %edx +9: mov (%rcx), %eax + mov (%rdx), %edi + mov %eax, (%rdx) + mov %edi, (%rcx) +DEFSIZE(EXG) +DEFPARAM(EXG, reg1_4, 8b, -1) +DEFPARAM(EXG, reg2_4, 9b, -1) + +/*************************************************************************/ +/*************************************************************************/ diff --git a/yabause/src/q68/q68-jit-x86.h b/yabause/src/q68/q68-jit-x86.h new file mode 100644 index 0000000000..1fef4589c2 --- /dev/null +++ b/yabause/src/q68/q68-jit-x86.h @@ -0,0 +1,453 @@ +/* src/q68/q68-jit-x86.h: x86 (32/64-bit) dynamic translation header for Q68 + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_JIT_X86_H +#define Q68_JIT_X86_H + +/*************************************************************************/ + +/** + * JIT_CALL: Run translated code from the given (native) address for the + * given number of cycles. + * + * [Parameters] + * state: Processor state block + * cycles: Number of clock cycles to execute + * address_ptr: Pointer to address of native code to execute; must be + * updated on return with the next address to execute + * or NULL if the end of the block was reached + * [Return value] + * Number of clock cycles actually executed + */ +static inline int JIT_CALL(Q68State *state, int cycles, void **address_ptr) +{ + asm( +#ifdef CPU_X64 + /* GCC doesn't know we're actually calling a function here, so make + * sure we don't accidentally overwrite the x64 redzone */ + "sub $128, %%rsp; call *%[address]; add $128, %%rsp" +#else + /* x86 doesn't have a redzone, so we can just do a direct call */ + "call *%[address]" +#endif + : [cycles] "=S" (cycles), [address] "=D" (*address_ptr) + : [state] "b" (state), "0" (cycles), "1" (*address_ptr) +#ifdef CPU_X64 + : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11" +#else + : "eax", "ecx", "edx" +#endif + , "memory" + ); + return cycles; +} + +/*************************************************************************/ + +/** + * JIT_FIXUP_BRANCH: Modify a branch instruction at the given offset to + * jump to the given target. + * + * [Parameters] + * entry: Block being translated + * offset: Offset within entry->native_code of branch instruction + * (as returned in *branch_offset EMIT parameter) + * target: Target offset within entry->native_code + * [Return value] + * None + */ +static inline void JIT_FIXUP_BRANCH(Q68JitEntry *entry, uint32_t offset, + uint32_t target) +{ + /* Not supported on x86/x64 */ +} + +/*************************************************************************/ + +/* + * The remaining macros are all used to insert a specific operation into + * the native code stream. For simplicity, we define the actual code for + * each operation in a separate assembly file, and use memcpy() to copy + * from the assembled code to the output code stream. The GEN_EMIT macro + * below is used to generate each of the JIT_EMIT_* functions; each + * function JIT_EMIT_xxx copies JIT_X86SIZE_xxx bytes from JIT_X86_xxx to + * the code stream, expanding the code buffer if necessary. + */ + +/* Sub-macros (platform-dependent): */ + +#ifdef CPU_X64 + +#define GEN_NAMESIZE(name) \ + extern const uint8_t JIT_X64_##name[]; \ + extern const uint32_t JIT_X64SIZE_##name; +#define GEN_PARAM(name,param) \ + extern const uint32_t JIT_X64PARAM_##name##_##param; +#define GEN_FUNC_TOP(name) \ + if (UNLIKELY(entry->native_size - entry->native_length \ + < JIT_X64SIZE_##name)) { \ + if (!expand_buffer(entry)) { \ + return; \ + } \ + } \ + if (JIT_X64SIZE_##name > 0) { \ + memcpy((uint8_t *)entry->native_code + entry->native_length, \ + JIT_X64_##name, JIT_X64SIZE_##name); \ + } +#define GEN_COPY_PARAM(name,type,param) \ + *(type *)((uint8_t *)entry->native_code + entry->native_length \ + + JIT_X64PARAM_##name##_##param) = param; +#define GEN_FUNC_BOTTOM(name) \ + entry->native_length += JIT_X64SIZE_##name; + +#else // CPU_X86 + +#define GEN_NAMESIZE(name) \ + extern const uint8_t JIT_X86_##name[]; \ + extern const uint32_t JIT_X86SIZE_##name; +#define GEN_PARAM(name,param) \ + extern const uint32_t JIT_X86PARAM_##name##_##param; +#define GEN_FUNC_TOP(name) \ + if (UNLIKELY(entry->native_size - entry->native_length \ + < JIT_X86SIZE_##name)) { \ + if (!expand_buffer(entry)) { \ + return; \ + } \ + } \ + if (JIT_X86SIZE_##name > 0) { \ + memcpy((uint8_t *)entry->native_code + entry->native_length, \ + JIT_X86_##name, JIT_X86SIZE_##name); \ + } +#define GEN_COPY_PARAM(name,type,param) \ + *(type *)((uint8_t *)entry->native_code + entry->native_length \ + + JIT_X86PARAM_##name##_##param) = param; +#define GEN_FUNC_BOTTOM(name) \ + entry->native_length += JIT_X86SIZE_##name; + +#endif // X64/X86 + +#define GEN_EMIT(name) \ + GEN_NAMESIZE(name) \ + static void JIT_EMIT_##name(Q68JitEntry *entry) { \ + GEN_FUNC_TOP(name) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_1(name,type1,param1) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_2(name,type1,param1,type2,param2) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_FUNC_BOTTOM(name) \ + } + +#define GEN_EMIT_3(name,type1,param1,type2,param2,type3,param3) \ + GEN_NAMESIZE(name) \ + GEN_PARAM(name,param1) \ + GEN_PARAM(name,param2) \ + GEN_PARAM(name,param3) \ + static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ + type2 param2, type3 param3) { \ + GEN_FUNC_TOP(name) \ + GEN_COPY_PARAM(name, type1, param1) \ + GEN_COPY_PARAM(name, type2, param2) \ + GEN_COPY_PARAM(name, type3, param3) \ + GEN_FUNC_BOTTOM(name) \ + } + +/*-----------------------------------------------------------------------*/ + +/* Code prologue and epilogue */ +GEN_EMIT(PROLOGUE) +GEN_EMIT(EPILOGUE) + +#ifdef Q68_TRACE +/* Trace the current instruction */ +GEN_EMIT(TRACE) +#endif + +/* Add the specified number of cycles to the cycle counter */ +GEN_EMIT_1(ADD_CYCLES, int32_t, cycles) + +/* Check the cycle limit and interrupt execution if necessary */ +GEN_EMIT(CHECK_CYCLES) + +/* Add the specified amount to the program counter and/or check whether + * to abort */ +GEN_EMIT_1(ADVANCE_PC, int32_t, value) +GEN_EMIT_1(ADVANCE_PC_CHECK_ABORT, int32_t, value) +GEN_EMIT(CHECK_ABORT) + +/* Exception raising */ +GEN_EMIT_1(EXCEPTION, uint32_t, num) +GEN_EMIT_2(CHECK_ALIGNED_EA, uint16_t, opcode, uint16_t, status) +GEN_EMIT_2(CHECK_ALIGNED_SP, uint16_t, opcode, uint16_t, status) +GEN_EMIT(CHECK_SUPER) + +/*-----------------------------------------------------------------------*/ + +/* Resolve an effective address */ +GEN_EMIT_1(RESOLVE_INDIRECT, uint8_t, reg4) +GEN_EMIT_2(RESOLVE_POSTINC, uint8_t, reg4, uint8_t, size) +GEN_EMIT(RESOLVE_POSTINC_A7_B) +GEN_EMIT_2(RESOLVE_PREDEC, uint8_t, reg4, uint8_t, size) +GEN_EMIT(RESOLVE_PREDEC_A7_B) +GEN_EMIT_2(RESOLVE_DISP, uint8_t, reg4, uint32_t, disp) +GEN_EMIT_3(RESOLVE_INDEX_W, uint8_t, reg4, uint8_t, ireg4, uint8_t, disp) +GEN_EMIT_3(RESOLVE_INDEX_L, uint8_t, reg4, uint8_t, ireg4, uint8_t, disp) +GEN_EMIT_1(RESOLVE_ABSOLUTE, uint32_t, addr) +GEN_EMIT_2(RESOLVE_ABS_INDEX_W, uint32_t, addr, uint8_t, ireg4) +GEN_EMIT_2(RESOLVE_ABS_INDEX_L, uint32_t, addr, uint8_t, ireg4) + +/* Retrieve various things as operand 1 */ +GEN_EMIT_1(GET_OP1_REGISTER, uint8_t, reg4) +GEN_EMIT(GET_OP1_EA_B) +GEN_EMIT(GET_OP1_EA_W) +GEN_EMIT(GET_OP1_EA_L) +GEN_EMIT_1(GET_OP1_IMMEDIATE, uint32_t, value) +GEN_EMIT(GET_OP1_CCR) +GEN_EMIT(GET_OP1_SR) + +/* Retrieve various things as operand 2 */ +GEN_EMIT_1(GET_OP2_REGISTER, uint8_t, reg4) +GEN_EMIT(GET_OP2_EA_B) +GEN_EMIT(GET_OP2_EA_W) +GEN_EMIT(GET_OP2_EA_L) +GEN_EMIT_1(GET_OP2_IMMEDIATE, uint32_t, value) +GEN_EMIT(GET_OP2_CCR) +GEN_EMIT(GET_OP2_SR) + +/* Update various things from result */ +GEN_EMIT_1(SET_REGISTER_B, uint8_t, reg4) +GEN_EMIT_1(SET_REGISTER_W, uint8_t, reg4) +GEN_EMIT_1(SET_REGISTER_L, uint8_t, reg4) +GEN_EMIT_1(SET_AREG_W, uint8_t, reg4) +GEN_EMIT(SET_EA_B) +GEN_EMIT(SET_EA_W) +GEN_EMIT(SET_EA_L) +GEN_EMIT(SET_CCR) +GEN_EMIT(SET_SR) + +/* Stack operations */ +GEN_EMIT(PUSH_L) +GEN_EMIT(POP_L) + +/* Condition code setting */ +GEN_EMIT(SETCC_ADD_B) +GEN_EMIT(SETCC_ADD_W) +GEN_EMIT(SETCC_ADD_L) +GEN_EMIT(SETCC_ADDX_B) +GEN_EMIT(SETCC_ADDX_W) +GEN_EMIT(SETCC_ADDX_L) +GEN_EMIT(SETCC_SUB_B) +GEN_EMIT(SETCC_SUB_W) +GEN_EMIT(SETCC_SUB_L) +GEN_EMIT(SETCC_SUBX_B) +GEN_EMIT(SETCC_SUBX_W) +GEN_EMIT(SETCC_SUBX_L) +GEN_EMIT(SETCC_CMP_B) +GEN_EMIT(SETCC_CMP_W) +GEN_EMIT(SETCC_CMP_L) +GEN_EMIT(SETCC_LOGIC_B) +GEN_EMIT(SETCC_LOGIC_W) +GEN_EMIT(SETCC_LOGIC_L) + +/* Condition testing */ +GEN_EMIT(TEST_T) +GEN_EMIT(TEST_F) +GEN_EMIT(TEST_HI) +GEN_EMIT(TEST_LS) +GEN_EMIT(TEST_CC) +GEN_EMIT(TEST_CS) +GEN_EMIT(TEST_NE) +GEN_EMIT(TEST_EQ) +GEN_EMIT(TEST_VC) +GEN_EMIT(TEST_VS) +GEN_EMIT(TEST_PL) +GEN_EMIT(TEST_MI) +GEN_EMIT(TEST_GE) +GEN_EMIT(TEST_LT) +GEN_EMIT(TEST_GT) +GEN_EMIT(TEST_LE) + +/* ALU operations */ +GEN_EMIT(MOVE_B) +GEN_EMIT(MOVE_W) +GEN_EMIT(MOVE_L) +GEN_EMIT(ADD_B) +GEN_EMIT(ADD_W) +GEN_EMIT(ADD_L) +GEN_EMIT(ADDA_W) +GEN_EMIT(ADDX_B) +GEN_EMIT(ADDX_W) +GEN_EMIT(ADDX_L) +GEN_EMIT(SUB_B) +GEN_EMIT(SUB_W) +GEN_EMIT(SUB_L) +GEN_EMIT(SUBA_W) +GEN_EMIT(SUBX_B) +GEN_EMIT(SUBX_W) +GEN_EMIT(SUBX_L) +GEN_EMIT(MULS_W) +GEN_EMIT(MULU_W) +GEN_EMIT(DIVS_W) +GEN_EMIT(DIVU_W) +GEN_EMIT(AND_B) +GEN_EMIT(AND_W) +GEN_EMIT(AND_L) +GEN_EMIT(OR_B) +GEN_EMIT(OR_W) +GEN_EMIT(OR_L) +GEN_EMIT(EOR_B) +GEN_EMIT(EOR_W) +GEN_EMIT(EOR_L) +GEN_EMIT(EXT_W) +GEN_EMIT(EXT_L) +GEN_EMIT(SWAP) + +/* BCD operations */ +GEN_EMIT(ABCD) +GEN_EMIT(SBCD) + +/* Bit-twiddling operations */ +GEN_EMIT(BTST_B) +GEN_EMIT(BTST_L) +GEN_EMIT(BCHG) +GEN_EMIT(BCLR) +GEN_EMIT(BSET) + +/* Shift/rotate operations */ +GEN_EMIT(ASL_B) +GEN_EMIT(ASL_W) +GEN_EMIT(ASL_L) +GEN_EMIT(ASR_B) +GEN_EMIT(ASR_W) +GEN_EMIT(ASR_L) +GEN_EMIT(LSL_B) +GEN_EMIT(LSL_W) +GEN_EMIT(LSL_L) +GEN_EMIT(LSR_B) +GEN_EMIT(LSR_W) +GEN_EMIT(LSR_L) +GEN_EMIT(ROXL_B) +GEN_EMIT(ROXL_W) +GEN_EMIT(ROXL_L) +GEN_EMIT(ROXR_B) +GEN_EMIT(ROXR_W) +GEN_EMIT(ROXR_L) +GEN_EMIT(ROL_B) +GEN_EMIT(ROL_W) +GEN_EMIT(ROL_L) +GEN_EMIT(ROR_B) +GEN_EMIT(ROR_W) +GEN_EMIT(ROR_L) + +/* Conditional and branch operations ("branch_offset" parameter receives + * the native offset of the branch to update when resolving, or -1 if not + * supported) */ +GEN_EMIT(Scc) +GEN_EMIT(ADD_CYCLES_Scc_Dn) +GEN_EMIT_2(DBcc, uint8_t, reg4, int32_t, target) +GEN_EMIT_3(DBcc_native, uint8_t, reg4, int32_t, target, int32_t, native_disp) +#ifdef CPU_X64 +# define SIZE_DBcc_native JIT_X64SIZE_DBcc_native +#else +# define SIZE_DBcc_native JIT_X86SIZE_DBcc_native +#endif +#define JIT_EMIT_DBcc_native(entry,reg4,target,offset) do { \ + Q68JitEntry *__entry = (entry); \ + int32_t __fragment_end = __entry->native_length + SIZE_DBcc_native; \ + JIT_EMIT_DBcc_native(__entry, (reg4), (target), \ + (offset) - __fragment_end); \ +} while (0) +GEN_EMIT_1(Bcc, int32_t, target) +#define JIT_EMIT_Bcc(entry,target,branch_offset) do { \ + JIT_EMIT_Bcc((entry), (target)); \ + *(branch_offset) = -1; \ +} while (0) +GEN_EMIT_2(Bcc_native, int32_t, target, int32_t, native_disp) +#ifdef CPU_X64 +# define SIZE_Bcc_native JIT_X64SIZE_Bcc_native +#else +# define SIZE_Bcc_native JIT_X86SIZE_Bcc_native +#endif +#define JIT_EMIT_Bcc_native(entry,target,native) do { \ + Q68JitEntry *__entry = (entry); \ + int32_t __fragment_end = __entry->native_length + SIZE_Bcc_native; \ + JIT_EMIT_Bcc_native(__entry, (target), (offset) - __fragment_end); \ +} while (0) +GEN_EMIT_2(BSR, uint32_t, return_addr, int32_t, target) +GEN_EMIT(JMP) +GEN_EMIT_1(JSR, uint32_t, return_addr) + +/* MOVEM-related operations */ +GEN_EMIT_1(STORE_DEC_W, uint8_t, reg4) +GEN_EMIT_1(STORE_DEC_L, uint8_t, reg4) +GEN_EMIT_1(STORE_INC_W, uint8_t, reg4) +GEN_EMIT_1(STORE_INC_L, uint8_t, reg4) +GEN_EMIT_1(LOAD_INC_W, uint8_t, reg4) +GEN_EMIT_1(LOAD_INC_L, uint8_t, reg4) +GEN_EMIT_1(LOADA_INC_W, uint8_t, reg4) +GEN_EMIT_1(MOVEM_WRITEBACK, uint8_t, reg4) + +/* Miscellaneous operations */ +GEN_EMIT(CHK_W) +GEN_EMIT_1(LEA, uint8_t, reg4) +GEN_EMIT(PEA) +GEN_EMIT(TAS) +GEN_EMIT_1(MOVE_FROM_USP, uint8_t, reg4) +GEN_EMIT_1(MOVE_TO_USP, uint8_t, reg4) +GEN_EMIT_1(STOP, uint16_t, newSR) +GEN_EMIT(TRAPV) +GEN_EMIT(RTS) +GEN_EMIT(RTR) +GEN_EMIT(RTE) +GEN_EMIT_3(MOVEP_READ_W, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) +GEN_EMIT_3(MOVEP_READ_L, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) +GEN_EMIT_3(MOVEP_WRITE_W, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) +GEN_EMIT_3(MOVEP_WRITE_L, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) +GEN_EMIT_2(EXG, uint8_t, reg1_4, uint8_t, reg2_4) + +/*************************************************************************/ + +#endif // Q68_JIT_X86_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-jit.c b/yabause/src/q68/q68-jit.c new file mode 100644 index 0000000000..a1c2348e3b --- /dev/null +++ b/yabause/src/q68/q68-jit.c @@ -0,0 +1,3722 @@ +/* src/q68/q68-jit.c: Dynamic translation support for Q68 + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include + +#include "q68.h" +#include "q68-const.h" +#include "q68-internal.h" +#include "q68-jit.h" + +/*************************************************************************/ + +/* + * Dynamic translation of 68000 instructions into native code is performed + * as follows: + * + * 1) The emulation core (q68-core.c) calls q68_jit_find() on the current + * PC to check whether there is a translated block starting at that + * address. + * + * 2) If no translated block exists, the emulation core calls + * q68_jit_translate() to translate a block beginning at the current + * PC. q68_jit_translate() continues translating through the end of + * the block of code (determined heuristically) and returns the + * translated block to be passed to q68_jit_run(). + * + * 3) If a translated block exists or was just created, the emulation core + * calls q68_jit_run() to begin execution of the translated code. + * + * 4) The translated code returns either when it reaches the end of the + * block, or when the number of cycles executed exceeds the requested + * cycle count. (For efficiency, cycle count checks are only performed + * before branching instructions: BRA, BSR, Bcc, DBcc, JMP, JSR, RTS, + * RTE, RTR, TRAP, STOP.) + * + * 5) If the translated code returns before the end of the block, the core + * continues to call q68_jit_run() until the end of the block is + * reached. + * + * 6) If a write is made to a region of memory containing one or more + * translated blocks, the core deletes the translations by calling + * q68_jit_clear_page() so the modified code can be retranslated. + * Writes originating in the translated code itself (i.e. self-modifying + * code) are handled by the native code calling q68_jit_clear_write() + * when detecting a write to a page containing translations. + * + * The amount of translated code which can be stored at one time is + * dependent upon two factors: the size of the hash table (set by the + * Q68_JIT_TABLE_SIZE define in q68-const.h) and the maximum translated + * data size (set by the Q68_JIT_DATA_LIMIT define). If either the hash + * table becomes full or the data size limit is reached, any subsequent + * translation will cause the oldest existing translation to be deleted, + * where "oldest" is defined as "last executed the greatest number of + * q68_jit_run() calls ago". Note that the data size limit is checked only + * when beginning a translation, so the total data size at the end of a + * translation may slightly exceed the specified limit. + * + * ======================================================================== + * + * Native code is generated through calls to JIT_EMIT_*() routines. + * Typically, these routines will copy a pre-assembled code fragment to the + * native code buffer, then patch that code fragment with values specific + * to the instruction being translated (such as register numbers or branch + * targets). The general sequence of operations emitted is as follows: + * + * JIT_EMIT_RESOLVE_* -- resolves an effective address to a 68000 + * memory location + * JIT_EMIT_GET_OP1_* -- loads the first operand for an instruction + * JIT_EMIT_GET_OP2_* -- loads the second operand for an instruction + * JIT_EMIT_TEST_* -- tests the state of condition codes + * JIT_EMIT_(insn) -- executes the instruction itself + * JIT_EMIT_SETCC_* -- updates the 68000 condition codes + * JIT_EMIT_SET_* -- stores the result of the instruction + * JIT_EMIT_ADD_CYCLES -- increments the count of clock cycles executed(*) + * JIT_EMIT_ADVANCE_PC -- increments the program counter register(*) + * + * (*) In some cases, particularly when the instruction may cause execution + * of the block to terminate, these operations will occur earlier in + * the sequence. The code generator takes responsibility for ensuring + * that such updates happen in the proper order. + * + * The code generator guarantees that the following invariants hold: + * + * - No operations which could result in a 68000 memory access will be + * generated between loading of the second operand (JIT_EMIT_GET_OP2_*) + * and execution of the instruction. + * + * - A JIT_EMIT_TEST_* operation will always be followed immediately by + * the instruction which uses the operation (such as JIT_EMIT_Scc). + * + * - A JIT_EMIT_SETCC_* operation will always immediately follow the + * operation which produced the result it is testing. + * + * The machine-dependent implementations must obey the following rules: + * + * - Each write to memory must be preceded by a check for translated code + * at the target address (by checking state->jit_pages[]) and a call to + * q68_jit_clear_write() if any translations are found. + * (Implementations may violate this rule in circumstances considered + * unlikely, at the risk of incorrect behavior if such circumstances + * actually occur; for example, the PSP implementation does not check + * for a longword write overlapping the end of a JIT page, and does not + * check writes from the MOVEM instruction.) + * + * - Any instruction which modifies the program counter (Bcc, etc.) must + * terminate execution of the native code block, _except for_ the + * following instructions: + * JMP, RTE, RTR, RTS + * + * - Any instruction which raises an exception must do so by storing the + * appropriate exception number in state->exception and terminating + * execution of the native code block. + * + * - It is the responsibility of the native code to check for a pending + * unmasked interrupt when modifying the status register. + * + * The BSR/JSR and RTS/RTR instructions may make use of a call stack + * provided by the JIT core to allow native code to quickly return to the + * point at which a subroutine call took place. The BSR and JSR + * implementations should, when they terminate execution, return a pointer + * to the following native code (as for termination in CHECK_CYCLES), and + * should set bit 15 of the cycle count returned from JIT_CALL(); + * q68_jit_run() will detect this as a subroutine call, and save the native + * code pointer returned before switching to the subroutine block. The RTS + * and RTR implementations should set bits 15 and 14 of the cycle count + * they return, which will cause q68_jit_run() to search the call stack + * from top to bottom for an entry matching the new 68000 PC; if one is + * found, the corresponding code will be immediately executed, bypassing + * the ordinary block search and execution process (steps 1 through 3 + * above). + * + * See the q68-jit-*.[hS] files for implementation details. + * + * ======================================================================== + * + * The JIT code generator includes a primitive optimization step (if the + * Q68_JIT_OPTIMIZE_FLAGS preprocessor symbol is defined) which omits the + * generation of code to set the 68000 condition flags (X, N, Z, V, and C) + * when unnecessary for correct execution. Specifically, the translator + * checks both the current and the following instruction to determine if + * there are any condition flags which are: + * - set by the current instruction, AND + * - used as input by the following instruction OR + * - NOT set by the following instruction + * If there are no such flags, then it is impossible for the flag values + * set by the current instruction to have any effect on program flow, so + * the native code to set the condition codes can be safely omitted. + * + * The above logic is contained in the cc_needed() routine (and its helper + * routine cc_info()). For each instruction that can set the condition + * flags, the translation routine first calls cc_needed() to determine + * whether the condition flags need to be set or not. If cc_needed() + * returns zero, then there are no flags whose output is required, and the + * relevant JIT_EMIT_SETCC_* operation will be skipped. + * + * In the interests of speed and code clarity, the helper routine cc_info() + * does not check the validity of the opcode passed to it; as a result, it + * may return invalid flag information for some invalid opcodes. If such + * an invalid opcode actually occurs in the instruction stream, the CCR + * register may therefore contain an incorrect value when control is + * transferred to the illegal instruction exception handler. In situations + * where this can cause undesired behavior, this optimization should be + * disabled. + * + * When Q68_JIT_OPTIMIZE_FLAGS is not defined, the definitions of + * cc_needed() and cc_info() are omitted, and cc_needed() is instead + * defined at the preprocessor level to return 1; this has the effect of + * always emitting code to set the condition flags. + */ + +/*************************************************************************/ + +/* For the PSP, we need to avoid local data here sharing a cache line with + * data in other files due to the lack of SC/ME cache coherency */ +#ifdef PSP +static __attribute__((aligned(64),used)) int dummy_top; +#endif + +/*----------------------------------*/ + +/* Entry into which translated code is currently being stored (set by + * q68_jit_translate(), used by opcode translation functions) */ +static Q68JitEntry *current_entry; + +/* Address from which data is being read */ +static uint32_t jit_PC; + +/* Flag indicating whether the PC was updated by an instruction (e.g. jumps) */ +static int PC_updated; + +/* Branch target lookup table (indicates where in the native code each + * address is located) */ +static struct { + uint32_t m68k_address; // Address of 68000 instruction + uint32_t native_offset; // Byte offset into current_entry->native_code +} btcache[Q68_JIT_BTCACHE_SIZE]; +static unsigned int btcache_index; // Where to store the next instruction + +/* Unresolved branch list (saves locations and targets of forward branches) */ +static struct { + uint32_t m68k_target; // Branch target (68000 address) + uint32_t native_offset; // Offset of native branch instruction to update +} unres_branches[Q68_JIT_UNRES_BRANCH_SIZE]; + +/*----------------------------------*/ + +#ifdef PSP // As above +static __attribute__((aligned(64),used)) int dummy_bottom; +#endif + +/*-----------------------------------------------------------------------*/ + +/* Redefine IFETCH to reference jit_PC */ + +static inline uint32_t jit_IFETCH(Q68State *state) { + uint32_t data = READU16(state, jit_PC); + jit_PC += 2; + return data; +} +#define IFETCH jit_IFETCH + +/*************************************************************************/ + +/* + * Forward declarations for helper functions and instruction implementations. + * These are set up identically to q68-core.c so that bugfixes or other + * changes to one can be easily ported to the other. + * + * Note that the return value of OpcodeFunc is taken to be the end-of-block + * flag as returned from q68_jit_translate(), not the number of clock cycles + * taken by the instruction. + */ + +static int translate_insn(Q68State *state, Q68JitEntry *entry); +static void clear_entry(Q68State *state, Q68JitEntry *entry); +static void clear_oldest_entry(Q68State *state); +static int expand_buffer(Q68JitEntry *entry); +static int32_t btcache_lookup(uint32_t address); +static void record_unresolved_branch(uint32_t m68k_target, + uint32_t native_offset); +static inline void JIT_EMIT_TEST_cc(int cond, Q68JitEntry *entry); +static void advance_PC(Q68State *state); +static int raise_exception(Q68State *state, uint8_t num); +static inline int op_ill(Q68State *state, uint32_t opcode); + +#ifdef Q68_JIT_OPTIMIZE_FLAGS +static unsigned int cc_needed(Q68State *state, uint16_t opcode); +# ifdef __GNUC__ +__attribute__((const)) +# endif +static unsigned int cc_info(uint16_t opcode); +#else +# define cc_needed(state,opcode) 1 +#endif + +static int ea_resolve(Q68State *state, uint32_t opcode, int size, + int access_type); +static void ea_get(Q68State *state, uint32_t opcode, int size, + int is_rmw, int *cycles_ret, int op_num); +static void ea_set(Q68State *state, uint32_t opcode, int size); + +static int op_imm(Q68State *state, uint32_t opcode); +static int op_bit(Q68State *state, uint32_t opcode); +static int opMOVE(Q68State *state, uint32_t opcode); +static int op4xxx(Q68State *state, uint32_t opcode); +static int op_CHK(Q68State *state, uint32_t opcode); +static int op_LEA(Q68State *state, uint32_t opcode); +static int opADSQ(Q68State *state, uint32_t opcode); +static int op_Scc(Q68State *state, uint32_t opcode); +static int opDBcc(Q68State *state, uint32_t opcode); +static int op_Bcc(Q68State *state, uint32_t opcode); +static int opMOVQ(Q68State *state, uint32_t opcode); +static int op_alu(Q68State *state, uint32_t opcode); +static int op_DIV(Q68State *state, uint32_t opcode); +static int opAxxx(Q68State *state, uint32_t opcode); +static int op_MUL(Q68State *state, uint32_t opcode); +static int opshft(Q68State *state, uint32_t opcode); +static int opFxxx(Q68State *state, uint32_t opcode); + +static int op4alu(Q68State *state, uint32_t opcode); +static int opMVSR(Q68State *state, uint32_t opcode); +static int opNBCD(Q68State *state, uint32_t opcode); +static int op_PEA(Q68State *state, uint32_t opcode); +static int opSWAP(Q68State *state, uint32_t opcode); +static int op_TAS(Q68State *state, uint32_t opcode); +static int op_EXT(Q68State *state, uint32_t opcode); +static int op_STM(Q68State *state, uint32_t opcode); +static int op_LDM(Q68State *state, uint32_t opcode); +static int opmisc(Q68State *state, uint32_t opcode); +static int opTRAP(Q68State *state, uint32_t opcode); +static int opLINK(Q68State *state, uint32_t opcode); +static int opUNLK(Q68State *state, uint32_t opcode); +static int opMUSP(Q68State *state, uint32_t opcode); +static int op4E7x(Q68State *state, uint32_t opcode); +static int opjump(Q68State *state, uint32_t opcode); + +static int opMOVP(Q68State *state, uint32_t opcode); +static int opADSX(Q68State *state, uint32_t opcode); +static int op_BCD(Q68State *state, uint32_t opcode); +static int opCMPM(Q68State *state, uint32_t opcode); +static int op_EXG(Q68State *state, uint32_t opcode); + +/*-----------------------------------------------------------------------*/ + +/* Main table of instruction implemenation functions; table index is bits + * 15-12 and 8-6 of the opcode (ABCD ...E FG.. .... -> 0ABC DEFG). */ +static OpcodeFunc * const opcode_table[128] = { + op_imm, op_imm, op_imm, op_imm, op_bit, op_bit, op_bit, op_bit, // 00 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 10 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 20 + opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 30 + + op4xxx, op4xxx, op4xxx, op4xxx, op_ill, op_ill, op_CHK, op_LEA, // 40 + opADSQ, opADSQ, opADSQ, op_Scc, opADSQ, opADSQ, opADSQ, op_Scc, // 50 + op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, // 60 + opMOVQ, opMOVQ, opMOVQ, opMOVQ, op_ill, op_ill, op_ill, op_ill, // 70 + + op_alu, op_alu, op_alu, op_DIV, op_alu, op_alu, op_alu, op_DIV, // 80 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // 90 + opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, // A0 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // B0 + + op_alu, op_alu, op_alu, op_MUL, op_alu, op_alu, op_alu, op_MUL, // C0 + op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // D0 + opshft, opshft, opshft, opshft, opshft, opshft, opshft, opshft, // E0 + opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, // F0 +}; + +/* Subtable for instructions in the $4xxx (miscellaneous) group; table index + * is bits 11-9 and 7-6 of the opcode (1000 ABC0 DE.. .... -> 000A BCDE). */ +static OpcodeFunc * const opcode_4xxx_table[32] = { + op4alu, op4alu, op4alu, opMVSR, // 40xx + op4alu, op4alu, op4alu, op_ill, // 42xx + op4alu, op4alu, op4alu, opMVSR, // 44xx + op4alu, op4alu, op4alu, opMVSR, // 46xx + opNBCD, op_PEA, op_STM, op_STM, // 48xx + op4alu, op4alu, op4alu, op_TAS, // 4Axx + op_ill, op_ill, op_LDM, op_LDM, // 4Cxx + op_ill, opmisc, opjump, opjump, // 4Exx +}; + +/* Sub-subtable for instructions in the $4E40-$4E7F range, used by opmisc(); + * index is bits 5-3 of the opcode. */ +static OpcodeFunc * const opcode_4E4x_table[8] = { + opTRAP, opTRAP, opLINK, opUNLK, + opMUSP, opMUSP, op4E7x, op_ill, +}; + +/*************************************************************************/ + +/* Include the header appropriate to the platform (make sure to do this + * after the local function declarations) */ + +#if defined(CPU_X86) || defined(CPU_X64) +# include "q68-jit-x86.h" +#elif defined(CPU_PSP) +# include "q68-jit-psp.h" +#else +# error Dynamic translation is not supported on this platform +#endif + +/*************************************************************************/ +/********************** External interface routines **********************/ +/*************************************************************************/ + +/** + * q68_jit_init: Allocate memory for JIT data. Must be called before any + * other JIT function. + * + * [Parameters] + * state: Processor state block + * [Return value] + * Nonzero on success, zero on error + */ +int q68_jit_init(Q68State *state) +{ + state->jit_table = + state->malloc_func(sizeof(*state->jit_table) * Q68_JIT_TABLE_SIZE); + if (!state->jit_table) { + DMSG("No memory for JIT table"); + goto error_return; + } + state->jit_hashchain = + state->malloc_func(sizeof(*state->jit_hashchain) * Q68_JIT_TABLE_SIZE); + if (!state->jit_hashchain) { + DMSG("No memory for JIT hash chain table"); + goto error_free_jit_table; + } + + /* Make sure all entries are marked as unused (so we don't try to free + * invalid pointers in q68_jit_reset()) */ + int i; + for (i = 0; i < Q68_JIT_TABLE_SIZE; i++) { + state->jit_table[i].m68k_start = 0; + } + + /* Make sure page table is clear (so writes before processor reset + * don't trigger JIT clearing */ + memset(state->jit_pages, 0, sizeof(state->jit_pages)); + + /* Default to no cache flush function */ + state->jit_flush = NULL; + +#ifdef Q68_DISABLE_ADDRESS_ERROR + /* Hack to avoid compiler warnings about unused functions */ + if (0) { + JIT_EMIT_CHECK_ALIGNED_EA(&state->jit_table[0], 0, 0); + JIT_EMIT_CHECK_ALIGNED_SP(&state->jit_table[0], 0, 0); + } +#endif + + return 1; + + error_free_jit_table: + state->free_func(state->jit_table); + state->jit_table = NULL; + error_return: + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_reset: Reset the dynamic translation state, clearing out all + * previously stored data. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +void q68_jit_reset(Q68State *state) +{ + int index; + + state->jit_abort = 0; + for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { + if (state->jit_table[index].m68k_start) { + clear_entry(state, &state->jit_table[index]); + } + } + for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { + state->jit_hashchain[index] = NULL; + } + state->jit_total_data = 0; + state->jit_timestamp = 0; + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + state->jit_blacklist[index].m68k_start = 0; + state->jit_blacklist[index].m68k_end = 0; + } + state->jit_in_blist = 0; + state->jit_blist_num = 0; + state->jit_callstack_top = 0; + memset(state->jit_pages, 0, sizeof(state->jit_pages)); +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_cleanup: Destroy all JIT-related data. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +void q68_jit_cleanup(Q68State *state) +{ + q68_jit_reset(state); + state->free_func(state->jit_hashchain); + state->jit_hashchain = NULL; + state->free_func(state->jit_table); + state->jit_table = NULL; +} + +/*************************************************************************/ + +/** + * q68_jit_translate: Dynamically translate a block of instructions + * starting at the given address. If a translation already exists for the + * given address, it is cleared. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * Translated block to be passed to q68_jit_run(), or NULL on error + */ +Q68JitEntry *q68_jit_translate(Q68State *state, uint32_t address) +{ + int index; + + if (address == 0) { + /* We use address 0 to indicate an unused entry, so we can't + * translate from address 0. But this should never happen except + * in pathological cases (or unhandled exceptions), so just punt + * and let the interpreter handle it */ + return NULL; + } + if (address & 1) { + /* Odd addresses are invalid, so we can't translate them in the + * first place */ + return NULL; + } + address &= 0xFFFFFF; + + /* Check whether we're trying to translate a blacklisted address. */ + + if (state->jit_in_blist) { + index = state->jit_blist_num; + /* See whether we've exited the blacklisted block */ + if (address >= state->jit_blacklist[index].m68k_start + && address <= state->jit_blacklist[index].m68k_end + ) { + return NULL; + } + state->jit_in_blist = 0; + } // We might have entered another blacklisted block, so no "else" here + + if (!state->jit_in_blist) { + /* See if we're in a blacklisted block, and skip its translation + * if so */ + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + if (address >= state->jit_blacklist[index].m68k_start + && address <= state->jit_blacklist[index].m68k_end + ) { + uint32_t dt = state->jit_timestamp + - state->jit_blacklist[index].timestamp; + if (dt < Q68_JIT_BLACKLIST_TIMEOUT) { + state->jit_in_blist = 1; + state->jit_blist_num = index; + return NULL; + } else { + /* Entry expired, so clear it */ + state->jit_blacklist[index].m68k_start = 0; + state->jit_blacklist[index].m68k_end = 0; + } + } + } + } + + /* Clear out any existing translation, then search for an empty slot in + * the hash table. If we've reached the data size limit, first evict + * old entries until we're back under the limit. */ + q68_jit_clear(state, address); + while (state->jit_total_data >= Q68_JIT_DATA_LIMIT) { + clear_oldest_entry(state); + } + const int hashval = JIT_HASH(address); + index = hashval; + int oldest = index; + while (state->jit_table[index].m68k_start != 0) { + if (TIMESTAMP_COMPARE(state->jit_timestamp, + state->jit_table[index].timestamp, + state->jit_table[oldest].timestamp) < 0) { + oldest = index; + } + index++; + /* Using an if here is faster than taking the remainder with % */ + if (UNLIKELY(index >= Q68_JIT_TABLE_SIZE)) { + index = 0; + } + if (UNLIKELY(index == hashval)) { + /* Out of entries, so clear the oldest one and use it */ +#ifdef Q68_JIT_VERBOSE + DMSG("No free slots for code at $%06X, clearing oldest ($%06X)", + (int)address, (int)state->jit_table[oldest].m68k_start); +#endif + clear_entry(state, &state->jit_table[oldest]); + index = oldest; + } + } + current_entry = &state->jit_table[index]; + + /* Initialize the new entry */ + + current_entry->native_code = state->malloc_func(Q68_JIT_BLOCK_EXPAND_SIZE); + if (!current_entry->native_code) { + DMSG("No memory for code at $%06X", address); + current_entry = NULL; + return NULL; + } + current_entry->next = state->jit_hashchain[hashval]; + if (state->jit_hashchain[hashval]) { + state->jit_hashchain[hashval]->prev = current_entry; + } + state->jit_hashchain[hashval] = current_entry; + current_entry->prev = NULL; + current_entry->state = state; + current_entry->m68k_start = address; + current_entry->native_size = Q68_JIT_BLOCK_EXPAND_SIZE; + current_entry->native_length = 0; + current_entry->exec_address = NULL; + current_entry->timestamp = state->jit_timestamp; + current_entry->must_clear = 0; + JIT_EMIT_PROLOGUE(current_entry); + + /* Clear out the branch target cache and unresolved branch list */ + for (index = 0; index < lenof(btcache); index++) { + btcache[index].m68k_address = 0; + } + btcache_index = 0; + for (index = 0; index < lenof(unres_branches); index++) { + unres_branches[index].m68k_target = 0; + } + + /* Translate a block of 68000 code */ + + jit_PC = address; + const uint32_t limit = address + Q68_JIT_MAX_BLOCK_SIZE; + int done = 0; + while (!done && jit_PC < limit) { + /* Make sure we haven't entered a blacklisted block */ + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + if (UNLIKELY(address >= state->jit_blacklist[index].m68k_start + && address <= state->jit_blacklist[index].m68k_end) + ) { + const uint32_t age = state->jit_timestamp + - state->jit_blacklist[index].timestamp; + if (age < Q68_JIT_BLACKLIST_TIMEOUT) { + done = 1; + break; + } else { + /* Entry expired, so clear it */ + state->jit_blacklist[index].m68k_start = 0; + state->jit_blacklist[index].m68k_end = 0; + } + } + } + if (LIKELY(!done)) { + done = translate_insn(state, current_entry); + } + } + + /* Close out the translated block */ + + JIT_EMIT_EPILOGUE(current_entry); + current_entry->m68k_end = jit_PC - 1; + for (index = current_entry->m68k_start >> Q68_JIT_PAGE_BITS; + index <= current_entry->m68k_end >> Q68_JIT_PAGE_BITS; + index++ + ) { + JIT_PAGE_SET(state, index); + } + void *newptr = state->realloc_func(current_entry->native_code, + current_entry->native_length); + if (newptr) { + current_entry->native_code = newptr; + current_entry->native_size = current_entry->native_length; + } + state->jit_total_data += current_entry->native_size; + /* Prepare the block for execution so it can be immediately passed to + * q68_jit_run() (see q68_jit_find() for why we do it here) */ + current_entry->exec_address = current_entry->native_code; + + Q68JitEntry *retval = current_entry; + current_entry = NULL; + if (state->jit_flush) { + state->jit_flush(); + } + return retval; +} + +/*************************************************************************/ + +/** + * q68_jit_find: Find the translated block for a given address, if any. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * Translated block to be passed to q68_jit_run(), or NULL if no such + * block exists + */ +Q68JitEntry *q68_jit_find(Q68State *state, uint32_t address) +{ + const int hashval = JIT_HASH(address); + Q68JitEntry *entry = state->jit_hashchain[hashval]; + while (entry) { + if (entry->m68k_start == address) { + /* Prepare the block for execution. We set exec_address here + * both to avoid overhead in q68_jit_run(), and because + * exec_address could be non-NULL if (for example) the native + * code stopped due to reaching the cycle limit and the core + * then serviced an interrupt. */ + entry->exec_address = entry->native_code; + return entry; + } + entry = entry->next; + } + return NULL; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_run: Run translated 68000 code. + * + * [Parameters] + * state: Processor state block + * cycle_limit: Clock cycle limit on execution (code will stop when + * state->cycles >= cycles) + * address_ptr: Pointer to translated block to execute; will be cleared + * to NULL on return if the end of the block was reached + * [Return value] + * None + */ +void q68_jit_run(Q68State *state, uint32_t cycle_limit, + Q68JitEntry **entry_ptr) +{ + Q68JitEntry *entry = *entry_ptr; + + again: + entry->timestamp = state->jit_timestamp; + state->jit_timestamp++; + entry->running = 1; + int cycles = JIT_CALL(state, cycle_limit - state->cycles, + &entry->exec_address); + entry->running = 0; + state->jit_abort = 0; + state->cycles += cycles & 0x3FFF; + + if (UNLIKELY(entry->must_clear)) { + clear_entry(state, entry); + entry = NULL; + } else if (cycles & 0x8000) { // BSR/JSR/RTS/RTR + if (cycles & 0x4000) { // RTS/RTR + entry = NULL; + unsigned int top = state->jit_callstack_top; + unsigned int i; + for (i = Q68_JIT_CALLSTACK_SIZE; i > 0; i--) { + top = (top + Q68_JIT_CALLSTACK_SIZE-1) % Q68_JIT_CALLSTACK_SIZE; + if (state->jit_callstack[top].return_PC == state->PC) { + entry = state->jit_callstack[top].return_entry; + entry->exec_address = + state->jit_callstack[top].return_native; + state->jit_callstack_top = top; + if (state->cycles < cycle_limit) { + goto again; + } else { + break; + } + } + } + } else { // BSR/JSR + const unsigned int top = state->jit_callstack_top; + const uint32_t return_PC = READU32(state, state->A[7]); + state->jit_callstack[top].return_PC = return_PC; + state->jit_callstack[top].return_entry = entry; + state->jit_callstack[top].return_native = entry->exec_address; + state->jit_callstack_top = (top+1) % Q68_JIT_CALLSTACK_SIZE; + entry = NULL; + } + } else if (!entry->exec_address) { + entry = NULL; + } + + /* If we finished a block, we still have cycles to go, there's no + * exception pending, and there's already a translated block at the + * next PC, jump right to it so we don't incur the extra overhead of + * returning to the caller */ + if (!entry && state->cycles < cycle_limit && !state->exception) { + entry = q68_jit_find(state, state->PC); + if (entry) { + goto again; + } + } + + *entry_ptr = entry; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_clear: Clear any translation beginning at the given address. + * + * [Parameters] + * state: Processor state block + * address: Start address in 68000 address space + * [Return value] + * None + */ +void q68_jit_clear(Q68State *state, uint32_t address) +{ + const int hashval = JIT_HASH(address); + Q68JitEntry *entry = state->jit_hashchain[hashval]; + while (entry) { + if (entry->m68k_start == address) { + clear_entry(state, entry); + return; + } + entry = entry->next; + } +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_clear_page: Clear any translation which occurs in the JIT page + * containing the given address. Intended for use on writes from external + * sources. + * + * [Parameters] + * state: Processor state block + * address: Address to which data was written + * [Return value] + * None + */ +void q68_jit_clear_page(Q68State *state, uint32_t address) +{ + const uint32_t page = address >> Q68_JIT_PAGE_BITS; +#ifdef Q68_JIT_VERBOSE + DMSG("WARNING: jit_clear_page($%06X)", page << Q68_JIT_PAGE_BITS); +#endif + + int index; + for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { + if (state->jit_table[index].m68k_start != 0 + && state->jit_table[index].m68k_start >> Q68_JIT_PAGE_BITS <= page + && state->jit_table[index].m68k_end >> Q68_JIT_PAGE_BITS >= page + ) { + if (UNLIKELY(state->jit_table[index].running)) { + state->jit_table[index].must_clear = 1; + state->jit_abort = 1; + } else { + clear_entry(state, &state->jit_table[index]); + } + } + } + + JIT_PAGE_CLEAR(state, page); +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_jit_clear_write: Clear any translation which includes the given + * address, and (if there is at least one such translation) blacklist the + * address from being translated. + * + * [Parameters] + * state: Processor state block + * address: Address to which data was written + * size: Size of data written + * [Return value] + * None + */ +void q68_jit_clear_write(Q68State *state, uint32_t address, uint32_t size) +{ + int index; + + /* If the address is in a blacklisted block, we don't need to do + * anything (but update the timestamp to extend its timeout) */ + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + if (address >= state->jit_blacklist[index].m68k_start + && address <= state->jit_blacklist[index].m68k_end + ) { + state->jit_blacklist[index].timestamp = state->jit_timestamp; + return; + } + } + + /* Clear the translations-exist flag now; we'll set it later if we + * find a translation on the page that we don't clear */ + const uint32_t page = address >> Q68_JIT_PAGE_BITS; + const uint32_t page_start = page << Q68_JIT_PAGE_BITS; + const uint32_t page_end = ((page+1) << Q68_JIT_PAGE_BITS) - 1; + JIT_PAGE_CLEAR(state, page); + + /* Clear any translations affected by the address, and determine + * the range to be blacklisted. We default to assuming the address is + * the last byte/word of the longest possible instruction (10 bytes: + * MOVE.L #$12345678, ($12345678).l), but do not extend backwards past + * the beginning of a block. */ + int found = 0; + uint32_t start = address + size, end = address + (size-1); +#ifdef Q68_JIT_VERBOSE + DMSG("WARNING: jit_clear_write($%06X,%d)", (int)address, (int)size); +#endif + for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { + if (state->jit_table[index].m68k_start == 0) { + continue; + } + if (state->jit_table[index].m68k_start < address + size + && state->jit_table[index].m68k_end >= address) { + found = 1; + if (start > state->jit_table[index].m68k_start) { + /* Use the earliest start address of those we see */ + start = state->jit_table[index].m68k_start; + } + if (UNLIKELY(state->jit_table[index].running)) { + state->jit_table[index].must_clear = 1; + state->jit_abort = 1; + } else { + clear_entry(state, &state->jit_table[index]); + } + } else if (state->jit_table[index].m68k_start <= page_start + && state->jit_table[index].m68k_end >= page_end) { + /* No need to clear this one, so set the page bit again */ + JIT_PAGE_SET(state, page); + } + } + if (!found || start < (address & ~1) - 8) { + start = (address & ~1) - 8; + } + + /* Add the blacklist entry */ +#ifdef Q68_JIT_VERBOSE + DMSG("Blacklisting $%06X...$%06X", (int)start, (int)end); +#endif + /* First see if this overlaps with another entry */ + found = 0; + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + if (state->jit_blacklist[index].m68k_start <= end + && state->jit_blacklist[index].m68k_end >= start) { +#ifdef Q68_JIT_VERBOSE + DMSG("(Merging with $%06X...%06X)", + (int)state->jit_blacklist[index].m68k_start, + (int)state->jit_blacklist[index].m68k_end); +#endif + if (start > state->jit_blacklist[index].m68k_start) { + start = state->jit_blacklist[index].m68k_start; + } + if (end < state->jit_blacklist[index].m68k_end) { + end = state->jit_blacklist[index].m68k_end; + } + found = 1; + break; + } + } + /* Otherwise, add this as a new entry; if there are no free slots, + * evict the oldest entry */ + if (!found) { + int oldest = 0; + for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { + if (state->jit_blacklist[index].m68k_start == 0) { + found = 1; + break; + } else if (TIMESTAMP_COMPARE(state->jit_timestamp, + state->jit_blacklist[index].timestamp, + state->jit_blacklist[oldest].timestamp) < 0) { + oldest = index; + } + } + if (!found) { + index = oldest; + } + } + state->jit_blacklist[index].m68k_start = start; + state->jit_blacklist[index].m68k_end = end; + state->jit_blacklist[index].timestamp = state->jit_timestamp; +} + +/*************************************************************************/ +/************************ Local helper functions *************************/ +/*************************************************************************/ + +/** + * translate_insn: Translate a single 68000 instruction. + * + * [Parameters] + * state: Processor state block + * entry: Q68JitEntry structure pointer + * [Return value] + * Nonzero if the instruction marks the end of the block, else zero + */ +static int translate_insn(Q68State *state, Q68JitEntry *entry) +{ + /* See if there are any branches to this address we can update */ + int i; + for (i = 0; i < lenof(unres_branches); i++) { + if (unres_branches[i].m68k_target == jit_PC) { + JIT_FIXUP_BRANCH(entry, unres_branches[i].native_offset, + current_entry->native_length); + unres_branches[i].m68k_target = 0; + } + } + + /* Update the branch target cache with this address */ + btcache[btcache_index].m68k_address = jit_PC; + btcache[btcache_index].native_offset = current_entry->native_length; + btcache_index = (btcache_index + 1) % lenof(btcache); + + /* Fetch the next instruction */ + const unsigned int opcode = IFETCH(state); + state->current_PC = jit_PC; + + /* Emit a cycle count check if appropriate */ +#ifdef Q68_JIT_LOOSE_TIMING + if ((opcode & 0xFF00) == 0x6100 // Bcc + || (opcode & 0xF0F8) == 0x50C8 // DBcc + || (opcode & 0xFFF0) == 0x4E40 // TRAP + || (opcode & 0xFF80) == 0x4E80 // JSR/JMP + || opcode == 0x4E72 // STOP + || opcode == 0x4E73 // RTE + || opcode == 0x4E75 // RTS + || opcode == 0x4E77 // RTR +# ifdef Q68_M68K_TESTER // Define when linking with m68k-tester + || opcode == 0x7100 // m68k-tester abort opcode +# endif + ) { +#endif + JIT_EMIT_CHECK_CYCLES(current_entry); +#ifdef Q68_JIT_LOOSE_TIMING + } +#endif + + /* Add a trace call if we're tracing */ +#ifdef Q68_TRACE + JIT_EMIT_TRACE(current_entry); +#endif + + /* Translate the instruction itself and update the 68000 PC */ + PC_updated = 0; + const unsigned int index = (opcode>>9 & 0x78) | (opcode>>6 & 0x07); + int done = (*opcode_table[index])(state, opcode); + /* Only update the PC if the function didn't do so itself (see e.g. + * op_imm() to SR), but check the jit_abort flag unless we're + * terminating anyway */ + if (!done) { + if (!PC_updated) { + const int32_t advance = jit_PC - (state->current_PC - 2); + JIT_EMIT_ADVANCE_PC_CHECK_ABORT(current_entry, advance); + } else { + JIT_EMIT_CHECK_ABORT(current_entry); + } + } else { + if (!PC_updated) { + advance_PC(state); + } + } + + return done; +} + +/*************************************************************************/ + +/** + * clear_entry: Clear a specific entry from the JIT table, freeing the + * native code buffer and unlinking the entry from its references. + * + * [Parameters] + * state: Processor state block + * entry: Q68JitEntry structure pointer + * [Return value] + * None + */ +static void clear_entry(Q68State *state, Q68JitEntry *entry) +{ + /* Clear the entry out of the call stack first */ + int i; + for (i = 0; i < Q68_JIT_CALLSTACK_SIZE; i++) { + if (state->jit_callstack[i].return_entry == entry) { + state->jit_callstack[i].return_PC = 0; + } + } + + /* Free the native code */ + state->jit_total_data -= entry->native_size; + state->free_func(entry->native_code); + entry->native_code = NULL; + + /* Clear the entry from the table and hash chain */ + if (entry->next) { + entry->next->prev = entry->prev; + } + if (entry->prev) { + entry->prev->next = entry->next; + } else { + state->jit_hashchain[JIT_HASH(entry->m68k_start)] = entry->next; + } + + /* Mark the entry as free */ + entry->m68k_start = 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * clear_oldest_entry: Clear the oldest entry from the JIT table. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +static void clear_oldest_entry(Q68State *state) +{ + int oldest = -1; + int index; + for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { + if (state->jit_table[index].m68k_start == 0) { + continue; + } + if (oldest < 0 + || TIMESTAMP_COMPARE(state->jit_timestamp, + state->jit_table[index].timestamp, + state->jit_table[oldest].timestamp) < 0) { + oldest = index; + } + } + if (LIKELY(oldest >= 0)) { + clear_entry(state, &state->jit_table[oldest]); + } else { + DMSG("Tried to clear oldest entry from an empty table!"); + /* Set the total size to zero, just in case something weird happened */ + state->jit_total_data = 0; + } +} + +/*************************************************************************/ + +/** + * expand_buffer: Expands the native code buffer in the given JIT entry by + * Q68_JIT_BLOCK_EXPAND_SIZE. + * + * [Parameters] + * entry: Q68JitEntry structure pointer + * [Return value] + * Nonzero on success, zero on failure (out of memory) + */ +static int expand_buffer(Q68JitEntry *entry) +{ + const uint32_t newsize = entry->native_size + Q68_JIT_BLOCK_EXPAND_SIZE; + void *newptr = entry->state->realloc_func(entry->native_code, newsize); + if (!newptr) { + DMSG("Out of memory"); + return 0; + } + entry->native_code = newptr; + entry->native_size = newsize; + return 1; +} + +/*************************************************************************/ + +/** + * btcache_lookup: Search the branch target cache for the given 68000 + * address. + * + * [Parameters] + * address: 68000 address to search for + * [Return value] + * Corresponding byte offset into current_entry->native_code, or -1 if + * the address could not be found + */ +static int32_t btcache_lookup(uint32_t address) +{ + /* Search backwards from the current instruction so we can handle short + * loops quickly; note that btcache_index is now pointing to where the + * _next_ instruction will go */ + const int current = (btcache_index + (lenof(btcache)-1)) % lenof(btcache); + int index = current; + do { + if (btcache[index].m68k_address == address) { + return btcache[index].native_offset; + } + index = (index + (lenof(btcache)-1)) % lenof(btcache); + } while (index != current); + return -1; +} + +/*-----------------------------------------------------------------------*/ + +/** + * record_unresolved_branch: Record the given branch target and native + * offset in an empty slot in the unresolved branch table. If there are + * no empty slots, purge the oldest (lowest native offset) entry. + * + * [Parameters] + * m68k_target: Branch target address in 68000 address space + * native_offset: Offset of branch to update in native code + * [Return value] + * None + */ +static void record_unresolved_branch(uint32_t m68k_target, + uint32_t native_offset) +{ + int oldest = 0; + int i; + for (i = 0; i < lenof(unres_branches); i++) { + if (unres_branches[i].m68k_target == 0) { + oldest = i; + break; + } else if (unres_branches[i].native_offset + < unres_branches[oldest].native_offset) { + oldest = i; + } + } + unres_branches[oldest].m68k_target = m68k_target; + unres_branches[oldest].native_offset = native_offset; +} + +/*************************************************************************/ + +/** + * JIT_EMIT_TEST_cc: Emit the appropriate TEST_* operation depending on + * the specified condition. + * + * [Parameters] + * cond: Condition code + * entry: Q68JitEntry structure pointer + */ +static inline void JIT_EMIT_TEST_cc(int cond, Q68JitEntry *entry) +{ + switch ((cond)) { + case COND_T: JIT_EMIT_TEST_T (entry); break; + case COND_F: JIT_EMIT_TEST_F (entry); break; + case COND_HI: JIT_EMIT_TEST_HI(entry); break; + case COND_LS: JIT_EMIT_TEST_LS(entry); break; + case COND_CC: JIT_EMIT_TEST_CC(entry); break; + case COND_CS: JIT_EMIT_TEST_CS(entry); break; + case COND_NE: JIT_EMIT_TEST_NE(entry); break; + case COND_EQ: JIT_EMIT_TEST_EQ(entry); break; + case COND_VC: JIT_EMIT_TEST_VC(entry); break; + case COND_VS: JIT_EMIT_TEST_VS(entry); break; + case COND_PL: JIT_EMIT_TEST_PL(entry); break; + case COND_MI: JIT_EMIT_TEST_MI(entry); break; + case COND_GE: JIT_EMIT_TEST_GE(entry); break; + case COND_LT: JIT_EMIT_TEST_LT(entry); break; + case COND_GT: JIT_EMIT_TEST_GT(entry); break; + case COND_LE: JIT_EMIT_TEST_LE(entry); break; + } +} + +/*************************************************************************/ + +/** + * advance_PC: Emit JIT code to advance the PC to the location indicated + * by jit_PC, and set the PC_updated flag so that the PC is not advanced + * again after the current instruction has been processed. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +static void advance_PC(Q68State *state) +{ + JIT_EMIT_ADVANCE_PC(current_entry, jit_PC - (state->current_PC - 2)); + PC_updated = 1; +} + +/*************************************************************************/ + +/** + * raise_exception: Emit JIT code to raise an exception. + * + * [Parameters] + * state: Processor state block + * num: Exception number + * [Return value] + * Nonzero (end translated block) + */ +static int raise_exception(Q68State *state, uint8_t num) +{ + JIT_EMIT_EXCEPTION(current_entry, num); + return 1; +} + +/*************************************************************************/ + +/** + * op_ill: Emit JIT code to handle a generic illegal opcode. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * [Return value] + * Nonzero (end translated block) + */ +static inline int op_ill(Q68State *state, uint32_t opcode) +{ + return raise_exception(state, EX_ILLEGAL_INSTRUCTION); +} + +/*************************************************************************/ +/*************************************************************************/ + +#ifdef Q68_JIT_OPTIMIZE_FLAGS + +/** + * cc_needed: Return whether any condition code outputs are required from + * the current instruction, based on the instruction located at jit_PC. + * + * This routine assumes that the current and following instructions are + * valid ones; certain illegal forms of instructions may be incorrectly + * treated as valid and thus cause an incorrect result. + * + * [Parameters] + * state: Processor state block + * opcode: Opcode of the instruction currently being processed + * [Return value] + * Nonzero if condition code outputs are required, zero otherwise + */ +static unsigned int cc_needed(Q68State *state, uint16_t opcode) +{ + const uint16_t next_opcode = READU16(state, jit_PC); + const unsigned int this_output = cc_info(opcode) & 0x1F; + const unsigned int next_input = (cc_info(next_opcode) >> 8) & 0x1F; + const unsigned int next_output = cc_info(next_opcode) & 0x1F; + /* A condition code output from this instruction is known to be + * unneeded if the following instruction (1) does not use that + * condition code as input and (2) also outputs to the same condition + * code. We want to know whether there are any condition codes in the + * current instruction's output set for which these conditions are + * _not_ fulfilled. */ + return this_output & ~(~next_input & next_output); +} + +/*************************************************************************/ + +/** + * cc_info: Return a bitmask of which condition codes are used and which + * are modified by the given opcode. + * + * [Parameters] + * opcode: Opcode to check + * [Return value] + * Bits 12-8: Which of XNZVC are used as input by the given opcode + * Bits 4-0: Which of XNZVC are modified by the given opcode + */ +static unsigned int cc_info(uint16_t opcode) +{ + const unsigned int INPUT_XNZVC = 0x1F00; + const unsigned int INPUT_XZ = 0x1400; + const unsigned int INPUT_X = 0x1000; + const unsigned int INPUT_N = 0x0800; + const unsigned int INPUT_V = 0x0200; + const unsigned int INPUT_NONE = 0x0000; + const unsigned int OUTPUT_XNZVC = 0x001F; + const unsigned int OUTPUT_XZC = 0x0015; + const unsigned int OUTPUT_NZVC = 0x000F; + const unsigned int OUTPUT_N = 0x0008; + const unsigned int OUTPUT_Z = 0x0004; + const unsigned int OUTPUT_NONE = 0x0000; + static const unsigned int cond_inputs[] = { + [COND_T ] = 0x0000, + [COND_F ] = 0x0000, + [COND_HI] = 0x0500, + [COND_LS] = 0x0500, + [COND_CC] = 0x0100, + [COND_CS] = 0x0100, + [COND_NE] = 0x0400, + [COND_EQ] = 0x0400, + [COND_VC] = 0x0200, + [COND_VS] = 0x0200, + [COND_PL] = 0x0800, + [COND_MI] = 0x0800, + [COND_GE] = 0x0A00, + [COND_LT] = 0x0A00, + [COND_GT] = 0x0E00, + [COND_LE] = 0x0E00, + }; + + switch (opcode>>12) { + + case 0x0: + if (opcode & 0x100) { + if ((opcode>>3 & 7) == 1) { // MOVEP + return INPUT_NONE | OUTPUT_NONE; + } else { // BTST, etc. (dynamic) + return INPUT_NONE | OUTPUT_Z; + } + } else if ((opcode>>6 & 3) == 3) { // Illegal (size==3) + return 0; + } else { + switch (opcode>>9 & 7) { + case 0: // ORI + if ((opcode & 0xBF) == 0x3C) { // ORI to CCR/SR + return INPUT_XNZVC | OUTPUT_XNZVC; + } else { + return INPUT_NONE | OUTPUT_NZVC; + } + case 1: // ANDI + if ((opcode & 0xBF) == 0x3C) { // ANDI to CCR/SR + return INPUT_XNZVC | OUTPUT_XNZVC; + } else { + return INPUT_NONE | OUTPUT_NZVC; + } + case 2: // SUBI + return INPUT_NONE | OUTPUT_XNZVC; + case 3: // ADDI + return INPUT_NONE | OUTPUT_XNZVC; + case 4: // BTST, etc. (static) + return INPUT_NONE | OUTPUT_Z; + case 5: // EORI + if ((opcode & 0xBF) == 0x3C) { // EORI to CCR/SR + return INPUT_XNZVC | OUTPUT_XNZVC; + } else { + return INPUT_NONE | OUTPUT_NZVC; + } + case 6: // CMPI + return INPUT_NONE | OUTPUT_NZVC; + case 7: // Illegal + return 0; + } + } + + case 0x1: + case 0x2: + case 0x3: + if ((opcode>>6 & 7) == 1) { // MOVEA.[LW] + return INPUT_NONE | OUTPUT_NONE; + } else { // MOVE.[BLW] + return INPUT_NONE | OUTPUT_NZVC; + } + + case 0x4: + if (opcode & 0x0100) { + switch (opcode>>6 & 3) { + case 0: // Illegal + case 1: // Illegal + return 0; + case 2: // CHK + /* N is unmodified if no exception occurs, so treat as input */ + return INPUT_N | OUTPUT_N; + case 3: // LEA + return INPUT_NONE | OUTPUT_NONE; + } + } else { + switch (opcode & 0x0EC0) { + case 0x0000: // NEGX.B + case 0x0040: // NEGX.W + case 0x0080: // NEGX.L + return INPUT_XZ | OUTPUT_XNZVC; + case 0x00C0: // MOVE from SR + return INPUT_XNZVC | OUTPUT_NONE; + case 0x0200: // CLR.B + case 0x0240: // CLR.W + case 0x0280: // CLR.L + return INPUT_NONE | OUTPUT_NZVC; + case 0x02C0: // Illegal + return 0; + case 0x0400: // NEG.B + case 0x0440: // NEG.W + case 0x0480: // NEG.L + return INPUT_NONE | OUTPUT_XNZVC; + case 0x04C0: // MOVE to CCR + return INPUT_NONE | OUTPUT_XNZVC; + case 0x0600: // NOT.B + case 0x0640: // NOT.W + case 0x0680: // NOT.L + return INPUT_NONE | OUTPUT_NZVC; + case 0x06C0: // MOVE to SR + return INPUT_NONE | OUTPUT_XNZVC; + case 0x0800: // NBCD + return INPUT_XZ | OUTPUT_XZC; + case 0x0840: // PEA + if ((opcode>>3 & 7) == 0) { // SWAP.L + return INPUT_NONE | OUTPUT_NZVC; + } else { + return INPUT_NONE | OUTPUT_NONE; + } + case 0x0880: // MOVEM.W reglist, + case 0x08C0: // MOVEM.L reglist, + if ((opcode>>3 & 7) == 0) { // EXT.* + return INPUT_NONE | OUTPUT_NZVC; + } else { + return INPUT_NONE | OUTPUT_NONE; + } + case 0x0A00: // TST.B + case 0x0A40: // TST.W + case 0x0A80: // TST.L + case 0x0AC0: // TAS + return INPUT_NONE | OUTPUT_NZVC; + case 0x0C00: // TST.B + return 0; + case 0x0C40: // Miscellaneous + switch (opcode>>3 & 7) { + case 0: // TRAP #0-7 + case 1: // TRAP #8-15 + case 2: // LINK + case 3: // UNLK + case 4: // MOVE from USP + case 5: // MOVE to USP + return INPUT_NONE | OUTPUT_NONE; + case 6: // Miscellaneous + switch (opcode & 7) { + case 0: // RESET + case 1: // NOP + return INPUT_NONE | OUTPUT_NONE; + case 2: // STOP + case 3: // RTE + return INPUT_NONE | OUTPUT_XNZVC; + case 4: // Illegal + return 0; + case 5: // RTS + return INPUT_NONE | OUTPUT_NONE; + case 6: // TRAPV + return INPUT_V | OUTPUT_NONE; + case 7: // RTR + return INPUT_NONE | OUTPUT_XNZVC; + } + case 7: // Illegal + return 0; + } + case 0x0C80: // MOVEM.W ,reglist + case 0x0CC0: // MOVEM.L ,reglist + return INPUT_NONE | OUTPUT_NONE; + case 0x0E00: // Illegal + case 0x0E40: // Illegal + return 0; + case 0x0E80: // JSR + case 0x0EC0: // JMP + return INPUT_NONE | OUTPUT_NONE; + } + } + + case 0x5: + if ((opcode>>6 & 3) == 3) { // Scc/DBcc + return cond_inputs[opcode>>8 & 0xF] | OUTPUT_NONE; + } else { // ADDQ/SUBQ + if ((opcode>>3 & 7) == 1) { // Address register target + return INPUT_NONE | OUTPUT_NONE; + } else { // Other target + return INPUT_NONE | OUTPUT_XNZVC; + } + } + + case 0x6: + /* Bcc/BSR */ + return cond_inputs[opcode>>8 & 0xF] | OUTPUT_NONE; + + case 0x7: + if (opcode & 0x0100) { // Illegal + return 0; + } else { // MOVEQ + return INPUT_NONE | OUTPUT_NZVC; + } + + case 0x8: + if ((opcode>>6 & 3) == 3) { // MULS/MULU + return INPUT_NONE | OUTPUT_NZVC; + } else if ((opcode & 0x01F0) == 0x0100) { // SBCD + return INPUT_XZ | OUTPUT_XZC; + } else { // OR + return INPUT_NONE | OUTPUT_NZVC; + } + + case 0x9: + if ((opcode>>6 & 3) == 3) { // SUBA + return INPUT_NONE | OUTPUT_NONE; + } else if ((opcode & 0x0130) == 0x0100) { // SUBX + return INPUT_XZ | OUTPUT_XNZVC; + } else { // SUB + return INPUT_NONE | OUTPUT_XNZVC; + } + + case 0xA: + /* Nothing here */ + return 0; + + case 0xB: + /* CMP/CMPA/CMPM/EOR */ + return INPUT_NONE | OUTPUT_NZVC; + + case 0xC: + if ((opcode>>6 & 3) == 3) { // DIVS/DIVD + return INPUT_NONE | OUTPUT_NZVC; + } else if ((opcode & 0x01F0) == 0x0100) { // ABCD + return INPUT_XZ | OUTPUT_XZC; + } else if ((opcode & 0x0130) == 0x0100) { // EXG + return INPUT_NONE | OUTPUT_NONE; + } else { // AND + return INPUT_NONE | OUTPUT_NZVC; + } + + case 0xD: + if ((opcode>>6 & 3) == 3) { // ADDA + return INPUT_NONE | OUTPUT_NONE; + } else if ((opcode & 0x0130) == 0x0100) { // ADDX + return INPUT_XZ | OUTPUT_XNZVC; + } else { // ADD + return INPUT_NONE | OUTPUT_XNZVC; + } + + case 0xE: + /* Shift/rotate */ + return INPUT_X | OUTPUT_XNZVC; + + case 0xF: + /* Nothing here */ + return 0; + + } // switch (opcode>>12) + + return 0; // Should be unreachable, but just for safety +} + +#endif // Q68_JIT_OPTIMIZE_FLAGS + +/*************************************************************************/ +/*************************************************************************/ + +/** + * ea_resolve: Emit JIT code to resolve the address for the + * memory-reference EA indicated by opcode[5:0] and store it in + * state->ea_addr. Behavior is undefined if the EA is a direct register + * reference. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * access_type: Access type (ACCESS_*) + * [Return value] + * Clock cycles used (negative indicates an illegal EA) + */ +static int ea_resolve(Q68State *state, uint32_t opcode, int size, + int access_type) +{ + const unsigned int mode = EA_MODE(opcode); + const unsigned int reg = EA_REG(opcode); + const unsigned int bytes = SIZE_TO_BYTES(size); + + static const int base_cycles[8] = {0, 0, 4, 4, 6, 8, 10, 0}; + int cycles = base_cycles[mode] + (size==SIZE_L ? 4 : 0); + + switch (mode) { + case EA_INDIRECT: + JIT_EMIT_RESOLVE_INDIRECT(current_entry, (8+reg)*4); + break; + case EA_POSTINCREMENT: + if (bytes == 1 && reg == 7) { // A7 must stay even + JIT_EMIT_RESOLVE_POSTINC_A7_B(current_entry); + } else { + JIT_EMIT_RESOLVE_POSTINC(current_entry, (8+reg)*4, bytes); + } + break; + case EA_PREDECREMENT: + if (access_type == ACCESS_WRITE) { + /* 2-cycle penalty not applied to write-only accesses + * (MOVE and MOVEM) */ + cycles -= 2; + } + if (bytes == 1 && reg == 7) { // A7 must stay even + JIT_EMIT_RESOLVE_PREDEC_A7_B(current_entry); + } else { + JIT_EMIT_RESOLVE_PREDEC(current_entry, (8+reg)*4, bytes); + } + break; + case EA_DISPLACEMENT: + JIT_EMIT_RESOLVE_DISP(current_entry, (8+reg)*4, (int16_t)IFETCH(state)); + break; + case EA_INDEX: { + const uint16_t ext = IFETCH(state); + const unsigned int ireg = ext >> 12; // 0..15 + const int8_t disp = (int8_t)ext; + if (ext & 0x0800) { + JIT_EMIT_RESOLVE_INDEX_L(current_entry, (8+reg)*4, ireg*4, disp); + } else { + JIT_EMIT_RESOLVE_INDEX_W(current_entry, (8+reg)*4, ireg*4, disp); + } + break; + } + default: /* case EA_MISC */ + switch (reg) { + case EA_MISC_ABSOLUTE_W: + cycles += 8; + JIT_EMIT_RESOLVE_ABSOLUTE(current_entry, (int16_t)IFETCH(state)); + break; + case EA_MISC_ABSOLUTE_L: { + cycles += 12; + uint32_t addr = IFETCH(state) << 16; + addr |= (uint16_t)IFETCH(state); + JIT_EMIT_RESOLVE_ABSOLUTE(current_entry, addr); + break; + } + case EA_MISC_PCREL: + if (access_type != ACCESS_READ) { + return -1; + } else { + cycles += 8; + JIT_EMIT_RESOLVE_ABSOLUTE( + current_entry, state->current_PC + (int16_t)IFETCH(state) + ); + } + break; + case EA_MISC_PCREL_INDEX: + if (access_type != ACCESS_READ) { + return -1; + } else { + cycles += 10; + const uint16_t ext = IFETCH(state); + const unsigned int ireg = ext >> 12; // 0..15 + const int32_t disp = (int32_t)((int8_t)ext); + if (ext & 0x0800) { + JIT_EMIT_RESOLVE_ABS_INDEX_L( + current_entry, state->current_PC + disp, ireg*4 + ); + } else { + JIT_EMIT_RESOLVE_ABS_INDEX_W( + current_entry, state->current_PC + disp, ireg*4 + ); + } + } + break; + default: + return -1; + } + } + return cycles; +} + +/*-----------------------------------------------------------------------*/ + +/** + * ea_get: Emit JIT code to read an unsigned value from the EA indicated + * by opcode[5:0] and use it as either the first or the second operand to + * an operation, as specified by the op_num parameter. + * + * If the EA selector is invalid for the access size and mode, an illegal + * instruction exception is raised, and the error is indicated by a + * negative value returned in *cycles_ret. + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * is_rmw: Nonzero if the operand will be modified and written back + * cycles_ret: Pointer to variable to receive clock cycles used + * (negative indicates an illegal EA) + * op_num: Which operand to read the value into (1 or 2) + * [Return value] + * None + */ +static void ea_get(Q68State *state, uint32_t opcode, int size, + int is_rmw, int *cycles_ret, int op_num) +{ + switch (EA_MODE(opcode)) { + + case EA_DATA_REG: + *cycles_ret = 0; + if (op_num == 1) { + JIT_EMIT_GET_OP1_REGISTER(current_entry, EA_REG(opcode) * 4); + } else { + JIT_EMIT_GET_OP2_REGISTER(current_entry, EA_REG(opcode) * 4); + } + break; + + case EA_ADDRESS_REG: + *cycles_ret = 0; + if (size == SIZE_B) { + /* An.b not permitted */ + raise_exception(state, EX_ILLEGAL_INSTRUCTION); + *cycles_ret = -1; + return; + } else { + if (op_num == 1) { + JIT_EMIT_GET_OP1_REGISTER(current_entry, + (8 + EA_REG(opcode)) * 4); + } else { + JIT_EMIT_GET_OP2_REGISTER(current_entry, + (8 + EA_REG(opcode)) * 4); + } + } + break; + + case EA_MISC: + if (EA_REG(opcode) == EA_MISC_IMMEDIATE) { + if (is_rmw) { + raise_exception(state, EX_ILLEGAL_INSTRUCTION); + *cycles_ret = -1; + return; + } else { + *cycles_ret = (size==SIZE_L ? 8 : 4); + uint32_t val; + val = IFETCH(state); + if (size == SIZE_B) { + val &= 0xFF; + } else if (size == SIZE_L) { + val <<= 16; + val |= (uint16_t)IFETCH(state); + } + if (op_num == 1) { + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, val); + } else { + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, val); + } + } + break; + } + /* else fall through */ + + default: + *cycles_ret = ea_resolve(state, opcode, size, + is_rmw ? ACCESS_MODIFY : ACCESS_READ); + if (*cycles_ret < 0) { + raise_exception(state, EX_ILLEGAL_INSTRUCTION); + return; + } + if (size == SIZE_B) { + if (op_num == 1) { + JIT_EMIT_GET_OP1_EA_B(current_entry); + } else { + JIT_EMIT_GET_OP2_EA_B(current_entry); + } + } else if (size == SIZE_W) { +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA( + current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ + ); +#endif + if (op_num == 1) { + JIT_EMIT_GET_OP1_EA_W(current_entry); + } else { + JIT_EMIT_GET_OP2_EA_W(current_entry); + } + } else { // size == SIZE_L +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA( + current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ + ); +#endif + if (op_num == 1) { + JIT_EMIT_GET_OP1_EA_L(current_entry); + } else { + JIT_EMIT_GET_OP2_EA_L(current_entry); + } + } + break; + + } // switch (EA_MODE(opcode)) +} + +/*-----------------------------------------------------------------------*/ + +/** + * ea_set: Emit JIT code to update a value at the EA indicated by + * opcode[5:0]. If the EA is a memory reference, uses the previously + * resolved address in state->ea_addr rather than resolving the address + * again. Behavior is undefined if the previous ea_resolve() or ea_get() + * failed (or if no previous call was made). + * + * [Parameters] + * state: Processor state block + * opcode: Instruction opcode + * size: Access size (SIZE_*) + * [Return value] + * None + */ +static void ea_set(Q68State *state, uint32_t opcode, int size) +{ + switch (EA_MODE(opcode)) { + case EA_DATA_REG: + if (size == SIZE_B) { + JIT_EMIT_SET_REGISTER_B(current_entry, EA_REG(opcode) * 4); + } else if (size == SIZE_W) { + JIT_EMIT_SET_REGISTER_W(current_entry, EA_REG(opcode) * 4); + } else { // size == SIZE_L + JIT_EMIT_SET_REGISTER_L(current_entry, EA_REG(opcode) * 4); + } + return; + case EA_ADDRESS_REG: + if (size == SIZE_W) { + JIT_EMIT_SET_AREG_W(current_entry, (8 + EA_REG(opcode)) * 4); + } else { // size == SIZE_L + JIT_EMIT_SET_REGISTER_L(current_entry, (8 + EA_REG(opcode)) * 4); + } + return; + default: { + if (size == SIZE_B) { + JIT_EMIT_SET_EA_B(current_entry); + } else if (size == SIZE_W) { +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA( + current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE + ); +#endif + JIT_EMIT_SET_EA_W(current_entry); + } else { // size == SIZE_L +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA( + current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE + ); +#endif + JIT_EMIT_SET_EA_L(current_entry); + } + return; + } + } +} + +/*************************************************************************/ +/*********************** Major instruction groups ************************/ +/*************************************************************************/ + +/** + * op_imm: Immediate instructions (format 0000 xxx0 xxxx xxxx). + */ +static int op_imm(Q68State *state, uint32_t opcode) +{ + /* Check for bit-twiddling and illegal opcodes first */ + enum {OR = 0, AND, SUB, ADD, _BIT, EOR, CMP, _ILL} aluop; + aluop = opcode>>9 & 7; + if (aluop == _BIT) { + return op_bit(state, opcode); + } else if (aluop == _ILL) { + return op_ill(state, opcode); + } + + /* Get the instruction size */ + INSN_GET_SIZE; + if (size == 3) { + return op_ill(state, opcode); + } + + /* Fetch the immediate value */ + int cycles_dummy; + ea_get(state, EA_MISC<<3 | EA_MISC_IMMEDIATE, size, 0, &cycles_dummy, 1); + + /* Fetch the EA operand (which may be SR or CCR) */ + int use_SR; + int cycles; + if ((aluop==OR || aluop==AND || aluop==EOR) && (opcode & 0x3F) == 0x3C) { + /* xxxI #imm,SR (or CCR) use the otherwise-invalid form of an + * immediate value destination */ + use_SR = 1; + cycles = 8; // Total instruction time is 20 cycles + switch (size) { + case SIZE_B: + JIT_EMIT_GET_OP2_CCR(current_entry); + break; + case SIZE_W: + JIT_EMIT_CHECK_SUPER(current_entry); + JIT_EMIT_GET_OP2_SR(current_entry); + break; + default: + return op_ill(state, opcode); + } + } else { + use_SR = 0; + ea_get(state, opcode, size, 1, &cycles, 2); + if (cycles < 0) { + return 1; + } + } + + /* Check whether we need to output condition codes */ + const int do_cc = cc_needed(state, opcode); + + /* Perform the operation */ + switch (aluop) { + case OR: if (size == SIZE_B) { + JIT_EMIT_OR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_OR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_OR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case AND: if (size == SIZE_B) { + JIT_EMIT_AND_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_AND_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_AND_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case EOR: if (size == SIZE_B) { + JIT_EMIT_EOR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_EOR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_EOR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case CMP: if (size == SIZE_L) { // CMPI takes less time in most cases + if (EA_MODE(opcode) != EA_DATA_REG) { + cycles -= 8; + } else { + cycles -= 2; + } + } else { + if (EA_MODE(opcode) != EA_DATA_REG) { + cycles -= 4; + } + } + if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); + } + break; + case SUB: if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); + } + break; + default: // case ADD + if (size == SIZE_B) { + JIT_EMIT_ADD_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ADD_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ADD_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); + } + break; + } + + /* Update the cycle counter (and PC) before writing the result, in case + * a change to SR triggers an interrupt */ + cycles += (size==SIZE_L ? 16 : 8); + cycles += (EA_MODE(opcode) == EA_DATA_REG ? 0 : 4); + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + advance_PC(state); + + /* Update the EA operand (if not CMPI) */ + if (aluop != CMP) { + if (use_SR) { + if (size == SIZE_B) { + JIT_EMIT_SET_CCR(current_entry); + } else { + JIT_EMIT_SET_SR(current_entry); + } + } else { + ea_set(state, opcode, size); + } + } + + /* All done */ + return 0; +} + +/*************************************************************************/ + +/** + * op_bit: Bit-twiddling instructions (formats 0000 rrr1 xxxx xxxx and + * 0000 1000 xxxx xxxx). + */ +static int op_bit(Q68State *state, uint32_t opcode) +{ + /* Check early for MOVEP (coded as BTST/BCHG/BCLR/BSET Dn,An) */ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + if (opcode & 0x0100) { + return opMOVP(state, opcode); + } else { + return op_ill(state, opcode); + } + } + + enum {BTST = 0, BCHG = 1, BCLR = 2, BSET = 3} op = opcode>>6 & 3; + int cycles; + + /* Get the bit number to operate on */ + if (opcode & 0x0100) { + /* Bit number in register */ + INSN_GET_REG; + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); + cycles = 0; + } else { + unsigned int bitnum = IFETCH(state); + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, bitnum); + cycles = 4; + } + + /* EA operand is 32 bits when coming from a register, 8 when from memory */ + int size = (EA_MODE(opcode)==EA_DATA_REG ? SIZE_L : SIZE_B); + int cycles_tmp; + ea_get(state, opcode, size, 1, &cycles_tmp, 2); + if (cycles_tmp < 0) { + return 1; + } + cycles += cycles_tmp; + if (size == SIZE_L && (op == BCLR || op == BTST)) { + cycles += 2; + } + + /* Perform the operation: first test the bit, then (for non-BTST cases) + * twiddle it as appropriate. All size-related checking is performed + * in BTST, so the remaining operations are unsized. */ + if (size == SIZE_B) { + JIT_EMIT_BTST_B(current_entry); + } else { // size == SIZE_L + JIT_EMIT_BTST_L(current_entry); + } + switch (op) { + default: break; // case BTST: nothing to do + case BCHG: JIT_EMIT_BCHG(current_entry); break; + case BCLR: JIT_EMIT_BCLR(current_entry); break; + case BSET: JIT_EMIT_BSET(current_entry); break; + } + + /* Update EA operand (but not for BTST) */ + if (op != BTST) { + ea_set(state, opcode, size); + } + + /* Update cycle counter; note that the times for BCHG.L, BCLR.L, and + * BSET.L are maximums (though how they vary is undocumented) */ + JIT_EMIT_ADD_CYCLES(current_entry, (op==BTST ? 4 : 8) + cycles); + + return 0; +} + +/*************************************************************************/ + +/** + * opMOVE: MOVE.[bwl] instruction (format {01,10,11}xx xxxx xxxx xxxx). + */ +static int opMOVE(Q68State *state, uint32_t opcode) +{ + const int size = (opcode>>12==1 ? SIZE_B : opcode>>12==2 ? SIZE_L : SIZE_W); + + int cycles_src; + ea_get(state, opcode, size, 0, &cycles_src, 1); + if (cycles_src < 0) { + return 1; + } + + /* Rearrange the opcode bits so we can pass the destination EA to + * ea_resolve() */ + const uint32_t dummy_opcode = (opcode>>9 & 7) | (opcode>>3 & 0x38); + int cycles_dest; + if (EA_MODE(dummy_opcode) <= EA_ADDRESS_REG) { + cycles_dest = 0; + } else { + cycles_dest = ea_resolve(state, dummy_opcode, size, ACCESS_WRITE); + if (cycles_dest < 0) { + return op_ill(state, opcode); + } + } + + /* Copy the operand to the result and set flags (if needed) */ + const int do_cc = cc_needed(state, opcode); + if (EA_MODE(dummy_opcode) == EA_ADDRESS_REG) { + if (size == SIZE_W) { + JIT_EMIT_EXT_L(current_entry); + } else { // size == SIZE_L + JIT_EMIT_MOVE_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_MOVE_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_MOVE_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_MOVE_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + } + + /* Update the destination EA and cycle count */ + ea_set(state, dummy_opcode, size); + JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles_src + cycles_dest); + + return 0; +} + +/*************************************************************************/ + +/** + * op4xxx: Miscellaneous instructions (format 0100 xxx0 xxxx xxxx). + */ +static int op4xxx(Q68State *state, uint32_t opcode) +{ + const unsigned int index = (opcode>>7 & 0x1C) | (opcode>>6 & 3); + return (*opcode_4xxx_table[index])(state, opcode); +} + +/*************************************************************************/ + +/** + * op_CHK: CHK instruction (format 0100 rrr1 10xx xxxx). + */ +static int op_CHK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); + + int cycles; + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + return op_ill(state, opcode); + } + ea_get(state, opcode, SIZE_W, 0, &cycles, 2); + if (cycles < 0) { + return 1; + } + + JIT_EMIT_ADD_CYCLES(current_entry, 10 + cycles); + /* The JIT code takes care of adding the extra 34 cycles of exception + * processing if necessary */ + JIT_EMIT_CHK_W(current_entry); + return 0; +} + +/*************************************************************************/ + +/** + * op_LEA: LEA instruction (format 0100 rrr1 11xx xxxx). + */ +static int op_LEA(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + + /* Register, predecrement, postincrement, immediate modes are illegal */ + if (EA_MODE(opcode) == EA_DATA_REG + || EA_MODE(opcode) == EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT + || EA_MODE(opcode) == EA_PREDECREMENT + || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) + ) { + return op_ill(state, opcode); + } + + int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles + cycles += 2; + } + + JIT_EMIT_LEA(current_entry, (8+reg)*4); + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opADSQ: ADDQ and SUBQ instructions (format 0101 iiix xxxx xxxx). + */ +static int opADSQ(Q68State *state, uint32_t opcode) +{ + const int is_sub = opcode & 0x0100; + INSN_GET_COUNT; + INSN_GET_SIZE; + if (EA_MODE(opcode) == EA_ADDRESS_REG && size == 1) { + size = 2; // ADDQ.W #imm,An is equivalent to ADDQ.L #imm,An + } + + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, count); + + int cycles; + ea_get(state, opcode, size, 1, &cycles, 2); + if (cycles < 0) { + return 1; + } + + const int do_cc = cc_needed(state, opcode); + if (is_sub) { + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + JIT_EMIT_SUB_L(current_entry); + } else { + if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); + } + } + } else { + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + JIT_EMIT_ADD_L(current_entry); + } else { + if (size == SIZE_B) { + JIT_EMIT_ADD_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ADD_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ADD_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); + } + } + } + + ea_set(state, opcode, size); + + cycles += (size==SIZE_L || EA_MODE(opcode) == EA_ADDRESS_REG ? 8 : 4); + cycles += (EA_MODE(opcode) >= EA_INDIRECT ? 4 : 0); + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + return 0; +} + +/*************************************************************************/ + +/** + * op_Scc: Scc instruction (format 0101 cccc 11xx xxxx). + */ +static int op_Scc(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { + /* DBcc Dn,disp is coded as Scc An with an extension word */ + return opDBcc(state, opcode); + } + + INSN_GET_COND; + /* From the cycle counts, it looks like this is a standard read/write + * access rather than a write-only access */ + int cycles; + if (EA_MODE(opcode) == EA_DATA_REG) { + cycles = 0; + } else { + cycles = ea_resolve(state, opcode, SIZE_B, ACCESS_MODIFY); + if (cycles < 0) { + return op_ill(state, opcode); + } + } + JIT_EMIT_TEST_cc(cond, current_entry); + JIT_EMIT_Scc(current_entry); + if (EA_MODE(opcode) == EA_DATA_REG) { + /* Scc Dn is a special case */ + JIT_EMIT_ADD_CYCLES_Scc_Dn(current_entry); + } else { + JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); + } + ea_set(state, opcode, SIZE_B); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_DBcc: DBcc instruction (format 0101 cccc 1100 1xxx). + */ +static int opDBcc(Q68State *state, uint32_t opcode) +{ + INSN_GET_COND; + INSN_GET_REG0; + INSN_GET_IMM16; + uint32_t target = state->current_PC + imm16; + int32_t offset = btcache_lookup(target); + JIT_EMIT_TEST_cc(cond, current_entry); + if (offset >= 0) { + JIT_EMIT_DBcc_native(current_entry, reg0*4, target, offset); + } else { + JIT_EMIT_DBcc(current_entry, reg0*4, target); + } + return 0; +} + +/*************************************************************************/ + +/** + * op_Bcc: Conditional branch instructions (format 0110 cccc dddd dddd). + */ +static int op_Bcc(Q68State *state, uint32_t opcode) +{ + INSN_GET_COND; + INSN_GET_DISP8; + int cycles = 0; + if (disp == 0) { + disp = (int16_t)IFETCH(state); + cycles = 4; + } + uint32_t target = state->current_PC + disp; + if (cond == COND_F) { + /* BF is really BSR */ +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); +#endif + JIT_EMIT_ADD_CYCLES(current_entry, 18); + advance_PC(state); + JIT_EMIT_BSR(current_entry, jit_PC, target); + return 0; + } else { + int32_t offset; +#ifdef Q68_OPTIMIZE_IDLE + /* FIXME: Temporary hack to improve PSP performance */ + if (target == 0x1066 + && ((cond == COND_EQ && state->current_PC - 2 == 0x001092) + || (cond == COND_PL && state->current_PC - 2 == 0x0010B4)) + ) { + /* BIOS intro animation */ + JIT_EMIT_ADD_CYCLES(current_entry, + 468); // Length of one loop when idle + } else if (target == 0x10BC + && ((cond == COND_PL && state->current_PC - 2 == 0x001122) + || (cond == COND_T && state->current_PC - 2 == 0x00116A)) + ) { + /* Azel: Panzer Dragoon RPG (JP) */ + JIT_EMIT_ADD_CYCLES(current_entry, + 178*4); // Assuming a cycle_limit of 768 + } +#endif + if (target < state->current_PC) { + offset = btcache_lookup(target); + } else { + offset = -1; // Forward jumps can't be in the cache + } + JIT_EMIT_TEST_cc(cond, current_entry); + if (offset >= 0) { + JIT_EMIT_Bcc_native(current_entry, target, offset); + } else { + int32_t branch_offset; + JIT_EMIT_Bcc(current_entry, target, &branch_offset); + if (target >= state->current_PC && branch_offset >= 0) { + record_unresolved_branch(target, branch_offset); + } + } + JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); + return 0; + } +} + +/*************************************************************************/ + +/** + * opMOVQ: MOVEQ instruction (format 0111 rrr0 iiii iiii). + */ +static int opMOVQ(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_IMM8; + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, imm8); + const int do_cc = cc_needed(state, opcode); + JIT_EMIT_MOVE_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; +} + +/*************************************************************************/ + +/** + * op_alu: Non-immediate ALU instructions (format 1ooo rrrx xxxx xxxx for + * ooo = 000, 001, 011, 100, 101). + */ +static int op_alu(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + + /* Pass off special and invalid instructions early */ + if (size != 3) { + if ((opcode & 0xB130) == 0x9100) { + /* ADDX/SUBX are coded as ADD/SUB.* Dn, */ + return opADSX(state, opcode); + } + if ((opcode & 0xB1F0) == 0x8100) { + /* ABCD/SBCD are coded as AND/OR.b Dn, */ + return op_BCD(state, opcode); + } + if ((opcode & 0xF130) == 0xC100) { + /* EXG is coded as AND.[wl] Dn, */ + return op_EXG(state, opcode); + } + if ((opcode & 0xF130) == 0x8100) { + /* OR.[wl] Dn, is invalid on the 68000 (later PACK/UNPK) */ + return op_ill(state, opcode); + } + if ((opcode & 0xF138) == 0xB108 && (opcode>>6 & 3) != 3) { + /* CMPM is coded as EOR.* Dn, */ + return opCMPM(state, opcode); + } + } + + int ea_dest = opcode & 0x100; + int areg_dest = 0; // For ADDA/SUBA/CMPA + enum {OR, AND, EOR, CMP, SUB, ADD} aluop; + + /* Find the instruction for the opcode group */ + switch (opcode>>12) { + case 0x8: aluop = OR; break; + case 0x9: aluop = SUB; break; + case 0xB: aluop = (((opcode>>6)+1) & 7) <= 4 ? CMP : EOR; break; + case 0xC: aluop = AND; break; + default: aluop = ADD; break; // case 0xD + } + + /* Handle the special formats of ADDA/SUBA/CMPA */ + if ((aluop == ADD || aluop == SUB || aluop == CMP) && size == 3) { + size = ea_dest ? SIZE_L : SIZE_W; + ea_dest = 0; + areg_dest = 1; + } + + /* Retrieve the register and EA values; make sure to load operand 1 + * first, since operand 2 may be destroyed by memory operations */ + int cycles; + if (ea_dest) { + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); + ea_get(state, opcode, size, ea_dest, &cycles, 2); + } else { + ea_get(state, opcode, size, ea_dest, &cycles, 1); + if (areg_dest) { + JIT_EMIT_GET_OP2_REGISTER(current_entry, (8+reg)*4); + } else { + JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); + } + } + if (cycles < 0) { + return 1; + } + if (size == SIZE_L || areg_dest) { + cycles += 4; + } + if (ea_dest) { + cycles += 4; + } else if ((aluop == CMP && areg_dest) + || (size == SIZE_L + && (EA_MODE(opcode) <= EA_ADDRESS_REG + || (EA_MODE(opcode) == EA_MISC + && EA_REG(opcode) == EA_MISC_IMMEDIATE)))) { + cycles -= 2; + } + + /* Perform the actual computation */ + const int do_cc = cc_needed(state, opcode); + switch (aluop) { + case OR: if (size == SIZE_B) { + JIT_EMIT_OR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_OR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_OR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case AND: if (size == SIZE_B) { + JIT_EMIT_AND_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_AND_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_AND_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case EOR: if (size == SIZE_B) { + JIT_EMIT_EOR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_EOR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_EOR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case CMP: if (areg_dest && size == SIZE_W) { + JIT_EMIT_SUBA_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); + } else if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); + } + break; + case SUB: if (areg_dest && size == SIZE_W) { + JIT_EMIT_SUBA_W(current_entry); + } else if (areg_dest && size == SIZE_L) { + JIT_EMIT_SUB_L(current_entry); + } else if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); + } + break; + default: // case ADD + if (areg_dest && size == SIZE_W) { + JIT_EMIT_ADDA_W(current_entry); + } else if (areg_dest && size == SIZE_L) { + JIT_EMIT_ADD_L(current_entry); + } else if (size == SIZE_B) { + JIT_EMIT_ADD_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ADD_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ADD_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); + } + break; + } // switch (aluop) + + /* Store the result in the proper place (if the instruction is not CMP) */ + if (aluop != CMP) { + if (ea_dest) { + ea_set(state, opcode, size); + } else if (areg_dest) { + JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg)*4); + } else if (size == SIZE_B) { + JIT_EMIT_SET_REGISTER_B(current_entry, reg*4); + } else if (size == SIZE_W) { + JIT_EMIT_SET_REGISTER_W(current_entry, reg*4); + } else { // size == SIZE_L + JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); + } + } + + JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * op_DIV: DIVU and DIVS instructions (format 1000 rrrx 11xx xxxx). + */ +static int op_DIV(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + const int sign = opcode & (1<<8); + + int cycles; + ea_get(state, opcode, SIZE_W, 0, &cycles, 1); + if (cycles < 0) { + return 1; + } + JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); + /* Add the EA cycles now, in case a divide-by-zero exception occurs */ + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + + if (sign) { + JIT_EMIT_DIVS_W(current_entry); + } else { + JIT_EMIT_DIVU_W(current_entry); + } + JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); + + /* The 68000 docs say that the timing difference between best and + * worst cases is less than 10%, so we just return the worst case */ + JIT_EMIT_ADD_CYCLES(current_entry, sign ? 158 : 140); + return 0; +} + +/*************************************************************************/ + +/** + * opAxxx: $Axxx illegal instruction set (format 1010 xxxx xxxx xxxx). + */ +static int opAxxx(Q68State *state, uint32_t opcode) +{ + return raise_exception(state, EX_LINE_1010); +} + +/*************************************************************************/ + +/** + * op_MUL: MULU and MULS instructions (format 1100 rrrx 11xx xxxx). + */ +static int op_MUL(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + const int sign = opcode & (1<<8); + + int cycles; + ea_get(state, opcode, SIZE_W, 0, &cycles, 1); + if (cycles < 0) { + return 1; + } + JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); + + const int do_cc = cc_needed(state, opcode); + if (sign) { + JIT_EMIT_MULS_W(current_entry); + } else { + JIT_EMIT_MULU_W(current_entry); + } + /* 16*16 -> 32 multiplication can't produce carry or overflow, so we + * can treat it like a logical operation for setting condition codes */ + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); + + JIT_EMIT_ADD_CYCLES(current_entry, 54 + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opshft: Shift and rotate instructions (format 1110 xxxx xxxx xxxx). + */ +static int opshft(Q68State *state, uint32_t opcode) +{ + const int is_left = opcode & 0x0100; + INSN_GET_SIZE; + INSN_GET_COUNT; + INSN_GET_REG0; + int is_memory; + int type; // Shift/rotate type (0=ASL/ASR, 1=LSL/LSR, ...) + int cycles; + + if (size == 3) { + /* Memory shift/rotate */ + is_memory = 1; + if ((opcode & 0x0800) || EA_MODE(opcode) <= EA_ADDRESS_REG) { + return op_ill(state, opcode); + } + size = SIZE_W; + type = opcode>>9 & 3; + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, 1); + ea_get(state, opcode, size, 1, &cycles, 2); + if (cycles < 0) { + return 1; + } + } else { + /* Register shift/rotate */ + is_memory = 0; + type = opcode>>3 & 3; + if (opcode & 0x0020) { + INSN_GET_REG; + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); + } else { + JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, count); + } + JIT_EMIT_GET_OP2_REGISTER(current_entry, reg0*4); + cycles = 0; + } + + switch (type) { + case 0: // ASL/ASR + if (is_left) { + if (size == SIZE_B) { + JIT_EMIT_ASL_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ASL_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ASL_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_ASR_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ASR_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ASR_L(current_entry); + } + } + break; + case 1: // LSL/LSR + if (is_left) { + if (size == SIZE_B) { + JIT_EMIT_LSL_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_LSL_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_LSL_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_LSR_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_LSR_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_LSR_L(current_entry); + } + } + break; + case 2: // ROXL/ROXR + if (is_left) { + if (size == SIZE_B) { + JIT_EMIT_ROXL_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ROXL_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ROXL_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_ROXR_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ROXR_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ROXR_L(current_entry); + } + } + break; + case 3: // ROL/ROR + if (is_left) { + if (size == SIZE_B) { + JIT_EMIT_ROL_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ROL_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ROL_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_ROR_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ROR_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ROR_L(current_entry); + } + } + break; + } // switch (type) + + if (is_memory) { + ea_set(state, opcode, size); + } else if (size == SIZE_B) { + JIT_EMIT_SET_REGISTER_B(current_entry, reg0*4); + } else if (size == SIZE_W) { + JIT_EMIT_SET_REGISTER_W(current_entry, reg0*4); + } else { // size == SIZE_L + JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); + } + + /* Cycles based on count are added in the shift/rotate processing */ + JIT_EMIT_ADD_CYCLES(current_entry, (size==SIZE_L ? 8 : 6) + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opFxxx: $Fxxx illegal instruction set (format 1111 xxxx xxxx xxxx). + */ +static int opFxxx(Q68State *state, uint32_t opcode) +{ + return raise_exception(state, EX_LINE_1111); +} + +/*************************************************************************/ +/*********************** $4xxx group instructions ************************/ +/*************************************************************************/ + +/** + * op4alu: Single-operand ALU instructions in the $4xxx opcode range + * (format 0100 ooo0 ssxx xxxx for ooo = 000, 001, 010, 011, 101). + */ +static int op4alu(Q68State *state, uint32_t opcode) +{ + INSN_GET_SIZE; + enum {NEGX = 0, CLR = 1, NEG = 2, NOT = 3, TST = 5} aluop; + aluop = opcode>>9 & 7; + + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + /* Retrieve the EA value */ + int cycles; + ea_get(state, opcode, size, 1, &cycles, 1); + if (cycles < 0) { + return 1; + } + if (aluop != TST) { + if (EA_MODE(opcode) == EA_DATA_REG) { + if (size == SIZE_L) { + cycles += 2; + } + } else { + cycles += (size == SIZE_L) ? 8 : 4; + } + } + + /* Perform the actual computation */ + /* For simplicity, use the 2-argument operations with 0 or ~0 as the + * second operand: + * -n = 0 - n + * 0 = 0 & n + * ~n = ~0 ^ n + * n = 0 | n + */ + JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, aluop==NOT ? ~(uint32_t)0 : 0); + const int do_cc = cc_needed(state, opcode); + switch (aluop) { + case NEGX:if (size == SIZE_B) { + JIT_EMIT_SUBX_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUBX_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUBX_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_L(current_entry); + } + break; + case NEG: if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); + } + break; + case CLR: if (size == SIZE_B) { + JIT_EMIT_AND_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_AND_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_AND_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + case NOT: if (size == SIZE_B) { + JIT_EMIT_EOR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_EOR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_EOR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + default: // case TST + if (size == SIZE_B) { + JIT_EMIT_OR_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_OR_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_OR_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + } + break; + } // switch (aluop) + + /* Store the result in the proper place (if the instruction is not TST) */ + if (aluop != TST) { + ea_set(state, opcode, size); + } + + JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opMVSR: MOVE to/from SR/CCR instructions (format 0100 0xx0 11xx xxxx). + */ +static int opMVSR(Q68State *state, uint32_t opcode) +{ + int is_CCR; + int ea_dest; + int cycles; + switch (opcode>>9 & 3) { + case 0: // MOVE SR, + is_CCR = 0; + ea_dest = 1; + cycles = (EA_MODE(opcode) == EA_DATA_REG) ? 6 : 8; + break; + case 1: // Undefined (MOVE CCR, on 68010) + return op_ill(state, opcode); + case 2: // MOVE ,CCR + is_CCR = 1; + ea_dest = 0; + cycles = 12; + break; + default: // MOVE ,SR (case 3) + JIT_EMIT_CHECK_SUPER(current_entry); + is_CCR = 0; + ea_dest = 0; + cycles = 12; + break; + } + + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + /* Motorola docs say the address is read before being written, even + * for the SR, format; also, the access size is a word even for + * CCR operations. */ + int cycles_tmp; + ea_get(state, opcode, SIZE_W, ea_dest, &cycles_tmp, 1); + if (cycles_tmp < 0) { + return 1; + } + cycles += cycles_tmp; + + /* Update the cycle counter (and PC) before writing the result, in case + * a change to SR triggers an interrupt */ + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + advance_PC(state); + + if (ea_dest) { + if (is_CCR) { + JIT_EMIT_GET_OP1_CCR(current_entry); + } else { + JIT_EMIT_GET_OP1_SR(current_entry); + } + JIT_EMIT_MOVE_W(current_entry); + ea_set(state, opcode, SIZE_W); + } else { + JIT_EMIT_MOVE_W(current_entry); + /* No need to set condition codes--we're about to overwrite them */ + if (is_CCR) { + JIT_EMIT_SET_CCR(current_entry); + } else { + JIT_EMIT_SET_SR(current_entry); + } + } + + return 0; +} + +/*************************************************************************/ + +/** + * opNBCD: NBCD instruction (format 0100 1000 00xx xxxx). + */ +static int opNBCD(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + int cycles; + ea_get(state, opcode, SIZE_B, 1, &cycles, 1); + if (cycles < 0) { + return 1; + } + + /* Treat it as something like SBCD ,#0 for simplicity */ + JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, 0); + JIT_EMIT_SBCD(current_entry); + + ea_set(state, opcode, SIZE_B); + JIT_EMIT_ADD_CYCLES(current_entry, + (EA_MODE(opcode) == EA_DATA_REG ? 6 : 8) + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * op_PEA: PEA instruction (format 0100 1000 01xx xxxx). + */ +static int op_PEA(Q68State *state, uint32_t opcode) +{ + /* SWAP is coded as PEA Dn */ + if (EA_MODE(opcode) == EA_DATA_REG) { + return opSWAP(state, opcode); + } + + if (EA_MODE(opcode) == EA_DATA_REG + || EA_MODE(opcode) == EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT + || EA_MODE(opcode) == EA_PREDECREMENT + || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) + ) { + return op_ill(state, opcode); + } + + int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles + cycles += 2; + } + +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); +#endif + JIT_EMIT_PEA(current_entry); + JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opSWAP: SWAP instruction (format 0100 1000 0100 0rrr). + */ +static int opSWAP(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg0*4); + const int do_cc = cc_needed(state, opcode); + JIT_EMIT_SWAP(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; +} + +/*************************************************************************/ + +/** + * op_TAS: TAS instruction (format 0100 1010 11xx xxxx). Also covers the + * ILLEGAL instruction (format 0100 1010 1111 1100). + */ +static int op_TAS(Q68State *state, uint32_t opcode) +{ + if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed + return op_ill(state, opcode); + } + + int cycles; + ea_get(state, opcode, SIZE_B, 1, &cycles, 1); + if (cycles < 0) { + /* Note that the ILLEGAL instruction is coded as TAS #imm, so it + * will be rejected as unwriteable by ea_get() */ + return 1; + } + JIT_EMIT_TAS(current_entry); + ea_set(state, opcode, SIZE_B); + JIT_EMIT_ADD_CYCLES(current_entry, + (EA_MODE(opcode) == EA_DATA_REG ? 4 : 10) + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * op_EXT: EXT instruction (format 0100 1000 1s00 0rrr). + */ +static int op_EXT(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + JIT_EMIT_GET_OP1_REGISTER(current_entry, reg0*4); + const int do_cc = cc_needed(state, opcode); + if (opcode & 0x0040) { + JIT_EMIT_EXT_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); + } else { + JIT_EMIT_EXT_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); + JIT_EMIT_SET_REGISTER_W(current_entry, reg0*4); + } + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; +} + +/*************************************************************************/ + +/** + * op_STM: MOVEM reglist, (i.e. STore Multiple) instruction (format + * 0100 1000 1sxx xxxx). + */ +static int op_STM(Q68State *state, uint32_t opcode) +{ + /* EXT.* is coded as MOVEM.* reglist,Dn */ + if (EA_MODE(opcode) == EA_DATA_REG) { + return op_EXT(state, opcode); + } + + unsigned int regmask = IFETCH(state); + int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; + if (EA_MODE(opcode) <= EA_ADDRESS_REG + || EA_MODE(opcode) == EA_POSTINCREMENT // Not allowed for store + ) { + return op_ill(state, opcode); + } + + /* Avoid modifying the register during address resolution */ + uint16_t safe_ea; + if (EA_MODE(opcode) == EA_PREDECREMENT) { + safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); + } else { + safe_ea = opcode; + } + int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_WRITE); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (regmask != 0) { // FIXME: does a real 68000 choke even if regmask==0? +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); +#endif + } + + if (EA_MODE(opcode) == EA_PREDECREMENT) { + /* Register order is reversed in predecrement mode */ + int reg; + for (reg = 15; reg >= 0; reg--, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + JIT_EMIT_STORE_DEC_W(current_entry, reg*4); + cycles += 4; + } else { + JIT_EMIT_STORE_DEC_L(current_entry, reg*4); + cycles += 8; + } + } + } + JIT_EMIT_MOVEM_WRITEBACK(current_entry, (8 + EA_REG(opcode)) * 4); + } else { + int reg; + for (reg = 0; reg < 16; reg++, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + JIT_EMIT_STORE_INC_W(current_entry, reg*4); + cycles += 4; + } else { + JIT_EMIT_STORE_INC_L(current_entry, reg*4); + cycles += 8; + } + } + } + } + + JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_LDM: MOVEM ,reglist (i.e. LoaD Multiple) instruction (format + * 0100 1100 1sxx xxxx). + */ +static int op_LDM(Q68State *state, uint32_t opcode) +{ + unsigned int regmask = IFETCH(state); + int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; + if (EA_MODE(opcode) <= EA_ADDRESS_REG + || EA_MODE(opcode) == EA_PREDECREMENT // Not allowed for load + ) { + return op_ill(state, opcode); + } + + /* Avoid modifying the register during address resolution */ + uint16_t safe_ea; + if (EA_MODE(opcode) == EA_POSTINCREMENT) { + safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); + } else { + safe_ea = opcode; + } + int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_READ); + if (cycles < 0) { + return op_ill(state, opcode); + } + if (regmask != 0) { // FIXME: does a real 68000 choke even if regmask==0? +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_EA(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); +#endif + } + + int reg; + for (reg = 0; reg < 16; reg++, regmask >>= 1) { + if (regmask & 1) { + if (size == SIZE_W) { + if (reg < 8) { + JIT_EMIT_LOAD_INC_W(current_entry, reg*4); + } else { + JIT_EMIT_LOADA_INC_W(current_entry, reg*4); + } + cycles += 4; + } else { + JIT_EMIT_LOAD_INC_L(current_entry, reg*4); + cycles += 8; + } + } + } + if (EA_MODE(opcode) == EA_POSTINCREMENT) { + JIT_EMIT_MOVEM_WRITEBACK(current_entry, (8 + EA_REG(opcode)) * 4); + } + + JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); + return 0; +} + +/*************************************************************************/ + +/** + * opmisc: $4xxx-group misc. instructions (format 0100 1110 01xx xxxx). + */ +static int opmisc(Q68State *state, uint32_t opcode) +{ + const unsigned int index = (opcode>>3 & 7); + return (*opcode_4E4x_table[index])(state, opcode); +} + +/*-----------------------------------------------------------------------*/ + +/** + * opTRAP: TRAP #n instruction (format 0100 1110 0100 nnnn). + */ +static int opTRAP(Q68State *state, uint32_t opcode) +{ + return raise_exception(state, EX_TRAP + (opcode & 0x000F)); +} + +/*-----------------------------------------------------------------------*/ + +/** + * opLINK: LINK instruction (format 0100 1110 0101 0rrr). + */ +static int opLINK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + int16_t disp = IFETCH(state); +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); +#endif + JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+reg0)*4); + JIT_EMIT_PUSH_L(current_entry); + JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+7)*4); + JIT_EMIT_MOVE_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg0)*4); + JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, disp); + JIT_EMIT_ADD_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, (8+7)*4); + JIT_EMIT_ADD_CYCLES(current_entry, 16); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opUNLK: UNLK instruction (format 0100 1110 0101 1rrr). + */ +static int opUNLK(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG0; + JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+reg0)*4); + JIT_EMIT_MOVE_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, (8+7)*4); +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); +#endif + JIT_EMIT_POP_L(current_entry); + JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg0)*4); + JIT_EMIT_ADD_CYCLES(current_entry, 12); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opMUSP: MOVE An,USP and MOVE USP,An instructions (format + * 0100 1110 0110 xrrr). + */ +static int opMUSP(Q68State *state, uint32_t opcode) +{ + JIT_EMIT_CHECK_SUPER(current_entry); + INSN_GET_REG0; + if (opcode & 0x0008) { + JIT_EMIT_MOVE_TO_USP(current_entry, reg0*4); + } else { + JIT_EMIT_MOVE_FROM_USP(current_entry, reg0*4); + } + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op4E7x: Instructions with opcodes $4E70-$4E77 that don't fit anywhere + * else. + */ +static int op4E7x(Q68State *state, uint32_t opcode) +{ + switch (opcode & 7) { + case 0: // $4E70 RESET + JIT_EMIT_CHECK_SUPER(current_entry); + JIT_EMIT_ADD_CYCLES(current_entry, 132); + return 0; + case 1: // $4E71 NOP + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; + case 2: // $4E72 STOP + JIT_EMIT_CHECK_SUPER(current_entry); + JIT_EMIT_ADD_CYCLES(current_entry, 4); + advance_PC(state); + JIT_EMIT_STOP(current_entry, IFETCH(state)); + return 1; + case 3: { // $4E73 RTE + JIT_EMIT_CHECK_SUPER(current_entry); +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); +#endif + JIT_EMIT_ADD_CYCLES(current_entry, 20); + JIT_EMIT_RTE(current_entry); + PC_updated = 1; + return 1; + } + case 5: // $4E75 RTS +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); +#endif + JIT_EMIT_ADD_CYCLES(current_entry, 16); + JIT_EMIT_RTS(current_entry); + PC_updated = 1; + return 1; + case 6: // $4E76 TRAPV + JIT_EMIT_TRAPV(current_entry); + JIT_EMIT_ADD_CYCLES(current_entry, 4); + return 0; + case 7: { // $4E77 RTR +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); +#endif + JIT_EMIT_ADD_CYCLES(current_entry, 20); + JIT_EMIT_RTR(current_entry); + PC_updated = 1; + return 1; + } + default: // $4E74 RTD is 68010 only + return op_ill(state, opcode); + } +} + +/*************************************************************************/ + +/** + * opjump: JSR and JMP instructions (format 0100 1110 1xxx xxxx). + */ +static int opjump(Q68State *state, uint32_t opcode) +{ + int is_jsr = ~opcode & 0x0040; + + /* JMP is essentially identical to LEA PC, and has the same + * constraints. JSR is equivalent to MOVE.L PC,-(A7) followed by a + * JMP to the address. Both use a separate timing table, however. */ + + int cycles; + switch (EA_MODE(opcode)) { + case EA_INDIRECT: + cycles = 8; + break; + case EA_DISPLACEMENT: + cycles = 10; + break; + case EA_INDEX: + cycles = 14; + break; + case EA_MISC: + switch (EA_REG(opcode)) { + case EA_MISC_ABSOLUTE_W: + cycles = 10; + break; + case EA_MISC_ABSOLUTE_L: + cycles = 12; + break; + case EA_MISC_PCREL: + cycles = 10; + break; + case EA_MISC_PCREL_INDEX: + cycles = 14; + break; + default: + return op_ill(state, opcode); + } + break; + default: + return op_ill(state, opcode); + } + if (is_jsr) { + cycles += 8; + } + JIT_EMIT_ADD_CYCLES(current_entry, cycles); + advance_PC(state); + + ea_resolve(state, opcode, SIZE_W, ACCESS_READ); // cannot fail + if (is_jsr) { +#ifndef Q68_DISABLE_ADDRESS_ERROR + JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, + FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); +#endif + JIT_EMIT_JSR(current_entry, jit_PC); + return 0; + } else { + JIT_EMIT_JMP(current_entry); + return 1; + } +} + +/*************************************************************************/ +/******************* Other miscellaneous instructions ********************/ +/*************************************************************************/ + +/** + * opMOVP: MOVEP instruction (0000 rrr1 xx00 1rrr). + */ +static int opMOVP(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + int to_memory = opcode & 0x0080; + int is_long = opcode & 0x0040; + int16_t disp = IFETCH(state); + + if (to_memory) { + if (is_long) { + JIT_EMIT_MOVEP_WRITE_L(current_entry, reg0*4, disp, reg*4); + } else { + JIT_EMIT_MOVEP_WRITE_W(current_entry, reg0*4, disp, reg*4); + } + } else { + if (is_long) { + JIT_EMIT_MOVEP_READ_L(current_entry, reg0*4, disp, reg*4); + } else { + JIT_EMIT_MOVEP_READ_W(current_entry, reg0*4, disp, reg*4); + } + } + + JIT_EMIT_ADD_CYCLES(current_entry, is_long ? 24 : 16); + return 0; +} + +/*************************************************************************/ + +/** + * opADSX: ADDX/SUBX instructions (1x01 rrr1 ss00 xrrr). + */ +static int opADSX(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + INSN_GET_REG0; + const int is_add = opcode & 0x4000; + const int is_memory = opcode & 0x0008; + + const uint16_t src_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; + const uint16_t dest_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; + int dummy; + ea_get(state, src_ea, size, 0, &dummy, 1); + ea_get(state, dest_ea, size, 1, &dummy, 2); + + const int do_cc = cc_needed(state, opcode); + if (is_add) { + if (size == SIZE_B) { + JIT_EMIT_ADDX_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADDX_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_ADDX_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADDX_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_ADDX_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_ADDX_L(current_entry); + } + } else { + if (size == SIZE_B) { + JIT_EMIT_SUBX_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUBX_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUBX_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_SUBX_L(current_entry); + } + } + + ea_set(state, dest_ea, size); + JIT_EMIT_ADD_CYCLES(current_entry, (is_memory ? (size==SIZE_L ? 30 : 18) + : (size==SIZE_L ? 8 : 4))); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * op_BCD: ABCD/SBCD instructions (1x00 rrr1 0000 xrrr). + */ +static int op_BCD(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + const int is_add = opcode & 0x4000; + const int is_memory = opcode & 0x0008; + + const uint16_t src_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; + const uint16_t dest_ea = + (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; + int dummy; + ea_get(state, src_ea, SIZE_B, 0, &dummy, 1); + ea_get(state, dest_ea, SIZE_B, 1, &dummy, 2); + + if (is_add) { + JIT_EMIT_ABCD(current_entry); + } else { + JIT_EMIT_SBCD(current_entry); + } + + ea_set(state, dest_ea, SIZE_B); + JIT_EMIT_ADD_CYCLES(current_entry, is_memory ? 18 : 6); + return 0; +} + +/*-----------------------------------------------------------------------*/ + +/** + * opCMPM: CMPM instructions (1011 rrr1 ss00 1rrr). + */ +static int opCMPM(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_SIZE; + INSN_GET_REG0; + + const uint16_t src_ea = EA_POSTINCREMENT<<3 | reg0; + const uint16_t dest_ea = EA_POSTINCREMENT<<3 | reg; + int dummy; + ea_get(state, src_ea, size, 0, &dummy, 1); + ea_get(state, dest_ea, size, 0, &dummy, 2); + + const int do_cc = cc_needed(state, opcode); // Just for consistency + if (size == SIZE_B) { + JIT_EMIT_SUB_B(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); + } else if (size == SIZE_W) { + JIT_EMIT_SUB_W(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); + } else { // size == SIZE_L + JIT_EMIT_SUB_L(current_entry); + if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); + } + + JIT_EMIT_ADD_CYCLES(current_entry, SIZE_L ? 20 : 12); + return 0; +} + +/*************************************************************************/ + +/** + * op_EXG: EXG instruction (1100 rrr1 xx00 1rrr). + */ +static int op_EXG(Q68State *state, uint32_t opcode) +{ + INSN_GET_REG; + INSN_GET_REG0; + const int mode = opcode & 0xF8; + + if (mode == 0x40) { + JIT_EMIT_EXG(current_entry, reg*4, reg0*4); + } else if (mode == 0x48) { + JIT_EMIT_EXG(current_entry, (8+reg)*4, (8+reg0)*4); + } else if (mode == 0x88) { + JIT_EMIT_EXG(current_entry, reg*4, (8+reg0)*4); + } else { + return op_ill(state, opcode); + } + JIT_EMIT_ADD_CYCLES(current_entry, 6); + return 0; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68-jit.h b/yabause/src/q68/q68-jit.h new file mode 100644 index 0000000000..e57885bf3f --- /dev/null +++ b/yabause/src/q68/q68-jit.h @@ -0,0 +1,81 @@ +/* src/q68/q68-jit.h: Dynamic translation header for Q68 + Copyright 2009 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_JIT_H +#define Q68_JIT_H + +/*************************************************************************/ + +/* Info structure for translated code blocks */ +struct Q68JitEntry_ { + Q68JitEntry *next, *prev; // Hash table collision chain pointers + Q68State *state; // Associated processor state block + uint32_t m68k_start; // Code start address in 68000 address space + // (zero indicates a free entry) + uint32_t m68k_end; // Code end address in 68000 address space + OpcodeFunc *native_code; // Pointer to native code + uint32_t native_length; // Length of native code (bytes) + uint32_t native_size; // Size of native code buffer (bytes) + void *exec_address; // Next execution address (NULL if not started) + uint32_t timestamp; // Time this entry was added + uint8_t running; // Nonzero if entry is currently running + uint8_t must_clear; // Nonzero if entry must be cleared on completion +}; + +/* Hash function */ +#define JIT_HASH(addr) ((uint32_t)(addr) % Q68_JIT_TABLE_SIZE) + +/*************************************************************************/ + +/** + * TIMESTAMP_COMPARE: Compare two timestamps. + * + * [Parameters] + * a, b: Timestamps to compare + * reference: Reference timestamp by which the comparison is made + * [Return value] + * -1 if a < b (i.e. "a is older than b") + * 0 if a == b + * 1 if a > b + */ +#ifdef __GNUC__ +__attribute__((const)) +#endif +static inline int TIMESTAMP_COMPARE(uint32_t reference, uint32_t a, uint32_t b) +{ + const uint32_t age_a = reference - a; + const uint32_t age_b = reference - b; + return age_a > age_b ? -1 : + age_a < age_b ? 1 : 0; +} + +/*************************************************************************/ + +#endif // Q68_JIT_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68.c b/yabause/src/q68/q68.c new file mode 100644 index 0000000000..53125b8160 --- /dev/null +++ b/yabause/src/q68/q68.c @@ -0,0 +1,324 @@ +/* src/q68/q68.c: Quick-and-dirty MC68000 emulator with dynamic + translation support + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include "q68.h" +#include "q68-internal.h" + +/*************************************************************************/ + +/* + * The source code for Q68 is divided into the following files: + * + * q68.c (this file) -- Main interface function definitions + * q68.h -------------- Interface definition (users should include this header) + * q68-const.h -------- 68000-related constants (status register bits, etc.) + * q68-core.c --------- Processor execution core + * q68-disasm.c ------- 68000 instruction disassembly and tracing support + * (for debugging) + * q68-internal.h ----- General definitions and declarations for internal use + * q68-jit.c ---------- Dynamic ("just-in-time") translation support + * q68-jit.h ---------- Declarations used only by the JIT code + * q68-jit-psp.[hS] --- JIT implementation for the PSP's Allegrex processor + * q68-jit-x86.[hS] --- JIT implementation for the Intel x86 architecture + * (both 32-bit and 64-bit environments supported) + */ + +/*************************************************************************/ +/*************************************************************************/ + +/** + * q68_create: Create a new virtual processor. The virtual processor is + * created uninitialized; before starting the processor, the caller must + * set the IRQ level and read/write callbacks, then call q68_reset(). + * + * [Parameters] + * None + * [Return value] + * Processor state block on success, NULL on error + */ +Q68State *q68_create(void) +{ + return q68_create_ex(malloc, realloc, free); +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_create_ex: Create a new virtual processor, using the specified + * functions for all memory allocation. + * + * [Parameters] + * malloc_func: Function for allocating a memory block + * realloc_func: Function for adjusting the size of a memory block + * free_func: Function for freeing a memory block + * [Return value] + * Processor state block on success, NULL on error + */ +Q68State *q68_create_ex(void *(*malloc_func)(size_t size), + void *(*realloc_func)(void *ptr, size_t size), + void (*free_func)(void *ptr)) +{ + Q68State *state; + + state = (*malloc_func)(sizeof(*state)); + if (!state) { + return NULL; + } + state->malloc_func = malloc_func; + state->realloc_func = realloc_func; + state->free_func = free_func; + +#ifdef Q68_USE_JIT + if (!q68_jit_init(state)) { + state->free_func(state); + return NULL; + } +#endif + + state->halted = Q68_HALTED_DOUBLE_FAULT; // Let's initialize this, at least + return state; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_destroy: Free all resources used by a virtual processor. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +void q68_destroy(Q68State *state) +{ +#ifdef Q68_USE_JIT + q68_jit_cleanup(state); +#endif + state->free_func(state); +} + +/*************************************************************************/ + +/** + * q68_set_irq: Set the interrupt request (IRQ) input to the processor. + * + * [Parameters] + * state: Processor state block + * irq: IRQ level (0-7) + * [Return value] + * None + */ +void q68_set_irq(Q68State *state, int irq) +{ + state->irq = irq & 7; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_set_{readb,readw,writeb,writew}_func: Set the read/write callback + * functions called by the virtual processor on memory accesses. + * + * For the read functions, only the lower 8 (readb) or 16 (readw) bits of + * the return value are used; the function does not need to sign-extend or + * zero-extend the value. Similarly, the value passed to the write + * functions will only have the low 8 (writeb) or 16 (writew) bits valid, + * and the function should ignore the upper bits of the value. + * + * For the word access functions (readw and writew), the address is + * guaranteed to be even, so the function does not need to check for this + * itself. (However, see the Q68_DISABLE_ADDRESS_ERROR configuration + * option in q68-internal.h.) + * + * [Parameters] + * state: Processor state block + * func: Callback function to set + * [Return value] + * None + */ +void q68_set_readb_func(Q68State *state, Q68ReadFunc func) +{ + state->readb_func = func; +} + +void q68_set_readw_func(Q68State *state, Q68ReadFunc func) +{ + state->readw_func = func; +} + +void q68_set_writeb_func(Q68State *state, Q68WriteFunc func) +{ + state->writeb_func = func; +} + +void q68_set_writew_func(Q68State *state, Q68WriteFunc func) +{ + state->writew_func = func; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_set_jit_flush_func: Set a function to be used to flush the native + * CPU's caches after a block of 68k code has been translated into native + * code. If not set, no cache flushing is performed. This function has no + * effect if dynamic translation is not enabled. + * + * [Parameters] + * state: Processor state block + * flush_func: Function for flushing the native CPU's caches (NULL if none) + * [Return value] + * None + */ +void q68_set_jit_flush_func(Q68State *state, void (*flush_func)(void)) +{ + state->jit_flush = flush_func; +} + +/*************************************************************************/ + +/** + * q68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of the + * specified register. + * + * [Parameters] + * state: Processor state block + * num: Register number (q68_get_dreg() and q68_get_areg() only) + * [Return value] + * Register value + */ +uint32_t q68_get_dreg(const Q68State *state, int num) +{ + return state->D[num]; +} + +uint32_t q68_get_areg(const Q68State *state, int num) +{ + return state->A[num]; +} + +uint32_t q68_get_pc(const Q68State *state) +{ + return state->PC; +} + +uint16_t q68_get_sr(const Q68State *state) +{ + return state->SR; +} + +uint32_t q68_get_usp(const Q68State *state) +{ + return state->USP; +} + +uint32_t q68_get_ssp(const Q68State *state) +{ + return state->SSP; +} + +/*-----------------------------------------------------------------------*/ + +/** + * q68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified + * register. + * + * [Parameters] + * state: Processor state block + * num: Register number (q68_set_dreg() and q68_set_areg() only) + * value: Value to set + * [Return value] + * None + */ +void q68_set_dreg(Q68State *state, int num, uint32_t value) +{ + state->D[num] = value; +} + +void q68_set_areg(Q68State *state, int num, uint32_t value) +{ + state->A[num] = value; +} + +void q68_set_pc(Q68State *state, uint32_t value) +{ + state->PC = value; +} + +void q68_set_sr(Q68State *state, uint16_t value) +{ + state->SR = value; +} + +void q68_set_usp(Q68State *state, uint32_t value) +{ + state->USP = value; +} + +void q68_set_ssp(Q68State *state, uint32_t value) +{ + state->SSP = value; +} + +/*************************************************************************/ + +/** + * q68_touch_memory: Clear any cached translations covering the given + * address range. Users should call this function whenever 68000-accessible + * memory is modified by an external agent. + * + * [Parameters] + * state: Processor state block + * address: 68000 address of modified data + * size: Size of modified data (in bytes) + * [Return value] + * None + */ +void q68_touch_memory(Q68State *state, uint32_t address, uint32_t size) +{ +#ifdef Q68_USE_JIT + const uint32_t first_page = address >> Q68_JIT_PAGE_BITS; + const uint32_t last_page = (address + (size-1)) >> Q68_JIT_PAGE_BITS; + uint32_t page; + for (page = first_page; page <= last_page; page++) { + if (UNLIKELY(JIT_PAGE_TEST(state, page))) { + q68_jit_clear_page(state, page << Q68_JIT_PAGE_BITS); + } + } +#endif +} + +/*************************************************************************/ +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/q68/q68.h b/yabause/src/q68/q68.h new file mode 100644 index 0000000000..fa4af2c254 --- /dev/null +++ b/yabause/src/q68/q68.h @@ -0,0 +1,295 @@ +/* src/q68/q68.h: Q68 main header + Copyright 2009-2010 Andrew Church + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef Q68_H +#define Q68_H + +#include +#include + +/*************************************************************************/ +/****************** Exported definitions and data types ******************/ +/*************************************************************************/ + +/* Memory read/write function types. The size of the operation is not + * specified here, but is rather determined by which callback the function + * is assigned to. */ + +/** + * Q68ReadFunc: Read data from memory. + * + * [Parameters] + * address: Address to read from + * [Return value] + * Value read (zero-extended to 32 bits) + */ +typedef uint32_t Q68ReadFunc(uint32_t address); + +/** + * Q68WriteFunc: Write data to memory. + * + * [Parameters] + * address: Address to write to + * data: Value to write + * [Return value] + * None + */ +typedef void Q68WriteFunc(uint32_t address, uint32_t data); + +/*************************************************************************/ + +/* Virtual processor state (opaque) */ + +typedef struct Q68State_ Q68State; + +/*************************************************************************/ +/************************** Emulator interface ***************************/ +/*************************************************************************/ + +/** + * q68_create: Create a new virtual processor. The virtual processor is + * created uninitialized; before starting the processor, the caller must + * set the IRQ level and read/write callbacks, then call q68_reset(). + * + * [Parameters] + * None + * [Return value] + * Processor state block on success, NULL on error + */ +extern Q68State *q68_create(void); + +/** + * q68_create_ex: Create a new virtual processor, using the specified + * functions for all memory allocation. + * + * [Parameters] + * malloc_func: Function for allocating a memory block + * realloc_func: Function for adjusting the size of a memory block + * free_func: Function for freeing a memory block + * [Return value] + * Processor state block on success, NULL on error + */ +extern Q68State *q68_create_ex(void *(*malloc_func)(size_t size), + void *(*realloc_func)(void *ptr, size_t size), + void (*free_func)(void *ptr)); + +/** + * q68_destroy: Free all resources used by a virtual processor. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +extern void q68_destroy(Q68State *state); + +/*----------------------------------*/ + +/** + * q68_set_irq: Set the interrupt request (IRQ) input to the processor. + * + * [Parameters] + * state: Processor state block + * irq: IRQ level (0-7) + * [Return value] + * None + */ +extern void q68_set_irq(Q68State *state, int irq); + +/** + * q68_set_{readb,readw,writeb,writew}_func: Set the read/write callback + * functions called by the virtual processor on memory accesses. + * + * For the read functions, only the lower 8 (readb) or 16 (readw) bits of + * the return value are used; the function does not need to sign-extend or + * zero-extend the value. Similarly, the value passed to the write + * functions will only have the low 8 (writeb) or 16 (writew) bits valid, + * and the function should ignore the upper bits of the value. + * + * For the word access functions (readw and writew), the address is + * guaranteed to be even, so the function does not need to check for this + * itself. (However, see the Q68_DISABLE_ADDRESS_ERROR configuration + * option in q68-internal.h.) + * + * [Parameters] + * state: Processor state block + * func: Callback function to set + * [Return value] + * None + */ +extern void q68_set_readb_func(Q68State *state, Q68ReadFunc func); +extern void q68_set_readw_func(Q68State *state, Q68ReadFunc func); +extern void q68_set_writeb_func(Q68State *state, Q68WriteFunc func); +extern void q68_set_writew_func(Q68State *state, Q68WriteFunc func); + +/** + * q68_set_jit_flush_func: Set a function to be used to flush the native + * CPU's caches after a block of 68k code has been translated into native + * code. If not set, no cache flushing is performed. This function has no + * effect if dynamic translation is not enabled. + * + * [Parameters] + * state: Processor state block + * flush_func: Function for flushing the native CPU's caches (NULL if none) + * [Return value] + * None + */ +extern void q68_set_jit_flush_func(Q68State *state, void (*flush_func)(void)); + +/*----------------------------------*/ + +/** + * q68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of the + * specified register. + * + * [Parameters] + * state: Processor state block + * num: Register number (q68_get_dreg() and q68_get_areg() only) + * [Return value] + * Register value + */ +extern uint32_t q68_get_dreg(const Q68State *state, int num); +extern uint32_t q68_get_areg(const Q68State *state, int num); +extern uint32_t q68_get_pc(const Q68State *state); +extern uint16_t q68_get_sr(const Q68State *state); +extern uint32_t q68_get_usp(const Q68State *state); +extern uint32_t q68_get_ssp(const Q68State *state); + +/** + * q68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified + * register. + * + * [Parameters] + * state: Processor state block + * num: Register number (q68_set_dreg() and q68_set_areg() only) + * value: Value to set + * [Return value] + * None + */ +extern void q68_set_dreg(Q68State *state, int num, uint32_t value); +extern void q68_set_areg(Q68State *state, int num, uint32_t value); +extern void q68_set_pc(Q68State *state, uint32_t value); +extern void q68_set_sr(Q68State *state, uint16_t value); +extern void q68_set_usp(Q68State *state, uint32_t value); +extern void q68_set_ssp(Q68State *state, uint32_t value); + +/*----------------------------------*/ + +/** + * q68_touch_memory: Clear any cached translations covering the given + * address range. Users should call this function whenever 68000-accessible + * memory is modified by an external agent. + * + * [Parameters] + * state: Processor state block + * address: 68000 address of modified data + * size: Size of modified data (in bytes) + * [Return value] + * None + */ +extern void q68_touch_memory(Q68State *state, uint32_t address, uint32_t size); + +/*-----------------------------------------------------------------------*/ + +/** + * q68_reset: Reset the virtual processor. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +extern void q68_reset(Q68State *state); + +/** + * q68_run: Execute instructions for the given number of clock cycles. + * + * [Parameters] + * state: Processor state block + * cycles: Number of clock cycles to execute + * [Return value] + * Number of clock cycles executed (may be greater than "cycles") + */ +extern int q68_run(Q68State *state, int cycles); + +/*-----------------------------------------------------------------------*/ + +/** + * q68_disassemble: Disassemble the instruction at the given address. + * Returns "???" if the address or opcode is invalid. + * + * [Parameters] + * state: Processor state block + * address: Address of instruction to disassemble + * [Return value] + * String containined disassembled instruction + * [Notes] + * The returned string is only valid until the next call to this function. + */ +extern const char *q68_disassemble(Q68State *state, uint32_t address, + int *nwords_ret); + +/*----------------------------------*/ + +/** + * q68_trace_init: Initialize the tracing code. + * + * [Parameters] + * state: Processor state block + * [Return value] + * None + */ +extern void q68_trace_init(Q68State *state_); + +/** + * q68_trace_add_cycles: Add the given number of cycles to the global + * accumulator. + * + * [Parameters] + * cycles: Number of cycles to add + * [Return value] + * None + */ +extern void q68_trace_add_cycles(int32_t cycles); + +/** + * q68_trace: Output a trace for the instruction at the current PC. + * + * [Parameters] + * None + * [Return value] + * None + */ +extern void q68_trace(void); + +/*************************************************************************/ +/*************************************************************************/ + +#endif // Q68_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/yabause/src/qt/Arguments.cpp b/yabause/src/qt/Arguments.cpp new file mode 100644 index 0000000000..f92a757ea2 --- /dev/null +++ b/yabause/src/qt/Arguments.cpp @@ -0,0 +1,209 @@ +#include "Arguments.h" +#include "VolatileSettings.h" +#include "QtYabause.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Arguments +{ + + void autoframeskip(const QString& param); + void autoload(const QString& param); + void autostart(const QString& param); + void binary(const QString& param); + void bios(const QString& param); + void cdrom(const QString& param); + void fullscreen(const QString& param); + void help(const QString& param); + void iso(const QString& param); + void nobios(const QString& param); + void nosound(const QString& param); + void version(const QString& param); + + struct Option + { + const char * shortname; + const char * longname; + const char * parameter; + const char * description; + unsigned short priority; + void (*callback)(const QString& param); + }; + + static Option LAST_OPTION = { NULL, NULL, NULL, NULL, 0 }; + + static Option availableOptions[] = + { + { NULL, "--autoframeskip=", "0|1", "Enable or disable auto frame skipping / limiting.", 2, autoframeskip }, + { NULL, "--autoload=", "", "Automatically start emulation and load a save state.",1, autoload }, + { "-a", "--autostart", NULL, "Automatically start emulation.", 1, autostart }, + { NULL, "--binary=", "[:ADDRESS]", "Use a binary file.", 1, binary }, + { "-b", "--bios=", "", "Choose a bios file.", 3, bios }, + { "-c", "--cdrom=", "", "Choose the cdrom device.", 4, cdrom }, + { "-f", "--fullscreen", NULL, "Start the emulator in fullscreen.", 5, fullscreen }, + { "-h", "--help", NULL, "Show this help and exit.", 0, help }, + { "-i", "--iso=", "", "Choose a dump file.", 4, iso }, + { "-nb", "--no-bios", NULL, "Use the emulated bios", 3, nobios }, + { "-ns", "--no-sound", NULL, "Turns sound off.", 6, nosound }, + { "-v", "--version", NULL, "Show version and exit.", 0, version }, + LAST_OPTION + }; + + void parse() + { + QVector

JWv-wD=4c7PO1ykBFS6Ilhqo- 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