mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-27 10:29:32 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
316 lines
8.7 KiB
C
316 lines
8.7 KiB
C
/*
|
|
File: Debugger.c
|
|
|
|
Contains: Routines to assist the registered high-level debugger.
|
|
|
|
Written by: Russ Daniels
|
|
|
|
Copyright: © 1987-1992 by Apple Computer, Inc., all rights reserved.
|
|
|
|
Change History (most recent first):
|
|
|
|
<6> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
|
|
<5> 2/4/91 DFH gbm,WhiteBoard:Fixed CoreRegisterDebugger's check for initial
|
|
registration vs re-registration. Kept InitExceptions from being
|
|
called during registration. This check was incorrect even before
|
|
change <4>.
|
|
<4> 1/21/91 DFH (ksm) Fixed CoreRegisterDebugger so that it can be called to
|
|
change attributes.
|
|
<0> 7/10/87 RDD New Today.
|
|
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <errors.h>
|
|
#include <memory.h>
|
|
#include <files.h>
|
|
#include <quickdraw.h>
|
|
#include <windows.h>
|
|
#include <menus.h>
|
|
#include <events.h>
|
|
#include <resources.h>
|
|
#include <osutils.h>
|
|
#include <retrace.h>
|
|
#include <segload.h>
|
|
#include <desk.h>
|
|
#include <MFPrivate.h>
|
|
#include <sysequ.h>
|
|
|
|
#include "Glue.h"
|
|
#include "Lomem.h"
|
|
#include "Data.h"
|
|
#include "SysMisc.h"
|
|
#include "Puppet.h"
|
|
|
|
#pragma segment Debugger
|
|
|
|
/* Some function prototypes that should be in (yet another) header file */
|
|
void InitDebuggerExceptions(void);
|
|
void UnplugDebuggerExceptions(void);
|
|
|
|
/* CoreRegisterDebugger. Register process as the current debugger. Passing kNoProcess
|
|
* unregisters the debugger.
|
|
* NOTE: Assumes a5 == PROCESSMGRGLOBALS.
|
|
*/
|
|
OSErr
|
|
CoreRegisterDebugger(ProcessSerialNumberPtr pPSN, Ptr pEntryRoutine, Ptr pExitRoutine, Ptr pToAppRoutine, unsigned short newDebugControlKeyCode)
|
|
{
|
|
CodeEntry *pCodeEntry;
|
|
PEntryPtr pProc;
|
|
|
|
/* Are we are unregistering the current debugger? */
|
|
if (EqualPSNConst(kNoProcess, pPSN))
|
|
{
|
|
/* Don't unregister unless there is one */
|
|
if (pDebugProcess != nil)
|
|
{
|
|
assert(IsProcessEntry(pDebugProcess));
|
|
|
|
/* Kill of all his debuggees first. */
|
|
pProc = pProcessList;
|
|
while (pProc != nil)
|
|
{
|
|
PEntryPtr pNextProc;
|
|
|
|
assert(IsProcessEntry(pProc));
|
|
pNextProc = pProc->p_NextProcess;
|
|
if (pProc->p_state == PRNULL)
|
|
{
|
|
KillProcess(&pProc->p_serialNumber);
|
|
}
|
|
pProc = pNextProc;
|
|
}
|
|
|
|
UnplugDebuggerExceptions();
|
|
|
|
/* And get rid of the Debugger code segment, too */
|
|
pCodeEntry = (CodeEntry *)CoreRegisterDebugger;
|
|
UnloadSeg((Ptr)pCodeEntry);
|
|
ReleaseResource(GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry));
|
|
|
|
pDebugProcess = nil;
|
|
}
|
|
}
|
|
|
|
/* We are registering or changing the debugger attributes */
|
|
else
|
|
{
|
|
/* locate PEntry associated with given process */
|
|
if ((pProc = PEntryFromPSN(pPSN)) == nil)
|
|
return(procNotFound);
|
|
|
|
/* if registering, rather than changing, put in the exception handlers */
|
|
if (pDebugProcess == nil)
|
|
InitDebuggerExceptions();
|
|
|
|
/* set the procptrs in global variables */
|
|
pDebugProcess = pProc;
|
|
debugEntryRoutine = pEntryRoutine;
|
|
debugExitRoutine = pExitRoutine;
|
|
debugToAppRoutine = pToAppRoutine;
|
|
debugControlKeyCode = newDebugControlKeyCode;
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
|
|
/* swap_state. This is an attenuated copy of switch_task. It changes all of the mapped
|
|
* memory, but doesn't actually switch to the "new" task.
|
|
*/
|
|
unsigned short
|
|
swap_state(PEntryPtr pInProc, PEntryPtr pOutProc)
|
|
{
|
|
void save_state(PEntryPtr);
|
|
unsigned short restore_state(PEntryPtr, Boolean);
|
|
|
|
/* Don't need to disable here, but don't know valid processor state! */
|
|
if (pInProc == pOutProc)
|
|
return(disable());
|
|
|
|
/* save outgoers state, then switch to incomer's */
|
|
save_state(pOutProc);
|
|
return(restore_state(pInProc, false));
|
|
}
|
|
|
|
/* CoreReadWriteProcessMemory. To read the contents of memory for a given task, we
|
|
* switch in that task's memory (by calling swap_state), move the memory, then
|
|
* switch the caller's memory back in. Interrupts are disabled during the process.
|
|
*/
|
|
OSErr
|
|
CoreReadWriteProcessMemory(ProcessSerialNumberPtr pPSN, Ptr procMemPtr, u_long cBytes, Ptr callerMemPtr, Boolean doRead)
|
|
{
|
|
Ptr tmpPtr;
|
|
PEntryPtr pProc;
|
|
unsigned short ps;
|
|
|
|
if ((pProc = PEntryFromPSN(pPSN)) == nil)
|
|
return(procNotFound);
|
|
|
|
/* Assume readin' 'till proven it's writin' */
|
|
if (!doRead)
|
|
{
|
|
/* Do the big el swapo */
|
|
tmpPtr = procMemPtr;
|
|
procMemPtr = callerMemPtr;
|
|
callerMemPtr = tmpPtr;
|
|
}
|
|
|
|
ps = swap_state(pProc, pCurrentProcess);
|
|
|
|
BlockMove (procMemPtr, callerMemPtr, cBytes);
|
|
|
|
(void)swap_state(pCurrentProcess, pProc);
|
|
spl(ps);
|
|
|
|
return(noErr);
|
|
}
|
|
|
|
/* CoreReadWriteProcessFPURegs. To read the or write the Floating Point Unit context
|
|
* for a process, we go straight to where we saved them when we last switched the
|
|
* process out. Of course, the caller's FPU registers can be sampled directly.
|
|
* NOTE: Extremely dependant on the order in which the registers are saved.
|
|
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
|
|
*/
|
|
OSErr
|
|
CoreReadWriteProcessFPURegs(ProcessSerialNumberPtr pPSN, Ptr callerMemPtr, Boolean doRead)
|
|
{
|
|
PEntryPtr pProc;
|
|
AllRegs *pAllRegs;
|
|
Ptr srcPtr, dstPtr;
|
|
|
|
/* Can do nothing if there is no FPU! */
|
|
if (MachineHasFPU == false)
|
|
return(hardwareConfigErr);
|
|
|
|
/* Locate the victim */
|
|
if ((pProc = PEntryFromPSN(pPSN)) == nil)
|
|
return(procNotFound);
|
|
|
|
if (pProc == pCurrentProcess)
|
|
{
|
|
if (doRead)
|
|
GetLiveFPURegs(callerMemPtr);
|
|
else
|
|
SetLiveFPURegs(callerMemPtr);
|
|
}
|
|
else
|
|
{
|
|
pAllRegs = (AllRegs *)pProc->p_sp;
|
|
|
|
/* If not a full FPU context, create one by munging the stack to give the
|
|
* values an FRESTORE would.
|
|
*/
|
|
if (!pAllRegs->fpuFrame.isFullFPUFrame)
|
|
{
|
|
BlockMove(pAllRegs, (Ptr)pAllRegs - sizeof(FPUFrame), sizeof(SaveRegs));
|
|
pProc->p_sp = (Ptr)pAllRegs -= sizeof(FPUFrame); /* Lower the stack */
|
|
MemClear(&pAllRegs->fpuFrame, sizeof(FPUFrame)); /* Do whole frame for alignment speed, hopefully */
|
|
pAllRegs->fpuFrame.isFullFPUFrame = true;
|
|
}
|
|
|
|
/* Move data the right direction */
|
|
srcPtr = (doRead) ? &pAllRegs->fpuFrame.fpuUserRegs : callerMemPtr;
|
|
dstPtr = (doRead) ? callerMemPtr : &pAllRegs->fpuFrame.fpuUserRegs;
|
|
BlockMove(srcPtr, dstPtr, sizeof(FPUUserRegs));
|
|
}
|
|
|
|
return(noErr);
|
|
}
|
|
|
|
#pragma segment kernel_segment
|
|
|
|
/* c_RegisterDebugger. Verify caller, then call core routine in Debugger segment.
|
|
* NOTE: Assumes THEZONE != ProcessMgrZone, in that it restores THEZONE unconditionally,
|
|
* instead of using SafeSetZone and SafeRestoreZone.
|
|
*/
|
|
pascal OSErr
|
|
c_RegisterDebugger(ProcessSerialNumberPtr pPSN, Ptr pEntryRoutine, Ptr pExitRoutine, Ptr pToAppRoutine, unsigned short newDebugControlKeyCode)
|
|
{
|
|
short appCurMap;
|
|
THz appTheZone;
|
|
|
|
unsigned long olda5;
|
|
short retval;
|
|
CodeEntry *pCodeEntry;
|
|
|
|
olda5 = ProcessMgrA5SimpleSetup();
|
|
assert(THEZONE != ProcessMgrZone);
|
|
appCurMap = CURMAP; CURMAP = SYSMAP;
|
|
appTheZone = THEZONE; THEZONE = SYSZONE;
|
|
|
|
pCodeEntry = CoreRegisterDebugger;
|
|
if (CODE_ENTRY_UNLOADED(pCodeEntry)
|
|
&& GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry) == nil)
|
|
{
|
|
if ((retval = RESERR) == noErr)
|
|
retval = resNotFound;
|
|
}
|
|
else
|
|
retval = CoreRegisterDebugger(pPSN, pEntryRoutine, pExitRoutine, pToAppRoutine, newDebugControlKeyCode);
|
|
|
|
THEZONE = appTheZone;
|
|
CURMAP = appCurMap;
|
|
A5SimpleRestore(olda5);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* c_ReadWriteProcessMemory. Verify caller, then call core routine in Debugger segment. */
|
|
pascal OSErr
|
|
c_ReadWriteProcessMemory(ProcessSerialNumberPtr pPSN, Ptr procMemPtr, unsigned long cBytes, Ptr callerMemPtr, Boolean doRead)
|
|
{
|
|
OSErr retval;
|
|
PEntryPtr pProc;
|
|
unsigned long olda5;
|
|
|
|
olda5 = ProcessMgrA5SimpleSetup();
|
|
retval = noErr;
|
|
|
|
/* Pay special favor to TMON. Mr. Horwat needs to be able get the topmaphandle
|
|
* of other processes in a reentrant way (since, unlike SADE, he'd like to be
|
|
* able to step through context switching code).
|
|
* NOTE: This should be taken care of by better debugger support in general.
|
|
*/
|
|
if (doRead && (cBytes == (-1)) && (StripAddress(procMemPtr) == TopMapHndl))
|
|
{
|
|
if ((pProc = PEntryFromPSN(pPSN)) == nil)
|
|
retval = procNotFound;
|
|
else
|
|
BlockMove(&((*pProc->p_pcb)->topmaphandle), callerMemPtr, sizeof(Handle));
|
|
|
|
A5SimpleRestore(olda5);
|
|
return(retval);
|
|
}
|
|
|
|
/* Require that the caller be preregistered. Also guarantees that the Debugger
|
|
* segment is loaded.
|
|
*/
|
|
if (pCurrentProcess == pDebugProcess)
|
|
CoreReadWriteProcessMemory(pPSN, procMemPtr, cBytes, callerMemPtr, doRead);
|
|
else
|
|
retval = protocolErr;
|
|
|
|
A5SimpleRestore(olda5);
|
|
return(retval);
|
|
}
|
|
|
|
/* c_ReadWriteProcessFPURegs. Verify caller, then call core routine in Debugger segment. */
|
|
pascal OSErr
|
|
c_ReadWriteProcessFPURegs(ProcessSerialNumberPtr pPSN, Ptr callerMemPtr, Boolean doRead)
|
|
{
|
|
OSErr retval;
|
|
unsigned long olda5;
|
|
|
|
olda5 = ProcessMgrA5SimpleSetup();
|
|
|
|
/* Require that the caller be preregistered. Also guarantees that the Debugger
|
|
* segment is loaded.
|
|
*/
|
|
retval = (pCurrentProcess == pDebugProcess)
|
|
? CoreReadWriteProcessFPURegs(pPSN, callerMemPtr, doRead)
|
|
: protocolErr;
|
|
|
|
A5SimpleRestore(olda5);
|
|
return(retval);
|
|
}
|