]>
Commit | Line | Data |
---|---|---|
14c7c974 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
f083c6c3 A |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14c7c974 A |
14 | * |
15 | * The Original Code and all software distributed under the License are | |
f083c6c3 | 16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
14c7c974 A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
f083c6c3 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
14c7c974 A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* disasm.c where all the _work_ gets done in the Netwide Disassembler | |
26 | * | |
27 | * The Netwide Assembler is copyright (C) 1996 Simon Tatham and | |
28 | * Julian Hall. All rights reserved. The software is | |
29 | * redistributable under the licence given in the file "Licence" | |
30 | * distributed in the NASM archive. | |
31 | * | |
32 | * initial version 27/iii/95 by Simon Tatham | |
33 | */ | |
34 | ||
35 | #include <stdio.h> | |
36 | #include <string.h> | |
37 | ||
38 | #include "nasm.h" | |
39 | #include "disasm.h" | |
40 | #include "sync.h" | |
41 | #include "insns.h" | |
42 | ||
43 | #include "names.c" | |
44 | ||
45 | extern struct itemplate **itable[]; | |
46 | ||
47 | /* | |
48 | * Flags that go into the `segment' field of `insn' structures | |
49 | * during disassembly. | |
50 | */ | |
51 | #define SEG_RELATIVE 1 | |
52 | #define SEG_32BIT 2 | |
53 | #define SEG_RMREG 4 | |
54 | #define SEG_DISP8 8 | |
55 | #define SEG_DISP16 16 | |
56 | #define SEG_DISP32 32 | |
57 | #define SEG_NODISP 64 | |
58 | #define SEG_SIGNED 128 | |
59 | ||
60 | static int whichreg(long regflags, int regval) { | |
61 | static int reg32[] = { | |
62 | R_EAX, R_ECX, R_EDX, R_EBX, R_ESP, R_EBP, R_ESI, R_EDI }; | |
63 | static int reg16[] = { | |
64 | R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI }; | |
65 | static int reg8[] = { | |
66 | R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH }; | |
67 | static int sreg[] = { | |
68 | R_ES, R_CS, R_SS, R_DS, R_FS, R_GS, 0, 0 }; | |
69 | static int creg[] = { | |
70 | R_CR0, 0, R_CR2, R_CR3, R_CR4, 0, 0, 0 }; | |
71 | static int dreg[] = { | |
72 | R_DR0, R_DR1, R_DR2, R_DR3, 0, 0, R_DR6, R_DR7 }; | |
73 | static int treg[] = { | |
74 | 0, 0, 0, R_TR3, R_TR4, R_TR5, R_TR6, R_TR7 }; | |
75 | static int fpureg[] = { | |
76 | R_ST0, R_ST1, R_ST2, R_ST3, R_ST4, R_ST5, R_ST6, R_ST7 }; | |
77 | static int mmxreg[] = { | |
78 | R_MM0, R_MM1, R_MM2, R_MM3, R_MM4, R_MM5, R_MM6, R_MM7 }; | |
79 | ||
80 | if (!(REG_AL & ~regflags)) | |
81 | return R_AL; | |
82 | if (!(REG_AX & ~regflags)) | |
83 | return R_AX; | |
84 | if (!(REG_EAX & ~regflags)) | |
85 | return R_EAX; | |
86 | if (!(REG_DX & ~regflags)) | |
87 | return R_DX; | |
88 | if (!(REG_CL & ~regflags)) | |
89 | return R_CL; | |
90 | if (!(REG_CX & ~regflags)) | |
91 | return R_CX; | |
92 | if (!(REG_ECX & ~regflags)) | |
93 | return R_ECX; | |
94 | if (!(REG_CR4 & ~regflags)) | |
95 | return R_CR4; | |
96 | if (!(FPU0 & ~regflags)) | |
97 | return R_ST0; | |
98 | if (!(REG_CS & ~regflags)) | |
99 | return R_CS; | |
100 | if (!(REG_DESS & ~regflags)) | |
101 | return (regval == 0 || regval == 2 || regval == 3 ? sreg[regval] : 0); | |
102 | if (!(REG_FSGS & ~regflags)) | |
103 | return (regval == 4 || regval == 5 ? sreg[regval] : 0); | |
104 | if (!((REGMEM|BITS8) & ~regflags)) | |
105 | return reg8[regval]; | |
106 | if (!((REGMEM|BITS16) & ~regflags)) | |
107 | return reg16[regval]; | |
108 | if (!((REGMEM|BITS32) & ~regflags)) | |
109 | return reg32[regval]; | |
110 | if (!(REG_SREG & ~regflags)) | |
111 | return sreg[regval]; | |
112 | if (!(REG_CREG & ~regflags)) | |
113 | return creg[regval]; | |
114 | if (!(REG_DREG & ~regflags)) | |
115 | return dreg[regval]; | |
116 | if (!(REG_TREG & ~regflags)) | |
117 | return treg[regval]; | |
118 | if (!(FPUREG & ~regflags)) | |
119 | return fpureg[regval]; | |
120 | if (!(MMXREG & ~regflags)) | |
121 | return mmxreg[regval]; | |
122 | return 0; | |
123 | } | |
124 | ||
125 | static char *whichcond(int condval) { | |
126 | static int conds[] = { | |
127 | C_O, C_NO, C_C, C_NC, C_Z, C_NZ, C_NA, C_A, | |
128 | C_S, C_NS, C_PE, C_PO, C_L, C_NL, C_NG, C_G | |
129 | }; | |
130 | return conditions[conds[condval]]; | |
131 | } | |
132 | ||
133 | /* | |
134 | * Process an effective address (ModRM) specification. | |
135 | */ | |
136 | static unsigned char *do_ea (unsigned char *data, int modrm, int asize, | |
137 | int segsize, operand *op) { | |
138 | int mod, rm, scale, index, base; | |
139 | ||
140 | mod = (modrm >> 6) & 03; | |
141 | rm = modrm & 07; | |
142 | ||
143 | if (mod == 3) { /* pure register version */ | |
144 | op->basereg = rm; | |
145 | op->segment |= SEG_RMREG; | |
146 | return data; | |
147 | } | |
148 | ||
149 | op->addr_size = 0; | |
150 | ||
151 | if (asize == 16) { | |
152 | /* | |
153 | * <mod> specifies the displacement size (none, byte or | |
154 | * word), and <rm> specifies the register combination. | |
155 | * Exception: mod=0,rm=6 does not specify [BP] as one might | |
156 | * expect, but instead specifies [disp16]. | |
157 | */ | |
158 | op->indexreg = op->basereg = -1; | |
159 | op->scale = 1; /* always, in 16 bits */ | |
160 | switch (rm) { | |
161 | case 0: op->basereg = R_BX; op->indexreg = R_SI; break; | |
162 | case 1: op->basereg = R_BX; op->indexreg = R_DI; break; | |
163 | case 2: op->basereg = R_BP; op->indexreg = R_SI; break; | |
164 | case 3: op->basereg = R_BP; op->indexreg = R_DI; break; | |
165 | case 4: op->basereg = R_SI; break; | |
166 | case 5: op->basereg = R_DI; break; | |
167 | case 6: op->basereg = R_BP; break; | |
168 | case 7: op->basereg = R_BX; break; | |
169 | } | |
170 | if (rm == 6 && mod == 0) { /* special case */ | |
171 | op->basereg = -1; | |
172 | if (segsize != 16) | |
173 | op->addr_size = 16; | |
174 | mod = 2; /* fake disp16 */ | |
175 | } | |
176 | switch (mod) { | |
177 | case 0: | |
178 | op->segment |= SEG_NODISP; | |
179 | break; | |
180 | case 1: | |
181 | op->segment |= SEG_DISP8; | |
182 | op->offset = (signed char) *data++; | |
183 | break; | |
184 | case 2: | |
185 | op->segment |= SEG_DISP16; | |
186 | op->offset = *data++; | |
187 | op->offset |= (*data++) << 8; | |
188 | break; | |
189 | } | |
190 | return data; | |
191 | } else { | |
192 | /* | |
193 | * Once again, <mod> specifies displacement size (this time | |
194 | * none, byte or *dword*), while <rm> specifies the base | |
195 | * register. Again, [EBP] is missing, replaced by a pure | |
196 | * disp32 (this time that's mod=0,rm=*5*). However, rm=4 | |
197 | * indicates not a single base register, but instead the | |
198 | * presence of a SIB byte... | |
199 | */ | |
200 | op->indexreg = -1; | |
201 | switch (rm) { | |
202 | case 0: op->basereg = R_EAX; break; | |
203 | case 1: op->basereg = R_ECX; break; | |
204 | case 2: op->basereg = R_EDX; break; | |
205 | case 3: op->basereg = R_EBX; break; | |
206 | case 5: op->basereg = R_EBP; break; | |
207 | case 6: op->basereg = R_ESI; break; | |
208 | case 7: op->basereg = R_EDI; break; | |
209 | } | |
210 | if (rm == 5 && mod == 0) { | |
211 | op->basereg = -1; | |
212 | if (segsize != 32) | |
213 | op->addr_size = 32; | |
214 | mod = 2; /* fake disp32 */ | |
215 | } | |
216 | if (rm == 4) { /* process SIB */ | |
217 | scale = (*data >> 6) & 03; | |
218 | index = (*data >> 3) & 07; | |
219 | base = *data & 07; | |
220 | data++; | |
221 | ||
222 | op->scale = 1 << scale; | |
223 | switch (index) { | |
224 | case 0: op->indexreg = R_EAX; break; | |
225 | case 1: op->indexreg = R_ECX; break; | |
226 | case 2: op->indexreg = R_EDX; break; | |
227 | case 3: op->indexreg = R_EBX; break; | |
228 | case 4: op->indexreg = -1; break; | |
229 | case 5: op->indexreg = R_EBP; break; | |
230 | case 6: op->indexreg = R_ESI; break; | |
231 | case 7: op->indexreg = R_EDI; break; | |
232 | } | |
233 | ||
234 | switch (base) { | |
235 | case 0: op->basereg = R_EAX; break; | |
236 | case 1: op->basereg = R_ECX; break; | |
237 | case 2: op->basereg = R_EDX; break; | |
238 | case 3: op->basereg = R_EBX; break; | |
239 | case 4: op->basereg = R_ESP; break; | |
240 | case 6: op->basereg = R_ESI; break; | |
241 | case 7: op->basereg = R_EDI; break; | |
242 | case 5: | |
243 | if (mod == 0) { | |
244 | mod = 2; | |
245 | op->basereg = -1; | |
246 | } else | |
247 | op->basereg = R_EBP; | |
248 | break; | |
249 | } | |
250 | } | |
251 | switch (mod) { | |
252 | case 0: | |
253 | op->segment |= SEG_NODISP; | |
254 | break; | |
255 | case 1: | |
256 | op->segment |= SEG_DISP8; | |
257 | op->offset = (signed char) *data++; | |
258 | break; | |
259 | case 2: | |
260 | op->segment |= SEG_DISP32; | |
261 | op->offset = *data++; | |
262 | op->offset |= (*data++) << 8; | |
263 | op->offset |= ((long) *data++) << 16; | |
264 | op->offset |= ((long) *data++) << 24; | |
265 | break; | |
266 | } | |
267 | return data; | |
268 | } | |
269 | } | |
270 | ||
271 | /* | |
272 | * Determine whether the code string in r corresponds to the data | |
273 | * stream in data. Return the number of bytes matched if so. | |
274 | */ | |
275 | static int matches (unsigned char *r, unsigned char *data, int asize, | |
276 | int osize, int segsize, insn *ins) { | |
277 | unsigned char *origdata = data; | |
278 | int a_used = FALSE, o_used = FALSE; | |
279 | ||
280 | while (*r) { | |
281 | int c = *r++; | |
282 | if (c >= 01 && c <= 03) { | |
283 | while (c--) | |
284 | if (*r++ != *data++) | |
285 | return FALSE; | |
286 | } | |
287 | if (c == 04) { | |
288 | switch (*data++) { | |
289 | case 0x07: ins->oprs[0].basereg = 0; break; | |
290 | case 0x17: ins->oprs[0].basereg = 2; break; | |
291 | case 0x1F: ins->oprs[0].basereg = 3; break; | |
292 | default: return FALSE; | |
293 | } | |
294 | } | |
295 | if (c == 05) { | |
296 | switch (*data++) { | |
297 | case 0xA1: ins->oprs[0].basereg = 4; break; | |
298 | case 0xA9: ins->oprs[0].basereg = 5; break; | |
299 | default: return FALSE; | |
300 | } | |
301 | } | |
302 | if (c == 06) { | |
303 | switch (*data++) { | |
304 | case 0x06: ins->oprs[0].basereg = 0; break; | |
305 | case 0x0E: ins->oprs[0].basereg = 1; break; | |
306 | case 0x16: ins->oprs[0].basereg = 2; break; | |
307 | case 0x1E: ins->oprs[0].basereg = 3; break; | |
308 | default: return FALSE; | |
309 | } | |
310 | } | |
311 | if (c == 07) { | |
312 | switch (*data++) { | |
313 | case 0xA0: ins->oprs[0].basereg = 4; break; | |
314 | case 0xA8: ins->oprs[0].basereg = 5; break; | |
315 | default: return FALSE; | |
316 | } | |
317 | } | |
318 | if (c >= 010 && c <= 012) { | |
319 | int t = *r++, d = *data++; | |
320 | if (d < t || d > t+7) | |
321 | return FALSE; | |
322 | else { | |
323 | ins->oprs[c-010].basereg = d-t; | |
324 | ins->oprs[c-010].segment |= SEG_RMREG; | |
325 | } | |
326 | } | |
327 | if (c == 017) | |
328 | if (*data++) | |
329 | return FALSE; | |
330 | if (c >= 014 && c <= 016) { | |
331 | ins->oprs[c-014].offset = (signed char) *data++; | |
332 | ins->oprs[c-014].segment |= SEG_SIGNED; | |
333 | } | |
334 | if (c >= 020 && c <= 022) | |
335 | ins->oprs[c-020].offset = *data++; | |
336 | if (c >= 024 && c <= 026) | |
337 | ins->oprs[c-024].offset = *data++; | |
338 | if (c >= 030 && c <= 032) { | |
339 | ins->oprs[c-030].offset = *data++; | |
340 | ins->oprs[c-030].offset |= (*data++ << 8); | |
341 | } | |
342 | if (c >= 034 && c <= 036) { | |
343 | ins->oprs[c-034].offset = *data++; | |
344 | ins->oprs[c-034].offset |= (*data++ << 8); | |
345 | if (asize == 32) { | |
346 | ins->oprs[c-034].offset |= (((long) *data++) << 16); | |
347 | ins->oprs[c-034].offset |= (((long) *data++) << 24); | |
348 | } | |
349 | if (segsize != asize) | |
350 | ins->oprs[c-034].addr_size = asize; | |
351 | } | |
352 | if (c >= 040 && c <= 042) { | |
353 | ins->oprs[c-040].offset = *data++; | |
354 | ins->oprs[c-040].offset |= (*data++ << 8); | |
355 | ins->oprs[c-040].offset |= (((long) *data++) << 16); | |
356 | ins->oprs[c-040].offset |= (((long) *data++) << 24); | |
357 | } | |
358 | if (c >= 050 && c <= 052) { | |
359 | ins->oprs[c-050].offset = (signed char) *data++; | |
360 | ins->oprs[c-050].segment |= SEG_RELATIVE; | |
361 | } | |
362 | if (c >= 060 && c <= 062) { | |
363 | ins->oprs[c-060].offset = *data++; | |
364 | ins->oprs[c-060].offset |= (*data++ << 8); | |
365 | ins->oprs[c-060].segment |= SEG_RELATIVE; | |
366 | ins->oprs[c-060].segment &= ~SEG_32BIT; | |
367 | } | |
368 | if (c >= 064 && c <= 066) { | |
369 | ins->oprs[c-064].offset = *data++; | |
370 | ins->oprs[c-064].offset |= (*data++ << 8); | |
371 | if (asize == 32) { | |
372 | ins->oprs[c-064].offset |= (((long) *data++) << 16); | |
373 | ins->oprs[c-064].offset |= (((long) *data++) << 24); | |
374 | ins->oprs[c-064].segment |= SEG_32BIT; | |
375 | } else | |
376 | ins->oprs[c-064].segment &= ~SEG_32BIT; | |
377 | ins->oprs[c-064].segment |= SEG_RELATIVE; | |
378 | if (segsize != asize) | |
379 | ins->oprs[c-064].addr_size = asize; | |
380 | } | |
381 | if (c >= 070 && c <= 072) { | |
382 | ins->oprs[c-070].offset = *data++; | |
383 | ins->oprs[c-070].offset |= (*data++ << 8); | |
384 | ins->oprs[c-070].offset |= (((long) *data++) << 16); | |
385 | ins->oprs[c-070].offset |= (((long) *data++) << 24); | |
386 | ins->oprs[c-070].segment |= SEG_32BIT | SEG_RELATIVE; | |
387 | } | |
388 | if (c >= 0100 && c <= 0177) { | |
389 | int modrm = *data++; | |
390 | ins->oprs[c & 07].basereg = (modrm >> 3) & 07; | |
391 | ins->oprs[c & 07].segment |= SEG_RMREG; | |
392 | data = do_ea (data, modrm, asize, segsize, | |
393 | &ins->oprs[(c >> 3) & 07]); | |
394 | } | |
395 | if (c >= 0200 && c <= 0277) { | |
396 | int modrm = *data++; | |
397 | if (((modrm >> 3) & 07) != (c & 07)) | |
398 | return FALSE; /* spare field doesn't match up */ | |
399 | data = do_ea (data, modrm, asize, segsize, | |
400 | &ins->oprs[(c >> 3) & 07]); | |
401 | } | |
402 | if (c >= 0300 && c <= 0302) { | |
403 | if (asize) | |
404 | ins->oprs[c-0300].segment |= SEG_32BIT; | |
405 | else | |
406 | ins->oprs[c-0300].segment &= ~SEG_32BIT; | |
407 | a_used = TRUE; | |
408 | } | |
409 | if (c == 0310) { | |
410 | if (asize == 32) | |
411 | return FALSE; | |
412 | else | |
413 | a_used = TRUE; | |
414 | } | |
415 | if (c == 0311) { | |
416 | if (asize == 16) | |
417 | return FALSE; | |
418 | else | |
419 | a_used = TRUE; | |
420 | } | |
421 | if (c == 0312) { | |
422 | if (asize != segsize) | |
423 | return FALSE; | |
424 | else | |
425 | a_used = TRUE; | |
426 | } | |
427 | if (c == 0320) { | |
428 | if (osize == 32) | |
429 | return FALSE; | |
430 | else | |
431 | o_used = TRUE; | |
432 | } | |
433 | if (c == 0321) { | |
434 | if (osize == 16) | |
435 | return FALSE; | |
436 | else | |
437 | o_used = TRUE; | |
438 | } | |
439 | if (c == 0322) { | |
440 | if (osize != segsize) | |
441 | return FALSE; | |
442 | else | |
443 | o_used = TRUE; | |
444 | } | |
445 | if (c == 0330) { | |
446 | int t = *r++, d = *data++; | |
447 | if (d < t || d > t+15) | |
448 | return FALSE; | |
449 | else | |
450 | ins->condition = d - t; | |
451 | } | |
452 | } | |
453 | ||
454 | /* | |
455 | * Check for unused a/o prefixes. | |
456 | */ | |
457 | ins->nprefix = 0; | |
458 | if (!a_used && asize != segsize) | |
459 | ins->prefixes[ins->nprefix++] = (asize == 16 ? P_A16 : P_A32); | |
460 | if (!o_used && osize != segsize) | |
461 | ins->prefixes[ins->nprefix++] = (osize == 16 ? P_O16 : P_O32); | |
462 | ||
463 | return data - origdata; | |
464 | } | |
465 | ||
466 | long disasm (unsigned char *data, char *output, int segsize, long offset, | |
467 | int autosync) { | |
468 | struct itemplate **p; | |
469 | int length = 0; | |
470 | char *segover; | |
471 | int rep, lock, asize, osize, i, slen, colon; | |
472 | unsigned char *origdata; | |
473 | int works; | |
474 | insn ins; | |
475 | ||
476 | /* | |
477 | * Scan for prefixes. | |
478 | */ | |
479 | asize = osize = segsize; | |
480 | segover = NULL; | |
481 | rep = lock = 0; | |
482 | origdata = data; | |
483 | for (;;) { | |
484 | if (*data == 0xF3 || *data == 0xF2) | |
485 | rep = *data++; | |
486 | else if (*data == 0xF0) | |
487 | lock = *data++; | |
488 | else if (*data == 0x2E || *data == 0x36 || *data == 0x3E || | |
489 | *data == 0x26 || *data == 0x64 || *data == 0x65) { | |
490 | switch (*data++) { | |
491 | case 0x2E: segover = "cs"; break; | |
492 | case 0x36: segover = "ss"; break; | |
493 | case 0x3E: segover = "ds"; break; | |
494 | case 0x26: segover = "es"; break; | |
495 | case 0x64: segover = "fs"; break; | |
496 | case 0x65: segover = "gs"; break; | |
497 | } | |
498 | } else if (*data == 0x66) | |
499 | osize = 48 - segsize, data++; | |
500 | else if (*data == 0x67) | |
501 | asize = 48 - segsize, data++; | |
502 | else | |
503 | break; | |
504 | } | |
505 | ||
506 | ins.oprs[0].segment = ins.oprs[1].segment = ins.oprs[2].segment = | |
507 | ins.oprs[0].addr_size = ins.oprs[1].addr_size = ins.oprs[2].addr_size = | |
508 | (segsize == 16 ? 0 : SEG_32BIT); | |
509 | ins.condition = -1; | |
510 | works = TRUE; | |
511 | for (p = itable[*data]; *p; p++) | |
512 | if ( (length = matches((unsigned char *)((*p)->code), data, | |
513 | asize, osize, segsize, &ins)) ) { | |
514 | works = TRUE; | |
515 | /* | |
516 | * Final check to make sure the types of r/m match up. | |
517 | */ | |
518 | for (i = 0; i < (*p)->operands; i++) | |
519 | if ( | |
520 | ||
521 | /* If it's a mem-only EA but we have a register, die. */ | |
522 | ((ins.oprs[i].segment & SEG_RMREG) && | |
523 | !(MEMORY & ~(*p)->opd[i])) || | |
524 | ||
525 | /* If it's a reg-only EA but we have a memory ref, die. */ | |
526 | (!(ins.oprs[i].segment & SEG_RMREG) && | |
527 | !(REGNORM & ~(*p)->opd[i]) && | |
528 | !((*p)->opd[i] & REG_SMASK)) || | |
529 | ||
530 | /* Register type mismatch (eg FS vs REG_DESS): die. */ | |
531 | ((((*p)->opd[i] & (REGISTER | FPUREG)) || | |
532 | (ins.oprs[i].segment & SEG_RMREG)) && | |
533 | !whichreg ((*p)->opd[i], ins.oprs[i].basereg))) | |
534 | ||
535 | works = FALSE; | |
536 | if (works) | |
537 | break; | |
538 | } | |
539 | if (!length || !works) | |
540 | return 0; /* no instruction was matched */ | |
541 | ||
542 | slen = 0; | |
543 | ||
544 | if (rep) { | |
545 | slen += sprintf(output+slen, "rep%s ", | |
546 | (rep == 0xF2 ? "ne" : | |
547 | (*p)->opcode == I_CMPSB || | |
548 | (*p)->opcode == I_CMPSW || | |
549 | (*p)->opcode == I_CMPSD || | |
550 | (*p)->opcode == I_SCASB || | |
551 | (*p)->opcode == I_SCASW || | |
552 | (*p)->opcode == I_SCASD ? "e" : "")); | |
553 | } | |
554 | if (lock) | |
555 | slen += sprintf(output+slen, "lock "); | |
556 | for (i = 0; i < ins.nprefix; i++) | |
557 | switch (ins.prefixes[i]) { | |
558 | case P_A16: slen += sprintf(output+slen, "a16 "); break; | |
559 | case P_A32: slen += sprintf(output+slen, "a32 "); break; | |
560 | case P_O16: slen += sprintf(output+slen, "o16 "); break; | |
561 | case P_O32: slen += sprintf(output+slen, "o32 "); break; | |
562 | } | |
563 | ||
f083c6c3 | 564 | for (i = 0; i < (int)elements(ico); i++) |
14c7c974 A |
565 | if ((*p)->opcode == ico[i]) { |
566 | slen += sprintf(output+slen, "%s%s", icn[i], | |
567 | whichcond(ins.condition)); | |
568 | break; | |
569 | } | |
f083c6c3 | 570 | if (i >= (int)elements(ico)) |
14c7c974 A |
571 | slen += sprintf(output+slen, "%s", insn_names[(*p)->opcode]); |
572 | colon = FALSE; | |
573 | length += data - origdata; /* fix up for prefixes */ | |
574 | for (i=0; i<(*p)->operands; i++) { | |
575 | output[slen++] = (colon ? ':' : i==0 ? ' ' : ','); | |
576 | ||
577 | if (ins.oprs[i].segment & SEG_RELATIVE) { | |
578 | ins.oprs[i].offset += offset + length; | |
579 | /* | |
580 | * sort out wraparound | |
581 | */ | |
582 | if (!(ins.oprs[i].segment & SEG_32BIT)) | |
583 | ins.oprs[i].offset &= 0xFFFF; | |
584 | /* | |
585 | * add sync marker, if autosync is on | |
586 | */ | |
587 | if (autosync) | |
588 | add_sync (ins.oprs[i].offset, 0L); | |
589 | } | |
590 | ||
591 | if ((*p)->opd[i] & COLON) | |
592 | colon = TRUE; | |
593 | else | |
594 | colon = FALSE; | |
595 | ||
596 | if (((*p)->opd[i] & (REGISTER | FPUREG)) || | |
597 | (ins.oprs[i].segment & SEG_RMREG)) { | |
598 | ins.oprs[i].basereg = whichreg ((*p)->opd[i], | |
599 | ins.oprs[i].basereg); | |
600 | slen += sprintf(output+slen, "%s", | |
601 | reg_names[ins.oprs[i].basereg-EXPR_REG_START]); | |
602 | } else if (!(UNITY & ~(*p)->opd[i])) { | |
603 | output[slen++] = '1'; | |
604 | } else if ( (*p)->opd[i] & IMMEDIATE ) { | |
605 | if ( (*p)->opd[i] & BITS8 ) { | |
606 | slen += sprintf(output+slen, "byte "); | |
607 | if (ins.oprs[i].segment & SEG_SIGNED) { | |
608 | if (ins.oprs[i].offset < 0) { | |
609 | ins.oprs[i].offset *= -1; | |
610 | output[slen++] = '-'; | |
611 | } else | |
612 | output[slen++] = '+'; | |
613 | } | |
614 | } else if ( (*p)->opd[i] & BITS16 ) { | |
615 | slen += sprintf(output+slen, "word "); | |
616 | } else if ( (*p)->opd[i] & BITS32 ) { | |
617 | slen += sprintf(output+slen, "dword "); | |
618 | } else if ( (*p)->opd[i] & NEAR ) { | |
619 | slen += sprintf(output+slen, "near "); | |
620 | } else if ( (*p)->opd[i] & SHORT ) { | |
621 | slen += sprintf(output+slen, "short "); | |
622 | } | |
623 | slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); | |
624 | } else if ( !(MEM_OFFS & ~(*p)->opd[i]) ) { | |
625 | slen += sprintf(output+slen, "[%s%s%s0x%lx]", | |
626 | (segover ? segover : ""), | |
627 | (segover ? ":" : ""), | |
628 | (ins.oprs[i].addr_size == 32 ? "dword " : | |
629 | ins.oprs[i].addr_size == 16 ? "word " : ""), | |
630 | ins.oprs[i].offset); | |
631 | segover = NULL; | |
632 | } else if ( !(REGMEM & ~(*p)->opd[i]) ) { | |
633 | int started = FALSE; | |
634 | if ( (*p)->opd[i] & BITS8 ) | |
635 | slen += sprintf(output+slen, "byte "); | |
636 | if ( (*p)->opd[i] & BITS16 ) | |
637 | slen += sprintf(output+slen, "word "); | |
638 | if ( (*p)->opd[i] & BITS32 ) | |
639 | slen += sprintf(output+slen, "dword "); | |
640 | if ( (*p)->opd[i] & BITS64 ) | |
641 | slen += sprintf(output+slen, "qword "); | |
642 | if ( (*p)->opd[i] & BITS80 ) | |
643 | slen += sprintf(output+slen, "tword "); | |
644 | if ( (*p)->opd[i] & FAR ) | |
645 | slen += sprintf(output+slen, "far "); | |
646 | if ( (*p)->opd[i] & NEAR ) | |
647 | slen += sprintf(output+slen, "near "); | |
648 | output[slen++] = '['; | |
649 | if (ins.oprs[i].addr_size) | |
650 | slen += sprintf(output+slen, "%s", | |
651 | (ins.oprs[i].addr_size == 32 ? "dword " : | |
652 | ins.oprs[i].addr_size == 16 ? "word " : "")); | |
653 | if (segover) { | |
654 | slen += sprintf(output+slen, "%s:", segover); | |
655 | segover = NULL; | |
656 | } | |
657 | if (ins.oprs[i].basereg != -1) { | |
658 | slen += sprintf(output+slen, "%s", | |
659 | reg_names[(ins.oprs[i].basereg - | |
660 | EXPR_REG_START)]); | |
661 | started = TRUE; | |
662 | } | |
663 | if (ins.oprs[i].indexreg != -1) { | |
664 | if (started) | |
665 | output[slen++] = '+'; | |
666 | slen += sprintf(output+slen, "%s", | |
667 | reg_names[(ins.oprs[i].indexreg - | |
668 | EXPR_REG_START)]); | |
669 | if (ins.oprs[i].scale > 1) | |
670 | slen += sprintf(output+slen, "*%d", ins.oprs[i].scale); | |
671 | started = TRUE; | |
672 | } | |
673 | if (ins.oprs[i].segment & SEG_DISP8) { | |
674 | int sign = '+'; | |
675 | if (ins.oprs[i].offset & 0x80) { | |
676 | ins.oprs[i].offset = - (signed char) ins.oprs[i].offset; | |
677 | sign = '-'; | |
678 | } | |
679 | slen += sprintf(output+slen, "%c0x%lx", sign, | |
680 | ins.oprs[i].offset); | |
681 | } else if (ins.oprs[i].segment & SEG_DISP16) { | |
682 | if (started) | |
683 | output[slen++] = '+'; | |
684 | slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); | |
685 | } else if (ins.oprs[i].segment & SEG_DISP32) { | |
686 | if (started) | |
687 | output[slen++] = '+'; | |
688 | slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); | |
689 | } | |
690 | output[slen++] = ']'; | |
691 | } else { | |
692 | slen += sprintf(output+slen, "<operand%d>", i); | |
693 | } | |
694 | } | |
695 | output[slen] = '\0'; | |
696 | if (segover) { /* unused segment override */ | |
697 | char *p = output; | |
698 | int count = slen+1; | |
699 | while (count--) | |
700 | p[count+3] = p[count]; | |
701 | strncpy (output, segover, 2); | |
702 | output[2] = ' '; | |
703 | } | |
704 | return length; | |
705 | } | |
706 | ||
707 | long eatbyte (unsigned char *data, char *output) { | |
708 | sprintf(output, "db 0x%02X", *data); | |
709 | return 1; | |
710 | } |