// PeekEther.bcpl -- Peek stuff that needs to deal with the
//			Ether interface at level 0.
// Copyright Xerox Corporation 1979
// Last modified February 11, 1979  5:55 PM by Boggs

get "Pup0.decl"
get "PupAlEth.decl"
get "Pup1.decl"
get "Streams.d"
get "AltoDefs.d"

external
[
// outgoing procedures
CreatePeekServ; CreateEtherEchoer

// incoming procedures
CreateDisplayStream; MyShowDisplayStream; MakeBar
InitializeContext; Block; Enqueue; Dequeue
Allocate; CallSwat; SetBlock; EtherPupFilter
OpenFile; Puts; Closes; FileLength
PutTemplate; WRITEUDT; Wss; LoadRam; StartIO

// incoming statics
ctxQ; sysZone; sysFont; ndbQ
quitFlag; quitCount; pbiFreeQ
PeekEther1Mc; PeekEther2Mc; lvUserFinishProc
]

static [ ourHostAddress; savedUFP ]

manifest
[
black = -1

peekHost = 376b
peekTypeDMT = 1

etPeekReport = 402b
etEchoMe = 700b
etImAnEcho = 701b
]

structure PeekPBI:
[
@EtherPBI
peekType word
contents↑0,1 word
]

//----------------------------------------------------------------------------
let CreatePeekServ(fileName) be
//----------------------------------------------------------------------------
[
ourHostAddress = StartIO(0) & 377b
// Try to load special version of Ethernet microcode which accepts
// packets to host 376b in addition to the standard filtering rules.
let mcOK = LoadRam((table [ 61014b; 1401b ])()<<VERS.eng le 1? PeekEther1Mc,
 PeekEther2Mc, true) eq 0
if mcOK then
   [
   savedUFP = @lvUserFinishProc
   @lvUserFinishProc = PeekEtherUFP
   ]

let ctx = InitializeContext(Allocate(sysZone, 1000), 1000, PeekServ, 4)
let ndb = ndbQ!0; while ndb ne 0 do
   [
   if ndb>>NDB.netType eq netTypeEther then
      [
      let pf = Allocate(sysZone, lenPF)
      pf>>PF.predicate = PeekPacketFilter
      pf>>PF.queue = ctx+5  // the IQ is in the extension to the ctx
      Enqueue(lv ndb>>NDB.pfQ, pf)
      unless mcOK do  // if failed to load microcode then filter in software
         [
         pf = ndb>>NDB.pfQ.head; while pf ne 0 do
            [
            if pf>>PF.predicate eq EtherPupFilter then
               [
               pf>>PF.predicate = SpecialPupFilter
               @(ndb>>EtherNDB.eHLoc) = 0  // promiscuous
               ]
            pf = pf>>PF.link
            ]
         ]
      ]
   ndb = ndb>>NDB.link
   ]

// make a display window
let dsp = CreateDisplayStream(20, Allocate(sysZone, 5000), 5000, sysFont)
MyShowDisplayStream(dsp)
MyShowDisplayStream(MakeBar(black, 1))

// set up a split stream to disk and display
let st = Allocate(sysZone, lST); SetBlock(st, CallSwat, lST)
st>>ST.par2 = dsp
st>>ST.puts = PeekPuts

let file = OpenFile(fileName, ksTypeWriteOnly, charItem)
st>>ST.par1 = file
ctx!3 = file
ctx!4 = st
ctx!5 = 0  // ctx!5 & 6 used as Peek IQ
Enqueue(ctxQ, ctx)

unless mcOK do
   Wss(st, "*N     Failed to load microcode: using software packet filter")
]

//----------------------------------------------------------------------------
and PeekServ(ctx) be  //a context
//----------------------------------------------------------------------------
// ctx!3 is the file stream
// ctx!4 is the split stream
// ctx!5 is the raw Ether input queue
[
let file = ctx!3
let split = ctx!4
let iq = ctx+5
quitCount = quitCount +1
FileLength(file)  //positions to the end of file quickly
PutTemplate(split, "*N     Peeking started at $P*N", WRITEUDT, 0)
   [
   Block() repeatuntil iq!0 ne 0 % quitFlag
   if quitFlag break
   let pbi = Dequeue(iq)
   PutTemplate(split,"*N$UO#$3UF0O#  $S",
    pbi>>PBI.ndb>>NDB.localNet, pbi>>PeekPBI.src, lv pbi>>PeekPBI.contents)
   Enqueue(pbiFreeQ, pbi)
   ] repeat
PutTemplate(split, "*N     Peeking stopped at $P*N", WRITEUDT, 0)

// clean up in preparation for finishing
Closes(file)
quitCount = quitCount -1
// returning from a context does an implicit Block()
]

//----------------------------------------------------------------------------
and SpecialPupFilter(pbi) =
//----------------------------------------------------------------------------
   pbi>>EtherPBI.dest eq ourHostAddress & pbi>>EtherPBI.type eq typePup &
   (pbi>>PBI.pup.length+5) rshift 1 eq pbi>>PBI.packetLength

//----------------------------------------------------------------------------
and PeekPacketFilter(pbi) =
//----------------------------------------------------------------------------
   pbi>>PeekPBI.dest eq peekHost & pbi>>PeekPBI.src ne 0 &
    pbi>>PeekPBI.type eq etPeekReport & pbi>>PeekPBI.peekType eq peekTypeDMT

//----------------------------------------------------------------------------
and PeekEtherUFP() be
//----------------------------------------------------------------------------
[
(table [ 61010b; 1401b ])(177776b, 1)  // BootLocusVector ← 177776b
StartIO(100000b)  // Flip ether task back into the Rom
@lvUserFinishProc = savedUFP
]

//----------------------------------------------------------------------------
and PeekPuts(stream, item) be  //a stream splitter
//----------------------------------------------------------------------------
[
Puts(stream>>ST.par1, item)
Puts(stream>>ST.par2, item)
]

//----------------------------------------------------------------------------
and CreateEtherEchoer() be
//----------------------------------------------------------------------------
[
ourHostAddress = StartIO(0) & 377b
let ctx = InitializeContext(Allocate(sysZone, 200), 200, RawEtherEchoer, 2)
ctx!3 = 0 // ctx!3 & 4 used as Echo IQ
Enqueue(ctxQ, ctx)

// set up an Ether packet filter for raw Ether Echo packets
let ndb = ndbQ!0; while ndb ne 0 do
   [
   if ndb>>NDB.netType eq netTypeEther then
      [
      let pf = Allocate(sysZone, lenPF)
      pf>>PF.predicate = RawEtherEchoFilter
      pf>>PF.queue = ctx+3
      Enqueue(lv ndb>>NDB.pfQ, pf)
      ]
   ndb = ndb>>NDB.link
   ]
]

//----------------------------------------------------------------------------
and RawEtherEchoer(ctx) be  //a context
//----------------------------------------------------------------------------
[
let iq = ctx+3
Block() repeatwhile iq!0 eq 0
let pbi = Dequeue(iq)
pbi>>EtherPBI.dest = pbi>>EtherPBI.src
pbi>>EtherPBI.src = ourHostAddress
pbi>>EtherPBI.type = etImAnEcho
pbi>>PBI.queue = pbiFreeQ
(pbi>>PBI.ndb>>NDB.level0Transmit)(pbi)
] repeat

//----------------------------------------------------------------------------
and RawEtherEchoFilter(pbi) =
//----------------------------------------------------------------------------
   pbi>>EtherPBI.dest eq ourHostAddress & pbi>>EtherPBI.src ne 0 &
    pbi>>EtherPBI.type eq etEchoMe