-- MMDebug.mesa; edited by Sandman June 19, 1979  9:43 AM
-- Edited by Forrest July 15, 1980  2:21 PM
-- Edited by Gobbel November 3, 1980  11:43 PM

DIRECTORY
  BcplOps USING [BcplJSR],
  ControlDefs USING [FrameHandle, GlobalFrameHandle, StateVector, SVPointer],
  DiskDefs USING [DS],
  FrameDefs USING [UnNew],
  FrameOps USING [GetReturnFrame, GetReturnLink, MyLocalFrame],
  ImageDefs USING [
    AbortMesa, CleanupItem, CleanupMask, CleanupProcedure, PuntMesa,
    StopMesa],
  InlineDefs USING [BITAND],
  MMInit USING [MakeBootFile, MMMakeBoot, MMStart],
  MMOps USING [BootNetExec, etherBooted, FakeModulesCode, MMEtherBoot],
  Mopcodes USING [zSTARTIO],
  OsStaticDefs USING [OsStatics],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  SDDefs USING [
    sCallDebugger, SD, sInterrupt, sProcessBreakpoint, sUncaughtSignal],
  SystemDefs USING [FreePages];

MMDebug: PROGRAM [user: PROGRAM]
IMPORTS BcplOps, FrameDefs, FrameOps, ImageDefs, InlineDefs, MMInit, MMOps,
  ProcessDefs, SystemDefs
EXPORTS ImageDefs, MMInit, MMOps SHARES DiskDefs =
BEGIN OPEN ControlDefs;

debugState: SVPointer;
reason: {uncaughtsignal, interrupt, breakpoint, explicitcall} ← explicitcall;
etherBooted: PUBLIC BOOLEAN ← FALSE;

ProcessBreakpoint: PROCEDURE [s: SVPointer] =
  BEGIN
  reason ← breakpoint;
  debugState ← s;
  Swap[];
  RETURN
  END;

Interrupt: PROCEDURE =
  BEGIN -- called by BRK trap handler in resident code
  state: ControlDefs.StateVector;
  state ← STATE;
  state.dest ← FrameOps.MyLocalFrame[];
  reason ← interrupt;
  debugState ← @state;
  Swap[];
  END;

Catcher: PROCEDURE [msg, signal: UNSPECIFIED, frame: FrameHandle] =
  BEGIN
  OPEN ControlDefs;
  state: StateVector;
  state.stk[0] ← msg;
  state.stk[1] ← signal;
  state.stkptr ← 0;
  state.dest ← FrameOps.GetReturnFrame[];
  reason ← uncaughtsignal;
  debugState ← @state;
  Swap[];
  RETURN
  END;

CallDebugger: PROCEDURE [s: STRING] =
  BEGIN -- user's entry point to debugger
  state: ControlDefs.StateVector;
  state ← STATE;
  state.stk[0] ← s;
  state.stkptr ← 1;
  state.dest ← FrameOps.GetReturnLink[];
  reason ← explicitcall;
  debugState ← @state;
  Swap[];
  RETURN
  END;

SetSD: PROCEDURE =
  BEGIN OPEN SDDefs;
  sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
  sd[sProcessBreakpoint] ← ProcessBreakpoint;
  sd[sUncaughtSignal] ← Catcher;
  sd[sInterrupt] ← Interrupt;
  sd[sCallDebugger] ← CallDebugger;
  END;

Swap: PROCEDURE =
  BEGIN
  break: RECORD[a,b: WORD] ← [77400B, 1400B];
  DiskStatus: POINTER TO DiskDefs.DS = LOOPHOLE[522B];
  IF DiskStatus.notReady # 0 THEN NetDebug[]
  ELSE [] ← BcplOps.BcplJSR[JSR, @break, 0];
  RETURN
  END;

TypeStore: UNSPECIFIED = 200B;
TypeFetch: UNSPECIFIED = 201B;
TypeProceed: UNSPECIFIED = 202B;
TypeProceedReply: UNSPECIFIED = 203B;
TypeAck: UNSPECIFIED = 204B;

TeleDebugSocket: CARDINAL = 60B;

-- these types for clarity
Byte: TYPE = [0..255];
ByteCount, WordCount: TYPE = CARDINAL;

MachineAddress: TYPE = MACHINE DEPENDENT RECORD [
  net, host: Byte];

Pair: TYPE = MACHINE DEPENDENT RECORD [a,b: CARDINAL];

Port: TYPE = MACHINE DEPENDENT RECORD [
  machine: MachineAddress, socket: Pair];

PacketTypePup: CARDINAL = 1000B;

SocDebugger: Pair = [0,TeleDebugSocket];

DPup:TYPE= MACHINE DEPENDENT RECORD [
  -- the Ethernet encapsulation
  eDest, eSource: Byte,
  eWord2: CARDINAL,
  -- Pup starts here
  pupLength: ByteCount,
  transportControl, pupType: Byte,
  pupID: Pair,
  destPort: Port,
  sourcePort: Port,
  data: Data,
  xSum: CARDINAL];

PacketSize: WordCount = SIZE[DPup];
PupSize: ByteCount = (PacketSize-2)*2;

Data: TYPE = MACHINE DEPENDENT RECORD [
  SELECT OVERLAID * FROM
    short => [
      sAddress: POINTER,
      sValue: UNSPECIFIED,
      lenChunk: CARDINAL],
    long => [
      lAddress: LONG POINTER,
      lValue: UNSPECIFIED],
    ENDCASE];

NoChecksum: CARDINAL = 177777B;

EthernetDeviceBlock: TYPE = MACHINE DEPENDENT RECORD [
  EPLocMicrocodeStatus, EPLocHardwareStatus: Byte,
  EBLocInterruptBit: WORD,
  EELocInputFinishCount: INTEGER,
  ELLocCollisionMagic: WORD,
  EILocInputCount: INTEGER,
  EILocInputPointer: POINTER,
  EOLocOutputCount: INTEGER,
  EOLocOutputPointer: POINTER];

EtherDevice: POINTER TO EthernetDeviceBlock = LOOPHOLE[600B];
  
EtherCommand: --MACHINE DEPENDENT-- TYPE = {null, output, input, reset};

Ether: PROCEDURE [EtherCommand] =
  MACHINE CODE BEGIN Mopcodes.zSTARTIO END;

rtClock: POINTER TO INTEGER = LOOPHOLE[430B];
TicksPerSec: CARDINAL = 26;
TenSec: CARDINAL = 10 * TicksPerSec;

delayCount: CARDINAL ← 200;

TeleSwatCursor: ARRAY [0..16) OF WORD = [
  0, 73507B, 22104B, 23507B, 22104B, 23567B, 0, 0,
  65227B, 105252B, 45252B, 25272B, 142452B, 0, 0, 0];

NetDebug: PROCEDURE =
  BEGIN
  myHost: Byte ← OsStaticDefs.OsStatics.SerialNumber;
  d0: BOOLEAN = OsStaticDefs.OsStatics.AltoVersion.engineeringnumber = 4;
  debugger: Port;
  device: POINTER TO EthernetDeviceBlock ← LOOPHOLE[600B];
  cursor: POINTER TO ARRAY [0..16) OF WORD ← LOOPHOLE[431B];
  saveCursor: ARRAY [0..16) OF WORD ← cursor↑;
  xpup: DPup;
  dpup: POINTER TO DPup = @xpup;
  proceedTime: INTEGER ← 0;
  cursor↑ ← TeleSwatCursor;
  device.EBLocInterruptBit ← 0;
  Ether[reset];
  device.EILocInputPointer ← device.EOLocOutputPointer ← dpup;
  device.EILocInputCount ← device.EOLocOutputCount ← PacketSize;
  UNTIL proceedTime # 0 AND proceedTime-rtClock↑ <= 0 DO
    Ether[reset];
    device.EPLocMicrocodeStatus ← 0;
    device.EPLocHardwareStatus ← 0;
    device.ELLocCollisionMagic ← 0;
    Ether[input];
    DO
      IF device.EPLocHardwareStatus#0 THEN
	BEGIN
	IF device.EPLocMicrocodeStatus = 0
	  AND dpup.eWord2 = PacketTypePup
	  AND dpup.destPort.socket = SocDebugger THEN
	    BEGIN
	    IF dpup.pupType # TypeProceedReply THEN proceedTime ← 0;
	    SELECT dpup.pupType FROM
	      TypeFetch =>
		BEGIN
		dpup.data.sValue ← dpup.data.sAddress↑;
		dpup.data.lenChunk ← 0;
		END;
	      TypeStore =>
		BEGIN
		dpup.data.sAddress↑ ← dpup.data.sValue;
		dpup.data.lenChunk ← 0;
		END;
	      TypeProceed => proceedTime ← rtClock↑ + TenSec;
	      TypeProceedReply =>
		IF proceedTime # 0 THEN
		  BEGIN cursor↑ ← saveCursor; RETURN END;
	      ENDCASE => EXIT;
	    END
	ELSE EXIT;
	dpup.eDest ← dpup.eSource; dpup.eSource ← myHost;
	debugger ← dpup.sourcePort;
	dpup.sourcePort ← dpup.destPort;
	dpup.destPort ← debugger;
	dpup.pupType ← TypeAck;
	dpup.pupLength ← PupSize;
	dpup.transportControl ← 0;
	dpup.xSum ← NoChecksum;
	device.EPLocMicrocodeStatus ← 0;
	device.EPLocHardwareStatus ← 0;
	device.ELLocCollisionMagic ← 0;
	-- delay before sending ack
	THROUGH [0..delayCount) DO NULL ENDLOOP;
	Ether[output];
	EXIT;
	END;
      ENDLOOP;
    UNTIL device.EPLocHardwareStatus#0 DO NULL ENDLOOP;
    ENDLOOP;
  cursor↑ ← saveCursor;
  RETURN
  END;

BootViaNet: PUBLIC PROCEDURE [bootIndex: CARDINAL, host: WORD ← 0] =
  BEGIN
  StartLoader: ARRAY [0..8] OF UNSPECIFIED ← [
     20406B,  --  lda 0 lvBootLoaderPacket	; Get pointer to loader
    101400B,  --  inc 0 0			; Compute page0image-1
     24405B,  --  lda 1 k400			; Last destination address
     34405B,  --  lda 3 lblt			; Negative of number of words
     61005B,  --  blt				; Move loader into page 0
         6B,  --  jmp boot0-page0image+page0origin	; Dive into loader
         0B,  --  lvBootLoaderPacket: bootLoaderPacket
       400B,  --  k400:	400
    177402B]; --  lblt:	-401+page0origin
  BootFileNumberIndex: CARDINAL = 74B;
  HostIndex: CARDINAL = 67B;
  EtherCodeStartIndex: CARDINAL = 31B;
  etherBootCode: POINTER TO ARRAY [0..0) OF WORD ←
    MMOps.FakeModulesCode[MMOps.MMEtherBoot];
  ProcessDefs.DisableInterrupts[];
  etherBootCode[BootFileNumberIndex] ← bootIndex;
  etherBootCode[HostIndex] ← host*400B;
  StartLoader[6B] ← @etherBootCode[EtherCodeStartIndex];
  [] ← BcplOps.BcplJSR[JSR,@StartLoader[0B],NIL];  -- bye bye
  END;

MakeBoot: PUBLIC PROCEDURE =
  BEGIN
  MMInit.MakeBootFile[];
  FrameDefs.UnNew[LOOPHOLE[MMInit.MMMakeBoot]];
  RETURN
  END;

UserCleanupList: POINTER TO ImageDefs.CleanupItem ← NIL;

AddCleanupProcedure: PUBLIC PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] =
  BEGIN
  ProcessDefs.DisableInterrupts[];
  RemoveCleanupProcedure[item];
  item.link ← UserCleanupList;
  UserCleanupList ← item;
  ProcessDefs.EnableInterrupts[];
  END;

RemoveCleanupProcedure: PUBLIC PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] =
  BEGIN
  prev, this: POINTER TO ImageDefs.CleanupItem;
  IF UserCleanupList = NIL THEN RETURN;
  ProcessDefs.DisableInterrupts[];
  prev ← this ← UserCleanupList;
  IF this = item THEN UserCleanupList ← this.link
  ELSE UNTIL (this ← this.link) = NIL DO
    IF this = item THEN BEGIN prev.link ← this.link; EXIT END;
    prev ← this;
    ENDLOOP;
  ProcessDefs.EnableInterrupts[];
  END;

UserCleanupProc: PUBLIC ImageDefs.CleanupProcedure =
  BEGIN OPEN ImageDefs, MMOps; -- all interrupts off if why = finish or abort
  this, next: POINTER TO ImageDefs.CleanupItem;
  this ← UserCleanupList;
  UserCleanupList ← NIL;
  WHILE this # NIL DO
    next ← this.link;
    IF InlineDefs.BITAND[ImageDefs.CleanupMask[why], this.mask] # 0 THEN
      this.proc[why ! ANY => IF why = Abort OR why = Finish THEN CONTINUE];
    AddCleanupProcedure[this];
    this ← next;
    ENDLOOP;
  SELECT why FROM
    Finish => IF ~etherBooted THEN StopMesa[] ELSE BootNetExec[];
    Abort => IF ~etherBooted THEN AbortMesa[] ELSE BootNetExec[];
    ENDCASE;
  END;

-- Main body

STOP;

BEGIN
  ENABLE ANY => ImageDefs.PuntMesa;
SetSD[];
SystemDefs.FreePages[LOOPHOLE[MMInit.MMStart, GlobalFrameHandle].code.handle];
FrameDefs.UnNew[LOOPHOLE[MMInit.MMStart]];
END;

START user;

ImageDefs.StopMesa[];

END...