From 40b3f8194c2cf2d566801c671c187ae3723db6f6 Mon Sep 17 00:00:00 2001 From: dschmenk <dschmenk@sbcglobal.net> Date: Mon, 2 Nov 2015 11:58:26 -0800 Subject: [PATCH] Working Uthernet2 driver --- HTTPD.PO | Bin 0 -> 143360 bytes src/libsrc/inet.pla | 2 +- src/libsrc/uthernet2.pla | 962 +++++++++++++++++++++++++++++++++++++++ src/libsrc/wiznet.pla | 18 +- src/makefile | 11 +- 5 files changed, 978 insertions(+), 15 deletions(-) create mode 100644 HTTPD.PO create mode 100644 src/libsrc/uthernet2.pla diff --git a/HTTPD.PO b/HTTPD.PO new file mode 100644 index 0000000000000000000000000000000000000000..315e107146cb33dbd59c137aec58e768afc301d0 GIT binary patch literal 143360 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%0AEZ-wCs5EFgXd$j+!5j$jVdAxbuM#vu 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 literal 0 HcmV?d00001 diff --git a/src/libsrc/inet.pla b/src/libsrc/inet.pla index 73aa6b2..b9ca313 100644 --- a/src/libsrc/inet.pla +++ b/src/libsrc/inet.pla @@ -45,7 +45,7 @@ export byte[t_inet] iNet // // List of loadable network device drivers // -byte netDrivers = "WIZNET" +byte netDrivers = "UTHERNET2" byte = "UTHERNET" byte = "" word driver = @netDrivers diff --git a/src/libsrc/uthernet2.pla b/src/libsrc/uthernet2.pla new file mode 100644 index 0000000..2400f04 --- /dev/null +++ b/src/libsrc/uthernet2.pla @@ -0,0 +1,962 @@ +// +// Uthernet II Wiznet 5100 based ethernet card +// +// TCP/IP is built into hardware, so no dependencies on the software +// layers, like the Uthernet +// +import cmdsys + predef syscall, call, getc, gets, putc, puts, putln + predef isugt, isuge, isult, isule + predef memset, memcpy, modaddr, modexec + predef heapmark, heapallocalign, heapalloc, heaprelease, heapavail + byte MACHID +end +// +// Net object +// +import inet + word iNet +end +struc t_inet + word initIP + word serviceIP + word openUDP + word sendUDP + word closeUDP + word listenTCP + word connectTCP + word sendTCP + word closeTCP + word setInterfaceIP + word getInterfaceHA +end +// +// Module don't free memory +// +const modkeep = $2000 +const modinitkeep = $4000 +// +// Wiznet registers +// +const WIZ_MR = $00 +const WIZ_GWR = $01 +const WIZ_SUBR = $05 +const WIZ_SHAR = $09 +const WIZ_SIPR = $0F +const WIZ_IR = $15 +const WIZ_IMR = $16 +const WIZ_RTR = $17 +const WIZ_RCR = $19 +const WIZ_RMSR = $1A +const WIZ_TMSR = $1B +const WIZ_PATR = $1C +const WIZ_PTMR = $28 +const WIZ_PMGC = $29 +const WIZ_UIPR = $2A +const WIZ_UPRT = $2E +// +// Wiznet socket registers +// +const WIZ_SREGS = $0400 +const WIZ_SSIZE = $0100 +const WIZ_SOCK0 = $0400 +const WIZ_SOCK1 = $0500 +const WIZ_SOCK2 = $0600 +const WIZ_SOCK3 = $0700 +const WIZ_SnMR = $00 +const WIZ_SnCR = $01 +const WIZ_SnIR = $02 +const WIZ_SnSR = $03 +const WIZ_SnPORT = $04 +const WIZ_SnDHAR = $06 +const WIZ_SnDIPR = $0C +const WIZ_SnDPORT = $10 +const WIZ_SnMSSR = $12 +const WIZ_SnPROTO = $14 +const WIZ_SnTOS = $15 +const WIZ_SnTTL = $16 +const WIZ_SnFSR = $20 +const WIZ_SnTXRD = $22 +const WIZ_SnTXWR = $24 +const WIZ_SnRSR = $26 +const WIZ_SnRXRD = $28 +// +// Wiznet socket data +// +const WIZ_TXMEM = $4000 +const WIZ_TXSIZE = $0800 +const WIZ_TXMASK = WIZ_TXSIZE-1 +const WIZ_TXMEM0 = $4000 +const WIZ_TXMEM1 = $4800 +const WIZ_TXMEM2 = $5000 +const WIZ_TXMEM3 = $5800 +const WIZ_RXMEM = $6000 +const WIZ_RXSIZE = $0800 +const WIZ_RXMASK = WIZ_RXSIZE-1 +const WIZ_RXMEM0 = $6000 +const WIZ_RXMEM1 = $6800 +const WIZ_RXMEM2 = $7000 +const WIZ_RXMEM3 = $7800 +// +// Wiznet indirect registers +// +byte slot, regidx, regdata +word saveidx +// +// Wiznet MAC address +// +byte[6] wizMAC = $00,$0A,$99,$1E,$02,$B0 +// +// Wiznet IP addresses +// +byte[4] localip +byte[4] subnet +byte[4] gateway +// +// Predefine service routine +// +predef wizServiceIP +// +// Segment list element +// +struc t_segment + word seg_buf + word seg_len +end +// +// Max Ethernet frame size +// +const MAX_FRAME_SIZE = 1518 +const MAC_BROADCAST = $FFFF +const MAC_SIZE = 6 +// +// Ethernet header +// +struc t_ethrhdr + byte[MAC_SIZE] ethr_dst + byte[MAC_SIZE] ethr_src + word ethr_payload +end +const PAYLOAD_IP = $0008 // BE format +const PAYLOAD_ARP = $0608 // BE format +// +// IP datagram header +// +const IP4ADR_SIZE = 4 +struc t_iphdr + byte ip_vers_hlen + byte ip_service + word ip_length + word ip_id + word ip_flags_fragofst + byte ip_ttl + byte ip_proto + word ip_chksm + byte[IP4ADR_SIZE] ip_src + byte[IP4ADR_SIZE] ip_dst + byte[] ip_options +end +const IP_BROADCAST = $FFFF +const IP_PROTO_ICMP = $01 +const IP_PROTO_UDP = $11 +const IP_PROTO_TCP = $06 +word bcast = IP_BROADCAST, IP_BROADCAST +// +// ICMP type/codes +// +const IP_PROTO_ICMP = 1 +const ICMP_ECHO_REQST = 8 +const ICMP_ECHO_REPLY = 0 +// +// ICMP message format +// +struc t_icmp + byte icmp_type + byte icmp_code + word icmp_chksm + word[2] icmp_header +end +// +// UDP IPv4 psuedo header +// +struc t_piphdr + byte[IP4ADR_SIZE] pip_src + byte[IP4ADR_SIZE] pip_dst + byte pip_zero + byte pip_proto + word pip_len +end +// +// UDP header +// +struc t_udphdr + word udp_src + word udp_dst + word udp_len + word udp_chksm +end +// +// TCP header +// +struc t_tcphdr + word tcp_src + word tcp_dst + word tcp_len + word tcp_chksm +end +// +// Local network parameters +// +const MAX_WIZ_CHANNELS = 4 +// +// Channel protocols +// +const WIZ_PROTO_CLOSED = 0 +const WIZ_PROTO_TCP = 1 +const WIZ_PROTO_UDP = 2 +const WIZ_PROTO_IP = 3 +const WIZ_PROTO_RAW = 4 +// +// State transistions +// +const TCP_STATE_CLOSED = 0 +const TCP_STATE_CLOSING = 1 +const TCP_STATE_LISTEN = 2 +const TCP_STATE_CONNECT = 3 +const TCP_STATE_OPEN = 4 +// +// HW channels +// +struc t_channel + byte channel_proto + byte channel_state + word channel_regs + word channel_txmem + word channel_rxmem + word channel_lclport + word channel_remport + byte[4] channel_remip + word channel_recv_func + word channel_recv_parm +end +byte[t_channel * MAX_WIZ_CHANNELS] wizChannel +// +// Service ICMP hook +// +export word hookICMP +// +// Defines for ASM routines +// +asm equates + !SOURCE "vmsrc/plvmzp.inc" +end +// +// Swap bytes in word +// +asm swab + LDA ESTKL,X + LDY ESTKH,X + STA ESTKH,X + STY ESTKL,X + RTS +end +// +// Wiznet I/O functions +// +// POKE WORD TO I/O SPACE +// Note: Big Endian format +// +asm _pokeiow + LDA ESTKH,X +end +asm _pokeiowl + STA $C000 + LDA ESTKL,X +end +asm _pokeiowh + STA $C000 + RTS +end +// +// POKE BYTE TO I/O SPACE +// +asm _pokeio + LDA ESTKL,X +end +asm _pokeiol + STA $C000 + RTS +end +// +// PEEK BYTE FROM I/O SPACE +// +asm _peekio + DEX +end +asm _peekiol + LDA $C000 + STA ESTKL,X + LDA #$00 + STA ESTKH,X + RTS +end +// +// PEEK WORD FROM I/O SPACE +// Note: Big Endian format +// +asm _peekiow + DEX +end +asm _peekiowl + LDA $C000 + STA ESTKH,X +end +asm _peekiowh + LDA $C000 + STA ESTKL,X + RTS +end +// +// WRITE DATA INTO I/O SPACE +// pokedata(BUF, LEN) +// +asm pokedata + LDA ESTKL+1,X + STA SRCL + LDA ESTKH+1,X + STA SRCH + LDY ESTKL,X + BEQ POKELP + LDY #$00 + INC ESTKH,X +POKELP LDA (SRC),Y +end +asm _pokedata + STA $C000 + INY + BNE + + INC SRCH ++ DEC ESTKL,X + BNE POKELP + DEC ESTKH,X + BNE POKELP + INX + RTS +end +// +// READ DATA FROM I/O SPACE +// peekdata(BUF, LEN) +// +asm peekdata + LDA ESTKL+1,X + STA DSTL + LDA ESTKH+1,X + STA DSTH + LDY ESTKL,X + BEQ PEEKLP + LDY #$00 + INC ESTKH,X +end +asm _peekdata +PEEKLP LDA $C000 + STA (DST),Y + INY + BNE + + INC DSTH ++ DEC ESTKL,X + BNE PEEKLP + DEC ESTKH,X + BNE PEEKLP + INX + RTS +end +def pokeiow(io, data) + _pokeiowl.1 = io + _pokeiowh.1 = io+1 + return _pokeiow(data) +end +def pokeio(io, data) + _pokeiol.1 = io + return _pokeio(data) +end +def peekio(io) + _peekiol.1 = io + return _peekio() +end +def peekiow(io) + _peekiowl.1 = io + _peekiowh.1 = io+1 + return _peekiow() +end +def pokereg(reg, data) + _pokeiow(reg) + return _pokeio(data) +end +def peekreg(reg) + _pokeiow(reg) + return _peekio() +end +def pokeregs(reg, buf, len) + _pokeiow(reg) + return pokedata(buf, len) +end +def peekregs(reg, buf, len) + _pokeiow(reg) + return peekdata(buf, len) +end +def pokeregw(reg, dataw) + _pokeiow(reg) + _pokeio(dataw.1) + return _pokeio(dataw.0) +end +def peekregw(reg) + word dataw + + _pokeiow(reg) + dataw.1 = _peekio() + _pokeiow(reg + 1) + dataw.0 = _peekio() + return dataw +end +// +// DEBUG +// +def putln + return putc($0D) +end +def putb(hexb) + return call($FDDA, hexb, 0, 0, 0) +end +def puth(hex) + return call($F941, hex >> 8, hex, 0, 0) +end +def puti(i) + if i < 0; putc('-'); i = -i; fin + if i < 10 + putc(i + '0') + else + puti(i / 10) + putc(i % 10 + '0') + fin +end +def putip(ipptr) + byte i + + for i = 0 to 2 + puti(ipptr->[i]); putc('.') + next + return puti(ipptr->[i]) +end +// +// Send UDP datagram +// +def wizSendUDP(wiz, ipdst, portdst, data, len) + word wizregs, wizdata, txrr, txwr, splitlen + + wizregs = wiz=>channel_regs + wizdata = wiz=>channel_txmem + if !ipdst + ipdst = @bcast + fin + // + // Wait for Tx room + // + repeat; until peekregw(wizregs + WIZ_SnFSR) >= len + // + // Calc new write ptr, check for split + // + txwr = peekregw(wizregs + WIZ_SnTXWR) + txrr = txwr & WIZ_TXMASK + if txrr + len > WIZ_TXSIZE + splitlen = WIZ_TXSIZE - txrr + pokeregs(wizdata + txrr, data, splitlen) + pokeregs(wizdata, data + splitlen, len - splitlen) + else + pokeregs(wizdata + txrr, data, len) + fin + // + // Set destination address/port + // + pokeregs(wizregs + WIZ_SnDIPR, ipdst, IP4ADR_SIZE) + pokeregw(wizregs + WIZ_SnDPORT, portdst) + // + // Update write pointer and send + // + pokeregw(wizregs + WIZ_SnTXWR, txwr + len) + pokereg(wizregs + WIZ_SnCR, $20) // SEND +end +// +// Open UDP channel and set datagram received callback +// +def wizOpenUDP(localport, callback, param) + word wiz + byte i + + if !localport; return -1; fin // invalid port + // + // Look for an existing notification on localport + // + //putc('O') + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if wiz->channel_proto == IP_PROTO_UDP and wiz=>channel_lclport == localport + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + // + // Add notification on localport if room + // + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if !wiz->channel_proto + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + return 0 + fin + fin + //putc('0' + i);putln + // + // Fill in this channel and open it + // + wiz->channel_proto = WIZ_PROTO_UDP + wiz=>channel_lclport = localport + wiz=>channel_recv_func = callback + wiz=>channel_recv_parm = param + pokeregw(wiz=>channel_regs + WIZ_SnPORT, localport) + pokereg(wiz=>channel_regs + WIZ_SnMR, $02) // UDP protocol + pokereg(wiz=>channel_regs + WIZ_SnCR, $01) // OPEN + return wiz +end +// +// Close UDP port +// +def wizCloseUDP(wiz) + //putc('S') + if isuge(wiz, @wizChannel) and isult(wiz, @wizChannel + MAX_WIZ_CHANNELS * t_channel) + // + // Clear notiications on this port + // + if wiz->channel_proto == WIZ_PROTO_UDP + //putc('1' + ((wiz=>channel_regs - WIZ_SREGS) >> 8));putln + wiz->channel_proto = WIZ_PROTO_CLOSED + pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE + return 0 + fin + fin + //putc('!');putln + // + // Invalid port + // + return -1 +end +// +// Open TCP socket in SERVER mode +// +def wizListenTCP(lclport, callback, param) + word wiz + byte i + + // + // Look for an existing notification on localport + // + //putc('L') + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if wiz->channel_proto == WIZ_PROTO_TCP and wiz->channel_state == TCP_STATE_LISTEN and wiz=>channel_lclport == lclport + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + // + // Add notification on localport if room + // + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if !wiz->channel_proto + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + return 0 + fin + fin + //putc('0' + i);putln + // + // Fill in this channel and open it + // + wiz->channel_proto = WIZ_PROTO_TCP + wiz->channel_state = TCP_STATE_LISTEN + wiz=>channel_remip:0 = 0 + wiz=>channel_remip:2 = 0 + wiz=>channel_remport = 0 + wiz=>channel_lclport = lclport + wiz=>channel_recv_func = callback + wiz=>channel_recv_parm = param + pokereg(wiz=>channel_regs + WIZ_SnMR, $01) // TCP protocol + pokeregw(wiz=>channel_regs + WIZ_SnPORT, lclport) + pokereg(wiz=>channel_regs + WIZ_SnCR, $01) // OPEN + while peekreg(wiz=>channel_regs + WIZ_SnSR) <> $13; loop // Wait for init + pokereg(wiz=>channel_regs + WIZ_SnCR, $02) // LISTEN + return wiz +end +// +// Open TCP socket in CLIENT mode +// +def wizConnectTCP(remip, remport, lclport, callback, param) + word wiz + byte i + + // + // Look for an existing notification on localport + // + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if wiz->channel_proto == WIZ_PROTO_TCP and wiz->channel_state == TCP_STATE_CONNECT and wiz=>channel_lclport == lclport + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + // + // Add notification on localport if room + // + wiz = @wizChannel + for i = 1 to MAX_WIZ_CHANNELS + if !wiz->channel_proto + break + fin + wiz = wiz + t_channel + next + if i > MAX_WIZ_CHANNELS + return 0 + fin + fin + // + // Fill in this channel and open it + // + wiz->channel_proto = WIZ_PROTO_TCP + wiz->channel_state = TCP_STATE_CONNECT + wiz=>channel_remip:0 = remip=>0 + wiz=>channel_remip:2 = remip=>2 + wiz=>channel_remport = remport + wiz=>channel_lclport = lclport + wiz=>channel_recv_func = callback + wiz=>channel_recv_parm = param + pokereg(wiz=>channel_regs + WIZ_SnMR, $01) // TCP protocol + pokeregs(wiz=>channel_regs + WIZ_SnDIPR, remip, IP4ADR_SIZE) + pokeregw(wiz=>channel_regs + WIZ_SnDPORT, remport) + pokeregw(wiz=>channel_regs + WIZ_SnPORT, lclport) + pokereg(wiz=>channel_regs + WIZ_SnCR, $01) // OPEN + while peekreg(wiz=>channel_regs + WIZ_SnSR) <> $13; loop // Wait for init + pokereg(wiz=>channel_regs + WIZ_SnCR, $04) // CONNECT + return wiz +end +// +// Write to TCP socket +// +def wizSendTCP(wiz, data, len) + word wizregs, wizdata, txrr, txwr, splitlen + + if wiz->channel_state <> TCP_STATE_OPEN; return -1; fin + //putc('W');puti(len);putc(':') + wizregs = wiz=>channel_regs + wizdata = wiz=>channel_txmem + // + // Wait for Tx room + // + repeat; until peekregw(wizregs + WIZ_SnFSR) >= len + // + // Calc new write ptr, check for split + // + txwr = peekregw(wizregs + WIZ_SnTXWR) + txrr = txwr & WIZ_TXMASK + if txrr + len > WIZ_TXSIZE + splitlen = WIZ_TXSIZE - txrr + pokeregs(wizdata + txrr, data, splitlen) + pokeregs(wizdata, data + splitlen, len - splitlen) + //putc('(');puti(splitlen);putc(',');puti(len-splitlen);putc(')') + else + pokeregs(wizdata + txrr, data, len) + fin + //puth(txrr);putc('-');putc('>');puth(txwr+len);putln + // + // Update write pointer and send + // + pokeregw(wizregs + WIZ_SnTXWR, txwr + len) + pokereg(wizregs + WIZ_SnCR, $20) // SEND +end +// +// Close TCP socket +// +def wizCloseTCP(wiz) + if isuge(wiz, @wizChannel) and isult(wiz, @wizChannel + MAX_WIZ_CHANNELS * t_channel) + // + // Clear notiications on this port + // + if wiz->channel_proto == WIZ_PROTO_TCP and (wiz->channel_state == TCP_STATE_OPEN or wiz->channel_state == TCP_STATE_CLOSING) + wiz->channel_state = TCP_STATE_CLOSING + pokereg(wiz=>channel_regs + WIZ_SnCR, $08) // DISCON + repeat + wizServiceIP() + until wiz->channel_state == TCP_STATE_CLOSED + wiz->channel_proto = WIZ_PROTO_CLOSED + return 0 + fin + fin + // + // Invalid port + // + return -1 +end +// +// Service incoming packets +// +def wizServiceIP + word wiz, wizregs, wizdata, rxlen, rxrr, rxwr, rxpkt, splitlen + byte ir, i, sir + + ir = peekreg(WIZ_IR) + if ir and ir <> $FF // Ignore spurious read of IR + //putc('I');putb(ir) + wiz = @wizChannel + for i = 0 to 3 + when ir & (1 << i) + is 1 + is 2 + is 4 + is 8 + wizregs = wiz=>channel_regs + wizdata = wiz=>channel_rxmem + sir = peekreg(wizregs + WIZ_SnIR) + when wiz->channel_proto + is WIZ_PROTO_TCP + if sir & $01 + //putc('C') + // + // Connect TCP socket + // + when wiz->channel_state + is TCP_STATE_LISTEN + peekregs(wiz=>channel_regs + WIZ_SnDIPR, @wiz=>channel_remip, IP4ADR_SIZE) + wiz=>channel_remport = peekregw(wiz=>channel_regs + WIZ_SnDPORT) + is TCP_STATE_CONNECT + wiz->channel_state = TCP_STATE_OPEN + break + otherwise + //putc('?') + wend + fin + if sir & $04 + //putc('R') + // + // Receive TCP packet + // + rxlen = peekregw(wizregs + WIZ_SnRSR) + rxrr = peekregw(wizregs + WIZ_SnRXRD) + rxwr = rxrr & WIZ_RXMASK + rxpkt = heapalloc(rxlen) + //puti(rxlen);putc(':') + if rxwr + rxlen > WIZ_RXSIZE + splitlen = WIZ_RXSIZE - rxwr + peekregs(wizdata + rxwr, rxpkt, splitlen) + peekregs(wizdata, rxpkt + splitlen, rxlen - splitlen) + //putc('(');puti(splitlen);putc(',');puti(rxlen-splitlen);putc(')') + else + peekregs(wizdata + rxwr, rxpkt, rxlen) + fin + //puth(rxwr);putc('-');putc('>');puth(rxwr+rxlen);putln + pokeregw(wizregs + WIZ_SnRXRD, rxrr + rxlen) + pokereg(wizregs + WIZ_SnCR, $40) // RECV + wiz=>channel_recv_func(@wiz=>channel_remip,wiz=>channel_remport,wiz=>channel_lclport,rxpkt,rxlen,wiz=>channel_recv_parm) + heaprelease(rxpkt) + fin + if sir & $02 + //putc('S') + // + // Close TCP socket + // + when wiz->channel_state + is TCP_STATE_OPEN + wiz->channel_state = TCP_STATE_CLOSING + wiz=>channel_recv_func(@wiz=>channel_remip,wiz=>channel_remport,0,wiz=>channel_lclport,0,wiz=>channel_recv_parm) + break + is TCP_STATE_CLOSING + wiz->channel_state = TCP_STATE_CLOSED + pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE + break + otherwise + //putc('?') + wend + fin + if sir & $08 + //putc('T') + // + // Timeout on TCP socket + // + when wiz->channel_state + is TCP_STATE_OPEN + wiz->channel_state = TCP_STATE_CLOSING + wiz=>channel_recv_func(@wiz=>channel_remip,wiz=>channel_remport,wiz=>channel_lclport,0,0,wiz=>channel_recv_parm) + break + is TCP_STATE_CONNECT + wiz=>channel_recv_func(@wiz=>channel_remip,wiz=>channel_remport,wiz=>channel_lclport,0,0,wiz=>channel_recv_parm) + is TCP_STATE_CLOSING + wiz->channel_state = TCP_STATE_CLOSED + pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE + break + otherwise + //putc('?') + wend + fin + //if sir & $10 + //putc('W');putc('O');putc('K');puth(peekregw(wiz=>channel_regs+WIZ_SnTXWR));putln + // + // Write TCP socket OK + // + //fin + break + is WIZ_PROTO_UDP + //putc('U');putb(sir) + if sir & $04 + //putc('R') + // + // Receive UDP packet + // + rxlen = peekregw(wizregs + WIZ_SnRSR) + rxrr = peekregw(wizregs + WIZ_SnRXRD) + rxwr = rxrr & WIZ_RXMASK + rxpkt = heapalloc(rxlen) + if rxwr + rxlen >= WIZ_RXSIZE + //putc('!') + splitlen = WIZ_RXSIZE - rxwr + peekregs(wizdata + rxwr, rxpkt, splitlen) + peekregs(wizdata, rxpkt + splitlen, rxlen - splitlen) + else + peekregs(wizdata + rxwr, rxpkt, rxlen) + fin + //putc('=');putip(rxpkt);putc(' ');puti(rxlen) + //putc('/');puti(swab(rxpkt=>6)) + //putc(' ');puth(rxrr);putc(' ');puth(rxwr);putln + pokeregw(wizregs + WIZ_SnRXRD, rxrr + rxlen) + pokereg(wizregs + WIZ_SnCR, $40) // RECV + wiz=>channel_recv_func(rxpkt,swab(rxpkt=>4),rxpkt+8,rxlen-8,wiz=>channel_recv_parm) + heaprelease(rxpkt) + fin + break + otherwise + wend + pokereg(wiz=>channel_regs + WIZ_SnIR, sir) // Clear SnIR + ir = ir ^ (1 << i) + break + wend + wiz = wiz + t_channel + next + if ir + // + // Clear IR for now + // + pokereg(WIZ_IR, ir) + fin + fin +end +// +// Set the local IP addresses +// +def setWizIP(newIP, newSubnet, newGateway) + if newIP + localip:0 = newIP=>0; localip:2 = newIP=>2 + pokeregs(WIZ_SIPR, newIP, IP4ADR_SIZE) + fin + if newSubnet + subnet:0 = newSubnet=>0; subnet:2 = newSubnet=>2 + pokeregs(WIZ_SUBR, newSubnet, IP4ADR_SIZE) + fin + if newGateway + gateway:0 = newGateway=>0; gateway:2 = newGateway=>2 + pokeregs(WIZ_GWR, newGateway, IP4ADR_SIZE) + fin +end +// +// Get the interface hardware address +// +def getWizHA(ha) + if ha + ha=>0 = wizMAC:0; ha=>2 = wizMAC:2; ha=>4 = wizMAC:4 + fin + return MAC_SIZE +end +// +// Identify Uthernet II card and initialize +// +for slot = $F0 downto $90 step $10 + regdata = peekio(slot) + if (regdata & $E4) == $00 + pokeio(slot, $03) // Try setting auto-increment indirect I/F + if peekio(slot) == $03 + saveidx = peekiow(slot + 1) + peekio(slot + 3) // Dummy read to data register should increment index + if peekiow(slot + 1) == saveidx + 1 + // + // Good chance this is it + // + pokeio(slot, $80) // RESET + regidx = slot + 1 + regdata = slot + 3 + _pokedata.1 = regdata + _peekdata.1 = regdata + pokeio(slot, $03) // Auto-increment indirect I/F + enable ping + // + // The following looks redundant, but it sets up the peek/poke locations + // for peekreg(s)/pokereg(s) + // + pokeiow(regidx, WIZ_MR) + pokeio(regdata, $03) // Auto-increment indirect I/F + enable ping + peekio(regdata) + // + // Initialize common registers + // + pokeregs(WIZ_SHAR, @wizMAC, 6) // MAC addr + pokeregw(WIZ_RTR, 5000) // Timeout period to 500ms + pokereg(WIZ_RMSR, $55) // 2K Rx memory/channel + pokereg(WIZ_TMSR, $55) // 2K Tx memory/channel + // + // Fill channel structure + // + saveidx = @wizChannel + for slot = 0 to 3 + saveidx=>channel_regs = WIZ_SREGS + (WIZ_SSIZE * slot) + saveidx=>channel_txmem = WIZ_TXMEM + (WIZ_TXSIZE * slot) + saveidx=>channel_rxmem = WIZ_RXMEM + (WIZ_RXSIZE * slot) + saveidx = saveidx + t_channel + next + // + // Fill in Net class + // + iNet:serviceIP = @wizServiceIP + iNet:openUDP = @wizOpenUDP + iNet:sendUDP = @wizSendUDP + iNet:closeUDP = @wizCloseUDP + iNet:listenTCP = @wizListenTCP + iNet:connectTCP = @wizConnectTCP + iNet:sendTCP = @wizSendTCP + iNet:closeTCP = @wizCloseTCP + iNet:setInterfaceIP = @setWizIP + iNet:getInterfaceHA = @getWizHA + return modkeep + fin + fin + pokeio(slot, regdata) // Restore register + fin +next +// +// Not found +// +return -1 +done diff --git a/src/libsrc/wiznet.pla b/src/libsrc/wiznet.pla index c24b611..8ef71ad 100644 --- a/src/libsrc/wiznet.pla +++ b/src/libsrc/wiznet.pla @@ -397,20 +397,20 @@ def peekreg(reg) return _peekio() end def pokeregs(reg, buf, len) - word i - - len = len - 1 - for i = 0 to len - _pokeiow(reg + i) - _pokeio(buf->[i]) - next + _pokeiow(reg) + return pokedata(buf, len) +// word i +// len = len - 1 +// for i = 0 to len +// _pokeiow(reg + i) +// _pokeio(buf->[i]) +// next end def peekregs(reg, buf, len) -// There is an issue missing data on back-to-back reads _pokeiow(reg) return peekdata(buf, len) +// There is an issue missing data on back-to-back reads // word i - // len = len - 1 // for i = 0 to len // _pokeiow(reg + i) diff --git a/src/makefile b/src/makefile index 9b71f6c..2be09e3 100644 --- a/src/makefile +++ b/src/makefile @@ -10,6 +10,7 @@ SB = SB\#FF2000 ROD = ROD\#FE1000 SIEVE = SIEVE\#FE1000 WIZNET = WIZNET\#FE1000 +UTHERNET2= UTHERNET2\#FE1000 UTHERNET= UTHERNET\#FE1000 ETHERIP = ETHERIP\#FE1000 INET = INET\#FE1000 @@ -48,7 +49,7 @@ TXTTYPE = .TXT #SYSTYPE = \#FF2000 #TXTTYPE = \#040000 -all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03) $(CMD) $(MEMMGR) $(MEMTEST) $(FIBER) $(SB) $(MON) $(ROD) $(SIEVE) $(WIZNET) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) +all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03) $(CMD) $(MEMMGR) $(MEMTEST) $(FIBER) $(SB) $(MON) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) clean: -rm *FE1000 *FF2000 $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03) @@ -129,14 +130,14 @@ $(SIEVE): samplesrc/sieve.pla $(PLVM02) $(PLASM) ./$(PLASM) -AM < samplesrc/sieve.pla > samplesrc/sieve.a acme --setpc 4094 -o $(SIEVE) samplesrc/sieve.a -$(WIZNET): libsrc/wiznet.pla $(PLVM02) $(PLASM) - ./$(PLASM) -AM < libsrc/wiznet.pla > libsrc/wiznet.a - acme --setpc 4094 -o $(WIZNET) libsrc/wiznet.a - $(UTHERNET): libsrc/uthernet.pla $(PLVM02) $(PLASM) ./$(PLASM) -AM < libsrc/uthernet.pla > libsrc/uthernet.a acme --setpc 4094 -o $(UTHERNET) libsrc/uthernet.a +$(UTHERNET2): libsrc/uthernet2.pla $(PLVM02) $(PLASM) + ./$(PLASM) -AM < libsrc/uthernet2.pla > libsrc/uthernet2.a + acme --setpc 4094 -o $(UTHERNET2) libsrc/uthernet2.a + $(ETHERIP): libsrc/etherip.pla $(PLVM02) $(PLASM) ./$(PLASM) -AM < libsrc/etherip.pla > libsrc/etherip.a acme --setpc 4094 -o $(ETHERIP) libsrc/etherip.a