-- file Sequencer.Mesa
-- last modified by Sandman, Jan 15, 1980 1:54 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [PageSize],
  AltoFileDefs: FROM "altofiledefs" USING [LD, TIME],
  CharIO: FROM "chario" USING [CR, PutChar, PutOctal, PutString],
  CompilerOps: FROM "compilerops" USING [TableId, Transaction],
  CompilerUtil: FROM "compilerutil"
    USING [
      Pass1, Pass2, Pass3, Pass4, Code, PassIndex,
      P1Unit, P2Unit, P3Unit, P4Unit, P5module,
      EndObjectFile, PrintBodies, PrintSymbols, PrintTree, SetObjectStamp,
      StartObjectFile, TableOut],
  ComData: FROM "comdata"
    USING [
      compilerVersion, definitionsOnly, errorFile, errorStream,
      linkCount, netNumber, nErrors, nWarnings, objectBytes, objectFile,
      objectFrameSize, objectStream, objectVersion, ownSymbols,
      sourceFile, sourceStream, sourceTokens, sourceVersion, switches],
--ControlDefs: FROM "controldefs" USING [GlobalFrameHandle],
  Copier: FROM "copier" USING [FileInit, FileReset, OwnFile],
  FrameDefs: FROM "framedefs" USING [SwapOutCode],
  FrameOps: FROM "frameops" USING [CodeHandle],
  ImageDefs: FROM "imagedefs" USING [ImageVersion],
  LiteralOps: FROM "literalops" USING [Initialize, Finalize],
  Log: FROM "log" USING [Error],
  MiscDefs: FROM "miscdefs" USING [GetNetworkNumber],
  Mopcodes: FROM "mopcodes" USING [zEXCH],
  SegmentDefs: FROM "segmentdefs"
    USING [
      FileHandle, DataSegmentHandle, FileSegmentHandle, PageCount,
      Read, Write, Append, DefaultVersion, OldFileOnly, DefaultBase,
      CopyDataToFileSegment, DeleteDataSegment, DeleteFileSegment,
      FileSegmentAddress, InsufficientVM, MoveFileSegment, NewFile,
      NewFileSegment, SegmentAddress, SegmentFault, SetEndOfFile,
      SwapError, SwapIn, SwapOut, Unlock, MakeDataSegment, EasyUp],
  StreamDefs: FROM "streamdefs"
    USING [StreamHandle, StreamObject, CloseDiskStream, CreateByteStream],
  SystemDefs: FROM "systemdefs"
    USING [AllocateHeapNode, FreeHeapNode, PruneHeap],
  SymbolPack: FROM "symbolpack",
  SymbolTable: FROM "symboltable" USING [RestartCache, SuspendCache],
  SymbolOps: FROM "symbolops" USING [Finalize, Initialize],
  SymbolSegment: FROM "symbolsegment" USING [Tables],
  Table: FROM "table" USING [Region, Create, Destroy, Failure, Overflow],
  TrapDefs: FROM "trapdefs" USING [SendMsgSignal],
  Tree: FROM "tree" USING [Link],
  TreeOps: FROM "treeops" USING [Finalize, Initialize, PopTree];

Sequencer: PROGRAM [
    explicitSwapping: BOOLEAN,
    scratchFile: SegmentDefs.FileHandle,
    tableSegment: ARRAY CompilerOps.TableId OF SegmentDefs.FileSegmentHandle]
    IMPORTS
	CompilerUtil, Copier, Log, FrameDefs, FrameOps, ImageDefs,
	CharIO, LiteralOps, MiscDefs, SegmentDefs, StreamDefs,
	SymbolTable, SymbolOps, SystemDefs, Table, TrapDefs, TreeOps, 
	ownSymbols: SymbolPack, dataPtr: ComData
    EXPORTS CompilerOps, CompilerUtil = 
  BEGIN

-- overlay control

  PassIndex: TYPE = CompilerUtil.PassIndex;
  GlobalFrameHandle: TYPE = POINTER --ControlDefs.GlobalFrameHandle--;

  PassLink: TYPE = RECORD [
    frame: GlobalFrameHandle,
    link: POINTER TO PassLink];

  passRoot: ARRAY PassIndex OF POINTER TO PassLink ← ALL[NIL];

  LoadPass: PROCEDURE [pass: PassIndex] =
    BEGIN
    p: POINTER TO PassLink;
    handle: SegmentDefs.FileSegmentHandle;
    IF explicitSwapping THEN
      FOR p ← passRoot[pass], p.link UNTIL p = NIL
	DO
	IF (handle ← FrameOps.CodeHandle[p.frame]) # NIL
	  THEN
	    BEGIN	-- Don't use SwapInCode, it will mess up Start Traps
	    SegmentDefs.SwapIn[handle];  SegmentDefs.Unlock[handle];
	    END;
	ENDLOOP;
    END;

  UnloadPass: PROCEDURE [pass: PassIndex] =
    BEGIN
    p: POINTER TO PassLink;
    IF explicitSwapping THEN
      FOR p ← passRoot[pass], p.link UNTIL p = NIL
	DO
	FrameDefs.SwapOutCode[p.frame !SegmentDefs.SwapError => CONTINUE];
	ENDLOOP;
    [] ← SystemDefs.PruneHeap[];
    END;

  MakeSwappable: PUBLIC PROCEDURE [module: PROGRAM, pass: PassIndex] =
    BEGIN
    IF explicitSwapping THEN
      BEGIN
      frame: GlobalFrameHandle = LOOPHOLE[module];
      p: POINTER TO PassLink = SystemDefs.AllocateHeapNode[SIZE[PassLink]];
      q: POINTER TO PassLink;
      p↑ ← PassLink[frame:frame, link:NIL];
      IF passRoot[pass] = NIL
	THEN passRoot[pass] ← p
	ELSE
	  BEGIN  q ← passRoot[pass];
	  UNTIL q.link = NIL DO q ← q.link ENDLOOP;
	  q.link ← p;
	  END;
      END;
    END;


-- cursor control

  Cursor: TYPE = MACHINE DEPENDENT RECORD [
    top: PRIVATE CursorRow,
    row1: CursorRow,
    m12: PRIVATE CursorFill,
    row2: CursorRow,
    m23: PRIVATE CursorFill,
    row3: CursorRow,
    bottom: PRIVATE CursorRow];

  TheCursor: POINTER TO Cursor = LOOPHOLE[431B];
  savedCursor: Cursor;

  CursorRow: TYPE = ARRAY [0..2) OF WORD;
  CursorFill: TYPE = ARRAY [0..3) OF WORD;

  Two: CursorRow = [147763B, 147763B];
  L1: CursorRow = [147777B, 147777B];
  R1: CursorRow = [177763B, 177763B];
  M1: CursorRow = [177177B, 177177B];

  ClearCursor: PROCEDURE =
    BEGIN
    CursorBase: POINTER = TheCursor;
    i: CARDINAL;
    FOR i IN [0 .. 16) DO (CursorBase+i)↑ ← -1 ENDLOOP;
    END;


-- table storage management
  
  PageCount: TYPE = SegmentDefs.PageCount;

  TablePageStart: PageCount = 64;
  TablePageStep:  PageCount = 8;
  TablePageLimit: PageCount = 128;

  tableDataSegment: SegmentDefs.DataSegmentHandle;
  scratchFileSegment: SegmentDefs.FileSegmentHandle;
  tablePages: PageCount;

  tableRegion: Table.Region;

  LoadTable: PROCEDURE [nPages: PageCount] =
    BEGIN
    OPEN SegmentDefs;
    IF nPages # tablePages
      THEN
	BEGIN
	IF scratchFileSegment = NIL
	  THEN
	    BEGIN
	    scratchFileSegment ← NewFileSegment[
		file: scratchFile,
		base: DefaultBase,
		pages: tablePages,
		access: Read+Write];
	    CopyDataToFileSegment[tableDataSegment, scratchFileSegment
	      ! SegmentFault =>
		BEGIN
		SetEndOfFile[scratchFile, scratchFileSegment.base+nPages, 0];
		RETRY
		END];
	    DeleteDataSegment[tableDataSegment];  tableDataSegment ← NIL;
	    END
	  ELSE
	    BEGIN Unlock[scratchFileSegment]; SwapOut[scratchFileSegment] END;
	MoveFileSegment[scratchFileSegment, DefaultBase, nPages];
	tablePages ← nPages;
	SwapIn[scratchFileSegment ! SegmentFault =>
	  BEGIN
	  SetEndOfFile[scratchFile, scratchFileSegment.base+nPages, 0];
	  RETRY
	  END];
	END;
    tableRegion ← [
	origin: LOOPHOLE[SegmentAddress[IF tableDataSegment # NIL
		    THEN tableDataSegment ELSE scratchFileSegment]],
	size: tablePages*AltoDefs.PageSize];
    END;
    

-- table segments

  TableSegment: PUBLIC PROCEDURE [id: CompilerOps.TableId]
      RETURNS [SegmentDefs.FileSegmentHandle] =
    BEGIN  RETURN [tableSegment[id]]  END;


-- compiler sequencing

  pass: CHARACTER ['1..'5];

  Initialize: PROCEDURE =
    BEGIN
    weights: ARRAY SymbolSegment.Tables OF CARDINAL ←
      [20, 8, 2, 4, 2, 2, 2, 1, 1, 1];  -- relative sizes (empirical)
    savedCursor ← TheCursor↑;  ClearCursor[];
    LoadTable[TablePageStart];
    Table.Create[tableRegion, DESCRIPTOR[weights]];
    SymbolOps.Initialize[];  LiteralOps.Initialize[];  TreeOps.Initialize[];
    END;

  Finalize: PROCEDURE [parms: POINTER TO CompilerOps.Transaction] =
    BEGIN
    parms.sourceTokens ← dataPtr.sourceTokens;
    parms.nErrors ← dataPtr.nErrors;  parms.nWarnings ← dataPtr.nWarnings;
    parms.objectBytes ← dataPtr.objectBytes;
    parms.objectFrameSize ← dataPtr.objectFrameSize;  
    parms.linkCount ← dataPtr.linkCount;
    IF dataPtr.objectStream # NIL
      THEN CompilerUtil.EndObjectFile[dataPtr.nErrors=0];
    TreeOps.Finalize[];  LiteralOps.Finalize[];  SymbolOps.Finalize[];
    Table.Destroy[];
    TheCursor↑ ← savedCursor;
    END;

  Debug: PROCEDURE [tree, symbols: PROCEDURE] =
    BEGIN  LoadPass[debug];  tree[];  symbols[];  UnloadPass[debug]  END;


  ErrorPut: PROCEDURE [s: StreamDefs.StreamHandle, c: CHARACTER] =
    BEGIN OPEN SegmentDefs;
    s.destroy[s];
    dataPtr.errorStream ← StreamDefs.CreateByteStream[
		NewFile[dataPtr.errorFile, Write+Append, DefaultVersion],
		Write+Append];
    dataPtr.errorStream.put[dataPtr.errorStream, c];
    END;

  ErrorDestroy: PROCEDURE [s: StreamDefs.StreamHandle] =
    BEGIN SystemDefs.FreeHeapNode[s] END;

  NoSource: PUBLIC ERROR = CODE;
  Punt: PUBLIC ERROR = CODE;

  CreateTime: PROCEDURE [s: StreamDefs.StreamHandle] RETURNS [time: LONG INTEGER] =
    BEGIN
    WITH s: s SELECT FROM
      Disk =>
	BEGIN  OPEN SegmentDefs;
	Exch: PROCEDURE [AltoFileDefs.TIME] RETURNS [LONG INTEGER] =
	  MACHINE CODE BEGIN  Mopcodes.zEXCH  END;
	seg: FileSegmentHandle ← NewFileSegment[s.file, 0, 1, Read];
	p: POINTER TO AltoFileDefs.LD;
	SwapIn[seg];  p ← FileSegmentAddress[seg];
	time ← Exch[p.created];
	Unlock[seg];  DeleteFileSegment[seg];
	END;
      ENDCASE =>  time ← 0;
    END;


  Compile: PUBLIC PROCEDURE [parms: POINTER TO CompilerOps.Transaction] =
    BEGIN
    root: Tree.Link;
    objectFileHint: SegmentDefs.FileHandle;
    msg, signal: UNSPECIFIED;
    nParseErrors: CARDINAL;
    parsed, aborted: BOOLEAN;

    PrintTreeRoot: PROCEDURE = BEGIN CompilerUtil.PrintTree[root] END;

    IF parms.source.stream = NIL
      THEN
	BEGIN  OPEN SegmentDefs;
	ENABLE ANY => GO TO noSource;
	parms.source.stream ← StreamDefs.CreateByteStream[
		NewFile[parms.source.name, Read, OldFileOnly],
		Read];
	EXITS
	  noSource => ERROR NoSource;
	END;
    IF parms.error.stream = NIL
      THEN
	BEGIN
	parms.error.stream ←
	  SystemDefs.AllocateHeapNode[SIZE[Other StreamDefs.StreamObject]];
	parms.error.stream↑ ← 
	  [NULL, NULL, NULL, ErrorPut, NULL, ErrorDestroy, NIL, Other[,]];
	END;
    [dataPtr.sourceFile, dataPtr.sourceStream] ← parms.source;
    [dataPtr.objectFile, dataPtr.objectStream] ← parms.object;
    [dataPtr.errorFile, dataPtr.errorStream] ← parms.error;
    dataPtr.switches ← parms.switches;

    dataPtr.sourceVersion ← [0, 0, CreateTime[parms.source.stream]];
    dataPtr.nErrors ← dataPtr.nWarnings ← 0;  aborted ← FALSE;
    Initialize[];

      BEGIN
      ENABLE 
	BEGIN
	Table.Overflow => 
	  BEGIN
	  IF tablePages < TablePageLimit THEN
	    BEGIN 
	    LoadTable[tablePages+TablePageStep
	      ! SegmentDefs.InsufficientVM => IF ~dataPtr.switches['d] THEN
		GO TO storageFragmented];
	    RESUME[tableRegion]
	    END;
	  IF ~dataPtr.switches['d] THEN GO TO storageFull;
	  END;
	Table.Failure =>  IF ~dataPtr.switches['d] THEN GO TO storageFull;
	UNWIND =>  Finalize[parms];
	ANY =>
	  IF ~dataPtr.switches['d] THEN
	    BEGIN
	    [msg, signal] ← TrapDefs.SendMsgSignal[]; GO TO uncaughtSignal
	    END
	END;

    -- first pass
      pass ← '1;  LoadPass[pass1];
      TheCursor.row2 ← M1;
      parsed ← CompilerUtil.P1Unit[];
      nParseErrors ← dataPtr.nErrors;
      ClearCursor[];  UnloadPass[pass1];
      IF ~parsed THEN GO TO failed;
      root ← TreeOps.PopTree[];
      StreamDefs.CloseDiskStream[dataPtr.sourceStream];
      IF parms.debugPass <= 1
	THEN Debug[PrintTreeRoot, CompilerUtil.PrintSymbols];

    -- second pass
      pass ← '2;  LoadPass[pass2];
      TheCursor.row1 ← L1; TheCursor.row3 ← R1;
      root ← CompilerUtil.P2Unit[root];
      ClearCursor[];  UnloadPass[pass2];
      IF parms.debugPass <= 2
	THEN Debug[PrintTreeRoot, CompilerUtil.PrintSymbols];
      IF dataPtr.nErrors # 0 THEN dataPtr.switches['x] ← FALSE;

    -- third and fourth passes
      CompilerUtil.SetObjectStamp[];
      Copier.FileInit[dataPtr.objectFile, dataPtr.objectVersion];
      objectFileHint ← NIL;

	BEGIN
	  ENABLE
	    BEGIN
	    Table.Overflow =>
	      BEGIN
	      IF tablePages >= TablePageLimit THEN GO TO noSpace;
	      SymbolTable.SuspendCache[];
	      LoadTable[tablePages+TablePageStep
		! SegmentDefs.InsufficientVM => GO TO noVM];
	      SymbolTable.RestartCache[];
	      RESUME[tableRegion]
	      END;
	    Table.Failure => GO TO noSpace;
	    Copier.OwnFile => BEGIN objectFileHint ← file; RESUME END;
	    END;

	pass ← '3;  LoadPass[pass3];
	TheCursor.row1 ← R1; TheCursor.row2 ← M1; TheCursor.row3 ← L1; 
	root ← CompilerUtil.P3Unit[root];
	ClearCursor[];  UnloadPass[pass3];

	IF parms.debugPass <= 3
	  THEN Debug[PrintTreeRoot, CompilerUtil.PrintSymbols];
	IF dataPtr.nErrors > nParseErrors THEN GO TO DeleteFiles;

	IF dataPtr.objectStream = NIL
	  THEN  parms.object.stream ← dataPtr.objectStream ←
		  CompilerUtil.StartObjectFile[objectFileHint];

	pass ← '4;  LoadPass[pass4];
	TheCursor.row1 ← TheCursor.row3 ← Two;
	CompilerUtil.P4Unit[root];
	ClearCursor[];  UnloadPass[pass4];
	IF parms.debugPass <= 4
	  THEN Debug[CompilerUtil.PrintBodies, CompilerUtil.PrintSymbols];
	GO TO DeleteFiles;

	EXITS
	  DeleteFiles => Copier.FileReset[];
	  noSpace =>  BEGIN Copier.FileReset[]; GO TO storageFull END;
	  noVM =>  BEGIN Copier.FileReset[]; GO TO storageFragmented END;
	END;

      IF dataPtr.nErrors # 0 THEN GO TO failed;

    -- fifth pass
      IF ~dataPtr.definitionsOnly THEN
	BEGIN
	ENABLE  UNWIND =>  CompilerUtil.EndObjectFile[FALSE];
	pass ← '5;  LoadPass[pass5];
	TheCursor.row1 ← TheCursor.row3 ← Two; TheCursor.row2 ← M1;
	CompilerUtil.P5module[];
	ClearCursor[];  UnloadPass[pass5];
	END;

      TheCursor.row1 ← TheCursor.row2 ← TheCursor.row3 ← Two;
      CompilerUtil.TableOut[dataPtr.sourceFile];
      IF dataPtr.nErrors # 0 THEN GO TO failed;

      EXITS
	failed =>  aborted ← TRUE;
	uncaughtSignal =>
	  BEGIN  OPEN CharIO;
	  Log.Error[compilerError];  aborted ← TRUE;
	  PutString[dataPtr.errorStream, "Pass = "L];
	  PutChar[dataPtr.errorStream, pass];
	  PutString[dataPtr.errorStream, ", signal = "L];
	  PutOctal[dataPtr.errorStream, signal];
	  PutString[dataPtr.errorStream, ", message = "L];
	  PutOctal[dataPtr.errorStream, msg];
	  PutChar[dataPtr.errorStream, CR];
	  Finalize[parms];  ERROR Punt[]
	  END;
	storageFragmented =>  StorageProblem["Too Fragmented"L];
	storageFull =>  StorageProblem["Overflow"L];
      END;

    Finalize[parms];

    END;

  StorageProblem: PROCEDURE [message: STRING] =
    BEGIN  OPEN CharIO;
    dataPtr.nErrors ← dataPtr.nErrors+1;
    PutChar[dataPtr.errorStream, CR];
    PutString[dataPtr.errorStream, "Storage "L];
    PutString[dataPtr.errorStream, message];
    PutString[dataPtr.errorStream, " in Pass "L];
    PutChar[dataPtr.errorStream, pass];
    PutChar[dataPtr.errorStream, CR];
    END;


-- * * * * * *  M A I N   B O D Y   C O D E  * * * * * *

  START dataPtr; 	-- initialize STRING variables, etc.
  START ownSymbols;
  dataPtr.ownSymbols ← ownSymbols;

-- set up swapping
  BEGIN  OPEN CompilerUtil;
  MakeSwappable[Pass1, pass1];  --START Pass1;  UnloadPass[pass1];
  MakeSwappable[Pass2, pass2];  --START Pass2;  UnloadPass[pass2];
  MakeSwappable[Pass3, pass3];  --START Pass3;  UnloadPass[pass3];
  MakeSwappable[Pass4, pass4];  --START Pass4;  UnloadPass[pass4];
  MakeSwappable[Code, pass5];   --START Code;   UnloadPass[pass5];
  END;

  dataPtr.compilerVersion ← ImageDefs.ImageVersion[];
  dataPtr.netNumber ← MiscDefs.GetNetworkNumber[];

-- obtain the scratch area
  tableDataSegment ← SegmentDefs.MakeDataSegment[
	base: SegmentDefs.DefaultBase,
	pages: TablePageStart,
	info: SegmentDefs.EasyUp];
  tablePages ← TablePageStart;
  scratchFileSegment ← NIL;

  END.