// // 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 word setDNS word resolveIP word setCallback word setParam 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 word saveidx byte regidx byte regdata // // 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 // // 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 // 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 // // 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) 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 wiz->channel_proto = WIZ_PROTO_CLOSED pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE return 0 fin fin // // 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 // 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 // // 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 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 // // 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 pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE wiz->channel_proto = WIZ_PROTO_CLOSED return 0 fin fin // // Invalid port // return -1 end // // Update notify callback // def wizSetCallback(wiz, callback) if wiz->channel_proto == WIZ_PROTO_UDP or wiz->channel_proto == WIZ_PROTO_TCP // // Update callback on this port // wiz=>channel_recv_func = callback return 0 fin // // Invalid port // return -1 end // // Update notify param // def wizSetParam(wiz, param) if wiz->channel_proto == WIZ_PROTO_UDP or wiz->channel_proto == WIZ_PROTO_TCP // // Update param on this port // wiz=>channel_recv_parm = param return 0 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 wiz = @wizChannel for i = 0 to 3 // // Socket activity // if ir & (1 << i) wizregs = wiz=>channel_regs wizdata = wiz=>channel_rxmem sir = peekreg(wizregs + WIZ_SnIR) pokereg(wiz=>channel_regs + WIZ_SnIR, sir) // Clear SnIR when wiz->channel_proto is WIZ_PROTO_UDP if sir & $04 // // 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 splitlen = WIZ_RXSIZE - rxwr peekregs(wizdata + rxwr, rxpkt, splitlen) peekregs(wizdata, rxpkt + splitlen, rxlen - splitlen) else peekregs(wizdata + rxwr, rxpkt, rxlen) fin 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 is WIZ_PROTO_TCP if sir & $01 // // 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 wend fin if sir & $04 // // Receive TCP packet // if wiz->channel_state == TCP_STATE_OPEN rxlen = peekregw(wizregs + WIZ_SnRSR) rxrr = peekregw(wizregs + WIZ_SnRXRD) rxwr = rxrr & WIZ_RXMASK rxpkt = heapalloc(rxlen) if rxwr + rxlen > WIZ_RXSIZE splitlen = WIZ_RXSIZE - rxwr peekregs(wizdata + rxwr, rxpkt, splitlen) peekregs(wizdata, rxpkt + splitlen, rxlen - splitlen) else peekregs(wizdata + rxwr, rxpkt, rxlen) fin 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 fin if sir & $02 // // Close TCP socket // if wiz->channel_state == TCP_STATE_OPEN // Notify callback w/ len = 0 wiz=>channel_recv_func(@wiz=>channel_remip,wiz=>channel_remport,0,wiz=>channel_lclport,0,wiz=>channel_recv_parm) fin wiz->channel_state = TCP_STATE_CLOSED pokereg(wiz=>channel_regs + WIZ_SnCR, $10) // CLOSE fin if sir & $08 // // 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 wend fin wend fin wiz = wiz + t_channel next if ir & $80 // // IP conflict // pokereg(WIZ_IR, $80) fin if ir & $40 // // Destination unreachable // pokereg(WIZ_IR, $40) fin if ir & $20 // // PPOE connection close // pokereg(WIZ_IR, $20) 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 = $90 to $F0 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 // // Print settings // puts("Found Uthernet II in slot #") putc('0' + ((slot - $80) >> 4)) putln // // 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 iNet:setCallback = @wizSetCallback iNet:setParam = @wizSetParam return modkeep fin fin pokeio(slot, regdata) // Restore register fin next // // Not found // return -1 done