1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | //============================================================================ |
19 | // This class provides Thumb emulation code ("Thumbulator") |
20 | // by David Welch (dwelch@dwelch.com) |
21 | // Modified by Fred Quimby |
22 | // Code is public domain and used with the author's consent |
23 | //============================================================================ |
24 | |
25 | #include "bspf.hxx" |
26 | #include "Base.hxx" |
27 | #include "Cart.hxx" |
28 | #include "Thumbulator.hxx" |
29 | using Common::Base; |
30 | |
31 | // Uncomment the following to enable specific functionality |
32 | // WARNING!!! This slows the runtime to a crawl |
33 | // #define THUMB_DISS |
34 | //#define THUMB_DBUG |
35 | |
36 | #if defined(THUMB_DISS) |
37 | #define DO_DISS(statement) statement |
38 | #else |
39 | #define DO_DISS(statement) |
40 | #endif |
41 | #if defined(THUMB_DBUG) |
42 | #define DO_DBUG(statement) statement |
43 | #else |
44 | #define DO_DBUG(statement) |
45 | #endif |
46 | |
47 | #ifdef __BIG_ENDIAN__ |
48 | #define CONV_DATA(d) (((d & 0xFFFF)>>8) | ((d & 0xffff)<<8)) & 0xffff |
49 | #define CONV_RAMROM(d) ((d>>8) | (d<<8)) & 0xffff |
50 | #else |
51 | #define CONV_DATA(d) (d & 0xFFFF) |
52 | #define CONV_RAMROM(d) (d) |
53 | #endif |
54 | |
55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
56 | Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt16 rom_size, |
57 | bool traponfatal, Thumbulator::ConfigureFor configurefor, |
58 | Cartridge* cartridge) |
59 | : rom(rom_ptr), |
60 | romSize(rom_size), |
61 | decodedRom(new Op[romSize / 2]), |
62 | ram(ram_ptr), |
63 | T1TCR(0), |
64 | T1TC(0), |
65 | configuration(configurefor), |
66 | myCartridge(cartridge) |
67 | { |
68 | for(uInt16 i = 0; i < romSize / 2; ++i) |
69 | decodedRom[i] = decodeInstructionWord(CONV_RAMROM(rom[i])); |
70 | |
71 | setConsoleTiming(ConsoleTiming::ntsc); |
72 | #ifndef UNSAFE_OPTIMIZATIONS |
73 | trapFatalErrors(traponfatal); |
74 | #endif |
75 | reset(); |
76 | } |
77 | |
78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
79 | string Thumbulator::run() |
80 | { |
81 | reset(); |
82 | for(;;) |
83 | { |
84 | if(execute()) break; |
85 | #ifndef UNSAFE_OPTIMIZATIONS |
86 | if(instructions > 500000) // way more than would otherwise be possible |
87 | throw runtime_error("instructions > 500000" ); |
88 | #endif |
89 | } |
90 | #if defined(THUMB_DISS) || defined(THUMB_DBUG) |
91 | dump_counters(); |
92 | cout << statusMsg.str() << endl; |
93 | return statusMsg.str(); |
94 | #else |
95 | return "" ; |
96 | #endif |
97 | } |
98 | |
99 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
100 | void Thumbulator::setConsoleTiming(ConsoleTiming timing) |
101 | { |
102 | // this sets how many ticks of the Harmony/Melody clock |
103 | // will occur per tick of the 6507 clock |
104 | constexpr double NTSC = 70.0 / 1.193182; // NTSC 6507 clock rate |
105 | constexpr double PAL = 70.0 / 1.182298; // PAL 6507 clock rate |
106 | constexpr double SECAM = 70.0 / 1.187500; // SECAM 6507 clock rate |
107 | |
108 | switch(timing) |
109 | { |
110 | case ConsoleTiming::ntsc: timing_factor = NTSC; break; |
111 | case ConsoleTiming::secam: timing_factor = SECAM; break; |
112 | case ConsoleTiming::pal: timing_factor = PAL; break; |
113 | } |
114 | } |
115 | |
116 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
117 | void Thumbulator::updateTimer(uInt32 cycles) |
118 | { |
119 | if (T1TCR & 1) // bit 0 controls timer on/off |
120 | T1TC += uInt32(cycles * timing_factor); |
121 | } |
122 | |
123 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
124 | string Thumbulator::run(uInt32 cycles) |
125 | { |
126 | updateTimer(cycles); |
127 | return run(); |
128 | } |
129 | |
130 | #ifndef UNSAFE_OPTIMIZATIONS |
131 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
132 | inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg) |
133 | { |
134 | statusMsg << "Thumb ARM emulation fatal error: " << endl |
135 | << opcode << "(" << Base::HEX8 << v1 << "), " << msg << endl; |
136 | dump_regs(); |
137 | if(trapOnFatal) |
138 | throw runtime_error(statusMsg.str()); |
139 | return 0; |
140 | } |
141 | |
142 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
143 | inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2, |
144 | const char* msg) |
145 | { |
146 | statusMsg << "Thumb ARM emulation fatal error: " << endl |
147 | << opcode << "(" << Base::HEX8 << v1 << "," << v2 << "), " << msg << endl; |
148 | dump_regs(); |
149 | if(trapOnFatal) |
150 | throw runtime_error(statusMsg.str()); |
151 | return 0; |
152 | } |
153 | |
154 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
155 | void Thumbulator::dump_counters() |
156 | { |
157 | cout << endl << endl |
158 | << "instructions " << instructions << endl |
159 | << "fetches " << fetches << endl |
160 | << "reads " << reads << endl |
161 | << "writes " << writes << endl |
162 | << "memcycles " << (fetches+reads+writes) << endl; |
163 | } |
164 | |
165 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
166 | void Thumbulator::dump_regs() |
167 | { |
168 | for (int cnt = 1; cnt < 14; cnt++) |
169 | { |
170 | statusMsg << "R" << cnt << " = " << Base::HEX8 << reg_norm[cnt-1] << " " ; |
171 | if(cnt % 4 == 0) statusMsg << endl; |
172 | } |
173 | statusMsg << endl |
174 | << "SP = " << Base::HEX8 << reg_norm[13] << " " |
175 | << "LR = " << Base::HEX8 << reg_norm[14] << " " |
176 | << "PC = " << Base::HEX8 << reg_norm[15] << " " |
177 | << endl; |
178 | } |
179 | #endif |
180 | |
181 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
182 | uInt32 Thumbulator::fetch16(uInt32 addr) |
183 | { |
184 | #ifndef NO_THUMB_STATS |
185 | ++fetches; |
186 | #endif |
187 | |
188 | #ifndef UNSAFE_OPTIMIZATIONS |
189 | uInt32 data; |
190 | switch(addr & 0xF0000000) |
191 | { |
192 | case 0x00000000: //ROM |
193 | addr &= ROMADDMASK; |
194 | if(addr < 0x50) |
195 | fatalError("fetch16" , addr, "abort" ); |
196 | addr >>= 1; |
197 | data = CONV_RAMROM(rom[addr]); |
198 | DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); |
199 | return data; |
200 | |
201 | case 0x40000000: //RAM |
202 | addr &= RAMADDMASK; |
203 | addr >>= 1; |
204 | data=CONV_RAMROM(ram[addr]); |
205 | DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); |
206 | return data; |
207 | } |
208 | return fatalError("fetch16" , addr, "abort" ); |
209 | #else |
210 | addr &= ROMADDMASK; |
211 | addr >>= 1; |
212 | return CONV_RAMROM(rom[addr]); |
213 | #endif |
214 | } |
215 | |
216 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
217 | void Thumbulator::write16(uInt32 addr, uInt32 data) |
218 | { |
219 | #ifndef UNSAFE_OPTIMIZATIONS |
220 | if((addr > 0x40001fff) && (addr < 0x50000000)) |
221 | fatalError("write16" , addr, "abort - out of range" ); |
222 | |
223 | if (isProtected(addr)) fatalError("write16" , addr, "to driver area" ); |
224 | |
225 | if(addr & 1) |
226 | fatalError("write16" , addr, "abort - misaligned" ); |
227 | #endif |
228 | #ifndef NO_THUMB_STATS |
229 | ++writes; |
230 | #endif |
231 | |
232 | DO_DBUG(statusMsg << "write16(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl); |
233 | |
234 | switch(addr & 0xF0000000) |
235 | { |
236 | case 0x40000000: //RAM |
237 | addr &= RAMADDMASK; |
238 | addr >>= 1; |
239 | ram[addr] = CONV_DATA(data); |
240 | return; |
241 | |
242 | #ifndef UNSAFE_OPTIMIZATIONS |
243 | case 0xE0000000: //MAMCR |
244 | #else |
245 | default: |
246 | #endif |
247 | if(addr == 0xE01FC000) |
248 | { |
249 | DO_DBUG(statusMsg << "write16(" << Base::HEX8 << "MAMCR" << "," << Base::HEX8 << data << ") *" << endl); |
250 | mamcr = data; |
251 | return; |
252 | } |
253 | } |
254 | #ifndef UNSAFE_OPTIMIZATIONS |
255 | fatalError("write16" , addr, data, "abort" ); |
256 | #endif |
257 | } |
258 | |
259 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
260 | void Thumbulator::write32(uInt32 addr, uInt32 data) |
261 | { |
262 | #ifndef UNSAFE_OPTIMIZATIONS |
263 | if(addr & 3) |
264 | fatalError("write32" , addr, "abort - misaligned" ); |
265 | |
266 | if (isProtected(addr)) fatalError("write32" , addr, "to driver area" ); |
267 | #endif |
268 | DO_DBUG(statusMsg << "write32(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl); |
269 | |
270 | switch(addr & 0xF0000000) |
271 | { |
272 | #ifndef UNSAFE_OPTIMIZATIONS |
273 | case 0xF0000000: //halt |
274 | dump_counters(); |
275 | throw runtime_error("HALT" ); |
276 | #endif |
277 | |
278 | case 0xE0000000: //periph |
279 | switch(addr) |
280 | { |
281 | #ifndef UNSAFE_OPTIMIZATIONS |
282 | case 0xE0000000: |
283 | DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl); |
284 | break; |
285 | #endif |
286 | |
287 | case 0xE0008004: // T1TCR - Timer 1 Control Register |
288 | T1TCR = data; |
289 | break; |
290 | |
291 | case 0xE0008008: // T1TC - Timer 1 Counter |
292 | T1TC = data; |
293 | break; |
294 | |
295 | case 0xE000E010: |
296 | { |
297 | uInt32 old = systick_ctrl; |
298 | systick_ctrl = data & 0x00010007; |
299 | if(((old & 1) == 0) && (systick_ctrl & 1)) |
300 | { |
301 | // timer started, load count |
302 | systick_count = systick_reload; |
303 | } |
304 | break; |
305 | } |
306 | |
307 | case 0xE000E014: |
308 | systick_reload = data & 0x00FFFFFF; |
309 | break; |
310 | |
311 | case 0xE000E018: |
312 | systick_count = data & 0x00FFFFFF; |
313 | break; |
314 | |
315 | case 0xE000E01C: |
316 | systick_calibrate = data & 0x00FFFFFF; |
317 | break; |
318 | } |
319 | return; |
320 | |
321 | case 0xD0000000: //debug |
322 | #ifndef UNSAFE_OPTIMIZATIONS |
323 | switch(addr & 0xFF) |
324 | { |
325 | case 0x00: |
326 | statusMsg << "[" << Base::HEX8 << read_register(14) << "][" |
327 | << addr << "] " << data << endl; |
328 | return; |
329 | |
330 | case 0x10: |
331 | statusMsg << Base::HEX8 << data << endl; |
332 | return; |
333 | |
334 | case 0x20: |
335 | statusMsg << Base::HEX8 << data << endl; |
336 | return; |
337 | } |
338 | #endif |
339 | return; |
340 | |
341 | #ifndef UNSAFE_OPTIMIZATIONS |
342 | case 0x40000000: //RAM |
343 | #else |
344 | default: |
345 | #endif |
346 | write16(addr+0, (data >> 0) & 0xFFFF); |
347 | write16(addr+2, (data >> 16) & 0xFFFF); |
348 | return; |
349 | } |
350 | #ifndef UNSAFE_OPTIMIZATIONS |
351 | fatalError("write32" , addr, data, "abort" ); |
352 | #endif |
353 | } |
354 | |
355 | #ifndef UNSAFE_OPTIMIZATIONS |
356 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
357 | bool Thumbulator::isProtected(uInt32 addr) |
358 | { |
359 | if (addr < 0x40000000) return false; |
360 | addr -= 0x40000000; |
361 | |
362 | switch (configuration) { |
363 | case ConfigureFor::DPCplus: |
364 | return (addr < 0x0c00) && (addr > 0x0028); |
365 | |
366 | case ConfigureFor::CDF: |
367 | return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x06e0) && (addr < (0x0e60 + 284))); |
368 | |
369 | case ConfigureFor::CDF1: |
370 | return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x00a0) && (addr < (0x00a0 + 284))); |
371 | |
372 | case ConfigureFor::CDFJ: |
373 | return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x0098) && (addr < (0x0098 + 292))); |
374 | |
375 | case ConfigureFor::BUS: |
376 | return (addr < 0x06d8) && (addr > 0x0028); |
377 | } |
378 | |
379 | return false; |
380 | } |
381 | #endif |
382 | |
383 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
384 | uInt32 Thumbulator::read16(uInt32 addr) |
385 | { |
386 | uInt32 data; |
387 | #ifndef UNSAFE_OPTIMIZATIONS |
388 | if((addr > 0x40001fff) && (addr < 0x50000000)) |
389 | fatalError("read16" , addr, "abort - out of range" ); |
390 | else if((addr > 0x7fff) && (addr < 0x10000000)) |
391 | fatalError("read16" , addr, "abort - out of range" ); |
392 | if(addr & 1) |
393 | fatalError("read16" , addr, "abort - misaligned" ); |
394 | #endif |
395 | #ifndef NO_THUMB_STATS |
396 | ++reads; |
397 | #endif |
398 | |
399 | switch(addr & 0xF0000000) |
400 | { |
401 | case 0x00000000: //ROM |
402 | addr &= ROMADDMASK; |
403 | addr >>= 1; |
404 | data = CONV_RAMROM(rom[addr]); |
405 | DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); |
406 | return data; |
407 | |
408 | case 0x40000000: //RAM |
409 | addr &= RAMADDMASK; |
410 | addr >>= 1; |
411 | data = CONV_RAMROM(ram[addr]); |
412 | DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); |
413 | return data; |
414 | |
415 | #ifndef UNSAFE_OPTIMIZATIONS |
416 | case 0xE0000000: //MAMCR |
417 | if(addr == 0xE01FC000) |
418 | #else |
419 | default: |
420 | #endif |
421 | { |
422 | DO_DBUG(statusMsg << "read16(" << "MAMCR" << addr << ")=" << mamcr << " *" ); |
423 | return mamcr; |
424 | } |
425 | } |
426 | #ifndef UNSAFE_OPTIMIZATIONS |
427 | return fatalError("read16" , addr, "abort" ); |
428 | #endif |
429 | } |
430 | |
431 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
432 | uInt32 Thumbulator::read32(uInt32 addr) |
433 | { |
434 | #ifndef UNSAFE_OPTIMIZATIONS |
435 | if(addr & 3) |
436 | fatalError("read32" , addr, "abort - misaligned" ); |
437 | #endif |
438 | |
439 | uInt32 data; |
440 | switch(addr & 0xF0000000) |
441 | { |
442 | case 0x00000000: //ROM |
443 | case 0x40000000: //RAM |
444 | data = read16(addr+0); |
445 | data |= (uInt32(read16(addr+2))) << 16; |
446 | DO_DBUG(statusMsg << "read32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl); |
447 | return data; |
448 | |
449 | #ifndef UNSAFE_OPTIMIZATIONS |
450 | case 0xE0000000: |
451 | #else |
452 | default: |
453 | #endif |
454 | { |
455 | switch(addr) |
456 | { |
457 | case 0xE0008004: // T1TCR - Timer 1 Control Register |
458 | data = T1TCR; |
459 | return data; |
460 | |
461 | case 0xE0008008: // T1TC - Timer 1 Counter |
462 | data = T1TC; |
463 | return data; |
464 | |
465 | case 0xE000E010: |
466 | data = systick_ctrl; |
467 | systick_ctrl &= (~0x00010000); |
468 | return data; |
469 | |
470 | case 0xE000E014: |
471 | data = systick_reload; |
472 | return data; |
473 | |
474 | case 0xE000E018: |
475 | data = systick_count; |
476 | return data; |
477 | |
478 | #ifndef UNSAFE_OPTIMIZATIONS |
479 | case 0xE000E01C: |
480 | #else |
481 | default: |
482 | #endif |
483 | data = systick_calibrate; |
484 | return data; |
485 | } |
486 | } |
487 | } |
488 | #ifndef UNSAFE_OPTIMIZATIONS |
489 | return fatalError("read32" , addr, "abort" ); |
490 | #endif |
491 | } |
492 | |
493 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
494 | uInt32 Thumbulator::read_register(uInt32 reg) |
495 | { |
496 | reg &= 0xF; |
497 | |
498 | uInt32 data = reg_norm[reg]; |
499 | DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl); |
500 | #ifndef UNSAFE_OPTIMIZATIONS |
501 | if(reg == 15) |
502 | { |
503 | if(data & 1) |
504 | { |
505 | DO_DBUG(statusMsg << "pc has lsbit set 0x" << Base::HEX8 << data << endl); |
506 | } |
507 | data &= ~1; |
508 | } |
509 | #endif |
510 | return data; |
511 | } |
512 | |
513 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
514 | void Thumbulator::write_register(uInt32 reg, uInt32 data) |
515 | { |
516 | reg &= 0xF; |
517 | |
518 | DO_DBUG(statusMsg << "write_register(" << dec << reg << "," << Base::HEX8 << data << ")" << endl); |
519 | //#ifndef UNSAFE_OPTIMIZATIONS // this fails when combined with read_register UNSAFE_OPTIMIZATIONS |
520 | if(reg == 15) data &= ~1; |
521 | //#endif |
522 | reg_norm[reg] = data; |
523 | } |
524 | |
525 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
526 | void Thumbulator::do_zflag(uInt32 x) |
527 | { |
528 | if(x == 0) cpsr |= CPSR_Z; else cpsr &= ~CPSR_Z; |
529 | } |
530 | |
531 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
532 | void Thumbulator::do_nflag(uInt32 x) |
533 | { |
534 | if(x & 0x80000000) cpsr |= CPSR_N; else cpsr &= ~CPSR_N; |
535 | } |
536 | |
537 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
538 | void Thumbulator::do_cflag(uInt32 a, uInt32 b, uInt32 c) |
539 | { |
540 | uInt32 rc; |
541 | |
542 | rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in |
543 | rc = (rc >> 31) + (a >> 31) + (b >> 31); //carry out |
544 | if(rc & 2) |
545 | cpsr |= CPSR_C; |
546 | else |
547 | cpsr &= ~CPSR_C; |
548 | } |
549 | |
550 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
551 | void Thumbulator::do_vflag(uInt32 a, uInt32 b, uInt32 c) |
552 | { |
553 | uInt32 rc, rd; |
554 | |
555 | rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in |
556 | rc >>= 31; //carry in in lsbit |
557 | rd = (rc & 1) + ((a >> 31) & 1) + ((b >> 31) & 1); //carry out |
558 | rd >>= 1; //carry out in lsbit |
559 | rc = (rc^rd) & 1; //if carry in != carry out then signed overflow |
560 | if(rc) |
561 | cpsr |= CPSR_V; |
562 | else |
563 | cpsr &= ~CPSR_V; |
564 | } |
565 | |
566 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
567 | void Thumbulator::do_cflag_bit(uInt32 x) |
568 | { |
569 | if(x) cpsr |= CPSR_C; else cpsr &= ~CPSR_C; |
570 | } |
571 | |
572 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
573 | void Thumbulator::do_vflag_bit(uInt32 x) |
574 | { |
575 | if(x) cpsr |= CPSR_V; else cpsr &= ~CPSR_V; |
576 | } |
577 | |
578 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
579 | Thumbulator::Op Thumbulator::decodeInstructionWord(uint16_t inst) { |
580 | //ADC |
581 | if((inst & 0xFFC0) == 0x4140) return Op::adc; |
582 | |
583 | //ADD(1) small immediate two registers |
584 | if((inst & 0xFE00) == 0x1C00 && (inst >> 6) & 0x7) return Op::add1; |
585 | |
586 | //ADD(2) big immediate one register |
587 | if((inst & 0xF800) == 0x3000) return Op::add2; |
588 | |
589 | //ADD(3) three registers |
590 | if((inst & 0xFE00) == 0x1800) return Op::add3; |
591 | |
592 | //ADD(4) two registers one or both high no flags |
593 | if((inst & 0xFF00) == 0x4400) return Op::add4; |
594 | |
595 | //ADD(5) rd = pc plus immediate |
596 | if((inst & 0xF800) == 0xA000) return Op::add5; |
597 | |
598 | //ADD(6) rd = sp plus immediate |
599 | if((inst & 0xF800) == 0xA800) return Op::add6; |
600 | |
601 | //ADD(7) sp plus immediate |
602 | if((inst & 0xFF80) == 0xB000) return Op::add7; |
603 | |
604 | //AND |
605 | if((inst & 0xFFC0) == 0x4000) return Op::and_; |
606 | |
607 | //ASR(1) two register immediate |
608 | if((inst & 0xF800) == 0x1000) return Op::asr1; |
609 | |
610 | //ASR(2) two register |
611 | if((inst & 0xFFC0) == 0x4100) return Op::asr2; |
612 | |
613 | //B(1) conditional branch |
614 | if((inst & 0xF000) == 0xD000) return Op::b1; |
615 | |
616 | //B(2) unconditional branch |
617 | if((inst & 0xF800) == 0xE000) return Op::b2; |
618 | |
619 | //BIC |
620 | if((inst & 0xFFC0) == 0x4380) return Op::bic; |
621 | |
622 | //BKPT |
623 | if((inst & 0xFF00) == 0xBE00) return Op::bkpt; |
624 | |
625 | //BL/BLX(1) |
626 | if((inst & 0xE000) == 0xE000) return Op::blx1; |
627 | |
628 | //BLX(2) |
629 | if((inst & 0xFF87) == 0x4780) return Op::blx2; |
630 | |
631 | //BX |
632 | if((inst & 0xFF87) == 0x4700) return Op::bx; |
633 | |
634 | //CMN |
635 | if((inst & 0xFFC0) == 0x42C0) return Op::cmn; |
636 | |
637 | //CMP(1) compare immediate |
638 | if((inst & 0xF800) == 0x2800) return Op::cmp1; |
639 | |
640 | //CMP(2) compare register |
641 | if((inst & 0xFFC0) == 0x4280) return Op::cmp2; |
642 | |
643 | //CMP(3) compare high register |
644 | if((inst & 0xFF00) == 0x4500) return Op::cmp3; |
645 | |
646 | //CPS |
647 | if((inst & 0xFFE8) == 0xB660) return Op::cps; |
648 | |
649 | //CPY copy high register |
650 | if((inst & 0xFFC0) == 0x4600) return Op::cpy; |
651 | |
652 | //EOR |
653 | if((inst & 0xFFC0) == 0x4040) return Op::eor; |
654 | |
655 | //LDMIA |
656 | if((inst & 0xF800) == 0xC800) return Op::ldmia; |
657 | |
658 | //LDR(1) two register immediate |
659 | if((inst & 0xF800) == 0x6800) return Op::ldr1; |
660 | |
661 | //LDR(2) three register |
662 | if((inst & 0xFE00) == 0x5800) return Op::ldr2; |
663 | |
664 | //LDR(3) |
665 | if((inst & 0xF800) == 0x4800) return Op::ldr3; |
666 | |
667 | //LDR(4) |
668 | if((inst & 0xF800) == 0x9800) return Op::ldr4; |
669 | |
670 | //LDRB(1) |
671 | if((inst & 0xF800) == 0x7800) return Op::ldrb1; |
672 | |
673 | //LDRB(2) |
674 | if((inst & 0xFE00) == 0x5C00) return Op::ldrb2; |
675 | |
676 | //LDRH(1) |
677 | if((inst & 0xF800) == 0x8800) return Op::ldrh1; |
678 | |
679 | //LDRH(2) |
680 | if((inst & 0xFE00) == 0x5A00) return Op::ldrh2; |
681 | |
682 | //LDRSB |
683 | if((inst & 0xFE00) == 0x5600) return Op::ldrsb; |
684 | |
685 | //LDRSH |
686 | if((inst & 0xFE00) == 0x5E00) return Op::ldrsh; |
687 | |
688 | //LSL(1) |
689 | if((inst & 0xF800) == 0x0000) return Op::lsl1; |
690 | |
691 | //LSL(2) two register |
692 | if((inst & 0xFFC0) == 0x4080) return Op::lsl2; |
693 | |
694 | //LSR(1) two register immediate |
695 | if((inst & 0xF800) == 0x0800) return Op::lsr1; |
696 | |
697 | //LSR(2) two register |
698 | if((inst & 0xFFC0) == 0x40C0) return Op::lsr2; |
699 | |
700 | //MOV(1) immediate |
701 | if((inst & 0xF800) == 0x2000) return Op::mov1; |
702 | |
703 | //MOV(2) two low registers |
704 | if((inst & 0xFFC0) == 0x1C00) return Op::mov2; |
705 | |
706 | //MOV(3) |
707 | if((inst & 0xFF00) == 0x4600) return Op::mov3; |
708 | |
709 | //MUL |
710 | if((inst & 0xFFC0) == 0x4340) return Op::mul; |
711 | |
712 | //MVN |
713 | if((inst & 0xFFC0) == 0x43C0) return Op::mvn; |
714 | |
715 | //NEG |
716 | if((inst & 0xFFC0) == 0x4240) return Op::neg; |
717 | |
718 | //ORR |
719 | if((inst & 0xFFC0) == 0x4300) return Op::orr; |
720 | |
721 | //POP |
722 | if((inst & 0xFE00) == 0xBC00) return Op::pop; |
723 | |
724 | //PUSH |
725 | if((inst & 0xFE00) == 0xB400) return Op::push; |
726 | |
727 | //REV |
728 | if((inst & 0xFFC0) == 0xBA00) return Op::rev; |
729 | |
730 | //REV16 |
731 | if((inst & 0xFFC0) == 0xBA40) return Op::rev16; |
732 | |
733 | //REVSH |
734 | if((inst & 0xFFC0) == 0xBAC0) return Op::revsh; |
735 | |
736 | //ROR |
737 | if((inst & 0xFFC0) == 0x41C0) return Op::ror; |
738 | |
739 | //SBC |
740 | if((inst & 0xFFC0) == 0x4180) return Op::sbc; |
741 | |
742 | //SETEND |
743 | if((inst & 0xFFF7) == 0xB650) return Op::setend; |
744 | |
745 | //STMIA |
746 | if((inst & 0xF800) == 0xC000) return Op::stmia; |
747 | |
748 | //STR(1) |
749 | if((inst & 0xF800) == 0x6000) return Op::str1; |
750 | |
751 | //STR(2) |
752 | if((inst & 0xFE00) == 0x5000) return Op::str2; |
753 | |
754 | //STR(3) |
755 | if((inst & 0xF800) == 0x9000) return Op::str3; |
756 | |
757 | //STRB(1) |
758 | if((inst & 0xF800) == 0x7000) return Op::strb1; |
759 | |
760 | //STRB(2) |
761 | if((inst & 0xFE00) == 0x5400) return Op::strb2; |
762 | |
763 | //STRH(1) |
764 | if((inst & 0xF800) == 0x8000) return Op::strh1; |
765 | |
766 | //STRH(2) |
767 | if((inst & 0xFE00) == 0x5200) return Op::strh2; |
768 | |
769 | //SUB(1) |
770 | if((inst & 0xFE00) == 0x1E00) return Op::sub1; |
771 | |
772 | //SUB(2) |
773 | if((inst & 0xF800) == 0x3800) return Op::sub2; |
774 | |
775 | //SUB(3) |
776 | if((inst & 0xFE00) == 0x1A00) return Op::sub3; |
777 | |
778 | //SUB(4) |
779 | if((inst & 0xFF80) == 0xB080) return Op::sub4; |
780 | |
781 | //SWI |
782 | if((inst & 0xFF00) == 0xDF00) return Op::swi; |
783 | |
784 | //SXTB |
785 | if((inst & 0xFFC0) == 0xB240) return Op::sxtb; |
786 | |
787 | //SXTH |
788 | if((inst & 0xFFC0) == 0xB200) return Op::sxth; |
789 | |
790 | //TST |
791 | if((inst & 0xFFC0) == 0x4200) return Op::tst; |
792 | |
793 | //UXTB |
794 | if((inst & 0xFFC0) == 0xB2C0) return Op::uxtb; |
795 | |
796 | //UXTH |
797 | if((inst & 0xFFC0) == 0xB280) return Op::uxth; |
798 | |
799 | return Op::invalid; |
800 | } |
801 | |
802 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
803 | int Thumbulator::execute() |
804 | { |
805 | uInt32 pc, sp, inst, ra, rb, rc, rm, rd, rn, rs, op; |
806 | |
807 | pc = read_register(15); |
808 | |
809 | uInt32 instructionPtr = pc - 2; |
810 | inst = fetch16(instructionPtr); |
811 | |
812 | pc += 2; |
813 | write_register(15, pc); |
814 | DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " " ); |
815 | |
816 | #ifndef UNSAFE_OPTIMIZATIONS |
817 | ++instructions; |
818 | #endif |
819 | |
820 | Op decodedOp; |
821 | #ifndef UNSAFE_OPTIMIZATIONS |
822 | if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize) |
823 | decodedOp = decodedRom[instructionPtr >> 1]; |
824 | else |
825 | decodedOp = decodeInstructionWord(inst); |
826 | #else |
827 | decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1]; |
828 | #endif |
829 | |
830 | switch (decodedOp) { |
831 | //ADC |
832 | case Op::adc: { |
833 | rd = (inst >> 0) & 0x07; |
834 | rm = (inst >> 3) & 0x07; |
835 | DO_DISS(statusMsg << "adc r" << dec << rd << ",r" << dec << rm << endl); |
836 | ra = read_register(rd); |
837 | rb = read_register(rm); |
838 | rc = ra + rb; |
839 | if(cpsr & CPSR_C) |
840 | ++rc; |
841 | write_register(rd, rc); |
842 | do_nflag(rc); |
843 | do_zflag(rc); |
844 | if(cpsr & CPSR_C) { do_cflag(ra, rb, 1); do_vflag(ra, rb, 1); } |
845 | else { do_cflag(ra, rb, 0); do_vflag(ra, rb, 0); } |
846 | return 0; |
847 | } |
848 | |
849 | //ADD(1) small immediate two registers |
850 | case Op::add1: { |
851 | rd = (inst >> 0) & 0x7; |
852 | rn = (inst >> 3) & 0x7; |
853 | rb = (inst >> 6) & 0x7; |
854 | if(rb) |
855 | { |
856 | DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << "," |
857 | << "#0x" << Base::HEX2 << rb << endl); |
858 | ra = read_register(rn); |
859 | rc = ra + rb; |
860 | //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); |
861 | write_register(rd, rc); |
862 | do_nflag(rc); |
863 | do_zflag(rc); |
864 | do_cflag(ra, rb, 0); |
865 | do_vflag(ra, rb, 0); |
866 | return 0; |
867 | } |
868 | else |
869 | { |
870 | //this is a mov |
871 | } |
872 | |
873 | break; |
874 | } |
875 | |
876 | //ADD(2) big immediate one register |
877 | case Op::add2: { |
878 | rb = (inst >> 0) & 0xFF; |
879 | rd = (inst >> 8) & 0x7; |
880 | DO_DISS(statusMsg << "adds r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); |
881 | ra = read_register(rd); |
882 | rc = ra + rb; |
883 | write_register(rd, rc); |
884 | do_nflag(rc); |
885 | do_zflag(rc); |
886 | do_cflag(ra, rb, 0); |
887 | do_vflag(ra, rb, 0); |
888 | return 0; |
889 | } |
890 | |
891 | //ADD(3) three registers |
892 | case Op::add3: { |
893 | rd = (inst >> 0) & 0x7; |
894 | rn = (inst >> 3) & 0x7; |
895 | rm = (inst >> 6) & 0x7; |
896 | DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << ",r" << rm << endl); |
897 | ra = read_register(rn); |
898 | rb = read_register(rm); |
899 | rc = ra + rb; |
900 | write_register(rd, rc); |
901 | do_nflag(rc); |
902 | do_zflag(rc); |
903 | do_cflag(ra, rb, 0); |
904 | do_vflag(ra, rb, 0); |
905 | return 0; |
906 | } |
907 | |
908 | //ADD(4) two registers one or both high no flags |
909 | case Op::add4: { |
910 | if((inst >> 6) & 3) |
911 | { |
912 | //UNPREDICTABLE |
913 | } |
914 | rd = (inst >> 0) & 0x7; |
915 | rd |= (inst >> 4) & 0x8; |
916 | rm = (inst >> 3) & 0xF; |
917 | DO_DISS(statusMsg << "add r" << dec << rd << ",r" << dec << rm << endl); |
918 | ra = read_register(rd); |
919 | rb = read_register(rm); |
920 | rc = ra + rb; |
921 | if(rd == 15) |
922 | { |
923 | #ifndef UNSAFE_OPTIMIZATIONS |
924 | if((rc & 1) == 0) |
925 | fatalError("add pc" , pc, rc, " produced an arm address" ); |
926 | #endif |
927 | //rc &= ~1; //write_register may do this as well |
928 | rc += 2; //The program counter is special |
929 | } |
930 | //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); |
931 | write_register(rd, rc); |
932 | return 0; |
933 | } |
934 | |
935 | //ADD(5) rd = pc plus immediate |
936 | case Op::add5: { |
937 | rb = (inst >> 0) & 0xFF; |
938 | rd = (inst >> 8) & 0x7; |
939 | rb <<= 2; |
940 | DO_DISS(statusMsg << "add r" << dec << rd << ",PC,#0x" << Base::HEX2 << rb << endl); |
941 | ra = read_register(15); |
942 | rc = (ra & (~3u)) + rb; |
943 | write_register(rd, rc); |
944 | return 0; |
945 | } |
946 | |
947 | //ADD(6) rd = sp plus immediate |
948 | case Op::add6: { |
949 | rb = (inst >> 0) & 0xFF; |
950 | rd = (inst >> 8) & 0x7; |
951 | rb <<= 2; |
952 | DO_DISS(statusMsg << "add r" << dec << rd << ",SP,#0x" << Base::HEX2 << rb << endl); |
953 | ra = read_register(13); |
954 | rc = ra + rb; |
955 | write_register(rd, rc); |
956 | return 0; |
957 | } |
958 | |
959 | //ADD(7) sp plus immediate |
960 | case Op::add7: { |
961 | rb = (inst >> 0) & 0x7F; |
962 | rb <<= 2; |
963 | DO_DISS(statusMsg << "add SP,#0x" << Base::HEX2 << rb << endl); |
964 | ra = read_register(13); |
965 | rc = ra + rb; |
966 | write_register(13, rc); |
967 | return 0; |
968 | } |
969 | |
970 | //AND |
971 | case Op::and_: { |
972 | rd = (inst >> 0) & 0x7; |
973 | rm = (inst >> 3) & 0x7; |
974 | DO_DISS(statusMsg << "ands r" << dec << rd << ",r" << dec << rm << endl); |
975 | ra = read_register(rd); |
976 | rb = read_register(rm); |
977 | rc = ra & rb; |
978 | write_register(rd, rc); |
979 | do_nflag(rc); |
980 | do_zflag(rc); |
981 | return 0; |
982 | } |
983 | |
984 | //ASR(1) two register immediate |
985 | case Op::asr1: { |
986 | rd = (inst >> 0) & 0x07; |
987 | rm = (inst >> 3) & 0x07; |
988 | rb = (inst >> 6) & 0x1F; |
989 | DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); |
990 | rc = read_register(rm); |
991 | if(rb == 0) |
992 | { |
993 | if(rc & 0x80000000) |
994 | { |
995 | do_cflag_bit(1); |
996 | rc = ~0u; |
997 | } |
998 | else |
999 | { |
1000 | do_cflag_bit(0); |
1001 | rc = 0; |
1002 | } |
1003 | } |
1004 | else |
1005 | { |
1006 | do_cflag_bit(rc & (1 << (rb-1))); |
1007 | ra = rc & 0x80000000; |
1008 | rc >>= rb; |
1009 | if(ra) //asr, sign is shifted in |
1010 | rc |= (~0u) << (32-rb); |
1011 | } |
1012 | write_register(rd, rc); |
1013 | do_nflag(rc); |
1014 | do_zflag(rc); |
1015 | return 0; |
1016 | } |
1017 | |
1018 | //ASR(2) two register |
1019 | case Op::asr2: { |
1020 | rd = (inst >> 0) & 0x07; |
1021 | rs = (inst >> 3) & 0x07; |
1022 | DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rs << endl); |
1023 | rc = read_register(rd); |
1024 | rb = read_register(rs); |
1025 | rb &= 0xFF; |
1026 | if(rb == 0) |
1027 | { |
1028 | } |
1029 | else if(rb < 32) |
1030 | { |
1031 | do_cflag_bit(rc & (1 << (rb-1))); |
1032 | ra = rc & 0x80000000; |
1033 | rc >>= rb; |
1034 | if(ra) //asr, sign is shifted in |
1035 | { |
1036 | rc |= (~0u) << (32-rb); |
1037 | } |
1038 | } |
1039 | else |
1040 | { |
1041 | if(rc & 0x80000000) |
1042 | { |
1043 | do_cflag_bit(1); |
1044 | rc = (~0u); |
1045 | } |
1046 | else |
1047 | { |
1048 | do_cflag_bit(0); |
1049 | rc = 0; |
1050 | } |
1051 | } |
1052 | write_register(rd, rc); |
1053 | do_nflag(rc); |
1054 | do_zflag(rc); |
1055 | return 0; |
1056 | } |
1057 | |
1058 | //B(1) conditional branch |
1059 | case Op::b1: { |
1060 | rb = (inst >> 0) & 0xFF; |
1061 | if(rb & 0x80) |
1062 | rb |= (~0u) << 8; |
1063 | op=(inst >> 8) & 0xF; |
1064 | rb <<= 1; |
1065 | rb += pc; |
1066 | rb += 2; |
1067 | switch(op) |
1068 | { |
1069 | case 0x0: //b eq z set |
1070 | DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl); |
1071 | if(cpsr & CPSR_Z) |
1072 | write_register(15, rb); |
1073 | return 0; |
1074 | |
1075 | case 0x1: //b ne z clear |
1076 | DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl); |
1077 | if(!(cpsr & CPSR_Z)) |
1078 | write_register(15, rb); |
1079 | return 0; |
1080 | |
1081 | case 0x2: //b cs c set |
1082 | DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl); |
1083 | if(cpsr & CPSR_C) |
1084 | write_register(15, rb); |
1085 | return 0; |
1086 | |
1087 | case 0x3: //b cc c clear |
1088 | DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl); |
1089 | if(!(cpsr & CPSR_C)) |
1090 | write_register(15, rb); |
1091 | return 0; |
1092 | |
1093 | case 0x4: //b mi n set |
1094 | DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl); |
1095 | if(cpsr & CPSR_N) |
1096 | write_register(15, rb); |
1097 | return 0; |
1098 | |
1099 | case 0x5: //b pl n clear |
1100 | DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl); |
1101 | if(!(cpsr & CPSR_N)) |
1102 | write_register(15, rb); |
1103 | return 0; |
1104 | |
1105 | case 0x6: //b vs v set |
1106 | DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl); |
1107 | if(cpsr & CPSR_V) |
1108 | write_register(15,rb); |
1109 | return 0; |
1110 | |
1111 | case 0x7: //b vc v clear |
1112 | DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl); |
1113 | if(!(cpsr & CPSR_V)) |
1114 | write_register(15, rb); |
1115 | return 0; |
1116 | |
1117 | case 0x8: //b hi c set z clear |
1118 | DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl); |
1119 | if((cpsr & CPSR_C) && (!(cpsr & CPSR_Z))) |
1120 | write_register(15, rb); |
1121 | return 0; |
1122 | |
1123 | case 0x9: //b ls c clear or z set |
1124 | DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl); |
1125 | if((cpsr & CPSR_Z) || (!(cpsr & CPSR_C))) |
1126 | write_register(15, rb); |
1127 | return 0; |
1128 | |
1129 | case 0xA: //b ge N == V |
1130 | DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl); |
1131 | if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || |
1132 | ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) |
1133 | write_register(15, rb); |
1134 | return 0; |
1135 | |
1136 | case 0xB: //b lt N != V |
1137 | DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl); |
1138 | if((!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || |
1139 | (((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) |
1140 | write_register(15, rb); |
1141 | return 0; |
1142 | |
1143 | case 0xC: //b gt Z==0 and N == V |
1144 | DO_DISS(statusMsg << "bgt 0x" << Base::HEX8 << (rb-3) << endl); |
1145 | if(!(cpsr & CPSR_Z)) |
1146 | { |
1147 | if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || |
1148 | ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) |
1149 | write_register(15, rb); |
1150 | } |
1151 | return 0; |
1152 | |
1153 | case 0xD: //b le Z==1 or N != V |
1154 | DO_DISS(statusMsg << "ble 0x" << Base::HEX8 << (rb-3) << endl); |
1155 | if((cpsr & CPSR_Z) || |
1156 | (!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || |
1157 | (((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) |
1158 | write_register(15, rb); |
1159 | return 0; |
1160 | |
1161 | case 0xE: |
1162 | //undefined instruction |
1163 | break; |
1164 | |
1165 | case 0xF: |
1166 | //swi |
1167 | break; |
1168 | } |
1169 | |
1170 | break; |
1171 | } |
1172 | |
1173 | //B(2) unconditional branch |
1174 | case Op::b2: { |
1175 | rb = (inst >> 0) & 0x7FF; |
1176 | if(rb & (1 << 10)) |
1177 | rb |= (~0u) << 11; |
1178 | rb <<= 1; |
1179 | rb += pc; |
1180 | rb += 2; |
1181 | DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl); |
1182 | write_register(15, rb); |
1183 | return 0; |
1184 | } |
1185 | |
1186 | //BIC |
1187 | case Op::bic: { |
1188 | rd = (inst >> 0) & 0x7; |
1189 | rm = (inst >> 3) & 0x7; |
1190 | DO_DISS(statusMsg << "bics r" << dec << rd << ",r" << dec << rm << endl); |
1191 | ra = read_register(rd); |
1192 | rb = read_register(rm); |
1193 | rc = ra & (~rb); |
1194 | write_register(rd, rc); |
1195 | do_nflag(rc); |
1196 | do_zflag(rc); |
1197 | return 0; |
1198 | } |
1199 | |
1200 | #ifndef UNSAFE_OPTIMIZATIONS |
1201 | //BKPT |
1202 | case Op::bkpt: { |
1203 | rb = (inst >> 0) & 0xFF; |
1204 | statusMsg << "bkpt 0x" << Base::HEX2 << rb << endl; |
1205 | return 1; |
1206 | } |
1207 | #endif |
1208 | |
1209 | //BL/BLX(1) |
1210 | case Op::blx1: { |
1211 | if((inst & 0x1800) == 0x1000) //H=b10 |
1212 | { |
1213 | DO_DISS(statusMsg << endl); |
1214 | rb = inst & ((1 << 11) - 1); |
1215 | if(rb & 1<<10) rb |= (~((1 << 11) - 1)); //sign extend |
1216 | rb <<= 12; |
1217 | rb += pc; |
1218 | write_register(14, rb); |
1219 | return 0; |
1220 | } |
1221 | else if((inst & 0x1800) == 0x1800) //H=b11 |
1222 | { |
1223 | //branch to thumb |
1224 | rb = read_register(14); |
1225 | rb += (inst & ((1 << 11) - 1)) << 1; |
1226 | rb += 2; |
1227 | DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); |
1228 | write_register(14, (pc-2) | 1); |
1229 | write_register(15, rb); |
1230 | return 0; |
1231 | } |
1232 | else if((inst & 0x1800) == 0x0800) //H=b01 |
1233 | { |
1234 | //fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst); |
1235 | // fxq: this should exit the code without having to detect it |
1236 | rb = read_register(14); |
1237 | rb += (inst & ((1 << 11) - 1)) << 1; |
1238 | rb &= 0xFFFFFFFC; |
1239 | rb += 2; |
1240 | DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); |
1241 | write_register(14, (pc-2) | 1); |
1242 | write_register(15, rb); |
1243 | return 0; |
1244 | } |
1245 | |
1246 | break; |
1247 | } |
1248 | |
1249 | //BLX(2) |
1250 | case Op::blx2: { |
1251 | rm = (inst >> 3) & 0xF; |
1252 | DO_DISS(statusMsg << "blx r" << dec << rm << endl); |
1253 | rc = read_register(rm); |
1254 | //fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc); |
1255 | rc += 2; |
1256 | if(rc & 1) |
1257 | { |
1258 | write_register(14, (pc-2) | 1); |
1259 | //rc &= ~1; |
1260 | write_register(15, rc); |
1261 | return 0; |
1262 | } |
1263 | else |
1264 | { |
1265 | //fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst); |
1266 | // fxq: this could serve as exit code |
1267 | return 1; |
1268 | } |
1269 | } |
1270 | |
1271 | //BX |
1272 | case Op::bx: { |
1273 | rm = (inst >> 3) & 0xF; |
1274 | DO_DISS(statusMsg << "bx r" << dec << rm << endl); |
1275 | rc = read_register(rm); |
1276 | rc += 2; |
1277 | //fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc); |
1278 | if(rc & 1) |
1279 | { |
1280 | // branch to odd address denotes 16 bit ARM code |
1281 | //rc &= ~1; |
1282 | write_register(15, rc); |
1283 | return 0; |
1284 | } |
1285 | else |
1286 | { |
1287 | // branch to even address denotes 32 bit ARM code, which the Thumbulator |
1288 | // class does not support. So capture relavent information and hand it |
1289 | // off to the Cartridge class for it to handle. |
1290 | |
1291 | bool handled = false; |
1292 | |
1293 | switch(configuration) |
1294 | { |
1295 | case ConfigureFor::BUS: |
1296 | // this subroutine interface is used in the BUS driver, |
1297 | // it starts at address 0x000006d8 |
1298 | // _SetNote: |
1299 | // ldr r4, =NoteStore |
1300 | // bx r4 // bx instruction at 0x000006da |
1301 | // _ResetWave: |
1302 | // ldr r4, =ResetWaveStore |
1303 | // bx r4 // bx instruction at 0x000006de |
1304 | // _GetWavePtr: |
1305 | // ldr r4, =WavePtrFetch |
1306 | // bx r4 // bx instruction at 0x000006e2 |
1307 | // _SetWaveSize: |
1308 | // ldr r4, =WaveSizeStore |
1309 | // bx r4 // bx instruction at 0x000006e6 |
1310 | |
1311 | // address to test for is + 4 due to pipelining |
1312 | |
1313 | #define BUS_SetNote (0x000006da + 4) |
1314 | #define BUS_ResetWave (0x000006de + 4) |
1315 | #define BUS_GetWavePtr (0x000006e2 + 4) |
1316 | #define BUS_SetWaveSize (0x000006e6 + 4) |
1317 | |
1318 | if (pc == BUS_SetNote) |
1319 | { |
1320 | myCartridge->thumbCallback(0, read_register(2), read_register(3)); |
1321 | handled = true; |
1322 | } |
1323 | else if (pc == BUS_ResetWave) |
1324 | { |
1325 | myCartridge->thumbCallback(1, read_register(2), 0); |
1326 | handled = true; |
1327 | } |
1328 | else if (pc == BUS_GetWavePtr) |
1329 | { |
1330 | write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); |
1331 | handled = true; |
1332 | } |
1333 | else if (pc == BUS_SetWaveSize) |
1334 | { |
1335 | myCartridge->thumbCallback(3, read_register(2), read_register(3)); |
1336 | handled = true; |
1337 | } |
1338 | else if (pc == 0x0000083a) |
1339 | { |
1340 | // exiting Custom ARM code, returning to BUS Driver control |
1341 | } |
1342 | else |
1343 | { |
1344 | #if 0 // uncomment this for testing |
1345 | uInt32 r0 = read_register(0); |
1346 | uInt32 r1 = read_register(1); |
1347 | uInt32 r2 = read_register(2); |
1348 | uInt32 r3 = read_register(3); |
1349 | uInt32 r4 = read_register(4); |
1350 | #endif |
1351 | myCartridge->thumbCallback(255, 0, 0); |
1352 | } |
1353 | |
1354 | break; |
1355 | |
1356 | case ConfigureFor::CDF: |
1357 | // this subroutine interface is used in the CDF driver, |
1358 | // it starts at address 0x000006e0 |
1359 | // _SetNote: |
1360 | // ldr r4, =NoteStore |
1361 | // bx r4 // bx instruction at 0x000006e2 |
1362 | // _ResetWave: |
1363 | // ldr r4, =ResetWaveStore |
1364 | // bx r4 // bx instruction at 0x000006e6 |
1365 | // _GetWavePtr: |
1366 | // ldr r4, =WavePtrFetch |
1367 | // bx r4 // bx instruction at 0x000006ea |
1368 | // _SetWaveSize: |
1369 | // ldr r4, =WaveSizeStore |
1370 | // bx r4 // bx instruction at 0x000006ee |
1371 | |
1372 | // address to test for is + 4 due to pipelining |
1373 | |
1374 | #define CDF_SetNote (0x000006e2 + 4) |
1375 | #define CDF_ResetWave (0x000006e6 + 4) |
1376 | #define CDF_GetWavePtr (0x000006ea + 4) |
1377 | #define CDF_SetWaveSize (0x000006ee + 4) |
1378 | |
1379 | if (pc == CDF_SetNote) |
1380 | { |
1381 | myCartridge->thumbCallback(0, read_register(2), read_register(3)); |
1382 | handled = true; |
1383 | } |
1384 | else if (pc == CDF_ResetWave) |
1385 | { |
1386 | myCartridge->thumbCallback(1, read_register(2), 0); |
1387 | handled = true; |
1388 | } |
1389 | else if (pc == CDF_GetWavePtr) |
1390 | { |
1391 | write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); |
1392 | handled = true; |
1393 | } |
1394 | else if (pc == CDF_SetWaveSize) |
1395 | { |
1396 | myCartridge->thumbCallback(3, read_register(2), read_register(3)); |
1397 | handled = true; |
1398 | } |
1399 | else if (pc == 0x0000083a) |
1400 | { |
1401 | // exiting Custom ARM code, returning to BUS Driver control |
1402 | } |
1403 | else |
1404 | { |
1405 | #if 0 // uncomment this for testing |
1406 | uInt32 r0 = read_register(0); |
1407 | uInt32 r1 = read_register(1); |
1408 | uInt32 r2 = read_register(2); |
1409 | uInt32 r3 = read_register(3); |
1410 | uInt32 r4 = read_register(4); |
1411 | #endif |
1412 | myCartridge->thumbCallback(255, 0, 0); |
1413 | } |
1414 | |
1415 | break; |
1416 | |
1417 | case ConfigureFor::CDF1: |
1418 | case ConfigureFor::CDFJ: |
1419 | // this subroutine interface is used in the CDF driver, |
1420 | // it starts at address 0x00000750 |
1421 | // _SetNote: |
1422 | // ldr r4, =NoteStore |
1423 | // bx r4 // bx instruction at 0x000006e2 |
1424 | // _ResetWave: |
1425 | // ldr r4, =ResetWaveStore |
1426 | // bx r4 // bx instruction at 0x000006e6 |
1427 | // _GetWavePtr: |
1428 | // ldr r4, =WavePtrFetch |
1429 | // bx r4 // bx instruction at 0x000006ea |
1430 | // _SetWaveSize: |
1431 | // ldr r4, =WaveSizeStore |
1432 | // bx r4 // bx instruction at 0x000006ee |
1433 | |
1434 | // address to test for is + 4 due to pipelining |
1435 | |
1436 | #define CDF1_SetNote (0x00000752 + 4) |
1437 | #define CDF1_ResetWave (0x00000756 + 4) |
1438 | #define CDF1_GetWavePtr (0x0000075a + 4) |
1439 | #define CDF1_SetWaveSize (0x0000075e + 4) |
1440 | |
1441 | if (pc == CDF1_SetNote) |
1442 | { |
1443 | myCartridge->thumbCallback(0, read_register(2), read_register(3)); |
1444 | handled = true; |
1445 | } |
1446 | else if (pc == CDF1_ResetWave) |
1447 | { |
1448 | myCartridge->thumbCallback(1, read_register(2), 0); |
1449 | handled = true; |
1450 | } |
1451 | else if (pc == CDF1_GetWavePtr) |
1452 | { |
1453 | write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); |
1454 | handled = true; |
1455 | } |
1456 | else if (pc == CDF1_SetWaveSize) |
1457 | { |
1458 | myCartridge->thumbCallback(3, read_register(2), read_register(3)); |
1459 | handled = true; |
1460 | } |
1461 | else if (pc == 0x0000083a) |
1462 | { |
1463 | // exiting Custom ARM code, returning to BUS Driver control |
1464 | } |
1465 | else |
1466 | { |
1467 | #if 0 // uncomment this for testing |
1468 | uInt32 r0 = read_register(0); |
1469 | uInt32 r1 = read_register(1); |
1470 | uInt32 r2 = read_register(2); |
1471 | uInt32 r3 = read_register(3); |
1472 | uInt32 r4 = read_register(4); |
1473 | #endif |
1474 | myCartridge->thumbCallback(255, 0, 0); |
1475 | } |
1476 | |
1477 | break; |
1478 | |
1479 | case ConfigureFor::DPCplus: |
1480 | // no 32-bit subroutines in DPC+ |
1481 | break; |
1482 | } |
1483 | |
1484 | if (handled) |
1485 | { |
1486 | rc = read_register(14); // lr |
1487 | rc += 2; |
1488 | //rc &= ~1; |
1489 | write_register(15, rc); |
1490 | return 0; |
1491 | } |
1492 | |
1493 | return 1; |
1494 | } |
1495 | } |
1496 | |
1497 | //CMN |
1498 | case Op::cmn: { |
1499 | rn = (inst >> 0) & 0x7; |
1500 | rm = (inst >> 3) & 0x7; |
1501 | DO_DISS(statusMsg << "cmns r" << dec << rn << ",r" << dec << rm << endl); |
1502 | ra = read_register(rn); |
1503 | rb = read_register(rm); |
1504 | rc = ra + rb; |
1505 | do_nflag(rc); |
1506 | do_zflag(rc); |
1507 | do_cflag(ra, rb, 0); |
1508 | do_vflag(ra, rb, 0); |
1509 | return 0; |
1510 | } |
1511 | |
1512 | //CMP(1) compare immediate |
1513 | case Op::cmp1: { |
1514 | rb = (inst >> 0) & 0xFF; |
1515 | rn = (inst >> 8) & 0x07; |
1516 | DO_DISS(statusMsg << "cmp r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl); |
1517 | ra = read_register(rn); |
1518 | rc = ra - rb; |
1519 | //fprintf(stderr,"0x%08X 0x%08X\n",ra,rb); |
1520 | do_nflag(rc); |
1521 | do_zflag(rc); |
1522 | do_cflag(ra, ~rb, 1); |
1523 | do_vflag(ra, ~rb, 1); |
1524 | return 0; |
1525 | } |
1526 | |
1527 | //CMP(2) compare register |
1528 | case Op::cmp2: { |
1529 | rn = (inst >> 0) & 0x7; |
1530 | rm = (inst >> 3) & 0x7; |
1531 | DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl); |
1532 | ra = read_register(rn); |
1533 | rb = read_register(rm); |
1534 | rc = ra - rb; |
1535 | //fprintf(stderr,"0x%08X 0x%08X\n",ra,rb); |
1536 | do_nflag(rc); |
1537 | do_zflag(rc); |
1538 | do_cflag(ra, ~rb, 1); |
1539 | do_vflag(ra, ~rb, 1); |
1540 | return 0; |
1541 | } |
1542 | |
1543 | //CMP(3) compare high register |
1544 | case Op::cmp3: { |
1545 | if(((inst >> 6) & 3) == 0x0) |
1546 | { |
1547 | //UNPREDICTABLE |
1548 | } |
1549 | rn = (inst >> 0) & 0x7; |
1550 | rn |= (inst >> 4) & 0x8; |
1551 | if(rn == 0xF) |
1552 | { |
1553 | //UNPREDICTABLE |
1554 | } |
1555 | rm = (inst >> 3) & 0xF; |
1556 | DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl); |
1557 | ra = read_register(rn); |
1558 | rb = read_register(rm); |
1559 | rc = ra - rb; |
1560 | do_nflag(rc); |
1561 | do_zflag(rc); |
1562 | do_cflag(ra, ~rb, 1); |
1563 | do_vflag(ra, ~rb, 1); |
1564 | return 0; |
1565 | } |
1566 | |
1567 | #ifndef UNSAFE_OPTIMIZATIONS |
1568 | //CPS |
1569 | case Op::cps: { |
1570 | DO_DISS(statusMsg << "cps TODO" << endl); |
1571 | return 1; |
1572 | } |
1573 | #endif |
1574 | |
1575 | //CPY copy high register |
1576 | case Op::cpy: { |
1577 | //same as mov except you can use both low registers |
1578 | //going to let mov handle high registers |
1579 | rd = (inst >> 0) & 0x7; |
1580 | rm = (inst >> 3) & 0x7; |
1581 | DO_DISS(statusMsg << "cpy r" << dec << rd << ",r" << dec << rm << endl); |
1582 | rc = read_register(rm); |
1583 | write_register(rd, rc); |
1584 | return 0; |
1585 | } |
1586 | |
1587 | //EOR |
1588 | case Op::eor: { |
1589 | rd = (inst >> 0) & 0x7; |
1590 | rm = (inst >> 3) & 0x7; |
1591 | DO_DISS(statusMsg << "eors r" << dec << rd << ",r" << dec << rm << endl); |
1592 | ra = read_register(rd); |
1593 | rb = read_register(rm); |
1594 | rc = ra ^ rb; |
1595 | write_register(rd, rc); |
1596 | do_nflag(rc); |
1597 | do_zflag(rc); |
1598 | return 0; |
1599 | } |
1600 | |
1601 | //LDMIA |
1602 | case Op::ldmia: { |
1603 | rn = (inst >> 8) & 0x7; |
1604 | #if defined(THUMB_DISS) |
1605 | statusMsg << "ldmia r" << dec << rn << "!,{" ; |
1606 | for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) |
1607 | { |
1608 | if(inst&rb) |
1609 | { |
1610 | if(rc) statusMsg << "," ; |
1611 | statusMsg << "r" << dec << ra; |
1612 | rc++; |
1613 | } |
1614 | } |
1615 | statusMsg << "}" << endl; |
1616 | #endif |
1617 | sp = read_register(rn); |
1618 | for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) |
1619 | { |
1620 | if(inst & rb) |
1621 | { |
1622 | write_register(ra, read32(sp)); |
1623 | sp += 4; |
1624 | } |
1625 | } |
1626 | //there is a write back exception. |
1627 | if((inst & (1 << rn)) == 0) |
1628 | write_register(rn, sp); |
1629 | |
1630 | return 0; |
1631 | } |
1632 | |
1633 | //LDR(1) two register immediate |
1634 | case Op::ldr1: { |
1635 | rd = (inst >> 0) & 0x07; |
1636 | rn = (inst >> 3) & 0x07; |
1637 | rb = (inst >> 6) & 0x1F; |
1638 | rb <<= 2; |
1639 | DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); |
1640 | rb = read_register(rn) + rb; |
1641 | rc = read32(rb); |
1642 | write_register(rd, rc); |
1643 | return 0; |
1644 | } |
1645 | |
1646 | //LDR(2) three register |
1647 | case Op::ldr2: { |
1648 | rd = (inst >> 0) & 0x7; |
1649 | rn = (inst >> 3) & 0x7; |
1650 | rm = (inst >> 6) & 0x7; |
1651 | DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",r" << dec << "]" << endl); |
1652 | rb = read_register(rn) + read_register(rm); |
1653 | rc = read32(rb); |
1654 | write_register(rd, rc); |
1655 | return 0; |
1656 | } |
1657 | |
1658 | //LDR(3) |
1659 | case Op::ldr3: { |
1660 | rb = (inst >> 0) & 0xFF; |
1661 | rd = (inst >> 8) & 0x07; |
1662 | rb <<= 2; |
1663 | DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] " ); |
1664 | ra = read_register(15); |
1665 | ra &= ~3; |
1666 | rb += ra; |
1667 | DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl); |
1668 | rc = read32(rb); |
1669 | write_register(rd, rc); |
1670 | return 0; |
1671 | } |
1672 | |
1673 | //LDR(4) |
1674 | case Op::ldr4: { |
1675 | rb = (inst >> 0) & 0xFF; |
1676 | rd = (inst >> 8) & 0x07; |
1677 | rb <<= 2; |
1678 | DO_DISS(statusMsg << "ldr r" << dec << rd << ",[SP+#0x" << Base::HEX2 << rb << "]" << endl); |
1679 | ra = read_register(13); |
1680 | //ra&=~3; |
1681 | rb += ra; |
1682 | rc = read32(rb); |
1683 | write_register(rd, rc); |
1684 | return 0; |
1685 | } |
1686 | |
1687 | //LDRB(1) |
1688 | case Op::ldrb1: { |
1689 | rd = (inst >> 0) & 0x07; |
1690 | rn = (inst >> 3) & 0x07; |
1691 | rb = (inst >> 6) & 0x1F; |
1692 | DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); |
1693 | rb = read_register(rn) + rb; |
1694 | #ifndef UNSAFE_OPTIMIZATIONS |
1695 | rc = read16(rb & (~1u)); |
1696 | #else |
1697 | rc = read16(rb); |
1698 | #endif |
1699 | if(rb & 1) |
1700 | { |
1701 | rc >>= 8; |
1702 | } |
1703 | else |
1704 | { |
1705 | } |
1706 | write_register(rd, rc & 0xFF); |
1707 | return 0; |
1708 | } |
1709 | |
1710 | //LDRB(2) |
1711 | case Op::ldrb2: { |
1712 | rd = (inst >> 0) & 0x7; |
1713 | rn = (inst >> 3) & 0x7; |
1714 | rm = (inst >> 6) & 0x7; |
1715 | DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
1716 | rb = read_register(rn) + read_register(rm); |
1717 | #ifndef UNSAFE_OPTIMIZATIONS |
1718 | rc = read16(rb & (~1u)); |
1719 | #else |
1720 | rc = read16(rb); |
1721 | #endif |
1722 | if(rb & 1) |
1723 | { |
1724 | rc >>= 8; |
1725 | } |
1726 | write_register(rd, rc & 0xFF); |
1727 | return 0; |
1728 | } |
1729 | |
1730 | //LDRH(1) |
1731 | case Op::ldrh1: { |
1732 | rd = (inst >> 0) & 0x07; |
1733 | rn = (inst >> 3) & 0x07; |
1734 | rb = (inst >> 6) & 0x1F; |
1735 | rb <<= 1; |
1736 | DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); |
1737 | rb = read_register(rn) + rb; |
1738 | rc = read16(rb); |
1739 | write_register(rd, rc & 0xFFFF); |
1740 | return 0; |
1741 | } |
1742 | |
1743 | //LDRH(2) |
1744 | case Op::ldrh2: { |
1745 | rd = (inst >> 0) & 0x7; |
1746 | rn = (inst >> 3) & 0x7; |
1747 | rm = (inst >> 6) & 0x7; |
1748 | DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
1749 | rb = read_register(rn) + read_register(rm); |
1750 | rc = read16(rb); |
1751 | write_register(rd, rc & 0xFFFF); |
1752 | return 0; |
1753 | } |
1754 | |
1755 | //LDRSB |
1756 | case Op::ldrsb: { |
1757 | rd = (inst >> 0) & 0x7; |
1758 | rn = (inst >> 3) & 0x7; |
1759 | rm = (inst >> 6) & 0x7; |
1760 | DO_DISS(statusMsg << "ldrsb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
1761 | rb = read_register(rn) + read_register(rm); |
1762 | #ifndef UNSAFE_OPTIMIZATIONS |
1763 | rc = read16(rb & (~1u)); |
1764 | #else |
1765 | rc = read16(rb); |
1766 | #endif |
1767 | if(rb & 1) |
1768 | { |
1769 | rc >>= 8; |
1770 | } |
1771 | rc &= 0xFF; |
1772 | if(rc & 0x80) |
1773 | rc |= ((~0u) << 8); |
1774 | write_register(rd, rc); |
1775 | return 0; |
1776 | } |
1777 | |
1778 | //LDRSH |
1779 | case Op::ldrsh: { |
1780 | rd = (inst >> 0) & 0x7; |
1781 | rn = (inst >> 3) & 0x7; |
1782 | rm = (inst >> 6) & 0x7; |
1783 | DO_DISS(statusMsg << "ldrsh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
1784 | rb = read_register(rn) + read_register(rm); |
1785 | rc = read16(rb); |
1786 | rc &= 0xFFFF; |
1787 | if(rc & 0x8000) |
1788 | rc |= ((~0u) << 16); |
1789 | write_register(rd, rc); |
1790 | return 0; |
1791 | } |
1792 | |
1793 | //LSL(1) |
1794 | case Op::lsl1: { |
1795 | rd = (inst >> 0) & 0x07; |
1796 | rm = (inst >> 3) & 0x07; |
1797 | rb = (inst >> 6) & 0x1F; |
1798 | DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); |
1799 | rc = read_register(rm); |
1800 | if(rb == 0) |
1801 | { |
1802 | //if immed_5 == 0 |
1803 | //C unaffected |
1804 | //result not shifted |
1805 | } |
1806 | else |
1807 | { |
1808 | //else immed_5 > 0 |
1809 | do_cflag_bit(rc & (1 << (32-rb))); |
1810 | rc <<= rb; |
1811 | } |
1812 | write_register(rd, rc); |
1813 | do_nflag(rc); |
1814 | do_zflag(rc); |
1815 | return 0; |
1816 | } |
1817 | |
1818 | //LSL(2) two register |
1819 | case Op::lsl2: { |
1820 | rd = (inst >> 0) & 0x07; |
1821 | rs = (inst >> 3) & 0x07; |
1822 | DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rs << endl); |
1823 | rc = read_register(rd); |
1824 | rb = read_register(rs); |
1825 | rb &= 0xFF; |
1826 | if(rb == 0) |
1827 | { |
1828 | } |
1829 | else if(rb < 32) |
1830 | { |
1831 | do_cflag_bit(rc & (1 << (32-rb))); |
1832 | rc <<= rb; |
1833 | } |
1834 | else if(rb == 32) |
1835 | { |
1836 | do_cflag_bit(rc & 1); |
1837 | rc = 0; |
1838 | } |
1839 | else |
1840 | { |
1841 | do_cflag_bit(0); |
1842 | rc = 0; |
1843 | } |
1844 | write_register(rd, rc); |
1845 | do_nflag(rc); |
1846 | do_zflag(rc); |
1847 | return 0; |
1848 | } |
1849 | |
1850 | //LSR(1) two register immediate |
1851 | case Op::lsr1: { |
1852 | rd = (inst >> 0) & 0x07; |
1853 | rm = (inst >> 3) & 0x07; |
1854 | rb = (inst >> 6) & 0x1F; |
1855 | DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); |
1856 | rc = read_register(rm); |
1857 | if(rb == 0) |
1858 | { |
1859 | do_cflag_bit(rc & 0x80000000); |
1860 | rc = 0; |
1861 | } |
1862 | else |
1863 | { |
1864 | do_cflag_bit(rc & (1 << (rb-1))); |
1865 | rc >>= rb; |
1866 | } |
1867 | write_register(rd, rc); |
1868 | do_nflag(rc); |
1869 | do_zflag(rc); |
1870 | return 0; |
1871 | } |
1872 | |
1873 | //LSR(2) two register |
1874 | case Op::lsr2: { |
1875 | rd = (inst >> 0) & 0x07; |
1876 | rs = (inst >> 3) & 0x07; |
1877 | DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rs << endl); |
1878 | rc = read_register(rd); |
1879 | rb = read_register(rs); |
1880 | rb &= 0xFF; |
1881 | if(rb == 0) |
1882 | { |
1883 | } |
1884 | else if(rb < 32) |
1885 | { |
1886 | do_cflag_bit(rc & (1 << (rb-1))); |
1887 | rc >>= rb; |
1888 | } |
1889 | else if(rb == 32) |
1890 | { |
1891 | do_cflag_bit(rc & 0x80000000); |
1892 | rc = 0; |
1893 | } |
1894 | else |
1895 | { |
1896 | do_cflag_bit(0); |
1897 | rc = 0; |
1898 | } |
1899 | write_register(rd, rc); |
1900 | do_nflag(rc); |
1901 | do_zflag(rc); |
1902 | return 0; |
1903 | } |
1904 | |
1905 | //MOV(1) immediate |
1906 | case Op::mov1: { |
1907 | rb = (inst >> 0) & 0xFF; |
1908 | rd = (inst >> 8) & 0x07; |
1909 | DO_DISS(statusMsg << "movs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); |
1910 | write_register(rd, rb); |
1911 | do_nflag(rb); |
1912 | do_zflag(rb); |
1913 | return 0; |
1914 | } |
1915 | |
1916 | //MOV(2) two low registers |
1917 | case Op::mov2: { |
1918 | rd = (inst >> 0) & 7; |
1919 | rn = (inst >> 3) & 7; |
1920 | DO_DISS(statusMsg << "movs r" << dec << rd << ",r" << dec << rn << endl); |
1921 | rc = read_register(rn); |
1922 | //fprintf(stderr,"0x%08X\n",rc); |
1923 | write_register(rd, rc); |
1924 | do_nflag(rc); |
1925 | do_zflag(rc); |
1926 | do_cflag_bit(0); |
1927 | do_vflag_bit(0); |
1928 | return 0; |
1929 | } |
1930 | |
1931 | //MOV(3) |
1932 | case Op::mov3: { |
1933 | rd = (inst >> 0) & 0x7; |
1934 | rd |= (inst >> 4) & 0x8; |
1935 | rm = (inst >> 3) & 0xF; |
1936 | DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl); |
1937 | rc = read_register(rm); |
1938 | if((rd == 14) && (rm == 15)) |
1939 | { |
1940 | //printf("mov lr,pc warning 0x%08X\n",pc-2); |
1941 | //rc|=1; |
1942 | } |
1943 | if(rd == 15) |
1944 | { |
1945 | //rc &= ~1; //write_register may do this as well |
1946 | rc += 2; //The program counter is special |
1947 | } |
1948 | write_register(rd, rc); |
1949 | return 0; |
1950 | } |
1951 | |
1952 | //MUL |
1953 | case Op::mul: { |
1954 | rd = (inst >> 0) & 0x7; |
1955 | rm = (inst >> 3) & 0x7; |
1956 | DO_DISS(statusMsg << "muls r" << dec << rd << ",r" << dec << rm << endl); |
1957 | ra = read_register(rd); |
1958 | rb = read_register(rm); |
1959 | rc = ra * rb; |
1960 | write_register(rd, rc); |
1961 | do_nflag(rc); |
1962 | do_zflag(rc); |
1963 | return 0; |
1964 | } |
1965 | |
1966 | //MVN |
1967 | case Op::mvn: { |
1968 | rd = (inst >> 0) & 0x7; |
1969 | rm = (inst >> 3) & 0x7; |
1970 | DO_DISS(statusMsg << "mvns r" << dec << rd << ",r" << dec << rm << endl); |
1971 | ra = read_register(rm); |
1972 | rc = (~ra); |
1973 | write_register(rd, rc); |
1974 | do_nflag(rc); |
1975 | do_zflag(rc); |
1976 | return 0; |
1977 | } |
1978 | |
1979 | //NEG |
1980 | case Op::neg: { |
1981 | rd = (inst >> 0) & 0x7; |
1982 | rm = (inst >> 3) & 0x7; |
1983 | DO_DISS(statusMsg << "negs r" << dec << rd << ",r" << dec << rm << endl); |
1984 | ra = read_register(rm); |
1985 | rc = 0 - ra; |
1986 | write_register(rd, rc); |
1987 | do_nflag(rc); |
1988 | do_zflag(rc); |
1989 | do_cflag(0, ~ra, 1); |
1990 | do_vflag(0, ~ra, 1); |
1991 | return 0; |
1992 | } |
1993 | |
1994 | //ORR |
1995 | case Op::orr: { |
1996 | rd = (inst >> 0) & 0x7; |
1997 | rm = (inst >> 3) & 0x7; |
1998 | DO_DISS(statusMsg << "orrs r" << dec << rd << ",r" << dec << rm << endl); |
1999 | ra = read_register(rd); |
2000 | rb = read_register(rm); |
2001 | rc = ra | rb; |
2002 | write_register(rd, rc); |
2003 | do_nflag(rc); |
2004 | do_zflag(rc); |
2005 | return 0; |
2006 | } |
2007 | |
2008 | //POP |
2009 | case Op::pop: { |
2010 | #if defined(THUMB_DISS) |
2011 | statusMsg << "pop {" ; |
2012 | for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) |
2013 | { |
2014 | if(inst&rb) |
2015 | { |
2016 | if(rc) statusMsg << "," ; |
2017 | statusMsg << "r" << dec << ra; |
2018 | rc++; |
2019 | } |
2020 | } |
2021 | if(inst&0x100) |
2022 | { |
2023 | if(rc) statusMsg << "," ; |
2024 | statusMsg << "pc" ; |
2025 | } |
2026 | statusMsg << "}" << endl; |
2027 | #endif |
2028 | |
2029 | sp = read_register(13); |
2030 | for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) |
2031 | { |
2032 | if(inst & rb) |
2033 | { |
2034 | write_register(ra, read32(sp)); |
2035 | sp += 4; |
2036 | } |
2037 | } |
2038 | if(inst & 0x100) |
2039 | { |
2040 | rc = read32(sp); |
2041 | rc += 2; |
2042 | write_register(15, rc); |
2043 | sp += 4; |
2044 | } |
2045 | write_register(13, sp); |
2046 | return 0; |
2047 | } |
2048 | |
2049 | //PUSH |
2050 | case Op::push: { |
2051 | #if defined(THUMB_DISS) |
2052 | statusMsg << "push {" ; |
2053 | for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) |
2054 | { |
2055 | if(inst&rb) |
2056 | { |
2057 | if(rc) statusMsg << "," ; |
2058 | statusMsg << "r" << dec << ra; |
2059 | rc++; |
2060 | } |
2061 | } |
2062 | if(inst&0x100) |
2063 | { |
2064 | if(rc) statusMsg << "," ; |
2065 | statusMsg << "lr" ; |
2066 | } |
2067 | statusMsg << "}" << endl; |
2068 | #endif |
2069 | |
2070 | sp = read_register(13); |
2071 | //fprintf(stderr,"sp 0x%08X\n",sp); |
2072 | for(ra = 0, rb = 0x01, rc = 0; rb; rb = (rb << 1) & 0xFF, ++ra) |
2073 | { |
2074 | if(inst & rb) |
2075 | { |
2076 | ++rc; |
2077 | } |
2078 | } |
2079 | if(inst & 0x100) ++rc; |
2080 | rc <<= 2; |
2081 | sp -= rc; |
2082 | rd = sp; |
2083 | for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) |
2084 | { |
2085 | if(inst & rb) |
2086 | { |
2087 | write32(rd, read_register(ra)); |
2088 | rd += 4; |
2089 | } |
2090 | } |
2091 | if(inst & 0x100) |
2092 | { |
2093 | rc = read_register(14); |
2094 | write32(rd, rc); |
2095 | if((rc & 1) == 0) |
2096 | { |
2097 | // FIXME fprintf(stderr,"push {lr} with an ARM address pc 0x%08X popped 0x%08X\n",pc,rc); |
2098 | } |
2099 | } |
2100 | write_register(13, sp); |
2101 | return 0; |
2102 | } |
2103 | |
2104 | //REV |
2105 | case Op::rev: { |
2106 | rd = (inst >> 0) & 0x7; |
2107 | rn = (inst >> 3) & 0x7; |
2108 | DO_DISS(statusMsg << "rev r" << dec << rd << ",r" << dec << rn << endl); |
2109 | ra = read_register(rn); |
2110 | rc = ((ra >> 0) & 0xFF) << 24; |
2111 | rc |= ((ra >> 8) & 0xFF) << 16; |
2112 | rc |= ((ra >> 16) & 0xFF) << 8; |
2113 | rc |= ((ra >> 24) & 0xFF) << 0; |
2114 | write_register(rd, rc); |
2115 | return 0; |
2116 | } |
2117 | |
2118 | //REV16 |
2119 | case Op::rev16: { |
2120 | rd = (inst >> 0) & 0x7; |
2121 | rn = (inst >> 3) & 0x7; |
2122 | DO_DISS(statusMsg << "rev16 r" << dec << rd << ",r" << dec << rn << endl); |
2123 | ra = read_register(rn); |
2124 | rc = ((ra >> 0) & 0xFF) << 8; |
2125 | rc |= ((ra >> 8) & 0xFF) << 0; |
2126 | rc |= ((ra >> 16) & 0xFF) << 24; |
2127 | rc |= ((ra >> 24) & 0xFF) << 16; |
2128 | write_register(rd, rc); |
2129 | return 0; |
2130 | } |
2131 | |
2132 | //REVSH |
2133 | case Op::revsh: { |
2134 | rd = (inst >> 0) & 0x7; |
2135 | rn = (inst >> 3) & 0x7; |
2136 | DO_DISS(statusMsg << "revsh r" << dec << rd << ",r" << dec << rn << endl); |
2137 | ra = read_register(rn); |
2138 | rc = ((ra >> 0) & 0xFF) << 8; |
2139 | rc |= ((ra >> 8) & 0xFF) << 0; |
2140 | if(rc & 0x8000) rc |= 0xFFFF0000; |
2141 | else rc &= 0x0000FFFF; |
2142 | write_register(rd, rc); |
2143 | return 0; |
2144 | } |
2145 | |
2146 | //ROR |
2147 | case Op::ror: { |
2148 | rd = (inst >> 0) & 0x7; |
2149 | rs = (inst >> 3) & 0x7; |
2150 | DO_DISS(statusMsg << "rors r" << dec << rd << ",r" << dec << rs << endl); |
2151 | rc = read_register(rd); |
2152 | ra = read_register(rs); |
2153 | ra &= 0xFF; |
2154 | if(ra == 0) |
2155 | { |
2156 | } |
2157 | else |
2158 | { |
2159 | ra &= 0x1F; |
2160 | if(ra == 0) |
2161 | { |
2162 | do_cflag_bit(rc & 0x80000000); |
2163 | } |
2164 | else |
2165 | { |
2166 | do_cflag_bit(rc & (1 << (ra-1))); |
2167 | rb = rc << (32-ra); |
2168 | rc >>= ra; |
2169 | rc |= rb; |
2170 | } |
2171 | } |
2172 | write_register(rd, rc); |
2173 | do_nflag(rc); |
2174 | do_zflag(rc); |
2175 | return 0; |
2176 | } |
2177 | |
2178 | //SBC |
2179 | case Op::sbc: { |
2180 | rd = (inst >> 0) & 0x7; |
2181 | rm = (inst >> 3) & 0x7; |
2182 | DO_DISS(statusMsg << "sbc r" << dec << rd << ",r" << dec << rm << endl); |
2183 | ra = read_register(rd); |
2184 | rb = read_register(rm); |
2185 | rc = ra - rb; |
2186 | if(!(cpsr & CPSR_C)) --rc; |
2187 | write_register(rd, rc); |
2188 | do_nflag(rc); |
2189 | do_zflag(rc); |
2190 | if(cpsr & CPSR_C) |
2191 | { |
2192 | do_cflag(ra, ~rb, 1); |
2193 | do_vflag(ra, ~rb, 1); |
2194 | } |
2195 | else |
2196 | { |
2197 | do_cflag(ra, ~rb, 0); |
2198 | do_vflag(ra, ~rb, 0); |
2199 | } |
2200 | return 0; |
2201 | } |
2202 | |
2203 | #ifndef UNSAFE_OPTIMIZATIONS |
2204 | //SETEND |
2205 | case Op::setend: { |
2206 | statusMsg << "setend not implemented" << endl; |
2207 | return 1; |
2208 | } |
2209 | #endif |
2210 | |
2211 | //STMIA |
2212 | case Op::stmia: { |
2213 | rn = (inst >> 8) & 0x7; |
2214 | #if defined(THUMB_DISS) |
2215 | statusMsg << "stmia r" << dec << rn << "!,{" ; |
2216 | for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) |
2217 | { |
2218 | if(inst & rb) |
2219 | { |
2220 | if(rc) statusMsg << "," ; |
2221 | statusMsg << "r" << dec << ra; |
2222 | rc++; |
2223 | } |
2224 | } |
2225 | statusMsg << "}" << endl; |
2226 | #endif |
2227 | |
2228 | sp = read_register(rn); |
2229 | for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) |
2230 | { |
2231 | if(inst & rb) |
2232 | { |
2233 | write32(sp, read_register(ra)); |
2234 | sp += 4; |
2235 | } |
2236 | } |
2237 | write_register(rn, sp); |
2238 | return 0; |
2239 | } |
2240 | |
2241 | //STR(1) |
2242 | case Op::str1: { |
2243 | rd = (inst >> 0) & 0x07; |
2244 | rn = (inst >> 3) & 0x07; |
2245 | rb = (inst >> 6) & 0x1F; |
2246 | rb <<= 2; |
2247 | DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); |
2248 | rb = read_register(rn) + rb; |
2249 | rc = read_register(rd); |
2250 | write32(rb, rc); |
2251 | return 0; |
2252 | } |
2253 | |
2254 | //STR(2) |
2255 | case Op::str2: { |
2256 | rd = (inst >> 0) & 0x7; |
2257 | rn = (inst >> 3) & 0x7; |
2258 | rm = (inst >> 6) & 0x7; |
2259 | DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
2260 | rb = read_register(rn) + read_register(rm); |
2261 | rc = read_register(rd); |
2262 | write32(rb, rc); |
2263 | return 0; |
2264 | } |
2265 | |
2266 | //STR(3) |
2267 | case Op::str3: { |
2268 | rb = (inst >> 0) & 0xFF; |
2269 | rd = (inst >> 8) & 0x07; |
2270 | rb <<= 2; |
2271 | DO_DISS(statusMsg << "str r" << dec << rd << ",[SP,#0x" << Base::HEX2 << rb << "]" << endl); |
2272 | rb = read_register(13) + rb; |
2273 | //fprintf(stderr,"0x%08X\n",rb); |
2274 | rc = read_register(rd); |
2275 | write32(rb, rc); |
2276 | return 0; |
2277 | } |
2278 | |
2279 | //STRB(1) |
2280 | case Op::strb1: { |
2281 | rd = (inst >> 0) & 0x07; |
2282 | rn = (inst >> 3) & 0x07; |
2283 | rb = (inst >> 6) & 0x1F; |
2284 | DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX8 << rb << "]" << endl); |
2285 | rb = read_register(rn) + rb; |
2286 | rc = read_register(rd); |
2287 | #ifndef UNSAFE_OPTIMIZATIONS |
2288 | ra = read16(rb & (~1u)); |
2289 | #else |
2290 | ra = read16(rb); |
2291 | #endif |
2292 | if(rb & 1) |
2293 | { |
2294 | ra &= 0x00FF; |
2295 | ra |= rc << 8; |
2296 | } |
2297 | else |
2298 | { |
2299 | ra &= 0xFF00; |
2300 | ra |= rc & 0x00FF; |
2301 | } |
2302 | write16(rb & (~1u), ra & 0xFFFF); |
2303 | return 0; |
2304 | } |
2305 | |
2306 | //STRB(2) |
2307 | case Op::strb2: { |
2308 | rd = (inst >> 0) & 0x7; |
2309 | rn = (inst >> 3) & 0x7; |
2310 | rm = (inst >> 6) & 0x7; |
2311 | DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",r" << rm << "]" << endl); |
2312 | rb = read_register(rn) + read_register(rm); |
2313 | rc = read_register(rd); |
2314 | #ifndef UNSAFE_OPTIMIZATIONS |
2315 | ra = read16(rb & (~1u)); |
2316 | #else |
2317 | ra = read16(rb); |
2318 | #endif |
2319 | if(rb & 1) |
2320 | { |
2321 | ra &= 0x00FF; |
2322 | ra |= rc << 8; |
2323 | } |
2324 | else |
2325 | { |
2326 | ra &= 0xFF00; |
2327 | ra |= rc & 0x00FF; |
2328 | } |
2329 | write16(rb & (~1u), ra & 0xFFFF); |
2330 | return 0; |
2331 | } |
2332 | |
2333 | //STRH(1) |
2334 | case Op::strh1: { |
2335 | rd = (inst >> 0) & 0x07; |
2336 | rn = (inst >> 3) & 0x07; |
2337 | rb = (inst >> 6) & 0x1F; |
2338 | rb <<= 1; |
2339 | DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); |
2340 | rb = read_register(rn) + rb; |
2341 | rc= read_register(rd); |
2342 | write16(rb, rc & 0xFFFF); |
2343 | return 0; |
2344 | } |
2345 | |
2346 | //STRH(2) |
2347 | case Op::strh2: { |
2348 | rd = (inst >> 0) & 0x7; |
2349 | rn = (inst >> 3) & 0x7; |
2350 | rm = (inst >> 6) & 0x7; |
2351 | DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); |
2352 | rb = read_register(rn) + read_register(rm); |
2353 | rc = read_register(rd); |
2354 | write16(rb, rc & 0xFFFF); |
2355 | return 0; |
2356 | } |
2357 | |
2358 | //SUB(1) |
2359 | case Op::sub1: { |
2360 | rd = (inst >> 0) & 0x7; |
2361 | rn = (inst >> 3) & 0x7; |
2362 | rb = (inst >> 6) & 0x7; |
2363 | DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl); |
2364 | ra = read_register(rn); |
2365 | rc = ra - rb; |
2366 | write_register(rd, rc); |
2367 | do_nflag(rc); |
2368 | do_zflag(rc); |
2369 | do_cflag(ra, ~rb, 1); |
2370 | do_vflag(ra, ~rb, 1); |
2371 | return 0; |
2372 | } |
2373 | |
2374 | //SUB(2) |
2375 | case Op::sub2: { |
2376 | rb = (inst >> 0) & 0xFF; |
2377 | rd = (inst >> 8) & 0x07; |
2378 | DO_DISS(statusMsg << "subs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); |
2379 | ra = read_register(rd); |
2380 | rc = ra - rb; |
2381 | write_register(rd, rc); |
2382 | do_nflag(rc); |
2383 | do_zflag(rc); |
2384 | do_cflag(ra, ~rb, 1); |
2385 | do_vflag(ra, ~rb, 1); |
2386 | return 0; |
2387 | } |
2388 | |
2389 | //SUB(3) |
2390 | case Op::sub3: { |
2391 | rd = (inst >> 0) & 0x7; |
2392 | rn = (inst >> 3) & 0x7; |
2393 | rm = (inst >> 6) & 0x7; |
2394 | DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",r" << dec << rm << endl); |
2395 | ra = read_register(rn); |
2396 | rb = read_register(rm); |
2397 | rc = ra - rb; |
2398 | write_register(rd, rc); |
2399 | do_nflag(rc); |
2400 | do_zflag(rc); |
2401 | do_cflag(ra, ~rb, 1); |
2402 | do_vflag(ra, ~rb, 1); |
2403 | return 0; |
2404 | } |
2405 | |
2406 | //SUB(4) |
2407 | case Op::sub4: { |
2408 | rb = inst & 0x7F; |
2409 | rb <<= 2; |
2410 | DO_DISS(statusMsg << "sub SP,#0x" << Base::HEX2 << rb << endl); |
2411 | ra = read_register(13); |
2412 | ra -= rb; |
2413 | write_register(13, ra); |
2414 | return 0; |
2415 | } |
2416 | |
2417 | //SWI |
2418 | case Op::swi: { |
2419 | rb = inst & 0xFF; |
2420 | DO_DISS(statusMsg << "swi 0x" << Base::HEX2 << rb << endl); |
2421 | |
2422 | if((inst & 0xFF) == 0xCC) |
2423 | { |
2424 | write_register(0, cpsr); |
2425 | return 0; |
2426 | } |
2427 | else |
2428 | { |
2429 | #if defined(THUMB_DISS) |
2430 | statusMsg << endl << endl << "swi 0x" << Base::HEX2 << rb << endl; |
2431 | #endif |
2432 | return 1; |
2433 | } |
2434 | } |
2435 | |
2436 | //SXTB |
2437 | case Op::sxtb: { |
2438 | rd = (inst >> 0) & 0x7; |
2439 | rm = (inst >> 3) & 0x7; |
2440 | DO_DISS(statusMsg << "sxtb r" << dec << rd << ",r" << dec << rm << endl); |
2441 | ra = read_register(rm); |
2442 | rc = ra & 0xFF; |
2443 | if(rc & 0x80) |
2444 | rc |= (~0u) << 8; |
2445 | write_register(rd, rc); |
2446 | return 0; |
2447 | } |
2448 | |
2449 | //SXTH |
2450 | case Op::sxth: { |
2451 | rd = (inst >> 0) & 0x7; |
2452 | rm = (inst >> 3) & 0x7; |
2453 | DO_DISS(statusMsg << "sxth r" << dec << rd << ",r" << dec << rm << endl); |
2454 | ra = read_register(rm); |
2455 | rc = ra & 0xFFFF; |
2456 | if(rc & 0x8000) |
2457 | rc |= (~0u) << 16; |
2458 | write_register(rd, rc); |
2459 | return 0; |
2460 | } |
2461 | |
2462 | //TST |
2463 | case Op::tst: { |
2464 | rn = (inst >> 0) & 0x7; |
2465 | rm = (inst >> 3) & 0x7; |
2466 | DO_DISS(statusMsg << "tst r" << dec << rn << ",r" << dec << rm << endl); |
2467 | ra = read_register(rn); |
2468 | rb = read_register(rm); |
2469 | rc = ra & rb; |
2470 | do_nflag(rc); |
2471 | do_zflag(rc); |
2472 | return 0; |
2473 | } |
2474 | |
2475 | //UXTB |
2476 | case Op::uxtb: { |
2477 | rd = (inst >> 0) & 0x7; |
2478 | rm = (inst >> 3) & 0x7; |
2479 | DO_DISS(statusMsg << "uxtb r" << dec << rd << ",r" << dec << rm << endl); |
2480 | ra = read_register(rm); |
2481 | rc = ra & 0xFF; |
2482 | write_register(rd, rc); |
2483 | return 0; |
2484 | } |
2485 | |
2486 | //UXTH |
2487 | case Op::uxth: { |
2488 | rd = (inst >> 0) & 0x7; |
2489 | rm = (inst >> 3) & 0x7; |
2490 | DO_DISS(statusMsg << "uxth r" << dec << rd << ",r" << dec << rm << endl); |
2491 | ra = read_register(rm); |
2492 | rc = ra & 0xFFFF; |
2493 | write_register(rd, rc); |
2494 | return 0; |
2495 | } |
2496 | |
2497 | #ifndef UNSAFE_OPTIMIZATIONS |
2498 | case Op::invalid: |
2499 | break; |
2500 | #else |
2501 | default: |
2502 | break; |
2503 | #endif |
2504 | } |
2505 | |
2506 | #ifndef UNSAFE_OPTIMIZATIONS |
2507 | statusMsg << "invalid instruction " << Base::HEX8 << pc << " " << Base::HEX4 << inst << endl; |
2508 | #endif |
2509 | return 1; |
2510 | } |
2511 | |
2512 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2513 | int Thumbulator::reset() |
2514 | { |
2515 | std::fill(reg_norm, reg_norm+12, 0); |
2516 | reg_norm[13] = 0x40001FB4; |
2517 | |
2518 | switch(configuration) |
2519 | { |
2520 | // future 2K Harmony/Melody drivers will most likely use these settings |
2521 | case ConfigureFor::BUS: |
2522 | case ConfigureFor::CDF: |
2523 | case ConfigureFor::CDF1: |
2524 | case ConfigureFor::CDFJ: |
2525 | reg_norm[14] = 0x00000800; // Link Register |
2526 | reg_norm[15] = 0x0000080B; // Program Counter |
2527 | break; |
2528 | |
2529 | // future 3K Harmony/Melody drivers will most likely use these settings |
2530 | case ConfigureFor::DPCplus: |
2531 | reg_norm[14] = 0x00000C00; // Link Register |
2532 | reg_norm[15] = 0x00000C0B; // Program Counter |
2533 | break; |
2534 | } |
2535 | |
2536 | cpsr = mamcr = 0; |
2537 | handler_mode = false; |
2538 | |
2539 | systick_ctrl = 0x00000004; |
2540 | systick_reload = 0x00000000; |
2541 | systick_count = 0x00000000; |
2542 | systick_calibrate = 0x00ABCDEF; |
2543 | |
2544 | // fxq: don't care about below so much (maybe to guess timing???) |
2545 | #ifndef UNSAFE_OPTIMIZATIONS |
2546 | instructions = 0; |
2547 | statusMsg.str("" ); |
2548 | #endif |
2549 | #ifndef NO_THUMB_STATS |
2550 | fetches = reads = writes = 0; |
2551 | #endif |
2552 | |
2553 | return 0; |
2554 | } |
2555 | |
2556 | #ifndef UNSAFE_OPTIMIZATIONS |
2557 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2558 | bool Thumbulator::trapOnFatal = true; |
2559 | #endif |
2560 | |