macemu/SheepShaver/src/emul_op.cpp

497 lines
13 KiB
C++
Raw Normal View History

2002-02-04 16:58:13 +00:00
/*
* emul_op.cpp - 68k opcodes for ROM patches
*
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
*/
#include <stdio.h>
#include "sysdeps.h"
#include "main.h"
#include "version.h"
#include "prefs.h"
#include "cpu_emulation.h"
#include "xlowmem.h"
#include "xpram.h"
#include "timer.h"
#include "adb.h"
#include "sony.h"
#include "disk.h"
#include "cdrom.h"
#include "scsi.h"
#include "video.h"
#include "audio.h"
#include "ether.h"
#include "serial.h"
#include "clip.h"
#include "extfs.h"
#include "macos_util.h"
#include "rom_patches.h"
#include "rsrc_patches.h"
#include "name_registry.h"
#include "user_strings.h"
#include "emul_op.h"
#include "thunks.h"
2002-02-04 16:58:13 +00:00
#define DEBUG 0
#include "debug.h"
// TVector of MakeExecutable
static uint32 MakeExecutableTvec;
2002-02-04 16:58:13 +00:00
/*
* Execute EMUL_OP opcode (called by 68k emulator)
*/
void EmulOp(M68kRegisters *r, uint32 pc, int selector)
{
D(bug("EmulOp %04x at %08x\n", selector, pc));
switch (selector) {
case OP_BREAK: // Breakpoint
printf("*** Breakpoint\n");
Dump68kRegs(r);
break;
case OP_XPRAM1: { // Read/write from/to XPRam
uint32 len = r->d[3];
uint8 *adr = Mac2HostAddr(r->a[3]);
D(bug("XPRAMReadWrite d3: %08lx, a3: %p\n", len, adr));
int ofs = len & 0xffff;
len >>= 16;
if (len & 0x8000) {
len &= 0x7fff;
for (uint32 i=0; i<len; i++)
XPRAM[((ofs + i) & 0xff) + 0x1300] = *adr++;
} else {
for (uint32 i=0; i<len; i++)
*adr++ = XPRAM[((ofs + i) & 0xff) + 0x1300];
}
break;
}
case OP_XPRAM2: // Read from XPRam
r->d[1] = XPRAM[(r->d[1] & 0xff) + 0x1300];
break;
case OP_XPRAM3: // Write to XPRam
XPRAM[(r->d[1] & 0xff) + 0x1300] = r->d[2];
break;
case OP_NVRAM1: { // Read from NVRAM
int ofs = r->d[0];
r->d[0] = XPRAM[ofs & 0x1fff];
bool localtalk = !(XPRAM[0x13e0] || XPRAM[0x13e1]); // LocalTalk enabled?
switch (ofs) {
case 0x13e0: // Disable LocalTalk (use EtherTalk instead)
if (localtalk)
r->d[0] = 0x00;
break;
case 0x13e1:
if (localtalk)
r->d[0] = 0x01;
break;
case 0x13e2:
if (localtalk)
r->d[0] = 0x00;
break;
case 0x13e3:
if (localtalk)
r->d[0] = 0x0a;
break;
}
break;
}
case OP_NVRAM2: // Write to NVRAM
XPRAM[r->d[0] & 0x1fff] = r->d[1];
break;
case OP_NVRAM3: // Read/write from/to NVRAM
if (r->d[3]) {
r->d[0] = XPRAM[(r->d[4] + 0x1300) & 0x1fff];
} else {
XPRAM[(r->d[4] + 0x1300) & 0x1fff] = r->d[5];
r->d[0] = 0;
}
break;
case OP_FIX_MEMTOP: // Fixes MemTop in BootGlobs during startup
D(bug("Fix MemTop\n"));
WriteMacInt32(BootGlobsAddr - 20, RAMBase + RAMSize); // MemTop
r->a[6] = RAMBase + RAMSize;
break;
case OP_FIX_MEMSIZE: { // Fixes physical/logical RAM size during startup
D(bug("Fix MemSize\n"));
uint32 diff = ReadMacInt32(0x1ef8) - ReadMacInt32(0x1ef4);
WriteMacInt32(0x1ef8, RAMSize); // Physical RAM size
WriteMacInt32(0x1ef4, RAMSize - diff); // Logical RAM size
break;
}
case OP_FIX_BOOTSTACK: // Fixes boot stack pointer in boot 3 resource
D(bug("Fix BootStack\n"));
r->a[1] = r->a[7] = RAMBase + RAMSize * 3 / 4;
break;
case OP_SONY_OPEN: // Floppy driver functions
r->d[0] = SonyOpen(r->a[0], r->a[1]);
break;
case OP_SONY_PRIME:
r->d[0] = SonyPrime(r->a[0], r->a[1]);
break;
case OP_SONY_CONTROL:
r->d[0] = SonyControl(r->a[0], r->a[1]);
break;
case OP_SONY_STATUS:
r->d[0] = SonyStatus(r->a[0], r->a[1]);
break;
case OP_DISK_OPEN: // Disk driver functions
r->d[0] = DiskOpen(r->a[0], r->a[1]);
break;
case OP_DISK_PRIME:
r->d[0] = DiskPrime(r->a[0], r->a[1]);
break;
case OP_DISK_CONTROL:
r->d[0] = DiskControl(r->a[0], r->a[1]);
break;
case OP_DISK_STATUS:
r->d[0] = DiskStatus(r->a[0], r->a[1]);
break;
case OP_CDROM_OPEN: // CD-ROM driver functions
r->d[0] = CDROMOpen(r->a[0], r->a[1]);
break;
case OP_CDROM_PRIME:
r->d[0] = CDROMPrime(r->a[0], r->a[1]);
break;
case OP_CDROM_CONTROL:
r->d[0] = CDROMControl(r->a[0], r->a[1]);
break;
case OP_CDROM_STATUS:
r->d[0] = CDROMStatus(r->a[0], r->a[1]);
break;
case OP_AUDIO_DISPATCH: // Audio component functions
r->d[0] = AudioDispatch(r->a[3], r->a[4]);
break;
case OP_SOUNDIN_OPEN: // Sound input driver functions
r->d[0] = SoundInOpen(r->a[0], r->a[1]);
break;
case OP_SOUNDIN_PRIME:
r->d[0] = SoundInPrime(r->a[0], r->a[1]);
break;
case OP_SOUNDIN_CONTROL:
r->d[0] = SoundInControl(r->a[0], r->a[1]);
break;
case OP_SOUNDIN_STATUS:
r->d[0] = SoundInStatus(r->a[0], r->a[1]);
break;
case OP_SOUNDIN_CLOSE:
r->d[0] = SoundInClose(r->a[0], r->a[1]);
break;
case OP_ADBOP: // ADBOp() replacement
ADBOp(r->d[0], Mac2HostAddr(ReadMacInt32(r->a[0])));
break;
case OP_INSTIME: // InsTime() replacement
r->d[0] = InsTime(r->a[0], r->d[1]);
break;
case OP_RMVTIME: // RmvTime() replacement
r->d[0] = RmvTime(r->a[0]);
break;
case OP_PRIMETIME: // PrimeTime() replacement
r->d[0] = PrimeTime(r->a[0], r->d[0]);
break;
case OP_MICROSECONDS: // Microseconds() replacement
Microseconds(r->a[0], r->d[0]);
break;
Patch for copying and pasting styled text in Basilisk II / SheepShaver Added code to parse the Classic Mac OS 'styl' resources, allowing formatted text to be copied and pasted out of SheepShaver, not just plain text. In order to do this, I made some changes to the emul_op mechanism, patching ZeroScrap() in addition to the scrap methods that were already being patched. The reason for this is that since we need to read data from multiple items that are on the clipboard at once, we cannot simply assume a zero at the beginning of each PutScrap() operation. This patch uses RTF to store styled text on the host side; unfortunately, since the APIs to convert to and from RTF data are in Cocoa but not in CoreFoundation, I had to write the new portions in Objective-C rather than C, and changed the extension from .cpp to .mm accordingly. In the future, if we are confident that this file will only be used on Mac OS X 10.6 and up, we can rewrite the Pasteboard Manager code to use NSPasteboardReading/Writing instead. This would allow us to read and write NSAttributedString objects directly to and from the pasteboard, which would make sure we were always using the OS's preferred rich text format internally instead of hard-coding it specifically to RTF as in the current implementation. I believe that this patch should also fix the problem Ronald reported with copying accented characters. Since I am new to 68k assembly and the emul_op mechanism, I would appreciate if someone could double-check all my changes to make sure that I have done everything correctly. Thanks, Charles
2012-06-29 22:53:13 +00:00
case OP_ZERO_SCRAP: // ZeroScrap() patch
ZeroScrap();
break;
2002-02-04 16:58:13 +00:00
case OP_PUT_SCRAP: // PutScrap() patch
PutScrap(ReadMacInt32(r->a[7] + 8), Mac2HostAddr(ReadMacInt32(r->a[7] + 4)), ReadMacInt32(r->a[7] + 12));
break;
case OP_GET_SCRAP: // GetScrap() patch
GetScrap((void **)Mac2HostAddr(ReadMacInt32(r->a[7] + 4)), ReadMacInt32(r->a[7] + 8), ReadMacInt32(r->a[7] + 12));
break;
case OP_DEBUG_STR: // DebugStr() shows warning message
if (PrefsFindBool("nogui")) {
uint8 *pstr = Mac2HostAddr(ReadMacInt32(r->a[7] + 4));
char str[256];
int i;
for (i=0; i<pstr[0]; i++)
str[i] = pstr[i+1];
str[i] = 0;
WarningAlert(str);
}
break;
case OP_INSTALL_DRIVERS: { // Patch to install our own drivers during startup
// Install drivers
InstallDrivers();
// Patch MakeExecutable()
MakeExecutableTvec = FindLibSymbol("\023PrivateInterfaceLib", "\016MakeExecutable");
D(bug("MakeExecutable TVECT at %08x\n", MakeExecutableTvec));
WriteMacInt32(MakeExecutableTvec, NativeFunction(NATIVE_MAKE_EXECUTABLE));
#if !EMULATED_PPC
WriteMacInt32(MakeExecutableTvec + 4, (uint32)TOC);
2002-02-04 16:58:13 +00:00
#endif
// Patch DebugStr()
static const uint8 proc_template[] = {
2011-12-27 07:22:18 +00:00
M68K_EMUL_OP_DEBUG_STR >> 8, M68K_EMUL_OP_DEBUG_STR & 0xFF,
0x4e, 0x74, // rtd #4
0x00, 0x04
2002-02-04 16:58:13 +00:00
};
BUILD_SHEEPSHAVER_PROCEDURE(proc);
WriteMacInt32(0x1dfc, proc);
2002-02-04 16:58:13 +00:00
break;
}
case OP_NAME_REGISTRY: // Patch Name Registry and initialize CallUniversalProc
r->d[0] = (uint32)-1;
PatchNameRegistry();
InitCallUniversalProc();
break;
case OP_RESET: // Early in MacOS reset
D(bug("*** RESET ***\n"));
TimerReset();
MacOSUtilReset();
AudioReset();
// Enable DR emulator (disabled for now)
if (PrefsFindBool("jit68k") && 0) {
D(bug("DR activated\n"));
WriteMacInt32(KernelDataAddr + 0x17a0, 3); // Prepare for DR emulator activation
WriteMacInt32(KernelDataAddr + 0x17c0, DR_CACHE_BASE);
WriteMacInt32(KernelDataAddr + 0x17c4, DR_CACHE_SIZE);
WriteMacInt32(KernelDataAddr + 0x1b04, DR_CACHE_BASE);
WriteMacInt32(KernelDataAddr + 0x1b00, DR_EMULATOR_BASE);
[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
memcpy((void *)DR_EMULATOR_BASE, (void *)(ROMBase + 0x370000), DR_EMULATOR_SIZE);
MakeExecutable(0, DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
}
2002-02-04 16:58:13 +00:00
break;
case OP_IRQ: // Level 1 interrupt
WriteMacInt16(ReadMacInt32(KernelDataAddr + 0x67c), 0); // Clear interrupt
r->d[0] = 0;
if (HasMacStarted()) {
if (InterruptFlags & INTFLAG_VIA) {
ClearInterruptFlag(INTFLAG_VIA);
#if !PRECISE_TIMING
TimerInterrupt();
#endif
ExecuteNative(NATIVE_VIDEO_VBL);
2002-02-04 16:58:13 +00:00
static int tick_counter = 0;
if (++tick_counter >= 60) {
tick_counter = 0;
SonyInterrupt();
DiskInterrupt();
CDROMInterrupt();
}
r->d[0] = 1; // Flag: 68k interrupt routine executes VBLTasks etc.
}
if (InterruptFlags & INTFLAG_SERIAL) {
ClearInterruptFlag(INTFLAG_SERIAL);
SerialInterrupt();
}
if (InterruptFlags & INTFLAG_ETHER) {
ClearInterruptFlag(INTFLAG_ETHER);
ExecuteNative(NATIVE_ETHER_IRQ);
2002-02-04 16:58:13 +00:00
}
if (InterruptFlags & INTFLAG_TIMER) {
ClearInterruptFlag(INTFLAG_TIMER);
TimerInterrupt();
}
if (InterruptFlags & INTFLAG_AUDIO) {
ClearInterruptFlag(INTFLAG_AUDIO);
AudioInterrupt();
}
if (InterruptFlags & INTFLAG_ADB) {
ClearInterruptFlag(INTFLAG_ADB);
ADBInterrupt();
}
} else
r->d[0] = 1;
break;
case OP_SCSI_DISPATCH: { // SCSIDispatch() replacement
uint32 ret = ReadMacInt32(r->a[7]);
uint16 sel = ReadMacInt16(r->a[7] + 4);
r->a[7] += 6;
// D(bug("SCSIDispatch(%d)\n", sel));
int stack;
switch (sel) {
case 0: // SCSIReset
WriteMacInt16(r->a[7], SCSIReset());
stack = 0;
break;
case 1: // SCSIGet
WriteMacInt16(r->a[7], SCSIGet());
stack = 0;
break;
case 2: // SCSISelect
case 11: // SCSISelAtn
WriteMacInt16(r->a[7] + 2, SCSISelect(ReadMacInt8(r->a[7] + 1)));
stack = 2;
break;
case 3: // SCSICmd
WriteMacInt16(r->a[7] + 6, SCSICmd(ReadMacInt16(r->a[7]), Mac2HostAddr(ReadMacInt32(r->a[7] + 2))));
stack = 6;
break;
case 4: // SCSIComplete
WriteMacInt16(r->a[7] + 12, SCSIComplete(ReadMacInt32(r->a[7]), ReadMacInt32(r->a[7] + 4), ReadMacInt32(r->a[7] + 8)));
stack = 12;
break;
case 5: // SCSIRead
case 8: // SCSIRBlind
WriteMacInt16(r->a[7] + 4, SCSIRead(ReadMacInt32(r->a[7])));
stack = 4;
break;
case 6: // SCSIWrite
case 9: // SCSIWBlind
WriteMacInt16(r->a[7] + 4, SCSIWrite(ReadMacInt32(r->a[7])));
stack = 4;
break;
case 10: // SCSIStat
WriteMacInt16(r->a[7], SCSIStat());
stack = 0;
break;
case 12: // SCSIMsgIn
WriteMacInt16(r->a[7] + 4, 0);
stack = 4;
break;
case 13: // SCSIMsgOut
WriteMacInt16(r->a[7] + 2, 0);
stack = 2;
break;
case 14: // SCSIMgrBusy
WriteMacInt16(r->a[7], SCSIMgrBusy());
stack = 0;
break;
default:
printf("FATAL: SCSIDispatch: illegal selector\n");
stack = 0;
//!! SysError(12)
}
r->a[0] = ret;
r->a[7] += stack;
break;
}
case OP_SCSI_ATOMIC: // SCSIAtomic() replacement
D(bug("SCSIAtomic\n"));
r->d[0] = (uint32)-7887;
break;
case OP_CHECK_SYSV: { // Check we are not using MacOS < 8.1 with a NewWorld ROM
r->a[1] = r->d[1];
r->a[0] = ReadMacInt32(r->d[1]);
uint32 sysv = ReadMacInt16(r->a[0]);
D(bug("Detected MacOS version %d.%d.%d\n", (sysv >> 8) & 0xf, (sysv >> 4) & 0xf, sysv & 0xf));
if (ROMType == ROMTYPE_NEWWORLD && sysv < 0x0801)
r->d[1] = 0;
break;
}
2002-02-04 16:58:13 +00:00
case OP_NTRB_17_PATCH:
r->a[2] = ReadMacInt32(r->a[7]);
r->a[7] += 4;
if (ReadMacInt16(r->a[2] + 6) == 17)
PatchNativeResourceManager();
break;
case OP_NTRB_17_PATCH2:
r->a[7] += 8;
PatchNativeResourceManager();
break;
case OP_NTRB_17_PATCH3:
r->a[2] = ReadMacInt32(r->a[7]);
r->a[7] += 4;
D(bug("%d %d\n", ReadMacInt16(r->a[2]), ReadMacInt16(r->a[2] + 6)));
if (ReadMacInt16(r->a[2]) == 11 && ReadMacInt16(r->a[2] + 6) == 17)
PatchNativeResourceManager();
break;
2004-06-20 19:10:02 +00:00
case OP_NTRB_17_PATCH4:
r->d[0] = ReadMacInt16(r->a[7]);
r->a[7] += 2;
D(bug("%d %d\n", ReadMacInt16(r->a[2]), ReadMacInt16(r->a[2] + 6)));
if (ReadMacInt16(r->a[2]) == 11 && ReadMacInt16(r->a[2] + 6) == 17)
PatchNativeResourceManager();
break;
2002-02-04 16:58:13 +00:00
case OP_CHECKLOAD: { // vCheckLoad() patch
uint32 type = ReadMacInt32(r->a[7]);
r->a[7] += 4;
int16 id = ReadMacInt16(r->a[2]);
if (r->a[0] == 0)
break;
uint32 adr = ReadMacInt32(r->a[0]);
if (adr == 0)
break;
uint16 *p = (uint16 *)Mac2HostAddr(adr);
uint32 size = ReadMacInt32(adr - 8) & 0xffffff;
CheckLoad(type, id, p, size);
break;
}
case OP_EXTFS_COMM: // External file system routines
WriteMacInt16(r->a[7] + 14, ExtFSComm(ReadMacInt16(r->a[7] + 12), ReadMacInt32(r->a[7] + 8), ReadMacInt32(r->a[7] + 4)));
break;
case OP_EXTFS_HFS:
WriteMacInt16(r->a[7] + 20, ExtFSHFS(ReadMacInt32(r->a[7] + 16), ReadMacInt16(r->a[7] + 14), ReadMacInt32(r->a[7] + 10), ReadMacInt32(r->a[7] + 6), ReadMacInt16(r->a[7] + 4)));
break;
case OP_IDLE_TIME:
// Sleep if no events pending
if (ReadMacInt32(0x14c) == 0)
idle_wait();
2002-02-04 16:58:13 +00:00
r->a[0] = ReadMacInt32(0x2b6);
break;
case OP_IDLE_TIME_2:
// Sleep if no events pending
if (ReadMacInt32(0x14c) == 0)
idle_wait();
r->d[0] = (uint32)-2;
break;
2002-02-04 16:58:13 +00:00
default:
printf("FATAL: EMUL_OP called with bogus selector %08x\n", selector);
QuitEmulator();
break;
}
}