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