diff --git a/client/examples/make_sine_data.rb b/client/examples/make_sine_data.rb index 467381f..3caa125 100644 --- a/client/examples/make_sine_data.rb +++ b/client/examples/make_sine_data.rb @@ -1,8 +1,8 @@ f=File.open("sine_data.i","w") TABLE_ENTRIES=0x80 -AMPLITUDE=205 -OFFSET=50 +AMPLITUDE=255 +OFFSET=00 TABLE_ENTRIES.times do |i| value=OFFSET+Math.sin(Math::PI*i.to_f/TABLE_ENTRIES.to_f)*AMPLITUDE diff --git a/client/examples/sine_data.i b/client/examples/sine_data.i index 3c7933f..9bd4215 100644 --- a/client/examples/sine_data.i +++ b/client/examples/sine_data.i @@ -1,17 +1,17 @@ -.byte $32, $37, $3c, $41, $46, $4b, $50, $55 -.byte $59, $5e, $63, $68, $6d, $72, $77, $7b -.byte $80, $85, $89, $8e, $92, $97, $9b, $9f -.byte $a3, $a8, $ac, $b0, $b4, $b7, $bb, $bf -.byte $c2, $c6, $c9, $cd, $d0, $d3, $d6, $d9 -.byte $dc, $df, $e1, $e4, $e6, $e9, $eb, $ed -.byte $ef, $f1, $f3, $f4, $f6, $f7, $f8, $fa -.byte $fb, $fb, $fc, $fd, $fe, $fe, $fe, $fe -.byte $ff, $fe, $fe, $fe, $fe, $fd, $fc, $fb -.byte $fb, $fa, $f8, $f7, $f6, $f4, $f3, $f1 -.byte $ef, $ed, $eb, $e9, $e6, $e4, $e1, $df -.byte $dc, $d9, $d6, $d3, $d0, $cd, $c9, $c6 -.byte $c2, $bf, $bb, $b7, $b4, $b0, $ac, $a8 -.byte $a3, $9f, $9b, $97, $92, $8e, $89, $85 -.byte $80, $7b, $77, $72, $6d, $68, $63, $5e -.byte $59, $55, $50, $4b, $46, $41, $3c, $37 \ No newline at end of file +.byte $00, $06, $0c, $12, $18, $1f, $25, $2b +.byte $31, $37, $3d, $44, $4a, $4f, $55, $5b +.byte $61, $67, $6d, $72, $78, $7d, $83, $88 +.byte $8d, $92, $97, $9c, $a1, $a6, $ab, $af +.byte $b4, $b8, $bc, $c1, $c5, $c9, $cc, $d0 +.byte $d4, $d7, $da, $dd, $e0, $e3, $e6, $e9 +.byte $eb, $ed, $f0, $f2, $f4, $f5, $f7, $f8 +.byte $fa, $fb, $fc, $fd, $fd, $fe, $fe, $fe +.byte $ff, $fe, $fe, $fe, $fd, $fd, $fc, $fb +.byte $fa, $f8, $f7, $f5, $f4, $f2, $f0, $ed +.byte $eb, $e9, $e6, $e3, $e0, $dd, $da, $d7 +.byte $d4, $d0, $cc, $c9, $c5, $c1, $bc, $b8 +.byte $b4, $af, $ab, $a6, $a1, $9c, $97, $92 +.byte $8d, $88, $83, $7d, $78, $72, $6d, $67 +.byte $61, $5b, $55, $4f, $4a, $44, $3d, $37 +.byte $31, $2b, $25, $1f, $18, $12, $0c, $06 \ No newline at end of file diff --git a/client/examples/tune.bin b/client/examples/tune.bin new file mode 100644 index 0000000..3bdabff Binary files /dev/null and b/client/examples/tune.bin differ diff --git a/client/examples/upnatom.s b/client/examples/upnatom.s index 20c0f6e..efdd8e9 100644 --- a/client/examples/upnatom.s +++ b/client/examples/upnatom.s @@ -237,12 +237,10 @@ init: cli ;enable maskable interrupts again - lda #0 + lda #3 sta scroll_state ;position for the first text - ldax #scroll_buffer_0 - stax current_input_ptr_ptr jsr reset_input_buffer ;now download the feed @@ -262,14 +260,13 @@ init: inc BORDER_COLOR ;little marker of success + lda #2 + sta scroll_state + ldax #scroll_buffer_1 stax current_output_ptr jsr emit_titles - ldax #scroll_buffer_1 - stax current_input_ptr_ptr ;will get picked up once we have finished going through the message once - lda #1 - sta scroll_state @endless_loop: jsr NB65_PERIODIC_PROCESSING_VECTOR @@ -278,16 +275,28 @@ init: jmp @endless_loop reset_input_buffer: - ldax current_input_ptr_ptr + + lda scroll_state + beq @config_scroller + dec scroll_state + cmp #1 + beq @feed_scroller + cmp #3 + beq @title_scroller +@config_scroller: + ldax #scroll_buffer_0 stax current_input_ptr - - ldax scroll_buffer_0 - stax current_input_ptr_ptr - lda #0 - sta scroll_state - rts - + +@feed_scroller: + ldax #scroll_buffer_1 + stax current_input_ptr + rts +@title_scroller: + ldax #static_title + stax current_input_ptr + rts + ;look for NB65 signature at location pointed at by AX look_for_signature: stax temp_buff @@ -349,6 +358,8 @@ setup_sprites: lda #$FF sta $d01c + sta sprite_ticker + lda #DARK_GRAY sta $d025 ;sprite multicolor register 0 lda #LIGHT_GRAY @@ -830,10 +841,14 @@ sprite_x_msb: sprite_y_pos: .include "sine_data.i" -.include "sine_data.i" +.repeat 128 + .byte 0 +.endrep +;.include "sine_data.i" +static_title: .byte "up 'n atom - the old skool feed reader!",$20,0 sprite_text: .byte "UP\NATOM" ;options are A-Z, "[\]^_" @@ -844,8 +859,8 @@ scroll_template: feed_url: -.byte "http://search.twitter.com/search.atom?q=kipper",0 -;.byte "http://static.cricinfo.com/rss/livescores.xml",0 +;.byte "http://search.twitter.com/search.atom?q=kipper",0 +.byte "http://static.cricinfo.com/rss/livescores.xml",0 title: .byte "",0 @@ -885,7 +900,7 @@ musicdata_size=*-musicdata .segment "SAFE_BSS" ;we want our variables to start at $3000, out of the way of our music player and the font data -current_input_ptr_ptr: .res 2 + param_offset: .res 1 temp_bin: .res 1 @@ -899,6 +914,7 @@ download_buffer: download_buffer_length=8000 .res download_buffer_length +.res 10 ;filler scroll_buffer_0: .res 1000 diff --git a/client/inc/commonprint.i b/client/inc/commonprint.i index e51e192..ad1662d 100644 --- a/client/inc/commonprint.i +++ b/client/inc/commonprint.i @@ -10,7 +10,7 @@ .export failed_msg .export init_msg .export print - .export print_decimal + .export print_integer .export print_dotted_quad .export print_arp_cache .export mac_address_msg @@ -32,8 +32,8 @@ pptr = copy_src .bss -temp_bin: .res 1 -temp_bcd: .res 2 +temp_bin: .res 2 +temp_bcd: .res 3 temp_ptr: .res 2 .code .macro print_driver_init @@ -150,7 +150,7 @@ print: @print_loop: ldy #0 lda (pptr),y - beq @done_print + beq @done_print jsr print_a inc pptr bne @print_loop @@ -209,27 +209,24 @@ print_arp_cache: print_dotted_quad: sta pptr stx pptr + 1 - ldy #0 + lda #0 +@print_one_byte: + pha + tay lda (pptr),y - jsr print_decimal + ldx #0 + jsr print_integer + pla + cmp #3 + beq @done + clc + adc #1 + pha lda #'.' jsr print_a - - ldy #1 - lda (pptr),y - jsr print_decimal - lda #'.' - jsr print_a - - ldy #2 - lda (pptr),y - jsr print_decimal - lda #'.' - jsr print_a - - ldy #3 - lda (pptr),y - jsr print_decimal + pla + bne @print_one_byte +@done: rts @@ -252,58 +249,70 @@ print_mac: cpy #06 bne @one_mac_digit rts -print_decimal: ;print byte in A as a decimal number - pha - sta temp_bin ;save + +print_integer: ;print 16 bit number in AX as a decimal number + +;hex to bcd routine taken from Andrew Jacob's code at http://www.6502.org/source/integers/hex2dec-more.htm + stax temp_bin sed ; Switch to decimal mode lda #0 ; Ensure the result is clear sta temp_bcd sta temp_bcd+1 - ldx #8 ; The number of source bits + sta temp_bcd+2 + ldx #16 ; The number of source bits : asl temp_bin+0 ; Shift out one bit + rol temp_bin+1 lda temp_bcd+0 ; And add into result adc temp_bcd+0 sta temp_bcd+0 lda temp_bcd+1 ; propagating any carry adc temp_bcd+1 sta temp_bcd+1 + lda temp_bcd+2 ; ... thru whole result + adc temp_bcd+2 + sta temp_bcd+2 + dex ; And repeat for next bit bne :- + stx temp_bin+1 ;x is now zero - reuse temp_bin as a count of non-zero digits cld ;back to binary - - pla ;get back the original passed in number - bmi @print_hundreds ; if N is set, the number is >=128 so print all 3 digits - cmp #10 - bmi @print_units - cmp #100 - bmi @print_tens -@print_hundreds: - lda temp_bcd+1 ;get the most significant digit + ldx #2 + stx temp_bin+1 ;reuse temp_bin+1 as loop counter +@print_one_byte: + ldx temp_bin+1 + lda temp_bcd,x + pha + lsr + lsr + lsr + lsr + jsr @print_one_digit + pla and #$0f - clc - adc #'0' - jsr print_a - -@print_tens: - lda temp_bcd - lsr - lsr - lsr - lsr - clc - adc #'0' - jsr print_a -@print_units: - lda temp_bcd - and #$0f - clc - adc #'0' - jsr print_a - + jsr @print_one_digit + dec temp_bin+1 + bpl @print_one_byte rts - +@print_one_digit: + cmp #0 + beq @this_digit_is_zero + inc temp_bin ;increment count of non-zero digits +@ok_to_print: + clc + adc #'0' + jsr print_a + rts +@this_digit_is_zero: + ldx temp_bin ;how many non-zero digits have we printed? + bne @ok_to_print + ldx temp_bin+1 ;how many digits are left to print? + bne @this_is_not_last_digit + inc temp_bin ;to get to this point, this must be the high nibble of the last byte. + ;by making 'count of non-zero digits' to be >0, we force printing of the last digit +@this_is_not_last_digit: + rts print_hex: pha diff --git a/client/inc/nb65_constants.i b/client/inc/nb65_constants.i index b8e17fd..0573ce4 100644 --- a/client/inc/nb65_constants.i +++ b/client/inc/nb65_constants.i @@ -44,16 +44,15 @@ NB65_TFTP_UPLOAD EQU $24 ;upload: AX points to a TFTP transfer par NB65_TFTP_CALLBACK_UPLOAD EQU $25 ;upload: AX points to a TFTP transfer parameter structure, outputs: none NB65_DNS_RESOLVE EQU $30 ;inputs: AX points to a DNS parameter structure, outputs: DNS param structure updated with - ;NB65_DNS_HOSTNAME_IP updated with IP address corresponding to hostname. - -NB65_DOWNLOAD_RESOURCE EQU $40 ;inputs: AX points to a URL download structure, outputs: none - + ;NB65_DNS_HOSTNAME_IP updated with IP address corresponding to hostname. +NB65_DOWNLOAD_RESOURCE EQU $31 ;inputs: AX points to a URL download structure, outputs: none +NB65_PING_HOST EQU $32 ;inputs: AX points to destination IP address for ping, outputs: AX=time (in milliseconds) to get response NB65_PRINT_ASCIIZ EQU $80 ;inputs: AX=pointer to null terminated string to be printed to screen, outputs: none NB65_PRINT_HEX EQU $81 ;inputs: A=byte digit to be displayed on screen as (zero padded) hex digit, outputs: none NB65_PRINT_DOTTED_QUAD EQU $82 ;inputs: AX=pointer to 4 bytes that will be displayed as a decimal dotted quad (e.g. 192.168.1.1) NB65_PRINT_IP_CONFIG EQU $83 ;no inputs, no outputs, prints to screen current IP configuration - +NB65_PRINT_INTEGER EQU $84 ;inputs: AX=16 byte number that will be printed as an unsigned decimal NB65_INPUT_STRING EQU $90 ;no inputs, outputs: AX = pointer to null terminated string NB65_INPUT_HOSTNAME EQU $91 ;no inputs, outputs: AX = pointer to hostname (which may be IP address). @@ -115,6 +114,10 @@ NB65_PAYLOAD_LENGTH EQU $08 ;2 byte length of payload ; 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) +;offsets in ICMP listener parameter structure +NB65_ICMP_LISTENER_TYPE EQU $00 ;ICMP type +NB65_ICMP_LISTENER_CALLBACK EQU $01 ;2 byte address of routine to call when ICMP packet of specified type arrives + ;offsets in URL download structure ;inputs: @@ -122,10 +125,6 @@ NB65_URL EQU $00 ;2 byte pointer to null te NB65_URL_DOWNLOAD_BUFFER EQU $02 ;2 byte pointer to buffer that resource specified by URL will be downloaded into NB65_URL_DOWNLOAD_BUFFER_LENGTH EQU $04 ;2 byte length of buffer (download will truncate when buffer is full) -;AX = address of URL string -; url_download_buffer - points to a buffer that url will be downloaded into -; url_download_buffer_length - length of buffer - ;error codes (as returned by NB65_GET_LAST_ERROR) NB65_ERROR_PORT_IN_USE EQU $80 NB65_ERROR_TIMEOUT_ON_RECEIVE EQU $81 @@ -139,7 +138,7 @@ NB65_ERROR_NO_SUCH_LISTENER EQU $88 NB65_ERROR_CONNECTION_RESET_BY_PEER EQU $89 NB65_ERROR_CONNECTION_CLOSED EQU $8A NB65_ERROR_FILE_ACCESS_FAILURE EQU $90 -NB65_MALFORMED_URL EQU $A0 -NB65_DNS_LOOKUP_FAILED EQU $A1 +NB65_ERROR_MALFORMED_URL EQU $A0 +NB65_ERROR_DNS_LOOKUP_FAILED EQU $A1 NB65_ERROR_OPTION_NOT_SUPPORTED EQU $FE NB65_ERROR_FUNCTION_NOT_SUPPORTED EQU $FF diff --git a/client/inc/ping.i b/client/inc/ping.i new file mode 100644 index 0000000..6ca0df8 --- /dev/null +++ b/client/inc/ping.i @@ -0,0 +1,66 @@ +.import icmp_ping +.import icmp_echo_ip + +NUM_PING_RETRIES=3 +.bss +ping_retries: .res 1 + +.code +ping_loop: + ldax #remote_host + jsr print + nb65call #NB65_INPUT_HOSTNAME + bcc @host_entered + ;if no host entered, then bail. + rts +@host_entered: + stax nb65_param_buffer + jsr print_cr + ldax #resolving + jsr print + ldax nb65_param_buffer + nb65call #NB65_PRINT_ASCIIZ + jsr print_cr + ldax #nb65_param_buffer + nb65call #NB65_DNS_RESOLVE + bcc @resolved_ok +@failed: + print_failed + jsr print_cr + jsr print_errorcode + jmp ping_loop +@resolved_ok: + + lda #NUM_PING_RETRIES + sta ping_retries +@ping_once: + ldax #pinging + jsr print + ldax #nb65_param_buffer + jsr print_dotted_quad + lda #' ' + jsr print_a + lda #':' + jsr print_a + lda #' ' + jsr print_a + + ldax #nb65_param_buffer + nb65call #NB65_PING_HOST + +bcs @ping_error + jsr print_integer + ldax #ms + jsr print +@check_retries: + dec ping_retries + bpl @ping_once + jmp ping_loop + +@ping_error: + jsr print_errorcode + jmp @check_retries + + +ms: .byte " MS",13,0 +pinging: .byte "PINGING ",0 diff --git a/client/inc/version.i b/client/inc/version.i index 433ab7a..6e356b6 100644 --- a/client/inc/version.i +++ b/client/inc/version.i @@ -1 +1 @@ -.byte "0.9.24" +.byte "0.9.25" diff --git a/client/ip65/Makefile b/client/ip65/Makefile index c76907e..8c7831f 100644 --- a/client/ip65/Makefile +++ b/client/ip65/Makefile @@ -16,7 +16,6 @@ ETHOBJS= \ cs8900a.o \ eth.o \ arp.o \ - icmp.o \ udp.o \ ip65.o \ printf.o \ @@ -34,15 +33,17 @@ ETHOBJS= \ all: ip65.lib ip65_tcp.lib -ip65.lib: $(ETHOBJS) function_dispatcher.s ip.s +ip65.lib: $(ETHOBJS) function_dispatcher.s ip.s icmp.s $(AS) $(AFLAGS) function_dispatcher.s - $(AS) $(AFLAGS) ip.s - ar65 a ip65.lib $(ETHOBJS) function_dispatcher.o ip.o + $(AS) $(AFLAGS) ip.s + $(AS) $(AFLAGS) icmp.s + ar65 a ip65.lib $(ETHOBJS) function_dispatcher.o ip.o icmp.o -ip65_tcp.lib: tcp.o $(ETHOBJS) function_dispatcher.s ip.s tcp.s +ip65_tcp.lib: tcp.o $(ETHOBJS) function_dispatcher.s ip.s tcp.s icmp.s $(AS) $(AFLAGS) function_dispatcher.s -DTCP -DAPI_VERSION=2 $(AS) $(AFLAGS) ip.s -DTCP - ar65 a ip65_tcp.lib $(ETHOBJS) function_dispatcher.o ip.o tcp.o + $(AS) $(AFLAGS) icmp.s -DTCP + ar65 a ip65_tcp.lib $(ETHOBJS) function_dispatcher.o ip.o tcp.o icmp.o clean: rm -f *.o diff --git a/client/ip65/function_dispatcher.s b/client/ip65/function_dispatcher.s index 2d11e6a..db51f24 100644 --- a/client/ip65/function_dispatcher.s +++ b/client/ip65/function_dispatcher.s @@ -427,6 +427,13 @@ ip_configured: rts : + cpy #NB65_PRINT_INTEGER + bne :+ + jsr print_integer + clc + rts +: + ;these are the API "version 2" functions .ifdef API_VERSION @@ -466,6 +473,20 @@ ip_configured: jmp url_download : + + cpy #NB65_PING_HOST + .import icmp_echo_ip + .import icmp_ping + bne :+ + ldy #3 +@copy_ping_ip_loop: + lda (nb65_params),y + sta icmp_echo_ip,y + dey + bpl @copy_ping_ip_loop + jmp icmp_ping + +: cpy #NB65_TCP_CONNECT bne :+ .import tcp_connect diff --git a/client/ip65/icmp.s b/client/ip65/icmp.s index d4ceabe..0204861 100644 --- a/client/ip65/icmp.s +++ b/client/ip65/icmp.s @@ -2,6 +2,10 @@ ; .include "../inc/common.i" +.ifndef NB65_API_VERSION_NUMBER + .define EQU = + .include "../inc/nb65_constants.i" +.endif .export icmp_init .export icmp_process @@ -16,32 +20,51 @@ .exportzp icmp_code .exportzp icmp_cksum .exportzp icmp_data - + +.ifdef TCP + .export icmp_echo_ip + .export icmp_send_echo + .export icmp_ping +.endif + + + + .import ip65_process + .import ip65_error .import ip_calc_cksum .import ip_inp .import ip_outp - .import ip_broadcast + .import ip_broadcast + .import ip_send + .import ip_create_packet + .importzp ip_proto + .importzp ip_proto_icmp + .importzp ip_cksum_ptr .importzp ip_header_cksum .importzp ip_src .importzp ip_dest .importzp ip_data - + .importzp ip_len + .import eth_tx .import eth_inp .import eth_inp_len .import eth_outp - .import eth_outp_len - + .import eth_outp_len + .import timer_read + .import timer_timeout + .bss ; argument for icmp_add_listener -icmp_callback: .res 2 +icmp_callback: .res 2 + ; icmp callbacks -icmp_cbmax = 4 +icmp_cbmax = 2 icmp_cbtmp: .res 3 ; temporary vector icmp_cbveclo: .res icmp_cbmax ; table of listener vectors (lsb) icmp_cbvechi: .res icmp_cbmax ; table of listener vectors (msb) @@ -60,7 +83,32 @@ icmp_data = 4;offset of 'data' field in icmp packet 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 - + +;icmp type codes +icmp_msg_type_echo_reply=0 +icmp_msg_type_destination_unreachable=3 +icmp_msg_type_source_quench=4 +icmp_msg_type_redirect=5 +icmp_msg_type_echo_request=8 +icmp_msg_type_time_exceeded=11 +icmp_msg_type_paramater_problem=12 +icmp_msg_type_timestamp=13 +icmp_msg_type_timestamp_reply=14 +icmp_msg_type_information_request=15 +icmp_msg_type_information_reply=16 + +;ping states +ping_state_request_sent=0 +ping_state_response_received=1 + + +.ifdef TCP +.segment "TCP_VARS" +icmp_echo_ip: .res 4 ; destination IP address for echo request ("ping") +icmp_echo_cnt: .res 1 ;ping sequence counter +ping_state: .res 1 + ping_timer: .res 2 ; +.endif .code @@ -84,7 +132,7 @@ icmp_init: ; generated and sent out (overwriting the eth_outp buffer) icmp_process: lda icmp_inp + icmp_type - cmp #8 ; ping + cmp #icmp_msg_type_echo_request ; ping beq @echo lda icmp_cbcount ; any installed icmp listeners? @@ -203,7 +251,7 @@ icmp_add_listener: rts -;add an icmp listener +;remove an icmp listener ;inputs: ; A = icmp type ;outputs: @@ -211,12 +259,12 @@ icmp_add_listener: ; clear if no error icmp_remove_listener: ldx icmp_cbcount ; any listeners installed? - beq @notfound + beq @notfound + dex : cmp icmp_cbtype,x ; check if type is listened beq @remove - inx - cpx icmp_cbcount - bne :- + dex + bpl :- @notfound: sec rts @@ -241,3 +289,158 @@ icmp_remove_listener: dec icmp_cbcount ; decrement counter clc rts + +.ifdef TCP + +; icmp_send_echo was contributed by Glen Holmer (ShadowM) + +;send an ICMP echo ("ping") request +;inputs: +; icmp_echo_ip: destination IP address +;outputs: +; carry flag - set if error, clear if no error +icmp_send_echo: + ldy #3 +: + lda icmp_echo_ip,y + sta ip_outp + ip_dest,y + dey + bpl :- + + + lda #icmp_msg_type_echo_request + sta icmp_outp + icmp_type + lda #0 ;not used for echo packets + sta icmp_outp + icmp_code + sta icmp_outp + icmp_cksum ;clear checksum + sta icmp_outp + icmp_cksum + 1 + sta icmp_outp + icmp_echo_id ;set id to 0 + sta icmp_outp + icmp_echo_id + 1 + inc icmp_echo_cnt + 1 ;big-endian + bne :+ + inc icmp_echo_cnt +: + ldax icmp_echo_cnt + stax icmp_outp + icmp_echo_seq + + ldy #0 +: + lda ip65_msg,y + beq @set_ip_len + sta icmp_outp + icmp_echo_data,y + iny + bne :- +@set_ip_len: + tya + clc + adc #28 ;IP header + ICMP type, code, cksum, id, seq + sta ip_outp + ip_len + 1 ;high byte first + lda #0 ;will never be >256 + sta ip_outp + ip_len + + ldax #icmp_outp ;start of ICMP packet + stax ip_cksum_ptr + tya + clc + adc #8 ;ICMP type, code, cksum, id, seq + ldx #0 ;AX = length of ICMP data + jsr ip_calc_cksum + stax icmp_outp + icmp_cksum + lda #ip_proto_icmp + sta ip_outp + ip_proto + jsr ip_create_packet + jmp ip_send + +;send a ping (ICMP echo request) to a remote host, and wait for a response +;inputs: +; icmp_echo_ip: destination IP address +;outputs: +; carry flag - set if no response, otherwise AX is time (in miliseconds) for host to respond +icmp_ping: + + lda #0 ;reset the "packet sent" counter + sta icmp_echo_cnt +@send_one_message: + jsr icmp_send_echo + bcc @message_sent_ok + ;we couldn't send the message - most likely we needed to do an ARP lookup. + ;so wait a bit, and retry + + jsr timer_read ; read current timer value + stax ping_timer +@loop_during_arp_lookup: + jsr ip65_process + ldax ping_timer + adc #50 ; set timeout to now + 50 ms + bcc :+ + inx +: + + jsr timer_timeout + bcs @loop_during_arp_lookup + jsr icmp_send_echo + bcc @message_sent_ok + ;still can't send? then give up + lda #NB65_ERROR_TRANSMIT_FAILED + sta ip65_error + rts +@message_sent_ok: + jsr timer_read ; read current timer value + stax ping_timer + ldax #icmp_ping_callback + stax icmp_callback + lda #icmp_msg_type_echo_reply + jsr icmp_add_listener + lda #ping_state_request_sent + sta ping_state +@loop_till_get_ping_response: + jsr ip65_process + + lda ping_state + cmp #ping_state_response_received + beq @got_reply + ldax ping_timer + inx ;x rolls over about 4 times per second + inx ;so we will timeout after about 2 seconds + inx + inx + inx + inx + inx + inx + + + jsr timer_timeout + bcs @loop_till_get_ping_response + lda #NB65_ERROR_TIMEOUT_ON_RECEIVE + sta ip65_error + lda #icmp_msg_type_echo_reply + jsr icmp_remove_listener + sec + rts +@got_reply: + lda #icmp_msg_type_echo_reply + jsr icmp_remove_listener + jsr timer_read + sec + sbc ping_timer + pha + txa + sbc ping_timer+1 + tax + pla + clc + rts + +icmp_ping_callback: + lda icmp_inp + icmp_echo_seq + cmp icmp_echo_cnt + bne @not_what_we_were_waiting_for + lda #ping_state_response_received + sta ping_state +@not_what_we_were_waiting_for: + rts + +ip65_msg: + .byte "ip65 - the 6502 IP stack",0 +.endif \ No newline at end of file diff --git a/client/ip65/url.s b/client/ip65/url.s index 62cd06a..d977566 100644 --- a/client/ip65/url.s +++ b/client/ip65/url.s @@ -107,7 +107,7 @@ url_parse: cmp #'H' beq @http @exit_with_error: - lda #NB65_MALFORMED_URL + lda #NB65_ERROR_MALFORMED_URL sta ip65_error @exit_with_sec: sec @@ -132,7 +132,7 @@ lda #url_type_gopher bcs @exit_with_sec jsr dns_resolve bcc :+ - lda #NB65_DNS_LOOKUP_FAILED + lda #NB65_ERROR_DNS_LOOKUP_FAILED sta ip65_error jmp @exit_with_sec : diff --git a/client/nb65/Makefile b/client/nb65/Makefile index f9ee1e6..501207c 100644 --- a/client/nb65/Makefile +++ b/client/nb65/Makefile @@ -11,6 +11,7 @@ INCFILES=\ ../inc/nb65_constants.i\ ../inc/version.i\ +TCP_INCFILES=../inc/gopher.i ../inc/telnet.i ../inc/ping.i IP65LIB=../ip65/ip65.lib IP65TCPLIB=../ip65/ip65_tcp.lib @@ -30,7 +31,7 @@ nb65_c64_ram.o: nb65_c64.s $(INCFILES) nb65_std_cart.o: nb65_c64.s $(INCFILES) $(AS) -DBANKSWITCH_SUPPORT=1 $(AFLAGS) -o $@ $< -nb65_tcp_cart.o: nb65_c64.s $(INCFILES) ../inc/gopher.i ../inc/telnet.i +nb65_tcp_cart.o: nb65_c64.s $(INCFILES) $(TCP_INCFILES) $(AS) -DBANKSWITCH_SUPPORT=3 $(AFLAGS) -o $@ $< nb65_rrnet.o: nb65_c64.s $(INCFILES) diff --git a/client/nb65/nb65_c64.s b/client/nb65/nb65_c64.s index 0401cb7..6ffddb0 100644 --- a/client/nb65/nb65_c64.s +++ b/client/nb65/nb65_c64.s @@ -55,6 +55,7 @@ .include "../inc/gopher.i" .include "../inc/telnet.i" + .include "../inc/ping.i" .endif .import cls .import beep @@ -72,6 +73,7 @@ .import parse_dotted_quad .import dotted_quad_value .import parse_integer + .import print_integer .import get_key_ip65 .import cfg_ip .import cfg_netmask @@ -592,8 +594,21 @@ net_apps_menu: jsr cls lda #14 jsr print_a ;switch to lower case + ldax #telnet_header + jsr print jmp telnet_main_entry @not_telnet: + cmp #KEYCODE_F2 + bne @not_gopher + jsr cls + lda #14 + jsr print_a ;switch to lower case + ldax #gopher_header + jsr print + jsr prompt_for_gopher_resource ;only returns if no server was entered. + jmp exit_gopher +@not_gopher: + cmp #KEYCODE_F3 bne @not_gopher_floodgap_com jsr cls @@ -605,18 +620,20 @@ net_apps_menu: stx resource_pointer_hi ldx #0 jsr select_resource_from_current_directory - - jmp exit_gopher + jmp exit_gopher @not_gopher_floodgap_com: + cmp #KEYCODE_F5 - bne @not_gopher + bne @not_ping jsr cls lda #14 jsr print_a ;switch to lower case - jsr prompt_for_gopher_resource ;only returns if no server was entered. - jmp exit_gopher - -@not_gopher: + ldax #ping_header + jsr print + jsr ping_loop + jmp exit_ping +@not_ping: + cmp #KEYCODE_F7 bne @not_main jmp main_menu @@ -680,13 +697,14 @@ cfg_get_configuration_ptr: rts .if (BANKSWITCH_SUPPORT=$03) +exit_ping: exit_telnet: exit_gopher: lda #142 jsr print_a ;switch to upper case lda #$05 ;petscii for white text jsr print_a - jmp main_menu + jmp net_apps_menu .endif .rodata @@ -695,32 +713,33 @@ netboot65_msg: .include "../inc/version.i" .byte 13,0 main_menu_msg: -.byte 13," MAIN MENU",13,13 +.byte 13,"MAIN MENU",13,13 .byte "F1: TFTP BOOT" .if (BANKSWITCH_SUPPORT=$03) -.byte " F3: NET APPS" +.byte " F3: NET APPS" .else -.byte " F3: BASIC" +.byte " F3: BASIC" .endif .byte 13 -.byte "F5: ARP TABLE F7: CONFIG",13,13 +.byte "F5: ARP TABLE F7: CONFIG",13,13 .byte 0 config_menu_msg: -.byte 13," CONFIGURATION",13,13 -.byte "F1: IP ADDRESS F2: NETMASK",13 -.byte "F3: GATEWAY F4: DNS SERVER",13 -.byte "F5: TFTP SERVER F6: RESET TO DEFAULT",13 +.byte 13,"CONFIGURATION",13,13 +.byte "F1: IP ADDRESS F2: NETMASK",13 +.byte "F3: GATEWAY F4: DNS SERVER",13 +.byte "F5: TFTP SERVER F6: RESET TO DEFAULT",13 .byte "F7: MAIN MENU",13,13 .byte 0 .if (BANKSWITCH_SUPPORT=$03) net_apps_menu_msg: -.byte 13," NET APPS",13,13 -.byte "F1: TELNET F3: GOPHER.FLOODGAP.COM",13 -.byte "F5: GOPHER F7: MAIN MENU",13,13 +.byte 13,"NET APPS",13,13 +.byte "F1: TELNET F2: GOPHER ",13 +.byte "F3: GOPHER (FLOODGAP.COM)",13 +.byte "F5: PING F7: MAIN MENU",13,13 .byte 0 cant_boot_basic: @@ -728,6 +747,10 @@ cant_boot_basic: gopher_initial_location: .byte "1gopher.floodgap.com",$09,"/",$09,"gopher.floodgap.com",$09,"70",$0D,$0A,0 +ping_header: .byte "ping",13,0 +gopher_header: .byte "gopher",13,0 +telnet_header: .byte "telnet",13,0 + .endif downloading_msg: .asciiz "DOWNLOADING " diff --git a/client/test/Makefile b/client/test/Makefile index 006b6bb..6702896 100644 --- a/client/test/Makefile +++ b/client/test/Makefile @@ -19,7 +19,6 @@ INCFILES=\ ../inc/net.i\ all: \ -# ip65test.dsk \ testdns.prg \ test_disk_io.prg \ test_disk_io.d64 \ @@ -31,8 +30,10 @@ all: \ testdottedquad.prg\ test_tcp.prg \ test_parser.prg \ + test_ping.prg \ test_get_url.prg \ - +# ip65test.dsk \ + %.o: %.c $(CC) -c $(CFLAGS) $< @@ -51,6 +52,9 @@ test_parser.prg: test_parser.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c6 test_get_url.prg: test_get_url.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg $(LD) -m test_get_url.map -vm -C ../cfg/c64prg.cfg -o test_get_url.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) +test_ping.prg: test_ping.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg + $(LD) -m test_ping.map -vm -C ../cfg/c64prg.cfg -o test_ping.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) + %.pg2: %.o $(IP65LIB) $(APPLE2PROGLIB) $(INCFILES) ../cfg/a2bin.cfg $(LD) -C ../cfg/a2bin.cfg -o $*.pg2 $(AFLAGS) $< $(IP65LIB) $(APPLE2PROGLIB) diff --git a/client/test/test_disk_io.s b/client/test/test_disk_io.s index 9bc3974..919167f 100644 --- a/client/test/test_disk_io.s +++ b/client/test/test_disk_io.s @@ -289,9 +289,6 @@ fname: loading: .byte "LOADING ",0 .rodata -press_a_key_to_continue: - .byte "PRESS A KEY TO CONTINUE",13,0 - filetype: .byte "TYPE: $",0 diff --git a/client/test/test_ping.s b/client/test/test_ping.s new file mode 100644 index 0000000..a11c567 --- /dev/null +++ b/client/test/test_ping.s @@ -0,0 +1,75 @@ + .include "../inc/common.i" + .include "../inc/commonprint.i" + .include "../inc/net.i" + + .import exit_to_basic + + .import cfg_get_configuration_ptr + .import copymem + .importzp copy_src + .importzp copy_dest + + .import icmp_echo_ip + .import icmp_ping + + + .import __CODE_LOAD__ + .import __CODE_SIZE__ + .import __RODATA_SIZE__ + .import __DATA_SIZE__ + + .segment "STARTUP" ;this is what gets put at the start of the file on the C64 + + .word basicstub ; load address + +basicstub: + .word @nextline + .word 2003 + .byte $9e + .byte <(((init / 1000) .mod 10) + $30) + .byte <(((init / 100 ) .mod 10) + $30) + .byte <(((init / 10 ) .mod 10) + $30) + .byte <(((init ) .mod 10) + $30) + .byte 0 +@nextline: + .word 0 + +.code + +init: + jsr print_cr + init_ip_via_dhcp + jsr print_ip_config + jsr print_cr + + ;our default gateway is probably a safe thing to ping + ldx #$3 +: + lda cfg_gateway,x + sta icmp_echo_ip,x + dex + bpl :- + ldax #pinging + jsr print + + ldax #icmp_echo_ip + jsr print_dotted_quad + jsr print_cr + jsr icmp_ping + bcs @error + jsr print_integer + ldax #ms + jsr print + jsr print_arp_cache + rts +@error: + jmp print_errorcode + +.rodata +ms: .byte " MS",13,0 +pinging: .byte "PINGING ",0 +.bss +block_number: .res 1 +block_length: .res 2 +buffer1: .res 256 +buffer2: .res 256 \ No newline at end of file diff --git a/dist/make_error_codes.rb b/dist/make_error_codes.rb new file mode 100644 index 0000000..be43f9a --- /dev/null +++ b/dist/make_error_codes.rb @@ -0,0 +1,11 @@ +errors="<table><tr><th>ERROR CODE</th><th>DESCRIPTION</th></tr>\n" +IO.readlines("netboot65/inc/nb65_constants.i").each do |line| + if line=~/NB65_ERROR_(\S*).*(\$\S\S)/ then + code=$2 + description=$1.gsub("_"," ") + errors<<"<tr><td>#{code}</td><td>#{description}</td></tr>\n" + end +end +errors<<"</table>\n" +puts errors + diff --git a/dist/version_number.txt b/dist/version_number.txt index f76e5a8..f5b38be 100644 --- a/dist/version_number.txt +++ b/dist/version_number.txt @@ -1 +1 @@ -0.9.24 \ No newline at end of file +0.9.25 \ No newline at end of file diff --git a/doc/README.C64.html b/doc/README.C64.html index 265d3ce..bf5c18a 100644 --- a/doc/README.C64.html +++ b/doc/README.C64.html @@ -57,8 +57,9 @@ Once the IP stack is initialised, the "main menu" screen will be displayed. Ther <li>Line - Data is converted to/from ASCII, but each line of input can be edited and is not sent until the RETURN key is pressed.</li> </ul> Once a connection is made, it can be terminated by hitting RUN/STOP + <li>F2 : GOPHER. You will be prompted to enter the hostname (only - no port number can be specified) of a gopher server, and the gopher client will be launched connecting to the specified server. <li>F3 : GOPHER.FLOODGAP.COM. This will launch the Gopher client, and connect to the gopher portal at gopher://gopher.floodgap.com/</li> - <li>F5 : GOPHER. You will be prompted to enter the hostname (only - no port number can be specified) of a gopher server, and the gopher client will be launched connecting to the specified server. + <li>F5 : PING. You will be prompted for a hostname which will be pinged 3 times, and a response time (in milliseconds) is printed for each ping.</li> <li>F7 : MAIN MENU. This will return to the main menu.</li> </ul> @@ -100,7 +101,26 @@ Files need to be placed in the 'boot/' folder. <p> Due to a limitation in the menu selection code, only the first 128 PRG files in the boot/ folder can be selected. - +<h2>ERROR CODES</h2> +Most network functions will return an 8 bit error code if things go wrong. +<table><tr><th>ERROR CODE</th><th>DESCRIPTION</th></tr> +<tr><td>$80</td><td>PORT IN USE</td></tr> +<tr><td>$81</td><td>TIMEOUT ON RECEIVE</td></tr> +<tr><td>$82</td><td>TRANSMIT FAILED</td></tr> +<tr><td>$83</td><td>TRANSMISSION REJECTED BY PEER</td></tr> +<tr><td>$84</td><td>INPUT TOO LARGE</td></tr> +<tr><td>$85</td><td>DEVICE FAILURE</td></tr> +<tr><td>$86</td><td>ABORTED BY USER</td></tr> +<tr><td>$87</td><td>LISTENER NOT AVAILABLE</td></tr> +<tr><td>$88</td><td>NO SUCH LISTENER</td></tr> +<tr><td>$89</td><td>CONNECTION RESET BY PEER</td></tr> +<tr><td>$8A</td><td>CONNECTION CLOSED</td></tr> +<tr><td>$90</td><td>FILE ACCESS FAILURE</td></tr> +<tr><td>$A0</td><td>MALFORMED URL</td></tr> +<tr><td>$A1</td><td>DNS LOOKUP FAILED</td></tr> +<tr><td>$FE</td><td>OPTION NOT SUPPORTED</td></tr> +<tr><td>$FF</td><td>FUNCTION NOT SUPPORTED</td></tr> +</table> <h2>REQUIREMENTS</h2> <ol> <li>RR-NET or compatible adaptor (to use under VICE, you will need pcap or winpcap installed)</li> diff --git a/doc/nb65_api_technical_reference.doc b/doc/nb65_api_technical_reference.doc index 7908811..32e3bf5 100644 Binary files a/doc/nb65_api_technical_reference.doc and b/doc/nb65_api_technical_reference.doc differ