1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2024-07-05 04:28:57 +00:00

Uthernet IP/ICMP/UDP/DHCP drivers

This commit is contained in:
dschmenk 2015-01-27 21:24:29 -08:00
parent 90c1a459d7
commit e261d14467
6 changed files with 881 additions and 194 deletions

View File

@ -11,6 +11,9 @@ ROD = ROD\#FE1000
SIEVE = SIEVE\#FE1000
UTHERNET= UTHERNET\#FE1000
ETHERIP = ETHERIP\#FE1000
ICMP = ICMP\#FE1000
DHCP = DHCP\#FE1000
STATICIP= STATICIP\#FE1000
ROGUE = ROGUE\#FE1000
ROGUEIO = ROGUEIO\#FE1000
ROGUEMAP= ROGUEMAP\#FE1000
@ -43,7 +46,7 @@ TXTTYPE = .TXT
#SYSTYPE = \#FF2000
#TXTTYPE = \#040000
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03) $(CMD) $(MEMMGR) $(MEMTEST) $(SB) $(MON) $(ROD) $(SIEVE) $(UTHERNET) $(ETHERIP) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1)
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03) $(CMD) $(MEMMGR) $(MEMTEST) $(SB) $(MON) $(ROD) $(SIEVE) $(UTHERNET) $(ETHERIP) $(ICMP) $(DHCP) $(STATICIP) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1)
clean:
-rm *FE1000 *FF2000 $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03)
@ -128,6 +131,18 @@ $(ETHERIP): samplesrc/etherip.pla $(PLVM02) $(PLASM)
./$(PLASM) -AM < samplesrc/etherip.pla > samplesrc/etherip.a
acme --setpc 4094 -o $(ETHERIP) samplesrc/etherip.a
$(ICMP): samplesrc/icmp.pla $(PLVM02) $(PLASM)
./$(PLASM) -AM < samplesrc/icmp.pla > samplesrc/icmp.a
acme --setpc 4094 -o $(ICMP) samplesrc/icmp.a
$(DHCP): samplesrc/dhcp.pla $(PLVM02) $(PLASM)
./$(PLASM) -AM < samplesrc/dhcp.pla > samplesrc/dhcp.a
acme --setpc 4094 -o $(DHCP) samplesrc/dhcp.a
$(STATICIP): samplesrc/staticip.pla $(PLVM02) $(PLASM)
./$(PLASM) -AM < samplesrc/staticip.pla > samplesrc/staticip.a
acme --setpc 4094 -o $(STATICIP) samplesrc/staticip.a
$(ROGUE): samplesrc/rogue.pla $(PLVM02) $(PLASM)
./$(PLASM) -AM < samplesrc/rogue.pla > samplesrc/rogue.a
acme --setpc 4094 -o $(ROGUE) samplesrc/rogue.a

210
src/samplesrc/dhcp.pla Normal file
View File

@ -0,0 +1,210 @@
//
// DHCP
//
import stdlib
predef syscall, call, memset, getc, gets, putc, puts, putln
predef memset, memcpy, modaddr, modexec
predef heapmark, heapallocalign, heapalloc, heaprelease
byte MACHID
end
import etherip
byte localha, localip, subnet_mask, gateway
predef swab, sendUDP, recvUDP, shutUDP, serviceIP
end
import icmp
end
//
// Needed to init subnet_mask
//
const IP_BROADCAST = $FFFF
//
// DHCP message
//
struc t_dhcp
byte dhcp_op
byte dhcp_htype
byte dhcp_hlen
byte dhcp_hops
byte[4] dhcp_xid
word dhcp_secs
word dhcp_flags
byte[4] dhcp_clientip
byte[4] dhcp_yourip
byte[4] dhcp_serverip
byte[4] dhcp_gatewayip
byte[16] dhcp_clientha
byte[192] dhcp_bootp
byte[4] dhcp_magic
byte[60] dhcp_opts
end
//
// DHCP messages
//
const DHCP_DISCOVER = $01
const DHCP_OFFER = $02
const DHCP_REQUEST = $03
const DHCP_DECLINE = $04
const DHCP_ACK = $05
const DHCP_NACK = $06
const DHCP_RELEASE = $07
//
// DHCP ports
//
const DHCP_CLIENT_PORT = 68
const DHCP_SERVER_PORT = 67
//
// Pre-configured DHCP packet
//
byte DHCP = $01 // OP = DISCOVER
byte = $01 // HTYPE = ETHERNET
byte = $06 // HLEN = 6
byte = $00 // HOPS
byte[4] = $01,$02,$03,$04 // TRANSACTION ID
word = $0000 // SECONDS
word = 0 // FLAGS
byte[4] = 0 // CLIENT IP
byte[4] = 0 // YOUR IP
byte[4] = 0 // SERVER IP
byte[4] = 0 // GATEWAY IP
byte[16] = 0 // CLIENT HA
byte[64] = 0 // SERVER HOST NAME
byte[128] = 0 // BOOT FILE NAME
byte[4] = $63,$82,$53,$63 // MAGIC
byte optsDHCP = 53,1,1 // DISCOVER
byte = 12, "AppleII"
byte = 55,4 ,1,28,3,42
byte = 255
byte optsREQ = 53,1,3 // REQUEST
byte = 50,4,0,0,0,0 // IP requested
byte = 54,4,0,0,0,0 // DHCP server
byte = 255
//
// DEBUG
//
byte offerstr = "DHCP server offering IP address "
byte ackstr = "DHCP acknowledge\n"
byte boundstr = "Apple II bound to:\n"
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
def dumpbytes(buf, len)
word i
for i = 0 to len - 1
putb(buf->[i])
if i & 15 == 15
putln
else
putc(' ')
fin
next
end
def dumpdhcp(pkt)
putb(pkt->dhcp_op);putln
putb(pkt->dhcp_htype);putln
putb(pkt->dhcp_hlen);putln
putb(pkt->dhcp_hops);putln
dumpbytes(@pkt->dhcp_xid, 4);putln
putip(@pkt->dhcp_clientip);putln
putip(@pkt->dhcp_yourip);putln
putip(@pkt->dhcp_serverip);putln
putip(@pkt->dhcp_gatewayip);putln
dumpbytes(@pkt->dhcp_opts, 32);putln
end
def parseopts(opts, match)
byte i
i = 0
while opts->[i] <> $FF and i < 64
while !opts->[i] and i < 64
i = i + 1
loop
if opts->[i] == match
return i
fin
i = i + opts->[i + 1] + 2
loop
return -1
end
def servdhcp(remip, remport, lclport, pkt, len, param)
word optofst
//putip(remip);putc(':');puti(remport);putln
if pkt=>dhcp_xid:0 == $0201 and pkt=>dhcp_xid:2 = $0403
when pkt->dhcp_opts.[parseopts(@pkt->dhcp_opts, 53) + 2]
is DHCP_OFFER
//puts(@offerstr); putip(@pkt->dhcp_yourip); putln
//
// Copy offer parameters to request
//
memcpy(@optsDHCP, @optsREQ, 16)
memcpy(@optsDHCP.5, @pkt->dhcp_yourip, 4)
memcpy(@optsDHCP.11, @pkt->dhcp_serverip, 4)
memcpy(@DHCP.dhcp_serverip, @pkt->dhcp_serverip, 4)
sendUDP(0, DHCP_SERVER_PORT, DHCP_CLIENT_PORT, @DHCP, t_dhcp)
break
is DHCP_ACK
//puts(@ackstr)
//
// Copy parameters to working copy
//
memcpy(@localip, @pkt->dhcp_yourip, 4)
memcpy(@subnet_mask, @pkt->dhcp_opts.[parseopts(@pkt->dhcp_opts, 1) + 2], 4)
puts(@boundstr);putip(@localip);putc('/');putip(@subnet_mask);putln
break
otherwise
dumpdhcp(pkt)
wend
fin
end
//
// Get the local hardware address into the DHCP packet
//
memcpy(@DHCP.dhcp_clientha, @localha, 6)
//
// Clear our local IP address
//
memset(@localip, 4, 0)
//
// Set subnet mask to all 1's
//
memset(@subnet_mask, 4, IP_BROADCAST)
//
// Prepare to receive DHCP packets from a server
//
recvUDP(DHCP_CLIENT_PORT, @servdhcp, 0)
//
// Broadcast DHCP DISCOVER message
//
sendUDP(0, DHCP_SERVER_PORT, DHCP_CLIENT_PORT, @DHCP, t_dhcp)
//
// Service IP
//
repeat
serviceIP
until ^$C000 > 127
^$C010
done

View File

@ -5,29 +5,36 @@ import stdlib
byte MACHID
end
import uthernet
predef MAC, writeEther, writevEther, readEther, readvEther, recvEther
predef copyMAC, writeEther, writevEther, readEther, recvEther
end
//
// Segment list element
//
struc t_segment
word seg_buf
word seg_len
end
//
// Max Ethernet frame size
//
const MAX_FRAME_SIZE = 1518
const BROADCAST_MAC = $FFFF
const MAC_BROADCAST = $FFFF
const MAC_SIZE = 6
//
// Ethernet header
//
struc t_ehdr
byte[MAC_SIZE] ehdr_dstaddr
byte[MAC_SIZE] ehdr_srcaddr
word ehdr_payload
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 IP_SIZE = 4
struc t_ip
const IPADR_SIZE = 4
struc t_iphdr
byte ip_vers_hlen
byte ip_service
word ip_length
@ -35,22 +42,15 @@ struc t_ip
word ip_flags_fragofst
byte ip_ttl
byte ip_proto
word ip_checksum
byte[IP_SIZE] ip_src
byte[IP_SIZE] ip_dst
byte[] ip_options
end
const IP_PROTO_ICMP = 1
const IP_PROTO_UDP = 2
const IP_PROTO_TCP = 3
//
// ICMP message format
//
struc t_icmp
byte icmp_type
byte icmp_code
word icmp_checksum
word ip_chksm
byte[IPADR_SIZE] ip_src
byte[IPADR_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
//
// ARP packet
//
@ -65,11 +65,11 @@ struc t_arp
byte arp_plen
word arp_op
byte[MAC_SIZE] arp_senderha
byte[IP_SIZE] arp_senderip
byte[IPADR_SIZE] arp_senderip
byte[MAC_SIZE] arp_targha
byte[IP_SIZE] arp_targip
byte[IPADR_SIZE] arp_targip
end
const t_earp = t_ehdr+t_arp
const t_earp = t_ethrhdr+t_arp
//
// Pre-configured ARP packet
//
@ -77,15 +77,64 @@ byte[] ARP
byte[MAC_SIZE] dstMAC
byte[MAC_SIZE] srcMAC
word = PAYLOAD_ARP
word = HW_ETHER // HW TYPE
word = ARP_PROTO // PROTO TYPE
byte = MAC_SIZE // HLEN
byte = IP_SIZE // PLEN
word opARP // OP
byte[MAC_SIZE] localha
byte[IP_SIZE ] localip = 192,168,123,10
byte[MAC_SIZE] remoteha
byte[IP_SIZE] remoteip = 192,168,123,1
word = HW_ETHER // HW TYPE
word = ARP_PROTO // PROTO TYPE
byte = MAC_SIZE // HLEN
byte = IPADR_SIZE // PLEN
word opARP // OP
export byte[MAC_SIZE] localha
export byte[IPADR_SIZE] localip
byte[MAC_SIZE] remoteha
byte[IPADR_SIZE] remoteip
//
// Local network parameters
//
export byte[IPADR_SIZE] subnet_mask
export byte[IPADR_SIZE] gateway
//
// UDP IPv4 psuedo header
//
struc t_piphdr
byte[IPADR_SIZE] pip_src
byte[IPADR_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
//
// Notify callbacks
//
struc t_notify
word notify_port
word notify_func
word notify_parm
end
const MAX_UDP_NOTIFIES = 16
const MAX_TCP_NOTIFIES = 8
word portsUDP
word portsTCP
//
// Service ICMP externally
//
export word serviceICMP
//
// Defines for ASM routines
//
@ -97,7 +146,7 @@ end
//
// Swap bytes in word
//
asm swab
export asm swab
LDA ESTKL,X
LDY ESTKH,X
STA ESTKH,X
@ -105,47 +154,54 @@ asm swab
RTS
end
//
// SUM BE format
// sum(BUF, LEN)
// 1'S COMPLIMENT SUM BE format
// sum1(PREVSUM, BUF, LEN)
//
export asm sum
export asm sum1
LDY #$00
LDA ESTKL+1,X
STY ESTKL+1,X
STA SRCL
LDA ESTKH+1,X
STY ESTKH+1,X
STA SRCH
LSR ESTKH,X ; CONVERT BYTE LEN TO WORD LEN
LDA ESTKL,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 CHKLP
INC ESTKH,X
CHKLP LDA (SRC),Y
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
CLC
ADC ESTKH+1,X
STA ESTKH+1,X
BNE +
INC SRCH
+ LDA (SRC),Y
ADC ESTKH+2,X
STA ESTKH+2,X
PLA
ADC ESTKL+1,X
STA ESTKL+1,X
ADC ESTKL+2,X
STA ESTKL+2,X
INY
BNE +
INC SRCH
+ DEC ESTKL,X
BNE CHKLP
DEC ESTKH,X
BNE CHKLP
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
//
end//
// DEBUG
//
def putln
@ -182,25 +238,35 @@ def putip(ipptr)
next
return puti(ipptr->[i])
end
def dumpehdr(packet)
putha(packet + ethr_dst); putc(' ')
putha(packet + ethr_src); putc(' ')
putc('$');puth(packet=>ethr_payload); putln
end
def dumparp(packet)
puth(packet=>arp_hw); putln
puth(packet=>arp_proto); putln
putb(packet->arp_hlen); putln
putb(packet->arp_plen); putln
puth(packet=>arp_op); putln
putc('$');puth(packet=>arp_hw); putln
putc('$');puth(packet=>arp_proto); putln
putc('$');putb(packet->arp_hlen); putln
putc('$');putb(packet->arp_plen); putln
putc('$');puth(packet=>arp_op); putln
putha(packet + arp_senderha)
putc('=')
putip(packet + arp_senderip)
putln
putip(packet + arp_senderip); putln
putha(packet + arp_targha)
putc('=')
putip(packet + arp_targip)
putln
putip(packet + arp_targip); putln
end
def dumpehdr(packet)
putha(packet + ehdr_dstaddr); putc(' ')
putha(packet + ehdr_srcaddr); putc(' ')
puth(packet=>ehdr_payload); putln
def dumpip(packet)
putc('$');putb(packet->ip_vers_hlen); putln
puti(swab(packet=>ip_length)); putln
puti(packet->ip_proto); putln
putip(packet + ip_src); putln
putip(packet + ip_dst); putln
end
def dumpudp(packet)
puti(swab(packet=>udp_src));putln
puti(swab(packet=>udp_dst));putln
puti(swab(packet=>udp_len));putln
end
def dumpfrm(packet, len)
word i
@ -220,93 +286,270 @@ def dumpfrm(packet, len)
putln
end
//
// Write IP datagram
//
export def writeIP(dstip, proto, packet, len)
byte[t_ip] iphdr
iphdr.ip_vers_hlen = 0
iphdr.ip_service = 0
iphdr:ip_length = len
iphdr:ip_id = 0
iphdr:ip_flags_fragofst = 0
iphdr.ip_ttl = 1
iphdr.ip_proto = proto
iphdr:ip_checksum = 0
memcpy(@iphdr.ip_src, @localip, IP_SIZE)
memcpy(@ip_dst, @dstip, IP_SIZE)
end
//
// Read IP datagram
//
export def readIP(dst, proto, packet)
end
//
// Service incoming packets
//
def serviceIP
word pkt, iphdr, ipopt, len
export def serviceIP
word pkt, iphdr, hdr, opt, len, optlen, port, lclport, remport
byte i
len = recvEther
if len
len = len - t_ehdr
when readEther(t_ehdr)=>ehdr_payload
len = len - t_ethrhdr
when readEther(t_ethrhdr)=>ethr_payload
is PAYLOAD_ARP
pkt = readEther(len)
len = 0
when pkt=>arp_op
is ARP_REPLY
//
// Fill in ARP cache
//
memcpy(@remoteha, @pkt=>arp_senderha, 10) // copy ha and ip
putha(pkt+arp_senderha);putc('=');putip(pkt+arp_senderip);putln
//putha(pkt+arp_senderha);putc('=');putip(pkt+arp_senderip);putln
break
is ARP_REQST
//
// Is this a request for me?
//
if pkt=>arp_targip:0 == localip:0 and pkt=>arp_targip:2 == localip:2
memcpy(@dstMAC, pkt=>arp_senderha, MAC_SIZE)
memcpy(@dstMAC, @pkt=>arp_senderha, MAC_SIZE)
memcpy(@remoteha, @pkt=>arp_senderha, 10) // copy ha and ip
opARP = ARP_REPLY
writeEther(@ARP, t_earp)
putha(pkt+arp_senderha);putc('=');putip(pkt+arp_senderip);putln
//putha(pkt+arp_senderha);putc('=');putip(pkt+arp_senderip);putln
fin
break
otherwise
dumparp(pkt)
//dumparp(pkt)
wend
break
is PAYLOAD_IP
len = len - t_ip
iphdr = readEther(t_ip)
if iphdr=>ip_length > t_ip
len = len - (iphdr=>ip_length - t_ip)
ipopt = readEther(iphdr=>ip_length - t_ip)
len = len - t_iphdr
iphdr = readEther(t_iphdr)
if iphdr->ip_vers_hlen <> $45
optlen = iphdr=>ip_vers_hlen
if optlen & $F0 <> $40
//
// Not IPv4, ignore
//
break
fin
optlen = (optlen & $0F) << 2
if optlen > t_iphdr
//
// Read the options and throw them on the ground!
//
opt = readEther(optlen - t_iphdr)
len = len - (optlen - t_iphdr)
fin
fin
//
// Filter valid destination address
//
if iphdr=>ip_dst:2 <> localip:2 // Yes, this is a little lazy
if (iphdr=>ip_dst:0|subnet_mask:0) & (iphdr=>ip_dst:2|subnet_mask:2) <> IP_BROADCAST
break
fin
fin
when iphdr->ip_proto
is IP_PROTO_UDP
port = portsUDP
if port
len = len - t_udphdr
hdr = readEther(t_udphdr)
lclport = swab(hdr=>udp_dst)
for i = 1 to MAX_UDP_NOTIFIES
if port=>notify_port == lclport)
//dumpip(iphdr)
//dumpudp(hdr)
port=>notify_func(@iphdr=>ip_src,swab(hdr=>udp_src),lclport,readEther(len),swab(hdr=>udp_len),port=>notify_parm)
len = 0
break
fin
port = port + t_notify
next
fin
break
is IP_PROTO_TCP
port = portsTCP
if port
len = len - t_tcphdr
hdr = readEther(t_tcphdr)
lclport = swab(hdr=>tcp_dst)
remport = swab(hdr=>tcp_src)
for i = 1 to MAX_TCP_NOTIFIES
if port=>notify_port == lclport)
//dumpip(iphdr)
//dumptcp(tcphdr)
port=>notify_func(@iphdr=>ip_src,swab(hdr=>tcp_src),lclport,readEther(len),swab(hdr=>tcp_len),port=>notify_parm)
len = 0
break
fin
port = port + t_notify
next
fin
break
is IP_PROTO_ICMP
if serviceICMP
serviceICMP(@iphdr=>ip_src, readEther(len), len)
len = 0
fin
wend
break
otherwise
pkt = readEther(len)
dumpehdr(pkt - t_ehdr)
//dumpfrm(pkt, len)
//dumpehdr(readEther(len) - t_ethrhdr)
//len = 0
wend
if len; readEther(len); fin // Drop it
fin
end
//
// Send IP datagram
//
export def sendIP(ipdst, proto, seglist, size)
byte[t_iphdr] hdr
byte[MAC_SIZE] dstha
byte retry
word timeout
hdr.ip_vers_hlen = $45
hdr.ip_service = 0
hdr:ip_length = swab(t_iphdr + size)
hdr:ip_id = 0
hdr:ip_flags_fragofst = 0 //$40 // Don't fragment
hdr.ip_ttl = 10 // Is this reasonable?
hdr.ip_proto = proto
hdr:ip_chksm = 0
memcpy(@hdr.ip_src, @localip, IPADR_SIZE)
if !ipdst // IP_BROADCAST
memset(@hdr.ip_dst, IPADR_SIZE, IP_BROADCAST)
memset(@dstha, MAC_SIZE, MAC_BROADCAST)
else
retry = 0
while ipdst=>0 <> remoteip:0 and ipdst=>2 <> remoteip:2
if retry >= 3
return -1 // ARP failed
fin
retry = retry + 1
memset(@dstMAC, MAC_SIZE, MAC_BROADCAST)
memset(@remoteha, MAC_SIZE, 0)
memcpy(@remoteip, ipdst, IPADR_SIZE)
opARP = ARP_REQST
writeEther(@ARP, t_earp)
for timeout = 1000 downto 0
serviceIP
if remoteha:0 | remoteha:2 | remoteha:4
break
fin
next
loop
memcpy(@hdr.ip_dst, ipdst, IPADR_SIZE)
memcpy(@dstha, @remoteha, MAC_SIZE)
fin
//
// Calculate checksum
//
hdr:ip_chksm = sum1(0, @hdr, t_iphdr) ^ $FFFF
//
// Fill in remaining segment list for IP header and write it out
//
seglist=>seg_buf = @hdr
seglist=>seg_len = t_iphdr
return writevEther(@dstha, PAYLOAD_IP, seglist, t_iphdr + size)
end
//
// Send UDP datagram
//
export def sendUDP(ipdst, portdst, portsrc, data, len)
word[12] seglist // list of data and header segments
//word segsum
//byte[t_piphdr] phdr
byte[t_udphdr] hdr
//memcpy(@phdr.pip_src, @localip, IPADR_SIZE)
//memcpy(@phdr.pip_dst, ipdst, IPADR_SIZE)
//phdr.pip_zero = 0
//phdr.pip_proto = IP_PROTO_UDP
//phdr.pip_len = swab(t_udphdr + len)
hdr:udp_src = swab(portsrc)
hdr:udp_dst = swab(portdst)
hdr:udp_len = swab(t_udphdr + len)
hdr:udp_chksm = 0
//segsum = sum1(0, @phdr, t_piphdr + t_udphdr)
//segsum = sum1(segsum, data, len)
//hdr:udp_chksm = segsum ^ $FFFF
//seglist:0:seg_buf reserved for IP header
//seglist:0:seg_len reserved for IP header
seglist:4:seg_buf = @hdr
seglist:4:seg_len = t_udphdr
seglist:8:seg_buf = data
seglist:8:seg_len = len
return sendIP(ipdst, IP_PROTO_UDP, @seglist, t_udphdr + len)
end
//
// Notify on UDP datagram received
//
export def recvUDP(localport, callback, param)
word port
byte i
if !localport; return -1; fin // invalid port
//
// Allocate the port notify array if needed
//
if !portsUDP
portsUDP = heapalloc(MAX_UDP_NOTIFIES * t_notify)
if !portsUDP; return -1; fin
memset(portsUDP, MAX_UDP_NOTIFIES * t_notify, 0)
fin
//
// Look for an existing notification on localport
//
port = portsUDP
for i = 1 to MAX_UDP_NOTIFIES
if port=>notify_port == localport
port=>notify_func = callback
port=>notify_parm = param
return 0
fin
next
//
// Add notification on localport if room
//
port = portsUDP
for i = 1 to MAX_UDP_NOTIFIES
if !port=>notify_port
port=>notify_port = localport
port=>notify_func = callback
port=>notify_parm = param
return 0
fin
next
return -1
end
//
// Clear notify on UDP port
//
export def shutUDP(localport)
word port
byte i
if !localport; return -1; fin // invalid port
//
// Look for an existing notification on localport
//
port = portsUDP
for i = 1 to MAX_UDP_NOTIFIES
if port=>notify_port == localport
port=>notify_port = 0
return 0
fin
next
return -1
end
//
// Fill in MAC
//
memcpy(@srcMAC, @MAC, MAC_SIZE)
memcpy(@localha, @MAC, MAC_SIZE)
//
// Start things off with an ARP request
//
memset(@dstMAC, MAC_SIZE, BROADCAST_MAC)
memset(@remoteha, MAC_SIZE, 0)
opARP = ARP_REQST
writeEther(@ARP, t_earp)
repeat
serviceIP
until ^$C000 > 127
^$C010
copyMAC(@srcMAC)
copyMAC(@localha)
done

96
src/samplesrc/icmp.pla Normal file
View File

@ -0,0 +1,96 @@
//
// ICMP service
//
import stdlib
predef syscall, call, memset, getc, gets, putc, puts, putln
predef memset, memcpy, modaddr, modexec
predef heapmark, heapallocalign, heapalloc, heaprelease
byte MACHID
end
import etherip
byte localha, localip
word serviceICMP
predef sendIP
end
//
// Segment list element
//
struc t_segment
word seg_buf
word seg_len
end
//
// 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
//
// DEBUG
//
byte icmpstr = "ICMP "
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
//
// Service ICMP packets
//
def icmp(srcip, pkt, len)
byte[4] echoip
word[8] seglist
byte[128] echo_reply
//puts(@icmpstr);putip(srcip);putc(' ')
//puti(pkt->icmp_type);putc(' ');puti(pkt->icmp_code);putln
if pkt->icmp_type == ICMP_ECHO_REQST
if len > 128
len = 128
fin
memcpy(@echo_reply, pkt, len)
echo_reply.icmp_type = ICMP_ECHO_REPLY
echo_reply:icmp_chksm = 0
//echo_reply:icmp_chksm = sum1(0, @echo_reply, len) ^ $FFFF
memcpy(@echoip, srcip, 4)
seglist:4:seg_buf = @echo_reply
seglist:4:seg_len = len
sendIP(@echoip, IP_PROTO_ICMP, @seglist, len)
fin
end
//
// Set to notify on ICMP packets
//
serviceICMP = @icmp
done

View File

@ -0,0 +1,98 @@
//
// Static IP
//
import stdlib
predef syscall, call, memset, getc, gets, putc, puts, putln
predef memset, memcpy, modaddr, modexec
predef heapmark, heapallocalign, heapalloc, heaprelease
byte MACHID
end
import etherip
byte localha, localip
predef sendUDP, recvUDP, shutUDP, sendIP, serviceIP, sum1
end
import icmp
end
//
// Segment list element
//
struc t_segment
word seg_buf
word seg_len
end
//
// ICMP type/codes
//
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
//
// ICMP echo packet
//
const IP_PROTO_ICMP = $01
byte ECHO = 8 // ECHO request
byte = 0 // code
word = 0 // checksum
byte = 0, 69, 0, 1 // rest of header
word segecho = 0, 0, @ECHO, 8
byte serverip = 192,168,123,129
byte staticip = 192,168,123,10
byte staticmask = 255,255,255,0
word echo_reply
//
// 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
//
// Set our local IP address
//
memcpy(@localip, @staticip, 4)
//
// Calculate checksum
//
ECHO:icmp_chksm = sum1(0, @ECHO, t_icmp) ^ $FFFF
//
// Send an echo to the server
//
sendIP(@serverip, IP_PROTO_ICMP, @segecho, 0)
//
// Service IP
//
repeat
serviceIP
until ^$C000 > 127
^$C010
done

View File

@ -10,12 +10,23 @@ end
const modkeep = $2000
const modinitkeep = $4000
//
// Ethernet header definition
// Segment list element
//
struc t_ehdr
byte[6] ehdr_dstaddr
byte[6] ehdr_srcaddr
word ehdr_type
struc t_segment
word seg_buf
word seg_len
end
//
// MAC size
//
const MAC_SIZE = 6
//
// Ethernet header
//
struc t_ethrhdr
byte[MAC_SIZE] ethr_dst
byte[MAC_SIZE] ethr_src
word ethr_payload
end
//
// Max Ethernet frame size
@ -24,7 +35,7 @@ const MAX_FRAME_SIZE = 1518
//
// MAC address
//
export byte MAC = $00,$0A,$99,$1E,$02,$00
byte MAC = $00,$0A,$99,$1E,$02,$00
//
// Uthernet register offsets
//
@ -39,17 +50,17 @@ const AUTO_INC = $8000
//
// Uthernet register addresses
//
byte[] slot // Init time only
byte rxdata_lo, rxdata_hi
byte txcmd
byte txlen
byte isq
byte pregidx
byte pregdata
byte[] slot // Init time only
//
// Current receive status and packet size
//
word rxstatus, rxsize // MUST be in this order!
word rxpacket, rxptr
word rxsize, rxpacket, rxptr
//
// Defines for ASM routines
//
@ -59,6 +70,16 @@ end
//
// I/O access
//
// POKE BYTE INTO I/O SPACE
// _pokeio(DATA)
//
//asm _pokeio
// LDA ESTKL,X
//end
//asm _pokeiol
// STA $C000
// RTS
//end
//
// POKE WORD INTO I/O SPACE
// _pokeiow(DATA)
@ -75,6 +96,20 @@ asm _pokeiowh
RTS
end
//
// PEEK BYTE FROM I/O SPACE
// _peekio()
//
asm _peekio
DEX
end
asm _peekiol
LDA $C000
STA ESTKL,X
LDA #$00
STA ESTKH,X
RTS
end
//
// PEEK WORD FROM I/O SPACE
// _peekiow()
//
@ -105,16 +140,16 @@ asm pokefrm
ROR
ADC #$00
STA ESTKL,X
BEQ POKELP
BEQ +
INC ESTKH,X
+ BCC POKELP
INC ESTKH,X
POKELP LDA (SRC),Y
end
asm _pokefrml
STA $C000
INY
BNE +
INC SRCH
+ LDA (SRC),Y
LDA (SRC),Y
end
asm _pokefrmh
STA $C000
@ -133,31 +168,29 @@ end
// peekfrm(BUF, LEN)
//
asm peekfrm
LDY #$00
LDA ESTKL+1,X
STA DSTL
LDA ESTKH+1,X
STA DSTH
LDY ESTKL,X
BEQ PEEKLP
LDY #$00
LSR ESTKH,X ; CONVERT BYTE LEN TO WORD LEN
LDA ESTKL,X
ROR
ADC #$00
STA ESTKL,X
BEQ +
INC ESTKH,X
+ BCC PEEKLP
INC ESTKH,X
end
asm _peekfrmh
PEEKLP LDA $C000
PHA
end
asm _peekfrml
LDA $C000
PEEKLP LDA $C000
STA (DST),Y
PLA
INY
BNE +
INC DSTH
+ DEC ESTKL,X
BNE +
DEC ESTKH,X
BEQ EXPSW
+ STA (DST),Y
end
asm _peekfrmh
+ LDA $C000
STA (DST),Y
INY
BNE +
INC DSTH
@ -168,11 +201,19 @@ asm _peekfrml
EXPSW INX
RTS
end
//def pokeio(io, data)
// _pokeiowl.1 = io
// return _pokeio(data)
//end
def pokeiow(io, data)
_pokeiowl.1 = io
_pokeiowh.1 = io+1
return _pokeiow(data)
end
def peekio(io)
_peekiol.1 = io
return _peekio()
end
def peekiow(io)
_peekiowl.1 = io
_peekiowh.1 = io+1
@ -189,6 +230,11 @@ end
//
// Uthernet interface
//
export def copyMAC(ptr)
if !rxpacket; rxpacket = heapalloc(MAX_FRAME_SIZE); fin
memcpy(ptr, @MAC, MAC_SIZE)
return MAC_SIZE
end
export def writeEther(packet, size)
pokeiow(txcmd, $C0)
pokeiow(txlen, size)
@ -196,22 +242,16 @@ export def writeEther(packet, size)
return pokefrm(packet, size)
end
export def writevEther(destha, payload, seglist, size)
word segsize
pokeiow(txcmd, $C0)
pokeiow(txlen, size + t_ehdr)
pokeiow(txlen, size + t_ethrhdr)
repeat; until peekpreg($0138) & $0100
pokefrm(destha, 6)
pokefrm(@MAC, 6)
pokefrm(destha, MAC_SIZE)
pokefrm(@MAC, MAC_SIZE)
pokefrm(@payload, 2)
while size > 0
segsize = seglist=>2
if segsize > size
segsize = size
fin
pokefrm(seglist=>0, segsize)
size = size - segsize
seglist = seglist + 4
pokefrm(seglist=>seg_buf, seglist=>seg_len)
size = size - seglist=>seg_len
seglist = seglist + t_segment
loop
end
export def readEther(size)
@ -229,34 +269,18 @@ export def readEther(size)
fin
return segptr
end
export def readvEther(seglist, size)
word segsize
while size > 0
segsize = seglist=>2
size = size - segsize
if segsize > rxsize
segsize = rxsize
fin
memcpy(seglist=>0, rxptr, segsize)
rxptr = rxptr + segsize
rxsize = rxsize - segsize
seglist = seglist + 4
loop
return rxsize
end
export def recvEther
if rxsize == 0
if peekiow(isq) & $3F == $04
peekfrm(@rxstatus, 4)
if rxstatus & $0100
if !rxpacket; rxpacket = heapalloc(MAX_FRAME_SIZE); fin
if peekio(rxdata_hi) & $01
peekio(rxdata_lo)
rxsize.1 = peekio(rxdata_hi)
rxsize.0 = peekio(rxdata_lo)
peekfrm(rxpacket, rxsize)
rxptr = rxpacket
else
peekio(rxdata_lo)
pokepreg($0102, $0140) // Skip pkt
rxstatus = 0
rxsize = 0
fin
fin
fin
@ -270,6 +294,7 @@ for slot = $F0 downto $90 step $10
pokeiow(slot+PREG_INDEX, 0)
if peekiow(slot+PREG_DATA) == $630E
pokepreg($0114, $40) // RESET
rxdata_hi = slot + 1
txcmd = slot + TXCMD
txlen = slot + TXLEN
isq = slot + INT_STATUS