From 8eac97e4bf32bf355e3334fb3cac0f93003574b1 Mon Sep 17 00:00:00 2001 From: jonnosan Date: Sun, 12 Jul 2009 10:43:42 +0000 Subject: [PATCH] git-svn-id: http://svn.code.sf.net/p/netboot65/code@156 93682198-c243-4bdb-bd91-e943c89aac3b --- client/cfg/c64prg.cfg | 2 +- client/inc/nb65_constants.i | 8 +++--- client/ip65/tcp.s | 36 +++++++++++++++------------ client/nb65/nb65_c64.s | 4 ++- client/nb65/nb65_version.i | 2 +- client/test/Makefile | 6 ++--- dist/version_number.txt | 2 +- doc/C64 gopher client.txt | 33 ++++++++++++++++++++++++ doc/ip65.html | 3 ++- doc/nb65_api_technical_reference.doc | Bin 130560 -> 128000 bytes 10 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 doc/C64 gopher client.txt diff --git a/client/cfg/c64prg.cfg b/client/cfg/c64prg.cfg index 9cd8359..a9ef89f 100644 --- a/client/cfg/c64prg.cfg +++ b/client/cfg/c64prg.cfg @@ -1,7 +1,7 @@ MEMORY { ZP: start = $02, size = $1A, type = rw, define = yes; IP65ZP: start = $5f, size = $10, type = rw, define = yes; - RAM: start = $07FF, size = $58ab, define = yes, file = %O; + RAM: start = $07FF, size = $77ab, define = yes, file = %O; DISCARD: start = $77FF, size = $10, define = yes; } SEGMENTS { diff --git a/client/inc/nb65_constants.i b/client/inc/nb65_constants.i index 7c15183..ffb646b 100644 --- a/client/inc/nb65_constants.i +++ b/client/inc/nb65_constants.i @@ -33,7 +33,7 @@ NB65_SEND_UDP_PACKET EQU $12 ;inputs: AX points to a UDP packet parame NB65_UDP_REMOVE_LISTENER EQU $13 ;inputs: AX contains UDP port number that listener will be removed from NB65_TCP_CONNECT EQU $14 ;inputs: AX points to a TCP connect parameter structure, outputs: A = connection # -NB65_TCP_SEND_PACKET EQU $15 ;inputs: AX points to a TCP send parameter structure, outputs: none packet is sent +NB65_SEND_TCP_PACKET EQU $15 ;inputs: AX points to a TCP send parameter structure, outputs: none packet is sent NB65_TCP_CLOSE_CONNECTION EQU $16 ;inputs: A = connection # to close, outputs: none NB65_TFTP_SET_SERVER EQU $20 ;inputs: AX points to a TFTP server parameter structure, outputs: none @@ -88,9 +88,8 @@ NB65_TCP_PORT EQU $04 ;2 byte port number (to li NB65_TCP_CALLBACK EQU $06 ;2 byte address of routine to be called whenever a new packet arrives ;offsets in TCP send parameter structure -NB65_TCP_CONNECTION_NUMBER EQU $00 ;1 byte connection number for previously set up connection -NB65_TCP_PAYLOAD_LENGTH EQU $01 ;2 byte length of payload of packet (after all ethernet,IP,UDP/TCP headers) -NB65_TCP_PAYLOAD_POINTER EQU $03 ;2 byte pointer to payload of packet (after all headers) +NB65_TCP_PAYLOAD_LENGTH EQU $00 ;2 byte length of payload of packet (after all ethernet,IP,UDP/TCP headers) +NB65_TCP_PAYLOAD_POINTER EQU $02 ;2 byte pointer to payload of packet (after all headers) ;offsets in TCP/UDP packet parameter structure NB65_REMOTE_IP EQU $00 ;4 byte IP address of remote machine (src of inbound packets, dest of outbound packets) @@ -99,7 +98,6 @@ NB65_LOCAL_PORT EQU $06 ;2 byte port number of loc NB65_PAYLOAD_LENGTH EQU $08 ;2 byte length of payload of packet (after all ethernet,IP,UDP/TCP headers) ; in a TCP connection, if the length is $FFFF, this actually means "end of connection" NB65_PAYLOAD_POINTER EQU $0A ;2 byte pointer to payload of packet (after all headers) -NB65_CONNECTION_NUMBER EQU $0C ;1 byte "connection number" (valid for TCP connections only) ;error codes (as returned by NB65_GET_LAST_ERROR) NB65_ERROR_PORT_IN_USE EQU $80 diff --git a/client/ip65/tcp.s b/client/ip65/tcp.s index 5ce5d77..0632b10 100644 --- a/client/ip65/tcp.s +++ b/client/ip65/tcp.s @@ -1,4 +1,10 @@ ;TCP (transmission control protocol) functions +;NB to use these functions, you must pass "-DTCP" to ca65 when assembling "ip.s" +;otherwise inbound tcp packets won't get passed in to tcp_process +;currently only a single outbound (client) connection is supported +;to use, first call "tcp_connect" to create a connection. to send data on that connection, call "tcp_send". +;whenever data arrives, a call will be made to the routine pointed at by tcp_callback. + MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 seconds (1+2+3+4+5+6+7+8)/4 @@ -12,7 +18,6 @@ MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 sec .export tcp_init .export tcp_process -.export tcp_listen .export tcp_connect .export tcp_callback .export tcp_connect_ip @@ -21,7 +26,7 @@ MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 sec .export tcp_inbound_data_ptr .export tcp_inbound_data_length -.export tcp_ack_number + .import ip_calc_cksum .import ip_send @@ -113,22 +118,22 @@ tcp_ack_number: .res 4 tcp_data_ptr: .res 2 tcp_data_len: .res 2 tcp_send_data_ptr: .res 2 -tcp_send_data_len: .res 2 -tcp_callback: .res 2 +tcp_send_data_len: .res 2 ;length (in bytes) of data to be sent over tcp connection +tcp_callback: .res 2 ;vector to routine to be called when data is received over tcp connection tcp_flags: .res 1 -tcp_inbound_data_ptr: .res 2 -tcp_inbound_data_length: .res 2 - +tcp_inbound_data_ptr: .res 2 ;pointer to data just recieved over tcp connection +tcp_inbound_data_length: .res 2 ;length of data just received over tcp connection +;(if this is $ffff, that means "end of file", i.e. remote end has closed connection) tcp_connect_sequence_number: .res 4 ;the seq number we will next send out tcp_connect_expected_ack_number: .res 4 ;what we expect to see in the next inbound ack tcp_connect_ack_number: .res 4 ;what we will next ack tcp_connect_last_received_seq_number: .res 4 ;the seq field in the last inbound packet for this connection tcp_connect_last_ack: .res 4 ;ack field in the last inbound packet for this connection -tcp_connect_local_port: .res 2 +tcp_connect_local_port: .res 2 ; tcp_connect_remote_port: .res 2 -tcp_connect_ip: .res 4 +tcp_connect_ip: .res 4 ;ip address of remote server to connect to tcp_timer: .res 1 @@ -137,6 +142,10 @@ tcp_packet_sent_count: .res 1 .code +; initialize tcp +;called automatically by ip_init if "ip.s" was compiled with -DTCP +; inputs: none +; outputs: none tcp_init: rts @@ -473,11 +482,6 @@ tcp_send_packet: jmp ip_send ; send packet, sec on error -;listen on the tcp port specified -; tcp_callback: vector to call when data arrives on specified port -; AX: set to tcp port to listen on -tcp_listen: - rts check_current_connection: ;see if the ip packet we just got is for a valid (non-closed) tcp connection @@ -516,14 +520,14 @@ check_current_connection: clc rts -tcp_process: ;process incoming tcp packet +;called automatically by ip_process if "ip.s" was compiled with -DTCP ;inputs: ; eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet ;outputs: ; none but if connection was found, an outbound message may be created, overwriting eth_outp ; also tcp_state and other tcp variables may be modified - +tcp_process: lda #tcp_flag_RST bit tcp_inp+tcp_flags_field diff --git a/client/nb65/nb65_c64.s b/client/nb65/nb65_c64.s index 035b482..6cdd5a3 100644 --- a/client/nb65/nb65_c64.s +++ b/client/nb65/nb65_c64.s @@ -587,7 +587,9 @@ netboot65_msg: main_menu_msg: .byte 13," MAIN MENU",13,13 .byte "F1: TFTP BOOT" -.if !(BANKSWITCH_SUPPORT=$03) +.if (BANKSWITCH_SUPPORT=$03) +.byte " F3: GOPHER" +.else .byte " F3: BASIC" .endif .byte 13 diff --git a/client/nb65/nb65_version.i b/client/nb65/nb65_version.i index a0b445f..20597ad 100644 --- a/client/nb65/nb65_version.i +++ b/client/nb65/nb65_version.i @@ -1 +1 @@ -.byte "0.9.8" +.byte "0.9.14" diff --git a/client/test/Makefile b/client/test/Makefile index 46e9d42..150558a 100644 --- a/client/test/Makefile +++ b/client/test/Makefile @@ -21,7 +21,7 @@ INCFILES=\ all: \ ip65test.dsk \ testdns.prg \ - test_gopher.prg \ + gopher_browser.prg \ testdns.pg2 \ testtftp.prg \ testtftp.pg2\ @@ -42,8 +42,8 @@ all: \ test_tcp.prg: test_tcp.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg $(LD) -m test_tcp.map -vm -C ../cfg/c64prg.cfg -o test_tcp.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) -test_gopher.prg: test_gopher.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg - $(LD) -m test_gopher.map -vm -C ../cfg/c64prg.cfg -o test_gopher.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) +gopher_browser.prg: gopher_browser.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg + $(LD) -m gopher_browser.map -vm -C ../cfg/c64prg.cfg -o gopher_browser.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) %.pg2: %.o $(IP65LIB) $(APPLE2PROGLIB) $(INCFILES) ../cfg/a2bin.cfg diff --git a/dist/version_number.txt b/dist/version_number.txt index b5d0ec5..69010fa 100644 --- a/dist/version_number.txt +++ b/dist/version_number.txt @@ -1 +1 @@ -0.9.8 \ No newline at end of file +0.9.14 \ No newline at end of file diff --git a/doc/C64 gopher client.txt b/doc/C64 gopher client.txt new file mode 100644 index 0000000..bf16e2b --- /dev/null +++ b/doc/C64 gopher client.txt @@ -0,0 +1,33 @@ +C64 gopher browser v0.1 + + +REQUIREMENTS + 1) RR-NET or compatible + 2) DHCP server on LAN + +USAGE + +When run, the program will use DHCP to assign itself an IP address, default gateway and DNS server address. + +You will then be prompted for the address of a gopher server. you can enter a hostname or an ip address. If +you just hit enter at this prompt, "gopher.floodgap.com" will load. NB - in the current version you can only +enter a server name, not a specific resource on that server, or a port (the default gopher port 70 is used). + +Once a gopher resource is loaded, the following keys are active: + +SPACE, F7 or down arrow : scroll down to next page +F1 or up arror : scroll up to previous page +F2 : show recently visited resource +F3 or left arrow : go back to last visited resource +F5 : prompt to enter a new server (nb server name only - not resource or port) +RUN/STOP : quit + +If the page currently being displayed has links to further resources in it, each link will be displayed +starting with a highlighted letter (e.g. the first link on a page will have an inverse "A" next to it, +the 2nd link on a page will have an inverse "B" next to it etc). Press the letter assigned to the link +will load up that resource. + +Currently only Text and Directory resources are handled. + +jonno @ jamtronix.com +2009-07-11 \ No newline at end of file diff --git a/doc/ip65.html b/doc/ip65.html index 53dfb08..9270d01 100644 --- a/doc/ip65.html +++ b/doc/ip65.html @@ -89,7 +89,7 @@ IP65 is a TCP/IP stack for 6502 based computers. Services - TCP + TCP DHCP/DNS/TFTP Echo @@ -153,6 +153,7 @@ IP65 is a TCP/IP stack for 6502 based computers.
   Release	Maintainer	Changes
   -------	----------	-------
+  2009-07-12	Jonno Downes	Initial TCP implementation (use -DTCP to include)
   2009-03-21	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.
diff --git a/doc/nb65_api_technical_reference.doc b/doc/nb65_api_technical_reference.doc
index 2899433526a81de960869a90a518385a08dd8e12..5643bd96829b4fedeafb851f24d8cd86c2acee8a 100644
GIT binary patch
delta 11757
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

X|!as(}Zoyvwd8>blrhgi%Ltzz-r0< zEDuLYFFkx3Bap-MZBaE!O8y+{lR~PrcLt=*H^a4?qCDG~Qkj2=tuKjIe~>=Vi*}7h zZ@h|ONQ!pu>Wp7@sFFL%SX)-jl3o59>yX~i43TJ$7w{rV*EWjvRE)+1e1yr!LN;cg z5Z5u0FXKm;jOob19sC=25y^<|6^uk?43BVQSQv-#co*+sA~xbj9Kms%f!2p^4mXs6 zJ3QbGf3!dpTH+N9!2+zqCEUGv>F%YwXOI1~fA=<~{ksdBY};M9o@w{~{M`I+^1sQ= z&t2r2^3U#`J9~FFxh1n)U-Z!@>gx+nwDYI;_b*pW@0U;M_164$%tO8CP`Fqf zPo8xd4KMLKCZl1kO%l(GeS1I<68+D~=sl?uovATwQ6tt%rX} z$)97r`;scr?vKG(sC!>%q=%jJ)Q{zTtnWKl-BxTBIPJZV3}4}Ee2ZUn@9T~9wimv! z&8_XK^>n~5FVXI}2fvp!trh|ij#e-00he~#dIUIgUq0=SmbG%1AxBMErCPJx2?H@0 zL+}PhA_*DTgMHYKLny*6JV5on)P*pFqZj(1FJ3`LU%K#U7T(5KWMMj%VGXvR2uE-f z$8ZM^P~{aKzR>`U5rMY2Uwr+->0b{P@BM+Pc<=VTh1)l7U%hPcvboc@XK$aBZBE;r zv+PObwtw>eC+}-|m)~vaDUZG!2avTF6y$X1+}#HBqXHWG`s^zd^v z^{^{zZU5y8?D^9^R*+K3Ac5LjlJVSH+pIc|+e;Fx2ukE6Qc>uP7yHs)4H*&6v30du zZRYbLDOM~cj^Dy{3{-v1m62nRgyqPC#Iq{#ab>*lg}&{MulppqT^n_+gYl6Z64e^; z$7qx)QS2(wG0-aEkeIi}v*?Ba7=cWTfddQi6~4uCC~kgr*0ho`S_^=dw5THG5>Ox)7x7neqV!kmT6JqGJl9`w-T9*lJx zTsv5=|9cDH2#K7W|2RktMsw_?B6-&p$%~I8=_5Jh?8m_YiNIf!$f{?>-p7hj4mlTM z?_ZZ_8)TITkQ}8WDv_*$ItY2q)!D{ouTz1}u5rdy^zv!%5VAjZFv7}_{o<3^{Uq6HZE58OXpZ*%UE>5#J^pZ?ERR4rK+dbYvByJIvl$Ia_WBG5 z*`6X+2a(P(2CmqjTVCO2o!OfCP3aoqDt4@vY}3j}KIuf`F#-Abi*nZYP+@Nn=^A7Lnv zA3VSJ zAk+3OYqzXgvC417sx3>mEZ&min!cE^Wop*ctVvm)WEpL|)hy$!0+x=ljq$_=5Z& z*`y@>1-5rbFN}1huPBv1lI=-I#RN0`o(e4~K7q{>A-!XYtN3rF(xncks+D)idq>X|<<4<2NAbs#;dsaf7k52p7?uZ_eg zSNfw;>7&@5j0}86dK1cLHG5n>;4`*cy;yp1Km37z4PneQM2@GS(I!kaFp|U6m$u+Y zXUT9?=#aks2vx{A2~uJP7T_N4WBBXr9hHajAc=J z@q(o%m#&?=dF|}AT!oC-*2>3tx3%hO>}sv*4y;%)&Af#U?0wsu?{AiA&byDjZq6?b zbPlB#*#PaC%m1hT~Y{yHIy>e*2d039SLk+JeI@Vh)9vAnz z%XVpE51RJG8`6=^7&qIfTDCG#C7DjR+Wso@2E&}a+bEUZK-wK)aF27QH)>f@-krYS z0UxY%rGHnF-gG6~rN88XKBMix6uN}fRcg@8RyUC5|G?<*oT_Q`cusAx#k6wnr?V;? zB5A83-9oy<1&mCfKD?gD>$5kxWZ-LTLGeiXIDSmx-8@<*bG5@B>_wdv?nSVr@&uZy zr>6NE>$@mF^CN#*7xj{INKYGv(Krv;We;|jh#ahd?EV^ha|lBqhcXHiaT#Sfl=7&E z5OjweY6jlHNB9)hp?|`m?!pgAe7)q@<(QXZH>_i~aom0w;5vs`=4nWW;(VOv#Mk8Z z{To-$F`Z)Uc<{%AJGK^V-Ep;GL&2(o6-yT{U9foW?8VcwW%}&n53=9odtv?YOnlmC z)=hnAZ0e>u8lkOKod5=wx&FMGAno(2b9r7KHF%QQ>17_i`UN_)QSg%bIzMHg8la4T zLF#My_$oiSzxt_i{+A=ve4BjUQ$~2KT9W^5it@4&kOlJ>FTol zBp$K)Z)$T{6`No9cQwY#!;P3%;uVR}X{xfDKV8d@g!0>my!e&Zdh#wr9wy~^Kt?xm zv6L2dBS5V=&(7$AuIL7e$Qucb>@rBv_~K~vQ;nEvWAGmSftgqc&4X8Z80(=KC#R|w z4*8Y7N~mW3UM|0=%5R|ZbD8{>CBH)LYUTVPa9w-n?*#H@Uf!Y0>u-7YDsN2Xou<5R zlhW4!5XiGWPwfs(p2p>2TOJAJX-^*HG_1znH1?#$g1u<$MT;{& zzsPNK!$sw99KEP&7@^aZz3ksf`Pis?Np&-(PggZOb6B5;`B;#@Y`O|kjxt1}M=amo z1UB-gV8fzkG;J=C85!$Ld-N!Pt>v_e75YYbkmG1!y>jI=PuJS4ZL;R&TJty4Dp_e; zB(1VlZj-EeTR9zDWwnZxb+fEhwbsn6)vSVMP9Lk__mb1swKm9Ht8SGu*J@bh%(a?U zIrBjMta618siu}y(A}N6j;|TRF|Ox>io}lme|ZbFH3L zuAts4>^{h~I;%hqCfF)yuGO~+nrjWLf@U=#uC+spLTQ+WuC=~jhibsdcYKXvCn&bk=8=BS>kjInW!s$;a6tC~AL zwexs}h!Ec8AUBLhJFJalkcQfA8R?;1JAQJ&hE7~(5WrV581ggR0Laf4AHv30P~OgI z9IQOPY(xRh;ygSFTqQ`9TMyRSX#wa~Pu1z_E;aqX zRF$7NUsbK{Gl5G)8LITaS*e>phw=G(^{jDdy_#Qc#lQgVMZf%yHmEc;b)y<@OxUPC zHY#pXsm2GJ)FLCIK(#WG3e*mx)n*l9#BEjyUF05D`U>=@qbIwm$2kctVvJ33Wipq! z_Q<^B2LEUv+xx6~W{G&>|O6C$*S!=2cU(t;@m(-vd1*~mGA+}&E%+zX^RfgjE}ik4xxUs3VTr2cW%{NL3NjLa*_QEm}sPPa1d mUQzFq`<~^hU5t0GDu;?T?p{^zs0&8sHP)`@-@c|Ql>Kj;*MQLg