macemu/SheepShaver/src/emul_ppc/emul_ppc.cpp

1662 lines
32 KiB
C++
Raw Normal View History

2002-02-04 16:58:13 +00:00
/*
* emul_ppc.cpp - PowerPC processor emulation
*
2008-01-01 09:47:39 +00:00
* SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
2002-02-04 16:58:13 +00:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
TODO:
addme
addmeo
addze
addzeo
dcbst
dcbt
dcbtst
divwuo
fabs
fadd
fadds
fcmpo
fcmpu
fctiw
fctiwz
fdiv
fdivs
fmadd
fmadds
fmr
fmsub
fmsubs
fmul
fmuls
fnabs
fneg
fnmadd
fnmadds
fnmsub
fnmsubs
fres
frsp
frsqrte
fsel
fsqrt
fsqrts
fsub
fsubs
lfdu
lfdux
lfdx
lfs
lfsu
lfsux
lfsx
lhbrx
lwbrx
mcrfs
mcrxr
mtfsb0
mtfsb1
mtfsfi
mulhwu
mullwo
nego
sc
stfdu
stfdux
stfdx
stfs
stfsu
stfsux
stfsx
sthbrx
stwbrx
subfo
subfme
subfmeo
subfze
subfzeo
tw
twi
CHECK:
crxor
creqv
*/
#include <stdio.h>
#include <signal.h>
#include "sysdeps.h"
#include "cpu_emulation.h"
#include "main.h"
#include "xlowmem.h"
#include "emul_op.h"
#if ENABLE_MON
#include "mon.h"
#include "mon_disass.h"
#endif
#define DEBUG 1
#include "debug.h"
#define FLIGHT_RECORDER 1
// PowerPC user mode registers
uint32 r[32];
double fr[32];
uint32 lr, ctr;
uint32 cr, xer;
uint32 fpscr;
uint32 pc;
// Convert 8-bit field mask (e.g. mtcrf) to bit mask
static uint32 field2mask[256];
/*
* Flight recorder
*/
#if FLIGHT_RECORDER
struct rec_step {
uint32 r[32];
double fr[32];
uint32 lr, ctr;
uint32 cr, xer;
uint32 fpscr;
uint32 pc;
uint32 opcode;
};
const int LOG_SIZE = 8192;
static rec_step log[LOG_SIZE];
static int log_ptr = 0;
static void record_step(uint32 opcode)
{
for (int i=0; i<32; i++) {
log[log_ptr].r[i] = r[i];
log[log_ptr].fr[i] = fr[i];
}
log[log_ptr].lr = lr;
log[log_ptr].ctr = ctr;
log[log_ptr].cr = cr;
log[log_ptr].xer = xer;
log[log_ptr].fpscr = fpscr;
log[log_ptr].pc = pc;
log[log_ptr].opcode = opcode;
log_ptr++;
if (log_ptr == LOG_SIZE)
log_ptr = 0;
}
static void dump_log(void)
{
FILE *f = fopen("log", "w");
if (f == NULL)
return;
for (int i=0; i<LOG_SIZE; i++) {
int j = (i + log_ptr) % LOG_SIZE;
fprintf(f, "pc %08x lr %08x ctr %08x cr %08x xer %08x ", log[j].pc, log[j].lr, log[j].ctr, log[j].cr, log[j].xer);
fprintf(f, "r0 %08x r1 %08x r2 %08x r3 %08x ", log[j].r[0], log[j].r[1], log[j].r[2], log[j].r[3]);
fprintf(f, "r4 %08x r5 %08x r6 %08x r7 %08x ", log[j].r[4], log[j].r[5], log[j].r[6], log[j].r[7]);
fprintf(f, "r8 %08x r9 %08x r10 %08x r11 %08x ", log[j].r[8], log[j].r[9], log[j].r[10], log[j].r[11]);
fprintf(f, "r12 %08x r13 %08x r14 %08x r15 %08x ", log[j].r[12], log[j].r[13], log[j].r[14], log[j].r[15]);
fprintf(f, "r16 %08x r17 %08x r18 %08x r19 %08x ", log[j].r[16], log[j].r[17], log[j].r[18], log[j].r[19]);
fprintf(f, "r20 %08x r21 %08x r22 %08x r23 %08x ", log[j].r[20], log[j].r[21], log[j].r[22], log[j].r[23]);
fprintf(f, "r24 %08x r25 %08x r26 %08x r27 %08x ", log[j].r[24], log[j].r[25], log[j].r[26], log[j].r[27]);
fprintf(f, "r28 %08x r29 %08x r30 %08x r31 %08x\n", log[j].r[28], log[j].r[29], log[j].r[30], log[j].r[31]);
#if ENABLE_MON
disass_ppc(f, log[j].pc, log[j].opcode);
#endif
}
fclose(f);
}
#endif
/*
* Dump PPC registers
*/
static void dump(void)
{
// Dump registers
printf(" r0 %08x r1 %08x r2 %08x r3 %08x\n", r[0], r[1], r[2], r[3]);
printf(" r4 %08x r5 %08x r6 %08x r7 %08x\n", r[4], r[5], r[6], r[7]);
printf(" r8 %08x r9 %08x r10 %08x r11 %08x\n", r[8], r[9], r[10], r[11]);
printf("r12 %08x r13 %08x r14 %08x r15 %08x\n", r[12], r[13], r[14], r[15]);
printf("r16 %08x r17 %08x r18 %08x r19 %08x\n", r[16], r[17], r[18], r[19]);
printf("r20 %08x r21 %08x r22 %08x r23 %08x\n", r[20], r[21], r[22], r[23]);
printf("r24 %08x r25 %08x r26 %08x r27 %08x\n", r[24], r[25], r[26], r[27]);
printf("r28 %08x r29 %08x r30 %08x r31 %08x\n", r[28], r[29], r[30], r[31]);
printf(" lr %08x ctr %08x cr %08x xer %08x\n", lr, ctr, cr, xer);
printf(" pc %08x fpscr %08x\n", pc, fpscr);
// Start up mon in real-mode
#if ENABLE_MON
2017-12-02 17:21:13 +00:00
const char *arg[4] = {"mon", "-m", "-r", NULL};
2002-02-04 16:58:13 +00:00
mon(3, arg);
#endif
QuitEmulator();
}
/*
* Record result in CR0
*/
static void record(uint32 val)
{
uint32 crf = 0;
if (val == 0)
crf |= 0x20000000;
else if (val & 0x80000000)
crf |= 0x80000000;
else
crf |= 0x40000000;
if (xer & 0x80000000)
crf |= 0x10000000;
cr = (cr & 0x0fffffff) | crf;
}
/*
* Record result in CR1
*/
static inline void record1(void)
{
cr = (cr & 0xf0ffffff) | ((fpscr >> 4) & 0x0f000000);
}
/*
* Convert mask begin/end to mask
*/
static uint32 mbme2mask(uint32 op)
{
uint32 mb = (op >> 6) & 0x1f;
uint32 me = (op >> 1) & 0x1f;
uint32 m = 0;
uint32 i;
if (mb <= me)
for (i=mb; i<=me; i++)
m |= 0x80000000 >> i;
else {
for (i=0; i<=me; i++)
m |= 0x80000000 >> i;
for (i=mb; i<=31; i++)
m |= 0x80000000 >> i;
}
return m;
}
/*
* Emulate instruction with primary opcode = 19
*/
static void emul19(uint32 op)
{
uint32 exop = (op >> 1) & 0x3ff;
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
switch (exop) {
case 0: { // mcrf
uint32 crfd = 0x1c - (rd & 0x1c);
uint32 crfa = 0x1c - (ra & 0x1c);
uint32 crf = (cr >> crfa) & 0xf;
cr = (cr & ~(0xf << crfd)) | (crf << crfd);
break;
}
case 16: { // bclr
uint32 oldpc = pc;
if (!(rd & 4)) {
ctr--;
if (rd & 2) {
if (ctr)
goto blr_nobranch;
} else {
if (!ctr)
goto blr_nobranch;
}
}
if (!(rd & 0x10)) {
if (rd & 8) {
if (!(cr & (0x80000000 >> ra)))
goto blr_nobranch;
} else {
if (cr & (0x80000000 >> ra))
goto blr_nobranch;
}
}
pc = lr & 0xfffffffc;
blr_nobranch:
if (op & 1)
lr = oldpc;
break;
}
case 33: // crnor
if ((cr & (0x80000000 >> ra)) || ((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr &= ~(0x80000000 >> rd);
else
cr |= 0x80000000 >> rd;
break;
case 129: // crandc
if ((cr & (0x80000000 >> ra)) && !((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr |= 0x80000000 >> rd;
else
cr &= ~(0x80000000 >> rd);
break;
case 150: // isync
break;
case 193: { // crxor
uint32 mask = 0x80000000 >> rd;
cr = (((((cr >> (31 - ra)) ^ (cr >> (31 - ((op >> 11) & 0x1f)))) & 1) << (31 - rd)) & mask) | (cr & ~mask);
break;
}
case 225: // crnand
if ((cr & (0x80000000 >> ra)) && ((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr &= ~(0x80000000 >> rd);
else
cr |= 0x80000000 >> rd;
break;
case 257: // crand
if ((cr & (0x80000000 >> ra)) && ((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr |= 0x80000000 >> rd;
else
cr &= ~(0x80000000 >> rd);
break;
case 289: { // creqv
uint32 mask = 0x80000000 >> rd;
cr = (((~((cr >> (31 - ra)) ^ (cr >> (31 - ((op >> 11) & 0x1f)))) & 1) << (31 - rd)) & mask) | (cr & ~mask);
break;
}
case 417: // crorc
if ((cr & (0x80000000 >> ra)) || !((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr |= 0x80000000 >> rd;
else
cr &= ~(0x80000000 >> rd);
break;
case 449: // cror
if ((cr & (0x80000000 >> ra)) || ((cr & (0x80000000 >> ((op >> 11) & 0x1f)))))
cr |= 0x80000000 >> rd;
else
cr &= ~(0x80000000 >> rd);
break;
case 528: { // bcctr
if (op & 1)
lr = pc;
if (!(rd & 4)) {
ctr--;
if (rd & 2) {
if (ctr)
goto bctr_nobranch;
} else {
if (!ctr)
goto bctr_nobranch;
}
}
if (!(rd & 0x10)) {
if (rd & 8) {
if (!(cr & (0x80000000 >> ra)))
goto bctr_nobranch;
} else {
if (cr & (0x80000000 >> ra))
goto bctr_nobranch;
}
}
pc = ctr & 0xfffffffc;
bctr_nobranch:
break;
}
default:
printf("Illegal 19 opcode %08x (exop %d) at %08x\n", op, exop, pc-4);
dump();
break;
}
}
/*
* Emulate instruction with primary opcode = 31
*/
static void emul31(uint32 op)
{
uint32 exop = (op >> 1) & 0x3ff;
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 rb = (op >> 11) & 0x1f;
switch (exop) {
case 0: { // cmpw
uint32 crfd = 0x1c - (rd & 0x1c);
uint8 crf = 0;
if (r[ra] == r[rb])
crf |= 2;
else if ((int32)r[ra] < (int32)r[rb])
crf |= 8;
else
crf |= 4;
if (xer & 0x80000000)
crf |= 1;
cr = (cr & ~(0xf << crfd)) | (crf << crfd);
break;
}
case 8: { // subfc
uint64 tmp = (uint64)r[rb] - (uint64)r[ra];
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer &= ~0x20000000;
else
xer |= 0x20000000;
if (op & 1)
record(r[rd]);
break;
}
case 10: { // addc
uint64 tmp = (uint64)r[ra] + (uint64)r[rb];
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
if (op & 1)
record(r[rd]);
break;
}
case 19: // mfcr
r[rd] = cr;
break;
case 20: // lwarx
r[rd] = ReadMacInt32(r[rb] + (ra ? r[ra] : 0));
//!! set reservation bit
break;
case 23: // lwzx
r[rd] = ReadMacInt32(r[rb] + (ra ? r[ra] : 0));
break;
case 24: // slw
r[ra] = r[rd] << (r[rb] & 0x3f);
if (op & 1)
record(r[ra]);
break;
case 26: { // cntlzw
uint32 mask = 0x80000000;
for (int i=0; i<32; i++, mask>>=1) {
if (r[rd] & mask) {
r[ra] = i;
goto cntlzw_done;
}
}
r[ra] = 32;
cntlzw_done:if (op & 1)
record(r[ra]);
break;
}
case 28: // and
r[ra] = r[rd] & r[rb];
if (op & 1)
record(r[ra]);
break;
case 32: { // cmplw
uint32 crfd = 0x1c - (rd & 0x1c);
uint8 crf = 0;
if (r[ra] == r[rb])
crf |= 2;
else if (r[ra] < r[rb])
crf |= 8;
else
crf |= 4;
if (xer & 0x80000000)
crf |= 1;
cr = (cr & ~(0xf << crfd)) | (crf << crfd);
break;
}
case 40: // subf
r[rd] = r[rb] - r[ra];
if (op & 1)
record(r[rd]);
break;
case 55: // lwzux
r[ra] += r[rb];
r[rd] = ReadMacInt32(r[ra]);
break;
case 60: // andc
r[ra] = r[rd] & ~r[rb];
if (op & 1)
record(r[ra]);
break;
case 75: // mulhw
r[rd] = ((int64)(int32)r[ra] * (int32)r[rb]) >> 32;
if (op & 1)
record(r[rd]);
break;
case 86: // dcbf
break;
case 87: // lbzx
r[rd] = ReadMacInt8(r[rb] + (ra ? r[ra] : 0));
break;
case 104: // neg
if (r[ra] == 0x80000000)
r[rd] = 0x80000000;
else
r[rd] = -(int32)r[ra];
if (op & 1)
record(r[rd]);
break;
case 119: // lbzux
r[ra] += r[rb];
r[rd] = ReadMacInt8(r[ra]);
break;
case 124: // nor
r[ra] = ~(r[rd] | r[rb]);
if (op & 1)
record(r[ra]);
break;
case 136: { // subfe
uint64 tmp = (uint64)r[rb] - (uint64)r[ra];
if (!(xer & 0x20000000))
tmp--;
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer &= ~0x20000000;
else
xer |= 0x20000000;
if (op & 1)
record(r[rd]);
break;
}
case 138: { // adde
uint64 tmp = (uint64)r[ra] + (uint64)r[rb];
if (xer & 0x20000000)
tmp++;
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
if (op & 1)
record(r[rd]);
break;
}
case 144: { // mtcrf
uint32 mask = field2mask[(op >> 12) & 0xff];
cr = (r[rd] & mask) | (cr & ~mask);
break;
}
case 150: // stwcx
//!! check reserved bit
WriteMacInt32(r[rb] + (ra ? r[ra] : 0), r[rd]);
record(0);
break;
case 151: // stwx
WriteMacInt32(r[rb] + (ra ? r[ra] : 0), r[rd]);
break;
case 183: // stwux
r[ra] += r[rb];
WriteMacInt32(r[ra], r[rd]);
break;
case 215: // stbx
WriteMacInt8(r[rb] + (ra ? r[ra] : 0), r[rd]);
break;
case 235: // mullw
r[rd] = (int32)r[ra] * (int32)r[rb];
if (op & 1)
record(r[rd]);
break;
case 247: // stbux
r[ra] += r[rb];
WriteMacInt8(r[ra], r[rd]);
break;
case 266: // add
r[rd] = r[ra] + r[rb];
if (op & 1)
record(r[rd]);
break;
case 279: // lhzx
r[rd] = ReadMacInt16(r[rb] + (ra ? r[ra] : 0));
break;
case 284: // eqv
r[ra] = ~(r[rd] ^ r[rb]);
if (op & 1)
record(r[ra]);
break;
case 311: // lhzux
r[ra] += r[rb];
r[rd] = ReadMacInt16(r[ra]);
break;
case 316: // xor
r[ra] = r[rd] ^ r[rb];
if (op & 1)
record(r[ra]);
break;
case 339: { // mfspr
uint32 spr = ra | (rb << 5);
switch (spr) {
case 1: r[rd] = xer; break;
case 8: r[rd] = lr; break;
case 9: r[rd] = ctr; break;
default:
printf("Illegal mfspr opcode %08x at %08x\n", op, pc-4);
dump();
}
break;
}
case 343: // lhax
r[rd] = (int32)(int16)ReadMacInt16(r[rb] + (ra ? r[ra] : 0));
break;
case 371: // mftb
r[rd] = 0; //!!
break;
case 375: // lhaux
r[ra] += r[rb];
r[rd] = (int32)(int16)ReadMacInt16(r[ra]);
break;
case 407: // sthx
WriteMacInt16(r[rb] + (ra ? r[ra] : 0), r[rd]);
break;
case 412: // orc
r[ra] = r[rd] | ~r[rb];
if (op & 1)
record(r[ra]);
break;
case 439: // sthux
r[ra] += r[rb];
WriteMacInt16(r[ra], r[rd]);
break;
case 444: // or
r[ra] = r[rd] | r[rb];
if (op & 1)
record(r[ra]);
break;
case 459: // divwu
if (r[rb])
r[rd] = r[ra] / r[rb];
if (op & 1)
record(r[rd]);
break;
case 467: { // mtspr
uint32 spr = ra | (rb << 5);
switch (spr) {
case 1: xer = r[rd] & 0xe000007f; break;
case 8: lr = r[rd]; break;
case 9: ctr = r[rd]; break;
default:
printf("Illegal mtspr opcode %08x at %08x\n", op, pc-4);
dump();
}
break;
}
case 476: // nand
r[ra] = ~(r[rd] & r[rb]);
if (op & 1)
record(r[ra]);
break;
case 491: // divw
if (r[rb])
r[rd] = (int32)r[ra] / (int32)r[rb];
if (op & 1)
record(r[rd]);
break;
case 520: { // subfco
uint64 tmp = (uint64)r[rb] - (uint64)r[ra];
uint32 ov = (r[ra] ^ r[rb]) & ((uint32)tmp ^ r[rb]);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer &= ~0x20000000;
else
xer |= 0x20000000;
if (ov & 0x80000000)
xer |= 0xc0000000;
else
xer &= ~0x40000000;
if (op & 1)
record(r[rd]);
break;
}
case 522: { // addco
uint64 tmp = (uint64)r[ra] + (uint64)r[rb];
uint32 ov = (r[ra] ^ (uint32)tmp) & (r[rb] ^ (uint32)tmp);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
if (ov & 0x80000000)
xer |= 0xc0000000;
else
xer &= ~0x40000000;
if (op & 1)
record(r[rd]);
break;
}
case 533: { // lswx
uint32 addr = r[rb] + (ra ? r[ra] : 0);
int nb = xer & 0x7f;
int reg = rd;
for (int i=0; i<nb; i++) {
switch (i & 3) {
case 0:
r[reg] = ReadMacInt8(addr + i) << 24;
break;
case 1:
r[reg] = (r[reg] & 0xff00ffff) | (ReadMacInt8(addr + i) << 16);
break;
case 2:
r[reg] = (r[reg] & 0xffff00ff) | (ReadMacInt8(addr + i) << 8);
break;
case 3:
r[reg] = (r[reg] & 0xffffff00) | ReadMacInt8(addr + i);
reg = (reg + 1) & 0x1f;
break;
}
}
break;
}
case 536: // srw
r[ra] = r[rd] >> (r[rb] & 0x3f);
if (op & 1)
record(r[ra]);
break;
case 597: { // lswi
uint32 addr = ra ? r[ra] : 0;
int nb = rb ? rb : 32;
int reg = rd;
for (int i=0; i<nb; i++) {
switch (i & 3) {
case 0:
r[reg] = ReadMacInt8(addr + i) << 24;
break;
case 1:
r[reg] = (r[reg] & 0xff00ffff) | (ReadMacInt8(addr + i) << 16);
break;
case 2:
r[reg] = (r[reg] & 0xffff00ff) | (ReadMacInt8(addr + i) << 8);
break;
case 3:
r[reg] = (r[reg] & 0xffffff00) | ReadMacInt8(addr + i);
reg = (reg + 1) & 0x1f;
break;
}
}
break;
}
case 598: // sync
break;
case 648: { // subfeo
uint64 tmp = (uint64)r[rb] - (uint64)r[ra];
if (!(xer & 0x20000000))
tmp--;
uint32 ov = (r[ra] ^ r[rb]) & ((uint32)tmp ^ r[rb]);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer &= ~0x20000000;
else
xer |= 0x20000000;
if (ov & 0x80000000)
xer |= 0xc0000000;
else
xer &= ~0x40000000;
if (op & 1)
record(r[rd]);
break;
}
case 650: { // addeo
uint64 tmp = (uint64)r[ra] + (uint64)r[rb];
if (xer & 0x20000000)
tmp++;
uint32 ov = (r[ra] ^ (uint32)tmp) & (r[rb] ^ (uint32)tmp);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
if (ov & 0x80000000)
xer |= 0xc0000000;
else
xer &= ~0x40000000;
if (op & 1)
record(r[rd]);
break;
}
case 661: { // stswx
uint32 addr = r[rb] + (ra ? r[ra] : 0);
int nb = xer & 0x7f;
int reg = rd;
int shift = 24;
for (int i=0; i<nb; i++) {
WriteMacInt8(addr + i, (r[reg] >> shift));
shift -= 8;
if ((i & 3) == 3) {
shift = 24;
reg = (reg + 1) & 0x1f;
}
}
break;
}
case 725: { // stswi
uint32 addr = ra ? r[ra] : 0;
int nb = rb ? rb : 32;
int reg = rd;
int shift = 24;
for (int i=0; i<nb; i++) {
WriteMacInt8(addr + i, (r[reg] >> shift));
shift -= 8;
if ((i & 3) == 3) {
shift = 24;
reg = (reg + 1) & 0x1f;
}
}
break;
}
case 778: { // addo
uint32 tmp = r[ra] + r[rb];
uint32 ov = (r[ra] ^ tmp) & (r[rb] ^ tmp);
r[rd] = tmp;
if (ov & 0x80000000)
xer |= 0xc0000000;
else
xer &= ~0x40000000;
if (op & 1)
record(r[rd]);
break;
}
case 792: { // sraw
uint32 sh = r[rb] & 0x3f;
uint32 mask = ~(0xffffffff << sh);
if ((r[rd] & 0x80000000) && (r[rd] & mask))
xer |= 0x20000000;
else
xer &= ~0x20000000;
r[ra] = (int32)r[rd] >> sh;
if (op & 1)
record(r[ra]);
break;
}
case 824: { // srawi
uint32 mask = ~(0xffffffff << rb);
if ((r[rd] & 0x80000000) && (r[rd] & mask))
xer |= 0x20000000;
else
xer &= ~0x20000000;
r[ra] = (int32)r[rd] >> rb;
if (op & 1)
record(r[ra]);
break;
}
case 854: // eieio
break;
case 922: // extsh
r[ra] = (int32)(int16)r[rd];
if (op & 1)
record(r[ra]);
break;
case 954: // extsb
r[ra] = (int32)(int8)r[rd];
if (op & 1)
record(r[ra]);
break;
case 982: // icbi
break;
case 1003: // divwo
if (r[rb] == 0 || (r[ra] == 0x80000000 && r[rb] == 0xffffffff))
xer |= 0xc0000000;
else {
r[rd] = (int32)r[ra] / (int32)r[rb];
xer &= ~0x40000000;
}
if (op & 1)
record(r[rd]);
break;
#if 0
case 1014: // dcbz
memset(r[rb] + (ra ? r[ra] : 0), 0, 32);
break;
#endif
default:
printf("Illegal 31 opcode %08x (exop %d) at %08x\n", op, exop, pc-4);
dump();
break;
}
}
/*
* Emulate instruction with primary opcode = 59
*/
static void emul59(uint32 op)
{
uint32 exop = (op >> 1) & 0x3ff;
switch (exop) {
default:
printf("Illegal 59 opcode %08x (exop %d) at %08x\n", op, exop, pc-4);
dump();
break;
}
}
/*
* Emulate instruction with primary opcode = 63
*/
static void emul63(uint32 op)
{
uint32 exop = (op >> 1) & 0x3ff;
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 rb = (op >> 11) & 0x1f;
switch (exop) {
case 583: // mffs
fr[rd] = (double)(uint64)fpscr;
if (op & 1)
record1();
break;
case 711: // mtfsf
//!!
if (op & 1)
record1();
break;
default:
printf("Illegal 63 opcode %08x (exop %d) at %08x\n", op, exop, pc-4);
dump();
break;
}
}
/*
* Emulation loop
*/
void emul_ppc(uint32 start)
{
pc = start;
//uint32 old_val = 0;
for (;;) {
//uint32 val = ReadMacInt32(0x68fff778);
//if (val != old_val) {
// printf("insn at %08lx changed %08lx->%08lx\n", pc-4, old_val, val);
// old_val = val;
//}
uint32 op = ReadMacInt32(pc);
#if FLIGHT_RECORDER
record_step(op);
#endif
// printf("%08lx at %08lx\n", op, pc);
uint32 primop = op >> 26;
pc += 4;
switch (primop) {
case 6: // SheepShaver extensions
printf("Extended opcode %08x at %08x (68k pc %08x)\n", op, pc-4, r[24]);
switch (op & 0x3f) {
case 0: // EMUL_RETURN
QuitEmulator();
break;
case 1: // EXEC_RETURN
//!!
dump();
break;
default: { // EMUL_OP
M68kRegisters r68;
WriteMacInt32(XLM_68K_R25, r[25]);
WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP);
for (int i=0; i<8; i++)
r68.d[i] = r[8 + i];
for (int i=0; i<7; i++)
r68.a[i] = r[16 + i];
r68.a[7] = r[1];
EmulOp(&r68, r[24], (op & 0x3f) - 2);
for (int i=0; i<8; i++)
r[8 + i] = r68.d[i];
for (int i=0; i<7; i++)
r[16 + i] = r68.a[i];
r[1] = r68.a[7];
WriteMacInt32(XLM_RUN_MODE, MODE_68K);
break;
}
}
break;
case 7: { // mulli
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = (int32)r[ra] * (int32)(int16)(op & 0xffff);
break;
}
case 8: { // subfic
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint64 tmp = (uint32)(int32)(int16)(op & 0xffff) - (uint64)r[ra];
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer &= ~0x20000000;
else
xer |= 0x20000000;
break;
}
case 10: { // cmpli
uint32 crfd = 0x1c - ((op >> 21) & 0x1c);
uint32 ra = (op >> 16) & 0x1f;
uint32 val = (op & 0xffff);
uint8 crf = 0;
if (r[ra] == val)
crf |= 2;
else if (r[ra] < val)
crf |= 8;
else
crf |= 4;
if (xer & 0x80000000)
crf |= 1;
cr = (cr & ~(0xf << crfd)) | (crf << crfd);
break;
}
case 11: { // cmpi
uint32 crfd = 0x1c - ((op >> 21) & 0x1c);
uint32 ra = (op >> 16) & 0x1f;
int32 val = (int32)(int16)(op & 0xffff);
uint8 crf = 0;
if ((int32)r[ra] == val)
crf |= 2;
else if ((int32)r[ra] < val)
crf |= 8;
else
crf |= 4;
if (xer & 0x80000000)
crf |= 1;
cr = (cr & ~(0xf << crfd)) | (crf << crfd);
break;
}
case 12: { // addic
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint64 tmp = (uint64)r[ra] + (uint32)(int32)(int16)(op & 0xffff);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
break;
}
case 13: { // addic.
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint64 tmp = (uint64)r[ra] + (uint32)(int32)(int16)(op & 0xffff);
r[rd] = tmp;
if (tmp & 0x100000000LL)
xer |= 0x20000000;
else
xer &= ~0x20000000;
record(r[rd]);
break;
}
case 14: { // addi
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = (ra ? r[ra] : 0) + (int32)(int16)(op & 0xffff);
break;
}
case 15: { // addis
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = (ra ? r[ra] : 0) + (op << 16);
break;
}
case 16: { // bc
uint32 bo = (op >> 21) & 0x1f;
uint32 bi = (op >> 16) & 0x1f;
if (op & 1)
lr = pc;
if (!(bo & 4)) {
ctr--;
if (bo & 2) {
if (ctr)
goto bc_nobranch;
} else {
if (!ctr)
goto bc_nobranch;
}
}
if (!(bo & 0x10)) {
if (bo & 8) {
if (!(cr & (0x80000000 >> bi)))
goto bc_nobranch;
} else {
if (cr & (0x80000000 >> bi))
goto bc_nobranch;
}
}
if (op & 2)
pc = (int32)(int16)(op & 0xfffc);
else
pc += (int32)(int16)(op & 0xfffc) - 4;
bc_nobranch:
break;
}
case 18: { // b
int32 target = op & 0x03fffffc;
if (target & 0x02000000)
target |= 0xfc000000;
if (op & 1)
lr = pc;
if (op & 2)
pc = target;
else
pc += target - 4;
break;
}
case 19:
emul19(op);
break;
case 20: { // rlwimi
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 sh = (op >> 11) & 0x1f;
uint32 mask = mbme2mask(op);
r[ra] = (((r[rs] << sh) | (r[rs] >> (32-sh))) & mask) | (r[ra] & ~mask);
if (op & 1)
record(r[ra]);
break;
}
case 21: { // rlwinm
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 sh = (op >> 11) & 0x1f;
r[ra] = ((r[rs] << sh) | (r[rs] >> (32-sh))) & mbme2mask(op);
if (op & 1)
record(r[ra]);
break;
}
case 23: { // rlwnm
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 sh = r[(op >> 11) & 0x1f] & 0x1f;
r[ra] = ((r[rs] << sh) | (r[rs] >> (32-sh))) & mbme2mask(op);
if (op & 1)
record(r[ra]);
break;
}
case 24: { // ori
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] | (op & 0xffff);
break;
}
case 25: { // oris
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] | (op << 16);
break;
}
case 26: { // xori
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] ^ (op & 0xffff);
break;
}
case 27: { // xoris
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] ^ (op << 16);
break;
}
case 28: { // andi.
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] & (op & 0xffff);
record(r[ra]);
break;
}
case 29: { // andis.
uint32 rs = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] = r[rs] & (op << 16);
record(r[ra]);
break;
}
case 31:
emul31(op);
break;
case 32: { // lwz
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = ReadMacInt32(int16(op & 0xffff) + (ra ? r[ra] : 0));
break;
}
case 33: { // lwzu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
r[rd] = ReadMacInt32(r[ra]);
break;
}
case 34: { // lbz
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = ReadMacInt8(int16(op & 0xffff) + (ra ? r[ra] : 0));
break;
}
case 35: { // lbzu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
r[rd] = ReadMacInt8(r[ra]);
break;
}
case 36: { // stw
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
WriteMacInt32(int16(op & 0xffff) + (ra ? r[ra] : 0), r[rd]);
break;
}
case 37: { // stwu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
WriteMacInt32(r[ra], r[rd]);
break;
}
case 38: { // stb
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
WriteMacInt8(int16(op & 0xffff) + (ra ? r[ra] : 0), r[rd]);
break;
}
case 39: { // stbu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
WriteMacInt8(r[ra], r[rd]);
break;
}
case 40: { // lhz
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = ReadMacInt16(int16(op & 0xffff) + (ra ? r[ra] : 0));
break;
}
case 41: { // lhzu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
r[rd] = ReadMacInt16(r[ra]);
break;
}
case 42: { // lha
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[rd] = (int32)(int16)ReadMacInt16(int16(op & 0xffff) + (ra ? r[ra] : 0));
break;
}
case 43: { // lhau
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
r[rd] = (int32)(int16)ReadMacInt16(r[ra]);
break;
}
case 44: { // sth
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
WriteMacInt16(int16(op & 0xffff) + (ra ? r[ra] : 0), r[rd]);
break;
}
case 45: { // sthu
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
r[ra] += int16(op & 0xffff);
WriteMacInt16(r[ra], r[rd]);
break;
}
case 46: { // lmw
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 addr = int16(op & 0xffff) + (ra ? r[ra] : 0);
while (rd <= 31) {
r[rd] = ReadMacInt32(addr);
rd++;
addr += 4;
}
break;
}
case 47: { // stmw
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
uint32 addr = int16(op & 0xffff) + (ra ? r[ra] : 0);
while (rd <= 31) {
WriteMacInt32(addr, r[rd]);
rd++;
addr += 4;
}
break;
}
case 50: { // lfd
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
fr[rd] = (double)ReadMacInt64(int16(op & 0xffff) + (ra ? r[ra] : 0));
break;
}
case 54: { // stfd
uint32 rd = (op >> 21) & 0x1f;
uint32 ra = (op >> 16) & 0x1f;
WriteMacInt64(int16(op & 0xffff) + (ra ? r[ra] : 0), (uint64)fr[rd]);
break;
}
case 59:
emul59(op);
break;
case 63:
emul63(op);
break;
default:
printf("Illegal opcode %08x at %08x\n", op, pc-4);
dump();
break;
}
}
}
static struct sigaction sigsegv_action;
static void sigsegv_handler(int sig)
{
printf("SIGSEGV\n");
dump();
}
void init_emul_ppc(void)
{
// Init field2mask
for (int i=0; i<256; i++) {
uint32 mask = 0;
if (i & 0x01) mask |= 0x0000000f;
if (i & 0x02) mask |= 0x000000f0;
if (i & 0x04) mask |= 0x00000f00;
if (i & 0x08) mask |= 0x0000f000;
if (i & 0x10) mask |= 0x000f0000;
if (i & 0x20) mask |= 0x00f00000;
if (i & 0x40) mask |= 0x0f000000;
if (i & 0x80) mask |= 0xf0000000;
field2mask[i] = mask;
}
// Init registers
for (int i=0; i<32; i++) {
r[i] = 0;
fr[i] = 0.0;
}
lr = ctr = 0;
cr = xer = 0;
fpscr = 0;
[Michael Schmitt] Attached is a patch to SheepShaver to fix memory allocation problems when OS X 10.5 is the host. It also relaxes the 512 MB RAM limit on OS X hosts. Problem ------- Some users have been unable to run SheepShaver on OS X 10.5 (Leopard) hosts. The symptom is error "ERROR: Cannot map RAM: File already exists". SheepShaver allocates RAM at fixed addresses. If it is running in "Real" addressing mode, and can't allocate at address 0, then it was hard-coded to allocate the RAM area at 0x20000000. The ROM area as allocated at 0x40800000. The normal configuration is for SheepShaver to run under SDL, which is a Cocoa wrapper. By the time SheepShaver does its memory allocations, the Cocoa application has already started. The result is the SheepShaver memory address space already contains libraries, fonts, Input Managers, and IOKit areas. On Leopard hosts these areas can land on the same addresses SheepShaver needs, so SheepShaver's memory allocation fails. Solution -------- The approach is to change SheepShaver (on Unix & OS X hosts) to allocate the RAM area anywhere it can find the space, rather than at a fixed address. This could result in the RAM allocated higher than the ROM area, which causes a crash. To prevent this from occurring, the RAM and ROM areas are allocated contiguously. Previously the ROM starting address was a constant ROM_BASE, which was used throughout the source files. The ROM start address is now a variable ROMBase. ROMBase is allocated and set by main_*.cpp just like RAMBase. A side-effect of this change is that it lifts the 512 MB RAM limit for OS X hosts. The limit was because the fixed RAM and ROM addresses were such that the RAM could only be 512 MB before it overlapped the ROM area. Impact ------ The change to make ROMBase a variable is throughout all hosts & addressing modes. The RAM and ROM areas will only shift when run on Unix & OS X hosts, otherwise the same fixed allocation address is used as before. This change is limited to "Real" addressing mode. Unlike Basilisk II, SheepShaver *pre-calculates* the offset for "Direct" addressing mode; the offset is compiled into the program. If the RAM address were allowed to shift, it could result in the RAM area wrapping around address 0. Changes to main_unix.cpp ------------------------ 1. Real addressing mode no longer defines a RAM_BASE constant. 2. The base address of the Mac ROM (ROMBase) is defined and exported by this program. 3. Memory management helper vm_mac_acquire is renamed to vm_mac_acquire_fixed. Added a new memory management helper vm_mac_acquire, which allocates memory at any address. 4. Changed and rearranged the allocation of RAM and ROM areas. Before it worked like this: - Allocate ROM area - If can, attempt to allocate RAM at address zero - If RAM not allocated at 0, allocate at fixed address We still want to try allocating the RAM at zero, and if using DIRECT addressing we're still going to use the fixed addresses. So we don't know where the ROM should be until after we do the RAM. The new logic is: - If can, attempt to allocate RAM at address zero - If RAM not allocated at 0 if REAL addressing allocate RAM and ROM together. The ROM address is aligned to a 1 MB boundary else (direct addressing) allocate RAM at fixed address - If ROM hasn't been allocated yet, allocate at fixed address 5. Calculate ROMBase and ROMBaseHost based on where the ROM was loaded. 6. There is a crash if the RAM is allocated too high. To try and catch this, check if it was allocated higher than the kernel data address. 7. Change subsequent code from using constant ROM_BASE to variable ROMBase. Changes to Other Programs ------------------------- emul_op.cpp, main.cpp, name_registery.cpp, rom_patches.cpp, rsrc_patches.cpp, emul_ppc.cpp, sheepshaver_glue.cpp, ppc-translate-cpp: Change from constant ROM_BASE to variable ROMBase. ppc_asm.S: It was setting register to a hard-coded literal address: 0x40b0d000. Changed to set it to ROMBase + 0x30d000. ppc_asm.tmpl: It defined a macro ASM_LO16 but it assumed that the macro would always be used with operands that included a register specification. This is not true. Moved the register specification from the macro to the macro invocations. main_beos.cpp, main_windows.cpp: Since the subprograms are all expecting a variable ROMBase, all the main_*.cpp pgrams have to define and export it. The ROM_BASE constant is moved here for consistency. The mains for beos and windows just allocate the ROM at the same fixed address as before, set ROMBaseHost and ROMBase to that address, and then use ROMBase for the subsequent code. cpu_emulation.h: removed ROM_BASE constant. This value is moved to the main_*.cpp modules, to be consistent with RAM_BASE. user_strings_unix.cpp, user_strings_unix.h: Added new error messages related to errors that occur when the RAM and ROM are allocated anywhere.
2009-08-18 18:26:11 +00:00
r[3] = ROMBase + 0x30d000;
2002-02-04 16:58:13 +00:00
// Install SIGSEGV handler
sigemptyset(&sigsegv_action.sa_mask);
sigsegv_action.sa_handler = (__sighandler_t)sigsegv_handler;
sigsegv_action.sa_flags = 0;
sigsegv_action.sa_restorer = NULL;
sigaction(SIGSEGV, &sigsegv_action, NULL);
#if FLIGHT_RECORDER && ENABLE_MON
// Install "log" command in mon
mon_add_command("log", dump_log, "log Dump PowerPC emulation log\n");
#endif
}
/*
* Execute 68k subroutine (must be ended with EXEC_RETURN)
* This must only be called by the emul_thread when in EMUL_OP mode
* r->a[7] is unused, the routine runs on the caller's stack
*/
void Execute68k(uint32 pc, M68kRegisters *r)
{
printf("ERROR: Execute68k() unimplemented\n");
QuitEmulator();
}
/*
* Execute 68k A-Trap from EMUL_OP routine
* r->a[7] is unused, the routine runs on the caller's stack
*/
void Execute68kTrap(uint16 trap, M68kRegisters *r)
{
printf("ERROR: Execute68kTrap() unimplemented\n");
QuitEmulator();
}
/*
* Call MacOS PPC code
*/
uint32 call_macos(uint32 tvect)
{
printf("ERROR: call_macos() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos1(uint32 tvect, uint32 arg1)
{
printf("ERROR: call_macos1() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos2(uint32 tvect, uint32 arg1, uint32 arg2)
{
printf("ERROR: call_macos2() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos3(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3)
{
printf("ERROR: call_macos3() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos4(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4)
{
printf("ERROR: call_macos4() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos5(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5)
{
printf("ERROR: call_macos5() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos6(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6)
{
printf("ERROR: call_macos6() unimplemented\n");
QuitEmulator();
return 0;
}
uint32 call_macos7(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6, uint32 arg7)
{
printf("ERROR: call_macos7() unimplemented\n");
QuitEmulator();
return 0;
}
/*
* Atomic operations
*/
extern int atomic_add(int *var, int v)
{
int ret = *var;
*var += v;
return ret;
}
extern int atomic_and(int *var, int v)
{
int ret = *var;
*var &= v;
return ret;
}
extern int atomic_or(int *var, int v)
{
int ret = *var;
*var |= v;
return ret;
}
extern "C" void get_resource(void);
extern "C" void get_1_resource(void);
extern "C" void get_ind_resource(void);
extern "C" void get_1_ind_resource(void);
extern "C" void r_get_resource(void);
void get_resource(void)
{
printf("ERROR: get_resource() unimplemented\n");
QuitEmulator();
}
void get_1_resource(void)
{
printf("ERROR: get_1_resource() unimplemented\n");
QuitEmulator();
}
void get_ind_resource(void)
{
printf("ERROR: get_ind_resource() unimplemented\n");
QuitEmulator();
}
void get_1_ind_resource(void)
{
printf("ERROR: get_1_ind_resource() unimplemented\n");
QuitEmulator();
}
void r_get_resource(void)
{
printf("ERROR: r_get_resource() unimplemented\n");
QuitEmulator();
}