25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_ppc.c,v 1.63 2005/06/26 22:23:42 debug Exp $ |
* $Id: cpu_ppc.c,v 1.85 2005/08/15 05:59:53 debug Exp $ |
29 |
* |
* |
30 |
* PowerPC/POWER CPU emulation. |
* PowerPC/POWER CPU emulation. |
31 |
*/ |
*/ |
65 |
#include "opcodes_ppc.h" |
#include "opcodes_ppc.h" |
66 |
#include "symbol.h" |
#include "symbol.h" |
67 |
|
|
68 |
|
#define DYNTRANS_DUALMODE_32 |
69 |
extern volatile int single_step; |
#define DYNTRANS_32 |
70 |
extern int old_show_trace_tree; |
#include "tmp_ppc_head.c" |
|
extern int old_instruction_trace; |
|
|
extern int old_quiet_mode; |
|
|
extern int quiet_mode; |
|
71 |
|
|
72 |
|
|
73 |
/* |
/* |
98 |
if (found == -1) |
if (found == -1) |
99 |
return 0; |
return 0; |
100 |
|
|
101 |
cpu->memory_rw = ppc_memory_rw; |
cpu->memory_rw = ppc_memory_rw; |
102 |
|
cpu->update_translation_table = ppc_update_translation_table; |
103 |
|
cpu->invalidate_translation_caches_paddr = |
104 |
|
ppc_invalidate_translation_caches_paddr; |
105 |
|
cpu->invalidate_code_translation_caches = |
106 |
|
ppc_invalidate_code_translation_caches; |
107 |
|
|
108 |
cpu->cd.ppc.cpu_type = cpu_type_defs[found]; |
cpu->cd.ppc.cpu_type = cpu_type_defs[found]; |
109 |
cpu->name = cpu->cd.ppc.cpu_type.name; |
cpu->name = cpu->cd.ppc.cpu_type.name; |
110 |
cpu->byte_order = EMUL_BIG_ENDIAN; |
cpu->byte_order = EMUL_BIG_ENDIAN; |
114 |
/* Current operating mode: */ |
/* Current operating mode: */ |
115 |
cpu->cd.ppc.bits = cpu->cd.ppc.cpu_type.bits; |
cpu->cd.ppc.bits = cpu->cd.ppc.cpu_type.bits; |
116 |
|
|
117 |
|
cpu->is_32bit = (cpu->cd.ppc.bits == 32)? 1 : 0; |
118 |
|
|
119 |
/* Only show name and caches etc for CPU nr 0 (in SMP machines): */ |
/* Only show name and caches etc for CPU nr 0 (in SMP machines): */ |
120 |
if (cpu_id == 0) { |
if (cpu_id == 0) { |
121 |
debug("%s", cpu->cd.ppc.cpu_type.name); |
debug("%s", cpu->cd.ppc.cpu_type.name); |
209 |
/* |
/* |
210 |
* reg_access_msr(): |
* reg_access_msr(): |
211 |
*/ |
*/ |
212 |
static void reg_access_msr(struct cpu *cpu, uint64_t *valuep, int writeflag) |
void reg_access_msr(struct cpu *cpu, uint64_t *valuep, int writeflag) |
213 |
{ |
{ |
214 |
if (valuep == NULL) { |
if (valuep == NULL) { |
215 |
fatal("reg_access_msr(): NULL\n"); |
fatal("reg_access_msr(): NULL\n"); |
262 |
debug("%08x", (int)cpu->cd.ppc.lr); |
debug("%08x", (int)cpu->cd.ppc.lr); |
263 |
else |
else |
264 |
debug("%016llx", (long long)cpu->cd.ppc.lr); |
debug("%016llx", (long long)cpu->cd.ppc.lr); |
265 |
debug(" cr = 0x%08x\n", (int)cpu->cd.ppc.cr); |
debug(" cr = 0x%08x\n", (int)cpu->cd.ppc.cr); |
266 |
|
|
267 |
debug("cpu%i: ctr = 0x", x); |
debug("cpu%i: ctr = 0x", x); |
268 |
if (bits32) |
if (bits32) |
289 |
} else { |
} else { |
290 |
/* 64-bit: */ |
/* 64-bit: */ |
291 |
for (i=0; i<PPC_NGPRS; i++) { |
for (i=0; i<PPC_NGPRS; i++) { |
292 |
|
int r = (i >> 1) + ((i & 1) << 4); |
293 |
if ((i % 2) == 0) |
if ((i % 2) == 0) |
294 |
debug("cpu%i:", x); |
debug("cpu%i:", x); |
295 |
debug(" r%02i = 0x%016llx ", i, |
debug(" r%02i = 0x%016llx ", r, |
296 |
(long long)cpu->cd.ppc.gpr[i]); |
(long long)cpu->cd.ppc.gpr[r]); |
297 |
if ((i % 2) == 1) |
if ((i % 2) == 1) |
298 |
debug("\n"); |
debug("\n"); |
299 |
} |
} |
302 |
/* Other special registers: */ |
/* Other special registers: */ |
303 |
reg_access_msr(cpu, &tmp, 0); |
reg_access_msr(cpu, &tmp, 0); |
304 |
debug("cpu%i: msr = 0x%016llx ", x, (long long)tmp); |
debug("cpu%i: msr = 0x%016llx ", x, (long long)tmp); |
305 |
debug("tb = 0x%08x%08x\n", |
debug("tb = 0x%08x%08x\n", |
306 |
(int)cpu->cd.ppc.tbu, (int)cpu->cd.ppc.tbl); |
(int)cpu->cd.ppc.tbu, (int)cpu->cd.ppc.tbl); |
307 |
debug("cpu%i: dec = 0x%08x hdec = 0x%08x\n", |
debug("cpu%i: dec = 0x%08x hdec = 0x%08x\n", |
308 |
x, (int)cpu->cd.ppc.dec, (int)cpu->cd.ppc.hdec); |
x, (int)cpu->cd.ppc.dec, (int)cpu->cd.ppc.hdec); |
417 |
|
|
418 |
|
|
419 |
/* |
/* |
420 |
|
* ppc_cpu_show_full_statistics(): |
421 |
|
* |
422 |
|
* Show detailed statistics on opcode usage on each cpu. |
423 |
|
*/ |
424 |
|
void ppc_cpu_show_full_statistics(struct machine *m) |
425 |
|
{ |
426 |
|
fatal("ppc_cpu_show_full_statistics(): TODO\n"); |
427 |
|
} |
428 |
|
|
429 |
|
|
430 |
|
/* |
431 |
|
* ppc_cpu_tlbdump(): |
432 |
|
* |
433 |
|
* Called from the debugger to dump the TLB in a readable format. |
434 |
|
* x is the cpu number to dump, or -1 to dump all CPUs. |
435 |
|
* |
436 |
|
* If rawflag is nonzero, then the TLB contents isn't formated nicely, |
437 |
|
* just dumped. |
438 |
|
*/ |
439 |
|
void ppc_cpu_tlbdump(struct machine *m, int x, int rawflag) |
440 |
|
{ |
441 |
|
fatal("ppc_cpu_tlbdump(): TODO\n"); |
442 |
|
} |
443 |
|
|
444 |
|
|
445 |
|
/* |
446 |
|
* ppc_cpu_interrupt(): |
447 |
|
*/ |
448 |
|
int ppc_cpu_interrupt(struct cpu *cpu, uint64_t irq_nr) |
449 |
|
{ |
450 |
|
fatal("ppc_cpu_interrupt(): TODO\n"); |
451 |
|
return 0; |
452 |
|
} |
453 |
|
|
454 |
|
|
455 |
|
/* |
456 |
|
* ppc_cpu_interrupt_ack(): |
457 |
|
*/ |
458 |
|
int ppc_cpu_interrupt_ack(struct cpu *cpu, uint64_t irq_nr) |
459 |
|
{ |
460 |
|
/* fatal("ppc_cpu_interrupt_ack(): TODO\n"); */ |
461 |
|
return 0; |
462 |
|
} |
463 |
|
|
464 |
|
|
465 |
|
/* |
466 |
* ppc_cpu_disassemble_instr(): |
* ppc_cpu_disassemble_instr(): |
467 |
* |
* |
468 |
* Convert an instruction word into human readable format, for instruction |
* Convert an instruction word into human readable format, for instruction |
507 |
|
|
508 |
debug(": %08x\t", iword); |
debug(": %08x\t", iword); |
509 |
|
|
|
if (bintrans && !running) { |
|
|
debug("(bintrans)"); |
|
|
goto disasm_ret; |
|
|
} |
|
|
|
|
510 |
/* |
/* |
511 |
* Decode the instruction: |
* Decode the instruction: |
512 |
*/ |
*/ |
787 |
rt = (iword >> 21) & 31; |
rt = (iword >> 21) & 31; |
788 |
debug("mfcr\tr%i", rt); |
debug("mfcr\tr%i", rt); |
789 |
break; |
break; |
|
case PPC_31_DCBST: |
|
|
case PPC_31_ICBI: |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
switch (xo) { |
|
|
case PPC_31_DCBST: mnem = "dcbst"; break; |
|
|
case PPC_31_ICBI: mnem = "icbi"; break; |
|
|
} |
|
|
debug("%s\tr%i,r%i", mnem, ra, rb); |
|
|
break; |
|
790 |
case PPC_31_MFMSR: |
case PPC_31_MFMSR: |
791 |
rt = (iword >> 21) & 31; |
rt = (iword >> 21) & 31; |
792 |
debug("mfmsr\tr%i", rt); |
debug("mfmsr\tr%i", rt); |
815 |
case PPC_31_STHUX: |
case PPC_31_STHUX: |
816 |
case PPC_31_STWX: |
case PPC_31_STWX: |
817 |
case PPC_31_STWUX: |
case PPC_31_STWUX: |
818 |
|
case PPC_31_STDX: |
819 |
|
case PPC_31_STDUX: |
820 |
/* rs for stores, rt for loads, actually */ |
/* rs for stores, rt for loads, actually */ |
821 |
rs = (iword >> 21) & 31; |
rs = (iword >> 21) & 31; |
822 |
ra = (iword >> 16) & 31; |
ra = (iword >> 16) & 31; |
842 |
case PPC_31_STWUX: |
case PPC_31_STWUX: |
843 |
mnem = power? "stux" : "stwux"; |
mnem = power? "stux" : "stwux"; |
844 |
break; |
break; |
845 |
|
case PPC_31_STDX: mnem = "stdx"; break; |
846 |
|
case PPC_31_STDUX: mnem = "stdux"; break; |
847 |
} |
} |
848 |
debug("%s\tr%i,r%i,r%i", mnem, rs, ra, rb); |
debug("%s\tr%i,r%i,r%i", mnem, rs, ra, rb); |
|
if (running) |
|
|
goto disasm_ret_nonewline; |
|
849 |
break; |
break; |
850 |
case PPC_31_NEG: |
case PPC_31_NEG: |
851 |
case PPC_31_NEGO: |
case PPC_31_NEGO: |
968 |
case PPC_31_MFSPR: |
case PPC_31_MFSPR: |
969 |
rt = (iword >> 21) & 31; |
rt = (iword >> 21) & 31; |
970 |
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
971 |
debug("mfspr\tr%i,spr%i", rt, spr); |
switch (spr) { |
972 |
|
case 8: debug("mflr\tr%i", rt); break; |
973 |
|
case 272: debug("mfsprg\t0,r%i", rt); break; |
974 |
|
case 273: debug("mfsprg\t1,r%i", rt); break; |
975 |
|
case 274: debug("mfsprg\t2,r%i", rt); break; |
976 |
|
case 275: debug("mfsprg\t3,r%i", rt); break; |
977 |
|
case 1008: debug("mfdbsr\tr%i", rt); break; |
978 |
|
default:debug("mfspr\tr%i,spr%i", rt, spr); |
979 |
|
} |
980 |
break; |
break; |
981 |
case PPC_31_TLBIE: |
case PPC_31_TLBIE: |
982 |
/* TODO: what is ra? The IBM online docs didn't say */ |
/* TODO: what is ra? The IBM online docs didn't say */ |
1003 |
mnem = power? "cntlz" : "cntlzw"; |
mnem = power? "cntlz" : "cntlzw"; |
1004 |
debug("%s\tr%i,r%i", mnem, rc? "." : "", ra, rs); |
debug("%s\tr%i,r%i", mnem, rc? "." : "", ra, rs); |
1005 |
break; |
break; |
1006 |
|
case PPC_31_CLF: /* POWER only */ |
1007 |
|
case PPC_31_CLI: /* POWER only */ |
1008 |
|
case PPC_31_DCLST: /* POWER only */ |
1009 |
|
case PPC_31_DCBF: /* PowerPC only */ |
1010 |
|
case PPC_31_DCBI: /* PowerPC only */ |
1011 |
|
case PPC_31_DCBST: /* PowerPC only */ |
1012 |
|
case PPC_31_DCBTST: /* PowerPC only */ |
1013 |
|
case PPC_31_DCBT: /* PowerPC only */ |
1014 |
|
case PPC_31_ICBI: /* PowerPC only */ |
1015 |
|
case PPC_31_DCBZ: /* POWER/PowerPC */ |
1016 |
|
ra = (iword >> 16) & 31; |
1017 |
|
rb = (iword >> 11) & 31; |
1018 |
|
switch (xo) { |
1019 |
|
case PPC_31_CLF: mnem = "clf"; break; |
1020 |
|
case PPC_31_CLI: mnem = "cli"; break; |
1021 |
|
case PPC_31_DCLST: mnem = "dclst"; break; |
1022 |
|
case PPC_31_DCBF: mnem = "dcbf"; break; |
1023 |
|
case PPC_31_DCBI: mnem = "dcbi"; break; |
1024 |
|
case PPC_31_DCBST: mnem = "dcbst"; break; |
1025 |
|
case PPC_31_DCBTST:mnem = "dcbtst"; break; |
1026 |
|
case PPC_31_DCBT: mnem = "dcbt"; break; |
1027 |
|
case PPC_31_ICBI: mnem = "icbi"; break; |
1028 |
|
case PPC_31_DCBZ: mnem = power ? |
1029 |
|
"dclz" : "dcbz"; break; |
1030 |
|
} |
1031 |
|
debug("%s\tr%i,r%i", mnem, ra, rb); |
1032 |
|
break; |
1033 |
case PPC_31_SLW: |
case PPC_31_SLW: |
1034 |
case PPC_31_SRAW: |
case PPC_31_SRAW: |
1035 |
case PPC_31_SRW: |
case PPC_31_SRW: |
1097 |
case PPC_31_MTSPR: |
case PPC_31_MTSPR: |
1098 |
rs = (iword >> 21) & 31; |
rs = (iword >> 21) & 31; |
1099 |
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
1100 |
debug("mtspr\tspr%i,r%i", spr, rs); |
switch (spr) { |
1101 |
|
case 8: debug("mtlr\tr%i", rs); break; |
1102 |
|
case 272: debug("mtsprg\t0,r%i", rs); break; |
1103 |
|
case 273: debug("mtsprg\t1,r%i", rs); break; |
1104 |
|
case 274: debug("mtsprg\t2,r%i", rs); break; |
1105 |
|
case 275: debug("mtsprg\t3,r%i", rs); break; |
1106 |
|
default:debug("mtspr\tspr%i,r%i", spr, rs); |
1107 |
|
} |
1108 |
break; |
break; |
1109 |
case PPC_31_SYNC: |
case PPC_31_SYNC: |
1110 |
debug("%s", power? "dcs" : "sync"); |
debug("%s", power? "dcs" : "sync"); |
1121 |
mnem = power? "stsi" : "stswi"; break; |
mnem = power? "stsi" : "stswi"; break; |
1122 |
} |
} |
1123 |
debug("%s\tr%i,r%i,%i", mnem, rs, ra, nb); |
debug("%s\tr%i,r%i,%i", mnem, rs, ra, nb); |
1124 |
if (running) |
break; |
1125 |
goto disasm_ret_nonewline; |
case PPC_31_LHBRX: |
1126 |
|
case PPC_31_LWBRX: |
1127 |
|
case PPC_31_STHBRX: |
1128 |
|
case PPC_31_STWBRX: |
1129 |
|
rt = (iword >> 21) & 31; /* stores use rs */ |
1130 |
|
ra = (iword >> 16) & 31; |
1131 |
|
rb = (iword >> 11) & 31; |
1132 |
|
switch (xo) { |
1133 |
|
case PPC_31_LHBRX: mnem = "lhbrx"; break; |
1134 |
|
case PPC_31_LWBRX: mnem = power? |
1135 |
|
"lbrx" : "lwbrx"; break; |
1136 |
|
case PPC_31_STHBRX: mnem = "sthbrx"; break; |
1137 |
|
case PPC_31_STWBRX: mnem = power? |
1138 |
|
"stbrx" : "stwbrx"; break; |
1139 |
|
} |
1140 |
|
debug("%s\tr%i,r%i,r%i", mnem, rt, ra, rb); |
1141 |
break; |
break; |
1142 |
case PPC_31_SRAWI: |
case PPC_31_SRAWI: |
1143 |
rs = (iword >> 21) & 31; |
rs = (iword >> 21) & 31; |
1182 |
case PPC_HI6_LHAU: |
case PPC_HI6_LHAU: |
1183 |
case PPC_HI6_LBZ: |
case PPC_HI6_LBZ: |
1184 |
case PPC_HI6_LBZU: |
case PPC_HI6_LBZU: |
1185 |
|
case PPC_HI6_LMW: |
1186 |
case PPC_HI6_STW: |
case PPC_HI6_STW: |
1187 |
case PPC_HI6_STWU: |
case PPC_HI6_STWU: |
1188 |
case PPC_HI6_STH: |
case PPC_HI6_STH: |
1224 |
else |
else |
1225 |
debug("r"); |
debug("r"); |
1226 |
debug("%i,%i(r%i)", rs, imm, ra); |
debug("%i,%i(r%i)", rs, imm, ra); |
|
if (running) |
|
|
goto disasm_ret_nonewline; |
|
1227 |
break; |
break; |
1228 |
default: |
default: |
1229 |
/* TODO */ |
/* TODO */ |
1230 |
debug("unimplemented hi6 = 0x%02x", hi6); |
debug("unimplemented hi6 = 0x%02x", hi6); |
1231 |
} |
} |
1232 |
|
|
|
disasm_ret: |
|
1233 |
debug("\n"); |
debug("\n"); |
|
disasm_ret_nonewline: |
|
1234 |
return sizeof(iword); |
return sizeof(iword); |
1235 |
} |
} |
1236 |
|
|
1237 |
|
|
1238 |
/* |
/* |
|
* show_trace(): |
|
|
* |
|
|
* Show trace tree. This function should be called every time |
|
|
* a function is called. cpu->cd.ppc.trace_tree_depth is increased here |
|
|
* and should not be increased by the caller. |
|
|
* |
|
|
* Note: This function should not be called if show_trace_tree == 0. |
|
|
*/ |
|
|
static void show_trace(struct cpu *cpu) |
|
|
{ |
|
|
uint64_t offset, addr = cpu->pc; |
|
|
int x, n_args_to_print; |
|
|
char strbuf[60]; |
|
|
char *symbol; |
|
|
|
|
|
cpu->cd.ppc.trace_tree_depth ++; |
|
|
|
|
|
if (cpu->machine->ncpus > 1) |
|
|
debug("cpu%i:", cpu->cpu_id); |
|
|
|
|
|
symbol = get_symbol_name(&cpu->machine->symbol_context, addr, &offset); |
|
|
|
|
|
for (x=0; x<cpu->cd.ppc.trace_tree_depth; x++) |
|
|
debug(" "); |
|
|
|
|
|
/* debug("<%s>\n", symbol!=NULL? symbol : "no symbol"); */ |
|
|
|
|
|
if (symbol != NULL) |
|
|
debug("<%s(", symbol); |
|
|
else { |
|
|
debug("<0x"); |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("%08x", (int)addr); |
|
|
else |
|
|
debug("%016llx", (long long)addr); |
|
|
debug("("); |
|
|
} |
|
|
|
|
|
/* |
|
|
* TODO: The number of arguments and the symbol type of each |
|
|
* argument should be taken from the symbol table, in some way. |
|
|
*/ |
|
|
n_args_to_print = 5; |
|
|
|
|
|
for (x=0; x<n_args_to_print; x++) { |
|
|
int64_t d = cpu->cd.ppc.gpr[x + 3]; |
|
|
|
|
|
if (d > -256 && d < 256) |
|
|
debug("%i", (int)d); |
|
|
else if (memory_points_to_string(cpu, cpu->mem, d, 1)) { |
|
|
debug("\"%s\"", memory_conv_to_string(cpu, |
|
|
cpu->mem, d, strbuf, sizeof(strbuf))); |
|
|
if (strlen(strbuf) >= sizeof(strbuf)-1) |
|
|
debug(".."); |
|
|
} else { |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("0x%x", (int)d); |
|
|
else |
|
|
debug("0x%llx", (long long)d); |
|
|
} |
|
|
|
|
|
if (x < n_args_to_print - 1) |
|
|
debug(","); |
|
|
|
|
|
if (x == n_args_to_print - 1) |
|
|
break; |
|
|
} |
|
|
|
|
|
if (n_args_to_print > 9) |
|
|
debug(".."); |
|
|
|
|
|
debug(")>\n"); |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
1239 |
* update_cr0(): |
* update_cr0(): |
1240 |
* |
* |
1241 |
* Sets the top 4 bits of the CR register. |
* Sets the top 4 bits of the CR register. |
1242 |
*/ |
*/ |
1243 |
static void update_cr0(struct cpu *cpu, uint64_t value) |
void update_cr0(struct cpu *cpu, uint64_t value) |
1244 |
{ |
{ |
1245 |
int c; |
int c; |
1246 |
|
|
1268 |
} |
} |
1269 |
|
|
1270 |
|
|
1271 |
/* |
#include "tmp_ppc_tail.c" |
|
* ppc_cpu_run_instr(): |
|
|
* |
|
|
* Execute one instruction on a specific CPU. |
|
|
* |
|
|
* Return value is the number of instructions executed during this call, |
|
|
* 0 if no instruction was executed. |
|
|
*/ |
|
|
int ppc_cpu_run_instr(struct emul *emul, struct cpu *cpu) |
|
|
{ |
|
|
uint32_t iword; |
|
|
unsigned char buf[4]; |
|
|
unsigned char tmp_data[8]; |
|
|
size_t tmp_data_len; |
|
|
char *mnem = NULL; |
|
|
int r, hi6, rt, rs, ra, rb, xo, lev, sh, me, rc, imm, l_bit, oe_bit; |
|
|
int c, i, spr, aa_bit, bo, bi, bh, lk_bit, bf, ctr_ok, cond_ok; |
|
|
int update, load, mb, nb, bt, ba, bb, fpreg, arithflag, old_ca, bfa; |
|
|
uint64_t tmp=0, tmp2, addr; |
|
|
uint64_t cached_pc; |
|
|
|
|
|
cached_pc = cpu->cd.ppc.pc_last = cpu->pc & ~3; |
|
|
|
|
|
/* Check PC against breakpoints: */ |
|
|
if (!single_step) |
|
|
for (i=0; i<cpu->machine->n_breakpoints; i++) |
|
|
if (cached_pc == cpu->machine->breakpoint_addr[i]) { |
|
|
fatal("Breakpoint reached, pc=0x"); |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
fatal("%08x", (int)cached_pc); |
|
|
else |
|
|
fatal("%016llx", (long long)cached_pc); |
|
|
fatal("\n"); |
|
|
single_step = 1; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Update the Time Base and Decrementer: */ |
|
|
if ((++ cpu->cd.ppc.tbl) == 0) |
|
|
cpu->cd.ppc.tbu ++; |
|
|
|
|
|
cpu->cd.ppc.dec --; |
|
|
/* TODO: dec interrupt! */ |
|
|
|
|
|
/* TODO: hdec for POWER4+ */ |
|
|
|
|
|
/* ROM emulation: (TODO: non-OF-emuls) */ |
|
|
if (cpu->pc == cpu->cd.ppc.of_emul_addr && |
|
|
cpu->machine->prom_emulation) { |
|
|
int res = of_emul(cpu); |
|
|
if (res) { |
|
|
cpu->pc = cpu->cd.ppc.lr; |
|
|
} |
|
|
return 100; |
|
|
} |
|
|
|
|
|
r = cpu->memory_rw(cpu, cpu->mem, cached_pc, &buf[0], sizeof(buf), |
|
|
MEM_READ, CACHE_INSTRUCTION | PHYSICAL); |
|
|
if (!r) |
|
|
return 0; |
|
|
|
|
|
iword = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; |
|
|
|
|
|
if (cpu->machine->instruction_trace) |
|
|
ppc_cpu_disassemble_instr(cpu, buf, 1, 0, 0); |
|
|
|
|
|
cpu->pc += sizeof(iword); |
|
|
cached_pc += sizeof(iword); |
|
|
|
|
|
hi6 = iword >> 26; |
|
|
|
|
|
switch (hi6) { |
|
|
|
|
|
case PPC_HI6_MULLI: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
cpu->cd.ppc.gpr[rt] = (int64_t)cpu->cd.ppc.gpr[ra] |
|
|
* (int64_t)imm; |
|
|
break; |
|
|
|
|
|
case PPC_HI6_SUBFIC: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
cpu->cd.ppc.xer &= ~PPC_XER_CA; |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
tmp = (~cpu->cd.ppc.gpr[ra]) & 0xffffffff; |
|
|
cpu->cd.ppc.gpr[rt] = tmp + imm + 1; |
|
|
/* TODO: is this CA correct? */ |
|
|
/* printf("subfic: tmp = %016llx\n", (long long)tmp); |
|
|
printf("subfic: rt = %016llx\n\n", |
|
|
(long long)cpu->cd.ppc.gpr[rt]); */ |
|
|
if ((tmp >> 32) != (cpu->cd.ppc.gpr[rt] >> 32)) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
/* High 32 bits are probably undefined in |
|
|
32-bit mode (I hope) */ |
|
|
} else { |
|
|
/* |
|
|
* Ugly, but I can't figure out a way right now how |
|
|
* to get the carry bit out of a 64-bit addition, |
|
|
* without access to more-than-64-bit operations in C. |
|
|
*/ |
|
|
tmp = ~cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = (tmp >> 32); /* High 32 bits */ |
|
|
tmp &= 0xffffffff; /* Low 32 bits */ |
|
|
|
|
|
tmp += imm + 1; |
|
|
if ((tmp >> 32) == 0) { |
|
|
/* No change to upper 32 bits */ |
|
|
} else if ((tmp >> 32) == 1) { |
|
|
/* Positive change: */ |
|
|
tmp2 ++; |
|
|
} else { |
|
|
/* Negative change: */ |
|
|
tmp2 --; |
|
|
} |
|
|
|
|
|
tmp &= 0xffffffff; |
|
|
|
|
|
/* TODO: is this CA calculation correct? */ |
|
|
if ((tmp2 >> 32) != 0) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
|
|
|
cpu->cd.ppc.gpr[rt] = (tmp2 << 32) + tmp; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_HI6_CMPLI: |
|
|
case PPC_HI6_CMPI: |
|
|
bf = (iword >> 23) & 7; |
|
|
l_bit = (iword >> 21) & 1; |
|
|
ra = (iword >> 16) & 31; |
|
|
if (hi6 == PPC_HI6_CMPLI) |
|
|
imm = iword & 0xffff; |
|
|
else |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
tmp = cpu->cd.ppc.gpr[ra]; |
|
|
|
|
|
if (hi6 == PPC_HI6_CMPI) { |
|
|
if (!l_bit) |
|
|
tmp = (int64_t)(int32_t)tmp; |
|
|
if ((int64_t)tmp < (int64_t)imm) |
|
|
c = 8; |
|
|
else if ((int64_t)tmp > (int64_t)imm) |
|
|
c = 4; |
|
|
else |
|
|
c = 2; |
|
|
} else { |
|
|
if (!l_bit) |
|
|
tmp &= 0xffffffff; |
|
|
if ((uint64_t)tmp < (uint64_t)imm) |
|
|
c = 8; |
|
|
else if ((uint64_t)tmp > (uint64_t)imm) |
|
|
c = 4; |
|
|
else |
|
|
c = 2; |
|
|
} |
|
|
|
|
|
/* SO bit, copied from XER: */ |
|
|
c |= ((cpu->cd.ppc.xer >> 31) & 1); |
|
|
|
|
|
cpu->cd.ppc.cr &= ~(0xf << (28 - 4*bf)); |
|
|
cpu->cd.ppc.cr |= (c << (28 - 4*bf)); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_ADDIC: |
|
|
case PPC_HI6_ADDIC_DOT: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rc = hi6 == PPC_HI6_ADDIC_DOT; |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
/* NOTE: Addic doesn't clear CA! */ |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
tmp = cpu->cd.ppc.gpr[ra] & 0xffffffff; |
|
|
cpu->cd.ppc.gpr[rt] = tmp + (uint32_t)imm; |
|
|
/* TODO: is this CA correct? */ |
|
|
/* printf("addic: tmp = %016llx\n", (long long)tmp); |
|
|
printf("addic: rt = %016llx\n\n", |
|
|
(long long)cpu->cd.ppc.gpr[rt]); */ |
|
|
if ((tmp >> 32) != (cpu->cd.ppc.gpr[rt] >> 32)) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
/* High 32 bits are probably undefined in |
|
|
32-bit mode (I hope) */ |
|
|
} else { |
|
|
/* See comment about ugliness regarding SUBFIC */ |
|
|
tmp = cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = (tmp >> 32); /* High 32 bits */ |
|
|
tmp &= 0xffffffff; /* Low 32 bits */ |
|
|
|
|
|
tmp += (int64_t)imm; |
|
|
if ((tmp >> 32) == 0) { |
|
|
/* No change to upper 32 bits */ |
|
|
} else if ((tmp >> 32) == 1) { |
|
|
/* Positive change: */ |
|
|
tmp2 ++; |
|
|
} else { |
|
|
/* Negative change: */ |
|
|
tmp2 --; |
|
|
} |
|
|
|
|
|
tmp &= 0xffffffff; |
|
|
|
|
|
/* TODO: is this CA calculation correct? */ |
|
|
if ((tmp2 >> 32) != 0) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
|
|
|
cpu->cd.ppc.gpr[rt] = (tmp2 << 32) + tmp; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[rt]); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_ADDI: |
|
|
case PPC_HI6_ADDIS: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
if (hi6 == PPC_HI6_ADDI) |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
else |
|
|
imm = (int32_t)((iword & 0xffff) << 16); |
|
|
if (ra == 0) |
|
|
tmp = 0; |
|
|
else |
|
|
tmp = cpu->cd.ppc.gpr[ra]; |
|
|
cpu->cd.ppc.gpr[rt] = tmp + imm; |
|
|
break; |
|
|
|
|
|
case PPC_HI6_BC: |
|
|
aa_bit = (iword >> 1) & 1; |
|
|
lk_bit = iword & 1; |
|
|
bo = (iword >> 21) & 31; |
|
|
bi = (iword >> 16) & 31; |
|
|
/* Sign-extend addr: */ |
|
|
addr = (int64_t)(int16_t)(iword & 0xfffc); |
|
|
|
|
|
if (!aa_bit) |
|
|
addr += cpu->cd.ppc.pc_last; |
|
|
|
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
addr &= 0xffffffff; |
|
|
|
|
|
if (!(bo & 4)) |
|
|
cpu->cd.ppc.ctr --; |
|
|
ctr_ok = (bo >> 2) & 1; |
|
|
tmp = cpu->cd.ppc.ctr; |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
tmp &= 0xffffffff; |
|
|
ctr_ok |= ( (tmp != 0) ^ ((bo >> 1) & 1) ); |
|
|
|
|
|
cond_ok = (bo >> 4) & 1; |
|
|
cond_ok |= ( ((bo >> 3) & 1) == |
|
|
((cpu->cd.ppc.cr >> (31-bi)) & 1) ); |
|
|
|
|
|
if (lk_bit) |
|
|
cpu->cd.ppc.lr = cpu->pc; |
|
|
if (ctr_ok && cond_ok) |
|
|
cpu->pc = addr & ~3; |
|
|
if (lk_bit && cpu->machine->show_trace_tree) |
|
|
show_trace(cpu); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_SC: |
|
|
lev = (iword >> 5) & 0x7f; |
|
|
if (cpu->machine->userland_emul != NULL) { |
|
|
useremul_syscall(cpu, lev); |
|
|
} else { |
|
|
fatal("[ PPC: pc = 0x%016llx, sc not yet " |
|
|
"implemented ]\n", (long long)cached_pc); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_HI6_B: |
|
|
aa_bit = (iword & 2) >> 1; |
|
|
lk_bit = iword & 1; |
|
|
/* Sign-extend addr: */ |
|
|
addr = (int64_t)(int32_t)((iword & 0x03fffffc) << 6); |
|
|
addr = (int64_t)addr >> 6; |
|
|
|
|
|
if (!aa_bit) |
|
|
addr += cpu->cd.ppc.pc_last; |
|
|
|
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
addr &= 0xffffffff; |
|
|
|
|
|
if (lk_bit) |
|
|
cpu->cd.ppc.lr = cpu->pc; |
|
|
|
|
|
cpu->pc = addr; |
|
|
|
|
|
if (lk_bit && cpu->machine->show_trace_tree) |
|
|
show_trace(cpu); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_19: |
|
|
xo = (iword >> 1) & 1023; |
|
|
switch (xo) { |
|
|
|
|
|
case PPC_19_MCRF: |
|
|
bf = (iword >> 23) & 7; |
|
|
bfa = (iword >> 18) & 7; |
|
|
tmp = cpu->cd.ppc.cr >> (28 - bfa*4); |
|
|
tmp &= 0xf; |
|
|
cpu->cd.ppc.cr &= ~(0xf << (28 - bf*4)); |
|
|
cpu->cd.ppc.cr |= (tmp << (28 - bf*4)); |
|
|
break; |
|
|
|
|
|
case PPC_19_BCLR: |
|
|
case PPC_19_BCCTR: |
|
|
bo = (iword >> 21) & 31; |
|
|
bi = (iword >> 16) & 31; |
|
|
bh = (iword >> 11) & 3; |
|
|
lk_bit = iword & 1; |
|
|
if (xo == PPC_19_BCLR) { |
|
|
addr = cpu->cd.ppc.lr; |
|
|
if (!(bo & 4)) |
|
|
cpu->cd.ppc.ctr --; |
|
|
ctr_ok = (bo >> 2) & 1; |
|
|
tmp = cpu->cd.ppc.ctr; |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
tmp &= 0xffffffff; |
|
|
ctr_ok |= ( (tmp != 0) ^ ((bo >> 1) & 1) ); |
|
|
if (!quiet_mode && !lk_bit && |
|
|
cpu->machine->show_trace_tree) { |
|
|
cpu->cd.ppc.trace_tree_depth --; |
|
|
/* TODO: show return value? */ |
|
|
} |
|
|
} else { |
|
|
addr = cpu->cd.ppc.ctr; |
|
|
ctr_ok = 1; |
|
|
} |
|
|
cond_ok = (bo >> 4) & 1; |
|
|
cond_ok |= ( ((bo >> 3) & 1) == |
|
|
((cpu->cd.ppc.cr >> (31-bi)) & 1) ); |
|
|
if (lk_bit) |
|
|
cpu->cd.ppc.lr = cpu->pc; |
|
|
if (ctr_ok && cond_ok) { |
|
|
cpu->pc = addr & ~3; |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
cpu->pc &= 0xffffffff; |
|
|
} |
|
|
if (lk_bit && cpu->machine->show_trace_tree) |
|
|
show_trace(cpu); |
|
|
break; |
|
|
|
|
|
case PPC_19_ISYNC: |
|
|
/* TODO: actually sync */ |
|
|
break; |
|
|
|
|
|
case PPC_19_CRAND: |
|
|
case PPC_19_CRXOR: |
|
|
case PPC_19_CROR: |
|
|
case PPC_19_CRNAND: |
|
|
case PPC_19_CRNOR: |
|
|
case PPC_19_CRANDC: |
|
|
case PPC_19_CREQV: |
|
|
case PPC_19_CRORC: |
|
|
bt = (iword >> 21) & 31; |
|
|
ba = (iword >> 16) & 31; |
|
|
bb = (iword >> 11) & 31; |
|
|
ba = (cpu->cd.ppc.cr >> (31-ba)) & 1; |
|
|
bb = (cpu->cd.ppc.cr >> (31-bb)) & 1; |
|
|
cpu->cd.ppc.cr &= ~(1 << (31-bt)); |
|
|
switch (xo) { |
|
|
case PPC_19_CRXOR: |
|
|
if (ba ^ bb) |
|
|
cpu->cd.ppc.cr |= (1 << (31-bt)); |
|
|
break; |
|
|
case PPC_19_CROR: |
|
|
if (ba | bb) |
|
|
cpu->cd.ppc.cr |= (1 << (31-bt)); |
|
|
break; |
|
|
default: |
|
|
fatal("[ TODO: crXXX, xo = %i, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
xo, (long long) (cpu->cd.ppc.pc_last)); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
default: |
|
|
fatal("[ unimplemented PPC hi6_19, xo = 0x%04x, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
xo, (long long) (cpu->cd.ppc.pc_last)); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_HI6_RLWIMI: |
|
|
case PPC_HI6_RLWINM: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
sh = (iword >> 11) & 31; |
|
|
mb = (iword >> 6) & 31; |
|
|
me = (iword >> 1) & 31; |
|
|
rc = iword & 1; |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
/* TODO: Fix this, its performance is awful: */ |
|
|
while (sh-- != 0) { |
|
|
int b = (tmp >> 31) & 1; |
|
|
tmp = (tmp << 1) | b; |
|
|
} |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_RLWIMI: |
|
|
for (;;) { |
|
|
uint64_t mask; |
|
|
mask = (uint64_t)1 << (31-mb); |
|
|
cpu->cd.ppc.gpr[ra] &= ~mask; |
|
|
cpu->cd.ppc.gpr[ra] |= (tmp & mask); |
|
|
if (mb == me) |
|
|
break; |
|
|
mb ++; |
|
|
if (mb == 32) |
|
|
mb = 0; |
|
|
} |
|
|
break; |
|
|
case PPC_HI6_RLWINM: |
|
|
cpu->cd.ppc.gpr[ra] = 0; |
|
|
for (;;) { |
|
|
uint64_t mask; |
|
|
mask = (uint64_t)1 << (31-mb); |
|
|
cpu->cd.ppc.gpr[ra] |= (tmp & mask); |
|
|
if (mb == me) |
|
|
break; |
|
|
mb ++; |
|
|
if (mb == 32) |
|
|
mb = 0; |
|
|
} |
|
|
break; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_ORI: |
|
|
case PPC_HI6_ORIS: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
if (hi6 == PPC_HI6_ORI) |
|
|
imm = (iword & 0xffff); |
|
|
else |
|
|
imm = (iword & 0xffff) << 16; |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
cpu->cd.ppc.gpr[ra] = tmp | (uint32_t)imm; |
|
|
break; |
|
|
|
|
|
case PPC_HI6_XORI: |
|
|
case PPC_HI6_XORIS: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
if (hi6 == PPC_HI6_XORI) |
|
|
imm = (iword & 0xffff); |
|
|
else |
|
|
imm = (iword & 0xffff) << 16; |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
cpu->cd.ppc.gpr[ra] = tmp ^ (uint32_t)imm; |
|
|
break; |
|
|
|
|
|
case PPC_HI6_ANDI_DOT: |
|
|
case PPC_HI6_ANDIS_DOT: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
if (hi6 == PPC_HI6_ANDI_DOT) |
|
|
imm = (iword & 0xffff); |
|
|
else |
|
|
imm = (iword & 0xffff) << 16; |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
cpu->cd.ppc.gpr[ra] = tmp & (uint32_t)imm; |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
case PPC_HI6_30: |
|
|
xo = (iword >> 2) & 7; |
|
|
switch (xo) { |
|
|
case PPC_30_RLDICR: |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
/* TODO: Illegal instruction. */ |
|
|
break; |
|
|
} |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
sh = ((iword >> 11) & 31) | ((iword & 2) << 4); |
|
|
me = ((iword >> 6) & 31) | (iword & 0x20); |
|
|
rc = iword & 1; |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
/* TODO: Fix this, its performance is awful: */ |
|
|
while (sh-- != 0) { |
|
|
int b = (tmp >> 63) & 1; |
|
|
tmp = (tmp << 1) | b; |
|
|
} |
|
|
while (me++ < 63) |
|
|
tmp &= ~((uint64_t)1 << (63-me)); |
|
|
cpu->cd.ppc.gpr[ra] = tmp; |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
default: |
|
|
fatal("[ unimplemented PPC hi6_30, xo = 0x%04x, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
xo, (long long) (cpu->cd.ppc.pc_last)); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_HI6_31: |
|
|
xo = (iword >> 1) & 1023; |
|
|
switch (xo) { |
|
|
|
|
|
case PPC_31_CMPL: |
|
|
case PPC_31_CMP: |
|
|
bf = (iword >> 23) & 7; |
|
|
l_bit = (iword >> 21) & 1; |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
|
|
|
tmp = cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = cpu->cd.ppc.gpr[rb]; |
|
|
|
|
|
if (hi6 == PPC_31_CMP) { |
|
|
if (!l_bit) { |
|
|
tmp = (int64_t)(int32_t)tmp; |
|
|
tmp2 = (int64_t)(int32_t)tmp2; |
|
|
} |
|
|
if ((int64_t)tmp < (int64_t)tmp2) |
|
|
c = 8; |
|
|
else if ((int64_t)tmp > (int64_t)tmp2) |
|
|
c = 4; |
|
|
else |
|
|
c = 2; |
|
|
} else { |
|
|
if (!l_bit) { |
|
|
tmp &= 0xffffffff; |
|
|
tmp2 &= 0xffffffff; |
|
|
} |
|
|
if ((uint64_t)tmp < (uint64_t)tmp2) |
|
|
c = 8; |
|
|
else if ((uint64_t)tmp > (uint64_t)tmp2) |
|
|
c = 4; |
|
|
else |
|
|
c = 2; |
|
|
} |
|
|
|
|
|
/* SO bit, copied from XER: */ |
|
|
c |= ((cpu->cd.ppc.xer >> 31) & 1); |
|
|
|
|
|
cpu->cd.ppc.cr &= ~(0xf << (28 - 4*bf)); |
|
|
cpu->cd.ppc.cr |= (c << (28 - 4*bf)); |
|
|
break; |
|
|
|
|
|
case PPC_31_MFCR: |
|
|
rt = (iword >> 21) & 31; |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.cr; |
|
|
break; |
|
|
|
|
|
case PPC_31_DCBST: |
|
|
case PPC_31_ICBI: |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
switch (xo) { |
|
|
case PPC_31_DCBST: mnem = "dcbst"; break; |
|
|
case PPC_31_ICBI: mnem = "icbi"; break; |
|
|
} |
|
|
/* debug("[ %s r%i,r%i: TODO ]\n", mnem, ra, rb); */ |
|
|
break; |
|
|
|
|
|
case PPC_31_MFMSR: |
|
|
rt = (iword >> 21) & 31; |
|
|
/* TODO: check pr */ |
|
|
reg_access_msr(cpu, &cpu->cd.ppc.gpr[rt], 0); |
|
|
break; |
|
|
|
|
|
case PPC_31_MTCRF: |
|
|
rs = (iword >> 21) & 31; |
|
|
mb = (iword >> 12) & 255; /* actually fxm, not mb */ |
|
|
tmp = 0; |
|
|
for (i=0; i<8; i++, mb <<= 1, tmp <<= 4) |
|
|
if (mb & 128) |
|
|
tmp |= 0xf; |
|
|
cpu->cd.ppc.cr &= ~tmp; |
|
|
cpu->cd.ppc.cr |= (cpu->cd.ppc.gpr[rs] & tmp); |
|
|
break; |
|
|
|
|
|
case PPC_31_MTMSR: |
|
|
rs = (iword >> 21) & 31; |
|
|
l_bit = (iword >> 16) & 1; |
|
|
/* TODO: the l_bit */ |
|
|
reg_access_msr(cpu, &cpu->cd.ppc.gpr[rs], 1); |
|
|
break; |
|
|
|
|
|
case PPC_31_LBZX: |
|
|
case PPC_31_LBZUX: |
|
|
case PPC_31_LHZX: |
|
|
case PPC_31_LHZUX: |
|
|
case PPC_31_LWZX: |
|
|
case PPC_31_LWZUX: |
|
|
case PPC_31_STBX: |
|
|
case PPC_31_STBUX: |
|
|
case PPC_31_STHX: |
|
|
case PPC_31_STHUX: |
|
|
case PPC_31_STWX: |
|
|
case PPC_31_STWUX: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
update = 0; |
|
|
switch (xo) { |
|
|
case PPC_31_LBZUX: |
|
|
case PPC_31_LHZUX: |
|
|
case PPC_31_LWZUX: |
|
|
case PPC_31_STBUX: |
|
|
case PPC_31_STHUX: |
|
|
case PPC_31_STWUX: |
|
|
update = 1; |
|
|
} |
|
|
if (ra == 0) |
|
|
addr = 0; |
|
|
else |
|
|
addr = cpu->cd.ppc.gpr[ra]; |
|
|
addr += cpu->cd.ppc.gpr[rb]; |
|
|
load = 0; |
|
|
switch (xo) { |
|
|
case PPC_31_LBZX: |
|
|
case PPC_31_LBZUX: |
|
|
case PPC_31_LHZX: |
|
|
case PPC_31_LHZUX: |
|
|
case PPC_31_LWZX: |
|
|
case PPC_31_LWZUX: |
|
|
load = 1; |
|
|
} |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("\t[0x%08llx", (long long)addr); |
|
|
else |
|
|
debug("\t[0x%016llx", (long long)addr); |
|
|
} |
|
|
|
|
|
tmp_data_len = 4; |
|
|
switch (xo) { |
|
|
case PPC_31_LBZX: |
|
|
case PPC_31_LBZUX: |
|
|
case PPC_31_STBX: |
|
|
case PPC_31_STBUX: |
|
|
tmp_data_len = 1; |
|
|
break; |
|
|
case PPC_31_LHZX: |
|
|
case PPC_31_LHZUX: |
|
|
case PPC_31_STHX: |
|
|
case PPC_31_STHUX: |
|
|
tmp_data_len = 2; |
|
|
break; |
|
|
} |
|
|
|
|
|
tmp = 0; |
|
|
|
|
|
if (load) { |
|
|
r = cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, tmp_data_len, MEM_READ, |
|
|
CACHE_DATA); |
|
|
if (r == MEMORY_ACCESS_OK) { |
|
|
if (cpu->byte_order == |
|
|
EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[i]; |
|
|
} |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[ |
|
|
tmp_data_len - 1 |
|
|
- i]; |
|
|
} |
|
|
} |
|
|
cpu->cd.ppc.gpr[rs] = tmp; |
|
|
} |
|
|
} else { |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
if (cpu->byte_order == EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[tmp_data_len-1-i] = |
|
|
tmp >> (8*i); |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[i] = tmp >> (8*i); |
|
|
} |
|
|
|
|
|
r = cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, tmp_data_len, MEM_WRITE, |
|
|
CACHE_DATA); |
|
|
} |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (r == MEMORY_ACCESS_OK) { |
|
|
switch (tmp_data_len) { |
|
|
case 1: debug(", data = 0x%02x]\n", |
|
|
(int)tmp); |
|
|
break; |
|
|
case 2: debug(", data = 0x%04x]\n", |
|
|
(int)tmp); |
|
|
break; |
|
|
case 4: debug(", data = 0x%08x]\n", |
|
|
(int)tmp); |
|
|
break; |
|
|
default:debug(", data = 0x%016llx]\n", |
|
|
(long long)tmp); |
|
|
} |
|
|
} else |
|
|
debug(", FAILED]\n"); |
|
|
} |
|
|
|
|
|
if (r != MEMORY_ACCESS_OK) { |
|
|
/* TODO: exception? */ |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (update && ra != 0) |
|
|
cpu->cd.ppc.gpr[ra] = addr; |
|
|
break; |
|
|
|
|
|
case PPC_31_NEG: |
|
|
case PPC_31_NEGO: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
oe_bit = (iword >> 10) & 1; |
|
|
rc = iword & 1; |
|
|
if (oe_bit) { |
|
|
fatal("[ neg: PPC oe not yet implemeted ]\n"); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
cpu->cd.ppc.gpr[rt] = ~cpu->cd.ppc.gpr[ra] + 1; |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[rt]); |
|
|
break; |
|
|
|
|
|
case PPC_31_ADDZE: |
|
|
case PPC_31_ADDZEO: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
oe_bit = (iword >> 10) & 1; |
|
|
rc = iword & 1; |
|
|
if (oe_bit) { |
|
|
fatal("[ addz: PPC oe not yet implemeted ]\n"); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
old_ca = cpu->cd.ppc.xer & PPC_XER_CA; |
|
|
cpu->cd.ppc.xer &= PPC_XER_CA; |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
tmp = (uint32_t)cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = tmp; |
|
|
/* printf("addze: tmp2 = %016llx\n", |
|
|
(long long)tmp2); */ |
|
|
if (old_ca) |
|
|
tmp ++; |
|
|
/* printf("addze: tmp = %016llx\n\n", |
|
|
(long long)tmp); */ |
|
|
/* TODO: is this CA correct? */ |
|
|
if ((tmp >> 32) != (tmp2 >> 32)) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
/* High 32 bits are probably undefined |
|
|
in 32-bit mode (I hope) */ |
|
|
cpu->cd.ppc.gpr[rt] = tmp; |
|
|
} else { |
|
|
fatal("ADDZE 64-bit, TODO\n"); |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[rt]); |
|
|
break; |
|
|
|
|
|
case PPC_31_MTSR: |
|
|
/* Move to segment register (?) */ |
|
|
/* TODO */ |
|
|
break; |
|
|
|
|
|
case PPC_31_MFSRIN: |
|
|
case PPC_31_MTSRIN: |
|
|
/* mfsrin: Move to segment register indirect (?) */ |
|
|
/* mtsrin: Move to segment register indirect (?) */ |
|
|
rt = (iword >> 21) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
|
|
|
/* TODO */ |
|
|
|
|
|
if (xo == PPC_31_MFSRIN) |
|
|
cpu->cd.ppc.gpr[rt] = 0; |
|
|
break; |
|
|
|
|
|
case PPC_31_ADDC: |
|
|
case PPC_31_ADDCO: |
|
|
case PPC_31_ADDE: |
|
|
case PPC_31_ADDEO: |
|
|
case PPC_31_ADD: |
|
|
case PPC_31_ADDO: |
|
|
case PPC_31_MULHW: |
|
|
case PPC_31_MULHWU: |
|
|
case PPC_31_MULLW: |
|
|
case PPC_31_MULLWO: |
|
|
case PPC_31_SUBFE: |
|
|
case PPC_31_SUBFEO: |
|
|
case PPC_31_SUBFZE: |
|
|
case PPC_31_SUBFZEO: |
|
|
case PPC_31_SUBFC: |
|
|
case PPC_31_SUBFCO: |
|
|
case PPC_31_SUBF: |
|
|
case PPC_31_SUBFO: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
oe_bit = (iword >> 10) & 1; |
|
|
rc = iword & 1; |
|
|
if (oe_bit) { |
|
|
fatal("[ add: PPC oe not yet implemeted ]\n"); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
switch (xo) { |
|
|
case PPC_31_ADD: |
|
|
case PPC_31_ADDO: |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.gpr[ra] + |
|
|
cpu->cd.ppc.gpr[rb]; |
|
|
break; |
|
|
case PPC_31_ADDC: |
|
|
case PPC_31_ADDCO: |
|
|
case PPC_31_ADDE: |
|
|
case PPC_31_ADDEO: |
|
|
old_ca = cpu->cd.ppc.xer & PPC_XER_CA; |
|
|
cpu->cd.ppc.xer &= PPC_XER_CA; |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
tmp = (uint32_t)cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = tmp; |
|
|
/* printf("adde: tmp2 = %016llx\n", |
|
|
(long long)tmp2); */ |
|
|
tmp += (uint32_t)cpu->cd.ppc.gpr[rb]; |
|
|
if ((xo == PPC_31_ADDE || |
|
|
xo == PPC_31_ADDEO) && old_ca) |
|
|
tmp ++; |
|
|
/* printf("adde: tmp = %016llx\n\n", |
|
|
(long long)tmp); */ |
|
|
/* TODO: is this CA correct? */ |
|
|
if ((tmp >> 32) != (tmp2 >> 32)) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
/* High 32 bits are probably undefined |
|
|
in 32-bit mode (I hope) */ |
|
|
cpu->cd.ppc.gpr[rt] = tmp; |
|
|
} else { |
|
|
fatal("ADDE 64-bit, TODO\n"); |
|
|
} |
|
|
break; |
|
|
case PPC_31_MULHW: |
|
|
cpu->cd.ppc.gpr[rt] = (int64_t) ( |
|
|
(int64_t)(int32_t)cpu->cd.ppc.gpr[ra] * |
|
|
(int64_t)(int32_t)cpu->cd.ppc.gpr[rb]); |
|
|
cpu->cd.ppc.gpr[rt] >>= 32; |
|
|
break; |
|
|
case PPC_31_MULHWU: |
|
|
cpu->cd.ppc.gpr[rt] = (uint64_t) ( |
|
|
(uint64_t)(uint32_t)cpu->cd.ppc.gpr[ra] * |
|
|
(uint64_t)(uint32_t)cpu->cd.ppc.gpr[rb]); |
|
|
cpu->cd.ppc.gpr[rt] >>= 32; |
|
|
break; |
|
|
case PPC_31_MULLW: |
|
|
case PPC_31_MULLWO: |
|
|
cpu->cd.ppc.gpr[rt] = (int64_t) ( |
|
|
(int32_t)cpu->cd.ppc.gpr[ra] * |
|
|
(int32_t)cpu->cd.ppc.gpr[rb]); |
|
|
break; |
|
|
case PPC_31_SUBF: |
|
|
case PPC_31_SUBFO: |
|
|
cpu->cd.ppc.gpr[rt] = ~cpu->cd.ppc.gpr[ra] + |
|
|
cpu->cd.ppc.gpr[rb] + 1; |
|
|
break; |
|
|
case PPC_31_SUBFC: |
|
|
case PPC_31_SUBFCO: |
|
|
case PPC_31_SUBFE: |
|
|
case PPC_31_SUBFEO: |
|
|
case PPC_31_SUBFZE: |
|
|
case PPC_31_SUBFZEO: |
|
|
old_ca = cpu->cd.ppc.xer & PPC_XER_CA; |
|
|
if (xo == PPC_31_SUBFC || xo == PPC_31_SUBFCO) |
|
|
old_ca = 1; |
|
|
cpu->cd.ppc.xer &= PPC_XER_CA; |
|
|
if (cpu->cd.ppc.bits == 32) { |
|
|
tmp = (~cpu->cd.ppc.gpr[ra]) |
|
|
& 0xffffffff; |
|
|
tmp2 = tmp; |
|
|
if (xo != PPC_31_SUBFZE && |
|
|
xo != PPC_31_SUBFZEO) |
|
|
tmp += (cpu->cd.ppc.gpr[rb] & |
|
|
0xffffffff); |
|
|
if (old_ca) |
|
|
tmp ++; |
|
|
/* printf("subfe: tmp2 = %016llx\n", |
|
|
(long long)tmp2); |
|
|
printf("subfe: tmp = %016llx\n\n", |
|
|
(long long)tmp); */ |
|
|
/* TODO: is this CA correct? */ |
|
|
if ((tmp >> 32) != (tmp2 >> 32)) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
/* High 32 bits are probably undefined |
|
|
in 32-bit mode (I hope) */ |
|
|
cpu->cd.ppc.gpr[rt] = tmp; |
|
|
} else { |
|
|
fatal("SUBFE 64-bit, TODO\n"); |
|
|
} |
|
|
break; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[rt]); |
|
|
break; |
|
|
|
|
|
case PPC_31_MFSPR: |
|
|
case PPC_31_MFTB: |
|
|
rt = (iword >> 21) & 31; |
|
|
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
|
|
switch (spr) { |
|
|
case 1: cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.xer; |
|
|
break; |
|
|
case 8: cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.lr; |
|
|
break; |
|
|
case 9: cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.ctr; |
|
|
break; |
|
|
case 22:/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.dec; |
|
|
break; |
|
|
case 259: /* NOTE: no pr check */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.sprg3; |
|
|
break; |
|
|
case 268: /* MFTB, NOTE: no pr check */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.tbl; |
|
|
break; |
|
|
case 269: /* MFTBU, NOTE: no pr check */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.tbu; |
|
|
break; |
|
|
case 272: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.sprg0; |
|
|
break; |
|
|
case 273: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.sprg1; |
|
|
break; |
|
|
case 274: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.sprg2; |
|
|
break; |
|
|
case 275: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.sprg3; |
|
|
break; |
|
|
case 287: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.pvr; |
|
|
break; |
|
|
case 310:/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.hdec; |
|
|
break; |
|
|
case 1023: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.gpr[rt] = cpu->cd.ppc.pir; |
|
|
break; |
|
|
default: |
|
|
fatal("[ unimplemented PPC spr 0x%04x, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
spr, (long long) (cpu->cd.ppc.pc_last)); |
|
|
/* cpu->running = 0; |
|
|
return 0; */ |
|
|
break; |
|
|
} |
|
|
|
|
|
/* TODO: is this correct? */ |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
cpu->cd.ppc.gpr[rt] &= 0xffffffff; |
|
|
break; |
|
|
|
|
|
case PPC_31_CNTLZW: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rc = iword & 1; |
|
|
cpu->cd.ppc.gpr[ra] = 0; |
|
|
for (i=0; i<32; i++) { |
|
|
if (cpu->cd.ppc.gpr[rs] & |
|
|
((uint64_t)1 << (31-i))) |
|
|
break; |
|
|
cpu->cd.ppc.gpr[ra] ++; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
case PPC_31_SLW: |
|
|
case PPC_31_SRAW: |
|
|
case PPC_31_SRW: |
|
|
case PPC_31_AND: |
|
|
case PPC_31_ANDC: |
|
|
case PPC_31_NOR: |
|
|
case PPC_31_OR: |
|
|
case PPC_31_ORC: |
|
|
case PPC_31_XOR: |
|
|
case PPC_31_NAND: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
rc = iword & 1; |
|
|
switch (xo) { |
|
|
case PPC_31_SLW: |
|
|
sh = cpu->cd.ppc.gpr[rb] & 0x3f; |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs]; |
|
|
while (sh-- > 0) |
|
|
cpu->cd.ppc.gpr[ra] <<= 1; |
|
|
cpu->cd.ppc.gpr[ra] &= 0xffffffff; |
|
|
break; |
|
|
case PPC_31_SRAW: |
|
|
tmp = cpu->cd.ppc.gpr[rs] & 0xffffffff; |
|
|
cpu->cd.ppc.xer &= ~PPC_XER_CA; |
|
|
i = 0; |
|
|
sh = cpu->cd.ppc.gpr[rb] & 0x3f; |
|
|
if (tmp & 0x80000000) |
|
|
i = 1; |
|
|
while (sh-- > 0) { |
|
|
if (tmp & 1) |
|
|
i++; |
|
|
tmp >>= 1; |
|
|
if (tmp & 0x40000000) |
|
|
tmp |= 0x80000000; |
|
|
} |
|
|
cpu->cd.ppc.gpr[ra] = (int64_t)(int32_t)tmp; |
|
|
/* Set the CA bit if rs contained a negative |
|
|
number to begin with, and any 1-bits were |
|
|
shifted out: */ |
|
|
if (i > 1) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
break; |
|
|
case PPC_31_SRW: |
|
|
sh = cpu->cd.ppc.gpr[rb] & 0x3f; |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] |
|
|
& 0xffffffff; |
|
|
while (sh-- > 0) |
|
|
cpu->cd.ppc.gpr[ra] >>= 1; |
|
|
break; |
|
|
case PPC_31_AND: |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] & |
|
|
cpu->cd.ppc.gpr[rb]; |
|
|
break; |
|
|
case PPC_31_ANDC: |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] & |
|
|
(~cpu->cd.ppc.gpr[rb]); |
|
|
break; |
|
|
case PPC_31_NOR: |
|
|
cpu->cd.ppc.gpr[ra] = ~(cpu->cd.ppc.gpr[rs] | |
|
|
cpu->cd.ppc.gpr[rb]); |
|
|
break; |
|
|
case PPC_31_OR: |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] | |
|
|
cpu->cd.ppc.gpr[rb]; |
|
|
break; |
|
|
case PPC_31_ORC: |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] | |
|
|
(~cpu->cd.ppc.gpr[rb]); |
|
|
break; |
|
|
case PPC_31_XOR: |
|
|
cpu->cd.ppc.gpr[ra] = cpu->cd.ppc.gpr[rs] ^ |
|
|
cpu->cd.ppc.gpr[rb]; |
|
|
break; |
|
|
case PPC_31_NAND: |
|
|
cpu->cd.ppc.gpr[ra] = ~(cpu->cd.ppc.gpr[rs] |
|
|
& cpu->cd.ppc.gpr[rb]); |
|
|
break; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
case PPC_31_TLBIE: |
|
|
rb = (iword >> 11) & 31; |
|
|
/* TODO */ |
|
|
break; |
|
|
|
|
|
case PPC_31_TLBSYNC: |
|
|
/* Only on 603 and 604 (?) */ |
|
|
|
|
|
/* TODO */ |
|
|
break; |
|
|
|
|
|
case PPC_31_DCCCI: |
|
|
case PPC_31_ICCCI: |
|
|
/* Supervisor IBM 4xx Data Cache Congruence Class |
|
|
Invalidate, see www.xilinx.com/publications/ |
|
|
xcellonline/partners/xc_pdf/xc_ibm_pwrpc42.pdf |
|
|
or similar */ |
|
|
/* ICCCI is probably Instruction... blah blah */ |
|
|
/* TODO */ |
|
|
break; |
|
|
|
|
|
case PPC_31_DIVWU: |
|
|
case PPC_31_DIVWUO: |
|
|
case PPC_31_DIVW: |
|
|
case PPC_31_DIVWO: |
|
|
rt = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rb = (iword >> 11) & 31; |
|
|
oe_bit = (iword >> 10) & 1; |
|
|
rc = iword & 1; |
|
|
switch (xo) { |
|
|
case PPC_31_DIVWU: |
|
|
case PPC_31_DIVWUO: |
|
|
tmp = cpu->cd.ppc.gpr[ra] & 0xffffffff; |
|
|
tmp2 = cpu->cd.ppc.gpr[rb] & 0xffffffff; |
|
|
if (tmp2 == 0) { |
|
|
/* Undefined: */ |
|
|
tmp = 0; |
|
|
} else { |
|
|
tmp = tmp / tmp2; |
|
|
} |
|
|
cpu->cd.ppc.gpr[rt] = (int64_t)(int32_t)tmp; |
|
|
break; |
|
|
case PPC_31_DIVW: |
|
|
case PPC_31_DIVWO: |
|
|
tmp = (int64_t)(int32_t)cpu->cd.ppc.gpr[ra]; |
|
|
tmp2 = (int64_t)(int32_t)cpu->cd.ppc.gpr[rb]; |
|
|
if (tmp2 == 0) { |
|
|
/* Undefined: */ |
|
|
tmp = 0; |
|
|
} else { |
|
|
tmp = (int64_t)tmp / (int64_t)tmp2; |
|
|
} |
|
|
cpu->cd.ppc.gpr[rt] = (int64_t)(int32_t)tmp; |
|
|
break; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[rt]); |
|
|
if (oe_bit) { |
|
|
fatal("[ divwu: PPC oe not yet implemeted ]\n"); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_31_MTSPR: |
|
|
rs = (iword >> 21) & 31; |
|
|
spr = ((iword >> 6) & 0x3e0) + ((iword >> 16) & 31); |
|
|
switch (spr) { |
|
|
case 1: cpu->cd.ppc.xer = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 8: cpu->cd.ppc.lr = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 9: cpu->cd.ppc.ctr = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 22: /* TODO: check pr */ |
|
|
cpu->cd.ppc.dec = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 272: |
|
|
/* TODO: check hypv */ |
|
|
cpu->cd.ppc.sprg0 = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 273: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.sprg1 = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 274: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.sprg2 = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 275: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.sprg3 = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 284: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.tbl = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 285: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.tbu = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 287: |
|
|
fatal("[ PPC: attempt to write to PVR ]\n"); |
|
|
break; |
|
|
case 310: /* TODO: check hypv */ |
|
|
cpu->cd.ppc.hdec = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case 1023: |
|
|
/* TODO: check pr */ |
|
|
cpu->cd.ppc.pir = cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
default: |
|
|
fatal("[ unimplemented PPC spr 0x%04x, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
spr, (long long) (cpu->cd.ppc.pc_last)); |
|
|
/* cpu->running = 0; |
|
|
return 0; */ |
|
|
break; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_31_SYNC: |
|
|
/* TODO: actually sync */ |
|
|
break; |
|
|
|
|
|
case PPC_31_LSWI: |
|
|
case PPC_31_STSWI: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
nb = (iword >> 11) & 31; |
|
|
if (nb == 0) |
|
|
nb = 32; |
|
|
if (ra == 0) |
|
|
addr = 0; |
|
|
else |
|
|
addr = cpu->cd.ppc.gpr[ra]; |
|
|
|
|
|
load = 0; |
|
|
if (xo == PPC_31_LSWI) |
|
|
load = 1; |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("\t[0x%08llx", (long long)addr); |
|
|
else |
|
|
debug("\t[0x%016llx", (long long)addr); |
|
|
} |
|
|
|
|
|
i = 24; |
|
|
r = 0; /* Error count. */ |
|
|
while (nb-- > 0) { |
|
|
if (load) { |
|
|
/* (Actually rt should be used.) */ |
|
|
if (cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, 1, MEM_READ, CACHE_DATA) |
|
|
!= MEMORY_ACCESS_OK) { |
|
|
r++; |
|
|
break; |
|
|
} |
|
|
if (i == 24) |
|
|
cpu->cd.ppc.gpr[rs] = 0; |
|
|
cpu->cd.ppc.gpr[rs] |= |
|
|
(tmp_data[0] << i); |
|
|
} else { |
|
|
tmp_data[0] = cpu->cd.ppc.gpr[rs] >> i; |
|
|
if (cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, 1, MEM_WRITE, CACHE_DATA) |
|
|
!= MEMORY_ACCESS_OK) { |
|
|
r++; |
|
|
break; |
|
|
} |
|
|
} |
|
|
addr++; i-=8; |
|
|
if (i < 0) { |
|
|
i = 24; |
|
|
rs = (rs + 1) % 32; |
|
|
} |
|
|
} |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (r == 0) |
|
|
debug(", ...]\n"); |
|
|
else |
|
|
debug(", FAILED]\n"); |
|
|
} |
|
|
|
|
|
if (r > 0) { |
|
|
/* TODO: exception */ |
|
|
fatal("TODO: exception.\n"); |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_31_SRAWI: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
sh = (iword >> 11) & 31; |
|
|
rc = iword & 1; |
|
|
tmp = cpu->cd.ppc.gpr[rs] & 0xffffffff; |
|
|
cpu->cd.ppc.xer &= ~PPC_XER_CA; |
|
|
i = 0; |
|
|
if (tmp & 0x80000000) |
|
|
i = 1; |
|
|
while (sh-- > 0) { |
|
|
if (tmp & 1) |
|
|
i++; |
|
|
tmp >>= 1; |
|
|
if (tmp & 0x40000000) |
|
|
tmp |= 0x80000000; |
|
|
} |
|
|
cpu->cd.ppc.gpr[ra] = (int64_t)(int32_t)tmp; |
|
|
/* Set the CA bit if rs contained a negative |
|
|
number to begin with, and any 1-bits were |
|
|
shifted out: */ |
|
|
if (i > 1) |
|
|
cpu->cd.ppc.xer |= PPC_XER_CA; |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
case PPC_31_EIEIO: |
|
|
/* TODO: actually eieio */ |
|
|
break; |
|
|
|
|
|
case PPC_31_EXTSB: |
|
|
case PPC_31_EXTSH: |
|
|
case PPC_31_EXTSW: |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
rc = iword & 1; |
|
|
switch (xo) { |
|
|
case PPC_31_EXTSB: |
|
|
cpu->cd.ppc.gpr[ra] = (int64_t) |
|
|
(int8_t)cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case PPC_31_EXTSH: |
|
|
cpu->cd.ppc.gpr[ra] = (int64_t) |
|
|
(int16_t)cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
case PPC_31_EXTSW: |
|
|
cpu->cd.ppc.gpr[ra] = (int64_t) |
|
|
(int32_t)cpu->cd.ppc.gpr[rs]; |
|
|
break; |
|
|
} |
|
|
if (rc) |
|
|
update_cr0(cpu, cpu->cd.ppc.gpr[ra]); |
|
|
break; |
|
|
|
|
|
default: |
|
|
fatal("[ unimplemented PPC hi6_31, xo = 0x%04x, " |
|
|
"pc = 0x%016llx ]\n", |
|
|
xo, (long long) (cpu->cd.ppc.pc_last)); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
break; |
|
|
|
|
|
case PPC_HI6_LWZ: |
|
|
case PPC_HI6_LWZU: |
|
|
case PPC_HI6_LHZ: |
|
|
case PPC_HI6_LHZU: |
|
|
case PPC_HI6_LHA: |
|
|
case PPC_HI6_LHAU: |
|
|
case PPC_HI6_LBZ: |
|
|
case PPC_HI6_LBZU: |
|
|
case PPC_HI6_STW: |
|
|
case PPC_HI6_STWU: |
|
|
case PPC_HI6_STH: |
|
|
case PPC_HI6_STHU: |
|
|
case PPC_HI6_STB: |
|
|
case PPC_HI6_STBU: |
|
|
case PPC_HI6_LFD: |
|
|
case PPC_HI6_STFD: |
|
|
/* NOTE: Loads use rt, not rs, but are otherwise similar |
|
|
to stores. This code uses rs for both. */ |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
|
|
|
fpreg = 0; load = 1; update = 0; tmp_data_len = 4; |
|
|
arithflag = 0; |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_LWZU: |
|
|
case PPC_HI6_LHZU: |
|
|
case PPC_HI6_LHAU: |
|
|
case PPC_HI6_LBZU: |
|
|
case PPC_HI6_STBU: |
|
|
case PPC_HI6_STHU: |
|
|
case PPC_HI6_STWU: |
|
|
update = 1; |
|
|
} |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_STW: |
|
|
case PPC_HI6_STWU: |
|
|
case PPC_HI6_STH: |
|
|
case PPC_HI6_STHU: |
|
|
case PPC_HI6_STB: |
|
|
case PPC_HI6_STBU: |
|
|
case PPC_HI6_STFD: |
|
|
load = 0; |
|
|
} |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_LFD: |
|
|
case PPC_HI6_STFD: |
|
|
tmp_data_len = 8; |
|
|
break; |
|
|
case PPC_HI6_LBZ: |
|
|
case PPC_HI6_LBZU: |
|
|
case PPC_HI6_STB: |
|
|
case PPC_HI6_STBU: |
|
|
tmp_data_len = 1; |
|
|
break; |
|
|
case PPC_HI6_LHZ: |
|
|
case PPC_HI6_LHZU: |
|
|
case PPC_HI6_LHA: |
|
|
case PPC_HI6_LHAU: |
|
|
case PPC_HI6_STH: |
|
|
case PPC_HI6_STHU: |
|
|
tmp_data_len = 2; |
|
|
break; |
|
|
} |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_LFD: |
|
|
case PPC_HI6_STFD: |
|
|
fpreg = 1; |
|
|
} |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_LHA: |
|
|
case PPC_HI6_LHAU: |
|
|
arithflag = 1; |
|
|
} |
|
|
|
|
|
if (ra == 0) { |
|
|
if (update) |
|
|
fatal("[ PPC WARNING: invalid Update form ]\n"); |
|
|
addr = 0; |
|
|
} else |
|
|
addr = cpu->cd.ppc.gpr[ra]; |
|
|
|
|
|
if (load && update && ra == rs) |
|
|
fatal("[ PPC WARNING: invalid Update load form ]\n"); |
|
|
|
|
|
addr += imm; |
|
|
|
|
|
/* TODO: alignment check? */ |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("\t[0x%08llx", (long long)addr); |
|
|
else |
|
|
debug("\t[0x%016llx", (long long)addr); |
|
|
} |
|
|
|
|
|
if (load) { |
|
|
r = cpu->memory_rw(cpu, cpu->mem, addr, tmp_data, |
|
|
tmp_data_len, MEM_READ, CACHE_DATA); |
|
|
|
|
|
if (r == MEMORY_ACCESS_OK) { |
|
|
tmp = 0; |
|
|
if (arithflag) { |
|
|
if (cpu->byte_order == |
|
|
EMUL_BIG_ENDIAN) { |
|
|
if (tmp_data[0] & 0x80) |
|
|
tmp --; |
|
|
} else { |
|
|
if (tmp_data[tmp_data_len-1] |
|
|
& 0x80) |
|
|
tmp --; |
|
|
} |
|
|
} |
|
|
if (cpu->byte_order == EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[i]; |
|
|
} |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[ |
|
|
tmp_data_len - 1 -i]; |
|
|
} |
|
|
} |
|
|
|
|
|
if (!fpreg) |
|
|
cpu->cd.ppc.gpr[rs] = tmp; |
|
|
else |
|
|
cpu->cd.ppc.fpr[rs] = tmp; |
|
|
} |
|
|
} else { |
|
|
if (!fpreg) |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
else |
|
|
tmp = cpu->cd.ppc.fpr[rs]; |
|
|
|
|
|
if (cpu->byte_order == EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[tmp_data_len-1-i] = |
|
|
tmp >> (8*i); |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[i] = tmp >> (8*i); |
|
|
} |
|
|
|
|
|
r = cpu->memory_rw(cpu, cpu->mem, addr, tmp_data, |
|
|
tmp_data_len, MEM_WRITE, CACHE_DATA); |
|
|
} |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (r == MEMORY_ACCESS_OK) { |
|
|
switch (tmp_data_len) { |
|
|
case 1: debug(", data = 0x%02x]\n", (int)tmp); |
|
|
break; |
|
|
case 2: debug(", data = 0x%04x]\n", (int)tmp); |
|
|
break; |
|
|
case 4: debug(", data = 0x%08x]\n", (int)tmp); |
|
|
break; |
|
|
default:debug(", data = 0x%016llx]\n", |
|
|
(long long)tmp); |
|
|
} |
|
|
} else |
|
|
debug(", FAILED]\n"); |
|
|
} |
|
|
|
|
|
if (r != MEMORY_ACCESS_OK) { |
|
|
/* TODO: exception? */ |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (update && ra != 0) |
|
|
cpu->cd.ppc.gpr[ra] = addr; |
|
|
break; |
|
|
|
|
|
case PPC_HI6_LMW: |
|
|
case PPC_HI6_STMW: |
|
|
/* NOTE: Loads use rt, not rs, but are otherwise similar |
|
|
to stores. This code uses rs for both. */ |
|
|
rs = (iword >> 21) & 31; |
|
|
ra = (iword >> 16) & 31; |
|
|
imm = (int16_t)(iword & 0xffff); |
|
|
|
|
|
load = 1; tmp_data_len = 4; |
|
|
|
|
|
switch (hi6) { |
|
|
case PPC_HI6_STMW: |
|
|
load = 0; |
|
|
} |
|
|
|
|
|
if (ra == 0) { |
|
|
addr = 0; |
|
|
} else |
|
|
addr = cpu->cd.ppc.gpr[ra]; |
|
|
|
|
|
if (load && rs == 0) |
|
|
fatal("[ PPC WARNING: invalid LMW form ]\n"); |
|
|
|
|
|
addr += imm; |
|
|
|
|
|
/* TODO: alignment check? */ |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("\t[0x%08llx", (long long)addr); |
|
|
else |
|
|
debug("\t[0x%016llx", (long long)addr); |
|
|
} |
|
|
|
|
|
/* There can be multiple errors! */ |
|
|
r = 0; |
|
|
|
|
|
while (rs <= 31) { |
|
|
if (load) { |
|
|
if (cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, tmp_data_len, MEM_READ, |
|
|
CACHE_DATA) != MEMORY_ACCESS_OK) |
|
|
r++; |
|
|
|
|
|
if (r == 0) { |
|
|
tmp = 0; |
|
|
if (cpu->byte_order == |
|
|
EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[i]; |
|
|
} |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) { |
|
|
tmp <<= 8; |
|
|
tmp += tmp_data[ |
|
|
tmp_data_len - 1 |
|
|
- i]; |
|
|
} |
|
|
} |
|
|
|
|
|
cpu->cd.ppc.gpr[rs] = tmp; |
|
|
} |
|
|
} else { |
|
|
tmp = cpu->cd.ppc.gpr[rs]; |
|
|
|
|
|
if (cpu->byte_order == EMUL_BIG_ENDIAN) { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[tmp_data_len-1-i] = |
|
|
tmp >> (8*i); |
|
|
} else { |
|
|
for (i=0; i<tmp_data_len; i++) |
|
|
tmp_data[i] = tmp >> (8*i); |
|
|
} |
|
|
|
|
|
if (cpu->memory_rw(cpu, cpu->mem, addr, |
|
|
tmp_data, tmp_data_len, MEM_WRITE, |
|
|
CACHE_DATA) != MEMORY_ACCESS_OK) |
|
|
r ++; |
|
|
} |
|
|
|
|
|
/* TODO: Exception! */ |
|
|
|
|
|
/* Go to next register, multiword... */ |
|
|
rs ++; |
|
|
addr += tmp_data_len; |
|
|
} |
|
|
|
|
|
if (cpu->machine->instruction_trace) { |
|
|
if (r == 0) { |
|
|
debug(", data = ...]\n"); |
|
|
} else |
|
|
debug(", FAILED]\n"); |
|
|
} |
|
|
|
|
|
if (r > 0) |
|
|
return 0; |
|
|
break; |
|
|
|
|
|
default: |
|
|
fatal("[ unimplemented PPC hi6 = 0x%02x, pc = 0x%016llx ]\n", |
|
|
hi6, (long long) (cpu->cd.ppc.pc_last)); |
|
|
cpu->running = 0; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
return 1; |
|
|
} |
|
|
|
|
|
|
|
|
#define CPU_RUN ppc_cpu_run |
|
|
#define CPU_RINSTR ppc_cpu_run_instr |
|
|
#define CPU_RUN_PPC |
|
|
#include "cpu_run.c" |
|
|
#undef CPU_RINSTR |
|
|
#undef CPU_RUN_PPC |
|
|
#undef CPU_RUN |
|
|
|
|
|
|
|
|
#define MEMORY_RW ppc_memory_rw |
|
|
#define MEM_PPC |
|
|
#include "memory_rw.c" |
|
|
#undef MEM_PPC |
|
|
#undef MEMORY_RW |
|
|
|
|
|
|
|
|
/* |
|
|
* ppc_cpu_family_init(): |
|
|
* |
|
|
* Fill in the cpu_family struct for PPC. |
|
|
*/ |
|
|
int ppc_cpu_family_init(struct cpu_family *fp) |
|
|
{ |
|
|
fp->name = "PPC"; |
|
|
fp->cpu_new = ppc_cpu_new; |
|
|
fp->list_available_types = ppc_cpu_list_available_types; |
|
|
fp->register_match = ppc_cpu_register_match; |
|
|
fp->disassemble_instr = ppc_cpu_disassemble_instr; |
|
|
fp->register_dump = ppc_cpu_register_dump; |
|
|
fp->run = ppc_cpu_run; |
|
|
fp->dumpinfo = ppc_cpu_dumpinfo; |
|
|
/* fp->show_full_statistics = ppc_cpu_show_full_statistics; */ |
|
|
/* fp->tlbdump = ppc_cpu_tlbdump; */ |
|
|
/* fp->interrupt = ppc_cpu_interrupt; */ |
|
|
/* fp->interrupt_ack = ppc_cpu_interrupt_ack; */ |
|
|
return 1; |
|
|
} |
|
1272 |
|
|
1273 |
|
|
1274 |
#endif /* ENABLE_PPC */ |
#endif /* ENABLE_PPC */ |