1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* codeinfo.c */ |
4 | /* */ |
5 | /* Additional information about 6502 code */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2001-2015, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdlib.h> |
37 | #include <string.h> |
38 | |
39 | /* common */ |
40 | #include "chartype.h" |
41 | #include "coll.h" |
42 | #include "debugflag.h" |
43 | |
44 | /* cc65 */ |
45 | #include "codeent.h" |
46 | #include "codeseg.h" |
47 | #include "datatype.h" |
48 | #include "error.h" |
49 | #include "global.h" |
50 | #include "reginfo.h" |
51 | #include "symtab.h" |
52 | #include "codeinfo.h" |
53 | |
54 | |
55 | |
56 | /*****************************************************************************/ |
57 | /* Data */ |
58 | /*****************************************************************************/ |
59 | |
60 | |
61 | |
62 | /* Table with the compare suffixes */ |
63 | static const char CmpSuffixTab [][4] = { |
64 | "eq" , "ne" , "gt" , "ge" , "lt" , "le" , "ugt" , "uge" , "ult" , "ule" |
65 | }; |
66 | |
67 | /* Table listing the function names and code info values for known internally |
68 | ** used functions. This table should get auto-generated in the future. |
69 | */ |
70 | typedef struct FuncInfo FuncInfo; |
71 | struct FuncInfo { |
72 | const char* Name; /* Function name */ |
73 | unsigned short Use; /* Register usage */ |
74 | unsigned short Chg; /* Changed/destroyed registers */ |
75 | }; |
76 | |
77 | /* Note for the shift functions: Shifts are done modulo 32, so all shift |
78 | ** routines are marked to use only the A register. The remainder is ignored |
79 | ** anyway. |
80 | */ |
81 | static const FuncInfo FuncInfoTable[] = { |
82 | { "addeq0sp" , REG_AX, REG_AXY }, |
83 | { "addeqysp" , REG_AXY, REG_AXY }, |
84 | { "addysp" , REG_Y, REG_NONE }, |
85 | { "aslax1" , REG_AX, REG_AX | REG_TMP1 }, |
86 | { "aslax2" , REG_AX, REG_AX | REG_TMP1 }, |
87 | { "aslax3" , REG_AX, REG_AX | REG_TMP1 }, |
88 | { "aslax4" , REG_AX, REG_AX | REG_TMP1 }, |
89 | { "aslaxy" , REG_AXY, REG_AXY | REG_TMP1 }, |
90 | { "asleax1" , REG_EAX, REG_EAX | REG_TMP1 }, |
91 | { "asleax2" , REG_EAX, REG_EAX | REG_TMP1 }, |
92 | { "asleax3" , REG_EAX, REG_EAX | REG_TMP1 }, |
93 | { "asleax4" , REG_EAX, REG_EAXY | REG_TMP1 }, |
94 | { "asrax1" , REG_AX, REG_AX | REG_TMP1 }, |
95 | { "asrax2" , REG_AX, REG_AX | REG_TMP1 }, |
96 | { "asrax3" , REG_AX, REG_AX | REG_TMP1 }, |
97 | { "asrax4" , REG_AX, REG_AX | REG_TMP1 }, |
98 | { "asraxy" , REG_AXY, REG_AXY | REG_TMP1 }, |
99 | { "asreax1" , REG_EAX, REG_EAX | REG_TMP1 }, |
100 | { "asreax2" , REG_EAX, REG_EAX | REG_TMP1 }, |
101 | { "asreax3" , REG_EAX, REG_EAX | REG_TMP1 }, |
102 | { "asreax4" , REG_EAX, REG_EAXY | REG_TMP1 }, |
103 | { "bnega" , REG_A, REG_AX }, |
104 | { "bnegax" , REG_AX, REG_AX }, |
105 | { "bnegeax" , REG_EAX, REG_EAX }, |
106 | { "booleq" , REG_NONE, REG_AX }, |
107 | { "boolge" , REG_NONE, REG_AX }, |
108 | { "boolgt" , REG_NONE, REG_AX }, |
109 | { "boolle" , REG_NONE, REG_AX }, |
110 | { "boollt" , REG_NONE, REG_AX }, |
111 | { "boolne" , REG_NONE, REG_AX }, |
112 | { "booluge" , REG_NONE, REG_AX }, |
113 | { "boolugt" , REG_NONE, REG_AX }, |
114 | { "boolule" , REG_NONE, REG_AX }, |
115 | { "boolult" , REG_NONE, REG_AX }, |
116 | { "callax" , REG_AX, REG_ALL }, |
117 | { "complax" , REG_AX, REG_AX }, |
118 | { "decax1" , REG_AX, REG_AX }, |
119 | { "decax2" , REG_AX, REG_AX }, |
120 | { "decax3" , REG_AX, REG_AX }, |
121 | { "decax4" , REG_AX, REG_AX }, |
122 | { "decax5" , REG_AX, REG_AX }, |
123 | { "decax6" , REG_AX, REG_AX }, |
124 | { "decax7" , REG_AX, REG_AX }, |
125 | { "decax8" , REG_AX, REG_AX }, |
126 | { "decaxy" , REG_AXY, REG_AX | REG_TMP1 }, |
127 | { "deceaxy" , REG_EAXY, REG_EAX }, |
128 | { "decsp1" , REG_NONE, REG_Y }, |
129 | { "decsp2" , REG_NONE, REG_A }, |
130 | { "decsp3" , REG_NONE, REG_A }, |
131 | { "decsp4" , REG_NONE, REG_A }, |
132 | { "decsp5" , REG_NONE, REG_A }, |
133 | { "decsp6" , REG_NONE, REG_A }, |
134 | { "decsp7" , REG_NONE, REG_A }, |
135 | { "decsp8" , REG_NONE, REG_A }, |
136 | { "incax1" , REG_AX, REG_AX }, |
137 | { "incax2" , REG_AX, REG_AX }, |
138 | { "incax3" , REG_AX, REG_AXY | REG_TMP1 }, |
139 | { "incax4" , REG_AX, REG_AXY | REG_TMP1 }, |
140 | { "incax5" , REG_AX, REG_AXY | REG_TMP1 }, |
141 | { "incax6" , REG_AX, REG_AXY | REG_TMP1 }, |
142 | { "incax7" , REG_AX, REG_AXY | REG_TMP1 }, |
143 | { "incax8" , REG_AX, REG_AXY | REG_TMP1 }, |
144 | { "incaxy" , REG_AXY, REG_AXY | REG_TMP1 }, |
145 | { "incsp1" , REG_NONE, REG_NONE }, |
146 | { "incsp2" , REG_NONE, REG_Y }, |
147 | { "incsp3" , REG_NONE, REG_Y }, |
148 | { "incsp4" , REG_NONE, REG_Y }, |
149 | { "incsp5" , REG_NONE, REG_Y }, |
150 | { "incsp6" , REG_NONE, REG_Y }, |
151 | { "incsp7" , REG_NONE, REG_Y }, |
152 | { "incsp8" , REG_NONE, REG_Y }, |
153 | { "laddeq" , REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
154 | { "laddeq0sp" , REG_EAX, REG_EAXY }, |
155 | { "laddeq1" , REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
156 | { "laddeqa" , REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
157 | { "laddeqysp" , REG_EAXY, REG_EAXY }, |
158 | { "ldaidx" , REG_AXY, REG_AX | REG_PTR1 }, |
159 | { "ldauidx" , REG_AXY, REG_AX | REG_PTR1 }, |
160 | { "ldax0sp" , REG_NONE, REG_AXY }, |
161 | { "ldaxi" , REG_AX, REG_AXY | REG_PTR1 }, |
162 | { "ldaxidx" , REG_AXY, REG_AXY | REG_PTR1 }, |
163 | { "ldaxysp" , REG_Y, REG_AXY }, |
164 | { "ldeax0sp" , REG_NONE, REG_EAXY }, |
165 | { "ldeaxi" , REG_AX, REG_EAXY | REG_PTR1 }, |
166 | { "ldeaxidx" , REG_AXY, REG_EAXY | REG_PTR1 }, |
167 | { "ldeaxysp" , REG_Y, REG_EAXY }, |
168 | { "leaa0sp" , REG_A, REG_AX }, |
169 | { "leaaxsp" , REG_AX, REG_AX }, |
170 | { "lsubeq" , REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
171 | { "lsubeq0sp" , REG_EAX, REG_EAXY }, |
172 | { "lsubeq1" , REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
173 | { "lsubeqa" , REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, |
174 | { "lsubeqysp" , REG_EAXY, REG_EAXY }, |
175 | { "mulax10" , REG_AX, REG_AX | REG_PTR1 }, |
176 | { "mulax3" , REG_AX, REG_AX | REG_PTR1 }, |
177 | { "mulax5" , REG_AX, REG_AX | REG_PTR1 }, |
178 | { "mulax6" , REG_AX, REG_AX | REG_PTR1 }, |
179 | { "mulax7" , REG_AX, REG_AX | REG_PTR1 }, |
180 | { "mulax9" , REG_AX, REG_AX | REG_PTR1 }, |
181 | { "negax" , REG_AX, REG_AX }, |
182 | { "push0" , REG_NONE, REG_AXY }, |
183 | { "push0ax" , REG_AX, REG_Y | REG_SREG }, |
184 | { "push1" , REG_NONE, REG_AXY }, |
185 | { "push2" , REG_NONE, REG_AXY }, |
186 | { "push3" , REG_NONE, REG_AXY }, |
187 | { "push4" , REG_NONE, REG_AXY }, |
188 | { "push5" , REG_NONE, REG_AXY }, |
189 | { "push6" , REG_NONE, REG_AXY }, |
190 | { "push7" , REG_NONE, REG_AXY }, |
191 | { "pusha" , REG_A, REG_Y }, |
192 | { "pusha0" , REG_A, REG_XY }, |
193 | { "pusha0sp" , REG_NONE, REG_AY }, |
194 | { "pushaFF" , REG_A, REG_Y }, |
195 | { "pushax" , REG_AX, REG_Y }, |
196 | { "pushaysp" , REG_Y, REG_AY }, |
197 | { "pushc0" , REG_NONE, REG_A | REG_Y }, |
198 | { "pushc1" , REG_NONE, REG_A | REG_Y }, |
199 | { "pushc2" , REG_NONE, REG_A | REG_Y }, |
200 | { "pusheax" , REG_EAX, REG_Y }, |
201 | { "pushl0" , REG_NONE, REG_AXY }, |
202 | { "pushw" , REG_AX, REG_AXY | REG_PTR1 }, |
203 | { "pushw0sp" , REG_NONE, REG_AXY }, |
204 | { "pushwidx" , REG_AXY, REG_AXY | REG_PTR1 }, |
205 | { "pushwysp" , REG_Y, REG_AXY }, |
206 | { "regswap" , REG_AXY, REG_AXY | REG_TMP1 }, |
207 | { "regswap1" , REG_XY, REG_A }, |
208 | { "regswap2" , REG_XY, REG_A | REG_Y }, |
209 | { "return0" , REG_NONE, REG_AX }, |
210 | { "return1" , REG_NONE, REG_AX }, |
211 | { "shlax1" , REG_AX, REG_AX | REG_TMP1 }, |
212 | { "shlax2" , REG_AX, REG_AX | REG_TMP1 }, |
213 | { "shlax3" , REG_AX, REG_AX | REG_TMP1 }, |
214 | { "shlax4" , REG_AX, REG_AX | REG_TMP1 }, |
215 | { "shlaxy" , REG_AXY, REG_AXY | REG_TMP1 }, |
216 | { "shleax1" , REG_EAX, REG_EAX | REG_TMP1 }, |
217 | { "shleax2" , REG_EAX, REG_EAX | REG_TMP1 }, |
218 | { "shleax3" , REG_EAX, REG_EAX | REG_TMP1 }, |
219 | { "shleax4" , REG_EAX, REG_EAXY | REG_TMP1 }, |
220 | { "shrax1" , REG_AX, REG_AX | REG_TMP1 }, |
221 | { "shrax2" , REG_AX, REG_AX | REG_TMP1 }, |
222 | { "shrax3" , REG_AX, REG_AX | REG_TMP1 }, |
223 | { "shrax4" , REG_AX, REG_AX | REG_TMP1 }, |
224 | { "shraxy" , REG_AXY, REG_AXY | REG_TMP1 }, |
225 | { "shreax1" , REG_EAX, REG_EAX | REG_TMP1 }, |
226 | { "shreax2" , REG_EAX, REG_EAX | REG_TMP1 }, |
227 | { "shreax3" , REG_EAX, REG_EAX | REG_TMP1 }, |
228 | { "shreax4" , REG_EAX, REG_EAXY | REG_TMP1 }, |
229 | { "staspidx" , REG_A | REG_Y, REG_Y | REG_TMP1 | REG_PTR1 }, |
230 | { "stax0sp" , REG_AX, REG_Y }, |
231 | { "staxspidx" , REG_AXY, REG_TMP1 | REG_PTR1 }, |
232 | { "staxysp" , REG_AXY, REG_Y }, |
233 | { "steax0sp" , REG_EAX, REG_Y }, |
234 | { "steaxysp" , REG_EAXY, REG_Y }, |
235 | { "subeq0sp" , REG_AX, REG_AXY }, |
236 | { "subeqysp" , REG_AXY, REG_AXY }, |
237 | { "subysp" , REG_Y, REG_AY }, |
238 | { "tosadd0ax" , REG_AX, REG_EAXY | REG_TMP1 }, |
239 | { "tosadda0" , REG_A, REG_AXY }, |
240 | { "tosaddax" , REG_AX, REG_AXY }, |
241 | { "tosaddeax" , REG_EAX, REG_EAXY | REG_TMP1 }, |
242 | { "tosand0ax" , REG_AX, REG_EAXY | REG_TMP1 }, |
243 | { "tosanda0" , REG_A, REG_AXY }, |
244 | { "tosandax" , REG_AX, REG_AXY }, |
245 | { "tosandeax" , REG_EAX, REG_EAXY | REG_TMP1 }, |
246 | { "tosaslax" , REG_A, REG_AXY | REG_TMP1 }, |
247 | { "tosasleax" , REG_A, REG_EAXY | REG_TMP1 }, |
248 | { "tosasrax" , REG_A, REG_AXY | REG_TMP1 }, |
249 | { "tosasreax" , REG_A, REG_EAXY | REG_TMP1 }, |
250 | { "tosdiv0ax" , REG_AX, REG_ALL }, |
251 | { "tosdiva0" , REG_A, REG_ALL }, |
252 | { "tosdivax" , REG_AX, REG_ALL }, |
253 | { "tosdiveax" , REG_EAX, REG_ALL }, |
254 | { "toseq00" , REG_NONE, REG_AXY | REG_SREG }, |
255 | { "toseqa0" , REG_A, REG_AXY | REG_SREG }, |
256 | { "toseqax" , REG_AX, REG_AXY | REG_SREG }, |
257 | { "toseqeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
258 | { "tosge00" , REG_NONE, REG_AXY | REG_SREG }, |
259 | { "tosgea0" , REG_A, REG_AXY | REG_SREG }, |
260 | { "tosgeax" , REG_AX, REG_AXY | REG_SREG }, |
261 | { "tosgeeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
262 | { "tosgt00" , REG_NONE, REG_AXY | REG_SREG }, |
263 | { "tosgta0" , REG_A, REG_AXY | REG_SREG }, |
264 | { "tosgtax" , REG_AX, REG_AXY | REG_SREG }, |
265 | { "tosgteax" , REG_EAX, REG_AXY | REG_PTR1 }, |
266 | { "tosicmp" , REG_AX, REG_AXY | REG_SREG }, |
267 | { "tosicmp0" , REG_A, REG_AXY | REG_SREG }, |
268 | { "toslcmp" , REG_EAX, REG_A | REG_Y | REG_PTR1 }, |
269 | { "tosle00" , REG_NONE, REG_AXY | REG_SREG }, |
270 | { "toslea0" , REG_A, REG_AXY | REG_SREG }, |
271 | { "tosleax" , REG_AX, REG_AXY | REG_SREG }, |
272 | { "tosleeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
273 | { "toslt00" , REG_NONE, REG_AXY | REG_SREG }, |
274 | { "toslta0" , REG_A, REG_AXY | REG_SREG }, |
275 | { "tosltax" , REG_AX, REG_AXY | REG_SREG }, |
276 | { "toslteax" , REG_EAX, REG_AXY | REG_PTR1 }, |
277 | { "tosmod0ax" , REG_AX, REG_ALL }, |
278 | { "tosmodeax" , REG_EAX, REG_ALL }, |
279 | { "tosmul0ax" , REG_AX, REG_ALL }, |
280 | { "tosmula0" , REG_A, REG_ALL }, |
281 | { "tosmulax" , REG_AX, REG_ALL }, |
282 | { "tosmuleax" , REG_EAX, REG_ALL }, |
283 | { "tosne00" , REG_NONE, REG_AXY | REG_SREG }, |
284 | { "tosnea0" , REG_A, REG_AXY | REG_SREG }, |
285 | { "tosneax" , REG_AX, REG_AXY | REG_SREG }, |
286 | { "tosneeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
287 | { "tosor0ax" , REG_AX, REG_EAXY | REG_TMP1 }, |
288 | { "tosora0" , REG_A, REG_AXY | REG_TMP1 }, |
289 | { "tosorax" , REG_AX, REG_AXY | REG_TMP1 }, |
290 | { "tosoreax" , REG_EAX, REG_EAXY | REG_TMP1 }, |
291 | { "tosrsub0ax" , REG_AX, REG_EAXY | REG_TMP1 }, |
292 | { "tosrsuba0" , REG_A, REG_AXY | REG_TMP1 }, |
293 | { "tosrsubax" , REG_AX, REG_AXY | REG_TMP1 }, |
294 | { "tosrsubeax" , REG_EAX, REG_EAXY | REG_TMP1 }, |
295 | { "tosshlax" , REG_A, REG_AXY | REG_TMP1 }, |
296 | { "tosshleax" , REG_A, REG_EAXY | REG_TMP1 }, |
297 | { "tosshrax" , REG_A, REG_AXY | REG_TMP1 }, |
298 | { "tosshreax" , REG_A, REG_EAXY | REG_TMP1 }, |
299 | { "tossub0ax" , REG_AX, REG_EAXY }, |
300 | { "tossuba0" , REG_A, REG_AXY }, |
301 | { "tossubax" , REG_AX, REG_AXY }, |
302 | { "tossubeax" , REG_EAX, REG_EAXY }, |
303 | { "tosudiv0ax" , REG_AX, REG_ALL & ~REG_SAVE }, |
304 | { "tosudiva0" , REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ |
305 | { "tosudivax" , REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ |
306 | { "tosudiveax" , REG_EAX, REG_ALL & ~REG_SAVE }, |
307 | { "tosuge00" , REG_NONE, REG_AXY | REG_SREG }, |
308 | { "tosugea0" , REG_A, REG_AXY | REG_SREG }, |
309 | { "tosugeax" , REG_AX, REG_AXY | REG_SREG }, |
310 | { "tosugeeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
311 | { "tosugt00" , REG_NONE, REG_AXY | REG_SREG }, |
312 | { "tosugta0" , REG_A, REG_AXY | REG_SREG }, |
313 | { "tosugtax" , REG_AX, REG_AXY | REG_SREG }, |
314 | { "tosugteax" , REG_EAX, REG_AXY | REG_PTR1 }, |
315 | { "tosule00" , REG_NONE, REG_AXY | REG_SREG }, |
316 | { "tosulea0" , REG_A, REG_AXY | REG_SREG }, |
317 | { "tosuleax" , REG_AX, REG_AXY | REG_SREG }, |
318 | { "tosuleeax" , REG_EAX, REG_AXY | REG_PTR1 }, |
319 | { "tosult00" , REG_NONE, REG_AXY | REG_SREG }, |
320 | { "tosulta0" , REG_A, REG_AXY | REG_SREG }, |
321 | { "tosultax" , REG_AX, REG_AXY | REG_SREG }, |
322 | { "tosulteax" , REG_EAX, REG_AXY | REG_PTR1 }, |
323 | { "tosumod0ax" , REG_AX, REG_ALL & ~REG_SAVE }, |
324 | { "tosumoda0" , REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ |
325 | { "tosumodax" , REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ |
326 | { "tosumodeax" , REG_EAX, REG_ALL & ~REG_SAVE }, |
327 | { "tosumul0ax" , REG_AX, REG_ALL }, |
328 | { "tosumula0" , REG_A, REG_ALL }, |
329 | { "tosumulax" , REG_AX, REG_ALL }, |
330 | { "tosumuleax" , REG_EAX, REG_ALL }, |
331 | { "tosxor0ax" , REG_AX, REG_EAXY | REG_TMP1 }, |
332 | { "tosxora0" , REG_A, REG_AXY | REG_TMP1 }, |
333 | { "tosxorax" , REG_AX, REG_AXY | REG_TMP1 }, |
334 | { "tosxoreax" , REG_EAX, REG_EAXY | REG_TMP1 }, |
335 | { "tsteax" , REG_EAX, REG_Y }, |
336 | { "utsteax" , REG_EAX, REG_Y }, |
337 | }; |
338 | #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) |
339 | |
340 | /* Table with names of zero page locations used by the compiler */ |
341 | static const ZPInfo ZPInfoTable[] = { |
342 | { 0, "ptr1" , REG_PTR1_LO, REG_PTR1 }, |
343 | { 0, "ptr1+1" , REG_PTR1_HI, REG_PTR1 }, |
344 | { 0, "ptr2" , REG_PTR2_LO, REG_PTR2 }, |
345 | { 0, "ptr2+1" , REG_PTR2_HI, REG_PTR2 }, |
346 | { 4, "ptr3" , REG_NONE, REG_NONE }, |
347 | { 4, "ptr4" , REG_NONE, REG_NONE }, |
348 | { 7, "regbank" , REG_NONE, REG_NONE }, |
349 | { 0, "regsave" , REG_SAVE_LO, REG_SAVE }, |
350 | { 0, "regsave+1" , REG_SAVE_HI, REG_SAVE }, |
351 | { 0, "sp" , REG_SP_LO, REG_SP }, |
352 | { 0, "sp+1" , REG_SP_HI, REG_SP }, |
353 | { 0, "sreg" , REG_SREG_LO, REG_SREG }, |
354 | { 0, "sreg+1" , REG_SREG_HI, REG_SREG }, |
355 | { 0, "tmp1" , REG_TMP1, REG_TMP1 }, |
356 | { 0, "tmp2" , REG_NONE, REG_NONE }, |
357 | { 0, "tmp3" , REG_NONE, REG_NONE }, |
358 | { 0, "tmp4" , REG_NONE, REG_NONE }, |
359 | }; |
360 | #define ZPInfoCount (sizeof(ZPInfoTable) / sizeof(ZPInfoTable[0])) |
361 | |
362 | |
363 | |
364 | /*****************************************************************************/ |
365 | /* Code */ |
366 | /*****************************************************************************/ |
367 | |
368 | |
369 | |
370 | static int CompareFuncInfo (const void* Key, const void* Info) |
371 | /* Compare function for bsearch */ |
372 | { |
373 | return strcmp (Key, ((const FuncInfo*) Info)->Name); |
374 | } |
375 | |
376 | |
377 | |
378 | void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) |
379 | /* For the given function, lookup register information and store it into |
380 | ** the given variables. If the function is unknown, assume it will use and |
381 | ** load all registers. |
382 | */ |
383 | { |
384 | /* If the function name starts with an underline, it is an external |
385 | ** function. Search for it in the symbol table. If the function does |
386 | ** not start with an underline, it may be a runtime support function. |
387 | ** Search for it in the list of builtin functions. |
388 | */ |
389 | if (Name[0] == '_') { |
390 | /* Search in the symbol table, skip the leading underscore */ |
391 | SymEntry* E = FindGlobalSym (Name+1); |
392 | |
393 | /* Did we find it in the top-level table? */ |
394 | if (E && IsTypeFunc (E->Type)) { |
395 | FuncDesc* D = E->V.F.Func; |
396 | |
397 | /* A variadic function will use the Y register (the parameter list |
398 | ** size is passed there). A fastcall function will use the A or A/X |
399 | ** registers. In all other cases, no registers are used. However, |
400 | ** we assume that any function will destroy all registers. |
401 | */ |
402 | if ((D->Flags & FD_VARIADIC) != 0) { |
403 | *Use = REG_Y; |
404 | } else if (D->Flags & FD_CALL_WRAPPER) { |
405 | /* Wrappers may go to any functions, so mark them as using all |
406 | ** registers. |
407 | */ |
408 | *Use = REG_EAXY; |
409 | } else if (D->ParamCount > 0 && |
410 | (AutoCDecl ? |
411 | IsQualFastcall (E->Type) : |
412 | !IsQualCDecl (E->Type))) { |
413 | /* Will use registers depending on the last param. */ |
414 | switch (CheckedSizeOf (D->LastParam->Type)) { |
415 | case 1u: |
416 | *Use = REG_A; |
417 | break; |
418 | case 2u: |
419 | *Use = REG_AX; |
420 | break; |
421 | default: |
422 | *Use = REG_EAX; |
423 | } |
424 | } else { |
425 | /* Will not use any registers */ |
426 | *Use = REG_NONE; |
427 | } |
428 | |
429 | /* Will destroy all registers */ |
430 | *Chg = REG_ALL; |
431 | |
432 | /* Done */ |
433 | return; |
434 | } |
435 | |
436 | } else if (IsDigit (Name[0]) || Name[0] == '$') { |
437 | |
438 | /* A call to a numeric address. Assume that anything gets used and |
439 | ** destroyed. This is not a real problem, since numeric addresses |
440 | ** are used mostly in inline assembly anyway. |
441 | */ |
442 | *Use = REG_ALL; |
443 | *Chg = REG_ALL; |
444 | return; |
445 | |
446 | } else { |
447 | |
448 | /* Search for the function in the list of builtin functions */ |
449 | const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount, |
450 | sizeof(FuncInfo), CompareFuncInfo); |
451 | |
452 | /* Do we know the function? */ |
453 | if (Info) { |
454 | /* Use the information we have */ |
455 | *Use = Info->Use; |
456 | *Chg = Info->Chg; |
457 | } else { |
458 | /* It's an internal function we have no information for. If in |
459 | ** debug mode, output an additional warning, so we have a chance |
460 | ** to fix it. Otherwise assume that the internal function will |
461 | ** use and change all registers. |
462 | */ |
463 | if (Debug) { |
464 | fprintf (stderr, "No info about internal function '%s'\n" , Name); |
465 | } |
466 | *Use = REG_ALL; |
467 | *Chg = REG_ALL; |
468 | } |
469 | return; |
470 | } |
471 | |
472 | /* Function not found - assume that the primary register is input, and all |
473 | ** registers are changed |
474 | */ |
475 | *Use = REG_EAXY; |
476 | *Chg = REG_ALL; |
477 | } |
478 | |
479 | |
480 | |
481 | static int CompareZPInfo (const void* Name, const void* Info) |
482 | /* Compare function for bsearch */ |
483 | { |
484 | /* Cast the pointers to the correct data type */ |
485 | const char* N = (const char*) Name; |
486 | const ZPInfo* E = (const ZPInfo*) Info; |
487 | |
488 | /* Do the compare. Be careful because of the length (Info may contain |
489 | ** more than just the zeropage name). |
490 | */ |
491 | if (E->Len == 0) { |
492 | /* Do a full compare */ |
493 | return strcmp (N, E->Name); |
494 | } else { |
495 | /* Only compare the first part */ |
496 | int Res = strncmp (N, E->Name, E->Len); |
497 | if (Res == 0 && (N[E->Len] != '\0' && N[E->Len] != '+')) { |
498 | /* Name is actually longer than Info->Name */ |
499 | Res = -1; |
500 | } |
501 | return Res; |
502 | } |
503 | } |
504 | |
505 | |
506 | |
507 | const ZPInfo* GetZPInfo (const char* Name) |
508 | /* If the given name is a zero page symbol, return a pointer to the info |
509 | ** struct for this symbol, otherwise return NULL. |
510 | */ |
511 | { |
512 | /* Search for the zp location in the list */ |
513 | return bsearch (Name, ZPInfoTable, ZPInfoCount, |
514 | sizeof(ZPInfo), CompareZPInfo); |
515 | } |
516 | |
517 | |
518 | |
519 | static unsigned GetRegInfo2 (CodeSeg* S, |
520 | CodeEntry* E, |
521 | int Index, |
522 | Collection* Visited, |
523 | unsigned Used, |
524 | unsigned Unused, |
525 | unsigned Wanted) |
526 | /* Recursively called subfunction for GetRegInfo. */ |
527 | { |
528 | /* Follow the instruction flow recording register usage. */ |
529 | while (1) { |
530 | |
531 | unsigned R; |
532 | |
533 | /* Check if we have already visited the current code entry. If so, |
534 | ** bail out. |
535 | */ |
536 | if (CE_HasMark (E)) { |
537 | break; |
538 | } |
539 | |
540 | /* Mark this entry as already visited */ |
541 | CE_SetMark (E); |
542 | CollAppend (Visited, E); |
543 | |
544 | /* Evaluate the used registers */ |
545 | R = E->Use; |
546 | if (E->OPC == OP65_RTS || |
547 | ((E->Info & OF_UBRA) != 0 && E->JumpTo == 0)) { |
548 | /* This instruction will leave the function */ |
549 | R |= S->ExitRegs; |
550 | } |
551 | if (R != REG_NONE) { |
552 | /* We are not interested in the use of any register that has been |
553 | ** used before. |
554 | */ |
555 | R &= ~Unused; |
556 | /* Remember the remaining registers */ |
557 | Used |= R; |
558 | } |
559 | |
560 | /* Evaluate the changed registers */ |
561 | if ((R = E->Chg) != REG_NONE) { |
562 | /* We are not interested in the use of any register that has been |
563 | ** used before. |
564 | */ |
565 | R &= ~Used; |
566 | /* Remember the remaining registers */ |
567 | Unused |= R; |
568 | } |
569 | |
570 | /* If we know about all registers now, bail out */ |
571 | if (((Used | Unused) & Wanted) == Wanted) { |
572 | break; |
573 | } |
574 | |
575 | /* If the instruction is an RTS or RTI, we're done */ |
576 | if ((E->Info & OF_RET) != 0) { |
577 | break; |
578 | } |
579 | |
580 | /* If we have an unconditional branch, follow this branch if possible, |
581 | ** otherwise we're done. |
582 | */ |
583 | if ((E->Info & OF_UBRA) != 0) { |
584 | |
585 | /* Does this jump have a valid target? */ |
586 | if (E->JumpTo) { |
587 | |
588 | /* Unconditional jump */ |
589 | E = E->JumpTo->Owner; |
590 | Index = -1; /* Invalidate */ |
591 | |
592 | } else { |
593 | /* Jump outside means we're done */ |
594 | break; |
595 | } |
596 | |
597 | /* In case of conditional branches, follow the branch if possible and |
598 | ** follow the normal flow (branch not taken) afterwards. If we cannot |
599 | ** follow the branch, we're done. |
600 | */ |
601 | } else if ((E->Info & OF_CBRA) != 0) { |
602 | |
603 | /* Recursively determine register usage at the branch target */ |
604 | unsigned U1; |
605 | unsigned U2; |
606 | |
607 | if (E->JumpTo) { |
608 | |
609 | /* Jump to internal label */ |
610 | U1 = GetRegInfo2 (S, E->JumpTo->Owner, -1, Visited, Used, Unused, Wanted); |
611 | |
612 | } else { |
613 | |
614 | /* Jump to external label. This will effectively exit the |
615 | ** function, so we use the exitregs information here. |
616 | */ |
617 | U1 = S->ExitRegs; |
618 | |
619 | } |
620 | |
621 | /* Get the next entry */ |
622 | if (Index < 0) { |
623 | Index = CS_GetEntryIndex (S, E); |
624 | } |
625 | if ((E = CS_GetEntry (S, ++Index)) == 0) { |
626 | Internal ("GetRegInfo2: No next entry!" ); |
627 | } |
628 | |
629 | /* Follow flow if branch not taken */ |
630 | U2 = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); |
631 | |
632 | /* Registers are used if they're use in any of the branches */ |
633 | return U1 | U2; |
634 | |
635 | } else { |
636 | |
637 | /* Just go to the next instruction */ |
638 | if (Index < 0) { |
639 | Index = CS_GetEntryIndex (S, E); |
640 | } |
641 | E = CS_GetEntry (S, ++Index); |
642 | if (E == 0) { |
643 | /* No next entry */ |
644 | Internal ("GetRegInfo2: No next entry!" ); |
645 | } |
646 | |
647 | } |
648 | |
649 | } |
650 | |
651 | /* Return to the caller the complement of all unused registers */ |
652 | return Used; |
653 | } |
654 | |
655 | |
656 | |
657 | static unsigned GetRegInfo1 (CodeSeg* S, |
658 | CodeEntry* E, |
659 | int Index, |
660 | Collection* Visited, |
661 | unsigned Used, |
662 | unsigned Unused, |
663 | unsigned Wanted) |
664 | /* Recursively called subfunction for GetRegInfo. */ |
665 | { |
666 | /* Remember the current count of the line collection */ |
667 | unsigned Count = CollCount (Visited); |
668 | |
669 | /* Call the worker routine */ |
670 | unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); |
671 | |
672 | /* Restore the old count, unmarking all new entries */ |
673 | unsigned NewCount = CollCount (Visited); |
674 | while (NewCount-- > Count) { |
675 | CodeEntry* E = CollAt (Visited, NewCount); |
676 | CE_ResetMark (E); |
677 | CollDelete (Visited, NewCount); |
678 | } |
679 | |
680 | /* Return the registers used */ |
681 | return R; |
682 | } |
683 | |
684 | |
685 | |
686 | unsigned GetRegInfo (struct CodeSeg* S, unsigned Index, unsigned Wanted) |
687 | /* Determine register usage information for the instructions starting at the |
688 | ** given index. |
689 | */ |
690 | { |
691 | CodeEntry* E; |
692 | Collection Visited; /* Visited entries */ |
693 | unsigned R; |
694 | |
695 | /* Get the code entry for the given index */ |
696 | if (Index >= CS_GetEntryCount (S)) { |
697 | /* There is no such code entry */ |
698 | return REG_NONE; |
699 | } |
700 | E = CS_GetEntry (S, Index); |
701 | |
702 | /* Initialize the data structure used to collection information */ |
703 | InitCollection (&Visited); |
704 | |
705 | /* Call the recursive subfunction */ |
706 | R = GetRegInfo1 (S, E, Index, &Visited, REG_NONE, REG_NONE, Wanted); |
707 | |
708 | /* Delete the line collection */ |
709 | DoneCollection (&Visited); |
710 | |
711 | /* Return the registers used */ |
712 | return R; |
713 | } |
714 | |
715 | |
716 | |
717 | int RegAUsed (struct CodeSeg* S, unsigned Index) |
718 | /* Check if the value in A is used. */ |
719 | { |
720 | return (GetRegInfo (S, Index, REG_A) & REG_A) != 0; |
721 | } |
722 | |
723 | |
724 | |
725 | int RegXUsed (struct CodeSeg* S, unsigned Index) |
726 | /* Check if the value in X is used. */ |
727 | { |
728 | return (GetRegInfo (S, Index, REG_X) & REG_X) != 0; |
729 | } |
730 | |
731 | |
732 | |
733 | int RegYUsed (struct CodeSeg* S, unsigned Index) |
734 | /* Check if the value in Y is used. */ |
735 | { |
736 | return (GetRegInfo (S, Index, REG_Y) & REG_Y) != 0; |
737 | } |
738 | |
739 | |
740 | |
741 | int RegAXUsed (struct CodeSeg* S, unsigned Index) |
742 | /* Check if the value in A or(!) the value in X are used. */ |
743 | { |
744 | return (GetRegInfo (S, Index, REG_AX) & REG_AX) != 0; |
745 | } |
746 | |
747 | |
748 | |
749 | int RegEAXUsed (struct CodeSeg* S, unsigned Index) |
750 | /* Check if any of the four bytes in EAX are used. */ |
751 | { |
752 | return (GetRegInfo (S, Index, REG_EAX) & REG_EAX) != 0; |
753 | } |
754 | |
755 | |
756 | |
757 | unsigned GetKnownReg (unsigned Use, const RegContents* RC) |
758 | /* Return the register or zero page location from the set in Use, thats |
759 | ** contents are known. If Use does not contain any register, or if the |
760 | ** register in question does not have a known value, return REG_NONE. |
761 | */ |
762 | { |
763 | if ((Use & REG_A) != 0) { |
764 | return (RC == 0 || RC->RegA >= 0)? REG_A : REG_NONE; |
765 | } else if ((Use & REG_X) != 0) { |
766 | return (RC == 0 || RC->RegX >= 0)? REG_X : REG_NONE; |
767 | } else if ((Use & REG_Y) != 0) { |
768 | return (RC == 0 || RC->RegY >= 0)? REG_Y : REG_NONE; |
769 | } else if ((Use & REG_TMP1) != 0) { |
770 | return (RC == 0 || RC->Tmp1 >= 0)? REG_TMP1 : REG_NONE; |
771 | } else if ((Use & REG_PTR1_LO) != 0) { |
772 | return (RC == 0 || RC->Ptr1Lo >= 0)? REG_PTR1_LO : REG_NONE; |
773 | } else if ((Use & REG_PTR1_HI) != 0) { |
774 | return (RC == 0 || RC->Ptr1Hi >= 0)? REG_PTR1_HI : REG_NONE; |
775 | } else if ((Use & REG_SREG_LO) != 0) { |
776 | return (RC == 0 || RC->SRegLo >= 0)? REG_SREG_LO : REG_NONE; |
777 | } else if ((Use & REG_SREG_HI) != 0) { |
778 | return (RC == 0 || RC->SRegHi >= 0)? REG_SREG_HI : REG_NONE; |
779 | } else { |
780 | return REG_NONE; |
781 | } |
782 | } |
783 | |
784 | |
785 | |
786 | static cmp_t FindCmpCond (const char* Code, unsigned CodeLen) |
787 | /* Search for a compare condition by the given code using the given length */ |
788 | { |
789 | unsigned I; |
790 | |
791 | /* Linear search */ |
792 | for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) { |
793 | if (strncmp (Code, CmpSuffixTab [I], CodeLen) == 0) { |
794 | /* Found */ |
795 | return I; |
796 | } |
797 | } |
798 | |
799 | /* Not found */ |
800 | return CMP_INV; |
801 | } |
802 | |
803 | |
804 | |
805 | cmp_t FindBoolCmpCond (const char* Name) |
806 | /* Check if the given string is the name of one of the boolean transformer |
807 | ** subroutine, and if so, return the condition that is evaluated by this |
808 | ** routine. Return CMP_INV if the condition is not recognised. |
809 | */ |
810 | { |
811 | /* Check for the correct subroutine name */ |
812 | if (strncmp (Name, "bool" , 4) == 0) { |
813 | /* Name is ok, search for the code in the table */ |
814 | return FindCmpCond (Name+4, strlen(Name)-4); |
815 | } else { |
816 | /* Not found */ |
817 | return CMP_INV; |
818 | } |
819 | } |
820 | |
821 | |
822 | |
823 | cmp_t FindTosCmpCond (const char* Name) |
824 | /* Check if this is a call to one of the TOS compare functions (tosgtax). |
825 | ** Return the condition code or CMP_INV on failure. |
826 | */ |
827 | { |
828 | unsigned Len = strlen (Name); |
829 | |
830 | /* Check for the correct subroutine name */ |
831 | if (strncmp (Name, "tos" , 3) == 0 && strcmp (Name+Len-2, "ax" ) == 0) { |
832 | /* Name is ok, search for the code in the table */ |
833 | return FindCmpCond (Name+3, Len-3-2); |
834 | } else { |
835 | /* Not found */ |
836 | return CMP_INV; |
837 | } |
838 | } |
839 | |