├── 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 |
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 | 
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 | }
--------------------------------------------------------------------------------