From e3e22ba41c3de813e68d3dd0c815d90b35e8a50d Mon Sep 17 00:00:00 2001 From: jonnosan Date: Sat, 21 Mar 2009 06:40:53 +0000 Subject: [PATCH] git-svn-id: http://svn.code.sf.net/p/netboot65/code@45 93682198-c243-4bdb-bd91-e943c89aac3b --- Makefile | 2 +- client/drivers/a2input.s | 3 ++ client/drivers/a2print.s | 14 +++++- client/drivers/a2timer.s | 11 +++-- client/drivers/c64inputs.s | 4 ++ client/drivers/c64kernal.s | 3 +- client/drivers/c64print.s | 12 ++++++ client/drivers/c64timer.s | 8 +++- client/drivers/rr-net.s | 23 +++++----- client/drivers/uthernet.s | 14 +++--- client/ip65/arp.s | 86 +++++++++++++------------------------ client/ip65/config.s | 22 +++++----- client/ip65/copymem.s | 12 +++--- client/ip65/cs8900a.s | 40 +++++++++-------- client/ip65/debug.s | 26 +++++++++-- client/ip65/dhcp.s | 44 +++++++++++-------- client/ip65/dns.s | 37 ++++++++-------- client/ip65/dottedquad.s | 23 ++++------ client/ip65/eth.s | 32 ++++++++------ client/ip65/icmp.s | 51 ++++++++++++++-------- client/ip65/ip.s | 88 +++++++++++++++++++++++--------------- client/ip65/ip65.s | 22 +++++++--- client/ip65/printf.s | 17 ++++++-- client/ip65/tftp.s | 58 +++++++++++++------------ client/ip65/timer.s | 10 ++--- client/ip65/udp.s | 73 +++++++++++++++++++------------ dist/make_dist.rb | 5 +++ dist/make_dist_ip65.rb | 13 +++++- doc/ip65.html | 13 +++++- 29 files changed, 460 insertions(+), 306 deletions(-) diff --git a/Makefile b/Makefile index 4d108e6..030262a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ dist: dist-ip65: rm -rf dist/ip65 - ruby dist/make_dist_ip65.rb + ruby dist/make_dist_ip65.rb distclean: make -C client distclean diff --git a/client/drivers/a2input.s b/client/drivers/a2input.s index 640c3d3..5ccdcae 100644 --- a/client/drivers/a2input.s +++ b/client/drivers/a2input.s @@ -1,5 +1,8 @@ .export get_key .code +;use Apple 2 monitor ROM function to read from keyboard +;inputs: none +;outputs: A contains ASCII code of key pressed get_key: jmp $fd1b \ No newline at end of file diff --git a/client/drivers/a2print.s b/client/drivers/a2print.s index e6e08b3..7d748da 100644 --- a/client/drivers/a2print.s +++ b/client/drivers/a2print.s @@ -5,19 +5,31 @@ .export beep .code +; +;use Apple 2 monitor ROM function to display 1 char +;inputs: A should be set to ASCII char to display +;outputs: none print_a: ora #$80 ;turn ASCII into Apple 2 screen codes jmp $fdf0 +;use Apple 2 monitor ROM function to move to new line +;inputs: none +;outputs: none print_cr: jmp $fd8e - +;use Apple 2 monitor ROM function to move to clear the screen +;inputs: none +;outputs: none cls: jmp $fc58 +;use Apple 2 monitor ROM function to move to make a 'beep' noise +;inputs: none +;outputs: none beep: jmp $fbdd \ No newline at end of file diff --git a/client/drivers/a2timer.s b/client/drivers/a2timer.s index 508df28..9a60b80 100644 --- a/client/drivers/a2timer.s +++ b/client/drivers/a2timer.s @@ -17,14 +17,19 @@ .code +;reset timer to 0 +;inputs: none +;outputs: none timer_init: ldax #0 stax current_time_value rts - -; return the current value (actually, delay a while, then update current value, then return it in ax) - +;this SHOULD just read the current timer value +;but since a standard apple 2 has no dedicated timing circuit, +;each call to this function actually delays a while, then updates the current value +; inputs: none +; outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init') timer_read: lda #111 jsr $fca8 ;wait for about 33ms diff --git a/client/drivers/c64inputs.s b/client/drivers/c64inputs.s index e80363f..79ba3fe 100644 --- a/client/drivers/c64inputs.s +++ b/client/drivers/c64inputs.s @@ -1,6 +1,10 @@ .export get_key .code + +;use C64 Kernel ROM function to read a key +;inputs: none +;outputs: A contains ASCII value of key just pressed get_key: jsr $ffe4 cmp #0 diff --git a/client/drivers/c64kernal.s b/client/drivers/c64kernal.s index 1c712a1..2f980df 100644 --- a/client/drivers/c64kernal.s +++ b/client/drivers/c64kernal.s @@ -1,5 +1,6 @@ .export exit_to_basic .code +; jump to BASIC interpreter loop exit_to_basic: - jmp $a7ae ; jump to BASIC interpreter loop + jmp $a7ae diff --git a/client/drivers/c64print.s b/client/drivers/c64print.s index 988da29..1db0f2b 100644 --- a/client/drivers/c64print.s +++ b/client/drivers/c64print.s @@ -5,17 +5,29 @@ .export beep .data +;use C64 Kernel ROM function to print a character to the screen +;inputs: A contains petscii value of character to print +;outputs: none print_a = $ffd2 .code +;use C64 Kernel ROM function to move to a new line +;inputs: none +;outputs: none print_cr: lda #13 jmp print_a +;use C64 Kernel ROM function to clear the screen +;inputs: none +;outputs: none cls: lda #147 ; 'CLR/HOME' jmp print_a +;currently does nothing (should make a 'beep noise') +;inputs: none +;outputs: none beep: rts \ No newline at end of file diff --git a/client/drivers/c64timer.s b/client/drivers/c64timer.s index 5e7ae94..4fb2772 100644 --- a/client/drivers/c64timer.s +++ b/client/drivers/c64timer.s @@ -14,7 +14,9 @@ .code -; initialize timers +;initialize timers +;inputs: none +;outputs: none timer_init: lda #$80 ; stop timers sta $dd0e @@ -35,7 +37,9 @@ timer_init: rts -; return the current value +;initialize timers +;inputs: none +;outputs: AX = count in milliseconds sent last call to timer_init timer_read: lda $dd07 ; cia counts backwards, return inverted value eor #$ff diff --git a/client/drivers/rr-net.s b/client/drivers/rr-net.s index f20030a..9d9be07 100644 --- a/client/drivers/rr-net.s +++ b/client/drivers/rr-net.s @@ -10,20 +10,19 @@ .export cs_tx_len .export cs_driver_name -rr_ctl = $de01 - -;cs_irq = $de00 -cs_packet_page = $de02 -cs_packet_data = $de04 -;cs_packet_data2 = $de06 -cs_rxtx_data = $de08 -;cs_rxtx_data2 = $de0a -cs_tx_cmd = $de0c -cs_tx_len = $de0e +rr_ctl = $de01 ;address of 'control' port on Retro-Replay +cs_packet_page = $de02 ;address of 'packet page' port on RR-Net +cs_packet_data = $de04;address of 'packet data' port on RR-Net +cs_rxtx_data = $de08 ;address of 'recieve/transmit data' port on RR-Net +cs_tx_cmd = $de0c;address of 'transmit command' port on RR-Net +cs_tx_len = $de0e;address of 'transmission length' port on RR-Net - .code - +.code + +;initialise Retro Replay so we can access the network adapter +;inputs: none +;outputs: none cs_init: lda rr_ctl ora #1 diff --git a/client/drivers/uthernet.s b/client/drivers/uthernet.s index 952c396..24aa27a 100644 --- a/client/drivers/uthernet.s +++ b/client/drivers/uthernet.s @@ -1,5 +1,5 @@ -; uthernet driver -; for the moment, always assume slot 3 +;uthernet driver +;currently hardcoded to use slot 3 addresses only .export cs_init @@ -11,11 +11,11 @@ .export cs_tx_len .export cs_driver_name -cs_rxtx_data = $c0b0 -cs_tx_cmd = $c0b4 -cs_tx_len = $c0b6 -cs_packet_page = $c0ba -cs_packet_data = $c0bc +cs_rxtx_data = $c0b0 ;address of 'recieve/transmit data' port on Uthernet +cs_tx_cmd = $c0b4;address of 'transmit command' port on Uthernet +cs_tx_len = $c0b6;address of 'transmission length' port on Uthernet +cs_packet_page = $c0ba;address of 'packet page' port on Uthernet +cs_packet_data = $c0bc;address of 'packet data' port on Uthernet .code diff --git a/client/ip65/arp.s b/client/ip65/arp.s index 95e9427..36dbe79 100644 --- a/client/ip65/arp.s +++ b/client/ip65/arp.s @@ -1,13 +1,11 @@ ; ARP address resolution - -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 .include "../inc/common.i" .export arp_init .export arp_lookup .export arp_process - .export arp_add + .export arp_add .export arp_ip .export arp_mac @@ -46,14 +44,14 @@ arp_state: .res 1 ; current activity ; arguments for lookup and add arp: ; ptr to mac/ip pair -arp_mac: .res 6 ; result is delivered here -arp_ip: .res 4 ; set ip before calling lookup +arp_mac: .res 6 ; result is delivered here when arp_lookup returns with carry flag clear +arp_ip: .res 4 ; set arp_ip before calling arp_lookup ; arp cache ac_size = 8 ; lookup cache ac_ip = 6 ; offset for ip ac_mac = 0 ; offset for mac -arp_cache: .res (6+4)*ac_size +arp_cache: .res (6+4)*ac_size ;cache of IP addresses and corresponding MAC addresses ; offsets for arp packet generation ap_hw = 14 ; hw type (eth = 0001) @@ -77,8 +75,9 @@ arptimeout: .res 2 ; time when we will have timed out .code - -; initialize arp +;initialize arp (including clearing the arp cache) +;inputs: none +;outputs: none arp_init: lda #0 @@ -109,9 +108,13 @@ arp_init: rts -; lookup an ip -; clc = mac returned in arp_mac -; sec = request sent, call again for result +;lookup the mac address for an ip +;inputs: arp_ip should be set to ip address to be resolved +;outputs: +; if carry flag is clear, then arp_mac will be set to correct mac address +; if carry flag is set, then the correct mac address could not be found in +; the arp cache, so an arp request was sent. so the caller should wait a while +; (to allow time for an arp response message to arrive) and then call arp_lookup again. arp_lookup: lda arp_ip ; check for broadcast IP (255.255.255.255) @@ -252,14 +255,15 @@ findip: rts -; handle incoming arp packets +;handle incoming arp packets +;inputs: eth_inp should contain an arp packet +;outputs: +; carry flag is set if there was an error processing the arp packet, clear otherwise +; the arp_cache will be updated with the mac & ip address (whether the inbound +; message was a request or a response). if the incoming packet was an arp +; request for this machine, then an arp response will be created (overwriting +; eth_outp) and sent out arp_process: -; lda eth_inp_len ; check packet size -; cmp #ap_packlen -; bne @badpacket lda eth_inp + ap_op ; should be 0 bne @badpacket @@ -326,14 +330,6 @@ arp_process: cmp #arp_wait ; are we waiting for a reply? bne @badpacket -; ldx #0 -;: lda gotmsg,x -; beq :+ -; jsr $ffd2 -; inx -; bne :- -;: - ldax #eth_inp + ap_shw jsr ac_add_source ; add to cache @@ -342,14 +338,13 @@ arp_process: rts -;gotmsg: -; .byte "gOT arp REPLY",13,0 -; -;addmsg: -; .byte "aDDING ARP ENTRY",13,0 - -; add arp_mac and arp_ip to the cache +; add arp_mac and arp_ip to the cache +;inputs: +; arp_ip is ip address to add to cache +; arp_mac is corresponding mac address of specified ip +;outputs: +; arp_cache is updated arp_add: jsr findip ; check if ip is already in cache bcs @add @@ -365,38 +360,15 @@ arp_add: ldax #arp ; add -; add source to cache +;add source to cache ac_add_source: stax ap -; ldx #0 -;: lda addmsg,x -; beq :+ -; jsr $ffd2 -; inx -; bne :- -;: ldx #9 ; make space in the arp cache : -; lda arp_cache + 140,x -; sta arp_cache + 150,x -; lda arp_cache + 130,x -; sta arp_cache + 140,x -; lda arp_cache + 120,x -; sta arp_cache + 130,x -; lda arp_cache + 110,x -; sta arp_cache + 120,x -; lda arp_cache + 100,x -; sta arp_cache + 110,x -; lda arp_cache + 90,x -; sta arp_cache + 100,x -; lda arp_cache + 80,x -; sta arp_cache + 90,x -; lda arp_cache + 70,x -; sta arp_cache + 80,x lda arp_cache + 60,x sta arp_cache + 70,x lda arp_cache + 50,x diff --git a/client/ip65/config.s b/client/ip65/config.s index 47ad5b4..22ff5d7 100644 --- a/client/ip65/config.s +++ b/client/ip65/config.s @@ -1,7 +1,5 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - -; Configuration - +;IP configuration defaults +;most of these will be overwritten if dhcp is used for configuration .export cfg_mac .export cfg_ip @@ -10,12 +8,14 @@ .export cfg_dns .export cfg_tftp_server -.data ; these are defaults +.data -cfg_mac: .byte $00, $80, $10, $6d, $76, $30 +cfg_mac: .byte $00, $80, $10, $6d, $76, $30 ;mac address to be assigned to local machine ;cfg_ip: .byte 192, 168, 0, 64 -cfg_ip: .byte 0,0,0,0 -cfg_netmask: .byte 255, 255, 255, 0 -cfg_gateway: .byte 0, 0, 0, 0 -cfg_dns: .byte 0, 0, 0, 0 -cfg_tftp_server: .byte $ff,$ff,$ff,$ff + +cfg_ip: .byte 0,0,0,0 ;ip address of local machine (will be overwritten if dhcp_init is called) +cfg_netmask: .byte 255, 255, 255, 0; netmask of local network (will be overwritten if dhcp_init is called) +cfg_gateway: .byte 0, 0, 0, 0 ;ip address of router on local network (will be overwritten if dhcp_init is called) +cfg_dns: .byte 0, 0, 0, 0; ip address of dns server to use (will be overwritten if dhcp_init is called) +cfg_tftp_server: .byte $ff,$ff,$ff,$ff ; ip address of server to send tftp requests to (can be a broadcast address) + \ No newline at end of file diff --git a/client/ip65/copymem.s b/client/ip65/copymem.s index 1f968c6..b0fca31 100644 --- a/client/ip65/copymem.s +++ b/client/ip65/copymem.s @@ -1,6 +1,4 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - -; copy memory +; utility routine to copy memory .export copymem @@ -22,8 +20,12 @@ end: .res 1 .code -; copy memory -; set copy_src and copy_dest, length in A/X +;copy memory +;inputs: +; copy_src is address of buffer to copy from +; copy_dest is address of buffer to copy to +; AX = number of bytes to copy +;outputs: none copymem: sta end ldy #0 diff --git a/client/ip65/cs8900a.s b/client/ip65/cs8900a.s index f7d5e6b..a42fd8d 100644 --- a/client/ip65/cs8900a.s +++ b/client/ip65/cs8900a.s @@ -1,6 +1,4 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - -; Ethernet driver for CS8900A +; Ethernet driver for CS8900A chip (as used in RR-NET and Uthernet adapters) ; ; Based on Doc Bacardi's tftp source @@ -8,10 +6,6 @@ .include "../inc/common.i" .include "cs8900a.i" - - ;.import dbg_dump_eth_header - - .export eth_init .export eth_rx .export eth_tx @@ -58,15 +52,17 @@ eth_outp_len: .res 2 ; output packet length eth_outp: .res 1518 ; space for output packet ; ethernet packet offsets -eth_dest = 0 ; destination address -eth_src = 6 ; source address -eth_type = 12 ; packet type -eth_data = 14 ; packet data +eth_dest = 0 ; offset of destination mac address in an ethernet packet +eth_src = 6 ; offset of source address in an ethernet packet +eth_type = 12 ; offset of packet type in an ethernet packet +eth_data = 14 ; offset of packet data in an ethernet packet .code -; initialize, return clc on success +;initialize the ethernet adaptor +;inputs: none +;outputs: carry flag is set if there was an error, clear otherwise eth_init: jsr cs_init @@ -86,7 +82,6 @@ eth_init: write_page pp_self_ctl, $0055 ; $0114, reset chip write_page pp_rx_ctl, $0d05 ; $0104, accept individual and broadcast packets - ;write_page pp_rx_ctl, $0d85 ; $0104, promiscuous mode lda #pp_ia/2 ; $0158, write mac address ldx cfg_mac @@ -113,7 +108,13 @@ eth_init: rts -; receive a packet +;receive a packet +;inputs: none +;outputs: +; if there was an error receiving the packet (or no packet was ready) then carry flag is set +; if packet was received correctly then carry flag is clear, +; eth_inp contains the received packet, +; and eth_inp_len contains the length of the packet eth_rx: lda #$24 ; check rx status sta cs_packet_page @@ -179,10 +180,15 @@ eth_rx: rts -; send a packet +; send a packet +;inputs: +; eth_outp: packet to send +; eth_outp_len: length of packet to send +;outputs: +; if there was an error sending the packet then carry flag is set +; otherwise carry flag is cleared eth_tx: - ;jsr dbg_dump_eth_header - + lda #$c9 ; ask for buffer space sta cs_tx_cmd lda #0 diff --git a/client/ip65/debug.s b/client/ip65/debug.s index 30efaa8..016dcc8 100644 --- a/client/ip65/debug.s +++ b/client/ip65/debug.s @@ -1,4 +1,4 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 +;routines for dumping debug information .include "../inc/common.i" .include "../inc/printf.i" @@ -26,6 +26,11 @@ cptr: .res 2 .code +;prints out the header of ethernet packet that is ready to be sent; +; inputs: +; eth_outp: pointer to ethernet packet +; eth_outp_len: length of ethernet packet +; outputs: none dbg_dump_eth_header: pha txa @@ -46,7 +51,11 @@ dbg_dump_eth_header: pla rts - +;prints out the header of ip packet that is ready to be sent; +; inputs: +; eth_outp: pointer to ethernet packet containing an ip packet +; eth_outp_len: length of ethernet packet +; outputs: none dbg_dump_ip_header: pha txa @@ -72,7 +81,11 @@ dbg_dump_ip_header: pla rts - +;prints out the header of udp packet that is ready to be sent; +; inputs: +; eth_outp: pointer to ethernet packet containing a udp packet +; eth_outp_len: length of ethernet packet +; outputs: none dbg_dump_udp_header: pha txa @@ -96,6 +109,9 @@ dbg_dump_udp_header: console_out = $ffd2 +;print a string to the console +;inputs: AX = address of (null terminated) string to print +;outputs: none console_strout: stax cptr @@ -118,7 +134,9 @@ console_strout: pla rts - +;print a 32 bit number as 4 hex digits +;inputs: AX = 32 bit number to print +;outputs: none dbgout16: stax val16 printf "%04x", val16 diff --git a/client/ip65/dhcp.s b/client/ip65/dhcp.s index 32b83e7..0e60c6b 100644 --- a/client/ip65/dhcp.s +++ b/client/ip65/dhcp.s @@ -1,23 +1,18 @@ -;######################## -; minimal dhcp implementation -; written by jonno@jamtronix.com 2009 -; -;######################## -; to use - first call ip65_init, then call dhcp_init -; no inputs required. -; on return, carry flag clear means IP config has been -; sucesfully obtained (cfg_ip, cfg_netmask, cfg_gateway and cfg_dns set as appropriate). -; if carry flag is set, IP config could not be set. that could be because of a network -; error or because there was no response from a DHCP server within about 5 seconds. -;######################## - +; minimal dhcp implementation - ip addresses are requested from a dhcp server +; (aka 'leased') but are not renewed or released. although this is not correct +; behaviour according to the DHCP RFC, this works fine in practice in a typical +; home network environment. +; +; cfg_ip,cfg_netmask,cfg_gateway and cfg_dns variables are all overwritten, +; therefore, these values must be stored in RAM not ROM +; MAX_DHCP_MESSAGES_SENT=12 ;timeout after sending 12 messages will be about 15 seconds (1+2+3...)/4 .include "../inc/common.i" .export dhcp_init - .export dhcp_server - .export dhcp_state + .export dhcp_server ;will be set address of dhcp server that configuration was obtained from + .export dhcp_state .import cfg_mac .import cfg_ip @@ -79,7 +74,8 @@ dhcp_ready_to_request = 3 ; got a DHCPOFFER, ready to send a DHCPREQUEST dhcp_requesting = 4 ; sent a DHCPREQUEST, waiting for a DHCPACK dhcp_bound = 5 ; we have been allocated an IP address -dhcp_state: .res 1 ; current activity +;flag indicating current state of dhcp initialization. +dhcp_state: .res 1 dhcp_message_sent_count: .res 1 dhcp_timer: .res 1 @@ -103,10 +99,22 @@ DHCPINFORM =8 .code - +; +;inputs: none (although ip65_init should be called first) +;outputs: +; carry flag clear means IP config has been sucesfully obtained and +; cfg_ip, cfg_netmask, cfg_gateway and cfg_dns will be set per response from dhcp server. +; dhcp_server will be set to address of server that provided configuration +; if carry flag is set there was an error. +; in either case, dhcp_state will indicate where dhcp initialization ended (to help debug) +; possible values for dhcp_state are: +; 1 - initial state +; 2 - sent a DHCPDISCOVER, waiting for a DHCPOFFER +; 3 - got a DHCPOFFER, ready to send a DHCPREQUEST +; 4 - sent a DHCPREQUEST, waiting for a DHCPACK +; 5 - we have been allocated an IP address dhcp_init: - ldx #3 ; rewrite ip address lda #0 : sta cfg_ip,x diff --git a/client/ip65/dns.s b/client/ip65/dns.s index 4e54135..2c921db 100644 --- a/client/ip65/dns.s +++ b/client/ip65/dns.s @@ -1,17 +1,4 @@ -;######################## -; minimal dns implementation -; requires a DNS server that supports recursion -; written by jonno@jamtronix.com 2009 -; -;######################## -; to use - -; ensure cfg_dns points to a DNS server that supports recursion -; call dns_set_hostname with AX pointing to null terminated hostname -; then call dns_resolve -; on exit: carry flag is set if there was an error. if carry flag is clear, then dns_ip will point to the -; IP address of the hostname -;######################## - +; minimal dns implementation - requires a DNS server that supports recursion MAX_DNS_MESSAGES_SENT=8 ;timeout after sending 8 messages will be about 7 seconds (1+2+3+4+5+6+7+8)/4 @@ -67,7 +54,7 @@ dns_qname=12 dns_server_port=53 dns_client_port_low_byte: .res 1 -dns_ip: .res 4 +dns_ip: .res 4 ;will be contain ip address of hostname after succesful exection of dns_resolve dns_msg_id: .res 2 @@ -84,12 +71,12 @@ dns_query_sent = 2 ; sent a query, waiting for a response dns_complete = 3 ; got a good response dns_failed = 4 ; got either a 'no such name' or 'recursion declined' response -dns_state: .res 1 ; current activity +dns_state: .res 1 ; flag indicating the current stage in the dns resolution process dns_timer: .res 1 dns_loop_count: .res 1 dns_break_polling_loop: .res 1 -dns_status: .res 2 +dns_status: .res 2 ; for debugging purposes only (behaviour not garuanteed) hostname_copied: .res 1 @@ -98,7 +85,14 @@ questions_in_response: .res 1 hostname_was_dotted_quad: .res 1 .code - + +; sets up for resolution of a hostname to an ip address +; inputs: +; AX = pointer to null terminated string that contains either a dns hostname +; (e.g. "host.example.com",0) or an address in "dotted quad" format, +; (e.g. "192.168.1.0",0) +; outputs: +; carry flag is set on error (i.e. hostname too long), clear otherwise dns_set_hostname: stax dns_hostname ;copy the hostname into a buffer suitable to copy directly into the qname field @@ -178,6 +172,13 @@ dns_set_hostname: sec rts +; resolve a string containing a hostname (or a dotted quad) to an ip address +; inputs: +; cfg_dns must point to a DNS server that supports recursion +; dns_set_hostname must have been called to load the string to be resolved +; outputs: +; carry flag is set if there was an error, clear otherwise +; dns_ip: set to the ip address of the hostname (if no error) dns_resolve: lda hostname_was_dotted_quad beq @hostname_not_dotted_quad diff --git a/client/ip65/dottedquad.s b/client/ip65/dottedquad.s index 10805c4..471a1bc 100644 --- a/client/ip65/dottedquad.s +++ b/client/ip65/dottedquad.s @@ -1,14 +1,3 @@ -;######################## -; helper routine to convert a string representing a dotted quad (IP address, netmask) into 4 octets -; written by jonno@jamtronix.com 2009 -; -;######################## -; to use - -; ldax with ptr of dotted quad string -; then call parse_dotted_quad -; on exit: carry flag is set if there was an error. if carry flag is clear, then dotted_quad_value will be set -;######################## - .include "../inc/common.i" @@ -17,15 +6,21 @@ .export dotted_quad_value .bss - dotted_quad_value: .res 4 + dotted_quad_value: .res 4 ;set to 32 bit ip address on a succesful call to parse_dotted_quad dotted_quad_ptr: .res 4 .code - - parse_dotted_quad: +; convert a string representing a dotted quad (IP address, netmask) into 4 octets +; inputs: +; AX= pointer to null-terminated string containing dotted quad +; e.g. "192.168.1.0",0 +; outputs: +; carry flag is set if there was an error, clear otherwise +; dotted_quad_value: will be set to (32 bit) ip address (if no error) +parse_dotted_quad: stax dotted_quad_ptr+1 lda #$AD ; $AD='LDA immediate' diff --git a/client/ip65/eth.s b/client/ip65/eth.s index 61ad8de..80359c7 100644 --- a/client/ip65/eth.s +++ b/client/ip65/eth.s @@ -1,7 +1,4 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - -; Common ethernet driver code - +; Common ethernet driver code (independant of host computer or ethernet chipset) .include "../inc/common.i" @@ -18,18 +15,23 @@ ; ethernet packet offsets -eth_dest = 0 ; destination address -eth_src = 6 ; source address -eth_type = 12 ; packet type -eth_data = 14 ; packet data +eth_dest = 0 ; offset of destination address in ethernet packet +eth_src = 6 ; offset of source address in ethernet packet +eth_type = 12 ; offset of packet type in ethernet packet +eth_data = 14 ; offset of packet data in ethernet packet + +; protocols -; protocols eth_proto_ip = 0 eth_proto_arp = 6 .code - +;set the destination address in the packet under construction to be the ethernet +;broadcast address (FF:FF:FF:FF:FF:FF) +;inputs: +; eth_outp: buffer in which outbound ethernet packet is being constructed +;outputs: none eth_set_broadcast_dest: ldx #5 lda #$ff @@ -38,7 +40,10 @@ eth_set_broadcast_dest: bpl :- rts - +;set the source address in the packet under construction to be local mac address +;inputs: +; eth_outp: buffer in which outbound ethernet packet is being constructed +;outputs: none eth_set_my_mac_src: ldx #5 : lda cfg_mac,x @@ -47,7 +52,10 @@ eth_set_my_mac_src: bpl :- rts - +;set the 'protocol' field in the packet under construction +;inputs: +; A = protocol number (per 'eth_proto_*' constants) +;outputs: none eth_set_proto: sta eth_outp + eth_type + 1 lda #8 diff --git a/client/ip65/icmp.s b/client/ip65/icmp.s index 2e1b432..d4ceabe 100644 --- a/client/ip65/icmp.s +++ b/client/ip65/icmp.s @@ -1,4 +1,5 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 +;ICMP implementation +; .include "../inc/common.i" @@ -48,22 +49,24 @@ icmp_cbtype: .res icmp_cbmax ; table of listener types icmp_cbcount: .res 1 ; number of active listeners ; icmp packet offsets -icmp_inp = ip_inp + ip_data -icmp_outp = ip_outp + ip_data -icmp_type = 0 -icmp_code = 1 -icmp_cksum = 2 -icmp_data = 4 +icmp_inp = ip_inp + ip_data ;pointer to inbound icmp packet +icmp_outp = ip_outp + ip_data ;pointer to outbound icmp packet +icmp_type = 0 ;offset of 'type' field in icmp packet +icmp_code = 1 ;offset of 'code' field in icmp packet +icmp_cksum = 2 ;offset of 'checksum' field in icmp packet +icmp_data = 4;offset of 'data' field in icmp packet ; icmp echo packet offsets -icmp_echo_id = 4 -icmp_echo_seq = 6 -icmp_echo_data = 8 +icmp_echo_id = 4 ;offset of 'id' field in icmp echo request/echo response +icmp_echo_seq = 6 ;offset of 'sequence' field in icmp echo request/echo response +icmp_echo_data = 8 ;offset of 'data' field in icmp echo request/echo response .code -; initialize icmp +; initialize icmp +; inputs: none +; outputs: none icmp_init: lda #0 sta icmp_cbcount @@ -71,8 +74,14 @@ icmp_init: sta icmp_cbtmp rts - -; process incoming icmp packet +;process incoming icmp packet +;inputs: +; eth_inp points to an ethernet frame containing an icmp packet +;outputs: +; carry flag - set on any error, clear if OK +; if inbound packet is a request (e.g. 'echo request') and an icmp listener +; has been installed, then an appropriate response message will be +; generated and sent out (overwriting the eth_outp buffer) icmp_process: lda icmp_inp + icmp_type cmp #8 ; ping @@ -161,8 +170,12 @@ icmp_process: rts -; add an icmp listener -; icmp type in A, vector in icmp_callback +;add an icmp listener +;inputs: +; A = icmp type +; icmp_callback: vector to call when an icmp packet of specified type arrives +;outputs: +; carry flag - set if error, clear if no error icmp_add_listener: ldx icmp_cbcount ; any listeners at all? beq @add @@ -190,8 +203,12 @@ icmp_add_listener: rts -; remove an icmp listener -; icmp type in A +;add an icmp listener +;inputs: +; A = icmp type +;outputs: +; carry flag - set if error (i.e. no listner for this type exists), +; clear if no error icmp_remove_listener: ldx icmp_cbcount ; any listeners installed? beq @notfound diff --git a/client/ip65/ip.s b/client/ip65/ip.s index 5a09dfb..c72d57d 100644 --- a/client/ip65/ip.s +++ b/client/ip65/ip.s @@ -1,10 +1,5 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - .include "../inc/common.i" - ;.import dbg_dump_ip_header - - .export ip_init .export ip_process .export ip_calc_cksum @@ -65,37 +60,38 @@ .segment "IP65ZP" : zeropage ; checksum -ip_cksum_ptr: .res 2 ; data pointer +ip_cksum_ptr: .res 2 ; pointer to data to be checksummed .bss -ip_cksum_len: .res 2 ; length of data +ip_cksum_len: .res 2 ; length of data to be checksummed ; ip packets start at ethernet packet + 14 -ip_inp = eth_inp + eth_data -ip_outp = eth_outp + eth_data +ip_inp = eth_inp + eth_data ;pointer to start of IP packet in input ethernet frame +ip_outp = eth_outp + eth_data ;pointer to start of IP packet in output ethernet frame ; temp storage for size calculation len: .res 2 ; flag for incoming broadcast packets -ip_broadcast: .res 1 +ip_broadcast: .res 1 ;flag set when an incoming IP packet was sent to a broadcast address ; ip packet offsets -ip_ver_ihl = 0 -ip_tos = 1 -ip_len = 2 -ip_id = 4 -ip_frag = 6 -ip_ttl = 8 -ip_proto = 9 -ip_header_cksum = 10 -ip_src = 12 -ip_dest = 16 -ip_data = 20 +ip_ver_ihl = 0 ;offset of 4 bit "version" field and 4 bit "header length" field in an IP packet header +ip_tos = 1 ;offset of "type of service" field in an IP packet header +ip_len = 2 ;offset of "length" field in an IP packet header +ip_id = 4 ;offset of "identification" field in an IP packet header +ip_frag = 6 ;offset of "fragmentation offset" field in an IP packet header +ip_ttl = 8 ;offset of "time to live" field in an IP packet header +ip_proto = 9 ;offset of "protocol number" field in an IP packet header +ip_header_cksum = 10 ;offset of "ip header checksum" field in an IP packet header +ip_src = 12 ;offset of "source address" field in an IP packet header +ip_dest = 16 ;offset of "destination address" field in an IP packet header +ip_data = 20 ;offset of data payload in an IP packet + +; ip protocols -; ip protocols ip_proto_icmp = 1 ip_proto_tcp = 6 ip_proto_udp = 17 @@ -111,7 +107,9 @@ bad_addr: .res 2 .code -; initialize ip routines +; initialize ip routines +; inputs: none +; outputs: none ip_init: lda #0 sta bad_header @@ -126,7 +124,13 @@ ip_init: rts -; process an incoming packet +;process an incoming packet & call the appropriate protocol handler +;inputs: +; eth_inp: should point to the received ethernet packet +;outputs: +; carry flag - set on any error, clear if OK +; depending on the packet contents and the protocol handler, a response +; message may be generated and sent out (overwriting eth_outp buffer) ip_process: jsr verifyheader ; ver, ihl, len, frag, checksum bcc @ok @@ -235,7 +239,13 @@ checkaddr: rts -; create a packet template +; create an IP header (with all the appropriate flags and common fields set) inside an +; ethernet frame +;inputs: +; eth_outp: should point to a buffer in which the ethernet frame is being built +;outputs: +; eth_outp: contains an IP header with version, TTL, flags, src address & IP header +; checksum fields set. ip_create_packet: lda #$45 ; set IP version and header length sta ip_outp + ip_ver_ihl @@ -272,15 +282,18 @@ ip_create_packet: rts -; send an IP packet -; -; but first: -; -; call ip_create_packet -; set length -; set ID -; set protocol -; set destination address +; send an IP packet +;inputs +; eth_outp: should point to an ethernet frame that has an IP header created (by +; calling ip_create_packet) +; ip_len: should contain length of IP packet (header + data) +; ip_id: should contain an ID that is unique for each packet +; ip_protocol: should contain protocol ID +; ip_dest: should contain the destination IP address +;outputs: +; eth_outp: ethernet frame updated with correct IP header, then sent out over +; the wire +; carry flag - set on any error, clear if OK ip_send: ldx #3 ; get mac addr from ip : lda ip_outp + ip_dest,x @@ -338,7 +351,12 @@ ip_send: jmp eth_tx ; send packet and return status -; calculate checksum for ip header +; calculate checksum for a buffer according to the standard IP checksum algorithm +;inputs: +; ip_cksum_ptr: points at buffer to be checksummed +; AX: length of buffer to be checksumed +;outputs: +; AX: checkum of buffer ip_calc_cksum: sta ip_cksum_len ; save length stx ip_cksum_len + 1 diff --git a/client/ip65/ip65.s b/client/ip65/ip65.s index 9289f42..b8f7d5e 100644 --- a/client/ip65/ip65.s +++ b/client/ip65/ip65.s @@ -1,5 +1,3 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - ; ip65 main routines .include "../inc/common.i" @@ -34,9 +32,14 @@ ip65_ctr_ip: .res 1 ; incremented for every incoming ip packet .code -; initialize stack +; initialise the IP stack +; this calls the individual protocol & driver initialisations, so this is +; the only *_init routine that must be called by a user application, +; except for dhcp_init which must also be called if the application +; is using dhcp rather than hardcoded ip configuration +; inputs: none +; outputs: none ip65_init: - jsr eth_init ; initialize ethernet driver @@ -49,8 +52,15 @@ ip65_init: rts -; maintenance routine -; polls for packets, and dispatches to listeners +;main ip polling loop +;this routine should be periodically called by an application at any time +;that an inbound packet needs to be handled. +;it is 'non-blocking', i.e. it will return if there is no packet waiting to be +;handled. any inbound packet will be handed off to the appropriate handler. +;inputs: none +;outputs: carry flag set if no packet was waiting, or packet handling caused error. +; since the inbound packet may trigger generation of an outbound, eth_outp +; and eth_outp_len may be overwriiten. ip65_process: jsr eth_rx ; check for incoming packets bcs @done diff --git a/client/ip65/printf.s b/client/ip65/printf.s index 01d9609..bef24c2 100644 --- a/client/ip65/printf.s +++ b/client/ip65/printf.s @@ -1,5 +1,3 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - .include "../inc/common.i" .export console_printf @@ -30,7 +28,20 @@ ext: .res 2 .code - + +;print a string to console, with (some) standard printf % codes converted +;inputs: AX = pointer to 'argument list' +; first word in argument list is the string to be printed +; subsequent words in argument list are interpolated in to the string as +; it is displayed. (argument list is automagically created by the 'printf' macro +; defined in inc/printf.i) +;outputs: none +;supported % codes: +; %s: string (argument interpreted as pointer to null terminated string) +; %d: decimal number (arguement interpreted as 16 bit number) +; %x: hex number (arguement interpreted as 16 bit number) +; %c: char (arguement interpreted as pointer to single ASCII char) +;"field width" modifiers are also supported, e.g "%02x" to print 2 hex digits console_printf: stax argptr ldy #0 diff --git a/client/ip65/tftp.s b/client/ip65/tftp.s index a524192..1fec482 100644 --- a/client/ip65/tftp.s +++ b/client/ip65/tftp.s @@ -1,26 +1,5 @@ -;######################## -; minimal tftp implementation (client only) -; supports file download (not upload) and custom directory listing (using opcode of 0x65) -; written by jonno@jamtronix.com 2009 -; -;######################## -; to get a directory listing -; set tftp_ip to host to download from (which can be broadcast address 255.255.255.255) -; set tftp_load_address to point to memory location that dir will be stored in -; set tftp_filename to null terminated filemask (e.g. "*.prg") -; then call tftp_directory_listing -; on exit: carry flag is set if there was an error. -; -; to d/l a file: -; set tftp_ip to host to download from (which can be broadcast address 255.255.255.255) -; set tftp_load_address to point to memory location that file will be downloaded to -; OR set tftp_load_address to $0000, and first 2 bytes of downloaded file will be treated as the load address -; set tftp_filename to null terminated filename to download -; then call tftp_download -; on exit: carry flag is set if there was an error. tftp_load_address will be set to address file loaded to (i.e. gets overwritten if originally set to $0000) - -; -;######################## +;minimal tftp implementation (client only) +;supports file download (not upload) and custom directory listing (using non-standard tftp opcode of 0x65) TFTP_MAX_RESENDS=10 TFTP_TIMER_MASK=$F8 ;mask lower two bits, means we wait for 8 x1/4 seconds @@ -62,20 +41,20 @@ .segment "IP65ZP" : zeropage -tftp_filename: .res 2 +tftp_filename: .res 2 ;name of file to d/l or filemask to get directory listing for .bss ;packet offsets -tftp_inp = udp_inp + udp_data +tftp_inp = udp_inp + udp_data tftp_outp: .res 128 ;everything after filename in a request at a relative address, not fixed, so don't bother defining offset constants tftp_server_port=69 tftp_client_port_low_byte: .res 1 -tftp_load_address: .res 2 -tftp_ip: .res 4 +tftp_load_address: .res 2 ;address file will be (or was) downloaded to +tftp_ip: .res 4 ;ip address of tftp server - set to 255.255.255.255 (broadcast) to send request to all tftp servers on local lan tftp_bytes_to_copy: .res 2 tftp_current_memloc: .res 2 @@ -102,10 +81,35 @@ tftp_opcode: .res 2 ; will be set to 4 if we are doing a RRQ, or 7 if we are doi .code +; query a tftp server for a directory listing (uses a non-standard tftp opcode, +; currently only supported by tftp server built in to 'netboot65' server) +; inputs: +; tftp_ip: ip address of host to download from (set to 255.255.255.255 for broadcast) +; tftp_load_address: memory location that dir will be stored in +; tftp_filename: pointer to null terminated filemask (e.g. "*.prg",0) +; outputs: carry flag is set if there was an error +; if there was no error, the buffer at tftp_load_address will be filled +; with null-terminated strings containing the names of all files on the +; server that matched the specified file mask, and an additional null +; byte will follow the null byte terminating the last file name. +; NB - there is no limit to the amount of memory this function will consume, +; so depending on where tftp_load_address is set, there is potential to +; overwrite other data or code tftp_directory_listing: ldax #$6500 ;opcode 65 = netboot65 DIR (non-standard opcode) jmp set_tftp_opcode +;download a file from a tftp server +; inputs: +; tftp_ip: ip address of host to download from (set to 255.255.255.255 for broadcast) +; tftp_load_address: memory location that dir will be stored in, or $0000 to +; treat first 2 bytes received from tftp server as memory address that rest +; of file should be loaded into (e.g. if downloading a C64 'prg' file) +; tftp_filename: pointer to null terminated name of file to download +; outputs: carry flag is set if there was an error +; if there was no error, the buffer at tftp_load_address will be filled +; with file downloaded. +; tftp_load_address: will be set to the actual address loaded into tftp_download: ldax #$0100 ;opcode 01 = RRQ set_tftp_opcode: diff --git a/client/ip65/timer.s b/client/ip65/timer.s index fc1b199..5381319 100644 --- a/client/ip65/timer.s +++ b/client/ip65/timer.s @@ -1,13 +1,11 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 - ; timer routines ; ; the timer should be a 16-bit counter that's incremented by about ; 1000 units per second. it doesn't have to be particularly accurate, ; if you're working with e.g. a 60 Hz VBLANK IRQ, adding 17 to the ; counter every frame would be just fine. - - +; +; this is generic timer routines, machine specific code goes in drivers/timer.s .include "../inc/common.i" @@ -21,7 +19,9 @@ time: .res 2 .code -; check if value in A/X is smaller than current timer value +;check if specified period of time has passed yet +;inputs: AX - maximum number of milliseconds we are willing to wait for +;outputs: carry flag set if timeout occured, clear otherwise timer_timeout: pha txa diff --git a/client/ip65/udp.s b/client/ip65/udp.s index a79be49..3027840 100644 --- a/client/ip65/udp.s +++ b/client/ip65/udp.s @@ -1,4 +1,4 @@ -;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65 +;UDP (user datagram protocol) functions .include "../inc/common.i" @@ -53,13 +53,13 @@ .bss ; argument for udp_add_listener -udp_callback: .res 2 +udp_callback: .res 2 ;vector to routine to be called when a udp packet arrives ; arguments for udp_send -udp_send_dest: .res 4 -udp_send_src_port: .res 2 -udp_send_dest_port: .res 2 -udp_send_len: .res 2 +udp_send_dest: .res 4 ;set to ip address that udp packet will be sent to +udp_send_src_port: .res 2 ;set to port that udp packet will be sent from +udp_send_dest_port: .res 2 ;set to port that udp packet will be sent to +udp_send_len: .res 2 ;set to length of data to be sent in udp packet (excluding ethernet,ip & udp headers) ; udp listener callbacks udp_cbmax = 4 @@ -71,13 +71,13 @@ udp_cbporthi: .res udp_cbmax ; table of ports (msb) udp_cbcount: .res 1 ; number of active listeners ; udp packet offsets -udp_inp = ip_inp + ip_data -udp_outp = ip_outp + ip_data -udp_src_port = 0 -udp_dest_port = 2 -udp_len = 4 -udp_cksum = 6 -udp_data = 8 +udp_inp = ip_inp + ip_data ;pointer to udp packet inside inbound ethernet frame +udp_outp = ip_outp + ip_data ;pointer to udp packet inside outbound ethernet frame +udp_src_port = 0 ;offset of source port field in udp packet +udp_dest_port = 2 ;offset of destination port field in udp packet +udp_len = 4 ;offset of length field in udp packet +udp_cksum = 6 ;offset of checksum field in udp packet +udp_data = 8 ;offset of data in udp packet ; virtual header udp_vh = udp_outp - 12 @@ -94,7 +94,9 @@ port: .res 2 .code -; initialize udp +; initialize udp +; inputs: none +; outputs: none udp_init: lda #0 sta udp_cbcount @@ -103,7 +105,15 @@ udp_init: rts -; process incoming udp packet +;process incoming udp packet +;inputs: +; eth_inp: should contain an ethernet frame encapsulating an inbound udp packet +;outputs: +; carry flag set if any error occured (including if no handler for specified port +; was found) +; carry flag clear if no error +; if handler was found, an outbound message may be created, overwriting eth_outp + udp_process: lda udp_cbcount ; any installed udp listeners? beq @drop @@ -134,8 +144,12 @@ udp_process: rts -; add an udp listener -; udp port in A/X, vector in udp_callback +;add a udp listener +;inputs: +; udp_callback: vector to call when udp packet arrives on specified port +; AX: set to udp port to listen on +;outputs: +; carry flag set if too may listeners already installed, clear otherwise udp_add_listener: sta port stx port + 1 @@ -173,8 +187,12 @@ udp_add_listener: rts -; remove an udp listener -; udp port in A/X +; remove an udp listener +; inputs: +; AX = port to stop listening on +; outputs: +; carry flag clear of handler found and removed +; carry flag set if handler for specified port not found udp_remove_listener: sta port stx port + 1 @@ -219,14 +237,15 @@ udp_remove_listener: rts -; send udp packet -; -; but first: -; -; set destination address -; set source port -; set destination port -; set length +;send udp packet +;inputs: +; udp_send_dest: destination ip address (4 bytes) +; udp_send_dest_port: destination port (2 bytes) +; udp_send_src_port: source port (2 bytes) +; udp_send_len: length of data to send (exclusive of any headers) +; AX: pointer to buffer containing data to be sent +;outputs: +; carry flag is set if an error occured, clear otherwise udp_send: stax copy_src ; copy data to output buffer ldax #udp_outp + udp_data diff --git a/dist/make_dist.rb b/dist/make_dist.rb index 34ef54e..ccdaf73 100644 --- a/dist/make_dist.rb +++ b/dist/make_dist.rb @@ -1,3 +1,7 @@ + +$:.unshift(File.dirname(__FILE__)) unless + $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) + gem 'archive-zip' require 'archive/zip' require 'ftools' @@ -22,5 +26,6 @@ end File.copy(src,dest) end + zipfile_name=File.dirname(__FILE__)+"/netboot65-#{Time.now.strftime("%Y-%m-%d")}.zip" Archive::Zip.archive(zipfile_name, WORKING_DIR) diff --git a/dist/make_dist_ip65.rb b/dist/make_dist_ip65.rb index 1eeee55..1d41139 100644 --- a/dist/make_dist_ip65.rb +++ b/dist/make_dist_ip65.rb @@ -1,3 +1,7 @@ + +$:.unshift(File.dirname(__FILE__)) unless + $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) + gem 'archive-zip' require 'archive/zip' require 'ftools' @@ -18,7 +22,8 @@ end ["client/drivers/*.[s|i]","drivers/"], ["client/drivers/Makefile","drivers/"], ["client/cfg/*","cfg/"], - ["doc/ip65.html","doc/"], + ["doc/ip65.html","doc/index.html"], + ["doc/ca65-doc*.*","doc/"], ["client/Makefile","/"], ].each do |args| dest="#{WORKING_DIR}/#{args[1]}" @@ -32,6 +37,10 @@ dummy_makefile=File.new("#{WORKING_DIR}/clients/Makefile","w") dummy_makefile<<"#dummy makefile, so we can reuse the top level Makefile from the netboot65/clients directory\nall:\n" dummy_makefile.close +require 'document_ca65_source_as_html.rb' +codebase_dir=WORKING_DIR +output_dir="#{WORKING_DIR}/doc" +codebase_title='ip65' +document_ca65_source_as_html(codebase_dir,codebase_title,output_dir) zipfile_name=File.dirname(__FILE__)+"/ip65-#{Time.now.strftime("%Y-%m-%d")}.zip" Archive::Zip.archive(zipfile_name, WORKING_DIR) - diff --git a/doc/ip65.html b/doc/ip65.html index 4ce8a5c..ffd7039 100644 --- a/doc/ip65.html +++ b/doc/ip65.html @@ -137,6 +137,11 @@ IP65 is a TCP/IP stack for 6502 based computers. +

Documentation

+ +

Download

@@ -148,7 +153,8 @@ IP65 is a TCP/IP stack for 6502 based computers.
   Release	Maintainer	Changes
   -------	----------	-------
-  2009-03-08	Jonno Downes	Added DHCP, DNS & TFTP
+  2009-03-15	Jonno Downes	Added technical reference documentation
+  2009-03-15	Jonno Downes	Added DHCP, DNS & TFTP
   2009-01-22	Per Olofsson	Added copymem fix from Jonno Downes. Added MPL license.
   2008-09-27	Per Olofsson 	Added timeout fix for ineth_tx from David Schmidt.
   2006-09-20	Per Olofsson 	Fixed checksum calculation for odd packet sizes.
@@ -208,5 +214,10 @@ copy:
 This project is released under the Mozilla Public License Version 1.1.
 For details, please visit http://www.mozilla.org/MPL/.
 
+

+ +Project Web Hosted by SourceForge.net + +