mpw/bin/loader.cpp

1010 lines
21 KiB
C++
Raw Normal View History

2013-02-06 02:58:26 +00:00
// clang++ -c -std=c++11 -stdlib=libc++ -Wno-deprecated-declarations loader.cpp
2013-07-30 05:06:19 +00:00
/*
* Copyright (c) 2013, Kelvin W Sherlock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
2013-07-30 05:06:19 +00:00
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
2013-07-30 05:06:19 +00:00
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
2013-07-30 05:06:19 +00:00
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
2013-02-06 02:58:26 +00:00
#include <cstdint>
#include <cctype>
2013-02-06 02:58:26 +00:00
#include <cstring>
#include <string>
#include <vector>
2013-05-13 02:14:49 +00:00
#include <chrono>
2013-02-06 02:58:26 +00:00
#include <sysexits.h>
#include <getopt.h>
2013-02-10 01:06:58 +00:00
#include <libgen.h>
2013-02-11 01:19:53 +00:00
#include <unistd.h>
2013-02-18 05:06:27 +00:00
#include <sys/stat.h>
2013-02-06 02:58:26 +00:00
#include <CoreServices/CoreServices.h>
2013-02-07 01:07:27 +00:00
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
2013-02-07 01:36:56 +00:00
#include <cpu/fmem.h>
2013-02-06 02:58:26 +00:00
2013-02-08 00:22:13 +00:00
#include <toolbox/toolbox.h>
2013-02-08 03:49:20 +00:00
#include <toolbox/mm.h>
2013-07-14 20:45:06 +00:00
#include <toolbox/os.h>
2013-08-18 04:21:37 +00:00
#include <toolbox/loader.h>
2013-07-14 20:45:06 +00:00
2013-02-11 01:19:53 +00:00
#include <mpw/mpw.h>
2013-02-08 03:49:20 +00:00
#include <mplite/mplite.h>
2013-02-08 00:22:13 +00:00
2013-05-13 02:25:35 +00:00
#include <macos/sysequ.h>
2013-07-02 22:48:40 +00:00
#include <macos/traps.h>
2013-05-13 02:25:35 +00:00
#include "loader.h"
#include "debugger.h"
2013-02-16 04:47:26 +00:00
2015-02-02 00:42:16 +00:00
#include <cxx/string_splitter.h>
2013-08-18 04:21:37 +00:00
#define LOADER_LOAD
Settings Flags;
2013-02-16 04:47:26 +00:00
const uint32_t kGlobalSize = 0x10000;
// retained to make debugging easier.
2013-02-18 02:54:45 +00:00
uint8_t *Memory = nullptr;
2013-02-07 01:36:56 +00:00
uint32_t MemorySize = 0;
2013-02-06 02:58:26 +00:00
uint8_t ReadByte(const void *data, uint32_t offset)
{
offset &= 0xffffff;
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;
((uint8_t *)data)[offset] = value;
}
void WriteWord(void *data, uint32_t offset, uint16_t value)
{
offset &= 0xffffff;
((uint8_t *)data)[offset++] = value >> 8;
((uint8_t *)data)[offset++] = value;
}
void WriteLong(void *data, uint32_t offset, uint32_t value)
{
offset &= 0xffffff;
((uint8_t *)data)[offset++] = value >> 24;
((uint8_t *)data)[offset++] = value >> 16;
((uint8_t *)data)[offset++] = value >> 8;
((uint8_t *)data)[offset++] = value;
}
2013-08-18 04:21:37 +00:00
#ifndef LOADER_LOAD
2013-08-16 03:40:08 +00:00
void reloc1(const uint8_t *r, uint32_t address, uint32_t offset)
{
// %00000000 00000000 -> break
// %0xxxxxxx -> 7-bit value
// %1xxxxxxx xxxxxxxx -> 15-bit value
// %00000000 1xxxxxxx x{8} x{8} x{8} -> 31 bit value
// ^ that's what the documentation says..
2013-08-16 03:40:08 +00:00
// that's how the 32-bit bootstrap works
// DumpCode ignores the high 2 bytes.
for(;;)
{
uint32_t x;
uint32_t value;
x = *r++;
if (x == 0x00)
{
x = *r++;
if (x == 0x00) break;
2013-08-16 03:40:08 +00:00
x = (x << 8) | *r++;
x = (x << 8) | *r++;
x = (x << 8) | *r++;
}
else if (x & 0x80)
{
x &= 0x7f;
x = (x << 8) | *r++;
}
x <<= 1; // * 2
address += x;
value = memoryReadLong(address);
memoryWriteLong(value + offset, address);
}
}
// relocate a far model segment.
void relocate(uint32_t address, uint32_t size, uint32_t a5)
{
// see MacOS RT Architecture, 10-23 .. 10-26
uint32_t offset;
offset = memoryReadLong(address + 0x14);
if (memoryReadLong(address + 0x18) != a5 && offset != 0)
{
memoryWriteLong(a5, address + 0x18); // current value of A5
reloc1(memoryPointer(address + offset), address, a5);
}
offset = memoryReadLong(address + 0x1c);
if (memoryReadLong(address + 0x20) != address && offset != 0)
{
memoryWriteLong(address, address + 0x20); // segment load address.
reloc1(memoryPointer(address + offset), address, address + 0x28);
}
}
uint32_t load(const char *file)
2013-02-06 02:58:26 +00:00
{
ResFileRefNum refNum;
FSRef ref;
uint32_t returnAddress = 0;
2013-08-04 18:35:05 +00:00
struct SegInfo {
public:
SegInfo()
{}
SegInfo(uint32_t a, uint32_t s, bool f) :
address(a), size(s), farModel(f)
{}
uint32_t address = 0;
uint32_t size = 0;
bool farModel = 0;
};
std::vector< SegInfo> segments;
2013-02-06 02:58:26 +00:00
uint32_t a5 = 0;
uint32_t jtStart = 0;
uint32_t jtEnd = 0;
2013-07-02 22:48:40 +00:00
// todo -- call RM::Native to open and load the Resource File.
2013-02-06 02:58:26 +00:00
assert(FSPathMakeRef( (const UInt8 *)file, &ref, NULL) == noErr);
2013-08-04 18:35:05 +00:00
// todo -- if it wasn't a resource file, this will fail
// should provide a nicer error message.
2013-02-06 02:58:26 +00:00
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;
uint16_t error;
2013-02-06 02:58:26 +00:00
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;
// TODO -- verify numbers are on word boundary?
error = MM::Native::NewPtr(a5size, true, address);
if (error)
{
fprintf(stderr, "Memory allocation error.\n");
return 0;
}
2013-02-06 02:58:26 +00:00
a5 = address + below;
std::memcpy(memoryPointer(a5 + jtOffset), data + 16 , jtSize);
2013-02-06 02:58:26 +00:00
2013-08-04 18:35:05 +00:00
segments[resID] = SegInfo(address, a5size, false);
2013-02-06 02:58:26 +00:00
jtStart = a5 + jtOffset;
jtEnd = jtStart + jtSize;
2013-02-06 03:59:04 +00:00
2013-02-06 03:59:04 +00:00
// 0x0934 - CurJTOffset (16-bit)
2013-05-13 02:25:35 +00:00
memoryWriteWord(jtOffset, MacOS::CurJTOffset);
2013-02-07 04:44:33 +00:00
// 0x0904 -- CurrentA5 (32-bit)
2013-05-13 02:25:35 +00:00
memoryWriteLong(a5, MacOS::CurrentA5);
2013-02-07 01:07:27 +00:00
cpuSetAReg(5, a5);
2013-02-06 02:58:26 +00:00
}
else
{
2013-08-04 18:35:05 +00:00
bool farModel = false;
if (data[0] == 0xff && data[1] == 0xff) farModel = true;
error = MM::Native::NewPtr(size, false, address);
if (error)
{
fprintf(stderr, "Memory allocation error.\n");
return 0;
}
std::memcpy(memoryPointer(address), data, size);
2013-02-06 02:58:26 +00:00
2013-08-04 18:35:05 +00:00
segments[resID] = SegInfo(address, size, farModel);
2013-02-06 02:58:26 +00:00
}
ReleaseResource(h);
}
2013-02-06 02:58:26 +00:00
// now link the segment 0 jump table...
assert(a5);
2013-02-07 01:36:56 +00:00
bool first = true;
2013-08-04 18:35:05 +00:00
bool farModel = false;
2013-02-06 02:58:26 +00:00
for (; jtStart < jtEnd; jtStart += 8)
{
2013-08-04 18:35:05 +00:00
uint16_t seg;
uint32_t offset;
if (farModel)
{
seg = memoryReadWord(jtStart + 0);
offset = memoryReadLong(jtStart + 4);
2013-02-06 02:58:26 +00:00
2013-08-04 18:35:05 +00:00
assert(memoryReadWord(jtStart + 2) == 0xA9F0);
}
else
{
uint16_t farModelCheck;
offset = memoryReadWord(jtStart);
farModelCheck = memoryReadWord(jtStart + 2);
seg = memoryReadWord(jtStart + 4);
if (farModelCheck == 0xffff)
{
farModel = true;
continue;
}
2013-02-06 02:58:26 +00:00
2013-08-04 18:35:05 +00:00
assert(memoryReadWord(jtStart + 2) == 0x3F3C);
assert(memoryReadWord(jtStart + 6) == 0xA9F0);
}
assert(seg < segments.size());
2013-02-06 02:58:26 +00:00
auto p = segments[seg];
2013-08-04 18:35:05 +00:00
//assert(p.first); // missing segment?!
//assert(offset < p.second);
assert(p.address); // missing segment?!
assert(offset < p.size);
2013-02-06 02:58:26 +00:00
2013-08-04 18:35:05 +00:00
// +$4/$28 for the jump table info header.
2013-08-16 03:40:08 +00:00
uint32_t address = p.address + offset + (p.farModel ? 0x00 : 0x04); // was 0x28
2013-02-06 02:58:26 +00:00
// JMP absolute long
memoryWriteWord(0x4EF9, jtStart + 2);
memoryWriteLong(address, jtStart + 4);
2013-02-07 01:36:56 +00:00
if (first)
{
//cpuSetPC(address);
returnAddress = address;
2013-02-07 01:36:56 +00:00
first = false;
}
2013-02-06 02:58:26 +00:00
}
2013-08-16 03:40:08 +00:00
// far-model relocation. This happens here, after a5 is loaded.
for (const auto &seg : segments)
{
if (seg.farModel)
{
relocate(seg.address, seg.size, a5);
}
}
2013-02-07 01:36:56 +00:00
2013-02-06 02:58:26 +00:00
// set pc to jump table entry 0.
return returnAddress;
2013-02-06 02:58:26 +00:00
}
2013-08-18 04:21:37 +00:00
#endif
2013-02-11 01:19:53 +00:00
void GlobalInit()
{
// todo -- move this somewhere better.
// 0x031a - Lo3Bytes
2013-05-13 02:25:35 +00:00
memoryWriteLong(0x00ffffff, MacOS::Lo3Bytes);
// 0x0a02 - OneOne
2013-05-13 02:25:35 +00:00
memoryWriteLong(0x00010001, MacOS::OneOne);
// 0x0a06 - MinusOne
2013-05-13 02:25:35 +00:00
memoryWriteLong(0xffffffff, MacOS::MinusOne);
2013-02-16 04:47:26 +00:00
// 0x0130 -- ApplLimit
memoryWriteLong(Flags.memorySize - Flags.stackSize - 1, MacOS::ApplLimit);
2015-01-21 00:32:04 +00:00
memoryWriteLong(kGlobalSize, MacOS::ApplZone);
memoryWriteLong(Flags.memorySize - 1, MacOS::BufPtr);
2015-02-27 13:55:37 +00:00
//
memoryWriteLong(Flags.stackRange.first, MacOS::CurStackBase);
}
void CreateStack()
{
// allocate stack, set A7...
uint32_t address;
//uint16_t error;
#if 0
Flags.stackSize = (Flags.stackSize + 3) & ~0x03;
error = MM::Native::NewPtr(Flags.stackSize, true, address);
if (error)
{
fprintf(stderr, "Unable to allocate stack (%08x bytes)\n", Flags.stackSize);
exit(EX_CONFIG);
}
#else
address = Flags.memorySize - Flags.stackSize;
#endif
Flags.stackRange.first = address;
Flags.stackRange.second = address + Flags.stackSize;
// TODO -- is there a global for the max (min) stack pointer?
// address grows down
// -4 is for the return address.
cpuSetAReg(7, Flags.stackRange.second - 4);
// return address.
memoryWriteLong(MacOS::MinusOne, Flags.stackRange.second - 4); // MinusOne Global -- 0xffff ffff
2013-02-06 02:58:26 +00:00
}
2013-02-11 01:19:53 +00:00
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();
2013-02-11 01:19:53 +00:00
uint16_t opcode = ReadWord(Memory, pc);
if ((opcode & 0xf000) == 0xa000)
{
2013-02-11 01:19:53 +00:00
LogToolBox(pc, opcode);
return;
}
2013-02-16 04:47:26 +00:00
if (Flags.traceCPU)
{
cpuDisOpcode(pc, strings[0], strings[1], strings[2], strings[3]);
2013-02-16 04:47:26 +00:00
// address, data, instruction, operand
fprintf(stderr, "%s %-10s %-40s ; %s\n", strings[0], strings[2], strings[3], strings[1]);
2013-02-16 04:47:26 +00:00
// todo -- trace registers (only print changed registers?)
2013-02-16 04:47:26 +00:00
#if 0
if (pc >= 0x00010E94 && pc <= 0x00010FC0)
{
fprintf(stderr, "d7 = %08x\n", cpuGetDReg(7));
}
#endif
}
2013-02-19 23:27:33 +00:00
int mboffset = 0;
switch (opcode)
2013-02-11 01:19:53 +00:00
{
2013-02-19 23:27:33 +00:00
case 0x4E75: // rts
case 0x4ED0: // jmp (a0)
mboffset = 2;
break;
case 0x4E74: // rtd #
mboffset = 4;
break;
}
if (mboffset) // RTS or JMP (A0)
{
pc += mboffset;
2013-02-11 01:19:53 +00:00
// check for MacsBug name after rts.
std::string s;
2013-02-19 23:27:33 +00:00
unsigned b = memoryReadByte(pc);
2013-09-02 02:27:09 +00:00
if (b >= 0x80 && b <= 0x9f)
2013-02-11 01:19:53 +00:00
{
b -= 0x80;
2013-02-19 23:27:33 +00:00
pc++;
2013-09-02 02:27:09 +00:00
if (!b) b = memoryReadByte(pc++);
2013-02-11 01:19:53 +00:00
s.reserve(b);
for (unsigned i = 0; i < b; ++i)
{
s.push_back(memoryReadByte(pc++));
}
fprintf(stderr, "%s\n\n", s.c_str());
}
}
}
2013-07-02 22:48:40 +00:00
void MemoryLogger(uint32_t address, int size, int readWrite, uint32_t value)
{
if (address < kGlobalSize)
{
const char *name = GlobalName(address);
if (!name) name = "unknown";
fprintf(stderr, "%-20s %08x - ", name, address);
if (!readWrite)
2013-07-02 22:48:40 +00:00
{
switch(size)
{
case 1:
value = ReadByte(Memory, address);
break;
case 2:
value = ReadWord(Memory, address);
break;
case 4:
value = ReadLong(Memory, address);
break;
2013-07-02 22:48:40 +00:00
}
}
// todo -- for write, display previous value?
fprintf(stderr, " %s %d bytes", readWrite ? "write" : "read ", size);
switch(size)
2013-07-02 22:48:40 +00:00
{
case 1:
fprintf(stderr, " [%02x]\n", value);
break;
case 2:
fprintf(stderr, " [%04x]\n", value);
break;
case 3:
fprintf(stderr, " [%06x]\n", value);
break;
case 4:
fprintf(stderr, " [%08x]\n", value);
break;
default:
fprintf(stderr, "\n");
break;
2013-07-02 22:48:40 +00:00
}
}
}
2013-09-02 23:59:11 +00:00
void MidInstructionExceptionFunc()
{
// todo - cpu exception?
fprintf(stderr, "Mid Instruction Exception!\n");
//throw std::runtime_error::runtime_error("mid instruction exception");
}
2013-07-02 22:48:40 +00:00
2015-02-13 01:14:15 +00:00
#define MPW_VERSION "0.8.0"
2013-02-06 02:58:26 +00:00
void help()
{
2013-02-18 05:06:02 +00:00
printf("MPW " MPW_VERSION "\n");
printf("Usage: mpw [options] utility ...\n");
printf("\n");
printf(" --help display usage information\n");
printf(" --trace-cpu print cpu information\n");
printf(" --trace-macsbug print macsbug names\n");
printf(" --trace-toolbox print toolbox calls\n");
printf(" --trace-mpw print mpw calls\n");
2013-06-30 17:13:35 +00:00
printf(" --memory-stats print memory usage information\n");
2013-03-28 03:49:12 +00:00
printf(" --ram=<number> set the ram size. Default=16M\n");
printf(" --stack=<number> set the stack size. Default=8K\n");
printf("\n");
2013-02-06 02:58:26 +00:00
}
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);
2013-02-06 03:46:17 +00:00
if (errno || value < 0 || input == end)
2013-02-06 02:58:26 +00:00
{
fprintf(stderr, "%s - invalid input\n", input);
return false;
}
2013-02-06 03:46:17 +00:00
// M/K
if (*end)
{
int old = value;
if (strcasecmp(end, "M") == 0)
value *= 1024 * 1024;
else if (strcasecmp(end, "K") == 0)
value *= 1024;
else
2013-02-06 03:46:17 +00:00
{
fprintf(stderr, "%s - invalid input\n", input);
return false;
}
if (value < old)
{
// overflow
fprintf(stderr, "%s - invalid input\n", input);
return false;
}
}
2013-02-06 02:58:26 +00:00
if (dest) *dest = value;
return true;
}
2013-02-18 05:06:27 +00:00
bool file_exists(const std::string & name)
{
struct stat st;
2013-05-13 02:14:22 +00:00
return ::stat(name.c_str(), &st) == 0 && S_ISREG(st.st_mode);
2013-02-18 05:06:27 +00:00
}
std::string old_find_exe(const std::string &name)
2013-02-18 05:06:27 +00:00
{
if (file_exists(name)) return name;
// if name is a path, then it doesn't exist.
if (name.find('/') != name.npos) return std::string();
2013-06-09 18:47:27 +00:00
std::string path = MPW::RootDir();
if (path.empty()) return path;
if (path.back() != '/') path.push_back('/');
path.append("Tools/");
path.append(name);
if (file_exists(path)) return path;
return std::string();
2013-02-18 05:06:27 +00:00
}
2013-02-06 02:58:26 +00:00
// this needs to run *after* the MPW environment variables are loaded.
std::string find_exe(const std::string &name)
{
// if this is an absolute or relative name, return as-is.
if (name.find(':') != name.npos) {
std::string path = ToolBox::MacToUnix(name);
if (file_exists(path)) return path;
return "";
}
if (name.find('/') != name.npos) {
if (file_exists(name)) return name;
return "";
}
// otherwise, check the Commands variable for locations.
2015-02-02 00:42:16 +00:00
std::string commands = MPW::GetEnv("Commands");
if (commands.empty()) return old_find_exe(name);
// string is , separated, possibly in MacOS format.
2015-02-02 00:42:16 +00:00
for (auto iter = string_splitter(commands, ','); iter; ++iter)
{
if (iter->empty()) continue;
std::string path = *iter;
// convert to unix.
path = ToolBox::MacToUnix(path);
// should always have a length...
if (path.length() && path.back() != '/') path.push_back('/');
path.append(name);
if (file_exists(path)) return path;
}
2015-02-02 00:42:16 +00:00
return "";
}
void MainLoop()
{
#if 0
auto begin_emu_time = std::chrono::high_resolution_clock::now();
fprintf(stderr, "Begin Emulation Time: %20lld\n", (begin_emu_time - start_time).count());
#endif
uint64_t cycles = 0;
for (;;)
{
uint32_t pc = cpuGetPC();
uint32_t sp = cpuGetAReg(7);
if (pc == 0x00000000)
{
fprintf(stderr, "Exiting - PC = 0\n");
exit(EX_SOFTWARE);
}
if (sp < Flags.stackRange.first)
{
fprintf(stderr, "Stack overflow error - please increase the stack size (--stack=size)\n");
fprintf(stderr, "Current stack size is 0x%06x\n", Flags.stackSize);
exit(EX_SOFTWARE);
}
if (sp > Flags.stackRange.second)
{
fprintf(stderr, "Stack underflow error\n");
exit(EX_SOFTWARE);
}
if (cpuGetStop()) break; // will this also be set by an interrupt?
#ifndef CPU_INSTRUCTION_LOGGING
if (Flags.traceCPU || Flags.traceMacsbug)
{
InstructionLogger();
}
#endif
cycles += cpuExecuteInstruction();
}
#if 0
auto end_emu_time = std::chrono::high_resolution_clock::now();
fprintf(stderr, " End Emulation Time: %20lld\n", (end_emu_time - start_time).count());
fprintf(stderr, " Cycles: %20lld\n", cycles);
#endif
}
2013-02-06 02:58:26 +00:00
int main(int argc, char **argv)
{
// getopt...
2013-02-07 01:36:56 +00:00
enum
{
kTraceCPU = 1,
2013-02-16 04:47:26 +00:00
kTraceMacsBug,
kTraceGlobals,
2013-02-16 23:32:37 +00:00
kTraceToolBox,
kTraceMPW,
kDebugger,
2013-02-18 02:54:45 +00:00
kMemoryStats,
kShell,
2013-02-07 01:36:56 +00:00
};
static struct option LongOpts[] =
2013-02-06 02:58:26 +00:00
{
{ "ram",required_argument, NULL, 'r' },
{ "stack", required_argument, NULL, 's' },
{ "machine", required_argument, NULL, 'm' },
2013-02-07 01:36:56 +00:00
{ "trace-cpu", no_argument, NULL, kTraceCPU },
2013-02-16 04:47:26 +00:00
{ "trace-macsbug", no_argument, NULL, kTraceMacsBug },
{ "trace-globals", no_argument, NULL, kTraceGlobals },
2013-02-16 23:32:37 +00:00
{ "trace-toolbox", no_argument, NULL, kTraceToolBox },
2013-02-18 02:54:45 +00:00
{ "trace-tools", no_argument, NULL, kTraceToolBox },
2013-02-16 23:32:37 +00:00
{ "trace-mpw", no_argument, NULL, kTraceMPW },
2013-02-18 02:54:45 +00:00
{ "debug", no_argument, NULL, kDebugger },
{ "debugger", no_argument, NULL, kDebugger },
2013-02-18 02:54:45 +00:00
{ "memory-stats", no_argument, NULL, kMemoryStats },
2013-02-06 02:58:26 +00:00
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "shell", no_argument, NULL, kShell },
2013-02-06 02:58:26 +00:00
{ NULL, 0, NULL, 0 }
};
2013-05-13 02:14:49 +00:00
//auto start_time = std::chrono::high_resolution_clock::now();
std::vector<std::string> defines;
2013-02-06 02:58:26 +00:00
int c;
while ((c = getopt_long(argc, argv, "+hVm:r:s:D:", LongOpts, NULL)) != -1)
2013-02-06 02:58:26 +00:00
{
switch(c)
{
2013-02-07 01:36:56 +00:00
case kTraceCPU:
Flags.traceCPU = true;
break;
2013-02-06 02:58:26 +00:00
2013-02-16 04:47:26 +00:00
case kTraceMacsBug:
Flags.traceMacsbug = true;
break;
case kTraceGlobals:
Flags.traceGlobals = true;
break;
2013-02-16 23:32:37 +00:00
case kTraceToolBox:
Flags.traceToolBox = true;
break;
case kTraceMPW:
Flags.traceMPW = true;
break;
2013-02-18 02:54:45 +00:00
case kMemoryStats:
Flags.memoryStats = true;
break;
2013-02-16 23:32:37 +00:00
case kDebugger:
Flags.debugger = true;
break;
case kShell:
break;
2013-02-06 02:58:26 +00:00
case 'm':
if (!parse_number(optarg, &Flags.machine))
exit(EX_CONFIG);
break;
case 'r':
if (!parse_number(optarg, &Flags.memorySize))
2013-02-06 02:58:26 +00:00
exit(EX_CONFIG);
break;
case 's':
if (!parse_number(optarg, &Flags.stackSize))
2013-02-06 02:58:26 +00:00
exit(EX_CONFIG);
break;
case 'D':
defines.push_back(optarg);
break;
2013-02-06 02:58:26 +00:00
case ':':
case '?':
help();
exit(EX_USAGE);
break;
case 'h':
help();
exit(EX_OK);
break;
case 'V':
2013-02-18 05:06:02 +00:00
printf("mpw version " MPW_VERSION "\n");
2013-02-06 02:58:26 +00:00
exit(EX_OK);
break;
}
}
argc -= optind;
argv += optind;
if (!argc)
{
help();
exit(EX_USAGE);
}
Flags.stackSize = (Flags.stackSize + 0xff) & ~0xff;
Flags.memorySize = (Flags.memorySize + 0xff) & ~0xff;
if (Flags.stackSize < 0x100)
{
fprintf(stderr, "Invalid stack size\n");
exit(EX_CONFIG);
}
if (Flags.memorySize < 0x200)
{
fprintf(stderr, "Invalid ram size\n");
exit(EX_CONFIG);
}
if (Flags.stackSize >= Flags.memorySize)
{
fprintf(stderr, "Invalid stack/ram size\n");
exit(EX_CONFIG);
}
MPW::InitEnvironment(defines);
std::string command(argv[0]); // InitMPW updates argv...
2013-02-18 05:06:27 +00:00
command = find_exe(command);
if (command.empty())
{
2013-07-18 02:49:41 +00:00
std::string mpw = MPW::RootDir();
2013-02-18 05:06:27 +00:00
fprintf(stderr, "Unable to find command %s\n", argv[0]);
2013-07-18 02:49:41 +00:00
fprintf(stderr, "$MPW = %s\n", mpw.c_str());
fprintf(stderr, "$Commands = %s\n", MPW::GetEnv("Commands").c_str());
2013-02-18 05:06:27 +00:00
exit(EX_USAGE);
}
argv[0] = ::strdup(command.c_str()); // hmm.. could setenv(mpw_command) instead.
// move to CreateRam()
Memory = Flags.memory = new uint8_t[Flags.memorySize];
MemorySize = Flags.memorySize;
2013-02-06 03:59:04 +00:00
/// ahhh... need to set PC after memory.
// for pre-fetch.
memorySetMemory(Memory, MemorySize);
2013-02-06 02:58:26 +00:00
MM::Init(Memory, MemorySize, kGlobalSize, Flags.stackSize);
2013-07-14 20:45:06 +00:00
OS::Init();
Squashed commit of the following: commit f4807cd071ba5c4ce9ba4aa8c906ba335da07a2d Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Feb 13 16:09:55 2015 -0500 merge in missing tool calls commit 6830ab51ce7c48e3d0d1352f04df8df914146b3e Merge: 1675a47 24c17a9 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Feb 13 16:06:05 2015 -0500 Merge branch 'feature_trap_address_2015' into merge_dispatcher Conflicts: bin/loader.cpp toolbox/rm.cpp toolbox/toolbox.cpp commit 24c17a9a888b2a95e99761ee7ee8b15874a9ce42 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Feb 13 13:22:07 2015 -0500 SetOSTrapAddress stub commit b255937e6c9a6765a9763b011bd99c3f675d997c Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sat Jan 24 14:40:45 2015 -0500 Use the $Commands environment variable as a list of directories where commands may be located. Conflicts: bin/loader.cpp commit a3925747cef0203d178091cdd827c50ded134484 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Jan 23 10:26:52 2015 -0500 HWPriv OS Trap commit 16d0038fd1b9b6db3e154ab5e810624ab7d3ca08 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Jan 23 10:26:40 2015 -0500 HWPriv OS Trap commit bf92f806326925b68b497127da421f833e2cdba2 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Wed Jan 21 17:34:30 2015 -0500 RM::GetNamedResource commit 540490d0c5ba7c8310271b1c7fd68259a77a2f28 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Wed Jan 21 14:31:03 2015 -0500 GetNamedResource trap commit 09aa8d0f2600668b37a51e7872db9a9f9c9ea4ef Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue Jan 20 19:32:04 2015 -0500 Set ApplZone global (ARMCFront) commit 45fa54abca52a80c980c870f43f1f82225d7c43d Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue Jan 20 13:58:24 2015 -0500 update test makefile commit b1e6635630d2fc112b613312000e091ff7f297bc Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue Jan 20 13:35:07 2015 -0500 SysEnvirons ($a090) OS Trap commit a5126544b8bdda155a5f8e9395d547a7bf3eae94 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon Jan 19 15:02:29 2015 -0500 undo HGetVolume cwd, for now commit 912c8748254a83d3a5eacf776a6b6b3a4439aa75 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon Jan 19 10:55:58 2015 -0500 Fix SetPtrSize commit c0fe74c1b81d1782a61240a18ecc4e51a3ecb88c Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun Jan 18 17:31:09 2015 -0500 support the auto-pop bit for tool calls (MPW Pascal IIgs 1.0b1) commit 2e9ab5200c285bb97bc15a11dcc7e7127e779245 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun Jan 18 15:20:38 2015 -0500 update dispatch code... commit c7c548e5ac2096e29aebf532e0eb69e2227bdecd Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun Jan 18 14:48:22 2015 -0500 add back new tool calls. commit cbb554174ef186b8b74383596212d9b8f04ebfea Merge: 44d19f7 5e616d3 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun Jan 18 14:43:58 2015 -0500 Merge branch 'feature_trap_address' into feature_trap_address_2015 Conflicts: toolbox/CMakeLists.txt toolbox/os.cpp toolbox/toolbox.cpp commit 5e616d353b1421b96cc4e3a4f82d4422911d1243 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun Dec 14 18:01:38 2014 -0500 new dispatcher. commit ea5f2139217df8aebe975b8e4680080d58a772bd Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon Dec 8 11:42:23 2014 -0500 get ready for trap overrides. commit 17c5b40ac866a7adaa559bc9164ffe32e926cc96 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Dec 5 14:39:17 2014 -0500 remove dead code commit fc7df738cc9dd72d65ba656df71dd6aa124ce8af Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Dec 5 14:32:18 2014 -0500 display trap name for GetToolTrap, etc.
2015-02-13 21:11:55 +00:00
ToolBox::Init();
MPW::Init(argc, argv);
cpuStartup();
cpuSetModel(3,0);
2013-02-06 02:58:26 +00:00
CreateStack();
2013-02-07 01:07:27 +00:00
2013-08-18 04:21:37 +00:00
#ifdef LOADER_LOAD
uint16_t err = Loader::Native::LoadFile(command);
if (err) exit(EX_CONFIG);
#else
uint32_t address = load(command.c_str());
if (!address) exit(EX_CONFIG);
2013-08-18 04:21:37 +00:00
#endif
GlobalInit();
2013-02-11 01:19:53 +00:00
cpuSetALineExceptionFunc(ToolBox::dispatch);
cpuSetFLineExceptionFunc(MPW::dispatch);
2013-09-02 23:59:11 +00:00
cpuSetMidInstructionExceptionFunc(MidInstructionExceptionFunc);
2013-07-02 22:48:40 +00:00
if (Flags.traceGlobals) //memorySetGlobalLog(kGlobalSize);
memorySetLoggingFunc(MemoryLogger);
MPW::Trace = Flags.traceMPW;
ToolBox::Trace = Flags.traceToolBox;
2013-02-16 04:47:26 +00:00
if (Flags.traceCPU || Flags.traceMacsbug)
2013-02-07 01:07:27 +00:00
{
2013-05-13 02:14:49 +00:00
#ifdef CPU_INSTRUCTION_LOGGING
cpuSetInstructionLoggingFunc(InstructionLogger);
2013-05-13 02:14:49 +00:00
#endif
// else do it manually below.
}
2013-08-18 04:21:37 +00:00
#ifndef LOADER_LOAD
cpuInitializeFromNewPC(address);
2013-08-18 04:21:37 +00:00
#endif
if (Flags.debugger) Debug::Shell();
else MainLoop();
2013-05-16 04:10:26 +00:00
2013-07-02 22:48:40 +00:00
2013-05-13 02:14:49 +00:00
2013-02-18 02:54:45 +00:00
if (Flags.memoryStats)
{
MM::Native::PrintMemoryStats();
}
2013-02-17 20:49:28 +00:00
uint32_t rv = MPW::ExitStatus();
if (rv > 0xff) rv = 0xff;
2013-05-13 02:14:49 +00:00
2013-02-17 20:49:28 +00:00
exit(rv);
}