-- FormatTridentImpl.mesa
-- Last Edited by: Taft, May 22, 1983 2:45 pm

DIRECTORY
  DeviceTypes USING [sa4000],
  DiskChannel USING [
    Address, Create, Delete, Drive, DiskPageNumber, GetDriveAttributes,
    GetPageAddress, GetPageNumber, Handle, PVHandle],
  Environment USING [Base, first64K, wordsPerPage],
  FormatTrident,
  Inline USING [BITNOT, LowHalf, LongCOPY],
  MicrocodeFile,
  PhysicalVolume USING [], -- exports Handle
  PilotDisk USING [Label, NextLabel],
  Process USING [Yield],
  RandomCard USING [Init, Next],
  ResidentHeap USING [MakeNode],
  SA4000Face USING [
    Command, DeviceHandle, DiskAddress, Initiate, Operation, OperationPtr,
    operationSize, Poll, Recalibrate, Status],
  SA4000FaceExtras USING [www],
  Space USING [
    Create, defaultWindow, Handle, LongPointer, Map, Pointer, Unmap, mds],
  SpecialSystem USING [nullProcessorID],
  String USING [AppendString],
  System USING [GreenwichMeanTime],
  SystemInternal USING [UniversalID],
  Zone USING [Status];

FormatTridentImpl: PROGRAM -- should be a monitor since globals shared
  IMPORTS DiskChannel, Inline, MicrocodeFile, PilotDisk, Process, RandomCard, ResidentHeap,
    SA4000Face, Space, String
  EXPORTS FormatTrident, PhysicalVolume
  SHARES DiskChannel =
  
  BEGIN OPEN Inline;
  
  Handle: PUBLIC TYPE = DiskChannel.PVHandle;
  
  DiskPageNumber: TYPE = FormatTrident.DiskPageNumber;
  RetryLimit: TYPE = [0..10);
  retryLimit: RetryLimit = LAST[RetryLimit];
  
  space: Space.Handle = Space.Create[
    size: 1, parent: Space.mds];
  op: SA4000Face.OperationPtr = MakeOperation[];
  
  BadPage: PUBLIC SIGNAL [p: DiskPageNumber] = CODE;
  PassStarting: PUBLIC SIGNAL [pass: CARDINAL, operation: FormatTrident.Operation] = CODE;
  MicrocodeInstallFailure: PUBLIC SIGNAL [m: FormatTrident.FailureType] = CODE;
  NotTrident: PUBLIC ERROR = CODE;
  
  Format: PUBLIC PROC [h: Handle, firstPage: DiskPageNumber, count: LONG CARDINAL,
    passes: CARDINAL] =
    BEGIN
    ckData: LONG POINTER = Space.LongPointer[space];
    cnl: DiskChannel.Handle;
    label: PilotDisk.Label;
    SetLabel: PROC [value: CARDINAL] =
      BEGIN
      SetBlock[@label, SIZE[PilotDisk.Label], value];
      label.pad1 ← 0;
      IF label.filePageLo#0 OR label.filePageHi#0 THEN
        label.immutable ← label.temporary ← label.zeroSize ← FALSE;
      END;
    AssertTrident[h, firstPage, count];
    Space.Map[space, Space.defaultWindow];
    cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
    BEGIN
    ENABLE UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]};
    [] ← RandomCard.Init[-1];
    FOR pass: CARDINAL IN [0..passes) DO
      labelValue: CARDINAL ← (IF pass MOD 2 = 0 THEN RandomCard.Next[] ELSE BITNOT[labelValue]);
      SIGNAL PassStarting[pass+1, write];
      SetLabel[labelValue];
      FOR i: CARDINAL IN [0..Environment.wordsPerPage) DO
        (ckData+i)↑ ← (IF pass MOD 2 = 0 THEN RandomCard.Next[] ELSE BITNOT[(ckData+i)↑]);
        ENDLOOP;
      Transfer[h, cnl, firstPage, count,
        (IF pass=0 THEN SA4000FaceExtras.www ELSE vww), @label, ckData];
      SIGNAL PassStarting[pass+1, read];
      SetLabel[labelValue];
      Transfer[h, cnl, firstPage, count, vvv, @label, ckData, 1];
      ENDLOOP;
    END;
    Space.Unmap[space];
    DiskChannel.Delete[cnl];
    END;
  
  initialMicrocodeLabel: PilotDisk.Label = [
    fileID: LOOPHOLE[SystemInternal.UniversalID[
      processor: SpecialSystem.nullProcessorID, sequence: FormatTrident.hardUCodeSerial]],
    filePageLo: 0, filePageHi: 0, immutable: FALSE, temporary: FALSE, zeroSize: FALSE,
    type: [0], bootChainLink: LOOPHOLE[LONG[0]]];

  IdentifyInitialMicrocode: PUBLIC PROCEDURE [h: Handle, s: STRING]
    RETURNS [microcodeInstalled: BOOLEAN ← FALSE, time: System.GreenwichMeanTime] =
    BEGIN
    label: PilotDisk.Label ← initialMicrocodeLabel;
    cnl: DiskChannel.Handle;
    header: POINTER TO MicrocodeFile.Header;
    AssertTrident[h];
    cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
    Space.Map[space, Space.defaultWindow];
      BEGIN
      Transfer[h: h, channel: cnl, firstPage: FormatTrident.firstHardUCodePage, count: 1,
        cmd: vvr, label: @label, data: Space.LongPointer[space] !
        BadPage => GOTO error];
      header ← Space.Pointer[space];
      IF header.name.length IN [1..header.name.maxlength] AND
        header.name.maxlength<=MicrocodeFile.maxNameLength AND
	header.name.length<=s.maxlength THEN
	BEGIN
	time ← MicrocodeFile.GMTFromBCPLTime[header.createDate];
	s.length ← 0;
	String.AppendString[s, @header.name];
	microcodeInstalled ← TRUE;
	END;
      EXITS
        error => NULL;
      END;
    Space.Unmap[space];
    DiskChannel.Delete[cnl];
    END;
  
  InstallBootMicrocode: PUBLIC PROC [
    h: Handle, getPage: PROC RETURNS [LONG POINTER]] =
    BEGIN
    cnl: DiskChannel.Handle;
    AssertTrident[h];
    cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
    Space.Map[space, Space.defaultWindow];
    InstallBootMicrocode1[
      h, cnl, Space.LongPointer[space], getPage
      ! UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]}];
    Space.Unmap[space];
    DiskChannel.Delete[cnl];
    END;
    
  Scan: PUBLIC PROC [h: Handle, firstPage: DiskPageNumber, count: LONG CARDINAL,
    passes: CARDINAL] =
    BEGIN
    label: PilotDisk.Label;
    cnl: DiskChannel.Handle;
    AssertTrident[h];
    cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
    Space.Map[space, Space.defaultWindow];
    FOR pass: CARDINAL IN [0..passes) DO
      SIGNAL PassStarting[pass+1, read];
      Transfer[
        h, cnl, firstPage, count, vrr, @label, Space.LongPointer[space]
        ! UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]}];
      ENDLOOP;
    Space.Unmap[space];
    DiskChannel.Delete[cnl];
    END;
    
  -- Private Procedures
  AssertTrident: PROC [
    h: Handle, first: DiskPageNumber ← 0, count: LONG CARDINAL ← 0] =
    BEGIN
    SELECT DiskChannel.GetDriveAttributes[h.drive].deviceType FROM
      DeviceTypes.sa4000 => RETURN;  -- kludge for now, really Trident
      ENDCASE => ERROR NotTrident
    END;
    
  CopyPage: PROC [from, to: LONG POINTER] = INLINE
    {LongCOPY[from: from, to: to, nwords: Environment.wordsPerPage]; };
    
  GetDiskAddress: PROC [h: DiskChannel.Handle, p: DiskPageNumber]
    RETURNS [SA4000Face.DiskAddress] =
    BEGIN
    x: DiskChannel.Address = DiskChannel.GetPageAddress[h, p];
    RETURN[[cylinder: x.cylinder, head: x.head, sector: x.sector]];
    END;
    
  GetPageNumber: PROC [d: DiskChannel.Drive, a: SA4000Face.DiskAddress]
    RETURNS [DiskPageNumber] =
    BEGIN
    RETURN[DiskChannel.GetPageNumber[
      d, [cylinder: a.cylinder, head: a.head, sector: a.sector]]]
    END;

  -- h has been asserted to be SA4000
  InstallBootMicrocode1: PROC [
    h: Handle, cnl: DiskChannel.Handle, myPage: LONG POINTER,
    getPage: PROC RETURNS [LONG POINTER]] =
    BEGIN
    bits: LONG POINTER;
    device: SA4000Face.DeviceHandle = DiskChannel.GetDriveAttributes[
      h.drive].deviceHandle;
    thisPage, prevPage: DiskPageNumber ← FormatTrident.firstHardUCodePage;
    label: PilotDisk.Label ← initialMicrocodeLabel;
    GetDA: PROC [p: DiskPageNumber] RETURNS [SA4000Face.DiskAddress] = INLINE
      {RETURN[GetDiskAddress[cnl, p]]};
    IO: PROC [command: SA4000Face.Command, diskAddress: SA4000Face.DiskAddress,
      label: POINTER TO PilotDisk.Label]
      RETURNS [success: BOOLEAN] =
      BEGIN
      rCount: RetryLimit ← 0;
      status: SA4000Face.Status;
      op↑ ← [
	  command: command, device: device, clientHeader: diskAddress,
          pageCount: 1,
	  labelPtr: label, dataPtr: myPage, incrementDataPtr: FALSE];
      SA4000Face.Initiate[op];
      DO
	SELECT status ← SA4000Face.Poll[op] FROM
	  inProgress => {Process.Yield[]; LOOP; };
	  goodCompletion => EXIT;
	  ENDCASE =>
	    BEGIN
	    SELECT rCount FROM
	      >= retryLimit => RETURN[FALSE];
	      = retryLimit/2 => SA4000Face.Recalibrate[device];
	      ENDCASE => NULL;
	    rCount ← rCount + 1;
	    op.pageCount ← 1;
	    SA4000Face.Initiate[op];
	    END
	ENDLOOP;
      label.filePageLo ← label.filePageLo-1;  -- undo +1 done by microcode
      RETURN[IF command = vww THEN IO[vvv, diskAddress, label] ELSE TRUE];
      END;
      
    bits ← getPage[];
    IF bits = NIL THEN GOTO emptyFile;
    CopyPage[from: bits, to: myPage];
    DO
      bits ← getPage[];
      label.bootChainLink ← LOOPHOLE[LONG[IF bits=NIL THEN -1 ELSE 0]];
      DO
	IF thisPage >= FormatTrident.pagesReservedInPartition1 THEN GOTO tooBig;
	IF IO[vww, GetDA[thisPage], @label] THEN EXIT;
	IF thisPage = FormatTrident.firstHardUCodePage THEN GOTO firstPageBad;
	thisPage ← thisPage + 1;
	ENDLOOP;
      IF thisPage # prevPage + 1 AND thisPage # FormatTrident.firstHardUCodePage THEN
	-- have to fix up previous link if we had an error
	BEGIN
	label.filePageLo ← label.filePageLo-1;
	IF ~IO[vvr, GetDA[prevPage], @label] THEN GOTO flakey;
	label.bootChainLink ← LOOPHOLE[GetDA[thisPage]];
	IF ~IO[vww, GetDA[prevPage], @label] THEN GOTO flakey;
	label.filePageLo ← label.filePageLo+1;
	END;
      IF bits = NIL THEN EXIT;
      -- we are done and have written a zero link in the last page
      CopyPage[from: bits, to: myPage];
      prevPage ← thisPage;
      thisPage ← thisPage + 1;
      label.filePageLo ← label.filePageLo+1;
      ENDLOOP;
    EXITS
      emptyFile => SIGNAL MicrocodeInstallFailure[emptyFile];
      firstPageBad => SIGNAL MicrocodeInstallFailure[firstPageBad];
      flakey => SIGNAL MicrocodeInstallFailure[flakeyPageFound];
      tooBig => SIGNAL MicrocodeInstallFailure[microcodeTooBig];
    END;
    
  MakeOperation: PROC RETURNS [SA4000Face.OperationPtr] =
    BEGIN
    rp: Environment.Base RELATIVE POINTER TO SA4000Face.Operation;
    NoWay: ERROR = CODE;
    status: Zone.Status;
    [rp, status] ← ResidentHeap.MakeNode[n: SA4000Face.operationSize, alignment: a16];
    IF status # okay THEN ERROR NoWay;
    RETURN[@Environment.first64K[rp]]
    END;
    
  SetBlock: PROC [blk: LONG POINTER, length, v: CARDINAL] = INLINE
    {blk↑ ← v; LongCOPY[from: blk, to: blk + 1, nwords: length - 1]; };
    
  -- SA4000-ness has been asserted
  
  Transfer: PROC [
    h: Handle, channel: DiskChannel.Handle, firstPage: DiskPageNumber,
    count: LONG CARDINAL, cmd: SA4000Face.Command,
    label: POINTER TO PilotDisk.Label, data: LONG POINTER,
    retries: RetryLimit ← retryLimit] =
    BEGIN
    device: SA4000Face.DeviceHandle = DiskChannel.GetDriveAttributes[
      h.drive].deviceHandle;
    rCount: RetryLimit ← 0;
    status: SA4000Face.Status;
    runStartAddr: SA4000Face.DiskAddress ← GetDiskAddress[channel, firstPage];
    
    Ooops: PROC RETURNS [notDoneYet: BOOLEAN] =
      BEGIN
      rCount ← IF op.clientHeader = runStartAddr THEN rCount + 1 ELSE 0;
      SELECT TRUE FROM
	rCount >= retries =>
	  BEGIN
	  page: DiskPageNumber = GetPageNumber[h.drive, op.clientHeader];
	  SIGNAL BadPage[page];
	  op.clientHeader ← GetDiskAddress[channel, page + 1];
	  op.pageCount ← op.pageCount - 1;
             PilotDisk.NextLabel[label]; -- Fix up the label to the next page
	  rCount ← 0;
	  END;
	rCount = retryLimit/2 OR (status IN [wrongSector..wrongHead] AND rCount = retries/2) =>
	  SA4000Face.Recalibrate[device];
	ENDCASE => NULL;
      runStartAddr ← op.clientHeader;
      IF (notDoneYet ← op.pageCount > 0) THEN SA4000Face.Initiate[op];
      END;

    op↑ ← [
      device: device, clientHeader: runStartAddr, pageCount: NULL,
      labelPtr: label, dataPtr: data, incrementDataPtr: FALSE, command: cmd];

    WHILE count>0 DO
      thisCount: CARDINAL = IF count>LAST[CARDINAL] THEN LAST[CARDINAL] ELSE LowHalf[count];
      op.pageCount ← thisCount;
      SA4000Face.Initiate[op];
      DO
        SELECT status ← SA4000Face.Poll[op] FROM
          inProgress => {Process.Yield[]; LOOP};
          goodCompletion => EXIT;
          ENDCASE => IF Ooops[] THEN LOOP ELSE EXIT;
        ENDLOOP;
      count ← count-thisCount;
      ENDLOOP;
    END;
    
  
  END.


LOG
Time: June 1, 1980  1:23 AM	By: Forrest	Action: Changed to use sanitized interface.  Added limit checking on InstallMicrocode.  Trimmed log.
Time: July 28, 1980  8:22 AM	By: Forrest	Action: Retrofited McJones changes for new SA4000face.  Converted to SA1000and4000.
Time: September 30, 1980  5:31 PM	By: Luniewski	Action: Increment File Page numbers in labels after finding a bad page in Transfer. 
December 12, 1980  10:39 AM  Taft  Create from FormatSA1000andSA4000impl.mesa for Dorado.
March 10, 1981  5:31 PM:  Taft  Make Transfer work for run of >65535 pages.
17-Jun-82 10:06:54  Taft  Re-implement InstallBootMicrocode for Dorado
20-Jun-82 16:18:48  Taft  Add IdentifyInitialMicrocode
May 22, 1983 12:27 pm  Taft  Change Format to do multiple passes with random data.