// ImpFrontEnd.bcpl
// Last modified August 22, 1983 11:08 AM by Taft
get "Pup0.decl"
get "PupAlImp.decl"
get "AltoDefs.d"
get "SysDefs.d"
get "Streams.d"
get "ImpFENames.d"
get "MenuDefs.d"
external
[
// outgoing procedures
Wss
// incoming procedures
InitPupLevel1; LoadRam; InitBcplRuntime
Enqueue; Dequeue
InitializeContext; CallContextList; Block
SetTimer; TimerHasExpired; Dismiss
CreateStringStream; Puts; Closes; PutTemplate
ShowDisplayStream; CreateDisplayStream; CreateKeyboardStream
InitializeZone; AddToZone; Allocate; Free
MoveBlock; SetBlock; Zero; MyFrame; Junta; DoubleIncrement; Umin; CallSwat; SysErr
// incoming statics
impNDB; ndbQ; lenPBI; pbiFreeQ; sysZone; RamImage; keys; dsp; gacha12; sysFont
]
static
[
version
impIQ; etherIQ
etherIPHost = 0 // host to send IP packets to
etherNDB
ctxQ
stats
beginStorage; endStorage
]
//----------------------------------------------------------------------------
structure ArpanetAddress: // IP address of an Arpanet host
//----------------------------------------------------------------------------
[
net byte
host byte
imp word
]
manifest lenArpanetAddress = size ArpanetAddress/16
//----------------------------------------------------------------------------
structure IPPBI: // IP encapsulation in Ethernet packet to/from front-end
//----------------------------------------------------------------------------
[
blank word offset PBI.encapsulation/16
dest byte // standard Ethernet header
src byte
type word
arpaAdr @ArpanetAddress // Arpanet source or destination
data word 0 = ip @IPHeader // encapsulated IP packet
]
manifest lenIPEncap = (offset IPPBI.data - offset PBI.encapsulation)/16
//----------------------------------------------------------------------------
structure Stats: // Forwarding statistics
//----------------------------------------------------------------------------
[
fromImp word 2
toImp word 2
discard word 2
hostDown word
impDown word
]
manifest lenStats = size Stats/16
structure String: [ length byte; char↑1,1 byte ]
manifest
[
// Ethernet types for encapsulating IP packet between IP host and front-end
etIPtoFE = 1004B // IP host to front-end (arpaAdr is destination)
etFEtoIP = 1005B // front-end to IP host (arpaAdr is source)
netArpa = 10 // Arpanet IP net number
minLenIPPkt = 10 // words in minimum-length IP packet
maxLenIPPkt = 576/2 // words in maximum-length IP packet
maxLenEtherIPPkt = lenIPEncap+maxLenIPPkt // max-length Ethernet-encapsulated IP packet
lenForwarderCtx = 150
lenDisplayCtx = 400
endCode = 335B
]
//----------------------------------------------------------------------------
let Main() be
//----------------------------------------------------------------------------
[
version = "ImpFrontEnd of August 22, 1983"
Junta(levAlloc, Top)
]
//----------------------------------------------------------------------------
and Top() be
//----------------------------------------------------------------------------
[
Init()
// Give leftover storage to sysZone and turn it into packet buffers
AddToZone(sysZone, InitPupLevel1, beginStorage-InitPupLevel1)
@endCode = MyFrame()-50
AddToZone(sysZone, endStorage, @endCode-endStorage)
let slop = Allocate(sysZone, 500)
[ // repeat
let pbi = Allocate(sysZone, lenPBI, true)
if pbi eq 0 then break
Enqueue(pbiFreeQ, pbi)
] repeat
Free(sysZone, slop)
CallContextList(ctxQ!0) repeat
]
//----------------------------------------------------------------------------
and ImpForwarderCtx(ctx) be // Handles forwarding in both directions
//----------------------------------------------------------------------------
[ // repeat
while impIQ!0 ne 0 do
[
let ipbi = Dequeue(impIQ)
if etherIPHost eq 0 then
[ Discard(ipbi); loop ] // no IP host to send to yet
let opbi = Dequeue(pbiFreeQ) // copying packet is faster than shifting up in-place
if opbi eq 0 then
[ Discard(ipbi); loop ] // can't get PBI to copy into
let lenIPPkt = ipbi>>PBI.packetLength
if offset IPPBI.data/16+lenIPPkt gr lenPBI then
[ Discard(opbi); loop ] // too large to be encapsulated in local PBI
MoveBlock(lv opbi>>IPPBI.data, lv ipbi>>ImpPBI.data, lenIPPkt)
opbi>>IPPBI.arpaAdr.net = netArpa
opbi>>IPPBI.arpaAdr.host = ipbi>>ImpPBI.host
opbi>>IPPBI.arpaAdr.imp = ipbi>>ImpPBI.imp
opbi>>PBI.packetLength = lenIPEncap+lenIPPkt
opbi>>IPPBI.dest = etherIPHost
opbi>>IPPBI.src = etherNDB>>NDB.localHost
opbi>>IPPBI.type = etFEtoIP
opbi>>PBI.ndb = etherNDB
opbi>>PBI.queue = pbiFreeQ
Enqueue(pbiFreeQ, ipbi)
(etherNDB>>NDB.level0Transmit)(opbi)
DoubleIncrement(lv stats>>Stats.fromImp)
]
while etherIQ!0 ne 0 do
[
let pbi = Dequeue(etherIQ)
if pbi>>IPPBI.dest eq 0 then
[ Discard(pbi); loop ] // ignore broadcasts
etherIPHost = pbi>>IPPBI.src // partner is most recent guy who sent to us
let lenIPPkt = pbi>>PBI.packetLength - lenIPEncap
pbi>>ImpPBI.host = pbi>>IPPBI.arpaAdr.host
pbi>>ImpPBI.imp = pbi>>IPPBI.arpaAdr.imp
pbi>>ImpPBI.link = linkInternet
MoveBlock(lv pbi>>ImpPBI.data, lv pbi>>IPPBI.data, lenIPPkt)
pbi>>PBI.packetLength = lenIPPkt
pbi>>PBI.ndb = impNDB
pbi>>PBI.queue = pbiFreeQ
(impNDB>>NDB.level0Transmit)(pbi)
DoubleIncrement(lv stats>>Stats.toImp)
]
Block()
] repeat
//----------------------------------------------------------------------------
and Discard(pbi) be
//----------------------------------------------------------------------------
[
Enqueue(pbiFreeQ, pbi)
DoubleIncrement(lv stats>>Stats.discard)
]
//----------------------------------------------------------------------------
and ImpIPFilter(pbi) = valof
//----------------------------------------------------------------------------
[
let lenIPPkt = (pbi>>ImpPBI.ip.length+1) rshift 1
test pbi>>ImpPBI.link eq linkInternet & pbi>>ImpPBI.ip.version eq versionIP &
pbi>>PBI.packetLength ge minLenIPPkt & lenIPPkt le pbi>>PBI.packetLength
ifso [ pbi>>PBI.packetLength = lenIPPkt; resultis true ] // flush Imp padding
ifnot resultis false
]
//----------------------------------------------------------------------------
and EtherIPFilter(pbi) =
//----------------------------------------------------------------------------
pbi>>PBI.packetLength ge lenIPEncap+minLenIPPkt & pbi>>IPPBI.type eq etIPtoFE
//----------------------------------------------------------------------------
and DisplayCtx(ctx) be
//----------------------------------------------------------------------------
// Handles interactions with display and menu
[
let updateTimer = nil
SetTimer(lv updateTimer, 0)
let menu = MenuData>>DATA.menu
let oldStats = vec lenStats
SetBlock(oldStats, -1, lenStats)
[ // repeat
let selection = ScanMenu(menu, false)
switchon selection into
[
case bQuit:
@displayListHead = 0
Dismiss(1)
finish
default:
if selection ne 0 then DeSelect(menu!selection)
]
if TimerHasExpired(lv updateTimer) then
[
DisplayNumber(bFromImp, lv stats>>Stats.fromImp, lv oldStats>>Stats.fromImp)
DisplayNumber(bToImp, lv stats>>Stats.toImp, lv oldStats>>Stats.toImp)
DisplayNumber(bDiscarded, lv stats>>Stats.discard, lv oldStats>>Stats.discard)
stats>>Stats.hostDown = impNDB>>ImpNDB.status.hostNotReady
stats>>Stats.impDown = impNDB>>ImpNDB.status.impNotReady
DisplayUpDown(bHostUpDown, lv stats>>Stats.hostDown, lv oldStats>>Stats.hostDown,
"Host")
DisplayUpDown(bImpUpDown, lv stats>>Stats.impDown, lv oldStats>>Stats.impDown,
"Imp")
SetTimer(lv updateTimer, 100) // 1 second
]
Dismiss(1)
] repeat
]
//----------------------------------------------------------------------------
and DisplayNumber(boxName, lvNum, lvOldNum) be
//----------------------------------------------------------------------------
[
if lvNum!0 ne lvOldNum!0 % lvNum!1 ne lvOldNum!1 then
[
lvOldNum!0 = lvNum!0
lvOldNum!1 = lvNum!1
let box = (MenuData>>DATA.menu)!boxName
FillBox(box, white)
let string = vec 10
let ss = CreateStringStream(string, 20)
PutTemplate(ss, "$8ED", lvNum)
Closes(ss)
WriteBox(box, string, 0, sysFont)
Dismiss(1)
]
]
//----------------------------------------------------------------------------
and DisplayUpDown(boxName, lvDown, lvOldDown, who) be
//----------------------------------------------------------------------------
[
if lvDown!0 ne lvOldDown!0 then
[
lvOldDown!0 = lvDown!0
let box = (MenuData>>DATA.menu)!boxName
FillBox(box, white)
let string = vec 10
let ss = CreateStringStream(string, 20)
PutTemplate(ss, "$S $S", who, (lvDown!0? "down", "up"))
Closes(ss)
WriteBox(box, string, 0, sysFont)
Dismiss(1)
]
]
//----------------------------------------------------------------------------
and Wss(s, str) be // needed because we Junta it and PutTemplate calls it
//----------------------------------------------------------------------------
for i=1 to str>>String.length do Puts(s, str>>String.char↑i)
//----------------------------------------------------------------------------
and Init() be
//----------------------------------------------------------------------------
// This code could be discarded at the end of initialization if we felt like it.
[
// Storage initialization
endStorage = MyFrame() - 1000
let lenStorage = Umin(endStorage-@endCode, 77777B)
beginStorage = endStorage-lenStorage
@endCode = endStorage
sysZone = InitializeZone(beginStorage, lenStorage, SysErr, 0)
// Context initialization
ctxQ = Allocate(sysZone, 2); Zero(ctxQ, 2)
Enqueue(ctxQ, InitializeContext(Allocate(sysZone, lenForwarderCtx),
lenForwarderCtx, ImpForwarderCtx))
Enqueue(ctxQ, InitializeContext(Allocate(sysZone, lenDisplayCtx),
lenDisplayCtx, DisplayCtx))
// Microcode initialization
test LoadRam(RamImage, true) eq 0
ifso InitBcplRuntime()
ifnot CallSwat("Can't load microcode")
// Pup package initialization
// Packet buffers must be larger than normal so as to accomodate Ethernet-encapsulated
// IP packets with perhaps one extra word of Imp padding. Computation is tricky
// since the number we pass to InitPupLevel1 is based on number of Pup data bytes
// whereas we are dealing in raw packets.
manifest bytes = 2*maxLenEtherIPPkt - pupOvBytes - size PBI.encapsulation/8 + 2
InitPupLevel1(sysZone, ctxQ, 10, bytes)
// Make sure we have an Imp interface, and find the Ethernet NDB
let ndb = ndbQ!0
let haveImp = false
while ndb ne 0 do
[
if ndb>>NDB.netType eq netTypeArpa then haveImp = true
if ndb>>NDB.netType eq netTypeEther then etherNDB = ndb
ndb = ndb!0
]
unless haveImp do CallSwat("Imp interface not installed")
if etherNDB eq 0 then CallSwat("Ethernet interface not installed")
// Set up IP packet filters for Imp and Ethernet
let pf = Allocate(sysZone, lenPF+2); Zero(pf, lenPF+2)
impIQ = pf+lenPF
pf>>PF.predicate = ImpIPFilter
pf>>PF.queue = impIQ
if haveImp then Enqueue(lv impNDB>>NDB.pfQ, pf)
pf = Allocate(sysZone, lenPF+2); Zero(pf, lenPF+2)
etherIQ = pf+lenPF
pf>>PF.predicate = EtherIPFilter
pf>>PF.queue = etherIQ
if etherNDB ne 0 then Enqueue(lv etherNDB>>NDB.pfQ, pf)
// Display and menu initialization
// Make a DisplayStream window at the top of the screen, both to
// space the Menu display down and to give the Menu package
// a real dsp to work from (it gets the default font by calling
// GetFont(dsp)!!!!).
sysFont = gacha12+2
dsp = CreateDisplayStream(6, Allocate(sysZone, 200), 200, sysFont, 2)
ShowDisplayStream(dsp, DSalone)
let lenBitMap = MenuSize()
let bitMap = Allocate(sysZone, lenBitMap)
let stream = CreateMenuDisplayStream(bitMap, lenBitMap)
ShowDisplayStream(stream)
MenuIdle = Block
FillBox((MenuData>>DATA.menu)!bTitle, white)
WriteBox((MenuData>>DATA.menu)!bTitle, version, 0, sysFont)
// Misc other initialization
keys = CreateKeyboardStream()
stats = Allocate(sysZone, lenStats); Zero(stats, lenStats)
]