// 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) ]