PACKET basic net DEFINES                          (* D. Heinrichs *)
                          (* Version  10 (!) *)   (* 18.02.87     *)
  nam,                                            (* 03.06.87     *)
  max verbindungsnummer,                          (*              *)
  neuer start,
  neue routen,
  packet eingang,
  neue sendung,
  zeitueberwachung,
  verbindung,
  loesche verbindung:

TEXT PROC nam (TASK CONST t):
  IF t = collector THEN name (t)
  ELIF station (t) <> station (myself) 
  THEN "** fremd "+text(station(t))+" **"
  ELSE name (t)
  FI
END PROC nam;

INT PROC tasknr (TASK CONST t):
  IF t = collector THEN maxtasks
  ELSE index (t)
  FI
END PROC tasknr;

LET
    maxtasks = 127,
    maxstat = 127,
    max strom = 20,
    max strom 1 = 21,
    stx = ""2"",
    code stx = 2, 
    error nak = 2,
    seiten groesse = 512,
    dr verwaltungslaenge = 8,
    dr verwaltungslaenge2=10,
    openlaenge = 24,
    vorspannlaenge = 14, 
    ack laenge = 12,
    min data length = 64,
    (* Codes der Verbindungsebene *) 
 
    task id code = 6, 
    name code = 7,
    task info code = 8, 
    routen liefern code = 9, 

    (* Typen von Kommunikationsströmen *)

    send wait = 0,
    zustellung = 1,
    call pingpong = 2,
    call im wait = 3,
    call im abbruch = 4,
    call in zustellung = 5,

    (*quittungscodes*)

    ok = 0,
    von vorne = 1,
    wiederhole = 2,
    loesche = 3,
    beende = 4;

LET STEUER =
      STRUCT ( 
      INT  head, 
           zwischenziel,
           zielrechner, 
           quellrechner,
           strom,
           sequenz,
           seitennummer,
      TASK quelle,ziel,
      INT  sende code);
 
BOUND STEUER VAR open block;

BOUND STRUCT (STEUER steuer, INT typ, maxseq) VAR info block; 
 
BOUND STRUCT (
      INT  head, 
           zwischenziel,
           zielrechner, 
           quellrechner,
           strom,
           sequenz,
           seitennummer) VAR vorspann ;
 
LET ACK = STRUCT ( 
      INT  head, 
           zwischenziel,
           zielrechner, 
           quellrechner,
           strom, 
           code);
BOUND ACK VAR ack packet ; 
BOUND ACK VAR transmitted ack packet;

BOUND STRUCT (ROW maxstat INT port, 
              ROW maxstat INT zwischen)  VAR route;
 
INT CONST max verbindungsnummer := max strom;
INT VAR codet,net mode, nutzlaenge := data length,
        data len via node := data length via node;

TEXT VAR buffer first;
 
DATASPACE VAR work space := nilspace;
DATASPACE VAR transmitted ack space := nilspace;
 
 
INT VAR pakete pro seite,
        pakete pro seite minus 1,
        packets per page via node,
        packets per page via node minus 1,
        datenpacketlaenge via node,
        datenpacketlaenge ;
 
INT VAR strom; 
INT VAR last data := -1;
INT VAR   own:=station (myself) ,
          quit max := 3,
          quit zaehler := 3,
          own256 := 256*own;
INT CONST stx open := code stx+256*openlaenge,
          stx quit := code stx+256*acklaenge;

  STEUER VAR opti; 
  ROW maxstrom1 STEUER VAR verbindungen; 
  ROW maxstrom1 DATASPACE VAR netz dr; 
  ROW maxstrom1 INT VAR zeit, typ, open try;
  FOR strom FROM 1 UPTO maxstrom1 REP vdr := nilspace; forget (vdr) PER;
  ROW maxstrom INT VAR dr page ;
  ROW maxtasks INT VAR alter call;

.vx : verbindungen (strom). 
 
vdr: netz dr (strom). 

 via node:
    vx.zielrechner <= 0 OR vx.quellrechner <= 0 OR
    transmit via node  OR receive via node.

 transmit via node:
    route.zwischen (vx.zielrechner) <> vx.zielrechner AND vx.zielrechner <> own.

 receive via node:
    route.zwischen (vx.quellrechner) <> vx.quellrechner AND vx.quellrechner <> own.

falsche stromnummer: strom < 1 OR strom > maxstrom.

zielrechner ok: vorspann.zielrechner > 0 AND vorspann.zielrechner <= maxstat.

quellrechner ok: vorspann.quellrechner > 0
                 AND vorspann.quellrechner <= maxstat.

call aufruf: typ(strom) >= call pingpong.

alles raus: vx.seitennummer = -1 AND letztes packet der seite . 

letztes packet der seite :
(vx.sequenz AND packets per page minus 1) = packets per page minus 1.

neue verbindung: code t = open laenge.

PROC neue routen:
  route := old ("port intern");
END PROC neue routen;

PROC neuer start (INT CONST empfangsstroeme, mode):
  net mode := mode;
  strom := 1;
  neue routen;
  transmitted ack space := nilspace;
  workspace := nilspace;
  open block := workspace; 
  info block := workspace;
  nutzlaenge := data length;
  data len via node := data length via node;
  pakete pro seite:= seitengroesse DIV nutzlaenge;
  pakete pro seite minus 1 := pakete pro seite -1;
  packets per page via node := seitengroesse DIV data len via node;
  packets per page via node minus 1 := packets per page via node - 1;
  datenpacketlaenge := vorspannlaenge + nutzlaenge;
  datenpacketlaenge via node := vorspannlaenge + data len via node;
  vorspann := workspace; 
  ack packet := workspace;
  transmitted ack packet := transmitted ack space;
  FOR strom FROM 1 UPTO maxstrom1 REP 
  vx.strom := 0;  forget (vdr)
  PER; 
  INT VAR i;
  FOR i FROM 1 UPTO maxtasks REP alter call (i) := 0 PER;
  quitmax := empfangsstroeme;
  own:=station (myself);
  quit zaehler := quit max;
  own256 := 256*own; 
  reset box (net mode);
  buffer first := "";
  flush buffers;
  INT VAR err;
  fehlermeldung ruecksetzen.

 fehlermeldung ruecksetzen:
  control (12,0,0,err).

END PROC neuer start;

DATASPACE PROC verbindung (INT CONST nr):
  INT VAR memory := strom;
  strom := nr;
  infoblock.steuer := verbindungen (nr);
  infoblock.typ := typ (nr);
  infoblock.maxseq := dspages (netzdr(nr)) * packets per page;
  strom := memory;
  workspace
END PROC verbindung;

PROC neue sendung (TASK CONST q,z, INT CONST cod,z stat, DATASPACE CONST dr):

  naechste verbindung vorbereiten;
  forget (vdr); vdr := dr;
  sendung starten (q, z, z stat, cod)
END PROC neue sendung; 

PROC zeitueberwachung
     (INT VAR snr, TASK VAR  q, z, INT VAR ant,DATASPACE VAR  dr):
  snr INCR 1;
  FOR strom FROM snr UPTO maxstrom REP zeitkontrolle PER;
  snr := 0. 

zeitkontrolle: 
  IF vx.strom <> 0 AND zeit(strom) > 0
  THEN 
    zeit(strom) DECR 1; 
    IF sendung noch nicht zugestellt
    THEN 
      IF zeit(strom) = 0
      THEN
         empfangsreport ("Nicht zustellbar. ");
         loesche verbindung (strom)
      ELSE
         snr := strom;
         q := vx.quelle;
         z := vx.ziel;
         ant := vx.sendecode;
         dr := vdr;
         LEAVE zeitueberwachung
      FI
    ELIF zeit(strom) = 0
    THEN wiederholen
    FI 
  FI. 
 
sendung noch nicht zugestellt:
  typ (strom) = zustellung.

wiederholen: 
  IF sendeeintrag 
  THEN 
    sendung wiederholen
  ELSE
    empfangseintrag freigeben
  FI. 
 
sendeeintrag : vx.quellrechner = own . 
 
sendung wiederholen:
  IF wiederholung noch sinnvoll 
  THEN
    IF frisch 
    THEN 
      time out bei open
    ELSE 
      datenteil wiederholen 
    FI
  ELSE
    sendung loeschen
  FI.

wiederholung noch sinnvoll:
  task noch da AND bei call noch im call.

task noch da: vx.quelle = collector OR exists (vx.quelle).

bei call noch im call: 
  IF call aufruf
  THEN
    callee (vx.quelle) = vx.ziel
  ELSE
    TRUE
  FI.

frisch: vx.sequenz = -1. 
 
time out bei open:
  IF vx.sendecode > -4 OR opentry (strom) > 0
  THEN
    open wiederholen ;
    opentry (strom) DECR 1
  ELSE
    nak an quelle senden
  FI.

nak an quelle senden:
  dr := nilspace;
  BOUND TEXT VAR erm := dr;
  erm := "Station "+text(vx.zielrechner)+" antwortet nicht";
  snr := strom;
  q := vx.ziel;
  z := vx.quelle;
  ant := error nak;
  sendung loeschen;
  LEAVE zeitueberwachung .

open wiederholen: 
  sendereport ("wdh open"); 
  IF opentry (strom) > 0 THEN zeit(strom) := 4 ELSE zeit(strom) := 40 FI;
  openblock := vx; 
  openblock.head := stx open; 
  ab die post. 
 
datenteil wiederholen: 
  sendereport ("wdh data. sqnr "+text (vx.sequenz));
  senden .

empfangseintrag freigeben:
  IF antwort auf call 
  THEN
    weiter warten
  ELSE
    empfangsreport ("Empfangseintrag freigegeben");
    loesche verbindung (strom)
  FI.
antwort auf call: callee (vx.ziel) = vx.quelle.

weiter warten: zeit (strom) := 400.

END PROC zeitueberwachung;

PROC sendereport (TEXT CONST txt):
  report (text (strom)+":"+txt+". Absender: """+nam (vx.quelle)+
          """. Ziel "+text(vx.zielrechner) + " Taskindex: " +
          text (index (vx.ziel))); 
END PROC sendereport;

PROC empfangsreport  (TEXT CONST txt):
  report (text (strom)+":"+txt+". Empfänger: """
          +nam (vx.ziel)+""". Quelle "+text (vx.quellrechner) +
          " Taskindex: " + text (index (vx.quelle)));
END PROC empfangsreport  ;

PROC sendung loeschen:
  strom loeschen (tasknr (vx.quelle))
END PROC sendung loeschen;

PROC strom loeschen (INT CONST tasknr):
    IF callaufruf CAND alter call (tasknr ) = strom
    THEN
       alter call (tasknr ) := 0
    FI;
    vx.strom := 0;
    forget (vdr)
END PROC strom loeschen;

PROC empfang loeschen:
    quit zaehler INCR 1;
    strom loeschen (tasknr (vx.ziel))
END PROC empfang loeschen;

PROC loesche verbindung (INT CONST nr):
  strom := nr;
  IF sendeeintrag
  THEN
    sendung loeschen
  ELSE
    gegenstelle zum loeschen auffordern; 
    empfang loeschen
  FI.

gegenstelle zum loeschen auffordern:
  IF verbindung aktiv THEN quittieren (-loesche) FI.

verbindung aktiv: vx.strom > 0.

sendeeintrag: vx.quellrechner = own . 

END PROC loesche verbindung;

PROC weiter senden: 
  IF NOT alles raus
  THEN
    sequenz zaehlung; 
    IF neue seite THEN seitennummer eintragen FI;
    senden
  FI.

sequenz zaehlung: 
  vx.sequenz INCR 1. 
 
neue seite: 
  IF  via node THEN (vx.sequenz AND packets per page via node minus 1) = 0
                       ELSE (vx.sequenz AND pakete pro seite minus 1) = 0
  FI.

seitennummer eintragen: 
  dr page (strom) :=  vx.seiten nummer;
  vx.seitennummer := next ds page (vdr, dr page (strom)). 
 
 
END PROC weiter senden;
 
.packets per page:

  IF via node THEN packets per page via node
              ELSE pakete pro seite
  FI.

packets per page minus 1:
   IF via node THEN packets per page via node minus 1
               ELSE pakete pro seite minus 1
   FI.

used length:

   IF via node THEN data len via node
               ELSE nutzlaenge
   FI.

PROC senden:
  INT VAR nl;
  zeit(strom) := 6;
  openblock := vx;
  nl := used length;
  transmit header (workspace);
  vorspann senden;
  daten senden;
  transmit trailer.

vorspann senden: 
  blockout (workspace, 1, dr verwaltungslaenge, vorspannlaenge). 
 
daten senden: 
  blockout (vdr,dr page (strom),distanz,nl). 
 
distanz: nl* (vx.sequenz AND packets per page minus 1). 

END PROC senden;

PROC naechste verbindung vorbereiten: 
  FOR strom FROM 1 UPTO maxstrom REP 
  UNTIL vx.strom = 0 PER; 
  IF vx.strom <> 0 THEN errorstop ("Verbindungsengpass") FI. 
END PROC naechste verbindung vorbereiten;

PROC sendung starten (TASK CONST quelle, ziel, INT CONST code): 
  sendung starten (quelle,ziel, station(ziel), code) 
END PROC sendung starten;
 
PROC sendung starten (TASK CONST quelle, ziel, INT CONST ziel station,code):
  IF ziel station = own 
  THEN
    report ("Irrläufer: Sendung an eigene Station. Absender:"""+
            nam (quelle)+""".");
    vx.strom := 0;
    forget (vdr)
  ELSE
    openblock.ziel := ziel;
    openblock.quelle :=quelle; 
    openblock.sendecode := code;
    openblock.zielrechner:= ziel station;
    openblock.quellrechner :=own;
    openblock.zwischenziel := route.zwischen (ziel station)+own256;
    alten call loeschen (quelle);
    IF call oder ping pong
    THEN typ (strom) := call pingpong; call merken
    ELSE typ (strom) := send wait FI;
    sendung neu starten
  FI.

call oder pingpong: openblock.ziel = callee (openblock.quelle).

call merken: alter call (tasknr (quelle)) := strom.

END PROC sendung starten;

PROC encode packet length (INT VAR val):

   IF val < 96 THEN
     ELIF val < 160 THEN val DECR 32
       ELIF val < 288 THEN val DECR 128
         ELIF val < 544 THEN val DECR  352
           ELIF val < 1056 THEN val DECR 832
             ELIF val < 2080 THEN val DECR 1824
    FI;
    rotate (val, 8)

ENDPROC encode packet length;

PROC sendung neu starten:
  INT VAR value;
  openblock.head:= stx open; 
  openblock.sequenz := -1; 
  openblock.seitennummer:= next ds page (vdr,-1); 
  openblock.strom := strom;
  vx := open block; 
  schnelles nak bei routen liefern;
  ab die post;
  value := vorspannlaenge + used length;
  encode packet length (value);
  vx.head:=code stx+value.

schnelles nak bei routen liefern:
  IF openblock.sendecode = -routen liefern code
  THEN
    openblock.zwischenziel := openblock.zielrechner+own256;
    zeit(strom) := 2;
    opentry (strom) := 0
  ELSE
    zeit (strom) :=8;
    opentry (strom) := 2
  FI.
   
END PROC sendung neu starten; .

ab die post: 
   transmit header (workspace);
   block out (work space,1, dr verwaltungslaenge,open laenge);
   transmit trailer.

PROC alten call loeschen (TASK CONST quelle):
  IF alter call aktiv
  THEN
    INT VAR lstrom := strom;
    vx:=openblock;
    strom := alter call (tasknr (quelle));
    IF in ausfuehrungsphase
    THEN
      sendereport ("Call-Löschung vorgemerkt");
      loeschung vormerken
    ELSE
      report ("Call gelöscht."""+nam(quelle)+""". Strom "+text(strom));
      loesche verbindung (strom)
    FI;
    strom := lstrom;
    openblock := vx
  FI.

in ausfuehrungsphase:
  typ(strom) = call im wait OR typ (strom) = call in zustellung.

loeschung vormerken:
  typ(strom) := call im abbruch;
  alter call (tasknr (quelle)) := 0.

 
  alter call aktiv:
    alter call (tasknr (quelle)) > 0.

END PROC alten call loeschen;

PROC packet eingang
  ( INT VAR snr, TASK VAR  q, z, INT VAR ant,DATASPACE VAR  dr):
    snr := 0;
    fehlertest;
    vorspann holen; 
    IF NOT ring logik THEN daten teil FI.

ring logik: FALSE.

fehlertest:
#
    INT VAR c12;
    control (12,0,0,c12);
    IF c12 <> 0
    THEN
      flush buffers;
      report ("E/A-Fehler "+text (c12));
      control (12,0,0,c12);
      LEAVE packet eingang
    FI.

                                                #.

vorspann holen: 
  sync; 
  IF NOT blockin (workspace, 1, dr verwaltungslaenge2, block laenge)
  THEN LEAVE packeteingang 
  FI.


blocklaenge: IF code t  > min data length
             THEN 
               vorspannlaenge-2 
             ELSE 
               code t -2 
             FI. 

sync: 
 IF NOT packet start already inspected
 THEN
   TEXT VAR skipped, t:= "";
   skipped := next packet start;
   IF skipped = "" THEN LEAVE packet eingang FI;
   t := incharety (1);
   code t := code (t);
 ELSE
   skipped := buffer first;
   buffer first := "";
   t := incharety (1);
   code t := code (t);
 FI;
 decode packet length;
IF skipped=stx AND laenge ok THEN LEAVE sync FI;
  REP 
    skipped CAT t;
    t := incharety (1);  (* next character *)
    IF t = "" THEN 
      report ("skipped",skipped);
      LEAVE packet eingang
    FI ;
    codet := code (t);
  UNTIL blockanfang OR length (skipped) > 200 PER; 
  decode packet length;
  IF skipped <> stx THEN report ("skipped bei sync:", skipped)    FI. 
 
decode packet length:

IF code t < 96 THEN
  ELIF code t < 128 THEN code t INCR 32
    ELIF code t < 160 THEN code t INCR 128
      ELIF code t < 192 THEN code t INCR 352
        ELIF code t < 224 THEN code t INCR 832
          ELIF code t < 256 THEN code t INCR 1824
FI.

packet start already inspected: buffer first <> "".

blockanfang: 
  (skipped SUB length(skipped)) = stx AND laenge ok.

laenge ok:
  (codet = datenpacketlaenge OR codet = datenpacketlaenge via node
  OR codet = ack laenge OR code t = openlaenge). 
 
zielnummer:    vorspann.zielrechner.

daten teil: 
  IF zielnummer = own
  THEN
    ziel erreicht (openblock,snr,q,z,ant,dr)
  ELSE
    weiter faedeln
  FI.

weiter faedeln:
  INT VAR value;
  IF zielrechner ok
  THEN
    IF neue verbindung
    THEN
      IF (openblock.sendecode = -routenlieferncode) OR NOT route ok
        THEN LEAVE packet eingang
      FI
    FI;
    value := code t;
    encode packet length (value);
    vorspann.head := code stx + value;
    vorspann.zwischenziel := own256 + route.zwischen (vorspann.zielrechner);
    nutzdaten einlesen;
    dr := workspace;
    snr := 1000;
    ant := zielnummer
  FI.

nutzdaten einlesen:
  IF code t > data len via node
  THEN
    IF NOT blockin (workspace, 1, drverwaltungslaenge+vorspannlaenge, data len via node)
    THEN
      LEAVE packeteingang
    FI;
    IF NOT next packet ok THEN LEAVE packeteingang FI
  FI.

END PROC packet eingang;

PROC ziel erreicht (STEUER CONST prefix,
          INT VAR snr, TASK VAR  q, z, INT VAR ant,DATASPACE VAR  dr):
  last data := -1;
  IF NOT quellrechner ok
  THEN
    report ("Quellrechner "+text(prefix.quellrechner));
    LEAVE ziel erreicht
  FI;
  IF neue verbindung 
  THEN
    IF NOT route ok OR NOT quelltask ok
    THEN report ("verbotene Route: " + text (prefix.quellrechner));
         LEAVE ziel erreicht
    FI;
    verbindung bereitstellen
  ELIF quittung 
  THEN 
    strom := ack packet.strom; 
    IF falsche stromnummer THEN report ("Strom falsch in Quittung");
                                LEAVE ziel erreicht FI;
    IF vx.strom = 0 THEN LEAVE ziel erreicht FI;
    IF ackpacket.code >= ok   THEN weiter senden
    ELIF NOT route ok THEN
        sendereport ("verbotene Route bei Quittung");
        LEAVE ziel erreicht
    ELIF ackpacket.code = -von vorne THEN
        sendereport ("Neustart");
        openblock := vx; 
        sendung neu starten 
    ELIF ackpacket.code = -wiederhole THEN back 16
    ELIF ackpacket.code = -loesche    THEN fremdloeschung
    ELIF ackpacket.code = -beende AND alles raus THEN strom abschliessen
    FI
  ELIF verbindung festgestellt 
  THEN
    zeit(strom) := 400;
    opti := vx;
    datenpacket
  ELSE 
    strom := maxstrom1; 
    vx:=prefix;
    report ("Daten ohne Eroeffnung von " +text(prefix.quellrechner) 
    +"  Sequenznr "+text(prefix.sequenz)); 
    daten entfernen (used length);
    IF alles raus THEN quittieren (-beende) ELSE quittieren(-von vorne) FI
  FI. 

quelltask ok:
  prefix.quelle = collector OR antwort auf routen liefern
  OR station (prefix.quelle) = prefix.quellrechner.

antwort auf routen liefern: prefix.quelle = myself.

verbindung bereitstellen:
  IF (prefix.sendecode < 0 OR station (prefix.ziel) = own)
     AND quellrechner ok
  THEN
    freie verbindungsnummer; 
    vdr := nilspace;
    vx := open block; 
    zeit(strom) := 30;
    quittieren falls genug pufferplatz;
    vx.sequenz := 0 ; 
    opti := vx;
    dr page (strom) :=-2;
    IF abschluss THEN rueckmeldung FI
  FI.

loeschung vorgemerkt: typ(strom) = call im abbruch.

strom abschliessen:
  IF call aufruf
  THEN
    wdh data vor ablauf der zustellversuche bei der gegenstation;
    ausfuehrungsphase merken
  ELSE
    wdh data sperren
  FI.

wdh data sperren:
    zeit (strom) := 12000.

wdh data vor ablauf der zustellversuche bei der gegenstation:
    zeit (strom) := 80.

ausfuehrungsphase merken: typ(strom) := call in zustellung.
 
back16:
  datenraum etwas rueckspulen; 
  opentry (strom) := 2;
  nicht sofort senden (* wegen vagabundierender Quittungen *).

nicht sofort senden: zeit(strom) := 2.

datenraum etwas rueckspulen:
  INT VAR pps := packets per page ;
  sendereport ("etwas rueckgespult");
  INT VAR  vs :=-1;
  dr page (strom) := -1;
  INT VAR i;
  FOR i FROM 1 UPTO vx.sequenz DIV pps - etwas REP
    vs INCR pps;
    dr page (strom) := next ds page (vdr, dr page (strom)) 
  PER;
  vx.seiten nummer := next ds page (vdr, dr page (strom)) ;
  vx.sequenz := vs.

etwas: 3.

fremdloeschung:
  IF fremdrechner ok und sendung 
  THEN
    IF typ (strom) = call in zustellung
    THEN
      typ (strom) := call im wait
    ELSE
      IF NOT alles raus
      THEN
        sendereport ("Sendung von Gegenstelle geloescht")
      FI;
      sendung loeschen
    FI
  FI.

fremdrechner ok und sendung:
  ackpacket.quellrechner = vx.zielrechner .


quittieren falls genug pufferplatz:
  IF quit zaehler > 0 THEN
    quit zaehler DECR 1;
    open quittieren;
    block vorab quittieren
  ELSE
    quittieren (-wiederhole)
  FI.

open quittieren: quittieren (ok).
block vorab quittieren:
  IF prio (myself) < 3 THEN quittieren (ok) FI.

quittung:  code t <= ack laenge. 
 
 
verbindung festgestellt: 
  FOR strom FROM maxstrom DOWNTO 1 REP 
    IF bekannter strom
    THEN LEAVE verbindung festgestellt WITH TRUE FI 
  PER; 
  FALSE. 
 
bekannter strom: 
  vx.strom = prefix.strom AND vom selben rechner. 
 
vom selben rechner: 
  vx.quellrechner = prefix.quellrechner. 
 
daten: 
      IF neue seite da THEN check for valid pagenr;
                            dr page(strom) := prefix.seitennummer;
        ELIF prefix.seitennummer < dr page(strom)
              THEN empfangsreport ("Falsche Seitennummer, Soll: " +
                                  text(drpage(strom)) + " ist: " +
                                  text (prefix.seitennummer)
                                  + " bei Sequenznr: " +
                                  text(prefix.sequenz));
                   flush buffers;
                   quittieren (- wiederhole);
                   LEAVE ziel erreicht
  FI;
  sequenz zaehlung;
  IF neue seite kommt 
  THEN 
    vx.seiten nummer := prefix.seiten nummer; 
    dr page(strom) := prefix.seitennummer;
  FI;
  quittieren(ok); 
  IF NOT blockin (vdr, opti.seiten nummer, distanz, nl)
  COR NOT next packet ok
  THEN quittieren (-wiederhole);
       LEAVE ziel erreicht
  FI; 
  last data := strom.
 
check for valid pagenr:
   IF prefix.seitennummer < dr page(strom) AND prefix.seitennummer > -1
         THEN report ("Absteigende Seitennummern, alt: " + text(drpage(strom))+
                      " neu: "+ text(prefix.seitennummer) + " Seq.nr: " +
                       text(vx.sequenz) ) ;
              flush buffers;
              quittieren (- von vorne);
              LEAVE ziel erreicht;
   FI.

datenpacket:
  INT VAR nl := used length;
  INT VAR pps1 := packets per page minus 1;
  IF sendung wartet auf zustellung THEN auffrischen ELSE daten holen FI.

sendung wartet auf zustellung: typ (strom) = zustellung.

auffrischen: zeit (strom) := 200; daten entfernen (nl).

daten holen:
  IF opti.sequenz >= prefix.sequenz AND opti.sequenz < prefix.sequenz+100
     AND prefix.sequenz >= 0
  THEN 
    IF opti.sequenz <> prefix.sequenz
    THEN empfangsreport ("Sequenzreset von "+text(opti.sequenz)+" auf "+
                  text (prefix.sequenz));
         vx.sequenz := prefix.sequenz;
         IF pagenumber ok
            THEN dr page (strom) := prefix.seitennummer
            ELSE empfangsreport ("Blocknummer falsch, neu: "+
                 text (prefix.seitennummer) + ", alt : " +
                 text (drpage(strom)) );
         FI;
         vorabquittung regenerieren 
    FI;
    daten  ; 
    IF abschluss THEN rueckmeldung FI;
  ELSE 
    empfangsreport ("Sequenzfehler: soll "+text(vx.sequenz)+" ist "+ 
            text(prefix.sequenz));
    quittieren (-wiederhole);
    daten entfernen (nl)
  FI. 
 
pagenumber ok:
   dr page (strom) >= prefix.seitennummer .

rueckmeldung:
  snr := strom;
  q := vx.quelle;
  z := vx.ziel;
  ant := vx.sendecode;
  dr := vdr;
  LEAVE ziel erreicht.

vorabquittung regenerieren:
  IF prio (myself) < 3
  THEN
    quittieren (ok)
  FI.
 
distanz: (opti.sequenz AND  pps1 ) * nl. 
 
sequenz zaehlung: 
  vx.sequenz INCR 1. 
 
neue seite da:
  neue seite kommt.

neue seite kommt: 
(vx.sequenz AND pps1) = 0. 
 
freie verbindungsnummer: 
  INT VAR h strom :=maxstrom1, cstrom := 0;
  FOR strom FROM 1 UPTO maxstrom REP 
    IF vx.strom = 0 THEN h strom := strom ;
                         typ(strom) := send wait
    ELIF bekannter strom
    THEN empfangsreport ("Reopen"); 
         quit zaehler INCR 1;
         IF typ (strom) = zustellung THEN typ (strom) := send wait FI;
         forget (vdr);
         LEAVE freie verbindungsnummer 
    ELIF antwort auf call
    THEN
      IF loeschung vorgemerkt
      THEN 
        vx := prefix;
        loesche verbindung (strom);
        LEAVE ziel erreicht
      FI;
      cstrom := strom;
      typ (strom) := call pingpong;
      forget (vdr);
    FI
  PER; 
  IF cstrom > 0 THEN strom := cstrom ELSE strom := h strom FI; 
  IF strom = maxstrom1 THEN
    vx:=prefix;
    empfangsreport ("Verbindungsengpass");
    quittieren (-wiederhole);
    LEAVE ziel erreicht
  FI. 

antwort auf call:
  prefix.sendecode >= 0 AND
  call aufruf AND vx.quelle = prefix.ziel AND vx.ziel = prefix.quelle. 

END PROC ziel erreicht;

PROC daten entfernen (INT CONST wieviel):
    BOOL VAR dummy ;
    dummy:=blockin (workspace, 2, 0, wieviel) 
END PROC daten entfernen;

BOOL PROC route ok:
  INT VAR zwischenquelle := vorspann.zwischenziel DIV 256,
          endquelle := vorspann.quellrechner;
    zwischenquelle abgleichen;
    endquelle abgleichen;
    TRUE.

zwischenquelle abgleichen:
  IF NOT zwischenroute gleich
  THEN
    IF NOT zwischenabgleich erlaubt THEN LEAVE route ok WITH FALSE FI;
    route.port (zwischenquelle) := channel;
    route.zwischen (zwischenquelle) := zwischenquelle;
    abgleich (zwischenquelle, zwischenquelle)
  FI.

zwischenabgleich erlaubt: route.port (zwischenquelle) < 256.

endquelle abgleichen:
  IF NOT endroute gleich
  THEN
    IF NOT endabgleich erlaubt THEN LEAVE route ok WITH FALSE FI;
    route.port (endquelle) := channel;
    route.zwischen (endquelle) := zwischenquelle;
    abgleich (endquelle, zwischenquelle)
  FI.

endabgleich erlaubt: route.port (endquelle) < 256.

zwischenroute gleich:
  (route.port (zwischenquelle) AND 255) = channel
  AND
  route.zwischen (zwischenquelle) = zwischenquelle.

endroute gleich:
  (route.port (endquelle) AND 255) = channel
  AND
  route.zwischen (endquelle) = zwischenquelle.

END PROC route ok;

BOOL PROC abschluss:

  last data := -1;
  IF neue seite kommt AND vx.seiten nummer = -1 
  THEN 
    quittieren (-beende);
    an ziel weitergeben 
  ELSE
    FALSE
  FI. 
neue seite kommt: 
(vx.sequenz AND packets per page minus 1) = 0. 
 
an ziel weitergeben: 
  IF tasknummerfrage  THEN taskfrage beantworten ;pufferplatz ; FALSE
  ELIF tasknamenfrage THEN name senden ;pufferplatz ; FALSE
  ELIF taskinfofrage  THEN task info senden;pufferplatz ; FALSE
  ELIF routenfrage    THEN routen senden; pufferplatz; FALSE
  ELSE                     senden ; TRUE
  FI. 

pufferplatz : quitzaehler INCR 1 .

senden:
  IF  callaufruf
  THEN
    ein versuch  (* bei Antwort auf Call muß ein Zustellversuch reichen *)
  ELSE
    max 100 versuche; 
    typ (strom) := zustellung
  FI.

tasknummerfrage:opti.sendecode = -taskid code.
 
tasknamenfrage: opti.sendecode = -name code.
 
taskinfofrage: opti.sendecode = -task info code.

routenfrage: opti.sendecode = -routen liefern code.

max 100 versuche: zeit(strom) := 100.

ein versuch: zeit (strom) := 1.

taskfrage beantworten:
  disable stop;
  BOUND TEXT VAR tsk := vdr; 
  TEXT VAR save tsk := tsk;
  forget (vdr); vdr := nilspace; 
  BOUND TASK VAR task id := vdr;
  task id := task(save tsk);
  IF is error THEN 
    clear error; enable stop;
    forget (vdr); vdr := nilspace; 
    BOUND TEXT VAR errtxt := vdr;
    errtxt := text(own)+"/"""+save tsk+""" gibt es nicht";
    sendung starten (collector, opti.quelle, 2)
  ELSE
    enable stop;
    sendung starten (collector, opti.quelle, 0)
  FI.
 
name senden: 
  quittieren (-loesche);
  forget (vdr); vdr := nilspace;
  tsk := vdr; 
  tsk := nam (opti.ziel);
  sendung starten (collector, opti.quelle, 0). 
 
routen senden:
  forget (vdr); vdr := old ("port intern");
  sendung starten (opti.ziel, opti.quelle, 0).

task info senden:
  disable stop;
  BOUND INT VAR ti code := vdr;
  INT VAR ti cd := ti code;
  forget (vdr); vdr := nilspace;
  FILE VAR task inf := sequential file (output,vdr);
  head line (task inf,"Station "+text(own)); 
  task info (ti cd, task inf);
  IF is error
  THEN
    forget (vdr); vdr := nilspace;
    errtxt := vdr;
    errtxt := errormessage;
    clear error;
    sendung starten (collector, opti.quelle, 2)
  ELSE
    sendung starten (collector,opti.quelle,0)
  FI;
  enable stop
END PROC abschluss ;

PROC quittieren(INT CONST code) : 
  INT VAR quell := vx.quellrechner ;
  transmitted ackpacket := ACK:(stx quit, route.zwischen (quell)+own256, quell, own,
                    vx.strom, code);
  transmit header (transmitted ack space);
  blockout (transmitted ack space,1,dr verwaltungslaenge, ack laenge); 
  transmit trailer;
END PROC quittieren; 

BOOL PROC next packet ok:
  buffer first := next packet start;
  buffer first = "" COR normal packet start.

normal packet start:
  IF buffer first = stx
  THEN
    TRUE
  ELSE
    buffer first := ""; flush buffers; FALSE
  FI.

END PROC next packet ok;
END PACKET basic net;