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="ERROR CODE | DESCRIPTION |
\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<<"#{code} | #{description} |
\n"
+ end
+end
+errors<<"
\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
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.
Once a connection is made, it can be terminated by hitting RUN/STOP
+ 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.
F3 : GOPHER.FLOODGAP.COM. This will launch the Gopher client, and connect to the gopher portal at gopher://gopher.floodgap.com/
- 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.
+ 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.
F7 : MAIN MENU. This will return to the main menu.
@@ -100,7 +101,26 @@ Files need to be placed in the 'boot/' folder.
Due to a limitation in the menu selection code, only the first 128 PRG files in the boot/ folder can be selected.
-
+
ERROR CODES
+Most network functions will return an 8 bit error code if things go wrong.
+ERROR CODE | DESCRIPTION |
+$80 | PORT IN USE |
+$81 | TIMEOUT ON RECEIVE |
+$82 | TRANSMIT FAILED |
+$83 | TRANSMISSION REJECTED BY PEER |
+$84 | INPUT TOO LARGE |
+$85 | DEVICE FAILURE |
+$86 | ABORTED BY USER |
+$87 | LISTENER NOT AVAILABLE |
+$88 | NO SUCH LISTENER |
+$89 | CONNECTION RESET BY PEER |
+$8A | CONNECTION CLOSED |
+$90 | FILE ACCESS FAILURE |
+$A0 | MALFORMED URL |
+$A1 | DNS LOOKUP FAILED |
+$FE | OPTION NOT SUPPORTED |
+$FF | FUNCTION NOT SUPPORTED |
+
REQUIREMENTS
- RR-NET or compatible adaptor (to use under VICE, you will need pcap or winpcap installed)
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