├── Module.manifest ├── .gitignore ├── images ├── skz.png ├── ghidra_ss.png └── rl78_state.png ├── test_bins └── mcu-firm.bin ├── ghidra_work ├── testfirm.gzf └── testfirm_load.gzf ├── docs └── ISA_programmer_manual.pdf ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── extension.properties ├── data ├── languages │ ├── rl78.pspec │ ├── rl78.ldefs │ ├── rl78.cspec │ └── rl78.slaspec └── build.xml ├── LICENSE ├── gradlew.bat ├── README.md └── gradlew /Module.manifest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sla 2 | test_bins/* 3 | !test_bins/mcu-firm.bin 4 | dist/* 5 | -------------------------------------------------------------------------------- /images/skz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/images/skz.png -------------------------------------------------------------------------------- /images/ghidra_ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/images/ghidra_ss.png -------------------------------------------------------------------------------- /images/rl78_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/images/rl78_state.png -------------------------------------------------------------------------------- /test_bins/mcu-firm.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/test_bins/mcu-firm.bin -------------------------------------------------------------------------------- /ghidra_work/testfirm.gzf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/ghidra_work/testfirm.gzf -------------------------------------------------------------------------------- /docs/ISA_programmer_manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/docs/ISA_programmer_manual.pdf -------------------------------------------------------------------------------- /ghidra_work/testfirm_load.gzf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/ghidra_work/testfirm_load.gzf -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=RL78 MCU Extension 3 | author=hedgeberg 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /data/languages/rl78.pspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /data/languages/rl78.ldefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | Renesas RL78 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 hedgeberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /data/languages/rl78.cspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GHIDRA RL78 2 | =========== 3 | 4 | WIP RL78 implementation for Ghidra SRE. This repo should now be at the point of being at least somewhat usable for reversing RL78. Pull requests and issue submissions would be appreciated. 5 | 6 | ![Screenshot of current disassembly](https://raw.githubusercontent.com/hedgeberg/RL78_sleigh/master/images/rl78_state.png) 7 | 8 | 9 | Setup 10 | ----- 11 | 12 | Follow the setup instructions as explained in [Ghidra_Falcon](https://github.com/Thog/ghidra_falcon), and if you plan to modify any of the files I recommend you go into $(ghidra_root)/Ghidra/Extensions/rl78_sleigh/data and delete the "languages" folder, then symlink to the data/languages folder of whereever you download this repo to. That will allow you to iterate and test in Ghidra without having to go through a loop of uninstall->restart->reinstall->restart->reopen project every time you want to test changes. 13 | 14 | 15 | Status 16 | ------ 17 | 18 | What works: 19 | + Disassembles all code in the test binary, to some degree of accuracy. Has known bugs, but it mostly functions. Sample project is included in the ghidra_work folder. 20 | + Decompiler handles basic flow correctly (but still has a looooooong way to go, switch idioms in particular are messy) 21 | + A lot of auto-discovered memory offsets are good as-is, but some leave a lot to be desired. 22 | 23 | What doesn't work yet: 24 | + Large body of instructions still unimplemented, but none of the most common ones. 25 | + Decompiler output is full of nastiness, as no work has been done on refining this 26 | + Stack and RAM had to be separated, as Ghidra gets confused by the fact that the stack pointer doesn't actually line up with the area being referenced. This may be a more fundamental ghidra issue that needs some modifications to the codebase before a cleaner, more unified memory map can be designed. 27 | 28 | What's on the docket: 29 | + Adding context register functionality to enable register banking. 30 | + Implementing the remainder of the ISA 31 | + Huge amount of cleanup work in the .slaspec file, it's super messy 32 | + Experimenting with a basic loader 33 | + Default memory mappings and register locations 34 | 35 | 36 | FYI 37 | --- 38 | 39 | For those interested in extending this (or just learning about RL78), I've included the Renesas ISA programmer's manual in the "docs" folder. 40 | 41 | 42 | Credits 43 | ------- 44 | 45 | Thanks to [hthh](https://github.com/hthh/), [thog](https://github.com/thog), and [roblabla](https://github.com/roblabla) for their work on [Ghidra_Falcon](https://github.com/Thog/ghidra_falcon), which served as the base workflow for this project (i.e. I stole their repo and build system and just edited a couple files with the exception of the rl78 defintion files). Definitely check through their repo, their debugging workflow that they highlighted there is how I've been debugging this. 46 | -------------------------------------------------------------------------------- /data/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /data/languages/rl78.slaspec: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # 3 | # Attempt at implementing the RL78 4 | # 16-bit MCU ISA in SLEIGH. 5 | # Wish me luck. 6 | # 7 | # Disclaimer: If this disclaimer is 8 | # still present when you obtain this 9 | # file, assume it is non-functional. 10 | # I'm trying my best here. 11 | # 12 | ########################################################################## 13 | 14 | define endian=little; 15 | define alignment=1; 16 | 17 | @define PTRSIZE "3" 18 | 19 | define space ram type=ram_space size=$(PTRSIZE) wordsize=1 default; 20 | define space stackarea type=ram_space size=2; 21 | define space register type=register_space size=1; 22 | 23 | #TODO: mess with a virtual stack register of some sort using context registers? 24 | #TODO: figure out how to cleanly implement register banks 25 | #TODO: refactor bunk register access logic, using pg. 3 of the data sheet as a reference 26 | #TODO: figure out how to use sleigh's carry operation, pretty sure thats available. 27 | #TODO: check literally every single operand namespace, you may have screwed up ordering 28 | 29 | #general purpose registers: 30 | define register offset= 0x00 size=1 [X3 A3 C3 B3 E3 D3 L3 H3]; # 8-bit-mode bank3 31 | define register offset= 0x00 size=2 [AX3 BC3 DE3 HL3]; #16-bit-mode bank3 32 | define register offset= 0x08 size=1 [X2 A2 C2 B2 E2 D2 L2 H2]; # 8-bit-mode bank2 33 | define register offset= 0x08 size=2 [AX2 BC2 DE2 HL2]; #16-bit-mode bank2 34 | define register offset= 0x10 size=1 [X1 A1 C1 B1 E1 D1 L1 H1]; # 8-bit-mode bank1 35 | define register offset= 0x10 size=2 [AX1 BC1 DE1 HL1]; #16-bit-mode bank1 36 | define register offset= 0x18 size=1 [X A C B E D L H]; # 8-bit-mode bank0 37 | define register offset= 0x18 size=2 [AX BC DE HL]; #16-bit-mode bank0 38 | #define register offset= 0x20 size=1 [RB0 RB1 RB2 RB3]; #dummy args for register banks 39 | 40 | #special purpose registers 41 | define register offset=0x40 size=3 [PC]; #I Think this is right??? they don't specify 42 | define register offset=0x50 size=2 [SP _ _ _ ]; 43 | define register offset=0x50 size=1 [SPL SPH PSW _ CS ES PMC MEM]; 44 | define register offset=0x60 size=4 [contextreg _]; # dumb hack to help the context reg dealio 45 | 46 | 47 | define context contextreg 48 | banksel = (0,1) 49 | ; 50 | 51 | 52 | 53 | #TODO: change to using bitranges, once its fixed 54 | #define bitrange IE=PSW[7,1] Z=PSW[6,1] RBS1=PSW[5,1] AC=PSW[4,1] RBS0=PSW[3,1] 55 | # ISP1=PSW[2,1] ISP0=PSW[1,1] CY=PSW[0,1]; 56 | 57 | #Flag bits 58 | #Macros? I think? to make accessing the bits in the PSW easier 59 | #TODO: convert elements of the PSW to use the bitrange feature 60 | @define IE_flag "PSW[7,1]" # Interrupt Enable (IE) 61 | @define Z_flag "PSW[6,1]" # Zero Flag (Z) 62 | @define RBS1_flag "PSW[5,1]" # Register Bank Select Hi-Bit (RBS1) 63 | @define AC_flag "PSW[4,1]" # Auxiliary Carry Flag (AC) 64 | @define RBS0_flag "PSW[3,1]" # Register Bank Select Lo-Bit (RBS0) 65 | @define ISP1_flag "PSW[2,1]" # In-service priority flag Hi-Bit (ISP1) 66 | @define ISP0_flag "PSW[1,1]" # In-service priority flag Lo-Bit (ISP0) 67 | @define CY_flag "PSW[0,1]" # Carry flag (CY) 68 | 69 | ######################################################################### 70 | # 71 | # Pseudocode ops 72 | # 73 | ######################################################################### 74 | 75 | #define pcodeop skip; 76 | define pcodeop todo; 77 | define pcodeop halt; 78 | define pcodeop stop; 79 | define pcodeop invalid; 80 | 81 | ######################################################################### 82 | # 83 | # Operand data types 84 | # 85 | # FYI: You can think of sleigh tokens as both bitfields and parser 86 | # directives. Basically, a token is any element that is a potential 87 | # sub-field you may want to parse out of an instruction, and the 88 | # attach keyword is used to turn those parsed fields into 89 | # elements of a register map cleanly. 90 | # 91 | ######################################################################### 92 | 93 | define token op1b (8) 94 | op1b_byte = (0,7) 95 | 96 | op1b_hi = (4,7) 97 | op1b_lo = (0,3) 98 | op1b_lo3 = (0,2) 99 | 100 | op1b_reg0 = (0,2) 101 | op1b_reg_4_5 = (4,5) 102 | op1b_reg0_dupe = (0,2) 103 | op1b_2xreg = (1,2) 104 | op1b_2xreg_dupe = (1,2) #duplicate to check val 105 | op1b_reg_dehl = (1,1) #special namespace for mov operations 106 | #which are limited to DE and HL r/w. 107 | #This includes all mem load/store ops. 108 | op1b_reg_axbc = (0,0) #special namespace for clrw & co, only uses ax/bc 109 | op1b_movw_rp = (4,5) 110 | op1b_movw_rp_dupe = (4,5) 111 | op1b_reg_rp = (1,2) 112 | op1b_reg_rp_dupe = (1,2) 113 | op1b_movr = (4,5) 114 | op1b_movr_dupe = (4,5) 115 | op1b_setb_reg0 = (0,1) #special namespace for ONEB/CLRB 116 | 117 | op1b_b7 = (7,7) 118 | op1b_b6 = (6,6) 119 | op1b_b5 = (5,5) 120 | op1b_b4 = (4,4) 121 | op1b_b3 = (3,3) 122 | op1b_b2 = (2,2) 123 | op1b_b1 = (1,1) 124 | op1b_b0 = (0,0) 125 | ; 126 | 127 | define token op2b (8) 128 | op2b_byte = (0,7) 129 | op2b_hi = (4,7) 130 | op2b_hi_val = (4,7) #duplicate to check val without causing issues 131 | op2b_lo = (0,3) 132 | op2b_sfr = (0,2) 133 | op2b_sfrp = (0,1) 134 | 135 | op2b_b7 = (7,7) 136 | op2b_b6 = (6,6) 137 | op2b_b3 = (3,3) 138 | op2b_b2 = (2,2) 139 | 140 | op2b_reg0 = (0,2) 141 | op2b_reg0_dupe = (0,2) 142 | op2b_reg_rbsel = (4,5) 143 | 144 | op2b_reg_rp = (4,5) 145 | 146 | op2b_hi2 = (6,7) 147 | op2b_bit_sel = (4,6) 148 | ; 149 | 150 | define token calltref (8) 151 | calltref_b7 = (7,7) 152 | calltref_hi = (4,6) 153 | calltref_b3 = (3,3) 154 | calltref_b2 = (2,2) 155 | calltref_lo = (0,1) 156 | ; 157 | 158 | define token op3b (8) 159 | op3b_byte = (0,7) 160 | op3b_sfr = (0,2) 161 | op3b_hi = (4,7) 162 | op3b_lo = (0,3) 163 | op3b_b3 = (3,3) 164 | ; 165 | 166 | define token esbyte (8) 167 | esmarker = (0,7) 168 | ; 169 | 170 | define token data8 (8) 171 | uadr8 = (0,7) 172 | adr8_dupe = (0,7) 173 | disp8 = (0,7) signed 174 | adr8 = (0,7) signed 175 | imm8 = (0,7) 176 | simm8 = (0,7) signed 177 | ; 178 | 179 | define token data16 (16) 180 | disp16 = (0,15) signed 181 | imm16 = (0,15) 182 | imm16_lo = (0,7) 183 | imm16_hi = (8,15) 184 | simm16 = (0,15) signed 185 | ; 186 | 187 | define token data24 (24) 188 | imm24 = (0,23) 189 | ; 190 | 191 | attach variables [op1b_reg0 op2b_reg0] [X A C B E D L H]; 192 | attach variables [op1b_2xreg op2b_reg_rp] [AX BC DE HL]; 193 | attach variables [op2b_sfr op3b_sfr] [SPL SPH PSW _ CS ES PMC MEM]; 194 | attach variables [op1b_reg_dehl] [DE HL]; 195 | attach variables [op1b_reg_axbc] [AX BC]; 196 | attach variables [op2b_sfrp] [SP _ _ _ ]; 197 | attach variables [op1b_reg_4_5] [_ B C _]; 198 | attach variables [op1b_reg_rp] [_ DE HL _]; 199 | attach variables [op1b_movw_rp] [_ BC DE HL]; 200 | attach variables [op1b_movr] [_ X B C]; 201 | attach variables [op1b_setb_reg0] [X A C B]; 202 | 203 | 204 | ########################################################################## 205 | # 206 | # Macros 207 | # 208 | ########################################################################## 209 | 210 | #macro push16(val16) { 211 | # SP = SP - 2; 212 | # ptr:$(PTRSIZE) = SP; 213 | # *:2 ptr = val16; 214 | #} 215 | 216 | 217 | 218 | macro addw_set_flags(val1, val2){ 219 | local tmp1:2; 220 | local tmp2:2; 221 | tmp1 = (val1>>15)&0x1; 222 | tmp2 = (val2>>15)&0x1; 223 | $(CY_flag) = tmp1[0,1] & tmp2[0,1]; 224 | $(Z_flag) = (val1 == (-val2)); 225 | $(AC_flag) = 0; 226 | } 227 | 228 | macro add_set_flags(val1, val2){ 229 | $(CY_flag) = ((val1>>7)&0x1) & ((val2>>7)&0x1); 230 | $(Z_flag) = (val1 == (-val2)); 231 | local tmp:1 = 0x0; 232 | tmp = val1[0,3] + val2[0,3]; 233 | tmp = (tmp >> 3) & 0x1; 234 | $(AC_flag) = tmp; 235 | } 236 | 237 | macro sr_set_flags16(reg, imm){ 238 | local tmp:2 = (reg >> imm) & 0x1:2; 239 | local tmp2:1 = tmp[0,7]; 240 | $(CY_flag) = tmp2; 241 | } 242 | 243 | macro sr_set_flags8(reg, imm){ 244 | local tmp:1 = (reg >> imm) & 0x1:1; 245 | $(CY_flag) = tmp; 246 | } 247 | 248 | macro read_byte(dst, addr){ 249 | ptr:$(PTRSIZE) = 0x0; 250 | ptr[0, 16] = addr[0,16]; 251 | ptr[16, 4] = 0xF; 252 | dst = *:1 ptr; 253 | } 254 | 255 | macro read_stack_byte(dst, addr){ 256 | ptr:$(PTRSIZE) = 0x0; 257 | ptr[0, 16] = addr[0,16]; 258 | ptr[16, 4] = 0xF; 259 | dst = *:1 ptr; 260 | } 261 | 262 | macro read_es_byte(dst, addr){ 263 | ptr:$(PTRSIZE) = 0x0; 264 | ptr[0, 16] = ptr[0, 16] | addr; 265 | ptr[16, 4] = ptr[16, 4] | ES[0, 4]; 266 | dst = *:1 ptr; 267 | } 268 | 269 | macro write_byte(src, addr){ 270 | ptr:$(PTRSIZE) = 0x0F0000; 271 | ptr[0,16] = ptr[0,16] | addr; 272 | *:1 ptr = src; 273 | } 274 | 275 | macro write_stack_byte(src, addr){ 276 | local ptr:$(PTRSIZE) = 0x0F0000; 277 | ptr[0, 16] = addr[0,16]; 278 | *:1 ptr = src; 279 | } 280 | 281 | #macro write_es_byte(src, addr){} 282 | 283 | #TODO: check if base for mem reads is 0xF or 0x0, 284 | # datasheet and ODA seem to disagree. 285 | macro read_word(dst, addr){ 286 | local dst_lo:2 = 0x0; 287 | local dst_hi:2 = 0x0; 288 | ptr:$(PTRSIZE) = 0x0; 289 | ptr[0, 16] = addr[0,16]; 290 | ptr[16, 4] = 0xF; 291 | dst_lo = *:1 ptr; 292 | ptr = ptr + 1; 293 | dst_hi = *:1 ptr; 294 | dst = ((dst_hi << 8)| dst_lo); 295 | } 296 | 297 | macro write_word(src, addr){ 298 | local src_lo:1 = 0x0; 299 | local src_hi:1 = 0x0; 300 | ptr:$(PTRSIZE) = 0x0; 301 | ptr[0, 16] = addr[0,16]; 302 | ptr[16, 4] = 0xF; 303 | src_lo = src[0,8]; 304 | src_hi = src[8,8]; 305 | *:1 ptr = src_lo; 306 | ptr = ptr + 1; 307 | *:1 ptr = src_hi; 308 | } 309 | 310 | macro write_stack_word(src, addr){ 311 | #TODO: replace all write_stack_word uses with push_word uses 312 | local src_lo:1 = 0x0; 313 | local src_hi:1 = 0x0; 314 | ptr:$(PTRSIZE) = 0x0; 315 | ptr[0, 16] = addr[0,16]; 316 | ptr[16, 4] = 0xF; 317 | src_lo = src[0,8]; 318 | src_hi = src[8,8]; 319 | *:1 ptr = src_lo; 320 | ptr = ptr + 1; 321 | *:1 ptr = src_hi; 322 | } 323 | 324 | macro push(src){ 325 | SP = SP - 2; 326 | *[stackarea]:2 SP = src; 327 | } 328 | 329 | macro pop(dst){ 330 | dst = *[stackarea]:2 SP; 331 | SP = SP + 2; 332 | } 333 | 334 | macro pushpc(pc, inter){ 335 | SP = SP - 3; 336 | *[stackarea]:3 SP = pc:3; 337 | SP = SP - 1; 338 | *[stackarea]:1 SP = inter:1; 339 | } 340 | 341 | macro poppc(pc, inter){ 342 | inter = *[stackarea]:1 SP; 343 | SP = SP + 1; 344 | pc = *[stackarea]:3 SP; 345 | SP = SP + 3; 346 | } 347 | 348 | macro push_word(src){ 349 | SP = SP - 2; 350 | ptr:$(PTRSIZE) = 0x0; 351 | ptr[0, 16] = SP; 352 | ptr[16, 4] = 0xF; 353 | *:2 ptr = SP; 354 | } 355 | 356 | 357 | macro read_stack_word(dst, addr){ 358 | local dst_lo:2 = 0x0; 359 | local dst_hi:2 = 0x0; 360 | ptr:$(PTRSIZE) = 0x0; 361 | ptr[0, 16] = addr; 362 | ptr[16, 4] = 0xF; 363 | dst_lo = *:1 ptr; 364 | ptr = ptr + 1; 365 | dst_hi = *:1 ptr; 366 | dst = ((dst_hi << 8)| dst_lo); 367 | } 368 | 369 | macro pop_word(dst){ 370 | #TODO: replace all read_stack_word uses with pop_word 371 | ptr:$(PTRSIZE) = 0x0; 372 | ptr[0, 16] = SP; 373 | ptr[16, 4] = 0xF; 374 | dst = *:2 ptr; 375 | SP = SP + 2; 376 | } 377 | 378 | macro cmp8_set_flags(arg1, arg2){ 379 | $(Z_flag) = ((arg2 - arg1) == 0); 380 | $(CY_flag) = ((arg2 & 0x7f) < (arg1 & 0x7f)); 381 | $(AC_flag) = ((arg2 & 0x07) < (arg1 & 0x07)); 382 | } 383 | 384 | macro cmpw_set_flags(arg1, arg2){ 385 | $(Z_flag) = (arg2 == arg1); 386 | } 387 | 388 | macro subw_set_flags(dst, src){ 389 | $(AC_flag) = 0; 390 | $(Z_flag) = (dst == src); 391 | $(CY_flag) = (dst < src); 392 | } 393 | 394 | macro bit_set_to(val, bit_index, set_to){ 395 | local mask = ~(0x1 << bit_index); 396 | val = val & mask; 397 | val = val|(set_to<>bit_index); 402 | dst = (tmp & 0x1); 403 | } 404 | 405 | #TODO: can I turn the 2 macro's below into 1? don't see why not? 406 | macro reg_stackarea_offset8(saptr, ptr_reg, adr){ 407 | saptr = 0x0F0000; 408 | local tmp:3 = saptr | zext(ptr_reg); 409 | saptr = tmp + adr; 410 | } 411 | 412 | macro reg_stackarea_offset16(saptr, ptr_reg, adr){ 413 | saptr = 0x0F0000; 414 | local tmp:3 = saptr | zext(ptr_reg); 415 | saptr = tmp + adr; 416 | } 417 | 418 | #TODO: fix up all the mangled references 419 | # This was based off the z80 slaspec, but they screwed up the handling real bad. 420 | # Export *: doesn't export a pointer, it exports a reference which ghiddra converts 421 | # into a pointer down the road, and the z80 guys didnt seem to get that. add 422 | # TODO: replace all addr16 with add16_1x or add16_2x 423 | RelAddr8: loc is simm8 [ loc = inst_next + simm8; ] 424 | { export *:1 loc; } 425 | #{ export *:$(PTRSIZE) loc; } 426 | 427 | jdisp16: loc is disp16 [ loc = inst_next + disp16; ] 428 | { export *:1 loc; } 429 | 430 | addr16: loc is imm16 [ loc = 0xf0000 + imm16;] 431 | { export *:$(PTRSIZE) loc; } 432 | 433 | addr16_1x: loc is imm16 [ loc = 0xf0000 + imm16;] 434 | { export *:1 loc; } 435 | 436 | addr16_2x: loc is imm16 [ loc = 0xf0000 + imm16;] 437 | { export *:2 loc;} 438 | 439 | jaddr16: loc is imm16 [ loc = 0x00000 | imm16;] 440 | { export *:1 loc; } 441 | 442 | jaddr20: loc is imm24 [ loc = imm24&0x0FFFFF; ] 443 | { export *:1 loc; } 444 | 445 | callt_addr: ["vector_table:"adr] is calltref_b7=1 & calltref_hi & calltref_b3=0 & calltref_b2=1 & calltref_lo 446 | [ 447 | adr = 0x80 | ((calltref_hi<<1) | (calltref_lo<<4)); 448 | ] 449 | { 450 | local newadr:3 = 0x0:3 | zext(adr:1); 451 | local table_deref:2 = *:2 newadr; 452 | local jmptarget:$(PTRSIZE) = zext(table_deref); 453 | export *:1 jmptarget; 454 | } 455 | 456 | #also called saddr in data sheet? wait no i think i was wrong -- need to fix this 457 | #TODO: separate sfr and sadr calls 458 | #TODO: replace messy sfr/sfrp invocations 459 | sfr: op2b_sfr is op2b_hi=0xF & op2b_b3=1 & op2b_sfr { export op2b_sfr; } 460 | 461 | sfrp: op2b_sfrp is op2b_hi=0xF & op2b_b3=1 & op2b_b2=0 & op2b_sfrp { export op2b_sfrp; } 462 | 463 | sfr_raw1: "sfr:"loc is uadr8 [loc = 0x0FFF00 + uadr8; ] 464 | { export *[ram]:1 loc; } 465 | 466 | sfr_raw2: "sfr:"loc is uadr8 [ loc = 0x0FFF00 + uadr8; ] 467 | { export *[ram]:2 loc; } 468 | 469 | 470 | #TODO: fix the range for which saddr/saddrp is accessible. See G13 docs, page 158 471 | saddr: loc is uadr8 [ loc = 0xFFF00 + uadr8; ] 472 | { export *[ram]:1 loc; } 473 | 474 | saddrp: loc is uadr8 [ loc = 0xFFF00 + uadr8; ] 475 | { export *[ram]:2 loc; } 476 | 477 | #TODO: add prefix operand to remove need for esmarker=0x11 478 | #TODO: replace all [HL+x] calls with hl_offset 479 | #TODO: figure out how to clean these up so that ghidra is less weird about offsets during disasm. 480 | #TODO: check if you screwed up with the offsets. seemm better now, but still need quadruple checking 481 | 482 | #TODO: collapse # of argument definitions using context registers? 483 | es_hl_b_byte: ES:[HL+B] is HL & B & ES 484 | { local ptr = 0:3; 485 | ptr[0,16] = HL + sext(B); 486 | ptr[16,4] = ES[0,4]; 487 | export *:1 ptr; } 488 | 489 | es_addr16_byte: ES:!imm16 is imm16 & ES 490 | { local ptr = 0:3; 491 | ptr[0,16] = imm16; 492 | ptr[16,4] = ES[0,4]; 493 | export *:1 ptr; } 494 | 495 | es_addr16_word: ES:!imm16 is imm16 & ES 496 | { local ptr = 0:3; 497 | ptr[0,16] = imm16; 498 | ptr[16,4] = ES[0,4]; 499 | export *:2 ptr; } 500 | 501 | es_hl_ofst_byte: ES:[HL+adr8] is adr8 & ES & HL 502 | { local ptr:$(PTRSIZE) = 0x0; 503 | ptr[16,4] = ES[0,4]; 504 | ptr[0,16] = HL; 505 | ptr = ptr + sext(adr8:1); 506 | export *:1 ptr; } 507 | 508 | hl_ptr_byte: [HL] is HL 509 | { local ptr:$(PTRSIZE) = 0xF0000; 510 | ptr = ptr | zext(HL); 511 | export *:1 ptr;} 512 | 513 | hl_ptr_word: [HL] is HL 514 | { local ptr:$(PTRSIZE) = 0xF0000; 515 | ptr = ptr | zext(HL); 516 | export *:2 ptr;} 517 | 518 | hl_offset: [HL+disp8] is disp8 & HL 519 | { local tmp:2 = HL + sext(disp8:1); 520 | local loc:3 = 0xF0000:3 | zext(tmp); 521 | export *[ram]:1 loc; } 522 | 523 | hl_offset_word: [HL+disp8] is disp8 & HL 524 | { local tmp:2 = HL + sext(disp8:1); 525 | local loc:3 = 0xF0000:3 | zext(tmp); 526 | export *[ram]:2 loc; } 527 | 528 | b_offset_byte: disp16[B] is disp16 & B 529 | { local tmp:2 = disp16 + sext(B); 530 | local loc:3 = 0xF0000:3 | zext(tmp); 531 | export *[ram]:1 loc; } 532 | 533 | b_offset_word: disp16[B] is disp16 & B 534 | { local tmp:2 = disp16 + sext(B); 535 | local loc:3 = 0xF0000:3 | zext(tmp); 536 | export *[ram]:2 loc; } 537 | 538 | c_offset_byte: disp16[C] is disp16 & C 539 | { local tmp:2 = disp16 + sext(C); 540 | local loc:3 = 0xF0000:3 | zext(tmp); 541 | export *[ram]:1 loc; } 542 | 543 | c_offset_word: disp16[C] is disp16 & C 544 | { local tmp:2 = disp16 + sext(C); 545 | local loc:3 = 0xF0000:3 | zext(tmp); 546 | export *[ram]:2 loc; } 547 | 548 | bc_offset_word: disp16[BC] is disp16 & BC 549 | { local tmp:2 = BC + disp16; 550 | local loc:3 = 0xF0000:3 | zext(tmp); 551 | export *[ram]:2 loc; } 552 | #{ local tmp:3 = 0xF0000:3 + zext(BC:2); 553 | # local loc:3 = tmp + sext(disp16:2); 554 | # export *[ram]:2 loc;} 555 | 556 | sp_offset_byte: [SP+disp8] is disp8 & SP 557 | { local loc:2 = sext(disp8:1) + SP; 558 | export *[stackarea]:1 loc; 559 | } 560 | 561 | sp_offset_word: [SP+disp8] is disp8 & SP 562 | { local loc:2 = sext(disp8:1) + SP; 563 | export *[stackarea]:2 loc; 564 | } 565 | 566 | ########################################################################## 567 | # 568 | # Instructions 569 | # 570 | ########################################################################## 571 | 572 | #:MOV 573 | #TODO: The rest of MOV 574 | :MOV op1b_reg0," #"imm8 is op1b_hi=0x5 & op1b_b3!=0x1 & op1b_reg0; imm8 { 575 | op1b_reg0 = imm8; 576 | } 577 | 578 | :MOV A,op1b_reg0 is op1b_hi=0x6 & op1b_b3!=0x1 & op1b_reg0 & op1b_lo3!=0x1 & A { 579 | A = op1b_reg0; 580 | } 581 | 582 | :MOV op1b_reg0,A is op1b_hi=0x7 & op1b_b3!=0x1 & op1b_reg0 & op1b_lo3!=0x1 & A { 583 | op1b_reg0 = A; 584 | } 585 | 586 | :MOV op2b_sfr,A is op1b_byte=0x9E; op2b_hi=0xF & op2b_b3=0x1 & op2b_sfr & A { 587 | op2b_sfr = A; 588 | } 589 | 590 | :MOV op2b_sfr,"#"imm8 is op1b_byte=0xCE; op2b_hi=0xF & op2b_b3=0x1 & op2b_sfr; imm8 591 | { 592 | op2b_sfr = imm8; 593 | } 594 | 595 | :MOV sfr_raw1,"#"imm8 is op1b_byte=0xCE; sfr_raw1 & adr8_dupe<0xF0; imm8 596 | { 597 | sfr_raw1 = imm8; 598 | } 599 | 600 | :MOV sfr_raw1,"#"imm8 is op1b_byte=0xCD; sfr_raw1; imm8 601 | { 602 | sfr_raw1 = imm8; 603 | } 604 | 605 | 606 | :MOV [op1b_reg_dehl],A is op1b_hi=0x9 & op1b_b3=1 & op1b_b2=0 & op1b_reg_dehl & op1b_b0=1 & A 607 | { 608 | write_byte(A, op1b_reg_dehl); 609 | } 610 | 611 | :MOV A,"["op1b_reg_dehl"]" is op1b_hi=0x8 612 | & op1b_b3=1 & op1b_b2=0 & op1b_reg_dehl & op1b_b0=1 & A 613 | { 614 | read_byte(A, op1b_reg_dehl); 615 | } 616 | 617 | #todo: confirm that offset is calculated correctly here. seems like its not. 618 | :MOV A,ES":["op1b_reg_dehl"]" is esmarker=0x11; 619 | op1b_hi=0x8 & op1b_b3=1 & op1b_b2=0 & op1b_reg_dehl & op1b_b0=1 620 | & A & ES 621 | { 622 | read_es_byte(A, op1b_reg_dehl); 623 | } 624 | 625 | :MOV ES,"#"imm8 is op1b_byte=0x41; imm8 & ES 626 | { 627 | ES = imm8; 628 | } 629 | 630 | :MOV A,imm16[BC] is op1b_byte=0x49; imm16 & A & BC { 631 | addr:2 = BC + imm16; 632 | read_byte(A, addr); 633 | } 634 | 635 | :MOV [op1b_reg_rp+adr8],"#"imm8 is op1b_hi=0xC & op1b_b3=1 & op1b_reg_rp & op1b_b0=0; adr8; imm8 636 | { 637 | local ptr:$(PTRSIZE) = 0x000000; 638 | reg_stackarea_offset8(ptr, op1b_reg_rp, adr8); 639 | *:1 ptr = imm8; 640 | } 641 | 642 | :MOV [op1b_reg_rp+adr8],A is op1b_hi=0x9 & op1b_b3=1 & op1b_reg_rp & op1b_b0=0; adr8 & A 643 | { 644 | local ptr:$(PTRSIZE) = 0x0; 645 | reg_stackarea_offset8(ptr, op1b_reg_rp, adr8); 646 | *:1 ptr = A; 647 | } 648 | 649 | :MOV A,[op1b_reg_rp+adr8] is op1b_hi=0x8 & op1b_b3=1 & op1b_reg_rp & op1b_b0=0 & (op1b_reg_rp_dupe=1 | op1b_reg_rp_dupe=2); adr8 & A 650 | { 651 | local ptr:$(PTRSIZE) = 0x0; 652 | reg_stackarea_offset8(ptr, op1b_reg_rp, adr8); 653 | A = *:1 ptr; 654 | } 655 | 656 | :MOV A,sfr_raw1 is op1b_byte=0x8E; sfr_raw1 & A 657 | { 658 | A = sfr_raw1; 659 | } 660 | 661 | :MOV sfr_raw1,A is op1b_byte=0x9E; sfr_raw1 & A 662 | { 663 | sfr_raw1 = A; 664 | } 665 | 666 | :MOV A,!addr16_1x is op1b_byte=0x8F; addr16_1x & A 667 | { 668 | A = addr16_1x; 669 | } 670 | 671 | :MOV !addr16, A is op1b_byte=0x9F; addr16 & A 672 | { 673 | *:1 addr16 = A; 674 | } 675 | 676 | :MOV !addr16_1x,"#"imm8 is op1b_byte=0xCF; addr16_1x; imm8 677 | { 678 | addr16_1x = imm8; 679 | } 680 | 681 | :MOV op1b_movr,!addr16_1x is op1b_b7=1 & op1b_b6=1 & op1b_movr & op1b_lo=0x9 & 682 | op1b_movr_dupe!=0; addr16_1x 683 | { 684 | op1b_movr = addr16_1x; 685 | } 686 | 687 | :MOV op1b_movr,es_addr16_byte is esmarker=0x11; op1b_b7=1 & op1b_b6=1 & op1b_movr & op1b_lo=0x9 & 688 | op1b_movr_dupe!=0; es_addr16_byte 689 | { 690 | op1b_movr = es_addr16_byte; 691 | } 692 | 693 | 694 | :MOV op1b_movr,sfr_raw1 is op1b_b7=1 & op1b_b6=1 & op1b_movr & op1b_lo=0x8 & 695 | op1b_movr_dupe!=0; sfr_raw1 696 | { 697 | op1b_movr = sfr_raw1; 698 | } 699 | 700 | :MOV A,saddr is op1b_byte=0x8D; saddr & A 701 | { 702 | A=saddr; 703 | } 704 | 705 | :MOV saddr,A is op1b_byte=0x9D; saddr & A 706 | { 707 | saddr=A; 708 | } 709 | 710 | :MOV b_offset_byte,"#"imm8 is op1b_byte=0x19; b_offset_byte; imm8 711 | { 712 | b_offset_byte=imm8; 713 | } 714 | 715 | :MOV A,b_offset_byte is op1b_byte=0x09; b_offset_byte & A 716 | { 717 | A = b_offset_byte; 718 | } 719 | 720 | :MOV b_offset_byte,A is op1b_byte=0x18; b_offset_byte & A 721 | { 722 | b_offset_byte = A; 723 | } 724 | 725 | :MOV sp_offset_byte,A is op1b_byte=0x98; sp_offset_byte & A 726 | { 727 | sp_offset_byte = A; 728 | } 729 | 730 | :MOV A,sp_offset_byte is op1b_byte=0x88; sp_offset_byte & A 731 | { 732 | A=sp_offset_byte; 733 | } 734 | 735 | :MOV c_offset_byte,"#"imm8 is op1b_byte=0x38; c_offset_byte; imm8 736 | { 737 | c_offset_byte=imm8; 738 | } 739 | 740 | :MOV A,es_addr16_byte is esmarker=0x11; op1b_byte=0x8F; es_addr16_byte & A 741 | { 742 | A = es_addr16_byte; 743 | } 744 | 745 | :MOV A,es_hl_b_byte is esmarker=0x11; op1b_byte=0x61; op2b_byte=0xC9 & A & es_hl_b_byte 746 | { 747 | A = es_hl_b_byte; 748 | } 749 | 750 | :MOV c_offset_byte,A is op1b_byte=0x28; c_offset_byte & A 751 | { 752 | c_offset_byte=A; 753 | } 754 | 755 | #:TODO: * 756 | #:XCH 757 | # XCH A, X 758 | macro inst_xch(dst, src){ 759 | local tmp = dst; 760 | dst = src; 761 | src = tmp; 762 | } 763 | 764 | :XCH A,X is op1b_byte=0x08 & A & X 765 | { 766 | inst_xch(A, X); 767 | } 768 | 769 | :XCH A,op2b_reg0 is op1b_byte=0x61; op2b_reg0 & op2b_reg0_dupe>1 & A 770 | { 771 | inst_xch(A, op2b_reg0); 772 | } 773 | 774 | #:ONEB 775 | #TODO: etc 776 | #DONE: ONEB !addr16 777 | # oneb, xacb 778 | # ONEB saddr 779 | :ONEB !addr16_1x is op1b_byte=0xE5; addr16_1x 780 | { 781 | addr16_1x = 0x1:1; 782 | } 783 | 784 | :ONEB op1b_setb_reg0 is op1b_hi=0xE & op1b_b3=0 & op1b_b2=0 & op1b_setb_reg0 785 | { 786 | op1b_setb_reg0 = 0x1:1; 787 | } 788 | 789 | :ONEB saddr is op1b_byte=0xE4; saddr 790 | { 791 | saddr = 0x1:1; 792 | } 793 | 794 | #:CLRB 795 | #TODO: CLRB ES:!addr16 796 | :CLRB !addr16_1x is op1b_byte=0xF5; addr16_1x 797 | { 798 | addr16_1x = 0x00; 799 | } 800 | 801 | :CLRB saddr is op1b_byte=0xF4; saddr 802 | { 803 | saddr = 0x00; 804 | } 805 | 806 | 807 | :CLRB op1b_setb_reg0 is op1b_hi=0xF & op1b_b3=0 & op1b_b2=0 & op1b_setb_reg0 808 | { 809 | op1b_setb_reg0 = 0x00; 810 | } 811 | 812 | #:MOVS 813 | 814 | #:MOVW 815 | #TODO: The rest of MOVW 816 | # we've done: movw imm16 into reg pair 817 | # movw from regpair to AX 818 | # movw from AX to regpair 819 | # movw sfrp,#word 820 | # movw addr16,ax 821 | # movw ax,addr16 822 | # movw imm16[r], ax 823 | # movw ax, 1mm16[r] 824 | # movw rp, !imm16 825 | # movw [dehl], ax 826 | # movw ax, [dehl] 827 | # movw sfrp, #word 828 | # movw AX, [hl+byte] 829 | # movw [hl+byte], AX 830 | # movw sfrp, AX 831 | # movw AX, sfrp 832 | # movw ax, saddrp 833 | # movw saddrp, AX 834 | # movw bc/de/hl, saddrp 835 | # movw imm16[rp], ax 836 | # movw ax, imm16[rp] 837 | # movw saddrp, word 838 | # movw AX, [SP+addr8] 839 | # movw [SP+addr8], AX 840 | # movw HL, ES:!addr16 841 | :MOVW op1b_2xreg," #"imm16 is op1b_hi=0x3 & op1b_b3=0 & op1b_2xreg & op1b_b0=0; imm16{ 842 | op1b_2xreg = imm16; 843 | } 844 | 845 | :MOVW AX,op1b_2xreg is op1b_hi=0x1 & op1b_b3=0 & op1b_2xreg & op1b_b0=1 & op1b_2xreg_dupe!=0x0 & AX { 846 | AX = op1b_2xreg; 847 | } 848 | 849 | :MOVW op1b_2xreg,AX is op1b_hi=0x1 & op1b_b3=0 & op1b_2xreg & op1b_b0=0 & op1b_2xreg_dupe!=0x0 & AX { 850 | op1b_2xreg = AX; 851 | } 852 | 853 | :MOVW op2b_sfrp,"#"imm16 is op1b_byte=0xCB; op2b_hi=0xF & op2b_b3=1 & op2b_b2=0 & op2b_sfrp; imm16 854 | { 855 | op2b_sfrp=imm16; 856 | } 857 | 858 | :MOVW AX,!addr16 is op1b_byte=0xAF; addr16 & AX 859 | { 860 | AX = *:2 addr16; 861 | } 862 | 863 | :MOVW !addr16_2x,AX is op1b_byte=0xBF; addr16_2x & AX 864 | { 865 | addr16_2x = AX; 866 | } 867 | 868 | #TODO: can we combine b_offset_word and c_offset_word into 1 arg to reduce instruction definition count? 869 | :MOVW b_offset_word,AX is op1b_byte=0x58; b_offset_word & AX 870 | { 871 | b_offset_word = AX; 872 | } 873 | 874 | :MOVW c_offset_word,AX is op1b_byte=0x68; c_offset_word & AX 875 | { 876 | c_offset_word = AX; 877 | } 878 | 879 | :MOVW AX,b_offset_word is op1b_byte=0x59; b_offset_word & AX 880 | { 881 | AX = b_offset_word; 882 | } 883 | 884 | :MOVW AX,c_offset_word is op1b_byte=0x69; c_offset_word & AX 885 | { 886 | AX = c_offset_word; 887 | } 888 | 889 | :MOVW op1b_movw_rp,!addr16 is op1b_b7=1 & op1b_b6=1 & op1b_movw_rp & op1b_lo=0xB & op1b_movw_rp_dupe!=0; addr16 890 | { 891 | op1b_movw_rp = *:2 addr16; 892 | } 893 | 894 | :MOVW AX,[op1b_reg_dehl] is op1b_hi=0xA & op1b_b3=1 & op1b_b2=0 & op1b_reg_dehl & op1b_b0=1 & AX 895 | { 896 | read_word(AX, op1b_reg_dehl); 897 | } 898 | 899 | :MOVW [op1b_reg_dehl],AX is op1b_hi=0xB & op1b_b3=1 & op1b_b2=0 & op1b_reg_dehl & op1b_b0=1 & AX 900 | { 901 | write_word(AX, op1b_reg_dehl); 902 | } 903 | 904 | :MOVW sfr_raw2,"#"imm16 is op1b_byte=0xCB; sfr_raw2; imm16 905 | { 906 | sfr_raw2 = imm16; 907 | } 908 | 909 | :MOVW AX,[HL+adr8] is op1b_byte=0xAC; adr8 & AX & HL 910 | { 911 | local ptr:$(PTRSIZE) = 0; 912 | reg_stackarea_offset8(ptr, HL, adr8); 913 | AX = *:2 ptr; 914 | } 915 | 916 | :MOVW [HL+adr8],AX is op1b_byte=0xBC; adr8 & AX & HL 917 | { 918 | local ptr:$(PTRSIZE) = 0; 919 | reg_stackarea_offset8(ptr, HL, adr8); 920 | *:2 ptr = AX; 921 | } 922 | 923 | :MOVW AX,[DE+adr8] is op1b_byte=0xAA; adr8 & AX & DE 924 | { 925 | local ptr:$(PTRSIZE) = 0; 926 | reg_stackarea_offset8(ptr, DE, adr8); 927 | AX = *:2 ptr; 928 | } 929 | 930 | :MOVW [DE+adr8],AX is op1b_byte=0xBA; adr8 & AX & DE 931 | { 932 | local ptr:$(PTRSIZE) = 0; 933 | reg_stackarea_offset8(ptr, DE, adr8); 934 | *:2 ptr = AX; 935 | } 936 | 937 | :MOVW sfrp,AX is op1b_byte=0xBE; sfrp & AX 938 | { 939 | sfrp = AX; 940 | } 941 | 942 | :MOVW sfr_raw2,AX is op1b_byte=0xBE; sfr_raw2 & AX 943 | { 944 | sfr_raw2 = AX; 945 | } 946 | 947 | :MOVW AX,sfrp is op1b_byte=0xAE; sfrp & AX 948 | { 949 | AX = sfrp; 950 | } 951 | 952 | :MOVW AX,sfr_raw2 is op1b_byte=0xAE; sfr_raw2 & AX 953 | { 954 | AX = sfr_raw2; 955 | } 956 | 957 | :MOVW saddrp,AX is op1b_byte=0xBD; saddrp & AX 958 | { 959 | saddrp = AX; 960 | } 961 | 962 | :MOVW AX,saddrp is op1b_byte=0xAD; saddrp & AX 963 | { 964 | AX = saddrp; 965 | } 966 | 967 | :MOVW op1b_movw_rp,saddrp is op1b_b7=1 & op1b_b6=1 & op1b_movw_rp & op1b_lo=0xA; saddrp 968 | { 969 | op1b_movw_rp = saddrp; 970 | } 971 | 972 | :MOVW AX,bc_offset_word is op1b_byte=0x79; bc_offset_word & AX 973 | { 974 | AX = bc_offset_word; 975 | } 976 | 977 | :MOVW bc_offset_word,AX is op1b_byte=0x78; bc_offset_word & AX 978 | { 979 | bc_offset_word = AX; 980 | } 981 | 982 | :MOVW saddrp,"#"imm16 is op1b_byte=0xC9; saddrp; imm16 983 | { 984 | saddrp = imm16; 985 | } 986 | 987 | :MOVW AX,sp_offset_word is op1b_byte=0xA8; sp_offset_word & AX 988 | { 989 | AX = sp_offset_word; 990 | } 991 | 992 | :MOVW sp_offset_word,AX is op1b_byte=0xB8; sp_offset_word & AX 993 | { 994 | sp_offset_word = AX; 995 | } 996 | 997 | :MOVW HL,es_addr16_word is esmarker=0x11; es_addr16_word & HL 998 | { 999 | HL = es_addr16_word; 1000 | } 1001 | 1002 | 1003 | :XCHW AX,op1b_2xreg is op1b_hi=0x3 & op1b_b3=0 & op1b_2xreg & op1b_b0=1 & AX 1004 | { 1005 | local tmp:2 = AX; 1006 | AX = op1b_2xreg; 1007 | op1b_2xreg=tmp; 1008 | } 1009 | 1010 | :ONEW op1b_reg_axbc is (op1b_byte=0xE6 | op1b_byte=0xE7) & op1b_reg_axbc 1011 | { 1012 | op1b_reg_axbc = 0x0001:2; 1013 | } 1014 | 1015 | :CLRW op1b_reg_axbc is (op1b_byte=0xF6 | op1b_byte=0xF7) & op1b_reg_axbc 1016 | { 1017 | op1b_reg_axbc = 0x0000:2; 1018 | } 1019 | 1020 | #:ADD 1021 | #TODO: All of add except ADD reg, reg 1022 | # ADD A, imm8 1023 | # ADD A, [hl+adr8] 1024 | # ADD saddr, imm8 1025 | :ADD A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x0 & op2b_b3=1 & op2b_reg0 & op2b_reg0_dupe!=0x1 & A 1026 | { 1027 | add_set_flags(A, op2b_reg0); 1028 | A = A + op2b_reg0; 1029 | } 1030 | 1031 | :ADD op2b_reg0, A is op1b_byte=0x61; op2b_hi=0x0 & op2b_b3=0 & op2b_reg0 & A 1032 | { 1033 | add_set_flags(op2b_reg0, A); 1034 | op2b_reg0 = op2b_reg0 + A; 1035 | } 1036 | 1037 | :ADD A,"#"imm8 is op1b_byte=0x0C; imm8 & A 1038 | { 1039 | add_set_flags(A, imm8:1); 1040 | A = A + imm8; 1041 | } 1042 | 1043 | :ADD A,hl_offset is op1b_byte=0x0E; hl_offset & A 1044 | { 1045 | add_set_flags(A, hl_offset); 1046 | A = A + hl_offset; 1047 | } 1048 | 1049 | :ADD saddr,"#"imm8 is op1b_byte=0x0A; saddr; imm8 1050 | { 1051 | add_set_flags(saddr, imm8:1); 1052 | saddr = saddr + imm8; 1053 | } 1054 | 1055 | #TODO: * 1056 | #DONE: ADDC A, r 1057 | # ADDC r, A 1058 | # A, #byte 1059 | # ADDC saddr, imm8 1060 | :ADDC A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x1 & op2b_b3=1 & op2b_reg0 & op2b_reg0_dupe!=1 & A 1061 | { 1062 | local tmp:1 = op2b_reg0 + $(CY_flag); 1063 | add_set_flags(A, tmp); 1064 | A = A + tmp; 1065 | } 1066 | 1067 | :ADDC op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x1 & op2b_b3=0 & op2b_reg0 & A 1068 | { 1069 | local tmp:1 = op2b_reg0 + $(CY_flag); 1070 | add_set_flags(tmp, A); 1071 | op2b_reg0 = A + tmp; 1072 | } 1073 | 1074 | :ADDC A,"#"imm8 is op1b_byte=0x1C; imm8 & A 1075 | { 1076 | local tmp:1 = A + $(CY_flag); 1077 | add_set_flags(tmp, imm8:1); 1078 | A = tmp + imm8; 1079 | 1080 | } 1081 | 1082 | :ADDC saddr,"#"imm8 is op1b_byte=0x1A; saddr; imm8 1083 | { 1084 | add_set_flags(saddr, imm8:1); 1085 | saddr = saddr + imm8; 1086 | } 1087 | 1088 | #:SUB 1089 | #TODO: figure out AC and CY? CY is prolly wrong here anyway 1090 | #TODO: * 1091 | #DONE: sub a, [hl+byte] 1092 | # sub a, r 1093 | # sub r, a 1094 | # sub A, !addr16 1095 | # SUB A, ES:[HL+B] 1096 | macro inst_sub(dst, src){ 1097 | local tmp:1 = dst - src; 1098 | $(Z_flag) = (tmp == 0); 1099 | local neg_src = -src; 1100 | $(CY_flag) = dst[7,1] & neg_src[7,1]; 1101 | dst = dst - src; 1102 | } 1103 | :SUB A,hl_offset is op1b_byte=0x2E; hl_offset & A 1104 | { 1105 | inst_sub(A, hl_offset); 1106 | } 1107 | 1108 | :SUB A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x2 & op2b_b3=1 & op2b_reg0 & op2b_reg0_dupe!=1 & A 1109 | { 1110 | inst_sub(A,op2b_reg0); 1111 | } 1112 | 1113 | :SUB op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x2 & op2b_b3=0 & op2b_reg0 & A 1114 | { 1115 | inst_sub(op2b_reg0,A); 1116 | } 1117 | 1118 | :SUB A,!addr16_1x is op1b_byte=0x2F; addr16_1x & A 1119 | { 1120 | inst_sub(A, addr16_1x); 1121 | } 1122 | 1123 | :SUB A,es_hl_b_byte is esmarker=0x11; op1b_byte=0x61; op2b_byte=0xA0 & A & es_hl_b_byte 1124 | { 1125 | inst_sub(A, es_hl_b_byte); 1126 | } 1127 | 1128 | #:SUBC 1129 | #TODO: * 1130 | #DONE: SUBC A,r 1131 | # SUBC r,A 1132 | # SUBC saddr,imm8 1133 | # SUBC A,saddr 1134 | macro inst_subc(dst, src){ 1135 | #TODO: figure out how tf the AC flag actually works 1136 | local tmp:1 = dst - src; 1137 | tmp = tmp - $(CY_flag); 1138 | $(Z_flag) = (tmp == 0); 1139 | local neg_src_p1:1 = src + 1; 1140 | neg_src_p1 = -1*neg_src_p1; 1141 | $(CY_flag) = (dst[7,1] & neg_src_p1[7,1]); 1142 | dst = tmp; 1143 | } 1144 | 1145 | :SUBC A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x3 & op2b_b3=1 & op2b_reg0 & op2b_reg0_dupe!=1 & A 1146 | { 1147 | inst_subc(A,op2b_reg0); 1148 | } 1149 | 1150 | :SUBC op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x3 & op2b_b3=0 & op2b_reg0 & A 1151 | { 1152 | inst_subc(op2b_reg0,A); 1153 | } 1154 | 1155 | :SUBC saddr,"#"imm8 is op1b_byte=0x3A; saddr; imm8 1156 | { 1157 | inst_subc(saddr,imm8); 1158 | } 1159 | 1160 | :SUBC A,saddr is op1b_byte=0x3B; saddr & A 1161 | { 1162 | inst_subc(A,saddr); 1163 | } 1164 | 1165 | #:AND 1166 | #TODO: ~DONE 1167 | #DONE: and A, #imm8 1168 | # and A, saddr 1169 | # and a, addr16_1x 1170 | # and a [hl+byte] 1171 | # and saddr, #byte 1172 | # and reg, A 1173 | # and A, reg 1174 | macro inst_and(dst, src){ 1175 | local tmp = dst & src; 1176 | $(Z_flag) = (tmp == 0); 1177 | dst = tmp; 1178 | } 1179 | :AND A,"#"imm8 is op1b_byte=0x5C; imm8 & A 1180 | { 1181 | inst_and(A,imm8); 1182 | } 1183 | 1184 | :AND A,sfr_raw1 is op1b_byte=0x5B; sfr_raw1 & A 1185 | { 1186 | inst_and(A,sfr_raw1); 1187 | } 1188 | 1189 | :AND A,addr16_1x is op1b_byte=0x5F; addr16_1x & A 1190 | { 1191 | inst_and(A,addr16_1x); 1192 | } 1193 | 1194 | :AND A,hl_offset is op1b_byte=0x5E; hl_offset & A 1195 | { 1196 | inst_and(A,hl_offset); 1197 | } 1198 | 1199 | :AND saddr,"#"imm8 is op1b_byte=0x5A; saddr; imm8 1200 | { 1201 | inst_and(saddr,imm8); 1202 | } 1203 | 1204 | :AND A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x5 & op2b_b3=1 & op2b_reg0 & op2b_reg0_dupe!=1 & A 1205 | { 1206 | inst_and(A, op2b_reg0); 1207 | } 1208 | 1209 | :AND op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x5 & op2b_b3=0 & op2b_reg0 & A 1210 | { 1211 | inst_and(op2b_reg0, A); 1212 | } 1213 | 1214 | #:OR 1215 | #TODO: more macros for instructions in this style. way cleaner. 1216 | #TODO: add documentation of instructions at each instruction 1217 | #TODO: * 1218 | #DONE: or saddr, #byte 1219 | # or A, #byte 1220 | # or A, reg 1221 | # or reg, A 1222 | # or A, [hl+byte] 1223 | # or A, !addr16 1224 | # or A, saddr 1225 | macro inst_or(dst, src){ 1226 | local tmp = 0x0; 1227 | tmp = dst | src; 1228 | $(Z_flag) = (tmp == 0); 1229 | dst = tmp; 1230 | } 1231 | 1232 | :OR saddr,"#"imm8 is op1b_byte=0x6A; saddr; imm8 1233 | { 1234 | inst_or(saddr, imm8); 1235 | } 1236 | 1237 | :OR A,"#"imm8 is op1b_byte=0x6C; imm8 & A 1238 | { 1239 | inst_or(A, imm8); 1240 | } 1241 | 1242 | :OR A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x6 & op2b_b3=1 & op2b_reg0 & A 1243 | { 1244 | inst_or(A,op2b_reg0); 1245 | } 1246 | 1247 | :OR op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x6 & op2b_b3=0 & op2b_reg0 & A 1248 | { 1249 | inst_or(op2b_reg0,A); 1250 | } 1251 | 1252 | :OR A,hl_offset is op1b_byte=0x6E; hl_offset & A 1253 | { 1254 | inst_or(A, hl_offset); 1255 | } 1256 | 1257 | :OR A,!addr16_1x is op1b_byte=0x6F; addr16_1x & A 1258 | { 1259 | inst_or(A, addr16_1x); 1260 | } 1261 | 1262 | :OR A,saddr is op1b_byte=0x6B; saddr & A 1263 | { 1264 | inst_or(A, saddr); 1265 | } 1266 | 1267 | #:XOR 1268 | #TODO: * 1269 | #DONE: XOR A,r 1270 | # XOR r,A 1271 | # XOR A,#byte 1272 | # xor a,saddr 1273 | macro inst_xor(dst, src){ 1274 | $(Z_flag) = (dst == src); 1275 | dst = (dst ^ src); 1276 | } 1277 | 1278 | :XOR A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x7 & op2b_b3=1 & op2b_reg0 & A 1279 | { 1280 | inst_xor(A,op2b_reg0); 1281 | } 1282 | 1283 | :XOR op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x7 & op2b_b3=0 & op2b_reg0 & A 1284 | { 1285 | inst_xor(op2b_reg0,A); 1286 | } 1287 | 1288 | :XOR A,"#"imm8 is op1b_byte=0x7C; imm8 & A 1289 | { 1290 | inst_xor(A,imm8); 1291 | } 1292 | 1293 | :XOR A,saddr is op1b_byte=0x7B; saddr & A 1294 | { 1295 | inst_xor(A, saddr); 1296 | } 1297 | #:CMP 1298 | #TODO: Check if CMP operands are in wrong order in macro. ISA is unclear. 1299 | #TODO: Rest of CMP 1300 | # we've done cmp a, imm8 1301 | # cmp a, reg 1302 | # cmp reg, a 1303 | # cmp !addr16, #byte 1304 | # cmp a, saddr 1305 | # cmp saddr, imm8 1306 | # cmp A,[hl+byte] 1307 | # cmp A, !addr16 1308 | # cmp !add16, #byte 1309 | # cmp A, [hl] 1310 | :CMP A,"#"imm8 is op1b_byte=0x4C; imm8 & A { 1311 | cmp8_set_flags(A, imm8); 1312 | } 1313 | 1314 | :CMP A,op2b_reg0 is op1b_byte=0x61; op2b_hi=0x4 & op2b_b3=0x1 & 1315 | op2b_reg0 & op2b_reg0_dupe!=0x1 & A 1316 | { 1317 | cmp8_set_flags(A, op2b_reg0); 1318 | } 1319 | 1320 | :CMP op2b_reg0,A is op1b_byte=0x61; op2b_hi=0x4 & op2b_b3=0x0 & 1321 | op2b_reg0 & op2b_reg0_dupe!=0x1 & A 1322 | { 1323 | cmp8_set_flags(op2b_reg0, A); 1324 | } 1325 | 1326 | :CMP !addr16,"#"imm8 is op1b_byte=0x40; addr16; imm8{ 1327 | val:1 = 0x0; 1328 | read_byte(val, addr16); 1329 | cmp8_set_flags(val, imm8); 1330 | } 1331 | 1332 | :CMP A, saddr is op1b_byte=0x4B; saddr & A 1333 | { 1334 | cmp8_set_flags(A, saddr); 1335 | } 1336 | 1337 | :CMP saddr,"#"imm8 is op1b_byte=0x4A; saddr; imm8 1338 | { 1339 | cmp8_set_flags(saddr, imm8); 1340 | } 1341 | 1342 | :CMP A,hl_offset is op1b_byte=0x4E; hl_offset & A 1343 | { 1344 | cmp8_set_flags(A, hl_offset); 1345 | } 1346 | 1347 | :CMP A,!addr16_1x is op1b_byte=0x4F; addr16_1x & A 1348 | { 1349 | cmp8_set_flags(A, addr16_1x); 1350 | } 1351 | 1352 | :CMP es_addr16_byte,"#"imm8 is esmarker=0x11; op1b_byte=0x40; es_addr16_byte; imm8 1353 | { 1354 | cmp8_set_flags(es_addr16_byte, imm8); 1355 | } 1356 | 1357 | :CMP A,hl_ptr_byte is op1b_byte=0x4D & A & hl_ptr_byte 1358 | { 1359 | cmp8_set_flags(A, hl_ptr_byte); 1360 | } 1361 | 1362 | 1363 | :CMP0 op1b_reg0 is op1b_hi=0xD & op1b_b3=0 & op1b_reg0 & op1b_reg0_dupe<4 1364 | { 1365 | $(AC_flag) = 0; 1366 | $(CY_flag) = 0; 1367 | $(Z_flag) = (op1b_reg0 == 0x0); 1368 | } 1369 | 1370 | :CMP0 !addr16_1x is op1b_byte=0xD5; addr16_1x 1371 | { 1372 | $(AC_flag) = 0; 1373 | $(CY_flag) = 0; 1374 | $(Z_flag) = (addr16_1x == 0x0); 1375 | } 1376 | 1377 | :CMP0 saddr is op1b_byte=0xD4; saddr 1378 | { 1379 | $(AC_flag) = 0; 1380 | $(CY_flag) = 0; 1381 | $(Z_flag) = (saddr == 0x0); 1382 | } 1383 | 1384 | :CMP0 es_addr16_byte is op1b_byte=0x11; op2b_byte=0xD5; es_addr16_byte 1385 | { 1386 | $(AC_flag) = 0; 1387 | $(CY_flag) = 0; 1388 | $(Z_flag) = (es_addr16_byte == 0); 1389 | } 1390 | 1391 | 1392 | #:CMPS 1393 | 1394 | #:ADDW 1395 | #TODO: instruction macro for addw 1396 | #TODO: etc 1397 | #DONE: addw ES instructions 1398 | :ADDW AX,"#"simm16 is op1b_byte=0x04; simm16 & AX 1399 | { 1400 | addw_set_flags(AX, simm16); 1401 | AX = AX + simm16; 1402 | } 1403 | 1404 | :ADDW AX,!addr16_2x is op1b_byte=0x02; addr16_2x & AX 1405 | { 1406 | addw_set_flags(AX, addr16_2x); 1407 | AX = AX + addr16_2x; 1408 | } 1409 | 1410 | :ADDW SP,"#"imm8 is op1b_byte=0x10; imm8 & SP 1411 | { 1412 | SP = SP + zext(imm8:1); 1413 | } 1414 | 1415 | :ADDW AX,op1b_2xreg is op1b_hi=0x0 & op1b_2xreg & op1b_b0=1 & AX 1416 | { 1417 | addw_set_flags(AX, op1b_2xreg); 1418 | AX = AX + op1b_2xreg; 1419 | } 1420 | 1421 | :ADDW AX,hl_offset_word is op1b_byte=0x61; op2b_byte=0x09; hl_offset_word & AX 1422 | { 1423 | addw_set_flags(AX, hl_offset_word); 1424 | AX = AX + hl_offset_word; 1425 | } 1426 | 1427 | :ADDW AX,saddrp is op1b_byte=0x06; saddrp & AX 1428 | { 1429 | addw_set_flags(AX, saddrp); 1430 | AX = AX + saddrp; 1431 | } 1432 | 1433 | #:SUBW 1434 | #TODO: build instruction macro 1435 | #TODO: etc 1436 | #DONE: subw AX, #imm16 1437 | # SUBW AX, rp 1438 | # SUBW SP, #imm8 1439 | # subw AX, !addr16 1440 | :SUBW AX,"#"imm16 is op1b_byte=0x24; imm16 & AX 1441 | { 1442 | subw_set_flags(AX,imm16); 1443 | AX = AX - imm16; 1444 | } 1445 | 1446 | :SUBW AX, op1b_reg_rp is op1b_hi=0x2 & op1b_b3=0 & op1b_reg_rp & op1b_b0=1 & op1b_reg_rp_dupe!=0 & AX 1447 | { 1448 | subw_set_flags(AX,op1b_reg_rp); 1449 | AX = AX - op1b_reg_rp; 1450 | } 1451 | 1452 | :SUBW AX,saddrp is op1b_byte=0x26; saddrp & AX 1453 | { 1454 | subw_set_flags(AX, saddrp); 1455 | AX = AX - saddrp; 1456 | } 1457 | 1458 | :SUBW SP,"#"imm8 is op1b_byte=0x20; imm8 & SP 1459 | { 1460 | local tmp:2 = sext(imm8:1); 1461 | subw_set_flags(SP,tmp); 1462 | SP = SP - tmp; 1463 | } 1464 | 1465 | :SUBW AX,!addr16_2x is op1b_byte=0x22; addr16_2x & AX 1466 | { 1467 | subw_set_flags(AX, addr16_2x); 1468 | AX = AX - addr16_2x; 1469 | } 1470 | 1471 | #:CMPW 1472 | #TODO: cmpw ES cases 1473 | :CMPW AX,"#"imm16 is op1b_byte=0x44; imm16 & AX{ 1474 | cmpw_set_flags(AX, imm16); 1475 | } 1476 | 1477 | :CMPW AX,op1b_2xreg is op1b_hi=0x4 & op1b_b3=0 & op1b_2xreg & op1b_b0=1 & AX 1478 | { 1479 | cmpw_set_flags(AX, op1b_2xreg); 1480 | } 1481 | 1482 | :CMPW AX,[HL+adr8] is op1b_byte=0x61; op2b_byte=0x49; adr8 & AX & HL 1483 | { 1484 | local ptr:$(PTRSIZE) = 0x0; 1485 | reg_stackarea_offset8(ptr, HL, adr8); 1486 | local tmp = *:2 ptr; 1487 | cmpw_set_flags(AX, tmp); 1488 | } 1489 | 1490 | :CMPW AX,!addr16_2x is op1b_byte=0x42; addr16_2x & AX 1491 | { 1492 | cmpw_set_flags(AX, addr16_2x); 1493 | } 1494 | 1495 | :CMPW AX,saddrp is op1b_byte=0x46; saddrp & AX 1496 | { 1497 | cmpw_set_flags(AX, saddrp); 1498 | } 1499 | 1500 | 1501 | #TODO: figure out how to make this less nasty without confusing the compiler: 1502 | :MULU X (AX"="A"*X") is op1b_byte=0xD6 & X & A & AX 1503 | { 1504 | local Aext:2 = zext(A:1); 1505 | local Xext:2 = zext(X:1); 1506 | AX = Aext * Xext; 1507 | } 1508 | 1509 | #:MULHU 1510 | #:MULH 1511 | #:DIVHU 1512 | #:DIVWU 1513 | #:MACHU 1514 | #:MACH 1515 | 1516 | #:INC 1517 | #TODO: set CY and AC flags 1518 | #TODO: INC ES operations 1519 | 1520 | macro inst_inc(dst){ 1521 | $(Z_flag) = (dst == 0xFF); 1522 | dst = dst + 1; 1523 | } 1524 | 1525 | :INC op1b_reg0 is op1b_hi=0x8 & op1b_b3=0 & op1b_reg0 { 1526 | inst_inc(op1b_reg0); 1527 | } 1528 | 1529 | :INC hl_offset is op1b_byte=0x61; op2b_byte=0x59; hl_offset 1530 | { 1531 | inst_inc(hl_offset); 1532 | } 1533 | 1534 | :INC saddr is op1b_byte=0xA4; saddr 1535 | { 1536 | inst_inc(saddr); 1537 | } 1538 | 1539 | :INC !addr16_1x is op1b_byte=0xA0; addr16_1x 1540 | { 1541 | inst_inc(addr16_1x); 1542 | } 1543 | 1544 | #:DEC 1545 | #TODO: set CY and AC flags 1546 | #TODO: all of DEC except register mode 1547 | # dec hl+byte 1548 | # dec saddr 1549 | macro inst_dec(dst){ 1550 | $(Z_flag) = (dst == 0x1); 1551 | dst = dst - 1; 1552 | } 1553 | 1554 | :DEC op1b_reg0 is op1b_hi=0x9 & op1b_b3=0 & op1b_reg0 { 1555 | inst_dec(op1b_reg0); 1556 | } 1557 | 1558 | :DEC hl_offset is op1b_byte=0x61; op2b_byte=0x69; hl_offset 1559 | { 1560 | inst_dec(hl_offset); 1561 | } 1562 | 1563 | :DEC saddr is op1b_byte=0xB4; saddr 1564 | { 1565 | inst_dec(saddr); 1566 | } 1567 | 1568 | #:INCW 1569 | #TODO: set CY and AC flags 1570 | #TODO: incw es:!addr16 / incw es:[HL+byte] 1571 | macro inst_incw(dst){ 1572 | $(Z_flag) = (dst == 0xFFFF); 1573 | dst = dst + 1; 1574 | } 1575 | 1576 | :INCW op1b_2xreg is op1b_hi=0xA & op1b_b3=0 & op1b_2xreg & op1b_b0=1 1577 | { 1578 | inst_incw(op1b_2xreg); 1579 | } 1580 | 1581 | :INCW hl_offset_word is op1b_byte=0x61; op2b_byte=0x79; hl_offset_word 1582 | { 1583 | inst_incw(hl_offset_word); 1584 | } 1585 | 1586 | :INCW saddrp is op1b_byte=0xA6; saddrp 1587 | { 1588 | inst_incw(saddrp); 1589 | } 1590 | 1591 | :INCW !addr16_2x is op1b_byte=0xA2; addr16_2x 1592 | { 1593 | inst_incw(addr16_2x); 1594 | } 1595 | 1596 | #:DECW 1597 | #TODO: set CY and AC flags 1598 | #TODO: decw ES: cases 1599 | macro inst_decw(dst){ 1600 | $(Z_flag) = (dst == 0x0001); 1601 | dst = dst - 1; 1602 | } 1603 | 1604 | :DECW op1b_2xreg is op1b_hi=0xB & op1b_b3=0 & op1b_2xreg & op1b_b0=1 1605 | { 1606 | inst_decw(op1b_2xreg); 1607 | } 1608 | 1609 | :DECW hl_offset is op1b_byte=0x61; op2b_byte=0x89; hl_offset 1610 | { 1611 | inst_decw(hl_offset); 1612 | } 1613 | 1614 | :DECW saddrp is op1b_byte=0xB6; saddrp 1615 | { 1616 | inst_decw(saddrp); 1617 | } 1618 | 1619 | :DECW !addr16_2x is op1b_byte=0xB2; addr16_2x 1620 | { 1621 | inst_decw(addr16_2x); 1622 | } 1623 | 1624 | #TODO: create right-shift macro 1625 | :SHR A, op2b_bit_sel is op1b_byte=0x31; op2b_b7=0 & op2b_bit_sel & op2b_lo=0xA & A 1626 | { 1627 | local lobit:1 = 0; 1628 | local shiftcount:1 = 0; 1629 | local shifted:1 = A; 1630 | 1631 | lobit = shifted[0,1]; 1632 | shiftcount = shiftcount + 1; 1633 | shifted = shifted >> 1; 1634 | if (shiftcount != op2b_bit_sel) goto ; 1635 | $(CY_flag) = lobit&0x1; 1636 | A = shifted; 1637 | } 1638 | 1639 | :SHRW AX,op2b_hi_val is op1b_byte=0x31; op2b_hi_val & op2b_lo=0xE & op2b_hi!=0x0 & AX { 1640 | #TODO: just realized i screwed up how flags are set in shift ops, need to fix that. 1641 | sr_set_flags16(AX, op2b_hi_val); 1642 | AX = AX >> op2b_hi_val; 1643 | } 1644 | 1645 | #:SHL 1646 | 1647 | macro inst_shlw(reg, shiftamt){ 1648 | local hibt:1=0; 1649 | local shiftcount:1 = 0; 1650 | local shifted:2 = reg; 1651 | 1652 | hibit = shifted[15,1]; 1653 | shiftcount = shiftcount+1; 1654 | shifted = shifted<<1; 1655 | if (shiftcount!=shiftamt) goto ; 1656 | $(CY_flag)= hibit[0,1]; 1657 | reg = shifted; 1658 | } 1659 | 1660 | :SHLW AX,op2b_hi_val is op1b_byte=0x31; op2b_hi_val & op2b_lo=0xD & op2b_hi!=0 & AX 1661 | { 1662 | inst_shlw(AX, op2b_hi_val); 1663 | } 1664 | 1665 | :SHLW BC,op2b_hi_val is op1b_byte=0x31; op2b_hi_val & op2b_lo=0xC & op2b_hi!=0 & BC 1666 | { 1667 | inst_shlw(BC, op2b_hi_val); 1668 | } 1669 | 1670 | :SAR A,op2b_hi_val is op1b_byte=0x31; op2b_hi_val & op2b_lo=0xB & op2b_hi<0x8 & A { 1671 | sr_set_flags8(A, op2b_hi_val); 1672 | A = A s>> op2b_hi_val; 1673 | } 1674 | 1675 | macro inst_sarw(reg, shiftamt){ 1676 | local lobit:1=0; 1677 | local shiftcount:1 = 0; 1678 | local shifted:2 = reg; 1679 | 1680 | lobit = shifted[15,1]; 1681 | shiftcount = shiftcount+1; 1682 | shifted = shifted s>> 1; 1683 | if (shiftcount!=shiftamt) goto ; 1684 | $(CY_flag)= lobit[0,1]; 1685 | reg = shifted; 1686 | } 1687 | 1688 | :SARW AX,op2b_hi_val is op1b_byte=0x31; op2b_hi_val & op2b_lo=0xF & op2b_hi!=0 & AX 1689 | { 1690 | inst_sarw(AX, op2b_hi_val); 1691 | } 1692 | 1693 | #:ROR 1694 | #:ROL 1695 | 1696 | :RORC A,1 is op1b_byte=0x61; op2b_byte=0xFB & A 1697 | { 1698 | local tmp:1 = $(CY_flag); 1699 | $(CY_flag) = A[0,1]; 1700 | A = A >> 1; 1701 | A[7,1] = tmp; 1702 | } 1703 | 1704 | :ROLC A,1 is op1b_byte=0x61; op2b_byte=0xDC & A 1705 | { 1706 | local tmp:1 = $(CY_flag); 1707 | $(CY_flag) = A[7,1]; 1708 | A = A << 1; 1709 | A[0,1] = tmp[0,1]; 1710 | } 1711 | 1712 | macro inst_rolwc(dst){ 1713 | local tmp:1 = $(CY_flag); 1714 | $(CY_flag) = dst[15,1]; 1715 | dst = dst << 1; 1716 | dst[0,1] = tmp[0,1]; 1717 | } 1718 | 1719 | :ROLWC AX,1 is op1b_byte=0x61; op2b_byte=0xEE & AX 1720 | { 1721 | inst_rolwc(AX); 1722 | } 1723 | 1724 | :ROLWC BC,1 is op1b_byte=0x61; op2b_byte=0xFE & BC 1725 | { 1726 | inst_rolwc(BC); 1727 | } 1728 | 1729 | #:MOV1 1730 | #TODO: clean up with macros, this is nasty 1731 | #TODO: all mov1 except mov1 CY, A.x 1732 | # mov1 sfr.x, CY 1733 | # mov1 CY, [HL].x 1734 | # mov1 [HL].x, CY 1735 | # MOV1 CY, saddr.x 1736 | # MOV1 saddr.x, CY 1737 | # MOV1 A.x, CY 1738 | :MOV1 op3b_sfr"."op2b_bit_sel,"CY" is op1b_byte=0x71; op2b_b7=0x0 & op2b_bit_sel & op2b_lo=0x9; op3b_hi=0xF & op3b_b3=1 & op3b_sfr 1739 | { 1740 | local tmp = op3b_sfr; 1741 | local mask = (0xFF & (~(0x1)<>op2b_bit_sel)&0x1); 1749 | } 1750 | 1751 | #TODO: Ghidra thinks the bit select value is the offset. needs to be fixed. 1752 | :MOV1 "CY,"[HL]"."op2b_bit_sel is op1b_byte=0x71; op2b_b7=1 & op2b_bit_sel & op2b_lo=0x4 & HL 1753 | { 1754 | local tmp:1 = 0x0; 1755 | read_byte(tmp, HL); 1756 | $(CY_flag) = ((tmp>>op2b_bit_sel)&0x1); 1757 | } 1758 | 1759 | :MOV1 [HL]"."op2b_bit_sel,"CY" is op1b_byte=0x71; op2b_b7=1 & op2b_bit_sel & op2b_lo=0x1 & HL 1760 | { 1761 | local hl_dupe:1 = 0x0; 1762 | read_byte(hl_dupe, HL); 1763 | local mask = ~(0x1 << op2b_bit_sel); 1764 | hl_dupe = hl_dupe & mask; 1765 | hl_dupe = hl_dupe|($(CY_flag)<>1)&0x1); 2184 | $(RBS0_flag) = op2b_reg_rbsel&0x1; 2185 | } 2186 | 2187 | :NOP is op1b_byte=0x00 {} 2188 | 2189 | #:EI 2190 | #:DI 2191 | 2192 | :HALT is op1b_byte=0x61; op2b_byte=0xED 2193 | { 2194 | halt(); 2195 | } 2196 | 2197 | :STOP is op1b_byte=0x61; op2b_byte=0xFD 2198 | { 2199 | stop(); 2200 | } 2201 | 2202 | :INVALID is op1b_byte=0xFF 2203 | { 2204 | invalid(); 2205 | } --------------------------------------------------------------------------------