Fix crash if text containing \n is printed at nonzero x
[lsnes.git] / src / emulation / bsnes-legacy / scpu-disasm.cpp
blob58da3d72ab330b1a445f819a71f06843b5f763ae
1 #include "interface/disassembler.hpp"
2 #include "library/hex.hpp"
3 #include "library/string.hpp"
4 #include <sstream>
5 #include <iomanip>
7 namespace
9 template<bool k> struct varsize {};
10 template<> struct varsize<false> { typedef uint8_t type_t; };
11 template<> struct varsize<true> { typedef uint16_t type_t; };
13 template<bool laddr, bool lacc> struct scpu_disassembler : public disassembler
15 scpu_disassembler(const std::string& n) : disassembler(n) {}
16 std::string disassemble(uint64_t base, std::function<unsigned char()> fetchpc);
19 struct ssmp_disassembler : public disassembler
21 ssmp_disassembler(const std::string& n) : disassembler(n) {}
22 std::string disassemble(uint64_t base, std::function<unsigned char()> fetchpc);
25 scpu_disassembler<false, false> d1("snes-xa");
26 scpu_disassembler<false, true> d2("snes-xA");
27 scpu_disassembler<true, false> d3("snes-Xa");
28 scpu_disassembler<true, true> d4("snes-XA");
29 ssmp_disassembler d5("snes-smp");
31 //%0: Byte-sized quantity, loaded before %b.
32 //%a: Accumulator-sized quantity.
33 //%b: Byte-sized quantity.
34 //%l: Long address.
35 //%r: Byte relative.
36 //%R: Word relative.
37 //%w: Word-sized quantity.
38 //%x: Index-sizeed quantity.
39 const char* scpu_instructions[] = {
40 "brk #%b", "ora (%b,x)", "cop #%b", "ora %b,s", //00
41 "tsb %b", "ora %b", "asl %b", "ora [%b]", //04
42 "php", "ora #%a", "asl a", "phd", //08
43 "tsb %w", "ora %w", "asl %w", "ora %l", //0C
44 "bpl %r", "ora (%b),y", "ora (%b)", "ora (%b,s),y", //10
45 "trb %b", "ora %b,x", "asl %b,x", "ora [%b],y", //14
46 "clc", "ora %w,y", "inc", "tcs", //18
47 "trb %w", "ora %w,x", "asl %w,x", "ora %l,x", //1C
48 "jsr %w", "and (%b,x)", "jsl %l", "and %b,s", //20
49 "bit %b", "and %b", "rol %b", "and [%b]", //24
50 "plp", "and #%a", "rol a", "pld", //28
51 "bit %w", "and %w", "rol %w", "and %l", //2C
52 "bmi %r", "and (%b),y", "and (%b)", "and (%b,s),y", //30
53 "bit %b", "and %b,x", "rol %b,x", "and [%b],y", //34
54 "sec", "and %w,y", "dec", "tsc", //38
55 "bit %w,x", "and %w,x", "rol %w,x", "and %l,x", //3C
56 "rti", "eor (%b,x)", "wdm #%b", "eor %b,s", //40
57 "mvp %b,%0", "eor %b", "lsr %b", "eor [%b]", //44
58 "pha", "eor #%a", "lsr a", "phk", //48
59 "jmp %w", "eor %w", "lsr %w", "eor %l", //4C
60 "bvc %r", "eor (%b),y", "eor (%b)", "eor (%b,s),y", //50
61 "mnv %b,%0", "eor %b,x", "lsr %b,x", "eor [%b],y", //54
62 "cli", "eor %w,y", "phy", "tcd", //58
63 "jml %l", "eor %w,x", "lsr %w,x", "eor %l,x", //5C
64 "rts", "adc (%b,x)", "per %w", "adc %b,s", //60
65 "stz %b", "adc %b", "ror %b", "adc [%b]", //64
66 "pla", "adc #%a", "ror a", "rtl", //68
67 "jmp (%w)", "adc %w", "ror %w", "adc %l", //6C
68 "bvs %r", "adc (%b),y", "adc (%b)", "adc (%b,s),y", //70
69 "stz %b,x", "adc %b,x", "ror %b,x", "adc [%b],y", //74
70 "sei", "adc %w,y", "ply", "tdc", //78
71 "jmp (%w,x)", "adc %w,x", "ror %w,x", "adc %l,x", //7C
72 "bra %r", "sta (%b,x)", "brl %R", "sta %b,s", //80
73 "sty %b", "sta %b", "stx %b", "sta [%b]", //84
74 "dey", "bit #%a", "txa", "phb", //88
75 "sty %w", "sta %w", "stx %w", "sta %l", //8C
76 "bcc %r", "sta (%b),y", "sta (%b)", "sta (%b,s),y", //90
77 "sty %b,x", "sta %b,x", "stx %b,y", "sta [%b],y", //94
78 "tya", "sta %w,y", "txs", "txy", //98
79 "stz %w", "sta %w,x", "stz %w,x", "sta %l,x", //9C
80 "ldy #%x", "lda (%b,x)", "ldx #%x", "lda %b,s", //A0
81 "ldy %b", "lda %b", "ldx %b", "lda [%b]", //A4
82 "tay", "lda #%a", "tax", "plb", //A8
83 "ldy %w", "lda %w", "ldx %w", "lda %l", //AC
84 "bcs %r", "lda (%b),y", "lda (%b)", "lda (%b,s),y", //B0
85 "ldy %b,x", "lda %b,x", "ldx %b,y", "lda [%b],y", //B4
86 "clv", "lda %w,y", "tsx", "tyx", //B8
87 "ldy %w,x", "lda %w,x", "ldx %w,x", "lda %l,x", //BC
88 "cpy #%x", "cmp (%b,x)", "rep #%b", "cmp %b,s", //C0
89 "cpy %b", "cmp %b", "dec %b", "cmp [%b]", //C4
90 "iny", "cmp #%a", "dex", "wai", //C8
91 "cpy %w", "cmp %w", "dec %w", "cmp %l", //CC
92 "bne %r", "cmp (%b),y", "cmp (%b)", "cmp (%b,s),y", //D0
93 "pei (%b)", "cmp %b,x", "dec %b,y", "cmp [%b],y", //D4
94 "cld", "cmp %w,y", "phx", "stp", //D8
95 "jmp [%w]", "cmp %w,x", "dec %w,x", "cmp %l,x", //DC
96 "cpx #%x", "sbc (%b,x)", "sep #%b", "sbc %b,s", //E0
97 "cpx %b", "sbc %b", "inc %b", "sbc [%b]", //E4
98 "inx", "sbc #%a", "nop", "xba", //E8
99 "cpx %w", "sbc %w", "inc %w", "sbc %l", //EC
100 "beq %r", "sbc (%b),y", "sbc (%b)", "sbc (%b,s),y", //F0
101 "pea %w", "sbc %b,x", "inc %b,y", "sbc [%b],y", //F4
102 "sed", "sbc %w,y", "plx", "xce", //F8
103 "jsr (%w,x)", "sbc %w,x", "inc %w,x", "sbc %l,x" //FC
106 //%0: Byte-sized quantity, loaded before %b.
107 //%b: Byte-sized quantity.
108 //%c: ??? <n>:<m> construct.
109 //%r: Relative
110 //%R: Relative
111 //%w: Word-sized quantity.
112 const char* ssmp_instructions[] = {
113 "nop", "jst $ffde", "set %b:0", "bbs %b:0=%R", //00
114 "ora %b", "ora %w", "ora (x)", "ora (%b,x)", //04
115 "ora #%b", "orr %b=%0", "orc %c", "asl %b", //08
116 "asl %w", "php", "tsb %w", "brk", //0C
117 "bpl %r", "jst $ffdc", "clr %b:0", "bbc %b:0=%R", //10
118 "ora %b,x", "ora %w,x", "ora %w,y", "ora (%b),y", //14
119 "orr %b=#%0", "orr (x)=(y)", "dew %b", "asl %b,x", //18
120 "asl", "dex", "cpx %w", "jmp (%w,x)", //1C
121 "clp", "jst $ffda", "set %b:1", "bbs %b:1=%R", //20
122 "and %b", "and %w", "and (x)", "and (%b,x)", //24
123 "and #%b", "and %b=%0", "orc !%c", "rol %b", //28
124 "rol %w", "pha", "bne %b=%R", "bra %r", //2C
125 "bmi %r", "jst $ffd8", "clr %b:1", "bbc %b:1=$R", //30
126 "and %b,x", "and %w,x", "and %w,y", "and (%b),y", //34
127 "and %b=#%0", "and (x)=(y)", "inw %b", "rol $b,x", //38
128 "rol", "inx", "cpx %b", "jsr %w", //3C
129 "sep", "jst $ffd6", "set %b:2", "bbs %b:2=$R", //40
130 "eor %b", "eor %w", "eor (x)", "eor ($b,x)", //44
131 "eor #%b", "eor %b=%0", "and %c", "lsr %b", //48
132 "lsr %w", "phx", "trb %w", "jsp $ff", //4C
133 "bvc %r", "jst $ffd4", "clr %b:2", "bbc %b:2=%R", //50
134 "eor %b,x", "eor %w,x", "eor %w,y", "eor (%b),y", //54
135 "eor %b=#%0", "eor (x)=(y)", "cpw %w", "lsr %b,x", //58
136 "lsr", "tax", "cpy %w", "jmp %w", //5C
137 "clc", "jst $ffd2", "set %b:3", "bbs %b:3=$R", //60
138 "cmp %b", "cmp %w", "cmp (x)", "cmp ($b,x)", //64
139 "cmp #%b", "cmp %b=%0", "and !%c", "ror %b", //68
140 "ror %w", "phy", "bne --$b=%R", "rts", //6C
141 "bvs %r", "jst $ffd0", "clr %b:3", "bbc %b:3=%R", //70
142 "cmp %b,x", "cmp %w,x", "cmp %w,y", "cmp (%b),y", //74
143 "cmp %b=#%0", "cmp (x)=(y)", "adw %w", "ror %b,x", //78
144 "ror", "txa", "cpy %b", "rti", //7C
145 "sec", "jst $ffce", "set %b:4", "bbs %b:4=$R", //80
146 "adc %b", "adc %w", "adc (x)", "adc ($b,x)", //84
147 "adc #%b", "cmp %b=%0", "eor %c", "dec %b", //88
148 "dec %w", "ldy #%b", "plp", "str %b=#%0", //8C
149 "bcc %r", "jst $ffcc", "clr %b:4", "bbc %b:4=%R", //90
150 "adc %b,x", "adc %w,x", "adc %w,y", "adc (%b),y", //94
151 "adc %b=#%0", "adc (x)=(y)", "sbw %w", "dec %b,x", //98
152 "dec", "tsx", "div", "xcn", //9C
153 "sei", "jst $ffca", "set %b:5", "bbs %b:5=$R", //A0
154 "sbc %b", "sbc %w", "sbc (x)", "sbc ($b,x)", //A4
155 "sbc #%b", "sbc %b=%0", "ldc %c", "inc %b", //A8
156 "inc %w", "cpy #%b", "pla", "sta (x++)", //AC
157 "bcs %r", "jst $ffc8", "clr %b:5", "bbc %b:5=%R", //B0
158 "sbc %b,x", "sbc %w,x", "sbc %w,y", "sbc (%b),y", //B4
159 "sbc %b=#%0", "sbc (x)=(y)", "ldw %b", "inc %b,x", //B8
160 "inc", "txs", "das", "lda (x++)", //BC
161 "cli", "jst $ffc6", "set %b:6", "bbs %b:6=$R", //C0
162 "sta %b", "sta %w", "sta (x)", "sta ($b,x)", //C4
163 "cpx #%b", "stx %w", "stc %c", "sty %b", //C8
164 "sty %w", "ldx #%b", "plx", "mul", //CC
165 "bne %r", "jst $ffc4", "clr %b:6", "bbc %b:6=%R", //D0
166 "sta %b,x", "sta %w,x", "sta %w,y", "sta (%b),y", //D4
167 "stx %b", "stx %b,y", "stw %b", "stw %b,x", //D8
168 "dey", "tya", "bne %b,x=%R", "daa", //DC
169 "clv", "jst $ffc2", "set %b:7", "bbs %b:7=$R", //E0
170 "lda %b", "lda %w", "lda (x)", "lda ($b,x)", //E4
171 "lda #%b", "ldx %w", "not %c", "ldy %b", //E8
172 "ldy %w", "cmc", "ply", "wai", //EC
173 "beq %r", "jst $ffc0", "clr %b:7", "bbc %b:7=%R", //F0
174 "lda %b,x", "lda %w,x", "lda %w,y", "lda (%b),y", //F4
175 "ldx %b", "ldx %b,y", "str %b=%0", "ldy %b,x", //F8
176 "iny", "tay", "bne --y=%r", "stp" //FC
179 template<bool laddr, bool lacc>
180 std::string scpu_disassembler<laddr, lacc>::disassemble(uint64_t base, std::function<unsigned char()> fetchpc)
182 std::ostringstream o;
183 const char* ins = scpu_instructions[fetchpc()];
184 uint8_t x = 0;
185 //Handle %0 specially.
186 for(size_t i = 0; ins[i]; i++)
187 if(ins[i] == '%' && ins[i + 1] == '0')
188 x = fetchpc();
190 for(size_t i = 0; ins[i]; i++) {
191 if(ins[i] != '%')
192 o << ins[i];
193 else {
194 switch(ins[i + 1]) {
195 case '0':
196 o << "$" << hex::to(x);
197 break;
198 case 'a':
199 o << "$" << hex::to(fetch_le<typename varsize<lacc>::type_t>(fetchpc));
200 break;
201 case 'b':
202 o << "$" << hex::to(fetch_le<uint8_t>(fetchpc));
203 break;
204 case 'l': {
205 uint16_t offset = fetch_le<uint16_t>(fetchpc);
206 o << "$" << hex::to(fetch_le<uint8_t>(fetchpc));
207 o << hex::to(offset);
208 break;
210 case 'r':
211 o << "$" << hex::to(static_cast<uint16_t>(base + 2 +
212 fetch_le<int8_t>(fetchpc)));
213 break;
214 case 'R':
215 o << "$" << hex::to(static_cast<uint16_t>(base + 3 +
216 fetch_le<int16_t>(fetchpc)));
217 break;
218 case 'w':
219 o << "$" << hex::to(fetch_le<uint16_t>(fetchpc));
220 break;
221 case 'x':
222 o << "$" << hex::to(fetch_le<typename varsize<laddr>::type_t>(fetchpc));
223 break;
225 i++;
228 return o.str();
231 std::string ssmp_disassembler::disassemble(uint64_t base, std::function<unsigned char()> fetchpc)
233 std::ostringstream o;
234 const char* ins = ssmp_instructions[fetchpc()];
235 uint8_t x = 0;
236 uint16_t tmp = 0;
237 //Handle %0 specially.
238 for(size_t i = 0; ins[i]; i++)
239 if(ins[i] == '%' && ins[i + 1] == '0')
240 x = fetchpc();
242 for(size_t i = 0; ins[i]; i++) {
243 if(ins[i] != '%')
244 o << ins[i];
245 else {
246 switch(ins[i + 1]) {
247 case '0':
248 o << "$" << hex::to(x);
249 break;
250 case 'b':
251 o << "$" << hex::to(fetch_le<uint8_t>(fetchpc));
252 break;
253 case 'c':
254 tmp = fetch_le<uint16_t>(fetchpc);
255 o << "$" << hex::to(static_cast<uint16_t>(tmp & 0x1FFF)) << ":"
256 << (tmp >> 13);
257 break;
258 case 'r':
259 o << "$" << hex::to(static_cast<uint16_t>(base + 2 +
260 fetch_le<int8_t>(fetchpc)));
261 break;
262 case 'R':
263 o << "$" << hex::to(static_cast<uint16_t>(base + 3 +
264 fetch_le<int8_t>(fetchpc)));
265 case 'w':
266 o << "$" << hex::to(fetch_le<uint16_t>(fetchpc));
267 break;
269 i++;
272 return o.str();