-- file ModuleMaker.Mesa
-- last edited by Satterthwaite, August 26, 1980  1:43 PM

DIRECTORY
  AltoDefs: TYPE USING [BytesPerPage, BytesPerWord],
  BcdDefs: TYPE ,
  MiscDefs: TYPE USING [GetNetworkNumber, Zero],
  OsStaticDefs: TYPE USING [OsStatics],
  PGScondefs: TYPE USING [WriteSymbols],
  SegmentDefs: TYPE USING [
    FileSegmentHandle, FileHandle, OldFileOnly, Append, Read, Write, NewFile],
  StreamDefs: TYPE USING [
    StreamHandle, StreamIndex, GetIndex,
    CreateWordStream, FileLength, NewWordStream,
    ReadBlock, SetIndex, WriteBlock],
  StringDefs: TYPE USING [AppendChar, AppendString, EquivalentString, WordsForString],
  SystemDefs: TYPE USING [
    AllocateHeapNode, AllocateSegment, FreeHeapNode, FreeSegment, PagesForWords],
  TableCommand: TYPE USING [CreateTime, FindInterface, FindItem, MyBcdVersion],
  TimeDefs: TYPE USING [CurrentDayTime];

ModuleMaker: PROGRAM
    IMPORTS
      MiscDefs, PGScondefs, SegmentDefs, StreamDefs,
      StringDefs, SystemDefs, TableCommand, TimeDefs
    EXPORTS PGScondefs, TableCommand =
  BEGIN  OPEN SegmentDefs;

 -- BCD construction

  bcdHeader: BcdDefs.BCD;
  module: BcdDefs.MTRecord;
  export: POINTER TO BcdDefs.EXPRecord;
  defsFile: BcdDefs.FTRecord;
  codeSeg, symbolSeg: BcdDefs.SGRecord;
  ssbString: STRING ← [60];
  ssb: POINTER TO BcdDefs.PackedString ← LOOPHOLE[ssbString];

  pgsVersion: PUBLIC BcdDefs.VersionStamp ← TableCommand.MyBcdVersion[];
  sourceVersion: PUBLIC BcdDefs.VersionStamp ← [0, 0, 0];
  objectVersion: PUBLIC BcdDefs.VersionStamp ← [
	time: LOOPHOLE[TimeDefs.CurrentDayTime[]],
	net: MiscDefs.GetNetworkNumber[],
	host: OsStaticDefs.OsStatics.SerialNumber];

  moduleIndex, segIndex: StreamDefs.StreamIndex;	-- for fixup
  moduleId: STRING ← [40];
  out: StreamDefs.StreamHandle;

  InitializePackedString: PROC = {
    ssb.string.length ← 1;  ssb.size[1] ← 0;  moduleId.length ← 0};

  AddName: PROC [n: STRING] RETURNS [name: BcdDefs.NameRecord] = {
    StringDefs.AppendChar[@ssb.string, LOOPHOLE[n.length]];
    name ← BcdDefs.NameRecord[ssb.string.length];
    StringDefs.AppendString[@ssb.string, n];
    RETURN};

  FillInModule: PROC [
      name: BcdDefs.NameRecord, segmentSize: CARDINAL, altoCode: BOOLEAN] = {
    OPEN BcdDefs;
    module ← MTRecord[
      name: name, namedInstance: FALSE, initial: FALSE,
      file: FTSelf, links: frame, config: CTNull, code: [sgi: FIRST[SGIndex],
      linkspace: FALSE, packed: FALSE, offset: 0, length: 2*segmentSize],
      sseg: FIRST[SGIndex]+SIZE[SGRecord], framesize: 4,
      altoCode: altoCode, long: FALSE, tableCompiled: TRUE,
      packageable: TRUE,
      residentFrame: FALSE, crossJumped: FALSE, boundsChecks: FALSE, nilChecks: FALSE,
      acMap: SGNull,
      gfi: 1, variables: EVNull, ngfi: 1, frame: [length: 0, frag: ]];
    codeSeg ← SGRecord[class: code, file: FTSelf, base: 2,
      pages: SystemDefs.PagesForWords[segmentSize], extraPages: 0];
    symbolSeg ← SGRecord[class: symbols, file: FTNull, base: 0,
      pages: 0, extraPages: 0]};

  FillInExport: PROC [name: BcdDefs.NameRecord, size, entry: CARDINAL] = {
    export ← SystemDefs.AllocateHeapNode[SIZE[BcdDefs.EXPRecord]+size];
    MiscDefs.Zero[export, SIZE[BcdDefs.EXPRecord]+size];
    export↑ ← BcdDefs.EXPRecord[name: name, size: size, port: interface,
      namedInstance: FALSE, typeExported: FALSE, file: FIRST[BcdDefs.FTIndex], links:];
    export.links[entry] ← BcdDefs.Link[variable[vgfi:1, var:0, vtag:var]]};

  FillInHeader: PROC = {
    OPEN bcdHeader;
    net: CARDINAL = MiscDefs.GetNetworkNumber[];
    MiscDefs.Zero[@bcdHeader, SIZE[BcdDefs.BCD]];	-- clear all fields
    versionIdent ← BcdDefs.VersionID;
    version ← objectVersion;
    sourceVersion ← sourceVersion;
    creator ← pgsVersion;
    nPages ← 1;
    nConfigs ← 0;  nModules ← 1;
    nImports ← 0;  nExports ← IF export = NIL THEN 0 ELSE 1;
    definitions ← repackaged ← typeExported ← FALSE;  tableCompiled ← TRUE;
    firstdummy ← 2;  nDummies ← 0;
    ssOffset ← ctOffset ← impOffset ← ntOffset ← SIZE[BcdDefs.BCD];
    ssLimit ← StringDefs.WordsForString[ssb.string.length];
    mtOffset ← ssOffset + LOOPHOLE[ssLimit, CARDINAL];
    mtLimit ← LOOPHOLE[SIZE[BcdDefs.MTRecord]];
    sgOffset ← mtOffset + LOOPHOLE[mtLimit, CARDINAL];
    sgLimit ← LOOPHOLE[2*SIZE[BcdDefs.SGRecord]];
    IF export # NIL
      THEN {
	ftOffset ← sgOffset + LOOPHOLE[sgLimit, CARDINAL];
	ftLimit ← LOOPHOLE[SIZE[BcdDefs.FTRecord]];
	expOffset ← ftOffset + LOOPHOLE[ftLimit, CARDINAL];
	expLimit ← LOOPHOLE[SIZE[BcdDefs.EXPRecord]+export.size]};
    source ← BcdDefs.NullName};

  WriteBcd: PROC [out: StreamDefs.StreamHandle] = {
    OPEN StreamDefs;
    [] ← WriteBlock[out, @bcdHeader, SIZE[BcdDefs.BCD]];
    [] ← WriteBlock[out, ssb, StringDefs.WordsForString[ssb.string.length]];
    moduleIndex ← StreamDefs.GetIndex[out];
    [] ← WriteBlock[out, @module, SIZE[BcdDefs.MTRecord]];
    segIndex ← StreamDefs.GetIndex[out];
    [] ← WriteBlock[out, @codeSeg, SIZE[BcdDefs.SGRecord]];
    [] ← WriteBlock[out, @symbolSeg, SIZE[BcdDefs.SGRecord]];
    IF export # NIL
      THEN {
	[] ← WriteBlock[out, @defsFile, SIZE[BcdDefs.FTRecord]];
	[] ← WriteBlock[out, export, SIZE[BcdDefs.EXPRecord]+export.size];
	SystemDefs.FreeHeapNode[export]}};

  CreateBCDStream: PUBLIC PROC [
	in: StreamDefs.StreamHandle,
	modId, interfaceId: STRING,
	count: CARDINAL,
	altoCode: BOOLEAN ← TRUE]
      RETURNS [output: StreamDefs.StreamHandle] = {
    modRoot: STRING ← [40];
    interfaceRoot: STRING ← [40];
    symbolSeg: SegmentDefs.FileSegmentHandle;
    SetSourceVersion[in];
    FOR i: CARDINAL IN [0..modId.length)
      DO
      IF modId[i] = '. THEN EXIT;
      StringDefs.AppendChar[modRoot, modId[i]];
      ENDLOOP;
    out ← output ←
      StreamDefs.NewWordStream[modId, SegmentDefs.Write+SegmentDefs.Append];
    InitializePackedString[];
    StringDefs.AppendString[moduleId, modRoot];
    FillInModule[AddName[modRoot], count, altoCode];
    FOR i: CARDINAL IN [0..interfaceId.length)
      DO
      IF interfaceId[i] = '. THEN EXIT;
      StringDefs.AppendChar[interfaceRoot, interfaceId[i]];
      ENDLOOP;
    -- fill in interface info
    IF StringDefs.EquivalentString[interfaceRoot, "SELF"L]
      THEN export ← NIL
      ELSE {
	dName: BcdDefs.NameRecord ← AddName[interfaceRoot];
	size, entry: CARDINAL;
	[defsFile.version, symbolSeg] ← TableCommand.FindInterface[interfaceRoot];
	defsFile.name ← dName;
	[size, entry] ← TableCommand.FindItem[symbolSeg, modRoot];
	FillInExport[dName, size, entry]};
    FillInHeader[]; -- Do this after all strings entered
    WriteBcd[out];
    StreamDefs.SetIndex[out, [1, 0]];
    RETURN};

  -- input management

  CheckForBr: PROC [name: STRING] RETURNS [BOOLEAN] = {
    FOR i: CARDINAL IN [0..name.length) DO
      IF name[i] = '. THEN GOTO extension;
      REPEAT
	extension =>
	  IF name.length = i+3 AND
            (name[i+1] = 'b OR name[i+1] = 'B) AND
            (name[i+2] = 'r OR name[i+2] = 'R) THEN RETURN[TRUE];
      ENDLOOP;
    RETURN[FALSE]};

  FindSegmentSize: PROC [br: BOOLEAN, file: SegmentDefs.FileHandle]
      RETURNS [count: CARDINAL, in: StreamDefs.StreamHandle] = {
    OPEN StreamDefs;
    in ← CreateWordStream[file, Read];
    IF br
      THEN {
	SetIndex[in, [0, 7*AltoDefs.BytesPerWord]];  count ← in.get[in];
	SetIndex[in, [0, count*AltoDefs.BytesPerWord]];  count ← in.get[in]}
      ELSE {
	eof: StreamIndex = StreamDefs.FileLength[in];
	count ← (eof.page*AltoDefs.BytesPerPage + eof.byte)/AltoDefs.BytesPerWord;
	in.reset[in]};
    RETURN};

  CreateSegmentStream: PROC [segmentId: STRING]
      RETURNS [in: StreamDefs.StreamHandle, count: CARDINAL] = {
    OPEN SegmentDefs;
    brFile: BOOLEAN = CheckForBr[segmentId];
    file: FileHandle = NewFile[segmentId, Read, OldFileOnly];
    [count, in] ← FindSegmentSize[brFile, file];
    RETURN};

  FixupBcdHeader: PUBLIC PROC = {
    OPEN AltoDefs;
    endIndex: StreamDefs.StreamIndex ← StreamDefs.GetIndex[out];
    nBytes: CARDINAL = (endIndex.page-1)*BytesPerPage + endIndex.byte;
    IF export # NIL THEN RETURN;
    module.code.length ← nBytes;
    codeSeg.pages ← SystemDefs.PagesForWords[(nBytes + (BytesPerWord-1))/BytesPerWord];
    IF bcdHeader.nExports = 0
      THEN {
	startIndex: StreamDefs.StreamIndex;
	symbolBytes: CARDINAL;
	UNTIL (startIndex ← StreamDefs.GetIndex[out]).byte = 0 DO out.put[out, 0] ENDLOOP;
	symbolSeg ← [
            class: symbols, file: BcdDefs.FTSelf,
            base: codeSeg.base+codeSeg.pages, pages: , extraPages: 0];
	PGScondefs.WriteSymbols[out, moduleId];
	endIndex ← StreamDefs.GetIndex[out];
	symbolBytes ← (endIndex.page-startIndex.page)*BytesPerPage + endIndex.byte;
	symbolSeg.pages ←
	  SystemDefs.PagesForWords[(symbolBytes + (BytesPerWord-1))/BytesPerWord]};
    StreamDefs.SetIndex[out, moduleIndex];
    [] ← StreamDefs.WriteBlock[out, @module, SIZE[BcdDefs.MTRecord]];
    StreamDefs.SetIndex[out, segIndex];
    [] ← StreamDefs.WriteBlock[out, @codeSeg, SIZE[BcdDefs.SGRecord]];
    [] ← StreamDefs.WriteBlock[out, @symbolSeg, SIZE[BcdDefs.SGRecord]];
    StreamDefs.SetIndex[out, endIndex]};

  SetSourceVersion: PROC [sh: StreamDefs.StreamHandle] = {
    sourceVersion ← [0, 0, TableCommand.CreateTime[sh]]};

 -- overall control

  WriteSegment: PROC [in, out: StreamDefs.StreamHandle, count: CARDINAL] = {
    p: POINTER ← SystemDefs.AllocateSegment[count];
    [] ← StreamDefs.ReadBlock[in, p, count];
    IF StreamDefs.WriteBlock[out, p, count] # count THEN ERROR;
    SystemDefs.FreeSegment[p]};

  MakeModule: PUBLIC PROC [
      inputFile, moduleId, interfaceId: STRING, altocode: BOOLEAN] = {
    in, output: StreamDefs.StreamHandle;
    count: CARDINAL;
    [in, count] ← CreateSegmentStream[inputFile];
    output ← CreateBCDStream[in, moduleId, interfaceId, count, altocode];
    WriteSegment[in, output, count];
    FixupBcdHeader[];
    in.destroy[in];
    output.destroy[output]};

  END.