forked from projectPiki/pikmin2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
genasm.sh
executable file
·230 lines (209 loc) · 7.78 KB
/
genasm.sh
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/env bash
# README
# genasm.sh: Hacky utility for rapidly generating comparable codegen. (previously called compare.sh)
# Disclaimer: I'm not good at shell scripting.
# License: MIT? I guess? I don't really care. :shrug:
#
# WARNING: Make sure to edit the settings section for your own purposes!
#
# 1st argument is code unit relative to root of repository (e.g. src/JSystem/JSU/JSUList.cpp).
# 2nd argument is "function name" to search for.
# (really, it's just doing substring matches against the mangled name,
# so overloaded names have issues without specifically writing those out...).
# By default, the script trims the function-relative offsets in the first output column,
# and after branch instructions (e.g. ` ; 0x00000020`).
# Passing a 3rd argument that isn't 0 to the script disables this feature.
#
# There is an optional non-positional flag "-k" (or "--no-strip"), which simply generates the files
# and only strips branch labels from the ASM disassembly. This can be useful for confirming that data sections
# are being generated correctly.
#
# Example vscode task to invoke on the current selection (when it's something that uniquely matches a mangled name):
# {
# "label": "genasm",
# "command": "./genasm.sh ${relativeFile} ${selectedText}",
# "type": "shell",
# "args": [],
# "options": {
# "cwd": "${workspaceFolder}",
# },
# },
# }
#
# The following command would open the compare view for the two files in vscode.
# I personally have this as a separate task in that, so I can bring it up if I don't already have it up.
# code -d ${Ours} ${Theirs}
ShouldStripFiles=1
POSITIONAL=()
while (( $# > 0 )); do
case "${1}" in
-k|--no-strip)
echo flag: "${1}"
shift # shift once since flags have no values
ShouldStripFiles=0
;;
-s|--switch)
numOfArgs=1 # number of switch arguments
if (( $# < numOfArgs + 1 )); then
shift $#
else
echo "switch: ${1} with value: ${2}"
shift $((numOfArgs + 1)) # shift 'numOfArgs + 1' to bypass switch and its value
fi
;;
*) # unknown flag/switch
POSITIONAL+=("${1}")
shift
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional params
# SETTINGS
# Note: None of the paths should have terminating slashes.
# The desired location of the output files (relative to compiler). "Ours" is the decomped one.
Theirs="../../../temp.old.s"
Ours="../../../temp.new.s"
# "src" folder where "your" compiled .o files are located, relative to ${CompilerDir} (or absolute).
BuildSrcDir="../../../build/pikmin2.usa/src"
# "asm" folder where "their" assembled .o files are located, relative to ${CompilerDir} (or absolute).
BuildASMDir="../../../build/pikmin2.usa/asm"
# Source code folder. Relative to ${CompilerDir} (or absolute).
SrcDir="../../../src"
# Includes (headers) folder. Relative to ${CompilerDir} (or absolute).
IncludeDir="../../../include"
LibDir="../../../build/pikmin2.usa/lib"
CompilerExe="mwcceppc.exe"
# Compiler Options. Make sure to set the include folder!
Opts="-Cpp_exceptions off -inline auto -enum int -proc gekko -RTTI off -fp hard -fp_contract on -rostr -O4,p -use_lmw_stmw on -sdata 8 -sdata2 8 -nodefaults -msgstyle gcc -l ${LibDir} -i ${IncludeDir}/ -c"
# Settings are over, now garbage shell script is my new friend!
# -o build/pikmin2.usa/src/sysCommonU/sysTemplates.o src/sysCommonU/sysTemplates.cpp
Temp=${1%.*}
CodeUnitPath=${Temp#*/}
Library=${CodeUnitPath%*/}
CodeUnit=${CodeUnitPath##*/}
HasLocalFuncs=1
# Compiler Version
CompilerVersion="2.6"
if [[ "${Library}" == *"Dolphin"* ]]; then
echo "${Library} contains: Dolphin"
# if [[ "${CodeUnit}" == *"dvd"* ] -or [ "${CodeUnit}" == *"GBA"* ]]; then
# echo "${CodeUnit} contains: dvd or GBA"
# CompilerVersion="1.2.5" # For GBA, DVD
# else
if [[ "${CodeUnit}" == *"dvd"* ]]; then
echo "${CodeUnit} contains: dvd"
CompilerVersion="1.2.5"
# Opts="-strict off -pragma \"defer_codegen on\" -pragma \"gcc_extensions on\" ${Opts}"
# Opts="-strict off -pragma gcc_extensions ${Opts}"
# Opts="-gcc_extensions ${Opts}"
# Opts="-strict off -pragma defer_codegen -pragma gcc_extensions ${Opts}"
HasLocalFuncs=1
elif [[ "${CodeUnit}" == *"GBA"* ]]; then
echo "${CodeUnit} contains: GBA"
CompilerVersion="1.2.5"
else
CompilerVersion="1.3.2" # For TRK
fi
fi
# Compiler folder, relative to current working directory.
CompilerDir="tools/mwcc_compiler/${CompilerVersion}"
# echo $Temp
# echo $CodeUnitPath
# echo $Library
# TODO: this and $Library might be useful for opts
CodeUnitExtension=${1##*.}
Func=$2
# 0 = genasm
# 1 = genasm w/o cutting addresses
# 2 = genrodata
if (( $# < 3 )); then
ShouldCutAddresses=1
Mode=0
elif (( $3 == 0 )); then
Mode=$3
ShouldCutAddresses=1
else
Mode=$3
ShouldCutAddresses=0
fi
# if (( $# < 3 )); then
# ShouldCutAddresses=1
# else
# ShouldCutAddresses=0
# fi;
# TODO: Check for linux/windows
# Nevermind, not needed. Wine automatically handles it so long as I have my paths right.
# Wine="wine "
Wine=""
# CompilerVersion="2.7e"
# Setting path doesn't entirely cut it. Gotta cd instead.
cd $CompilerDir
PATH="$PATH:$(pwd)"
# echo $PATH
# Make folders if they don't exist.
mkdir -p ${BuildSrcDir}/${CodeUnitPath%/*}
mkdir -p ${BuildASMDir}/${CodeUnitPath%/*}
# Build the src object file
${Wine}${CompilerExe} ${Opts} -o ${BuildSrcDir}/${CodeUnitPath}.o ${SrcDir}/${CodeUnitPath}.${CodeUnitExtension}
# ${Wine}${CompilerExe} ${Opts} -inline auto -o ${BuildSrcDir}/${CodeUnitPath}.o ${SrcDir}/${CodeUnitPath}.${CodeUnitExtension}
# Disasm both object files.
if [[ "${Library}" == *"Dolphin"* ]]; then
# echo "${Wine}${CompilerExe} -S ${Opts} ${BuildSrcDir}/${CodeUnitPath}.o"
${Wine}${CompilerExe} -S ${Opts} ${BuildSrcDir}/${CodeUnitPath}.o || exit 1
mv ${CodeUnit%*.}.s ${Ours}
${Wine}${CompilerExe} -S ${Opts} ${BuildASMDir}/${CodeUnitPath}.o
mv ${CodeUnit%*.}.s ${Theirs}
else
${Wine}${CompilerExe} -S ${Opts} ${BuildSrcDir}/${CodeUnitPath}.o -o ${Ours} || exit 1
${Wine}${CompilerExe} -S ${Opts} ${BuildASMDir}/${CodeUnitPath}.o -o ${Theirs}
fi
# sed -i 's#\s*Hunk:\s+Kind=HUNK_LOCAL_CODE\s+Name="lbl_.*##g' $Theirs
# Purge all branch labels from the ASM disassembly.
sed -ri '/\s*Hunk:\s+Kind=HUNK_LOCAL_CODE\s+Name="lbl_.*/d' $Theirs
# Experimental: Actually, do the same with HUNK_GLOBAL_CODE, because of switch statements...
sed -ri '/\s*Hunk:\s+Kind=HUNK_GLOBAL_CODE\s+Name="lbl_.*/d' $Theirs
# sed -ri '/\n\s*Hunk:\s+Kind=HUNK_LOCAL_CODE\s+Name="lbl_.*/='
function stripFile () {
File=$1
echo "Stripping $File"
# Remove Blank lines.
sed -i '/^[[:space:]]*$/d' ${File}
# Delete everything before the target.
# TODO: capture the name and prepend to file? Can you preserve back references like that?
if (( $Mode == 2 )); then
# set -x
sed -i '0,/==> .rodata[[:space:]]*$/d' ${File}
# set +x
# echo "Help?"
# wc ${File}
else
if (( $HasLocalFuncs == 0 )); then
sed -ri "0,/.*+Kind=HUNK_GLOBAL_CODE\\s+Name=\".*${Func}.*/d" ${File}
else
sed -ri "0,/.*+Kind=HUNK_((GLOBAL)|(LOCAL))_CODE\\s+Name=\".*${Func}.*/d" ${File}
fi
fi
# Delete all lines after the target function/section.
# This will either be at the start of next hunk...:
if (( $HasLocalFuncs == 0 )); then
sed -ri '/\s*Hunk:\s+Kind=HUNK_GLOBAL_CODE\s+Name=\".*/,$d' ${File}
else
sed -ri '/\s*Hunk:\s+Kind=HUNK_((GLOBAL)|(LOCAL))_CODE\s+Name=\".*/,$d' ${File}
fi
# ...or start of next section if it's the last function in the file:
sed -ri '/==>.*/,$d' ${File}
if (($ShouldCutAddresses > 0)); then
# Delete branch offsets. They're signal noise when looking for differences.
sed -ri 's/\s+;\s*0x.*$//g' ${File}
# cut doesn't buffer input, so just simulating an "in-place" by redirecting output to same file as input results in a blank file.
# TODO: Use mktemp
mv $File ${File}.tmp
# Cut addresses from start of lines.
cut -c11- ${File}.tmp > ${File}
rm ${File}.tmp
fi
}
if (( $ShouldStripFiles == 1 )); then
stripFile ${Ours}
stripFile ${Theirs}
fi