mirror of
https://github.com/ksherlock/mpw.git
synced 2024-11-22 15:31:50 +00:00
706 lines
15 KiB
C++
706 lines
15 KiB
C++
// clang++ -c -std=c++11 -stdlib=libc++ -Wno-deprecated-declarations loader.cpp
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sysexits.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <unistd.h>
|
|
|
|
#include <CoreServices/CoreServices.h>
|
|
|
|
#include <cpu/defs.h>
|
|
#include <cpu/CpuModule.h>
|
|
#include <cpu/fmem.h>
|
|
|
|
#include <toolbox/toolbox.h>
|
|
#include <toolbox/mm.h>
|
|
#include <mpw/mpw.h>
|
|
#include <mplite/mplite.h>
|
|
|
|
uint8_t *Memory;
|
|
uint32_t HighWater = 0x10000;
|
|
uint32_t MemorySize = 0;
|
|
|
|
uint32_t EmulatedNewPtr(uint32_t size)
|
|
{
|
|
if (size & 0x01) size++;
|
|
|
|
if (HighWater + size > MemorySize)
|
|
{
|
|
fprintf(stderr, "Insufficient Memory!\n");
|
|
exit(EX_CONFIG);
|
|
}
|
|
|
|
|
|
uint32_t address = HighWater;
|
|
|
|
|
|
HighWater += size;
|
|
|
|
std::memset(Memory + HighWater, 0, size);
|
|
|
|
return address;
|
|
}
|
|
|
|
uint8_t ReadByte(const void *data, uint32_t offset)
|
|
{
|
|
offset &= 0xffffff;
|
|
if (offset >= MemorySize) return 0;
|
|
return ((uint8_t *)data)[offset];
|
|
}
|
|
|
|
uint16_t ReadWord(const void *data, uint32_t offset)
|
|
{
|
|
offset &= 0xffffff;
|
|
return (ReadByte(data, offset) << 8) | ReadByte(data, offset+1);
|
|
}
|
|
|
|
uint32_t ReadLong(const void *data, uint32_t offset)
|
|
{
|
|
offset &= 0xffffff;
|
|
return (ReadWord(data, offset) << 16) | ReadWord(data, offset+2);
|
|
}
|
|
|
|
void WriteByte(void *data, uint32_t offset, uint8_t value)
|
|
{
|
|
offset &= 0xffffff;
|
|
if (offset >= MemorySize) return;
|
|
((uint8_t *)data)[offset] = value;
|
|
}
|
|
|
|
void WriteWord(void *data, uint32_t offset, uint16_t value)
|
|
{
|
|
offset &= 0xffffff;
|
|
|
|
if (offset + 1 >= MemorySize) return;
|
|
|
|
((uint8_t *)data)[offset++] = value >> 8;
|
|
((uint8_t *)data)[offset++] = value;
|
|
}
|
|
|
|
void WriteLong(void *data, uint32_t offset, uint32_t value)
|
|
{
|
|
offset &= 0xffffff;
|
|
|
|
if (offset + 3 >= MemorySize) return;
|
|
|
|
((uint8_t *)data)[offset++] = value >> 24;
|
|
((uint8_t *)data)[offset++] = value >> 16;
|
|
((uint8_t *)data)[offset++] = value >> 8;
|
|
((uint8_t *)data)[offset++] = value;
|
|
}
|
|
|
|
/*
|
|
//0x0316 = MPWArgs *
|
|
struct MPWArgs
|
|
{
|
|
uint32_t MPGM; // = 'MPGM';
|
|
int32_t argptr;
|
|
|
|
args:
|
|
uint16_t SH;
|
|
uint32_t argc;
|
|
uint32_t argv;
|
|
uint32_t envp;
|
|
|
|
envp stored as key\0value\0, not key=value\0
|
|
};
|
|
|
|
0x910 = name ptr (32-char pstring)
|
|
|
|
*/
|
|
|
|
uint32_t load(const char *file)
|
|
{
|
|
|
|
ResFileRefNum refNum;
|
|
FSRef ref;
|
|
|
|
uint32_t returnAddress = 0;
|
|
|
|
std::vector< std::pair<uint32_t, uint32_t> > segments; // segment, address
|
|
|
|
|
|
uint32_t a5 = 0;
|
|
|
|
uint32_t jtStart = 0;
|
|
uint32_t jtEnd = 0;
|
|
|
|
assert(FSPathMakeRef( (const UInt8 *)file, &ref, NULL) == noErr);
|
|
refNum = FSOpenResFile(&ref, fsRdPerm);
|
|
assert(refNum != -1 );
|
|
|
|
int l = Count1Resources('CODE');
|
|
segments.reserve(l);
|
|
|
|
assert(l > 0);
|
|
|
|
for (int i = 0; i < l; ++i)
|
|
{
|
|
ResAttributes attr;
|
|
ResID resID;
|
|
ResType resType;
|
|
Str255 name;
|
|
uint32_t size;
|
|
uint32_t address;
|
|
Handle h;
|
|
const uint8_t *data;
|
|
|
|
|
|
h = Get1IndResource('CODE', i + 1);
|
|
if (!h) continue;
|
|
HLock(h);
|
|
data = *(const uint8_t **)h;
|
|
|
|
attr = GetResAttrs(h);
|
|
GetResInfo(h, &resID, &resType, name);
|
|
|
|
size = GetHandleSize(h);
|
|
|
|
if (segments.size() <= resID) segments.resize(resID + 1);
|
|
// can't have duplicate resIDs, so no need to check that...
|
|
|
|
if (resID == 0)
|
|
{
|
|
// jump table/a5
|
|
uint32_t above = ReadLong(data, 0);
|
|
uint32_t below = ReadLong(data, 4);
|
|
uint32_t jtSize = ReadLong(data, 8);
|
|
uint32_t jtOffset = ReadLong(data, 12);
|
|
|
|
uint32_t a5size = above + below;
|
|
|
|
address = EmulatedNewPtr(a5size);
|
|
|
|
a5 = address + below;
|
|
std::memcpy(Memory + a5 + jtOffset, data + 16 , jtSize);
|
|
|
|
segments[resID] = std::make_pair(address, a5size);
|
|
|
|
jtStart = a5 + jtOffset;
|
|
jtEnd = jtStart + jtSize;
|
|
|
|
// 0x0934 - CurJTOffset (16-bit)
|
|
WriteWord(Memory, 0x0934, jtOffset);
|
|
|
|
// 0x0904 -- CurrentA5 (32-bit)
|
|
WriteLong(Memory, 0x0904, a5);
|
|
cpuSetAReg(5, a5);
|
|
}
|
|
else
|
|
{
|
|
address = EmulatedNewPtr(size);
|
|
std::memcpy(Memory + address, data, size);
|
|
|
|
segments[resID] = std::make_pair(address, size);
|
|
}
|
|
|
|
ReleaseResource(h);
|
|
}
|
|
|
|
// now link the segment 0 jump table...
|
|
assert(a5);
|
|
|
|
bool first = true;
|
|
for (; jtStart < jtEnd; jtStart += 8)
|
|
{
|
|
uint16_t offset = ReadWord(Memory, jtStart);
|
|
uint16_t seg = ReadWord(Memory, jtStart + 4);
|
|
|
|
assert(ReadWord(Memory, jtStart + 2) == 0x3F3C);
|
|
assert(ReadWord(Memory, jtStart + 6) == 0xA9F0);
|
|
|
|
assert(seg < segments.size());
|
|
|
|
auto p = segments[seg];
|
|
assert(p.first); // missing segment?!
|
|
assert(offset < p.second);
|
|
|
|
// +4 for the jump table info header.
|
|
uint32_t address = p.first + offset + 4;
|
|
|
|
// JMP absolute long
|
|
WriteWord(Memory, jtStart + 2, 0x4EF9);
|
|
WriteLong(Memory, jtStart + 4, address);
|
|
|
|
if (first)
|
|
{
|
|
//cpuSetPC(address);
|
|
returnAddress = address;
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// set pc to jump table entry 0.
|
|
return returnAddress;
|
|
}
|
|
|
|
void InitializeMPW(int argc, char **argv)
|
|
{
|
|
|
|
argv[0] = basename(argv[0]);
|
|
// 0x0910 CurApName
|
|
{
|
|
char * name = argv[0];
|
|
char str32[32];
|
|
int l = strlen(name);
|
|
l = std::min(l, 32);
|
|
str32[0] = l;
|
|
memcpy(str32 + 1, name, l);
|
|
while (l < 32) str32[l++] = 0;
|
|
|
|
memcpy(Memory + 0x910, str32, 32);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 0x0316 is a pointer to the argc, argv, envp.
|
|
// very ugly.
|
|
|
|
uint32_t argvAddress = 0;
|
|
uint32_t envpAddress = 0;
|
|
|
|
{
|
|
std::vector<uint32_t> argvSpine;
|
|
argvSpine.reserve(argc + 1);
|
|
|
|
for (int i = 0; i < argc; ++i)
|
|
{
|
|
int length;
|
|
uint32_t address;
|
|
length = strlen(argv[i]);
|
|
address = EmulatedNewPtr(length + 1);
|
|
memcpy(Memory + address, argv[i], length + 1);
|
|
|
|
argvSpine.push_back(address);
|
|
}
|
|
argvSpine.push_back(0);
|
|
|
|
argvAddress = EmulatedNewPtr(4 * argvSpine.size());
|
|
uint32_t address = argvAddress;
|
|
for (uint32_t x : argvSpine)
|
|
{
|
|
WriteLong(Memory, address, x);
|
|
address += 4;
|
|
}
|
|
}
|
|
// same thing for envp... but \0 instead of =
|
|
|
|
|
|
// _macpgminfo
|
|
uint32_t address = EmulatedNewPtr(8 + 0x30);
|
|
|
|
address = 0x2000; // monitor reads...
|
|
WriteLong(Memory, 0x0316, address);
|
|
WriteLong(Memory, address, 0x4d50474d); // MPGM - magic
|
|
WriteLong(Memory, address + 4, 0x2100 /* address + 8 */); // block ptr
|
|
|
|
// address += 8;
|
|
address = 0x2100;
|
|
WriteWord(Memory, address + 0x00, 0x5348); // SH - more magic
|
|
WriteLong(Memory, address + 0x02, argc);
|
|
WriteLong(Memory, address + 0x06, argvAddress);
|
|
WriteLong(Memory, address + 0x0a, envpAddress);
|
|
|
|
|
|
|
|
// possible the application exit code...
|
|
WriteLong(Memory, address + 0x0e, 0x00007000);
|
|
|
|
WriteLong(Memory, address + 0x12, 0x00008000);
|
|
WriteLong(Memory, address + 0x16, 0x00009000);
|
|
WriteWord(Memory, address + 0x1a, 0x0190); // ????
|
|
|
|
// default file table? block of size 0x3c.
|
|
// copied into IntEnv+0x1c
|
|
// _initIOPtable
|
|
WriteLong(Memory, address + 0x1c, 0x0000a000);
|
|
|
|
// stdin
|
|
WriteLong(Memory, 0x0000a000+0, 0x00010000); // ???
|
|
WriteLong(Memory, 0x0000a000+4, 0x00003000); // type ptr (FSYS)
|
|
WriteLong(Memory, 0x0000a000+8, STDIN_FILENO); // ptr to refcount/fd struct?
|
|
WriteLong(Memory, 0x0000a000+12, 0x00000000); // transferCount
|
|
WriteLong(Memory, 0x0000a000+16, 0x00000000); // transferBuffer
|
|
|
|
// stdout
|
|
//0x0001 = readable
|
|
//0x0002 = writable
|
|
// others?...
|
|
WriteLong(Memory, 0x0000a000+20, 0x00020000); // ??? {uint16_t flags, uint16_t error? }
|
|
WriteLong(Memory, 0x0000a000+24, 0x00003000); // type ptr (FSYS)
|
|
WriteLong(Memory, 0x0000a000+28, STDOUT_FILENO); // ptr to refcount/fd struct?
|
|
WriteLong(Memory, 0x0000a000+32, 0x00000000); //???
|
|
WriteLong(Memory, 0x0000a000+36, 0x00000000); //???
|
|
|
|
// stderr
|
|
WriteLong(Memory, 0x0000a000+40, 0x00020000); // ???
|
|
WriteLong(Memory, 0x0000a000+44, 0x00003000); // type ptr (FSYS)
|
|
WriteLong(Memory, 0x0000a000+48, STDERR_FILENO); // ptr to refcount/fd struct?
|
|
WriteLong(Memory, 0x0000a000+52, 0x00000000); //???
|
|
WriteLong(Memory, 0x0000a000+56, 0x00000000); //???
|
|
|
|
|
|
WriteWord(Memory, 0xf000, MPW::fQuit);
|
|
WriteWord(Memory, 0xf002, 0x4E75); // rts
|
|
|
|
WriteWord(Memory, 0xf004, MPW::fAccess);
|
|
WriteWord(Memory, 0xf006, 0x4E75); // rts
|
|
|
|
WriteWord(Memory, 0xf008, MPW::fClose);
|
|
WriteWord(Memory, 0xf00a, 0x4E75); // rts
|
|
|
|
WriteWord(Memory, 0xf00c, MPW::fRead);
|
|
WriteWord(Memory, 0xf00e, 0x4E75); // rts
|
|
|
|
WriteWord(Memory, 0xf010, MPW::fWrite);
|
|
WriteWord(Memory, 0xf012, 0x4E75); // rts
|
|
|
|
WriteWord(Memory, 0xf014, MPW::fIOCtl);
|
|
WriteWord(Memory, 0xf016, 0x4E75); // rts
|
|
|
|
|
|
// StdDevs (0x78 bytes)
|
|
// copied into a $78 byte buffer stored at _IntEnv+20
|
|
// this has pointers to read/write functions
|
|
// (although the executable has it's own functions as well...)
|
|
WriteLong(Memory, address + 0x20, 0x00003000);
|
|
|
|
WriteLong(Memory, 0x00003000+0, 0x46535953); // 'FSYS'
|
|
WriteLong(Memory, 0x00003000+4, 0xf004); //access
|
|
WriteLong(Memory, 0x00003000+8, 0xf008); // close
|
|
WriteLong(Memory, 0x00003000+12, 0xf00c); // read
|
|
WriteLong(Memory, 0x00003000+16, 0xf010); // write
|
|
WriteLong(Memory, 0x00003000+20, 0xf014); // ioctl
|
|
|
|
WriteLong(Memory, 0x00003000+24, 0x45434f4e); // 'ECON'
|
|
WriteLong(Memory, 0x00003000+28, 0); //access
|
|
WriteLong(Memory, 0x00003000+32, 0); // close
|
|
WriteLong(Memory, 0x00003000+36, 0); // read
|
|
WriteLong(Memory, 0x00003000+40, 0); // write
|
|
WriteLong(Memory, 0x00003000+44, 0); // ioctl
|
|
|
|
WriteLong(Memory, 0x00003000+48, 0x53595354); // 'SYST'
|
|
WriteLong(Memory, 0x00003000+52, 0); //access
|
|
WriteLong(Memory, 0x00003000+56, 0); // close
|
|
WriteLong(Memory, 0x00003000+60, 0); // read
|
|
WriteLong(Memory, 0x00003000+68, 0); // write
|
|
WriteLong(Memory, 0x00003000+72, 0); // ioctl
|
|
|
|
WriteLong(Memory, 0x00003000+76, 0);
|
|
WriteLong(Memory, 0x00003000+80, 0);
|
|
WriteLong(Memory, 0x00003000+84, 0);
|
|
WriteLong(Memory, 0x00003000+88, 0);
|
|
WriteLong(Memory, 0x00003000+92, 0);
|
|
WriteLong(Memory, 0x00003000+96, 0);
|
|
WriteLong(Memory, 0x00003000+100, 0);
|
|
WriteLong(Memory, 0x00003000+104, 0);
|
|
WriteLong(Memory, 0x00003000+108, 0);
|
|
WriteLong(Memory, 0x00003000+112, 0);
|
|
WriteLong(Memory, 0x00003000+116, 0);
|
|
|
|
|
|
|
|
// _RTInit fills in this location with &_IntEnv.
|
|
WriteLong(Memory, address + 0x24, 0x00004000);
|
|
WriteLong(Memory, address + 0x28, 0x00005000);
|
|
WriteLong(Memory, address + 0x2c, 0x00006000);
|
|
|
|
|
|
|
|
|
|
|
|
// 0x1c should have something, too .... :(
|
|
|
|
|
|
// 0x031a - Lo3Bytes
|
|
WriteLong(Memory, 0x031a, 0x00ffffff);
|
|
// 0x0a02 - OneOne
|
|
WriteLong(Memory, 0x0a02, 0x00010001);
|
|
// 0x0a06 - MinusOne
|
|
WriteLong(Memory, 0x0a06, 0xffffffff);
|
|
}
|
|
|
|
|
|
extern "C" { const char *TrapName(uint16_t trap); }
|
|
|
|
void LogToolBox(uint32_t pc, uint16_t trap)
|
|
{
|
|
const char *name;
|
|
|
|
name = TrapName(trap);
|
|
|
|
if (name)
|
|
{
|
|
fprintf(stderr, "$%08X %-51s ; %04X\n", pc, name, trap);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "$%08X Tool #$%04X ; %04X\n", pc, trap, trap);
|
|
}
|
|
}
|
|
|
|
void InstructionLogger()
|
|
{
|
|
|
|
static char strings[4][256];
|
|
for (unsigned j = 0; j < 4; ++j) strings[j][0] = 0;
|
|
|
|
uint32_t pc = cpuGetPC();
|
|
uint16_t opcode = ReadWord(Memory, pc);
|
|
|
|
if ((opcode & 0xf000) == 0xa000)
|
|
{
|
|
LogToolBox(pc, opcode);
|
|
return;
|
|
}
|
|
|
|
|
|
#if 0
|
|
fprintf(stderr, "D: %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
|
cpuGetDReg(0), cpuGetDReg(1), cpuGetDReg(2), cpuGetDReg(3),
|
|
cpuGetDReg(4), cpuGetDReg(5), cpuGetDReg(6), cpuGetDReg(7)
|
|
|
|
);
|
|
|
|
fprintf(stderr, "A: %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
|
cpuGetAReg(0), cpuGetAReg(1), cpuGetAReg(2), cpuGetAReg(3),
|
|
cpuGetAReg(4), cpuGetAReg(5), cpuGetAReg(6), cpuGetAReg(7)
|
|
);
|
|
#endif
|
|
|
|
cpuDisOpcode(pc, strings[0], strings[1], strings[2], strings[3]);
|
|
|
|
// address, data, instruction, operand
|
|
fprintf(stderr, "%s %-10s %-40s ; %s\n", strings[0], strings[2], strings[3], strings[1]);
|
|
|
|
// todo -- trace registers (only print changed registers?)
|
|
|
|
#if 0
|
|
if (pc >= 0x00010E94 && pc <= 0x00010FC0)
|
|
{
|
|
fprintf(stderr, "d7 = %08x\n", cpuGetDReg(7));
|
|
}
|
|
#endif
|
|
|
|
if (opcode == 0x4E75)
|
|
{
|
|
// check for MacsBug name after rts.
|
|
std::string s;
|
|
unsigned b = memoryReadByte(pc + 2);
|
|
if (b > 0x80)
|
|
{
|
|
b -= 0x80;
|
|
pc += 3;
|
|
s.reserve(b);
|
|
for (unsigned i = 0; i < b; ++i)
|
|
{
|
|
s.push_back(memoryReadByte(pc++));
|
|
}
|
|
fprintf(stderr, "%s\n\n", s.c_str());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void help()
|
|
{
|
|
|
|
|
|
}
|
|
|
|
bool parse_number(const char *input, uint32_t *dest)
|
|
{
|
|
char *end;
|
|
long value;
|
|
int base = 0;
|
|
|
|
// octal is dumb so don't allow it.
|
|
|
|
while (isspace(*input)) ++input;
|
|
if (*input == '0' && isdigit(input[1])) base = 10;
|
|
|
|
errno = 0;
|
|
value = strtol(input, &end, base);
|
|
|
|
if (errno || value < 0 || input == end)
|
|
{
|
|
fprintf(stderr, "%s - invalid input\n", input);
|
|
return false;
|
|
}
|
|
|
|
// M/K
|
|
if (*end)
|
|
{
|
|
int old = value;
|
|
if (strcasecmp(end, "M") == 0)
|
|
value *= 1024 * 1024;
|
|
else if (strcasecmp(end, "K") == 0)
|
|
value *= 1024;
|
|
else
|
|
{
|
|
fprintf(stderr, "%s - invalid input\n", input);
|
|
return false;
|
|
}
|
|
if (value < old)
|
|
{
|
|
// overflow
|
|
fprintf(stderr, "%s - invalid input\n", input);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (dest) *dest = value;
|
|
return true;
|
|
}
|
|
|
|
struct {
|
|
uint32_t ram;
|
|
uint32_t stack;
|
|
uint32_t machine;
|
|
bool traceCPU;
|
|
|
|
} Flags = { 16 * 1024 * 1024, 8 * 1024, 68030, false };
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// getopt...
|
|
|
|
enum
|
|
{
|
|
kTraceCPU = 1,
|
|
};
|
|
static struct option LongOpts[] =
|
|
{
|
|
{ "ram",required_argument, NULL, 'r' },
|
|
{ "stack", required_argument, NULL, 's' },
|
|
{ "machine", required_argument, NULL, 'm' },
|
|
{ "trace-cpu", no_argument, NULL, kTraceCPU },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
int c;
|
|
while ((c = getopt_long(argc, argv, "+hVm:r:s:", LongOpts, NULL)) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case kTraceCPU:
|
|
Flags.traceCPU = true;
|
|
break;
|
|
|
|
case 'm':
|
|
if (!parse_number(optarg, &Flags.machine))
|
|
exit(EX_CONFIG);
|
|
break;
|
|
|
|
case 'r':
|
|
if (!parse_number(optarg, &Flags.ram))
|
|
exit(EX_CONFIG);
|
|
break;
|
|
|
|
case 's':
|
|
if (!parse_number(optarg, &Flags.stack))
|
|
exit(EX_CONFIG);
|
|
break;
|
|
|
|
case ':':
|
|
case '?':
|
|
help();
|
|
exit(EX_USAGE);
|
|
break;
|
|
|
|
case 'h':
|
|
help();
|
|
exit(EX_OK);
|
|
break;
|
|
|
|
case 'V':
|
|
printf("mpw version 0");
|
|
exit(EX_OK);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (!argc)
|
|
{
|
|
help();
|
|
exit(EX_USAGE);
|
|
}
|
|
|
|
Memory = new uint8_t[Flags.ram];
|
|
MemorySize = Flags.ram;
|
|
|
|
cpuStartup();
|
|
cpuSetModel(3,0);
|
|
|
|
uint32_t address = load(argv[0]);
|
|
|
|
InitializeMPW(argc, argv);
|
|
|
|
|
|
if (!Flags.stack)
|
|
{
|
|
fprintf(stderr, "Invalid stack size\n");
|
|
exit(EX_CONFIG);
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> StackRange;
|
|
// allocate stack, set A7...
|
|
{
|
|
Flags.stack = (Flags.stack + 3) & ~0x03;
|
|
uint32_t address = EmulatedNewPtr(Flags.stack);
|
|
|
|
|
|
StackRange.first = address;
|
|
StackRange.second = address + Flags.stack;
|
|
|
|
// address grows down
|
|
// -4 is for the return address.
|
|
cpuSetAReg(7, address + Flags.stack - 4);
|
|
// return address.
|
|
WriteLong(Memory, address + Flags.stack - 4, 0x0a06); // MinusOne global :)
|
|
}
|
|
|
|
cpuSetALineExceptionFunc(ToolBox::dispatch);
|
|
cpuSetFLineExceptionFunc(MPW::dispatch);
|
|
|
|
|
|
/// ahhh... need to set PC after memory.
|
|
// for pre-fetch.
|
|
memorySetMemory(Memory, MemorySize);
|
|
memorySetGlobalLog(0x10000);
|
|
cpuInitializeFromNewPC(address);
|
|
|
|
MM::Init(Memory, MemorySize, HighWater);
|
|
|
|
if (Flags.traceCPU)
|
|
{
|
|
cpuSetInstructionLoggingFunc(InstructionLogger);
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
uint32_t pc = cpuGetPC();
|
|
if (pc == 0x00000000) break;
|
|
|
|
if (cpuGetStop()) break;
|
|
cpuExecuteInstruction();
|
|
}
|
|
|
|
// return value in mpwblock + 0x0e?
|
|
exit(0);
|
|
} |