-- LoadState.mesa  Last Modified by Sandman on August 1, 1980  10:31 AM 
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoFileDefs USING [eofDA, FP, NullSN, vDA],
  BcdOps USING [BcdBase],
  ControlDefs USING [GFTIndex],
  LoadStateFormat USING [
    AltoVersionID, BcdObject, ConfigIndex, LoadState, ModuleInfo, ModuleTable,
    NullConfig, NullModule],
  LoadStateOps USING [EnumerationDirection, Map],
  MiscDefs USING [Zero],
  NucleusOps USING [],
  SDDefs USING [SD, sGFTLength],
  SegmentDefs: FROM "segmentdefs" USING [
    DefaultMDSBase, DeleteFileSegment, FileHint, FileSegmentAddress,
    FileSegmentHandle, GetFileFP, GetFileSegmentDA, HardUp, InsertFile,
    MakeSwappedIn, NewFileSegment, Read, SwapUp, Unlock, VMtoFileSegment],
  Storage USING [Node, Free];

LoadState: PROGRAM
  IMPORTS MiscDefs, SegmentDefs, Storage EXPORTS LoadStateOps, NucleusOps =PUBLIC

  BEGIN OPEN ControlDefs, LoadStateOps, LoadStateFormat;

  FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;

  state, initstate: FileSegmentHandle;
  loadstate: LoadState;
  gft: ModuleTable;
  dirty: BOOLEAN;

  LoadStateFull: SIGNAL = CODE;
  LoadStateInvalid: SIGNAL = CODE;

  InputLoadState: PROCEDURE RETURNS [ConfigIndex] =
    BEGIN OPEN SegmentDefs;
    MakeSwappedIn[seg: state, base: DefaultMDSBase, info: HardUp];
    loadstate ← FileSegmentAddress[state];
    IF loadstate.versionident # AltoVersionID THEN
      ERROR LoadStateInvalid[ ! UNWIND => Unlock[state]];
    gft ← DESCRIPTOR[@loadstate.gft, SDDefs.SD[SDDefs.sGFTLength]];
    RETURN[loadstate.nBcds]
    END;

  UpdateLoadState: PROCEDURE [config: ConfigIndex, bcd: BcdOps.BcdBase] =
    BEGIN OPEN SegmentDefs;
    bcdseg: FileSegmentHandle ← VMtoFileSegment[bcd];
    fp: AltoFileDefs.FP;
    da: AltoFileDefs.vDA;
    IF bcd = NIL THEN ERROR LoadStateInvalid;
    IF config >= LAST[ConfigIndex] THEN ERROR LoadStateFull;
    da ←
      WITH s: bcdseg SELECT FROM disk => s.hint.da, ENDCASE => AltoFileDefs.eofDA;
    GetFileFP[bcdseg.file, @fp];
    loadstate.bcds[config] ←
      [exports: bcd.nExports # 0 OR bcd.nModules = 1,
	typeExported: bcd.typeExported, pages: bcdseg.pages,
	body: alto[fp: fp, da: da, base: bcdseg.base]];
    IF config >= loadstate.nBcds THEN loadstate.nBcds ← loadstate.nBcds + 1;
    dirty ← TRUE;
    END;

  RemoveConfig: PUBLIC PROCEDURE [map: Map, config: ConfigIndex] =
    BEGIN
    i: CARDINAL;
    FOR i IN [1..LENGTH[gft]) DO
      IF gft[i].config > config AND gft[i].config # NullConfig THEN
	gft[i].config ← gft[i].config - 1;
      ENDLOOP;
    FOR i IN [1..LENGTH[map]) DO gft[map[i]] ← NullModule; ENDLOOP;
    FOR i IN [config..loadstate.nBcds) DO
      loadstate.bcds[i] ← loadstate.bcds[i + 1]; ENDLOOP;
    dirty ← TRUE;
    loadstate.nBcds ← loadstate.nBcds - 1;
    END;

  ReleaseLoadState: PROCEDURE =
    BEGIN
    IF ~state.swappedin THEN RETURN;
    SegmentDefs.Unlock[state];
    state.inuse ← TRUE;
    IF state.lock = 0 THEN
      BEGIN
      IF dirty THEN
	BEGIN
	state.write ← TRUE;
	SegmentDefs.SwapUp[state];
	dirty ← state.write ← FALSE;
	END;
      loadstate ← NIL;
      END;
    END;

  EnterModule: PROCEDURE [rgfi: GFTIndex, module: ModuleInfo] =
    BEGIN gft[rgfi] ← module; dirty ← TRUE; END;

  GetModule: PROCEDURE [rgfi: GFTIndex] RETURNS [module: ModuleInfo] =
    BEGIN RETURN[gft[rgfi]]; END;

  MapConfigToReal: PROCEDURE [cgfi: GFTIndex, config: ConfigIndex]
    RETURNS [rgfi: GFTIndex] =
    BEGIN
    IF cgfi = 0 THEN RETURN[0];
    FOR rgfi IN [0..LENGTH[gft]) DO
      IF gft[rgfi].config = config AND gft[rgfi].gfi = cgfi THEN RETURN[rgfi];
      ENDLOOP;
    RETURN[0];
    END;

  MapRealToConfig: PROCEDURE [rgfi: GFTIndex]
    RETURNS [cgfi: GFTIndex, config: ConfigIndex] =
    BEGIN RETURN[gft[rgfi].gfi, gft[rgfi].config]; END;

  GetMap: PROCEDURE [config: ConfigIndex] RETURNS [map: Map] =
    BEGIN
    max: CARDINAL ← 0;
    i: GFTIndex;
    FOR i IN [0..LENGTH[gft]) DO
      IF gft[i].config = config THEN max ← MAX[max, gft[i].gfi]; ENDLOOP;
    map ← DESCRIPTOR[Storage.Node[max + 1], max + 1];
    MiscDefs.Zero[BASE[map], max + 1];
    FOR i IN [0..LENGTH[gft]) DO
      IF gft[i].config = config THEN map[gft[i].gfi] ← i; ENDLOOP;
    END;

  ReleaseMap: PROCEDURE [map: Map] =
    BEGIN IF BASE[map] # NIL THEN Storage.Free[BASE[map]]; END;

  AcquireBcd: PROCEDURE [config: ConfigIndex] RETURNS [bcd: BcdOps.BcdBase] =
    BEGIN OPEN SegmentDefs;
    fp: POINTER TO AltoFileDefs.FP;
    b: POINTER TO alto BcdObject;
    seg: FileSegmentHandle;
    WITH object: loadstate.bcds[config] SELECT FROM
      alto => b ← @object;
      ENDCASE => ERROR LoadStateInvalid;
    fp ← IF b.fp.serial = AltoFileDefs.NullSN THEN @state.file.fp ELSE @b.fp;
    seg ← NewFileSegment[InsertFile[fp, Read], b.base, b.pages, Read];
    IF b.da # AltoFileDefs.eofDA THEN
      WITH s: seg SELECT FROM
	disk => s.hint ← SegmentDefs.FileHint[b.da, b.base];
	ENDCASE;
    MakeSwappedIn[seg: seg, base: DefaultMDSBase, info: HardUp];
    IF b.da = AltoFileDefs.eofDA THEN
      BEGIN b.da ← GetFileSegmentDA[seg]; dirty ← TRUE END;
    RETURN[FileSegmentAddress[seg]];
    END;

  ReleaseBcd: PROCEDURE [bcd: BcdOps.BcdBase] =
    BEGIN OPEN SegmentDefs;
    seg: FileSegmentHandle ← VMtoFileSegment[bcd];
    IF seg = NIL THEN RETURN;
    Unlock[seg];
    IF seg.lock = 0 THEN DeleteFileSegment[seg];
    END;

  EnumerateModules: PROCEDURE [
    proc: PROCEDURE [GFTIndex, ModuleInfo] RETURNS [BOOLEAN]] RETURNS [GFTIndex] =
    BEGIN
    i: GFTIndex;
    FOR i IN [0..LENGTH[gft]) DO IF proc[i, gft[i]] THEN RETURN[i]; ENDLOOP;
    RETURN[0]
    END;

  EnumerateBcds: PROCEDURE [
    dir: EnumerationDirection, proc: PROCEDURE [ConfigIndex] RETURNS [BOOLEAN]]
    RETURNS [config: ConfigIndex] =
    BEGIN
    SELECT dir FROM
      recentfirst =>
	FOR config DECREASING IN [0..loadstate.nBcds) DO
	  IF proc[config] THEN RETURN[config]; ENDLOOP;
      recentlast =>
	FOR config IN [0..loadstate.nBcds) DO
	  IF proc[config] THEN RETURN[config]; ENDLOOP;
      ENDCASE;
    RETURN[NullConfig]
    END;

  BcdUnresolved: PROCEDURE [bcd: ConfigIndex] RETURNS [BOOLEAN] =
    BEGIN
    i: GFTIndex;
    FOR i IN [0..LENGTH[gft]) DO
      IF bcd = gft[i].config AND ~gft[i].resolved THEN RETURN[TRUE]; ENDLOOP;
    RETURN[FALSE];
    END;

  BcdExports: PROCEDURE [bcd: ConfigIndex] RETURNS [BOOLEAN] =
    BEGIN RETURN[loadstate.bcds[bcd].exports]; END;

  BcdExportsTypes: PROCEDURE [bcd: ConfigIndex] RETURNS [BOOLEAN] =
    BEGIN RETURN[loadstate.bcds[bcd].typeExported]; END;

  GetBcdInfo: PROCEDURE [config: ConfigIndex]
    RETURNS [fp: POINTER TO AltoFileDefs.FP, base, pages: CARDINAL] =
    BEGIN
    WITH b: loadstate.bcds[config] SELECT FROM
      alto => RETURN[@b.fp, b.base, b.pages];
      ENDCASE;
    RETURN[NIL, 0, 0];
    END;

  SetBcdInfo: PROCEDURE [
    config: ConfigIndex, fp: POINTER TO AltoFileDefs.FP, base, pages: CARDINAL] =
    BEGIN
    WITH b: loadstate.bcds[config] SELECT FROM
      alto => BEGIN b.fp ← fp↑; b.base ← base; b.pages ← pages END;
      ENDCASE;
    END;


  END..