1 /************************************************************************/
2 /* Arm/Thumb command set disassembler                                   */
3 /************************************************************************/
4 #include <stdio.h>
5 
6 #include "../System.h"
7 #include "../common/Port.h"
8 #include "GBA.h"
9 #include "armdis.h"
10 #include "elf.h"
11 
12 struct Opcodes {
13   u32 mask;
14   u32 cval;
15   const char *mnemonic;
16 };
17 
18 #define debuggerReadMemory(addr) \
19   READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
20 
21 #define debuggerReadHalfWord(addr) \
22   READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
23 
24 #define debuggerReadByte(addr) \
25   map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]
26 
27 const char hdig[] = "0123456789abcdef";
28 
29 const char *decVals[16] = {
30   "0","1","2","3","4","5","6","7","8",
31   "9","10","11","12","13","14","15"
32 };
33 
34 const char *regs[16] = {
35   "r0","r1","r2","r3","r4","r5","r6","r7",
36   "r8","r9","r10","r11","r12","sp","lr","pc"
37 };
38 
39 const char *conditions[16] = {
40   "eq","ne","cs","cc","mi","pl","vs","vc",
41   "hi","ls","ge","lt","gt","le","","nv"
42 };
43 
44 const char *shifts[5] = {
45   "lsl","lsr","asr","ror","rrx"
46 };
47 
48 const char *armMultLoadStore[12] = {
49   // non-stack
50   "da","ia","db","ib",
51   // stack store
52   "ed","ea","fd","fa",
53   // stack load
54   "fa","fd","ea","ed"
55 };
56 
57 const Opcodes thumbOpcodes[] = {
58   // Format 1
59   {0xf800, 0x0000, "lsl %r0, %r3, %o"},
60   {0xf800, 0x0800, "lsr %r0, %r3, %o"},
61   {0xf800, 0x1000, "asr %r0, %r3, %o"},
62   // Format 2
63   {0xfe00, 0x1800, "add %r0, %r3, %r6"},
64   {0xfe00, 0x1a00, "sub %r0, %r3, %r6"},
65   {0xfe00, 0x1c00, "add %r0, %r3, %i"},
66   {0xfe00, 0x1e00, "sub %r0, %r3, %i"},
67   // Format 3
68   {0xf800, 0x2000, "mov %r8, %O"},
69   {0xf800, 0x2800, "cmp %r8, %O"},
70   {0xf800, 0x3000, "add %r8, %O"},
71   {0xf800, 0x3800, "sub %r8, %O"},
72   // Format 4
73   {0xffc0, 0x4000, "and %r0, %r3"},
74   {0xffc0, 0x4040, "eor %r0, %r3"},
75   {0xffc0, 0x4080, "lsl %r0, %r3"},
76   {0xffc0, 0x40c0, "lsr %r0, %r3"},
77   {0xffc0, 0x4100, "asr %r0, %r3"},
78   {0xffc0, 0x4140, "adc %r0, %r3"},
79   {0xffc0, 0x4180, "sbc %r0, %r3"},
80   {0xffc0, 0x41c0, "ror %r0, %r3"},
81   {0xffc0, 0x4200, "tst %r0, %r3"},
82   {0xffc0, 0x4240, "neg %r0, %r3"},
83   {0xffc0, 0x4280, "cmp %r0, %r3"},
84   {0xffc0, 0x42c0, "cmn %r0, %r3"},
85   {0xffc0, 0x4300, "orr %r0, %r3"},
86   {0xffc0, 0x4340, "mul %r0, %r3"},
87   {0xffc0, 0x4380, "bic %r0, %r3"},
88   {0xffc0, 0x43c0, "mvn %r0, %r3"},
89   // Format 5
90   {0xff80, 0x4700, "bx %h36"},
91   {0xfcc0, 0x4400, "[ ??? ]"},
92   {0xff00, 0x4400, "add %h07, %h36"},
93   {0xff00, 0x4500, "cmp %h07, %h36"},
94   {0xff00, 0x4600, "mov %h07, %h36"},
95   // Format 6
96   {0xf800, 0x4800, "ldr %r8, [%I] (=%J)"},
97   // Format 7
98   {0xfa00, 0x5000, "str%b %r0, [%r3, %r6]"},
99   {0xfa00, 0x5800, "ldr%b %r0, [%r3, %r6]"},
100   // Format 8
101   {0xfe00, 0x5200, "strh %r0, [%r3, %r6]"},
102   {0xfe00, 0x5600, "ldsb %r0, [%r3, %r6]"},
103   {0xfe00, 0x5a00, "ldrh %r0, [%r3, %r6]"},
104   {0xfe00, 0x5e00, "ldsh %r0, [%r3, %r6]"},
105   // Format 9
106   {0xe800, 0x6000, "str%B %r0, [%r3, %p]"},
107   {0xe800, 0x6800, "ldr%B %r0, [%r3, %p]"},
108   // Format 10
109   {0xf800, 0x8000, "strh %r0, [%r3, %e]"},
110   {0xf800, 0x8800, "ldrh %r0, [%r3, %e]"},
111   // Format 11
112   {0xf800, 0x9000, "str %r8, [sp, %w]"},
113   {0xf800, 0x9800, "ldr %r8, [sp, %w]"},
114   // Format 12
115   {0xf800, 0xa000, "add %r8, pc, %w (=%K)"},
116   {0xf800, 0xa800, "add %r8, sp, %w"},
117   // Format 13
118   {0xff00, 0xb000, "add sp, %s"},
119   // Format 14
120   {0xffff, 0xb500, "push {lr}"},
121   {0xff00, 0xb400, "push {%l}"},
122   {0xff00, 0xb500, "push {%l,lr}"},
123   {0xffff, 0xbd00, "pop {pc}"},
124   {0xff00, 0xbd00, "pop {%l,pc}"},
125   {0xff00, 0xbc00, "pop {%l}"},
126   // Format 15
127   {0xf800, 0xc000, "stmia %r8!, {%l}"},
128   {0xf800, 0xc800, "ldmia %r8!, {%l}"},
129   // Format 17
130   {0xff00, 0xdf00, "swi %m"},
131   // Format 16
132   {0xf000, 0xd000, "b%c %W"},
133   // Format 18
134   {0xf800, 0xe000, "b %a"},
135   // Format 19
136   {0xf800, 0xf000, "bl %A"},
137   {0xf800, 0xf800, "blh %Z"},
138   {0xff00, 0xbe00, "bkpt %O"},
139   // Unknown
140   {0x0000, 0x0000, "[ ??? ]"}
141 };
142 
143 const Opcodes armOpcodes[] = {
144   // Undefined
145   {0x0e000010, 0x06000010, "[ undefined ]"},
146   // Branch instructions
147   {0x0ff000f0, 0x01200010, "bx%c %r0"},
148   {0x0f000000, 0x0a000000, "b%c %o"},
149   {0x0f000000, 0x0b000000, "bl%c %o"},
150   {0x0f000000, 0x0f000000, "swi%c %q"},
151   // PSR transfer
152   {0x0fbf0fff, 0x010f0000, "mrs%c %r3, %p"},
153   {0x0db0f000, 0x0120f000, "msr%c %p, %i"},
154   // Multiply instructions
155   {0x0fe000f0, 0x00000090, "mul%c%s %r4, %r0, %r2"},
156   {0x0fe000f0, 0x00200090, "mla%c%s %r4, %r0, %r2, %r3"},
157   {0x0fa000f0, 0x00800090, "%umull%c%s %r3, %r4, %r0, %r2"},
158   {0x0fa000f0, 0x00a00090, "%umlal%c%s %r3, %r4, %r0, %r2"},
159   // Load/Store instructions
160   {0x0fb00ff0, 0x01000090, "swp%c%b %r3, %r0, [%r4]"},
161   {0x0fb000f0, 0x01000090, "[ ??? ]"},
162   {0x0c100000, 0x04000000, "str%c%b%t %r3, %a"},
163   {0x0c100000, 0x04100000, "ldr%c%b%t %r3, %a"},
164   {0x0e100090, 0x00000090, "str%c%h %r3, %a"},
165   {0x0e100090, 0x00100090, "ldr%c%h %r3, %a"},
166   {0x0e100000, 0x08000000, "stm%c%m %r4%l"},
167   {0x0e100000, 0x08100000, "ldm%c%m %r4%l"},
168   // Data processing
169   {0x0de00000, 0x00000000, "and%c%s %r3, %r4, %i"},
170   {0x0de00000, 0x00200000, "eor%c%s %r3, %r4, %i"},
171   {0x0de00000, 0x00400000, "sub%c%s %r3, %r4, %i"},
172   {0x0de00000, 0x00600000, "rsb%c%s %r3, %r4, %i"},
173   {0x0de00000, 0x00800000, "add%c%s %r3, %r4, %i"},
174   {0x0de00000, 0x00a00000, "adc%c%s %r3, %r4, %i"},
175   {0x0de00000, 0x00c00000, "sbc%c%s %r3, %r4, %i"},
176   {0x0de00000, 0x00e00000, "rsc%c%s %r3, %r4, %i"},
177   {0x0de00000, 0x01000000, "tst%c%s %r4, %i"},
178   {0x0de00000, 0x01200000, "teq%c%s %r4, %i"},
179   {0x0de00000, 0x01400000, "cmp%c%s %r4, %i"},
180   {0x0de00000, 0x01600000, "cmn%c%s %r4, %i"},
181   {0x0de00000, 0x01800000, "orr%c%s %r3, %r4, %i"},
182   {0x0de00000, 0x01a00000, "mov%c%s %r3, %i"},
183   {0x0de00000, 0x01c00000, "bic%c%s %r3, %r4, %i"},
184   {0x0de00000, 0x01e00000, "mvn%c%s %r3, %i"},
185   // Coprocessor operations
186   {0x0f000010, 0x0e000000, "cdp%c %P, %N, %r3, %R4, %R0%V"},
187   {0x0e100000, 0x0c000000, "stc%c%L %P, %r3, %A"},
188   {0x0f100010, 0x0e000010, "mcr%c %P, %N, %r3, %R4, %R0%V"},
189   {0x0f100010, 0x0e100010, "mrc%c %P, %N, %r3, %R4, %R0%V"},
190   // Unknown
191   {0x00000000, 0x00000000, "[ ??? ]"}
192 };
193 
addStr(char * dest,const char * src)194 char* addStr(char *dest, const char *src){
195   while (*src){
196     *dest++ = *src++;
197   }
198   return dest;
199 }
200 
addHex(char * dest,int siz,u32 val)201 char* addHex(char *dest, int siz, u32 val){
202   if (siz==0){
203     siz = 28;
204     while ( (((val>>siz)&15)==0) && (siz>=4) )
205       siz -= 4;
206     siz += 4;
207   }
208   while (siz>0){
209     siz -= 4;
210     *dest++ = hdig[(val>>siz)&15];
211   }
212   return dest;
213 }
214 
disArm(u32 offset,char * dest,int flags)215 int disArm(u32 offset, char *dest, int flags){
216   u32 opcode = debuggerReadMemory(offset);
217 
218   const Opcodes *sp = armOpcodes;
219   while( sp->cval != (opcode & sp->mask) )
220     sp++;
221 
222   if (flags&DIS_VIEW_ADDRESS){
223     dest = addHex(dest, 32, offset);
224     *dest++ = ' ';
225   }
226   if (flags&DIS_VIEW_CODE){
227     dest = addHex(dest, 32, opcode);
228     *dest++ = ' ';
229   }
230 
231   const char *src = sp->mnemonic;
232   while (*src){
233     if (*src!='%')
234       *dest++ = *src++;
235     else{
236       src++;
237       switch (*src){
238       case 'c':
239         dest = addStr(dest, conditions[opcode>>28]);
240         break;
241       case 'r':
242         dest = addStr(dest, regs[(opcode>>((*(++src)-'0')*4))&15]);
243         break;
244       case 'o':
245         {
246           *dest++ = '$';
247           int off = opcode&0xffffff;
248           if (off&0x800000)
249             off |= 0xff000000;
250           off <<= 2;
251           dest = addHex(dest, 32, offset+8+off);
252         }
253         break;
254       case 'i':
255         if (opcode&(1<<25)){
256           dest = addStr(dest, "#0x");
257           int imm = opcode&0xff;
258           int rot = (opcode&0xf00)>>7;
259           int val = (imm<<(32-rot))|(imm>>rot);
260           dest = addHex(dest, 0, val);
261         } else{
262           dest = addStr(dest, regs[opcode&0x0f]);
263           int shi = (opcode>>5)&3;
264           int sdw = (opcode>>7)&0x1f;
265           if ((sdw==0)&&(shi==3))
266             shi = 4;
267           if ( (sdw) || (opcode&0x10) || (shi)) {
268             dest = addStr(dest, ", ");
269             dest = addStr(dest, shifts[shi]);
270             if (opcode&0x10){
271               *dest++ = ' ';
272               dest = addStr(dest, regs[(opcode>>8)&15]);
273             } else {
274               if (sdw==0 && ( (shi==1) || (shi==2) ))
275                 sdw = 32;
276               if(shi != 4) {
277                 dest = addStr(dest, " #0x");
278                 dest = addHex(dest, 8, sdw);
279               }
280             }
281           }
282         }
283         break;
284       case 'p':
285         if (opcode&(1<<22))
286           dest = addStr(dest, "spsr");
287         else
288           dest = addStr(dest, "cpsr");
289         if(opcode & 0x00F00000) {
290           *dest++ = '_';
291           if(opcode & 0x00080000)
292             *dest++ = 'f';
293           if(opcode & 0x00040000)
294             *dest++ = 's';
295           if(opcode & 0x00020000)
296             *dest++ = 'x';
297           if(opcode & 0x00010000)
298             *dest++ = 'c';
299         }
300         break;
301       case 's':
302         if (opcode&(1<<20))
303           *dest++ = 's';
304         break;
305       case 'S':
306         if (opcode&(1<<22))
307           *dest++ = 's';
308         break;
309       case 'u':
310         if (opcode&(1<<22))
311           *dest++ = 's';
312         else
313           *dest++ = 'u';
314         break;
315       case 'b':
316         if (opcode&(1<<22))
317           *dest++ = 'b';
318         break;
319       case 'a':
320         if ((opcode&0x076f0000)==0x004f0000){
321           *dest++ = '[';
322           *dest++ = '$';
323           int adr = offset+8;
324           int add = (opcode&15)|((opcode>>8)&0xf0);
325           if (opcode&(1<<23))
326             adr += add;
327           else
328             adr -= add;
329           dest = addHex(dest, 32, adr);
330           *dest++ = ']';
331           dest = addStr(dest, " (=");
332           *dest++ = '$';
333           dest = addHex(dest ,32, debuggerReadMemory(adr));
334           *dest++=')';
335         }
336         if ((opcode&0x072f0000)==0x050f0000){
337           *dest++ = '[';
338           *dest++ = '$';
339           int adr = offset+8;
340           if (opcode&(1<<23))
341             adr += opcode&0xfff;
342           else
343             adr -= opcode&0xfff;
344           dest = addHex(dest, 32, adr);
345           *dest++ = ']';
346           dest = addStr(dest, " (=");
347           *dest++ = '$';
348           dest = addHex(dest ,32, debuggerReadMemory(adr));
349           *dest++=')';
350         } else {
351           int reg = (opcode>>16)&15;
352           *dest++ = '[';
353           dest = addStr(dest, regs[reg]);
354           if (!(opcode&(1<<24)))
355             *dest++ = ']';
356           if ( ((opcode&(1<<25))&&(opcode&(1<<26))) || (!(opcode&(1<<22))&&!(opcode&(1<<26))) ){
357             dest = addStr(dest, ", ");
358             if (!(opcode&(1<<23)))
359               *dest++ = '-';
360             dest = addStr(dest, regs[opcode&0x0f]);
361             int shi = (opcode>>5)&3;
362             if (opcode&(1<<26)){
363               if ( ((opcode>>7)&0x1f) || (opcode&0x10) || (shi==1) || (shi==2)){
364                 dest = addStr(dest, ", ");
365                 dest = addStr(dest, shifts[shi]);
366                 if (opcode&0x10){
367                   *dest++ = ' ';
368                   dest = addStr(dest, regs[(opcode>>8)&15]);
369                 } else {
370                   int sdw = (opcode>>7)&0x1f;
371                   if (sdw==0 && ( (shi==1) || (shi==2) ))
372                     sdw = 32;
373                   dest = addStr(dest, " #0x");
374                   dest = addHex(dest, 8, sdw);
375                 }
376               }
377             }
378           } else {
379             int off;
380             if (opcode&(1<<26))
381               off = opcode&0xfff;
382             else
383               off = (opcode&15)|((opcode>>4)&0xf0);
384             if (off){
385               dest = addStr(dest, ", ");
386               if (!(opcode&(1<<23)))
387                 *dest++ = '-';
388               dest = addStr(dest, "#0x");
389               dest = addHex(dest, 0, off);
390             }
391           }
392           if (opcode&(1<<24)){
393             *dest++ = ']';
394             if (opcode&(1<<21))
395               *dest++ = '!';
396           }
397         }
398         break;
399       case 't':
400         if ((opcode&0x01200000)==0x01200000)
401           *dest++ = 't';
402         break;
403       case 'h':
404         if (opcode&(1<<6))
405           *dest++ = 's';
406         if (opcode&(1<<5))
407           *dest++ = 'h';
408         else
409           *dest++ = 'b';
410         break;
411       case 'm':
412         if (((opcode>>16)&15)==13) {
413           if(opcode & 0x00100000)
414             dest = addStr(dest, armMultLoadStore[8+((opcode>>23)&3)]);
415           else
416             dest = addStr(dest, armMultLoadStore[4+((opcode>>23)&3)]);
417         } else
418           dest = addStr(dest, armMultLoadStore[(opcode>>23)&3]);
419         break;
420       case 'l':
421         if (opcode&(1<<21))
422           *dest++ = '!';
423         dest = addStr(dest, ", {");
424         {
425           int rlst = opcode&0xffff;
426           int msk = 0;
427           int not_first = 0;
428           while (msk<16){
429             if (rlst&(1<<msk)){
430               int fr = msk;
431               while (rlst&(1<<msk))
432                 msk++;
433               int to = msk-1;
434               if (not_first)
435                 //dest = addStr(dest, ", ");
436                 *dest++ = ',';
437               dest = addStr(dest, regs[fr]);
438               if (fr!=to){
439                 if (fr==to-1)
440                   //dest = addStr(", ");
441                   *dest++ = ',';
442                 else
443                   *dest++ = '-';
444                 dest = addStr(dest, regs[to]);
445               }
446               not_first = 1;
447             } else
448               msk++;
449           }
450           *dest++ = '}';
451           if (opcode&(1<<22))
452             *dest++ = '^';
453         }
454         break;
455       case 'q':
456         *dest++ = '$';
457         dest = addHex(dest, 24, opcode&0xffffff);
458         break;
459       case 'P':
460         *dest++ = 'p';
461         dest = addStr(dest, decVals[(opcode>>8)&15]);
462         break;
463       case 'N':
464         if (opcode&0x10)
465           dest = addStr(dest, decVals[(opcode>>21)&7]);
466         else
467           dest = addStr(dest, decVals[(opcode>>20)&15]);
468         break;
469       case 'R':
470         {
471           src++;
472           int reg = 4*(*src-'0');
473           *dest++ = 'c';
474           dest = addStr(dest, decVals[(opcode>>reg)&15]);
475         }
476         break;
477       case 'V':
478         {
479           int val = (opcode>>5)&7;
480           if (val){
481             dest = addStr(dest, ", ");
482             dest = addStr(dest, decVals[val]);
483           }
484         }
485         break;
486       case 'L':
487         if (opcode&(1<<22))
488           *dest++ = 'l';
489         break;
490       case 'A':
491         if ((opcode&0x012f0000)==0x010f0000){
492           int adr = offset+8;
493           int add = (opcode&0xff)<<2;
494           if (opcode&(1<<23))
495             adr += add;
496           else
497             adr -= add;
498           *dest++ = '$';
499           addHex(dest, 32, adr);
500         } else {
501           *dest++ = '[';
502           dest = addStr(dest, regs[(opcode>>16)&15]);
503           if (!(opcode&(1<<24)))
504             *dest++ = ']';
505           int off = (opcode&0xff)<<2;
506           if (off){
507             dest = addStr(dest, ", ");
508             if (!(opcode&(1<<23)))
509               *dest++ = '-';
510             dest = addStr(dest, "#0x");
511             dest = addHex(dest, 0, off);
512           }
513           if (opcode&(1<<24)){
514             *dest++ = ']';
515             if (opcode&(1<<21))
516               *dest++ = '!';
517           }
518         }
519         break;
520       }
521       src++;
522     }
523   }
524   *dest++ = 0;
525 
526   return 4;
527 }
528 
disThumb(u32 offset,char * dest,int flags)529 int disThumb(u32 offset, char *dest, int flags){
530   u32 opcode = debuggerReadHalfWord(offset);
531 
532   const Opcodes *sp = thumbOpcodes;
533   int ret = 2;
534   while( sp->cval != (opcode & sp->mask) )
535     sp++;
536 
537   if (flags&DIS_VIEW_ADDRESS){
538     dest = addHex(dest, 32, offset);
539     *dest++ = ' ';
540   }
541   if (flags&DIS_VIEW_CODE){
542     dest = addHex(dest, 16, opcode);
543     *dest++ = ' ';
544   }
545 
546   const char *src = sp->mnemonic;
547   while (*src){
548     if (*src!='%')
549       *dest++ = *src++;
550     else {
551       src++;
552       switch (*src){
553       case 'r':
554         src++;
555         dest = addStr(dest, regs[(opcode>>(*src-'0'))&7]);
556         break;
557       case 'o':
558         dest = addStr(dest, "#0x");
559         {
560           int val = (opcode>>6)&0x1f;
561           dest = addHex(dest, 8, val);
562         }
563         break;
564       case 'p':
565         dest = addStr(dest, "#0x");
566         {
567           int val = (opcode>>6)&0x1f;
568           if (!(opcode&(1<<12)))
569             val <<= 2;
570           dest = addHex(dest, 0, val);
571         }
572         break;
573       case 'e':
574         dest = addStr(dest, "#0x");
575         dest = addHex(dest, 0, ((opcode>>6)&0x1f)<<1);
576         break;
577       case 'i':
578         dest = addStr(dest, "#0x");
579         dest = addHex(dest, 0, (opcode>>6)&7);
580         break;
581       case 'h':
582         {
583           src++;
584           int reg = (opcode>>(*src-'0'))&7;
585           src++;
586           if (opcode&(1<<(*src-'0')))
587             reg += 8;
588           dest = addStr(dest, regs[reg]);
589         }
590         break;
591       case 'O':
592         dest = addStr(dest, "#0x");
593         dest = addHex(dest, 0, (opcode&0xff));
594         break;
595       case 'I':
596         *dest++ = '$';
597         dest = addHex(dest, 32, (offset&0xfffffffc)+4+((opcode&0xff)<<2));
598         break;
599       case 'J':
600         {
601           u32 value = debuggerReadMemory((offset&0xfffffffc)+4+
602                                          ((opcode & 0xff)<<2));
603           *dest++ = '$';
604           dest = addHex(dest, 32, value);
605           const char *s = elfGetAddressSymbol(value);
606           if(*s) {
607             *dest++ = ' ';
608             dest = addStr(dest, s);
609           }
610         }
611         break;
612       case 'K':
613         {
614           u32 value = (offset&0xfffffffc)+4+((opcode & 0xff)<<2);
615           *dest++ = '$';
616           dest = addHex(dest, 32, value);
617           const char *s = elfGetAddressSymbol(value);
618           if(*s) {
619             *dest++ = ' ';
620             dest = addStr(dest, s);
621           }
622         }
623         break;
624       case 'b':
625         if (opcode&(1<<10))
626           *dest++ = 'b';
627         break;
628       case 'B':
629         if (opcode&(1<<12))
630           *dest++ = 'b';
631         break;
632       case 'w':
633         dest = addStr(dest, "#0x");
634         dest = addHex(dest, 0, (opcode&0xff)<<2);
635         break;
636       case 'W':
637         *dest++ = '$';
638         {
639           int add = opcode&0xff;
640           if (add&0x80)
641             add |= 0xffffff00;
642           dest = addHex(dest, 32, (offset&0xfffffffe)+4+(add<<1));
643         }
644         break;
645       case 'c':
646         dest = addStr(dest, conditions[(opcode>>8)&15]);
647         break;
648       case 's':
649         if (opcode&(1<<7))
650           *dest++ = '-';
651         dest = addStr(dest, "#0x");
652         dest = addHex(dest, 0, (opcode&0x7f)<<2);
653         break;
654       case 'l':
655         {
656           int rlst = opcode&0xff;
657           int msk = 0;
658           int not_first = 0;
659           while (msk<8){
660             if (rlst&(1<<msk)){
661               int fr = msk;
662               while (rlst&(1<<msk))
663                 msk++;
664               int to = msk-1;
665               if (not_first)
666                 *dest++ = ',';
667               dest = addStr(dest, regs[fr]);
668               if (fr!=to){
669                 if (fr==to-1)
670                   *dest++ = ',';
671                 else
672                   *dest++ = '-';
673                 dest = addStr(dest, regs[to]);
674               }
675               not_first = 1;
676             } else
677               msk++;
678           }
679         }
680         break;
681       case 'm':
682         *dest++ = '$';
683         dest = addHex(dest, 8, opcode&0xff);
684         break;
685       case 'Z':
686         *dest++ = '$';
687         dest = addHex(dest, 16, (opcode&0x7ff)<<1);
688         break;
689       case 'a':
690         *dest++ = '$';
691         {
692           int add = opcode&0x07ff;
693           if (add&0x400)
694             add |= 0xfffff800;
695           add <<= 1;
696           dest = addHex(dest, 32, offset+4+add);
697         }
698         break;
699       case 'A':
700         {
701           int nopcode = debuggerReadHalfWord(offset+2);
702           int add = opcode&0x7ff;
703           if (add&0x400)
704             add |= 0xfff800;
705           add = (add<<12)|((nopcode&0x7ff)<<1);
706           *dest++ = '$';
707           dest = addHex(dest,32, offset+4+add);
708           const char *s = elfGetAddressSymbol(offset+4+add);
709           if(*s) {
710             *dest++ = ' ';
711             *dest++ = '(';
712             dest = addStr(dest, s);
713             *dest++ = ')';
714           }
715           ret = 4;
716         }
717         break;
718       }
719       src++;
720     }
721   }
722   *dest++ = 0;
723   return ret;
724 }
725