IMPORTS
Breakpoint, BreakWorldArchitecture, Shepherd, SPARCArchitecture, SPARCBreakWorldUtilities, SPARCManger, TargetArchitecture
Public Procedures exported to Breakpoint.
SetBreakpoint:
PUBLIC Breakpoint.SetBreakProc ~ {
PROCEDURE [
address: BreakWorldArchitecture.Address,
clientData: Breakpoint.ClientData,
breakProc: Breakpoint.BreakProc,
breakData: Breakpoint.BreakData,
damages: TargetArchitecture.RegisterClass ← TargetArchitecture.RegisterClass.all]
RETURNS [Breakpoint.Break]
break: Breakpoint.Break ← Breakpoint.nullBreak;
patch: Shepherd.Patch ← Shepherd.nullPatch;
errorCode: Breakpoint.ErrorCode ← Breakpoint.nullErrorCode;
errorMessage: Breakpoint.ErrorMessage ← Breakpoint.nullErrorMessage;
CheckForDelaySlot:
PROCEDURE [address: BreakWorldArchitecture.Address]
RETURNS [] ~{
instruction: SPARCArchitecture.SPARCInstruction ~
SPARCBreakWorldUtilities.SPARCInstructionFromBreakWorldAddress[address: address];
prevInstruction: SPARCArchitecture.SPARCInstruction ~
SPARCBreakWorldUtilities.SPARCInstructionFromBreakWorldAddress[
address: address, displacement: -BYTES[SPARCArchitecture.SPARCInstruction]];
nextInstruction: SPARCArchitecture.SPARCInstruction ~
SPARCBreakWorldUtilities.SPARCInstructionFromBreakWorldAddress[
address: address, displacement: +BYTES[SPARCArchitecture.SPARCInstruction]];
IF SPARCArchitecture.IsDelayedControlTransfer[instruction: prevInstruction]
THEN {
ERROR Breakpoint.CantSet[
code: $NotInDelaySlot, message: "Cannot set breakpoint in delay slot"];
};
RETURN;
};
IF address.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $NotAtNullAddress, message: "Cannot set breakpoint at null address"];
};
IF breakProc.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $NotWithNullAddress,
message: "Cannot set breakpoint with null break procedure world address"];
};
{
breakProcTargetAddress: TargetArchitecture.Address ~
breakProc.TargetAddressFromBreakWorldAddress[];
IF breakProcTargetAddress.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $NotWithNullAddress,
message: "Cannot set breakpoint with null break procedure target address"];
};
};
CheckForDelaySlot[address: address];
{
-- extra scope to contain EXITS
patch ← Shepherd.ReservePatch[
pc: address, codeSize: BYTES[SPARCBreakpoint.PatchStruct]
! Shepherd.NoMeadow => {
errorCode ← $NoMeadow;
errorMessage ← message;
GO TO Cannot;
};
Shepherd.BusyMeadow => {
errorCode ← $BusyMeadow;
errorMessage ← message;
GO TO Cannot;
};
Shepherd.BusyPC => {
errorCode ← $BusyPC;
errorMessage ← message;
GO TO Cannot;
};
Shepherd.NoRoom => {
errorCode ← $NoRoom;
errorMessage ← message;
GO TO Cannot;
};
Shepherd.CantReach => {
errorCode ← $CantReach;
errorMessage ← message;
GO TO Cannot;
};
];
Install[
address: address,
patch: patch,
breakProc: breakProc,
breakData: breakData,
damages: damages];
break ← Breakpoint.NewBreak[address: address, patch: patch, clientData: clientData];
Breakpoint.RememberBreak[break
! Breakpoint.Cant => {
errorCode ← $CantRemember;
errorMessage ← message;
GO TO Cannot;
};];
EXITS
Cannot => {
-- Can't deallocate patches.
Well, I could just clear the size and address, but is that safe?
IF NOT patch.IsNullPatch[] THEN {
Shepherd.ReleasePatch[patch: patch];
};
ERROR Breakpoint.CantSet[code: errorCode, message: errorMessage];
};
};
RETURN [break];
};
Install:
PROCEDURE [
address: BreakWorldArchitecture.Address,
patch: Shepherd.Patch,
breakProc: Breakpoint.BreakProc,
breakData: Breakpoint.BreakData,
damages: TargetArchitecture.RegisterClass]
RETURNS [] ~ {
IF address.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[code: $NullAddress, message: "Install[nullAddress]"];
};
IF patch.IsNullPatch[]
THEN {
ERROR Breakpoint.CantSet[code: $NullPatch, message: "Install[nullPatch]"];
};
IF breakProc.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[code: $NullAddress, message: "Install[nullBreakProc]"];
};
InstallClosureCaller[
address: address,
patch: patch,
breakProc: breakProc,
breakData: breakData,
damages: damages];
InstallManger[address: address, patch: patch];
};
InstallClosureCaller:
PROCEDURE [
address: BreakWorldArchitecture.Address,
patch: Shepherd.Patch,
breakProc: Breakpoint.BreakProc,
breakData: Breakpoint.BreakData,
damages: TargetArchitecture.RegisterClass]
RETURNS [] ~ {
IF address.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $NullAddress, message: "InstallClosureCaller[nullAddress]"];
};
IF breakProc.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $NullAddress, message: "InstallClosureCaller[nullBreakProc]"];
};
IF patch.IsNullPatch[]
THEN {
ERROR Breakpoint.CantSet[
code: $NullPatch, message: "InstallClosureCaller[nullPatch]"];
};
{
instructionAddress: SPARCArchitecture.SPARCAddress ~
SPARCBreakWorldUtilities.SPARCAddressFromBreakWorldAddress[address: address];
patchAddress: BreakWorldArchitecture.Address ~ Shepherd.CodeAddressFromPatch[patch: patch];
patchSPARCAddress: SPARCArchitecture.SPARCAddress ~
SPARCBreakWorldUtilities.SPARCAddressFromBreakWorldAddress[address: patchAddress];
frameSize:
INT ~
SPARCArchitecture.stackPointerOffset
+ SPARCArchitecture.stackAllocationForCallee
+ SPARCBreakpoint.registerSaveArea;
savedRegsStackOffset:
INT ~
SPARCArchitecture.stackPointerOffset
+ SPARCArchitecture.stackAllocationForCallee;
damagesRegisters: SPARCArchitecture.RegisterClass ~
SPARCArchitecture.SPARCRegisterClassFromTargetRegisterClass[registerClass: damages];
registerSaveName: Rope.
ROPE ~
SELECT damagesRegisters
FROM
none => "←save←regs←none",
globals => "←save←regs←globals",
globalsAndIns => "←save←regs←mini",
ENDCASE => "←save←regs";
registerRestoreName: Rope.
ROPE ~
SELECT damagesRegisters
FROM
none => "←restore←regs←none",
globals => "←restore←regs←globals",
globalsAndIns => "←restore←regs←mini",
ENDCASE => "←restore←regs";
Generate the code in the patch.
closureCaller: SPARCBreakpoint.ClosureCaller;
{
closureCaller.save ← SPARCArchitecture.SaveConst[
source: SPARCArchitecture.stackPointer,
constant: -frameSize,
dest: SPARCArchitecture.stackPointer];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: SaveOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.save]];
};
{
callSaveRegsAddress: SPARCArchitecture.SPARCAddress ~
SPARCArchitecture.SPARCAddressFromDisplacement[
address: patchSPARCAddress, displacement: CallSaveRegsOffset[]];
saveRegsAddress: SPARCArchitecture.SPARCAddress ~
SPARCBreakWorldUtilities.SPARCAddressFromBreakWorldAddress[
address: BreakWorldArchitecture.GetProcAddress[
breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[
address: address],
procName: registerSaveName]];
IF saveRegsAddress.IsNullSPARCAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $CantFindRegisterSaveProcedure,
message: "Can't find the register save procedure"];
};
closureCaller.callSaveRegs ← SPARCArchitecture.Call[
pc: callSaveRegsAddress, to: saveRegsAddress];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: CallSaveRegsOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.callSaveRegs]];
};
{
closureCaller.argSaveAreaOnce ← SPARCArchitecture.AddConst[
source: SPARCArchitecture.stackPointer,
constant: savedRegsStackOffset,
dest: SPARCArchitecture.Register.out0];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: ArgSaveAreaOnceOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.argSaveAreaOnce]];
};
{
closureCaller.argHiClientData ← SPARCArchitecture.Sethi[
hi: SPARCArchitecture.Hi[value: breakData],
dest: SPARCArchitecture.Register.out0];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: ArgHiClientDataOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.argHiClientData]];
};
{
callClientProcAddress: SPARCArchitecture.SPARCAddress ~
SPARCArchitecture.SPARCAddressFromDisplacement[
address: patchSPARCAddress, displacement: CallClientProcOffset[]];
clientProcAddress: SPARCArchitecture.SPARCAddress ~
SPARCBreakWorldUtilities.SPARCAddressFromBreakWorldAddress[
address: breakProc];
closureCaller.callClientProc ← SPARCArchitecture.Call[
pc: callClientProcAddress, to: clientProcAddress];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: CallClientProcOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.callClientProc]];
};
{
closureCaller.argLoClientData ← SPARCArchitecture.OrConst[
source: SPARCArchitecture.Register.out0,
constant: SPARCArchitecture.Lo[value: breakData],
dest: SPARCArchitecture.Register.out0];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: ArgLoClientDataOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.argLoClientData]];
};
{
callRestoreRegsAddress: SPARCArchitecture.SPARCAddress ~
SPARCArchitecture.SPARCAddressFromDisplacement[
address: patchSPARCAddress, displacement: CallRestoreRegsOffset[]];
restoreRegsAddress: SPARCArchitecture.SPARCAddress ~
SPARCBreakWorldUtilities.SPARCAddressFromBreakWorldAddress[
address: BreakWorldArchitecture.GetProcAddress[
breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[
address: address],
procName: registerRestoreName]];
IF restoreRegsAddress.IsNullSPARCAddress[]
THEN {
ERROR Breakpoint.CantSet[
code: $CantFindRegisterRestoreProcedure,
message: "Can't find the register restore procedure"];
};
closureCaller.callRestoreRegs ← SPARCArchitecture.Call[
pc: callRestoreRegsAddress, to: restoreRegsAddress];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: CallRestoreRegsOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.callRestoreRegs]];
};
{
closureCaller.argSaveAreaAgain ← SPARCArchitecture.AddConst[
source: SPARCArchitecture.stackPointer,
constant: savedRegsStackOffset,
dest: SPARCArchitecture.Register.out0];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: ArgSaveAreaAgainOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.argSaveAreaAgain]];
};
{
closureCaller.restore ← SPARCArchitecture.RestoreConst[
source: SPARCArchitecture.stackPointer,
constant: +frameSize,
dest: SPARCArchitecture.stackPointer];
BreakWorldArchitecture.PokeInstruction[
pc: patchAddress,
displacement: RestoreOffset[],
instruction: SPARCArchitecture.TargetInstructionFromSPARCInstruction[
instruction: closureCaller.restore]];
};
};
};
InstallManger:
PROCEDURE [
address: BreakWorldArchitecture.Address, patch: Shepherd.Patch]
RETURNS [] ~ {
IF address.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[code: $NullAddress, message: "InstallManger[nullAddress]"];
};
IF patch.IsNullPatch[]
THEN {
ERROR Breakpoint.CantSet[code: $NullPatch, message: "InstallManger[nullPatch]"];
};
{
patchCodeAddress: BreakWorldArchitecture.Address ~ Shepherd.CodeAddressFromPatch[
patch: patch];
mangerAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.AddressFromDisplacement[
address: patchCodeAddress, displacement: MangerOffset[]];
errorCode: SPARCManger.ErrorCode ← SPARCManger.nullErrorCode;
errorMessage: SPARCManger.ErrorMessage ← SPARCManger.nullErrorMessage;
{
--Extra scope to contain EXITS.
SPARCManger.Install[
address: address, manger: mangerAddress, patchCode: patchCodeAddress
! SPARCManger.CantInstall => {
errorCode ← code;
errorMessage ← message;
GO TO Cannot;
}];
EXITS
Cannot => {
ERROR Breakpoint.CantSet[code: errorCode, message: errorMessage];
};
};
};
};
ClearBreakpoint:
PUBLIC Breakpoint.ClearBreakProc ~ {
PROCEDURE [break: Break] RETURNS [];
errorCode: Breakpoint.ErrorCode ← Breakpoint.nullErrorCode;
errorMessage: Breakpoint.ErrorMessage ← Breakpoint.nullErrorMessage;
IF Breakpoint.IsNullBreak[break: break]
THEN {
ERROR Breakpoint.CantClear[code: $NullBreak, message: "Cannot clear null break"];
};
{
-- extra scope for EXITS
address: BreakWorldArchitecture.Address ~ Breakpoint.AddressFromBreak[break: break];
patch: Shepherd.Patch ← Breakpoint.PatchFromBreak[break: break];
Uninstall[patch: patch, address: address];
Shepherd.ReleasePatch[patch: patch
! Shepherd.NoMeadow => {
errorCode ← $NoMeadow;
errorMessage ← message;
GO TO Cannot;
};
Shepherd.BusyMeadow => {
errorCode ← $BusyMeadow;
errorMessage ← message;
GO TO Cannot;
};];
Breakpoint.ForgetBreak[break: break
! Breakpoint.Cant => {
errorCode ← $CantForget;
errorMessage ← message;
GO TO Cannot;
}];
EXITS
Cannot => {
ERROR Breakpoint.CantClear[code: errorCode, message: errorMessage];
};
};
};
Uninstall:
PROCEDURE [patch: Shepherd.Patch, address: BreakWorldArchitecture.Address] ~ {
IF patch.IsNullPatch[]
THEN {
ERROR Breakpoint.CantSet[code: $NullPatch, message: "Uninstall[nullPatch]"];
};
IF address.IsNullAddress[]
THEN {
ERROR Breakpoint.CantSet[code: $NullAddress, message: "Uninstall[nullAddress]"];
};
{
patchCodeAddress: BreakWorldArchitecture.Address ~ Shepherd.CodeAddressFromPatch[
patch: patch];
mangerAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.AddressFromDisplacement[
address: patchCodeAddress, displacement: MangerOffset[]];
errorCode: SPARCManger.ErrorCode ← SPARCManger.nullErrorCode;
errorMessage: SPARCManger.ErrorMessage ← SPARCManger.nullErrorMessage;
{
-- Extra scope for EXITS
SPARCManger.Uninstall[
address: address, manger: mangerAddress, patchCode: patchCodeAddress
! SPARCManger.CantUninstall => {
errorCode ← code;
errorMessage ← message;
GO TO Cannot;
}];
EXITS
Cannot => {
ERROR Breakpoint.CantClear[code: errorCode, message: errorMessage];
};
};
};
};
PatchStruct offset procedures.
SaveOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
saveAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.save;
saveCard: CARD32 ~ LOOPHOLE[saveAddress];
offset: TargetArchitecture.Displacement ~
(saveCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
CallSaveRegsOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
callSaveRegsAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.callSaveRegs;
callSaveRegsCard: CARD32 ~ LOOPHOLE[callSaveRegsAddress];
offset: TargetArchitecture.Displacement ~
(callSaveRegsCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
ArgSaveAreaOnceOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
argSaveAreaOnceAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.argSaveAreaOnce;
argSaveAreaOnceCard: CARD32 ~ LOOPHOLE[argSaveAreaOnceAddress];
offset: TargetArchitecture.Displacement ~
(argSaveAreaOnceCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
ArgHiClientDataOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
argHiClientDataAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.argHiClientData;
argHiClientDataCard: CARD32 ~ LOOPHOLE[argHiClientDataAddress];
offset: TargetArchitecture.Displacement ~
(argHiClientDataCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
CallClientProcOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress: LONG POINTER TO SPARCBreakpoint.PatchStruct ~ @patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
callClientProcAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.callClientProc;
callClientProcCard: CARD32 ~ LOOPHOLE[callClientProcAddress];
offset: TargetArchitecture.Displacement ~
(callClientProcCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
ArgLoClientDataOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
argLoClientDataAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.argLoClientData;
argLoClientDataCard: CARD32 ~ LOOPHOLE[argLoClientDataAddress];
offset: TargetArchitecture.Displacement ~
(argLoClientDataCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
CallRestoreRegsOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
callRestoreRegsAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.callRestoreRegs;
callRestoreRegsCard: CARD32 ~ LOOPHOLE[callRestoreRegsAddress];
offset: TargetArchitecture.Displacement ~
(callRestoreRegsCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
ArgSaveAreaAgainOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
argSaveAreaAgainAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.argSaveAreaAgain;
argSaveAreaAgainCard: CARD32 ~ LOOPHOLE[argSaveAreaAgainAddress];
offset: TargetArchitecture.Displacement ~
(argSaveAreaAgainCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
RestoreOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
restoreAddress:
LONG
POINTER
TO SPARCArchitecture.SPARCInstruction ~
@patchStruct.closureCaller.restore;
restoreCard: CARD32 ~ LOOPHOLE[restoreAddress];
offset: TargetArchitecture.Displacement ~
(restoreCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
MangerOffset:
PROCEDURE []
RETURNS [TargetArchitecture.Displacement] ~
TRUSTED {
patchStruct: SPARCBreakpoint.PatchStruct;
patchStructAddress:
LONG
POINTER
TO SPARCBreakpoint.PatchStruct ~
@patchStruct;
patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress];
mangerAddress:
LONG
POINTER
TO SPARCManger.Manger ~
@patchStruct.manger;
mangerCard: CARD32 ~ LOOPHOLE[mangerAddress];
offset: TargetArchitecture.Displacement ~
(mangerCard - patchStructCard) * BYTES[UNIT];
RETURN [offset];
};
}.