-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- FixBootFileChecksum.mesa, HGM,  8-Jan-83 12:57:34

DIRECTORY
  Checksum USING [ComputeChecksum],
  Environment USING [wordsPerPage],
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem, Display, newLine,
    NumberItem, ProcType, StringItem],
  Heap USING [systemZone],
  Inline USING [BITNOT, LowHalf],
  MFile USING [Error, GetLength, Handle, ReadWrite],
  MSegment USING [Address, Create, Delete, Handle],
  Runtime USING [GetBcdTime],
  String USING [AppendString],
  Time USING [Append, Unpack],
  Tool USING [Create, MakeSWsProc, MakeFormSW],
  ToolWindow USING [TransitionProcType],
  UserTerminal USING [BlinkDisplay],
  Window USING [Handle];

FixBootFileChecksum: PROGRAM
  IMPORTS
    Checksum, FormSW, Heap, Inline, MFile, MSegment, Runtime, String, Time,
    Tool, UserTerminal =
  BEGIN

  z: UNCOUNTED ZONE = Heap.systemZone;

  form: Window.Handle;
  fileName: LONG STRING ← NIL;
  oldCS, oldValue, newCS, newValue: WORD ← 0;

  Init: PROCEDURE =
    BEGIN
    herald: STRING = [100];
    String.AppendString[herald, "FixBootFileChecksum of  "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  Smash: FormSW.ProcType =
    BEGIN
    file: MFile.Handle ← NIL;
    seg: MSegment.Handle;
    words: LONG CARDINAL;
    buffer: LONG POINTER;
    file ← MFile.ReadWrite[fileName, [], null ! MFile.Error => CONTINUE];
    IF file = NIL THEN BEGIN UserTerminal.BlinkDisplay[]; RETURN; END;
    words ← (MFile.GetLength[file] + 1)/2;
    seg ← MSegment.Create[file, [], 0];
    buffer ← MSegment.Address[seg];
    BEGIN
    loc: LONG POINTER ← buffer + words - 1;
    IF words > 100*Environment.wordsPerPage THEN
      BEGIN
      hunk: CARDINAL ← 20*Environment.wordsPerPage;
      finger: LONG POINTER ← buffer;
      left: LONG CARDINAL ← words;
      oldCS ← 0;
      UNTIL left = 0 DO
        IF left < hunk THEN hunk ← Inline.LowHalf[left];
        oldCS ← Checksum.ComputeChecksum[oldCS, hunk, finger];
        left ← left - hunk;
        finger ← finger + hunk;
        ENDLOOP;
      END
    ELSE oldCS ← Checksum.ComputeChecksum[0, Inline.LowHalf[words], buffer];
    oldValue ← loc↑;
    newValue ← NewValueToMakeChecksumZero[oldCS, oldValue, words - 1, words];
    loc↑ ← newValue;
    IF words > 100*Environment.wordsPerPage THEN
      BEGIN
      hunk: CARDINAL ← 20*Environment.wordsPerPage;
      finger: LONG POINTER ← buffer;
      left: LONG CARDINAL ← words;
      newCS ← 0;
      UNTIL left = 0 DO
        IF left < hunk THEN hunk ← Inline.LowHalf[left];
        newCS ← Checksum.ComputeChecksum[newCS, hunk, finger];
        left ← left - hunk;
        finger ← finger + hunk;
        ENDLOOP;
      END
    ELSE newCS ← Checksum.ComputeChecksum[0, Inline.LowHalf[words], buffer];
    END;
    MSegment.Delete[seg];
    FormSW.Display[form];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 6;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "Smash"L, proc: Smash, place: FormSW.newLine];
    items[1] ← FormSW.StringItem[tag: "Filename"L, string: @fileName, inHeap: TRUE];
    items[2] ← FormSW.NumberItem[
      tag: "OldCS"L, value: @oldCS, radix: octal, place: FormSW.newLine];
    items[3] ← FormSW.NumberItem[
      tag: "OldValue"L, value: @oldValue, radix: octal];
    items[4] ← FormSW.NumberItem[
      tag: "NewCS"L, value: @newCS, radix: octal, place: FormSW.newLine];
    items[5] ← FormSW.NumberItem[
      tag: "NewValue"L, value: @newValue, radix: octal];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive => fileName ← z.NEW[StringBody[10]];
      new = inactive => z.FREE[@fileName];
      ENDCASE;
    END;

  NewValueToMakeChecksumZero: PROCEDURE [
    oldChecksum: WORD, oldValue: WORD, offsetOfOldValue: LONG CARDINAL,  -- words
    length: LONG CARDINAL] RETURNS [newValue: WORD] =
    BEGIN
    newValue ← OnesAdd[
      LeftCycle[OnesSub[0, oldChecksum], offsetOfOldValue - length], oldValue]
    END;

  OnesAdd: PROCEDURE [a, b: CARDINAL] RETURNS [c: CARDINAL] = INLINE
    BEGIN c ← a + b; IF c < a THEN c ← c + 1; IF c = 177777B THEN c ← 0; END;

  OnesSub: PROCEDURE [a, b: CARDINAL] RETURNS [CARDINAL] = INLINE
    BEGIN RETURN[OnesAdd[a, Inline.BITNOT[b]]]; END;

  LeftCycle: PROCEDURE [a: WORD, b: LONG INTEGER] RETURNS [c: CARDINAL] = INLINE
    BEGIN
    n: LONG INTEGER ← b MOD 16;
    c ← a;
    IF b < 0 THEN n ← n + 16;
    UNTIL n = 0 DO
      IF c < 100000B THEN c ← c*2 ELSE c ← c*2 + 1; n ← n - 1; ENDLOOP;
    END;

  Init[];
  END.