diff --git a/src/libsrc/wiznet.pla b/src/libsrc/wiznet.pla new file mode 100644 index 0000000..a35f9fa --- /dev/null +++ b/src/libsrc/wiznet.pla @@ -0,0 +1,721 @@ +// +// 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 +// +// 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 serviceIP +// +// 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 +// +// HW channels +// +struc t_channel + byte channel_proto + word channel_regs + word channel_txmem + word channel_rxmem + word channel_lclport + word channel_remport + word 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 +// +export asm swab + LDA ESTKL,X + LDY ESTKH,X + STA ESTKH,X + STY ESTKL,X + RTS +end +// +// 1'S COMPLIMENT SUM BE format +// sum1(PREVSUM, BUF, LEN) +// +export asm sum1 + LDY #$00 + LDA ESTKL+1,X + STA SRCL + LDA ESTKH+1,X + STA SRCH + LSR ESTKH,X ; CONVERT BYTE LEN TO WORD LEN + LDA ESTKL,X + ROR + ADC #$00 + STA ESTKL,X + BEQ + + INC ESTKH,X + BCC CHKLP + INC ESTKH,X + CLC +CHKLP LDA (SRC),Y + PHA + INY + BNE + + INC SRCH ++ LDA (SRC),Y + ADC ESTKH+2,X + STA ESTKH+2,X + PLA + ADC ESTKL+2,X + STA ESTKL+2,X + INY + BNE + + INC SRCH ++ DEC ESTKL,X + BNE CHKLP + DEC ESTKH,X + BNE CHKLP +- LDA #$00 + ADC ESTKH+2,X + STA ESTKH+2,X + LDA #$00 + ADC ESTKL+2,X + STA ESTKL+2,X + BCS - + INX + INX + 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) + 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) + word i + + len = len - 1 + for i = 0 to len + _pokeiow(reg + i) + buf->[i] = _peekio() + next +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() + 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 +// +export def sendUDP(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 + txrr = peekregw(wizregs + WIZ_SnTXRD) + txwr = peekregw(wizregs + WIZ_SnTXWR) + until txrr == txwr + // + // Set destination address/port + // + pokeregs(wizregs + WIZ_SnDIPR, ipdst, IP4ADR_SIZE) + pokeregw(wizregs + WIZ_SnDPORT, portdst) + // + // Calc new write ptr, check for split + // + txwr = txrr + len + if txwr >= WIZ_TXSIZE + splitlen = WIZ_TXSIZE - txrr + pokeregs(wizdata + txrr, data, splitlen) + pokeregs(wizdata, data + splitlen, len - splitlen) + else + pokeregs(wizdata + txrr, data, len) + fin + pokeregw(wizregs + WIZ_SnTXWR, txwr) + pokereg(wizregs + WIZ_SnCR, $20) // SEND +end +// +// Open UDP channel and set datagram received callback +// +export def openUDP(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 = IP_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 +// +export def closeUDP(wiz) + if isuge(wiz, wizChannel) and isult(wiz, wizChannel + MAX_WIZ_CHANNELS * t_channel) + // + // Clear notiications on this port + // + if wiz->channel_proto == IP_PROTO_UDP + wiz->channel_proto = 0 + pokereg(wiz=>channel_regs + WIZ_SnCR, $00) // CLOSE + return 0 + fin + fin + // + // Invalid port + // + return -1 +end +// +// Service incoming packets +// +export def serviceIP + 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 IP_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 + rxlen + rxpkt = heapalloc(rxlen) + if rxwr >= WIZ_RXSIZE + putc('!') + splitlen = WIZ_RXSIZE - rxrr + peekregs(wizdata + rxrr, rxpkt, splitlen) + peekregs(wizdata, rxpkt + splitlen, rxlen - splitlen) + else + peekregs(wizdata + rxrr, 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, rxwr) + 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 IP_PROTO_TCP + 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 +// +export def setInterfaceIP(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 +// +export def getInterfaceHA(ha) + if ha + ha=>0 = wizMAC:0; ha=>2 = wizMAC:2; ha=>4 = wizMAC:4 + fin + return MAC_SIZE +end +// +// Identify Wiznet 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 + // + // 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 + return modkeep + fin + fin + pokeio(slot, regdata) // Restore register + fin +next +// +// Not found +// +return -1 +done