/**************************************************************************** OS/A65 Version 2.0.0 Multitasking Operating System for 6502 Computers Copyright (C) 1989-1998 Andre Fachat This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************/ /* * This file is a TCP implementation for 6502 computer * * it exports: * tcpinit - init TCP * tcploop - must be called * tcprx - gets incoming packets * * tcp_open - (active) open TCP connection * tcp_listen - (passive) open TCP connection * tcp_close - close TCP connection * * tcp_kill - kill random connection because we ran into * memory problems * */ /* TODO: make the thing 32-bit-modulo save for ack and seq! */ /* TODO: retransmission timeout increase with retransmission */ /* #define DEBUGTCP */ #ifdef DEBUGTCP #define DBTCP(a) DB(a) #else #define DBTCP(a) #endif .( .zero tcbp .word 0 p2 .word 0 srv =p2 ;srv =syszp+4 .bss dlen .word 0 ; length tcp data received doffset .byt 0 ; data offset from pp! ;toffset =sysmem+3 ; tcp header offset from pp! .byt 0 ; ??? seg_flag .byt 0 ; incoming segment flag byte conn .byt 0 ; connection number discd .word 0 ; data preceding valid data in segment timer .word 0 ; timer counter &&tcb .dsb MAXCONN*TCB_LEN tmp .byt 0 tmp2 .dsb 3 ; should be able to hold seq number state .byt 0 scnt .byt 0 ; start counter for loop .text &&tcpinit .( ldx #0 stx scnt stx timer stx timer+1 i1 jsr settcb lda #TCP_CLOSED ldy #TCB_STATE sta (tcbp),y inx cpx #MAXCONN bcc i1 jsr user_init clc rts .) &&tcploop .( .bss fl .byt 0 .text /* I have - yet - no way to measure the time, so we do a simple loop */ lda timer ora timer+1 sta fl beq load lda timer bne l1 dec timer+1 l1 dec timer jmp te end rts load lda #<TIMER sta timer lda #>TIMER sta timer+1 te ldx scnt inx cpx #MAXCONN bcc i1c ldx #0 i1c stx scnt jmp doit i1 inx cpx #MAXCONN bcc i1a ldx #0 i1a cpx scnt beq end ;------------- doit stx conn jsr settcb ldy #TCB_STATE lda (tcbp),y cmp #TCP_CLOSED beq i1 ldy #TCB_LSTATE cmp (tcbp),y beq same sta (tcbp),y ; ok, state has changed since last loop cmp #TCP_CLOSEW bne n1 lda #TE_SIG_FIN jsr signal_user n1 same lda fl bne noti ldy #TCB_MSL+1 lda (tcbp),y dey ora (tcbp),y beq nomsl sec lda (tcbp),y sbc #1 sta (tcbp),y tax iny lda (tcbp),y sbc #0 sta (tcbp),y bne nomsl txa bne nomsl ldy #TCB_STATE lda (tcbp),y cmp #TCP_FINW2 beq lastack cmp #TCP_LASTACK beq lastack cmp #TCP_TIMEW bne nomsl lastack DBTCP("lastack^m^j") jsr tclose jmp noretrans nomsl ldy #TCB_RETRANS+1 lda (tcbp),y dey ora (tcbp),y beq noretrans sec lda (tcbp),y sbc #1 sta (tcbp),y tax iny lda (tcbp),y sbc #0 sta (tcbp),y bne noretrans txa bne noretrans jsr retransmit ; jmp noti noretrans noti ldy #TCB_FLAG lda (tcbp),y beq noqueuedfin ;inc $d020 jsr send_flag bcs noqueuedfin ldy #TCB_FLAG lda #0 sta (tcbp),y noqueuedfin ldy #TCB_STATE lda (tcbp),y cmp #TCP_ESTAB bcc next cmp #TCP_CLOSEW+1 bcs next ldy #TCB_SERVICE lda (tcbp),y sta srv iny lda (tcbp),y sta srv+1 ldy #SRV_LOOP lda (srv),y iny ora (srv),y beq next jsr end2 next ldx conn jmp i1 end2 lda (srv),y pha dey lda (srv),y pha rts .) &&tcprx2 .( jsr getpinfo bcc piok DBTCP("getpi error in tcprx2^m^j") rts piok jsr ip2tcp #if 0 /*def DEBUGTCP*/ DB("TCP check: pd=") lda pd+1: jsr EHexout: lda pd: jsr EHexout DB(", pdl=") lda pdl+1: jsr EHexout: lda pdl: jsr EHexout DB(", pp=") lda pp+1: jsr EHexout: lda pp: jsr EHexout DB(", ppl=") lda ppl+1: jsr EHexout: lda ppl: jsr EHexout jsr ECrlfout #endif lda pdl ldy pdl+1 ldx #pd jsr checksum3 #if 0 /*def DEBUGTCP*/ php pha txa pha DB("TCP Checksum=") pla tay jsr EHexout pla pha jsr EHexout jsr ECrlfout tya tax pla plp #endif bcc tcpok tdisc_check DB("Packet discarded: TCP checksum^m^j") &tdisc ldx pslot bmi illtd jsr bfree lda #<-1 sta pslot clc rts illtd DBTCP("tdisc with illegal slot!^m^j") sec rts tcpok /* here we have a valid TCP packet in (pd),0-pdl. */ #if 1 /* def DEBUGTCP2 */ DB("Receiving: ") ldy #TH_DOFFSET+1 lda (pd),y:jsr EHexout:DB(" ack=") ldy #TH_ACK+2:lda (pd),y:jsr EHexout:iny:lda (pd),y:jsr EHexout DB(" seq=") ldy #TH_SEQ+2:lda (pd),y:jsr EHexout:iny:lda (pd),y:jsr EHexout jsr ECrlfout #endif lda #0 ; data to be discarded at packet beginning. sta discd sta discd+1 ldy #TH_FLAG lda (pd),y sta seg_flag ldy #TH_DOFFSET lda (pd),y and #$f0 lsr lsr clc adc #TPH_LEN sta doffset ; data offset from pseudo header start lda pdl sec sbc doffset sta dlen lda pdl+1 sbc #0 sta dlen+1 ; TCP data received length lda phl sec sbc #TPH_LEN sta phl ; offset from pp to pd clc adc doffset sta doffset ; offset bcc looktcb datl DB("Data offset too large^m^j") tdisc1 jmp tdisc /* we now also have: phl = offset from pp to TCP pseudo header (pd) doffset= offset from pp to TCP data start dlen = length of TCP data received discd = TCP data to be discarded */ looktcb /* DB("LookTCB^m^j")*/ jsr findtcb bcc found /* TODO: send reset on no connection? */ jmp tcp_closed found stx conn #ifdef DEBUGTCP2 lda #" " jsr ECout txa jsr EHexout lda #" " jsr ECout lda tcbp+1 jsr EHexout lda tcbp jsr EHexout jsr ECrlfout #endif #ifdef DEBUGTCP DB("rP=") ldy #TH_SRCP lda (pd),y jsr EHexout iny lda (pd),y jsr EHexout DB(" tcbp=") lda tcbp+1 jsr EHexout lda tcbp jsr EHexout DB(" s=") ldy #TH_SEQ+2 lda (pd),y jsr EHexout iny lda (pd),y jsr EHexout DB(" a=") ldy #TH_ACK+2 lda (pd),y jsr EHexout iny lda (pd),y jsr EHexout DB(" f=") ldy #TH_FLAG lda (pd),y jsr EHexout jsr ECrlfout #endif ldy #TCB_STATE lda (tcbp),y sta state #if 0 /*def DEBUGTCP*/ pha lda tcbp+1 jsr EHexout lda tcbp jsr EHexout lda #":" jsr ECout lda state asl tax lda ttab+1,x tay lda ttab,x jsr ETxtout pla jmp noout ttab .word tclosed, tlisten, tsynrxd, tsyntxd, testab, tfinw1, tfinw2 .word tclosew, tlastack, tclosing, ttimew tclosed .asc "Closed:^m^j",0 tlisten .asc "Listen:^m^j",0 tsynrxd .asc "SynRXd:^m^j",0 tsyntxd .asc "SynTXd:^m^j",0 testab .asc "Established:^m^j",0 tfinw1 .asc "FinW1:^m^j",0 tfinw2 .asc "FinW2:^m^j",0 tclosew .asc "CloseWait:^m^j",0 tlastack .asc "LastAck:^m^j",0 tclosing .asc "Closing:^m^j",0 ttimew .asc "TimeWait^m^j",0 noout #endif cmp #TCP_CLOSED beq tcp_closed cmp #TCP_LISTEN bne nolisten jmp tcp_listen2 nolisten cmp #TCP_SYNTXD bne nosyntxd jmp tcp_syntxd nosyntxd jmp tcp_else .) tcp_closed .( lda seg_flag and #THF_RST bne end lda seg_flag and #THF_ACK bne ackset jsr seql2ack jsr clrseq lda #THF_ACK + THF_RST bne noack &send_rst &listen_ack ackset jsr ack2seq lda #THF_RST noack ldy #TH_FLAG sta (pd),y jmp bangbuf end jmp tdisc .) tcp_listen2 .( ldy #TCB_STATE lda #TCP_CLOSED sta (tcbp),y ; if error, release tcb lda seg_flag and #THF_RST beq norst DB("RST while listening^m^j") jmp tdisc norst lda seg_flag and #THF_ACK beq noack DB("ACK while listening^m^j") jmp listen_ack noack lda seg_flag and #THF_SYN bne listen_syn DB("Listen but no Syn!^m^j") jmp tdisc listen_syn DBTCP("Listen_syn^m^j") jsr seqsyn jsr srv_open jsr hasdata beq nodat DB("rxd syn has data!^m^j") jsr copy_n_queue nodat jsr iniseq ldy #TCB_FLAG lda #THF_SYN + THF_ACK sta (tcbp),y ldy #TCB_STATE lda #TCP_SYNRXD sta (tcbp),y sta state ;inc $d020 jmp tdisc ; ldy #TH_FLAG ; lda #THF_SYN + THF_ACK ; sta (pd),y ; ; jsr iniseq ; jsr setseq ; jsr setack ; ; ldy #TCB_STATE ; lda #TCP_SYNRXD ; sta (tcbp),y ; sta state ; ; lda #1 ; ldx #0 ; jsr addtxnxt ; ; jmp bangbuf .) tcp_else .( ; .bss ;&needack .byt 0 ; .text ; ; lda #0 ; sta needack /* DB("tcp_*: ip=") lda pp+1:jsr EHexout:lda pp:jsr EHexout DB(", qslot=") lda pslot:jsr EHexout DB(", sla=") lda slotladr:jsr EHexout jsr ECrlfout */ /* first, check sequence number */ jsr checkseq bcc seqok lda seg_flag and #THF_RST bne disca /* discard packet */ DB("bad SEQ!^m^j") jsr setseq jsr setack ldy #TH_FLAG lda #THF_ACK sta (pd),y jmp bangbuf disca DB("got RST with bad SEQ!^m^j") jmp tdisc seqok /* second, check the RST bit */ lda seg_flag and #THF_RST beq noreset DB("got RST!^m^j") lda state cmp #TCP_SYNRXD bne seq1 /* TODO: if from active open, then close! */ DB("Reset rxd!^m^j") ldy #TCB_STATE lda #TCP_SYNRXD sta (tcbp),y sta state jsr tdisc /* signal_user may use q* variables! */ lda #TE_SIG_RESET jmp signal_user seq1 ; cmp #TCP_LASTACK ; bcs seq2 ; ; /* flush and close everything */ ; jsr tdisc ; ; /* signal_user may use q* variables! */ ; ;lda #TE_RESET1 ; ;jmp signal_user ;DBTCP("seq1^m^j") ; jmp tclose ; ;seq2 DBTCP("seq2^m^j") jsr tdisc jmp tclose noreset /* third, check security and precedence... */ /* fourth, check SYN bit */ lda seg_flag and #THF_SYN beq nosyn ; this is an error - abort anything DB("got SYN!^m^j") /* flush everything, close */ jsr send_rst ;lda #TE_RESET2 ;jmp signal_user jmp tclose nosyn /* fifth, check the ACK bit */ lda seg_flag and #THF_ACK bne ackok DB("No Ack^m^j") jmp tdisc ackok jsr checkack sta tmp #ifdef DEBUGTCP DBTCP("checkack returns "): lda tmp: jsr EHexout: jsr ECrlfout #endif lda state cmp #TCP_SYNRXD bne ack1 lda tmp beq badack cmp #3 bcc ackok2 badack DB("Bad Ack in packet^m^j") jmp send_rst ackok2 ldy #TCB_STATE /* everything ok -> enter estab and continue... */ lda #TCP_ESTAB sta (tcbp),y sta state /* TODO: check queued FIN */ ack1 lda tmp beq aignore cmp #3 bcc a0 /* TODO: ack ahead send_next -> send ack, return */ #ifdef DEBUGTCP DB("Ack ahead send_next^m^jpkt.ack=") ldy #TH_ACK:ldx #4:x0 lda (pd),y:jsr EHexout:iny:dex:bne x0 DB(" seq.sndnxt=") ldy #TCB_SND_NXT: ldx #4:x1 lda (tcbp),y: jsr EHexout: iny: dex: bne x1 jsr ECrlfout #endif jmp tdisc a0 jsr getuna lda tmp pha jsr ackxmit pla sta tmp aignore lda state cmp #TCP_FINW1 bcc do6a bne ack3 lda tmp cmp #2 ; exactly SND_NXT -> FIN ack'd bne do6a ldy #TCB_MSL lda #<TIME_FINW2 sta (tcbp),y iny lda #>TIME_FINW2 sta (tcbp),y ldy #TCB_STATE lda #TCP_FINW2 sta (tcbp),y sta state ack3 cmp #TCP_FINW2 bne ack4 do6a jmp do6 ack4 cmp #TCP_CLOSING bne ack5 lda tmp cmp #2 bne atw ldy #TCB_STATE lda #TCP_TIMEW sta state sta (tcbp),y ldy #TCB_MSL lda #<TIME_MSL sta (tcbp),y iny lda #>TIME_MSL sta (tcbp),y atw jmp tdisc ack5 cmp #TCP_LASTACK bne ack6 /* DB("LAST ACK received^m^j") */ lda tmp cmp #2 bne atw jsr tdisc jmp tclose ack6 cmp #TCP_TIMEW bne do6 ldy #TCB_MSL lda #<TIME_MSL sta (tcbp),y iny lda #>TIME_MSL sta (tcbp),y ; ack retransmitted FIN jsr setack jsr setseq ldy #TH_FLAG lda #THF_ACK sta (pd),y jmp bangbuf /* sixth, check the URG bit */ &do6 /* seventh, process segment text */ lda state cmp #TCP_ESTAB bcc idat cmp #TCP_FINW2+1 bcs idat /* TODO: discd? */ ldy #0 lda doffset sta (pp),y tya iny sta (pp),y iny lda dlen sta (pp),y iny lda dlen+1 sta (pp),y jsr hasdata beq idat jsr copy_n_queue ; ok, we need not copy without FIN... ; inc needack ldy #TCB_FLAG lda (tcbp),y ora #THF_ACK sta (tcbp),y idat /* eighth, check the FIN bit */ lda state cmp #TCP_CLOSED beq nofin cmp #TCP_LISTEN beq nofin cmp #TCP_SYNTXD beq nofin lda seg_flag and #THF_FIN beq nofin lda #1 ldx #0 jsr addrxnxt ; inc needack ldy #TCB_FLAG lda (tcbp),y ora #THF_ACK+THF_FIN sta (tcbp),y ldx state cpx #TCP_SYNRXD beq closew cpx #TCP_ESTAB bne noestab jsr flushqueue jmp closew noestab cpx #TCP_FINW1 beq closing cpx #TCP_FINW2 bne nofinw2 lda (tcbp),y and #255-THF_FIN sta (tcbp),y ldy #TCB_STATE lda #TCP_TIMEW sta state sta (tcbp),y ldy #TCB_MSL lda #<TIME_MSL sta (tcbp),y iny lda #>TIME_MSL sta (tcbp),y jmp nofin nofinw2 ; jsr setack ; jsr setseq ; ldy #TH_FLAG ; lda #THF_ACK ; sta (pd),y ; jsr bangbuf ldy #TCB_FLAG lda (tcbp),y ora #THF_ACK sta (tcbp),y lda state cmp #TCP_ESTAB+1 bcs f1 closew lda #TCP_CLOSEW .byt $2c closing lda #TCP_CLOSING fe ldy #TCB_STATE sta (tcbp),y f1 /* TODO: check other states */ nofin ldx pslot bmi noslot ; lda needack ; bne needit jmp tdisc ;needit ; jsr setack ; jsr setseq ; ldy #TH_FLAG ; lda #THF_ACK ; sta (pd),y ; jsr bangbuf ; maybe check with retransmissions... ; bcc noslot ;inc $d020 ; ldy #TCB_FLAG ; lda (tcbp),y ; ora #4 ; saved ack ; sta (tcbp),y noslot clc rts .) tcp_syntxd .( /*DB("SynTxd^m^j")*/ /* first, check ack bit */ lda seg_flag and #THF_ACK beq noack jsr checkack bcc noack badack DB("Bad Ack in packet^m^j") jmp send_rst noack /* second, check rst bit */ lda seg_flag and #THF_RST beq norst lda seg_flag and #THF_ACK beq rstack jsr tdisc ;lda #TE_RESET3 ;jmp signal_user DBTCP("noack^m^j") jmp tclose /* TODO: close? or define that user signal routine should do that... */ rstack jmp tdisc norst /* third, check security */ /* fourth, check syn bit */ lda seg_flag and #THF_SYN beq nosyn jsr getseq ; get rcv_nxt number from packet lda #1 ldx #0 jsr addrxnxt lda seg_flag and #THF_ACK beq nosynack jsr getuna jsr ackxmit nosynack jsr aheadiss bcs noiss ; SYN not yet ACK'd lda #TCP_ESTAB ldy #TCB_STATE sta (tcbp),y sta state ; lda #1 ; sta needack ldy #TCB_FLAG lda (tcbp),y ora #THF_ACK sta (tcbp),y jmp do6 noiss lda #TCP_SYNRXD ldy #TCB_STATE sta (tcbp),y ldy #TCB_FLAG lda (tcbp),y ora #THF_SYN+THF_ACK sta (tcbp),y ; jmp tdisc /* jsr setack jsr decseq ; dec snd_nxt back to ISS jsr setseq lda #1 ldx #0 jsr addtxnxt ; and inc for SYN jsr do_queue ldy #TH_FLAG lda #THF_ACK+THF_SYN jmp bangbuf */ nosyn jmp tdisc .) /* check acknowledge of packets in retransmission queue */ /* TODO: save end pointer for each packet in queue -> no calculations here */ ackxmit .( #ifdef DEBUGTCP2 DB("ackxmit:") .(:ldy #TCB_SND_UNA+2: x1 lda (tcbp),y: jsr EHexout: iny: cpy #TCB_SND_UNA+4:bcc x1: .) lda #":":jsr ECout jsr ECrlfout #endif ldy #TCB_NTXBUF lda (tcbp),y bne noend2 clc rts noend2 ldy #TCB_TXBUF lda (tcbp),y tax /*pha: DB("slot:"): pla: jsr EHexout: lda #":": jsr ECout*/ jsr getbadr bcc adrok DB("illegal slot# in ackxmit^m^j") jmp freebuf adrok sta p2 sty p2+1 ldx #3 ldy #TH_SEQ+TCP_OFFSET+3 l0 lda (p2),y sta tmp,x /*jsr EHexout*/ dey dex bpl l0 ldy #IPH_LEN+1 lda (p2),y clc adc tmp+3 sta tmp+3 dey lda (p2),y adc tmp+2 sta tmp+2 bcc l1 inc tmp+1 bne l1 inc tmp l1 lda tmp+3 sec sbc #<TCP_OFFSET+TH_OPTIONS sta tmp+3 lda tmp+2 sbc #>TCP_OFFSET+TH_OPTIONS sta tmp+2 bcs l2 lda tmp+1 bne l3 dec tmp l3 dec tmp+1 l2 #if 0 lda #":": jsr ECout .(:ldy #0: x1 lda tmp,y: jsr EHexout: iny: cpy #4:bcc x1: .) jsr ECrlfout lda #":": jsr ECout .(:ldy #TCB_SND_UNA: x1 lda (tcbp),y: jsr EHexout: iny: cpy #TCB_SND_UNA+4:bcc x1: .) jsr ECrlfout #endif ldy #TCB_SND_UNA l4 lda (tcbp),y cmp tmp-TCB_SND_UNA,y bcc end ; less than packet -> end bne freebuf ; more than packet -> ok iny cpy #TCB_SND_UNA+4 bcc l4 freebuf ; got ack for first packet in queue #ifdef DEBUGTCP2 DB("freebuf :") ldy #TCP_OFFSET+TH_SEQ+2 lda (p2),y:jsr EHexout:iny:lda (p2),y:jsr EHexout:jsr ECrlfout #endif ldy #TCB_TXBUF lda (tcbp),y tax jsr bfree ldy #TCB_NTXBUF lda (tcbp),y tax dex txa sta (tcbp),y beq end ; ok, end, nothing left ldy #TCB_TXBUF l5 iny lda (tcbp),y dey sta (tcbp),y iny dex bne l5 /* set retransmission counter to 1 to send the next buffer now */ ldy #TCB_TRIES lda #0 sta (tcbp),y ldy #TCB_RETRANS lda #1 sta (tcbp),y iny lda #0 sta (tcbp),y jmp ackxmit end rts .) .bss d2len .word 0 .text hasdata .( /* TODO: check with discd */ lda dlen ora dlen+1 rts .) bangbuf .( #ifdef DEBUGTCP DB("bangbuf: pp=") lda pp+1:jsr EHexout: lda pp:jsr EHexout DB(", pd=") lda pd+1:jsr EHexout: lda pd:jsr EHexout jsr ECrlfout #endif ; first exchange port and IP addresses jsr tcpxp jsr tcpxip jsr shortpk /*DB("from bangbuf: ")*/ &mkpacket2 #if 0 DB("mkpacket: pp=") lda pp+1: jsr EHexout: lda pp: jsr EHexout DB(", pd=") lda pd+1: jsr EHexout: lda pd: jsr EHexout DB(", ppl=") lda ppl+1: jsr EHexout: lda ppl: jsr EHexout DB(", pdl=") lda pdl+1: jsr EHexout: lda pdl: jsr EHexout DB(", slotladr=") lda slotladr:jsr EHexout jsr ECrlfout #endif ; this is for other packets also... jsr preptcp ; make IP header from TCP pseudo header ; (i.e. copy the IP addresses to the right location and set protocol ; the rest is done in the IP layer jsr tcp2ip bcs needmove jsr prepip #if 0 /*def DEBUGTCP*/ DB("Send Seq: ") ldy #TH_SEQ x1 lda (pd),y jsr EHexout iny cpy #TH_SEQ+4 bcc x1 DB("^m^jSend Ack: ") ldy #TH_ACK x2 lda (pd),y jsr EHexout iny cpy #TH_ACK+4 bcc x2 DB("^m^jFlag= ") ldy #TH_FLAG lda (pd),y jsr EHexout jsr ECrlfout #endif ldx pslot lda #<-1 sta pslot jmp queueslot needmove jsr tdisc sec rts .) /* incuna .( ldy #TCB_SND_UNA+3 ldx #3 lda (tcbp),y clc adc #1 sta (tcbp),y dey l5 lda (tcbp),y adc #0 sta (tcbp),y dey dex bne l5 rts .) */ /* setqseq .( lda tcbp clc adc #8 sta p2 lda tcbp+1 adc #0 sta p2+1 ldy #TH_SEQ l5 lda (p2),y sta (qd),y iny cpy #TH_SEQ+4 bcc l5 rts .) setqack .( lda qd clc adc #4 sta p2 lda qd+1 adc #0 sta p2+1 ldy #TCB_RCV_NXT l5 lda (tcbp),y sta (p2),y iny cpy #TCB_RCV_NXT+4 bcc l5 rts .) */ /* The following routine takes the packet from * islot/ip/id/ipl/idlen/dlen/doffset/toffset * and queues it to the user data rx queue. * It sets ack appropriately. * It copies the TCP header into a new packet and sets * islot/ip/..., but dlen is zero. */ copy_n_queue .( .bss myslot .byt 0 myhl .byt 0 mydlen .word 0 .zero myp .word 0 .text #if 0 DB("copy_n_queue: ip=") lda ip+1: jsr EHexout: lda ip: jsr EHexout DB(", request ") lda doffset: jsr EHexout DB(" b., dlen=") lda dlen+1: jsr EHexout: lda dlen: jsr EHexout DB(" , discd=") lda discd+1: jsr EHexout: lda discd: jsr EHexout jsr ECrlfout #endif lda dlen+1 ; dlen = discd ? then return ok cmp discd+1 bne ok lda dlen cmp discd bne ok clc rts ok lda doffset ldy #0 jsr balloc bcc gotbuffer rts gotbuffer stx myslot jsr getbadr bcc adrok DBTCP("illegal slot# in copy_n_queue^m^j") sec rts adrok sta myp sty myp+1 lda phl sta myhl /* DB("got buffer slot ") lda myslot: jsr EHexout: DB(" @ address ") lda myp+1: jsr EHexout: lda myp: jsr EHexout jsr ECrlfout */ ldy #0 l0 lda (pp),y sta (myp),y /*jsr EHexout*/ iny cpy doffset bne l0 ldy #0 lda doffset clc adc discd sta (pp),y /* save for queue routines */ tya adc discd+1 iny sta (pp),y iny lda dlen sec sbc discd sta mydlen sta (pp),y iny lda dlen+1 sbc discd+1 sta mydlen+1 sta (pp),y /*lda #" ": jsr ECout: tya: jsr EHexout*/ /* lda myp sta ip clc adc phl sta id lda myp+1 sta ip+1 adc #0 sta id+1 lda doffset sec sbc phl sta idlen lda #0 sta idlen+1 */ lda pslot jsr rx_queue_packet bcs boom lda mydlen ldx mydlen+1 jsr addrxnxt boom lda #0 sta mydlen sta mydlen+1 ldx myslot lda myhl jsr getpinfo bcc piok DBTCP("getpi error in copy_n_queue^m^j") piok /* DB("copy_n_queue returns ip=") lda ip+1: jsr EHexout: lda ip: jsr EHexout DB(", id=") lda id+1: jsr EHexout: lda id: jsr EHexout jsr ECrlfout */ clc nobuffer rts .) /* retransmit the first packet in the retransmission queue */ &retransmit .( ldy #TCB_NTXBUF lda (tcbp),y beq ende ldy #TCB_TXBUF lda (tcbp),y tax ldy #TCB_TRIES lda (tcbp),y clc adc #1 cmp #10 ; retransmit 6 times only ? bcs error pha jsr incownr jsr queueslot pla bcs endeini ldy #TCB_TRIES sta (tcbp),y #ifdef DEBUGTCP2 DB("retransmit seq=") ldy #TCB_TXBUF:lda (tcbp),y:tax:jsr getbadr .zero:foo .word 0:.text:sta foo:sty foo+1 ldy #TCP_OFFSET+TH_SEQ+2:lda (foo),y:jsr EHexout:iny:lda (foo),y:jsr EHexout jsr ECrlfout #endif ldy #TCB_STATE lda (tcbp),y cmp #TCP_TIMEW bne doit jsr flushqueue rts doit ldy #TCB_RETRANS lda #<TIME_RETRANS sta (tcbp),y iny lda #>TIME_RETRANS sta (tcbp),y rts error DBTCP("error^m^j") jsr tclose ende rts endeini ldy #TCB_RETRANS lda #1 sta (tcbp),y iny lda #0 sta (tcbp),y clc rts .) /* this routine puts outgoing packets into the retransmission queue and sends * them. It needs tcbp, and the packet buffer number in x. It always does * a bfree. */ &tx_queue_packet .( lda #TCP_OFFSET jsr getpinfo bcc piok DBTCP("getpi error in tx_queue_packet^m^j") sec rts piok ldy #0 lda (pp),y cmp #<TCP_DOFFSET bne move iny lda (pp),y cmp #>TCP_DOFFSET beq ok move DB("TCP offset wrong!^m^j") #ifdef DEBUGTCP lda pslot:jsr EHexout:lda #"-":jsr ECout lda pp+1: jsr EHexout: lda pp: jsr EHexout: lda #":": jsr ECout ldy #0: xx lda (pp),y: jsr EHexout: iny: cpy #10: bcc xx jsr ECrlfout #endif qdisc ldx pslot lda #<-1 sta pslot jsr bfree lda #<-1 sec rts ok iny lda (pp),y sta dlen ; len iny lda (pp),y sta dlen+1 jsr setout jsr setseq jsr setack lda dlen ldx dlen+1 jsr addtxnxt ; inc seq number jsr preptcp jsr tcp2ip bcs qdisc jsr prepip &do_requeue jsr do_queue ; does an incownr bcs notqueued ldx pslot lda #<-1 sta pslot jsr queueslot lda #0 clc rts notqueued ldx pslot ; we are not expected to keep a reference here jsr bfree lda #0 sec rts .) /* * requeue a packet that has already been to tx_queue_packet, but could * not be queued. does a bfree. */ &tx_requeue_packet .( stx pslot #ifdef DEBUGTCP2 lda #TCP_OFFSET jsr getpinfo #endif jmp do_requeue /* jsr do_queue php ldx pslot jsr bfree plp rts */ .) do_queue .( ldy #TCB_NTXBUF lda (tcbp),y cmp #TCP_MAXTXB bcc queueok rts queueok adc #1 sta (tcbp),y clc adc #TCB_TXBUF-1 tay lda pslot sta (tcbp),y tax jsr incownr cpy #TCB_TXBUF ; not only block in buffer -> don't set time bne notime ldy #TCB_TRIES lda #0 sta (tcbp),y ; no tries so far ldy #TCB_RETRANS+1 ; send in the next loop sta (tcbp),y lda #1 dey sta (tcbp),y notime #ifdef DEBUGTCP2 DB("queued seq=") ldy #TCP_OFFSET+TH_SEQ+2 lda (pp),y jsr EHexout iny lda (pp),y jsr EHexout DB(" pp= ") lda pp+1:jsr EHexout:lda pp:jsr EHexout jsr ECrlfout #endif clc rts .) setout .( ; make TCP packet from data clc lda #4 adc pd sta p2 lda #0 adc pd+1 sta p2+1 ldy #TH_SRCIP l0 lda (tcbp),y sta (p2),y iny cpy #TH_SRCIP+4 bcc l0 sec lda pd sbc #4 sta p2 lda pd+1 sbc #0 sta p2+1 ldy #TH_TRGIP l1 lda (tcbp),y sta (p2),y iny cpy #TH_TRGIP+4 bcc l1 ldy #TCB_SRCP lda (tcbp),y tax iny lda (tcbp),y ldy #TH_TRGP+1 sta (pd),y dey txa sta (pd),y ldy #TCB_TRGP lda (tcbp),y tax iny lda (tcbp),y ldy #TH_SRCP+1 sta (pd),y dey txa sta (pd),y ldy #TH_PTCL-1 lda #0 sta (pd),y iny lda #6 sta (pd),y iny iny lda pdl sec sbc #<TPH_LEN sta (pd),y dey lda pdl+1 sbc #>TPH_LEN sta (pd),y ldy #TH_DOFFSET lda #$50 sta (pd),y iny lda #THF_ACK sta (pd),y rts .) &send_syn lda #THF_SYN .byt $2c &send_reset lda #THF_RST ; .byt $2c ;&send_fin ; lda #THF_FIN+THF_ACK &send_flag .( ;inc $d020 /* TODO: set queued FIN until everything is really ok */ sta tmp lda #<TH_OPTIONS+TCP_OFFSET ldy #>TH_OPTIONS+TCP_OFFSET jsr balloc bcc ok DB("Couldn't alloc send_* buffer!^m^j") sec rts ok ;stx qslot lda #TCP_OFFSET jsr getpinfo bcc piok DB("getpi error in send_*^m^j") sec rts piok jsr setout jsr setack ldy #TH_FLAG lda tmp sta (pd),y and #THF_FIN+THF_SYN beq nofin ldy #TCB_FLAG2 and (tcbp),y beq nodec ; already sent -> decrement seq before sending jsr decseq nodec jsr setseq ; set packet sequence number lda #1 ldx #0 jsr addtxnxt ldy #TCB_FLAG2 lda (tcbp),y ora tmp sta (tcbp),y ;ldx pslot - implicit jsr do_queue ; everthing adding to snd_nxt should be queued! bcc nofin2 ldx pslot jsr bfree sec rts ; return if not able to queue nofin jsr setseq nofin2 jmp mkpacket2 .) &tclose .( /* ok, clean up connection specific stuff */ lda #TE_SIG_TERM jsr signal_user ldy #TCB_STATE lda #TCP_CLOSED sta (tcbp),y &&flushqueue #ifdef DEBUGTCP2 DB("flushqueue^m^j") #endif ldy #TCB_NTXBUF lda (tcbp),y beq end clc adc #TCB_TXBUF-1 sta tmp l1 ldy tmp cpy #TCB_TXBUF ; -1 bcc end lda (tcbp),y tax jsr bfree dec tmp jmp l1 end ldy #TCB_NTXBUF lda #0 sta (tcbp),y rts .) settcb .( #if (TCB_LEN - 64) #warning TCB length not correct! #else lda #0 sta tcbp+1 txa asl rol tcbp+1 asl rol tcbp+1 asl rol tcbp+1 asl rol tcbp+1 asl rol tcbp+1 asl rol tcbp+1 clc adc #<tcb sta tcbp lda #>tcb adc tcbp+1 sta tcbp+1 rts #endif .) gettcb .( ldx #0 l0 jsr settcb ldy #TCB_STATE lda (tcbp),y cmp #TCP_CLOSED beq l1 inx cpx #MAXCONN bcc l0 sec rts l1 ldy #TCB_NTXBUF lda #0 sta (tcbp),y ldy #TCB_FLAG sta (tcbp),y iny ; TCB_FLAG2 sta (tcbp),y ldy #TCB_LSTATE lda #TCP_CLOSED sta (tcbp),y ; clear connection specific data lda #0 ldy #TCB_CONN l2 sta (tcbp),y iny cpy #TCB_LEN bcc l2 clc rts .) &&tcp_kill .( DB("tcp_kill^m^j") ldx #0 l0 stx conn jsr settcb ldy #TCB_STATE lda (tcbp),y beq next cmp #TCP_LISTEN beq next txa pha DB("kill conn ") txa:jsr EHexout:jsr ECrlfout lda #TE_SIG_RESET jsr signal_user pla tax next inx cpx #MAXCONN bcc l0 rts .) #include "tutil.a65" #include "tcpuser.a65" #ifndef NO_TEST #include "tcpsrv.a65" #endif #ifndef NO_FSTCP #include "fstcp.a65" #endif #ifndef NO_WWW #include "wwwsrv.a65" #endif #ifdef RSH_BIN /* must be before LIBIP */ #include "rsh.a65" #endif #ifndef NO_LIBIP #include "libip.a65" #endif .)