commit 23d795e60a4d3fc81b519d1cee5f795b1ab3065b Author: izzy2lost Date: Tue Dec 16 21:51:41 2025 -0500 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4b8425 --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Build output +bin/ +obj/ +bin64/ +obj64/ +build/ +out/ +dist/ +target/ +Debug/ +Release/ +x64/ +x86/ + +# Compiled artifacts +*.o +*.obj +*.lo +*.la +*.a +*.lib +*.so +*.dylib +*.dll +*.exe +*.exp +*.pdb +*.idb +*.ilk +*.ipdb +*.iobj +*.pch + +# Generated files +*.nv +Makefile +*.log +*.tmp +*.swp +*.swo + +# Archives +*.zip +*.tar +*.gz +*.bz2 +*.7z +*.rar + +# Android/Gradle +android/.gradle/ +android/.idea/ +android/local.properties +android/*.iml +android/**/build/ +android/**/.cxx/ + +# IDE/Editor +.idea/ +.vs/ +.vscode/ +*.suo +*.user +*.userosscache +*.sln.docstates +*.VC.db +*.VC.opendb + +# OS-generated +.DS_Store +Thumbs.db +desktop.ini diff --git a/Config/Games.xml b/Config/Games.xml new file mode 100644 index 0000000..882e2d2 --- /dev/null +++ b/Config/Games.xml @@ -0,0 +1,3372 @@ + + + + + Sega Bass Fishing + USA, Deluxe + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Deluxe + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Upright + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + Get Bass: Sega Bass Fishing + Japan, Standard + Sega + 1997 + + + Sega Model 3 + 1.0 + MPC106 + + + + + + + + + + + + + + + + + + + Daytona USA 2 - Battle on the Edge + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 24 + true + + + + + + + 0x29250e16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Daytona USA 2 - Power Edition + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + true + + + + + + + 0x29222CC4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dirt Devils + Export, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dirt Devils + USA, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Australia, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Dirt Devils + Export, Ver. G? + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Wheel + 0x16C311DB + true + + + + + + + 0x29290f17 + + + + + + + + + + + + + + Emergency Call Ambulance + Export + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + US location test? + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + USA + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + Emergency Call Ambulance + Japan + Sega + 1999 + + + Sega Model 3 + 2.1 + Wheel + + + + + + + + 0x2923aa91 + + + + + + + + + + + + + + + + + + Fighting Vipers 2 + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 24 + + + + + + + 0x29260e96 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fighting Vipers 2 + ? + Sega + 1998 + + + Sega Model 3 + 2.0 + 24 + + + + + + + 0x29260e96 + + + + + + + + + + + + + + Harley-Davidson and L.A. Riders + Export, Revision B + Sega + 1997 + + + Sega Model 3 + 2.0 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Harley-Davidson and L.A. Riders + Export, Revision A + Sega + 1997 + + + Sega Model 3 + 2.0 + true + + + + + + + + + + + + + + + + + + + + + L.A. Machineguns: Rage of the Machines + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + + 0x292A2BC5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Le Mans 24 + Japan, Revision B + Sega + 1997 + + + Sega Model 3 + 1.5 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Lost World + Japan, Revision A + Sega + 1997 + + + Sega Model 3 + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Lost World + + Location Test + Sega + 1997 + + + Sega Model 3 + 1.5 + + + + + + + + + + + + + + + + + + + + + + + Magical Truck Adventure + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + 0x29266e45 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magical Truck Adventure + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + 0x16C311DB + + + + + 0x29266e45 + + + + + + + + + + + + + + + + + + + + The Ocean Hunter + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.1 + + + + + + 0x292B6A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The Ocean Hunter + Japan + Sega + 1998 + + + Sega Model 3 + 2.1 + + + + + + 0x292B6A01 + + + + + + + + + + + + + + Scud Race + Export, Twin/DX + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Australia, Twin/DX + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Export, Deluxe, Revision A + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race + Export, Deluxe + Sega + 1996 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race Plus + Export, Twin/DX, Revision A + Sega + 1997 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scud Race Plus + Export, Twin/DX + Sega + 1997 + + + Sega Model 3 + 1.5 + DSB1 + Wheel + MPC106 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ski Champ + Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Ski + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spikeout Final Edition + Export + Sega + 1999 + + + Sega Model 3 + 2.1 + DSB2 + true + + + + + + 0x29236fc8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spikeout + Export, Revision C + Sega + 1998 + + + Sega Model 3 + 2.1 + DSB2 + true + + + + + + 0x292f2b04 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Export + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Prototype + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Prototype Version A + Sega + 1998 + + + Sega Model 3 + 2.0 + DSB2 + Wheel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sega Rally 2 + Export, Deluxe + Sega + 1998 + + + + Sega Model 3 + 2.0 + DSB2 + Wheel + 48 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Export, Revision A + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Export + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Star Wars Trilogy Arcade + Location Test, 16.09.98 + Sega, LucasArts + 1998 + + + Sega Model 3 + 2.1 + DSB2 + Joystick + + + + + 0x31272A01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision D + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision A + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 + Japan, Revision C + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Fighter 3 Team Battle + Japan + Sega + 1996 + + + Sega Model 3 + 1.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtual On 2: Oratorio Tangram + Japan, Revision B + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtual On 2: Oratorio Tangram + Japan, Ver 5.4g + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Virtual On 2: Oratorio Tangram + Japan, Revision A + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Virtual On 2: Oratorio Tangram + Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + 0x16C311DB + true + + + + + 0x292a0e97 + + + + + + + + + + + + + + Virtua Striker 2 + Step 2.0, Export, USA + Sega + 1997 + + + Sega Model 3 + 2.0 + Billboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 + Step 1.5, Export, USA + Sega + 1997 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 + Step 1.5, Japan (test?) + Sega + 1997 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '98 + Step 2.0, Japan + Sega + 1998 + + + Sega Model 3 + 2.0 + Billboard + + + + + + + 0x29234e96 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '98 + Step 1.5, Japan + Sega + 1998 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Export, USA, Revision B + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99 + Export, USA, Revision A + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99 + Export, USA + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Japan, Revision B + Sega + 1999 + + + Sega Model 3 + 2.1 + Billboard + + + + + + + 0x29222ac8 + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Step 1.5 Export, USA, Revision B + Sega + 1999 + + + Sega Model 3 + 1.5 + Billboard + MPC106 + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99 + Step 1.5 Export, USA + Sega + 1999 + + + Sega Model 3 + 1.5 + MPC106 + Billboard + + + + + + + + + + + + + + + + + + + + Virtua Striker 2 '99.1 + Step 1.5 Japan, Revision B + Sega + 1999 + + + Sega Model 3 + 1.5 + MPC106 + Billboard + + + + + + + + + + + + + + + + + diff --git a/Config/Supermodel.ini b/Config/Supermodel.ini new file mode 100644 index 0000000..35dcd89 --- /dev/null +++ b/Config/Supermodel.ini @@ -0,0 +1,433 @@ +;; +;; Supermodel Configuration File +;; Default settings. +;; + + +; +; Quick Overview +; -------------- +; +; All settings are case sensitive. Numbers must be integers. Check your +; spelling carefully because invalid settings are silently ignored. To verify +; that your settings are being parsed correctly, check the contents of +; Supermodel.log. +; +; Global options apply to all games. To create configuration profiles for +; individual games, place settings under sections with the same name as the +; corresponding MAME ROM set, like so: +; +; ; Scud Race +; [ scud ] +; +; SoundVolume = 50 +; MusicVolume = 200 +; ; ... etc. ... +; +; For a list of all valid settings, please consult README.txt. Only default +; inputs are assigned here. +; + + +[ Global ] + +; Graphics +New3DEngine = false +QuadRendering = false +WideScreen = false +Stretch = false +WideBackground = false + +; Refresh rate (milliHertz accuracy). Actual Model 3 refresh rate is 57.524 Hz +; but this can cause judder so we use 60 Hz by default. +RefreshRate = 60.000 + +; Legacy SCSP DSP implementation for games that do not play well with the newer +; one (e.g., Fighting Vipers 2) +LegacySoundDSP = false + +; Network board +Network = false +SimulateNet = true +PortIn = 1970 +PortOut = 1971 +AddressOut = "127.0.0.1" + +; Input +; Sets the ManyMouse backend to ignore any mouse devices that don't provide +; absolute positioning when enabled, ideal for using Lightguns. +ABSMiceOnly = false + +; Common +InputUIExit = "JOY1_BUTTON10+JOY1_BUTTON9" +InputUIPause = "JOY1_BUTTON9+JOY1_BUTTON4" +InputUISelectCrosshairs = "JOY1_BUTTON9+JOY1_BUTTON2" +InputUISaveState = "KEY_9,JOY1_BUTTON10+JOY1_BUTTON4" +InputUILoadState = "KEY_0,JOY1_BUTTON10+JOY1_BUTTON2" +InputStart1 = "KEY_1,JOY1_BUTTON10" +InputStart2 = "KEY_2,JOY2_BUTTON10" +InputCoin1 = "KEY_3,KEY_ALT,JOY1_BUTTON9" +InputCoin2 = "KEY_4,JOY2_BUTTON9" +InputServiceA = "KEY_5" +InputServiceB = "KEY_7" +InputTestA = "KEY_6" +InputTestB = "KEY_8" + +; 4-way digital joysticks +InputJoyUp = "KEY_UP,JOY1_UP" +InputJoyDown = "KEY_DOWN,JOY1_DOWN" +InputJoyLeft = "KEY_LEFT,JOY1_LEFT" +InputJoyRight = "KEY_RIGHT,JOY1_RIGHT" +InputJoyUp2 = "JOY2_UP" +InputJoyDown2 = "JOY2_DOWN" +InputJoyLeft2 = "JOY2_LEFT" +InputJoyRight2 = "JOY2_RIGHT" + +; Fighting game buttons +InputPunch = "KEY_A,JOY1_BUTTON1" +InputKick = "KEY_S,JOY1_BUTTON2" +InputGuard = "KEY_D,JOY1_BUTTON3" +InputEscape = "KEY_F,JOY1_BUTTON4" +InputPunch2 = "JOY2_BUTTON1" +InputKick2 = "JOY2_BUTTON2" +InputGuard2 = "JOY2_BUTTON3" +InputEscape2 = "JOY2_BUTTON4" + +; Spikeout buttons +InputShift = "KEY_A,JOY1_BUTTON1" +InputBeat = "KEY_S,JOY1_BUTTON2" +InputCharge = "KEY_D,JOY1_BUTTON3" +InputJump = "KEY_F,JOY1_BUTTON4" + +; Virtua Striker buttons +InputShortPass = "KEY_A,JOY1_BUTTON1" +InputLongPass = "KEY_S,JOY1_BUTTON2" +InputShoot = "KEY_D,JOY1_BUTTON3" +InputShortPass2 = "JOY2_BUTTON1" +InputLongPass2 = "JOY2_BUTTON2" +InputShoot2 = "JOY2_BUTTON3" + +; Steering wheel +InputSteeringLeft = "KEY_LEFT" ; digital, turn wheel left +InputSteeringRight = "KEY_RIGHT" ; digital, turn wheel right +InputSteering = "JOY1_XAXIS" ; analog, full steering range + +; Pedals +InputAccelerator = "KEY_UP,JOY1_UP" +InputBrake = "KEY_DOWN,JOY1_DOWN" + +; Up/down shifter manual transmission (all racers) +InputGearShiftUp = "KEY_Y" ; sequential shift up +InputGearShiftDown = "KEY_H" ; sequential shift down + +; 4-Speed manual transmission (Daytona 2, Sega Rally 2, Scud Race) +InputGearShift1 = "KEY_Q,JOY1_BUTTON5" +InputGearShift2 = "KEY_W,JOY1_BUTTON6" +InputGearShift3 = "KEY_E,JOY1_BUTTON7" +InputGearShift4 = "KEY_R,JOY1_BUTTON8" +InputGearShiftN = "KEY_T" + +; VR4 view change buttons (Daytona 2, Le Mans 24, Scud Race) +InputVR1 = "KEY_A,JOY1_BUTTON1" +InputVR2 = "KEY_S,JOY1_BUTTON2" +InputVR3 = "KEY_D,JOY1_BUTTON3" +InputVR4 = "KEY_F,JOY1_BUTTON4" + +; Single view change button (Dirt Devils, ECA, Harley-Davidson, Sega Rally 2) +InputViewChange = "KEY_A,JOY1_BUTTON1" + +; Handbrake (Sega Rally 2) +InputHandBrake = "KEY_S,JOY1_BUTTON2" + +; Harley-Davidson controls +InputRearBrake = "KEY_S,JOY1_BUTTON2" +InputMusicSelect = "KEY_D,JOY1_BUTTON3" + +; Virtual On macros +InputTwinJoyTurnLeft = "KEY_Q,JOY1_RXAXIS_NEG" +InputTwinJoyTurnRight = "KEY_W,JOY1_RXAXIS_POS" +InputTwinJoyForward = "KEY_UP,JOY1_YAXIS_NEG" +InputTwinJoyReverse = "KEY_DOWN,JOY1_YAXIS_POS" +InputTwinJoyStrafeLeft = "KEY_LEFT,JOY1_XAXIS_NEG" +InputTwinJoyStrafeRight = "KEY_RIGHT,JOY1_XAXIS_POS" +InputTwinJoyJump = "KEY_E,JOY1_BUTTON1" +InputTwinJoyCrouch = "KEY_R,JOY1_BUTTON2" + +; Virtual On individual joystick mapping +InputTwinJoyLeft1 = "NONE" +InputTwinJoyLeft2 = "NONE" +InputTwinJoyRight1 = "NONE" +InputTwinJoyRight2 = "NONE" +InputTwinJoyUp1 = "NONE" +InputTwinJoyUp2 = "NONE" +InputTwinJoyDown1 = "NONE" +InputTwinJoyDown2 = "NONE" + +; Virtual On buttons +InputTwinJoyShot1 = "KEY_A,JOY1_BUTTON5" +InputTwinJoyShot2 = "KEY_S,JOY1_BUTTON6" +InputTwinJoyTurbo1 = "KEY_Z,JOY1_BUTTON7" +InputTwinJoyTurbo2 = "KEY_X,JOY1_BUTTON8" + +; Analog joystick (Star Wars Trilogy) +InputAnalogJoyLeft = "KEY_LEFT" ; digital, move left +InputAnalogJoyRight = "KEY_RIGHT" ; digital, move right +InputAnalogJoyUp = "KEY_UP" ; digital, move up +InputAnalogJoyDown = "KEY_DOWN" ; digital, move down +InputAnalogJoyX = "JOY_XAXIS,MOUSE1_XAXIS_INV" ; analog, full X axis +InputAnalogJoyY = "JOY_YAXIS,MOUSE1_YAXIS_INV" ; analog, full Y axis +InputAnalogJoyTrigger = "KEY_A,JOY_BUTTON1,MOUSE1_LEFT_BUTTON" +InputAnalogJoyEvent = "KEY_S,JOY_BUTTON2,MOUSE1_RIGHT_BUTTON" +InputAnalogJoyTrigger2 = "KEY_D,JOY_BUTTON2" +InputAnalogJoyEvent2 = "NONE" + +; Light guns (Lost World) +InputGunLeft = "KEY_LEFT" ; digital, move gun left +InputGunRight = "KEY_RIGHT" ; digital, move gun right +InputGunUp = "KEY_UP" ; digital, move gun up +InputGunDown = "KEY_DOWN" ; digital, move gun down +InputGunX = "MOUSE1_XAXIS,JOY1_XAXIS" ; analog, full X axis +InputGunY = "MOUSE1_YAXIS,JOY1_YAXIS" ; analog, full Y axis +InputTrigger = "KEY_A,JOY1_BUTTON1,MOUSE1_LEFT_BUTTON" +InputOffscreen = "KEY_S,JOY1_BUTTON2,MOUSE1_RIGHT_BUTTON" ; point off-screen +InputAutoTrigger = 1 ; automatic reload when off-screen +InputGunLeft2 = "NONE" +InputGunRight2 = "NONE" +InputGunUp2 = "NONE" +InputGunDown2 = "NONE" +InputGunX2 = "JOY2_XAXIS,MOUSE2_XAXIS" +InputGunY2 = "JOY2_YAXIS,MOUSE2_YAXIS" +InputTrigger2 = "JOY2_BUTTON1,MOUSE2_LEFT_BUTTON" +InputOffscreen2 = "JOY2_BUTTON2,MOUSE2_RIGHT_BUTTON" +InputAutoTrigger2 = 1 + +; Analog guns (Ocean Hunter, LA Machineguns) +InputAnalogGunLeft = "KEY_LEFT" ; digital, move gun left +InputAnalogGunRight = "KEY_RIGHT" ; digital, move gun right +InputAnalogGunUp = "KEY_UP" ; digital, move gun up +InputAnalogGunDown = "KEY_DOWN" ; digital, move gun down +InputAnalogGunX = "MOUSE1_XAXIS,JOY1_XAXIS" ; analog, full X axis +InputAnalogGunY = "MOUSE1_YAXIS,JOY1_YAXIS" ; analog, full Y axis +InputAnalogTriggerLeft = "KEY_A,JOY1_BUTTON1,MOUSE1_LEFT_BUTTON" +InputAnalogTriggerRight = "KEY_S,JOY1_BUTTON2,MOUSE1_RIGHT_BUTTON" +InputAnalogGunLeft2 = "NONE" +InputAnalogGunRight2 = "NONE" +InputAnalogGunUp2 = "NONE" +InputAnalogGunDown2 = "NONE" +InputAnalogGunX2 = "MOUSE2_XAXIS" +InputAnalogGunY2 = "MOUSE2_YAXIS" +InputAnalogTriggerLeft2 = "MOUSE2_LEFT_BUTTON" +InputAnalogTriggerRight2 = "MOUSE2_RIGHT_BUTTON" + +; Ski Champ controls +InputSkiLeft = "KEY_LEFT" +InputSkiRight = "KEY_RIGHT" +InputSkiUp = "KEY_UP" +InputSkiDown = "KEY_DOWN" +InputSkiX = "JOY1_XAXIS" +InputSkiY = "JOY1_YAXIS" +InputSkiPollLeft = "KEY_A,JOY1_BUTTON1" +InputSkiPollRight = "KEY_S,JOY1_BUTTON2" +InputSkiSelect1 = "KEY_Q,JOY1_BUTTON3" +InputSkiSelect2 = "KEY_W,JOY1_BUTTON4" +InputSkiSelect3 = "KEY_E,JOY1_BUTTON5" + +; Magical Truck Adventure controls +InputMagicalLeverUp1 = "KEY_UP" +InputMagicalLeverDown1 = "KEY_DOWN" +InputMagicalLeverUp2 = "NONE" +InputMagicalLeverDown2 = "NONE" +InputMagicalLever1 = "JOY1_YAXIS" +InputMagicalLever2 = "JOY2_YAXIS" +InputMagicalPedal1 = "KEY_A,JOY1_BUTTON1" +InputMagicalPedal2 = "KEY_S,JOY2_BUTTON1" + +; Sega Bass Fishing / Get Bass controls +InputFishingRodLeft = "KEY_LEFT" +InputFishingRodRight = "KEY_RIGHT" +InputFishingRodUp = "KEY_UP" +InputFishingRodDown = "KEY_DOWN" +InputFishingStickLeft = "KEY_A" +InputFishingStickRight = "KEY_D" +InputFishingStickUp = "KEY_W" +InputFishingStickDown = "KEY_S" +InputFishingRodX = "JOY1_XAXIS" +InputFishingRodY = "JOY1_YAXIS" +InputFishingStickX = "JOY1_RXAXIS" +InputFishingStickY = "JOY1_RYAXIS" +InputFishingReel = "KEY_SPACE,JOY1_ZAXIS_POS" +InputFishingCast = "KEY_Z,JOY1_BUTTON1" +InputFishingSelect = "KEY_X,JOY1_BUTTON2" +InputFishingTension = "KEY_T,JOY1_ZAXIS_NEG" + + ;Our non input settings... + +; Game specific settings +;daytona 2 power edition + [ dayto2pe ] +PowerPCFrequency = 90 +EmulateDSB = 1 +;daytona 2: battle to the edge + [ daytona2 ] +PowerPCFrequency = 90 +EmulateDSB = 1 + ;Dirt Devils (Export, Revision A) + [ dirtdvls ] +PowerPCFrequency = 60 +;Dirt Devils (Australia, Revision A) + [ dirtdvlsa ] +PowerPCFrequency = 60 +;Dirt Devils (German) + [ dirtdvlsg ] +PowerPCFrequency = 60 +;Dirt Devils (Japan, Revision A) + [ dirtdvlsj ] +PowerPCFrequency = 60 +;Emergency Call Ambulance (Export) - NOT WORKING PI + [ eca ] +;Emergency Call Ambulance (Japan) - NOIT WORKING PI + [ ecaj ] +;Emergency Call Ambulance (US location test?) - NOT WORKING PI + [ ecap ] +;Emergency Call Ambulance (USA) - NOT WORKING PI + [ ecau ] +;Fighting Vipers 2 (Japan, Revision A) + [ fvipers2 ] +PowerPCFrequency = 55 +;Fighting Vipers 2 (?) + [ fvipers2o ] +PowerPCFrequency = 48 +;Get Bass (?) + [ getbass ] +PowerPCFrequency = 30 +;Harley-Davidson and L.A. Riders (Revision B) - pi too slow + [ harley ] +PowerPCFrequency = 166 +;Harley-Davidson and L.A. Riders (Revision A) - pi too slow + [ harleya ] +PowerPCFrequency = 166 +;L.A. Machineguns: Rage of the Machines (Japan) + [ lamachin ] +PowerPCFrequency = 40 +;Le Mans 24 (Revision B) + [ lemans24 ] + ;The Lost World (Japan, Revision A) + [ lostwsga ];The Lost World (Original Revision) +PowerPCFrequency = 33 + [ lostwsgo ] +PowerPCFrequency = 33 +;Magical Truck Adventure (Japan) + [ magtruck ] +PowerPCFrequency = 40 +;The Ocean Hunter - Default works fine on pi + [ oceanhun ] +MusicVolume = 200 +SoundVolume = 200 + [ oceanhuna ] +MusicVolume = 200 +SoundVolume = 200 +;Scud Race (Australia, Twin) + [ scud ] +PowerPCFrequency = 35 +MusicVolume = 200 +EmulateDSB = 1 +;Scud Race (Export, Twin) + [ scuda ] +PowerPCFrequency = 35 +MusicVolume = 200 +EmulateDSB = 1 +;Scud Race (Japan, Deluxe) + [ scudj ] +PowerPCFrequency = 35 +MusicVolume = 200 +EmulateDSB = 1 +;Scud Race Plus (Revision A) + [ scudplus ] +PowerPCFrequency = 35 +MusicVolume = 200 +EmulateDSB = 1 +;Scud Race Plus (?) + [ scudplusa ] +PowerPCFrequency = 35 +MusicVolume = 200 +EmulateDSB = 1 +;Ski Champ (Japan) + [ skichamp ] +;PowerPCFrequency = 43 +PowerPCFrequency = 60 +;Spikeout Final Edition (?) - too slow on pi + [ spikeofe ] +PowerPCFrequency = 100 +EmulateDSB = 1 +;Spikeout (Revision C) + [ spikeout ] +;PowerPCFrequency = 35 +PowerPCFrequency = 55 +EmulateDSB = 1 +;Sega Rally 2 (?) + [ srally2 ] +EmulateDSB = 1 +PowerPCFrequency = 60 +;Sega Rally 2 (Prototype) + [ srally2p ] +PowerPCFrequency = 48 +EmulateDSB = 1 +;Sega Rally 2 (Prototype Version A) + [ srally2pa ] +PowerPCFrequency = 48 +EmulateDSB = 1 +;Sega Rally 2 (Deluxe) + [ srally2x ] +;Playable on pi but more slower down than srally2 +PowerPCFrequency = 100 +EmulateDSB = 1 +;Star Wars Trilogy Arcade - Default works fine on pi + [ swtrilgy ] +;Star Wars Trilogy Arcade (?) + [ swtrilgya ] +;Star Wars Trilogy Arcade (?) + [ swtrilgyp ] +;Virtua Fighter 3 (Revision D) + [ vf3 ] +PowerPCFrequency = 40 +;PowerPCFrequency = 100 +;Virtua Fighter 3 (Revision A) + [ vf3a ] +PowerPCFrequency = 40 +;Virtua Fighter 3 (Revision C) + [ vf3c ] +PowerPCFrequency = 40 +;Virtua Fighter 3 Team Battle (?) + [ vf3tb ] +PowerPCFrequency = 40 +;Virtual On 2: Oratorio Tangram (Revision B) + [ von2 ] +PowerPCFrequency = 30 +;Virtual On 2: Oratorio Tangram (Ver 5.4g) + [ von254g ] +PowerPCFrequency = 30 +;Virtual On 2: Oratorio Tangram (Revision A) + [ von2a ] +PowerPCFrequency = 30 +;Virtual On 2: Oratorio Tangram (?) + [ von2o ] +PowerPCFrequency = 35 +;Virtua Striker 2 (Step 2.0) - no special settings + [ vs2 ] +;Virtua Striker 2 (Step 1.5) - no special settings + [ vs215 ] +;Virtua Striker 2 (Step 1.5, older) - no special settings + [ vs215o ] +;Virtua Striker 2 '98 (Step 2.0) - no special settings + [ vs298 ] +;Virtua Striker 2 '98 (Step 1.5) - no special settings + [ vs29815 ] +;Virtua Striker 2 '99 (?) + [ vs299 ] +;Virtua Striker 2 '99 (Step 1.5) + [ vs29915 ] +;Virtua Striker 2 '99 (Revision A) + [ vs299a ] +;Virtua Striker 2 '99 (Revision B) + [ vs299b ] +;Virtua Striker 2 '99.1 (Revision B) diff --git a/Docs/Images/Daytona2_1.gif b/Docs/Images/Daytona2_1.gif new file mode 100644 index 0000000..33de337 Binary files /dev/null and b/Docs/Images/Daytona2_1.gif differ diff --git a/Docs/Images/Daytona2_2.gif b/Docs/Images/Daytona2_2.gif new file mode 100644 index 0000000..1307be4 Binary files /dev/null and b/Docs/Images/Daytona2_2.gif differ diff --git a/Docs/Images/FVipers2.gif b/Docs/Images/FVipers2.gif new file mode 100644 index 0000000..db7d641 Binary files /dev/null and b/Docs/Images/FVipers2.gif differ diff --git a/Docs/Images/LAMachin.gif b/Docs/Images/LAMachin.gif new file mode 100644 index 0000000..3534f84 Binary files /dev/null and b/Docs/Images/LAMachin.gif differ diff --git a/Docs/Images/Real3D_Logo.png b/Docs/Images/Real3D_Logo.png new file mode 100644 index 0000000..cbf5d9b Binary files /dev/null and b/Docs/Images/Real3D_Logo.png differ diff --git a/Docs/Images/Scud.gif b/Docs/Images/Scud.gif new file mode 100644 index 0000000..14ced6c Binary files /dev/null and b/Docs/Images/Scud.gif differ diff --git a/Docs/Images/StarWars.gif b/Docs/Images/StarWars.gif new file mode 100644 index 0000000..c294270 Binary files /dev/null and b/Docs/Images/StarWars.gif differ diff --git a/Docs/LICENSE.txt b/Docs/LICENSE.txt new file mode 100644 index 0000000..8c85531 --- /dev/null +++ b/Docs/LICENSE.txt @@ -0,0 +1,699 @@ + + + #### ### ### + ## ## ## ## + ### ## ## ## ### #### ## ### ## ## #### ## #### ## + ### ## ## ## ## ## ## ### ## ####### ## ## ##### ## ## ## + ### ## ## ## ## ###### ## ## ####### ## ## ## ## ###### ## + ## ## ## ## ##### ## ## ## # ## ## ## ## ## ## ## + #### ### ## ## #### #### ## ## #### ### ## #### #### + #### + + A Sega Model 3 Arcade Emulator. + Copyright 2003-2022 The Supermodel Team + + + LICENSE AGREEMENT FOR SUPERMODEL + + +Supermodel is distributed as free software under the terms of the GNU General +Public License, included below. See README.txt for additional documentation. +The source code may be obtained from the official Supermodel web site: + + http://www.Supermodel3.com + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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 Lesser General +Public License instead of this License. But first, please read +. diff --git a/Docs/README.txt b/Docs/README.txt new file mode 100644 index 0000000..c11b00c --- /dev/null +++ b/Docs/README.txt @@ -0,0 +1,1894 @@ + + + #### ### ### + ## ## ## ## + ### ## ## ## ### #### ## ### ## ## #### ## #### ## + ### ## ## ## ## ## ## ### ## ####### ## ## ##### ## ## ## + ### ## ## ## ## ###### ## ## ####### ## ## ## ## ###### ## + ## ## ## ## ##### ## ## ## # ## ## ## ## ## ## ## + #### ### ## ## #### #### ## ## #### ### ## #### #### + #### + + A Sega Model 3 Arcade Emulator. + Copyright 2003-2023 The Supermodel Team + + + USER MANUAL FOR SUPERMODEL + + +================ + Introduction +================ + +Supermodel emulates the Sega Model 3 arcade platform. It uses OpenGL 2.1 and +SDL, and can run on Windows, Linux, and Mac OS X. Development began in January +of 2011 and continues to focus on reverse engineering all aspects of the Model +3 arcade platform. + +In order to use Supermodel, you must legally possess ROM images of Model 3 games. +Learning to operate Supermodel will come with a steep learning curve for most +people. Before seeking out help, please read this user manual carefully. + +Supermodel is distributed as free software under the terms of the GNU General +Public License, included in LICENSE.txt. Additional copyright information for +software included within Supermodel is located at the bottom of this file. + +Windows 64-bit builds are updated automatically and are available for download +on the official Supermodel download page. Linux and MacOS users currently have +to compile/build their own executable from the GitHub source code repository. + +The official Supermodel website: + + http://www.Supermodel3.com + +Supermodel's GitHub source code repository: + + https://github.com/trzy/Supermodel + + +============== + Disclaimer +============== + +Supermodel is a work-in-progress, open source emulator. Emulation in Supermodel +has evolved to the point where most games run well, however there may be some +minor visual issues. You experience may vary. + + +===================== + Table of Contents +===================== + + --- Introduction + --- Disclaimer + --- Table of Conents + 1. Revision History + 2. Installing Supermodel + 3. Running Supermodel + 4. Game Compatibility + 5. Video Settings + 6. Audio Settings + 7. Controls + 8. Force Feedback + 9. Save States and NVRAM + 10. Game-Specific Comments and Tips + 11. The Configuration File + 12. Index of Command Line Options + 13. Index of Configuration File Settings + 14. Compiling the Source Code + 15. Contact Information + 16. Acknowledgments + + +======================= + 1. Revision History +======================= + + Version 0.2a (September 24, 2011) + - New, fully customizable input system. Supports any combination of + keyboards, mice, and analog and digital controllers. [Nik Henson] + - Texture offsets. Fixes models in 'Fighting Vipers 2', 'Virtual On', + cars in the 'Scud Race' and 'Daytona USA 2' selection screens, etc. + - Fixed a 2D palette bug that would cause black pixels to occasionally + turn transparent. + - Added all remaining ROM sets. [krom] + - Got some new games to boot: 'Spikeout', 'Ski Champ', 'Sega Bass + Fishing', 'Dirt Devils', etc. + - Sound support. Special thanks to ElSemi for contributing his SCSP + emulator and Karl Stenerud for allowing us to use his 68K emulator. + - Multi-threading support. Sound and drive board emulation are off- + loaded to separate threads, substantially enhancing performance. + [Nik Henson] + - Z80 emulation based on code by Frank D. Cringle and YAZE-AG by + Andreas Gerlich. + - Digital Sound Board (MPEG music) emulation courtesy of R. Belmont and + the MPEG decoder library by Tomislav Uzelac. + - Improved ROM loader. It should no longer be as easily confused by + combined ROM sets as long as unused files are removed. + - Configuration file now supports more settings and allows game- + specific customization. + - Added light gun crosshairs ('The Lost World'), enabled by default in + full screen mode and selectable by pressing Alt-I. + - Drive board and force feedback emulation for 'Scud Race', 'Daytona + USA 2', and 'Sega Rally 2'. [Nik Henson] + - Viewable display area properly clipped. Ghost artifacts no longer + appear in border regions when the resolution exceeds the display + area. + - Changed gear shifting: added a dedicated neutral gear and sequential + shifting. + - Console-based debugger (not enabled by default, must be + enabled during compile-time). [Nik Henson] + - Source code and Makefile cleanup. + + Version 0.1.2a (April 3, 2011) + - Included missing GLEW files. + + Version 0.1.1a (April 2, 2011) + - Minor source code update. + - Set Render3D to NULL in the CReal3D constructor. Fixes crashes that + occur on some builds. [Nik Henson] + - Cleaned up UNIX Makefile and added OS X Makefile. [R. Belmont] + - Small changes to ppc_ops.cpp for C++0x compliance. [R. Belmont] + - Included glew.h into the source tree. [R. Belmont] + - Changed WIN32 definition to SUPERMODEL_WIN32. + + Version 0.1a (April 1, 2011) + - Initial public alpha release. + + +============================ + 2. Installing Supermodel +============================ + +To install Supermodel on Windows, extract the ZIP archive containing the +Supermodel executable to a folder of your choice. The following files and +directories should be created: + + Name Description + ---- ----------- + Supermodel.exe Supermodel program. Run this. + SDL.dll The SDL library. Use the bundled DLL file. + README.txt This text file. + LICENSE.txt Supermodel license and terms of use. + Config/ Directory where the configuration file is stored. + Config/Supermodel.ini Configuration file containing default input + settings. + NVRAM/ Directory where NVRAM contents will be saved. + Saves/ Directory where save states will be saved. + +Supermodel requires OpenGL 2.1 and a substantial amount of both video and +system memory. A modern 64-bit CPU and a mid range GPU are the minimum +recommendations to achieve consistant frame rates of 60 FPS. + + +========================= + 3. Running Supermodel +========================= + +For now, Supermodel does not include a proper user interface. It is operated +entirely from the command line. Run 'supermodel' without any command line +arguments for an explanation of supported options. + +Supermodel uses MAME-compatible ROM sets. It loads from ZIP archives based on +file checksums and automatically detects games; therefore, file names are not +important. Only one ZIP file can be specified on the command line. For +example: + + supermodel scud.zip -fullscreen + +This will load 'scud.zip' (Scud Race) and run it in full screen mode. + +Initially, inputs are assigned according to the settings in 'Supermodel.ini', +located in the 'Config' subdirectory. + +Note that there is no user interface and all messages are printed to the +command prompt. In full screen mode, they will not be visible. + + +========================= + 4. Game Compatibility +========================= + +Supermodel recognizes all known Model 3 ROM sets. Below is a compatibility matrix. +Other than minor graphical issues, if any, major issues are reported below: + + ROM Set Title Comments ++------------+-----------------------------------------+-----------------------+ +| bassdx | Sega Bass Fishing (USA) | | +| getbassdx | Get Bass: Sega Bass Fishing (Deluxe) | | +| getbassur | Get Bass: Sega Bass Fishing (Upright) | | +| getbass | Get Bass: Sega Bass Fishing | | ++------------+-----------------------------------------+-----------------------+ +| daytona2 | Daytona USA 2 Battle on the Edge | | +| dayto2pe | Daytona USA 2 Power Edition | | ++------------+-----------------------------------------+-----------------------+ +| dirtdvls | Dirt Devils (Export, Revision A) | | +| dirtdvlsu | Dirt Devils (USA, Revision A) | | +| dirtdvlsj | Dirt Devils (Japan, Revision A) | | +| dirtdvlsau| Dirt Devils (Australia, Revision A) | | +| dirtdvlsg | Dirt Devils (Export, Ver. G?) | | ++------------+-----------------------------------------+-----------------------+ +| eca | Emergency Call Ambulance (Export) | | +| ecau | Emergency Call Ambulance (USA) | | +| ecaj | Emergency Call Ambulance (Japan) | | +| ecap | Emergency Call Ambulance (US proto?) | | ++------------+-----------------------------------------+-----------------------+ +| fvipers2 | Fighting Vipers 2 (Japan, Revision A) | | +| fvipers2o | Fighting Vipers 2 (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| harley | Harley Davidson & L.A. Riders (Rev. B) | | +| harleya | Harley Davidson & L.A. Riders (Rev. A) | | ++------------+-----------------------------------------+-----------------------+ +| lamachin | L.A. Machineguns: Rage of the Machines | may run slow | ++------------+-----------------------------------------+-----------------------+ +| lemans24 | Le Mans 24 (Japan, Revision B) | | ++------------+-----------------------------------------+-----------------------+ +| lostwsga | The Lost World Arcade (Japan, Rev A) | | +| lostwsgp | The Lost World Aracde (Location Test) | | ++------------+-----------------------------------------+-----------------------+ +| magtruck | Magical Truck Adventure (Japan) | | +| mgtrkbad | Magical Truck Adventure (bit rot dump) | | ++------------+-----------------------------------------+-----------------------+ +| oceanhun | The Ocean Hunter (Japan, Revision A) | | +| oceanhuna | The Ocean Hunter (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| scud | Scud Race (Export, Twin/DX) | | +| scudau | Scud Race (Australia, Twin/DX) | | +| scuddx | Scud Race (Export, Deluxe, Revision A) | | +| scudplus | Scud Race (Export, Twin/DX, Revision A) | | +| scudplusa | Scud Race (Export, Twin/DX) | | ++------------+-----------------------------------------+-----------------------+ +| skichamp | Ski Champ (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| spikeofe | Spikeout Final Edition (Export) | | ++------------+-----------------------------------------+-----------------------+ +| spikeout | Spikeout (Export, Revision C) | | ++------------+-----------------------------------------+-----------------------+ +| srally2 | Sega Rally 2 (Export) | | +| srally2p | Sega Rally 2 (Prototype) | proto is non working | +| srally2pa | Sega Rally 2 (Prototype Version A) | proto is non working | +| srally2dx | Sega Rally 2 (Export, Deluxe) | DX set may run slow | ++------------+-----------------------------------------+-----------------------+ +| swtrilgy | Star Wars Trilogy (Export, Revision A) | see note 1 below | +| swtrilgya | Star Wars Trilogy (Export) | see note 1 below | +| swtrilgyp | Star Wars Trilogy (Location Test) | see note 2 below | ++------------+-----------------------------------------+-----------------------+ +| vf3 | Virtua Fighter 3 (Japan, Revision D) | | +| vf3c | Virtua Fighter 3 (Japan, Revision C) | | +| vf3a | Virtua Fighter 3 (Japan, Revision A) | | +| vf3tb | Virtua Fighter 3 Team Battle (Japan) | | ++------------+-----------------------------------------+-----------------------+ +| von2 | Virtual On Oratorio Tangram (Revision B)| | +| von2a | Virtual On Oratorio Tangram (Revision A)| | +| von2o | Virtual On Oratorio Tangram | | +| von254g | Virtual On Oratorio Tangram (Ver. 5.4g) | | ++------------+-----------------------------------------+-----------------------+ +| vs2 | Virtua Striker 2 (Step 2.0, Export, USA)| | +| vs215 | Virtua Striker 2 (Step 1.5, Export, USA)| | +| vs215o | Virtua Striker 2 (Step 1.5, Japan test?)| | ++------------+-----------------------------------------+-----------------------+ +| vs298 | Virtua Striker 2 '98 (Step 2.0, Japan) | | +| vs29815 | Virtua Striker 2 '98 (Step 1.5, Japan) | | ++------------+-----------------------------------------+------------+----------+ +| vs2v991 | Virtua Striker 2 '99.1 (Step 2.1 Export, USA, Rev B) | | +| vs299a | Virtua Striker 2 '99 (Step 2.1 Export, USA, Rev A) | | +| vs299 | Virtua Striker 2 '99 (Step 2.1 Export, USA) | | +| vs299j | Virtua Striker 2 '99.1 (Step 2.1 Japan, Rev B) | | +| vs29915 | Virtua Striker 2 '99.1 (Step 1.5 Export, USA, Rev B) | | +| vs29915a | Virtua Striker 2 '99 (Step 1.5 Export, USA) | | +| vs29915j | Virtua Striker 2 '99.1 (Step 1.5 Japan, Rev B) | | ++------------+------------------------------------------------------+----------+ + +Note 1: Set game to U/R in game assignment in the TEST MENU to bypass the + "WAIT SETUP THE FEEDBACK STICK" screen +Note 2: Set game to SD in game assignment in the TEST MENU or wait a few seconds + for the game to continue past the "WAIT SETUP THE FEEDBACK STICK" screen + + +===================== + 5. Video Settings +===================== + +Supermodel may be run in either windowed (default) or full screen mode. It +automatically adjusts the display area to retain the aspect ratio of the Model +3's native (and Supermodel's default) resolution, 496x384. Currently, this +cannot be overriden. Changing video modes at run-time is not yet supported. + +By default, Supermodel limits the frame rate to 60 frames per second. This has +no impact on performance except when the frame rate exceeds 60 FPS on fast +systems. Frame rate limiting can be disabled with the '-no-throttle' option. +Some video drivers may continue to lock to the refresh rate. + +To change the resolution, use the '-res' command line option. For full screen +mode, use '-fullscreen'. For example, to set a full screen 1920x1080 mode, +try: + + supermodel game.zip -res=1920,1080 -fullscreen + +Video settings may also be specified globally or on a per-game basis in the +configuration file, described elsewhere in this manual. + + +===================== + 6. Audio Settings +===================== + +All Model 3 games have a sound board that is used for sound effects and, in +some games, background music. A few games use additional Digital Sound Boards +(DSB) for MPEG music. 'Music' in Supermodel refers exclusively to MPEG music +produced by the DSB and 'sound' refers to both the sound effects and background +music produced by the regular sound board. + +Model 3 sound and MPEG music are generated separately and then mixed by an +amplifier. The relative signal levels are not known, so Supermodel simply +outputs all audio at full volume. This causes the MPEG music to be too quiet +in some games ('Scud Race', 'Daytona USA 2') and too loud in others ('Star Wars +Trilogy'). The '-sound-volume' and '-music-volume' options can be used to +change the volume. As arguments, they take a volume level in percent ranging +from 0 (muted) to 200% (maximum, doubled amplitude). For example: + + supermodel game.zip -sound-volume=50 -music-volume=170 + +This command line cuts the sound volume in half and increases the music volume +by 70%. + +The F9 and F10 keys can be used to adjust music volume during run-time, while +F11 and F12 control sound volume. + +Clipping and distortion will occur if the combined sound and music volume +levels become too high. + +To disable sound and music board emulation altogether, use the '-no-sound' and +'-no-dsb' options. These will not merely mute the corresponding audio channels +but will prevent the audio co-processors from being emulated altogether and +may slightly improve performance, especially on single-core systems. Save +states generated with these settings may not be able to properly restore audio +when loaded after emulation is re-enabled. + +Audio settings may also be specified globally or on a per-game basis in the +configuration file, described elsewhere in this document. + +Please keep in mind that MPEG music emulation is preliminary and the decoder is +buggy. Periodic squeaks and pops occur on many music tracks. The Sega Custom +Sound Processor (SCSP) emulator, used for sound emulation, is also still quite +buggy. Sound glitches are known to occur now and then, and many sounds and +tunes do not sound quite correct yet. + + +=============== + 7. Controls +=============== + +Game controls are fully configurable and can be mapped to keyboards, mice, and +game controllers. Emulator functions, on the other hand, cannot be changed and +are listed below. + + Function Key Assignment + -------- -------------- + Exit Escape + Pause Alt-P + Reset Alt-R + Clear NVRAM Alt-N + Crosshairs (for light gun games) Alt-I + Toggle 60 Hz Frame Limiting Alt-T + Save State F5 + Load State F7 + Change Save Slot F6 + Decrease Music Volume F9 + Increase Music Volume F10 + Decrease Sound Volume F11 + Increase Sound Volume F12 + + +Learning and Configuring Game Controls +-------------------------------------- + +Settings for game controls are stored in 'Supermodel.ini'. To learn the +current configuration, use the '-print-inputs' command line option. + + supermodel -print-inputs + +If you delete 'Supermodel.ini', all inputs will become unmapped and will have +to be reconfigured! + +To reconfigure the inputs, use the '-config-inputs' option. A blank window +will open up and instructions will be printed to the command prompt. Read them +carefully! Pressing the Escape key exits without saving changes. Pressing 'q' +will save changes to 'Supermodel.ini'. You can choose which input to configure +using the Up and Down arrow keys. Clearing an input means it will be unusable. + +If the configuration dialog is not responding to your key presses or mouse and +joystick movements, make sure that the blank pop-up window rather than the +command prompt is selected. This may seem counter-intuitive but inputs are +captured by the graphical window while messages are printed to the command +prompt. + + +Calibrating Controls +-------------------- + +Some controls, in particular the accelerator and brake pedals of steering +wheels, may need calibrating before they will function properly with +Supermodel. + +In order to calibrate a control, press 'b' when configuring the inputs and +follow the on-screen instructions carefully. + +NOTE: After calibration, you must remap the control again or it will not +function properly! + +Several input-related settings are stored in the configuration file. Refer to +the section discussing the configuration file for more details. + + +XBox 360 Controllers +-------------------- + +For full XBox 360 controller support, the XInput system must be used on Windows +('-input-system=xinput'). Otherwise, the left and right trigger buttons cannot +be mapped individually and force feedback will not work. Please read the +section titled 'Input Systems', further below. + +Mac OS X users can use Colin Munro's XBox 360 controller driver: +http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver + + +Analog Controls +--------------- + +Analog controls, such as steering wheels, pedals, and the light gun axes, can +be mapped to analog controllers, such as joysticks, wheels, and mice, if +available. Under digital control, the analog value will increment or decrement +until it reaches its maximum/minimum values. The rate of change can only be +set manually in the configuration file. These options are described elsewhere +in this document. + +Most analog inputs, such as the steering wheel, require two settings to be +controlled digitally. For example, the steering wheel can be turned left and +right by setting 'Steer Left' and 'Steer Right', or it can be mapped to a +single analog control ('Full Steering'). + + +Shifting Gears +-------------- + +Racing game gears can be mapped to individual buttons, including one for the +neutral position, or can be shifted sequentially. Games which only provide two +gears name them 'Up' and 'Down'. These should not be confused with 'Shift Up' +and 'Shift Down', the sequential shift commands. + + +Virtual On Twin Joysticks +------------------------- + +'Virtual On Oratorio Tangram' features a twin joystick control scheme similar +to a tracked vehicle (e.g. a tank). Movement is accomplished by pushing both +joysticks in the same direction. Pushing and pulling in opposite directions +will turn the robot, while pulling the joysticks apart sideways or pushing them +inwards are for jumping and falling back down to the ground, respectively. + +Supermodel supports mapping the individual joysticks but also provides 'macro' +controls by default. These allow all the necessary twin joystick commands to +be replicated with individual controls. The mapping is below: + + Macro Control Twin Joystick Equivalent + ------------- ------------------------ + Turn Left Left joystick down, right joystick up. + Turn Right Left joystick up, right joystick down. + Forward Both joysticks up. + Reverse Both joysticks down. + Strafe Left Both joysticks left. + Strafe Right Both joysticks right. + Jump Left joystick left, right joystick right. + Crouch Left joystick right, right joystick left. + + +Light Guns +---------- + +Light gun axes can be mapped to mice, analog controls, or even digital buttons. +To simulate pointing off-screen (required in order to reload), a 'point off- +screen' input is provided which, for as long as it is pressed, will aim the gun +off-screen. To reload, hold down this button and then, while holding it, press +the trigger. For easier reloading, the 'InputAutoTrigger' setting can be +enabled in the configuration file (described elsewhere in this manual). + +Crosshairs will be visible in full screen mode and can also be enabled in +windowed modes. Use Alt-I to cycle through different crosshair options (enable +for a single player, both, or none). + +For multiple mouse support, allowing two mice or PC light guns to be mapped, +Raw Input must be used. This is supported only on Windows and is described +below. + +For specific information on using Sinden Light guns with Supermodel, refer to +the Sinden Light gun Wiki: + +https://www.sindenwiki.org/wiki/Supermodel_M3 + + +Input Systems +------------- + +Supermodel supports multiple input APIs to provide the best possible +compatibility for different input devices and configuration schemes. On +Windows, the default is DirectInput. On all other platforms, SDL is the only +option available. + +Windows users can select between four different input systems: + + - DirectInput. Selected with '-input-system=dinput'. This is the default. + It provides the best support for PC game controllers and, when emulating + force feedback, allows all effects (if the controller supports them). + - XInput. Selected with '-input-system=xinput'. This must be used with + XBox 360 controllers, otherwise some buttons will not function properly + and force feedback will not work. + - Raw Input. Selected with '-input-system=rawinput'. This is intended for + use with multiple mice and keyboards but is not recommended otherwise. + - SDL. Selected with '-input-system=sdl'. The standard, cross-platform + input system intended for non-Windows builds. It is accessible on + Windows but does not provide full support for all devices. + +When switching input systems with '-input-system', you must also configure your +inputs using the same option. For example, when running Supermodel with XInput +('supermodel game.zip -input-system=xinput'), you must also configure with +XInput ('supermodel -config-inputs -input-system=xinput'). Many settings are +not compatible between input systems. + +A common mistake is to configure inputs using one system and then launch +Supermodel with another. + + +Troubleshooting +--------------- + +Common input-related problems are discussed below. + + + Problem: + No response while configuring inputs. + + Solution: + Make sure the main window is top window, especially with the SDL input + system. + + --------- + + Problem: + An attached controller is not recognized at all. + + Solution: + - On the input configuration screen, press 'I' to see information about + the input system and check that the required controller is in the + list. + - If it is not in the list, check that it is plugged in and visible in + the operating system. If a controller is plugged in while Supermodel + is running, Supermodel will need to be restarted in order for it to + recognize the new controller. + - On Windows, if after checking the above points the controller is + still not in the list, try running Supermodel with the SDL input + system by specifying '-input-system=sdl' on the command line. + + --------- + + Problem: + Unable to select a particular joysick axis when configuring a mapping. + + Solution: + This may mean that the joystick axis needs calibrating. This can be + done by pressing 'b' on the configuration screen and following the + instructions. + + --------- + + Problem: + Steering wheel pedals are not being recognized. + + Solution: + For some steering wheels where the pedals have been configured in a + 'split axis' mode (ie. not sharing a combined axis), the values that + the pedals send are inverted. This means that without configuration, + Supermodel will be unable to recognize them. To fix this, they should + be calibrated on the calibration screen in the same way as above. + + --------- + + Problem: + A control isn't working despite having been calibrated. + + Solution: + After calibration, while still in the configuration dialog, you must + remap the control again for the new settings to take effect. + + --------- + + Problem: + XBox 360 controller trigger buttons cannot be triggered simultaneously + or no rumble effects. + + Solution: + On Windows, make sure that you specify '-input-system=xinput'. + + +===================== + 8. Force Feedback +===================== + +Force feedback is presently supported in 'Scud Race' (including 'Scud Race +Plus'), 'Daytona USA 2' (both editions), and 'Sega Rally 2' on Windows only. To +enable it, use the '-force-feedback' option. + +Drive board ROMs are required. They first appear in the MAME 0.143u6 ROM +catalog and at the time of this writing, have not yet widely proliferated. + + Game Drive Board ROM File Size Checksum (CRC32) + ---- -------------------- ---- ---------------- + Daytona USA 2 epr-20985.bin 64 KB B139481D + Scud Race epr-19338a.bin 64 KB C9FAC464 + Sega Rally 2 epr-20512.bin 64 KB CF64350D + +The sizes and checksums must match those listed above. The file names may be +different but will almost certainly contain the same identifying numbers. +Ensure that the appropriate drive board ROM files are present in the +corresponding games' ZIP archives, otherwise Supermodel will silently proceed +without force feedback. + +Force feedback will only work with the DirectInput (the default on Windows) and +XInput input systems. XInput is intended only for XBox 360 controllers, which +do not support force feedback through DirectInput. + + +Tuning Force Feedback +--------------------- + +Force feedback can be enabled and tuned in the configuration file. Setting +'ForceFeedback' to 1 enables it: + + ForceFeedback = 1 + +There are four DirectInput effects: constant force, self centering, friction, +and vibration. The strength of each can be tuned with the following settings: + + DirectInputConstForceMax = 100 + DirectInputSelfCenterMax = 100 + DirectInputFrictionMax = 100 + DirectInputVibrateMax = 100 + +They are given as percentages and represent the maximum strength for each +effect. A setting of 0 disables them entirely. Values above 100% are +accepted but may clip or distort the effect. By default, they are set to 100%, +as shown above. + +XInput devices only support vibration feedback via the left and right motors. +Because the characteristics of the motors are different, the effects will feel +somewhat asymmetric. The constant force effect is simulated with vibrations. +The relevant settings are: + + XInputConstForceThreshold = 30 + XInputConstForceMax = 100 + XInputVibrateMax = 100 + +The constant force threshold specifies how strong a constant force command must +be before it is sent to the controller as a vibration effect (whose strength is +determined by XInputConstForceMax). The default values are shown above and +will require calibration by the user on a game-by-game basis to achieve the +best feel. + + +============================ + 9. Save States and NVRAM +============================ + +Save states are saved and restored by pressing F5 and F7, respectively. Up to +10 different save slots can be selected with F6. All files are written to the +Saves/ directory, which must exist beforehand. If you extracted the Supermodel +ZIP file correctly, it will have been created automatically. + +If a Model 3 co-processor (ie. sound board, DSB, drive board) is disabled when +a save state is taken, it will not resume normal operation when the state is +loaded, even if Supermodel is running with the co-processor re-enabled. The +drive board (and consequently, force feedback effects) is explicitly disabled +for the remainder of the session if a save state with an inactive drive board +is loaded. Audio co-processors are not, and therefore audio playback may +eventually resume after the audio boards have booted themselves up. + +Non-volatile memory (NVRAM) consists of battery-backed backup RAM and an EEPROM +chip. The former is used for high score data and statistics whereas the latter +stores machine settings (often accessed using the Test buttons). NVRAM is +automatically saved each time Supermodel exits and is loaded at start-up. It +can be cleared by deleting the NVRAM files or pressing Alt-N. + + +======================================= + 10. Game-Specific Comments and Tips +======================================= + + +Daytona USA 2 and Daytona USA 2 Power Edition +--------------------------------------------- + +By default, the 'Power Edition' ROM set features remixed music lyrics by +Takenobu Mitsuyoshi. These can be changed back to the Dennis St. James version +in the Test Menu, under 'Game Assignments'. + +Star Wars Trilogy +----------------- + +Inserting coins and starting a game before any 3D graphics have been displayed +in attract mode will result in a PowerPC crash before the stage loads. This is +caused by an unknown emulation bug. Simply wait until the Darth Vader sequence +appears before attempting to start a game. + +If 'Star Wars Trilogy' is booting directly into the stage select screen, it is +probably because you exited Supermodel with credits still in the machine. +Clear the NVRAM (Alt-N) and reset the game (Alt-R). + + +Sega Rally 2 +------------ + +As with 'Star Wars Trilogy', you may experience problems if you attempt to +start a game before any 3D graphics are displayed (for example, during the Sega +logo). + + +Spikeout and Spikeout Final Edition +----------------------------------- + +These games can be played all the way through. There may be periodic texture +glitches and or occasional flashing. + + +The Lost World +-------------- + +To reload, the light gun must be pointed off-screen by pressing (and holding) +the 'off-screen' button and, simultaneously, pressing the trigger to shoot. +This behavior can be changed with the 'InputAutoTrigger' setting in the +configuration file. + + +============================== + 11. The Configuration File +============================== + +Supermodel reads configuration settings from 'Supermodel.ini' located in the +'Config' subdirectory. If Supermodel was installed properly, a default file +should have been created. When starting up, Supermodel parses settings in the +following order: + + 1. Global settings are read from 'Supermodel.ini'. These include input + mappings and apply to all games. + 2. If the ROM set was loaded correctly, game-specific settings are read + from 'Supermodel.ini', overriding settings from step 1. + 3. Command line options are applied, overriding settings from the previous + steps. + +In other words, command line options have the highest precedence, followed by +game-specific settings, and lastly, global settings. + +An index of all allowed settings is provided further below in this document. + +NOTE: Type carefully! Supermodel will not report syntax errors nor detect +typos. Carefully read the discussion of the file syntax below. To verify that +your intended settings are taking effect, check the 'error.log' file that is +produced by Supermodel during each run. + + +File Structure and Syntax +------------------------- + +The configuration file is a list of settings grouped by sections. All setting +names and their arguments are case sensitive. They take the form: + + Name = Argument + +Only one setting per line is allowed. Only two types of arguments are allowed: +integers and strings. The choice of which to use is determined by the setting. +Integers can be negative. Strings can contain any characters and are enclosed +by double quotes. Examples: + + IntegerSetting1 = 0 + IntegerSetting2 = 65536 + IntegerSetting3 = -32768 + StringSetting1 = "This is a string." + StringSetting2 = "123" + StringSetting3 = "1+1, abc, these are all valid characters!" + +Many settings are simply boolean variables expecting an integer value of either +0 (disabled) or 1 (enabled). Some settings require values and others take +strings for file names or input mappings. + +Section names appear in between square brackets on their own lines. + + [ SectionName ] + +Settings that appear at the beginning of the file without a preceeding section +are automatically assigned to 'Global'. + +Comments begin with a semicolon and extend until the end of the line. + + ; This is a comment. + + +Global and Game-Specific Sections +--------------------------------- + +Sections determine whether settings are applied globally, to all games, or to +specific games. Game-specific settings will override global settings and can +be used to tune Supermodel on a game-by-game basis. Global settings must be +placed in the 'Global' section and game-specific settings in sections named +after the ROM sets. ROM sets must be typed in lower case and use the MAME +(http://www.mamedev.org) naming convention. They can be obtained by running +'supermodel -print-games'. + +Input mappings are special. They are only read from the 'Global' section! + +In the example below, custom configurations are created for 'Scud Race' and +'The Lost World'. All other games will use the global settings. + + [ Global ] + + ; Run full screen at 1024x768 + XResolution = 1024 + YResolution = 768 + FullScreen = 1 + + ; Scud Race + [ scud ] + + ; Run at 1080p + XResolution = 1920 + YResolution = 1080 + + ; Music is too quiet by default + SoundVolume = 50 + MusicVolume = 200 + + ; The Lost World + [ lostwsga ] + + PowerPCFrequency = 25 ; run PowerPC at 25 MHz + +In this example, only 'Scud Race' will run at 1920x1080. All other games will +use 1024x768. 'Scud Race' will also have altered volume settings. 'The Lost +World' will be run with a lower PowerPC frequency but all other games will use +the default. + + +Input Mappings +-------------- + +Input mappings are specified with strings that have their own internal syntax. +They may only be specified in the 'Global' section and are ignored elsewhere. + +A mapping may be specified in one of the following three ways: + + KEY_XXX For a key XXX on a keyboard (eg. KEY_A, KEY_SPACE, KEY_ALT). + + MOUSE_XXX For a mouse axis (X or Y), wheel, or button (1-5) (eg. + MOUSE_XAXIS, MOUSE_WHEEL_UP, MOUSE_BUTTON3). + + JOY1_XXX For a joystick axis (X, Y, Z, RX, RY, RZ), POV controller + JOY2_XXX (1-4), or button (1-12) (e.g. JOY1_XAXIS, JOY3_POV2_UP, + ... JOY2_BUTTON4). + +Joysticks, including game pads and steering wheels, are numbered from 1 on up. +Their ordering is system dependent and if controllers are swapped between +invocations of Supermodel, then the ordering may change. If the number is +omitted then the mapping applies to all joysticks, eg. JOY_BUTTON1 means button +1 on any attached joystick. + +In addition to the above, when using the Raw Input system on Windows (command +line option '-input-system=rawinput') it is possible to refer to a particular +mouse or keyboard by including its number in the mapping, eg. KEY2_A or +MOUSE3_XAXIS. This allows the independent mapping of dual mice or dual light +guns (which present themselves as mice to Supermodel) for 'The Lost World'. + +Mappings may be combined with a plus '+' or a comma ',' and negated with an +exclamation mark '!'. The plus signifies that both mappings must be active to +trigger the input and the comma means that just one needs to be active. The +exclamation mark means that a mapping must not be active. + +Of the above the comma is the most useful. It allows the mapping of several +different control methods to a single input. For example, both a keyboard and +a joystick could be mapped to the same input. When combining mappings in this +way, the ordering is important as mappings earlier in the list will take +precedence over later ones (see example 3 below). + +When an axis is mapped to a digital input, its direction must be qualified with +a _POS or _NEG after the axis name. For example, JOY1_XAXIS_NEG indicates that +joystick 1 must be pushed left (negative direction) in order to activate the +input. Additionally, the following shortcuts are provided for some common +joystick axis directions: + + JOY1_LEFT is the same as JOY1_XAXIS_NEG + JOY1_RIGHT is the same as JOY1_XAXIS_POS + JOY1_UP is the same as JOY1_YAXIS_NEG + JOY1_DOWN is the same as JOY1_YAXIS_POS + +Another axis manipulator is _INV. This inverts an axis when it is mapped to an +analog input. JOY1_YAXIS_INV could be used for example to invert the y-axis +of joystick 1 in Star Wars Trilogy so that it acts like a plane's yoke. + +Examples: + + 1. InputLeft = "KEY_LEFT,JOY1_LEFT" + + Digital input InputLeft will be triggered whenever the left arrow key + is pressed or joystick 1 is pushed left. + + 2. InputStart1 = "KEY_ALT+KEY_S" + + Both Alt and S must be pressed at the same time in order to trigger the + digital input InputStart1 (player 1 Start button). + + 3. InputSteering = "JOY1_XAXIS,MOUSE_XAXIS" + + Analog steering can be controlled with either the joystick or by moving + the mouse. Due to the ordering used, the joystick will take preference + and so the mouse can only control the steering when the joystick is + released. + +The complete list of input mappings can be found in the settings index below or +by generating a configuration file using '-config-inputs'. + + +Controller Settings +------------------- + +Several settings are available to fine tune the way Supermodel handles +controllers. + + +Keyboard Settings: + +The rate at which analog controls' values increase or decrease when controlled +by a key can be set with the InputyKeySensitivity option. It takes a value in +the range 1-100, with 100 being the most sensitive (fastest response) and 1 +being the least sensitive (slowest response). The default value is 25. + +The speed with which analog controls return to their rest or 'off' values after +a key has been released is configured with InputKeyDecaySpeed. This takes a +value in the range 1-200 and is expressed as a percentage of the attack speed. +The default setting is 50, which means that analog controls take twice as long +to return to their off values as they do to reach their maximum and minimum +values. + + +Mouse Settings: + +For a mouse it is possible to specify a rectangular area in the center of +Supermodel's display within which the mouse is considered to be inactive. The +width and height of this zone is set with the options 'InputMouseXDeadZone' and +'InputMouseYDeadZone'. They are expressed as a percentage 0-99 of the width +and height of the display. The default value is 0 which disables this option. + + +Joystick Options: + +Each joystick axis has a set of parameters that can be configured and these are +given below. The joystick number and axis name (X, Y, Z, RX, RY, or RZ) must +be substituted in for '**' like so: InputJoy1XDeadZone. If the joystick number +is omitted, then the option becomes the default for all joysticks, eg. +InputJoyYSaturation. + +InputJoy**Deadzone: The dead zone of a joystick axis is the size of the zone +around its central or 'off' position within which the axis is considered to be +inactive. It is important to specify this since joysticks rarely return to +their perfect center when released. The dead zone is expressed as a percentage +0-99 of the total range of the axis and the default value is 2%. + +InputJoy**Saturation: The saturation of a joystick axis is the point at which +the axis is considered to be at its most extreme position. It can be thought +of as a measure of the sensivity of the axis. Like the dead zone, it is +expressed as a percentage of the axis range but its value may be larger than +100, up to a maximum of 200. A value of 50 means that the joystick only needs +to be moved halfway in order for Supermodel to see it as fully extended. +Conversely a value of 200 means that when the joystick is at its extreme +position Supermodel will see it as only halfway. The default sensitivity is +100%, which corresponds to a 1-to-1 mapping. For playing driving games with a +game pad, it is sometimes a good idea to use a value larger than 100% so that +the steering feels less sensitive on a thumbstick. + +InputJoy**MinVal, InputJoy**OffVal, InputJoy**MaxVal: Normally a joystick axis +will send values to Supermodel that fall within the range -32768 to +32767, +with 0 representing the central or 'off' position. However, in some cases an +axis may behave differently. This has been observed with certain steering +wheels that have their pedals configured in a 'split axis' mode. The pedals +invert their range so that they send -32768 when released and +32767 when fully +depressed. In order for Supermodel to be able to handle such cases the +minimum, off, and maximum values of the axis can be specified with these three +options. The default values are MinVal -32768, OffVal 0, and MaxVal 32767 but +with the pedal example just given, they would need to be set to MinVal 32767, +OffVal 32767, and MaxVal -32768. + +In most cases it will be unecessary to alter the DeadZone, MinVal, OffVal, and +MaxVal options by hand as calibrating a joystick axis from within the input +configuration screens of Supermodel will set them automatically. Only the +sensitivity option is not configured in this way. + + +Additional Options: + +InputAutoTrigger, InputAutoTrigger2: When playing light gun games such as 'The +Lost World', the gun must be pointed off-screen and then fired in order to +reload. With a mouse and the default mappings, this requires clicking both the +right and left mouse buttons. In order to be able to reload the gun with just +a single click of the right mouse button, enable these options for players 1 +and 2 by setting them to 1. By default they are disabled (value 0). + +Enabling these options is also recommended when playing with PC light guns as +these often map the action "point off screen and press trigger" to a single +right mouse button click. Supermodel has been tested successfully with +UltiMarc's AimTrak light-guns. In order to use dual PC light-guns for two +player games, the Raw Input system must be selected with the command line +option '-input-system=rawinput' and the input mappings configured for each gun. + + +===================================== + 12. Index of Command Line Options +===================================== + +All valid command line settings are listed here, ordered by category. Defaults +are given under the assumption that they have not been changed in the +configuration file. + +Square brackets ('[' and ']') indicate optional parameters. Angled brackets +('<' and '>') indicate mandatory parameters. Do not type the brackets. No +spaces may appear inside of an option. For example, 'supermodel game.zip +-ppc-frequency=25' is correct but 'supermodel game.zip -ppc-frequency = 25' is +not. All options are case sensitive. + + + Option: -? + -h + + Description: Prints a message with an overview of the command line + options and how to run Supermodel. + + ---------------- + + Option: -print-games + + Description: Lists all supported ROM sets. Not all supported ROM sets + are playable yet. + + ---------------- + + Option: -no-threads + + Description: Disables multi-threading. When enabled (the default), the + PowerPC, graphics rendering, and user interface are run in + one thread, sound emulation in a second thread, and the + drive board in a third. It is enabled by default. On + single-core systems, multi-threading adds overhead and + slows Supermodel down but can make audio sound a bit + smoother. + + ---------------- + + Option: -ppc-frequency= + + Description: Sets the PowerPC frequency in MHz. The default is 50. + Higher frequencies will allow games to do more processing + per frame, making them run faster in 'virtual' time, but + may slow down Supermodel on weaker computers. Supermodel's + performance can frequently be improved by lowering the + PowerPC frequency (25 MHz works in many games). In some + games, this will cause timing problems and jerky + performance. The optimum frequency will vary from game to + game and system to system. Valid values for are 1 to + 1000. + + ---------------- + + Option: -fullscreen + + Description: Runs in full screen mode. The default is to run in a + window. + + ---------------- + + Option: -no-throttle + + Description: Disables 60 FPS throttling. The Model 3 runs at a 60 Hz + refresh rate, which Supermodel enforces if this option is + omitted. Unthrottled operation may not work on some + systems because graphics drivers may lock the refresh rate + on their own. + + ---------------- + + Option: -print-gl-info + + Description: Prints OpenGL driver information and quits. + + ---------------- + + Option: -res=, + + Description: Resolution of the display in pixels, with being width + and being height. The default is 496x384, the Model + 3's native resolution. + + ---------------- + + Option: -show-fps + + Description: Shows the frame rate in the window title bar. + + ---------------- + + Option: -frag-shader= + -vert-shader= + + Description: Allows external fragment and vertex shaders to be used for + 3D rendering. is the file path. Files should be + ASCII text files containing GLSL source code. The file + extension is not important. By default, Supermodel's + internal shader programs are used. These options are + intended for future extensibility and for use by + developers. + + ---------------- + + Option: -flip-stereo + + Description: Swaps the left and right audio channels. + + ---------------- + + Option: -no-dsb + + Description: Disables Digital Sound Board (MPEG music) emulation. See + the section on audio settings for more information. + + ---------------- + + Option: -no-sound + + Description: Disables sound board (sound effects) emulation. See the + section on audio settings for more information. + + ---------------- + + Option: -music-volume= + -sound-volume= + + Description: Specifies the volume of MPEG music produced by the Digital + Sound Board and the audio produced by the sound board in + percent. The default is 100, which is full volume, and the + valid range of is 0 (muted) to 200. See the section on + audio settings for more information. + + ---------------- + + Option: -config-inputs + + Description: Launches the interacive input configuration utility in the + command prompt window. Allows controls to be remapped. + See the section on controls for more information. + + ---------------- + + Option: -force-feedback + + Description: Enables force feedback. Force feedback is only supported + in a few games (see the section on force feedback for more + details). Available only on Windows. + + ---------------- + + Option: -input-system= + + Description: Sets the input system. This is only available on Windows, + where the default is 'dinput' (DirectInput). SDL is used + for all other platforms. Valid choices for are: + + dinput DirectInput. + xinput XInput + rawinput Raw Input. + sdl SDL. + + See the section on input systems for more details. + + ---------------- + + Option: -print-inputs + + Description: Prints the current input configuration. + + +============================================ + 13. Index of Configuration File Settings +============================================ + +All valid configuration file settings are listed here, ordered by category. +Please read the section describing the configuration file for more information. + +All settings are case sensitive. + + + Name: MultiThreaded + + Argument: Integer. + + Description: If set to 1, enables multi-threading; if set to 0, runs all + emulation in a single thread. Read the description of the + '-no-threads' command line option for more information. + + ---------------- + + Name: PowerPCFrequency + + Argument: Integer. + + Description: The PowerPC frequency in MHz. The default is 50. + Equivalent to the '-ppc-frequency' command line option. + + ---------------- + + Name: FullScreen + + Argument: Integer. + + Description: If set to 1, runs in full screen mode; if set to 0, runs in + a window. Disabled by default. Equivalent to the + '-fullscreen' command line option. + + ---------------- + + Name: ShowFrameRate + + Argument: Integer. + + Description: Shows the frame rate in the window title bar when set to 1. + If set to 0, the frame rate is not computed. Disabled by + default. Equivalent to the '-show-fps' command line + option. + + ---------------- + + Name: Throttle + + Argument: Integer. + + Description: Controls 60 FPS throttling. It is enabled by setting to 1 + and disabled by setting to 0. For more information, read + the description of the '-no-throttle' command line option. + + ---------------- + + Name: XResolution + YResolution + + Argument: Integer. + + Description: Resolution of the display in pixels. The default is + 496x384, the Model 3's native resolution. Equivalent to + the '-res' command line option. + + ---------------- + + Name: FragmentShader + VertexShader + + Argument: String. + + Description: Path to the external fragment or vertex shader file to use + for 3D rendering. By default, these are not set and the + internal default shaders are used. These are equivalent to + the '-frag-shader' and '-vert-shader' command line options. + + ---------------- + + Name: EmulateDSB + + Argument: Integer. + + Description: Emulates the Digital Sound Board if set to 1, disables it + if set to 0. See the section on audio settings for more + information. A setting of 0 is equivalent to the + '-no-dsb' command line option. + + ---------------- + + Name: EmulateSound + + Argument: Integer. + + Description: Emulates the sound board and its two Sega Custom Sound + Processors if set to 1, disables it if set to 0. See the + section on audio settings for more information. A setting + of 0 is equivalent to the '-no-sound' command line option. + + ---------------- + + Name: FlipStereo + + Argument: Integer. + + Description: Swaps the left and right stereo channels if set to 1. If + set to 0, outputs the channels normally. Disabled by + default. A setting of 1 is equivalent to using the + '-flip-stereo' command line option. + + ---------------- + + Name: MusicVolume + SoundVolume + + Argument: Integer. + + Description: Specifies the volume of MPEG music produced by the Digital + Sound Board and the audio produced by the sound board in + percent. The default is 100, which is full volume, and the + valid range is 0 (muted) to 200%. See the section on audio + settings for more information. The equivalent command line + options are '-music-volume' and '-sound-volume'. + + ---------------- + + Name: ForceFeedback + + Argument: Integer. + + Description: If set to 1, enables force feedback emulation; if set to 0, + disables it (the default behavior). Equivalent to the + '-force-feedback' command line option. Available only on + Windows. + + ---------------- + + Name: DirectInputConstForceMax + DirectInputFrictionMax + DirectInputSelfCenterMax + DirectInputVibrateMax + + Argument: Integer value. + + Description: Sets strength of the four DirectInput force feedback + effects in percent. Default is 100, indicating full + strength. Values exceeding 100% will distort the effects. + Available only on Windows. + + ---------------- + + Name: XInputConstForceMax + XInputVibrateMax + + Argument: Integer value. + + Description: Sets strength of XInput force feedback effects in percent. + Default is 100, indicating full strength. Values exceeding + 100% will distort the effects. The constant force effect + is simulated using vibration. Available only on Windows. + + ---------------- + + Name: XInputConstForceThreshold + + Argument: Integer value. + + Description: Minimum strength above which a Model 3 constant force + command will be simulated on an XInput device. + XInputConstForceMax determines the vibration strength for + this effect. The default value is 30. Available only on + Windows. + + ---------------- + + Names: InputStart1 + InputStart2 + InputCoin1 + InputCoin2 + InputServiceA + InputServiceB + InputTestA + InputTestB + + Argument: String. + + Description: Mappings for common inputs: player 1 and 2 Start buttons, + both coin slots, and both Service and Test buttons. Can + only be set in the 'Global' section. + + ---------------- + + Names: InputJoyDown + InputJoyDown2 + InputJoyLeft + InputJoyLeft2 + InputJoyRight + InputJoyRight2 + InputJoyUp + InputJoyUp2 + + Argument: String. + + Description: Mappings for 4-way digital joysticks (players 1 and 2). + These controls appear in 'Virtua Fighter 3', 'Fighting + Vipers 2', 'Spikeout', etc. Can only be set in the + 'Global' section. + + ---------------- + + Names: InputEscape + InputEscape2 + InputGuard + InputGuard2 + InputKick + InputKick2 + InputPunch + InputPunch2 + + Argument: String. + + Description: Mappings for fighting game buttons (players 1 and 2). Can + only be set in the 'Global' section. + + ---------------- + + Names: InputBeat + InputCharge + InputJump + InputShift + + Argument: String. + + Description: Mappings for 'Spikeout' and 'Spikeout Final Edition' + buttons. Can only be set in the 'Global' section. + + ---------------- + + Name: InputLongPass + InputLongPass2 + InputShortPass + InputShortPass2 + InputShoot + InputShoot2 + + Argument: String. + + Description: Mappings for the 'Virtua Striker 2' series of games + (players 1 and 2). Can only be set in the 'Global' + section. + + ---------------- + + Name: InputSteering + + Argument: String. + + Description: Mapping for the analog steering wheel axis, used in all + driving games. Can only be set in the 'Global' section. + + ---------------- + + Name: InputSteeringLeft + InputSteeringRight + + Argument: String. + + Description: Mappings for digital control of the steering wheel, + intended for users who do not have an analog controller. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputBrake + InputAccelerator + + Argument: String. + + Description: Mappings for brake and accelerator pedals used in driving + games. These are analog axes but can also be mapped to + digital inputs if needed. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputGearShift1 + InputGearShift2 + InputGearShift3 + InputGearShift4 + InputGearShiftN + + Argument: String. + + Description: Mappings for the gear box used in driving games. In games + with only two gears (e.g. 'Le Mans 24'), InputGearShift1 + maps to 'Up' and InputGearShift2 to 'Down'. A neutral + position can also be selected. Can only be set in the + 'Global' section. + + ---------------- + + Name: InputGearShiftDown + InputGearShiftUp + + Argument: String. + + Description: Mappings for sequential shifting. These allow gears to be + selected sequentially: N, 1, 2, 3, 4. Can only be set in + the 'Global' section. + + ---------------- + + Name: InputVR1 + InputVR2 + InputVR3 + InputVR4 + + Argument: String. + + Description: Mappings for the 'VR' (color-coded view select) buttons in + racing games (e.g. 'Scud Race', 'Daytona USA 2', etc.) Can + only be set in the 'Global' section. + + ---------------- + + Name: InputViewChange + + Argument: String. + + Description: Mapping for the view change button used in 'Sega Rally 2', + 'Dirt Devils', and 'Emergency Car Ambulance'. Can only be + set in the 'Global' section. + + ---------------- + + Name: InputHandBrake + + Argument: String. + + Description: Mapping for the hand brake button used in 'Sega Rally 2'. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputTwinJoyCrouch + InputTwinJoyForward + InputTwinJoyJump + InputTwinJoyReverse + InputTwinJoyStrafeLeft + InputTwinJoyStrafeRight + InputTwinJoyTurnLeft + InputTwinJoyTurnRight + + Argument: String. + + Description: Mappings for 'macro' commands for 'Virtual On Oratorio + Tangram'. These allow movements to be mapped to single + digital inputs and do not require the use of two joysticks. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputTwinJoyDown1 + InputTwinJoyDown2 + InputTwinJoyLeft1 + InputTwinJoyLeft2 + InputTwinJoyRight1 + InputTwinJoyRight2 + InputTwinJoyUp1 + InputTwinJoyUp2 + + Argument: String. + + Description: Mappings for the individual joysticks used by 'Virtual On + Oratorio Tangram'. The left stick is stick #1 and the + right stick is #2. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputTwinJoyShot1 + InputTwinJoyShot2 + InputTwinJoyTurbo1 + InputTwinJoyTurbo2 + + Argument: String. + + Description: Mappings for the shot (trigger) and turbo buttons located + on the left (1) and right (2) joysticks in 'Virtual On + Oratorio Tangram'. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputAnalogJoyDown + InputAnalogJoyLeft + InputAnalogJoyRight + InputAnalogJoyUp + + Argument: String. + + Description: Mappings for digital control of the analog joystick used in + 'Star Wars Trilogy'. Two inputs are provided for the two + directions available on each axis. Can only be set in the + 'Global' section. + + ---------------- + + Name: InputAnalogJoyX + InputAnalogJoyY + + Argument: String. + + Description: Mappings for X and Y axes of the analog joystick in 'Star + Wars Trilogy'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputAnalogJoyTrigger + InputAnalogJoyEvent + + Argument: String. + + Description: Mappings for the trigger and Event button in 'Star Wars + Trilogy'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputGunDown + InputGunDown2 + InputGunLeft + InputGunLeft2 + InputGunRight + InputGunRight2 + InputGunUp + InputGunUp2 + + Argument: String. + + Description: Mappings for digital control of the light guns in 'The Lost + World'. Inputs for motion in each direction are provided. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputGunX + InputGunX2 + InputGunY + InputGunY2 + + Argument: String. + + Description: Mappings for both analog axes of the light guns in 'The + Lost World'. Can only be set in the 'Global' section. + + ---------------- + + Name: InputOffscreen + InputOffscreen2 + InputTrigger + InputTrigger2 + + Argument: String. + + Description: Mappings for the light gun triggers and off-screen + controls in 'The Lost World'. The off-screen button aims + the light gun off-screen while it is pressed, which makes + re-loading possible. Can only be set in the 'Global' + section. + + ---------------- + + Name: InputAutoTrigger + InputAutoTrigger2 + + Argument: Integer. + + Description: When set to 1, the off-screen button will also + automatically reload (no need to pull the trigger). Can + only be set in the 'Global' section. + + ---------------- + + Name: InputKeySensitivity + + Argument: Integer. + + Description: The rate at which analog control values increase/decrease + when controlled by a key. Valid range is 1-100, with 1 + being the least sensitive (slowest response). The default + is 25. Can only be set in the 'Global' section. + + ---------------- + + Name: InputKeyDecaySpeed + + Argument: Integer. + + Description: The rate at which analog control values return to their + rest or 'off' position after a key has been released. The + value is expressed as a percentage of the attack speed, + ranging from 1-200. The default is 50, meaning analog + controls take twice as long to return to their rest state + as they do to reach their minimum/maximum values. Can only + be set in the 'Global' section. + + ---------------- + + Name: InputMouseXDeadZone + InputMouseYDeadZone + + Argument: Integer. + + Description: Specifies a rectangular region in the center of the display + within which the mouse is considered inactive. Expressed + as percentages, from 0-99, of the width and height of the + display. The default values are 0, which disable this + option. Can only be set in the 'Global' section. + + ---------------- + + Name: InputJoy**DeadZone + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the dead zone as a + percentage, 0-99, of the total range of the axis. Within + the dead zone, the joystick is inactive. This is useful + for joysticks that are 'noisy' when at rest. Can only be + set in the 'Global' section. + + ---------------- + + Name: InputJoy**Saturation + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the saturation, the + position at which the joystick is interpreted as being in + its most extreme position, as a percentage of the total + range of the axis, from 0-200. Values exceeding 100% mean + the axis will not use its full range. A value of 200 means + that the range will be halved (fully pressed will only + register as 50%). The default is 100, a 1-to-1 mapping. + Can only be set in the 'Global' section. + + ---------------- + + Name: InputJoy**MinVal + InputJoy**MaxVal + InputJoy**OffVal + + Argument: Integer. + + Description: '**' must be the joystick number and axis (eg. '1X', '2RZ', + etc.). If the joystick number is omitted, the setting will + apply to all joysticks. Specifies the maximum, minimum, or + offset value of an axis. The range is -32768 to 32768. By + default, MinVal is -32768, OffVal is 0, and MaxVal is + 32767. Can only be set in the 'Global' section. + + +================================= + 14. Compiling the Source Code +================================= + +First, ensure that OpenGL, SDL (http://www.libsdl.org), and zlib +(http://zlib.net) are installed. GLEW (http://glew.sourceforge.net) is +included in the source tree and does not need to be installed separately. + +Next, extract the Supermodel source code and copy the appropriate Makefile +from the Makefiles/ directory to the base directory (that is, the one above +Src/ and Makefiles/). Makefiles for 32-bit Windows (Microsoft Visual C++ +2008), Linux/UNIX (GCC), and Mac OS X (GCC) are provided, all requiring GNU +Make. For Windows developers, MinGW (http://www.mingw.org) provides GNU Make. +Alternatively, you can write a Makefile compatible with Microsoft Nmake and +submit it for inclusion in the next release. ;) Consult the Visual Studio +documentation to learn how to configure the Microsoft compiler for command line +operation. + +Edit SDL_LIBPATH and SDL_INCLUDEPATH to reflect the location of the SDL +development library and header files. This should only be necessary for +Windows. The UNIX and Mac OS X Makefiles should be able to automatically +locate SDL if it was installed properly. + +On Windows, Supermodel is compiled with the multi-threaded, static version of +the run-time library (/MT option). However, the SDL and zlib development +libraries, and the SDL run-time DLL, are distributed for use with the dynamic +run-time (/MD option). They must all be recompiled using /MT to work with +Supermodel. Alternatively, Supermodel's Makefile can be edited to use the /MD +option. + +When everything is ready, rename the appropriate Makefile to 'Makefile' and run +'make'. If all goes well it should produce a Supermodel binary. + + +=========================== + 15. Contact Information +=========================== + +The official Supermodel web site is: + + http://www.Supermodel3.com + +Questions? Comments? Contributions? Your feedback is welcome! A discussion +forum is available at the web site and the primary author, Bart Trzynadlowski, +can be reached by email at: + + supermodel.emu@gmail.com + +We ask that you remain mindful of the following courtesies: + + - Do NOT ask about ROMs. + - Do NOT request features. + - Do NOT ask about release dates of future versions. + + +======================= + 16. Acknowledgments +======================= + +Numerous people contributed their precious time and energy to this project. +Without them, Supermodel would not have been possible. In no particular order, +we would like to thank: + + - Ville Linde, original Supermodel team member and MAMEDev extraordinaire + - Stefano Teso, original Supermodel team member + - ElSemi, for all sorts of technical information and insight + - R. Belmont, for DSB code and modified Amp library from his music player, + M1, and the Mac OS X port + - Naibo Zhang, for his work on Model 3 graphics + - krom, for adding in the remaining ROM sets + - Andrew Lewis (a.k.a. Andy Geezer), for dumping the drive board ROMs and + providing region codes + - The Guru, for his efforts in dumping Model 3 ROM sets + - Brian Troha & The Dumping Union, for their continuing efforts in adding + new Model 3 ROM sets + - Abelardo Vidal Martos, for providing extremely useful video recordings of + actual Model 3 games + - Andrew Gardner, for fruitful discussion + - Chad Reker, Alex Corrigan, pcvideogamer, and Groni, for being especially + thorough play-testers + - Charles MacDonald, for his helpful description of the System 24 tile + generator + - Andreas Naive, Olivier Galibert, David Haywood & MAME for the interface + to and decryption code for the Sega 315-5881 protection device + +Supermodel includes code from the following projects: + + - zlib: http://zlib.net + - minizip: http://www.winimage.com/zLibDll/minizip.html + - YAZE-AG: http://www.mathematik.uni-ulm.de/users/ag/yaze/ + - Amp by Tomislav Uzalec + - The OpenGL Extension Wrangler Library (GLEW): + http://glew.sourceforge.net + +The OpenGL Extension Wrangler Library +Copyright (C) 2002-2008, Milan Ikits +Copyright (C) 2002-2008, Marcelo E. Magallon +Copyright (C) 2002, Lev Povalahev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* The name of the author may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e62ec04 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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 Lesser General +Public License instead of this License. But first, please read +. diff --git a/Libraries/DIR.txt b/Libraries/DIR.txt new file mode 100644 index 0000000..2374ea9 --- /dev/null +++ b/Libraries/DIR.txt @@ -0,0 +1 @@ +Source code for Third-party libraries needs to be placed in here. diff --git a/Makefiles/Makefile.OSX b/Makefiles/Makefile.OSX new file mode 100644 index 0000000..dba1dd7 --- /dev/null +++ b/Makefiles/Makefile.OSX @@ -0,0 +1,81 @@ +## +## Supermodel +## A Sega Model 3 Arcade Emulator. +## Copyright 2003-2022 The Supermodel Team +## +## This file is part of Supermodel. +## +## Supermodel 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 3 of the License, or (at your option) +## any later version. +## +## Supermodel 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 Supermodel. If not, see . +## + +# +# Makefile.OSX +# +# Makefile for Mac OS X systems using clang. +# + + +############################################################################### +# Build Options +############################################################################### + +DELETE = rm -d -r -f + + +############################################################################### +# Platform Configuration +# +# Edit library and include paths as needed. +############################################################################### + +# +# Must be included first +# +include Makefiles/Options.inc + +# +# Toolchain +# +CC = clang +CXX = clang +LD = clang + +# +# SDL +# + +SDL_CFLAGS = +SDL_LIBS = -framework SDL2 -framework AGL -framework OpenGL -framework GLUT -framework Cocoa +ifeq ($(strip $(NET_BOARD)),1) + SDL_LIBS += -framework SDL2_net +endif + +# +# OSX-specific +# + +PLATFORM_CXXFLAGS = $(SDL_CFLAGS) -DSUPERMODEL_OSX -F/Library/Frameworks/ -O3 +PLATFORM_LDFLAGS = $(SDL_LIBS) -lz -lm -lstdc++ -F/Library/Frameworks/ + + +############################################################################### +# Core Makefile +############################################################################### + +include Makefiles/Rules.inc + +clean: + $(SILENT)echo Cleaning up \"$(BIN_DIR)\" and \"$(OBJ_DIR)\"... + $(SILENT)$(DELETE) $(BIN_DIR) + $(SILENT)$(DELETE) $(OBJ_DIR) diff --git a/Makefiles/Makefile.UNIX b/Makefiles/Makefile.UNIX new file mode 100644 index 0000000..358d4f4 --- /dev/null +++ b/Makefiles/Makefile.UNIX @@ -0,0 +1,78 @@ +## +## Supermodel +## A Sega Model 3 Arcade Emulator. +## Copyright 2003-2022 The Supermodel Team +## +## This file is part of Supermodel. +## +## Supermodel 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 3 of the License, or (at your option) +## any later version. +## +## Supermodel 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 Supermodel. If not, see . +## + +# +# Makefile.UNIX +# +# Makefile for UNIX/Linux systems. +# + + +############################################################################### +# Build Options +############################################################################### + +DELETE = rm -d -r -f + + +############################################################################### +# Platform Configuration +# +# Edit library and include paths as needed. +############################################################################### + +# +# Must be included first +# +include Makefiles/Options.inc + +# +# Toolchain +# +CC = gcc +CXX = g++ +LD = gcc + +# +# SDL +# + +SDL2_CFLAGS = `sdl2-config --cflags` +SDL2_LIBS = `sdl2-config --libs` + +# +# UNIX-specific +# + +PLATFORM_CXXFLAGS = $(SDL2_CFLAGS) -O3 +PLATFORM_LDFLAGS = $(SDL2_LIBS) -lGL -lGLU -lz -lm -lstdc++ -lpthread -lSDL2_net -ldl + + +############################################################################### +# Core Makefile +############################################################################### + +include Makefiles/Rules.inc + +clean: + $(SILENT)echo Cleaning up \"$(BIN_DIR)\" and \"$(OBJ_DIR)\"... + $(SILENT)$(DELETE) $(BIN_DIR) + $(SILENT)$(DELETE) $(OBJ_DIR) diff --git a/Makefiles/Makefile.Win32 b/Makefiles/Makefile.Win32 new file mode 100644 index 0000000..3945411 --- /dev/null +++ b/Makefiles/Makefile.Win32 @@ -0,0 +1,115 @@ +## +## Supermodel +## A Sega Model 3 Arcade Emulator. +## Copyright 2003-2022 The Supermodel Team +## +## This file is part of Supermodel. +## +## Supermodel 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 3 of the License, or (at your option) +## any later version. +## +## Supermodel 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 Supermodel. If not, see . +## + +# +# Makefile.Win32 +# +# Makefile for Windows systems using gcc and the standard Command Prompt. +# +# Set variables in the "Configuration" section as required by your system +# configuration. +# + + +############################################################################### +# Configuration +# +# Edit as necessary. Some users may need to edit the "Platform Configuration" +# section as well, namely SDL2_LIBS and PLATFORM_LIBS. +############################################################################### + +# +# Must be included first +# +include Makefiles/Options.inc + +# +# Bitness of build ('32' or '64') +# +BITS = 64 + +# +# Path to SDL2 +# +SDL2_INCLUDE_DIR = \msys64\mingw64\include\SDL2 +SDL2_LIB_DIR = \msys64\mingw64\lib + +# +# Toolchain +# +CC = gcc +CXX = g++ +LD = g++ + + +############################################################################### +# Platform Configuration +############################################################################### + +# Safety check: can be only '32' or '64' otherwise defaults to '64' +ifneq ($(filter $(strip $(BITS)),32 64),$(strip $(BITS))) + override BITS = 64 +endif + +# +# Use Bash or Windows Prompt shell commands? +# +DELETE = rm -d -r -f +ifeq ($(strip $(MSYSTEM)),) + DELETE = rmdir /s /q +endif + +# +# SDL2 +# +SDL2_LIBS = -lmingw32 -lSDL2main -lSDL2 -Wl,--no-undefined -lshell32 -lsetupapi -ladvapi32 -luuid -lversion -limm32 -lwinmm -lgdi32 -luser32 -lm -pipe +SDL2_CFLAGS = +ifeq ($(strip $(NET_BOARD)),1) + SDL2_LIBS += -lSDL2_net -liphlpapi +endif + +# +# MinGW/Windows-specific +# + +PLATFORM_INCLUDE_DIR = $(SDL2_INCLUDE_DIR) +PLATFORM_LIB_DIR = $(SDL2_LIB_DIR) +PLATFORM_LIBS = -ldxerr8 -ldinput8 -lglu32 -lole32 -loleaut32 -lopengl32 -lwbemuuid -lws2_32 -lz +PLATFORM_CXXFLAGS = $(SDL2_CFLAGS) -DSUPERMODEL_WIN32 $(addprefix -I,$(sort $(PLATFORM_INCLUDE_DIR))) +PLATFORM_LDFLAGS = -static -L$(sort $(PLATFORM_LIB_DIR)) $(SDL2_LIBS) $(PLATFORM_LIBS) + + +############################################################################### +# Core Makefile +############################################################################### + +PLATFORM_SRC_FILES = \ + Src/OSD/Windows/DirectInputSystem.cpp \ + Src/OSD/Windows/WinOutputs.cpp + +.PHONY: clean + +include Makefiles/Rules.inc + +clean: + $(SILENT)echo Cleaning up $(BIN_DIR) and $(OBJ_DIR)... + $(SILENT)$(DELETE) $(BIN_DIR) + $(SILENT)$(DELETE) $(OBJ_DIR) diff --git a/Makefiles/Options.inc b/Makefiles/Options.inc new file mode 100644 index 0000000..ec2fe37 --- /dev/null +++ b/Makefiles/Options.inc @@ -0,0 +1,67 @@ +## +## Supermodel +## A Sega Model 3 Arcade Emulator. +## Copyright 2003-2022 The Supermodel Team +## +## This file is part of Supermodel. +## +## Supermodel 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 3 of the License, or (at your option) +## any later version. +## +## Supermodel 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 Supermodel. If not, see . +## + +# +# Options.inc +# +# Build options. +# + + +############################################################################### +# Build Options +############################################################################### + +# +# Verbose progression +# +VERBOSE = +ifneq ($(filter $(strip $(VERBOSE)),0 1),$(strip $(VERBOSE))) + override VERBOSE = +endif +SILENT = @ +ifeq ($(strip $(VERBOSE)),1) + SILENT = +endif + +# +# Enable render state analyser for the legacy 3D engine (will slow down emulation!) +# +DEBUG = +ifneq ($(filter $(strip $(DEBUG)),0 1),$(strip $(DEBUG))) + override DEBUG = +endif + +# +# Enable support for Model3 Net Board emulation +# +NET_BOARD = +ifneq ($(filter $(strip $(NET_BOARD)),0 1),$(strip $(NET_BOARD))) + override NET_BOARD = +endif + +# +# Include console-based debugger in emulator ('yes' or 'no') +# +ENABLE_DEBUGGER = +ifneq ($(filter $(strip $(ENABLE_DEBUGGER)),0 1),$(strip $(ENABLE_DEBUGGER))) + override ENABLE_DEBUGGER = +endif \ No newline at end of file diff --git a/Makefiles/Rules.inc b/Makefiles/Rules.inc new file mode 100644 index 0000000..01b8958 --- /dev/null +++ b/Makefiles/Rules.inc @@ -0,0 +1,334 @@ +## +## Supermodel +## A Sega Model 3 Arcade Emulator. +## Copyright 2003-2022 The Supermodel Team +## +## This file is part of Supermodel. +## +## Supermodel 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 3 of the License, or (at your option) +## any later version. +## +## Supermodel 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 Supermodel. If not, see . +## + +# +# Rules.inc +# +# Build rules, included by all (platform-specific) top-level Makefiles. +# + + +############################################################################### +# Output Locations +############################################################################### + +OBJ_DIR = obj$(strip $(BITS)) +BIN_DIR = bin$(strip $(BITS)) + +OUTFILE = supermodel + + +############################################################################### +# Compiler Flags +############################################################################### + +# +# Construct Supermodel build options +# +SUPERMODEL_BUILD_FLAGS = + +# If render state analyser is enabled, need to define DEBUG +ifeq ($(strip $(DEBUG)),1) + SUPERMODEL_BUILD_FLAGS += -DDEBUG +endif + +# If Net Board support is enabled, need to define NET_BOARD +ifeq ($(strip $(NET_BOARD)),1) + SUPERMODEL_BUILD_FLAGS += -DNET_BOARD +endif + +# If Bart's new frame timing is enabled, need to define NEW_FRAME_TIMING +ifeq ($(strip $(NEW_FRAME_TIMING)),1) + SUPERMODEL_BUILD_FLAGS += -DNEW_FRAME_TIMING +endif + +# If built-in debugger enabled, need to define SUPERMODEL_DEBUGGER +ifeq ($(strip $(ENABLE_DEBUGGER)),1) + SUPERMODEL_BUILD_FLAGS += -DSUPERMODEL_DEBUGGER +endif + +# +# Compiler options +# +ARCH = -march=native +OPT = -O3 +WARN = -Wall +CSTD = -std=iso9899:2011 +CXXSTD = -std=c++17 + + +# +# Construct the compiler (C and C++) and linker flags +# +COMMON_CFLAGS = -c $(ARCH) $(OPT) $(WARN) $(addprefix -I,$(sort $(INCLUDE_DIRS))) -DGLEW_STATIC $(SUPERMODEL_BUILD_FLAGS) +CFLAGS = $(COMMON_CFLAGS) $(CSTD) +CXXFLAGS = $(PLATFORM_CXXFLAGS) $(COMMON_CFLAGS) $(CXXSTD) +LDFLAGS = -o $(BIN_DIR)/$(OUTFILE) $(PLATFORM_LDFLAGS) -s + + +############################################################################### +# Source Files +############################################################################### + +SRC_FILES = \ + Src/CPU/PowerPC/PPCDisasm.cpp \ + Src/BlockFile.cpp \ + Src/Pkgs/unzip.cpp \ + Src/Pkgs/ioapi.cpp \ + Src/Model3/93C46.cpp \ + Src/Util/BitRegister.cpp \ + Src/JTAG.cpp \ + Src/Graphics/Legacy3D/Error.cpp \ + Src/Pkgs/glew.cpp \ + Src/Graphics/Shader.cpp \ + Src/Model3/Real3D.cpp \ + Src/Graphics/Legacy3D/Legacy3D.cpp \ + Src/Graphics/Legacy3D/Models.cpp \ + Src/Graphics/Legacy3D/TextureRefs.cpp \ + Src/Graphics/New3D/GLSLShader.cpp \ + Src/Graphics/New3D/R3DFrameBuffers.cpp \ + Src/Graphics/New3D/New3D.cpp \ + Src/Graphics/New3D/Mat4.cpp \ + Src/Graphics/New3D/Model.cpp \ + Src/Graphics/New3D/PolyHeader.cpp \ + Src/Graphics/New3D/Texture.cpp \ + Src/Graphics/New3D/TextureSheet.cpp \ + Src/Graphics/New3D/VBO.cpp \ + Src/Graphics/New3D/Vec.cpp \ + Src/Graphics/New3D/R3DShader.cpp \ + Src/Graphics/New3D/R3DFloat.cpp \ + Src/Graphics/New3D/R3DScrollFog.cpp \ + Src/Graphics/Render2D.cpp \ + Src/Model3/TileGen.cpp \ + Src/Model3/Model3.cpp \ + Src/CPU/PowerPC/ppc.cpp \ + Src/OSD/SDL/Main.cpp \ + Src/OSD/SDL/Audio.cpp \ + Src/OSD/SDL/Thread.cpp \ + Src/Model3/SoundBoard.cpp \ + Src/Sound/SCSP.cpp \ + Src/Sound/SCSPDSP.cpp \ + Src/CPU/68K/68K.cpp \ + $(OBJ_DIR)/m68kcpu.c \ + $(OBJ_DIR)/m68kopnz.c \ + $(OBJ_DIR)/m68kopdm.c \ + $(OBJ_DIR)/m68kopac.c \ + $(OBJ_DIR)/m68kops.c \ + Src/Model3/DSB.cpp \ + Src/CPU/Z80/Z80.cpp \ + Src/Model3/IRQ.cpp \ + Src/Model3/53C810.cpp \ + Src/Model3/PCI.cpp \ + Src/Model3/RTC72421.cpp \ + Src/Model3/DriveBoard/DriveBoard.cpp \ + Src/Model3/DriveBoard/WheelBoard.cpp \ + Src/Model3/DriveBoard/JoystickBoard.cpp \ + Src/Model3/DriveBoard/SkiBoard.cpp \ + Src/Model3/DriveBoard/BillBoard.cpp \ + Src/Model3/MPC10x.cpp \ + Src/Inputs/Input.cpp \ + Src/Inputs/Inputs.cpp \ + Src/Inputs/InputSource.cpp \ + Src/Inputs/InputSystem.cpp \ + Src/Inputs/InputTypes.cpp \ + Src/Inputs/Linux_evdev.c \ + Src/Inputs/X11_xinput2.c \ + Src/Inputs/Manymouse.c \ + Src/Inputs/MultiInputSource.cpp \ + Src/OSD/SDL/SDLInputSystem.cpp \ + Src/OSD/Outputs.cpp \ + Src/Sound/MPEG/MpegAudio.cpp \ + Src/Model3/Crypto.cpp \ + Src/OSD/Logger.cpp \ + Src/Util/Format.cpp \ + Src/Util/NewConfig.cpp \ + Src/Util/ByteSwap.cpp \ + Src/Util/ConfigBuilders.cpp \ + Src/GameLoader.cpp \ + Src/Pkgs/tinyxml2.cpp \ + Src/ROMSet.cpp \ + $(PLATFORM_SRC_FILES) + +ifeq ($(strip $(NET_BOARD)),1) + SRC_FILES += \ + Src/Network/TCPReceive.cpp \ + Src/Network/TCPSend.cpp \ + Src/Network/NetBoard.cpp \ + Src/Network/SimNetBoard.cpp +endif + +ifeq ($(strip $(ENABLE_DEBUGGER)),1) + SRC_FILES += \ + Src/Debugger/Debugger.cpp \ + Src/Debugger/ConsoleDebugger.cpp \ + Src/Debugger/SupermodelDebugger.cpp \ + Src/Debugger/CPUDebug.cpp \ + Src/Debugger/AddressTable.cpp \ + Src/Debugger/Breakpoint.cpp \ + Src/Debugger/CodeAnalyser.cpp \ + Src/Debugger/Exception.cpp \ + Src/Debugger/Interrupt.cpp \ + Src/Debugger/DebuggerIO.cpp \ + Src/Debugger/Label.cpp \ + Src/Debugger/Register.cpp \ + Src/Debugger/Watch.cpp \ + Src/Debugger/CPU/PPCDebug.cpp \ + Src/Debugger/CPU/68KDebug.cpp \ + Src/Debugger/CPU/Musashi68KDebug.cpp \ + Src/Debugger/CPU/Z80Debug.cpp +endif + +# +# Sorted-path compile order +# +OBJ_FILES = $(foreach file,$(SRC_FILES),$(OBJ_DIR)/$(basename $(notdir $(file))).o) + +# +# Deduce include directories from the source file list. The sort function +# removes duplicates and is used to construct a set. +# +INCLUDE_DIRS = $(sort $(foreach file,$(SRC_FILES),$(dir $(file)))) + + +############################################################################### +# Targets +############################################################################### + +# +# Default target: build Supermodel binary. Must be specified *before* the auto- +# generated dependencies because otherwise, make gets confused for some reason +# and thinks the default target is just one of the object files. +# +all: $(BIN_DIR)/$(OUTFILE) + +# +# Release targets: used only to create official builds stamped with the Git +# version number. Double-colon rules should force ordered execution. +# The automated build script relies on these -- don't modify their output! +# +.PHONY: set_version +.PHONY: print_message +.PHONY: version +release:: set_version +release:: print_message +release:: clean +release:: all +set_version: + $(eval VERSION = $(strip $(subst ',,0.3a-git-$(shell git rev-parse --short HEAD --sq)))) + $(eval SUPERMODEL_BUILD_FLAGS += -DSUPERMODEL_VERSION=\"$(VERSION)\") +print_message: + $(info Building Supermodel Version $(VERSION)) +version: set_version + @echo $(VERSION) + +$(BIN_DIR)/$(OUTFILE): $(BIN_DIR) $(OBJ_DIR) $(OBJ_FILES) + $(info --------------------------------------------------------------------------------) + $(info Linking Supermodel : $(BIN_DIR)/$(OUTFILE)) + $(SILENT)$(LD) $(OBJ_FILES) $(LDFLAGS) + $(info --------------------------------------------------------------------------------) + +$(BIN_DIR): + $(info Creating directory : $(BIN_DIR)) + $(SILENT)mkdir $(BIN_DIR) + +$(OBJ_DIR): + $(info Creating directory : $(OBJ_DIR)) + $(SILENT)mkdir $(OBJ_DIR) + + +############################################################################### +# Rules +############################################################################### + +# +# Create list of auto-generated dependency files (which contain rules that make +# understands) and include them all. +# +AUTODEPS := $(patsubst %.o,%.d,$(OBJ_FILES)) +-include $(AUTODEPS) + +# +# VPATH is the search path for files. This trick allows a single %.cpp rule to +# be matched against files in sub-directories of Src/. +# +VPATH = $(INCLUDE_DIRS) + +# +# Compilation rules that both auto-generate the dependency files and compile +# the source code. This technique is described in the reply by user "rr-" at: +# https://stackoverflow.com/questions/8025766/makefile-auto-dependency-generation +# +$(OBJ_DIR)/%.o: %.cpp + $(info Generating dependencies: $< -> $(OBJ_DIR)/$(*F).d) + $(SILENT)$(CXX) -MM -MP -MT $(OBJ_DIR)/$(*F).o -MT $(OBJ_DIR)/$(*F).d $(CXXFLAGS) $< > $(OBJ_DIR)/$(*F).d + $(info Compiling : $< -> $@) + $(SILENT)$(CXX) $(CXXFLAGS) $< -o $@ + +$(OBJ_DIR)/%.o: %.c + $(info Generating dependencies: $< -> $(OBJ_DIR)/$(*F).d) + $(SILENT)$(CC) -MM -MP -MT $(OBJ_DIR)/$(*F).o -MT $(OBJ_DIR)/$(*F).d $(CFLAGS) $< > $(OBJ_DIR)/$(*F).d + $(info Compiling : $< -> $@) + $(SILENT)$(CC) $(CFLAGS) $< -o $@ + + +# +# Musashi 68K emulator +# +# All generated source files are emitted to the object directory. For MSVC, +# INLINE must be redefined as "static __inline", which is the syntax in C mode. +# +# The .exe suffix is absolutely required for native Windows non-MSYS builds! +# + +MUSASHI_OUTFILE = $(OBJ_DIR)/m68kmake.exe # do not remove the .exe suffix! +MUSASHI_CFLAGS = -ISrc/CPU/68K/Musashi -I$(OBJ_DIR) -DINLINE="static inline" -Wno-unused-variable +MUSASHI_LDFLAGS = -o $(MUSASHI_OUTFILE) $(OBJ_DIR)/m68kmake.o -s + +$(MUSASHI_OUTFILE): Src/CPU/68K/Musashi/m68kmake.c Src/CPU/68K/Musashi/m68k_in.c + $(info --------------------------------------------------------------------------------) + $(info Compiling : $< -> $(OBJ_DIR)/m68kmake.o) + $(SILENT)$(CC) $< $(CFLAGS) -o $(OBJ_DIR)/m68kmake.o + $(info Linking : $(MUSASHI_OUTFILE)) + $(SILENT)$(LD) $(MUSASHI_LDFLAGS) + +$(OBJ_DIR)/m68kops.h $(OBJ_DIR)/m68kops.c $(OBJ_DIR)/m68kopac.c $(OBJ_DIR)/m68kopdm.c $(OBJ_DIR)/m68kopnz.c: $(MUSASHI_OUTFILE) Src/CPU/68K/Musashi/m68k_in.c Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h + $(info Generating 68K emulator: $@) + @$(MUSASHI_OUTFILE) $(OBJ_DIR) Src/CPU/68K/Musashi/m68k_in.c + +$(OBJ_DIR)/m68kcpu.o: Src/CPU/68K/Musashi/m68kcpu.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h + $(info Compiling : $< -> $@) + @$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $(OBJ_DIR)/m68kcpu.o + +$(OBJ_DIR)/m68kops.o: $(OBJ_DIR)/m68kops.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE) + $(info Compiling : $< -> $@) + @$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@ + +$(OBJ_DIR)/m68kopac.o: $(OBJ_DIR)/m68kopac.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE) + $(info Compiling : $< -> $@) + @$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@ + +$(OBJ_DIR)/m68kopdm.o: $(OBJ_DIR)/m68kopdm.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE) + $(info Compiling : $< -> $@) + @$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@ + +$(OBJ_DIR)/m68kopnz.o: $(OBJ_DIR)/m68kopnz.c $(OBJ_DIR)/m68kops.h Src/CPU/68K/Musashi/m68k.h Src/CPU/68K/Musashi/m68kconf.h $(MUSASHI_OUTFILE) + $(info Compiling : $< -> $@) + @$(CC) $< $(CFLAGS) $(MUSASHI_CFLAGS) -o $@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0aa5e60 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Super3 +Sega Model 3 emulator for android based on the supermodel emulator diff --git a/Scripts/supermodel_build_bot.py b/Scripts/supermodel_build_bot.py new file mode 100644 index 0000000..670d8ae --- /dev/null +++ b/Scripts/supermodel_build_bot.py @@ -0,0 +1,321 @@ +# +# Supermodel +# A Sega Model 3 Arcade Emulator. +# Copyright 2003-2022 The Supermodel Team +# +# This file is part of Supermodel. +# +# Supermodel 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 3 of the License, or (at your option) +# any later version. +# +# Supermodel 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 Supermodel. If not, see . +# + +# +# supermodel_build_bot.py +# +# Git snapshot build script. Checks latest Git commit against Supermodel3.com +# download page and builds and uploads a snapshot if needed. Intended to be run +# at least once daily as part of an automated job. +# +# Dependencies: +# - MSYS2 installed at c:\msys64 +# - mingw64/mingw-w64-x86_64-gcc package +# - mingw64/mingw-w64-x86_64-make package +# - mingw64/mingw-w64-x86_64-SDL2 package +# - mingw64/mingw-w64-x86_64-SDL2_net package +# - msys/subversion package +# - msys/zip package +# - git +# +# To perform a test run: +# - Download https://supermodel3.com/Download.html to the directory from +# which the script will be run. +# - Run: python update_model3emu_snapshot.py --working-dir=c:\tmp\build --test-run +# + +import argparse +import os +import shutil +import sys +import tempfile + +class CheckoutError(Exception): + pass + +class BuildError(Exception): + pass + +class PackageError(Exception): + pass + +class ParseError(Exception): + pass + +class Bash: + def __init__(self, bash_path): + self.bash_path = bash_path + + def execute(self, working_dir, command, log=False, print_output=False): + import subprocess + working_dir = working_dir.replace("\\", "/") # convert to UNIX-style path + invocation = self.bash_path + " -l -c '" + "cd " + working_dir + " && " + command + "'" + if log: + print("Executing: %s" % invocation) + result = subprocess.run(invocation, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if print_output: + print(result.stdout.decode()) + return result + +def get_web_page(test_run): + if test_run: + with open("Download.html") as fp: + html = fp.read() + else: + import urllib.request + with urllib.request.urlopen("https://supermodel3.com/Download.html") as data: + html = data.read().decode("utf-8") + return html + +def get_comment(line): + comment_begin = line.find("") + if comment_begin >= 0 and comment_end > 0 and comment_end > comment_begin: + return line[comment_begin+4:comment_end].strip() + return None + +def add_new_file(html, filename, version, git_sha, date): + # Scan for: + lines = html.splitlines() + for i in range(len(lines)): + line = lines[i] + text = get_comment(line) + if text and text.startswith("BEGIN_GIT_SNAPSHOTS "): + template = text[len("BEGIN_GIT_SNAPSHOTS "):] + new_row = template.replace("$FILENAME", filename).replace("$VERSION", version).replace("$GIT_SHA", git_sha).replace("$DATE", date) + git_tag = "" % git_sha + lines.insert(i + 1, new_row + " " + git_tag) + html = "\n".join(lines) + return html + raise ParseError("BEGIN_GIT_SNAPSHOTS not found") + +def get_uploaded_git_shas(html): + # Scan for all lines with: + shas = [] + for line in html.splitlines(): + text = get_comment(line) + if text and text.startswith("GIT_SHA"): + tokens = text.split("=") + if len(tokens) == 2: + sha = tokens[1] + shas.append(sha) + else: + raise ParseError("Error parsing GIT_SHA") + shas.reverse() + return shas + +def create_change_log(bash, repo_dir, file_path, uploaded_shas, current_sha): + # Log from first commit after 0.2a release until now + result = bash.execute(working_dir=repo_dir, command="git -c color.ui=never log 06594a5..." + current_sha + " --no-decorate --pretty=\"Commit: %H%nAuthor: %an%nDate: %ad%n%n%w(80,4,4)%B\"") + if result.returncode != 0: + return PackageError("Unable to obtain Git log") + change_log = result.stdout.decode().strip() + with open(file_path, "w", encoding="utf-8") as fp: + header = "\n\n" \ + " #### ### ###\n" \ + " ## ## ## ##\n" \ + " ### ## ## ## ### #### ## ### ## ## #### ## #### ##\n" \ + " ### ## ## ## ## ## ## ### ## ####### ## ## ##### ## ## ##\n" \ + " ### ## ## ## ## ###### ## ## ####### ## ## ## ## ###### ##\n" \ + " ## ## ## ## ##### ## ## ## # ## ## ## ## ## ## ##\n" \ + " #### ### ## ## #### #### ## ## #### ### ## #### ####\n" \ + " ####\n" \ + "\n" \ + " A Sega Model 3 Arcade Emulator.\n" \ + "\n" \ + " Copyright 2003-2022 The Supermodel Team\n" \ + "\n" \ + " CHANGE LOG\n" \ + "\n" \ + "\n" + fp.write(header) + fp.write(change_log) + +def write_html_file(html, file_path): + with open(file_path, "w") as fp: + fp.write(html) + +def upload(html_file, zip_file_path, username, password): + from ftplib import FTP + ftp = FTP("supermodel3.com") + ftp.login(username, password) + ftp.cwd("public_html/Files/Git_Snapshots") + with open(zip_file_path, "rb") as fp: + ftp.storbinary("STOR " + os.path.basename(zip_file_path), fp) + ftp.cwd("../../") + with open(html_file, "rb") as fp: + ftp.storlines("STOR Download.html", fp) + ftp.quit() + +def confirm_package_contents(package_dir, package_files): + all_found = True + for file in package_files: + path = os.path.join(package_dir, file) + if not os.path.exists(path): + print("Missing package file: %s" % path) + all_found = False + if not all_found: + raise PackageError("Failed to generate package files") + +def rmdir(dir): + def delete_readonly(action, name, exc): + # https://stackoverflow.com/questions/2656322/shutil-rmtree-fails-on-windows-with-access-is-denied + import stat + if not os.access(name, os.W_OK): + os.chmod(name, stat.S_IWUSR) + action(name) + for root, dirs, files in os.walk(dir): # we expect .git to be write-protected + if '.git' in dirs: + shutil.rmtree(root+'\.git',onerror=delete_readonly) + shutil.rmtree(dir) + +def update_git_snapshot(working_dir, username, password, test_run, make): + failed = False + print("Starting %s in working directory: %s" % ("test run" if test_run else "release process", working_dir)) + + try: + bash = Bash(bash_path="c:\\msys64\\usr\\bin\\bash.exe") + repo_dir = os.path.join(working_dir, "model3emu") + + # Clone Git repo. Unfortunately need to always do this in order to get short SHA + result = bash.execute(working_dir=working_dir, command="git clone https://github.com/trzy/Supermodel.git model3emu") + if result.returncode != 0: + raise CheckoutError("Git clone failed") + + # Fetch Supermodel download page and compare most recent uploaded Git snapshot against current Git sha + print("Fetching Supermodel download page...") + html = get_web_page(test_run=test_run) + uploaded_shas = get_uploaded_git_shas(html=html) + last_uploaded_sha = uploaded_shas[-1] if len(uploaded_shas) > 0 else None + print("Checking current Git SHA...") + result = bash.execute(working_dir=repo_dir, command="git rev-parse --short HEAD") + if result.returncode != 0: + raise CheckoutError("Git HEAD SHA check failed") + current_sha = result.stdout.decode().strip() + + # Get date of commit + result = bash.execute(working_dir=repo_dir, command="git show -s --format=%as HEAD") + if result.returncode != 0: + raise CheckoutError("Unable to obtain HEAD commit date") + current_date = result.stdout.decode().strip() + + # If Git has a newer version, or if performing a test run, build it and update the web page + if current_sha == last_uploaded_sha and (not test_run): + print("Nothing to do. Current Git SHA already uploaded: %s" % current_sha) + else: + # Check out + print("Git SHA is %s but last uploaded SHA is %s" % (current_sha, last_uploaded_sha)) + + # Build + print("Building Supermodel...") + result = bash.execute(working_dir=repo_dir, command=make + " -f Makefiles/Makefile.Win32 version") + if result.returncode != 0: + raise BuildError("Failed to obtain version") + version = result.stdout.decode().strip() + result = bash.execute(working_dir=repo_dir, command=make + " -f Makefiles/Makefile.Win32 release NET_BOARD=1", print_output=False) + if result.returncode != 0: + raise BuildError("Build failed") + + # Stage the release package files + print("Creating release package...") + pkg_dir = os.path.join(working_dir, "pkg") + bash.execute(working_dir=working_dir, command="mkdir pkg && mkdir pkg/Config && mkdir pkg/NVRAM && mkdir pkg/Saves && mkdir pkg/ROMs") + change_log_file_path = os.path.join(pkg_dir, "CHANGES.txt") + create_change_log(bash, repo_dir=repo_dir, file_path=change_log_file_path, uploaded_shas=uploaded_shas, current_sha=current_sha) + bash.execute(working_dir=working_dir, command="cp model3emu/Config/Supermodel.ini pkg/Config && cp model3emu/Config/Games.xml pkg/Config") + bash.execute(working_dir=working_dir, command="echo NVRAM files go here. >pkg/NVRAM/DIR.txt") + bash.execute(working_dir=working_dir, command="echo Save states go here. >pkg/Saves/DIR.txt") + bash.execute(working_dir=working_dir, command="echo Recommended \\(but not mandatory\\) location for ROM sets. >pkg/ROMs/DIR.txt") + bash.execute(working_dir=working_dir, command="cp model3emu/Docs/README.txt pkg && cp model3emu/Docs/LICENSE.txt pkg") + bash.execute(working_dir=working_dir, command="cp model3emu/bin64/supermodel.exe pkg/Supermodel.exe") + #bash.execute(working_dir=working_dir, command="cp /mingw64/bin/SDL2.dll pkg && cp /mingw64/bin/SDL2_net.dll pkg") + package_files = [ + "Supermodel.exe", + #"SDL2.dll", + #"SDL2_net.dll", + "README.txt", + "LICENSE.txt", + "CHANGES.txt", + "Config/Supermodel.ini", + "Config/Games.xml", + "NVRAM/DIR.txt", + "Saves/DIR.txt", + "ROMs/DIR.txt" + ] + confirm_package_contents(package_dir=pkg_dir, package_files=package_files) + + # Zip them up + print("Compressing...") + zip_file = "Supermodel_" + version + "_Win64.zip" + zip_path = os.path.join(pkg_dir, zip_file) + result = bash.execute(working_dir=pkg_dir, command="zip " + zip_file + " " + " ".join(package_files)) + if result.returncode != 0: + raise PackageError("Failed to compress package") + + # Update the web page + print("Updating Download page HTML...") + html = add_new_file(html=html, filename=zip_file, version=version, git_sha=current_sha, date=current_date) + html_file_path = os.path.join(working_dir, "Download.updated.html") + write_html_file(html=html, file_path=html_file_path) + if test_run: + input("Test run finished. Press Enter to complete clean-up process...") + else: + print("Uploading Download page...") + upload(html_file=html_file_path, zip_file_path=zip_path, username=username, password=password) + + except Exception as e: + print("Error: %s" % str(e)) + failed = True + except: + print("Unknown error: ", sys.exc_info()[0]) + failed = True + + print("Cleaning up...") + rmdir(working_dir) + return failed + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--username", metavar="user", type=str, action="store", help="Supermodel3.com FTP username") + parser.add_argument("--password", metavar="pass", type=str, action="store", help="Supermodel3.com FTP password") + parser.add_argument("--working-dir", metavar="path", type=str, action="store", help="Working directory to use (must not already exist); temporary directory if none specified") + parser.add_argument("--test-run", action="store_true", help="Force a build without uploading and insert a pause") + parser.add_argument("--make", metavar="command", type=str, default="mingw32-make", action="store", help="Make command to use") + options = parser.parse_args() + + max_attempts = 3 + attempt = 1 + + while attempt <= max_attempts: + if options.working_dir: + if os.path.exists(options.working_dir): + raise Exception("Specified working directory (%s) already exists. This script requires a non-existent path that can safely be overwritten and then deleted." % options.working_dir) + os.makedirs(options.working_dir) + failed = update_git_snapshot(working_dir=options.working_dir, username=options.username, password=options.password, test_run=options.test_run, make=options.make) + else: + with tempfile.TemporaryDirectory() as working_dir: + failed = update_git_snapshot(working_dir=working_dir, username=options.username, password=options.password, test_run=options.test_run, make=options.make) + # Retry until success + if not failed: + break + attempt += 1 + if attempt <= max_attempts: + print("Release failed. Retrying (%d/%d)..." % (attempt, max_attempts)) diff --git a/Src/BlockFile.cpp b/Src/BlockFile.cpp new file mode 100644 index 0000000..3ccc0dd --- /dev/null +++ b/Src/BlockFile.cpp @@ -0,0 +1,280 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * BlockFile.cpp + * + * Block format container file management. Implementation of the CBlockFile + * class. + */ + +#include "BlockFile.h" + +#include +#include +#include +#include "Supermodel.h" + + +/****************************************************************************** + Output Functions +******************************************************************************/ + +void CBlockFile::ReadString(std::string *str, uint32_t length) +{ + if (NULL == fp) + return; + str->clear(); + //TODO: use fstream to get rid of this ugly hack + bool keep_loading = true; + for (uint32_t i = 0; i < length; i++) + { + char c; + fread(&c, sizeof(char), 1, fp); + if (keep_loading) + { + if (!c) + keep_loading = false; + else + *str += c; + } + } +} + +unsigned CBlockFile::ReadBytes(void *data, uint32_t numBytes) +{ + if (NULL == fp) + return 0; + return fread(data, sizeof(uint8_t), numBytes, fp); +} + +unsigned CBlockFile::ReadDWord(uint32_t *data) +{ + if (NULL == fp) + return 0; + fread(data, sizeof(uint32_t), 1, fp); + return 4; +} + +void CBlockFile::UpdateBlockSize(void) +{ + long int curPos; + unsigned newBlockSize; + + if (NULL == fp) + return; + curPos = ftell(fp); // save current file position + fseek(fp, blockStartPos, SEEK_SET); + newBlockSize = curPos - blockStartPos; + fwrite(&newBlockSize, sizeof(uint32_t), 1, fp); + fseek(fp, curPos, SEEK_SET); // go back +} + +void CBlockFile::WriteByte(uint8_t data) +{ + if (NULL == fp) + return; + fwrite(&data, sizeof(uint8_t), 1, fp); + UpdateBlockSize(); +} + +void CBlockFile::WriteDWord(uint32_t data) +{ + if (NULL == fp) + return; + fwrite(&data, sizeof(uint32_t), 1, fp); + UpdateBlockSize(); +} + +void CBlockFile::WriteBytes(const void *data, uint32_t numBytes) +{ + if (NULL == fp) + return; + fwrite(data, sizeof(uint8_t), numBytes, fp); + UpdateBlockSize(); +} + +void CBlockFile::WriteBlockHeader(const std::string &name, const std::string &comment) +{ + if (NULL == fp) + return; + + // Record current block starting position + blockStartPos = ftell(fp); + + // Write the total block length field + WriteDWord(0); // will be automatically updated as we write the file + + // Write name and comment lengths + WriteDWord(name.size() + 1); + WriteDWord(comment.size() + 1); + Write(name); + Write(comment); + + // Record the start of the current data section + dataStartPos = ftell(fp); +} + + +/****************************************************************************** + Block Format Container File Implementation + + Files are just a consecutive array of blocks that must be searched. + + Block Format + ------------ + blockLength (uint32_t) Total length of block in bytes. + nameLength (uint32_t) Length of name field including terminating 0 (up to + 1025). + commentLength (uint32_t) Same as above, but for comment string. + name ... Name string (null-terminated, up to 1025 bytes). + comment ... Comment string (same as above). + data ... Raw data (blockLength - total header size). +******************************************************************************/ + +unsigned CBlockFile::Read(void *data, uint32_t numBytes) +{ + if (mode == 'r') + return ReadBytes(data, numBytes); + return 0; +} + +unsigned CBlockFile::Read(bool *value) +{ + uint8_t byte; + unsigned numBytes = Read(&byte, sizeof(byte)); + if (numBytes > 0) + *value = byte != 0; + return numBytes; +} + +void CBlockFile::Write(const void *data, uint32_t numBytes) +{ + if (mode == 'w') + WriteBytes(data, numBytes); +} + +void CBlockFile::Write(bool value) +{ + uint8_t byte = value ? 1 : 0; + Write(&byte, sizeof(byte)); +} + +void CBlockFile::Write(const std::string &str) +{ + if (mode == 'w') + WriteBytes(str.c_str(), str.length() + 1); +} + +void CBlockFile::NewBlock(const std::string &name, const std::string &comment) +{ + if (mode == 'w') + WriteBlockHeader(name, comment); +} + +bool CBlockFile::FindBlock(const std::string &name) +{ + if (mode != 'r') + return FAIL; + + fseek(fp, 0, SEEK_SET); + + long int curPos = 0; + while (curPos < fileSize) + { + blockStartPos = curPos; + + // Read header + uint32_t block_length; + uint32_t name_length; + uint32_t comment_length; + curPos += ReadDWord(&block_length); + curPos += ReadDWord(&name_length); + curPos += ReadDWord(&comment_length); + std::string block_name; + ReadString(&block_name, name_length); + + // Is this the block we want? + if (block_name == name) + { + fseek(fp, blockStartPos + 12 + name_length + comment_length, SEEK_SET); // move to beginning of data + dataStartPos = ftell(fp); + return OKAY; + } + + // Move to next block + fseek(fp, blockStartPos + block_length, SEEK_SET); + curPos = blockStartPos + block_length; + if (block_length == 0) // this would never advance + break; + } + + return FAIL; +} + +bool CBlockFile::Create(const std::string &file, const std::string &headerName, const std::string &comment) +{ + fp = fopen(file.c_str(), "wb"); + if (NULL == fp) + return FAIL; + mode = 'w'; + WriteBlockHeader(headerName, comment); + return OKAY; +} + +bool CBlockFile::Load(const std::string &file) +{ + fp = fopen(file.c_str(), "rb"); + if (NULL == fp) + return FAIL; + mode = 'r'; + + // TODO: is this a valid block file? + + // Get the file size + fseek(fp, 0, SEEK_END); + fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + return OKAY; +} + +void CBlockFile::Close(void) +{ + if (fp != NULL) + fclose(fp); + fp = NULL; + mode = 0; +} + +CBlockFile::CBlockFile(void) +{ + fp = NULL; + mode = 0; // neither reading nor writing (do nothing) +} + +CBlockFile::~CBlockFile(void) +{ + if (fp != NULL) // in case user forgot + fclose(fp); + fp = NULL; + mode = 0; +} diff --git a/Src/BlockFile.h b/Src/BlockFile.h new file mode 100644 index 0000000..f3f7da6 --- /dev/null +++ b/Src/BlockFile.h @@ -0,0 +1,206 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * BlockFile.h + * + * Header file for block format container file management. + */ + +#ifndef INCLUDED_BLOCKFILE_H +#define INCLUDED_BLOCKFILE_H + +#include +#include + +/* + * CBlockFile: + * + * Block format container file. The file format is a series of consecutive, + * variable-length blocks referenced by unique name strings. + * + * All strings (comments and names) will be truncated to 1024 bytes, not + * including the null terminator. + * + * Members do not generate any output messages. + */ +class CBlockFile +{ +public: + /* + * Read(data, numBytes): + * + * Reads data from the current file position. + * + * Parameters: + * data Buffer to read to. + * numBytes Number of bytes to read. + * + * Returns: + * Number of bytes read. If not the same as numBytes, an error + * occurred. + */ + unsigned Read(void *data, uint32_t numBytes); + + /* + * Read(value): + * + * Reads a bool value from the current file position. + * + * Parameters: + * value Bool to read to. + * + * Returns: + * Number of bytes read. If not 1, an error occurred. + */ + unsigned Read(bool *value); + + /* + * FindBlock(name): + * + * Scans the file for a block with the given name string. When it is found, + * the file pointer is set to the beginning of the data region. + * + * Parameters: + * name Name of block to locate. + * + * Returns: + * OKAY if found, FAIL if unable to locate. + */ + bool FindBlock(const std::string &name); + + /* + * Write(value): + * + * Outputs a bool value at the current file pointer position as a byte with + * value either 0 or 1. Updates the block header appropriately. + * + * Parameters: + * value A boolean value to write. + */ + void Write(bool value); + + /* + * Write(data, numBytes): + * + * Outputs data at the current file pointer position. Updates the block + * header appropriately. + * + * Parameters: + * data Data to write. + * numBytes Number of bytes to write. + */ + void Write(const void *data, uint32_t numBytes); + + /* + * Write(str): + * + * Outputs string (including null terminator) at the current file pointer + * position. Updates the block header appropriately. + * + * Parameters: + * str String to write. + */ + void Write(const std::string &str); + + /* + * NewBlock(name, comment): + * + * Begins a new block. Writes the block header and sets the file pointer to + * the beginning of its data area. + * + * Parameters: + * name Block name. Must be unique and not NULL. + * comment Comment string to embed in the block header. + */ + void NewBlock(const std::string &title, const std::string &comment); + + /* + * Create(file, headerName, comment): + * + * Opens a block file for writing and creates the header block. This + * function must be called before attempting to write data. Otherwise, all + * write commands will be silently ignored. Read commands will be ignored + * and will always return 0's. + * + * Parameters: + * file File path. + * headerName Block name for header. Must be unique and not NULL. + * comment Comment string that will be embedded into file header. + * + * Returns: + * OKAY if successfully opened, otherwise FAIL. + */ + bool Create(const std::string &file, const std::string &headerName, const std::string &comment); + + /* + * Load(file): + * + * Open a block file file for reading. + * + * Parameters: + * file File path. + * + * Returns: + * OKAY if successfully opened and confirmed to be a valid Supermodel + * block file, otherwise FAIL. If the file could not be opened, all + * subsequent operations will be silently ignored (reads will return + * 0's). Write commands will be ignored. + */ + bool Load(const std::string &file); + + /* + * Close(void): + * + * Closes the file. + */ + void Close(void); + + /* + * CBlockFile(void): + * ~CBlockFile(void): + * + * Constructor and destructor. + */ + CBlockFile(void); + ~CBlockFile(void); + +private: + // Helper functions + void ReadString(std::string *str, uint32_t length); + unsigned ReadBytes(void *data, uint32_t numBytes); + unsigned ReadDWord(uint32_t *data); + void UpdateBlockSize(void); + void WriteByte(uint8_t data); + void WriteDWord(uint32_t data); + void WriteBytes(const void *data, uint32_t numBytes); + void WriteBlockHeader(const std::string &name, const std::string &comment); + + // File state data + FILE *fp; + int mode; // 'r' for read, 'w' for write + long int fileSize; // size of file in bytes + long int blockStartPos; // points to beginning of current block (or file) header + long int dataStartPos; // points to beginning of current block's data section +}; + + +#endif // INCLUDED_BLOCKFILE_H diff --git a/Src/CPU/68K/68K.cpp b/Src/CPU/68K/68K.cpp new file mode 100644 index 0000000..496f6c5 --- /dev/null +++ b/Src/CPU/68K/68K.cpp @@ -0,0 +1,451 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * 68K.cpp + * + * 68K CPU interface. This is presently just a wrapper for the Musashi 68K core + * and therefore, only a single CPU is supported. In the future, we may want to + * add in another 68K core (eg., Turbo68K, A68K, or a recompiler). + * + * To-Do List + * ---------- + * - Registers may not completely describe the 68K state. Musashi also has + * additional CPU state information in the context that its MAME interface + * accesses (interrupts pending, halted status, etc.) + */ + +#include "68K.h" + +#include "Supermodel.h" +#include "Musashi/m68k.h" // Musashi 68K core +#include "Debugger/CPU/Musashi68KDebug.h" + +/****************************************************************************** + Internal Context + + An active context must be mapped before calling M68K interface functions. Only + the bus and IRQ handlers are copied here; the CPU context is passed directly + to Musashi. +******************************************************************************/ + +// Bus +static IBus *s_Bus = NULL; + +#ifdef SUPERMODEL_DEBUGGER +// Debugger +static Debugger::CMusashi68KDebug *s_Debug = NULL; +#endif + +// IRQ callback +static int (*IRQAck)(int nIRQ) = NULL; + +// Cycles remaining in timeslice +static int s_lastCycles; + + +/****************************************************************************** + 68K Interface +******************************************************************************/ + +// CPU state + +UINT32 M68KGetARegister(int n) +{ + m68k_register_t r; + + switch (n) + { + case 0: r = M68K_REG_A0; break; + case 1: r = M68K_REG_A1; break; + case 2: r = M68K_REG_A2; break; + case 3: r = M68K_REG_A3; break; + case 4: r = M68K_REG_A4; break; + case 5: r = M68K_REG_A5; break; + case 6: r = M68K_REG_A6; break; + case 7: r = M68K_REG_A7; break; + default: r = M68K_REG_A7; break; + } + + return m68k_get_reg(NULL, r); +} + +UINT32 M68KGetDRegister(int n) +{ + m68k_register_t r; + + switch (n) + { + case 0: r = M68K_REG_D0; break; + case 1: r = M68K_REG_D1; break; + case 2: r = M68K_REG_D2; break; + case 3: r = M68K_REG_D3; break; + case 4: r = M68K_REG_D4; break; + case 5: r = M68K_REG_D5; break; + case 6: r = M68K_REG_D6; break; + case 7: r = M68K_REG_D7; break; + default: r = M68K_REG_D7; break; + } + + return m68k_get_reg(NULL, r); +} + +UINT32 M68KGetPC(void) +{ + return m68k_get_reg(NULL, M68K_REG_PC); +} + +void M68KSaveState(CBlockFile *StateFile, const char *name) +{ + StateFile->NewBlock(name, __FILE__); + + /* + * Rather than writing the context directly, the get/set register + * functions are used, ensuring that all context members are packed/ + * unpacked correctly. + * + * Note: Some of these are undoubtedly 68010 or 68020 registers and not + * really necessary. But if the layout is changed now, the save state + * version has to be changed, so don't do it! + */ + + UINT32 data[34]; + m68ki_cpu_core Ctx; + + m68k_get_context(&Ctx); + + data[0] = Ctx.int_level; + data[1] = Ctx.int_cycles; + data[2] = Ctx.stopped; + data[3] = m68k_get_reg(NULL, M68K_REG_D0); + data[4] = m68k_get_reg(NULL, M68K_REG_D1); + data[5] = m68k_get_reg(NULL, M68K_REG_D2); + data[6] = m68k_get_reg(NULL, M68K_REG_D3); + data[7] = m68k_get_reg(NULL, M68K_REG_D4); + data[8] = m68k_get_reg(NULL, M68K_REG_D5); + data[9] = m68k_get_reg(NULL, M68K_REG_D6); + data[10] = m68k_get_reg(NULL, M68K_REG_D7); + data[11] = m68k_get_reg(NULL, M68K_REG_A0); + data[12] = m68k_get_reg(NULL, M68K_REG_A1); + data[13] = m68k_get_reg(NULL, M68K_REG_A2); + data[14] = m68k_get_reg(NULL, M68K_REG_A3); + data[15] = m68k_get_reg(NULL, M68K_REG_A4); + data[16] = m68k_get_reg(NULL, M68K_REG_A5); + data[17] = m68k_get_reg(NULL, M68K_REG_A6); + data[18] = m68k_get_reg(NULL, M68K_REG_A7); + data[19] = m68k_get_reg(NULL, M68K_REG_PC); + data[20] = m68k_get_reg(NULL, M68K_REG_SR); + data[21] = m68k_get_reg(NULL, M68K_REG_SP); + data[22] = m68k_get_reg(NULL, M68K_REG_USP); + data[23] = m68k_get_reg(NULL, M68K_REG_ISP); + data[24] = m68k_get_reg(NULL, M68K_REG_MSP); + data[25] = m68k_get_reg(NULL, M68K_REG_SFC); + data[26] = m68k_get_reg(NULL, M68K_REG_DFC); + data[27] = m68k_get_reg(NULL, M68K_REG_VBR); + data[28] = m68k_get_reg(NULL, M68K_REG_CACR); + data[29] = m68k_get_reg(NULL, M68K_REG_CAAR); + data[30] = m68k_get_reg(NULL, M68K_REG_PREF_ADDR); + data[31] = m68k_get_reg(NULL, M68K_REG_PREF_DATA); + data[32] = m68k_get_reg(NULL, M68K_REG_PPC); + data[33] = m68k_get_reg(NULL, M68K_REG_IR); + + StateFile->Write(data, sizeof(data)); +} + +void M68KLoadState(CBlockFile *StateFile, const char *name) +{ + if (OKAY != StateFile->FindBlock(name)) + { + ErrorLog("Unable to load 68K state. Save state file is corrupt."); + return; + } + + UINT32 data[34]; + m68ki_cpu_core Ctx; + + StateFile->Read(data, sizeof(data)); + + // These must be set first, to ensure another contexts' IRQs aren't active when PC is changed + m68k_get_context(&Ctx); + Ctx.int_level = data[0]; + Ctx.int_cycles = data[1]; + Ctx.stopped = data[2]; + m68k_set_context(&Ctx); // write them back to context + m68k_set_reg(M68K_REG_D0, data[3]); + m68k_set_reg(M68K_REG_D1, data[4]); + m68k_set_reg(M68K_REG_D2, data[5]); + m68k_set_reg(M68K_REG_D3, data[6]); + m68k_set_reg(M68K_REG_D4, data[7]); + m68k_set_reg(M68K_REG_D5, data[8]); + m68k_set_reg(M68K_REG_D6, data[9]); + m68k_set_reg(M68K_REG_D7, data[10]); + m68k_set_reg(M68K_REG_A0, data[11]); + m68k_set_reg(M68K_REG_A1, data[12]); + m68k_set_reg(M68K_REG_A2, data[13]); + m68k_set_reg(M68K_REG_A3, data[14]); + m68k_set_reg(M68K_REG_A4, data[15]); + m68k_set_reg(M68K_REG_A5, data[16]); + m68k_set_reg(M68K_REG_A6, data[17]); + m68k_set_reg(M68K_REG_A7, data[18]); + m68k_set_reg(M68K_REG_PC, data[19]); + m68k_set_reg(M68K_REG_SR, data[20]); + m68k_set_reg(M68K_REG_SP, data[21]); + m68k_set_reg(M68K_REG_USP, data[22]); + m68k_set_reg(M68K_REG_ISP, data[23]); + m68k_set_reg(M68K_REG_MSP, data[24]); + m68k_set_reg(M68K_REG_SFC, data[25]); + m68k_set_reg(M68K_REG_DFC, data[26]); + m68k_set_reg(M68K_REG_VBR, data[27]); + m68k_set_reg(M68K_REG_CACR, data[28]); + m68k_set_reg(M68K_REG_CAAR, data[29]); + m68k_set_reg(M68K_REG_PREF_ADDR, data[30]); + m68k_set_reg(M68K_REG_PREF_DATA, data[31]); + m68k_set_reg(M68K_REG_PPC, data[32]); + m68k_set_reg(M68K_REG_IR, data[33]); +} + +// Emulation functions + +void M68KSetIRQ(int irqLevel) +{ + m68k_set_irq(irqLevel); +} + +int M68KRun(int numCycles) +{ +#ifdef SUPERMODEL_DEBUGGER + if (s_Debug != NULL) + { + s_Debug->CPUActive(); + s_lastCycles += numCycles; + } +#endif // SUPERMODEL_DEBUGGER + int doneCycles = m68k_execute(numCycles); +#ifdef SUPERMODEL_DEBUGGER + if (s_Debug != NULL) + { + s_Debug->CPUInactive(); + s_lastCycles -= m68k_cycles_remaining(); + } +#endif // SUPERMODEL_DEBUGGER + return doneCycles; +} + +void M68KReset(void) +{ + m68k_pulse_reset(); +#ifdef SUPERMODEL_DEBUGGER + s_lastCycles = 0; +#endif + DebugLog("68K reset\n"); +} + +// Callback setup + +void M68KSetIRQCallback(int (*F)(int nIRQ)) +{ + IRQAck = F; +} + +void M68KAttachBus(IBus *BusPtr) +{ + s_Bus = BusPtr; + DebugLog("Attached bus to 68K\n"); +} + +// Context switching + +void M68KGetContext(M68KCtx *Dest) +{ + Dest->IRQAck = IRQAck; + Dest->Bus = s_Bus; +#ifdef SUPERMODEL_DEBUGGER + Dest->Debug = s_Debug; +#endif // SUPERMODEL_DEBUGGER + m68k_get_context(&(Dest->musashiCtx)); +} + +void M68KSetContext(M68KCtx *Src) +{ + IRQAck = Src->IRQAck; + s_Bus = Src->Bus; +#ifdef SUPERMODEL_DEBUGGER + s_Debug = Src->Debug; +#endif // SUPERMODEL_DEBUGGER + m68k_set_context(&(Src->musashiCtx)); +} + +// One-time initialization + +bool M68KInit(void) +{ + m68k_init(); + m68k_set_cpu_type(M68K_CPU_TYPE_68000); + m68k_set_int_ack_callback(M68KIRQCallback); + s_Bus = NULL; +#ifdef SUPERMODEL_DEBUGGER + s_Debug = NULL; +#endif // SUPERMODEL_DEBUGGER + DebugLog("Initialized 68K\n"); + return OKAY; +} + +#ifdef SUPERMODEL_DEBUGGER +UINT32 M68KGetRegister(M68KCtx *Src, unsigned reg) +{ + switch (reg) + { + case DBG68K_REG_PC: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_PC); + case DBG68K_REG_SR: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_SR); + case DBG68K_REG_SP: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_SP); + case DBG68K_REG_D0: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D0); + case DBG68K_REG_D1: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D1); + case DBG68K_REG_D2: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D2); + case DBG68K_REG_D3: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D3); + case DBG68K_REG_D4: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D4); + case DBG68K_REG_D5: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D5); + case DBG68K_REG_D6: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D6); + case DBG68K_REG_D7: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_D7); + case DBG68K_REG_A0: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A0); + case DBG68K_REG_A1: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A1); + case DBG68K_REG_A2: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A2); + case DBG68K_REG_A3: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A3); + case DBG68K_REG_A4: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A4); + case DBG68K_REG_A5: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A5); + case DBG68K_REG_A6: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A6); + case DBG68K_REG_A7: return m68k_get_reg(&(Src->musashiCtx), M68K_REG_A7); + default: return 0; + } +} + +UINT32 M68KSetRegister(M68KCtx *Src, unsigned reg, UINT32 val) +{ + switch (reg) + { + case DBG68K_REG_PC: m68k_set_reg(M68K_REG_PC, val); return true; + case DBG68K_REG_SR: m68k_set_reg(M68K_REG_SR, val); return true; + case DBG68K_REG_SP: m68k_set_reg(M68K_REG_SP, val); return true; + case DBG68K_REG_D0: m68k_set_reg(M68K_REG_D0, val); return true; + case DBG68K_REG_D1: m68k_set_reg(M68K_REG_D1, val); return true; + case DBG68K_REG_D2: m68k_set_reg(M68K_REG_D2, val); return true; + case DBG68K_REG_D3: m68k_set_reg(M68K_REG_D3, val); return true; + case DBG68K_REG_D4: m68k_set_reg(M68K_REG_D4, val); return true; + case DBG68K_REG_D5: m68k_set_reg(M68K_REG_D5, val); return true; + case DBG68K_REG_D6: m68k_set_reg(M68K_REG_D6, val); return true; + case DBG68K_REG_D7: m68k_set_reg(M68K_REG_D7, val); return true; + case DBG68K_REG_A0: m68k_set_reg(M68K_REG_A0, val); return true; + case DBG68K_REG_A1: m68k_set_reg(M68K_REG_A1, val); return true; + case DBG68K_REG_A2: m68k_set_reg(M68K_REG_A2, val); return true; + case DBG68K_REG_A3: m68k_set_reg(M68K_REG_A3, val); return true; + case DBG68K_REG_A4: m68k_set_reg(M68K_REG_A4, val); return true; + case DBG68K_REG_A5: m68k_set_reg(M68K_REG_A5, val); return true; + case DBG68K_REG_A6: m68k_set_reg(M68K_REG_A6, val); return true; + case DBG68K_REG_A7: m68k_set_reg(M68K_REG_A7, val); return true; + default: return false; + } +} +#endif // SUPERMODEL_DEBUGGER + +/****************************************************************************** + Musashi 68K Handlers + + Musashi/m68kconf.h has been configured to call these directly. +******************************************************************************/ + +extern "C" { + +#ifdef SUPERMODEL_DEBUGGER +void M68KDebugCallback() +{ + if (s_Debug != NULL) + { + UINT32 pc = m68k_get_reg(NULL, M68K_REG_PC); + UINT32 opcode = s_Bus->Read16(pc); + s_Debug->CPUExecute(pc, opcode, s_lastCycles - m68k_cycles_remaining()); + s_lastCycles = m68k_cycles_remaining(); + } +} +#endif // SUPERMODEL_DEBUGGER + +int M68KIRQCallback(int nIRQ) +{ +#ifdef SUPERMODEL_DEBUGGER + if (s_Debug != NULL) + { + s_Debug->CPUException(25); + s_Debug->CPUInterrupt(nIRQ - 1); + } +#endif // SUPERMODEL_DEBUGGER + if (NULL == IRQAck) // no handler, use default behavior + { + m68k_set_irq(0); // clear line + return M68K_IRQ_AUTOVECTOR; + } + else + return IRQAck(nIRQ); +} + +unsigned int FASTCALL M68KFetch8(unsigned int a) +{ + return s_Bus->Read8(a); +} + +unsigned int FASTCALL M68KFetch16(unsigned int a) +{ + return s_Bus->Read16(a); +} + +unsigned int FASTCALL M68KFetch32(unsigned int a) +{ + return s_Bus->Read32(a); +} + +unsigned int FASTCALL M68KRead8(unsigned int a) +{ + return s_Bus->Read8(a); +} + +unsigned int FASTCALL M68KRead16(unsigned int a) +{ + return s_Bus->Read16(a); +} + +unsigned int FASTCALL M68KRead32(unsigned int a) +{ + return s_Bus->Read32(a); +} + +void FASTCALL M68KWrite8(unsigned int a, unsigned int d) +{ + s_Bus->Write8(a, d); +} + +void FASTCALL M68KWrite16(unsigned int a, unsigned int d) +{ + s_Bus->Write16(a, d); +} + +void FASTCALL M68KWrite32(unsigned int a, unsigned int d) +{ + s_Bus->Write32(a, d); +} + +} // extern "C" diff --git a/Src/CPU/68K/68K.h b/Src/CPU/68K/68K.h new file mode 100644 index 0000000..09d7b9c --- /dev/null +++ b/Src/CPU/68K/68K.h @@ -0,0 +1,393 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * 68K.h + * + * Header file for 68K CPU interface. Caution: 68K emulator is not thread-safe. + * + * TO-DO List: + * ----------- + * - Optimize things, perhaps by using FASTCALL + */ + +#ifndef INCLUDED_68K_H +#define INCLUDED_68K_H + +#include +#include "Types.h" +#include "Musashi/m68k.h" +#include "Musashi/m68kctx.h" +#include "CPU/Bus.h" +#include "BlockFile.h" + +// This doesn't work for now (needs to be added to the prototypes in m68k.h for m68k_read_memory*) +//#ifndef FASTCALL + #undef FASTCALL + #define FASTCALL +//#endif + +#ifdef SUPERMODEL_DEBUGGER +namespace Debugger { + class CMusashi68KDebug; +} +#endif // SUPERMODEL_DEBUGGER + +/****************************************************************************** + Definitions +******************************************************************************/ + +// IRQ callback special return values +#define M68K_IRQ_AUTOVECTOR M68K_INT_ACK_AUTOVECTOR // signals an autovectored interrupt +#define M68K_IRQ_SPURIOUS M68K_INT_ACK_SPURIOUS // signals a spurious interrupt + + +/****************************************************************************** + CPU Context +******************************************************************************/ + +/* + * M68KCtx: + * + * Complete state of a single 68K. Do NOT manipulate these directly. Set the + * context and then use the M68K* functions below to attach a bus and IRQ + * callback to the active context. + */ +typedef struct SM68KCtx +{ +public: + m68ki_cpu_core musashiCtx; // CPU context + IBus *Bus; // memory handlers + int (*IRQAck)(int); // IRQ acknowledge callback +#ifdef SUPERMODEL_DEBUGGER + Debugger::CMusashi68KDebug *Debug; // holds debugger (if attached) +#endif // SUPERMODEL_DEBUGGER + + SM68KCtx(void) + { + Bus = NULL; + IRQAck = NULL; + memset(&musashiCtx, 0, sizeof(musashiCtx)); // very important! garbage in context at reset can cause very strange bugs +#ifdef SUPERMODEL_DEBUGGER + Debug = NULL; +#endif // SUPERMODEL_DEBUGGER + } + + ~SM68KCtx(void) + { + Bus = NULL; + IRQAck = NULL; + } +} M68KCtx;; + + +/****************************************************************************** + 68K Interface + + Unless otherwise noted, all functions operate on the active context. +******************************************************************************/ + +/* + * M68KGetARegister(n): + * + * Parameters: + * n Register number (0-7). + * + * Returns: + * Register An. + */ +extern UINT32 M68KGetARegister(int n); + +/* + * M68KSetARegister(n, v): + * + * Parameters: + * n Register number (0-7). + * v Value to set register An to. + */ +extern void M68KSetARegister(int n, UINT32 v); + +/* + * M68KGetDRegister(n): + * + * Parameters: + * n Register number (0-7). + * + * Returns: + * Register Dn. + */ +extern UINT32 M68KGetDRegister(int n); + +/* + * M68KGetDRegister(n, v): + * + * Parameters: + * n Register number (0-7). + * v Value to set register Dn to. + */ +extern void M68KSetDRegister(int n, UINT32 v); + +/* + * M68KGetPC(): + * + * Returns: + * Current program counter. + */ +extern UINT32 M68KGetPC(void); + +/* + * M68KSaveState(StateFile, name): + * + * Saves the CPU state to the block file. + * + * Parameters: + * StateFile Block file. + * name Name of block to create (e.g. "Main 68K"), to facilitate + * multiple 68K states in the same file. + */ +extern void M68KSaveState(CBlockFile *StateFile, const char *name); + +/* + * M68KLoadState(StateFile, name): + * + * Loads the CPU state. + * + * Parameters: + * StateFile Block file. + * name Name of block to load. + */ +extern void M68KLoadState(CBlockFile *StateFile, const char *name); + +/* + * M68KSetIRQ(irqLevel): + * + * Sets the interrupt level (IPL2-IPL0 pins). + * + * Parameters: + * irqLevel The interrupt level (1-7) or 0 to clear the interrupt. + */ +extern void M68KSetIRQ(int irqLevel); + +/* + * M68KRun(numCycles): + * + * Runs the 68K. + * + * Parameters: + * numCycles Number of cycles to run. + * + * Returns: + * Number of cycles executed. + */ +extern int M68KRun(int numCycles); + +/* + * M68KReset(): + * + * Resets the 68K. + */ +extern void M68KReset(void); + +/* + * M68KSetIRQCallback(F): + * + * Installs an interrupt acknowledge callback for the currently active CPU. The + * default behavior is to always assume autovectored interrupts. + * + * Parameters: + * F Callback function. + */ +extern void M68KSetIRQCallback(int (*F)(int)); + +/* + * M68KAttachBus(IBus *BusPtr): + * + * Attaches a bus object to the 68K, which will be used to perform all address + * space accesses. The 8, 16, and 32-bit read and write handlers are used. + * This must be set up before any 68K-related emulation functions are invoked + * or the program will crash! + * + * Parameters: + * BusPtr Pointer to bus object to use for all address space accesses. + */ +extern void M68KAttachBus(IBus *BusPtr); + +/* + * M68KInit(): + * + * Initializes the 68K emulator. Must be called once per program session prior + * to any 68K emulation functions. A context must be mapped before calling + * this. + * + * Returns: + * Always returns OKAY. + */ +extern bool M68KInit(void); + +/* + * M68KGetContext(M68KCtx *Dest): + * + * Copies the internal (active) 68K context back to the destination. + * + * Parameters: + * Dest Location to which to copy 68K context. + */ +extern void M68KGetContext(M68KCtx *Dest); + +/* + * M68KSetContext(M68KCtx *Src): + * + * Sets the specified 68K context by copying it to the internal context. + * + * Parameters: + * Src Location from which to copy 68K context. + */ +extern void M68KSetContext(M68KCtx *Src); + +#ifdef SUPERMODEL_DEBUGGER +#define DBG68K_REG_PC 0 +#define DBG68K_REG_SR 1 +#define DBG68K_REG_SP 2 +#define DBG68K_REG_D0 3 +#define DBG68K_REG_D1 4 +#define DBG68K_REG_D2 5 +#define DBG68K_REG_D3 6 +#define DBG68K_REG_D4 7 +#define DBG68K_REG_D5 8 +#define DBG68K_REG_D6 9 +#define DBG68K_REG_D7 10 +#define DBG68K_REG_A0 11 +#define DBG68K_REG_A1 12 +#define DBG68K_REG_A2 13 +#define DBG68K_REG_A3 14 +#define DBG68K_REG_A4 15 +#define DBG68K_REG_A5 16 +#define DBG68K_REG_A6 17 +#define DBG68K_REG_A7 18 + +/* + * M68KGetRegister(ctx, reg): + * + * Parameters: + * ctx Musashi 68K context. + * reg Register (use DBG68K_REG_* macros). + * + * Returns: + * Register value. + */ +UINT32 M68KGetRegister(SM68KCtx *ctx, unsigned reg); + +/* + * M68KSetRegister(ctx, reg, value): + * + * Parameters: + * ctx Musashi 68K context. + * reg Register (use DBG68K_REG_* macros). + * value Vale to write. + * + * Returns: + * True if successful, false if register does not exist. + */ +UINT32 M68KSetRegister(SM68KCtx *ctx, unsigned reg, UINT32 val); +#endif // SUPERMODEL_DEBUGGER + +/****************************************************************************** + 68K Handlers + + Intended for use directly by the 68K core. +******************************************************************************/ + + +extern "C" { + +#ifdef SUPERMODEL_DEBUGGER +extern void M68KDebugCallback(); +#endif // SUPERMODEL_DEBUGGER + +/* + * M68KIRQCallback(nIRQ): + * + * Interrupt acknowledge callback. Called when an interrupt is being serviced. + * Actually implemented by calling a user-supplied callback. + * + * Parameters: + * nIRQ IRQ level (1-7). + * + * Returns: + * The interrupt vector to use, M68K_IRQ_AUTOVECTOR, or + * M68K_IRQ_SPURIOUS. If no callback has been installed, the default + * callback is used and always returns M68K_IRQ_AUTOVECTOR. + */ +extern int M68KIRQCallback(int nIRQ); + +/* + * M68KFetch8(a): + * M68KFetch16(a): + * M68KFetch32(a): + * + * Read data from the program address space. + * + * Parameters: + * a Address to read from. + * + * Returns: + * The 8, 16, or 32-bit value read. + */ +unsigned int FASTCALL M68KFetch8(unsigned int a); +unsigned int FASTCALL M68KFetch16(unsigned int a); +unsigned int FASTCALL M68KFetch32(unsigned int a); + +/* + * M68KRead8(a): + * M68KRead16(a): + * M68KRead32(a): + * + * Read data from the data address space. + * + * Parameters: + * a Address to read from. + * + * Returns: + * The 8, 16, or 32-bit value read. + */ +unsigned int FASTCALL M68KRead8(unsigned int a); +unsigned int FASTCALL M68KRead16(unsigned int a); +unsigned int FASTCALL M68KRead32(unsigned int a); + +/* + * M68KWrite8(a, d): + * M68KWrite16(a, d): + * M68KWrite32(a, d): + * + * Writes to the data address space. + * + * Parameters: + * a Address to write to. + * d Data to write. + */ +void FASTCALL M68KWrite8(unsigned int a, unsigned int d); +void FASTCALL M68KWrite16(unsigned int a, unsigned int d); +void FASTCALL M68KWrite32(unsigned int a, unsigned int d); + +} // extern "C" + + +#endif // INCLUDED_68K_H \ No newline at end of file diff --git a/Src/CPU/68K/Musashi/CMakeLists.txt b/Src/CPU/68K/Musashi/CMakeLists.txt new file mode 100644 index 0000000..05d9f47 --- /dev/null +++ b/Src/CPU/68K/Musashi/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.10) +project(m68kmake_builder LANGUAGES C) +add_executable(m68kmake m68kmake.c) + +include(GNUInstallDirs) +install(TARGETS m68kmake RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/Src/CPU/68K/Musashi/m68k.h b/Src/CPU/68K/Musashi/m68k.h new file mode 100644 index 0000000..3f31ff0 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68k.h @@ -0,0 +1,395 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68k.h + * + * Header file for the Musashi 68K emulator. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +#ifndef M68K__HEADER +#define M68K__HEADER + +#ifdef __cplusplus + extern "C" { +#endif + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + +/* ======================================================================== */ +/* ============================ GENERAL DEFINES =========================== */ + +/* ======================================================================== */ + +/* There are 7 levels of interrupt to the 68K. + * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). + */ +#define M68K_IRQ_NONE 0 +#define M68K_IRQ_1 1 +#define M68K_IRQ_2 2 +#define M68K_IRQ_3 3 +#define M68K_IRQ_4 4 +#define M68K_IRQ_5 5 +#define M68K_IRQ_6 6 +#define M68K_IRQ_7 7 + + +/* Special interrupt acknowledge values. + * Use these as special returns from the interrupt acknowledge callback + * (specified later in this header). + */ + +/* Causes an interrupt autovector (0x18 + interrupt level) to be taken. + * This happens in a real 68K if VPA or AVEC is asserted during an interrupt + * acknowledge cycle instead of DTACK. + */ +#define M68K_INT_ACK_AUTOVECTOR 0xffffffff + +/* Causes the spurious interrupt vector (0x18) to be taken + * This happens in a real 68K if BERR is asserted during the interrupt + * acknowledge cycle (i.e. no devices responded to the acknowledge). + */ +#define M68K_INT_ACK_SPURIOUS 0xfffffffe + + +/* CPU types for use in m68k_set_cpu_type() */ +enum +{ + M68K_CPU_TYPE_INVALID, + M68K_CPU_TYPE_68000, + M68K_CPU_TYPE_68008, + M68K_CPU_TYPE_68010, + M68K_CPU_TYPE_68EC020, + M68K_CPU_TYPE_68020, + M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */ + M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */ +}; + +/* Registers used by m68k_get_reg() and m68k_set_reg() */ +typedef enum +{ + /* Real registers */ + M68K_REG_D0, /* Data registers */ + M68K_REG_D1, + M68K_REG_D2, + M68K_REG_D3, + M68K_REG_D4, + M68K_REG_D5, + M68K_REG_D6, + M68K_REG_D7, + M68K_REG_A0, /* Address registers */ + M68K_REG_A1, + M68K_REG_A2, + M68K_REG_A3, + M68K_REG_A4, + M68K_REG_A5, + M68K_REG_A6, + M68K_REG_A7, + M68K_REG_PC, /* Program Counter */ + M68K_REG_SR, /* Status Register */ + M68K_REG_SP, /* The current Stack Pointer (located in A7) */ + M68K_REG_USP, /* User Stack Pointer */ + M68K_REG_ISP, /* Interrupt Stack Pointer */ + M68K_REG_MSP, /* Master Stack Pointer */ + M68K_REG_SFC, /* Source Function Code */ + M68K_REG_DFC, /* Destination Function Code */ + M68K_REG_VBR, /* Vector Base Register */ + M68K_REG_CACR, /* Cache Control Register */ + M68K_REG_CAAR, /* Cache Address Register */ + + /* Assumed registers */ + /* These are cheat registers which emulate the 1-longword prefetch + * present in the 68000 and 68010. + */ + M68K_REG_PREF_ADDR, /* Last prefetch address */ + M68K_REG_PREF_DATA, /* Last prefetch data */ + + /* Convenience registers */ + M68K_REG_PPC, /* Previous value in the program counter */ + M68K_REG_IR, /* Instruction register */ + M68K_REG_CPU_TYPE /* Type of CPU being run */ +} m68k_register_t; + +/* ======================================================================== */ +/* ====================== FUNCTIONS CALLED BY THE CPU ===================== */ +/* ======================================================================== */ + +/* You will have to implement these functions */ + +/* read/write functions called by the CPU to access memory. + * while values used are 32 bits, only the appropriate number + * of bits are relevant (i.e. in write_memory_8, only the lower 8 bits + * of value should be written to memory). + * + * NOTE: I have separated the immediate and PC-relative memory fetches + * from the other memory fetches because some systems require + * differentiation between PROGRAM and DATA fetches (usually + * for security setups such as encryption). + * This separation can either be achieved by setting + * M68K_SEPARATE_READS in m68kconf.h and defining + * the read functions, or by setting M68K_EMULATE_FC and + * making a function code callback function. + * Using the callback offers better emulation coverage + * because you can also monitor whether the CPU is in SYSTEM or + * USER mode, but it is also slower. + */ + +/* Read from anywhere */ +unsigned int m68k_read_memory_8(unsigned int address); +unsigned int m68k_read_memory_16(unsigned int address); +unsigned int m68k_read_memory_32(unsigned int address); + +/* Read data immediately following the PC */ +unsigned int m68k_read_immediate_16(unsigned int address); +unsigned int m68k_read_immediate_32(unsigned int address); + +/* Read data relative to the PC */ +unsigned int m68k_read_pcrelative_8(unsigned int address); +unsigned int m68k_read_pcrelative_16(unsigned int address); +unsigned int m68k_read_pcrelative_32(unsigned int address); + +/* Memory access for the disassembler */ +unsigned int m68k_read_disassembler_8 (unsigned int address); +unsigned int m68k_read_disassembler_16 (unsigned int address); +unsigned int m68k_read_disassembler_32 (unsigned int address); + +/* Write to anywhere */ +void m68k_write_memory_8(unsigned int address, unsigned int value); +void m68k_write_memory_16(unsigned int address, unsigned int value); +void m68k_write_memory_32(unsigned int address, unsigned int value); + +/* Special call to simulate undocumented 68k behavior when move.l with a + * predecrement destination mode is executed. + * To simulate real 68k behavior, first write the high word to + * [address+2], and then write the low word to [address]. + * + * Enable this functionality with M68K_SIMULATE_PD_WRITES in m68kconf.h. + */ +void m68k_write_memory_32_pd(unsigned int address, unsigned int value); + + + +/* ======================================================================== */ +/* ============================== CALLBACKS =============================== */ +/* ======================================================================== */ + +/* These functions allow you to set callbacks to the host when specific events + * occur. Note that you must enable the corresponding value in m68kconf.h + * in order for these to do anything useful. + * Note: I have defined default callbacks which are used if you have enabled + * the corresponding #define in m68kconf.h but either haven't assigned a + * callback or have assigned a callback of NULL. + */ + +/* Set the callback for an interrupt acknowledge. + * You must enable M68K_EMULATE_INT_ACK in m68kconf.h. + * The CPU will call the callback with the interrupt level being acknowledged. + * The host program must return either a vector from 0x02-0xff, or one of the + * special interrupt acknowledge values specified earlier in this header. + * If this is not implemented, the CPU will always assume an autovectored + * interrupt, and will automatically clear the interrupt request when it + * services the interrupt. + * Default behavior: return M68K_INT_ACK_AUTOVECTOR. + */ +void m68k_set_int_ack_callback(int (*callback)(int int_level)); + + +/* Set the callback for a breakpoint acknowledge (68010+). + * You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h. + * The CPU will call the callback with whatever was in the data field of the + * BKPT instruction for 68020+, or 0 for 68010. + * Default behavior: do nothing. + */ +void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)); + + +/* Set the callback for the RESET instruction. + * You must enable M68K_EMULATE_RESET in m68kconf.h. + * The CPU calls this callback every time it encounters a RESET instruction. + * Default behavior: do nothing. + */ +void m68k_set_reset_instr_callback(void (*callback)(void)); + + +/* Set the callback for the CMPI.L #v, Dn instruction. + * You must enable M68K_CMPILD_HAS_CALLBACK in m68kconf.h. + * The CPU calls this callback every time it encounters a CMPI.L #v, Dn instruction. + * Default behavior: do nothing. + */ +void m68k_set_cmpild_instr_callback(void (*callback)(unsigned int val, int reg)); + + +/* Set the callback for the RTE instruction. + * You must enable M68K_RTE_HAS_CALLBACK in m68kconf.h. + * The CPU calls this callback every time it encounters a RTE instruction. + * Default behavior: do nothing. + */ +void m68k_set_rte_instr_callback(void (*callback)(void)); + + +/* Set the callback for informing of a large PC change. + * You must enable M68K_MONITOR_PC in m68kconf.h. + * The CPU calls this callback with the new PC value every time the PC changes + * by a large value (currently set for changes by longwords). + * Default behavior: do nothing. + */ +void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)); + + +/* Set the callback for CPU function code changes. + * You must enable M68K_EMULATE_FC in m68kconf.h. + * The CPU calls this callback with the function code before every memory + * access to set the CPU's function code according to what kind of memory + * access it is (supervisor/user, program/data and such). + * Default behavior: do nothing. + */ +void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)); + + +/* Set a callback for the instruction cycle of the CPU. + * You must enable M68K_INSTRUCTION_HOOK in m68kconf.h. + * The CPU calls this callback just before fetching the opcode in the + * instruction cycle. + * Default behavior: do nothing. + */ +void m68k_set_instr_hook_callback(void (*callback)(void)); + + + +/* ======================================================================== */ +/* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */ +/* ======================================================================== */ + +/* Use this function to set the CPU type you want to emulate. + * Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68008, + * M68K_CPU_TYPE_68010, M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020. + */ +void m68k_set_cpu_type(unsigned int cpu_type); + +/* Do whatever initialisations the core requires. Should be called + * at least once at init time. + */ +void m68k_init(void); + +/* Pulse the RESET pin on the CPU. + * You *MUST* reset the CPU at least once to initialize the emulation + * Note: If you didn't call m68k_set_cpu_type() before resetting + * the CPU for the first time, the CPU will be set to + * M68K_CPU_TYPE_68000. + */ +void m68k_pulse_reset(void); + +/* execute num_cycles worth of instructions. returns number of cycles used */ +int m68k_execute(int num_cycles); + +/* These functions let you read/write/modify the number of cycles left to run + * while m68k_execute() is running. + * These are useful if the 68k accesses a memory-mapped port on another device + * that requires immediate processing by another CPU. + */ +int m68k_cycles_run(void); /* Number of cycles run so far */ +int m68k_cycles_remaining(void); /* Number of cycles left */ +void m68k_modify_timeslice(int cycles); /* Modify cycles left */ +void m68k_end_timeslice(void); /* End timeslice now */ + +/* Set the IPL0-IPL2 pins on the CPU (IRQ). + * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). + * Setting IRQ to 0 will clear an interrupt request. + */ +void m68k_set_irq(unsigned int int_level); + + +/* Halt the CPU as if you pulsed the HALT pin. */ +void m68k_pulse_halt(void); + + +/* Context switching to allow multiple CPUs */ + +/* Get the size of the cpu context in bytes */ +unsigned int m68k_context_size(void); + +/* Get a cpu context */ +unsigned int m68k_get_context(void* dst); + +/* set the current cpu context */ +void m68k_set_context(void* dst); + +/* Register the CPU state information */ +void m68k_state_register(const char *type); + + +/* Peek at the internals of a CPU context. This can either be a context + * retrieved using m68k_get_context() or the currently running context. + * If context is NULL, the currently running CPU context will be used. + */ +unsigned int m68k_get_reg(void* context, m68k_register_t reg); + +/* Poke values into the internals of the currently running CPU context */ +void m68k_set_reg(m68k_register_t reg, unsigned int value); + +/* Check if an instruction is valid for the specified CPU type */ +unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type); + +/* Disassemble 1 instruction using the epecified CPU type at pc. Stores + * disassembly in str_buff and returns the size of the instruction in bytes. + */ +unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type); + + +/* ======================================================================== */ +/* ============================= CONFIGURATION ============================ */ +/* ======================================================================== */ + +/* Import the configuration for this build */ +#include "m68kconf.h" + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + +#ifdef __cplusplus + } +#endif + +#endif /* M68K__HEADER */ diff --git a/Src/CPU/68K/Musashi/m68k_in.c b/Src/CPU/68K/Musashi/m68k_in.c new file mode 100644 index 0000000..cf8c0e4 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68k_in.c @@ -0,0 +1,10405 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68k_in.c + * + * Input file to m68kmake, the Musashi code generator. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +/* +must fix: + callm + chk +*/ +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + +/* Special thanks to Bart Trzynadlowski for his insight into the + * undocumented features of this chip: + * + * http://dynarec.com/~bart/files/68knotes.txt + */ + + +/* Input file for m68kmake + * ----------------------- + * + * All sections begin with 80 X's in a row followed by an end-of-line + * sequence. + * After this, m68kmake will expect to find one of the following section + * identifiers: + * M68KMAKE_PROTOTYPE_HEADER - header for opcode handler prototypes + * M68KMAKE_PROTOTYPE_FOOTER - footer for opcode handler prototypes + * M68KMAKE_TABLE_HEADER - header for opcode handler jumptable + * M68KMAKE_TABLE_FOOTER - footer for opcode handler jumptable + * M68KMAKE_TABLE_BODY - the table itself + * M68KMAKE_OPCODE_HANDLER_HEADER - header for opcode handler implementation + * M68KMAKE_OPCODE_HANDLER_FOOTER - footer for opcode handler implementation + * M68KMAKE_OPCODE_HANDLER_BODY - body section for opcode handler implementation + * + * NOTE: M68KMAKE_OPCODE_HANDLER_BODY must be last in the file and + * M68KMAKE_TABLE_BODY must be second last in the file. + * + * The M68KMAKE_OPHANDLER_BODY section contains the opcode handler + * primitives themselves. Each opcode handler begins with: + * M68KMAKE_OP(A, B, C, D) + * + * where A is the opcode handler name, B is the size of the operation, + * C denotes any special processing mode, and D denotes a specific + * addressing mode. + * For C and D where nothing is specified, use "." + * + * Example: + * M68KMAKE_OP(abcd, 8, rr, .) abcd, size 8, register to register, default EA + * M68KMAKE_OP(abcd, 8, mm, ax7) abcd, size 8, memory to memory, register X is A7 + * M68KMAKE_OP(tst, 16, ., pcix) tst, size 16, PCIX addressing + * + * All opcode handler primitives end with a closing curly brace "}" at column 1 + * + * NOTE: Do not place a M68KMAKE_OP() directive inside the opcode handler, + * and do not put a closing curly brace at column 1 unless it is + * marking the end of the handler! + * + * Inside the handler, m68kmake will recognize M68KMAKE_GET_OPER_xx_xx, + * M68KMAKE_GET_EA_xx_xx, and M68KMAKE_CC directives, and create multiple + * opcode handlers to handle variations in the opcode handler. + * Note: M68KMAKE_CC will only be interpreted in condition code opcodes. + * As well, M68KMAKE_GET_EA_xx_xx and M68KMAKE_GET_OPER_xx_xx will only + * be interpreted on instructions where the corresponding table entry + * specifies multiple effective addressing modes. + * Example: + * clr 32 . . 0100001010...... A+-DXWL... U U U 12 6 4 + * + * This table entry says that the clr.l opcde has 7 variations (A+-DXWL). + * It is run in user or supervisor mode for all CPUs, and uses 12 cycles for + * 68000, 6 cycles for 68010, and 4 cycles for 68020. + */ + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_PROTOTYPE_HEADER + +#ifndef M68KOPS__HEADER +#define M68KOPS__HEADER + +/* ======================================================================== */ +/* ============================ OPCODE HANDLERS =========================== */ +/* ======================================================================== */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_PROTOTYPE_FOOTER + + +/* Build the opcode handler table */ +void m68ki_build_opcode_table(void); + +extern void (*m68ki_instruction_jump_table[0x10000])(void); /* opcode handler jump table */ +extern unsigned char m68ki_cycles[][0x10000]; + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + +#endif /* M68KOPS__HEADER */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_TABLE_HEADER + +/* ======================================================================== */ +/* ========================= OPCODE TABLE BUILDER ========================= */ +/* ======================================================================== */ + +#include "m68kops.h" + +#define NUM_CPU_TYPES 3 + +void (*m68ki_instruction_jump_table[0x10000])(void); /* opcode handler jump table */ +unsigned char m68ki_cycles[NUM_CPU_TYPES][0x10000]; /* Cycles used by CPU type */ + +/* This is used to generate the opcode handler jump table */ +typedef struct +{ + void (*opcode_handler)(void); /* handler function */ + unsigned int mask; /* mask on opcode */ + unsigned int match; /* what to match after masking */ + unsigned char cycles[NUM_CPU_TYPES]; /* cycles each cpu type takes */ +} opcode_handler_struct; + + +/* Opcode handler table */ +static opcode_handler_struct m68k_opcode_handler_table[] = +{ +/* function mask match 000 010 020 */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_TABLE_FOOTER + + {0, 0, 0, {0, 0, 0}} +}; + + +/* Build the opcode handler jump table */ +void m68ki_build_opcode_table(void) +{ + opcode_handler_struct *ostruct; + int instr; + int i; + int j; + int k; + + for(i = 0; i < 0x10000; i++) + { + /* default to illegal */ + m68ki_instruction_jump_table[i] = m68k_op_illegal; + for(k=0;kmask != 0xff00) + { + for(i = 0;i < 0x10000;i++) + { + if((i & ostruct->mask) == ostruct->match) + { + m68ki_instruction_jump_table[i] = ostruct->opcode_handler; + for(k=0;kcycles[k]; + } + } + ostruct++; + } + while(ostruct->mask == 0xff00) + { + for(i = 0;i <= 0xff;i++) + { + m68ki_instruction_jump_table[ostruct->match | i] = ostruct->opcode_handler; + for(k=0;kmatch | i] = ostruct->cycles[k]; + } + ostruct++; + } + while(ostruct->mask == 0xf1f8) + { + for(i = 0;i < 8;i++) + { + for(j = 0;j < 8;j++) + { + instr = ostruct->match | (i << 9) | j; + m68ki_instruction_jump_table[instr] = ostruct->opcode_handler; + for(k=0;kcycles[k]; + // For all shift operations with known shift distance (encoded in instruction word) + if((instr & 0xf000) == 0xe000 && (!(instr & 0x20))) + { + // On the 68000 and 68010 shift distance affect execution time. + // Add the cycle cost of shifting; 2 times the shift distance + int cycle_cost = ((((i-1)&7)+1)<<1); + m68ki_cycles[0][instr] += cycle_cost; + m68ki_cycles[1][instr] += cycle_cost; + // On the 68020 shift distance does not affect execution time + m68ki_cycles[2][instr] += 0; + } + } + } + ostruct++; + } + while(ostruct->mask == 0xfff0) + { + for(i = 0;i <= 0x0f;i++) + { + m68ki_instruction_jump_table[ostruct->match | i] = ostruct->opcode_handler; + for(k=0;kmatch | i] = ostruct->cycles[k]; + } + ostruct++; + } + while(ostruct->mask == 0xf1ff) + { + for(i = 0;i <= 0x07;i++) + { + m68ki_instruction_jump_table[ostruct->match | (i << 9)] = ostruct->opcode_handler; + for(k=0;kmatch | (i << 9)] = ostruct->cycles[k]; + } + ostruct++; + } + while(ostruct->mask == 0xfff8) + { + for(i = 0;i <= 0x07;i++) + { + m68ki_instruction_jump_table[ostruct->match | i] = ostruct->opcode_handler; + for(k=0;kmatch | i] = ostruct->cycles[k]; + } + ostruct++; + } + while(ostruct->mask == 0xffff) + { + m68ki_instruction_jump_table[ostruct->match] = ostruct->opcode_handler; + for(k=0;kmatch] = ostruct->cycles[k]; + ostruct++; + } +} + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_OPCODE_HANDLER_HEADER + +#include "m68kcpu.h" + +/* ======================================================================== */ +/* ========================= INSTRUCTION HANDLERS ========================= */ +/* ======================================================================== */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_OPCODE_HANDLER_FOOTER + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_TABLE_BODY + +The following table is arranged as follows: + +name: Opcode mnemonic + +size: Operation size + +spec proc: Special processing mode: + .: normal + s: static operand + r: register operand + rr: register to register + mm: memory to memory + er: effective address to register + re: register to effective address + dd: data register to data register + da: data register to address register + aa: address register to address register + cr: control register to register + rc: register to control register + toc: to condition code register + tos: to status register + tou: to user stack pointer + frc: from condition code register + frs: from status register + fru: from user stack pointer + * for move.x, the special processing mode is a specific + destination effective addressing mode. + +spec ea: Specific effective addressing mode: + .: normal + i: immediate + d: data register + a: address register + ai: address register indirect + pi: address register indirect with postincrement + pd: address register indirect with predecrement + di: address register indirect with displacement + ix: address register indirect with index + aw: absolute word address + al: absolute long address + pcdi: program counter relative with displacement + pcix: program counter relative with index + a7: register specified in instruction is A7 + ax7: register field X of instruction is A7 + ay7: register field Y of instruction is A7 + axy7: register fields X and Y of instruction are A7 + +bit pattern: Pattern to recognize this opcode. "." means don't care. + +allowed ea: List of allowed addressing modes: + .: not present + A: address register indirect + +: ARI (address register indirect) with postincrement + -: ARI with predecrement + D: ARI with displacement + X: ARI with index + W: absolute word address + L: absolute long address + d: program counter indirect with displacement + x: program counter indirect with index + I: immediate +mode: CPU operating mode for each cpu type. U = user or supervisor, + S = supervisor only, "." = opcode not present. + +cpu cycles: Base number of cycles required to execute this opcode on the + specified CPU type. + Use "." if CPU does not have this opcode. + + + + spec spec allowed ea mode cpu cycles +name size proc ea bit pattern A+-DXWLdxI 0 1 2 000 010 020 comments +====== ==== ==== ==== ================ ========== = = = === === === ============= +M68KMAKE_TABLE_START +1010 0 . . 1010............ .......... U U U 4 4 4 +1111 0 . . 1111............ .......... U U U 4 4 4 +abcd 8 rr . 1100...100000... .......... U U U 6 6 4 +abcd 8 mm ax7 1100111100001... .......... U U U 18 18 16 +abcd 8 mm ay7 1100...100001111 .......... U U U 18 18 16 +abcd 8 mm axy7 1100111100001111 .......... U U U 18 18 16 +abcd 8 mm . 1100...100001... .......... U U U 18 18 16 +add 8 er d 1101...000000... .......... U U U 4 4 2 +add 8 er . 1101...000...... A+-DXWLdxI U U U 4 4 2 +add 16 er d 1101...001000... .......... U U U 4 4 2 +add 16 er a 1101...001001... .......... U U U 4 4 2 +add 16 er . 1101...001...... A+-DXWLdxI U U U 4 4 2 +add 32 er d 1101...010000... .......... U U U 6 6 2 +add 32 er a 1101...010001... .......... U U U 6 6 2 +add 32 er . 1101...010...... A+-DXWLdxI U U U 6 6 2 +add 8 re . 1101...100...... A+-DXWL... U U U 8 8 4 +add 16 re . 1101...101...... A+-DXWL... U U U 8 8 4 +add 32 re . 1101...110...... A+-DXWL... U U U 12 12 4 +adda 16 . d 1101...011000... .......... U U U 8 8 2 +adda 16 . a 1101...011001... .......... U U U 8 8 2 +adda 16 . . 1101...011...... A+-DXWLdxI U U U 8 8 2 +adda 32 . d 1101...111000... .......... U U U 6 6 2 +adda 32 . a 1101...111001... .......... U U U 6 6 2 +adda 32 . . 1101...111...... A+-DXWLdxI U U U 6 6 2 +addi 8 . d 0000011000000... .......... U U U 8 8 2 +addi 8 . . 0000011000...... A+-DXWL... U U U 12 12 4 +addi 16 . d 0000011001000... .......... U U U 8 8 2 +addi 16 . . 0000011001...... A+-DXWL... U U U 12 12 4 +addi 32 . d 0000011010000... .......... U U U 16 14 2 +addi 32 . . 0000011010...... A+-DXWL... U U U 20 20 4 +addq 8 . d 0101...000000... .......... U U U 4 4 2 +addq 8 . . 0101...000...... A+-DXWL... U U U 8 8 4 +addq 16 . d 0101...001000... .......... U U U 4 4 2 +addq 16 . a 0101...001001... .......... U U U 4 4 2 +addq 16 . . 0101...001...... A+-DXWL... U U U 8 8 4 +addq 32 . d 0101...010000... .......... U U U 8 8 2 +addq 32 . a 0101...010001... .......... U U U 8 8 2 +addq 32 . . 0101...010...... A+-DXWL... U U U 12 12 4 +addx 8 rr . 1101...100000... .......... U U U 4 4 2 +addx 16 rr . 1101...101000... .......... U U U 4 4 2 +addx 32 rr . 1101...110000... .......... U U U 8 6 2 +addx 8 mm ax7 1101111100001... .......... U U U 18 18 12 +addx 8 mm ay7 1101...100001111 .......... U U U 18 18 12 +addx 8 mm axy7 1101111100001111 .......... U U U 18 18 12 +addx 8 mm . 1101...100001... .......... U U U 18 18 12 +addx 16 mm . 1101...101001... .......... U U U 18 18 12 +addx 32 mm . 1101...110001... .......... U U U 30 30 12 +and 8 er d 1100...000000... .......... U U U 4 4 2 +and 8 er . 1100...000...... A+-DXWLdxI U U U 4 4 2 +and 16 er d 1100...001000... .......... U U U 4 4 2 +and 16 er . 1100...001...... A+-DXWLdxI U U U 4 4 2 +and 32 er d 1100...010000... .......... U U U 6 6 2 +and 32 er . 1100...010...... A+-DXWLdxI U U U 6 6 2 +and 8 re . 1100...100...... A+-DXWL... U U U 8 8 4 +and 16 re . 1100...101...... A+-DXWL... U U U 8 8 4 +and 32 re . 1100...110...... A+-DXWL... U U U 12 12 4 +andi 16 toc . 0000001000111100 .......... U U U 20 16 12 +andi 16 tos . 0000001001111100 .......... S S S 20 16 12 +andi 8 . d 0000001000000... .......... U U U 8 8 2 +andi 8 . . 0000001000...... A+-DXWL... U U U 12 12 4 +andi 16 . d 0000001001000... .......... U U U 8 8 2 +andi 16 . . 0000001001...... A+-DXWL... U U U 12 12 4 +andi 32 . d 0000001010000... .......... U U U 14 14 2 +andi 32 . . 0000001010...... A+-DXWL... U U U 20 20 4 +asr 8 s . 1110...000000... .......... U U U 6 6 6 +asr 16 s . 1110...001000... .......... U U U 6 6 6 +asr 32 s . 1110...010000... .......... U U U 8 8 6 +asr 8 r . 1110...000100... .......... U U U 6 6 6 +asr 16 r . 1110...001100... .......... U U U 6 6 6 +asr 32 r . 1110...010100... .......... U U U 8 8 6 +asr 16 . . 1110000011...... A+-DXWL... U U U 8 8 5 +asl 8 s . 1110...100000... .......... U U U 6 6 8 +asl 16 s . 1110...101000... .......... U U U 6 6 8 +asl 32 s . 1110...110000... .......... U U U 8 8 8 +asl 8 r . 1110...100100... .......... U U U 6 6 8 +asl 16 r . 1110...101100... .......... U U U 6 6 8 +asl 32 r . 1110...110100... .......... U U U 8 8 8 +asl 16 . . 1110000111...... A+-DXWL... U U U 8 8 6 +bcc 8 . . 0110............ .......... U U U 8 8 6 +bcc 16 . . 0110....00000000 .......... U U U 10 10 6 +bcc 32 . . 0110....11111111 .......... . . U . . 6 +bchg 8 r . 0000...101...... A+-DXWL... U U U 8 8 4 +bchg 32 r d 0000...101000... .......... U U U 8 8 4 +bchg 8 s . 0000100001...... A+-DXWL... U U U 12 12 4 +bchg 32 s d 0000100001000... .......... U U U 12 12 4 +bclr 8 r . 0000...110...... A+-DXWL... U U U 8 10 4 +bclr 32 r d 0000...110000... .......... U U U 10 10 4 +bclr 8 s . 0000100010...... A+-DXWL... U U U 12 12 4 +bclr 32 s d 0000100010000... .......... U U U 14 14 4 +bfchg 32 . d 1110101011000... .......... . . U . . 12 timing not quite correct +bfchg 32 . . 1110101011...... A..DXWL... . . U . . 20 +bfclr 32 . d 1110110011000... .......... . . U . . 12 +bfclr 32 . . 1110110011...... A..DXWL... . . U . . 20 +bfexts 32 . d 1110101111000... .......... . . U . . 8 +bfexts 32 . . 1110101111...... A..DXWLdx. . . U . . 15 +bfextu 32 . d 1110100111000... .......... . . U . . 8 +bfextu 32 . . 1110100111...... A..DXWLdx. . . U . . 15 +bfffo 32 . d 1110110111000... .......... . . U . . 18 +bfffo 32 . . 1110110111...... A..DXWLdx. . . U . . 28 +bfins 32 . d 1110111111000... .......... . . U . . 10 +bfins 32 . . 1110111111...... A..DXWL... . . U . . 17 +bfset 32 . d 1110111011000... .......... . . U . . 12 +bfset 32 . . 1110111011...... A..DXWL... . . U . . 20 +bftst 32 . d 1110100011000... .......... . . U . . 6 +bftst 32 . . 1110100011...... A..DXWLdx. . . U . . 13 +bkpt 0 . . 0100100001001... .......... . U U . 10 10 +bra 8 . . 01100000........ .......... U U U 10 10 10 +bra 16 . . 0110000000000000 .......... U U U 10 10 10 +bra 32 . . 0110000011111111 .......... U U U . . 10 +bset 32 r d 0000...111000... .......... U U U 8 8 4 +bset 8 r . 0000...111...... A+-DXWL... U U U 8 8 4 +bset 8 s . 0000100011...... A+-DXWL... U U U 12 12 4 +bset 32 s d 0000100011000... .......... U U U 12 12 4 +bsr 8 . . 01100001........ .......... U U U 18 18 7 +bsr 16 . . 0110000100000000 .......... U U U 18 18 7 +bsr 32 . . 0110000111111111 .......... . . U . . 7 +btst 8 r . 0000...100...... A+-DXWLdxI U U U 4 4 4 +btst 32 r d 0000...100000... .......... U U U 6 6 4 +btst 8 s . 0000100000...... A+-DXWLdx. U U U 8 8 4 +btst 32 s d 0000100000000... .......... U U U 10 10 4 +callm 32 . . 0000011011...... A..DXWLdx. . . U . . 60 not properly emulated +cas 8 . . 0000101011...... A+-DXWL... . . U . . 12 +cas 16 . . 0000110011...... A+-DXWL... . . U . . 12 +cas 32 . . 0000111011...... A+-DXWL... . . U . . 12 +cas2 16 . . 0000110011111100 .......... . . U . . 12 +cas2 32 . . 0000111011111100 .......... . . U . . 12 +chk 16 . d 0100...110000... .......... U U U 10 8 8 +chk 16 . . 0100...110...... A+-DXWLdxI U U U 10 8 8 +chk 32 . d 0100...100000... .......... . . U . . 8 +chk 32 . . 0100...100...... A+-DXWLdxI . . U . . 8 +chk2cmp2 8 . pcdi 0000000011111010 .......... . . U . . 23 +chk2cmp2 8 . pcix 0000000011111011 .......... . . U . . 23 +chk2cmp2 8 . . 0000000011...... A..DXWL... . . U . . 18 +chk2cmp2 16 . pcdi 0000001011111010 .......... . . U . . 23 +chk2cmp2 16 . pcix 0000001011111011 .......... . . U . . 23 +chk2cmp2 16 . . 0000001011...... A..DXWL... . . U . . 18 +chk2cmp2 32 . pcdi 0000010011111010 .......... . . U . . 23 +chk2cmp2 32 . pcix 0000010011111011 .......... . . U . . 23 +chk2cmp2 32 . . 0000010011...... A..DXWL... . . U . . 18 +clr 8 . d 0100001000000... .......... U U U 4 4 2 +clr 8 . . 0100001000...... A+-DXWL... U U U 8 4 4 +clr 16 . d 0100001001000... .......... U U U 4 4 2 +clr 16 . . 0100001001...... A+-DXWL... U U U 8 4 4 +clr 32 . d 0100001010000... .......... U U U 6 6 2 +clr 32 . . 0100001010...... A+-DXWL... U U U 12 6 4 +cmp 8 . d 1011...000000... .......... U U U 4 4 2 +cmp 8 . . 1011...000...... A+-DXWLdxI U U U 4 4 2 +cmp 16 . d 1011...001000... .......... U U U 4 4 2 +cmp 16 . a 1011...001001... .......... U U U 4 4 2 +cmp 16 . . 1011...001...... A+-DXWLdxI U U U 4 4 2 +cmp 32 . d 1011...010000... .......... U U U 6 6 2 +cmp 32 . a 1011...010001... .......... U U U 6 6 2 +cmp 32 . . 1011...010...... A+-DXWLdxI U U U 6 6 2 +cmpa 16 . d 1011...011000... .......... U U U 6 6 4 +cmpa 16 . a 1011...011001... .......... U U U 6 6 4 +cmpa 16 . . 1011...011...... A+-DXWLdxI U U U 6 6 4 +cmpa 32 . d 1011...111000... .......... U U U 6 6 4 +cmpa 32 . a 1011...111001... .......... U U U 6 6 4 +cmpa 32 . . 1011...111...... A+-DXWLdxI U U U 6 6 4 +cmpi 8 . d 0000110000000... .......... U U U 8 8 2 +cmpi 8 . . 0000110000...... A+-DXWL... U U U 8 8 2 +cmpi 8 . pcdi 0000110000111010 .......... . . U . . 7 +cmpi 8 . pcix 0000110000111011 .......... . . U . . 9 +cmpi 16 . d 0000110001000... .......... U U U 8 8 2 +cmpi 16 . . 0000110001...... A+-DXWL... U U U 8 8 2 +cmpi 16 . pcdi 0000110001111010 .......... . . U . . 7 +cmpi 16 . pcix 0000110001111011 .......... . . U . . 9 +cmpi 32 . d 0000110010000... .......... U U U 14 12 2 +cmpi 32 . . 0000110010...... A+-DXWL... U U U 12 12 2 +cmpi 32 . pcdi 0000110010111010 .......... . . U . . 7 +cmpi 32 . pcix 0000110010111011 .......... . . U . . 9 +cmpm 8 . ax7 1011111100001... .......... U U U 12 12 9 +cmpm 8 . ay7 1011...100001111 .......... U U U 12 12 9 +cmpm 8 . axy7 1011111100001111 .......... U U U 12 12 9 +cmpm 8 . . 1011...100001... .......... U U U 12 12 9 +cmpm 16 . . 1011...101001... .......... U U U 12 12 9 +cmpm 32 . . 1011...110001... .......... U U U 20 20 9 +cpbcc 32 . . 1111...01....... .......... . . U . . 4 unemulated +cpdbcc 32 . . 1111...001001... .......... . . U . . 4 unemulated +cpgen 32 . . 1111...000...... .......... . . U . . 4 unemulated +cpscc 32 . . 1111...001...... .......... . . U . . 4 unemulated +cptrapcc 32 . . 1111...001111... .......... . . U . . 4 unemulated +dbt 16 . . 0101000011001... .......... U U U 12 12 6 +dbf 16 . . 0101000111001... .......... U U U 14 14 6 +dbcc 16 . . 0101....11001... .......... U U U 12 12 6 +divs 16 . d 1000...111000... .......... U U U 158 122 56 +divs 16 . . 1000...111...... A+-DXWLdxI U U U 158 122 56 +divu 16 . d 1000...011000... .......... U U U 140 108 44 +divu 16 . . 1000...011...... A+-DXWLdxI U U U 140 108 44 +divl 32 . d 0100110001000... .......... . . U . . 84 +divl 32 . . 0100110001...... A+-DXWLdxI . . U . . 84 +eor 8 . d 1011...100000... .......... U U U 4 4 2 +eor 8 . . 1011...100...... A+-DXWL... U U U 8 8 4 +eor 16 . d 1011...101000... .......... U U U 4 4 2 +eor 16 . . 1011...101...... A+-DXWL... U U U 8 8 4 +eor 32 . d 1011...110000... .......... U U U 8 6 2 +eor 32 . . 1011...110...... A+-DXWL... U U U 12 12 4 +eori 16 toc . 0000101000111100 .......... U U U 20 16 12 +eori 16 tos . 0000101001111100 .......... S S S 20 16 12 +eori 8 . d 0000101000000... .......... U U U 8 8 2 +eori 8 . . 0000101000...... A+-DXWL... U U U 12 12 4 +eori 16 . d 0000101001000... .......... U U U 8 8 2 +eori 16 . . 0000101001...... A+-DXWL... U U U 12 12 4 +eori 32 . d 0000101010000... .......... U U U 16 14 2 +eori 32 . . 0000101010...... A+-DXWL... U U U 20 20 4 +exg 32 dd . 1100...101000... .......... U U U 6 6 2 +exg 32 aa . 1100...101001... .......... U U U 6 6 2 +exg 32 da . 1100...110001... .......... U U U 6 6 2 +ext 16 . . 0100100010000... .......... U U U 4 4 4 +ext 32 . . 0100100011000... .......... U U U 4 4 4 +extb 32 . . 0100100111000... .......... . . U . . 4 +illegal 0 . . 0100101011111100 .......... U U U 4 4 4 +jmp 32 . . 0100111011...... A..DXWLdx. U U U 4 4 0 +jsr 32 . . 0100111010...... A..DXWLdx. U U U 12 12 0 +lea 32 . . 0100...111...... A..DXWLdx. U U U 0 0 2 +link 16 . a7 0100111001010111 .......... U U U 16 16 5 +link 16 . . 0100111001010... .......... U U U 16 16 5 +link 32 . a7 0100100000001111 .......... . . U . . 6 +link 32 . . 0100100000001... .......... . . U . . 6 +lsr 8 s . 1110...000001... .......... U U U 6 6 4 +lsr 16 s . 1110...001001... .......... U U U 6 6 4 +lsr 32 s . 1110...010001... .......... U U U 8 8 4 +lsr 8 r . 1110...000101... .......... U U U 6 6 6 +lsr 16 r . 1110...001101... .......... U U U 6 6 6 +lsr 32 r . 1110...010101... .......... U U U 8 8 6 +lsr 16 . . 1110001011...... A+-DXWL... U U U 8 8 5 +lsl 8 s . 1110...100001... .......... U U U 6 6 4 +lsl 16 s . 1110...101001... .......... U U U 6 6 4 +lsl 32 s . 1110...110001... .......... U U U 8 8 4 +lsl 8 r . 1110...100101... .......... U U U 6 6 6 +lsl 16 r . 1110...101101... .......... U U U 6 6 6 +lsl 32 r . 1110...110101... .......... U U U 8 8 6 +lsl 16 . . 1110001111...... A+-DXWL... U U U 8 8 5 +move 8 d d 0001...000000... .......... U U U 4 4 2 +move 8 d . 0001...000...... A+-DXWLdxI U U U 4 4 2 +move 8 ai d 0001...010000... .......... U U U 8 8 4 +move 8 ai . 0001...010...... A+-DXWLdxI U U U 8 8 4 +move 8 pi d 0001...011000... .......... U U U 8 8 4 +move 8 pi . 0001...011...... A+-DXWLdxI U U U 8 8 4 +move 8 pi7 d 0001111011000... .......... U U U 8 8 4 +move 8 pi7 . 0001111011...... A+-DXWLdxI U U U 8 8 4 +move 8 pd d 0001...100000... .......... U U U 8 8 5 +move 8 pd . 0001...100...... A+-DXWLdxI U U U 8 8 5 +move 8 pd7 d 0001111100000... .......... U U U 8 8 5 +move 8 pd7 . 0001111100...... A+-DXWLdxI U U U 8 8 5 +move 8 di d 0001...101000... .......... U U U 12 12 5 +move 8 di . 0001...101...... A+-DXWLdxI U U U 12 12 5 +move 8 ix d 0001...110000... .......... U U U 14 14 7 +move 8 ix . 0001...110...... A+-DXWLdxI U U U 14 14 7 +move 8 aw d 0001000111000... .......... U U U 12 12 4 +move 8 aw . 0001000111...... A+-DXWLdxI U U U 12 12 4 +move 8 al d 0001001111000... .......... U U U 16 16 6 +move 8 al . 0001001111...... A+-DXWLdxI U U U 16 16 6 +move 16 d d 0011...000000... .......... U U U 4 4 2 +move 16 d a 0011...000001... .......... U U U 4 4 2 +move 16 d . 0011...000...... A+-DXWLdxI U U U 4 4 2 +move 16 ai d 0011...010000... .......... U U U 8 8 4 +move 16 ai a 0011...010001... .......... U U U 8 8 4 +move 16 ai . 0011...010...... A+-DXWLdxI U U U 8 8 4 +move 16 pi d 0011...011000... .......... U U U 8 8 4 +move 16 pi a 0011...011001... .......... U U U 8 8 4 +move 16 pi . 0011...011...... A+-DXWLdxI U U U 8 8 4 +move 16 pd d 0011...100000... .......... U U U 8 8 5 +move 16 pd a 0011...100001... .......... U U U 8 8 5 +move 16 pd . 0011...100...... A+-DXWLdxI U U U 8 8 5 +move 16 di d 0011...101000... .......... U U U 12 12 5 +move 16 di a 0011...101001... .......... U U U 12 12 5 +move 16 di . 0011...101...... A+-DXWLdxI U U U 12 12 5 +move 16 ix d 0011...110000... .......... U U U 14 14 7 +move 16 ix a 0011...110001... .......... U U U 14 14 7 +move 16 ix . 0011...110...... A+-DXWLdxI U U U 14 14 7 +move 16 aw d 0011000111000... .......... U U U 12 12 4 +move 16 aw a 0011000111001... .......... U U U 12 12 4 +move 16 aw . 0011000111...... A+-DXWLdxI U U U 12 12 4 +move 16 al d 0011001111000... .......... U U U 16 16 6 +move 16 al a 0011001111001... .......... U U U 16 16 6 +move 16 al . 0011001111...... A+-DXWLdxI U U U 16 16 6 +move 32 d d 0010...000000... .......... U U U 4 4 2 +move 32 d a 0010...000001... .......... U U U 4 4 2 +move 32 d . 0010...000...... A+-DXWLdxI U U U 4 4 2 +move 32 ai d 0010...010000... .......... U U U 12 12 4 +move 32 ai a 0010...010001... .......... U U U 12 12 4 +move 32 ai . 0010...010...... A+-DXWLdxI U U U 12 12 4 +move 32 pi d 0010...011000... .......... U U U 12 12 4 +move 32 pi a 0010...011001... .......... U U U 12 12 4 +move 32 pi . 0010...011...... A+-DXWLdxI U U U 12 12 4 +move 32 pd d 0010...100000... .......... U U U 12 14 5 +move 32 pd a 0010...100001... .......... U U U 12 14 5 +move 32 pd . 0010...100...... A+-DXWLdxI U U U 12 14 5 +move 32 di d 0010...101000... .......... U U U 16 16 5 +move 32 di a 0010...101001... .......... U U U 16 16 5 +move 32 di . 0010...101...... A+-DXWLdxI U U U 16 16 5 +move 32 ix d 0010...110000... .......... U U U 18 18 7 +move 32 ix a 0010...110001... .......... U U U 18 18 7 +move 32 ix . 0010...110...... A+-DXWLdxI U U U 18 18 7 +move 32 aw d 0010000111000... .......... U U U 16 16 4 +move 32 aw a 0010000111001... .......... U U U 16 16 4 +move 32 aw . 0010000111...... A+-DXWLdxI U U U 16 16 4 +move 32 al d 0010001111000... .......... U U U 20 20 6 +move 32 al a 0010001111001... .......... U U U 20 20 6 +move 32 al . 0010001111...... A+-DXWLdxI U U U 20 20 6 +movea 16 . d 0011...001000... .......... U U U 4 4 2 +movea 16 . a 0011...001001... .......... U U U 4 4 2 +movea 16 . . 0011...001...... A+-DXWLdxI U U U 4 4 2 +movea 32 . d 0010...001000... .......... U U U 4 4 2 +movea 32 . a 0010...001001... .......... U U U 4 4 2 +movea 32 . . 0010...001...... A+-DXWLdxI U U U 4 4 2 +move 16 frc d 0100001011000... .......... . U U . 4 4 +move 16 frc . 0100001011...... A+-DXWL... . U U . 8 4 +move 16 toc d 0100010011000... .......... U U U 12 12 4 +move 16 toc . 0100010011...... A+-DXWLdxI U U U 12 12 4 +move 16 frs d 0100000011000... .......... U S S 6 4 8 U only for 000 +move 16 frs . 0100000011...... A+-DXWL... U S S 8 8 8 U only for 000 +move 16 tos d 0100011011000... .......... S S S 12 12 8 +move 16 tos . 0100011011...... A+-DXWLdxI S S S 12 12 8 +move 32 fru . 0100111001101... .......... S S S 4 6 2 +move 32 tou . 0100111001100... .......... S S S 4 6 2 +movec 32 cr . 0100111001111010 .......... . S S . 12 6 +movec 32 rc . 0100111001111011 .......... . S S . 10 12 +movem 16 re pd 0100100010100... .......... U U U 8 8 4 +movem 16 re . 0100100010...... A..DXWL... U U U 8 8 4 +movem 32 re pd 0100100011100... .......... U U U 8 8 4 +movem 32 re . 0100100011...... A..DXWL... U U U 8 8 4 +movem 16 er pi 0100110010011... .......... U U U 12 12 8 +movem 16 er pcdi 0100110010111010 .......... U U U 16 16 9 +movem 16 er pcix 0100110010111011 .......... U U U 18 18 11 +movem 16 er . 0100110010...... A..DXWL... U U U 12 12 8 +movem 32 er pi 0100110011011... .......... U U U 12 12 8 +movem 32 er pcdi 0100110011111010 .......... U U U 16 16 9 +movem 32 er pcix 0100110011111011 .......... U U U 18 18 11 +movem 32 er . 0100110011...... A..DXWL... U U U 12 12 8 +movep 16 er . 0000...100001... .......... U U U 16 16 12 +movep 32 er . 0000...101001... .......... U U U 24 24 18 +movep 16 re . 0000...110001... .......... U U U 16 16 11 +movep 32 re . 0000...111001... .......... U U U 24 24 17 +moveq 32 . . 0111...0........ .......... U U U 4 4 2 +moves 8 . . 0000111000...... A+-DXWL... . S S . 14 5 +moves 16 . . 0000111001...... A+-DXWL... . S S . 14 5 +moves 32 . . 0000111010...... A+-DXWL... . S S . 16 5 +muls 16 . d 1100...111000... .......... U U U 54 32 27 +muls 16 . . 1100...111...... A+-DXWLdxI U U U 54 32 27 +mulu 16 . d 1100...011000... .......... U U U 54 30 27 +mulu 16 . . 1100...011...... A+-DXWLdxI U U U 54 30 27 +mull 32 . d 0100110000000... .......... . . U . . 43 +mull 32 . . 0100110000...... A+-DXWLdxI . . U . . 43 +nbcd 8 . d 0100100000000... .......... U U U 6 6 6 +nbcd 8 . . 0100100000...... A+-DXWL... U U U 8 8 6 +neg 8 . d 0100010000000... .......... U U U 4 4 2 +neg 8 . . 0100010000...... A+-DXWL... U U U 8 8 4 +neg 16 . d 0100010001000... .......... U U U 4 4 2 +neg 16 . . 0100010001...... A+-DXWL... U U U 8 8 4 +neg 32 . d 0100010010000... .......... U U U 6 6 2 +neg 32 . . 0100010010...... A+-DXWL... U U U 12 12 4 +negx 8 . d 0100000000000... .......... U U U 4 4 2 +negx 8 . . 0100000000...... A+-DXWL... U U U 8 8 4 +negx 16 . d 0100000001000... .......... U U U 4 4 2 +negx 16 . . 0100000001...... A+-DXWL... U U U 8 8 4 +negx 32 . d 0100000010000... .......... U U U 6 6 2 +negx 32 . . 0100000010...... A+-DXWL... U U U 12 12 4 +nop 0 . . 0100111001110001 .......... U U U 4 4 2 +not 8 . d 0100011000000... .......... U U U 4 4 2 +not 8 . . 0100011000...... A+-DXWL... U U U 8 8 4 +not 16 . d 0100011001000... .......... U U U 4 4 2 +not 16 . . 0100011001...... A+-DXWL... U U U 8 8 4 +not 32 . d 0100011010000... .......... U U U 6 6 2 +not 32 . . 0100011010...... A+-DXWL... U U U 12 12 4 +or 8 er d 1000...000000... .......... U U U 4 4 2 +or 8 er . 1000...000...... A+-DXWLdxI U U U 4 4 2 +or 16 er d 1000...001000... .......... U U U 4 4 2 +or 16 er . 1000...001...... A+-DXWLdxI U U U 4 4 2 +or 32 er d 1000...010000... .......... U U U 6 6 2 +or 32 er . 1000...010...... A+-DXWLdxI U U U 6 6 2 +or 8 re . 1000...100...... A+-DXWL... U U U 8 8 4 +or 16 re . 1000...101...... A+-DXWL... U U U 8 8 4 +or 32 re . 1000...110...... A+-DXWL... U U U 12 12 4 +ori 16 toc . 0000000000111100 .......... U U U 20 16 12 +ori 16 tos . 0000000001111100 .......... S S S 20 16 12 +ori 8 . d 0000000000000... .......... U U U 8 8 2 +ori 8 . . 0000000000...... A+-DXWL... U U U 12 12 4 +ori 16 . d 0000000001000... .......... U U U 8 8 2 +ori 16 . . 0000000001...... A+-DXWL... U U U 12 12 4 +ori 32 . d 0000000010000... .......... U U U 16 14 2 +ori 32 . . 0000000010...... A+-DXWL... U U U 20 20 4 +pack 16 rr . 1000...101000... .......... . . U . . 6 +pack 16 mm ax7 1000111101001... .......... . . U . . 13 +pack 16 mm ay7 1000...101001111 .......... . . U . . 13 +pack 16 mm axy7 1000111101001111 .......... . . U . . 13 +pack 16 mm . 1000...101001... .......... . . U . . 13 +pea 32 . . 0100100001...... A..DXWLdx. U U U 6 6 5 +reset 0 . . 0100111001110000 .......... S S S 0 0 0 +ror 8 s . 1110...000011... .......... U U U 6 6 8 +ror 16 s . 1110...001011... .......... U U U 6 6 8 +ror 32 s . 1110...010011... .......... U U U 8 8 8 +ror 8 r . 1110...000111... .......... U U U 6 6 8 +ror 16 r . 1110...001111... .......... U U U 6 6 8 +ror 32 r . 1110...010111... .......... U U U 8 8 8 +ror 16 . . 1110011011...... A+-DXWL... U U U 8 8 7 +rol 8 s . 1110...100011... .......... U U U 6 6 8 +rol 16 s . 1110...101011... .......... U U U 6 6 8 +rol 32 s . 1110...110011... .......... U U U 8 8 8 +rol 8 r . 1110...100111... .......... U U U 6 6 8 +rol 16 r . 1110...101111... .......... U U U 6 6 8 +rol 32 r . 1110...110111... .......... U U U 8 8 8 +rol 16 . . 1110011111...... A+-DXWL... U U U 8 8 7 +roxr 8 s . 1110...000010... .......... U U U 6 6 12 +roxr 16 s . 1110...001010... .......... U U U 6 6 12 +roxr 32 s . 1110...010010... .......... U U U 8 8 12 +roxr 8 r . 1110...000110... .......... U U U 6 6 12 +roxr 16 r . 1110...001110... .......... U U U 6 6 12 +roxr 32 r . 1110...010110... .......... U U U 8 8 12 +roxr 16 . . 1110010011...... A+-DXWL... U U U 8 8 5 +roxl 8 s . 1110...100010... .......... U U U 6 6 12 +roxl 16 s . 1110...101010... .......... U U U 6 6 12 +roxl 32 s . 1110...110010... .......... U U U 8 8 12 +roxl 8 r . 1110...100110... .......... U U U 6 6 12 +roxl 16 r . 1110...101110... .......... U U U 6 6 12 +roxl 32 r . 1110...110110... .......... U U U 8 8 12 +roxl 16 . . 1110010111...... A+-DXWL... U U U 8 8 5 +rtd 32 . . 0100111001110100 .......... . U U . 16 10 +rte 32 . . 0100111001110011 .......... S S S 20 24 20 bus fault not emulated +rtm 32 . . 000001101100.... .......... . . U . . 19 not properly emulated +rtr 32 . . 0100111001110111 .......... U U U 20 20 14 +rts 32 . . 0100111001110101 .......... U U U 16 16 10 +sbcd 8 rr . 1000...100000... .......... U U U 6 6 4 +sbcd 8 mm ax7 1000111100001... .......... U U U 18 18 16 +sbcd 8 mm ay7 1000...100001111 .......... U U U 18 18 16 +sbcd 8 mm axy7 1000111100001111 .......... U U U 18 18 16 +sbcd 8 mm . 1000...100001... .......... U U U 18 18 16 +st 8 . d 0101000011000... .......... U U U 6 4 4 +st 8 . . 0101000011...... A+-DXWL... U U U 8 8 6 +sf 8 . d 0101000111000... .......... U U U 4 4 4 +sf 8 . . 0101000111...... A+-DXWL... U U U 8 8 6 +scc 8 . d 0101....11000... .......... U U U 4 4 4 +scc 8 . . 0101....11...... A+-DXWL... U U U 8 8 6 +stop 0 . . 0100111001110010 .......... S S S 4 4 8 +sub 8 er d 1001...000000... .......... U U U 4 4 2 +sub 8 er . 1001...000...... A+-DXWLdxI U U U 4 4 2 +sub 16 er d 1001...001000... .......... U U U 4 4 2 +sub 16 er a 1001...001001... .......... U U U 4 4 2 +sub 16 er . 1001...001...... A+-DXWLdxI U U U 4 4 2 +sub 32 er d 1001...010000... .......... U U U 6 6 2 +sub 32 er a 1001...010001... .......... U U U 6 6 2 +sub 32 er . 1001...010...... A+-DXWLdxI U U U 6 6 2 +sub 8 re . 1001...100...... A+-DXWL... U U U 8 8 4 +sub 16 re . 1001...101...... A+-DXWL... U U U 8 8 4 +sub 32 re . 1001...110...... A+-DXWL... U U U 12 12 4 +suba 16 . d 1001...011000... .......... U U U 8 8 2 +suba 16 . a 1001...011001... .......... U U U 8 8 2 +suba 16 . . 1001...011...... A+-DXWLdxI U U U 8 8 2 +suba 32 . d 1001...111000... .......... U U U 6 6 2 +suba 32 . a 1001...111001... .......... U U U 6 6 2 +suba 32 . . 1001...111...... A+-DXWLdxI U U U 6 6 2 +subi 8 . d 0000010000000... .......... U U U 8 8 2 +subi 8 . . 0000010000...... A+-DXWL... U U U 12 12 4 +subi 16 . d 0000010001000... .......... U U U 8 8 2 +subi 16 . . 0000010001...... A+-DXWL... U U U 12 12 4 +subi 32 . d 0000010010000... .......... U U U 16 14 2 +subi 32 . . 0000010010...... A+-DXWL... U U U 20 20 4 +subq 8 . d 0101...100000... .......... U U U 4 4 2 +subq 8 . . 0101...100...... A+-DXWL... U U U 8 8 4 +subq 16 . d 0101...101000... .......... U U U 4 4 2 +subq 16 . a 0101...101001... .......... U U U 8 4 2 +subq 16 . . 0101...101...... A+-DXWL... U U U 8 8 4 +subq 32 . d 0101...110000... .......... U U U 8 8 2 +subq 32 . a 0101...110001... .......... U U U 8 8 2 +subq 32 . . 0101...110...... A+-DXWL... U U U 12 12 4 +subx 8 rr . 1001...100000... .......... U U U 4 4 2 +subx 16 rr . 1001...101000... .......... U U U 4 4 2 +subx 32 rr . 1001...110000... .......... U U U 8 6 2 +subx 8 mm ax7 1001111100001... .......... U U U 18 18 12 +subx 8 mm ay7 1001...100001111 .......... U U U 18 18 12 +subx 8 mm axy7 1001111100001111 .......... U U U 18 18 12 +subx 8 mm . 1001...100001... .......... U U U 18 18 12 +subx 16 mm . 1001...101001... .......... U U U 18 18 12 +subx 32 mm . 1001...110001... .......... U U U 30 30 12 +swap 32 . . 0100100001000... .......... U U U 4 4 4 +tas 8 . d 0100101011000... .......... U U U 4 4 4 +tas 8 . . 0100101011...... A+-DXWL... U U U 14 14 12 +trap 0 . . 010011100100.... .......... U U U 4 4 4 +trapt 0 . . 0101000011111100 .......... . . U . . 4 +trapt 16 . . 0101000011111010 .......... . . U . . 6 +trapt 32 . . 0101000011111011 .......... . . U . . 8 +trapf 0 . . 0101000111111100 .......... . . U . . 4 +trapf 16 . . 0101000111111010 .......... . . U . . 6 +trapf 32 . . 0101000111111011 .......... . . U . . 8 +trapcc 0 . . 0101....11111100 .......... . . U . . 4 +trapcc 16 . . 0101....11111010 .......... . . U . . 6 +trapcc 32 . . 0101....11111011 .......... . . U . . 8 +trapv 0 . . 0100111001110110 .......... U U U 4 4 4 +tst 8 . d 0100101000000... .......... U U U 4 4 2 +tst 8 . . 0100101000...... A+-DXWL... U U U 4 4 2 +tst 8 . pcdi 0100101000111010 .......... . . U . . 7 +tst 8 . pcix 0100101000111011 .......... . . U . . 9 +tst 8 . i 0100101000111100 .......... . . U . . 6 +tst 16 . d 0100101001000... .......... U U U 4 4 2 +tst 16 . a 0100101001001... .......... . . U . . 2 +tst 16 . . 0100101001...... A+-DXWL... U U U 4 4 2 +tst 16 . pcdi 0100101001111010 .......... . . U . . 7 +tst 16 . pcix 0100101001111011 .......... . . U . . 9 +tst 16 . i 0100101001111100 .......... . . U . . 6 +tst 32 . d 0100101010000... .......... U U U 4 4 2 +tst 32 . a 0100101010001... .......... . . U . . 2 +tst 32 . . 0100101010...... A+-DXWL... U U U 4 4 2 +tst 32 . pcdi 0100101010111010 .......... . . U . . 7 +tst 32 . pcix 0100101010111011 .......... . . U . . 9 +tst 32 . i 0100101010111100 .......... . . U . . 6 +unlk 32 . a7 0100111001011111 .......... U U U 12 12 6 +unlk 32 . . 0100111001011... .......... U U U 12 12 6 +unpk 16 rr . 1000...110000... .......... . . U . . 8 +unpk 16 mm ax7 1000111110001... .......... . . U . . 13 +unpk 16 mm ay7 1000...110001111 .......... . . U . . 13 +unpk 16 mm axy7 1000111110001111 .......... . . U . . 13 +unpk 16 mm . 1000...110001... .......... . . U . . 13 + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_OPCODE_HANDLER_BODY + +M68KMAKE_OP(1010, 0, ., .) +{ + m68ki_exception_1010(); +} + + +M68KMAKE_OP(1111, 0, ., .) +{ + m68ki_exception_1111(); +} + + +M68KMAKE_OP(abcd, 8, rr, .) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = LOW_NIBBLE(src) + LOW_NIBBLE(dst) + XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res += 6; + res += HIGH_NIBBLE(src) + HIGH_NIBBLE(dst); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res -= 0xa0; + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; +} + + +M68KMAKE_OP(abcd, 8, mm, ax7) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(src) + LOW_NIBBLE(dst) + XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res += 6; + res += HIGH_NIBBLE(src) + HIGH_NIBBLE(dst); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res -= 0xa0; + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(abcd, 8, mm, ay7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(src) + LOW_NIBBLE(dst) + XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res += 6; + res += HIGH_NIBBLE(src) + HIGH_NIBBLE(dst); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res -= 0xa0; + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(abcd, 8, mm, axy7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(src) + LOW_NIBBLE(dst) + XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res += 6; + res += HIGH_NIBBLE(src) + HIGH_NIBBLE(dst); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res -= 0xa0; + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(abcd, 8, mm, .) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(src) + LOW_NIBBLE(dst) + XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res += 6; + res += HIGH_NIBBLE(src) + HIGH_NIBBLE(dst); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res -= 0xa0; + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(add, 8, er, d) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_8(DY); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(add, 8, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_8; + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(add, 16, er, d) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(DY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(add, 16, er, a) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(AY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(add, 16, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_16; + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(add, 32, er, d) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(add, 32, er, a) +{ + uint* r_dst = &DX; + uint src = AY; + uint dst = *r_dst; + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(add, 32, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_32; + uint dst = *r_dst; + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(add, 8, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = MASK_OUT_ABOVE_8(DX); + uint dst = m68ki_read_8(ea); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(add, 16, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = MASK_OUT_ABOVE_16(DX); + uint dst = m68ki_read_16(ea); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(add, 32, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint src = DX; + uint dst = m68ki_read_32(ea); + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(adda, 16, ., d) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + MAKE_INT_16(DY)); +} + + +M68KMAKE_OP(adda, 16, ., a) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + MAKE_INT_16(AY)); +} + + +M68KMAKE_OP(adda, 16, ., .) +{ + uint* r_dst = &AX; + uint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + src); +} + + +M68KMAKE_OP(adda, 32, ., d) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + DY); +} + + +M68KMAKE_OP(adda, 32, ., a) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + AY); +} + + +M68KMAKE_OP(adda, 32, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_32; + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + src); +} + + +M68KMAKE_OP(addi, 8, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_8(); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(addi, 8, ., .) +{ + uint src = OPER_I_8(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(addi, 16, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_16(); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(addi, 16, ., .) +{ + uint src = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint dst = m68ki_read_16(ea); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(addi, 32, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_32(); + uint dst = *r_dst; + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(addi, 32, ., .) +{ + uint src = OPER_I_32(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint dst = m68ki_read_32(ea); + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(addq, 8, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(addq, 8, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + uint res = src + dst; + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(addq, 16, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(addq, 16, ., a) +{ + uint* r_dst = &AY; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + (((REG_IR >> 9) - 1) & 7) + 1); +} + + +M68KMAKE_OP(addq, 16, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_16; + uint dst = m68ki_read_16(ea); + uint res = src + dst; + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(addq, 32, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = *r_dst; + uint res = src + dst; + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(addq, 32, ., a) +{ + uint* r_dst = &AY; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst + (((REG_IR >> 9) - 1) & 7) + 1); +} + + +M68KMAKE_OP(addq, 32, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_32; + uint dst = m68ki_read_32(ea); + uint res = src + dst; + + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(addx, 8, rr, .) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_8(DY); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; +} + + +M68KMAKE_OP(addx, 16, rr, .) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(DY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; +} + + +M68KMAKE_OP(addx, 32, rr, .) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + *r_dst = res; +} + + +M68KMAKE_OP(addx, 8, mm, ax7) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(addx, 8, mm, ay7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(addx, 8, mm, axy7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(addx, 8, mm, .) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_V = VFLAG_ADD_8(src, dst, res); + FLAG_X = FLAG_C = CFLAG_8(res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(addx, 16, mm, .) +{ + uint src = OPER_AY_PD_16(); + uint ea = EA_AX_PD_16(); + uint dst = m68ki_read_16(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_V = VFLAG_ADD_16(src, dst, res); + FLAG_X = FLAG_C = CFLAG_16(res); + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + m68ki_write_16(ea, res); +} + + +M68KMAKE_OP(addx, 32, mm, .) +{ + uint src = OPER_AY_PD_32(); + uint ea = EA_AX_PD_32(); + uint dst = m68ki_read_32(ea); + uint res = src + dst + XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_ADD_32(src, dst, res); + FLAG_X = FLAG_C = CFLAG_ADD_32(src, dst, res); + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + m68ki_write_32(ea, res); +} + + +M68KMAKE_OP(and, 8, er, d) +{ + FLAG_Z = MASK_OUT_ABOVE_8(DX &= (DY | 0xffffff00)); + + FLAG_N = NFLAG_8(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 8, er, .) +{ + FLAG_Z = MASK_OUT_ABOVE_8(DX &= (M68KMAKE_GET_OPER_AY_8 | 0xffffff00)); + + FLAG_N = NFLAG_8(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 16, er, d) +{ + FLAG_Z = MASK_OUT_ABOVE_16(DX &= (DY | 0xffff0000)); + + FLAG_N = NFLAG_16(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 16, er, .) +{ + FLAG_Z = MASK_OUT_ABOVE_16(DX &= (M68KMAKE_GET_OPER_AY_16 | 0xffff0000)); + + FLAG_N = NFLAG_16(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 32, er, d) +{ + FLAG_Z = DX &= DY; + + FLAG_N = NFLAG_32(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 32, er, .) +{ + FLAG_Z = DX &= M68KMAKE_GET_OPER_AY_32; + + FLAG_N = NFLAG_32(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(and, 8, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = DX & m68ki_read_8(ea); + + FLAG_N = NFLAG_8(res); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_Z = MASK_OUT_ABOVE_8(res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(and, 16, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = DX & m68ki_read_16(ea); + + FLAG_N = NFLAG_16(res); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_Z = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(and, 32, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = DX & m68ki_read_32(ea); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + + m68ki_write_32(ea, res); +} + + +M68KMAKE_OP(andi, 8, ., d) +{ + FLAG_Z = MASK_OUT_ABOVE_8(DY &= (OPER_I_8() | 0xffffff00)); + + FLAG_N = NFLAG_8(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(andi, 8, ., .) +{ + uint src = OPER_I_8(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = src & m68ki_read_8(ea); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(andi, 16, ., d) +{ + FLAG_Z = MASK_OUT_ABOVE_16(DY &= (OPER_I_16() | 0xffff0000)); + + FLAG_N = NFLAG_16(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(andi, 16, ., .) +{ + uint src = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = src & m68ki_read_16(ea); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + + m68ki_write_16(ea, res); +} + + +M68KMAKE_OP(andi, 32, ., d) +{ + FLAG_Z = DY &= (OPER_I_32()); + + FLAG_N = NFLAG_32(FLAG_Z); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(andi, 32, ., .) +{ + uint src = OPER_I_32(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = src & m68ki_read_32(ea); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + + m68ki_write_32(ea, res); +} + + +M68KMAKE_OP(andi, 16, toc, .) +{ + m68ki_set_ccr(m68ki_get_ccr() & OPER_I_8()); +} + + +M68KMAKE_OP(andi, 16, tos, .) +{ + if(FLAG_S) + { + uint src = OPER_I_16(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_set_sr(m68ki_get_sr() & src); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(asr, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> (shift - 1))<<8; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + if(GET_MSB_16(src)) + { + *r_dst |= 0xffff; + FLAG_C = CFLAG_SET; + FLAG_X = XFLAG_SET; + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst &= 0xffff0000; + FLAG_C = CFLAG_CLEAR; + FLAG_X = XFLAG_CLEAR; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(asr, 32, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = *r_dst; + uint res = src >> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> (shift - 1))<<8; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + if(GET_MSB_32(src)) + { + *r_dst = 0xffffffff; + FLAG_C = CFLAG_SET; + FLAG_X = XFLAG_SET; + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst = 0; + FLAG_C = CFLAG_CLEAR; + FLAG_X = XFLAG_CLEAR; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(asr, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = src >> 1; + + if(GET_MSB_16(src)) + res |= 0x8000; + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = FLAG_X = src << 8; +} + + +M68KMAKE_OP(asl, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = MASK_OUT_ABOVE_8(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = MASK_OUT_ABOVE_16(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> (8-shift); + src &= m68ki_shift_16_table[shift + 1]; + FLAG_V = (!(src == 0 || src == m68ki_shift_16_table[shift + 1]))<<7; +} + + +M68KMAKE_OP(asl, 32, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> (24-shift); + src &= m68ki_shift_32_table[shift + 1]; + FLAG_V = (!(src == 0 || src == m68ki_shift_32_table[shift + 1]))<<7; +} + + +M68KMAKE_OP(asl, 8, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = MASK_OUT_ABOVE_8(src << shift); + + if(shift != 0) + { + USE_CYCLES(shift<> 8; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + src &= m68ki_shift_16_table[shift + 1]; + FLAG_V = (!(src == 0 || src == m68ki_shift_16_table[shift + 1]))<<7; + return; + } + + *r_dst &= 0xffff0000; + FLAG_X = FLAG_C = ((shift == 16 ? src & 1 : 0))<<8; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = (!(src == 0))<<7; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(asl, 32, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32(src << shift); + + if(shift != 0) + { + USE_CYCLES(shift<> (32 - shift)) << 8; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + src &= m68ki_shift_32_table[shift + 1]; + FLAG_V = (!(src == 0 || src == m68ki_shift_32_table[shift + 1]))<<7; + return; + } + + *r_dst = 0; + FLAG_X = FLAG_C = ((shift == 32 ? src & 1 : 0))<<8; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = (!(src == 0))<<7; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(asl, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = MASK_OUT_ABOVE_16(src << 1); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_X = FLAG_C = src >> 7; + src &= 0xc000; + FLAG_V = (!(src == 0 || src == 0xc000))<<7; +} + + +M68KMAKE_OP(bcc, 8, ., .) +{ + if(M68KMAKE_CC) + { + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_8(MASK_OUT_ABOVE_8(REG_IR)); + return; + } + USE_CYCLES(CYC_BCC_NOTAKE_B); +} + + +M68KMAKE_OP(bcc, 16, ., .) +{ + if(M68KMAKE_CC) + { + uint offset = OPER_I_16(); + REG_PC -= 2; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_16(offset); + return; + } + REG_PC += 2; + USE_CYCLES(CYC_BCC_NOTAKE_W); +} + + +M68KMAKE_OP(bcc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + if(M68KMAKE_CC) + { + uint offset = OPER_I_32(); + REG_PC -= 4; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_32(offset); + return; + } + REG_PC += 4; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bchg, 32, r, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (DX & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst ^= mask; +} + + +M68KMAKE_OP(bchg, 8, r, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + uint mask = 1 << (DX & 7); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src ^ mask); +} + + +M68KMAKE_OP(bchg, 32, s, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (OPER_I_8() & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst ^= mask; +} + + +M68KMAKE_OP(bchg, 8, s, .) +{ + uint mask = 1 << (OPER_I_8() & 7); + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src ^ mask); +} + + +M68KMAKE_OP(bclr, 32, r, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (DX & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst &= ~mask; +} + + +M68KMAKE_OP(bclr, 8, r, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + uint mask = 1 << (DX & 7); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src & ~mask); +} + + +M68KMAKE_OP(bclr, 32, s, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (OPER_I_8() & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst &= ~mask; +} + + +M68KMAKE_OP(bclr, 8, s, .) +{ + uint mask = 1 << (OPER_I_8() & 7); + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src & ~mask); +} + + +M68KMAKE_OP(bfchg, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint* data = &DY; + uint64 mask; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + offset &= 31; + width = ((width-1) & 31) + 1; + + mask = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask = ROR_32(mask, offset); + + FLAG_N = NFLAG_32(*data<>6)&31; + uint width = word2; + uint mask_base; + uint data_long; + uint mask_long; + uint data_byte = 0; + uint mask_byte = 0; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + mask_base = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask_long = mask_base >> offset; + + data_long = m68ki_read_32(ea); + FLAG_N = NFLAG_32(data_long << offset); + FLAG_Z = data_long & mask_long; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + m68ki_write_32(ea, data_long ^ mask_long); + + if((width + offset) > 32) + { + mask_byte = MASK_OUT_ABOVE_8(mask_base); + data_byte = m68ki_read_8(ea+4); + FLAG_Z |= (data_byte & mask_byte); + m68ki_write_8(ea+4, data_byte ^ mask_byte); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfclr, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint* data = &DY; + uint64 mask; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + + offset &= 31; + width = ((width-1) & 31) + 1; + + + mask = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask = ROR_32(mask, offset); + + FLAG_N = NFLAG_32(*data<>6)&31; + uint width = word2; + uint mask_base; + uint data_long; + uint mask_long; + uint data_byte = 0; + uint mask_byte = 0; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + mask_base = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask_long = mask_base >> offset; + + data_long = m68ki_read_32(ea); + FLAG_N = NFLAG_32(data_long << offset); + FLAG_Z = data_long & mask_long; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + m68ki_write_32(ea, data_long & ~mask_long); + + if((width + offset) > 32) + { + mask_byte = MASK_OUT_ABOVE_8(mask_base); + data_byte = m68ki_read_8(ea+4); + FLAG_Z |= (data_byte & mask_byte); + m68ki_write_8(ea+4, data_byte & ~mask_byte); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfexts, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint64 data = DY; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + offset &= 31; + width = ((width-1) & 31) + 1; + + data = ROL_32(data, offset); + FLAG_N = NFLAG_32(data); + data = MAKE_INT_32(data) >> (32 - width); + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + REG_D[(word2>>12)&7] = data; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfexts, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint offset = (word2>>6)&31; + uint width = word2; + uint data; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + data = m68ki_read_32(ea); + + data = MASK_OUT_ABOVE_32(data< 32) + data |= (m68ki_read_8(ea+4) << offset) >> 8; + + FLAG_N = NFLAG_32(data); + data = MAKE_INT_32(data) >> (32 - width); + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + REG_D[(word2 >> 12) & 7] = data; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfextu, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint64 data = DY; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + offset &= 31; + width = ((width-1) & 31) + 1; + + data = ROL_32(data, offset); + FLAG_N = NFLAG_32(data); + data >>= 32 - width; + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + REG_D[(word2>>12)&7] = data; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfextu, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint offset = (word2>>6)&31; + uint width = word2; + uint data; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + data = m68ki_read_32(ea); + data = MASK_OUT_ABOVE_32(data< 32) + data |= (m68ki_read_8(ea+4) << offset) >> 8; + + FLAG_N = NFLAG_32(data); + data >>= (32 - width); + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + REG_D[(word2 >> 12) & 7] = data; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfffo, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint64 data = DY; + uint bit; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + offset &= 31; + width = ((width-1) & 31) + 1; + + data = ROL_32(data, offset); + FLAG_N = NFLAG_32(data); + data >>= 32 - width; + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + for(bit = 1<<(width-1);bit && !(data & bit);bit>>= 1) + offset++; + + REG_D[(word2>>12)&7] = offset; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfffo, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint offset = (word2>>6)&31; + sint local_offset; + uint width = word2; + uint data; + uint bit; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + local_offset = offset % 8; + if(local_offset < 0) + { + local_offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + data = m68ki_read_32(ea); + data = MASK_OUT_ABOVE_32(data< 32) + data |= (m68ki_read_8(ea+4) << local_offset) >> 8; + + FLAG_N = NFLAG_32(data); + data >>= (32 - width); + + FLAG_Z = data; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + for(bit = 1<<(width-1);bit && !(data & bit);bit>>= 1) + offset++; + + REG_D[(word2>>12)&7] = offset; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfins, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint* data = &DY; + uint64 mask; + uint64 insert = REG_D[(word2>>12)&7]; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + + offset &= 31; + width = ((width-1) & 31) + 1; + + + mask = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask = ROR_32(mask, offset); + + insert = MASK_OUT_ABOVE_32(insert << (32 - width)); + FLAG_N = NFLAG_32(insert); + FLAG_Z = insert; + insert = ROR_32(insert, offset); + + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + *data &= ~mask; + *data |= insert; + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfins, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint offset = (word2>>6)&31; + uint width = word2; + uint insert_base = REG_D[(word2>>12)&7]; + uint insert_long; + uint insert_byte; + uint mask_base; + uint data_long; + uint mask_long; + uint data_byte = 0; + uint mask_byte = 0; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + mask_base = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask_long = mask_base >> offset; + + insert_base = MASK_OUT_ABOVE_32(insert_base << (32 - width)); + FLAG_N = NFLAG_32(insert_base); + FLAG_Z = insert_base; + insert_long = insert_base >> offset; + + data_long = m68ki_read_32(ea); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + m68ki_write_32(ea, (data_long & ~mask_long) | insert_long); + + if((width + offset) > 32) + { + mask_byte = MASK_OUT_ABOVE_8(mask_base); + insert_byte = MASK_OUT_ABOVE_8(insert_base); + data_byte = m68ki_read_8(ea+4); + FLAG_Z |= (data_byte & mask_byte); + m68ki_write_8(ea+4, (data_byte & ~mask_byte) | insert_byte); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bfset, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint* data = &DY; + uint64 mask; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + + offset &= 31; + width = ((width-1) & 31) + 1; + + + mask = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask = ROR_32(mask, offset); + + FLAG_N = NFLAG_32(*data<>6)&31; + uint width = word2; + uint mask_base; + uint data_long; + uint mask_long; + uint data_byte = 0; + uint mask_byte = 0; + uint ea = M68KMAKE_GET_EA_AY_8; + + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + + mask_base = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask_long = mask_base >> offset; + + data_long = m68ki_read_32(ea); + FLAG_N = NFLAG_32(data_long << offset); + FLAG_Z = data_long & mask_long; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + m68ki_write_32(ea, data_long | mask_long); + + if((width + offset) > 32) + { + mask_byte = MASK_OUT_ABOVE_8(mask_base); + data_byte = m68ki_read_8(ea+4); + FLAG_Z |= (data_byte & mask_byte); + m68ki_write_8(ea+4, data_byte | mask_byte); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bftst, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint offset = (word2>>6)&31; + uint width = word2; + uint* data = &DY; + uint64 mask; + + + if(BIT_B(word2)) + offset = REG_D[offset&7]; + if(BIT_5(word2)) + width = REG_D[width&7]; + + + offset &= 31; + width = ((width-1) & 31) + 1; + + + mask = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask = ROR_32(mask, offset); + + FLAG_N = NFLAG_32(*data<>6)&31; + uint width = word2; + uint mask_base; + uint data_long; + uint mask_long; + uint data_byte = 0; + uint mask_byte = 0; + uint ea = M68KMAKE_GET_EA_AY_8; + + if(BIT_B(word2)) + offset = MAKE_INT_32(REG_D[offset&7]); + if(BIT_5(word2)) + width = REG_D[width&7]; + + /* Offset is signed so we have to use ugly math =( */ + ea += offset / 8; + offset %= 8; + if(offset < 0) + { + offset += 8; + ea--; + } + width = ((width-1) & 31) + 1; + + + mask_base = MASK_OUT_ABOVE_32(0xffffffff << (32 - width)); + mask_long = mask_base >> offset; + + data_long = m68ki_read_32(ea); + FLAG_N = ((data_long & (0x80000000 >> offset))<>24; + FLAG_Z = data_long & mask_long; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + + if((width + offset) > 32) + { + mask_byte = MASK_OUT_ABOVE_8(mask_base); + data_byte = m68ki_read_8(ea+4); + FLAG_Z |= (data_byte & mask_byte); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bkpt, 0, ., .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + m68ki_bkpt_ack(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE) ? REG_IR & 7 : 0); /* auto-disable (see m68kcpu.h) */ + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bra, 8, ., .) +{ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_8(MASK_OUT_ABOVE_8(REG_IR)); + if(REG_PC == REG_PPC) + USE_ALL_CYCLES(); +} + + +M68KMAKE_OP(bra, 16, ., .) +{ + uint offset = OPER_I_16(); + REG_PC -= 2; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_16(offset); + if(REG_PC == REG_PPC) + USE_ALL_CYCLES(); +} + + +M68KMAKE_OP(bra, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint offset = OPER_I_32(); + REG_PC -= 4; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_32(offset); + if(REG_PC == REG_PPC) + USE_ALL_CYCLES(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(bset, 32, r, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (DX & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst |= mask; +} + + +M68KMAKE_OP(bset, 8, r, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + uint mask = 1 << (DX & 7); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src | mask); +} + + +M68KMAKE_OP(bset, 32, s, d) +{ + uint* r_dst = &DY; + uint mask = 1 << (OPER_I_8() & 0x1f); + + FLAG_Z = *r_dst & mask; + *r_dst |= mask; +} + + +M68KMAKE_OP(bset, 8, s, .) +{ + uint mask = 1 << (OPER_I_8() & 7); + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + + FLAG_Z = src & mask; + m68ki_write_8(ea, src | mask); +} + + +M68KMAKE_OP(bsr, 8, ., .) +{ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_push_32(REG_PC); + m68ki_branch_8(MASK_OUT_ABOVE_8(REG_IR)); +} + + +M68KMAKE_OP(bsr, 16, ., .) +{ + uint offset = OPER_I_16(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_push_32(REG_PC); + REG_PC -= 2; + m68ki_branch_16(offset); +} + + +M68KMAKE_OP(bsr, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint offset = OPER_I_32(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_push_32(REG_PC); + REG_PC -= 4; + m68ki_branch_32(offset); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(btst, 32, r, d) +{ + FLAG_Z = DY & (1 << (DX & 0x1f)); +} + + +M68KMAKE_OP(btst, 8, r, .) +{ + FLAG_Z = M68KMAKE_GET_OPER_AY_8 & (1 << (DX & 7)); +} + + +M68KMAKE_OP(btst, 32, s, d) +{ + FLAG_Z = DY & (1 << (OPER_I_8() & 0x1f)); +} + + +M68KMAKE_OP(btst, 8, s, .) +{ + uint bit = OPER_I_8() & 7; + + FLAG_Z = M68KMAKE_GET_OPER_AY_8 & (1 << bit); +} + + +M68KMAKE_OP(callm, 32, ., .) +{ + /* note: watch out for pcrelative modes */ + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + { + uint ea = M68KMAKE_GET_EA_AY_32; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + REG_PC += 2; +(void)ea; /* just to avoid an 'unused variable' warning */ + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cas, 8, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint dest = m68ki_read_8(ea); + uint* compare = ®_D[word2 & 7]; + uint res = dest - MASK_OUT_ABOVE_8(*compare); + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(*compare, dest, res); + FLAG_C = CFLAG_8(res); + + if(COND_NE()) + *compare = MASK_OUT_BELOW_8(*compare) | dest; + else + { + USE_CYCLES(3); + m68ki_write_8(ea, MASK_OUT_ABOVE_8(REG_D[(word2 >> 6) & 7])); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cas, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint dest = m68ki_read_16(ea); + uint* compare = ®_D[word2 & 7]; + uint res = dest - MASK_OUT_ABOVE_16(*compare); + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(*compare, dest, res); + FLAG_C = CFLAG_16(res); + + if(COND_NE()) + *compare = MASK_OUT_BELOW_16(*compare) | dest; + else + { + USE_CYCLES(3); + m68ki_write_16(ea, MASK_OUT_ABOVE_16(REG_D[(word2 >> 6) & 7])); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cas, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint dest = m68ki_read_32(ea); + uint* compare = ®_D[word2 & 7]; + uint res = dest - *compare; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(*compare, dest, res); + FLAG_C = CFLAG_SUB_32(*compare, dest, res); + + if(COND_NE()) + *compare = dest; + else + { + USE_CYCLES(3); + m68ki_write_32(ea, REG_D[(word2 >> 6) & 7]); + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cas2, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_32(); + uint* compare1 = ®_D[(word2 >> 16) & 7]; + uint ea1 = REG_DA[(word2 >> 28) & 15]; + uint dest1 = m68ki_read_16(ea1); + uint res1 = dest1 - MASK_OUT_ABOVE_16(*compare1); + uint* compare2 = ®_D[word2 & 7]; + uint ea2 = REG_DA[(word2 >> 12) & 15]; + uint dest2 = m68ki_read_16(ea2); + uint res2; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + FLAG_N = NFLAG_16(res1); + FLAG_Z = MASK_OUT_ABOVE_16(res1); + FLAG_V = VFLAG_SUB_16(*compare1, dest1, res1); + FLAG_C = CFLAG_16(res1); + + if(COND_EQ()) + { + res2 = dest2 - MASK_OUT_ABOVE_16(*compare2); + + FLAG_N = NFLAG_16(res2); + FLAG_Z = MASK_OUT_ABOVE_16(res2); + FLAG_V = VFLAG_SUB_16(*compare2, dest2, res2); + FLAG_C = CFLAG_16(res2); + + if(COND_EQ()) + { + USE_CYCLES(3); + m68ki_write_16(ea1, REG_D[(word2 >> 22) & 7]); + m68ki_write_16(ea2, REG_D[(word2 >> 6) & 7]); + return; + } + } + *compare1 = BIT_1F(word2) ? (uint)MAKE_INT_16(dest1) : MASK_OUT_BELOW_16(*compare1) | dest1; + *compare2 = BIT_F(word2) ? (uint)MAKE_INT_16(dest2) : MASK_OUT_BELOW_16(*compare2) | dest2; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cas2, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_32(); + uint* compare1 = ®_D[(word2 >> 16) & 7]; + uint ea1 = REG_DA[(word2 >> 28) & 15]; + uint dest1 = m68ki_read_32(ea1); + uint res1 = dest1 - *compare1; + uint* compare2 = ®_D[word2 & 7]; + uint ea2 = REG_DA[(word2 >> 12) & 15]; + uint dest2 = m68ki_read_32(ea2); + uint res2; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + FLAG_N = NFLAG_32(res1); + FLAG_Z = MASK_OUT_ABOVE_32(res1); + FLAG_V = VFLAG_SUB_32(*compare1, dest1, res1); + FLAG_C = CFLAG_SUB_32(*compare1, dest1, res1); + + if(COND_EQ()) + { + res2 = dest2 - *compare2; + + FLAG_N = NFLAG_32(res2); + FLAG_Z = MASK_OUT_ABOVE_32(res2); + FLAG_V = VFLAG_SUB_32(*compare2, dest2, res2); + FLAG_C = CFLAG_SUB_32(*compare2, dest2, res2); + + if(COND_EQ()) + { + USE_CYCLES(3); + m68ki_write_32(ea1, REG_D[(word2 >> 22) & 7]); + m68ki_write_32(ea2, REG_D[(word2 >> 6) & 7]); + return; + } + } + *compare1 = dest1; + *compare2 = dest2; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk, 16, ., d) +{ + sint src = MAKE_INT_16(DX); + sint bound = MAKE_INT_16(DY); + + FLAG_Z = ZFLAG_16(src); /* Undocumented */ + FLAG_V = VFLAG_CLEAR; /* Undocumented */ + FLAG_C = CFLAG_CLEAR; /* Undocumented */ + + if(src >= 0 && src <= bound) + { + return; + } + FLAG_N = (src < 0)<<7; + m68ki_exception_trap(EXCEPTION_CHK); +} + + +M68KMAKE_OP(chk, 16, ., .) +{ + sint src = MAKE_INT_16(DX); + sint bound = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + + FLAG_Z = ZFLAG_16(src); /* Undocumented */ + FLAG_V = VFLAG_CLEAR; /* Undocumented */ + FLAG_C = CFLAG_CLEAR; /* Undocumented */ + + if(src >= 0 && src <= bound) + { + return; + } + FLAG_N = (src < 0)<<7; + m68ki_exception_trap(EXCEPTION_CHK); +} + + +M68KMAKE_OP(chk, 32, ., d) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + sint src = MAKE_INT_32(DX); + sint bound = MAKE_INT_32(DY); + + FLAG_Z = ZFLAG_32(src); /* Undocumented */ + FLAG_V = VFLAG_CLEAR; /* Undocumented */ + FLAG_C = CFLAG_CLEAR; /* Undocumented */ + + if(src >= 0 && src <= bound) + { + return; + } + FLAG_N = (src < 0)<<7; + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + sint src = MAKE_INT_32(DX); + sint bound = MAKE_INT_32(M68KMAKE_GET_OPER_AY_32); + + FLAG_Z = ZFLAG_32(src); /* Undocumented */ + FLAG_V = VFLAG_CLEAR; /* Undocumented */ + FLAG_C = CFLAG_CLEAR; /* Undocumented */ + + if(src >= 0 && src <= bound) + { + return; + } + FLAG_N = (src < 0)<<7; + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 8, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xff; + uint ea = EA_PCDI_8(); + sint lower_bound = m68ki_read_pcrel_8(ea); + sint upper_bound = m68ki_read_pcrel_8(ea + 1); + + if(!BIT_F(word2)) + compare = (INT32)(INT8)compare; + + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 8, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xff; + uint ea = EA_PCIX_8(); + sint lower_bound = m68ki_read_pcrel_8(ea); + sint upper_bound = m68ki_read_pcrel_8(ea + 1); + + if(!BIT_F(word2)) + compare = (INT32)(INT8)compare; + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||, faster operation short circuits + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 8, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xff; + uint ea = M68KMAKE_GET_EA_AY_8; + sint lower_bound = (INT8)m68ki_read_8(ea); + sint upper_bound = (INT8)m68ki_read_8(ea + 1); + + if(!BIT_F(word2)) + compare = (INT32)(INT8)compare; + + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 16, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xffff; + uint ea = EA_PCDI_16(); + sint lower_bound = (INT16)m68ki_read_pcrel_16(ea); + sint upper_bound = (INT16)m68ki_read_pcrel_16(ea + 2); + + if(!BIT_F(word2)) + compare = (INT32)(INT16)compare; + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 16, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xffff; + uint ea = EA_PCIX_16(); + sint lower_bound = (INT16)m68ki_read_pcrel_16(ea); + sint upper_bound = (INT16)m68ki_read_pcrel_16(ea + 2); + + if(!BIT_F(word2)) + compare = (INT32)(INT16)compare; + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]&0xffff; + uint ea = M68KMAKE_GET_EA_AY_16; + sint lower_bound = (INT16)m68ki_read_16(ea); + sint upper_bound = (INT16)m68ki_read_16(ea + 2); + + if(!BIT_F(word2)) + compare = (INT32)(INT16)compare; + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 32, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]; + uint ea = EA_PCDI_32(); + sint lower_bound = m68ki_read_pcrel_32(ea); + sint upper_bound = m68ki_read_pcrel_32(ea + 4); + + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 32, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + sint compare = REG_DA[(word2 >> 12) & 15]; + uint ea = EA_PCIX_32(); + sint lower_bound = m68ki_read_32(ea); + sint upper_bound = m68ki_read_32(ea + 4); + + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(chk2cmp2, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + // JFF changed the logic. chk2/cmp2 uses signed values, not unsigned + sint compare = REG_DA[(word2 >> 12) & 15]; + uint ea = M68KMAKE_GET_EA_AY_32; + sint lower_bound = m68ki_read_32(ea); + sint upper_bound = m68ki_read_32(ea + 4); + + FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => || + + FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8; + + if(COND_CS() && BIT_B(word2)) + m68ki_exception_trap(EXCEPTION_CHK); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(clr, 8, ., d) +{ + DY &= 0xffffff00; + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(clr, 8, ., .) +{ + m68ki_write_8(M68KMAKE_GET_EA_AY_8, 0); + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(clr, 16, ., d) +{ + DY &= 0xffff0000; + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(clr, 16, ., .) +{ + m68ki_write_16(M68KMAKE_GET_EA_AY_16, 0); + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(clr, 32, ., d) +{ + DY = 0; + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(clr, 32, ., .) +{ + m68ki_write_32(M68KMAKE_GET_EA_AY_32, 0); + + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; +} + + +M68KMAKE_OP(cmp, 8, ., d) +{ + uint src = MASK_OUT_ABOVE_8(DY); + uint dst = MASK_OUT_ABOVE_8(DX); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmp, 8, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_8; + uint dst = MASK_OUT_ABOVE_8(DX); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmp, 16, ., d) +{ + uint src = MASK_OUT_ABOVE_16(DY); + uint dst = MASK_OUT_ABOVE_16(DX); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmp, 16, ., a) +{ + uint src = MASK_OUT_ABOVE_16(AY); + uint dst = MASK_OUT_ABOVE_16(DX); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmp, 16, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_16; + uint dst = MASK_OUT_ABOVE_16(DX); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmp, 32, ., d) +{ + uint src = DY; + uint dst = DX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmp, 32, ., a) +{ + uint src = AY; + uint dst = DX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmp, 32, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_32; + uint dst = DX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 16, ., d) +{ + uint src = MAKE_INT_16(DY); + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 16, ., a) +{ + uint src = MAKE_INT_16(AY); + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 16, ., .) +{ + uint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 32, ., d) +{ + uint src = DY; + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 32, ., a) +{ + uint src = AY; + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpa, 32, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_32; + uint dst = AX; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpi, 8, ., d) +{ + uint src = OPER_I_8(); + uint dst = MASK_OUT_ABOVE_8(DY); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpi, 8, ., .) +{ + uint src = OPER_I_8(); + uint dst = M68KMAKE_GET_OPER_AY_8; + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpi, 8, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_8(); + uint dst = OPER_PCDI_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpi, 8, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_8(); + uint dst = OPER_PCIX_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpi, 16, ., d) +{ + uint src = OPER_I_16(); + uint dst = MASK_OUT_ABOVE_16(DY); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmpi, 16, ., .) +{ + uint src = OPER_I_16(); + uint dst = M68KMAKE_GET_OPER_AY_16; + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmpi, 16, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_16(); + uint dst = OPER_PCDI_16(); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpi, 16, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_16(); + uint dst = OPER_PCIX_16(); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpi, 32, ., d) +{ + uint src = OPER_I_32(); + uint dst = DY; + uint res = dst - src; + + m68ki_cmpild_callback(src, REG_IR & 7); /* auto-disable (see m68kcpu.h) */ + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpi, 32, ., .) +{ + uint src = OPER_I_32(); + uint dst = M68KMAKE_GET_OPER_AY_32; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cmpi, 32, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_32(); + uint dst = OPER_PCDI_32(); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpi, 32, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_I_32(); + uint dst = OPER_PCIX_32(); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(cmpm, 8, ., ax7) +{ + uint src = OPER_AY_PI_8(); + uint dst = OPER_A7_PI_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpm, 8, ., ay7) +{ + uint src = OPER_A7_PI_8(); + uint dst = OPER_AX_PI_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpm, 8, ., axy7) +{ + uint src = OPER_A7_PI_8(); + uint dst = OPER_A7_PI_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpm, 8, ., .) +{ + uint src = OPER_AY_PI_8(); + uint dst = OPER_AX_PI_8(); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_C = CFLAG_8(res); +} + + +M68KMAKE_OP(cmpm, 16, ., .) +{ + uint src = OPER_AY_PI_16(); + uint dst = OPER_AX_PI_16(); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_C = CFLAG_16(res); +} + + +M68KMAKE_OP(cmpm, 32, ., .) +{ + uint src = OPER_AY_PI_32(); + uint dst = OPER_AX_PI_32(); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_C = CFLAG_SUB_32(src, dst, res); +} + + +M68KMAKE_OP(cpbcc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_1111(); +} + + +M68KMAKE_OP(cpdbcc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_1111(); +} + + +M68KMAKE_OP(cpgen, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_1111(); +} + + +M68KMAKE_OP(cpscc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_1111(); +} + + +M68KMAKE_OP(cptrapcc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + // JFF: unsupported, but at least if the trap doesn't occur, app should still work, so at least PC increase is correct + REG_PC += 4; + return; + } + m68ki_exception_1111(); +} + + +M68KMAKE_OP(dbt, 16, ., .) +{ + REG_PC += 2; +} + + +M68KMAKE_OP(dbf, 16, ., .) +{ + uint* r_dst = &DY; + uint res = MASK_OUT_ABOVE_16(*r_dst - 1); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + if(res != 0xffff) + { + uint offset = OPER_I_16(); + REG_PC -= 2; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_16(offset); + USE_CYCLES(CYC_DBCC_F_NOEXP); + return; + } + REG_PC += 2; + USE_CYCLES(CYC_DBCC_F_EXP); +} + + +M68KMAKE_OP(dbcc, 16, ., .) +{ + if(M68KMAKE_NOT_CC) + { + uint* r_dst = &DY; + uint res = MASK_OUT_ABOVE_16(*r_dst - 1); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + if(res != 0xffff) + { + uint offset = OPER_I_16(); + REG_PC -= 2; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_branch_16(offset); + USE_CYCLES(CYC_DBCC_F_NOEXP); + return; + } + REG_PC += 2; + USE_CYCLES(CYC_DBCC_F_EXP); + return; + } + REG_PC += 2; +} + + +M68KMAKE_OP(divs, 16, ., d) +{ + uint* r_dst = &DX; + sint src = MAKE_INT_16(DY); + sint quotient; + sint remainder; + + if(src != 0) + { + if((uint32)*r_dst == 0x80000000 && src == -1) + { + FLAG_Z = 0; + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = 0; + return; + } + + quotient = MAKE_INT_32(*r_dst) / src; + remainder = MAKE_INT_32(*r_dst) % src; + + if(quotient == MAKE_INT_16(quotient)) + { + FLAG_Z = quotient; + FLAG_N = NFLAG_16(quotient); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = MASK_OUT_ABOVE_32(MASK_OUT_ABOVE_16(quotient) | (remainder << 16)); + return; + } + FLAG_V = VFLAG_SET; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); +} + + +M68KMAKE_OP(divs, 16, ., .) +{ + uint* r_dst = &DX; + sint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + sint quotient; + sint remainder; + + if(src != 0) + { + if((uint32)*r_dst == 0x80000000 && src == -1) + { + FLAG_Z = 0; + FLAG_N = NFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = 0; + return; + } + + quotient = MAKE_INT_32(*r_dst) / src; + remainder = MAKE_INT_32(*r_dst) % src; + + if(quotient == MAKE_INT_16(quotient)) + { + FLAG_Z = quotient; + FLAG_N = NFLAG_16(quotient); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = MASK_OUT_ABOVE_32(MASK_OUT_ABOVE_16(quotient) | (remainder << 16)); + return; + } + FLAG_V = VFLAG_SET; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); +} + + +M68KMAKE_OP(divu, 16, ., d) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(DY); + + if(src != 0) + { + uint quotient = *r_dst / src; + uint remainder = *r_dst % src; + + if(quotient < 0x10000) + { + FLAG_Z = quotient; + FLAG_N = NFLAG_16(quotient); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = MASK_OUT_ABOVE_32(MASK_OUT_ABOVE_16(quotient) | (remainder << 16)); + return; + } + FLAG_V = VFLAG_SET; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); +} + + +M68KMAKE_OP(divu, 16, ., .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_16; + + if(src != 0) + { + uint quotient = *r_dst / src; + uint remainder = *r_dst % src; + + if(quotient < 0x10000) + { + FLAG_Z = quotient; + FLAG_N = NFLAG_16(quotient); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst = MASK_OUT_ABOVE_32(MASK_OUT_ABOVE_16(quotient) | (remainder << 16)); + return; + } + FLAG_V = VFLAG_SET; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); +} + + +M68KMAKE_OP(divl, 32, ., d) +{ +#if M68K_USE_64_BIT + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint64 divisor = DY; + uint64 dividend = 0; + uint64 quotient = 0; + uint64 remainder = 0; + + if(divisor != 0) + { + if(BIT_A(word2)) /* 64 bit */ + { + dividend = REG_D[word2 & 7]; + dividend <<= 32; + dividend |= REG_D[(word2 >> 12) & 7]; + + if(BIT_B(word2)) /* signed */ + { + quotient = (uint64)((sint64)dividend / (sint64)((sint32)divisor)); + remainder = (uint64)((sint64)dividend % (sint64)((sint32)divisor)); + if((sint64)quotient != (sint64)((sint32)quotient)) + { + FLAG_V = VFLAG_SET; + return; + } + } + else /* unsigned */ + { + quotient = dividend / divisor; + if(quotient > 0xffffffff) + { + FLAG_V = VFLAG_SET; + return; + } + remainder = dividend % divisor; + } + } + else /* 32 bit */ + { + dividend = REG_D[(word2 >> 12) & 7]; + if(BIT_B(word2)) /* signed */ + { + quotient = (uint64)((sint64)((sint32)dividend) / (sint64)((sint32)divisor)); + remainder = (uint64)((sint64)((sint32)dividend) % (sint64)((sint32)divisor)); + } + else /* unsigned */ + { + quotient = dividend / divisor; + remainder = dividend % divisor; + } + } + + REG_D[word2 & 7] = remainder; + REG_D[(word2 >> 12) & 7] = quotient; + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); + return; + } + m68ki_exception_illegal(); + +#else + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint divisor = DY; + uint dividend_hi = REG_D[word2 & 7]; + uint dividend_lo = REG_D[(word2 >> 12) & 7]; + uint quotient = 0; + uint remainder = 0; + uint dividend_neg = 0; + uint divisor_neg = 0; + sint i; + uint overflow; + + if(divisor != 0) + { + /* quad / long : long quotient, long remainder */ + if(BIT_A(word2)) + { + if(BIT_B(word2)) /* signed */ + { + /* special case in signed divide */ + if(dividend_hi == 0 && dividend_lo == 0x80000000 && divisor == 0xffffffff) + { + REG_D[word2 & 7] = 0; + REG_D[(word2 >> 12) & 7] = 0x80000000; + + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + if(GET_MSB_32(dividend_hi)) + { + dividend_neg = 1; + dividend_hi = (uint)MASK_OUT_ABOVE_32((-(sint)dividend_hi) - (dividend_lo != 0)); + dividend_lo = (uint)MASK_OUT_ABOVE_32(-(sint)dividend_lo); + } + if(GET_MSB_32(divisor)) + { + divisor_neg = 1; + divisor = (uint)MASK_OUT_ABOVE_32(-(sint)divisor); + + } + } + + /* if the upper long is greater than the divisor, we're overflowing. */ + if(dividend_hi >= divisor) + { + FLAG_V = VFLAG_SET; + return; + } + + for(i = 31; i >= 0; i--) + { + quotient <<= 1; + remainder = (remainder << 1) + ((dividend_hi >> i) & 1); + if(remainder >= divisor) + { + remainder -= divisor; + quotient++; + } + } + for(i = 31; i >= 0; i--) + { + quotient <<= 1; + overflow = GET_MSB_32(remainder); + remainder = (remainder << 1) + ((dividend_lo >> i) & 1); + if(remainder >= divisor || overflow) + { + remainder -= divisor; + quotient++; + } + } + + if(BIT_B(word2)) /* signed */ + { + if(quotient > 0x7fffffff) + { + FLAG_V = VFLAG_SET; + return; + } + if(dividend_neg) + { + remainder = (uint)MASK_OUT_ABOVE_32(-(sint)remainder); + quotient = (uint)MASK_OUT_ABOVE_32(-(sint)quotient); + } + if(divisor_neg) + quotient = (uint)MASK_OUT_ABOVE_32(-(sint)quotient); + } + + REG_D[word2 & 7] = remainder; + REG_D[(word2 >> 12) & 7] = quotient; + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + + /* long / long: long quotient, maybe long remainder */ + if(BIT_B(word2)) /* signed */ + { + /* Special case in divide */ + if(dividend_lo == 0x80000000 && divisor == 0xffffffff) + { + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + REG_D[(word2 >> 12) & 7] = 0x80000000; + REG_D[word2 & 7] = 0; + return; + } + REG_D[word2 & 7] = MAKE_INT_32(dividend_lo) % MAKE_INT_32(divisor); + quotient = REG_D[(word2 >> 12) & 7] = MAKE_INT_32(dividend_lo) / MAKE_INT_32(divisor); + } + else + { + REG_D[word2 & 7] = MASK_OUT_ABOVE_32(dividend_lo) % MASK_OUT_ABOVE_32(divisor); + quotient = REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(dividend_lo) / MASK_OUT_ABOVE_32(divisor); + } + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); + return; + } + m68ki_exception_illegal(); + +#endif +} + + +M68KMAKE_OP(divl, 32, ., .) +{ +#if M68K_USE_64_BIT + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint64 divisor = M68KMAKE_GET_OPER_AY_32; + uint64 dividend = 0; + uint64 quotient = 0; + uint64 remainder = 0; + + if(divisor != 0) + { + if(BIT_A(word2)) /* 64 bit */ + { + dividend = REG_D[word2 & 7]; + dividend <<= 32; + dividend |= REG_D[(word2 >> 12) & 7]; + + if(BIT_B(word2)) /* signed */ + { + quotient = (uint64)((sint64)dividend / (sint64)((sint32)divisor)); + remainder = (uint64)((sint64)dividend % (sint64)((sint32)divisor)); + if((sint64)quotient != (sint64)((sint32)quotient)) + { + FLAG_V = VFLAG_SET; + return; + } + } + else /* unsigned */ + { + quotient = dividend / divisor; + if(quotient > 0xffffffff) + { + FLAG_V = VFLAG_SET; + return; + } + remainder = dividend % divisor; + } + } + else /* 32 bit */ + { + dividend = REG_D[(word2 >> 12) & 7]; + if(BIT_B(word2)) /* signed */ + { + quotient = (uint64)((sint64)((sint32)dividend) / (sint64)((sint32)divisor)); + remainder = (uint64)((sint64)((sint32)dividend) % (sint64)((sint32)divisor)); + } + else /* unsigned */ + { + quotient = dividend / divisor; + remainder = dividend % divisor; + } + } + + REG_D[word2 & 7] = remainder; + REG_D[(word2 >> 12) & 7] = quotient; + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); + return; + } + m68ki_exception_illegal(); + +#else + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint divisor = M68KMAKE_GET_OPER_AY_32; + uint dividend_hi = REG_D[word2 & 7]; + uint dividend_lo = REG_D[(word2 >> 12) & 7]; + uint quotient = 0; + uint remainder = 0; + uint dividend_neg = 0; + uint divisor_neg = 0; + sint i; + uint overflow; + + if(divisor != 0) + { + /* quad / long : long quotient, long remainder */ + if(BIT_A(word2)) + { + if(BIT_B(word2)) /* signed */ + { + /* special case in signed divide */ + if(dividend_hi == 0 && dividend_lo == 0x80000000 && divisor == 0xffffffff) + { + REG_D[word2 & 7] = 0; + REG_D[(word2 >> 12) & 7] = 0x80000000; + + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + if(GET_MSB_32(dividend_hi)) + { + dividend_neg = 1; + dividend_hi = (uint)MASK_OUT_ABOVE_32((-(sint)dividend_hi) - (dividend_lo != 0)); + dividend_lo = (uint)MASK_OUT_ABOVE_32(-(sint)dividend_lo); + } + if(GET_MSB_32(divisor)) + { + divisor_neg = 1; + divisor = (uint)MASK_OUT_ABOVE_32(-(sint)divisor); + + } + } + + /* if the upper long is greater than the divisor, we're overflowing. */ + if(dividend_hi >= divisor) + { + FLAG_V = VFLAG_SET; + return; + } + + for(i = 31; i >= 0; i--) + { + quotient <<= 1; + remainder = (remainder << 1) + ((dividend_hi >> i) & 1); + if(remainder >= divisor) + { + remainder -= divisor; + quotient++; + } + } + for(i = 31; i >= 0; i--) + { + quotient <<= 1; + overflow = GET_MSB_32(remainder); + remainder = (remainder << 1) + ((dividend_lo >> i) & 1); + if(remainder >= divisor || overflow) + { + remainder -= divisor; + quotient++; + } + } + + if(BIT_B(word2)) /* signed */ + { + if(quotient > 0x7fffffff) + { + FLAG_V = VFLAG_SET; + return; + } + if(dividend_neg) + { + remainder = (uint)MASK_OUT_ABOVE_32(-(sint)remainder); + quotient = (uint)MASK_OUT_ABOVE_32(-(sint)quotient); + } + if(divisor_neg) + quotient = (uint)MASK_OUT_ABOVE_32(-(sint)quotient); + } + + REG_D[word2 & 7] = remainder; + REG_D[(word2 >> 12) & 7] = quotient; + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + + /* long / long: long quotient, maybe long remainder */ + if(BIT_B(word2)) /* signed */ + { + /* Special case in divide */ + if(dividend_lo == 0x80000000 && divisor == 0xffffffff) + { + FLAG_N = NFLAG_SET; + FLAG_Z = ZFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + REG_D[(word2 >> 12) & 7] = 0x80000000; + REG_D[word2 & 7] = 0; + return; + } + REG_D[word2 & 7] = MAKE_INT_32(dividend_lo) % MAKE_INT_32(divisor); + quotient = REG_D[(word2 >> 12) & 7] = MAKE_INT_32(dividend_lo) / MAKE_INT_32(divisor); + } + else + { + REG_D[word2 & 7] = MASK_OUT_ABOVE_32(dividend_lo) % MASK_OUT_ABOVE_32(divisor); + quotient = REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(dividend_lo) / MASK_OUT_ABOVE_32(divisor); + } + + FLAG_N = NFLAG_32(quotient); + FLAG_Z = quotient; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_trap(EXCEPTION_ZERO_DIVIDE); + return; + } + m68ki_exception_illegal(); + +#endif +} + + +M68KMAKE_OP(eor, 8, ., d) +{ + uint res = MASK_OUT_ABOVE_8(DY ^= MASK_OUT_ABOVE_8(DX)); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eor, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = MASK_OUT_ABOVE_8(DX ^ m68ki_read_8(ea)); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eor, 16, ., d) +{ + uint res = MASK_OUT_ABOVE_16(DY ^= MASK_OUT_ABOVE_16(DX)); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eor, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = MASK_OUT_ABOVE_16(DX ^ m68ki_read_16(ea)); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eor, 32, ., d) +{ + uint res = DY ^= DX; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eor, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = DX ^ m68ki_read_32(ea); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 8, ., d) +{ + uint res = MASK_OUT_ABOVE_8(DY ^= OPER_I_8()); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 8, ., .) +{ + uint src = OPER_I_8(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = src ^ m68ki_read_8(ea); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 16, ., d) +{ + uint res = MASK_OUT_ABOVE_16(DY ^= OPER_I_16()); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 16, ., .) +{ + uint src = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = src ^ m68ki_read_16(ea); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 32, ., d) +{ + uint res = DY ^= OPER_I_32(); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 32, ., .) +{ + uint src = OPER_I_32(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = src ^ m68ki_read_32(ea); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(eori, 16, toc, .) +{ + m68ki_set_ccr(m68ki_get_ccr() ^ OPER_I_8()); +} + + +M68KMAKE_OP(eori, 16, tos, .) +{ + if(FLAG_S) + { + uint src = OPER_I_16(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_set_sr(m68ki_get_sr() ^ src); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(exg, 32, dd, .) +{ + uint* reg_a = &DX; + uint* reg_b = &DY; + uint tmp = *reg_a; + *reg_a = *reg_b; + *reg_b = tmp; +} + + +M68KMAKE_OP(exg, 32, aa, .) +{ + uint* reg_a = &AX; + uint* reg_b = &AY; + uint tmp = *reg_a; + *reg_a = *reg_b; + *reg_b = tmp; +} + + +M68KMAKE_OP(exg, 32, da, .) +{ + uint* reg_a = &DX; + uint* reg_b = &AY; + uint tmp = *reg_a; + *reg_a = *reg_b; + *reg_b = tmp; +} + + +M68KMAKE_OP(ext, 16, ., .) +{ + uint* r_dst = &DY; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | MASK_OUT_ABOVE_8(*r_dst) | (GET_MSB_8(*r_dst) ? 0xff00 : 0); + + FLAG_N = NFLAG_16(*r_dst); + FLAG_Z = MASK_OUT_ABOVE_16(*r_dst); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(ext, 32, ., .) +{ + uint* r_dst = &DY; + + *r_dst = MASK_OUT_ABOVE_16(*r_dst) | (GET_MSB_16(*r_dst) ? 0xffff0000 : 0); + + FLAG_N = NFLAG_32(*r_dst); + FLAG_Z = *r_dst; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(extb, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint* r_dst = &DY; + + *r_dst = MASK_OUT_ABOVE_8(*r_dst) | (GET_MSB_8(*r_dst) ? 0xffffff00 : 0); + + FLAG_N = NFLAG_32(*r_dst); + FLAG_Z = *r_dst; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(illegal, 0, ., .) +{ + m68ki_exception_illegal(); +} + +M68KMAKE_OP(jmp, 32, ., .) +{ + m68ki_jump(M68KMAKE_GET_EA_AY_32); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + if(REG_PC == REG_PPC) + USE_ALL_CYCLES(); +} + + +M68KMAKE_OP(jsr, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_push_32(REG_PC); + m68ki_jump(ea); +} + + +M68KMAKE_OP(lea, 32, ., .) +{ + AX = M68KMAKE_GET_EA_AY_32; +} + + +M68KMAKE_OP(link, 16, ., a7) +{ + REG_A[7] -= 4; + m68ki_write_32(REG_A[7], REG_A[7]); + REG_A[7] = MASK_OUT_ABOVE_32(REG_A[7] + MAKE_INT_16(OPER_I_16())); +} + + +M68KMAKE_OP(link, 16, ., .) +{ + uint* r_dst = &AY; + + m68ki_push_32(*r_dst); + *r_dst = REG_A[7]; + REG_A[7] = MASK_OUT_ABOVE_32(REG_A[7] + MAKE_INT_16(OPER_I_16())); +} + + +M68KMAKE_OP(link, 32, ., a7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_A[7] -= 4; + m68ki_write_32(REG_A[7], REG_A[7]); + REG_A[7] = MASK_OUT_ABOVE_32(REG_A[7] + OPER_I_32()); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(link, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint* r_dst = &AY; + + m68ki_push_32(*r_dst); + *r_dst = REG_A[7]; + REG_A[7] = MASK_OUT_ABOVE_32(REG_A[7] + OPER_I_32()); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(lsr, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = src >> shift; + + if(shift != 0) + USE_CYCLES(shift<> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> (shift - 1))<<8; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst &= 0xffff0000; + FLAG_X = XFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsr, 32, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = *r_dst; + uint res = src >> shift; + + if(shift != 0) + { + USE_CYCLES(shift<> (shift - 1))<<8; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst = 0; + FLAG_X = FLAG_C = (shift == 32 ? GET_MSB_32(src)>>23 : 0); + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsr, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = src >> 1; + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_CLEAR; + FLAG_Z = res; + FLAG_C = FLAG_X = src << 8; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsl, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = MASK_OUT_ABOVE_8(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = MASK_OUT_ABOVE_16(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> (8-shift); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsl, 32, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32(src << shift); + + if(shift != 0) + USE_CYCLES(shift<> (24-shift); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsl, 8, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = MASK_OUT_ABOVE_8(src << shift); + + if(shift != 0) + { + USE_CYCLES(shift<> 8; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst &= 0xffff0000; + FLAG_X = XFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsl, 32, r, .) +{ + uint* r_dst = &DY; + uint shift = DX & 0x3f; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32(src << shift); + + if(shift != 0) + { + USE_CYCLES(shift<> (32 - shift)) << 8; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + *r_dst = 0; + FLAG_X = FLAG_C = ((shift == 32 ? src & 1 : 0))<<8; + FLAG_N = NFLAG_CLEAR; + FLAG_Z = ZFLAG_SET; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(lsl, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = MASK_OUT_ABOVE_16(src << 1); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_X = FLAG_C = src >> 7; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, d, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, d, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, ai, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AX_AI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, ai, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AX_AI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pi7, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_A7_PI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pi, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AX_PI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pi7, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_A7_PI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pi, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AX_PI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pd7, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_A7_PD_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pd, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AX_PD_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pd7, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_A7_PD_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, pd, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AX_PD_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, di, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AX_DI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, di, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AX_DI_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, ix, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AX_IX_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, ix, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AX_IX_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, aw, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AW_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, aw, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AW_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, al, d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + uint ea = EA_AL_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 8, al, .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + uint ea = EA_AL_8(); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, d, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, d, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, d, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ai, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AX_AI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ai, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AX_AI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ai, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AX_AI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pi, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AX_PI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pi, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AX_PI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pi, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AX_PI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pd, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AX_PD_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pd, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AX_PD_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, pd, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AX_PD_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, di, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AX_DI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, di, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AX_DI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, di, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AX_DI_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ix, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AX_IX_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ix, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AX_IX_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, ix, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AX_IX_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, aw, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AW_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, aw, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AW_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, aw, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AW_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, al, d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + uint ea = EA_AL_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, al, a) +{ + uint res = MASK_OUT_ABOVE_16(AY); + uint ea = EA_AL_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 16, al, .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + uint ea = EA_AL_16(); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, d, d) +{ + uint res = DY; + uint* r_dst = &DX; + + *r_dst = res; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, d, a) +{ + uint res = AY; + uint* r_dst = &DX; + + *r_dst = res; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, d, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint* r_dst = &DX; + + *r_dst = res; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ai, d) +{ + uint res = DY; + uint ea = EA_AX_AI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ai, a) +{ + uint res = AY; + uint ea = EA_AX_AI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ai, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AX_AI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pi, d) +{ + uint res = DY; + uint ea = EA_AX_PI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pi, a) +{ + uint res = AY; + uint ea = EA_AX_PI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pi, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AX_PI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pd, d) +{ + uint res = DY; + uint ea = EA_AX_PD_32(); + + m68ki_write_16(ea+2, res & 0xFFFF ); + m68ki_write_16(ea, (res >> 16) & 0xFFFF ); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pd, a) +{ + uint res = AY; + uint ea = EA_AX_PD_32(); + + m68ki_write_16(ea+2, res & 0xFFFF ); + m68ki_write_16(ea, (res >> 16) & 0xFFFF ); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, pd, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AX_PD_32(); + + m68ki_write_16(ea+2, res & 0xFFFF ); + m68ki_write_16(ea, (res >> 16) & 0xFFFF ); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, di, d) +{ + uint res = DY; + uint ea = EA_AX_DI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, di, a) +{ + uint res = AY; + uint ea = EA_AX_DI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, di, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AX_DI_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ix, d) +{ + uint res = DY; + uint ea = EA_AX_IX_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ix, a) +{ + uint res = AY; + uint ea = EA_AX_IX_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, ix, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AX_IX_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, aw, d) +{ + uint res = DY; + uint ea = EA_AW_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, aw, a) +{ + uint res = AY; + uint ea = EA_AW_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, aw, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AW_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, al, d) +{ + uint res = DY; + uint ea = EA_AL_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, al, a) +{ + uint res = AY; + uint ea = EA_AL_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(move, 32, al, .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + uint ea = EA_AL_32(); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(movea, 16, ., d) +{ + AX = MAKE_INT_16(DY); +} + + +M68KMAKE_OP(movea, 16, ., a) +{ + AX = MAKE_INT_16(AY); +} + + +M68KMAKE_OP(movea, 16, ., .) +{ + AX = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); +} + + +M68KMAKE_OP(movea, 32, ., d) +{ + AX = DY; +} + + +M68KMAKE_OP(movea, 32, ., a) +{ + AX = AY; +} + + +M68KMAKE_OP(movea, 32, ., .) +{ + AX = M68KMAKE_GET_OPER_AY_32; +} + + +M68KMAKE_OP(move, 16, frc, d) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + DY = MASK_OUT_BELOW_16(DY) | m68ki_get_ccr(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(move, 16, frc, .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + m68ki_write_16(M68KMAKE_GET_EA_AY_16, m68ki_get_ccr()); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(move, 16, toc, d) +{ + m68ki_set_ccr(DY); +} + + +M68KMAKE_OP(move, 16, toc, .) +{ + m68ki_set_ccr(M68KMAKE_GET_OPER_AY_16); +} + + +M68KMAKE_OP(move, 16, frs, d) +{ + if(CPU_TYPE_IS_000(CPU_TYPE) || FLAG_S) /* NS990408 */ + { + DY = MASK_OUT_BELOW_16(DY) | m68ki_get_sr(); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(move, 16, frs, .) +{ + if(CPU_TYPE_IS_000(CPU_TYPE) || FLAG_S) /* NS990408 */ + { + uint ea = M68KMAKE_GET_EA_AY_16; + m68ki_write_16(ea, m68ki_get_sr()); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(move, 16, tos, d) +{ + if(FLAG_S) + { + m68ki_set_sr(DY); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(move, 16, tos, .) +{ + if(FLAG_S) + { + uint new_sr = M68KMAKE_GET_OPER_AY_16; + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_set_sr(new_sr); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(move, 32, fru, .) +{ + if(FLAG_S) + { + AY = REG_USP; + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(move, 32, tou, .) +{ + if(FLAG_S) + { + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + REG_USP = AY; + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(movec, 32, cr, .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + if(FLAG_S) + { + uint word2 = OPER_I_16(); + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + switch (word2 & 0xfff) + { + case 0x000: /* SFC */ + REG_DA[(word2 >> 12) & 15] = REG_SFC; + return; + case 0x001: /* DFC */ + REG_DA[(word2 >> 12) & 15] = REG_DFC; + return; + case 0x002: /* CACR */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_DA[(word2 >> 12) & 15] = REG_CACR; + return; + } + return; + case 0x800: /* USP */ + REG_DA[(word2 >> 12) & 15] = REG_USP; + return; + case 0x801: /* VBR */ + REG_DA[(word2 >> 12) & 15] = REG_VBR; + return; + case 0x802: /* CAAR */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_DA[(word2 >> 12) & 15] = REG_CAAR; + return; + } + m68ki_exception_illegal(); + break; + case 0x803: /* MSP */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_DA[(word2 >> 12) & 15] = FLAG_M ? REG_SP : REG_MSP; + return; + } + m68ki_exception_illegal(); + return; + case 0x804: /* ISP */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_DA[(word2 >> 12) & 15] = FLAG_M ? REG_ISP : REG_SP; + return; + } + m68ki_exception_illegal(); + return; + default: + m68ki_exception_illegal(); + return; + } + } + m68ki_exception_privilege_violation(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(movec, 32, rc, .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + if(FLAG_S) + { + uint word2 = OPER_I_16(); + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + switch (word2 & 0xfff) + { + case 0x000: /* SFC */ + REG_SFC = REG_DA[(word2 >> 12) & 15] & 7; + return; + case 0x001: /* DFC */ + REG_DFC = REG_DA[(word2 >> 12) & 15] & 7; + return; + case 0x002: /* CACR */ + /* Only EC020 and later have CACR */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_CACR = REG_DA[(word2 >> 12) & 15]; + return; + } + m68ki_exception_illegal(); + return; + case 0x800: /* USP */ + REG_USP = REG_DA[(word2 >> 12) & 15]; + return; + case 0x801: /* VBR */ + REG_VBR = REG_DA[(word2 >> 12) & 15]; + return; + case 0x802: /* CAAR */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_CAAR = REG_DA[(word2 >> 12) & 15]; + return; + } + m68ki_exception_illegal(); + return; + case 0x803: /* MSP */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* we are in supervisor mode so just check for M flag */ + if(!FLAG_M) + { + REG_MSP = REG_DA[(word2 >> 12) & 15]; + return; + } + REG_SP = REG_DA[(word2 >> 12) & 15]; + return; + } + m68ki_exception_illegal(); + return; + case 0x804: /* ISP */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + if(!FLAG_M) + { + REG_SP = REG_DA[(word2 >> 12) & 15]; + return; + } + REG_ISP = REG_DA[(word2 >> 12) & 15]; + return; + } + m68ki_exception_illegal(); + return; + default: + m68ki_exception_illegal(); + return; + } + } + m68ki_exception_privilege_violation(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(movem, 16, re, pd) +{ + uint i = 0; + uint register_list = OPER_I_16(); + uint ea = AY; + uint count = 0; + + for(; i < 16; i++) + if(register_list & (1 << i)) + { + ea -= 2; + m68ki_write_16(ea, MASK_OUT_ABOVE_16(REG_DA[15-i])); + count++; + } + AY = ea; + + USE_CYCLES(count<> 16) & 0xFFFF ); + count++; + } + AY = ea; + + USE_CYCLES(count<> 8)); + m68ki_write_8(ea += 2, MASK_OUT_ABOVE_8(src)); +} + + +M68KMAKE_OP(movep, 32, re, .) +{ + uint ea = EA_AY_DI_32(); + uint src = DX; + + m68ki_write_8(ea, MASK_OUT_ABOVE_8(src >> 24)); + m68ki_write_8(ea += 2, MASK_OUT_ABOVE_8(src >> 16)); + m68ki_write_8(ea += 2, MASK_OUT_ABOVE_8(src >> 8)); + m68ki_write_8(ea += 2, MASK_OUT_ABOVE_8(src)); +} + + +M68KMAKE_OP(movep, 16, er, .) +{ + uint ea = EA_AY_DI_16(); + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | ((m68ki_read_8(ea) << 8) + m68ki_read_8(ea + 2)); +} + + +M68KMAKE_OP(movep, 32, er, .) +{ + uint ea = EA_AY_DI_32(); + + DX = (m68ki_read_8(ea) << 24) + (m68ki_read_8(ea + 2) << 16) + + (m68ki_read_8(ea + 4) << 8) + m68ki_read_8(ea + 6); +} + + +M68KMAKE_OP(moves, 8, ., .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + if(FLAG_S) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_8; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + if(BIT_B(word2)) /* Register to memory */ + { + m68ki_write_8_fc(ea, REG_DFC, MASK_OUT_ABOVE_8(REG_DA[(word2 >> 12) & 15])); + return; + } + if(BIT_F(word2)) /* Memory to address register */ + { + REG_A[(word2 >> 12) & 7] = MAKE_INT_8(m68ki_read_8_fc(ea, REG_SFC)); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + /* Memory to data register */ + REG_D[(word2 >> 12) & 7] = MASK_OUT_BELOW_8(REG_D[(word2 >> 12) & 7]) | m68ki_read_8_fc(ea, REG_SFC); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + m68ki_exception_privilege_violation(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(moves, 16, ., .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + if(FLAG_S) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + if(BIT_B(word2)) /* Register to memory */ + { + m68ki_write_16_fc(ea, REG_DFC, MASK_OUT_ABOVE_16(REG_DA[(word2 >> 12) & 15])); + return; + } + if(BIT_F(word2)) /* Memory to address register */ + { + REG_A[(word2 >> 12) & 7] = MAKE_INT_16(m68ki_read_16_fc(ea, REG_SFC)); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + /* Memory to data register */ + REG_D[(word2 >> 12) & 7] = MASK_OUT_BELOW_16(REG_D[(word2 >> 12) & 7]) | m68ki_read_16_fc(ea, REG_SFC); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + m68ki_exception_privilege_violation(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(moves, 32, ., .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + if(FLAG_S) + { + uint word2 = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_32; + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + if(BIT_B(word2)) /* Register to memory */ + { + m68ki_write_32_fc(ea, REG_DFC, REG_DA[(word2 >> 12) & 15]); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + /* Memory to register */ + REG_DA[(word2 >> 12) & 15] = m68ki_read_32_fc(ea, REG_SFC); + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + USE_CYCLES(2); + return; + } + m68ki_exception_privilege_violation(); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(moveq, 32, ., .) +{ + uint res = DX = MAKE_INT_8(MASK_OUT_ABOVE_8(REG_IR)); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(muls, 16, ., d) +{ + uint* r_dst = &DX; + uint res = MASK_OUT_ABOVE_32(MAKE_INT_16(DY) * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); + + *r_dst = res; + + FLAG_Z = res; + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(muls, 16, ., .) +{ + uint* r_dst = &DX; + uint res = MASK_OUT_ABOVE_32(MAKE_INT_16(M68KMAKE_GET_OPER_AY_16) * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); + + *r_dst = res; + + FLAG_Z = res; + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(mulu, 16, ., d) +{ + uint* r_dst = &DX; + uint res = MASK_OUT_ABOVE_16(DY) * MASK_OUT_ABOVE_16(*r_dst); + + *r_dst = res; + + FLAG_Z = res; + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(mulu, 16, ., .) +{ + uint* r_dst = &DX; + uint res = M68KMAKE_GET_OPER_AY_16 * MASK_OUT_ABOVE_16(*r_dst); + + *r_dst = res; + + FLAG_Z = res; + FLAG_N = NFLAG_32(res); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(mull, 32, ., d) +{ +#if M68K_USE_64_BIT + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint64 src = DY; + uint64 dst = REG_D[(word2 >> 12) & 7]; + uint64 res; + + FLAG_C = CFLAG_CLEAR; + + if(BIT_B(word2)) /* signed */ + { + res = (sint64)((sint32)src) * (sint64)((sint32)dst); + if(!BIT_A(word2)) + { + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_N = NFLAG_32(res); + FLAG_V = ((sint64)res != (sint32)res)<<7; + REG_D[(word2 >> 12) & 7] = FLAG_Z; + return; + } + FLAG_Z = MASK_OUT_ABOVE_32(res) | (res>>32); + FLAG_N = NFLAG_64(res); + FLAG_V = VFLAG_CLEAR; + REG_D[word2 & 7] = (res >> 32); + REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(res); + return; + } + + res = src * dst; + if(!BIT_A(word2)) + { + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_N = NFLAG_32(res); + FLAG_V = (res > 0xffffffff)<<7; + REG_D[(word2 >> 12) & 7] = FLAG_Z; + return; + } + FLAG_Z = MASK_OUT_ABOVE_32(res) | (res>>32); + FLAG_N = NFLAG_64(res); + FLAG_V = VFLAG_CLEAR; + REG_D[word2 & 7] = (res >> 32); + REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(res); + return; + } + m68ki_exception_illegal(); + +#else + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint src = DY; + uint dst = REG_D[(word2 >> 12) & 7]; + uint neg = GET_MSB_32(src ^ dst); + uint src1; + uint src2; + uint dst1; + uint dst2; + uint r1; + uint r2; + uint r3; + uint r4; + uint lo; + uint hi; + + FLAG_C = CFLAG_CLEAR; + + if(BIT_B(word2)) /* signed */ + { + if(GET_MSB_32(src)) + src = (uint)MASK_OUT_ABOVE_32(-(sint)src); + if(GET_MSB_32(dst)) + dst = (uint)MASK_OUT_ABOVE_32(-(sint)dst); + } + + src1 = MASK_OUT_ABOVE_16(src); + src2 = src>>16; + dst1 = MASK_OUT_ABOVE_16(dst); + dst2 = dst>>16; + + + r1 = src1 * dst1; + r2 = src1 * dst2; + r3 = src2 * dst1; + r4 = src2 * dst2; + + lo = r1 + (MASK_OUT_ABOVE_16(r2)<<16) + (MASK_OUT_ABOVE_16(r3)<<16); + hi = r4 + (r2>>16) + (r3>>16) + (((r1>>16) + MASK_OUT_ABOVE_16(r2) + MASK_OUT_ABOVE_16(r3)) >> 16); + + if(BIT_B(word2) && neg) + { + hi = (uint)MASK_OUT_ABOVE_32((-(sint)hi) - (lo != 0)); + lo = (uint)MASK_OUT_ABOVE_32(-(sint)lo); + } + + if(BIT_A(word2)) + { + REG_D[word2 & 7] = hi; + REG_D[(word2 >> 12) & 7] = lo; + FLAG_N = NFLAG_32(hi); + FLAG_Z = hi | lo; + FLAG_V = VFLAG_CLEAR; + return; + } + + REG_D[(word2 >> 12) & 7] = lo; + FLAG_N = NFLAG_32(lo); + FLAG_Z = lo; + if(BIT_B(word2)) + FLAG_V = (!((GET_MSB_32(lo) && hi == 0xffffffff) || (!GET_MSB_32(lo) && !hi)))<<7; + else + FLAG_V = (hi != 0) << 7; + return; + } + m68ki_exception_illegal(); + +#endif +} + + +M68KMAKE_OP(mull, 32, ., .) +{ +#if M68K_USE_64_BIT + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint64 src = M68KMAKE_GET_OPER_AY_32; + uint64 dst = REG_D[(word2 >> 12) & 7]; + uint64 res; + + FLAG_C = CFLAG_CLEAR; + + if(BIT_B(word2)) /* signed */ + { + res = (sint64)((sint32)src) * (sint64)((sint32)dst); + if(!BIT_A(word2)) + { + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_N = NFLAG_32(res); + FLAG_V = ((sint64)res != (sint32)res)<<7; + REG_D[(word2 >> 12) & 7] = FLAG_Z; + return; + } + FLAG_Z = MASK_OUT_ABOVE_32(res) | (res>>32); + FLAG_N = NFLAG_64(res); + FLAG_V = VFLAG_CLEAR; + REG_D[word2 & 7] = (res >> 32); + REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(res); + return; + } + + res = src * dst; + if(!BIT_A(word2)) + { + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_N = NFLAG_32(res); + FLAG_V = (res > 0xffffffff)<<7; + REG_D[(word2 >> 12) & 7] = FLAG_Z; + return; + } + FLAG_Z = MASK_OUT_ABOVE_32(res) | (res>>32); + FLAG_N = NFLAG_64(res); + FLAG_V = VFLAG_CLEAR; + REG_D[word2 & 7] = (res >> 32); + REG_D[(word2 >> 12) & 7] = MASK_OUT_ABOVE_32(res); + return; + } + m68ki_exception_illegal(); + +#else + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint word2 = OPER_I_16(); + uint src = M68KMAKE_GET_OPER_AY_32; + uint dst = REG_D[(word2 >> 12) & 7]; + uint neg = GET_MSB_32(src ^ dst); + uint src1; + uint src2; + uint dst1; + uint dst2; + uint r1; + uint r2; + uint r3; + uint r4; + uint lo; + uint hi; + + FLAG_C = CFLAG_CLEAR; + + if(BIT_B(word2)) /* signed */ + { + if(GET_MSB_32(src)) + src = (uint)MASK_OUT_ABOVE_32(-(sint)src); + if(GET_MSB_32(dst)) + dst = (uint)MASK_OUT_ABOVE_32(-(sint)dst); + } + + src1 = MASK_OUT_ABOVE_16(src); + src2 = src>>16; + dst1 = MASK_OUT_ABOVE_16(dst); + dst2 = dst>>16; + + + r1 = src1 * dst1; + r2 = src1 * dst2; + r3 = src2 * dst1; + r4 = src2 * dst2; + + lo = r1 + (MASK_OUT_ABOVE_16(r2)<<16) + (MASK_OUT_ABOVE_16(r3)<<16); + hi = r4 + (r2>>16) + (r3>>16) + (((r1>>16) + MASK_OUT_ABOVE_16(r2) + MASK_OUT_ABOVE_16(r3)) >> 16); + + if(BIT_B(word2) && neg) + { + hi = (uint)MASK_OUT_ABOVE_32((-(sint)hi) - (lo != 0)); + lo = (uint)MASK_OUT_ABOVE_32(-(sint)lo); + } + + if(BIT_A(word2)) + { + REG_D[word2 & 7] = hi; + REG_D[(word2 >> 12) & 7] = lo; + FLAG_N = NFLAG_32(hi); + FLAG_Z = hi | lo; + FLAG_V = VFLAG_CLEAR; + return; + } + + REG_D[(word2 >> 12) & 7] = lo; + FLAG_N = NFLAG_32(lo); + FLAG_Z = lo; + if(BIT_B(word2)) + FLAG_V = (!((GET_MSB_32(lo) && hi == 0xffffffff) || (!GET_MSB_32(lo) && !hi)))<<7; + else + FLAG_V = (hi != 0) << 7; + return; + } + m68ki_exception_illegal(); + +#endif +} + + +M68KMAKE_OP(nbcd, 8, ., d) +{ + uint* r_dst = &DY; + uint dst = *r_dst; + uint res = MASK_OUT_ABOVE_8(0x9a - dst - XFLAG_AS_1()); + + if(res != 0x9a) + { + FLAG_V = ~res; /* Undefined V behavior */ + + if((res & 0x0f) == 0xa) + res = (res & 0xf0) + 0x10; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; + + FLAG_Z |= res; + FLAG_C = CFLAG_SET; + FLAG_X = XFLAG_SET; + } + else + { + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_X = XFLAG_CLEAR; + } + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ +} + + +M68KMAKE_OP(nbcd, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + uint res = MASK_OUT_ABOVE_8(0x9a - dst - XFLAG_AS_1()); + + if(res != 0x9a) + { + FLAG_V = ~res; /* Undefined V behavior */ + + if((res & 0x0f) == 0xa) + res = (res & 0xf0) + 0x10; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + + m68ki_write_8(ea, MASK_OUT_ABOVE_8(res)); + + FLAG_Z |= res; + FLAG_C = CFLAG_SET; + FLAG_X = XFLAG_SET; + } + else + { + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + FLAG_X = XFLAG_CLEAR; + } + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ +} + + +M68KMAKE_OP(neg, 8, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - MASK_OUT_ABOVE_8(*r_dst); + + FLAG_N = NFLAG_8(res); + FLAG_C = FLAG_X = CFLAG_8(res); + FLAG_V = *r_dst & res; + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(neg, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + uint res = 0 - src; + + FLAG_N = NFLAG_8(res); + FLAG_C = FLAG_X = CFLAG_8(res); + FLAG_V = src & res; + FLAG_Z = MASK_OUT_ABOVE_8(res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(neg, 16, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - MASK_OUT_ABOVE_16(*r_dst); + + FLAG_N = NFLAG_16(res); + FLAG_C = FLAG_X = CFLAG_16(res); + FLAG_V = (*r_dst & res)>>8; + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(neg, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = 0 - src; + + FLAG_N = NFLAG_16(res); + FLAG_C = FLAG_X = CFLAG_16(res); + FLAG_V = (src & res)>>8; + FLAG_Z = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(neg, 32, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - *r_dst; + + FLAG_N = NFLAG_32(res); + FLAG_C = FLAG_X = CFLAG_SUB_32(*r_dst, 0, res); + FLAG_V = (*r_dst & res)>>24; + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(neg, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint src = m68ki_read_32(ea); + uint res = 0 - src; + + FLAG_N = NFLAG_32(res); + FLAG_C = FLAG_X = CFLAG_SUB_32(src, 0, res); + FLAG_V = (src & res)>>24; + FLAG_Z = MASK_OUT_ABOVE_32(res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(negx, 8, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - MASK_OUT_ABOVE_8(*r_dst) - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = *r_dst & res; + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; +} + + +M68KMAKE_OP(negx, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = m68ki_read_8(ea); + uint res = 0 - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = src & res; + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(negx, 16, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - MASK_OUT_ABOVE_16(*r_dst) - XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = (*r_dst & res)>>8; + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; +} + + +M68KMAKE_OP(negx, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = 0 - MASK_OUT_ABOVE_16(src) - XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = (src & res)>>8; + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + m68ki_write_16(ea, res); +} + + +M68KMAKE_OP(negx, 32, ., d) +{ + uint* r_dst = &DY; + uint res = 0 - MASK_OUT_ABOVE_32(*r_dst) - XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(*r_dst, 0, res); + FLAG_V = (*r_dst & res)>>24; + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + *r_dst = res; +} + + +M68KMAKE_OP(negx, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint src = m68ki_read_32(ea); + uint res = 0 - MASK_OUT_ABOVE_32(src) - XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, 0, res); + FLAG_V = (src & res)>>24; + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + m68ki_write_32(ea, res); +} + + +M68KMAKE_OP(nop, 0, ., .) +{ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ +} + + +M68KMAKE_OP(not, 8, ., d) +{ + uint* r_dst = &DY; + uint res = MASK_OUT_ABOVE_8(~*r_dst); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(not, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = MASK_OUT_ABOVE_8(~m68ki_read_8(ea)); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(not, 16, ., d) +{ + uint* r_dst = &DY; + uint res = MASK_OUT_ABOVE_16(~*r_dst); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(not, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = MASK_OUT_ABOVE_16(~m68ki_read_16(ea)); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(not, 32, ., d) +{ + uint* r_dst = &DY; + uint res = *r_dst = MASK_OUT_ABOVE_32(~*r_dst); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(not, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = MASK_OUT_ABOVE_32(~m68ki_read_32(ea)); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 8, er, d) +{ + uint res = MASK_OUT_ABOVE_8((DX |= MASK_OUT_ABOVE_8(DY))); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 8, er, .) +{ + uint res = MASK_OUT_ABOVE_8((DX |= M68KMAKE_GET_OPER_AY_8)); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 16, er, d) +{ + uint res = MASK_OUT_ABOVE_16((DX |= MASK_OUT_ABOVE_16(DY))); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 16, er, .) +{ + uint res = MASK_OUT_ABOVE_16((DX |= M68KMAKE_GET_OPER_AY_16)); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 32, er, d) +{ + uint res = DX |= DY; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 32, er, .) +{ + uint res = DX |= M68KMAKE_GET_OPER_AY_32; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 8, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = MASK_OUT_ABOVE_8(DX | m68ki_read_8(ea)); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 16, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = MASK_OUT_ABOVE_16(DX | m68ki_read_16(ea)); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(or, 32, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = DX | m68ki_read_32(ea); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 8, ., d) +{ + uint res = MASK_OUT_ABOVE_8((DY |= OPER_I_8())); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 8, ., .) +{ + uint src = OPER_I_8(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint res = MASK_OUT_ABOVE_8(src | m68ki_read_8(ea)); + + m68ki_write_8(ea, res); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 16, ., d) +{ + uint res = MASK_OUT_ABOVE_16(DY |= OPER_I_16()); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 16, ., .) +{ + uint src = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint res = MASK_OUT_ABOVE_16(src | m68ki_read_16(ea)); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 32, ., d) +{ + uint res = DY |= OPER_I_32(); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 32, ., .) +{ + uint src = OPER_I_32(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint res = src | m68ki_read_32(ea); + + m68ki_write_32(ea, res); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ori, 16, toc, .) +{ + m68ki_set_ccr(m68ki_get_ccr() | OPER_I_8()); +} + + +M68KMAKE_OP(ori, 16, tos, .) +{ + if(FLAG_S) + { + uint src = OPER_I_16(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_set_sr(m68ki_get_sr() | src); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(pack, 16, rr, .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: DX and DY are reversed in Motorola's docs */ + uint src = DY + OPER_I_16(); + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | ((src >> 4) & 0x00f0) | (src & 0x000f); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(pack, 16, mm, ax7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint ea_src = EA_AY_PD_8(); + uint src = m68ki_read_8(ea_src); + ea_src = EA_AY_PD_8(); + src = ((src << 8) | m68ki_read_8(ea_src)) + OPER_I_16(); + + m68ki_write_8(EA_A7_PD_8(), ((src >> 4) & 0x00f0) | (src & 0x000f)); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(pack, 16, mm, ay7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint ea_src = EA_A7_PD_8(); + uint src = m68ki_read_8(ea_src); + ea_src = EA_A7_PD_8(); + src = ((src << 8) | m68ki_read_8(ea_src)) + OPER_I_16(); + + m68ki_write_8(EA_AX_PD_8(), ((src >> 4) & 0x00f0) | (src & 0x000f)); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(pack, 16, mm, axy7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint ea_src = EA_A7_PD_8(); + uint src = m68ki_read_8(ea_src); + ea_src = EA_A7_PD_8(); + src = ((src << 8) | m68ki_read_8(ea_src)) + OPER_I_16(); + + m68ki_write_8(EA_A7_PD_8(), ((src >> 4) & 0x00f0) | (src & 0x000f)); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(pack, 16, mm, .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint ea_src = EA_AY_PD_8(); + uint src = m68ki_read_8(ea_src); + ea_src = EA_AY_PD_8(); + src = ((src << 8) | m68ki_read_8(ea_src)) + OPER_I_16(); + + m68ki_write_8(EA_AX_PD_8(), ((src >> 4) & 0x00f0) | (src & 0x000f)); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(pea, 32, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + + m68ki_push_32(ea); +} + + +M68KMAKE_OP(reset, 0, ., .) +{ + if(FLAG_S) + { + m68ki_output_reset(); /* auto-disable (see m68kcpu.h) */ + USE_CYCLES(CYC_RESET); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(ror, 8, s, .) +{ + uint* r_dst = &DY; + uint orig_shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint shift = orig_shift & 7; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = ROR_8(src, shift); + + if(orig_shift != 0) + USE_CYCLES(orig_shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = ROR_16(src, shift); + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint64 src = *r_dst; + uint res = ROR_32(src, shift); + + if(shift != 0) + USE_CYCLES(shift<> ((shift - 1) & 15)) << 8; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ror, 32, r, .) +{ + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + uint shift = orig_shift & 31; + uint64 src = *r_dst; + uint res = ROR_32(src, shift); + + if(orig_shift != 0) + { + USE_CYCLES(orig_shift<> ((shift - 1) & 31)) << 8; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(ror, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = ROR_16(src, 1); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = src << 8; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rol, 8, s, .) +{ + uint* r_dst = &DY; + uint orig_shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint shift = orig_shift & 7; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = ROL_8(src, shift); + + if(orig_shift != 0) + USE_CYCLES(orig_shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = ROL_16(src, shift); + + if(shift != 0) + USE_CYCLES(shift<> (8-shift); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rol, 32, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint64 src = *r_dst; + uint res = ROL_32(src, shift); + + if(shift != 0) + USE_CYCLES(shift<> (24-shift); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rol, 8, r, .) +{ + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + uint shift = orig_shift & 7; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = ROL_8(src, shift); + + if(orig_shift != 0) + { + USE_CYCLES(orig_shift<> 8; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + FLAG_C = (src & 1)<<8; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_16(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rol, 32, r, .) +{ + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + uint shift = orig_shift & 31; + uint64 src = *r_dst; + uint res = ROL_32(src, shift); + + if(orig_shift != 0) + { + USE_CYCLES(orig_shift<> ((32 - shift) & 0x1f)) << 8; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = CFLAG_CLEAR; + FLAG_N = NFLAG_32(src); + FLAG_Z = src; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rol, 16, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = m68ki_read_16(ea); + uint res = MASK_OUT_ABOVE_16(ROL_16(src, 1)); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_C = src >> 7; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxr, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = ROR_9(src | (XFLAG_AS_1() << 8), shift); + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = ROR_17(src | (XFLAG_AS_1() << 16), shift); + + if(shift != 0) + USE_CYCLES(shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxr, 32, s, .) +{ +#if M68K_USE_64_BIT + + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint64 src = *r_dst; + uint64 res = src | (((uint64)XFLAG_AS_1()) << 32); + + if(shift != 0) + USE_CYCLES(shift<> 24; + res = MASK_OUT_ABOVE_32(res); + + *r_dst = res; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + +#else + + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32((ROR_33(src, shift) & ~(1 << (32 - shift))) | (XFLAG_AS_1() << (32 - shift))); + uint new_x_flag = src & (1 << (shift - 1)); + + if(shift != 0) + USE_CYCLES(shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = FLAG_X; + FLAG_N = NFLAG_16(*r_dst); + FLAG_Z = MASK_OUT_ABOVE_16(*r_dst); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxr, 32, r, .) +{ +#if M68K_USE_64_BIT + + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + + if(orig_shift != 0) + { + uint shift = orig_shift % 33; + uint64 src = *r_dst; + uint64 res = src | (((uint64)XFLAG_AS_1()) << 32); + + res = ROR_33_64(res, shift); + + USE_CYCLES(orig_shift<> 24; + res = MASK_OUT_ABOVE_32(res); + + *r_dst = res; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = FLAG_X; + FLAG_N = NFLAG_32(*r_dst); + FLAG_Z = *r_dst; + FLAG_V = VFLAG_CLEAR; + +#else + + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + uint shift = orig_shift % 33; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32((ROR_33(src, shift) & ~(1 << (32 - shift))) | (XFLAG_AS_1() << (32 - shift))); + uint new_x_flag = src & (1 << (shift - 1)); + + if(orig_shift != 0) + USE_CYCLES(orig_shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxl, 8, s, .) +{ + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_8(*r_dst); + uint res = ROL_9(src | (XFLAG_AS_1() << 8), shift); + + if(shift != 0) + USE_CYCLES(shift<> 9) - 1) & 7) + 1; + uint src = MASK_OUT_ABOVE_16(*r_dst); + uint res = ROL_17(src | (XFLAG_AS_1() << 16), shift); + + if(shift != 0) + USE_CYCLES(shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxl, 32, s, .) +{ +#if M68K_USE_64_BIT + + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint64 src = *r_dst; + uint64 res = src | (((uint64)XFLAG_AS_1()) << 32); + + if(shift != 0) + USE_CYCLES(shift<> 24; + res = MASK_OUT_ABOVE_32(res); + + *r_dst = res; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + +#else + + uint* r_dst = &DY; + uint shift = (((REG_IR >> 9) - 1) & 7) + 1; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32((ROL_33(src, shift) & ~(1 << (shift - 1))) | (XFLAG_AS_1() << (shift - 1))); + uint new_x_flag = src & (1 << (32 - shift)); + + if(shift != 0) + USE_CYCLES(shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = FLAG_X; + FLAG_N = NFLAG_16(*r_dst); + FLAG_Z = MASK_OUT_ABOVE_16(*r_dst); + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(roxl, 32, r, .) +{ +#if M68K_USE_64_BIT + + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + + if(orig_shift != 0) + { + uint shift = orig_shift % 33; + uint64 src = *r_dst; + uint64 res = src | (((uint64)XFLAG_AS_1()) << 32); + + res = ROL_33_64(res, shift); + + USE_CYCLES(orig_shift<> 24; + res = MASK_OUT_ABOVE_32(res); + + *r_dst = res; + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + return; + } + + FLAG_C = FLAG_X; + FLAG_N = NFLAG_32(*r_dst); + FLAG_Z = *r_dst; + FLAG_V = VFLAG_CLEAR; + +#else + + uint* r_dst = &DY; + uint orig_shift = DX & 0x3f; + uint shift = orig_shift % 33; + uint src = *r_dst; + uint res = MASK_OUT_ABOVE_32((ROL_33(src, shift) & ~(1 << (shift - 1))) | (XFLAG_AS_1() << (shift - 1))); + uint new_x_flag = src & (1 << (32 - shift)); + + if(orig_shift != 0) + USE_CYCLES(orig_shift<> 8; + res = MASK_OUT_ABOVE_16(res); + + m68ki_write_16(ea, res); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(rtd, 32, ., .) +{ + if(CPU_TYPE_IS_010_PLUS(CPU_TYPE)) + { + uint new_pc = m68ki_pull_32(); + + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + REG_A[7] = MASK_OUT_ABOVE_32(REG_A[7] + MAKE_INT_16(OPER_I_16())); + m68ki_jump(new_pc); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(rte, 32, ., .) +{ + if(FLAG_S) + { + uint new_sr; + uint new_pc; + uint format_word; + + m68ki_rte_callback(); /* auto-disable (see m68kcpu.h) */ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + + if(CPU_TYPE_IS_000(CPU_TYPE)) + { + new_sr = m68ki_pull_16(); + new_pc = m68ki_pull_32(); + m68ki_jump(new_pc); + m68ki_set_sr(new_sr); + + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + + return; + } + + if(CPU_TYPE_IS_010(CPU_TYPE)) + { + format_word = m68ki_read_16(REG_A[7]+6) >> 12; + if(format_word == 0) + { + new_sr = m68ki_pull_16(); + new_pc = m68ki_pull_32(); + m68ki_fake_pull_16(); /* format word */ + m68ki_jump(new_pc); + m68ki_set_sr(new_sr); + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + return; + } + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + /* Not handling other exception types (9) */ + m68ki_exception_format_error(); + return; + } + + /* Otherwise it's 020 */ +rte_loop: + format_word = m68ki_read_16(REG_A[7]+6) >> 12; + switch(format_word) + { + case 0: /* Normal */ + new_sr = m68ki_pull_16(); + new_pc = m68ki_pull_32(); + m68ki_fake_pull_16(); /* format word */ + m68ki_jump(new_pc); + m68ki_set_sr(new_sr); + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + return; + case 1: /* Throwaway */ + new_sr = m68ki_pull_16(); + m68ki_fake_pull_32(); /* program counter */ + m68ki_fake_pull_16(); /* format word */ + m68ki_set_sr_noint(new_sr); + goto rte_loop; + case 2: /* Trap */ + new_sr = m68ki_pull_16(); + new_pc = m68ki_pull_32(); + m68ki_fake_pull_16(); /* format word */ + m68ki_fake_pull_32(); /* address */ + m68ki_jump(new_pc); + m68ki_set_sr(new_sr); + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + return; + } + /* Not handling long or short bus fault */ + CPU_INSTR_MODE = INSTRUCTION_YES; + CPU_RUN_MODE = RUN_MODE_NORMAL; + m68ki_exception_format_error(); + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(rtm, 32, ., .) +{ + if(CPU_TYPE_IS_020_VARIANT(CPU_TYPE)) + { + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR, + m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2)))); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(rtr, 32, ., .) +{ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_set_ccr(m68ki_pull_16()); + m68ki_jump(m68ki_pull_32()); +} + + +M68KMAKE_OP(rts, 32, ., .) +{ + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + m68ki_jump(m68ki_pull_32()); +} + + +M68KMAKE_OP(sbcd, 8, rr, .) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res -= 6; + res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res += 0xa0; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; +} + + +M68KMAKE_OP(sbcd, 8, mm, ax7) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res -= 6; + res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res += 0xa0; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(sbcd, 8, mm, ay7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res -= 6; + res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res += 0xa0; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(sbcd, 8, mm, axy7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res -= 6; + res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res += 0xa0; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(sbcd, 8, mm, .) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1(); + + FLAG_V = ~res; /* Undefined V behavior */ + + if(res > 9) + res -= 6; + res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src); + FLAG_X = FLAG_C = (res > 0x99) << 8; + if(FLAG_C) + res += 0xa0; + + res = MASK_OUT_ABOVE_8(res); + + FLAG_V &= res; /* Undefined V behavior part II */ + FLAG_N = NFLAG_8(res); /* Undefined N behavior */ + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(st, 8, ., d) +{ + DY |= 0xff; +} + + +M68KMAKE_OP(st, 8, ., .) +{ + m68ki_write_8(M68KMAKE_GET_EA_AY_8, 0xff); +} + + +M68KMAKE_OP(sf, 8, ., d) +{ + DY &= 0xffffff00; +} + + +M68KMAKE_OP(sf, 8, ., .) +{ + m68ki_write_8(M68KMAKE_GET_EA_AY_8, 0); +} + + +M68KMAKE_OP(scc, 8, ., d) +{ + if(M68KMAKE_CC) + { + DY |= 0xff; + USE_CYCLES(CYC_SCC_R_TRUE); + return; + } + DY &= 0xffffff00; +} + + +M68KMAKE_OP(scc, 8, ., .) +{ + m68ki_write_8(M68KMAKE_GET_EA_AY_8, M68KMAKE_CC ? 0xff : 0); +} + + +M68KMAKE_OP(stop, 0, ., .) +{ + if(FLAG_S) + { + uint new_sr = OPER_I_16(); + m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */ + CPU_STOPPED |= STOP_LEVEL_STOP; + m68ki_set_sr(new_sr); + m68ki_remaining_cycles = 0; + return; + } + m68ki_exception_privilege_violation(); +} + + +M68KMAKE_OP(sub, 8, er, d) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_8(DY); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(sub, 8, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_8; + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(sub, 16, er, d) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(DY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(sub, 16, er, a) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(AY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(sub, 16, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_16; + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(sub, 32, er, d) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(sub, 32, er, a) +{ + uint* r_dst = &DX; + uint src = AY; + uint dst = *r_dst; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(sub, 32, er, .) +{ + uint* r_dst = &DX; + uint src = M68KMAKE_GET_OPER_AY_32; + uint dst = *r_dst; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(sub, 8, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint src = MASK_OUT_ABOVE_8(DX); + uint dst = m68ki_read_8(ea); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(sub, 16, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_16; + uint src = MASK_OUT_ABOVE_16(DX); + uint dst = m68ki_read_16(ea); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(sub, 32, re, .) +{ + uint ea = M68KMAKE_GET_EA_AY_32; + uint src = DX; + uint dst = m68ki_read_32(ea); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(suba, 16, ., d) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - MAKE_INT_16(DY)); +} + + +M68KMAKE_OP(suba, 16, ., a) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - MAKE_INT_16(AY)); +} + + +M68KMAKE_OP(suba, 16, ., .) +{ + uint* r_dst = &AX; + uint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - src); +} + + +M68KMAKE_OP(suba, 32, ., d) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - DY); +} + + +M68KMAKE_OP(suba, 32, ., a) +{ + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - AY); +} + + +M68KMAKE_OP(suba, 32, ., .) +{ + uint src = M68KMAKE_GET_OPER_AY_32; + uint* r_dst = &AX; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - src); +} + + +M68KMAKE_OP(subi, 8, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_8(); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(subi, 8, ., .) +{ + uint src = OPER_I_8(); + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(subi, 16, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_16(); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(subi, 16, ., .) +{ + uint src = OPER_I_16(); + uint ea = M68KMAKE_GET_EA_AY_16; + uint dst = m68ki_read_16(ea); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(subi, 32, ., d) +{ + uint* r_dst = &DY; + uint src = OPER_I_32(); + uint dst = *r_dst; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(subi, 32, ., .) +{ + uint src = OPER_I_32(); + uint ea = M68KMAKE_GET_EA_AY_32; + uint dst = m68ki_read_32(ea); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(subq, 8, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(subq, 8, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + uint res = dst - src; + + FLAG_N = NFLAG_8(res); + FLAG_Z = MASK_OUT_ABOVE_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + m68ki_write_8(ea, FLAG_Z); +} + + +M68KMAKE_OP(subq, 16, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z; +} + + +M68KMAKE_OP(subq, 16, ., a) +{ + uint* r_dst = &AY; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - ((((REG_IR >> 9) - 1) & 7) + 1)); +} + + +M68KMAKE_OP(subq, 16, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_16; + uint dst = m68ki_read_16(ea); + uint res = dst - src; + + FLAG_N = NFLAG_16(res); + FLAG_Z = MASK_OUT_ABOVE_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + m68ki_write_16(ea, FLAG_Z); +} + + +M68KMAKE_OP(subq, 32, ., d) +{ + uint* r_dst = &DY; + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint dst = *r_dst; + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + *r_dst = FLAG_Z; +} + + +M68KMAKE_OP(subq, 32, ., a) +{ + uint* r_dst = &AY; + + *r_dst = MASK_OUT_ABOVE_32(*r_dst - ((((REG_IR >> 9) - 1) & 7) + 1)); +} + + +M68KMAKE_OP(subq, 32, ., .) +{ + uint src = (((REG_IR >> 9) - 1) & 7) + 1; + uint ea = M68KMAKE_GET_EA_AY_32; + uint dst = m68ki_read_32(ea); + uint res = dst - src; + + FLAG_N = NFLAG_32(res); + FLAG_Z = MASK_OUT_ABOVE_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + m68ki_write_32(ea, FLAG_Z); +} + + +M68KMAKE_OP(subx, 8, rr, .) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_8(DY); + uint dst = MASK_OUT_ABOVE_8(*r_dst); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_8(*r_dst) | res; +} + + +M68KMAKE_OP(subx, 16, rr, .) +{ + uint* r_dst = &DX; + uint src = MASK_OUT_ABOVE_16(DY); + uint dst = MASK_OUT_ABOVE_16(*r_dst); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | res; +} + + +M68KMAKE_OP(subx, 32, rr, .) +{ + uint* r_dst = &DX; + uint src = DY; + uint dst = *r_dst; + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + *r_dst = res; +} + + +M68KMAKE_OP(subx, 8, mm, ax7) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(subx, 8, mm, ay7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(subx, 8, mm, axy7) +{ + uint src = OPER_A7_PD_8(); + uint ea = EA_A7_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(subx, 8, mm, .) +{ + uint src = OPER_AY_PD_8(); + uint ea = EA_AX_PD_8(); + uint dst = m68ki_read_8(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_8(res); + FLAG_X = FLAG_C = CFLAG_8(res); + FLAG_V = VFLAG_SUB_8(src, dst, res); + + res = MASK_OUT_ABOVE_8(res); + FLAG_Z |= res; + + m68ki_write_8(ea, res); +} + + +M68KMAKE_OP(subx, 16, mm, .) +{ + uint src = OPER_AY_PD_16(); + uint ea = EA_AX_PD_16(); + uint dst = m68ki_read_16(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_16(res); + FLAG_X = FLAG_C = CFLAG_16(res); + FLAG_V = VFLAG_SUB_16(src, dst, res); + + res = MASK_OUT_ABOVE_16(res); + FLAG_Z |= res; + + m68ki_write_16(ea, res); +} + + +M68KMAKE_OP(subx, 32, mm, .) +{ + uint src = OPER_AY_PD_32(); + uint ea = EA_AX_PD_32(); + uint dst = m68ki_read_32(ea); + uint res = dst - src - XFLAG_AS_1(); + + FLAG_N = NFLAG_32(res); + FLAG_X = FLAG_C = CFLAG_SUB_32(src, dst, res); + FLAG_V = VFLAG_SUB_32(src, dst, res); + + res = MASK_OUT_ABOVE_32(res); + FLAG_Z |= res; + + m68ki_write_32(ea, res); +} + + +M68KMAKE_OP(swap, 32, ., .) +{ + uint* r_dst = &DY; + + FLAG_Z = MASK_OUT_ABOVE_32(*r_dst<<16); + *r_dst = (*r_dst>>16) | FLAG_Z; + + FLAG_Z = *r_dst; + FLAG_N = NFLAG_32(*r_dst); + FLAG_C = CFLAG_CLEAR; + FLAG_V = VFLAG_CLEAR; +} + + +M68KMAKE_OP(tas, 8, ., d) +{ + uint* r_dst = &DY; + + FLAG_Z = MASK_OUT_ABOVE_8(*r_dst); + FLAG_N = NFLAG_8(*r_dst); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + *r_dst |= 0x80; +} + + +M68KMAKE_OP(tas, 8, ., .) +{ + uint ea = M68KMAKE_GET_EA_AY_8; + uint dst = m68ki_read_8(ea); + + FLAG_Z = dst; + FLAG_N = NFLAG_8(dst); + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + m68ki_write_8(ea, dst | 0x80); +} + + +M68KMAKE_OP(trap, 0, ., .) +{ + /* Trap#n stacks exception frame type 0 */ + m68ki_exception_trapN(EXCEPTION_TRAP_BASE + (REG_IR & 0xf)); /* HJB 990403 */ +} + + +M68KMAKE_OP(trapt, 0, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapt, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 2; // JFF else stackframe & return addresses are incorrect + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapt, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 4; // JFF else stackframe & return addresses are incorrect + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapf, 0, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapf, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 2; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapf, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 4; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapcc, 0, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + if(M68KMAKE_CC) + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapcc, 16, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 2; /* JFF increase before or 1) stackframe is incorrect 2) RTE address is wrong if trap is taken */ + if(M68KMAKE_CC) + { + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapcc, 32, ., .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + REG_PC += 4; /* JFF increase before or 1) stackframe is incorrect 2) RTE address is wrong if trap is taken */ + if(M68KMAKE_CC) + { + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ + return; + } + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(trapv, 0, ., .) +{ + if(COND_VC()) + { + return; + } + m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */ +} + + +M68KMAKE_OP(tst, 8, ., d) +{ + uint res = MASK_OUT_ABOVE_8(DY); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 8, ., .) +{ + uint res = M68KMAKE_GET_OPER_AY_8; + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 8, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCDI_8(); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 8, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCIX_8(); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 8, ., i) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_I_8(); + + FLAG_N = NFLAG_8(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 16, ., d) +{ + uint res = MASK_OUT_ABOVE_16(DY); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 16, ., a) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = MAKE_INT_16(AY); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 16, ., .) +{ + uint res = M68KMAKE_GET_OPER_AY_16; + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 16, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCDI_16(); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 16, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCIX_16(); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 16, ., i) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_I_16(); + + FLAG_N = NFLAG_16(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 32, ., d) +{ + uint res = DY; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 32, ., a) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = AY; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 32, ., .) +{ + uint res = M68KMAKE_GET_OPER_AY_32; + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; +} + + +M68KMAKE_OP(tst, 32, ., pcdi) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCDI_32(); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 32, ., pcix) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_PCIX_32(); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(tst, 32, ., i) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint res = OPER_I_32(); + + FLAG_N = NFLAG_32(res); + FLAG_Z = res; + FLAG_V = VFLAG_CLEAR; + FLAG_C = CFLAG_CLEAR; + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(unlk, 32, ., a7) +{ + REG_A[7] = m68ki_read_32(REG_A[7]); +} + + +M68KMAKE_OP(unlk, 32, ., .) +{ + uint* r_dst = &AY; + + REG_A[7] = *r_dst; + *r_dst = m68ki_pull_32(); +} + + +M68KMAKE_OP(unpk, 16, rr, .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: DX and DY are reversed in Motorola's docs */ + uint src = DY; + uint* r_dst = &DX; + + *r_dst = MASK_OUT_BELOW_16(*r_dst) | (((((src << 4) & 0x0f00) | (src & 0x000f)) + OPER_I_16()) & 0xffff); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(unpk, 16, mm, ax7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint src = OPER_AY_PD_8(); + uint ea_dst; + + src = (((src << 4) & 0x0f00) | (src & 0x000f)) + OPER_I_16(); + ea_dst = EA_A7_PD_8(); + m68ki_write_8(ea_dst, (src >> 8) & 0xff); + ea_dst = EA_A7_PD_8(); + m68ki_write_8(ea_dst, src & 0xff); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(unpk, 16, mm, ay7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint src = OPER_A7_PD_8(); + uint ea_dst; + + src = (((src << 4) & 0x0f00) | (src & 0x000f)) + OPER_I_16(); + ea_dst = EA_AX_PD_8(); + m68ki_write_8(ea_dst, (src >> 8) & 0xff); + ea_dst = EA_AX_PD_8(); + m68ki_write_8(ea_dst, src & 0xff); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(unpk, 16, mm, axy7) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + uint src = OPER_A7_PD_8(); + uint ea_dst; + + src = (((src << 4) & 0x0f00) | (src & 0x000f)) + OPER_I_16(); + ea_dst = EA_A7_PD_8(); + m68ki_write_8(ea_dst, (src >> 8) & 0xff); + ea_dst = EA_A7_PD_8(); + m68ki_write_8(ea_dst, src & 0xff); + return; + } + m68ki_exception_illegal(); +} + + +M68KMAKE_OP(unpk, 16, mm, .) +{ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Note: AX and AY are reversed in Motorola's docs */ + uint src = OPER_AY_PD_8(); + uint ea_dst; + + src = (((src << 4) & 0x0f00) | (src & 0x000f)) + OPER_I_16(); + ea_dst = EA_AX_PD_8(); + m68ki_write_8(ea_dst, (src >> 8) & 0xff); + ea_dst = EA_AX_PD_8(); + m68ki_write_8(ea_dst, src & 0xff); + return; + } + m68ki_exception_illegal(); +} + + + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +M68KMAKE_END diff --git a/Src/CPU/68K/Musashi/m68kconf.h b/Src/CPU/68K/Musashi/m68kconf.h new file mode 100644 index 0000000..edc0b7f --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kconf.h @@ -0,0 +1,257 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kconf.h + * + * Musashi configuration. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + + + +#ifndef M68KCONF__HEADER +#define M68KCONF__HEADER + +#define M68K_COMPILE_FOR_MAME OPT_OFF + +/* Configuration switches. + * Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks. + * OPT_SPECIFY_HANDLER causes the core to link directly to the function + * or macro you specify, rather than using callback functions whose pointer + * must be passed in using m68k_set_xxx_callback(). + */ +#define OPT_OFF 0 +#define OPT_ON 1 +#define OPT_SPECIFY_HANDLER 2 + + +/* ======================================================================== */ +/* ============================= CONFIGURATION ============================ */ +/* ======================================================================== */ + +/* Turn ON if you want to use the following M68K variants */ +#define M68K_EMULATE_008 OPT_OFF +#define M68K_EMULATE_010 OPT_OFF +#define M68K_EMULATE_EC020 OPT_OFF +#define M68K_EMULATE_020 OPT_OFF + + +/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing + * and m68k_read_pcrelative_xx() for PC-relative addressing. + * If off, all read requests from the CPU will be redirected to m68k_read_xx() + */ +#define M68K_SEPARATE_READS OPT_ON // fixes Supermodel compilation on MacOS + +/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a + * predecrement destination EA mode instead of m68k_write_32(). + * To simulate real 68k behavior, m68k_write_32_pd() must first write the high + * word to [address+2], and then write the low word to [address]. + */ +#define M68K_SIMULATE_PD_WRITES OPT_OFF + +/* If ON, CPU will call the interrupt acknowledge callback when it services an + * interrupt. + * If off, all interrupts will be autovectored and all interrupt requests will + * auto-clear when the interrupt is serviced. + */ +#define M68K_EMULATE_INT_ACK OPT_SPECIFY_HANDLER +#define M68K_INT_ACK_CALLBACK(A) M68KIRQCallback(A) + + +/* If ON, CPU will call the breakpoint acknowledge callback when it encounters + * a breakpoint instruction and it is running a 68010+. + */ +#define M68K_EMULATE_BKPT_ACK OPT_OFF +#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function() + + +/* If ON, the CPU will monitor the trace flags and take trace exceptions + */ +#define M68K_EMULATE_TRACE OPT_OFF + + +/* If ON, CPU will call the output reset callback when it encounters a reset + * instruction. + */ +#define M68K_EMULATE_RESET OPT_OFF +#define M68K_RESET_CALLBACK() M68KResetCallback() + + +/* If ON, CPU will call the callback when it encounters a cmpi.l #v, dn + * instruction. + */ +#define M68K_CMPILD_HAS_CALLBACK OPT_OFF +#define M68K_CMPILD_CALLBACK(v,r) your_cmpild_handler_function(v,r) + +/* If ON, CPU will call the callback when it encounters a rte + * instruction. + */ +#define M68K_RTE_HAS_CALLBACK OPT_OFF +#define M68K_RTE_CALLBACK() your_rte_handler_function() + + +/* If ON, CPU will call the set fc callback on every memory access to + * differentiate between user/supervisor, program/data access like a real + * 68000 would. This should be enabled and the callback should be set if you + * want to properly emulate the m68010 or higher. (moves uses function codes + * to read/write data from different address spaces) + */ +#define M68K_EMULATE_FC OPT_OFF +#define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A) + + +/* If ON, CPU will call the pc changed callback when it changes the PC by a + * large value. This allows host programs to be nicer when it comes to + * fetching immediate data and instructions on a banked memory system. + */ +#define M68K_MONITOR_PC OPT_OFF +#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A) + + +/* If ON, CPU will call the instruction hook callback before every + * instruction. + */ +#ifdef SUPERMODEL_DEBUGGER +#define M68K_INSTRUCTION_HOOK OPT_SPECIFY_HANDLER +#define M68K_INSTRUCTION_CALLBACK() M68KDebugCallback() +#else +#define M68K_INSTRUCTION_HOOK OPT_OFF +#define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function() +#endif // SUPERMODEL_DEBUGGER + +/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */ +#define M68K_EMULATE_PREFETCH OPT_OFF + + +/* If ON, the CPU will generate address error exceptions if it tries to + * access a word or longword at an odd address. + * NOTE: This is only emulated properly for 68000 mode. + */ +#define M68K_EMULATE_ADDRESS_ERROR OPT_OFF + + +/* Turn ON to enable logging of illegal instruction calls. + * M68K_LOG_FILEHANDLE must be #defined to a stdio file stream. + * Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls. + */ +#define M68K_LOG_ENABLE OPT_OFF +#define M68K_LOG_1010_1111 OPT_OFF +#define M68K_LOG_FILEHANDLE some_file_handle + + +/* ----------------------------- COMPATIBILITY ---------------------------- */ + +/* The following options set optimizations that violate the current ANSI + * standard, but will be compliant under the forthcoming C9X standard. + */ + + +/* If ON, the enulation core will use 64-bit integers to speed up some + * operations. +*/ +#define M68K_USE_64_BIT OPT_ON + + +/* Set to your compiler's static inline keyword to enable it, or + * set it to blank to disable it. + * If you define INLINE in the makefile, it will override this value. + * NOTE: not enabling inline functions will SEVERELY slow down emulation. + */ +#ifndef INLINE +#define INLINE static __inline__ // defined for GCC; if using MSVC, pass INLINE as "static __inline" from Makefile +#endif /* INLINE */ + +/****************************************************************************** + Supermodel Interface +******************************************************************************/ + +// Supermodel 68K interface (these functions defined in CPU/68K.cpp) +//#ifndef FASTCALL (this doesn't work for now (needs to be added to the prototypes in m68k.h for m68k_read_memory*) + #undef FASTCALL + #define FASTCALL +//#endif + +extern int M68KIRQCallback(int irq); + +unsigned int FASTCALL M68KFetch8(unsigned int a); +unsigned int FASTCALL M68KFetch16(unsigned int a); +unsigned int FASTCALL M68KFetch32(unsigned int a); +unsigned int FASTCALL M68KRead8(unsigned int a); +unsigned int FASTCALL M68KRead16(unsigned int a); +unsigned int FASTCALL M68KRead32(unsigned int a); +void FASTCALL M68KWrite8(unsigned int a, unsigned int d); +void FASTCALL M68KWrite16(unsigned int a, unsigned int d); +void FASTCALL M68KWrite32(unsigned int a, unsigned int d); + +/* Read data relative to the PC */ +#define m68k_read_pcrelative_8(address) M68KFetch8(address) +#define m68k_read_pcrelative_16(address) M68KFetch16(address) +#define m68k_read_pcrelative_32(address) M68KFetch32(address) + +/* Read data immediately following the PC */ +#define m68k_read_immediate_16(address) M68KFetch16(address) +#define m68k_read_immediate_32(address) M68KFetch32(address) + +/* Memory access for the disassembler */ +#define m68k_read_disassembler_8(address) M68KRead8(address) +#define m68k_read_disassembler_16(address) M68KRead16(address) +#define m68k_read_disassembler_32(address) M68KRead32(address) + +/* Read from anywhere */ +#define m68k_read_memory_8(address) M68KRead8(address) +#define m68k_read_memory_16(address) M68KRead16(address) +#define m68k_read_memory_32(address) M68KRead32(address) + +/* Write to anywhere */ +#define m68k_write_memory_8(address, value) M68KWrite8(address, value) +#define m68k_write_memory_16(address, value) M68KWrite16(address, value) +#define m68k_write_memory_32(address, value) M68KWrite32(address, value) + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + +#endif /* M68KCONF__HEADER */ diff --git a/Src/CPU/68K/Musashi/m68kcpu.c b/Src/CPU/68K/Musashi/m68kcpu.c new file mode 100644 index 0000000..207aaf7 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kcpu.c @@ -0,0 +1,957 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kcpu.c + * + * Musashi core functions and interface. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + * + * NOTE: The internal CPU context is initialized to all zeros here. If setting + * an external context for the first time, make sure it is cleared first, + * otherwise interrupts may appear pending and other nasty problems. + */ + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ + +#if 1 +static const char* copyright_notice = +"MUSASHI\n" +"Version 3.3 (2001-01-29)\n" +"A portable Motorola M680x0 processor emulation engine.\n" +"Copyright 1998-2001 Karl Stenerud. All rights reserved.\n" +"\n" +"This code may be freely used for non-commercial purpooses as long as this\n" +"copyright notice remains unaltered in the source code and any binary files\n" +"containing this code in compiled form.\n" +"\n" +"All other lisencing terms must be negotiated with the author\n" +"(Karl Stenerud).\n" +"\n" +"The latest version of this code can be obtained at:\n" +"http://kstenerud.cjb.net\n" +; +#endif + + +/* ======================================================================== */ +/* ================================= NOTES ================================ */ +/* ======================================================================== */ + + + +/* ======================================================================== */ +/* ================================ INCLUDES ============================== */ +/* ======================================================================== */ + +#include "m68kops.h" +#include "m68kcpu.h" + +/* ======================================================================== */ +/* ================================= DATA ================================= */ +/* ======================================================================== */ + +int m68ki_initial_cycles; +int m68ki_remaining_cycles = 0; /* Number of clocks remaining */ +uint m68ki_tracing = 0; +uint m68ki_address_space; + +#ifdef M68K_LOG_ENABLE +const char* m68ki_cpu_names[] = +{ + "Invalid CPU", + "M68000", + "M68008", + "Invalid CPU", + "M68010", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "M68EC020", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "Invalid CPU", + "M68020" +}; +#endif /* M68K_LOG_ENABLE */ + +/* The CPU core */ +m68ki_cpu_core m68ki_cpu = {0}; + +#if M68K_EMULATE_ADDRESS_ERROR +jmp_buf m68ki_aerr_trap; +#endif /* M68K_EMULATE_ADDRESS_ERROR */ + +uint m68ki_aerr_address; +uint m68ki_aerr_write_mode; +uint m68ki_aerr_fc; + +/* Used by shift & rotate instructions */ +const uint8 m68ki_shift_8_table[65] = +{ + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff +}; +const uint16 m68ki_shift_16_table[65] = +{ + 0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, + 0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff +}; +const uint m68ki_shift_32_table[65] = +{ + 0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000, + 0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000, + 0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000, + 0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00, + 0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8, + 0xfffffffc, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +/* Number of clock cycles to use for exception processing. + * I used 4 for any vectors that are undocumented for processing times. + */ +const uint8 m68ki_exception_cycle_table[3][256] = +{ + { /* 000 */ + 40, /* 0: Reset - Initial Stack Pointer */ + 4, /* 1: Reset - Initial Program Counter */ + 50, /* 2: Bus Error (unemulated) */ + 50, /* 3: Address Error (unemulated) */ + 34, /* 4: Illegal Instruction */ + 38, /* 5: Divide by Zero -- ASG: changed from 42 */ + 40, /* 6: CHK -- ASG: chanaged from 44 */ + 34, /* 7: TRAPV */ + 34, /* 8: Privilege Violation */ + 34, /* 9: Trace */ + 34, /* 10: 1010 */ + 34, /* 11: 1111 */ + 4, /* 12: RESERVED */ + 4, /* 13: Coprocessor Protocol Violation (unemulated) */ + 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 -- ASG: chanaged from 38 */ + 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 (unemulated) */ + 4, /* 49: FP Inexact Result (unemulated) */ + 4, /* 50: FP Divide by Zero (unemulated) */ + 4, /* 51: FP Underflow (unemulated) */ + 4, /* 52: FP Operand Error (unemulated) */ + 4, /* 53: FP Overflow (unemulated) */ + 4, /* 54: FP Signaling NAN (unemulated) */ + 4, /* 55: FP Unimplemented Data Type (unemulated) */ + 4, /* 56: MMU Configuration Error (unemulated) */ + 4, /* 57: MMU Illegal Operation Error (unemulated) */ + 4, /* 58: MMU Access Level Violation Error (unemulated) */ + 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 + }, + { /* 010 */ + 40, /* 0: Reset - Initial Stack Pointer */ + 4, /* 1: Reset - Initial Program Counter */ + 126, /* 2: Bus Error (unemulated) */ + 126, /* 3: Address Error (unemulated) */ + 38, /* 4: Illegal Instruction */ + 44, /* 5: Divide by Zero */ + 44, /* 6: CHK */ + 34, /* 7: TRAPV */ + 38, /* 8: Privilege Violation */ + 38, /* 9: Trace */ + 4, /* 10: 1010 */ + 4, /* 11: 1111 */ + 4, /* 12: RESERVED */ + 4, /* 13: Coprocessor Protocol Violation (unemulated) */ + 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 */ + 46, /* 24: Spurious Interrupt */ + 46, /* 25: Level 1 Interrupt Autovector */ + 46, /* 26: Level 2 Interrupt Autovector */ + 46, /* 27: Level 3 Interrupt Autovector */ + 46, /* 28: Level 4 Interrupt Autovector */ + 46, /* 29: Level 5 Interrupt Autovector */ + 46, /* 30: Level 6 Interrupt Autovector */ + 46, /* 31: Level 7 Interrupt Autovector */ + 38, /* 32: TRAP #0 */ + 38, /* 33: TRAP #1 */ + 38, /* 34: TRAP #2 */ + 38, /* 35: TRAP #3 */ + 38, /* 36: TRAP #4 */ + 38, /* 37: TRAP #5 */ + 38, /* 38: TRAP #6 */ + 38, /* 39: TRAP #7 */ + 38, /* 40: TRAP #8 */ + 38, /* 41: TRAP #9 */ + 38, /* 42: TRAP #10 */ + 38, /* 43: TRAP #11 */ + 38, /* 44: TRAP #12 */ + 38, /* 45: TRAP #13 */ + 38, /* 46: TRAP #14 */ + 38, /* 47: TRAP #15 */ + 4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */ + 4, /* 49: FP Inexact Result (unemulated) */ + 4, /* 50: FP Divide by Zero (unemulated) */ + 4, /* 51: FP Underflow (unemulated) */ + 4, /* 52: FP Operand Error (unemulated) */ + 4, /* 53: FP Overflow (unemulated) */ + 4, /* 54: FP Signaling NAN (unemulated) */ + 4, /* 55: FP Unimplemented Data Type (unemulated) */ + 4, /* 56: MMU Configuration Error (unemulated) */ + 4, /* 57: MMU Illegal Operation Error (unemulated) */ + 4, /* 58: MMU Access Level Violation Error (unemulated) */ + 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 + }, + { /* 020 */ + 4, /* 0: Reset - Initial Stack Pointer */ + 4, /* 1: Reset - Initial Program Counter */ + 50, /* 2: Bus Error (unemulated) */ + 50, /* 3: Address Error (unemulated) */ + 20, /* 4: Illegal Instruction */ + 38, /* 5: Divide by Zero */ + 40, /* 6: CHK */ + 20, /* 7: TRAPV */ + 34, /* 8: Privilege Violation */ + 25, /* 9: Trace */ + 20, /* 10: 1010 */ + 20, /* 11: 1111 */ + 4, /* 12: RESERVED */ + 4, /* 13: Coprocessor Protocol Violation (unemulated) */ + 4, /* 14: Format Error */ + 30, /* 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 */ + 30, /* 24: Spurious Interrupt */ + 30, /* 25: Level 1 Interrupt Autovector */ + 30, /* 26: Level 2 Interrupt Autovector */ + 30, /* 27: Level 3 Interrupt Autovector */ + 30, /* 28: Level 4 Interrupt Autovector */ + 30, /* 29: Level 5 Interrupt Autovector */ + 30, /* 30: Level 6 Interrupt Autovector */ + 30, /* 31: Level 7 Interrupt Autovector */ + 20, /* 32: TRAP #0 */ + 20, /* 33: TRAP #1 */ + 20, /* 34: TRAP #2 */ + 20, /* 35: TRAP #3 */ + 20, /* 36: TRAP #4 */ + 20, /* 37: TRAP #5 */ + 20, /* 38: TRAP #6 */ + 20, /* 39: TRAP #7 */ + 20, /* 40: TRAP #8 */ + 20, /* 41: TRAP #9 */ + 20, /* 42: TRAP #10 */ + 20, /* 43: TRAP #11 */ + 20, /* 44: TRAP #12 */ + 20, /* 45: TRAP #13 */ + 20, /* 46: TRAP #14 */ + 20, /* 47: TRAP #15 */ + 4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */ + 4, /* 49: FP Inexact Result (unemulated) */ + 4, /* 50: FP Divide by Zero (unemulated) */ + 4, /* 51: FP Underflow (unemulated) */ + 4, /* 52: FP Operand Error (unemulated) */ + 4, /* 53: FP Overflow (unemulated) */ + 4, /* 54: FP Signaling NAN (unemulated) */ + 4, /* 55: FP Unimplemented Data Type (unemulated) */ + 4, /* 56: MMU Configuration Error (unemulated) */ + 4, /* 57: MMU Illegal Operation Error (unemulated) */ + 4, /* 58: MMU Access Level Violation Error (unemulated) */ + 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 + } +}; + +const uint8 m68ki_ea_idx_cycle_table[64] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* ..01.000 no memory indirect, base NULL */ + 5, /* ..01..01 memory indirect, base NULL, outer NULL */ + 7, /* ..01..10 memory indirect, base NULL, outer 16 */ + 7, /* ..01..11 memory indirect, base NULL, outer 32 */ + 0, 5, 7, 7, 0, 5, 7, 7, 0, 5, 7, 7, + 2, /* ..10.000 no memory indirect, base 16 */ + 7, /* ..10..01 memory indirect, base 16, outer NULL */ + 9, /* ..10..10 memory indirect, base 16, outer 16 */ + 9, /* ..10..11 memory indirect, base 16, outer 32 */ + 0, 7, 9, 9, 0, 7, 9, 9, 0, 7, 9, 9, + 6, /* ..11.000 no memory indirect, base 32 */ + 11, /* ..11..01 memory indirect, base 32, outer NULL */ + 13, /* ..11..10 memory indirect, base 32, outer 16 */ + 13, /* ..11..11 memory indirect, base 32, outer 32 */ + 0, 11, 13, 13, 0, 11, 13, 13, 0, 11, 13, 13 +}; + + + +/* ======================================================================== */ +/* =============================== CALLBACKS ============================== */ +/* ======================================================================== */ + +/* Default callbacks used if the callback hasn't been set yet, or if the + * callback is set to NULL + */ + +/* Interrupt acknowledge */ +static int default_int_ack_callback_data; +static int default_int_ack_callback(int int_level) +{ + default_int_ack_callback_data = int_level; + CPU_INT_LEVEL = 0; + return M68K_INT_ACK_AUTOVECTOR; +} + +/* Breakpoint acknowledge */ +static unsigned int default_bkpt_ack_callback_data; +static void default_bkpt_ack_callback(unsigned int data) +{ + default_bkpt_ack_callback_data = data; +} + +/* Called when a reset instruction is executed */ +static void default_reset_instr_callback(void) +{ +} + +/* Called when a cmpi.l #v, dn instruction is executed */ +static void default_cmpild_instr_callback(unsigned int val, int reg) +{ +} + +/* Called when a rte instruction is executed */ +static void default_rte_instr_callback(void) +{ +} + +/* Called when the program counter changed by a large value */ +static unsigned int default_pc_changed_callback_data; +static void default_pc_changed_callback(unsigned int new_pc) +{ + default_pc_changed_callback_data = new_pc; +} + +/* Called every time there's bus activity (read/write to/from memory */ +static unsigned int default_set_fc_callback_data; +static void default_set_fc_callback(unsigned int new_fc) +{ + default_set_fc_callback_data = new_fc; +} + +/* Called every instruction cycle prior to execution */ +static void default_instr_hook_callback(void) +{ +} + + +#if M68K_EMULATE_ADDRESS_ERROR + #include + jmp_buf m68ki_aerr_trap; +#endif /* M68K_EMULATE_ADDRESS_ERROR */ + + +/* ======================================================================== */ +/* ================================= API ================================== */ +/* ======================================================================== */ + +/* Access the internals of the CPU */ +unsigned int m68k_get_reg(void* context, m68k_register_t regnum) +{ + m68ki_cpu_core* cpu = context != NULL ?(m68ki_cpu_core*)context : &m68ki_cpu; + + switch(regnum) + { + case M68K_REG_D0: return cpu->dar[0]; + case M68K_REG_D1: return cpu->dar[1]; + case M68K_REG_D2: return cpu->dar[2]; + case M68K_REG_D3: return cpu->dar[3]; + case M68K_REG_D4: return cpu->dar[4]; + case M68K_REG_D5: return cpu->dar[5]; + case M68K_REG_D6: return cpu->dar[6]; + case M68K_REG_D7: return cpu->dar[7]; + case M68K_REG_A0: return cpu->dar[8]; + case M68K_REG_A1: return cpu->dar[9]; + case M68K_REG_A2: return cpu->dar[10]; + case M68K_REG_A3: return cpu->dar[11]; + case M68K_REG_A4: return cpu->dar[12]; + case M68K_REG_A5: return cpu->dar[13]; + case M68K_REG_A6: return cpu->dar[14]; + case M68K_REG_A7: return cpu->dar[15]; + case M68K_REG_PC: return MASK_OUT_ABOVE_32(cpu->pc); + case M68K_REG_SR: return cpu->t1_flag | + cpu->t0_flag | + (cpu->s_flag << 11) | + (cpu->m_flag << 11) | + cpu->int_mask | + ((cpu->x_flag & XFLAG_SET) >> 4) | + ((cpu->n_flag & NFLAG_SET) >> 4) | + ((!cpu->not_z_flag) << 2) | + ((cpu->v_flag & VFLAG_SET) >> 6) | + ((cpu->c_flag & CFLAG_SET) >> 8); + case M68K_REG_SP: return cpu->dar[15]; + case M68K_REG_USP: return cpu->s_flag ? cpu->sp[0] : cpu->dar[15]; + case M68K_REG_ISP: return cpu->s_flag && !cpu->m_flag ? cpu->dar[15] : cpu->sp[4]; + case M68K_REG_MSP: return cpu->s_flag && cpu->m_flag ? cpu->dar[15] : cpu->sp[6]; + case M68K_REG_SFC: return cpu->sfc; + case M68K_REG_DFC: return cpu->dfc; + case M68K_REG_VBR: return cpu->vbr; + case M68K_REG_CACR: return cpu->cacr; + case M68K_REG_CAAR: return cpu->caar; + case M68K_REG_PREF_ADDR: return cpu->pref_addr; + case M68K_REG_PREF_DATA: return cpu->pref_data; + case M68K_REG_PPC: return MASK_OUT_ABOVE_32(cpu->ppc); + case M68K_REG_IR: return cpu->ir; + case M68K_REG_CPU_TYPE: + switch(cpu->cpu_type) + { + case CPU_TYPE_000: return (unsigned int)M68K_CPU_TYPE_68000; + case CPU_TYPE_008: return (unsigned int)M68K_CPU_TYPE_68008; + case CPU_TYPE_010: return (unsigned int)M68K_CPU_TYPE_68010; + case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020; + case CPU_TYPE_020: return (unsigned int)M68K_CPU_TYPE_68020; + } + return M68K_CPU_TYPE_INVALID; + default: return 0; + } + return 0; +} + +void m68k_set_reg(m68k_register_t regnum, unsigned int value) +{ + switch(regnum) + { + case M68K_REG_D0: REG_D[0] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D1: REG_D[1] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D2: REG_D[2] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D3: REG_D[3] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D4: REG_D[4] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D5: REG_D[5] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D6: REG_D[6] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_D7: REG_D[7] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A0: REG_A[0] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A1: REG_A[1] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A2: REG_A[2] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A3: REG_A[3] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A4: REG_A[4] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A5: REG_A[5] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A6: REG_A[6] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_A7: REG_A[7] = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_PC: m68ki_jump(MASK_OUT_ABOVE_32(value)); return; + case M68K_REG_SR: m68ki_set_sr_noint_nosp(value); return; + case M68K_REG_SP: REG_SP = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_USP: if(FLAG_S) + REG_USP = MASK_OUT_ABOVE_32(value); + else + REG_SP = MASK_OUT_ABOVE_32(value); + return; + case M68K_REG_ISP: if(FLAG_S && !FLAG_M) + REG_SP = MASK_OUT_ABOVE_32(value); + else + REG_ISP = MASK_OUT_ABOVE_32(value); + return; + case M68K_REG_MSP: if(FLAG_S && FLAG_M) + REG_SP = MASK_OUT_ABOVE_32(value); + else + REG_MSP = MASK_OUT_ABOVE_32(value); + return; + case M68K_REG_VBR: REG_VBR = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_SFC: REG_SFC = value & 7; return; + case M68K_REG_DFC: REG_DFC = value & 7; return; + case M68K_REG_CACR: REG_CACR = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_CAAR: REG_CAAR = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_PPC: REG_PPC = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_IR: REG_IR = MASK_OUT_ABOVE_16(value); return; + case M68K_REG_PREF_ADDR: CPU_PREF_ADDR = MASK_OUT_ABOVE_32(value); return; + case M68K_REG_CPU_TYPE: m68k_set_cpu_type(value); return; + default: return; + } +} + +/* Set the callbacks */ +void m68k_set_int_ack_callback(int (*callback)(int int_level)) +{ + CALLBACK_INT_ACK = callback ? callback : default_int_ack_callback; +} + +void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)) +{ + CALLBACK_BKPT_ACK = callback ? callback : default_bkpt_ack_callback; +} + +void m68k_set_reset_instr_callback(void (*callback)(void)) +{ + CALLBACK_RESET_INSTR = callback ? callback : default_reset_instr_callback; +} + +void m68k_set_cmpild_instr_callback(void (*callback)(unsigned int, int)) +{ + CALLBACK_CMPILD_INSTR = callback ? callback : default_cmpild_instr_callback; +} + +void m68k_set_rte_instr_callback(void (*callback)(void)) +{ + CALLBACK_RTE_INSTR = callback ? callback : default_rte_instr_callback; +} + +void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)) +{ + CALLBACK_PC_CHANGED = callback ? callback : default_pc_changed_callback; +} + +void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)) +{ + CALLBACK_SET_FC = callback ? callback : default_set_fc_callback; +} + +void m68k_set_instr_hook_callback(void (*callback)(void)) +{ + CALLBACK_INSTR_HOOK = callback ? callback : default_instr_hook_callback; +} + +#include +/* Set the CPU type. */ +void m68k_set_cpu_type(unsigned int cpu_type) +{ + switch(cpu_type) + { + case M68K_CPU_TYPE_68000: + CPU_TYPE = CPU_TYPE_000; + CPU_ADDRESS_MASK = 0x00ffffff; + CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */ + CYC_INSTRUCTION = m68ki_cycles[0]; + CYC_EXCEPTION = m68ki_exception_cycle_table[0]; + CYC_BCC_NOTAKE_B = -2; + CYC_BCC_NOTAKE_W = 2; + CYC_DBCC_F_NOEXP = -2; + CYC_DBCC_F_EXP = 2; + CYC_SCC_R_TRUE = 2; + CYC_MOVEM_W = 2; + CYC_MOVEM_L = 3; + CYC_SHIFT = 1; + CYC_RESET = 132; + return; + case M68K_CPU_TYPE_68008: + CPU_TYPE = CPU_TYPE_008; + CPU_ADDRESS_MASK = 0x003fffff; + CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */ + CYC_INSTRUCTION = m68ki_cycles[0]; + CYC_EXCEPTION = m68ki_exception_cycle_table[0]; + CYC_BCC_NOTAKE_B = -2; + CYC_BCC_NOTAKE_W = 2; + CYC_DBCC_F_NOEXP = -2; + CYC_DBCC_F_EXP = 2; + CYC_SCC_R_TRUE = 2; + CYC_MOVEM_W = 2; + CYC_MOVEM_L = 3; + CYC_SHIFT = 1; + CYC_RESET = 132; + return; + case M68K_CPU_TYPE_68010: + CPU_TYPE = CPU_TYPE_010; + CPU_ADDRESS_MASK = 0x00ffffff; + CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */ + CYC_INSTRUCTION = m68ki_cycles[1]; + CYC_EXCEPTION = m68ki_exception_cycle_table[1]; + CYC_BCC_NOTAKE_B = -4; + CYC_BCC_NOTAKE_W = 0; + CYC_DBCC_F_NOEXP = 0; + CYC_DBCC_F_EXP = 6; + CYC_SCC_R_TRUE = 0; + CYC_MOVEM_W = 2; + CYC_MOVEM_L = 3; + CYC_SHIFT = 1; + CYC_RESET = 130; + return; + case M68K_CPU_TYPE_68EC020: + CPU_TYPE = CPU_TYPE_EC020; + CPU_ADDRESS_MASK = 0x00ffffff; + CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */ + CYC_INSTRUCTION = m68ki_cycles[2]; + CYC_EXCEPTION = m68ki_exception_cycle_table[2]; + CYC_BCC_NOTAKE_B = -2; + CYC_BCC_NOTAKE_W = 0; + CYC_DBCC_F_NOEXP = 0; + CYC_DBCC_F_EXP = 4; + CYC_SCC_R_TRUE = 0; + CYC_MOVEM_W = 2; + CYC_MOVEM_L = 2; + CYC_SHIFT = 0; + CYC_RESET = 518; + return; + case M68K_CPU_TYPE_68020: + CPU_TYPE = CPU_TYPE_020; + CPU_ADDRESS_MASK = 0xffffffff; + CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */ + CYC_INSTRUCTION = m68ki_cycles[2]; + CYC_EXCEPTION = m68ki_exception_cycle_table[2]; + CYC_BCC_NOTAKE_B = -2; + CYC_BCC_NOTAKE_W = 0; + CYC_DBCC_F_NOEXP = 0; + CYC_DBCC_F_EXP = 4; + CYC_SCC_R_TRUE = 0; + CYC_MOVEM_W = 2; + CYC_MOVEM_L = 2; + CYC_SHIFT = 0; + CYC_RESET = 518; + return; + } +} + +/* Execute some instructions until we use up num_cycles clock cycles */ +/* ASG: removed per-instruction interrupt checks */ +int m68k_execute(int num_cycles) +{ + /* Make sure we're not stopped */ + if(!CPU_STOPPED) + { + /* Set our pool of clock cycles available */ + SET_CYCLES(num_cycles); + m68ki_initial_cycles = num_cycles; + + /* ASG: update cycles */ + USE_CYCLES(CPU_INT_CYCLES); + CPU_INT_CYCLES = 0; + + /* Return point if we had an address error */ + m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */ + + /* Main loop. Keep going until we run out of clock cycles */ + do + { + /* Set tracing accodring to T1. (T0 is done inside instruction) */ + m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */ + + /* Set the address space for reads */ + m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */ + + /* Call external hook to peek at CPU */ + m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */ + + /* Record previous program counter */ + REG_PPC = REG_PC; + + /* Read an instruction and call its handler */ + REG_IR = m68ki_read_imm_16(); + m68ki_instruction_jump_table[REG_IR](); + USE_CYCLES(CYC_INSTRUCTION[REG_IR]); + + /* Trace m68k_exception, if necessary */ + m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */ + } while(GET_CYCLES() > 0); + + /* set previous PC to current PC for the next entry into the loop */ + REG_PPC = REG_PC; + + /* ASG: update cycles */ + USE_CYCLES(CPU_INT_CYCLES); + CPU_INT_CYCLES = 0; + + /* return how many clocks we used */ + return m68ki_initial_cycles - GET_CYCLES(); + } + + /* We get here if the CPU is stopped or halted */ + SET_CYCLES(0); + CPU_INT_CYCLES = 0; + + return num_cycles; +} + + +int m68k_cycles_run(void) +{ + return m68ki_initial_cycles - GET_CYCLES(); +} + +int m68k_cycles_remaining(void) +{ + return GET_CYCLES(); +} + +/* Change the timeslice */ +void m68k_modify_timeslice(int cycles) +{ + m68ki_initial_cycles += cycles; + ADD_CYCLES(cycles); +} + + +void m68k_end_timeslice(void) +{ + m68ki_initial_cycles -= GET_CYCLES(); + SET_CYCLES(0); +} + + +/* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */ +/* KS: Modified so that IPL* bits match with mask positions in the SR + * and cleaned out remenants of the interrupt controller. + */ +void m68k_set_irq(unsigned int int_level) +{ + uint old_level = CPU_INT_LEVEL; + CPU_INT_LEVEL = int_level << 8; + + /* A transition from < 7 to 7 always interrupts (NMI) */ + /* Note: Level 7 can also level trigger like a normal IRQ */ + if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700) + m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */ + else + m68ki_check_interrupts(); /* Level triggered (IRQ) */ +} + +void m68k_init(void) +{ + static uint emulation_initialized = 0; + + /* The first call to this function initializes the opcode handler jump table */ + if(!emulation_initialized) + { + m68ki_build_opcode_table(); + emulation_initialized = 1; + } + + m68k_set_int_ack_callback(NULL); + m68k_set_bkpt_ack_callback(NULL); + m68k_set_reset_instr_callback(NULL); + m68k_set_cmpild_instr_callback(NULL); + m68k_set_rte_instr_callback(NULL); + m68k_set_pc_changed_callback(NULL); + m68k_set_fc_callback(NULL); + m68k_set_instr_hook_callback(NULL); +} + +/* Pulse the RESET line on the CPU */ +void m68k_pulse_reset(void) +{ + /* Clear all stop levels and eat up all remaining cycles */ + CPU_STOPPED = 0; + SET_CYCLES(0); + + CPU_RUN_MODE = RUN_MODE_BERR_AERR_RESET; + + /* Turn off tracing */ + FLAG_T1 = FLAG_T0 = 0; + m68ki_clear_trace(); + /* Interrupt mask to level 7 */ + FLAG_INT_MASK = 0x0700; + /* Reset VBR */ + REG_VBR = 0; + /* Go to supervisor mode */ + m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR); + + /* Invalidate the prefetch queue */ +#if M68K_EMULATE_PREFETCH + /* Set to arbitrary number since our first fetch is from 0 */ + CPU_PREF_ADDR = 0x1000; +#endif /* M68K_EMULATE_PREFETCH */ + + /* Read the initial stack pointer and program counter */ + m68ki_jump(0); + REG_SP = m68ki_read_imm_32(); + REG_PC = m68ki_read_imm_32(); + m68ki_jump(REG_PC); + + CPU_RUN_MODE = RUN_MODE_NORMAL; +} + +/* Pulse the HALT line on the CPU */ +void m68k_pulse_halt(void) +{ + CPU_STOPPED |= STOP_LEVEL_HALT; +} + + +/* Get and set the current CPU context */ +/* This is to allow for multiple CPUs */ +unsigned int m68k_context_size() +{ + return sizeof(m68ki_cpu_core); +} + +unsigned int m68k_get_context(void* dst) +{ + if(dst) *(m68ki_cpu_core*)dst = m68ki_cpu; + return sizeof(m68ki_cpu_core); +} + +void m68k_set_context(void* src) +{ + if(src) m68ki_cpu = *(m68ki_cpu_core*)src; +} + + + +/* ======================================================================== */ +/* ============================== MAME STUFF ============================== */ +/* ======================================================================== */ + +#if M68K_COMPILE_FOR_MAME == OPT_ON + +#include "state.h" + +static struct { + UINT16 sr; + int stopped; + int halted; +} m68k_substate; + +static void m68k_prepare_substate(void) +{ + m68k_substate.sr = m68ki_get_sr(); + m68k_substate.stopped = (CPU_STOPPED & STOP_LEVEL_STOP) != 0; + m68k_substate.halted = (CPU_STOPPED & STOP_LEVEL_HALT) != 0; +} + +static void m68k_post_load(void) +{ + m68ki_set_sr_noint_nosp(m68k_substate.sr); + CPU_STOPPED = m68k_substate.stopped ? STOP_LEVEL_STOP : 0 + | m68k_substate.halted ? STOP_LEVEL_HALT : 0; + m68ki_jump(REG_PC); +} + +void m68k_state_register(const char *type) +{ + int cpu = cpu_getactivecpu(); + + state_save_register_UINT32(type, cpu, "D" , REG_D, 8); + state_save_register_UINT32(type, cpu, "A" , REG_A, 8); + state_save_register_UINT32(type, cpu, "PPC" , ®_PPC, 1); + state_save_register_UINT32(type, cpu, "PC" , ®_PC, 1); + state_save_register_UINT32(type, cpu, "USP" , ®_USP, 1); + state_save_register_UINT32(type, cpu, "ISP" , ®_ISP, 1); + state_save_register_UINT32(type, cpu, "MSP" , ®_MSP, 1); + state_save_register_UINT32(type, cpu, "VBR" , ®_VBR, 1); + state_save_register_UINT32(type, cpu, "SFC" , ®_SFC, 1); + state_save_register_UINT32(type, cpu, "DFC" , ®_DFC, 1); + state_save_register_UINT32(type, cpu, "CACR" , ®_CACR, 1); + state_save_register_UINT32(type, cpu, "CAAR" , ®_CAAR, 1); + state_save_register_UINT16(type, cpu, "SR" , &m68k_substate.sr, 1); + state_save_register_UINT32(type, cpu, "INT_LEVEL" , &CPU_INT_LEVEL, 1); + state_save_register_UINT32(type, cpu, "INT_CYCLES", &CPU_INT_CYCLES, 1); + state_save_register_int (type, cpu, "STOPPED" , &m68k_substate.stopped); + state_save_register_int (type, cpu, "HALTED" , &m68k_substate.halted); + state_save_register_UINT32(type, cpu, "PREF_ADDR" , &CPU_PREF_ADDR, 1); + state_save_register_UINT32(type, cpu, "PREF_DATA" , &CPU_PREF_DATA, 1); + state_save_register_func_presave(m68k_prepare_substate); + state_save_register_func_postload(m68k_post_load); +} + +#endif /* M68K_COMPILE_FOR_MAME */ + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ diff --git a/Src/CPU/68K/Musashi/m68kcpu.h b/Src/CPU/68K/Musashi/m68kcpu.h new file mode 100644 index 0000000..64fb238 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kcpu.h @@ -0,0 +1,1973 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kcpu.h + * + * Musashi internal header file. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + + + + +#ifndef M68KCPU__HEADER +#define M68KCPU__HEADER + +#include "m68k.h" +#include + +#if M68K_EMULATE_ADDRESS_ERROR +#include +#endif /* M68K_EMULATE_ADDRESS_ERROR */ + +/* ======================================================================== */ +/* ==================== ARCHITECTURE-DEPENDANT DEFINES ==================== */ +/* ======================================================================== */ + +/* Check for > 32bit sizes */ +#if UINT_MAX > 0xffffffff + #define M68K_INT_GT_32_BIT 1 +#else + #define M68K_INT_GT_32_BIT 0 +#endif + +/* Data types used in this emulation core */ +#undef sint8 +#undef sint16 +#undef sint32 +#undef sint64 +#undef uint8 +#undef uint16 +#undef uint32 +#undef uint64 +#undef sint +#undef uint + +#define sint8 signed char /* ASG: changed from char to signed char */ +#define sint16 signed short +#define sint32 signed long +#define uint8 unsigned char +#define uint16 unsigned short +#define uint32 unsigned int + +/* signed and unsigned int must be at least 32 bits wide */ +#define sint signed int +#if defined(__ANDROID__) +/* sys/types.h already typedefs uint on Android; avoid redefining it as a macro. */ +typedef unsigned int uint; +#else +#define uint unsigned int +#endif + + +#if M68K_USE_64_BIT +#define sint64 signed long long +#define uint64 unsigned long long +#else +#define sint64 sint32 +#define uint64 uint32 +#endif /* M68K_USE_64_BIT */ + + + +/* Allow for architectures that don't have 8-bit sizes */ +#if UCHAR_MAX == 0xff + #define MAKE_INT_8(A) (sint8)(A) +#else + #undef sint8 + #define sint8 signed int + #undef uint8 + #define uint8 unsigned int + INLINE sint MAKE_INT_8(uint value) + { + return (value & 0x80) ? value | ~0xff : value & 0xff; + } +#endif /* UCHAR_MAX == 0xff */ + + +/* Allow for architectures that don't have 16-bit sizes */ +#if USHRT_MAX == 0xffff + #define MAKE_INT_16(A) (sint16)(A) +#else + #undef sint16 + #define sint16 signed int + #undef uint16 + #define uint16 unsigned int + INLINE sint MAKE_INT_16(uint value) + { + return (value & 0x8000) ? value | ~0xffff : value & 0xffff; + } +#endif /* USHRT_MAX == 0xffff */ + + +/* Allow for architectures that don't have 32-bit sizes */ +#if ULONG_MAX == 0xffffffff + #define MAKE_INT_32(A) (sint32)(A) +#else + #undef sint32 + #define sint32 signed int + #undef uint32 + #define uint32 unsigned int + INLINE sint MAKE_INT_32(uint value) + { + return (value & 0x80000000) ? value | ~0xffffffff : value & 0xffffffff; + } +#endif /* ULONG_MAX == 0xffffffff */ + + + + +/* ======================================================================== */ +/* ============================ GENERAL DEFINES =========================== */ +/* ======================================================================== */ + +/* Exception Vectors handled by emulation */ +#define EXCEPTION_BUS_ERROR 2 /* This one is not emulated! */ +#define EXCEPTION_ADDRESS_ERROR 3 /* This one is partially emulated (doesn't stack a proper frame yet) */ +#define EXCEPTION_ILLEGAL_INSTRUCTION 4 +#define EXCEPTION_ZERO_DIVIDE 5 +#define EXCEPTION_CHK 6 +#define EXCEPTION_TRAPV 7 +#define EXCEPTION_PRIVILEGE_VIOLATION 8 +#define EXCEPTION_TRACE 9 +#define EXCEPTION_1010 10 +#define EXCEPTION_1111 11 +#define EXCEPTION_FORMAT_ERROR 14 +#define EXCEPTION_UNINITIALIZED_INTERRUPT 15 +#define EXCEPTION_SPURIOUS_INTERRUPT 24 +#define EXCEPTION_INTERRUPT_AUTOVECTOR 24 +#define EXCEPTION_TRAP_BASE 32 + +/* Function codes set by CPU during data/address bus activity */ +#define FUNCTION_CODE_USER_DATA 1 +#define FUNCTION_CODE_USER_PROGRAM 2 +#define FUNCTION_CODE_SUPERVISOR_DATA 5 +#define FUNCTION_CODE_SUPERVISOR_PROGRAM 6 +#define FUNCTION_CODE_CPU_SPACE 7 + +/* CPU types for deciding what to emulate */ +#define CPU_TYPE_000 1 +#define CPU_TYPE_008 2 +#define CPU_TYPE_010 4 +#define CPU_TYPE_EC020 8 +#define CPU_TYPE_020 16 + +/* Different ways to stop the CPU */ +#define STOP_LEVEL_STOP 1 +#define STOP_LEVEL_HALT 2 + +/* Used for 68000 address error processing */ +#define INSTRUCTION_YES 0 +#define INSTRUCTION_NO 0x08 +#define MODE_READ 0x10 +#define MODE_WRITE 0 + +#define RUN_MODE_NORMAL 0 +#define RUN_MODE_BERR_AERR_RESET 1 + +#ifndef NULL +#define NULL ((void*)0) +#endif + +/* ======================================================================== */ +/* ================================ MACROS ================================ */ +/* ======================================================================== */ + + +/* ---------------------------- General Macros ---------------------------- */ + +/* Bit Isolation Macros */ +#define BIT_0(A) ((A) & 0x00000001) +#define BIT_1(A) ((A) & 0x00000002) +#define BIT_2(A) ((A) & 0x00000004) +#define BIT_3(A) ((A) & 0x00000008) +#define BIT_4(A) ((A) & 0x00000010) +#define BIT_5(A) ((A) & 0x00000020) +#define BIT_6(A) ((A) & 0x00000040) +#define BIT_7(A) ((A) & 0x00000080) +#define BIT_8(A) ((A) & 0x00000100) +#define BIT_9(A) ((A) & 0x00000200) +#define BIT_A(A) ((A) & 0x00000400) +#define BIT_B(A) ((A) & 0x00000800) +#define BIT_C(A) ((A) & 0x00001000) +#define BIT_D(A) ((A) & 0x00002000) +#define BIT_E(A) ((A) & 0x00004000) +#define BIT_F(A) ((A) & 0x00008000) +#define BIT_10(A) ((A) & 0x00010000) +#define BIT_11(A) ((A) & 0x00020000) +#define BIT_12(A) ((A) & 0x00040000) +#define BIT_13(A) ((A) & 0x00080000) +#define BIT_14(A) ((A) & 0x00100000) +#define BIT_15(A) ((A) & 0x00200000) +#define BIT_16(A) ((A) & 0x00400000) +#define BIT_17(A) ((A) & 0x00800000) +#define BIT_18(A) ((A) & 0x01000000) +#define BIT_19(A) ((A) & 0x02000000) +#define BIT_1A(A) ((A) & 0x04000000) +#define BIT_1B(A) ((A) & 0x08000000) +#define BIT_1C(A) ((A) & 0x10000000) +#define BIT_1D(A) ((A) & 0x20000000) +#define BIT_1E(A) ((A) & 0x40000000) +#define BIT_1F(A) ((A) & 0x80000000) + +/* Get the most significant bit for specific sizes */ +#define GET_MSB_8(A) ((A) & 0x80) +#define GET_MSB_9(A) ((A) & 0x100) +#define GET_MSB_16(A) ((A) & 0x8000) +#define GET_MSB_17(A) ((A) & 0x10000) +#define GET_MSB_32(A) ((A) & 0x80000000) +#if M68K_USE_64_BIT +#define GET_MSB_33(A) ((A) & 0x100000000) +#endif /* M68K_USE_64_BIT */ + +/* Isolate nibbles */ +#define LOW_NIBBLE(A) ((A) & 0x0f) +#define HIGH_NIBBLE(A) ((A) & 0xf0) + +/* These are used to isolate 8, 16, and 32 bit sizes */ +#define MASK_OUT_ABOVE_2(A) ((A) & 3) +#define MASK_OUT_ABOVE_8(A) ((A) & 0xff) +#define MASK_OUT_ABOVE_16(A) ((A) & 0xffff) +#define MASK_OUT_BELOW_2(A) ((A) & ~3) +#define MASK_OUT_BELOW_8(A) ((A) & ~0xff) +#define MASK_OUT_BELOW_16(A) ((A) & ~0xffff) + +/* No need to mask if we are 32 bit */ +#if M68K_INT_GT_32_BIT || M68K_USE_64_BIT + #define MASK_OUT_ABOVE_32(A) ((A) & 0xffffffff) + #define MASK_OUT_BELOW_32(A) ((A) & ~0xffffffff) +#else + #define MASK_OUT_ABOVE_32(A) (A) + #define MASK_OUT_BELOW_32(A) 0 +#endif /* M68K_INT_GT_32_BIT || M68K_USE_64_BIT */ + +/* Simulate address lines of 68k family */ +#define ADDRESS_68K(A) ((A)&CPU_ADDRESS_MASK) + + +/* Shift & Rotate Macros. */ +#define LSL(A, C) ((A) << (C)) +#define LSR(A, C) ((A) >> (C)) + +/* Some > 32-bit optimizations */ +#if M68K_INT_GT_32_BIT + /* Shift left and right */ + #define LSR_32(A, C) ((A) >> (C)) + #define LSL_32(A, C) ((A) << (C)) +#else + /* We have to do this because the morons at ANSI decided that shifts + * by >= data size are undefined. + */ + #define LSR_32(A, C) ((C) < 32 ? (A) >> (C) : 0) + #define LSL_32(A, C) ((C) < 32 ? (A) << (C) : 0) +#endif /* M68K_INT_GT_32_BIT */ + +#if M68K_USE_64_BIT + #define LSL_32_64(A, C) ((A) << (C)) + #define LSR_32_64(A, C) ((A) >> (C)) + #define ROL_33_64(A, C) (LSL_32_64(A, C) | LSR_32_64(A, 33-(C))) + #define ROR_33_64(A, C) (LSR_32_64(A, C) | LSL_32_64(A, 33-(C))) +#endif /* M68K_USE_64_BIT */ + +#define ROL_8(A, C) MASK_OUT_ABOVE_8(LSL(A, C) | LSR(A, 8-(C))) +#define ROL_9(A, C) (LSL(A, C) | LSR(A, 9-(C))) +#define ROL_16(A, C) MASK_OUT_ABOVE_16(LSL(A, C) | LSR(A, 16-(C))) +#define ROL_17(A, C) (LSL(A, C) | LSR(A, 17-(C))) +#define ROL_32(A, C) MASK_OUT_ABOVE_32(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) MASK_OUT_ABOVE_8(LSR(A, C) | LSL(A, 8-(C))) +#define ROR_9(A, C) (LSR(A, C) | LSL(A, 9-(C))) +#define ROR_16(A, C) MASK_OUT_ABOVE_16(LSR(A, C) | LSL(A, 16-(C))) +#define ROR_17(A, C) (LSR(A, C) | LSL(A, 17-(C))) +#define ROR_32(A, C) MASK_OUT_ABOVE_32(LSR_32(A, C) | LSL_32(A, 32-(C))) +#define ROR_33(A, C) (LSR_32(A, C) | LSL_32(A, 33-(C))) + + + +/* ------------------------------ CPU Access ------------------------------ */ + +/* Access the CPU registers */ +#define CPU_TYPE m68ki_cpu.cpu_type + +#define REG_DA m68ki_cpu.dar /* easy access to data and address regs */ +#define REG_D m68ki_cpu.dar +#define REG_A (m68ki_cpu.dar+8) +#define REG_PPC m68ki_cpu.ppc +#define REG_PC m68ki_cpu.pc +#define REG_SP_BASE m68ki_cpu.sp +#define REG_USP m68ki_cpu.sp[0] +#define REG_ISP m68ki_cpu.sp[4] +#define REG_MSP m68ki_cpu.sp[6] +#define REG_SP m68ki_cpu.dar[15] +#define REG_VBR m68ki_cpu.vbr +#define REG_SFC m68ki_cpu.sfc +#define REG_DFC m68ki_cpu.dfc +#define REG_CACR m68ki_cpu.cacr +#define REG_CAAR m68ki_cpu.caar +#define REG_IR m68ki_cpu.ir + +#define FLAG_T1 m68ki_cpu.t1_flag +#define FLAG_T0 m68ki_cpu.t0_flag +#define FLAG_S m68ki_cpu.s_flag +#define FLAG_M m68ki_cpu.m_flag +#define FLAG_X m68ki_cpu.x_flag +#define FLAG_N m68ki_cpu.n_flag +#define FLAG_Z m68ki_cpu.not_z_flag +#define FLAG_V m68ki_cpu.v_flag +#define FLAG_C m68ki_cpu.c_flag +#define FLAG_INT_MASK m68ki_cpu.int_mask + +#define CPU_INT_LEVEL m68ki_cpu.int_level /* ASG: changed from CPU_INTS_PENDING */ +#define CPU_INT_CYCLES m68ki_cpu.int_cycles /* ASG */ +#define CPU_STOPPED m68ki_cpu.stopped +#define CPU_PREF_ADDR m68ki_cpu.pref_addr +#define CPU_PREF_DATA m68ki_cpu.pref_data +#define CPU_ADDRESS_MASK m68ki_cpu.address_mask +#define CPU_SR_MASK m68ki_cpu.sr_mask +#define CPU_INSTR_MODE m68ki_cpu.instr_mode +#define CPU_RUN_MODE m68ki_cpu.run_mode + +#define CYC_INSTRUCTION m68ki_cpu.cyc_instruction +#define CYC_EXCEPTION m68ki_cpu.cyc_exception +#define CYC_BCC_NOTAKE_B m68ki_cpu.cyc_bcc_notake_b +#define CYC_BCC_NOTAKE_W m68ki_cpu.cyc_bcc_notake_w +#define CYC_DBCC_F_NOEXP m68ki_cpu.cyc_dbcc_f_noexp +#define CYC_DBCC_F_EXP m68ki_cpu.cyc_dbcc_f_exp +#define CYC_SCC_R_TRUE m68ki_cpu.cyc_scc_r_true +#define CYC_MOVEM_W m68ki_cpu.cyc_movem_w +#define CYC_MOVEM_L m68ki_cpu.cyc_movem_l +#define CYC_SHIFT m68ki_cpu.cyc_shift +#define CYC_RESET m68ki_cpu.cyc_reset + + +#define CALLBACK_INT_ACK m68ki_cpu.int_ack_callback +#define CALLBACK_BKPT_ACK m68ki_cpu.bkpt_ack_callback +#define CALLBACK_RESET_INSTR m68ki_cpu.reset_instr_callback +#define CALLBACK_CMPILD_INSTR m68ki_cpu.cmpild_instr_callback +#define CALLBACK_RTE_INSTR m68ki_cpu.rte_instr_callback +#define CALLBACK_PC_CHANGED m68ki_cpu.pc_changed_callback +#define CALLBACK_SET_FC m68ki_cpu.set_fc_callback +#define CALLBACK_INSTR_HOOK m68ki_cpu.instr_hook_callback + + + +/* ----------------------------- Configuration ---------------------------- */ + +/* These defines are dependant on the configuration defines in m68kconf.h */ + +/* Disable certain comparisons if we're not using all CPU types */ +#if M68K_EMULATE_020 + #define CPU_TYPE_IS_020_PLUS(A) ((A) & CPU_TYPE_020) + #define CPU_TYPE_IS_020_LESS(A) 1 +#else + #define CPU_TYPE_IS_020_PLUS(A) 0 + #define CPU_TYPE_IS_020_LESS(A) 1 +#endif + +#if M68K_EMULATE_EC020 + #define CPU_TYPE_IS_EC020_PLUS(A) ((A) & (CPU_TYPE_EC020 | CPU_TYPE_020)) + #define CPU_TYPE_IS_EC020_LESS(A) ((A) & (CPU_TYPE_000 | CPU_TYPE_008 | CPU_TYPE_010 | CPU_TYPE_EC020)) +#else + #define CPU_TYPE_IS_EC020_PLUS(A) CPU_TYPE_IS_020_PLUS(A) + #define CPU_TYPE_IS_EC020_LESS(A) CPU_TYPE_IS_020_LESS(A) +#endif + +#if M68K_EMULATE_010 + #define CPU_TYPE_IS_010(A) ((A) == CPU_TYPE_010) + #define CPU_TYPE_IS_010_PLUS(A) ((A) & (CPU_TYPE_010 | CPU_TYPE_EC020 | CPU_TYPE_020)) + #define CPU_TYPE_IS_010_LESS(A) ((A) & (CPU_TYPE_000 | CPU_TYPE_008 | CPU_TYPE_010)) +#else + #define CPU_TYPE_IS_010(A) 0 + #define CPU_TYPE_IS_010_PLUS(A) CPU_TYPE_IS_EC020_PLUS(A) + #define CPU_TYPE_IS_010_LESS(A) CPU_TYPE_IS_EC020_LESS(A) +#endif + +#if M68K_EMULATE_020 || M68K_EMULATE_EC020 + #define CPU_TYPE_IS_020_VARIANT(A) ((A) & (CPU_TYPE_EC020 | CPU_TYPE_020)) +#else + #define CPU_TYPE_IS_020_VARIANT(A) 0 +#endif + +#if M68K_EMULATE_020 || M68K_EMULATE_EC020 || M68K_EMULATE_010 + #define CPU_TYPE_IS_000(A) ((A) == CPU_TYPE_000 || (A) == CPU_TYPE_008) +#else + #define CPU_TYPE_IS_000(A) 1 +#endif + + +#if !M68K_SEPARATE_READS +#define m68k_read_immediate_16(A) m68ki_read_program_16(A) +#define m68k_read_immediate_32(A) m68ki_read_program_32(A) + +#define m68k_read_pcrelative_8(A) m68ki_read_program_8(A) +#define m68k_read_pcrelative_16(A) m68ki_read_program_16(A) +#define m68k_read_pcrelative_32(A) m68ki_read_program_32(A) +#endif /* M68K_SEPARATE_READS */ + + +/* Enable or disable callback functions */ +#if M68K_EMULATE_INT_ACK + #if M68K_EMULATE_INT_ACK == OPT_SPECIFY_HANDLER + #define m68ki_int_ack(A) M68K_INT_ACK_CALLBACK(A) + #else + #define m68ki_int_ack(A) CALLBACK_INT_ACK(A) + #endif +#else + /* Default action is to used autovector mode, which is most common */ + #define m68ki_int_ack(A) M68K_INT_ACK_AUTOVECTOR +#endif /* M68K_EMULATE_INT_ACK */ + +#if M68K_EMULATE_BKPT_ACK + #if M68K_EMULATE_BKPT_ACK == OPT_SPECIFY_HANDLER + #define m68ki_bkpt_ack(A) M68K_BKPT_ACK_CALLBACK(A) + #else + #define m68ki_bkpt_ack(A) CALLBACK_BKPT_ACK(A) + #endif +#else + #define m68ki_bkpt_ack(A) +#endif /* M68K_EMULATE_BKPT_ACK */ + +#if M68K_EMULATE_RESET + #if M68K_EMULATE_RESET == OPT_SPECIFY_HANDLER + #define m68ki_output_reset() M68K_RESET_CALLBACK() + #else + #define m68ki_output_reset() CALLBACK_RESET_INSTR() + #endif +#else + #define m68ki_output_reset() +#endif /* M68K_EMULATE_RESET */ + +#if M68K_CMPILD_HAS_CALLBACK + #if M68K_CMPILD_HAS_CALLBACK == OPT_SPECIFY_HANDLER + #define m68ki_cmpild_callback(v,r) M68K_CMPILD_CALLBACK(v,r) + #else + #define m68ki_cmpild_callback(v,r) CALLBACK_CMPILD_INSTR(v,r) + #endif +#else + #define m68ki_cmpild_callback(v,r) +#endif /* M68K_CMPILD_HAS_CALLBACK */ + +#if M68K_RTE_HAS_CALLBACK + #if M68K_RTE_HAS_CALLBACK == OPT_SPECIFY_HANDLER + #define m68ki_rte_callback() M68K_RTE_CALLBACK() + #else + #define m68ki_rte_callback() CALLBACK_RTE_INSTR() + #endif +#else + #define m68ki_rte_callback() +#endif /* M68K_RTE_HAS_CALLBACK */ + +#if M68K_INSTRUCTION_HOOK + #if M68K_INSTRUCTION_HOOK == OPT_SPECIFY_HANDLER + #define m68ki_instr_hook() M68K_INSTRUCTION_CALLBACK() + #else + #define m68ki_instr_hook() CALLBACK_INSTR_HOOK() + #endif +#else + #define m68ki_instr_hook() +#endif /* M68K_INSTRUCTION_HOOK */ + +#if M68K_MONITOR_PC + #if M68K_MONITOR_PC == OPT_SPECIFY_HANDLER + #define m68ki_pc_changed(A) M68K_SET_PC_CALLBACK(ADDRESS_68K(A)) + #else + #define m68ki_pc_changed(A) CALLBACK_PC_CHANGED(ADDRESS_68K(A)) + #endif +#else + #define m68ki_pc_changed(A) +#endif /* M68K_MONITOR_PC */ + + +/* Enable or disable function code emulation */ +#if M68K_EMULATE_FC + #if M68K_EMULATE_FC == OPT_SPECIFY_HANDLER + #define m68ki_set_fc(A) M68K_SET_FC_CALLBACK(A) + #else + #define m68ki_set_fc(A) CALLBACK_SET_FC(A) + #endif + #define m68ki_use_data_space() m68ki_address_space = FUNCTION_CODE_USER_DATA + #define m68ki_use_program_space() m68ki_address_space = FUNCTION_CODE_USER_PROGRAM + #define m68ki_get_address_space() m68ki_address_space +#else + #define m68ki_set_fc(A) + #define m68ki_use_data_space() + #define m68ki_use_program_space() + #define m68ki_get_address_space() FUNCTION_CODE_USER_DATA +#endif /* M68K_EMULATE_FC */ + + +/* Enable or disable trace emulation */ +#if M68K_EMULATE_TRACE + /* Initiates trace checking before each instruction (t1) */ + #define m68ki_trace_t1() m68ki_tracing = FLAG_T1 + /* adds t0 to trace checking if we encounter change of flow */ + #define m68ki_trace_t0() m68ki_tracing |= FLAG_T0 + /* Clear all tracing */ + #define m68ki_clear_trace() m68ki_tracing = 0 + /* Cause a trace exception if we are tracing */ + #define m68ki_exception_if_trace() if(m68ki_tracing) m68ki_exception_trace() +#else + #define m68ki_trace_t1() + #define m68ki_trace_t0() + #define m68ki_clear_trace() + #define m68ki_exception_if_trace() +#endif /* M68K_EMULATE_TRACE */ + + + +/* Address error */ +#if M68K_EMULATE_ADDRESS_ERROR + #include + extern jmp_buf m68ki_aerr_trap; + + #define m68ki_set_address_error_trap() \ + if(setjmp(m68ki_aerr_trap) != 0) \ + { \ + m68ki_exception_address_error(); \ + if(CPU_STOPPED) \ + { \ + SET_CYCLES(0); \ + CPU_INT_CYCLES = 0; \ + return m68ki_initial_cycles; \ + } \ + } + + #define m68ki_check_address_error(ADDR, WRITE_MODE, FC) \ + if((ADDR)&1) \ + { \ + m68ki_aerr_address = ADDR; \ + m68ki_aerr_write_mode = WRITE_MODE; \ + m68ki_aerr_fc = FC; \ + longjmp(m68ki_aerr_trap, 1); \ + } +#else + #define m68ki_set_address_error_trap() + #define m68ki_check_address_error(ADDR, WRITE_MODE, FC) +#endif /* M68K_ADDRESS_ERROR */ + +/* Logging */ +#if M68K_LOG_ENABLE + #include + extern FILE* M68K_LOG_FILEHANDLE + extern const char *const m68ki_cpu_names[]; + + #define M68K_DO_LOG(A) if(M68K_LOG_FILEHANDLE) fprintf A + #if M68K_LOG_1010_1111 + #define M68K_DO_LOG_EMU(A) if(M68K_LOG_FILEHANDLE) fprintf A + #else + #define M68K_DO_LOG_EMU(A) + #endif +#else + #define M68K_DO_LOG(A) + #define M68K_DO_LOG_EMU(A) +#endif + + + +/* -------------------------- EA / Operand Access ------------------------- */ + +/* + * The general instruction format follows this pattern: + * .... XXX. .... .YYY + * where XXX is register X and YYY is register Y + */ +/* Data Register Isolation */ +#define DX (REG_D[(REG_IR >> 9) & 7]) +#define DY (REG_D[REG_IR & 7]) +/* Address Register Isolation */ +#define AX (REG_A[(REG_IR >> 9) & 7]) +#define AY (REG_A[REG_IR & 7]) + + +/* Effective Address Calculations */ +#define EA_AY_AI_8() AY /* address register indirect */ +#define EA_AY_AI_16() EA_AY_AI_8() +#define EA_AY_AI_32() EA_AY_AI_8() +#define EA_AY_PI_8() (AY++) /* postincrement (size = byte) */ +#define EA_AY_PI_16() ((AY+=2)-2) /* postincrement (size = word) */ +#define EA_AY_PI_32() ((AY+=4)-4) /* postincrement (size = long) */ +#define EA_AY_PD_8() (--AY) /* predecrement (size = byte) */ +#define EA_AY_PD_16() (AY-=2) /* predecrement (size = word) */ +#define EA_AY_PD_32() (AY-=4) /* predecrement (size = long) */ +#define EA_AY_DI_8() (AY+MAKE_INT_16(m68ki_read_imm_16())) /* displacement */ +#define EA_AY_DI_16() EA_AY_DI_8() +#define EA_AY_DI_32() EA_AY_DI_8() +#define EA_AY_IX_8() m68ki_get_ea_ix(AY) /* indirect + index */ +#define EA_AY_IX_16() EA_AY_IX_8() +#define EA_AY_IX_32() EA_AY_IX_8() + +#define EA_AX_AI_8() AX +#define EA_AX_AI_16() EA_AX_AI_8() +#define EA_AX_AI_32() EA_AX_AI_8() +#define EA_AX_PI_8() (AX++) +#define EA_AX_PI_16() ((AX+=2)-2) +#define EA_AX_PI_32() ((AX+=4)-4) +#define EA_AX_PD_8() (--AX) +#define EA_AX_PD_16() (AX-=2) +#define EA_AX_PD_32() (AX-=4) +#define EA_AX_DI_8() (AX+MAKE_INT_16(m68ki_read_imm_16())) +#define EA_AX_DI_16() EA_AX_DI_8() +#define EA_AX_DI_32() EA_AX_DI_8() +#define EA_AX_IX_8() m68ki_get_ea_ix(AX) +#define EA_AX_IX_16() EA_AX_IX_8() +#define EA_AX_IX_32() EA_AX_IX_8() + +#define EA_A7_PI_8() ((REG_A[7]+=2)-2) +#define EA_A7_PD_8() (REG_A[7]-=2) + +#define EA_AW_8() MAKE_INT_16(m68ki_read_imm_16()) /* absolute word */ +#define EA_AW_16() EA_AW_8() +#define EA_AW_32() EA_AW_8() +#define EA_AL_8() m68ki_read_imm_32() /* absolute long */ +#define EA_AL_16() EA_AL_8() +#define EA_AL_32() EA_AL_8() +#define EA_PCDI_8() m68ki_get_ea_pcdi() /* pc indirect + displacement */ +#define EA_PCDI_16() EA_PCDI_8() +#define EA_PCDI_32() EA_PCDI_8() +#define EA_PCIX_8() m68ki_get_ea_pcix() /* pc indirect + index */ +#define EA_PCIX_16() EA_PCIX_8() +#define EA_PCIX_32() EA_PCIX_8() + + +#define OPER_I_8() m68ki_read_imm_8() +#define OPER_I_16() m68ki_read_imm_16() +#define OPER_I_32() m68ki_read_imm_32() + + + +/* --------------------------- Status Register ---------------------------- */ + +/* Flag Calculation Macros */ +#define CFLAG_8(A) (A) +#define CFLAG_16(A) ((A)>>8) + +#if M68K_INT_GT_32_BIT + #define CFLAG_ADD_32(S, D, R) ((R)>>24) + #define CFLAG_SUB_32(S, D, R) ((R)>>24) +#else + #define CFLAG_ADD_32(S, D, R) (((S & D) | (~R & (S | D)))>>23) + #define CFLAG_SUB_32(S, D, R) (((S & R) | (~D & (S | R)))>>23) +#endif /* M68K_INT_GT_32_BIT */ + +#define VFLAG_ADD_8(S, D, R) ((S^R) & (D^R)) +#define VFLAG_ADD_16(S, D, R) (((S^R) & (D^R))>>8) +#define VFLAG_ADD_32(S, D, R) (((S^R) & (D^R))>>24) + +#define VFLAG_SUB_8(S, D, R) ((S^D) & (R^D)) +#define VFLAG_SUB_16(S, D, R) (((S^D) & (R^D))>>8) +#define VFLAG_SUB_32(S, D, R) (((S^D) & (R^D))>>24) + +#define NFLAG_8(A) (A) +#define NFLAG_16(A) ((A)>>8) +#define NFLAG_32(A) ((A)>>24) +#define NFLAG_64(A) ((A)>>56) + +#define ZFLAG_8(A) MASK_OUT_ABOVE_8(A) +#define ZFLAG_16(A) MASK_OUT_ABOVE_16(A) +#define ZFLAG_32(A) MASK_OUT_ABOVE_32(A) + + +/* Flag values */ +#define NFLAG_SET 0x80 +#define NFLAG_CLEAR 0 +#define CFLAG_SET 0x100 +#define CFLAG_CLEAR 0 +#define XFLAG_SET 0x100 +#define XFLAG_CLEAR 0 +#define VFLAG_SET 0x80 +#define VFLAG_CLEAR 0 +#define ZFLAG_SET 0 +#define ZFLAG_CLEAR 0xffffffff + +#define SFLAG_SET 4 +#define SFLAG_CLEAR 0 +#define MFLAG_SET 2 +#define MFLAG_CLEAR 0 + +/* Turn flag values into 1 or 0 */ +#define XFLAG_AS_1() ((FLAG_X>>8)&1) +#define NFLAG_AS_1() ((FLAG_N>>7)&1) +#define VFLAG_AS_1() ((FLAG_V>>7)&1) +#define ZFLAG_AS_1() (!FLAG_Z) +#define CFLAG_AS_1() ((FLAG_C>>8)&1) + + +/* Conditions */ +#define COND_CS() (FLAG_C&0x100) +#define COND_CC() (!COND_CS()) +#define COND_VS() (FLAG_V&0x80) +#define COND_VC() (!COND_VS()) +#define COND_NE() FLAG_Z +#define COND_EQ() (!COND_NE()) +#define COND_MI() (FLAG_N&0x80) +#define COND_PL() (!COND_MI()) +#define COND_LT() ((FLAG_N^FLAG_V)&0x80) +#define COND_GE() (!COND_LT()) +#define COND_HI() (COND_CC() && COND_NE()) +#define COND_LS() (COND_CS() || COND_EQ()) +#define COND_GT() (COND_GE() && COND_NE()) +#define COND_LE() (COND_LT() || COND_EQ()) + +/* Reversed conditions */ +#define COND_NOT_CS() COND_CC() +#define COND_NOT_CC() COND_CS() +#define COND_NOT_VS() COND_VC() +#define COND_NOT_VC() COND_VS() +#define COND_NOT_NE() COND_EQ() +#define COND_NOT_EQ() COND_NE() +#define COND_NOT_MI() COND_PL() +#define COND_NOT_PL() COND_MI() +#define COND_NOT_LT() COND_GE() +#define COND_NOT_GE() COND_LT() +#define COND_NOT_HI() COND_LS() +#define COND_NOT_LS() COND_HI() +#define COND_NOT_GT() COND_LE() +#define COND_NOT_LE() COND_GT() + +/* Not real conditions, but here for convenience */ +#define COND_XS() (FLAG_X&0x100) +#define COND_XC() (!COND_XS) + + +/* Get the condition code register */ +#define m68ki_get_ccr() ((COND_XS() >> 4) | \ + (COND_MI() >> 4) | \ + (COND_EQ() << 2) | \ + (COND_VS() >> 6) | \ + (COND_CS() >> 8)) + +/* Get the status register */ +#define m68ki_get_sr() ( FLAG_T1 | \ + FLAG_T0 | \ + (FLAG_S << 11) | \ + (FLAG_M << 11) | \ + FLAG_INT_MASK | \ + m68ki_get_ccr()) + + + +/* ---------------------------- Cycle Counting ---------------------------- */ + +#define ADD_CYCLES(A) m68ki_remaining_cycles += (A) +#define USE_CYCLES(A) m68ki_remaining_cycles -= (A) +#define SET_CYCLES(A) m68ki_remaining_cycles = A +#define GET_CYCLES() m68ki_remaining_cycles +#define USE_ALL_CYCLES() m68ki_remaining_cycles = 0 + + + +/* ----------------------------- Read / Write ----------------------------- */ + +/* Read from the current address space */ +#define m68ki_read_8(A) m68ki_read_8_fc (A, FLAG_S | m68ki_get_address_space()) +#define m68ki_read_16(A) m68ki_read_16_fc(A, FLAG_S | m68ki_get_address_space()) +#define m68ki_read_32(A) m68ki_read_32_fc(A, FLAG_S | m68ki_get_address_space()) + +/* Write to the current data space */ +#define m68ki_write_8(A, V) m68ki_write_8_fc (A, FLAG_S | FUNCTION_CODE_USER_DATA, V) +#define m68ki_write_16(A, V) m68ki_write_16_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA, V) +#define m68ki_write_32(A, V) m68ki_write_32_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA, V) + +#if M68K_SIMULATE_PD_WRITES +#define m68ki_write_32_pd(A, V) m68ki_write_32_pd_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA, V) +#else +#define m68ki_write_32_pd(A, V) m68ki_write_32_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA, V) +#endif + +/* map read immediate 8 to read immediate 16 */ +#define m68ki_read_imm_8() MASK_OUT_ABOVE_8(m68ki_read_imm_16()) + +/* Map PC-relative reads */ +#define m68ki_read_pcrel_8(A) m68k_read_pcrelative_8(A) +#define m68ki_read_pcrel_16(A) m68k_read_pcrelative_16(A) +#define m68ki_read_pcrel_32(A) m68k_read_pcrelative_32(A) + +/* Read from the program space */ +#define m68ki_read_program_8(A) m68ki_read_8_fc(A, FLAG_S | FUNCTION_CODE_USER_PROGRAM) +#define m68ki_read_program_16(A) m68ki_read_16_fc(A, FLAG_S | FUNCTION_CODE_USER_PROGRAM) +#define m68ki_read_program_32(A) m68ki_read_32_fc(A, FLAG_S | FUNCTION_CODE_USER_PROGRAM) + +/* Read from the data space */ +#define m68ki_read_data_8(A) m68ki_read_8_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA) +#define m68ki_read_data_16(A) m68ki_read_16_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA) +#define m68ki_read_data_32(A) m68ki_read_32_fc(A, FLAG_S | FUNCTION_CODE_USER_DATA) + + + +/* ======================================================================== */ +/* =============================== PROTOTYPES ============================= */ +/* ======================================================================== */ + +#include "m68kctx.h" + + +extern m68ki_cpu_core m68ki_cpu; +extern sint m68ki_remaining_cycles; +extern uint m68ki_tracing; +extern const uint8 m68ki_shift_8_table[]; +extern const uint16 m68ki_shift_16_table[]; +extern const uint m68ki_shift_32_table[]; +extern const uint8 m68ki_exception_cycle_table[][256]; +extern uint m68ki_address_space; +extern const uint8 m68ki_ea_idx_cycle_table[]; + +extern uint m68ki_aerr_address; +extern uint m68ki_aerr_write_mode; +extern uint m68ki_aerr_fc; + +/* Read data immediately after the program counter */ +INLINE uint m68ki_read_imm_16(void); +INLINE uint m68ki_read_imm_32(void); + +/* Read data with specific function code */ +INLINE uint m68ki_read_8_fc (uint address, uint fc); +INLINE uint m68ki_read_16_fc (uint address, uint fc); +INLINE uint m68ki_read_32_fc (uint address, uint fc); + +/* Write data with specific function code */ +INLINE void m68ki_write_8_fc (uint address, uint fc, uint value); +INLINE void m68ki_write_16_fc(uint address, uint fc, uint value); +INLINE void m68ki_write_32_fc(uint address, uint fc, uint value); +#if M68K_SIMULATE_PD_WRITES +INLINE void m68ki_write_32_pd_fc(uint address, uint fc, uint value); +#endif /* M68K_SIMULATE_PD_WRITES */ + +/* Indexed and PC-relative ea fetching */ +INLINE uint m68ki_get_ea_pcdi(void); +INLINE uint m68ki_get_ea_pcix(void); +INLINE uint m68ki_get_ea_ix(uint An); + +/* Operand fetching */ +INLINE uint OPER_AY_AI_8(void); +INLINE uint OPER_AY_AI_16(void); +INLINE uint OPER_AY_AI_32(void); +INLINE uint OPER_AY_PI_8(void); +INLINE uint OPER_AY_PI_16(void); +INLINE uint OPER_AY_PI_32(void); +INLINE uint OPER_AY_PD_8(void); +INLINE uint OPER_AY_PD_16(void); +INLINE uint OPER_AY_PD_32(void); +INLINE uint OPER_AY_DI_8(void); +INLINE uint OPER_AY_DI_16(void); +INLINE uint OPER_AY_DI_32(void); +INLINE uint OPER_AY_IX_8(void); +INLINE uint OPER_AY_IX_16(void); +INLINE uint OPER_AY_IX_32(void); + +INLINE uint OPER_AX_AI_8(void); +INLINE uint OPER_AX_AI_16(void); +INLINE uint OPER_AX_AI_32(void); +INLINE uint OPER_AX_PI_8(void); +INLINE uint OPER_AX_PI_16(void); +INLINE uint OPER_AX_PI_32(void); +INLINE uint OPER_AX_PD_8(void); +INLINE uint OPER_AX_PD_16(void); +INLINE uint OPER_AX_PD_32(void); +INLINE uint OPER_AX_DI_8(void); +INLINE uint OPER_AX_DI_16(void); +INLINE uint OPER_AX_DI_32(void); +INLINE uint OPER_AX_IX_8(void); +INLINE uint OPER_AX_IX_16(void); +INLINE uint OPER_AX_IX_32(void); + +INLINE uint OPER_A7_PI_8(void); +INLINE uint OPER_A7_PD_8(void); + +INLINE uint OPER_AW_8(void); +INLINE uint OPER_AW_16(void); +INLINE uint OPER_AW_32(void); +INLINE uint OPER_AL_8(void); +INLINE uint OPER_AL_16(void); +INLINE uint OPER_AL_32(void); +INLINE uint OPER_PCDI_8(void); +INLINE uint OPER_PCDI_16(void); +INLINE uint OPER_PCDI_32(void); +INLINE uint OPER_PCIX_8(void); +INLINE uint OPER_PCIX_16(void); +INLINE uint OPER_PCIX_32(void); + +/* Stack operations */ +INLINE void m68ki_push_16(uint value); +INLINE void m68ki_push_32(uint value); +INLINE uint m68ki_pull_16(void); +INLINE uint m68ki_pull_32(void); + +/* Program flow operations */ +INLINE void m68ki_jump(uint new_pc); +INLINE void m68ki_jump_vector(uint vector); +INLINE void m68ki_branch_8(uint offset); +INLINE void m68ki_branch_16(uint offset); +INLINE void m68ki_branch_32(uint offset); + +/* Status register operations. */ +INLINE void m68ki_set_s_flag(uint value); /* Only bit 2 of value should be set (i.e. 4 or 0) */ +INLINE void m68ki_set_sm_flag(uint value); /* only bits 1 and 2 of value should be set */ +INLINE void m68ki_set_ccr(uint value); /* set the condition code register */ +INLINE void m68ki_set_sr(uint value); /* set the status register */ +INLINE void m68ki_set_sr_noint(uint value); /* set the status register */ + +/* Exception processing */ +INLINE uint m68ki_init_exception(void); /* Initial exception processing */ + +INLINE void m68ki_stack_frame_3word(uint pc, uint sr); /* Stack various frame types */ +INLINE void m68ki_stack_frame_buserr(uint sr); + +INLINE void m68ki_stack_frame_0000(uint pc, uint sr, uint vector); +INLINE void m68ki_stack_frame_0001(uint pc, uint sr, uint vector); +INLINE void m68ki_stack_frame_0010(uint sr, uint vector); +INLINE void m68ki_stack_frame_1000(uint pc, uint sr, uint vector); +INLINE void m68ki_stack_frame_1010(uint sr, uint vector, uint pc); +INLINE void m68ki_stack_frame_1011(uint sr, uint vector, uint pc); + +INLINE void m68ki_exception_trap(uint vector); +INLINE void m68ki_exception_trapN(uint vector); +INLINE void m68ki_exception_trace(void); +INLINE void m68ki_exception_privilege_violation(void); +INLINE void m68ki_exception_1010(void); +INLINE void m68ki_exception_1111(void); +INLINE void m68ki_exception_illegal(void); +INLINE void m68ki_exception_format_error(void); +INLINE void m68ki_exception_address_error(void); +INLINE void m68ki_exception_interrupt(uint int_level); +INLINE void m68ki_check_interrupts(void); /* ASG: check for interrupts */ + +/* quick disassembly (used for logging) */ +char* m68ki_disassemble_quick(unsigned int pc, unsigned int cpu_type); + + +/* ======================================================================== */ +/* =========================== UTILITY FUNCTIONS ========================== */ +/* ======================================================================== */ + + +/* ---------------------------- Read Immediate ---------------------------- */ + +/* Handles all immediate reads, does address error check, function code setting, + * and prefetching if they are enabled in m68kconf.h + */ +INLINE uint m68ki_read_imm_16(void) +{ + m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(REG_PC, MODE_READ, FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ +#if M68K_EMULATE_PREFETCH + if(MASK_OUT_BELOW_2(REG_PC) != CPU_PREF_ADDR) + { + CPU_PREF_ADDR = MASK_OUT_BELOW_2(REG_PC); + CPU_PREF_DATA = m68k_read_immediate_32(ADDRESS_68K(CPU_PREF_ADDR)); + } + REG_PC += 2; + return MASK_OUT_ABOVE_16(CPU_PREF_DATA >> ((2-((REG_PC-2)&2))<<3)); +#else + REG_PC += 2; + return m68k_read_immediate_16(ADDRESS_68K(REG_PC-2)); +#endif /* M68K_EMULATE_PREFETCH */ +} +INLINE uint m68ki_read_imm_32(void) +{ +#if M68K_EMULATE_PREFETCH + uint temp_val; + + m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(REG_PC, MODE_READ, FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + if(MASK_OUT_BELOW_2(REG_PC) != CPU_PREF_ADDR) + { + CPU_PREF_ADDR = MASK_OUT_BELOW_2(REG_PC); + CPU_PREF_DATA = m68k_read_immediate_32(ADDRESS_68K(CPU_PREF_ADDR)); + } + temp_val = CPU_PREF_DATA; + REG_PC += 2; + if(MASK_OUT_BELOW_2(REG_PC) != CPU_PREF_ADDR) + { + CPU_PREF_ADDR = MASK_OUT_BELOW_2(REG_PC); + CPU_PREF_DATA = m68k_read_immediate_32(ADDRESS_68K(CPU_PREF_ADDR)); + temp_val = MASK_OUT_ABOVE_32((temp_val << 16) | (CPU_PREF_DATA >> 16)); + } + REG_PC += 2; + + return temp_val; +#else + m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(REG_PC, MODE_READ, FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + REG_PC += 4; + return m68k_read_immediate_32(ADDRESS_68K(REG_PC-4)); +#endif /* M68K_EMULATE_PREFETCH */ +} + + + +/* ------------------------- Top level read/write ------------------------- */ + +/* Handles all memory accesses (except for immediate reads if they are + * configured to use separate functions in m68kconf.h). + * All memory accesses must go through these top level functions. + * These functions will also check for address error and set the function + * code if they are enabled in m68kconf.h. + */ +INLINE uint m68ki_read_8_fc(uint address, uint fc) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + return m68k_read_memory_8(ADDRESS_68K(address)); +} +INLINE uint m68ki_read_16_fc(uint address, uint fc) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(address, MODE_READ, fc); /* auto-disable (see m68kcpu.h) */ + return m68k_read_memory_16(ADDRESS_68K(address)); +} +INLINE uint m68ki_read_32_fc(uint address, uint fc) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(address, MODE_READ, fc); /* auto-disable (see m68kcpu.h) */ + return m68k_read_memory_32(ADDRESS_68K(address)); +} + +INLINE void m68ki_write_8_fc(uint address, uint fc, uint value) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68k_write_memory_8(ADDRESS_68K(address), value); +} +INLINE void m68ki_write_16_fc(uint address, uint fc, uint value) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ + m68k_write_memory_16(ADDRESS_68K(address), value); +} +INLINE void m68ki_write_32_fc(uint address, uint fc, uint value) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ + m68k_write_memory_32(ADDRESS_68K(address), value); +} + +#if M68K_SIMULATE_PD_WRITES +INLINE void m68ki_write_32_pd_fc(uint address, uint fc, uint value) +{ + m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_check_address_error(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ + m68k_write_memory_32_pd(ADDRESS_68K(address), value); +} +#endif + + +/* --------------------- Effective Address Calculation -------------------- */ + +/* The program counter relative addressing modes cause operands to be + * retrieved from program space, not data space. + */ +INLINE uint m68ki_get_ea_pcdi(void) +{ + uint old_pc = REG_PC; + m68ki_use_program_space(); /* auto-disable */ + return old_pc + MAKE_INT_16(m68ki_read_imm_16()); +} + + +INLINE uint m68ki_get_ea_pcix(void) +{ + m68ki_use_program_space(); /* auto-disable */ + return m68ki_get_ea_ix(REG_PC); +} + +/* Indexed addressing modes are encoded as follows: + * + * Base instruction format: + * F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 + * x x x x x x x x x x | 1 1 0 | BASE REGISTER (An) + * + * Base instruction format for destination EA in move instructions: + * F E D C | B A 9 | 8 7 6 | 5 4 3 2 1 0 + * x x x x | BASE REG | 1 1 0 | X X X X X X (An) + * + * Brief extension format: + * F | E D C | B | A 9 | 8 | 7 6 5 4 3 2 1 0 + * D/A | REGISTER | W/L | SCALE | 0 | DISPLACEMENT + * + * Full extension format: + * F E D C B A 9 8 7 6 5 4 3 2 1 0 + * D/A | REGISTER | W/L | SCALE | 1 | BS | IS | BD SIZE | 0 | I/IS + * BASE DISPLACEMENT (0, 16, 32 bit) (bd) + * OUTER DISPLACEMENT (0, 16, 32 bit) (od) + * + * D/A: 0 = Dn, 1 = An (Xn) + * W/L: 0 = W (sign extend), 1 = L (.SIZE) + * SCALE: 00=1, 01=2, 10=4, 11=8 (*SCALE) + * BS: 0=add base reg, 1=suppress base reg (An suppressed) + * IS: 0=add index, 1=suppress index (Xn suppressed) + * BD SIZE: 00=reserved, 01=NULL, 10=Word, 11=Long (size of bd) + * + * IS I/IS Operation + * 0 000 No Memory Indirect + * 0 001 indir prex with null outer + * 0 010 indir prex with word outer + * 0 011 indir prex with long outer + * 0 100 reserved + * 0 101 indir postx with null outer + * 0 110 indir postx with word outer + * 0 111 indir postx with long outer + * 1 000 no memory indirect + * 1 001 mem indir with null outer + * 1 010 mem indir with word outer + * 1 011 mem indir with long outer + * 1 100-111 reserved + */ +INLINE uint m68ki_get_ea_ix(uint An) +{ + /* An = base register */ + uint extension = m68ki_read_imm_16(); + uint Xn = 0; /* Index register */ + uint bd = 0; /* Base Displacement */ + uint od = 0; /* Outer Displacement */ + + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) + { + /* Calculate index */ + Xn = REG_DA[extension>>12]; /* Xn */ + if(!BIT_B(extension)) /* W/L */ + Xn = MAKE_INT_16(Xn); + + /* Add base register and displacement and return */ + return An + Xn + MAKE_INT_8(extension); + } + + /* Brief extension format */ + if(!BIT_8(extension)) + { + /* Calculate index */ + Xn = REG_DA[extension>>12]; /* Xn */ + if(!BIT_B(extension)) /* W/L */ + Xn = MAKE_INT_16(Xn); + /* Add scale if proper CPU type */ + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + Xn <<= (extension>>9) & 3; /* SCALE */ + + /* Add base register and displacement and return */ + return An + Xn + MAKE_INT_8(extension); + } + + /* Full extension format */ + + USE_CYCLES(m68ki_ea_idx_cycle_table[extension&0x3f]); + + /* Check if base register is present */ + if(BIT_7(extension)) /* BS */ + An = 0; /* An */ + + /* Check if index is present */ + if(!BIT_6(extension)) /* IS */ + { + Xn = REG_DA[extension>>12]; /* Xn */ + if(!BIT_B(extension)) /* W/L */ + Xn = MAKE_INT_16(Xn); + Xn <<= (extension>>9) & 3; /* SCALE */ + } + + /* Check if base displacement is present */ + if(BIT_5(extension)) /* BD SIZE */ + bd = BIT_4(extension) ? m68ki_read_imm_32() : (uint32)MAKE_INT_16(m68ki_read_imm_16()); + + /* If no indirect action, we are done */ + if(!(extension&7)) /* No Memory Indirect */ + return An + bd + Xn; + + /* Check if outer displacement is present */ + if(BIT_1(extension)) /* I/IS: od */ + od = BIT_0(extension) ? m68ki_read_imm_32() : (uint32)MAKE_INT_16(m68ki_read_imm_16()); + + /* Postindex */ + if(BIT_2(extension)) /* I/IS: 0 = preindex, 1 = postindex */ + return m68ki_read_32(An + bd) + Xn + od; + + /* Preindex */ + return m68ki_read_32(An + bd + Xn) + od; +} + + +/* Fetch operands */ +INLINE uint OPER_AY_AI_8(void) {uint ea = EA_AY_AI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AY_AI_16(void) {uint ea = EA_AY_AI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AY_AI_32(void) {uint ea = EA_AY_AI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AY_PI_8(void) {uint ea = EA_AY_PI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AY_PI_16(void) {uint ea = EA_AY_PI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AY_PI_32(void) {uint ea = EA_AY_PI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AY_PD_8(void) {uint ea = EA_AY_PD_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AY_PD_16(void) {uint ea = EA_AY_PD_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AY_PD_32(void) {uint ea = EA_AY_PD_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AY_DI_8(void) {uint ea = EA_AY_DI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AY_DI_16(void) {uint ea = EA_AY_DI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AY_DI_32(void) {uint ea = EA_AY_DI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AY_IX_8(void) {uint ea = EA_AY_IX_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AY_IX_16(void) {uint ea = EA_AY_IX_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AY_IX_32(void) {uint ea = EA_AY_IX_32(); return m68ki_read_32(ea);} + +INLINE uint OPER_AX_AI_8(void) {uint ea = EA_AX_AI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AX_AI_16(void) {uint ea = EA_AX_AI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AX_AI_32(void) {uint ea = EA_AX_AI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AX_PI_8(void) {uint ea = EA_AX_PI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AX_PI_16(void) {uint ea = EA_AX_PI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AX_PI_32(void) {uint ea = EA_AX_PI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AX_PD_8(void) {uint ea = EA_AX_PD_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AX_PD_16(void) {uint ea = EA_AX_PD_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AX_PD_32(void) {uint ea = EA_AX_PD_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AX_DI_8(void) {uint ea = EA_AX_DI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AX_DI_16(void) {uint ea = EA_AX_DI_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AX_DI_32(void) {uint ea = EA_AX_DI_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AX_IX_8(void) {uint ea = EA_AX_IX_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AX_IX_16(void) {uint ea = EA_AX_IX_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AX_IX_32(void) {uint ea = EA_AX_IX_32(); return m68ki_read_32(ea);} + +INLINE uint OPER_A7_PI_8(void) {uint ea = EA_A7_PI_8(); return m68ki_read_8(ea); } +INLINE uint OPER_A7_PD_8(void) {uint ea = EA_A7_PD_8(); return m68ki_read_8(ea); } + +INLINE uint OPER_AW_8(void) {uint ea = EA_AW_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AW_16(void) {uint ea = EA_AW_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AW_32(void) {uint ea = EA_AW_32(); return m68ki_read_32(ea);} +INLINE uint OPER_AL_8(void) {uint ea = EA_AL_8(); return m68ki_read_8(ea); } +INLINE uint OPER_AL_16(void) {uint ea = EA_AL_16(); return m68ki_read_16(ea);} +INLINE uint OPER_AL_32(void) {uint ea = EA_AL_32(); return m68ki_read_32(ea);} +INLINE uint OPER_PCDI_8(void) {uint ea = EA_PCDI_8(); return m68ki_read_pcrel_8(ea); } +INLINE uint OPER_PCDI_16(void) {uint ea = EA_PCDI_16(); return m68ki_read_pcrel_16(ea);} +INLINE uint OPER_PCDI_32(void) {uint ea = EA_PCDI_32(); return m68ki_read_pcrel_32(ea);} +INLINE uint OPER_PCIX_8(void) {uint ea = EA_PCIX_8(); return m68ki_read_pcrel_8(ea); } +INLINE uint OPER_PCIX_16(void) {uint ea = EA_PCIX_16(); return m68ki_read_pcrel_16(ea);} +INLINE uint OPER_PCIX_32(void) {uint ea = EA_PCIX_32(); return m68ki_read_pcrel_32(ea);} + + + +/* ---------------------------- Stack Functions --------------------------- */ + +/* Push/pull data from the stack */ +INLINE void m68ki_push_16(uint value) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP - 2); + m68ki_write_16(REG_SP, value); +} + +INLINE void m68ki_push_32(uint value) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP - 4); + m68ki_write_32(REG_SP, value); +} + +INLINE uint m68ki_pull_16(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP + 2); + return m68ki_read_16(REG_SP-2); +} + +INLINE uint m68ki_pull_32(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP + 4); + return m68ki_read_32(REG_SP-4); +} + + +/* Increment/decrement the stack as if doing a push/pull but + * don't do any memory access. + */ +INLINE void m68ki_fake_push_16(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP - 2); +} + +INLINE void m68ki_fake_push_32(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP - 4); +} + +INLINE void m68ki_fake_pull_16(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP + 2); +} + +INLINE void m68ki_fake_pull_32(void) +{ + REG_SP = MASK_OUT_ABOVE_32(REG_SP + 4); +} + + +/* ----------------------------- Program Flow ----------------------------- */ + +/* Jump to a new program location or vector. + * These functions will also call the pc_changed callback if it was enabled + * in m68kconf.h. + */ +INLINE void m68ki_jump(uint new_pc) +{ + REG_PC = new_pc; + m68ki_pc_changed(REG_PC); +} + +INLINE void m68ki_jump_vector(uint vector) +{ + REG_PC = (vector<<2) + REG_VBR; + REG_PC = m68ki_read_data_32(REG_PC); + m68ki_pc_changed(REG_PC); +} + + +/* Branch to a new memory location. + * The 32-bit branch will call pc_changed if it was enabled in m68kconf.h. + * So far I've found no problems with not calling pc_changed for 8 or 16 + * bit branches. + */ +INLINE void m68ki_branch_8(uint offset) +{ + REG_PC += MAKE_INT_8(offset); +} + +INLINE void m68ki_branch_16(uint offset) +{ + REG_PC += MAKE_INT_16(offset); +} + +INLINE void m68ki_branch_32(uint offset) +{ + REG_PC += offset; + m68ki_pc_changed(REG_PC); +} + + + +/* ---------------------------- Status Register --------------------------- */ + +/* Set the S flag and change the active stack pointer. + * Note that value MUST be 4 or 0. + */ +INLINE void m68ki_set_s_flag(uint value) +{ + /* Backup the old stack pointer */ + REG_SP_BASE[FLAG_S | ((FLAG_S>>1) & FLAG_M)] = REG_SP; + /* Set the S flag */ + FLAG_S = value; + /* Set the new stack pointer */ + REG_SP = REG_SP_BASE[FLAG_S | ((FLAG_S>>1) & FLAG_M)]; +} + +/* Set the S and M flags and change the active stack pointer. + * Note that value MUST be 0, 2, 4, or 6 (bit2 = S, bit1 = M). + */ +INLINE void m68ki_set_sm_flag(uint value) +{ + /* Backup the old stack pointer */ + REG_SP_BASE[FLAG_S | ((FLAG_S>>1) & FLAG_M)] = REG_SP; + /* Set the S and M flags */ + FLAG_S = value & SFLAG_SET; + FLAG_M = value & MFLAG_SET; + /* Set the new stack pointer */ + REG_SP = REG_SP_BASE[FLAG_S | ((FLAG_S>>1) & FLAG_M)]; +} + +/* Set the S and M flags. Don't touch the stack pointer. */ +INLINE void m68ki_set_sm_flag_nosp(uint value) +{ + /* Set the S and M flags */ + FLAG_S = value & SFLAG_SET; + FLAG_M = value & MFLAG_SET; +} + + +/* Set the condition code register */ +INLINE void m68ki_set_ccr(uint value) +{ + FLAG_X = BIT_4(value) << 4; + FLAG_N = BIT_3(value) << 4; + FLAG_Z = !BIT_2(value); + FLAG_V = BIT_1(value) << 6; + FLAG_C = BIT_0(value) << 8; +} + +/* Set the status register but don't check for interrupts */ +INLINE void m68ki_set_sr_noint(uint value) +{ + /* Mask out the "unimplemented" bits */ + value &= CPU_SR_MASK; + + /* Now set the status register */ + FLAG_T1 = BIT_F(value); + FLAG_T0 = BIT_E(value); + FLAG_INT_MASK = value & 0x0700; + m68ki_set_ccr(value); + m68ki_set_sm_flag((value >> 11) & 6); +} + +/* Set the status register but don't check for interrupts nor + * change the stack pointer + */ +INLINE void m68ki_set_sr_noint_nosp(uint value) +{ + /* Mask out the "unimplemented" bits */ + value &= CPU_SR_MASK; + + /* Now set the status register */ + FLAG_T1 = BIT_F(value); + FLAG_T0 = BIT_E(value); + FLAG_INT_MASK = value & 0x0700; + m68ki_set_ccr(value); + m68ki_set_sm_flag_nosp((value >> 11) & 6); +} + +/* Set the status register and check for interrupts */ +INLINE void m68ki_set_sr(uint value) +{ + m68ki_set_sr_noint(value); + m68ki_check_interrupts(); +} + + +/* ------------------------- Exception Processing ------------------------- */ + +/* Initiate exception processing */ +INLINE uint m68ki_init_exception(void) +{ + /* Save the old status register */ + uint sr = m68ki_get_sr(); + + /* Turn off trace flag, clear pending traces */ + FLAG_T1 = FLAG_T0 = 0; + m68ki_clear_trace(); + /* Enter supervisor mode */ + m68ki_set_s_flag(SFLAG_SET); + + return sr; +} + +/* 3 word stack frame (68000 only) */ +INLINE void m68ki_stack_frame_3word(uint pc, uint sr) +{ + m68ki_push_32(pc); + m68ki_push_16(sr); +} + +/* Format 0 stack frame. + * This is the standard stack frame for 68010+. + */ +INLINE void m68ki_stack_frame_0000(uint pc, uint sr, uint vector) +{ + /* Stack a 3-word frame if we are 68000 */ + if(CPU_TYPE == CPU_TYPE_000 || CPU_TYPE == CPU_TYPE_008) + { + m68ki_stack_frame_3word(pc, sr); + return; + } + m68ki_push_16(vector<<2); + m68ki_push_32(pc); + m68ki_push_16(sr); +} + +/* Format 1 stack frame (68020). + * For 68020, this is the 4 word throwaway frame. + */ +INLINE void m68ki_stack_frame_0001(uint pc, uint sr, uint vector) +{ + m68ki_push_16(0x1000 | (vector<<2)); + m68ki_push_32(pc); + m68ki_push_16(sr); +} + +/* Format 2 stack frame. + * This is used only by 68020 for trap exceptions. + */ +INLINE void m68ki_stack_frame_0010(uint sr, uint vector) +{ + m68ki_push_32(REG_PPC); + m68ki_push_16(0x2000 | (vector<<2)); + m68ki_push_32(REG_PC); + m68ki_push_16(sr); +} + + +/* Bus error stack frame (68000 only). + */ +INLINE void m68ki_stack_frame_buserr(uint sr) +{ + m68ki_push_32(REG_PC); + m68ki_push_16(sr); + m68ki_push_16(REG_IR); + m68ki_push_32(m68ki_aerr_address); /* access address */ + /* 0 0 0 0 0 0 0 0 0 0 0 R/W I/N FC + * R/W 0 = write, 1 = read + * I/N 0 = instruction, 1 = not + * FC 3-bit function code + */ + m68ki_push_16(m68ki_aerr_write_mode | CPU_INSTR_MODE | m68ki_aerr_fc); +} + +/* Format 8 stack frame (68010). + * 68010 only. This is the 29 word bus/address error frame. + */ +static inline void m68ki_stack_frame_1000(uint pc, uint sr, uint vector) +{ + /* VERSION + * NUMBER + * INTERNAL INFORMATION, 16 WORDS + */ + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + m68ki_fake_push_32(); + + /* INSTRUCTION INPUT BUFFER */ + m68ki_push_16(0); + + /* UNUSED, RESERVED (not written) */ + m68ki_fake_push_16(); + + /* DATA INPUT BUFFER */ + m68ki_push_16(0); + + /* UNUSED, RESERVED (not written) */ + m68ki_fake_push_16(); + + /* DATA OUTPUT BUFFER */ + m68ki_push_16(0); + + /* UNUSED, RESERVED (not written) */ + m68ki_fake_push_16(); + + /* FAULT ADDRESS */ + m68ki_push_32(0); + + /* SPECIAL STATUS WORD */ + m68ki_push_16(0); + + /* 1000, VECTOR OFFSET */ + m68ki_push_16(0x8000 | (vector<<2)); + + /* PROGRAM COUNTER */ + m68ki_push_32(pc); + + /* STATUS REGISTER */ + m68ki_push_16(sr); +} + +/* Format A stack frame (short bus fault). + * This is used only by 68020 for bus fault and address error + * if the error happens at an instruction boundary. + * PC stacked is address of next instruction. + */ +static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc) +{ + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* DATA OUTPUT BUFFER (2 words) */ + m68ki_push_32(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* DATA CYCLE FAULT ADDRESS (2 words) */ + m68ki_push_32(0); + + /* INSTRUCTION PIPE STAGE B */ + m68ki_push_16(0); + + /* INSTRUCTION PIPE STAGE C */ + m68ki_push_16(0); + + /* SPECIAL STATUS REGISTER */ + m68ki_push_16(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* 1010, VECTOR OFFSET */ + m68ki_push_16(0xa000 | (vector<<2)); + + /* PROGRAM COUNTER */ + m68ki_push_32(pc); + + /* STATUS REGISTER */ + m68ki_push_16(sr); +} + +/* Format B stack frame (long bus fault). + * This is used only by 68020 for bus fault and address error + * if the error happens during instruction execution. + * PC stacked is address of instruction in progress. + */ +static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc) +{ + /* INTERNAL REGISTERS (18 words) */ + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + + /* VERSION# (4 bits), INTERNAL INFORMATION */ + m68ki_push_16(0); + + /* INTERNAL REGISTERS (3 words) */ + m68ki_push_32(0); + m68ki_push_16(0); + + /* DATA INTPUT BUFFER (2 words) */ + m68ki_push_32(0); + + /* INTERNAL REGISTERS (2 words) */ + m68ki_push_32(0); + + /* STAGE B ADDRESS (2 words) */ + m68ki_push_32(0); + + /* INTERNAL REGISTER (4 words) */ + m68ki_push_32(0); + m68ki_push_32(0); + + /* DATA OUTPUT BUFFER (2 words) */ + m68ki_push_32(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* DATA CYCLE FAULT ADDRESS (2 words) */ + m68ki_push_32(0); + + /* INSTRUCTION PIPE STAGE B */ + m68ki_push_16(0); + + /* INSTRUCTION PIPE STAGE C */ + m68ki_push_16(0); + + /* SPECIAL STATUS REGISTER */ + m68ki_push_16(0); + + /* INTERNAL REGISTER */ + m68ki_push_16(0); + + /* 1011, VECTOR OFFSET */ + m68ki_push_16(0xb000 | (vector<<2)); + + /* PROGRAM COUNTER */ + m68ki_push_32(pc); + + /* STATUS REGISTER */ + m68ki_push_16(sr); +} + + +/* Used for Group 2 exceptions. + * These stack a type 2 frame on the 020. + */ +INLINE void m68ki_exception_trap(uint vector) +{ + uint sr = m68ki_init_exception(); + + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) + m68ki_stack_frame_0000(REG_PC, sr, vector); + else + m68ki_stack_frame_0010(sr, vector); + + m68ki_jump_vector(vector); + + /* Use up some clock cycles */ + USE_CYCLES(CYC_EXCEPTION[vector]); +} + +/* Trap#n stacks a 0 frame but behaves like group2 otherwise */ +INLINE void m68ki_exception_trapN(uint vector) +{ + uint sr = m68ki_init_exception(); + m68ki_stack_frame_0000(REG_PC, sr, vector); + m68ki_jump_vector(vector); + + /* Use up some clock cycles */ + USE_CYCLES(CYC_EXCEPTION[vector]); +} + +/* Exception for trace mode */ +INLINE void m68ki_exception_trace(void) +{ + uint sr = m68ki_init_exception(); + + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) + { + #if M68K_EMULATE_ADDRESS_ERROR == OPT_ON + if(CPU_TYPE_IS_000(CPU_TYPE)) + { + CPU_INSTR_MODE = INSTRUCTION_NO; + } + #endif /* M68K_EMULATE_ADDRESS_ERROR */ + m68ki_stack_frame_0000(REG_PC, sr, EXCEPTION_TRACE); + } + else + m68ki_stack_frame_0010(sr, EXCEPTION_TRACE); + + m68ki_jump_vector(EXCEPTION_TRACE); + + /* Trace nullifies a STOP instruction */ + CPU_STOPPED &= ~STOP_LEVEL_STOP; + + /* Use up some clock cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_TRACE]); +} + +/* Exception for privilege violation */ +INLINE void m68ki_exception_privilege_violation(void) +{ + uint sr = m68ki_init_exception(); + + #if M68K_EMULATE_ADDRESS_ERROR == OPT_ON + if(CPU_TYPE_IS_000(CPU_TYPE)) + { + CPU_INSTR_MODE = INSTRUCTION_NO; + } + #endif /* M68K_EMULATE_ADDRESS_ERROR */ + + m68ki_stack_frame_0000(REG_PPC, sr, EXCEPTION_PRIVILEGE_VIOLATION); + m68ki_jump_vector(EXCEPTION_PRIVILEGE_VIOLATION); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_PRIVILEGE_VIOLATION] - CYC_INSTRUCTION[REG_IR]); +} + +/* Exception for A-Line instructions */ +INLINE void m68ki_exception_1010(void) +{ + uint sr; +#if M68K_LOG_1010_1111 == OPT_ON + M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: called 1010 instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); +#endif + + sr = m68ki_init_exception(); + m68ki_stack_frame_0000(REG_PPC, sr, EXCEPTION_1010); + m68ki_jump_vector(EXCEPTION_1010); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_1010] - CYC_INSTRUCTION[REG_IR]); +} + +/* Exception for F-Line instructions */ +INLINE void m68ki_exception_1111(void) +{ + uint sr; + +#if M68K_LOG_1010_1111 == OPT_ON + M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: called 1111 instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); +#endif + + sr = m68ki_init_exception(); + m68ki_stack_frame_0000(REG_PPC, sr, EXCEPTION_1111); + m68ki_jump_vector(EXCEPTION_1111); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_1111] - CYC_INSTRUCTION[REG_IR]); +} + +/* Exception for illegal instructions */ +INLINE void m68ki_exception_illegal(void) +{ + uint sr; + + M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: illegal instruction %04x (%s)\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); + + sr = m68ki_init_exception(); + + #if M68K_EMULATE_ADDRESS_ERROR == OPT_ON + if(CPU_TYPE_IS_000(CPU_TYPE)) + { + CPU_INSTR_MODE = INSTRUCTION_NO; + } + #endif /* M68K_EMULATE_ADDRESS_ERROR */ + + m68ki_stack_frame_0000(REG_PPC, sr, EXCEPTION_ILLEGAL_INSTRUCTION); + m68ki_jump_vector(EXCEPTION_ILLEGAL_INSTRUCTION); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_ILLEGAL_INSTRUCTION] - CYC_INSTRUCTION[REG_IR]); +} + +/* Exception for format errror in RTE */ +INLINE void m68ki_exception_format_error(void) +{ + uint sr = m68ki_init_exception(); + m68ki_stack_frame_0000(REG_PC, sr, EXCEPTION_FORMAT_ERROR); + m68ki_jump_vector(EXCEPTION_FORMAT_ERROR); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_FORMAT_ERROR] - CYC_INSTRUCTION[REG_IR]); +} + +/* Exception for address error */ +INLINE void m68ki_exception_address_error(void) +{ + uint sr = m68ki_init_exception(); + + /* If we were processing a bus error, address error, or reset, + * while writing the stack frame, this is a catastrophic failure. + * Halt the CPU + */ + if(CPU_RUN_MODE == RUN_MODE_BERR_AERR_RESET) + { +m68k_read_memory_8(0x00ffff01); + CPU_STOPPED = STOP_LEVEL_HALT; + return; + } + CPU_RUN_MODE = RUN_MODE_BERR_AERR_RESET; + + /* Note: This is implemented for 68000 only! */ + m68ki_stack_frame_buserr(sr); + + m68ki_jump_vector(EXCEPTION_ADDRESS_ERROR); + + /* Use up some clock cycles and undo the instruction's cycles */ + USE_CYCLES(CYC_EXCEPTION[EXCEPTION_ADDRESS_ERROR] - CYC_INSTRUCTION[REG_IR]); +} + + +/* Service an interrupt request and start exception processing */ +static inline void m68ki_exception_interrupt(uint int_level) +{ + uint vector; + uint sr; + uint new_pc; + + #if M68K_EMULATE_ADDRESS_ERROR == OPT_ON + if(CPU_TYPE_IS_000(CPU_TYPE)) + { + CPU_INSTR_MODE = INSTRUCTION_NO; + } + #endif /* M68K_EMULATE_ADDRESS_ERROR */ + + /* Turn off the stopped state */ + CPU_STOPPED &= ~STOP_LEVEL_STOP; + + /* If we are halted, don't do anything */ + if(CPU_STOPPED) + return; + + /* Acknowledge the interrupt */ + vector = m68ki_int_ack(int_level); + + /* Get the interrupt vector */ + if(vector == M68K_INT_ACK_AUTOVECTOR) + /* Use the autovectors. This is the most commonly used implementation */ + vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level; + else if(vector == M68K_INT_ACK_SPURIOUS) + /* Called if no devices respond to the interrupt acknowledge */ + vector = EXCEPTION_SPURIOUS_INTERRUPT; + else if(vector > 255) + { + M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n", + m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector)); + return; + } + + /* Start exception processing */ + sr = m68ki_init_exception(); + + /* Set the interrupt mask to the level of the one being serviced */ + FLAG_INT_MASK = int_level<<8; + + /* Get the new PC */ + new_pc = m68ki_read_data_32((vector<<2) + REG_VBR); + + /* If vector is uninitialized, call the uninitialized interrupt vector */ + if(new_pc == 0) + new_pc = m68ki_read_data_32((EXCEPTION_UNINITIALIZED_INTERRUPT<<2) + REG_VBR); + + /* Generate a stack frame */ + m68ki_stack_frame_0000(REG_PC, sr, vector); + if(FLAG_M && CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + /* Create throwaway frame */ + m68ki_set_sm_flag(FLAG_S); /* clear M */ + sr |= 0x2000; /* Same as SR in master stack frame except S is forced high */ + m68ki_stack_frame_0001(REG_PC, sr, vector); + } + + m68ki_jump(new_pc); + + /* Defer cycle counting until later */ + CPU_INT_CYCLES += CYC_EXCEPTION[vector]; + +#if !M68K_EMULATE_INT_ACK + /* Automatically clear IRQ if we are not using an acknowledge scheme */ + CPU_INT_LEVEL = 0; +#endif /* M68K_EMULATE_INT_ACK */ +} + + +/* ASG: Check for interrupts */ +INLINE void m68ki_check_interrupts(void) +{ + if(CPU_INT_LEVEL > FLAG_INT_MASK) + { + m68ki_exception_interrupt(CPU_INT_LEVEL>>8); + } +} + + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ + +#endif /* M68KCPU__HEADER */ diff --git a/Src/CPU/68K/Musashi/m68kctx.h b/Src/CPU/68K/Musashi/m68kctx.h new file mode 100644 index 0000000..1b114d2 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kctx.h @@ -0,0 +1,87 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kctx.h + * + * Musashi CPU context. This was made a separate file to more easily facilitate + * save state management. It is used internally by m68kcpu.h. + */ + +#include "Types.h" // Supermodel types + +typedef struct +{ + UINT32 cpu_type; /* CPU Type: 68000, 68008, 68010, 68EC020, or 68020 */ + UINT32 dar[16]; /* Data and Address Registers */ + UINT32 ppc; /* Previous program counter */ + UINT32 pc; /* Program Counter */ + UINT32 sp[7]; /* User, Interrupt, and Master Stack Pointers */ + UINT32 vbr; /* Vector Base Register (m68010+) */ + UINT32 sfc; /* Source Function Code Register (m68010+) */ + UINT32 dfc; /* Destination Function Code Register (m68010+) */ + UINT32 cacr; /* Cache Control Register (m68020, unemulated) */ + UINT32 caar; /* Cache Address Register (m68020, unemulated) */ + UINT32 ir; /* Instruction Register */ + UINT32 t1_flag; /* Trace 1 */ + UINT32 t0_flag; /* Trace 0 */ + UINT32 s_flag; /* Supervisor */ + UINT32 m_flag; /* Master/Interrupt state */ + UINT32 x_flag; /* Extend */ + UINT32 n_flag; /* Negative */ + UINT32 not_z_flag; /* Zero, inverted for speedups */ + UINT32 v_flag; /* Overflow */ + UINT32 c_flag; /* Carry */ + UINT32 int_mask; /* I0-I2 */ + UINT32 int_level; /* State of interrupt pins IPL0-IPL2 -- ASG: changed from ints_pending */ + UINT32 int_cycles; /* ASG: extra cycles from generated interrupts */ + UINT32 stopped; /* Stopped state */ + UINT32 pref_addr; /* Last prefetch address */ + UINT32 pref_data; /* Data in the prefetch queue */ + UINT32 address_mask; /* Available address pins */ + UINT32 sr_mask; /* Implemented status register bits */ + UINT32 instr_mode; /* Stores whether we are in instruction mode or group 0/1 exception mode */ + UINT32 run_mode; /* Stores whether we are processing a reset, bus error, address error, or something else */ + + /* Clocks required for instructions / exceptions */ + UINT32 cyc_bcc_notake_b; + UINT32 cyc_bcc_notake_w; + UINT32 cyc_dbcc_f_noexp; + UINT32 cyc_dbcc_f_exp; + UINT32 cyc_scc_r_true; + UINT32 cyc_movem_w; + UINT32 cyc_movem_l; + UINT32 cyc_shift; + UINT32 cyc_reset; + UINT8* cyc_instruction; + UINT8* cyc_exception; + + /* Callbacks to host */ + int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */ + void (*bkpt_ack_callback)(unsigned int data); /* Breakpoint Acknowledge */ + void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */ + void (*cmpild_instr_callback)(unsigned int, int); /* Called when a CMPI.L #v, Dn instruction is encountered */ + void (*rte_instr_callback)(void); /* Called when a RTE instruction is encountered */ + void (*pc_changed_callback)(unsigned int new_pc); /* Called when the PC changes by a large amount */ + void (*set_fc_callback)(unsigned int new_fc); /* Called when the CPU function code changes */ + void (*instr_hook_callback)(void); /* Called every instruction cycle prior to execution */ + +} m68ki_cpu_core; \ No newline at end of file diff --git a/Src/CPU/68K/Musashi/m68kdasm.c b/Src/CPU/68K/Musashi/m68kdasm.c new file mode 100644 index 0000000..295057a --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kdasm.c @@ -0,0 +1,3563 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kdasm.c + * + * Musashi 68K disassembler. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + + + +/* ======================================================================== */ +/* ================================ INCLUDES ============================== */ +/* ======================================================================== */ + +#include +#include +#include +#include "m68k.h" + +#ifndef DECL_SPEC +#define DECL_SPEC +#endif + +/* ======================================================================== */ +/* ============================ GENERAL DEFINES =========================== */ +/* ======================================================================== */ + +/* unsigned int and int must be at least 32 bits wide */ +#undef uint +#define uint unsigned int + +/* Bit Isolation Functions */ +#define BIT_0(A) ((A) & 0x00000001) +#define BIT_1(A) ((A) & 0x00000002) +#define BIT_2(A) ((A) & 0x00000004) +#define BIT_3(A) ((A) & 0x00000008) +#define BIT_4(A) ((A) & 0x00000010) +#define BIT_5(A) ((A) & 0x00000020) +#define BIT_6(A) ((A) & 0x00000040) +#define BIT_7(A) ((A) & 0x00000080) +#define BIT_8(A) ((A) & 0x00000100) +#define BIT_9(A) ((A) & 0x00000200) +#define BIT_A(A) ((A) & 0x00000400) +#define BIT_B(A) ((A) & 0x00000800) +#define BIT_C(A) ((A) & 0x00001000) +#define BIT_D(A) ((A) & 0x00002000) +#define BIT_E(A) ((A) & 0x00004000) +#define BIT_F(A) ((A) & 0x00008000) +#define BIT_10(A) ((A) & 0x00010000) +#define BIT_11(A) ((A) & 0x00020000) +#define BIT_12(A) ((A) & 0x00040000) +#define BIT_13(A) ((A) & 0x00080000) +#define BIT_14(A) ((A) & 0x00100000) +#define BIT_15(A) ((A) & 0x00200000) +#define BIT_16(A) ((A) & 0x00400000) +#define BIT_17(A) ((A) & 0x00800000) +#define BIT_18(A) ((A) & 0x01000000) +#define BIT_19(A) ((A) & 0x02000000) +#define BIT_1A(A) ((A) & 0x04000000) +#define BIT_1B(A) ((A) & 0x08000000) +#define BIT_1C(A) ((A) & 0x10000000) +#define BIT_1D(A) ((A) & 0x20000000) +#define BIT_1E(A) ((A) & 0x40000000) +#define BIT_1F(A) ((A) & 0x80000000) + +/* These are the CPU types understood by this disassembler */ +#define TYPE_68000 1 +#define TYPE_68008 2 +#define TYPE_68010 4 +#define TYPE_68020 8 +#define TYPE_68030 16 +#define TYPE_68040 32 + +#define M68000_ONLY (TYPE_68000 | TYPE_68008) + +#define M68010_ONLY TYPE_68010 +#define M68010_LESS (TYPE_68000 | TYPE_68008 | TYPE_68010) +#define M68010_PLUS (TYPE_68010 | TYPE_68020 | TYPE_68030 | TYPE_68040) + +#define M68020_ONLY TYPE_68020 +#define M68020_LESS (TYPE_68010 | TYPE_68020) +#define M68020_PLUS (TYPE_68020 | TYPE_68030 | TYPE_68040) + +#define M68030_ONLY TYPE_68030 +#define M68030_LESS (TYPE_68010 | TYPE_68020 | TYPE_68030) +#define M68030_PLUS (TYPE_68030 | TYPE_68040) + +#define M68040_PLUS TYPE_68040 + + +/* Extension word formats */ +#define EXT_8BIT_DISPLACEMENT(A) ((A)&0xff) +#define EXT_FULL(A) BIT_8(A) +#define EXT_EFFECTIVE_ZERO(A) (((A)&0xe4) == 0xc4 || ((A)&0xe2) == 0xc0) +#define EXT_BASE_REGISTER_PRESENT(A) (!BIT_7(A)) +#define EXT_INDEX_REGISTER_PRESENT(A) (!BIT_6(A)) +#define EXT_INDEX_REGISTER(A) (((A)>>12)&7) +#define EXT_INDEX_PRE_POST(A) (EXT_INDEX_PRESENT(A) && (A)&3) +#define EXT_INDEX_PRE(A) (EXT_INDEX_PRESENT(A) && ((A)&7) < 4 && ((A)&7) != 0) +#define EXT_INDEX_POST(A) (EXT_INDEX_PRESENT(A) && ((A)&7) > 4) +#define EXT_INDEX_SCALE(A) (((A)>>9)&3) +#define EXT_INDEX_LONG(A) BIT_B(A) +#define EXT_INDEX_AR(A) BIT_F(A) +#define EXT_BASE_DISPLACEMENT_PRESENT(A) (((A)&0x30) > 0x10) +#define EXT_BASE_DISPLACEMENT_WORD(A) (((A)&0x30) == 0x20) +#define EXT_BASE_DISPLACEMENT_LONG(A) (((A)&0x30) == 0x30) +#define EXT_OUTER_DISPLACEMENT_PRESENT(A) (((A)&3) > 1 && ((A)&0x47) < 0x44) +#define EXT_OUTER_DISPLACEMENT_WORD(A) (((A)&3) == 2 && ((A)&0x47) < 0x44) +#define EXT_OUTER_DISPLACEMENT_LONG(A) (((A)&3) == 3 && ((A)&0x47) < 0x44) + + +/* Opcode flags */ +#if M68K_COMPILE_FOR_MAME == OPT_ON +#define SET_OPCODE_FLAGS(x) g_opcode_type = x; +#define COMBINE_OPCODE_FLAGS(x) ((x) | g_opcode_type | DASMFLAG_SUPPORTED) +#else +#define SET_OPCODE_FLAGS(x) +#define COMBINE_OPCODE_FLAGS(x) (x) +#endif + + +/* ======================================================================== */ +/* =============================== PROTOTYPES ============================= */ +/* ======================================================================== */ + +/* Read data at the PC and increment PC */ +uint read_imm_8(void); +uint read_imm_16(void); +uint read_imm_32(void); + +/* Read data at the PC but don't imcrement the PC */ +uint peek_imm_8(void); +uint peek_imm_16(void); +uint peek_imm_32(void); + +/* make signed integers 100% portably */ +static int make_int_8(int value); +static int make_int_16(int value); + +/* make a string of a hex value */ +static char* make_signed_hex_str_8(uint val); +static char* make_signed_hex_str_16(uint val); +static char* make_signed_hex_str_32(uint val); + +/* make string of ea mode */ +static char* get_ea_mode_str(uint instruction, uint size); + +char* get_ea_mode_str_8(uint instruction); +char* get_ea_mode_str_16(uint instruction); +char* get_ea_mode_str_32(uint instruction); + +/* make string of immediate value */ +static char* get_imm_str_s(uint size); +static char* get_imm_str_u(uint size); + +char* get_imm_str_s8(void); +char* get_imm_str_s16(void); +char* get_imm_str_s32(void); + +/* Stuff to build the opcode handler jump table */ +static void build_opcode_table(void); +static int valid_ea(uint opcode, uint mask); +static int DECL_SPEC compare_nof_true_bits(const void *aptr, const void *bptr); + +/* used to build opcode handler jump table */ +typedef struct +{ + void (*opcode_handler)(void); /* handler function */ + uint mask; /* mask on opcode */ + uint match; /* what to match after masking */ + uint ea_mask; /* what ea modes are allowed */ +} opcode_struct; + + + +/* ======================================================================== */ +/* ================================= DATA ================================= */ +/* ======================================================================== */ + +/* Opcode handler jump table */ +static void (*g_instruction_table[0x10000])(void); +/* Flag if disassembler initialized */ +static int g_initialized = 0; + +/* Address mask to simulate address lines */ +static unsigned int g_address_mask = 0xffffffff; + +static char g_dasm_str[100]; /* string to hold disassembly */ +static char g_helper_str[100]; /* string to hold helpful info */ +static uint g_cpu_pc; /* program counter */ +static uint g_cpu_ir; /* instruction register */ +static uint g_cpu_type; +static uint g_opcode_type; + +/* used by ops like asr, ror, addq, etc */ +static uint g_3bit_qdata_table[8] = {8, 1, 2, 3, 4, 5, 6, 7}; + +static uint g_5bit_data_table[32] = +{ + 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 +}; + +static const char* g_cc[16] = +{"t", "f", "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le"}; + +static const char* g_cpcc[64] = +{/* 000 001 010 011 100 101 110 111 */ + "f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or", /* 000 */ + "un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t", /* 001 */ + "sf", "seq", "gt", "ge", "lt", "le", "gl", "gle", /* 010 */ + "ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne","st", /* 011 */ + "?", "?", "?", "?", "?", "?", "?", "?", /* 100 */ + "?", "?", "?", "?", "?", "?", "?", "?", /* 101 */ + "?", "?", "?", "?", "?", "?", "?", "?", /* 110 */ + "?", "?", "?", "?", "?", "?", "?", "?" /* 111 */ +}; + + +/* ======================================================================== */ +/* =========================== UTILITY FUNCTIONS ========================== */ +/* ======================================================================== */ + +#define LIMIT_CPU_TYPES(ALLOWED_CPU_TYPES) \ + if(!(g_cpu_type & ALLOWED_CPU_TYPES)) \ + { \ + d68000_illegal(); \ + return; \ + } + +#define read_imm_8() (m68k_read_disassembler_16(((g_cpu_pc+=2)-2)&g_address_mask)&0xff) +#define read_imm_16() m68k_read_disassembler_16(((g_cpu_pc+=2)-2)&g_address_mask) +#define read_imm_32() m68k_read_disassembler_32(((g_cpu_pc+=4)-4)&g_address_mask) + +#define peek_imm_8() (m68k_read_disassembler_16(g_cpu_pc & g_address_mask)&0xff) +#define peek_imm_16() m68k_read_disassembler_16(g_cpu_pc & g_address_mask) +#define peek_imm_32() m68k_read_disassembler_32(g_cpu_pc & g_address_mask) + +/* Fake a split interface */ +#define get_ea_mode_str_8(instruction) get_ea_mode_str(instruction, 0) +#define get_ea_mode_str_16(instruction) get_ea_mode_str(instruction, 1) +#define get_ea_mode_str_32(instruction) get_ea_mode_str(instruction, 2) + +#define get_imm_str_s8() get_imm_str_s(0) +#define get_imm_str_s16() get_imm_str_s(1) +#define get_imm_str_s32() get_imm_str_s(2) + +#define get_imm_str_u8() get_imm_str_u(0) +#define get_imm_str_u16() get_imm_str_u(1) +#define get_imm_str_u32() get_imm_str_u(2) + + +/* 100% portable signed int generators */ +static int make_int_8(int value) +{ + return (value & 0x80) ? value | ~0xff : value & 0xff; +} + +static int make_int_16(int value) +{ + return (value & 0x8000) ? value | ~0xffff : value & 0xffff; +} + + +/* Get string representation of hex values */ +static char* make_signed_hex_str_8(uint val) +{ + static char str[20]; + + val &= 0xff; + + if(val == 0x80) + sprintf(str, "-$80"); + else if(val & 0x80) + sprintf(str, "-$%x", (0-val) & 0x7f); + else + sprintf(str, "$%x", val & 0x7f); + + return str; +} + +static char* make_signed_hex_str_16(uint val) +{ + static char str[20]; + + val &= 0xffff; + + if(val == 0x8000) + sprintf(str, "-$8000"); + else if(val & 0x8000) + sprintf(str, "-$%x", (0-val) & 0x7fff); + else + sprintf(str, "$%x", val & 0x7fff); + + return str; +} + +static char* make_signed_hex_str_32(uint val) +{ + static char str[20]; + + val &= 0xffffffff; + + if(val == 0x80000000) + sprintf(str, "-$80000000"); + else if(val & 0x80000000) + sprintf(str, "-$%x", (0-val) & 0x7fffffff); + else + sprintf(str, "$%x", val & 0x7fffffff); + + return str; +} + + +/* make string of immediate value */ +static char* get_imm_str_s(uint size) +{ + static char str[15]; + if(size == 0) + sprintf(str, "#%s", make_signed_hex_str_8(read_imm_8())); + else if(size == 1) + sprintf(str, "#%s", make_signed_hex_str_16(read_imm_16())); + else + sprintf(str, "#%s", make_signed_hex_str_32(read_imm_32())); + return str; +} + +static char* get_imm_str_u(uint size) +{ + static char str[15]; + if(size == 0) + sprintf(str, "#$%x", read_imm_8() & 0xff); + else if(size == 1) + sprintf(str, "#$%x", read_imm_16() & 0xffff); + else + sprintf(str, "#$%x", read_imm_32() & 0xffffffff); + return str; +} + +/* Make string of effective address mode */ +static char* get_ea_mode_str(uint instruction, uint size) +{ + static char b1[64]; + static char b2[64]; + static char* mode = b2; + uint extension; + uint base; + uint outer; + char base_reg[4]; + char index_reg[8]; + uint preindex; + uint postindex; + uint comma = 0; + uint temp_value; + char invalid_mode = 0; + + /* Switch buffers so we don't clobber on a double-call to this function */ + mode = mode == b1 ? b2 : b1; + + switch(instruction & 0x3f) + { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: + /* data register direct */ + sprintf(mode, "D%d", instruction&7); + break; + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: + /* address register direct */ + sprintf(mode, "A%d", instruction&7); + break; + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + /* address register indirect */ + sprintf(mode, "(A%d)", instruction&7); + break; + case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: + /* address register indirect with postincrement */ + sprintf(mode, "(A%d)+", instruction&7); + break; + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: + /* address register indirect with predecrement */ + sprintf(mode, "-(A%d)", instruction&7); + break; + case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: + /* address register indirect with displacement*/ + sprintf(mode, "(%s,A%d)", make_signed_hex_str_16(read_imm_16()), instruction&7); + break; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: + /* address register indirect with index */ + extension = read_imm_16(); + + if((g_cpu_type & M68010_LESS) && EXT_INDEX_SCALE(extension)) + { + invalid_mode = 1; + break; + } + + if(EXT_FULL(extension)) + { + if(g_cpu_type & M68010_LESS) + { + invalid_mode = 1; + break; + } + + if(EXT_EFFECTIVE_ZERO(extension)) + { + strcpy(mode, "0"); + break; + } + + base = EXT_BASE_DISPLACEMENT_PRESENT(extension) ? (EXT_BASE_DISPLACEMENT_LONG(extension) ? read_imm_32() : read_imm_16()) : 0; + outer = EXT_OUTER_DISPLACEMENT_PRESENT(extension) ? (EXT_OUTER_DISPLACEMENT_LONG(extension) ? read_imm_32() : read_imm_16()) : 0; + if(EXT_BASE_REGISTER_PRESENT(extension)) + sprintf(base_reg, "A%d", instruction&7); + else + *base_reg = 0; + if(EXT_INDEX_REGISTER_PRESENT(extension)) + { + sprintf(index_reg, "%c%d.%c", EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + if(EXT_INDEX_SCALE(extension)) + sprintf(index_reg+strlen(index_reg), "*%d", 1 << EXT_INDEX_SCALE(extension)); + } + else + *index_reg = 0; + preindex = (extension&7) > 0 && (extension&7) < 4; + postindex = (extension&7) > 4; + + strcpy(mode, "("); + if(preindex || postindex) + strcat(mode, "["); + if(base) + { + strcat(mode, make_signed_hex_str_16(base)); + comma = 1; + } + if(*base_reg) + { + if(comma) + strcat(mode, ","); + strcat(mode, base_reg); + comma = 1; + } + if(postindex) + { + strcat(mode, "]"); + comma = 1; + } + if(*index_reg) + { + if(comma) + strcat(mode, ","); + strcat(mode, index_reg); + comma = 1; + } + if(preindex) + { + strcat(mode, "]"); + comma = 1; + } + if(outer) + { + if(comma) + strcat(mode, ","); + strcat(mode, make_signed_hex_str_16(outer)); + } + strcat(mode, ")"); + break; + } + + if(EXT_8BIT_DISPLACEMENT(extension) == 0) + sprintf(mode, "(A%d,%c%d.%c", instruction&7, EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + else + sprintf(mode, "(%s,A%d,%c%d.%c", make_signed_hex_str_8(extension), instruction&7, EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + if(EXT_INDEX_SCALE(extension)) + sprintf(mode+strlen(mode), "*%d", 1 << EXT_INDEX_SCALE(extension)); + strcat(mode, ")"); + break; + case 0x38: + /* absolute short address */ + sprintf(mode, "$%x.w", read_imm_16()); + break; + case 0x39: + /* absolute long address */ + sprintf(mode, "$%x.l", read_imm_32()); + break; + case 0x3a: + /* program counter with displacement */ + temp_value = read_imm_16(); + sprintf(mode, "(%s,PC)", make_signed_hex_str_16(temp_value)); + sprintf(g_helper_str, "; ($%x)", (make_int_16(temp_value) + g_cpu_pc-2) & 0xffffffff); + break; + case 0x3b: + /* program counter with index */ + extension = read_imm_16(); + + if((g_cpu_type & M68010_LESS) && EXT_INDEX_SCALE(extension)) + { + invalid_mode = 1; + break; + } + + if(EXT_FULL(extension)) + { + if(g_cpu_type & M68010_LESS) + { + invalid_mode = 1; + break; + } + + if(EXT_EFFECTIVE_ZERO(extension)) + { + strcpy(mode, "0"); + break; + } + base = EXT_BASE_DISPLACEMENT_PRESENT(extension) ? (EXT_BASE_DISPLACEMENT_LONG(extension) ? read_imm_32() : read_imm_16()) : 0; + outer = EXT_OUTER_DISPLACEMENT_PRESENT(extension) ? (EXT_OUTER_DISPLACEMENT_LONG(extension) ? read_imm_32() : read_imm_16()) : 0; + if(EXT_BASE_REGISTER_PRESENT(extension)) + strcpy(base_reg, "PC"); + else + *base_reg = 0; + if(EXT_INDEX_REGISTER_PRESENT(extension)) + { + sprintf(index_reg, "%c%d.%c", EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + if(EXT_INDEX_SCALE(extension)) + sprintf(index_reg+strlen(index_reg), "*%d", 1 << EXT_INDEX_SCALE(extension)); + } + else + *index_reg = 0; + preindex = (extension&7) > 0 && (extension&7) < 4; + postindex = (extension&7) > 4; + + strcpy(mode, "("); + if(preindex || postindex) + strcat(mode, "["); + if(base) + { + strcat(mode, make_signed_hex_str_16(base)); + comma = 1; + } + if(*base_reg) + { + if(comma) + strcat(mode, ","); + strcat(mode, base_reg); + comma = 1; + } + if(postindex) + { + strcat(mode, "]"); + comma = 1; + } + if(*index_reg) + { + if(comma) + strcat(mode, ","); + strcat(mode, index_reg); + comma = 1; + } + if(preindex) + { + strcat(mode, "]"); + comma = 1; + } + if(outer) + { + if(comma) + strcat(mode, ","); + strcat(mode, make_signed_hex_str_16(outer)); + } + strcat(mode, ")"); + break; + } + + if(EXT_8BIT_DISPLACEMENT(extension) == 0) + sprintf(mode, "(PC,%c%d.%c", EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + else + sprintf(mode, "(%s,PC,%c%d.%c", make_signed_hex_str_8(extension), EXT_INDEX_AR(extension) ? 'A' : 'D', EXT_INDEX_REGISTER(extension), EXT_INDEX_LONG(extension) ? 'l' : 'w'); + if(EXT_INDEX_SCALE(extension)) + sprintf(mode+strlen(mode), "*%d", 1 << EXT_INDEX_SCALE(extension)); + strcat(mode, ")"); + break; + case 0x3c: + /* Immediate */ + sprintf(mode, "%s", get_imm_str_u(size)); + break; + default: + invalid_mode = 1; + } + + if(invalid_mode) + sprintf(mode, "INVALID %x", instruction & 0x3f); + + return mode; +} + + + +/* ======================================================================== */ +/* ========================= INSTRUCTION HANDLERS ========================= */ +/* ======================================================================== */ +/* Instruction handler function names follow this convention: + * + * d68000_NAME_EXTENSIONS(void) + * where NAME is the name of the opcode it handles and EXTENSIONS are any + * extensions for special instances of that opcode. + * + * Examples: + * d68000_add_er_8(): add opcode, from effective address to register, + * size = byte + * + * d68000_asr_s_8(): arithmetic shift right, static count, size = byte + * + * + * Common extensions: + * 8 : size = byte + * 16 : size = word + * 32 : size = long + * rr : register to register + * mm : memory to memory + * r : register + * s : static + * er : effective address -> register + * re : register -> effective address + * ea : using effective address mode of operation + * d : data register direct + * a : address register direct + * ai : address register indirect + * pi : address register indirect with postincrement + * pd : address register indirect with predecrement + * di : address register indirect with displacement + * ix : address register indirect with index + * aw : absolute word + * al : absolute long + */ + +static void d68000_illegal(void) +{ + sprintf(g_dasm_str, "dc.w $%04x; ILLEGAL", g_cpu_ir); +} + +static void d68000_1010(void) +{ + sprintf(g_dasm_str, "dc.w $%04x; opcode 1010", g_cpu_ir); +} + + +static void d68000_1111(void) +{ + sprintf(g_dasm_str, "dc.w $%04x; opcode 1111", g_cpu_ir); +} + + +static void d68000_abcd_rr(void) +{ + sprintf(g_dasm_str, "abcd D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + + +static void d68000_abcd_mm(void) +{ + sprintf(g_dasm_str, "abcd -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_add_er_8(void) +{ + sprintf(g_dasm_str, "add.b %s, D%d", get_ea_mode_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + + +static void d68000_add_er_16(void) +{ + sprintf(g_dasm_str, "add.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_add_er_32(void) +{ + sprintf(g_dasm_str, "add.l %s, D%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_add_re_8(void) +{ + sprintf(g_dasm_str, "add.b D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_add_re_16(void) +{ + sprintf(g_dasm_str, "add.w D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_add_re_32(void) +{ + sprintf(g_dasm_str, "add.l D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_adda_16(void) +{ + sprintf(g_dasm_str, "adda.w %s, A%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_adda_32(void) +{ + sprintf(g_dasm_str, "adda.l %s, A%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_addi_8(void) +{ + char* str = get_imm_str_s8(); + sprintf(g_dasm_str, "addi.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_addi_16(void) +{ + char* str = get_imm_str_s16(); + sprintf(g_dasm_str, "addi.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_addi_32(void) +{ + char* str = get_imm_str_s32(); + sprintf(g_dasm_str, "addi.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_addq_8(void) +{ + sprintf(g_dasm_str, "addq.b #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_addq_16(void) +{ + sprintf(g_dasm_str, "addq.w #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_addq_32(void) +{ + sprintf(g_dasm_str, "addq.l #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_addx_rr_8(void) +{ + sprintf(g_dasm_str, "addx.b D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_addx_rr_16(void) +{ + sprintf(g_dasm_str, "addx.w D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_addx_rr_32(void) +{ + sprintf(g_dasm_str, "addx.l D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_addx_mm_8(void) +{ + sprintf(g_dasm_str, "addx.b -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_addx_mm_16(void) +{ + sprintf(g_dasm_str, "addx.w -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_addx_mm_32(void) +{ + sprintf(g_dasm_str, "addx.l -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_and_er_8(void) +{ + sprintf(g_dasm_str, "and.b %s, D%d", get_ea_mode_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_and_er_16(void) +{ + sprintf(g_dasm_str, "and.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_and_er_32(void) +{ + sprintf(g_dasm_str, "and.l %s, D%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_and_re_8(void) +{ + sprintf(g_dasm_str, "and.b D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_and_re_16(void) +{ + sprintf(g_dasm_str, "and.w D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_and_re_32(void) +{ + sprintf(g_dasm_str, "and.l D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_andi_8(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "andi.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_andi_16(void) +{ + char* str = get_imm_str_u16(); + sprintf(g_dasm_str, "andi.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_andi_32(void) +{ + char* str = get_imm_str_u32(); + sprintf(g_dasm_str, "andi.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_andi_to_ccr(void) +{ + sprintf(g_dasm_str, "andi %s, CCR", get_imm_str_u8()); +} + +static void d68000_andi_to_sr(void) +{ + sprintf(g_dasm_str, "andi %s, SR", get_imm_str_u16()); +} + +static void d68000_asr_s_8(void) +{ + sprintf(g_dasm_str, "asr.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asr_s_16(void) +{ + sprintf(g_dasm_str, "asr.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asr_s_32(void) +{ + sprintf(g_dasm_str, "asr.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asr_r_8(void) +{ + sprintf(g_dasm_str, "asr.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asr_r_16(void) +{ + sprintf(g_dasm_str, "asr.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asr_r_32(void) +{ + sprintf(g_dasm_str, "asr.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asr_ea(void) +{ + sprintf(g_dasm_str, "asr.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_asl_s_8(void) +{ + sprintf(g_dasm_str, "asl.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asl_s_16(void) +{ + sprintf(g_dasm_str, "asl.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asl_s_32(void) +{ + sprintf(g_dasm_str, "asl.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_asl_r_8(void) +{ + sprintf(g_dasm_str, "asl.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asl_r_16(void) +{ + sprintf(g_dasm_str, "asl.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asl_r_32(void) +{ + sprintf(g_dasm_str, "asl.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_asl_ea(void) +{ + sprintf(g_dasm_str, "asl.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_bcc_8(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "b%-2s %x", g_cc[(g_cpu_ir>>8)&0xf], temp_pc + make_int_8(g_cpu_ir)); +} + +static void d68000_bcc_16(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "b%-2s %x", g_cc[(g_cpu_ir>>8)&0xf], temp_pc + make_int_16(read_imm_16())); +} + +static void d68020_bcc_32(void) +{ + uint temp_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "b%-2s %x; (2+)", g_cc[(g_cpu_ir>>8)&0xf], temp_pc + read_imm_32()); +} + +static void d68000_bchg_r(void) +{ + sprintf(g_dasm_str, "bchg D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_bchg_s(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "bchg %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_bclr_r(void) +{ + sprintf(g_dasm_str, "bclr D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_bclr_s(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "bclr %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68010_bkpt(void) +{ + LIMIT_CPU_TYPES(M68010_PLUS); + sprintf(g_dasm_str, "bkpt #%d; (1+)", g_cpu_ir&7); +} + +static void d68020_bfchg(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfchg %s {%s:%s}; (2+)", get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfclr(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfclr %s {%s:%s}; (2+)", get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfexts(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfexts D%d, %s {%s:%s}; (2+)", (extension>>12)&7, get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfextu(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfextu D%d, %s {%s:%s}; (2+)", (extension>>12)&7, get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfffo(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfffo D%d, %s {%s:%s}; (2+)", (extension>>12)&7, get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfins(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfins D%d, %s {%s:%s}; (2+)", (extension>>12)&7, get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bfset(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bfset %s {%s:%s}; (2+)", get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68020_bftst(void) +{ + uint extension; + char offset[3]; + char width[3]; + + LIMIT_CPU_TYPES(M68020_PLUS); + + extension = read_imm_16(); + + if(BIT_B(extension)) + sprintf(offset, "D%d", (extension>>6)&7); + else + sprintf(offset, "%d", (extension>>6)&31); + if(BIT_5(extension)) + sprintf(width, "D%d", extension&7); + else + sprintf(width, "%d", g_5bit_data_table[extension&31]); + sprintf(g_dasm_str, "bftst %s {%s:%s}; (2+)", get_ea_mode_str_8(g_cpu_ir), offset, width); +} + +static void d68000_bra_8(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "bra %x", temp_pc + make_int_8(g_cpu_ir)); +} + +static void d68000_bra_16(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "bra %x", temp_pc + make_int_16(read_imm_16())); +} + +static void d68020_bra_32(void) +{ + uint temp_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "bra %x; (2+)", temp_pc + read_imm_32()); +} + +static void d68000_bset_r(void) +{ + sprintf(g_dasm_str, "bset D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_bset_s(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "bset %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_bsr_8(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "bsr %x", temp_pc + make_int_8(g_cpu_ir)); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_bsr_16(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "bsr %x", temp_pc + make_int_16(read_imm_16())); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68020_bsr_32(void) +{ + uint temp_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "bsr %x; (2+)", temp_pc + peek_imm_32()); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_btst_r(void) +{ + sprintf(g_dasm_str, "btst D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_btst_s(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "btst %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_callm(void) +{ + char* str; + LIMIT_CPU_TYPES(M68020_ONLY); + str = get_imm_str_u8(); + + sprintf(g_dasm_str, "callm %s, %s; (2)", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cas_8(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "cas.b D%d, D%d, %s; (2+)", extension&7, (extension>>8)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cas_16(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "cas.w D%d, D%d, %s; (2+)", extension&7, (extension>>8)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_cas_32(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "cas.l D%d, D%d, %s; (2+)", extension&7, (extension>>8)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_cas2_16(void) +{ +/* CAS2 Dc1:Dc2,Du1:Dc2:(Rn1):(Rn2) +f e d c b a 9 8 7 6 5 4 3 2 1 0 + DARn1 0 0 0 Du1 0 0 0 Dc1 + DARn2 0 0 0 Du2 0 0 0 Dc2 +*/ + + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_32(); + sprintf(g_dasm_str, "cas2.w D%d:D%d:D%d:D%d, (%c%d):(%c%d); (2+)", + (extension>>16)&7, extension&7, (extension>>22)&7, (extension>>6)&7, + BIT_1F(extension) ? 'A' : 'D', (extension>>28)&7, + BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68020_cas2_32(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_32(); + sprintf(g_dasm_str, "cas2.l D%d:D%d:D%d:D%d, (%c%d):(%c%d); (2+)", + (extension>>16)&7, extension&7, (extension>>22)&7, (extension>>6)&7, + BIT_1F(extension) ? 'A' : 'D', (extension>>28)&7, + BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68000_chk_16(void) +{ + sprintf(g_dasm_str, "chk.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68020_chk_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "chk.l %s, D%d; (2+)", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68020_chk2_cmp2_8(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "%s.b %s, %c%d; (2+)", BIT_B(extension) ? "chk2" : "cmp2", get_ea_mode_str_8(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68020_chk2_cmp2_16(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "%s.w %s, %c%d; (2+)", BIT_B(extension) ? "chk2" : "cmp2", get_ea_mode_str_16(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68020_chk2_cmp2_32(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + sprintf(g_dasm_str, "%s.l %s, %c%d; (2+)", BIT_B(extension) ? "chk2" : "cmp2", get_ea_mode_str_32(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68040_cinv(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + switch((g_cpu_ir>>3)&3) + { + case 0: + sprintf(g_dasm_str, "cinv (illegal scope); (4)"); + break; + case 1: + sprintf(g_dasm_str, "cinvl %d, (A%d); (4)", (g_cpu_ir>>6)&3, g_cpu_ir&7); + break; + case 2: + sprintf(g_dasm_str, "cinvp %d, (A%d); (4)", (g_cpu_ir>>6)&3, g_cpu_ir&7); + break; + case 3: + sprintf(g_dasm_str, "cinva %d; (4)", (g_cpu_ir>>6)&3); + break; + } +} + +static void d68000_clr_8(void) +{ + sprintf(g_dasm_str, "clr.b %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_clr_16(void) +{ + sprintf(g_dasm_str, "clr.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_clr_32(void) +{ + sprintf(g_dasm_str, "clr.l %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_cmp_8(void) +{ + sprintf(g_dasm_str, "cmp.b %s, D%d", get_ea_mode_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_cmp_16(void) +{ + sprintf(g_dasm_str, "cmp.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_cmp_32(void) +{ + sprintf(g_dasm_str, "cmp.l %s, D%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_cmpa_16(void) +{ + sprintf(g_dasm_str, "cmpa.w %s, A%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_cmpa_32(void) +{ + sprintf(g_dasm_str, "cmpa.l %s, A%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_cmpi_8(void) +{ + char* str = get_imm_str_s8(); + sprintf(g_dasm_str, "cmpi.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cmpi_pcdi_8(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s8(); + sprintf(g_dasm_str, "cmpi.b %s, %s; (2+)", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cmpi_pcix_8(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s8(); + sprintf(g_dasm_str, "cmpi.b %s, %s; (2+)", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_cmpi_16(void) +{ + char* str; + str = get_imm_str_s16(); + sprintf(g_dasm_str, "cmpi.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_cmpi_pcdi_16(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s16(); + sprintf(g_dasm_str, "cmpi.w %s, %s; (2+)", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_cmpi_pcix_16(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s16(); + sprintf(g_dasm_str, "cmpi.w %s, %s; (2+)", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_cmpi_32(void) +{ + char* str; + str = get_imm_str_s32(); + sprintf(g_dasm_str, "cmpi.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_cmpi_pcdi_32(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s32(); + sprintf(g_dasm_str, "cmpi.l %s, %s; (2+)", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_cmpi_pcix_32(void) +{ + char* str; + LIMIT_CPU_TYPES(M68010_PLUS); + str = get_imm_str_s32(); + sprintf(g_dasm_str, "cmpi.l %s, %s; (2+)", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_cmpm_8(void) +{ + sprintf(g_dasm_str, "cmpm.b (A%d)+, (A%d)+", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_cmpm_16(void) +{ + sprintf(g_dasm_str, "cmpm.w (A%d)+, (A%d)+", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_cmpm_32(void) +{ + sprintf(g_dasm_str, "cmpm.l (A%d)+, (A%d)+", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68020_cpbcc_16(void) +{ + uint extension; + uint new_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + new_pc += make_int_16(peek_imm_16()); + sprintf(g_dasm_str, "%db%-4s %s; %x (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[g_cpu_ir&0x3f], get_imm_str_s16(), new_pc, extension); +} + +static void d68020_cpbcc_32(void) +{ + uint extension; + uint new_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + new_pc += peek_imm_32(); + sprintf(g_dasm_str, "%db%-4s %s; %x (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[g_cpu_ir&0x3f], get_imm_str_s16(), new_pc, extension); +} + +static void d68020_cpdbcc(void) +{ + uint extension1; + uint extension2; + uint new_pc = g_cpu_pc; + LIMIT_CPU_TYPES(M68020_PLUS); + extension1 = read_imm_16(); + extension2 = read_imm_16(); + new_pc += make_int_16(peek_imm_16()); + sprintf(g_dasm_str, "%ddb%-4s D%d,%s; %x (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[extension1&0x3f], g_cpu_ir&7, get_imm_str_s16(), new_pc, extension2); +} + +static void d68020_cpgen(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "%dgen %s; (2-3)", (g_cpu_ir>>9)&7, get_imm_str_u32()); +} + +static void d68020_cprestore(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "%drestore %s; (2-3)", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cpsave(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "%dsave %s; (2-3)", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_cpscc(void) +{ + uint extension1; + uint extension2; + LIMIT_CPU_TYPES(M68020_PLUS); + extension1 = read_imm_16(); + extension2 = read_imm_16(); + sprintf(g_dasm_str, "%ds%-4s %s; (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[extension1&0x3f], get_ea_mode_str_8(g_cpu_ir), extension2); +} + +static void d68020_cptrapcc_0(void) +{ + uint extension1; + uint extension2; + LIMIT_CPU_TYPES(M68020_PLUS); + extension1 = read_imm_16(); + extension2 = read_imm_16(); + sprintf(g_dasm_str, "%dtrap%-4s; (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[extension1&0x3f], extension2); +} + +static void d68020_cptrapcc_16(void) +{ + uint extension1; + uint extension2; + LIMIT_CPU_TYPES(M68020_PLUS); + extension1 = read_imm_16(); + extension2 = read_imm_16(); + sprintf(g_dasm_str, "%dtrap%-4s %s; (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[extension1&0x3f], get_imm_str_u16(), extension2); +} + +static void d68020_cptrapcc_32(void) +{ + uint extension1; + uint extension2; + LIMIT_CPU_TYPES(M68020_PLUS); + extension1 = read_imm_16(); + extension2 = read_imm_16(); + sprintf(g_dasm_str, "%dtrap%-4s %s; (extension = %x) (2-3)", (g_cpu_ir>>9)&7, g_cpcc[extension1&0x3f], get_imm_str_u32(), extension2); +} + +static void d68040_cpush(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + switch((g_cpu_ir>>3)&3) + { + case 0: + sprintf(g_dasm_str, "cpush (illegal scope); (4)"); + break; + case 1: + sprintf(g_dasm_str, "cpushl %d, (A%d); (4)", (g_cpu_ir>>6)&3, g_cpu_ir&7); + break; + case 2: + sprintf(g_dasm_str, "cpushp %d, (A%d); (4)", (g_cpu_ir>>6)&3, g_cpu_ir&7); + break; + case 3: + sprintf(g_dasm_str, "cpusha %d; (4)", (g_cpu_ir>>6)&3); + break; + } +} + +static void d68000_dbra(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "dbra D%d, %x", g_cpu_ir & 7, temp_pc + make_int_16(read_imm_16())); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_dbcc(void) +{ + uint temp_pc = g_cpu_pc; + sprintf(g_dasm_str, "db%-2s D%d, %x", g_cc[(g_cpu_ir>>8)&0xf], g_cpu_ir & 7, temp_pc + make_int_16(read_imm_16())); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_divs(void) +{ + sprintf(g_dasm_str, "divs.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_divu(void) +{ + sprintf(g_dasm_str, "divu.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68020_divl(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + + if(BIT_A(extension)) + sprintf(g_dasm_str, "div%c.l %s, D%d:D%d; (2+)", BIT_B(extension) ? 's' : 'u', get_ea_mode_str_32(g_cpu_ir), extension&7, (extension>>12)&7); + else if((extension&7) == ((extension>>12)&7)) + sprintf(g_dasm_str, "div%c.l %s, D%d; (2+)", BIT_B(extension) ? 's' : 'u', get_ea_mode_str_32(g_cpu_ir), (extension>>12)&7); + else + sprintf(g_dasm_str, "div%cl.l %s, D%d:D%d; (2+)", BIT_B(extension) ? 's' : 'u', get_ea_mode_str_32(g_cpu_ir), extension&7, (extension>>12)&7); +} + +static void d68000_eor_8(void) +{ + sprintf(g_dasm_str, "eor.b D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_eor_16(void) +{ + sprintf(g_dasm_str, "eor.w D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_eor_32(void) +{ + sprintf(g_dasm_str, "eor.l D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_eori_8(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "eori.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_eori_16(void) +{ + char* str = get_imm_str_u16(); + sprintf(g_dasm_str, "eori.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_eori_32(void) +{ + char* str = get_imm_str_u32(); + sprintf(g_dasm_str, "eori.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_eori_to_ccr(void) +{ + sprintf(g_dasm_str, "eori %s, CCR", get_imm_str_u8()); +} + +static void d68000_eori_to_sr(void) +{ + sprintf(g_dasm_str, "eori %s, SR", get_imm_str_u16()); +} + +static void d68000_exg_dd(void) +{ + sprintf(g_dasm_str, "exg D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_exg_aa(void) +{ + sprintf(g_dasm_str, "exg A%d, A%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_exg_da(void) +{ + sprintf(g_dasm_str, "exg D%d, A%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_ext_16(void) +{ + sprintf(g_dasm_str, "ext.w D%d", g_cpu_ir&7); +} + +static void d68000_ext_32(void) +{ + sprintf(g_dasm_str, "ext.l D%d", g_cpu_ir&7); +} + +static void d68020_extb_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "extb.l D%d; (2+)", g_cpu_ir&7); +} + +static void d68000_jmp(void) +{ + sprintf(g_dasm_str, "jmp %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_jsr(void) +{ + sprintf(g_dasm_str, "jsr %s", get_ea_mode_str_32(g_cpu_ir)); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_lea(void) +{ + sprintf(g_dasm_str, "lea %s, A%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_link_16(void) +{ + sprintf(g_dasm_str, "link A%d, %s", g_cpu_ir&7, get_imm_str_s16()); +} + +static void d68020_link_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "link A%d, %s; (2+)", g_cpu_ir&7, get_imm_str_s32()); +} + +static void d68000_lsr_s_8(void) +{ + sprintf(g_dasm_str, "lsr.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsr_s_16(void) +{ + sprintf(g_dasm_str, "lsr.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsr_s_32(void) +{ + sprintf(g_dasm_str, "lsr.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsr_r_8(void) +{ + sprintf(g_dasm_str, "lsr.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsr_r_16(void) +{ + sprintf(g_dasm_str, "lsr.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsr_r_32(void) +{ + sprintf(g_dasm_str, "lsr.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsr_ea(void) +{ + sprintf(g_dasm_str, "lsr.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_lsl_s_8(void) +{ + sprintf(g_dasm_str, "lsl.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsl_s_16(void) +{ + sprintf(g_dasm_str, "lsl.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsl_s_32(void) +{ + sprintf(g_dasm_str, "lsl.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_lsl_r_8(void) +{ + sprintf(g_dasm_str, "lsl.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsl_r_16(void) +{ + sprintf(g_dasm_str, "lsl.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsl_r_32(void) +{ + sprintf(g_dasm_str, "lsl.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_lsl_ea(void) +{ + sprintf(g_dasm_str, "lsl.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_move_8(void) +{ + char* str = get_ea_mode_str_8(g_cpu_ir); + sprintf(g_dasm_str, "move.b %s, %s", str, get_ea_mode_str_8(((g_cpu_ir>>9) & 7) | ((g_cpu_ir>>3) & 0x38))); +} + +static void d68000_move_16(void) +{ + char* str = get_ea_mode_str_16(g_cpu_ir); + sprintf(g_dasm_str, "move.w %s, %s", str, get_ea_mode_str_16(((g_cpu_ir>>9) & 7) | ((g_cpu_ir>>3) & 0x38))); +} + +static void d68000_move_32(void) +{ + char* str = get_ea_mode_str_32(g_cpu_ir); + sprintf(g_dasm_str, "move.l %s, %s", str, get_ea_mode_str_32(((g_cpu_ir>>9) & 7) | ((g_cpu_ir>>3) & 0x38))); +} + +static void d68000_movea_16(void) +{ + sprintf(g_dasm_str, "movea.w %s, A%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_movea_32(void) +{ + sprintf(g_dasm_str, "movea.l %s, A%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_move_to_ccr(void) +{ + sprintf(g_dasm_str, "move %s, CCR", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68010_move_fr_ccr(void) +{ + LIMIT_CPU_TYPES(M68010_PLUS); + sprintf(g_dasm_str, "move CCR, %s; (1+)", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_move_fr_sr(void) +{ + sprintf(g_dasm_str, "move SR, %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_move_to_sr(void) +{ + sprintf(g_dasm_str, "move %s, SR", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_move_fr_usp(void) +{ + sprintf(g_dasm_str, "move USP, A%d", g_cpu_ir&7); +} + +static void d68000_move_to_usp(void) +{ + sprintf(g_dasm_str, "move A%d, USP", g_cpu_ir&7); +} + +static void d68010_movec(void) +{ + uint extension; + const char* reg_name; + const char* processor; + LIMIT_CPU_TYPES(M68010_PLUS); + extension = read_imm_16(); + + switch(extension & 0xfff) + { + case 0x000: + reg_name = "SFC"; + processor = "1+"; + break; + case 0x001: + reg_name = "DFC"; + processor = "1+"; + break; + case 0x800: + reg_name = "USP"; + processor = "1+"; + break; + case 0x801: + reg_name = "VBR"; + processor = "1+"; + break; + case 0x002: + reg_name = "CACR"; + processor = "2+"; + break; + case 0x802: + reg_name = "CAAR"; + processor = "2,3"; + break; + case 0x803: + reg_name = "MSP"; + processor = "2+"; + break; + case 0x804: + reg_name = "ISP"; + processor = "2+"; + break; + case 0x003: + reg_name = "TC"; + processor = "4+"; + break; + case 0x004: + reg_name = "ITT0"; + processor = "4+"; + break; + case 0x005: + reg_name = "ITT1"; + processor = "4+"; + break; + case 0x006: + reg_name = "DTT0"; + processor = "4+"; + break; + case 0x007: + reg_name = "DTT1"; + processor = "4+"; + break; + case 0x805: + reg_name = "MMUSR"; + processor = "4+"; + break; + case 0x806: + reg_name = "URP"; + processor = "4+"; + break; + case 0x807: + reg_name = "SRP"; + processor = "4+"; + break; + default: + reg_name = make_signed_hex_str_16(extension & 0xfff); + processor = "?"; + } + + if(BIT_1(g_cpu_ir)) + sprintf(g_dasm_str, "movec %c%d, %s; (%s)", BIT_F(extension) ? 'A' : 'D', (extension>>12)&7, reg_name, processor); + else + sprintf(g_dasm_str, "movec %s, %c%d; (%s)", reg_name, BIT_F(extension) ? 'A' : 'D', (extension>>12)&7, processor); +} + +static void d68000_movem_pd_16(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1<<(15-i))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(15-(i+1))))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "D%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(7-i))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(7-(i+1))))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.w %s, %s", buffer, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_movem_pd_32(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1<<(15-i))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(15-(i+1))))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "D%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(7-i))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(7-(i+1))))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.l %s, %s", buffer, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_movem_er_16(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1< 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(i+8))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(i+8+1)))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.w %s, %s", get_ea_mode_str_16(g_cpu_ir), buffer); +} + +static void d68000_movem_er_32(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1< 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(i+8))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(i+8+1)))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.l %s, %s", get_ea_mode_str_32(g_cpu_ir), buffer); +} + +static void d68000_movem_re_16(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1< 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(i+8))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(i+8+1)))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.w %s, %s", buffer, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_movem_re_32(void) +{ + uint data = read_imm_16(); + char buffer[40]; + uint first; + uint run_length; + uint i; + + buffer[0] = 0; + for(i=0;i<8;i++) + { + if(data&(1< 0) + sprintf(buffer+strlen(buffer), "-D%d", first + run_length); + } + } + for(i=0;i<8;i++) + { + if(data&(1<<(i+8))) + { + first = i; + run_length = 0; + while(i<7 && (data&(1<<(i+8+1)))) + { + i++; + run_length++; + } + if(buffer[0] != 0) + strcat(buffer, "/"); + sprintf(buffer+strlen(buffer), "A%d", first); + if(run_length > 0) + sprintf(buffer+strlen(buffer), "-A%d", first + run_length); + } + } + sprintf(g_dasm_str, "movem.l %s, %s", buffer, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_movep_re_16(void) +{ + sprintf(g_dasm_str, "movep.w D%d, ($%x,A%d)", (g_cpu_ir>>9)&7, read_imm_16(), g_cpu_ir&7); +} + +static void d68000_movep_re_32(void) +{ + sprintf(g_dasm_str, "movep.l D%d, ($%x,A%d)", (g_cpu_ir>>9)&7, read_imm_16(), g_cpu_ir&7); +} + +static void d68000_movep_er_16(void) +{ + sprintf(g_dasm_str, "movep.w ($%x,A%d), D%d", read_imm_16(), g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_movep_er_32(void) +{ + sprintf(g_dasm_str, "movep.l ($%x,A%d), D%d", read_imm_16(), g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68010_moves_8(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68010_PLUS); + extension = read_imm_16(); + if(BIT_B(extension)) + sprintf(g_dasm_str, "moves.b %c%d, %s; (1+)", BIT_F(extension) ? 'A' : 'D', (extension>>12)&7, get_ea_mode_str_8(g_cpu_ir)); + else + sprintf(g_dasm_str, "moves.b %s, %c%d; (1+)", get_ea_mode_str_8(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68010_moves_16(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68010_PLUS); + extension = read_imm_16(); + if(BIT_B(extension)) + sprintf(g_dasm_str, "moves.w %c%d, %s; (1+)", BIT_F(extension) ? 'A' : 'D', (extension>>12)&7, get_ea_mode_str_16(g_cpu_ir)); + else + sprintf(g_dasm_str, "moves.w %s, %c%d; (1+)", get_ea_mode_str_16(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68010_moves_32(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68010_PLUS); + extension = read_imm_16(); + if(BIT_B(extension)) + sprintf(g_dasm_str, "moves.l %c%d, %s; (1+)", BIT_F(extension) ? 'A' : 'D', (extension>>12)&7, get_ea_mode_str_32(g_cpu_ir)); + else + sprintf(g_dasm_str, "moves.l %s, %c%d; (1+)", get_ea_mode_str_32(g_cpu_ir), BIT_F(extension) ? 'A' : 'D', (extension>>12)&7); +} + +static void d68000_moveq(void) +{ + sprintf(g_dasm_str, "moveq #%s, D%d", make_signed_hex_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68040_move16_pi_pi(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + sprintf(g_dasm_str, "move16 (A%d)+, (A%d)+; (4)", g_cpu_ir&7, (read_imm_16()>>12)&7); +} + +static void d68040_move16_pi_al(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + sprintf(g_dasm_str, "move16 (A%d)+, %s; (4)", g_cpu_ir&7, get_imm_str_u32()); +} + +static void d68040_move16_al_pi(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + sprintf(g_dasm_str, "move16 %s, (A%d)+; (4)", get_imm_str_u32(), g_cpu_ir&7); +} + +static void d68040_move16_ai_al(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + sprintf(g_dasm_str, "move16 (A%d), %s; (4)", g_cpu_ir&7, get_imm_str_u32()); +} + +static void d68040_move16_al_ai(void) +{ + LIMIT_CPU_TYPES(M68040_PLUS); + sprintf(g_dasm_str, "move16 %s, (A%d); (4)", get_imm_str_u32(), g_cpu_ir&7); +} + +static void d68000_muls(void) +{ + sprintf(g_dasm_str, "muls.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_mulu(void) +{ + sprintf(g_dasm_str, "mulu.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68020_mull(void) +{ + uint extension; + LIMIT_CPU_TYPES(M68020_PLUS); + extension = read_imm_16(); + + if(BIT_A(extension)) + sprintf(g_dasm_str, "mul%c.l %s, D%d-D%d; (2+)", BIT_B(extension) ? 's' : 'u', get_ea_mode_str_32(g_cpu_ir), extension&7, (extension>>12)&7); + else + sprintf(g_dasm_str, "mul%c.l %s, D%d; (2+)", BIT_B(extension) ? 's' : 'u', get_ea_mode_str_32(g_cpu_ir), (extension>>12)&7); +} + +static void d68000_nbcd(void) +{ + sprintf(g_dasm_str, "nbcd %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_neg_8(void) +{ + sprintf(g_dasm_str, "neg.b %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_neg_16(void) +{ + sprintf(g_dasm_str, "neg.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_neg_32(void) +{ + sprintf(g_dasm_str, "neg.l %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_negx_8(void) +{ + sprintf(g_dasm_str, "negx.b %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_negx_16(void) +{ + sprintf(g_dasm_str, "negx.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_negx_32(void) +{ + sprintf(g_dasm_str, "negx.l %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_nop(void) +{ + sprintf(g_dasm_str, "nop"); +} + +static void d68000_not_8(void) +{ + sprintf(g_dasm_str, "not.b %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_not_16(void) +{ + sprintf(g_dasm_str, "not.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_not_32(void) +{ + sprintf(g_dasm_str, "not.l %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_or_er_8(void) +{ + sprintf(g_dasm_str, "or.b %s, D%d", get_ea_mode_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_or_er_16(void) +{ + sprintf(g_dasm_str, "or.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_or_er_32(void) +{ + sprintf(g_dasm_str, "or.l %s, D%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_or_re_8(void) +{ + sprintf(g_dasm_str, "or.b D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_or_re_16(void) +{ + sprintf(g_dasm_str, "or.w D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_or_re_32(void) +{ + sprintf(g_dasm_str, "or.l D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_ori_8(void) +{ + char* str = get_imm_str_u8(); + sprintf(g_dasm_str, "ori.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_ori_16(void) +{ + char* str = get_imm_str_u16(); + sprintf(g_dasm_str, "ori.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_ori_32(void) +{ + char* str = get_imm_str_u32(); + sprintf(g_dasm_str, "ori.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_ori_to_ccr(void) +{ + sprintf(g_dasm_str, "ori %s, CCR", get_imm_str_u8()); +} + +static void d68000_ori_to_sr(void) +{ + sprintf(g_dasm_str, "ori %s, SR", get_imm_str_u16()); +} + +static void d68020_pack_rr(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "pack D%d, D%d, %s; (2+)", g_cpu_ir&7, (g_cpu_ir>>9)&7, get_imm_str_u16()); +} + +static void d68020_pack_mm(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "pack -(A%d), -(A%d), %s; (2+)", g_cpu_ir&7, (g_cpu_ir>>9)&7, get_imm_str_u16()); +} + +static void d68000_pea(void) +{ + sprintf(g_dasm_str, "pea %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_reset(void) +{ + sprintf(g_dasm_str, "reset"); +} + +static void d68000_ror_s_8(void) +{ + sprintf(g_dasm_str, "ror.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_ror_s_16(void) +{ + sprintf(g_dasm_str, "ror.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7],g_cpu_ir&7); +} + +static void d68000_ror_s_32(void) +{ + sprintf(g_dasm_str, "ror.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_ror_r_8(void) +{ + sprintf(g_dasm_str, "ror.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_ror_r_16(void) +{ + sprintf(g_dasm_str, "ror.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_ror_r_32(void) +{ + sprintf(g_dasm_str, "ror.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_ror_ea(void) +{ + sprintf(g_dasm_str, "ror.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_rol_s_8(void) +{ + sprintf(g_dasm_str, "rol.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_rol_s_16(void) +{ + sprintf(g_dasm_str, "rol.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_rol_s_32(void) +{ + sprintf(g_dasm_str, "rol.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_rol_r_8(void) +{ + sprintf(g_dasm_str, "rol.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_rol_r_16(void) +{ + sprintf(g_dasm_str, "rol.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_rol_r_32(void) +{ + sprintf(g_dasm_str, "rol.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_rol_ea(void) +{ + sprintf(g_dasm_str, "rol.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_roxr_s_8(void) +{ + sprintf(g_dasm_str, "roxr.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_roxr_s_16(void) +{ + sprintf(g_dasm_str, "roxr.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + + +static void d68000_roxr_s_32(void) +{ + sprintf(g_dasm_str, "roxr.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_roxr_r_8(void) +{ + sprintf(g_dasm_str, "roxr.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxr_r_16(void) +{ + sprintf(g_dasm_str, "roxr.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxr_r_32(void) +{ + sprintf(g_dasm_str, "roxr.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxr_ea(void) +{ + sprintf(g_dasm_str, "roxr.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_roxl_s_8(void) +{ + sprintf(g_dasm_str, "roxl.b #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_roxl_s_16(void) +{ + sprintf(g_dasm_str, "roxl.w #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_roxl_s_32(void) +{ + sprintf(g_dasm_str, "roxl.l #%d, D%d", g_3bit_qdata_table[(g_cpu_ir>>9)&7], g_cpu_ir&7); +} + +static void d68000_roxl_r_8(void) +{ + sprintf(g_dasm_str, "roxl.b D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxl_r_16(void) +{ + sprintf(g_dasm_str, "roxl.w D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxl_r_32(void) +{ + sprintf(g_dasm_str, "roxl.l D%d, D%d", (g_cpu_ir>>9)&7, g_cpu_ir&7); +} + +static void d68000_roxl_ea(void) +{ + sprintf(g_dasm_str, "roxl.w %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68010_rtd(void) +{ + LIMIT_CPU_TYPES(M68010_PLUS); + sprintf(g_dasm_str, "rtd %s; (1+)", get_imm_str_s16()); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OUT); +} + +static void d68000_rte(void) +{ + sprintf(g_dasm_str, "rte"); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OUT); +} + +static void d68020_rtm(void) +{ + LIMIT_CPU_TYPES(M68020_ONLY); + sprintf(g_dasm_str, "rtm %c%d; (2+)", BIT_3(g_cpu_ir) ? 'A' : 'D', g_cpu_ir&7); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OUT); +} + +static void d68000_rtr(void) +{ + sprintf(g_dasm_str, "rtr"); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OUT); +} + +static void d68000_rts(void) +{ + sprintf(g_dasm_str, "rts"); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OUT); +} + +static void d68000_sbcd_rr(void) +{ + sprintf(g_dasm_str, "sbcd D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_sbcd_mm(void) +{ + sprintf(g_dasm_str, "sbcd -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_scc(void) +{ + sprintf(g_dasm_str, "s%-2s %s", g_cc[(g_cpu_ir>>8)&0xf], get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_stop(void) +{ + sprintf(g_dasm_str, "stop %s", get_imm_str_s16()); +} + +static void d68000_sub_er_8(void) +{ + sprintf(g_dasm_str, "sub.b %s, D%d", get_ea_mode_str_8(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_sub_er_16(void) +{ + sprintf(g_dasm_str, "sub.w %s, D%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_sub_er_32(void) +{ + sprintf(g_dasm_str, "sub.l %s, D%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_sub_re_8(void) +{ + sprintf(g_dasm_str, "sub.b D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_sub_re_16(void) +{ + sprintf(g_dasm_str, "sub.w D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_sub_re_32(void) +{ + sprintf(g_dasm_str, "sub.l D%d, %s", (g_cpu_ir>>9)&7, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_suba_16(void) +{ + sprintf(g_dasm_str, "suba.w %s, A%d", get_ea_mode_str_16(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_suba_32(void) +{ + sprintf(g_dasm_str, "suba.l %s, A%d", get_ea_mode_str_32(g_cpu_ir), (g_cpu_ir>>9)&7); +} + +static void d68000_subi_8(void) +{ + char* str = get_imm_str_s8(); + sprintf(g_dasm_str, "subi.b %s, %s", str, get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_subi_16(void) +{ + char* str = get_imm_str_s16(); + sprintf(g_dasm_str, "subi.w %s, %s", str, get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_subi_32(void) +{ + char* str = get_imm_str_s32(); + sprintf(g_dasm_str, "subi.l %s, %s", str, get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_subq_8(void) +{ + sprintf(g_dasm_str, "subq.b #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_subq_16(void) +{ + sprintf(g_dasm_str, "subq.w #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_subq_32(void) +{ + sprintf(g_dasm_str, "subq.l #%d, %s", g_3bit_qdata_table[(g_cpu_ir>>9)&7], get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_subx_rr_8(void) +{ + sprintf(g_dasm_str, "subx.b D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_subx_rr_16(void) +{ + sprintf(g_dasm_str, "subx.w D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_subx_rr_32(void) +{ + sprintf(g_dasm_str, "subx.l D%d, D%d", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_subx_mm_8(void) +{ + sprintf(g_dasm_str, "subx.b -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_subx_mm_16(void) +{ + sprintf(g_dasm_str, "subx.w -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_subx_mm_32(void) +{ + sprintf(g_dasm_str, "subx.l -(A%d), -(A%d)", g_cpu_ir&7, (g_cpu_ir>>9)&7); +} + +static void d68000_swap(void) +{ + sprintf(g_dasm_str, "swap D%d", g_cpu_ir&7); +} + +static void d68000_tas(void) +{ + sprintf(g_dasm_str, "tas %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_trap(void) +{ + sprintf(g_dasm_str, "trap #$%x", g_cpu_ir&0xf); +} + +static void d68020_trapcc_0(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "trap%-2s; (2+)", g_cc[(g_cpu_ir>>8)&0xf]); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68020_trapcc_16(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "trap%-2s %s; (2+)", g_cc[(g_cpu_ir>>8)&0xf], get_imm_str_u16()); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68020_trapcc_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "trap%-2s %s; (2+)", g_cc[(g_cpu_ir>>8)&0xf], get_imm_str_u32()); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_trapv(void) +{ + sprintf(g_dasm_str, "trapv"); + SET_OPCODE_FLAGS(DASMFLAG_STEP_OVER); +} + +static void d68000_tst_8(void) +{ + sprintf(g_dasm_str, "tst.b %s", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_tst_pcdi_8(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.b %s; (2+)", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_tst_pcix_8(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.b %s; (2+)", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68020_tst_i_8(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.b %s; (2+)", get_ea_mode_str_8(g_cpu_ir)); +} + +static void d68000_tst_16(void) +{ + sprintf(g_dasm_str, "tst.w %s", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_tst_a_16(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.w %s; (2+)", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_tst_pcdi_16(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.w %s; (2+)", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_tst_pcix_16(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.w %s; (2+)", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68020_tst_i_16(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.w %s; (2+)", get_ea_mode_str_16(g_cpu_ir)); +} + +static void d68000_tst_32(void) +{ + sprintf(g_dasm_str, "tst.l %s", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_tst_a_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.l %s; (2+)", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_tst_pcdi_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.l %s; (2+)", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_tst_pcix_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.l %s; (2+)", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68020_tst_i_32(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "tst.l %s; (2+)", get_ea_mode_str_32(g_cpu_ir)); +} + +static void d68000_unlk(void) +{ + sprintf(g_dasm_str, "unlk A%d", g_cpu_ir&7); +} + +static void d68020_unpk_rr(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "unpk D%d, D%d, %s; (2+)", g_cpu_ir&7, (g_cpu_ir>>9)&7, get_imm_str_u16()); +} + +static void d68020_unpk_mm(void) +{ + LIMIT_CPU_TYPES(M68020_PLUS); + sprintf(g_dasm_str, "unpk -(A%d), -(A%d), %s; (2+)", g_cpu_ir&7, (g_cpu_ir>>9)&7, get_imm_str_u16()); +} + + + +/* ======================================================================== */ +/* ======================= INSTRUCTION TABLE BUILDER ====================== */ +/* ======================================================================== */ + +/* EA Masks: +800 = data register direct +400 = address register direct +200 = address register indirect +100 = ARI postincrement + 80 = ARI pre-decrement + 40 = ARI displacement + 20 = ARI index + 10 = absolute short + 8 = absolute long + 4 = immediate / sr + 2 = pc displacement + 1 = pc idx +*/ + +static opcode_struct g_opcode_info[] = +{ +/* opcode handler mask match ea mask */ + {d68000_1010 , 0xf000, 0xa000, 0x000}, + {d68000_1111 , 0xf000, 0xf000, 0x000}, + {d68000_abcd_rr , 0xf1f8, 0xc100, 0x000}, + {d68000_abcd_mm , 0xf1f8, 0xc108, 0x000}, + {d68000_add_er_8 , 0xf1c0, 0xd000, 0xbff}, + {d68000_add_er_16 , 0xf1c0, 0xd040, 0xfff}, + {d68000_add_er_32 , 0xf1c0, 0xd080, 0xfff}, + {d68000_add_re_8 , 0xf1c0, 0xd100, 0x3f8}, + {d68000_add_re_16 , 0xf1c0, 0xd140, 0x3f8}, + {d68000_add_re_32 , 0xf1c0, 0xd180, 0x3f8}, + {d68000_adda_16 , 0xf1c0, 0xd0c0, 0xfff}, + {d68000_adda_32 , 0xf1c0, 0xd1c0, 0xfff}, + {d68000_addi_8 , 0xffc0, 0x0600, 0xbf8}, + {d68000_addi_16 , 0xffc0, 0x0640, 0xbf8}, + {d68000_addi_32 , 0xffc0, 0x0680, 0xbf8}, + {d68000_addq_8 , 0xf1c0, 0x5000, 0xbf8}, + {d68000_addq_16 , 0xf1c0, 0x5040, 0xff8}, + {d68000_addq_32 , 0xf1c0, 0x5080, 0xff8}, + {d68000_addx_rr_8 , 0xf1f8, 0xd100, 0x000}, + {d68000_addx_rr_16 , 0xf1f8, 0xd140, 0x000}, + {d68000_addx_rr_32 , 0xf1f8, 0xd180, 0x000}, + {d68000_addx_mm_8 , 0xf1f8, 0xd108, 0x000}, + {d68000_addx_mm_16 , 0xf1f8, 0xd148, 0x000}, + {d68000_addx_mm_32 , 0xf1f8, 0xd188, 0x000}, + {d68000_and_er_8 , 0xf1c0, 0xc000, 0xbff}, + {d68000_and_er_16 , 0xf1c0, 0xc040, 0xbff}, + {d68000_and_er_32 , 0xf1c0, 0xc080, 0xbff}, + {d68000_and_re_8 , 0xf1c0, 0xc100, 0x3f8}, + {d68000_and_re_16 , 0xf1c0, 0xc140, 0x3f8}, + {d68000_and_re_32 , 0xf1c0, 0xc180, 0x3f8}, + {d68000_andi_to_ccr , 0xffff, 0x023c, 0x000}, + {d68000_andi_to_sr , 0xffff, 0x027c, 0x000}, + {d68000_andi_8 , 0xffc0, 0x0200, 0xbf8}, + {d68000_andi_16 , 0xffc0, 0x0240, 0xbf8}, + {d68000_andi_32 , 0xffc0, 0x0280, 0xbf8}, + {d68000_asr_s_8 , 0xf1f8, 0xe000, 0x000}, + {d68000_asr_s_16 , 0xf1f8, 0xe040, 0x000}, + {d68000_asr_s_32 , 0xf1f8, 0xe080, 0x000}, + {d68000_asr_r_8 , 0xf1f8, 0xe020, 0x000}, + {d68000_asr_r_16 , 0xf1f8, 0xe060, 0x000}, + {d68000_asr_r_32 , 0xf1f8, 0xe0a0, 0x000}, + {d68000_asr_ea , 0xffc0, 0xe0c0, 0x3f8}, + {d68000_asl_s_8 , 0xf1f8, 0xe100, 0x000}, + {d68000_asl_s_16 , 0xf1f8, 0xe140, 0x000}, + {d68000_asl_s_32 , 0xf1f8, 0xe180, 0x000}, + {d68000_asl_r_8 , 0xf1f8, 0xe120, 0x000}, + {d68000_asl_r_16 , 0xf1f8, 0xe160, 0x000}, + {d68000_asl_r_32 , 0xf1f8, 0xe1a0, 0x000}, + {d68000_asl_ea , 0xffc0, 0xe1c0, 0x3f8}, + {d68000_bcc_8 , 0xf000, 0x6000, 0x000}, + {d68000_bcc_16 , 0xf0ff, 0x6000, 0x000}, + {d68020_bcc_32 , 0xf0ff, 0x60ff, 0x000}, + {d68000_bchg_r , 0xf1c0, 0x0140, 0xbf8}, + {d68000_bchg_s , 0xffc0, 0x0840, 0xbf8}, + {d68000_bclr_r , 0xf1c0, 0x0180, 0xbf8}, + {d68000_bclr_s , 0xffc0, 0x0880, 0xbf8}, + {d68020_bfchg , 0xffc0, 0xeac0, 0xa78}, + {d68020_bfclr , 0xffc0, 0xecc0, 0xa78}, + {d68020_bfexts , 0xffc0, 0xebc0, 0xa7b}, + {d68020_bfextu , 0xffc0, 0xe9c0, 0xa7b}, + {d68020_bfffo , 0xffc0, 0xedc0, 0xa7b}, + {d68020_bfins , 0xffc0, 0xefc0, 0xa78}, + {d68020_bfset , 0xffc0, 0xeec0, 0xa78}, + {d68020_bftst , 0xffc0, 0xe8c0, 0xa7b}, + {d68010_bkpt , 0xfff8, 0x4848, 0x000}, + {d68000_bra_8 , 0xff00, 0x6000, 0x000}, + {d68000_bra_16 , 0xffff, 0x6000, 0x000}, + {d68020_bra_32 , 0xffff, 0x60ff, 0x000}, + {d68000_bset_r , 0xf1c0, 0x01c0, 0xbf8}, + {d68000_bset_s , 0xffc0, 0x08c0, 0xbf8}, + {d68000_bsr_8 , 0xff00, 0x6100, 0x000}, + {d68000_bsr_16 , 0xffff, 0x6100, 0x000}, + {d68020_bsr_32 , 0xffff, 0x61ff, 0x000}, + {d68000_btst_r , 0xf1c0, 0x0100, 0xbff}, + {d68000_btst_s , 0xffc0, 0x0800, 0xbfb}, + {d68020_callm , 0xffc0, 0x06c0, 0x27b}, + {d68020_cas_8 , 0xffc0, 0x0ac0, 0x3f8}, + {d68020_cas_16 , 0xffc0, 0x0cc0, 0x3f8}, + {d68020_cas_32 , 0xffc0, 0x0ec0, 0x3f8}, + {d68020_cas2_16 , 0xffff, 0x0cfc, 0x000}, + {d68020_cas2_32 , 0xffff, 0x0efc, 0x000}, + {d68000_chk_16 , 0xf1c0, 0x4180, 0xbff}, + {d68020_chk_32 , 0xf1c0, 0x4100, 0xbff}, + {d68020_chk2_cmp2_8 , 0xffc0, 0x00c0, 0x27b}, + {d68020_chk2_cmp2_16 , 0xffc0, 0x02c0, 0x27b}, + {d68020_chk2_cmp2_32 , 0xffc0, 0x04c0, 0x27b}, + {d68040_cinv , 0xff20, 0xf400, 0x000}, + {d68000_clr_8 , 0xffc0, 0x4200, 0xbf8}, + {d68000_clr_16 , 0xffc0, 0x4240, 0xbf8}, + {d68000_clr_32 , 0xffc0, 0x4280, 0xbf8}, + {d68000_cmp_8 , 0xf1c0, 0xb000, 0xbff}, + {d68000_cmp_16 , 0xf1c0, 0xb040, 0xfff}, + {d68000_cmp_32 , 0xf1c0, 0xb080, 0xfff}, + {d68000_cmpa_16 , 0xf1c0, 0xb0c0, 0xfff}, + {d68000_cmpa_32 , 0xf1c0, 0xb1c0, 0xfff}, + {d68000_cmpi_8 , 0xffc0, 0x0c00, 0xbf8}, + {d68020_cmpi_pcdi_8 , 0xffff, 0x0c3a, 0x000}, + {d68020_cmpi_pcix_8 , 0xffff, 0x0c3b, 0x000}, + {d68000_cmpi_16 , 0xffc0, 0x0c40, 0xbf8}, + {d68020_cmpi_pcdi_16 , 0xffff, 0x0c7a, 0x000}, + {d68020_cmpi_pcix_16 , 0xffff, 0x0c7b, 0x000}, + {d68000_cmpi_32 , 0xffc0, 0x0c80, 0xbf8}, + {d68020_cmpi_pcdi_32 , 0xffff, 0x0cba, 0x000}, + {d68020_cmpi_pcix_32 , 0xffff, 0x0cbb, 0x000}, + {d68000_cmpm_8 , 0xf1f8, 0xb108, 0x000}, + {d68000_cmpm_16 , 0xf1f8, 0xb148, 0x000}, + {d68000_cmpm_32 , 0xf1f8, 0xb188, 0x000}, + {d68020_cpbcc_16 , 0xf1c0, 0xf080, 0x000}, + {d68020_cpbcc_32 , 0xf1c0, 0xf0c0, 0x000}, + {d68020_cpdbcc , 0xf1f8, 0xf048, 0x000}, + {d68020_cpgen , 0xf1c0, 0xf000, 0x000}, + {d68020_cprestore , 0xf1c0, 0xf140, 0x37f}, + {d68020_cpsave , 0xf1c0, 0xf100, 0x2f8}, + {d68020_cpscc , 0xf1c0, 0xf040, 0xbf8}, + {d68020_cptrapcc_0 , 0xf1ff, 0xf07c, 0x000}, + {d68020_cptrapcc_16 , 0xf1ff, 0xf07a, 0x000}, + {d68020_cptrapcc_32 , 0xf1ff, 0xf07b, 0x000}, + {d68040_cpush , 0xff20, 0xf420, 0x000}, + {d68000_dbcc , 0xf0f8, 0x50c8, 0x000}, + {d68000_dbra , 0xfff8, 0x51c8, 0x000}, + {d68000_divs , 0xf1c0, 0x81c0, 0xbff}, + {d68000_divu , 0xf1c0, 0x80c0, 0xbff}, + {d68020_divl , 0xffc0, 0x4c40, 0xbff}, + {d68000_eor_8 , 0xf1c0, 0xb100, 0xbf8}, + {d68000_eor_16 , 0xf1c0, 0xb140, 0xbf8}, + {d68000_eor_32 , 0xf1c0, 0xb180, 0xbf8}, + {d68000_eori_to_ccr , 0xffff, 0x0a3c, 0x000}, + {d68000_eori_to_sr , 0xffff, 0x0a7c, 0x000}, + {d68000_eori_8 , 0xffc0, 0x0a00, 0xbf8}, + {d68000_eori_16 , 0xffc0, 0x0a40, 0xbf8}, + {d68000_eori_32 , 0xffc0, 0x0a80, 0xbf8}, + {d68000_exg_dd , 0xf1f8, 0xc140, 0x000}, + {d68000_exg_aa , 0xf1f8, 0xc148, 0x000}, + {d68000_exg_da , 0xf1f8, 0xc188, 0x000}, + {d68020_extb_32 , 0xfff8, 0x49c0, 0x000}, + {d68000_ext_16 , 0xfff8, 0x4880, 0x000}, + {d68000_ext_32 , 0xfff8, 0x48c0, 0x000}, + {d68000_illegal , 0xffff, 0x4afc, 0x000}, + {d68000_jmp , 0xffc0, 0x4ec0, 0x27b}, + {d68000_jsr , 0xffc0, 0x4e80, 0x27b}, + {d68000_lea , 0xf1c0, 0x41c0, 0x27b}, + {d68000_link_16 , 0xfff8, 0x4e50, 0x000}, + {d68020_link_32 , 0xfff8, 0x4808, 0x000}, + {d68000_lsr_s_8 , 0xf1f8, 0xe008, 0x000}, + {d68000_lsr_s_16 , 0xf1f8, 0xe048, 0x000}, + {d68000_lsr_s_32 , 0xf1f8, 0xe088, 0x000}, + {d68000_lsr_r_8 , 0xf1f8, 0xe028, 0x000}, + {d68000_lsr_r_16 , 0xf1f8, 0xe068, 0x000}, + {d68000_lsr_r_32 , 0xf1f8, 0xe0a8, 0x000}, + {d68000_lsr_ea , 0xffc0, 0xe2c0, 0x3f8}, + {d68000_lsl_s_8 , 0xf1f8, 0xe108, 0x000}, + {d68000_lsl_s_16 , 0xf1f8, 0xe148, 0x000}, + {d68000_lsl_s_32 , 0xf1f8, 0xe188, 0x000}, + {d68000_lsl_r_8 , 0xf1f8, 0xe128, 0x000}, + {d68000_lsl_r_16 , 0xf1f8, 0xe168, 0x000}, + {d68000_lsl_r_32 , 0xf1f8, 0xe1a8, 0x000}, + {d68000_lsl_ea , 0xffc0, 0xe3c0, 0x3f8}, + {d68000_move_8 , 0xf000, 0x1000, 0xbff}, + {d68000_move_16 , 0xf000, 0x3000, 0xfff}, + {d68000_move_32 , 0xf000, 0x2000, 0xfff}, + {d68000_movea_16 , 0xf1c0, 0x3040, 0xfff}, + {d68000_movea_32 , 0xf1c0, 0x2040, 0xfff}, + {d68000_move_to_ccr , 0xffc0, 0x44c0, 0xbff}, + {d68010_move_fr_ccr , 0xffc0, 0x42c0, 0xbf8}, + {d68000_move_to_sr , 0xffc0, 0x46c0, 0xbff}, + {d68000_move_fr_sr , 0xffc0, 0x40c0, 0xbf8}, + {d68000_move_to_usp , 0xfff8, 0x4e60, 0x000}, + {d68000_move_fr_usp , 0xfff8, 0x4e68, 0x000}, + {d68010_movec , 0xfffe, 0x4e7a, 0x000}, + {d68000_movem_pd_16 , 0xfff8, 0x48a0, 0x000}, + {d68000_movem_pd_32 , 0xfff8, 0x48e0, 0x000}, + {d68000_movem_re_16 , 0xffc0, 0x4880, 0x2f8}, + {d68000_movem_re_32 , 0xffc0, 0x48c0, 0x2f8}, + {d68000_movem_er_16 , 0xffc0, 0x4c80, 0x37b}, + {d68000_movem_er_32 , 0xffc0, 0x4cc0, 0x37b}, + {d68000_movep_er_16 , 0xf1f8, 0x0108, 0x000}, + {d68000_movep_er_32 , 0xf1f8, 0x0148, 0x000}, + {d68000_movep_re_16 , 0xf1f8, 0x0188, 0x000}, + {d68000_movep_re_32 , 0xf1f8, 0x01c8, 0x000}, + {d68010_moves_8 , 0xffc0, 0x0e00, 0x3f8}, + {d68010_moves_16 , 0xffc0, 0x0e40, 0x3f8}, + {d68010_moves_32 , 0xffc0, 0x0e80, 0x3f8}, + {d68000_moveq , 0xf100, 0x7000, 0x000}, + {d68040_move16_pi_pi , 0xfff8, 0xf620, 0x000}, + {d68040_move16_pi_al , 0xfff8, 0xf600, 0x000}, + {d68040_move16_al_pi , 0xfff8, 0xf608, 0x000}, + {d68040_move16_ai_al , 0xfff8, 0xf610, 0x000}, + {d68040_move16_al_ai , 0xfff8, 0xf618, 0x000}, + {d68000_muls , 0xf1c0, 0xc1c0, 0xbff}, + {d68000_mulu , 0xf1c0, 0xc0c0, 0xbff}, + {d68020_mull , 0xffc0, 0x4c00, 0xbff}, + {d68000_nbcd , 0xffc0, 0x4800, 0xbf8}, + {d68000_neg_8 , 0xffc0, 0x4400, 0xbf8}, + {d68000_neg_16 , 0xffc0, 0x4440, 0xbf8}, + {d68000_neg_32 , 0xffc0, 0x4480, 0xbf8}, + {d68000_negx_8 , 0xffc0, 0x4000, 0xbf8}, + {d68000_negx_16 , 0xffc0, 0x4040, 0xbf8}, + {d68000_negx_32 , 0xffc0, 0x4080, 0xbf8}, + {d68000_nop , 0xffff, 0x4e71, 0x000}, + {d68000_not_8 , 0xffc0, 0x4600, 0xbf8}, + {d68000_not_16 , 0xffc0, 0x4640, 0xbf8}, + {d68000_not_32 , 0xffc0, 0x4680, 0xbf8}, + {d68000_or_er_8 , 0xf1c0, 0x8000, 0xbff}, + {d68000_or_er_16 , 0xf1c0, 0x8040, 0xbff}, + {d68000_or_er_32 , 0xf1c0, 0x8080, 0xbff}, + {d68000_or_re_8 , 0xf1c0, 0x8100, 0x3f8}, + {d68000_or_re_16 , 0xf1c0, 0x8140, 0x3f8}, + {d68000_or_re_32 , 0xf1c0, 0x8180, 0x3f8}, + {d68000_ori_to_ccr , 0xffff, 0x003c, 0x000}, + {d68000_ori_to_sr , 0xffff, 0x007c, 0x000}, + {d68000_ori_8 , 0xffc0, 0x0000, 0xbf8}, + {d68000_ori_16 , 0xffc0, 0x0040, 0xbf8}, + {d68000_ori_32 , 0xffc0, 0x0080, 0xbf8}, + {d68020_pack_rr , 0xf1f8, 0x8140, 0x000}, + {d68020_pack_mm , 0xf1f8, 0x8148, 0x000}, + {d68000_pea , 0xffc0, 0x4840, 0x27b}, + {d68000_reset , 0xffff, 0x4e70, 0x000}, + {d68000_ror_s_8 , 0xf1f8, 0xe018, 0x000}, + {d68000_ror_s_16 , 0xf1f8, 0xe058, 0x000}, + {d68000_ror_s_32 , 0xf1f8, 0xe098, 0x000}, + {d68000_ror_r_8 , 0xf1f8, 0xe038, 0x000}, + {d68000_ror_r_16 , 0xf1f8, 0xe078, 0x000}, + {d68000_ror_r_32 , 0xf1f8, 0xe0b8, 0x000}, + {d68000_ror_ea , 0xffc0, 0xe6c0, 0x3f8}, + {d68000_rol_s_8 , 0xf1f8, 0xe118, 0x000}, + {d68000_rol_s_16 , 0xf1f8, 0xe158, 0x000}, + {d68000_rol_s_32 , 0xf1f8, 0xe198, 0x000}, + {d68000_rol_r_8 , 0xf1f8, 0xe138, 0x000}, + {d68000_rol_r_16 , 0xf1f8, 0xe178, 0x000}, + {d68000_rol_r_32 , 0xf1f8, 0xe1b8, 0x000}, + {d68000_rol_ea , 0xffc0, 0xe7c0, 0x3f8}, + {d68000_roxr_s_8 , 0xf1f8, 0xe010, 0x000}, + {d68000_roxr_s_16 , 0xf1f8, 0xe050, 0x000}, + {d68000_roxr_s_32 , 0xf1f8, 0xe090, 0x000}, + {d68000_roxr_r_8 , 0xf1f8, 0xe030, 0x000}, + {d68000_roxr_r_16 , 0xf1f8, 0xe070, 0x000}, + {d68000_roxr_r_32 , 0xf1f8, 0xe0b0, 0x000}, + {d68000_roxr_ea , 0xffc0, 0xe4c0, 0x3f8}, + {d68000_roxl_s_8 , 0xf1f8, 0xe110, 0x000}, + {d68000_roxl_s_16 , 0xf1f8, 0xe150, 0x000}, + {d68000_roxl_s_32 , 0xf1f8, 0xe190, 0x000}, + {d68000_roxl_r_8 , 0xf1f8, 0xe130, 0x000}, + {d68000_roxl_r_16 , 0xf1f8, 0xe170, 0x000}, + {d68000_roxl_r_32 , 0xf1f8, 0xe1b0, 0x000}, + {d68000_roxl_ea , 0xffc0, 0xe5c0, 0x3f8}, + {d68010_rtd , 0xffff, 0x4e74, 0x000}, + {d68000_rte , 0xffff, 0x4e73, 0x000}, + {d68020_rtm , 0xfff0, 0x06c0, 0x000}, + {d68000_rtr , 0xffff, 0x4e77, 0x000}, + {d68000_rts , 0xffff, 0x4e75, 0x000}, + {d68000_sbcd_rr , 0xf1f8, 0x8100, 0x000}, + {d68000_sbcd_mm , 0xf1f8, 0x8108, 0x000}, + {d68000_scc , 0xf0c0, 0x50c0, 0xbf8}, + {d68000_stop , 0xffff, 0x4e72, 0x000}, + {d68000_sub_er_8 , 0xf1c0, 0x9000, 0xbff}, + {d68000_sub_er_16 , 0xf1c0, 0x9040, 0xfff}, + {d68000_sub_er_32 , 0xf1c0, 0x9080, 0xfff}, + {d68000_sub_re_8 , 0xf1c0, 0x9100, 0x3f8}, + {d68000_sub_re_16 , 0xf1c0, 0x9140, 0x3f8}, + {d68000_sub_re_32 , 0xf1c0, 0x9180, 0x3f8}, + {d68000_suba_16 , 0xf1c0, 0x90c0, 0xfff}, + {d68000_suba_32 , 0xf1c0, 0x91c0, 0xfff}, + {d68000_subi_8 , 0xffc0, 0x0400, 0xbf8}, + {d68000_subi_16 , 0xffc0, 0x0440, 0xbf8}, + {d68000_subi_32 , 0xffc0, 0x0480, 0xbf8}, + {d68000_subq_8 , 0xf1c0, 0x5100, 0xbf8}, + {d68000_subq_16 , 0xf1c0, 0x5140, 0xff8}, + {d68000_subq_32 , 0xf1c0, 0x5180, 0xff8}, + {d68000_subx_rr_8 , 0xf1f8, 0x9100, 0x000}, + {d68000_subx_rr_16 , 0xf1f8, 0x9140, 0x000}, + {d68000_subx_rr_32 , 0xf1f8, 0x9180, 0x000}, + {d68000_subx_mm_8 , 0xf1f8, 0x9108, 0x000}, + {d68000_subx_mm_16 , 0xf1f8, 0x9148, 0x000}, + {d68000_subx_mm_32 , 0xf1f8, 0x9188, 0x000}, + {d68000_swap , 0xfff8, 0x4840, 0x000}, + {d68000_tas , 0xffc0, 0x4ac0, 0xbf8}, + {d68000_trap , 0xfff0, 0x4e40, 0x000}, + {d68020_trapcc_0 , 0xf0ff, 0x50fc, 0x000}, + {d68020_trapcc_16 , 0xf0ff, 0x50fa, 0x000}, + {d68020_trapcc_32 , 0xf0ff, 0x50fb, 0x000}, + {d68000_trapv , 0xffff, 0x4e76, 0x000}, + {d68000_tst_8 , 0xffc0, 0x4a00, 0xbf8}, + {d68020_tst_pcdi_8 , 0xffff, 0x4a3a, 0x000}, + {d68020_tst_pcix_8 , 0xffff, 0x4a3b, 0x000}, + {d68020_tst_i_8 , 0xffff, 0x4a3c, 0x000}, + {d68000_tst_16 , 0xffc0, 0x4a40, 0xbf8}, + {d68020_tst_a_16 , 0xfff8, 0x4a48, 0x000}, + {d68020_tst_pcdi_16 , 0xffff, 0x4a7a, 0x000}, + {d68020_tst_pcix_16 , 0xffff, 0x4a7b, 0x000}, + {d68020_tst_i_16 , 0xffff, 0x4a7c, 0x000}, + {d68000_tst_32 , 0xffc0, 0x4a80, 0xbf8}, + {d68020_tst_a_32 , 0xfff8, 0x4a88, 0x000}, + {d68020_tst_pcdi_32 , 0xffff, 0x4aba, 0x000}, + {d68020_tst_pcix_32 , 0xffff, 0x4abb, 0x000}, + {d68020_tst_i_32 , 0xffff, 0x4abc, 0x000}, + {d68000_unlk , 0xfff8, 0x4e58, 0x000}, + {d68020_unpk_rr , 0xf1f8, 0x8180, 0x000}, + {d68020_unpk_mm , 0xf1f8, 0x8188, 0x000}, + {0, 0, 0, 0} +}; + +/* Check if opcode is using a valid ea mode */ +static int valid_ea(uint opcode, uint mask) +{ + if(mask == 0) + return 1; + + switch(opcode & 0x3f) + { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + return (mask & 0x800) != 0; + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + return (mask & 0x400) != 0; + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + return (mask & 0x200) != 0; + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + return (mask & 0x100) != 0; + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + return (mask & 0x080) != 0; + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + return (mask & 0x040) != 0; + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + return (mask & 0x020) != 0; + case 0x38: + return (mask & 0x010) != 0; + case 0x39: + return (mask & 0x008) != 0; + case 0x3a: + return (mask & 0x002) != 0; + case 0x3b: + return (mask & 0x001) != 0; + case 0x3c: + return (mask & 0x004) != 0; + } + return 0; + +} + +/* Used by qsort */ +static int DECL_SPEC compare_nof_true_bits(const void *aptr, const void *bptr) +{ + uint a = ((const opcode_struct*)aptr)->mask; + uint b = ((const opcode_struct*)bptr)->mask; + + a = ((a & 0xAAAA) >> 1) + (a & 0x5555); + a = ((a & 0xCCCC) >> 2) + (a & 0x3333); + a = ((a & 0xF0F0) >> 4) + (a & 0x0F0F); + a = ((a & 0xFF00) >> 8) + (a & 0x00FF); + + b = ((b & 0xAAAA) >> 1) + (b & 0x5555); + b = ((b & 0xCCCC) >> 2) + (b & 0x3333); + b = ((b & 0xF0F0) >> 4) + (b & 0x0F0F); + b = ((b & 0xFF00) >> 8) + (b & 0x00FF); + + return b - a; /* reversed to get greatest to least sorting */ +} + +/* build the opcode handler jump table */ +static void build_opcode_table(void) +{ + uint i; + uint opcode; + opcode_struct* ostruct; + uint opcode_info_length = 0; + + for(ostruct = g_opcode_info;ostruct->opcode_handler != 0;ostruct++) + opcode_info_length++; + + qsort((void *)g_opcode_info, opcode_info_length, sizeof(g_opcode_info[0]), compare_nof_true_bits); + + for(i=0;i<0x10000;i++) + { + g_instruction_table[i] = d68000_illegal; /* default to illegal */ + opcode = i; + /* search through opcode info for a match */ + for(ostruct = g_opcode_info;ostruct->opcode_handler != 0;ostruct++) + { + /* match opcode mask and allowed ea modes */ + if((opcode & ostruct->mask) == ostruct->match) + { + /* Handle destination ea for move instructions */ + if((ostruct->opcode_handler == d68000_move_8 || + ostruct->opcode_handler == d68000_move_16 || + ostruct->opcode_handler == d68000_move_32) && + !valid_ea(((opcode>>9)&7) | ((opcode>>3)&0x38), 0xbf8)) + continue; + if(valid_ea(opcode, ostruct->ea_mask)) + { + g_instruction_table[i] = ostruct->opcode_handler; + break; + } + } + } + } +} + + + +/* ======================================================================== */ +/* ================================= API ================================== */ +/* ======================================================================== */ + +/* Disasemble one instruction at pc and store in str_buff */ +unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type) +{ + if(!g_initialized) + { + build_opcode_table(); + g_initialized = 1; + } + switch(cpu_type) + { + case M68K_CPU_TYPE_68000: + g_cpu_type = TYPE_68000; + g_address_mask = 0x00ffffff; + break; + case M68K_CPU_TYPE_68008: + g_cpu_type = TYPE_68008; + g_address_mask = 0x003fffff; + break; + case M68K_CPU_TYPE_68010: + g_cpu_type = TYPE_68010; + g_address_mask = 0x00ffffff; + break; + case M68K_CPU_TYPE_68EC020: + g_cpu_type = TYPE_68020; + g_address_mask = 0x00ffffff; + break; + case M68K_CPU_TYPE_68020: + g_cpu_type = TYPE_68020; + g_address_mask = 0xffffffff; + break; + case M68K_CPU_TYPE_68030: + g_cpu_type = TYPE_68030; + g_address_mask = 0xffffffff; + break; + case M68K_CPU_TYPE_68040: + g_cpu_type = TYPE_68040; + g_address_mask = 0xffffffff; + break; + default: + return 0; + } + + g_cpu_pc = pc; + g_helper_str[0] = 0; + g_cpu_ir = read_imm_16(); + g_opcode_type = 0; + g_instruction_table[g_cpu_ir](); + sprintf(str_buff, "%s%s", g_dasm_str, g_helper_str); + return COMBINE_OPCODE_FLAGS(g_cpu_pc - pc); +} + +char* m68ki_disassemble_quick(unsigned int pc, unsigned int cpu_type) +{ + static char buff[100]; + buff[0] = 0; + m68k_disassemble(buff, pc, cpu_type); + return buff; +} + +/* Check if the instruction is a valid one */ +unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type) +{ + if(!g_initialized) + { + build_opcode_table(); + g_initialized = 1; + } + + instruction &= 0xffff; + if(g_instruction_table[instruction] == d68000_illegal) + return 0; + + switch(cpu_type) + { + case M68K_CPU_TYPE_68000: + case M68K_CPU_TYPE_68008: + if(g_instruction_table[instruction] == d68010_bkpt) + return 0; + if(g_instruction_table[instruction] == d68010_move_fr_ccr) + return 0; + if(g_instruction_table[instruction] == d68010_movec) + return 0; + if(g_instruction_table[instruction] == d68010_moves_8) + return 0; + if(g_instruction_table[instruction] == d68010_moves_16) + return 0; + if(g_instruction_table[instruction] == d68010_moves_32) + return 0; + if(g_instruction_table[instruction] == d68010_rtd) + return 0; + case M68K_CPU_TYPE_68010: + if(g_instruction_table[instruction] == d68020_bcc_32) + return 0; + if(g_instruction_table[instruction] == d68020_bfchg) + return 0; + if(g_instruction_table[instruction] == d68020_bfclr) + return 0; + if(g_instruction_table[instruction] == d68020_bfexts) + return 0; + if(g_instruction_table[instruction] == d68020_bfextu) + return 0; + if(g_instruction_table[instruction] == d68020_bfffo) + return 0; + if(g_instruction_table[instruction] == d68020_bfins) + return 0; + if(g_instruction_table[instruction] == d68020_bfset) + return 0; + if(g_instruction_table[instruction] == d68020_bftst) + return 0; + if(g_instruction_table[instruction] == d68020_bra_32) + return 0; + if(g_instruction_table[instruction] == d68020_bsr_32) + return 0; + if(g_instruction_table[instruction] == d68020_callm) + return 0; + if(g_instruction_table[instruction] == d68020_cas_8) + return 0; + if(g_instruction_table[instruction] == d68020_cas_16) + return 0; + if(g_instruction_table[instruction] == d68020_cas_32) + return 0; + if(g_instruction_table[instruction] == d68020_cas2_16) + return 0; + if(g_instruction_table[instruction] == d68020_cas2_32) + return 0; + if(g_instruction_table[instruction] == d68020_chk_32) + return 0; + if(g_instruction_table[instruction] == d68020_chk2_cmp2_8) + return 0; + if(g_instruction_table[instruction] == d68020_chk2_cmp2_16) + return 0; + if(g_instruction_table[instruction] == d68020_chk2_cmp2_32) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcdi_8) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcix_8) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcdi_16) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcix_16) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcdi_32) + return 0; + if(g_instruction_table[instruction] == d68020_cmpi_pcix_32) + return 0; + if(g_instruction_table[instruction] == d68020_cpbcc_16) + return 0; + if(g_instruction_table[instruction] == d68020_cpbcc_32) + return 0; + if(g_instruction_table[instruction] == d68020_cpdbcc) + return 0; + if(g_instruction_table[instruction] == d68020_cpgen) + return 0; + if(g_instruction_table[instruction] == d68020_cprestore) + return 0; + if(g_instruction_table[instruction] == d68020_cpsave) + return 0; + if(g_instruction_table[instruction] == d68020_cpscc) + return 0; + if(g_instruction_table[instruction] == d68020_cptrapcc_0) + return 0; + if(g_instruction_table[instruction] == d68020_cptrapcc_16) + return 0; + if(g_instruction_table[instruction] == d68020_cptrapcc_32) + return 0; + if(g_instruction_table[instruction] == d68020_divl) + return 0; + if(g_instruction_table[instruction] == d68020_extb_32) + return 0; + if(g_instruction_table[instruction] == d68020_link_32) + return 0; + if(g_instruction_table[instruction] == d68020_mull) + return 0; + if(g_instruction_table[instruction] == d68020_pack_rr) + return 0; + if(g_instruction_table[instruction] == d68020_pack_mm) + return 0; + if(g_instruction_table[instruction] == d68020_rtm) + return 0; + if(g_instruction_table[instruction] == d68020_trapcc_0) + return 0; + if(g_instruction_table[instruction] == d68020_trapcc_16) + return 0; + if(g_instruction_table[instruction] == d68020_trapcc_32) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcdi_8) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcix_8) + return 0; + if(g_instruction_table[instruction] == d68020_tst_i_8) + return 0; + if(g_instruction_table[instruction] == d68020_tst_a_16) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcdi_16) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcix_16) + return 0; + if(g_instruction_table[instruction] == d68020_tst_i_16) + return 0; + if(g_instruction_table[instruction] == d68020_tst_a_32) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcdi_32) + return 0; + if(g_instruction_table[instruction] == d68020_tst_pcix_32) + return 0; + if(g_instruction_table[instruction] == d68020_tst_i_32) + return 0; + if(g_instruction_table[instruction] == d68020_unpk_rr) + return 0; + if(g_instruction_table[instruction] == d68020_unpk_mm) + return 0; + case M68K_CPU_TYPE_68EC020: + case M68K_CPU_TYPE_68020: + case M68K_CPU_TYPE_68030: + if(g_instruction_table[instruction] == d68040_cinv) + return 0; + if(g_instruction_table[instruction] == d68040_cpush) + return 0; + if(g_instruction_table[instruction] == d68040_move16_pi_pi) + return 0; + if(g_instruction_table[instruction] == d68040_move16_pi_al) + return 0; + if(g_instruction_table[instruction] == d68040_move16_al_pi) + return 0; + if(g_instruction_table[instruction] == d68040_move16_ai_al) + return 0; + if(g_instruction_table[instruction] == d68040_move16_al_ai) + return 0; + } + if(cpu_type != M68K_CPU_TYPE_68020 && cpu_type != M68K_CPU_TYPE_68EC020 && + (g_instruction_table[instruction] == d68020_callm || + g_instruction_table[instruction] == d68020_rtm)) + return 0; + + return 1; +} + + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ diff --git a/Src/CPU/68K/Musashi/m68kmake.c b/Src/CPU/68K/Musashi/m68kmake.c new file mode 100644 index 0000000..edf3cc9 --- /dev/null +++ b/Src/CPU/68K/Musashi/m68kmake.c @@ -0,0 +1,1512 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * m68kmake.c + * + * Program to generate the Musashi 68K core. + * + * Permission was obtained from Karl Stenerud to apply the GPL license to this + * code. + */ + +/* ======================================================================== */ +/* ========================= LICENSING & COPYRIGHT ======================== */ +/* ======================================================================== */ +/* + * MUSASHI + * Version 3.3 + * + * A portable Motorola M680x0 processor emulation engine. + * Copyright 1998-2001 Karl Stenerud. All rights reserved. + * + * This code may be freely used for non-commercial purposes as long as this + * copyright notice remains unaltered in the source code and any binary files + * containing this code in compiled form. + * + * All other lisencing terms must be negotiated with the author + * (Karl Stenerud). + * + * The latest version of this code can be obtained at: + * http://kstenerud.cjb.net + */ + +/* + * Modified For OpenVMS By: Robert Alan Byer + * byer@mail.ourservers.net + */ + + +/* ======================================================================== */ +/* ============================ CODE GENERATOR ============================ */ +/* ======================================================================== */ +/* + * This is the code generator program which will generate the opcode table + * and the final opcode handlers. + * + * It requires an input file to function (default m68k_in.c), but you can + * specify your own like so: + * + * m68kmake + * + * where output path is the path where the output files should be placed, and + * input file is the file to use for input. + * + * If you modify the input file greatly from its released form, you may have + * to tweak the configuration section a bit since I'm using static allocation + * to keep things simple. + * + * + * TODO: - build a better code generator for the move instruction. + * - Add callm and rtm instructions + * - Fix RTE to handle other format words + * - Add address error (and bus error?) handling + */ + + +const char* g_version = "3.3"; + +/* ======================================================================== */ +/* =============================== INCLUDES =============================== */ +/* ======================================================================== */ + +#include +#include +#include +#include +#include + + + +/* ======================================================================== */ +/* ============================= CONFIGURATION ============================ */ +/* ======================================================================== */ + +#define M68K_MAX_PATH 1024 +#define M68K_MAX_DIR 1024 + +#define MAX_LINE_LENGTH 200 /* length of 1 line */ +#define MAX_BODY_LENGTH 300 /* Number of lines in 1 function */ +#define MAX_REPLACE_LENGTH 30 /* Max number of replace strings */ +#define MAX_INSERT_LENGTH 5000 /* Max size of insert piece */ +#define MAX_NAME_LENGTH 30 /* Max length of ophandler name */ +#define MAX_SPEC_PROC_LENGTH 4 /* Max length of special processing str */ +#define MAX_SPEC_EA_LENGTH 5 /* Max length of specified EA str */ +#define EA_ALLOWED_LENGTH 11 /* Max length of ea allowed str */ +#define MAX_OPCODE_INPUT_TABLE_LENGTH 1000 /* Max length of opcode handler tbl */ +#define MAX_OPCODE_OUTPUT_TABLE_LENGTH 3000 /* Max length of opcode handler tbl */ + +/* Default filenames */ +#define FILENAME_INPUT "m68k_in.c" +#define FILENAME_PROTOTYPE "m68kops.h" +#define FILENAME_TABLE "m68kops.c" +#define FILENAME_OPS_AC "m68kopac.c" +#define FILENAME_OPS_DM "m68kopdm.c" +#define FILENAME_OPS_NZ "m68kopnz.c" + + +/* Identifier sequences recognized by this program */ + +#define ID_INPUT_SEPARATOR "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + +#define ID_BASE "M68KMAKE" +#define ID_PROTOTYPE_HEADER ID_BASE "_PROTOTYPE_HEADER" +#define ID_PROTOTYPE_FOOTER ID_BASE "_PROTOTYPE_FOOTER" +#define ID_TABLE_HEADER ID_BASE "_TABLE_HEADER" +#define ID_TABLE_FOOTER ID_BASE "_TABLE_FOOTER" +#define ID_TABLE_BODY ID_BASE "_TABLE_BODY" +#define ID_TABLE_START ID_BASE "_TABLE_START" +#define ID_OPHANDLER_HEADER ID_BASE "_OPCODE_HANDLER_HEADER" +#define ID_OPHANDLER_FOOTER ID_BASE "_OPCODE_HANDLER_FOOTER" +#define ID_OPHANDLER_BODY ID_BASE "_OPCODE_HANDLER_BODY" +#define ID_END ID_BASE "_END" + +#define ID_OPHANDLER_NAME ID_BASE "_OP" +#define ID_OPHANDLER_EA_AY_8 ID_BASE "_GET_EA_AY_8" +#define ID_OPHANDLER_EA_AY_16 ID_BASE "_GET_EA_AY_16" +#define ID_OPHANDLER_EA_AY_32 ID_BASE "_GET_EA_AY_32" +#define ID_OPHANDLER_OPER_AY_8 ID_BASE "_GET_OPER_AY_8" +#define ID_OPHANDLER_OPER_AY_16 ID_BASE "_GET_OPER_AY_16" +#define ID_OPHANDLER_OPER_AY_32 ID_BASE "_GET_OPER_AY_32" +#define ID_OPHANDLER_CC ID_BASE "_CC" +#define ID_OPHANDLER_NOT_CC ID_BASE "_NOT_CC" + + +#ifndef DECL_SPEC +#define DECL_SPEC +#endif /* DECL_SPEC */ + + + +/* ======================================================================== */ +/* ============================== PROTOTYPES ============================== */ +/* ======================================================================== */ + +enum { + CPU_TYPE_000 = 0, + CPU_TYPE_010, + CPU_TYPE_020, + NUM_CPUS +}; + +#define UNSPECIFIED "." +#define UNSPECIFIED_CH '.' + +#define HAS_NO_EA_MODE(A) (strcmp(A, "..........") == 0) +#define HAS_EA_AI(A) ((A)[0] == 'A') +#define HAS_EA_PI(A) ((A)[1] == '+') +#define HAS_EA_PD(A) ((A)[2] == '-') +#define HAS_EA_DI(A) ((A)[3] == 'D') +#define HAS_EA_IX(A) ((A)[4] == 'X') +#define HAS_EA_AW(A) ((A)[5] == 'W') +#define HAS_EA_AL(A) ((A)[6] == 'L') +#define HAS_EA_PCDI(A) ((A)[7] == 'd') +#define HAS_EA_PCIX(A) ((A)[8] == 'x') +#define HAS_EA_I(A) ((A)[9] == 'I') + +enum +{ + EA_MODE_NONE, /* No special addressing mode */ + EA_MODE_AI, /* Address register indirect */ + EA_MODE_PI, /* Address register indirect with postincrement */ + EA_MODE_PI7, /* Address register 7 indirect with postincrement */ + EA_MODE_PD, /* Address register indirect with predecrement */ + EA_MODE_PD7, /* Address register 7 indirect with predecrement */ + EA_MODE_DI, /* Address register indirect with displacement */ + EA_MODE_IX, /* Address register indirect with index */ + EA_MODE_AW, /* Absolute word */ + EA_MODE_AL, /* Absolute long */ + EA_MODE_PCDI, /* Program counter indirect with displacement */ + EA_MODE_PCIX, /* Program counter indirect with index */ + EA_MODE_I /* Immediate */ +}; + + +/* Everything we need to know about an opcode */ +typedef struct +{ + char name[MAX_NAME_LENGTH]; /* opcode handler name */ + unsigned char size; /* Size of operation */ + char spec_proc[MAX_SPEC_PROC_LENGTH]; /* Special processing mode */ + char spec_ea[MAX_SPEC_EA_LENGTH]; /* Specified effective addressing mode */ + unsigned char bits; /* Number of significant bits (used for sorting the table) */ + unsigned short op_mask; /* Mask to apply for matching an opcode to a handler */ + unsigned short op_match; /* Value to match after masking */ + char ea_allowed[EA_ALLOWED_LENGTH]; /* Effective addressing modes allowed */ + char cpu_mode[NUM_CPUS]; /* User or supervisor mode */ + char cpus[NUM_CPUS+1]; /* Allowed CPUs */ + unsigned char cycles[NUM_CPUS]; /* cycles for 000, 010, 020 */ +} opcode_struct; + + +/* All modifications necessary for a specific EA mode of an instruction */ +typedef struct +{ + const char* fname_add; + const char* ea_add; + unsigned int mask_add; + unsigned int match_add; +} ea_info_struct; + + +/* Holds the body of a function */ +typedef struct +{ + char body[MAX_BODY_LENGTH][MAX_LINE_LENGTH+1]; + int length; +} body_struct; + + +/* Holds a sequence of search / replace strings */ +typedef struct +{ + char replace[MAX_REPLACE_LENGTH][2][MAX_LINE_LENGTH+1]; + int length; +} replace_struct; + + +/* Function Prototypes */ +void error_exit(const char* fmt, ...); +void perror_exit(const char* fmt, ...); +int check_strsncpy(char* dst, char* src, int maxlength); +int check_atoi(char* str, int *result); +int skip_spaces(char* str); +int num_bits(int value); +int atoh(char* buff); +int fgetline(char* buff, int nchars, FILE* file); +int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type); +opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea); +opcode_struct* find_illegal_opcode(void); +int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea); +void add_replace_string(replace_struct* replace, const char* search_str, const char* replace_str); +void write_body(FILE* filep, body_struct* body, replace_struct* replace); +void get_base_name(char* base_name, opcode_struct* op); +void write_prototype(FILE* filep, char* base_name); +void write_function_name(FILE* filep, char* base_name); +void add_opcode_output_table_entry(opcode_struct* op, char* name); +static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr); +void print_opcode_output_table(FILE* filep); +void write_table_entry(FILE* filep, opcode_struct* op); +void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode); +void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode); +void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op); +void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset); +void process_opcode_handlers(void); +void populate_table(void); +void read_insert(char* insert); + + + +/* ======================================================================== */ +/* ================================= DATA ================================= */ +/* ======================================================================== */ + +/* Name of the input file */ +char g_input_filename[M68K_MAX_PATH] = FILENAME_INPUT; + +/* File handles */ +FILE* g_input_file = NULL; +FILE* g_prototype_file = NULL; +FILE* g_table_file = NULL; +FILE* g_ops_ac_file = NULL; +FILE* g_ops_dm_file = NULL; +FILE* g_ops_nz_file = NULL; + +int g_num_functions = 0; /* Number of functions processed */ +int g_num_primitives = 0; /* Number of function primitives read */ +int g_line_number = 1; /* Current line number */ + +/* Opcode handler table */ +opcode_struct g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH]; + +opcode_struct g_opcode_output_table[MAX_OPCODE_OUTPUT_TABLE_LENGTH]; +int g_opcode_output_table_length = 0; + +ea_info_struct g_ea_info_table[13] = +{/* fname ea mask match */ + {"", "", 0x00, 0x00}, /* EA_MODE_NONE */ + {"ai", "AY_AI", 0x38, 0x10}, /* EA_MODE_AI */ + {"pi", "AY_PI", 0x38, 0x18}, /* EA_MODE_PI */ + {"pi7", "A7_PI", 0x3f, 0x1f}, /* EA_MODE_PI7 */ + {"pd", "AY_PD", 0x38, 0x20}, /* EA_MODE_PD */ + {"pd7", "A7_PD", 0x3f, 0x27}, /* EA_MODE_PD7 */ + {"di", "AY_DI", 0x38, 0x28}, /* EA_MODE_DI */ + {"ix", "AY_IX", 0x38, 0x30}, /* EA_MODE_IX */ + {"aw", "AW", 0x3f, 0x38}, /* EA_MODE_AW */ + {"al", "AL", 0x3f, 0x39}, /* EA_MODE_AL */ + {"pcdi", "PCDI", 0x3f, 0x3a}, /* EA_MODE_PCDI */ + {"pcix", "PCIX", 0x3f, 0x3b}, /* EA_MODE_PCIX */ + {"i", "I", 0x3f, 0x3c}, /* EA_MODE_I */ +}; + + +const char* g_cc_table[16][2] = +{ + { "t", "T"}, /* 0000 */ + { "f", "F"}, /* 0001 */ + {"hi", "HI"}, /* 0010 */ + {"ls", "LS"}, /* 0011 */ + {"cc", "CC"}, /* 0100 */ + {"cs", "CS"}, /* 0101 */ + {"ne", "NE"}, /* 0110 */ + {"eq", "EQ"}, /* 0111 */ + {"vc", "VC"}, /* 1000 */ + {"vs", "VS"}, /* 1001 */ + {"pl", "PL"}, /* 1010 */ + {"mi", "MI"}, /* 1011 */ + {"ge", "GE"}, /* 1100 */ + {"lt", "LT"}, /* 1101 */ + {"gt", "GT"}, /* 1110 */ + {"le", "LE"}, /* 1111 */ +}; + +/* size to index translator (0 -> 0, 8 and 16 -> 1, 32 -> 2) */ +int g_size_select_table[33] = +{ + 0, /* unsized */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 8 */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 16 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 /* 32 */ +}; + +/* Extra cycles required for certain EA modes */ +int g_ea_cycle_table[13][NUM_CPUS][3] = +{/* 000 010 020 */ + {{ 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}}, /* EA_MODE_NONE */ + {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_AI */ + {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_PI */ + {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_PI7 */ + {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}}, /* EA_MODE_PD */ + {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}}, /* EA_MODE_PD7 */ + {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}}, /* EA_MODE_DI */ + {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}}, /* EA_MODE_IX */ + {{ 0, 8, 12}, { 0, 8, 12}, { 0, 4, 4}}, /* EA_MODE_AW */ + {{ 0, 12, 16}, { 0, 12, 16}, { 0, 4, 4}}, /* EA_MODE_AL */ + {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}}, /* EA_MODE_PCDI */ + {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}}, /* EA_MODE_PCIX */ + {{ 0, 4, 8}, { 0, 4, 8}, { 0, 2, 4}}, /* EA_MODE_I */ +}; + +/* Extra cycles for JMP instruction (000, 010) */ +int g_jmp_cycle_table[13] = +{ + 0, /* EA_MODE_NONE */ + 4, /* EA_MODE_AI */ + 0, /* EA_MODE_PI */ + 0, /* EA_MODE_PI7 */ + 0, /* EA_MODE_PD */ + 0, /* EA_MODE_PD7 */ + 6, /* EA_MODE_DI */ + 10, /* EA_MODE_IX */ + 6, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 6, /* EA_MODE_PCDI */ + 10, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for JSR instruction (000, 010) */ +int g_jsr_cycle_table[13] = +{ + 0, /* EA_MODE_NONE */ + 4, /* EA_MODE_AI */ + 0, /* EA_MODE_PI */ + 0, /* EA_MODE_PI7 */ + 0, /* EA_MODE_PD */ + 0, /* EA_MODE_PD7 */ + 6, /* EA_MODE_DI */ + 10, /* EA_MODE_IX */ + 6, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 6, /* EA_MODE_PCDI */ + 10, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for LEA instruction (000, 010) */ +int g_lea_cycle_table[13] = +{ + 0, /* EA_MODE_NONE */ + 4, /* EA_MODE_AI */ + 0, /* EA_MODE_PI */ + 0, /* EA_MODE_PI7 */ + 0, /* EA_MODE_PD */ + 0, /* EA_MODE_PD7 */ + 8, /* EA_MODE_DI */ + 12, /* EA_MODE_IX */ + 8, /* EA_MODE_AW */ + 12, /* EA_MODE_AL */ + 8, /* EA_MODE_PCDI */ + 12, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for PEA instruction (000, 010) */ +int g_pea_cycle_table[13] = +{ + 0, /* EA_MODE_NONE */ + 6, /* EA_MODE_AI */ + 0, /* EA_MODE_PI */ + 0, /* EA_MODE_PI7 */ + 0, /* EA_MODE_PD */ + 0, /* EA_MODE_PD7 */ + 10, /* EA_MODE_DI */ + 14, /* EA_MODE_IX */ + 10, /* EA_MODE_AW */ + 14, /* EA_MODE_AL */ + 10, /* EA_MODE_PCDI */ + 14, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for MOVEM instruction (000, 010) */ +int g_movem_cycle_table[13] = +{ + 0, /* EA_MODE_NONE */ + 0, /* EA_MODE_AI */ + 0, /* EA_MODE_PI */ + 0, /* EA_MODE_PI7 */ + 0, /* EA_MODE_PD */ + 0, /* EA_MODE_PD7 */ + 4, /* EA_MODE_DI */ + 6, /* EA_MODE_IX */ + 4, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 0, /* EA_MODE_PCDI */ + 0, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for MOVES instruction (010) */ +int g_moves_cycle_table[13][3] = +{ + { 0, 0, 0}, /* EA_MODE_NONE */ + { 0, 4, 6}, /* EA_MODE_AI */ + { 0, 4, 6}, /* EA_MODE_PI */ + { 0, 4, 6}, /* EA_MODE_PI7 */ + { 0, 6, 12}, /* EA_MODE_PD */ + { 0, 6, 12}, /* EA_MODE_PD7 */ + { 0, 12, 16}, /* EA_MODE_DI */ + { 0, 16, 20}, /* EA_MODE_IX */ + { 0, 12, 16}, /* EA_MODE_AW */ + { 0, 16, 20}, /* EA_MODE_AL */ + { 0, 0, 0}, /* EA_MODE_PCDI */ + { 0, 0, 0}, /* EA_MODE_PCIX */ + { 0, 0, 0}, /* EA_MODE_I */ +}; + +/* Extra cycles for CLR instruction (010) */ +int g_clr_cycle_table[13][3] = +{ + { 0, 0, 0}, /* EA_MODE_NONE */ + { 0, 4, 6}, /* EA_MODE_AI */ + { 0, 4, 6}, /* EA_MODE_PI */ + { 0, 4, 6}, /* EA_MODE_PI7 */ + { 0, 6, 8}, /* EA_MODE_PD */ + { 0, 6, 8}, /* EA_MODE_PD7 */ + { 0, 8, 10}, /* EA_MODE_DI */ + { 0, 10, 14}, /* EA_MODE_IX */ + { 0, 8, 10}, /* EA_MODE_AW */ + { 0, 10, 14}, /* EA_MODE_AL */ + { 0, 0, 0}, /* EA_MODE_PCDI */ + { 0, 0, 0}, /* EA_MODE_PCIX */ + { 0, 0, 0}, /* EA_MODE_I */ +}; + + + +/* ======================================================================== */ +/* =========================== UTILITY FUNCTIONS ========================== */ +/* ======================================================================== */ + +/* Print an error message and exit with status error */ +void error_exit(const char* fmt, ...) +{ + va_list args; + fprintf(stderr, "In %s, near or on line %d:\n\t", g_input_filename, g_line_number); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + + if(g_prototype_file) fclose(g_prototype_file); + if(g_table_file) fclose(g_table_file); + if(g_ops_ac_file) fclose(g_ops_ac_file); + if(g_ops_dm_file) fclose(g_ops_dm_file); + if(g_ops_nz_file) fclose(g_ops_nz_file); + if(g_input_file) fclose(g_input_file); + + exit(EXIT_FAILURE); +} + +/* Print an error message, call perror(), and exit with status error */ +void perror_exit(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + perror(""); + + if(g_prototype_file) fclose(g_prototype_file); + if(g_table_file) fclose(g_table_file); + if(g_ops_ac_file) fclose(g_ops_ac_file); + if(g_ops_dm_file) fclose(g_ops_dm_file); + if(g_ops_nz_file) fclose(g_ops_nz_file); + if(g_input_file) fclose(g_input_file); + + exit(EXIT_FAILURE); +} + + +/* copy until 0 or space and exit with error if we read too far */ +int check_strsncpy(char* dst, char* src, int maxlength) +{ + char* p = dst; + while(*src && *src != ' ') + { + *p++ = *src++; + if(p - dst > maxlength) + error_exit("Field too long"); + } + *p = 0; + return p - dst; +} + +/* copy until 0 or specified character and exit with error if we read too far */ +int check_strcncpy(char* dst, char* src, char delim, int maxlength) +{ + char* p = dst; + while(*src && *src != delim) + { + *p++ = *src++; + if(p - dst > maxlength) + error_exit("Field too long"); + } + *p = 0; + return p - dst; +} + +/* convert ascii to integer and exit with error if we find invalid data */ +int check_atoi(char* str, int *result) +{ + int accum = 0; + char* p = str; + while(*p >= '0' && *p <= '9') + { + accum *= 10; + accum += *p++ - '0'; + } + if(*p != ' ' && *p != 0) + error_exit("Malformed integer value (%c)", *p); + *result = accum; + return p - str; +} + +/* Skip past spaces in a string */ +int skip_spaces(char* str) +{ + char* p = str; + + while(*p == ' ') + p++; + + return p - str; +} + +/* Count the number of set bits in a value */ +int num_bits(int value) +{ + value = ((value & 0xaaaa) >> 1) + (value & 0x5555); + value = ((value & 0xcccc) >> 2) + (value & 0x3333); + value = ((value & 0xf0f0) >> 4) + (value & 0x0f0f); + value = ((value & 0xff00) >> 8) + (value & 0x00ff); + return value; +} + +/* Convert a hex value written in ASCII */ +int atoh(char* buff) +{ + int accum = 0; + + for(;;buff++) + { + if(*buff >= '0' && *buff <= '9') + { + accum <<= 4; + accum += *buff - '0'; + } + else if(*buff >= 'a' && *buff <= 'f') + { + accum <<= 4; + accum += *buff - 'a' + 10; + } + else break; + } + return accum; +} + +/* Get a line of text from a file, discarding any end-of-line characters */ +int fgetline(char* buff, int nchars, FILE* file) +{ + int length; + + if(fgets(buff, nchars, file) == NULL) + return -1; + if(buff[0] == '\r') + memmove(buff, buff + 1, nchars - 1); + + length = strlen(buff); + while(length && (buff[length-1] == '\r' || buff[length-1] == '\n')) + length--; + buff[length] = 0; + g_line_number++; + + return length; +} + + + +/* ======================================================================== */ +/* =========================== HELPER FUNCTIONS =========================== */ +/* ======================================================================== */ + +/* Calculate the number of cycles an opcode requires */ +int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type) +{ + int size = g_size_select_table[op->size]; + + if(op->cpus[cpu_type] == '.') + return 0; + + if(cpu_type < CPU_TYPE_020) + { + if(cpu_type == CPU_TYPE_010) + { + if(strcmp(op->name, "moves") == 0) + return op->cycles[cpu_type] + g_moves_cycle_table[ea_mode][size]; + if(strcmp(op->name, "clr") == 0) + return op->cycles[cpu_type] + g_clr_cycle_table[ea_mode][size]; + } + + /* ASG: added these cases -- immediate modes take 2 extra cycles here */ + /* SV: but only when operating on long, and also on register direct mode */ + if(cpu_type == CPU_TYPE_000 && (ea_mode == EA_MODE_I || ea_mode == EA_MODE_NONE) && op->size == 32 && + ((strcmp(op->name, "add") == 0 && strcmp(op->spec_proc, "er") == 0) || + strcmp(op->name, "adda") == 0 || + (strcmp(op->name, "and") == 0 && strcmp(op->spec_proc, "er") == 0) || + (strcmp(op->name, "or") == 0 && strcmp(op->spec_proc, "er") == 0) || + (strcmp(op->name, "sub") == 0 && strcmp(op->spec_proc, "er") == 0) || + strcmp(op->name, "suba") == 0)) + return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size] + 2; + + if(strcmp(op->name, "jmp") == 0) + return op->cycles[cpu_type] + g_jmp_cycle_table[ea_mode]; + if(strcmp(op->name, "jsr") == 0) + return op->cycles[cpu_type] + g_jsr_cycle_table[ea_mode]; + if(strcmp(op->name, "lea") == 0) + return op->cycles[cpu_type] + g_lea_cycle_table[ea_mode]; + if(strcmp(op->name, "pea") == 0) + return op->cycles[cpu_type] + g_pea_cycle_table[ea_mode]; + if(strcmp(op->name, "movem") == 0) + return op->cycles[cpu_type] + g_movem_cycle_table[ea_mode]; + } + return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size]; +} + +/* Find an opcode in the opcode handler list */ +opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea) +{ + opcode_struct* op; + + + for(op = g_opcode_input_table;op->name != NULL;op++) + { + if( strcmp(name, op->name) == 0 && + (size == op->size) && + strcmp(spec_proc, op->spec_proc) == 0 && + strcmp(spec_ea, op->spec_ea) == 0) + return op; + } + return NULL; +} + +/* Specifically find the illegal opcode in the list */ +opcode_struct* find_illegal_opcode(void) +{ + opcode_struct* op; + + for(op = g_opcode_input_table;op->name != NULL;op++) + { + if(strcmp(op->name, "illegal") == 0) + return op; + } + return NULL; +} + +/* Parse an opcode handler name */ +int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea) +{ + char* ptr = strstr(src, ID_OPHANDLER_NAME); + + if(ptr == NULL) + return 0; + + ptr += strlen(ID_OPHANDLER_NAME) + 1; + + ptr += check_strcncpy(name, ptr, ',', MAX_NAME_LENGTH); + if(*ptr != ',') return 0; + ptr++; + ptr += skip_spaces(ptr); + + *size = atoi(ptr); + ptr = strstr(ptr, ","); + if(ptr == NULL) return 0; + ptr++; + ptr += skip_spaces(ptr); + + ptr += check_strcncpy(spec_proc, ptr, ',', MAX_SPEC_PROC_LENGTH); + if(*ptr != ',') return 0; + ptr++; + ptr += skip_spaces(ptr); + + ptr += check_strcncpy(spec_ea, ptr, ')', MAX_SPEC_EA_LENGTH); + if(*ptr != ')') return 0; + ptr++; + ptr += skip_spaces(ptr); + + return 1; +} + + +/* Add a search/replace pair to a replace structure */ +void add_replace_string(replace_struct* replace, const char* search_str, const char* replace_str) +{ + if(replace->length >= MAX_REPLACE_LENGTH) + error_exit("overflow in replace structure"); + + strcpy(replace->replace[replace->length][0], search_str); + strcpy(replace->replace[replace->length++][1], replace_str); +} + +/* Write a function body while replacing any selected strings */ +void write_body(FILE* filep, body_struct* body, replace_struct* replace) +{ + int i; + int j; + char* ptr; + char output[MAX_LINE_LENGTH+1]; + char temp_buff[MAX_LINE_LENGTH+1]; + int found; + + for(i=0;ilength;i++) + { + strcpy(output, body->body[i]); + /* Check for the base directive header */ + if(strstr(output, ID_BASE) != NULL) + { + /* Search for any text we need to replace */ + found = 0; + for(j=0;jlength;j++) + { + ptr = strstr(output, replace->replace[j][0]); + if(ptr) + { + /* We found something to replace */ + found = 1; + strcpy(temp_buff, ptr+strlen(replace->replace[j][0])); + strcpy(ptr, replace->replace[j][1]); + strcat(ptr, temp_buff); + } + } + /* Found a directive with no matching replace string */ + if(!found) + error_exit("Unknown " ID_BASE " directive"); + } + fprintf(filep, "%s\n", output); + } + fprintf(filep, "\n\n"); +} + +/* Generate a base function name from an opcode struct */ +void get_base_name(char* base_name, opcode_struct* op) +{ + sprintf(base_name, "m68k_op_%s", op->name); + if(op->size > 0) + sprintf(base_name+strlen(base_name), "_%d", op->size); + if(strcmp(op->spec_proc, UNSPECIFIED) != 0) + sprintf(base_name+strlen(base_name), "_%s", op->spec_proc); + if(strcmp(op->spec_ea, UNSPECIFIED) != 0) + sprintf(base_name+strlen(base_name), "_%s", op->spec_ea); +} + +/* Write the prototype of an opcode handler function */ +void write_prototype(FILE* filep, char* base_name) +{ + fprintf(filep, "void %s(void);\n", base_name); +} + +/* Write the name of an opcode handler function */ +void write_function_name(FILE* filep, char* base_name) +{ + fprintf(filep, "void %s(void)\n", base_name); +} + +void add_opcode_output_table_entry(opcode_struct* op, char* name) +{ + opcode_struct* ptr; + if(g_opcode_output_table_length > MAX_OPCODE_OUTPUT_TABLE_LENGTH) + error_exit("Opcode output table overflow"); + + ptr = g_opcode_output_table + g_opcode_output_table_length++; + + *ptr = *op; + strcpy(ptr->name, name); + ptr->bits = num_bits(ptr->op_mask); +} + +/* + * Comparison function for qsort() + * For entries with an equal number of set bits in + * the mask compare the match values + */ +static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr) +{ + const opcode_struct *a = aptr, *b = bptr; + if(a->bits != b->bits) + return a->bits - b->bits; + if(a->op_mask != b->op_mask) + return a->op_mask - b->op_mask; + return a->op_match - b->op_match; +} + +void print_opcode_output_table(FILE* filep) +{ + int i; + qsort((void *)g_opcode_output_table, g_opcode_output_table_length, sizeof(g_opcode_output_table[0]), compare_nof_true_bits); + + for(i=0;iname, op->op_mask, op->op_match); + + for(i=0;icycles[i]); + if(i < NUM_CPUS-1) + fprintf(filep, ", "); + } + + fprintf(filep, "}},\n"); +} + +/* Fill out an opcode struct with a specific addressing mode of the source opcode struct */ +void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode) +{ + int i; + + *dst = *src; + + for(i=0;icycles[i] = get_oper_cycles(dst, ea_mode, i); + if(strcmp(dst->spec_ea, UNSPECIFIED) == 0 && ea_mode != EA_MODE_NONE) + sprintf(dst->spec_ea, "%s", g_ea_info_table[ea_mode].fname_add); + dst->op_mask |= g_ea_info_table[ea_mode].mask_add; + dst->op_match |= g_ea_info_table[ea_mode].match_add; +} + + +/* Generate a final opcode handler from the provided data */ +void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode) +{ + char str[MAX_LINE_LENGTH+1]; + opcode_struct* op = malloc(sizeof(opcode_struct)); + + /* Set the opcode structure and write the tables, prototypes, etc */ + set_opcode_struct(opinfo, op, ea_mode); + get_base_name(str, op); + write_prototype(g_prototype_file, str); + add_opcode_output_table_entry(op, str); + write_function_name(filep, str); + + /* Add any replace strings needed */ + if(ea_mode != EA_MODE_NONE) + { + sprintf(str, "EA_%s_8()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_EA_AY_8, str); + sprintf(str, "EA_%s_16()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_EA_AY_16, str); + sprintf(str, "EA_%s_32()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_EA_AY_32, str); + sprintf(str, "OPER_%s_8()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_OPER_AY_8, str); + sprintf(str, "OPER_%s_16()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_OPER_AY_16, str); + sprintf(str, "OPER_%s_32()", g_ea_info_table[ea_mode].ea_add); + add_replace_string(replace, ID_OPHANDLER_OPER_AY_32, str); + } + + /* Now write the function body with the selected replace strings */ + write_body(filep, body, replace); + g_num_functions++; + free(op); +} + +/* Generate opcode variants based on available addressing modes */ +void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op) +{ + int old_length = replace->length; + + /* No ea modes available for this opcode */ + if(HAS_NO_EA_MODE(op->ea_allowed)) + { + generate_opcode_handler(filep, body, replace, op, EA_MODE_NONE); + return; + } + + /* Check for and create specific opcodes for each available addressing mode */ + if(HAS_EA_AI(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_AI); + replace->length = old_length; + if(HAS_EA_PI(op->ea_allowed)) + { + generate_opcode_handler(filep, body, replace, op, EA_MODE_PI); + replace->length = old_length; + if(op->size == 8) + generate_opcode_handler(filep, body, replace, op, EA_MODE_PI7); + } + replace->length = old_length; + if(HAS_EA_PD(op->ea_allowed)) + { + generate_opcode_handler(filep, body, replace, op, EA_MODE_PD); + replace->length = old_length; + if(op->size == 8) + generate_opcode_handler(filep, body, replace, op, EA_MODE_PD7); + } + replace->length = old_length; + if(HAS_EA_DI(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_DI); + replace->length = old_length; + if(HAS_EA_IX(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_IX); + replace->length = old_length; + if(HAS_EA_AW(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_AW); + replace->length = old_length; + if(HAS_EA_AL(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_AL); + replace->length = old_length; + if(HAS_EA_PCDI(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_PCDI); + replace->length = old_length; + if(HAS_EA_PCIX(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_PCIX); + replace->length = old_length; + if(HAS_EA_I(op->ea_allowed)) + generate_opcode_handler(filep, body, replace, op, EA_MODE_I); + replace->length = old_length; +} + +/* Generate variants of condition code opcodes */ +void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset) +{ + char repl[20]; + char replnot[20]; + int i; + int old_length = replace->length; + opcode_struct* op = malloc(sizeof(opcode_struct)); + + *op = *op_in; + + op->op_mask |= 0x0f00; + + /* Do all condition codes except t and f */ + for(i=2;i<16;i++) + { + /* Add replace strings for this condition code */ + sprintf(repl, "COND_%s()", g_cc_table[i][1]); + sprintf(replnot, "COND_NOT_%s()", g_cc_table[i][1]); + + add_replace_string(replace, ID_OPHANDLER_CC, repl); + add_replace_string(replace, ID_OPHANDLER_NOT_CC, replnot); + + /* Set the new opcode info */ + strcpy(op->name+offset, g_cc_table[i][0]); + + op->op_match = (op->op_match & 0xf0ff) | (i<<8); + + /* Generate all opcode variants for this modified opcode */ + generate_opcode_ea_variants(filep, body, replace, op); + /* Remove the above replace strings */ + replace->length = old_length; + } + free(op); +} + +/* Process the opcode handlers section of the input file */ +void process_opcode_handlers(void) +{ + FILE* input_file = g_input_file; + FILE* output_file; + char func_name[MAX_LINE_LENGTH+1]; + char oper_name[MAX_LINE_LENGTH+1]; + int oper_size; + char oper_spec_proc[MAX_LINE_LENGTH+1]; + char oper_spec_ea[MAX_LINE_LENGTH+1]; + opcode_struct* opinfo; + replace_struct* replace = malloc(sizeof(replace_struct)); + body_struct* body = malloc(sizeof(body_struct)); + + + output_file = g_ops_ac_file; + + for(;;) + { + /* Find the first line of the function */ + func_name[0] = 0; + while(strstr(func_name, ID_OPHANDLER_NAME) == NULL) + { + if(strcmp(func_name, ID_INPUT_SEPARATOR) == 0) + { + free(replace); + free(body); + return; /* all done */ + } + if(fgetline(func_name, MAX_LINE_LENGTH, input_file) < 0) + error_exit("Premature end of file when getting function name"); + } + /* Get the rest of the function */ + for(body->length=0;;body->length++) + { + if(body->length > MAX_BODY_LENGTH) + error_exit("Function too long"); + + if(fgetline(body->body[body->length], MAX_LINE_LENGTH, input_file) < 0) + error_exit("Premature end of file when getting function body"); + + if(body->body[body->length][0] == '}') + { + body->length++; + break; + } + } + + g_num_primitives++; + + /* Extract the function name information */ + if(!extract_opcode_info(func_name, oper_name, &oper_size, oper_spec_proc, oper_spec_ea)) + error_exit("Invalid " ID_OPHANDLER_NAME " format"); + + /* Find the corresponding table entry */ + opinfo = find_opcode(oper_name, oper_size, oper_spec_proc, oper_spec_ea); + if(opinfo == NULL) + error_exit("Unable to find matching table entry for %s", func_name); + + /* Change output files if we pass 'c' or 'n' */ + if(output_file == g_ops_ac_file && oper_name[0] > 'c') + output_file = g_ops_dm_file; + else if(output_file == g_ops_dm_file && oper_name[0] > 'm') + output_file = g_ops_nz_file; + + replace->length = 0; + + /* Generate opcode variants */ + if(strcmp(opinfo->name, "bcc") == 0 || strcmp(opinfo->name, "scc") == 0) + generate_opcode_cc_variants(output_file, body, replace, opinfo, 1); + else if(strcmp(opinfo->name, "dbcc") == 0) + generate_opcode_cc_variants(output_file, body, replace, opinfo, 2); + else if(strcmp(opinfo->name, "trapcc") == 0) + generate_opcode_cc_variants(output_file, body, replace, opinfo, 4); + else + generate_opcode_ea_variants(output_file, body, replace, opinfo); + } + + free(replace); + free(body); +} + + +/* Populate the opcode handler table from the input file */ +void populate_table(void) +{ + char* ptr; + char bitpattern[17]; + opcode_struct* op; + char buff[MAX_LINE_LENGTH]; + int i; + int temp; + + buff[0] = 0; + + /* Find the start of the table */ + while(strcmp(buff, ID_TABLE_START) != 0) + if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) + error_exit("(table_start) Premature EOF while reading table"); + + /* Process the entire table */ + for(op = g_opcode_input_table;;op++) + { + if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) + error_exit("(inline) Premature EOF while reading table"); + if(strlen(buff) == 0) + continue; + /* We finish when we find an input separator */ + if(strcmp(buff, ID_INPUT_SEPARATOR) == 0) + break; + + /* Extract the info from the table */ + ptr = buff; + + /* Name */ + ptr += skip_spaces(ptr); + ptr += check_strsncpy(op->name, ptr, MAX_NAME_LENGTH); + + /* Size */ + ptr += skip_spaces(ptr); + ptr += check_atoi(ptr, &temp); + op->size = (unsigned char)temp; + + /* Special processing */ + ptr += skip_spaces(ptr); + ptr += check_strsncpy(op->spec_proc, ptr, MAX_SPEC_PROC_LENGTH); + + /* Specified EA Mode */ + ptr += skip_spaces(ptr); + ptr += check_strsncpy(op->spec_ea, ptr, MAX_SPEC_EA_LENGTH); + + /* Bit Pattern (more processing later) */ + ptr += skip_spaces(ptr); + ptr += check_strsncpy(bitpattern, ptr, 17); + + /* Allowed Addressing Mode List */ + ptr += skip_spaces(ptr); + ptr += check_strsncpy(op->ea_allowed, ptr, EA_ALLOWED_LENGTH); + + /* CPU operating mode (U = user or supervisor, S = supervisor only */ + ptr += skip_spaces(ptr); + for(i=0;icpu_mode[i] = *ptr++; + ptr += skip_spaces(ptr); + } + + /* Allowed CPUs for this instruction */ + for(i=0;icpus[i] = UNSPECIFIED_CH; + op->cycles[i] = 0; + ptr++; + } + else + { + op->cpus[i] = '0' + i; + ptr += check_atoi(ptr, &temp); + op->cycles[i] = (unsigned char)temp; + } + } + + /* generate mask and match from bitpattern */ + op->op_mask = 0; + op->op_match = 0; + for(i=0;i<16;i++) + { + op->op_mask |= (bitpattern[i] != '.') << (15-i); + op->op_match |= (bitpattern[i] == '1') << (15-i); + } + } + /* Terminate the list */ + op->name[0] = 0; +} + +/* Read a header or footer insert from the input file */ +void read_insert(char* insert) +{ + char* ptr = insert; + char* overflow = insert + MAX_INSERT_LENGTH - MAX_LINE_LENGTH; + int length; + char* first_blank = NULL; + + first_blank = NULL; + + /* Skip any leading blank lines */ + for(length = 0;length == 0;length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) + if(ptr >= overflow) + error_exit("Buffer overflow reading inserts"); + if(length < 0) + error_exit("Premature EOF while reading inserts"); + + /* Advance and append newline */ + ptr += length; + strcpy(ptr++, "\n"); + + /* Read until next separator */ + for(;;) + { + /* Read a new line */ + if(ptr >= overflow) + error_exit("Buffer overflow reading inserts"); + if((length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) < 0) + error_exit("Premature EOF while reading inserts"); + + /* Stop if we read a separator */ + if(strcmp(ptr, ID_INPUT_SEPARATOR) == 0) + break; + + /* keep track in case there are trailing blanks */ + if(length == 0) + { + if(first_blank == NULL) + first_blank = ptr; + } + else + first_blank = NULL; + + /* Advance and append newline */ + ptr += length; + strcpy(ptr++, "\n"); + } + + /* kill any trailing blank lines */ + if(first_blank) + ptr = first_blank; + *ptr++ = 0; +} + + + +/* ======================================================================== */ +/* ============================= MAIN FUNCTION ============================ */ +/* ======================================================================== */ + +int main(int argc, char **argv) +{ + /* File stuff */ + char output_path[M68K_MAX_DIR] = ""; + char filename[M68K_MAX_PATH*2]; + /* Section identifier */ + char section_id[MAX_LINE_LENGTH+1]; + /* Inserts */ + char temp_insert[MAX_INSERT_LENGTH+1]; + char prototype_footer_insert[MAX_INSERT_LENGTH+1]; + char table_footer_insert[MAX_INSERT_LENGTH+1]; + char ophandler_footer_insert[MAX_INSERT_LENGTH+1]; + /* Flags if we've processed certain parts already */ + int prototype_header_read = 0; + int prototype_footer_read = 0; + int table_header_read = 0; + int table_footer_read = 0; + int ophandler_header_read = 0; + int ophandler_footer_read = 0; + int table_body_read = 0; + int ophandler_body_read = 0; + + printf("\n\t\tMusashi v%s 68000, 68008, 68010, 68EC020, 68020 emulator\n", g_version); + printf("\t\tCopyright 1998-2000 Karl Stenerud (karl@mame.net)\n\n"); + + /* Check if output path and source for the input file are given */ + if(argc > 1) + { + char *ptr; + strcpy(output_path, argv[1]); + + for(ptr = strchr(output_path, '\\'); ptr; ptr = strchr(ptr, '\\')) + *ptr = '/'; + +#if !(defined(__DECC) && defined(VMS)) + if(output_path[strlen(output_path)-1] != '/') + strcat(output_path, "/"); +#endif + + if(argc > 2) + strcpy(g_input_filename, argv[2]); + } + + +#if defined(__DECC) && defined(VMS) + + /* Open the files we need */ + sprintf(filename, "%s%s", output_path, FILENAME_PROTOTYPE); + if((g_prototype_file = fopen(filename, "w")) == NULL) + perror_exit("Unable to create prototype file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_TABLE); + if((g_table_file = fopen(filename, "w")) == NULL) + perror_exit("Unable to create table file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_AC); + if((g_ops_ac_file = fopen(filename, "w")) == NULL) + perror_exit("Unable to create ops ac file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_DM); + if((g_ops_dm_file = fopen(filename, "w")) == NULL) + perror_exit("Unable to create ops dm file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_NZ); + if((g_ops_nz_file = fopen(filename, "w")) == NULL) + perror_exit("Unable to create ops nz file (%s)\n", filename); + + if((g_input_file=fopen(g_input_filename, "r")) == NULL) + perror_exit("can't open %s for input", g_input_filename); + +#else + + + /* Open the files we need */ + sprintf(filename, "%s%s", output_path, FILENAME_PROTOTYPE); + if((g_prototype_file = fopen(filename, "wt")) == NULL) + perror_exit("Unable to create prototype file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_TABLE); + if((g_table_file = fopen(filename, "wt")) == NULL) + perror_exit("Unable to create table file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_AC); + if((g_ops_ac_file = fopen(filename, "wt")) == NULL) + perror_exit("Unable to create ops ac file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_DM); + if((g_ops_dm_file = fopen(filename, "wt")) == NULL) + perror_exit("Unable to create ops dm file (%s)\n", filename); + + sprintf(filename, "%s%s", output_path, FILENAME_OPS_NZ); + if((g_ops_nz_file = fopen(filename, "wt")) == NULL) + perror_exit("Unable to create ops nz file (%s)\n", filename); + + if((g_input_file=fopen(g_input_filename, "rt")) == NULL) + perror_exit("can't open %s for input", g_input_filename); + +#endif + + /* Get to the first section of the input file */ + section_id[0] = 0; + while(strcmp(section_id, ID_INPUT_SEPARATOR) != 0) + if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) + error_exit("Premature EOF while reading input file"); + + /* Now process all sections */ + for(;;) + { + if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) + error_exit("Premature EOF while reading input file"); + if(strcmp(section_id, ID_PROTOTYPE_HEADER) == 0) + { + if(prototype_header_read) + error_exit("Duplicate prototype header"); + read_insert(temp_insert); + fprintf(g_prototype_file, "%s\n\n", temp_insert); + prototype_header_read = 1; + } + else if(strcmp(section_id, ID_TABLE_HEADER) == 0) + { + if(table_header_read) + error_exit("Duplicate table header"); + read_insert(temp_insert); + fprintf(g_table_file, "%s", temp_insert); + table_header_read = 1; + } + else if(strcmp(section_id, ID_OPHANDLER_HEADER) == 0) + { + if(ophandler_header_read) + error_exit("Duplicate opcode handler header"); + read_insert(temp_insert); + fprintf(g_ops_ac_file, "%s\n\n", temp_insert); + fprintf(g_ops_dm_file, "%s\n\n", temp_insert); + fprintf(g_ops_nz_file, "%s\n\n", temp_insert); + ophandler_header_read = 1; + } + else if(strcmp(section_id, ID_PROTOTYPE_FOOTER) == 0) + { + if(prototype_footer_read) + error_exit("Duplicate prototype footer"); + read_insert(prototype_footer_insert); + prototype_footer_read = 1; + } + else if(strcmp(section_id, ID_TABLE_FOOTER) == 0) + { + if(table_footer_read) + error_exit("Duplicate table footer"); + read_insert(table_footer_insert); + table_footer_read = 1; + } + else if(strcmp(section_id, ID_OPHANDLER_FOOTER) == 0) + { + if(ophandler_footer_read) + error_exit("Duplicate opcode handler footer"); + read_insert(ophandler_footer_insert); + ophandler_footer_read = 1; + } + else if(strcmp(section_id, ID_TABLE_BODY) == 0) + { + if(!prototype_header_read) + error_exit("Table body encountered before prototype header"); + if(!table_header_read) + error_exit("Table body encountered before table header"); + if(!ophandler_header_read) + error_exit("Table body encountered before opcode handler header"); + + if(table_body_read) + error_exit("Duplicate table body"); + + populate_table(); + table_body_read = 1; + } + else if(strcmp(section_id, ID_OPHANDLER_BODY) == 0) + { + if(!prototype_header_read) + error_exit("Opcode handlers encountered before prototype header"); + if(!table_header_read) + error_exit("Opcode handlers encountered before table header"); + if(!ophandler_header_read) + error_exit("Opcode handlers encountered before opcode handler header"); + if(!table_body_read) + error_exit("Opcode handlers encountered before table body"); + + if(ophandler_body_read) + error_exit("Duplicate opcode handler section"); + + process_opcode_handlers(); + + ophandler_body_read = 1; + } + else if(strcmp(section_id, ID_END) == 0) + { + /* End of input file. Do a sanity check and then write footers */ + if(!prototype_header_read) + error_exit("Missing prototype header"); + if(!prototype_footer_read) + error_exit("Missing prototype footer"); + if(!table_header_read) + error_exit("Missing table header"); + if(!table_footer_read) + error_exit("Missing table footer"); + if(!table_body_read) + error_exit("Missing table body"); + if(!ophandler_header_read) + error_exit("Missing opcode handler header"); + if(!ophandler_footer_read) + error_exit("Missing opcode handler footer"); + if(!ophandler_body_read) + error_exit("Missing opcode handler body"); + + print_opcode_output_table(g_table_file); + + fprintf(g_prototype_file, "%s\n\n", prototype_footer_insert); + fprintf(g_table_file, "%s\n\n", table_footer_insert); + fprintf(g_ops_ac_file, "%s\n\n", ophandler_footer_insert); + fprintf(g_ops_dm_file, "%s\n\n", ophandler_footer_insert); + fprintf(g_ops_nz_file, "%s\n\n", ophandler_footer_insert); + + break; + } + else + { + error_exit("Unknown section identifier: %s", section_id); + } + } + + /* Close all files and exit */ + fclose(g_prototype_file); + fclose(g_table_file); + fclose(g_ops_ac_file); + fclose(g_ops_dm_file); + fclose(g_ops_nz_file); + fclose(g_input_file); + + printf("Generated %d opcode handlers from %d primitives\n", g_num_functions, g_num_primitives); + + return 0; +} + + + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ diff --git a/Src/CPU/68K/Turbo68K/Make68K.c b/Src/CPU/68K/Turbo68K/Make68K.c new file mode 100644 index 0000000..676554f --- /dev/null +++ b/Src/CPU/68K/Turbo68K/Make68K.c @@ -0,0 +1,9007 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Turbo68K: Motorola 680X0 emulator + * Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use + */ + +/* + * Make68K: This program emits Turbo68K. Please see README.TXT for details. + * + * Note: If you get a link error involving pow(), remember to link in the math + * library. Try using "-lm". + * + * Note: I am not certain about dummy reads. The manual says they occur with + * some instructions on the MC68000 and MC68008 only. Does this include + * the MC68EC000? I'm assuming it does. Also, is a dummy read made for + * all Scc instructions, or just ST? I'm assuming all of them do it. + * + * Note: Do not use the profiling feature (#define PROFILE.) It will add + * useless junk to the emulator. + * + * Note: Future plans: I intend on removing as much of the Save/RestoreReg() + * junk from the API functions as possible. PUSH/POPs should work much + * better. + * + * Additional developers' notes pertaining to all aspects of Turbo68K can be + * found at the very end of this file. + */ + + +#include +#include +#include +#include +#include +#include "turbo68k.h" + +#define VERSION "0.6" + +#define SIZEOF_CONTEXT "(context_end - context_start)" +#define SIZEOF_FETCHREGION "12" +#define SIZEOF_DATAREGION "16" +#define OFFSET_DATA_BASE "0" +#define OFFSET_DATA_LIMIT "4" +#define OFFSET_DATA_PTR "8" +#define OFFSET_DATA_HANDLER "12" +#define OFFSET_FETCH_BASE "0" +#define OFFSET_FETCH_LIMIT "4" +#define OFFSET_FETCH_PTR "8" + +#define WRITEPCTOMEM(dest) { \ + e("mov dword ["dest"], esi\n");\ + e("sub dword ["dest"], ebp\n");\ + } +#define WRITEPCTOREG(dest) { \ + e("mov "dest", esi\n"); \ + e("sub "dest", ebp\n"); \ + } +#define EMULATING() e("or byte [status], 1\n"); +#define STOP_EMULATING() e("and byte [status], 0xfe\n"); +#define STOP_CPU() e("or byte [status], 2\n"); +#define UNSTOP_CPU() e("and byte [status], 0xfd\n"); +#define INTERRUPT_PROCESSING() e("or byte [status], 4\n"); +#define INTERRUPT_DONE() e("and byte [status], 0xfb\n"); +#define STOP_RUNNING() { \ + e("mov [remaining], ecx\n"); \ + WRITEPCTOMEM("pc"); \ + } +#define A7 "[a+7*4]" +#define SP "[__sp]" +#define SR "[sr]" + +#define UNKNOWN_SIZE 0 +#define BYTE_SIZE 1 +#define WORD_SIZE 2 +#define LONG_SIZE 3 + +unsigned short decoded[65536]; /* compressed jump table data */ +char id[17] = { '\0' }; /* prepended to identifiers */ +FILE *fp; /* output file */ +int mpu = 68000; /* 68000 by default */ +unsigned addr_bits = 24; /* 24-bit by default */ +unsigned addr_mask = 0xffffff; +int illegal = 1; /* illegal instruction traps */ +int dummyread = 1; /* dummy reads (68000 only) */ +int skip = 0; /* CPU (idle loop) skipping */ +int brafetch = 0; /* branch fetch ptr updating */ +int pcfetch = 0; /* special PC-relative fetching */ +int multiaddr = 1; /* supervisor and user spaces */ +int memmap_type = 0; /* memory map type */ +int call_convention = 0; /* calling convention */ +int debug = 0; /* call debug function at every instruction */ +int mmx = 0; /* MMX support -- don't use! */ +unsigned num_handlers = 0; /* # of inst. handlers emitted */ + +#ifdef PROFILE +char *prof[512]; +int prof_i = 0; +#endif + + +/***************************************************************************** +* General Emitters */ + +void e(char *input, ...) +{ + va_list arg; + char text[256]; + va_start(arg, input); + vsprintf(text, input, arg); + va_end(arg); + fprintf(fp, text); +} + +void EmitLabel(char *label) +{ + e("%s:\n", label); +} + +void Align(int i) +{ + e("times ($$-$) & %d nop\n", i - 1); +} + +void CacheAlign() +{ + Align(32); +} + +void EmitGlobalLabel(char *label) +{ + e("global %s%s, _%s%s, %s%s_\n", id, label, id, label, id, label); + e("%s%s:\n_%s%s:\n%s%s_:\n", id, label, id, label, id, label); +} + + +/***************************************************************************** +* Utility Functions */ + +/* + * SizeOfContext(): Returns the size of the context if needed by the emitter. + * This should be used instead of C's sizeof(). + */ + +int SizeOfContext() +{ + switch (mpu) + { + case 68010: + return sizeof(struct TURBO68K_CONTEXT_68010); + } + return sizeof(struct TURBO68K_CONTEXT_68000); +} + +/* + * NameOfContext(): Returns the name of the context based on the MPU type, + * but NOT with the identifier in front of it. + */ + +static char *NameOfContext() +{ + static char name[16 + 6 + 1]; + + sprintf(name, "turbo68kcontext_%d", mpu); + return name; +} + +/* + * NOTE: Only pass 32-bit or 8-bit (XL, not XH regs) to the SaveReg/SaveRegTo, + * RestoreReg/RestoreRegTo functions. Don't ever pass 16-bit regs! + */ + +/* + * SaveReg/RestoreReg: Save/restore registers to/from a specific save slot + * area. + */ + +void SaveReg(char *area, char *reg) +{ + if (mmx) + { + if (!strcmp(area, "memhandler")) + { /* memhandler always saves full reg */ + if (!strcmp(reg, "eax")) { e("movd mm0, eax\n"); return; } + if (!strcmp(reg, "ebx")) { e("movd mm1, ebx\n"); return; } + if (!strcmp(reg, "ebp")) { e("movd mm2, ebp\n"); return; } + } + else if (!strcmp(area, "run")) + { /* we have to check if its a full or byte reg */ + if (!strcmp(reg, "edx") || !strcmp(reg, "dl")) + { + e("movd mm3, edx\n"); + return; + } + if (!strcmp(reg, "esi")) { e("movd mm4, esi\n"); return; } + if (!strcmp(reg, "edi")) { e("movd mm5, edi\n"); return; } + } + } + + e("mov [%s_", area); + if (reg[1] == 'l') /* ?l byte reg */ + { + switch (reg[0]) + { + case 'a': e("eax"); break; + case 'b': e("ebx"); break; + case 'c': e("ecx"); break; + case 'd': e("edx"); break; + } + } + else + e("%s", reg); + e("], %s\n", reg); +} + +void RestoreReg(char *area, char *reg) +{ + if (mmx) + { + if (!strcmp(area, "memhandler")) + { /* memhandler always restores full reg */ + if (!strcmp(reg, "eax")) { e("movd eax, mm0\n"); return; } + if (!strcmp(reg, "ebx")) { e("movd ebx, mm1\n"); return; } + if (!strcmp(reg, "ebp")) { e("movd ebp, mm2\n"); return; } + } + else if (!strcmp(area, "run")) + { /* we have to check if its a full or byte reg */ + if (!strcmp(reg, "edx") || !strcmp(reg, "dl")) + { + e("movd edx, mm3\n"); + return; + } + if (!strcmp(reg, "esi")) { e("movd esi, mm4\n"); return; } + if (!strcmp(reg, "edi")) { e("movd edi, mm5\n"); return; } + } + } + + e("mov %s, [%s_", reg, area); + if (reg[1] == 'l') /* ?l byte reg */ + { + switch (reg[0]) + { + case 'a': e("eax"); break; + case 'b': e("ebx"); break; + case 'c': e("ecx"); break; + case 'd': e("edx"); break; + } + } + else + e("%s", reg); + e("]\n"); +} + +/* + * SaveRegTo/RestoreRegTo: Saves/restore a register to a different register + * than indicated by the name of the save slot. + */ + +void SaveRegTo(char *area, char *d_reg, char *s_reg) +{ + if (mmx) + { + /* currently, memhandlers don't need this, only run_ does. */ + if (!strcmp(area, "run")) + { + /* test to make sure dest is MMX-able */ + if (!strcmp(d_reg, "edx") || !strcmp(d_reg, "esi") || !strcmp(d_reg, "edi")) + { + e("movd "); + if (!strcmp(d_reg, "edx")) e("mm3, "); + else if (!strcmp(d_reg, "esi")) e("mm4, "); + else if (!strcmp(d_reg, "edi")) e("mm5, "); + + if (s_reg[1] == 'l') /* ?l byte reg */ + { + switch (s_reg[0]) + { + case 'a': e("eax\n"); break; + case 'b': e("ebx\n"); break; + case 'c': e("ecx\n"); break; + case 'd': e("edx\n"); break; + } + } + else + e("%s\n", s_reg); + return; + } + } + } + e("mov [%s_%s], %s\n", area, d_reg, s_reg); +} + +void RestoreRegTo(char *area, char *s_reg, char *d_reg) +{ + if (mmx) + { + /* currently, memhandlers don't need this, only run_ does. */ + if (!strcmp(area, "run")) + { + /* test to make sure src is MMX-able */ + if (!strcmp(s_reg, "edx") || !strcmp(s_reg, "esi") || !strcmp(s_reg, "edi")) + { + e("movd "); + if (d_reg[1] == 'l') /* ?l byte reg */ + { + switch (d_reg[0]) + { + case 'a': e("eax, "); break; + case 'b': e("ebx, "); break; + case 'c': e("ecx, "); break; + case 'd': e("edx, "); break; + } + } + else + e("%s, ", d_reg); + + if (!strcmp(s_reg, "edx")) e("mm3\n"); + else if (!strcmp(s_reg, "esi")) e("mm4\n"); + else if (!strcmp(s_reg, "edi")) e("mm5\n"); + return; + } + } + } + e("mov %s, [%s_%s]\n", d_reg, area, s_reg); +} + +void AddrClip(char *reg) +{ /* if it's 32-bit we don't need this */ + if (addr_mask != 0xffffffff) e("and %s, 0x%08X\n", reg, addr_mask); +} + +void RideIntoTheDangerZone() /* where EBP is modified and must be saved */ +{ + e("push ebp\n"); +} + +void RideOutOfTheDangerZone() +{ + e("pop ebp\n"); +} + +void GetArg(char *dest_reg, int num_arg, int stack_offs) +{ + char *reg[2] = { "eax", "edx" }; /* currently, only 2 args supported */ + + if (num_arg > 1) /* error! */ + { + fprintf(stderr, "Make68K: Internal Error: GetArg() called with num_arg = %d. Please contact Bart Trzynadlowski!\n", num_arg); + if (call_convention) + return; + } + + if (!call_convention) + e("mov %s, [esp+%d]\n", dest_reg, stack_offs); + else + { + if ((!strcmp(dest_reg, "eax") && !num_arg) || (!strcmp(dest_reg, "edx") && num_arg == 1)) + ; /* EAX,EAX or EDX,EDX */ + else + e("mov %s, %s\n", dest_reg, reg[num_arg]); + } +} + +void GetArgFromTheDangerZone(char *dest_reg, int num) +{ + char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */ + + if (!call_convention) /* stack-based */ + e("mov %s, [esp+8+(%d*4)]\n", dest_reg, num); + else /* register-based */ + { + if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1)) + ; /* EAX,EAX or EDX,EDX */ + else + e("mov %s, %s\n", dest_reg, reg[num]); + } +} + +void GetArgNoStringsAttached(char *dest_reg, int num) +{ + char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */ + + if (!call_convention) + e("mov %s, [esp+4+(%d*4)]\n", dest_reg, num); + else + { + if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1)) + ; /* EAX,EAX or EDX,EDX */ + else + e("mov %s, %s\n", dest_reg, reg[num]); + } +} + +void ReadLong() +{ + if (memmap_type == 0 || memmap_type == 1) e("call ReadLong\n"); + else if (memmap_type == 2) e("call dword [read_long]\n"); +} + +void ReadByte() +{ + if (memmap_type == 0 || memmap_type == 1) e("call ReadByte\n"); + else if (memmap_type == 2) e("call dword [read_byte]\n"); +} + +void ReadWord() +{ + if (memmap_type == 0 || memmap_type == 1) e("call ReadWord\n"); + else if (memmap_type == 2) e("call dword [read_word]\n"); +} + +void WriteLong() +{ + if (memmap_type == 0 || memmap_type == 1) e("call WriteLong\n"); + else if (memmap_type == 2) e("call dword [write_long]\n"); +} + +void WriteByte() +{ + if (memmap_type == 0 || memmap_type == 1) e("call WriteByte\n"); + else if (memmap_type == 2) e("call dword [write_byte]\n"); +} + +void WriteWord() +{ + if (memmap_type == 0 || memmap_type == 1) e("call WriteWord\n"); + else if (memmap_type == 2) e("call dword [write_word]\n"); +} + +void ReadByteSX() +{ + fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n"); + exit(1); +} + +void ReadWordSX() +{ + if (memmap_type == 0 || memmap_type == 1) e("call ReadWordSX\n"); + else if (memmap_type == 2) + { + e("call dword [read_word]\n"); + e("movsx edx, dx\n"); + } +} + +/* + * LoadFromEA() and MOVEM() use the ReadXXXPC() functions. + */ + +void ReadLongPC() +{ + e("call ReadLongPC\n"); +} + +void ReadBytePC() +{ + e("call ReadBytePC\n"); +} + +void ReadWordPC() +{ + e("call ReadWordPC\n"); +} + +void ReadByteSXPC() +{ + fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n"); + exit(1); + + e("call ReadBytePC\n"); + e("movsx edx, dl\n"); +} + +void ReadWordSXPC() +{ + e("call ReadWordPC\n"); + e("movsx edx, dx\n"); +} + +void UpdateFetchPtr() /* scan the fetch area */ +{ + int inst = 0; + /* + * In: ESI = address + * Out: ESI = PC + ptr + * EBP = ptr (base) + * EDX = trashed + */ + + /* + * Note: If address bus is NOT 32-bits, the following applies. + * + * The unused bits of the PC MUST be preserved. Therefore, those unused + * bits are subtracted from the base pointer so that when we add a PC + * WITH unused bits, it won't go out of range, because it will be far + * below range to begin with. + * + * The reasoning here is that if we subtract the unused bits from the base + * pointer, the unused bits in the normalized PC pointer (ESI) will cancel + * out. + */ + if (addr_mask != 0xffffffff) + SaveReg("fetch", "esi"); + AddrClip("esi"); + e("mov edx, [fetch]\n"); + e(".find_fetch_loop%d:\n", inst); + e("add edx, byte "SIZEOF_FETCHREGION"\n"); + e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */ + e("je near fetch_error\n"); + e("cmp esi, [edx]\n"); /* offset 0: base. above it? */ + e("jb .find_fetch_loop%d\n", inst); /* nope, not this region */ + e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ + e("ja .find_fetch_loop%d\n", inst); /* nope, not this region */ + e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */ + if (addr_mask != 0xffffffff) /* to handle those nasty unused bits! */ + { + e("mov edx, [fetch_esi]\n"); + e("mov esi, edx\n"); /* this is the ACTUAL PC we saved earlier */ + e("and edx, 0x%08X\n", ~addr_mask);/* we only want the unused bits */ + e("sub ebp, edx\n"); + } + e("add esi, ebp\n"); /* +pc=pc ptr.. */ +} + +void LoadCCR() /* CCR->AX ... trashes AX and DX */ +{ + e("mov dh, [sr]\n"); /* DH:***XNZVC */ + e("mov ah, dh\n"); /* AH:***XNZVC */ + e("shl ah, byte 4\n"); /* AH:NZVC0000 */ + e("test dh, byte 2\n"); /* V flag? */ + e("setnz al\n"); /* AL:0000000V */ + e("test dh, 0x10\n"); /* X flag? */ + e("setnz byte [x]\n"); /* X:0000000000000000000000000000000X */ + e("and dh, byte 1\n"); /* DH:0000000C */ + e("or ah, dh\n"); /* AH:NZ**000C */ +} + +void SaveCCR() /* AX->CCR... trashes AX and DX */ +{ + e("and eax, 0xc101\n"); /* EAX:0000000000000000NZ00000C0000000V */ + e("mov dh, ah\n"); /* DH:NZ00000C */ + e("shr dh, byte 4\n"); /* DH:0000NZ00 */ + e("shl al, byte 1\n"); /* AL:000000V0 */ + e("or dh, al\n"); /* DH:0000NZV0 */ + e("shr ah, byte 1\n"); /* CF:C */ + e("adc dh, 0\n"); /* DH:0000NZVC */ + e("mov ah, [x]\n"); /* AH:0000000X */ + e("shl ah, byte 4\n"); /* AH:000X0000 */ + e("or dh, ah\n"); /* DH:000XNZVC */ + e("mov [sr], dh\n"); +} + +void InstBegin(unsigned short op, char *mnem, unsigned num) +{ +#ifdef PROFILE + int i; +#endif + + Align(4); + if (num == 1) + e("I%04X: ; %s\t\t0x%04X\n", op, mnem, op); + else + e("I%04X: ; %s\t\t0x%04X-0x%04X\n", op, mnem, op, op+(num-1)); + if (debug) + { + // Debugging code + char noDebugLabel[16]; + char noPCChgLabel[16]; + sprintf(noDebugLabel, ".no_debug%04X", op); + sprintf(noPCChgLabel, ".no_pcchg%04X", op); + + e("mov ebx, [Debug]\n"); /* Get debug handler */ + e("test ebx, ebx\n"); /* If no handler set, skip debugging */ + e("jz short %s\n", noDebugLabel); + + e("push eax\n"); /* Note: __cdecl calling convention used to call handler */ + e("push edi\n"); + e("push ebp\n"); + WRITEPCTOMEM("pc"); /* Save esi */ + e("mov [remaining], ecx\n"); + e("push edi\n"); /* Pass instruction opcode to handler */ + e("sub esi, ebp\n"); /* Convert PC to base offset */ + e("sub esi, byte 2\n"); /* Adjust back to beginning of instruction */ + e("push esi\n"); /* Pass result to handler */ + e("call ebx\n"); /* Call handler */ + e("add esp, byte 8\n"); + e("mov esi, [pc]\n"); /* Restore esi (this could have been changed) */ + e("mov ecx, [remaining]\n"); /* This could have been changed */ + e("pop ebp\n"); + e("pop edi\n"); + e("add esi, ebp\n"); /* Convert esi to pointer (base+pc) */ + e("test eax, eax\n"); /* Check if debug handler changed PC */ + e("jz short %s\n", noPCChgLabel); + e("pop eax\n"); /* If so, move onto instruction at new PC */ + e("mov di, [esi]\n"); + e("add esi, byte 2\n"); /* Point to word after next opcode */ + e("jmp dword [jmptab+edi*4]\n"); + + EmitLabel(noPCChgLabel); + e("pop eax\n"); + + EmitLabel(noDebugLabel); + } + decoded[op] = num; + num_handlers++; + +#ifdef PROFILE + for (i = 0; i < strlen(mnem); i++) /* remove spaces */ + { + if (mnem[i] == ' ') + mnem[i] = '_'; + } + e("inc dword [prof%s]\n", mnem); + for (i = 0; prof[i] != NULL && i < 512; i++) + { + if (!strcmp(prof[i], mnem)) /* already have one of these... */ + return; + } + prof[prof_i] = calloc(strlen(mnem) + 1, 1); + strcpy(prof[prof_i], mnem); + prof_i++; +#endif +} + +void InstEnd() +{ + e("mov di, [esi]\n"); + e("add esi, byte 2\n"); /* point to word after next opcode */ + e("jmp dword [jmptab+edi*4]\n"); +} + + +/***************************************************************************** +* Address Space Management */ + +/* + * Input: Nothing + * Notes: Assumes that run_ecx, run_edi, and run_esi are available for use. + */ + +void SetSupervisorAddressSpace() +{ + if (!multiaddr) return; + e("push ecx\n"); + e("cld\n"); /* direction: forward */ + e("push esi\n"); + e("push edi\n"); + e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */ + e("mov edi, fetch\n"); + e("mov esi, super_fetch\n"); + e("rep movsd\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop ecx\n"); +} + +void SetUserAddressSpace() +{ + if (!multiaddr) return; + e("push ecx\n"); + e("cld\n"); /* direction: forward */ + e("push esi\n"); + e("push edi\n"); + e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */ + e("mov edi, fetch\n"); + e("mov esi, user_fetch\n"); + e("rep movsd\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop ecx\n"); +} + + +/***************************************************************************** +* Timing */ + +/* TimingEA: calculates amount of clocks taken by a given EA mode */ +unsigned TimingEA(unsigned ea, int size) +{ /* EA format = MMMRRR (mode/register) */ + switch ((ea >> 3) & 7) /* mode */ + { + case 0: case 1: return 0; break; + case 2: case 3: + switch (size) { case BYTE_SIZE: case WORD_SIZE: return 4; break; + case LONG_SIZE: return 8; break; } break; + case 4: + switch (size) { case BYTE_SIZE: case WORD_SIZE: return 6; break; + case LONG_SIZE: return 10; break; } break; + case 5: + switch (size) { case BYTE_SIZE: case WORD_SIZE: return 8; break; + case LONG_SIZE: return 12; break; } break; + case 6: + switch (size) { case BYTE_SIZE: case WORD_SIZE: return 10; break; + case LONG_SIZE: return 14; break; } break; + case 7: /* misc... */ + switch (ea & 0x7) { case 0: /* (xxx).W */ + switch (size) + { case BYTE_SIZE: case WORD_SIZE: return 8; break; + case LONG_SIZE: return 12; break; } break; + case 1: /* (xxx).L */ + switch (size) + { case BYTE_SIZE: case WORD_SIZE: return 12; break; + case LONG_SIZE: return 16; break; } break; + case 4: /* # */ + switch (size) + { case BYTE_SIZE: case WORD_SIZE: return 4; break; + case LONG_SIZE: return 8; break; } break; + case 2: /* (d16,pc) */ + switch (size) + { case BYTE_SIZE: case WORD_SIZE: return 8; break; + case LONG_SIZE: return 12; break; } break; + case 3: /* (d8,pc,Xn) */ + switch (size) + { case BYTE_SIZE: case WORD_SIZE: return 10; break; + case LONG_SIZE: return 14; break; } break; + } + } return 0; +} + +void EmitTiming(unsigned cycles) +{ + if (cycles) + { + if (cycles == 1) e("dec ecx\n"); + else if (!(cycles & 0x80)) e("sub ecx, byte %d\n", cycles); + else e("sub ecx, %d\n", cycles); + e("js near Turbo68KRun_done\n"); /* below 0, finished */ + } +} + + +/***************************************************************************** +* Effective Address Code */ + +int CheckEA(unsigned ea, char *valid) /* EA = MMMRRR (mode/reg) */ +{ + /* + * valid[] string is a mask for valid addressing modes as they appear + * in the 68K manual, in order, 1 = okay, 0 = not allowed + */ + + if (((ea >> 3) & 7) != 7) /* mode 7 is handled separately */ + { + if (valid[(ea >> 3) & 7] != '1') return 0; /* not allowed! */ + else return 1; + } + else /* handle mode 7 here */ + { + /* first check if the register is invalid */ + if ((ea & 7) >= 5) return 0; /* no such thing! */ + else + { + switch (ea & 7) + { + case 0: + case 1: + if (valid[7+(ea & 7)] != '1') return 0; + else return 1; break; + case 4: + if (valid[7+2] != '1') return 0; + else return 1; break; + case 2: + if (valid[7+3] != '1') return 0; + else return 1; break; + case 3: + if (valid[7+4] != '1') return 0; + else return 1; break; + } + } + } return 0; +} + +int NumDecodedEA(unsigned ea) /* how many handlers can this feed? */ +{ + /* + * Turbo68K does not generate an instruction handler for every possible + * opcode, instead, it generates one for every possible EA mode of a given + * instruction. This means if reg==0, it returns the # to pass to InstEnd + * (which indicates how many opcodes this will cover for) Otherwise, this + * function returns 0 and the instruction handler should not be emitted. + */ + + if (((ea >> 3) & 7) <= 6 && (ea & 7) != 0) return 0; + else if (((ea >> 3) & 7) <= 6 && (ea & 7) == 0) return 8; /* 8 regs */ + else if (((ea >> 3) & 7) == 7 && (ea & 7) <= 4) return 1; /* 1 reg */ + return 0; +} + +/* + * Effective Address calculations + * + * Note: When loading words, usually the whole dword is loaded. When storing, + * only the word part is stored + */ + +/* + * Returns: Data in EDX, address preserved in EBX + * Used: EBX, EDX, EDI (unless reading from register) + * Notes: ESI is assumed to point at word after opcode + * Assumes EDI contains opcode and that it has EA at the very end + * This function is only to be used when EA mode fields are located + * at bits 0-5. + */ + +void LoadFromEA(unsigned ea, int size, int sign_extend) +{ + switch ((ea >> 3) & 7) + { + case 0: /* Dn */ + case 1: /* An */ + { + char r = ((ea >> 3) & 7) ? 'a' : 'd'; + e("and edi, byte 7\n"); /* get reg number, EDI contains opcode */ + if (size == LONG_SIZE) + e("mov edx, [%c+edi*4]\n", r); + else + { + if (size == BYTE_SIZE) + { + if (!sign_extend) + e("mov dl, [%c+edi*4]\n", r); + else + e("movsx edx, byte [%c+edi*4]\n", r); + } + else /* word size */ + { + if (!sign_extend) + e("mov edx, [%c+edi*4]\n", r); + else + e("movsx edx, word [%c+edi*4]\n", r); + } + } + } + break; + case 2: /* (An) */ + case 3: /* (An)+ */ + case 4: /* -(An) */ + case 5: /* (d16,An) */ + e("and edi, byte 7\n"); /* reg */ + if (((ea >> 3) & 7) == 4) /* pre-dec */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* A7 bytes */ + e("cmc\n"); /* switch carry */ + e("sbb dword [a+edi*4], byte 1\n"); + } + else if (size == WORD_SIZE) + e("sub dword [a+edi*4], byte 2\n"); + else if (size == LONG_SIZE) + e("sub dword [a+edi*4], byte 4\n"); + } + if (((ea >> 3) & 7) == 5) /* (d16,An) */ + { + e("movsx ebx, word [esi]\n"); + e("add ebx, [a+edi*4]\n"); + e("add esi, byte 2\n"); /* past d16, next instruction */ + } + else + e("mov ebx, [a+edi*4]\n"); + if (((ea >> 3) & 7) == 3) /* post-inc */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* A7 bytes */ + e("cmc\n"); /* switch carry */ + e("adc dword [a+edi*4], byte 1\n"); + } + else if (size == WORD_SIZE) + e("add dword [a+edi*4], byte 2\n"); + else if (size == LONG_SIZE) + e("add dword [a+edi*4], byte 4\n"); + } + switch (size) + { + case BYTE_SIZE: + if (sign_extend) + ReadByteSX(); + else + ReadByte(); + break; + case WORD_SIZE: + if (sign_extend) + ReadWordSX(); + else + ReadWord(); + break; + case LONG_SIZE: + ReadLong(); + break; + } + break; + case 6: /* (d8,An,Xn) */ + e("and edi, byte 7\n"); /* An reg # */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edx, bl\n"); /* sign-extended index */ + e("add esi, byte 2\n"); /* point at next word */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ll\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ll"); + e("add ebx, edx\n"); /* disp+index_reg */ + e("add ebx, [a+edi*4]\n"); + switch (size) + { + case BYTE_SIZE: + if (sign_extend) + ReadByteSX(); + else + ReadByte(); + break; + case WORD_SIZE: + if (sign_extend) + ReadWordSX(); + else + ReadWord(); + break; + case LONG_SIZE: + ReadLong(); + break; + } + break; + case 0x7: + switch (ea & 7) + { + case 0: /* (xxx).W */ + case 1: /* (xxx).L */ + if ((ea & 7) == 0) /* word */ + { + e("movsx ebx, word [esi]\n"); + e("add esi, byte 2\n"); + } + else /* long */ + { + e("mov ebx, [esi]\n"); + e("ror ebx, byte 16\n"); /* word-swap */ + e("add esi, byte 4\n"); + } + switch (size) + { + case BYTE_SIZE: + if (sign_extend) + ReadByteSX(); + else + ReadByte(); + break; + case WORD_SIZE: + if (sign_extend) + ReadWordSX(); + else + ReadWord(); + break; + case LONG_SIZE: + ReadLong(); + break; + } + break; + case 4: /* # */ + switch (size) + { + case BYTE_SIZE: + if (!sign_extend) + e("mov dl, [esi]\n"); /* get low byte of ext. word */ + else + e("movsx edx, byte [esi]\n"); + e("add esi, byte 2\n"); + break; + case WORD_SIZE: + if (!sign_extend) + e("mov edx, [esi]\n"); /* bx = word, ebx=????:word */ + else + e("movsx edx, word [esi]\n"); + e("add esi, byte 2\n"); + break; + case LONG_SIZE: + e("mov edx, [esi]\n"); /* get long-word */ + e("add esi, byte 4\n"); + e("ror edx, byte 16\n"); /* word swap to work... */ + break; + } + break; + case 2: /* (d16,PC) */ + /* + * The PC is assumed to be pointing at the extension word, which + * is exactly how the emulator works + */ + e("movsx ebx, word [esi]\n"); + e("add ebx, esi\n"); + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); + switch (size) + { + case BYTE_SIZE: + if (sign_extend) + pcfetch ? ReadByteSXPC() : ReadByteSX(); + else + pcfetch ? ReadBytePC() : ReadByte(); + break; + case WORD_SIZE: + if (sign_extend) + pcfetch ? ReadWordSXPC() : ReadWordSX(); + else + pcfetch ? ReadWordPC() : ReadWord(); + break; + case LONG_SIZE: + pcfetch ? ReadLongPC() : ReadLong(); + break; + } + break; + case 3: /* (d8,PC,Xn) */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edx, bl\n"); /* sign-extended index */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ll\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ll"); + e("add ebx, edx\n"); /* disp+index_reg */ + e("add ebx, esi\n"); /* add the PC */ + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); /* point at next word */ + switch (size) + { + case BYTE_SIZE: + if (sign_extend) + pcfetch ? ReadByteSXPC() : ReadByteSX(); + else + pcfetch ? ReadBytePC() : ReadByte(); + break; + case WORD_SIZE: + if (sign_extend) + pcfetch ? ReadWordSXPC() : ReadWordSX(); + else + pcfetch ? ReadWordPC() : ReadWord(); + break; + case LONG_SIZE: + pcfetch ? ReadLongPC() : ReadLong(); + break; + } + break; + } + break; + default: + printf("Error!\a\n"); + break; + } +} + +/* + * Expects: Data in EDX, address in EBX + * Notes: Destroys EBX, make sure to preserve the address if you need it. + */ + +void StoreToEA(unsigned ea, int size, int same) +{ /* the "same" parameter is ignored. it no longer is needed */ + switch ((ea >> 3) & 7) + { + case 0: /* Dn */ + case 1: /* An */ + { + char r = ((ea >> 3) & 7) ? 'a' : 'd'; + if (size == LONG_SIZE) + e("mov [%c+%d*4], edx\n", r, ea & 7); + else if (size == BYTE_SIZE) + e("mov [%c+%d*4], dl\n", r, ea & 7); + else if (size == WORD_SIZE) + e("mov [%c+%d*4], dx\n", r, ea & 7); + } + break; + case 2: /* (An) */ + case 3: /* (An)+ */ + case 4: /* -(An) */ + case 5: /* (d16,An) */ + if (((ea >> 3) & 7) == 4) /* pre-dec */ + { + if (size == BYTE_SIZE) + { + if ((ea & 7) == 7) /* A7 kept word aligned */ + e("sub dword [a+%d*4], byte 2\n", ea & 7); + else + e("dec dword [a+%d*4]\n", ea & 7); + } + else if (size == WORD_SIZE) + e("sub dword [a+%d*4], byte 2\n", ea & 7); + else if (size == LONG_SIZE) + e("sub dword [a+%d*4], byte 4\n", ea & 7); + } + if (((ea >> 3) & 7) == 5) /* (d16,An) */ + { + e("movsx ebx, word [esi]\n"); + e("add ebx, [a+%d*4]\n", ea & 7); + e("add esi, byte 2\n"); /* past d16, next instruction */ + } + else + e("mov ebx, [a+%d*4]\n", ea & 7); + if (((ea >> 3) & 7) == 3) /* post-inc */ + { + if (size == BYTE_SIZE) + { + if ((ea & 7) == 7) /* A7 kept word aligned */ + e("add dword [a+%d*4], byte 2\n", ea & 7); + else + e("inc dword [a+%d*4]\n", ea & 7); + } + else if (size == WORD_SIZE) + e("add dword [a+%d*4], byte 2\n", ea & 7); + else if (size == LONG_SIZE) + e("add dword [a+%d*4], byte 4\n", ea & 7); + } + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 6: /* (d8,An,Xn) */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edi, bl\n"); /* sign-extended index */ + e("add esi, byte 2\n"); /* point at next word */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ls\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ls"); + e("add ebx, edi\n"); /* disp+index_reg */ + e("add ebx, [a+%d*4]\n", ea & 7); + switch (size) /* WriteXXX() clears EDI */ + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 0x7: + switch (ea & 7) + { + case 0: /* (xxx).W */ + case 1: /* (xxx).L */ + if ((ea & 7) == 0) /* word */ + { + e("movsx ebx, word [esi]\n"); + e("add esi, byte 2\n"); + } + else /* long */ + { + e("mov ebx, [esi]\n"); + e("ror ebx, byte 16\n"); /* word-swap */ + e("add esi, byte 4\n"); + } + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 4: /* # */ + /* can't store to #data! */ + printf("Error!\a\n"); + break; + case 2: /* (d16,PC) */ + /* + * The PC is assumed to be pointing at the extension word, which + * is exactly how the emulator works + */ + e("movsx ebx, word [esi]\n"); + e("add ebx, esi\n"); + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 3: /* (d8,PC,Xn) */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edi, bl\n"); /* sign-extended index */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ls\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ls"); + e("add ebx, edi\n"); /* disp+index_reg */ + e("add ebx, esi\n"); /* add the PC */ + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); /* point at next word */ + switch (size) /* WriteXXX() clears EDI */ + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + } + break; + default: + break; + } +} + +/* + * This is the same as StoreToEA() except that it uses EDI&7 to find the + * register. It is assumed that EA is bits 0-5 of the opcode. This only works + * in special situations like CLR and Scc. + */ + +void StoreToEAUsingEDI(unsigned ea, int size, int same) +{ /* the "same" parameter is ignored. it no longer is needed */ + switch ((ea >> 3) & 7) + { + case 0: /* Dn */ + case 1: /* An */ + { + char r = ((ea >> 3) & 7) ? 'a' : 'd'; + e("and edi, byte 7\n"); + if (size == LONG_SIZE) + e("mov [%c+edi*4], edx\n", r); + else if (size == BYTE_SIZE) + e("mov [%c+edi*4], dl\n", r); + else if (size == WORD_SIZE) + e("mov [%c+edi*4], dx\n", r); + } + break; + case 2: /* (An) */ + case 3: /* (An)+ */ + case 4: /* -(An) */ + case 5: /* (d16,An) */ + e("and edi, byte 7\n"); + if (((ea >> 3) & 7) == 4) /* pre-dec */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* A7 bytes */ + e("cmc\n"); /* switch carry */ + e("sbb dword [a+edi*4], byte 1\n"); + } + else if (size == WORD_SIZE) + e("sub dword [a+edi*4], byte 2\n"); + else if (size == LONG_SIZE) + e("sub dword [a+edi*4], byte 4\n"); + } + if (((ea >> 3) & 7) == 5) /* (d16,An) */ + { + e("movsx ebx, word [esi]\n"); + e("add ebx, [a+edi*4]\n"); + e("add esi, byte 2\n"); /* past d16, next instruction */ + } + else + e("mov ebx, [a+edi*4]\n"); + if (((ea >> 3) & 7) == 3) /* post-inc */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* A7 bytes */ + e("cmc\n"); /* switch carry */ + e("adc dword [a+edi*4], byte 1\n"); + } + else if (size == WORD_SIZE) + e("add dword [a+edi*4], byte 2\n"); + else if (size == LONG_SIZE) + e("add dword [a+edi*4], byte 4\n"); + } + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 6: /* (d8,An,Xn) */ + e("and edi, byte 7\n"); /* An reg # */ + SaveReg("memhandler", "edi"); /* no memhandlers using this now... */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edi, bl\n"); /* sign-extended index */ + e("add esi, byte 2\n"); /* point at next word */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ll\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ll"); + e("add ebx, edi\n"); /* disp+index_reg */ + RestoreReg("memhandler", "edi"); + e("add ebx, [a+edi*4]\n"); + switch (size) /* WriteXXX() clears EDI */ + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 0x7: + switch (ea & 7) + { + case 0: /* (xxx).W */ + case 1: /* (xxx).L */ + if ((ea & 7) == 0) /* word */ + { + e("movsx ebx, word [esi]\n"); + e("add esi, byte 2\n"); + } + else /* long */ + { + e("mov ebx, [esi]\n"); + e("ror ebx, byte 16\n"); /* word-swap */ + e("add esi, byte 4\n"); + } + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 4: /* # */ + /* can't store to #data! */ + break; + case 2: /* (d16,PC) */ + /* + * The PC is assumed to be pointing at the extension word, which + * is exactly how the emulator works + */ + e("movsx ebx, word [esi]\n"); + e("add ebx, esi\n"); + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + case 3: /* (d8,PC,Xn) */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edi, bl\n"); /* sign-extended index */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ls\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ls"); + e("add ebx, edi\n"); /* disp+index_reg */ + e("add ebx, esi\n"); /* add the PC */ + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); /* point at next word */ + switch (size) /* WriteXXX() clears EDI */ + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + break; + } + break; + default: + printf("Error!\a\n"); + break; + } +} + + +/* + * Returns: Address in EBX + * Used: EBX, EDX + * Notes: ESI is assumed to point at word after opcode + * Assumes EDI contains opcode and that it has EA at the very end + * This function is only to be used when EA mode fields are located + * at bits 0-5. + */ + +void LoadControlEA(unsigned ea) +{ + switch ((ea >> 3) & 7) + { + case 2: /* (An) */ + case 3: /* (An)+ (just load what's in (An) -- MOVEM needs this) */ + case 4: /* -(An) (just load what's in (An) -- MOVEM needs this) */ + case 5: /* (d16,An) */ + e("and edi, byte 7\n"); /* reg */ + if (((ea >> 3) & 7) == 5) /* (d16,An) */ + { + e("movsx ebx, word [esi]\n"); + e("add ebx, [a+edi*4]\n"); + e("add esi, byte 2\n"); /* past d16, next instruction */ + } + else + e("mov ebx, [a+edi*4]\n"); + break; + case 6: /* (d8,An,Xn) */ + e("and edi, byte 7\n"); /* edi=An # */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edx, bl\n"); /* sign-extended index */ + e("add esi, byte 2\n"); /* point at next word */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ll\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ll"); + e("add ebx, edx\n"); /* disp+index_reg */ + e("add ebx, [a+edi*4]\n"); + break; + case 0x7: + switch (ea & 7) + { + case 0: /* (xxx).W */ + case 1: /* (xxx).L */ + if ((ea & 7) == 0) /* word */ + { + e("movsx ebx, word [esi]\n"); + e("add esi, byte 2\n"); + } + else /* long */ + { + e("mov ebx, [esi]\n"); + e("ror ebx, byte 16\n"); /* word-swap */ + e("add esi, byte 4\n"); + } + break; + case 2: /* (d16,PC) */ + /* + * The PC is assumed to be pointing at the extension word, which + * is exactly how the emulator works + */ + e("movsx ebx, word [esi]\n"); + e("add ebx, esi\n"); + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); + break; + case 3: /* (d8,PC,Xn) */ + e("xor ebx, ebx\n"); /* so the high part is clear */ + e("mov bx, [esi]\n"); /* fetch extension word */ + e("movsx edx, bl\n"); /* sign-extended index */ + e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ + /* the shr sets CF if Long reg */ + e("mov ebx, [d+ebx*4]\n"); + e("jc short .ll\n"); /* CF=1? index reg is LONG */ + e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ + EmitLabel(".ll"); + e("add ebx, edx\n"); /* disp+index_reg */ + e("add ebx, esi\n"); /* add the PC */ + e("sub ebx, ebp\n"); + e("add esi, byte 2\n"); /* point at next word */ + break; + } + break; + default: + printf("Warning: LoadControlEA() defaulted\n"); + break; + } +} + + +/***************************************************************************** +* Instruction Handlers */ + +int RTD(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("mov ebx, [a+7*4]\n"); + ReadLong(); + e("xor ebx, ebx\n"); + e("add dword [a+7*4], byte 4\n"); + e("mov bx, [esi]\n"); + e("add dword [a+7*4], ebx\n"); /* SP+4+disp */ + e("mov esi, edx\n"); + UpdateFetchPtr(); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int MOVEC(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * Timing for an illegal control register is not handled, control is + * simply passed to the illegal instruction exception handler, which uses + * different timing. + */ + + InstBegin(op, mnem, 2); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + + e("xor ebx, ebx\n"); + e("xor edx, edx\n"); + e("mov dx, [esi]\n"); /* fetch data word */ + e("mov ebx, edx\n"); + e("shr ebx, byte 12\n"); /* EBX=general register */ + + e("shr edi, byte 1\n"); /* dr field */ + e("jc short .to_control\n"); + + /* + * Test for control register. The control register value is placed in + * EDX. At the end, EDX is moved to [d+ebx*4]. + */ + + e("and edx, 0xfff\n"); + e("jnz short .from_dfc\n"); + e("mov dl, [fc]\n"); /* SFC */ + e("jmp short .end_from\n"); + + EmitLabel(".from_dfc"); + e("cmp edx, byte 1\n"); + e("jne short .from_usp\n"); + e("mov dl, [fc+1]\n"); /* DFC */ + e("jmp short .end_from\n"); + + EmitLabel(".from_usp"); + e("cmp edx, 0x800\n"); + e("jne short .from_vbr\n"); + e("mov edx, [__sp]\n"); /* we're in supervisor, USP is in __sp */ + e("jmp short .end_from\n"); + + EmitLabel(".from_vbr"); + e("cmp edx, 0x801\n"); + e("jne near exception_illegal_instruction\n"); /* invalid control reg */ + e("mov edx, [vbr]\n"); /* VBR */ + + EmitLabel(".end_from"); + e("mov [d+ebx*4], edx\n"); /* move to general reg */ + e("add esi, byte 2\n"); /* remember, we fetched a word */ + EmitTiming(12); + e("jmp short .end\n"); + + /* + * Test for control register. General reg is written to control reg. + */ + + EmitLabel(".to_control"); + e("mov ebx, [d+ebx*4]\n"); /* general reg */ + + e("and edx, 0xfff\n"); + e("jnz short .to_dfc\n"); + e("and bl, 3\n"); + e("mov [fc], bl\n"); /* SFC */ + e("jmp short .end_to\n"); + + EmitLabel(".to_dfc"); + e("cmp edx, byte 1\n"); + e("jne short .to_usp\n"); + e("and bl, 3\n"); + e("mov [fc+1], bl\n"); /* DFC */ + e("jmp short .end_to\n"); + + EmitLabel(".to_usp"); + e("cmp edx, 0x800\n"); + e("jne short .to_vbr\n"); + e("mov [__sp], ebx\n"); /* we're in supervisor, USP is in __sp */ + e("jmp short .end_to\n"); + + EmitLabel(".to_vbr"); + e("cmp edx, 0x801\n"); + e("jne near exception_illegal_instruction\n"); /* invalid control reg */ + e("mov [vbr], ebx\n"); /* VBR */ + + EmitLabel(".end_to"); + e("add esi, byte 2\n"); /* remember, we fetched a word */ + EmitTiming(10); + + /* + * End + */ + + EmitLabel(".end"); + InstEnd(); + return 2; +} + +int BKPT(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * NOTE: Timing isn't done here, the exception processing code will do it. + * The exception code doesn't use the proper timing for a BKPT, but it's + * good enough ;) + */ + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); /* vector # */ + e("mov ebx, [Bkpt]\n"); /* BKPT handler */ + e("test ebx, ebx\n"); /* if no handler, don't do anything */ + e("jz short .no_handler\n"); + + e("push eax\n"); + e("push edi\n"); + e("push ebp\n"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + e("push edi\n"); /* vector # */ + e("call ebx\n"); + e("add esp, byte 4\n"); + e("mov esi, [pc]\n"); + e("mov ecx, [remaining]\n"); /* this could have been changed */ + e("pop ebp\n"); + e("pop edi\n"); + e("pop eax\n"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + + EmitLabel(".no_handler"); + e("jmp near exception_illegal_instruction\n"); + return 8; +} + +int MOVEfromCCR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, t = 4; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + e("xor edx, edx\n"); /* CCR->(sign extend)->word */ + e("mov dl, [sr]\n"); + StoreToEA(ea, WORD_SIZE, 0); + if ((ea >> 3) != 0) t += 4; /* mem */ + EmitTiming(t + TimingEA(ea, WORD_SIZE)); /* technically, word sized */ + InstEnd(); + return 1; +} + +int TRAPV(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + + e("test al, al\n"); /* do only if V */ + e("jz near .no_trap\n"); + + e("mov edx, 0x2000\n"); /* what the SR will be */ + e("xor edx, [sr]\n"); /* see if S bit is different */ + e("test edx, 0x2000\n"); + e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ + e("mov edx, [__sp]\n"); + e("xchg [a+7*4], edx\n"); + e("mov [__sp], edx\n"); + SetSupervisorAddressSpace(); + EmitLabel(".no_sp_magic"); + + e("mov ebx, [a+7*4]\n"); /* SP */ + + /* + * If 68010, push format info + */ + + if (mpu == 68010) + { + e("sub ebx, byte 2\n");/* SP-2 */ + e("mov edx, 7*4\n"); /* TRAPV vector | 0x00000000 */ + e("push ebx\n"); + WriteWord(); + e("pop ebx\n"); + } + + e("mov edx, esi\n"); + e("sub edx, ebp\n"); /* PC->EDX */ + e("sub ebx, byte 4\n"); /* SP-4 */ + e("push ebx\n"); + WriteLong(); + e("pop ebx\n"); + e("mov edx, [sr]\n"); /* get SR */ + e("sub ebx, byte 2\n"); /* SP-2 */ + e("push ebx\n"); + WriteWord(); /* save SR to stack */ + e("pop ebx\n"); + e("or dh, 0x20\n"); /* set supervisor for exception */ + e("and edx, 0xa71f\n"); /* clear unwanted bits */ + e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */ + e("mov [a+7*4], ebx\n"); /* write back SP */ + e("mov ebx, 7*4\n"); /* TRAPV vector */ + if (mpu == 68010) + e("add ebx, [vbr]\n"); + ReadLong(); /* get PC */ + e("mov esi, edx\n"); /* set new PC */ + UpdateFetchPtr(); + EmitTiming(34+4); /* TRAPV=4, exception=34 */ + InstEnd(); + EmitLabel(".no_trap"); + e("xor edi, edi\n"); + EmitTiming(4); + InstEnd(); + + return 1; +} + +int STOP(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + e("mov edx, [esi]\n"); + e("add esi, byte 2\n"); + e("and edx, 0xa71f\n"); /* mask out unimplemented bits */ + e("mov [sr], dx\n"); + LoadCCR(); + STOP_CPU(); + EmitTiming(base_timing); + e("mov dword [remaining], 0\n"); /* stop executing */ + e("jmp near Turbo68KRun_done\n"); + return 1; +} + +int RESET(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + + e("mov ebx, [Reset]\n"); /* Reset handler */ + e("test ebx, ebx\n"); /* if no handler, don't do anything */ + e("jz short .no_reset\n"); + + e("push eax\n"); + e("push ebp\n"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + e("call ebx\n"); + e("mov esi, [pc]\n"); + e("mov ecx, [remaining]\n"); /* this could have been changed */ + e("pop ebp\n"); + e("pop eax\n"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + + EmitLabel(".no_reset"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int TAS(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, BYTE_SIZE, 0); + e("test dl, dl\n"); /* test... */ + e("lahf\n"); + e("xor al, al\n"); + e("or dl, 0x80\n"); /* ...and set bit 7 */ + if ((ea >> 3) == 0) + { + e("mov [d+edi*4], dl\n"); + EmitTiming(4); + } + else + { + +/* + * NOTE: + * ----- + * + * You may wish to uncomment the WriteByte() below when emulating the Sega + * Genesis if you wish to get Gargoyles running. The TAS instruction is used + * to synchronize multiple 68Ks by grabbing the bus, but on the Genesis, the + * VDP will not release the bus resulting in data not being written. + */ + + WriteByte(); + EmitTiming(14 + TimingEA(ea, BYTE_SIZE)); + } + InstEnd(); + return 1; +} + +int NBCD(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, t; + + /* + * Notes About Undefined Flags: + * ---------------------------- + * + * N flag = Set the same as the MSB of the result + * V flag = If MSB changes from 1 to 0 when result is adjusted for BCD, + * the V flag is set, otherwise it is cleared. + */ + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + + e("xor al, al\n"); /* we use AL so we can use DAS */ + + e("shr byte [x], 1\n"); /* X->CF */ + e("sbb al, [d+edi*4]\n"); + e("mov bh, al\n"); /* save unadjusted result in BH */ + e("das\n"); /* BCD! */ + e("mov bl, ah\n"); /* store old flags */ + e("lahf\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); /* Z clear */ + e("and bl, byte 0x40\n"); /* isolate old Z */ + e("and ah, byte 0x3f\n"); /* kill new Z */ + e("or ah, bl\n"); /* in w/ old Z */ + EmitLabel(".clr"); + e("test al, al\n"); /* N flag */ + e("sets dh\n"); + e("ror dh, 1\n"); /* into MSB of DH */ + e("or ah, dh\n"); /* -> N flag */ + e("mov [d+edi*4], al\n"); + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 2, the V flag + * is set. + */ + + e("shr bh, byte 6\n"); + e("shr al, byte 7\n"); + e("and bh, 2\n"); + e("or bh, al\n"); + e("xor al, al\n"); + e("cmp bh, 2\n"); + e("jne short .no_v\n"); + e("mov al, 1\n"); /* move 1 into V flag */ + EmitLabel(".no_v"); + + t = 6; + } + else /* memory */ + { + LoadFromEA(ea, BYTE_SIZE, 0); + + e("xor al, al\n"); + e("shr byte [x], 1\n"); /* X->CF */ + e("sbb al, dl\n"); + e("mov dh, al\n"); /* DH now has unadjusted result */ + e("mov dl, ah\n"); + e("das\n"); /* BCD! */ + e("lahf\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and dl, byte 0x40\n"); + e("and ah, byte 0x3f\n"); + e("or ah, dl\n"); + EmitLabel(".clr"); + + e("test al, al\n"); /* N flag */ + e("sets dl\n"); + e("ror dl, 1\n"); /* into MSB of DL */ + e("or ah, dl\n"); /* -> N flag */ + + e("mov dl, al\n"); /* move result into DL to write it */ + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 2, the V flag + * is set. + */ + + e("shr dh, byte 6\n"); + e("shr al, byte 7\n"); + e("and dh, 2\n"); + e("or dh, al\n"); + e("xor al, al\n"); + e("cmp dh, 2\n"); + e("jne short .no_v\n"); + e("mov al, 1\n"); /* move 1 into V flag */ + EmitLabel(".no_v"); + + /* WriteXXX will clear EDI for us */ + WriteByte(); + t = 8 + TimingEA(ea, BYTE_SIZE); + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int CHK(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned reg = (op >> 9) & 7, size, ea = op & 0x3f; + + /* + * Notes About Undefined Flags: + * ---------------------------- + * + * Z flag = Set if register operand is 0 (second operand), cleared + * otherwise + * V flag = Always cleared (?) + * C flag = Always cleared (?) + */ + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + switch ((op >> 7) & 3) + { + case 3: size = WORD_SIZE; break; + default: return 0; + } + + InstBegin(op, mnem, NumDecodedEA(ea)); + /* + * We have to emit the EA timing first because it must be accounted for + * whether or not a trap occurs. DIVU and DIVS work like this too. + */ + if (TimingEA(ea, size)) + e("sub ecx, byte %d\n", TimingEA(ea, size)); + LoadFromEA(ea, size, 0); + e("xor al, al\n"); /* V should be cleared */ + e("test word [d+%d*4], 0xffff\n", reg); /* If Dn == 0 Then Z=1 */ + e("setz ah\n"); /* this also clears N and C */ + e("shl ah, 6\n"); /* put Z in proper position */ + e("test byte [d+%d*4+1], 0x80\n", reg); /* If Dn < 0 Then TRAP */ + e("jnz near .trap_dn\n"); + e("cmp [d+%d*4], dx\n", reg); /* If Dn > Source Then TRAP */ + e("jg near .trap_ea\n"); + EmitTiming(base_timing); + InstEnd(); + EmitLabel(".trap_dn"); /* set N to 1 because Dn<0 test failed */ + e("or ah, 0x80\n"); + e("jmp short .trap\n"); + EmitLabel(".trap_ea"); /* clear N because Dn>Source test failed */ + e("and ah, 0x7f\n"); + EmitLabel(".trap"); + e("jmp near exception_chk\n"); + + return 1; +} + +int MOVEP(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned dx = (op >> 9) & 7, opmode = (op >> 6) & 7, t; + + if (opmode != 4 && opmode != 5 && opmode != 6 && opmode != 7) return 0; + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + e("movsx ebx, word [esi]\n"); /* (d16,An) */ + e("add ebx, [a+edi*4]\n"); + e("add esi, byte 2\n"); + switch (opmode & 2) + { + case 2: /* reg->mem */ + if (opmode & 1) /* long */ + { + t = 24; + e("push eax\n"); /* we will be using EAX to save EBX */ + e("mov edx, [d+%d*4]\n", dx); + e("mov eax, ebx\n"); /* save EBX */ + e("rol edx, byte 8\n");/* write high order */ + WriteByte(); + e("add eax, byte 2\n"); + e("rol edx, byte 8\n");/* write mid-upper */ + e("mov ebx, eax\n"); + WriteByte(); + e("add eax, byte 2\n"); + e("rol edx, byte 8\n");/* write mid-lower */ + e("mov ebx, eax\n"); + WriteByte(); + e("add eax, byte 2\n"); + e("rol edx, byte 8\n");/* write lower order */ + e("mov ebx, eax\n"); + WriteByte(); + e("pop eax\n"); + } + else /* word */ + { + t = 16; + e("push eax\n"); /* we will be using EAX to save EBX */ + e("mov dx, [d+%d*4]\n", dx); + e("mov eax, ebx\n"); /* save EBX */ + e("ror edx, byte 8\n");/* write high order */ + WriteByte(); + e("add eax, byte 2\n"); + e("rol edx, byte 8\n");/* write low order */ + e("mov ebx, eax\n"); + WriteByte(); + e("pop eax\n"); + } + break; + case 0: /* mem->reg */ + if (opmode & 1) /* long */ + { + t = 24; + ReadByte(); /* read high order */ + e("mov [d+%d*4+3], dl\n", dx); /* store high order in Dn */ + e("add ebx, byte 2\n"); + ReadByte(); /* mid-upper order */ + e("mov [d+%d*4+2], dl\n", dx); /* store mid-upper order */ + e("add ebx, byte 2\n"); + ReadByte(); /* mid-lower order */ + e("mov [d+%d*4+1], dl\n", dx); /* store mid-lower order */ + e("add ebx, byte 2\n"); + ReadByte(); /* low order */ + e("mov [d+%d*4+0], dl\n", dx); /* store low order */ + } + else /* word */ + { + t = 16; + ReadByte(); /* read high order */ + e("mov [d+%d*4+1], dl\n", dx); /* store high order in Dn */ + e("add ebx, byte 2\n"); + ReadByte(); /* read low order */ + e("mov [d+%d*4+0], dl\n", dx); /* store low order */ + } + break; + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int CMPM(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned size, ax = (op >> 9) & 7; + + switch ((op >> 6) & 3) + { + case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; + } + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + + e("mov ebx, [a+edi*4]\n"); /* get Ay */ + if (size == BYTE_SIZE) /* Ay */ + { + e("cmp edi, byte 7\n"); /* A7 bytes */ + e("cmc\n"); /* switch carry */ + e("adc dword [a+edi*4], byte 1\n"); + } + else if (size == WORD_SIZE) + e("add dword [a+edi*4], byte 2\n"); + else if (size == LONG_SIZE) + e("add dword [a+edi*4], byte 4\n"); + + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; } + SaveReg("run", "edx"); /* save Ay */ + + e("mov ebx, [a+%d*4]\n", ax);/* get Ax */ + if (size == BYTE_SIZE) /* Ax */ + { + if (ax == 7) + e("add dword [a+7*4], byte 2\n"); + else + e("inc dword [a+%d*4]\n", ax); + } + else if (size == WORD_SIZE) + e("add dword [a+%d*4], byte 2\n", ax); + else /* LONG */ + e("add dword [a+%d*4], byte 4\n", ax); + + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; } + switch (size) /* compare */ + { + case BYTE_SIZE: + RestoreRegTo("run", "edx", "bl"); /* [run_edx]->bl */ + e("cmp dl, bl\n"); + break; + case WORD_SIZE: + RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */ + e("cmp dx, bx\n"); + break; + case LONG_SIZE: + RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */ + e("cmp edx, ebx\n"); + break; + } + e("lahf\n"); + e("seto al\n"); + + if (size != LONG_SIZE) /* byte, word */ + EmitTiming(12); + else /* long */ + EmitTiming(20); + InstEnd(); + return 1; +} + +int TRAP(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 16); + + e("and edi, byte 0xf\n"); /* lower 4 bits=vector */ + SaveReg("run", "edi"); + + e("mov edx, 0x2000\n"); /* what the SR will be */ + e("xor edx, [sr]\n"); /* see if S bit is different */ + e("test edx, 0x2000\n"); + e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ + e("mov edx, [__sp]\n"); + e("xchg [a+7*4], edx\n"); + e("mov [__sp], edx\n"); + SetSupervisorAddressSpace(); + EmitLabel(".no_sp_magic"); + + e("mov ebx, [a+7*4]\n"); /* SP */ + + /* + * If 68010, push format info + */ + + if (mpu == 68010) + { + e("sub ebx, byte 2\n");/* SP-2 */ + RestoreReg("run", "edi"); /* vector */ + e("mov edx, edi\n"); + SaveReg("run", "edi"); + e("add edx, byte 32\n"); + e("shl edx, byte 2\n"); /* TRAP vector | 0x00000000 */ + SaveReg("run", "ebx"); + WriteWord(); + RestoreReg("run", "ebx"); + } + + e("mov edx, esi\n"); + e("sub edx, ebp\n"); /* PC->EDX */ + e("sub ebx, byte 4\n"); /* SP-4 */ + e("push ebx\n"); + WriteLong(); + e("pop ebx\n"); + e("mov edx, [sr]\n"); /* get SR */ + e("sub ebx, byte 2\n"); /* SP-2 */ + e("push ebx\n"); + SaveReg("run", "ebx"); + WriteWord(); /* save SR to stack */ + e("pop ebx\n"); + e("or dh, 0x20\n"); /* set supervisor for exception */ + e("and edx, 0xa71f\n"); /* clear unwanted bits */ + e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */ + e("mov [a+7*4], ebx\n"); /* write back SP */ + RestoreReg("run", "edi"); + e("mov ebx, edi\n"); + e("add ebx, byte 32\n"); /* vector+32=trap vector */ + e("shl ebx, 2\n"); + if (mpu == 68010) + e("add ebx, [vbr]\n"); + + ReadLong(); /* get PC */ + e("mov esi, edx\n"); /* set new PC */ + UpdateFetchPtr(); + e("xor edi, edi\n"); + EmitTiming(base_timing); + InstEnd(); + + return 1; +} + +int EORItoSR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + SaveCCR(); /* save flags to SR */ + e("mov edx, [esi]\n"); + e("and edx, 0xa71f\n"); /* mask out unwanted bits */ + e("xor [sr], dx\n"); + e("add esi, byte 2\n"); + LoadCCR(); /* get flags back */ + e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */ + e("jnz short .in_s\n"); + e("mov ebx, [a+7*4]\n"); + e("mov edx, [__sp]\n"); + e("mov [a+7*4], edx\n"); + e("mov [__sp], ebx\n"); + SetUserAddressSpace(); /* map in user address space */ + EmitLabel(".in_s"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int EORItoCCR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + SaveCCR(); + e("mov dl, [esi]\n"); + e("and dl, 0x1f\n"); + e("xor [sr], dl\n"); + LoadCCR(); + e("add esi, byte 2\n"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int RTR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("mov ebx, [a+7*4]\n"); /* (SP)->CCR */ + ReadWord(); + e("and dl, 0x1f\n"); + e("mov [sr], dl\n"); + e("add dword [a+7*4], byte 2\n"); /* SP+2->SP */ + LoadCCR(); + e("mov ebx, [a+7*4]\n"); /* (SP)->PC */ + ReadLong(); + e("add dword [a+7*4], byte 4\n"); /* SP+4->SP */ + e("mov esi, edx\n"); + UpdateFetchPtr(); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int SUBX(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + InstBegin(op, mnem, 8); + + e("and edi, byte 7\n"); + if (!rm) /* Dy,Dx */ + { + if (size == BYTE_SIZE) /* EBX,BX,BL=destination */ + e("mov bl, [d+%d*4]\n", rx); + else + e("mov ebx, [d+%d*4]\n", rx); + if (size == BYTE_SIZE) /* EDX,DX,DL=source */ + e("mov dl, [d+edi*4]\n"); + else + e("mov edx, [d+edi*4]\n"); + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("sbb bl, dl\n"); break; + case WORD_SIZE: e("sbb bx, dx\n"); break; + case LONG_SIZE: e("sbb ebx, edx\n"); break; + } + e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ + e("lahf\n"); + e("setc byte [x]\n"); + e("seto al\n"); + e("jnz short .z\n"); /* if non-zero, cleared */ + e("and dl, 0x40\n"); /* otherwise, unchanged */ + e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ + e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ + EmitLabel(".z"); + + switch (size) /* store results */ + { case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break; + case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break; + case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break; + } + } + else /* -(Ay),-(Ax) */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ + e("cmc\n"); + e("sbb dword [a+edi*4], byte 1\n"); + if (rx != 7) + e("dec dword [a+%d*4]\n", rx); + else + e("sub dword [a+7*4], byte 2\n"); + } + else if (size == WORD_SIZE) + { + e("sub dword [a+edi*4], byte 2\n"); + e("sub dword [a+%d*4], byte 2\n", rx); + } + else if (size == LONG_SIZE) + { + e("sub dword [a+edi*4], byte 4\n"); + e("sub dword [a+%d*4], byte 4\n", rx); + } + + e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; + } + SaveReg("run", "edx"); /* save data obtained */ + e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; + } + SaveReg("run", "ebx"); + e("mov ebx, edx\n"); /* EBX,BX,BL=destination */ + RestoreReg("run", "edx"); /* EDX,DX,DL=source */ + + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("sbb bl, dl\n"); break; + case WORD_SIZE: e("sbb bx, dx\n"); break; + case LONG_SIZE: e("sbb ebx, edx\n"); break; + } + e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ + e("lahf\n"); + e("setc byte [x]\n"); + e("seto al\n"); + e("jnz short .z\n"); /* if non-zero, cleared */ + e("and dl, 0x40\n"); /* otherwise, unchanged */ + e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ + e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ + EmitLabel(".z"); + + e("mov edx, ebx\n"); /* store result */ + RestoreReg("run", "ebx"); + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + } + + if (!rm) + { + if (size == LONG_SIZE) + EmitTiming(8); + else + EmitTiming(4); + } + else + { + if (size == LONG_SIZE) + EmitTiming(30); + else + EmitTiming(18); + } + + InstEnd(); + return 1; +} + +int ADDX(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + if (!rm) /* Dy,Dx */ + { + if (size == BYTE_SIZE) /* EBX,BX,BL=destination */ + e("mov bl, [d+%d*4]\n", rx); + else + e("mov ebx, [d+%d*4]\n", rx); + if (size == BYTE_SIZE) /* EDX,DX,DL=source */ + e("mov dl, [d+edi*4]\n"); + else + e("mov edx, [d+edi*4]\n"); + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("adc bl, dl\n"); break; + case WORD_SIZE: e("adc bx, dx\n"); break; + case LONG_SIZE: e("adc ebx, edx\n"); break; + } + e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ + e("lahf\n"); + e("setc byte [x]\n"); + e("seto al\n"); + e("jnz short .z\n"); /* if non-zero, cleared */ + e("and dl, 0x40\n"); /* otherwise, unchanged */ + e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ + e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ + EmitLabel(".z"); + + switch (size) /* store results */ + { case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break; + case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break; + case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break; + } + } + else /* -(Ay),-(Ax) */ + { + if (size == BYTE_SIZE) + { + e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ + e("cmc\n"); + e("sbb dword [a+edi*4], byte 1\n"); + if (rx != 7) + e("dec dword [a+%d*4]\n", rx); + else + e("sub dword [a+7*4], byte 2\n"); + } + else if (size == WORD_SIZE) + { + e("sub dword [a+edi*4], byte 2\n"); + e("sub dword [a+%d*4], byte 2\n", rx); + } + else if (size == LONG_SIZE) + { + e("sub dword [a+edi*4], byte 4\n"); + e("sub dword [a+%d*4], byte 4\n", rx); + } + + e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; + } + SaveReg("run", "edx"); /* save data obtained */ + e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ + switch (size) + { case BYTE_SIZE: ReadByte(); break; + case WORD_SIZE: ReadWord(); break; + case LONG_SIZE: ReadLong(); break; + } + SaveReg("run", "ebx"); + e("mov ebx, edx\n"); /* EBX,BX,BL=destination */ + RestoreReg("run", "edx"); /* EDX,DX,DL=source */ + + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("adc bl, dl\n"); break; + case WORD_SIZE: e("adc bx, dx\n"); break; + case LONG_SIZE: e("adc ebx, edx\n"); break; + } + e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ + e("lahf\n"); + e("setc byte [x]\n"); + e("seto al\n"); + e("jnz short .z\n"); /* if non-zero, cleared */ + e("and dl, 0x40\n"); /* otherwise, unchanged */ + e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ + e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ + EmitLabel(".z"); + + e("mov edx, ebx\n"); /* store result */ + RestoreReg("run", "ebx"); + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + } + + if (!rm) + { + if (size == LONG_SIZE) + EmitTiming(8); + else + EmitTiming(4); + } + else + { + if (size == LONG_SIZE) + EmitTiming(30); + else + EmitTiming(18); + } + + InstEnd(); + return 1; +} + +int SBCD(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * Notes About Undefined Flags: + * ---------------------------- + * + * N flag = Set the same as the MSB of the result + * V flag = If MSB changes from 1 to 0 when result is adjusted for BCD, + * the V flag is set, otherwise it is cleared. + */ + + unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1; + + InstBegin(op, mnem, 8); + + e("and edi, byte 7\n"); + if (!rm) /* Dy,Dx */ + { + e("mov al, [d+%d*4]\n", rx); /* AL=destination */ + e("mov bl, [d+edi*4]\n"); /* BL=source */ + e("shr byte [x], 1\n"); /* X->CF */ + e("sbb al, bl\n"); /* subtract */ + e("mov bh, al\n"); /* save the unadjusted result in BH */ + e("das\n"); /* adjust the packed BCD result */ + e("setc dl\n"); + e("setc byte [x]\n"); + e("jz short .unchanged_z\n"); + e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ + EmitLabel(".unchanged_z"); + e("and ah, 0x7e\n"); + e("or ah, dl\n"); /* set C */ + e("test al, al\n"); /* N flag */ + e("sets dl\n"); + e("ror dl, 1\n"); /* N flag -> MSB of DL */ + e("or ah, dl\n"); /* into N flag */ + e("mov [d+%d*4], al\n", rx); /* store result */ + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 2, the V flag + * is set. + */ + + e("shr bh, byte 6\n"); + e("shr al, byte 7\n"); + e("and bh, 2\n"); + e("or bh, al\n"); + e("cmp bh, 2\n"); + e("sete al\n"); /* 1 -> V */ + } + else /* -(Ay),-(Ax) */ + { + e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ + e("cmc\n"); + e("sbb dword [a+edi*4], byte 1\n"); + if (rx != 7) + e("dec dword [a+%d*4]\n", rx); + else + e("sub dword [a+7*4], byte 2\n"); + + e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ + ReadByte(); + SaveReg("run", "dl"); /* save byte obtained */ + e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ + ReadByte(); + e("mov al, dl\n"); /* AL=destination */ + RestoreReg("run", "dl"); /* DL=source */ + + e("shr byte [x], 1\n"); /* X->CF */ + e("sbb al, dl\n"); /* subtract */ + e("mov dh, al\n"); /* save unadjusted result in DH */ + e("das\n"); /* adjust the packed BCD result */ + + e("setc dl\n"); + e("setc byte [x]\n"); + e("jz short .unchanged_z\n"); + e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ + EmitLabel(".unchanged_z"); + e("and ah, 0x7e\n"); + e("or ah, dl\n"); /* set C */ + e("test al, al\n"); /* N flag */ + e("sets dl\n"); + e("ror dl, 1\n"); /* N flag -> MSB of DL */ + e("or ah, dl\n"); /* into N flag */ + + e("mov dl, al\n"); /* prepare to write result */ + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 2, the V flag + * is set. + */ + + e("shr dh, byte 6\n"); + e("shr al, byte 7\n"); + e("and dh, 2\n"); + e("or dh, al\n"); + e("cmp dh, 2\n"); + e("sete al\n"); /* 1->V */ + + WriteByte(); + } + + if (!rm) + EmitTiming(6); + else + EmitTiming(18); + + InstEnd(); + return 1; +} + +int ABCD(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1; + + /* + * Notes About Undefined Flags: + * ---------------------------- + * + * N flag = Set the same as the MSB of the result + * V flag = If MSB changes from 0 to 1 when result is adjusted for BCD, + * the V flag is set, otherwise it is cleared. + */ + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + if (!rm) /* Dy,Dx */ + { + e("mov al, [d+%d*4]\n", rx); /* AL=destination */ + e("mov bl, [d+edi*4]\n"); /* BL=source */ + e("shr byte [x], 1\n"); /* X->CF */ + e("adc al, bl\n"); /* add with extend */ + e("mov bh, al\n"); /* save unadjusted result in BH */ + e("daa\n"); /* adjust the packed BCD result */ + e("setc dl\n"); + e("setc byte [x]\n"); + e("jz short .unchanged_z\n"); + e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ + EmitLabel(".unchanged_z"); + e("and ah, 0x7e\n"); + e("or ah, dl\n"); /* set C */ + e("test al, al\n"); /* N flag */ + e("sets dl\n"); + e("ror dl, 1\n"); /* N flag -> MSB of DL */ + e("or ah, dl\n"); /* into N flag */ + + e("mov [d+%d*4], al\n", rx); /* store result */ + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 01, the V flag + * is set. + */ + + e("shr bh, byte 6\n"); + e("shr al, byte 7\n"); + e("and bh, 2\n"); + e("or bh, al\n"); + e("cmp bh, 1\n"); + e("sete al\n"); /* move 1 into V flag */ + + } + else /* -(Ay),-(Ax) */ + { + e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ + e("cmc\n"); + e("sbb dword [a+edi*4], byte 1\n"); + if (rx != 7) + e("dec dword [a+%d*4]\n", rx); + else + e("sub dword [a+7*4], byte 2\n"); + + e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ + ReadByte(); + SaveReg("run", "dl"); /* save byte obtained */ + e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ + ReadByte(); + e("mov al, dl\n"); /* AL=destination */ + RestoreReg("run", "dl"); /* DL=source */ + + e("shr byte [x], 1\n"); /* X->CF */ + e("adc al, dl\n"); /* add */ + e("mov dh, al\n"); /* save unadjusted result in DH */ + e("daa\n"); /* adjust the packed BCD result */ + + e("setc dl\n"); + e("setc byte [x]\n"); + e("jz short .unchanged_z\n"); + e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ + EmitLabel(".unchanged_z"); + e("and ah, 0x7e\n"); + e("or ah, dl\n"); /* set C */ + e("test al, al\n"); /* N flag */ + e("sets dl\n"); + e("ror dl, 1\n"); /* N flag -> MSB of DL */ + e("or ah, dl\n"); /* into N flag */ + + e("mov dl, al\n"); /* store result */ + + /* + * V flag calculation: Put unadjusted result's MSB into bit 1, and the + * adjusted result's MSB into bit 0. If the result is 1, the V flag + * is set. + */ + + e("shr dh, byte 6\n"); + e("shr al, byte 7\n"); + e("and dh, 2\n"); + e("or dh, al\n"); + e("cmp dh, 1\n"); + e("sete al\n"); /* 1 -> V */ + + WriteByte(); + } + + if (!rm) + EmitTiming(6); + else + EmitTiming(18); + + InstEnd(); + return 1; +} + +int ORItoCCR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + SaveCCR(); + e("mov dl, [esi]\n"); /* get data */ + e("and dl, 0x1f\n"); /* mask out unwanted bits */ + e("or [sr], dl\n"); + e("add esi, byte 2\n"); + LoadCCR(); + EmitTiming(base_timing); + InstEnd(); + e("db 66, 65, 82, 84, 33, 1\n"); + return 1; +} + +int Scc(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, cond = (op >> 8) & 0xf; + char mnem[4]; + char *mnem_t[] = { "ST", "SF", "SHI", "SLS", + "SCC", "SCS", "SNE", "SEQ", + "SVC", "SVS", "SPL", "SMI", + "SGE", "SLT", "SGT", "SLE" }; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + strcpy(mnem, mnem_t[cond]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + switch (cond) + { + /* EAX:0000000000000000 NZ00 000C 0000 000V */ + case 0: /* T: Always true */ + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + e("mov byte [d+edi*4], 0xff\n"); + EmitTiming(6); /* reg: byte, true */ + } + else + { + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * If the processor type is 68000, and the EA mode is not + * register, we can emulate dummy reads! + */ + + LoadFromEA(ea, BYTE_SIZE, 0); + e("mov dl, 0xff\n"); + WriteByte(); + } + else + { + e("mov dl, 0xff\n"); + StoreToEAUsingEDI(ea, BYTE_SIZE, 0); + } + EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */ + } + InstEnd(); + return 1; + break; + case 1: /* F: Always false */ + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + e("mov byte [d+edi*4], 0\n"); + EmitTiming(4); /* reg: byte, false */ + } + else + { + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * If the processor type is 68000, and the EA mode is not + * register, we can emulate dummy reads! + */ + + LoadFromEA(ea, BYTE_SIZE, 0); + e("xor dl, dl\n"); + WriteByte(); + } + else + { + e("xor dl, dl\n"); + StoreToEAUsingEDI(ea, BYTE_SIZE, 0); + } + EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */ + } + InstEnd(); + return 1; + break; + case 2: /* HI: !C & !Z */ + e("test ah, byte 0x41\n"); + e("jz short do%d\n", op); + break; + case 3: /* LS: C | Z */ + e("test ah, byte 0x41\n"); + e("jnz short do%d\n", op); + break; + case 4: /* CC: !C */ + e("test ah, byte 1\n"); + e("jz short do%d\n", op); + break; + case 5: /* CS: C */ + e("test ah, byte 1\n"); + e("jnz short do%d\n", op); + break; + case 6: /* NE: !Z */ + e("test ah, byte 0x40\n"); + e("jz short do%d\n", op); + break; + case 7: /* EQ: Z */ + e("test ah, byte 0x40\n"); + e("jnz short do%d\n", op); + break; + case 8: /* VC: !V */ + e("test al, byte 1\n"); + e("jz short do%d\n", op); + break; + case 9: /* VS: V */ + e("test al, byte 1\n"); + e("jnz short do%d\n", op); + break; + case 0xa: /* PL: !N */ + e("test ah, byte 0x80\n"); + e("jz short do%d\n", op); + break; + case 0xb: /* MI: N */ + e("test ah, byte 0x80\n"); + e("jnz short do%d\n", op); + break; + case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short do%d\n", op); + break; + case 0xd: /* LT: N & !V | !N & V */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short do%d\n", op); + EmitLabel(".dont_do"); + break; + case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ + /* if Z, then it is GE and we don't take this branch */ + e("test ah, byte 0x40\n"); + e("jnz short .dont_do\n"); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short do%d\n", op); + EmitLabel(".dont_do"); + break; + case 0xf: /* LE: Z | N & !V | !N & V */ + e("test ah, 0x40\n"); + e("jnz short do%d\n", op); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short do%d\n", op); + break; + } + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + e("mov byte [d+edi*4], 0\n"); + EmitTiming(4); /* reg: byte, false */ + } + else + { + e("false%d:\n", op); /* this prevents redefinitions of labels in + the EA code */ + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * If the processor type is 68000, and the EA mode is not + * register, we can emulate dummy reads! + */ + + LoadFromEA(ea, BYTE_SIZE, 0); + e("xor dl, dl\n"); + WriteByte(); + } + else + { + e("xor dl, dl\n"); + StoreToEAUsingEDI(ea, BYTE_SIZE, 0); + } + EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */ + } + InstEnd(); + Align(4); + e("do%d:\n", op); + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + e("mov byte [d+edi*4], 0xff\n"); + EmitTiming(6); /* reg: byte, true */ + } + else + { + e("true%d:\n", op); /* this prevents redefinitions of labels in + the EA code */ + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * If the processor type is 68000, and the EA mode is not + * register, we can emulate dummy reads! + */ + + LoadFromEA(ea, BYTE_SIZE, 0); + e("mov dl, 0xff\n"); + WriteByte(); + } + else + { + e("mov dl, 0xff\n"); + StoreToEAUsingEDI(ea, BYTE_SIZE, 0); + } + EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */ + } + InstEnd(); + return 1; +} + +int EXG(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned rx = (op >> 9) & 7, opmode = (op >> 3) & 0x1f; + + if (opmode != 0x8 && opmode != 0x9 && opmode != 0x11) return 0; + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + switch (opmode) + { + case 8: /* Dx,Dy */ + e("mov ebx, [d+%d*4]\n", rx); + e("mov edx, [d+edi*4]\n"); + e("mov [d+%d*4], edx\n", rx); + e("mov [d+edi*4], ebx\n"); + break; + case 9: /* Ax,Ay */ + e("mov ebx, [a+%d*4]\n", rx); + e("mov edx, [a+edi*4]\n"); + e("mov [a+%d*4], edx\n", rx); + e("mov [a+edi*4], ebx\n"); + break; + case 0x11: /* Dx,Ay */ + e("mov ebx, [d+%d*4]\n", rx); + e("mov edx, [a+edi*4]\n"); + e("mov [d+%d*4], edx\n", rx); + e("mov [a+edi*4], ebx\n"); + break; + } + EmitTiming(base_timing); + InstEnd(); + + return 1; +} + +int MULS(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dn = (op >> 9) & 7; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + e("mov ax, [d+%d*4]\n", dn); + e("imul dx\n"); /* ax*dx->dx:ax */ + e("shl edx, byte 16\n"); /* EDX=32-bit result */ + e("mov dx, ax\n"); + e("mov [d+%d*4], edx\n", dn); + e("test edx, edx\n"); /* C is always cleared */ + e("lahf\n"); + e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + + return 1; +} + +int MULU(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dn = (op >> 9) & 7; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + e("mov ax, [d+%d*4]\n", dn); + e("mul dx\n"); /* ax*dx->dx:ax */ + e("shl edx, byte 16\n"); /* EDX=32-bit result */ + e("mov dx, ax\n"); + e("mov [d+%d*4], edx\n", dn); + e("test edx, edx\n"); /* C is always cleared */ + e("lahf\n"); + e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + + return 1; +} + +int DIVS(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dn = (op >> 9) & 7; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + /* See the note in DIVU() for info on how overflow is detected */ + + InstBegin(op, mnem, NumDecodedEA(ea)); + + if (TimingEA(ea, WORD_SIZE)) + e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */ + LoadFromEA(ea, WORD_SIZE, 0); + + e("test dx, dx\n"); + e("jz .divide_by_zero\n"); + e("movsx ebx, dx\n"); /* divisor->EBX (sign extend to work) */ + SaveReg("run", "eax"); + e("mov eax, [d+%d*4]\n", dn); /* dividend */ + e("cdq\n"); /* EAX->sign extend->EDX:EAX */ + e("idiv ebx\n"); + e("cmp eax, -32768\n"); + e("jl .overflow\n"); + e("cmp eax, 32767\n"); + e("jg .overflow\n"); + e("test ax, ax\n"); /* test for flags */ + e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */ + e("mov [d+(%d*4)+0], ax\n", dn); + e("lahf\n"); /* the mov's did not affect the flags... */ + e("xor al, al\n"); /* C cleared by TEST */ + EmitTiming(base_timing); + InstEnd(); + + EmitLabel(".overflow"); + RestoreReg("run", "eax"); + e("mov eax, 1\n"); /* clear C and set overflow */ + EmitTiming(base_timing); + InstEnd(); + + EmitLabel(".divide_by_zero"); + e("xor eax, eax\n"); /* clear C */ + e("jmp near exception_divide_by_zero\n"); + + return 1; +} + +int DIVU(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * Overflow Detection: + * + * Overflow detection is actually pretty simple. The 68000 DIVU and DIVS + * instructions divide 32-bit operands by 16-bit operands. What I've done + * is zero extend the operands to 64-bit and 32-bit and then divide. If + * the result is larger than 0xFFFF (16 bits), the overflow condition is + * set. + * + * See the end of this function for an alternate method. + */ + + unsigned ea = op & 0x3f, dn = (op >> 9) & 7; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + + if (TimingEA(ea, WORD_SIZE)) + e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */ + + e("test dx, dx\n"); + e("jz .divide_by_zero\n"); + e("mov ebx, edx\n"); + e("and ebx, 0xffff\n"); /* divisor->EBX */ + SaveReg("run", "eax"); + e("mov eax, [d+%d*4]\n", dn); /* dividend */ + e("xor edx, edx\n"); /* EDX:EAX=dividend */ + e("div ebx\n"); + e("cmp eax, 0xffff\n"); + e("jg .overflow\n"); + e("test ax, ax\n"); /* test for flags */ + e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */ + e("mov [d+(%d*4)+0], ax\n", dn); + e("lahf\n"); /* the mov's did not affect the flags... */ + e("xor al, al\n"); /* C cleared by TEST */ + EmitTiming(base_timing); + InstEnd(); + + EmitLabel(".overflow"); + RestoreReg("run", "eax"); + e("mov eax, 1\n"); /* clear C and set overflow */ + EmitTiming(base_timing); + InstEnd(); + + EmitLabel(".divide_by_zero"); + e("xor eax, eax\n"); /* clear C */ + e("jmp near exception_divide_by_zero\n"); + + return 1; + + /* + * + * An alternate method to detecting overflows is shown below. It doesn't + * seem to be as acurrate as the method that is actually used, but perhaps + * I implemented it wrong or misunderstood it. It broke Comix Zone, Hard + * Drivin', and Race Drivin' (well, caused graphical glitches actually.) + * + * Kuwanger in #programmers (DALnet, of course) came up with a cool idea + * on how to detect overflow before actually dividing. He used an example + * of AH:AL/BL ... in the emulator, it is DX:AX/BX. The dividend, DX:AX + * is stored entirely in EDX, and it is shifted right by the bit number of + * the highest 1 bit in BX, the divisor. If the high 16-bits of EDX (which + * would be DX in DX:AX) are not clear, then an overflow would occur. + * + * Here's Kuwanger's classic lecture: + * + * Maybe I should use a smaller example. + * Lets use, ah:al/bl + * ... + * trzy: You could just convert one to a bit value. + * trzy: Ie, hb(bl).. + * And then shift the other one ah:al + * ... + * Though hb(bl) would have to return 0, that time. + * then ah:all>>hb(bl) + * err, ah:al, rather + * Ie, you shift by the most prominent bit. + * And if ah is non-zero.. + * Well, then the number won't fit. + * ... + * Lets try an extreme case. + * 65535/255 + * 65535>>7 + * ... + * That obviously doesn't fit. :) + * + * Hip-hip-HURRAY! + */ +} + +int NOT(unsigned short op, char *mnem, unsigned base_timing) +{ + int size; + unsigned ea = op & 0x3f, t; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + InstBegin(op, mnem, NumDecodedEA(ea)); + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + if (size == BYTE_SIZE) + e("mov dl, [d+edi*4]\n"); + else + e("mov edx, [d+edi*4]\n"); + switch (size) + { case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); /* C cleared */ + e("xor al, al\n"); /* V cleared */ + switch (size) + { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; + } + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: + t = 4; break; + case LONG_SIZE: + t = 6; break; } + } + else /* mem */ + { + LoadFromEA(ea, size, 0); + switch (size) + { case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); /* C cleared */ + e("xor al, al\n"); /* V cleared */ + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: + t = 8; break; + case LONG_SIZE: + t = 12; break; } + t += TimingEA(ea, size); + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int RTE(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* must be in supervisor mode */ + e("jz near exception_privilege_violation\n"); + e("mov ebx, [a+7*4]\n"); + SaveReg("run", "ebx"); + ReadWord(); /* get SR */ + RestoreReg("run", "ebx"); + e("and edx, 0xa71f\n"); + e("mov [sr], edx\n"); + LoadCCR(); + e("add ebx, byte 2\n"); + SaveReg("run", "ebx"); + ReadLong(); /* get PC */ + RestoreReg("run", "ebx"); + e("add ebx, byte 4\n"); + /* + * If 68010, we have to fetch the format word and check it. If invalid, + * we branch to stackframe_error. The PC points at the word AFTER the + * faulty RTE. + */ + if (mpu == 68010) + { + SaveReg("run", "edx"); /* EDX has the PC! */ + SaveReg("run", "ebx"); + ReadWord(); + RestoreReg("run", "ebx"); + e("add ebx, byte 2\n"); + e("test dh, 0xf0\n"); /* format should only be 0x0000 */ + e("jnz near stackframe_error\n"); + RestoreReg("run", "edx"); + } + + e("mov [a+7*4], ebx\n"); + e("mov esi, edx\n"); + UpdateFetchPtr(); + e("test byte [sr+1], 0x20\n"); /* if no longer in supervisor, + SP->SSP, USP->SP */ + e("jnz short .in_s\n"); + e("mov ebx, [a+7*4]\n"); + e("mov edx, [__sp]\n"); + e("mov [a+7*4], edx\n"); + e("mov [__sp], ebx\n"); + SetUserAddressSpace(); /* map in user address space */ + EmitLabel(".in_s"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int ANDItoCCR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + SaveCCR(); + e("mov dl, [esi]\n"); /* get data */ + e("and dl, 0x1f\n"); /* mask out unwanted bits */ + e("and [sr], dl\n"); + e("add esi, byte 2\n"); + LoadCCR(); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int CMPA(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned opmode = (op >> 6) & 7, t; + + if (!CheckEA(op & 0x3f, "111111111111")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + if (opmode != 3 && opmode != 7) return 0; + + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (opmode == 3) LoadFromEA(op & 0x3f, WORD_SIZE, 1); + else LoadFromEA(op & 0x3f, LONG_SIZE, 0); + e("cmp [a+%d*4], edx\n", (op >> 9) & 7); + e("lahf\n"); + e("seto al\n"); + if (opmode == 3) /* word size */ + t = 6 + TimingEA(op & 0x3f, WORD_SIZE); + else /* long */ + t = 6 + TimingEA(op & 0x3f, LONG_SIZE); + EmitTiming(t); + InstEnd(); + return 1; +} + +int PEA(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + + if (!CheckEA(op & 0x3f, "001001111011")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadControlEA(op & 0x3f); + e("sub dword [a+7*4], byte 4\n"); /* SP-4->SP */ + e("mov edx, ebx\n"); + e("mov ebx, [a+7*4]\n"); + WriteLong(); /* ->(SP) */ + switch ((op >> 3) & 7) /* timing based on control addressing mode */ + { case 2: t = 12; break; /* (An) */ + case 5: t = 16; break; /* (d16,An) */ + case 6: t = 20; break; /* (d8,An,Xn) */ + case 7: + switch (op & 7) + { case 0: t = 16; break; /* (xxx).W */ + case 1: t = 20; break; /* (xxx).L */ + case 2: t = 16; break; /* (d16,pc) */ + case 3: t = 20; break; /* (d8,pc,Xn) */ + } break; + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int SWAP(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + e("mov edx, [d+edi*4]\n"); + e("rol edx, byte 16\n"); /* swap words */ + e("test edx, edx\n"); /* clears CF, will set N and Z */ + e("lahf\n"); + e("xor al, al\n"); /* clear V */ + e("mov [d+edi*4], edx\n"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int BSET(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + + if (((op >> 6) & 7) != 7 && ((op >> 6) & 7) != 3) return 0; + if (op & 0x100) /* dynamic */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 8; + e("and edi, byte 7\n"); + e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 8; + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("bts edx, edi\n"); /* bit->CF, set */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* clears EDI for us */ + } + } + else if (!(op & 0x100)) /* static */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 12; + e("and edi, byte 7\n"); /* isolate Dn reg # */ + e("mov ebx, [esi]\n"); /* get bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("add esi, byte 2\n"); + e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 12; + SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ + e("add esi, byte 2\n"); + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + RestoreReg("run", "edi"); + e("mov edi, [edi]\n"); + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("bts edx, edi\n"); /* bit->CF, set */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* write back, EDI is cleared */ + } + } + if (((op & 0x3f) & 7) == 0) /* Dn is long */ + EmitTiming(t); + else /* mem is byte */ + EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); + InstEnd(); + return 1; +} + +int BCLR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + + if (((op >> 6) & 7) != 6 && ((op >> 6) & 7) != 2) return 0; + if (op & 0x100) /* dynamic */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 10; + e("and edi, byte 7\n"); + e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 8; + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("btr edx, edi\n"); /* bit->CF, clear */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* clears EDI for us */ + } + } + else if (!(op & 0x100)) /* static */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 14; + e("and edi, byte 7\n"); /* isolate Dn reg # */ + e("mov ebx, [esi]\n"); /* get bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("add esi, byte 2\n"); + e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 12; + SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ + e("add esi, byte 2\n"); + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + RestoreReg("run", "edi"); + e("mov edi, [edi]\n"); + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("btr edx, edi\n"); /* bit->CF, clear */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* write back, EDI is cleared */ + } + } + if (((op & 0x3f) & 7) == 0) /* Dn is long */ + EmitTiming(t); + else /* mem is byte */ + EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); + InstEnd(); + return 1; +} + +int BCHG(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + + if (((op >> 6) & 7) != 5 && ((op >> 6) & 7) != 1) return 0; + if (op & 0x100) /* dynamic */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 8; + e("and edi, byte 7\n"); + e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 8; + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("btc edx, edi\n"); /* bit->CF, change */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* clears EDI for us */ + } + } + else if (!(op & 0x100)) /* static */ + { + if (!CheckEA(op & 0x3f, "101111111000")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 12; + e("and edi, byte 7\n"); /* isolate Dn reg # */ + e("mov ebx, [esi]\n"); /* get bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("add esi, byte 2\n"); + e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + } + else /* mem is byte */ + { + t = 12; + SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ + e("add esi, byte 2\n"); + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + RestoreReg("run", "edi"); + e("mov edi, [edi]\n"); + e("and edi, byte 7\n"); /* same as modulo 8 */ + e("btc edx, edi\n"); /* bit->CF, change */ + e("setnc dh\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl dh, 6\n"); /* put C in Z position */ + e("or ah, dh\n"); /* set the new Z */ + WriteByte(); /* write back, EDI is cleared */ + } + } + if (((op & 0x3f) & 7) == 0) /* Dn is long */ + EmitTiming(t); + else /* mem is byte */ + EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); + InstEnd(); + return 1; +} + +int ANDItoSR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + SaveCCR(); /* save flags to SR */ + e("mov edx, [esi]\n"); + e("and edx, 0xa71f\n"); /* mask out unwanted bits */ + e("and [sr], dx\n"); + e("add esi, byte 2\n"); + LoadCCR(); /* get flags back */ + e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */ + e("jnz short .in_s\n"); + e("mov ebx, [a+7*4]\n"); + e("mov edx, [__sp]\n"); + e("mov [a+7*4], edx\n"); + e("mov [__sp], ebx\n"); + SetUserAddressSpace(); /* map in user address space */ + EmitLabel(".in_s"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int UNLK(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + e("mov ebx, [a+edi*4]\n"); /* An->SP */ + SaveReg("run", "ebx"); /* to avoid addrclip() */ + SaveReg("run", "edi"); + ReadLong(); /* (SP)->An */ + RestoreReg("run", "edi"); + RestoreReg("run", "ebx"); + e("mov [a+edi*4], edx\n"); + e("add ebx, byte 4\n"); + e("mov [a+7*4], ebx\n"); /* SP+4->SP */ + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int LINK(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + e("mov ebx, [a+7*4]\n"); /* EBX=SP */ + e("sub ebx, byte 4\n"); + e("mov edx, [a+edi*4]\n"); + SaveReg("run", "ebx"); + SaveReg("run", "edi"); + WriteLong(); /* An->(SP) */ + RestoreReg("run", "edi"); + RestoreReg("run", "ebx"); + e("mov [a+edi*4], ebx\n"); /* SP->An */ + e("movsx edx, word [esi]\n"); + e("add ebx, edx\n"); /* dn + SP->SP */ + e("mov [a+7*4], ebx\n"); + e("add esi, byte 2\n"); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int ROxMem(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dr = (op >> 8) & 1; + char mnem[4]; + char *mnem_t[] = { "ROR", "ROL" }; + + if (!CheckEA(ea, "001111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + strcpy(mnem, mnem_t[(op >> 8) & 1]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + if (dr) /* left */ + e("rol dx, byte 1\n"); + else + e("ror dx, byte 1\n"); + e("setc al\n"); /* AL=CF */ + e("test dx, dx\n"); /* CF is cleared */ + e("lahf\n"); + e("or ah, al\n"); /* put in CF */ + e("xor al, al\n"); /* V is always cleared */ + WriteWord(); /* write back to mem */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int ROxReg(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned size, t, opr_i, count; + char *opr[] = { "ror", "rol" }; + char mnem[4]; + char *mnem_t[] = { "ROR", "ROL" }; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + opr_i = (op >> 8) & 1; + strcpy(mnem, mnem_t[opr_i]); + + count = (op >> 9) & 7; + if (!count) count = 8; + + InstBegin(op, mnem, 8); /* 8 regs */ + e("and edi, byte 7\n"); + if ((op >> 5) & 1) /* reg contains count */ + { + e("mov ebx, ecx\n"); /* save ECX */ + e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); + e("and ecx, byte 0x3f\n"); /* modulo 64 */ + e("mov edx, [d+edi*4]\n"); + switch (size) + { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); + e("test cl, cl\n"); /* if shift count of 0, X is unaffected */ + e("jnz short .not_zero\n"); + e("xor al, al\n"); /* clear CF, count was 0 */ + EmitLabel(".not_zero"); + e("or ah, al\n"); /* put CF in with rest of flags */ + e("xor al, al\n"); /* V always cleared */ + switch (size) /* write back results */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; + } + /* timing */ + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + e("shl cl, byte 1\n"); /* 2n */ + e("add cl, byte %d\n", t); + e("neg ecx\n"); + e("add ecx, ebx\n"); + e("js near Turbo68KRun_done\n"); + } + else /* immediate count */ + { + e("mov edx, [d+edi*4]\n"); + switch (size) + { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); + e("or ah, al\n"); /* set CF */ + e("xor al, al\n"); /* V always cleared */ + switch (size) /* write back results */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; + } + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + EmitTiming(t + count * 2); + } + InstEnd(); + return 1; +} + +int EXT(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned opmode = (op >> 6) & 7; + + if (opmode != 2 && opmode != 3) return 0; + + InstBegin(op, mnem, 8); + e("and edi, byte 7\n"); + e("mov edx, [d+edi*4]\n"); + if (opmode == 2) /* byte -> word */ + { + e("movsx dx, dl\n"); + e("mov [d+edi*4], dx\n"); + e("test dx, dx\n"); + } + else if (opmode == 3) /* word -> long */ + { + e("movsx edx, dx\n"); + e("mov [d+edi*4], edx\n"); + e("test edx, edx\n"); + } + else if (opmode == 7) /* byte -> long */ + { + /* + * Note: This code is never executed. EXTB is present on 68020 and + * higher processors. Turbo68K was only intended to emulate the + * 68000. This code was an accident, but it will be kept here in case + * in the future it is needed. + */ + + e("movsx edx, dl\n"); + e("mov [d+edi*4], edx\n"); + e("test edx, edx\n"); + } + e("lahf\n"); /* CF is already cleared by TEST */ + e("xor al, al\n"); /* V always cleared */ + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int ROXxMem(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dr = (op >> 8) & 1; + char mnem[5]; + char *mnem_t[] = { "ROXR", "ROXL" }; + + if (!CheckEA(ea, "001111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + strcpy(mnem, mnem_t[(op >> 8) & 1]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + e("shr byte [x], 1\n"); + if (dr) /* left */ + e("rcl dx, byte 1\n"); + else + e("rcr dx, byte 1\n"); + e("setc al\n"); /* AL=CF */ + e("setc byte [x]\n"); + e("test dx, dx\n"); /* CF is cleared */ + e("lahf\n"); + e("or ah, al\n"); /* put in CF */ + e("xor al, al\n"); + WriteWord(); /* write back to mem */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int ROXxReg(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned size, t, opr_i, count; + char *opr[] = { "rcr", "rcl" }; + char mnem[5]; + char *mnem_t[] = { "ROXR", "ROXL" }; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + opr_i = (op >> 8) & 1; + strcpy(mnem, mnem_t[opr_i]); + + count = (op >> 9) & 7; + if (!count) count = 8; + + InstBegin(op, mnem, 8); /* 8 regs */ + e("and edi, byte 7\n"); + if ((op >> 5) & 1) /* reg contains count */ + { + e("mov ebx, ecx\n"); /* save ECX */ + e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); + e("and ecx, byte 0x3f\n"); /* modulo 64 */ + e("mov dl, [x]\n"); + e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */ + e("mov edx, [d+edi*4]\n"); + switch (size) + { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); + e("test cl, cl\n"); /* if shift count of 0, X is unaffected */ + e("jz short .zero_count\n"); + e("or ah, al\n"); /* set CF */ + e("test ah, 1\n"); /* CF */ + e("setnz byte [x]\n"); + e("jmp short .skip\n"); + EmitLabel(".zero_count"); + e("mov al, [x]\n"); /* X->C */ + e("and al, byte 1\n"); + e("and ah, 0xfe\n"); /* get rid of old C before putting new C in */ + e("or ah, al\n"); + EmitLabel(".skip"); + e("xor al, al\n"); /* V always cleared */ + switch (size) /* write back results */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; + } + + /* timing */ + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + e("shl cl, byte 1\n"); /* 2n */ + e("add cl, byte %d\n", t); + e("neg ecx\n"); + e("add ecx, ebx\n"); + e("js near Turbo68KRun_done\n"); + } + else /* immediate count */ + { + e("mov dl, [x]\n"); + e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */ + e("mov edx, [d+edi*4]\n"); + switch (size) + { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break; + case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break; + case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break; + } + e("lahf\n"); + e("or ah, al\n"); /* set CF */ + e("test ah, 1\n"); /* CF */ + e("setnz byte [x]\n"); + e("xor al, al\n"); /* V always cleared */ + switch (size) /* write back results */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; + } + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + EmitTiming(t + count * 2); + } + InstEnd(); + return 1; +} + +int MOVEtoCCR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + e("mov [sr], dl\n"); + LoadCCR(); /* update flags */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); /* technically, word + sized */ + InstEnd(); + return 1; +} + +int LSxMem(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dr = (op >> 8) & 1; + char mnem[4]; + char *mnem_t[] = { "LSR", "LSL" }; + + if (!CheckEA(ea, "001111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + strcpy(mnem, mnem_t[(op >> 8) & 1]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + if (dr) /* left */ + e("shl dx, byte 1\n"); + else + e("shr dx, byte 1\n"); + e("lahf\n"); + e("setc byte [x]\n"); + e("xor al, al\n"); + WriteWord(); /* write back to mem */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int LSxReg(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned size, t, opr_i, count; + char *opr[] = { "shr", "shl" }; + char mnem[4]; + char *mnem_t[] = { "LSR", "LSL" }; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + opr_i = (op >> 8) & 1; + strcpy(mnem, mnem_t[opr_i]); + + count = (op >> 9) & 7; + if (!count) count = 8; + + InstBegin(op, mnem, 8); /* 8 regs */ + e("and edi, byte 7\n"); + if ((op >> 5) & 1) /* reg contains count */ + { + e("mov ebx, ecx\n"); /* save ECX */ + e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); + e("and ecx, byte 0x3f\n"); /* modulo 64 */ + e("mov edx, [d+edi*4]\n"); /* get register... */ + switch (size) + { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); + e("setc al\n"); /* CF -> AL */ + e("test dl, dl\n"); + e("mov [d+edi*4], dl\n"); /* MOV does not + affect flags */ + break; + case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); + e("setc al\n"); + e("test dx, dx\n"); + e("mov [d+edi*4], dx\n"); + break; + case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); + e("setc al\n"); + e("test edx, edx\n"); + e("mov [d+edi*4], edx\n"); + break; + } + e("lahf\n"); + e("test cl, cl\n"); /* if zero count... */ + e("jz short .zero\n"); + e("or ah, al\n"); /* new CF... the first test cleared old C */ + e("test al, 1\n"); /* C->X */ + e("setnz byte [x]\n"); + EmitLabel(".zero"); + e("xor al, al\n"); /* V cleared */ + /* timing */ + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + e("shl cl, byte 1\n"); /* 2n */ + e("add cl, byte %d\n", t); + e("neg ecx\n"); + e("add ecx, ebx\n"); + e("js near Turbo68KRun_done\n"); + } + else /* immediate count */ + { + e("%s ", opr[opr_i]); + switch (size) + { case BYTE_SIZE: e("byte "); break; + case WORD_SIZE: e("word "); break; + case LONG_SIZE: e("dword "); break; } + e("[d+edi*4], %d\n", count); + e("lahf\n"); + e("setc byte [x]\n"); + e("xor al, al\n"); + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + EmitTiming(t + count * 2); + } + InstEnd(); + return 1; +} + +int NOP(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int CLR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f, size, t; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + InstBegin(op, mnem, NumDecodedEA(ea)); + e("mov eax, 0x4000\n"); /* Z always set, everything else cleared */ + + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * Emulate the dummy read for memory addressing modes, if necessary. + * We only do it for memory modes, because it really can't make any + * difference if the destination is a register! + */ + + LoadFromEA(ea, size, 0); /* do the dummy read */ + e("xor edx, edx\n"); /* now, write a 0 */ + switch (size) /* write */ + { + case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + } + else + { + /* + * This method is quicker, since dummy reads aren't needed + */ + + e("xor edx, edx\n"); + StoreToEAUsingEDI(ea, size, 0); + } + + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: + if ((ea >> 3) == 0) t = 4; /* reg */ + else t = 8 + TimingEA(ea, size); /* mem */ + break; + case LONG_SIZE: + if ((ea >> 3) == 0) t = 6; /* reg */ + else t = 12 + TimingEA(ea, size); /* mem */ + break; + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int ORItoSR(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + SaveCCR(); /* save flags to SR */ + e("mov edx, [esi]\n"); + e("and edx, 0xa71f\n"); /* mask out unwanted bits */ + e("or [sr], dx\n"); + e("add esi, byte 2\n"); + LoadCCR(); /* get flags back */ + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int MOVEfromSR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f; + + if (!CheckEA(ea, "101111111000")) return 0; + + InstBegin(op, mnem, 1); + /* + * This instruction is not privileged for 68000 and 68008 processors. + */ + if (mpu == 68010) + { + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + } + SaveCCR(); /* save flags to SR */ + + if (mpu == 68000 && dummyread && (ea >> 3)) + { + /* + * When dummy reads are enabled, and the EA mode is not register, we + * perform a dummy read + */ + + LoadFromEA(ea, WORD_SIZE, 0); + e("mov edx, [sr]\n"); + WriteWord(); + } + else + { + e("mov edx, [sr]\n"); /* get SR */ + StoreToEAUsingEDI(ea, WORD_SIZE, 0); + } + + LoadCCR(); /* get back SR */ + if ((ea >> 3) == 0) + EmitTiming(6); + else + EmitTiming(8 + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int ArithmeticA(unsigned short op, char *mnem, unsigned base_timing) +{ + int size = UNKNOWN_SIZE, t; + + if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */ + return 0; + if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ + return 0; + switch ((op >> 6) & 7) /* set allowed sizes */ + { case 3: size = WORD_SIZE; break; + case 7: size = LONG_SIZE; break; + default: return 0; } + if (((op >> 12) & 0xf) != 0xd && ((op >> 12) & 0xf) != 9) return 0; + + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadFromEA(op & 0x3f, size, 1); /* load */ + if (((op >> 12) & 0xf) == 0xd) /* ADDA */ + e("add [a+%d*4], edx\n", (op >> 9) & 7); /* store */ + else /* SUBA */ + e("sub [a+%d*4], edx\n", (op >> 9) & 7); + switch (size) + { case WORD_SIZE: t = 8; break; + case LONG_SIZE: t = 6; + if (((op & 0x3f) >> 3) == 0) t += 2; /* Dn */ + else if (((op & 0x3f) >> 3) == 1) t += 2; /* An */ + else if (((op & 0x3f) >> 3) == 7 && (op & 7) == 4) + t += 2; /* # */ + break; + } + EmitTiming(TimingEA(op & 0x3f, size) + t); + InstEnd(); + return 1; +} + +int ASxMem(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, dr = (op >> 8) & 1; + char mnem[4]; + char *mnem_t[] = { "ASR", "ASL" }; + + if (!CheckEA(ea, "001111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + + strcpy(mnem, mnem_t[(op >> 8) & 1]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + if (dr) /* left */ + e("shl dx, byte 1\n"); + else + e("sar dx, byte 1\n"); + e("lahf\n"); + e("setc byte [x]\n"); + if (dr) /* only for left shifts, right shifts don't change the MSB */ + { + e("sets al\n"); /* AL=current MSB */ + e("adc al, byte 0\n"); /* CF contains old MSB */ + e("and al, 1\n"); /* preserve only the flag bit */ + } + WriteWord(); /* write back to mem */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int ASxReg(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned i, size, t = 0, opr_i, count; + char mnem[4]; + char *mnem_t[] = { "ASR", "ASL" }; + + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + opr_i = (op >> 8) & 1; + strcpy(mnem, mnem_t[opr_i]); + + count = (op >> 9) & 7; + if (!count) count = 8; + + InstBegin(op, mnem, 8); /* 8 regs */ + + e("and edi, byte 7\n"); + if ((op >> 5) & 1) /* reg contains count */ + { + if (opr_i) /* ASL */ + { + e("xor al, al\n"); + SaveReg("run", "edi"); /* save EDI */ + e("mov edx, [d+edi*4]\n"); /* get reg to shift */ + e("mov edi, [d+%d*4]\n", (op >> 9) & 7); + e("and edi, byte 0x3f\n"); /* modulo 64 */ + e("jz short .zero_shift\n"); + EmitLabel(".loop"); + e("shl "); + switch (size) + { case BYTE_SIZE: e("dl, "); break; + case WORD_SIZE: e("dx, "); break; + case LONG_SIZE: e("edx, "); break; } + e("1\n"); + e("setc bl\n"); /* old MSB */ + e("sets bh\n"); /* new MSB */ + e("xor bh, bl\n"); /* detect change */ + e("or al, bh\n"); /* reflect change in V */ + e("sub ecx, byte 2\n"); /* each rotation=2 cycles */ + e("dec edi\n"); + e("jnz short .loop\n"); + RestoreReg("run", "edi"); + switch (size) /* store reg, get flags */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); + e("test dl, dl\n"); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n"); + e("test dx, dx\n"); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n"); + e("test edx, edx\n"); break; + } + e("lahf\n"); + e("or ah, bl\n"); /* CF */ + e("test bl, bl\n"); + e("setnz byte [x]\n"); /* CF==X */ + e("jmp short .end\n"); + + EmitLabel(".zero_shift"); + switch (size) + { case BYTE_SIZE: e("test dl, dl\n"); break; + case WORD_SIZE: e("test dx, dx\n"); break; + case LONG_SIZE: e("test edx, edx\n"); break; + } + e("lahf\n"); + e("xor al, al\n"); + + EmitLabel(".end"); + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + EmitTiming(t); + } + else /* ASR */ + { + e("mov ebx, ecx\n"); /* save ECX */ + e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); + e("and ecx, byte 0x3f\n"); /* modulo 64 */ + switch (size) + { case BYTE_SIZE: e("mov dl, [d+edi*4]\n"); + e("sar dl, cl\n"); + e("setc bl\n"); + e("test dl, dl\n"); + break; + case WORD_SIZE: e("mov edx, [d+edi*4]\n"); + e("sar dx, cl\n"); + e("setc bl\n"); + e("test dx, dx\n"); + break; + case LONG_SIZE: e("mov edx, [d+edi*4]\n"); + e("sar edx, cl\n"); + e("setc bl\n"); /* get CF from SHL/SHR */ + e("test edx, edx\n");/* this trashes the CF */ + break; + } + + e("lahf\n"); + e("test cl, cl\n"); /* if shift count of 0, X is unaffected, C=0 */ + e("jz short .no_x\n"); + e("or ah, bl\n"); /* CF! */ + e("test ah, 1\n"); /* CF */ + e("setnz byte [x]\n"); + EmitLabel(".no_x"); + switch (size) /* write back and detect MSB change */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); + break; + case WORD_SIZE: e("mov [d+edi*4], dx\n"); + break; + case LONG_SIZE: e("mov [d+edi*4], edx\n"); + break; + } + e("xor al, al\n"); /* V=0 because MSB never changes w/ ASR */ + /* timing */ + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t = 6; break; + case LONG_SIZE: t = 8; break; } + e("shl cl, byte 1\n"); /* 2n */ + e("add cl, byte %d\n", t); + e("neg ecx\n"); + e("add ecx, ebx\n"); + e("js near Turbo68KRun_done\n"); + } + } + else /* immediate count */ + { + if (!opr_i) /* ASR */ + { + e("sar "); + switch (size) + { case BYTE_SIZE: e("byte "); break; + case WORD_SIZE: e("word "); break; + case LONG_SIZE: e("dword "); break; } + e("[d+edi*4], %d\n", count); + e("lahf\n"); + e("setc byte [x]\n"); + e("xor al, al\n"); + t = count * 2; /* 2 cycles per shift */ + } + else /* ASL */ + { + e("xor al, al\n"); /* clear V */ + switch (size) + { case BYTE_SIZE: e("mov dl, [d+edi*4]\n"); break; + case WORD_SIZE: e("mov dx, [d+edi*4]\n"); break; + case LONG_SIZE: e("mov edx, [d+edi*4]\n"); break; } + for (i = 1; i <= count; i++) + { + e("shl "); + switch (size) + { case BYTE_SIZE: e("dl, "); break; + case WORD_SIZE: e("dx, "); break; + case LONG_SIZE: e("edx, "); break; } + e("1\n"); + if (i == count) + { + e("lahf\n"); + e("setc byte [x]\n"); + } + e("setc bl\n"); /* old MSB */ + e("sets bh\n"); /* new MSB */ + e("xor bh, bl\n"); /* detect change */ + e("or al, bh\n"); /* reflect change in V */ + e("sub ecx, byte 2\n"); /* each rotation=2 cycles */ + } + switch (size) /* write results back to Dn */ + { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; + case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; + case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; } + } + switch (size) + { case BYTE_SIZE: + case WORD_SIZE: t += 6; break; + case LONG_SIZE: t += 8; break; } + EmitTiming(t); + } + InstEnd(); + return 1; +} + +int ArithmeticQ(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, size, t, data, opr_i; + char mnem[5]; + char *mnem_t[] = { "ADDQ", "SUBQ" }; + char *opr[] = { "add", "sub" }; + + if (!CheckEA(ea, "111111111000")) return 0; + t = base_timing; + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; t += 4; break; + default: return 0; } + if ((ea >> 3) != 0 && (ea >> 3) != 1) t += 4; /* mem */ + if (size == BYTE_SIZE && (ea >> 3) == 1) /* byte An not allowed */ + return 0; + if (!NumDecodedEA(ea)) return 0; + data = (op >> 9) & 7; + if (!data) data = 8; + + strcpy(mnem, mnem_t[(op >> 8) & 1]); + opr_i = (op >> 8) & 1; + + InstBegin(op, mnem, NumDecodedEA(ea)); + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + switch (size) + { case BYTE_SIZE: + e("%s byte [d+edi*4], %d\n", opr[opr_i], data); + break; + case WORD_SIZE: + e("%s word [d+edi*4], %d\n", opr[opr_i], data); + break; + case LONG_SIZE: + e("%s dword [d+edi*4], byte %d\n", opr[opr_i], data); + break; + } + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + } + else if ((ea >> 3) == 1) /* An */ + { + /* + * Manual says the entire address register is used despite the size + * of the operation. + */ + e("and edi, byte 7\n"); + e("%s dword [a+edi*4], byte %d\n", opr[opr_i], data); + } + else /* memory */ + { + LoadFromEA(ea, size, 0); + switch (size) + { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], data); break; + case WORD_SIZE: e("%s dx, %d\n", opr[opr_i], data); break; + case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], data); break; } + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; } + } + EmitTiming(t + TimingEA(ea, size)); + InstEnd(); + return 1; +} + +int NEGx(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned ea = op & 0x3f, size, t; + char mnem[5]; + char *mnem_t[] = { "NEGX", "NEG" }; + + if (!CheckEA(ea, "101111111000")) return 0; + if (!NumDecodedEA(ea)) return 0; + switch ((op >> 6) & 3) + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + strcpy(mnem, mnem_t[(op >> 10) & 1]); + + InstBegin(op, mnem, NumDecodedEA(ea)); + if ((ea >> 3) == 0) /* Dn */ + { + e("and edi, byte 7\n"); + if (!(op & 0x0400)) /* NEGX */ + { + switch (size) + { case BYTE_SIZE: e("xor dl, dl\n"); break; + case WORD_SIZE: case LONG_SIZE: + e("xor edx, edx\n"); break; + } + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("sbb dl, [d+edi*4]\n"); + e("mov bl, ah\n"); /* store old flags */ + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); /* Z clear */ + e("and bl, byte 0x40\n"); /* isolate old Z */ + e("and ah, byte 0xbf\n"); /* kill new Z */ + e("or ah, bl\n"); /* in w/ old Z */ + EmitLabel(".clr"); + e("mov [d+edi*4], dl\n"); t = 4; break; + case WORD_SIZE: e("sbb dx, [d+edi*4]\n"); + e("mov bl, ah\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and bl, byte 0x40\n"); + e("and ah, byte 0xbf\n"); + e("or ah, bl\n"); + EmitLabel(".clr"); + e("mov [d+edi*4], dx\n"); t = 4; break; + case LONG_SIZE: e("sbb edx, [d+edi*4]\n"); + e("mov bl, ah\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and bl, byte 0x40\n"); + e("and ah, byte 0xbf\n"); + e("or ah, bl\n"); + EmitLabel(".clr"); + e("mov [d+edi*4], edx\n"); t = 6; break; } + } + else /* NEG */ + { + switch (size) + { case BYTE_SIZE: e("neg byte [d+edi*4]\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + t = 4; break; + case WORD_SIZE: e("neg word [d+edi*4]\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + t = 4; break; + case LONG_SIZE: e("neg dword [d+edi*4]\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + t = 6; break; + } + } + } + else /* memory */ + { + if (!(op & 0x0400)) /* NEGX */ + { + LoadFromEA(ea, size, 0); + e("mov edi, ebx\n"); /* save address where data came from */ + switch (size) + { case BYTE_SIZE: e("xor bl, bl\n"); break; + case WORD_SIZE: case LONG_SIZE: + e("xor ebx, ebx\n"); break; + } + e("shr byte [x], 1\n"); /* X->CF */ + switch (size) + { case BYTE_SIZE: e("sbb bl, dl\n"); + e("mov dl, ah\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and dl, byte 0x40\n"); + e("and ah, byte 0xbf\n"); + e("or ah, dl\n"); + EmitLabel(".clr"); + break; + case WORD_SIZE: e("sbb bx, dx\n"); + e("mov dl, ah\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and dl, byte 0x40\n"); + e("and ah, byte 0xbf\n"); + e("or ah, dl\n"); + EmitLabel(".clr"); + break; + case LONG_SIZE: e("sbb ebx, edx\n"); + e("mov dl, ah\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + e("jnz short .clr\n"); + e("and dl, byte 0x40\n"); + e("and ah, byte 0xbf\n"); + e("or ah, dl\n"); + EmitLabel(".clr"); + break; } + e("mov edx, ebx\n"); + e("mov ebx, edi\n"); + /* WriteXXX will clear EDI for us */ + switch (size) + { case BYTE_SIZE: WriteByte(); t = 8; break; + case WORD_SIZE: WriteWord(); t = 8; break; + case LONG_SIZE: WriteLong(); t = 12; break; } + t += TimingEA(ea, size); + } + else /* NEG */ + { + LoadFromEA(ea, size, 0); + switch (size) + { case BYTE_SIZE: e("neg dl\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + WriteByte(); t = 8; break; + case WORD_SIZE: e("neg dx\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + WriteWord(); t = 8; break; + case LONG_SIZE: e("neg edx\n"); + e("lahf\n"); + e("seto al\n"); + e("setc byte [x]\n"); + WriteLong(); t = 12; break; } + t += TimingEA(ea, size); + } + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int RTS(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 1); + e("mov ebx, [a+7*4]\n"); + ReadLong(); + e("add dword [a+7*4], byte 4\n"); + e("mov esi, edx\n"); + UpdateFetchPtr(); + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int MOVEtoSR(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned ea = op & 0x3f; + + if (!CheckEA(ea, "101111111111")) return 0; + if (!NumDecodedEA(ea)) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + LoadFromEA(ea, WORD_SIZE, 0); + e("test byte [sr+1], 0x20\n"); /* in supervisor? */ + e("jz near exception_privilege_violation\n"); + e("test dh, 0x20\n"); /* if still in supervisor... */ + e("jnz .still_s\n"); /* ...no changes need to be made */ + e("mov ebx, [__sp]\n"); /* swap SSP and USP */ + e("xchg [a+7*4], ebx\n"); + e("mov [__sp], ebx\n"); + SetUserAddressSpace(); /* map in user address space */ + EmitLabel(".still_s"); + e("and edx, 0xa71f\n"); /* mask out unwanted bits */ + e("mov [sr], edx\n"); /* load new SR */ + LoadCCR(); /* reload flags we changed in CCR */ + EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); + InstEnd(); + return 1; +} + +int DBcc(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned cond = (op >> 8) & 0xf; + char mnem[5]; + char *mnem_t[] = { "DBT", "DBF", "DBHI", "DBLS", + "DBCC", "DBCS", "DBNE", "DBEQ", + "DBVC", "DBVS", "DBPL", "DBMI", + "DBGE", "DBLT", "DBGT", "DBLE" }; + + strcpy(mnem, mnem_t[cond]); /* set mnemonic */ + + InstBegin(op, mnem, 8); + switch (cond) + { + /* EAX:0000000000000000 NZ00 000C 0000 000V */ + case 0: /* always true */ + e("add esi, byte 2\n"); + EmitTiming(10); + InstEnd(); + return 1; + case 1: /* DBF/DBRA: no condition to test, just loop */ + break; + case 2: /* HI: !C & !Z */ + e("test ah, byte 0x41\n"); + e("jz short .do\n"); + break; + case 3: /* LS: C | Z */ + e("test ah, byte 0x41\n"); + e("jnz short .do\n"); + break; + case 4: /* CC: !C */ + e("test ah, byte 1\n"); + e("jz short .do\n"); + break; + case 5: /* CS: C */ + e("test ah, byte 1\n"); + e("jnz short .do\n"); + break; + case 6: /* NE: !Z */ + e("test ah, byte 0x40\n"); + e("jz short .do\n"); + break; + case 7: /* EQ: Z */ + e("test ah, byte 0x40\n"); + e("jnz short .do\n"); + break; + case 8: /* VC: !V */ + e("test al, byte 1\n"); + e("jz short .do\n"); + break; + case 9: /* VS: V */ + e("test al, byte 1\n"); + e("jnz short .do\n"); + break; + case 0xa: /* PL: !N */ + e("test ah, byte 0x80\n"); + e("jz short .do\n"); + break; + case 0xb: /* MI: N */ + e("test ah, byte 0x80\n"); + e("jnz short .do\n"); + break; + case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short .do\n"); + break; + case 0xd: /* LT: N & !V | !N & V */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short .do\n"); + EmitLabel(".dont_do"); + break; + case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ + /* if Z, then it is GE and we don't take this branch */ + e("test ah, byte 0x40\n"); + e("jnz short .dont_do\n"); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short .do\n"); + EmitLabel(".dont_do"); + break; + case 0xf: /* LE: Z | N & !V | !N & V */ + e("test ah, 0x40\n"); + e("jnz short .do\n"); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short .do\n"); + break; + } + e("and edi, byte 7\n");/* condition not met, decrement and branch */ + if (cond == 1 && skip) /* idle loop skipping w/ DBRA */ + e("mov edx, esi\n"); + e("sub word [d+edi*4], 1\n"); + e("jc short .do_loop_expired\n"); /* loop expired... */ + e("movsx ebx, word [esi]\n"); + e("add esi, ebx\n"); + if (brafetch) + { + if (skip) /* skip uses EDX, we must save it */ + SaveReg("run", "edx"); + e("sub esi, ebp\n"); /* ESI=denormalized PC */ + UpdateFetchPtr(); + if (skip) + RestoreReg("run", "edx"); + } + if (cond == 1 && skip) + { + e("sub edx, byte 2\n"); /* to get address of instruction */ + e("cmp edx, esi\n"); + e("je short .skip\n"); /* the same: this is an idle loop */ + } + EmitTiming(10); + InstEnd(); + Align(4); + EmitLabel(".do"); /* condition was met, go on to next instruction */ + e("add esi, byte 2\n"); + EmitTiming(12); + InstEnd(); + Align(4); + EmitLabel(".do_loop_expired"); /* condition false, but loop expired */ + e("add esi, byte 2\n"); + EmitTiming(14); + InstEnd(); + if (cond == 1 && skip) + { + /* + * This code makes it look as if the loop was executed and expired. + */ + EmitLabel(".skip"); + e("add edx, byte 4\n"); + e("mov esi, edx\n"); /* EDX contained PC for DBRA */ + e("mov edx, [d+edi*4]\n"); /* get counter loop */ + e("and edx, 0xffff\n"); + e("inc edx\n"); /* we subtracted 1 a little ways above */ + e("mov word [d+edi*4], 0xffff\n"); + e("mov ebx, edx\n"); + e("shl edx, byte 3\n"); /* (i << 3) + (i << 2) = i*10 */ + e("shl ebx, byte 2\n"); + e("sub ecx, edx\n"); + e("sub ecx, ebx\n"); + e("js near Turbo68KRun_done\n"); + InstEnd(); + } + return 1; +} + +int MOVEM(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * If a non-post/pre-inc/decrement mode is used, the registers are + * transferred to the address, and that address is inc/decremented but the + * change is NOT reflected in the address register. + * + * If the mode is pre-dec or post-inc, it IS reflected. First, load up the + * value of the register, and then write it back and the end, but don't + * modify it while performing the operation, use a temp. X86 register + * instead. + * + * NOTE: The reg->mem mode should be fine, I've tested it -- but there + * might be some bugs. + * + * NOTE (April 8, 2001): Pete Dabbs just emailed me to report a problem + * with MOVEM mem->reg. Basically, code like this wasn't working properly: + * + * MOVEM.L (A7)+,A7 + * + * What should happen there is the data from (A7) is read. Then, 4 is added + * to A7, and finally, the data is written to A7. What Turbo68K was doing + * wrong was it was writing the data from (A7) to A7 and THEN incrementing + * A7. Not sure if the fix is correct, but I hope it is... I didn't change + * anything in MOVEM reg->mem... + */ + + int dr = (op >> 10) & 1, size = UNKNOWN_SIZE; + unsigned ea = op & 0x3f; + + switch ((op >> 6) & 1) + { case 0: size = WORD_SIZE; break; + case 1: size = LONG_SIZE; break; + default: return 0; } + if (!NumDecodedEA(ea)) return 0; + if (!dr) /* reg->mem */ + { + if (!CheckEA(ea, "001011111000")) return 0; + } + else /* mem->reg */ + if (!CheckEA(ea, "001101111011")) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + + if (!dr) /* reg->mem */ + { + SaveReg("run", "eax"); /* save flags */ + e("mov eax, [esi]\n"); /* AX=register list mask */ + e("add esi, byte 2\n"); + LoadControlEA(ea); /* EBX=store address */ + SaveReg("run", "esi"); + if ((ea >> 3) == 4) /* write back if (An)+,-(An) */ + SaveReg("run", "edi"); /* EDI may contain reg # */ + if ((ea >> 3) == 4) /* -(An) has reg mask reversed */ + e("mov esi, d+15*4\n"); /* start from top and decrement */ + else + e("mov esi, d\n"); + EmitLabel(".loop"); + if (size == WORD_SIZE) + e("sub ecx, byte 4\n"); + else + e("sub ecx, byte 8\n"); + e("shr eax, byte 1\n"); /* mask bit->CF */ + e("jnc short .skip\n"); /* not 1? skip this reg */ + + if ((ea >> 3) == 4) /* -(An) */ + { + if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */ + else e("sub ebx, byte 4\n"); + } + + e("mov edx, [esi]\n"); /* get reg to store... */ + SaveReg("run", "ebx"); + if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */ + RestoreReg("run", "ebx"); + + if (!((ea >> 3) == 4)) /* not predec, increment */ + { + if (size == WORD_SIZE) e("add ebx, byte 2\n"); + else e("add ebx, byte 4\n"); + } + + EmitLabel(".skip"); + if ((ea >> 3) == 4) /* -(An) */ + { + e("sub esi, byte 4\n"); /* next reg down */ + e("cmp esi, d-4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + } + else + { + e("add esi, byte 4\n"); /* next reg up */ + e("cmp esi, d+16*4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + } + + if ((ea >> 3) == 4) /* write back if -(An) */ + { + RestoreReg("run", "edi"); + e("mov [a+edi*4], ebx\n"); + } + RestoreReg("run", "esi"); + RestoreReg("run", "eax"); + + switch (ea >> 3) /* timing for address mode */ + { case 2: base_timing = 8; break; /* (An) */ + case 4: base_timing = 8; break; /* -(An) */ + case 5: base_timing = 12; break; /* (d16,An) */ + case 6: base_timing = 14; break; /* (d8,An,Xn) */ + case 7: + switch (ea & 7) + { case 0: base_timing = 12; break; /* (xxx).W */ + case 1: base_timing = 16; break; /* (xxx).L */ + } break; + } + } + else /* mem->reg */ + { + SaveReg("run", "eax"); /* save flags */ + e("mov eax, [esi]\n"); /* AX=register list mask */ + e("add esi, byte 2\n"); + LoadControlEA(ea); /* EBX=load address */ + SaveReg("run", "esi"); + if ((ea >> 3) == 3) /* write back if (An)+,-(An) */ + SaveReg("run", "edi"); /* EDI may contain reg # */ + e("mov esi, d\n"); /* start from bottom and increment */ + EmitLabel(".loop"); + if (size == WORD_SIZE) + e("sub ecx, byte 4\n"); + else + e("sub ecx, byte 8\n"); + e("shr eax, byte 1\n"); /* mask bit->CF */ + e("jnc short .skip\n"); /* not 1? skip this reg */ + + /* if word, sign extend to long */ + if (size == WORD_SIZE) + { + if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) + pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */ + else + ReadWordSX(); + } + else + { + if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) + pcfetch ? ReadLongPC() : ReadLong(); + else + ReadLong(); + } + + if (size == WORD_SIZE) e("add ebx, byte 2\n"); + else e("add ebx, byte 4\n"); + if ((ea >> 3) == 3) + { + RestoreReg("run", "edi"); + e("mov [a+edi*4], ebx\n"); + SaveReg("run", "edi"); + } + + e("mov [esi], edx\n"); /* store to reg */ + + EmitLabel(".skip"); + e("add esi, byte 4\n"); /* next reg up */ + e("cmp esi, d+16*4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + + if ((ea >> 3) == 3) /* just in case SaveReg()/RestoreReg() use stack */ + RestoreReg("run", "edi"); + + RestoreReg("run", "esi"); + RestoreReg("run", "eax"); + + switch (ea >> 3) /* timing for address mode */ + { case 2: base_timing = 12; break; /* (An) */ + case 3: base_timing = 12; break; /* (An)+ */ + case 5: base_timing = 16; break; /* (d16,An) */ + case 6: base_timing = 18; break; /* (d8,An,Xn) */ + case 7: + switch (ea & 7) + { case 0: base_timing = 16; break; /* (xxx).W */ + case 1: base_timing = 20; break; /* (xxx).L */ + case 2: base_timing = 16; break; /* (d16,PC) */ + case 3: base_timing = 18; break; /* (d16,PC,Xn */ + } break; + } + } + + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +/* In case I screwed up MOVEM(), I have a back-up right here ;) */ + +int OldMOVEM(unsigned short op, char *mnem, unsigned base_timing) +{ + /* + * If a non-post/pre-inc/decrement mode is used, the registers are + * transferred to the address, and that address is inc/decremented but the + * change is NOT reflected in the address register. + * + * If the mode is pre-dec or post-inc, it IS reflected. First, load up the + * value of the register, and then write it back and the end, but don't + * modify it while performing the operation, use a temp. X86 register + * instead. + * + * NOTE: The reg->mem mode should be fine, I've tested it -- but there + * might be some bugs. + */ + + int dr = (op >> 10) & 1, size = UNKNOWN_SIZE; + unsigned ea = op & 0x3f; + + switch ((op >> 6) & 1) + { case 0: size = WORD_SIZE; break; + case 1: size = LONG_SIZE; break; + default: return 0; } + if (!NumDecodedEA(ea)) return 0; + if (!dr) /* reg->mem */ + { + if (!CheckEA(ea, "001011111000")) return 0; + } + else /* mem->reg */ + if (!CheckEA(ea, "001101111011")) return 0; + + InstBegin(op, mnem, NumDecodedEA(ea)); + + if (!dr) /* reg->mem */ + { + SaveReg("run", "eax"); /* save flags */ + e("mov eax, [esi]\n"); /* AX=register list mask */ + e("add esi, byte 2\n"); + LoadControlEA(ea); /* EBX=store address */ + SaveReg("run", "esi"); + if ((ea >> 3) == 4) /* write back if (An)+,-(An) */ + SaveReg("run", "edi"); /* EDI may contain reg # */ + if ((ea >> 3) == 4) /* -(An) has reg mask reversed */ + e("mov esi, d+15*4\n"); /* start from top and decrement */ + else + e("mov esi, d\n"); + EmitLabel(".loop"); + if (size == WORD_SIZE) + e("sub ecx, byte 4\n"); + else + e("sub ecx, byte 8\n"); + e("shr eax, byte 1\n"); /* mask bit->CF */ + e("jnc short .skip\n"); /* not 1? skip this reg */ + + if ((ea >> 3) == 4) /* -(An) */ + { + if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */ + else e("sub ebx, byte 4\n"); + } + + e("mov edx, [esi]\n"); /* get reg to store... */ + SaveReg("run", "ebx"); + if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */ + RestoreReg("run", "ebx"); + + if (!((ea >> 3) == 4)) /* not predec, increment */ + { + if (size == WORD_SIZE) e("add ebx, byte 2\n"); + else e("add ebx, byte 4\n"); + } + + EmitLabel(".skip"); + if ((ea >> 3) == 4) /* -(An) */ + { + e("sub esi, byte 4\n"); /* next reg down */ + e("cmp esi, d-4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + } + else + { + e("add esi, byte 4\n"); /* next reg up */ + e("cmp esi, d+16*4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + } + + if ((ea >> 3) == 4) /* write back if -(An) */ + { + RestoreReg("run", "edi"); + e("mov [a+edi*4], ebx\n"); + } + RestoreReg("run", "esi"); + RestoreReg("run", "eax"); + + switch (ea >> 3) /* timing for address mode */ + { case 2: base_timing = 8; break; /* (An) */ + case 4: base_timing = 8; break; /* -(An) */ + case 5: base_timing = 12; break; /* (d16,An) */ + case 6: base_timing = 14; break; /* (d8,An,Xn) */ + case 7: + switch (ea & 7) + { case 0: base_timing = 12; break; /* (xxx).W */ + case 1: base_timing = 16; break; /* (xxx).L */ + } break; + } + } + else /* mem->reg */ + { + SaveReg("run", "eax"); /* save flags */ + e("mov eax, [esi]\n"); /* AX=register list mask */ + e("add esi, byte 2\n"); + LoadControlEA(ea); /* EBX=load address */ + SaveReg("run", "esi"); + if ((ea >> 3) == 3) /* write back if (An)+,-(An) */ + SaveReg("run", "edi"); /* EDI may contain reg # */ + e("mov esi, d\n"); /* start from bottom and increment */ + EmitLabel(".loop"); + if (size == WORD_SIZE) + e("sub ecx, byte 4\n"); + else + e("sub ecx, byte 8\n"); + e("shr eax, byte 1\n"); /* mask bit->CF */ + e("jnc short .skip\n"); /* not 1? skip this reg */ + + /* if word, sign extend to long */ + if (size == WORD_SIZE) + { + if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) + pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */ + else + ReadWordSX(); + } + else + { + if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) + pcfetch ? ReadLongPC() : ReadLong(); + else + ReadLong(); + } + + e("mov [esi], edx\n"); /* store to reg */ + + if (size == WORD_SIZE) e("add ebx, byte 2\n"); + else e("add ebx, byte 4\n"); + + EmitLabel(".skip"); + e("add esi, byte 4\n"); /* next reg up */ + e("cmp esi, d+16*4\n"); /* done? */ + e("jne short .loop\n"); /* not done, keep looping */ + + if ((ea >> 3) == 3) /* write back if (An)+ */ + { + RestoreReg("run", "edi"); + e("mov [a+edi*4], ebx\n"); + } + RestoreReg("run", "esi"); + RestoreReg("run", "eax"); + + switch (ea >> 3) /* timing for address mode */ + { case 2: base_timing = 12; break; /* (An) */ + case 3: base_timing = 12; break; /* (An)+ */ + case 5: base_timing = 16; break; /* (d16,An) */ + case 6: base_timing = 18; break; /* (d8,An,Xn) */ + case 7: + switch (ea & 7) + { case 0: base_timing = 16; break; /* (xxx).W */ + case 1: base_timing = 20; break; /* (xxx).L */ + case 2: base_timing = 16; break; /* (d16,PC) */ + case 3: base_timing = 18; break; /* (d16,PC,Xn */ + } break; + } + } + + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int MOVEUSP(unsigned short op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 8); /* handles all 8 possible An regs */ + e("and edi, byte 7\n"); + e("test byte [sr+1], 0x20\n"); + e("jz near exception_privilege_violation\n"); + if (op & 8) /* USP->An */ + { + e("mov edx, [__sp]\n"); + e("mov [a+edi*4], edx\n"); + } + else /* An->USP */ + { + e("mov edx, [a+edi*4]\n"); + e("mov [__sp], edx\n"); + } + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int Jxx(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + if (!CheckEA(op & 0x3f, "001001111011")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadControlEA(op & 0x3f); + + if ((op & 0xffc0) == 0x4e80) /* JSR */ + { + e("mov edx, esi\n"); /* EDX=PC to save to stack */ + e("sub edx, ebp\n"); + SaveReg("run", "edx"); + } + + e("mov esi, ebx\n"); + UpdateFetchPtr(); + if ((op & 0xffc0) == 0x4e80) /* JSR stuff again... */ + { + RestoreReg("run", "edx"); /* remember? EDX contains PC to save */ + e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */ + e("mov ebx, [a+7*4]\n"); + WriteLong(); /* PC -> (SP) */ + } + + switch ((op >> 3) & 7) /* timing based on control addressing mode */ + { case 2: t = 8; break; /* (An) */ + case 5: t = 10; break; /* (d16,An) */ + case 6: t = 14; break; /* (d8,An,Xn) */ + case 7: + switch (op & 7) + { case 0: t = 10; break; /* (xxx).W */ + case 1: t = 12; break; /* (xxx).L */ + case 2: t = 10; break; /* (d16,pc) */ + case 3: t = 14; break; /* (d8,pc,Xn) */ + } break; + } + if ((op & 0xffc0) == 0x4e80) t += 8; /* JSR */ + EmitTiming(t); + InstEnd(); + return 1; +} + +int LEA(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + if (!CheckEA(op & 0x3f, "001001111011")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadControlEA(op & 0x3f); + e("mov [a+%d*4], ebx\n", (op >> 9) & 7); + switch ((op >> 3) & 7) /* timing based on control addressing mode */ + { case 2: t = 4; break; /* (An) */ + case 5: t = 8; break; /* (d16,An) */ + case 6: t = 12; break; /* (d8,An,Xn) */ + case 7: + switch (op & 7) + { case 0: t = 8; break; /* (xxx).W */ + case 1: t = 12; break; /* (xxx).L */ + case 2: t = 8; break; /* (d16,pc) */ + case 3: t = 12; break; /* (d8,pc,Xn) */ + } break; + } + EmitTiming(t); + InstEnd(); + return 1; +} + +int BTST(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned t; + + if (((op >> 6) & 7) != 4 && ((op >> 6) & 7) != 0) return 0; + if (op & 0x100) /* dynamic */ + { + if (!CheckEA(op & 0x3f, "101111111111")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 6; + e("and edi, byte 7\n"); + e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("bt dword [d+edi*4], ebx\n"); /* bit->CF */ + } + else /* mem is byte */ + { + t = 4; + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ + e("and ebx, byte 7\n"); /* same as modulo 8 */ + e("bt edx, ebx\n"); /* bit->CF */ + } + } + else if (!(op & 0x100)) /* static */ + { + if (!CheckEA(op & 0x3f, "101111111011")) return 0; + if (!NumDecodedEA(op & 0x3f)) return 0; + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op >> 3) & 7) == 0) /* Dn is long */ + { + t = 10; + e("and edi, byte 7\n"); /* isolate Dn reg # */ + e("mov ebx, [esi]\n"); /* get bit # */ + e("and ebx, byte 31\n"); /* same as modulo 32 */ + e("add esi, byte 2\n"); + e("bt dword [d+edi*4], ebx\n"); /* bit->CF */ + } + else /* mem is byte */ + { + t = 8; + e("mov ebx, [esi]\n"); /* get bit # */ + e("and ebx, byte 7\n"); /* same as modulo 8 */ + e("add esi, byte 2\n"); + SaveReg("run", "ebx"); + LoadFromEA(op & 0x3f, BYTE_SIZE, 0); + RestoreReg("run", "ebx"); + e("bt edx, ebx\n"); /* bit->CF */ + } + } + e("setnc bl\n"); /* zero status of C->DH */ + e("and ah, 0xbf\n"); /* get rid of old Z */ + e("shl bl, 6\n"); /* put C in Z position */ + e("or ah, bl\n"); /* set the new Z */ + + if (((op & 0x3f) & 7) == 0) /* Dn is long */ + EmitTiming(t); + else /* mem is byte */ + EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); + InstEnd(); + return 1; +} + +int TST(unsigned short op, char *mnem, unsigned base_timing) +{ + int size; + + switch ((op >> 6) & 3) /* set valid sizes */ + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ + return 0; + if (!CheckEA(op & 0x3f, "101111111000")) + return 0; /* no An,#dat,(d16,pc),(d8,pc,Xn) for 68000 */ + + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + if (((op & 0x3f) >> 3) == 0) /* Dn shortcut */ + { + e("and edi, byte 7\n"); + e("mov edx, [d+edi*4]\n"); + if (size == BYTE_SIZE) + e("test dl, dl\n"); + else if (size == WORD_SIZE) + e("test dx, dx\n"); + else if (size == LONG_SIZE) + e("test edx, edx\n"); + } + else + { + LoadFromEA(op & 0x3f, size, 0); + if (size == BYTE_SIZE) + e("test dl, dl\n"); + else if (size == WORD_SIZE) + e("test dx, dx\n"); + else if (size == LONG_SIZE) + e("test edx, edx\n"); + } + e("lahf\n"); + e("xor al, al\n"); + EmitTiming(base_timing + TimingEA(op & 0x3f, size)); + InstEnd(); + return 1; +} + +int Bxx(unsigned short op, char *mnem, unsigned base_timing) +{ + if ((op & 0xff) != 0 && (op & 0xff) != 1) + return 0; + switch (op & 0xff) + { case 0: InstBegin(op, mnem, 1); break; + case 1: InstBegin(op, mnem, 255); break; + break; } + switch (op & 0xff) + { + case 0: /* 16-bit displacement */ + if ((op & 0xff00) == 0x6100) + { + e("mov edx, 2\n"); + e("add edx, esi\n"); + } + e("movsx ebx, word [esi]\n"); + e("add esi, ebx\n"); + break; + case 1: /* 8-bit displacement -- XX01-XXFF */ + if ((op & 0xff00) == 0x6100) + e("mov edx, esi\n"); + e("and edi, 0xff\n"); + e("mov ebx, edi\n"); + e("movsx ebx, bl\n"); + e("add esi, ebx\n"); + break; + } + if (brafetch) + { + e("sub esi, ebp\n"); /* ESI=denormalized PC */ + if ((op & 0xff00) == 0x6100) /* if BSR, save EDX */ + SaveReg("run", "edx"); + UpdateFetchPtr(); + if ((op & 0xff00) == 0x6100) + RestoreReg("run", "edx"); + } + if ((op & 0xff00) == 0x6100) /* BSR */ + { + e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */ + e("sub edx, ebp\n"); /* EDX=PC of next instruction */ + e("mov ebx, [a+7*4]\n"); + WriteLong(); /* PC -> (SP) */ + } + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int Bcc(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned cond = (op >> 8) & 0xf; + char mnem[4]; + char *mnem_t[] = { NULL, NULL, "BHI", "BLS", + "BCC", "BCS", "BNE", "BEQ", + "BVC", "BVS", "BPL", "BMI", + "BGE", "BLT", "BGT", "BLE" }; + + if (!cond || cond == 1) return 0; /* these not available for Bcc */ + strcpy(mnem, mnem_t[cond]); /* set mnemonic */ + + switch (op & 0xff) + { + case 0: /* 16-bit displacement */ + InstBegin(op, mnem, 1); break; + case 1: /* 8-bit displacement -- XX01-XXFE */ + InstBegin(op, mnem, 255); break; + default: + return 0; + } + + switch (cond) + { + /* EAX:0000000000000000 NZ00 000C 0000 000V */ + case 2: /* HI: !C & !Z */ + e("test ah, byte 0x41\n"); + e("jz short .do\n"); + break; + case 3: /* LS: C | Z */ + e("test ah, byte 0x41\n"); + e("jnz short .do\n"); + break; + case 4: /* CC: !C */ + e("test ah, byte 1\n"); + e("jz short .do\n"); + break; + case 5: /* CS: C */ + e("test ah, byte 1\n"); + e("jnz short .do\n"); + break; + case 6: /* NE: !Z */ + e("test ah, byte 0x40\n"); + e("jz short .do\n"); + break; + case 7: /* EQ: Z */ + e("test ah, byte 0x40\n"); + e("jnz short .do\n"); + break; + case 8: /* VC: !V */ + e("test al, byte 1\n"); + e("jz short .do\n"); + break; + case 9: /* VS: V */ + e("test al, byte 1\n"); + e("jnz short .do\n"); + break; + case 0xa: /* PL: !N */ + e("test ah, byte 0x80\n"); + e("jz short .do\n"); + break; + case 0xb: /* MI: N */ + e("test ah, byte 0x80\n"); + e("jnz short .do\n"); + break; + case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short .do\n"); + break; + case 0xd: /* LT: N & !V | !N & V */ + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short .do\n"); + EmitLabel(".dont_do"); + break; + case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ + /* if Z, then it is GE and we don't take this branch */ + e("test ah, byte 0x40\n"); + e("jnz short .dont_do\n"); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpe short .do\n"); + EmitLabel(".dont_do"); + break; + case 0xf: /* LE: Z | N & !V | !N & V */ + e("test ah, 0x40\n"); + e("jnz short .do\n"); + e("mov bl, al\n"); + e("mov bh, ah\n"); + e("and bh, 0xfe\n"); + e("or bl, bh\n"); + e("test bl, byte 0x81\n"); + e("jpo short .do\n"); + break; + } + + /* branch not taken */ + switch (op & 0xff) /* look at this to figure out timing and increment */ + { + case 0: /* 16-bit */ + e("add esi, byte 2\n"); EmitTiming(12); break; + case 1: /* 8-bit */ + EmitTiming(8); break; + } + InstEnd(); + Align(4); + EmitLabel(".do"); + switch (op & 0xff) + { + case 0: /* 16-bit */ + e("movsx ebx, word [esi]\n"); + e("add esi, ebx\n"); + break; + case 1: /* 8-bit */ + e("and edi, 0xff\n"); + e("mov ebx, edi\n"); + e("movsx ebx, bl\n"); + e("add esi, ebx\n"); + break; + } + if (brafetch) + { + e("sub esi, ebp\n"); /* ESI=denormalized PC */ + UpdateFetchPtr(); + } + EmitTiming(10); + InstEnd(); + return 1; +} + +int Arithmetic(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned t, ea = op & 0x3f, opr_code = (op >> 12), + opmode = (op >> 6) & 7; + int size = UNKNOWN_SIZE; + char mnem[4]; + char opr[4], *size_s[] = { "byte", "word", "dword" }, + *edx[] = { "dl", "dx", "edx" }; + + if (opmode & 4) /* EA == destination */ + { + switch (opr_code) + { + case 8: /* OR */ + case 9: /* SUB */ + case 0xc: /* AND */ + case 0xd: if (!CheckEA(ea, "001111111000")) return 0; /* ADD */ + break; + case 0xb: if (!CheckEA(ea, "101111111000")) return 0; /* EOR */ + break; + default: return 0; } + } + else /* EA == source */ + { + switch (opr_code) + { + case 0xd: /* ADD */ + case 0xb: /* CMP */ + case 9: if (!CheckEA(ea, "111111111111")) return 0; /* SUB */ + if ((ea >> 3) == 1 && (opmode & 3) == 0) + return 0; /* byte access to An not allowed! */ + break; + case 8: /* OR */ + case 0xc: if (!CheckEA(ea, "101111111111")) return 0; /* AND */ + break; + default: return 0; } + } + if (!NumDecodedEA(ea)) /* check if decoded this EA */ + return 0; + switch (opmode & 3) /* set allowed sizes */ + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + switch (opr_code) /* timing */ + { + case 0xd: /* ADD */ + case 9: /* SUB */ + if (size == BYTE_SIZE || size == WORD_SIZE) + { + if ((ea >> 3) == 1) t = 8; + else if ((ea >> 3) == 0) t = 4; + else t = 8; + } + else + { + if ((ea >> 3) == 1) t = 6; + else if ((ea >> 3) == 0) t = 6; + else t = 12; + if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) + t += 2; /* if reg direct or immediate, timing of 6 is + increased to 8 */ + } break; + case 0xb: + if (!(opmode & 4)) /* CMP */ + { + if (size == BYTE_SIZE || size == WORD_SIZE) + { + if ((ea >> 3) == 1) t = 6; + else if ((ea >> 3) == 0) t = 4; + } + else + { + if ((ea >> 3) == 1) t = 6; + else if ((ea >> 3) == 0) t = 6; + } + } + else /* EOR */ + { + if (size == BYTE_SIZE || size == WORD_SIZE) + { + if ((ea >> 3) == 0) t = 4; + else t = 8; + } + else + { + if ((ea >> 3) == 0) t = 8; + else t = 12; + } break; + } + case 0xc: /* AND */ + if (size == BYTE_SIZE || size == WORD_SIZE) + { + if ((ea >> 3) == 0) t = 4; + else t = 8; + } + else + { + if ((ea >> 3) == 0) t = 6; + else t = 12; + if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) + t += 2; /* if reg direct or immediate, timing of 6 is + increased to 8 */ + } break; + case 0x8: /* OR */ + if (size == BYTE_SIZE || size == WORD_SIZE) + { + if ((ea >> 3) == 0) t = 4; + else t = 8; + } + else + { + if ((ea >> 3) == 0) t = 6; + else t = 12; + if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) + t += 2; /* if reg direct or immediate, timing of 6 is + increased to 8 */ + } break; + } + /* set mnem and opr */ + switch (opr_code) + { case 0xd: strcpy(mnem, "ADD"); strcpy(opr, "add"); break; + case 0xc: strcpy(mnem, "AND"); strcpy(opr, "and"); break; + case 0xb: + if (opmode & 4) { strcpy(mnem, "EOR"); strcpy(opr, "xor"); } + else { strcpy(mnem, "CMP"); strcpy(opr, "cmp"); } + break; + case 8: strcpy(mnem, "OR"); strcpy(opr, "or"); break; + case 9: strcpy(mnem, "SUB"); strcpy(opr, "sub"); break; + default: return 0; + } + + + InstBegin(op, mnem, NumDecodedEA(ea)); + if (opmode & 4) /* op Dn,ea */ + { + if ((ea >> 3) == 0 || (ea >> 3) == 1) /* Dn, An shortcut */ + { + char r[2] = { 'd', 'a' }; + int i = ea >> 3; + + e("and edi, byte 7\n"); + e("mov edx, [d+(%d*4)]\n", (op >> 9) & 7); + e("%s %s [%c+(edi*4)], %s\n", opr, size_s[opmode & 3], r[i], + edx[opmode & 3]); + e("lahf\n"); + switch ((op >> 12) & 0xf) /* flags calculations */ + { case 0xc: e("xor al, al\n"); break; /* AND */ + case 0xd: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADD */ + case 0xb: + if (opmode & 4) e("xor al, al\n"); /* EOR */ + else e("seto al\n"); /* CMP */ + break; + case 8: e("xor al, al\n"); break; /* OR */ + case 9: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUB */ } + } + else + { + LoadFromEA(ea, size, 0); + e("%s %s, [d+(%d*4)]\n", opr, edx[opmode & 3], (op >> 9) & 7); + e("lahf\n"); + switch ((op >> 12) & 0xf) /* flags calculations */ + { case 0xc: e("xor al, al\n"); break; /* AND */ + case 0xd: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADD */ + case 0xb: + if (opmode & 4) e("xor al, al\n"); /* EOR */ + else e("seto al\n"); /* CMP */ + break; + case 8: e("xor al, al\n"); break; /* OR */ + case 9: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUB */ } + + if(!(((op >> 12) & 0xf) == 0xb && !(opmode & 4))) // don't write if CMP + { + switch (size) + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + } + else + { + e("xor edi, edi\n"); // the write functions normally handle this + } + } + } + else /* op ea,Dn */ + { + LoadFromEA(ea, size, 0); + e("%s [d+(%d*4)], %s\n", opr, (op >> 9) & 7, edx[opmode & 3]); + e("lahf\n"); + switch ((op >> 12) & 0xf) /* flags calculations */ + { case 0xc: e("xor al, al\n"); break; /* AND */ + case 0xd: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADD */ + case 0xb: + if (opmode & 4) e("xor al, al\n"); /* EOR */ + else e("seto al\n"); /* CMP */ + break; + case 8: e("xor al, al\n"); break; /* OR */ + case 9: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUB */ } + } + + EmitTiming(t); + InstEnd(); + return 1; +} + +int ArithmeticI(unsigned short op, char *mnem_unused, unsigned base_timing) +{ + unsigned t = base_timing, ea = op & 0x3f; /* base_timing = 8 */ + int size = UNKNOWN_SIZE; + char mnem[5]; + char opr[5], *size_s[] = { "byte", "word", "dword" }, + *edx[] = { "dl", "dx", "edx" }; + + if (!CheckEA(ea, "101111111000")) + return 0; + if (!NumDecodedEA(ea)) /* check if decoded this EA */ + return 0; + switch ((op >> 6) & 3) /* set allowed sizes */ + { case 0: size = BYTE_SIZE; break; + case 1: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + switch ((op >> 8) & 0xf) /* set operation, mnem */ + { case 2: strcpy(mnem, "ANDI"); strcpy(opr, "and"); break; + case 6: strcpy(mnem, "ADDI"); strcpy(opr, "add"); break; + case 0xc: strcpy(mnem, "CMPI"); strcpy(opr, "cmp"); break; + case 0xa: strcpy(mnem, "EORI"); strcpy(opr, "xor"); break; + case 0: strcpy(mnem, "ORI"); strcpy(opr, "or"); break; + case 4: strcpy(mnem, "SUBI"); strcpy(opr, "sub"); break; + default: return 0; } + /* all but CMPI,ANDI take 8+8 cycles for long sizes */ + if (((op >> 8) & 0xf) != 0xc && ((op >> 8) & 0xf) != 2 && size == LONG_SIZE) + t += 8; + else if (((op >> 8) & 0xf) == 0xc) t += 6; + + InstBegin(op, mnem, NumDecodedEA(ea)); + if (((ea >> 3) & 7) == 0) /* Dn is treated differently */ + { + e("and edi, byte 7\n");/* get reg # */ + e("mov edx, [esi]\n"); /* get imm data */ + if (size == LONG_SIZE) + e("ror edx, byte 16\n"); /* prep. the long-word */ + e("%s %s [d+edi*4], %s\n", opr, size_s[(op >> 6) & 3], edx[(op >> 6) & 3]); + e("lahf\n"); + switch ((op >> 8) & 0xf) /* flags calculations */ + { case 2: e("xor al, al\n"); break; /* ANDI */ + case 6: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADDI */ + case 0xc: e("seto al\n"); break; /* CMPI */ + case 0xa: e("xor al, al\n"); break; /* EORI */ + case 0: e("xor al, al\n"); break; /* ORI */ + case 4: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUBI */ } + if (size != LONG_SIZE) + e("add esi, byte 2\n"); + else + e("add esi, byte 4\n"); + } + else + { + SaveReg("run", "esi"); /* address of immediate data */ + switch (size) /* make ESI point to EA data */ + { case BYTE_SIZE: + case WORD_SIZE: e("add esi, byte 2\n"); break; + case LONG_SIZE: e("add esi, byte 4\n"); break; + } + LoadFromEA(ea, size, 0); + RestoreRegTo("run", "esi", "edi"); /* EDI=address of immediate data */ + /* if byte or word, perform operation directly from memory */ + if (size == BYTE_SIZE || size == WORD_SIZE) + { + e("%s %s, [edi]\n", opr, edx[(op >> 6) & 3]); + e("lahf\n"); + switch ((op >> 8) & 0xf) /* flags calculations */ + { case 2: e("xor al, al\n"); break; /* ANDI */ + case 6: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADDI */ + case 0xc: e("seto al\n"); break; /* CMPI */ + case 0xa: e("xor al, al\n"); break; /* EORI */ + case 0: e("xor al, al\n"); break; /* ORI */ + case 4: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUBI */ } + } + else /* long */ + { + e("mov edi, [edi]\n"); + e("ror edi, byte 16\n"); /* word-swap long-words... */ + e("%s %s, edi\n", opr, edx[(op >> 6) & 3]); + e("lahf\n"); + switch ((op >> 8) & 0xf) /* flags calculations */ + { case 2: e("xor al, al\n"); break; /* ANDI */ + case 6: e("setc byte [x]\n"); + e("seto al\n"); break; /* ADDI */ + case 0xc: e("seto al\n"); break; /* CMPI */ + case 0xa: e("xor al, al\n"); break; /* EORI */ + case 0: e("xor al, al\n"); break; /* ORI */ + case 4: e("setc byte [x]\n"); + e("seto al\n"); break; /* SUBI */ } + } + + if(((op >> 8) & 0x0F) != 0x0C) + { + switch (size) /* write back to mem */ + { case BYTE_SIZE: WriteByte(); break; + case WORD_SIZE: WriteWord(); break; + case LONG_SIZE: WriteLong(); break; + } + } + else + { + e("xor edi, edi\n"); + } + + } + EmitTiming(t + TimingEA(ea, size)); + InstEnd(); + return 1; +} + + +int MOVE(unsigned short op, char *mnem, unsigned base_timing) +{ + unsigned src = op & 0x3f, dest = (op >> 6) & 0x3f; + int size = UNKNOWN_SIZE; + + dest = ((dest & 0x7) << 3) | ((dest >> 3) & 0x7); + if (!CheckEA(src, "111111111111") || !CheckEA(dest, "101111111000")) + return 0; + if (!NumDecodedEA(src)) /* check if decoded this source EA */ + return 0; + switch ((op >> 12) & 3) /* set allowed sizes */ + { case 1: size = BYTE_SIZE; break; + case 3: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + if (size == BYTE_SIZE && ((src >> 3) & 7) == 1) + return 0; /* no An byte accesses allowed */ + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadFromEA(src, size, 0); + switch (size) + { case BYTE_SIZE: e("test dl, dl\n"); break; + case WORD_SIZE: e("test dx, dx\n"); break; + case LONG_SIZE: e("test edx, edx\n"); break; } + e("lahf\n"); + e("xor al, al\n"); /* V=always cleared */ + if (src == dest) + StoreToEA(dest, size, 1); + else + StoreToEA(dest, size, 0); + EmitTiming(base_timing + TimingEA(src, size) + TimingEA(dest, size)); + InstEnd(); + return 1; +} + +int MOVEQ(unsigned op, char *mnem, unsigned base_timing) +{ + InstBegin(op, mnem, 256); /* 256 possible immediate data combinations */ + e("mov ebx, edi\n"); /* opcode is in edi */ + e("movsx ebx, bl\n"); /* sign extend */ + e("mov [d+%d*4], ebx\n", (op >> 9) & 7); + e("test bl, bl\n"); /* set N,Z,C appropriately */ + e("lahf\n"); /* get changes */ + e("xor al, al\n"); /* V is cleared */ + EmitTiming(base_timing); + InstEnd(); + return 1; +} + +int MOVEA(unsigned short op, char *mnem, unsigned base_timing) +{ + int size = UNKNOWN_SIZE; + + if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */ + return 0; + if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ + return 0; + switch ((op >> 12) & 3) /* set allowed sizes */ + { case 3: size = WORD_SIZE; break; + case 2: size = LONG_SIZE; break; + default: return 0; } + + InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); + LoadFromEA(op & 0x3f, size, 1); /* load */ + e("mov [a+%d*4], edx\n", (op >> 9) & 7); /* store */ + EmitTiming(TimingEA(op & 0x3f, size) + base_timing); + InstEnd(); + return 1; +} + + +/***************************************************************************** +* Instruction Emission */ + +void EmitInstructions() +{ + unsigned i, j, k, l, n; + + /* TST */ + n = 0; + for (i = 0; i <= 2; i++) + for (j = 0; j <= 0x3f; j++) + n += TST((i << 6) + j + 0x4a00, "TST", 4); + printf("TST:\t\t%d\n", n); + + /* Bcc */ + n = 0; + for (i = 0; i <= 0xf; i++) + for (j = 0; j < 0xff; j++) + n += Bcc((i << 8) + j + 0x6000, "B??", 0); + printf("Bcc:\t\t%d\n", n); + + /* MOVE */ + n = 0; + for (i = 1; i <= 3; i++) /* size */ + for (j = 0; j <= 0x3f; j++) /* dest. EA */ + for (k = 0; k <= 0x3f; k++) /* source EA */ + n += MOVE((i << 12) + (j << 6) + k + 0x0000, "MOVE", 4); + printf("MOVE:\t\t%d\n", n); + + /* ADD, AND, CMP, EOR, OR, SUB -- "Arithmetic" */ + n = 0; + for (i = 8; i <= 0xd; i++) /* operation */ + for (j = 0; j <= 7; j++) /* Dn */ + for (k = 0; k <= 6; k++) /* opmode */ + for (l = 0; l <= 0x3f; l++) /* EA */ + n += Arithmetic((i << 12) + (j << 9) + (k << 6) + l, "????", 0); + printf("ADD, AND, CMP, EOR, OR, SUB: %d\n", n); + + /* ADDI, ANDI, CMPI, EORI, ORI, SUBI -- "ArithmeticI" */ + n = 0; + for (i = 0; i <= 0xc; i++) /* operation */ + for (j = 0; j <= 2; j++) /* size */ + for (k = 0; k <= 0x3f; k++) /* EA */ + n += ArithmeticI((i << 8) + (j << 6) + k + 0x0000, "????I", 8); + printf("ADDI, ANDI, CMPI, EORI, ORI, SUBI: %d\n", n); + + /* DBcc */ + n = 0; + for (i = 0; i <= 0xf; i++) /* condition */ + n += DBcc((i << 8) + 0x50c8, "DB??", 0); + printf("DBcc:\t\t%d\n", n); + + /* BTST */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += BTST((i << 9) + j + 0x0100, "BTST", 0); + for (i = 0; i <= 0x3f; i++) /* EA */ + n += BTST(i + 0x0800, "BTST", 0); + printf("BTST:\t\t%d\n", n); + + /* ADDQ, SUBQ -- "ArithmeticQ" */ + n = 0; + for (i = 0; i <= 7; i++) /* data */ + for (j = 0; j <= 1; j++) /* ADDQ/SUBQ */ + for (k = 0; k <= 2; k++) /* size */ + for (l = 0; l <= 0x3f; l++) /* EA */ + n += ArithmeticQ((i << 9) + (j << 8) + (k << 6) + l + 0x5000, "???Q", 4); + printf("ADDQ, SUBQ:\t%d\n", n); + + /* BRA */ + n = 0; + for (i = 0; i <= 0xff; i++) + n += Bxx(i + 0x6000, "BRA", 10); + printf("BRA:\t\t%d\n", n); + + /* LEA */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += LEA((i << 9) + 0x41c0 + j, "LEA", 0); + printf("LEA:\t\t%d\n", n); + + /* RTS */ + RTS(0x4e75, "RTS", 16); + printf("RTS:\t\t1\n"); + + /* MOVEA */ + n = 0; + for (i = 2; i <= 3; i++) /* size */ + for (j = 0; j <= 7; j++) /* dest. reg */ + for (k = 0; k <= 0x3f; k++) /* source EA */ + n += MOVEA((i << 12) + (j << 9) + k + 0x0040, "MOVEA", 4); + printf("MOVEA:\t\t%d\n", n); + + /* ADDA */ + n = 0; + for (i = 3; i <= 7; i++) /* size */ + for (j = 0; j <= 7; j++) /* dest. reg */ + for (k = 0; k <= 0x3f; k++) /* source EA */ + n += ArithmeticA((i << 6) + (j << 9) + k + 0xd000, "ADDA", 4); + printf("ADDA:\t\t%d\n", n); + + /* MOVEQ */ + n = 0; + for (i = 0; i < 8; i++) + n += MOVEQ((i << 9) + 0x7000, "MOVEQ", 4); + printf("MOVEQ:\t\t%d\n", n); + + /* JSR */ + n = 0; + for (i = 0; i <= 0x3f; i++) /* register */ + n += Jxx(i + 0x4e80, "JSR", 0); + printf("JSR:\t\t%d\n", n); + + /* LSL, LSR */ + n = 0; + for (i = 0; i <= 7; i++) /* count/register */ + for (j = 0; j <= 1; j++) /* direction */ + for (k = 0; k <= 2; k++) /* size */ + for (l = 0; l <= 1; l++) /* immediate/reg */ + n += LSxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe008, "LS?", 0); + for (i = 0; i <= 1; i++) /* direction */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += LSxMem((i << 8) + j + 0xe2c0, "LS?", 8); + printf("LSL, LSR:\t%d\n", n); + + /* SWAP */ + SWAP(0x4840, "SWAP", 4); + printf("SWAP:\t\t1\n"); + + /* BSR */ + n = 0; + for (i = 0; i <= 0xff; i++) + n += Bxx(i + 0x6100, "BSR", 18); + printf("BSR:\t\t%d\n", n); + + /* EXT */ + n = 0; + for (i = 2; i <= 7; i++) + n += EXT((i << 6) + 0x4800, "EXT", 4); + printf("EXT:\t\t%d\n", n); + + /* CLR */ + n = 0; + for (i = 0; i <= 2; i++) /* size */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += CLR((i << 6) + j + 0x4200, "CLR", 0); + printf("CLR:\t\t%d\n", n); + + /* MOVEM */ + n = 0; + for (i = 0; i <= 1; i++) /* direction */ + for (j = 0; j <= 1; j++) /* size */ + for (k = 0; k <= 0x3f; k++) /* EA */ + n += MOVEM((i << 10) + (j << 6) + k + 0x4880, "MOVEM", 0); + printf("MOVEM:\t\t%d\n", n); + + /* MOVE to SR */ + n = 0; + for (i = 0; i <= 0x3f; i++) + n += MOVEtoSR(i + 0x46c0, "MOVE to SR", 12); + printf("MOVE to SR:\t%d\n", n); + + /* NOP */ + NOP(0x4e71, "NOP", 4); + printf("NOP:\t\t1\n"); + + /* JMP */ + n = 0; + for (i = 0; i <= 0x3f; i++) /* register */ + n += Jxx(i + 0x4ec0, "JMP", 0); + printf("JMP:\t\t%d\n", n); + + /* CMPA */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 3; j <= 7; j++) /* opmode */ + for (k = 0; k <= 0x3f; k++) /* EA */ + n += CMPA((i << 9) + (j << 6) + k + 0xb000, "CMPA", 0); + printf("CMPA:\t\t%d\n", n); + + /* ASL, ASR */ + n = 0; + for (i = 0; i <= 7; i++) /* count/reg */ + for (j = 0; j <= 1; j++) /* direction */ + for (k = 0; k <= 2; k++) /* size */ + for (l = 0; l <= 1; l++) /* immediate/reg */ + n += ASxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe000, "AS?", 0); + for (i = 0; i <= 1; i++) /* direction */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += ASxMem((i << 8) + j + 0xe0c0, "AS?", 8); + printf("ASL, ASR:\t%d\n", n); + + /* MULS */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += MULS((i << 9) + j + 0xc1c0, "MULS", 70); + printf("MULS:\t\t%d\n", n); + + /* RTE */ + RTE(0x4e73, "RTE", 20); + printf("RTE:\t\t1\n"); + + /* NEG, NEGX */ + n = 0; + for (i = 0; i <= 1; i++) /* NEG/NEGX */ + for (j = 0; j <= 2; j++) /* size */ + for (k = 0; k <= 0x3f; k++) /* EA */ + n += NEGx((i << 10) + (j << 6) + k + 0x4000, "NEG?", 0); + printf("NEG, NEGX:\t%d\n", n); + + /* ROL, ROR */ + n = 0; + for (i = 0; i <= 7; i++) /* count/register */ + for (j = 0; j <= 1; j++) /* direction */ + for (k = 0; k <= 2; k++) /* size */ + for (l = 0; l <= 1; l++) /* immediate/reg */ + n += ROxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe018, "RO?", 0); + for (i = 0; i <= 1; i++) /* direction */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += ROxMem((i << 8) + j + 0xe6c0, "RO?", 8); + printf("ROL, ROR:\t%d\n", n); + + /* BCLR */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += BCLR((i << 9) + j + 0x0180, "BCLR", 0); + for (i = 0; i <= 0x3f; i++) /* EA */ + n += BCLR(i + 0x0880, "BCLR", 0); + printf("BCLR:\t\t%d\n", n); + + /* NOT */ + n = 0; + for (i = 0; i <= 2; i++) + for (j = 0; j <= 0x3f; j++) + n += NOT((i << 6) + j + 0x4600, "NOT", 0); + printf("NOT:\t\t%d\n", n); + + /* MOVE USP */ + n = 0; + for (i = 0; i <= 1; i++) + n += MOVEUSP((i << 3) + 0x4e60, "MOVE USP", 4); + printf("MOVE USP:\t%d\n", n); + + /* ROXL, ROXR */ + n = 0; + for (i = 0; i <= 7; i++) /* count/register */ + for (j = 0; j <= 1; j++) /* direction */ + for (k = 0; k <= 2; k++) /* size */ + for (l = 0; l <= 1; l++) /* immediate/reg */ + n += ROXxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe010, "ROX?", 0); + for (i = 0; i <= 1; i++) /* direction */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += ROXxMem((i << 8) + j + 0xe4c0, "ROX?", 8); + printf("ROXL, ROXR:\t%d\n", n); + + /* BSET */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += BSET((i << 9) + j + 0x01c0, "BSET", 0); + for (i = 0; i <= 0x3f; i++) /* EA */ + n += BSET(i + 0x08c0, "BSET", 0); + printf("BSET:\t\t%d\n", n); + + /* MOVE from SR */ + + /* + * The 68000 and 68008 "MOVE from SR" is not privileged. But for the 68010 + * and higher, it is. + */ + + n = 0; + for (i = 0; i <= 0x3f; i++) + n += MOVEfromSR(i + 0x40c0, "MOVE from SR", 0); + printf("MOVE from SR:\t%d\n", n); + + /* SUBA */ + n = 0; + for (i = 3; i <= 7; i++) /* size */ + for (j = 0; j <= 7; j++) /* dest. reg */ + for (k = 0; k <= 0x3f; k++) /* source EA */ + n += ArithmeticA((i << 6) + (j << 9) + k + 0x9000, "SUBA", 4); + printf("SUBA:\t\t%d\n", n); + + /* DIVS */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += DIVS((i << 9) + j + 0x81c0, "DIVS", 158); + printf("DIVS:\t\t%d\n", n); + + /* MULU */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += MULU((i << 9) + j + 0xc0c0, "MULU", 70); + printf("MULU:\t\t%d\n", n); + + /* ADDX */ + n = 0; + for (i = 0; i <= 7; i++) /* Rx */ + for (j = 0; j <= 2; j++) /* size */ + for (k = 0; k <= 1; k++) /* R/M */ + n += ADDX((i << 9) + (j << 6) + (k << 3) + 0xd100, "ADDX", 0); + printf("ADDX:\t\t%d\n", n); + + /* Scc */ + n = 0; + for (i = 0; i <= 0xf; i++) /* condition */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += Scc((i << 8) + j + 0x50c0, "S??", 0); + printf("Scc:\t\t%d\n", n); + + /* ORI to SR */ + ORItoSR(0x007c, "ORI to SR", 20); + printf("ORI to SR:\t1\n"); + + /* MOVE to CCR */ + n = 0; + for (i = 0; i <= 0x3f; i++) + n += MOVEtoCCR(i + 0x44c0, "MOVE to CCR", 12); + printf("MOVE to CCR:\t%d\n", n); + + /* EXG */ + n = 0; + for (i = 0; i <= 7; i++) /* Rx */ + for (j = 0; j <= 0x11; j++) /* opmode */ + n += EXG((i << 9) + (j << 3) + 0xc100, "EXG", 6); + printf("EXG:\t\t%d\n", n); + + /* UNLK */ + UNLK(0x4e58, "UNLK", 12); + printf("UNLK:\t\t1\n"); + + /* LINK */ + LINK(0x4e50, "LINK", 16); /* word */ + printf("LINK:\t\t1\n"); + + /* DIVU */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += DIVU((i << 9) + j + 0x80c0, "DIVU", 144); + printf("DIVU:\t\t%d\n", n); + + /* ANDI to CCR */ + ANDItoCCR(0x023c, "ANDI to CCR", 20); + printf("ANDI to CCR:\t1\n"); + + /* PEA */ + n = 0; + for (i = 0; i <= 0x3f; i++) + n += PEA(i + 0x4840, "PEA", 0); + printf("PEA:\t\t%d\n", n); + + /* ANDI to SR */ + ANDItoSR(0x027c, "ANDI to SR", 20); + printf("ANDI to SR:\t%d\n", n); + + /* BCHG */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 0x3f; j++) /* EA */ + n += BCHG((i << 9) + j + 0x0140, "BCHG", 0); + for (i = 0; i <= 0x3f; i++) /* EA */ + n += BCHG(i + 0x0840, "BCHG", 0); + printf("BCHG:\t\t%d\n", n); + + /* ORI to CCR */ + ORItoCCR(0x003c, "ORI to CCR", 20); + printf("ORI to CCR:\t1\n"); + + /* RTR */ + RTR(0x4e77, "RTR", 20); + printf("RTR:\t\t1\n"); + + /* TRAP */ + TRAP(0x4e40, "TRAP", 34); + printf("TRAP:\t\t1\n"); + + /* SBCD */ + n = 0; + for (i = 0; i <= 7; i++) /* Rx */ + for (j = 0; j <= 1; j++) /* R/M */ + n += SBCD((i << 9) + (j << 3) + 0x8100, "SBCD", 0); + printf("SBCD:\t\t%d\n", n); + + /* SUBX */ + n = 0; + for (i = 0; i <= 7; i++) /* Rx */ + for (j = 0; j <= 2; j++) /* size */ + for (k = 0; k <= 1; k++) /* R/M */ + n += SUBX((i << 9) + (j << 6) + (k << 3) + 0x9100, "SUBX", 0); + printf("SUBX:\t\t%d\n", n); + + /* ABCD */ + n = 0; + for (i = 0; i <= 7; i++) /* Rx */ + for (j = 0; j <= 1; j++) /* R/M */ + n += ABCD((i << 9) + (j << 3) + 0xc100, "ABCD", 0); + printf("ABCD:\t\t%d\n", n); + + /* CMPM */ + n = 0; + for (i = 0; i <= 7; i++) /* Ax */ + for (j = 0; j <= 2; j++) /* size */ + n += CMPM((i << 9) + (j << 6) + 0xb108, "CMPM", 0); + printf("CMPM:\t\t%d\n", n); + + /* MOVEP */ + n = 0; + for (i = 0; i <= 7; i++) /* Dx */ + for (j = 0; j <= 7; j++) /* opmode */ + n += MOVEP((i << 9) + (j << 6) + 0x0008, "MOVEP", 0); + printf("MOVEP:\t\t%d\n", n); + + /* + * The following have no profile data associated with them, as far as my + * tests have shown. + */ + + /* BKPT */ + if (mpu == 68010) + { + n = BKPT(0x4848, "BKPT", 45); + printf("BKPT:\t\t%d\n", n); + } + + /* CHK */ + n = 0; + for (i = 0; i <= 7; i++) /* register */ + for (j = 0; j <= 3; j++) /* size */ + for (k = 0; k <= 0x3f; k++) /* EA */ + n += CHK((i << 9) + (j << 7) + k + 0x4000, "CHK", 10); + printf("CHK:\t\t%d\n", n); + + /* EORI to CCR */ + EORItoCCR(0x0a3c, "EORI to CCR", 20); + printf("EORI to CCR:\t1\n"); + + /* EORI to SR */ + EORItoSR(0x0a7c, "EORI to SR", 20); + printf("EORI to SR:\t1\n"); + + /* ILLEGAL */ + InstBegin(0x4afc, "ILLEGAL", 1); + e("jmp near exception_illegal_instruction\n"); + printf("ILLEGAL:\t1\n"); + + /* MOVEC */ + if (mpu == 68010) + { + n = MOVEC(0x4e7a, "MOVEC", 0); + printf("MOVEC:\t\t%d\n", n); + } + + /* MOVE from CCR -- 68010, 68020, 68030, 68040, CPU32 */ + if (mpu == 68010) + { + n = 0; + for (i = 0; i <= 0x3f; i++) + n += MOVEfromCCR(i + 0x42c0, "MOVE from CCR", 0); + printf("MOVE from CCR:\t%d\n", n); + } + + /* NBCD */ + n = 0; + for (i = 0; i <= 0x3f; i++) /* EA */ + n += NBCD(i + 0x4800, "NBCD", 0); + printf("NBCD:\t\t%d\n", n); + + /* RESET */ + RESET(0x4e70, "RESET", 132); + printf("RESET:\t\t1\n"); + + /* RTD */ + if (mpu == 68010) + { + RTD(0x4e74, "RTD", 16); + printf("RTD:\t\t1\n"); + } + + /* STOP */ + STOP(0x4e72, "STOP", 4); + printf("STOP:\t\t1\n"); + + /* TAS */ + n = 0; + for (i = 0; i <= 0x3f; i++) + n += TAS(i + 0x4ac0, "TAS", 0); + printf("TAS:\t\t%d\n", n); + + /* TRAPV */ + TRAPV(0x4e76, "TRAPV", 0); + printf("TRAPV:\t\t1\n"); + + if (illegal) /* emulate Line 1010 and Line 1111 exceptions */ + { + /* Line 1010 */ + InstBegin(0xa000, "Line 1010 Emulator", 4096); + e("jmp near exception_line_1010_emulator\n"); + + /* Line 1111 */ + InstBegin(0xf000, "Line 1111 Emulator", 4096); + e("jmp near exception_line_1111_emulator\n"); + + num_handlers -= 2; /* don't count these as instruction handlers */ + } + + /* I_Invalid: Invalid instruction */ + EmitLabel("I_Invalid"); + if (illegal) /* emulate illegal instruction exceptions */ + e("jmp near exception_illegal_instruction\n"); + else + { + e("sub esi, byte 2\n"); /* back up to the bad instruction */ + e("jmp invalid_error\n"); /* invalid instruction error */ + } +} + +void EmitExceptions() +{ + + /* + * exception_chk: + * exception_divide_by_zero: + * Assume the PC points to the instruction AFTER the instruction which + * caused the trap. + */ + Align(4); + EmitLabel("exception_chk"); + e("push dword 6*4\n"); /* CHK exception vector */ + e("jmp short do_exception\n"); + Align(4); + EmitLabel("exception_divide_by_zero"); + e("push dword 5*4\n"); /* division by zero exception vector */ + e("jmp short do_exception\n"); + + /* + * exception_privilege_violation: + * exception_illegal_instruction: + * exception_line_1010_emulator: + * exception_line_1111_emulator: + * Assume the PC points to the word AFTER the instruction which caused the + * trap. The value 2 will be subtracted from the PC in order to make them + * point AT the instruction which caused the trap, as this is how the + * exceptions work. + */ + Align(4); + EmitLabel("exception_privilege_violation"); + e("push dword 8*4\n"); /* privilege violation exception vector */ + e("jmp short do_exception_fix_pc\n"); + Align(4); + EmitLabel("exception_illegal_instruction"); + e("push dword 4*4\n"); /* illegal instruction exception vector */ + e("jmp short do_exception_fix_pc\n"); + Align(4); + EmitLabel("exception_line_1010_emulator"); + e("push dword 10*4\n"); /* Line 1010 emulator exception vector */ + e("jmp short do_exception_fix_pc\n"); + Align(4); + EmitLabel("exception_line_1111_emulator"); + e("push dword 11*4\n"); /* Line 1111 emulator exception vector */ + e("jmp short do_exception_fix_pc\n"); + + EmitLabel("do_exception_fix_pc"); /* points PC at trap instruction */ + e("sub esi, byte 2\n"); + EmitLabel("do_exception"); + e("mov edx, 0x2000\n"); /* what the SR will be */ + e("xor edx, [sr]\n"); /* see if S bit is different */ + e("test edx, 0x2000\n"); + e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ + e("mov edx, [__sp]\n"); + e("xchg [a+7*4], edx\n"); + e("mov [__sp], edx\n"); + SetSupervisorAddressSpace(); /* map in supervisor address space */ + EmitLabel(".no_sp_magic"); + + e("mov ebx, [a+7*4]\n"); /* SP */ + /* + * If 68010, we have to push the format word on the stack first. + */ + if (mpu == 68010) + { + e("sub ebx, byte 2\n");/* SP-2 */ + e("mov edx, [esp]\n"); /* vector offset | 0x00000000 */ + SaveReg("run", "ebx"); + WriteWord(); + RestoreReg("run", "ebx"); + } + e("mov edx, esi\n"); + e("sub edx, ebp\n"); /* PC->EDX */ + e("sub ebx, byte 4\n"); /* SP-4 */ + SaveReg("run", "ebx"); + WriteLong(); + RestoreReg("run", "ebx"); + e("mov edx, [sr]\n"); /* get SR */ + e("sub ebx, byte 2\n"); /* SP-2 */ + SaveReg("run", "ebx"); + WriteWord(); /* save SR to stack */ + RestoreReg("run", "ebx"); + e("or dh, 0x20\n"); /* set supervisor for exception */ + e("and edx, 0xa71f\n"); /* clear unwanted bits */ + e("mov [sr], edx\n"); /* set new SR, get old SR->EDX */ + e("mov [a+7*4], ebx\n"); /* write back SP */ + + e("pop ebx\n"); /* get exception vector address */ + if (mpu == 68010) + e("add ebx, [vbr]\n"); + + ReadLong(); /* get PC */ + e("mov esi, edx\n"); /* set new PC */ + UpdateFetchPtr(); + e("xor edi, edi\n"); + e("sub ecx, byte 40\n"); /* timing... */ + e("mov di, [esi]\n"); + e("add esi, byte 2\n"); + e("jmp [jmptab+edi*4]\n"); /* continue execution */ +} + +/***************************************************************************** +* Data */ + +void EmitData() +{ + int i; + + CacheAlign(); + EmitGlobalLabel(NameOfContext()); + e("context_start:\n"); + e("fetch dd 0\n"); /* pointer to fetch array */ + e("pcfetch dd 0\n"); /* PC-relative fetch */ + e("read_byte dd 0\n"); /* pointer to read_byte array */ + e("read_word dd 0\n"); /* pointer to read_word array */ + e("read_long dd 0\n"); /* pointer to read_long array */ + e("write_byte dd 0\n"); /* pointer to write_byte array */ + e("write_word dd 0\n"); /* pointer to write_word array */ + e("write_long dd 0\n"); /* pointer to write_long array */ + e("super_fetch dd 0\n"); /* supervisor memory map... */ + e("super_pcfetch dd 0\n"); + e("super_read_byte dd 0\n"); + e("super_read_word dd 0\n"); + e("super_read_long dd 0\n"); + e("super_write_byte dd 0\n"); + e("super_write_word dd 0\n"); + e("super_write_long dd 0\n"); + e("user_fetch dd 0\n"); /* user memory map... */ + e("user_pcfetch dd 0\n"); + e("user_read_byte dd 0\n"); + e("user_read_word dd 0\n"); + e("user_read_long dd 0\n"); + e("user_write_byte dd 0\n"); + e("user_write_word dd 0\n"); + e("user_write_long dd 0\n"); + + e("intr times 8 dd 0\n"); /* intr[0-6]=vectors for interrupt + levels 1-7 (0=not pending.) + intr[7]=# of interrupts pending */ + e("cycles dd 0\n"); /* cycles total to emulate */ + e("remaining dd 0\n"); /* cycles remaining */ + e("d times 8 dd 0\n"); /* D0-D7 */ + e("a times 8 dd 0\n"); /* A0-A7 */ + e("__sp dd 0\n"); /* User mode: SSP, Supervisor: USP */ + e("sr dd 0\n"); /* SR */ + e("pc dd 0\n"); /* PC */ + if (mpu == 68010) + { + e("fc dd 0\n"); /* FC (DFC, SFC) */ + e("vbr dd 0\n"); /* VBR */ + } + e("status dd 0\n"); /* bit 0:1=emulating, 0=not + bit 1:1=stopped, 0=not + bit 2:1=processing ints, 0=not */ + e("InterruptAcknowledge dd 0\n"); /* interrupt acknowledge callback */ + e("Reset dd 0\n"); /* pointer to RESET handler */ + if (mpu == 68010) + e("Bkpt dd 0\n"); /* pointer to BKPT handler */ + e("Debug dd 0\n"); /* pointer to debug handler */ + e("context_end:\n"); + e("x dd 0\n"); /* X flag (maintained internally) */ + for (i = 0; i < 7; i++) + { + char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" }; + e("memhandler_%s dd 0\n", reg[i]); + } + for (i = 0; i < 7; i++) + { + char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" }; + e("run_%s dd 0\n", reg[i]); + } + e("fetch_esi dd 0\n"); /* for UpdateFetchPtr() */ +} + +/***************************************************************************** +* Code */ + +void EmitCode() +{ + int i; + + /* Turbo68KInit() */ + + /* + * Decompresses the jump table. See main() for the compression format + */ + + EmitGlobalLabel("Turbo68KInit"); + e("push ecx\n"); + e("push edx\n"); + e("push esi\n"); + e("push edi\n"); + e("mov esi, compressed_jmptab\n"); + e("mov edi, jmptab\n"); + e("xor ecx, ecx\n"); /* we only need lower half (CX) */ + EmitLabel(".l"); + e("mov cx, [esi]\n"); /* get repeat information */ + e("add esi, byte 2\n"); /* point at data */ + e("mov edx, ecx\n"); + e("and edx, 0xc000\n"); + e("cmp dx, 0xc000\n"); /* #handlers * 1 each? */ + e("je .r1\n"); + e("cmp dx, 0x8000\n"); /* #handlers * 8 each? */ + e("je .r8\n"); + e("mov eax, [esi]\n"); /* get data and repeat */ + e("add esi, byte 4\n"); + e("cld\n"); + e("rep stosd\n"); + + EmitLabel(".c"); + e("cmp dword [esi], byte -1\n"); /* end of compressed data? */ + e("jne .l\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("xor eax, eax\n"); + e("ret\n"); + + EmitLabel(".r1"); + e("and ecx, 0x3ff\n"); /* get rid of 0xc000 marker */ + e("cld\n"); + e("rep movsd\n"); /* move address to decomp'd jmptab */ + e("jmp .c\n"); /* continue decompression */ + + EmitLabel(".r8"); + e("and ecx, 0x7ff\n"); /* get rid of 0x8000 marker */ + EmitLabel(".r8_l"); + e("mov edx, ecx\n"); /* save counter temporarily */ + e("mov ecx, 8\n"); /* repeat handler 8 times */ + e("mov eax, [esi]\n"); /* get handler to repeat */ + e("add esi, byte 4\n"); /* point to next */ + e("cld\n"); + e("rep stosd\n"); /* store! */ + e("mov ecx, edx\n"); /* restore loop counter and loop */ + e("dec ecx\n"); + e("jnz .r8_l\n"); + e("jmp .c\n"); + + /* Turbo68KReset() */ + + Align(4); + EmitGlobalLabel("Turbo68KReset"); + e("push ebx\n"); + e("push ecx\n"); + e("push edx\n"); + e("push esi\n"); + e("push edi\n"); + e("push ebp\n"); + SetSupervisorAddressSpace(); + EMULATING(); + UNSTOP_CPU(); /* in case we're STOPped, unstop */ + if (mpu == 68010) /* clear 68010 VBR */ + e("mov dword [vbr], 0\n"); + + e("xor ecx, ecx\n"); /* no cycles executed in case error */ + + /* + * Read the PC vector from the fetch memory map + */ + if (!call_convention) /* stack calling convention */ + e("push dword 4\n"); + else /* register calling convention */ + e("mov eax, 4\n"); + e("call %sTurbo68KFetchPtr\n", id); + if (!call_convention) + e("add esp, byte 4\n"); + e("test eax, eax\n"); + e("jz near fetch_error\n"); + e("mov eax, [eax]\n"); + e("rol eax, byte 16\n"); /* memory is byte swapped */ + + e("mov dword "SR", 0x2700\n"); /* in supervisor mode at start */ + e("mov dword [cycles], 0\n"); /* no cycles executed */ + e("mov dword [remaining], 0\n"); /* ..ditto.. */ + e("mov esi, eax\n"); /* from Turbo68KFetchPtr() */ + e("xor eax, eax\n"); /* clear registers */ + e("mov edi, d\n"); + e("mov ecx, 16\n"); + e("cld\n"); + e("rep stosd\n"); + e("mov edi, intr\n"); /* clear interrupt queue */ + e("mov ecx, 8\n"); + e("rep stosd\n"); + e("xor ecx, ecx\n"); /* no cycles executed in case error */ + UpdateFetchPtr(); /* this also makes sure it can be fetched */ + WRITEPCTOMEM("pc"); + + /* + * Read the SP vector from the fetch memory map + */ + if (!call_convention) /* stack calling convention */ + e("push dword 0\n"); + else /* register calling convention */ + e("xor eax, eax\n"); + e("call %sTurbo68KFetchPtr\n", id); + if (!call_convention) + e("add esp, byte 4\n"); + e("test eax, eax\n"); + e("jz near fetch_error\n"); + e("mov eax, [eax]\n"); + e("rol eax, byte 16\n"); /* memory is byte swapped */ + + e("mov "A7", eax\n"); /* supervisor */ + e("mov "SP", eax\n"); /* user */ + STOP_EMULATING(); /* no longer running */ + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("xor eax, eax\n"); + if (mmx) e("emms\n"); + e("ret\n"); + + EmitLabel("invalid_error"); /* invalid instruction */ + SaveCCR(); + STOP_EMULATING(); + STOP_RUNNING(); /* save PC and cycles remaining */ + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("mov eax, %d\n", TURBO68K_ERROR_INVINST); + e("ret\n"); + + /* + * NOTE: Only UpdateFetchPtr() and Turbo68KReset() can call this + */ + + EmitLabel("fetch_error"); /* could not fetch instruction */ + e("mov [remaining], ecx\n"); /* save cycles remaining */ + STOP_EMULATING(); + e("mov [pc], esi\n"); /* UpdateFetchPtr() didn't yet change this */ + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("mov eax, %d\n", TURBO68K_ERROR_FETCH); + e("ret\n"); + + /* Turbo68KReadPC() */ + + /* + * Can be used anywhere, but usage while Turbo68K is emulating may + * result in the value being offset a few bytes into the current + * instruction, but it shouldn't + */ + + Align(4); + EmitGlobalLabel("Turbo68KReadPC"); + e("mov eax, [pc]\n"); + e("ret\n"); + + /* Turbo68KSetFetch() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetFetch"); + e("push eax\n"); + e("push edx\n"); + GetArg("eax", 0, 12); + + e("sub eax, "SIZEOF_FETCHREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_fetch], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [fetch], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_fetch], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [fetch], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [fetch], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetFetch() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetFetch"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_fetch]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_fetch]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [fetch]\n"); + e("pop edx\n"); + e("add eax, "SIZEOF_FETCHREGION"\n"); + e("ret\n"); + + /* Turbo68KSetPCFetch() */ + + if (pcfetch) + { + Align(4); + EmitGlobalLabel("Turbo68KSetPCFetch"); + e("push eax\n"); + e("push edx\n"); + GetArg("eax", 0, 12); + e("sub eax, "SIZEOF_FETCHREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_pcfetch], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [pcfetch], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_pcfetch], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [pcfetch], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [pcfetch], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetPCFetch() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetPCFetch"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_pcfetch]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_pcfetch]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [pcfetch]\n"); + e("pop edx\n"); + e("add eax, "SIZEOF_FETCHREGION"\n"); + e("ret\n"); + } + + /* + * If the memory map mode is NOT 0 (defmap), we do not add or subtract + * from the pointers because they aren't arrays, but function pointers. + */ + + /* Turbo68KSetReadByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetReadByte"); + e("push eax\n"); + e("push ebx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_read_byte], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [read_byte], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_read_byte], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [read_byte], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [read_byte], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetReadByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetReadByte"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_read_byte]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_read_byte]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [read_byte]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KSetReadWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetReadWord"); + e("push eax\n"); + e("push ebx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_read_word], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [read_word], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_read_word], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [read_word], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [read_word], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetReadWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetReadWord"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_read_word]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_read_word]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [read_word]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KSetReadLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetReadLong"); + e("push eax\n"); + e("push ebx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_read_long], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [read_long], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_read_long], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [read_long], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [read_long], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetReadLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetReadLong"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_read_long]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_read_long]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [read_long]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KSetWriteByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetWriteByte"); + e("push eax\n"); + e("push edx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_write_byte], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [write_byte], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_write_byte], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [write_byte], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [write_byte], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetWriteByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetWriteByte"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_write_byte]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_write_byte]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [write_byte]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KSetWriteWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetWriteWord"); + e("push eax\n"); + e("push edx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_write_word], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [write_word], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_write_word], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [write_word], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [write_word], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetWriteWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetWriteWord"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_write_word]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_write_word]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [write_word]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KSetWriteLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KSetWriteLong"); + e("push eax\n"); + e("push edx\n"); + GetArg("eax", 0, 12); + if (memmap_type == 0) + e("sub eax, "SIZEOF_DATAREGION"\n"); + if (multiaddr) + { + GetArg("edx", 1, 16); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov [super_write_long], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ + e("jz near .not_super\n"); + e("mov [write_long], eax\n"); + EmitLabel(".not_super"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + + EmitLabel(".user"); + e("mov [user_write_long], eax\n"); + e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ + e("jnz near .not_user\n"); + e("mov [write_long], eax\n"); + EmitLabel(".not_user"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + else + { + e("mov [write_long], eax\n"); + e("pop edx\n"); + e("pop eax\n"); + e("ret\n"); + } + + /* Turbo68KGetWriteLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KGetWriteLong"); + e("push edx\n"); + if (multiaddr) + { + GetArg("edx", 0, 8); + e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); + e("jne short .user\n"); + e("mov eax, [super_write_long]\n"); + e("jmp near .finish\n"); + EmitLabel(".user"); + e("mov eax, [user_write_long]\n"); + EmitLabel(".finish"); + } + else + e("mov eax, [write_long]\n"); + if (memmap_type == 0) + e("add eax, "SIZEOF_DATAREGION"\n"); + e("pop edx\n"); + e("ret\n"); + + /* Turbo68KFetchPtr() */ + + Align(4); + EmitGlobalLabel("Turbo68KFetchPtr"); + + e("push ebx\n"); + e("push edx\n"); + e("push esi\n"); + e("push ebp\n"); + + GetArg("esi", 0, 20); /* address */ + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + AddrClip("esi"); + e("mov edx, [fetch]\n"); + e(".find_fetch_loop:\n"); + e("add edx, byte "SIZEOF_FETCHREGION"\n"); + e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */ + e("je short .not_found\n"); + e("cmp esi, [edx]\n"); /* offset 0: base. above it? */ + e("jb short .find_fetch_loop\n"); /* nope, not this region */ + e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ + e("ja short .find_fetch_loop\n"); /* nope, not this region */ + e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */ + e("add esi, ebp\n"); /* +pc=pc ptr.. */ + e("mov eax, esi\n"); + + e("pop ebp\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ebx\n"); + + e("ret\n"); + EmitLabel(".not_found"); + + e("pop ebp\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ebx\n"); + + e("xor eax, eax\n"); + e("ret\n"); + + /* Turbo68KReadByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KReadByte"); + e("push ebx\n"); + e("push ecx\n"); + e("push edx\n"); + e("push esi\n"); + e("push edi\n"); + e("push ebp\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 28); + ReadByte(); + e("mov eax, edx\n"); + if (mmx) e("emms\n"); + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("ret\n"); + + /* Turbo68KReadWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KReadWord"); + e("push ebx\n"); + e("push ecx\n"); + e("push edx\n"); + e("push esi\n"); + e("push edi\n"); + e("push ebp\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 28); + ReadWord(); + e("mov eax, edx\n"); + if (mmx) e("emms\n"); + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("ret\n"); + + /* Turbo68KReadLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KReadLong"); + e("push ebx\n"); + e("push ecx\n"); + e("push edx\n"); + e("push esi\n"); + e("push edi\n"); + e("push ebp\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 28); + ReadLong(); + e("mov eax, edx\n"); + if (mmx) e("emms\n"); + e("pop ebp\n"); + e("pop edi\n"); + e("pop esi\n"); + e("pop edx\n"); + e("pop ecx\n"); + e("pop ebx\n"); + e("ret\n"); + + /* Turbo68KWriteByte() */ + + Align(4); + EmitGlobalLabel("Turbo68KWriteByte"); + e("pusha\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 36); + GetArg("edx", 1, 40); + WriteByte(); + if (mmx) e("emms\n"); + e("popa\n"); + e("ret\n"); + + /* Turbo68KWriteWord() */ + + Align(4); + EmitGlobalLabel("Turbo68KWriteWord"); + e("pusha\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 36); + GetArg("edx", 1, 40); + WriteWord(); + if (mmx) e("emms\n"); + e("popa\n"); + e("ret\n"); + + /* Turbo68KWriteLong() */ + + Align(4); + EmitGlobalLabel("Turbo68KWriteLong"); + e("pusha\n"); + e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ + e("mov esi, [pc]\n"); + if (multiaddr) + { + e("test byte [sr+1], 0x20\n"); /* set proper address space */ + e("jz short .user\n"); + SetSupervisorAddressSpace(); + e("jmp short .continue\n"); + EmitLabel(".user"); + SetUserAddressSpace(); + EmitLabel(".continue"); + } + GetArg("ebx", 0, 36); + GetArg("edx", 1, 40); + WriteLong(); + if (mmx) e("emms\n"); + e("popa\n"); + e("ret\n"); + + /* Turbo68KSet/GetContext() */ + + /* + * Notice the trick here, all of the memory map pointers have 16 + * subtracted from them, this helps optimize the read/write handlers + * since an "add edi, 16" is possible straight off. This was NB's + * idea. The fetch area is also handled this way + */ + + Align(4); + EmitGlobalLabel("Turbo68KSetContext"); + e("push ecx\n"); + e("push esi\n"); + e("push edi\n"); + GetArg("esi", 0, 16); + e("mov edi, %s%s\n", id, NameOfContext()); + if (mmx && !(SizeOfContext() & 7)) + { + e("mov ecx, " SIZEOF_CONTEXT " / 8\n"); + EmitLabel(".l"); + e("movq mm6, [esi]\n"); + e("movq [edi], mm6\n"); + e("add esi, byte 8\n"); + e("add edi, byte 8\n"); + e("dec ecx\n"); + e("jnz short .l\n"); + e("emms\n"); + } + else + { + e("mov ecx, " SIZEOF_CONTEXT " / 4\n"); + e("cld\n"); + e("rep movsd\n"); + } + + e("sub dword [fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); + if (memmap_type == 0) + { + e("sub dword [read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_long], "SIZEOF_DATAREGION"\n"); + + e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n"); + + e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n"); + } + e("pop edi\n"); + e("pop esi\n"); + e("pop ecx\n"); + e("ret\n"); + + Align(4); + EmitGlobalLabel("Turbo68KGetContext"); + e("push ecx\n"); + e("push esi\n"); + e("push edi\n"); + + e("add dword [fetch], "SIZEOF_FETCHREGION"\n"); + e("add dword [pcfetch], "SIZEOF_FETCHREGION"\n"); + e("add dword [super_fetch], "SIZEOF_FETCHREGION"\n"); + e("add dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); + e("add dword [user_fetch], "SIZEOF_FETCHREGION"\n"); + e("add dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); + if (memmap_type == 0) + { + e("add dword [read_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [read_word], "SIZEOF_DATAREGION"\n"); + e("add dword [read_long], "SIZEOF_DATAREGION"\n"); + e("add dword [write_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [write_word], "SIZEOF_DATAREGION"\n"); + e("add dword [write_long], "SIZEOF_DATAREGION"\n"); + + e("add dword [super_read_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [super_read_word], "SIZEOF_DATAREGION"\n"); + e("add dword [super_read_long], "SIZEOF_DATAREGION"\n"); + e("add dword [super_write_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [super_write_word], "SIZEOF_DATAREGION"\n"); + e("add dword [super_write_long], "SIZEOF_DATAREGION"\n"); + + e("add dword [user_read_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [user_read_word], "SIZEOF_DATAREGION"\n"); + e("add dword [user_read_long], "SIZEOF_DATAREGION"\n"); + e("add dword [user_write_byte], "SIZEOF_DATAREGION"\n"); + e("add dword [user_write_word], "SIZEOF_DATAREGION"\n"); + e("add dword [user_write_long], "SIZEOF_DATAREGION"\n"); + } + GetArg("edi", 0, 16); + e("mov esi, %s%s\n", id, NameOfContext()); + if (mmx && !(SizeOfContext() & 7)) + { + e("mov ecx, " SIZEOF_CONTEXT " / 8\n"); + EmitLabel(".l"); + e("movq mm6, [esi]\n"); + e("movq [edi], mm6\n"); + e("add esi, byte 8\n"); + e("add edi, byte 8\n"); + e("dec ecx\n"); + e("jnz short .l\n"); + e("emms\n"); + } + else + { + e("mov ecx, " SIZEOF_CONTEXT " / 4\n"); + e("cld\n"); + e("rep movsd\n"); + } + + e("sub dword [fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n"); + e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); + if (memmap_type == 0) + { + e("sub dword [read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [write_long], "SIZEOF_DATAREGION"\n"); + + e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n"); + + e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n"); + e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n"); + } + e("pop edi\n"); + e("pop esi\n"); + e("pop ecx\n"); + e("ret\n"); + + /* Turbo68KGetContextSize() */ + EmitGlobalLabel("Turbo68KGetContextSize"); + e("mov eax, "SIZEOF_CONTEXT"\n"); + e("ret\n"); + + /* Turbo68KClearCycles */ + EmitGlobalLabel("Turbo68KClearCycles"); + e("mov dword [cycles], 0\n"); + e("mov dword [remaining], 0\n"); + e("ret\n"); + + /* Turbo68KFreeTimeSlice */ + EmitGlobalLabel("Turbo68KFreeTimeSlice"); + e("push eax\n"); + e("mov eax, [cycles]\n"); + e("sub eax, [remaining]\n"); + e("mov [cycles], eax\n"); + e("mov dword [remaining], 0\n"); + e("pop eax\n"); + e("ret\n"); + + /* Turbo68KGetElapsedCycles */ + Align(4); + EmitGlobalLabel("Turbo68KGetElapsedCycles"); + e("mov eax, [cycles]\n"); + e("sub eax, [remaining]\n"); + e("ret\n"); + + /* Turbo68KProcessInterrupts() */ + + Align(4); + EmitGlobalLabel("Turbo68KProcessInterrupts"); + e("pusha\n"); + e("test byte [status], 1\n"); + e("jnz near .end\n"); /* does not work while running */ + e("mov ecx, [remaining]\n"); + EMULATING(); /* running=1 */ + e("mov esi, [pc]\n"); /* fetch PC */ + UpdateFetchPtr(); + e("call ProcessInterrupts\n"); + STOP_RUNNING(); /* save PC and cycles remaining */ + STOP_EMULATING(); + EmitLabel(".end"); + if (mmx) e("emms\n"); + e("popa\n"); + e("xor eax, eax\n"); + e("ret\n"); + + /* Turbo68KInterrupt() */ + + /* + * Note: This function assumes that TURBO68K_AUTOVECTOR == 256, make + * sure the definition in TURBO68K.H reflects this + * + * If 68K is halted (STOP instruction), if an interrupt with a priority + * equal to or lower than the SR priority is requested, is the interrupt + * made pending or discarded? (ask) Are there any situations when invalid- + * priority interrupts are discarded completely? + */ + + Align(4); + EmitGlobalLabel("Turbo68KInterrupt"); + e("push\tebx\n"); + e("push\tecx\n"); + e("push\tedx\n"); + e("push\tesi\n"); + e("push\tedi\n"); + + GetArg("eax", 0, 24); + + e("test eax, eax\n"); /* 0 is not a valid level */ + e("jz short .inv_level\n"); + e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */ + e("ja short .inv_level\n"); + + GetArg("ebx", 1, 28); /* get vector */ + e("cmp ebx, byte 2\n"); + e("jb short .inv_vector\n"); + e("cmp ebx, 256\n"); + e("ja short .inv_vector\n"); + e("jne short .not_auto\n"); + e("mov ebx, eax\n"); + e("add ebx, byte 24\n"); + EmitLabel(".not_auto"); + e("dec eax\n"); /* (level-1)*4=offset into intr[] */ + e("shl eax, byte 2\n"); + e("cmp [intr+eax], byte 0\n"); + e("jne short .pending\n"); /* already pending */ + e("mov [intr+eax], ebx\n"); /* store vector into intr[] */ + e("inc dword [intr+7*4]\n"); /* one interrupt added */ + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("xor eax, eax\n"); + e("ret\n"); + + EmitLabel(".inv_level"); + e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL); + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("ret\n"); + EmitLabel(".inv_vector"); + e("mov eax, %d\n", TURBO68K_ERROR_INTVECTOR); + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("ret\n"); + EmitLabel(".pending"); + e("mov eax, %d\n", TURBO68K_ERROR_INTPENDING); + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("ret\n"); + + /* Turbo68KCancelInterrupt */ + + EmitGlobalLabel("Turbo68KCancelInterrupt"); + e("push\tebx\n"); + e("push\tecx\n"); + e("push\tedx\n"); + e("push\tesi\n"); + e("push\tedi\n"); + GetArg("eax", 0, 24); + e("test eax, eax\n"); /* 0 is not a valid level */ + e("jz short .inv_level\n"); + e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */ + e("ja short .inv_level\n"); + e("test byte [status], 0x04\n"); /* processing interrupts? */ + e("jnz short .busy\n"); /* yep... can't do this */ + e("dec eax\n"); /* make index into intr[] */ + e("cmp dword [intr+eax*4], byte 0\n"); + e("je .nothing_to_cancel\n"); + e("mov dword [intr+eax*4], 0\n"); + e("dec dword [intr+7*4]\n"); /* removed 1 interrupt */ + EmitLabel(".busy"); + EmitLabel(".nothing_to_cancel"); + + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("xor eax, eax\n"); + e("ret\n"); + EmitLabel(".inv_level"); + e("pop\tedi\n"); + e("pop\tesi\n"); + e("pop\tedx\n"); + e("pop\tecx\n"); + e("pop\tebx\n"); + e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL); + e("ret\n"); + + /* + * ProcessInterrupts: Processes any pending interrupts if allowed. + * Assumes all registers have been set up as per Turbo68KRun + */ + + Align(4); + EmitLabel("ProcessInterrupts"); + INTERRUPT_PROCESSING(); + e("cmp dword [intr+7*4], byte 0\n"); + e("je near .no_int_end\n"); /* no interrupts pending */ + e("push eax\n"); /* EAX=flags, save it! */ + e("mov al, [sr+1]\n"); + e("and eax, byte 7\n"); /* EAX=interrupt priority */ + e("cmp al, 7\n"); + e("jne short .no_int7\n"); + e("mov edi, intr+6*4\n"); + e("jmp short .l\n"); + EmitLabel(".no_int7"); + e("mov edi, eax\n"); + e("shl edi, byte 2\n"); /* *4, index into intr[] */ + e("add edi, intr\n"); /* EDI=interrupt queue */ + e("inc al\n"); /* new SR priority mask */ + EmitLabel(".l"); + e("cmp dword [edi], byte 0\n"); + e("je near .skip\n"); /* no interrupt at this level */ + + e("test byte [sr+1], 0x20\n"); /* see if in User mode */ + e("jnz short .no_sp_magic\n"); /* nope, don't need to swap SPs */ + e("mov edx, [__sp]\n"); + e("xchg [a+7*4], edx\n"); + e("mov [__sp], edx\n"); + SetSupervisorAddressSpace(); + EmitLabel(".no_sp_magic"); + + e("mov ebx, [a+7*4]\n"); /* SP */ + /* + * If 68010, we have to push the format word on the stack. + */ + if (mpu == 68010) + { + e("mov edx, [edi]\n"); /* vector number */ + e("sub ebx, byte 2\n");/* SP-2 */ + e("shl edx, byte 2\n");/* vector*4 for offset */ + e("push ebx\n"); + e("push edi\n"); + WriteWord(); + e("pop edi\n"); + e("pop ebx\n"); + } + e("mov edx, [pc]\n"); /* get old PC */ + e("sub ebx, byte 4\n"); /* SP-4 */ + e("push ebx\n"); + e("push edi\n"); + WriteLong(); + e("pop edi\n"); + e("pop ebx\n"); + e("mov edx, [sr]\n"); /* get SR */ + e("sub ebx, byte 2\n"); /* SP-2 */ + e("push edi\n"); + e("push ebx\n"); + WriteWord(); /* save SR to stack */ + e("pop ebx\n"); + e("pop edi\n"); + e("mov dh, al\n"); /* new interrupt priority bits */ + e("or dh, 0x20\n"); /* set supervisor for exception */ + e("and edx, 0xa71f\n"); /* clear unwanted bits */ + e("mov [sr], edx\n"); /* set new SR */ + e("mov [a+7*4], ebx\n"); /* write back SP */ + e("mov ebx, [edi]\n"); /* vector of interrupt */ + + e("test dword [InterruptAcknowledge], -1\n"); /* interrupt call-back */ + e("jz short .no_interrupt_ack_handler\n"); + e("pushad\n"); + e("push ebx\n"); + e("call dword [InterruptAcknowledge]\n"); + e("add esp, byte 4\n"); + e("popad\n"); + e(".no_interrupt_ack_handler:\n"); + + e("shl ebx, byte 2\n"); /* vector *= 4 */ + if (mpu == 68010) + e("add ebx, [vbr]\n"); + e("push edi\n"); + ReadLong(); /* get PC */ + e("pop edi\n"); + e("mov [pc], edx\n"); /* set new PC */ + e("mov dword [edi], 0\n"); /* done w/ this interrupt */ + e("dec dword [intr+7*4]\n"); /* processed 1 interrupt... */ + e("sub ecx, byte 44\n"); /* interrupts take 44 clocks */ + UNSTOP_CPU(); + EmitLabel(".skip"); + e("add edi, byte 4\n"); + e("inc al\n"); /* next entry */ + e("cmp edi, intr+7*4\n"); + e("jne near .l\n"); + e("mov esi, [pc]\n"); + e("pop eax\n"); /* get flags back */ + UpdateFetchPtr(); + EmitLabel(".no_int_end"); + INTERRUPT_DONE(); + e("ret\n"); + + + /* Turbo68KRun() */ + + Align(4); + EmitGlobalLabel("Turbo68KRun"); + e("pusha\n"); + GetArg("ecx", 0, 36); /* ECX = cycles */ + e("mov [cycles], ecx\n"); + e("mov [remaining], ecx\n"); + e("test byte [sr+1], 0x20\n"); + e("jz near .set_user_space\n"); + SetSupervisorAddressSpace(); + e("jmp near .continue\n"); + EmitLabel(".set_user_space"); + SetUserAddressSpace(); + EmitLabel(".continue"); + EMULATING(); /* running=1 */ + e("mov esi, [pc]\n"); /* fetch PC */ + UpdateFetchPtr(); + LoadCCR(); /* get flags */ + e("call ProcessInterrupts\n"); + e("test byte [status], 2\n"); /* stopped? */ + e("jnz short .stopped\n"); + e("xor edi, edi\n"); + e("mov di, [esi]\n"); /* next instruction */ + e("add esi, byte 2\n"); + e("jmp dword [jmptab+edi*4]\n"); /* jump... */ + EmitLabel(".stopped"); + e("xor ecx, ecx\n"); + e("jmp Turbo68KRun_done\n"); + + Align(4); + EmitLabel("Turbo68KRun_done"); + SaveCCR(); /* save flags */ + STOP_RUNNING(); /* save PC and cycles */ + STOP_EMULATING(); + if (mmx) e("emms\n"); + e("popa\n"); + e("xor eax, eax\n"); /* everything okay... */ + e("ret\n"); + + /* + * NOTE: Only the 68010 RTE handler can call this. PC is assumed to point + * one word after the faulty RTE. + */ + + if (mpu == 68010) + { + EmitLabel("stackframe_error"); + e("sub esi, byte 2\n"); /* point at faulty RTE */ + SaveCCR(); + STOP_EMULATING(); + STOP_RUNNING(); /* save PC and cycles remaining */ + e("popa\n"); + e("mov eax, %d\n", TURBO68K_ERROR_STACKFRAME); + e("ret\n"); + } + + + if (memmap_type == 2) /* low level handling? don't generate the rest */ + return; + + /* + * ReadXXXXPC: EBX = address + * Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word, + * EDX=long-word + * EBX=address + * Notes: EDI is trashed in the process, but cleared at the end. + * These functions read from the *pcfetch areas. There are no + * SX functions, this is handled by the emitter. I did this to + * save space. + */ + + if (pcfetch) + { + for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ + { + CacheAlign(); + switch (i) + { + case 0: + EmitLabel("ReadBytePC"); + break; + case 1: + EmitLabel("ReadWordPC"); + break; + case 2: + EmitLabel("ReadLongPC"); + break; + } + e("mov edi, [pcfetch]\n"); + SaveReg("memhandler", "ebx"); /* save address */ + AddrClip("ebx"); + EmitLabel(".loop"); + e("add edi, byte "SIZEOF_FETCHREGION"\n"); + e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ + e("je short .not_found\n"); + e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ + e("jb short .loop\n"); /* nope, not this region */ + e("cmp ebx, [edi+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ + e("ja short .loop\n"); /* nope, not this region */ + e("mov edx, ebx\n"); + if (!i) /* byte accesses need this because buffer is swapped */ + e("xor dl, 1\n"); + e("add edx, [edi+"OFFSET_FETCH_PTR"]\n"); /* into ptr.. */ + if (!i) /* byte */ + e("mov dl, [edx]\n"); /* fetch */ + else if (i == 1) /* word */ + e("mov edx, [edx]\n"); + else /* long */ + { + e("mov edx, [edx]\n"); + e("rol edx, byte 16\n"); /* swap words */ + } + e("xor edi, edi\n"); + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".not_found"); + e("xor edi, edi\n"); + e("xor edx, edx\n"); + e("dec edx\n"); /* not found, return all 1s */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + } + } + + /* + * ReadXXXX: EBX = address + * Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word, + * EDX=long-word + * EBX=address + * Notes: EDI is trashed in the process, but cleared at the end. + * This applies to all ReadXXX and WriteXXX handlers, some + * instructions (NEGX) rely on this behavior. + */ + + if (memmap_type == 0) /* default memory mapping system */ + { + for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ + { + CacheAlign(); + switch (i) + { + case 0: + EmitLabel("ReadByte"); + e("mov edi, [read_byte]\n"); + break; + case 1: + EmitLabel("ReadWord"); + e("mov edi, [read_word]\n"); + break; + case 2: + EmitLabel("ReadLong"); + e("mov edi, [read_long]\n"); + break; + } + SaveReg("memhandler", "ebx"); /* save address */ + AddrClip("ebx"); + EmitLabel(".loop"); + e("add edi, byte "SIZEOF_DATAREGION"\n"); + e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ + e("je short .not_found\n"); + e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ + e("jb short .loop\n"); /* nope, not this region */ + e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */ + e("ja short .loop\n"); /* nope, not this region */ + e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); + e("je short .read_from_handler\n"); + e("mov edx, ebx\n"); + if (!i) /* byte accesses need this because buffer is swapped */ + e("xor dl, 1\n"); + e("add edx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */ + if (!i) /* byte */ + e("mov dl, [edx]\n"); /* fetch */ + else if (i == 1) /* word */ + e("mov edx, [edx]\n"); + else /* long */ + { + e("mov edx, [edx]\n"); + e("rol edx, byte 16\n"); /* swap words */ + } + e("xor edi, edi\n"); + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".not_found"); + e("xor edi, edi\n"); + e("xor edx, edx\n"); + e("dec edx\n"); /* not found, return all 1s */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".read_from_handler"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + + /* address */ + if (!call_convention) /* stack-based */ + e("push ebx\n"); + else /* register-based */ + e("mov eax, ebx\n"); + + e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); + + if (!call_convention) + e("add esp, byte 4\n"); + + e("mov edx, eax\n"); /* get the data */ + e("mov esi, [pc]\n"); /* handler could have changed PC */ + e("mov ecx, [remaining]\n");/* this could have been changed */ + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + } + } + else if (memmap_type == 1) /* high level handler */ + { + for (i = 0; i < 3; i++) + { + CacheAlign(); + switch (i) + { + case 0: EmitLabel("ReadByte"); break; + case 1: EmitLabel("ReadWord"); break; + case 2: EmitLabel("ReadLong"); break; + } + SaveReg("memhandler", "ebx"); /* save address */ + AddrClip("ebx"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + + /* address */ + if (!call_convention) /* stack-based */ + e("push ebx\n"); + else /* register-based */ + e("mov eax, ebx\n"); + + switch (i) + { + case 0: e("call dword [read_byte]\n"); break; + case 1: e("call dword [read_word]\n"); break; + case 2: e("call dword [read_long]\n"); break; + } + + if (!call_convention) + e("add esp, byte 4\n"); + + e("mov edx, eax\n"); /* get the data */ + e("mov esi, [pc]\n"); /* handler could have changed PC */ + e("mov ecx, [remaining]\n");/* this could have been changed */ + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + } + } + + if (memmap_type == 0) + { + /* + * ReadWordSX is the same as ReadWord except that it sign extends the + * word to 32 bits. + */ + + CacheAlign(); + EmitLabel("ReadWordSX"); + e("mov edi, [read_word]\n"); + SaveReg("memhandler", "ebx"); /* save address */ + AddrClip("ebx"); + EmitLabel(".loop"); + e("add edi, byte "SIZEOF_DATAREGION"\n"); + e("cmp dword [edi], byte -1\n"); + e("je short .not_found\n"); + e("cmp ebx, [edi]\n"); + e("jb short .loop\n"); + e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); + e("ja short .loop\n"); + e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); + e("je short .read_from_handler\n"); + e("mov edx, ebx\n"); + e("add edx, [edi+"OFFSET_DATA_PTR"]\n"); + e("movsx edx, word [edx]\n"); /* sign extend! */ + e("xor edi, edi\n"); + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".not_found"); + e("xor edi, edi\n"); + e("xor edx, edx\n"); + e("dec edx\n"); /* not found, return all 1s */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".read_from_handler"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); + e("mov [remaining], ecx\n"); + + /* address */ + if (!call_convention) /* stack-based */ + e("push ebx\n"); + else /* register-based */ + e("mov eax, ebx\n"); + + e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); + + if (!call_convention) + e("add esp, byte 4\n"); + + e("movsx edx, ax\n"); /* sign extend! */ + e("mov esi, [pc]\n"); + e("mov ecx, [remaining]\n"); + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); + e("xor edi, edi\n"); + RestoreReg("memhandler", "ebx"); + e("ret\n"); + } + else if (memmap_type == 1) + { + CacheAlign(); + EmitLabel("ReadWordSX"); + SaveReg("memhandler", "ebx"); /* save address */ + AddrClip("ebx"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + + /* address */ + if (!call_convention) /* stack-based */ + e("push ebx\n"); + else /* register-based */ + e("mov eax, ebx\n"); + + e("call dword [read_word]\n"); + + if (!call_convention) + e("add esp, byte 4\n"); + + e("movsx edx, ax\n"); /* sign extend */ + + e("mov esi, [pc]\n"); /* handler could have changed PC */ + e("mov ecx, [remaining]\n");/* this could have been changed */ + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + RestoreReg("memhandler", "ebx"); + e("ret\n"); + } + + /* + * WriteXXXX: EBX = address + * In: EDX = data; DL=byte, EDX{DX}=word, + * EDX=long-word + * Out: EBX=trashed, EDX=preserved + * Notes: EDI is trashed in the process, but cleared at the end + */ + + if (memmap_type == 0) + { + for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ + { + CacheAlign(); + switch (i) + { + case 0: + EmitLabel("WriteByte"); + e("mov edi, [write_byte]\n"); + break; + case 1: + EmitLabel("WriteWord"); + e("mov edi, [write_word]\n"); + break; + case 2: + EmitLabel("WriteLong"); + e("mov edi, [write_long]\n"); + break; + } + AddrClip("ebx"); + EmitLabel(".loop"); + e("add edi, byte "SIZEOF_DATAREGION"\n"); + e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ + e("je short .not_found\n"); + e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ + e("jb short .loop\n"); /* nope, not this region */ + e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */ + e("ja short .loop\n"); /* nope, not this region */ + e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); + e("je short .write_with_handler\n"); + if (!i) /* byte accesses need this because buffer is swapped */ + e("xor bl, 1\n"); + e("add ebx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */ + if (!i) /* byte */ + e("mov [ebx], dl\n"); /* write */ + else if (i == 1) /* word */ + e("mov [ebx], dx\n"); + else /* long */ + { + e("ror edx, byte 16\n"); /* swap words */ + e("mov [ebx], edx\n"); + e("ror edx, byte 16\n"); /* reswap back */ + } + EmitLabel(".not_found"); + e("xor edi, edi\n"); + RestoreReg("memhandler", "ebx"); + e("ret\n"); + Align(4); + EmitLabel(".write_with_handler"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + SaveReg("memhandler", "edx"); /* save params */ + + /* data, address */ + if (!call_convention) /* stack-based */ + { + e("push edx\n"); + e("push ebx\n"); + } + else /* register-based */ + { + e("mov eax, ebx\n"); + /* don't need: e("mov edx, edx\n"); */ + } + + e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); + + if (!call_convention) + e("add esp, byte 8\n"); + + RestoreReg("memhandler", "edx"); /* restore params */ + e("mov esi, [pc]\n"); + e("mov ecx, [remaining]\n"); /* this could have been changed */ + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + e("ret\n"); + } + } + else if (memmap_type == 1) + { + for (i = 0; i < 3; i++) + { + CacheAlign(); + switch (i) + { + case 0: EmitLabel("WriteByte"); break; + case 1: EmitLabel("WriteWord"); break; + case 2: EmitLabel("WriteLong"); break; + } + AddrClip("ebx"); + SaveReg("memhandler", "eax"); + SaveReg("memhandler", "ebp"); + WRITEPCTOMEM("pc"); /* saves esi */ + e("mov [remaining], ecx\n"); + SaveReg("memhandler", "edx"); /* save params */ + + /* data, address */ + if (!call_convention) /* stack-based */ + { + e("push edx\n"); + e("push ebx\n"); + } + else /* register-based */ + { + e("mov eax, ebx\n"); + /* don't need: e("mov edx, edx\n"); */ + } + + switch (i) + { + case 0: e("call dword [write_byte]\n"); break; + case 1: e("call dword [write_word]\n"); break; + case 2: e("call dword [write_long]\n"); break; + } + + if (!call_convention) + e("add esp, byte 8\n"); + + RestoreReg("memhandler", "edx"); /* restore params */ + e("mov esi, [pc]\n"); + e("mov ecx, [remaining]\n"); /* this could have been changed */ + RestoreReg("memhandler", "eax"); + RestoreReg("memhandler", "ebp"); + e("add esi, ebp\n"); /* base+pc=pc pointer */ + e("xor edi, edi\n"); /* keep clear for fetch */ + e("ret\n"); + } + } +} + + +/***************************************************************************** +* main() and Friends */ + +int FindV(char *option, int p, int h, int u, int argc, char **argv, int m) +{ + static int t[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int i; + char *c; + + if (m) /* mark as touched */ + { + if (m < 128) + t[m] = 1; + return 0; + } + if (argc > 128) + argc = 128; /* maximum this function can handle is 128 */ + + if (u) /* find first untouched element */ + { + for (i = 1; i < argc; i++) + { + if (!t[i]) /* 0 indicates untouched */ + return i; + } + return 0; + } + + if (p) /* find option and return integer value following it */ + { + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], option) == 0) /* found */ + { + if (i >= (argc - 1)) /* bounds! */ + return 0; + t[i + 1] = t[i] = 1; /* touched */ + if (!h) + return atoi(argv[i + 1]); + else + return strtoul(argv[i + 1], &c, 16); + } + } + return 0; /* no match */ + } + else /* find option and return position */ + { + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], option) == 0) + { + t[i] = 1; + return i; /* found! return position */ + } + } + return 0; + } + return 0; +} + +void ShowHelp() +{ + printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n"); + printf("Usage: make68k [options]\n"); + printf("Options: -?,-h Show this help text\n"); + printf(" -mpu Processor to emulate [Default=68000]\n"); + printf(" -addr Address bus width [Default=24]\n"); + printf(" -illegal Emulate illegal instruction exceptions [Default]\n"); + printf(" -noillegal Treat illegal instructions as errors\n"); + printf(" -dummyread Emulate 68000 dummy reads [Default]\n"); + printf(" -nodummyread Do not emulate dummy reads\n"); + printf(" -skip Skip over idle loops\n"); + printf(" -noskip Do not skip over idle loops [Default]\n"); + printf(" -brafetch Update fetch pointer on Bcc, BRA, BSR, DBcc\n"); + printf(" -nobrafetch Do not update fetch pointer for branches [Default]\n"); + printf(" -pcfetch Special PC-relative read mode\n"); + printf(" -nopcfetch Use data regions for PC-relative reading [Default]\n"); + printf(" -multiaddr Address spaces for supervisor and user [Default]\n"); + printf(" -singleaddr Single address space for supervisor and user\n"); + printf(" -defmap Definable memory map arrays [Default]\n"); + printf(" -handler Single handlers for memory access\n"); + printf(" -stackcall Stack calling conventions [Default]\n"); + printf(" -regcall Register calling conventions\n"); + printf(" -id Add string to beginning of identifiers\n"); + exit(0); +} + +void SetAddrMask(int num_bits) +{ + addr_bits = num_bits; + if (!num_bits) + { + addr_mask = 0xffffff; /* 24-bit is default address bus width */ + addr_bits = 24; + } + else + addr_mask = (unsigned) pow(2, num_bits) - 1; +} + +int main(int argc, char **argv) +{ + int i, j, k, l, f; + +#ifdef PROFILE + for (i = 0; i < 512; i++) prof[i] = NULL; +#endif + + if (argc <= 1 || FindV("-?", 0, 0, 0, argc, argv, 0) || FindV("-h", 0, 0, 0, argc, argv, 0)) + ShowHelp(); + + mpu = FindV("-mpu", 1, 0, 0, argc, argv, 0); + switch (mpu) /* make sure a valid MPU type was specified */ + { + case 0: + mpu = 68000; + break; + case 68000: + case 68010: + break; + default: + fprintf(stderr, "Make68K: Unknown or unsupported processor type: %d\n", mpu); + exit(1); + } + SetAddrMask(FindV("-addr", 1, 0, 0, argc, argv, 0)); + if (FindV("-illegal", 0, 0, 0, argc, argv, 0)) illegal = 1; + if (FindV("-noillegal", 0, 0, 0, argc, argv, 0)) illegal = 0; + if (FindV("-dummyread", 0, 0, 0, argc, argv, 0)) dummyread = 1; + if (FindV("-nodummyread", 0, 0, 0, argc, argv, 0)) dummyread = 0; + if (FindV("-skip", 0, 0, 0, argc, argv, 0)) skip = 1; + if (FindV("-noskip", 0, 0, 0, argc, argv, 0)) skip = 0; + if (FindV("-brafetch", 0, 0, 0, argc, argv, 0)) brafetch = 1; + if (FindV("-nobrafetch", 0, 0, 0, argc, argv, 0)) brafetch = 0; + if (FindV("-pcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 1; + if (FindV("-nopcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 0; + if (FindV("-multiaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 1; + if (FindV("-singleaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 0; + if (FindV("-defmap", 0, 0, 0, argc, argv, 0)) memmap_type = 0; + if (FindV("-handler", 0, 0, 0, argc, argv, 0)) memmap_type = 1; + if (FindV("-stackcall", 0, 0, 0, argc, argv, 0)) call_convention = 0; + if (FindV("-regcall", 0, 0, 0, argc, argv, 0)) call_convention = 1; + if (FindV("-debug", 0, 0, 0, argc, argv, 0)) debug = 1; + + if ((i = FindV("-id", 0, 0, 0, argc, argv, 0))) + { + if ((i + 1) < argc) /* make sure we're within argv[] bounds */ + { + if (strlen(argv[i + 1]) > 16) + { + fprintf(stderr, "Make68K: Identifier string is too long (maximum is 16 characters)\n"); + exit(1); + } + strncpy(id, argv[i + 1], 16); + FindV(NULL, 0, 0, 0, argc, argv, i + 1); + } + } + + if (!(f = FindV(NULL, 0, 0, 1, argc, argv, 0))) + { + fprintf(stderr, "Make68K: No output file specified\n"); + exit(1); + } + if ((fp = fopen(argv[f], "w")) == NULL) + { + fprintf(stderr, "Make68K: Failed to open file for writing: %s\n", argv[f]); + exit(1); + } + + for (i = 0; i < 65536; i++) decoded[i] = 0; + + printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n"); + printf("\n"); + printf("Emitting file: %s\n", argv[f]); + printf("Configuration:\n"); + + e(";\n"); + e("; Turbo68K Version "VERSION": Motorola 680X0 emulator\n"); + e("; Copyright 2000-2002 Bart Trzynadlowski, see \"README.TXT\" for terms of use\n"); + e("; Assemble with NASM (http://www.web-sites.co.uk/nasm) only\n"); + e(";\n"); + e("; Configuration:\n"); + printf("- %d processor\n", mpu); + e("; - %d processor\n", mpu); + printf("- %d-bit addresses\n", addr_bits); + e("; - %d-bit addresses\n", addr_bits); + printf("- %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors"); + e("; - %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors"); + if (mpu == 68000) + { + printf("- Dummy reads %s\n", dummyread ? "emulated" : "not emulated"); + e("; - Dummy reads %s\n", dummyread ? "emulated" : "not emulated"); + } + printf("- Idle loop skipping %s\n", skip ? "enabled" : "disabled"); + e("; - Idle loop skipping %s\n", skip ? "enabled" : "disabled"); + printf("- Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not "); + e("; - Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not "); + printf("- %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data"); + e("; - %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data"); + printf("- %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space"); + e("; - %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space"); + switch (memmap_type) + { + case 0: printf("- Definable memory map arrays\n"); + e("; - Definable memory map arrays\n"); + break; + case 1: printf("- Single memory handlers\n"); + e("; - Single memory handlers\n"); + break; + case 2: printf("- Low-level memory handlers\n"); + e("; - Low-level memory handlers\n"); + break; + } + printf("- %s calling conventions\n", call_convention ? "Register" : "Stack"); + e("; - %s calling conventions\n", call_convention ? "Register" : "Stack"); + if (id[0] != '\0') + { + printf("- Identifers start with: %s\n", id); + e("; - Identifiers start with: %s\n", id); + } + if (debug) + { + printf("- Debug function called at every instruction\n"); + e("; - Debug function called at every instruction\n"); + } + e(";\n"); + printf("\n"); + e("bits 32\n"); + e("section .data\n"); + EmitData(); + e("section .text\n"); + EmitCode(); + printf("Generating instruction handlers:\n"); + EmitInstructions(); + EmitExceptions(); + e("section .bss\n"); + CacheAlign(); + e("jmptab resd 65536\n"); + + + /* + * Emit the compressed jump table + * + * Format: + * dw 0x8000 + # ; # of handler addresses follow, each repeated 8 times + * ... dd # ... ; handler addresses... + * dw 0xc000 + # ; # of handler addresses follow, each repeated 1 time + * ... dd # ... ; handler addresses... + * dw 0x0000 + # ; repeat following address handler # times + * dd # ; handler address to repeat + * dd -1 ; terminator + */ + + e("section .data\n"); + Align(4); + e("compressed_jmptab:\n"); + + i = 0; + j = 0; + while (i < 65536) + { + if (!decoded[i]) /* invalid */ + { + for (i = i; decoded[i] == 0 && i < 65536; i++) j++; + e("dw 0x0000 + %d\n", j); + e("dd I_Invalid\n"); + j = 0; + } + else + { + if (decoded[i] == 8) /* if more handlers*8, make a big block */ + { + k = 0; + l = i; /* save opcode # */ + while (decoded[i] == 8) + { + i += decoded[i]; + k++; + } + e("dw 0x8000 + %d\n", k); /* # of handlers w/ 8 reps */ + while (k != 0) /* list handlers */ + { + e("dd I%04X\n", l); + l += decoded[l]; + k--; + } + } + if (decoded[i] == 1) /* if more handlers*1, make a big block */ + { + k = 0; + l = i; /* save opcode # */ + while (decoded[i] == 1) + { + i += decoded[i]; + k++; + } + e("dw 0xc000 + %d\n", k); /* # of handlers w/ 1 reps */ + while (k != 0) /* list handlers */ + { + e("dd I%04X\n", l); + l += decoded[l]; + k--; + } + } + else if (decoded[i] == 0) /* invalid instruction */ + { + e("dw 1\n"); + e("dd I_Invalid\n"); + i++; + } + else /* misc. */ + { + e("dw %d\n", decoded[i]); /* # of valids */ + e("dd I%04X\n", i); /* opcode */ + i += decoded[i]; + } + } + } + e("dd -1\n"); + + /* + * Emit profile data + * + * Step 1: Output the names of the instructions. + * Step 2: Output the usage count variables and pointers to the strings. + */ + +#ifdef PROFILE + for (i = 0; prof[i] != NULL && i < 512; i++) + e("str%s db \"%s\",0\n", prof[i], prof[i]); + e("global _t68k_prof\n"); + e("_t68k_prof: dd begin_t68k_prof\n"); + e("begin_t68k_prof:\n"); + for (i = 0; prof[i] != NULL && i < 512; i++) + { + e("prof%s dd 0, str%s\n", prof[i], prof[i]); + free(prof[i]); + } + e("dd -1, -1\n"); +#endif + + fclose(fp); + + printf("Total:\t%d\n", num_handlers); + printf("\nSee \"README.TXT\" for terms of use and documentation!\n"); + return 0; +} + +/* + * + * Developers' Notes: A Guide to the Guts of Turbo68K ;) + * ----------------------------------------------------- + * + * Register Usage: + * + * EAX: ****************NZ*****C*******V + * AH: NZ*****C AL: *******V + * EBX: Address + * ECX: Cycle counter + * EDX: Data + * ESI: Current fetch (PC) pointer + * EDI: Instruction fetch/decoding (keep upper 16 bits clear!). At the + * beginning of any instruction handler, contains opcode in lower 16 bits + * EBP: Pointer to base of PC region. May be below the base if the unused PC + * bits are non-zero + * --- + * MMX optimizations are unsupported -- DO NOT USE THEM! They happen to slow + * things down + * + * MMX Register Usage: + * + * MM0: Saved EAX (memhandler_eax) + * MM1: Saved EBX (memhandler_ebx) + * MM2: Saved EBP (memhandler_ebp) + * MM3: Saved EDX (run_edx) + * MM4: Saved ESI (run_esi) + * MM5: Saved EDI (run_edi) + * MM6,MM7: Misc. usage + * --- + * Format of context.intr[]: + * + * intr[0-6] = Interrupt levels 1-7, elements contain the vector #, if 0 then + * no interrupt is pending at this level + * intr[7] = Number of interrupts pending (set to 0 at reset) + * --- + * Format of context.status: + * + * 0000 0000 0000 0000 0000 0000 0000 0ISR + * + * I=Interrupt; 1=interrupts being processed, 0=not + * S=Stop; 1=68K is stopped (STOP instruction), 0=not + * R=Running; 1=68K is running, 0=not + * --- + * Format of decoded[]: + * + * [0] = 1: 2 opcode 0 handlers + * [1] = 0: see above.. this is unused + * [2] = next handler... (0 + 2) + * --- + * SR values obtained from instructions are ANDed with 0xa71f to mask out + * unused bits. CCR values are ANDed with 0x1f to mask out the unused bits. + * This is how a real 68K behaves. + * --- + * Memory mapping types: The low-level handler mode code is present, but not + * accessible. I decided to forbid using it because of performance problems. + * --- + * The unused address bits of the PC are preserved. They are only lost if the + * PC goes out of bounds. + */ diff --git a/Src/CPU/68K/Turbo68K/README.TXT b/Src/CPU/68K/Turbo68K/README.TXT new file mode 100644 index 0000000..763f5c0 --- /dev/null +++ b/Src/CPU/68K/Turbo68K/README.TXT @@ -0,0 +1,1299 @@ + + + ###### ## ## ###### ###### ##### #### ##### ### ## + # ## # ## ## ## ## ## ### ## ## ## ## ## ## ## + ## ## ## ## ## ## ## ## ## ## ## ## ## ## + ## ## ## ##### ##### ## ## ###### ##### #### + ## ## ## #### ## ## ## ## ## ## ## ## #### + ## ## ## ## ## ## ### ## ## ## ## ## ## ## ## + #### #### ### ## ###### ##### ##### ##### ### ## + + [ Turbo68K Readme ] + Copyright 2000-2002 Bart Trzynadlowski + + +This is the documentation for Turbo68K Version 0.6, please read it. Terms of +use are explained a little further below, it is required that you read and +abide by them in order to use this software. + +Turbo68K is a Motorola 680X0 emulator. It currently emulates the 68000 and +68010 microprocessors. It was written for Intel X86-based (Pentium or better) +systems. Originally designed for Genital it is now available for public use. + +Contact Bart Trzynadlowski: + Email: trzy@mailandnews.com + WWW: http://trzy.overclocked.org + http://trzy.overclocked.org/turbo68k + + +------------------- + 0. Terms of Use +------------------- + +Turbo68K is Copyright 2000, 2001, 2002 Bart Trzynadlowski. + +"Turbo68K" refers to: MAKE68K.C, TURBO68K.H, README.TXT, any binaries compiled +from those files, any source code emitted from MAKE68K.C, and any object file +created from such code. + +To use Turbo68K, you must agree to the following: + + - Turbo68K shall be distributed freely, unmodified, and intact. + - Charging money, goods, or services for Turbo68K is forbidden. + - Claiming ownership or authorship of Turbo68K is forbidden. + - Turbo68K may not be distributed in any medium for which money, goods, or + services are solicited. + - The author, Bart Trzynadlowski, will not be held liable for damages. + - Credit must be given to the author either in the program itself (where + the user can see it) or in its documentation. + +You are encouraged to contact Bart Trzynadlowski if you wish to use Turbo68K +in a commercial product (to negotiate licensing.) + +Bart Trzynadlowski reserves the right to use Turbo68K in any way he sees fit. +If you do not agree with all of these terms, please remove Turbo68K from your +possession. + + +------------------------ + 1. Table of Contents +------------------------ + + 0. Terms of Use + 1. Table of Contents + 2. Making Turbo68K + 3. Command Line Options + 4. Using Turbo68K + 4.1 Basic Data Types + 4.2 Definitions + 4.3 Context + 4.4 Instruction Fetching + 4.5 Data Access + 4.6 PC-Relative Reads + 4.7 Interrupts + 4.8 Byte Swapped Memory + 4.9 The RESET and BKPT Instructions + 4.10 Initializing and Resetting Turbo68K + 4.11 Running the Emulator + 5. Function Reference + 6. Hints and Tips + 6.1 Alternative Identifiers + 6.2 Multiple Processors + 6.3 Maximizing Speed and Minimizing Size + 6.4 Sega Genesis TAS Bug + 7. Additional Notes + 7.1 Change History + 7.2 Un-emulated Features + 7.3 Errata + 7.4 Bibliography + 8. Special Thanks + + +---------------------- + 2. Making Turbo68K +---------------------- + +The Turbo68K 680X0 emulator is usually distributed in a compressed archive, +simply extract it and you should have MAKE68K.C, TURBO68K.H, and this +README.TXT file. + +The first step is to get the source code emitter, Make68K, up and running. It +should compile with any 32-bit C compiler (compilers other than gcc will often +produce warnings, these can be ignored.) The following have been confirmed: + + - DJGPP (gcc v2.8.1) [ MS-DOS ] + - gcc v2.7.2.1 [ FreeBSD ] + - Microsoft VisualC++ 6.0 Professional Edition [ Windows 95 ] + - WATCOM C/C++32 Version 10.6 [ MS-DOS ] + +If you get link errors concerning pow(), make sure to include the appropriate +math library. Try "-lm" if you are using GNU tools. + +Once Make68K has been built, run it with the name of the file you wish to emit +on the command line. For example: + + make68k turbo68k.asm + +Make68K can take a number of options. Try "make68k -h" to see a list. Section +3 of this text describes them in more detail. + +Assemble the emitted file with NASM. Version 0.97 or better should work, 0.98 +is known to work. + +Turbo68K contains identifiers with leading and trailing underscores as +well as without any, so all the object formats supported by NASM should work. + +Remember to include TURBO68K.H to use Turbo68K. + + +--------------------------- + 3. Command Line Options +--------------------------- + +Turbo68K can be tailored in a variety of ways to suit your needs through the +use of the following command line options: + + -mpu Processor type. Valid types are 68000 and 68010. + [Default=68000] + -addr Address bus width. This simulates the specified number of + address lines by cutting off any unsupported address bits + when accessing memory or updating the PC. The 68000 and + 68010 have a 24-bit address bus. [Default=24] + -illegal Emulates illegal instruction traps. Line 1010 and Line + 1111 emulator exceptions are processed as is the normal + illegal instruction exception. [Default] + -noillegal Does not emulate illegal instruction traps but instead + returns an error from Turbo68KRun(). The ILLEGAL opcode + still generates an illegal instruction trap regardless of + whether -illegal or -noillegal has been specified. + -dummyread Emulates dummy reads for the CLR, Scc, and unprivileged + "MOVE from SR" instructions. These instructions perform a + read (and do not make use of the data) before writing. + Some systems may require this for accurate emulation (for + example: some systems will lock up when write-only + locations are read.) This is only available if the + processor type is 68000. [Default] + -nodummyread Does not emulate dummy reads and instead performs a write + immediately. + -skip Skips over idle DBRA loops by setting the counter register + to 0xFFFF, adjusting the timer appropriately, and moving + to the next instruction. + -noskip Does not skip over idle loops. This eliminates problems + with compatibility and is recommended. [Default] + -brafetch Uses the fetch memory map array to update the PC on Bcc, + BRA, BSR, and DBcc instructions. + -nobrafetch Bcc, BRA, BSR, and DBcc branches are handled by adding the + displacement to the internal normalized PC. This is less + safe, in theory, but you should not normally have any + problems. It is also faster and generates less code than + -brafetch. [Default] + -pcfetch PC-relative reads are handled through a special fetch map. + Please read section 4.6 for more information. + -nopcfetch PC-relative reads are treated as normal data reads. + [Default] + -multiaddr Separate address spaces for supervisor and user. This has + not been extensively tested, so let me know how it works. + [Default] + -singleaddr Single address space for supervisor and user. + -defmap Definable memory map arrays are used to handle reading and + writing to the address space. [Default] + -handler Uses handlers for reading/writing bytes, words, and long- + words. The fetch memory map array must still be set up. + -stackcall Use stack-based calling conventions. [Default] + -regcall Use register-based calling conventions. EAX contains the + first argument, the second is in EDX. + -id Adds the specified string to the beginning of all + identifiers. Up to 16 characters are allowed. See section + 6.1 for more information on this. + +Please read further to learn more about the details of some of these options. + + +--------------------- + 4. Using Turbo68K +--------------------- + +A general overview of Turbo68K is provided here. More information on certain +topics is provided throughout the document. + + +4.1 Basic Data Types + +The following data types are provided by Turbo68K: + + TURBO68K_UINT32 Unsigned 32-bit + TURBO68K_INT32 Signed 32-bit + TURBO68K_UINT16 Unsigned 16-bit + TURBO68K_INT16 Signed 16-bit + TURBO68K_UINT8 Unsigned 8-bit + TURBO68K_INT8 Signed 8-bit + + +4.2 Definitions + +Turbo68K functions return and accept defined arguments. Use these instead of +hard-coded values. Different functions return and accept different values, so +make sure to check with the function reference first. + + TURBO68K_NULL 0 + TURBO68K_OKAY Success + TURBO68K_SUPERVISOR Supervisor address space + TURBO68K_USER User address space + TURBO68K_ERROR_FETCH Fetch error (PC out of bounds) + TURBO68K_ERROR_INVINST Invalid instruction + TURBO68K_ERROR_INTLEVEL Invalid interrupt level was specified + TURBO68K_ERROR_INTVECTOR Invalid interrupt vector was specified + TURBO68K_ERROR_INTPENDING Specified interrupt level already pending + TURBO68K_UNINITIALIZED Uninitialized interrupt vector + TURBO68K_SPURIOUS Spurious interrupt vector + TURBO68K_AUTOVECTOR Autovectored interrupt + + +4.3 Context + +Processor contexts are special structures which contain all the data necessary +to manage the emulated 680X0. The procedure for emulating a 680X0 processor +with Turbo68K involves setting up a context, mapping it in, emulating some +code, and perhaps mapping the context out. + + Context Definitions: + -------------------- + + struct TURBO68K_CONTEXT_68000 + { + void *fetch; + void *read_byte, *read_word, *read_long; + void *write_byte, *write_word, *write_long; + void *super_fetch; + void *super_read_byte, *super_read_word, *super_read_long; + void *super_write_byte, *super_write_word, + *super_write_long; + void *user_fetch; + void *user_read_byte, *user_read_word, *user_read_long; + void *user_write_byte, *user_write_word, *user_write_long; + TURBO68K_UINT32 intr[8], cycles, remaining; + TURBO68K_UINT32 d[8], a[8], sp, sr, pc, status; + void *InterruptAcknowledge; + void *Reset; + }; + + struct TURBO68K_CONTEXT_68010 + { + void *fetch, *pcfetch; + void *read_byte, *read_word, *read_long; + void *write_byte, *write_word, *write_long; + void *super_fetch, *super_pcfetch; + void *super_read_byte, *super_read_word, *super_read_long; + void *super_write_byte, *super_write_word, + *super_write_long; + void *user_fetch, *user_pcfetch; + void *user_read_byte, *user_read_word, *user_read_long; + void *user_write_byte, *user_write_word, *user_write_long; + TURBO68K_UINT32 intr[8], cycles, remaining; + TURBO68K_UINT32 d[8], a[8], sp, sr, pc, fc, vbr, status; + void *InterruptAcknowledge; + void *Reset, *Bkpt; + }; + +If you are emulating different address spaces for supervisor and user modes, +the super_ and user_ pointers must be set to point at the respective memory +maps. The other memory map pointers (fetch, read_byte, write_byte, etc.) will +be managed internally by Turbo68K. + +If you are only emulating a single address space for both privilege modes, +you do not have to set up the super_ and user_ pointers but must set up the +others. + +The "intr" array holds pending interrupt information. Entries 0-6 are for +interrupt levels 1-7, they contain the vector of the pending interrupt, or 0 +if no interrupt at the given level is pending. Entry 7 contains the number of +pending interrupts. You should not modify this yourself, use the interrupt +functions. + +The "cycles" and "remaining" members are used to control execution time. +The total amount of request cycles is held in "cycles" and the number left is +held in "remaining." + +Data and address registers are kept in the "d" and "a" array, the PC is kept +in "pc", and the SR is kept in "sr" (the low word is used, the upper word +should be kept 0.) + +Whichever stack pointer is not in A7 is stored in "sp." Thus, if the processor +is in Supervisor mode, A7 is the SSP and "sp" contains the USP. Turbo68K will +only swap these when it has to switch privilege modes internally, such as with +a MOVE SR instruction, exception processing, etc. + +NOTE: If you absolutely must modify the SR outside of Turbo68K (for example, +if you are writing a debugger), you have to swap A7 and "sp" manually if the +privilege mode changed. + +For the 68010, "fc" is both SFC (first 3 bits of byte 0) and DFC (first 3 bits +of byte 1.) The 68010's VBR is kept in "vbr." + +Additional information on the processor status is reflected in the "status" +member. Its format is: + + Bit 0: R (Running) flag -- 0 = not running, 1 = running + Bit 1: S (Stopped) flag -- 0 = not stopped, 1 = stopped via the STOP + instruction + Bit 2: I (Interrupt) flag -- 0 = not processing interrupts, 1 = process- + ing interrupts (Turbo68KProcessInterrupts()) + +The "InterruptAcknowledge" pointer points to an interrupt acknowledge handler +which is called when interrupts are being processed. If the pointer is set to +TURBO68K_NULL, it will not be called. See section 4.7. + +Finally, the "Reset" pointer points to a Reset handler. If the RESET +instruction is executed, this handler will be called. If the pointer is set to +TURBO68K_NULL, it will not be called. The "Bkpt" pointer is for the 68010's +BKPT instruction. + +Contexts can be mapped in and out of Turbo68K's internal data space by using +Turbo68KSetContext() and Turbo68KGetContext(). Once they are mapped in, no +changes will be reflected in your context structures until you map them out. + +NOTE: You CANNOT access context members while the context is mapped in unless +there are functions provided for that purpose (such as Turbo68KSetFetch(), +Turbo68KFreeTimeSlice(), Turbo68KReadPC(), etc.) + +If there is no function for the operation you want to perform on the context, +you simply cannot do it until the context is mapped out. You may NOT map +contexts in and out from memory handlers while Turbo68K is running. Please +consult the function reference for more information. + +Turbo68K provides an internal context called "turbo68kcontext_68000" if +emulating a 68000, or "turbo68kcontext_68010" if emulating a 68010. These are +where you copy your context when you use Turbo68KSetContext(). They should +only be used if you are emulating one processor. + +When setting up memory maps, you can modify context pointers only if the +context was defined by you. If you wish to use the internal context, you will +have to use special functions to perform the same tasks. This is because +Turbo68K expects pointers in the internal context to be mangled (for +performance reasons.) Turbo68KSetContext()/Turbo68KGetContext() handle +this task automatically. + +Here is an example of defining a context and mapping it in and out: + + struct TURBO68K_CONTEXT_68000 mycontext; + + /* ... Set up everything ... */ + + Turbo68KSetContext(&mycontext); + /* ... Run some code ... */ + Turbo68KGetContext(&mycontext); + + +4.4 Instruction Fetching + +Turbo68K needs to know how to fetch instruction from 680X0 memory space. An +array of fetch regions must be set up describing where valid program areas are +located. + + Fetch Region Definition: + ------------------------ + + struct TURBO68K_FETCHREGION + { + TURBO68K_UINT32 base; + TURBO68K_UINT32 limit; + TURBO68K_UINT32 ptr; + }; + +The "base" is the start address of the fetch region. The "limit" is the end +address. The "ptr" must be set to a host machine pointer value with the base +subtracted from it. + +NOTE: Memory must be byte swapped. See section 4.8 for more information. + +Here is an example of how a hypothetical system's fetch map might look: + + struct TURBO68K_FETCHREGION my_fetch[] = + { + { 0x000000, 0x3fffff, rom - 0x000000 }, + { 0xff0000, 0xffffff, ram - 0xff0000 }, + { -1, -1, TURBO68K_NULL } + } + +The last element is required to let Turbo68K know where the array ends. + +To hook the fetch map up to your context, you would code: + + mycontext.super_fetch = my_fetch; + mycontext.user_fetch = my_fetch; + +If you are only using one unified address space for both supervisor and user +modes, all you have to do is: + + mycontext.fetch = my_fetch; + +For the internal context, you must use the appropriate functions: + + Turbo68KSetFetch(my_fetch, TURBO68K_SUPERVISOR); + Turbo68KSetFetch(my_fetch, TURBO68K_USER); + +Or, if one address space is being used: + + Turbo68KSetFetch(my_fetch, TURBO68K_NULL); /* second arg. ignored */ + + +4.5 Data Access + +Data accessing (byte, word, and long word read/writes) can be handled in a +number of ways. The first method is handled through definable memory map +arrays, somewhat similar to instruction fetching. This is the default method +(-defmap.) + + Data Region Definition: + ----------------------- + + struct TURBO68K_DATAREGION + { + TURBO68K_UINT32 base; + TURBO68K_UINT32 limit; + TURBO68K_UINT32 ptr; + void *handler; + }; + +You may either set up a pointer, exactly the same as with instruction fetch +regions. But, if the pointer is not set up (TURBO68K_NULL), Turbo68K will +assume you want to use a handler. Simply set "handler" to the address of the +function you want to use, nothing has to be mangled. + +Handler functions vary for different sizes of data: + + /* + * Read byte + */ + + TURBO68K_UINT8 ReadByte(TURBO68K_UINT32 addr) + { + /* ... */ + } + + /* + * Read word + */ + + TURBO68K_UINT16 ReadWord(TURBO68K_UINT32 addr) + { + /* ... */ + } + + /* + * Read long word + */ + + TURBO68K_UINT32 ReadLong(TURBO68K_UINT32 addr) + { + /* ... */ + } + + /* + * Write byte + */ + + void WriteByte(TURBO68K_UINT32 addr, TURBO68K_UINT8 data) + { + /* ... */ + } + + /* + * Write word + */ + + void WriteWord(TURBO68K_UINT32 addr, TURBO68K_UINT16 data) + { + /* ... */ + } + + /* + * Write long word + */ + + void WriteLong(TURBO68K_UINT32 addr, TURBO68K_UINT32 data) + { + /* ... */ + } + +All handlers are passed addresses that are clipped to whichever value you +chose when emitting the emulator. By default, the address space is 24-bit. + +NOTE: It is not a good idea to try to read the CCR from within a handler. Some +flag calculations are not completed until after data has been read/written. +In fact, do not read any registers besides PC (with the Turbo68KReadPC() +function.) + +The 68000 and 68010 break down long word accesses into 2 consecutive word +word accesses, yet Turbo68K still uses long word handlers to provide +opportunities for optimization. + +NOTE: Memory must be byte swapped. See section 4.8 for more information. + +To hook up the memory maps, you would code: + + mycontext.super_read_byte = my_read_byte; + mycontext.super_read_word = my_read_word; + mycontext.super_read_long = my_read_long; + mycontext.super_write_byte = my_write_byte; + mycontext.super_write_word = my_write_word; + mycontext.super_write_long = my_write_long; + + mycontext.user_read_byte = my_read_byte; + mycontext.user_read_word = my_read_word; + mycontext.user_read_long = my_read_long; + mycontext.user_write_byte = my_write_byte; + mycontext.user_write_word = my_write_word; + mycontext.user_write_long = my_write_long; + +If only one address space is being emulated, try: + + mycontext.read_byte = my_read_byte; + mycontext.read_word = my_read_word; + /* ... */ + +When using an internal context, use the appropriate functions: + + Turbo68KSetReadByte(my_read_byte, TURBO68K_SUPERVISOR); + Turbo68KSetReadWord(my_read_word, TURBO68K_SUPERVISOR); + /* ... */ + + +The second method of handling data is to use functions exclusively. Use the +-handler option when emitting Turbo68K. Six functions (as shown above) must +be provided and directly hooked up to the context just as with memory maps. +Now, whenever the address space is read or written, your function will be +called and it is up to you to handle everything. + + +4.6 PC-Relative Reads + +In some rare situations, PC-relative address reading cannot function properly +through the normal data access regions/handlers. An example of when this can +occur is when the emulated system uses partially encrypted ROMs (CPS-2.) + +Turbo68K must be emitted with the -pcfetch option and a special fetch region +for PC-relative reads must be set up. Writes are still handled using the data +regions/handlers. The region is a TURBO68K_FETCHREGION and thus no handlers +can be used. Memory pointers must be provided in the exact same way as you +would for a normal fetch region. + +The array of fetch regions must be attached to the "pcfetch" pointers of +the context: + + mycontext.pcfetch = pcfetch; + mycontext.super_pcfetch = pcfetch; + mycontext.user_pcfetch = pcfetch; + +When using an internal context, use Turbo68KSetPCFetch() and +Turbo68KGetPCFetch(). + +This is a pretty poor hack, but it should do the job in most cases. This +should not be needed for most systems. + + +4.7 Interrupts + +Interrupts may be triggered with the Turbo68KInterrupt() function and when +they are being processed, an option call-back function may be called. + +Turbo68KInterrupt() takes 2 arguments, the first is the level number, and the +second is the vector. For the vector, you may use TURBO68K_AUTOVECTOR for an +auto-vectored interrupt, TURBO68K_SPURIOUS for a spurious interrupt, and +TURBO68K_UNINITIALIZED for an uninitialized interrupt. + +Interrupts will be checked for and processed at the start of Turbo68KRun() or +when Turbo68KProcessInterrupts() is called. + +If the "InterruptAcknowledge" member of the context is null, the function it +points to will not be called, otherwise, the call-back can be used to make +certain that interrupts have been processed, if the hardware being emulated +requires this (for example, on the Sega Genesis, the call-back can be used to +update IRQ-related bit settings in the VDP status register.) + +The call-back takes a single argument: The interrupt vector number passed to +Turbo68KInterrupt() (a TURBO68K_UINT32), and returns nothing. If +TURBO68K_AUTOVECTOR was passed to Turbo68KInterrupt(), the call-back will get +the proper vector for the level being processed. If non-autovectored +interrupts are triggered, there is no way to determine which level is being +processed at the time the call-back is invoked. + +The InterruptAcknowledge pointer can be directly accessed regardless of +whether you use your own context, or Turbo68K's internal context: + + turbo68kcontext_68000.InterruptAcknowledge = &MyIRQAckHandler; + +Make sure to set it to TURBO68K_NULL if you do not wish to make use of the +call-back feature. It is possible to access this member at any time. + + +4.8 Byte Swapped Memory + +Host memory which Turbo68K can access directly must be byte swapped! The +MSB (most significant byte) and LSB (least significant byte) of each 16-bit +aligned word must be reversed. + + Sample Byte Swapping Function: + ------------------------------ + + /* + * void SwapMemory(TURBO68K_UINT8 *mem, TURBO68K_INT32 size); + * + * Byte swaps a given region of memory to work with Turbo68K's native + * access capabilities. + */ + + void SwapMemory(TURBO68K_UINT8 *mem, TURBO68K_INT32 size) + { + TURBO68K_UINT32 i, j; + + /* + * Swap bytes in each word + */ + + for (i = 0; i < length; i += 2) + { + j = buffer[i]; + buffer[i] = buffer[i + 1]; + buffer[i + 1] = j; + } + } + +Byte swapping is an optimization which greatly benefits Turbo68K in terms of +speed. It relies on the fact that the 68000 and 68010 use a 16-bit data bus. +To access individual bytes in byte swapped memory, XOR the address with 1. +Words can be read or written normally. Long words are best accessed as 2 +consecutive words. + + +4.9 The RESET and BKPT Instructions + +When the RESET or BKPT instruction is executed, the respective handler is +called. If the handler is found to be TURBO68K_NULL, no action is taken. +This is useful for emulating the functionality of the RESET (some systems need +to reset hardware when it is executed) and BKPT (some systems might need to +react to the Breakpoint Acknowledge Cycle) instructions. The BKPT handler is +called before the 68010 processes the breakpoint exception. + +You can also perform high level emulation (HLE) by strategically inserting +RESET (or BKPT, if dealing with a 68010) instructions into the emulated code. +Use Turbo68KReadPC() to make sure you know which RESET/BKPT was executed. +Turbo68KReadPC() will point to the next instruction. + +When setting up the handlers, no special functions are needed, even if the +internal context is being manipulated. + + mycontext.Reset = &MyRESETHandler; + + turbo68kcontext_68000.Reset = &MyRESETHandler; + +The RESET handler takes no arguments and returns nothing. The BKPT handler is +passed the breakpoint vector number (0-7.) + + /* + * Sample RESET handler + */ + + void MyRESETHandler() + { + /* ... Reset some hardware ... */ + } + + /* + * Sample BKPT handler + */ + + void MyBKPTHandler(TURBO68K_INT32 num) + { + /* ... */ + } + + +4.10 Initializing and Resetting Turbo68K + +Prior to doing anything, call Turbo68KInit() to initialize Turbo68K. This +only needs to be done once per program session. + +Next, set up your memory maps. Then, reset the 680X0 processor by calling the +Turbo68KReset() function. Turbo68K fetches the initial PC and SP from the +supervisor fetch memory map, not through the long word read map. Turbo68K +will return an error if it could not reset. This means you have set up your +memory map incorrectly. + + /* + * Initialize Turbo68K (VERY important) + */ + + Turbo68KInit(); + + /* ... set up memory maps and interrupt call-back (optional) ... */ + + /* + * Reset the processor + */ + + if (Turbo68KReset() != TURBO68K_OKAY) + Error(); + + /* ... begin emulating ... */ + + +4.11 Running the Emulator + +To execute code, use Turbo68KRun(). Pass it the number of cycles you wish +to run for. Turbo68K will execute at least as many cycles as you wanted, but +often executes a bit more. You can use Turbo68KGetElapsedCycles() to find +out exactly how many were emulated. See the function reference for more +information. + +Turbo68KInterrupt() can be used to trigger an interrupt. Please see section +4.7 for more information on interrupts. + + /* + * Run Turbo68K for 1000 cycles + */ + + Turbo68KRun(1000); + + /* + * Level 6 auto-vectored interrupt + */ + + Turbo68KInterrupt(6, TURBO68K_AUTOVECTOR); + +Both Turbo68KRun() and Turbo68KInterrupt() can return error information. +See the function reference for more information. + + +------------------------- + 5. Function Reference +------------------------- + +All of Turbo68K's functions are described here. Functions which are said not +to work under given circumstances will have undefined results if used under +those conditions. Usually, this means the context will be trashed, so be very +careful! + +When Turbo68K is running, any handlers you may have set up are considered +part of Turbo68K. Thus, when it is said a function cannot be called while +Turbo68K is running, this means it cannot be called from a handler. + + TURBO68K_INT32 Turbo68KInit(); + Initializes the emulator. This must be called before anything else. + Input: + Nothing. + Returns: + TURBO68K_OKAY + Restrictions: + For future compatibility, call this only once per program session. + + TURBO68K_INT32 Turbo68KReset(); + Resets the 680X0. It uses the supervisor instruction fetch region to + obtain the start-up vectors. Make sure everything is set up. A fetch + error indicates either that the reset vectors could not be fetched or + the PC reset vector points to an unhandled region of memory. + Input: + Nothing. + Returns: + TURBO68K_OKAY + TURBO68K_ERROR_FETCH (PC is out of bounds) + Restrictions: + Do not use while Turbo68K is running. + + TURBO68K_INT32 Turbo68KRun(TURBO68K_INT32 cycles); + Executes at least the specified number of cycles. To get the actual + number of cycles that were emulated, use Turbo68KGetElapsedCycles(). + Input: + Number of cycles. + Returns: + TURBO68K_OKAY + TURBO68K_ERROR_FETCH (PC is out of bounds) + TURBO68K_ERROR_INVINST (invalid instruction, if -noillegal was + specified) + TURBO68K_ERROR_STACKFRAME (68010 only, unsupported/invalid stack + frame, see section 7.2) + Restrictions: + Do not use while Turbo68K is running. + + TURBO68K_INT32 Turbo68KProcessInterrupts(); + Processes any pending interrupts (unless the priority level does not + agree with the SR priority bits.) + Input: + Nothing. + Returns: + TURBO68K_OKAY + TURBO68K_ERROR_FETCH + Restrictions: + None. (If called while Turbo68K is running, it has no effect.) + + + TURBO68K_INT32 Turbo68KInterrupt(TURBO68K_INT32 level, + TURBO68K_UINT32 vector); + Generates an interrupt with the specified level and vector. The level + must be 1-7 and the vector cannot be less than 2. For the vector, you + may specify the vector itself, TURBO68K_AUTOVECTOR for an autovectored + interrupt, TURBO68K_SPURIOUS for a spurious interrupt, or + TURBO68K_UNINITIALIZED for an uninitialized interrupt. If the + "InterruptAcknowledge" member of the context is not null, the function + it points to will be called as each interrupt is being taken. + Input: + Level. + Vector. + Returns: + TURBO68K_OKAY + TURBO68K_INTLEVEL (invalid level) + TURBO68K_INTVECTOR (invalid vector) + TURBO68K_INTPENDING (interrupt already pending at this level; this + is not an error and will frequently occur, it is best not to check + for this) + Restrictions: + None. + + TURBO68K_INT32 Turbo68KCancelInterrupt(TURBO68K_INT32 level); + Cancels the pending interrupt at the specified level. + Input: + Level. + Returns: + TURBO68K_OKAY + TURBO68K_INTLEVEL (invalid level) + Restrictions: + None. + + TURBO68K_UINT32 Turbo68KReadPC(); + Returns the current PC. If this is called from within a handler, the + PC may point at the next instruction or to one of the operand words. + Input: + Nothing. + Returns: + PC + Restrictions: + None. + + void Turbo68KSetFetch(void *fetch, TURBO68K_INT32 type); + void Turbo68KSetPCFetch(void *pcfetch, TURBO68K_INT32 type); + void Turbo68KSetReadByte(void *read_byte, TURBO68K_INT32 type); + void Turbo68KSetReadWord(void *read_word, TURBO68K_INT32 type); + void Turbo68KSetReadLong(void *read_long, TURBO68K_INT32 type); + void Turbo68KSetWriteByte(void *write_byte, TURBO68K_INT32 type); + void Turbo68KSetWriteWord(void *write_word, TURBO68K_INT32 type); + void Turbo68KSetWriteLong(void *write_long, TURBO68K_INT32 type); + These functions will attach memory maps to the internal context. If + you want to attach memory maps to a context you have created (which is + not mapped in), you must assign them manually. + Input: + Memory map array or function pointer (depending on whether + Turbo68K was configured for memory map arrays or handlers) + Type: TURBO68K_SUPERVISOR or TURBO68K_USER. If you are not + emulating the distinction, this argument can be set to anything + and will be ignored. + Returns: + Nothing. + Restrictions: + If different address spaces for supervisor and user modes are + being emulated and the "type" argument is unrecognized, the + behavior is undefined. + + void *Turbo68KGetFetch(TURBO68K_INT32 type); + void *Turbo68KGetPCFetch(TURBO68K_INT32 type); + void *Turbo68KGetReadByte(TURBO68K_INT32 type); + void *Turbo68KGetReadWord(TURBO68K_INT32 type); + void *Turbo68KGetReadLong(TURBO68K_INT32 type); + void *Turbo68KGetWriteByte(TURBO68K_INT32 type); + void *Turbo68KGetWriteWord(TURBO68K_INT32 type); + void *Turbo68KGetWriteLong(TURBO68K_INT32 type); + Will return the memory map or function pointers that are associated + with each type of access. + Input: + Type: TURBO68K_SUPERVISOR or TURBO68K_USER. If you are not + emulating the distinction, this argument can be set to anything + and will be ignored. + Returns: + Memory map array or function pointer. + Restrictions: + If different address spaces for supervisor and user modes are + being emulated and the "type" argument is unrecognized, the + behavior is undefined. + + TURBO68K_UINT8 *Turbo68KFetchPtr(TURBO68K_UINT32 addr); + Return a host pointer to the specified address in the current address + space. Turbo68K scans the fetch region and if the address is not + found, it will return 0. This is useful for disassemblers. + Input: + Address. + Returns: + Host address. + Restrictions: + Only the address space currently being used will be scanned. In + order to select the supervisor or user address space, you will + have to modify the privilege mode bit in SR and restore it to its + original value when finished. + + TURBO68K_UINT8 Turbo68KReadByte(TURBO68K_UINT32 addr); + TURBO68K_UINT16 Turbo68KReadWord(TURBO68K_UINT32 addr); + TURBO68K_UINT32 Turbo68KReadLong(TURBO68K_UINT32 addr); + void Turbo68KWriteByte(TURBO68K_UINT32 addr, TURBO68K_UINT8 data); + void Turbo68KWriteWord(TURBO68K_UINT32 addr, TURBO68K_UINT16 data); + void Turbo68KWriteLong(TURBO68K_UINT32 addr, TURBO68K_UINT32 data); + These functions read/write data from the current Turbo68K address + space. Thus, they can call handlers you have set up. These are + particularly useful for debuggers. Be careful, the PC (and possibly + other context data) will be invalid. + Input: + Address (and data if writing.) + Returns: + Data (or nothing if writing.) + Restrictions: + Do not use while Turbo68K is running. + + void Turbo68KSetContext(struct TURBO68K_CONTEXT *context); + void Turbo68KGetContext(struct TURBO68K_CONTEXT *context); + Map contexts in and out of the internal context using this pair of + functions. Turbo68K operates only on its internal context, so it is + necessary to copy externally-defined contexts into turbo68kcontext_* + when you want to emulate the processor they define. These functions + also make adjustments to the addresses contained in fetch, read_byte, + etc. in order to speed things up -- therefore you cannot access these + members, or any other context member, while it is mapped in. + Input: + Address of context to map in/out. + Returns: + Nothing. + Restrictions: + Do not use while Turbo68K is running. + + TURBO68K_UINT32 Turbo68KGetContextSize(); + Returns the size, in bytes, of a Turbo68K context. This is useful for + asserting that your compiler is packing the contexts correctly. + Input: + Nothing. + Returns: + Size of context in bytes. + Restrictions: + None. + + void Turbo68KClearCycles(); + Clears both "cycles" and "remaining" causing Turbo68K to exit early. + Be careful: a call to Turbo68KGetElapsedCycles() after this will + return 0 or a small number which does not reflect the total amount of + cycles used since Turbo68K was last called to run. + Input: + Nothing. + Returns: + Nothing. + Restrictions: + None. + + void Turbo68KFreeTimeSlice(); + Causes Turbo68K to stop running early by working some magic on both + "cycles" and "remaining." Unlike Turbo68KClearCycles(), you can get + the number of cycles executed with Turbo68KGetElapsedCycles(). + Input: + Nothing. + Returns: + Nothing. + Restrictions: + If called from a handler, Turbo68K does not actually stop running + until the current instruction finishes. + + TURBO68K_INT32 Turbo68KGetElapsedCycles(); + Returns the amount of cycles run. + Input: + Nothing. + Returns: + Cycles run. + Restrictions: + None. + + +--------------------- + 6. Hints and Tips +--------------------- + +For those of you who want to get the most out of Turbo68K, this section aims +to provide you with some neat little tricks to try. + + +6.1 Alternative Identifiers + +The default identifier names have nothing preceding them (Turbo68KInit(), +turbo68kcontext_68000, Turbo68KReset(), etc.) However, if the need arises, you +can change the identifier names by adding a string (up to 16 characters) to +the beginning of them. + +Use the -id option to emit Turbo68K with different identifier names. The data +types and macros will not be changed, TURBO68K_CONTEXT_68000 will remain as it +is, so will TURBO68K_OKAY, etc. + +You must also use the TURBO68K_ID() macro defined in TURBO68K.H to set the new +identifier names immediately after the header file has been included. For +example, if you want identifiers that start with "foo" you would write: + + #include "turbo68k.h" + TURBO68K_ID(foo); + +You can do this multiple times. If you have linked in 2 copies of Turbo68K, +one with identifiers that start with "foo", and another with identifiers +starting with "bar", you would write: + + #include "turbo68k.h" + TURBO68K_ID(foo); + TURBO68K_ID(bar); + +With the above example, you would have functions like: fooTurbo68KInit(), +barTurbo68KInit(), and (if you linked in another copy of Turbo68K without +modified identifiers) Turbo68KInit(). + + +6.2 Multiple Processors + +Emulating multiple processors is fairly simple (unless extremely accurate +timing is required.) You have to set up a context for each processor. You may +not use the internal context as one of them, since it will be overwritten if +you map in the other processor. + + Simplistic Example of Running 2 68000s: + --------------------------------------- + + struct TURBO68K_CONTEXT_68000 context[2]; + + /* ... */ + + for (int i = 0; i < 2; i++) + { + Turbo68KSetContext(&context[i]); + Turbo68KRun(500); + Turbo68KGetContext(&context[i]); + } + +Since Turbo68K is non-reentrant, you cannot multi-thread 2 processors unless +you link in 2 separate builds of Turbo68K (with different identifier names, +you can use the -id option for this.) + + +6.3 Maximizing Speed and Minimizing Size + +What better reasons to write a CPU emulator in assembly language than for +speed and size? To get the most speed and least size (the 2 don't always co- +exist) out of Turbo68K, here are a few things you can try: + + 1. If you are using memory map arrays, set them up wisely. If you have + memory regions bordering each other, try turning them into a single + large region. This not only cuts down the time it takes to scan through + the map, but lets code flow between the regions. Put the most commonly + accessed regions at the beginning of the array, and the least commonly + accessed at the end. + 2. Consider using handlers and not memory map arrays (-handler option.) If + you can write a fast and clean set of handlers, you can speed things up + by taking a load of work off of Turbo68K's back. Optimize your long- + word handlers as well. It is perfectly okay to have a long-word handler + call your word handlers twice and return the combined data, but it is + faster to write the handlers specifically for long-word accesses. + 3. Execute code in large time slices. The overhead of Turbo68KRun() is + significant but it can be compensated by spending more time emulating. + Throw Turbo68KSetContext()/Turbo68KGetContext() in the mix and the + gains are even greater. + 4. Try using -nodummyread if you can get away with it. For most systems, + it is unnecessary to emulate this tiny detail. + 5. If possible, use -nobrafetch (the default) instead of -brafetch. The + branch instructions are used a lot. If -skip does not impact compatib- + ility, consider using it, too. + 6. If you can get away with sneaking in a 32-bit address bus, do it. The + 68000 and 68010 actuall use a 24-bit address bus, which means that + Turbo68K has to mask off the unused bits when fetching data. The + overhead of doing this is negligible, but if you can get away with + using the full 32-bits, go for it. + 7. Don't allow separate address spaces for supervisor and user if you can + get away with it (with most systems, it isn't needed.) This reduces + some overhead in some of the instruction handlers and functions. + 8. Link the Turbo68K object file close to the object files which call it + most frequently. The theory behind this is that the processor will be + able to easily cache in code areas that use each other often. It might + not always yield any speed increase, but I have observed slightly + quicker performance when optimizing the link order in Genital. + 9. Strip the program you compile, this will reduce the executable tremend- + ously. Turbo68K leaves behind a lot of symbols and junk. + +There is a lot to do on my part. Turbo68K features some really poor code, but +usually in uncommon instructions. When/if I have time, I will optimize what I +can, but not all at once. + + +6.4 Sega Genesis TAS Bug + +The TAS instruction is usually used in multi-processor systems to synchronize +execution between multiple 68Ks because it takes the bus. But on the Sega +Genesis, the VDP will not release the bus and TAS instructions consequently +do not write to memory. + +In order to emulate this, comment out the call to WriteByte() in the TAS() +function in make68k.c. This will fix Gargoyles and possible some other games +which use the TAS instruction. The Genesis 3 apparently fixes this little bug +and games like Gargoyles no longer work. + + +----------------------- + 7. Additional Notes +----------------------- + +This is a collection of miscellaneous notes, points of interest, and errata. + + +7.1 Change History + +January 19, 2002: Version 0.6 + - CMPI and CMP no longer write back to memory + - Turbo68KCancelInterrut() fixed (uses EAX*4 as an index into the + interrupt array, so it now actually cancels interrupts) + - Optmized Turbo68KCancelInterrupt() slightly + - Added the "InterruptAcknowledge" call-back to facilitate more accurate + interrupt handling on some systems (such as the Sega Genesis) + - API functions now use the stack to save registers. Turbo68KInterrupt() + can now be called from within a memory handler + - Turbo68KGetXXX() functions now properly retrieve arguments passed to + them + - Some instruction handlers now use the stack to save registers + + ( Thanks to Charles MacDonald for submitting most of these fixes and + additions! ) + +June 29, 2001: Version 0.5 + - Changed the name to "Turbo68K" + - Undocumented flags emulated (verified against real 68000 processors) + - N flag calculation for ABCD, SBCD, and NBCD instructions is now truly + accurate + - Dummy read emulation added for CLR, Scc, and the unprivileged version of + "MOVE from SR." It is on by default and works only when the processor + type is 68000 + - CHK comparisons are now signed as they should be + - Fixed internal calls to API functions to work with register calling + conventions + +June 15, 2001: Version 0.4 + - Now uses proper identifier names when internally calling + Genital68KUpdateFetchPtr() + - N flag is now calculated for SBCD (thanks to ElSemi for this!) + - Optimized C flag calculation for SBCD -(Ax),-(Ay) and ABCD -(Ax),-(Ay) + - N flag for ABCD is calculated the same way as for SBCD (not sure if this + is correct, I'll verify it once my Saturn 68K debugger is complete) + +May 7, 2001: Version 0.35 + - Output file is now properly closed on exit + - RTD is no longer emitted as a 68000 instruction (68010 only) + - Initial PC and SP are now read from the instruction fetch memory maps + - Instructions are emitted in a more optimal order, cache performance + might be better + +April 29, 2001: Version 0.3 + - Fixed a MOVEM bug (example: MOVEM.L (A7)+,A7) + - Added rudimentary 68010 support + - "MOVE from SR" is no longer privileged for 68000 processors + - Rewrote documentation + - Possibly other minor changes I forgot to list + +February 11, 2001: Version 0.22 + - Added a PC-relative read mode (useful in very special circumstances) + - Removed some unused data + - Removed sign-extended byte read functions (they were not being used) + +January 13, 2001: Version 0.21 + - Fixed Genital68KReadXXX() and Genital68KWriteXXX() functions, they no + longer trash the PC + +January 7, 2001: Version 0.2 + - Slightly modified Genital68KReadXXX() and Genital68KWriteXXX() functions + - Registers are now saved when Genital68K functions are called + - Minor optimizations here and there + +December 8, 2000: Version 0.12 + - Fixed a bug with register calling conventions and read/write handlers + +December 4, 2000: Version 0.11 + - Fixed a bug in Genital68KGetContext() and Genital68KSetContext() which + corrupted user and supervisor address spaces + - Corrected a misleading comment in MAKE68K.C and updated the macro refer- + ence in this document + +December 3, 2000: Version 0.1 + - Added user/supervisor address space distinction (optional) + - Changed address space functions + - Added a GENITAL68K_NULL macro + - Fixed some mistakes in this document + +November 23, 2000: Version 0.0 + - Initial release + + +7.2 Un-emulated Features + +The following are not emulated: + + Tracing Turbo68K does not emulate trace exceptions. + Address Errors Turbo68K does not emulate address error exceptions. The + X86 allows for misaligned accesses (unless alignment + checking is on), and consequently, so does Turbo68K. + Bus Errors Bus errors are not emulated. + Undefined Bits Undefined bits in the SR are usually kept 0. + FC Output Lines The real 68000 and 68010 classify address space accesses + as either "User Data", "User Program", "Supervisor Data", + "Supervisor Program", or "CPU Space." Turbo68K + simplifies this by supporting a user address space and a + supervisor address space. There is also an option to + disable this distinction and use one address space. + MOVES The effects of the alternate function code registers (SFC + and DFC) are not emulated. This includes the 68010 MOVES + instruction. + Loop Mode Loop mode (68010) is not emulated. Perhaps if someone + supplies me with very detailed information on how it + works, I might consider adding it. + Stack Frames Only stack frame 0 is supported. Stack frame 8 (associated + with group 0 exceptions: bus and address errors) is never + used and if encountered, Turbo68KRun() returns a stack + frame error. When such an error occurs, the processor + state will look as if the problematic RTE instruction had + never been executed. + 68010 Timing For instructions common to both the 68000 and 68010 (most + of them), 68000 timing is used. + + +7.3 Errata + +Although I have attempted to fix every bug I came across, there is bound to be +something obscure, or possibly major, left undiscovered. There are a few +unimportant inaccuracies I do know about, but have not bothered to fix yet: + + DIVS, DIVU Timing is off by up to 10% in worst-case scenarios. + MULS, MULU Timing is off by an unknown amount. The multiplication + algorithm used by the 68000 and 68010 affects timing in + pretty odd ways. + Timing Some of the instructions have slightly inacurrate timing, + usually because of human error. Timing for exceptions and + interrupts is often wrong (but not TOO far off.) + Exceptions Priority between interrupts and other exceptions (such as + privilege violation, division by 0, TRAP, TRAPV, etc.) is + not fully emulated. Priority among interrupts themselves + seems to be properly handled, and that is most important. + I have not seen any situations where interrupts and other + exceptions affect each other. + +More information on the priority of spurious and uninitialized interrupts as +well as level 7 would be appreciated. I am going off what little the Motorola +manuals say. + + +7.4 Bibliography + +Most of what I know about 680X0 processors comes from the 2 free manuals I +ordered from Motorola. The rest comes from individuals who kindly helped me +and are mentioned in section 8. + + Documents Used: + --------------- + + - "Programmer's Reference Manual" by Motorola (M68000PM/AD REV 1) + - "M68000 8-/16-/32-Bit Microprocessor User's Manual Ninth Edition" by + Motorola (M68000UM/AD REV 8) + + +--------------------- + 8. Special Thanks +--------------------- + +Many thanks go out to those who helped me out with Turbo68K and contributed +to the project in any form. + + - Neil Bradley + - Victor Moya + - Dynarec Mailing List: M.I.K.e, Neil Griffiths, Gwenole, etc. + - Quintesson + - Steve Snake + - Tim Meekins + - Kuwanger + - Charles MacDonald + - Stephane Dallongeville + - Eli Dayan + - Neill Corlett + - Pete Dabbs + - ElSemi + +And, of course, thank YOU! Remember, comments, suggestions, and contributions +are more than welcome, so get in touch with me. See you next time! diff --git a/Src/CPU/68K/Turbo68K/Turbo68K.h b/Src/CPU/68K/Turbo68K/Turbo68K.h new file mode 100644 index 0000000..b1ba05e --- /dev/null +++ b/Src/CPU/68K/Turbo68K/Turbo68K.h @@ -0,0 +1,181 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Turbo68K: Motorola 680X0 emulator + * Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use + */ + +#ifndef _TURBO68K_H_ +#define _TURBO68K_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************************************************** +* Generic Macros */ + +#define TURBO68K_OKAY 0 /* no error */ +#define TURBO68K_NULL 0 /* null */ +#define TURBO68K_SUPERVISOR 0 /* affects supervisor space */ +#define TURBO68K_USER 1 /* function affects user space */ +#define TURBO68K_UNINITIALIZED 15 /* uninitialized interrupt */ +#define TURBO68K_SPURIOUS 24 /* spurious interrupt */ +#define TURBO68K_AUTOVECTOR 256 /* autovectored interrupt */ + + +/***************************************************************************** +* Error Codes */ + +#define TURBO68K_ERROR_FETCH 1 /* fetch error (PC bounds) */ +#define TURBO68K_ERROR_INVINST 2 /* invalid instruction */ +#define TURBO68K_ERROR_INTLEVEL 3 /* invalid interrupt level */ +#define TURBO68K_ERROR_INTVECTOR 4 /* invalid interrupt vector */ +#define TURBO68K_ERROR_INTPENDING 5 /* level already pending */ +#define TURBO68K_ERROR_STACKFRAME 6 /* unhandled stack frame */ + + +/***************************************************************************** +* Data Types */ + +typedef unsigned int TURBO68K_UINT32; /* unsigned 32-bit */ +typedef signed int TURBO68K_INT32; /* signed 32-bit */ +typedef unsigned short TURBO68K_UINT16; /* unsigned 16-bit */ +typedef signed short TURBO68K_INT16; /* signed 16-bit */ +typedef unsigned char TURBO68K_UINT8; /* unsigned 8-bit */ +typedef signed char TURBO68K_INT8; /* signed 8-bit */ + + +/***************************************************************************** +* Data Structures */ + +struct TURBO68K_FETCHREGION +{ + TURBO68K_UINT32 base; + TURBO68K_UINT32 limit; + TURBO68K_UINT32 ptr; +}; + +struct TURBO68K_DATAREGION +{ + TURBO68K_UINT32 base; + TURBO68K_UINT32 limit; + TURBO68K_UINT32 ptr; + void *handler; +}; + +struct TURBO68K_CONTEXT_68000 +{ + void *fetch, *pcfetch; + void *read_byte, *read_word, *read_long; + void *write_byte, *write_word, *write_long; + void *super_fetch, *super_pcfetch; + void *super_read_byte, *super_read_word, *super_read_long; + void *super_write_byte, *super_write_word, *super_write_long; + void *user_fetch, *user_pcfetch; + void *user_read_byte, *user_read_word, *user_read_long; + void *user_write_byte, *user_write_word, *user_write_long; + TURBO68K_UINT32 intr[8], cycles, remaining; + TURBO68K_UINT32 d[8], a[8], sp, sr, pc, status; + void *InterruptAcknowledge; + void *Reset; + void *Debug; +}; + +struct TURBO68K_CONTEXT_68010 +{ + void *fetch, *pcfetch; + void *read_byte, *read_word, *read_long; + void *write_byte, *write_word, *write_long; + void *super_fetch, *super_pcfetch; + void *super_read_byte, *super_read_word, *super_read_long; + void *super_write_byte, *super_write_word, *super_write_long; + void *user_fetch, *user_pcfetch; + void *user_read_byte, *user_read_word, *user_read_long; + void *user_write_byte, *user_write_word, *user_write_long; + TURBO68K_UINT32 intr[8], cycles, remaining; + TURBO68K_UINT32 d[8], a[8], sp, sr, pc, fc, vbr, status; + void *InterruptAcknowledge; + void *Reset, *Bkpt; + void *Debug; +}; + + +/***************************************************************************** +* Functions and External Data */ + +#define TURBO68K_ID(ID) \ + \ +extern struct TURBO68K_CONTEXT_68000 ID##turbo68kcontext_68000; \ +extern struct TURBO68K_CONTEXT_68010 ID##turbo68kcontext_68010; \ + \ +TURBO68K_INT32 ID##Turbo68KInit(); \ +TURBO68K_INT32 ID##Turbo68KReset(); \ +TURBO68K_INT32 ID##Turbo68KRun(TURBO68K_INT32); \ +TURBO68K_INT32 ID##Turbo68KProcessInterrupts(); \ +TURBO68K_INT32 ID##Turbo68KInterrupt(TURBO68K_INT32, TURBO68K_UINT32); \ +TURBO68K_INT32 ID##Turbo68KCancelInterrupt(TURBO68K_INT32); \ + \ +TURBO68K_UINT32 ID##Turbo68KReadPC(); \ + \ +void ID##Turbo68KSetFetch(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetPCFetch(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetReadByte(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetReadWord(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetReadLong(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetWriteByte(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetWriteWord(void *, TURBO68K_INT32); \ +void ID##Turbo68KSetWriteLong(void *, TURBO68K_INT32); \ +void *ID##Turbo68KGetFetch(TURBO68K_INT32); \ +void *ID##Turbo68KGetPCFetch(TURBO68K_INT32); \ +void *ID##Turbo68KGetReadByte(TURBO68K_INT32); \ +void *ID##Turbo68KGetReadWord(TURBO68K_INT32); \ +void *ID##Turbo68KGetReadLong(TURBO68K_INT32); \ +void *ID##Turbo68KGetWriteByte(TURBO68K_INT32); \ +void *ID##Turbo68KGetWriteWord(TURBO68K_INT32); \ +void *ID##Turbo68KGetWriteLong(TURBO68K_INT32); \ + \ +TURBO68K_UINT8 *ID##Turbo68KFetchPtr(TURBO68K_UINT32); \ +TURBO68K_UINT8 ID##Turbo68KReadByte(TURBO68K_UINT32); \ +TURBO68K_UINT16 ID##Turbo68KReadWord(TURBO68K_UINT32); \ +TURBO68K_UINT32 ID##Turbo68KReadLong(TURBO68K_UINT32); \ +void ID##Turbo68KWriteByte(TURBO68K_UINT32, TURBO68K_UINT8); \ +void ID##Turbo68KWriteWord(TURBO68K_UINT32, TURBO68K_UINT16);\ +void ID##Turbo68KWriteLong(TURBO68K_UINT32, TURBO68K_UINT32);\ + \ +void ID##Turbo68KSetContext(void *); \ +void ID##Turbo68KGetContext(void *); \ +TURBO68K_UINT32 ID##Turbo68KGetContextSize(); \ + \ +void ID##Turbo68KClearCycles(); \ +void ID##Turbo68KFreeTimeSlice(); \ +TURBO68K_INT32 ID##Turbo68KGetElapsedCycles(); \ + +TURBO68K_ID( ); /* default identifiers begin with nothing */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _TURBO68K_H_ */ diff --git a/Src/CPU/Bus.h b/Src/CPU/Bus.h new file mode 100644 index 0000000..acd72db --- /dev/null +++ b/Src/CPU/Bus.h @@ -0,0 +1,110 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Bus.h + * + * Header file for the IBus abstract base class. + */ + +#ifndef INCLUDED_BUS_H +#define INCLUDED_BUS_H + +#include "Types.h" + +/* + * IBus: + * + * An interface for an address bus. Defines handlers for 8-, 16-, 32-, and 64- + * bit random access. Endianness should depend on the application. + * + * Two buses are supported: a memory bus and an IO bus. All addresses are 32 + * bits. + */ +class IBus +{ +public: + /* + * Read8(addr): + * Read16(addr): + * Read32(addr): + * Read64(addr): + * + * Read handlers. + * + * Parameters: + * addr Address (caller should ensure it is aligned to the correct + * boundary, corresponding to the size). + * + * Returns: + * Data of the appropriate size (8, 16, 32, or 64 bits). + */ + virtual UINT8 Read8(UINT32 addr) { return 0xFF; } + virtual UINT16 Read16(UINT32 addr) { return 0xFFFF; } + virtual UINT32 Read32(UINT32 addr) { return 0xFFFFFFFF; } + virtual UINT64 Read64(UINT32 addr) { return 0xFFFFFFFFFFFFFFFFULL; } + + /* + * Write8(addr, data): + * Write16(addr, data): + * Write32(addr, data): + * Write64(addr, data): + * + * Write handlers. + * + * Parameters: + * addr Address (caller should ensure it is aligned to the correct + * boundary, corresponding to the size). + * data Data to write. + */ + virtual void Write8(UINT32 addr, UINT8 data) {} + virtual void Write16(UINT32 addr, UINT16 data) {} + virtual void Write32(UINT32 addr, UINT32 data) {} + virtual void Write64(UINT32 addr, UINT64 data) {} + + /* + * IORead8(addr): + * + * Read handler for the IO bus. Only 8-bit data supported for now. Add more + * as needed. + * + * Parameters: + * addr Address. + * + * Returns: + * Data. + */ + virtual UINT8 IORead8(UINT32 addr) { return 0xFF; } + + /* + * IOWrite8(addr, data): + * + * Write handler for the IO bus. + * + * Parameters: + * addr Address. + * data Data to write. + */ + virtual void IOWrite8(UINT32 addr, UINT8 data) {} +}; + + +#endif // INCLUDED_BUS_H diff --git a/Src/CPU/PowerPC/PPCDisasm.cpp b/Src/CPU/PowerPC/PPCDisasm.cpp new file mode 100644 index 0000000..d21722f --- /dev/null +++ b/Src/CPU/PowerPC/PPCDisasm.cpp @@ -0,0 +1,1258 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * PPCDisasm.cpp + * + * PowerPC 603e disassembler from the original Supermodel project. + * Copyright 2003 Bart Trzynadlowski, Ville Linde, and Stefano Teso. + * + * When possible, invalid forms of instructions are checked for. To the best + * of my knowledge, all appropriate load/store instructions are checked. I'm + * not sure whether any other kinds of instructions need checking. + */ + +#include +#include +#include +#ifdef STANDALONE +#include +#endif +#include "Supermodel.h" + +#define DISASM_VERSION "1.1" + + +/****************************************************************************** + Instruction Descriptions + + The disassembler is primarily table-driven making it easily modifiable. +******************************************************************************/ + +/* + * Masks + * + * These masks isolate fields in an instruction word. + */ +#define M_LI 0x03fffffc +#define M_AA 0x00000002 +#define M_LK 0x00000001 +#define M_BO 0x03e00000 +#define M_BI 0x001f0000 +#define M_BD 0x0000fffc +#define M_RT 0x03e00000 +#define M_RA 0x001f0000 +#define M_RB 0x0000f800 +#define M_CRFD 0x03800000 +#define M_L 0x00200000 +#define M_TO 0x03e00000 +#define M_D 0x0000ffff +#define M_SIMM 0x0000ffff +#define M_UIMM 0x0000ffff +#define M_NB 0x0000f800 +#define M_SR 0x000f0000 +#define M_SH 0x0000f800 +#define M_CRFS 0x001c0000 +#define M_IMM 0x0000f000 +#define M_CRBD 0x03e00000 +#define M_RC 0x00000001 +#define M_CRBA 0x001f0000 +#define M_CRBB 0x0000f800 +#define M_SPR 0x001FF800 +#define M_TBR 0x001FF800 +#define M_CRM 0x000FF000 +#define M_FM 0x01FE0000 +#define M_OE 0x00000400 +#define M_REGC 0x000007c0 +#define M_MB 0x000007c0 +#define M_ME 0x0000003e +#define M_XO 0x000007fe + +/* + * Field Defining Macros + * + * These macros generate instruction words with their associated fields filled + * in with the passed value. + */ +#define D_OP(op) ((uint32_t(op) & 0x3f) << 26) +#define D_XO(xo) ((uint32_t(xo) & 0x3ff) << 1) +#define D_RT(r) ((uint32_t(r) & 0x1f) << (31 - 10)) +#define D_RA(r) ((uint32_t(r) & 0x1f) << (31 - 15)) +#define D_UIMM(u) (uint32_t(u) & 0xffff) + +/* + * Macros to Get Field Values + * + * These macros return the values of fields in an opcode. They all return + * unsigned values and do not perform any sign extensions. + */ +#define G_RT(op) ((op & M_RT) >> (31 - 10)) +#define G_RA(op) ((op & M_RA) >> (31 - 15)) +#define G_RB(op) ((op & M_RB) >> (31 - 20)) +#define G_SIMM(op) (op & M_SIMM) +#define G_UIMM(op) (op & M_UIMM) +#define G_LI(op) ((op & M_LI) >> 2) +#define G_BO(op) ((op & M_BO) >> (31 - 10)) +#define G_BI(op) ((op & M_BI) >> (31 - 15)) +#define G_BD(op) ((op & M_BD) >> 2) +#define G_CRFD(op) ((op & M_CRFD) >> (31 - 8)) +#define G_L(op) ((op & M_L) >> (31 - 10)) +#define G_CRBD(op) ((op & M_CRBD) >> (31 - 10)) +#define G_CRBA(op) ((op & M_CRBA) >> (31 - 15)) +#define G_CRBB(op) ((op & M_CRBB) >> (31 - 20)) +#define G_REGC(op) ((op & M_REGC) >> (31 - 25)) +#define G_D(op) (op & M_D) +#define G_NB(op) ((op & M_NB) >> (31 - 20)) +#define G_CRFS(op) ((op & M_CRFS) >> (31 - 13)) +#define G_SPR(op) ((op & M_SPR) >> (31 - 20)) +#define G_SR(op) ((op & M_SR) >> (31 - 15)) +#define G_CRM(op) ((op & M_CRM) >> (31 - 19)) +#define G_FM(op) ((op & M_FM) >> (31 - 14)) +#define G_IMM(op) ((op & M_IMM) >> (31 - 19)) +#define G_SH(op) ((op & M_SH) >> (31 - 20)) +#define G_MB(op) ((op & M_MB) >> (31 - 25)) +#define G_ME(op) ((op & M_ME) >> 1) +#define G_TO(op) ((op & M_TO) >> (31 - 10)) + +/* + * Operand Formats + * + * These convey information on what operand fields are present and how they + * ought to be printed. + * + * I'm fairly certain all of these are used, but that is not guaranteed. + */ +enum +{ + F_NONE, // + F_LI, // LI*4+PC if AA=0 else LI*4 + F_BCx, // BO, BI, target_addr used only by BCx + F_RT_RA_0_SIMM, // rT, rA|0, SIMM rA|0 means if rA == 0, print 0 + F_ADDIS, // rT, rA, SIMM (printed as unsigned) only used by ADDIS + F_RT_RA_SIMM, // rT, rA, SIMM + F_RA_RT_UIMM, // rA, rT, UIMM + F_CMP_SIMM, // crfD, L, A, SIMM + F_CMP_UIMM, // crfD, L, A, UIMM + F_RT_RA_0_RB, // rT, rA|0, rB + F_RT_RA_RB, // rT, rA, rB + F_RT_D_RA_0, // rT, d(rA|0) + F_RT_D_RA, // rT, d(rA) + F_RA_RT_RB, // rA, rT, rB + F_FRT_D_RA_0, // frT, d(RA|0) + F_FRT_D_RA, // frT, d(RA) + F_FRT_RA_0_RB, // frT, rA|0, rB + F_FRT_RA_RB, // frT, rA, rB + F_TWI, // TO, rA, SIMM only used by TWI instruction + F_CMP, // crfD, L, rA, rB + F_RA_RT, // rA, rT + F_RA_0_RB, // rA|0, rB + F_FRT_FRB, // frT, frB + F_FCMP, // crfD, frA, frB + F_CRFD_CRFS, // crfD, crfS + F_MCRXR, // crfD only used by MCRXR + F_RT, // rT + F_MFSR, // rT, SR only used by MFSR + F_MTSR, // SR, rT only used by MTSR + F_MFFSx, // frT only used by MFFSx + F_FCRBD, // crbD FPSCR[crbD] + F_MTFSFIx, // crfD, IMM only used by MTFSFIx + F_RB, // rB + F_TW, // TO, rA, rB only used by TW + F_RT_RA_0_NB, // rT, rA|0, NB print 32 if NB == 0 + F_SRAWIx, // rA, rT, SH only used by SRAWIx + F_BO_BI, // BO, BI + F_CRBD_CRBA_CRBB, // crbD, crbA, crbB + F_RT_SPR, // rT, SPR and TBR + F_MTSPR, // SPR, rT only used by MTSPR + F_MTCRF, // CRM, rT only used by MTCRF + F_MTFSFx, // FM, frB only used by MTFSFx + F_RT_RA, // rT, rA + F_FRT_FRA_FRC_FRB, // frT, frA, frC, frB + F_FRT_FRA_FRB, // frT, frA, frB + F_FRT_FRA_FRC, // frT, frA, frC + F_RA_RT_SH_MB_ME, // rA, rT, SH, MB, ME + F_RLWNMx, // rT, rA, rB, MB, ME only used by RLWNMx + F_RT_RB, // rT, rB +}; + +/* + * Flags + */ +#define FL_OE (1 << 0) // if there is an OE field +#define FL_RC (1 << 1) // if there is an RC field +#define FL_LK (1 << 2) // if there is an LK field +#define FL_AA (1 << 3) // if there is an AA field +#define FL_CHECK_RA_RT (1 << 4) // assert rA!=0 and rA!=rT +#define FL_CHECK_RA (1 << 5) // assert rA!=0 +#define FL_CHECK_LSWI (1 << 6) // specific check for LSWI validity +#define FL_CHECK_LSWX (1 << 7) // specific check for LSWX validity + + +/* + * Instruction Descriptor + * + * Describes the layout of an instruction. + */ +typedef struct +{ + const char *mnem; // mnemonic + uint32_t match; // bit pattern of instruction after it has been masked + uint32_t mask; // mask of variable fields (AND with ~mask to compare w/ + // bit pattern to determine a match) + int format; // operand format + unsigned flags; // flags +} IDESCR; + +/* + * Instruction Table + * + * Table of instruction descriptors which allows the disassembler to decode + * and print instructions. + */ +static const IDESCR itab[] = +{ + { "add", D_OP(31)|D_XO(266), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "addc", D_OP(31)|D_XO(10), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "adde", D_OP(31)|D_XO(138), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "addi", D_OP(14), M_RT|M_RA|M_SIMM, F_RT_RA_0_SIMM, 0 }, + { "addic", D_OP(12), M_RT|M_RA|M_SIMM, F_RT_RA_SIMM, 0 }, + { "addic.", D_OP(13), M_RT|M_RA|M_SIMM, F_RT_RA_SIMM, 0 }, + { "addis", D_OP(15), M_RT|M_RA|M_SIMM, F_ADDIS, 0 }, + { "addme", D_OP(31)|D_XO(234), M_RT|M_RA|M_OE|M_RC, F_RT_RA, FL_OE|FL_RC }, + { "addze", D_OP(31)|D_XO(202), M_RT|M_RA|M_OE|M_RC, F_RT_RA, FL_OE|FL_RC }, + { "and", D_OP(31)|D_XO(28), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "andc", D_OP(31)|D_XO(60), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "andi.", D_OP(28), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + { "andis.", D_OP(29), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + { "b", D_OP(18), M_LI|M_AA|M_LK, F_LI, FL_AA|FL_LK }, + { "bc", D_OP(16), M_BO|M_BI|M_BD|M_AA|M_LK, F_BCx, FL_AA|FL_LK }, + { "bcctr", D_OP(19)|D_XO(528), M_BO|M_BI|M_LK, F_BO_BI, FL_LK }, + { "bclr", D_OP(19)|D_XO(16), M_BO|M_BI|M_LK, F_BO_BI, FL_LK }, + { "cmp", D_OP(31)|D_XO(0), M_CRFD|M_L|M_RA|M_RB, F_CMP, 0 }, + { "cmpi", D_OP(11), M_CRFD|M_L|M_RA|M_SIMM, F_CMP_SIMM, 0 }, + { "cmpl", D_OP(31)|D_XO(32), M_CRFD|M_L|M_RA|M_RB, F_CMP, 0 }, + { "cmpli", D_OP(10), M_CRFD|M_L|M_RA|M_UIMM, F_CMP_UIMM, 0 }, + { "cntlzw", D_OP(31)|D_XO(26), M_RT|M_RA|M_RC, F_RA_RT, FL_RC }, + { "crand", D_OP(19)|D_XO(257), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "crandc", D_OP(19)|D_XO(129), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "creqv", D_OP(19)|D_XO(289), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "crnand", D_OP(19)|D_XO(225), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "crnor", D_OP(19)|D_XO(33), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "cror", D_OP(19)|D_XO(449), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "crorc", D_OP(19)|D_XO(417), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "crxor", D_OP(19)|D_XO(193), M_CRBD|M_CRBA|M_CRBB, F_CRBD_CRBA_CRBB, 0 }, + { "dcba", D_OP(31)|D_XO(758), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbf", D_OP(31)|D_XO(86), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbi", D_OP(31)|D_XO(470), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbst", D_OP(31)|D_XO(54), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbt", D_OP(31)|D_XO(278), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbtst", D_OP(31)|D_XO(246), M_RA|M_RB, F_RA_0_RB, 0 }, + { "dcbz", D_OP(31)|D_XO(1014),M_RA|M_RB, F_RA_0_RB, 0 }, + { "divw", D_OP(31)|D_XO(491), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "divwu", D_OP(31)|D_XO(459), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "eciwx", D_OP(31)|D_XO(310), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "ecowx", D_OP(31)|D_XO(438), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "eieio", D_OP(31)|D_XO(854), 0, F_NONE, 0 }, + { "eqv", D_OP(31)|D_XO(284), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "extsb", D_OP(31)|D_XO(954), M_RT|M_RA|M_RC, F_RA_RT, FL_RC }, + { "extsh", D_OP(31)|D_XO(922), M_RT|M_RA|M_RC, F_RA_RT, FL_RC }, + { "fabs", D_OP(63)|D_XO(264), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fadd", D_OP(63)|D_XO(21), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "fadds", D_OP(59)|D_XO(21), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "fcmpo", D_OP(63)|D_XO(32), M_CRFD|M_RA|M_RB, F_FCMP, 0 }, + { "fcmpu", D_OP(63)|D_XO(0), M_CRFD|M_RA|M_RB, F_FCMP, 0 }, + { "fctiw", D_OP(63)|D_XO(14), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fctiwz", D_OP(63)|D_XO(15), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fdiv", D_OP(63)|D_XO(18), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "fdivs", D_OP(59)|D_XO(18), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "fmadd", D_OP(63)|D_XO(29), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fmadds", D_OP(59)|D_XO(29), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fmr", D_OP(63)|D_XO(72), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fmsub", D_OP(63)|D_XO(28), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fmsubs", D_OP(59)|D_XO(28), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fmul", D_OP(63)|D_XO(25), M_RT|M_RA|M_REGC|M_RC, F_FRT_FRA_FRC, FL_RC }, + { "fmuls", D_OP(59)|D_XO(25), M_RT|M_RA|M_REGC|M_RC, F_FRT_FRA_FRC, FL_RC }, + { "fnabs", D_OP(63)|D_XO(136), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fneg", D_OP(63)|D_XO(40), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fnmadd", D_OP(63)|D_XO(31), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fnmadds",D_OP(59)|D_XO(31), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fnmsub", D_OP(63)|D_XO(30), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fnmsubs",D_OP(59)|D_XO(30), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fres", D_OP(59)|D_XO(24), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "frsp", D_OP(63)|D_XO(12), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "frsqrte",D_OP(63)|D_XO(26), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fsel", D_OP(63)|D_XO(23), M_RT|M_RA|M_RB|M_REGC|M_RC, F_FRT_FRA_FRC_FRB, FL_RC }, + { "fsqrt", D_OP(63)|D_XO(22), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fsqrts", D_OP(59)|D_XO(22), M_RT|M_RB|M_RC, F_FRT_FRB, FL_RC }, + { "fsub", D_OP(63)|D_XO(20), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "fsubs", D_OP(59)|D_XO(20), M_RT|M_RA|M_RB|M_RC, F_FRT_FRA_FRB, FL_RC }, + { "icbi", D_OP(31)|D_XO(982), M_RA|M_RB, F_RA_0_RB, 0 }, + { "isync", D_OP(19)|D_XO(150), 0, F_NONE, 0 }, + { "lbz", D_OP(34), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "lbzu", D_OP(35), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA_RT }, + { "lbzux", D_OP(31)|D_XO(119), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA_RT }, + { "lbzx", D_OP(31)|D_XO(87), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lfd", D_OP(50), M_RT|M_RA|M_D, F_FRT_D_RA_0, 0 }, + { "lfdu", D_OP(51), M_RT|M_RA|M_D, F_FRT_D_RA, FL_CHECK_RA }, + { "lfdux", D_OP(31)|D_XO(631), M_RT|M_RA|M_RB, F_FRT_RA_RB, FL_CHECK_RA }, + { "lfdx", D_OP(31)|D_XO(599), M_RT|M_RA|M_RB, F_FRT_RA_0_RB, 0 }, + { "lfs", D_OP(48), M_RT|M_RA|M_D, F_FRT_D_RA_0, 0 }, + { "lfsu", D_OP(49), M_RT|M_RA|M_D, F_FRT_D_RA, FL_CHECK_RA }, + { "lfsux", D_OP(31)|D_XO(567), M_RT|M_RA|M_RB, F_FRT_RA_RB, FL_CHECK_RA }, + { "lfsx", D_OP(31)|D_XO(535), M_RT|M_RA|M_RB, F_FRT_RA_0_RB, 0 }, + { "lha", D_OP(42), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "lhau", D_OP(43), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA_RT }, + { "lhaux", D_OP(31)|D_XO(375), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA_RT }, + { "lhax", D_OP(31)|D_XO(343), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lhbrx", D_OP(31)|D_XO(790), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lhz", D_OP(40), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "lhzu", D_OP(41), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA_RT }, + { "lhzux", D_OP(31)|D_XO(311), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA_RT }, + { "lhzx", D_OP(31)|D_XO(279), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lmw", D_OP(46), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "lswi", D_OP(31)|D_XO(597), M_RT|M_RA|M_NB, F_RT_RA_0_NB, FL_CHECK_LSWI }, + { "lswx", D_OP(31)|D_XO(533), M_RT|M_RA|M_RB, F_RT_RA_0_RB, FL_CHECK_LSWX }, + { "lwarx", D_OP(31)|D_XO(20), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lwbrx", D_OP(31)|D_XO(534), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "lwz", D_OP(32), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "lwzu", D_OP(33), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA_RT }, + { "lwzux", D_OP(31)|D_XO(55), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA_RT }, + { "lwzx", D_OP(31)|D_XO(23), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "mcrf", D_OP(19)|D_XO(0), M_CRFD|M_CRFS, F_CRFD_CRFS, 0 }, + { "mcrfs", D_OP(63)|D_XO(64), M_CRFD|M_CRFS, F_CRFD_CRFS, 0 }, + { "mcrxr", D_OP(31)|D_XO(512), M_CRFD, F_MCRXR, 0 }, + { "mfcr", D_OP(31)|D_XO(19), M_RT, F_RT, 0 }, + { "mffs", D_OP(63)|D_XO(583), M_RT|M_RC, F_MFFSx, FL_RC }, + { "mfmsr", D_OP(31)|D_XO(83), M_RT, F_RT, 0 }, + { "mfspr", D_OP(31)|D_XO(339), M_RT|M_SPR, F_RT_SPR, 0 }, + { "mfsr", D_OP(31)|D_XO(595), M_RT|M_SR, F_MFSR, 0 }, + { "mfsrin", D_OP(31)|D_XO(659), M_RT|M_RB, F_RT_RB, 0 }, + { "mftb", D_OP(31)|D_XO(371), M_RT|M_TBR, F_RT_SPR, 0 }, + { "mtcrf", D_OP(31)|D_XO(144), M_RT|M_CRM, F_MTCRF, 0 }, + { "mtfsb0", D_OP(63)|D_XO(70), M_CRBD|M_RC, F_FCRBD, FL_RC }, + { "mtfsb1", D_OP(63)|D_XO(38), M_CRBD|M_RC, F_FCRBD, FL_RC }, + { "mtfsf", D_OP(63)|D_XO(711), M_FM|M_RB|M_RC, F_MTFSFx, FL_RC }, + { "mtfsfi", D_OP(63)|D_XO(134), M_CRFD|M_IMM|M_RC, F_MTFSFIx, FL_RC }, + { "mtmsr", D_OP(31)|D_XO(146), M_RT, F_RT, 0 }, + { "mtspr", D_OP(31)|D_XO(467), M_RT|M_SPR, F_MTSPR, 0 }, + { "mtsr", D_OP(31)|D_XO(210), M_RT|M_SR, F_MTSR, 0 }, + { "mtsrin", D_OP(31)|D_XO(242), M_RT|M_RB, F_RT_RB, 0 }, + { "mulhw", D_OP(31)|D_XO(75), M_RT|M_RA|M_RB|M_RC, F_RT_RA_RB, FL_RC }, + { "mulhwu", D_OP(31)|D_XO(11), M_RT|M_RA|M_RB|M_RC, F_RT_RA_RB, FL_RC }, + { "mulli", D_OP(7), M_RT|M_RA|M_SIMM, F_RT_RA_SIMM, 0 }, + { "mullw", D_OP(31)|D_XO(235), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "nand", D_OP(31)|D_XO(476), M_RA|M_RT|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "neg", D_OP(31)|D_XO(104), M_RT|M_RA|M_OE|M_RC, F_RT_RA, FL_OE|FL_RC }, + { "nor", D_OP(31)|D_XO(124), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "or", D_OP(31)|D_XO(444), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "orc", D_OP(31)|D_XO(412), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "ori", D_OP(24), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + { "oris", D_OP(25), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + { "rfi", D_OP(19)|D_XO(50), 0, F_NONE, 0 }, + { "rlwimi", D_OP(20), M_RT|M_RA|M_SH|M_MB|M_ME|M_RC, F_RA_RT_SH_MB_ME, FL_RC }, + { "rlwinm", D_OP(21), M_RT|M_RA|M_SH|M_MB|M_ME|M_RC, F_RA_RT_SH_MB_ME, FL_RC }, + { "rlwnm", D_OP(23), M_RT|M_RA|M_RB|M_MB|M_ME|M_RC, F_RLWNMx, FL_RC }, + { "sc", D_OP(17)|2, 0, F_NONE, 0 }, + { "slw", D_OP(31)|D_XO(24), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "sraw", D_OP(31)|D_XO(792), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "srawi", D_OP(31)|D_XO(824), M_RT|M_RA|M_SH|M_RC, F_SRAWIx, FL_RC }, + { "srw", D_OP(31)|D_XO(536), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "stb", D_OP(38), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "stbu", D_OP(39), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA }, + { "stbux", D_OP(31)|D_XO(247), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA }, + { "stbx", D_OP(31)|D_XO(215), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "stfd", D_OP(54), M_RT|M_RA|M_D, F_FRT_D_RA_0, 0 }, + { "stfdu", D_OP(55), M_RT|M_RA|M_D, F_FRT_D_RA, FL_CHECK_RA }, + { "stfdux", D_OP(31)|D_XO(759), M_RT|M_RA|M_RB, F_FRT_RA_RB, FL_CHECK_RA }, + { "stfdx", D_OP(31)|D_XO(727), M_RT|M_RA|M_RB, F_FRT_RA_0_RB, 0 }, + { "stfiwx", D_OP(31)|D_XO(983), M_RT|M_RA|M_RB, F_FRT_RA_0_RB, 0 }, + { "stfs", D_OP(52), M_RT|M_RA|M_D, F_FRT_D_RA_0, 0 }, + { "stfsu", D_OP(53), M_RT|M_RA|M_D, F_FRT_D_RA, FL_CHECK_RA }, + { "stfsux", D_OP(31)|D_XO(695), M_RT|M_RA|M_RB, F_FRT_RA_RB, FL_CHECK_RA }, + { "stfsx", D_OP(31)|D_XO(663), M_RT|M_RA|M_RB, F_FRT_RA_0_RB, 0 }, + { "sth", D_OP(44), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "sthbrx", D_OP(31)|D_XO(918), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "sthu", D_OP(45), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA }, + { "sthux", D_OP(31)|D_XO(439), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA }, + { "sthx", D_OP(31)|D_XO(407), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "stmw", D_OP(47), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "stswi", D_OP(31)|D_XO(725), M_RT|M_RA|M_NB, F_RT_RA_0_NB, 0 }, + { "stswx", D_OP(31)|D_XO(661), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "stw", D_OP(36), M_RT|M_RA|M_D, F_RT_D_RA_0, 0 }, + { "stwbrx", D_OP(31)|D_XO(662), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "stwcx.", D_OP(31)|D_XO(150)|1, M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "stwu", D_OP(37), M_RT|M_RA|M_D, F_RT_D_RA, FL_CHECK_RA }, + { "stwux", D_OP(31)|D_XO(183), M_RT|M_RA|M_RB, F_RT_RA_RB, FL_CHECK_RA }, + { "stwx", D_OP(31)|D_XO(151), M_RT|M_RA|M_RB, F_RT_RA_0_RB, 0 }, + { "subf", D_OP(31)|D_XO(40), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "subfc", D_OP(31)|D_XO(8), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "subfe", D_OP(31)|D_XO(136), M_RT|M_RA|M_RB|M_OE|M_RC, F_RT_RA_RB, FL_OE|FL_RC }, + { "subfic", D_OP(8), M_RT|M_RA|M_SIMM, F_RT_RA_SIMM, 0 }, + { "subfme", D_OP(31)|D_XO(232), M_RT|M_RA|M_OE|M_RC, F_RT_RA, FL_OE|FL_RC }, + { "subfze", D_OP(31)|D_XO(200), M_RT|M_RA|M_OE|M_RC, F_RT_RA, FL_OE|FL_RC }, + { "sync", D_OP(31)|D_XO(598), 0, F_NONE, 0 }, + { "tlbia", D_OP(31)|D_XO(370), 0, F_NONE, 0 }, + { "tlbie", D_OP(31)|D_XO(306), M_RB, F_RB, 0 }, + { "tlbsync",D_OP(31)|D_XO(566), 0, F_NONE, 0 }, + { "tw", D_OP(31)|D_XO(4), M_TO|M_RA|M_RB, F_TW, 0 }, + { "twi", D_OP(3), M_TO|M_RA|M_SIMM, F_TWI, 0 }, + { "xor", D_OP(31)|D_XO(316), M_RT|M_RA|M_RB|M_RC, F_RA_RT_RB, FL_RC }, + { "xori", D_OP(26), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + { "xoris", D_OP(27), M_RT|M_RA|M_UIMM, F_RA_RT_UIMM, 0 }, + + /* + * PowerPC 603e/EC603e-specific instructions + */ + + { "tlbld", D_OP(31)|D_XO(978), M_RB, F_RB, 0 }, + { "tlbli", D_OP(31)|D_XO(1010),M_RB, F_RB, 0 } +}; + + +/****************************************************************************** + Instruction Decoding and Disassembly Functions +******************************************************************************/ + +/* + * CR Bits + * + * Use an index of BI&3 into this table to obtain the CR field bit name. + */ +static const char *crbit[] = { "lt", "gt", "eq", "so" }; + +/* + * SPR(): + * + * Decode the SPR (or TBR) field and append the register name to dest. If + * no name is associated with the field value, the value itself is printed. + */ +static void SPR(char *dest, unsigned spr_field) +{ + unsigned spr; + + /* + * Construct the SPR number -- SPR field is 2 5-bit fields + */ + + spr = (spr_field >> 5) & 0x1f; + spr |= (spr_field & 0x1f) << 5; + + /* + * Append the SPR name to the destination string using strcat() + */ + + switch (spr) + { + case 1: strcat(dest, "xer"); break; + case 8: strcat(dest, "lr"); break; + case 9: strcat(dest, "ctr"); break; + case 18: strcat(dest, "dsisr"); break; + case 19: strcat(dest, "dar"); break; + case 22: strcat(dest, "dec"); break; + case 25: strcat(dest, "sdr1"); break; + case 26: strcat(dest, "srr0"); break; + case 27: strcat(dest, "srr1"); break; + case 272: strcat(dest, "sprg0"); break; + case 273: strcat(dest, "sprg1"); break; + case 274: strcat(dest, "sprg2"); break; + case 275: strcat(dest, "sprg3"); break; + case 282: strcat(dest, "ear"); break; + case 287: strcat(dest, "pvr"); break; + case 528: strcat(dest, "ibat0u"); break; + case 529: strcat(dest, "ibat0l"); break; + case 530: strcat(dest, "ibat1u"); break; + case 531: strcat(dest, "ibat1l"); break; + case 532: strcat(dest, "ibat2u"); break; + case 533: strcat(dest, "ibat2l"); break; + case 534: strcat(dest, "ibat3u"); break; + case 535: strcat(dest, "ibat3l"); break; + case 536: strcat(dest, "dbat0u"); break; + case 537: strcat(dest, "dbat0l"); break; + case 538: strcat(dest, "dbat1u"); break; + case 539: strcat(dest, "dbat1l"); break; + case 540: strcat(dest, "dbat2u"); break; + case 541: strcat(dest, "dbat2l"); break; + case 542: strcat(dest, "dbat3u"); break; + case 543: strcat(dest, "dbat3l"); break; + case 1013: strcat(dest, "dabr"); break; // unsupported on 603e/EC603e + + /* + * Some PowerPC implementations may implement MFTB and MFSPR identically, + * therefore TBR registers are also decoded here + */ + + case 268: strcat(dest, "tbl"); break; + case 269: strcat(dest, "tbu"); break; + + /* + * PowerPC 603e/EC603e-specific registers + */ + + case 1008: strcat(dest, "hid0"); break; + case 1009: strcat(dest, "hid1"); break; + case 976: strcat(dest, "dmiss"); break; + case 977: strcat(dest, "dcmp"); break; + case 978: strcat(dest, "hash2"); break; + case 979: strcat(dest, "hash2"); break; + case 980: strcat(dest, "imiss"); break; + case 981: strcat(dest, "icmp"); break; + case 982: strcat(dest, "rpa"); break; + case 1010: strcat(dest, "iabr"); break; + + default: sprintf(dest, "%s%d", dest, spr); + break; + } +} + +/* + * DecodeSigned16(): + * + * Predecodes the SIMM field for us. If do_unsigned, it is printed as an + * unsigned 32-bit integer. + */ +static void DecodeSigned16(char *outbuf, uint32_t op, bool do_unsigned) +{ + INT16 s; + + s = G_SIMM(op); + if (do_unsigned) // sign extend to unsigned 32-bits + sprintf(outbuf, "0x%04X", (uint32_t) s); + else // print as signed 16 bits + { + if (s < 0) + sprintf(outbuf, "-0x%02X", -s); + else + sprintf(outbuf, "0x%02X",s); + } +} + +/* + * Mask(): + * + * Generate a mask from bit MB through ME (PPC-style backwards bit numbering.) + */ +static uint32_t Mask(unsigned mb, unsigned me) +{ + uint32_t i, mask; + + mb &= 31; + me &= 31; + + i = mb; + mask = 0; + while (1) + { + mask |= (1 << (31 - i)); + if (i == me) + break; + i = (i + 1) & 31; + } + + return mask; +} + +/* + * Check(): + * + * Perform checks on the instruction as required by the flags. Returns 1 if + * the instruction failed. + */ +static bool Check(uint32_t op, unsigned flags) +{ + unsigned nb, rt, ra; + + if (!flags) return OKAY; // nothing to check for! + + rt = G_RT(op); + ra = G_RA(op); + + if (flags & FL_CHECK_RA_RT) // invalid if rA==0 or rA==rT + { + if ((G_RA(op) == 0) || (G_RA(op) == G_RT(op))) + return FAIL; + } + + if (flags & FL_CHECK_RA) // invalid if rA==0 + { + if (G_RA(op) == 0) + return FAIL; + } + + if (flags & FL_CHECK_LSWI) + { + /* + * Check that rA is not in the range of registers to be loaded (even + * if rA == 0) + */ + + nb = G_NB(op); + + if (ra >= rt && ra <= (rt + nb - 1)) return FAIL; + if ((rt + nb - 1) > 31) // register wrap-around! + { + if (ra < ((rt + nb - 1) - 31)) + return FAIL; + } + } + + if (flags & FL_CHECK_LSWX) + { + /* + * Check that rT != rA, rT != rB, and rD and rA both do not specify + * R0. + * + * We cannot check fully whether rA or rB are in the range of + * registers specified to be loaded because that depends on XER. + */ + + if (rt == ra || rt == G_RB(op) || ((rt == 0) && (ra == 0))) + return FAIL; + } + + return OKAY; // passed checks +} + +/* + * Simplified(): + * + * Handles all simplified instruction forms. Returns 1 if one was decoded, + * otherwise 0 to indicate disassembly should carry on as normal. + */ +static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, char *oprs) +{ + uint32_t value, disp; + + value = G_SIMM(op); // value is fully sign-extended SIMM field + if (value & 0x8000) + value |= 0xffff0000; + + if (op == (D_OP(24)|D_RT(0)|D_RA(0)|D_UIMM(0))) + strcat(mnem, "nop"); // ori r0,r0,0 -> nop + else if ((op & ~(M_RT|M_RA|M_RB|M_RC)) == (D_OP(31)|D_XO(444))) + { + if (G_RT(op) == G_RB(op)) + { + strcat(mnem, "mr"); // orx rA,rT,rT -> mrx rA,rT + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d", G_RA(op), G_RT(op)); + } + else + return 0; + } + else if ((op & ~(M_RT|M_RA|M_RB|M_RC)) == (D_OP(31)|D_XO(124))) + { + if (G_RT(op) == G_RB(op)) + { + strcat(mnem, "not"); // nor rA,rT,rT -> not rA,rT + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d", G_RA(op), G_RT(op)); + } + else + return 0; + } + else if ((op & ~(M_RT|M_RA|M_SIMM)) == D_OP(14)) + { + if (G_RA(op) == 0) + { + strcat(mnem, "li"); // addi rT,0,value -> li rT,value + sprintf(oprs, "r%d,0x%08X", G_RT(op), value); + } + else + return 0; + } + else if ((op & ~(M_RT|M_RA|M_SIMM)) == D_OP(15)) + { + if (G_RA(op) == 0) + { + strcat(mnem, "li"); // addis rT,0,value -> li rT,(value<<16) + sprintf(oprs, "r%d,0x%08X", G_RT(op), value << 16); + } + else + { + strcat(mnem, "addi"); // addis rT,rA,SIMM -> addi rT,rA,SIMM<<16 + sprintf(oprs, "r%d,r%d,0x%08X", G_RT(op), G_RA(op), value << 16); + } + } + else if ((op & ~(M_RT|M_RA|M_UIMM)) == D_OP(29)) + { + strcat(mnem, "andi."); // andis. rA,rT,UIMM -> andi. rA,rT,UIMM<<16 + sprintf(oprs, "r%d,r%d,0x%08X", G_RA(op), G_RT(op), G_UIMM(op) << 16); + } + else if ((op & ~(M_RT|M_RA|M_UIMM)) == D_OP(25)) + { + strcat(mnem, "ori"); // oris rA,rT,UIMM -> ori rA,rT,UIMM<<16 + sprintf(oprs, "r%d,r%d,0x%08X", G_RA(op), G_RT(op), G_UIMM(op) << 16); + } + else if ((op & ~(M_RT|M_RA|M_UIMM)) == D_OP(27)) + { + strcat(mnem, "xori"); // xoris rA,rT,UIMM -> xori rA,rT,UIMM<<16 + sprintf(oprs, "r%d,r%d,0x%08X", G_RA(op), G_RT(op), G_UIMM(op) << 16); + } + else if ((op & ~(M_RT|M_RA|M_SH|M_MB|M_ME|M_RC)) == D_OP(20)) + { + value = Mask(G_MB(op), G_ME(op)); + strcat(mnem, "rlwimi"); // rlwimi[.] rA,rT,SH,MB,ME -> rlwimi[.] rA,rT,SH,MASK + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,%d,0x%08X", G_RA(op), G_RT(op), G_SH(op), value); + } + else if ((op & ~(M_RT|M_RA|M_SH|M_MB|M_ME|M_RC)) == D_OP(21)) + { + value = Mask(G_MB(op), G_ME(op)); + if (G_SH(op) == 0) // rlwinm[.] rA,rT,0,MB,ME -> and[.] rA,rT,MASK + { + strcat(mnem, "and"); + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,0x%08X", G_RA(op), G_RT(op), value); + } + else // rlwinm[.] rA,rT,SH,MASK + { + strcat(mnem, "rlwinm"); + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,%d,0x%08X", G_RA(op), G_RT(op), G_SH(op), value); + } + } + else if ((op & ~(M_RT|M_RA|M_RB|M_MB|M_ME|M_RC)) == D_OP(23)) + { + value = Mask(G_MB(op), G_ME(op)); + strcat(mnem, "rlwnm"); // rlwnm[.] rA,rT,SH,MB,ME -> rlwnm[.] rA,rT,SH,MASK + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,r%d,0x%08X", G_RA(op), G_RT(op), G_RB(op), value); + } + else if ((op & ~(M_BO|M_BI|M_BD|M_AA|M_LK)) == D_OP(16)) + { + disp = G_BD(op) * 4; + if (disp & 0x00008000) + disp |= 0xffff0000; + + switch (G_BO(op)) + { + case 0x04: // branch if condition is false + case 0x05: + case 0x06: + case 0x07: + strcat(mnem, "bf"); + break; + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + strcat(mnem, "bt"); + break; + default: + return 0; + } + + if (op & M_LK) strcat(mnem, "l"); + if (op & M_AA) strcat(mnem, "a"); + + sprintf(oprs, "cr%d[%s],0x%08X", G_BI(op) / 4, crbit[G_BI(op) & 3], disp + ((op & M_AA) ? 0 : vpc)); + } + else if ((op & ~(M_RT|M_RA|M_RB|M_OE|M_RC)) == (D_OP(31)|D_XO(40))) + { + strcat(mnem, "sub"); + if (op & M_OE) strcat(mnem, "o"); + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,r%d", G_RT(op), G_RB(op), G_RA(op)); + } + else if ((op & ~(M_RT|M_RA|M_RB|M_OE|M_RC)) == (D_OP(31)|D_XO(8))) + { + strcat(mnem, "subc"); + if (op & M_OE) strcat(mnem, "o"); + if (op & M_RC) strcat(mnem, "."); + sprintf(oprs, "r%d,r%d,r%d", G_RT(op), G_RB(op), G_RA(op)); + } + else + return 0; // no match + return 1; +} + +/* + * DisassemblePowerPC(op, vpc, mnem, oprs, simplify): + * + * Disassembles one PowerPC 603e instruction. + * + * A non-zero return code indicates that the instruction could not be + * recognized or that the operands to an instruction were invalid. To + * determine which case occured, check if mnem[0] == '\0'. If it does not, + * then the latter case happened. + * + * Arguments: + * op Instruction word to disassemble. + * vpc Current instruction address. + * mnem Buffer to write instruction mnemonic to. If no + * instruction was decoded, mnem[0] and oprs[0] will be set + * to '\0'. + * oprs Buffer to write any operands to. + * simplify If non-zero, simplified forms of instructions will be + * printed in certain cases. + * + * Returns: + * Zero if successful, non-zero if the instruction was unrecognized or + * had an invalid form (see note above in function description.) + */ +bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs, + bool simplify) +{ + char signed16[12]; + uint32_t disp; + + mnem[0] = '\0'; // so we can use strcat() + oprs[0] = '\0'; + + /* + * Decode signed 16-bit fields (SIMM and d) to spare us the work later + */ + + DecodeSigned16(signed16, op, 0); + + /* + * Try simplified forms first, then real instructions + */ + + if (simplify) + { + if (Simplified(op, vpc, signed16, mnem, oprs)) + return OKAY; + } + + /* + * Search for the instruction in the list and print it if there's a match + */ + + for (size_t i = 0; i < sizeof(itab) / sizeof(IDESCR); i++) + { + if ((op & ~itab[i].mask) == itab[i].match) // check for match + { + /* + * Base mnemonic followed be O, ., L, A + */ + + strcat(mnem, itab[i].mnem); + if (itab[i].flags & FL_OE) if (op & M_OE) strcat(mnem, "o"); + if (itab[i].flags & FL_RC) if (op & M_RC) strcat(mnem, "."); + if (itab[i].flags & FL_LK) if (op & M_LK) strcat(mnem, "l"); + if (itab[i].flags & FL_AA) if (op & M_AA) strcat(mnem, "a"); + + /* + * Print operands + */ + + switch (itab[i].format) + { + case F_RT_RA_RB: + sprintf(oprs, "r%d,r%d,r%d", G_RT(op), G_RA(op), G_RB(op)); + break; + + case F_RT_RA_0_SIMM: + if (G_RA(op)) + sprintf(oprs, "r%d,r%d,%s", G_RT(op), G_RA(op), signed16); + else + sprintf(oprs, "r%d,0,%s", G_RT(op), signed16); + break; + + case F_ADDIS: + if (G_RA(op)) + sprintf(oprs, "r%d,r%d,0x%04X", G_RT(op), G_RA(op), G_SIMM(op)); + else + sprintf(oprs, "r%d,0,0x%04X", G_RT(op), G_SIMM(op)); + break; + + case F_RT_RA_SIMM: + sprintf(oprs, "r%d,r%d,%s", G_RT(op), G_RA(op), signed16); + break; + + case F_RT_RA: + sprintf(oprs, "r%d,r%d", G_RT(op), G_RA(op)); + break; + + case F_RA_RT_RB: + sprintf(oprs, "r%d,r%d,r%d", G_RA(op), G_RT(op), G_RB(op)); + break; + + case F_RA_RT_UIMM: + sprintf(oprs, "r%d,r%d,0x%04X", G_RA(op), G_RT(op), G_UIMM(op)); + break; + + case F_LI: + disp = G_LI(op) * 4; + if (disp & 0x02000000) // sign extend + disp |= 0xfc000000; + sprintf(oprs, "0x%08X", disp + ((op & M_AA) ? 0 : vpc)); + break; + + case F_BCx: + disp = G_BD(op) * 4; + if (disp & 0x00008000) + disp |= 0xffff0000; + + if (G_BO(op) & 0x10) // BI is ignored (don't print CR bit) + sprintf(oprs, "0x%02X,%d,0x%08X", G_BO(op), G_BI(op), disp + ((op & M_AA) ? 0 : vpc)); + else // BI gives us the condition bit + sprintf(oprs, "0x%02X,cr%d[%s],0x%08X", G_BO(op), G_BI(op) / 4, crbit[G_BI(op) & 3], disp + ((op & M_AA) ? 0 : vpc)); + break; + + case F_BO_BI: + if (G_BO(op) & 0x10) // BI is ignored (don't print CR bit) + sprintf(oprs, "0x%02X,%d", G_BO(op), G_BI(op)); + else + sprintf(oprs, "0x%02X,cr%d[%s]", G_BO(op), G_BI(op) / 4, crbit[G_BI(op) & 3]); + break; + + case F_CMP: + sprintf(oprs, "cr%d,%d,r%d,r%d", G_CRFD(op), G_L(op), G_RA(op), G_RB(op)); + break; + + case F_CMP_SIMM: + sprintf(oprs, "cr%d,%d,r%d,%s", G_CRFD(op), G_L(op), G_RA(op), signed16); + break; + + case F_CMP_UIMM: + sprintf(oprs, "cr%d,%d,r%d,0x%04X", G_CRFD(op), G_L(op), G_RA(op), G_UIMM(op)); + break; + + case F_RA_RT: + sprintf(oprs, "r%d,r%d", G_RA(op), G_RT(op)); + break; + + case F_CRBD_CRBA_CRBB: + sprintf(oprs, "cr%d[%s],cr%d[%s],cr%d[%s]", G_CRBD(op) / 4, crbit[G_CRBD(op) & 3], G_CRBA(op) / 4, crbit[G_CRBA(op) & 3], G_CRBB(op) / 4, crbit[G_CRBB(op) & 3]); + break; + + case F_RA_0_RB: + if (G_RA(op)) + sprintf(oprs, "r%d,r%d", G_RA(op), G_RB(op)); + else + sprintf(oprs, "0,r%d", G_RB(op)); + break; + + case F_RT_RA_0_RB: + if (G_RA(op)) + sprintf(oprs, "r%d,r%d,r%d", G_RT(op), G_RA(op), G_RB(op)); + else + sprintf(oprs, "r%d,0,r%d", G_RT(op), G_RB(op)); + break; + + case F_FRT_FRB: + sprintf(oprs, "f%d,f%d", G_RT(op), G_RB(op)); + break; + + case F_FRT_FRA_FRB: + sprintf(oprs, "f%d,f%d,f%d", G_RT(op), G_RA(op), G_RB(op)); + break; + + case F_FCMP: + sprintf(oprs, "cr%d,f%d,f%d", G_CRFD(op), G_RA(op), G_RB(op)); + break; + + case F_FRT_FRA_FRC_FRB: + sprintf(oprs, "f%d,f%d,f%d,f%d", G_RT(op), G_RA(op), G_REGC(op), G_RB(op)); + break; + + case F_FRT_FRA_FRC: + sprintf(oprs, "f%d,f%d,f%d", G_RT(op), G_RA(op), G_REGC(op)); + break; + + case F_RT_D_RA_0: + if (G_RA(op)) + sprintf(oprs, "r%d,%s(r%d)", G_RT(op), signed16, G_RA(op)); + else + sprintf(oprs, "r%d,0x%08X", G_RT(op), (uint32_t) ((INT16) G_D(op))); + break; + + case F_RT_D_RA: + sprintf(oprs, "r%d,%s(r%d)", G_RT(op), signed16, G_RA(op)); + break; + + case F_FRT_D_RA_0: + if (G_RA(op)) + sprintf(oprs, "f%d,%s(r%d)", G_RT(op), signed16, G_RA(op)); + else + sprintf(oprs, "f%d,0x%08X", G_RT(op), (uint32_t) ((INT16) G_D(op))); + break; + + case F_FRT_D_RA: + sprintf(oprs, "f%d,%s(r%d)", G_RT(op), signed16, G_RA(op)); + break; + + case F_FRT_RA_RB: + sprintf(oprs, "f%d,r%d,r%d", G_RT(op), G_RA(op), G_RB(op)); + break; + + case F_FRT_RA_0_RB: + if (G_RA(op)) + sprintf(oprs, "f%d,r%d,r%d", G_RT(op), G_RA(op), G_RB(op)); + else + sprintf(oprs, "f%d,0,r%d", G_RT(op), G_RB(op)); + break; + + case F_RT_RA_0_NB: + if (G_RA(op)) + sprintf(oprs, "r%d,r%d,%d", G_RT(op), G_RA(op), G_NB(op) ? G_NB(op) : 32); + else + sprintf(oprs, "r%d,0,%d", G_RT(op), G_NB(op) ? G_NB(op) : 32); + break; + + case F_CRFD_CRFS: + sprintf(oprs, "cr%d,cr%d", G_CRFD(op), G_CRFS(op)); + break; + + case F_MCRXR: + sprintf(oprs, "cr%d", G_CRFD(op)); + break; + + case F_RT: + sprintf(oprs, "r%d", G_RT(op)); + break; + + case F_MFFSx: + sprintf(oprs, "f%d", G_RT(op)); + break; + + case F_FCRBD: + sprintf(oprs, "fpscr[%d]", G_CRBD(op)); + break; + + case F_RT_SPR: + sprintf(oprs, "r%d,", G_RT(op)); + SPR(oprs, G_SPR(op)); + break; + + case F_MFSR: + sprintf(oprs, "r%d,sr%d", G_RT(op), G_SR(op)); + break; + + case F_MTCRF: + sprintf(oprs, "0x%02X,r%d", G_CRM(op), G_RT(op)); + break; + + case F_MTFSFx: + sprintf(oprs, "0x%02X,f%d", G_FM(op), G_RB(op)); + break; + + case F_MTFSFIx: + sprintf(oprs, "cr%d,0x%X", G_CRFD(op), G_IMM(op)); + break; + + case F_MTSPR: + SPR(oprs, G_SPR(op)); + sprintf(oprs, "%s,r%d", oprs, G_RT(op)); + break; + + case F_MTSR: + sprintf(oprs, "sr%d,r%d", G_SR(op), G_RT(op)); + break; + + case F_RT_RB: + sprintf(oprs, "r%d,r%d", G_RT(op), G_RB(op)); + break; + + case F_RA_RT_SH_MB_ME: + sprintf(oprs, "r%d,r%d,%d,%d,%d", G_RA(op), G_RT(op), G_SH(op), G_MB(op), G_ME(op)); + break; + + case F_RLWNMx: + sprintf(oprs, "r%d,r%d,r%d,%d,%d", G_RA(op), G_RT(op), G_RB(op), G_MB(op), G_ME(op)); + break; + + case F_SRAWIx: + sprintf(oprs, "r%d,r%d,%d", G_RA(op), G_RT(op), G_SH(op)); + break; + + case F_RB: + sprintf(oprs, "r%d", G_RB(op)); + break; + + case F_TW: + sprintf(oprs, "%d,r%d,r%d", G_TO(op), G_RA(op), G_RB(op)); + break; + + case F_TWI: + sprintf(oprs, "%d,r%d,%s", G_TO(op), G_RA(op), signed16); + break; + + case F_NONE: + default: + break; + } + + return Check(op, itab[i].flags); + } + } + + return FAIL; // no match found +} + + +/****************************************************************************** + Standalone Disassembler + + Define STANDALONE to build a command line-driven PowerPC disassembler. +******************************************************************************/ + +#ifdef STANDALONE + +static void PrintUsage(void) +{ + puts("ppcd Version " DISASM_VERSION " by Bart Trzynadlowski: PowerPC 603e Disassembler"); + puts("Usage: ppcd [options]"); + puts("Options: -?,-h Show this help text"); + puts(" -s Start offset (hexadecimal)"); + puts(" -l Number of instructions"); + puts(" -o Set origin (hexadecimal)"); + puts(" -big Big endian [Default]"); + puts(" -little Little endian"); + puts(" -simple Use simplified instruction forms [Default]"); + puts(" -nosimple Do not use simplified forms"); + exit(0); +} + +/* + * main(argc, argv): + * + * Standalone PowerPC disassembler. + */ +int main(int argc, char **argv) +{ + char mnem[16], oprs[48]; + FILE *fp; + uint8_t *buffer; + unsigned i, fsize, start = 0, len, org, file = 0; + uint32_t op; + bool len_specified = 0, org_specified = 0, little = 0, simple = 1; + char *c; + + + if (argc <= 1) + PrintUsage(); + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")) + PrintUsage(); + else if (!strcmp(argv[i], "-s")) + { + ++i; + if (i >= argc) + fprintf(stderr, "ppcd: warning: no argument to %s\n", "-s"); + else + start = strtoul(argv[i], &c, 16); + } + else if (!strcmp(argv[i], "-l")) + { + ++i; + if (i >= argc) + fprintf(stderr, "ppcd: warning: no argument to %s\n", "-l"); + else + { + len = atoi(argv[i]); + len_specified = 1; + } + } + else if (!strcmp(argv[i], "-o")) + { + ++i; + if (i >= argc) + fprintf(stderr, "ppcd: warning: no argument to %s\n", "-o"); + else + { + org = strtoul(argv[i], &c, 16); + org_specified = 1; + } + } + else if (!strcmp(argv[i], "-big")) + little = 0; + else if (!strcmp(argv[i], "-little")) + little = 1; + else if (!strcmp(argv[i], "-simple")) + simple = 1; + else if (!strcmp(argv[i], "-nosimple")) + simple = 0; + else + file = i; + } + + if (!file) + { + fprintf(stderr, "ppcd: no input file specified\n"); + exit(1); + } + + /* + * Load file + */ + + if ((fp = fopen(argv[file], "rb")) == NULL) + { + fprintf(stderr, "ppcd: failed to open file: %s\n", argv[file]); + exit(1); + } + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + rewind(fp); + + if ((buffer = (uint8_t *) calloc(fsize, sizeof(uint8_t))) == NULL) + { + fprintf(stderr, "ppcd: not enough memory to load input file: %s, %lu bytes\n", argv[file], (unsigned long) fsize); + fclose(fp); + exit(1); + } + fread(buffer, sizeof(uint8_t), fsize, fp); + fclose(fp); + + if (!len_specified) + len = fsize - start; + else + len *= 4; // each instruction == 4 bytes + + if (!org_specified) + org = start; + + /* + * Disassemble! + */ + + for (i = start; i < fsize && i < (start + len); i += 4, org += 4) + { + if (!little) + op = (buffer[i] << 24) | (buffer[i + 1] << 16) | + (buffer[i + 2] << 8) | buffer[i + 3]; + else + op = (buffer[i + 3] << 24) | (buffer[i + 2] << 16) | + (buffer[i + 1] << 8) | buffer[i + 0]; + + if (DisassemblePowerPC(op, org, mnem, oprs, simple)) + { + if (mnem[0] != '\0') // invalid form + printf("0x%08X: 0x%08X\t%s*\t%s\n", org, op, mnem, oprs); + else + printf("0x%08X: 0x%08X\t?\n", org, op); + } + else + printf("0x%08X: 0x%08X\t%s\t%s\n", org, op, mnem, oprs); + } + + free(buffer); + + return 0; +} + +#endif + diff --git a/Src/CPU/PowerPC/PPCDisasm.h b/Src/CPU/PowerPC/PPCDisasm.h new file mode 100644 index 0000000..c8b8314 --- /dev/null +++ b/Src/CPU/PowerPC/PPCDisasm.h @@ -0,0 +1,58 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * PPCDisasm.h + * + * Header file for PowerPC disassembler. + */ + +#ifndef INCLUDED_PPCDISASM_H +#define INCLUDED_PPCDISASM_H + +/* + * DisassemblePowerPC(op, vpc, mnem, oprs, simplify): + * + * Disassembles one PowerPC 603e instruction. + * + * A non-zero return code indicates that the instruction could not be + * recognized or that the operands to an instruction were invalid. To + * determine which case occured, check if mnem[0] == '\0'. If it does not, + * then the latter case happened. + * + * Arguments: + * op Instruction word to disassemble. + * vpc Current instruction address. + * mnem Buffer to write instruction mnemonic to. If no + * instruction was decoded, mnem[0] and oprs[0] will be set + * to '\0'. + * oprs Buffer to write any operands to. + * simplify If non-zero, simplified forms of instructions will be + * printed in certain cases. + * + * Returns: + * OKAY if successful, FAIL if the instruction was unrecognized or had an + * invalid form (see note above in function description.) + */ +extern bool DisassemblePowerPC(UINT32 op, UINT32 vpc, char *mnem, char *oprs, + bool simplify); + +#endif // INCLUDED_PPCDISASM_H diff --git a/Src/CPU/PowerPC/ppc.cpp b/Src/CPU/PowerPC/ppc.cpp new file mode 100644 index 0000000..e3ff5d9 --- /dev/null +++ b/Src/CPU/PowerPC/ppc.cpp @@ -0,0 +1,1174 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ppc.cpp + * + * PowerPC emulator main module. Written by Ville Linde for the original + * Supermodel project. + */ + +/* IBM/Motorola PowerPC 4xx/6xx Emulator */ + +#include "ppc.h" + +#include // memset() +#include "Supermodel.h" +#include "CPU/Bus.h" + +// Typedefs that Supermodel no longer provides +typedef unsigned int UINT; + +// Model 3 context provides read/write handlers +static class IBus *Bus = NULL; // pointer to Model 3 bus object (for access handlers) + +#ifdef SUPERMODEL_DEBUGGER +// Pointer to current PPC debugger (if any) +static class Debugger::CPPCDebug *PPCDebug = NULL; +#endif + +void ppc603_exception(int exception); +static void ppc603_check_interrupts(void); + +#define RD ((op >> 21) & 0x1F) +#define RT ((op >> 21) & 0x1f) +#define RS ((op >> 21) & 0x1f) +#define RA ((op >> 16) & 0x1f) +#define RB ((op >> 11) & 0x1f) +#define RC ((op >> 6) & 0x1f) + +#define MB ((op >> 6) & 0x1f) +#define ME ((op >> 1) & 0x1f) +#define SH ((op >> 11) & 0x1f) +#define BO ((op >> 21) & 0x1f) +#define BI ((op >> 16) & 0x1f) +#define CRFD ((op >> 23) & 0x7) +#define CRFA ((op >> 18) & 0x7) +#define FXM ((op >> 12) & 0xff) +#define SPR (((op >> 16) & 0x1f) | ((op >> 6) & 0x3e0)) + +#define SIMM16 (INT32)(INT16)(op & 0xffff) +#define UIMM16 (UINT32)(op & 0xffff) + +#define RCBIT (op & 0x1) +#define OEBIT (op & 0x400) +#define AABIT (op & 0x2) +#define LKBIT (op & 0x1) + +#define REG(x) (ppc.r[x]) +#define LR (ppc.lr) +#define CTR (ppc.ctr) +#define XER (ppc.xer) +#define CR(x) (ppc.cr[x]) +#define MSR (ppc.msr) +#define SRR0 (ppc.srr0) +#define SRR1 (ppc.srr1) +#define SRR2 (ppc.srr2) +#define SRR3 (ppc.srr3) +#define EVPR (ppc.evpr) +#define EXIER (ppc.exier) +#define EXISR (ppc.exisr) +#define DEC (ppc.dec) + + +// Stuff added for the 6xx +#define FPR(x) (ppc.fpr[x]) +#define FM ((op >> 17) & 0xFF) +#define SPRF (((op >> 6) & 0x3E0) | ((op >> 16) & 0x1F)) + + +#define CHECK_SUPERVISOR() \ + if((ppc.msr & 0x4000) != 0){ \ + } + +#define CHECK_FPU_AVAILABLE() \ + if((ppc.msr & 0x2000) == 0){ \ + } + +static UINT32 ppc_field_xlat[256]; + + + +#define FPSCR_FX 0x80000000 +#define FPSCR_FEX 0x40000000 +#define FPSCR_VX 0x20000000 +#define FPSCR_OX 0x10000000 +#define FPSCR_UX 0x08000000 +#define FPSCR_ZX 0x04000000 +#define FPSCR_XX 0x02000000 + + + +#define BITMASK_0(n) (UINT32)(((UINT64)1 << n) - 1) +#define CRBIT(x) ((ppc.cr[x / 4] & (1 << (3 - (x % 4)))) ? 1 : 0) +#define _BIT(n) (1 << (n)) +#define GET_ROTATE_MASK(mb,me) (ppc_rotate_mask[mb][me]) +#define ADD_CA(r,a,b) ((UINT32)r < (UINT32)a) +#define SUB_CA(r,a,b) (!((UINT32)a < (UINT32)b)) +#define ADD_OV(r,a,b) ((~((a) ^ (b)) & ((a) ^ (r))) & 0x80000000) +#define SUB_OV(r,a,b) (( ((a) ^ (b)) & ((a) ^ (r))) & 0x80000000) + +#define XER_SO 0x80000000 +#define XER_OV 0x40000000 +#define XER_CA 0x20000000 + +#define MSR_POW 0x00040000 /* Power Management Enable */ +#define MSR_WE 0x00040000 +#define MSR_CE 0x00020000 +#define MSR_ILE 0x00010000 /* Interrupt Little Endian Mode */ +#define MSR_EE 0x00008000 /* External Interrupt Enable */ +#define MSR_PR 0x00004000 /* Problem State */ +#define MSR_FP 0x00002000 /* Floating Point Available */ +#define MSR_ME 0x00001000 /* Machine Check Enable */ +#define MSR_FE0 0x00000800 +#define MSR_SE 0x00000400 /* Single Step Trace Enable */ +#define MSR_BE 0x00000200 /* Branch Trace Enable */ +#define MSR_DE 0x00000200 +#define MSR_FE1 0x00000100 +#define MSR_IP 0x00000040 /* Interrupt Prefix */ +#define MSR_IR 0x00000020 /* Instruction Relocate */ +#define MSR_DR 0x00000010 /* Data Relocate */ +#define MSR_PE 0x00000008 +#define MSR_PX 0x00000004 +#define MSR_RI 0x00000002 /* Recoverable Interrupt Enable */ +#define MSR_LE 0x00000001 + +#define TSR_ENW 0x80000000 +#define TSR_WIS 0x40000000 + +#define BYTE_REVERSE16(x) (((x >> 8) | (x << 8)) & 0xFFFF) +#define BYTE_REVERSE32(x) ((x >> 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x << 24)) + +typedef union { + UINT64 id; + double fd; +} FPR; + +typedef union { + UINT32 i; + float f; +} FPR32; + +typedef struct { + UINT32 u; + UINT32 l; +} BATENT; + + +typedef struct { + bool fatalError; // if true, halt PowerPC until hard reset + + UINT32 r[32]; + UINT32 pc; + UINT32 npc; + + UINT32 *op; + + UINT32 lr; + UINT32 ctr; + UINT32 xer; + UINT32 msr; + UINT8 cr[8]; + UINT32 pvr; + UINT32 srr0; + UINT32 srr1; + UINT32 srr2; + UINT32 srr3; + UINT32 hid0; + UINT32 hid1; + UINT32 hid2; + UINT32 sdr1; + UINT32 sprg[4]; + + UINT32 dsisr; + UINT32 dar; + UINT32 ear; + UINT32 dmiss; + UINT32 dcmp; + UINT32 hash1; + UINT32 hash2; + UINT32 imiss; + UINT32 icmp; + UINT32 rpa; + + + BATENT ibat[4]; + BATENT dbat[4]; + + UINT32 evpr; + UINT32 exier; + UINT32 exisr; + UINT32 bear; + UINT32 besr; + UINT32 iocr; + UINT32 br[8]; + UINT32 iabr; + UINT32 esr; + UINT32 iccr; + UINT32 dccr; + UINT32 pit; + UINT32 pit_counter; + UINT32 pit_int_enable; + UINT32 tsr; + UINT32 dbsr; + UINT32 sgr; + UINT32 pid; + + int reserved; + UINT32 reserved_address; + + int interrupt_pending; + int external_int; + + UINT64 tb; /* 56-bit timebase register */ + + int (*irq_callback)(int irqline); + + PPC_FETCH_REGION cur_fetch; + PPC_FETCH_REGION * fetch; + + // STUFF added for the 6xx series + UINT32 dec; + UINT32 fpscr; + + FPR fpr[32]; + UINT32 sr[16]; + + // Timing related + int timer_ratio; + UINT32 timer_frac; + int tb_base_icount; + int dec_base_icount; + int dec_trigger_cycle; + + // Cycle related + UINT64 total_cycles; + int icount; + int cur_cycles; + int bus_freq_multiplier; + int cycles_per_second; + +#if HAS_PPC603 + int is603; +#endif +#if HAS_PPC602 + int is602; +#endif +} PPC_REGS; + + + +typedef struct { + int code; + int subcode; + void (* handler)(UINT32); +} PPC_OPCODE; + + + +static PPC_REGS ppc; +static UINT32 ppc_rotate_mask[32][32]; + +static void ppc_change_pc(UINT32 newpc) +{ + if (ppc.cur_fetch.start <= newpc && newpc <= ppc.cur_fetch.end) + { + ppc.op = &ppc.cur_fetch.ptr[(newpc-ppc.cur_fetch.start)/4]; +// ppc.op = (UINT32 *)((void *)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start)); + return; + } + + for(UINT i = 0; ppc.fetch[i].ptr != NULL; i++) + { + if (ppc.fetch[i].start <= newpc && newpc <= ppc.fetch[i].end) + { + ppc.cur_fetch.start = ppc.fetch[i].start; + ppc.cur_fetch.end = ppc.fetch[i].end; + ppc.cur_fetch.ptr = ppc.fetch[i].ptr; + +// ppc.op = (UINT32 *)((UINT32)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start)); + ppc.op = &ppc.cur_fetch.ptr[(newpc-ppc.cur_fetch.start)/4]; + return; + } + } + + DebugLog("Invalid PC %08X, previous PC %08X\n", newpc, ppc.pc); + ErrorLog("PowerPC is out of bounds. Halting emulation until reset."); + ppc.fatalError = true; +} + +static inline UINT8 READ8(UINT32 address) +{ + return Bus->Read8(address); +} + +static inline UINT16 READ16(UINT32 address) +{ + return Bus->Read16(address); +} + +static inline UINT32 READ32(UINT32 address) +{ + return Bus->Read32(address); +} + +static inline UINT64 READ64(UINT32 address) +{ + return Bus->Read64(address); +} + +static inline void WRITE8(UINT32 address, UINT8 data) +{ + Bus->Write8(address,data); +} + +static inline void WRITE16(UINT32 address, UINT16 data) +{ + Bus->Write16(address,data); +} + +static inline void WRITE32(UINT32 address, UINT32 data) +{ + Bus->Write32(address,data); +} + +static inline void WRITE64(UINT32 address, UINT64 data) +{ + Bus->Write64(address,data); +} + + +/*********************************************************************/ + + +static inline void SET_CR0(INT32 rd) +{ + if( rd < 0 ) { + CR(0) = 0x8; + } else if( rd > 0 ) { + CR(0) = 0x4; + } else { + CR(0) = 0x2; + } + + if( XER & XER_SO ) + CR(0) |= 0x1; +} + +static inline void SET_CR1(void) +{ + CR(1) = (ppc.fpscr >> 28) & 0xf; +} + +static inline void SET_ADD_OV(UINT32 rd, UINT32 ra, UINT32 rb) +{ + if( ADD_OV(rd, ra, rb) ) + XER |= XER_SO | XER_OV; + else + XER &= ~XER_OV; +} + +static inline void SET_SUB_OV(UINT32 rd, UINT32 ra, UINT32 rb) +{ + if( SUB_OV(rd, ra, rb) ) + XER |= XER_SO | XER_OV; + else + XER &= ~XER_OV; +} + +static inline void SET_ADD_CA(UINT32 rd, UINT32 ra, UINT32 rb) +{ + if( ADD_CA(rd, ra, rb) ) + XER |= XER_CA; + else + XER &= ~XER_CA; +} + +static inline void SET_SUB_CA(UINT32 rd, UINT32 ra, UINT32 rb) +{ + if( SUB_CA(rd, ra, rb) ) + XER |= XER_CA; + else + XER &= ~XER_CA; +} + +static inline UINT32 check_condition_code(UINT32 bo, UINT32 bi) +{ + UINT32 bo0 = (bo & 0x10) ? 1 : 0; + UINT32 bo1 = (bo & 0x08) ? 1 : 0; + UINT32 bo2 = (bo & 0x04) ? 1 : 0; + UINT32 bo3 = (bo & 0x02) ? 1 : 0; + + if( bo2 == 0 ) + --CTR; + + UINT32 ctr_ok = bo2 | ((CTR != 0) ^ bo3); + UINT32 condition_ok = bo0 | (CRBIT(bi) ^ (bo1 ^ 0x1)); + + return ctr_ok & condition_ok; +} + +static inline UINT64 ppc_read_timebase(void) +{ + int cycles = ppc.tb_base_icount - ppc.icount; + + // Timebase is incremented according to timer ratio, so adjust value accordingly + return ppc.tb + (cycles / ppc.timer_ratio); +} + +static inline void ppc_write_timebase_l(UINT32 tbl) +{ + UINT64 tb = ppc_read_timebase(); + + ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio); + + ppc.tb = (tb&~0xffffffff)|tbl; +} + +static inline void ppc_write_timebase_h(UINT32 tbh) +{ + UINT64 tb = ppc_read_timebase(); + + ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio); + + ppc.tb = (tb&0xffffffff)|((UINT64)(tbh) << 32); +} + +static inline UINT32 read_decrementer(void) +{ + int cycles = ppc.dec_base_icount - ppc.icount; + + // Decrementer is decremented at same rate as timebase, so adjust value accordingly + return DEC - (cycles / ppc.timer_ratio); +} + +static inline void write_decrementer(UINT32 value) +{ + if (((value&0x80000000) && !(read_decrementer()&0x80000000))) + { + /* trigger interrupt */ + ppc.interrupt_pending |= 0x2; + ppc603_check_interrupts(); + } + + ppc.dec_base_icount = ppc.icount + ((ppc.dec_base_icount - ppc.icount) % ppc.timer_ratio); + + DEC = value; + + // Check if decrementer exception occurs during execution (exception occurs after decrementer + // has passed through zero) + if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC) + ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio); + else + ppc.dec_trigger_cycle = 0x7fffffff; +} + +/*********************************************************************/ + +static inline void ppc_set_spr(int spr, UINT32 value) +{ + switch (spr) + { + case SPR_LR: LR = value; return; + case SPR_CTR: CTR = value; return; + case SPR_XER: XER = value; return; + case SPR_SRR0: ppc.srr0 = value; return; + case SPR_SRR1: ppc.srr1 = value; return; + case SPR_SPRG0: ppc.sprg[0] = value; return; + case SPR_SPRG1: ppc.sprg[1] = value; return; + case SPR_SPRG2: ppc.sprg[2] = value; return; + case SPR_SPRG3: ppc.sprg[3] = value; return; + case SPR_PVR: return; + + case SPR603E_DEC: + write_decrementer(value); + return; + + case SPR603E_TBL_W: + case SPR603E_TBL_R: // special 603e case + ppc_write_timebase_l(value); + return; + + case SPR603E_TBU_R: + case SPR603E_TBU_W: // special 603e case + ppc_write_timebase_h(value); + return; + + case SPR603E_HID0: ppc.hid0 = value; return; + case SPR603E_HID1: ppc.hid1 = value; return; + case SPR603E_HID2: ppc.hid2 = value; return; + + case SPR603E_DSISR: ppc.dsisr = value; return; + case SPR603E_DAR: ppc.dar = value; return; + case SPR603E_EAR: ppc.ear = value; return; + case SPR603E_DMISS: ppc.dmiss = value; return; + case SPR603E_DCMP: ppc.dcmp = value; return; + case SPR603E_HASH1: ppc.hash1 = value; return; + case SPR603E_HASH2: ppc.hash2 = value; return; + case SPR603E_IMISS: ppc.imiss = value; return; + case SPR603E_ICMP: ppc.icmp = value; return; + case SPR603E_RPA: ppc.rpa = value; return; + + case SPR603E_IBAT0L: ppc.ibat[0].l = value; return; + case SPR603E_IBAT0U: ppc.ibat[0].u = value; return; + case SPR603E_IBAT1L: ppc.ibat[1].l = value; return; + case SPR603E_IBAT1U: ppc.ibat[1].u = value; return; + case SPR603E_IBAT2L: ppc.ibat[2].l = value; return; + case SPR603E_IBAT2U: ppc.ibat[2].u = value; return; + case SPR603E_IBAT3L: ppc.ibat[3].l = value; return; + case SPR603E_IBAT3U: ppc.ibat[3].u = value; return; + case SPR603E_DBAT0L: ppc.dbat[0].l = value; return; + case SPR603E_DBAT0U: ppc.dbat[0].u = value; return; + case SPR603E_DBAT1L: ppc.dbat[1].l = value; return; + case SPR603E_DBAT1U: ppc.dbat[1].u = value; return; + case SPR603E_DBAT2L: ppc.dbat[2].l = value; return; + case SPR603E_DBAT2U: ppc.dbat[2].u = value; return; + case SPR603E_DBAT3L: ppc.dbat[3].l = value; return; + case SPR603E_DBAT3U: ppc.dbat[3].u = value; return; + + case SPR603E_SDR1: + ppc.sdr1 = value; + return; + + case SPR603E_IABR: ppc.iabr = value; return; + } + + ErrorLog("PowerPC wrote to an invalid register. Halting emulation until reset."); + DebugLog("ppc: set_spr: unknown spr %d (%03X) !\n", spr, spr); + ppc.fatalError = true; +} + +static inline UINT32 ppc_get_spr(int spr) +{ + switch(spr) + { + case SPR_LR: return LR; + case SPR_CTR: return CTR; + case SPR_XER: return XER; + case SPR_SRR0: return ppc.srr0; + case SPR_SRR1: return ppc.srr1; + case SPR_SPRG0: return ppc.sprg[0]; + case SPR_SPRG1: return ppc.sprg[1]; + case SPR_SPRG2: return ppc.sprg[2]; + case SPR_SPRG3: return ppc.sprg[3]; + case SPR_PVR: return ppc.pvr; + case SPR603E_TBL_R: + DebugLog("ppc: get_spr: TBL_R\n"); + break; + + case SPR603E_TBU_R: + DebugLog("ppc: get_spr: TBU_R\n"); + break; + + case SPR603E_TBL_W: return (UINT32)(ppc_read_timebase()); + case SPR603E_TBU_W: return (UINT32)(ppc_read_timebase() >> 32); + case SPR603E_HID0: return ppc.hid0; + case SPR603E_HID1: return ppc.hid1; + case SPR603E_HID2: return ppc.hid2; + case SPR603E_DEC: return read_decrementer(); + case SPR603E_SDR1: return ppc.sdr1; + case SPR603E_DSISR: return ppc.dsisr; + case SPR603E_DAR: return ppc.dar; + case SPR603E_EAR: return ppc.ear; + case SPR603E_DMISS: return ppc.dmiss; + case SPR603E_DCMP: return ppc.dcmp; + case SPR603E_HASH1: return ppc.hash1; + case SPR603E_HASH2: return ppc.hash2; + case SPR603E_IMISS: return ppc.imiss; + case SPR603E_ICMP: return ppc.icmp; + case SPR603E_RPA: return ppc.rpa; + case SPR603E_IBAT0L: return ppc.ibat[0].l; + case SPR603E_IBAT0U: return ppc.ibat[0].u; + case SPR603E_IBAT1L: return ppc.ibat[1].l; + case SPR603E_IBAT1U: return ppc.ibat[1].u; + case SPR603E_IBAT2L: return ppc.ibat[2].l; + case SPR603E_IBAT2U: return ppc.ibat[2].u; + case SPR603E_IBAT3L: return ppc.ibat[3].l; + case SPR603E_IBAT3U: return ppc.ibat[3].u; + case SPR603E_DBAT0L: return ppc.dbat[0].l; + case SPR603E_DBAT0U: return ppc.dbat[0].u; + case SPR603E_DBAT1L: return ppc.dbat[1].l; + case SPR603E_DBAT1U: return ppc.dbat[1].u; + case SPR603E_DBAT2L: return ppc.dbat[2].l; + case SPR603E_DBAT2U: return ppc.dbat[2].u; + case SPR603E_DBAT3L: return ppc.dbat[3].l; + case SPR603E_DBAT3U: return ppc.dbat[3].u; + } + + ErrorLog("PowerPC read from an invalid register. Halting emulation until reset."); + DebugLog("ppc: get_spr: unknown spr %d (%03X) !\n", spr, spr); + ppc.fatalError = true; + return 0; +} + +static inline void ppc_set_msr(UINT32 value) +{ + if( value & (MSR_ILE | MSR_LE) ) + { + ErrorLog("PowerPC entered an unemulated mode. Halting emulation until reset."); + DebugLog("ppc: set_msr: little_endian mode not supported !\n"); + ppc.fatalError = true; + } + + MSR = value; + + ppc603_check_interrupts(); +} + +static inline UINT32 ppc_get_msr(void) +{ + return MSR; +} + +static inline void ppc_set_cr(UINT32 value) +{ + CR(0) = (value >> 28) & 0xf; + CR(1) = (value >> 24) & 0xf; + CR(2) = (value >> 20) & 0xf; + CR(3) = (value >> 16) & 0xf; + CR(4) = (value >> 12) & 0xf; + CR(5) = (value >> 8) & 0xf; + CR(6) = (value >> 4) & 0xf; + CR(7) = (value >> 0) & 0xf; +} + +static inline UINT32 ppc_get_cr(void) +{ + return CR(0) << 28 | CR(1) << 24 | CR(2) << 20 | CR(3) << 16 | CR(4) << 12 | CR(5) << 8 | CR(6) << 4 | CR(7); +} + +/***********************************************************************/ + +static void (* optable19[1024])(UINT32); +static void (* optable31[1024])(UINT32); +static void (* optable59[1024])(UINT32); +static void (* optable63[1024])(UINT32); +static void (* optable[64])(UINT32); + +#include "ppc603.c" + +/********************************************************************/ + +#include "ppc_ops.c" +#include "ppc_ops.h" + +/* Initialization and shutdown */ + +void ppc_base_init(void) +{ + int i,j; + + memset(&ppc, 0, sizeof(ppc)); + + for( i=0; i < 64; i++ ) { + optable[i] = ppc_invalid; + } + for( i=0; i < 1024; i++ ) { + optable19[i] = ppc_invalid; + optable31[i] = ppc_invalid; + optable59[i] = ppc_invalid; + optable63[i] = ppc_invalid; + } + + /* Fill the opcode tables */ + for( i=0; i < (sizeof(ppc_opcode_common) / sizeof(PPC_OPCODE)); i++ ) { + + switch(ppc_opcode_common[i].code) + { + case 19: + optable19[ppc_opcode_common[i].subcode] = ppc_opcode_common[i].handler; + break; + + case 31: + optable31[ppc_opcode_common[i].subcode] = ppc_opcode_common[i].handler; + break; + + case 59: + case 63: + break; + + default: + optable[ppc_opcode_common[i].code] = ppc_opcode_common[i].handler; + } + + } + + /* Calculate rotate mask table */ + for( i=0; i < 32; i++ ) { + for( j=0; j < 32; j++ ) { + UINT32 mask; + int mb = i; + int me = j; + mask = ((UINT32)0xFFFFFFFF >> mb) ^ ((me >= 31) ? 0 : ((UINT32)0xFFFFFFFF >> (me + 1))); + if( mb > me ) + mask = ~mask; + + ppc_rotate_mask[i][j] = mask; + } + } +} + +void ppc_init(const PPC_CONFIG *config) +{ + int pll_config = 0; + float multiplier; + int i ; + + ppc_base_init() ; + + optable[48] = ppc_lfs; + optable[49] = ppc_lfsu; + optable[50] = ppc_lfd; + optable[51] = ppc_lfdu; + optable[52] = ppc_stfs; + optable[53] = ppc_stfsu; + optable[54] = ppc_stfd; + optable[55] = ppc_stfdu; + optable31[631] = ppc_lfdux; + optable31[599] = ppc_lfdx; + optable31[567] = ppc_lfsux; + optable31[535] = ppc_lfsx; + optable31[595] = ppc_mfsr; + optable31[659] = ppc_mfsrin; + optable31[371] = ppc_mftb; + optable31[210] = ppc_mtsr; + optable31[242] = ppc_mtsrin; + optable31[758] = ppc_dcba; + optable31[759] = ppc_stfdux; + optable31[727] = ppc_stfdx; + optable31[983] = ppc_stfiwx; + optable31[695] = ppc_stfsux; + optable31[663] = ppc_stfsx; + optable31[370] = ppc_tlbia; + optable31[306] = ppc_tlbie; + optable31[566] = ppc_tlbsync; + optable31[310] = ppc_eciwx; + optable31[438] = ppc_ecowx; + + optable63[264] = ppc_fabsx; + optable63[21] = ppc_faddx; + optable63[32] = ppc_fcmpo; + optable63[0] = ppc_fcmpu; + optable63[14] = ppc_fctiwx; + optable63[15] = ppc_fctiwzx; + optable63[18] = ppc_fdivx; + optable63[72] = ppc_fmrx; + optable63[136] = ppc_fnabsx; + optable63[40] = ppc_fnegx; + optable63[12] = ppc_frspx; + optable63[26] = ppc_frsqrtex; + optable63[22] = ppc_fsqrtx; + optable63[20] = ppc_fsubx; + optable63[583] = ppc_mffsx; + optable63[70] = ppc_mtfsb0x; + optable63[38] = ppc_mtfsb1x; + optable63[711] = ppc_mtfsfx; + optable63[134] = ppc_mtfsfix; + optable63[64] = ppc_mcrfs; + + optable59[21] = ppc_faddsx; + optable59[18] = ppc_fdivsx; + optable59[24] = ppc_fresx; + optable59[22] = ppc_fsqrtsx; + optable59[20] = ppc_fsubsx; + + for(i = 0; i < 32; i++) + { + optable63[i * 32 | 29] = ppc_fmaddx; + optable63[i * 32 | 28] = ppc_fmsubx; + optable63[i * 32 | 25] = ppc_fmulx; + optable63[i * 32 | 31] = ppc_fnmaddx; + optable63[i * 32 | 30] = ppc_fnmsubx; + optable63[i * 32 | 23] = ppc_fselx; + + optable59[i * 32 | 29] = ppc_fmaddsx; + optable59[i * 32 | 28] = ppc_fmsubsx; + optable59[i * 32 | 25] = ppc_fmulsx; + optable59[i * 32 | 31] = ppc_fnmaddsx; + optable59[i * 32 | 30] = ppc_fnmsubsx; + } + + for(i = 0; i < 256; i++) + { + ppc_field_xlat[i] = + ((i & 0x80) ? 0xF0000000 : 0) | + ((i & 0x40) ? 0x0F000000 : 0) | + ((i & 0x20) ? 0x00F00000 : 0) | + ((i & 0x10) ? 0x000F0000 : 0) | + ((i & 0x08) ? 0x0000F000 : 0) | + ((i & 0x04) ? 0x00000F00 : 0) | + ((i & 0x02) ? 0x000000F0 : 0) | + ((i & 0x01) ? 0x0000000F : 0); + } + + ppc.pvr = config->pvr; + + multiplier = (float)((config->bus_frequency_multiplier >> 4) & 0xf) + + (float)(config->bus_frequency_multiplier & 0xf) / 10.0f; + ppc.bus_freq_multiplier = (int)(multiplier * 2); + + // tb and dec are incremented every four bus cycles, so calculate default timer ratio + ppc.timer_ratio = 2 * ppc.bus_freq_multiplier; + + switch (config->bus_frequency) + { + case BUS_FREQUENCY_16MHZ: ppc.cycles_per_second = (int)(multiplier * 16000000); break; + case BUS_FREQUENCY_20MHZ: ppc.cycles_per_second = (int)(multiplier * 20000000); break; + case BUS_FREQUENCY_25MHZ: ppc.cycles_per_second = (int)(multiplier * 25000000); break; + case BUS_FREQUENCY_33MHZ: ppc.cycles_per_second = (int)(multiplier * 33000000); break; + case BUS_FREQUENCY_40MHZ: ppc.cycles_per_second = (int)(multiplier * 40000000); break; + case BUS_FREQUENCY_50MHZ: ppc.cycles_per_second = (int)(multiplier * 50000000); break; + case BUS_FREQUENCY_60MHZ: ppc.cycles_per_second = (int)(multiplier * 60000000); break; + case BUS_FREQUENCY_66MHZ: ppc.cycles_per_second = (int)(multiplier * 66000000); break; + case BUS_FREQUENCY_75MHZ: ppc.cycles_per_second = (int)(multiplier * 75000000); break; + } + + switch(config->pvr) + { + case PPC_MODEL_603E: pll_config = mpc603e_pll_config[ppc.bus_freq_multiplier-1][config->bus_frequency]; break; + case PPC_MODEL_603EV: pll_config = mpc603ev_pll_config[ppc.bus_freq_multiplier-1][config->bus_frequency]; break; + case PPC_MODEL_603R: pll_config = mpc603r_pll_config[ppc.bus_freq_multiplier-1][config->bus_frequency]; break; + default: break; + } + + if (pll_config == -1) + { + //ErrorLog("PPC: Invalid bus/multiplier combination (bus frequency = %d, multiplier = %1.1f)", config->bus_frequency, multiplier); + } + + ppc.hid1 = pll_config << 28; +} + +void ppc_shutdown(void) +{ + +} + +void ppc_set_irq_line(int irqline) +{ + if (irqline) + { + ppc.interrupt_pending |= 0x1; + ppc603_check_interrupts(); + } + else + { + ppc.interrupt_pending &= ~0x1; + } +} + +UINT32 ppc_get_pc(void) +{ + return ppc.pc; +} + +void ppc_set_fetch(PPC_FETCH_REGION * fetch) +{ + ppc.fetch = fetch; +} + +UINT64 ppc_total_cycles(void) +{ + return ppc.total_cycles + (UINT64)(ppc.cur_cycles - ppc.icount); +} + +int ppc_get_cycles_per_sec() +{ + return ppc.cycles_per_second; +} + +int ppc_get_bus_freq_multipler() +{ + return ppc.bus_freq_multiplier; +} + +void ppc_set_timer_ratio(int ratio) +{ + ppc.timer_ratio = ratio; +} + +int ppc_get_timer_ratio() +{ + return ppc.timer_ratio; +} + +/****************************************************************************** + Supermodel Interface +******************************************************************************/ + +void ppc_attach_bus(IBus *BusPtr) +{ + Bus = BusPtr; +} + +void ppc_save_state(CBlockFile *SaveState) +{ + SaveState->NewBlock("PowerPC", __FILE__); + + // Cycle counting + SaveState->Write(&ppc.icount, sizeof(ppc.icount)); + SaveState->Write(&ppc.cur_cycles, sizeof(ppc.cur_cycles)); + SaveState->Write(&ppc.total_cycles, sizeof(ppc.total_cycles)); + + // Registers + SaveState->Write(ppc.r, sizeof(ppc.r)); + SaveState->Write(&ppc.pc, sizeof(ppc.pc)); + SaveState->Write(&ppc.npc, sizeof(ppc.npc)); + SaveState->Write(&ppc.lr, sizeof(ppc.lr)); + SaveState->Write(&ppc.ctr, sizeof(ppc.ctr)); + SaveState->Write(&ppc.xer, sizeof(ppc.xer)); + SaveState->Write(&ppc.msr, sizeof(ppc.msr)); + SaveState->Write(ppc.cr, sizeof(ppc.cr)); + SaveState->Write(&ppc.pvr, sizeof(ppc.pvr)); + SaveState->Write(&ppc.srr0, sizeof(ppc.srr0)); + SaveState->Write(&ppc.srr1, sizeof(ppc.srr1)); + SaveState->Write(&ppc.srr2, sizeof(ppc.srr2)); + SaveState->Write(&ppc.srr3, sizeof(ppc.srr3)); + SaveState->Write(&ppc.hid0, sizeof(ppc.hid0)); + SaveState->Write(&ppc.hid1, sizeof(ppc.hid1)); + SaveState->Write(&ppc.hid2, sizeof(ppc.hid2)); + SaveState->Write(&ppc.sdr1, sizeof(ppc.sdr1)); + SaveState->Write(ppc.sprg, sizeof(ppc.sprg)); + SaveState->Write(&ppc.dsisr, sizeof(ppc.dsisr)); + SaveState->Write(&ppc.dar, sizeof(ppc.dar)); + SaveState->Write(&ppc.ear, sizeof(ppc.ear)); + SaveState->Write(&ppc.dmiss, sizeof(ppc.dmiss)); + SaveState->Write(&ppc.dcmp, sizeof(ppc.dcmp)); + SaveState->Write(&ppc.hash1, sizeof(ppc.hash1)); + SaveState->Write(&ppc.hash2, sizeof(ppc.hash2)); + SaveState->Write(&ppc.imiss, sizeof(ppc.imiss)); + SaveState->Write(&ppc.icmp, sizeof(ppc.icmp)); + SaveState->Write(&ppc.rpa, sizeof(ppc.rpa)); + SaveState->Write(ppc.ibat, sizeof(ppc.ibat)); + SaveState->Write(ppc.dbat, sizeof(ppc.dbat)); + + // These are probably PPC 4xx registers, but who cares, save 'em anyway! + SaveState->Write(&ppc.evpr, sizeof(ppc.evpr)); + SaveState->Write(&ppc.exier, sizeof(ppc.exier)); + SaveState->Write(&ppc.exisr, sizeof(ppc.exisr)); + SaveState->Write(&ppc.bear, sizeof(ppc.bear)); + SaveState->Write(&ppc.besr, sizeof(ppc.besr)); + SaveState->Write(&ppc.iocr, sizeof(ppc.iocr)); + SaveState->Write(ppc.br, sizeof(ppc.br)); + SaveState->Write(&ppc.iabr, sizeof(ppc.iabr)); + SaveState->Write(&ppc.esr, sizeof(ppc.esr)); + SaveState->Write(&ppc.iccr, sizeof(ppc.iccr)); + SaveState->Write(&ppc.dccr, sizeof(ppc.dccr)); + SaveState->Write(&ppc.pit, sizeof(ppc.pit)); + SaveState->Write(&ppc.pit_counter, sizeof(ppc.pit_counter)); + SaveState->Write(&ppc.pit_int_enable, sizeof(ppc.pit_int_enable)); + SaveState->Write(&ppc.tsr, sizeof(ppc.tsr)); + SaveState->Write(&ppc.dbsr, sizeof(ppc.dbsr)); + SaveState->Write(&ppc.sgr, sizeof(ppc.sgr)); + SaveState->Write(&ppc.pid, sizeof(ppc.pid)); + + SaveState->Write(&ppc.reserved, sizeof(ppc.reserved)); + SaveState->Write(&ppc.reserved_address, sizeof(ppc.reserved_address)); + SaveState->Write(&ppc.external_int, sizeof(ppc.external_int)); + + SaveState->Write(&ppc.tb, sizeof(ppc.tb)); + + SaveState->Write(&ppc.dec, sizeof(ppc.dec)); + SaveState->Write(&ppc.timer_frac, sizeof(ppc.timer_frac)); + SaveState->Write(&ppc.fpscr, sizeof(ppc.fpscr)); + + SaveState->Write(ppc.fpr, sizeof(ppc.fpr)); + SaveState->Write(ppc.sr, sizeof(ppc.sr)); +} + +void ppc_load_state(CBlockFile *SaveState) +{ + if (OKAY != SaveState->FindBlock("PowerPC")) + { + ErrorLog("Unable to load PowerPC state. Save state file is corrupt."); + return; + } + + // Timer and decrementer + SaveState->Read(&ppc.icount, sizeof(ppc.icount)); + SaveState->Read(&ppc.cur_cycles, sizeof(ppc.cur_cycles)); + SaveState->Read(&ppc.total_cycles, sizeof(ppc.total_cycles)); + + // Registers + SaveState->Read(ppc.r, sizeof(ppc.r)); + SaveState->Read(&ppc.pc, sizeof(ppc.pc)); + SaveState->Read(&ppc.npc, sizeof(ppc.npc)); + ppc_change_pc(ppc.npc); + SaveState->Read(&ppc.lr, sizeof(ppc.lr)); + SaveState->Read(&ppc.ctr, sizeof(ppc.ctr)); + SaveState->Read(&ppc.xer, sizeof(ppc.xer)); + SaveState->Read(&ppc.msr, sizeof(ppc.msr)); + SaveState->Read(ppc.cr, sizeof(ppc.cr)); + SaveState->Read(&ppc.pvr, sizeof(ppc.pvr)); + SaveState->Read(&ppc.srr0, sizeof(ppc.srr0)); + SaveState->Read(&ppc.srr1, sizeof(ppc.srr1)); + SaveState->Read(&ppc.srr2, sizeof(ppc.srr2)); + SaveState->Read(&ppc.srr3, sizeof(ppc.srr3)); + SaveState->Read(&ppc.hid0, sizeof(ppc.hid0)); + SaveState->Read(&ppc.hid1, sizeof(ppc.hid1)); + SaveState->Read(&ppc.hid2, sizeof(ppc.hid2)); + SaveState->Read(&ppc.sdr1, sizeof(ppc.sdr1)); + SaveState->Read(ppc.sprg, sizeof(ppc.sprg)); + SaveState->Read(&ppc.dsisr, sizeof(ppc.dsisr)); + SaveState->Read(&ppc.dar, sizeof(ppc.dar)); + SaveState->Read(&ppc.ear, sizeof(ppc.ear)); + SaveState->Read(&ppc.dmiss, sizeof(ppc.dmiss)); + SaveState->Read(&ppc.dcmp, sizeof(ppc.dcmp)); + SaveState->Read(&ppc.hash1, sizeof(ppc.hash1)); + SaveState->Read(&ppc.hash2, sizeof(ppc.hash2)); + SaveState->Read(&ppc.imiss, sizeof(ppc.imiss)); + SaveState->Read(&ppc.icmp, sizeof(ppc.icmp)); + SaveState->Read(&ppc.rpa, sizeof(ppc.rpa)); + SaveState->Read(ppc.ibat, sizeof(ppc.ibat)); + SaveState->Read(ppc.dbat, sizeof(ppc.dbat)); + + SaveState->Read(&ppc.evpr, sizeof(ppc.evpr)); + SaveState->Read(&ppc.exier, sizeof(ppc.exier)); + SaveState->Read(&ppc.exisr, sizeof(ppc.exisr)); + SaveState->Read(&ppc.bear, sizeof(ppc.bear)); + SaveState->Read(&ppc.besr, sizeof(ppc.besr)); + SaveState->Read(&ppc.iocr, sizeof(ppc.iocr)); + SaveState->Read(ppc.br, sizeof(ppc.br)); + SaveState->Read(&ppc.iabr, sizeof(ppc.iabr)); + SaveState->Read(&ppc.esr, sizeof(ppc.esr)); + SaveState->Read(&ppc.iccr, sizeof(ppc.iccr)); + SaveState->Read(&ppc.dccr, sizeof(ppc.dccr)); + SaveState->Read(&ppc.pit, sizeof(ppc.pit)); + SaveState->Read(&ppc.pit_counter, sizeof(ppc.pit_counter)); + SaveState->Read(&ppc.pit_int_enable, sizeof(ppc.pit_int_enable)); + SaveState->Read(&ppc.tsr, sizeof(ppc.tsr)); + SaveState->Read(&ppc.dbsr, sizeof(ppc.dbsr)); + SaveState->Read(&ppc.sgr, sizeof(ppc.sgr)); + SaveState->Read(&ppc.pid, sizeof(ppc.pid)); + + SaveState->Read(&ppc.reserved, sizeof(ppc.reserved)); + SaveState->Read(&ppc.reserved_address, sizeof(ppc.reserved_address)); + SaveState->Read(&ppc.external_int, sizeof(ppc.external_int)); + + SaveState->Read(&ppc.tb, sizeof(ppc.tb)); + + SaveState->Read(&ppc.dec, sizeof(ppc.dec)); + SaveState->Read(&ppc.timer_frac, sizeof(ppc.timer_frac)); + SaveState->Read(&ppc.fpscr, sizeof(ppc.fpscr)); + + SaveState->Read(ppc.fpr, sizeof(ppc.fpr)); + SaveState->Read(ppc.sr, sizeof(ppc.sr)); +} + +UINT32 ppc_get_gpr(unsigned num) +{ + return ppc.r[num&31]; +} + +double ppc_get_fpr(unsigned num) +{ + return ppc.fpr[num&31].fd; +} + +UINT32 ppc_get_lr(void) +{ + return ppc.lr; +} + +UINT32 ppc_read_spr(unsigned spr) +{ + return ppc_get_spr(spr); +} + +UINT32 ppc_read_sr(unsigned num) +{ + return ppc.sr[num&15]; +} + +/****************************************************************************** + Debugger Interface +******************************************************************************/ + +#ifdef SUPERMODEL_DEBUGGER +void ppc_attach_debugger(Debugger::CPPCDebug *PPCDebugPtr) +{ + if (PPCDebug != NULL) + ppc_detach_debugger(); + PPCDebug = PPCDebugPtr; + Bus = PPCDebug->AttachBus(Bus); +} + +void ppc_detach_debugger() +{ + if (PPCDebug == NULL) + return; + Bus = PPCDebug->DetachBus(); + PPCDebug = NULL; +} + +void ppc_break() +{ + if (PPCDebug != NULL) + PPCDebug->ForceBreak(true); +} +#else // SUPERMODEL_DEBUGGER +void ppc_break() +{ + // +} +#endif // SUPERMODEL_DEBUGGER + +void ppc_set_pc(UINT32 pc) +{ + ppc.pc = pc; + ppc_change_pc(pc); + ppc.npc = pc + 4; +} + +UINT8 ppc_get_cr(unsigned num) +{ + return ppc.cr[num&7]; +} + +void ppc_set_cr(unsigned num, UINT8 val) +{ + ppc.cr[num&7] = val; +} + +void ppc_set_gpr(unsigned num, UINT32 val) +{ + ppc.r[num&31] = val; +} + +void ppc_set_fpr(unsigned num, double val) +{ + ppc.fpr[num&31].fd = val; +} + +void ppc_write_spr(unsigned spr, UINT32 val) +{ + ppc_set_spr(spr, val); +} + +void ppc_write_sr(unsigned num, UINT32 val) +{ + ppc.sr[num&15] = val; +} + +UINT32 ppc_read_msr() +{ + return ppc_get_msr(); +} diff --git a/Src/CPU/PowerPC/ppc.h b/Src/CPU/PowerPC/ppc.h new file mode 100644 index 0000000..dd8d8cb --- /dev/null +++ b/Src/CPU/PowerPC/ppc.h @@ -0,0 +1,388 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ppc.h + * + * Header file for PowerPC emulator. + */ + +#ifndef INCLUDED_PPC_H +#define INCLUDED_PPC_H + +#include "BlockFile.h" +#include "Types.h" +#include "Debugger/CPU/PPCDebug.h" + +/****************************************************************************** + Definitions +******************************************************************************/ + +#define SPR_XER 1 /* Fixed Point Exception Register Read / Write */ +#define SPR_LR 8 /* Link Register Read / Write */ +#define SPR_CTR 9 /* Count Register Read / Write */ +#define SPR_SRR0 26 /* Save/Restore Register 0 Read / Write */ +#define SPR_SRR1 27 /* Save/Restore Register 1 Read / Write */ +#define SPR_SPRG0 272 /* SPR General 0 Read / Write */ +#define SPR_SPRG1 273 /* SPR General 1 Read / Write */ +#define SPR_SPRG2 274 /* SPR General 2 Read / Write */ +#define SPR_SPRG3 275 /* SPR General 3 Read / Write */ +#define SPR_PVR 287 /* Processor Version Number Read Only */ + +#define SPR403_ICDBDR 0x3D3 /* 406GA Instruction Cache Debug Data Register Rad Only */ +#define SPR403_ESR 0x3D4 /* 406GA Exception Syndrome Register Read / Write */ +#define SPR403_DEAR 0x3D5 /* 406GA Data Exception Address Register Read Only */ +#define SPR403_EVPR 0x3D6 /* 406GA Exception Vector Prefix Register Read / Write */ +#define SPR403_CDBCR 0x3D7 /* 406GA Cache Debug Control Register Read/Write */ +#define SPR403_TSR 0x3D8 /* 406GA Timer Status Register Read / Clear */ +#define SPR403_TCR 0x3DA /* 406GA Timer Control Register Read / Write */ +#define SPR403_PIT 0x3DB /* 406GA Programmable Interval Timer Read / Write */ +#define SPR403_TBHI 988 /* 406GA Time Base High Read / Write */ +#define SPR403_TBLO 989 /* 406GA Time Base Low Read / Write */ +#define SPR403_SRR2 0x3DE /* 406GA Save/Restore Register 2 Read / Write */ +#define SPR403_SRR3 0x3DF /* 406GA Save/Restore Register 3 Read / Write */ +#define SPR403_DBSR 0x3F0 /* 406GA Debug Status Register Read / Clear */ +#define SPR403_DBCR 0x3F2 /* 406GA Debug Control Register Read / Write */ +#define SPR403_IAC1 0x3F4 /* 406GA Instruction Address Compare 1 Read / Write */ +#define SPR403_IAC2 0x3F5 /* 406GA Instruction Address Compare 2 Read / Write */ +#define SPR403_DAC1 0x3F6 /* 406GA Data Address Compare 1 Read / Write */ +#define SPR403_DAC2 0x3F7 /* 406GA Data Address Compare 2 Read / Write */ +#define SPR403_DCCR 0x3FA /* 406GA Data Cache Cacheability Register Read / Write */ +#define SPR403_ICCR 0x3FB /* 406GA I Cache Cacheability Registe Read / Write */ +#define SPR403_PBL1 0x3FC /* 406GA Protection Bound Lower 1 Read / Write */ +#define SPR403_PBU1 0x3FD /* 406GA Protection Bound Upper 1 Read / Write */ +#define SPR403_PBL2 0x3FE /* 406GA Protection Bound Lower 2 Read / Write */ +#define SPR403_PBU2 0x3FF /* 406GA Protection Bound Upper 2 Read / Write */ + +#define SPR403_SGR 0x3b9 /* 403GCX Storage Guarded Register */ +#define SPR403_DCWR 0x3ba /* 403GCX Data Cache Write Through */ +#define SPR403_PID 0x3b1 /* 403GCX Process ID */ +#define SPR403_TBHU 0x3cc /* 403GCX Time Base High User-mode */ +#define SPR403_TBLU 0x3cd /* 403GCX Time Base Low User-mode */ + +#define SPR603E_DSISR 18 /* 603E */ +#define SPR603E_DAR 19 /* 603E */ +#define SPR603E_DEC 22 /* 603E */ +#define SPR603E_SDR1 25 /* 603E */ +#define SPR603E_TBL_R 268 /* 603E Time Base Low (Read-only) */ +#define SPR603E_TBU_R 269 /* 603E Time Base High (Read-only) */ +#define SPR603E_TBL_W 284 /* 603E Time Base Low (Write-only) */ +#define SPR603E_TBU_W 285 /* 603E Time Base Hight (Write-only) */ +#define SPR603E_EAR 282 /* 603E */ +#define SPR603E_IBAT0U 528 /* 603E */ +#define SPR603E_IBAT0L 529 /* 603E */ +#define SPR603E_IBAT1U 530 /* 603E */ +#define SPR603E_IBAT1L 531 /* 603E */ +#define SPR603E_IBAT2U 532 /* 603E */ +#define SPR603E_IBAT2L 533 /* 603E */ +#define SPR603E_IBAT3U 534 /* 603E */ +#define SPR603E_IBAT3L 535 /* 603E */ +#define SPR603E_DBAT0U 536 /* 603E */ +#define SPR603E_DBAT0L 537 /* 603E */ +#define SPR603E_DBAT1U 538 /* 603E */ +#define SPR603E_DBAT1L 539 /* 603E */ +#define SPR603E_DBAT2U 540 /* 603E */ +#define SPR603E_DBAT2L 541 /* 603E */ +#define SPR603E_DBAT3U 542 /* 603E */ +#define SPR603E_DBAT3L 543 /* 603E */ +#define SPR603E_DABR 1013 /* 603E */ +#define SPR603E_DMISS 0x3d0 /* 603E */ +#define SPR603E_DCMP 0x3d1 /* 603E */ +#define SPR603E_HASH1 0x3d2 /* 603E */ +#define SPR603E_HASH2 0x3d3 /* 603E */ +#define SPR603E_IMISS 0x3d4 /* 603E */ +#define SPR603E_ICMP 0x3d5 /* 603E */ +#define SPR603E_RPA 0x3d6 /* 603E */ +#define SPR603E_HID0 1008 /* 603E */ +#define SPR603E_HID1 1009 /* 603E */ +#define SPR603E_IABR 1010 /* 603E */ +#define SPR603E_HID2 1011 /* 603E */ + +#define SPR602_TCR 984 /* 602 */ +#define SPR602_IBR 986 /* 602 */ +#define SPR602_SP 1021 /* 602 */ +#define SPR602_LT 1022 /* 602 */ + + +#define DCR_BEAR 0x90 /* bus error address */ +#define DCR_BESR 0x91 /* bus error syndrome */ +#define DCR_BR0 0x80 /* bank */ +#define DCR_BR1 0x81 /* bank */ +#define DCR_BR2 0x82 /* bank */ +#define DCR_BR3 0x83 /* bank */ +#define DCR_BR4 0x84 /* bank */ +#define DCR_BR5 0x85 /* bank */ +#define DCR_BR6 0x86 /* bank */ +#define DCR_BR7 0x87 /* bank */ +#define DCR_DMACC0 0xc4 /* dma chained count */ +#define DCR_DMACC1 0xcc /* dma chained count */ +#define DCR_DMACC2 0xd4 /* dma chained count */ +#define DCR_DMACC3 0xdc /* dma chained count */ +#define DCR_DMACR0 0xc0 /* dma channel control */ +#define DCR_DMACR1 0xc8 /* dma channel control */ +#define DCR_DMACR2 0xd0 /* dma channel control */ +#define DCR_DMACR3 0xd8 /* dma channel control */ +#define DCR_DMACT0 0xc1 /* dma destination address */ +#define DCR_DMACT1 0xc9 /* dma destination address */ +#define DCR_DMACT2 0xd1 /* dma destination address */ +#define DCR_DMACT3 0xd9 /* dma destination address */ +#define DCR_DMADA0 0xc2 /* dma destination address */ +#define DCR_DMADA1 0xca /* dma destination address */ +#define DCR_DMADA2 0xd2 /* dma source address */ +#define DCR_DMADA3 0xda /* dma source address */ +#define DCR_DMASA0 0xc3 /* dma source address */ +#define DCR_DMASA1 0xcb /* dma source address */ +#define DCR_DMASA2 0xd3 /* dma source address */ +#define DCR_DMASA3 0xdb /* dma source address */ +#define DCR_DMASR 0xe0 /* dma status */ +#define DCR_EXIER 0x42 /* external interrupt enable */ +#define DCR_EXISR 0x40 /* external interrupt status */ +#define DCR_IOCR 0xa0 /* io configuration */ + +enum { + EXCEPTION_IRQ = 1, + EXCEPTION_DECREMENTER = 2, + EXCEPTION_TRAP = 3, + EXCEPTION_SYSTEM_CALL = 4, + EXCEPTION_SMI = 5, + EXCEPTION_DSI = 6, + EXCEPTION_ISI = 7, + EXCEPTION_PROGRAMMABLE_INTERVAL_TIMER = 20, + EXCEPTION_FIXED_INTERVAL_TIMER = 21, + EXCEPTION_WATCHDOG_TIMER = 22, + EXCEPTION_CRITICAL_INTERRUPT = 23, +}; + +enum { + PPC_INPUT_LINE_SMI = 10, + PPC_INPUT_LINE_TLBISYNC +}; + +enum { + PPC_PC=1, + PPC_MSR, + PPC_CR, + PPC_LR, + PPC_CTR, + PPC_XER, + PPC_DEC, + PPC_SRR0, + PPC_SRR1, + PPC_R0, + PPC_R1, + PPC_R2, + PPC_R3, + PPC_R4, + PPC_R5, + PPC_R6, + PPC_R7, + PPC_R8, + PPC_R9, + PPC_R10, + PPC_R11, + PPC_R12, + PPC_R13, + PPC_R14, + PPC_R15, + PPC_R16, + PPC_R17, + PPC_R18, + PPC_R19, + PPC_R20, + PPC_R21, + PPC_R22, + PPC_R23, + PPC_R24, + PPC_R25, + PPC_R26, + PPC_R27, + PPC_R28, + PPC_R29, + PPC_R30, + PPC_R31 +}; + +typedef enum { + PPC_MODEL_403GA = 0x00200000, + PPC_MODEL_403GB = 0x00200100, + PPC_MODEL_403GC = 0x00200200, + PPC_MODEL_403GCX = 0x00201400, + PPC_MODEL_405GP = 0x40110000, + PPC_MODEL_601 = 0x00010000, + PPC_MODEL_603 = 0x00030000, /* "Wart" */ + PPC_MODEL_604 = 0x00040000, /* "Zephyr" */ + PPC_MODEL_602 = 0x00050000, /* "Galahad" */ + PPC_MODEL_603E = 0x00060103, /* "Stretch", version 1.3 */ + PPC_MODEL_603EV = 0x00070000, /* "Valiant" */ + PPC_MODEL_603R = 0x00071202, /* "Goldeneye", version 2.1 */ + PPC_MODEL_740 = 0x00080301, /* "Arthur", version 3.1 */ + PPC_MODEL_750 = PPC_MODEL_740, + PPC_MODEL_740P = 0x00080202, /* "Conan Doyle", version 1.2 */ + PPC_MODEL_750P = PPC_MODEL_740P, + PPC_MODEL_755 = 0x00083203 /* "Goldfinger", version 2.3 */ +} PPC_MODEL; + +typedef enum { + BUS_FREQUENCY_16MHZ = 0, + BUS_FREQUENCY_20MHZ, + BUS_FREQUENCY_25MHZ, + BUS_FREQUENCY_33MHZ, + BUS_FREQUENCY_40MHZ, + BUS_FREQUENCY_50MHZ, + BUS_FREQUENCY_60MHZ, + BUS_FREQUENCY_66MHZ, + BUS_FREQUENCY_75MHZ, +} PPC_BUS_FREQUENCY; + +// PLL Configuration based on the table in MPC603EUM page 7-31 +static const int mpc603e_pll_config[12][9] = +{ + // 16, 20, 25, 33, 40, 50, 60, 66, 75 + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, -1, 0x0, -1 }, + { -1, -1, -1, -1, -1, 0xc, -1, 0xc, -1 }, + { 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, -1, -1, -1 }, + { -1, -1, -1, 0x6, 0x6, -1, -1, -1, -1 }, + { -1, -1, 0x8, 0x8, -1, -1, -1, -1, -1 }, + { -1, 0xe, 0xe, -1, -1, -1, -1, -1, -1 }, + { 0xa, 0xa, 0xa, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +}; + +// PLL Configuration based on the table in MPC603E7VEC page 29 +static const int mpc603ev_pll_config[12][9] = +{ + // 16, 20, 25, 33, 40, 50, 60, 66, 75 + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + // 2:1 + { -1, -1, -1, -1, -1, -1, -1, 0x4, 0x4 }, + // 2.5:1 + { -1, -1, -1, -1, -1, 0x6, 0x6, 0x6, 0x6 }, + // 3:1 + { -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8, 0x8 }, + // 3.5:1 + { -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe, -1 }, + // 4:1 + { -1, -1, -1, 0xa, 0xa, 0xa, 0xa, -1, -1 }, + // 4.5:1 + { -1, -1, -1, 0x7, 0x7, 0x7, -1, -1, -1 }, + // 5:1 + { -1, -1, 0xb, 0xb, 0xb, -1, -1, -1, -1 }, + // 5.5:1 + { -1, -1, 0x9, 0x9, 0x9, -1, -1, -1, -1 }, + // 6:1 + { -1, -1, 0xd, 0xd, 0xd, -1, -1, -1, -1 } +}; + +// PLL Configuration based on the table in MPC603E7TEC page 23 +static const int mpc603r_pll_config[12][9] = +{ + // 16, 20, 25, 33, 40, 50, 60, 66, 75 + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + // 2:1 + { -1, -1, -1, -1, 0x5, 0x5, 0x5, 0x5, 0x5 }, + // 2.5:1 + { -1, -1, -1, -1, -1, -1, 0x6, 0x6, 0x6 }, + // 3:1 + { -1, -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8 }, + // 3.5:1 + { -1, -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe }, + // 4:1 + { -1, -1, -1, -1, 0xa, 0xa, 0xa, 0xa, 0xa }, + // 4.5:1 + { -1, -1, -1, 0x7, 0x7, 0x7, 0x7, 0x7, -1 }, + // 5:1 + { -1, -1, -1, 0xb, 0xb, 0xb, 0xb, -1, -1 }, + // 5.5:1 + { -1, -1, -1, 0x9, 0x9, 0x9, -1, -1, -1 }, + // 6:1 + { -1, -1, 0xd, 0xd, 0xd, 0xd, -1, -1, -1 }, +}; + + +/****************************************************************************** + Configuration Data Structures +******************************************************************************/ + +typedef struct { + PPC_MODEL pvr; + int bus_frequency_multiplier; + PPC_BUS_FREQUENCY bus_frequency; +} PPC_CONFIG; + +typedef struct +{ + UINT32 start; + UINT32 end; + UINT32 * ptr; + +} PPC_FETCH_REGION; + + +/****************************************************************************** + Functions +******************************************************************************/ + +extern UINT32 ppc_get_pc(void); +extern void ppc_set_irq_line(int irqline); +extern int ppc_execute(int cycles); +extern void ppc_reset(void); +extern void ppc_shutdown(void); +extern void ppc_init(const PPC_CONFIG *config); // must be called second! +extern void ppc_set_fetch(PPC_FETCH_REGION * fetch); +extern UINT64 ppc_total_cycles(void); +extern int ppc_get_cycles_per_sec(void); +extern int ppc_get_bus_freq_multipler(void); +extern int ppc_get_timer_ratio(void); +extern void ppc_set_timer_ratio(int ratio); + +// These have been added to support the new Supermodel +extern void ppc_attach_bus(class IBus *BusPtr); // must be called first! +extern void ppc_save_state(class CBlockFile *SaveState); +extern void ppc_load_state(class CBlockFile *SaveState); +extern UINT32 ppc_get_gpr(unsigned num); +extern double ppc_get_fpr(unsigned num); +extern UINT32 ppc_get_lr(void); +extern UINT32 ppc_read_spr(unsigned spr); +extern UINT32 ppc_read_sr(unsigned num); + +#ifdef SUPERMODEL_DEBUGGER +// These have been added to support the Supermodel debugger +extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr); +extern void ppc_detach_debugger(); +#endif // SUPERMODEL_DEBUGGER +extern void ppc_break(); +extern void ppc_set_pc(UINT32 pc); +extern UINT8 ppc_get_cr(unsigned num); +extern void ppc_set_cr(unsigned num, UINT8 val); +extern void ppc_set_gpr(unsigned num, UINT32 val); +extern void ppc_set_fpr(unsigned num, double val); +extern void ppc_write_spr(unsigned spr, UINT32 val); +extern void ppc_write_sr(unsigned num, UINT32 val); +extern UINT32 ppc_read_msr(); +#endif // INCLUDED_PPC_H diff --git a/Src/CPU/PowerPC/ppc603.c b/Src/CPU/PowerPC/ppc603.c new file mode 100644 index 0000000..79e9582 --- /dev/null +++ b/Src/CPU/PowerPC/ppc603.c @@ -0,0 +1,363 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ppc603.c + * + * PowerPC 603e functions. Included from ppc.cpp; do not compile separately. + */ + +void ppc603_exception(int exception) +{ +#ifdef SUPERMODEL_DEBUGGER + if (PPCDebug != NULL) + PPCDebug->CPUException(exception); +#endif + + switch( exception ) + { + case EXCEPTION_IRQ: /* External Interrupt */ + if( ppc_get_msr() & MSR_EE ) { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = msr & 0xff73; + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0500; + else + ppc.npc = 0x00000000 | 0x0500; + + //MAME has this: ppc.interrupt_pending &= ~0x1; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_DECREMENTER: /* Decrementer overflow exception */ + if( ppc_get_msr() & MSR_EE ) { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = msr & 0xff73; + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0900; + else + ppc.npc = 0x00000000 | 0x0900; + + ppc.interrupt_pending &= ~0x2; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_TRAP: /* Program exception / Trap */ + { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.pc; + SRR1 = (msr & 0xff73) | 0x20000; /* 0x20000 = TRAP bit */ + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0700; + else + ppc.npc = 0x00000000 | 0x0700; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_SYSTEM_CALL: /* System call */ + { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = (msr & 0xff73); + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0c00; + else + ppc.npc = 0x00000000 | 0x0c00; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_SMI: + if( ppc_get_msr() & MSR_EE ) { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = msr & 0xff73; + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x1400; + else + ppc.npc = 0x00000000 | 0x1400; + + ppc.interrupt_pending &= ~0x4; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_DSI: + { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = msr & 0xff73; + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0300; + else + ppc.npc = 0x00000000 | 0x0300; + + ppc.interrupt_pending &= ~0x4; + ppc_change_pc(ppc.npc); + } + break; + + case EXCEPTION_ISI: + { + UINT32 msr = ppc_get_msr(); + + SRR0 = ppc.npc; + SRR1 = msr & 0xff73; + + msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI); + if( msr & MSR_ILE ) + msr |= MSR_LE; + else + msr &= ~MSR_LE; + ppc_set_msr(msr); + + if( msr & MSR_IP ) + ppc.npc = 0xfff00000 | 0x0400; + else + ppc.npc = 0x00000000 | 0x0400; + + ppc.interrupt_pending &= ~0x4; + ppc_change_pc(ppc.npc); + } + break; + + default: + ErrorLog("PowerPC triggered an unknown exception. Emulation halted until reset."); + DebugLog("PowerPC triggered an unknown exception (%d).\n", exception); + ppc.fatalError = true; + break; + } +} + +static void ppc603_set_smi_line(int state) +{ + if( state ) { + ppc.interrupt_pending |= 0x4; + } +} + +static void ppc603_check_interrupts(void) +{ + if (MSR & MSR_EE) + { + if (ppc.interrupt_pending != 0) + { + if (ppc.interrupt_pending & 0x1) + { + ppc603_exception(EXCEPTION_IRQ); + } + else if (ppc.interrupt_pending & 0x2) + { + ppc603_exception(EXCEPTION_DECREMENTER); + } + else if (ppc.interrupt_pending & 0x4) + { + ppc603_exception(EXCEPTION_SMI); + } + } + } +} + +void ppc_reset(void) +{ + ppc.fatalError = false; // reset the fatal error flag + + ppc.pc = ppc.npc = 0xfff00100; + + ppc_set_msr(0x40); + ppc_change_pc(ppc.pc); + + ppc.hid0 = 1; + + ppc.interrupt_pending = 0; + + ppc.tb = 0; + ppc.timer_frac = 0; + DEC = 0xffffffff; + ppc.total_cycles = 0; + ppc.cur_cycles = 0; + ppc.icount = 0; +} + +int ppc_execute(int cycles) +{ + UINT32 opcode; + + ppc.cur_cycles = cycles; + ppc.icount = cycles; + ppc.tb_base_icount = cycles + ppc.timer_frac; + ppc.dec_base_icount = cycles + ppc.timer_frac; + + // Check if decrementer exception occurs during execution (exception occurs after decrementer + // has passed through zero) + if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC) + ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio); + else + ppc.dec_trigger_cycle = 0x7fffffff; + + ppc_change_pc(ppc.npc); + + /*{ + char string1[200]; + char string2[200]; + opcode = BSWAP32(*ppc.op); + DisassemblePowerPC(opcode, ppc.npc, string1, string2, true); + printf("%08X: %s %s\n", ppc.npc, string1, string2); + }*/ + + ppc603_check_interrupts(); + +#ifdef SUPERMODEL_DEBUGGER + if (PPCDebug != NULL) + PPCDebug->CPUActive(); +#endif // SUPERMODEL_DEBUGGER + + while( ppc.icount > 0 && !ppc.fatalError) + { + ppc.pc = ppc.npc; + + // Debug breakpoints + /* + if (ppc.pc == 0x9d40) + { + printf("%X R3=%08X R4=%08X\n", ppc.pc, REG(3), REG(4)); + + } + */ + + opcode = *ppc.op++; // Supermodel byte reverses each aligned word (converting them to little endian) so they can be fetched directly + ppc.npc = ppc.pc + 4; + +#ifdef SUPERMODEL_DEBUGGER + if (PPCDebug != NULL) + { + while (PPCDebug->CPUExecute(ppc.pc, opcode, (PPCDebug->instrCount > 0 ? 1 : 0))) + opcode = *ppc.op++; + } +#endif // SUPERMODEL_DEBUGGER + + switch(opcode >> 26) + { + case 19: optable19[(opcode >> 1) & 0x3ff](opcode); break; + case 31: optable31[(opcode >> 1) & 0x3ff](opcode); break; + case 59: optable59[(opcode >> 1) & 0x3ff](opcode); break; + case 63: optable63[(opcode >> 1) & 0x3ff](opcode); break; + default: optable[opcode >> 26](opcode); break; + } + + ppc.icount--; + + if (ppc.icount == ppc.dec_trigger_cycle) + { + ppc.interrupt_pending |= 0x2; + ppc603_check_interrupts(); + } + + //ppc603_check_interrupts(); + } + +#ifdef SUPERMODEL_DEBUGGER + if (PPCDebug != NULL) + PPCDebug->CPUInactive(); +#endif // SUPERMODEL_DEBUGGER + + // Update timebase and decrementer. Both are updated at same rate as specified by timer_ratio. + ppc.timer_frac = ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio); + ppc.tb += ((ppc.tb_base_icount - ppc.icount) / ppc.timer_ratio); + DEC -= ((ppc.dec_base_icount - ppc.icount) / ppc.timer_ratio); + + /* + { + char string1[200]; + char string2[200]; + opcode = BSWAP32(*ppc.op); + DisassemblePowerPC(opcode, ppc.npc, string1, string2, true); + printf("%08X: %s %s\n", ppc.npc, string1, string2); + } + */ + + int executed = cycles - ppc.icount; + ppc.total_cycles += executed; + ppc.cur_cycles = 0; + ppc.icount = 0; + ppc.tb_base_icount = 0; + ppc.dec_base_icount = 0; + return executed; +} diff --git a/Src/CPU/PowerPC/ppc_ops.c b/Src/CPU/PowerPC/ppc_ops.c new file mode 100644 index 0000000..2fb2e61 --- /dev/null +++ b/Src/CPU/PowerPC/ppc_ops.c @@ -0,0 +1,2797 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ppc_ops.c + * + * PowerPC common opcodes. Included from ppc.cpp; do not compile compile + * separately. + * + * Changes to opcode handlers since inclusion in new Supermodel: + * - Feb. 13 2011: Changed stwcx. to always set the EQ flag. + */ + +/* PowerPC common opcodes */ + +// it really seems like this should be elsewhere - like maybe the floating point checks can hang out someplace else +#include + +static void ppc_unimplemented(UINT32 op) +{ + ErrorLog("PowerPC hit an unimplemented instruction. Halting emulation until reset."); + DebugLog("PowerPC encountered an unimplemented opcode %08X at %08X\n", op, ppc.pc); + ppc.fatalError = true; +} + +static void ppc_addx(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + + REG(RT) = ra + rb; + + if( OEBIT ) { + SET_ADD_OV(REG(RT), ra, rb); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_addcx(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + + REG(RT) = ra + rb; + + SET_ADD_CA(REG(RT), ra, rb); + + if( OEBIT ) { + SET_ADD_OV(REG(RT), ra, rb); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_addex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + UINT32 carry = (XER >> 29) & 0x1; + UINT32 tmp; + + tmp = rb + carry; + REG(RT) = ra + tmp; + + if( ADD_CA(tmp, rb, carry) || ADD_CA(REG(RT), ra, tmp) ) + XER |= XER_CA; + else + XER &= ~XER_CA; + + if( OEBIT ) { + SET_ADD_OV(REG(RT), ra, rb); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_addi(UINT32 op) +{ + UINT32 i = SIMM16; + UINT32 a = RA; + + if( a ) + i += REG(a); + + REG(RT) = i; +} + +static void ppc_addic(UINT32 op) +{ + UINT32 i = SIMM16; + UINT32 ra = REG(RA); + + REG(RT) = ra + i; + + if( ADD_CA(REG(RT), ra, i) ) + XER |= XER_CA; + else + XER &= ~XER_CA; +} + +static void ppc_addic_rc(UINT32 op) +{ + UINT32 i = SIMM16; + UINT32 ra = REG(RA); + + REG(RT) = ra + i; + + if( ADD_CA(REG(RT), ra, i) ) + XER |= XER_CA; + else + XER &= ~XER_CA; + + SET_CR0(REG(RT)); +} + +static void ppc_addis(UINT32 op) +{ + UINT32 i = UIMM16 << 16; + UINT32 a = RA; + + if( a ) + i += REG(a); + + REG(RT) = i; +} + +static void ppc_addmex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 carry = (XER >> 29) & 0x1; + UINT32 tmp; + + tmp = ra + carry; + REG(RT) = tmp + -1; + + if( ADD_CA(tmp, ra, carry) || ADD_CA(REG(RT), tmp, -1) ) + XER |= XER_CA; + else + XER &= ~XER_CA; + + if( OEBIT ) { + SET_ADD_OV(REG(RT), ra, carry - 1); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_addzex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 carry = (XER >> 29) & 0x1; + + REG(RT) = ra + carry; + + if( ADD_CA(REG(RT), ra, carry) ) + XER |= XER_CA; + else + XER &= ~XER_CA; + + if( OEBIT ) { + SET_ADD_OV(REG(RT), ra, carry); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_andx(UINT32 op) +{ + REG(RA) = REG(RS) & REG(RB); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_andcx(UINT32 op) +{ + REG(RA) = REG(RS) & ~REG(RB); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_andi_rc(UINT32 op) +{ + UINT32 i = UIMM16; + + REG(RA) = REG(RS) & i; + + SET_CR0(REG(RA)); +} + +static void ppc_andis_rc(UINT32 op) +{ + UINT32 i = UIMM16 << 16; + + REG(RA) = REG(RS) & i; + + SET_CR0(REG(RA)); +} + +static void ppc_bx(UINT32 op) +{ + INT32 li = op & 0x3fffffc; + if( li & 0x2000000 ) + li |= 0xfc000000; + + ppc.npc = li; + + if( !AABIT ) { + ppc.npc += ppc.pc; + } + + if( LKBIT ) { + LR = ppc.pc + 4; + } + + ppc_change_pc(ppc.npc); +} + +static void ppc_bcx(UINT32 op) +{ + int condition = check_condition_code(BO, BI); + + if( condition ) { + ppc.npc = SIMM16 & ~0x3; + if( !AABIT ) + ppc.npc += ppc.pc; + + ppc_change_pc(ppc.npc); + } + + if( LKBIT ) { + LR = ppc.pc + 4; + } +} + +static void ppc_bcctrx(UINT32 op) +{ + int condition = check_condition_code(BO, BI); + + if( condition ) { + ppc.npc = CTR & ~0x3; + ppc_change_pc(ppc.npc); + } + + if( LKBIT ) { + LR = ppc.pc + 4; + } +} + +static void ppc_bclrx(UINT32 op) +{ + int condition = check_condition_code(BO, BI); + + if( condition ) { + ppc.npc = LR & ~0x3; + ppc_change_pc(ppc.npc); + } + + if( LKBIT ) { + LR = ppc.pc + 4; + } +} + +static void ppc_cmp(UINT32 op) +{ + INT32 ra = REG(RA); + INT32 rb = REG(RB); + int d = CRFD; + + if( ra < rb ) + CR(d) = 0x8; + else if( ra > rb ) + CR(d) = 0x4; + else + CR(d) = 0x2; + + if( XER & XER_SO ) + CR(d) |= 0x1; +} + +static void ppc_cmpi(UINT32 op) +{ + INT32 ra = REG(RA); + INT32 i = SIMM16; + int d = CRFD; + + if( ra < i ) + CR(d) = 0x8; + else if( ra > i ) + CR(d) = 0x4; + else + CR(d) = 0x2; + + if( XER & XER_SO ) + CR(d) |= 0x1; +} + +static void ppc_cmpl(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + int d = CRFD; + + if( ra < rb ) + CR(d) = 0x8; + else if( ra > rb ) + CR(d) = 0x4; + else + CR(d) = 0x2; + + if( XER & XER_SO ) + CR(d) |= 0x1; +} + +static void ppc_cmpli(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 i = UIMM16; + int d = CRFD; + + if( ra < i ) + CR(d) = 0x8; + else if( ra > i ) + CR(d) = 0x4; + else + CR(d) = 0x2; + + if( XER & XER_SO ) + CR(d) |= 0x1; +} + +static void ppc_cntlzw(UINT32 op) +{ + int n = 0; + int t = RT; + UINT32 m = 0x80000000; + + while(n < 32) + { + if( REG(t) & m ) + break; + m >>= 1; + n++; + } + + REG(RA) = n; + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_crand(UINT32 op) +{ + int bit = RT; + int b = CRBIT(RA) & CRBIT(RB); + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_crandc(UINT32 op) +{ + int bit = RT; + int b = CRBIT(RA) & (CRBIT(RB) ^ 0x1); + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_creqv(UINT32 op) +{ + int bit = RT; + int b = (CRBIT(RA) ^ CRBIT(RB)) ^ 0x1; + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_crnand(UINT32 op) +{ + int bit = RT; + int b = (CRBIT(RA) & CRBIT(RB)) ^ 0x1; + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_crnor(UINT32 op) +{ + int bit = RT; + int b = (CRBIT(RA) | CRBIT(RB)) ^ 0x1; + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_cror(UINT32 op) +{ + int bit = RT; + int b = CRBIT(RA) | CRBIT(RB); + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_crorc(UINT32 op) +{ + int bit = RT; + int b = CRBIT(RA) | (CRBIT(RB) ^ 0x1); + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_crxor(UINT32 op) +{ + int bit = RT; + int b = CRBIT(RA) ^ CRBIT(RB); + if( b ) + CR(bit / 4) |= _BIT(3-(bit % 4)); + else + CR(bit / 4) &= ~_BIT(3-(bit % 4)); +} + +static void ppc_dcbf(UINT32 op) +{ + +} + +static void ppc_dcbi(UINT32 op) +{ + +} + +static void ppc_dcbst(UINT32 op) +{ + +} + +static void ppc_dcbt(UINT32 op) +{ + +} + +static void ppc_dcbtst(UINT32 op) +{ + +} + +static void ppc_dcbz(UINT32 op) +{ + +} + +static void ppc_divwx(UINT32 op) +{ + if( REG(RB) == 0 && REG(RA) < 0x80000000 ) + { + REG(RT) = 0; + if( OEBIT ) { + XER |= XER_SO | XER_OV; + } + } + else if( REG(RB) == 0 || (REG(RB) == 0xffffffff && REG(RA) == 0x80000000) ) + { + REG(RT) = 0xffffffff; + if( OEBIT ) { + XER |= XER_SO | XER_OV; + } + } + else + { + REG(RT) = (INT32)REG(RA) / (INT32)REG(RB); + if( OEBIT ) { + XER &= ~XER_OV; + } + } + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_divwux(UINT32 op) +{ + if( REG(RB) == 0 ) + { + REG(RT) = 0; + if( OEBIT ) { + XER |= XER_SO | XER_OV; + } + } + else + { + REG(RT) = (UINT32)REG(RA) / (UINT32)REG(RB); + if( OEBIT ) { + XER &= ~XER_OV; + } + } + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_eieio(UINT32 op) +{ + +} + +static void ppc_eqvx(UINT32 op) +{ + REG(RA) = ~(REG(RS) ^ REG(RB)); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_extsbx(UINT32 op) +{ + REG(RA) = (INT32)(INT8)REG(RS); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_extshx(UINT32 op) +{ + REG(RA) = (INT32)(INT16)REG(RS); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_icbi(UINT32 op) +{ + +} + +static void ppc_isync(UINT32 op) +{ + +} + +static void ppc_lbz(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (UINT32)READ8(ea); +} + +static void ppc_lbzu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + REG(RT) = (UINT32)READ8(ea); + REG(RA) = ea; +} + +static void ppc_lbzux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + REG(RT) = (UINT32)READ8(ea); + REG(RA) = ea; +} + +static void ppc_lbzx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (UINT32)READ8(ea); +} + +static void ppc_lha(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (INT32)(INT16)READ16(ea); +} + +static void ppc_lhau(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + REG(RT) = (INT32)(INT16)READ16(ea); + REG(RA) = ea; +} + +static void ppc_lhaux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + REG(RT) = (INT32)(INT16)READ16(ea); + REG(RA) = ea; +} + +static void ppc_lhax(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (INT32)(INT16)READ16(ea); +} + +static void ppc_lhbrx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT16 w; + + if( RA != 0 ) + ea += REG(RA); + + w = READ16(ea); + REG(RT) = (UINT32)BYTE_REVERSE16(w); +} + +static void ppc_lhz(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (UINT32)READ16(ea); +} + +static void ppc_lhzu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + REG(RT) = (UINT32)READ16(ea); + REG(RA) = ea; +} + +static void ppc_lhzux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + REG(RT) = (UINT32)READ16(ea); + REG(RA) = ea; +} + +static void ppc_lhzx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = (UINT32)READ16(ea); +} + +static void ppc_lmw(UINT32 op) +{ + int r = RT; + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + while( r <= 31 ) + { + REG(r) = READ32(ea); + ea += 4; + r++; + } +} + +static void ppc_lswi(UINT32 op) +{ + int n, r, i; + UINT32 ea = (RA != 0) ? REG(RA) : 0; + + n = (RB == 0) ? 32 : RB; + + r = RT - 1; + i = 0; + + while(n > 0) + { + if (i == 0) { + r = (r + 1) % 32; + REG(r) = 0; + } + REG(r) |= ((READ8(ea) & 0xff) << (24 - i)); + i += 8; + if (i == 32) { + i = 0; + } + ea++; + n--; + } +} + +static void ppc_lswx(UINT32 op) +{ + int n, r, i; + UINT32 ea = REG(RB); + if(RA != 0) + ea += REG(RA); + + n = ppc.xer & 0x7f; + + r = RT - 1; + i = 0; + + while(n > 0) + { + if (i == 0) { + r = (r + 1) % 32; + REG(r) = 0; + } + REG(r) |= ((READ8(ea) & 0xff) << (24 - i)); + i += 8; + if (i == 32) { + i = 0; + } + ea++; + n--; + } +} + +static void ppc_lwarx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + ppc.reserved_address = ea; + ppc.reserved = 1; + + REG(RT) = READ32(ea); +} + +static void ppc_lwbrx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 w; + + if( RA != 0 ) + ea += REG(RA); + + w = READ32(ea); + REG(RT) = BYTE_REVERSE32(w); +} + +static void ppc_lwz(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = READ32(ea); +} + +static void ppc_lwzu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + REG(RT) = READ32(ea); + REG(RA) = ea; +} + +static void ppc_lwzux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + REG(RT) = READ32(ea); + REG(RA) = ea; +} + +static void ppc_lwzx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + REG(RT) = READ32(ea); +} + +static void ppc_mcrf(UINT32 op) +{ + CR(RT >> 2) = CR(RA >> 2); +} + +static void ppc_mcrxr(UINT32 op) +{ + CR(RT >> 2) = (XER >> 28) & 0x0F; + XER &= ~0xf0000000; +} + +static void ppc_mfcr(UINT32 op) +{ + REG(RT) = ppc_get_cr(); +} + +static void ppc_mfmsr(UINT32 op) +{ + REG(RT) = ppc_get_msr(); +} + +static void ppc_mfspr(UINT32 op) +{ + REG(RT) = ppc_get_spr(SPR); +} + +static void ppc_mtcrf(UINT32 op) +{ + int fxm = FXM; + int t = RT; + + if( fxm & 0x80 ) CR(0) = (REG(t) >> 28) & 0xf; + if( fxm & 0x40 ) CR(1) = (REG(t) >> 24) & 0xf; + if( fxm & 0x20 ) CR(2) = (REG(t) >> 20) & 0xf; + if( fxm & 0x10 ) CR(3) = (REG(t) >> 16) & 0xf; + if( fxm & 0x08 ) CR(4) = (REG(t) >> 12) & 0xf; + if( fxm & 0x04 ) CR(5) = (REG(t) >> 8) & 0xf; + if( fxm & 0x02 ) CR(6) = (REG(t) >> 4) & 0xf; + if( fxm & 0x01 ) CR(7) = (REG(t) >> 0) & 0xf; +} + +static void ppc_mtmsr(UINT32 op) +{ + ppc_set_msr(REG(RS)); +} + +static void ppc_mtspr(UINT32 op) +{ + ppc_set_spr(SPR, REG(RS)); +} + +static void ppc_mulhwx(UINT32 op) +{ + INT64 ra = (INT64)(INT32)REG(RA); + INT64 rb = (INT64)(INT32)REG(RB); + + REG(RT) = (UINT32)((ra * rb) >> 32); + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_mulhwux(UINT32 op) +{ + UINT64 ra = (UINT64)REG(RA); + UINT64 rb = (UINT64)REG(RB); + + REG(RT) = (UINT32)((ra * rb) >> 32); + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_mulli(UINT32 op) +{ + INT32 ra = (INT32)REG(RA); + INT32 i = SIMM16; + + REG(RT) = ra * i; +} + +static void ppc_mullwx(UINT32 op) +{ + INT64 ra = (INT64)(INT32)REG(RA); + INT64 rb = (INT64)(INT32)REG(RB); + INT64 r; + + r = ra * rb; + REG(RT) = (UINT32)r; + + if( OEBIT ) { + XER &= ~XER_OV; + + if( r != (INT64)(INT32)r ) + XER |= XER_OV | XER_SO; + } + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_nandx(UINT32 op) +{ + REG(RA) = ~(REG(RS) & REG(RB)); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_negx(UINT32 op) +{ + REG(RT) = -(INT32)(REG(RA)); + + if( OEBIT ) { + if( REG(RT) == 0x80000000 ) + XER |= XER_OV | XER_SO; + else + XER &= ~XER_OV; + } + + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_norx(UINT32 op) +{ + REG(RA) = ~(REG(RS) | REG(RB)); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_orx(UINT32 op) +{ + REG(RA) = REG(RS) | REG(RB); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_orcx(UINT32 op) +{ + REG(RA) = REG(RS) | ~REG(RB); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_ori(UINT32 op) +{ + REG(RA) = REG(RS) | UIMM16; +} + +static void ppc_oris(UINT32 op) +{ + REG(RA) = REG(RS) | (UIMM16 << 16); +} + +static void ppc_rfi(UINT32 op) +{ + UINT32 msr; + ppc.npc = ppc_get_spr(SPR_SRR0); + msr = ppc_get_spr(SPR_SRR1); + ppc_set_msr( msr ); + + ppc_change_pc(ppc.npc); +} + +static void ppc_rlwimix(UINT32 op) +{ + UINT32 r; + UINT32 mask = GET_ROTATE_MASK(MB, ME); + UINT32 rs = REG(RS); + int sh = SH; + + r = (rs << sh) | (rs >> (32-sh)); + REG(RA) = (REG(RA) & ~mask) | (r & mask); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_rlwinmx(UINT32 op) +{ + UINT32 r; + UINT32 mask = GET_ROTATE_MASK(MB, ME); + UINT32 rs = REG(RS); + int sh = SH; + + r = (rs << sh) | (rs >> (32-sh)); + REG(RA) = r & mask; + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_rlwnmx(UINT32 op) +{ + UINT32 r; + UINT32 mask = GET_ROTATE_MASK(MB, ME); + UINT32 rs = REG(RS); + int sh = REG(RB) & 0x1f; + + r = (rs << sh) | (rs >> (32-sh)); + REG(RA) = r & mask; + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_sc(UINT32 op) +{ + ppc603_exception(EXCEPTION_SYSTEM_CALL); +} + +static void ppc_slwx(UINT32 op) +{ + int sh = REG(RB) & 0x3f; + + if( sh > 31 ) { + REG(RA) = 0; + } + else { + REG(RA) = REG(RS) << sh; + } + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_srawx(UINT32 op) +{ + int sh = REG(RB) & 0x3f; + + XER &= ~XER_CA; + + if( sh > 31 ) { + if (REG(RS) & 0x80000000) + REG(RA) = 0xffffffff; + else + REG(RA) = 0; + if( REG(RA) ) + XER |= XER_CA; + } + else { + REG(RA) = (INT32)(REG(RS)) >> sh; + if( ((INT32)(REG(RS)) < 0) && (REG(RS) & BITMASK_0(sh)) ) + XER |= XER_CA; + } + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_srawix(UINT32 op) +{ + int sh = SH; + + XER &= ~XER_CA; + if( ((INT32)(REG(RS)) < 0) && (REG(RS) & BITMASK_0(sh)) ) + XER |= XER_CA; + + REG(RA) = (INT32)(REG(RS)) >> sh; + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_srwx(UINT32 op) +{ + int sh = REG(RB) & 0x3f; + + if( sh > 31 ) { + REG(RA) = 0; + } + else { + REG(RA) = REG(RS) >> sh; + } + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_stb(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + WRITE8(ea, (UINT8)REG(RS)); +} + +static void ppc_stbu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + WRITE8(ea, (UINT8)REG(RS)); + REG(RA) = ea; +} + +static void ppc_stbux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + WRITE8(ea, (UINT8)REG(RS)); + REG(RA) = ea; +} + +static void ppc_stbx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + WRITE8(ea, (UINT8)REG(RS)); +} + +static void ppc_sth(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + WRITE16(ea, (UINT16)REG(RS)); +} + +static void ppc_sthbrx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT16 w; + + if( RA != 0 ) + ea += REG(RA); + + w = REG(RS); + WRITE16(ea, (UINT16)BYTE_REVERSE16(w)); +} + +static void ppc_sthu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + WRITE16(ea, (UINT16)REG(RS)); + REG(RA) = ea; +} + +static void ppc_sthux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + WRITE16(ea, (UINT16)REG(RS)); + REG(RA) = ea; +} + +static void ppc_sthx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + WRITE16(ea, (UINT16)REG(RS)); +} + +static void ppc_stmw(UINT32 op) +{ + UINT32 ea = SIMM16; + int r = RS; + + if( RA != 0 ) + ea += REG(RA); + + while( r <= 31 ) + { + WRITE32(ea, REG(r)); + ea += 4; + r++; + } +} + +static void ppc_stswi(UINT32 op) +{ + int n, r, i; + UINT32 ea = (RA != 0) ? REG(RA) : 0; + + n = (RB == 0) ? 32 : RB; + + r = RT - 1; + i = 0; + + while(n > 0) + { + if (i == 0) { + r = (r + 1) % 32; + } + WRITE8(ea, (REG(r) >> (24-i)) & 0xff); + i += 8; + if (i == 32) { + i = 0; + } + ea++; + n--; + } +} + +static void ppc_stswx(UINT32 op) +{ + int n, r, i; + UINT32 ea = REG(RB); + if (RA != 0) + ea += REG(RA); + + n = ppc.xer & 0x7f; + + r = RT - 1; + i = 0; + + while(n > 0) + { + if (i == 0) { + r = (r + 1) % 32; + } + WRITE8(ea, (REG(r) >> (24-i)) & 0xff); + i += 8; + if (i == 32) { + i = 0; + } + ea++; + n--; + } +} + +static void ppc_stw(UINT32 op) +{ + UINT32 ea = SIMM16; + + if( RA != 0 ) + ea += REG(RA); + + WRITE32(ea, REG(RS)); +} + +static void ppc_stwbrx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 w; + + if( RA != 0 ) + ea += REG(RA); + + w = REG(RS); + WRITE32(ea, BYTE_REVERSE32(w)); +} + +static void ppc_stwcx_rc(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + if( ppc.reserved ) { + WRITE32(ea, REG(RS)); + + ppc.reserved = 0; + ppc.reserved_address = 0; + + CR(0) = 0x2; // set EQ to indicate success + } else { + CR(0) = 0; + } + + if (XER & XER_SO) + CR(0) |= 0x1; +} + +static void ppc_stwu(UINT32 op) +{ + UINT32 ea = REG(RA) + SIMM16; + + WRITE32(ea, REG(RS)); + REG(RA) = ea; +} + +static void ppc_stwux(UINT32 op) +{ + UINT32 ea = REG(RA) + REG(RB); + + WRITE32(ea, REG(RS)); + REG(RA) = ea; +} + +static void ppc_stwx(UINT32 op) +{ + UINT32 ea = REG(RB); + + if( RA != 0 ) + ea += REG(RA); + + WRITE32(ea, REG(RS)); +} + +static void ppc_subfx(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + REG(RT) = rb - ra; + + if( OEBIT ) { + SET_SUB_OV(REG(RT), rb, ra); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_subfcx(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + REG(RT) = rb - ra; + + SET_SUB_CA(REG(RT), rb, ra); + + if( OEBIT ) { + SET_SUB_OV(REG(RT), rb, ra); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_subfex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 rb = REG(RB); + UINT32 carry = (XER >> 29) & 0x1; + UINT32 r; + + r = ~ra + carry; + REG(RT) = rb + r; + + SET_ADD_CA(r, ~ra, carry); /* step 1 carry */ + if( REG(RT) < r ) /* step 2 carry */ + XER |= XER_CA; + + if( OEBIT ) { + SET_SUB_OV(REG(RT), rb, ra); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_subfic(UINT32 op) +{ + UINT32 i = SIMM16; + UINT32 ra = REG(RA); + + REG(RT) = i - ra; + + SET_SUB_CA(REG(RT), i, ra); +} + +static void ppc_subfmex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 carry = (XER >> 29) & 0x1; + UINT32 r; + + r = ~ra + carry; + REG(RT) = r - 1; + + SET_SUB_CA(r, ~ra, carry); /* step 1 carry */ + if( REG(RT) < r ) + XER |= XER_CA; /* step 2 carry */ + + if( OEBIT ) { + SET_SUB_OV(REG(RT), -1, ra); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_subfzex(UINT32 op) +{ + UINT32 ra = REG(RA); + UINT32 carry = (XER >> 29) & 0x1; + + REG(RT) = ~ra + carry; + + SET_ADD_CA(REG(RT), ~ra, carry); + + if( OEBIT ) { + SET_SUB_OV(REG(RT), 0, REG(RA)); + } + if( RCBIT ) { + SET_CR0(REG(RT)); + } +} + +static void ppc_sync(UINT32 op) +{ + +} + +static void ppc_tw(UINT32 op) +{ + int exception = 0; + INT32 a = REG(RA); + INT32 b = REG(RB); + int to = RT; + + if(( (a < b) && (to & 0x10) ) || + ( (a > b) && (to & 0x08) ) || + ( (a == b) && (to & 0x04) ) || + ( ((UINT32)a < (UINT32)b) && (to & 0x02) ) || + ( ((UINT32)a > (UINT32)b) && (to & 0x01) )) { + exception = 1; + } + + if (exception) { + ppc603_exception(EXCEPTION_TRAP); + } +} + +static void ppc_twi(UINT32 op) +{ + int exception = 0; + INT32 a = REG(RA); + INT32 i = SIMM16; + int to = RT; + + if(( (a < i) && (to & 0x10) ) || + ( (a > i) && (to & 0x08) ) || + ( (a == i) && (to & 0x04) ) || + ( ((UINT32)a < (UINT32)i) && (to & 0x02) ) || + ( ((UINT32)a > (UINT32)i) && (to & 0x01) )) { + exception = 1; + } + + if (exception) { + ppc603_exception(EXCEPTION_TRAP); + } +} + +static void ppc_xorx(UINT32 op) +{ + REG(RA) = REG(RS) ^ REG(RB); + + if( RCBIT ) { + SET_CR0(REG(RA)); + } +} + +static void ppc_xori(UINT32 op) +{ + REG(RA) = REG(RS) ^ UIMM16; +} + +static void ppc_xoris(UINT32 op) +{ + REG(RA) = REG(RS) ^ (UIMM16 << 16); +} + + + +static void ppc_invalid(UINT32 op) +{ + ErrorLog("PowerPC hit an invalid instruction. Halting emulation until reset."); + DebugLog("ppc: Invalid opcode %08X PC : %X, %08X\n", op, ppc.pc, ppc.npc); + ppc.fatalError = true; +} + + + +#define DOUBLE_SIGN (0x8000000000000000ULL) +#define DOUBLE_EXP (0x7ff0000000000000ULL) +#define DOUBLE_FRAC (0x000fffffffffffffULL) +#define DOUBLE_ZERO (0ULL) + +/* + Floating point operations. +*/ + +/*************************OLD +inline int is_nan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + +inline int is_qnan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & 0x0007fffffffffff) == 0x000000000000000) && + ((x.id & 0x000800000000000) == 0x000800000000000) ); +} + +inline int is_snan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) && + ((x.id & 0x0008000000000000) == DOUBLE_ZERO) ); +} + +inline int is_infinity_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) == DOUBLE_ZERO) ); +} + +inline int is_normalized_double(FPR x) +{ + UINT64 exp; + + exp = (x.id & DOUBLE_EXP) >> 52; + + return (exp >= 1) && (exp <= 2046); +} + +inline int is_denormalized_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == 0) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + +inline int sign_double(FPR x) +{ + return ((x.id & DOUBLE_SIGN) != 0); +} + +inline INT64 round_to_nearest(FPR f) +{ + //return (INT64)(f.fd + 0.5); + if (f.fd >= 0) + { + return (INT64)(f.fd + 0.5); + } + else + { + return -(INT64)(-f.fd + 0.5); + } +} + +inline INT64 round_toward_zero(FPR f) +{ + return (INT64)(f.fd); +} + +inline INT64 round_toward_positive_infinity(FPR f) +{ + double r = ceil(f.fd); + return (INT64)(r); +} + +inline INT64 round_toward_negative_infinity(FPR f) +{ + double r = floor(f.fd); + return (INT64)(r); +} +*/ + + +// New below, based on changes in MAME +inline int is_nan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + +inline int is_qnan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & 0x0007fffffffffffULL) == 0x000000000000000ULL) && + ((x.id & 0x000800000000000ULL) == 0x000800000000000ULL) ); +} + +inline int is_snan_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) && + ((x.id & (0x0008000000000000ULL)) == DOUBLE_ZERO) ); +} + +inline int is_infinity_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == DOUBLE_EXP) && + ((x.id & DOUBLE_FRAC) == DOUBLE_ZERO) ); +} + +inline int is_normalized_double(FPR x) +{ + UINT64 exp; + + exp = (x.id & DOUBLE_EXP) >> 52; + + return (exp >= 1) && (exp <= 2046); +} + +inline int is_denormalized_double(FPR x) +{ + return( ((x.id & DOUBLE_EXP) == 0) && + ((x.id & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + +inline int sign_double(FPR x) +{ + return ((x.id & DOUBLE_SIGN) != 0); +} + +inline INT64 smround_to_nearest(FPR f) +{ + if (f.fd >= 0) + { + return (INT64)(f.fd + 0.5); + } + else + { + return -(INT64)(-f.fd + 0.5); + } +} + +inline INT64 smround_toward_zero(FPR f) +{ + return (INT64)(f.fd); +} + +inline INT64 round_toward_positive_infinity(FPR f) +{ + double r = ceil(f.fd); + return (INT64)(r); +} + +inline INT64 round_toward_negative_infinity(FPR f) +{ + double r = floor(f.fd); + return (INT64)(r); +} + +#define SET_VXSNAN(a, b) if (is_snan_double(a) || is_snan_double(b)) ppc.fpscr |= 0x80000000 +#define SET_VXSNAN_1(c) if (is_snan_double(c)) ppc.fpscr |= 0x80000000 + +inline void set_fprf(FPR f) +{ + UINT32 fprf; + + // see page 3-30, 3-31 + + if (is_qnan_double(f)) + { + fprf = 0x11; + } + else if (is_infinity_double(f)) + { + if (sign_double(f)) // -INF + fprf = 0x09; + else // +INF + fprf = 0x05; + } + else if (is_normalized_double(f)) + { + if (sign_double(f)) // -Normalized + fprf = 0x08; + else // +Normalized + fprf = 0x04; + } + else if (is_denormalized_double(f)) + { + if (sign_double(f)) // -Denormalized + fprf = 0x18; + else // +Denormalized + fprf = 0x14; + } + else // Zero + { + if (sign_double(f)) // -Zero + fprf = 0x12; + else // +Zero + fprf = 0x02; + } + + ppc.fpscr &= ~0x0001f000; + ppc.fpscr |= (fprf << 12); +} + + + + +static void ppc_lfs(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + if(a) + ea += REG(a); + + f.i = READ32(ea); + FPR(t).fd = (double)(f.f); +} + +static void ppc_lfsu(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + ea += REG(a); + + f.i = READ32(ea); + FPR(t).fd = (double)(f.f); + + REG(a) = ea; +} + +static void ppc_lfd(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + + if(a) + ea += REG(a); + + FPR(t).id = READ64(ea); +} + +static void ppc_lfdu(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 d = RD; + + ea += REG(a); + + FPR(d).id = READ64(ea); + + REG(a) = ea; +} + +static void ppc_stfs(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + if(a) + ea += REG(a); + + f.f = (float)(FPR(t).fd); + WRITE32(ea, f.i); +} + +static void ppc_stfsu(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + ea += REG(a); + + f.f = (float)(FPR(t).fd); + WRITE32(ea, f.i); + + REG(a) = ea; +} + +static void ppc_stfd(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + + if(a) + ea += REG(a); + + WRITE64(ea, FPR(t).id); +} + +static void ppc_stfdu(UINT32 op) +{ + UINT32 ea = SIMM16; + UINT32 a = RA; + UINT32 t = RT; + + ea += REG(a); + + WRITE64(ea, FPR(t).id); + + REG(a) = ea; +} + +static void ppc_lfdux(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 d = RD; + + ea += REG(a); + + FPR(d).id = READ64(ea); + + REG(a) = ea; +} + +static void ppc_lfdx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 d = RD; + + if(a) + ea += REG(a); + + FPR(d).id = READ64(ea); +} + +static void ppc_lfsux(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + ea += REG(a); + + f.i = READ32(ea); + FPR(t).fd = (double)(f.f); + + REG(a) = ea; +} + +static void ppc_lfsx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + if(a) + ea += REG(a); + + f.i = READ32(ea); + FPR(t).fd = (double)(f.f); +} + +static void ppc_mfsr(UINT32 op) +{ + UINT32 sr = (op >> 16) & 15; + UINT32 t = RT; + + CHECK_SUPERVISOR(); + + REG(t) = ppc.sr[sr]; +} + +static void ppc_mfsrin(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_SUPERVISOR(); + + REG(t) = ppc.sr[REG(b) >> 28]; +} + +static void ppc_mftb(UINT32 op) +{ + UINT32 x = SPRF; + + switch(x) + { + case 268: REG(RT) = (UINT32)(ppc_read_timebase()); break; + case 269: REG(RT) = (UINT32)(ppc_read_timebase() >> 32); break; + default: + ErrorLog("PowerPC read from an invalid register. Halting emulation until reset."); + DebugLog("ppc: Invalid timebase register %d at %08X\n", x, ppc.pc); + ppc.fatalError = true; + break; + } +} + +static void ppc_mtsr(UINT32 op) +{ + UINT32 sr = (op >> 16) & 15; + UINT32 t = RT; + + CHECK_SUPERVISOR(); + + ppc.sr[sr] = REG(t); +} + +static void ppc_mtsrin(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_SUPERVISOR(); + + ppc.sr[REG(b) >> 28] = REG(t); +} + +static void ppc_dcba(UINT32 op) +{ + /* TODO: Cache not emulated so this opcode doesn't need to be implemented */ +} + +static void ppc_stfdux(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + + ea += REG(a); + + WRITE64(ea, FPR(t).id); + + REG(a) = ea; +} + +static void ppc_stfdx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + + if(a) + ea += REG(a); + + WRITE64(ea, FPR(t).id); +} + +static void ppc_stfiwx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + + if(a) + ea += REG(a); + + WRITE32(ea, (UINT32)FPR(t).id); +} + +static void ppc_stfsux(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + ea += REG(a); + + f.f = (float)(FPR(t).fd); + WRITE32(ea, f.i); + + REG(a) = ea; +} + +static void ppc_stfsx(UINT32 op) +{ + UINT32 ea = REG(RB); + UINT32 a = RA; + UINT32 t = RT; + FPR32 f; + + if(a) + ea += REG(a); + + f.f = (float)(FPR(t).fd); + + WRITE32(ea, f.i); +} + +static void ppc_tlbia(UINT32 op) +{ + /* TODO: TLB not emulated so this opcode doesn't need to implemented */ +} + +static void ppc_tlbie(UINT32 op) +{ + /* TODO: TLB not emulated so this opcode doesn't need to implemented */ +} + +static void ppc_tlbsync(UINT32 op) +{ + /* TODO: TLB not emulated so this opcode doesn't need to implemented */ +} + +static void ppc_eciwx(UINT32 op) +{ + ppc_unimplemented(op); +} + +static void ppc_ecowx(UINT32 op) +{ + ppc_unimplemented(op); +} + +static void ppc_fabsx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + FPR(t).id = FPR(b).id & ~DOUBLE_SIGN; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_faddx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = FPR(a).fd + FPR(b).fd; + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fcmpo(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = (RT >> 2); + UINT32 c; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + if(is_nan_double(FPR(a)) || is_nan_double(FPR(b))) + { + c = 1; /* OX */ + if(is_snan_double(FPR(a)) || is_snan_double(FPR(b))) { + ppc.fpscr |= 0x01000000; /* VXSNAN */ + + if(!(ppc.fpscr & 0x40000000) || is_qnan_double(FPR(a)) || is_qnan_double(FPR(b))) + ppc.fpscr |= 0x00080000; /* VXVC */ + } + } + else if(FPR(a).fd < FPR(b).fd){ + c = 8; /* FX */ + } + else if(FPR(a).fd > FPR(b).fd){ + c = 4; /* FEX */ + } + else { + c = 2; /* VX */ + } + + CR(t) = c; + + // TODO + // Enabled by Bart + ppc.fpscr &= ~0x0001F000; + ppc.fpscr |= (c << 12); +} + +static void ppc_fcmpu(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = (RT >> 2); + UINT32 c; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + if(is_nan_double(FPR(a)) || is_nan_double(FPR(b))) + { + c = 1; /* OX */ + if(is_snan_double(FPR(a)) || is_snan_double(FPR(b))) { + ppc.fpscr |= 0x01000000; /* VXSNAN */ + } + } + else if(FPR(a).fd < FPR(b).fd){ + c = 8; /* FX */ + } + else if(FPR(a).fd > FPR(b).fd){ + c = 4; /* FEX */ + } + else { + c = 2; /* VX */ + } + + CR(t) = c; + + // TODO + ppc.fpscr &= ~0x0001F000; + ppc.fpscr |= (c << 12); +} + +static void ppc_fctiwx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + INT64 r = 0; + + // TODO: fix FPSCR flags FX,VXSNAN,VXCVI + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + switch(ppc.fpscr & 3) + { + case 0: r = (INT64)smround_to_nearest(FPR(b)); break; + case 1: r = (INT64)smround_toward_zero(FPR(b)); break; + case 2: r = (INT64)round_toward_positive_infinity(FPR(b)); break; + case 3: r = (INT64)round_toward_negative_infinity(FPR(b)); break; + } + + if(r > (INT64)((INT32)0x7FFFFFFF)) + { + FPR(t).id = 0x7FFFFFFF; + // FPSCR[FR] = 0 + // FPSCR[FI] = 1 + // FPSCR[XX] = 1 + } + else if(FPR(b).fd < (INT64)((INT32)0x80000000)) + { + FPR(t).id = 0x80000000; + // FPSCR[FR] = 1 + // FPSCR[FI] = 1 + // FPSCR[XX] = 1 + } + else + { + FPR(t).id = (UINT32)r; + // FPSCR[FR] = t.iw > t.fd + // FPSCR[FI] = t.iw == t.fd + // FPSCR[XX] = ? + } + + // FPSCR[FPRF] = undefined (leave it as is) + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fctiwzx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + INT64 r; + + // TODO: fix FPSCR flags FX,VXSNAN,VXCVI + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + r = smround_toward_zero(FPR(b)); + + if(r > (INT64)((INT32)0x7fffffff)) + { + FPR(t).id = 0x7fffffff; + // FPSCR[FR] = 0 + // FPSCR[FI] = 1 + // FPSCR[XX] = 1 + + } + else if(r < (INT64)((INT32)0x80000000)) + { + FPR(t).id = 0x80000000; + // FPSCR[FR] = 1 + // FPSCR[FI] = 1 + // FPSCR[XX] = 1 + } + else + { + FPR(t).id = (UINT32)r; + // FPSCR[FR] = t.iw > t.fd + // FPSCR[FI] = t.iw == t.fd + // FPSCR[XX] = ? + } + + // FPSCR[FPRF] = undefined (leave it as is) + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fdivx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = FPR(a).fd / FPR(b).fd; + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmrx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + FPR(t).fd = FPR(b).fd; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnabsx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + FPR(t).id = FPR(b).id | DOUBLE_SIGN; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnegx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + FPR(t).id = FPR(b).id ^ DOUBLE_SIGN; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_frspx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + FPR(t).fd = (float)FPR(b).fd; + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_frsqrtex(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + FPR(t).fd = 1.0 / sqrt(FPR(b).fd); /* verify this */ + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fsqrtx(UINT32 op) +{ + /* NOTE: PPC603e doesn't support this opcode */ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + FPR(t).fd = (double)(sqrt(FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fsubx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = FPR(a).fd - FPR(b).fd; + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mffsx(UINT32 op) +{ + FPR(RT).id = (UINT32)ppc.fpscr; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mtfsb0x(UINT32 op) +{ + UINT32 crbD; + + crbD = (op >> 21) & 0x1F; + + if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared + ppc.fpscr &= ~(1 << (31 - crbD)); + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mtfsb1x(UINT32 op) +{ + UINT32 crbD; + + crbD = (op >> 21) & 0x1F; + + if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared + ppc.fpscr |= (1 << (31 - crbD)); + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mtfsfx(UINT32 op) +{ + UINT32 b = RB; + UINT32 f = ppc_field_xlat[FM]; + + ppc.fpscr &= (~f) | ~(FPSCR_FEX | FPSCR_VX); + ppc.fpscr |= (UINT32)(FPR(b).id) & ~(FPSCR_FEX | FPSCR_VX); + + // FEX, VX + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mtfsfix(UINT32 op) +{ + UINT32 crfd = CRFD; + UINT32 imm = (op >> 12) & 0xF; + + /* + * According to the manual: + * + * If bits 0 and 3 of FPSCR are to be modified, they take the immediate + * value specified. Bits 1 and 2 (FEX and VX) are set according to the + * "usual rule" and not from IMM[1-2]. + * + * The "usual rule" is not emulated, so these bits simply aren't modified + * at all here. + */ + + crfd = (7 - crfd) * 4; // calculate LSB position of field + + if (crfd == 28) // field containing FEX and VX is special... + { // bits 1 and 2 of FPSCR must not be altered + ppc.fpscr &= 0x9fffffff; + ppc.fpscr |= (imm & 0x9fffffff); + } + + ppc.fpscr &= ~(0xf << crfd); // clear field + ppc.fpscr |= (imm << crfd); // insert new data + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_mcrfs(UINT32 op) +{ + UINT32 crfs, f; + crfs = CRFA; + + f = ppc.fpscr >> ((7 - crfs) * 4); // get crfS field from FPSCR + f &= 0xf; + + switch(crfs) // determine which exception bits to clear in FPSCR + { + case 0: // FX, OX + ppc.fpscr &= ~0x90000000; + break; + case 1: // UX, ZX, XX, VXSNAN + ppc.fpscr &= ~0x0f000000; + break; + case 2: // VXISI, VXIDI, VXZDZ, VXIMZ + ppc.fpscr &= ~0x00F00000; + break; + case 3: // VXVC + ppc.fpscr &= ~0x00080000; + break; + case 5: // VXSOFT, VXSQRT, VXCVI + ppc.fpscr &= ~0x00000700; + break; + default: + break; + } + + CR(CRFD) = f; +} + +static void ppc_faddsx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = (float)(FPR(a).fd + FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fdivsx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = (float)(FPR(a).fd / FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fresx(UINT32 op) +{ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + FPR(t).fd = 1.0 / FPR(b).fd; /* ??? */ + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fsqrtsx(UINT32 op) +{ + /* NOTE: This opcode is not supported in PPC603e */ + UINT32 b = RB; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN_1(FPR(b)); + + FPR(t).fd = (float)(sqrt(FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fsubsx(UINT32 op) +{ + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + + FPR(t).fd = (float)(FPR(a).fd - FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmaddx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = ((FPR(a).fd * FPR(c).fd) + FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmsubx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = ((FPR(a).fd * FPR(c).fd) - FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmulx(UINT32 op) +{ + UINT32 c = RC; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(c)); + + FPR(t).fd = (FPR(a).fd * FPR(c).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnmaddx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (-((FPR(a).fd * FPR(c).fd) + FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnmsubx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (-((FPR(a).fd * FPR(c).fd) - FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fselx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + FPR(t).fd = (FPR(a).fd >= 0.0) ? FPR(c).fd : FPR(b).fd; + + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmaddsx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (float)((FPR(a).fd * FPR(c).fd) + FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmsubsx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (float)((FPR(a).fd * FPR(c).fd) - FPR(b).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fmulsx(UINT32 op) +{ + UINT32 c = RC; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + SET_VXSNAN(FPR(a), FPR(c)); + + FPR(t).fd = (float)(FPR(a).fd * FPR(c).fd); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnmaddsx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (float)(-((FPR(a).fd * FPR(c).fd) + FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} + +static void ppc_fnmsubsx(UINT32 op) +{ + UINT32 c = RC; + UINT32 b = RB; + UINT32 a = RA; + UINT32 t = RT; + + CHECK_FPU_AVAILABLE(); + + SET_VXSNAN(FPR(a), FPR(b)); + SET_VXSNAN_1(FPR(c)); + + FPR(t).fd = (float)(-((FPR(a).fd * FPR(c).fd) - FPR(b).fd)); + + set_fprf(FPR(t)); + if( RCBIT ) { + SET_CR1(); + } +} diff --git a/Src/CPU/PowerPC/ppc_ops.h b/Src/CPU/PowerPC/ppc_ops.h new file mode 100644 index 0000000..0179359 --- /dev/null +++ b/Src/CPU/PowerPC/ppc_ops.h @@ -0,0 +1,170 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ppc_ops.h + * + * Header file defining PowerPC opcode handlers. + */ + +static PPC_OPCODE ppc_opcode_common[] = +{ + /*code subcode handler */ + { 31, 266, ppc_addx }, + { 31, 266 | 512, ppc_addx }, + { 31, 10, ppc_addcx }, + { 31, 10 | 512, ppc_addcx }, + { 31, 138, ppc_addex }, + { 31, 138 | 512, ppc_addex }, + { 14, -1, ppc_addi }, + { 12, -1, ppc_addic }, + { 13, -1, ppc_addic_rc }, + { 15, -1, ppc_addis }, + { 31, 234, ppc_addmex }, + { 31, 234 | 512, ppc_addmex }, + { 31, 202, ppc_addzex }, + { 31, 202 | 512, ppc_addzex }, + { 31, 28, ppc_andx }, + { 31, 28 | 512, ppc_andx }, + { 31, 60, ppc_andcx }, + { 28, -1, ppc_andi_rc }, + { 29, -1, ppc_andis_rc }, + { 18, -1, ppc_bx }, + { 16, -1, ppc_bcx }, + { 19, 528, ppc_bcctrx }, + { 19, 16, ppc_bclrx }, + { 31, 0, ppc_cmp }, + { 11, -1, ppc_cmpi }, + { 31, 32, ppc_cmpl }, + { 10, -1, ppc_cmpli }, + { 31, 26, ppc_cntlzw }, + { 19, 257, ppc_crand }, + { 19, 129, ppc_crandc }, + { 19, 289, ppc_creqv }, + { 19, 225, ppc_crnand }, + { 19, 33, ppc_crnor }, + { 19, 449, ppc_cror }, + { 19, 417, ppc_crorc }, + { 19, 193, ppc_crxor }, + { 31, 86, ppc_dcbf }, + { 31, 470, ppc_dcbi }, + { 31, 54, ppc_dcbst }, + { 31, 278, ppc_dcbt }, + { 31, 246, ppc_dcbtst }, + { 31, 1014, ppc_dcbz }, + { 31, 491, ppc_divwx }, + { 31, 491 | 512, ppc_divwx }, + { 31, 459, ppc_divwux }, + { 31, 459 | 512, ppc_divwux }, + { 31, 854, ppc_eieio }, + { 31, 284, ppc_eqvx }, + { 31, 954, ppc_extsbx }, + { 31, 922, ppc_extshx }, + { 31, 982, ppc_icbi }, + { 19, 150, ppc_isync }, + { 34, -1, ppc_lbz }, + { 35, -1, ppc_lbzu }, + { 31, 119, ppc_lbzux }, + { 31, 87, ppc_lbzx }, + { 42, -1, ppc_lha }, + { 43, -1, ppc_lhau }, + { 31, 375, ppc_lhaux }, + { 31, 343, ppc_lhax }, + { 31, 790, ppc_lhbrx }, + { 40, -1, ppc_lhz }, + { 41, -1, ppc_lhzu }, + { 31, 311, ppc_lhzux }, + { 31, 279, ppc_lhzx }, + { 46, -1, ppc_lmw }, + { 31, 597, ppc_lswi }, + { 31, 533, ppc_lswx }, + { 31, 20, ppc_lwarx }, + { 31, 534, ppc_lwbrx }, + { 32, -1, ppc_lwz }, + { 33, -1, ppc_lwzu }, + { 31, 55, ppc_lwzux }, + { 31, 23, ppc_lwzx }, + { 19, 0, ppc_mcrf }, + { 31, 512, ppc_mcrxr }, + { 31, 19, ppc_mfcr }, + { 31, 83, ppc_mfmsr }, + { 31, 339, ppc_mfspr }, + { 31, 144, ppc_mtcrf }, + { 31, 146, ppc_mtmsr }, + { 31, 467, ppc_mtspr }, + { 31, 75, ppc_mulhwx }, + { 31, 11, ppc_mulhwux }, + { 7, -1, ppc_mulli }, + { 31, 235, ppc_mullwx }, + { 31, 235 | 512, ppc_mullwx }, + { 31, 476, ppc_nandx }, + { 31, 104, ppc_negx }, + { 31, 104 | 512, ppc_negx }, + { 31, 124, ppc_norx }, + { 31, 444, ppc_orx }, + { 31, 412, ppc_orcx }, + { 24, -1, ppc_ori }, + { 25, -1, ppc_oris }, + { 19, 50, ppc_rfi }, + { 20, -1, ppc_rlwimix }, + { 21, -1, ppc_rlwinmx }, + { 23, -1, ppc_rlwnmx }, + { 17, -1, ppc_sc }, + { 31, 24, ppc_slwx }, + { 31, 792, ppc_srawx }, + { 31, 824, ppc_srawix }, + { 31, 536, ppc_srwx }, + { 38, -1, ppc_stb }, + { 39, -1, ppc_stbu }, + { 31, 247, ppc_stbux }, + { 31, 215, ppc_stbx }, + { 44, -1, ppc_sth }, + { 31, 918, ppc_sthbrx }, + { 45, -1, ppc_sthu }, + { 31, 439, ppc_sthux }, + { 31, 407, ppc_sthx }, + { 47, -1, ppc_stmw }, + { 31, 725, ppc_stswi }, + { 31, 661, ppc_stswx }, + { 36, -1, ppc_stw }, + { 31, 662, ppc_stwbrx }, + { 31, 150, ppc_stwcx_rc }, + { 37, -1, ppc_stwu }, + { 31, 183, ppc_stwux }, + { 31, 151, ppc_stwx }, + { 31, 40, ppc_subfx }, + { 31, 40 | 512, ppc_subfx }, + { 31, 8, ppc_subfcx }, + { 31, 8 | 512, ppc_subfcx }, + { 31, 136, ppc_subfex }, + { 31, 136 | 512, ppc_subfex }, + { 8, -1, ppc_subfic }, + { 31, 232, ppc_subfmex }, + { 31, 232 | 512, ppc_subfmex }, + { 31, 200, ppc_subfzex }, + { 31, 200 | 512, ppc_subfzex }, + { 31, 598, ppc_sync }, + { 31, 4, ppc_tw }, + { 3, -1, ppc_twi }, + { 31, 316, ppc_xorx }, + { 26, -1, ppc_xori }, + { 27, -1, ppc_xoris } +}; diff --git a/Src/CPU/Z80/Z80.cpp b/Src/CPU/Z80/Z80.cpp new file mode 100644 index 0000000..7327875 --- /dev/null +++ b/Src/CPU/Z80/Z80.cpp @@ -0,0 +1,4017 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Z80.cpp + * + * Z80 instruction set simulator. + * Copyright (C) 1995 Frank D. Cringle. + * Adapted for use in Supermodel by Bart Trzynadlowski (July 15, 2011). + * + * Please see Z80.h for a discussion of known inaccuracies. + */ + +#include "Z80.h" // must include this first to define CZ80 + +#include // for NULL +#include "Supermodel.h" + +#include "Debugger/CPU/Z80Debug.h" + +/****************************************************************************** + Internal Helper Macros +******************************************************************************/ + +// Address space access +#define GetBYTE(a) ( Bus->Read8(a&0xFFFF) ) +#define GetBYTE_pp(a) ( Bus->Read8(((a)++)&0xFFFF) ) +#define GetBYTE_mm(a) ( Bus->Read8(((a)--)&0xFFFF) ) +#define mm_GetBYTE(a) ( Bus->Read8((--(a))&0xFFFF) ) + +#define PutBYTE(a,v) Bus->Write8((a)&0xFFFF,v) +#define PutBYTE_pp(a,v) Bus->Write8(((a)++)&0xFFFF,v) +#define PutBYTE_mm(a,v) Bus->Write8(((a)--)&0xFFFF,v) +#define mm_PutBYTE(a,v) Bus->Write8((--(a))&0xFFFF,v) + +#define GetWORD(a) (Bus->Read8((a)&0xFFFF) | (Bus->Read8(((a)+1)&0xFFFF)<<8)) + +#define PutWORD(a, v) \ + do \ + { \ + PutBYTE((a),(v)&0xFF); \ + PutBYTE((a)+1,((v)>>8)); \ + } while (0) + +#define OUTPUT(a,v) Bus->IOWrite8((a)&0xFF,v) +#define INPUT(a) ( Bus->IORead8((a)&0xFF) ) + +// Flags +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) + +// Piecewise register access +#define ldig(x) ((x) & 0xf) +#define hdig(x) (((x)>>4)&0xf) +#define lreg(x) ((x)&0xff) +#define hreg(x) (((x)>>8)&0xff) + +#define Setlreg(x, v) x = (((x)&0xff00) | ((v)&0xff)) +#define Sethreg(x, v) x = (((x)&0xff) | (((v)&0xff) << 8)) + +// Parity bit calculation +static const unsigned char partab[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +// Instruction cycle tables +static const unsigned char cycleTables[5][256] = { + { + // Table 0: single byte instructions + 4,10,7,6,4,4,7,4,4,11,7,6,4,4,7,4, + 8,10,7,6,4,4,7,4,12,11,7,6,4,4,7,4, + 7,10,16,6,4,4,7,4,7,11,16,6,4,4,7,4, + 7,10,13,6,11,11,10,4,7,11,13,6,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 7,7,7,7,7,7,4,7,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 4,4,4,4,4,4,7,4,4,4,4,4,4,4,7,4, + 5,10,10,10,10,11,7,11,5,10,10,0,10,17,7,11, + 5,10,10,11,10,11,7,11,5,4,10,11,10,0,7,11, + 5,10,10,19,10,11,7,11,5,4,10,4,10,0,7,11, + 5,10,10,4,10,11,7,11,5,6,10,4,10,0,7,11 + }, { + // Table 1: two byte instructions of form CB-XX + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,12,8,8,8,8,8,8,8,12,8, + 8,8,8,8,8,8,12,8,8,8,8,8,8,8,12,8, + 8,8,8,8,8,8,12,8,8,8,8,8,8,8,12,8, + 8,8,8,8,8,8,12,8,8,8,8,8,8,8,12,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8, + 8,8,8,8,8,8,15,8,8,8,8,8,8,8,15,8 + }, { + // Table 2: two byte instructions of form ED-XX + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 12,12,15,20,8,14,8,9,12,12,15,20,0,14,0,9, + 12,12,15,20,0,0,8,9,12,12,15,20,0,0,8,9, + 12,12,15,20,0,0,0,18,12,12,15,20,0,0,0,18, + 12,0,15,20,0,0,0,0,12,12,15,20,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 16,16,16,16,0,0,0,0,16,16,16,16,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }, { + // Table 3: two byte instructions of form DD-XX or FD-XX + 0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0, + 0,14,20,10,9,9,9,0,0,15,20,10,9,9,9,0, + 0,0,0,0,23,23,19,0,0,15,0,0,0,0,0,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 9,9,9,9,9,9,19,9,9,9,9,9,9,9,19,9, + 19,19,19,19,19,19,19,19,0,0,0,0,9,9,19,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 0,0,0,0,9,9,19,0,0,0,0,0,9,9,19,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,14,0,23,0,15,0,0,0,8,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0 + }, { + // Table 4: three byte instructions of form DD-CB-XX or FD-CB-XX + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0, + 0,0,0,0,0,0,23,0,0,0,0,0,0,0,23,0 + } +}; + +#define parity(x) partab[(x)&0xff] + +// Stack +#define POP(x) \ + do \ + { \ + unsigned int y = GetBYTE_pp(SP); \ + x = y + (GetBYTE_pp(SP) << 8); \ + } while (0) + +#define PUSH(x) \ + do \ + { \ + mm_PutBYTE(SP,((x)>>8)); \ + mm_PutBYTE(SP,(x)&0xFF); \ + } while (0) + +// Branching +#define Jpc(cond) pc = cond ? GetWORD(pc) : pc+2 + +#define CALLC(cond) \ + { \ + if (cond) \ + { \ + unsigned int adrr = GetWORD(pc); \ + PUSH(pc+2); \ + pc = adrr; \ + } \ + else \ + pc += 2; \ + } + + +/******************************************************************************* + Functions +*******************************************************************************/ + +int CZ80::Run(int numCycles) +{ +#ifdef SUPERMODEL_DEBUGGER + // If debugging enabled, don't optimize access to registers as they need to be accesible to debugger during execution +#define AF af[af_sel] +#define BC regs[regs_sel].bc +#define DE regs[regs_sel].de +#define HL regs[regs_sel].hl +#define SP sp +#define IX ix +#define IY iy +#else + // Optimization: copy registers into native word-sized local variables + unsigned int AF = af[af_sel]; + unsigned int BC = regs[regs_sel].bc; + unsigned int DE = regs[regs_sel].de; + unsigned int HL = regs[regs_sel].hl; + unsigned int SP = sp; + unsigned int IX = ix; + unsigned int IY = iy; +#endif + unsigned int temp = 0; + unsigned int acu = 0; + unsigned int sum = 0; + unsigned int cbits = 0; + unsigned int op = 0; + unsigned int adr = 0; + + int cycles = numCycles; +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + { + Debug->CPUActive(); + lastCycles += numCycles; + } +#endif // SUPERMODEL_DEBUGGER + + while (cycles > 0) + { + op = GetBYTE_pp(pc); +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + { + while (Debug->CPUExecute(pc - 1, op, lastCycles - cycles)) + op = GetBYTE_pp(pc); + lastCycles = cycles; + } +#endif // SUPERMODEL_DEBUGGER + switch(op) { + case 0x00: /* NOP */ + cycles -= cycleTables[0][0x00]; + break; + case 0x01: /* LD BC,nnnn */ + cycles -= cycleTables[0][0x01]; + BC = GetWORD(pc); + pc += 2; + break; + case 0x02: /* LD (BC),A */ + cycles -= cycleTables[0][0x02]; + PutBYTE(BC, hreg(AF)); + break; + case 0x03: /* INC BC */ + cycles -= cycleTables[0][0x03]; + ++BC; + break; + case 0x04: /* INC B */ + cycles -= cycleTables[0][0x04]; + BC += 0x100; + temp = hreg(BC); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x05: /* DEC B */ + cycles -= cycleTables[0][0x05]; + BC -= 0x100; + temp = hreg(BC); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x06: /* LD B,nn */ + cycles -= cycleTables[0][0x06]; + Sethreg(BC, GetBYTE_pp(pc)); + break; + case 0x07: /* RLCA */ + cycles -= cycleTables[0][0x07]; + AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + case 0x08: /* EX AF,AF' */ + cycles -= cycleTables[0][0x08]; + af[af_sel] = AF; + af_sel = 1 - af_sel; + AF = af[af_sel]; + break; + case 0x09: /* ADD HL,BC */ + cycles -= cycleTables[0][0x09]; + HL &= 0xffff; + BC &= 0xffff; + sum = HL + BC; + cbits = (HL ^ BC ^ sum) >> 8; + HL = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x0A: /* LD A,(BC) */ + cycles -= cycleTables[0][0x0A]; + Sethreg(AF, GetBYTE(BC)); + break; + case 0x0B: /* DEC BC */ + cycles -= cycleTables[0][0x0B]; + --BC; + break; + case 0x0C: /* INC C */ + cycles -= cycleTables[0][0x0C]; + temp = lreg(BC)+1; + Setlreg(BC, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x0D: /* DEC C */ + cycles -= cycleTables[0][0x0D]; + temp = lreg(BC)-1; + Setlreg(BC, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x0E: /* LD C,nn */ + cycles -= cycleTables[0][0x0E]; + Setlreg(BC, GetBYTE_pp(pc)); + break; + case 0x0F: /* RRCA */ + cycles -= cycleTables[0][0x0F]; + temp = hreg(AF); + sum = temp >> 1; + AF = ((temp & 1) << 15) | (sum << 8) | + (sum & 0x28) | (AF & 0xc4) | (temp & 1); + break; + case 0x10: /* DJNZ dd */ + cycles -= cycleTables[0][0x10]; + pc += ((BC -= 0x100) & 0xff00) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x11: /* LD DE,nnnn */ + cycles -= cycleTables[0][0x11]; + DE = GetWORD(pc); + pc += 2; + break; + case 0x12: /* LD (DE),A */ + cycles -= cycleTables[0][0x12]; + PutBYTE(DE, hreg(AF)); + break; + case 0x13: /* INC DE */ + cycles -= cycleTables[0][0x13]; + ++DE; + break; + case 0x14: /* INC D */ + cycles -= cycleTables[0][0x14]; + DE += 0x100; + temp = hreg(DE); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x15: /* DEC D */ + cycles -= cycleTables[0][0x15]; + DE -= 0x100; + temp = hreg(DE); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x16: /* LD D,nn */ + cycles -= cycleTables[0][0x16]; + Sethreg(DE, GetBYTE_pp(pc)); + break; + case 0x17: /* RLA */ + cycles -= cycleTables[0][0x17]; + AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + case 0x18: /* JR dd */ + cycles -= cycleTables[0][0x18]; + pc += (1) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x19: /* ADD HL,DE */ + cycles -= cycleTables[0][0x19]; + HL &= 0xffff; + DE &= 0xffff; + sum = HL + DE; + cbits = (HL ^ DE ^ sum) >> 8; + HL = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x1A: /* LD A,(DE) */ + cycles -= cycleTables[0][0x1A]; + Sethreg(AF, GetBYTE(DE)); + break; + case 0x1B: /* DEC DE */ + cycles -= cycleTables[0][0x1B]; + --DE; + break; + case 0x1C: /* INC E */ + cycles -= cycleTables[0][0x1C]; + temp = lreg(DE)+1; + Setlreg(DE, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x1D: /* DEC E */ + cycles -= cycleTables[0][0x1D]; + temp = lreg(DE)-1; + Setlreg(DE, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x1E: /* LD E,nn */ + cycles -= cycleTables[0][0x1E]; + Setlreg(DE, GetBYTE_pp(pc)); + break; + case 0x1F: /* RRA */ + cycles -= cycleTables[0][0x1F]; + temp = hreg(AF); + sum = temp >> 1; + AF = ((AF & 1) << 15) | (sum << 8) | + (sum & 0x28) | (AF & 0xc4) | (temp & 1); + break; + case 0x20: /* JR NZ,dd */ + cycles -= cycleTables[0][0x20]; + pc += (!TSTFLAG(Z)) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x21: /* LD HL,nnnn */ + cycles -= cycleTables[0][0x21]; + HL = GetWORD(pc); + pc += 2; + break; + case 0x22: /* LD (nnnn),HL */ + cycles -= cycleTables[0][0x22]; + temp = GetWORD(pc); + PutWORD(temp, HL); + pc += 2; + break; + case 0x23: /* INC HL */ + cycles -= cycleTables[0][0x23]; + ++HL; + break; + case 0x24: /* INC H */ + cycles -= cycleTables[0][0x24]; + HL += 0x100; + temp = hreg(HL); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x25: /* DEC H */ + cycles -= cycleTables[0][0x25]; + HL -= 0x100; + temp = hreg(HL); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x26: /* LD H,nn */ + cycles -= cycleTables[0][0x26]; + Sethreg(HL, GetBYTE_pp(pc)); + break; + case 0x27: /* DAA */ + cycles -= cycleTables[0][0x27]; + acu = hreg(AF); + temp = ldig(acu); + cbits = TSTFLAG(C); + if (TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) + SETFLAG(H, 0); + acu -= 6; + acu &= 0xff; + } + if (hd) /* adjust high digit */ + acu -= 0x160; + } + else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) /* adjust high digit */ + acu += 0x60; + } + cbits |= (acu >> 8) & 1; + acu &= 0xff; + AF = (acu << 8) | (acu & 0xa8) | ((acu == 0) << 6) | + (AF & 0x12) | partab[acu] | cbits; + break; + case 0x28: /* JR Z,dd */ + cycles -= cycleTables[0][0x28]; + pc += (TSTFLAG(Z)) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x29: /* ADD HL,HL */ + cycles -= cycleTables[0][0x29]; + HL &= 0xffff; + sum = HL + HL; + cbits = (HL ^ HL ^ sum) >> 8; + HL = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x2A: /* LD HL,(nnnn) */ + cycles -= cycleTables[0][0x2A]; + temp = GetWORD(pc); + HL = GetWORD(temp); + pc += 2; + break; + case 0x2B: /* DEC HL */ + cycles -= cycleTables[0][0x2B]; + --HL; + break; + case 0x2C: /* INC L */ + cycles -= cycleTables[0][0x2C]; + temp = lreg(HL)+1; + Setlreg(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x2D: /* DEC L */ + cycles -= cycleTables[0][0x2D]; + temp = lreg(HL)-1; + Setlreg(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x2E: /* LD L,nn */ + cycles -= cycleTables[0][0x2E]; + Setlreg(HL, GetBYTE_pp(pc)); + break; + case 0x2F: /* CPL */ + cycles -= cycleTables[0][0x2F]; + AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + break; + case 0x30: /* JR NC,dd */ + cycles -= cycleTables[0][0x30]; + pc += (!TSTFLAG(C)) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x31: /* LD SP,nnnn */ + cycles -= cycleTables[0][0x31]; + SP = GetWORD(pc); + pc += 2; + break; + case 0x32: /* LD (nnnn),A */ + cycles -= cycleTables[0][0x32]; + temp = GetWORD(pc); + PutBYTE(temp, hreg(AF)); + pc += 2; + break; + case 0x33: /* INC SP */ + cycles -= cycleTables[0][0x33]; + ++SP; + break; + case 0x34: /* INC (HL) */ + cycles -= cycleTables[0][0x34]; + temp = GetBYTE(HL)+1; + PutBYTE(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x35: /* DEC (HL) */ + cycles -= cycleTables[0][0x35]; + temp = GetBYTE(HL)-1; + PutBYTE(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x36: /* LD (HL),nn */ + cycles -= cycleTables[0][0x36]; + PutBYTE(HL, GetBYTE_pp(pc)); + break; + case 0x37: /* SCF */ + cycles -= cycleTables[0][0x37]; + AF = (AF&~0x3b)|((AF>>8)&0x28)|1; + break; + case 0x38: /* JR C,dd */ + cycles -= cycleTables[0][0x38]; + pc += (TSTFLAG(C)) ? (signed char) GetBYTE(pc) + 1 : 1; + break; + case 0x39: /* ADD HL,SP */ + cycles -= cycleTables[0][0x39]; + HL &= 0xffff; + SP &= 0xffff; + sum = HL + SP; + cbits = (HL ^ SP ^ sum) >> 8; + HL = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x3A: /* LD A,(nnnn) */ + cycles -= cycleTables[0][0x3A]; + temp = GetWORD(pc); + Sethreg(AF, GetBYTE(temp)); + pc += 2; + break; + case 0x3B: /* DEC SP */ + cycles -= cycleTables[0][0x3B]; + --SP; + break; + case 0x3C: /* INC A */ + cycles -= cycleTables[0][0x3C]; + AF += 0x100; + temp = hreg(AF); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x3D: /* DEC A */ + cycles -= cycleTables[0][0x3D]; + AF -= 0x100; + temp = hreg(AF); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x3E: /* LD A,nn */ + cycles -= cycleTables[0][0x3E]; + Sethreg(AF, GetBYTE_pp(pc)); + break; + case 0x3F: /* CCF */ + cycles -= cycleTables[0][0x3F]; + AF = (AF&~0x3b)|((AF>>8)&0x28)|((AF&1)<<4)|(~AF&1); + break; + case 0x40: /* LD B,B */ + cycles -= cycleTables[0][0x40]; + /* nop */ + break; + case 0x41: /* LD B,C */ + cycles -= cycleTables[0][0x41]; + BC = (BC & 255) | ((BC & 255) << 8); + break; + case 0x42: /* LD B,D */ + cycles -= cycleTables[0][0x42]; + BC = (BC & 255) | (DE & ~255); + break; + case 0x43: /* LD B,E */ + cycles -= cycleTables[0][0x43]; + BC = (BC & 255) | ((DE & 255) << 8); + break; + case 0x44: /* LD B,H */ + cycles -= cycleTables[0][0x44]; + BC = (BC & 255) | (HL & ~255); + break; + case 0x45: /* LD B,L */ + cycles -= cycleTables[0][0x45]; + BC = (BC & 255) | ((HL & 255) << 8); + break; + case 0x46: /* LD B,(HL) */ + cycles -= cycleTables[0][0x46]; + Sethreg(BC, GetBYTE(HL)); + break; + case 0x47: /* LD B,A */ + cycles -= cycleTables[0][0x47]; + BC = (BC & 255) | (AF & ~255); + break; + case 0x48: /* LD C,B */ + cycles -= cycleTables[0][0x48]; + BC = (BC & ~255) | ((BC >> 8) & 255); + break; + case 0x49: /* LD C,C */ + cycles -= cycleTables[0][0x49]; + /* nop */ + break; + case 0x4A: /* LD C,D */ + cycles -= cycleTables[0][0x4A]; + BC = (BC & ~255) | ((DE >> 8) & 255); + break; + case 0x4B: /* LD C,E */ + cycles -= cycleTables[0][0x4B]; + BC = (BC & ~255) | (DE & 255); + break; + case 0x4C: /* LD C,H */ + cycles -= cycleTables[0][0x4C]; + BC = (BC & ~255) | ((HL >> 8) & 255); + break; + case 0x4D: /* LD C,L */ + cycles -= cycleTables[0][0x4D]; + BC = (BC & ~255) | (HL & 255); + break; + case 0x4E: /* LD C,(HL) */ + cycles -= cycleTables[0][0x4E]; + Setlreg(BC, GetBYTE(HL)); + break; + case 0x4F: /* LD C,A */ + cycles -= cycleTables[0][0x4F]; + BC = (BC & ~255) | ((AF >> 8) & 255); + break; + case 0x50: /* LD D,B */ + cycles -= cycleTables[0][0x50]; + DE = (DE & 255) | (BC & ~255); + break; + case 0x51: /* LD D,C */ + cycles -= cycleTables[0][0x51]; + DE = (DE & 255) | ((BC & 255) << 8); + break; + case 0x52: /* LD D,D */ + cycles -= cycleTables[0][0x52]; + /* nop */ + break; + case 0x53: /* LD D,E */ + cycles -= cycleTables[0][0x53]; + DE = (DE & 255) | ((DE & 255) << 8); + break; + case 0x54: /* LD D,H */ + cycles -= cycleTables[0][0x54]; + DE = (DE & 255) | (HL & ~255); + break; + case 0x55: /* LD D,L */ + cycles -= cycleTables[0][0x55]; + DE = (DE & 255) | ((HL & 255) << 8); + break; + case 0x56: /* LD D,(HL) */ + cycles -= cycleTables[0][0x56]; + Sethreg(DE, GetBYTE(HL)); + break; + case 0x57: /* LD D,A */ + cycles -= cycleTables[0][0x57]; + DE = (DE & 255) | (AF & ~255); + break; + case 0x58: /* LD E,B */ + cycles -= cycleTables[0][0x58]; + DE = (DE & ~255) | ((BC >> 8) & 255); + break; + case 0x59: /* LD E,C */ + cycles -= cycleTables[0][0x59]; + DE = (DE & ~255) | (BC & 255); + break; + case 0x5A: /* LD E,D */ + cycles -= cycleTables[0][0x5A]; + DE = (DE & ~255) | ((DE >> 8) & 255); + break; + case 0x5B: /* LD E,E */ + cycles -= cycleTables[0][0x5B]; + /* nop */ + break; + case 0x5C: /* LD E,H */ + cycles -= cycleTables[0][0x5C]; + DE = (DE & ~255) | ((HL >> 8) & 255); + break; + case 0x5D: /* LD E,L */ + cycles -= cycleTables[0][0x5D]; + DE = (DE & ~255) | (HL & 255); + break; + case 0x5E: /* LD E,(HL) */ + cycles -= cycleTables[0][0x5E]; + Setlreg(DE, GetBYTE(HL)); + break; + case 0x5F: /* LD E,A */ + cycles -= cycleTables[0][0x5F]; + DE = (DE & ~255) | ((AF >> 8) & 255); + break; + case 0x60: /* LD H,B */ + cycles -= cycleTables[0][0x60]; + HL = (HL & 255) | (BC & ~255); + break; + case 0x61: /* LD H,C */ + cycles -= cycleTables[0][0x61]; + HL = (HL & 255) | ((BC & 255) << 8); + break; + case 0x62: /* LD H,D */ + cycles -= cycleTables[0][0x62]; + HL = (HL & 255) | (DE & ~255); + break; + case 0x63: /* LD H,E */ + cycles -= cycleTables[0][0x63]; + HL = (HL & 255) | ((DE & 255) << 8); + break; + case 0x64: /* LD H,H */ + cycles -= cycleTables[0][0x64]; + /* nop */ + break; + case 0x65: /* LD H,L */ + cycles -= cycleTables[0][0x65]; + HL = (HL & 255) | ((HL & 255) << 8); + break; + case 0x66: /* LD H,(HL) */ + cycles -= cycleTables[0][0x66]; + Sethreg(HL, GetBYTE(HL)); + break; + case 0x67: /* LD H,A */ + cycles -= cycleTables[0][0x67]; + HL = (HL & 255) | (AF & ~255); + break; + case 0x68: /* LD L,B */ + cycles -= cycleTables[0][0x68]; + HL = (HL & ~255) | ((BC >> 8) & 255); + break; + case 0x69: /* LD L,C */ + cycles -= cycleTables[0][0x69]; + HL = (HL & ~255) | (BC & 255); + break; + case 0x6A: /* LD L,D */ + cycles -= cycleTables[0][0x6A]; + HL = (HL & ~255) | ((DE >> 8) & 255); + break; + case 0x6B: /* LD L,E */ + cycles -= cycleTables[0][0x6B]; + HL = (HL & ~255) | (DE & 255); + break; + case 0x6C: /* LD L,H */ + cycles -= cycleTables[0][0x6C]; + HL = (HL & ~255) | ((HL >> 8) & 255); + break; + case 0x6D: /* LD L,L */ + cycles -= cycleTables[0][0x6D]; + /* nop */ + break; + case 0x6E: /* LD L,(HL) */ + cycles -= cycleTables[0][0x6E]; + Setlreg(HL, GetBYTE(HL)); + break; + case 0x6F: /* LD L,A */ + cycles -= cycleTables[0][0x6F]; + HL = (HL & ~255) | ((AF >> 8) & 255); + break; + case 0x70: /* LD (HL),B */ + cycles -= cycleTables[0][0x70]; + PutBYTE(HL, hreg(BC)); + break; + case 0x71: /* LD (HL),C */ + cycles -= cycleTables[0][0x71]; + PutBYTE(HL, lreg(BC)); + break; + case 0x72: /* LD (HL),D */ + cycles -= cycleTables[0][0x72]; + PutBYTE(HL, hreg(DE)); + break; + case 0x73: /* LD (HL),E */ + cycles -= cycleTables[0][0x73]; + PutBYTE(HL, lreg(DE)); + break; + case 0x74: /* LD (HL),H */ + cycles -= cycleTables[0][0x74]; + PutBYTE(HL, hreg(HL)); + break; + case 0x75: /* LD (HL),L */ + cycles -= cycleTables[0][0x75]; + PutBYTE(HL, lreg(HL)); + break; + case 0x76: /* HALT */ + cycles -= cycleTables[0][0x76]; +// ErrorLog("Z80 encountered an unemulated instruction at 0x%04X", (pc-1)&0xFFFF); + goto HALTExit; + case 0x77: /* LD (HL),A */ + cycles -= cycleTables[0][0x77]; + PutBYTE(HL, hreg(AF)); + break; + case 0x78: /* LD A,B */ + cycles -= cycleTables[0][0x78]; + AF = (AF & 255) | (BC & ~255); + break; + case 0x79: /* LD A,C */ + cycles -= cycleTables[0][0x79]; + AF = (AF & 255) | ((BC & 255) << 8); + break; + case 0x7A: /* LD A,D */ + cycles -= cycleTables[0][0x7A]; + AF = (AF & 255) | (DE & ~255); + break; + case 0x7B: /* LD A,E */ + cycles -= cycleTables[0][0x7B]; + AF = (AF & 255) | ((DE & 255) << 8); + break; + case 0x7C: /* LD A,H */ + cycles -= cycleTables[0][0x7C]; + AF = (AF & 255) | (HL & ~255); + break; + case 0x7D: /* LD A,L */ + cycles -= cycleTables[0][0x7D]; + AF = (AF & 255) | ((HL & 255) << 8); + break; + case 0x7E: /* LD A,(HL) */ + cycles -= cycleTables[0][0x7E]; + Sethreg(AF, GetBYTE(HL)); + break; + case 0x7F: /* LD A,A */ + cycles -= cycleTables[0][0x7F]; + /* nop */ + break; + case 0x80: /* ADD A,B */ + cycles -= cycleTables[0][0x80]; + temp = hreg(BC); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x81: /* ADD A,C */ + cycles -= cycleTables[0][0x81]; + temp = lreg(BC); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x82: /* ADD A,D */ + cycles -= cycleTables[0][0x82]; + temp = hreg(DE); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x83: /* ADD A,E */ + cycles -= cycleTables[0][0x83]; + temp = lreg(DE); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x84: /* ADD A,H */ + cycles -= cycleTables[0][0x84]; + temp = hreg(HL); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x85: /* ADD A,L */ + cycles -= cycleTables[0][0x85]; + temp = lreg(HL); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x86: /* ADD A,(HL) */ + cycles -= cycleTables[0][0x86]; + temp = GetBYTE(HL); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x87: /* ADD A,A */ + cycles -= cycleTables[0][0x87]; + temp = hreg(AF); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x88: /* ADC A,B */ + cycles -= cycleTables[0][0x88]; + temp = hreg(BC); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x89: /* ADC A,C */ + cycles -= cycleTables[0][0x89]; + temp = lreg(BC); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8A: /* ADC A,D */ + cycles -= cycleTables[0][0x8A]; + temp = hreg(DE); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8B: /* ADC A,E */ + cycles -= cycleTables[0][0x8B]; + temp = lreg(DE); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8C: /* ADC A,H */ + cycles -= cycleTables[0][0x8C]; + temp = hreg(HL); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8D: /* ADC A,L */ + cycles -= cycleTables[0][0x8D]; + temp = lreg(HL); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8E: /* ADC A,(HL) */ + cycles -= cycleTables[0][0x8E]; + temp = GetBYTE(HL); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8F: /* ADC A,A */ + cycles -= cycleTables[0][0x8F]; + temp = hreg(AF); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x90: /* SUB B */ + cycles -= cycleTables[0][0x90]; + temp = hreg(BC); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x91: /* SUB C */ + cycles -= cycleTables[0][0x91]; + temp = lreg(BC); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x92: /* SUB D */ + cycles -= cycleTables[0][0x92]; + temp = hreg(DE); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x93: /* SUB E */ + cycles -= cycleTables[0][0x93]; + temp = lreg(DE); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x94: /* SUB H */ + cycles -= cycleTables[0][0x94]; + temp = hreg(HL); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x95: /* SUB L */ + cycles -= cycleTables[0][0x95]; + temp = lreg(HL); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x96: /* SUB (HL) */ + cycles -= cycleTables[0][0x96]; + temp = GetBYTE(HL); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x97: /* SUB A */ + cycles -= cycleTables[0][0x97]; + temp = hreg(AF); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x98: /* SBC A,B */ + cycles -= cycleTables[0][0x98]; + temp = hreg(BC); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x99: /* SBC A,C */ + cycles -= cycleTables[0][0x99]; + temp = lreg(BC); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9A: /* SBC A,D */ + cycles -= cycleTables[0][0x9A]; + temp = hreg(DE); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9B: /* SBC A,E */ + cycles -= cycleTables[0][0x9B]; + temp = lreg(DE); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9C: /* SBC A,H */ + cycles -= cycleTables[0][0x9C]; + temp = hreg(HL); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9D: /* SBC A,L */ + cycles -= cycleTables[0][0x9D]; + temp = lreg(HL); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9E: /* SBC A,(HL) */ + cycles -= cycleTables[0][0x9E]; + temp = GetBYTE(HL); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9F: /* SBC A,A */ + cycles -= cycleTables[0][0x9F]; + temp = hreg(AF); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0xA0: /* AND B */ + cycles -= cycleTables[0][0xA0]; + sum = ((AF & (BC)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA1: /* AND C */ + cycles -= cycleTables[0][0xA1]; + sum = ((AF >> 8) & BC) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA2: /* AND D */ + cycles -= cycleTables[0][0xA2]; + sum = ((AF & (DE)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA3: /* AND E */ + cycles -= cycleTables[0][0xA3]; + sum = ((AF >> 8) & DE) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA4: /* AND H */ + cycles -= cycleTables[0][0xA4]; + sum = ((AF & (HL)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA5: /* AND L */ + cycles -= cycleTables[0][0xA5]; + sum = ((AF >> 8) & HL) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA6: /* AND (HL) */ + cycles -= cycleTables[0][0xA6]; + sum = ((AF >> 8) & GetBYTE(HL)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA7: /* AND A */ + cycles -= cycleTables[0][0xA7]; + sum = ((AF & (AF)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA8: /* XOR B */ + cycles -= cycleTables[0][0xA8]; + sum = ((AF ^ (BC)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xA9: /* XOR C */ + cycles -= cycleTables[0][0xA9]; + sum = ((AF >> 8) ^ BC) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAA: /* XOR D */ + cycles -= cycleTables[0][0xAA]; + sum = ((AF ^ (DE)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAB: /* XOR E */ + cycles -= cycleTables[0][0xAB]; + sum = ((AF >> 8) ^ DE) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAC: /* XOR H */ + cycles -= cycleTables[0][0xAC]; + sum = ((AF ^ (HL)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAD: /* XOR L */ + cycles -= cycleTables[0][0xAD]; + sum = ((AF >> 8) ^ HL) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAE: /* XOR (HL) */ + cycles -= cycleTables[0][0xAE]; + sum = ((AF >> 8) ^ GetBYTE(HL)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAF: /* XOR A */ + cycles -= cycleTables[0][0xAF]; + sum = ((AF ^ (AF)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB0: /* OR B */ + cycles -= cycleTables[0][0xB0]; + sum = ((AF | (BC)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB1: /* OR C */ + cycles -= cycleTables[0][0xB1]; + sum = ((AF >> 8) | BC) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB2: /* OR D */ + cycles -= cycleTables[0][0xB2]; + sum = ((AF | (DE)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB3: /* OR E */ + cycles -= cycleTables[0][0xB3]; + sum = ((AF >> 8) | DE) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB4: /* OR H */ + cycles -= cycleTables[0][0xB4]; + sum = ((AF | (HL)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB5: /* OR L */ + cycles -= cycleTables[0][0xB5]; + sum = ((AF >> 8) | HL) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB6: /* OR (HL) */ + cycles -= cycleTables[0][0xB6]; + sum = ((AF >> 8) | GetBYTE(HL)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB7: /* OR A */ + cycles -= cycleTables[0][0xB7]; + sum = ((AF | (AF)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB8: /* CP B */ + cycles -= cycleTables[0][0xB8]; + temp = hreg(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xB9: /* CP C */ + cycles -= cycleTables[0][0xB9]; + temp = lreg(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBA: /* CP D */ + cycles -= cycleTables[0][0xBA]; + temp = hreg(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBB: /* CP E */ + cycles -= cycleTables[0][0xBB]; + temp = lreg(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBC: /* CP H */ + cycles -= cycleTables[0][0xBC]; + temp = hreg(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBD: /* CP L */ + cycles -= cycleTables[0][0xBD]; + temp = lreg(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBE: /* CP (HL) */ + cycles -= cycleTables[0][0xBE]; + temp = GetBYTE(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBF: /* CP A */ + cycles -= cycleTables[0][0xBF]; + temp = hreg(AF); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xC0: /* RET NZ */ + cycles -= cycleTables[0][0xC0]; + if (!TSTFLAG(Z)) POP(pc); + break; + case 0xC1: /* POP BC */ + cycles -= cycleTables[0][0xC1]; + POP(BC); + break; + case 0xC2: /* JP NZ,nnnn */ + cycles -= cycleTables[0][0xC2]; + Jpc(!TSTFLAG(Z)); + break; + case 0xC3: /* JP nnnn */ + cycles -= cycleTables[0][0xC3]; + Jpc(1); + break; + case 0xC4: /* CALL NZ,nnnn */ + cycles -= cycleTables[0][0xC4]; + CALLC(!TSTFLAG(Z)); + break; + case 0xC5: /* PUSH BC */ + cycles -= cycleTables[0][0xC5]; + PUSH(BC); + break; + case 0xC6: /* ADD A,nn */ + cycles -= cycleTables[0][0xC6]; + temp = GetBYTE_pp(pc); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0xC7: /* RST 0 */ + cycles -= cycleTables[0][0xC7]; + PUSH(pc); pc = 0; + break; + case 0xC8: /* RET Z */ + cycles -= cycleTables[0][0xC8]; + if (TSTFLAG(Z)) POP(pc); + break; + case 0xC9: /* RET */ + cycles -= cycleTables[0][0xC9]; + POP(pc); + break; + case 0xCA: /* JP Z,nnnn */ + cycles -= cycleTables[0][0xCA]; + Jpc(TSTFLAG(Z)); + break; + case 0xCB: /* CB prefix */ + adr = HL; + op = GetBYTE(pc); + cycles -= cycleTables[1][op]; + switch (op & 7) { + case 0: ++pc; acu = hreg(BC); break; + case 1: ++pc; acu = lreg(BC); break; + case 2: ++pc; acu = hreg(DE); break; + case 3: ++pc; acu = lreg(DE); break; + case 4: ++pc; acu = hreg(HL); break; + case 5: ++pc; acu = lreg(HL); break; + case 6: ++pc; acu = GetBYTE(adr); break; + case 7: ++pc; acu = hreg(AF); break; + } + switch (op & 0xc0) { + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp) | !!cbits; + } + break; + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | + (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op&7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + case 0: Sethreg(BC, temp); break; + case 1: Setlreg(BC, temp); break; + case 2: Sethreg(DE, temp); break; + case 3: Setlreg(DE, temp); break; + case 4: Sethreg(HL, temp); break; + case 5: Setlreg(HL, temp); break; + case 6: PutBYTE(adr, temp); break; + case 7: Sethreg(AF, temp); break; + } + break; + case 0xCC: /* CALL Z,nnnn */ + cycles -= cycleTables[0][0xCC]; + CALLC(TSTFLAG(Z)); + break; + case 0xCD: /* CALL nnnn */ + cycles -= cycleTables[0][0xCD]; + CALLC(1); + break; + case 0xCE: /* ADC A,nn */ + cycles -= cycleTables[0][0xCE]; + temp = GetBYTE_pp(pc); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0xCF: /* RST 8 */ + cycles -= cycleTables[0][0xCF]; + PUSH(pc); pc = 8; + break; + case 0xD0: /* RET NC */ + cycles -= cycleTables[0][0xD0]; + if (!TSTFLAG(C)) POP(pc); + break; + case 0xD1: /* POP DE */ + cycles -= cycleTables[0][0xD1]; + POP(DE); + break; + case 0xD2: /* JP NC,nnnn */ + cycles -= cycleTables[0][0xD2]; + Jpc(!TSTFLAG(C)); + break; + case 0xD3: /* OUT (nn),A */ + cycles -= cycleTables[0][0xD3]; + OUTPUT(GetBYTE_pp(pc), hreg(AF)); + break; + case 0xD4: /* CALL NC,nnnn */ + cycles -= cycleTables[0][0xD4]; + CALLC(!TSTFLAG(C)); + break; + case 0xD5: /* PUSH DE */ + cycles -= cycleTables[0][0xD5]; + PUSH(DE); + break; + case 0xD6: /* SUB nn */ + cycles -= cycleTables[0][0xD6]; + temp = GetBYTE_pp(pc); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0xD7: /* RST 10H */ + cycles -= cycleTables[0][0xD7]; + PUSH(pc); pc = 0x10; + break; + case 0xD8: /* RET C */ + cycles -= cycleTables[0][0xD8]; + if (TSTFLAG(C)) POP(pc); + break; + case 0xD9: /* EXX */ + cycles -= cycleTables[0][0xD9]; + regs[regs_sel].bc = BC; + regs[regs_sel].de = DE; + regs[regs_sel].hl = HL; + regs_sel = 1 - regs_sel; + BC = regs[regs_sel].bc; + DE = regs[regs_sel].de; + HL = regs[regs_sel].hl; + break; + case 0xDA: /* JP C,nnnn */ + cycles -= cycleTables[0][0xDA]; + Jpc(TSTFLAG(C)); + break; + case 0xDB: /* IN A,(nn) */ + cycles -= cycleTables[0][0xDB]; + Sethreg(AF, INPUT(GetBYTE_pp(pc))); + break; + case 0xDC: /* CALL C,nnnn */ + cycles -= cycleTables[0][0xDC]; + CALLC(TSTFLAG(C)); + break; + case 0xDD: /* DD prefix */ + op = GetBYTE_pp(pc); + switch (op) { + case 0x09: /* ADD IX,BC */ + cycles -= cycleTables[3][0x09]; + IX &= 0xffff; + BC &= 0xffff; + sum = IX + BC; + cbits = (IX ^ BC ^ sum) >> 8; + IX = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x19: /* ADD IX,DE */ + cycles -= cycleTables[3][0x19]; + IX &= 0xffff; + DE &= 0xffff; + sum = IX + DE; + cbits = (IX ^ DE ^ sum) >> 8; + IX = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x21: /* LD IX,nnnn */ + cycles -= cycleTables[3][0x21]; + IX = GetWORD(pc); + pc += 2; + break; + case 0x22: /* LD (nnnn),IX */ + cycles -= cycleTables[3][0x22]; + temp = GetWORD(pc); + PutWORD(temp, IX); + pc += 2; + break; + case 0x23: /* INC IX */ + cycles -= cycleTables[3][0x23]; + ++IX; + break; + case 0x24: /* INC IXH */ + cycles -= cycleTables[3][0x24]; + IX += 0x100; + temp = hreg(IX); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x25: /* DEC IXH */ + cycles -= cycleTables[3][0x25]; + IX -= 0x100; + temp = hreg(IX); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x26: /* LD IXH,nn */ + cycles -= cycleTables[3][0x26]; + Sethreg(IX, GetBYTE_pp(pc)); + break; + case 0x29: /* ADD IX,IX */ + cycles -= cycleTables[3][0x29]; + IX &= 0xffff; + sum = IX + IX; + cbits = (IX ^ IX ^ sum) >> 8; + IX = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x2A: /* LD IX,(nnnn) */ + cycles -= cycleTables[3][0x2A]; + temp = GetWORD(pc); + IX = GetWORD(temp); + pc += 2; + break; + case 0x2B: /* DEC IX */ + cycles -= cycleTables[3][0x2B]; + --IX; + break; + case 0x2C: /* INC IXL */ + cycles -= cycleTables[3][0x2C]; + temp = lreg(IX)+1; + Setlreg(IX, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x2D: /* DEC IXL */ + cycles -= cycleTables[3][0x2D]; + temp = lreg(IX)-1; + Setlreg(IX, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x2E: /* LD IXL,nn */ + cycles -= cycleTables[3][0x2E]; + Setlreg(IX, GetBYTE_pp(pc)); + break; + case 0x34: /* INC (IX+dd) */ + cycles -= cycleTables[3][0x34]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr)+1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x35: /* DEC (IX+dd) */ + cycles -= cycleTables[3][0x35]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr)-1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x36: /* LD (IX+dd),nn */ + cycles -= cycleTables[3][0x36]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, GetBYTE_pp(pc)); + break; + case 0x39: /* ADD IX,SP */ + cycles -= cycleTables[3][0x39]; + IX &= 0xffff; + SP &= 0xffff; + sum = IX + SP; + cbits = (IX ^ SP ^ sum) >> 8; + IX = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x44: /* LD B,IXH */ + cycles -= cycleTables[3][0x44]; + Sethreg(BC, hreg(IX)); + break; + case 0x45: /* LD B,IXL */ + cycles -= cycleTables[3][0x45]; + Sethreg(BC, lreg(IX)); + break; + case 0x46: /* LD B,(IX+dd) */ + cycles -= cycleTables[3][0x46]; + adr = IX + (signed char) GetBYTE_pp(pc); + Sethreg(BC, GetBYTE(adr)); + break; + case 0x4C: /* LD C,IXH */ + cycles -= cycleTables[3][0x4C]; + Setlreg(BC, hreg(IX)); + break; + case 0x4D: /* LD C,IXL */ + cycles -= cycleTables[3][0x4D]; + Setlreg(BC, lreg(IX)); + break; + case 0x4E: /* LD C,(IX+dd) */ + cycles -= cycleTables[3][0x4E]; + adr = IX + (signed char) GetBYTE_pp(pc); + Setlreg(BC, GetBYTE(adr)); + break; + case 0x54: /* LD D,IXH */ + cycles -= cycleTables[3][0x54]; + Sethreg(DE, hreg(IX)); + break; + case 0x55: /* LD D,IXL */ + cycles -= cycleTables[3][0x55]; + Sethreg(DE, lreg(IX)); + break; + case 0x56: /* LD D,(IX+dd) */ + cycles -= cycleTables[3][0x56]; + adr = IX + (signed char) GetBYTE_pp(pc); + Sethreg(DE, GetBYTE(adr)); + break; + case 0x5C: /* LD E,H */ + cycles -= cycleTables[3][0x5C]; + Setlreg(DE, hreg(IX)); + break; + case 0x5D: /* LD E,L */ + cycles -= cycleTables[3][0x5D]; + Setlreg(DE, lreg(IX)); + break; + case 0x5E: /* LD E,(IX+dd) */ + cycles -= cycleTables[3][0x5E]; + adr = IX + (signed char) GetBYTE_pp(pc); + Setlreg(DE, GetBYTE(adr)); + break; + case 0x60: /* LD IXH,B */ + cycles -= cycleTables[3][0x60]; + Sethreg(IX, hreg(BC)); + break; + case 0x61: /* LD IXH,C */ + cycles -= cycleTables[3][0x61]; + Sethreg(IX, lreg(BC)); + break; + case 0x62: /* LD IXH,D */ + cycles -= cycleTables[3][0x62]; + Sethreg(IX, hreg(DE)); + break; + case 0x63: /* LD IXH,E */ + cycles -= cycleTables[3][0x63]; + Sethreg(IX, lreg(DE)); + break; + case 0x64: /* LD IXH,IXH */ + cycles -= cycleTables[3][0x64]; + /* nop */ + break; + case 0x65: /* LD IXH,IXL */ + cycles -= cycleTables[3][0x65]; + Sethreg(IX, lreg(IX)); + break; + case 0x66: /* LD H,(IX+dd) */ + cycles -= cycleTables[3][0x66]; + adr = IX + (signed char) GetBYTE_pp(pc); + Sethreg(HL, GetBYTE(adr)); + break; + case 0x67: /* LD IXH,A */ + cycles -= cycleTables[3][0x67]; + Sethreg(IX, hreg(AF)); + break; + case 0x68: /* LD IXL,B */ + cycles -= cycleTables[3][0x68]; + Setlreg(IX, hreg(BC)); + break; + case 0x69: /* LD IXL,C */ + cycles -= cycleTables[3][0x69]; + Setlreg(IX, lreg(BC)); + break; + case 0x6A: /* LD IXL,D */ + cycles -= cycleTables[3][0x6A]; + Setlreg(IX, hreg(DE)); + break; + case 0x6B: /* LD IXL,E */ + cycles -= cycleTables[3][0x6B]; + Setlreg(IX, lreg(DE)); + break; + case 0x6C: /* LD IXL,IXH */ + cycles -= cycleTables[3][0x6C]; + Setlreg(IX, hreg(IX)); + break; + case 0x6D: /* LD IXL,IXL */ + cycles -= cycleTables[3][0x6D]; + /* nop */ + break; + case 0x6E: /* LD L,(IX+dd) */ + cycles -= cycleTables[3][0x6E]; + adr = IX + (signed char) GetBYTE_pp(pc); + Setlreg(HL, GetBYTE(adr)); + break; + case 0x6F: /* LD IXL,A */ + cycles -= cycleTables[3][0x6F]; + Setlreg(IX, hreg(AF)); + break; + case 0x70: /* LD (IX+dd),B */ + cycles -= cycleTables[3][0x70]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(BC)); + break; + case 0x71: /* LD (IX+dd),C */ + cycles -= cycleTables[3][0x71]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(BC)); + break; + case 0x72: /* LD (IX+dd),D */ + cycles -= cycleTables[3][0x72]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(DE)); + break; + case 0x73: /* LD (IX+dd),E */ + cycles -= cycleTables[3][0x73]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(DE)); + break; + case 0x74: /* LD (IX+dd),H */ + cycles -= cycleTables[3][0x74]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(HL)); + break; + case 0x75: /* LD (IX+dd),L */ + cycles -= cycleTables[3][0x75]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(HL)); + break; + case 0x77: /* LD (IX+dd),A */ + cycles -= cycleTables[3][0x77]; + adr = IX + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(AF)); + break; + case 0x7C: /* LD A,IXH */ + cycles -= cycleTables[3][0x7C]; + Sethreg(AF, hreg(IX)); + break; + case 0x7D: /* LD A,IXL */ + cycles -= cycleTables[3][0x7D]; + Sethreg(AF, lreg(IX)); + break; + case 0x7E: /* LD A,(IX+dd) */ + cycles -= cycleTables[3][0x7E]; + adr = IX + (signed char) GetBYTE_pp(pc); + Sethreg(AF, GetBYTE(adr)); + break; + case 0x84: /* ADD A,IXH */ + cycles -= cycleTables[3][0x84]; + temp = hreg(IX); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x85: /* ADD A,IXL */ + cycles -= cycleTables[3][0x85]; + temp = lreg(IX); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x86: /* ADD A,(IX+dd) */ + cycles -= cycleTables[3][0x86]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8C: /* ADC A,IXH */ + cycles -= cycleTables[3][0x8C]; + temp = hreg(IX); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8D: /* ADC A,IXL */ + cycles -= cycleTables[3][0x8D]; + temp = lreg(IX); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8E: /* ADC A,(IX+dd) */ + cycles -= cycleTables[3][0x8E]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x94: /* SUB IXH */ + cycles -= cycleTables[3][0x94]; + temp = hreg(IX); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x95: /* SUB IXL */ + cycles -= cycleTables[3][0x95]; + temp = lreg(IX); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x96: /* SUB (IX+dd) */ + cycles -= cycleTables[3][0x96]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9C: /* SBC A,IXH */ + cycles -= cycleTables[3][0x9C]; + temp = hreg(IX); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9D: /* SBC A,IXL */ + cycles -= cycleTables[3][0x9D]; + temp = lreg(IX); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9E: /* SBC A,(IX+dd) */ + cycles -= cycleTables[3][0x9E]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0xA4: /* AND IXH */ + cycles -= cycleTables[3][0xA4]; + sum = ((AF & (IX)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA5: /* AND IXL */ + cycles -= cycleTables[3][0xA5]; + sum = ((AF >> 8) & IX) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA6: /* AND (IX+dd) */ + cycles -= cycleTables[3][0xA6]; + adr = IX + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) & GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xAC: /* XOR IXH */ + cycles -= cycleTables[3][0xAC]; + sum = ((AF ^ (IX)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAD: /* XOR IXL */ + cycles -= cycleTables[3][0xAD]; + sum = ((AF >> 8) ^ IX) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAE: /* XOR (IX+dd) */ + cycles -= cycleTables[3][0xAE]; + adr = IX + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) ^ GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB4: /* OR IXH */ + cycles -= cycleTables[3][0xB4]; + sum = ((AF | (IX)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB5: /* OR IXL */ + cycles -= cycleTables[3][0xB5]; + sum = ((AF >> 8) | IX) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB6: /* OR (IX+dd) */ + cycles -= cycleTables[3][0xB6]; + adr = IX + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) | GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xBC: /* CP IXH */ + cycles -= cycleTables[3][0xBC]; + temp = hreg(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBD: /* CP IXL */ + cycles -= cycleTables[3][0xBD]; + temp = lreg(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBE: /* CP (IX+dd) */ + cycles -= cycleTables[3][0xBE]; + adr = IX + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xCB: /* CB prefix */ + adr = IX + (signed char) GetBYTE_pp(pc); + adr = adr; + op = GetBYTE(pc); + cycles -= cycleTables[4][op]; + switch (op & 7) { + case 0: ++pc; acu = hreg(BC); break; + case 1: ++pc; acu = lreg(BC); break; + case 2: ++pc; acu = hreg(DE); break; + case 3: ++pc; acu = lreg(DE); break; + case 4: ++pc; acu = hreg(HL); break; + case 5: ++pc; acu = lreg(HL); break; + case 6: ++pc; acu = GetBYTE(adr); break; + case 7: ++pc; acu = hreg(AF); break; + } + switch (op & 0xc0) { + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp) | !!cbits; + } + break; + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | + (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op&7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + case 0: Sethreg(BC, temp); break; + case 1: Setlreg(BC, temp); break; + case 2: Sethreg(DE, temp); break; + case 3: Setlreg(DE, temp); break; + case 4: Sethreg(HL, temp); break; + case 5: Setlreg(HL, temp); break; + case 6: PutBYTE(adr, temp); break; + case 7: Sethreg(AF, temp); break; + } + break; + case 0xE1: /* POP IX */ + cycles -= cycleTables[3][0xE1]; + POP(IX); + break; + case 0xE3: /* EX (SP),IX */ + cycles -= cycleTables[3][0xE3]; + temp = IX; POP(IX); PUSH(temp); + break; + case 0xE5: /* PUSH IX */ + cycles -= cycleTables[3][0xE5]; + PUSH(IX); + break; + case 0xE9: /* JP (IX) */ + cycles -= cycleTables[3][0xE9]; + pc = IX; + break; + case 0xF9: /* LD SP,IX */ + cycles -= cycleTables[3][0xF9]; + SP = IX; + break; + default: pc--; /* ignore DD */ + } + break; + case 0xDE: /* SBC A,nn */ + cycles -= cycleTables[0][0xDE]; + temp = GetBYTE_pp(pc); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0xDF: /* RST 18H */ + cycles -= cycleTables[0][0xDF]; + PUSH(pc); pc = 0x18; + break; + case 0xE0: /* RET PO */ + cycles -= cycleTables[0][0xE0]; + if (!TSTFLAG(P)) POP(pc); + break; + case 0xE1: /* POP HL */ + cycles -= cycleTables[0][0xE1]; + POP(HL); + break; + case 0xE2: /* JP PO,nnnn */ + cycles -= cycleTables[0][0xE2]; + Jpc(!TSTFLAG(P)); + break; + case 0xE3: /* EX (SP),HL */ + cycles -= cycleTables[0][0xE3]; + temp = HL; POP(HL); PUSH(temp); + break; + case 0xE4: /* CALL PO,nnnn */ + cycles -= cycleTables[0][0xE4]; + CALLC(!TSTFLAG(P)); + break; + case 0xE5: /* PUSH HL */ + cycles -= cycleTables[0][0xE5]; + PUSH(HL); + break; + case 0xE6: /* AND nn */ + cycles -= cycleTables[0][0xE6]; + sum = ((AF >> 8) & GetBYTE_pp(pc)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xE7: /* RST 20H */ + cycles -= cycleTables[0][0xE7]; + PUSH(pc); pc = 0x20; + break; + case 0xE8: /* RET PE */ + cycles -= cycleTables[0][0xE8]; + if (TSTFLAG(P)) POP(pc); + break; + case 0xE9: /* JP (HL) */ + cycles -= cycleTables[0][0xE9]; + pc = HL; + break; + case 0xEA: /* JP PE,nnnn */ + cycles -= cycleTables[0][0xEA]; + Jpc(TSTFLAG(P)); + break; + case 0xEB: /* EX DE,HL */ + cycles -= cycleTables[0][0xEB]; + temp = HL; HL = DE; DE = temp; + break; + case 0xEC: /* CALL PE,nnnn */ + cycles -= cycleTables[0][0xEC]; + CALLC(TSTFLAG(P)); + break; + case 0xED: /* ED prefix */ + op = GetBYTE_pp(pc); + switch (op) { + case 0x40: /* IN B,(C) */ + cycles -= cycleTables[2][0x40]; + temp = INPUT(lreg(BC)); + Sethreg(BC, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x41: /* OUT (C),B */ + cycles -= cycleTables[2][0x41]; + OUTPUT(lreg(BC), hreg(BC)); + break; + case 0x42: /* SBC HL,BC */ + cycles -= cycleTables[2][0x42]; + HL &= 0xffff; + BC &= 0xffff; + sum = HL - BC - TSTFLAG(C); + cbits = (HL ^ BC ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + break; + case 0x43: /* LD (nnnn),BC */ + cycles -= cycleTables[2][0x43]; + temp = GetWORD(pc); + PutWORD(temp, BC); + pc += 2; + break; + case 0x44: /* NEG */ + cycles -= cycleTables[2][0x44]; + temp = hreg(AF); + AF = (-(AF & 0xff00) & 0xff00); + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | + (((temp & 0x0f) != 0) << 4) | ((temp == 0x80) << 2) | + 2 | (temp != 0); + break; + case 0x45: /* RETN */ + cycles -= cycleTables[2][0x45]; + iff |= iff >> 1; + POP(pc); + break; + case 0x46: /* IM 0 */ + cycles -= cycleTables[2][0x46]; + im = 0; // interrupt mode 0 + break; + case 0x47: /* LD I,A */ + cycles -= cycleTables[2][0x47]; + ir = (ir & 255) | (AF & ~255); + break; + case 0x48: /* IN C,(C) */ + cycles -= cycleTables[2][0x48]; + temp = INPUT(lreg(BC)); + Setlreg(BC, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x49: /* OUT (C),C */ + cycles -= cycleTables[2][0x49]; + OUTPUT(lreg(BC), lreg(BC)); + break; + case 0x4A: /* ADC HL,BC */ + cycles -= cycleTables[2][0x4A]; + HL &= 0xffff; + BC &= 0xffff; + sum = HL + BC + TSTFLAG(C); + cbits = (HL ^ BC ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x4B: /* LD BC,(nnnn) */ + cycles -= cycleTables[2][0x4B]; + temp = GetWORD(pc); + BC = GetWORD(temp); + pc += 2; + break; + case 0x4D: /* RETI */ + cycles -= cycleTables[2][0x4D]; + iff |= iff >> 1; + POP(pc); + break; + case 0x4F: /* LD R,A */ + cycles -= cycleTables[2][0x4F]; + ir = (ir & ~255) | ((AF >> 8) & 255); + break; + case 0x50: /* IN D,(C) */ + cycles -= cycleTables[2][0x50]; + temp = INPUT(lreg(BC)); + Sethreg(DE, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x51: /* OUT (C),D */ + cycles -= cycleTables[2][0x51]; + OUTPUT(lreg(BC), hreg(DE)); + break; + case 0x52: /* SBC HL,DE */ + cycles -= cycleTables[2][0x52]; + HL &= 0xffff; + DE &= 0xffff; + sum = HL - DE - TSTFLAG(C); + cbits = (HL ^ DE ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + break; + case 0x53: /* LD (nnnn),DE */ + cycles -= cycleTables[2][0x53]; + temp = GetWORD(pc); + PutWORD(temp, DE); + pc += 2; + break; + case 0x56: /* IM 1 */ + cycles -= cycleTables[2][0x56]; + im = 1; // interrupt mode 1 + break; + case 0x57: /* LD A,I */ + cycles -= cycleTables[2][0x57]; + AF = (AF & 0x29) | (ir & ~255) | ((ir >> 8) & 0x80) | (((ir & ~255) == 0) << 6) | ((iff & 2) << 1); + break; + case 0x58: /* IN E,(C) */ + cycles -= cycleTables[2][0x58]; + temp = INPUT(lreg(BC)); + Setlreg(DE, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x59: /* OUT (C),E */ + cycles -= cycleTables[2][0x59]; + OUTPUT(lreg(BC), lreg(DE)); + break; + case 0x5A: /* ADC HL,DE */ + cycles -= cycleTables[2][0x5A]; + HL &= 0xffff; + DE &= 0xffff; + sum = HL + DE + TSTFLAG(C); + cbits = (HL ^ DE ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x5B: /* LD DE,(nnnn) */ + cycles -= cycleTables[2][0x5B]; + temp = GetWORD(pc); + DE = GetWORD(temp); + pc += 2; + break; + case 0x5E: /* IM 2 */ + cycles -= cycleTables[2][0x5E]; + im = 2; // interrupt mode 2 + break; + case 0x5F: /* LD A,R */ + cycles -= cycleTables[2][0x5F]; + AF = (AF & 0x29) | ((ir & 255) << 8) | (ir & 0x80) | (((ir & 255) == 0) << 6) | ((iff & 2) << 1); + break; + case 0x60: /* IN H,(C) */ + cycles -= cycleTables[2][0x60]; + temp = INPUT(lreg(BC)); + Sethreg(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x61: /* OUT (C),H */ + cycles -= cycleTables[2][0x61]; + OUTPUT(lreg(BC), hreg(HL)); + break; + case 0x62: /* SBC HL,HL */ + cycles -= cycleTables[2][0x62]; + HL &= 0xffff; + sum = HL - HL - TSTFLAG(C); + cbits = (HL ^ HL ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + break; + case 0x63: /* LD (nnnn),HL */ + cycles -= cycleTables[2][0x63]; + temp = GetWORD(pc); + PutWORD(temp, HL); + pc += 2; + break; + case 0x67: /* RRD */ + cycles -= cycleTables[2][0x67]; + temp = GetBYTE(HL); + acu = hreg(AF); + PutBYTE(HL, hdig(temp) | (ldig(acu) << 4)); + acu = (acu & 0xf0) | ldig(temp); + AF = (acu << 8) | (acu & 0xa8) | (((acu & 0xff) == 0) << 6) | + partab[acu] | (AF & 1); + break; + case 0x68: /* IN L,(C) */ + cycles -= cycleTables[2][0x68]; + temp = INPUT(lreg(BC)); + Setlreg(HL, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x69: /* OUT (C),L */ + cycles -= cycleTables[2][0x69]; + OUTPUT(lreg(BC), lreg(HL)); + break; + case 0x6A: /* ADC HL,HL */ + cycles -= cycleTables[2][0x6A]; + HL &= 0xffff; + sum = HL + HL + TSTFLAG(C); + cbits = (HL ^ HL ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x6B: /* LD HL,(nnnn) */ + cycles -= cycleTables[2][0x6B]; + temp = GetWORD(pc); + HL = GetWORD(temp); + pc += 2; + break; + case 0x6F: /* RLD */ + cycles -= cycleTables[2][0x6F]; + temp = GetBYTE(HL); + acu = hreg(AF); + PutBYTE(HL, (ldig(temp) << 4) | ldig(acu)); + acu = (acu & 0xf0) | hdig(temp); + AF = (acu << 8) | (acu & 0xa8) | (((acu & 0xff) == 0) << 6) | + partab[acu] | (AF & 1); + break; + case 0x70: /* IN (C) */ + cycles -= cycleTables[2][0x70]; + temp = INPUT(lreg(BC)); + Setlreg(temp, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x71: /* OUT (C),0 */ + cycles -= cycleTables[2][0x71]; + OUTPUT(lreg(BC), 0); + break; + case 0x72: /* SBC HL,SP */ + cycles -= cycleTables[2][0x72]; + HL &= 0xffff; + SP &= 0xffff; + sum = HL - SP - TSTFLAG(C); + cbits = (HL ^ SP ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + break; + case 0x73: /* LD (nnnn),SP */ + cycles -= cycleTables[2][0x73]; + temp = GetWORD(pc); + PutWORD(temp, SP); + pc += 2; + break; + case 0x78: /* IN A,(C) */ + cycles -= cycleTables[2][0x78]; + temp = INPUT(lreg(BC)); + Sethreg(AF, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp); + break; + case 0x79: /* OUT (C),A */ + cycles -= cycleTables[2][0x79]; + OUTPUT(lreg(BC), hreg(AF)); + break; + case 0x7A: /* ADC HL,SP */ + cycles -= cycleTables[2][0x7A]; + HL &= 0xffff; + SP &= 0xffff; + sum = HL + SP + TSTFLAG(C); + cbits = (HL ^ SP ^ sum) >> 8; + HL = sum; + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | + (((sum & 0xffff) == 0) << 6) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x7B: /* LD SP,(nnnn) */ + cycles -= cycleTables[2][0x7B]; + temp = GetWORD(pc); + SP = GetWORD(temp); + pc += 2; + break; + case 0xA0: /* LDI */ + cycles -= cycleTables[2][0xA0]; + acu = GetBYTE_pp(HL); + PutBYTE_pp(DE, acu); + acu += hreg(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & 0xffff) != 0) << 2); + break; + case 0xA1: /* CPI */ + cycles -= cycleTables[2][0xA1]; + acu = hreg(AF); + temp = GetBYTE_pp(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits&16)>>4))&2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & 0xffff) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + case 0xA2: /* INI */ + cycles -= cycleTables[2][0xA2]; + PutBYTE(HL, INPUT(lreg(BC))); ++HL; + SETFLAG(N, 1); + SETFLAG(P, (--BC & 0xffff) != 0); + break; + case 0xA3: /* OUTI */ + cycles -= cycleTables[2][0xA3]; + OUTPUT(lreg(BC), GetBYTE(HL)); ++HL; + SETFLAG(N, 1); + Sethreg(BC, hreg(BC) - 1); + SETFLAG(Z, hreg(BC) == 0); + break; + case 0xA8: /* LDD */ + cycles -= cycleTables[2][0xA8]; + acu = GetBYTE_mm(HL); + PutBYTE_mm(DE, acu); + acu += hreg(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & 0xffff) != 0) << 2); + break; + case 0xA9: /* CPD */ + cycles -= cycleTables[2][0xA9]; + acu = hreg(AF); + temp = GetBYTE_mm(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits&16)>>4))&2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & 0xffff) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + case 0xAA: /* IND */ + cycles -= cycleTables[2][0xAA]; + PutBYTE(HL, INPUT(lreg(BC))); --HL; + SETFLAG(N, 1); + Sethreg(BC, lreg(BC) - 1); + SETFLAG(Z, lreg(BC) == 0); + break; + case 0xAB: /* OUTD */ + cycles -= cycleTables[2][0xAB]; + OUTPUT(lreg(BC), GetBYTE(HL)); --HL; + SETFLAG(N, 1); + Sethreg(BC, hreg(BC) - 1); + SETFLAG(Z, hreg(BC) == 0); + break; + case 0xB0: /* LDIR */ + cycles -= cycleTables[2][0xB0]; + acu = hreg(AF); + BC &= 0xffff; + do { + acu = GetBYTE_pp(HL); + PutBYTE_pp(DE, acu); + } while (--BC); + acu += hreg(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + case 0xB1: /* CPIR */ + cycles -= cycleTables[2][0xB1]; + acu = hreg(AF); + BC &= 0xffff; + do { + temp = GetBYTE_pp(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits&16)>>4))&2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + case 0xB2: /* INIR */ + cycles -= cycleTables[2][0xB2]; + temp = hreg(BC); + do { + PutBYTE(HL, INPUT(lreg(BC))); ++HL; + } while (--temp); + Sethreg(BC, 0); + SETFLAG(N, 1); + SETFLAG(Z, 1); + break; + case 0xB3: /* OTIR */ + cycles -= cycleTables[2][0xB3]; + temp = hreg(BC); + do { + OUTPUT(lreg(BC), GetBYTE(HL)); ++HL; + } while (--temp); + Sethreg(BC, 0); + SETFLAG(N, 1); + SETFLAG(Z, 1); + break; + case 0xB8: /* LDDR */ + cycles -= cycleTables[2][0xB8]; + BC &= 0xffff; + do { + acu = GetBYTE_mm(HL); + PutBYTE_mm(DE, acu); + } while (--BC); + acu += hreg(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + case 0xB9: /* CPDR */ + cycles -= cycleTables[2][0xB9]; + acu = hreg(AF); + BC &= 0xffff; + do { + temp = GetBYTE_mm(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits&16)>>4))&2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + case 0xBA: /* INDR */ + cycles -= cycleTables[2][0xBA]; + temp = hreg(BC); + do { + PutBYTE(HL, INPUT(lreg(BC))); --HL; + } while (--temp); + Sethreg(BC, 0); + SETFLAG(N, 1); + SETFLAG(Z, 1); + break; + case 0xBB: /* OTDR */ + cycles -= cycleTables[2][0xBB]; + temp = hreg(BC); + do { + OUTPUT(lreg(BC), GetBYTE(HL)); --HL; + } while (--temp); + Sethreg(BC, 0); + SETFLAG(N, 1); + SETFLAG(Z, 1); + break; + default: if (0x40 <= op && op <= 0x7f) pc--; /* ignore ED */ + } + break; + case 0xEE: /* XOR nn */ + cycles -= cycleTables[0][0xEE]; + sum = ((AF >> 8) ^ GetBYTE_pp(pc)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xEF: /* RST 28H */ + cycles -= cycleTables[0][0xEF]; + PUSH(pc); pc = 0x28; + break; + case 0xF0: /* RET P */ + cycles -= cycleTables[0][0xF0]; + if (!TSTFLAG(S)) POP(pc); + break; + case 0xF1: /* POP AF */ + cycles -= cycleTables[0][0xF1]; + POP(AF); + break; + case 0xF2: /* JP P,nnnn */ + cycles -= cycleTables[0][0xF2]; + Jpc(!TSTFLAG(S)); + break; + case 0xF3: /* DI */ + cycles -= cycleTables[0][0xF3]; + iff = 0; + break; + case 0xF4: /* CALL P,nnnn */ + cycles -= cycleTables[0][0xF4]; + CALLC(!TSTFLAG(S)); + break; + case 0xF5: /* PUSH AF */ + cycles -= cycleTables[0][0xF5]; + PUSH(AF); + break; + case 0xF6: /* OR nn */ + cycles -= cycleTables[0][0xF6]; + sum = ((AF >> 8) | GetBYTE_pp(pc)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xF7: /* RST 30H */ + cycles -= cycleTables[0][0xF7]; + PUSH(pc); pc = 0x30; + break; + case 0xF8: /* RET M */ + cycles -= cycleTables[0][0xF8]; + if (TSTFLAG(S)) POP(pc); + break; + case 0xF9: /* LD SP,HL */ + cycles -= cycleTables[0][0xF9]; + SP = HL; + break; + case 0xFA: /* JP M,nnnn */ + cycles -= cycleTables[0][0xFA]; + Jpc(TSTFLAG(S)); + break; + case 0xFB: /* EI */ + cycles -= cycleTables[0][0xFB]; + iff = 3; + break; + case 0xFC: /* CALL M,nnnn */ + cycles -= cycleTables[0][0xFC]; + CALLC(TSTFLAG(S)); + break; + case 0xFD: /* FD prefix */ + op = GetBYTE_pp(pc); + switch (op) { + case 0x09: /* ADD IY,BC */ + cycles -= cycleTables[3][0x09]; + IY &= 0xffff; + BC &= 0xffff; + sum = IY + BC; + cbits = (IY ^ BC ^ sum) >> 8; + IY = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x19: /* ADD IY,DE */ + cycles -= cycleTables[3][0x19]; + IY &= 0xffff; + DE &= 0xffff; + sum = IY + DE; + cbits = (IY ^ DE ^ sum) >> 8; + IY = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x21: /* LD IY,nnnn */ + cycles -= cycleTables[3][0x21]; + IY = GetWORD(pc); + pc += 2; + break; + case 0x22: /* LD (nnnn),IY */ + cycles -= cycleTables[3][0x22]; + temp = GetWORD(pc); + PutWORD(temp, IY); + pc += 2; + break; + case 0x23: /* INC IY */ + cycles -= cycleTables[3][0x23]; + ++IY; + break; + case 0x24: /* INC IYH */ + cycles -= cycleTables[3][0x24]; + IY += 0x100; + temp = hreg(IY); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x25: /* DEC IYH */ + cycles -= cycleTables[3][0x25]; + IY -= 0x100; + temp = hreg(IY); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x26: /* LD IYH,nn */ + cycles -= cycleTables[3][0x26]; + Sethreg(IY, GetBYTE_pp(pc)); + break; + case 0x29: /* ADD IY,IY */ + cycles -= cycleTables[3][0x29]; + IY &= 0xffff; + sum = IY + IY; + cbits = (IY ^ IY ^ sum) >> 8; + IY = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x2A: /* LD IY,(nnnn) */ + cycles -= cycleTables[3][0x2A]; + temp = GetWORD(pc); + IY = GetWORD(temp); + pc += 2; + break; + case 0x2B: /* DEC IY */ + cycles -= cycleTables[3][0x2B]; + --IY; + break; + case 0x2C: /* INC IYL */ + cycles -= cycleTables[3][0x2C]; + temp = lreg(IY)+1; + Setlreg(IY, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x2D: /* DEC IYL */ + cycles -= cycleTables[3][0x2D]; + temp = lreg(IY)-1; + Setlreg(IY, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x2E: /* LD IYL,nn */ + cycles -= cycleTables[3][0x2E]; + Setlreg(IY, GetBYTE_pp(pc)); + break; + case 0x34: /* INC (IY+dd) */ + cycles -= cycleTables[3][0x34]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr)+1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | + ((temp == 0x80) << 2); + break; + case 0x35: /* DEC (IY+dd) */ + cycles -= cycleTables[3][0x35]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr)-1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | + ((temp == 0x7f) << 2) | 2; + break; + case 0x36: /* LD (IY+dd),nn */ + cycles -= cycleTables[3][0x36]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, GetBYTE_pp(pc)); + break; + case 0x39: /* ADD IY,SP */ + cycles -= cycleTables[3][0x39]; + IY &= 0xffff; + SP &= 0xffff; + sum = IY + SP; + cbits = (IY ^ SP ^ sum) >> 8; + IY = sum; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0x44: /* LD B,IYH */ + cycles -= cycleTables[3][0x44]; + Sethreg(BC, hreg(IY)); + break; + case 0x45: /* LD B,IYL */ + cycles -= cycleTables[3][0x45]; + Sethreg(BC, lreg(IY)); + break; + case 0x46: /* LD B,(IY+dd) */ + cycles -= cycleTables[3][0x46]; + adr = IY + (signed char) GetBYTE_pp(pc); + Sethreg(BC, GetBYTE(adr)); + break; + case 0x4C: /* LD C,IYH */ + cycles -= cycleTables[3][0x4C]; + Setlreg(BC, hreg(IY)); + break; + case 0x4D: /* LD C,IYL */ + cycles -= cycleTables[3][0x4D]; + Setlreg(BC, lreg(IY)); + break; + case 0x4E: /* LD C,(IY+dd) */ + cycles -= cycleTables[3][0x4E]; + adr = IY + (signed char) GetBYTE_pp(pc); + Setlreg(BC, GetBYTE(adr)); + break; + case 0x54: /* LD D,IYH */ + cycles -= cycleTables[3][0x54]; + Sethreg(DE, hreg(IY)); + break; + case 0x55: /* LD D,IYL */ + cycles -= cycleTables[3][0x55]; + Sethreg(DE, lreg(IY)); + break; + case 0x56: /* LD D,(IY+dd) */ + cycles -= cycleTables[3][0x56]; + adr = IY + (signed char) GetBYTE_pp(pc); + Sethreg(DE, GetBYTE(adr)); + break; + case 0x5C: /* LD E,H */ + cycles -= cycleTables[3][0x5C]; + Setlreg(DE, hreg(IY)); + break; + case 0x5D: /* LD E,L */ + cycles -= cycleTables[3][0x5D]; + Setlreg(DE, lreg(IY)); + break; + case 0x5E: /* LD E,(IY+dd) */ + cycles -= cycleTables[3][0x5E]; + adr = IY + (signed char) GetBYTE_pp(pc); + Setlreg(DE, GetBYTE(adr)); + break; + case 0x60: /* LD IYH,B */ + cycles -= cycleTables[3][0x60]; + Sethreg(IY, hreg(BC)); + break; + case 0x61: /* LD IYH,C */ + cycles -= cycleTables[3][0x61]; + Sethreg(IY, lreg(BC)); + break; + case 0x62: /* LD IYH,D */ + cycles -= cycleTables[3][0x62]; + Sethreg(IY, hreg(DE)); + break; + case 0x63: /* LD IYH,E */ + cycles -= cycleTables[3][0x63]; + Sethreg(IY, lreg(DE)); + break; + case 0x64: /* LD IYH,IYH */ + cycles -= cycleTables[3][0x64]; + /* nop */ + break; + case 0x65: /* LD IYH,IYL */ + cycles -= cycleTables[3][0x65]; + Sethreg(IY, lreg(IY)); + break; + case 0x66: /* LD H,(IY+dd) */ + cycles -= cycleTables[3][0x66]; + adr = IY + (signed char) GetBYTE_pp(pc); + Sethreg(HL, GetBYTE(adr)); + break; + case 0x67: /* LD IYH,A */ + cycles -= cycleTables[3][0x67]; + Sethreg(IY, hreg(AF)); + break; + case 0x68: /* LD IYL,B */ + cycles -= cycleTables[3][0x68]; + Setlreg(IY, hreg(BC)); + break; + case 0x69: /* LD IYL,C */ + cycles -= cycleTables[3][0x69]; + Setlreg(IY, lreg(BC)); + break; + case 0x6A: /* LD IYL,D */ + cycles -= cycleTables[3][0x6A]; + Setlreg(IY, hreg(DE)); + break; + case 0x6B: /* LD IYL,E */ + cycles -= cycleTables[3][0x6B]; + Setlreg(IY, lreg(DE)); + break; + case 0x6C: /* LD IYL,IYH */ + cycles -= cycleTables[3][0x6C]; + Setlreg(IY, hreg(IY)); + break; + case 0x6D: /* LD IYL,IYL */ + cycles -= cycleTables[3][0x6D]; + /* nop */ + break; + case 0x6E: /* LD L,(IY+dd) */ + cycles -= cycleTables[3][0x6E]; + adr = IY + (signed char) GetBYTE_pp(pc); + Setlreg(HL, GetBYTE(adr)); + break; + case 0x6F: /* LD IYL,A */ + cycles -= cycleTables[3][0x6F]; + Setlreg(IY, hreg(AF)); + break; + case 0x70: /* LD (IY+dd),B */ + cycles -= cycleTables[3][0x70]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(BC)); + break; + case 0x71: /* LD (IY+dd),C */ + cycles -= cycleTables[3][0x71]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(BC)); + break; + case 0x72: /* LD (IY+dd),D */ + cycles -= cycleTables[3][0x72]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(DE)); + break; + case 0x73: /* LD (IY+dd),E */ + cycles -= cycleTables[3][0x73]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(DE)); + break; + case 0x74: /* LD (IY+dd),H */ + cycles -= cycleTables[3][0x74]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(HL)); + break; + case 0x75: /* LD (IY+dd),L */ + cycles -= cycleTables[3][0x75]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, lreg(HL)); + break; + case 0x77: /* LD (IY+dd),A */ + cycles -= cycleTables[3][0x77]; + adr = IY + (signed char) GetBYTE_pp(pc); + PutBYTE(adr, hreg(AF)); + break; + case 0x7C: /* LD A,IYH */ + cycles -= cycleTables[3][0x7C]; + Sethreg(AF, hreg(IY)); + break; + case 0x7D: /* LD A,IYL */ + cycles -= cycleTables[3][0x7D]; + Sethreg(AF, lreg(IY)); + break; + case 0x7E: /* LD A,(IY+dd) */ + cycles -= cycleTables[3][0x7E]; + adr = IY + (signed char) GetBYTE_pp(pc); + Sethreg(AF, GetBYTE(adr)); + break; + case 0x84: /* ADD A,IYH */ + cycles -= cycleTables[3][0x84]; + temp = hreg(IY); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x85: /* ADD A,IYL */ + cycles -= cycleTables[3][0x85]; + temp = lreg(IY); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x86: /* ADD A,(IY+dd) */ + cycles -= cycleTables[3][0x86]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8C: /* ADC A,IYH */ + cycles -= cycleTables[3][0x8C]; + temp = hreg(IY); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8D: /* ADC A,IYL */ + cycles -= cycleTables[3][0x8D]; + temp = lreg(IY); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x8E: /* ADC A,(IY+dd) */ + cycles -= cycleTables[3][0x8E]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + break; + case 0x94: /* SUB IYH */ + cycles -= cycleTables[3][0x94]; + temp = hreg(IY); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x95: /* SUB IYL */ + cycles -= cycleTables[3][0x95]; + temp = lreg(IY); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x96: /* SUB (IY+dd) */ + cycles -= cycleTables[3][0x96]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9C: /* SBC A,IYH */ + cycles -= cycleTables[3][0x9C]; + temp = hreg(IY); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9D: /* SBC A,IYL */ + cycles -= cycleTables[3][0x9D]; + temp = lreg(IY); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0x9E: /* SBC A,(IY+dd) */ + cycles -= cycleTables[3][0x9E]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + acu = hreg(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = ((sum & 0xff) << 8) | (sum & 0xa8) | + (((sum & 0xff) == 0) << 6) | (cbits & 0x10) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + ((cbits >> 8) & 1); + break; + case 0xA4: /* AND IYH */ + cycles -= cycleTables[3][0xA4]; + sum = ((AF & (IY)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | + ((sum == 0) << 6) | 0x10 | partab[sum]; + break; + case 0xA5: /* AND IYL */ + cycles -= cycleTables[3][0xA5]; + sum = ((AF >> 8) & IY) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xA6: /* AND (IY+dd) */ + cycles -= cycleTables[3][0xA6]; + adr = IY + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) & GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | 0x10 | + ((sum == 0) << 6) | partab[sum]; + break; + case 0xAC: /* XOR IYH */ + cycles -= cycleTables[3][0xAC]; + sum = ((AF ^ (IY)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAD: /* XOR IYL */ + cycles -= cycleTables[3][0xAD]; + sum = ((AF >> 8) ^ IY) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xAE: /* XOR (IY+dd) */ + cycles -= cycleTables[3][0xAE]; + adr = IY + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) ^ GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB4: /* OR IYH */ + cycles -= cycleTables[3][0xB4]; + sum = ((AF | (IY)) >> 8) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB5: /* OR IYL */ + cycles -= cycleTables[3][0xB5]; + sum = ((AF >> 8) | IY) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xB6: /* OR (IY+dd) */ + cycles -= cycleTables[3][0xB6]; + adr = IY + (signed char) GetBYTE_pp(pc); + sum = ((AF >> 8) | GetBYTE(adr)) & 0xff; + AF = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | partab[sum]; + break; + case 0xBC: /* CP IYH */ + cycles -= cycleTables[3][0xBC]; + temp = hreg(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBD: /* CP IYL */ + cycles -= cycleTables[3][0xBD]; + temp = lreg(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xBE: /* CP (IY+dd) */ + cycles -= cycleTables[3][0xBE]; + adr = IY + (signed char) GetBYTE_pp(pc); + temp = GetBYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xCB: /* CB prefix */ + adr = IY + (signed char) GetBYTE_pp(pc); + adr = adr; + op = GetBYTE(pc); + cycles -= cycleTables[4][op]; + switch (op & 7) { + case 0: ++pc; acu = hreg(BC); break; + case 1: ++pc; acu = lreg(BC); break; + case 2: ++pc; acu = hreg(DE); break; + case 3: ++pc; acu = lreg(DE); break; + case 4: ++pc; acu = hreg(HL); break; + case 5: ++pc; acu = lreg(HL); break; + case 6: ++pc; acu = GetBYTE(adr); break; + case 7: ++pc; acu = hreg(AF); break; + } + switch (op & 0xc0) { + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | (temp & 0xa8) | + (((temp & 0xff) == 0) << 6) | + parity(temp) | !!cbits; + } + break; + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | + (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op&7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + case 0: Sethreg(BC, temp); break; + case 1: Setlreg(BC, temp); break; + case 2: Sethreg(DE, temp); break; + case 3: Setlreg(DE, temp); break; + case 4: Sethreg(HL, temp); break; + case 5: Setlreg(HL, temp); break; + case 6: PutBYTE(adr, temp); break; + case 7: Sethreg(AF, temp); break; + } + break; + case 0xE1: /* POP IY */ + cycles -= cycleTables[3][0xE1]; + POP(IY); + break; + case 0xE3: /* EX (SP),IY */ + cycles -= cycleTables[3][0xE3]; + temp = IY; POP(IY); PUSH(temp); + break; + case 0xE5: /* PUSH IY */ + cycles -= cycleTables[3][0xE5]; + PUSH(IY); + break; + case 0xE9: /* JP (IY) */ + cycles -= cycleTables[3][0xE9]; + pc = IY; + break; + case 0xF9: /* LD SP,IY */ + cycles -= cycleTables[3][0xF9]; + SP = IY; + break; + default: pc--; /* ignore DD */ + } + break; + case 0xFE: /* CP nn */ + cycles -= cycleTables[0][0xFE]; + temp = GetBYTE_pp(pc); + AF = (AF & ~0x28) | (temp & 0x28); + acu = hreg(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | (sum & 0x80) | + (((sum & 0xff) == 0) << 6) | (temp & 0x28) | + (((cbits >> 6) ^ (cbits >> 5)) & 4) | 2 | + (cbits & 0x10) | ((cbits >> 8) & 1); + break; + case 0xFF: /* RST 38H */ + cycles -= cycleTables[0][0xFF]; + PUSH(pc); pc = 0x38; + break; + } + + // Interrupts + + if (nmiTrigger) // NMI triggered (higher priority than INT) + { + /* + * NMI sequence: + * + * - Push pc on stack + * - Set pc to NMI vector (0x0066) + * - Copy IFF1 to IFF2 (save interrupt enable status) + * - Clear IFF1 (disable interrupts) + * - Un-halt CPU (if in HALT state) + */ + +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + Debug->CPUException(Z80_EX_NMI); +#endif // SUPERMODEL_DEBUGGER + + PUSH(pc); + pc = 0x0066; + iff = (iff&~2) | ((iff&1)<<1); + iff &= ~1; + nmiTrigger = false; // clear NMI + // TODO: if in HALTed state, un-halt + } + else if (intLine) // INT asserted + { + // If interrupts are enabled (IFF1 != 0) + if ((iff&1)) + { + int v; + + /* + * INT sequence: + * + * - Disable interrupts (clear IFF1 and IFF2) + * - Push pc on stack + * - Un-halt CPU (if in HALT state) + * - Set pc to vector (which depends on mode) + * + * If no callback is provided when required, nothing happens and + * the interrupt line is cleared. Otherwise, callbacks are + * responsible for clearing the lines themselves. + */ + // TODO: if in HALTed state, un-halt + switch (im) // interrupt mode (0, 1, or 2 only!) + { + case 0: + /* + * Mode 0: + * + * Fetches up 3 bytes from bus and executes them directly. + * Usually, this will just be an RST instruction, so this is + * all that we accept here. + */ + if (NULL != INTCallback) + { + v = INTCallback(this); + +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + Debug->CPUException(v); +#endif // SUPERMODEL_DEBUGGER + + switch (v) + { + case Z80_INT_RST_00: v = 0x0000; break; + case Z80_INT_RST_08: v = 0x0008; break; + case Z80_INT_RST_10: v = 0x0010; break; + case Z80_INT_RST_18: v = 0x0018; break; + case Z80_INT_RST_20: v = 0x0020; break; + case Z80_INT_RST_28: v = 0x0028; break; + case Z80_INT_RST_30: v = 0x0030; break; + case Z80_INT_RST_38: v = 0x0038; break; + default: v = -1; break; // invalid, do nothing + } + + if (v >= 0) // valid vector + { + PUSH(pc); + pc = (UINT16) v; + iff = 0; + } + } + else // if no callback, do nothing, clear INT line + intLine = false; + break; + case 1: + /* + * Mode 1: + * + * Vector is 0x0038. + */ + +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + Debug->CPUException(Z80_IM1_IRQ); +#endif // SUPERMODEL_DEBUGGER + + PUSH(pc); + pc = 0x0038; + iff = 0; + if (NULL != INTCallback) + INTCallback(this); + else // no callback, clear INT line automatically + intLine = false; + break; + case 2: + /* + * Mode 2: + * + * A 16-bit address is formed by concatenating the I register + * and 8 bits read from the bus (with bit 0 cleared). The final + * 16-bit vector is read from this address. + */ + +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + Debug->CPUException(Z80_IM2_VECTOR); +#endif // SUPERMODEL_DEBUGGER + + if (NULL != INTCallback) + { + v = INTCallback(this); + v = (ir&0xFF00) | (v&0xFE); + PUSH(pc); + pc = GetWORD(v); + iff = 0; + } + else // if no callback, do nothing, clear INT line + intLine = false; + break; + default: // should never happen (nothing will be done) + intLine = false; + break; + } + } + } + + + } // end while + + // write registers back to context +HALTExit: +#ifdef SUPERMODEL_DEBUGGER + if (Debug != NULL) + { + Debug->CPUInactive(); + lastCycles -= cycles; + } +#else + // Save local copies of Z80 registers back to context + af[af_sel] = AF; + regs[regs_sel].bc = BC; + regs[regs_sel].de = DE; + regs[regs_sel].hl = HL; + ix = IX; + iy = IY; + sp = SP; +#endif // SUPERMODEL_DEBUGGER + + // Return number of cycles actually executed + return numCycles - cycles; +} + +void CZ80::TriggerNMI(void) +{ + nmiTrigger = true; +} + +void CZ80::SetINT(bool state) +{ + intLine = state; +} + +UINT16 CZ80::GetPC(void) +{ + return pc; +} + +#ifdef SUPERMODEL_DEBUGGER +UINT8 CZ80::GetReg8(unsigned reg8) +{ + switch (reg8) + { + case Z80_REG8_IFF: return iff; + case Z80_REG8_IM: return im; + case Z80_REG8_I: return ir>>8; + case Z80_REG8_R: return ir&0xFF; + case Z80_REG8_A: return af[0]>>8; + case Z80_REG8_F: return af[0]&0xFF; + case Z80_REG8_B: return regs[0].bc>>8; + case Z80_REG8_C: return regs[0].bc&0xFF; + case Z80_REG8_D: return regs[0].de>>8; + case Z80_REG8_E: return regs[0].de&0xFF; + case Z80_REG8_H: return regs[0].hl>>8; + case Z80_REG8_L: return regs[0].hl&0xFF; + default: return 0; + } +} + +bool CZ80::SetReg8(unsigned reg8, UINT8 value) +{ + switch (reg8) + { + case Z80_REG8_IFF: iff = value; return true; + case Z80_REG8_IM: im = value; return true; + case Z80_REG8_I: ir |= value<<8; return true; + case Z80_REG8_R: ir |= value; return true; + case Z80_REG8_A: af[0] |= value<<8; return true; + case Z80_REG8_F: af[0] |= value; return true; + case Z80_REG8_B: regs[0].bc |= value<<8; return true; + case Z80_REG8_C: regs[0].bc |= value; return true; + case Z80_REG8_D: regs[0].de |= value<<8; return true; + case Z80_REG8_E: regs[0].de |= value; return true; + case Z80_REG8_H: regs[0].hl |= value<<8; return true; + case Z80_REG8_L: regs[0].hl |= value; return true; + default: return false; + } +} + +UINT16 CZ80::GetReg16(unsigned reg16) +{ + switch (reg16) + { + case Z80_REG16_SP: return sp; + case Z80_REG16_PC: return pc; + case Z80_REG16_IR: return ir; + case Z80_REG16_AF: return af[0]; + case Z80_REG16_BC: return regs[0].bc; + case Z80_REG16_DE: return regs[0].de; + case Z80_REG16_HL: return regs[0].hl; + case Z80_REG16_IX: return ix; + case Z80_REG16_IY: return iy; + case Z80_REG16_AF_: return af[1]; + case Z80_REG16_BC_: return regs[1].bc; + case Z80_REG16_DE_: return regs[1].de; + case Z80_REG16_HL_: return regs[1].hl; + default: return 0; + } +} + +bool CZ80::SetReg16(unsigned reg16, UINT16 value) +{ + switch (reg16) + { + case Z80_REG16_SP: sp = value; return true; + case Z80_REG16_PC: pc = value; return true; + case Z80_REG16_IR: ir = value; return true; + case Z80_REG16_AF: af[0] = value; return true; + case Z80_REG16_BC: regs[0].bc = value; return true; + case Z80_REG16_DE: regs[0].de = value; return true; + case Z80_REG16_HL: regs[0].hl = value; return true; + case Z80_REG16_IX: ix = value; return true; + case Z80_REG16_IY: iy = value; return true; + case Z80_REG16_AF_: af[1] = value; return true; + case Z80_REG16_BC_: regs[1].bc = value; return true; + case Z80_REG16_DE_: regs[1].de = value; return true; + case Z80_REG16_HL_: regs[1].hl = value; return true; + default: return false; + } +} +#endif // SUPERMODEL_DEBUGGER + +void CZ80::Reset(void) +{ + pc = 0x0000; + sp = 0xF000; + af[0] = 0x0000; + af[1] = 0x0000; + regs[0].bc = 0x0000; + regs[0].de = 0x0000; + regs[0].hl = 0x0000; + regs[1].bc = 0x0000; + regs[1].de = 0x0000; + regs[1].hl = 0x0000; + ix = 0x0000; + iy = 0x0000; + ir = 0x0000; + iff = 0; + im = 0; + + af_sel = 0; + regs_sel = 0; + + intLine = false; + nmiTrigger = false; +#ifdef SUPERMODEL_DEBUGGER + lastCycles = 0; +#endif // SUPERMODEL_DEBUGGER +} + +void CZ80::SaveState(CBlockFile *StateFile, const char *name) +{ + StateFile->NewBlock(name, __FILE__); + + for (int i = 0; i < 2; i++) + { + StateFile->Write(®s[i].bc, sizeof(regs[i].bc)); + StateFile->Write(®s[i].de, sizeof(regs[i].de)); + StateFile->Write(®s[i].hl, sizeof(regs[i].hl)); + } + + StateFile->Write(af, sizeof(af)); + StateFile->Write(&ir, sizeof(ir)); + StateFile->Write(&ix, sizeof(ix)); + StateFile->Write(&iy, sizeof(iy)); + StateFile->Write(&sp, sizeof(sp)); + StateFile->Write(&pc, sizeof(pc)); + StateFile->Write(&iff, sizeof(iff)); + StateFile->Write(&im, sizeof(im)); + StateFile->Write(®s_sel, sizeof(regs_sel)); + StateFile->Write(&af_sel, sizeof(af_sel)); + StateFile->Write(&nmiTrigger, sizeof(nmiTrigger)); + StateFile->Write(&intLine, sizeof(intLine)); +} + +void CZ80::LoadState(CBlockFile *StateFile, const char *name) +{ + if (OKAY != StateFile->FindBlock(name)) + { + ErrorLog("Unable to load Z80 state. Save state file is corrupt."); + return; + } + + for (int i = 0; i < 2; i++) + { + StateFile->Read(®s[i].bc, sizeof(regs[i].bc)); + StateFile->Read(®s[i].de, sizeof(regs[i].de)); + StateFile->Read(®s[i].hl, sizeof(regs[i].hl)); + } + + StateFile->Read(af, sizeof(af)); + StateFile->Read(&ir, sizeof(ir)); + StateFile->Read(&ix, sizeof(ix)); + StateFile->Read(&iy, sizeof(iy)); + StateFile->Read(&sp, sizeof(sp)); + StateFile->Read(&pc, sizeof(pc)); + StateFile->Read(&iff, sizeof(iff)); + StateFile->Read(&im, sizeof(im)); + StateFile->Read(®s_sel, sizeof(regs_sel)); + StateFile->Read(&af_sel, sizeof(af_sel)); + StateFile->Read(&nmiTrigger, sizeof(nmiTrigger)); + StateFile->Read(&intLine, sizeof(intLine)); +} + +void CZ80::Init(IBus *BusPtr, int (*INTF)(CZ80 *Z80)) +{ + Bus = BusPtr; + INTCallback = INTF; +} + +#ifdef SUPERMODEL_DEBUGGER +void CZ80::AttachDebugger(Debugger::CZ80Debug *DebugPtr) +{ + if (Debug != NULL) + DetachDebugger(); + Debug = DebugPtr; + Bus = Debug->AttachBus(Bus); +} + +void CZ80::DetachDebugger() +{ + if (Debug == NULL) + return; + Bus = Debug->DetachBus(); + Debug = NULL; +} +#endif //SUPERMODEL_DEBUGGER + +CZ80::CZ80(void) +{ + INTCallback = NULL; // so we can later check to see if one has been installed + Bus = NULL; +#ifdef SUPERMODEL_DEBUGGER + Debug = NULL; +#endif //SUPERMODEL_DEBUGGER +} + +CZ80::~CZ80(void) +{ + INTCallback = NULL; + Bus = NULL; +#ifdef SUPERMODEL_DEBUGGER + Debug = NULL; +#endif //SUPERMODEL_DEBUGGER +} \ No newline at end of file diff --git a/Src/CPU/Z80/Z80.h b/Src/CPU/Z80/Z80.h new file mode 100644 index 0000000..214b378 --- /dev/null +++ b/Src/CPU/Z80/Z80.h @@ -0,0 +1,339 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Z80.h + * + * Z80 emulator header file. Based on the Z80 instruction set simulator by + * Frank D. Cringle and taken from YAZE-AG by Andreas Gerlich. + * + * Known inaccuracies: + * + * - Interrupts are accepted immediately after EI instruction. In reality, + * interrupts become enabled after the instruction following EI. This + * could be implemented with a state machine in Run() if needed. + * This feature exists to prevent interrupts from occuring before a RETI + * instruction executes in the interrupt handler. Interrupt callbacks + * should take care to immediately clear the interrupt line, otherwise, + * an improperly timed interrupt may occur right after an EI instruction + * but before the service routine has a chance to return. + * - HALT instruction is not implemented (it just exits). + * - 16-bit words are read as two bytes but these reads may not occur in + * the exact same order as the real device. Needs to be checked. + */ + +#ifndef INCLUDED_Z80_H +#define INCLUDED_Z80_H + +#include "Types.h" +#include "CPU/Bus.h" +#include "BlockFile.h" + +/* + * Special Return Codes for Interrupt Callbacks + * + * When the Z80 takes an interrupt in mode 0, it expects an instruction (up to + * 3 bytes) to be presented on the bus. Typically, an RST instruction is + * supplied, and this is the only supported behavior here. The interrupt + * callback must return one of these values when the Z80 is in mode 0. + */ +#define Z80_INT_RST_00 0xC7 // RST 0x00 (jumps to 0x0000) +#define Z80_INT_RST_08 0xCF // RST 0x08 (jumps to 0x0008) +#define Z80_INT_RST_10 0xD7 // RST 0x10 (...) +#define Z80_INT_RST_18 0xDF // RST 0x18 +#define Z80_INT_RST_20 0xE7 // RST 0x20 +#define Z80_INT_RST_28 0xEF // RST 0x28 +#define Z80_INT_RST_30 0xF7 // RST 0x30 +#define Z80_INT_RST_38 0xFF // RST 0x38 + +#ifdef SUPERMODEL_DEBUGGER +// Extra interrupt types +#define Z80_EX_NMI 0 +#define Z80_IM1_IRQ 1 +#define Z80_IM2_VECTOR 2 + +// 8-bit registers +#define Z80_REG8_IFF 0 +#define Z80_REG8_IM 1 +#define Z80_REG8_I 2 +#define Z80_REG8_R 3 +#define Z80_REG8_A 4 +#define Z80_REG8_F 5 +#define Z80_REG8_B 6 +#define Z80_REG8_C 7 +#define Z80_REG8_D 8 +#define Z80_REG8_E 9 +#define Z80_REG8_H 10 +#define Z80_REG8_L 11 + +// 16-bit registers +#define Z80_REG16_SP 0 +#define Z80_REG16_PC 1 +#define Z80_REG16_IR 2 +#define Z80_REG16_AF 3 +#define Z80_REG16_BC 4 +#define Z80_REG16_DE 5 +#define Z80_REG16_HL 6 +#define Z80_REG16_IX 7 +#define Z80_REG16_IY 8 +#define Z80_REG16_AF_ 9 +#define Z80_REG16_BC_ 10 +#define Z80_REG16_DE_ 11 +#define Z80_REG16_HL_ 12 + +namespace Debugger { + class CZ80Debug; +} +#endif // SUPERMODEL_DEBUGGER + +/* + * CZ80: + * + * A Z80 CPU. + */ +class CZ80 +{ +public: + /* + * Run(numCycles): + * + * Runs the Z80 for the specified number of instruction cycles. + * + * Parameters: + * numCycles Number of instruction cycles to execute. + * + * Returns: + * Number of instruction cycles actually executed. + */ + int Run(int numCycles); + + /* + * TriggerNMI(void): + * + * Triggers a non-maskable interrupt. This is equivalent to a high-to-low + * transition on the NMI pin (NMI pin is triggered on falling edges). This + * may be called while the Z80 is running. NMIs are always higher priority + * than interrupts and are taken first. + */ + void TriggerNMI(void); + + /* + * SetINT(state): + * + * Set or clear the interrupt request. This may be called from memory + * handlers while the Z80 is running and should be used by interrupt + * callbacks to clear interrupts. + * + * Parameters: + * state If TRUE, this is equivalent to /INT being asserted on the + * Z80 (INT line low, which triggers an interrupt). If FALSE, + * this deasserts /INT (INT line high, no interrupt pending). + */ + void SetINT(bool state); + + /* + * GetPC(void): + * + * Returns the current PC value. + * + * Returns: + * Current value of PC register. + */ + UINT16 GetPC(void); + +#ifdef SUPERMODEL_DEBUGGER + /* + * GetReg8(reg8): + * + * Reads an 8-bit register. + * + * Parameters: + * reg8 Register number (use Z80_REG8_* macros). + * + * Returns: + * Value of given 8-bit register. + */ + UINT8 GetReg8(unsigned reg8); + + /* + * SetReg8(reg8, value): + * + * Sets value of given 8-bit register. + * + * Parameters: + * reg8 Register number. + * value Value to write. + * + * Returns: + * True if succeeded, false if invalid register. + */ + bool SetReg8(unsigned reg8, UINT8 value); + + /* + * GetReg16(reg16, value): + * + * Reads a 16-bit register. + * + * Parameters: + * reg16 Register number (use Z80_REG16_* macros). + * + * Returns: + * Value of given 16-bit register. + */ + UINT16 GetReg16(unsigned reg16); + + /* + * SetReg16(state): + * + * Sets value of given 16-bit register. + * + * Parameters: + * reg16 Register. + * value Value to write. + * + * Returns: + * True if succeeded, false if invalid register. + */ + bool SetReg16(unsigned reg16, UINT16 value); +#endif // SUPERMODEL_DEBUGGER + + /* + * Reset(void): + * + * Resets the Z80, clearing all registers and pending interrupt requests. + */ + void Reset(void); + + /* + * SaveState(StateFile, name): + * + * Saves the CPU state to the block file. + * + * Parameters: + * StateFile Block file. + * name Name of block to create (e.g. "Main Z80"), to facilitate + * multiple Z80 states in the same file. + */ + void SaveState(CBlockFile *StateFile, const char *name); + + /* + * LoadState(StateFile, name): + * + * Loads the CPU state. + * + * Parameters: + * StateFile Block file. + * name Name of block to load. + */ + void LoadState(CBlockFile *StateFile, const char *name); + + /* + * Init(BusPtr, INTF): + * + * One-time initialization of the Z80 emulator. Must be called first. + * + * A bus object must be supplied which will be used to handle all memory + * and IO accesses. The Read8, Write8, IORead8, and IOWrite8 members need + * to be defined. Addresses are automatically clamped to 16 bits for memory + * and 8 bits for IO accesses. + * + * An interrupt callback, which is called each time an interrupt occurs, + * should also be supplied. The interrupt callback should explicitly clear + * the INT status (using SetINT(FALSE)) and then return the appropriate + * vector depending on the interrupt mode that is used by the system. + * + * For mode 0, only Z80_INT_RST_* values are acceptable. For mode 1, the + * return value is discarded because the Z80 will always use vector 0x0038. + * In mode 2, an 8-bit value supplying the lower 8-bits of the interrupt + * vector address must be returned. Bit 0 will be ignored in this case (0 + * is used). + * + * If a callback is not installed, the interrupt status will automatically + * be cleared and interrupts will not be taken when the Z80 is in a mode + * that requires vector data from the callback. In mode 1, the interrupt + * callback (if one is provided) should explicitly clear the interrupt. The + * value returned will be unused. + * + * Parameters: + * BusPtr Pointer to a bus object. + * INTF Pointer to callback function. The function accepts a + * a pointer to the Z80 object that received the interrupt. + */ + void Init(IBus *BusPtr, int (*INTF)(CZ80 *Z80)); + +#ifdef SUPERMODEL_DEBUGGER + /* + * AttachDebugger(DebugPtr): + * + * Attaches debugger to CPU. + */ + void AttachDebugger(Debugger::CZ80Debug *DebugPtr); + + /* + * DetachDebugger(): + * + * Detaches debugger from CPU. + */ + void DetachDebugger(); +#endif // SUPERMODEL_DEBUGGER + + /* + * CZ80(void): + * ~CZ80(void): + * + * Constructor and destructor. + */ + CZ80(void); + ~CZ80(void); + +private: + // Registers + struct GPR { // general purpose registers + UINT16 bc; + UINT16 de; + UINT16 hl; + } regs[2]; // two sets: primary (0) and alternate (1) + UINT16 af[2]; // AF (primary and alternate) + UINT16 ir; // interrupt vector (I) and memory refresh (R) + UINT16 ix; // index register X + UINT16 iy; // index register Y + UINT16 sp; // stack pointer + UINT16 pc; // program counter + UINT8 iff; // interrupt flip flops (bit 1: IFF2, 0: IFF1) + UINT8 im; // interrupt mode (0, 1, or 2 only) + int regs_sel; // active register set (primary or alternate) + int af_sel; // active AF + + // Memory and IO bus + IBus *Bus; + + // Interrupts + bool nmiTrigger; + bool intLine; + int (*INTCallback)(CZ80 *Z80); + +#ifdef SUPERMODEL_DEBUGGER + int lastCycles; + Debugger::CZ80Debug *Debug; +#endif // SUPERMODEL_DEBUGGER +}; + +#endif // INCLUDED_Z80_H diff --git a/Src/Debugger/AddressTable.cpp b/Src/Debugger/AddressTable.cpp new file mode 100644 index 0000000..2ff424f --- /dev/null +++ b/Src/Debugger/AddressTable.cpp @@ -0,0 +1,148 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * AddressTable.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "AddressTable.h" + +#include +#include + +namespace Debugger +{ + CAddressRef::CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize) : + cpu(refCPU), addr(refAddr), size(refSize), addrEnd(refAddr + refSize - 1) + { + // + } + + CAddressTable::CAddressTable() : m_count(0) + { + memset(m_tables, NULL, sizeof(m_tables)); + } + + CAddressTable::~CAddressTable() + { + Clear(); + } + + CAddressRef **CAddressTable::GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable) + { + // Convert address to table index + tableIndex = addr >> INDEX_SHIFT; + // See if have a table at index + if (m_tables[tableIndex] == NULL) + { + // If not, create one now and increase count + m_tables[tableIndex] = new CAddressRef*[TABLE_SIZE]; + memset(m_tables[tableIndex], NULL, sizeof(CAddressRef*) * TABLE_SIZE); + m_count++; + } + // Calculate index within table + indexInTable = addr & TABLE_MASK; + return m_tables[tableIndex]; + } + + void CAddressTable::CheckAndReleaseTable(int tableIndex) + { + // See if have table at give index + CAddressRef **table = m_tables[tableIndex]; + if (table == NULL) + return; + // If so, check if it is empty + for (int i = 0; i < TABLE_SIZE; i++) + { + if (table[i] != NULL) + return; + } + // If so, delete it and decrease count + delete[] table; + m_tables[tableIndex] = NULL; + m_count--; + } + + bool CAddressTable::IsEmpty() + { + return m_count == 0; + } + + void CAddressTable::Clear() + { + if (m_count == 0) + return; + // Delete all tables and reset count + for (int i = 0; i < NUM_TABLES; i++) + { + if (m_tables[i] != NULL) + { + delete[] m_tables[i]; + m_tables[i] = NULL; + } + } + m_count = 0; + } + + void CAddressTable::Add(CAddressRef *value) + { + int tableIndex; + unsigned indexInTable; + CAddressRef **table = GetTableCreate(value->addr, tableIndex, indexInTable); + for (UINT32 i = 0; i < value->size; i++) + { + table[indexInTable] = value; + if (indexInTable >= TABLE_SIZE - 1) + table = GetTableCreate(value->addr + i, tableIndex, indexInTable); + else + indexInTable++; + } + } + + bool CAddressTable::Remove(CAddressRef *value) + { + int tableIndex; + unsigned indexInTable; + CAddressRef **table = GetTableNoCreate(value->addr, tableIndex, indexInTable); + bool removed = false; + for (UINT32 i = 0; i < value->size; i++) + { + if (table != NULL) + { + removed |= table[indexInTable] != NULL; + table[indexInTable] = NULL; + } + if (indexInTable >= TABLE_SIZE - 1) + { + CheckAndReleaseTable(tableIndex); + table = GetTableNoCreate(value->addr + i, tableIndex, indexInTable); + } + else + indexInTable++; + } + CheckAndReleaseTable(tableIndex); + return removed; + } +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/AddressTable.h b/Src/Debugger/AddressTable.h new file mode 100644 index 0000000..7df55be --- /dev/null +++ b/Src/Debugger/AddressTable.h @@ -0,0 +1,155 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * AddressTable.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_ADDRESSTABLE_H +#define INCLUDED_ADDRESSTABLE_H + +#include "Types.h" + +#include +#include + +#define TABLE_WIDTH 16 +#define TABLE_SIZE (1 << TABLE_WIDTH) +#define TABLE_MASK (TABLE_SIZE - 1) +#define INDEX_SHIFT TABLE_WIDTH +#define NUM_TABLES (0x100000000ULL / TABLE_SIZE) + +namespace Debugger +{ + class CCPUDebug; + + /* + * Base class for objects that reference an address, such as a label or a comment. + */ + class CAddressRef + { + protected: + CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize = 1); + + public: + CCPUDebug *cpu; + const UINT32 addr; + const UINT32 size; + const UINT32 addrEnd; + + bool CheckAddr(UINT32 loc); + + // TODO - implement operators + }; + + /* + * Class that holds a table of address references. It does so in an a memory-expensive manner but with the result that it + * performs better (hopefully!) as lookups are constant-time. + */ + class CAddressTable + { + private: + int m_count; + CAddressRef **m_tables[NUM_TABLES]; + + CAddressRef **GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable); + + CAddressRef **GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable); + + void CheckAndReleaseTable(int tableIndex); + + public: + CAddressTable(); + + ~CAddressTable(); + + bool IsEmpty(); + + void Clear(); + + CAddressRef *Get(UINT32 addr); + + CAddressRef *Get(UINT32 addr, UINT32 size); + + void Add(CAddressRef *value); + + bool Remove(CAddressRef *value); + }; + + // + // Inlined methods + // + + inline bool CAddressRef::CheckAddr(UINT32 loc) + { + return addr <= loc && loc <= addrEnd; + } + + inline CAddressRef **CAddressTable::GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable) + { + tableIndex = addr >> INDEX_SHIFT; + if (m_tables[tableIndex] == NULL) + return NULL; + indexInTable = addr & TABLE_MASK; + return m_tables[tableIndex]; + } + + inline CAddressRef *CAddressTable::Get(UINT32 addr) + { + int tableIndex; + unsigned indexInTable; + CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable); + if (table == NULL) + return NULL; + return table[indexInTable]; + } + + inline CAddressRef *CAddressTable::Get(UINT32 addr, UINT32 size) + { + int tableIndex; + unsigned indexInTable; + CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable); + for (UINT32 i = 0; i < size; i++) + { + if (table != NULL) + { + CAddressRef *ref = table[indexInTable]; + if (ref != NULL) + return ref; + addr++; + if (indexInTable >= TABLE_SIZE - 1) + table = GetTableNoCreate(addr, tableIndex, indexInTable); + else + indexInTable++; + } + else + { + addr++; + table = GetTableNoCreate(addr, tableIndex, indexInTable); + } + } + return NULL; + } +} + +#endif // INCLUDED_ADDRESSTABLE_H +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/Breakpoint.cpp b/Src/Debugger/Breakpoint.cpp new file mode 100644 index 0000000..424ed0c --- /dev/null +++ b/Src/Debugger/Breakpoint.cpp @@ -0,0 +1,112 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Breakpoint.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "CPUDebug.h" +#include "Breakpoint.h" + +#include + +namespace Debugger +{ + CBreakpoint::CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType) : + CAddressRef(bpCPU, bpAddr), symbol(bpSymbol), type(bpType), active(true) + { + // + } + + bool CBreakpoint::Check(UINT32 pc, UINT32 opcode) + { + return CheckAddr(pc) && active && CheckBreak(pc, opcode); + } + + void CBreakpoint::Reset() + { + // + } + + CSimpleBreakpoint::CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, '*', "simple") + { + // + } + + bool CSimpleBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode) + { + return true; + } + + bool CSimpleBreakpoint::GetInfo(char *str) + { + return false; + } + + CCountBreakpoint::CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count) : CBreakpoint(bpCPU, bpAddr, '+', "count"), + m_count(count), m_counter(0) + { + // + } + + bool CCountBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode) + { + return ++m_counter == m_count; + } + + void CCountBreakpoint::Reset() + { + m_counter = 0; + } + + bool CCountBreakpoint::GetInfo(char *str) + { + sprintf(str, "%d / %d", m_counter, m_count); + return true; + } + + CPrintBreakpoint::CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, 'P', "print") + { + // + } + + bool CPrintBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode) + { + char addrStr[50]; + cpu->FormatAddress(addrStr, addr, true); + cpu->debugger->PrintEvent(cpu, "Breakpoint #%u hit at %s.\n", num, addrStr); + return false; + } + + void CPrintBreakpoint::Reset() + { + // + } + + bool CPrintBreakpoint::GetInfo(char *str) + { + return false; + } +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/Breakpoint.h b/Src/Debugger/Breakpoint.h new file mode 100644 index 0000000..44c2be8 --- /dev/null +++ b/Src/Debugger/Breakpoint.h @@ -0,0 +1,113 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Breakpoint.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_BREAKPOINT_H +#define INCLUDED_BREAKPOINT_H + +#include "AddressTable.h" +#include "Types.h" + +namespace Debugger +{ + class CCPUDebug; + + /* + * Base class for a breakpoint. + */ + class CBreakpoint : public CAddressRef + { + protected: + CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType); + + public: + const char symbol; + const char *type; + + unsigned num; + + bool active; + + bool Check(UINT32 pc, UINT32 opcode); + + virtual bool CheckBreak(UINT32 pc, UINT32 opcode) = 0; + + virtual void Reset(); + + virtual bool GetInfo(char *str) = 0; + }; + + /* + * Simple breakpoint that will always halt execution when hit. + */ + class CSimpleBreakpoint : public CBreakpoint + { + public: + CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr); + + bool CheckBreak(UINT32 pc, UINT32 opcode); + + bool GetInfo(char *str); + }; + + /* + * Count breakpoint that will halt execution after it has been hit a certain number of times. + */ + class CCountBreakpoint : public CBreakpoint + { + private: + int m_count; + int m_counter; + + public: + CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count); + + bool CheckBreak(UINT32 pc, UINT32 opcode); + + void Reset(); + + bool GetInfo(char *str); + }; + + class CPrintBreakpoint : public CBreakpoint + { + public: + CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr); + + bool CheckBreak(UINT32 pc, UINT32 opcode); + + void Reset(); + + bool GetInfo(char *str); + }; + + //class CConditionBreakpoint : public CBrekapoint + //{ + // // TODO + //} +} + +#endif // INCLUDED_BREAKPOINT_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/68KDebug.cpp b/Src/Debugger/CPU/68KDebug.cpp new file mode 100644 index 0000000..b348812 --- /dev/null +++ b/Src/Debugger/CPU/68KDebug.cpp @@ -0,0 +1,2225 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * 68KDebug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "68KDebug.h" + +#include +#include + +#define M68KSPECIAL_SP 0 +#define M68KSPECIAL_SR 1 + +namespace Debugger +{ + C68KDebug::C68KDebug(const char *name) : CCPUDebug("68K", name, 2, 10, true, 24, 7) + { + // Exceptions + AddException("BUS", 2, "Bus Error"); + AddException("ADDRESS", 3, "Address Error"); + AddException("ILLEGAL", 4, "Illegal Instruction"); + AddException("DIVZERO", 5, "Divide by Zero"); + AddException("CHK", 6, "CHK Instruction"); + AddException("TRAPV", 7, "TRAPV Instruction"); + AddException("PRIVEX", 8, "Privilege Exception"); + AddException("TRACE", 9, "Trace"); + AddException("L1010", 10, "Line 1010 Emulator"); + AddException("L1111", 11, "Line 1111 Emulator"); + AddException("SPUR", 24, "Spurious Interrupt"); + AddException("AUTO", 25, "Interrupt Autovector"); + AddException("TRAP0", 32, "TRAP #0 Instruction Vector"); + AddException("TRAP1", 33, "TRAP #1 Instruction Vector"); + AddException("TRAP2", 34, "TRAP #2 Instruction Vector"); + AddException("TRAP3", 35, "TRAP #3 Instruction Vector"); + AddException("TRAP4", 36, "TRAP #4 Instruction Vector"); + AddException("TRAP5", 37, "TRAP #5 Instruction Vector"); + AddException("TRAP6", 38, "TRAP #6 Instruction Vector"); + AddException("TRAP7", 39, "TRAP #7 Instruction Vector"); + AddException("TRAP8", 40, "TRAP #8 Instruction Vector"); + AddException("TRAP9", 41, "TRAP #9 Instruction Vector"); + AddException("TRAP10", 42, "TRAP #10 Instruction Vector"); + AddException("TRAP11", 43, "TRAP #11 Instruction Vector"); + AddException("TRAP12", 44, "TRAP #12 Instruction Vector"); + AddException("TRAP13", 45, "TRAP #13 Instruction Vector"); + AddException("TRAP14", 46, "TRAP #14 Instruction Vector"); + AddException("TRAP15", 47, "TRAP #15 Instruction Vector"); + + // Interrupts + AddInterrupt("AUTO1", 0, "Level 1 Interrupt Autovector"); + AddInterrupt("AUTO2", 1, "Level 2 Interrupt Autovector"); + AddInterrupt("AUTO3", 2, "Level 3 Interrupt Autovector"); + AddInterrupt("AUTO4", 3, "Level 4 Interrupt Autovector"); + AddInterrupt("AUTO5", 4, "Level 5 Interrupt Autovector"); + AddInterrupt("AUTO6", 5, "Level 6 Interrupt Autovector"); + AddInterrupt("AUTO7", 6, "Level 7 Interrupt Autovector"); + } + + static const char *opATable0004[] = { "movep.w [DW3](A0),D0","movep.w [DW3](A1),D0","movep.w [DW3](A2),D0", + "movep.w [DW3](A3),D0","movep.w [DW3](A4),D0","movep.w [DW3](A5),D0","movep.w [DW3](A6),D0", + "movep.w [DW3](A7),D0" }; + static const char *opATable0005[] = { "movep.l [DW3](A0),D0","movep.l [DW3](A1),D0","movep.l [DW3](A2),D0", + "movep.l [DW3](A3),D0","movep.l [DW3](A4),D0","movep.l [DW3](A5),D0","movep.l [DW3](A6),D0", + "movep.l [DW3](A7),D0" }; + static const char *opATable0006[] = { "movep.w D0,[DW3](A0)","movep.w D0,[DW3](A1)","movep.w D0,[DW3](A2)", + "movep.w D0,[DW3](A3)","movep.w D0,[DW3](A4)","movep.w D0,[DW3](A5)","movep.w D0,[DW3](A6)", + "movep.w D0,[DW3](A7)" }; + static const char *opATable0007[] = { "movep.l D0,[DW3](A0)","movep.l D0,[DW3](A1)","movep.l D0,[DW3](A2)", + "movep.l D0,[DW3](A3)","movep.l D0,[DW3](A4)","movep.l D0,[DW3](A5)","movep.l D0,[DW3](A6)", + "movep.l D0,[DW3](A7)" }; + static const char *opATable000C[] = { "movep.w [DW3](A0),D1","movep.w [DW3](A1),D1","movep.w [DW3](A2),D1", + "movep.w [DW3](A3),D1","movep.w [DW3](A4),D1","movep.w [DW3](A5),D1","movep.w [DW3](A6),D1", + "movep.w [DW3](A7),D1" }; + static const char *opATable000D[] = { "movep.l [DW3](A0),D1","movep.l [DW3](A1),D1","movep.l [DW3](A2),D1", + "movep.l [DW3](A3),D1","movep.l [DW3](A4),D1","movep.l [DW3](A5),D1","movep.l [DW3](A6),D1", + "movep.l [DW3](A7),D1" }; + static const char *opATable000E[] = { "movep.w D1,[DW3](A0)","movep.w D1,[DW3](A1)","movep.w D1,[DW3](A2)", + "movep.w D1,[DW3](A3)","movep.w D1,[DW3](A4)","movep.w D1,[DW3](A5)","movep.w D1,[DW3](A6)", + "movep.w D1,[DW3](A7)" }; + static const char *opATable000F[] = { "movep.l D1,[DW3](A0)","movep.l D1,[DW3](A1)","movep.l D1,[DW3](A2)", + "movep.l D1,[DW3](A3)","movep.l D1,[DW3](A4)","movep.l D1,[DW3](A5)","movep.l D1,[DW3](A6)", + "movep.l D1,[DW3](A7)" }; + static const char *opATable0014[] = { "movep.w [DW3](A0),D2","movep.w [DW3](A1),D2","movep.w [DW3](A2),D2", + "movep.w [DW3](A3),D2","movep.w [DW3](A4),D2","movep.w [DW3](A5),D2","movep.w [DW3](A6),D2", + "movep.w [DW3](A7),D2" }; + static const char *opATable0015[] = { "movep.l [DW3](A0),D2","movep.l [DW3](A1),D2","movep.l [DW3](A2),D2", + "movep.l [DW3](A3),D2","movep.l [DW3](A4),D2","movep.l [DW3](A5),D2","movep.l [DW3](A6),D2", + "movep.l [DW3](A7),D2" }; + static const char *opATable0016[] = { "movep.w D2,[DW3](A0)","movep.w D2,[DW3](A1)","movep.w D2,[DW3](A2)", + "movep.w D2,[DW3](A3)","movep.w D2,[DW3](A4)","movep.w D2,[DW3](A5)","movep.w D2,[DW3](A6)", + "movep.w D2,[DW3](A7)" }; + static const char *opATable0017[] = { "movep.l D2,[DW3](A0)","movep.l D2,[DW3](A1)","movep.l D2,[DW3](A2)", + "movep.l D2,[DW3](A3)","movep.l D2,[DW3](A4)","movep.l D2,[DW3](A5)","movep.l D2,[DW3](A6)", + "movep.l D2,[DW3](A7)" }; + static const char *opATable001C[] = { "movep.w [DW3](A0),D3","movep.w [DW3](A1),D3","movep.w [DW3](A2),D3", + "movep.w [DW3](A3),D3","movep.w [DW3](A4),D3","movep.w [DW3](A5),D3","movep.w [DW3](A6),D3", + "movep.w [DW3](A7),D3" }; + static const char *opATable001D[] = { "movep.l [DW3](A0),D3","movep.l [DW3](A1),D3","movep.l [DW3](A2),D3", + "movep.l [DW3](A3),D3","movep.l [DW3](A4),D3","movep.l [DW3](A5),D3","movep.l [DW3](A6),D3", + "movep.l [DW3](A7),D3" }; + static const char *opATable001E[] = { "movep.w D3,[DW3](A0)","movep.w D3,[DW3](A1)","movep.w D3,[DW3](A2)", + "movep.w D3,[DW3](A3)","movep.w D3,[DW3](A4)","movep.w D3,[DW3](A5)","movep.w D3,[DW3](A6)", + "movep.w D3,[DW3](A7)" }; + static const char *opATable001F[] = { "movep.l D3,[DW3](A0)","movep.l D3,[DW3](A1)","movep.l D3,[DW3](A2)", + "movep.l D3,[DW3](A3)","movep.l D3,[DW3](A4)","movep.l D3,[DW3](A5)","movep.l D3,[DW3](A6)", + "movep.l D3,[DW3](A7)" }; + static const char *opATable0024[] = { "movep.w [DW3](A0),D4","movep.w [DW3](A1),D4","movep.w [DW3](A2),D4", + "movep.w [DW3](A3),D4","movep.w [DW3](A4),D4","movep.w [DW3](A5),D4","movep.w [DW3](A6),D4", + "movep.w [DW3](A7),D4" }; + static const char *opATable0025[] = { "movep.l [DW3](A0),D4","movep.l [DW3](A1),D4","movep.l [DW3](A2),D4", + "movep.l [DW3](A3),D4","movep.l [DW3](A4),D4","movep.l [DW3](A5),D4","movep.l [DW3](A6),D4", + "movep.l [DW3](A7),D4" }; + static const char *opATable0026[] = { "movep.w D4,[DW3](A0)","movep.w D4,[DW3](A1)","movep.w D4,[DW3](A2)", + "movep.w D4,[DW3](A3)","movep.w D4,[DW3](A4)","movep.w D4,[DW3](A5)","movep.w D4,[DW3](A6)", + "movep.w D4,[DW3](A7)" }; + static const char *opATable0027[] = { "movep.l D4,[DW3](A0)","movep.l D4,[DW3](A1)","movep.l D4,[DW3](A2)", + "movep.l D4,[DW3](A3)","movep.l D4,[DW3](A4)","movep.l D4,[DW3](A5)","movep.l D4,[DW3](A6)", + "movep.l D4,[DW3](A7)" }; + static const char *opATable002C[] = { "movep.w [DW3](A0),D5","movep.w [DW3](A1),D5","movep.w [DW3](A2),D5", + "movep.w [DW3](A3),D5","movep.w [DW3](A4),D5","movep.w [DW3](A5),D5","movep.w [DW3](A6),D5", + "movep.w [DW3](A7),D5" }; + static const char *opATable002D[] = { "movep.l [DW3](A0),D5","movep.l [DW3](A1),D5","movep.l [DW3](A2),D5", + "movep.l [DW3](A3),D5","movep.l [DW3](A4),D5","movep.l [DW3](A5),D5","movep.l [DW3](A6),D5", + "movep.l [DW3](A7),D5" }; + static const char *opATable002E[] = { "movep.w D5,[DW3](A0)","movep.w D5,[DW3](A1)","movep.w D5,[DW3](A2)", + "movep.w D5,[DW3](A3)","movep.w D5,[DW3](A4)","movep.w D5,[DW3](A5)","movep.w D5,[DW3](A6)", + "movep.w D5,[DW3](A7)" }; + static const char *opATable002F[] = { "movep.l D5,[DW3](A0)","movep.l D5,[DW3](A1)","movep.l D5,[DW3](A2)", + "movep.l D5,[DW3](A3)","movep.l D5,[DW3](A4)","movep.l D5,[DW3](A5)","movep.l D5,[DW3](A6)", + "movep.l D5,[DW3](A7)" }; + static const char *opATable0034[] = { "movep.w [DW3](A0),D6","movep.w [DW3](A1),D6","movep.w [DW3](A2),D6", + "movep.w [DW3](A3),D6","movep.w [DW3](A4),D6","movep.w [DW3](A5),D6","movep.w [DW3](A6),D6", + "movep.w [DW3](A7),D6" }; + static const char *opATable0035[] = { "movep.l [DW3](A0),D6","movep.l [DW3](A1),D6","movep.l [DW3](A2),D6", + "movep.l [DW3](A3),D6","movep.l [DW3](A4),D6","movep.l [DW3](A5),D6","movep.l [DW3](A6),D6", + "movep.l [DW3](A7),D6" }; + static const char *opATable0036[] = { "movep.w D6,[DW3](A0)","movep.w D6,[DW3](A1)","movep.w D6,[DW3](A2)", + "movep.w D6,[DW3](A3)","movep.w D6,[DW3](A4)","movep.w D6,[DW3](A5)","movep.w D6,[DW3](A6)", + "movep.w D6,[DW3](A7)" }; + static const char *opATable0037[] = { "movep.l D6,[DW3](A0)","movep.l D6,[DW3](A1)","movep.l D6,[DW3](A2)", + "movep.l D6,[DW3](A3)","movep.l D6,[DW3](A4)","movep.l D6,[DW3](A5)","movep.l D6,[DW3](A6)", + "movep.l D6,[DW3](A7)" }; + static const char *opATable003C[] = { "movep.w [DW3](A0),D7","movep.w [DW3](A1),D7","movep.w [DW3](A2),D7", + "movep.w [DW3](A3),D7","movep.w [DW3](A4),D7","movep.w [DW3](A5),D7","movep.w [DW3](A6),D7", + "movep.w [DW3](A7),D7" }; + static const char *opATable003D[] = { "movep.l [DW3](A0),D7","movep.l [DW3](A1),D7","movep.l [DW3](A2),D7", + "movep.l [DW3](A3),D7","movep.l [DW3](A4),D7","movep.l [DW3](A5),D7","movep.l [DW3](A6),D7", + "movep.l [DW3](A7),D7" }; + static const char *opATable003E[] = { "movep.w D7,[DW3](A0)","movep.w D7,[DW3](A1)","movep.w D7,[DW3](A2)", + "movep.w D7,[DW3](A3)","movep.w D7,[DW3](A4)","movep.w D7,[DW3](A5)","movep.w D7,[DW3](A6)", + "movep.w D7,[DW3](A7)" }; + static const char *opATable003F[] = { "movep.l D7,[DW3](A0)","movep.l D7,[DW3](A1)","movep.l D7,[DW3](A2)", + "movep.l D7,[DW3](A3)","movep.l D7,[DW3](A4)","movep.l D7,[DW3](A5)","movep.l D7,[DW3](A6)", + "movep.l D7,[DW3](A7)" }; + static const char *opATable0121[] = { "swap D0","swap D1","swap D2","swap D3","swap D4","swap D5", + "swap D6","swap D7" }; + static const char *opATable0122[] = { "ext.w D0","ext.w D1","ext.w D2","ext.w D3","ext.w D4", + "ext.w D5","ext.w D6","ext.w D7" }; + static const char *opATable0123[] = { "ext.l D0","ext.l D1","ext.l D2","ext.l D3","ext.l D4", + "ext.l D5","ext.l D6","ext.l D7" }; + static const char *opATable0139[] = { "trap #0","trap #1","trap #2","trap #3","trap #4","trap #5", + "trap #6","trap #7","trap #8","trap #9","trap #10","trap #11","trap #12","trap #13", + "trap #14","trap #15","link A0,#[DW3]","link A1,#[DW3]","link A2,#[DW3]","link A3,#[DW3]", + "link A4,#[DW3]","link A5,#[DW3]","link A6,#[DW3]","link A7,#[DW3]","unlk A0","unlk A1", + "unlk A2","unlk A3","unlk A4","unlk A5","unlk A6","unlk A7","move A0,USP","move A1,USP", + "move A2,USP","move A3,USP","move A4,USP","move A5,USP","move A6,USP","move A7,USP", + "move USP,A0","move USP,A1","move USP,A2","move USP,A3","move USP,A4","move USP,A5", + "move USP,A6","move USP,A7","reset","nop","stop #[DW3]","rte",NULL,"rts","trapv","rtr" }; + static const char *opATable0143[] = { "dbt D0,[LL3]","dbt D1,[LL3]","dbt D2,[LL3]","dbt D3,[LL3]", + "dbt D4,[LL3]","dbt D5,[LL3]","dbt D6,[LL3]","dbt D7,[LL3]" }; + static const char *opATable0147[] = { "dbf D0,[LL3]","dbf D1,[LL3]","dbf D2,[LL3]","dbf D3,[LL3]", + "dbf D4,[LL3]","dbf D5,[LL3]","dbf D6,[LL3]","dbf D7,[LL3]" }; + static const char *opATable014B[] = { "dbhi D0,[LL3]","dbhi D1,[LL3]","dbhi D2,[LL3]","dbhi D3,[LL3]", + "dbhi D4,[LL3]","dbhi D5,[LL3]","dbhi D6,[LL3]","dbhi D7,[LL3]" }; + static const char *opATable014F[] = { "dbls D0,[LL3]","dbls D1,[LL3]","dbls D2,[LL3]","dbls D3,[LL3]", + "dbls D4,[LL3]","dbls D5,[LL3]","dbls D6,[LL3]","dbls D7,[LL3]" }; + static const char *opATable0153[] = { "dbcc D0,[LL3]","dbcc D1,[LL3]","dbcc D2,[LL3]","dbcc D3,[LL3]", + "dbcc D4,[LL3]","dbcc D5,[LL3]","dbcc D6,[LL3]","dbcc D7,[LL3]" }; + static const char *opATable0157[] = { "dbcs D0,[LL3]","dbcs D1,[LL3]","dbcs D2,[LL3]","dbcs D3,[LL3]", + "dbcs D4,[LL3]","dbcs D5,[LL3]","dbcs D6,[LL3]","dbcs D7,[LL3]" }; + static const char *opATable015B[] = { "dbne D0,[LL3]","dbne D1,[LL3]","dbne D2,[LL3]","dbne D3,[LL3]", + "dbne D4,[LL3]","dbne D5,[LL3]","dbne D6,[LL3]","dbne D7,[LL3]" }; + static const char *opATable015F[] = { "dbeq D0,[LL3]","dbeq D1,[LL3]","dbeq D2,[LL3]","dbeq D3,[LL3]", + "dbeq D4,[LL3]","dbeq D5,[LL3]","dbeq D6,[LL3]","dbeq D7,[LL3]" }; + static const char *opATable0163[] = { "dbvc D0,[LL3]","dbvc D1,[LL3]","dbvc D2,[LL3]","dbvc D3,[LL3]", + "dbvc D4,[LL3]","dbvc D5,[LL3]","dbvc D6,[LL3]","dbvc D7,[LL3]" }; + static const char *opATable0167[] = { "dbvs D0,[LL3]","dbvs D1,[LL3]","dbvs D2,[LL3]","dbvs D3,[LL3]", + "dbvs D4,[LL3]","dbvs D5,[LL3]","dbvs D6,[LL3]","dbvs D7,[LL3]" }; + static const char *opATable016B[] = { "dbpl D0,[LL3]","dbpl D1,[LL3]","dbpl D2,[LL3]","dbpl D3,[LL3]", + "dbpl D4,[LL3]","dbpl D5,[LL3]","dbpl D6,[LL3]","dbpl D7,[LL3]" }; + static const char *opATable016F[] = { "dbmi D0,[LL3]","dbmi D1,[LL3]","dbmi D2,[LL3]","dbmi D3,[LL3]", + "dbmi D4,[LL3]","dbmi D5,[LL3]","dbmi D6,[LL3]","dbmi D7,[LL3]" }; + static const char *opATable0173[] = { "dbge D0,[LL3]","dbge D1,[LL3]","dbge D2,[LL3]","dbge D3,[LL3]", + "dbge D4,[LL3]","dbge D5,[LL3]","dbge D6,[LL3]","dbge D7,[LL3]" }; + static const char *opATable0177[] = { "dblt D0,[LL3]","dblt D1,[LL3]","dblt D2,[LL3]","dblt D3,[LL3]", + "dblt D4,[LL3]","dblt D5,[LL3]","dblt D6,[LL3]","dblt D7,[LL3]" }; + static const char *opATable017B[] = { "dbgt D0,[LL3]","dbgt D1,[LL3]","dbgt D2,[LL3]","dbgt D3,[LL3]", + "dbgt D4,[LL3]","dbgt D5,[LL3]","dbgt D6,[LL3]","dbgt D7,[LL3]" }; + static const char *opATable017F[] = { "dble D0,[LL3]","dble D1,[LL3]","dble D2,[LL3]","dble D3,[LL3]", + "dble D4,[LL3]","dble D5,[LL3]","dble D6,[LL3]","dble D7,[LL3]" }; + static const char *opATable0204[] = { "sbcd D0,D0","sbcd D1,D0","sbcd D2,D0","sbcd D3,D0","sbcd D4,D0", + "sbcd D5,D0","sbcd D6,D0","sbcd D7,D0","sbcd -(A0),-(A0)","sbcd -(A1),-(A0)","sbcd -(A2),-(A0)", + "sbcd -(A3),-(A0)","sbcd -(A4),-(A0)","sbcd -(A5),-(A0)","sbcd -(A6),-(A0)","sbcd -(A7),-(A0)" }; + static const char *opATable020C[] = { "sbcd D0,D1","sbcd D1,D1","sbcd D2,D1","sbcd D3,D1","sbcd D4,D1", + "sbcd D5,D1","sbcd D6,D1","sbcd D7,D1","sbcd -(A0),-(A1)","sbcd -(A1),-(A1)","sbcd -(A2),-(A1)", + "sbcd -(A3),-(A1)","sbcd -(A4),-(A1)","sbcd -(A5),-(A1)","sbcd -(A6),-(A1)","sbcd -(A7),-(A1)" }; + static const char *opATable0214[] = { "sbcd D0,D2","sbcd D1,D2","sbcd D2,D2","sbcd D3,D2","sbcd D4,D2", + "sbcd D5,D2","sbcd D6,D2","sbcd D7,D2","sbcd -(A0),-(A2)","sbcd -(A1),-(A2)","sbcd -(A2),-(A2)", + "sbcd -(A3),-(A2)","sbcd -(A4),-(A2)","sbcd -(A5),-(A2)","sbcd -(A6),-(A2)","sbcd -(A7),-(A2)" }; + static const char *opATable021C[] = { "sbcd D0,D3","sbcd D1,D3","sbcd D2,D3","sbcd D3,D3","sbcd D4,D3", + "sbcd D5,D3","sbcd D6,D3","sbcd D7,D3","sbcd -(A0),-(A3)","sbcd -(A1),-(A3)","sbcd -(A2),-(A3)", + "sbcd -(A3),-(A3)","sbcd -(A4),-(A3)","sbcd -(A5),-(A3)","sbcd -(A6),-(A3)","sbcd -(A7),-(A3)" }; + static const char *opATable0224[] = { "sbcd D0,D4","sbcd D1,D4","sbcd D2,D4","sbcd D3,D4","sbcd D4,D4", + "sbcd D5,D4","sbcd D6,D4","sbcd D7,D4","sbcd -(A0),-(A4)","sbcd -(A1),-(A4)","sbcd -(A2),-(A4)", + "sbcd -(A3),-(A4)","sbcd -(A4),-(A4)","sbcd -(A5),-(A4)","sbcd -(A6),-(A4)","sbcd -(A7),-(A4)" }; + static const char *opATable022C[] = { "sbcd D0,D5","sbcd D1,D5","sbcd D2,D5","sbcd D3,D5","sbcd D4,D5", + "sbcd D5,D5","sbcd D6,D5","sbcd D7,D5","sbcd -(A0),-(A5)","sbcd -(A1),-(A5)","sbcd -(A2),-(A5)", + "sbcd -(A3),-(A5)","sbcd -(A4),-(A5)","sbcd -(A5),-(A5)","sbcd -(A6),-(A5)","sbcd -(A7),-(A5)" }; + static const char *opATable0234[] = { "sbcd D0,D6","sbcd D1,D6","sbcd D2,D6","sbcd D3,D6","sbcd D4,D6", + "sbcd D5,D6","sbcd D6,D6","sbcd D7,D6","sbcd -(A0),-(A6)","sbcd -(A1),-(A6)","sbcd -(A2),-(A6)", + "sbcd -(A3),-(A6)","sbcd -(A4),-(A6)","sbcd -(A5),-(A6)","sbcd -(A6),-(A6)","sbcd -(A7),-(A6)" }; + static const char *opATable023C[] = { "sbcd D0,D7","sbcd D1,D7","sbcd D2,D7","sbcd D3,D7","sbcd D4,D7", + "sbcd D5,D7","sbcd D6,D7","sbcd D7,D7","sbcd -(A0),-(A7)","sbcd -(A1),-(A7)","sbcd -(A2),-(A7)", + "sbcd -(A3),-(A7)","sbcd -(A4),-(A7)","sbcd -(A5),-(A7)","sbcd -(A6),-(A7)","sbcd -(A7),-(A7)" }; + static const char *opATable0244[] = { "subx.b D0,D0","subx.b D1,D0","subx.b D2,D0","subx.b D3,D0", + "subx.b D4,D0","subx.b D5,D0","subx.b D6,D0","subx.b D7,D0","subx.b -(A0),-(A0)","subx.b -(A1),-(A0)", + "subx.b -(A2),-(A0)","subx.b -(A3),-(A0)","subx.b -(A4),-(A0)","subx.b -(A5),-(A0)", + "subx.b -(A6),-(A0)","subx.b -(A7),-(A0)" }; + static const char *opATable0245[] = { "subx.w D0,D0","subx.w D1,D0","subx.w D2,D0","subx.w D3,D0", + "subx.w D4,D0","subx.w D5,D0","subx.w D6,D0","subx.w D7,D0","subx.w -(A0),-(A0)","subx.w -(A1),-(A0)", + "subx.w -(A2),-(A0)","subx.w -(A3),-(A0)","subx.w -(A4),-(A0)","subx.w -(A5),-(A0)", + "subx.w -(A6),-(A0)","subx.w -(A7),-(A0)" }; + static const char *opATable0246[] = { "subx.l D0,D0","subx.l D1,D0","subx.l D2,D0","subx.l D3,D0", + "subx.l D4,D0","subx.l D5,D0","subx.l D6,D0","subx.l D7,D0","subx.l -(A0),-(A0)","subx.l -(A1),-(A0)", + "subx.l -(A2),-(A0)","subx.l -(A3),-(A0)","subx.l -(A4),-(A0)","subx.l -(A5),-(A0)", + "subx.l -(A6),-(A0)","subx.l -(A7),-(A0)" }; + static const char *opATable024C[] = { "subx.b D0,D1","subx.b D1,D1","subx.b D2,D1","subx.b D3,D1", + "subx.b D4,D1","subx.b D5,D1","subx.b D6,D1","subx.b D7,D1","subx.b -(A0),-(A1)","subx.b -(A1),-(A1)", + "subx.b -(A2),-(A1)","subx.b -(A3),-(A1)","subx.b -(A4),-(A1)","subx.b -(A5),-(A1)", + "subx.b -(A6),-(A1)","subx.b -(A7),-(A1)" }; + static const char *opATable024D[] = { "subx.w D0,D1","subx.w D1,D1","subx.w D2,D1","subx.w D3,D1", + "subx.w D4,D1","subx.w D5,D1","subx.w D6,D1","subx.w D7,D1","subx.w -(A0),-(A1)","subx.w -(A1),-(A1)", + "subx.w -(A2),-(A1)","subx.w -(A3),-(A1)","subx.w -(A4),-(A1)","subx.w -(A5),-(A1)", + "subx.w -(A6),-(A1)","subx.w -(A7),-(A1)" }; + static const char *opATable024E[] = { "subx.l D0,D1","subx.l D1,D1","subx.l D2,D1","subx.l D3,D1", + "subx.l D4,D1","subx.l D5,D1","subx.l D6,D1","subx.l D7,D1","subx.l -(A0),-(A1)","subx.l -(A1),-(A1)", + "subx.l -(A2),-(A1)","subx.l -(A3),-(A1)","subx.l -(A4),-(A1)","subx.l -(A5),-(A1)", + "subx.l -(A6),-(A1)","subx.l -(A7),-(A1)" }; + static const char *opATable0254[] = { "subx.b D0,D2","subx.b D1,D2","subx.b D2,D2","subx.b D3,D2", + "subx.b D4,D2","subx.b D5,D2","subx.b D6,D2","subx.b D7,D2","subx.b -(A0),-(A2)","subx.b -(A1),-(A2)", + "subx.b -(A2),-(A2)","subx.b -(A3),-(A2)","subx.b -(A4),-(A2)","subx.b -(A5),-(A2)", + "subx.b -(A6),-(A2)","subx.b -(A7),-(A2)" }; + static const char *opATable0255[] = { "subx.w D0,D2","subx.w D1,D2","subx.w D2,D2","subx.w D3,D2", + "subx.w D4,D2","subx.w D5,D2","subx.w D6,D2","subx.w D7,D2","subx.w -(A0),-(A2)","subx.w -(A1),-(A2)", + "subx.w -(A2),-(A2)","subx.w -(A3),-(A2)","subx.w -(A4),-(A2)","subx.w -(A5),-(A2)", + "subx.w -(A6),-(A2)","subx.w -(A7),-(A2)" }; + static const char *opATable0256[] = { "subx.l D0,D2","subx.l D1,D2","subx.l D2,D2","subx.l D3,D2", + "subx.l D4,D2","subx.l D5,D2","subx.l D6,D2","subx.l D7,D2","subx.l -(A0),-(A2)","subx.l -(A1),-(A2)", + "subx.l -(A2),-(A2)","subx.l -(A3),-(A2)","subx.l -(A4),-(A2)","subx.l -(A5),-(A2)", + "subx.l -(A6),-(A2)","subx.l -(A7),-(A2)" }; + static const char *opATable025C[] = { "subx.b D0,D3","subx.b D1,D3","subx.b D2,D3","subx.b D3,D3", + "subx.b D4,D3","subx.b D5,D3","subx.b D6,D3","subx.b D7,D3","subx.b -(A0),-(A3)","subx.b -(A1),-(A3)", + "subx.b -(A2),-(A3)","subx.b -(A3),-(A3)","subx.b -(A4),-(A3)","subx.b -(A5),-(A3)", + "subx.b -(A6),-(A3)","subx.b -(A7),-(A3)" }; + static const char *opATable025D[] = { "subx.w D0,D3","subx.w D1,D3","subx.w D2,D3","subx.w D3,D3", + "subx.w D4,D3","subx.w D5,D3","subx.w D6,D3","subx.w D7,D3","subx.w -(A0),-(A3)","subx.w -(A1),-(A3)", + "subx.w -(A2),-(A3)","subx.w -(A3),-(A3)","subx.w -(A4),-(A3)","subx.w -(A5),-(A3)", + "subx.w -(A6),-(A3)","subx.w -(A7),-(A3)" }; + static const char *opATable025E[] = { "subx.l D0,D3","subx.l D1,D3","subx.l D2,D3","subx.l D3,D3", + "subx.l D4,D3","subx.l D5,D3","subx.l D6,D3","subx.l D7,D3","subx.l -(A0),-(A3)","subx.l -(A1),-(A3)", + "subx.l -(A2),-(A3)","subx.l -(A3),-(A3)","subx.l -(A4),-(A3)","subx.l -(A5),-(A3)", + "subx.l -(A6),-(A3)","subx.l -(A7),-(A3)" }; + static const char *opATable0264[] = { "subx.b D0,D4","subx.b D1,D4","subx.b D2,D4","subx.b D3,D4", + "subx.b D4,D4","subx.b D5,D4","subx.b D6,D4","subx.b D7,D4","subx.b -(A0),-(A4)","subx.b -(A1),-(A4)", + "subx.b -(A2),-(A4)","subx.b -(A3),-(A4)","subx.b -(A4),-(A4)","subx.b -(A5),-(A4)", + "subx.b -(A6),-(A4)","subx.b -(A7),-(A4)" }; + static const char *opATable0265[] = { "subx.w D0,D4","subx.w D1,D4","subx.w D2,D4","subx.w D3,D4", + "subx.w D4,D4","subx.w D5,D4","subx.w D6,D4","subx.w D7,D4","subx.w -(A0),-(A4)","subx.w -(A1),-(A4)", + "subx.w -(A2),-(A4)","subx.w -(A3),-(A4)","subx.w -(A4),-(A4)","subx.w -(A5),-(A4)", + "subx.w -(A6),-(A4)","subx.w -(A7),-(A4)" }; + static const char *opATable0266[] = { "subx.l D0,D4","subx.l D1,D4","subx.l D2,D4","subx.l D3,D4", + "subx.l D4,D4","subx.l D5,D4","subx.l D6,D4","subx.l D7,D4","subx.l -(A0),-(A4)","subx.l -(A1),-(A4)", + "subx.l -(A2),-(A4)","subx.l -(A3),-(A4)","subx.l -(A4),-(A4)","subx.l -(A5),-(A4)", + "subx.l -(A6),-(A4)","subx.l -(A7),-(A4)" }; + static const char *opATable026C[] = { "subx.b D0,D5","subx.b D1,D5","subx.b D2,D5","subx.b D3,D5", + "subx.b D4,D5","subx.b D5,D5","subx.b D6,D5","subx.b D7,D5","subx.b -(A0),-(A5)","subx.b -(A1),-(A5)", + "subx.b -(A2),-(A5)","subx.b -(A3),-(A5)","subx.b -(A4),-(A5)","subx.b -(A5),-(A5)", + "subx.b -(A6),-(A5)","subx.b -(A7),-(A5)" }; + static const char *opATable026D[] = { "subx.w D0,D5","subx.w D1,D5","subx.w D2,D5","subx.w D3,D5", + "subx.w D4,D5","subx.w D5,D5","subx.w D6,D5","subx.w D7,D5","subx.w -(A0),-(A5)","subx.w -(A1),-(A5)", + "subx.w -(A2),-(A5)","subx.w -(A3),-(A5)","subx.w -(A4),-(A5)","subx.w -(A5),-(A5)", + "subx.w -(A6),-(A5)","subx.w -(A7),-(A5)" }; + static const char *opATable026E[] = { "subx.l D0,D5","subx.l D1,D5","subx.l D2,D5","subx.l D3,D5", + "subx.l D4,D5","subx.l D5,D5","subx.l D6,D5","subx.l D7,D5","subx.l -(A0),-(A5)","subx.l -(A1),-(A5)", + "subx.l -(A2),-(A5)","subx.l -(A3),-(A5)","subx.l -(A4),-(A5)","subx.l -(A5),-(A5)", + "subx.l -(A6),-(A5)","subx.l -(A7),-(A5)" }; + static const char *opATable0274[] = { "subx.b D0,D6","subx.b D1,D6","subx.b D2,D6","subx.b D3,D6", + "subx.b D4,D6","subx.b D5,D6","subx.b D6,D6","subx.b D7,D6","subx.b -(A0),-(A6)","subx.b -(A1),-(A6)", + "subx.b -(A2),-(A6)","subx.b -(A3),-(A6)","subx.b -(A4),-(A6)","subx.b -(A5),-(A6)", + "subx.b -(A6),-(A6)","subx.b -(A7),-(A6)" }; + static const char *opATable0275[] = { "subx.w D0,D6","subx.w D1,D6","subx.w D2,D6","subx.w D3,D6", + "subx.w D4,D6","subx.w D5,D6","subx.w D6,D6","subx.w D7,D6","subx.w -(A0),-(A6)","subx.w -(A1),-(A6)", + "subx.w -(A2),-(A6)","subx.w -(A3),-(A6)","subx.w -(A4),-(A6)","subx.w -(A5),-(A6)", + "subx.w -(A6),-(A6)","subx.w -(A7),-(A6)" }; + static const char *opATable0276[] = { "subx.l D0,D6","subx.l D1,D6","subx.l D2,D6","subx.l D3,D6", + "subx.l D4,D6","subx.l D5,D6","subx.l D6,D6","subx.l D7,D6","subx.l -(A0),-(A6)","subx.l -(A1),-(A6)", + "subx.l -(A2),-(A6)","subx.l -(A3),-(A6)","subx.l -(A4),-(A6)","subx.l -(A5),-(A6)", + "subx.l -(A6),-(A6)","subx.l -(A7),-(A6)" }; + static const char *opATable027C[] = { "subx.b D0,D7","subx.b D1,D7","subx.b D2,D7","subx.b D3,D7", + "subx.b D4,D7","subx.b D5,D7","subx.b D6,D7","subx.b D7,D7","subx.b -(A0),-(A7)","subx.b -(A1),-(A7)", + "subx.b -(A2),-(A7)","subx.b -(A3),-(A7)","subx.b -(A4),-(A7)","subx.b -(A5),-(A7)", + "subx.b -(A6),-(A7)","subx.b -(A7),-(A7)" }; + static const char *opATable027D[] = { "subx.w D0,D7","subx.w D1,D7","subx.w D2,D7","subx.w D3,D7", + "subx.w D4,D7","subx.w D5,D7","subx.w D6,D7","subx.w D7,D7","subx.w -(A0),-(A7)","subx.w -(A1),-(A7)", + "subx.w -(A2),-(A7)","subx.w -(A3),-(A7)","subx.w -(A4),-(A7)","subx.w -(A5),-(A7)", + "subx.w -(A6),-(A7)","subx.w -(A7),-(A7)" }; + static const char *opATable027E[] = { "subx.l D0,D7","subx.l D1,D7","subx.l D2,D7","subx.l D3,D7", + "subx.l D4,D7","subx.l D5,D7","subx.l D6,D7","subx.l D7,D7","subx.l -(A0),-(A7)","subx.l -(A1),-(A7)", + "subx.l -(A2),-(A7)","subx.l -(A3),-(A7)","subx.l -(A4),-(A7)","subx.l -(A5),-(A7)", + "subx.l -(A6),-(A7)","subx.l -(A7),-(A7)" }; + static const char *opATable02C4[] = { "cmpm.b D0,D0","cmpm.b D1,D0","cmpm.b D2,D0","cmpm.b D3,D0", + "cmpm.b D4,D0","cmpm.b D5,D0","cmpm.b D6,D0","cmpm.b D7,D0","cmpm.b (A0)+,(A0)+","cmpm.b (A1)+,(A0)+", + "cmpm.b (A2)+,(A0)+","cmpm.b (A3)+,(A0)+","cmpm.b (A4)+,(A0)+","cmpm.b (A5)+,(A0)+", + "cmpm.b (A6)+,(A0)+","cmpm.b (A7)+,(A0)+" }; + static const char *opATable02C5[] = { "cmpm.w D0,D0","cmpm.w D1,D0","cmpm.w D2,D0","cmpm.w D3,D0", + "cmpm.w D4,D0","cmpm.w D5,D0","cmpm.w D6,D0","cmpm.w D7,D0","cmpm.w (A0)+,(A0)+","cmpm.w (A1)+,(A0)+", + "cmpm.w (A2)+,(A0)+","cmpm.w (A3)+,(A0)+","cmpm.w (A4)+,(A0)+","cmpm.w (A5)+,(A0)+", + "cmpm.w (A6)+,(A0)+","cmpm.w (A7)+,(A0)+" }; + static const char *opATable02C6[] = { "cmpm.l D0,D0","cmpm.l D1,D0","cmpm.l D2,D0","cmpm.l D3,D0", + "cmpm.l D4,D0","cmpm.l D5,D0","cmpm.l D6,D0","cmpm.l D7,D0","cmpm.l (A0)+,(A0)+","cmpm.l (A1)+,(A0)+", + "cmpm.l (A2)+,(A0)+","cmpm.l (A3)+,(A0)+","cmpm.l (A4)+,(A0)+","cmpm.l (A5)+,(A0)+", + "cmpm.l (A6)+,(A0)+","cmpm.l (A7)+,(A0)+" }; + static const char *opATable02CC[] = { "cmpm.b D0,D1","cmpm.b D1,D1","cmpm.b D2,D1","cmpm.b D3,D1", + "cmpm.b D4,D1","cmpm.b D5,D1","cmpm.b D6,D1","cmpm.b D7,D1","cmpm.b (A0)+,(A1)+","cmpm.b (A1)+,(A1)+", + "cmpm.b (A2)+,(A1)+","cmpm.b (A3)+,(A1)+","cmpm.b (A4)+,(A1)+","cmpm.b (A5)+,(A1)+", + "cmpm.b (A6)+,(A1)+","cmpm.b (A7)+,(A1)+" }; + static const char *opATable02CD[] = { "cmpm.w D0,D1","cmpm.w D1,D1","cmpm.w D2,D1","cmpm.w D3,D1", + "cmpm.w D4,D1","cmpm.w D5,D1","cmpm.w D6,D1","cmpm.w D7,D1","cmpm.w (A0)+,(A1)+","cmpm.w (A1)+,(A1)+", + "cmpm.w (A2)+,(A1)+","cmpm.w (A3)+,(A1)+","cmpm.w (A4)+,(A1)+","cmpm.w (A5)+,(A1)+", + "cmpm.w (A6)+,(A1)+","cmpm.w (A7)+,(A1)+" }; + static const char *opATable02CE[] = { "cmpm.l D0,D1","cmpm.l D1,D1","cmpm.l D2,D1","cmpm.l D3,D1", + "cmpm.l D4,D1","cmpm.l D5,D1","cmpm.l D6,D1","cmpm.l D7,D1","cmpm.l (A0)+,(A1)+","cmpm.l (A1)+,(A1)+", + "cmpm.l (A2)+,(A1)+","cmpm.l (A3)+,(A1)+","cmpm.l (A4)+,(A1)+","cmpm.l (A5)+,(A1)+", + "cmpm.l (A6)+,(A1)+","cmpm.l (A7)+,(A1)+" }; + static const char *opATable02D4[] = { "cmpm.b D0,D2","cmpm.b D1,D2","cmpm.b D2,D2","cmpm.b D3,D2", + "cmpm.b D4,D2","cmpm.b D5,D2","cmpm.b D6,D2","cmpm.b D7,D2","cmpm.b (A0)+,(A2)+","cmpm.b (A1)+,(A2)+", + "cmpm.b (A2)+,(A2)+","cmpm.b (A3)+,(A2)+","cmpm.b (A4)+,(A2)+","cmpm.b (A5)+,(A2)+", + "cmpm.b (A6)+,(A2)+","cmpm.b (A7)+,(A2)+" }; + static const char *opATable02D5[] = { "cmpm.w D0,D2","cmpm.w D1,D2","cmpm.w D2,D2","cmpm.w D3,D2", + "cmpm.w D4,D2","cmpm.w D5,D2","cmpm.w D6,D2","cmpm.w D7,D2","cmpm.w (A0)+,(A2)+","cmpm.w (A1)+,(A2)+", + "cmpm.w (A2)+,(A2)+","cmpm.w (A3)+,(A2)+","cmpm.w (A4)+,(A2)+","cmpm.w (A5)+,(A2)+", + "cmpm.w (A6)+,(A2)+","cmpm.w (A7)+,(A2)+" }; + static const char *opATable02D6[] = { "cmpm.l D0,D2","cmpm.l D1,D2","cmpm.l D2,D2","cmpm.l D3,D2", + "cmpm.l D4,D2","cmpm.l D5,D2","cmpm.l D6,D2","cmpm.l D7,D2","cmpm.l (A0)+,(A2)+","cmpm.l (A1)+,(A2)+", + "cmpm.l (A2)+,(A2)+","cmpm.l (A3)+,(A2)+","cmpm.l (A4)+,(A2)+","cmpm.l (A5)+,(A2)+", + "cmpm.l (A6)+,(A2)+","cmpm.l (A7)+,(A2)+" }; + static const char *opATable02DC[] = { "cmpm.b D0,D3","cmpm.b D1,D3","cmpm.b D2,D3","cmpm.b D3,D3", + "cmpm.b D4,D3","cmpm.b D5,D3","cmpm.b D6,D3","cmpm.b D7,D3","cmpm.b (A0)+,(A3)+","cmpm.b (A1)+,(A3)+", + "cmpm.b (A2)+,(A3)+","cmpm.b (A3)+,(A3)+","cmpm.b (A4)+,(A3)+","cmpm.b (A5)+,(A3)+", + "cmpm.b (A6)+,(A3)+","cmpm.b (A7)+,(A3)+" }; + static const char *opATable02DD[] = { "cmpm.w D0,D3","cmpm.w D1,D3","cmpm.w D2,D3","cmpm.w D3,D3", + "cmpm.w D4,D3","cmpm.w D5,D3","cmpm.w D6,D3","cmpm.w D7,D3","cmpm.w (A0)+,(A3)+","cmpm.w (A1)+,(A3)+", + "cmpm.w (A2)+,(A3)+","cmpm.w (A3)+,(A3)+","cmpm.w (A4)+,(A3)+","cmpm.w (A5)+,(A3)+", + "cmpm.w (A6)+,(A3)+","cmpm.w (A7)+,(A3)+" }; + static const char *opATable02DE[] = { "cmpm.l D0,D3","cmpm.l D1,D3","cmpm.l D2,D3","cmpm.l D3,D3", + "cmpm.l D4,D3","cmpm.l D5,D3","cmpm.l D6,D3","cmpm.l D7,D3","cmpm.l (A0)+,(A3)+","cmpm.l (A1)+,(A3)+", + "cmpm.l (A2)+,(A3)+","cmpm.l (A3)+,(A3)+","cmpm.l (A4)+,(A3)+","cmpm.l (A5)+,(A3)+", + "cmpm.l (A6)+,(A3)+","cmpm.l (A7)+,(A3)+" }; + static const char *opATable02E4[] = { "cmpm.b D0,D4","cmpm.b D1,D4","cmpm.b D2,D4","cmpm.b D3,D4", + "cmpm.b D4,D4","cmpm.b D5,D4","cmpm.b D6,D4","cmpm.b D7,D4","cmpm.b (A0)+,(A4)+","cmpm.b (A1)+,(A4)+", + "cmpm.b (A2)+,(A4)+","cmpm.b (A3)+,(A4)+","cmpm.b (A4)+,(A4)+","cmpm.b (A5)+,(A4)+", + "cmpm.b (A6)+,(A4)+","cmpm.b (A7)+,(A4)+" }; + static const char *opATable02E5[] = { "cmpm.w D0,D4","cmpm.w D1,D4","cmpm.w D2,D4","cmpm.w D3,D4", + "cmpm.w D4,D4","cmpm.w D5,D4","cmpm.w D6,D4","cmpm.w D7,D4","cmpm.w (A0)+,(A4)+","cmpm.w (A1)+,(A4)+", + "cmpm.w (A2)+,(A4)+","cmpm.w (A3)+,(A4)+","cmpm.w (A4)+,(A4)+","cmpm.w (A5)+,(A4)+", + "cmpm.w (A6)+,(A4)+","cmpm.w (A7)+,(A4)+" }; + static const char *opATable02E6[] = { "cmpm.l D0,D4","cmpm.l D1,D4","cmpm.l D2,D4","cmpm.l D3,D4", + "cmpm.l D4,D4","cmpm.l D5,D4","cmpm.l D6,D4","cmpm.l D7,D4","cmpm.l (A0)+,(A4)+","cmpm.l (A1)+,(A4)+", + "cmpm.l (A2)+,(A4)+","cmpm.l (A3)+,(A4)+","cmpm.l (A4)+,(A4)+","cmpm.l (A5)+,(A4)+", + "cmpm.l (A6)+,(A4)+","cmpm.l (A7)+,(A4)+" }; + static const char *opATable02EC[] = { "cmpm.b D0,D5","cmpm.b D1,D5","cmpm.b D2,D5","cmpm.b D3,D5", + "cmpm.b D4,D5","cmpm.b D5,D5","cmpm.b D6,D5","cmpm.b D7,D5","cmpm.b (A0)+,(A5)+","cmpm.b (A1)+,(A5)+", + "cmpm.b (A2)+,(A5)+","cmpm.b (A3)+,(A5)+","cmpm.b (A4)+,(A5)+","cmpm.b (A5)+,(A5)+", + "cmpm.b (A6)+,(A5)+","cmpm.b (A7)+,(A5)+" }; + static const char *opATable02ED[] = { "cmpm.w D0,D5","cmpm.w D1,D5","cmpm.w D2,D5","cmpm.w D3,D5", + "cmpm.w D4,D5","cmpm.w D5,D5","cmpm.w D6,D5","cmpm.w D7,D5","cmpm.w (A0)+,(A5)+","cmpm.w (A1)+,(A5)+", + "cmpm.w (A2)+,(A5)+","cmpm.w (A3)+,(A5)+","cmpm.w (A4)+,(A5)+","cmpm.w (A5)+,(A5)+", + "cmpm.w (A6)+,(A5)+","cmpm.w (A7)+,(A5)+" }; + static const char *opATable02EE[] = { "cmpm.l D0,D5","cmpm.l D1,D5","cmpm.l D2,D5","cmpm.l D3,D5", + "cmpm.l D4,D5","cmpm.l D5,D5","cmpm.l D6,D5","cmpm.l D7,D5","cmpm.l (A0)+,(A5)+","cmpm.l (A1)+,(A5)+", + "cmpm.l (A2)+,(A5)+","cmpm.l (A3)+,(A5)+","cmpm.l (A4)+,(A5)+","cmpm.l (A5)+,(A5)+", + "cmpm.l (A6)+,(A5)+","cmpm.l (A7)+,(A5)+" }; + static const char *opATable02F4[] = { "cmpm.b D0,D6","cmpm.b D1,D6","cmpm.b D2,D6","cmpm.b D3,D6", + "cmpm.b D4,D6","cmpm.b D5,D6","cmpm.b D6,D6","cmpm.b D7,D6","cmpm.b (A0)+,(A6)+","cmpm.b (A1)+,(A6)+", + "cmpm.b (A2)+,(A6)+","cmpm.b (A3)+,(A6)+","cmpm.b (A4)+,(A6)+","cmpm.b (A5)+,(A6)+", + "cmpm.b (A6)+,(A6)+","cmpm.b (A7)+,(A6)+" }; + static const char *opATable02F5[] = { "cmpm.w D0,D6","cmpm.w D1,D6","cmpm.w D2,D6","cmpm.w D3,D6", + "cmpm.w D4,D6","cmpm.w D5,D6","cmpm.w D6,D6","cmpm.w D7,D6","cmpm.w (A0)+,(A6)+","cmpm.w (A1)+,(A6)+", + "cmpm.w (A2)+,(A6)+","cmpm.w (A3)+,(A6)+","cmpm.w (A4)+,(A6)+","cmpm.w (A5)+,(A6)+", + "cmpm.w (A6)+,(A6)+","cmpm.w (A7)+,(A6)+" }; + static const char *opATable02F6[] = { "cmpm.l D0,D6","cmpm.l D1,D6","cmpm.l D2,D6","cmpm.l D3,D6", + "cmpm.l D4,D6","cmpm.l D5,D6","cmpm.l D6,D6","cmpm.l D7,D6","cmpm.l (A0)+,(A6)+","cmpm.l (A1)+,(A6)+", + "cmpm.l (A2)+,(A6)+","cmpm.l (A3)+,(A6)+","cmpm.l (A4)+,(A6)+","cmpm.l (A5)+,(A6)+", + "cmpm.l (A6)+,(A6)+","cmpm.l (A7)+,(A6)+" }; + static const char *opATable02FC[] = { "cmpm.b D0,D7","cmpm.b D1,D7","cmpm.b D2,D7","cmpm.b D3,D7", + "cmpm.b D4,D7","cmpm.b D5,D7","cmpm.b D6,D7","cmpm.b D7,D7","cmpm.b (A0)+,(A7)+","cmpm.b (A1)+,(A7)+", + "cmpm.b (A2)+,(A7)+","cmpm.b (A3)+,(A7)+","cmpm.b (A4)+,(A7)+","cmpm.b (A5)+,(A7)+", + "cmpm.b (A6)+,(A7)+","cmpm.b (A7)+,(A7)+" }; + static const char *opATable02FD[] = { "cmpm.w D0,D7","cmpm.w D1,D7","cmpm.w D2,D7","cmpm.w D3,D7", + "cmpm.w D4,D7","cmpm.w D5,D7","cmpm.w D6,D7","cmpm.w D7,D7","cmpm.w (A0)+,(A7)+","cmpm.w (A1)+,(A7)+", + "cmpm.w (A2)+,(A7)+","cmpm.w (A3)+,(A7)+","cmpm.w (A4)+,(A7)+","cmpm.w (A5)+,(A7)+", + "cmpm.w (A6)+,(A7)+","cmpm.w (A7)+,(A7)+" }; + static const char *opATable02FE[] = { "cmpm.l D0,D7","cmpm.l D1,D7","cmpm.l D2,D7","cmpm.l D3,D7", + "cmpm.l D4,D7","cmpm.l D5,D7","cmpm.l D6,D7","cmpm.l D7,D7","cmpm.l (A0)+,(A7)+","cmpm.l (A1)+,(A7)+", + "cmpm.l (A2)+,(A7)+","cmpm.l (A3)+,(A7)+","cmpm.l (A4)+,(A7)+","cmpm.l (A5)+,(A7)+", + "cmpm.l (A6)+,(A7)+","cmpm.l (A7)+,(A7)+" }; + static const char *opATable0304[] = { "abcd D0,D0","abcd D1,D0","abcd D2,D0","abcd D3,D0","abcd D4,D0", + "abcd D5,D0","abcd D6,D0","abcd D7,D0","abcd -(A0),-(A0)","abcd -(A1),-(A0)","abcd -(A2),-(A0)", + "abcd -(A3),-(A0)","abcd -(A4),-(A0)","abcd -(A5),-(A0)","abcd -(A6),-(A0)","abcd -(A7),-(A0)" }; + static const char *opATable0305[] = { "exg D0,D0","exg D1,D0","exg D2,D0","exg D3,D0","exg D4,D0", + "exg D5,D0","exg D6,D0","exg D7,D0","exg A0,A0","exg A1,A0","exg A2,A0","exg A3,A0", + "exg A4,A0","exg A5,A0","exg A6,A0","exg A7,A0" }; + static const char *opATable0306[] = { "exg A0,D0","exg A1,D0","exg A2,D0","exg A3,D0","exg A4,D0", + "exg A5,D0","exg A6,D0","exg A7,D0" }; + static const char *opATable030C[] = { "abcd D0,D1","abcd D1,D1","abcd D2,D1","abcd D3,D1","abcd D4,D1", + "abcd D5,D1","abcd D6,D1","abcd D7,D1","abcd -(A0),-(A1)","abcd -(A1),-(A1)","abcd -(A2),-(A1)", + "abcd -(A3),-(A1)","abcd -(A4),-(A1)","abcd -(A5),-(A1)","abcd -(A6),-(A1)","abcd -(A7),-(A1)" }; + static const char *opATable030D[] = { "exg D0,D1","exg D1,D1","exg D2,D1","exg D3,D1","exg D4,D1", + "exg D5,D1","exg D6,D1","exg D7,D1","exg A0,A1","exg A1,A1","exg A2,A1","exg A3,A1", + "exg A4,A1","exg A5,A1","exg A6,A1","exg A7,A1" }; + static const char *opATable030E[] = { "exg A0,D1","exg A1,D1","exg A2,D1","exg A3,D1","exg A4,D1", + "exg A5,D1","exg A6,D1","exg A7,D1" }; + static const char *opATable0314[] = { "abcd D0,D2","abcd D1,D2","abcd D2,D2","abcd D3,D2","abcd D4,D2", + "abcd D5,D2","abcd D6,D2","abcd D7,D2","abcd -(A0),-(A2)","abcd -(A1),-(A2)","abcd -(A2),-(A2)", + "abcd -(A3),-(A2)","abcd -(A4),-(A2)","abcd -(A5),-(A2)","abcd -(A6),-(A2)","abcd -(A7),-(A2)" }; + static const char *opATable0315[] = { "exg D0,D2","exg D1,D2","exg D2,D2","exg D3,D2","exg D4,D2", + "exg D5,D2","exg D6,D2","exg D7,D2","exg A0,A2","exg A1,A2","exg A2,A2","exg A3,A2", + "exg A4,A2","exg A5,A2","exg A6,A2","exg A7,A2" }; + static const char *opATable0316[] = { "exg A0,D2","exg A1,D2","exg A2,D2","exg A3,D2","exg A4,D2", + "exg A5,D2","exg A6,D2","exg A7,D2" }; + static const char *opATable031C[] = { "abcd D0,D3","abcd D1,D3","abcd D2,D3","abcd D3,D3","abcd D4,D3", + "abcd D5,D3","abcd D6,D3","abcd D7,D3","abcd -(A0),-(A3)","abcd -(A1),-(A3)","abcd -(A2),-(A3)", + "abcd -(A3),-(A3)","abcd -(A4),-(A3)","abcd -(A5),-(A3)","abcd -(A6),-(A3)","abcd -(A7),-(A3)" }; + static const char *opATable031D[] = { "exg D0,D3","exg D1,D3","exg D2,D3","exg D3,D3","exg D4,D3", + "exg D5,D3","exg D6,D3","exg D7,D3","exg A0,A3","exg A1,A3","exg A2,A3","exg A3,A3", + "exg A4,A3","exg A5,A3","exg A6,A3","exg A7,A3" }; + static const char *opATable031E[] = { "exg A0,D3","exg A1,D3","exg A2,D3","exg A3,D3","exg A4,D3", + "exg A5,D3","exg A6,D3","exg A7,D3" }; + static const char *opATable0324[] = { "abcd D0,D4","abcd D1,D4","abcd D2,D4","abcd D3,D4","abcd D4,D4", + "abcd D5,D4","abcd D6,D4","abcd D7,D4","abcd -(A0),-(A4)","abcd -(A1),-(A4)","abcd -(A2),-(A4)", + "abcd -(A3),-(A4)","abcd -(A4),-(A4)","abcd -(A5),-(A4)","abcd -(A6),-(A4)","abcd -(A7),-(A4)" }; + static const char *opATable0325[] = { "exg D0,D4","exg D1,D4","exg D2,D4","exg D3,D4","exg D4,D4", + "exg D5,D4","exg D6,D4","exg D7,D4","exg A0,A4","exg A1,A4","exg A2,A4","exg A3,A4", + "exg A4,A4","exg A5,A4","exg A6,A4","exg A7,A4" }; + static const char *opATable0326[] = { "exg A0,D4","exg A1,D4","exg A2,D4","exg A3,D4","exg A4,D4", + "exg A5,D4","exg A6,D4","exg A7,D4" }; + static const char *opATable032C[] = { "abcd D0,D5","abcd D1,D5","abcd D2,D5","abcd D3,D5","abcd D4,D5", + "abcd D5,D5","abcd D6,D5","abcd D7,D5","abcd -(A0),-(A5)","abcd -(A1),-(A5)","abcd -(A2),-(A5)", + "abcd -(A3),-(A5)","abcd -(A4),-(A5)","abcd -(A5),-(A5)","abcd -(A6),-(A5)","abcd -(A7),-(A5)" }; + static const char *opATable032D[] = { "exg D0,D5","exg D1,D5","exg D2,D5","exg D3,D5","exg D4,D5", + "exg D5,D5","exg D6,D5","exg D7,D5","exg A0,A5","exg A1,A5","exg A2,A5","exg A3,A5", + "exg A4,A5","exg A5,A5","exg A6,A5","exg A7,A5" }; + static const char *opATable032E[] = { "exg A0,D5","exg A1,D5","exg A2,D5","exg A3,D5","exg A4,D5", + "exg A5,D5","exg A6,D5","exg A7,D5" }; + static const char *opATable0334[] = { "abcd D0,D6","abcd D1,D6","abcd D2,D6","abcd D3,D6","abcd D4,D6", + "abcd D5,D6","abcd D6,D6","abcd D7,D6","abcd -(A0),-(A6)","abcd -(A1),-(A6)","abcd -(A2),-(A6)", + "abcd -(A3),-(A6)","abcd -(A4),-(A6)","abcd -(A5),-(A6)","abcd -(A6),-(A6)","abcd -(A7),-(A6)" }; + static const char *opATable0335[] = { "exg D0,D6","exg D1,D6","exg D2,D6","exg D3,D6","exg D4,D6", + "exg D5,D6","exg D6,D6","exg D7,D6","exg A0,A6","exg A1,A6","exg A2,A6","exg A3,A6", + "exg A4,A6","exg A5,A6","exg A6,A6","exg A7,A6" }; + static const char *opATable0336[] = { "exg A0,D6","exg A1,D6","exg A2,D6","exg A3,D6","exg A4,D6", + "exg A5,D6","exg A6,D6","exg A7,D6" }; + static const char *opATable033C[] = { "abcd D0,D7","abcd D1,D7","abcd D2,D7","abcd D3,D7","abcd D4,D7", + "abcd D5,D7","abcd D6,D7","abcd D7,D7","abcd -(A0),-(A7)","abcd -(A1),-(A7)","abcd -(A2),-(A7)", + "abcd -(A3),-(A7)","abcd -(A4),-(A7)","abcd -(A5),-(A7)","abcd -(A6),-(A7)","abcd -(A7),-(A7)" }; + static const char *opATable033D[] = { "exg D0,D7","exg D1,D7","exg D2,D7","exg D3,D7","exg D4,D7", + "exg D5,D7","exg D6,D7","exg D7,D7","exg A0,A7","exg A1,A7","exg A2,A7","exg A3,A7", + "exg A4,A7","exg A5,A7","exg A6,A7","exg A7,A7" }; + static const char *opATable033E[] = { "exg A0,D7","exg A1,D7","exg A2,D7","exg A3,D7","exg A4,D7", + "exg A5,D7","exg A6,D7","exg A7,D7" }; + static const char *opATable0344[] = { "addx.b D0,D0","addx.b D1,D0","addx.b D2,D0","addx.b D3,D0", + "addx.b D4,D0","addx.b D5,D0","addx.b D6,D0","addx.b D7,D0","addx.b -(A0),-(A0)","addx.b -(A1),-(A0)", + "addx.b -(A2),-(A0)","addx.b -(A3),-(A0)","addx.b -(A4),-(A0)","addx.b -(A5),-(A0)", + "addx.b -(A6),-(A0)","addx.b -(A7),-(A0)" }; + static const char *opATable0345[] = { "addx.w D0,D0","addx.w D1,D0","addx.w D2,D0","addx.w D3,D0", + "addx.w D4,D0","addx.w D5,D0","addx.w D6,D0","addx.w D7,D0","addx.w -(A0),-(A0)","addx.w -(A1),-(A0)", + "addx.w -(A2),-(A0)","addx.w -(A3),-(A0)","addx.w -(A4),-(A0)","addx.w -(A5),-(A0)", + "addx.w -(A6),-(A0)","addx.w -(A7),-(A0)" }; + static const char *opATable0346[] = { "addx.l D0,D0","addx.l D1,D0","addx.l D2,D0","addx.l D3,D0", + "addx.l D4,D0","addx.l D5,D0","addx.l D6,D0","addx.l D7,D0","addx.l -(A0),-(A0)","addx.l -(A1),-(A0)", + "addx.l -(A2),-(A0)","addx.l -(A3),-(A0)","addx.l -(A4),-(A0)","addx.l -(A5),-(A0)", + "addx.l -(A6),-(A0)","addx.l -(A7),-(A0)" }; + static const char *opATable034C[] = { "addx.b D0,D1","addx.b D1,D1","addx.b D2,D1","addx.b D3,D1", + "addx.b D4,D1","addx.b D5,D1","addx.b D6,D1","addx.b D7,D1","addx.b -(A0),-(A1)","addx.b -(A1),-(A1)", + "addx.b -(A2),-(A1)","addx.b -(A3),-(A1)","addx.b -(A4),-(A1)","addx.b -(A5),-(A1)", + "addx.b -(A6),-(A1)","addx.b -(A7),-(A1)" }; + static const char *opATable034D[] = { "addx.w D0,D1","addx.w D1,D1","addx.w D2,D1","addx.w D3,D1", + "addx.w D4,D1","addx.w D5,D1","addx.w D6,D1","addx.w D7,D1","addx.w -(A0),-(A1)","addx.w -(A1),-(A1)", + "addx.w -(A2),-(A1)","addx.w -(A3),-(A1)","addx.w -(A4),-(A1)","addx.w -(A5),-(A1)", + "addx.w -(A6),-(A1)","addx.w -(A7),-(A1)" }; + static const char *opATable034E[] = { "addx.l D0,D1","addx.l D1,D1","addx.l D2,D1","addx.l D3,D1", + "addx.l D4,D1","addx.l D5,D1","addx.l D6,D1","addx.l D7,D1","addx.l -(A0),-(A1)","addx.l -(A1),-(A1)", + "addx.l -(A2),-(A1)","addx.l -(A3),-(A1)","addx.l -(A4),-(A1)","addx.l -(A5),-(A1)", + "addx.l -(A6),-(A1)","addx.l -(A7),-(A1)" }; + static const char *opATable0354[] = { "addx.b D0,D2","addx.b D1,D2","addx.b D2,D2","addx.b D3,D2", + "addx.b D4,D2","addx.b D5,D2","addx.b D6,D2","addx.b D7,D2","addx.b -(A0),-(A2)","addx.b -(A1),-(A2)", + "addx.b -(A2),-(A2)","addx.b -(A3),-(A2)","addx.b -(A4),-(A2)","addx.b -(A5),-(A2)", + "addx.b -(A6),-(A2)","addx.b -(A7),-(A2)" }; + static const char *opATable0355[] = { "addx.w D0,D2","addx.w D1,D2","addx.w D2,D2","addx.w D3,D2", + "addx.w D4,D2","addx.w D5,D2","addx.w D6,D2","addx.w D7,D2","addx.w -(A0),-(A2)","addx.w -(A1),-(A2)", + "addx.w -(A2),-(A2)","addx.w -(A3),-(A2)","addx.w -(A4),-(A2)","addx.w -(A5),-(A2)", + "addx.w -(A6),-(A2)","addx.w -(A7),-(A2)" }; + static const char *opATable0356[] = { "addx.l D0,D2","addx.l D1,D2","addx.l D2,D2","addx.l D3,D2", + "addx.l D4,D2","addx.l D5,D2","addx.l D6,D2","addx.l D7,D2","addx.l -(A0),-(A2)","addx.l -(A1),-(A2)", + "addx.l -(A2),-(A2)","addx.l -(A3),-(A2)","addx.l -(A4),-(A2)","addx.l -(A5),-(A2)", + "addx.l -(A6),-(A2)","addx.l -(A7),-(A2)" }; + static const char *opATable035C[] = { "addx.b D0,D3","addx.b D1,D3","addx.b D2,D3","addx.b D3,D3", + "addx.b D4,D3","addx.b D5,D3","addx.b D6,D3","addx.b D7,D3","addx.b -(A0),-(A3)","addx.b -(A1),-(A3)", + "addx.b -(A2),-(A3)","addx.b -(A3),-(A3)","addx.b -(A4),-(A3)","addx.b -(A5),-(A3)", + "addx.b -(A6),-(A3)","addx.b -(A7),-(A3)" }; + static const char *opATable035D[] = { "addx.w D0,D3","addx.w D1,D3","addx.w D2,D3","addx.w D3,D3", + "addx.w D4,D3","addx.w D5,D3","addx.w D6,D3","addx.w D7,D3","addx.w -(A0),-(A3)","addx.w -(A1),-(A3)", + "addx.w -(A2),-(A3)","addx.w -(A3),-(A3)","addx.w -(A4),-(A3)","addx.w -(A5),-(A3)", + "addx.w -(A6),-(A3)","addx.w -(A7),-(A3)" }; + static const char *opATable035E[] = { "addx.l D0,D3","addx.l D1,D3","addx.l D2,D3","addx.l D3,D3", + "addx.l D4,D3","addx.l D5,D3","addx.l D6,D3","addx.l D7,D3","addx.l -(A0),-(A3)","addx.l -(A1),-(A3)", + "addx.l -(A2),-(A3)","addx.l -(A3),-(A3)","addx.l -(A4),-(A3)","addx.l -(A5),-(A3)", + "addx.l -(A6),-(A3)","addx.l -(A7),-(A3)" }; + static const char *opATable0364[] = { "addx.b D0,D4","addx.b D1,D4","addx.b D2,D4","addx.b D3,D4", + "addx.b D4,D4","addx.b D5,D4","addx.b D6,D4","addx.b D7,D4","addx.b -(A0),-(A4)","addx.b -(A1),-(A4)", + "addx.b -(A2),-(A4)","addx.b -(A3),-(A4)","addx.b -(A4),-(A4)","addx.b -(A5),-(A4)", + "addx.b -(A6),-(A4)","addx.b -(A7),-(A4)" }; + static const char *opATable0365[] = { "addx.w D0,D4","addx.w D1,D4","addx.w D2,D4","addx.w D3,D4", + "addx.w D4,D4","addx.w D5,D4","addx.w D6,D4","addx.w D7,D4","addx.w -(A0),-(A4)","addx.w -(A1),-(A4)", + "addx.w -(A2),-(A4)","addx.w -(A3),-(A4)","addx.w -(A4),-(A4)","addx.w -(A5),-(A4)", + "addx.w -(A6),-(A4)","addx.w -(A7),-(A4)" }; + static const char *opATable0366[] = { "addx.l D0,D4","addx.l D1,D4","addx.l D2,D4","addx.l D3,D4", + "addx.l D4,D4","addx.l D5,D4","addx.l D6,D4","addx.l D7,D4","addx.l -(A0),-(A4)","addx.l -(A1),-(A4)", + "addx.l -(A2),-(A4)","addx.l -(A3),-(A4)","addx.l -(A4),-(A4)","addx.l -(A5),-(A4)", + "addx.l -(A6),-(A4)","addx.l -(A7),-(A4)" }; + static const char *opATable036C[] = { "addx.b D0,D5","addx.b D1,D5","addx.b D2,D5","addx.b D3,D5", + "addx.b D4,D5","addx.b D5,D5","addx.b D6,D5","addx.b D7,D5","addx.b -(A0),-(A5)","addx.b -(A1),-(A5)", + "addx.b -(A2),-(A5)","addx.b -(A3),-(A5)","addx.b -(A4),-(A5)","addx.b -(A5),-(A5)", + "addx.b -(A6),-(A5)","addx.b -(A7),-(A5)" }; + static const char *opATable036D[] = { "addx.w D0,D5","addx.w D1,D5","addx.w D2,D5","addx.w D3,D5", + "addx.w D4,D5","addx.w D5,D5","addx.w D6,D5","addx.w D7,D5","addx.w -(A0),-(A5)","addx.w -(A1),-(A5)", + "addx.w -(A2),-(A5)","addx.w -(A3),-(A5)","addx.w -(A4),-(A5)","addx.w -(A5),-(A5)", + "addx.w -(A6),-(A5)","addx.w -(A7),-(A5)" }; + static const char *opATable036E[] = { "addx.l D0,D5","addx.l D1,D5","addx.l D2,D5","addx.l D3,D5", + "addx.l D4,D5","addx.l D5,D5","addx.l D6,D5","addx.l D7,D5","addx.l -(A0),-(A5)","addx.l -(A1),-(A5)", + "addx.l -(A2),-(A5)","addx.l -(A3),-(A5)","addx.l -(A4),-(A5)","addx.l -(A5),-(A5)", + "addx.l -(A6),-(A5)","addx.l -(A7),-(A5)" }; + static const char *opATable0374[] = { "addx.b D0,D6","addx.b D1,D6","addx.b D2,D6","addx.b D3,D6", + "addx.b D4,D6","addx.b D5,D6","addx.b D6,D6","addx.b D7,D6","addx.b -(A0),-(A6)","addx.b -(A1),-(A6)", + "addx.b -(A2),-(A6)","addx.b -(A3),-(A6)","addx.b -(A4),-(A6)","addx.b -(A5),-(A6)", + "addx.b -(A6),-(A6)","addx.b -(A7),-(A6)" }; + static const char *opATable0375[] = { "addx.w D0,D6","addx.w D1,D6","addx.w D2,D6","addx.w D3,D6", + "addx.w D4,D6","addx.w D5,D6","addx.w D6,D6","addx.w D7,D6","addx.w -(A0),-(A6)","addx.w -(A1),-(A6)", + "addx.w -(A2),-(A6)","addx.w -(A3),-(A6)","addx.w -(A4),-(A6)","addx.w -(A5),-(A6)", + "addx.w -(A6),-(A6)","addx.w -(A7),-(A6)" }; + static const char *opATable0376[] = { "addx.l D0,D6","addx.l D1,D6","addx.l D2,D6","addx.l D3,D6", + "addx.l D4,D6","addx.l D5,D6","addx.l D6,D6","addx.l D7,D6","addx.l -(A0),-(A6)","addx.l -(A1),-(A6)", + "addx.l -(A2),-(A6)","addx.l -(A3),-(A6)","addx.l -(A4),-(A6)","addx.l -(A5),-(A6)", + "addx.l -(A6),-(A6)","addx.l -(A7),-(A6)" }; + static const char *opATable037C[] = { "addx.b D0,D7","addx.b D1,D7","addx.b D2,D7","addx.b D3,D7", + "addx.b D4,D7","addx.b D5,D7","addx.b D6,D7","addx.b D7,D7","addx.b -(A0),-(A7)","addx.b -(A1),-(A7)", + "addx.b -(A2),-(A7)","addx.b -(A3),-(A7)","addx.b -(A4),-(A7)","addx.b -(A5),-(A7)", + "addx.b -(A6),-(A7)","addx.b -(A7),-(A7)" }; + static const char *opATable037D[] = { "addx.w D0,D7","addx.w D1,D7","addx.w D2,D7","addx.w D3,D7", + "addx.w D4,D7","addx.w D5,D7","addx.w D6,D7","addx.w D7,D7","addx.w -(A0),-(A7)","addx.w -(A1),-(A7)", + "addx.w -(A2),-(A7)","addx.w -(A3),-(A7)","addx.w -(A4),-(A7)","addx.w -(A5),-(A7)", + "addx.w -(A6),-(A7)","addx.w -(A7),-(A7)" }; + static const char *opATable037E[] = { "addx.l D0,D7","addx.l D1,D7","addx.l D2,D7","addx.l D3,D7", + "addx.l D4,D7","addx.l D5,D7","addx.l D6,D7","addx.l D7,D7","addx.l -(A0),-(A7)","addx.l -(A1),-(A7)", + "addx.l -(A2),-(A7)","addx.l -(A3),-(A7)","addx.l -(A4),-(A7)","addx.l -(A5),-(A7)", + "addx.l -(A6),-(A7)","addx.l -(A7),-(A7)" }; + static const char *opATable0380[] = { "asrd.b #8,D0","asrd.b #8,D1","asrd.b #8,D2","asrd.b #8,D3", + "asrd.b #8,D4","asrd.b #8,D5","asrd.b #8,D6","asrd.b #8,D7","lsrd.b #8,D0","lsrd.b #8,D1", + "lsrd.b #8,D2","lsrd.b #8,D3","lsrd.b #8,D4","lsrd.b #8,D5","lsrd.b #8,D6","lsrd.b #8,D7", + "roxrd.b #8,D0","roxrd.b #8,D1","roxrd.b #8,D2","roxrd.b #8,D3","roxrd.b #8,D4","roxrd.b #8,D5", + "roxrd.b #8,D6","roxrd.b #8,D7","rord.b #8,D0","rord.b #8,D1","rord.b #8,D2","rord.b #8,D3", + "rord.b #8,D4","rord.b #8,D5","rord.b #8,D6","rord.b #8,D7","asrd.b D0,D0","asrd.b D0,D1", + "asrd.b D0,D2","asrd.b D0,D3","asrd.b D0,D4","asrd.b D0,D5","asrd.b D0,D6","asrd.b D0,D7", + "lsrd.b D0,D0","lsrd.b D0,D1","lsrd.b D0,D2","lsrd.b D0,D3","lsrd.b D0,D4","lsrd.b D0,D5", + "lsrd.b D0,D6","lsrd.b D0,D7","roxrd.b D0,D0","roxrd.b D0,D1","roxrd.b D0,D2","roxrd.b D0,D3", + "roxrd.b D0,D4","roxrd.b D0,D5","roxrd.b D0,D6","roxrd.b D0,D7","rord.b D0,D0","rord.b D0,D1", + "rord.b D0,D2","rord.b D0,D3","rord.b D0,D4","rord.b D0,D5","rord.b D0,D6","rord.b D0,D7" }; + static const char *opATable0381[] = { "asrd.w #8,D0","asrd.w #8,D1","asrd.w #8,D2","asrd.w #8,D3", + "asrd.w #8,D4","asrd.w #8,D5","asrd.w #8,D6","asrd.w #8,D7","lsrd.w #8,D0","lsrd.w #8,D1", + "lsrd.w #8,D2","lsrd.w #8,D3","lsrd.w #8,D4","lsrd.w #8,D5","lsrd.w #8,D6","lsrd.w #8,D7", + "roxrd.w #8,D0","roxrd.w #8,D1","roxrd.w #8,D2","roxrd.w #8,D3","roxrd.w #8,D4","roxrd.w #8,D5", + "roxrd.w #8,D6","roxrd.w #8,D7","rord.w #8,D0","rord.w #8,D1","rord.w #8,D2","rord.w #8,D3", + "rord.w #8,D4","rord.w #8,D5","rord.w #8,D6","rord.w #8,D7","asrd.w D0,D0","asrd.w D0,D1", + "asrd.w D0,D2","asrd.w D0,D3","asrd.w D0,D4","asrd.w D0,D5","asrd.w D0,D6","asrd.w D0,D7", + "lsrd.w D0,D0","lsrd.w D0,D1","lsrd.w D0,D2","lsrd.w D0,D3","lsrd.w D0,D4","lsrd.w D0,D5", + "lsrd.w D0,D6","lsrd.w D0,D7","roxrd.w D0,D0","roxrd.w D0,D1","roxrd.w D0,D2","roxrd.w D0,D3", + "roxrd.w D0,D4","roxrd.w D0,D5","roxrd.w D0,D6","roxrd.w D0,D7","rord.w D0,D0","rord.w D0,D1", + "rord.w D0,D2","rord.w D0,D3","rord.w D0,D4","rord.w D0,D5","rord.w D0,D6","rord.w D0,D7" }; + static const char *opATable0382[] = { "asrd.l #8,D0","asrd.l #8,D1","asrd.l #8,D2","asrd.l #8,D3", + "asrd.l #8,D4","asrd.l #8,D5","asrd.l #8,D6","asrd.l #8,D7","lsrd.l #8,D0","lsrd.l #8,D1", + "lsrd.l #8,D2","lsrd.l #8,D3","lsrd.l #8,D4","lsrd.l #8,D5","lsrd.l #8,D6","lsrd.l #8,D7", + "roxrd.l #8,D0","roxrd.l #8,D1","roxrd.l #8,D2","roxrd.l #8,D3","roxrd.l #8,D4","roxrd.l #8,D5", + "roxrd.l #8,D6","roxrd.l #8,D7","rord.l #8,D0","rord.l #8,D1","rord.l #8,D2","rord.l #8,D3", + "rord.l #8,D4","rord.l #8,D5","rord.l #8,D6","rord.l #8,D7","asrd.l D0,D0","asrd.l D0,D1", + "asrd.l D0,D2","asrd.l D0,D3","asrd.l D0,D4","asrd.l D0,D5","asrd.l D0,D6","asrd.l D0,D7", + "lsrd.l D0,D0","lsrd.l D0,D1","lsrd.l D0,D2","lsrd.l D0,D3","lsrd.l D0,D4","lsrd.l D0,D5", + "lsrd.l D0,D6","lsrd.l D0,D7","roxrd.l D0,D0","roxrd.l D0,D1","roxrd.l D0,D2","roxrd.l D0,D3", + "roxrd.l D0,D4","roxrd.l D0,D5","roxrd.l D0,D6","roxrd.l D0,D7","rord.l D0,D0","rord.l D0,D1", + "rord.l D0,D2","rord.l D0,D3","rord.l D0,D4","rord.l D0,D5","rord.l D0,D6","rord.l D0,D7" }; + static const char *opATable0384[] = { "asld.b #8,D0","asld.b #8,D1","asld.b #8,D2","asld.b #8,D3", + "asld.b #8,D4","asld.b #8,D5","asld.b #8,D6","asld.b #8,D7","lsld.b #8,D0","lsld.b #8,D1", + "lsld.b #8,D2","lsld.b #8,D3","lsld.b #8,D4","lsld.b #8,D5","lsld.b #8,D6","lsld.b #8,D7", + "roxld.b #8,D0","roxld.b #8,D1","roxld.b #8,D2","roxld.b #8,D3","roxld.b #8,D4","roxld.b #8,D5", + "roxld.b #8,D6","roxld.b #8,D7","rold.b #8,D0","rold.b #8,D1","rold.b #8,D2","rold.b #8,D3", + "rold.b #8,D4","rold.b #8,D5","rold.b #8,D6","rold.b #8,D7","asld.b D0,D0","asld.b D0,D1", + "asld.b D0,D2","asld.b D0,D3","asld.b D0,D4","asld.b D0,D5","asld.b D0,D6","asld.b D0,D7", + "lsld.b D0,D0","lsld.b D0,D1","lsld.b D0,D2","lsld.b D0,D3","lsld.b D0,D4","lsld.b D0,D5", + "lsld.b D0,D6","lsld.b D0,D7","roxld.b D0,D0","roxld.b D0,D1","roxld.b D0,D2","roxld.b D0,D3", + "roxld.b D0,D4","roxld.b D0,D5","roxld.b D0,D6","roxld.b D0,D7","rold.b D0,D0","rold.b D0,D1", + "rold.b D0,D2","rold.b D0,D3","rold.b D0,D4","rold.b D0,D5","rold.b D0,D6","rold.b D0,D7" }; + static const char *opATable0385[] = { "asld.w #8,D0","asld.w #8,D1","asld.w #8,D2","asld.w #8,D3", + "asld.w #8,D4","asld.w #8,D5","asld.w #8,D6","asld.w #8,D7","lsld.w #8,D0","lsld.w #8,D1", + "lsld.w #8,D2","lsld.w #8,D3","lsld.w #8,D4","lsld.w #8,D5","lsld.w #8,D6","lsld.w #8,D7", + "roxld.w #8,D0","roxld.w #8,D1","roxld.w #8,D2","roxld.w #8,D3","roxld.w #8,D4","roxld.w #8,D5", + "roxld.w #8,D6","roxld.w #8,D7","rold.w #8,D0","rold.w #8,D1","rold.w #8,D2","rold.w #8,D3", + "rold.w #8,D4","rold.w #8,D5","rold.w #8,D6","rold.w #8,D7","asld.w D0,D0","asld.w D0,D1", + "asld.w D0,D2","asld.w D0,D3","asld.w D0,D4","asld.w D0,D5","asld.w D0,D6","asld.w D0,D7", + "lsld.w D0,D0","lsld.w D0,D1","lsld.w D0,D2","lsld.w D0,D3","lsld.w D0,D4","lsld.w D0,D5", + "lsld.w D0,D6","lsld.w D0,D7","roxld.w D0,D0","roxld.w D0,D1","roxld.w D0,D2","roxld.w D0,D3", + "roxld.w D0,D4","roxld.w D0,D5","roxld.w D0,D6","roxld.w D0,D7","rold.w D0,D0","rold.w D0,D1", + "rold.w D0,D2","rold.w D0,D3","rold.w D0,D4","rold.w D0,D5","rold.w D0,D6","rold.w D0,D7" }; + static const char *opATable0386[] = { "asld.l #8,D0","asld.l #8,D1","asld.l #8,D2","asld.l #8,D3", + "asld.l #8,D4","asld.l #8,D5","asld.l #8,D6","asld.l #8,D7","lsld.l #8,D0","lsld.l #8,D1", + "lsld.l #8,D2","lsld.l #8,D3","lsld.l #8,D4","lsld.l #8,D5","lsld.l #8,D6","lsld.l #8,D7", + "roxld.l #8,D0","roxld.l #8,D1","roxld.l #8,D2","roxld.l #8,D3","roxld.l #8,D4","roxld.l #8,D5", + "roxld.l #8,D6","roxld.l #8,D7","rold.l #8,D0","rold.l #8,D1","rold.l #8,D2","rold.l #8,D3", + "rold.l #8,D4","rold.l #8,D5","rold.l #8,D6","rold.l #8,D7","asld.l D0,D0","asld.l D0,D1", + "asld.l D0,D2","asld.l D0,D3","asld.l D0,D4","asld.l D0,D5","asld.l D0,D6","asld.l D0,D7", + "lsld.l D0,D0","lsld.l D0,D1","lsld.l D0,D2","lsld.l D0,D3","lsld.l D0,D4","lsld.l D0,D5", + "lsld.l D0,D6","lsld.l D0,D7","roxld.l D0,D0","roxld.l D0,D1","roxld.l D0,D2","roxld.l D0,D3", + "roxld.l D0,D4","roxld.l D0,D5","roxld.l D0,D6","roxld.l D0,D7","rold.l D0,D0","rold.l D0,D1", + "rold.l D0,D2","rold.l D0,D3","rold.l D0,D4","rold.l D0,D5","rold.l D0,D6","rold.l D0,D7" }; + static const char *opATable0388[] = { "asrd.b #1,D0","asrd.b #1,D1","asrd.b #1,D2","asrd.b #1,D3", + "asrd.b #1,D4","asrd.b #1,D5","asrd.b #1,D6","asrd.b #1,D7","lsrd.b #1,D0","lsrd.b #1,D1", + "lsrd.b #1,D2","lsrd.b #1,D3","lsrd.b #1,D4","lsrd.b #1,D5","lsrd.b #1,D6","lsrd.b #1,D7", + "roxrd.b #1,D0","roxrd.b #1,D1","roxrd.b #1,D2","roxrd.b #1,D3","roxrd.b #1,D4","roxrd.b #1,D5", + "roxrd.b #1,D6","roxrd.b #1,D7","rord.b #1,D0","rord.b #1,D1","rord.b #1,D2","rord.b #1,D3", + "rord.b #1,D4","rord.b #1,D5","rord.b #1,D6","rord.b #1,D7","asrd.b D1,D0","asrd.b D1,D1", + "asrd.b D1,D2","asrd.b D1,D3","asrd.b D1,D4","asrd.b D1,D5","asrd.b D1,D6","asrd.b D1,D7", + "lsrd.b D1,D0","lsrd.b D1,D1","lsrd.b D1,D2","lsrd.b D1,D3","lsrd.b D1,D4","lsrd.b D1,D5", + "lsrd.b D1,D6","lsrd.b D1,D7","roxrd.b D1,D0","roxrd.b D1,D1","roxrd.b D1,D2","roxrd.b D1,D3", + "roxrd.b D1,D4","roxrd.b D1,D5","roxrd.b D1,D6","roxrd.b D1,D7","rord.b D1,D0","rord.b D1,D1", + "rord.b D1,D2","rord.b D1,D3","rord.b D1,D4","rord.b D1,D5","rord.b D1,D6","rord.b D1,D7" }; + static const char *opATable0389[] = { "asrd.w #1,D0","asrd.w #1,D1","asrd.w #1,D2","asrd.w #1,D3", + "asrd.w #1,D4","asrd.w #1,D5","asrd.w #1,D6","asrd.w #1,D7","lsrd.w #1,D0","lsrd.w #1,D1", + "lsrd.w #1,D2","lsrd.w #1,D3","lsrd.w #1,D4","lsrd.w #1,D5","lsrd.w #1,D6","lsrd.w #1,D7", + "roxrd.w #1,D0","roxrd.w #1,D1","roxrd.w #1,D2","roxrd.w #1,D3","roxrd.w #1,D4","roxrd.w #1,D5", + "roxrd.w #1,D6","roxrd.w #1,D7","rord.w #1,D0","rord.w #1,D1","rord.w #1,D2","rord.w #1,D3", + "rord.w #1,D4","rord.w #1,D5","rord.w #1,D6","rord.w #1,D7","asrd.w D1,D0","asrd.w D1,D1", + "asrd.w D1,D2","asrd.w D1,D3","asrd.w D1,D4","asrd.w D1,D5","asrd.w D1,D6","asrd.w D1,D7", + "lsrd.w D1,D0","lsrd.w D1,D1","lsrd.w D1,D2","lsrd.w D1,D3","lsrd.w D1,D4","lsrd.w D1,D5", + "lsrd.w D1,D6","lsrd.w D1,D7","roxrd.w D1,D0","roxrd.w D1,D1","roxrd.w D1,D2","roxrd.w D1,D3", + "roxrd.w D1,D4","roxrd.w D1,D5","roxrd.w D1,D6","roxrd.w D1,D7","rord.w D1,D0","rord.w D1,D1", + "rord.w D1,D2","rord.w D1,D3","rord.w D1,D4","rord.w D1,D5","rord.w D1,D6","rord.w D1,D7" }; + static const char *opATable038A[] = { "asrd.l #1,D0","asrd.l #1,D1","asrd.l #1,D2","asrd.l #1,D3", + "asrd.l #1,D4","asrd.l #1,D5","asrd.l #1,D6","asrd.l #1,D7","lsrd.l #1,D0","lsrd.l #1,D1", + "lsrd.l #1,D2","lsrd.l #1,D3","lsrd.l #1,D4","lsrd.l #1,D5","lsrd.l #1,D6","lsrd.l #1,D7", + "roxrd.l #1,D0","roxrd.l #1,D1","roxrd.l #1,D2","roxrd.l #1,D3","roxrd.l #1,D4","roxrd.l #1,D5", + "roxrd.l #1,D6","roxrd.l #1,D7","rord.l #1,D0","rord.l #1,D1","rord.l #1,D2","rord.l #1,D3", + "rord.l #1,D4","rord.l #1,D5","rord.l #1,D6","rord.l #1,D7","asrd.l D1,D0","asrd.l D1,D1", + "asrd.l D1,D2","asrd.l D1,D3","asrd.l D1,D4","asrd.l D1,D5","asrd.l D1,D6","asrd.l D1,D7", + "lsrd.l D1,D0","lsrd.l D1,D1","lsrd.l D1,D2","lsrd.l D1,D3","lsrd.l D1,D4","lsrd.l D1,D5", + "lsrd.l D1,D6","lsrd.l D1,D7","roxrd.l D1,D0","roxrd.l D1,D1","roxrd.l D1,D2","roxrd.l D1,D3", + "roxrd.l D1,D4","roxrd.l D1,D5","roxrd.l D1,D6","roxrd.l D1,D7","rord.l D1,D0","rord.l D1,D1", + "rord.l D1,D2","rord.l D1,D3","rord.l D1,D4","rord.l D1,D5","rord.l D1,D6","rord.l D1,D7" }; + static const char *opATable038C[] = { "asld.b #1,D0","asld.b #1,D1","asld.b #1,D2","asld.b #1,D3", + "asld.b #1,D4","asld.b #1,D5","asld.b #1,D6","asld.b #1,D7","lsld.b #1,D0","lsld.b #1,D1", + "lsld.b #1,D2","lsld.b #1,D3","lsld.b #1,D4","lsld.b #1,D5","lsld.b #1,D6","lsld.b #1,D7", + "roxld.b #1,D0","roxld.b #1,D1","roxld.b #1,D2","roxld.b #1,D3","roxld.b #1,D4","roxld.b #1,D5", + "roxld.b #1,D6","roxld.b #1,D7","rold.b #1,D0","rold.b #1,D1","rold.b #1,D2","rold.b #1,D3", + "rold.b #1,D4","rold.b #1,D5","rold.b #1,D6","rold.b #1,D7","asld.b D1,D0","asld.b D1,D1", + "asld.b D1,D2","asld.b D1,D3","asld.b D1,D4","asld.b D1,D5","asld.b D1,D6","asld.b D1,D7", + "lsld.b D1,D0","lsld.b D1,D1","lsld.b D1,D2","lsld.b D1,D3","lsld.b D1,D4","lsld.b D1,D5", + "lsld.b D1,D6","lsld.b D1,D7","roxld.b D1,D0","roxld.b D1,D1","roxld.b D1,D2","roxld.b D1,D3", + "roxld.b D1,D4","roxld.b D1,D5","roxld.b D1,D6","roxld.b D1,D7","rold.b D1,D0","rold.b D1,D1", + "rold.b D1,D2","rold.b D1,D3","rold.b D1,D4","rold.b D1,D5","rold.b D1,D6","rold.b D1,D7" }; + static const char *opATable038D[] = { "asld.w #1,D0","asld.w #1,D1","asld.w #1,D2","asld.w #1,D3", + "asld.w #1,D4","asld.w #1,D5","asld.w #1,D6","asld.w #1,D7","lsld.w #1,D0","lsld.w #1,D1", + "lsld.w #1,D2","lsld.w #1,D3","lsld.w #1,D4","lsld.w #1,D5","lsld.w #1,D6","lsld.w #1,D7", + "roxld.w #1,D0","roxld.w #1,D1","roxld.w #1,D2","roxld.w #1,D3","roxld.w #1,D4","roxld.w #1,D5", + "roxld.w #1,D6","roxld.w #1,D7","rold.w #1,D0","rold.w #1,D1","rold.w #1,D2","rold.w #1,D3", + "rold.w #1,D4","rold.w #1,D5","rold.w #1,D6","rold.w #1,D7","asld.w D1,D0","asld.w D1,D1", + "asld.w D1,D2","asld.w D1,D3","asld.w D1,D4","asld.w D1,D5","asld.w D1,D6","asld.w D1,D7", + "lsld.w D1,D0","lsld.w D1,D1","lsld.w D1,D2","lsld.w D1,D3","lsld.w D1,D4","lsld.w D1,D5", + "lsld.w D1,D6","lsld.w D1,D7","roxld.w D1,D0","roxld.w D1,D1","roxld.w D1,D2","roxld.w D1,D3", + "roxld.w D1,D4","roxld.w D1,D5","roxld.w D1,D6","roxld.w D1,D7","rold.w D1,D0","rold.w D1,D1", + "rold.w D1,D2","rold.w D1,D3","rold.w D1,D4","rold.w D1,D5","rold.w D1,D6","rold.w D1,D7" }; + static const char *opATable038E[] = { "asld.l #1,D0","asld.l #1,D1","asld.l #1,D2","asld.l #1,D3", + "asld.l #1,D4","asld.l #1,D5","asld.l #1,D6","asld.l #1,D7","lsld.l #1,D0","lsld.l #1,D1", + "lsld.l #1,D2","lsld.l #1,D3","lsld.l #1,D4","lsld.l #1,D5","lsld.l #1,D6","lsld.l #1,D7", + "roxld.l #1,D0","roxld.l #1,D1","roxld.l #1,D2","roxld.l #1,D3","roxld.l #1,D4","roxld.l #1,D5", + "roxld.l #1,D6","roxld.l #1,D7","rold.l #1,D0","rold.l #1,D1","rold.l #1,D2","rold.l #1,D3", + "rold.l #1,D4","rold.l #1,D5","rold.l #1,D6","rold.l #1,D7","asld.l D1,D0","asld.l D1,D1", + "asld.l D1,D2","asld.l D1,D3","asld.l D1,D4","asld.l D1,D5","asld.l D1,D6","asld.l D1,D7", + "lsld.l D1,D0","lsld.l D1,D1","lsld.l D1,D2","lsld.l D1,D3","lsld.l D1,D4","lsld.l D1,D5", + "lsld.l D1,D6","lsld.l D1,D7","roxld.l D1,D0","roxld.l D1,D1","roxld.l D1,D2","roxld.l D1,D3", + "roxld.l D1,D4","roxld.l D1,D5","roxld.l D1,D6","roxld.l D1,D7","rold.l D1,D0","rold.l D1,D1", + "rold.l D1,D2","rold.l D1,D3","rold.l D1,D4","rold.l D1,D5","rold.l D1,D6","rold.l D1,D7" }; + static const char *opATable0390[] = { "asrd.b #2,D0","asrd.b #2,D1","asrd.b #2,D2","asrd.b #2,D3", + "asrd.b #2,D4","asrd.b #2,D5","asrd.b #2,D6","asrd.b #2,D7","lsrd.b #2,D0","lsrd.b #2,D1", + "lsrd.b #2,D2","lsrd.b #2,D3","lsrd.b #2,D4","lsrd.b #2,D5","lsrd.b #2,D6","lsrd.b #2,D7", + "roxrd.b #2,D0","roxrd.b #2,D1","roxrd.b #2,D2","roxrd.b #2,D3","roxrd.b #2,D4","roxrd.b #2,D5", + "roxrd.b #2,D6","roxrd.b #2,D7","rord.b #2,D0","rord.b #2,D1","rord.b #2,D2","rord.b #2,D3", + "rord.b #2,D4","rord.b #2,D5","rord.b #2,D6","rord.b #2,D7","asrd.b D2,D0","asrd.b D2,D1", + "asrd.b D2,D2","asrd.b D2,D3","asrd.b D2,D4","asrd.b D2,D5","asrd.b D2,D6","asrd.b D2,D7", + "lsrd.b D2,D0","lsrd.b D2,D1","lsrd.b D2,D2","lsrd.b D2,D3","lsrd.b D2,D4","lsrd.b D2,D5", + "lsrd.b D2,D6","lsrd.b D2,D7","roxrd.b D2,D0","roxrd.b D2,D1","roxrd.b D2,D2","roxrd.b D2,D3", + "roxrd.b D2,D4","roxrd.b D2,D5","roxrd.b D2,D6","roxrd.b D2,D7","rord.b D2,D0","rord.b D2,D1", + "rord.b D2,D2","rord.b D2,D3","rord.b D2,D4","rord.b D2,D5","rord.b D2,D6","rord.b D2,D7" }; + static const char *opATable0391[] = { "asrd.w #2,D0","asrd.w #2,D1","asrd.w #2,D2","asrd.w #2,D3", + "asrd.w #2,D4","asrd.w #2,D5","asrd.w #2,D6","asrd.w #2,D7","lsrd.w #2,D0","lsrd.w #2,D1", + "lsrd.w #2,D2","lsrd.w #2,D3","lsrd.w #2,D4","lsrd.w #2,D5","lsrd.w #2,D6","lsrd.w #2,D7", + "roxrd.w #2,D0","roxrd.w #2,D1","roxrd.w #2,D2","roxrd.w #2,D3","roxrd.w #2,D4","roxrd.w #2,D5", + "roxrd.w #2,D6","roxrd.w #2,D7","rord.w #2,D0","rord.w #2,D1","rord.w #2,D2","rord.w #2,D3", + "rord.w #2,D4","rord.w #2,D5","rord.w #2,D6","rord.w #2,D7","asrd.w D2,D0","asrd.w D2,D1", + "asrd.w D2,D2","asrd.w D2,D3","asrd.w D2,D4","asrd.w D2,D5","asrd.w D2,D6","asrd.w D2,D7", + "lsrd.w D2,D0","lsrd.w D2,D1","lsrd.w D2,D2","lsrd.w D2,D3","lsrd.w D2,D4","lsrd.w D2,D5", + "lsrd.w D2,D6","lsrd.w D2,D7","roxrd.w D2,D0","roxrd.w D2,D1","roxrd.w D2,D2","roxrd.w D2,D3", + "roxrd.w D2,D4","roxrd.w D2,D5","roxrd.w D2,D6","roxrd.w D2,D7","rord.w D2,D0","rord.w D2,D1", + "rord.w D2,D2","rord.w D2,D3","rord.w D2,D4","rord.w D2,D5","rord.w D2,D6","rord.w D2,D7" }; + static const char *opATable0392[] = { "asrd.l #2,D0","asrd.l #2,D1","asrd.l #2,D2","asrd.l #2,D3", + "asrd.l #2,D4","asrd.l #2,D5","asrd.l #2,D6","asrd.l #2,D7","lsrd.l #2,D0","lsrd.l #2,D1", + "lsrd.l #2,D2","lsrd.l #2,D3","lsrd.l #2,D4","lsrd.l #2,D5","lsrd.l #2,D6","lsrd.l #2,D7", + "roxrd.l #2,D0","roxrd.l #2,D1","roxrd.l #2,D2","roxrd.l #2,D3","roxrd.l #2,D4","roxrd.l #2,D5", + "roxrd.l #2,D6","roxrd.l #2,D7","rord.l #2,D0","rord.l #2,D1","rord.l #2,D2","rord.l #2,D3", + "rord.l #2,D4","rord.l #2,D5","rord.l #2,D6","rord.l #2,D7","asrd.l D2,D0","asrd.l D2,D1", + "asrd.l D2,D2","asrd.l D2,D3","asrd.l D2,D4","asrd.l D2,D5","asrd.l D2,D6","asrd.l D2,D7", + "lsrd.l D2,D0","lsrd.l D2,D1","lsrd.l D2,D2","lsrd.l D2,D3","lsrd.l D2,D4","lsrd.l D2,D5", + "lsrd.l D2,D6","lsrd.l D2,D7","roxrd.l D2,D0","roxrd.l D2,D1","roxrd.l D2,D2","roxrd.l D2,D3", + "roxrd.l D2,D4","roxrd.l D2,D5","roxrd.l D2,D6","roxrd.l D2,D7","rord.l D2,D0","rord.l D2,D1", + "rord.l D2,D2","rord.l D2,D3","rord.l D2,D4","rord.l D2,D5","rord.l D2,D6","rord.l D2,D7" }; + static const char *opATable0394[] = { "asld.b #2,D0","asld.b #2,D1","asld.b #2,D2","asld.b #2,D3", + "asld.b #2,D4","asld.b #2,D5","asld.b #2,D6","asld.b #2,D7","lsld.b #2,D0","lsld.b #2,D1", + "lsld.b #2,D2","lsld.b #2,D3","lsld.b #2,D4","lsld.b #2,D5","lsld.b #2,D6","lsld.b #2,D7", + "roxld.b #2,D0","roxld.b #2,D1","roxld.b #2,D2","roxld.b #2,D3","roxld.b #2,D4","roxld.b #2,D5", + "roxld.b #2,D6","roxld.b #2,D7","rold.b #2,D0","rold.b #2,D1","rold.b #2,D2","rold.b #2,D3", + "rold.b #2,D4","rold.b #2,D5","rold.b #2,D6","rold.b #2,D7","asld.b D2,D0","asld.b D2,D1", + "asld.b D2,D2","asld.b D2,D3","asld.b D2,D4","asld.b D2,D5","asld.b D2,D6","asld.b D2,D7", + "lsld.b D2,D0","lsld.b D2,D1","lsld.b D2,D2","lsld.b D2,D3","lsld.b D2,D4","lsld.b D2,D5", + "lsld.b D2,D6","lsld.b D2,D7","roxld.b D2,D0","roxld.b D2,D1","roxld.b D2,D2","roxld.b D2,D3", + "roxld.b D2,D4","roxld.b D2,D5","roxld.b D2,D6","roxld.b D2,D7","rold.b D2,D0","rold.b D2,D1", + "rold.b D2,D2","rold.b D2,D3","rold.b D2,D4","rold.b D2,D5","rold.b D2,D6","rold.b D2,D7" }; + static const char *opATable0395[] = { "asld.w #2,D0","asld.w #2,D1","asld.w #2,D2","asld.w #2,D3", + "asld.w #2,D4","asld.w #2,D5","asld.w #2,D6","asld.w #2,D7","lsld.w #2,D0","lsld.w #2,D1", + "lsld.w #2,D2","lsld.w #2,D3","lsld.w #2,D4","lsld.w #2,D5","lsld.w #2,D6","lsld.w #2,D7", + "roxld.w #2,D0","roxld.w #2,D1","roxld.w #2,D2","roxld.w #2,D3","roxld.w #2,D4","roxld.w #2,D5", + "roxld.w #2,D6","roxld.w #2,D7","rold.w #2,D0","rold.w #2,D1","rold.w #2,D2","rold.w #2,D3", + "rold.w #2,D4","rold.w #2,D5","rold.w #2,D6","rold.w #2,D7","asld.w D2,D0","asld.w D2,D1", + "asld.w D2,D2","asld.w D2,D3","asld.w D2,D4","asld.w D2,D5","asld.w D2,D6","asld.w D2,D7", + "lsld.w D2,D0","lsld.w D2,D1","lsld.w D2,D2","lsld.w D2,D3","lsld.w D2,D4","lsld.w D2,D5", + "lsld.w D2,D6","lsld.w D2,D7","roxld.w D2,D0","roxld.w D2,D1","roxld.w D2,D2","roxld.w D2,D3", + "roxld.w D2,D4","roxld.w D2,D5","roxld.w D2,D6","roxld.w D2,D7","rold.w D2,D0","rold.w D2,D1", + "rold.w D2,D2","rold.w D2,D3","rold.w D2,D4","rold.w D2,D5","rold.w D2,D6","rold.w D2,D7" }; + static const char *opATable0396[] = { "asld.l #2,D0","asld.l #2,D1","asld.l #2,D2","asld.l #2,D3", + "asld.l #2,D4","asld.l #2,D5","asld.l #2,D6","asld.l #2,D7","lsld.l #2,D0","lsld.l #2,D1", + "lsld.l #2,D2","lsld.l #2,D3","lsld.l #2,D4","lsld.l #2,D5","lsld.l #2,D6","lsld.l #2,D7", + "roxld.l #2,D0","roxld.l #2,D1","roxld.l #2,D2","roxld.l #2,D3","roxld.l #2,D4","roxld.l #2,D5", + "roxld.l #2,D6","roxld.l #2,D7","rold.l #2,D0","rold.l #2,D1","rold.l #2,D2","rold.l #2,D3", + "rold.l #2,D4","rold.l #2,D5","rold.l #2,D6","rold.l #2,D7","asld.l D2,D0","asld.l D2,D1", + "asld.l D2,D2","asld.l D2,D3","asld.l D2,D4","asld.l D2,D5","asld.l D2,D6","asld.l D2,D7", + "lsld.l D2,D0","lsld.l D2,D1","lsld.l D2,D2","lsld.l D2,D3","lsld.l D2,D4","lsld.l D2,D5", + "lsld.l D2,D6","lsld.l D2,D7","roxld.l D2,D0","roxld.l D2,D1","roxld.l D2,D2","roxld.l D2,D3", + "roxld.l D2,D4","roxld.l D2,D5","roxld.l D2,D6","roxld.l D2,D7","rold.l D2,D0","rold.l D2,D1", + "rold.l D2,D2","rold.l D2,D3","rold.l D2,D4","rold.l D2,D5","rold.l D2,D6","rold.l D2,D7" }; + static const char *opATable0398[] = { "asrd.b #3,D0","asrd.b #3,D1","asrd.b #3,D2","asrd.b #3,D3", + "asrd.b #3,D4","asrd.b #3,D5","asrd.b #3,D6","asrd.b #3,D7","lsrd.b #3,D0","lsrd.b #3,D1", + "lsrd.b #3,D2","lsrd.b #3,D3","lsrd.b #3,D4","lsrd.b #3,D5","lsrd.b #3,D6","lsrd.b #3,D7", + "roxrd.b #3,D0","roxrd.b #3,D1","roxrd.b #3,D2","roxrd.b #3,D3","roxrd.b #3,D4","roxrd.b #3,D5", + "roxrd.b #3,D6","roxrd.b #3,D7","rord.b #3,D0","rord.b #3,D1","rord.b #3,D2","rord.b #3,D3", + "rord.b #3,D4","rord.b #3,D5","rord.b #3,D6","rord.b #3,D7","asrd.b D3,D0","asrd.b D3,D1", + "asrd.b D3,D2","asrd.b D3,D3","asrd.b D3,D4","asrd.b D3,D5","asrd.b D3,D6","asrd.b D3,D7", + "lsrd.b D3,D0","lsrd.b D3,D1","lsrd.b D3,D2","lsrd.b D3,D3","lsrd.b D3,D4","lsrd.b D3,D5", + "lsrd.b D3,D6","lsrd.b D3,D7","roxrd.b D3,D0","roxrd.b D3,D1","roxrd.b D3,D2","roxrd.b D3,D3", + "roxrd.b D3,D4","roxrd.b D3,D5","roxrd.b D3,D6","roxrd.b D3,D7","rord.b D3,D0","rord.b D3,D1", + "rord.b D3,D2","rord.b D3,D3","rord.b D3,D4","rord.b D3,D5","rord.b D3,D6","rord.b D3,D7" }; + static const char *opATable0399[] = { "asrd.w #3,D0","asrd.w #3,D1","asrd.w #3,D2","asrd.w #3,D3", + "asrd.w #3,D4","asrd.w #3,D5","asrd.w #3,D6","asrd.w #3,D7","lsrd.w #3,D0","lsrd.w #3,D1", + "lsrd.w #3,D2","lsrd.w #3,D3","lsrd.w #3,D4","lsrd.w #3,D5","lsrd.w #3,D6","lsrd.w #3,D7", + "roxrd.w #3,D0","roxrd.w #3,D1","roxrd.w #3,D2","roxrd.w #3,D3","roxrd.w #3,D4","roxrd.w #3,D5", + "roxrd.w #3,D6","roxrd.w #3,D7","rord.w #3,D0","rord.w #3,D1","rord.w #3,D2","rord.w #3,D3", + "rord.w #3,D4","rord.w #3,D5","rord.w #3,D6","rord.w #3,D7","asrd.w D3,D0","asrd.w D3,D1", + "asrd.w D3,D2","asrd.w D3,D3","asrd.w D3,D4","asrd.w D3,D5","asrd.w D3,D6","asrd.w D3,D7", + "lsrd.w D3,D0","lsrd.w D3,D1","lsrd.w D3,D2","lsrd.w D3,D3","lsrd.w D3,D4","lsrd.w D3,D5", + "lsrd.w D3,D6","lsrd.w D3,D7","roxrd.w D3,D0","roxrd.w D3,D1","roxrd.w D3,D2","roxrd.w D3,D3", + "roxrd.w D3,D4","roxrd.w D3,D5","roxrd.w D3,D6","roxrd.w D3,D7","rord.w D3,D0","rord.w D3,D1", + "rord.w D3,D2","rord.w D3,D3","rord.w D3,D4","rord.w D3,D5","rord.w D3,D6","rord.w D3,D7" }; + static const char *opATable039A[] = { "asrd.l #3,D0","asrd.l #3,D1","asrd.l #3,D2","asrd.l #3,D3", + "asrd.l #3,D4","asrd.l #3,D5","asrd.l #3,D6","asrd.l #3,D7","lsrd.l #3,D0","lsrd.l #3,D1", + "lsrd.l #3,D2","lsrd.l #3,D3","lsrd.l #3,D4","lsrd.l #3,D5","lsrd.l #3,D6","lsrd.l #3,D7", + "roxrd.l #3,D0","roxrd.l #3,D1","roxrd.l #3,D2","roxrd.l #3,D3","roxrd.l #3,D4","roxrd.l #3,D5", + "roxrd.l #3,D6","roxrd.l #3,D7","rord.l #3,D0","rord.l #3,D1","rord.l #3,D2","rord.l #3,D3", + "rord.l #3,D4","rord.l #3,D5","rord.l #3,D6","rord.l #3,D7","asrd.l D3,D0","asrd.l D3,D1", + "asrd.l D3,D2","asrd.l D3,D3","asrd.l D3,D4","asrd.l D3,D5","asrd.l D3,D6","asrd.l D3,D7", + "lsrd.l D3,D0","lsrd.l D3,D1","lsrd.l D3,D2","lsrd.l D3,D3","lsrd.l D3,D4","lsrd.l D3,D5", + "lsrd.l D3,D6","lsrd.l D3,D7","roxrd.l D3,D0","roxrd.l D3,D1","roxrd.l D3,D2","roxrd.l D3,D3", + "roxrd.l D3,D4","roxrd.l D3,D5","roxrd.l D3,D6","roxrd.l D3,D7","rord.l D3,D0","rord.l D3,D1", + "rord.l D3,D2","rord.l D3,D3","rord.l D3,D4","rord.l D3,D5","rord.l D3,D6","rord.l D3,D7" }; + static const char *opATable039C[] = { "asld.b #3,D0","asld.b #3,D1","asld.b #3,D2","asld.b #3,D3", + "asld.b #3,D4","asld.b #3,D5","asld.b #3,D6","asld.b #3,D7","lsld.b #3,D0","lsld.b #3,D1", + "lsld.b #3,D2","lsld.b #3,D3","lsld.b #3,D4","lsld.b #3,D5","lsld.b #3,D6","lsld.b #3,D7", + "roxld.b #3,D0","roxld.b #3,D1","roxld.b #3,D2","roxld.b #3,D3","roxld.b #3,D4","roxld.b #3,D5", + "roxld.b #3,D6","roxld.b #3,D7","rold.b #3,D0","rold.b #3,D1","rold.b #3,D2","rold.b #3,D3", + "rold.b #3,D4","rold.b #3,D5","rold.b #3,D6","rold.b #3,D7","asld.b D3,D0","asld.b D3,D1", + "asld.b D3,D2","asld.b D3,D3","asld.b D3,D4","asld.b D3,D5","asld.b D3,D6","asld.b D3,D7", + "lsld.b D3,D0","lsld.b D3,D1","lsld.b D3,D2","lsld.b D3,D3","lsld.b D3,D4","lsld.b D3,D5", + "lsld.b D3,D6","lsld.b D3,D7","roxld.b D3,D0","roxld.b D3,D1","roxld.b D3,D2","roxld.b D3,D3", + "roxld.b D3,D4","roxld.b D3,D5","roxld.b D3,D6","roxld.b D3,D7","rold.b D3,D0","rold.b D3,D1", + "rold.b D3,D2","rold.b D3,D3","rold.b D3,D4","rold.b D3,D5","rold.b D3,D6","rold.b D3,D7" }; + static const char *opATable039D[] = { "asld.w #3,D0","asld.w #3,D1","asld.w #3,D2","asld.w #3,D3", + "asld.w #3,D4","asld.w #3,D5","asld.w #3,D6","asld.w #3,D7","lsld.w #3,D0","lsld.w #3,D1", + "lsld.w #3,D2","lsld.w #3,D3","lsld.w #3,D4","lsld.w #3,D5","lsld.w #3,D6","lsld.w #3,D7", + "roxld.w #3,D0","roxld.w #3,D1","roxld.w #3,D2","roxld.w #3,D3","roxld.w #3,D4","roxld.w #3,D5", + "roxld.w #3,D6","roxld.w #3,D7","rold.w #3,D0","rold.w #3,D1","rold.w #3,D2","rold.w #3,D3", + "rold.w #3,D4","rold.w #3,D5","rold.w #3,D6","rold.w #3,D7","asld.w D3,D0","asld.w D3,D1", + "asld.w D3,D2","asld.w D3,D3","asld.w D3,D4","asld.w D3,D5","asld.w D3,D6","asld.w D3,D7", + "lsld.w D3,D0","lsld.w D3,D1","lsld.w D3,D2","lsld.w D3,D3","lsld.w D3,D4","lsld.w D3,D5", + "lsld.w D3,D6","lsld.w D3,D7","roxld.w D3,D0","roxld.w D3,D1","roxld.w D3,D2","roxld.w D3,D3", + "roxld.w D3,D4","roxld.w D3,D5","roxld.w D3,D6","roxld.w D3,D7","rold.w D3,D0","rold.w D3,D1", + "rold.w D3,D2","rold.w D3,D3","rold.w D3,D4","rold.w D3,D5","rold.w D3,D6","rold.w D3,D7" }; + static const char *opATable039E[] = { "asld.l #3,D0","asld.l #3,D1","asld.l #3,D2","asld.l #3,D3", + "asld.l #3,D4","asld.l #3,D5","asld.l #3,D6","asld.l #3,D7","lsld.l #3,D0","lsld.l #3,D1", + "lsld.l #3,D2","lsld.l #3,D3","lsld.l #3,D4","lsld.l #3,D5","lsld.l #3,D6","lsld.l #3,D7", + "roxld.l #3,D0","roxld.l #3,D1","roxld.l #3,D2","roxld.l #3,D3","roxld.l #3,D4","roxld.l #3,D5", + "roxld.l #3,D6","roxld.l #3,D7","rold.l #3,D0","rold.l #3,D1","rold.l #3,D2","rold.l #3,D3", + "rold.l #3,D4","rold.l #3,D5","rold.l #3,D6","rold.l #3,D7","asld.l D3,D0","asld.l D3,D1", + "asld.l D3,D2","asld.l D3,D3","asld.l D3,D4","asld.l D3,D5","asld.l D3,D6","asld.l D3,D7", + "lsld.l D3,D0","lsld.l D3,D1","lsld.l D3,D2","lsld.l D3,D3","lsld.l D3,D4","lsld.l D3,D5", + "lsld.l D3,D6","lsld.l D3,D7","roxld.l D3,D0","roxld.l D3,D1","roxld.l D3,D2","roxld.l D3,D3", + "roxld.l D3,D4","roxld.l D3,D5","roxld.l D3,D6","roxld.l D3,D7","rold.l D3,D0","rold.l D3,D1", + "rold.l D3,D2","rold.l D3,D3","rold.l D3,D4","rold.l D3,D5","rold.l D3,D6","rold.l D3,D7" }; + static const char *opATable03A0[] = { "asrd.b #4,D0","asrd.b #4,D1","asrd.b #4,D2","asrd.b #4,D3", + "asrd.b #4,D4","asrd.b #4,D5","asrd.b #4,D6","asrd.b #4,D7","lsrd.b #4,D0","lsrd.b #4,D1", + "lsrd.b #4,D2","lsrd.b #4,D3","lsrd.b #4,D4","lsrd.b #4,D5","lsrd.b #4,D6","lsrd.b #4,D7", + "roxrd.b #4,D0","roxrd.b #4,D1","roxrd.b #4,D2","roxrd.b #4,D3","roxrd.b #4,D4","roxrd.b #4,D5", + "roxrd.b #4,D6","roxrd.b #4,D7","rord.b #4,D0","rord.b #4,D1","rord.b #4,D2","rord.b #4,D3", + "rord.b #4,D4","rord.b #4,D5","rord.b #4,D6","rord.b #4,D7","asrd.b D4,D0","asrd.b D4,D1", + "asrd.b D4,D2","asrd.b D4,D3","asrd.b D4,D4","asrd.b D4,D5","asrd.b D4,D6","asrd.b D4,D7", + "lsrd.b D4,D0","lsrd.b D4,D1","lsrd.b D4,D2","lsrd.b D4,D3","lsrd.b D4,D4","lsrd.b D4,D5", + "lsrd.b D4,D6","lsrd.b D4,D7","roxrd.b D4,D0","roxrd.b D4,D1","roxrd.b D4,D2","roxrd.b D4,D3", + "roxrd.b D4,D4","roxrd.b D4,D5","roxrd.b D4,D6","roxrd.b D4,D7","rord.b D4,D0","rord.b D4,D1", + "rord.b D4,D2","rord.b D4,D3","rord.b D4,D4","rord.b D4,D5","rord.b D4,D6","rord.b D4,D7" }; + static const char *opATable03A1[] = { "asrd.w #4,D0","asrd.w #4,D1","asrd.w #4,D2","asrd.w #4,D3", + "asrd.w #4,D4","asrd.w #4,D5","asrd.w #4,D6","asrd.w #4,D7","lsrd.w #4,D0","lsrd.w #4,D1", + "lsrd.w #4,D2","lsrd.w #4,D3","lsrd.w #4,D4","lsrd.w #4,D5","lsrd.w #4,D6","lsrd.w #4,D7", + "roxrd.w #4,D0","roxrd.w #4,D1","roxrd.w #4,D2","roxrd.w #4,D3","roxrd.w #4,D4","roxrd.w #4,D5", + "roxrd.w #4,D6","roxrd.w #4,D7","rord.w #4,D0","rord.w #4,D1","rord.w #4,D2","rord.w #4,D3", + "rord.w #4,D4","rord.w #4,D5","rord.w #4,D6","rord.w #4,D7","asrd.w D4,D0","asrd.w D4,D1", + "asrd.w D4,D2","asrd.w D4,D3","asrd.w D4,D4","asrd.w D4,D5","asrd.w D4,D6","asrd.w D4,D7", + "lsrd.w D4,D0","lsrd.w D4,D1","lsrd.w D4,D2","lsrd.w D4,D3","lsrd.w D4,D4","lsrd.w D4,D5", + "lsrd.w D4,D6","lsrd.w D4,D7","roxrd.w D4,D0","roxrd.w D4,D1","roxrd.w D4,D2","roxrd.w D4,D3", + "roxrd.w D4,D4","roxrd.w D4,D5","roxrd.w D4,D6","roxrd.w D4,D7","rord.w D4,D0","rord.w D4,D1", + "rord.w D4,D2","rord.w D4,D3","rord.w D4,D4","rord.w D4,D5","rord.w D4,D6","rord.w D4,D7" }; + static const char *opATable03A2[] = { "asrd.l #4,D0","asrd.l #4,D1","asrd.l #4,D2","asrd.l #4,D3", + "asrd.l #4,D4","asrd.l #4,D5","asrd.l #4,D6","asrd.l #4,D7","lsrd.l #4,D0","lsrd.l #4,D1", + "lsrd.l #4,D2","lsrd.l #4,D3","lsrd.l #4,D4","lsrd.l #4,D5","lsrd.l #4,D6","lsrd.l #4,D7", + "roxrd.l #4,D0","roxrd.l #4,D1","roxrd.l #4,D2","roxrd.l #4,D3","roxrd.l #4,D4","roxrd.l #4,D5", + "roxrd.l #4,D6","roxrd.l #4,D7","rord.l #4,D0","rord.l #4,D1","rord.l #4,D2","rord.l #4,D3", + "rord.l #4,D4","rord.l #4,D5","rord.l #4,D6","rord.l #4,D7","asrd.l D4,D0","asrd.l D4,D1", + "asrd.l D4,D2","asrd.l D4,D3","asrd.l D4,D4","asrd.l D4,D5","asrd.l D4,D6","asrd.l D4,D7", + "lsrd.l D4,D0","lsrd.l D4,D1","lsrd.l D4,D2","lsrd.l D4,D3","lsrd.l D4,D4","lsrd.l D4,D5", + "lsrd.l D4,D6","lsrd.l D4,D7","roxrd.l D4,D0","roxrd.l D4,D1","roxrd.l D4,D2","roxrd.l D4,D3", + "roxrd.l D4,D4","roxrd.l D4,D5","roxrd.l D4,D6","roxrd.l D4,D7","rord.l D4,D0","rord.l D4,D1", + "rord.l D4,D2","rord.l D4,D3","rord.l D4,D4","rord.l D4,D5","rord.l D4,D6","rord.l D4,D7" }; + static const char *opATable03A4[] = { "asld.b #4,D0","asld.b #4,D1","asld.b #4,D2","asld.b #4,D3", + "asld.b #4,D4","asld.b #4,D5","asld.b #4,D6","asld.b #4,D7","lsld.b #4,D0","lsld.b #4,D1", + "lsld.b #4,D2","lsld.b #4,D3","lsld.b #4,D4","lsld.b #4,D5","lsld.b #4,D6","lsld.b #4,D7", + "roxld.b #4,D0","roxld.b #4,D1","roxld.b #4,D2","roxld.b #4,D3","roxld.b #4,D4","roxld.b #4,D5", + "roxld.b #4,D6","roxld.b #4,D7","rold.b #4,D0","rold.b #4,D1","rold.b #4,D2","rold.b #4,D3", + "rold.b #4,D4","rold.b #4,D5","rold.b #4,D6","rold.b #4,D7","asld.b D4,D0","asld.b D4,D1", + "asld.b D4,D2","asld.b D4,D3","asld.b D4,D4","asld.b D4,D5","asld.b D4,D6","asld.b D4,D7", + "lsld.b D4,D0","lsld.b D4,D1","lsld.b D4,D2","lsld.b D4,D3","lsld.b D4,D4","lsld.b D4,D5", + "lsld.b D4,D6","lsld.b D4,D7","roxld.b D4,D0","roxld.b D4,D1","roxld.b D4,D2","roxld.b D4,D3", + "roxld.b D4,D4","roxld.b D4,D5","roxld.b D4,D6","roxld.b D4,D7","rold.b D4,D0","rold.b D4,D1", + "rold.b D4,D2","rold.b D4,D3","rold.b D4,D4","rold.b D4,D5","rold.b D4,D6","rold.b D4,D7" }; + static const char *opATable03A5[] = { "asld.w #4,D0","asld.w #4,D1","asld.w #4,D2","asld.w #4,D3", + "asld.w #4,D4","asld.w #4,D5","asld.w #4,D6","asld.w #4,D7","lsld.w #4,D0","lsld.w #4,D1", + "lsld.w #4,D2","lsld.w #4,D3","lsld.w #4,D4","lsld.w #4,D5","lsld.w #4,D6","lsld.w #4,D7", + "roxld.w #4,D0","roxld.w #4,D1","roxld.w #4,D2","roxld.w #4,D3","roxld.w #4,D4","roxld.w #4,D5", + "roxld.w #4,D6","roxld.w #4,D7","rold.w #4,D0","rold.w #4,D1","rold.w #4,D2","rold.w #4,D3", + "rold.w #4,D4","rold.w #4,D5","rold.w #4,D6","rold.w #4,D7","asld.w D4,D0","asld.w D4,D1", + "asld.w D4,D2","asld.w D4,D3","asld.w D4,D4","asld.w D4,D5","asld.w D4,D6","asld.w D4,D7", + "lsld.w D4,D0","lsld.w D4,D1","lsld.w D4,D2","lsld.w D4,D3","lsld.w D4,D4","lsld.w D4,D5", + "lsld.w D4,D6","lsld.w D4,D7","roxld.w D4,D0","roxld.w D4,D1","roxld.w D4,D2","roxld.w D4,D3", + "roxld.w D4,D4","roxld.w D4,D5","roxld.w D4,D6","roxld.w D4,D7","rold.w D4,D0","rold.w D4,D1", + "rold.w D4,D2","rold.w D4,D3","rold.w D4,D4","rold.w D4,D5","rold.w D4,D6","rold.w D4,D7" }; + static const char *opATable03A6[] = { "asld.l #4,D0","asld.l #4,D1","asld.l #4,D2","asld.l #4,D3", + "asld.l #4,D4","asld.l #4,D5","asld.l #4,D6","asld.l #4,D7","lsld.l #4,D0","lsld.l #4,D1", + "lsld.l #4,D2","lsld.l #4,D3","lsld.l #4,D4","lsld.l #4,D5","lsld.l #4,D6","lsld.l #4,D7", + "roxld.l #4,D0","roxld.l #4,D1","roxld.l #4,D2","roxld.l #4,D3","roxld.l #4,D4","roxld.l #4,D5", + "roxld.l #4,D6","roxld.l #4,D7","rold.l #4,D0","rold.l #4,D1","rold.l #4,D2","rold.l #4,D3", + "rold.l #4,D4","rold.l #4,D5","rold.l #4,D6","rold.l #4,D7","asld.l D4,D0","asld.l D4,D1", + "asld.l D4,D2","asld.l D4,D3","asld.l D4,D4","asld.l D4,D5","asld.l D4,D6","asld.l D4,D7", + "lsld.l D4,D0","lsld.l D4,D1","lsld.l D4,D2","lsld.l D4,D3","lsld.l D4,D4","lsld.l D4,D5", + "lsld.l D4,D6","lsld.l D4,D7","roxld.l D4,D0","roxld.l D4,D1","roxld.l D4,D2","roxld.l D4,D3", + "roxld.l D4,D4","roxld.l D4,D5","roxld.l D4,D6","roxld.l D4,D7","rold.l D4,D0","rold.l D4,D1", + "rold.l D4,D2","rold.l D4,D3","rold.l D4,D4","rold.l D4,D5","rold.l D4,D6","rold.l D4,D7" }; + static const char *opATable03A8[] = { "asrd.b #5,D0","asrd.b #5,D1","asrd.b #5,D2","asrd.b #5,D3", + "asrd.b #5,D4","asrd.b #5,D5","asrd.b #5,D6","asrd.b #5,D7","lsrd.b #5,D0","lsrd.b #5,D1", + "lsrd.b #5,D2","lsrd.b #5,D3","lsrd.b #5,D4","lsrd.b #5,D5","lsrd.b #5,D6","lsrd.b #5,D7", + "roxrd.b #5,D0","roxrd.b #5,D1","roxrd.b #5,D2","roxrd.b #5,D3","roxrd.b #5,D4","roxrd.b #5,D5", + "roxrd.b #5,D6","roxrd.b #5,D7","rord.b #5,D0","rord.b #5,D1","rord.b #5,D2","rord.b #5,D3", + "rord.b #5,D4","rord.b #5,D5","rord.b #5,D6","rord.b #5,D7","asrd.b D5,D0","asrd.b D5,D1", + "asrd.b D5,D2","asrd.b D5,D3","asrd.b D5,D4","asrd.b D5,D5","asrd.b D5,D6","asrd.b D5,D7", + "lsrd.b D5,D0","lsrd.b D5,D1","lsrd.b D5,D2","lsrd.b D5,D3","lsrd.b D5,D4","lsrd.b D5,D5", + "lsrd.b D5,D6","lsrd.b D5,D7","roxrd.b D5,D0","roxrd.b D5,D1","roxrd.b D5,D2","roxrd.b D5,D3", + "roxrd.b D5,D4","roxrd.b D5,D5","roxrd.b D5,D6","roxrd.b D5,D7","rord.b D5,D0","rord.b D5,D1", + "rord.b D5,D2","rord.b D5,D3","rord.b D5,D4","rord.b D5,D5","rord.b D5,D6","rord.b D5,D7" }; + static const char *opATable03A9[] = { "asrd.w #5,D0","asrd.w #5,D1","asrd.w #5,D2","asrd.w #5,D3", + "asrd.w #5,D4","asrd.w #5,D5","asrd.w #5,D6","asrd.w #5,D7","lsrd.w #5,D0","lsrd.w #5,D1", + "lsrd.w #5,D2","lsrd.w #5,D3","lsrd.w #5,D4","lsrd.w #5,D5","lsrd.w #5,D6","lsrd.w #5,D7", + "roxrd.w #5,D0","roxrd.w #5,D1","roxrd.w #5,D2","roxrd.w #5,D3","roxrd.w #5,D4","roxrd.w #5,D5", + "roxrd.w #5,D6","roxrd.w #5,D7","rord.w #5,D0","rord.w #5,D1","rord.w #5,D2","rord.w #5,D3", + "rord.w #5,D4","rord.w #5,D5","rord.w #5,D6","rord.w #5,D7","asrd.w D5,D0","asrd.w D5,D1", + "asrd.w D5,D2","asrd.w D5,D3","asrd.w D5,D4","asrd.w D5,D5","asrd.w D5,D6","asrd.w D5,D7", + "lsrd.w D5,D0","lsrd.w D5,D1","lsrd.w D5,D2","lsrd.w D5,D3","lsrd.w D5,D4","lsrd.w D5,D5", + "lsrd.w D5,D6","lsrd.w D5,D7","roxrd.w D5,D0","roxrd.w D5,D1","roxrd.w D5,D2","roxrd.w D5,D3", + "roxrd.w D5,D4","roxrd.w D5,D5","roxrd.w D5,D6","roxrd.w D5,D7","rord.w D5,D0","rord.w D5,D1", + "rord.w D5,D2","rord.w D5,D3","rord.w D5,D4","rord.w D5,D5","rord.w D5,D6","rord.w D5,D7" }; + static const char *opATable03AA[] = { "asrd.l #5,D0","asrd.l #5,D1","asrd.l #5,D2","asrd.l #5,D3", + "asrd.l #5,D4","asrd.l #5,D5","asrd.l #5,D6","asrd.l #5,D7","lsrd.l #5,D0","lsrd.l #5,D1", + "lsrd.l #5,D2","lsrd.l #5,D3","lsrd.l #5,D4","lsrd.l #5,D5","lsrd.l #5,D6","lsrd.l #5,D7", + "roxrd.l #5,D0","roxrd.l #5,D1","roxrd.l #5,D2","roxrd.l #5,D3","roxrd.l #5,D4","roxrd.l #5,D5", + "roxrd.l #5,D6","roxrd.l #5,D7","rord.l #5,D0","rord.l #5,D1","rord.l #5,D2","rord.l #5,D3", + "rord.l #5,D4","rord.l #5,D5","rord.l #5,D6","rord.l #5,D7","asrd.l D5,D0","asrd.l D5,D1", + "asrd.l D5,D2","asrd.l D5,D3","asrd.l D5,D4","asrd.l D5,D5","asrd.l D5,D6","asrd.l D5,D7", + "lsrd.l D5,D0","lsrd.l D5,D1","lsrd.l D5,D2","lsrd.l D5,D3","lsrd.l D5,D4","lsrd.l D5,D5", + "lsrd.l D5,D6","lsrd.l D5,D7","roxrd.l D5,D0","roxrd.l D5,D1","roxrd.l D5,D2","roxrd.l D5,D3", + "roxrd.l D5,D4","roxrd.l D5,D5","roxrd.l D5,D6","roxrd.l D5,D7","rord.l D5,D0","rord.l D5,D1", + "rord.l D5,D2","rord.l D5,D3","rord.l D5,D4","rord.l D5,D5","rord.l D5,D6","rord.l D5,D7" }; + static const char *opATable03AC[] = { "asld.b #5,D0","asld.b #5,D1","asld.b #5,D2","asld.b #5,D3", + "asld.b #5,D4","asld.b #5,D5","asld.b #5,D6","asld.b #5,D7","lsld.b #5,D0","lsld.b #5,D1", + "lsld.b #5,D2","lsld.b #5,D3","lsld.b #5,D4","lsld.b #5,D5","lsld.b #5,D6","lsld.b #5,D7", + "roxld.b #5,D0","roxld.b #5,D1","roxld.b #5,D2","roxld.b #5,D3","roxld.b #5,D4","roxld.b #5,D5", + "roxld.b #5,D6","roxld.b #5,D7","rold.b #5,D0","rold.b #5,D1","rold.b #5,D2","rold.b #5,D3", + "rold.b #5,D4","rold.b #5,D5","rold.b #5,D6","rold.b #5,D7","asld.b D5,D0","asld.b D5,D1", + "asld.b D5,D2","asld.b D5,D3","asld.b D5,D4","asld.b D5,D5","asld.b D5,D6","asld.b D5,D7", + "lsld.b D5,D0","lsld.b D5,D1","lsld.b D5,D2","lsld.b D5,D3","lsld.b D5,D4","lsld.b D5,D5", + "lsld.b D5,D6","lsld.b D5,D7","roxld.b D5,D0","roxld.b D5,D1","roxld.b D5,D2","roxld.b D5,D3", + "roxld.b D5,D4","roxld.b D5,D5","roxld.b D5,D6","roxld.b D5,D7","rold.b D5,D0","rold.b D5,D1", + "rold.b D5,D2","rold.b D5,D3","rold.b D5,D4","rold.b D5,D5","rold.b D5,D6","rold.b D5,D7" }; + static const char *opATable03AD[] = { "asld.w #5,D0","asld.w #5,D1","asld.w #5,D2","asld.w #5,D3", + "asld.w #5,D4","asld.w #5,D5","asld.w #5,D6","asld.w #5,D7","lsld.w #5,D0","lsld.w #5,D1", + "lsld.w #5,D2","lsld.w #5,D3","lsld.w #5,D4","lsld.w #5,D5","lsld.w #5,D6","lsld.w #5,D7", + "roxld.w #5,D0","roxld.w #5,D1","roxld.w #5,D2","roxld.w #5,D3","roxld.w #5,D4","roxld.w #5,D5", + "roxld.w #5,D6","roxld.w #5,D7","rold.w #5,D0","rold.w #5,D1","rold.w #5,D2","rold.w #5,D3", + "rold.w #5,D4","rold.w #5,D5","rold.w #5,D6","rold.w #5,D7","asld.w D5,D0","asld.w D5,D1", + "asld.w D5,D2","asld.w D5,D3","asld.w D5,D4","asld.w D5,D5","asld.w D5,D6","asld.w D5,D7", + "lsld.w D5,D0","lsld.w D5,D1","lsld.w D5,D2","lsld.w D5,D3","lsld.w D5,D4","lsld.w D5,D5", + "lsld.w D5,D6","lsld.w D5,D7","roxld.w D5,D0","roxld.w D5,D1","roxld.w D5,D2","roxld.w D5,D3", + "roxld.w D5,D4","roxld.w D5,D5","roxld.w D5,D6","roxld.w D5,D7","rold.w D5,D0","rold.w D5,D1", + "rold.w D5,D2","rold.w D5,D3","rold.w D5,D4","rold.w D5,D5","rold.w D5,D6","rold.w D5,D7" }; + static const char *opATable03AE[] = { "asld.l #5,D0","asld.l #5,D1","asld.l #5,D2","asld.l #5,D3", + "asld.l #5,D4","asld.l #5,D5","asld.l #5,D6","asld.l #5,D7","lsld.l #5,D0","lsld.l #5,D1", + "lsld.l #5,D2","lsld.l #5,D3","lsld.l #5,D4","lsld.l #5,D5","lsld.l #5,D6","lsld.l #5,D7", + "roxld.l #5,D0","roxld.l #5,D1","roxld.l #5,D2","roxld.l #5,D3","roxld.l #5,D4","roxld.l #5,D5", + "roxld.l #5,D6","roxld.l #5,D7","rold.l #5,D0","rold.l #5,D1","rold.l #5,D2","rold.l #5,D3", + "rold.l #5,D4","rold.l #5,D5","rold.l #5,D6","rold.l #5,D7","asld.l D5,D0","asld.l D5,D1", + "asld.l D5,D2","asld.l D5,D3","asld.l D5,D4","asld.l D5,D5","asld.l D5,D6","asld.l D5,D7", + "lsld.l D5,D0","lsld.l D5,D1","lsld.l D5,D2","lsld.l D5,D3","lsld.l D5,D4","lsld.l D5,D5", + "lsld.l D5,D6","lsld.l D5,D7","roxld.l D5,D0","roxld.l D5,D1","roxld.l D5,D2","roxld.l D5,D3", + "roxld.l D5,D4","roxld.l D5,D5","roxld.l D5,D6","roxld.l D5,D7","rold.l D5,D0","rold.l D5,D1", + "rold.l D5,D2","rold.l D5,D3","rold.l D5,D4","rold.l D5,D5","rold.l D5,D6","rold.l D5,D7" }; + static const char *opATable03B0[] = { "asrd.b #6,D0","asrd.b #6,D1","asrd.b #6,D2","asrd.b #6,D3", + "asrd.b #6,D4","asrd.b #6,D5","asrd.b #6,D6","asrd.b #6,D7","lsrd.b #6,D0","lsrd.b #6,D1", + "lsrd.b #6,D2","lsrd.b #6,D3","lsrd.b #6,D4","lsrd.b #6,D5","lsrd.b #6,D6","lsrd.b #6,D7", + "roxrd.b #6,D0","roxrd.b #6,D1","roxrd.b #6,D2","roxrd.b #6,D3","roxrd.b #6,D4","roxrd.b #6,D5", + "roxrd.b #6,D6","roxrd.b #6,D7","rord.b #6,D0","rord.b #6,D1","rord.b #6,D2","rord.b #6,D3", + "rord.b #6,D4","rord.b #6,D5","rord.b #6,D6","rord.b #6,D7","asrd.b D6,D0","asrd.b D6,D1", + "asrd.b D6,D2","asrd.b D6,D3","asrd.b D6,D4","asrd.b D6,D5","asrd.b D6,D6","asrd.b D6,D7", + "lsrd.b D6,D0","lsrd.b D6,D1","lsrd.b D6,D2","lsrd.b D6,D3","lsrd.b D6,D4","lsrd.b D6,D5", + "lsrd.b D6,D6","lsrd.b D6,D7","roxrd.b D6,D0","roxrd.b D6,D1","roxrd.b D6,D2","roxrd.b D6,D3", + "roxrd.b D6,D4","roxrd.b D6,D5","roxrd.b D6,D6","roxrd.b D6,D7","rord.b D6,D0","rord.b D6,D1", + "rord.b D6,D2","rord.b D6,D3","rord.b D6,D4","rord.b D6,D5","rord.b D6,D6","rord.b D6,D7" }; + static const char *opATable03B1[] = { "asrd.w #6,D0","asrd.w #6,D1","asrd.w #6,D2","asrd.w #6,D3", + "asrd.w #6,D4","asrd.w #6,D5","asrd.w #6,D6","asrd.w #6,D7","lsrd.w #6,D0","lsrd.w #6,D1", + "lsrd.w #6,D2","lsrd.w #6,D3","lsrd.w #6,D4","lsrd.w #6,D5","lsrd.w #6,D6","lsrd.w #6,D7", + "roxrd.w #6,D0","roxrd.w #6,D1","roxrd.w #6,D2","roxrd.w #6,D3","roxrd.w #6,D4","roxrd.w #6,D5", + "roxrd.w #6,D6","roxrd.w #6,D7","rord.w #6,D0","rord.w #6,D1","rord.w #6,D2","rord.w #6,D3", + "rord.w #6,D4","rord.w #6,D5","rord.w #6,D6","rord.w #6,D7","asrd.w D6,D0","asrd.w D6,D1", + "asrd.w D6,D2","asrd.w D6,D3","asrd.w D6,D4","asrd.w D6,D5","asrd.w D6,D6","asrd.w D6,D7", + "lsrd.w D6,D0","lsrd.w D6,D1","lsrd.w D6,D2","lsrd.w D6,D3","lsrd.w D6,D4","lsrd.w D6,D5", + "lsrd.w D6,D6","lsrd.w D6,D7","roxrd.w D6,D0","roxrd.w D6,D1","roxrd.w D6,D2","roxrd.w D6,D3", + "roxrd.w D6,D4","roxrd.w D6,D5","roxrd.w D6,D6","roxrd.w D6,D7","rord.w D6,D0","rord.w D6,D1", + "rord.w D6,D2","rord.w D6,D3","rord.w D6,D4","rord.w D6,D5","rord.w D6,D6","rord.w D6,D7" }; + static const char *opATable03B2[] = { "asrd.l #6,D0","asrd.l #6,D1","asrd.l #6,D2","asrd.l #6,D3", + "asrd.l #6,D4","asrd.l #6,D5","asrd.l #6,D6","asrd.l #6,D7","lsrd.l #6,D0","lsrd.l #6,D1", + "lsrd.l #6,D2","lsrd.l #6,D3","lsrd.l #6,D4","lsrd.l #6,D5","lsrd.l #6,D6","lsrd.l #6,D7", + "roxrd.l #6,D0","roxrd.l #6,D1","roxrd.l #6,D2","roxrd.l #6,D3","roxrd.l #6,D4","roxrd.l #6,D5", + "roxrd.l #6,D6","roxrd.l #6,D7","rord.l #6,D0","rord.l #6,D1","rord.l #6,D2","rord.l #6,D3", + "rord.l #6,D4","rord.l #6,D5","rord.l #6,D6","rord.l #6,D7","asrd.l D6,D0","asrd.l D6,D1", + "asrd.l D6,D2","asrd.l D6,D3","asrd.l D6,D4","asrd.l D6,D5","asrd.l D6,D6","asrd.l D6,D7", + "lsrd.l D6,D0","lsrd.l D6,D1","lsrd.l D6,D2","lsrd.l D6,D3","lsrd.l D6,D4","lsrd.l D6,D5", + "lsrd.l D6,D6","lsrd.l D6,D7","roxrd.l D6,D0","roxrd.l D6,D1","roxrd.l D6,D2","roxrd.l D6,D3", + "roxrd.l D6,D4","roxrd.l D6,D5","roxrd.l D6,D6","roxrd.l D6,D7","rord.l D6,D0","rord.l D6,D1", + "rord.l D6,D2","rord.l D6,D3","rord.l D6,D4","rord.l D6,D5","rord.l D6,D6","rord.l D6,D7" }; + static const char *opATable03B4[] = { "asld.b #6,D0","asld.b #6,D1","asld.b #6,D2","asld.b #6,D3", + "asld.b #6,D4","asld.b #6,D5","asld.b #6,D6","asld.b #6,D7","lsld.b #6,D0","lsld.b #6,D1", + "lsld.b #6,D2","lsld.b #6,D3","lsld.b #6,D4","lsld.b #6,D5","lsld.b #6,D6","lsld.b #6,D7", + "roxld.b #6,D0","roxld.b #6,D1","roxld.b #6,D2","roxld.b #6,D3","roxld.b #6,D4","roxld.b #6,D5", + "roxld.b #6,D6","roxld.b #6,D7","rold.b #6,D0","rold.b #6,D1","rold.b #6,D2","rold.b #6,D3", + "rold.b #6,D4","rold.b #6,D5","rold.b #6,D6","rold.b #6,D7","asld.b D6,D0","asld.b D6,D1", + "asld.b D6,D2","asld.b D6,D3","asld.b D6,D4","asld.b D6,D5","asld.b D6,D6","asld.b D6,D7", + "lsld.b D6,D0","lsld.b D6,D1","lsld.b D6,D2","lsld.b D6,D3","lsld.b D6,D4","lsld.b D6,D5", + "lsld.b D6,D6","lsld.b D6,D7","roxld.b D6,D0","roxld.b D6,D1","roxld.b D6,D2","roxld.b D6,D3", + "roxld.b D6,D4","roxld.b D6,D5","roxld.b D6,D6","roxld.b D6,D7","rold.b D6,D0","rold.b D6,D1", + "rold.b D6,D2","rold.b D6,D3","rold.b D6,D4","rold.b D6,D5","rold.b D6,D6","rold.b D6,D7" }; + static const char *opATable03B5[] = { "asld.w #6,D0","asld.w #6,D1","asld.w #6,D2","asld.w #6,D3", + "asld.w #6,D4","asld.w #6,D5","asld.w #6,D6","asld.w #6,D7","lsld.w #6,D0","lsld.w #6,D1", + "lsld.w #6,D2","lsld.w #6,D3","lsld.w #6,D4","lsld.w #6,D5","lsld.w #6,D6","lsld.w #6,D7", + "roxld.w #6,D0","roxld.w #6,D1","roxld.w #6,D2","roxld.w #6,D3","roxld.w #6,D4","roxld.w #6,D5", + "roxld.w #6,D6","roxld.w #6,D7","rold.w #6,D0","rold.w #6,D1","rold.w #6,D2","rold.w #6,D3", + "rold.w #6,D4","rold.w #6,D5","rold.w #6,D6","rold.w #6,D7","asld.w D6,D0","asld.w D6,D1", + "asld.w D6,D2","asld.w D6,D3","asld.w D6,D4","asld.w D6,D5","asld.w D6,D6","asld.w D6,D7", + "lsld.w D6,D0","lsld.w D6,D1","lsld.w D6,D2","lsld.w D6,D3","lsld.w D6,D4","lsld.w D6,D5", + "lsld.w D6,D6","lsld.w D6,D7","roxld.w D6,D0","roxld.w D6,D1","roxld.w D6,D2","roxld.w D6,D3", + "roxld.w D6,D4","roxld.w D6,D5","roxld.w D6,D6","roxld.w D6,D7","rold.w D6,D0","rold.w D6,D1", + "rold.w D6,D2","rold.w D6,D3","rold.w D6,D4","rold.w D6,D5","rold.w D6,D6","rold.w D6,D7" }; + static const char *opATable03B6[] = { "asld.l #6,D0","asld.l #6,D1","asld.l #6,D2","asld.l #6,D3", + "asld.l #6,D4","asld.l #6,D5","asld.l #6,D6","asld.l #6,D7","lsld.l #6,D0","lsld.l #6,D1", + "lsld.l #6,D2","lsld.l #6,D3","lsld.l #6,D4","lsld.l #6,D5","lsld.l #6,D6","lsld.l #6,D7", + "roxld.l #6,D0","roxld.l #6,D1","roxld.l #6,D2","roxld.l #6,D3","roxld.l #6,D4","roxld.l #6,D5", + "roxld.l #6,D6","roxld.l #6,D7","rold.l #6,D0","rold.l #6,D1","rold.l #6,D2","rold.l #6,D3", + "rold.l #6,D4","rold.l #6,D5","rold.l #6,D6","rold.l #6,D7","asld.l D6,D0","asld.l D6,D1", + "asld.l D6,D2","asld.l D6,D3","asld.l D6,D4","asld.l D6,D5","asld.l D6,D6","asld.l D6,D7", + "lsld.l D6,D0","lsld.l D6,D1","lsld.l D6,D2","lsld.l D6,D3","lsld.l D6,D4","lsld.l D6,D5", + "lsld.l D6,D6","lsld.l D6,D7","roxld.l D6,D0","roxld.l D6,D1","roxld.l D6,D2","roxld.l D6,D3", + "roxld.l D6,D4","roxld.l D6,D5","roxld.l D6,D6","roxld.l D6,D7","rold.l D6,D0","rold.l D6,D1", + "rold.l D6,D2","rold.l D6,D3","rold.l D6,D4","rold.l D6,D5","rold.l D6,D6","rold.l D6,D7" }; + static const char *opATable03B8[] = { "asrd.b #7,D0","asrd.b #7,D1","asrd.b #7,D2","asrd.b #7,D3", + "asrd.b #7,D4","asrd.b #7,D5","asrd.b #7,D6","asrd.b #7,D7","lsrd.b #7,D0","lsrd.b #7,D1", + "lsrd.b #7,D2","lsrd.b #7,D3","lsrd.b #7,D4","lsrd.b #7,D5","lsrd.b #7,D6","lsrd.b #7,D7", + "roxrd.b #7,D0","roxrd.b #7,D1","roxrd.b #7,D2","roxrd.b #7,D3","roxrd.b #7,D4","roxrd.b #7,D5", + "roxrd.b #7,D6","roxrd.b #7,D7","rord.b #7,D0","rord.b #7,D1","rord.b #7,D2","rord.b #7,D3", + "rord.b #7,D4","rord.b #7,D5","rord.b #7,D6","rord.b #7,D7","asrd.b D7,D0","asrd.b D7,D1", + "asrd.b D7,D2","asrd.b D7,D3","asrd.b D7,D4","asrd.b D7,D5","asrd.b D7,D6","asrd.b D7,D7", + "lsrd.b D7,D0","lsrd.b D7,D1","lsrd.b D7,D2","lsrd.b D7,D3","lsrd.b D7,D4","lsrd.b D7,D5", + "lsrd.b D7,D6","lsrd.b D7,D7","roxrd.b D7,D0","roxrd.b D7,D1","roxrd.b D7,D2","roxrd.b D7,D3", + "roxrd.b D7,D4","roxrd.b D7,D5","roxrd.b D7,D6","roxrd.b D7,D7","rord.b D7,D0","rord.b D7,D1", + "rord.b D7,D2","rord.b D7,D3","rord.b D7,D4","rord.b D7,D5","rord.b D7,D6","rord.b D7,D7" }; + static const char *opATable03B9[] = { "asrd.w #7,D0","asrd.w #7,D1","asrd.w #7,D2","asrd.w #7,D3", + "asrd.w #7,D4","asrd.w #7,D5","asrd.w #7,D6","asrd.w #7,D7","lsrd.w #7,D0","lsrd.w #7,D1", + "lsrd.w #7,D2","lsrd.w #7,D3","lsrd.w #7,D4","lsrd.w #7,D5","lsrd.w #7,D6","lsrd.w #7,D7", + "roxrd.w #7,D0","roxrd.w #7,D1","roxrd.w #7,D2","roxrd.w #7,D3","roxrd.w #7,D4","roxrd.w #7,D5", + "roxrd.w #7,D6","roxrd.w #7,D7","rord.w #7,D0","rord.w #7,D1","rord.w #7,D2","rord.w #7,D3", + "rord.w #7,D4","rord.w #7,D5","rord.w #7,D6","rord.w #7,D7","asrd.w D7,D0","asrd.w D7,D1", + "asrd.w D7,D2","asrd.w D7,D3","asrd.w D7,D4","asrd.w D7,D5","asrd.w D7,D6","asrd.w D7,D7", + "lsrd.w D7,D0","lsrd.w D7,D1","lsrd.w D7,D2","lsrd.w D7,D3","lsrd.w D7,D4","lsrd.w D7,D5", + "lsrd.w D7,D6","lsrd.w D7,D7","roxrd.w D7,D0","roxrd.w D7,D1","roxrd.w D7,D2","roxrd.w D7,D3", + "roxrd.w D7,D4","roxrd.w D7,D5","roxrd.w D7,D6","roxrd.w D7,D7","rord.w D7,D0","rord.w D7,D1", + "rord.w D7,D2","rord.w D7,D3","rord.w D7,D4","rord.w D7,D5","rord.w D7,D6","rord.w D7,D7" }; + static const char *opATable03BA[] = { "asrd.l #7,D0","asrd.l #7,D1","asrd.l #7,D2","asrd.l #7,D3", + "asrd.l #7,D4","asrd.l #7,D5","asrd.l #7,D6","asrd.l #7,D7","lsrd.l #7,D0","lsrd.l #7,D1", + "lsrd.l #7,D2","lsrd.l #7,D3","lsrd.l #7,D4","lsrd.l #7,D5","lsrd.l #7,D6","lsrd.l #7,D7", + "roxrd.l #7,D0","roxrd.l #7,D1","roxrd.l #7,D2","roxrd.l #7,D3","roxrd.l #7,D4","roxrd.l #7,D5", + "roxrd.l #7,D6","roxrd.l #7,D7","rord.l #7,D0","rord.l #7,D1","rord.l #7,D2","rord.l #7,D3", + "rord.l #7,D4","rord.l #7,D5","rord.l #7,D6","rord.l #7,D7","asrd.l D7,D0","asrd.l D7,D1", + "asrd.l D7,D2","asrd.l D7,D3","asrd.l D7,D4","asrd.l D7,D5","asrd.l D7,D6","asrd.l D7,D7", + "lsrd.l D7,D0","lsrd.l D7,D1","lsrd.l D7,D2","lsrd.l D7,D3","lsrd.l D7,D4","lsrd.l D7,D5", + "lsrd.l D7,D6","lsrd.l D7,D7","roxrd.l D7,D0","roxrd.l D7,D1","roxrd.l D7,D2","roxrd.l D7,D3", + "roxrd.l D7,D4","roxrd.l D7,D5","roxrd.l D7,D6","roxrd.l D7,D7","rord.l D7,D0","rord.l D7,D1", + "rord.l D7,D2","rord.l D7,D3","rord.l D7,D4","rord.l D7,D5","rord.l D7,D6","rord.l D7,D7" }; + static const char *opATable03BC[] = { "asld.b #7,D0","asld.b #7,D1","asld.b #7,D2","asld.b #7,D3", + "asld.b #7,D4","asld.b #7,D5","asld.b #7,D6","asld.b #7,D7","lsld.b #7,D0","lsld.b #7,D1", + "lsld.b #7,D2","lsld.b #7,D3","lsld.b #7,D4","lsld.b #7,D5","lsld.b #7,D6","lsld.b #7,D7", + "roxld.b #7,D0","roxld.b #7,D1","roxld.b #7,D2","roxld.b #7,D3","roxld.b #7,D4","roxld.b #7,D5", + "roxld.b #7,D6","roxld.b #7,D7","rold.b #7,D0","rold.b #7,D1","rold.b #7,D2","rold.b #7,D3", + "rold.b #7,D4","rold.b #7,D5","rold.b #7,D6","rold.b #7,D7","asld.b D7,D0","asld.b D7,D1", + "asld.b D7,D2","asld.b D7,D3","asld.b D7,D4","asld.b D7,D5","asld.b D7,D6","asld.b D7,D7", + "lsld.b D7,D0","lsld.b D7,D1","lsld.b D7,D2","lsld.b D7,D3","lsld.b D7,D4","lsld.b D7,D5", + "lsld.b D7,D6","lsld.b D7,D7","roxld.b D7,D0","roxld.b D7,D1","roxld.b D7,D2","roxld.b D7,D3", + "roxld.b D7,D4","roxld.b D7,D5","roxld.b D7,D6","roxld.b D7,D7","rold.b D7,D0","rold.b D7,D1", + "rold.b D7,D2","rold.b D7,D3","rold.b D7,D4","rold.b D7,D5","rold.b D7,D6","rold.b D7,D7" }; + static const char *opATable03BD[] = { "asld.w #7,D0","asld.w #7,D1","asld.w #7,D2","asld.w #7,D3", + "asld.w #7,D4","asld.w #7,D5","asld.w #7,D6","asld.w #7,D7","lsld.w #7,D0","lsld.w #7,D1", + "lsld.w #7,D2","lsld.w #7,D3","lsld.w #7,D4","lsld.w #7,D5","lsld.w #7,D6","lsld.w #7,D7", + "roxld.w #7,D0","roxld.w #7,D1","roxld.w #7,D2","roxld.w #7,D3","roxld.w #7,D4","roxld.w #7,D5", + "roxld.w #7,D6","roxld.w #7,D7","rold.w #7,D0","rold.w #7,D1","rold.w #7,D2","rold.w #7,D3", + "rold.w #7,D4","rold.w #7,D5","rold.w #7,D6","rold.w #7,D7","asld.w D7,D0","asld.w D7,D1", + "asld.w D7,D2","asld.w D7,D3","asld.w D7,D4","asld.w D7,D5","asld.w D7,D6","asld.w D7,D7", + "lsld.w D7,D0","lsld.w D7,D1","lsld.w D7,D2","lsld.w D7,D3","lsld.w D7,D4","lsld.w D7,D5", + "lsld.w D7,D6","lsld.w D7,D7","roxld.w D7,D0","roxld.w D7,D1","roxld.w D7,D2","roxld.w D7,D3", + "roxld.w D7,D4","roxld.w D7,D5","roxld.w D7,D6","roxld.w D7,D7","rold.w D7,D0","rold.w D7,D1", + "rold.w D7,D2","rold.w D7,D3","rold.w D7,D4","rold.w D7,D5","rold.w D7,D6","rold.w D7,D7" }; + static const char *opATable03BE[] = { "asld.l #7,D0","asld.l #7,D1","asld.l #7,D2","asld.l #7,D3", + "asld.l #7,D4","asld.l #7,D5","asld.l #7,D6","asld.l #7,D7","lsld.l #7,D0","lsld.l #7,D1", + "lsld.l #7,D2","lsld.l #7,D3","lsld.l #7,D4","lsld.l #7,D5","lsld.l #7,D6","lsld.l #7,D7", + "roxld.l #7,D0","roxld.l #7,D1","roxld.l #7,D2","roxld.l #7,D3","roxld.l #7,D4","roxld.l #7,D5", + "roxld.l #7,D6","roxld.l #7,D7","rold.l #7,D0","rold.l #7,D1","rold.l #7,D2","rold.l #7,D3", + "rold.l #7,D4","rold.l #7,D5","rold.l #7,D6","rold.l #7,D7","asld.l D7,D0","asld.l D7,D1", + "asld.l D7,D2","asld.l D7,D3","asld.l D7,D4","asld.l D7,D5","asld.l D7,D6","asld.l D7,D7", + "lsld.l D7,D0","lsld.l D7,D1","lsld.l D7,D2","lsld.l D7,D3","lsld.l D7,D4","lsld.l D7,D5", + "lsld.l D7,D6","lsld.l D7,D7","roxld.l D7,D0","roxld.l D7,D1","roxld.l D7,D2","roxld.l D7,D3", + "roxld.l D7,D4","roxld.l D7,D5","roxld.l D7,D6","roxld.l D7,D7","rold.l D7,D0","rold.l D7,D1", + "rold.l D7,D2","rold.l D7,D3","rold.l D7,D4","rold.l D7,D5","rold.l D7,D6","rold.l D7,D7" }; + static const char *opBTable00[] = { "ori.b #[DB3],[AM2]","ori.w #[DW3],[AM2]","ori.l #[DL3],[AM2]", + NULL,"btst D0,[AM2]","bchg D0,[AM2]","bclr D0,[AM2]","bset D0,[AM2]","andi.b #[DB3],[AM2]", + "andi.w #[DW3],[AM2]","andi.l #[DL3],[AM2]",NULL,"btst D1,[AM2]","bchg D1,[AM2]","bclr D1,[AM2]", + "bset D1,[AM2]","subi.b #[DB3],[AM2]","subi.w #[DW3],[AM2]","subi.l #[DL3],[AM2]",NULL, + "btst D2,[AM2]","bchg D2,[AM2]","bclr D2,[AM2]","bset D2,[AM2]","addi.b #[DB3],[AM2]", + "addi.w #[DW3],[AM2]","addi.l #[DL3],[AM2]",NULL,"btst D3,[AM2]","bchg D3,[AM2]","bclr D3,[AM2]", + "bset D3,[AM2]","btst #[BN3],[AM2]","bchg #[BN3],[AM2]","bclr #[BN3],[AM2]","bset #[BN3],[AM2]", + "btst D4,[AM2]","bchg D4,[AM2]","bclr D4,[AM2]","bset D4,[AM2]","eori.b #[DB3],[AM2]", + "eori.w #[DW3],[AM2]","eori.l #[DL3],[AM2]",NULL,"btst D5,[AM2]","bchg D5,[AM2]","bclr D5,[AM2]", + "bset D5,[AM2]","cmpi.b #[DB3],[AM2]","cmpi.w #[DW3],[AM2]","cmpi.l #[DL3],[AM2]",NULL, + "btst D6,[AM2]","bchg D6,[AM2]","bclr D6,[AM2]","bset D6,[AM2]",NULL,NULL,NULL,NULL, + "btst D7,[AM2]","bchg D7,[AM2]","bclr D7,[AM2]","bset D7,[AM2]" }; + static const char *opBTable02[] = { "movea.l as,A0",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.l as,A1", + NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.l as,A2",NULL,NULL,NULL,NULL,NULL,NULL,NULL, + "movea.l as,A3",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.l as,A4",NULL,NULL,NULL,NULL, + NULL,NULL,NULL,"movea.l as,A5",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.l as,A6",NULL, + NULL,NULL,NULL,NULL,NULL,NULL,"movea.l as,A7" }; + static const char *opBTable03[] = { "movea.w as,A0",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.w as,A1", + NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.w as,A2",NULL,NULL,NULL,NULL,NULL,NULL,NULL, + "movea.w as,A3",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.w as,A4",NULL,NULL,NULL,NULL, + NULL,NULL,NULL,"movea.w as,A5",NULL,NULL,NULL,NULL,NULL,NULL,NULL,"movea.w as,A6",NULL, + NULL,NULL,NULL,NULL,NULL,NULL,"movea.w as,A7" }; + static const char *opBTable04[] = { "negx.b [AM2]","negx.w [AM2]","negx.l [AM2]",NULL,NULL,NULL, + "chk.w [AM2],D0","lea [AM2],A0","clr.b [AM2]","clr.w [AM2]","clr.l [AM2]",NULL,NULL, + NULL,"chk.w [AM2],D1","lea [AM2],A1","neg.b [AM2]","neg.w [AM2]","neg.l [AM2]","move [AM2],CCR", + NULL,NULL,"chk.w [AM2],D2","lea [AM2],A2","not.b [AM2]","not.w [AM2]","not.l [AM2]", + "move [AM2],SR",NULL,NULL,"chk.w [AM2],D3","lea [AM2],A3","nbcd [AM2]","pea [AM2]","movem.w [RL3],[AM2]", + "movem.l [RL3],[AM2]",NULL,NULL,"chk.w [AM2],D4","lea [AM2],A4","tst.b [AM2]","tst.w [AM2]", + "tst.l [AM2]","tas [AM2]",NULL,NULL,"chk.w [AM2],D5","lea [AM2],A5",NULL,NULL,"movem.w [AM2],[RL3]", + "movem.l [AM2],[RL3]",NULL,NULL,"chk.w [AM2],D6","lea [AM2],A6",NULL,NULL,"jsr [AM2]", + "jmp [AM2]",NULL,NULL,"chk.w [AM2],D7","lea [AM2],A7" }; + static const char *opBTable05[] = { "addq.b #8,[AM2]","addq.w #8,[AM2]","addq.l #8,[AM2]","st [AM2]", + "subq.b #8,[AM2]","subq.w #8,[AM2]","subq.l #8,[AM2]","sf [AM2]","addq.b #1,[AM2]","addq.w #1,[AM2]", + "addq.l #1,[AM2]","shi [AM2]","subq.b #1,[AM2]","subq.w #1,[AM2]","subq.l #1,[AM2]", + "sls [AM2]","addq.b #2,[AM2]","addq.w #2,[AM2]","addq.l #2,[AM2]","scc [AM2]","subq.b #2,[AM2]", + "subq.w #2,[AM2]","subq.l #2,[AM2]","scs [AM2]","addq.b #3,[AM2]","addq.w #3,[AM2]", + "addq.l #3,[AM2]","sne [AM2]","subq.b #3,[AM2]","subq.w #3,[AM2]","subq.l #3,[AM2]", + "seq [AM2]","addq.b #4,[AM2]","addq.w #4,[AM2]","addq.l #4,[AM2]","svc [AM2]","subq.b #4,[AM2]", + "subq.w #4,[AM2]","subq.l #4,[AM2]","svs [AM2]","addq.b #5,[AM2]","addq.w #5,[AM2]", + "addq.l #5,[AM2]","spl [AM2]","subq.b #5,[AM2]","subq.w #5,[AM2]","subq.l #5,[AM2]", + "smi [AM2]","addq.b #6,[AM2]","addq.w #6,[AM2]","addq.l #6,[AM2]","sge [AM2]","subq.b #6,[AM2]", + "subq.w #6,[AM2]","subq.l #6,[AM2]","slt [AM2]","addq.b #7,[AM2]","addq.w #7,[AM2]", + "addq.l #7,[AM2]","sgt [AM2]","subq.b #7,[AM2]","subq.w #7,[AM2]","subq.l #7,[AM2]", + "sle [AM2]" }; + static const char *opBTable08[] = { "or.b [AM2],D0","or.w [AM2],D0","or.l [AM2],D0","divu.w [AM2],D0", + "or.b D0,[AM2]","or.w D0,[AM2]","or.l D0,[AM2]","divs.w [AM2],D0","or.b [AM2],D1","or.w [AM2],D1", + "or.l [AM2],D1","divu.w [AM2],D1","or.b D1,[AM2]","or.w D1,[AM2]","or.l D1,[AM2]","divs.w [AM2],D1", + "or.b [AM2],D2","or.w [AM2],D2","or.l [AM2],D2","divu.w [AM2],D2","or.b D2,[AM2]","or.w D2,[AM2]", + "or.l D2,[AM2]","divs.w [AM2],D2","or.b [AM2],D3","or.w [AM2],D3","or.l [AM2],D3","divu.w [AM2],D3", + "or.b D3,[AM2]","or.w D3,[AM2]","or.l D3,[AM2]","divs.w [AM2],D3","or.b [AM2],D4","or.w [AM2],D4", + "or.l [AM2],D4","divu.w [AM2],D4","or.b D4,[AM2]","or.w D4,[AM2]","or.l D4,[AM2]","divs.w [AM2],D4", + "or.b [AM2],D5","or.w [AM2],D5","or.l [AM2],D5","divu.w [AM2],D5","or.b D5,[AM2]","or.w D5,[AM2]", + "or.l D5,[AM2]","divs.w [AM2],D5","or.b [AM2],D6","or.w [AM2],D6","or.l [AM2],D6","divu.w [AM2],D6", + "or.b D6,[AM2]","or.w D6,[AM2]","or.l D6,[AM2]","divs.w [AM2],D6","or.b [AM2],D7","or.w [AM2],D7", + "or.l [AM2],D7","divu.w [AM2],D7","or.b D7,[AM2]","or.w D7,[AM2]","or.l D7,[AM2]","divs.w [AM2],D7" }; + static const char *opBTable09[] = { "sub.b [AM2],D0","sub.w [AM2],D0","sub.l [AM2],D0","suba.w [AM2],A0", + "sub.b D0,[AM2]","sub.w D0,[AM2]","sub.l D0,[AM2]","suba.l [AM2],A0","sub.b [AM2],D1", + "sub.w [AM2],D1","sub.l [AM2],D1","suba.w [AM2],A1","sub.b D1,[AM2]","sub.w D1,[AM2]", + "sub.l D1,[AM2]","suba.l [AM2],A1","sub.b [AM2],D2","sub.w [AM2],D2","sub.l [AM2],D2", + "suba.w [AM2],A2","sub.b D2,[AM2]","sub.w D2,[AM2]","sub.l D2,[AM2]","suba.l [AM2],A2", + "sub.b [AM2],D3","sub.w [AM2],D3","sub.l [AM2],D3","suba.w [AM2],A3","sub.b D3,[AM2]", + "sub.w D3,[AM2]","sub.l D3,[AM2]","suba.l [AM2],A3","sub.b [AM2],D4","sub.w [AM2],D4", + "sub.l [AM2],D4","suba.w [AM2],A4","sub.b D4,[AM2]","sub.w D4,[AM2]","sub.l D4,[AM2]", + "suba.l [AM2],A4","sub.b [AM2],D5","sub.w [AM2],D5","sub.l [AM2],D5","suba.w [AM2],A5", + "sub.b D5,[AM2]","sub.w D5,[AM2]","sub.l D5,[AM2]","suba.l [AM2],A5","sub.b [AM2],D6", + "sub.w [AM2],D6","sub.l [AM2],D6","suba.w [AM2],A6","sub.b D6,[AM2]","sub.w D6,[AM2]", + "sub.l D6,[AM2]","suba.l [AM2],A6","sub.b [AM2],D7","sub.w [AM2],D7","sub.l [AM2],D7", + "suba.w [AM2],A7","sub.b D7,[AM2]","sub.w D7,[AM2]","sub.l D7,[AM2]","suba.l [AM2],A7" }; + static const char *opBTable0B[] = { "cmp.b [AM2],D0","cmp.w [AM2],D0","cmp.l [AM2],D0","cmpa.w [AM2],A0", + "eor.b [AM2],D0","eor.w [AM2],D0","eor.l [AM2],D0","cmpa.l [AM2],A0","cmp.b [AM2],D1", + "cmp.w [AM2],D1","cmp.l [AM2],D1","cmpa.w [AM2],A1","eor.b [AM2],D1","eor.w [AM2],D1", + "eor.l [AM2],D1","cmpa.l [AM2],A1","cmp.b [AM2],D2","cmp.w [AM2],D2","cmp.l [AM2],D2", + "cmpa.w [AM2],A2","eor.b [AM2],D2","eor.w [AM2],D2","eor.l [AM2],D2","cmpa.l [AM2],A2", + "cmp.b [AM2],D3","cmp.w [AM2],D3","cmp.l [AM2],D3","cmpa.w [AM2],A3","eor.b [AM2],D3", + "eor.w [AM2],D3","eor.l [AM2],D3","cmpa.l [AM2],A3","cmp.b [AM2],D4","cmp.w [AM2],D4", + "cmp.l [AM2],D4","cmpa.w [AM2],A4","eor.b [AM2],D4","eor.w [AM2],D4","eor.l [AM2],D4", + "cmpa.l [AM2],A4","cmp.b [AM2],D5","cmp.w [AM2],D5","cmp.l [AM2],D5","cmpa.w [AM2],A5", + "eor.b [AM2],D5","eor.w [AM2],D5","eor.l [AM2],D5","cmpa.l [AM2],A5","cmp.b [AM2],D6", + "cmp.w [AM2],D6","cmp.l [AM2],D6","cmpa.w [AM2],A6","eor.b [AM2],D6","eor.w [AM2],D6", + "eor.l [AM2],D6","cmpa.l [AM2],A6","cmp.b [AM2],D7","cmp.w [AM2],D7","cmp.l [AM2],D7", + "cmpa.w [AM2],A7","eor.b [AM2],D7","eor.w [AM2],D7","eor.l [AM2],D7","cmpa.l [AM2],A7" }; + static const char *opBTable0C[] = { "and.b [AM2],D0","and.w [AM2],D0","and.l [AM2],D0","mulu.w [AM2],D0", + "and.b D0,[AM2]","and.w D0,[AM2]","and.l D0,[AM2]","muls.w [AM2],D0","and.b [AM2],D1", + "and.w [AM2],D1","and.l [AM2],D1","mulu.w [AM2],D1","and.b D1,[AM2]","and.w D1,[AM2]", + "and.l D1,[AM2]","muls.w [AM2],D1","and.b [AM2],D2","and.w [AM2],D2","and.l [AM2],D2", + "mulu.w [AM2],D2","and.b D2,[AM2]","and.w D2,[AM2]","and.l D2,[AM2]","muls.w [AM2],D2", + "and.b [AM2],D3","and.w [AM2],D3","and.l [AM2],D3","mulu.w [AM2],D3","and.b D3,[AM2]", + "and.w D3,[AM2]","and.l D3,[AM2]","muls.w [AM2],D3","and.b [AM2],D4","and.w [AM2],D4", + "and.l [AM2],D4","mulu.w [AM2],D4","and.b D4,[AM2]","and.w D4,[AM2]","and.l D4,[AM2]", + "muls.w [AM2],D4","and.b [AM2],D5","and.w [AM2],D5","and.l [AM2],D5","mulu.w [AM2],D5", + "and.b D5,[AM2]","and.w D5,[AM2]","and.l D5,[AM2]","muls.w [AM2],D5","and.b [AM2],D6", + "and.w [AM2],D6","and.l [AM2],D6","mulu.w [AM2],D6","and.b D6,[AM2]","and.w D6,[AM2]", + "and.l D6,[AM2]","muls.w [AM2],D6","and.b [AM2],D7","and.w [AM2],D7","and.l [AM2],D7", + "mulu.w [AM2],D7","and.b D7,[AM2]","and.w D7,[AM2]","and.l D7,[AM2]","muls.w [AM2],D7" }; + static const char *opBTable0D[] = { "add.b [AM2],D0","add.w [AM2],D0","add.l [AM2],D0","adda.w [AM2],A0", + "add.b D0,[AM2]","add.w D0,[AM2]","add.l D0,[AM2]","adda.l [AM2],A0","add.b [AM2],D1", + "add.w [AM2],D1","add.l [AM2],D1","adda.w [AM2],A1","add.b D1,[AM2]","add.w D1,[AM2]", + "add.l D1,[AM2]","adda.l [AM2],A1","add.b [AM2],D2","add.w [AM2],D2","add.l [AM2],D2", + "adda.w [AM2],A2","add.b D2,[AM2]","add.w D2,[AM2]","add.l D2,[AM2]","adda.l [AM2],A2", + "add.b [AM2],D3","add.w [AM2],D3","add.l [AM2],D3","adda.w [AM2],A3","add.b D3,[AM2]", + "add.w D3,[AM2]","add.l D3,[AM2]","adda.l [AM2],A3","add.b [AM2],D4","add.w [AM2],D4", + "add.l [AM2],D4","adda.w [AM2],A4","add.b D4,[AM2]","add.w D4,[AM2]","add.l D4,[AM2]", + "adda.l [AM2],A4","add.b [AM2],D5","add.w [AM2],D5","add.l [AM2],D5","adda.w [AM2],A5", + "add.b D5,[AM2]","add.w D5,[AM2]","add.l D5,[AM2]","adda.l [AM2],A5","add.b [AM2],D6", + "add.w [AM2],D6","add.l [AM2],D6","adda.w [AM2],A6","add.b D6,[AM2]","add.w D6,[AM2]", + "add.l D6,[AM2]","adda.l [AM2],A6","add.b [AM2],D7","add.w [AM2],D7","add.l [AM2],D7", + "adda.w [AM2],A7","add.b D7,[AM2]","add.w D7,[AM2]","add.l D7,[AM2]","adda.l [AM2],A7" }; + static const char *opBTable0E[] = { "asrd [AM2]",NULL,NULL,NULL,"asld [AM2]",NULL,NULL,NULL, + "lsrd [AM2]",NULL,NULL,NULL,"lsld [AM2]",NULL,NULL,NULL,"roxrd [AM2]",NULL,NULL,NULL, + "roxld [AM2]",NULL,NULL,NULL,"rord [AM2]",NULL,NULL,NULL,"rold [AM2]" }; + static const char *brTable[] = { "bra [LV2]","bsr [LV2]","bhi [LV2]","bls [LV2]","bcc [LV2]","bcs [LV2]", + "bne [LV2]","beq [LV2]","bvc [LV2]","bvs [LV2]","bpl [LV2]","bmi [LV2]","bge [LV2]", + "blt [LV2]","bgt [LV2]","ble [LV2]" }; + + int C68KDebug::Disassemble(UINT32 addr, char *mnemonic, char *operands) + { + // Read opcode head word + UINT16 opcode = (UINT16)ReadMem(addr, 2); + int offset = 2; + + const char *instr; + char moveStr[50]; + char dataStr[20]; + INT8 i8; + INT16 i16; + INT32 i32; + UINT8 u8; + UINT16 u16; + UINT32 u32; + + // First check for special cases that don't fit into op tables A & B + UINT16 hd = (opcode>>12); + UINT16 tl; + switch (hd) + { + case 0x1: + // move.b as,ad + instr = "move.b [AM2],[AR1]"; + break; + case 0x2: + if ((opcode&0x01C0) == 0x0040) + { + // movea.l as,ad + sprintf(moveStr, "movea.l [AM2],A%u", (opcode>>9)&0x7); + instr = moveStr; + } + else + { + // move.l as,Ad + instr = "move.l [AM2],[AR1]"; + } + break; + case 0x3: + if ((opcode&0x01C0) == 0x0040) + { + // movea.w as,ad + sprintf(moveStr, "movea.w [AM2],A%u", (opcode>>9)&0x7); + instr = moveStr; + } + else + { + // move.w as,Ad + instr = "move.w [AM2],[AR1]"; + } + break; + case 0x4: + if ((opcode&0x0B80) == 0x0880) + { + // movem.z reg-list,a or movem.z a,reg-list + u16 = (UINT16)ReadMem(addr + offset, 2); + offset += 2; + char sizeC = (opcode&0x40 ? 'l' : 'w'); + char regList[50]; + char *p = regList; + int range = 0; + for (unsigned r = 0; r < 17; r++) + { + // Get bit position of register r (if address mode is pre-decrement, then bits are in reverse order) + unsigned bitPos = ((opcode&0x38) == 0x20 ? 15 - r : r); + // Check given bit is set + if (r < 16 && ((u16>>bitPos)&0x0001)) + { + // Check not in middle of register range + if (range == 0) + { + if (p > regList) + *p++ = '/'; + // If address mode is pre-decrement, then reverse order of registers + if (r < 8) + { + *p++ = 'D'; + *p++ = '0' + r; + } + else + { + *p++ = 'A'; + *p++ = '0' + r - 8; + } + } + range++; + } + else + { + if (range > 1) + { + // Close off a register range + *p++ = '-'; + // If address mode is pre-decrement, then reverse order of registers + unsigned prevR = r - 1; + if (prevR < 8) + { + *p++ = 'D'; + *p++ = '0' + prevR; + } + else + { + *p++ = 'A'; + *p++ = '0' + prevR - 8; + } + } + range = 0; + } + } + *p++ = '\0'; + if (opcode&0x0400) + sprintf(moveStr, "movem.%c [AM2],%s", sizeC, regList); + else + sprintf(moveStr, "movem.%c %s,[AM2]", sizeC, regList); + instr = moveStr; + } + else + instr = NULL; + break; + case 0x6: + // bra label, bsr label && bCC label + tl = (opcode>>8)&0xF; + instr = brTable[tl]; + break; + case 0x7: + // moveq #data8,Dn + if (opcode&0x100) + goto invalid; + u8 = (UINT8)opcode&0xFF; + FormatData(dataStr, 1, u8); + sprintf(moveStr, "moveq #%s,D%u", dataStr, (opcode>>9)&0x7); + //i8 = (INT8)opcode&0xFF; + //if (i8 < 0) + //{ + // FormatData(dataStr, 1, -i8); + // sprintf(moveStr, "moveq #-%s,D%u", dataStr, (opcode>>9)&0x7); + // //sprintf(moveStr, "moveq #-0x%02X,D%u", -i8, (opcode>>9)&0x7); + //} + //else + //{ + // FormatData(dataStr, 1, i8); + // sprintf(moveStr, "moveq #%s,D%u", dataStr, (opcode>>9)&0x7); + // //sprintf(moveStr, "moveq #0x%02X,D%u", i8, (opcode>>9)&0x7); + //} + instr = moveStr; + break; + default: + instr = NULL; + break; + } + + // Next, try op table A if no match found + if (instr == NULL) + { + UINT16 hdA = (opcode>>6); + UINT16 tlA = opcode & 0x3F; + switch (hdA) + { + case 0x0000: instr = (tlA == 0x3C ? "ori.b #[DB3],ccr" : NULL); break; + case 0x0001: instr = (tlA == 0x3C ? "ori.w #[DW3],sr" : NULL); break; + case 0x0004: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0004[tlA - 0x08] : NULL); break; + case 0x0005: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0005[tlA - 0x08] : NULL); break; + case 0x0006: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0006[tlA - 0x08] : NULL); break; + case 0x0007: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0007[tlA - 0x08] : NULL); break; + case 0x0008: instr = (tlA == 0x3C ? "andi.b #[DB3],ccr" : NULL); break; + case 0x0009: instr = (tlA == 0x3C ? "andi.w #[DW3],sr" : NULL); break; + case 0x000C: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable000C[tlA - 0x08] : NULL); break; + case 0x000D: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable000D[tlA - 0x08] : NULL); break; + case 0x000E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable000E[tlA - 0x08] : NULL); break; + case 0x000F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable000F[tlA - 0x08] : NULL); break; + case 0x0014: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0014[tlA - 0x08] : NULL); break; + case 0x0015: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0015[tlA - 0x08] : NULL); break; + case 0x0016: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0016[tlA - 0x08] : NULL); break; + case 0x0017: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0017[tlA - 0x08] : NULL); break; + case 0x001C: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable001C[tlA - 0x08] : NULL); break; + case 0x001D: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable001D[tlA - 0x08] : NULL); break; + case 0x001E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable001E[tlA - 0x08] : NULL); break; + case 0x001F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable001F[tlA - 0x08] : NULL); break; + case 0x0024: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0024[tlA - 0x08] : NULL); break; + case 0x0025: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0025[tlA - 0x08] : NULL); break; + case 0x0026: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0026[tlA - 0x08] : NULL); break; + case 0x0027: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0027[tlA - 0x08] : NULL); break; + case 0x0028: instr = (tlA == 0x3C ? "eori.b #[DB3],ccr" : NULL); break; + case 0x0029: instr = (tlA == 0x3C ? "eori.w #[DW3],sr" : NULL); break; + case 0x002C: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable002C[tlA - 0x08] : NULL); break; + case 0x002D: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable002D[tlA - 0x08] : NULL); break; + case 0x002E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable002E[tlA - 0x08] : NULL); break; + case 0x002F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable002F[tlA - 0x08] : NULL); break; + case 0x0034: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0034[tlA - 0x08] : NULL); break; + case 0x0035: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0035[tlA - 0x08] : NULL); break; + case 0x0036: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0036[tlA - 0x08] : NULL); break; + case 0x0037: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0037[tlA - 0x08] : NULL); break; + case 0x003C: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable003C[tlA - 0x08] : NULL); break; + case 0x003D: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable003D[tlA - 0x08] : NULL); break; + case 0x003E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable003E[tlA - 0x08] : NULL); break; + case 0x003F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable003F[tlA - 0x08] : NULL); break; + case 0x0121: instr = (tlA <= 0x07 ? opATable0121[tlA] : NULL); break; + case 0x0122: instr = (tlA <= 0x07 ? opATable0122[tlA] : NULL); break; + case 0x0123: instr = (tlA <= 0x07 ? opATable0123[tlA] : NULL); break; + case 0x012B: instr = (tlA == 0x3C ? "illegal" : NULL); break; + case 0x0139: instr = (tlA <= 0x37 ? opATable0139[tlA] : NULL); break; + case 0x0143: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0143[tlA - 0x08] : NULL); break; + case 0x0147: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0147[tlA - 0x08] : NULL); break; + case 0x014B: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable014B[tlA - 0x08] : NULL); break; + case 0x014F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable014F[tlA - 0x08] : NULL); break; + case 0x0153: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0153[tlA - 0x08] : NULL); break; + case 0x0157: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0157[tlA - 0x08] : NULL); break; + case 0x015B: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable015B[tlA - 0x08] : NULL); break; + case 0x015F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable015F[tlA - 0x08] : NULL); break; + case 0x0163: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0163[tlA - 0x08] : NULL); break; + case 0x0167: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0167[tlA - 0x08] : NULL); break; + case 0x016B: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable016B[tlA - 0x08] : NULL); break; + case 0x016F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable016F[tlA - 0x08] : NULL); break; + case 0x0173: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0173[tlA - 0x08] : NULL); break; + case 0x0177: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0177[tlA - 0x08] : NULL); break; + case 0x017B: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable017B[tlA - 0x08] : NULL); break; + case 0x017F: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable017F[tlA - 0x08] : NULL); break; + case 0x0204: instr = (tlA <= 0x0F ? opATable0204[tlA] : NULL); break; + case 0x020C: instr = (tlA <= 0x0F ? opATable020C[tlA] : NULL); break; + case 0x0214: instr = (tlA <= 0x0F ? opATable0214[tlA] : NULL); break; + case 0x021C: instr = (tlA <= 0x0F ? opATable021C[tlA] : NULL); break; + case 0x0224: instr = (tlA <= 0x0F ? opATable0224[tlA] : NULL); break; + case 0x022C: instr = (tlA <= 0x0F ? opATable022C[tlA] : NULL); break; + case 0x0234: instr = (tlA <= 0x0F ? opATable0234[tlA] : NULL); break; + case 0x023C: instr = (tlA <= 0x0F ? opATable023C[tlA] : NULL); break; + case 0x0244: instr = (tlA <= 0x0F ? opATable0244[tlA] : NULL); break; + case 0x0245: instr = (tlA <= 0x0F ? opATable0245[tlA] : NULL); break; + case 0x0246: instr = (tlA <= 0x0F ? opATable0246[tlA] : NULL); break; + case 0x024C: instr = (tlA <= 0x0F ? opATable024C[tlA] : NULL); break; + case 0x024D: instr = (tlA <= 0x0F ? opATable024D[tlA] : NULL); break; + case 0x024E: instr = (tlA <= 0x0F ? opATable024E[tlA] : NULL); break; + case 0x0254: instr = (tlA <= 0x0F ? opATable0254[tlA] : NULL); break; + case 0x0255: instr = (tlA <= 0x0F ? opATable0255[tlA] : NULL); break; + case 0x0256: instr = (tlA <= 0x0F ? opATable0256[tlA] : NULL); break; + case 0x025C: instr = (tlA <= 0x0F ? opATable025C[tlA] : NULL); break; + case 0x025D: instr = (tlA <= 0x0F ? opATable025D[tlA] : NULL); break; + case 0x025E: instr = (tlA <= 0x0F ? opATable025E[tlA] : NULL); break; + case 0x0264: instr = (tlA <= 0x0F ? opATable0264[tlA] : NULL); break; + case 0x0265: instr = (tlA <= 0x0F ? opATable0265[tlA] : NULL); break; + case 0x0266: instr = (tlA <= 0x0F ? opATable0266[tlA] : NULL); break; + case 0x026C: instr = (tlA <= 0x0F ? opATable026C[tlA] : NULL); break; + case 0x026D: instr = (tlA <= 0x0F ? opATable026D[tlA] : NULL); break; + case 0x026E: instr = (tlA <= 0x0F ? opATable026E[tlA] : NULL); break; + case 0x0274: instr = (tlA <= 0x0F ? opATable0274[tlA] : NULL); break; + case 0x0275: instr = (tlA <= 0x0F ? opATable0275[tlA] : NULL); break; + case 0x0276: instr = (tlA <= 0x0F ? opATable0276[tlA] : NULL); break; + case 0x027C: instr = (tlA <= 0x0F ? opATable027C[tlA] : NULL); break; + case 0x027D: instr = (tlA <= 0x0F ? opATable027D[tlA] : NULL); break; + case 0x027E: instr = (tlA <= 0x0F ? opATable027E[tlA] : NULL); break; + case 0x02C4: instr = (tlA <= 0x0F ? opATable02C4[tlA] : NULL); break; + case 0x02C5: instr = (tlA <= 0x0F ? opATable02C5[tlA] : NULL); break; + case 0x02C6: instr = (tlA <= 0x0F ? opATable02C6[tlA] : NULL); break; + case 0x02CC: instr = (tlA <= 0x0F ? opATable02CC[tlA] : NULL); break; + case 0x02CD: instr = (tlA <= 0x0F ? opATable02CD[tlA] : NULL); break; + case 0x02CE: instr = (tlA <= 0x0F ? opATable02CE[tlA] : NULL); break; + case 0x02D4: instr = (tlA <= 0x0F ? opATable02D4[tlA] : NULL); break; + case 0x02D5: instr = (tlA <= 0x0F ? opATable02D5[tlA] : NULL); break; + case 0x02D6: instr = (tlA <= 0x0F ? opATable02D6[tlA] : NULL); break; + case 0x02DC: instr = (tlA <= 0x0F ? opATable02DC[tlA] : NULL); break; + case 0x02DD: instr = (tlA <= 0x0F ? opATable02DD[tlA] : NULL); break; + case 0x02DE: instr = (tlA <= 0x0F ? opATable02DE[tlA] : NULL); break; + case 0x02E4: instr = (tlA <= 0x0F ? opATable02E4[tlA] : NULL); break; + case 0x02E5: instr = (tlA <= 0x0F ? opATable02E5[tlA] : NULL); break; + case 0x02E6: instr = (tlA <= 0x0F ? opATable02E6[tlA] : NULL); break; + case 0x02EC: instr = (tlA <= 0x0F ? opATable02EC[tlA] : NULL); break; + case 0x02ED: instr = (tlA <= 0x0F ? opATable02ED[tlA] : NULL); break; + case 0x02EE: instr = (tlA <= 0x0F ? opATable02EE[tlA] : NULL); break; + case 0x02F4: instr = (tlA <= 0x0F ? opATable02F4[tlA] : NULL); break; + case 0x02F5: instr = (tlA <= 0x0F ? opATable02F5[tlA] : NULL); break; + case 0x02F6: instr = (tlA <= 0x0F ? opATable02F6[tlA] : NULL); break; + case 0x02FC: instr = (tlA <= 0x0F ? opATable02FC[tlA] : NULL); break; + case 0x02FD: instr = (tlA <= 0x0F ? opATable02FD[tlA] : NULL); break; + case 0x02FE: instr = (tlA <= 0x0F ? opATable02FE[tlA] : NULL); break; + case 0x0304: instr = (tlA <= 0x0F ? opATable0304[tlA] : NULL); break; + case 0x0305: instr = (tlA <= 0x0F ? opATable0305[tlA] : NULL); break; + case 0x0306: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0306[tlA - 0x08] : NULL); break; + case 0x030C: instr = (tlA <= 0x0F ? opATable030C[tlA] : NULL); break; + case 0x030D: instr = (tlA <= 0x0F ? opATable030D[tlA] : NULL); break; + case 0x030E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable030E[tlA - 0x08] : NULL); break; + case 0x0314: instr = (tlA <= 0x0F ? opATable0314[tlA] : NULL); break; + case 0x0315: instr = (tlA <= 0x0F ? opATable0315[tlA] : NULL); break; + case 0x0316: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0316[tlA - 0x08] : NULL); break; + case 0x031C: instr = (tlA <= 0x0F ? opATable031C[tlA] : NULL); break; + case 0x031D: instr = (tlA <= 0x0F ? opATable031D[tlA] : NULL); break; + case 0x031E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable031E[tlA - 0x08] : NULL); break; + case 0x0324: instr = (tlA <= 0x0F ? opATable0324[tlA] : NULL); break; + case 0x0325: instr = (tlA <= 0x0F ? opATable0325[tlA] : NULL); break; + case 0x0326: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0326[tlA - 0x08] : NULL); break; + case 0x032C: instr = (tlA <= 0x0F ? opATable032C[tlA] : NULL); break; + case 0x032D: instr = (tlA <= 0x0F ? opATable032D[tlA] : NULL); break; + case 0x032E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable032E[tlA - 0x08] : NULL); break; + case 0x0334: instr = (tlA <= 0x0F ? opATable0334[tlA] : NULL); break; + case 0x0335: instr = (tlA <= 0x0F ? opATable0335[tlA] : NULL); break; + case 0x0336: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable0336[tlA - 0x08] : NULL); break; + case 0x033C: instr = (tlA <= 0x0F ? opATable033C[tlA] : NULL); break; + case 0x033D: instr = (tlA <= 0x0F ? opATable033D[tlA] : NULL); break; + case 0x033E: instr = (tlA >= 0x08 && tlA <= 0x0F ? opATable033E[tlA - 0x08] : NULL); break; + case 0x0344: instr = (tlA <= 0x0F ? opATable0344[tlA] : NULL); break; + case 0x0345: instr = (tlA <= 0x0F ? opATable0345[tlA] : NULL); break; + case 0x0346: instr = (tlA <= 0x0F ? opATable0346[tlA] : NULL); break; + case 0x034C: instr = (tlA <= 0x0F ? opATable034C[tlA] : NULL); break; + case 0x034D: instr = (tlA <= 0x0F ? opATable034D[tlA] : NULL); break; + case 0x034E: instr = (tlA <= 0x0F ? opATable034E[tlA] : NULL); break; + case 0x0354: instr = (tlA <= 0x0F ? opATable0354[tlA] : NULL); break; + case 0x0355: instr = (tlA <= 0x0F ? opATable0355[tlA] : NULL); break; + case 0x0356: instr = (tlA <= 0x0F ? opATable0356[tlA] : NULL); break; + case 0x035C: instr = (tlA <= 0x0F ? opATable035C[tlA] : NULL); break; + case 0x035D: instr = (tlA <= 0x0F ? opATable035D[tlA] : NULL); break; + case 0x035E: instr = (tlA <= 0x0F ? opATable035E[tlA] : NULL); break; + case 0x0364: instr = (tlA <= 0x0F ? opATable0364[tlA] : NULL); break; + case 0x0365: instr = (tlA <= 0x0F ? opATable0365[tlA] : NULL); break; + case 0x0366: instr = (tlA <= 0x0F ? opATable0366[tlA] : NULL); break; + case 0x036C: instr = (tlA <= 0x0F ? opATable036C[tlA] : NULL); break; + case 0x036D: instr = (tlA <= 0x0F ? opATable036D[tlA] : NULL); break; + case 0x036E: instr = (tlA <= 0x0F ? opATable036E[tlA] : NULL); break; + case 0x0374: instr = (tlA <= 0x0F ? opATable0374[tlA] : NULL); break; + case 0x0375: instr = (tlA <= 0x0F ? opATable0375[tlA] : NULL); break; + case 0x0376: instr = (tlA <= 0x0F ? opATable0376[tlA] : NULL); break; + case 0x037C: instr = (tlA <= 0x0F ? opATable037C[tlA] : NULL); break; + case 0x037D: instr = (tlA <= 0x0F ? opATable037D[tlA] : NULL); break; + case 0x037E: instr = (tlA <= 0x0F ? opATable037E[tlA] : NULL); break; + case 0x0380: instr = opATable0380[tlA]; break; + case 0x0381: instr = opATable0381[tlA]; break; + case 0x0382: instr = opATable0382[tlA]; break; + case 0x0384: instr = opATable0384[tlA]; break; + case 0x0385: instr = opATable0385[tlA]; break; + case 0x0386: instr = opATable0386[tlA]; break; + case 0x0388: instr = opATable0388[tlA]; break; + case 0x0389: instr = opATable0389[tlA]; break; + case 0x038A: instr = opATable038A[tlA]; break; + case 0x038C: instr = opATable038C[tlA]; break; + case 0x038D: instr = opATable038D[tlA]; break; + case 0x038E: instr = opATable038E[tlA]; break; + case 0x0390: instr = opATable0390[tlA]; break; + case 0x0391: instr = opATable0391[tlA]; break; + case 0x0392: instr = opATable0392[tlA]; break; + case 0x0394: instr = opATable0394[tlA]; break; + case 0x0395: instr = opATable0395[tlA]; break; + case 0x0396: instr = opATable0396[tlA]; break; + case 0x0398: instr = opATable0398[tlA]; break; + case 0x0399: instr = opATable0399[tlA]; break; + case 0x039A: instr = opATable039A[tlA]; break; + case 0x039C: instr = opATable039C[tlA]; break; + case 0x039D: instr = opATable039D[tlA]; break; + case 0x039E: instr = opATable039E[tlA]; break; + case 0x03A0: instr = opATable03A0[tlA]; break; + case 0x03A1: instr = opATable03A1[tlA]; break; + case 0x03A2: instr = opATable03A2[tlA]; break; + case 0x03A4: instr = opATable03A4[tlA]; break; + case 0x03A5: instr = opATable03A5[tlA]; break; + case 0x03A6: instr = opATable03A6[tlA]; break; + case 0x03A8: instr = opATable03A8[tlA]; break; + case 0x03A9: instr = opATable03A9[tlA]; break; + case 0x03AA: instr = opATable03AA[tlA]; break; + case 0x03AC: instr = opATable03AC[tlA]; break; + case 0x03AD: instr = opATable03AD[tlA]; break; + case 0x03AE: instr = opATable03AE[tlA]; break; + case 0x03B0: instr = opATable03B0[tlA]; break; + case 0x03B1: instr = opATable03B1[tlA]; break; + case 0x03B2: instr = opATable03B2[tlA]; break; + case 0x03B4: instr = opATable03B4[tlA]; break; + case 0x03B5: instr = opATable03B5[tlA]; break; + case 0x03B6: instr = opATable03B6[tlA]; break; + case 0x03B8: instr = opATable03B8[tlA]; break; + case 0x03B9: instr = opATable03B9[tlA]; break; + case 0x03BA: instr = opATable03BA[tlA]; break; + case 0x03BC: instr = opATable03BC[tlA]; break; + case 0x03BD: instr = opATable03BD[tlA]; break; + case 0x03BE: instr = opATable03BE[tlA]; break; + default: instr = NULL; break; + } + } + + // Lastly, try op table B if no match found + if (instr == NULL) + { + UINT16 hdB = opcode>>12; + UINT16 tlB = (opcode>>6) & 0x3F; + switch (hdB) + { + case 0x00: instr = opBTable00[tlB]; break; + case 0x02: instr = (tlB >= 0x01 && tlB <= 0x39 ? opBTable02[tlB - 0x01] : NULL); break; + case 0x03: instr = (tlB >= 0x01 && tlB <= 0x39 ? opBTable03[tlB - 0x01] : NULL); break; + case 0x04: instr = opBTable04[tlB]; break; + case 0x05: instr = opBTable05[tlB]; break; + case 0x08: instr = opBTable08[tlB]; break; + case 0x09: instr = opBTable09[tlB]; break; + case 0x0B: instr = opBTable0B[tlB]; break; + case 0x0C: instr = opBTable0C[tlB]; break; + case 0x0D: instr = opBTable0D[tlB]; break; + case 0x0E: instr = (tlB >= 0x03 && tlB <= 0x1F ? opBTable0E[tlB - 0x03] : NULL); break; + default: instr = NULL; break; + } + } + + if (instr == NULL) + goto invalid; + + // Split instruction into mnemonic and operands and substitute any data and address mode tags + const char *p, *q; + char *r; + char opsCopy[255]; + char sizeC; + UINT8 addrMode; + int part; + EOpFlags opFlags; + + // TODO - address mode masks for each instruction + + q = instr; + if (p = strchr(q, ' ')) + { + // Split instruction + strncpy(mnemonic, instr, p - q); + mnemonic[p - q] = '\0'; + operands[0] = '\0'; + opsCopy[0] = '\0'; + + // Get size character in nmnemonic, if any, otherwise assume word + if (q = strchr(mnemonic, '.')) + sizeC = tolower(q[1]); + else + sizeC = 'w'; + + // Substitute any data tags + q = p + 1; + r = opsCopy; + while (p = strchr(q, '[')) + { + strncpy(r, q, p - q); + r[p - q] = '\0'; + r += strlen(r); + + // TODO - get rid of part indices - not needed for 68000 instruction set (only needed for 020 or 030 etc) + if (sscanf(p, "[BN%d]", &part) == 1 || + sscanf(p, "[DB%d]", &part) == 1 || sscanf(p, "[DW%d]", &part) == 1 || sscanf(p, "[DL%d]", &part) == 1) + { + // Bit number or byte, word or long immediate data + if (p[2] == 'N') + { + // Check first byte is zero + u8 = (UINT8)ReadMem(addr + offset++, 1); + if (u8 != 0) + { + offset++; + goto invalid; + } + // Get data byte + u8 = (UINT8)ReadMem(addr + offset++, 1); + // Check top 3 bits are zero + if (u8&0xE0) + goto invalid; + FormatData(r, 1, u8); + //sprintf(r, "0x%02X", u8); + } + else if (p[2] == 'B') + { + // Check first byte is zero + u8 = (UINT8)ReadMem(addr + offset++, 1); + if (u8 != 0) + { + offset++; + goto invalid; + } + // Get data byte + u8 = (UINT8)ReadMem(addr + offset++, 1); + FormatData(r, 1, u8); + //i8 = (INT8)ReadMem(addr + offset++, 1); + //if (i8 < 0) + //{ + // *r++ = '-'; + // FormatData(r, 1, -i8); + // //sprintf(r, "-0x%02X", -i8); + //} + //else + //{ + // FormatData(r, 1, i8); + // //sprintf(r, "0x%02X", i8); + //} + } + else if (p[2] == 'W') + { + u16 = (UINT16)ReadMem(addr + offset, 2); + offset += 2; + FormatData(r, 2, u16); + //i16 = (INT16)ReadMem(addr + offset, 2); + //offset += 2; + //if (i16 < 0) + //{ + // *r++ = '-'; + // FormatData(r, 2, -i16); + // //sprintf(r, "-0x%04X", -i16); + //} + //else + //{ + // FormatData(r, 2, i16); + // //sprintf(r, "0x%04X", i16); + //} + } + else + { + u32 = (UINT32)ReadMem(addr + offset, 4); + offset += 4; + FormatData(r, 4, u32); + //i32 = (INT32)ReadMem(addr + offset, 4); + //offset += 4; + //if (i32 < 0) + //{ + // *r++ = '-'; + // FormatData(r, 4, -i32); + // //sprintf(r, "-0x%08X", -i32); + //} + //else + //{ + // FormatData(r, 4, i32); + // //sprintf(r, "0x%08X", i32); + //} + } + q = p + 5; + r += strlen(r); + } + else if (sscanf(p, "[LL%d]", &part) == 1) // TODO - this should be LW + { + // Fixed width label offset + i16 = (INT16)ReadMem(addr + offset, 2); + offset += 2; + // Offset is from PC + 2 + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(r, addr + 2 + i16, opFlags); + //sprintf(r, "0x%06X", addr + 2 + i16); // Format this way as memory bus width is only 24-bits + q = p + 5; + r += strlen(r); + } + else if (sscanf(p, "[LV%d]", &part) == 1) + { + // Variable width label offset + i8 = (INT8)(opcode&0xFF); + INT32 labOffset; + if (i8 == 0x00) + { + i16 = (INT16)ReadMem(addr + offset, 2); + offset += 2; + labOffset = i16; + } + // Following 68020+ only + //else if (i8 == 0xFF) + //{ + // i32 = (INT32)ReadMem(addr + offset, 4); + // offset += 4; + // labOffset = i32; + //*/ + else + labOffset = i8; + // Offset is from PC + 2 + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(r, addr + 2 + labOffset, opFlags); + //sprintf(r, "0x%06X", addr + 2 + labOffset); // Format this way as memory bus width is only 24-bits + q = p + 5; + r += strlen(r); + } + else + { + // TODO - if not AM or AR, goto invalid + q = p + 1; + r += strlen(r); + *r++ = '['; + *r = '\0'; + } + } + strcat(r, q); + + // Substitute any address mode tags + q = opsCopy; + r = operands; + while (p = strchr(q, '[')) + { + strncpy(r, q, p - q); + r[p - q] = '\0'; + r += strlen(r); + + if (sscanf(p, "[AM%d]", &part) == 1 || sscanf(p, "[AR%d]", &part) == 1) + { + // Part index must be 1 or 2 + if (part == 1) + addrMode = (UINT8)((opcode >> 6) & 0x3F); + else if (part == 2) + addrMode = (UINT8)(opcode & 0x3F); + else + goto invalid; + // Check if address mode parts are reversed + if (p[2] == 'R') + { + // If so, swap 3-bit mode and register info + addrMode = ((addrMode&0x7)<<3) | ((addrMode>>3)&0x7); + } + if (!FormatAddrMode(addr, opcode, offset, addrMode, sizeC, r)) + goto invalid; + q = p + 5; + r += strlen(r); + } + else + { + q = p + 1; + r += strlen(r); + *r++ = '['; + *r = '\0'; + } + } + strcat(r, q); + } + else + { + // Empty operands + strcpy(mnemonic, instr); + operands[0] = '\0'; + } + return offset; + + invalid: + mnemonic[0] = '\0'; + operands[0] = '\0'; + return -offset; + } + + bool C68KDebug::FormatAddrMode(UINT32 addr, UINT32 opcode, int &offset, UINT8 addrMode, char sizeC, char *dest) + { + UINT8 mode = (addrMode>>3)&0x07; + UINT8 reg = addrMode&0x07; + char dataStr[20]; + UINT8 ofsReg; + char ofsRegC, ofsSizeC; + INT8 i8; + INT16 i16; + INT32 i32; + UINT8 u8; + UINT16 u16; + UINT32 u32; + EOpFlags opFlags; + switch (mode) + { + case 0x00: // Dn + sprintf(dest, "D%u", reg); + break; + case 0x01: // An + sprintf(dest, "A%u", reg); + break; + case 0x02: // (An) + sprintf(dest, "(A%u)", reg); + break; + case 0x03: // (An)+ + sprintf(dest, "(A%u)+", reg); + break; + case 0x04: // -(An) + sprintf(dest, "-(A%u)", reg); + break; + case 0x05: // (d16,An) + i16 = (INT16)ReadMem(addr + offset, 2); + offset += 2; + if (i16 < 0) + { + FormatData(dataStr, 2, -i16); + sprintf(dest, "(-%s,A%u)", dataStr, reg); + //sprintf(dest, "(-0x%04X,A%u)", -i16, reg); + } + else + { + FormatData(dataStr, 2, i16); + sprintf(dest, "(%s,A%u)", dataStr, reg); + //sprintf(dest, "(0x%04X,A%u)", i16, reg); + } + break; + case 0x06: // (d8,An,Ri.z) + u8 = (UINT8)ReadMem(addr + offset++, 1); + // Check first 3-bits of first byte are zero + if (u8&0x7) + { + offset++; + return false; + } + // Get second offset register type, number and size (w or l) + ofsReg = (u8>>4)&0xF; + if (ofsReg < 8) + ofsRegC = 'D'; + else + { + ofsRegC = 'A'; + ofsReg -= 8; + } + ofsSizeC = (u8&0x8 ? 'l' : 'w'); + i8 = (INT8)ReadMem(addr + offset++, 1); + if (i8 < 0) + { + FormatData(dataStr, 1, -i8); + sprintf(dest, "(-%s,A%u,%c%u.%c)", dataStr, reg, ofsRegC, ofsReg, ofsSizeC); + //sprintf(dest, "(-0x%02X,A%u,%c%u.%c)", -i8, reg, ofsRegC, ofsReg, ofsSizeC); + } + else + { + FormatData(dataStr, 1, i8); + sprintf(dest, "(%s,A%u,%c%u.%c)", dataStr, reg, ofsRegC, ofsReg, ofsSizeC); + //sprintf(dest, "(0x%02X,A%u,%c%u.%c)", i8, reg, ofsRegC, ofsReg, ofsSizeC); + } + break; + case 0x07: + switch (reg) + { + case 0x00: // addr16 + u16 = (UINT16)ReadMem(addr + offset, 2); + offset += 2; + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(dest, u16, opFlags); + //sprintf(dest, "0x%06X", u16); // Format this way as memory bus width is 24-bits + break; + case 0x01: // addr32 + u32 = (UINT32)ReadMem(addr + offset, 4); + offset += 4; + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(dest, u32, opFlags); + //sprintf(dest, "0x%06X", u32); // Format this way as memory bus width is only 24-bits + break; + case 0x02: // d16(PC) + i16 = (INT16)ReadMem(addr + offset, 2); + offset += 2; + if (i16 < 0) + { + FormatData(dataStr, 2, -i16); + sprintf(dest, "-%s(PC)", dataStr); + //sprintf(dest, "-0x%04X(PC)", -i16); + } + else + { + FormatData(dataStr, 2, i16); + sprintf(dest, "%s(PC)", dataStr); + //sprintf(dest, "0x%04X(PC)", i16); + } + break; + case 0x03: // u8(PC,Ri.x) + u8 = (UINT8)ReadMem(addr + offset++, 1); + // Check first 3-bits of first byte are zero + if (u8&0x7) + { + offset++; + return false; + } + // Get second offset register type, number and size (w or l) + ofsReg = (u8>>4)&0xF; + if (ofsReg < 8) + ofsRegC = 'D'; + else + { + ofsRegC = 'A'; + ofsReg -= 8; + } + ofsSizeC = (u8&0x8 ? 'l' : 'w'); + i8 = (INT8)ReadMem(addr + offset++, 1); + if (i8 < 0) + { + FormatData(dataStr, 1, -i8); + sprintf(dest, "-%s(PC,%c%u.%c)", dataStr, ofsRegC, ofsReg, ofsSizeC); + //sprintf(dest, "-0x%02X(PC,%c%u.%c)", -i8, ofsRegC, ofsReg, ofsSizeC); + } + else + { + FormatData(dataStr, 1, i8); + sprintf(dest, "%s(PC,%c%u.%c)", dataStr, ofsRegC, ofsReg, ofsSizeC); + //sprintf(dest, "0x%02X(PC,%c%u.%c)", i8, ofsRegC, ofsReg, ofsSizeC); + } + break; + case 0x04: // immediate data, implied size + if (sizeC == 'b') + { + // Check first byte is zero + u8 = (UINT8)ReadMem(addr + offset++, 1); + if (u8 != 0) + { + offset++; + return false; + } + // Get data byte + u8 = (UINT8)ReadMem(addr + offset++, 1); + FormatData(dataStr, 1, u8); + sprintf(dest, "#%s", dataStr); + //i8 = (INT8)ReadMem(addr + offset++, 1); + //if (i8 < 0) + //{ + // FormatData(dataStr, 1, -i8); + // sprintf(dest, "#-%s", dataStr); + // //sprintf(dest, "#-0x%02X", -i8); + //} + //else + //{ + // FormatData(dataStr, 1, i8); + // sprintf(dest, "#%s", dataStr); + // //sprintf(dest, "#0x%02X", i8); + //} + } + else if (sizeC == 'w') + { + u16 = (UINT16)ReadMem(addr + offset, 2); + offset += 2; + FormatData(dataStr, 2, u16); + sprintf(dest, "#%s", dataStr); + //i16 = (INT16)ReadMem(addr + offset, 2); + //offset += 2; + //if (i16 < 0) + //{ + // FormatData(dataStr, 2, -i16); + // sprintf(dest, "#-%s", dataStr); + // //sprintf(dest, "#-0x%04X", -i16); + //} + //else + //{ + // FormatData(dataStr, 2, i16); + // sprintf(dest, "#%s", dataStr); + // //sprintf(dest, "#0x%04X", i16); + //} + } + else if (sizeC == 'l') + { + u32 = (UINT32)ReadMem(addr + offset, 4); + offset += 4; + FormatData(dataStr, 4, u32); + sprintf(dest, "#%s", dataStr); + //i32 = (INT32)ReadMem(addr + offset, 4); + //offset += 4; + //if (i32 < 0) + //{ + // FormatData(dataStr, 4, -i32); + // sprintf(dest, "#-%s", dataStr); + // //sprintf(dest, "#-0x%08X", -i32); + //} + //else + //{ + // FormatData(dataStr, 4, i32); + // sprintf(dest, "#%s", dataStr); + // //sprintf(dest, "#0x%08X", i32); + //} + } + else + return false; + break; + default: + return false; + } + break; + default: + return false; + } + return true; + } + + EOpFlags C68KDebug::GetOpFlags(UINT32 addr, UINT32 opcode) + { + UINT32 head = opcode>>6; + if (head == 0x013A) // Instruction is jmp + return JumpSimple; + else if (head == 0x013B) // Instruction is jsr + return JumpSub; + else if ((opcode>>12) == 0x0006) // Instruction is bra, bsr or bCC + { + UINT32 cond = (opcode>>8)&0x000F; + if (cond == 0x0000) // Instruction is bra + return (EOpFlags)(JumpSimple | Relative); + else if (cond == 0x0001) // Instruction is bsr + return (EOpFlags)(JumpSub | Relative); + else // Instruction is bCC + return (EOpFlags)(JumpSimple | Relative | Conditional); + } + else if ((opcode&0xF0F8) == 0x50C8) // Instruction is dbCC + return (EOpFlags)(JumpLoop | Relative | Conditional); + else if (opcode == 0x4E74 || opcode == 0x4E75) // Instruction is rtr or rts + return ReturnSub; + else if (opcode == 0x4E73) // Instruction is rte + return ReturnEx; + else + return NormalOp; + } + + bool C68KDebug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) + { + if ((opcode>>7) == 0x009D) + { + // Instruction is jmp or jsr + UINT8 addrMode = (UINT8)(opcode&0x3F); + UINT8 mode = (addrMode>>3)&0x07; + UINT8 reg = addrMode&0x07; + if (mode == 0x07) + { + if (reg == 0x00) + { + jumpAddr = (UINT32)ReadMem(addr + 2, 4); + return true; + } + else if (reg == 0x01) + { + jumpAddr = (UINT32)ReadMem(addr + 2, 4); + return true; + } + } + return false; + } + else if ((opcode>>12) == 0x0006) + { + // Instruction is bra, bsr or bCC + INT8 i8 = (INT8)(opcode&0xFF); + if (i8 == 0x00) + jumpAddr = addr + 2 + (INT16)ReadMem(addr + 2, 2); + else + jumpAddr = addr + 2 + i8; + return true; + } + else if ((opcode&0xF0F8) == 0x50C8) + { + // Instruction is dbCC + jumpAddr = addr + 2 + (INT16)ReadMem(addr + 2, 2); + return true; + } + else + return false; + } + + bool C68KDebug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + if ((opcode>>6) == 0x013B) + { + // Instruction is jsr + UINT8 addrMode = (UINT8)(opcode&0x3F); + UINT8 mode = (addrMode>>3)&0x07; + UINT8 reg = addrMode&0x07; + if (mode == 0x05 || mode == 0x06) + retAddr = addr + 2; + else if (mode == 0x07) + { + if (reg == 0x00 || reg == 0x02 || reg == 0x03) + retAddr = addr + 2; + else if (reg == 0x01) + retAddr = addr + 4; + else + retAddr = addr; + } + else + retAddr = addr; + return true; + } + else if ((opcode>>8) == 0x0061) + { + // Instruction is bsr + if ((opcode&0xFF) == 0x00) + retAddr = addr + 4; + else + retAddr = addr + 2; + return true; + } + return false; + } + + bool C68KDebug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + // Check opcode is rtr, rts or rte + if (opcode != 0x4E74 && opcode != 0x4E75 && opcode != 0x4E73) + return false; + // Return address will be at top of stack for rts and stack + 2 for rtr or rte + UINT32 sp = GetSP(); + if (opcode == 0x4E75) + retAddr = (UINT32)ReadMem(sp, 4); + else + retAddr = (UINT32)ReadMem(sp + 2, 4); + return true; + } + + bool C68KDebug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr) + { + UINT32 vecAddr = ex->code * 4; + handlerAddr = (UINT32)ReadMem(vecAddr, 4); + return !!handlerAddr; + } + + bool C68KDebug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) + { + UINT32 vecAddr = (in->code + 25) * 4; + handlerAddr = (UINT32)ReadMem(vecAddr, 4); + return !!handlerAddr; + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/68KDebug.h b/Src/Debugger/CPU/68KDebug.h new file mode 100644 index 0000000..72a10ad --- /dev/null +++ b/Src/Debugger/CPU/68KDebug.h @@ -0,0 +1,68 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * 68KDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_68KDEBUG_H +#define INCLUDED_68KDEBUG_H + +#include "Debugger/CPUDebug.h" +#include "Types.h" + +namespace Debugger +{ + /* + * Base class CCPUDebug implementation for Motorola 68000 CPUs. + */ + class C68KDebug : public CCPUDebug + { + private: + bool FormatAddrMode(UINT32 addr, UINT32 opcode, int &offset, UINT8 addrMode, char sizeC, char *dest); + + protected: + virtual UINT32 GetSP() = 0; + + public: + C68KDebug(const char *name); + + // CCPUDebug methods + + int Disassemble(UINT32 addr, char *mnemonic, char *operands); + + EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode); + + bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr); + + bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr); + + bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr); + }; +} + +#endif // INCLUDED_68KDEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Musashi68KDebug.cpp b/Src/Debugger/CPU/Musashi68KDebug.cpp new file mode 100644 index 0000000..05b71a8 --- /dev/null +++ b/Src/Debugger/CPU/Musashi68KDebug.cpp @@ -0,0 +1,203 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Musashi68KDebug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Musashi68KDebug.h" + +#include +#include + +namespace Debugger +{ + UINT32 CMusashi68KDebug::GetReg(CCPUDebug *cpu, unsigned id) + { + CMusashi68KDebug *m68K = (CMusashi68KDebug*)cpu; + return M68KGetRegister(m68K->m_ctx, id); + } + + bool CMusashi68KDebug::SetReg(CCPUDebug *cpu, unsigned id, UINT32 data) + { + CMusashi68KDebug *m68K = (CMusashi68KDebug*)cpu; + m68K->SetM68KContext(); + bool okay = M68KSetRegister(m68K->m_ctx, id, data); + m68K->RestoreM68KContext(); + return okay; + } + + static const char *srGroup = "Special Registers"; + static const char *drGroup = "Data Registers"; + static const char *arGroup = "Address Regsters"; + + CMusashi68KDebug::CMusashi68KDebug(const char *name, M68KCtx *ctx) : C68KDebug(name), m_ctx(ctx), m_resetAddr(0) + { + // Special registers + AddPCRegister ("PC", srGroup); + AddAddrRegister ("SP", srGroup, DBG68K_REG_SP, GetReg, SetReg); + AddStatus32Register("SR", srGroup, DBG68K_REG_SR, "TtSM.210...XNZVC", GetReg, SetReg); + + // Data registers + AddInt32Register("D0", drGroup, DBG68K_REG_D0, GetReg, SetReg); + AddInt32Register("D1", drGroup, DBG68K_REG_D1, GetReg, SetReg); + AddInt32Register("D2", drGroup, DBG68K_REG_D2, GetReg, SetReg); + AddInt32Register("D3", drGroup, DBG68K_REG_D3, GetReg, SetReg); + AddInt32Register("D4", drGroup, DBG68K_REG_D4, GetReg, SetReg); + AddInt32Register("D5", drGroup, DBG68K_REG_D5, GetReg, SetReg); + AddInt32Register("D6", drGroup, DBG68K_REG_D6, GetReg, SetReg); + AddInt32Register("D7", drGroup, DBG68K_REG_D7, GetReg, SetReg); + + // Address registers + AddInt32Register("A0", arGroup, DBG68K_REG_A0, GetReg, SetReg); + AddInt32Register("A1", arGroup, DBG68K_REG_A1, GetReg, SetReg); + AddInt32Register("A2", arGroup, DBG68K_REG_A2, GetReg, SetReg); + AddInt32Register("A3", arGroup, DBG68K_REG_A3, GetReg, SetReg); + AddInt32Register("A4", arGroup, DBG68K_REG_A4, GetReg, SetReg); + AddInt32Register("A5", arGroup, DBG68K_REG_A5, GetReg, SetReg); + AddInt32Register("A6", arGroup, DBG68K_REG_A6, GetReg, SetReg); + AddInt32Register("A7", arGroup, DBG68K_REG_A7, GetReg, SetReg); + } + + CMusashi68KDebug::~CMusashi68KDebug() + { + DetachFromCPU(); + } + + UINT32 CMusashi68KDebug::GetSP() + { + return M68KGetRegister(m_ctx, DBG68K_REG_SP); + } + + void CMusashi68KDebug::AttachToCPU() + { + if (m_ctx->Debug != NULL) + DetachFromCPU(); + m_ctx->Debug = this; + m_bus = m_ctx->Bus; + m_ctx->Bus = this; + + // Reset address is held at 0x000004 + m_resetAddr = m_bus->Read32(0x000004); + } + + void CMusashi68KDebug::DetachFromCPU() + { + if (m_ctx->Debug == NULL) + return; + m_ctx->Bus = m_bus; + m_bus = NULL; + m_ctx->Debug = NULL; + } + + UINT32 CMusashi68KDebug::GetResetAddr() + { + return m_resetAddr; + } + + bool CMusashi68KDebug::UpdatePC(UINT32 newPC) + { + // TODO + return false; + } + + bool CMusashi68KDebug::ForceException(CException *ex) + { + // TODO + return false; + } + + bool CMusashi68KDebug::ForceInterrupt(CInterrupt *in) + { + if (in->code > 6) + return false; + M68KSetIRQ(in->code + 1); + return true; + } + + UINT64 CMusashi68KDebug::ReadMem(UINT32 addr, unsigned dataSize) + { + switch (dataSize) + { + case 1: return (UINT64)m_bus->Read8(addr); + case 2: return (UINT64)m_bus->Read16(addr); + case 4: return (UINT64)m_bus->Read32(addr); + default: return CCPUDebug::ReadMem(addr, dataSize); + } + } + + bool CMusashi68KDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) + { + switch (dataSize) + { + case 1: m_bus->Write8(addr, (UINT8)data); return true; + case 2: m_bus->Write16(addr, (UINT16)data); return true; + case 4: m_bus->Write32(addr, (UINT32)data); return true; + case 8: return CCPUDebug::WriteMem(addr, dataSize, data); + default: return false; + } + } + + // IBus methods + + UINT8 CMusashi68KDebug::Read8(UINT32 addr) + { + UINT8 data = m_bus->Read8(addr); + CheckRead8(addr, data); + return data; + } + + void CMusashi68KDebug::Write8(UINT32 addr, UINT8 data) + { + m_bus->Write8(addr, data); + CheckWrite8(addr, data); + } + + UINT16 CMusashi68KDebug::Read16(UINT32 addr) + { + UINT16 data = m_bus->Read16(addr); + CheckRead16(addr, data); + return data; + } + + void CMusashi68KDebug::Write16(UINT32 addr, UINT16 data) + { + m_bus->Write16(addr, data); + CheckWrite16(addr, data); + } + + UINT32 CMusashi68KDebug::Read32(UINT32 addr) + { + UINT32 data = m_bus->Read32(addr); + CheckRead32(addr, data); + return data; + } + + void CMusashi68KDebug::Write32(UINT32 addr, UINT32 data) + { + m_bus->Write32(addr, data); + CheckWrite32(addr, data); + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Musashi68KDebug.h b/Src/Debugger/CPU/Musashi68KDebug.h new file mode 100644 index 0000000..02357ad --- /dev/null +++ b/Src/Debugger/CPU/Musashi68KDebug.h @@ -0,0 +1,118 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Musashi68KDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_MUSASHI68KDEBUG_H +#define INCLUDED_MUSASHI68KDEBUG_H + +#include "68KDebug.h" +#include "CPU/Bus.h" +#include "CPU/68K/68K.h" +#include "Types.h" + +// TODO - can't get this namespace to work with 68K.h for some reason - strange Visual Studio errors +namespace Debugger +{ + /* + * CCPUDebug implementation for the Musashi Motorola 68000 emulator. + */ + class CMusashi68KDebug : public C68KDebug, public ::IBus + { + private: + static UINT32 GetReg(CCPUDebug *cpu, unsigned id); + + static bool SetReg(CCPUDebug *cpu, unsigned id, UINT32 data); + + M68KCtx *m_ctx; + UINT32 m_resetAddr; + + M68KCtx m_savedCtx; + + ::IBus *m_bus; + + // Inlined methods + + void SetM68KContext() + { + M68KGetContext(&m_savedCtx); + if (m_savedCtx.Debug != this) + M68KSetContext(m_ctx); + } + + void UpdateM68KContext(M68KCtx *ctx) + { + m_ctx = ctx; + } + + void RestoreM68KContext() + { + if (m_savedCtx.Debug != this) + M68KSetContext(&m_savedCtx); + } + + protected: + UINT32 GetSP(); + + public: + CMusashi68KDebug(const char *name, M68KCtx *m68K); + + virtual ~CMusashi68KDebug(); + + // CCPUDebug methods + + void AttachToCPU(); + + void DetachFromCPU(); + + UINT32 GetResetAddr(); + + bool UpdatePC(UINT32 pc); + + bool ForceException(CException *ex); + + bool ForceInterrupt(CInterrupt *in); + + UINT64 ReadMem(UINT32 addr, unsigned dataSize); + + bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data); + + // IBus methods + + UINT8 Read8(UINT32 addr); + + void Write8(UINT32 addr, UINT8 data); + + UINT16 Read16(UINT32 addr); + + void Write16(UINT32 addr, UINT16 data); + + UINT32 Read32(UINT32 addr); + + void Write32(UINT32 addr, UINT32 data); + }; +} + +#endif // INCLUDED_MUSASHI68KDEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/PPCDebug.cpp b/Src/Debugger/CPU/PPCDebug.cpp new file mode 100644 index 0000000..7caa8e6 --- /dev/null +++ b/Src/Debugger/CPU/PPCDebug.cpp @@ -0,0 +1,554 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * PPCDebug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "PPCDebug.h" +#include "CPU/PowerPC/ppc.h" +#include "CPU/PowerPC/PPCDisasm.h" + +#include +#include + +#define M_AA 0x00000002 +#define M_LK 0x00000001 +#define M_BO 0x03E00000 +#define M_BD 0x0000FFFC +#define M_LI 0x03FFFFFC + +#define MSR_IP 0x00000040 + +namespace Debugger +{ + static UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id) + { + switch (id) + { + case PPCSPECIAL_LR: return ::ppc_get_lr(); + case PPCSPECIAL_FPSCR: return 0; // TODO + case PPCSPECIAL_MSR: return ::ppc_read_msr(); + default: return 0; + } + } + + static bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data) + { + switch (id) + { + case PPCSPECIAL_LR: /* TODO */ return false; + case PPCSPECIAL_FPSCR: /* TODO */ return false; + default: return false; + } + } + + static UINT8 GetCR(CCPUDebug *cpu, unsigned id) + { + return ::ppc_get_cr(id); + } + + static bool SetCR(CCPUDebug *cpu, unsigned id, UINT8 data) + { + ::ppc_set_cr(id, data); + return true; + } + + static UINT32 GetSPR(CCPUDebug *cpu, unsigned id) + { + return ::ppc_read_spr(id); + } + + static bool SetSPR(CCPUDebug *cpu, unsigned id, UINT32 data) + { + ::ppc_write_spr(id, data); + return true; + } + + static UINT32 GetGPR(CCPUDebug *cpu, unsigned id) + { + return ::ppc_get_gpr(id); + } + + static bool SetGPR(CCPUDebug *cpu, unsigned id, UINT32 data) + { + ::ppc_set_gpr(id, data); + return true; + } + + static double GetFPR(CCPUDebug *cpu, unsigned id) + { + return ::ppc_get_fpr(id); + } + + static bool SetFPR(CCPUDebug *cpu, unsigned id, double data) + { + ::ppc_set_fpr(id, data); + return true; + } + + static const char *srGroup = "Special Registers"; + static const char *crGroup = "Condition Registers"; + static const char *grGroup = "GPR Registers"; + static const char *frGroup = "FPR Registers"; + + CPPCDebug::CPPCDebug(const char *name) : CCPUDebug("PPC", name, 4, 4, true, 32, 7), m_irqState(0) + { + // PC & Link registers + AddPCRegister ("pc", srGroup); + AddAddrRegister("lr", srGroup, PPCSPECIAL_LR, GetSpecialReg, SetSpecialReg); + + // SPR registers + AddInt32Register ("ctr", srGroup, SPR_LR, GetSPR, SetSPR); + AddInt32Register ("xer", srGroup, SPR_XER, GetSPR, SetSPR); + //AddStatus32Register("xer", srGroup, SPR_XER, "SOC", GetSPR, SetSPR); //TODO: bit mapping is wrong + AddInt32Register ("srr0", srGroup, SPR_SRR0, GetSPR, SetSPR); + AddInt32Register ("srr1", srGroup, SPR_SRR1, GetSPR, SetSPR); + AddInt32Register ("msr", srGroup, PPCSPECIAL_MSR, GetSpecialReg, SetSpecialReg); + AddInt32Register ("sdr1", srGroup, SPR603E_SDR1, GetSPR, SetSPR); + AddInt32Register ("imiss",srGroup, SPR603E_IMISS, GetSPR, SetSPR); + AddInt32Register ("dmiss",srGroup, SPR603E_DMISS, GetSPR, SetSPR); + AddInt32Register ("hid0", srGroup, SPR603E_HID0, GetSPR, SetSPR); + AddInt32Register ("hid1", srGroup, SPR603E_HID1, GetSPR, SetSPR); + + // etc... + + // Condition registers + for (unsigned id = 0; id < 8; id++) + { + sprintf(m_crNames[id], "cr%u", id); + AddStatus8Register(m_crNames[id], crGroup, id, "<>=O", GetCR, SetCR); + } + //AddStatus16Register("fpscr", "Condition Registers", PPCSPECIAL_FPSCR, "FEVOUZX789ABCRI 0123", GetSpecial, SetSpecial); + + // GPR registers + for (unsigned id = 0; id < 32; id++) + { + sprintf(m_gprNames[id], "r%u", id); + AddInt32Register(m_gprNames[id], grGroup, id, GetGPR, SetGPR); + } + + // FPR registers + for (unsigned id = 0; id < 32; id++) + { + sprintf(m_fprNames[id], "f%u", id); + AddFPointRegister(m_fprNames[id], frGroup, id, GetFPR, SetFPR); + } + + // Exceptions + AddException("IRQ", EXCEPTION_IRQ, "External Interrupt"); + AddException("DEC", EXCEPTION_DECREMENTER, "Decrement Overflow"); + AddException("TRAP", EXCEPTION_TRAP, "Program Exception/Trap"); + AddException("SYSCALL", EXCEPTION_SYSTEM_CALL, "System Call"); + AddException("SMI", EXCEPTION_SMI, "SMI"); + AddException("DSI", EXCEPTION_DSI, "DSI"); + AddException("ISI", EXCEPTION_ISI, "ISI"); + } + + CPPCDebug::~CPPCDebug() + { + DetachFromCPU(); + } + + void CPPCDebug::AttachToCPU() + { + ::ppc_attach_debugger(this); + } + + ::IBus *CPPCDebug::AttachBus(::IBus *bus) + { + m_bus = bus; + return this; + } + + void CPPCDebug::DetachFromCPU() + { + ::ppc_detach_debugger(); + } + + ::IBus *CPPCDebug::DetachBus() + { + ::IBus *bus = m_bus; + m_bus = NULL; + return bus; + } + + UINT32 CPPCDebug::GetResetAddr() + { + // Reset address appears to be hardcoded to 0xFFF00100 + return 0xFFF00100; + } + + bool CPPCDebug::UpdatePC(UINT32 pc) + { + ::ppc_set_pc(pc); + return true; + } + + bool CPPCDebug::ForceException(CException *ex) + { + // TODO - no way to force exceptions + return false; + } + + bool CPPCDebug::ForceInterrupt(CInterrupt *in) + { + if (in->code > 7) + return false; + UINT8 irqState = m_bus->Read8(0xF0100018) | 1<code; + m_bus->Write8(0xF0100018, irqState); + ::ppc_set_irq_line(1); // TODO - what is irqline arg for? not actually used? + return true; + } + + UINT64 CPPCDebug::ReadMem(UINT32 addr, unsigned dataSize) + { + switch (dataSize) + { + case 1: return (UINT64)m_bus->Read8(addr); + case 2: return (UINT64)m_bus->Read16(addr); + case 4: return (UINT64)m_bus->Read32(addr); + case 8: return m_bus->Read64(addr); + default: return 0; + } + } + + bool CPPCDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) + { + switch (dataSize) + { + case 1: m_bus->Write8(addr, (UINT8)data); return true; + case 2: m_bus->Write16(addr, (UINT16)data); return true; + case 4: m_bus->Write32(addr, (UINT32)data); return true; + case 8: m_bus->Write64(addr, data); return true; + default: return false; + } + } + + void CPPCDebug::CheckException(UINT16 exCode) + { + CCPUDebug::CPUException(exCode); + + if (exCode == EXCEPTION_IRQ) + { + UINT8 irqState = m_bus->Read8(0xF0100018); // TODO - replace this with function pointer + + UINT8 newIRQs = (irqState^m_irqState)&irqState; + for (int intCode = 0; newIRQs && intCode < 8; intCode++) + { + if (newIRQs&0x01) + CPUInterrupt(intCode); + newIRQs >>= 1; + } + m_irqState = irqState; + } + } + + int CPPCDebug::Disassemble(UINT32 addr, char *mnemonic, char *operands) + { + char opStr[255]; + char valStr[40]; + UINT32 opcode = m_bus->Read32(addr); + operands[0] = '\0'; + if (!::DisassemblePowerPC(opcode, addr, mnemonic, opStr, true)) + { + char *o = opStr; + char *s = strstr(o, "0x"); + while (s) + { + strncpy(operands, o, s - o); + operands[s - o] = '\0'; + s += 2; + + char *p = s; + unsigned len = 0; + UINT64 data = 0; + while (p) + { + char c = toupper(*(p++)); + if (c >= '0' && c <= '9') + { + data <<= 4; + data += (UINT64)(c - '0'); + } + else if (c >= 'A' && c <= 'F') + { + data <<= 4; + data += (UINT64)(10 + c - 'A'); + } + else + break; + len++; + } + + unsigned dataSize = (p - s) / 2; + if (dataSize == (unsigned)(memBusWidth / 8)) + { + EOpFlags opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(valStr, (UINT32)data, opFlags); + } + else + FormatData(valStr, dataSize, data); + + strcat(operands, valStr); + operands += strlen(operands); + o = p - 1; + s = strstr(o, "0x"); + } + strcat(operands, o); + return 4; + } + else + return -4; + } + + EOpFlags CPPCDebug::GetOpFlags(UINT32 addr, UINT32 opcode) + { + EOpFlags opFlags; + + UINT32 op = opcode>>26; + if (op == 0x10) + { + // Instruction is branch conditional: bc, bca, bcl or bcla + UINT32 bo = (opcode&M_BO)>>21; + if (opcode&M_LK) + { + if (opcode&M_AA) + opFlags = JumpSub; // bcla + else if (bo&0x04) + opFlags = (EOpFlags)(JumpSub | Relative); // bcl without counter + else + opFlags = (EOpFlags)(JumpSub | Relative); // bcl with counter + } + else + { + if (opcode&M_AA) + opFlags = JumpSimple; // bca + else if (bo&0x04) + opFlags = (EOpFlags)(JumpSimple | Relative); // bc without counter + else + opFlags = (EOpFlags)(JumpLoop | Relative); // bc with counter + } + // Check BO is not just branch always + return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); + } + else if (op == 0x12) + { + // Instruction is branch: b, ba, bl or bla + if (opcode&M_LK) + { + if (opcode&M_AA) + return JumpSub; // bla + else + return (EOpFlags)(JumpSub | Relative); // bl + } + else + { + if (opcode&M_AA) + return JumpSimple; // ba + else + return (EOpFlags)(JumpSimple | Relative); // b + } + } + else if (op == 0x13) + { + UINT32 exOp = (opcode>>1)&0x3ff; + UINT32 bo = (opcode&M_BO)>>21; + if (exOp == 0x0210) + { + // Instruction is branch conditional to count register: bcctr or bcctrl + if (opcode&M_LK) + opFlags = (EOpFlags)(JumpSub | Relative); // bcctrl + else if (bo&0x04) + opFlags = (EOpFlags)(JumpSimple | Relative); // bcctr without counter + else + opFlags = (EOpFlags)(JumpLoop | Relative); // bcctr with counter + // Check BO is not just branch always + return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); + } + else if (exOp == 0x0010) + { + // Instruction is branch conditional to link register: bclr or bclrl + if (opcode&M_LK) + opFlags = (EOpFlags)(JumpSub | ReturnSub); // bclrl + else + opFlags = ReturnSub; // bclr + // Check BO is not just branch always + return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); + } + else if (exOp == 0x0032) + { + // Instruction is return from interrupt: rfi + return ReturnEx; + } + // TODO - traps etc + } + return NormalOp; + } + + bool CPPCDebug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) + { + // Check instruction is one of following branches: b, ba, bl, bla, bc, bca, bcl or bcla + UINT32 disp; + UINT32 op = opcode>>26; + if (op == 0x10) + { + // Instruction is b, ba, bl or bla, so calculate branch displacement + disp = ((opcode&M_BD)>>2) * 4; + if (disp & 0x00008000) + disp |= 0xFFFF0000; // Sign extended + if (opcode&M_AA) + jumpAddr = disp; // ba or bla + else + jumpAddr = addr + disp; // b or bl + return true; + } + else if (op == 0x12) + { + // Instruction is bc, bca, bcl or bcla, so calculate branch displacement + disp = ((opcode&M_LI) >> 2) * 4; + if (disp & 0x02000000) + disp |= 0xFC000000; // Sign extended + if (opcode&M_AA) + jumpAddr = disp; // bca or bcla + else + jumpAddr = addr + disp; // bc or bcl + return true; + } + return false; + } + + bool CPPCDebug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + UINT32 op = opcode>>26; + if ((op == 0x10 || op == 0x12) && (opcode&M_LK)) + { + // Instruction is bl, bla, bcl or bcla (TODO - add bclrl?) + retAddr = addr + 4; + return true; + } + return false; + } + + bool CPPCDebug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + // Check instruction is one of following: bclr, bclrl or rfi + if ((opcode>>26) != 0x13) + return false; + + UINT32 exOp = (opcode>>1)&0x3ff; + if (exOp == 0x0010) + { + // For bclr and blclr, return address is in link register + retAddr = ::ppc_get_lr(); + return true; + } + else if (exOp == 0x0032) + { + // For rfi, return address is in SRR0 + retAddr = ::ppc_read_spr(SPR_SRR0); + return true; + } + return false; + } + + bool CPPCDebug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr) + { + UINT32 msr = ::ppc_read_msr(); + UINT32 base = (msr&MSR_IP ? 0xFFF00000 : 0x00000000); + switch (ex->code) + { + case EXCEPTION_DSI: handlerAddr = base + 0x0300; return true; + case EXCEPTION_ISI: handlerAddr = base + 0x0400; return true; + case EXCEPTION_IRQ: handlerAddr = base + 0x0500; return true; + case EXCEPTION_TRAP: handlerAddr = base + 0x0700; return true; + case EXCEPTION_DECREMENTER: handlerAddr = base + 0x0900; return true; + case EXCEPTION_SYSTEM_CALL: handlerAddr = base + 0x0C00; return true; + case EXCEPTION_SMI: handlerAddr = base + 0x1400; return true; + default: return false; + } + } + + bool CPPCDebug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) + { + UINT32 msr = ::ppc_read_msr(); + handlerAddr = (msr&MSR_IP ? 0xFFF00500 : 0x00000500); + return true; + } + + // IBus methods + + UINT8 CPPCDebug::Read8(UINT32 addr) + { + UINT8 data = m_bus->Read8(addr); + CheckRead8(addr, data); + return data; + } + + UINT16 CPPCDebug::Read16(UINT32 addr) + { + UINT16 data = m_bus->Read16(addr); + CheckRead16(addr, data); + return data; + } + + UINT32 CPPCDebug::Read32(UINT32 addr) + { + UINT32 data = m_bus->Read32(addr); + CheckRead32(addr, data); + return data; + } + + UINT64 CPPCDebug::Read64(UINT32 addr) + { + UINT64 data = m_bus->Read64(addr); + CheckRead64(addr, data); + return data; + } + + void CPPCDebug::Write8(UINT32 addr, UINT8 data) + { + m_bus->Write8(addr, data); + CheckWrite8(addr, data); + } + + void CPPCDebug::Write16(UINT32 addr, UINT16 data) + { + m_bus->Write16(addr, data); + CheckWrite16(addr, data); + } + + void CPPCDebug::Write32(UINT32 addr, UINT32 data) + { + m_bus->Write32(addr, data); + CheckWrite32(addr, data); + } + + void CPPCDebug::Write64(UINT32 addr, UINT64 data) + { + m_bus->Write64(addr, data); + CheckWrite64(addr, data); + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/PPCDebug.h b/Src/Debugger/CPU/PPCDebug.h new file mode 100644 index 0000000..0bc35ad --- /dev/null +++ b/Src/Debugger/CPU/PPCDebug.h @@ -0,0 +1,118 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * PPCDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_PPCDEBUG_H +#define INCLUDED_PPCDEBUG_H + +#include "Debugger/CPUDebug.h" +#include "CPU/Bus.h" +#include "Types.h" + +#define PPCSPECIAL_LR 0 +#define PPCSPECIAL_FPSCR 1 +#define PPCSPECIAL_MSR 2 + +namespace Debugger +{ + /* + * CCPUDebug implementation for the PowerPC PPC603 emulator. + */ + class CPPCDebug : public CCPUDebug, public ::IBus + { + private: + char m_crNames[32][5]; + char m_gprNames[32][4]; + char m_fprNames[32][4]; + + ::IBus *m_bus; + + UINT8 m_irqState; + + public: + CPPCDebug(const char *name); + + virtual ~CPPCDebug(); + + // CCPUDebug methods + + void AttachToCPU(); + + ::IBus *AttachBus(::IBus *bus); + + void DetachFromCPU(); + + ::IBus *DetachBus(); + + void CheckException(UINT16 exCode); + + UINT32 GetResetAddr(); + + bool UpdatePC(UINT32 pc); + + bool ForceException(CException *ex); + + bool ForceInterrupt(CInterrupt *in); + + UINT64 ReadMem(UINT32 addr, unsigned dataSize); + + bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data); + + int Disassemble(UINT32 addr, char *mnemonic, char *operands); + + EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode); + + bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr); + + bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr); + + bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr); + + // IBus methods + + UINT8 Read8(UINT32 addr); + + UINT16 Read16(UINT32 addr); + + UINT32 Read32(UINT32 addr); + + UINT64 Read64(UINT32 addr); + + void Write8(UINT32 addr, UINT8 data); + + void Write16(UINT32 addr, UINT16 data); + + void Write32(UINT32 addr, UINT32 data); + + void Write64(UINT32 addr, UINT64 data); + }; +} + +#endif // INCLUDED_PPCDEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Turbo68KDebug.cpp b/Src/Debugger/CPU/Turbo68KDebug.cpp new file mode 100644 index 0000000..a0e22b6 --- /dev/null +++ b/Src/Debugger/CPU/Turbo68KDebug.cpp @@ -0,0 +1,283 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Turbo68KDebug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Turbo68KDebug.h" + +#include +#include + +namespace Debugger +{ + UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id) + { + switch (id) + { + case TBO68K_REG_SP: return (UINT32)turbo68kcontext_68000.a[7]; + case TBO68K_REG_SR: return (UINT32)turbo68kcontext_68000.sr; + default: return 0; + } + } + + bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data) + { + switch (id) + { + case TBO68K_REG_SP: turbo68kcontext_68000.a[7] = data; return true; + case TBO68K_REG_SR: turbo68kcontext_68000.sr = data; return true; + default: return false; + } + } + + UINT32 GetDataReg(CCPUDebug *cpu, unsigned id) + { + return (UINT32)turbo68kcontext_68000.d[id]; + } + + bool SetDataReg(CCPUDebug *cpu, unsigned id, UINT32 data) + { + turbo68kcontext_68000.d[id] = data; + return true; + } + + UINT32 GetAddressReg(CCPUDebug *cpu, unsigned id) + { + return (UINT32)turbo68kcontext_68000.a[id]; + } + + bool SetAddressReg(CCPUDebug *cpu, unsigned id, UINT32 data) + { + turbo68kcontext_68000.a[id] = data; + return true; + } + + static const char *srGroup = "Special Registers"; + static const char *drGroup = "Data Registers"; + static const char *arGroup = "Address Regsters"; + + CTurbo68KDebug::CTurbo68KDebug(const char *name) : C68KDebug(name), m_resetAddr(0) + { + // Special registers + AddPCRegister ("PC", srGroup); + AddAddrRegister ("SP", srGroup, TBO68K_REG_SP, GetSpecialReg, SetSpecialReg); + AddStatus32Register("SR", srGroup, TBO68K_REG_SR, "TtSM.210...XNZVC", GetSpecialReg, SetSpecialReg); + + // Data registers + for (unsigned id = 0; id < 8; id++) + { + sprintf(m_drNames[id], "D%u", id); + AddInt32Register(m_drNames[id], drGroup, id, GetDataReg, SetDataReg); + } + + // Address registers + for (unsigned id = 0; id < 8; id++) + { + sprintf(m_arNames[id], "A%u", id); + AddInt32Register(m_arNames[id], arGroup, id, GetAddressReg, SetAddressReg); + } + } + + CTurbo68KDebug::~CTurbo68KDebug() + { + DetachFromCPU(); + } + + bool __cdecl DebugHandler(TURBO68K_INT32 pc, TURBO68K_INT32 opcode) + { + // Return true to let Turbo68K know if PC was changed by user + return debug->CPUExecute((UINT32)pc, (UINT32)opcode, 1); // TODO - lastCycles + } + + void __cdecl InterruptHandler(TURBO68K_UINT32 intVec) + { + // TODO - is following correct handling of interrupts? - need to check readme file + if (intVec > 47) + return; + else if (intVec >= 25 || intVec < 32) + { + debug->CPUException(25); + debug->CPUInterrupt((UINT16)intVec - 25); + } + else + debug->CPUException((UINT16)intVec); + + if (origIntAckPtr != NULL) + origIntAckPtr(intVec); + } + + void CTurbo68KDebug::AttachToCPU() + { + if (debug != NULL) + DetachFromCPU(); + + debug = this; + + origDebugPtr = (DebugPtr)turbo68kcontext_68000.Debug; + origIntAckPtr = (IntAckPtr)turbo68kcontext_68000.InterruptAcknowledge; + + origRead8Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadByte(TURBO68K_NULL); + origRead16Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadWord(TURBO68K_NULL); + origRead32Regions = (TURBO68K_DATAREGION*)Turbo68KGetReadLong(TURBO68K_NULL); + origWrite8Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteByte(TURBO68K_NULL); + origWrite16Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteWord(TURBO68K_NULL); + origWrite32Regions = (TURBO68K_DATAREGION*)Turbo68KGetWriteLong(TURBO68K_NULL); + + turbo68kcontext_68000.Debug = DebugHandler; + turbo68kcontext_68000.InterruptAcknowledge = InterruptHandler; + + debugRead8Regions[0].handler = ReadByteDebug; + debugRead16Regions[0].handler = ReadWordDebug; + debugRead32Regions[0].handler = ReadLongDebug; + debugWrite8Regions[0].handler = WriteByteDebug; + debugWrite16Regions[0].handler = WriteWordDebug; + debugWrite32Regions[0].handler = WriteLongDebug; + + Turbo68KSetReadByte(debugRead8Regions, TURBO68K_NULL); + Turbo68KSetReadWord(debugRead16Regions, TURBO68K_NULL); + Turbo68KSetReadLong(debugRead32Regions, TURBO68K_NULL); + Turbo68KSetWriteByte(debugWrite8Regions, TURBO68K_NULL); + Turbo68KSetWriteWord(debugWrite16Regions, TURBO68K_NULL); + Turbo68KSetWriteLong(debugWrite32Regions, TURBO68K_NULL); + + // Reset address is held at 0x000004 + m_resetAddr = ReadLongDirect(0x000004); + } + + void CTurbo68KDebug::DetachFromCPU() + { + if (debug == NULL) + return; + + turbo68kcontext_68000.Debug = origDebugPtr; + turbo68kcontext_68000.InterruptAcknowledge = origIntAckPtr; + + Turbo68KSetReadByte(origRead8Regions, TURBO68K_NULL); + Turbo68KSetReadWord(origRead16Regions, TURBO68K_NULL); + Turbo68KSetReadLong(origRead32Regions, TURBO68K_NULL); + Turbo68KSetWriteByte(origWrite8Regions, TURBO68K_NULL); + Turbo68KSetWriteWord(origWrite16Regions, TURBO68K_NULL); + Turbo68KSetWriteLong(origWrite32Regions, TURBO68K_NULL); + + debug = NULL; + } + + UINT32 CTurbo68KDebug::GetResetAddr() + { + return m_resetAddr; + } + + UINT32 CTurbo68KDebug::GetSP() + { + return (UINT32)turbo68kcontext_68000.a[7]; + } + + bool CTurbo68KDebug::UpdatePC(UINT32 newPC) + { + turbo68kcontext_68000.pc = newPC; + return true; + } + + bool CTurbo68KDebug::ForceException(CException *ex) + { + //switch (ex->code) + //{ + // TODO + //} + return false; + } + + bool CTurbo68KDebug::ForceInterrupt(CInterrupt *in) + { + if (in->code > 6) + return false; + //Turbo68KInterrupt(in->code, TURBO64K_AUTOVECTOR); + //Turbo68KProcessInterrupts(); TODO - call this? + return false; // TODO + } + + UINT64 CTurbo68KDebug::ReadMem(UINT32 addr, unsigned dataSize) + { + switch (dataSize) + { + case 1: return ReadByteDirect(addr); + case 2: return ReadWordDirect(addr); + case 4: return ReadLongDirect(addr); + default: return CCPUDebug::ReadMem(addr, dataSize); + } + } + + bool CTurbo68KDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) + { + switch (dataSize) + { + case 1: WriteByteDirect(addr, (TURBO68K_UINT8)data); return true; + case 2: WriteWordDirect(addr, (TURBO68K_UINT16)data); return true; + case 4: WriteLongDirect(addr, (TURBO68K_UINT32)data); return true; + default: return CCPUDebug::WriteMem(addr, dataSize, data); + } + } + + TURBO68K_UINT8 __cdecl ReadByteDebug(TURBO68K_UINT32 addr) + { + TURBO68K_UINT8 data = ReadByteDirect(addr); + debug->CheckRead8((UINT32)addr, (UINT8)data); + return data; + } + + TURBO68K_UINT16 __cdecl ReadWordDebug(TURBO68K_UINT32 addr) + { + TURBO68K_UINT16 data = ReadWordDirect(addr); + debug->CheckRead16((UINT32)addr, (UINT16)data); + return data; + } + + TURBO68K_UINT32 __cdecl ReadLongDebug(TURBO68K_UINT32 addr) + { + TURBO68K_UINT32 data = ReadLongDirect(addr); + debug->CheckRead32((UINT32)addr, (UINT32)data); + return data; + } + + void __cdecl WriteByteDebug(TURBO68K_UINT32 addr, TURBO68K_UINT8 data) + { + WriteByteDirect(addr, data); + debug->CheckWrite8((UINT32)addr, (UINT8)data); + } + + void __cdecl WriteWordDebug(TURBO68K_UINT32 addr, TURBO68K_UINT16 data) + { + WriteWordDirect(addr, data); + debug->CheckWrite16((UINT32)addr, (UINT16)data); + } + + void __cdecl WriteLongDebug(TURBO68K_UINT32 addr, TURBO68K_UINT32 data) + { + WriteLongDirect(addr, data); + debug->CheckWrite32((UINT32)addr, (UINT32)data); + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Turbo68KDebug.h b/Src/Debugger/CPU/Turbo68KDebug.h new file mode 100644 index 0000000..21052a8 --- /dev/null +++ b/Src/Debugger/CPU/Turbo68KDebug.h @@ -0,0 +1,341 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Turbo68KDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_TURBO68KDEBUG_H +#define INCLUDED_TURBO68KDEBUG_H + +#include "Debugger/CPU/68KDebug.h" +#include "Types.h" + +#include "CPU/68K/Turbo68K/Turbo68K.h" + +#define TBO68K_REG_SP 0 +#define TBO68K_REG_SR 1 + +#define USE_NATIVE_READ 0 +#define USE_NATIVE_WRITE 0 + +namespace Debugger +{ + class CTurbo68KDebug; + + static CTurbo68KDebug *debug = NULL; + + typedef bool (*DebugPtr)(TURBO68K_INT32 pc, TURBO68K_INT32 opcode); + typedef void (*IntAckPtr)(TURBO68K_UINT32 intVec); + + static DebugPtr origDebugPtr = NULL; + static IntAckPtr origIntAckPtr = NULL; + + static TURBO68K_DATAREGION *origRead8Regions; + static TURBO68K_DATAREGION *origRead16Regions; + static TURBO68K_DATAREGION *origRead32Regions; + static TURBO68K_DATAREGION *origWrite8Regions; + static TURBO68K_DATAREGION *origWrite16Regions; + static TURBO68K_DATAREGION *origWrite32Regions; + + static TURBO68K_DATAREGION debugRead8Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + static TURBO68K_DATAREGION debugRead16Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + static TURBO68K_DATAREGION debugRead32Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + static TURBO68K_DATAREGION debugWrite8Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + static TURBO68K_DATAREGION debugWrite16Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + static TURBO68K_DATAREGION debugWrite32Regions[] = + { + { 0x000000, 0xFFFFFF, TURBO68K_NULL, TURBO68K_NULL }, + { -1, -1, TURBO68K_NULL, TURBO68K_NULL } + }; + + static UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id); + static bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data); + static UINT32 GetDataReg(CCPUDebug *cpu, unsigned id); + static bool SetDataReg(CCPUDebug *cpu, unsigned id, UINT32 data) ; + static UINT32 GetAddressReg(CCPUDebug *cpu, unsigned id); + static bool SetAddressReg(CCPUDebug *cpu, unsigned id, UINT32 data); + + static TURBO68K_UINT8 ReadByteDebug(TURBO68K_UINT32 addr); + static TURBO68K_UINT16 ReadWordDebug(TURBO68K_UINT32 addr); + static TURBO68K_UINT32 ReadLongDebug(TURBO68K_UINT32 addr); + static void WriteByteDebug(TURBO68K_UINT32 addr, TURBO68K_UINT8 data); + static void WriteWordDebug(TURBO68K_UINT32 addr, TURBO68K_UINT16 data); + static void WriteLongDebug(TURBO68K_UINT32 addr, TURBO68K_UINT32 data); + + static TURBO68K_UINT8 ReadByteDirect(TURBO68K_UINT32 addr); + static TURBO68K_UINT16 ReadWordDirect(TURBO68K_UINT32 addr); + static TURBO68K_UINT32 ReadLongDirect(TURBO68K_UINT32 addr); + static void WriteByteDirect(TURBO68K_UINT32 addr, TURBO68K_UINT8 data); + static void WriteWordDirect(TURBO68K_UINT32 addr, TURBO68K_UINT16 data); + static void WriteLongDirect(TURBO68K_UINT32 addr, TURBO68K_UINT32 data); + + /* + * CCPUDebug implementation for the Turbo68K Motorola 68000 emulator. + */ + class CTurbo68KDebug : public C68KDebug + { + private: + char m_drNames[8][3]; + char m_arNames[8][3]; + + UINT32 m_resetAddr; + + protected: + UINT32 GetSP(); + + public: + CTurbo68KDebug(const char *name); + + virtual ~CTurbo68KDebug(); + + // CCPUDebug methods + + void AttachToCPU(); + + void DetachFromCPU(); + + UINT32 GetResetAddr(); + + bool UpdatePC(UINT32 pc); + + bool ForceException(CException *ex); + + bool ForceInterrupt(CInterrupt *in); + + UINT64 ReadMem(UINT32 addr, unsigned dataSize); + + bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data); + }; + + // + // Inlined functions + // + + typedef TURBO68K_UINT8 (*ReadByteFPtr)(TURBO68K_UINT32); + typedef TURBO68K_UINT16 (*ReadWordFPtr)(TURBO68K_UINT32); + typedef TURBO68K_UINT32 (*ReadLongFPtr)(TURBO68K_UINT32); + typedef void (*WriteByteFPtr)(TURBO68K_UINT32, TURBO68K_UINT8); + typedef void (*WriteWordFPtr)(TURBO68K_UINT32, TURBO68K_UINT16); + typedef void (*WriteLongFPtr)(TURBO68K_UINT32, TURBO68K_UINT32); + + inline TURBO68K_UINT8 ReadByteDirect(TURBO68K_UINT32 addr) + { +#if USE_NATIVE_READ + Turbo68KSetReadByte(origRead8Regions, TURBO68K_NULL); + TURBO68K_UINT8 data = Turbo68KReadByte(addr); + Turbo68KSetReadByte(debugRead8Regions, TURBO68K_NULL); + return data; +#else + for (TURBO68K_DATAREGION *region = origRead8Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes + TURBO68K_UINT8 *dataP = (TURBO68K_UINT8*)(region->ptr + (addr^1)); + return *dataP; + } + else + { + ReadByteFPtr fPtr = (ReadByteFPtr)region->handler; + return fPtr(addr); + } + } + } + return 0; +#endif + } + + inline TURBO68K_UINT16 ReadWordDirect(TURBO68K_UINT32 addr) + { +#if USE_NATIVE_READ + Turbo68KSetReadWord(origRead16Regions, TURBO68K_NULL); + TURBO68K_UINT16 data = Turbo68KReadWord(addr); + Turbo68KSetReadWord(debugRead16Regions, TURBO68K_NULL); + return data; +#else + for (TURBO68K_DATAREGION *region = origRead16Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes + TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr); + return *dataP; + } + else + { + ReadWordFPtr fPtr = (ReadWordFPtr)region->handler; + return fPtr(addr); + } + } + } + return 0; +#endif + } + + inline TURBO68K_UINT32 ReadLongDirect(TURBO68K_UINT32 addr) + { +#if USE_NATIVE_READ + Turbo68KSetReadLong(origRead32Regions, TURBO68K_NULL); + TURBO68K_UINT32 data = Turbo68KReadLong(addr); + Turbo68KSetReadLong(debugRead32Regions, TURBO68K_NULL); + return data; +#else + for (TURBO68K_DATAREGION *region = origRead32Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when reading bytes + TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr); + return (TURBO68K_UINT32)dataP[1] | ((TURBO68K_UINT32)dataP[0])<<16; + } + else + { + ReadLongFPtr fPtr = (ReadLongFPtr)region->handler; + return fPtr(addr); + } + } + } + return 0; +#endif + } + + inline void WriteByteDirect(TURBO68K_UINT32 addr, TURBO68K_UINT8 data) + { +#if USE_NATIVE_WRITE + Turbo68KSetWriteByte(origWrite8Regions, TURBO68K_NULL); + Turbo68KWriteByte(addr, data); + Turbo68KSetWriteByte(debugWrite8Regions, TURBO68K_NULL); +#else + for (TURBO68K_DATAREGION *region = origWrite8Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes + TURBO68K_UINT8 *dataP = (TURBO68K_UINT8*)(region->ptr + (addr^1)); + *dataP = data; + return; + } + else + { + WriteByteFPtr fPtr = (WriteByteFPtr)region->handler; + fPtr(addr, data); + return; + } + } + } + return; +#endif + } + + inline void WriteWordDirect(TURBO68K_UINT32 addr, TURBO68K_UINT16 data) + { +#if USE_NATIVE_WRITE + Turbo68KSetWriteWord(origWrite16Regions, TURBO68K_NULL); + Turbo68KWriteWord(addr, data); + Turbo68KSetWriteWord(debugWrite16Regions, TURBO68K_NULL); +#else + for (TURBO68K_DATAREGION *region = origWrite16Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes + TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr); + *dataP = data; + return; + } + else + { + WriteWordFPtr fPtr = (WriteWordFPtr)region->handler; + fPtr(addr, data); + return; + } + } + } + return; +#endif + } + + inline void WriteLongDirect(TURBO68K_UINT32 addr, TURBO68K_UINT32 data) + { +#if USE_NATIVE_WRITE + Turbo68KSetWriteLong(origWrite32Regions, TURBO68K_NULL); + Turbo68KWriteLong(addr, data); + Turbo68KSetWriteLong(debugWrite32Regions, TURBO68K_NULL); +#else + for (TURBO68K_DATAREGION *region = origWrite32Regions; region->ptr != TURBO68K_NULL || region->handler != TURBO68K_NULL; region++) + { + if (region->base <= addr && addr <= region->limit) + { + if (region->ptr != TURBO68K_NULL) + { + // Turbo68K requires native memory to be byte swapped, so must reverse this when writing bytes + TURBO68K_UINT16 *dataP = (TURBO68K_UINT16*)(region->ptr + addr); + dataP[0] = data>>16; + dataP[1] = data&0xFFFF; + return; + } + else + { + WriteLongFPtr fPtr = (WriteLongFPtr)region->handler; + fPtr(addr, data); + return; + } + } + } + return; +#endif + } +} + +#endif // INCLUDED_TURBO68KDEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Z80Debug.cpp b/Src/Debugger/CPU/Z80Debug.cpp new file mode 100644 index 0000000..66f3036 --- /dev/null +++ b/Src/Debugger/CPU/Z80Debug.cpp @@ -0,0 +1,729 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Z80Debug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Z80Debug.h" +#include "CPU/Z80/Z80.h" + +#include + +namespace Debugger +{ + // Instruction templates + static char *templates[5][256] = { + { + // Table 0: single byte instructions + "NOP","LD BC,@a","LD (BC),A","INC BC","INC B","DEC B","LD B,@d","RLCA", + "EX AF,AF'","ADD HL,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,@d","RRCA", + "DJNZ @b","LD DE,@a","LD (DE),A","INC DE","INC D","DEC D","LD D,@d","RLA", + "JR @b","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,@d","RRA", + "JR NZ,@b","LD HL,@a","LD (@a),HL","INC HL","INC H","DEC H","LD H,@d","DAA", + "JR Z,@b","ADD HL,HL","LD HL,(@a)","DEC HL","INC L","DEC L","LD L,@d","CPL", + "JR NC,@b","LD SP,@a","LD (@a),A","INC SP","INC (HL)","DEC (HL)","LD (HL),@d","SCF", + "JR C,@b","ADD HL,SP","LD A,(@a)","DEC SP","INC A","DEC A","LD A,@d","CCF", + "LD B,B","LD B,C","LD B,D","LD B,E","LD B,H","LD B,L","LD B,(HL)","LD B,A", + "LD C,B","LD C,C","LD C,D","LD C,E","LD C,H","LD C,L","LD C,(HL)","LD C,A", + "LD D,B","LD D,C","LD D,D","LD D,E","LD D,H","LD D,L","LD D,(HL)","LD D,A", + "LD E,B","LD E,C","LD E,D","LD E,E","LD E,H","LD E,L","LD E,(HL)","LD E,A", + "LD H,B","LD H,C","LD H,D","LD H,E","LD H,H","LD H,L","LD H,(HL)","LD H,A", + "LD L,B","LD L,C","LD L,D","LD L,E","LD L,H","LD L,L","LD L,(HL)","LD L,A", + "LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E","LD (HL),H","LD (HL),L","HALT","LD (HL),A", + "LD A,B","LD A,C","LD A,D","LD A,E","LD A,H","LD A,L","LD A,(HL)","LD A,A", + "ADD B","ADD C","ADD D","ADD E","ADD H","ADD L","ADD (HL)","ADD A", + "ADC B","ADC C","ADC D","ADC E","ADC H","ADC L","ADC (HL)","ADC A", + "SUB B","SUB C","SUB D","SUB E","SUB H","SUB L","SUB (HL)","SUB A", + "SBC B","SBC C","SBC D","SBC E","SBC H","SBC L","SBC (HL)","SBC A", + "AND B","AND C","AND D","AND E","AND H","AND L","AND (HL)","AND A", + "XOR B","XOR C","XOR D","XOR E","XOR H","XOR L","XOR (HL)","XOR A", + "OR B","OR C","OR D","OR E","OR H","OR L","OR (HL)","OR A", + "CP B","CP C","CP D","CP E","CP H","CP L","CP (HL)","CP A", + "RET NZ","POP BC","JP NZ,@j","JP @j","CALL NZ,@j","PUSH BC","ADD @d","RST 00h", + "RET Z","RET","JP Z,@j","PFX_CB","CALL Z,@j","CALL @j","ADC @d","RST 08h", + "RET NC","POP DE","JP NC,@j","OUTA (@p)","CALL NC,@j","PUSH DE","SUB @d","RST 10h", + "RET C","EXX","JP C,@j","INA (@p)","CALL C,@j","PFX_DD","SBC @d","RST 18h", + "RET PO","POP HL","JP PO,@j","EX HL,(SP)","CALL PO,@j","PUSH HL","AND @d","RST 20h", + "RET PE","LD PC,HL","JP PE,@j","EX DE,HL","CALL PE,@j","PFX_ED","XOR @d","RST 28h", + "RET P","POP AF","JP P,@j","DI","CALL P,@j","PUSH AF","OR @d","RST 30h", + "RET M","LD SP,HL","JP M,@j","EI","CALL M,@j","PFX_FD","CP @d","RST 38h" + }, { + // Table 1: two byte instructions of form CB-XX + "RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (HL)","RLC A", + "RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (HL)","RRC A", + "RL B","RL C","RL D","RL E","RL H","RL L","RL (HL)","RL A", + "RR B","RR C","RR D","RR E","RR H","RR L","RR (HL)","RR A", + "SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (HL)","SLA A", + "SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (HL)","SRA A", + "SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (HL)","SLL A", + "SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (HL)","SRL A", + "BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A", + "BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A", + "BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A", + "BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A", + "BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A", + "BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A", + "BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A", + "BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A", + "RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A", + "RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A", + "RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A", + "RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A", + "RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A", + "RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A", + "RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A", + "RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A", + "SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A", + "SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A", + "SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A", + "SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A", + "SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A", + "SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A", + "SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A", + "SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A" + }, { + // Table 2: two byte instructions of form ED-XX + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + "IN B,(C)","OUT (C),B","SBC HL,BC","LD (@a),BC","NEG","RETN","IM 0","LD I,A", + "IN C,(C)","OUT (C),C","ADC HL,BC","LD BC,(@a)",NULL,"RETI",NULL,"LD R,A", + "IN D,(C)","OUT (C),D","SBC HL,DE","LD (@a),DE",NULL,NULL,"IM 1","LD A,I", + "IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(@a)",NULL,NULL,"IM 2","LD A,R", + "IN H,(C)","OUT (C),H","SBC HL,HL","LD (@a),HL",NULL,NULL,NULL,"RRD", + "IN L,(C)","OUT (C),L","ADC HL,HL","LD HL,(@a)",NULL,NULL,NULL,"RLD", + "IN F,(C)",NULL,"SBC HL,SP","LD (@a),SP",NULL,NULL,NULL,NULL, + "IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(@a)",NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + "LDI","CPI","INI","OUTI",NULL,NULL,NULL,NULL, + "LDD","CPD","IND","OUTD",NULL,NULL,NULL,NULL, + "LDIR","CPIR","INIR","OTIR",NULL,NULL,NULL,NULL, + "LDDR","CPDR","INDR","OTDR",NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL + }, { + // Table 3: two byte instructions of form DD-XX or FD-XX + "NOP","LD BC,@a","LD (BC),A","INC BC","INC B","DEC B","LD B,@d","RLCA", + "EX AF,AF'","ADD I?,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,@d","RRCA", + "DJNZ @b","LD DE,@a","LD (DE),A","INC DE","INC D","DEC D","LD D,@d","RLA", + "JR @b","ADD I?,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,@d","RRA", + "JR NZ,@b","LD I?,@a","LD (@a),I?","INC I?","INC I?h","DEC I?h","LD I?h,@d","DAA", + "JR Z,@b","ADD I?,I?","LD I?,(@a)","DEC I?","INC I?l","DEC I?l","LD I?l,@d","CPL", + "JR NC,@b","LD SP,@a","LD (@a),A","INC SP","INC (I?+@d)","DEC (I?+@d)","LD (I?+@d),@d","SCF", + "JR C,@b","ADD I?,SP","LD A,(@a)","DEC SP","INC A","DEC A","LD A,@d","CCF", + "LD B,B","LD B,C","LD B,D","LD B,E","LD B,I?h","LD B,I?l","LD B,(I?+@d)","LD B,A", + "LD C,B","LD C,C","LD C,D","LD C,E","LD C,I?h","LD C,I?l","LD C,(I?+@d)","LD C,A", + "LD D,B","LD D,C","LD D,D","LD D,E","LD D,I?h","LD D,I?l","LD D,(I?+@d)","LD D,A", + "LD E,B","LD E,C","LD E,D","LD E,E","LD E,I?h","LD E,I?l","LD E,(I?+@d)","LD E,A", + "LD I?h,B","LD I?h,C","LD I?h,D","LD I?h,E","LD I?h,I?h","LD I?h,I?l","LD H,(I?+@d)","LD I?h,A", + "LD I?l,B","LD I?l,C","LD I?l,D","LD I?l,E","LD I?l,I?h","LD I?l,I?l","LD L,(I?+@d)","LD I?l,A", + "LD (I?+@d),B","LD (I?+@d),C","LD (I?+@d),D","LD (I?+@d),E","LD (I?+@d),H","LD (I?+@d),L","HALT","LD (I?+@d),A", + "LD A,B","LD A,C","LD A,D","LD A,E","LD A,I?h","LD A,I?l","LD A,(I?+@d)","LD A,A", + "ADD B","ADD C","ADD D","ADD E","ADD I?h","ADD I?l","ADD (I?+@d)","ADD A", + "ADC B","ADC C","ADC D","ADC E","ADC I?h","ADC I?l","ADC (I?+@d)","ADC,A", + "SUB B","SUB C","SUB D","SUB E","SUB I?h","SUB I?l","SUB (I?+@d)","SUB A", + "SBC B","SBC C","SBC D","SBC E","SBC I?h","SBC I?l","SBC (I?+@d)","SBC A", + "AND B","AND C","AND D","AND E","AND I?h","AND I?l","AND (I?+@d)","AND A", + "XOR B","XOR C","XOR D","XOR E","XOR I?h","XOR I?l","XOR (I?+@d)","XOR A", + "OR B","OR C","OR D","OR E","OR I?h","OR I?l","OR (I?+@d)","OR A", + "CP B","CP C","CP D","CP E","CP I?h","CP I?l","CP (I?+@d)","CP A", + "RET NZ","POP BC","JP NZ,@a","JP @a","CALL NZ,@a","PUSH BC","ADD @d","RST 00h", + "RET Z","RET","JP Z,@a","PFX_CB","CALL Z,@a","CALL @a","ADC @d","RST 08h", + "RET NC","POP DE","JP NC,@a","OUTA (@p)","CALL NC,@a","PUSH DE","SUB @d","RST 10h", + "RET C","EXX","JP C,@a","INA (@p)","CALL C,@a","PFX_DD","SBC @d","RST 18h", + "RET PO","POP I?","JP PO,@a","EX I?,(SP)","CALL PO,@a","PUSH I?","AND @d","RST 20h", + "RET PE","LD PC,I?","JP PE,@a","EX DE,I?","CALL PE,@a","PFX_ED","XOR @d","RST 28h", + "RET P","POP AF","JP P,@a","DI","CALL P,@a","PUSH AF","OR @d","RST 30h", + "RET M","LD SP,I?","JP M,@a","EI","CALL M,@a","PFX_FD","CP @d","RST 38h" + }, { + // Table 4: three byte instructions of form DD-CB-XX or FD-CB-XX + "RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (I?@o)","RLC A", + "RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (I?@o)","RRC A", + "RL B","RL C","RL D","RL E","RL H","RL L","RL (I?@o)","RL A", + "RR B","RR C","RR D","RR E","RR H","RR L","RR (I?@o)","RR A", + "SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (I?@o)","SLA A", + "SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (I?@o)","SRA A", + "SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (I?@o)","SLL A", + "SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (I?@o)","SRL A", + "BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(I?@o)","BIT 0,A", + "BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(I?@o)","BIT 1,A", + "BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(I?@o)","BIT 2,A", + "BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(I?@o)","BIT 3,A", + "BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(I?@o)","BIT 4,A", + "BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(I?@o)","BIT 5,A", + "BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(I?@o)","BIT 6,A", + "BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(I?@o)","BIT 7,A", + "RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(I?@o)","RES 0,A", + "RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(I?@o)","RES 1,A", + "RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(I?@o)","RES 2,A", + "RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(I?@o)","RES 3,A", + "RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(I?@o)","RES 4,A", + "RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(I?@o)","RES 5,A", + "RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(I?@o)","RES 6,A", + "RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(I?@o)","RES 7,A", + "SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(I?@o)","SET 0,A", + "SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(I?@o)","SET 1,A", + "SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(I?@o)","SET 2,A", + "SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(I?@o)","SET 3,A", + "SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(I?@o)","SET 4,A", + "SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(I?@o)","SET 5,A", + "SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(I?@o)","SET 6,A", + "SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(I?@o)","SET 7,A" + } + }; + + UINT8 CZ80Debug::ReadReg8(CCPUDebug *cpu, unsigned reg8) + { + return ((CZ80Debug*)cpu)->m_z80->GetReg8(reg8); + } + + bool CZ80Debug::WriteReg8(CCPUDebug *cpu, unsigned reg8, UINT8 value) + { + return ((CZ80Debug*)cpu)->m_z80->SetReg8(reg8, value); + } + + UINT16 CZ80Debug::ReadReg16(CCPUDebug *cpu, unsigned reg16) + { + return ((CZ80Debug*)cpu)->m_z80->GetReg16(reg16); + } + + bool CZ80Debug::WriteReg16(CCPUDebug *cpu, unsigned reg16, UINT16 value) + { + return ((CZ80Debug*)cpu)->m_z80->SetReg16(reg16, value); + } + + static const char *crGroup = "Control Regs"; + static const char *irGroup = "Int/Refresh Regs"; + static const char *r8Group = "8-Bit Regs"; + static const char *r16Group = "16-Bit Regs"; + static const char *srGroup = "Shadow Regs"; + + CZ80Debug::CZ80Debug(const char *name, CZ80 *z80) : CCPUDebug("Z80", name, 1, 4, false, 16, 4), m_z80(z80) + { + // Main registers + AddPCRegister ("PC", crGroup); + AddInt16Register ("SP", crGroup, Z80_REG16_SP, ReadReg16, WriteReg16); + AddStatus8Register("F", crGroup, Z80_REG8_F, "SZ.HPVNC", ReadReg8, WriteReg8); + + AddStatus8Register("IFF", irGroup, Z80_REG8_IFF, "H.E.G21F", ReadReg8, WriteReg8); + AddInt8Register ("I", irGroup, Z80_REG8_I, ReadReg8, WriteReg8); + AddInt8Register ("R", irGroup, Z80_REG8_R, ReadReg8, WriteReg8); + + AddInt8Register("A", r8Group, Z80_REG8_A, ReadReg8, WriteReg8); + AddInt8Register("B", r8Group, Z80_REG8_B, ReadReg8, WriteReg8); + AddInt8Register("C", r8Group, Z80_REG8_C, ReadReg8, WriteReg8); + AddInt8Register("D", r8Group, Z80_REG8_D, ReadReg8, WriteReg8); + AddInt8Register("E", r8Group, Z80_REG8_E, ReadReg8, WriteReg8); + + AddInt16Register("AF", r16Group, Z80_REG16_AF, ReadReg16, WriteReg16); + AddInt16Register("BC", r16Group, Z80_REG16_BC, ReadReg16, WriteReg16); + AddInt16Register("DE", r16Group, Z80_REG16_DE, ReadReg16, WriteReg16); + AddInt16Register("HL", r16Group, Z80_REG16_HL, ReadReg16, WriteReg16); + AddInt16Register("IX", r16Group, Z80_REG16_IX, ReadReg16, WriteReg16); + AddInt16Register("IY", r16Group, Z80_REG16_IY, ReadReg16, WriteReg16); + + AddInt16Register("AF'", srGroup, Z80_REG16_AF_, ReadReg16, WriteReg16); + AddInt16Register("BC'", srGroup, Z80_REG16_BC_, ReadReg16, WriteReg16); + AddInt16Register("DE'", srGroup, Z80_REG16_DE_, ReadReg16, WriteReg16); + AddInt16Register("HL'", srGroup, Z80_REG16_HL_, ReadReg16, WriteReg16); + + // Exceptions + AddException("NMI", Z80_EX_NMI, "Non-Maskable Interrupt"); + AddException("RST00", Z80_INT_RST_00, "RST 00h (IM0)"); + AddException("RST08", Z80_INT_RST_08, "RST 08h (IM0)"); + AddException("RST10", Z80_INT_RST_10, "RST 10h (IM0)"); + AddException("RST18", Z80_INT_RST_18, "RST 18h (IM0)"); + AddException("RST20", Z80_INT_RST_20, "RST 20h (IM0)"); + AddException("RST28", Z80_INT_RST_28, "RST 28h (IM0)"); + AddException("RST30", Z80_INT_RST_30, "RST 30h (IM0)"); + AddException("RST38", Z80_INT_RST_38, "RST 38h (IM0)"); + AddException("IRQ", Z80_IM1_IRQ, "Hardwired IRQ (IM1)"); + AddException("VECTOR", Z80_IM2_VECTOR, "Interrupt Vectors (IM2)"); + + // Interrupts + for (unsigned vecNum = 0; vecNum < 256; vecNum++) + { + sprintf(m_vecIds[vecNum], "V%u", vecNum + 1); + sprintf(m_vecNames[vecNum], "Interrupt Vector #%u", vecNum + 1); + AddInterrupt(m_vecIds[vecNum], vecNum, m_vecNames[vecNum]); + } + + // I/O ports + for (unsigned portNum = 0; portNum < 256; portNum++) + { + sprintf(m_portNames[portNum], "Port #%u", portNum); + AddPortIO(portNum, 1, m_portNames[portNum], "I/O Ports"); + } + } + + CZ80Debug::~CZ80Debug() + { + DetachFromCPU(); + } + + void CZ80Debug::AttachToCPU() + { + m_z80->AttachDebugger(this); + } + + ::IBus *CZ80Debug::AttachBus(::IBus *bus) + { + m_bus = bus; + return this; + } + + void CZ80Debug::DetachFromCPU() + { + m_z80->DetachDebugger(); + } + + ::IBus *CZ80Debug::DetachBus() + { + ::IBus *bus = m_bus; + m_bus = NULL; + return bus; + } + + UINT32 CZ80Debug::GetResetAddr() + { + return 0x0000; + } + + bool CZ80Debug::UpdatePC(UINT32 pc) + { + return m_z80->SetReg16(Z80_REG16_PC, pc); + } + + bool CZ80Debug::ForceException(CException *ex) + { + if (ex->code != Z80_EX_NMI) + { + // TODO + return false; + } + m_z80->TriggerNMI(); + return true; + } + + bool CZ80Debug::ForceInterrupt(CInterrupt *in) + { + // TODO + return false; + } + + UINT64 CZ80Debug::ReadMem(UINT32 addr, unsigned dataSize) + { + if (dataSize == 1) + return m_bus->Read8(addr); + // TODO - byte swapping + return CCPUDebug::ReadMem(addr, dataSize); + } + + bool CZ80Debug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) + { + if (dataSize == 1) + { + m_bus->Write8(addr, (UINT8)data); + return true; + } + // TODO - byte swapping + return CCPUDebug::WriteMem(addr, dataSize, data); + } + + UINT64 CZ80Debug::ReadPort(UINT16 portNum) + { + return m_bus->IORead8(portNum); + } + + bool CZ80Debug::WritePort(UINT16 portNum, UINT64 data) + { + m_bus->IOWrite8(portNum, (UINT8)data); + return true; + } + + int CZ80Debug::Disassemble(UINT32 addr, char *mnemonic, char *operands) + { + char tmpStr[128], valStr[50], *templ, *pos; + INT8 offs; + EOpFlags opFlags; + + UINT16 dAddr = addr; + UINT16 tAddr; + char xyChr = '\0'; + bool notJump = false; + + // Get instruction template from opcode + UINT8 opcode = m_bus->Read8(dAddr++); + UINT8 nextCode; + switch (opcode) + { + case 0xCB: + templ = templates[1][m_bus->Read8(dAddr++)]; + break; + case 0xED: + templ = templates[2][m_bus->Read8(dAddr++)]; + break; + case 0xDD: + xyChr = 'X'; + nextCode = m_bus->Read8(dAddr++); + if (nextCode == 0xCB) + { + offs = m_bus->Read8(dAddr++); + notJump = true; + templ = templates[4][m_bus->Read8(dAddr++)]; + } + else + templ = templates[3][nextCode]; + break; + case 0xFD: + xyChr = 'Y'; + nextCode = m_bus->Read8(dAddr++); + if (nextCode == 0xCB) + { + offs = m_bus->Read8(dAddr++); + notJump = true; + templ = templates[4][m_bus->Read8(dAddr++)]; + } + else + templ = templates[3][nextCode]; + break; + default: + templ = templates[0][opcode]; + break; + } + + // If no template found, then instruction is invalid + if (templ == NULL) + return -1; + + // See if instruction has any operands + if (pos = strchr(templ, ' ')) + { + // If so, substitute all @ parameters in template operand + strncpy(mnemonic, templ, pos - templ); + mnemonic[pos - templ] = '\0'; + operands[0] = '\0'; + char *opPos = operands; + templ = pos + 1; + for (;;) + { + if (pos = strchr(templ, '@')) + { + strncpy(opPos, templ, pos - templ); + opPos[pos - templ] = '\0'; + // Format data for parameter + switch (*(pos + 1)) + { + case 'd': // ^h or *h + FormatData(valStr, 1, m_bus->Read8(dAddr++)); + break; + case 'p': // *p + FormatPortNum(valStr, m_bus->Read8(dAddr++), true); + break; + case 'b': // @H + offs = m_bus->Read8(dAddr++); + tAddr = dAddr + offs; + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(valStr, tAddr, opFlags); + break; + case 'o': // @h + if (notJump) + { + // Keep argument as +/-offs + strcat(operands, (offs&0x80 ? "-" : "+")); + FormatData(valStr, 1, (offs&0x80 ? 256 - offs : offs)); + } + else + { + offs = m_bus->Read8(dAddr++); + tAddr = dAddr + offs; + FormatAddress(valStr, tAddr, true); + } + break; + case 'j': // #H + tAddr = m_bus->Read8(dAddr++); + tAddr += m_bus->Read8(dAddr++) << 8; + opFlags = GetOpFlags(addr, opcode); + FormatJumpAddress(valStr, tAddr, opFlags); + break; + case 'a': // #h + tAddr = m_bus->Read8(dAddr++); + tAddr += m_bus->Read8(dAddr++) << 8; + FormatAddress(valStr, tAddr, true); + break; + } + // Append formatted data and loop again to process any more @ parameters + strcat(opPos, valStr); + templ = pos + 2; + opPos += strlen(opPos); + } + else + { + // No more @ parameters found so append remainder of template operand and exit loop + strcat(opPos, templ); + break; + } + } + + // Finally substitute ? parameter (X or Y) + if (pos = strchr(operands, '?')) + *pos = xyChr; + } + else + { + // Instruction has no operands + strcpy(mnemonic, templ); + operands[0] = '\0'; + } + + // Return instruction size + return dAddr - addr; + } + + //int CZ80Debug::GetOpLength(UINT32 addr) + //{ + // // Get instruction template from opcode + // UINT32 dAddr = addr; + // UINT8 opcode = m_bus->Read8(dAddr++); + // const char *templ; + // switch (opcode) + // { + // case 0xCB: + // templ = templates[1][m_bus->Read8(dAddr++)]; + // break; + // case 0xED: + // templ = templates[2][m_bus->Read8(dAddr++)]; + // break; + // case 0xDD: + // opcode = m_bus->Read8(dAddr++); + // if (opcode == 0xCB) + // { + // dAddr++; + // templ = templates[4][m_bus->Read8(dAddr++)]; + // } + // else + // templ = templates[3][opcode]; + // break; + // case 0xFD: + // opcode = m_bus->Read8(dAddr++); + // if (opcode == 0xCB) + // { + // dAddr++; + // templ = templates[4][m_bus->Read8(dAddr++)]; + // } + // else + // templ = templates[3][opcode]; + // break; + // default: + // templ = templates[0][opcode]; + // break; + // } + // + // // If no template found, then instruction is invalid + // if (templ == NULL) + // return -1; + // + // // Return instruction size + // return dAddr - addr; + //} + + EOpFlags CZ80Debug::GetOpFlags(UINT32 addr, UINT32 opcode) + { + if (opcode == 0xC3) + return JumpSimple; // JP + else if (opcode == 0xC2 || opcode == 0xCA || opcode == 0xD2 || opcode == 0xDA || + opcode == 0xE2 || opcode == 0xEA || opcode == 0xF2 || opcode == 0xFA) + return (EOpFlags)(JumpSimple | Conditional); // JP NZ, JP Z, JP NC, JP C, JP PO, JP PE, JP P, JP M + else if (opcode == 0x18) + return (EOpFlags)(JumpSimple | Relative); // JR + else if (opcode == 0x20 || opcode == 0x28 || opcode == 0x30 || opcode == 0x38) + return (EOpFlags)(JumpSimple | Relative | Conditional); // JR NZ, JR Z, JR NC, JR C + else if (opcode == 0x10) + return (EOpFlags)(JumpLoop | Relative | Conditional); // DJNZ + else if (opcode == 0xCD) + return JumpSub; // CALL + else if (opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC || + opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC) + return (EOpFlags)(JumpSub | Conditional); // CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M + else if (opcode == 0xC9) + return ReturnSub; // RET + else if (opcode == 0xC0 || opcode == 0xC8 || opcode == 0xD0 || opcode == 0xD8 || + opcode == 0xE0 || opcode == 0xE8 || opcode == 0xF0 || opcode == 0xF8) + return (EOpFlags)(ReturnSub | Conditional); // RET NZ, RET Z, RET NC, RET C, RET PO, RET PE, RET P, RET M + else if (opcode == 0xC7 || opcode == 0xCF || opcode == 0xD7 || opcode == 0xD7 || + opcode == 0xE7 || opcode == 0xEF || opcode == 0xF7 || opcode == 0xFF) + return (EOpFlags)(JumpEx | NotFixed); // RST 0, RST 8, RST 10H, RST 18H, RST 20H, RST 28H, RST 30H, RST 38H + else if (opcode == 0xED) + { + UINT8 nextCode = m_bus->Read8(addr + 1); + if (nextCode == 0x4D || nextCode == 0x45) + return ReturnEx; // RETI, RETN + } + else if (opcode == 0xE9 || + opcode == 0xDD && m_bus->Read8(addr + 1) == 0xE9 || + opcode == 0xFD && m_bus->Read8(addr + 1) == 0xE9) + return (EOpFlags)(JumpSimple | NotFixed); // JR (HL), JR (IX) or JR (IY) + return NormalOp; + } + + bool CZ80Debug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) + { + if (opcode == 0xC3 || + opcode == 0xC2 || opcode == 0xCA || opcode == 0xD2 || opcode == 0xDA || + opcode == 0xE2 || opcode == 0xEA || opcode == 0xF2 || opcode == 0xFA || + opcode == 0xCD || + opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC || + opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC) + { + // JP, JP NZ, JP Z, JP NC, JP C, JP PO, JP PE, JP P, JP M, + // CALL, CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M + jumpAddr = (UINT32)(m_bus->Read8(addr + 1) + (m_bus->Read8(addr + 2)<<8)); + return true; + } + else if (opcode == 0x18 || opcode == 0x20 || opcode == 0x28 || opcode == 0x30 || opcode == 0x38 || opcode == 0x10) + { + // JR, JR NZ, JR NC, JR C, DJNZ + INT8 offs = m_bus->Read8(addr + 1); + jumpAddr = addr + 2 + offs; + return true; + } + return false; + } + + bool CZ80Debug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + if (opcode == 0xCD || + opcode == 0xC4 || opcode == 0xCC || opcode == 0xD4 || opcode == 0xDC || + opcode == 0xE4 || opcode == 0xEC || opcode == 0xF4 || opcode == 0xFC) + { + // CALL, CALL NZ, CALL Z, CALL NC, CALL C, CALL PO, CALL PE, CALL P, CALL M + retAddr = addr + 3; + return true; + } + else if (opcode == 0xC7 || opcode == 0xCF || opcode == 0xD7 || opcode == 0xD7 || + opcode == 0xE7 || opcode == 0xEF || opcode == 0xF7 || opcode == 0xFF) + { + // RST 0, RST 8, RST 10H, RST 18H, RST 20H, RST 28H, RST 30H, RST 38H + retAddr = addr + 1; + } + return false; + } + + bool CZ80Debug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) + { + if (opcode == 0xC9 || + opcode == 0xC0 || opcode == 0xC8 || opcode == 0xD0 || opcode == 0xD8 || + opcode == 0xE0 || opcode == 0xE8 || opcode == 0xF0 || opcode == 0xF8) + { + // RET, RET NZ, RET Z, RET NC, RET C, RET PO, RET PE, RET P, RET M + UINT16 sp = m_z80->GetReg16(Z80_REG16_SP); + retAddr = (UINT32)m_bus->Read8(sp) + (((UINT32)m_bus->Read8(sp + 1))<<8); + return true; + } + else if (opcode == 0xED) + { + UINT8 nextCode = m_bus->Read8(addr + 1); + if (nextCode == 0x4D || nextCode == 0x45) + { + // RETI, RETN + UINT16 sp = m_z80->GetReg16(Z80_REG16_SP); + retAddr = (UINT32)m_bus->Read8(sp) + (((UINT32)m_bus->Read8(sp + 1))<<8); + return true; + } + } + return false; + } + + bool CZ80Debug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr) + { + switch (ex->code) + { + case Z80_EX_NMI: handlerAddr = 0x0066; return true; // Hardwired NMI vector + case Z80_INT_RST_00: handlerAddr = 0x0000; return true; // RST 0h + case Z80_INT_RST_08: handlerAddr = 0x0008; return true; // RST 8h + case Z80_INT_RST_10: handlerAddr = 0x0010; return true; // RST 10h + case Z80_INT_RST_18: handlerAddr = 0x0018; return true; // RST 18h + case Z80_INT_RST_20: handlerAddr = 0x0020; return true; // RST 20h + case Z80_INT_RST_28: handlerAddr = 0x0028; return true; // RST 28h + case Z80_INT_RST_30: handlerAddr = 0x0030; return true; // RST 30h + case Z80_INT_RST_38: handlerAddr = 0x0038; return true; // RST 38h + case Z80_IM1_IRQ: handlerAddr = 0x0038; return true; // Hardwired IRQ vector + default: return false; + } + } + + bool CZ80Debug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) + { + UINT8 iff = m_z80->GetReg8(Z80_REG8_IFF); + if (!(iff&0x04)) // IM2 mode + return false; + UINT8 i = m_z80->GetReg8(Z80_REG8_I); + handlerAddr = (((UINT32)i)<<8)|(((UINT32)in->code)&0xFF); + return true; + } + + // IBus methods + + UINT8 CZ80Debug::Read8(UINT32 addr) + { + UINT8 data = m_bus->Read8(addr); + CheckRead8(addr, data); + return data; + } + + void CZ80Debug::Write8(UINT32 addr, UINT8 data) + { + m_bus->Write8(addr, data); + CheckWrite8(addr, data); + } + + UINT8 CZ80Debug::IORead8(UINT32 portNum) + { + UINT8 data = m_bus->IORead8(portNum); + CheckPortInput(portNum, data); + return data; + } + + void CZ80Debug::IOWrite8(UINT32 portNum, UINT8 data) + { + m_bus->IOWrite8(portNum, data); + CheckPortOutput(portNum, data); + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPU/Z80Debug.h b/Src/Debugger/CPU/Z80Debug.h new file mode 100644 index 0000000..e02ad5f --- /dev/null +++ b/Src/Debugger/CPU/Z80Debug.h @@ -0,0 +1,119 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Z80KDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_Z80DEBUG_H +#define INCLUDED_Z80DEBUG_H + +#include "Debugger/CPUDebug.h" +#include "CPU/Bus.h" +#include "Types.h" + +class CZ80; + +namespace Debugger +{ + /* + * CCPUDebug implementation for the Zilog Z80 emulator. + */ + class CZ80Debug : public CCPUDebug, public ::IBus + { + private: + static UINT8 ReadReg8(CCPUDebug *cpu, unsigned reg8); + + static bool WriteReg8(CCPUDebug *cpu, unsigned reg8, UINT8 value); + + static UINT16 ReadReg16(CCPUDebug *cpu, unsigned reg16); + + static bool WriteReg16(CCPUDebug *cpu, unsigned reg16, UINT16 value); + + CZ80 *m_z80; + ::IBus *m_bus; + + char m_vecIds[256][5]; + char m_vecNames[256][22]; + char m_portNames[256][10]; + + public: + CZ80Debug(const char *name, CZ80 *z80); + + virtual ~CZ80Debug(); + + // CZ80Debug methods + + void AttachToCPU(); + + ::IBus *AttachBus(::IBus *bus); + + void DetachFromCPU(); + + ::IBus *DetachBus(); + + UINT32 GetResetAddr(); + + bool UpdatePC(UINT32 pc); + + bool ForceException(CException *ex); + + bool ForceInterrupt(CInterrupt *in); + + UINT64 ReadMem(UINT32 addr, unsigned dataSize); + + bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data); + + UINT64 ReadPort(UINT16 portNum); + + bool WritePort(UINT16 portNum, UINT64 data); + + int Disassemble(UINT32 addr, char *mnemonic, char *operands); + + //int GetOpLength(UINT32 addr); + + EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode); + + bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr); + + bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr); + + bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr); + + bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr); + + // IBus methods + + UINT8 Read8(UINT32 addr); + + void Write8(UINT32 addr, UINT8 data); + + UINT8 IORead8(UINT32 portNum); + + void IOWrite8(UINT32 portNum, UINT8 data); + }; +} + +#endif // INCLUDED_Z80DEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CPUDebug.cpp b/Src/Debugger/CPUDebug.cpp new file mode 100644 index 0000000..0bbe27e --- /dev/null +++ b/Src/Debugger/CPUDebug.cpp @@ -0,0 +1,1650 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * CPUDebug.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Supermodel.h" +#include "CPUDebug.h" +#include "CodeAnalyser.h" +#include "Label.h" + +#include +#include + +using namespace std; + +namespace Debugger +{ + CCPUDebug::CCPUDebug(const char *cpuType, const char *cpuName, + UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen) : + type(cpuType), name(cpuName), minInstrLen(cpuMinInstrLen), maxInstrLen(cpuMaxInstrLen), + bigEndian(cpuBigEndian), memBusWidth(cpuMemBusWidth), maxMnemLen(cpuMaxMnemLen), + addrFmt(HexDollar), portFmt(Decimal), dataFmt(HexDollar), debugger(NULL), + numExCodes(0), numIntCodes(0), numPorts(0), memSize(0), active(false), instrCount(0), totalCycles(0), cyclesPerPoll(0), pc(0), opcode(0), + m_enabled(true), m_break(false), m_breakUser(false), m_halted(false), m_step(false), m_steppingOver(false), m_steppingOut(false), + m_count(0), m_until(false), m_untilAddr(0), + m_mappedIOTable(NULL), m_memWatchTable(NULL), m_bpTable(NULL), m_numRegMons(0), m_regMonArray(NULL), + m_analyser(NULL), m_stateUpdated(false), m_exRaised(NULL), m_exTrapped(NULL), m_intRaised(NULL), m_intTrapped(NULL), m_bpReached(NULL), + m_memWatchTriggered(NULL), m_ioWatchTriggered(NULL), m_regMonTriggered(NULL), m_prevTotalCycles(0) + { + memset(m_exArray, NULL, sizeof(m_exArray)); + memset(m_intArray, NULL, sizeof(m_intArray)); + memset(m_portArray, NULL, sizeof(m_portArray)); + +#ifdef DEBUGGER_HASTHREAD + m_breakWait = false; + m_mutex = CThread::CreateMutex(); + m_condVar = CThread::CreateCondVar(); +#endif // DEBUGGER_HASTHREAD + } + + CCPUDebug::~CCPUDebug() + { + // Delete registers + for (vector::iterator it = regs.begin(); it != regs.end(); it++) + delete *it; + regs.clear(); + + // Delete exceptions + for (vector::iterator it = exceps.begin(); it != exceps.end(); it++) + delete *it; + exceps.clear(); + memset(m_exArray, NULL, sizeof(m_exArray)); + + // Delete interrupts + for (vector::iterator it = inters.begin(); it != inters.end(); it++) + delete *it; + inters.clear(); + memset(m_intArray, NULL, sizeof(m_intArray)); + + // Delete IOs + for (vector::iterator it = ios.begin(); it != ios.end(); it++) + delete *it; + ios.clear(); + memset(m_portArray, NULL, sizeof(m_portArray)); + delete m_mappedIOTable; + + // Delete regions + for (vector::iterator it = regions.begin(); it != regions.end(); it++) + delete *it; + regions.clear(); + + // Delete labels, comments, memory watches, I/O watches, breakpoints and register monitors + RemoveAllLabels(); + RemoveAllComments(); + RemoveAllMemWatches(); + RemoveAllIOWatches(); + RemoveAllBreakpoints(); + RemoveAllRegMonitors(); + + // Delete analyser, if any + if (m_analyser != NULL) + delete m_analyser; + } + + void CCPUDebug::AttachToDebugger(CDebugger *theDebugger) + { + UpdateExecMasks(); + UpdateMemMasks(); + + debugger = theDebugger; + } + + void CCPUDebug::DetachFromDebugger(CDebugger *theDebugger) + { + if (debugger == theDebugger) + debugger = NULL; + } + + CPCRegister *CCPUDebug::AddPCRegister(const char *name, const char *group) + { + CPCRegister *reg = new CPCRegister(this, name, group); + regs.push_back(reg); + return reg; + } + + CAddrRegister *CCPUDebug::AddAddrRegister(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) + { + CAddrRegister *reg = new CAddrRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CIntRegister *CCPUDebug::AddInt8Register(const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc) + { + CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CIntRegister *CCPUDebug::AddInt16Register(const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc) + { + CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CIntRegister *CCPUDebug::AddInt32Register(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) + { + CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CIntRegister *CCPUDebug::AddInt64Register(const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc) + { + CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CStatusRegister *CCPUDebug::AddStatus8Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt8FPtr getFunc, SetInt8FPtr setFunc) + { + CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc); + reg->SetBits(bitStr); + regs.push_back(reg); + return reg; + } + + CStatusRegister *CCPUDebug::AddStatus16Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt16FPtr getFunc, SetInt16FPtr setFunc) + { + CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc); + reg->SetBits(bitStr); + regs.push_back(reg); + return reg; + } + + CStatusRegister *CCPUDebug::AddStatus32Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt32FPtr getFunc, SetInt32FPtr setFunc) + { + CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc); + reg->SetBits(bitStr); + regs.push_back(reg); + return reg; + } + + CFPointRegister *CCPUDebug::AddFPointRegister(const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc) + { + CFPointRegister *reg = new CFPointRegister(this, name, group, id, getFunc, setFunc); + regs.push_back(reg); + return reg; + } + + CException *CCPUDebug::AddException(const char *id, UINT16 code, const char *name) + { + if (code >= MAX_EXCEPTIONS) + return NULL; + CException *ex = new CException(this, id, code, name); + exceps.push_back(ex); + m_exArray[code] = ex; + numExCodes = max(numExCodes, code + 1); + return ex; + } + + CInterrupt *CCPUDebug::AddInterrupt(const char *id, UINT16 code, const char *name) + { + if (code >= MAX_INTERRUPTS) + return NULL; + CInterrupt *in = new CInterrupt(this, id, code, name); + inters.push_back(in); + m_intArray[code] = in; + numIntCodes = max(numIntCodes, code + 1); + return in; + } + + CRegion *CCPUDebug::AddRegion(UINT32 start, UINT32 end, bool isCode, bool isReadOnly, const char *name) + { + if (debugger != NULL) + return NULL; + CRegion *region = new CRegion(this, start, end, isCode, isReadOnly, name); + if (regions.size() > 0) + { + region->prevRegion = regions[regions.size() - 1]; + region->prevRegion->nextRegion = region; + } + regions.push_back(region); + memSize = max(memSize, end); + return region; + } + + CPortIO *CCPUDebug::AddPortIO(UINT16 portNum, unsigned dataSize, const char *name, const char *group) + { + if (debugger != NULL || portNum >= MAX_IOPORTS) + return NULL; + CPortIO *port = new CPortIO(this, name, group, dataSize, portNum); + ios.push_back(port); + m_portArray[portNum] = port; + numPorts = max(numPorts, portNum + 1); + return port; + } + + CMappedIO *CCPUDebug::AddMappedIO(UINT32 addr, unsigned dataSize, const char *name, const char *group) + { + if (debugger != NULL) + return NULL; + if (m_mappedIOTable == NULL) + m_mappedIOTable = new CAddressTable(); + CMappedIO *mapped = new CMappedIO(this, name, group, dataSize, addr); + ios.push_back(mapped); + m_mappedIOTable->Add(mapped); + return mapped; + } + + bool CCPUDebug::ParseData(const char *str, unsigned dataSize, UINT64 *data, bool registers) + { + // Check registers, if required + if (registers) + { + CRegister *reg = GetRegister(str); + if (reg != NULL) + { + *data = reg->GetValueAsInt(); + return *data == CDebugger::MaskData(dataSize, *data); + } + } + + return debugger->ParseData(str, dataFmt, dataSize, data) && *data == CDebugger::MaskData(dataSize, *data); + } + + void CCPUDebug::FormatData(char *str, unsigned dataSize, UINT64 data) + { + debugger->FormatData(str, dataFmt, dataSize, data); + } + + bool CCPUDebug::ParseAddress(const char *str, UINT32 *addr, bool customLabels, bool autoLabels, bool registers) + { + // Check custom labels, if required + if (customLabels) + { + CLabel *label = GetLabel(str); + if (label != NULL) + { + *addr = label->addr; + return GetRegion(*addr) != NULL; + } + } + + // Check auto-labels, if required + if (autoLabels && m_analyser != NULL) + { + CAutoLabel *autoLabel = m_analyser->analysis->GetAutoLabel(str); + if (autoLabel != NULL) + { + *addr = autoLabel->addr; + return GetRegion(*addr) != NULL; + } + } + + // Check registers, if required + if (registers) + { + CRegister *reg = GetRegister(str); + if (reg != NULL) + { + *addr = reg->GetValueAsInt(); + return GetRegion(*addr) != NULL; + } + } + + // Parse address + unsigned dataSize = (unsigned)(memBusWidth / 8); + UINT64 vlAddr; + if (!debugger->ParseData(str, addrFmt, dataSize, &vlAddr) || vlAddr > 0xFFFFFFFFULL) + return false; + *addr = (UINT32)vlAddr; + return GetRegion(*addr) != NULL; + } + + void CCPUDebug::FormatAddress(char *str, UINT32 addr, bool customLabels, ELabelFlags autoLabelFlags) + { + // Check custom labels, if required + if (customLabels) + { + CLabel *label = GetLabel(addr); + if (label != NULL) + { + strcpy(str, label->name); + return; + } + } + + // Check auto-labels, if required + if (autoLabelFlags != LFNone && m_analyser != NULL) + { + CAutoLabel *autoLabel = m_analyser->analysis->GetAutoLabel(addr); + if (autoLabel != NULL && autoLabel->GetLabel(str, autoLabelFlags)) + { + autoLabel->GetLabel(str, autoLabelFlags); + return; + } + } + + // Format address + debugger->FormatData(str, addrFmt, (unsigned)(memBusWidth / 8), (UINT64)addr); + } + + void CCPUDebug::FormatJumpAddress(char *str, UINT32 jumpAddr, EOpFlags opFlags) + { + if (opFlags & JumpSub) FormatAddress(str, jumpAddr, true, LFSubroutine); + else if (opFlags & JumpLoop) FormatAddress(str, jumpAddr, true, LFLoopPoint); + else if (opFlags & JumpSimple) FormatAddress(str, jumpAddr, true, LFJumpTarget); + else FormatAddress(str, jumpAddr, true, LFNone); + } + + bool CCPUDebug::ParsePortNum(const char *str, UINT16 *portNum, bool customLabels, bool registers) + { + // Check custom labels, if required + if (customLabels) + { + for (UINT16 loopNum = 0; loopNum < numPorts; loopNum++) + { + CPortIO *port = m_portArray[loopNum]; + if (port == NULL || port->label == NULL) + continue; + if (stricmp(port->label, str) == 0) + { + *portNum = loopNum; + return true; + } + } + } + + // Check registers, if required + if (registers) + { + CRegister *reg = GetRegister(str); + if (reg != NULL) + { + *portNum = reg->GetValueAsInt(); + return *portNum < numPorts; + } + } + + // Parse port num + unsigned dataSize = CDebugger::GetDataSize(numPorts); + UINT64 vlPortNum; + if (!debugger->ParseData(str, portFmt, dataSize, &vlPortNum)) + return false; + *portNum = (UINT16)vlPortNum; + return *portNum < numPorts && GetPortIO(*portNum) != NULL; + } + + void CCPUDebug::FormatPortNum(char *str, UINT16 portNum, bool customLabels) + { + if (portNum >= numPorts) + { + str[0] = '\0'; + return; + } + + // Check custom labels, if reqired + if (customLabels) + { + CPortIO *port = m_portArray[portNum]; + if (port != NULL && port->label != NULL) + { + strcpy(str, port->label); + return; + } + } + + // Format port num + unsigned dataSize = CDebugger::GetDataSize(numPorts); + debugger->FormatData(str, portFmt, dataSize, (UINT64)portNum); + } + + bool CCPUDebug::IsEnabled() + { + return m_enabled; + } + + void CCPUDebug::SetEnabled(bool enabled) + { + m_enabled = enabled; + UpdateExecMasks(); + } + + void CCPUDebug::ForceBreak(bool user) + { + m_breakUser |= user; + m_break = true; + UpdateExecMasks(); + } + + void CCPUDebug::ClearBreak() + { +#ifdef DEBUGGER_HASTHREAD + m_breakWait = false; +#endif // DEBUGGER_HASTHREAD + m_breakUser = false; + m_break = false; + UpdateExecMasks(); + } + + void CCPUDebug::SetContinue() + { + m_step = false; + m_count = 0; + m_until = false; + UpdateExecMasks(); + } + + void CCPUDebug::SetStepMode(EStepMode stepMode) + { + m_step = true; + m_stepMode = stepMode; + m_steppingOver = false; + m_steppingOut = false; + m_count = 0; + m_until = false; + UpdateExecMasks(); + } + + void CCPUDebug::SetCount(int count) + { + m_step = false; + m_count = count; + m_until = false; + UpdateExecMasks(); + } + + void CCPUDebug::SetUntil(UINT32 untilAddr) + { + m_step = false; + m_count = 0; + m_until = true; + m_untilAddr = untilAddr; + UpdateExecMasks(); + } + + bool CCPUDebug::SetPC(UINT32 newPC) + { + if (!UpdatePC(newPC)) + return false; + pc = newPC; + m_stateUpdated = true; + UpdateExecMasks(); + return true; + } + + bool CCPUDebug::GenerateException(CException *ex) + { + if (!ForceException(ex)) + return false; + m_stateUpdated = true; + UpdateExecMasks(); + return true; + } + + bool CCPUDebug::GenerateInterrupt(CInterrupt *in) + { + if (!ForceInterrupt(in)) + return false; + m_stateUpdated = true; + UpdateExecMasks(); + return true; + } + + CRegister *CCPUDebug::GetRegister(const char *name) + { + for (vector::iterator it = regs.begin(); it != regs.end(); it++) + { + if (stricmp((*it)->name, name) == 0) + return *it; + } + return NULL; + } + + CException *CCPUDebug::GetException(const char *id) + { + for (vector::iterator it = exceps.begin(); it != exceps.end(); it++) + { + if (stricmp((*it)->id, id) == 0) + return *it; + } + return NULL; + } + + CException *CCPUDebug::GetException(UINT16 code) + { + if (code >= numExCodes) + return NULL; + return m_exArray[code]; + } + + CInterrupt *CCPUDebug::GetInterrupt(const char *id) + { + for (vector::iterator it = inters.begin(); it != inters.end(); it++) + { + if (stricmp((*it)->id, id) == 0) + return *it; + } + return NULL; + } + + CInterrupt *CCPUDebug::GetInterrupt(UINT16 code) + { + if (code >= numIntCodes) + return NULL; + return m_intArray[code]; + } + + CPortIO *CCPUDebug::GetPortIO(UINT16 portNum) + { + if (portNum >= numPorts) + return NULL; + return m_portArray[portNum]; + } + + CMappedIO *CCPUDebug::GetMappedIO(UINT32 addr) + { + if (m_mappedIOTable == NULL) + return NULL; + return (CMappedIO*)m_mappedIOTable->Get(addr); + } + + CRegion *CCPUDebug::GetRegion(UINT32 addr) + { + for (vector::iterator it = regions.begin(); it != regions.end(); it++) + { + if ((*it)->CheckAddr(addr)) + return *it; + } + return NULL; + } + + CLabel *CCPUDebug::GetLabel(UINT32 addr) + { + for (vector::iterator it = labels.begin(); it != labels.end(); it++) + { + if ((*it)->CheckAddr(addr)) + return *it; + } + return NULL; + } + + CLabel *CCPUDebug::GetLabel(const char *name) + { + // TODO - use binary search + for (vector::iterator it = labels.begin(); it != labels.end(); it++) + { + if (stricmp((*it)->name, name) == 0) + return *it; + } + return NULL; + } + + CLabel *CCPUDebug::AddLabel(UINT32 addr, const char *name) + { + RemoveLabel(addr); + RemoveLabel(name); + CLabel *label = new CLabel(this, addr, name); + // TODO - keep labels in order of increasing address so can use binary search + labels.push_back(label); + return label; + } + + bool CCPUDebug::RemoveLabel(const char *name) + { + CLabel *label = GetLabel(name); + if (label == NULL) + return false; + return RemoveLabel(label); + } + + bool CCPUDebug::RemoveLabel(UINT32 addr) + { + CLabel *label = GetLabel(addr); + if (label == NULL) + return false; + RemoveLabel(label); + return true; + } + + bool CCPUDebug::RemoveLabel(CLabel *label) + { + vector::iterator it = find(labels.begin(), labels.end(), label); + if (it == labels.end()) + return false; + labels.erase(it); + delete label; + return true; + } + + bool CCPUDebug::RemoveAllLabels() + { + if (labels.size() == 0) + return false; + for (vector::iterator it = labels.begin(); it != labels.end(); it++) + delete *it; + labels.clear(); + return true; + } + + CComment *CCPUDebug::GetComment(UINT32 addr) + { + // TODO - use binary search + for (vector::iterator it = comments.begin(); it != comments.end(); it++) + { + if ((*it)->CheckAddr(addr)) + return *it; + } + return NULL; + } + + CComment *CCPUDebug::AddComment(UINT32 addr, const char *commentText) + { + RemoveComment(addr); + CComment *comment = new CComment(this, addr, commentText); + // TODO - keep comments in order of increasing address so can use binary search + comments.push_back(comment); + return comment; + } + + bool CCPUDebug::RemoveComment(UINT32 addr) + { + CComment *comment = GetComment(addr); + if (comment == NULL) + return false; + RemoveComment(comment); + return true; + } + + bool CCPUDebug::RemoveComment(CComment *comment) + { + vector::iterator it = find(comments.begin(), comments.end(), comment); + if (it == comments.end()) + return false; + comments.erase(it); + delete comment; + return true; + } + + bool CCPUDebug::RemoveAllComments() + { + if (comments.size() == 0) + return false; + for (vector::iterator it = comments.begin(); it != comments.end(); it++) + delete *it; + comments.clear(); + return true; + } + + CSimpleWatch *CCPUDebug::AddSimpleMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite) + { + CSimpleWatch *watch = NULL; + // First check for mapped I/O at given addr + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, create watch for I/O + if (mapped != NULL) + watch = new CSimpleWatch(this, mapped, trigRead, trigWrite); + } + // Otherwise, create watch for memory addr + if (watch == NULL) + watch = new CSimpleWatch(this, addr, size, trigRead, trigWrite); + AddWatch(watch); + return watch; + } + + CCountWatch *CCPUDebug::AddCountMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned count) + { + CCountWatch *watch = NULL; + // First check for mapped I/O at given addr + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, create watch for I/O + if (mapped != NULL) + watch = new CCountWatch(this, mapped, trigRead, trigWrite, count); + } + // Otherwise, create watch for memory addr + if (watch == NULL) + watch = new CCountWatch(this, addr, size, trigRead, trigWrite, count); + AddWatch(watch); + return watch; + } + + CMatchWatch *CCPUDebug::AddMatchMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, vector &dataSeq) + { + CMatchWatch *watch = NULL; + // First check for mapped I/O at given addr + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, create watch for I/O + if (mapped != NULL) + watch = new CMatchWatch(this, mapped, trigRead, trigWrite, dataSeq); + } + // Otherwise, create watch for memory addr + if (watch == NULL) + watch = new CMatchWatch(this, addr, size, trigRead, trigWrite, dataSeq); + AddWatch(watch); + return watch; + } + + CPrintWatch *CCPUDebug::AddPrintMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite) + { + CPrintWatch *watch = NULL; + // First check for mapped I/O at given addr + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, create watch for I/O + if (mapped != NULL) + watch = new CPrintWatch(this, mapped, trigRead, trigWrite); + } + // Otherwise, create watch for memory addr + if (watch == NULL) + watch = new CPrintWatch(this, addr, size, trigRead, trigWrite); + AddWatch(watch); + return watch; + } + + CCaptureWatch *CCPUDebug::AddCaptureMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned maxLen) + { + CCaptureWatch *watch = NULL; + // First check for mapped I/O at given addr + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, create watch for I/O + if (mapped != NULL) + watch = new CCaptureWatch(this, mapped, trigRead, trigWrite, maxLen); + } + // Otherwise, create watch for memory addr + if (watch == NULL) + watch = new CCaptureWatch(this, addr, size, trigRead, trigWrite, maxLen); + AddWatch(watch); + return watch; + } + + bool CCPUDebug::RemoveAllMemWatches() + { + if (memWatches.size() == 0) + return false; + for (vector::iterator it = memWatches.begin(); it != memWatches.end(); it++) + delete *it; + memWatches.clear(); + delete m_memWatchTable; + m_memWatchTable = NULL; + UpdateMemMasks(); + return true; + } + + bool CCPUDebug::RemoveAllIOWatches() + { + if (ioWatches.size() == 0) + return false; + for (vector::iterator it = ioWatches.begin(); it != ioWatches.end(); it++) + { + (*it)->io->watch = NULL; + delete *it; + } + ioWatches.clear(); + return true; + } + + bool CCPUDebug::RemoveMemWatch(UINT32 addr, unsigned size) + { + CWatch *watch; + bool removed = false; + while ((watch = GetMemWatch(addr, size)) != NULL) + removed |= RemoveWatch(watch); + return removed; + } + + CWatch *CCPUDebug::GetMemWatch(UINT32 addr, unsigned size) + { + // First check for mapped I/O at given addr and size + if (m_mappedIOTable != NULL) + { + CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size); + // If found, check watch on I/O + if (mapped != NULL) + return mapped->watch; + } + // If none, check memory table for watch + if (m_memWatchTable == NULL) + return NULL; + return (CWatch*)m_memWatchTable->Get(addr, size); + } + + void CCPUDebug::AddWatch(CWatch *watch) + { + CIO *io = watch->io; + if (io != NULL) + { + if (io->watch != NULL) + RemoveWatch(io->watch); + ioWatches.push_back(watch); + UpdateIOWatchNums(); + io->watch = watch; + } + else + { + RemoveMemWatch(watch->addr, watch->size); + memWatches.push_back(watch); + UpdateMemWatchNums(); + if (m_memWatchTable == NULL) + m_memWatchTable = new CAddressTable(); + m_memWatchTable->Add(watch); + UpdateMemMasks(); + } + } + + bool CCPUDebug::RemoveWatch(CWatch *watch) + { + CIO *io = watch->io; + if (io != NULL) + { + io->watch = NULL; + vector::iterator it = find(ioWatches.begin(), ioWatches.end(), watch); + if (it == ioWatches.end()) + return false; + ioWatches.erase(it); + UpdateIOWatchNums(); + } + else + { + vector::iterator it = find(memWatches.begin(), memWatches.end(), watch); + if (it == memWatches.end()) + return false; + memWatches.erase(it); + UpdateMemWatchNums(); + m_memWatchTable->Remove(watch); + if (m_memWatchTable->IsEmpty()) + { + delete m_memWatchTable; + m_memWatchTable = NULL; + } + UpdateMemMasks(); + } + delete watch; + return true; + } + + void CCPUDebug::UpdateIOWatchNums() + { + unsigned num = 0; + for (vector::iterator it = ioWatches.begin(); it != ioWatches.end(); it++) + (*it)->num = num++; + } + + void CCPUDebug::UpdateMemWatchNums() + { + unsigned num = 0; + for (vector::iterator it = memWatches.begin(); it != memWatches.end(); it++) + (*it)->num = num++; + } + + void CCPUDebug::UpdateExecMasks() + { + if (!m_enabled) + { + m_execAndMask = 0xFFFFFFFF; + m_execOrMask = 0xFFFFFFFF; + } + else if (m_break || m_stateUpdated || m_numRegMons > 0 || m_step || m_count > 0) + { + m_execAndMask = 0; + m_execOrMask = 0; + } + else if (bps.size() > 0 || m_until) + { + UINT32 andMask = 0xFFFFFFFF; + UINT32 orMask = 0; + for (vector::iterator it = bps.begin(), end = bps.end(); it != end; it++) + { + UINT32 addr = (*it)->addr; + andMask &= addr; + orMask |= addr; + } + if (m_until) + { + andMask &= m_untilAddr; + orMask |= m_untilAddr; + } + m_execAndMask = andMask; + m_execOrMask = ~orMask; + } + else + { + m_execAndMask = 0xFFFFFFFF; + m_execOrMask = 0xFFFFFFFF; + } + } + + void CCPUDebug::UpdateMemMasks() + { + UINT32 and8Mask = 0xFFFFFFFF; + UINT32 and16Mask = 0xFFFFFFFF; + UINT32 and32Mask = 0xFFFFFFFF; + UINT32 and64Mask = 0xFFFFFFFF; + UINT32 or8Mask = 0; + UINT32 or16Mask = 0; + UINT32 or32Mask = 0; + UINT32 or64Mask = 0; + for (vector::iterator it = ios.begin(), end = ios.end(); it != end; it++) + { + CMappedIO *mapped = dynamic_cast(*it); + if (!mapped) + continue; + UINT32 addr = mapped->addr; + CRegion *region = GetRegion(addr); + int intSize = (int)mapped->size; + for (int offset = -7; offset < intSize; offset++) + { + if (offset < 0 && addr < abs(offset) || offset > 0 && addr > 0xFFFFFFFF - offset) + continue; + UINT32 offAddr = addr + offset; + if (!region->CheckAddr(offAddr)) + continue; + if (offset >= 0) + { + and8Mask &= offAddr; + or8Mask |= offAddr; + and16Mask &= offAddr; + or16Mask |= offAddr; + and32Mask &= offAddr; + or32Mask |= offAddr; + and64Mask &= offAddr; + or64Mask |= offAddr; + } + else + { + if (offset > -2) + { + and16Mask &= offAddr; + or16Mask |= offAddr; + } + if (offset > -4) + { + and32Mask &= offAddr; + or32Mask |= offAddr; + } + and64Mask &= offAddr; + or64Mask |= offAddr; + } + } + } + for (vector::iterator it = memWatches.begin(), end = memWatches.end(); it != end; it++) + { + UINT32 addr = (*it)->addr; + CRegion *region = GetRegion(addr); + int intSize = (int)(*it)->size; + for (int offset = -7; offset < intSize; offset++) + { + if (offset < 0 && addr < abs(offset) || offset > 0 && addr > 0xFFFFFFFF - offset) + continue; + UINT32 offAddr = addr + offset; + if (!region->CheckAddr(offAddr)) + continue; + if (offset >= 0) + { + and8Mask &= offAddr; + or8Mask |= offAddr; + and16Mask &= offAddr; + or16Mask |= offAddr; + and32Mask &= offAddr; + or32Mask |= offAddr; + and64Mask &= offAddr; + or64Mask |= offAddr; + } + else + { + if (offset > -2) + { + and16Mask &= offAddr; + or16Mask |= offAddr; + } + if (offset > -4) + { + and32Mask &= offAddr; + or32Mask |= offAddr; + } + and64Mask &= offAddr; + or64Mask |= offAddr; + } + } + } + m_mem8AndMask = and8Mask; + m_mem8OrMask = ~or8Mask; + m_mem16AndMask = and16Mask; + m_mem16OrMask = ~or16Mask; + m_mem32AndMask = and32Mask; + m_mem32OrMask = ~or32Mask; + m_mem64AndMask = and64Mask; + m_mem64OrMask = ~or64Mask; + } + + CSimpleBreakpoint *CCPUDebug::AddSimpleBreakpoint(UINT32 addr) + { + CSimpleBreakpoint *bp = new CSimpleBreakpoint(this, addr); + AddBreakpoint(bp); + return bp; + } + + CCountBreakpoint *CCPUDebug::AddCountBreakpoint(UINT32 addr, unsigned count) + { + CCountBreakpoint *bp = new CCountBreakpoint(this, addr, count); + AddBreakpoint(bp); + return bp; + } + + CPrintBreakpoint *CCPUDebug::AddPrintBreakpoint(UINT32 addr) + { + CPrintBreakpoint *bp = new CPrintBreakpoint(this, addr); + AddBreakpoint(bp); + return bp; + } + + CBreakpoint *CCPUDebug::GetBreakpoint(UINT32 addr) + { + if (m_bpTable == NULL) + return NULL; + return (CBreakpoint*)m_bpTable->Get(addr); + } + + void CCPUDebug::AddBreakpoint(CBreakpoint *bp) + { + RemoveBreakpoint(bp->addr); + bps.push_back(bp); + UpdateBreakpointNums(); + if (m_bpTable == NULL) + m_bpTable = new CAddressTable(); + m_bpTable->Add(bp); + UpdateExecMasks(); + } + + bool CCPUDebug::RemoveBreakpoint(UINT32 addr) + { + CBreakpoint *bp = GetBreakpoint(addr); + if (bp == NULL) + return false; + return RemoveBreakpoint(bp); + } + + bool CCPUDebug::RemoveBreakpoint(CBreakpoint *bp) + { + vector::iterator it = find(bps.begin(), bps.end(), bp); + if (it == bps.end()) + return false; + bps.erase(it); + UpdateBreakpointNums(); + m_bpTable->Remove(bp); + if (m_bpTable->IsEmpty()) + { + delete m_bpTable; + m_bpTable = NULL; + } + delete bp; + UpdateExecMasks(); + return true; + } + + bool CCPUDebug::RemoveAllBreakpoints() + { + if (bps.size() == 0) + return false; + for (vector::iterator it = bps.begin(); it != bps.end(); it++) + delete *it; + bps.clear(); + delete m_bpTable; + m_bpTable = NULL; + UpdateExecMasks(); + return true; + } + + void CCPUDebug::UpdateBreakpointNums() + { + unsigned num = 0; + for (vector::iterator it = bps.begin(); it != bps.end(); it++) + (*it)->num = num++; + } + + CRegMonitor *CCPUDebug::AddRegMonitor(const char *regName) + { + RemoveRegMonitor(regName); + + CRegister *reg = GetRegister(regName); + if (reg == NULL) + return NULL; + CRegMonitor *regMon = reg->CreateMonitor(); + AddRegMonitor(regMon); + return regMon; + } + + CRegMonitor *CCPUDebug::GetRegMonitor(const char *regName) + { + for (vector::iterator it = regMons.begin(); it != regMons.end(); it++) + { + if (stricmp((*it)->reg->name, regName) == 0) + return *it; + } + return NULL; + } + + void CCPUDebug::AddRegMonitor(CRegMonitor *regMon) + { + regMons.push_back(regMon); + UpdateRegMonArray(); + UpdateExecMasks(); + } + + bool CCPUDebug::RemoveRegMonitor(const char *regName) + { + CRegMonitor *regMon = GetRegMonitor(regName); + if (regMon == NULL) + return false; + return RemoveRegMonitor(regMon); + } + + bool CCPUDebug::RemoveRegMonitor(CRegMonitor *regMon) + { + vector::iterator it = find(regMons.begin(), regMons.end(), regMon); + if (it == regMons.end()) + return false; + regMons.erase(it); + delete regMon; + UpdateRegMonArray(); + UpdateExecMasks(); + return true; + } + + bool CCPUDebug::RemoveAllRegMonitors() + { + if (regMons.size() == 0) + return false; + for (vector::iterator it = regMons.begin(); it != regMons.end(); it++) + delete *it; + regMons.clear(); + UpdateRegMonArray(); + UpdateExecMasks(); + return true; + } + + void CCPUDebug::UpdateRegMonArray() + { + if (m_regMonArray != NULL) + delete m_regMonArray; + m_numRegMons = regMons.size(); + if (m_numRegMons > 0) + { + m_regMonArray = new CRegMonitor*[m_numRegMons]; + copy(regMons.begin(), regMons.end(), m_regMonArray); + } + else + m_regMonArray = NULL; + } + + CCodeAnalyser *CCPUDebug::GetCodeAnalyser() + { + if (m_analyser == NULL) + m_analyser = new CCodeAnalyser(this); + return m_analyser; + } + + bool CCPUDebug::FindInMem(UINT32 start, UINT32 end, const char *str, bool matchCase, UINT32 &findAddr) + { + size_t len = strlen(str); + const char *matchEnd = str + len - 1; + const char *matchPos; + if (start <= end) + { + matchPos = str; + for (UINT32 addr = start; addr <= end + len - 1; addr++) + { + char c = (char)ReadMem(addr, 1); + if (matchCase && c == *matchPos || !matchCase && toupper(c) == toupper(*matchPos)) + { + if (matchPos == matchEnd) + { + findAddr = addr - len + 1; + return true; + } + else + matchPos++; + } + else + matchPos = str; + if (addr == 0xFFFFFFFFU) + break; + } + } + else + { + matchPos = matchEnd; + for (UINT32 addr = start + len - 1; addr >= end; addr--) + { + char c = (char)ReadMem(addr, 1); + if (matchCase && c == *matchPos || !matchCase && toupper(c) == toupper(*matchPos)) + { + if (matchPos == str) + { + findAddr = addr; + return true; + } + else + matchPos--; + } + else + matchPos = matchEnd; + if (addr == 0) + break; + } + } + return false; + } + + bool CCPUDebug::FindInMem(UINT32 start, UINT32 end, UINT8 *bytes, unsigned length, UINT32 &findAddr) + { + UINT32 matchEnd = length - 1; + UINT32 matchPos; + if (start <= end) + { + matchPos = 0; + for (UINT32 addr = start; addr <= end; addr++) + { + UINT8 b = (UINT8)ReadMem(addr, 1); + if (b == bytes[matchPos]) + { + if (matchPos == matchEnd) + { + findAddr = addr - matchPos; + return true; + } + else + matchPos++; + } + else + matchPos = 0; + if (addr == 0xFFFFFFFFU) + break; + } + } + else + { + matchPos = matchEnd; + for (UINT32 addr = end; addr >= start; addr--) + { + UINT8 b = (UINT8)ReadMem(addr, 1); + if (b == bytes[matchPos]) + { + if (matchPos == 0) + { + findAddr = addr; + return true; + } + else + matchPos++; + } + else + matchPos = matchEnd; + if (addr == 0) + break; + } + } + return false; + } + +#ifdef DEBUGGER_HASBLOCKFILE + bool CCPUDebug::LoadState(CBlockFile *state) + { + char blockStr[255]; + UINT32 addr; + UINT32 addrEnd; + UINT32 strLen; + char str[4096]; + + // Load labels + sprintf(blockStr, "%s.labels", name); + if (state->FindBlock(blockStr) == OKAY) + { + labels.clear(); + UINT32 numLabels; + state->Read(&numLabels, sizeof(numLabels)); + for (UINT32 i = 0; i < numLabels; i++) + { + state->Read(&addr, sizeof(addr)); + state->Read(&addrEnd, sizeof(addrEnd)); + state->Read(&strLen, sizeof(strLen)); + state->Read(str, strLen * sizeof(char)); + str[strLen] = '\0'; + CLabel *label = new CLabel(this, addr, addrEnd, str); + labels.push_back(label); + } + } + + // Load comments + sprintf(blockStr, "%s.comments", name); + if (state->FindBlock(blockStr) == OKAY) + { + comments.clear(); + UINT32 numComments; + state->Read(&numComments, sizeof(numComments)); + for (UINT32 i = 0; i < numComments; i++) + { + state->Read(&addr, sizeof(addr)); + state->Read(&strLen, sizeof(strLen)); + state->Read(str, strLen * sizeof(char)); + str[strLen] = '\0'; + CComment *comment = new CComment(this, addr, str); + comments.push_back(comment); + } + } + + // Load analyser state + CCodeAnalyser *analyser = GetCodeAnalyser(); + if (!analyser->LoadState(state)) + return false; + + // TODO - load breakpoints, watches, exception/interrupt traps and register monitors + + return true; + } + + bool CCPUDebug::SaveState(CBlockFile *state) + { + char blockStr[255]; + UINT32 addr; + UINT32 addrEnd; + UINT32 strLen; + const char *str; + + // Save labels + sprintf(blockStr, "%s.labels", name); + state->NewBlock(blockStr, __FILE__); + UINT32 numLabels = (UINT32)labels.size(); + state->Write(&numLabels, sizeof(numLabels)); + for (vector::iterator it = labels.begin(); it != labels.end(); it++) + { + addr = (*it)->addr; + addrEnd = (*it)->addrEnd; + str = (*it)->name; + strLen = min(4095, (UINT32)strlen(str)); + state->Write(&addr, sizeof(addr)); + state->Write(&addrEnd, sizeof(addrEnd)); + state->Write(&strLen, sizeof(strLen)); + state->Write(str, strLen * sizeof(char)); + } + + // Save comments + sprintf(blockStr, "%s.comments", name); + state->NewBlock(blockStr, __FILE__); + UINT32 numComments = (UINT32)comments.size(); + state->Write(&numComments, sizeof(numComments)); + for (vector::iterator it = comments.begin(); it != comments.end(); it++) + { + addr = (*it)->addr; + addrEnd = (*it)->addrEnd; + str = (*it)->text; + strLen = min(4095, (UINT32)strlen(str)); + state->Write(&addr, sizeof(addr)); + state->Write(&strLen, sizeof(strLen)); + state->Write(str, strLen * sizeof(char)); + } + + // Save analyser state, if available + if (m_analyser != NULL && !m_analyser->SaveState(state)) + return false; + + // TODO - save breakpoints, watches, exception/interrupt traps and register monitors + + return true; + } +#endif // DEBUGGER_HASBLOCKFILE + + void CCPUDebug::CPUException(UINT16 exCode) + { + if (exCode >= numExCodes) + return; + CException *ex = m_exArray[exCode]; + if (ex == NULL) + return; + + m_exRaised = ex; + ex->count++; + if (!ex->trap) + return; + + m_exTrapped = ex; + debugger->ExceptionTrapped(ex); + m_break = true; + UpdateExecMasks(); + } + + void CCPUDebug::CPUInterrupt(UINT16 intCode) + { + if (intCode >= numIntCodes) + return; + CInterrupt *in = m_intArray[intCode]; + if (in == NULL) + return; + + m_intRaised = in; + in->count++; + if (!in->trap) + return; + + m_intTrapped = in; + debugger->InterruptTrapped(in); + m_break = true; + UpdateExecMasks(); + } + + void CCPUDebug::CPUActive() + { +#ifdef DEBUGGER_HASTHREAD + m_mutex->Lock(); + + active = true; + m_condVar->Signal(); + + m_mutex->Unlock(); +#else + active = true; +#endif // DEBUGGER_HASTHREAD + } + + void CCPUDebug::CPUInactive() + { +#ifdef DEBUGGER_HASTHREAD + m_mutex->Lock(); + + active = false; + m_condVar->Signal(); + + m_mutex->Unlock(); +#else + active = false; +#endif // DEBUGGER_HASTHREAD + } + + void CCPUDebug::WaitCommand(EHaltReason reason) + { +#ifdef DEBUGGER_HASTHREAD + m_mutex->Lock(); + + m_halted = true; + m_condVar->Signal(); + + if (debugger->MakePrimary(this)) + { + if (reason != HaltNone) + debugger->ExecutionHalted(this, reason); + debugger->WaitCommand(this); + + if (!m_stateUpdated) + debugger->ReleasePrimary(); + } + else + { + if (reason != HaltNone) + debugger->ExecutionHalted(this, reason); + while (m_breakWait) + m_condVar->Wait(m_mutex); + } + + m_halted = false; + m_condVar->Signal(); + + m_mutex->Unlock(); +#else + if (reason != HaltNone) + debugger->ExecutionHalted(this, reason); + debugger->WaitCommand(this); + + debugger->ClearBreak(); +#endif // DEBUGGER_HASTHREAD + } + +#ifdef DEBUGGER_HASTHREAD + void CCPUDebug::ForceWait() + { + m_mutex->Lock(); + + m_breakWait = true; + m_break = true; + UpdateExecMasks(); + m_condVar->Signal(); + + m_mutex->Unlock(); + } + + void CCPUDebug::WaitForHalt() + { + m_mutex->Lock(); + + // Wait for CPU to become inactive or halt + while (active && !m_halted) + m_condVar->Wait(m_mutex); + + m_mutex->Unlock(); + } + + void CCPUDebug::ClearWait() + { + m_mutex->Lock(); + + ClearBreak(); + m_condVar->Signal(); + + m_mutex->Unlock(); + } +#endif // DEBUGGER_HASTHREAD + + void CCPUDebug::DebuggerReset() + { + instrCount = 0; + + // TODO - reset breakpoints, watches, I/O ports, exceptions, interrupts and reg monitors + + // Reset code analyser + if (m_analyser != NULL) + m_analyser->Reset(); + } + + void CCPUDebug::DebuggerPolled() + { + cyclesPerPoll = totalCycles - m_prevTotalCycles; + m_prevTotalCycles = totalCycles; + } + + UINT64 CCPUDebug::ReadMem(UINT32 addr, unsigned dataSize) + { + // TODO - currently assumes big-endian - should act according to this.bigEndian + UINT64 dataH, dataL; + switch (dataSize) + { + case 1: + // Sub-class should at the very least override and implement this case + return 0; + case 2: + dataH = ReadMem(addr, 1); + dataL = ReadMem(addr + 1, 1); + return (dataH<<8) | dataL; + case 4: + dataH = ReadMem(addr, 2); + dataL = ReadMem(addr + 2, 2); + return (dataH<<16) | dataL; + case 8: + dataH = ReadMem(addr, 4); + dataL = ReadMem(addr + 4, 4); + return (dataH<<32) | dataL; + default: return 0; + } + } + + bool CCPUDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) + { + // TODO - currently assumes big-endian - should act according to this.bigEndian + UINT64 dataH, dataL; + switch (dataSize) + { + case 1: + // Sub-class should at the very least override and implement this case + return false; + case 2: + dataH = (data>>8)&0xFF; + dataL = data&0xFF; + return WriteMem(addr, 1, dataH) && WriteMem(addr + 1, 1, dataL); + case 4: + dataH = (data>>16)&0xFFFF; + dataL = data&0xFFFF; + return WriteMem(addr, 2, dataH) && WriteMem(addr + 2, 2, dataL); + case 8: + dataH = data>>32; + dataL = data&0xFFFFFFFF; + return WriteMem(addr, 4, dataH) && WriteMem(addr + 4, 4, dataL); + default: return false; + } + } + + UINT64 CCPUDebug::ReadPort(UINT16 portNum) + { + return 0; + } + + bool CCPUDebug::WritePort(UINT16 portNum, UINT64 data) + { + return false; + } + + int CCPUDebug::GetOpLength(UINT32 addr) + { + // Default is to use disassemble to work out instruction length + char mnemonic[255]; + char operands[255]; + return Disassemble(addr, mnemonic, operands); + } + + UINT32 CCPUDebug::GetOpcode(UINT32 addr) + { + return (UINT32)ReadMem(addr, min(4, minInstrLen)); + } +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/CPUDebug.h b/Src/Debugger/CPUDebug.h new file mode 100644 index 0000000..4efdfab --- /dev/null +++ b/Src/Debugger/CPUDebug.h @@ -0,0 +1,1040 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * CPUDebug.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_CPUDEBUG_H +#define INCLUDED_CPUDEBUG_H + +#include +#include +#include + +#include "Types.h" +#include "CodeAnalyser.h" +#include "AddressTable.h" +#include "Breakpoint.h" +#include "Debugger.h" +#include "Exception.h" +#include "Interrupt.h" +#include "DebuggerIO.h" +#include "Register.h" +#include "Watch.h" + +#ifdef DEBUGGER_HASBLOCKFILE +#include "BlockFile.h" +#endif // DEBUGGER_HASBLOCKFILE + +#ifdef DEBUGGER_HASTHREAD +#include "OSD/Thread.h" +#endif // DEBUGGER_HASTHREAD + +#define MAX_EXCEPTIONS 255 +#define MAX_INTERRUPTS 255 +#define MAX_IOPORTS 255 + +namespace Debugger +{ + class CRegion; + class CLabel; + class CComment; + + enum EStepMode + { + StepInto = 0, + StepOver, + StepOut + }; + + enum EOpFlags + { + NormalOp = 0, + JumpSimple = 1, + JumpLoop = 2, + JumpSub = 4, + JumpEx = 8, + ReturnSub = 16, + ReturnEx = 32, + HaltExec = 64, + Relative = 128, + Conditional = 256, + NotFixed = 512 + }; + + /* + * Base class that facilitates the debugging of a particular emulated CPU. + * It holds the debugging hooks that the CPU should call to implement debugging. + * Also contains CPU-specific information and provides access to CPU-specific objects such as CRegister, CException etc. + */ + class CCPUDebug + { + friend class CDebugger; + + private: + bool m_enabled; + bool m_break; +#ifdef DEBUGGER_HASTHREAD + bool m_breakWait; +#endif // DEBUGGER_HASTHREAD + bool m_breakUser; + bool m_halted; + bool m_step; + EStepMode m_stepMode; + bool m_stepBreak; + bool m_steppingOver; + UINT32 m_stepOverAddr; + bool m_steppingOut; + UINT32 m_stepOutAddr; + int m_count; + bool m_until; + UINT32 m_untilAddr; + UINT32 m_execAndMask; + UINT32 m_execOrMask; + UINT32 m_mem8AndMask; + UINT32 m_mem8OrMask; + UINT32 m_mem16AndMask; + UINT32 m_mem16OrMask; + UINT32 m_mem32AndMask; + UINT32 m_mem32OrMask; + UINT32 m_mem64AndMask; + UINT32 m_mem64OrMask; + +#ifdef DEBUGGER_HASTHREAD + CMutex *m_mutex; + CCondVar *m_condVar; +#endif // DEBUGGER_HASTHREAD + + CException *m_exArray[MAX_EXCEPTIONS]; + CInterrupt *m_intArray[MAX_INTERRUPTS]; + CPortIO *m_portArray[MAX_IOPORTS]; + + CAddressTable *m_mappedIOTable; + CAddressTable *m_memWatchTable; + CAddressTable *m_bpTable; + + int m_numRegMons; + CRegMonitor **m_regMonArray; + + bool ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref); + + void CheckRead(UINT32 addr, unsigned dataSize, UINT64 data); + + void CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data); + + void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead); + + void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isRead); + + void AttachToDebugger(CDebugger *theDebugger); + + void DetachFromDebugger(CDebugger *theDebugger); + + void UpdateRegMonArray(); + + void UpdateIOWatchNums(); + + void UpdateMemWatchNums(); + + void UpdateExecMasks(); + + void UpdateMemMasks(); + + bool CheckExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles); + + protected: + CCodeAnalyser *m_analyser; + + bool m_stateUpdated; + CException *m_exRaised; + CException *m_exTrapped; + CInterrupt *m_intRaised; + CInterrupt *m_intTrapped; + CBreakpoint *m_bpReached; + CWatch *m_memWatchTriggered; + CWatch *m_ioWatchTriggered; + CRegMonitor *m_regMonTriggered; + + UINT64 m_prevTotalCycles; + + CCPUDebug(const char *cpuType, const char *cpuName, + UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen); + + // + // Protected virtual methods for sub-class to implement + // + + virtual bool UpdatePC(UINT32 pc) = 0; + + virtual bool ForceException(CException *ex) = 0; + + virtual bool ForceInterrupt(CInterrupt *in) = 0; + + public: + const char *type; + const char *name; + + const UINT8 minInstrLen; + const UINT8 maxInstrLen; + const bool bigEndian; + const UINT8 memBusWidth; + const UINT8 maxMnemLen; + + EFormat addrFmt; + EFormat portFmt; + EFormat dataFmt; + + CDebugger *debugger; + + UINT16 numExCodes; + UINT16 numIntCodes; + UINT16 numPorts; + UINT32 memSize; + + bool active; + UINT64 instrCount; + UINT64 totalCycles; + UINT64 cyclesPerPoll; + UINT32 pc; + UINT32 opcode; + + std::vector regs; + std::vector exceps; + std::vector inters; + std::vector ios; + // TODO - should use map for T=CRegion,CLabel&CComment so that look-ups via address are faster + std::vector regions; + std::vector labels; + std::vector comments; + std::vector memWatches; + std::vector ioWatches; + std::vector bps; + std::vector regMons; + + virtual ~CCPUDebug(); + + // + // Methods to define CPU registers (must be called before attached to debugger) + + CPCRegister *AddPCRegister(const char *name, const char *group); + + CAddrRegister *AddAddrRegister(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL); + + CIntRegister *AddInt8Register(const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL); + + CIntRegister *AddInt16Register(const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL); + + CIntRegister *AddInt32Register(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL); + + CIntRegister *AddInt64Register(const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc = NULL); + + CStatusRegister *AddStatus8Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL); + + CStatusRegister *AddStatus16Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL); + + CStatusRegister *AddStatus32Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL); + + CFPointRegister *AddFPointRegister(const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc = NULL); + + // + // Methods to add exceptions and interrupts (must be called before attached to debugger) + // + + CException *AddException(const char *id, UINT16 code, const char *name); + + CInterrupt *AddInterrupt(const char *id, UINT16 code, const char *name); + + // + // Methods to define memory layout (must be called before attached to debugger) + // + + CRegion *AddRegion(UINT32 start, UINT32 end, bool isCode, bool isReadOnly, const char *name); + + CPortIO *AddPortIO(UINT16 portNum, unsigned dataSize, const char *name, const char *group); + + CMappedIO *AddMappedIO(UINT32 addr, unsigned dataSize, const char *name, const char *group); + + // + // Parsing and formatting methods + // + + bool ParseData(const char *str, unsigned dataSize, UINT64 *data, bool registers = true); + + void FormatData(char *str, unsigned dataSize, UINT64 data); + + bool ParseAddress(const char *str, UINT32 *addr, bool customLabels = true, bool autoLabels = true, bool registers = true); + + void FormatAddress(char *str, UINT32 addr, bool customLabels = false, ELabelFlags autoLabelFlags = LFNone); + + void FormatJumpAddress(char *str, UINT32 jumpAddr, EOpFlags opFlags); + + bool ParsePortNum(const char *str, UINT16 *portNum, bool customLabels = true, bool registers = true); + + void FormatPortNum(char *str, UINT16 portNum, bool customLabels = false); + + // + // Checking methods that hook into CPU emulation code + // + + /* + * Should be called after every 8-bit read. + */ + void CheckRead8(UINT32 addr, UINT8 data); + + /* + * Should be called after every 16-bit read. + */ + void CheckRead16(UINT32 addr, UINT16 data); + + /* + * Should be called after every 32-bit read. + */ + void CheckRead32(UINT32 addr, UINT32 data); + + /* + * Should be called after every 64-bit read. + */ + void CheckRead64(UINT32 addr, UINT64 data); + + /* + * Should be called after every 8-bit write. + */ + void CheckWrite8(UINT32 addr, UINT8 data); + + /* + * Should be called after every 16-bit write. + */ + void CheckWrite16(UINT32 addr, UINT16 data); + + /* + * Should be called after every 32-bit write. + */ + void CheckWrite32(UINT32 addr, UINT32 data); + + /* + * Should be called after every 64-bit write. + */ + void CheckWrite64(UINT32 addr, UINT64 data); + + /* + * Should be called after every read from an I/O port. + */ + void CheckPortInput(UINT16 portNum, UINT64 data); + + /* + * Should be called after every write to an I/O port. + */ + void CheckPortOutput(UINT16 portNum, UINT64 data); + + /* + * Should be called by CPU when it becomes active and starts executing an instruction loop. + * This call is needed so that the debugger can handle multi-threading of CPUs. + */ + void CPUActive(); + + /* + * Should be called by CPU when it becomes inactive after having finished executing an instruction loop. + * This call is needed so that the debugger can handle multi-threading of CPUs. + */ + void CPUInactive(); + + /* + * Should be called by CPU before every instruction. + * If returns true, then PC may have been changed by user and/or an exception/interrupt may have forced, so CPU core should + * check for this and handle appropriately. Otherwise, it can continue to execute as normal. + */ + bool CPUExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles); + + /* + * Should be called by CPU whenever a CPU exception is raised (and before the exception handler is executed). + */ + virtual void CPUException(UINT16 exCode); + + /* + * Should be called by CPU whenever a CPU interrupt is raised (and before the interrupt handler is executed). + */ + virtual void CPUInterrupt(UINT16 intCode); + + void WaitCommand(EHaltReason reason); + +#ifdef DEBUGGER_HASTHREAD + void ForceWait(); + + void WaitForHalt(); + + void ClearWait(); +#endif // DEBUGGER_HASTHREAD + + // + // Execution control + // + + bool IsEnabled(); + + void SetEnabled(bool enabled); + + void ForceBreak(bool user); + + void ClearBreak(); + + void SetContinue(); + + void SetStepMode(EStepMode stepMode); + + void SetCount(int count); + + void SetUntil(UINT32 untilAddr); + + bool SetPC(UINT32 newPC); + + bool GenerateException(CException *ex); + + bool GenerateInterrupt(CInterrupt *in); + + // + // Register access + // + + CRegister *GetRegister(const char *name); + + // + // Exception access + // + + CException *GetException(const char *id); + + CException *GetException(UINT16 code); + + // + // Interrupt access + // + + CInterrupt *GetInterrupt(const char *id); + + CInterrupt *GetInterrupt(UINT16 code); + + // + // I/O access + // + + CPortIO *GetPortIO(UINT16 portNum); + + CMappedIO *GetMappedIO(UINT32 addr); + + // + // Region access + // + + CRegion *GetRegion(UINT32 addr); + + // + // Label handling + // + + CLabel *AddLabel(UINT32 addr, const char *name); + + CLabel *GetLabel(UINT32 addr); + + CLabel *GetLabel(const char *name); + + bool RemoveLabel(const char *name); + + bool RemoveLabel(UINT32 addr); + + bool RemoveLabel(CLabel *label); + + bool RemoveAllLabels(); + + // + // Comment handling + // + + CComment *AddComment(UINT32 addr, const char *commentText); + + CComment *GetComment(UINT32 addr); + + bool RemoveComment(UINT32 addr); + + bool RemoveComment(CComment *comment); + + bool RemoveAllComments(); + + // + // Watch handling + // + + CSimpleWatch *AddSimpleMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite); + + CCountWatch *AddCountMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned count); + + CMatchWatch *AddMatchMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, std::vector &dataSeq); + + CPrintWatch *AddPrintMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite); + + CCaptureWatch *AddCaptureMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned maxLen); + + bool RemoveAllMemWatches(); + + bool RemoveAllIOWatches(); + + bool RemoveMemWatch(UINT32 addr, unsigned size); + + CWatch *GetMemWatch(UINT32 addr, unsigned size); + + void AddWatch(CWatch *watch); + + bool RemoveWatch(CWatch *watch); + + // + // Breakpoint handling + // + + CSimpleBreakpoint *AddSimpleBreakpoint(UINT32 addr); + + CCountBreakpoint *AddCountBreakpoint(UINT32 addr, unsigned count); + + CPrintBreakpoint *AddPrintBreakpoint(UINT32 addr); + + void AddBreakpoint(CBreakpoint *bp); + + CBreakpoint *GetBreakpoint(UINT32 addr); + + bool RemoveBreakpoint(UINT32 addr); + + bool RemoveBreakpoint(CBreakpoint *bp); + + bool RemoveAllBreakpoints(); + + void UpdateBreakpointNums(); + + // + // Register monitor handling + // + + CRegMonitor *AddRegMonitor(const char *regName); + + CRegMonitor *GetRegMonitor(const char *regName); + + void AddRegMonitor(CRegMonitor *regMon); + + bool RemoveRegMonitor(const char *regName); + + bool RemoveRegMonitor(CRegMonitor *regMon); + + bool RemoveAllRegMonitors(); + + // + // Code analyser + // + + CCodeAnalyser *GetCodeAnalyser(); + + // + // Memory searching + // + + bool FindInMem(UINT32 start, UINT32 end, const char *str, bool matchCase, UINT32 &findAddr); + + bool FindInMem(UINT32 start, UINT32 end, UINT8 *bytes, unsigned length, UINT32 &findAddr); + + // + // Debugger state loading/saving + // + +#ifdef DEBUGGER_HASBLOCKFILE + bool LoadState(CBlockFile *state); + + bool SaveState(CBlockFile *state); +#endif // DEBUGGER_HASBLOCKFILE + + // + // Public virtual methods for sub-class to implement + // + + virtual void AttachToCPU() = 0; + + virtual void DetachFromCPU() = 0; + + virtual void DebuggerReset(); + + virtual void DebuggerPolled(); + + virtual UINT32 GetResetAddr() = 0; + + virtual UINT64 ReadMem(UINT32 addr, unsigned dataSize); + + virtual bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data); + + virtual UINT64 ReadPort(UINT16 portNum); + + virtual bool WritePort(UINT16 portNum, UINT64 data); + + virtual int Disassemble(UINT32 addr, char *mnemonic, char *operands) = 0; + + virtual int GetOpLength(UINT32 addr); + + // Returns head (no more than 32-bits or min instr length) of full opcode for use with following methods + virtual UINT32 GetOpcode(UINT32 addr); + + virtual EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode) = 0; + + virtual bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) = 0; + + virtual bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0; + + virtual bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0; + + virtual bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr) = 0; + + virtual bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) = 0; + }; + + // + // Inlined methods + // + + inline bool CCPUDebug::ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref) + { + UINT32 refAddr = ref->addr; + unsigned refSize = ref->size; + unsigned offset; + if (refAddr >= addr) + { + offset = refAddr - addr; + // dataSize will always be >= offset + dataSize -= offset; + if (dataSize <= refSize) + return false; + addr = refAddr + refSize; + dataSize -= refSize; + //data <<= (8 * (offset + refSize)); + return true; + } + else + { + offset = refAddr + refSize - addr; + if (dataSize <= offset) + return false; + addr += offset; + dataSize -= offset; + //data <<= (8 * offset); + return true; + } + } + + inline void CCPUDebug::CheckRead(UINT32 addr, unsigned dataSize, UINT64 data) + { + // TODO - currently assumes big-endian - should act according to this->bigEndian + + // For reads larger than 1 byte, care is taken with mapped I/O or watches that overlap within the read region + while (dataSize > 0) + { + // Check if address is mapped I/O + CMappedIO *mappedIO = NULL; + if (m_mappedIOTable != NULL) + { + mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize); + if (mappedIO != NULL) + { + // Check with I/O if its watch is triggered + if (mappedIO->CheckInput(addr, dataSize, data)) + IOWatchTriggered(mappedIO->watch, mappedIO, data, true); + + // See if still have remaining data to check + if (!ShiftAddress(addr, dataSize, data, mappedIO)) + return; + } + } + + // Check if address has a watch + CWatch *watch = NULL; + if (m_memWatchTable != NULL) + { + watch = (CWatch*)m_memWatchTable->Get(addr, dataSize); + if (watch != NULL) + { + // Check if watch is triggered + if (watch->CheckRead(addr, dataSize, data)) + MemWatchTriggered(watch, addr, dataSize, data, true); + + // See if still have remaining data to check + if (!ShiftAddress(addr, dataSize, data, watch)) + return; + } + else if (mappedIO == NULL) + return; + } + else if (mappedIO == NULL) + return; + } + } + + inline void CCPUDebug::CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data) + { + // TODO - currently assumes big-endian - should act according to this->bigEndian + + // For writes larger than 1 byte, care is taken with mapped I/O or watches that overlap within the written region + while (dataSize > 0) + { + // Check if writing to mapped I/O address + CMappedIO *mappedIO = NULL; + if (m_mappedIOTable != NULL) + { + mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize); + if (mappedIO != NULL) + { + // Check with I/O if its watch is triggered + if (mappedIO->CheckOutput(addr, dataSize, data)) + IOWatchTriggered(mappedIO->watch, mappedIO, data, false); + + // See if still have remaining data to check + if (!ShiftAddress(addr, dataSize, data, mappedIO)) + return; + } + } + + // Check if address has a watch + CWatch *watch = NULL; + if (m_memWatchTable != NULL) + { + watch = (CWatch*)m_memWatchTable->Get(addr, dataSize); + if (watch != NULL) + { + // Check if watch is triggered + if (watch->CheckWrite(addr, dataSize, data)) + MemWatchTriggered(watch, addr, dataSize, data, false); + + // See if still have remaining data to check + if (!ShiftAddress(addr, dataSize, data, watch)) + return; + } + else if (mappedIO == NULL) + return; + } + else if (mappedIO == NULL) + return; + } + } + + inline void CCPUDebug::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool read) + { + m_memWatchTriggered = watch; + debugger->MemWatchTriggered(watch, addr, dataSize, data, read); + watch->Reset(); + m_break = true; + UpdateExecMasks(); + } + + inline void CCPUDebug::IOWatchTriggered(CWatch* watch, CIO *io, UINT64 data, bool read) + { + m_ioWatchTriggered = watch; + debugger->IOWatchTriggered(watch, io, data, read); + watch->Reset(); + m_break = true; + UpdateExecMasks(); + } + + inline void CCPUDebug::CheckRead8(UINT32 addr, UINT8 data) + { + if ((addr&m_mem8AndMask) != m_mem8AndMask || (addr&m_mem8OrMask) != 0) + return; + + // Check if reading from mapped I/O address + unsigned dataSize = 1; + if (m_mappedIOTable != NULL) + { + CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr); + if (mappedIO != NULL) + { + if (mappedIO->CheckInput(addr, dataSize, data)) + IOWatchTriggered(mappedIO->watch, mappedIO, data, true); + return; + } + } + // Check if address has a watch + if (m_memWatchTable == NULL) + return; + CWatch *watch = (CWatch*)m_memWatchTable->Get(addr); + if (watch != NULL && watch->CheckRead(addr, dataSize, data)) + MemWatchTriggered(watch, addr, dataSize, data, true); + } + + inline void CCPUDebug::CheckRead16(UINT32 addr, UINT16 data) + { + if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0) + CheckRead(addr, 2, data); + } + + inline void CCPUDebug::CheckRead32(UINT32 addr, UINT32 data) + { + if ((addr&m_mem32AndMask) == m_mem32AndMask && (addr&m_mem32OrMask) == 0) + CheckRead(addr, 4, data); + } + + inline void CCPUDebug::CheckRead64(UINT32 addr, UINT64 data) + { + if ((addr&m_mem64AndMask) == m_mem64AndMask && (addr&m_mem64OrMask) == 0) + CheckRead(addr, 8, data); + } + + inline void CCPUDebug::CheckWrite8(UINT32 addr, UINT8 data) + { + if ((addr&m_mem8AndMask) != m_mem8AndMask || (addr&m_mem8OrMask) != 0) + return; + + // Check if writing to mapped I/O address + unsigned dataSize = 1; + if (m_mappedIOTable != NULL) + { + CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr); + if (mappedIO != NULL) + { + if (mappedIO->CheckOutput(addr, dataSize, data)) + IOWatchTriggered(mappedIO->watch, mappedIO, data, false); + return; + } + } + // Check if address has a watch + if (m_memWatchTable == NULL) + return; + CWatch *watch = (CWatch*)m_memWatchTable->Get(addr); + if (watch != NULL && watch->CheckWrite(addr, dataSize, data)) + MemWatchTriggered(watch, addr, dataSize, data, false); + } + + inline void CCPUDebug::CheckWrite16(UINT32 addr, UINT16 data) + { + if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0) + CheckWrite(addr, 2, data); + } + + inline void CCPUDebug::CheckWrite32(UINT32 addr, UINT32 data) + { + if ((addr&m_mem32AndMask) == m_mem32AndMask && (addr&m_mem32OrMask) == 0) + CheckWrite(addr, 4, data); + } + + inline void CCPUDebug::CheckWrite64(UINT32 addr, UINT64 data) + { + if ((addr&m_mem64AndMask) == m_mem64AndMask && (addr&m_mem64OrMask) == 0) + CheckWrite(addr, 8, data); + } + + inline void CCPUDebug::CheckPortInput(UINT16 portNum, UINT64 data) + { + if (portNum >= numPorts) + return; + CPortIO *port = m_portArray[portNum]; + if (port != NULL && port->CheckInput(data)) + IOWatchTriggered(port->watch, port, data, true); + } + + inline void CCPUDebug::CheckPortOutput(UINT16 portNum, UINT64 data) + { + if (portNum >= numPorts) + return; + CPortIO *port = m_portArray[portNum]; + if (port != NULL && port->CheckOutput(data)) + IOWatchTriggered(port->watch, port, data, false); + } + + inline bool CCPUDebug::CPUExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles) + { + // Check if should check execution flow + if ((newPC&m_execAndMask) == m_execAndMask && (newPC&m_execOrMask) == 0) + return CheckExecute(newPC, newOpcode, lastCycles); + else + { + // If not, then just update instruction count, total cycles counts, pc and opcode + instrCount++; + totalCycles += lastCycles; + pc = newPC; + opcode = newOpcode; + return false; + } + } + + inline bool CCPUDebug::CheckExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles) + { + // Check not just updating state + bool stepBreak, countBreak, untilBreak; + if (!m_stateUpdated) + { + // If so, then first check to see if any registers have changed from previous instruction before updating pc and opcode + if (m_numRegMons > 0) + { + for (int i = 0; i < m_numRegMons; i++) + { + CRegMonitor *regMon = m_regMonArray[i]; + if (regMon->HasChanged()) + { + m_regMonTriggered = regMon; + debugger->MonitorTriggered(regMon); + regMon->Reset(); + m_break = true; + } + } + } + + // Now update instruction count, total cycles count, pc and opcode + instrCount++; + totalCycles += lastCycles; + pc = newPC; + opcode = newOpcode; + + // Next check for breakpoints at new pc and opcode + if (m_bpTable != NULL) + { + CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(pc); + if (bp != NULL && bp->Check(pc, opcode)) + { + m_bpReached = bp; + debugger->BreakpointReached(bp); + m_break = true; + } + } + + // Check if currently implementing a step mode + if (m_step) + { + if (instrCount == 1) + stepBreak = true; + else + { + switch (m_stepMode) + { + case StepInto: + // Step-into always breaks + stepBreak = true; + break; + case StepOver: + // Step-over breaks if was not stepping over a jump or was stepping over and has returned from jump + stepBreak = !m_steppingOver || pc == m_stepOverAddr; + break; + case StepOut: + // Step-out steps over any jumps and breaks when it reaches a return or an exception/interrupt is raised + if (m_exRaised != NULL || m_intRaised != NULL) + stepBreak = true; + else if (m_steppingOver) + { + if (pc == m_stepOverAddr) + m_steppingOver = false; + stepBreak = false; + } + else + stepBreak = m_steppingOut && pc == m_stepOutAddr; + break; + default: + stepBreak = false; + break; + } + } + } + else + stepBreak = false; + + // Lastly, check if counting instructions or if running until a given address + countBreak = m_count > 0 && --m_count == 0; + untilBreak = m_until && pc == m_untilAddr; + } + else + { + // Just updating state so update pc and opcode, but do not update instruction count + pc = newPC; + opcode = newOpcode; + + stepBreak = false; + countBreak = false; + untilBreak = false; + } + + // If a break has been forced, state was updated, stepping through code or have run a set number of instructions or to a + // given address then break now and wait for command from user + if (m_break || m_stateUpdated || stepBreak || countBreak || untilBreak) + { + // See if execution halt was caused by user manually breaking execution in some manner + EHaltReason reason = HaltNone; + if (m_stateUpdated) reason = (EHaltReason)(reason | HaltState); + if (m_breakUser) reason = (EHaltReason)(reason | HaltUser); + if (stepBreak) reason = (EHaltReason)(reason | HaltStep); + if (countBreak) reason = (EHaltReason)(reason | HaltCount); + if (untilBreak) reason = (EHaltReason)(reason | HaltUntil); + + // Keep hold of breakpoint, if any, and its address so that can reset it later + UINT32 bpAddr = 0; + CBreakpoint *bpToReset = NULL; + if (m_bpReached != NULL) + { + bpAddr = m_bpReached->addr; + bpToReset = m_bpReached; + } + + // Reset all control flags + m_stateUpdated = false; + m_step = false; + m_steppingOver = false; + m_steppingOut = false; + m_count = 0; + m_until = false; + m_exRaised = NULL; + m_exTrapped = NULL; + m_intRaised = NULL; + m_intTrapped = NULL; + m_bpReached = NULL; + m_memWatchTriggered = NULL; + m_ioWatchTriggered = NULL; + m_regMonTriggered = NULL; + UpdateExecMasks(); + + // Wait for instruction from user + WaitCommand(reason); + + // Reset breakpoint, if any + if (bpToReset != NULL && m_bpTable != NULL) + { + // Check breakpoint to reset was not deleted by user + CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(bpAddr); + if (bp == bpToReset) + bp->Reset(); + } + + // If stepping over, get step over address if available + if (m_step && m_stepMode == StepOver) + m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr); + } + + // If stepping out, then make sure step over any jumps and if encounter a return instruction then break at return address + if (m_step && m_stepMode == StepOut) + { + if (!m_steppingOver) + m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr); + if (!m_steppingOver) + m_steppingOut = GetReturnAddr(pc, opcode, m_stepOutAddr); + } + + return m_stateUpdated; + } +} + +#endif // INCLUDED_CPUDEBUG_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CodeAnalyser.cpp b/Src/Debugger/CodeAnalyser.cpp new file mode 100644 index 0000000..2f3229a --- /dev/null +++ b/Src/Debugger/CodeAnalyser.cpp @@ -0,0 +1,740 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * CodeAnalyser.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "CodeAnalyser.h" +#include "CPUDebug.h" +#include "Label.h" + +#include +#include + +using namespace std; + +namespace Debugger +{ + CEntryPoint::CEntryPoint(const CEntryPoint &other) : addr(other.addr), autoFlag(other.autoFlag) + { + if (other.autoLabel != NULL) + { + strncpy(autoLabelStr, other.autoLabel, 254); + autoLabelStr[254] = '\0'; + autoLabel = autoLabelStr; + } + else + autoLabel = NULL; + } + + CEntryPoint::CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel) : + addr(eAddr), autoFlag(eAutoFlag) + { + if (eAutoLabel != NULL) + { + strncpy(autoLabelStr, eAutoLabel, 254); + autoLabelStr[254] = '\0'; + autoLabel = autoLabelStr; + } + else + autoLabel = NULL; + } + + CEntryPoint &CEntryPoint::operator=(const CEntryPoint &other) + { + addr = other.addr; + autoFlag = other.autoFlag; + if (other.autoLabel != NULL) + { + strncpy(autoLabelStr, other.autoLabel, 254); + autoLabelStr[254] = '\0'; + autoLabel = autoLabelStr; + } + else + autoLabel = NULL; + return *this; + } + + bool CEntryPoint::operator==(const CEntryPoint &other) + { + return addr == other.addr && autoFlag == other.autoFlag; + } + + bool CEntryPoint::operator!=(const CEntryPoint &other) + { + return addr != other.addr || autoFlag != other.autoFlag; + } + + const char *CAutoLabel::s_defaultLabelFmts[] = { "Entry%s", "Ex%s", "Int%s", "Jmp%s", "Loop%s", "Sub%s", NULL }; + + const unsigned CAutoLabel::numLabelFlags = sizeof(s_defaultLabelFmts) / sizeof(char*); + + ELabelFlags CAutoLabel::GetLabelFlag(int index) + { + if (index < 0 || index >= numLabelFlags) + return LFNone; + return (ELabelFlags)(1 << index); + } + + int CAutoLabel::GetFlagIndex(ELabelFlags flag) + { + switch (flag) + { + case LFEntryPoint: return 0; + case LFExcepHandler: return 1; + case LFInterHandler: return 2; + case LFJumpTarget: return 3; + case LFLoopPoint: return 4; + case LFSubroutine: return 5; + case LFUnseenCode: return 6; + default: return -1; + } + } + + const char *CAutoLabel::GetFlagString(ELabelFlags flag) + { + switch (flag) + { + case LFEntryPoint: return "Entry Point"; + case LFExcepHandler: return "Exception Handler"; + case LFInterHandler: return "Interrupt Handler"; + case LFJumpTarget: return "Jump Target"; + case LFLoopPoint: return "Loop Point"; + case LFSubroutine: return "Subroutine"; + case LFUnseenCode: return "Unseen Code"; + default: return ""; + } + } + + CAutoLabel::CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr) : CAddressRef(lCPU, lAddr), flags(LFNone), m_acquired(0) + { + memset(m_subLabels, NULL, sizeof(m_subLabels)); + } + + CAutoLabel::~CAutoLabel() + { + // Delete all sub-labels + for (int index = 0; index < numLabelFlags; index++) + { + if (m_subLabels[index] != NULL) + { + delete[] m_subLabels[index]; + m_subLabels[index] = NULL; + } + } + } + + void CAutoLabel::Acquire() + { + m_acquired++; + } + + void CAutoLabel::Release() + { + if (--m_acquired == 0) + delete this; + } + + void CAutoLabel::AddFlag(ELabelFlags flag, const char *subLabel) + { + int index = GetFlagIndex(flag); + if (index == -1) + return; + flags = (ELabelFlags)((unsigned)flags | (unsigned)flag); + if (subLabel != NULL) + { + size_t len = strlen(subLabel); + char *label = new char[len + 1]; + strncpy(label, subLabel, len); + label[len] = '\0'; + m_subLabels[index] = label; + } + else + m_subLabels[index] = CreateDefaultSubLabel(flag); + } + + bool CAutoLabel::GetLabel(char *labelStr, ELabelFlags subFlags) + { + char *p = labelStr; + *p = '\0'; + for (int index = 0; index < numLabelFlags; index++) + { + ELabelFlags flag = GetLabelFlag(index); + if (!(subFlags & flag)) + continue; + const char *subLabel = m_subLabels[index]; + if (subLabel == NULL) + continue; + if (p > labelStr) + { + (*p++) = '/'; + *p = '\0'; + } + strcat(p, subLabel); + p += strlen(subLabel); + *p = '\0'; + } + return p > labelStr; + } + + bool CAutoLabel::ContainsSubLabel(const char *subLabel) + { + for (int i = 0; i < numLabelFlags; i++) + { + if (m_subLabels[i] != NULL && stricmp(subLabel, m_subLabels[i]) == 0) + return true; + } + return false; + } + + const char *CAutoLabel::CreateDefaultSubLabel(ELabelFlags flag) + { + int index = GetFlagIndex(flag); + if (index == -1) + return NULL; + const char *labelFmt = s_defaultLabelFmts[index]; + if (labelFmt == NULL) + return NULL; + char addrStr[50]; + cpu->debugger->FormatData(addrStr, Hex, (unsigned)(cpu->memBusWidth / 8), (UINT64)addr); + char *label = new char[255]; + sprintf(label, labelFmt, addrStr); + return label; + } + + CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser) : analyser(aAnalyser) + { + // + } + + CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector &entryPoints, vector &unseenEntryAddrs) : + analyser(aAnalyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs), + m_seenIndices(aTotalIndices), m_validIndices(aTotalIndices), m_acquired(0) + { + // + } + + CCodeAnalysis::CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector &entryPoints, vector &unseenEntryAddrs) : + analyser(oldAnalysis->analyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs), + m_seenIndices(oldAnalysis->m_seenIndices), m_validIndices(oldAnalysis->m_validIndices), + m_autoLabelsMap(oldAnalysis->m_autoLabelsMap), m_acquired(0), validIndexSet(oldAnalysis->validIndexSet) + { + for (map::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++) + it->second->Acquire(); + } + + CCodeAnalysis::~CCodeAnalysis() + { + for (vector::iterator it = autoLabels.begin(); it != autoLabels.end(); it++) + (*it)->Release(); + autoLabels.clear(); + m_autoLabelsMap.clear(); + } + + void CCodeAnalysis::Acquire() + { + m_acquired++; + } + + void CCodeAnalysis::Release() + { + if (--m_acquired == 0) + delete this; + } + + void CCodeAnalysis::FinishAnalysis() + { + for (map::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++) + autoLabels.push_back(it->second); + } + + bool CCodeAnalysis::IsAddrValid(UINT32 addr) + { + unsigned index; + if (!analyser->GetIndexOfAddr(addr, index)) + return false; + return IsIndexValid(index); + } + + bool CCodeAnalysis::GetNextValidAddr(UINT32 &addr) + { + unsigned index; + if (!analyser->GetIndexOfAddr(addr, index)) + return false; + if (IsIndexValid(index)) + return true; + set::iterator it = validIndexSet.lower_bound(index); + if (it == validIndexSet.end()) + return false; + return analyser->GetAddrOfIndex(*it, addr); + } + + bool CCodeAnalysis::IsIndexValid(unsigned index) + { + return index < m_validIndices.size() && m_validIndices[index]; + } + + bool CCodeAnalysis::GetNextValidIndex(unsigned &index) + { + if (IsIndexValid(index)) + return true; + set::iterator it = validIndexSet.lower_bound(index); + if (it == validIndexSet.end()) + return false; + index = *it; + return true; + } + + bool CCodeAnalysis::HasSeenAddr(UINT32 addr) + { + unsigned index; + if (!analyser->GetIndexOfAddr(addr, index)) + return false; + return HaveSeenIndex(index); + } + + bool CCodeAnalysis::HaveSeenIndex(unsigned index) + { + return index < m_seenIndices.size() && m_seenIndices[index]; + } + + CAutoLabel *CCodeAnalysis::GetAutoLabel(UINT32 addr) + { + map::iterator it = m_autoLabelsMap.find(addr); + if (it == m_autoLabelsMap.end()) + return NULL; + return it->second; + } + + CAutoLabel *CCodeAnalysis::GetAutoLabel(const char *subLabel) + { + for (vector::iterator it = autoLabels.begin(); it != autoLabels.end(); it++) + { + if ((*it)->ContainsSubLabel(subLabel)) + return *it; + } + return NULL; + } + + vector CCodeAnalysis::GetAutoLabels(ELabelFlags flag) + { + vector matched; + for (vector::iterator it = autoLabels.begin(); it != autoLabels.end(); it++) + { + if ((*it)->flags & flag) + matched.push_back(*it); + } + return matched; + } + + CCodeAnalyser::CCodeAnalyser(CCPUDebug *aCPU) : cpu(aCPU), emptyAnalysis(this), analysis(&emptyAnalysis) + { + instrAlign = cpu->minInstrLen; + + totalIndices = 0; + for (vector::iterator it = cpu->regions.begin(); it != cpu->regions.end(); it++) + { + if (!(*it)->isCode) + continue; + m_codeRegions.push_back(*it); + totalIndices += (*it)->size / instrAlign; + m_indexBounds.push_back(totalIndices); + } + } + + CCodeAnalyser::~CCodeAnalyser() + { + if (analysis != &emptyAnalysis) + analysis->Release(); + } + + void CCodeAnalyser::Reset() + { + CCodeAnalysis *oldAnalysis = analysis; + analysis = &emptyAnalysis; + if (oldAnalysis != &emptyAnalysis) + oldAnalysis->Release(); + } + + bool CCodeAnalyser::GetAddrOfIndex(unsigned index, UINT32 &addr) + { + unsigned regIndex = 0; + unsigned prevBound = 0; + for (vector::iterator it = m_indexBounds.begin(); it != m_indexBounds.end(); it++) + { + if (*it > index) + { + addr = m_codeRegions[regIndex]->addr + (UINT32)(index - prevBound) * instrAlign; + return true; + } + prevBound = *it; + regIndex++; + } + return false; + } + + bool CCodeAnalyser::GetIndexOfAddr(UINT32 addr, unsigned &index) + { + unsigned regIndex = 0; + for (vector::iterator it = m_codeRegions.begin(); it != m_codeRegions.end(); it++) + { + if ((*it)->addr <= addr && addr <= (*it)->addrEnd) + { + unsigned offset = (unsigned)((addr - (*it)->addr) / instrAlign); + index = (regIndex > 0 ? m_indexBounds[regIndex - 1] + offset : (unsigned)offset); + return true; + } + regIndex++; + } + return false; + } + + void CCodeAnalyser::CheckEntryPoints(vector &entryPoints, vector &unseenEntryAddrs, vector &prevPoints, + bool &needsAnalysis, bool &reanalyse) + { + needsAnalysis = false; + + // Gather entry points + GatherEntryPoints(entryPoints, unseenEntryAddrs, reanalyse); + if (reanalyse) + { + needsAnalysis = true; + return; + } + + // Compare new entry points with previous ones + for (size_t i = 0; i < entryPoints.size(); i++) + { + // Check if have more than before + if (i >= prevPoints.size()) + { + needsAnalysis = true; + return; + } + // Check if any have changed + if (entryPoints[i] != prevPoints[i]) + { + // If entry points, exception handlers or interrupt handlers have changed, then force reanalysis + if (entryPoints[i].autoFlag == LFEntryPoint || prevPoints[i].autoFlag == LFEntryPoint || + entryPoints[i].autoFlag == LFExcepHandler || prevPoints[i].autoFlag == LFExcepHandler || + entryPoints[i].autoFlag == LFInterHandler || prevPoints[i].autoFlag == LFInterHandler) + reanalyse = true; + needsAnalysis = true; + return; + } + } + // Check if have less than before + if (entryPoints.size() < prevPoints.size()) + { + // If so, force reanalysis + reanalyse = true; + needsAnalysis = true; + return; + } + } + + void CCodeAnalyser::GatherEntryPoints(vector &entryPoints, vector &unseenEntryAddrs, bool &reanalyse) + { + char labelStr[255]; + UINT32 addr; + unsigned index; + + entryPoints.clear(); + reanalyse = false; + + // Add reset address as main entry point + AddEntryPoint(entryPoints, cpu->GetResetAddr(), LFEntryPoint, "MainEntry"); + + // Add exception handlers as entry points + for (vector::iterator it = cpu->exceps.begin(); it != cpu->exceps.end(); it++) + { + if (!cpu->GetHandlerAddr(*it, addr)) + continue; + sprintf(labelStr, "Ex%s", (*it)->id); + AddEntryPoint(entryPoints, addr, LFExcepHandler, labelStr); + } + + // Add interrupt handlers as entry points + for (vector::iterator it = cpu->inters.begin(); it != cpu->inters.end(); it++) + { + if (!cpu->GetHandlerAddr(*it, addr)) + continue; + sprintf(labelStr, "Int%s", (*it)->id); + AddEntryPoint(entryPoints, addr, LFInterHandler, labelStr); + } + + // Add custom entry addresses + unsigned i = 0; + for (vector::iterator it = m_customEntryAddrs.begin(); it != m_customEntryAddrs.end(); it++) + { + sprintf(labelStr, "Custom%u", i++); + AddEntryPoint(entryPoints, *it, LFEntryPoint, labelStr); + } + + // If current PC address is at an unseen location or at location that was previously invalid, then add address as unseen entry point + if (cpu->instrCount > 0 && GetIndexOfAddr(cpu->pc, index) && (!analysis->HaveSeenIndex(index) || !analysis->IsIndexValid(index))) + { + // If at location that was previously seen and was invalid, then force reanalysis (ie because code may have been modified) + if (analysis->HaveSeenIndex(index) && !analysis->IsIndexValid(index)) + reanalyse = true; + + // Check that address not already included in previous entry points + bool unseen = true; + for (vector::iterator it = entryPoints.begin(); it != entryPoints.end(); it++) + { + if ((*it).addr == cpu->pc) + { + unseen = false; + break; + } + } + if (unseen && find(unseenEntryAddrs.begin(), unseenEntryAddrs.end(), cpu->pc) == unseenEntryAddrs.end()) + unseenEntryAddrs.push_back(cpu->pc); + } + + // Add unseen entry points + for (vector::iterator it = unseenEntryAddrs.begin(); it != unseenEntryAddrs.end(); it++) + AddEntryPoint(entryPoints, *it, LFUnseenCode, NULL); + } + + void CCodeAnalyser::AddEntryPoint(vector &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel) + { + CEntryPoint entryPoint(addr, autoFlag, autoLabel); + if (find(entryPoints.begin(), entryPoints.end(), entryPoint) == entryPoints.end()) + entryPoints.push_back(entryPoint); + } + + bool CCodeAnalyser::NeedsAnalysis() + { + vector entryPoints; + vector unseenEntryAddrs(analysis->m_unseenEntryAddrs); + bool needsAnalysis; + bool reanalyse; + CheckEntryPoints(entryPoints, unseenEntryAddrs, analysis->m_entryPoints, needsAnalysis, reanalyse); + return needsAnalysis; + } + + bool CCodeAnalyser::AnalyseCode() + { + m_abortAnalysis = false; + + CCodeAnalysis *oldAnalysis = analysis; + + vector entryPoints; + vector unseenEntryAddrs(oldAnalysis->m_unseenEntryAddrs); + bool needsAnalysis; + bool reanalyse; + CheckEntryPoints(entryPoints, unseenEntryAddrs, oldAnalysis->m_entryPoints, needsAnalysis, reanalyse); + if (!needsAnalysis) + return false; + + CCodeAnalysis *newAnalysis; + if (reanalyse || oldAnalysis == &emptyAnalysis) + newAnalysis = new CCodeAnalysis(this, totalIndices, entryPoints, unseenEntryAddrs); + else + newAnalysis = new CCodeAnalysis(oldAnalysis, entryPoints, unseenEntryAddrs); + newAnalysis->Acquire(); + + for (vector::iterator it = newAnalysis->m_entryPoints.begin(); it != newAnalysis->m_entryPoints.end(); it++) + { + AddFlagToAddr(newAnalysis->m_autoLabelsMap, it->addr, it->autoFlag, it->autoLabel); + AnalyseCode(newAnalysis->m_seenIndices, newAnalysis->m_validIndices, newAnalysis->validIndexSet, newAnalysis->m_autoLabelsMap, it->addr); + } + newAnalysis->FinishAnalysis(); + + if (m_abortAnalysis) + { + newAnalysis->Release(); + return false; + } + + analysis = newAnalysis; + if (oldAnalysis != &emptyAnalysis) + oldAnalysis->Release(); + + cpu->debugger->AnalysisUpdated(this); + return true; + } + + void CCodeAnalyser::AnalyseCode(vector &seenIndices, vector &validIndices, set &validIndexSet, + map &autoLabelsMap, UINT32 addr) + { + if (m_abortAnalysis) + return; + + unsigned index; + if (!GetIndexOfAddr(addr, index) || seenIndices[index]) + return; + + CRegion *region = cpu->GetRegion(addr); + if (region == NULL || !region->isCode) + return; + + set::iterator setIt = validIndexSet.end(); + + unsigned startIndex = index; + do + { + if (m_abortAnalysis) + return; + + // Flag that have seen this address index + seenIndices[index] = true; + + // If unit is not valid (ie doesn't disassemble) then code block must be invalid (TODO - invalidate whole code block?) + int codesLen = cpu->GetOpLength(addr); + if (codesLen <= 0) + return; + + validIndices[index] = true; + if (setIt != validIndexSet.end()) + setIt = validIndexSet.insert(setIt, index); + else + setIt = validIndexSet.insert(index).first; + + UINT32 opcode = cpu->GetOpcode(addr); + EOpFlags opFlags = cpu->GetOpFlags(addr, opcode); + + // See if instruction is jump + if (opFlags & (JumpSimple|JumpLoop|JumpSub)) + { + // If so, see if address is valid (ie known at disassemble time) + UINT32 jumpAddr; + if (cpu->GetJumpAddr(addr, opcode, jumpAddr)) + { + // If so, add flags to jump address and analyse destination code block too + if (opFlags & JumpSub) AddFlagToAddr(autoLabelsMap, jumpAddr, LFSubroutine, NULL); + else if (opFlags & JumpLoop) AddFlagToAddr(autoLabelsMap, jumpAddr, LFLoopPoint, NULL); + else AddFlagToAddr(autoLabelsMap, jumpAddr, LFJumpTarget, NULL); + AnalyseCode(seenIndices, validIndices, validIndexSet, autoLabelsMap, jumpAddr); + } + } + + // Finish if instruction terminates code block (ie is not conditional and is either a non-returning jump, a return or some sort + // of reset/halting instruction) + if (!(opFlags & Conditional) && (opFlags & (JumpSimple|JumpLoop|ReturnEx|ReturnSub|HaltExec))) + return; + + // Move to next index + index += (unsigned)codesLen / instrAlign; + + // If reach end of address indices, code block must be invalid (TODO - invalidate whole code block?) + if (index >= totalIndices) + return; + + // Move to next address + addr += (UINT32)codesLen; + + // If move between regions, check new region is valid + if (addr > region->addrEnd) + { + region = cpu->GetRegion(addr); + if (region == NULL || !region->isCode) // (TODO - invalidate whole code block?) + return; + } + } + while (!seenIndices[index]); + } + + void CCodeAnalyser::AddFlagToAddr(map &autoLabelsMap, UINT32 addr, ELabelFlags flag, const char *subLabel) + { + if (flag == LFNone) + return; + map::iterator it = autoLabelsMap.find(addr); + CAutoLabel *label; + if (it == autoLabelsMap.end()) + { + label = new CAutoLabel(cpu, addr); + label->Acquire(); + autoLabelsMap[addr] = label; + } + else + label = it->second; + label->AddFlag(flag, subLabel); + } + + void CCodeAnalyser::AbortAnalysis() + { + m_abortAnalysis = true; + } + + void CCodeAnalyser::ClearCustomEntryAddrs() + { + m_customEntryAddrs.clear(); + } + + void CCodeAnalyser::AddCustomEntryAddr(UINT32 entryAddr) + { + if (find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr) == m_customEntryAddrs.end()) + m_customEntryAddrs.push_back(entryAddr); + } + + bool CCodeAnalyser::RemoveCustomEntryAddr(UINT32 entryAddr) + { + vector::iterator it = find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr); + if (it == m_customEntryAddrs.end()) + return false; + m_customEntryAddrs.erase(it); + return true; + } + +#ifdef DEBUGGER_HASBLOCKFILE + bool CCodeAnalyser::LoadState(CBlockFile *state) + { + // Load custom entry addresses + char blockStr[255]; + sprintf(blockStr, "%s.entryaddrs", cpu->name); + if (state->FindBlock(blockStr) == OKAY) + { + m_customEntryAddrs.clear(); + UINT32 numAddrs; + state->Read(&numAddrs, sizeof(numAddrs)); + for (UINT32 i = 0; i < numAddrs; i++) + { + UINT32 addr; + state->Read(&addr, sizeof(addr)); + m_customEntryAddrs.push_back(addr); + } + } + return true; + } + + bool CCodeAnalyser::SaveState(CBlockFile *state) + { + // Save custom entry addresses + char blockStr[255]; + sprintf(blockStr, "%s.entryaddrs", cpu->name); + state->NewBlock(blockStr, __FILE__); + UINT32 numAddrs = m_customEntryAddrs.size(); + state->Write(&numAddrs, sizeof(numAddrs)); + for (UINT32 i = 0; i < numAddrs; i++) + { + UINT32 addr = m_customEntryAddrs[i]; + state->Write(&addr, sizeof(addr)); + } + return true; + } +#endif // DEBUGGER_HASBLOCKFILE +} +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/CodeAnalyser.h b/Src/Debugger/CodeAnalyser.h new file mode 100644 index 0000000..23479c3 --- /dev/null +++ b/Src/Debugger/CodeAnalyser.h @@ -0,0 +1,262 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * CodeAnalyser.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_CODEANALYSER_H +#define INCLUDED_CODEANALYSER_H + +#include +#include +#include +#include +#include + +#include "Types.h" +#include "Debugger.h" +#include "AddressTable.h" + +#ifdef DEBUGGER_HASBLOCKFILE +#include "BlockFile.h" +#endif // DEBUGGER_HASBLOCKFILE + +namespace Debugger +{ + class CCPUDebug; + class CRegion; + class CLabel; + + enum ELabelFlags + { + LFNone = 0, + LFEntryPoint = 1, + LFExcepHandler = 2, + LFInterHandler = 4, + LFJumpTarget = 8, + LFLoopPoint = 16, + LFSubroutine = 32, + LFUnseenCode = 64, + LFAll = (LFEntryPoint | LFExcepHandler | LFInterHandler | LFJumpTarget | LFSubroutine | LFLoopPoint | LFUnseenCode) + }; + + /* + * Class that represents an entry point into a program's code in memory, eg a CPU's reset address or exception handler's address etc. + */ + class CEntryPoint + { + private: + char autoLabelStr[255]; + + public: + UINT32 addr; + ELabelFlags autoFlag; + const char *autoLabel; + + CEntryPoint(const CEntryPoint &other); + + CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel); + + CEntryPoint &operator=(const CEntryPoint &other); + + bool operator==(const CEntryPoint &other); + + bool operator!=(const CEntryPoint &other); + }; + + /* + * Class that represents an auto-generated label that resulted from code analysis, eg a known entry point or the destination of a subroutine call. + */ + class CAutoLabel : public CAddressRef + { + private: + static const char *s_defaultLabelFmts[]; + + const char *m_subLabels[7]; + + unsigned m_acquired; + + const char *CreateDefaultSubLabel(ELabelFlags flag); + + public: + static const unsigned numLabelFlags; + + static ELabelFlags GetLabelFlag(int index); + + static int GetFlagIndex(ELabelFlags flag); + + static const char *GetFlagString(ELabelFlags flag); + + ELabelFlags flags; + + CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr); + + ~CAutoLabel(); + + void Acquire(); + + void Release(); + + void AddFlag(ELabelFlags flag, const char *subLabel); + + bool GetLabel(char *label, ELabelFlags flag = LFAll); + + bool ContainsSubLabel(const char *subLabel); + }; + + class CCodeAnalyser; + + /* + * Class that holds the results of having analysed the program code. + */ + class CCodeAnalysis + { + friend class CCodeAnalyser; + + private: + std::vector m_entryPoints; + std::vector m_unseenEntryAddrs; + std::vector m_seenIndices; + std::vector m_validIndices; + std::map m_autoLabelsMap; + + unsigned m_acquired; + + CCodeAnalysis(CCodeAnalyser *aAnalyser); + + CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, std::vector &entryPoints, std::vector &m_unseenEntryAddrs); + + CCodeAnalysis(CCodeAnalysis *oldAnalysis, std::vector &entryPoints, std::vector &m_unseenEntryAddrs); + + void FinishAnalysis(); + + public: + CCodeAnalyser *analyser; + std::set validIndexSet; + std::vector autoLabels; + + ~CCodeAnalysis(); + + void Acquire(); + + void Release(); + + bool IsAddrValid(UINT32 addr); + + bool GetNextValidAddr(UINT32 &addr); + + bool IsIndexValid(unsigned index); + + bool GetNextValidIndex(unsigned &index); + + bool HasSeenAddr(UINT32 addr); + + bool HaveSeenIndex(unsigned index); + + CAutoLabel *GetAutoLabel(UINT32 addr); + + CAutoLabel *GetAutoLabel(const char *subLabel); + + std::vector GetAutoLabels(ELabelFlags flag); + }; + + /* + * Class that analyses a program's code to work out all the reachable program locations from a given set of entry points into the code. + * During the analysis, it keeps track of valid memory locations (to help with the disassembly of code for CPUs with variable-length + * instruction sets) and generates a set of auto-labels that identify places of interest such as subroutines, jump destinations and + * exception handlers etc. + * This sort of analysis works well for static addressing modes but not so well for dynamic address referencing or self-modifying code. + * To allow for the latter cases the analyser updates its analysis whenever it encounters an unseen memory location or it sees + * that code has changed from a previous inspection. + */ + class CCodeAnalyser + { + private: + std::vector m_codeRegions; + std::vector m_indexBounds; + + std::vector m_customEntryAddrs; + + bool m_abortAnalysis; + + void CheckEntryPoints(std::vector &entryPoints, std::vector &unseenEntryAddrs, std::vector &prevPoints, + bool &needsAnalysis, bool &reanalyse); + + void GatherEntryPoints(std::vector &entryPoints, std::vector &unseenEntryAddrs, bool &reanalyse); + + void AddEntryPoint(std::vector &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel); + + void AnalyseCode(std::vector &seenIndices, std::vector &validIndices, std::set &validIndexSet, std::map &autoLabelsMap, UINT32 addr); + + void AddFlagToAddr(std::map &autoLabelsMap, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel); + + public: + CCPUDebug *cpu; + + CCodeAnalysis emptyAnalysis; + + CCodeAnalysis *analysis; + + unsigned instrAlign; + unsigned totalIndices; + + CCodeAnalyser(CCPUDebug *aCPU); + + ~CCodeAnalyser(); + + void Reset(); + + bool GetAddrOfIndex(unsigned index, UINT32 &addr); + + bool GetIndexOfAddr(UINT32 addr, unsigned &index); + + // + // Methods to check, run or abort analysis + // + + bool NeedsAnalysis(); + + bool AnalyseCode(); + + void AbortAnalysis(); + + // + // Methods to manipulate custom entry addresses + // + + void ClearCustomEntryAddrs(); + + void AddCustomEntryAddr(UINT32 entryAddr); + + bool RemoveCustomEntryAddr(UINT32 entryAddr); + +#ifdef DEBUGGER_HASBLOCKFILE + bool LoadState(CBlockFile *state); + + bool SaveState(CBlockFile *state); +#endif // DEBUGGER_HASBLOCKFILE + }; +} + +#endif // INCLUDED_CODEANALYSER_H +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/ConsoleDebugger.cpp b/Src/Debugger/ConsoleDebugger.cpp new file mode 100644 index 0000000..3e4a892 --- /dev/null +++ b/Src/Debugger/ConsoleDebugger.cpp @@ -0,0 +1,2760 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ConsoleDebugger.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "ConsoleDebugger.h" +#include "CPUDebug.h" +#include "CodeAnalyser.h" +#include "Label.h" + +#include +#include + +using namespace std; + +namespace Debugger +{ + CConsoleDebugger::CConsoleDebugger() : CDebugger(), + m_nextFrame(false), m_listDism(0), m_listMem(0), m_analyseCode(true), + m_addrFmt(HexDollar), m_portFmt(Decimal), m_dataFmt(HexDollar), + m_showLabels(true), m_labelsOverAddr(true), m_showOpCodes(false), m_memBytesPerRow(12), m_file(NULL) + { + // + } + + // TODO - tidy up this function, ie do some proper parsing of commands - it is a mess! + void CConsoleDebugger::WaitCommand(CCPUDebug *cpu) + { + m_cpu = cpu; + + UINT32 pc = m_cpu->pc; + m_listDism = (m_cpu->instrCount > 0 && pc > 10 * m_cpu->minInstrLen ? pc - 10 * m_cpu->minInstrLen : 0); + + char bpChr; + char addrStr[20]; + char labelStr[13]; + char opCodes[50]; + int codesLen; + char mnemonic[255]; + char operands[255]; + char cmd[255]; + + for (;;) + { + // Get code analyser and if available analyse code now if required + if (m_analyseCode) + { + CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); + if (analyser->NeedsAnalysis()) + { + Print("Analysing %s...\n", m_cpu->name); + analyser->AnalyseCode(); + } + } + + // Close redirected output file, if exists + if (m_file != NULL) + { + fclose(m_file); + m_file = NULL; + } + + // Get details for current PC address + bool hasLabel; + if (m_cpu->instrCount > 0) + { + m_cpu->FormatAddress(addrStr, pc); + hasLabel = GetLabelText(labelStr, 12, pc); + codesLen = m_cpu->Disassemble(pc, mnemonic, operands); + FormatOpCodes(opCodes, pc, abs(codesLen)); + } + else + { + addrStr[0] = '-'; + addrStr[1] = '\0'; + hasLabel = false; + labelStr[0] = '\0'; + opCodes[0] = '-'; + opCodes[1] = '\0'; + codesLen = 0; + } + CBreakpoint *bp = m_cpu->GetBreakpoint(pc); + bpChr = (bp != NULL ? bp->symbol : ' '); + + // Output command prompt + Print("%s%c", m_cpu->name, bpChr); + if (m_showLabels) + { + if (m_labelsOverAddr) + Print("%-12s ", (hasLabel ? labelStr : addrStr)); + else + Print("%s %-12s ", addrStr, labelStr); + } + else + Print("%s ", addrStr); + if (m_showOpCodes) + Print("[%s] ", opCodes); + if (codesLen > 0) + Print("%-*s %s > ", (int)m_cpu->maxMnemLen, mnemonic, operands); + else + Print("??? > "); + Flush(); + + // Wait for command + Read(cmd, 255); + + if (cmd[0] == '\0') + { + m_cpu->SetStepMode(StepInto); + break; + } + + // Check for redirection + char *pos = strchr(cmd, '>'); + if (pos != NULL) + { + *pos = '\0'; + pos++; + const char *mode; + if (*pos == '>') + { + mode = "ab"; + pos++; + } + else + mode = "w"; + while (*pos == ' ') + pos++; + if (*pos != '\0') + { + m_file = fopen(pos, mode); + if (m_file == NULL) + { + Error("Unable to direct output to file %s\n", pos); + continue; + } + } + else + { + Error("Missing file to direct output to\n"); + continue; + } + } + + char *token = strtok(cmd, " "); + if (ProcessToken(token, cmd)) + break; + + pc = m_cpu->pc; + } + } + + bool CConsoleDebugger::ProcessToken(const char *token, const char *cmd) + { + UINT32 pc = m_cpu->pc; + + int number; + unsigned size; + const char *sizeStr; + UINT32 addr; + UINT16 portNum; + UINT64 data; + char addrStr[50]; + char portNumStr[50]; + char dataStr[50]; + char mod[10]; + EFormat fmt; + + // + // Execution + // + if (CheckToken(token, "n", "next")) // next [=1] + { + // Parse arguments + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseInt(token, &number) || number <= 0) + { + Error("Enter a valid instruction count.\n"); + return false; + } + + if (number > 1) + Print("Running %d instructions.\n", number); + m_cpu->SetCount(number); + } + else + m_cpu->SetStepMode(StepInto); + return true; + } + else if (CheckToken(token, "nf", "nextframe")) // nextframe [=1] + { + // Parse arguments + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseInt(token, &number) || number <= 0) + { + Error("Enter a valid frame count.\n"); + return false; + } + + if (number > 1) + Print("Running %d frames.\n", number); + m_nextFrameCount = number; + } + else + m_nextFrameCount = 1; + m_nextFrame = true; + return true; + } + else if (CheckToken(token, "s", "stepover")) // stepover + { + m_cpu->SetStepMode(StepOver); + return true; + } + else if (CheckToken(token, "si", "stepinto")) // stepinto + { + m_cpu->SetStepMode(StepInto); + return true; + } + else if (CheckToken(token, "so", "stepout")) // stepout + { + m_cpu->SetStepMode(StepOut); + return true; + } + else if (CheckToken(token, "c", "continue")) // continue [] + { + // Parse arguments + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, addr); + Print("Continuing until %s.\n", addrStr); + m_cpu->SetUntil(addr); + } + else + m_cpu->SetContinue(); + return true; + } + else if (CheckToken(token, "spc", "setpc")) // setpc + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + if (!m_cpu->SetPC(addr)) + { + Error("Unable to set PC.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, addr); + Print("PC set to %s.\n", addrStr); + return true; + } + // + // CPUs + // + else if (CheckToken(token, "lc", "listcpus")) // listcpus + { + ListCPUs(); + } + else if (CheckToken(token, "sc", "switchcpu")) // switchcpu (|) + { + // Parse arguments + token = strtok(NULL, " "); + CCPUDebug *cpu; + if (!ParseCPU(token, cpu)) + return false; + + if (!cpu->IsEnabled()) + { + Error("CPU %s is currently disabled for debugging.\n", cpu->name); + return false; + } + + m_cpu = cpu; + pc = cpu->pc; + m_listDism = (cpu->instrCount > 0 && pc > 10 * cpu->minInstrLen ? pc - 10 * cpu->minInstrLen : 0); + return false; + } + else if (CheckToken(token, "dc", "disablecpu")) // disablecpu (|) + { + // Parse arguments + token = strtok(NULL, " "); + CCPUDebug *cpu; + if (!ParseCPU(token, cpu)) + return false; + + if (cpu == m_cpu) + { + Error("Cannot enable/disable debugging on current CPU.\n"); + return false; + } + cpu->SetEnabled(false); + Print("Disabled debugging on CPU %s.\n", cpu->name); + } + else if (CheckToken(token, "ec", "enablecpu")) // enablecpu (|) + { + // Parse arguments + token = strtok(NULL, " "); + CCPUDebug *cpu; + if (!ParseCPU(token, cpu)) + return false; + + if (cpu == m_cpu) + { + Error("Cannot enable/disable debugging on current CPU.\n"); + return false; + } + cpu->SetEnabled(true); + Print("Enabled debugging on CPU %s.\n", cpu->name); + } + // + // Registers + // + else if (CheckToken(token, "lr", "listregisters")) // listregisters + { + ListRegisters(); + } + else if (CheckToken(token, "pr", "printregister")) // printregister + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + reg->GetValue(dataStr); + Print("Register %s = %s\n", reg->name, dataStr); + } + else if (CheckToken(token, "sr", "setregister")) // setregister + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing value to set.\n"); + return false; + } + + if (!reg->SetValue(token)) + { + Error("Unable to set value of register %s.\n", reg->name); + return false; + } + reg->GetValue(dataStr); + Print("Set register %s to %s.\n", reg->name, dataStr); + } + else if (CheckToken(token, "lm", "listmonitors")) // listmonitors + { + ListMonitors(); + } + else if (CheckToken(token, "m", "monitor") || // addmonitor + CheckToken(token, "am", "addmonitor")) + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + m_cpu->AddRegMonitor(reg->name); + Print("Monitor added to register %s.\n", reg->name); + } + else if (CheckToken(token, "rm", "removemonitor")) // removemonitor + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + m_cpu->RemoveRegMonitor(reg->name); + Print("Monitor for register %s removed.\n", reg->name); + } + else if (CheckToken(token, "ram", "removeallmonitors")) // removeallmonitors + { + m_cpu->RemoveAllRegMonitors(); + Print("All register monitors removed.\n"); + } + // + // Exceptions & interrupts + // + else if (CheckToken(token, "le", "listexceptions")) // listexceptions + { + ListExceptions(); + } + else if (CheckToken(token, "li", "listinterrupts")) // listinterrupts + { + ListInterrupts(); + } + else if (CheckToken(token, "t", "trap") || // addtrap ((e)xception|(i)nterrupt) + CheckToken(token, "at", "addtrap")) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing type (e)xception or (i)interrupt\n"); + return false; + } + + if (CheckToken(token, "e", "exception")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing exception id.\n"); + return false; + } + CException *ex = m_cpu->GetException(token); + if (ex == NULL) + { + Error("Enter a valid exception id.\n"); + return false; + } + + ex->trap = true; + Print("Trap added for exceptions of type %s.\n", ex->id); + } + else if (CheckToken(token, "i", "interrupt")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing interrupt id.\n"); + return false; + } + CInterrupt *in = m_cpu->GetInterrupt(token); + if (in == NULL) + { + Error("Enter a valid interrupt id.\n"); + return false; + } + + in->trap = true; + Print("Trap added for interrupts of type %s.\n", in->id); + } + else + { + Error("Enter valid type (e)xception or (i)interrupt.\n"); + return false; + } + } + else if (CheckToken(token, "rt", "removetrap")) // removetrap ((e)xception|(i)nterrupt) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing type (e)xception or (i)interrupt.\n"); + return false; + } + + if (CheckToken(token, "e", "exception")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing exception id.\n"); + return false; + } + CException *ex = m_cpu->GetException(token); + if (ex == NULL) + { + Error("Enter a valid exception id.\n"); + return false; + } + + ex->trap = false; + Print("Trap for exceptions of type %s removed.\n", ex->id); + } + else if (CheckToken(token, "i", "interrupt")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing interrupt id.\n"); + return false; + } + CInterrupt *in = m_cpu->GetInterrupt(token); + if (in == NULL) + { + Error("Enter a valid interrupt id.\n"); + return false; + } + + in->trap = false; + Print("Trap for interrupts ot type %s removed.\n", in->id); + } + else + { + Error("Enter a valid type (e)xception or (i)interrupt.\n"); + return false; + } + } + else if (CheckToken(token, "rat", "removealltraps")) // removealltraps [(a)ll|(e)xceptions|(i)nterrupts] + { + bool removeExs; + bool removeInts; + const char *coverage; + if (token == NULL || CheckToken(token, "a", "all")) + { + removeExs = true; + removeInts = true; + coverage = "exceptions and interrupts"; + } + else if (CheckToken(token, "e", "exceptions")) + { + removeExs = true; + removeInts = false; + coverage = "exceptions"; + } + else if (CheckToken(token, "i", "interrupts")) + { + removeExs = false; + removeInts = true; + coverage = "interrupts"; + } + else + { + Error("Enter a valid mode (a)ll, (e)xceptions or (i)nterrupts\n"); + return false; + } + + if (removeExs) + { + for (vector::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++) + (*it)->trap = false; + } + if (removeInts) + { + for (vector::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++) + (*it)->trap = false; + } + Print("All traps for %s removed.\n", coverage); + } + // + // Disassembly, labels & comments + // + else if (CheckToken(token, "l", "list") || // listdisassembly [=last [#=20|]] + CheckToken(token, "ld", "listdisassembly")) + { + // Get start address + UINT32 start; + token = strtok(NULL, " "); + if (token == NULL) + token = "last"; + if (stricmp(token, "last") == 0) + { + // Use end of last listing + start = m_listDism; + } + else if (!ParseAddress(token, &start)) + { + Error("Enter a valid start address.\n"); + return false; + } + + // Get end address + UINT32 end; + unsigned numInstrs; + token = strtok(NULL, " "); + if (token != NULL) + { + if (token[0] == '#') + { + if (!ParseInt(token + 1, &number) || number <= 0) + { + Error("Enter a valid number of instructions.\n"); + return false; + } + numInstrs = (unsigned)number; + end = 0xFFFFFFFF; + } + else + { + if (!ParseAddress(token, &end)) + { + Error("Enter a valid end address.\n"); + return false; + } + numInstrs = 0xFFFFFFFF; + } + } + else + { + // Default is 20 instructions after start + end = 0xFFFFFFFF; + numInstrs = 20; + } + + // List the disassembled code + m_listDism = ListDisassembly(start, end, numInstrs); + } + else if (CheckToken(token, "ll", "listlabels")) // listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints] + { + // Parse arguments + token = strtok(NULL, " "); + bool customLabels; + ELabelFlags labelFlags; + if (token == NULL || CheckToken(token, "d", "default")) + { + customLabels = true; + labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine); + } + else if (CheckToken(token, "c", "custom")) + { + customLabels = true; + labelFlags = LFNone; + } + else if (CheckToken(token, "a", "autos")) + { + customLabels = true; + labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine | LFJumpTarget | LFLoopPoint); + } + else if (CheckToken(token, "e", "entrypoints")) + { + customLabels = false; + labelFlags = LFEntryPoint; + } + else if (CheckToken(token, "x", "excephandlers")) + { + customLabels = false; + labelFlags = LFExcepHandler; + } + else if (CheckToken(token, "i", "interhandlers")) + { + customLabels = false; + labelFlags = LFInterHandler; + } + else if (CheckToken(token, "s", "subroutines")) + { + customLabels = false; + labelFlags = LFSubroutine; + } + else if (CheckToken(token, "j", "jumptargets")) + { + customLabels = false; + labelFlags = LFJumpTarget; + } + else if (CheckToken(token, "l", "looppoints")) + { + customLabels = false; + labelFlags = LFLoopPoint; + } + else + { + Error("Enter a valid filter (a)ll, (c)ustom, (e)ntrypoints, e(x)cephandlers, (i)interhandlers, (j)umptargets or (l)ooppoints.\n"); + return false; + } + + ListLabels(customLabels, labelFlags); + } + else if (CheckToken(token, "al", "addlabel")) // addlabel + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing label address.\n"); + return false; + } + else if (!ParseAddress(token, &addr)) + { + Error("Enter a valid label address.\n"); + return false; + } + + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing label name.\n"); + return false; + } + const char *name = token; + + // Add label + CLabel *label = m_cpu->AddLabel(addr, name); + m_cpu->FormatAddress(addrStr, label->addr); + Print("Label %s added at %s.\n", label->name, addrStr); + } + else if (CheckToken(token, "rl", "removelabel")) // removelabel [|] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid label name or address.\n"); + return false; + } + + CLabel *label = m_cpu->GetLabel(addr); + if (label == NULL) + { + m_cpu->FormatAddress(addrStr, addr); + Error("No label at %s.\n", addrStr); + return false; + } + + const char *name = label->name; + m_cpu->FormatAddress(addrStr, label->addr); + Print("Custom label '%s' removed at address %s.\n", name, addrStr); + } + else if (CheckToken(token, "ral", "removealllabels")) // removealllabels + { + m_cpu->RemoveAllLabels(); + Print("All custom labels removed.\n"); + } + else if (CheckToken(token, "ac", "addcomment")) // addcomment + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing comment address.\n"); + return false; + } + else if (!ParseAddress(token, &addr)) + { + Error("Enter a valid comment address.\n"); + return false; + } + + char text[255]; + text[0] = '\0'; + token = strtok(NULL, " "); + while (token != NULL) + { + size_t len = strlen(text); + if (len + strlen(token) > 253) + break; + if (len > 0) + strcat(text, " "); + strcat(text, token); + token = strtok(NULL, " "); + } + if (text[0] == '\0') + { + Error("Missing comment text.\n"); + return false; + } + + // Add comment + CComment *comment = m_cpu->AddComment(addr, text); + m_cpu->FormatAddress(addrStr, comment->addr); + Print("Comment added at %s.\n", addrStr); + } + else if (CheckToken(token, "rc", "removecomment")) // removecomment [] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid comment address.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, addr); + if (m_cpu->RemoveComment(addr)) + Print("Comment at address %s removed.\n", addrStr); + else + Error("No comment at address %s.\n", addrStr); + } + else if (CheckToken(token, "rac", "removeallcomments")) // removeallcomments + { + m_cpu->RemoveAllComments(); + Print("All comments removed.\n"); + } + // + // Breakpoints + // + else if (CheckToken(token, "lb", "listbreakpoints")) // listbreakpoints + { + ListBreakpoints(); + } + else if (CheckToken(token, "b", "breakpoint") || // addbreakpoint [ [[s)imple|(c)ount )]] + CheckToken(token, "ab", "addbreakpoint")) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + CBreakpoint *bp; + if (token == NULL || CheckToken(token, "s", "simple")) + bp = m_cpu->AddSimpleBreakpoint(addr); + else if (CheckToken(token, "c", "count")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing count.\n"); + return false; + } + int count; + if (!ParseInt(token, &count) || count <= 0) + { + Error("Enter a valid count.\n"); + return false; + } + + bp = m_cpu->AddCountBreakpoint(addr, count); + } + else if (CheckToken(token, "p", "print")) + bp = m_cpu->AddPrintBreakpoint(addr); + else + { + Error("Enter a valid breakpoint type (s)imple or (c)ount.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, bp->addr); + Print("Breakpoint #%d added at address %s.\n", bp->num, addrStr); + } + else if (CheckToken(token, "rb", "removebreakpoint")) // removebreakpoint [#|] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (token[0] == '#') + { + // Remove breakpoint by number + if (!ParseInt(token + 1, &number) || number < 0 || number >= m_cpu->bps.size()) + { + Error("Enter a valid breakpoint number.\n"); + return false; + } + + // Remove breakpoint + m_cpu->RemoveBreakpoint(m_cpu->bps[number]); + Print("Breakpoint #%d removed.\n", number); + } + else + { + // Remove breakpoint by address + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + // Remove breakpoint + m_cpu->FormatAddress(addrStr, addr); + if (m_cpu->RemoveBreakpoint(addr)) + Print("Breakpoint at address %s removed.\n", addrStr); + else + Error("No breakpoint at address %s.\n", addrStr); + } + } + else if (CheckToken(token, "rab", "removeallbreakpoints")) // removeallbreakpoints + { + m_cpu->RemoveAllBreakpoints(); + Print("All breakpoints removed.\n"); + } + // + // Memory, I/O & watches + // + else if (CheckToken(token, "ln", "listregions")) // listregions + { + ListRegions(); + } + else if (CheckToken(token, "ly", "listmemory")) // listmemory [=last [#=8|]] + { + // Get start address + UINT32 start; + token = strtok(NULL, " "); + if (token == NULL) + token = "last"; + if (stricmp(token, "last") == 0) + { + // Use end of last listing + start = m_listMem; + } + else if (!ParseAddress(token, &start)) + { + Error("Enter a valid start address.\n"); + return false; + } + + // Get end address + UINT32 end; + token = strtok(NULL, " "); + if (token != NULL) + { + if (token[0] == '#') + { + if (!ParseInt(token + 1, &number) || number <= 0) + { + Error("Enter a valid number of rows.\n"); + return false; + } + end = start + number * m_memBytesPerRow; + } + else + { + if (!ParseAddress(token, &end)) + { + Error("Enter a valid end address.\n"); + return false; + } + } + } + else + // Default is 8 rows after start + end = start + 8 * m_memBytesPerRow; + + // List the memory + m_listMem = ListMemory(start, end, m_memBytesPerRow); + } + else if (CheckToken(token, "py", "printmemory", mod, 9, "b")) // printmemory[.=b] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + { + // Parse arguments + if (!ParseDataSize(mod, size)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseFormat(token, fmt)) + return false; + } + else + fmt = m_dataFmt; + + // Read and print memory + char uSizeStr[12]; + sizeStr = GetDataSizeStr(size, false); + UpperFirst(uSizeStr, sizeStr); + data = m_cpu->ReadMem(addr, size); + FormatData(dataStr, fmt, size, data); + m_cpu->FormatAddress(addrStr, addr); + Print("%s data at %s = %s.\n", uSizeStr, addrStr, dataStr); + } + else if (CheckToken(token, "sy", "setmemory", mod, 9, "b")) // setmemory[.=b] + { + // Parse arguments + if (!ParseDataSize(mod, size)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing value to set.\n"); + return false; + } + sizeStr = GetDataSizeStr(size, false); + if (!m_cpu->ParseData(token, size, &data)) + { + Error("Enter a valid %s value.\n", sizeStr); + return false; + } + + // Set memory + char uSizeStr[12]; + UpperFirst(uSizeStr, sizeStr); + m_cpu->WriteMem(addr, size, data); + m_cpu->FormatData(dataStr, size, data); + m_cpu->FormatAddress(addrStr, addr); + Print("Set %s data at %s to %s.\n", uSizeStr, addrStr, dataStr); + } + else if (CheckToken(token, "lo", "listios")) // listios + { + ListIOs(); + } + else if (CheckToken(token, "lw", "listmemwatches")) // listmemwatches + { + ListMemWatches(); + } + else if (CheckToken(token, "w", "memwatch", mod, 9, "b") || // addmemwatch[.=b] [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] + CheckToken(token, "aw", "addmemwatch", mod, 9, "b")) + { + // Parse arguments + if (!ParseDataSize(mod, size)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + bool read; + bool write; + if (token == NULL || CheckToken(token, "n", "none")) + { + read = false; + write = false; + } + else if (CheckToken(token, "r", "read")) + { + read = true; + write = false; + } + else if (CheckToken(token, "w", "write")) + { + read = false; + write = true; + } + else if (CheckToken(token, "rw", "read/write") || CheckToken(token, "wr", "write/read")) + { + read = true; + write = true; + } + else + { + Error("Enter valid read/write flags (n)one, (r)ead, (w)rite or (rw)ead/write.\n"); + return false; + } + + // Add mem watch + CWatch *watch; + token = strtok(NULL, " "); + if (token == NULL || CheckToken(token, "s", "simple")) + watch = m_cpu->AddSimpleMemWatch(addr, size, read, write); + else if (CheckToken(token, "c", "count")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing count.\n"); + return false; + } + int count; + if (!ParseInt(token, &count) || count <= 0) + { + Error("Enter a valid count.\n"); + return false; + } + watch = m_cpu->AddCountMemWatch(addr, size, read, write, count); + } + else if (CheckToken(token, "m", "match")) + { + vector dataSeq; + while ((token = strtok(NULL, " ")) != NULL) + { + if (m_cpu->ParseData(token, size, &data)) + dataSeq.push_back(data); + } + if (dataSeq.size() == 0) + { + sizeStr = GetDataSizeStr(size, false); + Error("Enter a sequence of %s data to match.", sizeStr); + return false; + } + watch = m_cpu->AddMatchMemWatch(addr, size, read, write, dataSeq); + } + else if (CheckToken(token, "r", "capture")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing maximum capture length.\n"); + return false; + } + int maxLen; + if (!ParseInt(token, &maxLen) || maxLen <= 0) + { + Error("Enter a valid maximum capture length.\n"); + return false; + } + watch = m_cpu->AddCaptureMemWatch(addr, size, read, write, maxLen); + } + else if (CheckToken(token, "p", "print")) + watch = m_cpu->AddPrintMemWatch(addr, size, read, write); + else + { + Error("Enter a valid watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, watch->addr); + number = GetIndexOfMemWatch(watch); + Print("Memory watch #%d added at address %s.\n", number, addrStr); + } + else if (CheckToken(token, "rw", "removememwatch")) // removememwatch (#|) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing watch number or address.\n"); + return false; + } + if (token[0] == '#') + { + // Remove watch by number + vector watches; + GetAllMemWatches(watches); + if (!ParseInt(token + 1, &number) || number < 0 || number >= watches.size()) + { + Error("Enter a valid watch number.\n"); + return false; + } + + // Remove watch + m_cpu->RemoveWatch(watches[number]); + Print("Memory watch #%d removed.\n", number); + } + else + { + // Remove watch by address + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + // Remove watch + m_cpu->FormatAddress(addrStr, addr); + if (m_cpu->RemoveMemWatch(addr, 1)) + Print("Memory watch at address %s removed.\n", addrStr); + else + Error("No memory watch at address %s.\n", addrStr); + } + } + else if (CheckToken(token, "raw", "removeallmemwatches")) // removeallmemwatches + { + // Remove all memory watches + vector watches; + GetAllMemWatches(watches); + for (vector::iterator it = watches.begin(); it != watches.end(); it++) + m_cpu->RemoveWatch(*it); + Print("All memory watches removed.\n"); + } + else if (CheckToken(token, "lpw", "listportwatches")) // listportwatches + { + ListPortWatches(); + } + else if (CheckToken(token, "pw", "portwatch") || // addportwatch [((n)one|(i)nput|(o)|(io)nputoutput) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] + CheckToken(token, "apw", "addportwatch")) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing port number.\n"); + return false; + } + if (!m_cpu->ParsePortNum(token, &portNum)) + { + Error("Enter a valid port number.\n"); + return false; + } + token = strtok(NULL, " "); + bool input; + bool output; + if (token == NULL || CheckToken(token, "n", "none")) + { + input = false; + output = false; + } + else if (CheckToken(token, "i", "input")) + { + input = true; + output = false; + } + else if (CheckToken(token, "o", "output")) + { + input = false; + output = true; + } + else if (CheckToken(token, "io", "input/output") || CheckToken(token, "oi", "output/input")) + { + input = true; + output = true; + } + else + { + Error("Enter valid input/output flags (n)one, (i)nput, (o)utput or (io)nput/output.\n"); + return false; + } + + // Add watch + CPortIO *port = m_cpu->GetPortIO(portNum); + CWatch *watch; + token = strtok(NULL, " "); + if (token == NULL || CheckToken(token, "s", "simple")) + watch = port->AddSimpleWatch(input, output); + else if (CheckToken(token, "c", "count")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing count.\n"); + return false; + } + int count; + if (!ParseInt(token, &count) || count <= 0) + { + Error("Enter a valid count.\n"); + return false; + } + watch = port->AddCountWatch(input, output, count); + } + else if (CheckToken(token, "m", "match")) + { + vector dataSeq; + while ((token = strtok(NULL, " ")) != NULL) + { + if (m_cpu->ParseData(token, port->dataSize, &data)) + dataSeq.push_back(data); + } + if (dataSeq.size() == 0) + { + Error("Enter a sequence of %s to match.", GetDataSizeStr(port->dataSize, false)); + return false; + } + watch = port->AddMatchWatch(input, output, dataSeq); + } + else if (CheckToken(token, "r", "capture")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing maximum capture length.\n"); + return false; + } + int maxLen; + if (!ParseInt(token, &maxLen) || maxLen <= 0) + { + Error("Enter a valid maximum capture length.\n"); + return false; + } + watch = port->AddCaptureWatch(input, output, maxLen); + } + else if (CheckToken(token, "p", "print")) + watch = port->AddPrintWatch(input, output); + else + { + Error("Enter a valid watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint.\n"); + return false; + } + + m_cpu->FormatPortNum(portNumStr, portNum); + number = GetIndexOfPortWatch(watch); + Print("Port watch %d added for port %u.\n", number, portNumStr); + } + else if (CheckToken(token, "rpw", "removeportwatch")) // removeportwatch (a|n|p) [NUM|PORT] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing watch number or port number.\n"); + return false; + } + if (token[0] == '#') + { + // Remove watch by number + vector watches; + GetAllPortWatches(watches); + if (!ParseInt(token + 1, &number) || number < 0 || number >= watches.size()) + { + Error("Enter a valid watch number.\n"); + return false; + } + + // Remove watch + m_cpu->RemoveWatch(watches[number]); + Print("Port watch #%d removed.\n", number); + } + else + { + // Remove watch by port number + if (!m_cpu->ParsePortNum(token, &portNum)) + { + Error("Enter a valid port number.\n"); + return false; + } + + // Remove watch + CPortIO *port = m_cpu->GetPortIO(portNum); + m_cpu->FormatPortNum(portNumStr, portNum); + if (port->RemoveWatch()) + Print("Port watch for port %s removed.\n", portNumStr); + else + Error("No port watch for port %s.\n", portNumStr); + } + } + else if (CheckToken(token, "rapw", "removeallportwatches")) // removeallportwatches + { + // Remove all port watches + vector watches; + GetAllPortWatches(watches); + for (vector::iterator it = watches.begin(); it != watches.end(); it++) + m_cpu->RemoveWatch(*it); + Print("All port watches removed.\n"); + } + // + // General + // + else if (CheckToken(token, "p", "print", mod, 9, "")) // print[.=v] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + { + // Parse arguments + bool useDefSize; + if (mod[0] == '\0') + useDefSize = true; + else if (ParseDataSize(mod, size)) + useDefSize = false; + else + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing expression.\n"); + return false; + } + const char *expr = token; + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseFormat(token, fmt)) + return false; + } + else + fmt = m_dataFmt; + + // Check labels and registers + CLabel *label = m_cpu->GetLabel(expr); + if (label != NULL) + { + data = label->addr; + if (useDefSize) + size = m_cpu->memBusWidth / 8; + } + else + { + CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); + CAutoLabel *autoLabel = analyser->analysis->GetAutoLabel(expr); + if (autoLabel != NULL) + { + data = autoLabel->addr; + if (useDefSize) + size = m_cpu->memBusWidth / 8; + } + else + { + CRegister *reg = m_cpu->GetRegister(expr); + if (reg != NULL) + { + data = reg->GetValueAsInt(); + if (useDefSize) + size = reg->dataWidth / 8; + } + else + { + if (!ParseData(expr, m_dataFmt, 8, &data)) + { + Print("Unable to parse expression %s\n", expr); + return false; + } + if (useDefSize) + size = 8; + } + } + } + + char result[255]; + FormatData(result, fmt, size, data); + if (useDefSize) + Print("%s = %s\n", expr, result); + else + { + sizeStr = GetDataSizeStr(size, true); + Print("%s = %s.%s\n", expr, result, sizeStr); + } + } + else if (CheckToken(token, "cfg", "configure")) // configure + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + // If no arguments, then print out current configuration + Print("Configuration:\n"); + Print(" %-20s %-12s %s\n", "Code Analysis", (m_analyseCode ? "On" : "Off"), "(a)nalysis"); + Print(" %-20s %-12s %s\n", "Address Format", GetFmtConfig(m_addrFmt), "a(d)dressfmt"); + Print(" %-20s %-12s %s\n", "Port Format", GetFmtConfig(m_portFmt), "(p)ortfmt"); + Print(" %-20s %-12s %s\n", "Data Format", GetFmtConfig(m_dataFmt), "da(t)afmt"); + Print(" %-20s %-12s %s\n", "Show Labels", (m_showLabels ? "On" : "Off"), "show(l)abels"); + Print(" %-20s %-12s %s\n", "Show Opcodes", (m_showOpCodes ? "On" : "Off"), "show(o)pcodes"); + Print(" %-20s %-12u %s\n", "Mem Bytes Per Row", m_memBytesPerRow, "mem(b)ytesrow"); + return false; + } + + if (CheckToken(token, "a", "analysis")) + { + token = strtok(NULL, " "); + SetBoolConfig(token, m_analyseCode); + } + else if (CheckToken(token, "d", "addressfmt")) + { + token = strtok(NULL, " "); + SetFmtConfig(token, m_addrFmt); + } + else if (CheckToken(token, "p", "portfmt")) + { + token = strtok(NULL, " "); + SetFmtConfig(token, m_portFmt); + } + else if (CheckToken(token, "t", "datafmt")) + { + token = strtok(NULL, " "); + SetFmtConfig(token, m_dataFmt); + } + else if (CheckToken(token, "l", "showlabels")) + { + token = strtok(NULL, " "); + SetBoolConfig(token, m_showLabels); + } + else if (CheckToken(token, "o", "showopcodes")) + { + token = strtok(NULL, " "); + SetBoolConfig(token, m_showOpCodes); + } + else if (CheckToken(token, "b", "membytesrow")) + { + token = strtok(NULL, " "); + SetNumConfig(token, m_memBytesPerRow); + } + else + { + Error("Enter a valid option (a)nalysis, a(d)dressfmt, (p)ortfmt, da(t)afmt, show(l)abels, show(o)pcodes, mem(b)ytesrow.\n"); + return false; + } + } + else if (CheckToken(token, "ls", "loadstate")) // loadstate + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing filename.\n"); + return false; + } + + if (LoadState(token)) + Print("Debugger state successfully loaded from <%s>\n", token); + else + Error("Unable to load debugger state from <%s>\n", token); + } + else if (CheckToken(token, "ss", "savestate")) // savestate + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing filename.\n"); + return false; + } + + if (SaveState(token)) + Print("Debugger state successfully saved to <%s>\n", token); + else + Error("Unable to save debugger state to <%s>\n", token); + } + else if (CheckToken(token, "h", "help")) // help + { + // TODO - improve the following + const char *fmt = " %-6s %-25s %s\n"; + + Print("Debugger Commands:\n"); + + Print(" Execution:\n"); + Print(fmt, "n", "next", "[=1]"); + Print(fmt, "nf", "nextframe", "[=1]"); + Print(fmt, "s", "stepover", ""); + Print(fmt, "si", "stepinto", ""); + Print(fmt, "so", "stepout", ""); + Print(fmt, "c", "continue", "[]"); + Print(fmt, "spc", "setpc", ""); + + Print(" CPUs:\n"); + Print(fmt, "lc", "listcpus", ""); + Print(fmt, "sc", "switchcpu", "(|)"); + Print(fmt, "dc", "disablecpu", "(|)"); + Print(fmt, "ec", "enablecpu", "(|)"); + + Print(" Registers:\n"); + Print(fmt, "lr", "listregisters", ""); + Print(fmt, "pr", "printregister", ""); + Print(fmt, "sr", "setregister", " "); + Print(fmt, "lm", "listmonitors", ""); + Print(fmt, "m/am", "addmonitor", ""); + Print(fmt, "rm", "removemonitor", ""); + Print(fmt, "ram", "removeallmonitors", ""); + + Print(" Exceptions & interrupts:\n"); + Print(fmt, "le", "listexceptions", ""); + Print(fmt, "li", "listinterrupts", ""); + Print(fmt, "t/at", "addtrap", "((e)xception|(i)nterrupt) "); + Print(fmt, "rt", "removetrap", "((e)xception|(i)nterrupt) "); + Print(fmt, "rat", "removealltraps", "[(a)ll|(e)xceptions|(i)nterrupts]"); + + Print(" Disassembly, labels & comments:\n"); + Print(fmt, "l/ld", "listdisassembly", "[=last [#=20|]]"); + Print(fmt, "ll", "listlabels", "[(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints]"); + Print(fmt, "al", "addlabel", " "); + Print(fmt, "rl", "removelabel", "[|]"); + Print(fmt, "ral", "removealllabels", ""); + Print(fmt, "ac", "addcomment", " "); + Print(fmt, "rc", "removecomment", "[]"); + Print(fmt, "rac", "removeallcomments", ""); + + Print(" Breakpoints:\n"); + Print(fmt, "lb", "listbreakpoints", ""); + Print(fmt, "b/ab", "addbreakpoint", "[ [[s)imple|(c)ount )]]"); + Print(fmt, "rb", "removebreakpoint", "[#|]"); + Print(fmt, "rab", "removeallbreakpoints", ""); + + Print(" Memory, I/O & watches:\n"); + Print(fmt, "ln", "listregions", ""); + Print(fmt, "ly", "listmemory", "[=last [#=8|]]"); + Print(fmt, "py", "printmemory[.=b]", ""); + Print(fmt, "sy", "setmemory[.=b]", " "); + Print(fmt, "lo", "listios", ""); + Print(fmt, "lw", "listmemwatches", ""); + Print(fmt, "w/aw", "addmemwatch[.=b]", " [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]]"); + Print(fmt, "rw", "removememwatch", "(#|)"); + Print(fmt, "raw", "removeallmemwatches", ""); + Print(fmt, "lpw", "listportwatches", ""); + Print(fmt, "pw/apw", "addportwatch", " [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]]"); + Print(fmt, "rpw", "removeportwatch", "(#|)"); + Print(fmt, "rapw", "removeallportwatches", ""); + + Print("General:\n"); + Print(fmt, "p", "print[.=v]", " [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]"); + Print(fmt, "cfg", "configure", ""); + Print(fmt, "ls", "loadstate", ""); + Print(fmt, "ss", "savestate", ""); + Print(fmt, "h", "help", ""); + Print(fmt, "x", "exit", ""); + } + else if (CheckToken(token, "x", "exit")) // exit + { + Print("Exiting...\n"); + SetExit(); + return true; + } + else + Print("Unknown command '%s'.\n", token); + return false; + } + + void CConsoleDebugger::Read(char *str, size_t maxSize) + { + if (fgets(str, maxSize, stdin) != NULL) + { + char *pos = strchr(str, '\n'); + if (pos) + *pos = '\0'; + } + else + str[0] = '\0'; + } + + void CConsoleDebugger::Print(const char *fmtStr, ...) + { + va_list vl; + va_start(vl, fmtStr); + PrintVL(fmtStr, vl); + va_end(vl); + } + + void CConsoleDebugger::Error(const char *fmtStr, ...) + { + // Don't log errors to file + va_list vl; + va_start(vl, fmtStr); + vprintf(fmtStr, vl); + va_end(vl); + } + + void CConsoleDebugger::PrintVL(const char *fmtStr, va_list vl) + { + if (m_file != NULL) + vfprintf(m_file, fmtStr, vl); + else + vprintf(fmtStr, vl); + } + + void CConsoleDebugger::Flush() + { + fflush(stdout); + } + + bool CConsoleDebugger::CheckToken(const char *token, const char *simple, const char *full) + { + return stricmp(token, simple) == 0 || stricmp(token, full) == 0; + } + + bool CConsoleDebugger::CheckToken(const char *token, const char *simple, const char *full, char *modifier, size_t modSize, const char *defaultMod) + { + const char *pos = strchr(token, '.'); + if (pos == NULL) + { + strncpy(modifier, defaultMod, modSize); + modifier[modSize] = '\0'; + return CheckToken(token, simple, full); + } + else + { + pos++; + // BEGIN + if (*pos == '\0') + // END + return false; + strncpy(modifier, pos, modSize); + modifier[modSize] = '\0'; + + char actual[255]; + size_t actSize = min(pos - token - 1, 254); + strncpy(actual, token, actSize); + actual[actSize] = '\0'; + return CheckToken(actual, simple, full); + } + } + + void CConsoleDebugger::Truncate(char *dst, size_t maxLen, const char *src) + { + strncpy(dst, src, maxLen); + if (strlen(src) > maxLen) + strncpy(&dst[maxLen - 3], "...", 3); + dst[maxLen] = '\0'; + } + + void CConsoleDebugger::UpperFirst(char *dst, const char *src) + { + if (*src != '\0') + { + *dst++ = toupper(*src++); + while (*src != '\0') + *dst++ = *src++; + } + *dst = '\0'; + } + + void CConsoleDebugger::FormatOpCodes(char *str, int addr, int codesLen) + { + char *p = str; + int i = 0; + while (i < codesLen) + { + UINT8 opCode = (UINT8)m_cpu->ReadMem(addr++, 1); + sprintf(p, "%02X", opCode); + p += 2; + i++; + } + while (i++ < m_cpu->maxInstrLen) + { + *p++ = ' '; + *p++ = ' '; + } + *p = '\0'; + } + + bool CConsoleDebugger::GetLabelText(char *str, int maxLen, UINT32 addr) + { + char labelStr[255]; + + CLabel *label = m_cpu->GetLabel(addr); + if (label != NULL) + { + Truncate(str, maxLen, label->name); + return true; + } + + if (m_analyseCode) + { + CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); + CAutoLabel *autoLabel = analyser->analysis->GetAutoLabel(addr); + if (autoLabel != NULL && autoLabel->GetLabel(labelStr)) + { + Truncate(str, maxLen, labelStr); + return true; + } + } + + str[0] = '\0'; + return false; + } + + bool CConsoleDebugger::SetBoolConfig(const char *str, bool &cfg) + { + if (str == NULL) + { + Print("Current setting: %-12s\n", (cfg ? "On" : "Off")); + Print("Change setting with (o)n, o(f)f.\n"); + return false; + } + + if (CheckToken(str, "o", "on")) + { + cfg = true; + Print("Changed setting: On\n"); + ApplyConfig(); + return true; + } + else if (CheckToken(str, "f", "off")) + { + cfg = false; + Print("Changed setting: Off\n"); + ApplyConfig(); + return true; + } + else + { + Error("Enter a valid setting (o)n, o(f)f.\n"); + return false; + } + } + + bool CConsoleDebugger::SetNumConfig(const char *str, unsigned &cfg) + { + if (str == NULL) + { + Print("Current setting: %-12u\n", cfg); + return false; + } + + int number; + if (!ParseInt(str, &number)) + { + Error("Enter a valid number.\n"); + return false; + } + + cfg = (unsigned)number; + Print("Changed setting: %-12u\n", cfg); + ApplyConfig(); + return true; + } + + const char *CConsoleDebugger::GetFmtConfig(EFormat fmt) + { + switch (fmt) + { + case Hex0x: return "Hex (0x00)"; + case HexDollar: return "Hex ($00)"; + case HexPostH: return "Hex (00h)"; + case Decimal: return "Decimal"; + case Binary: return "Binary"; + default: return "-"; + } + } + + bool CConsoleDebugger::SetFmtConfig(const char *str, EFormat &cfg) + { + if (str == NULL) + { + Print("Current setting: %-12s\n", GetFmtConfig(cfg)); + Print("Change setting with (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.\n"); + return false; + } + + if (!ParseFormat(str, cfg)) + return false; + + Print("Changed setting: %-12s\n", GetFmtConfig(cfg)); + ApplyConfig(); + return true; + } + + bool CConsoleDebugger::ParseAddress(const char *str, UINT32 *addr) + { + if (m_cpu->instrCount > 0 && CheckToken(str, "-", "current")) + { + *addr = m_cpu->pc; + return true; + } + return m_cpu->ParseAddress(str, addr); + } + + const char *CConsoleDebugger::GetDataSizeStr(unsigned dataSize, bool shortName) + { + switch (dataSize) + { + case 1: return (shortName ? "b" : "byte"); + case 2: return (shortName ? "w" : "word"); + case 4: return (shortName ? "l" : "long"); + case 8: return (shortName ? "v" : "vlong"); + default: return GetSizeString(dataSize); + } + } + + bool CConsoleDebugger::ParseDataSize(const char *str, unsigned &dataSize) + { + int num = -1; + ParseInt(str, &num); + if (CheckToken(str, "b", "byte") || num == 1) + { + dataSize = 1; + return true; + } + else if (CheckToken(str, "w", "word") || num == 2) + { + dataSize = 2; + return true; + } + else if (CheckToken(str, "l", "long") || num == 4) + { + dataSize = 4; + return true; + } + else if (CheckToken(str, "v", "verylong") || num == 8) + { + dataSize = 8; + return true; + } + else + { + Error("Enter a valid size (b)yte, (w)ord, (l)ong or (v)erylong or a number 1, 2, 4 or 8.\n"); + return false; + } + } + + bool CConsoleDebugger::ParseFormat(const char *str, EFormat &fmt) + { + if (CheckToken(str, "h", "hex")) fmt = Hex; + else if (CheckToken(str, "z", "hexzero")) fmt = Hex0x; + else if (CheckToken(str, "l", "hexdollar")) fmt = HexDollar; + else if (CheckToken(str, "p", "hexposth")) fmt = HexPostH; + else if (CheckToken(str, "d", "decimal")) fmt = Decimal; + else if (CheckToken(str, "b", "binary")) fmt = Binary; + else + { + Error("Enter a valid format (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.\n"); + return false; + } + return true; + } + + bool CConsoleDebugger::ParseCPU(const char *str, CCPUDebug *&cpu) + { + if (str == NULL) + { + Error("Missing CPU name or number.\n"); + return false; + } + int cpuNum; + if (ParseInt(str, &cpuNum)) + { + if (cpuNum >= 0 && cpuNum < (int)cpus.size()) + { + cpu = cpus[cpuNum]; + return true; + } + } + cpu = GetCPU(str); + if (cpu == NULL) + { + Error("No CPU with that name or number.\n"); + return false; + } + return true; + } + + bool CConsoleDebugger::ParseRegister(const char *str, CRegister *®) + { + if (str == NULL) + { + Error("Missing register name.\n"); + return false; + } + reg = m_cpu->GetRegister(str); + if (reg == NULL) + { + Error("Enter a valid register name.\n"); + return false; + } + return true; + } + + void CConsoleDebugger::ListCPUs() + { + Print("CPUs:\n"); + if (cpus.size() == 0) + { + Print(" None\n"); + return; + } + + Print(" %-3s %-9s %-6s %-9s %-7s %-12s %-12s %-9s\n", "Num", "Name", "Type", "Debugging", "State", "Instr Count", "Total Cycles", "Frequency"); + unsigned num = 0; + double freq; + char onCPU; + const char *debugStr; + const char *stateStr; + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + onCPU = (*it == m_cpu ? '*': ' '); + debugStr = ((*it)->IsEnabled() ? "Enabled" : "Disabled"); + stateStr = ((*it)->active ? "Running" : "Waiting"); + + if ((*it)->instrCount > 0) + { + if (frameCount > 0) + { + freq = ((*it)->cyclesPerPoll * 60.0) / 1000000.0; + Print("%c%-3u %-9s %-6s %-9s %-7s %12llu %12llu %6.1fMHz\n", onCPU, num++, (*it)->name, (*it)->type, debugStr, stateStr, (*it)->instrCount - 1, (*it)->totalCycles, freq); + } + else + Print("%c%-3u %-9s %-6s %-9s %-7s %12llu %12llu %9s\n", onCPU, num++, (*it)->name, (*it)->type, debugStr, stateStr, (*it)->instrCount - 1, (*it)->totalCycles, "-"); + } + else + Print("%c%-3u %-9s %-6s %-9s %-7s %12s %12s %9s\n", onCPU, num++, (*it)->name, (*it)->type, debugStr, stateStr, "-", "-", "-"); + } + } + + void CConsoleDebugger::ListRegisters() + { + Print("%s Registers:\n", m_cpu->name); + if (m_cpu->regs.size() == 0) + { + Print(" None\n"); + return; + } + + // Get groups + vector groups; + vector > regsByGroup; + size_t totalRows = 0; + for (vector::iterator it = m_cpu->regs.begin(); it != m_cpu->regs.end(); it++) + { + const char *group = (*it)->group; + // TODO - find with stricmp rather than default find + if (find(groups.begin(), groups.end(), group) == groups.end()) + { + groups.push_back(group); + regsByGroup.resize(regsByGroup.size() + 1); + } + int index = find(groups.begin(), groups.end(), group) - groups.begin(); + vector *pRegsInGroup = ®sByGroup[index]; + pRegsInGroup->push_back(*it); + totalRows = max(totalRows, pRegsInGroup->size()); + } + + // Get max label and value widths in each group and print group headers + size_t numGroups = groups.size(); + vector labelWidths(numGroups); + vector valueWidths(numGroups); + vector groupWidths(numGroups); + char valStr[50]; + Print(" "); + for (size_t index = 0; index < numGroups; index++) + { + labelWidths[index] = 0; + valueWidths[index] = 0; + + vector *pRegsInGroup = ®sByGroup[index]; + for (vector::iterator it = pRegsInGroup->begin(); it != pRegsInGroup->end(); it++) + { + labelWidths[index] = max(labelWidths[index], strlen((*it)->name)); + (*it)->GetValue(valStr); + valueWidths[index] = max(valueWidths[index], strlen(valStr)); + } + + const char *group = groups[index]; + groupWidths[index] = max(labelWidths[index] + valueWidths[index] + 3, strlen(group) + 1); + Print("%-*s", (int)groupWidths[index], group); + } + Print("\n"); + + // Print rows of register values + char rowStr[50]; + for (size_t row = 0; row < totalRows; row++) + { + Print(" "); + for (size_t index = 0; index < numGroups; index++) + { + vector *pRegsInGroup = ®sByGroup[index]; + if (row < pRegsInGroup->size()) + { + CRegister *reg = (*pRegsInGroup)[row]; + reg->GetValue(valStr); + bool hasMon = m_cpu->GetRegMonitor(reg->name) != NULL; + + sprintf(rowStr, "%c%-*s %-*s", (hasMon ? '*' : ' '), (int)labelWidths[index], reg->name, (int)valueWidths[index], valStr); + } + else + rowStr[0] = '\0'; + + Print("%-*s", (int)groupWidths[index], rowStr); + } + Print("\n"); + } + } + + void CConsoleDebugger::ListExceptions() + { + Print("%s Exceptions:\n", m_cpu->name); + if (m_cpu->exceps.size() == 0) + { + Print(" None\n"); + return; + } + + char addrStr[20]; + UINT32 handlerAddr; + for (vector::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++) + { + if (m_cpu->GetHandlerAddr(*it, handlerAddr)) + m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFExcepHandler : LFNone)); + else + addrStr[0] = '\0'; + Print("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count); + } + } + + void CConsoleDebugger::ListInterrupts() + { + Print("%s Interrupts:\n", m_cpu->name); + if (m_cpu->inters.size() == 0) + { + Print(" None\n"); + return; + } + + char addrStr[20]; + UINT32 handlerAddr; + for (vector::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++) + { + if (m_cpu->GetHandlerAddr(*it, handlerAddr)) + m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFInterHandler : LFNone)); + else + addrStr[0] = '\0'; + Print("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count); + } + } + + void CConsoleDebugger::ListIOs() + { + Print("%s I/O Ports:\n", m_cpu->name); + if (m_cpu->ios.size() == 0) + { + Print(" None\n"); + return; + } + + const char *group = NULL; + char locStr[255]; + char dirChar; + char inStr[50]; + char outStr[50]; + for (vector::iterator it = m_cpu->ios.begin(); it != m_cpu->ios.end(); it++) + { + // Print group heading if starting a new group + if (group == NULL || stricmp((*it)->group, group) != 0) + { + group = (*it)->group; + Print(" %s:\n", group); + } + + // Get location string (memory address or port number) + (*it)->GetLocation(locStr); + + // See whether port was last read or written + if ((*it)->inCount + (*it)->outCount > 0) + dirChar = ((*it)->last == &(*it)->lastIn ? '<' : '>'); + else + dirChar = ' '; + // Format last input + if ((*it)->inCount > 0) + m_cpu->FormatData(inStr, (*it)->dataSize, (*it)->lastIn); + else + { + inStr[0] = '*'; + inStr[1] = '\0'; + } + size_t inLen = strlen(inStr); + int inLPad = 5 - (int)inLen / 2; + int inRPad = 5 - (int)inLen + (int)inLen / 2; + // Format last output + if ((*it)->outCount > 0) + m_cpu->FormatData(outStr, (*it)->dataSize, (*it)->lastOut); + else + { + outStr[0] = '*'; + outStr[1] = '\0'; + } + size_t outLen = strlen(outStr); + int outLPad = 5 - (int)outLen / 2; + int outRPad = 5 - (int)outLen + (int)outLen / 2; + + // Print details + Print(" %c%-12s %-30s %-8s %*s%s%*s%c%*s%s%*s\n", ((*it)->watch != NULL ? '*' : ' '), locStr, (*it)->name, + GetDataSizeStr((*it)->dataSize, true), inLPad, "", inStr, inRPad, "", dirChar, outLPad, "", outStr, outRPad, ""); + } + } + + void CConsoleDebugger::ListRegions() + { + Print("%s Regions:\n", m_cpu->name); + if (m_cpu->regions.size() == 0) + { + Print(" None\n"); + return; + } + + char startStr[255]; + char endStr[255]; + for (vector::iterator it = m_cpu->regions.begin(); it != m_cpu->regions.end(); it++) + { + // Format start and end address + m_cpu->FormatAddress(startStr, (*it)->addr); + m_cpu->FormatAddress(endStr, (*it)->addrEnd); + + // Print details + Print("%c%s-%s %s\n", ((*it)->isCode ? '*' : ' '), startStr, endStr, (*it)->name); + } + } + + void CConsoleDebugger::ListLabels(bool customLabels, ELabelFlags autoLabelFlags) + { + Print("%s Labels:\n", m_cpu->name); + + unsigned count = 0; + + char addrStr[20]; + if (customLabels && m_cpu->labels.size() > 0) + { + Print(" Custom Labels:\n"); + for (vector::iterator it = m_cpu->labels.begin(); it != m_cpu->labels.end(); it++) + { + m_cpu->FormatAddress(addrStr, (*it)->addr); + Print(" %s %s\n", addrStr, (*it)->name); + count++; + } + } + + if (m_analyseCode) + { + char labelStr[255]; + CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); + for (unsigned index = 0; index < CAutoLabel::numLabelFlags; index++) + { + ELabelFlags flag = CAutoLabel::GetLabelFlag(index); + if (!(autoLabelFlags & flag)) + continue; + vector withFlag = analyser->analysis->GetAutoLabels(flag); + if (withFlag.size() == 0) + continue; + const char *flagStr = CAutoLabel::GetFlagString(flag); + Print(" %ss:\n", flagStr); + for (vector::iterator it = withFlag.begin(); it != withFlag.end(); it++) + { + if (!(*it)->GetLabel(labelStr, flag)) + continue; + CBreakpoint *bp = m_cpu->GetBreakpoint((*it)->addr); + char bpChr = (bp != NULL ? bp->symbol : ' '); + m_cpu->FormatAddress(addrStr, (*it)->addr); + Print(" %c%s %s\n", bpChr, addrStr, labelStr); + count++; + } + } + } + + if (count == 0) + { + Print(" None\n"); + return; + } + } + + void CConsoleDebugger::GetAllMemWatches(vector &watches) + { + for (vector::iterator it = m_cpu->memWatches.begin(); it != m_cpu->memWatches.end(); it++) + watches.push_back(*it); + for (vector::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++) + { + if (dynamic_cast((*it)->io) != NULL) + watches.push_back(*it); + } + } + + int CConsoleDebugger::GetIndexOfMemWatch(CWatch *watch) + { + vector watches; + GetAllMemWatches(watches); + vector::iterator it = find(watches.begin(), watches.end(), watch); + if (it == watches.end()) + return -1; + return it - watches.begin(); + } + + void CConsoleDebugger::ListMemWatches() + { + Print("%s Memory Watches:\n", m_cpu->name); + + vector watches; + GetAllMemWatches(watches); + + if (watches.size() == 0) + { + Print(" None\n"); + return; + } + + Print(" %-3s %-8s %-12s %-5s %-4s %-20s %s\n", "Num", "Type", "Address", "Size", "Trig", "Value", "Info"); + + char typeStr[12]; + char addrStr[20]; + char sizeStr[20]; + const char *dSizeStr; + const char *trigStr; + char valStr[20]; + char infoStr[255]; + unsigned wNum = 0; + for (vector::iterator it = watches.begin(); it != watches.end(); it++) + { + UpperFirst(typeStr, (*it)->type); + m_cpu->FormatAddress(addrStr, (*it)->addr, true); + dSizeStr = GetDataSizeStr((*it)->size, false); + UpperFirst(sizeStr, dSizeStr); + if ((*it)->trigRead && (*it)->trigWrite) trigStr = "R/W"; + else if ((*it)->trigRead) trigStr = "R"; + else if ((*it)->trigWrite) trigStr = "W"; + else trigStr = "-"; + m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue()); + if (!(*it)->GetInfo(infoStr)) + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-5s %-4s %-20s %s\n", wNum++, typeStr, addrStr, sizeStr, trigStr, valStr, infoStr); + } + } + + void CConsoleDebugger::GetAllPortWatches(vector &watches) + { + for (vector::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++) + { + if (dynamic_cast((*it)->io) != NULL) + watches.push_back(*it); + } + } + + int CConsoleDebugger::GetIndexOfPortWatch(CWatch *watch) + { + vector watches; + GetAllPortWatches(watches); + vector::iterator it = find(watches.begin(), watches.end(), watch); + if (it == watches.end()) + return -1; + return it - watches.begin(); + } + + void CConsoleDebugger::ListPortWatches() + { + Print("%s I/O Port Watches:\n", m_cpu->name); + + vector watches; + GetAllPortWatches(watches); + + if (watches.size() == 0) + { + Print(" None\n"); + return; + } + + Print(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Location", "Trig", "Last In/Out", "Info"); + + char typeStr[12]; + char locStr[255]; + const char *trigStr; + char valStr[20]; + char infoStr[255]; + unsigned wNum = 0; + for (vector::iterator it = watches.begin(); it != watches.end(); it++) + { + UpperFirst(typeStr, (*it)->type); + (*it)->io->GetLocation(locStr); + if ((*it)->trigRead && (*it)->trigWrite) trigStr = "I/O"; + else if ((*it)->trigRead) trigStr = "I"; + else if ((*it)->trigWrite) trigStr = "O"; + else trigStr = "-"; + if ((*it)->readCount + (*it)->writeCount == 0) + { + valStr[0] = '-'; + valStr[1] = '\0'; + } + else + m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue()); + if (!(*it)->GetInfo(infoStr)) + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, typeStr, locStr, trigStr, valStr, infoStr); + } + } + + void CConsoleDebugger::ListBreakpoints() + { + Print("%s Breakpoints:\n", m_cpu->name); + if (m_cpu->bps.size() == 0) + { + Print(" None\n"); + return; + } + + Print(" %-3s %-8s %-12s %-20s\n", "Num", "Type", "Address", "Info"); + + char typeStr[12]; + char addrStr[20]; + char infoStr[255]; + for (vector::iterator it = m_cpu->bps.begin(); it != m_cpu->bps.end(); it++) + { + UpperFirst(typeStr, (*it)->type); + m_cpu->FormatAddress(addrStr, (*it)->addr, true, (m_analyseCode ? LFAll : LFNone)); + if (!(*it)->GetInfo(infoStr)) + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-20s\n", (*it)->num, typeStr, addrStr, infoStr); + } + } + + void CConsoleDebugger::ListMonitors() + { + Print("%s Register Monitors:\n", m_cpu->name); + if (m_cpu->regMons.size() == 0) + { + Print(" None\n"); + return; + } + + char valStr[255]; + int num = 0; + for (vector::iterator it = m_cpu->regMons.begin(); it != m_cpu->regMons.end(); it++) + { + (*it)->GetBeforeValue(valStr); + Print(" %d - %s change from: %s\n", num++, (*it)->reg->name, valStr); + } + } + + UINT32 CConsoleDebugger::ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs) + { + UINT32 addr; + char startStr[20]; + char endStr[20]; + char opCodes[50]; + char mnemonic[100]; + char operands[155]; + char addrStr[20]; + char labelStr[13]; + unsigned instr; + + UINT32 pc = m_cpu->pc; + CCodeAnalyser *analyser = (m_analyseCode ? m_cpu->GetCodeAnalyser() : NULL); + + // Align address to instruction boundary + start -= start%m_cpu->minInstrLen; + + if (analyser != NULL) + { + // Use code analyser to find valid start address + if (!analyser->analysis->GetNextValidAddr(start)) + return start; + } + else + { + // In the absence of code analyser, try to align code with current PC address + if (m_cpu->instrCount > 0 && pc >= start && pc <= end) + { + UINT64 count = m_cpu->instrCount; + while (start < end && count-- > 0) + { + bool okay = false; + addr = start; + instr = 0; + while (addr < end && instr < numInstrs) + { + if (addr == pc) + { + okay = true; + break; + } + int codesLen = m_cpu->GetOpLength(addr); + addr += abs(codesLen); + instr++; + } + if (okay) + break; + start += m_cpu->minInstrLen; + } + } + } + + // Get true end address + addr = start; + instr = 0; + while (addr < end && instr < numInstrs) + { + // Get instruction length + int codesLen = m_cpu->Disassemble(addr, mnemonic, operands); + // Move onto next valid instruction address + addr += abs(codesLen); + if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr)) + break; + instr++; + } + end = addr; + + // Format start and end addresses and output title + m_cpu->FormatAddress(startStr, start); + m_cpu->FormatAddress(endStr, end); + Print("%s Code %s - %s:\n", m_cpu->name, startStr, endStr); + + // Output the disassembly + addr = start; + instr = 0; + while (addr < end) + { + // Add markers for current PC address and any breakpoints + char ind[4]; + if (m_cpu->instrCount > 0 && addr == pc) + { + ind[0] = '>'; + ind[1] = '>'; + } + else + { + ind[0] = ' '; + ind[1] = ' '; + } + CBreakpoint *bp = m_cpu->GetBreakpoint(addr); + ind[2] = (bp != NULL ? bp->symbol : ' '); + ind[3] = '\0'; + + // Format current address + m_cpu->FormatAddress(addrStr, addr); + + // Get labels at address (if any) + bool hasLabel = GetLabelText(labelStr, 12, addr); + + // Get mnemonic, operands and instruction length and print them + int codesLen = m_cpu->Disassemble(addr, mnemonic, operands); + FormatOpCodes(opCodes, addr, abs(codesLen)); + + // Get comment at address (if any) + CComment *comment = m_cpu->GetComment(addr); + + // Output line + Print("%s", ind); + if (m_showLabels) + { + if (m_labelsOverAddr) + Print("%-12s ", (hasLabel ? labelStr : addrStr)); + else + Print("%s %-12s ", addrStr, labelStr); + } + else + Print("%s ", addrStr); + if (m_showOpCodes) + Print("[%s] ", opCodes); + if (codesLen > 0) + Print("%-*s %-20s", (int)m_cpu->maxMnemLen, mnemonic, operands); + else + Print("???"); + if (comment != NULL) + Print(" ; %s", comment->text); + Print("\n"); + + // Move onto next valid instruction address + addr += abs(codesLen); + if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr)) + break; + } + return end; + } + + UINT32 CConsoleDebugger::ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow) + { + char startStr[255]; + char endStr[255]; + char addrStr[20]; + char labelStr[13]; + char wChar, dChar; + UINT8 data; + + // Adjust start/end points to be on row boundary + start -= (start % bytesPerRow); + end += bytesPerRow - (end % bytesPerRow); + + // Format start and end addresses and output title + m_cpu->FormatAddress(startStr, start); + m_cpu->FormatAddress(endStr, end); + Print("%s Memory %s - %s:\n", m_cpu->name, startStr, endStr); + + UINT32 addr = start; + while (addr < end) + { + // TODO - check address going out of region or out of range + + // Format current address + m_cpu->FormatAddress(addrStr, addr); + + // Get labels at address (if any) + bool hasLabel = GetLabelText(labelStr, 12, addr); + + // Output line + if (m_showLabels) + { + if (m_labelsOverAddr) + Print(" %-12s", (hasLabel ? labelStr : addrStr)); + else + Print(" %s %-12s", addrStr, labelStr); + } + else + Print(" %s%c", addrStr); + UINT32 lAddr = addr; + for (unsigned i = 0; i < bytesPerRow; i++) + { + CWatch *watch = m_cpu->GetMemWatch(lAddr, 1); + // TODO - handling of mapped I/O + //CMappedIO *io = m_cpu->GetMappedIO(lAddr); + wChar = (watch != NULL ? watch->symbol : ' '); + //if (io != NULL) + // data = (UINT8)io->last; + //else + data = (UINT8)m_cpu->ReadMem(lAddr, 1); + Print("%c%02X", wChar, data); + lAddr++; + } + Print(" "); + lAddr = addr; + for (unsigned i = 0; i < bytesPerRow; i++) + { + // TODO - handling of mapped I/O + //CMappedIO *io = m_cpu->GetMappedIO(lAddr); + //if (io != NULL) + // data = io->last; + //else + data = (UINT8)m_cpu->ReadMem(lAddr, 1); + dChar = (data >= 32 && data <= 126 ? (char)data : '.'); + Print("%c", dChar); + lAddr++; + } + Print("\n"); + addr += bytesPerRow; + } + return addr; + } + + void CConsoleDebugger::AnalysisUpdated(CCodeAnalyser *analyser) + { + // + } + + void CConsoleDebugger::ExceptionTrapped(CException *ex) + { + PrintEvent(ex->cpu, "Exception %s (%s) trapped.\n", ex->id, ex->name); + } + + void CConsoleDebugger::InterruptTrapped(CInterrupt *in) + { + PrintEvent(in->cpu, "Interrupt %s (%s) trapped.\n", in->id, in->name); + } + + void CConsoleDebugger::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) + { + const char *sizeStr = GetDataSizeStr(dataSize, true); + const char *rwStr = (isRead ? "Read from" : "Write to"); + char dataStr[50]; + char addrStr[255]; + m_cpu->FormatData(dataStr, dataSize, data); + watch->cpu->FormatAddress(addrStr, addr, true); + int num = GetIndexOfMemWatch(watch); + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered memory watch #%d.\n", rwStr, addrStr, dataStr, sizeStr, num); + } + + void CConsoleDebugger::IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) + { + const char *sizeStr = GetDataSizeStr(io->dataSize, true); + const char *ioStr = (isInput ? "Input from" : "Output to"); + char dataStr[50]; + char locStr[255]; + m_cpu->FormatData(dataStr, io->dataSize, data); + watch->io->GetLocation(locStr); + int num = GetIndexOfPortWatch(watch); + if (num >= 0) + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered port watch #%d.\n", ioStr, locStr, dataStr, sizeStr, num); + else + { + num = GetIndexOfMemWatch(watch); + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered memory watch #%d.\n", ioStr, locStr, dataStr, sizeStr, num); + } + } + + void CConsoleDebugger::BreakpointReached(CBreakpoint *bp) + { + PrintEvent(bp->cpu, "Breakpoint #%d triggered.\n", bp->num); + } + + void CConsoleDebugger::MonitorTriggered(CRegMonitor *regMon) + { + char valStr[255]; + CRegister *reg = regMon->reg; + reg->GetValue(valStr); + PrintEvent(reg->cpu, "Write to register %s (%s) triggered monitor.\n", reg->name, valStr); + } + + void CConsoleDebugger::ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) + { + if (reason&HaltUser) + PrintEvent(cpu, "Execution halted.\n"); + } + + void CConsoleDebugger::Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) + { + if (cpu != NULL) + { + char pcStr[255]; + cpu->FormatAddress(pcStr, cpu->pc, true, LFNone); + Print("%s @ %s: ", cpu->name, pcStr); + } + if (typeStr != NULL) + Print(" %s - ", typeStr); + PrintVL(fmtStr, vl); + } + + void CConsoleDebugger::Attach() + { + CDebugger::Attach(); + + ApplyConfig(); + + Attached(); + } + + void CConsoleDebugger::Detach() + { + Detaching(); + + CDebugger::Detach(); + + // Close redirected output file, if exists + if (m_file != NULL) + { + fclose(m_file); + m_file = NULL; + } + } + + void CConsoleDebugger::Poll() + { + CDebugger::Poll(); + + if (m_nextFrame) + { + if (--m_nextFrameCount == 0) + ForceBreak(true); + } + } + + void CConsoleDebugger::ApplyConfig() + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + (*it)->addrFmt = m_addrFmt; + (*it)->portFmt = m_portFmt; + (*it)->dataFmt = m_dataFmt; + } + } + + void CConsoleDebugger::Attached() + { + // + } + + void CConsoleDebugger::Detaching() + { + // + } +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/ConsoleDebugger.h b/Src/Debugger/ConsoleDebugger.h new file mode 100644 index 0000000..dec31d4 --- /dev/null +++ b/Src/Debugger/ConsoleDebugger.h @@ -0,0 +1,193 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * ConsoleDebugger.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_CONSOLEDEBUGGER_H +#define INCLUDED_CONSOLEDEBUGGER_H + +#include "Debugger.h" +#include "CodeAnalyser.h" +#include "Types.h" + +#include +#include + +#define NUM_LISTAUTOLABELS (sizeof(s_listAutoLabels) / sizeof(ELabelFlags)) + +namespace Debugger +{ + class CCPUDebug; + class CException; + class CWatch; + class CBreakpoint; + class CRegister; + class CRegMonitor; + + /* + * Base class for a console-based debugger. + * TODO - a lot of commands are not yet implemented/missing and the main loop is a big ugly mess :-) ! + */ + class CConsoleDebugger : public CDebugger + { + private: + static ELabelFlags s_listAutoLabels[]; + + CCPUDebug *m_cpu; + + bool m_nextFrame; + unsigned m_nextFrameCount; + + UINT32 m_listDism; + UINT32 m_listMem; + + bool m_analyseCode; + EFormat m_addrFmt; + EFormat m_portFmt; + EFormat m_dataFmt; + bool m_showLabels; + bool m_labelsOverAddr; + bool m_showOpCodes; + unsigned m_memBytesPerRow; + + FILE *m_file; + + protected: + void Read(char *str, size_t maxLen); + + void Print(const char *fmtStr, ...); + + void Error(const char *fmtStr, ...); + + void PrintVL(const char *fmtStr, va_list vl); + + void Flush(); + + bool CheckToken(const char *token, const char *simple, const char *full); + + bool CheckToken(const char *token, const char *simple, const char *full, char *modifier, size_t modSize, const char *defaultMod); + + void Truncate(char *dst, size_t maxLen, const char *src); + + void UpperFirst(char *dst, const char *src); + + void FormatOpCodes(char *str, int addr, int codesLen); + + bool GetLabelText(char *str, int maxLen, UINT32 addr); + + bool SetBoolConfig(const char *str, bool &cfg); + + bool SetNumConfig(const char *str, unsigned &cfg); + + const char *GetFmtConfig(EFormat fmt); + + bool SetFmtConfig(const char *str, EFormat &cfg); + + bool ParseAddress(const char *str, UINT32 *addr); + + const char *GetDataSizeStr(unsigned dataSize, bool shortName); + + bool ParseDataSize(const char *str, unsigned &dataSize); + + bool ParseFormat(const char *str, EFormat &fmt); + + bool ParseCPU(const char *str, CCPUDebug *&cpu); + + bool ParseRegister(const char *str, CRegister *®); + + void ListCPUs(); + + void ListRegisters(); + + void ListExceptions(); + + void ListInterrupts(); + + void ListIOs(); + + void ListRegions(); + + void ListLabels(bool customLabels, ELabelFlags autoLabelFlags); + + void GetAllMemWatches(std::vector &watches); + + int GetIndexOfMemWatch(CWatch *watch); + + void ListMemWatches(); + + void GetAllPortWatches(std::vector &watches); + + int GetIndexOfPortWatch(CWatch *watch); + + void ListPortWatches(); + + void ListBreakpoints(); + + void ListMonitors(); + + UINT32 ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs); + + UINT32 ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow); + + virtual void ApplyConfig(); + + virtual void Attached(); + + virtual void Detaching(); + + virtual void AnalysisUpdated(CCodeAnalyser *analyser); + + virtual void ExceptionTrapped(CException *ex); + + virtual void InterruptTrapped(CInterrupt *ex); + + virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead); + + virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput); + + virtual void BreakpointReached(CBreakpoint *bp); + + virtual void MonitorTriggered(CRegMonitor *regMon); + + virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason); + + virtual void WaitCommand(CCPUDebug *cpu); + + virtual bool ProcessToken(const char *token, const char *cmd); + + virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl); + + public: + CConsoleDebugger(); + + void Attach(); + + void Detach(); + + virtual void Poll(); + }; +} + +#endif // INCLUDED_CONSOLEDEBUGGER_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Debugger.cpp b/Src/Debugger/Debugger.cpp new file mode 100644 index 0000000..44eceb8 --- /dev/null +++ b/Src/Debugger/Debugger.cpp @@ -0,0 +1,527 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Debugger.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Supermodel.h" +#include "Debugger.h" +#include "CPUDebug.h" + +#include +#include + +using namespace std; + +namespace Debugger +{ + unsigned CDebugger::GetDataSize(UINT64 data) + { + if (data <= 0xFFULL) return 1; + else if (data <= 0xFFFFULL) return 2; + else if (data <= 0xFFFFFFULL) return 3; + else if (data <= 0xFFFFFFFFULL) return 4; + else if (data <= 0xFFFFFFFFFFULL) return 5; + else if (data <= 0xFFFFFFFFFFFFULL) return 6; + else if (data <= 0xFFFFFFFFFFFFFFULL) return 7; + else return 8; + } + + const char *CDebugger::GetSizeString(unsigned dataSize) + { + switch (dataSize) + { + case 1: return "8-bit"; + case 2: return "16-bit"; + case 3: return "24-bit"; + case 4: return "32-bit"; + case 5: return "40-bit"; + case 6: return "48-bit"; + case 7: return "56-bit"; + case 8: return "64-bit"; + default: return ""; + } + } + + UINT64 CDebugger::MaskData(unsigned dataSize, UINT64 data) + { + switch (dataSize) + { + case 0: return 0; + case 1: return data&0xFFULL; + case 2: return data&0xFFFFULL; + case 3: return data&0xFFFFFFULL; + case 4: return data&0xFFFFFFFFULL; + case 5: return data&0xFFFFFFFFFFULL; + case 6: return data&0xFFFFFFFFFFFFULL; + case 7: return data&0xFFFFFFFFFFFFFFULL; + default: return data; + } + } + + UINT64 CDebugger::GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize) + { + if (addr == slotAddr && dataSize == slotDataSize) + { + // Data perfectly aligned with slot + return MaskData(slotDataSize, data); + } + unsigned overlap, shift; + if (addr >= slotAddr) + { + // Data starts after slot beginning + overlap = slotAddr + slotDataSize - addr; + if (overlap < dataSize) + { + // Data ends after slot end + shift = dataSize - overlap; + return MaskData(overlap, data>>(8 * shift)); + } + else + { + // Data falls completely inside slot + shift = overlap - dataSize; + return MaskData(dataSize, data)<<(8 * shift); + } + } + else + { + // Data starts before slot beginning + overlap = addr + dataSize - slotAddr; + if (overlap < slotDataSize) + { + // Data ends before slot end + shift = slotDataSize - overlap; + return MaskData(overlap, data)<<(8 * shift); + } + else + { + // Slot lies completely inside data + shift = overlap - slotDataSize; + return MaskData(slotDataSize, data>>(8 * shift)); + } + } + } + + bool CDebugger::ParseInt(const char *str, int *val) + { + return sscanf(str, "%d", val) == 1; + } + + CDebugger::CDebugger() : m_exit(false), m_pause(false), m_break(false), m_breakUser(false), + frameCount(0), logDebug(true), logInfo(true), logError(true) + { +#ifdef DEBUGGER_HASTHREAD + m_mutex = CThread::CreateMutex(); + m_primaryCPU = NULL; +#endif // DEBUGGER_HASTHREAD + } + + CDebugger::~CDebugger() + { + DeleteCPUs(); + } + + void CDebugger::DeleteCPUs() + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + delete *it; + cpus.clear(); + } + + void CDebugger::AddCPU(CCPUDebug *cpu) + { + cpu->AttachToDebugger(this); + cpus.push_back(cpu); + if (m_break) + cpu->ForceBreak(m_breakUser); + } + + void CDebugger::RemoveCPU(CCPUDebug *cpu) + { + cpu->DetachFromDebugger(this); + vector::iterator it = find(cpus.begin(), cpus.end(), cpu); + if (it != cpus.end()) + cpus.erase(it); + } + + CCPUDebug *CDebugger::GetCPU(const char *name) + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if (stricmp(name, (*it)->name) == 0) + return *it; + } + return NULL; + } + + void CDebugger::SetExit() + { + m_exit = true; + + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + //(*it)->RemoveAllExceptionTraps(); + (*it)->RemoveAllMemWatches(); + (*it)->RemoveAllIOWatches(); + (*it)->RemoveAllBreakpoints(); + (*it)->RemoveAllRegMonitors(); + (*it)->SetContinue(); + } + } + + void CDebugger::SetPause(bool pause) + { + m_pause = pause; + } + + void CDebugger::ForceBreak(bool user) + { + m_break = true; + m_breakUser = user; + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->ForceBreak(user); + } + + void CDebugger::ClearBreak() + { + m_break = false; + m_breakUser = false; + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->ClearBreak(); + } + +#ifdef DEBUGGER_HASTHREAD + bool CDebugger::MakePrimary(CCPUDebug *cpu) + { + m_mutex->Lock(); + + bool isPrimary = m_primaryCPU == NULL || m_primaryCPU == cpu; + if (isPrimary) + m_primaryCPU = cpu; + + m_mutex->Unlock(); + + if (isPrimary) + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if ((*it) != m_primaryCPU) + (*it)->ForceWait(); + } + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if ((*it) != m_primaryCPU) + (*it)->WaitForHalt(); + } + } + + return isPrimary; + } + + void CDebugger::ReleasePrimary() + { + m_mutex->Lock(); + + m_primaryCPU = NULL; + + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if ((*it) == m_primaryCPU) + (*it)->ClearBreak(); + else + (*it)->ClearWait(); + } + + m_mutex->Unlock(); + } +#endif // DEBUGGER_HASTHREAD + + void CDebugger::Attach() + { + AddCPUs(); + + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->AttachToCPU(); + } + + void CDebugger::Detach() + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->DetachFromCPU(); + + DeleteCPUs(); + } + + void CDebugger::Reset() + { + frameCount = 0; + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->DebuggerReset(); + } + + void CDebugger::Poll() + { + frameCount++; + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + (*it)->DebuggerPolled(); + } + + void CDebugger::PrintEvent(CCPUDebug *cpu, const char *fmt, ...) + { + va_list vl; + va_start(vl, fmt); + Log(cpu, NULL, fmt, vl); + va_end(vl); + } + +#ifdef DEBUGGER_HASLOGGER + void CDebugger::DebugLog(const char *fmt, va_list vl) + { + if (logDebug) + Log(NULL, "Debug", fmt, vl); + } + + void CDebugger::InfoLog(const char *fmt, va_list vl) + { + if (logInfo) + Log(NULL, "Info", fmt, vl); + } + + void CDebugger::ErrorLog(const char *fmt, va_list vl) + { + if (logError) + Log(NULL, "Error", fmt, vl); + } +#endif // DEBUGGER_HASLOGGER + + bool CDebugger::ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data) + { + unsigned i; + char upperStr[255]; + const char *p; + switch (format) + { + case Hex: + case Hex0x: + case HexDollar: + case HexPostH: + for (i = 0; str[i] && i < 254; i++) + upperStr[i] = toupper(str[i]); + upperStr[i] = '\0'; + if (sscanf(upperStr, "%llX", data) != 1 && + sscanf(upperStr, "0x%llX", data) != 1 && + sscanf(upperStr, "$%llX", data) != 1 && + sscanf(upperStr, "%llXH", data) != 1) + return false; + break; + case Decimal: + if (sscanf(str, "%llu", data) != 1) + return false; + break; + case Binary: + p = str; + while (*p != '\0' && *p != '0' && *p != '1') + p++; + if (*p == '\0') + return false; + *data = 0; + for (i = 0; i < dataSize * 8; i++) + { + if (*p == '1') + *data |= 1; + else if (*p != '0') + break; + *data <<= 1; + p++; + } + break; + case ASCII: + // TODO + return false; + default: + return false; + } + switch (dataSize) + { + case 0: return 0; + case 1: return *data <= (UINT64)0xFF; + case 2: return *data <= (UINT64)0xFFFF; + case 3: return *data <= (UINT64)0xFFFFFF; + case 4: return *data <= (UINT64)0xFFFFFFFF; + case 5: return *data <= (UINT64)0xFFFFFFFFFF; + case 6: return *data <= (UINT64)0xFFFFFFFFFFFF; + case 7: return *data <= (UINT64)0xFFFFFFFFFFFFFF; + default: return true; + } + } + + void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, INT64 data) + { + if (data >= 0) + FormatData(str, format, dataSize, (UINT64)data); + else + { + *str++ = '-'; + FormatData(str, format, dataSize, (UINT64)(data >= 0 ? data : -data)); // No portable abs64 available + } + } + + void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data) + { + data = MaskData(dataSize, data); + unsigned i; + switch (format) + { + case Hex: sprintf(str, "%0*llX", (int)dataSize * 2, data); break; + case Hex0x: sprintf(str, "0x%0*llX", (int)dataSize * 2, data); break; + case HexDollar: sprintf(str, "$%0*llX", (int)dataSize * 2, data); break; + case HexPostH: sprintf(str, "%0*llXh", (int)dataSize * 2, data); break; + case Decimal: sprintf(str, "%llu", data); break; + case Binary: + str += dataSize * 8; + *str-- = '\0'; + for (i = 0; i < dataSize * 8; i++) + { + *str-- = ((data&1) ? '1' : '0'); + data >>= 1; + } + break; + case ASCII: + for (i = 0; i < dataSize; i++) + { + *str++ = (char)(data & 0xFF); + data >>= 8; + } + *str++ = '\0'; + break; + default: + str[0] = '\0'; + break; + } + } + + bool CDebugger::HasState() + { + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if ((*it)->labels.size() + (*it)->comments.size() > 0) + return true; + } + return false; + } + + bool CDebugger::LoadState(const char *fileName) + { +#ifdef DEBUGGER_HASBLOCKFILE + // Open file and find header + CBlockFile state; + if (state.Load(fileName) != OKAY) + return false; + if (state.FindBlock("Debugger State") != OKAY) + { + state.Close(); + return false; + } + + // Check version in header matches + unsigned version; + state.Read(&version, sizeof(version)); + if (version != DEBUGGER_STATEFILE_VERSION) + { + state.Close(); + return false; + } + + // Load debugger state + if (!LoadState(&state)) + { + state.Close(); + return false; + } + + // Load state for each CPU + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if (!(*it)->LoadState(&state)) + { + state.Close(); + return false; + } + } + + state.Close(); + return true; +#else + return false; +#endif // DEBUGGER_HASBLOCKFILE + } + + bool CDebugger::SaveState(const char *fileName) + { +#ifdef DEBUGGER_HASBLOCKFILE + // Create file with header + CBlockFile state; + if (state.Create(fileName, "Debugger State", __FILE__) != OKAY) + return false; + + // Write out version in header + unsigned version = DEBUGGER_STATEFILE_VERSION; + state.Write(&version, sizeof(version)); + + // Save debugger state + if (!SaveState(&state)) + { + state.Close(); + return false; + } + + // Save state for each CPU + for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) + { + if (!(*it)->SaveState(&state)) + return false; + } + + state.Close(); + return true; +#else + return false; +#endif // DEBUGGER_HASBLOCKFILE + } + +#ifdef DEBUGGER_HASBLOCKFILE + bool CDebugger::LoadState(CBlockFile *state) + { + return true; + } + + bool CDebugger::SaveState(CBlockFile *state) + { + return true; + } +#endif +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/Debugger.h b/Src/Debugger/Debugger.h new file mode 100644 index 0000000..58719bf --- /dev/null +++ b/Src/Debugger/Debugger.h @@ -0,0 +1,275 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Debugger.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_DEBUGGER_H +#define INCLUDED_DEBUGGER_H + +#include +#include +#include +#include + +#include "Types.h" + +//#if defined(SUPERMODEL_WIN32) || defined(SUPERMODEL_UNIX) || defined(SUPERMODEL_OSX) +#define DEBUGGER_HASBLOCKFILE +#define DEBUGGER_HASLOGGER +#define DEBUGGER_HASTHREAD +//#endif + +#define DEBUGGER_STATEFILE_VERSION 0 + +#ifdef DEBUGGER_HASBLOCKFILE +#include "BlockFile.h" +#endif // DEBUGGER_HASBLOCKFILE + +#ifdef DEBUGGER_HASLOGGER +#ifndef SUPERMODEL_VERSION +#define SUPERMODEL_VERSION "" +#endif // SUPERMODEL_VERSEION +#include "OSD/Logger.h" +#endif // DEBUGGER_HASLOGGER + +#ifdef DEBUGGER_HASTHREAD +#include "OSD/Thread.h" +#endif // DEBUGGER_HASTHREAD + +#ifndef stricmp +#ifdef _MSC_VER // MS VisualC++ + #define stricmp _stricmp +#elif defined(__GNUC__) + #define stricmp strcasecmp +#endif // _MSC_VER +#endif // stricmp + +namespace Debugger +{ + class CCPUDebug; + class CCodeAnalyser; + class CException; + class CInterrupt; + class CIO; + class CWatch; + class CBreakpoint; + class CRegMonitor; + + enum EHaltReason + { + HaltNone = 0, + HaltState = 1, + HaltUser = 2, + HaltStep = 4, + HaltCount = 8, + HaltUntil = 16 + }; + + enum EFormat + { + Hex = 0, + Hex0x, + HexDollar, + HexPostH, + Decimal, + Binary, + ASCII + }; + + /* + * Base class for a debugger. + * Provides common methods for loading/saving debugger state, formatting/parsing data and general control of the debugger. + * Also holds references to the CCPUDebug objects, one for each debuggable CPU, and contains pure virtual methods for sub-classes to + * implement which act as callbacks for the various debugging events that can occur. + */ +#ifdef DEBUGGER_HASLOGGER + class CDebugger : public CLogger +#else + class CDebugger +#endif // DEBUGGER_HASLOGGER + { + friend class CCPUDebug; + friend class CCodeAnalyser; + + private: + bool m_exit; + bool m_pause; + bool m_break; + bool m_breakUser; + +#ifdef DEBUGGER_HASTHREAD + CMutex *m_mutex; + CCPUDebug *m_primaryCPU; +#endif + + protected: + virtual void AddCPUs() = 0; + + virtual void DeleteCPUs(); + +#ifdef DEBUGGER_HASBLOCKFILE + virtual bool LoadState(CBlockFile *state); + + virtual bool SaveState(CBlockFile *state); +#endif // DEBUGGER_HASBLOCKFILE + + // + // Protected virtual methods for sub-classes to implement + // + + virtual void AnalysisUpdated(CCodeAnalyser *analyser) = 0; + + virtual void ExceptionTrapped(CException *ex) = 0; + + virtual void InterruptTrapped(CInterrupt *in) = 0; + + virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) = 0; + + virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) = 0; + + virtual void BreakpointReached(CBreakpoint *bp) = 0; + + virtual void MonitorTriggered(CRegMonitor *regMon) = 0; + + virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) = 0; + + virtual void WaitCommand(CCPUDebug *cpu) = 0; + + virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0; + + public: + std::vector cpus; + + UINT64 frameCount; + +#ifdef DEBUGGER_HASLOGGER + bool logDebug; + bool logInfo; + bool logError; +#endif // DEBUGGER_HASLOGGER + + static unsigned GetDataSize(UINT64 data); + + static const char *GetSizeString(unsigned dataSize); + + static UINT64 MaskData(unsigned dataSize, UINT64 data); + + static UINT64 GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize); + + static bool ParseInt(const char *str, int *val); + + CDebugger(); + + virtual ~CDebugger(); + + void AddCPU(CCPUDebug *cpu); + + void RemoveCPU(CCPUDebug *cpu); + + CCPUDebug *GetCPU(const char *name); + + void SetExit(); + + void SetPause(bool pause); + + void ForceBreak(bool user); + + void ClearBreak(); + + bool CheckExit(); + + bool CheckPause(); + +#ifdef DEBUGGER_HASTHREAD + bool MakePrimary(CCPUDebug *cpu); + + void ReleasePrimary(); +#endif // DEBUGGER_HASTHREAD + + // + // Printing/logging + // + + void PrintEvent(CCPUDebug *cpu, const char *fmt, ...); + +#ifdef DEBUGGER_HASLOGGER + // CLogger logging methods + virtual void DebugLog(const char *fmt, va_list vl); + + virtual void InfoLog(const char *fmt, va_list vl); + + virtual void ErrorLog(const char *fmt, va_list vl); +#endif // DEBUGGER_HASLOGGER + + // + // Formatting/parsing + // + + // TODO - add in bufSize + virtual bool ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data); + + virtual void FormatData(char *str, EFormat format, unsigned dataSize, INT64 data); + + virtual void FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data); + + // + // State loading/saving + // + + virtual bool HasState(); + + virtual bool LoadState(const char *fileName); + + virtual bool SaveState(const char *fileName); + + // + // Public virtual methods for sub-classes to implement + // + + virtual void Attach(); + + virtual void Detach(); + + virtual void Reset(); + + virtual void Poll(); + }; + + // + // Inlined methods + // + + inline bool CDebugger::CheckExit() + { + return m_exit; + } + + inline bool CDebugger::CheckPause() + { + return m_pause; + } +} + +#endif // INCLUDED_DEBUGGER_H +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/DebuggerIO.cpp b/Src/Debugger/DebuggerIO.cpp new file mode 100644 index 0000000..3971e57 --- /dev/null +++ b/Src/Debugger/DebuggerIO.cpp @@ -0,0 +1,191 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * IO.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "CPUDebug.h" +#include "DebuggerIO.h" +#include "Watch.h" + +namespace Debugger +{ + CIO::CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize) : + cpu(ioCPU), name(ioName), group(ioGroup), dataSize(ioDataSize), + watch(NULL), inCount(0), outCount(0), lastIn(0), lastOut(0), last(&lastIn) + { + // + } + + bool CIO::CheckInput(UINT64 data) + { + inCount++; + RecordInput(data); + + // Check watch, if any + return watch != NULL && watch->CheckInput(this, data); + } + + bool CIO::CheckOutput(UINT64 data) + { + outCount++; + RecordOutput(data); + + // Check watch, if any + return watch != NULL && watch->CheckOutput(this, data); + } + + CSimpleWatch *CIO::AddSimpleWatch(bool trigInput, bool trigOutput) + { + CSimpleWatch *sWatch = new CSimpleWatch(cpu, this, trigInput, trigOutput); + cpu->AddWatch(sWatch); + return sWatch; + } + + CCountWatch *CIO::AddCountWatch(bool trigInput, bool trigOutput, unsigned count) + { + CCountWatch *cWatch = new CCountWatch(cpu, this, trigInput, trigOutput, count); + cpu->AddWatch(cWatch); + return cWatch; + } + + CMatchWatch *CIO::AddMatchWatch(bool trigInput, bool trigOutput, std::vector &dataSeq) + { + CMatchWatch *mWatch = new CMatchWatch(cpu, this, trigInput, trigOutput, dataSeq); + cpu->AddWatch(mWatch); + return mWatch; + } + + CPrintWatch *CIO::AddPrintWatch(bool trigInput, bool trigOutput) + { + CPrintWatch *pWatch = new CPrintWatch(cpu, this, trigInput, trigOutput); + cpu->AddWatch(pWatch); + return pWatch; + } + + CCaptureWatch *CIO::AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen) + { + CCaptureWatch *cWatch = new CCaptureWatch(cpu, this, trigInput, trigOutput, maxLen); + cpu->AddWatch(cWatch); + return cWatch; + } + + bool CIO::RemoveWatch() + { + return watch != NULL && cpu->RemoveWatch(watch); + } + + CPortIO::CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum) : + CIO(ioCPU, ioName, ioGroup, ioDataSize), portNum(ioPortNum), label(NULL) + { + // + } + + void CPortIO::AddLabel(const char *pLabel) + { + strncpy(labelStr, pLabel, MAX_LABEL_LENGTH); + labelStr[MAX_LABEL_LENGTH] = '\0'; + label = labelStr; + } + + void CPortIO::RemoveLabel() + { + label = NULL; + } + + void CPortIO::GetLocation(char *str) + { + if (name != NULL) + strcpy(str, name); + else + sprintf(str, "port %u", portNum); + } + + UINT64 CPortIO::Read() + { + UINT64 data = cpu->ReadPort(portNum); + RecordInput(data); + return data; + } + + bool CPortIO::Write(UINT64 data) + { + if (!cpu->WritePort(portNum, data)) + return false; + RecordOutput(data); + return true; + } + + CMappedIO::CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr) : + CIO(ioCPU, ioName, ioGroup, ioDataSize), CAddressRef(ioCPU, ioAddr, ioDataSize) + { + // + } + + bool CMappedIO::CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data) + { + // Take care with input that was not aligned with mapped I/O address and size + UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize); + return CIO::CheckInput(sData); + } + + bool CMappedIO::CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data) + { + // Take care with output that was not aligned with mapped I/O address and size + UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize); + return CIO::CheckOutput(sData); + } + + void CMappedIO::AddLabel(const char *label) + { + CIO::cpu->AddLabel(addr, label); + } + + void CMappedIO::RemoveLabel() + { + CIO::cpu->RemoveLabel(addr); + } + + void CMappedIO::GetLocation(char *str) + { + CIO::cpu->FormatAddress(str, addr); + } + + UINT64 CMappedIO::Read() + { + UINT64 data = CIO::cpu->ReadMem(addr, dataSize); + RecordInput(data); + return data; + } + + bool CMappedIO::Write(UINT64 data) + { + if (!CIO::cpu->WriteMem(addr, dataSize, data)) + return false; + RecordOutput(data); + return true; + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/DebuggerIO.h b/Src/Debugger/DebuggerIO.h new file mode 100644 index 0000000..e19d9b8 --- /dev/null +++ b/Src/Debugger/DebuggerIO.h @@ -0,0 +1,165 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * IO.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_IO_H +#define INCLUDED_IO_H + +#include "Types.h" + +#define MAX_LABEL_LENGTH 255 + +namespace Debugger +{ + class CCPUDebug; + class CWatch; + class CSimpleWatch; + class CCountWatch; + class CMatchWatch; + class CPrintWatch; + class CCaptureWatch; + + /* + * Base class that represents CPU input/output. + */ + class CIO + { + public: + CCPUDebug *cpu; + const char *name; + const char *group; + unsigned dataSize; + + CWatch *watch; + + unsigned inCount; + unsigned outCount; + UINT64 lastIn; + UINT64 lastOut; + UINT64 *last; + + CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize); + + void RecordInput(UINT64 data); + + void RecordOutput(UINT64 data); + + bool CheckInput(UINT64 data); + + bool CheckOutput(UINT64 data); + + virtual void AddLabel(const char *name) = 0; + + virtual void RemoveLabel() = 0; + + CSimpleWatch *AddSimpleWatch(bool trigInput, bool trigOutput); + + CCountWatch *AddCountWatch(bool trigInput, bool trigOutput, unsigned count); + + CMatchWatch *AddMatchWatch(bool trigInput, bool trigOutput, std::vector &dataSeq); + + CPrintWatch *AddPrintWatch(bool trigInput, bool trigOutput); + + CCaptureWatch *AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen); + + bool RemoveWatch(); + + virtual void GetLocation(char *str) = 0; + + virtual UINT64 Read() = 0; + + virtual bool Write(UINT64 data) = 0; + }; + + /* + * Class that represents a CPU input/ouput port. + */ + class CPortIO : public CIO + { + private: + char labelStr[MAX_LABEL_LENGTH + 1]; + + public: + const UINT16 portNum; + + const char *label; + + CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum); + + void AddLabel(const char *pLabel); + + void RemoveLabel(); + + void GetLocation(char *str); + + UINT64 Read(); + + bool Write(UINT64 data); + }; + + /* + * Class that represents CPU input/output that is mapped to a memory location. + */ + class CMappedIO : public CIO, public CAddressRef + { + public: + CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr); + + bool CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data); + + bool CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data); + + void AddLabel(const char *name); + + void RemoveLabel(); + + void GetLocation(char *str); + + UINT64 Read(); + + bool Write(UINT64 data); + }; + + // + // Inlined methods + // + + inline void CIO::RecordInput(UINT64 data) + { + // Keep track of data coming in + lastIn = data; + last = &lastIn; + } + + inline void CIO::RecordOutput(UINT64 data) + { + // Keep track of data going out + lastOut = data; + last = &lastOut; + } +} + +#endif // INCLUDED_IO_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Exception.cpp b/Src/Debugger/Exception.cpp new file mode 100644 index 0000000..0ff0634 --- /dev/null +++ b/Src/Debugger/Exception.cpp @@ -0,0 +1,39 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Exception.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Exception.h" + +namespace Debugger +{ + CException::CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName) : + cpu(exCPU), id(exId), code(exCode), name(exName), trap(false), count(0) + { + // + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Exception.h b/Src/Debugger/Exception.h new file mode 100644 index 0000000..4042ccb --- /dev/null +++ b/Src/Debugger/Exception.h @@ -0,0 +1,55 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Exception.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_EXCEPTION_H +#define INCLUDED_EXCEPTION_H + +#include "Types.h" + +namespace Debugger +{ + class CCPUDebug; + + /* + * Class that represents a CPU exception. + */ + class CException + { + public: + CCPUDebug *cpu; + const char *id; + const UINT16 code; + const char *name; + + bool trap; + unsigned long count; + + CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName); + }; +} + +#endif // INCLUDED_EXCEPTION_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Interrupt.cpp b/Src/Debugger/Interrupt.cpp new file mode 100644 index 0000000..4b67596 --- /dev/null +++ b/Src/Debugger/Interrupt.cpp @@ -0,0 +1,39 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Interrupt.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Interrupt.h" + +namespace Debugger +{ + CInterrupt::CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName) : + cpu(intCPU), id(intId), code(intCode), name(intName), trap(false), count(0) + { + // + } +} + +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Interrupt.h b/Src/Debugger/Interrupt.h new file mode 100644 index 0000000..d77d5a1 --- /dev/null +++ b/Src/Debugger/Interrupt.h @@ -0,0 +1,55 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Interrupt.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_INTERRUPT_H +#define INCLUDED_INTERRUPT_H + +#include "Types.h" + +namespace Debugger +{ + class CCPUDebug; + + /* + * Class that represents a CPU interrupt (just a specialised CPU exception). + */ + class CInterrupt + { + public: + CCPUDebug *cpu; + const char *id; + const UINT16 code; + const char *name; + + bool trap; + unsigned long count; + + CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName); + }; +} + +#endif // INCLUDED_INTERRUPT_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/Label.cpp b/Src/Debugger/Label.cpp new file mode 100644 index 0000000..9da232d --- /dev/null +++ b/Src/Debugger/Label.cpp @@ -0,0 +1,71 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Label.cpp + */ + +#ifdef SUPERMODEL_DEBUGGER + +#include "Label.h" + +#include +#include + +namespace Debugger +{ + CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName) : CAddressRef(lCPU, lAddr) + { + strncpy(nameStr, lName, MAX_LABEL_LENGTH); + nameStr[MAX_LABEL_LENGTH] = '\0'; + name = nameStr; + } + + CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName) : + CAddressRef(lCPU, lAddrStart, lAddrEnd - lAddrStart + 1) + { + strncpy(nameStr, lName, MAX_LABEL_LENGTH); + nameStr[MAX_LABEL_LENGTH] = '\0'; + name = nameStr; + } + + CComment::CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText) : CAddressRef(cCPU, cAddr) + { + size_t size = strlen(cText); + char *textStr = new char[size + 1]; + strncpy(textStr, cText, size); + textStr[size] = '\0'; + text = textStr; + } + + CComment::~CComment() + { + delete text; + } + + CRegion::CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName) : + CLabel(rCPU, rAddrStart, rAddrEnd, rName), isCode(rIsCode), isReadOnly(rIsReadOnly), prevRegion(NULL), nextRegion(NULL) + { + // + } +} + +#endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/Label.h b/Src/Debugger/Label.h new file mode 100644 index 0000000..dd2647d --- /dev/null +++ b/Src/Debugger/Label.h @@ -0,0 +1,85 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel 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 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel 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 Supermodel. If not, see . + **/ + +/* + * Label.h + */ + +#ifdef SUPERMODEL_DEBUGGER +#ifndef INCLUDED_LABEL_H +#define INCLUDED_LABEL_H + +#include "AddressTable.h" +#include "Types.h" + +#define MAX_LABEL_LENGTH 255 + +namespace Debugger +{ + class CCPUDebug; + + /* + * Class that represents a custom label added by user to a particular memory location. + */ + class CLabel : public CAddressRef + { + private: + char nameStr[MAX_LABEL_LENGTH + 1]; + + public: + const char *name; + + CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName); + + CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName); + }; + + /* + * Class that stores a comment added by a user to a particular memory location. + */ + class CComment : public CAddressRef + { + public: + const char *text; + + CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText); + + ~CComment(); + }; + + /* + * Class that represents various memory regions of a CPU, with information about the region. + */ + class CRegion : public CLabel + { + public: + const bool isCode; + const bool isReadOnly; + + CRegion *prevRegion; + CRegion *nextRegion; + + CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName); + }; +} + +#endif // INCLUDED_LABEL_H +#endif // SUPERMODEL_DEBUGGER \ No newline at end of file diff --git a/Src/Debugger/ReadMe.txt b/Src/Debugger/ReadMe.txt new file mode 100644 index 0000000..82d300a --- /dev/null +++ b/Src/Debugger/ReadMe.txt @@ -0,0 +1,396 @@ +Help for Supermodel Console-based Debugger +========================================== + +Relevant Supermodel Command Line Options +---------------------------------------- + + -enter-debugger Enters the debugger at the start of emulation, + + -disable-debugger Completely disables the debugger in emulator. + +At any point whilst running the emulator, execution can be halted and the debugger entered by pressing Alt+B. + +Code Analysis +------------- + +At startup, and whenever new code is encountered, the debugger analyses the program's code in order to work out all +reachable code locations from a given set of entry points (reset address, exception handlers etc). During the analysis, +it keeps track of all valid code locations and this is used to align instructions for CPUs with variable length instruction +sets. It also generates a set of auto-labels that identify places of interest such as sub-routines, jump destinations and +exception handlers etc. Code analysis can be switched off via the 'configure' command if required. + +Debugger Commands +================= + +Execution +--------- + +n next [=1] + + Runs the next single instruction or the next instructions. + +nf nextframe [=1] + + Runs until the next frame or for the next frames. + +s stepover + + Runs the next instruction. If it is a call to a sub-routine then the sub-routine is 'stepped over' (ie control + breaks at the return address of the sub-routine). + +si stepinto + + Runs the next single instruction, entering any sub-routines (ie same as 'next' above). + +so stepout + + Runs until control returns from the current sub-routine or exception/interrupt handler. Note that if an + exception/interrupt occurs whilst stepping out then this will break execution too as control will have left + the sub-routine or handler. + +c continue [] + + Continues running until a break is forced or if specified, until the given address is reached. + +spc setpc + + Sets the PC of the current CPU to the given address. + +CPUs +---- + +lc listcpus + + Lists all the available CPUs with details about them. + +sc switchcpu (|) + + Switches to another CPU with the given name or number. + +dc disablecpu (|) + + Disables debugging for the CPU with the given name or number. Once disabled, it is no longer + possible to switch to or halt execution for that CPU, ie all breakpoints, watches, monitors and traps + are ignored. The current CPU cannot be disabled. + +ec enablecpu (|) + + Enables debugging for the CPU with the given name or number. + +Registers +--------- + +lr listregisters + + Lists all registers for the current CPU and their current values. + +pr printregister + + Prints the current value of the given register for the current CPU. + +sr setregister + + Sets the value of the given register for the current CPU. + +lm listmonitors + + Lists all register monitors for the current CPU with details about them. + +m monitor +am addmonitor + + Adds a register monitor for the register with the given name, causing execution to break whenever the register's value + changes. + +rm removemonitor + + Removes the register monitor with the given name. + +ram removeallmonitors + + Removes all register monitors for the current CPU. + +Exceptions & Interrupts +----------------------- + +le listexceptions + + Lists all known exceptions for the current CPU and details about them. + +li listinterrupts + + Lists all known interrupts for the current CPU and details about them. + +t trap ((e)xception|(i)nterrupt) +at addtrap ((e)xception|(i)nterrupt) + + Adds an exception or interrupt trap for the exception/interrupt with the given identifier. + +rt removetrap ((e)xception|(i)nterrupt) + + Removes an exception or interrupt trap for the exception/interrupt with the given identifier. + +rat removealltraps [(a)ll|(e)xceptions|(i)nterrupts] + + Removes all exception and/or all interrupt traps for the current CPU. If no arguments are supplied, all exception and all + interrupt traps are removed. + +Disassembly, Labels & Comments +------------------------------ + +l list [=last [#=20|]] +ld listdisassembly [=last [#=20|]] + + Lists the disassembled code of the current CPU for the given address range and/or number of instructions. + + If the start address is not supplied, the listing continues on from the last call, or from just before the current PC + address if this is the first call. + If the end address or instruction count is not supplied, then the default of 20 instructions are listed. + + If code analysis is enabled, then this will be used to align the instructions in the disassembly. If code analysis is + off and the current PC address falls within the disassembly address range, then the instructions will line up with this. + Otherwise, instruction alignment will simply begin at the start address. + +ll listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints] + + Lists all labels for the current CPU. The default option is to list all custom labels (user-added) and any auto-labels + (labels generated by code analysis) of interest. The other options allow the display of particular types of auto-labels. + +al addlabel + + Adds a custom label at the given address and with the given name. + +rl removelabel [|] + + Removes a custom label with the given name or at the given address. If no argument is supplied, then it removes the + custom label at the current PC address. + +ral removealllabels + + Removes all custom labels for the current CPU. + +ac addcomment + + Adds a code comment at the given address and with the given text. + +rc removecomment [] + + Removes the code comment at the given address. If no argument is supplied, then it removes the code comment at the + current PC address. + +rac removeallcomments + + Removes all code comments for the current CPU. + +Breakpoints +----------- + +lb listbreakpoints + + Lists all breakpoints for the current CPU with details about them. + +b breakpoint [ [[s)imple|(c)ount |(p)rint)]] +ab addbreakpoint [ [[s)imple|(c)ount |(p)rint)]] + + Adds a breakpoint at the given address. If no arguments are supplied, then it adds a simple breakpoint at the current PC + address. + + Types of breakpoint (default is simple): + (s)imple - if the location is hit, execution always breaks, + (c)ount - if the location is hit the number of times specified by count, execution breaks. + (p)rint - if the location is hit, a message is printed to the console, but control does not break. + +rb removebreakpoint [#|] + + Removes the breakpoint with the given number of at the given address. If no arguments is supplied, then it removes + the breakpoint at the current PC address. + +Memory, I/O & Watches +--------------------- + +ln listregions + + Lists all known memory regions in the current CPU's address space. + +ly listmemory [=last [#=8|]] + + Lists the memory contents of the current CPU for the given address range and/or number of rows. + + If the start address is not supplied, the listing continues on from the last call, or from address 0 if this is the + first call. + If the end address or row count is not supplied, then the default of 8 rows are listed. + +py printmemory[.=b] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + + Prints the current memory value at the given address for the current CPU. If no format is supplied, the default data + format is used. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. + +sy setmemory[.=b] + + Sets the memory value at the given address for the current CPU. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. + +lo listios + + Lists all known I/Os (both mapped I/O addresses and I/O ports) for the current CPU and details about them. + +lw listmemwatches + + Lists all memory watches for the current CPU with details about them. + +w memwatch[.=b] [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] +aw addmemwatch[.=b] [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] + + Adds a read/write memory watch at the given address. + + If (r)ead, (w)rite or (rw)eadwrite are specified then the watch also triggers an event (see below) when the + address is read, written or either read or written, respectively. + If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write at the + given address (details about which are visible when listing memory watches). + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. If the + address is mapped I/O address, then the size is ignored. + + Types of watch (default is simple): + (s)imple - if the watch is triggered, execution always breaks, + (c)ount - if the watch is triggered, execution breaks after the number of times specified by count, + (m)atch - if the watch is triggered, execution breaks if the series of values read/written matches the given + sequence of data (comma separated), + captu(r)e - if the watch is triggered, the value read/written is recorded in a history whose maximum length + is as specified, + (p)rint - if the watch is triggered, a message with the value read/written is printed to the console. + +rw removememwatch (#|) + + Removes the memory watch with the given number or at the given address. + +raw removeallmemwatches + + Removes all memory watches for the current CPU. + +lpw listportwatches + + Lists all I/O port watches for the current CPU with details about them. + +pw portwatch [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]] +apw addportwatch [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]] + + Adds an input/output watch for the given I/O port. + + If (i)nput, (o)utput or (io)nputoutput are specified then the watch also triggers an event (see below) when the + port is read (input), written (output) or either read or written, respectively. + If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write for the + given port (details about which are visible when listing port watches). + + Types of watch (default is simple): + (s)imple - if the watch is triggered, execution always breaks, + (c)ount - if the watch is triggered the number of times specified by count, execution breaks, + (m)atch - if the watch is triggered, execution breaks if the series of values input/output matches the given + sequence of data (comma separated), + captu(r)e - if the watch is triggered, the value inputted/outputted is recorded in a history whose maximum length + is as specified, + (p)rint - if the watch is triggered, a message with the value inputted/outputted is printed to the console. + +rpw removeportwatch (#|) + + Removes the port watch with the given number or for the given port. + +rapw removeallportwatches + + Removes all port watches for the current CPU. + +General +-------- + +p print[.=v] [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + + Prints the given expression as a number in the given format. If no format is supplied, the default data format is used. + Currently the "expression" can just be a number, label or register name. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted depends on the + type of expression. + +cfg configure analysis [(o)n|of(f)] + addrfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + portfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + datafmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + showlabels [(o)n|of(f)] + showopcodes [(o)n|of(f)] + membytesrow [] + + Configures the debugger. + If no arguments are passed, it outputs all the current settings. + If a setting is supplied, it outputs the current value for the given setting. + If both a setting and a value are supplied, it sets the current value for the given setting. + +ls loadstate + + Loads the debugger state (custom labels and code comments) from the given file. + +ss savestate + + Save the debugger state (custom labels and code comments) to the given file. + +h help + + Prints the available commands. + +x exit + + Exits the debugger and emulator. + +Emulator +-------- + +les loademustate + + Loads the emulator state from the given file. + +ses saveemustate + + Saves the emulator state to the given file. + +res resetemu + + Resets the emulator. + +Inputs +------ + +lip listinputs + + Lists all available inputs for the current game. + +pip printinput (|