; s.iprom ; ; IP for the Beeb. Lovingly coded in hand-crafted assembler. Hmph. ; (c) 1995-1996 Philip Blundell. ; ; 12-Sep-95 - send_packet cheating removed ; - arp added. Doesn't seem to work - not sure if this is Freenet's ; fault or mine ; - routing added ; - BASIC loader program added ; - made ip_checksum more general, removed icmp_checksum ; - clock now 32 bits ; - started implementing TCP ; - fixed block_copy of >256 bytes ; ; 15-Dec-95 - fixed read_rx control-byte check ; - implemented response to incoming ARP ; - fixed outgoing ARP requests ; - implemented handling of incoming ARP replies ; - improved ARP/route table search code ; ; 17-Dec-95 - implemented TCP ; ; 27-Dec-95 - started to implement sideways ROM version ; ; 30-Dec-95 - add tcpack_buffer to fix overwrite problems in reject and ack ; - more tx retries (10 not 2) ; - randomise our port number ; - try to fix the zero-window deadlock problem ; - start implementing TELAPI ; ; 03-Jan-96 - add initflag ; - implement vector claim & passon properly ; ; 11-Apr-96 - fix resets being sent with wrong port numbers (was killing ; active connection when a spurious segment arrived) ; - fix CNP code to return correct values ; ; 16-Apr-96 - queue up another frame to go out if we have buffered data ; when an ACK arrives ; - don't re-open rxcb on soft break ; - do initialisation stuff after claiming workspace, not on ; auto-boot call ; - print numbers in decimal not hex ; - preserve more zero-page stuff more of the time ; - print our IP address on *HELP ; ; 17-Apr-96 - part implemented UDP and bootp transmission ; - can't really do broadcasts though, so we lose ; ; 01-Nov-96 - removed initflag, replaced with DEROUTE (to allow the ; ROM to be put into hardware) __DEBUG__ EQU 0 ; EVENTV EQU &30 temp EQU &A8 temp2 EQU &AA base EQU &AC data EQU &AE params EQU &70 tcp_frame EQU &72 temp3 EQU &74 pltemp EQU &74 bccount EQU &76 trcount EQU &77 ; ; Econet MTU=1280 bytes=&500. scratch EQU &A040 ; &40 bytes more temp space pblk EQU &A080 trx_buf EQU &A0C0 tcprx_buffer EQU &A200 ; 256 bytes TCP receive space tcptx_buffer EQU &A100 ; ditto, transmit varbase EQU &B200 oldvector EQU varbase ; &980 rxhandle EQU varbase+2 ; &982 ihl EQU varbase+3 ; &983 clock EQU varbase+4 ; &984 rstation EQU varbase+8 ; &988 rlength EQU varbase+&A ; &98A cpsrc EQU varbase+&C ; &98C cpdst EQU varbase+&E ; &98E cplen EQU varbase+&10 ; &990 his_ip EQU varbase+&12 ; &992 proto EQU varbase+&16 ; &996 retry EQU varbase+&17 ; &997 seg_seq EQU varbase+&18 ; &998 seg_ack EQU varbase+&1C ; &99C tcp_state EQU varbase+&20 ; &9A0 tcp_flags EQU varbase+&21 ; &9A1 locport EQU varbase+&22 ; &9A2 remport EQU varbase+&24 ; &9A4 tcp_peer EQU varbase+&26 ; &9A6 snd_nxt EQU varbase+&2A ; &9AA hisport EQU varbase+&2E ; &9AE ourport EQU varbase+&30 ; &9B0 tcp_retransmit EQU varbase+&32 ; &9B2 tcp_timer EQU varbase+&34 ; &9B4 rcv_nxt EQU varbase+&35 ; &9B5 snd_una EQU varbase+&39 ; &9B9 tcprx_in EQU varbase+&3D ; &9BD tcprx_out EQU varbase+&3E ; &9BE tcptx_in EQU varbase+&3F ; &9BF tcptx_out EQU varbase+&40 ; &9C0 tx_size EQU varbase+&41 ; &9C1 my_ip EQU varbase+&42 rxbuffer_ptr EQU varbase+&46 txbuffer_ptr EQU varbase+&47 rxbuffer_end_ptr EQU varbase+&48 txbuffer_end_ptr EQU varbase+&49 blk_ptr EQU varbase+&4A zwindow EQU varbase+&4B old_insv EQU varbase+&4C old_remv EQU varbase+&4F old_cnpv EQU varbase+&52 old_eventv EQU varbase+&55 zpstore EQU varbase+&80 tx_flag EQU varbase+&8A ack_timer EQU varbase+&8B iac_flag EQU varbase+&8C iac_cmd EQU varbase+&8D lockflag EQU varbase+&8E iac_x EQU varbase+&8F data_size EQU varbase+&90 our_fin_flag EQU varbase+&92 STATE_CLOSED EQU 0 STATE_SYN_SENT EQU 1 STATE_ESTABLISHED EQU 2 STATE_FIN EQU 3 URG EQU 32 ACK EQU 16 PSH EQU 8 RST EQU 4 SYN EQU 2 FIN EQU 1 arp EQU varbase+&100 ; space for 40x6-byte arp entries route EQU varbase+&1F0 ; space for 10x8-byte routes tcp_buffer EQU varbase+&240 ; space for 512 bytes TCP data tcpack_buffer EQU varbase+&440 ; space for TCP ack ORG &8000 header BRK ; we aren't a language DW 0 JMP service DFB %10000010 ;Service ROM in 6502 code DFB >copyright-1 DFB 14 ROMName ASC 'TCP/IP' namelen EQU *-ROMName BRK ASC 'v0.14 1996-04-16' BRK copyright ASC '(C) Philip Blundell' BRK DW 0 DW 0 service CMP #0 BEQ :noservice PHA TYA PHA LDA temp PHA LDA temp+1 PHA LDA temp2 PHA LDA temp2+1 PHA TSX LDA &106,X CMP #2 BEQ :claimworkspace CMP #9 BEQ :help CMP #3 BNE :notbreak JMP break :notbreak CMP #4 BNE :notstarcmd JMP starcmd :notstarcmd service_exit PLA STA temp2+1 PLA STA temp2 PLA STA temp+1 PLA STA temp PLA TAY PLA LDX &F4 :noservice RTS :claimworkspace LDX &F4 TYA STA &DF0,X STY rxbuffer_ptr INY INY STY txbuffer_ptr INY INY STY blk_ptr INY TSX TYA STA &105,X JMP service_exit :help LDA (&F2),Y CMP #&D BEQ :shorthelp LDX #0 JSR cmd_decode_setup JSR cmd_decode JMP service_exit :shorthelp JSR prinline DB 13 ASC 'Econet TCP/IP 0.14 ' BRK :init_ok LDA my_ip ; CMP #0 BEQ :no_addr_known PHA LDA #'(' JSR OSWRCH PLA JSR PrintDecimal LDA #'.' JSR OSWRCH LDA my_ip+1 JSR PrintDecimal LDA #'.' JSR OSWRCH LDA my_ip+2 JSR PrintDecimal LDA #'.' JSR OSWRCH LDA my_ip+3 JSR PrintDecimal LDA #')' JSR OSWRCH :no_addr_known JSR prinline DB 13 ASC ' TCPIP' DB 13 BRK JMP service_exit BLOCK cmd_decode_setup TYA CLC ADC &F2 STA temp2 LDA &F3 ADC #0 STA temp2+1 RTS cmd_decode LDA cmd_table,X STA temp LDA cmd_table+1,X STA temp+1 LDY #0 :loop LDA (temp),Y ; CMP #0 BEQ :ok LDA (temp2),Y CMP #46 BEQ :ok AND #&5F CMP (temp),Y BNE :bad INY JMP :loop :bad INX INX INX INX LDA #0 RTS :ok LDA cmd_table+2,X STA temp LDA cmd_table+3,X STA temp+1 JMP (temp) cmd_table DW tcpip_str DW tcpip_help DW arp_str DW arpcmd DW ipaddr_str DW ipaddrcmd DW telnet_str DW telnetcmd DW route_str DW routecmd DW deroute_str DW deroutecmd DW telapi_str DW telapicmd tcpip_str ASC 'TCPIP' BRK arp_str ASC 'ARP' BRK ipaddr_str ASC 'IPADDR' BRK telnet_str ASC 'TELNET' BRK route_str ASC 'ROUTE' BRK deroute_str ASC 'DEROUTE' BRK telapi_str ASC 'TELAPI' BRK tcpip_help JSR prinline DB 13 STR 'TCP/IP 0.14' STR '(C) 1996 Philip Blundell' STR ' IPADDR ' STR ' DEROUTE' STR ' ROUTE ' STR ' TELNET ()' STR ' TELAPI ()' STR ' ARP' BRK LDA #&FF RTS break JSR prinline STR 'TCP/IP 0.14' BRK LDA #253 LDX #0 LDY #255 JSR OSBYTE CPX #0 BEQ :softbreak JSR open_rx LDA #0 STA my_ip STA my_ip+1 STA my_ip+2 STA my_ip+3 LDX #239 :zlp STA arp,X DEX CPX #&FF BNE :zlp :softbreak LDA #STATE_CLOSED STA tcp_state :init1 LDA #0 STA zwindow STA clock STA clock+1 STA clock+2 STA clock+3 STA tcp_retransmit STA tcp_retransmit+1 STA tcprx_in STA tcprx_out STA tcptx_in STA tcptx_out STA tx_flag STA ack_timer STA iac_flag STA iac_cmd STA lockflag STA iac_x STA data_size STA data_size+1 STA our_fin_flag LDA #10 STA tcp_timer LDA #&A8 LDX #0 LDY #&FF JSR OSBYTE STX temp LDA #&A9 LDX #0 LDY #&FF JSR OSBYTE STX temp+1 LDA &22A STA old_insv LDA &22B STA old_insv+1 LDA &22C STA old_remv LDA &22D STA old_remv+1 LDA &22E STA old_cnpv LDA &22F STA old_cnpv+1 LDA &220 CMP #&30 BNE :eventv_notextended LDA &221 CMP #&FF BNE :eventv_notextended LDY #&30 LDA (temp),Y STA old_eventv INY LDA (temp),Y STA old_eventv+1 INY LDA (temp),Y STA old_eventv+2 JMP :eventv_wasextended :eventv_notextended LDA &220 STA old_eventv LDA &221 STA old_eventv+1 LDA #&FF STA old_eventv+2 :eventv_wasextended PHP SEI LDA #>vsync LDY #&30 STA (temp),Y INY LDA #insert STA (temp),Y INY LDA #remove STA (temp),Y INY LDA #countpurge STA (temp),Y INY LDA # ' BRK LDY #16 LDA (data),Y JSR PrintHex1 LDA #46 JSR &FFEE INY LDA (data),Y JSR PrintHex1 LDA #46 JSR &FFEE INY LDA (data),Y JSR PrintHex1 LDA #46 JSR &FFEE INY LDA (data),Y JSR PrintHex1 JSR prinline ASC ' IHL=' BRK FI LDY #9 LDA (data),Y STA proto LDY #0 LDA (data),Y AND #&F ASL A ASL A STA ihl IF __DEBUG__ JSR PrintHex1 JSR prinline ASC ' proto=' BRK LDA proto JSR PrintHex1 FI LDA rlength SEC SBC ihl STA rlength LDA rlength+1 SBC #0 STA rlength+1 LDA data CLC ADC ihl STA data LDA data+1 ADC #0 STA data+1 IF __DEBUG__ JSR OSNEWL FI LDA proto CMP #1 BNE :noticmp JMP icmp :noticmp CMP #6 BNE :nottcp JMP tcp :nottcp RTS tcp IF __DEBUG__ JSR prinline ASC 'TCP ' BRK FI LDY #0 LDA (data),Y STA remport+1 INY LDA (data),Y STA remport INY LDA (data),Y STA locport+1 INY LDA (data),Y STA locport INY LDA (data),Y STA seg_seq+3 INY LDA (data),Y STA seg_seq+2 INY LDA (data),Y STA seg_seq+1 INY LDA (data),Y STA seg_seq INY LDA (data),Y STA seg_ack+3 INY LDA (data),Y STA seg_ack+2 INY LDA (data),Y STA seg_ack+1 INY LDA (data),Y STA seg_ack+0 INY LDA (data),Y LSR A LSR A STA scratch ; TCP header size INY LDA (data),Y STA tcp_flags LDA data CLC ADC scratch STA data LDA data+1 ADC #0 STA data+1 LDA rlength SEC SBC scratch STA rlength LDA rlength+1 SBC #0 STA rlength+1 IF __DEBUG__ LDX remport LDY remport+1 JSR PrintHex2 JSR prinline ASC " -> " BRK LDX locport LDY locport+1 JSR PrintHex2 LDA #32 JSR OSWRCH LDX seg_seq+2 LDY seg_seq+3 JSR PrintHex2 LDX seg_seq LDY seg_seq+1 JSR PrintHex2 LDA #32 JSR OSWRCH LDX seg_ack+2 LDY seg_ack+3 JSR PrintHex2 LDX seg_ack LDY seg_ack+1 JSR PrintHex2 LDA #32 JSR OSWRCH LDA tcp_flags JSR PrintHex1 JSR OSNEWL FI LDA tcp_state CMP #STATE_CLOSED BEQ reject LDA locport CMP ourport BNE reject LDA remport CMP hisport BNE reject JMP :noreject reject IF __DEBUG__ JSR prinline ASC '[rejecting segment]' DB 13 BRK FI LDA tcp_flags BIT #RST BNE :notrst RTS :notrst ; use tcpack_buffer here LDX #>tcpack_buffer LDY #tcpack_buffer LDY #pblk LDY #tcpack_buffer STA tcp_frame LDA #pblk LDY #pblk LDY # ()' BRK telapicmd ; Open a TCP connection to some remote host LDA tcp_frame PHA LDA tcp_frame+1 PHA :loop LDA (temp2),Y CMP #&D BEQ :syntax CMP #32 BNE :go1 INY JMP :loop :go1 JSR getNumber STA tcp_peer LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA tcp_peer+1 LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA tcp_peer+2 LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA tcp_peer+3 LDA #23 STA hisport LDA (temp2),Y CMP #&D BEQ :cr CMP #' ' BEQ :oksyntax JMP :syntax :oksyntax INY JSR getNumber STA hisport :cr LDA tcp_state CMP #STATE_CLOSED BEQ :okstate JSR error BRK ASC "Connection already open" BRK :okstate LDA #0 STA our_fin_flag LDA #0 STA hisport+1 LDA tcp_timer STA ourport IF __DEBUG__ JSR prinline ASC 'TCP open connection to ' BRK LDA tcp_peer JSR PrintHex1 LDA #46 JSR OSWRCH LDA tcp_peer+1 JSR PrintHex1 LDA #46 JSR OSWRCH LDA tcp_peer+2 JSR PrintHex1 LDA #46 JSR OSWRCH LDA tcp_peer+3 JSR PrintHex1 LDA #'/' JSR OSWRCH LDA hisport+1 JSR PrintHex1 LDA hisport JSR PrintHex1 JSR OSNEWL FI LDA #0 STA iac_flag LDA #31 STA ourport+1 JSR lock_rx LDX #>tcp_buffer LDY #trx_buf LDY #tcp_buffer STX tcp_frame STY tcp_frame+1 LDY #32 LDX tcptx_out :copy LDA tcptx_buffer,X STA (tcp_frame),Y INX INY CPY scratch BNE :copy STX tcptx_out LDY #24 LDA #&50 STA (tcp_frame),Y LDY #10 LDA #0 STA (tcp_frame),Y INY LDA scratch SEC SBC #12 STA (tcp_frame),Y LDA #0 LDY #25 STA (tcp_frame),Y INY LDA #0 STA (tcp_frame),Y CLC LDA tcprx_out SBC tcprx_in LDY #27 STA (tcp_frame),Y ; set window CMP #0 BNE :notzerowindow LDA #1 STA zwindow :notzerowindow LDA snd_nxt+3 LDY #16 STA (tcp_frame),Y INY LDA snd_nxt+2 STA (tcp_frame),Y INY LDA snd_nxt+1 STA (tcp_frame),Y INY LDA snd_nxt STA (tcp_frame),Y JMP tcp_queue BLOCK ack_process LDA seg_ack STA snd_una LDA seg_ack+1 STA snd_una+1 LDA seg_ack+2 STA snd_una+2 LDA seg_ack+3 STA snd_una+3 LDA tcp_retransmit STA tcp_frame ; CMP #0 BNE :skip LDA tcp_retransmit+1 ; CMP #0 BEQ :no_frame_queued :skip LDA tcp_retransmit+1 STA tcp_frame+1 LDY #19 LDA (tcp_frame),Y CLC ADC tx_size STA scratch DEY LDA (tcp_frame),Y ADC #0 STA scratch+1 DEY LDA (tcp_frame),Y ADC #0 STA scratch+2 DEY LDA (tcp_frame),Y ADC #0 STA scratch+3 CMP snd_una+3 BNE :diff LDA scratch+2 CMP snd_una+2 BNE :diff LDA scratch+1 CMP snd_una+1 BNE :diff LDA scratch CMP snd_una BNE :diff :ok LDA #0 STA tcp_retransmit STA tcp_retransmit+1 STA tx_size STA tcp_frame STA tcp_frame+1 IF __DEBUG__ JSR prinline ASC 'ACK: dequeued frame' DB 13 BRK FI :no_frame_queued ; We may be able to queue some more data up for transmission LDA tcptx_in CMP tcptx_out BEQ :tx_empty JMP tcp_transmit :tx_empty RTS :diff BCS :no_frame_queued JMP reject fin_process LDA #1 STA our_fin_flag LDA #STATE_FIN STA tcp_state RTS BLOCK arpcmd LDX #0 :arploop TXA PHA LDA arp+4,X ; CMP #0 BEQ :arpskip LDA arp,X JSR PrintDecimal LDA #'.' JSR OSWRCH PLA PHA TAX LDA arp+1,X JSR PrintDecimal LDA #'.' JSR OSWRCH PLA PHA TAX LDA arp+2,X JSR PrintDecimal LDA #'.' JSR OSWRCH PLA PHA TAX LDA arp+3,X JSR PrintDecimal JSR prinline ASC ' at ' BRK PLA PHA TAX LDA arp+5,X JSR PrintDecimal LDA #'.' JSR OSWRCH PLA PHA TAX LDA arp+4,X JSR PrintDecimal JSR OSNEWL :arpskip PLA CLC ADC #6 TAX CMP #240 BNE :arploop LDA #&FF RTS starcmd BLOCK JSR cmd_decode_setup LDX #4 :cmdlp JSR cmd_decode CMP #&FF BEQ :ok CPX #28 BNE :cmdlp JMP service_exit :ok TSX LDA #0 STA &106,X JMP service_exit ipaddrcmd BLOCK :loop LDA (temp2),Y CMP #&D BEQ :syntax CMP #32 BNE :go INY JMP :loop :syntax JSR error BRK ASC 'Syntax: *IPADDR ' BRK :go JSR getNumber STA my_ip LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA my_ip+1 LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA my_ip+2 LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber STA my_ip+3 LDA #&FF RTS ; read a number in decimal from (temp2),Y ; return: result in A, Y points to next character, X corrupt getNumber BLOCK LDA scratch PHA LDA #0 STA scratch :next LDA (temp2),Y CMP #'0' BCC :notdigit CMP #58 ; '9'+1 BCS :notdigit SEC SBC #'0' ; get the digit PHA LDX #0 LDA scratch :multloop CLC ADC scratch INX CPX #9 BNE :multloop STA scratch PLA CLC ADC scratch STA scratch INY JMP :next :notdigit LDA scratch TAX PLA STA scratch TXA RTS deroutecmd BLOCK LDA #&FF TAX :loop INX STA route,X CPX #3 BNE :loop RTS routecmd BLOCK LDA #0 STA scratch+1 ; route entry :loop LDA (temp2),Y CMP #&D BEQ :syntax CMP #32 BNE :go INY JMP :loop :syntax JSR error BRK ASC 'Syntax: *ROUTE ' BRK :go LDA (temp2),Y CMP #42 BNE :notDefault JMP :isDefault :notDefault JSR getNumber LDX scratch+1 STA route,X LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber LDX scratch+1 STA route+1,X LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber LDX scratch+1 STA route+2,X LDA (temp2),Y CMP #'.' BNE :syntax INY JSR getNumber LDX scratch+1 STA route+3,X :part2 LDA (temp2),Y CMP #' ' BNE :syntax INY JSR getNumber LDX scratch+1 STA route+4,X LDA (temp2),Y CMP #'.' BNE :syntax1 INY JSR getNumber LDX scratch+1 STA route+5,X LDA (temp2),Y CMP #'.' BNE :syntax1 INY JSR getNumber LDX scratch+1 STA route+6,X LDA (temp2),Y CMP #'.' BNE :syntax1 INY JSR getNumber LDX scratch+1 STA route+7,X LDA #&FF STA route+8,X STA route+9,X STA route+10,X STA route+11,X RTS :syntax1 JMP :syntax :isDefault LDX scratch+1 LDA #0 STA route,X STA route+1,X STA route+2,X STA route+3,X INY JMP :part2 PrintDecimal LDX #0 BLOCK ; do 100s :loop CMP #&64 BCC :ping SBC #&64 INX JMP :loop :ping PHA TXA BEQ :skip ADC #'0' JSR OSWRCH :skip PLA LDX #0 BLOCK ; do 10s :loop CMP #&A BCC :ping SBC #&A INX JMP :loop :ping PHA TXA BEQ :skip ADC #'0' JSR OSWRCH :skip PLA ADC #'0' JMP OSWRCH