-- 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..