BreakWorldShepherdImpl.mesa
Copyright Ó 1989, 1990 by Xerox Corporation. All rights reserved.
Peter B. Kessler, July 30, 1990 10:27 am PDT
Philip James, June 25, 1991 3:12 pm PDT
DIRECTORY
TargetArchitecture,
Shepherd,
Breakpoint,
BreakWorldShepherd,
PCRMonitorDefs,
BreakWorldArchitecture,
RuntimeError;
BreakWorldShepherdImpl: CEDAR PROGRAM
IMPORTS
Shepherd, TargetArchitecture, BreakWorldArchitecture, RuntimeError
EXPORTS
Shepherd
~ {
Types EXPORT'ed to Shepherd.
Meadow: PUBLIC TYPE ~ BreakWorldShepherd.Meadow ← Shepherd.nullMeadow;
MeadowRep: PUBLIC TYPE ~ BreakWorldShepherd.MeadowRep;
Patch: PUBLIC TYPE ~ BreakWorldShepherd.Patch ← Shepherd.nullPatch;
PatchRep: PUBLIC TYPE ~ BreakWorldShepherd.PatchRep;
Public Procedures EXPORT'ed to Shepherd.
ReservePatch: PUBLIC PROCEDURE [
pc: BreakWorldArchitecture.Address, codeSize: BreakWorldArchitecture.ByteSize]
RETURNS [Shepherd.Patch] ~ {
ENABLE
BreakWorldArchitecture.Cant => ERROR Shepherd.Cant[message];
patch: BreakWorldShepherd.Patch ← BreakWorldShepherd.nullPatch;
IF pc.IsNullAddress[] THEN {
ERROR Shepherd.Cant[message: "ReservePatch[nullAddress]"];
};
{
meadow: BreakWorldShepherd.Meadow ~ MeadowFromPC[pc: pc];
ReserveUnderMonitor: PROCEDURE [] RETURNS [] ~ {
Reserve: PROCEDURE [
meadow: BreakWorldShepherd.Meadow,
pc: BreakWorldArchitecture.Address,
codeSize: BreakWorldArchitecture.ByteSize] ~ {
patch ← AllocatePatch[meadow: meadow, codeSize: codeSize];
SetPatchPC[
patch: patch,
pc: pc.TargetAddressFromBreakWorldAddress[]];
RETURN;
};
IF Shepherd.IsBusyPC[pc: pc] THEN {
ERROR Shepherd.BusyPC[message: "That address is busy"];
};
Reserve[meadow: meadow, pc: pc, codeSize: codeSize];
RETURN;
};
IF Shepherd.IsNullMeadow[meadow: meadow] THEN {
ERROR Shepherd.NoMeadow[message: "No meadow for that address"];
};
{ -- extra block to contain EXITS.
meadowAddress: BreakWorldArchitecture.Address ~ AddressFromMeadow[
meadow: meadow];
BreakWorldArchitecture.MonitoredCall[
address: meadowAddress, proc: ReserveUnderMonitor
! BreakWorldArchitecture.WouldBlock => {
GO TO wouldBlock;
}];
EXITS
wouldBlock => {
ERROR Shepherd.BusyMeadow[message: "Meadow for that address is busy"];
};
};
};
RETURN [patch];
};
ReleasePatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] ~ {
IF Shepherd.IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "ReleasePatch[nullPatch]"];
};
{
meadow: Shepherd.Meadow ~ MeadowFromPatch[patch: patch];
ReleaseUnderMonitor: PROCEDURE [] RETURNS [] ~ {
Release: PROCEDURE [patch: Shepherd.Patch] ~ {
SetPatchPC[patch: patch, pc: TargetArchitecture.nullAddress];
DeallocatePatch[patch: patch];
};
Release[patch: patch];
};
IF Shepherd.IsNullMeadow[meadow: meadow] THEN {
ERROR Shepherd.NoMeadow[message: "No meadow for that address"];
};
{ -- extra block to contain EXITS.
meadowAddress: BreakWorldArchitecture.Address ~ AddressFromMeadow[
meadow: meadow];
BreakWorldArchitecture.MonitoredCall[
address: meadowAddress, proc: ReleaseUnderMonitor
! BreakWorldArchitecture.WouldBlock => {
GO TO wouldBlock;
}];
EXITS
wouldBlock => {
ERROR Shepherd.BusyMeadow[message: "Meadow for that address is busy"];
};
};
};
RETURN;
};
IsNullMeadow: PUBLIC PROCEDURE [meadow: Shepherd.Meadow] RETURNS [BOOLEAN] ~ {
isNullMeadow: BOOLEAN ~ meadow = Shepherd.nullMeadow;
RETURN [isNullMeadow];
};
IsNullPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [BOOLEAN] ~ {
isNullPatch: BOOLEAN ~ patch = Shepherd.nullPatch;
RETURN [isNullPatch];
};
PCFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch]
RETURNS [BreakWorldArchitecture.Address] ~ {
IF Shepherd.IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "PCFromPatch[nullPatch]"];
};
{
targetAddress: TargetArchitecture.Address ~ GetPatchPC[patch: patch];
pc: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.NewAddress[
breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: patch]],
address: targetAddress];
RETURN [pc];
};
};
AddressFromMeadow: PRIVATE PROCEDURE [meadow: BreakWorldShepherd.Meadow]
RETURNS [BreakWorldArchitecture.Address] ~ {
IF IsNullMeadow[meadow: meadow] THEN {
ERROR Shepherd.Cant[message: "AddressFromMeadow[nullMeadow]"];
};
RETURN [meadow.address];
};
SizeFromMeadow: PRIVATE PROCEDURE [meadow: BreakWorldShepherd.Meadow]
RETURNS [BreakWorldArchitecture.ByteSize] ~ {
IF IsNullMeadow[meadow: meadow] THEN {
ERROR Shepherd.Cant[message: "SizeFromMeadow[nullMeadow]"];
};
RETURN [meadow.byteSize];
};
MeadowFromPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch]
RETURNS [Shepherd.Meadow] ~ {
meadow: Shepherd.Meadow ~ Shepherd.nullMeadow;
IF IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "MeadowFromPatch[nullPatch]"];
};
RETURN [patch.meadow];
};
PatchAddressFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch]
RETURNS [BreakWorldArchitecture.Address] ~ {
IF IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "PatchAddressFromPatch[nullPatch]"];
};
RETURN [patch.patchAddress];
};
CodeAddressFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch]
RETURNS [BreakWorldArchitecture.Address] ~ {
IF IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "PatchAddressFromPatch[nullPatch]"];
};
RETURN [patch.codeAddress];
};
PatchFromPC: PUBLIC PROCEDURE [pc: BreakWorldArchitecture.Address]
RETURNS [Shepherd.Patch] ~ {
found: Shepherd.Patch ← Shepherd.nullPatch;
IF pc.IsNullAddress[] THEN {
ERROR Shepherd.Cant[message: "PatchFromPC[nullAddress]"];
};
{
meadow: Shepherd.Meadow ~ MeadowFromPC[pc: pc];
targetPC: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[address: pc];
IF IsNullMeadow[meadow: meadow] THEN {
ERROR Shepherd.Cant[message: "PatchFromPC[nullMeadow]"];
};
FOR patch: Shepherd.Patch ← FirstPatch[meadow: meadow], NextPatch[patch: patch]
UNTIL Shepherd.IsNullPatch[patch: patch] DO {
IF NOT IsAllocatedPatch[patch: patch] THEN {
EXIT;
};
IF targetPC = GetPatchPC[patch: patch] THEN {
found ← patch;
EXIT;
};
} ENDLOOP;
};
RETURN [found];
};
IsAllocatedPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [BOOLEAN] ~ {
IF IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "IsAllocatedPatch[nullPatch]"];
};
{
size: BreakWorldArchitecture.ByteSize ~ GetPatchSize[patch: patch];
RETURN [size # 0];
};
};
IsBusyPC: PUBLIC PROCEDURE [pc: BreakWorldArchitecture.Address]
RETURNS [BOOLEAN] ~ {
IF pc.IsNullAddress[] THEN {
ERROR Shepherd.Cant[message: "IsBusyPC[nullAddress]"];
};
{
patch: Shepherd.Patch ~ PatchFromPC[pc: pc];
isBusy: BOOLEAN ~ NOT IsNullPatch[patch: patch];
RETURN [isBusy];
};
};
Public Errors.
NoRoom: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
NoMeadow: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
BusyMeadow: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
BusyPC: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
CantReach: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
Cant: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE;
Private Procedures.
NewMeadow: PRIVATE PROCEDURE [
address: BreakWorldArchitecture.Address,
byteSize: BreakWorldArchitecture.ByteSize]
RETURNS [Shepherd.Meadow] ~ {
meadow: BreakWorldShepherd.Meadow ~ NEW[BreakWorldShepherd.MeadowRep ← [
address: address, byteSize: byteSize]];
RETURN [meadow];
};
NewPatch: PRIVATE PROCEDURE [
meadow: Shepherd.Meadow,
address: BreakWorldArchitecture.Address]
RETURNS [Shepherd.Patch] ~ {
IF meadow.IsNullMeadow[] THEN {
ERROR Shepherd.Cant[message: "NewPatch[nullMeadow]"];
};
IF address.IsNullAddress[] THEN {
ERROR Shepherd.Cant[message: "NewPatch[nullAddress]"];
};
{
PatchCodeOffset: PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~ TRUSTED {
PatchStruct: TYPE ~ MACHINE DEPENDENT RECORD [
header: Shepherd.PatchHeaderStruct,
code: TargetArchitecture.Instruction];
patchStruct: PatchStruct;
patchStructAddress: LONG POINTER TO PatchStruct ~ @patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
codeAddress: LONG POINTER TO TargetArchitecture.Instruction ~ @patchStruct.code;
codeCard: CARD32 ~ LOOPHOLE[codeAddress];
offset: TargetArchitecture.Displacement ~ (codeCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
codeAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.AddressFromDisplacement[
address: address, displacement: PatchCodeOffset[]];
patch: BreakWorldShepherd.Patch ~ NEW[BreakWorldShepherd.PatchRep ← [
meadow: meadow, patchAddress: address, codeAddress: codeAddress]];
RETURN [patch];
};
};
MeadowFromPC: PRIVATE PROCEDURE [pc: BreakWorldArchitecture.Address]
RETURNS [Shepherd.Meadow] ~ {
ENABLE
BreakWorldArchitecture.Cant => ERROR Shepherd.Cant[message];
meadow: Shepherd.Meadow ← Shepherd.nullMeadow;
IF pc.IsNullAddress[] THEN {
ERROR Shepherd.Cant[message: "MeadowFromPC[nullAddress]"];
};
{
patchArea: BreakWorldArchitecture.PatchArea ~
BreakWorldArchitecture.GetPatchArea[address: pc];
IF NOT BreakWorldArchitecture.IsNullPatchArea[patchArea: patchArea] THEN {
meadow ← NewMeadow[address: patchArea.address, byteSize: patchArea.byteSize];
};
};
RETURN [meadow];
};
AllocatePatch: PRIVATE PROCEDURE [
meadow: Shepherd.Meadow,
codeSize: BreakWorldArchitecture.ByteSize]
RETURNS [Shepherd.Patch] ~ {
patch: Shepherd.Patch ← Shepherd.nullPatch;
IF meadow.IsNullMeadow[] THEN {
ERROR Shepherd.Cant[message: "AllocatePatch[nullMeadow]"];
};
{
meadowAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: AddressFromMeadow[meadow: meadow]];
meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow];
patchSize: BreakWorldArchitecture.ByteSize ~
codeSize + BYTES[Shepherd.PatchHeaderStruct];
This depends on Shepherd.PatchHeaderStruct leaving the patch code space aligned correctly for instructions.
FOR newPatch: Shepherd.Patch ←
FirstPatch[meadow: meadow], NextPatch[patch: newPatch] DO {
IF Shepherd.IsNullPatch[patch: newPatch] THEN {
ERROR Shepherd.NoRoom[message: "No more patches in the meadow"];
};
IF NOT IsAllocatedPatch[patch: newPatch] THEN {
patchAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: newPatch]];
patchDisplacement: TargetArchitecture.Displacement ~
TargetArchitecture.DisplacementFromAddresses[
here: meadowAddress, there: patchAddress];
requiredSize: BreakWorldArchitecture.ByteSize ~ patchDisplacement + patchSize;
IF requiredSize > meadowSize THEN {
ERROR Shepherd.NoRoom[message: "No room for patch in the meadow"];
};
SetPatchSize[patch: newPatch, size: patchSize];
patch ← newPatch;
EXIT;
};
} ENDLOOP;
};
RETURN [patch];
};
DeallocatePatch: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [] ~ {
We don't deallocate patches, since we can't reuse them until the garbage collection problem is solved.
IF Shepherd.IsNullPatch[patch: patch] THEN {
ERROR Shepherd.Cant[message: "DeallocatePatch[nullPatch]"];
};
RETURN;
};
MeadowHeader: PRIVATE TYPE ~ MACHINE DEPENDENT RECORD [
monitorLock: PCRMonitorDefs.MonitorLock,
the monitor lock for the meadow.
next: TargetArchitecture.Address
a pointer to an extension of the meadow.
];
FirstPatch: PRIVATE PROCEDURE [meadow: Shepherd.Meadow]
RETURNS [Shepherd.Patch] ~ {
patch: BreakWorldShepherd.Patch ← Shepherd.nullPatch;
IF meadow.IsNullMeadow[] THEN {
ERROR Shepherd.Cant[message: "FirstPatch[nullMeadow]"];
};
{
Watch this! We do target address arithmetic using structures we've defined over here!
meadowAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: AddressFromMeadow[meadow: meadow]];
meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow];
patchAddress: TargetArchitecture.Address ~
TargetArchitecture.AddressFromDisplacement[
address: meadowAddress, displacement: BYTES[MeadowHeader]];
patchDisplacement: TargetArchitecture.Displacement ~
TargetArchitecture.DisplacementFromAddresses[
here: meadowAddress, there: patchAddress];
IF BreakWorldArchitecture.ByteSize[patchDisplacement] < meadowSize THEN {
patchBreakWorldAddress: BreakWorldArchitecture.Address ~
BreakWorldArchitecture.NewAddress[
breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[
address: AddressFromMeadow[meadow: meadow]],
address: patchAddress];
patch ← NewPatch[meadow: meadow, address: patchBreakWorldAddress];
};
};
RETURN [patch];
};
NextPatch: PRIVATE PROCEDURE [patch: Shepherd.Patch]
RETURNS [Shepherd.Patch] ~ {
nextPatch: BreakWorldShepherd.Patch ← Shepherd.nullPatch;
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "NextPatch[nullPatch]"];
};
{
Watch this! We do target address arithmetic using structures we've defined over here!
meadow: BreakWorldShepherd.Meadow ~ MeadowFromPatch[patch: patch];
meadowAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: AddressFromMeadow[meadow: meadow]];
meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow];
patchAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: patch]];
patchSize: BreakWorldArchitecture.ByteSize ~ GetPatchSize[patch: patch];
newTargetAddress: TargetArchitecture.Address ~
TargetArchitecture.AddressFromDisplacement[
address: patchAddress, displacement: patchSize];
newDisplacement: TargetArchitecture.Displacement ~
TargetArchitecture.DisplacementFromAddresses[
here: meadowAddress, there: newTargetAddress];
IF BreakWorldArchitecture.ByteSize[newDisplacement] < meadowSize THEN {
newBreakWorldAddress: BreakWorldArchitecture.Address ~
BreakWorldArchitecture.NewAddress[
breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: patch]],
address: newTargetAddress];
nextPatch ← NewPatch[meadow: meadow, address: newBreakWorldAddress];
};
};
RETURN [nextPatch];
};
GetPatchHeader: PRIVATE PROCEDURE [patch: Shepherd.Patch]
RETURNS [Shepherd.PatchHeaderStruct] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchHeader[nullPatch]"];
};
{
contents: TargetArchitecture.Contents ~ BreakWorldArchitecture.PeekContents[
address: PatchAddressFromPatch[patch: patch]];
patchHeaderStruct: Shepherd.PatchHeaderStruct ~ LOOPHOLE[contents];
RETURN [patchHeaderStruct];
};
};
SetPatchHeader: PRIVATE PROCEDURE [
patch: Shepherd.Patch, patchHeaderStruct: Shepherd.PatchHeaderStruct]
RETURNS [] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "SetPatchHeader[nullPatch]"];
};
{
contents: TargetArchitecture.Contents ~ LOOPHOLE[patchHeaderStruct];
BreakWorldArchitecture.PokeContents[
address: PatchAddressFromPatch[patch: patch], contents: contents];
};
RETURN;
};
GetPatchSize: PRIVATE PROCEDURE [patch: Shepherd.Patch]
RETURNS [Shepherd.PatchSize] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchSize[nullPatch]"];
};
{
patchHeaderStruct: Shepherd.PatchHeaderStruct ~ GetPatchHeader[patch: patch];
RETURN [patchHeaderStruct.size * BYTES[TargetArchitecture.Contents]];
};
};
SetPatchSize: PRIVATE PROCEDURE [patch: Shepherd.Patch, size: BreakWorldArchitecture.ByteSize]
RETURNS [] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "SetPatchSize[nullPatch]"];
};
{
patchHeaderStruct: Shepherd.PatchHeaderStruct ← GetPatchHeader[patch: patch];
wordSize: Shepherd.PatchSize ~
(size + BYTES[TargetArchitecture.Contents] - 1) / BYTES[TargetArchitecture.Contents];
patchHeaderStruct.size ← wordSize;
SetPatchHeader[patch: patch, patchHeaderStruct: patchHeaderStruct];
RETURN;
};
};
GetPatchPC: PUBLIC PROCEDURE [patch: Shepherd.Patch]
RETURNS [TargetArchitecture.Address] ~ {
address: TargetArchitecture.Address ← TargetArchitecture.nullAddress;
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchPC[nullPatch]"];
};
{
relPC: Shepherd.RelPC ~ GetPatchRelPC[patch: patch];
IF relPC # TargetArchitecture.nullDisplacement THEN {
patchAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: patch]];
displacementBytes: TargetArchitecture.Displacement ~
relPC * BYTES[TargetArchitecture.Contents];
address ← TargetArchitecture.AddressFromDisplacement[
address: patchAddress, displacement: displacementBytes];
};
};
RETURN [address];
};
GetPatchRelPC: PRIVATE PROCEDURE [patch: Shepherd.Patch]
RETURNS [Shepherd.RelPC] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"];
};
{
patchHeaderStruct: Shepherd.PatchHeaderStruct ~ GetPatchHeader[patch: patch];
relPCLS: Shepherd.RelPCLS ~ patchHeaderStruct.relPCLS;
relPCMS: Shepherd.RelPCMS ~ patchHeaderStruct.relPCMS;
card: CARD32 ~ (relPCMS * (CARD32[Shepherd.lastRelPCLS] + 1)) + relPCLS;
signBits: CARD32 ~
(LAST[CARD32] /
((CARD32[Shepherd.lastRelPCMS] + 1) * (CARD32[Shepherd.lastRelPCLS] + 1))) *
((CARD32[Shepherd.lastRelPCMS] + 1) * (CARD32[Shepherd.lastRelPCLS] + 1));
relPC: Shepherd.RelPC ~
IF card <= CARD32[Shepherd.lastRelPC]
THEN card
ELSE LOOPHOLE[card + signBits];
IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN {
ERROR RuntimeError.BoundsFault;
};
RETURN [relPC];
};
};
SetPatchPC: PUBLIC PROCEDURE [patch: Shepherd.Patch, pc: TargetArchitecture.Address]
RETURNS [] ~ {
relPC: Shepherd.RelPC ← TargetArchitecture.nullDisplacement;
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"];
};
IF pc # TargetArchitecture.nullAddress THEN {
patchAddress: TargetArchitecture.Address ~
BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[
address: PatchAddressFromPatch[patch: patch]];
displacementBytes: TargetArchitecture.Displacement ~
TargetArchitecture.DisplacementFromAddresses[here: patchAddress, there: pc];
relPC ← displacementBytes / BYTES[TargetArchitecture.Contents];
IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN {
ERROR Shepherd.CantReach[message: "Patch too far from PC"];
};
};
SetPatchRelPC[patch: patch, relPC: relPC];
RETURN;
};
SetPatchRelPC: PRIVATE PROCEDURE [patch: Shepherd.Patch, relPC: Shepherd.RelPC]
RETURNS [] ~ {
IF patch.IsNullPatch[] THEN {
ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"];
};
{
patchHeaderStruct: Shepherd.PatchHeaderStruct ← GetPatchHeader[patch: patch];
card: CARD32 ~ LOOPHOLE[relPC];
relPCLS: Shepherd.RelPCLS ~ card MOD (CARD32[Shepherd.lastRelPCLS] + 1);
relPCMS: Shepherd.RelPCMS ~
(card / (CARD32[Shepherd.lastRelPCLS] + 1)) MOD (CARD32[Shepherd.lastRelPCMS] + 1);
IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN {
ERROR RuntimeError.BoundsFault;
};
patchHeaderStruct.relPCLS ← relPCLS;
patchHeaderStruct.relPCMS ← relPCMS;
SetPatchHeader[patch: patch, patchHeaderStruct: patchHeaderStruct];
RETURN;
};
};
}.