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