Commit | Line | Data |
---|---|---|
54cc8ed4 DE |
1 | /* Opcode table for the TXVU |
2 | Copyright 1998 Free Software Foundation, Inc. | |
3 | ||
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation; either version 2, or (at your option) | |
7 | any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License along | |
15 | with this program; if not, write to the Free Software Foundation, Inc., | |
16 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
17 | ||
18 | #include "ansidecl.h" | |
19 | #include "dis-asm.h" | |
20 | #include "opcode/txvu.h" | |
21 | ||
22 | #ifndef NULL | |
23 | #define NULL 0 | |
24 | #endif | |
25 | ||
26 | #if defined (__STDC__) || defined (ALMOST_STDC) | |
27 | #define XCONCAT2(a,b) a##b | |
28 | #else | |
29 | #define XCONCAT2(a,b) a/**/b | |
30 | #endif | |
31 | #define CONCAT2(a,b) XCONCAT2(a,b) | |
32 | ||
33 | /* ??? One can argue it's preferable to have the PARSE_FN support in tc-vxvu.c | |
34 | and the PRINT_FN support in txvu-dis.c. For this project I like having | |
35 | them all in one place. */ | |
36 | ||
37 | #define PARSE_FN(fn) \ | |
38 | static long CONCAT2 (parse_,fn) \ | |
39 | PARAMS ((char **, const char **)); | |
40 | #define INSERT_FN(fn) \ | |
41 | static TXVU_INSN CONCAT2 (insert_,fn) \ | |
42 | PARAMS ((TXVU_INSN, const struct txvu_operand *, \ | |
43 | int, long, const char **)) | |
44 | #define EXTRACT_FN(fn) \ | |
45 | static long CONCAT2 (extract_,fn) \ | |
46 | PARAMS ((TXVU_INSN, const struct txvu_operand *, \ | |
47 | int, int *)) | |
48 | #define PRINT_FN(fn) \ | |
49 | static void CONCAT2 (print_,fn) \ | |
50 | PARAMS ((disassemble_info *, TXVU_INSN, long)); | |
51 | ||
52 | PARSE_FN (dotdest); | |
71af45ec DE |
53 | INSERT_FN (dotdest); |
54 | EXTRACT_FN (dotdest); | |
54cc8ed4 DE |
55 | PRINT_FN (dotdest); |
56 | ||
71af45ec DE |
57 | PARSE_FN (vfreg); |
58 | PRINT_FN (vfreg); | |
54cc8ed4 | 59 | |
3f897263 DE |
60 | PARSE_FN (bc); |
61 | PRINT_FN (bc); | |
62 | ||
63 | PARSE_FN (ftregbc); | |
64 | PRINT_FN (ftregbc); | |
65 | ||
66 | PARSE_FN (accdest); | |
67 | PRINT_FN (accdest); | |
68 | ||
69 | PARSE_FN (xyz); | |
70 | ||
71 | /* Various types of TXVU operands, including insn suffixes. | |
54cc8ed4 DE |
72 | |
73 | Fields are: | |
74 | ||
75 | BITS SHIFT FLAGS PARSE_FN INSERT_FN EXTRACT_FN PRINT_FN | |
76 | ||
77 | Operand values are 128 + table index. This allows ASCII chars to be | |
78 | included in the syntax spec. */ | |
79 | ||
80 | const struct txvu_operand txvu_operands[] = | |
81 | { | |
3f897263 | 82 | /* place holder (??? not sure if needed) */ |
54cc8ed4 DE |
83 | #define UNUSED 128 |
84 | { 0 }, | |
85 | ||
3f897263 | 86 | /* Destination indicator, with leading '.'. */ |
54cc8ed4 DE |
87 | #define DOTDEST (UNUSED + 1) |
88 | { 4, TXVU_SHIFT_DEST, TXVU_OPERAND_SUFFIX, | |
71af45ec | 89 | parse_dotdest, insert_dotdest, extract_dotdest, print_dotdest }, |
54cc8ed4 | 90 | |
3f897263 | 91 | /* ft reg */ |
54cc8ed4 | 92 | #define FTREG (DOTDEST + 1) |
71af45ec | 93 | { 5, TXVU_SHIFT_FTREG, 0, parse_vfreg, 0, 0, print_vfreg }, |
54cc8ed4 | 94 | |
3f897263 | 95 | /* fs reg */ |
54cc8ed4 | 96 | #define FSREG (FTREG + 1) |
71af45ec | 97 | { 5, TXVU_SHIFT_FSREG, 0, parse_vfreg, 0, 0, print_vfreg }, |
54cc8ed4 | 98 | |
3f897263 | 99 | /* fd reg */ |
54cc8ed4 | 100 | #define FDREG (FSREG + 1) |
71af45ec | 101 | { 5, TXVU_SHIFT_FDREG, 0, parse_vfreg, 0, 0, print_vfreg }, |
54cc8ed4 | 102 | |
3f897263 DE |
103 | /* broadcast */ |
104 | #define BC (FDREG + 1) | |
105 | { 2, 0, 0, parse_bc, 0, 0, print_bc }, | |
106 | ||
107 | /* ftreg in broadcast case */ | |
108 | #define FTREGBC (BC + 1) | |
109 | { 5, TXVU_SHIFT_FTREG, 0, parse_ftregbc, 0, 0, print_ftregbc }, | |
110 | ||
111 | /* accumulator dest */ | |
112 | #define ACCDEST (FTREGBC + 1) | |
113 | { 0, 0, TXVU_OPERAND_FAKE, parse_accdest, 0, 0, print_accdest }, | |
114 | ||
115 | /* The XYZ operand is a fake one that is used to ensure only "xyz" is | |
116 | specified. It simplifies the opmula and opmsub entries. */ | |
117 | #define XYZ (FDREG + 1) | |
118 | { 0, 0, TXVU_OPERAND_FAKE, parse_xyz, 0, 0, 0 }, | |
119 | ||
54cc8ed4 DE |
120 | /* end of list place holder */ |
121 | { 0 } | |
122 | }; | |
123 | \f | |
124 | /* Macros to put a field's value into the right place. */ | |
3f897263 DE |
125 | /* FIXME: If assembler needs these, move to opcode/txvu.h. */ |
126 | ||
54cc8ed4 DE |
127 | #define R(x,b,m) (((x) & (m)) << (b)) /* value X, mask M, at bit B */ |
128 | ||
3f897263 DE |
129 | /* Upper Flag bits. */ |
130 | #define UF(x) R ((x), 27, 31) | |
131 | /* Upper REServed two bits next to flag bits. */ | |
132 | #define URES(x) R ((x), 25, 3) | |
133 | /* The DEST field. */ | |
134 | #define UDEST(x) R ((x), 21, 15) | |
135 | /* The FT reg field. */ | |
136 | #define UFT(x) R ((x), TXVU_SHIFT_FTREG, TXVU_MASK_VFREG) | |
137 | /* The FS reg field. */ | |
138 | #define UFS(x) R ((x), TXVU_SHIFT_FSREG, TXVU_MASK_VFREG) | |
139 | /* The FD reg field. */ | |
140 | #define UFD(x) R ((x), TXVU_SHIFT_FDREG, TXVU_MASK_VFREG) | |
141 | /* The 4 bit opcode field. */ | |
142 | #define UOP4(x) R ((x), 2, 15) | |
143 | /* The 6 bit opcode field. */ | |
144 | #define UOP6(x) R ((x), 0, 63) | |
145 | /* The 9 bit opcode field. */ | |
146 | #define UOP9(x) R ((x), 2, 0x1ff) | |
147 | /* The 11 bit opcode field. */ | |
148 | #define UOP11(x) R ((x), 0, 0x7ff) | |
149 | /* The BroadCast field. */ | |
150 | #define UBC(x) R ((x), 0, 3) | |
151 | ||
152 | /* Macros for special field values. */ | |
153 | /* The upper 7 bits of the upper word. */ | |
154 | #define UUBITS (UF (0) + URES (0)) | |
155 | /* Mask for UBITS. */ | |
156 | #define MUUBITS (UF (~0) + URES (~0)) | |
157 | /* Mask for URES. */ | |
158 | #define MURES URES (~0) | |
159 | /* Mask for OP4. */ | |
160 | #define MUOP4 UOP4 (~0) | |
161 | /* Mask for OP6. */ | |
162 | #define MUOP6 UOP6 (~0) | |
163 | /* Mask for OP9. */ | |
164 | #define MUOP9 UOP9 (~0) | |
165 | /* Mask for OP11. */ | |
166 | #define MUOP11 UOP11 (~0) | |
167 | ||
168 | /* A space, separates instruction name (mnemonic + mnemonic operands) from operands. */ | |
169 | #define SP ' ' | |
170 | ||
54cc8ed4 DE |
171 | /* TXVU instructions. |
172 | [??? some of these comments are left over from the ARC port from which | |
173 | this code is borrowed, delete in time] | |
174 | ||
175 | Longer versions of insns must appear before shorter ones (if gas sees | |
176 | "lsr r2,r3,1" when it's parsing "lsr %a,%b" it will think the ",1" is | |
177 | junk). This isn't necessary for `ld' because of the trailing ']'. | |
178 | ||
179 | Instructions that are really macros based on other insns must appear | |
180 | before the real insn so they're chosen when disassembling. Eg: The `mov' | |
181 | insn is really the `and' insn. | |
182 | ||
183 | This table is best viewed on a wide screen (161 columns). I'd prefer to | |
184 | keep it this way. The rest of the file, however, should be viewable on an | |
185 | 80 column terminal. */ | |
186 | ||
187 | /* ??? This table also includes macros: asl, lsl, and mov. The ppc port has | |
188 | a more general facility for dealing with macros which could be used if | |
189 | we need to. */ | |
190 | ||
191 | /* These tables can't be `const' because members `next_asm' and `next_dis' are | |
192 | computed at run-time. We could split this into two, as that would put the | |
193 | constant stuff into a readonly section. */ | |
194 | ||
195 | struct txvu_opcode txvu_upper_opcodes[] = { | |
196 | ||
197 | /* Macros appear first. */ | |
198 | /* ??? Any aliases? */ | |
199 | ||
3f897263 DE |
200 | /* The rest of these needn't be sorted, but it helps to find them if they are. */ |
201 | { "abs", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x1fd) }, | |
202 | { "add", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x28) }, | |
203 | { "addi", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x22) }, | |
204 | { "addq", { DOTDEST, SP, FDREG, FSREG, 'q' }, MURES + UFT (~0) + MUOP6, UOP6 (0x20) }, | |
205 | { "add", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + UOP4 (~0), UOP4 (0) }, | |
206 | { "adda", { DOTDEST, SP, ACCDEST, FSREG, FTREG }, MURES + MUOP11, UOP11 (0x2bc) }, | |
207 | { "addai", { DOTDEST, SP, ACCDEST, FSREG, 'i' }, MURES + UFT (~0) + MUOP11, UOP11 (0x23e) }, | |
208 | { "addaq", { DOTDEST, SP, ACCDEST, FSREG, 'q' }, MURES + UFT (~0) + MUOP11, UOP11 (0x23c) }, | |
209 | { "adda", { BC, DOTDEST, SP, ACCDEST, FSREG, FTREGBC }, MURES + MUOP9, UOP9 (0xf) }, | |
210 | { "clip", { DOTDEST, SP, FSREG }, MURES + UDEST (~0) + UFT (~0) + MUOP11, UDEST (0xf) + UOP11 (0x1ff) }, | |
211 | { "ftoi0", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x17c) }, | |
212 | { "ftoi4", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x17d) }, | |
213 | { "ftoi12", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x17e) }, | |
214 | { "ftoi15", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x17f) }, | |
215 | { "itof0", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x13c) }, | |
216 | { "itof4", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x13d) }, | |
217 | { "itof12", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x13e) }, | |
218 | { "itof15", { DOTDEST, SP, FTREG, FSREG }, MURES + MUOP11, UOP11 (0x13f) }, | |
219 | { "madd", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x29) }, | |
220 | { "maddi", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x23) }, | |
221 | { "maddq", { DOTDEST, SP, FDREG, FSREG, 'q' }, MURES + UFT (~0) + MUOP6, UOP6 (0x21) }, | |
222 | { "madd", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + MUOP4, UOP4 (0x2) }, | |
223 | { "madda", { DOTDEST, SP, ACCDEST, FSREG, FTREG }, MURES + MUOP11, UOP11 (0x2bd) }, | |
224 | { "maddai", { DOTDEST, SP, ACCDEST, FSREG, 'i' }, MURES + UFT (~0) + MUOP11, UOP11 (0x23f) }, | |
225 | { "maddaq", { DOTDEST, SP, ACCDEST, FSREG, 'q' }, MURES + UFT (~0) + MUOP11, UOP11 (0x23d) }, | |
226 | { "madda", { BC, DOTDEST, SP, ACCDEST, FSREG, FTREGBC }, MURES + MUOP9, UOP9 (0x2f) }, | |
227 | { "max", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x2b) }, | |
228 | { "maxi", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x2d) }, | |
229 | { "max", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + MUOP4, UOP4 (0x4) }, | |
230 | /* FIXME: mini or min? */ | |
231 | { "mini", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x2f) }, | |
232 | { "mini", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x1f) }, | |
233 | { "mini", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + MUOP4, UOP4 (0x5) }, | |
234 | { "msub", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x2d) }, | |
235 | { "msubi", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x27) }, | |
236 | { "msubq", { DOTDEST, SP, FDREG, FSREG, 'q' }, MURES + UFT (~0) + MUOP6, UOP6 (0x25) }, | |
237 | { "msub", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + MUOP4, UOP4 (0x3) }, | |
238 | { "msuba", { DOTDEST, SP, ACCDEST, FSREG, FTREG }, MURES + MUOP11, UOP11 (0x2fd) }, | |
239 | { "msubai", { DOTDEST, SP, ACCDEST, FSREG, 'i' }, MURES + UFT (~0) + MUOP11, UOP11 (0x27f) }, | |
240 | { "msubaq", { DOTDEST, SP, ACCDEST, FSREG, 'q' }, MURES + UFT (~0) + MUOP11, UOP11 (0x27d) }, | |
241 | { "msuba", { BC, DOTDEST, SP, ACCDEST, FSREG, FTREGBC }, MURES + MUOP9, UOP9 (0x3f) }, | |
242 | { "mul", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x2a) }, | |
243 | { "muli", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x1e) }, | |
244 | { "mulq", { DOTDEST, SP, FDREG, FSREG, 'q' }, MURES + UFT (~0) + MUOP6, UOP6 (0x1c) }, | |
245 | { "mul", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + UOP4 (~0), UOP4 (6) }, | |
246 | { "mula", { DOTDEST, SP, ACCDEST, FSREG, FTREG }, MURES + MUOP11, UOP11 (0x2be) }, | |
247 | { "mulai", { DOTDEST, SP, ACCDEST, FSREG, 'i' }, MURES + UFT (~0) + MUOP11, UOP11 (0x1fe) }, | |
248 | { "mulaq", { DOTDEST, SP, ACCDEST, FSREG, 'q' }, MURES + UFT (~0) + MUOP11, UOP11 (0x1fc) }, | |
249 | { "mula", { BC, DOTDEST, SP, ACCDEST, FSREG, FTREGBC }, MURES + MUOP9, UOP9 (0x6f) }, | |
250 | { "nop", { 0 }, MURES + UDEST (~0) + UFT (~0) + UFS (~0) + MUOP11, UOP11 (0x2ff) }, | |
251 | { "opmula", { DOTDEST, SP, ACCDEST, FSREG, FTREG, XYZ }, MURES + MUOP11, UOP11 (0x2fe) }, | |
252 | { "opmsub", { DOTDEST, SP, FDREG, FSREG, FTREG, XYZ }, MURES + MUOP6, UOP6 (0x2e) }, | |
253 | { "sub", { DOTDEST, SP, FDREG, FSREG, FTREG }, MURES + MUOP6, UOP6 (0x2c) }, | |
254 | { "subi", { DOTDEST, SP, FDREG, FSREG, 'i' }, MURES + UFT (~0) + MUOP6, UOP6 (0x26) }, | |
255 | { "subq", { DOTDEST, SP, FDREG, FSREG, 'q' }, MURES + UFT (~0) + MUOP6, UOP6 (0x24) }, | |
256 | { "sub", { BC, DOTDEST, SP, FDREG, FSREG, FTREGBC }, MURES + UOP4 (~0), UOP4 (1) }, | |
257 | { "suba", { DOTDEST, SP, ACCDEST, FSREG, FTREG }, MURES + MUOP11, UOP11 (0x2fc) }, | |
258 | { "subai", { DOTDEST, SP, ACCDEST, FSREG, 'i' }, MURES + UFT (~0) + MUOP11, UOP11 (0x27e) }, | |
259 | { "subaq", { DOTDEST, SP, ACCDEST, FSREG, 'q' }, MURES + UFT (~0) + MUOP11, UOP11 (0x27c) }, | |
260 | { "suba", { BC, DOTDEST, SP, ACCDEST, FSREG, FTREGBC }, MURES + MUOP9, UOP9 (0x1f) } | |
54cc8ed4 DE |
261 | }; |
262 | const int txvu_upper_opcodes_count = sizeof (txvu_upper_opcodes) / sizeof (txvu_opcodes[0]); | |
263 | ||
264 | struct txvu_opcode txvu_lower_opcodes[] = { | |
265 | ||
266 | /* Macros appear first. */ | |
267 | /* ??? Any aliases? */ | |
268 | ||
269 | /* The rest of these needn't be sorted, but it helps to find them if they | |
270 | are. */ | |
271 | { "waitp", { 0 }, 0xffffffff, 0x800007bf, 0 }, | |
272 | { "waitq", { 0 }, 0xffffffff, 0x800003bf, 0 }, | |
273 | }; | |
274 | const int txvu_lower_opcodes_count = sizeof (txvu_lower_opcodes) / sizeof (txvu_opcodes[0]); | |
275 | ||
276 | /* Indexed by first letter of opcode. Points to chain of opcodes with same | |
277 | first letter. */ | |
278 | /* ??? One can certainly use a better hash. Later. */ | |
279 | static struct txvu_opcode *upper_opcode_map[26 + 1]; | |
280 | static struct txvu_opcode *lower_opcode_map[26 + 1]; | |
281 | ||
282 | /* Indexed by insn code. Points to chain of opcodes with same insn code. */ | |
283 | static struct txvu_opcode *upper_icode_map[64]; | |
284 | static struct txvu_opcode *lower_icode_map[64]; | |
285 | \f | |
286 | /* Initialize any tables that need it. | |
287 | Must be called once at start up (or when first needed). | |
288 | ||
289 | FLAGS is currently unused but is intended to control initialization. */ | |
290 | ||
291 | void | |
292 | txvu_opcode_init_tables (flags) | |
293 | int flags; | |
294 | { | |
295 | static int init_p = 0; | |
296 | ||
297 | /* We may be intentionally called more than once (for example gdb will call | |
298 | us each time the user switches cpu). These tables only need to be init'd | |
299 | once though. */ | |
300 | /* ??? We can remove the need for txvu_opcode_supported by taking it into | |
301 | account here, but I'm not sure I want to do that yet (if ever). */ | |
302 | if (!init_p) | |
303 | { | |
304 | int i,n; | |
305 | ||
306 | memset (upper_opcode_map, 0, sizeof (upper_opcode_map)); | |
307 | memset (upper_icode_map, 0, sizeof (upper_icode_map)); | |
308 | ||
309 | /* Scan the table backwards so macros appear at the front. */ | |
310 | for (i = txvu_upper_opcodes_count - 1; i >= 0; --i) | |
311 | { | |
312 | int opcode_hash = TXVU_HASH_UPPER_OPCODE (txvu_upper_opcodes[i].mnemonic); | |
313 | int icode_hash = TXVU_HASH_UPPER_ICODE (txvu_upper_opcodes[i].value); | |
314 | ||
315 | txvu_upper_opcodes[i].next_asm = upper_opcode_map[opcode_hash]; | |
316 | upper_opcode_map[opcode_hash] = &txvu_upper_opcodes[i]; | |
317 | ||
318 | txvu_upper_opcodes[i].next_dis = upper_icode_map[icode_hash]; | |
319 | upper_icode_map[icode_hash] = &txvu_upper_opcodes[i]; | |
320 | } | |
321 | ||
322 | memset (lower_opcode_map, 0, sizeof (lower_opcode_map)); | |
323 | memset (lower_icode_map, 0, sizeof (lower_icode_map)); | |
324 | ||
325 | /* Scan the table backwards so macros appear at the front. */ | |
326 | for (i = txvu_lower_opcodes_count - 1; i >= 0; --i) | |
327 | { | |
328 | int opcode_hash = TXVU_HASH_LOWER_OPCODE (txvu_lower_opcodes[i].mnemonic); | |
329 | int icode_hash = TXVU_HASH_LOWER_ICODE (txvu_lower_opcodes[i].value); | |
330 | ||
331 | txvu_lower_opcodes[i].next_asm = lower_opcode_map[opcode_hash]; | |
332 | lower_opcode_map[opcode_hash] = &txvu_lower_opcodes[i]; | |
333 | ||
334 | txvu_lower_opcodes[i].next_dis = lower_icode_map[icode_hash]; | |
335 | lower_icode_map[icode_hash] = &txvu_lower_opcodes[i]; | |
336 | } | |
337 | ||
338 | init_p = 1; | |
339 | } | |
340 | } | |
341 | ||
342 | /* Return the first insn in the chain for assembling upper INSN. */ | |
343 | ||
344 | const struct txvu_opcode * | |
345 | txvu_upper_opcode_lookup_asm (insn) | |
346 | const char *insn; | |
347 | { | |
348 | return upper_opcode_map[TXVU_HASH_UPPER_OPCODE (insn)]; | |
349 | } | |
350 | ||
351 | /* Return the first insn in the chain for assembling lower INSN. */ | |
352 | ||
353 | const struct txvu_opcode * | |
354 | txvu_lower_opcode_lookup_asm (insn) | |
355 | const char *insn; | |
356 | { | |
357 | return lower_opcode_map[TXVU_HASH_LOWER_OPCODE (insn)]; | |
358 | } | |
359 | ||
360 | /* Return the first insn in the chain for disassembling upper INSN. */ | |
361 | ||
362 | const struct txvu_opcode * | |
363 | txvu_upper_opcode_lookup_dis (insn) | |
364 | TXVU_INSN insn; | |
365 | { | |
366 | return upper_icode_map[TXVU_HASH_UPPER_ICODE (insn)]; | |
367 | } | |
368 | ||
369 | /* Return the first insn in the chain for disassembling lower INSN. */ | |
370 | ||
371 | const struct txvu_opcode * | |
372 | txvu_lower_opcode_lookup_dis (insn) | |
373 | TXVU_INSN insn; | |
374 | { | |
375 | return lower_icode_map[TXVU_HASH_LOWER_ICODE (insn)]; | |
376 | } | |
377 | \f | |
378 | /* Value of DEST in use. | |
71af45ec | 379 | Each of the registers must specify the same value as the opcode. |
54cc8ed4 DE |
380 | ??? Perhaps remove the duplication? */ |
381 | static int dest; | |
3f897263 DE |
382 | |
383 | /* Value of BC to use. | |
384 | The register specified for the ftreg must match the broadcast register | |
385 | specified in the opcode. */ | |
386 | static int bc; | |
54cc8ed4 DE |
387 | \f |
388 | /* Init fns. | |
389 | These are called before doing each of the respective activities. */ | |
390 | ||
391 | /* Called by the assembler before parsing an instruction. */ | |
392 | ||
393 | void | |
394 | txvu_opcode_init_parse () | |
395 | { | |
396 | dest = -1; | |
3f897263 | 397 | bc = -1; |
54cc8ed4 DE |
398 | } |
399 | ||
400 | /* Called by the disassembler before printing an instruction. */ | |
401 | ||
402 | void | |
403 | txvu_opcode_init_print () | |
404 | { | |
405 | dest = -1; | |
3f897263 | 406 | bc = -1; |
54cc8ed4 DE |
407 | } |
408 | \f | |
409 | /* Destination choice support. | |
410 | The "dest" string selects any combination of x,y,z,w. | |
411 | [The letters are ordered that way to follow the manual's style.] */ | |
412 | ||
71af45ec DE |
413 | /* Parse a `dest' spec. |
414 | Return the found value. | |
415 | *PSTR is set to the character that terminated the parsing. | |
416 | It is up to the caller to do any error checking. */ | |
417 | ||
54cc8ed4 | 418 | static long |
71af45ec | 419 | parse_dest (pstr) |
54cc8ed4 | 420 | char **pstr; |
54cc8ed4 DE |
421 | { |
422 | long dest = 0; | |
423 | ||
54cc8ed4 DE |
424 | while (**pstr) |
425 | { | |
426 | switch (**pstr) | |
427 | { | |
428 | case 'x' : case 'X' : dest |= TXVU_DEST_X; break; | |
429 | case 'y' : case 'Y' : dest |= TXVU_DEST_Y; break; | |
430 | case 'z' : case 'Z' : dest |= TXVU_DEST_Z; break; | |
431 | case 'w' : case 'W' : dest |= TXVU_DEST_W; break; | |
71af45ec | 432 | default : return dest; |
54cc8ed4 DE |
433 | } |
434 | ++*pstr; | |
435 | } | |
436 | ||
71af45ec DE |
437 | return dest; |
438 | } | |
439 | ||
440 | static long | |
441 | parse_dotdest (pstr, errmsg) | |
442 | char **pstr; | |
443 | const char **errmsg; | |
444 | { | |
445 | long dest; | |
446 | ||
447 | if (**pstr != '.') | |
448 | { | |
449 | *errmsg = "missing `.'"; | |
450 | return 0; | |
451 | } | |
452 | ||
453 | ++*pstr; | |
454 | dest = parse_dest (pstr); | |
455 | if (dest == 0 || isalnum (**pstr)) | |
456 | { | |
457 | *errmsg = "invalid `dest'"; | |
458 | return 0; | |
459 | } | |
54cc8ed4 DE |
460 | *errmsg = NULL; |
461 | return dest; | |
462 | } | |
463 | ||
71af45ec DE |
464 | static TXVU_INSN |
465 | insert_dotdest (insn, operand, mods, value, errmsg) | |
466 | TXVU_INSN insn; | |
467 | const struct txvu_operand *operand; | |
468 | int mods; | |
469 | long value; | |
470 | const char **errmsg; | |
471 | { | |
472 | /* Record the DEST value in use so the register parser can use it. */ | |
473 | dest = value; | |
474 | if (errmsg) | |
475 | *errmsg = NULL; | |
476 | return insn |= value << operand->shift; | |
477 | } | |
478 | ||
479 | static long | |
480 | extract_dotdest (insn, operand, mods, pinvalid) | |
481 | TXVU_INSN insn; | |
482 | const struct txvu_operand *operand; | |
483 | int mods; | |
484 | int *pinvalid; | |
485 | { | |
486 | /* Record the DEST value in use so the register printer can use it. */ | |
487 | dest = (insn >> operand->shift) & ((1 << operand->bits) - 1); | |
488 | return dest; | |
489 | } | |
490 | ||
54cc8ed4 | 491 | static void |
71af45ec | 492 | print_dest (info, insn, value) |
54cc8ed4 DE |
493 | disassemble_info *info; |
494 | TXVU_INSN insn; | |
495 | long value; | |
496 | { | |
54cc8ed4 DE |
497 | if (value & TXVU_DEST_X) |
498 | (*info->fprintf_func) (info->stream, "x"); | |
499 | if (value & TXVU_DEST_Y) | |
500 | (*info->fprintf_func) (info->stream, "y"); | |
501 | if (value & TXVU_DEST_Z) | |
502 | (*info->fprintf_func) (info->stream, "z"); | |
503 | if (value & TXVU_DEST_W) | |
504 | (*info->fprintf_func) (info->stream, "w"); | |
505 | } | |
71af45ec DE |
506 | |
507 | static void | |
508 | print_dotdest (info, insn, value) | |
509 | disassemble_info *info; | |
510 | TXVU_INSN insn; | |
511 | long value; | |
512 | { | |
513 | (*info->fprintf_func) (info->stream, "."); | |
514 | print_dest (info, insn, value); | |
515 | } | |
54cc8ed4 DE |
516 | \f |
517 | static long | |
71af45ec | 518 | parse_vfreg (pstr, errmsg) |
54cc8ed4 DE |
519 | char **pstr; |
520 | const char **errmsg; | |
521 | { | |
522 | char *str = *pstr; | |
523 | char *start; | |
524 | long reg; | |
71af45ec | 525 | int reg_dest; |
54cc8ed4 DE |
526 | |
527 | if (tolower (str[0]) != 'v' | |
528 | || tolower (str[1]) != 'f') | |
529 | { | |
530 | *errmsg = "unknown register"; | |
531 | return 0; | |
532 | } | |
533 | ||
534 | /* FIXME: quick hack until the framework works. */ | |
535 | start = str = str + 2; | |
536 | while (*str && isdigit (*str)) | |
537 | ++str; | |
538 | reg = atoi (start); | |
71af45ec DE |
539 | reg_dest = parse_dest (&str); |
540 | if (reg_dest == 0 || isalnum (*str)) | |
541 | { | |
542 | *errmsg = "invalid `dest'"; | |
543 | return 0; | |
544 | } | |
545 | if (reg_dest != dest) | |
546 | { | |
547 | *errmsg = "register `dest' does not match instruction `dest'"; | |
548 | return 0; | |
549 | } | |
54cc8ed4 DE |
550 | *pstr = str; |
551 | *errmsg = NULL; | |
552 | return reg; | |
553 | } | |
554 | ||
555 | static void | |
71af45ec | 556 | print_vfreg (info, insn, value) |
54cc8ed4 DE |
557 | disassemble_info *info; |
558 | TXVU_INSN insn; | |
559 | long value; | |
560 | { | |
561 | (*info->fprintf_func) (info->stream, "vf%ld", value); | |
71af45ec | 562 | print_dest (info, insn, dest); |
54cc8ed4 | 563 | } |
3f897263 DE |
564 | \f |
565 | /* Broadcast handling. */ | |
566 | ||
567 | static long | |
568 | parse_bc (pstr, errmsg) | |
569 | char **pstr; | |
570 | const char **errmsg; | |
571 | { | |
572 | long dest = 0; | |
573 | ||
574 | switch (**pstr) | |
575 | { | |
576 | case 'x' : case 'X' : dest = TXVU_BC_X; break; | |
577 | case 'y' : case 'Y' : dest = TXVU_BC_Y; break; | |
578 | case 'z' : case 'Z' : dest = TXVU_BC_Z; break; | |
579 | case 'w' : case 'W' : dest = TXVU_BC_W; break; | |
580 | default : *errmsg = "invalid `bc'"; return 0; | |
581 | } | |
582 | ++*pstr; | |
583 | ||
584 | *errmsg = NULL; | |
585 | return dest; | |
586 | } | |
587 | ||
588 | static void | |
589 | print_bc (info, insn, value) | |
590 | disassemble_info *info; | |
591 | TXVU_INSN insn; | |
592 | long value; | |
593 | { | |
594 | char c; | |
595 | ||
596 | switch (value) | |
597 | { | |
598 | case TXVU_BC_X : c = 'x' ; break; | |
599 | case TXVU_BC_Y : c = 'y' ; break; | |
600 | case TXVU_BC_Z : c = 'z' ; break; | |
601 | case TXVU_BC_W : c = 'w' ; break; | |
602 | } | |
603 | ||
604 | (*info->fprintf_func) (info->stream, "%c", c); | |
605 | } | |
606 | \f | |
607 | /* FT register in broadcast case. */ | |
608 | ||
609 | static long | |
610 | parse_ftregbc (pstr, errmsg) | |
611 | char **pstr; | |
612 | const char **errmsg; | |
613 | { | |
614 | char *str = *pstr; | |
615 | char *start; | |
616 | long reg; | |
617 | int reg_bc; | |
618 | ||
619 | if (tolower (str[0]) != 'v' | |
620 | || tolower (str[1]) != 'f') | |
621 | { | |
622 | *errmsg = "unknown register"; | |
623 | return 0; | |
624 | } | |
625 | ||
626 | /* FIXME: quick hack until the framework works. */ | |
627 | start = str = str + 2; | |
628 | while (*str && isdigit (*str)) | |
629 | ++str; | |
630 | reg = atoi (start); | |
631 | reg_bc = parse_bc (&str, errmsg); | |
632 | if (*errmsg) | |
633 | return 0; | |
634 | if (reg_bc != bc) | |
635 | { | |
636 | *errmsg = "register `bc' does not match instruction `bc'"; | |
637 | return 0; | |
638 | } | |
639 | *pstr = str; | |
640 | *errmsg = NULL; | |
641 | return reg; | |
642 | } | |
643 | ||
644 | static void | |
645 | print_ftregbc (info, insn, value) | |
646 | disassemble_info *info; | |
647 | TXVU_INSN insn; | |
648 | long value; | |
649 | { | |
650 | (*info->fprintf_func) (info->stream, "vf%ld", value); | |
651 | print_bc (info, insn, bc); | |
652 | } | |
653 | \f | |
654 | /* ACC handling. */ | |
655 | ||
656 | static long | |
657 | parse_accdest (pstr, errmsg) | |
658 | char **pstr; | |
659 | const char **errmsg; | |
660 | { | |
661 | char *str = *pstr; | |
662 | long acc_dest = 0; | |
663 | ||
664 | if (strncasecmp (str, "acc", 3) != 0) | |
665 | { | |
666 | *errmsg = "expecting `acc'"; | |
667 | return 0; | |
668 | } | |
669 | str += 3; | |
670 | acc_dest = parse_dest (&str); | |
671 | if (acc_dest == 0 || isalnum (*str)) | |
672 | { | |
673 | *errmsg = "invalid `dest'"; | |
674 | return 0; | |
675 | } | |
676 | if (acc_dest != dest) | |
677 | { | |
678 | *errmsg = "acc `dest' does not match instruction `dest'"; | |
679 | return 0; | |
680 | } | |
681 | *pstr = str; | |
682 | *errmsg = NULL; | |
683 | /* Value isn't used, but we must return something. */ | |
684 | return 0; | |
685 | } | |
686 | ||
687 | static void | |
688 | print_accdest (info, insn, value) | |
689 | disassemble_info *info; | |
690 | TXVU_INSN insn; | |
691 | long value; | |
692 | { | |
693 | (*info->fprintf_func) (info->stream, "acc"); | |
694 | print_dest (info, insn, value); | |
695 | } | |
696 | \f | |
697 | /* XYZ operand handling. | |
698 | This simplifies the opmula,opmsub entries by keeping them equivalent to | |
699 | the others. */ | |
700 | ||
701 | static long | |
702 | parse_xyz (pstr, errmsg) | |
703 | char **pstr; | |
704 | const char **errmsg; | |
705 | { | |
706 | if (dest != (TXVU_DEST_X | TXVU_DEST_Y | TXVU_DEST_Z)) | |
707 | { | |
708 | *errmsg = "expecting `xyz' for `dest' value"; | |
709 | return 0; | |
710 | } | |
711 | return 0; | |
712 | } |