Merge branch 'develop'
[clinton/Virtual-Jaguar-Rx.git] / src / m68000 / m68kdasm.c
1 //
2 // m68kdasm.c: 68000 instruction disassembly
3 //
4 // Originally part of the UAE 68000 cpu core
5 // by Bernd Schmidt
6 //
7 // Adapted to Virtual Jaguar by James Hammons
8 //
9 // This file is distributed under the GNU Public License, version 3 or at your
10 // option any later version. Read the file GPLv3 for details.
11 //
12
13 #include <string.h>
14 #include "cpudefs.h"
15 #include "cpuextra.h"
16 #include "inlines.h"
17 #include "readcpu.h"
18
19
20 // Stuff from m68kinterface.c
21 extern unsigned long IllegalOpcode(uint32_t opcode);
22 extern cpuop_func * cpuFunctionTable[65536];
23
24 // Prototypes
25 void HandleMovem(char * output, uint16_t data, int direction);
26
27 // Local "global" variables
28 static long int m68kpc_offset;
29
30 #if 0
31 #define get_ibyte_1(o) get_byte(regs.pc + (regs.pc_p - regs.pc_oldp) + (o) + 1)
32 #define get_iword_1(o) get_word(regs.pc + (regs.pc_p - regs.pc_oldp) + (o))
33 #define get_ilong_1(o) get_long(regs.pc + (regs.pc_p - regs.pc_oldp) + (o))
34 #else
35 #define get_ibyte_1(o) m68k_read_memory_8(regs.pc + (o) + 1)
36 #define get_iword_1(o) m68k_read_memory_16(regs.pc + (o))
37 #define get_ilong_1(o) m68k_read_memory_32(regs.pc + (o))
38 #endif
39
40
41 //int32_t ShowEA(FILE * f, int reg, amodes mode, wordsizes size, char * buf)
42 int32_t ShowEA(int mnemonic, int reg, amodes mode, wordsizes size, char * buf)
43 {
44 uint16_t dp;
45 int8_t disp8;
46 int16_t disp16;
47 int r;
48 uint32_t dispreg;
49 uint32_t addr;
50 int32_t offset = 0;
51 char buffer[80];
52
53 switch (mode)
54 {
55 case Dreg:
56 sprintf(buffer,"D%d", reg);
57 break;
58 case Areg:
59 sprintf(buffer,"A%d", reg);
60 break;
61 case Aind:
62 sprintf(buffer,"(A%d)", reg);
63 break;
64 case Aipi:
65 sprintf(buffer,"(A%d)+", reg);
66 break;
67 case Apdi:
68 sprintf(buffer,"-(A%d)", reg);
69 break;
70 case Ad16:
71 disp16 = get_iword_1(m68kpc_offset); m68kpc_offset += 2;
72 addr = m68k_areg(regs,reg) + (int16_t)disp16;
73 sprintf(buffer,"(A%d,$%X) == $%lX", reg, disp16 & 0xFFFF,
74 (unsigned long)addr);
75 break;
76 case Ad8r:
77 dp = get_iword_1(m68kpc_offset); m68kpc_offset += 2;
78 disp8 = dp & 0xFF;
79 r = (dp & 0x7000) >> 12;
80 dispreg = (dp & 0x8000 ? m68k_areg(regs,r) : m68k_dreg(regs,r));
81
82 if (!(dp & 0x800))
83 dispreg = (int32_t)(int16_t)(dispreg);
84
85 dispreg <<= (dp >> 9) & 3;
86
87 if (dp & 0x100)
88 {
89 int32_t outer = 0, disp = 0;
90 int32_t base = m68k_areg(regs,reg);
91 char name[10];
92 sprintf (name,"A%d, ",reg);
93 if (dp & 0x80) { base = 0; name[0] = 0; }
94 if (dp & 0x40) dispreg = 0;
95 if ((dp & 0x30) == 0x20) { disp = (int32_t)(int16_t)get_iword_1(m68kpc_offset); m68kpc_offset += 2; }
96 if ((dp & 0x30) == 0x30) { disp = get_ilong_1(m68kpc_offset); m68kpc_offset += 4; }
97 base += disp;
98
99 if ((dp & 0x3) == 0x2) { outer = (int32_t)(int16_t)get_iword_1(m68kpc_offset); m68kpc_offset += 2; }
100 if ((dp & 0x3) == 0x3) { outer = get_ilong_1(m68kpc_offset); m68kpc_offset += 4; }
101
102 if (!(dp & 4)) base += dispreg;
103 if (dp & 3) base = m68k_read_memory_32(base);
104 if (dp & 4) base += dispreg;
105
106 addr = base + outer;
107 sprintf(buffer,"(%s%c%d.%c*%d+%ld)+%ld == $%lX", name,
108 dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
109 1 << ((dp >> 9) & 3),
110 (long)disp, (long)outer, (unsigned long)addr);
111 }
112 else
113 {
114 addr = m68k_areg(regs,reg) + (int32_t)((int8_t)disp8) + dispreg;
115 sprintf (buffer,"(A%d, %c%d.%c*%d, $%X) == $%lX", reg,
116 dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
117 1 << ((dp >> 9) & 3), disp8, (unsigned long)addr);
118 }
119 break;
120 case PC16:
121 addr = m68k_getpc() + m68kpc_offset;
122 disp16 = get_iword_1(m68kpc_offset); m68kpc_offset += 2;
123 addr += (int16_t)disp16;
124 sprintf(buffer,"(PC, $%X) == $%lX", disp16 & 0xFFFF, (unsigned long)addr);
125 break;
126 case PC8r:
127 addr = m68k_getpc() + m68kpc_offset;
128 dp = get_iword_1(m68kpc_offset); m68kpc_offset += 2;
129 disp8 = dp & 0xFF;
130 r = (dp & 0x7000) >> 12;
131 dispreg = dp & 0x8000 ? m68k_areg(regs,r) : m68k_dreg(regs,r);
132
133 if (!(dp & 0x800))
134 dispreg = (int32_t)(int16_t)(dispreg);
135
136 dispreg <<= (dp >> 9) & 3;
137
138 if (dp & 0x100)
139 {
140 int32_t outer = 0,disp = 0;
141 int32_t base = addr;
142 char name[10];
143 sprintf (name,"PC, ");
144 if (dp & 0x80) { base = 0; name[0] = 0; }
145 if (dp & 0x40) dispreg = 0;
146 if ((dp & 0x30) == 0x20) { disp = (int32_t)(int16_t)get_iword_1(m68kpc_offset); m68kpc_offset += 2; }
147 if ((dp & 0x30) == 0x30) { disp = get_ilong_1(m68kpc_offset); m68kpc_offset += 4; }
148 base += disp;
149
150 if ((dp & 0x3) == 0x2)
151 {
152 outer = (int32_t)(int16_t)get_iword_1(m68kpc_offset);
153 m68kpc_offset += 2;
154 }
155
156 if ((dp & 0x3) == 0x3)
157 {
158 outer = get_ilong_1(m68kpc_offset);
159 m68kpc_offset += 4;
160 }
161
162 if (!(dp & 4)) base += dispreg;
163 if (dp & 3) base = m68k_read_memory_32(base);
164 if (dp & 4) base += dispreg;
165
166 addr = base + outer;
167 sprintf(buffer,"(%s%c%d.%c*%d+%ld)+%ld == $%lX", name,
168 dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
169 1 << ((dp >> 9) & 3), (long)disp, (long)outer, (unsigned long)addr);
170 }
171 else
172 {
173 addr += (int32_t)((int8_t)disp8) + dispreg;
174 sprintf(buffer,"(PC, %c%d.%c*%d, $%X) == $%lX", dp & 0x8000 ? 'A' : 'D',
175 (int)r, dp & 0x800 ? 'L' : 'W', 1 << ((dp >> 9) & 3),
176 disp8, (unsigned long)addr);
177 }
178 break;
179 case absw:
180 sprintf(buffer,"$%lX", (unsigned long)(int32_t)(int16_t)get_iword_1(m68kpc_offset));
181 m68kpc_offset += 2;
182 break;
183 case absl:
184 sprintf(buffer,"$%lX", (unsigned long)get_ilong_1(m68kpc_offset));
185 m68kpc_offset += 4;
186 break;
187 case imm:
188 switch (size)
189 {
190 case sz_byte:
191 sprintf(buffer,"#$%X", (unsigned int)(get_iword_1(m68kpc_offset) & 0xFF));
192 m68kpc_offset += 2;
193 break;
194 case sz_word:
195 sprintf(buffer,"#$%X", (unsigned int)(get_iword_1(m68kpc_offset) & 0xFFFF));
196 m68kpc_offset += 2;
197 break;
198 case sz_long:
199 sprintf(buffer,"#$%lX", (unsigned long)(get_ilong_1(m68kpc_offset)));
200 m68kpc_offset += 4;
201 break;
202 default:
203 break;
204 }
205 break;
206 case imm0:
207 offset = (int32_t)(int8_t)get_iword_1(m68kpc_offset);
208 m68kpc_offset += 2;
209 sprintf(buffer,"#$%X", (unsigned int)(offset & 0xFF));
210 break;
211 case imm1:
212 offset = (int32_t)(int16_t)get_iword_1(m68kpc_offset);
213 m68kpc_offset += 2;
214
215 if (mnemonic == i_MVMEL)
216 HandleMovem(buffer, offset, 0);
217 else if (mnemonic == i_MVMLE)
218 HandleMovem(buffer, offset, 1);
219 else
220 sprintf(buffer,"#$%X", (unsigned int)(offset & 0xFFFF));
221
222 break;
223 case imm2:
224 offset = (int32_t)get_ilong_1(m68kpc_offset);
225 m68kpc_offset += 4;
226 sprintf(buffer,"#$%lX", (unsigned long)(offset & 0xFFFFFFFF));
227 break;
228 case immi:
229 offset = (int32_t)(int8_t)(reg & 0xFF);
230 sprintf(buffer,"#$%lX", (unsigned long)(offset & 0xFFFFFFFF));
231 break;
232 default:
233 break;
234 }
235
236 // if (buf == 0)
237 // fprintf(f, "%s", buffer);
238 // else
239 strcat(buf, buffer);
240
241 return offset;
242 }
243
244
245 void HandleMovem(char * output, uint16_t data, int direction)
246 {
247 uint16_t ascending[16] = {
248 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
249 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 };
250 uint16_t descending[16] = {
251 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100,
252 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001 };
253
254 int i, j, first, runLength, firstPrint = 1;
255 char buf[16];
256 uint16_t * bitMask;
257
258 bitMask = (direction ? descending : ascending);
259 output[0] = 0;
260
261 // Handle D0-D7...
262 for(i=0; i<8; i++)
263 {
264 if (data & bitMask[i])
265 {
266 first = i;
267 runLength = 0;
268
269 for(j=i+1; j<8 && (data & bitMask[j]); j++)
270 runLength++;
271
272 i += runLength;
273
274 if (firstPrint)
275 firstPrint = 0;
276 else
277 strcat(output, "/");
278
279 sprintf(buf, "D%d", first);
280 strcat(output, buf);
281
282 if (runLength > 0)
283 {
284 sprintf(buf, "-D%d", first + runLength);
285 strcat(output, buf);
286 }
287 }
288 }
289
290 // Handle A0-A7...
291 for(i=0; i<8; i++)
292 {
293 if (data & bitMask[i + 8])
294 {
295 first = i;
296 runLength = 0;
297
298 for(j=i+1; j<8 && (data & bitMask[j+8]); j++)
299 runLength++;
300
301 i += runLength;
302
303 if (firstPrint)
304 firstPrint = 0;
305 else
306 strcat(output, "/");
307
308 sprintf(buf, "A%d", first);
309 strcat(output, buf);
310
311 if (runLength > 0)
312 {
313 sprintf(buf, "-A%d", first + runLength);
314 strcat(output, buf);
315 }
316 }
317 }
318 }
319
320
321 // Disassemble the M68K line based on the address
322 // Append, or not, the line with the ouput pointer and return the number of bytes
323 unsigned int M68KDisassemble(char * output, uint32_t addr, unsigned int OpCodes)
324 {
325 char f[256], str[256];
326 char src[256], dst[256];
327 static const char * const ccnames[] =
328 { "RA","RN","HI","LS","CC","CS","NE","EQ",
329 "VC","VS","PL","MI","GE","LT","GT","LE" };
330
331 str[0] = 0;
332 output[0] = 0;
333 uint32_t newpc = 0;
334 m68kpc_offset = addr - m68k_getpc();
335 long int pcOffsetSave = m68kpc_offset;
336 int opwords;
337 char instrname[20];
338 const struct mnemolookup * lookup;
339
340 // get the opcode
341 uint32_t opcode = get_iword_1(m68kpc_offset);
342 m68kpc_offset += 2;
343
344 // replace an illegal opcode by the illegal opcode
345 if (cpuFunctionTable[opcode] == IllegalOpcode)
346 opcode = 0x4AFC;
347
348 // point on the opcode information
349 struct instr * dp = table68k + opcode;
350
351 // get the instruction name from the opcode
352 for(lookup=lookuptab; lookup->mnemo!=dp->mnemo; lookup++);
353 strcpy(instrname, lookup->name);
354
355 // look for a branching instruction
356 char * ccpt = strstr(instrname, "cc");
357
358 // correct branch naming
359 if (ccpt)
360 strncpy(ccpt, ccnames[dp->cc], 2);
361
362 // keep the instruction name
363 sprintf(f, "%s", instrname);
364 strcat(str, f);
365
366 // set instruction size
367 switch (dp->size)
368 {
369 case sz_byte: strcat(str, ".B\t"); break;
370 case sz_word: strcat(str, ".W\t"); break;
371 case sz_long: strcat(str, ".L\t"); break;
372 default: strcat(str, "\t"); break;
373 }
374
375 // reset the buffers
376 src[0] = dst[0] = f[0] = 0;
377
378 // get source operand in src
379 if (dp->suse)
380 newpc = m68k_getpc() + m68kpc_offset + ShowEA(dp->mnemo, dp->sreg, dp->smode, dp->size, src);
381
382 // get destination operand in dst
383 if (dp->duse)
384 newpc = m68k_getpc() + m68kpc_offset + ShowEA(dp->mnemo, dp->dreg, dp->dmode, dp->size, dst);
385
386 // Handle execptions to the standard rules
387 if (dp->mnemo == i_BSR || dp->mnemo == i_Bcc)
388 sprintf(f, "$%lX", (long)newpc);
389 else if (dp->mnemo == i_DBcc)
390 sprintf(f, "%s, $%lX", src, (long)newpc);
391 else if (dp->mnemo == i_MVMEL)
392 sprintf(f, "%s, %s", dst, src);
393 else
394 sprintf(f, "%s%s%s", src, (dp->suse && dp->duse ? ", " : ""), dst);
395
396 strcat(str, f);
397
398 if (ccpt)
399 {
400 sprintf(f, " (%s)", (cctrue(dp->cc) ? "true" : "false"));
401 strcat(str, f);
402 }
403
404 // Add byte(s) display to front of disassembly
405 long int numberOfBytes = m68kpc_offset - pcOffsetSave;
406
407 for(opwords=0; opwords<5; opwords++)
408 {
409 if (((opwords + 1) * 2) <= numberOfBytes)
410 sprintf(f, "%04X ", get_iword_1(pcOffsetSave + opwords * 2));
411 else
412 sprintf(f, " ");
413
414 strcat(output, f);
415 }
416
417 // add the line to the output
418 OpCodes ? strcat(output, str) : strcpy(output, str);
419
420 return numberOfBytes;
421 }
422
423
424 //
425 // Disassemble one instruction at pc and store in str_buff
426 //
427 unsigned int m68k_disassemble(char * str_buff, unsigned int pc, unsigned int cpu_type, unsigned int OpCodes)
428 {
429 return M68KDisassemble(str_buff, pc, OpCodes);
430 }
431