MODULE Enet; (* Ethernet fuer das MCB167-NET *) (* s. c't 10/12 S.188 *) CONST CS_seg = $10; (* Segment des CS8900 durch ADDRSEL2 festgekegt *) (* Flags im Ethernet_status: *) RX_int = 1; TX_int = 2; ARP_req = 4; ARP_rep = 8; ECHO_req = 16; TX_ok = 32; TX_busy = 64; TCP_rec = 128; page = TABLE OF WORD = 21576, 20564, 12591, 12334, 12832, 12336, 20256, 3403, 17162, 28271, 25972, 29806, 21549, 28793, 14949, 29728, 30821, 12148, 29800, 27757, 8251, 26723, 29281, 25971, 15732, 29813, 11622, 3384, 3338, 15370, 29800, 27757, 3390, 15370, 25960, 25697, 3390, 15370, 29811, 27769, 8293, 31092, 25968, 8765, 25972, 29816, 25391, 29555, 15906, 2573, 12648, 31520, 28515, 28524, 14962, 27746, 25973, 32059, 2573, 8304, 25467, 27759, 29295, 26426, 25970, 28261, 32059, 2573, 12092, 29811, 27769, 15973, 2573, 12092, 25960, 25697, 3390, 15370, 28514, 31076, 3390, 3338, 3338, 15370, 12648, 19774, 16963, 13873, 11575, 17742, 15444, 26671, 15921, 2573, 2573, 2573, 2573, 28732, 20286, 25954, 28530, 17262, 13873, 11640, 25943, 29538, 29285, 25974, 8306, 29545, 29216, 28277, 26990, 26478, 8238, 12092, 15984, 2573, 2573, 26684, 15922, 18464, 30305, 8293, 30054, 11374, 18208, 26997, 28516, 15392, 26671, 15922, 2573, 2573, 12092, 28514, 31076, 3390, 3338, 15370, 26671, 28020, 15980, 2573; TYPE events = RECORD Rx, Tx: WORD; END; TXbuf = ARRAY 256 OF WORD; RXbuf = ARRAY 128 OF WORD; Host = RECORD MAC1, MAC2, MAC3, IPh, IPl, ID, len: WORD; END; TCPt = RECORD MAC1, MAC2, MAC3, IPh, IPl, ID, myPort, Sent: WORD; Port, SeqH, SeqL, AcknH, AcknL, Flag, Tick, Status: WORD; END; VAR Http: TCPt; obuf: TXbuf; ibuf: RXbuf; qu, me: Host; RoundCount: ARRAY 32 OF WORD; RoundNr: WORD; TCPhead: ARRAY 10 OF WORD; Status: WORD; i: WORD; Event: events; RecLen: WORD; ticks: WORD; leds: WORD; PROCEDURE* ISR[$1F]; (* P2.15 - Interrupt *) VAR ISQ, temp, d1, d2, d3, d4, rounds, seg: WORD; BEGIN rounds := 0; seg := CS_seg; REPEAT temp := GET($0120, seg);; (* ISQ-Adresse *) ISQ := temp; temp := temp ANDB $3F; IF (temp = 4) & ((Status ANDB TX_busy) = 0) THEN (* RxEvent *) temp := ISQ; IF (temp ANDB $0100) # 0 THEN (* RxOK *) Status := Status ORB RX_int; Event.Rx := ISQ; temp := ISQ; IF (temp ANDB $0800) # 0 THEN (* broadcast *) d1 := GET($0410, seg); IF d1 = $0608 THEN (* ARP-Request geswappt*) (* lade Ziel-IP *) d1 := GET($042A, seg); d2 := GET($042C, seg); IF (d1 = me.IPh) & (d2 = me.IPl) THEN (* ich bins! *) Status := Status ORB ARP_req;(* ich sollte antworten *) d1 := GET($041A, seg); (* qmac1 *) d2 := GET($041C, seg); (* qmac2 *) d3 := GET($041E, seg); (* qmac3 *) qu.MAC1 := d1; qu.MAC2 := d2; qu.MAC3 := d3; (* lade Quell-IP *) d1 := GET($0420, seg); d2 := GET($0422, seg); qu.IPh := d1; qu.IPl := d2; END; END; ELSE (* not broadcast *) temp := ISQ; IF (temp ANDB $0400) # 0 THEN (* an unsere IA *) d1 := GET($0410, seg); (* lade Typ *) IF d1 = $0008 THEN (* IPv4 *) d1 := GET($041A, seg); (* TTL/Proto *) d2 := GET($0412, seg); (* Vers/HL... *) d3 := GET($0414, seg); SWAP(d3); (* Paketlaenge *) d4 := GET($0416, seg); (* ID *) qu.ID := d4; qu.len := d3; d1 := d1 DIV 256; (* separiere Protokoll *) IF d1 = 1 THEN (* ICMP *) d2 := d2 MOD 16; d2 := d2 * 4; (* IHL in Bytes *) d4 := d2; (* Headerlaenge merken *) d2 := d2 + $0412; d2 := GET(d2, seg); (* Typ *) d2 := d2 MOD 256; IF d2 = 8 THEN (* Echo request *) Status := Status ORB ECHO_req; d3 := d3 - d4; (* d3 := Paketlänge - IPheader *) d3 := d3 - 4; (* ICMPheader auch noch weg *) RecLen := d3; (* Empfangslaenge merken *) d1 := GET($040A, seg); (* qmac1 *) d2 := GET($040C, seg); (* qmac2 *) d3 := GET($040E, seg); (* qmac3 *) qu.MAC1 := d1; qu.MAC2 := d2; qu.MAC3 := d3; (* Lade Quell-IP *) d1 := GET($041E, seg); (* d_IPH *) d2 := GET($0420, seg); (* d_IPL *) qu.IPh := d1; qu.IPl := d2; d3 := RecLen; d3 := d3 DIV 2; d2 := $042A; (* ICMP-Quench *) FOR temp := 1 TO d3 DO (* Daten speichern in ibuf *) d4 := GET(d2, seg); (* Data[d2] *) ibuf[temp] := d4; d2 := d2 + 2; END; END; (* Echo request *) ELSE (* not ICMP *) IF d1 = 6 THEN (* TCP *) Status := Status ORB TCP_rec; (* merken *) (* lade Quell-MAC *) d1 := GET($40A, seg); (* qmac1 *) d2 := GET($40C, seg); (* qmac2 *) d3 := GET($40E, seg); (* qmac3 *) qu.MAC1 := d1; qu.MAC2 := d2; qu.MAC3 := d3; (* lade Quell-IP *) d1 := GET($41E, seg); (* d_iph *) d2 := GET($420, seg); (* qmacl *) qu.IPh := d1; qu.IPl := d2; d3 := $0426; (* kopiere TCP-Header *) FOR d2 := 1 TO 10 DO d1 := GET(d3, seg); SWAP(d1); d3 := d3 + 2; TCPhead[d2] := d1; END; FOR d2 := 1 TO 8 DO (* 16 Bytes Daten kopieren, d3 passt noch *) d1 := GET(d3, seg); SWAP(d1); d3 := d3 + 2; ibuf[d2] := d1; END; END; (* TCP *) END; (* ICMP or not *) END;(* not IPv4 *) END; (* Broadcast or not *) END;(* RxOK *) END; (* RxEvent *) ELSIF temp = 8 THEN (* TxEvent *) Status := Status ORB TX_int; Event.Tx := ISQ; temp := ISQ; IF (temp ANDB $0100) # 0 THEN Status := Status ORB TX_ok; Status := Status ANDB ($FFFF - TX_busy); END; (*ELSIF temp = 12 THEN Status := Status ORB Buf_int; Event. ELSIF temp = 16 THEN Status := Status ORB Miss_int; ELSIF temp = 18 THEN Status := Status ORB Coll_int; *) END; rounds := rounds + 1; UNTIL ISQ = 0; d1 := RoundNr; RoundCount[d1] := rounds; IF d1 = 32 THEN d1 := 1 ELSE d1 := d1 + 1; RoundNr := d1; END; END ISR; PROCEDURE* ISR[T3INT]; (* Timer3-Interrupt *) BEGIN IF ticks = $FFFF THEN ticks := 0 ELSE ticks := ticks + 1; END; END ISR; PROCEDURE* InitTimer3; (* Alle 100 ms einen Timerinterrupt *) BEGIN (* T3 von T2 reloaded, Clk = 24 MHz, *) T2CON := $27; (* Reloadmodus *) T2 := 18750; (* Relaodwert *) (* T3 als downcounting Timer mit Prescaler = 128. 128 * 18750 * 41,667 ns = 0,1 s *) T3CON := $02C4; (* T3 als Timer, gestartet *); (* T3-Interrupt mit Prioritaet 01/0 freigeben *) T3IC := 68; END InitTimer3; (* PROCEDURE* InitASC0; CONST Clock = 24000; Baudrate = 38400; VAR i: WORD; BEGIN i := DP3; DP3 := i ORB 1024; (* P3.10 = TxD, Ausgang *) i := P3; P3 := i ORB 1024; S0CON := $8011; (* 8N1 *) i := Baudrate DIV 100; i := Clock DIV i; i := i * 5; i := i DIV 16; i := i - 1; S0BG := i; (* Baudrate gesetzt *) S0RIC := 0; S0TIC := 0; (* keine Interrupts *) END InitASC0; *) PROCEDURE* Error(code:WORD); BEGIN P2 := code; WHILE code # 0 DO SLEEP; END; END Error; PROCEDURE* InitCS8900; VAR j, k, seg: WORD; BEGIN (* Der CS8900A ist im I/O-Mode *) seg := CS_seg; PUT($030A, seg, $0114); (* PPP := SelfCtl *) PUT($030C, seg, $0055); (* PPA, Reset Chip *) REPEAT (* warten auf Initende *) PUT($030A, seg, $0136); (* PPP := SelfST *) j := GET($030C, seg); (*ReadPort(j); *) UNTIL (j ANDB $0080) # 0; PUT($030A, seg, $0000); (* PPP := ProdId*) j := GET($030C, seg); (* ProdId *) IF j # $630E THEN i := j; Error(1); END; (* Speicherzugriff auf den CS8900 enablen: *) PUT($030A, seg, $802C); (* PPP := Memotxbaseaddr. *) FOR j := 1 TO 2 DO PUT($030C, seg, 0); END; (* sollte eh so sein *) PUT($030A, seg, $0116); (* PPP := BusCntl *) j := GET($030C, seg); j := j ORB $0400; PUT($030C, seg, j); (* MemoryE := 1 *) (* Initialisierungssequenz: MAC-Adresse *) PUT($0158, seg, me.MAC1); PUT($015A, seg, me.MAC2); PUT($015C, seg, me.MAC3); (* SerRxON und SerTxON setzen: *) j := GET($0112, seg); j := j ORB $08C0; PUT($0112, seg, j); PUT($0102, seg, $0103); (* enable RX_OK-Int. *) PUT($0104, seg, $0D05); (* Rx-Akzeptanz setzen *) PUT($0106, seg, $8FC7); (* TxCfg, alle Ints enablen *) PUT($0022, seg, 0); (* Interrupt 0 verwenden *) j := GET($0116, seg); j := j ORB $8000; PUT($0116, seg, j); (* Interrupt enablen *) j := GET(0, seg); IF j # $630E THEN Error(2); END; (* P2.15 für Interrupt einrichten: *) j := DP2; j := j ANDB $7FFF; DP2 := j; CCM3 := $1000; (* pos. Flanke an P2.15 löst Int. aus *) CC15IC := $0044; (* Int. mit Level 1 Grp. 0 enabled *) Status := 0; END InitCS8900; PROCEDURE* SendFrame(len:WORD); VAR adr, dat, i, j, seg: WORD; BEGIN seg := CS_seg; PUT($0144, seg, $00C9); (* TxCMD, add PAD and CRC *) PUT($0146, seg, len); (* TxLength *) REPEAT j := GET($0138, seg); (* BusST *) UNTIL (j ANDB $0100) # 0; len := len DIV 2; adr := $0A00; FOR i := 1 TO len DO PUT(adr, seg, obuf[i]); adr := adr + 2; END; Status := Status ANDB ($FFFF - TX_ok); END SendFrame; PROCEDURE* checksum(VAR start:WORD; ende:WORD); VAR i, carry, sum, temp, j: WORD; BEGIN i := start; sum := 0; carry := 0; WHILE i <= ende DO temp := $FFFF - sum; j := obuf[i]; SWAP(j); IF temp <= j THEN carry := carry + 1; sum := j - temp - 1; ELSE sum := sum + j; END; i:= i + 1; END; temp := $FFFF - sum; IF temp < carry THEN sum := carry - temp - 1; ELSE sum := sum + carry; END; SWAP(sum); sum := sum XORB $FFFF; start := sum; END checksum; (* Ethernetheader in obuf schreiben: 14 Bytes *) PROCEDURE* AddEther(typ: WORD); (* typ geswappt! *) BEGIN (* q_MAC *) obuf[1] := qu.MAC1; obuf[2] := qu.MAC2; obuf[3] := qu.MAC3; (* MyMAC *) obuf[4] := me.MAC1; obuf[5] := me.MAC2; obuf[6] := me.MAC3; obuf[7] := typ; END AddEther; (* IPheader in obuf schreiben, 20 Bytes ab Index 8 *) PROCEDURE* AddIP(ende, proto:WORD); VAR i, l: WORD; BEGIN obuf[8] := $0045; l := ende - 7; l := l * 2; (* Paketlaenge *) i := l; i := i DIV 256; l := l MOD 256; l := (l * 256) + i; obuf[9] := l; (* Paketlaenge *) obuf[10] := qu.ID; obuf[11] := 0; (* Frags *) i := proto; i := (i * 256) + $80; obuf[12] := i; (* Proto und TTL *) obuf[13] := 0; (* Checksumme *) obuf[14] := me.IPh; obuf[15] := me.IPl; obuf[16] := qu.IPh; obuf[17] := qu.IPl; i := 8; checksum(i,17); obuf[13] := i; END AddIP; (* TCPheader in obuf schreiben, 20 Bytes ab Index 18 Ende als Arrayindex *) PROCEDURE* AddTCP(Flags, ende: WORD); VAR i, j: WORD; BEGIN obuf[18] := Http.myPort; obuf[19] := Http.Port; obuf[20] := Http.AcknH; obuf[21] := Http.AcknL; obuf[22] := Http.SeqH; obuf[23] := Http.SeqL; obuf[24] := 5*4096 + Flags; (* Headersize = 5 + 32 Bit *) obuf[25] := $0001; (* Windowsize = Size of ibuf *) (* Pseudoheader, myIP erstmal 0 *) obuf[12] := 0; obuf[13] := 0; obuf[16] := 6; i := ende; i := i - 17; i := i * 2; obuf[17] := i; FOR i := 11 TO 24 DO SWAP(obuf[i]); END; obuf[12] := me.IPh; obuf[13] := me.IPl; obuf[14] := Http.IPh; obuf[15] := Http.IPl; obuf[26] := 0; obuf[27] := 0; i := 12; checksum(i, ende); obuf[26] := i; qu.ID := Http.ID; qu.IPh := Http.IPh; qu.IPl := Http.IPl; qu.MAC1 := Http.MAC1; qu.MAC2 := Http.MAC2; qu.MAC3 := Http.MAC3; AddIP(ende, 6); AddEther($0008); END AddTCP; (* Extraprozedur, da die Daten nicht aus Http übernommen werden *) PROCEDURE* TCPreset; VAR i: WORD; BEGIN obuf[18] := TCPhead[2]; obuf[19] := TCPhead[1]; obuf[20] := 0; obuf[21] := 0; obuf[22] := TCPhead[3]; obuf[23] := TCPhead[4]; obuf[24] := $5004; (* 4 = Reset *) obuf[25] := $0001; FOR i := 17 TO 24 DO SWAP(obuf[i]); END; (* Pseudoheader, Werte schon geswappt *) obuf[12] := me.IPh; obuf[13] := me.IPl; obuf[14] := qu.IPh; obuf[15] := qu.IPl; obuf[16] := $0600; (* Proto *) obuf[17] := $1400; obuf[26] := 0; obuf[27] := 0; i := 12; checksum(i,27); obuf[26] := i; AddIP(27, 6); AddEther($0008); SendFrame(54); END TCPreset; (* TCP-Daten in Http kopieren *) PROCEDURE* CopyTCP; VAR t: WORD; BEGIN Http.myPort := 80; Http.Port := TCPhead[1]; Http.MAC1 := qu.MAC1; Http.MAC2 := qu.MAC2; Http.MAC3 := qu.MAC3; Http.IPh := qu.IPh; Http.IPl := qu.IPl; Http.ID := qu.ID; Http.SeqH := TCPhead[3]; Http.SeqL := TCPhead[4]; Http.AcknH := TCPhead[5]; Http.AcknL := TCPhead[6]; Http.Flag := TCPhead[7]; Http.Flag := Http.Flag ANDB 63; Http.Tick := 0; Http.Sent := 0; (* nix gesendet *) END CopyTCP; PROCEDURE* AcknTCP(Flag: WORD); VAR i, l:WORD; BEGIN IF Flag = 0 THEN l := qu.len - 40; ELSE l := 1; END; Flag := Flag + 16; i := $FFFF - l; IF i >= Http.SeqL THEN Http.SeqL := Http.SeqL + l; ELSE Http.SeqH := Http.SeqH + 1; Http.SeqL := Http.SeqL - i - 1; END; AddTCP(Flag, 27); SendFrame(54); END AcknTCP; (* Kopiere die WebPage in obuf ab Index 28, lasti gibt das Ende zurück *) PROCEDURE* CopyPage(VAR lasti: WORD); VAR l, i: WORD; BEGIN l := LEN(page); FOR i := 1 TO l DO obuf[27 + i] := page[i]; END; i := i + 26; lasti := i; END CopyPage; PROCEDURE* ARPrep; BEGIN obuf[8] := $0100; (* HWtyp *) obuf[9] := $0008; (*IP_Frame *) obuf[10] := $0406; (* PLEN/HLEN *) obuf[11] := $0200; (* reply *) obuf[12] := me.MAC1; obuf[13] := me.MAC2; obuf[14] := me.MAC3; obuf[15] := me.IPh; obuf[16] := me.IPl; obuf[17] := qu.MAC1; obuf[18] := qu.MAC2; obuf[19] := qu.MAC3; obuf[20] := qu.IPh; obuf[21] := qu.IPl; (* d_IP *) AddEther($0608); SendFrame(42); END ARPrep; PROCEDURE* ECHOrep; VAR i, w, e, t: WORD; BEGIN obuf[18] := 0; (* Typ/code *) obuf[19] := 0; (* Checksumme *) e := RecLen; e := e DIV 2; w := 20; FOR i := 1 TO e DO obuf[w] := ibuf[i]; w := w + 1; END; w := w - 1; i := 18; checksum(i, w); obuf[19] := i; AddIP(w, 1); AddEther($0008); w := w * 2; SendFrame(w); END ECHOrep; (* was: 1 .. Empfang, 2 .. Timeout *) PROCEDURE* ServHTTP(was: WORD); VAR Flags, l, k: WORD; BEGIN Flags := TCPhead[7]; IF (Http.Status = 1) THEN (* listening *) CopyTCP; Http.AcknH := 0; Http.AcknL := 1000; Http.ID := Http.ID + 1; AcknTCP(2); Http.Status := 2; ELSIF Http.Status = 2 THEN (* Syn acknowledged *) IF (Flags ANDB 63) = 16 THEN Http.AcknH := TCPhead[5]; Http.AcknL := TCPhead[6]; Http.Status := 3; ELSE TCPreset; END; ELSIF Http.Status = 3 THEN (* established *) (* GET ? *) l := ibuf[1]; k := ibuf[2]; IF (l = $4745) & (k = $5420) THEN CopyTCP; Http.ID := Http.ID + 1; AcknTCP(0); Http.Tick := 4; Http.Status := 4; ELSE TCPreset; Http.Status := 1; END; ELSIF Http.Status = 4 THEN (* Folgeseite? *) IF was = 1 THEN (* Folgeseite! *) CopyTCP; Http.ID := Http.ID + 1; AcknTCP(0); Http.Tick := 2; ELSE CopyPage(l); AddTCP(16, l); l := l * 2; Http.Status := 5; Http.Tick := 21; SendFrame(l); Http.Sent := l - 54; END; ELSIF Http.Status = 5 THEN (* page sent *) IF was = 1 THEN Http.Tick := 0; Http.Sent := 0; Http.Status := 6; (* Acknowledge-Länge prüfen: TODO *) CopyTCP; AddTCP(17,27); SendFrame(54); ELSE (* Timeout! *) CopyPage(l); AddTCP(16, l); l := l * 2; SendFrame(l); Http.Tick := 21; Http.Sent := l - 54; END; ELSIF Http.Status = 6 THEN (* Fin sent *) IF (Flags ANDB 1) = 1 THEN CopyTCP; qu.len := 41; AcknTCP(0); Http.Status := 8; Http.Tick := 11; ELSE Http.Status := 7; Http.Tick := 21; END; ELSIF Http.Status = 7 THEN (* wait Fin *) IF was = 2 THEN TCPreset; Http.Status := 8; Http.Tick := 21; ELSE IF (Flags ANDB 1) = 1 THEN CopyTCP; qu.len := 41; AcknTCP(0); Http.Status := 8; Http.Tick := 11; END; END; ELSIF Http.Status = 8 THEN (* wait *) IF was = 1 THEN IF (Flags ANDB 1) = 1 THEN CopyTCP; qu.len := 41; AcknTCP(0); Http.Tick := 11; END; ELSE Http.Status := 1; Http.Tick := 0; END; END; END ServHTTP; PROCEDURE* ServTCP; VAR Port, Flags: WORD; BEGIN Port := TCPhead[2]; Flags := TCPhead[7]; IF Port = 80 THEN IF Http.Status = 1 THEN IF (Flags ANDB 2) = 2 THEN ServHTTP(1); ELSE TCPreset; END; ELSIF (qu.IPh = Http.IPh) & (qu.IPl = Http.IPl) THEN IF (Flags ANDB 4) = 4 THEN (* reset *) Http.Status := 1; ELSE ServHTTP(1); END; ELSE TCPreset; Http.Status := 1; END; ELSE (* Port # 80 *) TCPreset; END; END ServTCP; PROCEDURE* Service; VAR j: WORD; BEGIN IF (Status ANDB RX_int) # 0 THEN IF (Status ANDB ARP_req) # 0 THEN Status := Status ORB TX_busy; ARPrep; Status := Status ANDB ($FFFF - RX_int - ARP_req - TX_busy); ELSIF (Status ANDB ECHO_req) # 0 THEN Status := Status ORB TX_busy; ECHOrep; Status := Status ANDB ($FFFF - RX_int - ECHO_req - TX_busy); ELSIF (Status ANDB TCP_rec) # 0 THEN Status := Status ORB TX_busy; ServTCP; Status := Status ANDB ($FFFF - RX_int - TCP_rec - TX_busy); ELSE Status := Status ANDB ($FFFF - RX_int); (* bei Broadcast an andere *) END; ELSIF (Status ANDB TX_int) # 0 THEN IF (Status ANDB TX_ok) # 0 THEN Status := Status ANDB ($FFFF - TX_int); END; END; END Service; BEGIN DP2 := 255; leds := 1; P2 := 255; (* P2.0 ..7 Outputs fuer die LEDs *) FOR RoundNr := 1 TO 32 DO RoundCount[RoundNr] := 0; END; RoundNr := 1; (* IP-Adresse, Bytes geswappt: *) me.IPh := 168 * 256 + 192; me.IPl := 44 * 256 + 13; (* MAC geswappt *) me.MAC1 := $0201; me.MAC2 := $0403; me.MAC3 := $0605; InitCS8900; INTSEN; i := 0; P2 := 255 - leds; ticks := 0; InitTimer3; (*InitASC0;*) Http.Status := 1; Http.Tick := 0; REPEAT SLEEP; IF Status # TX_ok THEN Service; END; IF Http.Tick # 0 THEN Http.Tick := Http.Tick - 1; IF Http.Tick = 1 THEN ServHTTP(2); END; END; leds := Http.Status MOD 256; P2 := 255 - leds; UNTIL 1 > 2; REPEAT SLEEP UNTIL 1 > 2; END Enet.