-- File: Image.Mesa
-- Last edited by Sandman; September 12, 1980 11:03 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
AllocDefs USING [DefaultDataSegmentInfo],
AltoDefs USING [PageCount, PageNumber, PageSize],
AltoFileDefs USING [eofDA, FA, TIME, vDA],
CacheOps USING [
Flush, GetCoreFile, GetCS, GetPageItem, PageItem, PageNumber, SetCoreFile,
SetPageItem],
BootmesaOps,
CommanderDefs USING [AddCommand],
ControlDefs USING [GFTIndex],
FileLookupDefs USING [GetFileName],
ImageFormat USING [
FirstImageDataPage, HeaderPages, ImagePrefix, MapItem, VersionID],
IODefs USING [CR, NumberFormat, WriteChar, WriteLine, WriteNumber, WriteString],
MiscDefs USING [DAYTIME, Zero],
SegmentDefs USING [
AddressFromPage, Append, DataSegmentHandle, DefaultVersion, DeleteFileSegment,
FileHandle, FileSegmentAddress, FileSegmentHandle, LockFile, NewFile,
NewFileSegment, Read, SwapIn, Unlock, UnlockFile, Write],
SegOps USING [EnumerateSegs, Seg, vmFile],
WartDefs USING [MaxImagePage, TableBase],
StreamDefs USING [
CleanupDiskStream, CreateWordStream, GetFA, GetIndex, SetIndex, StreamIndex,
WriteBlock],
String USING [AppendString];
Image: PROGRAM
IMPORTS
CacheOps, CommanderDefs, BootmesaOps, FileLookupDefs, IODefs, MiscDefs,
StreamDefs, SegmentDefs, String, SegOps
EXPORTS BootmesaOps
SHARES ControlDefs =
BEGIN OPEN SegOps, AltoDefs, ControlDefs, BootmesaOps;
data: POINTER TO BootmesaOps.BootData ← @dataObject;
FileHandle: TYPE = SegmentDefs.FileHandle;
FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;
DataSegmentHandle: TYPE = SegmentDefs.DataSegmentHandle;
-- image file management
mapindex: CARDINAL ← 0;
MapOverflow: PUBLIC ERROR = CODE;
EnterMapItem: PROCEDURE [base: PageNumber, pages: PageCount] =
BEGIN
map: POINTER TO ARRAY [0..0) OF normal ImageFormat.MapItem =
LOOPHOLE[@data.image.map];
IF mapindex > AltoDefs.PageSize - SIZE[ImageFormat.ImagePrefix] THEN
BootmesaError["MapOverflow"L];
IF pages > 127 THEN BootmesaError["Segment too big"L];
map[mapindex] ← ImageFormat.MapItem[base, pages, normal[]];
mapindex ← mapindex + SIZE[normal ImageFormat.MapItem];
RETURN
END;
EnterMapSegment: PUBLIC PROCEDURE [s: Seg] =
BEGIN IF s.in THEN EnterMapItem[s.vmPage, s.pages]; RETURN END;
SegmentToMap: PUBLIC PROCEDURE [s: Seg] =
BEGIN OPEN IODefs;
octal: NumberFormat = NumberFormat[8, FALSE, FALSE, 1];
base: NumberFormat = NumberFormat[8, FALSE, FALSE, 4];
pages: NumberFormat = NumberFormat[8, FALSE, FALSE, 6];
address: NumberFormat = NumberFormat[8, FALSE, TRUE, 9];
filename: STRING;
s.imageBase ← StreamDefs.GetIndex[data.imageStream].page + 1;
WriteNumber[s.imageBase, base];
WriteNumber[s.pages, pages];
WriteNumber[SegmentDefs.AddressFromPage[s.vmPage], address];
WriteString[" "L];
filename ←
IF s.realFile = SegOps.vmFile THEN "VM"
ELSE FileLookupDefs.GetFileName[
IF s.trueFile = NIL THEN s.realFile ELSE s.trueFile];
WriteString[filename];
WriteString[" ["L];
WriteNumber[s.base, octal];
WriteChar[',];
WriteNumber[s.pages, octal];
WriteChar[']];
IF ~s.data THEN PrintModuleNames[s];
WriteChar[CR];
RETURN
END;
cseg1, cseg2: SegmentDefs.FileSegmentHandle ← NIL;
CacheSegment: PROCEDURE [seg: SegmentDefs.FileSegmentHandle] =
BEGIN OPEN SegmentDefs;
Unlock[seg];
IF cseg1 # NIL THEN DeleteFileSegment[cseg1];
cseg1 ← cseg2;
cseg2 ← seg;
RETURN
END;
WriteSegment: PROCEDURE [fs: Seg] =
BEGIN OPEN SegmentDefs;
f: FileHandle ← fs.realFile;
s: FileSegmentHandle;
base: WartDefs.TableBase = segTable;
page: CARDINAL ← GetCurrentPosition[];
IF page > WartDefs.MaxImagePage THEN BootmesaError["Image file too large!"];
IF ~fs.data THEN [] ← MarkImageSegment[fs];
IF fs = data.vmTableSeg OR fs = data.daSeg THEN RETURN;
SegmentToMap[fs];
IF f = SegOps.vmFile THEN WriteVMSegment[fs]
ELSE IF fs = BootmesaOps.fakebcdseg THEN
BEGIN
EnterMapSegment[fs];
IF f = data.imageFile THEN
BEGIN IF (f ← fs.trueFile) = NIL THEN ERROR; fs.trueFile ← NIL; END;
s ← fs.link2;
IF StreamDefs.WriteBlock[
data.imageStream, FileSegmentAddress[s], s.pages*PageSize] # CARDINAL[
s.pages*PageSize] THEN ERROR;
END
ELSE
BEGIN
EnterMapSegment[fs];
IF f = data.imageFile THEN
BEGIN IF (f ← fs.trueFile) = NIL THEN ERROR; fs.trueFile ← NIL; END;
s ← NewFileSegment[f, fs.base, fs.pages, Read];
SwapIn[s];
IF StreamDefs.WriteBlock[
data.imageStream, FileSegmentAddress[s], s.pages*PageSize] # CARDINAL[
s.pages*PageSize] THEN ERROR;
CacheSegment[s];
END;
base[fs.index].base ← page;
RETURN
END;
WriteVMSegment: PROCEDURE [fs: Seg] =
BEGIN OPEN CacheOps;
p, basepage, segbase: PageNumber;
pi: PageItem;
segcount: PageCount ← 0;
getda: PROCEDURE RETURNS [AltoFileDefs.vDA] =
BEGIN
fa: AltoFileDefs.FA;
StreamDefs.GetFA[data.imageStream, @fa];
RETURN[fa.da]
END;
basepage ← fs.vmPage;
FOR p IN [basepage..basepage + fs.pages) DO
IF segcount = 0 THEN segbase ← p;
IF GetPageItem[p].p.page = 0 THEN
BEGIN
IF segcount # 0 THEN
BEGIN EnterMapItem[segbase, segcount]; segcount ← 0; END;
END
ELSE
BEGIN
segcount ← segcount + 1;
pi ← PageItem[StreamDefs.GetIndex[data.imageStream].page + 1, getda[]];
IF StreamDefs.WriteBlock[
data.imageStream, SegmentDefs.FileSegmentAddress[GetCS[GetPageItem[p]]],
PageSize] # PageSize THEN ERROR;
SetPageItem[p, pi];
END;
ENDLOOP;
IF segcount # 0 THEN EnterMapItem[segbase, segcount];
RETURN
END;
MarkImageSegment: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] =
BEGIN
f: FileHandle ← fs.realFile;
IF f # SegOps.vmFile AND (fs.in OR ~fs.data) THEN
BEGIN
IF fs.trueFile # NIL THEN ERROR;
fs.trueFile ← f;
fs.realFile ← data.imageFile;
data.imageFile.segcount ← data.imageFile.segcount + 1;
IF fs.in THEN data.imageFile.swapcount ← data.imageFile.swapcount + 1;
END;
RETURN[FALSE]
END;
GetCurrentPosition: PROCEDURE RETURNS [page: CARDINAL] =
BEGIN page ← StreamDefs.GetIndex[data.imageStream].page + 1; RETURN END;
GetImageHeader: PUBLIC PROCEDURE
RETURNS [header: SegmentDefs.FileSegmentHandle] =
BEGIN RETURN[data.headerSeg] END;
WriteData: PUBLIC PROCEDURE =
BEGIN
FindVMSegments: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] =
BEGIN
IF fs = data.vmTableSeg OR ~fs.data THEN RETURN[FALSE];
WriteSegment[fs];
RETURN[FALSE];
END;
[] ← EnumerateSegs[FindVMSegments];
RETURN
END;
WriteNonData: PUBLIC PROCEDURE =
BEGIN
Resident: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] =
BEGIN
IF fs.data OR ~fs.resident THEN RETURN[FALSE];
WriteSegment[fs];
RETURN[FALSE];
END;
SwappedIn: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] =
BEGIN
IF fs.data OR fs.resident OR ~fs.in THEN RETURN[FALSE];
WriteSegment[fs];
RETURN[FALSE];
END;
Other: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] =
BEGIN
IF fs.data OR fs.resident OR fs.in THEN RETURN[FALSE];
WriteSegment[fs];
RETURN[FALSE];
END;
[] ← EnumerateSegs[Resident];
[] ← EnumerateSegs[SwappedIn];
[] ← EnumerateSegs[Other];
RETURN
END;
versionID: CARDINAL ← 0;
InitializeImage: PUBLIC PROCEDURE =
BEGIN OPEN ImageFormat, SegmentDefs;
name: STRING ← [40];
time: AltoFileDefs.TIME ← MiscDefs.DAYTIME[];
seg: SegmentDefs.FileSegmentHandle;
String.AppendString[name, data.imageFileRoot];
String.AppendString[name, ".Image"L];
data.imageFile ← NewFile[name, Read + Write + Append, DefaultVersion];
SegmentDefs.LockFile[data.imageFile];
data.imageStream ← StreamDefs.CreateWordStream[
data.imageFile, Write + Append];
StreamDefs.SetIndex[
data.imageStream, StreamDefs.StreamIndex[FirstImageDataPage - 1, 0]];
StreamDefs.CleanupDiskStream[data.imageStream];
data.headerSeg ← seg ← NewFileSegment[data.imageFile, 1, HeaderPages, Write];
MakeResident[seg];
data.image ← FileSegmentAddress[data.headerSeg ← seg];
MiscDefs.Zero[data.image, HeaderPages*AltoDefs.PageSize];
data.image.prefix.versionident ←
IF versionID # 0 THEN versionID ELSE ImageFormat.VersionID;
data.image.prefix.options ← 0;
data.image.prefix.type ← bootmesa;
data.image.prefix.leaderDA ← AltoFileDefs.eofDA;
--data.image.prefix.creator ← NullVersion;
IODefs.WriteChar[IODefs.CR];
IODefs.WriteLine[" Image"L];
IODefs.WriteLine["Base Pages Address Source [base,pages]"L];
RETURN
END;
WriteImage: PUBLIC PROCEDURE =
BEGIN OPEN SegOps, CacheOps;
WriteData[];
SegmentDefs.UnlockFile[GetCoreFile[]];
[] ← Flush[0, AllocDefs.DefaultDataSegmentInfo, NIL];
SetCoreFile[data.imageFile];
WriteNonData[];
data.imageStream.destroy[data.imageStream];
RETURN
END;
PrintModuleNames: PROCEDURE [seg: Seg] =
BEGIN
name: STRING ← [60];
gfi: ControlDefs.GFTIndex;
mt: DESCRIPTOR FOR ARRAY OF BootmesaOps.ModuleInfo ← data.moduleTable;
IF seg.data THEN RETURN;
FOR gfi IN [1..LENGTH[data.moduleTable]) DO
IF mt[gfi].whenLoaded = notLoaded THEN LOOP;
IF mt[gfi].code = seg THEN
BEGIN
ModuleName[mt[gfi].frame, name];
IODefs.WriteChar[' ];
IODefs.WriteString[name];
name.length ← 0;
EXIT;
END;
ENDLOOP;
RETURN
END;
SetID: PROCEDURE [n: CARDINAL] = {versionID ← n};
CommanderDefs.AddCommand["SetVersionID", LOOPHOLE[SetID], 1].params[0] ←
[type: numeric, prompt: "VersionID"];
END..