From 9c2e55ce3b70f1c8034c1f2509b455a01d66d3de Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Tue, 18 Sep 2018 22:01:19 +0200 Subject: [PATCH] Migrated GitHub pages from gh-pages branch to /docs folder. --- docs/ca65-doc-style.css | 55 + docs/constant_index.html | 1 + docs/drivers_a2charconv_s.html | 39 + docs/drivers_a2input_s.html | 230 +++ docs/drivers_a2kernal_s.html | 26 + docs/drivers_a2print_s.html | 86 + docs/drivers_a2timer_s.html | 80 + docs/drivers_c64_vt100_s.html | 2164 ++++++++++++++++++++++++++ docs/drivers_c64inputs_s.html | 223 +++ docs/drivers_c64kernal_s.html | 28 + docs/drivers_c64print_s.html | 123 ++ docs/drivers_c64timer_s.html | 75 + docs/drivers_cbm_disk_access_s.html | 689 ++++++++ docs/drivers_cs8900a_s.html | 312 ++++ docs/drivers_lan91c96_s.html | 483 ++++++ docs/drivers_petscii_charconv_s.html | 79 + docs/drivers_rr-net_s.html | 67 + docs/drivers_uthernet_s.html | 57 + docs/drivers_vic20-rr-net_s.html | 62 + docs/drivers_vic20input_s.html | 222 +++ docs/drivers_vic20print_s.html | 106 ++ docs/drivers_vic20timer_s.html | 134 ++ docs/drivers_w5100_s.html | 1123 +++++++++++++ docs/function_index.html | 1 + docs/index.html | 203 +++ docs/ip65_arithmetic_s.html | 179 +++ docs/ip65_arp_s.html | 465 ++++++ docs/ip65_cifs_s.html | 940 +++++++++++ docs/ip65_config_s.html | 105 ++ docs/ip65_copymem_s.html | 84 + docs/ip65_debug_s.html | 188 +++ docs/ip65_dhcp_s.html | 530 +++++++ docs/ip65_dns_s.html | 499 ++++++ docs/ip65_dottedquad_s.html | 113 ++ docs/ip65_eth_s.html | 114 ++ docs/ip65_function_dispatcher_s.html | 704 +++++++++ docs/ip65_http_s.html | 280 ++++ docs/ip65_httpd_s.html | 636 ++++++++ docs/ip65_icmp_s.html | 500 ++++++ docs/ip65_ip65_s.html | 166 ++ docs/ip65_ip_s.html | 533 +++++++ docs/ip65_output_buffer_s.html | 30 + docs/ip65_parser_s.html | 114 ++ docs/ip65_printf_s.html | 441 ++++++ docs/ip65_sntp_s.html | 219 +++ docs/ip65_string_utils_s.html | 118 ++ docs/ip65_tcp_s.html | 1140 ++++++++++++++ docs/ip65_telnet_s.html | 511 ++++++ docs/ip65_tftp_s.html | 651 ++++++++ docs/ip65_timer_s.html | 70 + docs/ip65_udp_s.html | 389 +++++ docs/ip65_url_s.html | 489 ++++++ docs/ip65_xmodem_s.html | 609 ++++++++ docs/ref_frames.html | 18 + docs/ref_index.html | 1 + docs/test_test_disk_io_s.html | 472 ++++++ docs/test_test_getc_s.html | 182 +++ docs/variable_index.html | 1 + 58 files changed, 18159 insertions(+) create mode 100644 docs/ca65-doc-style.css create mode 100644 docs/constant_index.html create mode 100644 docs/drivers_a2charconv_s.html create mode 100644 docs/drivers_a2input_s.html create mode 100644 docs/drivers_a2kernal_s.html create mode 100644 docs/drivers_a2print_s.html create mode 100644 docs/drivers_a2timer_s.html create mode 100644 docs/drivers_c64_vt100_s.html create mode 100644 docs/drivers_c64inputs_s.html create mode 100644 docs/drivers_c64kernal_s.html create mode 100644 docs/drivers_c64print_s.html create mode 100644 docs/drivers_c64timer_s.html create mode 100644 docs/drivers_cbm_disk_access_s.html create mode 100644 docs/drivers_cs8900a_s.html create mode 100644 docs/drivers_lan91c96_s.html create mode 100644 docs/drivers_petscii_charconv_s.html create mode 100644 docs/drivers_rr-net_s.html create mode 100644 docs/drivers_uthernet_s.html create mode 100644 docs/drivers_vic20-rr-net_s.html create mode 100644 docs/drivers_vic20input_s.html create mode 100644 docs/drivers_vic20print_s.html create mode 100644 docs/drivers_vic20timer_s.html create mode 100644 docs/drivers_w5100_s.html create mode 100644 docs/function_index.html create mode 100644 docs/index.html create mode 100644 docs/ip65_arithmetic_s.html create mode 100644 docs/ip65_arp_s.html create mode 100644 docs/ip65_cifs_s.html create mode 100644 docs/ip65_config_s.html create mode 100644 docs/ip65_copymem_s.html create mode 100644 docs/ip65_debug_s.html create mode 100644 docs/ip65_dhcp_s.html create mode 100644 docs/ip65_dns_s.html create mode 100644 docs/ip65_dottedquad_s.html create mode 100644 docs/ip65_eth_s.html create mode 100644 docs/ip65_function_dispatcher_s.html create mode 100644 docs/ip65_http_s.html create mode 100644 docs/ip65_httpd_s.html create mode 100644 docs/ip65_icmp_s.html create mode 100644 docs/ip65_ip65_s.html create mode 100644 docs/ip65_ip_s.html create mode 100644 docs/ip65_output_buffer_s.html create mode 100644 docs/ip65_parser_s.html create mode 100644 docs/ip65_printf_s.html create mode 100644 docs/ip65_sntp_s.html create mode 100644 docs/ip65_string_utils_s.html create mode 100644 docs/ip65_tcp_s.html create mode 100644 docs/ip65_telnet_s.html create mode 100644 docs/ip65_tftp_s.html create mode 100644 docs/ip65_timer_s.html create mode 100644 docs/ip65_udp_s.html create mode 100644 docs/ip65_url_s.html create mode 100644 docs/ip65_xmodem_s.html create mode 100644 docs/ref_frames.html create mode 100644 docs/ref_index.html create mode 100644 docs/test_test_disk_io_s.html create mode 100644 docs/test_test_getc_s.html create mode 100644 docs/variable_index.html diff --git a/docs/ca65-doc-style.css b/docs/ca65-doc-style.css new file mode 100644 index 0000000..8a9207e --- /dev/null +++ b/docs/ca65-doc-style.css @@ -0,0 +1,55 @@ +body { background-color: #cde; color: #333; } + +body, p, ol, ul, td, th { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 9pt; +/* line-height: 12pt;*/ +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 16px; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +td,th { + background-color: #eee; + vertical-align: text-top; +/* padding-left: 3px; + padding-right: 3px; +*/ +} + +tr.header { + background-color: #aaf; +} + +p.footer { + font-size: 12px; + line-height: 16px; +} + +h2 { + background-color: #bcd; +} + +h3 { + background-color: #bcd; +} + +img { + border: 0px; +} + + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + + + diff --git a/docs/constant_index.html b/docs/constant_index.html new file mode 100644 index 0000000..1f417a9 --- /dev/null +++ b/docs/constant_index.html @@ -0,0 +1 @@ +

constants

constantdefined in
ac_sizeip65/arp.s
acc16ip65/arithmetic.s
acc32ip65/arithmetic.s
cfg_mac_defaultip65/config.s
cfg_sizeip65/config.s
console_outip65/debug.s
cs_packet_datadrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
cs_packet_pagedrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
cs_rxtx_datadrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
cs_tx_cmddrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
cs_tx_lendrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
eth_dataip65/eth.s
eth_destip65/eth.s
eth_driver_io_basedrivers/w5100.s, drivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s, drivers/lan91c96.s
eth_driver_namedrivers/w5100.s, drivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s, drivers/lan91c96.s
eth_proto_arpip65/eth.s
eth_proto_ipip65/eth.s
eth_srcip65/eth.s
eth_typeip65/eth.s
filter_dnsdrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
filter_ipdrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
filter_numberdrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
filter_textdrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
filter_urldrivers/vic20input.s, drivers/c64inputs.s
icmp_cksumip65/icmp.s
icmp_codeip65/icmp.s
icmp_dataip65/icmp.s
icmp_inpip65/icmp.s
icmp_outpip65/icmp.s
icmp_typeip65/icmp.s
io_callbackdrivers/cbm_disk_access.s
io_device_nodrivers/cbm_disk_access.s
io_error_bufferdrivers/cbm_disk_access.s
ip_dataip65/ip.s
ip_destip65/ip.s
ip_fragip65/ip.s
ip_header_cksumip65/ip.s
ip_idip65/ip.s
ip_inpip65/ip.s
ip_lenip65/ip.s
ip_outpip65/ip.s
ip_protoip65/ip.s
ip_proto_icmpip65/ip.s
ip_proto_tcpip65/ip.s
ip_proto_udpip65/ip.s
ip_srcip65/ip.s
ip_tosip65/ip.s
ip_ttlip65/ip.s
ip_ver_ihlip65/ip.s
screen_current_coldrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
screen_current_rowdrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
sntp_ipip65/sntp.s
tcp_remote_ipip65/tcp.s, drivers/w5100.s
udp_cksumip65/udp.s
udp_dataip65/udp.s
udp_dest_portip65/udp.s
udp_inpip65/udp.s
udp_lenip65/udp.s
udp_outpip65/udp.s
udp_src_portip65/udp.s
url_ipip65/url.s
url_portip65/url.s
url_selectorip65/url.s
\ No newline at end of file diff --git a/docs/drivers_a2charconv_s.html b/docs/drivers_a2charconv_s.html new file mode 100644 index 0000000..76dc3ad --- /dev/null +++ b/docs/drivers_a2charconv_s.html @@ -0,0 +1,39 @@ +

ip65 technical reference

File : drivers/a2charconv.s

functions

functiondescription
ascii_to_native
given an ASCII char in A, return equivalent A2 Screen Code
native_to_ascii
given an A2 Screen Code char in A, return equivalent ASCII

implementation

+
+.export ascii_to_native
+.export native_to_ascii
+
+;given an A2 Screen Code char in A, return equivalent ASCII
+native_to_ascii:
+;just strip high bit
+  and #$7f
+  rts
+
+;given an ASCII char in A, return equivalent A2 Screen Code
+ascii_to_native:
+;set high bit
+  ora #$80
+  rts
+
+
+
+
+;-- LICENSE FOR a2charconv.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_a2input_s.html b/docs/drivers_a2input_s.html new file mode 100644 index 0000000..9a86765 --- /dev/null +++ b/docs/drivers_a2input_s.html @@ -0,0 +1,230 @@ +

ip65 technical reference

File : drivers/a2input.s

functions

functiondescription
check_for_abort_key
check whether the escape key is being pressed
+inputs: none
+outputs: sec if escape pressed, clear otherwise
get_filtered_input
cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+======================================================================
+Input a string and store it in GOTINPUT, terminated with a null byte.
+AX is a pointer to the allowed list of characters, null-terminated.
+set AX to $0000 for no filter on input
+max # of chars in y returns num of chars entered in y.
+======================================================================
+ Main entry
get_key
use Apple 2 monitor ROM function to read from keyboard
+inputs: none
+outputs: A contains ASCII code of key pressed
get_key_if_available
inputs: none
+outputs: A contains ASCII value of key just pressed (0 if no key pressed)
get_key_ip65
process inbound ip packets while waiting for a keypress

constants

constantsdescriptionvalue
filter_dns"-ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
filter_ip"." +
filter_number"1234567890",0 +
filter_text================================================= +Some example filters +================================================= ",+!#$%&'()* " +

implementation

.export get_key  
+.export check_for_abort_key
+.export get_filtered_input
+.export filter_text
+.export filter_ip
+.export filter_dns
+.export filter_number
+.export get_key_ip65
+.export get_key_if_available
+
+.import ip65_process
+.import print_a
+.import print_hex
+
+.importzp copy_src
+
+.include "../inc/common.i"
+
+allowed_ptr=copy_src ;reuse zero page
+.code
+;use Apple 2 monitor ROM function to read from keyboard
+;inputs: none
+;outputs: A contains ASCII code of key pressed
+get_key:
+  ;lda #$a0
+  ;lda #$20
+  ;ldy #$24 ;KEYIN assumes Y is loaded with column
+  ;jmp $fd1b
+  jmp $fd0c
+
+;inputs: none
+;outputs: A contains ASCII value of key just pressed (0 if no key pressed)
+get_key_if_available:
+  bit $c000 ;Key down?
+  bmi get_key
+  lda #0
+  rts
+
+
+
+  
+;check whether the escape key is being pressed
+;inputs: none
+;outputs: sec if escape pressed, clear otherwise
+check_for_abort_key:
+lda $c000 ;current key pressed
+cmp #$9B
+bne :+
+bit $c010 ;clear the keyboard strobe
+sec
+rts
+:
+clc
+rts
+
+;process inbound ip packets while waiting for a keypress
+get_key_ip65:
+  jsr ip65_process
+  bit $c000 ;Key down?
+  bpl get_key_ip65
+  jmp get_key
+
+
+
+;cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+;======================================================================
+;Input a string and store it in GOTINPUT, terminated with a null byte.
+;AX is a pointer to the allowed list of characters, null-terminated.
+;set AX to $0000 for no filter on input
+;max # of chars in y returns num of chars entered in y.
+;======================================================================
+
+
+; Main entry
+get_filtered_input:
+  sty MAXCHARS
+  stax temp_allowed
+
+  ;Zero characters received.
+  lda #$00
+  sta INPUT_Y
+
+;Wait for a character.
+@input_get:
+  jsr get_key_ip65  
+  ;convert to standard ASCII by turning off high bit
+  and #$7f
+  sta LASTCHAR
+  cmp #$08               ;Delete
+  beq @delete
+
+  cmp #$0d               ;Return
+  beq @input_done
+
+  ;End reached?
+  lda INPUT_Y
+  cmp MAXCHARS
+  beq @input_get
+
+  ;Check the allowed list of characters.
+  ldax temp_allowed
+  stax allowed_ptr  ;since we are reusing this zero page, it may not stil be the same value since last time!
+  ldy #$00
+  lda allowed_ptr+1     ;was the input filter point nul?
+  beq @input_ok
+@check_allowed:
+  lda (allowed_ptr),y           ;Overwritten
+  beq @input_get         ;Reached end of list (0)
+
+  cmp LASTCHAR
+  beq @input_ok           ;Match found
+
+  ;Not end or match, keep checking
+  iny
+  jmp @check_allowed
+
+@input_ok:
+  lda LASTCHAR          ;Get the char back
+  ldy INPUT_Y
+  sta GOTINPUT,y        ;Add it to string
+
+  inc INPUT_Y           ;Next character
+  jsr  print_a
+  ;Not yet.
+  jmp @input_get
+
+@input_done:
+   ldy INPUT_Y
+   beq  @no_input
+   lda #$00
+   sta GOTINPUT,y   ;Zero-terminate
+   clc
+   ldax #GOTINPUT
+   rts
+@no_input:
+   sec
+   rts
+; Delete last character.
+@delete:
+  ;First, check if we're at the beginning.  If so, just exit.
+  lda INPUT_Y
+  bne @delete_ok
+  jmp @input_get
+
+  ;At least one character entered.
+@delete_ok:
+  ;Move pointer back.
+  dec INPUT_Y
+
+  ;Store a zero over top of last character, just in case no other characters are entered.
+  ldy INPUT_Y
+  lda #$00
+  sta GOTINPUT,y
+
+  ;Print the backspace char
+  lda #$88
+  jsr print_a
+
+  ;Print the a space
+  lda #$a0
+  jsr print_a
+
+  ;Print the backspace char
+  lda #$88
+  jsr print_a
+
+  ;Wait for next char
+  jmp @input_get
+
+
+;=================================================
+;Some example filters
+;=================================================
+
+filter_text:
+  .byte ",+!#$%&'()* "
+filter_dns:
+.byte "-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+filter_ip:
+.byte "."
+filter_number: 
+.byte "1234567890",0
+
+;=================================================
+.bss
+temp_allowed: .res 2
+MAXCHARS: .res 1
+LASTCHAR: .res 1
+INPUT_Y: .res 1  
+GOTINPUT: .res 40
+
+
+
+;-- LICENSE FOR a2input.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_a2kernal_s.html b/docs/drivers_a2kernal_s.html new file mode 100644 index 0000000..2b02f41 --- /dev/null +++ b/docs/drivers_a2kernal_s.html @@ -0,0 +1,26 @@ +

ip65 technical reference

File : drivers/a2kernal.s

functions

functiondescription
exit_to_basic

implementation

.export exit_to_basic  
+
+.code
+exit_to_basic:
+  jmp $3d0
+
+
+;-- LICENSE FOR a2kernal.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_a2print_s.html b/docs/drivers_a2print_s.html new file mode 100644 index 0000000..5d2dd2a --- /dev/null +++ b/docs/drivers_a2print_s.html @@ -0,0 +1,86 @@ +

ip65 technical reference

File : drivers/a2print.s

functions

functiondescription
beep
use Apple 2 monitor ROM function to move to make a 'beep' noise
+inputs: none
+outputs: none
cls
use Apple 2 monitor ROM function to move to clear the screen
+inputs: none
+outputs: none  
+use Apple 2 monitor ROM function to display 1 char
+inputs: A should be set to ASCII char to display
+outputs: none
use Apple 2 monitor ROM function to move to new line
+inputs: none
+outputs: none

constants

constantsdescriptionvalue
screen_current_col CH - Horizontal cursor-position (0-39) $24
screen_current_row CV - Vertical cursor-position (0-23) $25

implementation

+.export print_a
+.export print_a_inverse
+.export print_cr
+.export cls
+.export beep
+.exportzp screen_current_row
+.exportzp screen_current_col
+
+.code
+
+screen_current_col=$24 ; CH - Horizontal cursor-position (0-39)
+screen_current_row=$25 ; CV - Vertical cursor-position (0-23)
+
+;
+;use Apple 2 monitor ROM function to display 1 char
+;inputs: A should be set to ASCII char to display
+;outputs: none
+print_a:
+  ora  #$80  ;turn ASCII into Apple 2 screen codes  
+  cmp #$8A   ;is it a line feed?
+  bne @not_line_feed
+;  jmp print_cr
+  pha
+  lda #$0
+  sta screen_current_col
+  pla
+@not_line_feed:
+  
+  jmp $fded
+
+
+;use Apple 2 monitor ROM function to move to new line
+;inputs: none
+;outputs: none
+print_cr:
+
+  jmp $fd8e
+    
+
+;use Apple 2 monitor ROM function to move to clear the screen
+;inputs: none
+;outputs: none  
+cls:
+    jmp $fc58
+
+;use Apple 2 monitor ROM function to move to make a 'beep' noise
+;inputs: none
+;outputs: none
+beep:
+  jmp $fbdd
+  
+print_a_inverse:
+  and  #$7F  ;turn off top bits
+  jsr $fded
+
+
+
+;-- LICENSE FOR a2print.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_a2timer_s.html b/docs/drivers_a2timer_s.html new file mode 100644 index 0000000..5837edf --- /dev/null +++ b/docs/drivers_a2timer_s.html @@ -0,0 +1,80 @@ +

ip65 technical reference

File : drivers/a2timer.s

 timer routines
+
+ unfortunately the standard Apple 2 has no CIA or VBI, so for the moment, we will
+ make each call to 'timer_read' delay for a little while
+ this kludge will make the polling loops work at least
+ 
+ timer_read is meant to return a counter with millisecond resolution
+

functions

functiondescription
timer_init
reset timer to 0
+inputs: none
+outputs: none
timer_read
this SHOULD just read the current timer value 
+but since a standard apple 2 has no dedicated timing circuit,
+each call to this function actually delays a while, then updates the current value
+ inputs: none
+ outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init')

implementation

; timer routines
+;
+; unfortunately the standard Apple 2 has no CIA or VBI, so for the moment, we will
+; make each call to 'timer_read' delay for a little while
+; this kludge will make the polling loops work at least
+; 
+; timer_read is meant to return a counter with millisecond resolution
+
+  .include "../inc/common.i"
+
+
+  .export timer_init
+  .export timer_read
+
+  .bss
+  current_time_value: .res 2
+  
+  .code
+
+;reset timer to 0
+;inputs: none
+;outputs: none
+timer_init:
+  ldax  #0
+  stax current_time_value
+  rts
+
+;this SHOULD just read the current timer value 
+;but since a standard apple 2 has no dedicated timing circuit,
+;each call to this function actually delays a while, then updates the current value
+; inputs: none
+; outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init')
+timer_read:
+  lda #111
+  jsr $fca8 ;wait for about 33ms
+  clc
+  lda #33
+  adc current_time_value
+  sta current_time_value
+  bcc :+
+  inc current_time_value+1
+:
+  ldax  current_time_value
+  rts
+
+
+
+
+;-- LICENSE FOR a2timer.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_c64_vt100_s.html b/docs/drivers_c64_vt100_s.html new file mode 100644 index 0000000..072ee2e --- /dev/null +++ b/docs/drivers_c64_vt100_s.html @@ -0,0 +1,2164 @@ +

ip65 technical reference

File : drivers/c64_vt100.s

 vt100 emulation for C64
+ vt100 emulation for C64
+ originally from CaTer - Copyright Lars Stollenwerk 2003
+ CaTer homepage is http://formica.nusseis.de/Cater/
+ converted for use with ip65 by Jonno Downes, 2009.
+ this version is for C64 only
+ CaTer originally licensed under GPL
+ Lars Stollenwerk has agreed to relicense the code in this file under MPL (Oct 2009)
+
+ to use:
+ 1) call vt100_init_terminal
+ 2) for every 'inbound' data (received from remote host), call "vt100_process_inbound_char" - this will update the screen
+ 3) pass every keypress into vt100_transform_outbound_char. on return from this call,
+      Y = 0 means don't send anything as a result of this keypress
+      Y = 1 means A contains single character to send to remote host
+      Y = 2 means AX points at null terminated string to send to remote host (e.g. an ANSI escape sequence)
+

functions

functiondescription
vt100_init_terminal
intialize VT100 emulation state
+inputs: none
+outputs: none
vt100_process_inbound_char
process incoming character
+inputs:
+ A is inbound character
+outputs: 
+ none, but screen and/or terminal state is updated.
vt100_transform_outbound_char
 *************************************
+ *
+ * outgoing data
+ *
+ *************************************
+ -------------------------------------
+ given a single char (read from keyboard)
+ work out what data should be sent to the remote host.
+ input:
+ A = keypress
+ output:
+ Y=0 - no data to be sent (i.e. ignore keypress)
+ Y=1 - A contains single byte to send
+ Y=2 - AX points to null terminated string to send
+ -------------------------------------
+Y=0 nothing to send
+Y=1 A = char to send
+Y=2 AX=pointer to asciiz string to send

implementation

; vt100 emulation for C64
+; vt100 emulation for C64
+; originally from CaTer - Copyright Lars Stollenwerk 2003
+; CaTer homepage is http://formica.nusseis.de/Cater/
+; converted for use with ip65 by Jonno Downes, 2009.
+; this version is for C64 only
+; CaTer originally licensed under GPL
+; Lars Stollenwerk has agreed to relicense the code in this file under MPL (Oct 2009)
+;
+; to use:
+; 1) call vt100_init_terminal
+; 2) for every 'inbound' data (received from remote host), call "vt100_process_inbound_char" - this will update the screen
+; 3) pass every keypress into vt100_transform_outbound_char. on return from this call,
+;      Y = 0 means don't send anything as a result of this keypress
+;      Y = 1 means A contains single character to send to remote host
+;      Y = 2 means AX points at null terminated string to send to remote host (e.g. an ANSI escape sequence)
+
+
+
+.include "../inc/common.i"
+
+.export vt100_init_terminal
+.export vt100_process_inbound_char
+.export vt100_transform_outbound_char
+
+.import beep
+
+; --- colour values ---
+col_black       = $00
+col_white       = $01
+col_red         = $02
+col_cyan        = $03
+col_purple      = $04
+col_green       = $05
+col_blue        = $06
+col_yellow      = $07
+col_orange      = $08
+col_brown       = $09
+col_light_red   = $0a
+col_gray_1      = $0b
+col_gray_2      = $0c
+col_light_green = $0d
+col_light_blue  = $0e
+col_gray_3      = $0f
+
+; --- colours ---
+; vanilla     f  bold 1
+; underline   e       3
+; blink       5       d
+; blink uline a       7
+charmode_vanilla = $0f
+charmode_bold = $01
+charmode_underline = $0e
+charmode_underline_bold = $03
+charmode_blink = $05
+charmode_blink_bold = $0d
+charmode_blink_underline = $0a
+charmode_blink_underline_bold = $07
+
+; text background 
+text_background_colour = col_black
+
+
+.segment "APP_SCRATCH" 
+escape_buffer: .res $100
+
+
+.zeropage
+
+; --- esc mode ---
+; $00 = normal
+; $0f = esc mode
+; $ff = esc [ mode
+; $f0 = ignore one char
+escape_mode: .res 1
+
+; --- Vector ---
+; four vectors in zeropage 
+; for temporary use
+temp_ptr_z: .res 2
+temp_ptr_y: .res 2
+temp_ptr_x: .res 2
+temp_ptr_w: .res 2
+
+escape_buffer_length: .res 1  ; points to first free position
+escape_parameter: .res 1       ; numeric parameter in esc sequence
+scroll_region_start: .res 1
+scroll_region_end: .res 1
+
+
+; font_mode contains three bits
+; bit 0 = bold
+; bit 1 = underline
+; bit 2 = blink
+; bit 7 = direct ANSI ESC colour
+font_mode: .res 1
+
+direct_colour: .res 1
+
+; --- crsr save area ---
+; here is crsr info saved with 
+; ESC 7 and restored from with
+; ESC 8
+
+saved_font_mode:  .res 1
+saved_reverse_mode:  .res 1
+saved_row:  .res 1
+saved_column:  .res 1
+
+print_ptr: .res 2 ;temp vector for printing to screen
+
+
+
+
+
+; -------------------------------------
+; memory map
+;
+; -------------------------------------
+
+; --- screen --- 
+; $0400 - $07ff
+Screen = $0400
+
+; --- escape buffer --- 
+; $0800 - $0bff
+
+; --- char --- 
+; $2000 - $27ff
+font_table = $2000
+
+; -------------------------------------
+; constant declaration
+;
+; -------------------------------------
+
+esc = $1b
+brace = $5b
+
+.code
+
+;intialize VT100 emulation state
+;inputs: none
+;outputs: none
+vt100_init_terminal:
+
+  jsr initialise_variables ; init memory variables
+  jsr initialise_font; init font
+  jsr initialise_screen ; init screen
+  rts
+  
+;process incoming character
+;inputs:
+; A is inbound character
+;outputs: 
+; none, but screen and/or terminal state is updated.
+
+vt100_process_inbound_char:
+  tay
+  lda escape_mode   ; handle esc mode
+  beq :+            ; to far for branch to escape
+  jmp handle_escape_char
+:        
+  lda ascii_to_petscii,y         ; ASCII to PETSCII
+  beq @done         ; ignore non-printing chars     
+  cmp #$01          ; something special?
+  beq handle_special_char        
+  jsr print_to_screen ; print to screen        
+@done:
+  rts
+
+
+
+do_cr:
+  ldx $d6     ; get row
+  ldy #$00    ; set col=0
+  jsr cursor_plot   ; set crsr
+  rts
+                     
+do_line_feed:
+  ldx $d6     ; crsr line
+  cpx scroll_region_end     ; end scroll region?
+  bne down_one_line    ;  no -> go on
+  jsr cursor_off
+  jsr scroll_up_scrollregion   ;  yes -> scroll up
+  jsr cursor_on
+  rts
+
+handle_special_char:
+  tya         ; restore original char
+  cmp #$0d    ; CR?
+  beq do_cr
+ 
+  cmp #$08    ; BS?
+  bne @not_bs
+  ldy $d3     ; get col
+  beq @bs_done   ; stop at left margin
+  dey         ; dec column
+  ldx $d6     ; get row
+  jsr cursor_plot   ; set crsr
+@bs_done:
+  rts
+@not_bs:
+  cmp #$1b    ; esc?
+  bne @not_escape
+  lda #$0f    ; set esc mode
+  sta escape_mode
+  rts
+@not_escape:
+  cmp #$07    ; BEL?
+  bne @not_bell  
+  jsr beep
+  rts
+@not_bell:
+  cmp #$0a    ; LF?
+  beq do_line_feed
+ 
+@not_lf:
+  cmp #$09    ; TAB?
+  bne @not_tab
+  lda $d3     ; crsr col
+  and #$f8    ; (col DIV 8) * 8
+  clc         ; col + 8
+  adc #$08
+  cmp #$28    ; col=40?
+  bne @not_last_col     ; no -> skip
+  lda #$27    ; yes -> col=39
+@not_last_col:
+  tay         ; col to y
+  ldx $d6     ; line to x
+  jsr cursor_plot   ; set crsr
+  rts
+
+@not_tab:
+  rts        
+
+down_one_line:
+  cpx #$18    ; end of screen?
+  bne @not_end_of_screen     ;  no -> go on
+  rts         ;  yes -> do nothing
+@not_end_of_screen:
+  inx         ; next line
+  ldy $d3     ; get col
+  jsr cursor_plot   ; set crsr
+  rts        
+
+
+; esc mode
+; data in Y
+; escape_mode <> $00 in A
+handle_escape_char:
+  tax         ; save escape_mode
+  and #$0f    ; escape_mode = $0f?
+  bne @not_discard_mode
+
+; --- discard mode --- escape_mode = $f0
+;discard char
+  lda #$00    ; reset escape_mode
+  sta escape_mode
+  rts
+
+@not_discard_mode:
+  txa         ; restore escape_mode
+  and #$f0    ; escape_mode = $ff?
+  beq @short_escape_mode    ; no -> short Emode
+  jmp long_escape_mode    ; yes -> long Emode
+  
+; short esc mode
+; escape_mode = $0f
+; process first char
+@short_escape_mode:
+  tya         ; restore char
+; --- [ ---
+  cmp #brace  ; [ ?
+  bne @not_brace
+  lda #$ff    ; set esc [ mode
+  sta escape_mode
+  rts
+; --- ( ---
+@not_brace:
+        cmp #$28    ; ( ?
+        bne :+
+        jmp set_discard_mode
+; --- ) ---
+: 
+  cmp #$29    ; ) ?
+  bne :+
+  jmp set_discard_mode
+; --- # ---
+:
+  cmp #$23    ; # ?
+  bne :+
+  jmp set_discard_mode
+; --- D --- index 
+:
+    cmp #$44    ; D ?
+    bne :+
+    jsr do_line_feed      ; same as LF
+    jmp done_escape
+; --- M --- reverse index
+:
+  cmp #$4d    ; M ?
+  bne @not_M
+  ldx $d6     ; get crsr row
+  cpx scroll_region_start     ; top of scroll reg?
+  bne :+
+  jsr cursor_off    ; yes -> scroll down
+  jsr scroll_down_scrollregion  
+  jsr cursor_on
+  jmp done_escape
+:
+  cpx #$00    ; top of screen?
+  bne :+
+  jmp done_escape    ; yes -> do nothing
+:     
+  dex         ; one line up
+  ldy $d3     ; get crsr col
+  jsr cursor_plot   ; set crsr
+  jmp done_escape
+
+@not_M:
+; --- E --- next line
+  cmp #$45    ; E ?
+  bne :+
+  jsr do_cr
+  jsr do_line_feed
+  jmp done_escape
+:
+; --- 7 --- save crsr
+  cmp #$37    ; 7?
+  bne :+
+  lda font_mode    ; save font
+  sta saved_font_mode
+  lda $c7     ; save reverse mode
+  sta saved_reverse_mode
+  ldx $d6     ; save position
+  ldy $d3
+  stx saved_row
+  sty saved_column
+  jmp done_escape
+:      
+; --- 8 --- restore crsr
+  cmp #$38    ; 8?
+  bne :+
+  ldx saved_row ; restore pos
+  ldy saved_column
+  jsr cursor_plot
+  lda saved_reverse_mode   ; restore ..
+  sta $c7     ; .. reverse mode
+  ldx saved_font_mode   ; restore font
+  stx font_mode
+  lda font_attribute_table,x
+  sta $0286   ; set colour
+  jmp done_escape
+
+; --- unknown ---
+:   
+; --- reset ESC mode ---
+done_escape:
+  lda #$00    ; reset escape_mode
+  sta escape_mode
+  rts 
+
+; --- set Discard mode ---
+set_discard_mode:
+  lda #$f0    ; set esc mode $f0
+  sta escape_mode
+  rts
+
+
+; -------------------------------------
+; [ esc mode
+;
+; escape_mode = $ff
+; -------------------------------------
+  
+long_escape_mode:
+  tya         ; restore char
+  ldy escape_buffer_length
+  sta escape_buffer,y  ; store char
+  iny
+  sty escape_buffer_length   ; inc esc buffer  
+  jsr test_if_letter   ; test letter
+  bcs :+     ; process command
+  rts
+
+; --- process esc command ---
+; A = last char
+; Y = escape_buffer_length
+; X counts processed command chars
+:
+  ldx #$00    ; first char 
+
+; --- A --- crsr up       
+  cmp #$41    ; A?
+  bne @not_A
+  jsr get_number_from_esc_seq  ; get argument
+  lda escape_parameter    ; escape_parameter = 0...
+  bne :+
+  inc escape_parameter    ; .. means 1
+:
+  lda $d6     ; get crsr row        
+  sec
+  sbc escape_parameter    ; row = row - up
+  cmp scroll_region_start     ; stop at top of ..
+  bpl :+    ; ..scroll region
+  lda scroll_region_start    
+:
+  tax         ; x is row
+  ldy $d3     ; y is col
+  jsr cursor_plot   ; set crsr
+  jmp @end_escape_seq
+  
+; --- B --- crsr down
+@not_A:
+  cmp #$42    ; B?
+  bne @not_B
+  jsr get_number_from_esc_seq  ; get argument
+  lda escape_parameter    ; escape_parameter = 0...
+  bne :+
+  inc escape_parameter    ; .. means 1        
+:
+  lda $d6     ; get crsr row        
+  clc
+  adc escape_parameter    ; row = row + down
+  cmp scroll_region_end     ; outside scrregion?
+  bcs :+    ; yes -> branch
+  tax         ; x is row
+  jmp @skip
+:
+ ldx scroll_region_end     ; x = row = scroll_region_end   
+@skip:
+  ldy $d3     ; y is col
+  jsr cursor_plot   ; set crsr
+  jmp @end_escape_seq
+
+; --- C --- crsr right
+@not_B:
+  cmp #$43    ; C?
+  bne @not_C
+  jsr get_number_from_esc_seq  ; get argument        
+  lda escape_parameter    ; escape_parameter = 0...
+  bne :+
+  inc escape_parameter    ; .. means 1
+:
+  lda $d3     ; get crsr col        
+  clc
+  adc escape_parameter    ; col = col + right
+  cmp #$27    ; outside screen?
+  bcs :+    ; yes -> branch
+  tay
+  jmp @skip2
+:    
+  ldy #$27    ; y=col=left margin
+@skip2:
+  ldx $d6     ; x is row
+  jsr cursor_plot   ; set crsr
+  jmp @end_escape_seq
+
+; --- D --- crsr left
+@not_C:
+  cmp #$44    ; D?
+  bne @not_D
+  jsr get_number_from_esc_seq  ; get argument
+  lda escape_parameter    ; escape_parameter = 0...
+  bne :+
+  inc escape_parameter    ; .. means 1        
+:
+  lda $d3     ; get crsr col        
+  sec
+  sbc escape_parameter    ; col = col - left
+  bpl :+      ; stop at left..
+  lda #$00    ; ..margin
+:
+  tay         ; y is col
+  ldx $d6     ; x is row
+  jsr cursor_plot   ; set crsr
+  jmp @end_escape_seq
+
+; --- m ---  font attributes
+@not_D:
+  cmp #$6d    ; m?
+  bne @not_m
+@next_font_attribute:
+  jsr get_number_from_esc_seq
+  pha         ; save nondigit char
+  lda escape_parameter    ; parameter to A
+  ; -- 0 --
+  bne :+    ; 0?
+  sta font_mode    ; set font = vanilla
+  sta $c7     ; reverse off
+  jmp @end_font_attribute    ; jmp next par
+  ; -- 1 -- bold
+:
+  cmp #$01
+  bne :+
+  lda font_mode    ; set bold
+  ora #$01
+  sta font_mode
+  jmp @end_font_attribute    ; next char
+  ; -- 4 -- underline
+:
+  cmp #$04
+  bne :+
+  lda font_mode    ; set u_line
+  ora #$02
+  sta font_mode
+  jmp @end_font_attribute    ; next char
+  ; -- 5 -- blink
+: 
+  cmp #$05
+  bne :+
+  lda font_mode    ; set blink
+  ora #$04
+  sta font_mode
+  jmp @end_font_attribute    ; next char
+  ; -- 7 -- reverse
+:
+  cmp #$07
+  bne :+
+  lda #$01    ; set revers
+  sta $c7
+  jmp @end_font_attribute    ; next char
+:
+  ; -- 30 - 37 --
+  cmp #38     ; >= 38?
+  bcs @end_font_attribute
+  cmp #30     ; < 30?
+  bcc @end_font_attribute
+  sbc #30     ; pointer for table
+  sta direct_colour
+  lda #$80    ; set direct colour
+  sta font_mode
+  
+@end_font_attribute:  ; -- next char --
+  pla         ; get nondigit char
+  cmp #$3b    ; is semicolon?
+  beq @next_font_attribute    ; then next cahr
+  ; -- set colour --
+  lda font_mode    ; 
+  bmi :+    ; bit 7->direct col
+  tax         ; font to colour
+  lda font_attribute_table,x
+  sta $0286   ; set colour
+  jmp @end_escape_seq
+:
+  ; -- set direct colour --
+  ldx direct_colour ; colour maping
+  lda direct_colour_table,x
+  sta $0286   ; set colour
+  jmp @end_escape_seq
+
+; --- K --- erase line
+@not_m:
+  cmp #$4b      ; K?
+  bne @not_K
+  jsr get_number_from_esc_seq    ; get parameter
+  lda escape_parameter      ; in A
+  ; -- 0 -- crsr to end of line
+  bne :+
+  jsr erase_to_end_of_line    ; erase end line
+  jmp @end_escape_seq
+  ; -- 1 -- begin to crsr
+:
+  cmp #$01
+  bne :+
+  jsr erase_line_to_cursor    ; erase beg line
+  jmp @end_escape_seq
+  ; -- 2 -- whole line
+:
+  cmp #$02
+  bne :+      ; par undefined 
+  ldx $d6       ; line in X
+  jsr erase_line_by_number      ; erase line
+  sta $ce       ; del char ..
+                ; ..under crsr
+:
+  jmp @end_escape_seq        
+  
+; --- f --- same as H
+@not_K:
+  cmp #$66
+  bne @not_f
+  jmp @set_cursor_position      ; same as H
+
+; --- H --- cursor position
+@not_f:
+  cmp #$48
+  bne @not_H
+@set_cursor_position:
+  cpy #$01    ; no par means home
+  bne :+
+  ; -- home --
+  ldx #$00
+  ldy #$00
+  jsr cursor_plot   ; set crsr
+  jmp @end_escape_seq
+  ; -- row, col --
+:
+  jsr get_number_from_esc_seq
+  cmp #$3b    ; is ;?
+  bne @end_set_cursor_position    ; no -> error
+  ; -- prepare row --
+  ldy escape_parameter    ; get row
+  bne :+    ; 0 means 1
+  iny
+:       
+  dey         ; line 1 -> line 0
+  cpy #$19    ; >= 25?..
+  bcs @end_set_cursor_position    ; ..error!
+  sty temp_ptr_x ; save row
+  ; -- prepare col
+  jsr get_number_from_esc_seq
+  ldy escape_parameter    ; get col
+  bne :+    ; 0 means 1
+  iny
+:
+  dey         ; line 1 -> line 0        
+  cpy #$28    ; >= 40?..
+  bcs @end_set_cursor_position    ; ..error!        
+  ldx temp_ptr_x ; restore row to X
+  jsr cursor_plot   ; set crsr
+@end_set_cursor_position:
+  jmp @end_escape_seq
+           
+
+; --- J --- erase screen
+@not_H:
+  cmp #$4a      ;J?
+  bne @not_J
+  jsr get_number_from_esc_seq    ; get parameter
+  lda escape_parameter      ; in A
+  ; -- 0 -- crsr to end
+  bne @not_cursor_to_end
+  jsr erase_to_end_of_line    ; del rest of line
+  ldx $d6       ; get crsr line
+@erase_next_line:
+  inx           ; next line
+  cpx #$19      ; line 25?
+  bcs @end_escape_seq     ; then end
+  txa
+  pha           ; save X
+  jsr erase_line_by_number      ; erase line
+  pla
+  tax           ; restore X
+  jmp @erase_next_line      ; next line
+  ; -- 1 -- beg of screen to crsr
+@not_cursor_to_end:
+  cmp #$01
+  bne @not_start_to_cursor
+  jsr erase_line_to_cursor    ; del start of ln
+  ldx $d6       ; get crsr line
+:
+  dex           ; previous line
+  bmi @end_escape_seq     ; neg line -> end
+  txa
+  pha           ; save X
+  jsr erase_line_by_number      ; erase line
+  pla
+  tax           ; restore X
+  jmp :-
+  ; -- 2 -- del screen
+@not_start_to_cursor:
+  cmp #$02      ; unknown?
+  bne @end_escape_seq     ; then ingnore
+  ldx #$18      ; start at ln 24        
+:
+  txa
+  pha           ; save X
+  jsr erase_line_by_number      ; erase line
+  pla
+  tax           ; restore X
+  dex           ; previous line
+  bpl :-
+  jmp @end_escape_seq
+
+
+; --- r ---  set scroll region                 
+@not_J:
+  cmp #$72    ; r?
+  bne @not_r
+  ; -- prepare top --
+  jsr get_number_from_esc_seq
+  cmp #$3b    ; is ;?
+  bne @error_in_escape_seq   ; no -> error
+  ldy escape_parameter    ; get top
+  dey         ; line 1 -> line 0
+  cpy #$19    ; >=25?..
+  bcs @error_in_escape_seq   ; ..error!
+  sty temp_ptr_x ; save top      
+  ; -- prepare bottom --
+  jsr get_number_from_esc_seq
+  ldy escape_parameter    ; get bottom
+  dey         ; line 1 -> line 0
+  cpy #$19    ; >=25?..
+  bcs @error_in_escape_seq   ; ..error! 
+  sty temp_ptr_y ; save bottom       
+  ; -- validate lines --
+  lda temp_ptr_x ; restore top
+  cmp temp_ptr_y ; >= bottom?..
+  bcs @error_in_escape_seq   ; ..error!
+  sta scroll_region_start     ; top -> SRStart
+  sty scroll_region_end     ; bottom -> SREnd
+  ; -- home crsr
+  ldx #$00
+  ldy #$00
+  jsr cursor_plot
+@error_in_escape_seq:
+  jmp @end_escape_seq        
+        
+
+@not_r:
+; --- unknown ---
+@end_escape_seq:
+  lda #$00
+  sta escape_buffer_length   ; reset esc buffer
+  sta escape_mode   ; reset esc mode
+  rts
+
+
+
+; -------------------------------------
+; Test letter
+;
+; char in A
+; returns carry = 1 for A = letter
+; -------------------------------------
+test_if_letter:
+  cmp #$41    ; smaller then A?
+  bcs :+     ; no -> go on
+  rts         ; return no letter
+:
+  cmp #$5b    ; smaller then Z+1?
+  bcs :+     ; no -> go on
+  sec         ; return letter
+  rts
+:  
+  cmp #$61    ; smaller then a?
+  bcs :+     ; no -> go on
+  rts         ; return no letter
+:  
+  cmp #$7b    ; smaller then z+1?        
+  bcs :+     ; no -> go on
+  sec         ; return letter
+  rts
+:
+  clc         ; return no letter
+  rts        
+        
+
+
+; -------------------------------------
+; test digit
+;
+; char in A
+; returns carry = 1 for A = digit
+; -------------------------------------
+
+test_if_digit:
+  cmp #$30    ; smaller then 0?
+  bcs :+     ; no -> go on
+  rts         ; return no digit
+:
+  cmp #$3a    ; smaller then 9+1?
+  bcs :+     ; no -> go on
+  sec         ; return digit
+  rts
+:
+  clc         ; return no digit
+  rts
+
+
+; -------------------------------------
+; get decimal number from esc sequence
+;
+; esc sequence in escape_buffer
+; first index to process in X
+; returns: number escape_parameter
+;          first non digit char in  A
+; -------------------------------------
+get_number_from_esc_seq:
+  lda #$00    ; assume $00
+  sta escape_parameter
+@next_digit:
+  lda escape_buffer,x  ; get next char
+  inx
+  jsr test_if_digit   ; digit?
+  bcc @done     ; no -> return
+  sbc #$30    ; ascii to #
+  pha         ; save digit
+  ; old value * 10
+  ; 10a = ( 4a + a ) * 2
+  lda escape_parameter
+  asl         
+  asl         ; ( 4a
+  clc
+  adc escape_parameter    ; + a )
+  asl         ; *2 
+  sta escape_parameter    ; = 10a
+  ; add new digit
+  pla         ; resore new digit
+  clc
+  adc escape_parameter
+  sta escape_parameter
+  jmp @next_digit     ; next char        
+@done:
+  rts        
+
+
+        
+; *************************************
+; *
+; * outgoing data
+; *
+; *************************************
+; -------------------------------------
+; given a single char (read from keyboard)
+; work out what data should be sent to the remote host.
+; input:
+; A = keypress
+; output:
+; Y=0 - no data to be sent (i.e. ignore keypress)
+; Y=1 - A contains single byte to send
+; Y=2 - AX points to null terminated string to send
+; -------------------------------------
+
+;Y=0 nothing to send
+;Y=1 A = char to send
+;Y=2 AX=pointer to asciiz string to send
+
+vt100_transform_outbound_char: 
+  tay
+  lda petscii_to_ascii,y   ; PETSCII to ASCII
+  bne :+
+  ldy #0  ; ignore key
+  rts
+:                
+  cmp #$ff
+  beq output_string
+  cmp #$fe
+  beq command_key  ; command key
+  ;default - send (possibly transformed) single char 
+  ldy #1      ;means A contains single byte to send
+@done:
+rts
+
+
+
+; -------------------------------------
+; create an ansi control sequence
+; -------------------------------------
+
+
+output_string:
+  tya         ; restore original key
+
+; --- crsr U ---
+  cmp #$91    ; test crsr U
+  bne @not_U
+  ldax  #ansi_cursor_up
+  ldy   #2
+  rts
+; --- crsr L ---
+@not_U:      
+  cmp #$9d    ; test crsr L
+  bne @not_L
+  ldax #ansi_cursor_left
+  ldy #2
+  rts
+@not_L:
+  cmp #$0d  ;test CR
+  bne @not_CR
+  ldax #crlf
+  ldy #2
+  rts
+
+@not_CR:
+  ldy #0  ;must be some kind of error 
+  rts
+
+
+; -------------------------------------
+; keypress was a command key
+; -------------------------------------
+
+command_key:  
+        tya         ; restore character
+
+; --- crsr R ---
+; ---   ^]   ---
+; both events send $1d
+  cmp #$1d
+  bne @not_crsr_R
+  lda #$04    ; test control Key
+  bit $028d
+  beq @cursor_right   ; not pressed
+  ; control ] is pressed
+  tya         ; send ^]
+  ldy #1
+  rts
+
+; crsr R 
+@cursor_right:
+  ldax #ansi_cursor_right
+  ldy #2
+  rts
+
+; --- crsr D ---
+; ---   ^Q   ---
+; both events send char $11
+@not_crsr_R:
+  cmp #$11    ;^Q / crsr down
+  bne @not_crsr_D
+  lda #$04    ; test control Key
+  bit $028d
+  beq @cursor_down   ; not pressed
+  ; control Q is pressed
+  tya         ; send ^Q
+  ldy #1
+  rts
+        
+  ; crsr down is pressed        
+@cursor_down:
+  ldax #ansi_cursor_down
+  ldy #2
+  rts
+
+; --- HOME key ---
+; ---    ^S    ---
+; both events send char $13
+@not_crsr_D:
+  cmp #$13    ;^S / HOME
+  bne @not_home
+  lda #$04    ; test control Key
+  bit $028d
+  beq @home  ; not pressed
+  ; control S is pressed
+  tya         ; send ^S
+  ldy #1
+  rts
+
+@home: 
+  lda #$09 ; send TAB
+  ldy #1
+  rts
+
+; --- DEL key ---
+; ---    ^T    ---
+; both events send char $14
+@not_home:
+  cmp #$14    ;^T / DEL
+  bne @not_del 
+  lda #$04    ; test control Key
+  bit $028d
+  beq @del   ; not pressed
+  ; control T is pressed
+  tya         ; send ^T
+  ldy #1
+  rts
+  
+  ; send DEL
+@del:
+  lda #$08
+  ldy #1
+  rts
+
+
+; --- unknown C=-Key ---
+@not_del:
+      ldy #0 ;means don't send anything
+      rts
+
+; *************************************
+; *
+; * screen handling
+; *
+; *************************************
+
+; --- these variables become updated ---
+;     on crsr movement.
+;
+; $d1 $d2  start of screen line
+; $d3      crsr column
+; $d6      crsr row
+; $f3 $f4  start of colour line
+; $0286    colour
+
+; --- these variables become updated ---
+;     on crsr switching.
+;
+; $cc    crsr flag, 0 = on
+; $cd    crsr blink counter
+; $ce    char under crsr
+; $cf    crsr blink phase, 0 normal
+; $0287  colour under crsr
+
+
+
+; -------------------------------------
+; switch curser off and restore char.
+; this has to be done before every crsr
+; movement.
+; After movement there has to be a jump
+; to cursor_on.
+; -------------------------------------
+cursor_off:
+  pha         ; save registers
+  tya
+  pha             
+
+  ldy #$01    ; crsr of
+  sty $cc        
+  lda $cf     ; crsr revers?
+  beq :+     ; no -> return
+  dey         ; set normal phase
+  sty $cf        
+  ldy $d3     ; get column
+  lda $ce     ; restore char
+  sta ($d1),y
+  lda $0287   ; restore colour
+  sta ($f3),y
+
+:
+  pla         ; restore registers
+  tay
+  pla
+  rts
+
+; -------------------------------------
+; opposite of cursor_off
+; -------------------------------------
+
+cursor_on:
+  pha
+  tya
+  pha
+          
+  ldy $d3     ; get column
+  lda ($d1),y ; save chr
+  sta $ce 
+  eor #$80    ; reverse char
+  sta ($d1),y        
+  lda ($f3),y ; save colour
+  sta $0287
+  lda $0286   ; set crsr colour
+  sta ($f3),y
+  inc $cf     ; set reverse phase
+  lda #$14    ; set crsr counter..
+  sta $cd     ; ..to max
+  lda #$00    ; cursor on
+  sta $cc 
+  
+  pla
+  tay
+  pla
+  rts
+
+
+
+; -------------------------------------
+; moves the crsr to column Y
+; and line X
+; the crsr ist turned off during 
+; operation
+; destroys all registers
+; -------------------------------------
+cursor_plot:
+  jsr cursor_off
+  
+  stx $d6     ; set row
+  sty $d3     ; set col
+  jsr set_line_vectors
+  ldx temp_ptr_x ; set screen line
+  ldy temp_ptr_x+1
+  stx $d1
+  sty $d2
+  ldx temp_ptr_y ; set color line
+  ldy temp_ptr_y+1
+  stx $f3
+  sty $f4
+  
+  jsr cursor_on        
+  rts
+        
+
+; -------------------------------------
+; Print char in A to screen
+; being aware of the crsr state
+; -------------------------------------
+
+print_to_screen:
+  jsr cursor_off
+  jsr plot_char
+  jsr cursor_on
+  rts
+
+; -------------------------------------
+; print char to screen
+; char = $ff means no output
+; chr in A
+; X and Y unaffected
+; -------------------------------------
+
+plot_char:
+  sta temp_ptr_x ; save char
+  txa         ; save registers
+  pha
+  tya
+  pha
+  lda temp_ptr_x ; restore char
+
+; PETSCII to ScreenCode (SC)
+; --- $c0-$ff ---   - illegal -
+  cmp #$c0
+  bcc :+
+  jmp end_plot_char   ; no output
+; --- $a0-$bf ---   C=(latin-1) chars
+:       
+  cmp #$a0
+  bcc :+
+  sbc #$40    ; SC = PET - $40
+  jmp @check_for_reverse
+; --- $80-$9f ---   - illegal -
+:       
+  cmp #$80
+  bcc :+
+  jmp end_plot_char   ; no output
+; --- $60-$7f ---  kapital letters        
+:
+  cmp #$60
+  bcc :+
+  sbc #$20    ; SC = PET - $20
+  jmp @check_for_reverse
+; --- $40-$5f ---  small letters
+:
+  cmp #$40
+  bcc :+        
+  sbc #$40    ; SC = PET - $40
+  jmp @check_for_reverse
+; --- $20-$3f ---  interpunction
+:
+  cmp #$20
+  bcc :+
+  jmp @check_for_reverse   ; SC = PET
+; --- $00-$1f ---  - illegal -
+:
+  jmp end_plot_char   ; no output
+
+; --- handle reverse mode---
+@check_for_reverse:
+  ldx $c7     ; reverse mode?
+  beq @put_char
+  ora #$80    ; reverse char
+
+; --- put char to screen ---
+@put_char:
+  ldy $d3     ; get crsr col
+  cpy #$28    ;col = 40
+  bcc @no_line_wrap
+              ;the only way we end up trying to write to column 40 should
+              ;be if we skipped the normal line wrap after writing to col 39
+              ;because we are at the end of the scroll region
+              ;that means we should do a scroll up and then write this char at col 0
+  pha
+  jsr scroll_up_scrollregion
+  pla
+  ldy #$00    ; begin of line
+  sty $d3
+@no_line_wrap:  
+  sta ($d1),y ; char to screen
+  lda $0286   ; get colour
+  sta ($f3),y ; set colour
+  
+; --- move on crsr ---
+  
+  ldx $d6     ; get crsr row
+  cpx scroll_region_end     ; end of scroll reg?  
+  beq @dont_scroll_yet     ; we don't want to trigger a scroll of the whole screen unless
+                                    ; we are actually writing a char. we shouldn't scroll just when
+                                    ; writing to the bottom right hand screen (else e.g. the title bar 
+                                    ; in 'nano' gets pushed off the top of the screen.
+                                    ;
+  cpy #$27    ; col = 39?
+  beq move_to_next_line   ; yes -> new line
+@dont_scroll_yet:  
+  iny         ; move on
+  sty $d3
+  
+end_plot_char:
+  pla         ; restore registers
+  tay
+  pla
+  tax
+  rts
+        
+; -------------------------------------
+; subtask of plot_char
+; ends at end_plot_char
+; -------------------------------------
+move_to_next_line:
+  ldx $d6     ; get crsr row
+  cpx scroll_region_end     ; end of scroll reg?
+  beq @scroll_up     ; yes -> branche
+  cpx #$18    ; line 24?
+  beq end_plot_char   ; yes -> crsr stays
+; --- normal wrap ---
+  inx         ; increase line
+  stx $d6
+  ldy #$00    ; begin of line
+  sty $d3
+  jsr set_line_vectors
+  ldx temp_ptr_x ; set screen line
+  ldy temp_ptr_x+1
+  stx $d1
+  sty $d2
+  ldx temp_ptr_y ; set colour line
+  ldy temp_ptr_y+1
+  stx $f3
+  sty $f4        
+  jmp end_plot_char
+; --- scroll up ---        
+@scroll_up:
+  jsr scroll_up_scrollregion
+  ldy #$00    ; begin of line
+  sty $d3
+  jmp end_plot_char
+  
+
+
+scroll_up_scrollregion:
+  ldx scroll_region_start     ; get first line
+@scroll_one_line:
+  ; -- new line: --
+  ; -- temp_ptr_z and temp_ptr_w --
+  jsr set_line_vectors
+  lda temp_ptr_x ; screen line
+  ldy temp_ptr_x+1
+  sta temp_ptr_z
+  sty temp_ptr_z+1
+  lda temp_ptr_y ; colour line
+  ldy temp_ptr_y+1
+  sta temp_ptr_w
+  sty temp_ptr_w+1
+  ; -- old line: --
+  ; -- temp_ptr_x and temp_ptr_y
+  inx             ; old line
+  jsr set_line_vectors
+  ; -- copy chars and colours --
+  ldy #$27        ; col 39
+@scroll_one_char:
+  lda (temp_ptr_x),y ; copy char
+  sta (temp_ptr_z),y
+  lda (temp_ptr_y),y ; copy colour
+  sta (temp_ptr_w),y
+  dey
+  bpl @scroll_one_char
+  cpx scroll_region_end         ; last line?
+  bne @scroll_one_line         ; no -> go on
+  jsr erase_line_by_vector       ; del last line
+  
+  rts
+  
+
+scroll_down_scrollregion:
+  ldx scroll_region_end     ; get last line
+@scroll_one_line:
+  jsr set_line_vectors
+  lda temp_ptr_x ; screen line
+  ldy temp_ptr_x+1
+  sta temp_ptr_z
+  sty temp_ptr_z+1
+  lda temp_ptr_y ; colour line
+  ldy temp_ptr_y+1
+  sta temp_ptr_w
+  sty temp_ptr_w+1
+  ; -- old line: --
+  ; -- temp_ptr_x and temp_ptr_y
+  dex             ; old line
+  jsr set_line_vectors
+  ; -- copy chars ond colours --
+  ldy #$27        ; col 39
+@scroll_one_char:
+  lda (temp_ptr_x),y ; copy char
+  sta (temp_ptr_z),y
+  lda (temp_ptr_y),y ; copy colour
+  sta (temp_ptr_w),y
+  dey
+  bpl @scroll_one_char
+  cpx scroll_region_start         ; first line?
+  bne @scroll_one_line         ; no -> go on
+  jsr erase_line_by_vector       ; del first line
+  
+  rts
+
+
+; -------------------------------------
+; print string to screen
+; string: chars, terminated by $00
+; start lo in x
+; start hi in y
+; affects A
+; takes care of crsr
+; the string must be smaller 
+;   than 255 chrs
+; -------------------------------------
+
+
+plot_string:
+  stx print_ptr   ; store start vector
+  sty print_ptr+1
+  jsr cursor_off        
+  ldy #$00
+@next_char:
+  lda (print_ptr),y
+  beq @end_string      ; $00 terminates string
+  jsr plot_char        
+  iny
+  jmp @next_char
+
+@end_string:
+  jsr cursor_on
+  rts
+
+
+
+; -------------------------------------
+; delete screen line 
+; (Erase Line)
+;
+; line number in X
+;
+; erase_line_by_vector needs line vectors in temp_ptr_x 
+;                         and temp_ptr_y
+;
+; destroys all registers
+; returns $20 (space) in A
+; -------------------------------------
+
+erase_line_by_number:
+  jsr set_line_vectors ; line start in temp_ptr_x
+          ; col  start in temp_ptr_y
+
+; erase chars
+erase_line_by_vector:
+  ldy #$27      ; col 39
+  lda #$20      ; load space
+:
+  sta (temp_ptr_x),y ; clear char
+  dey 
+  bpl :-
+
+; set colour
+  ldy #$27      ; col 39
+  lda #charmode_vanilla      ; load vanilla
+:  
+  sta (temp_ptr_y),y ; clear char
+  dey 
+  bpl :-
+  
+  rts
+        
+
+
+; -------------------------------------
+; delete screen line from crsr to end
+; (Erase End of Line)
+; destroys all registers
+; -------------------------------------
+
+erase_to_end_of_line:
+  jsr cursor_off
+; erase chars
+  ldy $d3       ; get crsr col
+  lda #$20      ; load space
+:
+  sta ($d1),y   ; clear char
+  iny
+  cpy #$28      ; pos 40?
+  bne :-      ; next char
+  sta $ce       ; del char ..
+                ; ..under crsr
+; set colour
+  ldy $d3       ; get crsr col
+  lda #charmode_vanilla      ; load vanilla
+:
+  sta ($f3),y   ; set colour
+  iny
+  cpy #$28      ; pos 40?
+  bne :-      ; next char        
+  jsr cursor_on
+  rts
+
+; -------------------------------------
+; delete screen line up to crsr
+; (Erase Begin of Line)
+; destroys all registers
+; -------------------------------------
+erase_line_to_cursor:
+; erase chars
+  ldy $d3       ; get crsr col
+  lda #$20      ; load space
+:  
+  sta ($d1),y   ; clear char
+  dey
+  bpl :-      ; pos>=0 -> next
+  sta $ce       ; del char ..
+                ; ..under crsr
+; set colour                      
+  ldy $d3       ; get crsr col
+  lda #charmode_vanilla      ; load vanilla
+:
+  sta ($f3),y   ; clear char
+  dey
+  bpl :-        ; pos>=0 -> next        
+  rts
+
+
+; -------------------------------------
+; set line vectors
+;
+; line no in X
+; destroys A and Y
+;
+; sets start of screen line in temp_ptr_x
+; sets start of colour line in temp_ptr_y
+; -------------------------------------
+
+set_line_vectors:
+  lda $ecf0,x   ; get lo byte
+  sta temp_ptr_x
+  sta temp_ptr_y
+  ; determin hi byte
+  ldy #$04      ; hi byte
+  cpx #$07      ; line < 7?
+  bcc @got_line_vector
+  iny           
+  cpx #$0d      ; line < 13?
+  bcc @got_line_vector
+  iny
+  cpx #$14      ; line < 20?
+  bcc @got_line_vector
+  iny           ; line 20-24
+@got_line_vector:
+  sty temp_ptr_x+1
+  tya
+  clc           ; colour RAM =
+  adc #$d4      ; video RAM + d4
+  sta temp_ptr_y+1        
+  rts
+
+
+
+; -------------------------------------
+; init routines
+;
+; -------------------------------------
+
+
+initialise_screen:
+
+ ;--- set background ---
+  lda #text_background_colour
+  sta $d021
+; --- disable Shift C= ---
+  lda #$80
+  sta $0291        
+; --- erase screen ---
+  ldx #$18      ; start at ln 24        
+@erase_one_line:
+  txa
+  pha           ; save X
+  jsr erase_line_by_number      ; erase line
+  pla
+  tax           ; restore X
+  dex           ; previous line
+  bpl @erase_one_line
+
+  lda #charmode_vanilla     ; load vanilla
+  sta $0286
+; --- crsr on ---
+  jsr cursor_off
+  jsr cursor_on
+; --- put crsr ---
+  jsr do_cr
+  jsr do_line_feed
+  jsr do_line_feed
+  
+  rts
+  
+
+
+initialise_variables:
+  lda #$00
+  sta escape_mode
+  sta escape_buffer_length
+  sta scroll_region_start
+  sta font_mode
+  sta saved_font_mode
+  sta saved_reverse_mode
+  sta saved_row
+  sta saved_column
+  
+  lda #$18    ; last line
+  sta scroll_region_end     ; = 24
+  
+  rts
+
+
+reverse_font_table = font_table + $0400
+initialise_font:  
+  sei
+  ldx #ROM_FONT
+  stx temp_ptr_z
+  sty temp_ptr_z+1
+  ldx #font_table
+  stx temp_ptr_y
+  sty temp_ptr_y+1
+  ldx #reverse_font_table
+  stx temp_ptr_x
+  sty temp_ptr_x+1
+  
+; copy font        
+  ldx #$04      ; copy 4 pages = 1KB
+  ldy #$00      
+:
+  lda (temp_ptr_z),y
+  sta (temp_ptr_y),y
+  eor #$ff      ; reverse char
+  sta (temp_ptr_x),y
+  iny
+  bne :-
+  ; switch to next page
+  inc temp_ptr_z+1
+  inc temp_ptr_y+1
+  inc temp_ptr_x+1
+  dex
+  bne :-
+  
+; enable font
+  lda $d018
+  and #$f1
+  ora #$09
+  sta $d018
+  cli
+  rts
+
+
+.rodata
+font_attribute_table:    ; bits mean blink, underline, bold
+.byte charmode_vanilla, charmode_bold                           ; 000 001
+.byte charmode_underline, charmode_underline_bold               ; 010 011
+.byte charmode_blink, charmode_blink_bold                       ; 100 101
+.byte charmode_blink_underline, charmode_blink_underline_bold   ; 110 111
+
+direct_colour_table:
+;ANSI 30   31 32 32   34 35 36 37 
+;    blk   rd gr ye  blu mg cy wh
+.byte  0, $0a, 5, 7, $0e, 4, 3, 1
+
+ansi_cursor_up:     .byte esc, brace, $41, $00 ; esc [ A 
+ansi_cursor_down:   .byte esc, brace, $42, $00 ; esc [ B
+ansi_cursor_right:  .byte esc, brace, $43, $00 ; esc [ C 
+ansi_cursor_left:   .byte esc, brace, $44, $00 ; esc [ D 
+crlf:  .byte $0d,$0a,0
+
+; -------------------------------------
+; table ASCII  to PETSCII 
+;
+; these characters cat be printed
+;
+; pet=$00 means ignore the char
+; pet=$01 means do something complicated
+; -------------------------------------
+ascii_to_petscii:
+  .byte $00   ; $00
+  .byte $00   ; $01
+  .byte $00   ; $02
+  .byte $00   ; $03
+  .byte $00   ; $04
+  .byte $00   ; $05
+  .byte $00   ; $06
+  .byte $01   ; $07 BEL
+  .byte $01   ; $08 BS/DEL
+  .byte $01   ; $09 TAB
+  .byte $01   ; $0a LF
+  .byte $00   ; $0b
+  .byte $00   ; $0c
+  .byte $01   ; $0d CR
+  .byte $00   ; $0e
+  .byte $00   ; $0f
+  .byte $00   ; $10
+  .byte $00   ; $11
+  .byte $00   ; $12
+  .byte $00   ; $13 
+  .byte $00   ; $14
+  .byte $00   ; $15
+  .byte $00   ; $16
+  .byte $00   ; $17
+  .byte $00   ; $18
+  .byte $00   ; $19
+  .byte $00   ; $1a
+  .byte $01   ; $1b ESC
+  .byte $00   ; $1c
+  .byte $00   ; $1d
+  .byte $00   ; $1e
+  .byte $00   ; $1f
+  .byte $20   ; $20  1:1
+  .byte $21   ; $21  1:1
+  .byte $22   ; $22  1:1
+  .byte $23   ; $23  1:1
+  .byte $24   ; $24  1:1
+  .byte $25   ; $25  1:1
+  .byte $26   ; $26  1:1
+  .byte $27   ; $27  1:1
+  .byte $28   ; $28  1:1
+  .byte $29   ; $29  1:1
+  .byte $2a   ; $2a  1:1
+  .byte $2b   ; $2b  1:1
+  .byte $2c   ; $2c  1:1
+  .byte $2d   ; $2d  1:1
+  .byte $2e   ; $2e  1:1
+  .byte $2f   ; $2f  1:1
+  .byte $30   ; $30  1:1
+  .byte $31   ; $31  1:1
+  .byte $32   ; $32  1:1
+  .byte $33   ; $33  1:1
+  .byte $34   ; $34  1:1
+  .byte $35   ; $35  1:1
+  .byte $36   ; $36  1:1
+  .byte $37   ; $37  1:1
+  .byte $38   ; $38  1:1
+  .byte $39   ; $39  1:1
+  .byte $3a   ; $3a  1:1
+  .byte $3b   ; $3b  1:1
+  .byte $3c   ; $3c  1:1
+  .byte $3d   ; $3d  1:1
+  .byte $3e   ; $3e  1:1
+  .byte $3f   ; $3f  1:1
+  .byte $40   ; $40  1:1
+  .byte $61   ; $41 -----
+  .byte $62   ; $42
+  .byte $63   ; $43
+  .byte $64   ; $44 capital
+  .byte $65   ; $45
+  .byte $66   ; $46
+  .byte $67   ; $47
+  .byte $68   ; $48
+  .byte $69   ; $49
+  .byte $6a   ; $4a
+  .byte $6b   ; $4b
+  .byte $6c   ; $4c
+  .byte $6d   ; $4d letters
+  .byte $6e   ; $4e
+  .byte $6f   ; $4f
+  .byte $70   ; $50
+  .byte $71   ; $51
+  .byte $72   ; $52
+  .byte $73   ; $53
+  .byte $74   ; $54
+  .byte $75   ; $55
+  .byte $76   ; $56
+  .byte $77   ; $57
+  .byte $78   ; $58
+  .byte $79   ; $59
+  .byte $7a   ; $5a -----
+  .byte $5b   ; $5b  1:1
+  .byte $5c   ; $5c  1:1
+  .byte $5d   ; $5d  1:1
+  .byte $5e   ; $5e  1:1
+  .byte $5f   ; $5f  1:1
+  .byte $60   ; $60  1:1
+  .byte $41   ; $61 -----
+  .byte $42   ; $62
+  .byte $43   ; $63
+  .byte $44   ; $64 small
+  .byte $45   ; $65
+  .byte $46   ; $66
+  .byte $47   ; $67
+  .byte $48   ; $68
+  .byte $49   ; $69
+  .byte $4a   ; $6a
+  .byte $4b   ; $6b letters
+  .byte $4c   ; $6c
+  .byte $4d   ; $6d
+  .byte $4e   ; $6e
+  .byte $4f   ; $6f
+  .byte $50   ; $70
+  .byte $51   ; $71
+  .byte $52   ; $72
+  .byte $53   ; $73
+  .byte $54   ; $74
+  .byte $55   ; $75
+  .byte $56   ; $76
+  .byte $57   ; $77
+  .byte $58   ; $78
+  .byte $59   ; $79
+  .byte $5a   ; $7a -----
+  .byte $7b   ; $7b  1:1 {
+  .byte $7c   ; $7c  1:1 |
+  .byte $7d   ; $7d  1:1 }
+  .byte $7e   ; $7e  1:1 ~
+  .byte $00   ; $7f
+  .byte $00   ; $80
+  .byte $00   ; $81
+  .byte $00   ; $82
+  .byte $00   ; $83
+  .byte $00   ; $84
+  .byte $00   ; $85
+  .byte $00   ; $86
+  .byte $00   ; $87
+  .byte $00   ; $88
+  .byte $00   ; $89
+  .byte $00   ; $8a
+  .byte $00   ; $8b
+  .byte $00   ; $8c
+  .byte $00   ; $8d
+  .byte $00   ; $8e
+  .byte $00   ; $8f
+  .byte $00   ; $90
+  .byte $00   ; $91
+  .byte $00   ; $92
+  .byte $00   ; $93
+  .byte $00   ; $94
+  .byte $00   ; $95
+  .byte $00   ; $96
+  .byte $00   ; $97
+  .byte $00   ; $98
+  .byte $00   ; $99
+  .byte $00   ; $9a
+  .byte $00   ; $9b
+  .byte $00   ; $9c
+  .byte $00   ; $9d
+  .byte $00   ; $9e
+  .byte $00   ; $9f
+  .byte $20   ; $a0
+  .byte $7f   ; $a1
+  .byte $7f   ; $a2
+  .byte $bf   ; $a3
+  .byte $be   ; $a4
+  .byte $7f   ; $a5
+  .byte $73   ; $a6
+  .byte $b5   ; $a7
+  .byte $53   ; $a8
+  .byte $bb   ; $a9
+  .byte $7f   ; $aa
+  .byte $bc   ; $ab
+  .byte $7f   ; $ac
+  .byte $2d   ; $ad
+  .byte $7f   ; $ae
+  .byte $7f   ; $af
+  .byte $ba   ; $b0
+  .byte $b8   ; $b1
+  .byte $b6   ; $b2
+  .byte $b7   ; $b3
+  .byte $7a   ; $b4
+  .byte $b9   ; $b5
+  .byte $7f   ; $b6
+  .byte $7f   ; $b7
+  .byte $5a   ; $b8
+  .byte $7f   ; $b9
+  .byte $7f   ; $ba
+  .byte $bd   ; $bb
+  .byte $b0   ; $bc
+  .byte $b0   ; $bd
+  .byte $79   ; $be
+  .byte $7f   ; $bf
+  .byte $a5   ; $c0
+  .byte $61   ; $c1
+  .byte $a4   ; $c2
+  .byte $61   ; $c3
+  .byte $a3   ; $c4
+  .byte $a4   ; $c5
+  .byte $7f   ; $c6
+  .byte $63   ; $c7
+  .byte $ad   ; $c8
+  .byte $ab   ; $c9
+  .byte $ac   ; $ca
+  .byte $65   ; $cb
+  .byte $69   ; $cc
+  .byte $69   ; $cd
+  .byte $69   ; $ce
+  .byte $69   ; $cf
+  .byte $64   ; $d0
+  .byte $6e   ; $d1
+  .byte $6f   ; $d2
+  .byte $6f   ; $d3
+  .byte $b1   ; $d4
+  .byte $6f   ; $d5
+  .byte $af   ; $d6
+  .byte $7f   ; $d7
+  .byte $6f   ; $d8
+  .byte $75   ; $d9
+  .byte $75   ; $da
+  .byte $75   ; $db
+  .byte $b3   ; $dc
+  .byte $79   ; $dd
+  .byte $7f   ; $de
+  .byte $b4   ; $df
+  .byte $a2   ; $e0
+  .byte $41   ; $e1
+  .byte $a1   ; $e2
+  .byte $41   ; $e3
+  .byte $a0   ; $e4
+  .byte $a1   ; $e5
+  .byte $7f   ; $e6
+  .byte $a6   ; $e7
+  .byte $aa   ; $e8
+  .byte $a8   ; $e9
+  .byte $a9   ; $ea
+  .byte $a7   ; $eb
+  .byte $49   ; $ec
+  .byte $49   ; $ed
+  .byte $49   ; $ee
+  .byte $49   ; $ef
+  .byte $7f   ; $f0
+  .byte $4e   ; $f1
+  .byte $4f   ; $f2
+  .byte $4f   ; $f3
+  .byte $b1   ; $f4
+  .byte $4f   ; $f5
+  .byte $ae   ; $f6
+  .byte $7f   ; $f7
+  .byte $4f   ; $f8
+  .byte $55   ; $f9
+  .byte $55   ; $fa
+  .byte $55   ; $fb
+  .byte $b2   ; $fc
+  .byte $59   ; $fd
+  .byte $7f   ; $fe
+  .byte $59   ; $ff
+
+; -------------------------------------
+; table PETSCII  to ASCII 
+;
+; these characters can be typed with 
+; the keyboard
+;
+; ascii = $00 means ignore key
+; ascii = $ff menas send string
+; ascii = $fe means do something 
+;             complicated (command key)
+; -------------------------------------
+petscii_to_ascii:
+  .byte $00   ; $00
+  .byte $01   ; $01
+  .byte $02   ; $02
+  .byte $03   ; $03
+  .byte $04   ; $04
+  .byte $05   ; $05
+  .byte $06   ; $06
+  .byte $07   ; $07
+  .byte $08   ; $08 DEL
+  .byte $09   ; $09 TAB
+  .byte $0a   ; $0a
+  .byte $0b   ; $0b
+  .byte $0c   ; $0c
+  .byte $ff   ; $0d CR
+  .byte $0e   ; $0e
+  .byte $0f   ; $0f
+  .byte $10   ; $10
+  .byte $fe   ; $11 ^Q (crsr down)
+  .byte $12   ; $12
+  .byte $fe   ; $13 ^S TAB (HOME)
+  .byte $fe   ; $14 ^T BS  (DEL)
+  .byte $15   ; $15
+  .byte $16   ; $16
+  .byte $17   ; $17
+  .byte $18   ; $18
+  .byte $19   ; $19
+  .byte $1a   ; $1a
+  .byte $1b   ; $1b ESC
+  .byte $1c   ; $1c
+  .byte $fe   ; $1d ^](crsr right)
+  .byte $1e   ; $1e
+  .byte $1f   ; $1f
+  .byte $20   ; $20 SPACE
+  .byte $21   ; $21 !
+  .byte $22   ; $22 "
+  .byte $23   ; $23 #
+  .byte $24   ; $24 $
+  .byte $25   ; $25 %
+  .byte $26   ; $26 &
+  .byte $27   ; $27 '
+  .byte $28   ; $28 (
+  .byte $29   ; $29 )
+  .byte $2a   ; $2a *
+  .byte $2b   ; $2b +
+  .byte $2c   ; $2c ,
+  .byte $2d   ; $2d -
+  .byte $2e   ; $2e .
+  .byte $2f   ; $2f /
+  .byte $30   ; $30 0
+  .byte $31   ; $31 1
+  .byte $32   ; $32 2
+  .byte $33   ; $33 3
+  .byte $34   ; $34 4
+  .byte $35   ; $35 5
+  .byte $36   ; $36 6
+  .byte $37   ; $37 7
+  .byte $38   ; $38 8
+  .byte $39   ; $39 9
+  .byte $3a   ; $3a :
+  .byte $3b   ; $3b ;
+  .byte $3c   ; $3c <
+  .byte $3d   ; $3d =
+  .byte $3e   ; $3e >
+  .byte $3f   ; $3f ?
+  .byte $40   ; $40 @
+  .byte $61   ; $41 a
+  .byte $62   ; $42 b
+  .byte $63   ; $43 c
+  .byte $64   ; $44 d
+  .byte $65   ; $45 e
+  .byte $66   ; $46 f
+  .byte $67   ; $47 g
+  .byte $68   ; $48 h
+  .byte $69   ; $49 i
+  .byte $6a   ; $4a j
+  .byte $6b   ; $4b k
+  .byte $6c   ; $4c l
+  .byte $6d   ; $4d m
+  .byte $6e   ; $4e n
+  .byte $6f   ; $4f o
+  .byte $70   ; $50 p
+  .byte $71   ; $51 q
+  .byte $72   ; $52 r
+  .byte $73   ; $53 s
+  .byte $74   ; $54 t
+  .byte $75   ; $55 u
+  .byte $76   ; $56 v
+  .byte $77   ; $57 w
+  .byte $78   ; $58 x
+  .byte $79   ; $59 y
+  .byte $7a   ; $5a z
+  .byte $5b   ; $5b [
+  .byte $5c   ; $5c \ (Pound)
+  .byte $5d   ; $5d ]
+  .byte $5e   ; $5e ^
+  .byte $1b   ; $5f ESC ( <- )
+  .byte $00   ; $60 
+  .byte $41   ; $61 A
+  .byte $42   ; $62 B
+  .byte $43   ; $63 C
+  .byte $44   ; $64 D
+  .byte $45   ; $65 E
+  .byte $46   ; $66 F
+  .byte $47   ; $67 G
+  .byte $48   ; $68 H
+  .byte $49   ; $69 I
+  .byte $4a   ; $6a J
+  .byte $4b   ; $6b K
+  .byte $4c   ; $6c L
+  .byte $4d   ; $6d M
+  .byte $4e   ; $6e N
+  .byte $4f   ; $6f O
+  .byte $50   ; $70 P
+  .byte $51   ; $71 Q
+  .byte $52   ; $72 R
+  .byte $53   ; $73 S
+  .byte $54   ; $74 T
+  .byte $55   ; $75 U
+  .byte $56   ; $76 V
+  .byte $57   ; $77 W
+  .byte $58   ; $78 X
+  .byte $59   ; $79 Y
+  .byte $5a   ; $7a Z
+  .byte $00   ; $7b
+  .byte $00   ; $7c
+  .byte $00   ; $7d
+  .byte $00   ; $7e
+  .byte $00   ; $7f
+  .byte $00   ; $80
+  .byte $00   ; $81
+  .byte $00   ; $82
+  .byte $00   ; $83
+  .byte $00   ; $84
+  .byte $00   ; $85 (f1)
+  .byte $00   ; $86 (f3)
+  .byte $00   ; $87 (f5)
+  .byte $00   ; $88 (f7)
+  .byte $00   ; $89 (f2)
+  .byte $00   ; $8a (f4)
+  .byte $00   ; $8b (f6)
+  .byte $00   ; $8c (f8)
+  .byte $00   ; $8d (Shift RET)
+  .byte $00   ; $8e
+  .byte $00   ; $8f
+  .byte $00   ; $90
+  .byte $ff   ; $91 (crsr up)
+  .byte $00   ; $92
+  .byte $00   ; $93 (Shift Clr/Home)
+  .byte $7f   ; $94 DEL (Shift Ins/Del)
+  .byte $00   ; $95
+  .byte $00   ; $96
+  .byte $00   ; $97
+  .byte $00   ; $98
+  .byte $00   ; $99
+  .byte $00   ; $9a
+  .byte $00   ; $9b
+  .byte $00   ; $9c
+  .byte $ff   ; $9d (crsr left)
+  .byte $00   ; $9e
+  .byte $00   ; $9f
+  .byte $00   ; $a0 (Shift Space)
+  .byte $00   ; $a1
+  .byte $00   ; $a2
+  .byte $00   ; $a3
+  .byte $00   ; $a4
+  .byte $00   ; $a5
+  .byte $00   ; $a6
+  .byte $00   ; $a7
+  .byte $00   ; $a8
+  .byte $7c   ; $a9 | (Shift Pound)
+  .byte $00   ; $aa
+  .byte $00   ; $ab
+  .byte $fe   ; $ac  C= D
+  .byte $00   ; $ad
+  .byte $fe   ; $ae  C= S
+  .byte $00   ; $af
+  .byte $fe   ; $b0  C= A
+  .byte $00   ; $b1
+  .byte $fe   ; $b2  C= R
+  .byte $00   ; $b3
+  .byte $00   ; $b4
+  .byte $00   ; $b5
+  .byte $fe   ; $b6  C= L
+  .byte $00   ; $b7
+  .byte $00   ; $b8
+  .byte $00   ; $b9
+  .byte $60   ; $ba ` ( Shift @ )
+  .byte $00   ; $bb
+  .byte $fe   ; $bc  C= C
+  .byte $00   ; $bd
+  .byte $00   ; $be
+  .byte $fe   ; $bf  C= B
+  .byte $5f   ; $c0 _ ( Shift * )
+  .byte $41   ; $c1 -----
+  .byte $42   ; $c2
+  .byte $43   ; $c3 capital
+  .byte $44   ; $c4
+  .byte $45   ; $c5 letters
+  .byte $46   ; $c6
+  .byte $47   ; $c7 generate
+  .byte $48   ; $c8 
+  .byte $49   ; $c9 these 
+  .byte $4a   ; $ca
+  .byte $4b   ; $cb codes
+  .byte $4c   ; $cc
+  .byte $4d   ; $cd
+  .byte $4e   ; $ce
+  .byte $4f   ; $cf
+  .byte $50   ; $d0
+  .byte $51   ; $d1
+  .byte $52   ; $d2
+  .byte $53   ; $d3
+  .byte $54   ; $d4
+  .byte $55   ; $d5
+  .byte $56   ; $d6
+  .byte $57   ; $d7
+  .byte $58   ; $d8
+  .byte $59   ; $d9
+  .byte $5a   ; $da -----
+  .byte $7b   ; $db { ( Shift + )
+  .byte $00   ; $dc   ( C= -   )
+  .byte $7d   ; $dd } ( Shift - )
+  .byte $7e   ; $de ~ ( Pi )
+  .byte $00   ; $df
+  .byte $00   ; $e0
+  .byte $00   ; $e1
+  .byte $00   ; $e2
+  .byte $00   ; $e3
+  .byte $00   ; $e4
+  .byte $00   ; $e5
+  .byte $00   ; $e6
+  .byte $00   ; $e7
+  .byte $00   ; $e8
+  .byte $00   ; $e9
+  .byte $00   ; $ea 
+  .byte $00   ; $eb
+  .byte $00   ; $ec
+  .byte $00   ; $ed
+  .byte $00   ; $ee
+  .byte $00   ; $ef
+  .byte $00   ; $f0
+  .byte $00   ; $f1
+  .byte $00   ; $f2
+  .byte $00   ; $f3
+  .byte $00   ; $f4
+  .byte $00   ; $f5
+  .byte $00   ; $f6
+  .byte $00   ; $f7
+  .byte $00   ; $f8
+  .byte $00   ; $f9
+  .byte $00   ; $fa
+  .byte $00   ; $fb
+  .byte $00   ; $fc
+  .byte $00   ; $fd
+  .byte $00   ; $fe
+  .byte $00   ; $ff
+
+ROM_FONT:
+.incbin "../inc/vt100_font.bin"
+
+;-- LICENSE FOR c64_vt100.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Initial Developer of the Original Code is Lars Stollenwerk.
+; 
+; Portions created by the Initial Developer are Copyright (C) 2003
+; Lars Stollenwerk. All Rights Reserved.  
+;
+;Contributor(s): Jonno Downes
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_c64inputs_s.html b/docs/drivers_c64inputs_s.html new file mode 100644 index 0000000..98c909a --- /dev/null +++ b/docs/drivers_c64inputs_s.html @@ -0,0 +1,223 @@ +

ip65 technical reference

File : drivers/c64inputs.s

functions

functiondescription
check_for_abort_key
check whether the RUN/STOP key is being pressed
+inputs: none
+outputs: sec if RUN/STOP pressed, clear otherwise
get_filtered_input
cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+======================================================================
+Input a string and store it in GOTINPUT, terminated with a null byte.
+AX is a pointer to the allowed list of characters, null-terminated.
+set AX to $0000 for no filter on input
+max # of chars in y returns num of chars entered in y.
+======================================================================
+ Main entry
get_key
use C64 Kernel ROM function to read a key
+inputs: none
+outputs: A contains ASCII value of key just pressed
get_key_if_available
not officially documented - where F13E (GETIN) falls through to if device # is 0 (KEYBD)
get_key_ip65
process inbound ip packets while waiting for a keypress

constants

constantsdescriptionvalue
filter_dns"-ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
filter_ip"." +
filter_number"1234567890",0 +
filter_text================================================= +Some example filters +================================================= ",!#'()* " +
filter_url":/%&?+$" +

implementation

.export get_key  
+.export get_filtered_input
+.export filter_text
+.export filter_ip
+.export filter_dns
+.export filter_url
+.export filter_number
+.export check_for_abort_key
+.export get_key_if_available
+.export get_key_ip65
+.importzp copy_src
+
+.import ip65_process
+
+.include "../inc/common.i"
+.code
+
+allowed_ptr=copy_src ;reuse zero page
+
+;use C64 Kernel ROM function to read a key
+;inputs: none
+;outputs: A contains ASCII value of key just pressed
+get_key:
+  jsr get_key_if_available
+  beq get_key
+  rts
+
+;use C64 Kernel ROM function to read a key
+;inputs: none
+;outputs: A contains ASCII value of key just pressed (0 if no key pressed)
+get_key_if_available=$f142 ;not officially documented - where F13E (GETIN) falls through to if device # is 0 (KEYBD)
+
+
+;process inbound ip packets while waiting for a keypress
+get_key_ip65:
+  jsr ip65_process
+  jsr get_key_if_available
+  beq get_key_ip65
+  rts
+
+
+
+
+;check whether the RUN/STOP key is being pressed
+;inputs: none
+;outputs: sec if RUN/STOP pressed, clear otherwise
+check_for_abort_key:
+  lda $cb ;current key pressed
+  cmp #$3F
+  bne @not_abort
+@flush_loop:
+  jsr get_key_if_available
+  bne @flush_loop
+  lda $cb ;current key pressed
+  cmp #$3F
+  beq @flush_loop
+  sec
+  rts
+@not_abort:
+  clc
+  rts
+
+;cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+;======================================================================
+;Input a string and store it in GOTINPUT, terminated with a null byte.
+;AX is a pointer to the allowed list of characters, null-terminated.
+;set AX to $0000 for no filter on input
+;max # of chars in y returns num of chars entered in y.
+;======================================================================
+
+
+; Main entry
+get_filtered_input:
+  sty MAXCHARS
+  stax temp_allowed
+
+  ;Zero characters received.
+  lda #$00
+  sta INPUT_Y
+
+;Wait for a character.
+@input_get:
+  jsr get_key
+  sta LASTCHAR
+
+  cmp #$14               ;Delete
+  beq @delete
+
+  cmp #$0d               ;Return
+  beq @input_done
+
+  ;End reached?
+  lda INPUT_Y
+  cmp MAXCHARS
+  beq @input_get
+
+  ;Check the allowed list of characters.
+  ldax temp_allowed
+  stax allowed_ptr  ;since we are reusing this zero page, it may not stil be the same value since last time!
+
+  ldy #$00
+  lda allowed_ptr+1     ;was the input filter point nul?
+  beq @input_ok
+@check_allowed:
+  lda (allowed_ptr),y           ;Overwritten
+  beq @input_get         ;Reached end of list (0)
+
+  cmp LASTCHAR
+  beq @input_ok           ;Match found
+
+  ;Not end or match, keep checking
+  iny
+  jmp @check_allowed
+
+@input_ok:
+  lda LASTCHAR          ;Get the char back
+  ldy INPUT_Y
+  sta GOTINPUT,y        ;Add it to string
+  jsr $ffd2             ;Print it
+
+  inc INPUT_Y           ;Next character
+
+  ;Not yet.
+  jmp @input_get
+
+@input_done:
+   ldy INPUT_Y
+   beq  @no_input
+   lda #$00
+   sta GOTINPUT,y   ;Zero-terminate
+   clc
+   ldax #GOTINPUT
+   rts
+@no_input:
+   sec
+   rts
+; Delete last character.
+@delete:
+  ;First, check if we're at the beginning.  If so, just exit.
+  lda INPUT_Y
+  bne @delete_ok
+  jmp @input_get
+
+  ;At least one character entered.
+@delete_ok:
+  ;Move pointer back.
+  dec INPUT_Y
+
+  ;Store a zero over top of last character, just in case no other characters are entered.
+  ldy INPUT_Y
+  lda #$00
+  sta GOTINPUT,y
+
+  ;Print the delete char
+  lda #$14
+  jsr $ffd2
+
+  ;Wait for next char
+  jmp @input_get
+
+
+;=================================================
+;Some example filters
+;=================================================
+
+filter_text:
+  .byte ",!#'()* "
+filter_url: 
+.byte ":/%&?+$"
+filter_dns:
+.byte "-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+filter_ip:
+.byte "."
+filter_number: 
+.byte "1234567890",0
+
+;=================================================
+.bss
+temp_allowed: .res 2
+MAXCHARS: .res 1
+LASTCHAR: .res 1
+INPUT_Y: .res 1  
+GOTINPUT: .res 40
+
+
+
+;-- LICENSE FOR c64inputs.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_c64kernal_s.html b/docs/drivers_c64kernal_s.html new file mode 100644 index 0000000..22b9d66 --- /dev/null +++ b/docs/drivers_c64kernal_s.html @@ -0,0 +1,28 @@ +

ip65 technical reference

File : drivers/c64kernal.s

functions

functiondescription
exit_to_basic
 jump to BASIC interpreter loop 

implementation

.export exit_to_basic  
+
+.code
+; jump to BASIC interpreter loop 
+exit_to_basic:
+  jmp $a7ae  
+
+
+
+;-- LICENSE FOR c64kernal.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_c64print_s.html b/docs/drivers_c64print_s.html new file mode 100644 index 0000000..d7802d5 --- /dev/null +++ b/docs/drivers_c64print_s.html @@ -0,0 +1,123 @@ +

ip65 technical reference

File : drivers/c64print.s

functions

functiondescription
beep
currently does nothing (should make a 'beep noise')
+inputs: none
+outputs: none
cls
use C64 Kernel ROM function to clear the screen
+inputs: none
+outputs: none
use C64 Kernel ROM function to print a character to the screen
+inputs: A contains petscii value of character to print
+outputs: none
print a single char in inverse text:
use C64 Kernel ROM function to move to a new line
+inputs: none
+outputs: none

constants

constantsdescriptionvalue
screen_current_col$d3 +
screen_current_row$d6 +

implementation

+.export print_a
+.export print_cr
+.export cls
+.export beep
+.export print_a_inverse
+
+.exportzp screen_current_row
+.exportzp screen_current_col
+
+
+screen_current_row=$d6
+screen_current_col=$d3
+
+
+;use C64 Kernel ROM function to print a character to the screen
+;inputs: A contains petscii value of character to print
+;outputs: none
+print_a = $ffd2
+
+.bss
+beep_timer: .res 1
+
+.code
+
+;use C64 Kernel ROM function to move to a new line
+;inputs: none
+;outputs: none
+print_cr:
+  lda #13
+  jmp print_a
+
+;use C64 Kernel ROM function to clear the screen
+;inputs: none
+;outputs: none
+cls:
+    lda #147  ; 'CLR/HOME'
+    jmp print_a
+
+;currently does nothing (should make a 'beep noise')
+;inputs: none
+;outputs: none
+beep:
+  lda #15
+  sta $d418  ;set volume
+
+  lda #0
+  sta $d405
+  lda #240
+  sta $d406
+  lda #8
+  sta $d403
+
+  ;tone values for voice 1
+  lda #48
+  sta $d400
+  lda #28
+  sta $d401
+
+  ;enable tone register
+  lda #65
+  sta $d404
+
+
+; pause for qtr second
+  lda $dd06   ;
+  sta beep_timer
+  inc beep_timer  ;time counts backwards
+:  
+  lda $dd06   ;
+  cmp beep_timer
+  bne :-
+
+  ;disable tone register
+  lda #65
+  sta $d404
+  lda #0
+  sta $d418  ;set volume
+
+  rts
+
+  
+;print a single char in inverse text:
+print_a_inverse:
+  pha
+  lda #18 ;inverse mode on 
+  jsr print_a
+  pla
+  jsr print_a
+  lda #146 ;inverse mode off
+  jmp print_a
+
+
+
+;-- LICENSE FOR c64print.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_c64timer_s.html b/docs/drivers_c64timer_s.html new file mode 100644 index 0000000..90f93b0 --- /dev/null +++ b/docs/drivers_c64timer_s.html @@ -0,0 +1,75 @@ +

ip65 technical reference

File : drivers/c64timer.s

 timer routines
+
+ the timer should be a 16-bit counter that's incremented by about
+ 1000 units per second. it doesn't have to be particularly accurate.
+

functions

functiondescription
timer_init
 initialize timers
timer_read
 return the current value
timer_seconds
 this should return a single BCD byte (00..59) which is a count of seconds

implementation

; timer routines
+;
+; the timer should be a 16-bit counter that's incremented by about
+; 1000 units per second. it doesn't have to be particularly accurate.
+
+  .include "../inc/common.i"
+
+  .export timer_init
+  .export timer_read
+  .export timer_seconds  ; this should return a single BCD byte (00..59) which is a count of seconds
+  .code
+  
+; initialize timers
+timer_init:
+  lda #$80    ; stop timers
+  sta $dd0e
+  sta $dd0f
+
+  ldax #999    ; timer A to 1000 cycles
+  stax $dd04
+
+  ldax #$ffff    ; timer B to max cycles
+  stax $dd06
+
+  lda #$81    ; timer A in continuous mode
+  sta $dd0e
+
+  lda #$c1    ; timer B to count timer A underflows
+  sta $dd0f
+
+  lda #0
+  sta $dc08
+  sta $dc09
+  rts
+
+timer_seconds:
+  lda $dc09
+  rts
+
+; return the current value
+timer_read:
+  lda $dd07    ; cia counts backwards, return inverted value
+  eor #$ff
+  tax
+  lda $dd06
+  eor #$ff
+  rts
+
+
+
+
+
+;-- LICENSE FOR c64timer.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_cbm_disk_access_s.html b/docs/drivers_cbm_disk_access_s.html new file mode 100644 index 0000000..4db2eab --- /dev/null +++ b/docs/drivers_cbm_disk_access_s.html @@ -0,0 +1,689 @@ +

ip65 technical reference

File : drivers/cbm_disk_access.s

C64 disk access routines
+
+

functions

functiondescription
io_read_catalogue
routine to catalogue disk (filenames only)
+ io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ AX - address of buffer to read catalogue into
+ outputs:
+ on errror, carry flag is set. 
+ otherwise, buffer will be filled with asciiz filenames (and an extra zero at the end of the last filename)
io_read_catalogue_ex
routine to catalogue disk (with filename, filetype, filesize)
+ io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ AX - address of buffer to read catalogue into
+ outputs:
+ on errror, carry flag is set. 
+ otherwise, buffer will be filled with asciiz filenames,followed by 1 byte filetype, followed by 2 byte file length (in 256 byte sectors)
+ there is an extra zero at the end of the last file.
io_read_file
routine to read a file 
+ inputs:
+ io_device_number  - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ io_filename - specifies filename to open
+ AX - address of buffer to read file into (set to $0000 to treat first 2 bytes as load address)
+ outputs:
+ on errror, carry flag is set
+ otherwise, io_filesize will be set to size of file and io_load_address will be set to actual load address used.
+
io_read_file_with_callback
routine to read a file with a callback after each 256 byte sector
+ inputs:
+ io_device_number  - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ io_filename - specifies filename to open
+ io_callback - address of routine to be called after each sector is read
+ AX - address of buffer to read sector into
+ outputs:
+ on errror, carry flag is set
io_read_sector
routine to read a sector (credited to "Graham")
+cribbed from http://codebase64.org/doku.php?id=base:reading_a_sector_from_disk
+inputs:
+ io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ io_sector_no - set to sector number to be read
+ io_track_no - set to track number to be read (only lo byte is used)
+ AX - address of buffer to read sector into
+ outputs:
+ on errror, carry flag is set. otherwise buffer will be filled with 256 bytes
io_write_sector
routine to write a sector 
+cribbed from http://codebase64.org/doku.php?id=base:writing_a_sector_to_disk (credited to "Graham")
+inputs:
+ io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+ io_sector_no - set to sector number to be written
+ io_track_no - set to track number to be written (only lo byte is used)
+ AX - address of buffer to to write to sector
+ outputs:; on errror, carry flag is set. 

variables

variabledescriptionsize (bytes)
io_filename2
io_filesizealthough a file on disk can be >64K, io_filesize is only used when loading into RAM hence file must be <64K 2
io_load_address2
io_sector_no1
io_track_no2

constants

constantsdescriptionvalue
io_callbackjmp_to_callback+1 +
io_device_no0 +
io_error_buffererror_buffer +

implementation

;C64 disk access routines
+;
+
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+.include "../inc/common.i"
+.export  io_device_no
+.export  io_sector_no
+.export  io_track_no
+.export  io_read_sector
+.export io_write_sector
+.export  io_read_catalogue
+.export  io_read_catalogue_ex
+.export io_read_file
+.export io_read_file_with_callback
+.export io_filename
+.export io_filesize
+.export io_load_address
+.export io_callback
+.export io_error_buffer
+
+.importzp copy_src
+.import ip65_error  
+.import output_buffer
+.importzp copy_dest
+
+
+io_error_buffer=error_buffer
+;reuse the copy_src zero page location
+buffer_ptr = copy_src
+
+;######### KERNEL functions
+CHKIN   = $ffc6
+CHKOUT  = $ffc9
+CHRIN = $ffcf
+CHROUT  = $ffd2
+CLRCHN = $ffcc
+CLALL = $FFE7
+CLOSE = $ffc3
+OPEN = $ffc0
+READST = $ffb7
+SETNAM = $ffbd
+SETLFS = $ffba
+
+.segment "SELF_MODIFIED_CODE"
+
+ io_track_no:  .res 2
+ io_sector_no: .res 1
+ io_device_no: .byte 0
+ io_filename:  .res 2
+ io_filesize:  .res 2 ;although a file on disk can be >64K, io_filesize is only used when loading into RAM hence file must be <64K
+ io_load_address:  .res 2
+ error_buffer = output_buffer + 256
+ command_buffer = error_buffer+128
+ sector_buffer_address: .res 2
+ buffer_counter: .res 1
+ extended_catalogue_flag: .res 1
+
+
+ drive_id: .byte 08  ;default to drive 8
+
+jmp_to_callback:
+  jmp $ffff
+io_callback=jmp_to_callback+1
+
+
+write_byte_to_buffer:
+tmp_buffer_ptr=write_byte_to_buffer+1
+  sta $ffff
+  inc tmp_buffer_ptr
+  bne :+
+  inc tmp_buffer_ptr+1
+:  
+  rts
+
+
+.code
+
+;routine to read a file 
+; inputs:
+; io_device_number  - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; io_filename - specifies filename to open
+; AX - address of buffer to read file into (set to $0000 to treat first 2 bytes as load address)
+; outputs:
+; on errror, carry flag is set
+; otherwise, io_filesize will be set to size of file and io_load_address will be set to actual load address used.
+;
+io_read_file:
+  stax io_load_address
+  sta sector_buffer_address
+  stx sector_buffer_address+1 ;this also sets the Z flag
+  bne @sector_buffer_address_set
+  ;if we get here, X was $00 so we need to use first 2 bytes of file as load address
+  ldax #output_buffer
+  stax sector_buffer_address
+
+@sector_buffer_address_set:
+  ldax #read_file_callback
+  stax io_callback
+  lda #0
+  sta io_filesize
+  sta io_filesize+1 
+  ldax sector_buffer_address  
+  jsr io_read_file_with_callback
+  rts
+
+read_file_callback:
+  sty io_filesize             ;only 1 (the last) sector can ever be !=$100 bytes
+  bne @not_full_sector
+  inc io_filesize+1
+  inc sector_buffer_address +1
+@not_full_sector:
+  lda io_load_address+1       ;is the high byte of the address $00?
+  bne @done    
+  ldax output_buffer          ;if we get here we must have used downloaded into the static output buffer, so the 
+                              ;first 2 bytes there are the real load address
+  stax copy_dest             ;now copy the rest of the sector  
+  stax sector_buffer_address
+  stax io_load_address
+  dey
+  dey
+@copy_one_byte:  
+  dey 
+  lda output_buffer+2,y
+  sta (copy_dest),y
+  inc sector_buffer_address
+  bne :+
+  inc sector_buffer_address+1
+:  
+  tya
+  bne @copy_one_byte
+    
+@done:  
+  rts
+
+;routine to read a file with a callback after each 256 byte sector
+; inputs:
+; io_device_number  - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; io_filename - specifies filename to open
+; io_callback - address of routine to be called after each sector is read
+; AX - address of buffer to read sector into
+; outputs:
+; on errror, carry flag is set
+
+io_read_file_with_callback:
+
+  stax sector_buffer_address
+  jsr CLALL
+  jsr parse_filename
+  
+  jsr SETNAM
+
+  jsr set_drive_id
+  lda #$02      ; file number 2
+  ldx drive_id
+
+  ldy #02       ; secondary address 2
+  jsr SETLFS
+  jsr OPEN
+
+  bcs @device_error    ; if carry set, the device could not be addressed
+
+  ;we should now check for file access errors
+  jsr open_error_channel
+@no_error_opening_error_channel:  
+  jsr check_error_channel
+  lda #$30
+  cmp error_buffer
+  
+  beq @was_not_an_error  
+@readerror:
+  lda #KPR_ERROR_FILE_ACCESS_FAILURE
+  sta ip65_error
+  sec  
+  rts
+ @was_not_an_error:
+
+@get_next_sector:
+  ldx #$02  ;file number 2
+  jsr CHKIN ;file 2 now used as input
+  
+  ldax  sector_buffer_address
+  stax  buffer_ptr
+  lda #$00
+  sta buffer_counter
+@get_next_byte:
+  jsr READST
+  bne @eof
+  jsr CHRIN
+  ldy buffer_counter
+  sta (buffer_ptr),y
+  inc buffer_counter
+  bne @get_next_byte
+  ldy #$00;= 256 bytes
+
+  jsr jmp_to_callback
+  jmp @get_next_sector
+  
+@eof:
+  and #$40      ; end of file?
+  beq @readerror
+
+  ;we have part loaded a sector
+  ldy buffer_counter  
+  beq @empty_sector
+  jsr jmp_to_callback
+@empty_sector:  
+
+@close:
+  jmp close_filenumber_2
+@device_error:
+  lda #KPR_ERROR_DEVICE_FAILURE
+  sta ip65_error
+  ldx #$00
+  jsr CHKIN
+  sec
+  rts
+  
+
+
+;io_filename is null-terminated. 
+;this routines sets up up A,X,Y as needed by kernal routines i.e. XY=pointer to name, A = length of name
+parse_filename:
+  ldax  io_filename
+  stax buffer_ptr
+  ldy #$ff
+@next_byte:
+  iny
+  lda  (buffer_ptr),y
+  bne @next_byte
+  tya
+  ldx buffer_ptr
+  ldy buffer_ptr+1
+  rts
+
+;routine to catalogue disk (with filename, filetype, filesize)
+; io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; AX - address of buffer to read catalogue into
+; outputs:
+; on errror, carry flag is set. 
+; otherwise, buffer will be filled with asciiz filenames,followed by 1 byte filetype, followed by 2 byte file length (in 256 byte sectors)
+; there is an extra zero at the end of the last file.
+io_read_catalogue_ex:
+  stax  tmp_buffer_ptr  
+  lda #1
+  bne extended_catalogue_flag_set
+
+;routine to catalogue disk (filenames only)
+; io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; AX - address of buffer to read catalogue into
+; outputs:
+; on errror, carry flag is set. 
+; otherwise, buffer will be filled with asciiz filenames (and an extra zero at the end of the last filename)
+io_read_catalogue:
+  stax  tmp_buffer_ptr  
+  lda #0
+extended_catalogue_flag_set:
+  sta extended_catalogue_flag
+  ;get the BAM
+  lda #$12
+  sta io_track_no
+  lda #00
+  sta io_sector_no
+  
+  ldax #output_buffer
+  jsr io_read_sector
+  bcs @end_catalogue
+
+@get_next_catalogue_sector:
+
+  clc
+  lda output_buffer 
+  beq @end_catalogue
+  sta io_track_no
+  lda output_buffer+1
+  sta io_sector_no
+  ldax #output_buffer
+  jsr io_read_sector  
+  bcs @end_catalogue  
+  ldy #0
+
+
+@read_one_file:
+  tya
+  pha
+  
+  lda output_buffer+2,y ;file type
+  and #$7f
+  beq @skip_to_next_file
+  
+@get_next_char:
+  lda output_buffer+5,y ;file name
+  beq @end_of_filename
+  cmp #$a0
+  beq @end_of_filename
+  jsr write_byte_to_buffer
+  iny
+  jmp @get_next_char
+@end_of_filename:  
+  lda #0
+  jsr write_byte_to_buffer
+  pla
+  pha
+  
+  tay ;get Y back to start of this file entry
+  
+  lda extended_catalogue_flag ;do we need to include the 'extended' data?
+  beq @skip_to_next_file
+  lda output_buffer+2,y ;file type
+  jsr write_byte_to_buffer  
+  lda output_buffer+30,y ;lo byte of file length in sectors
+  jsr write_byte_to_buffer
+  lda output_buffer+31,y ;hi byte of file length in sectors
+  jsr write_byte_to_buffer
+@skip_to_next_file:
+  pla  
+  clc
+  adc #$20
+  tay
+  bne @read_one_file
+  jmp @get_next_catalogue_sector
+@end_catalogue:  
+  lda #0
+  jsr write_byte_to_buffer
+  jsr write_byte_to_buffer
+  rts
+
+set_drive_id:
+  lda io_device_no
+  beq @drive_id_set
+  clc
+  adc #07   ;so 01->08, 02->09 etc
+  sta drive_id
+@drive_id_set:
+  rts
+
+;routine to write a sector 
+;cribbed from http://codebase64.org/doku.php?id=base:writing_a_sector_to_disk (credited to "Graham")
+;inputs:
+; io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; io_sector_no - set to sector number to be written
+; io_track_no - set to track number to be written (only lo byte is used)
+; AX - address of buffer to to write to sector
+; outputs:; on errror, carry flag is set. 
+io_write_sector:
+  stax sector_buffer_address
+  jsr set_drive_id
+  jsr CLALL
+  lda #$32 ;"2"
+  jsr make_user_command
+  lda #1
+  ldx #cname
+  jsr SETNAM
+  lda #02
+  ldx drive_id
+  ldy #02
+  jsr SETLFS
+  jsr OPEN
+  bcs @error
+  
+  lda #7
+  ldx #bpname
+  jsr SETNAM
+  lda #15
+  ldx drive_id
+  ldy #15
+  jsr SETLFS
+  jsr OPEN
+  bcs @error
+
+  jsr check_error_channel
+  lda #$30
+  cmp error_buffer
+  bne @error  
+
+  ldx #$02      ; filenumber 2
+  jsr CHKOUT    ; file 2 now used as output
+
+  ldax sector_buffer_address
+  stax buffer_ptr
+  ldy #0
+:
+  lda (buffer_ptr),y  ;get next byte in sector
+  jsr CHROUT          ;write it out
+  iny
+  bne :-
+
+  ldx #$0F      ; filenumber 15
+  jsr CHKOUT    ; file 15 now used as output
+
+  ldy #$00
+:
+  lda command_buffer,y
+  beq :+
+  jsr CHROUT   ;write byte to command channel
+  iny
+  bne :-
+:  
+
+  lda #$0d    ; carriage return, required to start command
+  jsr CHROUT  ;write it out
+  jsr check_error_channel
+  lda #$30
+  cmp error_buffer
+  bne @error  
+
+@close:
+  jsr close_filenumbers_2_and_15
+  jsr CLRCHN
+  clc
+  rts
+  
+@error:
+  lda #KPR_ERROR_DEVICE_FAILURE
+  sta ip65_error
+  jsr @close
+  sec
+  rts
+
+
+close_filenumbers_2_and_15:
+  lda #15      ; filenumber 15
+  jsr CLOSE
+close_filenumber_2:  
+  lda #$02      ; filenumber 2
+  jsr CLOSE
+  ldx #$00      ; filenumber 0 = keyboard
+  jsr CHKIN ;(keyboard now input device again)
+  clc
+  rts
+  
+;routine to read a sector (credited to "Graham")
+;cribbed from http://codebase64.org/doku.php?id=base:reading_a_sector_from_disk
+;inputs:
+; io_device_number set to specify drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9))
+; io_sector_no - set to sector number to be read
+; io_track_no - set to track number to be read (only lo byte is used)
+; AX - address of buffer to read sector into
+; outputs:
+; on errror, carry flag is set. otherwise buffer will be filled with 256 bytes
+
+io_read_sector:
+
+  stax sector_buffer_address
+  jsr set_drive_id
+  jsr CLALL
+  lda #$31  ;"1"
+  jsr make_user_command
+  lda #1
+  ldx #cname
+  jsr SETNAM
+  lda #02
+  ldx drive_id
+  ldy #02
+  jsr SETLFS
+  jsr OPEN
+  bcs @error
+  ldx #command_buffer
+  lda #12
+  jsr SETNAM
+  lda #15
+  ldx $BA ;use whatever was last device #
+  ldy #15
+  jsr SETLFS
+  jsr OPEN
+  bcs @error  
+  
+  jsr check_error_channel
+  lda #$30
+  cmp error_buffer
+  bne @error  
+  
+  ldx #$02      ; filenumber 2
+  jsr CHKIN ;(file 2 now used as input)
+
+  lda sector_buffer_address
+  sta buffer_ptr
+  lda sector_buffer_address+1
+  sta buffer_ptr+1
+  ldy #$00
+@loop:
+  jsr CHRIN ;(get a byte from file)
+  sta (buffer_ptr),Y   ; write byte to memory
+  iny
+  bne @loop     ; next byte, end when 256 bytes are read
+@close:
+  jmp close_filenumbers_2_and_15
+@error:
+  lda #KPR_ERROR_DEVICE_FAILURE
+  sta ip65_error
+  jsr @close
+  sec
+  rts
+
+open_error_channel:
+  lda #$00    ; no filename
+  tax
+  tay
+  jsr SETNAM
+  lda #$0f    ;file number 15
+  ldx drive_id
+  ldy #$0f    ; secondary address 15 (error channel)
+  jsr SETLFS
+  jsr OPEN
+  
+  rts
+
+
+check_error_channel:      
+  LDX #$0F      ; filenumber 15
+  JSR CHKIN ;(file 15 now used as input)
+  LDY #$00
+@loop:
+  JSR READST ;(read status byte)  
+  BNE @eof      ; either EOF or read error
+  JSR CHRIN ;(get a byte from file)
+  sta error_buffer,y
+  iny
+  
+  JMP @loop     ; next byte
+
+@eof:
+  lda #0
+  sta error_buffer,y
+  LDX #$00      ; filenumber 0 = keyboard
+  JSR CHKIN ;(keyboard now input device again)
+  rts
+
+make_user_command:
+;fill command buffer with "U " command, where "x" is passed in via A
+;i.e. A=1 makes command to read in track & sector 
+;A=2 makes command to write track & sector 
+;returns length of command in Y
+
+  pha
+  ldy #0
+  lda #85 ;"U"
+  sta command_buffer,y
+  iny
+  pla 
+  sta command_buffer,y
+  iny
+  lda #$20 ;" "
+  sta command_buffer,y
+  iny
+  lda #$32 ;"2" - file number
+  sta command_buffer,y
+  iny
+  lda #$20 ;" "
+  sta command_buffer,y
+  iny
+  lda #$30 ;"0" - drive number
+  sta command_buffer,y
+  iny
+  lda #$20 ;" "
+  sta command_buffer,y
+  iny
+  lda io_track_no
+  jsr byte_to_ascii
+  pha
+  txa
+  sta command_buffer,y
+  pla
+  iny
+  sta command_buffer,y
+  iny
+  lda #$20 ;" "
+  sta command_buffer,y
+  iny
+  lda io_sector_no
+  jsr byte_to_ascii
+  pha
+  txa
+  sta command_buffer,y
+  pla
+  iny
+  sta command_buffer,y
+  iny
+  
+  lda #0
+  sta command_buffer,y  ;make it ASCIIZ so we can print it
+  
+  rts
+
+byte_to_ascii:
+  cmp #30
+  bmi @not_30
+  ldx #$33
+  clc
+  adc #18
+  rts
+@not_30:  
+  cmp #20
+  bmi @not_20
+  ldx #$32
+  clc
+  adc #28
+  rts
+@not_20:
+  cmp #10  
+  bmi @not_10
+  ldx #$31
+  clc
+  adc #38
+  rts
+@not_10:
+  ldx #$30
+  clc
+  adc #48
+  rts
+
+.rodata
+cname: .byte '#'  
+bpname: .byte "B-P 2 0"
+
+
+;-- LICENSE FOR c64_disk_access.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_cs8900a_s.html b/docs/drivers_cs8900a_s.html new file mode 100644 index 0000000..d6730f7 --- /dev/null +++ b/docs/drivers_cs8900a_s.html @@ -0,0 +1,312 @@ +

ip65 technical reference

File : drivers/cs8900a.s

 Ethernet driver for CS8900A chip (as used in RR-NET and Uthernet adapters)
+
+ Based on Doc Bacardi's tftp source
+

functions

functiondescription
eth_init
initialize the ethernet adaptor
+inputs: none
+outputs: carry flag is set if there was an error, clear otherwise
eth_rx
receive a packet
+inputs: none
+outputs:
+ if there was an error receiving the packet (or no packet was ready) then carry flag is set
+ if packet was received correctly then carry flag is clear, 
+ eth_inp contains the received packet, 
+ and eth_inp_len contains the length of the packet
eth_tx
 send a packet
+inputs:
+ eth_outp: packet to send
+ eth_outp_len: length of packet to send
+outputs:
+ if there was an error sending the packet then carry flag is set
+ otherwise carry flag is cleared

implementation

; Ethernet driver for CS8900A chip (as used in RR-NET and Uthernet adapters)
+;
+; Based on Doc Bacardi's tftp source
+
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+.include "../inc/common.i"
+.include "cs8900a.i"
+
+  .export eth_init
+  .export eth_rx
+  .export eth_tx
+
+  .import eth_inp
+  .import eth_inp_len
+  .import eth_outp
+  .import eth_outp_len
+
+  .importzp eth_dest
+  .importzp eth_src
+  .importzp eth_type
+  .importzp eth_data
+
+  .import cs_init
+  .import cs_packet_page
+  .import cs_packet_data
+  .import cs_rxtx_data
+  .import cs_tx_cmd
+  .import cs_tx_len
+
+  .import cfg_mac
+
+  .import ip65_error
+
+  .macro write_page page, value
+  lda #page/2
+  ldx #value
+  jsr cs_write_page
+  .endmacro
+
+
+  .segment "IP65ZP" : zeropage
+
+eth_packet:  .res 2
+
+
+  
+  .code
+
+;initialize the ethernet adaptor
+;inputs: none
+;outputs: carry flag is set if there was an error, clear otherwise
+eth_init:
+  jsr cs_init
+
+  lda #0      ; check magic signature
+  jsr cs_read_page
+  cpx #$0e
+  bne @notfound
+  cpy #$63
+  bne @notfound
+
+  lda #1
+  jsr cs_read_page
+  cpx #0
+  bne @notfound
+  ; y contains chip rev
+
+  write_page pp_self_ctl, $0055  ; $0114, reset chip
+
+  write_page pp_rx_ctl, $0d05  ; $0104, accept individual and broadcast packets
+
+  lda #pp_ia/2      ; $0158, write mac address
+  ldx cfg_mac
+  ldy cfg_mac + 1
+  jsr cs_write_page
+
+  lda #pp_ia/2 + 1
+  ldx cfg_mac + 2
+  ldy cfg_mac + 3
+  jsr cs_write_page
+
+  lda #pp_ia/2 + 2
+  ldx cfg_mac + 4
+  ldy cfg_mac + 5
+  jsr cs_write_page
+
+  write_page pp_line_ctl, $00d3  ; $0112, enable rx and tx
+
+  clc
+  rts
+
+@notfound:
+  sec
+  rts
+
+
+;receive a packet
+;inputs: none
+;outputs:
+; if there was an error receiving the packet (or no packet was ready) then carry flag is set
+; if packet was received correctly then carry flag is clear, 
+; eth_inp contains the received packet, 
+; and eth_inp_len contains the length of the packet
+eth_rx:
+  lda #$24      ; check rx status
+  sta cs_packet_page
+  lda #$01
+  sta cs_packet_page + 1
+
+  lda cs_packet_data + 1
+  and #$0d
+  bne :+
+
+  sec        ; no packet ready
+  rts
+
+:  lda cs_rxtx_data + 1    ; ignore status
+  lda cs_rxtx_data
+
+  lda cs_rxtx_data + 1    ; read packet length
+  sta eth_inp_len + 1
+  tax        ; save
+  lda cs_rxtx_data
+  sta eth_inp_len
+
+  lda #eth_inp
+  sta eth_packet + 1
+
+  ldy #0
+  cpx #0        ; < 256 bytes left?
+  beq @tail
+
+@get256:
+  lda cs_rxtx_data
+  sta (eth_packet),y
+  iny
+  lda cs_rxtx_data + 1
+  sta (eth_packet),y
+  iny
+  bne @get256
+  inc eth_packet + 1
+  dex
+  bne @get256
+
+@tail:
+  lda eth_inp_len      ; bytes left / 2, round up
+  lsr
+  adc #0
+  beq @done
+  tax
+
+@get:
+  lda cs_rxtx_data
+  sta (eth_packet),y
+  iny
+  lda cs_rxtx_data + 1
+  sta (eth_packet),y
+  iny
+  dex
+  bne @get
+
+@done:
+  clc
+  rts
+
+
+; send a packet
+;inputs:
+; eth_outp: packet to send
+; eth_outp_len: length of packet to send
+;outputs:
+; if there was an error sending the packet then carry flag is set
+; otherwise carry flag is cleared
+eth_tx:
+  
+  lda #$c9      ; ask for buffer space
+  sta cs_tx_cmd
+  lda #0
+  sta cs_tx_cmd + 1
+
+  lda eth_outp_len    ; set length
+  sta cs_tx_len
+  lda eth_outp_len + 1
+  sta cs_tx_len + 1
+  cmp #6
+  bmi :+
+  lda #KPR_ERROR_INPUT_TOO_LARGE
+  sta ip65_error
+  sec        ; oversized packet
+  rts
+
+:  lda #pp_bus_status
+  sta cs_packet_page + 1
+
+@waitspace:
+  lda cs_packet_data + 1    ; wait for space
+  ldx cs_packet_data
+  lsr
+  bcs @gotspace
+  jsr @done      ; polling too fast doesn't work, delay added by David Schmidt
+  jmp @waitspace
+@gotspace:
+  ldax #eth_outp      ; send packet
+  stax eth_packet
+
+  ldy #0
+  ldx eth_outp_len + 1
+  beq @tail
+
+@send256:
+  lda (eth_packet),y
+  sta cs_rxtx_data
+  iny
+  lda (eth_packet),y
+  sta cs_rxtx_data + 1
+  iny
+  bne @send256
+  inc eth_packet + 1
+  dex
+  bne @send256
+
+@tail:
+  ldx eth_outp_len
+  beq @done
+
+@send:
+  lda (eth_packet),y
+  sta cs_rxtx_data
+  dex
+  beq @done
+  iny
+  lda (eth_packet),y
+  sta cs_rxtx_data + 1
+  iny
+  dex
+  bne @send
+
+@done:          ; also used by timeout code above
+  clc
+  rts
+
+
+; read X/Y from page A * 2
+cs_read_page:
+  asl
+  sta cs_packet_page
+  lda #0
+  rol
+  sta cs_packet_page + 1
+  ldx cs_packet_data
+  ldy cs_packet_data + 1
+  rts
+
+; write X/Y to page A * 2
+cs_write_page:
+  asl
+  sta cs_packet_page
+  lda #0
+  rol
+  sta cs_packet_page + 1
+  stx cs_packet_data
+  sty cs_packet_data + 1
+  rts
+
+
+
+;-- LICENSE FOR cs8900a.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_lan91c96_s.html b/docs/drivers_lan91c96_s.html new file mode 100644 index 0000000..4f10567 --- /dev/null +++ b/docs/drivers_lan91c96_s.html @@ -0,0 +1,483 @@ +

ip65 technical reference

File : drivers/lan91c96.s

 Ethernet driver for SMC LAN91C96 chip 
+
+

functions

functiondescription
eth_init
initialize the ethernet adaptor
+inputs: none
+outputs: carry flag is set if there was an error, clear otherwise
eth_rx
receive a packet
+inputs: none
+outputs:
+ if there was an error receiving the packet (or no packet was ready) then carry flag is set
+ if packet was received correctly then carry flag is clear, 
+ eth_inp contains the received packet, 
+ and eth_inp_len contains the length of the packet
eth_tx
 send a packet
+inputs:
+ eth_outp: packet to send
+ eth_outp_len: length of packet to send
+outputs:
+ if there was an error sending the packet then carry flag is set
+ otherwise carry flag is cleared

constants

constantsdescriptionvalue
eth_driver_io_basefixlan01+1 +
eth_driver_name"LANceGS (91C96)" +

implementation

                                                                     
+                                                                     
+                                                                     
+                                             
+; Ethernet driver for SMC LAN91C96 chip 
+;
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+.include "../inc/common.i"
+
+  .export eth_init
+  .export eth_rx
+  .export eth_tx
+  .export eth_driver_name
+  .export eth_driver_io_base
+  .import eth_inp
+  .import eth_inp_len
+  .import eth_outp
+  .import eth_outp_len
+
+  .import cfg_mac
+
+; LANceGS hardware addresses
+ethbsr    := $c00E  ; Bank select register             R/W (2B)
+
+; Register bank 0
+ethtcr    := $c000  ; Transmission control register    R/W (2B)
+ethephsr  := $c002  ; EPH status register              R/O (2B)
+ethrcr    := $c004  ; Receive control register         R/W (2B)
+ethecr    := $c006  ; Counter register                 R/O (2B)
+ethmir    := $c008  ; Memory information register      R/O (2B)
+ethmcr    := $c00A  ; Memory Config. reg.    +0 R/W +1 R/O (2B)
+
+; Register bank 1
+ethcr    := $c000  ; Configuration register           R/W (2B)
+ethbar    := $c002  ; Base address register            R/W (2B)
+ethiar    := $c004  ; Individual address register      R/W (6B)
+ethgpr    := $c00A  ; General address register         R/W (2B)
+ethctr    := $c00C  ; Control register                 R/W (2B)
+
+; Register bank 2
+ethmmucr  := $c000  ; MMU command register             W/O (1B)
+ethautotx  := $c001  ; AUTO TX start register           R/W (1B)
+ethpnr    := $c002  ; Packet number register           R/W (1B)
+etharr    := $c003  ; Allocation result register       R/O (1B)
+ethfifo    := $c004  ; FIFO ports register              R/O (2B)
+ethptr    := $c006  ; Pointer register                 R/W (2B)
+ethdata    := $c008  ; Data register                    R/W (4B)
+ethist    := $c00C  ; Interrupt status register        R/O (1B)
+ethack    := $c00C  ; Interrupt acknowledge register   W/O (1B)
+ethmsk    := $c00D  ; Interrupt mask register          R/W (1B)
+
+; Register bank 3
+ethmt    := $c000  ; Multicast table                  R/W (8B)
+ethmgmt    := $c008  ; Management interface             R/W (2B)
+ethrev    := $c00A  ; Revision register                R/W (2B)
+ethercv    := $c00C  ; Early RCV register               R/W (2B)
+
+  .segment "IP65ZP" : zeropage
+
+eth_packet:  .res 2
+
+  .data
+
+;initialize the ethernet adaptor
+;inputs: none
+;outputs: carry flag is set if there was an error, clear otherwise
+eth_init:
+  jsr lan_self_modify
+  lda #$01
+fixlan00:
+  sta ethbsr    ; Select register bank 1
+fixlan01:
+  lda ethcr    ; Read first four bytes - $31, $20, $67, $18
+  cmp #$31
+  bne lanerror
+fixlan03:
+  lda ethbar
+  cmp #$67
+  bne lanerror
+fixlan04:
+  lda ethbar+1
+  cmp #$18
+  bne lanerror
+  ; we have the magic signature
+
+  ; Reset ETH card
+  lda #$00    ; Bank 0
+fixlan05:
+  sta ethbsr
+  lda #%10000000    ; Software reset
+fixlan06:
+  sta ethrcr+1
+
+  ldy #$00
+fixlan07:
+  sty ethrcr
+fixlan08:
+  sty ethrcr+1
+
+  ; Delay
+:  cmp ($FF,x)    ; 6 cycles
+  cmp ($FF,x)    ; 6 cycles
+  iny      ; 2 cycles
+  bne :-      ; 3 cycles
+        ; 17 * 256 = 4352 -> 4,4 ms
+
+  ; Enable transmit and receive
+  lda #%10000001    ; Enable transmit TXENA, PAD_EN
+  ldx #%00000011    ; Enable receive, strip CRC ???
+fixlan09:
+  sta ethtcr
+fixlan10:
+  stx ethrcr+1
+
+  lda #$01    ; Bank 1
+fixlan11:
+  sta ethbsr
+  
+fixlan12:
+  lda ethcr+1
+  ora #%00010000    ; No wait (IOCHRDY)
+fixlan13:
+  sta ethcr+1
+
+  lda #%00001001    ; Auto release
+fixlan14:
+  sta ethctr+1
+  
+  ; Set MAC address
+  lda cfg_mac
+  ldx cfg_mac + 1
+fixlan15:
+  sta ethiar
+fixlan16:
+  stx ethiar + 1
+  lda cfg_mac + 2
+  ldx cfg_mac + 3
+fixlan17:
+  sta ethiar + 2
+fixlan18:
+  stx ethiar + 3
+  lda cfg_mac + 4
+  ldx cfg_mac + 5
+fixlan19:
+  sta ethiar + 4
+fixlan20:
+  stx ethiar + 5
+
+  ; Set interrupt mask
+  lda #$02    ; Bank 2
+fixlan21:
+  sta ethbsr
+  
+  lda #%00000000    ; No interrupts
+fixlan22:
+  sta ethmsk
+  clc
+  rts
+
+lanerror:
+  sec
+  rts
+  
+
+;receive a packet
+;inputs: none
+;outputs:
+; if there was an error receiving the packet (or no packet was ready) then carry flag is set
+; if packet was received correctly then carry flag is clear, 
+; eth_inp contains the received packet, 
+; and eth_inp_len contains the length of the packet
+eth_rx:
+fixlan38:
+  lda ethist
+  and #%00000001  ; Check receive interrupt
+  bne :+
+  sec    ; No packet available
+  rts
+  
+:  lda #$00
+  ldx #%11100000  ; Receive, Auto Increment, Read
+fixlan39:
+  sta ethptr
+fixlan40:
+  stx ethptr + 1
+
+  ; Last word contains 'last data byte' and $60 or 'fill byte' and $40 
+fixlan41:
+  lda ethdata  ; Status word
+fixlan42:
+  lda ethdata  ; Only need high byte
+
+  ; Move ODDFRM bit into carry:
+  ; - Even packet length -> carry clear -> subtract 6 bytes
+  ; - Odd packet length  -> carry set   -> subtract 5 bytes
+  lsr
+  lsr
+  lsr
+  lsr
+  lsr
+
+  ; The packet contains 3 extra words
+fixlan43:
+  lda ethdata    ; Total number of bytes
+  sbc #$05    ; Actually 5 or 6 depending on carry
+  sta eth_inp_len
+fixlan44:
+  lda ethdata
+  sbc #$00
+  sta eth_inp_len+1
+
+  ; Read bytes into buffer
+  lda #eth_inp
+  sta eth_packet
+  stx eth_packet+1
+    ldx eth_inp_len+1
+    ldy #$00
+lanread:
+fixlan46:
+  lda ethdata
+  sta (eth_packet),y
+  iny
+  bne :+
+  inc eth_packet+1
+:  cpy eth_inp_len
+  bne lanread
+  dex
+  bpl lanread
+  
+  ; Remove and release RX packet from the FIFO
+  lda #%10000000
+fixlan47:
+  sta ethmmucr
+
+  clc
+  rts
+  
+
+; send a packet
+;inputs:
+; eth_outp: packet to send
+; eth_outp_len: length of packet to send
+;outputs:
+; if there was an error sending the packet then carry flag is set
+; otherwise carry flag is cleared
+eth_tx:
+  lda eth_outp_len + 1  ;
+  ora #%00100000
+fixlan23:
+  sta ethmmucr  ; Allocate memory for transmission
+fixlan24:
+  lda ethist
+  and #%00001000  ; Allocation interrupt
+  bne :+
+  sec
+  rts    ; Not able to allocate; bail
+
+:  lda #%00001000
+fixlan25:
+  sta ethack  ; Acknowledge interrupt
+
+fixlan26:
+  lda etharr
+fixlan27:
+  sta ethpnr  ; Set packet number
+
+  lda #$00
+  ldx #%01000000  ; Auto increment
+fixlan28:
+  sta ethptr
+fixlan29:
+  stx ethptr + 1
+
+  lda #$00  ; Status written by CSMA
+fixlan30:
+  sta ethdata
+fixlan31:
+  sta ethdata
+
+  lda eth_outp_len
+  eor #$01
+  lsr
+  lda eth_outp_len
+  adc #$05  ; Actually will be 5 or 6 depending on carry
+fixlan32:
+  sta ethdata
+  lda eth_outp_len + 1
+  adc #$00
+fixlan33:
+  sta ethdata
+
+  lda #eth_outp
+  sta eth_packet
+  stx eth_packet + 1
+  ldx eth_outp_len + 1
+  ldy #$00
+lanwrite:
+  lda (eth_packet),y
+fixlan34:
+  sta ethdata
+  iny
+  bne :+
+  inc eth_packet + 1
+:  cpy eth_outp_len
+  bne lanwrite
+  dex
+  bpl lanwrite
+
+  lda eth_outp_len  ; Odd packet length?
+  lsr
+  bcc :+
+
+  lda #%001000000  ; Yes, Odd
+  bne fixlan36  ; Always
+
+:  lda #$00  ; No
+fixlan35:
+  sta ethdata  ; Fill byte
+fixlan36:
+  sta ethdata  ; Control byte
+  lda #%11000000  ; Enqueue packet - transmit
+fixlan37:
+  sta ethmmucr
+
+  clc
+  rts
+
+  
+;
+; lan_self_modify - make all entry points variable so we can move the
+;   LANceGS card around in the Apple
+;
+lan_self_modify:
+  lda #$C0  ; FIXME - hardcoded to slot 4
+  clc    ; We'll be adding later, so clear carry
+  ; Make the accumulator contain slot number plus $80
+  ;   i.e. Slot 1 = $90
+  ;   i.e. Slot 2 = $A0
+  ;   i.e. Slot 3 = $B0
+  ;   i.e. Slot 4 = $C0
+  ;   i.e. Slot 5 = $D0
+  ;   i.e. Slot 6 = $E0
+  ;   i.e. Slot 7 = $F0
+; $C0s0: Save off all ethtcr, ethcr, ethmmucr, and ethmt mods
+  sta fixlan01 + 1
+  sta fixlan09 + 1
+  sta fixlan23 + 1
+  sta fixlan37 + 1
+;  sta fixlan45 + 1  ; Removed
+  sta fixlan47 + 1
+
+; $C0s1: Save off all ethtcr+1, ethcr+1, ethmmucr+1, and ethmt+1 mods
+  adc #$01
+;  sta fixlan02 + 1  ; Removed
+  sta fixlan12 + 1
+  sta fixlan13 + 1
+
+; $C0s2: Save off all ethephsr, ethbar, and ethpnr mods
+  adc #$01
+  sta fixlan03 + 1
+  sta fixlan27 + 1
+
+; $C0s3: Save off all ethephsr+1, ethbar+1, ethpnr+1, and etharr mods
+  adc #$01
+  sta fixlan04 + 1
+  sta fixlan26 + 1
+
+; $C0s4: Save off all ethrcr, ethiar, and ethfifo mods
+  adc #$01
+  sta fixlan07 + 1
+  sta fixlan15 + 1
+
+; $C0s5: Save off all ethrcr+1, ethiar+1, and ethfifo+1 mods
+  adc #$01
+  sta fixlan06 + 1
+  sta fixlan08 + 1
+  sta fixlan10 + 1
+  sta fixlan16 + 1
+
+; $C0s6: Save off all ethecr, ethptr, and ethiar+2 mods
+  adc #$01
+  sta fixlan17 + 1
+  sta fixlan28 + 1
+  sta fixlan39 + 1
+
+; $C0s7: Save off all ethecr+1, ethptr+1, and ethiar+3 mods
+  adc #$01
+  sta fixlan18 + 1
+  sta fixlan29 + 1
+  sta fixlan40 + 1
+
+; $C0s8: Save off all ethmir, ethdata, ethmgmt, and ethiar+4 mods
+  adc #$01
+  sta fixlan19 + 1
+  sta fixlan30 + 1
+  sta fixlan31 + 1
+  sta fixlan32 + 1
+  sta fixlan33 + 1
+  sta fixlan34 + 1
+  sta fixlan35 + 1
+  sta fixlan36 + 1
+  sta fixlan41 + 1
+  sta fixlan42 + 1
+  sta fixlan43 + 1
+  sta fixlan44 + 1
+  sta fixlan46 + 1
+
+; $C0s9: Save off all ethmir+1, ethdata+1, ethmgmt+1, and ethiar+5 mods
+  adc #$01
+  sta fixlan20 + 1
+
+; $C0sA: Save off all ethmcr, ethgpr, and ethrev mods
+; $C0sB: Save off all ethmcr+1, ethgpr+1, and ethrev+1 mods
+  ; None
+
+; $C0sC: Save off all ethctr, ethist, ethack, and ethercv mods
+  adc #$03  ; Because there were no a or b mods
+  sta fixlan24 + 1
+  sta fixlan25 + 1
+  sta fixlan38 + 1
+
+; $C0sD: Save off all ethmsk, ethctr+1 mods
+  adc #$01
+  sta fixlan14 + 1
+  sta fixlan22 + 1
+
+; $C0sE: Save off all ethbsr mods
+  adc #$01
+  sta fixlan00 + 1
+  sta fixlan05 + 1
+  sta fixlan11 + 1
+  sta fixlan21 + 1
+
+  rts
+
+.rodata
+eth_driver_name:
+  .asciiz "LANceGS (91C96)"
+eth_driver_io_base=fixlan01+1
+  
+  
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is David Schmidt
+; Portions created by the Initial Developer is Copyright (C) 2011
+; All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_petscii_charconv_s.html b/docs/drivers_petscii_charconv_s.html new file mode 100644 index 0000000..5a2bfae --- /dev/null +++ b/docs/drivers_petscii_charconv_s.html @@ -0,0 +1,79 @@ +

ip65 technical reference

File : drivers/petscii_charconv.s

ASCII/PETSCII conversion tables
+cribbed from http://www.ffd2.com/fridge/misc/petcom.c
+

functions

functiondescription
ascii_to_native
given an ASCII char in A, return equivalent PETSCII
native_to_ascii
given a PETSCII char in A, return equivalent ASCII

implementation

;ASCII/PETSCII conversion tables
+;cribbed from http://www.ffd2.com/fridge/misc/petcom.c
+
+
+.export ascii_to_native
+.export native_to_ascii
+
+;given a PETSCII char in A, return equivalent ASCII
+native_to_ascii:
+  tax
+  lda petscii_to_ascii_table,x
+  rts
+
+;given an ASCII char in A, return equivalent PETSCII
+ascii_to_native:
+  tax
+  lda ascii_to_petscii_table,x
+  rts
+
+.rodata
+ascii_to_petscii_table:
+.byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f
+.byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f
+.byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f
+.byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f
+.byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf
+.byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$a4
+.byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f
+.byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df
+.byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f
+.byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f
+.byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+.byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+.byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f
+.byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f
+.byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef
+.byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff
+
+petscii_to_ascii_table:
+.byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f
+.byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f
+.byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f
+.byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f
+.byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f
+.byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f
+.byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf
+.byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df
+.byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f
+.byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f
+.byte $20,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+.byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+.byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f
+.byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f
+.byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+.byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+
+
+
+;-- LICENSE FOR c64charconv.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_rr-net_s.html b/docs/drivers_rr-net_s.html new file mode 100644 index 0000000..ff4f0dd --- /dev/null +++ b/docs/drivers_rr-net_s.html @@ -0,0 +1,67 @@ +

ip65 technical reference

File : drivers/rr-net.s

 RR-Net driver
+

functions

functiondescription
cs_init
initialise Retro Replay so we can access the network adapter
+inputs: none
+outputs: none

constants

constantsdescriptionvalue
cs_packet_dataaddress of 'packet data' port on RR-Net IO_BASE+4
cs_packet_pageaddress of 'packet page' port on RR-Net IO_BASE+2
cs_rxtx_dataaddress of 'recieve/transmit data' port on RR-Net IO_BASE+8
cs_tx_cmdaddress of 'transmit command' port on RR-Net IO_BASE+$0c
cs_tx_lenaddress of 'transmission length' port on RR-Net IO_BASE+$0e
eth_driver_io_base
eth_driver_name

implementation

; RR-Net driver
+
+
+  .export cs_init
+
+  .export cs_packet_page
+  .export cs_packet_data
+  .export cs_rxtx_data
+  .export cs_tx_cmd
+  .export cs_tx_len
+  .export eth_driver_name
+  .export eth_driver_io_base
+  
+IO_BASE=$de00
+;IO_BASE=$df00
+rr_ctl    = IO_BASE+1 ;address of 'control' port on Retro-Replay
+cs_packet_page  = IO_BASE+2 ;address of 'packet page' port on RR-Net
+cs_packet_data  = IO_BASE+4;address of 'packet data' port on RR-Net
+cs_rxtx_data  = IO_BASE+8 ;address of 'recieve/transmit data' port on RR-Net
+cs_tx_cmd  = IO_BASE+$0c;address of 'transmit command' port on RR-Net
+cs_tx_len  = IO_BASE+$0e;address of 'transmission length' port on RR-Net
+
+
+.code
+
+;initialise Retro Replay so we can access the network adapter
+;inputs: none
+;outputs: none
+cs_init:
+  lda rr_ctl
+  ora #1
+  sta rr_ctl
+  rts
+
+.rodata
+eth_driver_name:
+.if IO_BASE=$de00
+.byte "RR-NET",0
+.else
+.byte "64NIC+",0
+.endif
+eth_driver_io_base:
+.word IO_BASE
+
+
+;-- LICENSE FOR rr-net.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_uthernet_s.html b/docs/drivers_uthernet_s.html new file mode 100644 index 0000000..0758025 --- /dev/null +++ b/docs/drivers_uthernet_s.html @@ -0,0 +1,57 @@ +

ip65 technical reference

File : drivers/uthernet.s

uthernet driver
+currently hardcoded to use slot 3 addresses only
+

functions

functiondescription
cs_init
uthernet driver
+currently hardcoded to use slot 3 addresses only

constants

constantsdescriptionvalue
cs_packet_dataaddress of 'packet data' port on Uthernet $c0bc
cs_packet_pageaddress of 'packet page' port on Uthernet $c0ba
cs_rxtx_dataaddress of 'recieve/transmit data' port on Uthernet $c0b0
cs_tx_cmdaddress of 'transmit command' port on Uthernet $c0b4
cs_tx_lenaddress of 'transmission length' port on Uthernet $c0b6
eth_driver_io_base
eth_driver_name"UTHERNET",0 +

implementation

;uthernet driver
+;currently hardcoded to use slot 3 addresses only
+
+
+  .export cs_init
+
+  .export cs_packet_page
+  .export cs_packet_data
+  .export cs_rxtx_data
+  .export cs_tx_cmd
+  .export cs_tx_len
+  .export eth_driver_name
+    .export eth_driver_io_base
+    
+cs_rxtx_data  = $c0b0 ;address of 'recieve/transmit data' port on Uthernet
+cs_tx_cmd  = $c0b4;address of 'transmit command' port on Uthernet
+cs_tx_len  = $c0b6;address of 'transmission length' port on Uthernet
+cs_packet_page  = $c0ba;address of 'packet page' port on Uthernet
+cs_packet_data  = $c0bc;address of 'packet data' port on Uthernet
+ 
+
+  .code
+
+cs_init:
+  
+  rts
+
+.rodata
+eth_driver_name:
+  .byte "UTHERNET",0
+eth_driver_io_base:
+  .word cs_rxtx_data
+
+
+;-- LICENSE FOR uthernet.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_vic20-rr-net_s.html b/docs/drivers_vic20-rr-net_s.html new file mode 100644 index 0000000..0387b59 --- /dev/null +++ b/docs/drivers_vic20-rr-net_s.html @@ -0,0 +1,62 @@ +

ip65 technical reference

File : drivers/vic20-rr-net.s

 RR-Net driver, as seen on a VIC-20 (i.e. using a MasC=uerade adapter)
+

functions

functiondescription
cs_init
initialise Retro Replay so we can access the network adapter
+inputs: none
+outputs: none

constants

constantsdescriptionvalue
cs_packet_dataaddress of 'packet data' port on RR-Net $9804
cs_packet_pageaddress of 'packet page' port on RR-Net $9802
cs_rxtx_dataaddress of 'recieve/transmit data' port on RR-Net $9808
cs_tx_cmdaddress of 'transmit command' port on RR-Net $980c
cs_tx_lenaddress of 'transmission length' port on RR-Net $980e
eth_driver_io_base
eth_driver_name"VIC20 RR-NET" +

implementation

; RR-Net driver, as seen on a VIC-20 (i.e. using a MasC=uerade adapter)
+
+
+  .export cs_init
+
+  .export cs_packet_page
+  .export cs_packet_data
+  .export cs_rxtx_data
+  .export cs_tx_cmd
+  .export cs_tx_len
+  .export eth_driver_name
+  .export eth_driver_io_base
+
+rr_ctl    = $9801 ;address of 'control' port on Retro-Replay
+cs_packet_page  = $9802 ;address of 'packet page' port on RR-Net
+cs_packet_data  = $9804;address of 'packet data' port on RR-Net
+cs_rxtx_data  = $9808 ;address of 'recieve/transmit data' port on RR-Net
+cs_tx_cmd  = $980c;address of 'transmit command' port on RR-Net
+cs_tx_len  = $980e;address of 'transmission length' port on RR-Net
+
+
+.code
+
+;initialise Retro Replay so we can access the network adapter
+;inputs: none
+;outputs: none
+cs_init:
+  lda rr_ctl
+  ora #1
+  sta rr_ctl
+  rts
+
+.rodata
+eth_driver_name:
+  .asciiz "VIC20 RR-NET"
+eth_driver_io_base:
+  .word rr_ctl-1
+
+
+;-- LICENSE FOR vic20-rr-net.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_vic20input_s.html b/docs/drivers_vic20input_s.html new file mode 100644 index 0000000..61d55cb --- /dev/null +++ b/docs/drivers_vic20input_s.html @@ -0,0 +1,222 @@ +

ip65 technical reference

File : drivers/vic20input.s

functions

functiondescription
check_for_abort_key
check whether the RUN/STOP key is being pressed
+inputs: none
+outputs: sec if RUN/STOP pressed, clear otherwise
get_filtered_input
cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+======================================================================
+Input a string and store it in GOTINPUT, terminated with a null byte.
+AX is a pointer to the allowed list of characters, null-terminated.
+set AX to $0000 for no filter on input
+max # of chars in y returns num of chars entered in y.
+======================================================================
+ Main entry
get_key
use Vic 20 Kernel ROM function to read a key
+inputs: none
+outputs: A contains ASCII value of key just pressed
get_key_if_available
not officially documented - where F1f5 (GETIN) falls through to if device # is 0 (KEYBD)
get_key_ip65
process inbound ip packets while waiting for a keypress

constants

constantsdescriptionvalue
filter_dns"-ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
filter_ip"." +
filter_number"1234567890",0 +
filter_text================================================= +Some example filters +================================================= ",!#'()* " +
filter_url":/%&?+$" +

implementation

.export get_key  
+.export get_filtered_input
+.export filter_text
+.export filter_ip
+.export filter_dns
+.export filter_url
+.export filter_number
+.export check_for_abort_key
+.export get_key_if_available
+.export get_key_ip65
+.importzp copy_src
+
+.import ip65_process
+
+.include "../inc/common.i"
+.code
+
+allowed_ptr=copy_src ;reuse zero page
+
+;use Vic 20 Kernel ROM function to read a key
+;inputs: none
+;outputs: A contains ASCII value of key just pressed
+get_key:
+  jsr get_key_if_available
+  beq get_key
+  rts
+
+;use VIC 20 Kernel ROM function to read a key
+;inputs: none
+;outputs: A contains ASCII value of key just pressed (0 if no key pressed)
+get_key_if_available=$f1f9 ;not officially documented - where F1f5 (GETIN) falls through to if device # is 0 (KEYBD)
+
+
+;process inbound ip packets while waiting for a keypress
+get_key_ip65:
+  jsr ip65_process
+  jsr get_key_if_available
+  beq get_key_ip65
+  rts
+
+
+
+
+;check whether the RUN/STOP key is being pressed
+;inputs: none
+;outputs: sec if RUN/STOP pressed, clear otherwise
+check_for_abort_key:
+  lda $cb ;current key pressed
+  cmp #$18
+  bne @not_abort
+@flush_loop:
+  jsr get_key_if_available
+  bne @flush_loop
+  lda $cb ;current key pressed
+  cmp #$18
+  beq @flush_loop
+  sec
+  rts
+@not_abort:
+  clc
+  rts
+
+;cribbed from http://codebase64.org/doku.php?id=base:robust_string_input
+;======================================================================
+;Input a string and store it in GOTINPUT, terminated with a null byte.
+;AX is a pointer to the allowed list of characters, null-terminated.
+;set AX to $0000 for no filter on input
+;max # of chars in y returns num of chars entered in y.
+;======================================================================
+
+
+; Main entry
+get_filtered_input:
+  sty MAXCHARS
+  stax temp_allowed
+
+  ;Zero characters received.
+  lda #$00
+  sta INPUT_Y
+
+;Wait for a character.
+@input_get:
+  jsr get_key
+  sta LASTCHAR
+
+  cmp #$14               ;Delete
+  beq @delete
+
+  cmp #$0d               ;Return
+  beq @input_done
+
+  ;End reached?
+  lda INPUT_Y
+  cmp MAXCHARS
+  beq @input_get
+
+  ;Check the allowed list of characters.
+  ldax temp_allowed
+  stax allowed_ptr  ;since we are reusing this zero page, it may not stil be the same value since last time!
+
+  ldy #$00
+  lda allowed_ptr+1     ;was the input filter point nul?
+  beq @input_ok
+@check_allowed:
+  lda (allowed_ptr),y           ;Overwritten
+  beq @input_get         ;Reached end of list (0)
+
+  cmp LASTCHAR
+  beq @input_ok           ;Match found
+
+  ;Not end or match, keep checking
+  iny
+  jmp @check_allowed
+
+@input_ok:
+  lda LASTCHAR          ;Get the char back
+  ldy INPUT_Y
+  sta GOTINPUT,y        ;Add it to string
+  jsr $ffd2             ;Print it
+
+  inc INPUT_Y           ;Next character
+
+  ;Not yet.
+  jmp @input_get
+
+@input_done:
+   ldy INPUT_Y
+   beq  @no_input
+   lda #$00
+   sta GOTINPUT,y   ;Zero-terminate
+   clc
+   ldax #GOTINPUT
+   rts
+@no_input:
+   sec
+   rts
+; Delete last character.
+@delete:
+  ;First, check if we're at the beginning.  If so, just exit.
+  lda INPUT_Y
+  bne @delete_ok
+  jmp @input_get
+
+  ;At least one character entered.
+@delete_ok:
+  ;Move pointer back.
+  dec INPUT_Y
+
+  ;Store a zero over top of last character, just in case no other characters are entered.
+  ldy INPUT_Y
+  lda #$00
+  sta GOTINPUT,y
+
+  ;Print the delete char
+  lda #$14
+  jsr $ffd2
+
+  ;Wait for next char
+  jmp @input_get
+
+
+;=================================================
+;Some example filters
+;=================================================
+
+filter_text:
+  .byte ",!#'()* "
+filter_url: 
+.byte ":/%&?+$"
+filter_dns:
+.byte "-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+filter_ip:
+.byte "."
+filter_number: 
+.byte "1234567890",0
+
+;=================================================
+.bss
+temp_allowed: .res 2
+MAXCHARS: .res 1
+LASTCHAR: .res 1
+INPUT_Y: .res 1  
+GOTINPUT: .res 40
+
+
+
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_vic20print_s.html b/docs/drivers_vic20print_s.html new file mode 100644 index 0000000..6a8bbf7 --- /dev/null +++ b/docs/drivers_vic20print_s.html @@ -0,0 +1,106 @@ +

ip65 technical reference

File : drivers/vic20print.s

functions

functiondescription
beep
currently does nothing (should make a 'beep noise')
+inputs: none
+outputs: none
cls
use VIC 20 Kernel ROM function to clear the screen
+inputs: none
+outputs: none
use VIC 20 Kernel ROM function to print a character to the screen
+inputs: A contains petscii value of character to print
+outputs: none
print a single char in inverse text:
use VIC 20 Kernel ROM function to move to a new line
+inputs: none
+outputs: none

constants

constantsdescriptionvalue
screen_current_col$d3 +
screen_current_row$d6 +

implementation

+.export print_a
+.export print_cr
+.export cls
+.export beep
+.export print_a_inverse
+.import timer_read
+.exportzp screen_current_row
+.exportzp screen_current_col
+
+screen_current_row=$d6
+screen_current_col=$d3
+
+;use VIC 20 Kernel ROM function to print a character to the screen
+;inputs: A contains petscii value of character to print
+;outputs: none
+print_a = $ffd2
+
+.bss
+beep_timer: .res 1
+
+.code
+
+;use VIC 20 Kernel ROM function to move to a new line
+;inputs: none
+;outputs: none
+print_cr:
+  lda #13
+  jmp print_a
+
+;use VIC 20 Kernel ROM function to clear the screen
+;inputs: none
+;outputs: none
+cls:
+    lda #147  ; 'CLR/HOME'
+    jmp print_a
+
+;currently does nothing (should make a 'beep noise')
+;inputs: none
+;outputs: none
+beep:
+  lda $900e
+  ora #15
+  sta $900e  ;set volume
+
+  ;turn on osc. 3
+  lda #$FF
+  sta $900c
+
+; pause for qtr second
+  jsr timer_read
+  stx beep_timer
+  inc beep_timer
+  inc beep_timer  
+:  
+  jsr timer_read
+  cpx beep_timer
+  bne :-
+
+  ;turn off osc. 3
+  lda #$00
+  sta $900c
+
+  rts
+
+  
+;print a single char in inverse text:
+print_a_inverse:
+  pha
+  lda #18 ;inverse mode on 
+  jsr print_a
+  pla
+  jsr print_a
+  lda #146 ;inverse mode off
+  jmp print_a
+
+
+
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_vic20timer_s.html b/docs/drivers_vic20timer_s.html new file mode 100644 index 0000000..d7a030e --- /dev/null +++ b/docs/drivers_vic20timer_s.html @@ -0,0 +1,134 @@ +

ip65 technical reference

File : drivers/vic20timer.s

 timer routines
+
+ the timer should be a 16-bit counter that's incremented by about
+ 1000 units per second. it doesn't have to be particularly accurate.
+ this VIC20 implementation requires the routine timer_vbl_handler be called 60 times per second
+

functions

functiondescription
timer_init
reset timer to 0
+inputs: none
+outputs: none
timer_read
read the current timer value 
+ inputs: none
+ outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init')
timer_seconds

implementation

; timer routines
+;
+; the timer should be a 16-bit counter that's incremented by about
+; 1000 units per second. it doesn't have to be particularly accurate.
+; this VIC20 implementation requires the routine timer_vbl_handler be called 60 times per second
+
+  .include "../inc/common.i"
+
+
+  .export timer_init
+  .export timer_read
+  .export timer_seconds
+  
+  IRQ_VECTOR=$314
+
+  .bss
+  current_time_value: .res 2
+  current_seconds: .res 1
+  current_jiffies: .res 1
+  .data 
+  jmp_old_handler:
+    .byte $4c ;JMP    
+old_handler:    
+    .word $00
+  
+  .code
+
+;reset timer to 0
+;inputs: none
+;outputs: none
+timer_init:
+  lda old_handler
+  bne @handler_installed
+  ldax  IRQ_VECTOR
+  stax old_handler  
+  ldax #timer_vbl_handler
+  stax  IRQ_VECTOR
+@handler_installed:  
+  lda  #0
+  sta current_time_value
+  sta current_time_value+1
+  sta current_seconds
+  sta current_jiffies
+  rts
+
+;read the current timer value 
+; inputs: none
+; outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init')
+timer_read:
+  ldax  current_time_value
+  rts
+
+; tick over the current timer value - should be called 60 times per second
+; inputs: none
+; outputs: none (all registers preserved, by carry flag can be modified)
+timer_vbl_handler:
+  pha
+  lda #$11  ; 60 HZ =~ 17 ms per 'tick' 
+  adc current_time_value
+  sta current_time_value
+  bcc :+
+  inc current_time_value+1
+:
+
+  inc current_jiffies
+  lda current_jiffies
+  cmp #60
+  bne @done
+  lda #0
+  sta current_jiffies
+  inc current_seconds 
+  ;we don't want to mess around with decimal mode in an IRQ handler
+  lda current_seconds 
+  cmp #$0a
+  bne :+
+  lda #$10
+:  
+  cmp #$1a
+  bne :+
+  lda #$20
+:
+  cmp #$2a
+  bne :+
+  lda #$30
+:
+  cmp #$3a
+  bne :+
+  lda #$40
+:
+  cmp #$4a
+  bne :+
+  lda #$50
+:
+  cmp #$5a
+  bne :+
+  lda #$00
+:
+sta current_seconds
+@done:  
+  pla
+  jmp jmp_old_handler
+
+timer_seconds:
+  lda current_seconds
+  rts
+
+;-- LICENSE FOR c64timer_nb65.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/drivers_w5100_s.html b/docs/drivers_w5100_s.html new file mode 100644 index 0000000..1c0f23b --- /dev/null +++ b/docs/drivers_w5100_s.html @@ -0,0 +1,1123 @@ +

ip65 technical reference

File : drivers/w5100.s

 Ethernet driver for W5100 W5100 chip 
+
+

functions

functiondescription
eth_init
initialize the ethernet adaptor
+inputs: none
+outputs: carry flag is set if there was an error, clear otherwise
+this implementation uses a default address for the w5100, and can be
+called as a 'generic' eth driver init function
eth_rx
receive a packet
+inputs: none
+outputs:
+ if there was an error receiving the packet (or no packet was ready) then carry flag is set
+ if packet was received correctly then carry flag is clear, 
+ eth_inp contains the received packet, 
+ and eth_inp_len contains the length of the packet
eth_tx
 send a packet
+inputs:
+ eth_outp: packet to send
+ eth_outp_len: length of packet to send
+outputs:
+ if there was an error sending the packet then carry flag is set
+ otherwise carry flag is cleared
tcp_close
close the current connection
+inputs:
+   none
+outputs:
+   carry flag is set if an error occured, clear otherwise
tcp_connect
make outbound tcp connection
+inputs:
+ tcp_connect_ip:  destination ip address (4 bytes)
+ AX: destination port (2 bytes)
+ tcp_callback: vector to call when data arrives on this connection
+outputs:
+   carry flag is set if an error occured, clear otherwise
tcp_listen
listen for an inbound tcp connection
+this is a 'blocking' call, i.e. it will not return until a connection has been made
+inputs:
+ AX: destination port (2 bytes)
+ tcp_callback: vector to call when data arrives on this connection
+outputs:
+   carry flag is set if an error occured, clear otherwise
tcp_send
send tcp data
+inputs:
+   tcp connection should already be opened
+   tcp_send_data_len: length of data to send (exclusive of any headers)
+   AX: pointer to buffer containing data to be sent
+outputs:
+   carry flag is set if an error occured, clear otherwise  
tcp_send_keep_alive
send an empty ACK packet on the current connection
+inputs:
+   none
+outputs:
+   carry flag is set if an error occured, clear otherwise
tcp_send_string
send a string over the current tcp connection
+inputs:
+   tcp connection should already be opened
+   AX: pointer to buffer - data up to (but not including)
+ the first nul byte will be sent. max of 255 bytes will be sent.
+outputs:
+   carry flag is set if an error occured, clear otherwise
w5100_ip65_init
initialize the ip65 stack for the w5100 ethernet adaptor
+inputs: none
+outputs: carry flag is set if there was an error, clear otherwise
w5100_read_register
 read one of the W5100 registers
+ inputs: AX = register number to read
+ outputs: A = value of nominated register
+ y is overwritten
w5100_select_register
+ select one of the W5100 registers for subsequent read or write
+ inputs: AX = register number to select
+ outputs: none
w5100_set_ip_config
copy the IP65 configuration to the the w5100 onchip configuration
+we assume MAC has been configured already via eth_init, but IP
+address etc may not be known when the w5100 was initialised (e.g.
+if using DHCP).
w5100_write_register
 write to one of the W5100 registers
+ inputs: AX = register number to write
+	Y = value to write to register
+ outputs: none

variables

variabledescriptionsize (bytes)
tcp_callbackvector to routine to be called when data is received over tcp connection 2
tcp_connect_ipip address of remote server to connect to 4
tcp_connect_remote_port2
tcp_inbound_data_length2
tcp_inbound_data_ptr2
tcp_send_data_len2
tcp_state1

constants

constantsdescriptionvalue
eth_driver_io_base
eth_driver_name"RR-NET MK3 (WIZNET 5100)" +
tcp_remote_iptcp_connect_ip +

implementation

; Ethernet driver for W5100 W5100 chip 
+;
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+.include "../inc/common.i"
+
+.include "w5100.i"
+
+WIZNET_BASE=$DE04
+
+WIZNET_MODE_REG = WIZNET_BASE
+WIZNET_ADDR_HI = WIZNET_BASE+1
+WIZNET_ADDR_LO = WIZNET_BASE+2
+WIZNET_DATA_REG = WIZNET_BASE+3
+
+
+;DEBUG = 1
+  .export eth_init
+  .export eth_rx
+  .export eth_tx
+  .export eth_driver_name
+  .export eth_driver_io_base
+  .import eth_inp
+  .import eth_inp_len
+  .import eth_outp
+  .import eth_outp_len
+
+  .import timer_init
+  .import timer_read
+  
+  .import arp_init
+  .import ip_init
+  .import  cfg_init
+  
+  .importzp eth_dest
+  .importzp eth_src
+  .importzp eth_type
+  .importzp eth_data
+  .importzp copy_src
+  .importzp copy_dest
+
+  .export w5100_ip65_init
+  .export w5100_read_register
+  .export w5100_select_register
+
+  .export w5100_write_register
+  .export w5100_set_ip_config
+  .export tcp_connect
+  .export tcp_connect_ip
+  .export tcp_callback
+  .export tcp_send_data_len
+  .export tcp_send_string
+  .export tcp_send
+  .export tcp_send_keep_alive
+  .export  tcp_close
+  .export tcp_state
+
+  .export tcp_connect_remote_port
+  .export tcp_remote_ip
+  .export tcp_listen
+  
+  .export tcp_inbound_data_ptr
+  .export tcp_inbound_data_length
+
+  .import cfg_mac
+  .import  cfg_ip
+  .import  cfg_netmask
+  .import  cfg_gateway
+
+  .import ip65_error
+  .import ip65_process
+  .import check_for_abort_key
+
+  
+  .code
+
+;initialize the ethernet adaptor
+;inputs: none
+;outputs: carry flag is set if there was an error, clear otherwise
+;this implementation uses a default address for the w5100, and can be
+;called as a 'generic' eth driver init function
+eth_init:
+    lda $de01
+  ora #1      ;turn on clockport
+  sta $de01
+  
+  
+  lda #$80  ;reset
+  sta WIZNET_MODE_REG
+  lda WIZNET_MODE_REG
+  bne @error  ;writing a byte to the MODE register with bit 7 set should reset.
+        ;after a reset, mode register is zero
+        ;therefore, if there is a real W5100 at the specified address,
+        ;we should be able to write a $80 and read back a $00
+  lda #$13  ;set indirect mode, with autoinc, no auto PING
+  sta WIZNET_MODE_REG
+  lda WIZNET_MODE_REG
+  cmp #$13
+  bne @error  ;make sure if we write to mode register without bit 7 set,
+        ;the value persists.
+  lda #$00
+  sta  WIZNET_ADDR_HI
+  lda #$16
+  sta  WIZNET_ADDR_LO
+  
+  ldx #$00    ;start writing to reg $0016 - Interrupt Mask Register
+@loop:
+  lda w5100_config_data,x  
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$06
+  bne @loop
+  
+  lda #$09
+  sta  WIZNET_ADDR_LO
+  ldx #$00    ;start writing to reg $0009 - MAC address
+  
+@mac_loop:
+  lda cfg_mac,x
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$06
+  bne @mac_loop
+  
+  ;set up socket 0 for MAC RAW mode 
+
+  ldax #W5100_RMSR  ;rx memory size (each socket)
+  stx  WIZNET_ADDR_HI
+  sta  WIZNET_ADDR_LO
+
+  lda  #$0A      ;sockets 0 & 1 4KB each, other sockets 0KB
+            ;if this is changed, change the mask in eth_rx as well!
+
+  sta WIZNET_DATA_REG
+
+  ldax #W5100_TMSR  ;rx memory size (each socket)
+  stx  WIZNET_ADDR_HI
+  sta  WIZNET_ADDR_LO
+
+  lda  #$0A      ;sockets 0 & 1 4KB each, other sockets 0KB
+            ;if this is changed, change the mask in eth_tx as well!
+  sta WIZNET_DATA_REG
+
+  ldax #W5100_S0_MR
+  stx  WIZNET_ADDR_HI
+  sta  WIZNET_ADDR_LO
+  lda  #W5100_MODE_MAC_RAW
+  sta WIZNET_DATA_REG
+  
+  ;open socket 0 
+
+  
+  jsr  w5100_write_register
+  ldax #W5100_S0_CR
+  stx  WIZNET_ADDR_HI
+  sta  WIZNET_ADDR_LO
+  lda  #W5100_CMD_OPEN
+  sta WIZNET_DATA_REG
+
+  lda #tcp_cxn_state_closed
+  sta tcp_state
+  
+  clc
+  rts
+@error:  
+  sec
+  rts    ;
+
+;initialize the ip65 stack for the w5100 ethernet adaptor
+;inputs: none
+;outputs: carry flag is set if there was an error, clear otherwise
+
+w5100_ip65_init:
+    jsr cfg_init    ;copy default values (including MAC address) to RAM
+  jsr eth_init
+  
+  bcc @ok
+    lda #KPR_ERROR_DEVICE_FAILURE
+    sta ip65_error
+    rts
+@ok:  
+  jsr timer_init    ; initialize timer
+  jsr arp_init    ; initialize arp
+  jsr ip_init    ; initialize ip, icmp, udp, and tcp
+  clc
+  rts
+
+
+;receive a packet
+;inputs: none
+;outputs:
+; if there was an error receiving the packet (or no packet was ready) then carry flag is set
+; if packet was received correctly then carry flag is clear, 
+; eth_inp contains the received packet, 
+; and eth_inp_len contains the length of the packet
+eth_rx:
+
+  ;eth_rx will get called in the main polling loop
+  ;we shoe horn a check for data on the TCP socket here
+  ;if we do get TCP data, we will call the TCP callback routine
+  ;but we hide all of this from the ip65 stack proper.
+  lda tcp_state  
+  beq  @no_tcp
+
+  jsr  tcp_rx
+  bcc @no_tcp  ;if we didn't get any TCP traffic, go check for a raw ethernet packet
+    ;eth_inp and eth_inp_len are not valid, so leave carry flag set to indicate no ethernet frame data 
+  rts
+  
+@no_tcp:  
+
+  ldax #W5100_S0_RX_RSR0 
+  jsr  w5100_read_register
+  sta  eth_inp_len+1
+  ldax #W5100_S0_RX_RSR1
+  jsr  w5100_read_register
+  sta  eth_inp_len
+  bne  @got_data
+  lda  eth_inp_len+1
+  bne  @got_data
+  sec
+  rts
+@got_data:
+
+  lda #$8D  ;opcode for STA
+  sta next_eth_packet_byte
+  ldax #eth_inp
+  stax copy_dest
+  
+  lda #2
+  sta  byte_ctr_lo
+  lda  #0
+  sta  byte_ctr_hi
+
+;read the 2 byte frame length
+  jsr  @get_current_rx_rd
+  jsr  @mask_and_adjust_rx_read
+
+  
+  ldax rx_rd_ptr
+  jsr  w5100_read_register
+  sta eth_inp_len+1  ;high byte of frame length
+  jsr @inc_rx_rd_ptr
+  ldax rx_rd_ptr
+  jsr  w5100_read_register
+  sta eth_inp_len  ;lo byte of frame length
+
+  ;now copy the rest of the frame to the eth_inp buffer
+  ;we keep our own copy of RX_RD_PTR in sync, rather than read WIZNET_ADDR registers
+  ;because of issue where reads to WIZNET_ADDR can cause the autoinc ptr to advance erroneously
+  ;when WizNet cart used in a cartridge expander
+  
+  ldy  #0
+@get_next_byte:
+  inc  rx_rd_ptr
+  bne  :+
+  inc  rx_rd_ptr+1
+  lda rx_rd_ptr+1
+  and  #$0F
+  clc
+  adc  #$60
+  sta rx_rd_ptr+1
+  sta WIZNET_ADDR_HI
+:  
+  lda WIZNET_DATA_REG
+  sta (copy_dest),y
+  iny
+  bne  :+
+  inc  copy_dest+1
+:  
+  inc  byte_ctr_lo
+  bne  :+
+  inc  byte_ctr_hi
+:  
+
+  lda  byte_ctr_lo
+  cmp  eth_inp_len
+  bne  @get_next_byte
+  lda  byte_ctr_hi
+  cmp  eth_inp_len+1
+  bne  @get_next_byte
+
+  
+;update the RX RD pointer past the frame we just read  
+  jsr  @get_current_rx_rd
+  clc  
+  lda  rx_rd_ptr
+  adc eth_inp_len
+  sta rx_rd_ptr
+  lda  rx_rd_ptr+1
+  adc eth_inp_len+1
+  tay
+  ldax #W5100_S0_RX_RD0
+  jsr  w5100_write_register
+  ldy rx_rd_ptr
+  
+  ldax #W5100_S0_RX_RD1
+  jsr  w5100_write_register
+  ldax #W5100_S0_CR
+   ldy  #W5100_CMD_RECV
+  jsr  w5100_write_register
+
+;now adjust the input length to remove the 2 byte header length
+  sec
+  lda  eth_inp_len
+  sbc  #2
+  sta  eth_inp_len
+  bcs  :+
+  dec  eth_inp_len
+:  
+
+  
+  clc
+  rts
+
+@inc_rx_rd_ptr:
+  inc  rx_rd_ptr
+  bne  :+
+  inc  rx_rd_ptr+1
+@mask_and_adjust_rx_read:
+  lda rx_rd_ptr+1
+  and  #$0F
+  clc
+  adc  #$60
+  sta rx_rd_ptr+1
+:  
+  rts
+  
+@get_current_rx_rd:
+  ldax #W5100_S0_RX_RD0
+  jsr  w5100_read_register  
+  sta rx_rd_ptr+1
+  ldax #W5100_S0_RX_RD1
+  jsr  w5100_read_register
+  sta rx_rd_ptr
+  rts
+
+; send a packet
+;inputs:
+; eth_outp: packet to send
+; eth_outp_len: length of packet to send
+;outputs:
+; if there was an error sending the packet then carry flag is set
+; otherwise carry flag is cleared
+eth_tx:
+  
+  lda #$AD  ;opcode for LDA
+  sta next_eth_packet_byte
+  ldax #eth_outp
+  sta  eth_ptr_lo
+  stx eth_ptr_hi
+  lda #0
+  sta  byte_ctr_lo
+  sta  byte_ctr_hi
+  
+  jsr @get_current_tx_wr  
+  jmp  @calculate_tx_wr_ptr  
+@send_next_byte:
+  
+  jsr  next_eth_packet_byte
+  tay
+  ldax tx_wr_ptr
+  jsr  w5100_write_register
+    
+  inc  byte_ctr_lo
+  bne  :+
+  inc  byte_ctr_hi
+:  
+
+  inc  tx_wr_ptr
+  bne  :+
+  inc tx_wr_ptr+1
+@calculate_tx_wr_ptr:  
+  lda tx_wr_ptr+1
+  and  #$0F
+  clc
+  adc  #$40
+  sta tx_wr_ptr+1
+:
+
+  lda  byte_ctr_lo
+  cmp  eth_outp_len
+  bne  @send_next_byte
+  lda  byte_ctr_hi
+  cmp  eth_outp_len+1
+  bne  @send_next_byte  
+
+;all bytes copied, now adjust the tx write ptr and SEND
+  jsr @get_current_tx_wr  
+  clc  
+  lda  tx_wr_ptr
+  adc eth_outp_len
+  sta tx_wr_ptr
+  lda  tx_wr_ptr+1
+  adc eth_outp_len+1
+  tay
+  ldax #W5100_S0_TX_WR0
+  jsr  w5100_write_register
+  ldy tx_wr_ptr
+  ldax #W5100_S0_TX_WR1
+  jsr  w5100_write_register
+  ldax #W5100_S0_CR
+   ldy  #W5100_CMD_SEND
+   jsr  w5100_write_register
+  
+  clc
+  rts
+
+@get_current_tx_wr:
+  ldax #W5100_S0_TX_WR0
+  jsr  w5100_read_register  
+  sta tx_wr_ptr+1
+  ldax #W5100_S0_TX_WR1
+  jsr  w5100_read_register
+  sta tx_wr_ptr
+  rts
+
+advance_eth_ptr:
+  inc  eth_ptr_lo
+  bne  :+
+  inc  eth_ptr_hi
+:  
+  rts
+  
+  
+; read one of the W5100 registers
+; inputs: AX = register number to read
+; outputs: A = value of nominated register
+; y is overwritten
+w5100_read_register:  
+  jsr  w5100_select_register
+  lda WIZNET_DATA_REG
+  rts
+
+; write to one of the W5100 registers
+; inputs: AX = register number to write
+;  Y = value to write to register
+; outputs: none
+w5100_write_register:
+  jsr  w5100_select_register
+  tya
+  sta WIZNET_DATA_REG
+  rts
+
+
+
+
+;listen for an inbound tcp connection
+;this is a 'blocking' call, i.e. it will not return until a connection has been made
+;inputs:
+; AX: destination port (2 bytes)
+; tcp_callback: vector to call when data arrives on this connection
+;outputs:
+;   carry flag is set if an error occured, clear otherwise
+tcp_listen:
+
+  stax  tcp_local_port
+  jsr  setup_tcp_socket
+  ldax #W5100_S1_CR
+  ldy  #W5100_CMD_LISTEN
+  jsr  w5100_write_register
+
+  ;now wait for the status to change to 'established'
+@listen_loop:
+;  inc $d020
+  jsr  ip65_process
+  jsr check_for_abort_key
+  bcc @no_abort
+    lda #KPR_ERROR_ABORTED_BY_USER
+    sta  ip65_error
+    sec
+    rts
+@no_abort:
+  ldax #W5100_S1_SR
+  jsr w5100_read_register
+  cmp #W5100_STATUS_SOCK_ESTABLISHED
+  bne  @listen_loop
+
+  lda #tcp_cxn_state_established
+  sta tcp_state
+
+  ;copy the remote IP address & port number
+  ldax #W5100_S1_DIPR0
+  jsr  w5100_select_register
+  ldx  #0
+@ip_loop:  
+  lda WIZNET_DATA_REG
+  sta  tcp_remote_ip,x
+  inx
+  cpx #$04
+  bne  @ip_loop
+  
+  ldax #W5100_S1_DPORT0
+  jsr  w5100_select_register
+  lda WIZNET_DATA_REG
+  sta tcp_connect_remote_port+1
+  lda WIZNET_DATA_REG
+  sta tcp_connect_remote_port
+  
+  clc
+  rts
+
+
+;make outbound tcp connection
+;inputs:
+; tcp_connect_ip:  destination ip address (4 bytes)
+; AX: destination port (2 bytes)
+; tcp_callback: vector to call when data arrives on this connection
+;outputs:
+;   carry flag is set if an error occured, clear otherwise
+tcp_connect:
+  stax tcp_remote_port
+  jsr  timer_read  ;get a pseudo random value
+  sta tcp_local_port+1
+  inc tcp_local_port
+  
+
+  jsr  setup_tcp_socket
+    
+
+  ;set the destination IP address
+  ldax #W5100_S1_DIPR0
+  jsr  w5100_select_register
+  ldx  #0
+@remote_ip_loop:
+  lda  tcp_connect_ip,x
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$04
+  bne  @remote_ip_loop
+  ldx  #0  
+
+;W5100 register address is now W5100_S1_DPORT0, so set the destination port
+  lda  tcp_remote_port+1
+  sta WIZNET_DATA_REG
+  lda  tcp_remote_port
+  sta WIZNET_DATA_REG
+
+  ldax #W5100_S1_CR
+  ldy  #W5100_CMD_CONNECT
+  jsr  w5100_write_register
+
+  ;now wait for the status to change to 'established'
+@connect_loop:
+  ldax #W5100_S1_SR
+  jsr w5100_read_register
+  cmp #W5100_STATUS_SOCK_CLOSED
+  beq  @error
+  cmp #W5100_STATUS_SOCK_ESTABLISHED
+  beq  @ok
+
+  jsr check_for_abort_key
+  bcc @connect_loop
+    lda #KPR_ERROR_ABORTED_BY_USER
+    jmp @set_error_and_exit
+
+@ok:
+  lda #tcp_cxn_state_established
+  sta tcp_state
+
+  clc
+  rts
+@error:
+    lda #KPR_ERROR_CONNECTION_CLOSED
+@set_error_and_exit:
+    sta ip65_error
+  sec
+  rts
+
+;send a string over the current tcp connection
+;inputs:
+;   tcp connection should already be opened
+;   AX: pointer to buffer - data up to (but not including)
+; the first nul byte will be sent. max of 255 bytes will be sent.
+;outputs:
+;   carry flag is set if an error occured, clear otherwise
+tcp_send_string:
+  stax tcp_send_data_ptr
+  stax copy_src
+  lda #0
+  tay
+  sta tcp_send_data_len
+  sta tcp_send_data_len+1
+  lda (copy_src),y
+  bne @find_end_of_string
+  rts ; if the string is empty, don't send anything!
+@find_end_of_string:  
+  lda (copy_src),y
+  beq @done  
+  inc tcp_send_data_len
+  iny
+  bne @find_end_of_string
+@done:  
+  ldax tcp_send_data_ptr
+  ;now we can fall through into tcp_send
+
+;send tcp data
+;inputs:
+;   tcp connection should already be opened
+;   tcp_send_data_len: length of data to send (exclusive of any headers)
+;   AX: pointer to buffer containing data to be sent
+;outputs:
+;   carry flag is set if an error occured, clear otherwise  
+tcp_send:
+  stax tcp_send_data_ptr
+  
+  ;are we connected?
+  ldax #W5100_S1_SR
+  jsr w5100_read_register
+  cmp #W5100_STATUS_SOCK_ESTABLISHED
+  beq  @ok
+
+    lda #KPR_ERROR_CONNECTION_CLOSED
+    sta ip65_error
+    sec
+    rts
+@ok:
+
+  
+  lda #$AD  ;opcode for LDA
+  sta next_eth_packet_byte
+  
+  lda #0
+  sta  byte_ctr_lo
+  sta  byte_ctr_hi
+  
+  jsr @get_current_tx_wr  
+  jmp  @calculate_tx_wr_ptr  
+@send_next_byte:  
+  jsr  next_eth_packet_byte
+  tay
+  ldax tx_wr_ptr
+  jsr  w5100_write_register
+  
+  inc  byte_ctr_lo
+  bne  :+
+  inc  byte_ctr_hi
+:  
+
+  inc  tx_wr_ptr
+  bne  :+
+  inc tx_wr_ptr+1
+@calculate_tx_wr_ptr:  
+  lda tx_wr_ptr+1
+  and  #$0F
+  clc
+  adc  #$50
+  sta tx_wr_ptr+1
+:
+
+  lda  byte_ctr_lo
+  cmp  tcp_send_data_len
+  bne  @send_next_byte
+  lda  byte_ctr_hi
+  cmp  tcp_send_data_len+1
+  bne  @send_next_byte  
+
+;all bytes copied, now adjust the tx write ptr and SEND
+  jsr @get_current_tx_wr  
+  clc  
+  lda  tx_wr_ptr
+  adc tcp_send_data_len
+  sta tx_wr_ptr
+  lda  tx_wr_ptr+1
+  adc tcp_send_data_len+1
+  tay
+  ldax #W5100_S1_TX_WR0
+  jsr  w5100_write_register
+  ldy tx_wr_ptr
+  ldax #W5100_S1_TX_WR1
+  jsr  w5100_write_register
+  ldax #W5100_S1_CR
+   ldy  #W5100_CMD_SEND
+   jsr  w5100_write_register
+  
+  clc
+  rts
+
+@get_current_tx_wr:
+  ldax #W5100_S1_TX_WR0
+  jsr  w5100_read_register  
+  sta tx_wr_ptr+1
+  ldax #W5100_S1_TX_WR1
+  jsr  w5100_read_register
+  sta tx_wr_ptr
+  rts
+
+;send an empty ACK packet on the current connection
+;inputs:
+;   none
+;outputs:
+;   carry flag is set if an error occured, clear otherwise
+
+tcp_send_keep_alive:
+  ;are we connected?
+  ldax #W5100_S1_SR
+  jsr w5100_read_register
+  cmp #W5100_STATUS_SOCK_ESTABLISHED
+  beq  @ok
+
+    lda #KPR_ERROR_CONNECTION_CLOSED
+    sta ip65_error
+    sec
+    rts
+@ok:
+  ldax #W5100_S1_CR
+  ldy  #W5100_CMD_SEND_KEEP
+  jsr  w5100_write_register
+  clc
+  rts
+  
+
+
+  
+;close the current connection
+;inputs:
+;   none
+;outputs:
+;   carry flag is set if an error occured, clear otherwise
+tcp_close:
+
+  ldax #W5100_S1_CR
+  ldy  #W5100_CMD_DISCONNECT
+  jsr  w5100_write_register
+  clc
+  rts
+
+
+;poll the TCP socket
+;if there is data available, call the user supplied TCP callback
+;inputs:
+;   none
+;outputs:
+;   carry flag is set if there was data, clear otherwise
+tcp_rx:
+
+  ;is there data?
+  ldax #W5100_S1_RX_RSR0 
+  jsr  w5100_read_register
+  sta  tcp_inbound_data_length+1
+  ldax #W5100_S1_RX_RSR1
+  jsr  w5100_read_register
+  sta  tcp_inbound_data_length
+  bne  @got_data
+  lda  tcp_inbound_data_length+1
+  bne  @got_data
+  
+  ;are we connected?
+  ldax #W5100_S1_SR
+  jsr w5100_read_register
+  cmp #W5100_STATUS_SOCK_ESTABLISHED
+  beq  @connected_but_no_data
+  ;no longer connected
+  lda #tcp_cxn_state_closed
+  sta tcp_state
+  
+  lda #$ff
+  sta tcp_inbound_data_length
+  sta tcp_inbound_data_length+1
+  jsr @make_fake_eth_header
+  jsr jmp_to_callback   ;let the caller see the connection has closed
+  sec      ;don't poll the MAC RAW socket, else it may clobber the output buffer
+  rts
+@connected_but_no_data:
+  clc  ;no data - go check the MAC RAW socket
+  rts
+@got_data:
+  lda #$8D  ;opcode for STA
+  sta next_eth_packet_byte
+
+  ldax #eth_inp+$36  ;we will write to the location that TCP data would appear if this was a raw eth frame,
+            ;14 bytes of ethernet header
+            ;20 bytes of IP header
+            ;20 bytes of TCP header
+  
+  stax tcp_inbound_data_ptr
+  
+  
+  sta  eth_ptr_lo
+  stx eth_ptr_hi
+  
+  lda  #0
+  sta  byte_ctr_lo
+  sta  byte_ctr_hi
+
+  lda  tcp_inbound_data_length+1
+  cmp  #4  ;don't allow more than $4FF bytes at once ($1279) since we are writing to a 1500 byte 
+  bmi  :+
+  lda  #4
+  sta  tcp_inbound_data_length+1
+:  
+
+  ;now copy the data just arrived to the eth_inp buffer
+  jsr  @get_current_rx_rd
+  jsr @mask_and_adjust_rx_read
+@get_next_byte:
+
+  ldax rx_rd_ptr
+  jsr  w5100_read_register
+  jsr next_eth_packet_byte
+  
+  jsr @inc_rx_rd_ptr
+  
+  inc  byte_ctr_lo
+  bne  :+
+  inc  byte_ctr_hi
+:  
+
+  lda  byte_ctr_lo
+  cmp  tcp_inbound_data_length
+  bne  @get_next_byte
+  lda  byte_ctr_hi
+  cmp  tcp_inbound_data_length+1
+  bne  @get_next_byte
+
+  
+;update the RX RD pointer past the frame we just read  
+  jsr  @get_current_rx_rd
+  clc  
+  lda  rx_rd_ptr
+  adc tcp_inbound_data_length
+  sta rx_rd_ptr
+  lda  rx_rd_ptr+1
+  adc tcp_inbound_data_length+1
+  tay
+  ldax #W5100_S1_RX_RD0
+  jsr  w5100_write_register
+  ldy rx_rd_ptr
+  
+  ldax #W5100_S1_RX_RD1
+  jsr  w5100_write_register
+  ldax #W5100_S1_CR
+   ldy  #W5100_CMD_RECV
+  jsr  w5100_write_register
+
+  jsr @make_fake_eth_header
+  jsr jmp_to_callback   ;let the caller see the connection has closed
+  sec      ;don't poll the MAC RAW socket, else it may clobber the output buffer
+  rts
+  
+@inc_rx_rd_ptr:
+  inc  rx_rd_ptr
+  bne  :+
+  inc  rx_rd_ptr+1
+@mask_and_adjust_rx_read:
+  lda rx_rd_ptr+1
+  and  #$0F
+  clc
+  adc  #$70
+  sta rx_rd_ptr+1
+:  
+  rts
+  
+@get_current_rx_rd:
+  ldax #W5100_S1_RX_RD0
+  jsr  w5100_read_register  
+  sta rx_rd_ptr+1
+  ldax #W5100_S1_RX_RD1
+  jsr  w5100_read_register
+  sta rx_rd_ptr
+  rts
+
+;the function dispatcher (and possibly other parts of the ip65 stack) expect to find valid values in the eth_inp frame
+;when processing tcp data
+@make_fake_eth_header:
+
+  .import  ip_inp
+  .import udp_inp
+  ;first set the TCP protocol value
+  lda #6  ;TCP protocol number
+  sta ip_inp+9 ;proto number
+  
+  ;now copy the remote IP address
+  ldx  #0
+@ip_loop:  
+  lda  tcp_remote_ip,x
+  sta ip_inp+12,x  ;src IP 
+  inx
+  cpx #$04
+  bne  @ip_loop  
+  
+  ;now the local & remote ports
+  lda tcp_connect_remote_port
+  sta udp_inp+1 ;remote port (lo byte)
+  lda tcp_connect_remote_port+1  
+  sta udp_inp+0 ;remote port (high byte)
+  lda tcp_local_port
+  sta udp_inp+3 ;local port (lo byte)
+  lda tcp_local_port+1
+  sta udp_inp+2 ;local port (high byte)
+  
+  rts
+
+jmp_to_callback:
+  jmp (tcp_callback)
+
+
+;copy the IP65 configuration to the the w5100 onchip configuration
+;we assume MAC has been configured already via eth_init, but IP
+;address etc may not be known when the w5100 was initialised (e.g.
+;if using DHCP).
+w5100_set_ip_config:
+  ldax #W5100_GAR0
+  jsr  w5100_select_register
+  ldx  #0
+@gateway_loop:  
+  lda  cfg_gateway,x
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$04
+  bne  @gateway_loop
+  ldx  #0  
+@netmask_loop:  
+  lda  cfg_netmask,x
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$04
+  bne  @netmask_loop
+  
+  ldax #W5100_SIPR0
+  jsr  w5100_select_register
+  ldx  #0
+@ip_loop:  
+  lda  cfg_ip,x
+  sta WIZNET_DATA_REG
+  inx
+  cpx #$04
+  bne  @ip_loop
+  rts
+
+setup_tcp_socket:  
+  jsr w5100_set_ip_config
+  ldax #W5100_S1_PORT0
+  jsr  w5100_select_register
+  lda tcp_local_port+1
+  sta WIZNET_DATA_REG
+  lda tcp_local_port
+  sta WIZNET_DATA_REG
+  
+  lda #0
+  sta tcp_state
+
+  ldax #W5100_S1_MR
+  ldy  #W5100_MODE_TCP
+  jsr  w5100_write_register
+  
+  ;open socket 1
+  ldax #W5100_S1_CR
+  ldy  #W5100_CMD_OPEN
+  jsr  w5100_write_register  
+  rts
+
+  
+.rodata
+eth_driver_name:
+  .asciiz "RR-NET MK3 (WIZNET 5100)"
+
+eth_driver_io_base:
+  .word WIZNET_BASE
+
+w5100_config_data:  
+  .byte $00  ;no interrupts 
+  .byte $0f  ;400ms retry (default)
+  .byte $a0
+  .byte $08  ;# of timeouts
+  .byte $55  ;4 sockets @2K each, tx/rx
+  .byte $55
+
+
+;
+; select one of the W5100 registers for subsequent read or write
+; inputs: AX = register number to select
+; outputs: none
+w5100_select_register:
+set_hi:
+  stx WIZNET_ADDR_HI
+set_lo:
+  sta WIZNET_ADDR_LO
+  rts
+
+; return which W5100 register the next read or write will access
+; inputs: none
+; outputs: AX = selected register number
+w5100_get_current_register:  
+get_hi:
+  ldx WIZNET_ADDR_HI
+get_lo:
+  lda WIZNET_ADDR_LO
+  rts
+
+
+.segment "SELF_MODIFIED_CODE"
+
+next_eth_packet_byte:
+  lda  $FFFF  ;eth_packet
+  jmp advance_eth_ptr
+  
+eth_ptr_lo=next_eth_packet_byte+1
+eth_ptr_hi=next_eth_packet_byte+2
+
+; .bss
+; don't use BSS because we are out of room in the location that lives in the
+; config used for 16K carts ($C010..$CFFF)
+;there seems to be a little room still free in the seg used for SELF_MODIFIED_CODE
+
+ w5100_addr: .res 2
+ byte_ctr_lo: .res 1
+ byte_ctr_hi: .res 1
+ 
+ tx_wr_ptr: .res 2
+ rx_rd_ptr: .res 2
+tcp_local_port: .res 2
+
+tcp_state: .res 1
+
+tcp_connect_ip: .res 4 ;ip address of remote server to connect to
+tcp_callback: .res 2 ;vector to routine to be called when data is received over tcp connection
+
+tcp_remote_port: .res 2 ;temp space for holding port to listen on or connect to
+tcp_send_data_len: .res 2
+tcp_send_data_ptr = eth_ptr_lo
+
+
+tcp_inbound_data_length: .res 2
+tcp_inbound_data_ptr: .res 2
+
+tcp_connect_remote_port: .res 2
+tcp_remote_ip = tcp_connect_ip
+
+tcp_cxn_state_closed      = 0 
+tcp_cxn_state_listening   = 1  ;(waiting for an inbound SYN)
+tcp_cxn_state_syn_sent    = 2  ;(waiting for an inbound SYN/ACK)
+tcp_cxn_state_established = 3  ;  
+
+
+;-- LICENSE FOR w5100a.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes (jonno@jamtronix.com)
+; Portions created by the Initial Developer is Copyright (C) 2010
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/function_index.html b/docs/function_index.html new file mode 100644 index 0000000..20cb30d --- /dev/null +++ b/docs/function_index.html @@ -0,0 +1 @@ +

functions

functiondefined in
add_16_32ip65/arithmetic.s
add_32_32ip65/arithmetic.s
arp_addip65/arp.s
arp_calculate_gateway_maskip65/arp.s
arp_initip65/arp.s
arp_lookupip65/arp.s
arp_processip65/arp.s
ascii_to_nativedrivers/petscii_charconv.s, drivers/a2charconv.s
beepdrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
cfg_get_configuration_ptrip65/config.s
cfg_initip65/config.s
check_for_abort_keydrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
cifs_l1_decodeip65/cifs.s
cifs_l1_encodeip65/cifs.s
cifs_startip65/cifs.s
clsdrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
cmp_16_16ip65/arithmetic.s
cmp_32_32ip65/arithmetic.s
console_printfip65/printf.s
console_stroutip65/debug.s
copymemip65/copymem.s
cs_initdrivers/vic20-rr-net.s, drivers/uthernet.s, drivers/rr-net.s
dbg_dump_eth_headerip65/debug.s
dbg_dump_ip_headerip65/debug.s
dbg_dump_udp_headerip65/debug.s
dbgout16ip65/debug.s
dhcp_initip65/dhcp.s
dns_resolveip65/dns.s
dns_set_hostnameip65/dns.s
eth_initdrivers/w5100.s, drivers/lan91c96.s, drivers/cs8900a.s
eth_rxdrivers/w5100.s, drivers/lan91c96.s, drivers/cs8900a.s
eth_set_broadcast_destip65/eth.s
eth_set_my_mac_srcip65/eth.s
eth_set_protoip65/eth.s
eth_txdrivers/w5100.s, drivers/lan91c96.s, drivers/cs8900a.s
exit_to_basicdrivers/c64kernal.s, drivers/a2kernal.s
get_filtered_inputdrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
get_keydrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
get_key_if_availabledrivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
get_key_ip65drivers/vic20input.s, drivers/c64inputs.s, drivers/a2input.s
http_get_valueip65/http.s
http_parse_requestip65/http.s
http_variables_bufferip65/http.s
httpd_startip65/httpd.s
icmp_add_listenerip65/icmp.s
icmp_initip65/icmp.s
icmp_pingip65/icmp.s
icmp_processip65/icmp.s
icmp_remove_listenerip65/icmp.s
icmp_send_echoip65/icmp.s
io_read_cataloguedrivers/cbm_disk_access.s
io_read_catalogue_exdrivers/cbm_disk_access.s
io_read_filedrivers/cbm_disk_access.s
io_read_file_with_callbackdrivers/cbm_disk_access.s
io_read_sectordrivers/cbm_disk_access.s
io_write_sectordrivers/cbm_disk_access.s
ip65_initip65/ip65.s
ip65_processip65/ip65.s
ip65_random_wordip65/ip65.s, ip65/ip65.s
ip_calc_cksumip65/ip.s
ip_create_packetip65/ip.s
ip_initip65/ip.s
ip_processip65/ip.s
ip_sendip65/ip.s
kipper_dispatcherip65/function_dispatcher.s
mul_8_16ip65/arithmetic.s
native_to_asciidrivers/petscii_charconv.s, drivers/a2charconv.s
parse_dotted_quadip65/dottedquad.s
parse_hex_digitsip65/string_utils.s
parse_integerip65/string_utils.s
parser_initip65/parser.s
parser_skip_nextip65/parser.s
print_adrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
print_a_inversedrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
print_crdrivers/vic20print.s, drivers/c64print.s, drivers/a2print.s
resource_downloadip65/url.s
sntp_get_timeip65/sntp.s
sub_16_16ip65/arithmetic.s
tcp_closeip65/tcp.s, drivers/w5100.s
tcp_connectip65/tcp.s, drivers/w5100.s
tcp_initip65/tcp.s
tcp_listenip65/tcp.s, drivers/w5100.s
tcp_processip65/tcp.s
tcp_sendip65/tcp.s, drivers/w5100.s
tcp_send_keep_aliveip65/tcp.s, drivers/w5100.s
tcp_send_stringip65/tcp.s, drivers/w5100.s
telnet_connectip65/telnet.s
tftp_callback_vectorip65/tftp.s
tftp_clear_callbacksip65/tftp.s
tftp_downloadip65/tftp.s
tftp_set_callback_vectorip65/tftp.s
tftp_uploadip65/tftp.s
tftp_upload_from_memoryip65/tftp.s
timer_initdrivers/vic20timer.s, drivers/c64timer.s, drivers/a2timer.s
timer_readdrivers/vic20timer.s, drivers/c64timer.s, drivers/a2timer.s
timer_secondsdrivers/vic20timer.s, drivers/c64timer.s
timer_timeoutip65/timer.s
udp_add_listenerip65/udp.s
udp_initip65/udp.s
udp_processip65/udp.s
udp_remove_listenerip65/udp.s
udp_sendip65/udp.s
url_downloadip65/url.s
url_parseip65/url.s
vt100_init_terminaldrivers/c64_vt100.s
vt100_process_inbound_chardrivers/c64_vt100.s
vt100_transform_outbound_chardrivers/c64_vt100.s
w5100_ip65_initdrivers/w5100.s
w5100_read_registerdrivers/w5100.s
w5100_select_registerdrivers/w5100.s
w5100_set_ip_configdrivers/w5100.s
w5100_write_registerdrivers/w5100.s
xmodem_receiveip65/xmodem.s
xmodem_sendip65/xmodem.s
\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..c0e3215 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,203 @@ + + +IP65 - a TCP/IP stack for 6502 computers + + + + + + + +

IP65

+ +

+IP65 is a TCP/IP stack for 6502 based computers. +

+ + +

Status

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ApplicationsHTTP ClientHTTP ServerTelnet ClientGopher ClientTFTPPing
ServicesTCPDHCPDNSEcho
TransportUDPICMP
NetworkIP
AddressingARP
Ethernet controllerCS8900A / LAN91C96 / W5100
Ethernet driverRR-NetETH64UthernetLANceGSUthernet IIDragon CartRR-Net
Host computerC64 / C128Apple ][ATARI 8-bitVIC20
+ +

Documentation

+ +IP65 technical reference + + +

Download

+ +Latest code (github.com)

+ip65-2012-11-21.zip (sourceforge.net)

+ip65-2009-01-22.zip (paradroid.net) + +

History

+
+  Release	Maintainer	Changes
+  -------	----------	-------
+  2011-01-15	Jonno Downes	Drivers for Wiznet W5100 ethernet, VIC-20 host
+  2009-12-23	Jonno Downes	TCP and telnet bugfixes, vt100 emulation, XMODEM support
+  2009-10-31	Jonno Downes	Added Web Application Server functions
+  2009-08-02	Jonno Downes	More TCP functionality, includes telnet
+  2009-07-12	Jonno Downes	Initial TCP implementation (use -DTCP to include)
+  2009-03-21	Jonno Downes	Added technical reference documentation
+  2009-03-15	Jonno Downes	Added DHCP, DNS & TFTP
+  2009-01-22	Per Olofsson	Added copymem fix from Jonno Downes. Added MPL license.
+  2008-09-27	Per Olofsson	Added timeout fix for ineth_tx from David Schmidt.
+  2006-09-20	Per Olofsson	Fixed checksum calculation for odd packet sizes.
+  2006-02-22	Per Olofsson	Added fix for sending of packets larger than 256 bytes
+				from Ewen Wannop and Glenn Jones.
+
+ +

Sample UDP listener source

+ +
+gangedport      = 60064
+
+        jsr ip65_init
+        lda #<gotpacket
+        ldx #>gotpacket
+        sta udp_callback
+        stx udp_callback + 1
+        lda #<gangedport
+        ldx #>gangedport
+        jsr udp_add_listener
+
+main:
+        jsr ip65_process
+        jmp main
+
+gotpacket:
+        sei
+        lda $01
+        pha
+        lda udp_inp
+        sta $01
+
+        lda udp_inp + 1
+        ldx udp_inp + 2
+        sta zp_data
+        stx zp_data + 2
+        ldy udp_inp + 3
+copy:
+        lda udp_inp + 3,y
+        sta (zp_data),y
+        dey
+        bne copy
+
+        pla
+        sta $01
+        cli
+        rts
+
+ + +

License

+ +This project is released under the Mozilla Public License Version 1.1. +For details, please visit http://www.mozilla.org/MPL/. + + +

Source Code

+ +Browse online at http://github.com/cc65/ip65 or else download the whole tree through Git with the following instruction: +
git clone https://github.com/cc65/ip65.git
+ + +

Published with GitHub Pages

+ + + + diff --git a/docs/ip65_arithmetic_s.html b/docs/ip65_arithmetic_s.html new file mode 100644 index 0000000..e375ed7 --- /dev/null +++ b/docs/ip65_arithmetic_s.html @@ -0,0 +1,179 @@ +

ip65 technical reference

File : ip65/arithmetic.s

functions

functiondescription
add_16_32
add a 16bit operand to the 32 bit accumulater
+acc32=acc32+AX
add_32_32
add a 32bit operand to the 32 bit accumulater
+acc32=acc32+op32
cmp_16_16
compare 2 16bit numbers
+on exit, zero flag clear iff acc16==AX
cmp_32_32
compare 2 32bit numbers
+on exit, zero flag clear iff acc32==op32
mul_8_16
multiply a 16 bit number by an 8 bit number
+acc16=acc16*a
sub_16_16
subtract 2 16 bit numbers
+acc16=acc16-AX

variables

variabledescriptionsize (bytes)
op32

constants

constantsdescriptionvalue
acc16acc16=acc16*a acc16*a +
acc32acc32=acc32+AX acc32+AX +

implementation

.include "../inc/common.i"
+
+;helper routines for arithmetic on 32 bit numbers 
+
+;reuse the copy_* zero page locations as pointers for 32bit addition
+.importzp copy_src
+.importzp copy_dest
+
+acc32 =copy_src       ;32bit accumulater (pointer)
+op32 =copy_dest       ;32 bit operand (pointer)
+
+acc16 =acc32       ;16bit accumulater (value, NOT pointer)
+
+.bss
+temp_ax: .res 2
+.code
+;no 16bit operand as can just use AX    
+.exportzp acc32
+.exportzp op32
+.exportzp acc16
+
+.export add_32_32
+.export add_16_32
+
+.export sub_16_16   
+
+.export cmp_32_32
+.export cmp_16_16
+
+.export mul_8_16
+
+;compare 2 32bit numbers
+;on exit, zero flag clear iff acc32==op32
+cmp_32_32:
+  ldy #0
+  lda (op32),y
+  cmp (acc32),y
+  bne @exit
+  iny  
+  lda (op32),y
+  cmp (acc32),y
+  bne @exit
+  iny
+  lda (op32),y
+  cmp (acc32),y
+  bne @exit
+  iny
+  lda (op32),y
+  cmp (acc32),y
+@exit:  
+  rts
+
+;compare 2 16bit numbers
+;on exit, zero flag clear iff acc16==AX
+cmp_16_16:
+  cmp acc16
+  bne @exit
+  txa
+  cmp acc16+1
+@exit:  
+  rts
+  
+;subtract 2 16 bit numbers
+;acc16=acc16-AX
+sub_16_16:
+  stax  temp_ax
+  sec
+  lda acc16
+  sbc temp_ax
+  sta acc16
+  lda acc16+1
+  sbc temp_ax+1
+  sta acc16+1
+  rts
+  
+;add a 32bit operand to the 32 bit accumulater
+;acc32=acc32+op32
+add_32_32:
+  clc
+  ldy #0
+  lda (op32),y
+  adc (acc32),y
+  sta (acc32),y  
+  iny
+  lda (op32),y
+  adc (acc32),y
+  sta (acc32),y  
+  iny
+  lda (op32),y
+  adc (acc32),y
+  sta (acc32),y  
+  iny
+  lda (op32),y
+  adc (acc32),y
+  sta (acc32),y  
+    
+  rts
+  
+
+;add a 16bit operand to the 32 bit accumulater
+;acc32=acc32+AX
+add_16_32:
+  clc
+  ldy #0
+  adc (acc32),y
+  sta (acc32),y  
+  iny
+  txa
+  adc (acc32),y
+  sta (acc32),y  
+  iny
+  lda #0
+  adc (acc32),y
+  sta (acc32),y
+  iny
+  lda #0
+  adc (acc32),y
+  sta (acc32),y
+  rts
+  
+;multiply a 16 bit number by an 8 bit number
+;acc16=acc16*a
+mul_8_16:
+  tax
+  beq @operand_is_zero
+  lda  acc16
+  sta  temp_ax
+  lda  acc16+1
+  sta  temp_ax+1
+  
+@addition_loop:
+  dex
+  beq @done
+  clc
+  lda acc16
+  adc temp_ax
+  sta acc16
+  lda acc16+1
+  adc temp_ax+1
+  sta acc16+1
+  jmp @addition_loop  
+  
+@done:
+
+  rts  
+@operand_is_zero:
+  sta acc16
+  sta acc16+1  
+  rts
+
+
+
+;-- LICENSE FOR arithmetic.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_arp_s.html b/docs/ip65_arp_s.html new file mode 100644 index 0000000..5f86da7 --- /dev/null +++ b/docs/ip65_arp_s.html @@ -0,0 +1,465 @@ +

ip65 technical reference

File : ip65/arp.s

 ARP address resolution
+

functions

functiondescription
arp_add
 add arp_mac and arp_ip to the cache
+inputs: 
+  arp_ip is ip address to add to cache
+  arp_mac is corresponding mac address of specified ip  
+outputs: 
+  arp_cache is updated
arp_calculate_gateway_mask
arp_init
initialize arp (including clearing the arp cache)
+inputs: none
+outputs: none
arp_lookup
lookup the mac address for an ip
+inputs: arp_ip should be set to ip address to be resolved
+outputs: 
+   if carry flag is clear, then arp_mac will be set to correct mac address
+   if carry flag is set, then the correct mac address could not be found in
+   the arp cache, so an arp request was sent. so the caller should wait a while
+   (to allow time for an arp response message to arrive) and then call arp_lookup again.
arp_process
handle incoming arp packets
+inputs: eth_inp should contain an arp packet
+outputs: 
+   carry flag is set if there was an error processing the arp packet, clear otherwise
+   the arp_cache will be updated with the mac & ip address (whether the inbound 
+   message was a request or a response). if the incoming packet was an arp 
+   request for this machine, then an arp response will be  created (overwriting
+   eth_outp) and sent out

variables

variabledescriptionsize (bytes)
arp_cachecache of IP addresses and corresponding MAC addresses (6+4)*ac_size
arp_ip set arp_ip before calling arp_lookup 4
arp_mac result is delivered here when arp_lookup returns with carry flag clear 6

constants

constantsdescriptionvalue
ac_size lookup cache 8

implementation

; ARP address resolution
+
+.include "../inc/common.i"
+
+  .export arp_init
+  .export arp_lookup
+  .export arp_process
+  .export arp_add 
+  .export arp_calculate_gateway_mask
+  .export arp_ip
+  .export arp_mac
+  .export arp_cache
+  .exportzp ac_size
+  
+  .import eth_inp
+  .import eth_inp_len
+  .import eth_outp
+  .import eth_outp_len
+  .import eth_tx
+  .import eth_set_broadcast_dest
+  .import eth_set_my_mac_src
+  .import eth_set_proto
+  .importzp eth_proto_arp
+  
+  .import cfg_mac
+  .import cfg_ip
+  .import cfg_netmask
+  .import cfg_gateway
+
+  .import timer_read
+  .import timer_timeout
+  
+  .segment "IP65ZP" : zeropage
+
+ap:    .res 2
+
+ARP_TIMEOUT_MS=100
+
+  .bss
+
+; arp state machine
+arp_idle  = 1    ; idling
+arp_wait  = 2    ; waiting for reply
+arp_state:  .res 1    ; current activity
+
+; arguments for lookup and add 
+arp:        ; ptr to mac/ip pair
+arp_mac:  .res 6    ; result is delivered here when arp_lookup returns with carry flag clear  
+arp_ip:    .res 4    ; set arp_ip before calling arp_lookup
+
+; arp cache
+ac_size    = 8    ; lookup cache
+ac_ip    = 6      ; offset for ip
+ac_mac    = 0    ; offset for mac
+arp_cache:  .res (6+4)*ac_size ;cache of IP addresses and corresponding MAC addresses
+
+; offsets for arp packet generation
+ap_hw    = 14    ; hw type (eth = 0001)
+ap_proto  = 16    ; protocol (ip = 0800)
+ap_hwlen  = 18    ; hw addr len (eth = 06)
+ap_protolen  = 19    ; proto addr len (ip = 04)
+ap_op    = 20    ; request = 0001, reply = 0002
+ap_shw    = 22    ; sender hw addr
+ap_sp    = 28    ; sender proto addr
+ap_thw    = 32    ; target hw addr
+ap_tp    = 38    ; target protoaddr
+ap_packlen  = 42    ; total length of packet
+
+; gateway handling
+gw_mask:  .res 4    ; inverted netmask
+gw_test:  .res 4    ; gateway ip or:d with inverted netmask
+gw_last:  .res 1    ; netmask length - 1
+
+; timeout
+arptimeout:  .res 2    ; time when we will have timed out
+
+
+  .code
+;initialize arp (including clearing the arp cache)
+;inputs: none
+;outputs: none
+arp_init:
+  lda #0
+
+  ldx #(6+4)*ac_size - 1  ; clear cache
+:  
+  sta arp_cache,x
+  dex
+  bpl :-
+
+arp_calculate_gateway_mask:
+
+  lda #$ff    ; counter for netmask length - 1
+  sta gw_last
+
+  ldx #3
+@gw:
+  lda cfg_netmask,x
+  eor #$ff
+  cmp #$ff
+  bne :+
+  inc gw_last
+:  sta gw_mask,x
+  ora cfg_gateway,x
+  sta gw_test,x
+  dex
+  bpl @gw
+
+  lda #arp_idle    ; start out idle
+  sta arp_state
+
+  rts
+
+
+;lookup the mac address for an ip
+;inputs: arp_ip should be set to ip address to be resolved
+;outputs: 
+;   if carry flag is clear, then arp_mac will be set to correct mac address
+;   if carry flag is set, then the correct mac address could not be found in
+;   the arp cache, so an arp request was sent. so the caller should wait a while
+;   (to allow time for an arp response message to arrive) and then call arp_lookup again.
+arp_lookup:
+
+  lda arp_ip    ; check for broadcast IP (255.255.255.255)
+  and arp_ip + 1
+  and arp_ip + 2
+  and arp_ip + 3
+  cmp #$ff
+  bne @notbroadcast
+  ldx #6        ;copy ff:ff:ff:ff:ff:ff to ap_mac
+: sta arp_mac,x
+  dex
+  bpl :-
+  clc
+  rts
+  
+@notbroadcast:
+
+  ldx gw_last    ; check if address is on our subnet
+:  lda arp_ip,x
+  ora gw_mask,x
+  cmp gw_test,x
+  bne @notlocal
+  dex
+  bpl :-
+  bmi @local
+
+@notlocal:
+
+  ldx #3      ; copy gateway's ip address
+:  lda cfg_gateway,x
+  sta arp_ip,x
+  dex
+  bpl :-
+
+@local:
+  jsr findip
+  bcs @cachemiss
+
+  ldy #ac_ip - 1    ; copy mac
+:  lda (ap),y
+  sta arp,y
+  dey
+  bpl :-
+  rts
+
+@cachemiss:
+  lda arp_state    ; are we already waiting for a reply?
+  cmp #arp_idle
+  beq @sendrequest  ; yes, send request
+
+  ldax arptimeout    ; check if we've timed out
+  jsr timer_timeout
+  bcs @notimeout    ; no, don't send
+
+@sendrequest:      ; send out arp request
+  jsr eth_set_broadcast_dest
+  jsr eth_set_my_mac_src
+
+  jsr makearppacket  ; add arp, eth, ip, hwlen, protolen
+
+  lda #0      ; set opcode (request = 0001)
+  sta eth_outp + ap_op
+  lda #1
+  sta eth_outp + ap_op + 1
+
+  ldx #5
+:  lda cfg_mac,x    ; set source mac addr
+  sta eth_outp + ap_shw,x
+  lda #0      ; set target mac addr
+  sta eth_outp + ap_thw,x
+  dex
+  bpl :-
+
+  ldx #3
+:  lda cfg_ip,x    ; set source ip addr
+  sta eth_outp + ap_sp,x
+  lda arp_ip,x    ; set target ip addr
+  sta eth_outp + ap_tp,x
+  dex
+  bpl :-
+
+  lda #ap_packlen
+  sta eth_outp_len + 1
+
+  jsr eth_tx    ; send packet
+
+  lda #arp_wait    ; waiting for reply
+  sta arp_state
+
+  jsr timer_read    ; read current timer value
+  clc
+  adc #ARP_TIMEOUT_MS
+  sta arptimeout + 1
+
+@notimeout:
+  sec       ; set carry to indicate that
+  rts      ; no result is availble
+
+
+; find arp_ip in the cache
+; clc returns pointer to entry in (ap)
+findip:
+
+  ldax #arp_cache
+  stax ap  
+  ldx #ac_size
+@compare:      ; compare cache entry
+  ldy #ac_ip
+  lda (ap),y
+  beq @notfound
+:  lda (ap),y
+  cmp arp,y
+  bne @next
+  iny
+  cpy #ac_ip + 4
+  bne :-
+    
+  clc      ; return  
+  rts
+
+@next:        ; next entry
+  lda ap
+  clc
+  adc #10
+  sta ap
+  bcc :+
+  inc ap + 1
+:  dex
+  bne @compare
+
+@notfound:
+  sec
+  rts
+
+
+;handle incoming arp packets
+;inputs: eth_inp should contain an arp packet
+;outputs: 
+;   carry flag is set if there was an error processing the arp packet, clear otherwise
+;   the arp_cache will be updated with the mac & ip address (whether the inbound 
+;   message was a request or a response). if the incoming packet was an arp 
+;   request for this machine, then an arp response will be  created (overwriting
+;   eth_outp) and sent out
+arp_process:
+
+  lda eth_inp + ap_op  ; should be 0
+  bne @badpacket
+  lda eth_inp + ap_op + 1  ; check opcode
+  cmp #1      ; request?
+  beq @request
+  cmp #2      ; reply?
+  beq @reply
+
+@badpacket:
+  sec
+  rts
+
+@request:
+  ldx #3
+:  lda eth_inp + ap_tp,x  ; check if they're asking for
+  cmp cfg_ip,x    ; my address
+  bne @done
+  dex
+  bpl :-
+
+  ldax #eth_inp + ap_shw
+  jsr ac_add_source  ; add them to arp cache
+
+  ldx #5      ; send reply
+:  lda eth_inp + ap_shw,x
+  sta eth_outp,x    ; set sender packet dest
+  sta eth_outp + ap_thw,x  ; and as target
+  lda cfg_mac,x    ; me as source
+  sta eth_outp + ap_shw,x
+  dex
+  bpl :-
+
+  jsr eth_set_my_mac_src  ; me as packet source
+
+  jsr makearppacket  ; add arp, eth, ip, hwlen, protolen
+
+  lda #0      ; set opcode (reply = 0002)
+  sta eth_outp + ap_op
+  lda #2
+  sta eth_outp + ap_op + 1
+
+  ldx #3
+:  lda eth_inp + ap_sp,x  ; sender as target addr
+  sta eth_outp + ap_tp,x
+  lda cfg_ip,x    ; my ip as source addr
+  sta eth_outp + ap_sp,x
+  dex
+  bpl :-
+
+  lda #ap_packlen
+  sta eth_outp_len + 1
+
+  jsr eth_tx    ; send packet
+
+@done:
+  clc
+  rts
+
+@reply:
+  lda arp_state
+  cmp #arp_wait    ; are we waiting for a reply?
+  bne @badpacket
+
+  ldax #eth_inp + ap_shw
+  jsr ac_add_source  ; add to cache
+
+  lda #arp_idle
+  sta arp_state
+  
+  rts
+
+
+; add arp_mac and arp_ip to the cache
+;inputs: 
+;  arp_ip is ip address to add to cache
+;  arp_mac is corresponding mac address of specified ip  
+;outputs: 
+;  arp_cache is updated
+arp_add:
+  jsr findip    ; check if ip is already in cache
+  bcs @add
+
+  ldy #9      ; update old entry
+:  lda arp,y    ; move to top as well?
+  sta (ap),y
+  dey
+  bpl :-
+  
+  rts
+
+@add:
+  ldax #arp    ; add
+
+
+;add source to cache
+ac_add_source:
+  stax ap
+
+  ldx #9      ; make space in the arp cache
+:
+
+  lda arp_cache + 60,x
+  sta arp_cache + 70,x
+  lda arp_cache + 50,x
+  sta arp_cache + 60,x
+  lda arp_cache + 40,x
+  sta arp_cache + 50,x
+  lda arp_cache + 30,x
+  sta arp_cache + 40,x
+  lda arp_cache + 20,x
+  sta arp_cache + 30,x
+  lda arp_cache + 10,x
+  sta arp_cache + 20,x
+  lda arp_cache,x
+  sta arp_cache + 10,x
+
+  dex
+  bpl :-
+
+  ldy #9
+:  lda (ap),y    ; copy source
+  sta arp_cache,y
+  dey
+  bpl :-
+
+  rts
+
+
+; adds proto = arp, hw = eth, and proto = ip to outgoing packet
+makearppacket:
+  lda #eth_proto_arp
+  jsr eth_set_proto
+
+  lda #0      ; set hw type (eth = 0001)
+  sta eth_outp + ap_hw
+  lda #1
+  sta eth_outp + ap_hw + 1
+
+  lda #8      ; set protcol (ip = 0800)
+  sta eth_outp + ap_proto
+  lda #0
+  sta eth_outp + ap_proto + 1
+
+  lda #6      ; set hw addr len (eth = 06)
+  sta eth_outp + ap_hwlen
+  lda #4      ; set proto addr len (eth = 04)
+  sta eth_outp + ap_protolen
+
+  rts
+
+
+
+;-- LICENSE FOR arp.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_cifs_s.html b/docs/ip65_cifs_s.html new file mode 100644 index 0000000..752c25d --- /dev/null +++ b/docs/ip65_cifs_s.html @@ -0,0 +1,940 @@ +

ip65 technical reference

File : ip65/cifs.s

a simple NETBIOS over TCP server
+aka "Common Internet File System"
+
+ refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/
+

functions

functiondescription
cifs_l1_decode
given a 'level 1 encoded' hostname, decode to ASCII .
+
+inputs: 
+ AX: pointer to encoded hostname to be decoded
+outputs:
+ AX: pointer to decoded hostname (will be 16 byte hostname, right padded with spaces, nul terminated)
cifs_l1_encode
given an ASCII (or PETSCII) hostname, convert to
+canonical 'level 1 encoded' form.
+
+only supports the default scope (' ' : 0x20)
+inputs: 
+ AX: pointer to null terminated hostname to be encoded
+outputs:
+ AX: pointer to decoded hostname
cifs_start
start a CIFS (SMB) server process, and advertise the specified hostname on the local LAN
+
+inputs:
+AX = ptr to hostname to be used
+outputs:
+ none

implementation

;a simple NETBIOS over TCP server
+;aka "Common Internet File System"
+;
+; refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/
+
+.include "../inc/common.i"
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+DEFAULT_CIFS_CMD_BUFFER = $6800
+DEFAULT_SMB_RESPONSE_BUFFER=$6000
+.export cifs_l1_encode
+.export cifs_l1_decode
+.export cifs_start
+
+.import copymem
+.importzp copy_src
+.importzp copy_dest
+
+.import cfg_ip
+.import output_buffer
+.importzp udp_data
+.import udp_send
+.import udp_inp
+.importzp udp_data
+.import udp_send_dest
+.import udp_send_src_port
+.import udp_send_dest_port
+.import udp_send_len
+.importzp ip_src
+.importzp ip_dest
+.import ip_data
+.import ip_inp
+.import tcp_close
+.import tcp_listen
+.import tcp_callback
+.import tcp_inbound_data_length
+.import tcp_inbound_data_ptr
+.import tcp_send
+.import tcp_send_data_len
+
+.import ip65_process
+.import udp_add_listener
+.import udp_callback
+
+nbns_txn_id = 0
+nbns_opcode=2
+nbns_flags_rcode=3
+nbns_qdcount=4
+nbns_ancount=6
+nbns_nscount=8
+nbns_arcount=10
+nbns_question_name=12
+nbns_service_type=43
+
+
+nbns_my_ip=64
+nbns_registration_message_length=68
+
+
+;given an ASCII (or PETSCII) hostname, convert to
+;canonical 'level 1 encoded' form.
+;
+;only supports the default scope (' ' : 0x20)
+;inputs: 
+; AX: pointer to null terminated hostname to be encoded
+;outputs:
+; AX: pointer to decoded hostname
+cifs_l1_encode:
+  stax copy_src
+  lda #0
+  tax
+  sta hostname_buffer+32
+@empty_buffer_loop:  
+  lda  #$43
+  sta  hostname_buffer,x
+  inx
+  lda #$41
+  sta  hostname_buffer,x
+  inx
+  cpx  #$20
+  bmi  @empty_buffer_loop
+  ldy  #0
+  ldx  #0
+@copy_loop:
+
+  lda  (copy_src),y
+  beq  @done
+  lsr
+  lsr
+  lsr
+  lsr
+  clc
+  adc #$41
+  sta  hostname_buffer,x
+
+  inx
+  lda  (copy_src),y
+  and #$0F
+  clc
+  adc #$41
+  sta  hostname_buffer,x
+  inx
+  iny
+  cpx  #$1D  
+  bmi  @copy_loop
+@done:  
+  ldax #hostname_buffer
+  rts
+  
+;given a 'level 1 encoded' hostname, decode to ASCII .
+;
+;inputs: 
+; AX: pointer to encoded hostname to be decoded
+;outputs:
+; AX: pointer to decoded hostname (will be 16 byte hostname, right padded with spaces, nul terminated)
+cifs_l1_decode:
+  stax copy_src
+  ldy  #0
+  ldx  #0
+@decode_loop:
+  lda  (copy_src),y
+  sec
+  sbc  #$41
+  asl
+  asl
+  asl
+  asl
+  sta hi_nibble
+  iny
+  lda  (copy_src),y
+  sec
+  sbc  #$41
+  clc
+  adc  hi_nibble
+  sta  hostname_buffer,x
+  iny
+  inx
+  cpx  #$10
+  bmi  @decode_loop
+  lda  #0
+  sta  hostname_buffer,x
+  ldax #hostname_buffer
+  rts
+
+
+;start a CIFS (SMB) server process, and advertise the specified hostname on the local LAN
+;
+;inputs:
+;AX = ptr to hostname to be used
+;outputs:
+; none
+cifs_start:
+  
+  ;save the hostname in 'raw' form
+  stax  copy_src
+  ldax  #raw_local_hostname
+  stax  copy_dest
+  ldax  #$0f
+  stx raw_local_hostname+15
+  jsr copymem
+
+  ;set up callbacks
+  ldax #nbns_callback
+  stax udp_callback
+  ldax  #137  
+  jsr udp_add_listener
+
+  ldax #nbns_callback
+  stax udp_callback
+  ldax  #137  
+  jsr udp_add_listener
+
+  ldax  #raw_local_hostname
+  jsr cifs_l1_encode
+  ldx #0
+@copy_hostname_loop:  
+  lda hostname_buffer,x
+  sta local_hostname,x
+  inx
+  cpx #$21
+  bmi @copy_hostname_loop
+ 
+  jsr cifs_advertise_hostname
+  jsr cifs_advertise_hostname
+
+
+  ldax  #nb_session_callback
+  stax  tcp_callback
+@listen:
+
+  ldax  #-4                 ;start at -4, to skip the NBT header length
+  stax cifs_cmd_length
+  
+  
+  ldax   cifs_cmd_buffer
+  stax  cifs_cmd_buffer_ptr
+  ldax  #139
+  stx connection_closed  
+
+  jsr tcp_listen  
+
+@loop:
+  inc $d020 ;FIXME
+  jsr ip65_process
+  lda connection_closed
+  beq @loop
+  
+  jmp @listen
+  rts
+  
+
+;broadcast a Name Registration Request message to the local LAN
+cifs_advertise_hostname:
+    
+  ; advertise the 'server' service for own hostname
+  ;overwrite the hostname in 'DNS compressed form'
+  ;we assume this hostname ends with <20>
+  lda #$20  ;indicates what follows is a netbios name
+  sta output_buffer+nbns_question_name
+  
+  ldx #0
+@copy_hostname_loop:
+  lda local_hostname,x
+  sta registration_request_servername+1,x
+  inx
+  cpx #$21
+  bmi @copy_hostname_loop  
+  
+  jsr  @send_nbns_message  
+  
+  
+  ;copy our encode hostname to the host announcment
+  ldax #local_hostname
+  stax  copy_src
+  ldax #host_announce_hostname
+  stax  copy_dest
+  ldax #$20
+  jsr copymem
+
+  ;copy our raw hostname to the host announcment
+  ldax #raw_local_hostname
+  stax  copy_src
+  ldax #host_announce_servername
+  stax  copy_dest
+  ldax #$10
+  jsr copymem
+  
+
+;copy the local IP address to the 'sender' field of the host announcment
+  ldx #03
+@copy_sending_address_loop:
+  lda  cfg_ip,x
+  sta host_announce_my_ip,x
+  dex
+  bpl @copy_sending_address_loop
+
+
+  ldax  #138
+  stax  udp_send_dest_port
+  stax  udp_send_src_port
+  
+  ldax  #host_announce_message_length
+  stax  udp_send_len
+  
+  ldax  #host_announce_message
+  jsr udp_send
+  rts
+  
+  
+@send_nbns_message:
+;copy the local IP address
+  ldx #03
+@copy_my_address_loop:
+  lda  cfg_ip,x
+  sta nbns_my_ip,x
+  dex
+  bpl @copy_my_address_loop
+    
+  ;send to the broadcast address
+  lda #$ff
+  ldx #03
+@copy_broadcast_address_loop:
+  sta  udp_send_dest,x
+  dex
+  bpl @copy_broadcast_address_loop
+
+  ldax  #137
+  stax  udp_send_dest_port
+  stax  udp_send_src_port
+  
+  ldax  #nbns_registration_message_length
+  stax  udp_send_len
+  
+  ldax  #registration_request
+  jsr udp_send
+  
+    
+  rts
+        
+        
+        
+nbns_callback: 
+
+  lda udp_inp+udp_data+nbns_opcode  
+  and #$f8    ;mask the lower three bits
+  beq @name_request
+  rts
+@name_request:  
+
+  ;this is a NB NAME REQUEST.
+  ;is it a unicast message?
+  ldx  #3
+@check_unicast_loop:
+  lda  ip_inp+ip_dest,x
+  cmp  cfg_ip,x
+  bne @not_unicast
+  dex
+  bpl @check_unicast_loop
+
+  jmp @looking_for_us
+  
+@not_unicast:
+  ;is it looking for our local hostname?
+  ldax  #udp_inp+udp_data+nbns_question_name+1
+  stax  copy_src
+  ldy #0
+@cmp_loop: 
+  lda (copy_src),y
+  cmp local_hostname,y
+  bne @not_us
+  iny
+  cpy #30
+  bne @cmp_loop
+
+@looking_for_us:
+  ;this is a request for our name!
+  ;copy the txn id
+  ldax udp_inp+udp_data ;first 2 bytes of data are txn id
+  stax  netbios_name_query_response
+  
+ ;set the sender & recipients IP address
+  ldx #03
+@copy_address_loop:
+  lda  ip_inp+ip_src,x
+  sta  udp_send_dest,x
+  lda  cfg_ip,x
+  sta netbios_name_query_response_ip,x
+  dex
+  bpl @copy_address_loop
+  
+  ;copy our encoded hostname
+  ldax #local_hostname
+  stax  copy_src
+  ldax #netbios_name_query_response_hostname
+  stax  copy_dest
+  ldax #30
+  jsr copymem
+
+  ;copy the service identifier - last 2 bytes in the query hostname
+  .import eth_inp
+  ldax  eth_inp+$55
+  stax netbios_name_query_response_hostname+30
+  
+  ldax  #137
+  stax  udp_send_dest_port
+  stax  udp_send_src_port
+  
+  ldax  #netbios_name_query_response_length
+  stax  udp_send_len
+    
+  ldax  #netbios_name_query_response
+  jmp udp_send
+
+  
+@not_us: 
+  rts
+
+
+nb_session_callback:
+
+  lda tcp_inbound_data_length+1
+  cmp #$ff
+  bne @not_eof
+@eof:
+  inc connection_closed
+@done:  
+  rts
+@not_eof:
+  
+;copy this chunk to our input buffer
+  ldax cifs_cmd_buffer_ptr  
+  stax copy_dest
+  ldax tcp_inbound_data_ptr
+  stax copy_src
+  ldax tcp_inbound_data_length
+  jsr copymem
+
+;increment the pointer into the input buffer  
+  clc
+  lda cifs_cmd_buffer_ptr
+  adc tcp_inbound_data_length
+  sta cifs_cmd_buffer_ptr
+  lda cifs_cmd_buffer_ptr+1
+  adc tcp_inbound_data_length+1
+  sta cifs_cmd_buffer_ptr+1  
+  
+;increment the cmd buffer length
+  clc
+  lda cifs_cmd_length
+  adc tcp_inbound_data_length
+  sta cifs_cmd_length  
+  lda cifs_cmd_length+1
+  adc tcp_inbound_data_length+1
+  sta cifs_cmd_length+1  
+
+;have we got a complete message?
+  ldax  cifs_cmd_buffer
+  stax  copy_src
+  ldy #3
+  lda (copy_src),y
+  cmp cifs_cmd_length
+  bne @not_got_full_message
+  dey
+  lda (copy_src),y
+  cmp cifs_cmd_length+1
+  bne @not_got_full_message
+  
+  ;we have a complete message!
+  ldy #0
+  lda (copy_src),y  ;get the message type
+  cmp #$81    ;is it a session request?
+  bne @not_session_request
+  ldax  #positive_session_response_packet_length
+  stax  tcp_send_data_len
+  ldax  #positive_session_response_packet
+  jsr tcp_send
+  
+  jmp @message_handled
+  @not_session_request:
+  cmp #$00    ;is it a session message?  
+  bne @not_session_message
+
+  ;this SHOULD be a SMB - best check the header
+  
+  ldy #4
+  lda (copy_src),y  ;get the message data
+  cmp #$FF    ;start of SMB header
+  bne @not_smb
+  iny
+  lda (copy_src),y  ;get the message data
+  cmp #'S'    ;start of SMB header
+  bne @not_smb
+  
+  jsr smb_handler
+  
+  jmp @message_handled
+  
+  ;this doesn't look like a NBT session message or SMB, so give up
+  @not_session_message:  
+
+  @not_smb:
+  
+   jsr tcp_close
+  jmp @eof
+  
+@message_handled:  
+  ;reset ready for next message on this connection
+  ldax  #-4                 ;start at -4, to skip the NBT header length
+  stax cifs_cmd_length
+  
+  
+  ldax   cifs_cmd_buffer
+  stax  cifs_cmd_buffer_ptr  
+  
+  
+  
+  @not_got_full_message:
+  rts
+  
+
+smb_handler:
+;  at this point, copy_src points to an SMB block encapsulated in an NBT session header
+
+  clc
+  lda  copy_src
+  adc #4  
+  sta  smb_ptr    ;skip past the NBT header  
+  lda  copy_src+1
+  adc #00
+  sta  smb_ptr+1    
+
+  ldy #8
+  lda (copy_src),y  ;get the SMB type
+  cmp #$72
+  bne @not_negotiate_protocol 
+  jmp @negotiate_protocol
+@not_negotiate_protocol:
+  ;we assume it is an "AndX"   command
+  
+  
+  
+  sta andx_opcode
+  lda smb_ptr
+  clc
+  adc #$20     ;skip over SMB header
+  sta andx_ptr
+  
+  lda smb_ptr+1
+  adc #00
+  sta andx_ptr+1
+  
+  jsr start_smb_response
+  
+@parse_andx_block:
+  ldax  andx_ptr
+  stax  copy_src
+  lda andx_opcode
+  
+  cmp #$ff
+  beq @done_all_andx_blocks  
+
+  ldy #0
+@andx_opcode_scan:  
+  lda andx_opcodes,y
+  beq @opcode_not_found
+  cmp andx_opcode
+  beq @opcode_found
+  iny
+  iny
+  iny
+  jmp @andx_opcode_scan
+@opcode_found:
+  lda andx_opcodes+1,y
+  sta andx_handler+1
+  lda andx_opcodes+2,y
+  sta andx_handler+2
+  jsr andx_handler
+  jmp @go_to_next_andx_block
+
+@opcode_not_found:
+  jsr unknown_andx
+  
+@go_to_next_andx_block:  
+  ldy #3
+  lda (copy_src),y  ;get the AndX offset low byte
+  clc
+  adc smb_ptr
+  sta andx_ptr
+  iny
+  lda (copy_src),y  ;get the AndX offset high byte
+  adc smb_ptr+1
+  sta andx_ptr+1
+  ldy #1
+  lda (copy_src),y  ;get the subsequent AndX opcode
+  sta andx_opcode
+  
+  jmp @parse_andx_block
+  
+@done_all_andx_blocks:
+  ldax smb_response_length  
+  inx ;FIXME! this is to force wireshark to dump as SMB even tho packet is incorrect
+  stax tcp_send_data_len
+  ldax smb_response_buffer
+  jsr tcp_send
+
+  rts
+@negotiate_protocol:
+;copy the request TID,PID,UID,MID into the response
+  ldy #28 ;offset of TID from start of message
+  ldx #0
+:  
+  lda (copy_src),y
+  sta negotiate_protocol_response_tid,x
+  inx
+  iny
+  cpx #8
+  bne :-
+  
+
+  lda  #$ff
+  sta dialect_index
+  sta dialect_index+1
+  clc
+  lda cifs_cmd_buffer
+  adc #$26
+  sta copy_src
+  lda cifs_cmd_buffer+1
+  adc #$00
+  sta copy_src+1
+
+  ldy #$0
+@dialect_scan_loop:
+  iny
+  bmi @end_of_dialects ;if we go to offset $80, we have gone too far
+  lda dialect_index
+  cmp #$10    ;if we've scanned more than $10 dialects, something is wrong
+  beq @end_of_dialects
+  lda (copy_src),y
+  cmp #$02
+  bne @test_dialect
+  inc dialect_index
+  jmp @dialect_scan_loop
+@test_dialect:
+  
+  tya
+  clc
+  adc copy_src
+  sta copy_src
+  bcc :+
+  inc copy_src+1
+:
+  ldy #0
+  
+@test_dialect_loop:  
+  lda (copy_src),y
+  cmp preferred_dialect_id,y
+  bne @dialect_scan_loop
+  iny
+  cpy #preferred_dialect_id_length
+  bne @test_dialect_loop
+  inc dialect_index+1
+  jmp @found_preferred_dialect
+    
+@end_of_dialects:
+  lda #$ff
+  sta dialect_index
+    
+ @found_preferred_dialect:
+  
+  ldax #negotiate_protocol_response_message_length
+  stax tcp_send_data_len
+  ldax #negotiate_protocol_response_message
+  jsr tcp_send
+
+  rts
+
+start_smb_response:
+  ldax  smb_response_buffer
+  stax  copy_dest
+  ldy #0
+@copy_header_loop:
+  lda (copy_src),y  ; copy_src should be the SMB request - cloning this will set PID / MID etc
+  sta (copy_dest),y
+  iny
+  cpy #smb_response_header_length
+  bne @copy_header_loop  
+  lda #0
+  sta smb_response_length+1
+  lda #smb_response_header_length
+  sta smb_response_length
+  ldy #3
+  sta (copy_dest),y
+  
+  ;set the flags correctly
+  ldy #smb_response_flags_offset  
+  lda #$82   ;FLAGS byte
+  sta (copy_dest),y
+  iny
+  lda #$01   ;FLAGS2 low byte
+  sta (copy_dest),y
+  iny
+  lda #$00   ;FLAGS2 hi byte
+  sta (copy_dest),y
+  
+  rts
+  
+add_andx_response:
+  rts
+  
+
+.import print_a
+.import print_hex
+session_setup_andx:
+  lda #'S'
+  jsr print_a
+  rts
+  
+tree_connect_andx:
+  lda #'T'
+  jsr print_a
+  rts
+
+unknown_andx:
+  lda andx_opcode
+  jsr print_hex
+  lda #'?'
+  jsr print_a
+  rts
+
+.rodata
+
+andx_opcodes:
+  .byte $73
+  .word session_setup_andx
+  .byte $75
+  .word tree_connect_andx
+  .byte $00
+  
+
+
+.data
+
+andx_handler:
+  jmp $FFFF   ;filled in later
+  
+smb_response_header:
+negotiate_protocol_response_message:
+  .byte $00 ;message type = session message
+  .byte $00,$00,negotiate_protocol_response_message_length-4  ;NBT header
+  .byte $FF,"SMB"  ;SMB header
+  .byte $72   ;command = negotiate protocol
+  .byte $00,$00,$00,$00 ;status = OK
+smb_response_flags_offset  =*-smb_response_header  
+  .byte $82   ;flags : oplocks not supported, paths are case sensitive
+  .byte $01,$00 ;flags 2 - long file names allowed
+  .byte $00, $00 ;PID HIGH
+  .byte $00,$00,$00,$00,$00,$00,$00,$00 ; signature
+  .byte $00, $00 ;reserved
+negotiate_protocol_response_tid:  
+  .byte $00,$00 ;tree ID
+  .byte $98,$76  ;PID - to be overwritten
+  .byte $65,$64 ;USER ID - to be overwritten
+  .byte $ab,$cd ;multiplex ID - to be overwritten
+smb_response_header_length=*-smb_response_header  
+  .byte $11 ;word count 
+dialect_index: .res 2 ;index of selected dialect
+  .byte $00 ;security mode: share, no encryption
+  .byte $01,$00 ;Max MPX count
+  .byte $01,$00 ;Max VCs count
+  .byte $00,$08,$00,$00 ;max buffer size
+  .byte $00,$08,$00,$00 ;max raw size
+  .byte $12,$34,$56,$78 ;session key
+  .byte $00,$00,$00,$00 ;capabilities
+  .byte $00,$00,$00,$00 ;server time low
+  .byte $00,$00,$00,$00 ;server time high
+  .byte $00,$00 ;server GMT offset
+  .byte $00 ;encryption key length
+  .word negotiate_protocol_response_message_data_length ;data length
+negotiate_protocol_response_message_data:
+  .byte "WORKGROUP",0
+  .byte "SERVERNAME",0
+
+negotiate_protocol_response_message_length=*-negotiate_protocol_response_message  
+negotiate_protocol_response_message_data_length=*-negotiate_protocol_response_message_data
+  
+host_announce_message:
+  .byte $11 ;message type = direct group datagram
+  .byte $02 ;no more fragments, this is first fragment, node type = B
+  .byte $ab,$cd  ;txn id
+host_announce_my_ip:
+  .byte $0,0,0,0  ;source IP
+  .byte $0,138    ;source port
+  .byte $00,<(host_announce_message_length-4) ;datagram length
+  .byte $00,$00 ;packet offset
+  .byte $20       ;hostname length
+host_announce_hostname:
+  .res 32           ;hostname
+  .byte $0          ;nul at end of hostname
+  ;now WORKGROUP<1D> encoded
+  
+  .byte $20, $46, $48, $45, $50, $46, $43, $45, $4c, $45, $48, $46, $43, $45, $50, $46
+  .byte $46, $46, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $42, $4E, $00
+  
+  .byte $ff,"SMB" ;Server Message Block header
+  .byte $25 ;SMB command = Transaction
+  .byte $00 ;error class = success
+  .byte $00 ;reserved
+  .byte $00,$00 ;no error
+  .byte $00 ;flags
+  .byte $00,$00 ;flags2
+  .byte $00,$00 ;PID high
+  .byte $00,$00,$00,$00,$00,$00,$00,$00 ;Signature
+  .byte $00,$00 ;reserved
+  .byte $00,$00 ;tree ID
+  .byte $00,$00 ;process ID
+  .byte $00,$00 ;user ID
+  .byte $00,$00 ;multiplex ID
+  .byte $11 ;txn word count
+  .byte $00,$00 ;txn paramater count
+  .byte $21,$00 ;txn total data count
+  .byte $00,$00 ;txn max paramater count
+  .byte $00,$00 ;txn max data count
+  .byte $00 ;txn max setup count
+  .byte $00 ;reserved
+  .byte $00,$00 ;flags
+  .byte $ed,$03,$00,$00 ;timeout = 1 second
+  .byte $00,$00 ;reserved  
+  .byte $00,$00 ;paramater count
+  .byte $00,$00 ;paramater offset
+  .byte $21,$00 ;data count
+  .byte $56,$00 ;data offset
+  .byte $03 ;setup count
+  .byte $00 ;reserved
+  
+  .byte $01,$00 ;opcode = WRITE MAIL SLOT
+  .byte $00,$00 ;priority 0
+  .byte $02,$00 ;class = unreliable & broadcast
+  .byte $32,$00 ;byte count  
+  .byte "\MAILSLOT\BROWSE", 0
+  .byte $01 ;command - HOST ANNOUNCEMENT
+  .byte $0  ;update count 0
+  .byte $80,$fc,03,00 ;update period 
+  host_announce_servername:
+  .res 16
+  .byte $01 ;OS major version
+  .byte $64 ;OS minor version
+  .byte $03,$02,$0,$0 ;advertise as a workstation, server & print host
+  .byte $0F ;browser major version
+  .byte $01 ;browser minor version
+  .byte $55,$aa ;signature
+  .byte $0    ;host comment
+  host_announce_message_length=*-host_announce_message  
+
+
+netbios_name_query_response:
+  .byte $12,$34 ;transaction id
+  .byte $85,$00 ;flags: name query response, no error
+  .byte $00,$00  ;questions = 0
+  .byte $00,$01  ;answers = 1
+  .byte $00,$00  ;authority records = 0
+  .byte $00,$00  ;additional records = 0
+  .byte $20       ;
+netbios_name_query_response_hostname:  
+  .res 30       ;will be replaced with encoded hostname
+  .byte $43,$41   ;
+  .byte $00       ;
+  .byte $00,$20 ; type = NB
+  .byte $00,$01   ;class = IN
+  .byte $00,$00,$01,$40 ; TTL = 64 seconds
+  .byte $00,$06 ;data length
+  .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME
+netbios_name_query_response_ip:
+  .res 4            ;filled in with our IP
+  netbios_name_query_response_length=*-netbios_name_query_response
+  
+registration_request:
+
+  .byte $0c, $64  ;txn ID
+  .byte $29,$10   ;Registration Request opcode & flags
+  .byte $00,$01   ;questions = 1
+  .byte $00,$00   ;answers = 0
+  .byte $00,$00   ;authority records = 0
+  .byte $00,$01   ;additional records = 1
+registration_request_servername:  
+  ;now WORKGROUP<00> encoded
+  .byte $20, $46, $48, $45, $50, $46, $43, $45, $4c, $45, $48, $46, $43, $45, $50, $46
+  .byte $46, $46, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $41, $41, $00
+
+  .byte $00,$20   ;question_type = NB
+  .byte $00,$01   ;question_class = IN
+  .byte $c0,$0c   ;additional record name : ptr to string in QUESTION NAME
+  .byte $00,$20   ;question_type = NB
+  .byte $00,$01   ;question_class = IN
+  .byte $00,$00,$01,$40 ; TTL = 64 seconds
+  .byte $00,$06 ;data length
+  .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME
+
+.rodata
+preferred_dialect_id: .byte "NT LM 0.12"
+preferred_dialect_id_length=*-preferred_dialect_id
+
+positive_session_response_packet:
+  .byte $82     ;packet type = Positive session response
+  .byte $00     ;flags
+  .byte $00, $00  ;message length
+positive_session_response_packet_length=*-positive_session_response_packet
+
+.data 
+
+cifs_cmd_buffer: .word DEFAULT_CIFS_CMD_BUFFER
+
+smb_response_buffer: .word DEFAULT_SMB_RESPONSE_BUFFER
+
+.bss
+hostname_buffer:  .res 33
+  
+  
+local_hostname:    .res 33
+
+raw_local_hostname:  
+  .res 16
+
+hi_nibble: .res 1
+
+connection_closed: .res 1
+
+cifs_cmd_buffer_ptr: .res 2
+cifs_cmd_length: .res 2
+
+andx_opcode: .res 1
+andx_ptr: .res 2          ;pointer to next 'AndX' command
+andx_length: .res 2
+smb_ptr: .res 2
+smb_response_length: .res 2
+
+
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
+
\ No newline at end of file diff --git a/docs/ip65_config_s.html b/docs/ip65_config_s.html new file mode 100644 index 0000000..936a33a --- /dev/null +++ b/docs/ip65_config_s.html @@ -0,0 +1,105 @@ +

ip65 technical reference

File : ip65/config.s

IP configuration defaults
+most of these will be overwritten if dhcp is used for configuration
+

functions

functiondescription
cfg_get_configuration_ptr
return a pointer to where the IP configuration is kept
+this is really only useful for the NB65 API - for anything
+linking directly against ip65, you would just import the
+address of the individual configuration elements, rather
+than use a base pointer+offsets to find each item.
+inputs: none
+outputs: AX = pointer to IP configuration.
cfg_init
copy the IP stack defaults (probably stored in ROM) to the running values in RAM
+inputs: none
+outputs: AX = pointer to IP configuration.

variables

variabledescriptionsize (bytes)
cfg_default_drive1
cfg_dns ip address of dns server to use (will be overwritten if dhcp_init is called) 4;
cfg_gatewayip address of router on local network (will be overwritten if dhcp_init is called) 4
cfg_ipip address of local machine (will be overwritten if dhcp_init is called) 4
cfg_macmac address to be assigned to local machine 6
cfg_netmask netmask of local network (will be overwritten if dhcp_init is called) 4;
cfg_tftp_server ip address of server to send tftp requests to (can be a broadcast address) 4
dhcp_serverwill be set address of dhcp server that configuration was obtained from 4

constants

constantsdescriptionvalue
cfg_mac_defaultmac address to be assigned to local machine $00, $80, $10, $00, $51, $00
cfg_sizecfg_end_defaults-cfg_mac_default+1 +

implementation

;IP configuration defaults
+;most of these will be overwritten if dhcp is used for configuration
+
+.include "../inc/common.i"
+
+.export cfg_mac
+.export cfg_mac_default
+.export cfg_ip
+.export cfg_netmask
+.export cfg_gateway
+.export cfg_dns
+.export cfg_tftp_server
+.export cfg_get_configuration_ptr
+.export cfg_init
+.export cfg_default_drive
+.export cfg_size
+
+.export dhcp_server 
+.import copymem
+.importzp copy_src
+.importzp copy_dest
+.code
+
+;return a pointer to where the IP configuration is kept
+;this is really only useful for the NB65 API - for anything
+;linking directly against ip65, you would just import the
+;address of the individual configuration elements, rather
+;than use a base pointer+offsets to find each item.
+;inputs: none
+;outputs: AX = pointer to IP configuration.
+cfg_get_configuration_ptr:  
+  ldax  #cfg_mac
+  clc
+  rts
+
+;copy the IP stack defaults (probably stored in ROM) to the running values in RAM
+;inputs: none
+;outputs: AX = pointer to IP configuration.
+cfg_init:  
+  ldax  #cfg_mac_default
+  stax copy_src
+  ldax  #cfg_mac
+  stax copy_dest
+  ldax  #cfg_size
+  jmp copymem
+
+.segment "IP65_DEFAULTS"
+cfg_mac_default:  .byte $00, $80, $10, $00, $51, $00  ;mac address to be assigned to local machine
+cfg_ip_default:    .byte 192, 168, 1, 64 ;ip address of local machine (will be overwritten if dhcp_init is called)
+;cfg_ip_default:    .byte 0,0,0,0 ;ip address of local machine (will be overwritten if dhcp_init is called)
+cfg_netmask_default:  .byte 255, 255, 255, 0; netmask of local network (will be overwritten if dhcp_init is called)
+;cfg_gateway_default:  .byte 0, 0, 0, 0 ;ip address of router on local network (will be overwritten if dhcp_init is called)
+cfg_gateway_default:  .byte 192, 168, 1, 1 ;ip address of router on local network (will be overwritten if dhcp_init is called)
+cfg_dns_default:  .byte 0, 0, 0, 0; ip address of dns server to use (will be overwritten if dhcp_init is called)
+dhcp_server_default: .res 4   ;will be set address of dhcp server that configuration was obtained from
+cfg_tftp_server_default: .byte $ff,$ff,$ff,$ff ; ip address of server to send tftp requests to (can be a broadcast address)
+cfg_default_drive_default: .byte 8
+cfg_end_defaults:
+cfg_size=cfg_end_defaults-cfg_mac_default+1
+
+
+.bss
+
+cfg_mac:  .res 6  ;mac address to be assigned to local machine
+cfg_ip:    .res 4 ;ip address of local machine (will be overwritten if dhcp_init is called)
+cfg_netmask:  .res 4; netmask of local network (will be overwritten if dhcp_init is called)
+cfg_gateway:  .res 4 ;ip address of router on local network (will be overwritten if dhcp_init is called)
+cfg_dns:  .res 4; ip address of dns server to use (will be overwritten if dhcp_init is called)
+dhcp_server: .res 4   ;will be set address of dhcp server that configuration was obtained from
+cfg_tftp_server: .res 4 ; ip address of server to send tftp requests to (can be a broadcast address)
+cfg_default_drive: .res 1
+
+
+
+
+;-- LICENSE FOR config.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_copymem_s.html b/docs/ip65_copymem_s.html new file mode 100644 index 0000000..35387dd --- /dev/null +++ b/docs/ip65_copymem_s.html @@ -0,0 +1,84 @@ +

ip65 technical reference

File : ip65/copymem.s

 utility routine to copy memory
+

functions

functiondescription
copymem
copy memory
+inputs:
+ copy_src is address of buffer to copy from
+ copy_dest is address of buffer to copy to
+ AX = number of bytes to copy
+outputs: none

variables

variabledescriptionsize (bytes)
copy_dest destination pointer 2
copy_src source pointer 2

implementation

; utility routine to copy memory
+
+
+  .export copymem
+  .exportzp copy_src
+  .exportzp copy_dest
+
+
+  .segment "IP65ZP" : zeropage
+
+; pointers for copying
+copy_src:  .res 2      ; source pointer
+copy_dest:  .res 2      ; destination pointer
+
+
+  .bss
+
+end:    .res 1
+
+
+  .code
+
+;copy memory
+;inputs:
+; copy_src is address of buffer to copy from
+; copy_dest is address of buffer to copy to
+; AX = number of bytes to copy
+;outputs: none
+copymem:
+  sta end
+  ldy #0
+
+  cpx #0
+  beq @tail
+
+:  lda (copy_src),y
+  sta (copy_dest),y
+  iny
+  bne :-
+  inc copy_src+1    ;next page
+  inc copy_dest+1  ;next page
+  dex
+  bne :-
+
+@tail:
+  lda end
+  beq @done
+
+:  lda (copy_src),y
+  sta (copy_dest),y
+  iny
+  cpy end
+  bne :-
+
+@done:
+  rts
+
+
+
+;-- LICENSE FOR copymem.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_debug_s.html b/docs/ip65_debug_s.html new file mode 100644 index 0000000..c4258a1 --- /dev/null +++ b/docs/ip65_debug_s.html @@ -0,0 +1,188 @@ +

ip65 technical reference

File : ip65/debug.s

routines for dumping debug information
+

functions

functiondescription
console_strout
print a string to the console
+inputs: AX = address of (null terminated) string to print
+outputs: none
dbg_dump_eth_header
prints out the header  of ethernet packet that is ready to be sent;
+ inputs: 
+ eth_outp: pointer to ethernet packet
+ eth_outp_len: length of ethernet packet
+ outputs: none
dbg_dump_ip_header
prints out the header  of ip packet that is ready to be sent;
+ inputs: 
+ eth_outp: pointer to ethernet packet containing an ip packet
+ eth_outp_len: length of ethernet packet
+ outputs: none
dbg_dump_udp_header
prints out the header  of udp packet that is ready to be sent;
+ inputs: 
+ eth_outp: pointer to ethernet packet containing a udp packet
+ eth_outp_len: length of ethernet packet
+ outputs: none
dbgout16
print a 32 bit number as 4 hex digits
+inputs: AX = 32 bit number to print
+outputs: none

constants

constantsdescriptionvalue
console_out$ffd2 +

implementation

;routines for dumping debug information
+
+.include "../inc/common.i"
+.include "../inc/printf.i"
+
+
+  .export dbgout16
+  .export dbg_dump_eth_header
+  .export dbg_dump_ip_header
+  .export dbg_dump_udp_header
+
+  .export console_out
+  .export console_strout
+
+
+  .import eth_outp, eth_outp_len
+  .import ip_outp
+  .import udp_outp
+
+
+  .segment "IP65ZP" : zeropage
+
+cptr:  .res 2
+
+
+  .code
+
+
+;prints out the header  of ethernet packet that is ready to be sent;
+; inputs: 
+; eth_outp: pointer to ethernet packet
+; eth_outp_len: length of ethernet packet
+; outputs: none
+dbg_dump_eth_header:
+  pha
+  txa
+  pha
+  tya
+  pha
+
+  printf "\rethernet header:\r"
+  printf "len: %04x\r", eth_outp_len
+  printf "dest: %04x:%04x:%04x\r", eth_outp, eth_outp + 2, eth_outp + 4
+  printf "src: %04x:%04x:%04x\r", eth_outp + 6, eth_outp + 8, eth_outp + 10
+  printf "type: %04x\r", eth_outp + 12
+
+  pla
+  tay
+  pla
+  tax
+  pla
+  rts
+
+;prints out the header  of ip packet that is ready to be sent;
+; inputs: 
+; eth_outp: pointer to ethernet packet containing an ip packet
+; eth_outp_len: length of ethernet packet
+; outputs: none
+dbg_dump_ip_header:
+  pha
+  txa
+  pha
+  tya
+  pha
+
+  printf "\rip header:\r"
+  printf "ver,ihl,tos: %04x\r", ip_outp
+  printf "len: %04x\r", ip_outp + 2
+  printf "id: %04x\r", ip_outp + 4
+  printf "frag: %04x\r", ip_outp + 6
+  printf "ttl: %02x\r", ip_outp + 8
+  printf "proto: %02x\r", ip_outp + 9
+  printf "cksum: %04x\r", ip_outp + 10
+  printf "src: %04x%04x\r", ip_outp + 12, ip_outp + 14
+  printf "dest: %04x%04x\r", ip_outp + 16, ip_outp + 18
+
+  pla
+  tay
+  pla
+  tax
+  pla
+  rts
+
+;prints out the header  of udp packet that is ready to be sent;
+; inputs: 
+; eth_outp: pointer to ethernet packet containing a udp packet
+; eth_outp_len: length of ethernet packet
+; outputs: none
+dbg_dump_udp_header:
+  pha
+  txa
+  pha
+  tya
+  pha
+
+  printf "\rudp header:\r"
+  printf "srcport: %04x\r", ip_outp
+  printf "destport: %04x\r", ip_outp + 2
+  printf "len: %04x\r", ip_outp + 4
+  printf "cksum: %04x\r", ip_outp + 6
+
+  pla
+  tay
+  pla
+  tax
+  pla
+  rts
+
+
+console_out  = $ffd2
+
+;print a string to the console
+;inputs: AX = address of (null terminated) string to print
+;outputs: none
+console_strout:
+  stax cptr
+
+  pha
+  txa
+  pha
+  tya
+  pha
+  ldy #0
+:  lda (cptr),y
+  beq @done
+  jsr console_out
+  iny
+  bne :-
+@done:
+  pla
+  tay
+  pla
+  tax
+  pla
+  rts
+
+;print a 32 bit number as 4 hex digits
+;inputs: AX = 32 bit number to print
+;outputs: none
+dbgout16:
+  stax val16  
+  printf "%04x", val16
+  rts
+
+
+  .bss
+
+val16:  .res 2
+
+
+
+;-- LICENSE FOR debug.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_dhcp_s.html b/docs/ip65_dhcp_s.html new file mode 100644 index 0000000..00a06bb --- /dev/null +++ b/docs/ip65_dhcp_s.html @@ -0,0 +1,530 @@ +

ip65 technical reference

File : ip65/dhcp.s

 minimal dhcp implementation - ip addresses are requested from a dhcp server
+ (aka 'leased') but are not renewed or released. although this is not correct 
+ behaviour according to the DHCP RFC, this works fine in practice in a typical
+ home network environment.
+ 
+ cfg_ip,cfg_netmask,cfg_gateway and cfg_dns variables are all overwritten,
+ therefore, these values must be stored in RAM not ROM
+ 
+

functions

functiondescription
dhcp_init
+inputs: none (although ip65_init should be called first)
+outputs:
+ carry flag clear means IP config has been sucesfully obtained and
+ cfg_ip, cfg_netmask, cfg_gateway and cfg_dns will be set per response from dhcp server.
+ dhcp_server will be set to address of server that provided configuration
+ if carry flag is set there was an error.
+ in either case, dhcp_state will indicate where dhcp initialization ended (to help debug)
+ possible values for dhcp_state are:
+     1	- initial state
+     2	- sent a DHCPDISCOVER, waiting for a DHCPOFFER
+     3 - got a DHCPOFFER, ready to send a DHCPREQUEST
+     4 - sent a DHCPREQUEST, waiting for a DHCPACK
+     5 - we have been allocated an IP address

variables

variabledescriptionsize (bytes)
dhcp_stateflag indicating current state of dhcp initialization. 1

implementation

; minimal dhcp implementation - ip addresses are requested from a dhcp server
+; (aka 'leased') but are not renewed or released. although this is not correct 
+; behaviour according to the DHCP RFC, this works fine in practice in a typical
+; home network environment.
+; 
+; cfg_ip,cfg_netmask,cfg_gateway and cfg_dns variables are all overwritten,
+; therefore, these values must be stored in RAM not ROM
+; 
+
+MAX_DHCP_MESSAGES_SENT=12     ;timeout after sending 12 messages will be about 15 seconds (1+2+3...)/4
+
+  .include "../inc/common.i"
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+  
+  .export dhcp_init
+  .import dhcp_server 
+  .export dhcp_state  
+  
+  .import ip65_error
+  .import cfg_mac
+  .import cfg_ip
+  .import cfg_netmask
+  .import cfg_gateway
+  .import cfg_dns
+
+  .import arp_calculate_gateway_mask
+
+  .import ip65_process
+
+  .import udp_add_listener
+  .import udp_remove_listener
+
+  .import udp_callback
+  .import udp_send
+
+  .import udp_inp
+
+  .importzp udp_data
+  
+  .import output_buffer
+  
+  .import udp_send_dest
+  .import udp_send_src_port
+  .import udp_send_dest_port
+  .import udp_send_len
+  .import check_for_abort_key
+  .import timer_read
+  
+  .bss
+
+; dhcp packet offsets
+dhcp_inp    = udp_inp + udp_data
+
+dhcp_op = 0
+dhcp_htype = 1
+dhcp_hlen = 2
+dhcp_hops = 3
+dhcp_xid = 4
+dhcp_secs = 8
+dhcp_flags = 10
+dhcp_ciaddr = 12
+dhcp_yiaddr = 16
+dhcp_siaddr = 20
+dhcp_giaddr = 24
+dhcp_chaddr =28
+dhcp_sname = 44
+dhcp_file=108
+dhcp_cookie=236
+dhcp_options=240
+
+dhcp_server_port=67
+dhcp_client_port=68
+
+
+; dhcp state machine
+dhcp_initializing  = 1            ; initial state
+dhcp_selecting  = 2    ; sent a DHCPDISCOVER, waiting for a DHCPOFFER
+dhcp_ready_to_request = 3 ; got a DHCPOFFER, ready to send a DHCPREQUEST
+dhcp_requesting = 4   ; sent a DHCPREQUEST, waiting for a DHCPACK
+dhcp_bound = 5         ; we have been allocated an IP address
+
+;flag indicating current state of dhcp initialization.
+dhcp_state:  .res 1    
+
+dhcp_message_sent_count: .res 1
+dhcp_timer:  .res 1
+dhcp_loop_count: .res 1
+dhcp_break_polling_loop: .res 1
+
+
+
+;DHCP constants
+BOOTREQUEST   =1
+BOOTREPLY     =2
+
+DHCPDISCOVER  =1 
+DHCPOFFER     =2 
+DHCPREQUEST   =3
+DHCPDECLINE   =4
+DHCPACK       =5
+DHCPNAK       =6
+DHCPRELEASE   =7
+DHCPINFORM    =8
+
+
+  .code
+;
+;inputs: none (although ip65_init should be called first)
+;outputs:
+; carry flag clear means IP config has been sucesfully obtained and
+; cfg_ip, cfg_netmask, cfg_gateway and cfg_dns will be set per response from dhcp server.
+; dhcp_server will be set to address of server that provided configuration
+; if carry flag is set there was an error.
+; in either case, dhcp_state will indicate where dhcp initialization ended (to help debug)
+; possible values for dhcp_state are:
+;     1  - initial state
+;     2  - sent a DHCPDISCOVER, waiting for a DHCPOFFER
+;     3 - got a DHCPOFFER, ready to send a DHCPREQUEST
+;     4 - sent a DHCPREQUEST, waiting for a DHCPACK
+;     5 - we have been allocated an IP address
+dhcp_init:
+
+  ldx #3        ; rewrite ip address
+  lda #0
+:  sta cfg_ip,x
+  dex
+  bpl :-
+  
+  lda #dhcp_initializing
+  sta dhcp_state
+  
+  ldax #dhcp_in
+  stax udp_callback
+  ldax #dhcp_client_port
+  jsr udp_add_listener
+  bcc :+
+  rts
+:
+  lda #0  ;reset the "message sent" counter
+  sta dhcp_message_sent_count
+  jsr send_dhcpdiscover
+  
+@dhcp_polling_loop:
+
+  lda dhcp_message_sent_count
+  adc #1
+  sta dhcp_loop_count       ;we wait a bit longer between each resend  
+  
+@outer_delay_loop: 
+  lda #0
+  sta dhcp_break_polling_loop
+  jsr timer_read
+  stx dhcp_timer            ;we only care about the high byte  
+
+@inner_delay_loop:  
+  jsr ip65_process
+  jsr check_for_abort_key
+  bcc @no_abort
+  lda #KPR_ERROR_ABORTED_BY_USER
+  sta ip65_error
+  rts
+@no_abort:  
+  lda #0
+  cmp dhcp_break_polling_loop
+  bne @break_polling_loop
+  jsr timer_read
+  cpx dhcp_timer            ;this will tick over after about 1/4 of a second
+  beq @inner_delay_loop
+  
+  dec dhcp_loop_count
+  bne @outer_delay_loop  
+
+@break_polling_loop:
+  
+  inc dhcp_message_sent_count
+  lda dhcp_message_sent_count
+  cmp #MAX_DHCP_MESSAGES_SENT-1
+  bpl @too_many_messages_sent
+  lda dhcp_state
+  cmp #dhcp_initializing
+  beq @initializing
+  cmp #dhcp_selecting
+  beq @selecting
+  cmp #dhcp_ready_to_request
+  beq @ready_to_request
+  cmp #dhcp_bound  
+  beq @bound
+  jmp @dhcp_polling_loop
+@initializing:
+@selecting:
+  jsr send_dhcpdiscover
+  jmp @dhcp_polling_loop
+
+@ready_to_request:
+  jsr send_dhcprequest
+  jmp @dhcp_polling_loop
+  
+@bound:
+  ldax #dhcp_client_port
+  jsr udp_remove_listener  
+  rts
+
+@too_many_messages_sent:
+  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
+  sta ip65_error
+  jsr @bound   ;to remove the listener ( thanks to ShadowM for bug report)
+  sec             ;signal an error
+  rts
+  
+dhcp_create_request_msg:
+  lda #BOOTREQUEST  
+  sta output_buffer+dhcp_op
+  lda #1                                ;htype 1 = "10 MB ethernet"
+  sta output_buffer+dhcp_htype
+  lda #6                                ;ethernet MACs are 6 bytes
+  sta output_buffer+dhcp_hlen
+  lda #0                                ;hops = 0
+  sta output_buffer+dhcp_hops
+  ldx #3                                ;set xid to be "1234"
+  clc
+: txa
+  adc #01
+  sta output_buffer+dhcp_xid,x
+  dex
+  bpl :-
+  
+  lda #0                              ;secs =00
+  sta output_buffer+dhcp_secs
+  sta output_buffer+dhcp_secs+1
+                                        ;initially turn off all flags 
+  sta output_buffer+dhcp_flags  
+  sta output_buffer+dhcp_flags+1
+  
+  ldx #$0F                          ;set ciaddr to 0.0.0.0
+                                    ;set yiaddr to 0.0.0.0
+                                    ;set siaddr to 0.0.0.0
+                                    ;set giaddr to 0.0.0.0
+: sta output_buffer+dhcp_ciaddr,x
+  dex
+  bpl :-
+  
+  ldx #5                          ;set chaddr to mac  
+:  lda cfg_mac,x
+  sta output_buffer+dhcp_chaddr,x
+  dex
+  bpl :-
+
+  ldx #192                          ;set sname & file both to null
+  lda #0
+:  sta output_buffer+dhcp_sname-1,x
+  dex
+  bne :-
+  
+        
+  lda #$63                      ;copy the magic cookie
+  sta output_buffer+dhcp_cookie+0
+  lda #$82
+  sta output_buffer+dhcp_cookie+1
+  lda #$53
+  sta output_buffer+dhcp_cookie+2
+  lda #$63
+  sta output_buffer+dhcp_cookie+3
+  
+  ldax #dhcp_client_port      ; set source port
+  stax udp_send_src_port
+
+  ldax #dhcp_server_port      ; set destination port
+  stax udp_send_dest_port
+
+  rts
+
+send_dhcpdiscover:
+  lda #dhcp_initializing
+  sta dhcp_state
+
+  jsr dhcp_create_request_msg
+
+  lda #$80                           ;broadcast flag =1, all other bits 0
+  sta output_buffer+dhcp_flags
+  
+
+  ldx #dhcp_discover_options_length        ; set destination address  
+:
+  lda dhcp_discover_options,x
+  sta output_buffer+dhcp_options,x
+  dex
+  bpl :-
+
+  ldx #3        ; set destination address
+  lda #$FF      ; des = 255.255.255.255 (broadcast)
+:  sta udp_send_dest,x
+  dex
+  bpl :-
+
+  ldax #dhcp_options+dhcp_discover_options_length
+  stax udp_send_len
+  ldax #output_buffer
+  jsr udp_send
+  bcc :+
+  rts
+  
+: lda #dhcp_selecting
+  sta dhcp_state
+  rts
+
+dhcp_discover_options:
+.byte 53 ;option 53 - DHCP message type
+.byte  1 ;option length =1 
+.byte DHCPDISCOVER ; message type
+.byte 55 ; option 55 - Parameter Request List
+.byte 3  ;option length
+.byte 1 ; subnet mask
+.byte 3 ; router (gateway)
+.byte 6 ; DNS server
+.byte $FF ; end of options
+
+dhcp_discover_options_length=*-dhcp_discover_options
+
+;got a message on port 68
+dhcp_in:
+    
+  lda dhcp_inp+dhcp_op  
+  cmp #BOOTREPLY
+  beq :+
+  rts                       ;it's not what we were expecting  
+:    
+
+  lda #0
+  cmp dhcp_inp+dhcp_yiaddr  ;is the first byte in the assigned address 0?
+  bne :+
+  rts                                 ;if so, it's a bogus response - ignore
+:  
+  ldx #4                          ;copy the our new IP address
+:  
+  lda dhcp_inp+dhcp_yiaddr,x
+  sta cfg_ip,x
+  dex
+  bpl :-
+
+  ldx #0
+@unpack_dhcp_options:
+  lda dhcp_inp+dhcp_options,x
+  cmp #$ff
+  bne :+
+  jmp @finished_unpacking_dhcp_options
+:  
+  cmp #53                 ;is this field DHCP message type?  
+  bne @not_dhcp_message_type
+  jmp @get_next_option
+  lda dhcp_inp+dhcp_options+2,x
+  cmp #DHCPOFFER          ;if it's not a DHCP OFFER message, then stop processing
+  beq :+
+  rts   
+: jmp @get_next_option 
+
+@not_dhcp_message_type:
+  
+  cmp #1                ;option 1 is netmask
+  bne @not_netmask
+  lda dhcp_inp+dhcp_options+2,x
+  sta cfg_netmask
+  lda dhcp_inp+dhcp_options+3,x
+  sta cfg_netmask+1
+  lda dhcp_inp+dhcp_options+4,x
+  sta cfg_netmask+2
+  lda dhcp_inp+dhcp_options+5,x
+  sta cfg_netmask+3  
+  jmp @get_next_option 
+  
+@not_netmask:
+
+  cmp #3               ;option 3 is gateway
+  bne @not_gateway
+  lda dhcp_inp+dhcp_options+2,x
+  sta cfg_gateway
+  lda dhcp_inp+dhcp_options+3,x
+  sta cfg_gateway+1
+  lda dhcp_inp+dhcp_options+4,x
+  sta cfg_gateway+2
+  lda dhcp_inp+dhcp_options+5,x
+  sta cfg_gateway+3
+  jmp @get_next_option 
+  
+@not_gateway:
+
+  cmp #6               ;option 6 is dns server
+  bne @not_dns_server
+  lda dhcp_inp+dhcp_options+2,x
+  sta cfg_dns
+  lda dhcp_inp+dhcp_options+3,x
+  sta cfg_dns+1
+  lda dhcp_inp+dhcp_options+4,x
+  sta cfg_dns+2
+  lda dhcp_inp+dhcp_options+5,x
+  sta cfg_dns+3
+  jmp @get_next_option 
+  
+@not_dns_server:
+
+  cmp #54               ;option 54 is DHCP server
+  bne @not_server
+  lda dhcp_inp+dhcp_options+2,x
+  sta dhcp_server
+  lda dhcp_inp+dhcp_options+3,x
+  sta dhcp_server+1
+  lda dhcp_inp+dhcp_options+4,x
+  sta dhcp_server+2
+  lda dhcp_inp+dhcp_options+5,x
+  sta dhcp_server+3
+  jmp @get_next_option 
+  
+@not_server:
+
+
+@get_next_option:
+  txa
+  clc
+  adc #02
+  adc dhcp_inp+dhcp_options+1,x
+  bcs @finished_unpacking_dhcp_options   ; if we overflow, then we're done
+  tax
+  jmp @unpack_dhcp_options
+
+
+@finished_unpacking_dhcp_options:
+  jsr arp_calculate_gateway_mask                ;we have modified our netmask, so we need to recalculate gw_test
+  lda dhcp_state
+  cmp #dhcp_bound
+  beq :+
+  lda #dhcp_ready_to_request
+  sta dhcp_state
+: 
+  lda #1
+  sta dhcp_break_polling_loop
+  
+  rts
+
+send_dhcprequest:
+  jsr dhcp_create_request_msg
+  lda #53                           ;option 53 - DHCP message type
+  sta output_buffer+dhcp_options+0
+  lda #1                            ;option length is 1  
+  sta output_buffer+dhcp_options+1
+  lda #DHCPREQUEST
+  sta output_buffer+dhcp_options+2
+
+  lda #50                           ;option 50 - requested IP address
+  sta output_buffer+dhcp_options+3
+  ldx #4                               ;option length is 4  
+  stx output_buffer+dhcp_options+4
+  dex
+: lda cfg_ip,x
+  sta output_buffer+dhcp_options+5,x
+  dex
+  bpl :-
+
+  lda #54                           ;option 54 - DHCP server
+  sta output_buffer+dhcp_options+9
+  ldx #4                            ;option length is 4  
+  stx output_buffer+dhcp_options+10
+  dex
+
+: lda dhcp_server,x
+  sta output_buffer+dhcp_options+11,x
+  lda #$ff ;bugfix by ShadowM - DHCP request should be broadcast
+  sta udp_send_dest,x
+  dex
+  bpl :-
+
+  ;A still = option FF = end of options 
+  
+  sta output_buffer+dhcp_options+15
+
+  ldax #dhcp_options+16
+  stax udp_send_len
+
+  ldax #output_buffer
+  jsr udp_send
+  bcs :+            ;if we didn't send the message we probably need to wait for an ARP reply to come back.
+  lda #dhcp_bound   ;technically, we should wait till we get a DHCPACK message. but we'll assume success
+  sta dhcp_state  
+  rts
+:  
+  rts
+
+
+
+;-- LICENSE FOR dhcp.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_dns_s.html b/docs/ip65_dns_s.html new file mode 100644 index 0000000..93ce6a5 --- /dev/null +++ b/docs/ip65_dns_s.html @@ -0,0 +1,499 @@ +

ip65 technical reference

File : ip65/dns.s

 minimal dns implementation - requires a DNS server that supports recursion
+

functions

functiondescription
dns_resolve
 resolve a string containing a hostname (or a dotted quad) to an ip address
+ inputs:
+   cfg_dns must point to a DNS server that supports recursion
+   dns_set_hostname must have been called to load the string to be resolved
+ outputs: 
+   carry flag is set if there was an error, clear otherwise 
+   dns_ip: set to the  ip address of the hostname (if no error)
dns_set_hostname
 sets up for resolution of a hostname to an ip address
+ inputs:
+   AX = pointer to null terminated string that contains either a dns hostname
+     (e.g. "host.example.com",0) or an address in "dotted quad" format,
+     (e.g. "192.168.1.0",0)
+ outputs: 
+   carry flag is set on error (i.e. hostname too long), clear otherwise

variables

variabledescriptionsize (bytes)
dns_ipwill be contain ip address of hostname after succesful exection of dns_resolve 4
dns_status for debugging purposes only (behaviour not garuanteed) 2

implementation

; minimal dns implementation - requires a DNS server that supports recursion
+
+  MAX_DNS_MESSAGES_SENT=8     ;timeout after sending 8 messages will be about 7 seconds (1+2+3+4+5+6+7+8)/4
+
+.include "../inc/common.i"
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+  .export dns_set_hostname
+  .export dns_resolve
+  .export dns_ip
+  .export dns_status
+  .import ip65_error
+  .import cfg_dns
+  
+  .import parse_dotted_quad
+  .import dotted_quad_value
+  
+  .import ip65_process
+  
+  .import udp_add_listener
+  .import udp_remove_listener
+
+  .import udp_callback
+  .import udp_send
+
+  .import udp_inp
+  .import output_buffer
+  .importzp udp_data
+
+  .import udp_send_dest
+  .import udp_send_src_port
+  .import udp_send_dest_port
+  .import udp_send_len
+  .import check_for_abort_key
+  .import timer_read
+  
+  .segment "IP65ZP" : zeropage
+
+  dns_hostname: .res 2
+  
+  .bss
+
+; dns packet offsets
+dns_inp    = udp_inp + udp_data
+dns_id  = 0
+dns_flags=2
+dns_qdcount=4
+dns_ancount=6
+dns_nscount=8
+dns_arcount=10
+dns_qname=12
+
+dns_server_port=53
+dns_client_port_low_byte: .res 1
+
+dns_ip: .res 4  ;will be contain ip address of hostname after succesful exection of dns_resolve
+
+dns_msg_id: .res 2
+
+dns_current_label_length: .res 1
+dns_current_label_offset: .res 1
+
+dns_message_sent_count: .res 1
+
+dns_packed_hostname: .res 128
+
+; dns state machine
+dns_initializing  = 1        ; initial state
+dns_query_sent  = 2      ; sent a query, waiting for a response
+dns_complete = 3        ; got a good response
+dns_failed = 4        ; got either a 'no such name' or 'recursion declined' response
+
+dns_state:  .res 1    ; flag indicating the current stage in the dns resolution process
+dns_timer:  .res 1
+dns_loop_count: .res 1
+dns_break_polling_loop: .res 1
+
+dns_status: .res 2  ; for debugging purposes only (behaviour not garuanteed)
+
+hostname_copied:  .res 1
+
+questions_in_response: .res 1
+
+hostname_was_dotted_quad: .res 1
+
+  .code
+  
+; sets up for resolution of a hostname to an ip address
+; inputs:
+;   AX = pointer to null terminated string that contains either a dns hostname
+;     (e.g. "host.example.com",0) or an address in "dotted quad" format,
+;     (e.g. "192.168.1.0",0)
+; outputs: 
+;   carry flag is set on error (i.e. hostname too long), clear otherwise
+dns_set_hostname:  
+  stax  dns_hostname
+                                      ;copy the hostname into  a buffer suitable to copy directly into the qname field
+                                      ;we need to split on dots
+
+  jsr parse_dotted_quad      ; if we are passed an IP address instead of a hostname, don't bother looking it up in dns
+  bcs @wasnt_dotted_quad
+                             ;if the string was a dotted quad, then copy the parsed 4 bytes in to dns_ip
+  lda #1
+  sta hostname_was_dotted_quad
+  ldx #3        ; set destination address
+: lda dotted_quad_value,x
+  sta dns_ip,x
+  dex
+  bpl :-
+  
+  rts     ;done!
+  
+@wasnt_dotted_quad:
+  
+  
+  ldy #0                            ;input pointer
+  ldx #1                            ;output pointer (start at 1, to skip first length offset, which will be filled in later)
+  
+  sty hostname_was_dotted_quad
+  sty dns_current_label_length
+  sty dns_current_label_offset
+  sty hostname_copied
+  
+@next_hostname_byte:  
+  lda (dns_hostname),y          ;get next char in hostname
+  beq @end_of_hostname
+  cmp #'/'                           ; allow hostnames to be terminated by "/" or ":" to help with URL parsing
+  beq @end_of_hostname
+  cmp #':'
+  bne :+
+@end_of_hostname:  
+  inc hostname_copied
+  bne @set_length_of_last_label
+:
+
+  cmp #'.'                         ;do we need to split the labels?
+  bne @not_a_dot
+@set_length_of_last_label:  
+  txa
+  pha 
+  lda dns_current_label_length  
+  ldx dns_current_label_offset
+  sta  dns_packed_hostname,x
+  lda #0
+  sta dns_current_label_length
+  pla
+  tax
+  stx dns_current_label_offset
+  lda hostname_copied
+  beq @update_counters 
+  jmp @hostname_done
+@not_a_dot:
+  sta dns_packed_hostname,x
+  inc dns_current_label_length
+  
+@update_counters:  
+  iny  
+  inx
+  bmi @hostname_too_long    ;don't allow a hostname of more than 128 bytes
+  jmp @next_hostname_byte
+
+@hostname_done:
+  
+  lda dns_packed_hostname-1,x    ;get the last byte we wrote out
+  beq :+                            ;was it a zero?
+  lda #0
+  sta dns_packed_hostname,x      ;write a trailing zero (i.e. a zero length label)  
+  inx
+:    
+  clc   ;no error
+  
+  rts
+
+@hostname_too_long:
+  lda #KPR_ERROR_INPUT_TOO_LARGE
+  sta ip65_error
+  sec
+  rts
+
+; resolve a string containing a hostname (or a dotted quad) to an ip address
+; inputs:
+;   cfg_dns must point to a DNS server that supports recursion
+;   dns_set_hostname must have been called to load the string to be resolved
+; outputs: 
+;   carry flag is set if there was an error, clear otherwise 
+;   dns_ip: set to the  ip address of the hostname (if no error)
+dns_resolve:  
+  lda hostname_was_dotted_quad
+  beq @hostname_not_dotted_quad
+  clc
+  rts     ;we already set dns_ip when copying the hostname
+@hostname_not_dotted_quad:
+  ldax #dns_in
+  stax udp_callback 
+  lda #53
+  inc dns_client_port_low_byte    ;each call to resolve uses a different client address
+  ldx dns_client_port_low_byte    ;so we don't get confused by late replies to a previous call
+  jsr udp_add_listener
+  
+  bcc :+
+  rts
+:
+  
+  lda #dns_initializing
+  sta dns_state
+  lda #0  ;reset the "message sent" counter
+  sta dns_message_sent_count
+  
+  jsr send_dns_query
+  
+@dns_polling_loop:
+  lda dns_message_sent_count
+  adc #1
+  sta dns_loop_count       ;we wait a bit longer between each resend  
+@outer_delay_loop: 
+  lda #0
+  sta dns_break_polling_loop
+  jsr timer_read
+  stx dns_timer            ;we only care about the high byte  
+  
+@inner_delay_loop:  
+  jsr ip65_process
+  jsr check_for_abort_key
+  bcc @no_abort
+  lda #KPR_ERROR_ABORTED_BY_USER
+  sta ip65_error
+  rts
+@no_abort:  
+  
+  lda dns_state
+  cmp #dns_complete
+  beq @complete
+  cmp #dns_failed
+  beq @failed
+   
+  lda #0
+  cmp dns_break_polling_loop
+  bne @break_polling_loop
+  jsr timer_read
+  cpx dns_timer            ;this will tick over after about 1/4 of a second
+  beq @inner_delay_loop
+  
+  dec dns_loop_count
+  bne @outer_delay_loop  
+
+@break_polling_loop:
+  jsr send_dns_query  
+  inc dns_message_sent_count
+  lda dns_message_sent_count
+  cmp #MAX_DNS_MESSAGES_SENT-1
+  bpl @too_many_messages_sent
+  jmp @dns_polling_loop
+  
+@complete:
+
+  lda #53
+  ldx dns_client_port_low_byte    
+  jsr udp_remove_listener  
+  rts
+
+@too_many_messages_sent:
+@failed:
+  lda #53
+  ldx dns_client_port_low_byte    
+  jsr udp_remove_listener
+  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
+  sta ip65_error  
+  sec             ;signal an error
+  rts
+
+send_dns_query:  
+  ldax  dns_msg_id
+  inx
+  adc #0
+  stax  dns_msg_id
+  stax  output_buffer+dns_id
+
+  ldax #$0001          ;QR =0 (query), opcode=0 (query), AA=0, TC=0,RD=1,RA=0,Z=0,RCODE=0
+  stax output_buffer+dns_flags
+  ldax #$0100          ;we ask 1 question 
+  stax output_buffer+dns_qdcount
+  ldax #$0000                 
+  stax output_buffer+dns_ancount     ;we send no answers
+  stax output_buffer+dns_nscount     ;we send no name servers
+  stax output_buffer+dns_arcount     ;we send no authorative records
+  
+  ldx #0
+:  
+  lda dns_packed_hostname,x
+  sta output_buffer+dns_qname,x
+  inx
+  bpl  @hostname_still_ok
+  lda #KPR_ERROR_INPUT_TOO_LARGE
+  sta ip65_error
+  jmp @error_on_send                ;if we got past 128 bytes, there's a problem
+@hostname_still_ok:  
+  cmp #0
+  bne :-                                  ;keep looping until we have a zero byte.
+  
+  lda #0
+  sta output_buffer+dns_qname,x        ;high byte of QTYPE=1 (A)
+  sta output_buffer+dns_qname+2,x     ;high byte of QLASS=1 (IN)
+  lda #1
+  sta output_buffer+dns_qname+1,x     ;low byte of QTYPE=1 (A)
+  sta output_buffer+dns_qname+3,x     ;low byte of QLASS=1 (IN)
+  
+  txa
+  clc
+  adc #(dns_qname+4)
+  ldx #0
+  stax udp_send_len
+  
+  lda #53
+  ldx dns_client_port_low_byte    
+  stax udp_send_src_port
+
+  ldx #3        ; set destination address
+: lda cfg_dns,x
+  sta udp_send_dest,x
+  dex
+  bpl :-
+
+  ldax #dns_server_port      ; set destination port
+  stax udp_send_dest_port
+  ldax #output_buffer
+  jsr udp_send  
+  bcs @error_on_send
+  lda #dns_query_sent
+  sta dns_state
+
+  rts
+@error_on_send:  
+  sec
+  rts
+  
+dns_in:
+  lda dns_inp+dns_flags+1 ;
+  and #$0f   ;get the RCODE
+  cmp #0    
+  beq @not_an_error_response
+  
+  sta dns_status      ;anything non-zero is a permanent error (invalid domain, server doesn't support recursion etc)
+  sta dns_status+1
+  lda #dns_failed
+  sta dns_state
+  rts
+@not_an_error_response:
+  lda dns_inp+dns_qdcount+1
+  sta questions_in_response
+  cmp #1                          ;should be exactly 1 Q in the response (i.e. the one we sent)  
+  beq :+
+  jmp @error_in_response
+:  
+  lda dns_inp+dns_ancount+1
+  bne :+
+  jmp @error_in_response    ;should be at least 1 answer in response  
+:                                      ;we need to skip over the question (we will assume it's the question we just asked)
+  ldx #dns_qname              
+:  
+  lda dns_inp,x     ;get next length byte in question
+  beq :+                            ; we're done if length==0
+  clc
+  txa
+  
+  adc dns_inp,x ;add length of next label to ptr
+  adc #1                          ;+1 for the length byte itself
+  tax
+  bcs @error_in_response  ;if we overflowed x, then message is too big
+  bcc :-
+: 
+  inx                               ;skip past the nul byte
+  lda dns_inp+1,x
+  cmp #1                          ;QTYPE should 1
+  lda dns_inp+3,x
+  cmp #1                          ;QCLASS should 1
+  bne @error_in_response  
+          
+  inx                                 ;skip past the QTYPE/QCLASS
+  inx
+  inx
+  inx
+
+                                      ;x now points to the start of the answers
+                                      
+  lda  dns_inp,x
+  bpl @error_in_response    ;we are expecting the high bit to be set (we assume the server will send us back the answer to the question we just asked)
+  inx                               ;skip past the compression
+  inx
+                                    ;we are now pointing at the TYPE field
+  lda  dns_inp+1,x            ;
+    
+  cmp #5                        ; is this a CNAME?
+  bne @not_a_cname
+  
+  
+  txa
+  clc
+  adc #10                       ;skip 2 bytes TYPE, 2 bytes CLASS, 4 bytes TTL, 2 bytes RDLENGTH
+  tax
+                                    ;we're now pointing at the CNAME record
+  ldy #0                         ;start of CNAME hostname
+:
+  lda dns_inp,x
+  beq @last_byte_of_cname
+  bmi @found_compression_marker  
+  sta dns_packed_hostname,y
+  inx
+  iny
+  bmi @error_in_response  ;if we go past 128 bytes, something is wrong
+  bpl :-                          ;go get next byte
+  @last_byte_of_cname:
+  sta dns_packed_hostname,y
+  
+  lda #$ff                       ;set a status marker so we know whats going on
+  sta dns_status
+  stx dns_status+1
+  
+  lda #1
+  sta dns_break_polling_loop
+  
+  rts                              ; finished processing - the main dns polling loop should now resend a query, this time for the hostname from the CNAME record
+  
+@found_compression_marker:
+  lda dns_inp+1,x
+  tax
+  jmp :-
+  
+@not_a_cname:  
+  cmp #1                        ; should be 1 (A record)
+  bne @error_in_response
+  txa
+  clc
+  adc #10                       ;skip 2 bytes TYPE, 2 bytes CLASS, 4 bytes TTL, 2 bytes RDLENGTH
+  tax
+                                    ;we're now pointing at the answer!
+  lda  dns_inp,x
+  sta dns_ip
+
+  lda  dns_inp+1,x
+  sta dns_ip+1
+  
+  lda  dns_inp+2,x
+  sta dns_ip+2
+  
+  lda  dns_inp+3,x
+  sta dns_ip+3
+
+
+  lda #dns_complete
+  sta dns_state
+  
+  lda #1
+  sta dns_break_polling_loop
+
+@error_in_response:
+  
+  sta dns_status
+  stx dns_status+1
+  rts
+
+
+
+
+
+;-- LICENSE FOR dns.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_dottedquad_s.html b/docs/ip65_dottedquad_s.html new file mode 100644 index 0000000..240cefe --- /dev/null +++ b/docs/ip65_dottedquad_s.html @@ -0,0 +1,113 @@ +

ip65 technical reference

File : ip65/dottedquad.s

functions

functiondescription
parse_dotted_quad
 convert a string representing a dotted quad (IP address, netmask) into 4 octets
+ inputs:
+   AX= pointer to null-terminated (*) string containing dotted quad
+         e.g. "192.168.1.0",0
+ outputs:
+   carry flag is set if there was an error, clear otherwise
+   dotted_quad_value: will be set to (32 bit) ip address (if no error)
+ (*) NB to assist with url parsing, a ':' or '/' can also terminate the string

variables

variabledescriptionsize (bytes)
dotted_quad_valueset to 32 bit ip address on a succesful call to parse_dotted_quad 4

implementation

+  .include "../inc/common.i"
+ 
+  
+  .export parse_dotted_quad
+  .export dotted_quad_value
+  
+  .bss
+  dotted_quad_value: .res 4 ;set to 32 bit ip address on a succesful call to parse_dotted_quad
+  
+  .data
+  
+  ;self modifying code
+  dotted_quad_ptr:
+  lda $FFFF
+  rts
+    
+  
+  .code
+
+
+; convert a string representing a dotted quad (IP address, netmask) into 4 octets
+; inputs:
+;   AX= pointer to null-terminated (*) string containing dotted quad
+;         e.g. "192.168.1.0",0
+; outputs:
+;   carry flag is set if there was an error, clear otherwise
+;   dotted_quad_value: will be set to (32 bit) ip address (if no error)
+; (*) NB to assist with url parsing, a ':' or '/' can also terminate the string
+parse_dotted_quad:
+    stax  dotted_quad_ptr+1    
+    ldx #0
+    txa 
+    sta dotted_quad_value
+@each_byte:  
+    jsr get_next_byte
+    cmp #0
+    beq @done
+    and #$7F  ;turn off bit 7
+    cmp #'.'
+    beq @got_dot
+    cmp #':'
+    beq @done
+    cmp #'/'
+    beq @done
+    sec
+    sbc #'0'
+    bcc @error
+    cmp #10
+    bcs @error
+  
+    clc
+    ldy  #10
+@mul_by_y:
+    adc dotted_quad_value,x
+    bcs @error
+    dey
+    bne @mul_by_y  
+    sta dotted_quad_value,x
+    jmp @each_byte
+    
+@got_dot:
+  inx
+  cpx #4
+  beq @error
+  lda #0
+  sta dotted_quad_value,x
+  jmp @each_byte
+@done:
+    cpx #3
+    bne @error
+    clc
+    rts
+@error:   
+    sec
+    rts
+
+
+get_next_byte:
+    jsr dotted_quad_ptr
+    inc dotted_quad_ptr+1
+    bne :+
+    inc dotted_quad_ptr+2
+:
+    rts
+
+
+;-- LICENSE FOR dottedquad.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_eth_s.html b/docs/ip65_eth_s.html new file mode 100644 index 0000000..ea79891 --- /dev/null +++ b/docs/ip65_eth_s.html @@ -0,0 +1,114 @@ +

ip65 technical reference

File : ip65/eth.s

 Common ethernet driver code (independant of host computer or ethernet chipset)
+

functions

functiondescription
eth_set_broadcast_dest
set the destination address in the packet under construction to be the ethernet
+broadcast address (FF:FF:FF:FF:FF:FF)
+inputs:
+ eth_outp: buffer in which outbound ethernet packet is being constructed
+outputs: none
eth_set_my_mac_src
set the source address in the packet under construction to be local mac address
+inputs:
+ eth_outp: buffer in which outbound ethernet packet is being constructed
+outputs: none
eth_set_proto
set the 'protocol' field in the packet under construction
+inputs: 
+   A = protocol number (per 'eth_proto_*' constants)
+outputs: none

variables

variabledescriptionsize (bytes)
eth_inp space for input packet 1518
eth_inp_len input packet length 2
eth_outp space for output packet 1518
eth_outp_len output packet length 2

constants

constantsdescriptionvalue
eth_data offset of packet data in ethernet packet 14
eth_dest offset of destination address in ethernet packet 0
eth_proto_arp6 +
eth_proto_ip protocols 0 +
eth_src offset of source address in ethernet packet 6
eth_type offset of packet type in ethernet packet 12

implementation

; Common ethernet driver code (independant of host computer or ethernet chipset)
+
+.include "../inc/common.i"
+
+  .export eth_set_broadcast_dest
+  .export eth_set_my_mac_src
+  .export eth_set_proto
+
+  .exportzp eth_proto_ip
+  .exportzp eth_proto_arp
+  .exportzp eth_dest
+  .exportzp eth_src
+  .exportzp eth_type
+  .exportzp eth_data
+
+  .export eth_outp
+  .export eth_outp_len
+  .export eth_inp
+  .export eth_inp_len
+
+  .import cfg_mac
+
+  .bss
+
+; input and output buffers
+eth_inp_len:  .res 2    ; input packet length
+eth_inp:  .res 1518  ; space for input packet
+eth_outp_len:  .res 2    ; output packet length
+eth_outp:  .res 1518  ; space for output packet
+
+
+
+; ethernet packet offsets
+eth_dest  = 0    ; offset of destination address in ethernet packet
+eth_src    = 6    ; offset of source address in ethernet packet
+eth_type  = 12    ; offset of packet type in ethernet packet
+eth_data  = 14    ; offset of packet data in ethernet packet
+
+; protocols
+
+eth_proto_ip  = 0
+eth_proto_arp  = 6
+
+
+  .code
+;set the destination address in the packet under construction to be the ethernet
+;broadcast address (FF:FF:FF:FF:FF:FF)
+;inputs:
+; eth_outp: buffer in which outbound ethernet packet is being constructed
+;outputs: none
+eth_set_broadcast_dest:
+  ldx #5
+  lda #$ff
+:  sta eth_outp,x
+  dex
+  bpl :-
+  rts
+
+;set the source address in the packet under construction to be local mac address
+;inputs:
+; eth_outp: buffer in which outbound ethernet packet is being constructed
+;outputs: none
+eth_set_my_mac_src:
+  ldx #5
+:  lda cfg_mac,x
+  sta eth_outp + 6,x
+  dex
+  bpl :-
+  rts
+
+;set the 'protocol' field in the packet under construction
+;inputs: 
+;   A = protocol number (per 'eth_proto_*' constants)
+;outputs: none
+eth_set_proto:
+  sta eth_outp + eth_type + 1
+  lda #8
+  sta eth_outp + eth_type
+  rts
+
+
+
+;-- LICENSE FOR eth.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Per Olofsson,
+; MagerValp@gmail.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Per Olofsson. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_function_dispatcher_s.html b/docs/ip65_function_dispatcher_s.html new file mode 100644 index 0000000..6f6abb8 --- /dev/null +++ b/docs/ip65_function_dispatcher_s.html @@ -0,0 +1,704 @@ +

ip65 technical reference

File : ip65/function_dispatcher.s

this is some very quick and dirty glue to make the most useful IP65 functions available via a single entry point.
+this allows user applications to be developed that don't link ip65 in directly, rather they use an instance of ip65 that is preloaded (or in a cartridge/ROM)
+this whole file could (and should) be greatly optimised by making it all table driven, but since this file is probably only going to be used in a bankswitched ROM where
+space is not at such a premium, I'll go with the gross hack for now.
+

functions

functiondescription
kipper_dispatcher

implementation

;this is some very quick and dirty glue to make the most useful IP65 functions available via a single entry point.
+;this allows user applications to be developed that don't link ip65 in directly, rather they use an instance of ip65 that is preloaded (or in a cartridge/ROM)
+;this whole file could (and should) be greatly optimised by making it all table driven, but since this file is probably only going to be used in a bankswitched ROM where
+;space is not at such a premium, I'll go with the gross hack for now.
+
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+.include "../inc/common.i"
+.include "../inc/commonprint.i"
+.export kipper_dispatcher
+
+.import ip65_init
+.import dhcp_init
+.import cfg_get_configuration_ptr
+.import tftp_load_address
+.importzp tftp_filename
+.import tftp_ip
+.import ip65_error
+.import tftp_clear_callbacks
+.import tftp_download
+.import tftp_upload
+.import tftp_set_callback_vector
+.import tftp_filesize
+.import dns_ip
+.import dns_resolve
+.import dns_set_hostname
+.import udp_callback
+.import udp_add_listener
+.import udp_remove_listener
+.import ip_inp
+.import udp_inp
+.import udp_send
+.import udp_send_src
+.import udp_send_src_port
+.import udp_send_dest
+.import udp_send_dest_port
+.import udp_send_len
+
+.import copymem
+.import cfg_mac
+.import cfg_tftp_server
+.importzp copy_src
+.importzp copy_dest
+
+;reuse the copy_src zero page location
+kipper_params = copy_src
+buffer_ptr= copy_dest
+.data
+
+
+
+ip_configured_flag:
+  .byte 0
+
+.code
+
+
+set_tftp_params:
+    ldx #$03
+:
+  lda cfg_tftp_server,x
+  sta tftp_ip,x
+  dex
+  bpl :-
+
+  ldy #KPR_TFTP_FILENAME
+  lda (kipper_params),y
+  sta tftp_filename
+  iny
+  lda (kipper_params),y
+  sta tftp_filename+1
+
+  ldy #KPR_TFTP_POINTER
+  lda (kipper_params),y
+  sta tftp_load_address
+  iny
+  lda (kipper_params),y
+  sta tftp_load_address+1
+  
+  jsr tftp_clear_callbacks
+  
+  rts
+
+set_tftp_callback_vector:
+  ldy #KPR_TFTP_POINTER+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y  
+  jmp tftp_set_callback_vector
+  
+kipper_dispatcher:
+  stax kipper_params
+  
+
+  cpy #KPR_INITIALIZE
+  bne :+
+  lda ip_configured_flag
+  bne ip_configured
+  jsr ip65_init
+  bcs init_failed
+  jsr dhcp_init
+  bcc dhcp_ok
+  jsr ip65_init   ;if DHCP failed, then reinit the IP stack (which will reset IP address etc that DHCP messed with to cartridge default values)
+dhcp_ok:  
+  lda #1
+  sta ip_configured_flag
+  clc
+init_failed:  
+  rts
+  
+ip_configured:
+  clc
+  rts
+:
+
+  cpy #KPR_GET_IP_CONFIG
+  bne :+
+  ldax  #cfg_mac
+  clc
+  rts
+:
+
+  cpy #KPR_DNS_RESOLVE
+  bne :+  
+  phax
+  ldy #KPR_DNS_HOSTNAME+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  jsr dns_set_hostname 
+  bcs @dns_error
+  jsr dns_resolve
+  bcs @dns_error
+
+  ldy #KPR_DNS_HOSTNAME_IP  
+  plax
+  stax kipper_params
+  ldx #4
+@copy_dns_ip:
+  lda dns_ip,y
+  sta (kipper_params),y
+  iny
+  dex  
+  bne @copy_dns_ip
+  rts
+@dns_error:
+  plax
+  rts
+    
+:
+
+  cpy #KPR_UDP_ADD_LISTENER
+  bne :+  
+  ldy #KPR_UDP_LISTENER_CALLBACK
+  lda (kipper_params),y
+  sta udp_callback
+  iny
+  lda (kipper_params),y
+  sta udp_callback+1
+  ldy #KPR_UDP_LISTENER_PORT+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  
+  jmp udp_add_listener
+:
+
+  cpy #KPR_GET_INPUT_PACKET_INFO
+  bne :+
+  ldy #3
+@copy_src_ip:  
+  lda ip_inp+12,y  ;src IP 
+  sta (kipper_params),y
+  dey
+  bpl @copy_src_ip
+  
+  ldy #KPR_REMOTE_PORT
+  lda udp_inp+1 ;src port (lo byte)
+  sta (kipper_params),y
+  iny
+  lda udp_inp+0 ;src port (high byte)
+  sta (kipper_params),y
+  iny
+  lda udp_inp+3 ;dest port (lo byte)
+  sta (kipper_params),y
+  iny
+  lda udp_inp+2 ;dest port (high byte)
+  sta (kipper_params),y
+
+  iny
+  sec
+  lda udp_inp+5 ;payload length (lo byte)
+  sbc #8  ;to remove length of header
+  sta (kipper_params),y
+
+  iny
+  lda udp_inp+4 ;payload length (hi byte)
+  sbc #0  ;in case there was a carry from the lo byte
+  sta (kipper_params),y
+  
+  iny
+  lda #<(udp_inp+8) ;payload ptr (lo byte)
+  sta (kipper_params),y
+
+  iny
+  lda #>(udp_inp+8) ;payload ptr (hi byte)
+  sta (kipper_params),y
+
+.import tcp_inbound_data_ptr
+.import tcp_inbound_data_length
+
+  lda ip_inp+9 ;proto number
+  cmp #6  ;TCP
+  bne @not_tcp
+  ldy #KPR_PAYLOAD_LENGTH
+  lda tcp_inbound_data_length
+  sta (kipper_params),y
+  iny
+  lda tcp_inbound_data_length+1
+  sta (kipper_params),y
+  
+  ldy #KPR_PAYLOAD_POINTER
+  lda tcp_inbound_data_ptr
+  sta (kipper_params),y
+  iny
+  lda tcp_inbound_data_ptr+1
+  sta (kipper_params),y
+@not_tcp:
+
+  clc
+  rts
+:  
+
+  cpy #KPR_SEND_UDP_PACKET
+  bne :+
+  ldy #3
+@copy_dest_ip:  
+  lda (kipper_params),y
+  sta udp_send_dest,y
+  dey
+  bpl @copy_dest_ip
+  
+  ldy #KPR_REMOTE_PORT  
+  lda (kipper_params),y
+  sta udp_send_dest_port
+  iny
+  lda (kipper_params),y
+  sta udp_send_dest_port+1
+  iny
+
+  lda (kipper_params),y
+  sta udp_send_src_port
+  iny
+  lda (kipper_params),y
+  sta udp_send_src_port+1
+  iny
+
+
+  lda (kipper_params),y
+  sta udp_send_len
+  iny
+  lda (kipper_params),y
+  sta udp_send_len+1
+  iny
+
+  ;AX should point at data to send
+  lda (kipper_params),y
+  pha
+  iny
+  lda (kipper_params),y  
+  tax
+  pla
+  jmp udp_send
+:
+
+  cpy #KPR_UDP_REMOVE_LISTENER
+  bne :+
+  jmp udp_remove_listener
+:  
+
+
+  cpy #KPR_DEACTIVATE
+  ;nothing to do now we don't use IRQ
+  bne :+
+  clc
+  rts
+:  
+
+  cpy #KPR_TFTP_SET_SERVER
+  bne :+
+  ldy #3
+@copy_tftp_server_ip:  
+  lda (kipper_params),y
+  sta cfg_tftp_server,y
+  dey
+  bpl @copy_tftp_server_ip
+  clc
+  rts
+  
+:
+  cpy #KPR_TFTP_DOWNLOAD
+  bne :+
+  phax
+  jsr set_tftp_params
+  jsr tftp_download
+
+@after_tftp_call:  ;write the current load address back to the param buffer (so if $0000 was passed in, the caller can find out the actual value used)
+  plax
+  bcs @tftp_error
+  stax kipper_params
+
+  ldy #KPR_TFTP_POINTER
+  lda tftp_load_address
+  sta (kipper_params),y  
+  iny
+  lda tftp_load_address+1
+  sta (kipper_params),y
+
+  ldy #KPR_TFTP_FILESIZE
+  lda tftp_filesize
+  sta (kipper_params),y  
+  iny
+  lda tftp_filesize+1
+  sta (kipper_params),y
+  clc
+@tftp_error:   
+  rts
+:
+
+
+
+  cpy #KPR_TFTP_CALLBACK_DOWNLOAD
+  bne :+
+  phax
+  jsr set_tftp_params
+  jsr set_tftp_callback_vector
+  jsr tftp_download
+  jmp @after_tftp_call
+:
+
+  cpy #KPR_TFTP_UPLOAD
+  bne :+
+  phax
+  jsr set_tftp_params
+  ldy #KPR_TFTP_POINTER
+  lda (kipper_params),y
+  sta tftp_filesize
+  iny
+  lda (kipper_params),y  
+  sta tftp_filesize+1
+  
+  jsr tftp_download
+  jmp @after_tftp_call
+:
+
+  cpy #KPR_TFTP_CALLBACK_UPLOAD
+  bne :+
+  jsr set_tftp_params
+  jsr set_tftp_callback_vector
+  jmp tftp_upload
+:
+
+  cpy #KPR_PRINT_ASCIIZ
+  bne :+
+  jsr print
+  clc
+  rts
+:  
+
+  cpy #KPR_PRINT_HEX
+  bne :+
+  jsr print_hex
+  clc
+  rts
+:  
+
+  cpy #KPR_PRINT_DOTTED_QUAD
+  bne :+
+  jsr print_dotted_quad
+  clc
+  rts
+:  
+
+  cpy #KPR_PRINT_IP_CONFIG
+  bne :+
+  jsr print_ip_config
+  clc
+  rts
+:
+
+  cpy #KPR_PRINT_INTEGER
+  bne :+
+  jsr print_integer
+  clc
+  rts
+:
+
+
+  .segment "TCP_VARS"
+    port_number: .res 2
+    nonzero_octets: .res 1
+  .code
+
+  cpy #KPR_DOWNLOAD_RESOURCE
+  bne :+  
+.import url_download
+.import url_download_buffer
+.import url_download_buffer_length
+
+
+  ldy #KPR_URL_DOWNLOAD_BUFFER
+  lda (kipper_params),y
+  sta url_download_buffer
+  iny
+  lda (kipper_params),y
+  sta url_download_buffer+1
+
+  ldy #KPR_URL_DOWNLOAD_BUFFER_LENGTH
+  lda (kipper_params),y
+  sta url_download_buffer_length
+  iny
+  lda (kipper_params),y
+  sta url_download_buffer_length+1
+  
+  ldy #KPR_URL+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  jmp url_download
+:
+
+  cpy #KPR_FILE_LOAD
+bne :+  
+.import  io_device_no
+.import io_read_file
+.import io_filename
+.import io_filesize
+.import io_load_address
+  phax
+  ldy #KPR_FILE_ACCESS_FILENAME
+  lda (kipper_params),y
+  sta io_filename
+  iny
+  lda (kipper_params),y
+  sta io_filename+1
+
+  ldy #KPR_FILE_ACCESS_DEVICE
+  lda (kipper_params),y
+  sta io_device_no
+
+  ldy #KPR_FILE_ACCESS_POINTER+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  jsr io_read_file
+  plax
+  bcc @read_file_ok
+  rts
+  
+@read_file_ok:  
+  stax kipper_params
+
+  ldy #KPR_FILE_ACCESS_POINTER
+  lda io_load_address
+  sta (kipper_params),y
+  iny
+  lda io_load_address+1
+  sta (kipper_params),y
+
+  ldy #KPR_FILE_ACCESS_FILESIZE
+  lda io_filesize
+  sta (kipper_params),y
+  iny
+  lda io_filesize+1
+  sta (kipper_params),y
+  rts
+:
+
+  
+  cpy #KPR_HTTPD_START
+  bne :+  
+  .import httpd_start
+  jmp httpd_start
+:
+
+cpy #KPR_HTTPD_GET_VAR_VALUE
+  bne :+  
+  .import http_get_value
+  jmp http_get_value
+:
+
+
+
+  cpy #KPR_PING_HOST
+  .import icmp_echo_ip
+  .import icmp_ping
+  bne :+  
+  ldy #3
+@copy_ping_ip_loop:
+  lda (kipper_params),y
+  sta icmp_echo_ip,y
+  dey
+  bpl @copy_ping_ip_loop
+  jmp icmp_ping  
+  
+:  
+  cpy #KPR_TCP_CONNECT
+  bne :+  
+  .import tcp_connect
+  .import tcp_callback
+  .import tcp_connect_ip
+  .import tcp_listen
+  ldy #3
+  lda #0
+  sta nonzero_octets
+@copy_dest_ip:  
+  lda (kipper_params),y
+  beq @octet_was_zero
+  inc nonzero_octets
+@octet_was_zero:  
+  sta tcp_connect_ip,y
+  dey
+  bpl @copy_dest_ip
+  
+  ldy #KPR_TCP_CALLBACK
+  lda (kipper_params),y
+  sta tcp_callback
+  iny
+  lda (kipper_params),y
+  sta tcp_callback+1
+  
+  ldy #KPR_TCP_PORT+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  ldy nonzero_octets
+  bne @outbound_tcp_connection
+  jmp tcp_listen
+  
+@outbound_tcp_connection:  
+  jmp tcp_connect
+
+:
+
+  .import tcp_send
+  .import tcp_send_data_len
+  cpy #KPR_SEND_TCP_PACKET
+  bne :+
+  ldy #KPR_TCP_PAYLOAD_LENGTH
+  lda (kipper_params),y
+  sta tcp_send_data_len
+  iny
+  lda (kipper_params),y
+  sta tcp_send_data_len+1
+  ldy #KPR_TCP_PAYLOAD_POINTER+1
+  lda (kipper_params),y
+  tax
+  dey
+  lda (kipper_params),y
+  jmp tcp_send
+
+:
+
+
+.import tcp_close
+  cpy #KPR_TCP_CLOSE_CONNECTION
+  bne :+
+  jmp tcp_close
+:
+
+
+.import filter_dns
+.import get_filtered_input
+.import filter_number
+
+  cpy #KPR_INPUT_STRING
+  bne :+
+  ldy #40 ;max chars
+  ldax #$0000
+  jmp get_filtered_input
+:
+
+  cpy #KPR_INPUT_HOSTNAME  
+  bne :+
+  ldy #40 ;max chars
+  ldax #filter_dns
+  jmp get_filtered_input
+:
+
+cpy #KPR_INPUT_PORT_NUMBER
+  bne :+
+  ldy #5 ;max chars
+  ldax #filter_number
+  jsr get_filtered_input  
+  bcs @no_port_entered
+  
+  ;AX now points a string containing port number    
+  .import parse_integer
+  jmp parse_integer
+  
+@no_port_entered:
+  rts
+:
+
+cpy #KPR_BLOCK_COPY
+  bne :+
+  ;this is where we pay the price for trying to save a few 'zero page' pointers 
+  ;by reusing the 'copy_src' and 'copy_dest' addresses!
+.segment "TCP_VARS"
+  tmp_copy_src: .res 2
+  tmp_copy_dest: .res 2
+  tmp_copy_length: .res 2
+.code
+  
+  ldy #KPR_BLOCK_SRC
+  lda (kipper_params),y
+  sta tmp_copy_src
+  iny  
+  lda (kipper_params),y
+  sta tmp_copy_src+1
+  
+  ldy #KPR_BLOCK_DEST
+  lda (kipper_params),y
+  sta tmp_copy_dest
+  iny  
+  lda (kipper_params),y
+  sta tmp_copy_dest+1
+
+  ldy #KPR_BLOCK_SIZE
+  lda (kipper_params),y
+  sta tmp_copy_length
+  iny  
+  lda (kipper_params),y
+  sta tmp_copy_length+1
+
+  ldax tmp_copy_src
+  stax  copy_src
+  ldax tmp_copy_dest
+  stax  copy_dest
+  ldax tmp_copy_length
+  jmp copymem
+:
+
+  cpy #KPR_PARSER_INIT
+  bne :+
+  .import parser_init
+  jmp parser_init
+:
+
+  cpy #KPR_PARSER_SKIP_NEXT
+  bne :+
+  .import parser_skip_next
+  jmp parser_skip_next
+:
+
+
+
+  cpy #KPR_GET_LAST_ERROR
+  bne :+
+  lda ip65_error
+  clc
+  rts
+:  
+
+
+;default function handler
+  lda #KPR_ERROR_FUNCTION_NOT_SUPPORTED
+  sta ip65_error
+  sec        ;carry flag set = error
+  rts
+
+
+;-- LICENSE FOR function_dispatcher.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_http_s.html b/docs/ip65_http_s.html new file mode 100644 index 0000000..f6a9697 --- /dev/null +++ b/docs/ip65_http_s.html @@ -0,0 +1,280 @@ +

ip65 technical reference

File : ip65/http.s

routines for parsing a HTTP request
+to use - first call http_parse_request, then call http_get_value to get method name, path, and variable values
+NB - this routine uses the same buffer space and zero page locations as many other ip65 routines. so do not call
+other ip65 routines between the http_parse_request & http_get_value else odd things will happen.
+

functions

functiondescription
http_get_value
retrieve the value of a variable defined in the previously parsed HTTP request.
+inputs: 
+A = variable to retrieve. 
+ to get the method (GET/POST/HEAD), pass A=$01. 
+ to get the path (everything between the method and the first '?'), pass A=$02. 
+outputs:
+ if variable exists in HTTP request, carry flag will be clear and AX points to value (null terminated string)
+ if variable did not exist, carry flag will be set.
http_parse_request
split a HTTP request into method (e.g. GET or POST), the path, and any querystring variables
+NB only the first letter in a variable name is significant. i.e.  if a querystring contains variables 'a','alpha' & 'alabama', only the first one in will be retreivable.
+the method is stored in var $01
+the path is stored in var $02
+for example, parsing "GET /goober?a=foo&alpha=beta" would result in:
+value of A when calling http_get_value              value returned by http_get_value
+        #$01                                        "GET"
+        #$02                                        "/goober"
+        #'a'                                        "foo"
+        #'A'                                        (error)
+inputs: 
+AX = pointer to HTTP request
+outputs:
+ none - but values can be retrieved through subsequent calls to http_get_value
http_variables_buffer
work area for storing variables extracted from query string

implementation

;routines for parsing a HTTP request
+;to use - first call http_parse_request, then call http_get_value to get method name, path, and variable values
+;NB - this routine uses the same buffer space and zero page locations as many other ip65 routines. so do not call
+;other ip65 routines between the http_parse_request & http_get_value else odd things will happen.
+
+.include "../inc/common.i"
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+
+.export http_parse_request
+.export http_get_value
+.export http_variables_buffer
+
+.importzp copy_src
+.importzp copy_dest
+.import output_buffer
+.import parse_hex_digits
+;reuse the copy_src zero page var
+string_ptr = copy_src
+table_ptr=copy_dest
+
+
+.bss
+var_name: .res 1
+hex_digit: .res 1
+
+.data
+http_variables_buffer: .word $2800  ;work area for storing variables extracted from query string
+
+
+.code
+
+
+;split a HTTP request into method (e.g. GET or POST), the path, and any querystring variables
+;NB only the first letter in a variable name is significant. i.e.  if a querystring contains variables 'a','alpha' & 'alabama', only the first one in will be retreivable.
+;the method is stored in var $01
+;the path is stored in var $02
+;for example, parsing "GET /goober?a=foo&alpha=beta" would result in:
+;value of A when calling http_get_value              value returned by http_get_value
+;        #$01                                        "GET"
+;        #$02                                        "/goober"
+;        #'a'                                        "foo"
+;        #'A'                                        (error)
+;inputs: 
+;AX = pointer to HTTP request
+;outputs:
+; none - but values can be retrieved through subsequent calls to http_get_value
+http_parse_request:
+  stax string_ptr
+  
+  ldax http_variables_buffer
+  
+  stax  table_ptr
+
+  lda #1  ;start of method
+  ldy #0  
+  jsr put_byte
+  lda (string_ptr),y
+  cmp #'/'
+  beq @gopher
+  jsr @check_end_of_string
+  bcs @gopher
+  lda (string_ptr),y
+@extract_method:
+  
+  cmp #' '
+  beq @end_of_method
+  jsr @check_end_of_string
+  bcc :+
+  jmp  @done
+:  
+  jsr put_byte
+  jsr get_next_byte_in_source
+  jmp @extract_method
+
+@gopher:
+  jsr @output_end_of_method
+  lda #'/' 
+  jmp @got_path_char
+@output_end_of_method:  
+  lda #0  ;end of method
+  jsr put_byte
+  lda #2  ;start of path
+  jmp put_byte
+
+@end_of_method:
+  jsr @output_end_of_method
+  
+@extract_path:
+  jsr get_next_byte_in_source
+  jsr @check_end_of_string
+  bcs @done
+  cmp #'?'
+  beq @end_of_path
+  cmp #'&'
+  beq @end_of_path
+@got_path_char:  
+  jsr put_byte
+  jmp @extract_path
+@end_of_path:  
+  lda #0  ;end of path
+  jsr put_byte
+  
+@next_var:
+
+  jsr get_next_byte_in_source
+  jsr @check_end_of_string
+  bcs @done
+  jsr put_byte
+  
+  
+  @skip_to_equals:
+  jsr get_next_byte_in_source
+  jsr @check_end_of_string
+  bcs @done
+  cmp #'?'
+  beq @next_var
+  cmp #'&'
+  beq @next_var  
+  cmp #'='
+  beq @got_var
+  jmp @skip_to_equals
+  
+@got_var:
+
+  jsr get_next_byte_in_source
+  jsr @check_end_of_string
+  bcs @done
+  cmp #'?'
+  beq @end_of_var
+  cmp #'&'
+  beq @end_of_var
+  
+  cmp #'%'
+  beq @get_percent_encoded_byte
+  cmp #'+'
+  bne :+
+  lda #' '
+:
+@got_byte:
+  jsr put_byte    
+  jmp @got_var
+
+@end_of_var:  
+  lda #0
+  jsr put_byte
+  jmp @next_var
+  
+  
+@done:
+  lda #0
+  jsr put_byte
+  jsr put_byte
+  rts
+
+@check_end_of_string:
+  cmp #0
+  beq @end_of_string
+  cmp #' '
+  beq @end_of_string
+  cmp #$0a
+  beq @end_of_string
+  cmp #$0d
+  beq @end_of_string
+  clc
+  rts
+@end_of_string:
+  sec
+  rts
+  
+@get_percent_encoded_byte:
+  jsr get_next_byte_in_source
+  tax
+  jsr get_next_byte_in_source
+  jsr parse_hex_digits
+  jmp @got_byte
+  
+put_byte:
+  sta (table_ptr),y
+  inc table_ptr
+  bne :+
+  inc table_ptr+1
+:
+  rts
+
+;retrieve the value of a variable defined in the previously parsed HTTP request.
+;inputs: 
+;A = variable to retrieve. 
+; to get the method (GET/POST/HEAD), pass A=$01. 
+; to get the path (everything between the method and the first '?'), pass A=$02. 
+;outputs:
+; if variable exists in HTTP request, carry flag will be clear and AX points to value (null terminated string)
+; if variable did not exist, carry flag will be set.
+http_get_value:
+  sta var_name
+  ldax http_variables_buffer
+  stax string_ptr
+  ldy #0
+
+lda (string_ptr),y
+@check_next_var:
+  beq @end_of_vars
+  cmp var_name
+  beq @got_var
+  ;not the var we want, so skip over till next byte
+@skip_till_null_byte:
+  jsr get_next_byte_in_source  
+  bne @skip_till_null_byte
+  jsr get_next_byte_in_source  
+  bne @check_next_var
+  
+@end_of_vars:
+  sec
+  rts
+
+@got_var:
+  jsr get_next_byte_in_source
+  ldax string_ptr
+  clc
+  rts
+  
+  
+get_next_byte_in_source:
+  inc string_ptr
+  bne :+
+  inc string_ptr+1
+:
+  lda (string_ptr),y
+  rts
+
+
+
+
+;-- LICENSE FOR http.s --
+; The contents of this file are subject to the Mozilla Public License
+; Version 1.1 (the "License"); you may not use this file except in
+; compliance with the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+; 
+; Software distributed under the License is distributed on an "AS IS"
+; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+; License for the specific language governing rights and limitations
+; under the License.
+; 
+; The Original Code is ip65.
+; 
+; The Initial Developer of the Original Code is Jonno Downes,
+; jonno@jamtronix.com.
+; Portions created by the Initial Developer are Copyright (C) 2009
+; Jonno Downes. All Rights Reserved.  
+; -- LICENSE END --
+
\ No newline at end of file diff --git a/docs/ip65_httpd_s.html b/docs/ip65_httpd_s.html new file mode 100644 index 0000000..00f9dd0 --- /dev/null +++ b/docs/ip65_httpd_s.html @@ -0,0 +1,636 @@ +

ip65 technical reference

File : ip65/httpd.s

a simple HTTP server
+to use - call httpd_start with AX pointing at routine to call for each inbound page
+

functions

functiondescription
httpd_start
start a HTTP server
+this routine will stay in an endless loop that is broken only if user press the ABORT key (runstop on a c64)
+inputs: 
+httpd_port_number=port number to listen on
+AX = pointer to routine to callback for each inbound HTTP request=
+set this to $0000 to use the default handler (which will look for requested files on the local disk).
+outputs:
+ none

implementation

;a simple HTTP server
+;to use - call httpd_start with AX pointing at routine to call for each inbound page
+.include "../inc/common.i"
+
+.ifndef KPR_API_VERSION_NUMBER
+  .define EQU     =
+  .include "../inc/kipper_constants.i"
+.endif
+
+
+HTTPD_TIMEOUT_SECONDS=5 ;what's the maximum time we let 1 connection be open for?
+
+
+.export httpd_start
+
+.import http_parse_request
+.import http_get_value
+.import tcp_listen
+.import tcp_callback
+.import ip65_process
+.import check_for_abort_key
+.import ip65_error
+.import parse_hex_digits
+.import print
+.import copymem
+.importzp copy_src
+.importzp copy_dest
+.import tcp_inbound_data_ptr
+.import tcp_inbound_data_length
+.import tcp_send_data_len
+.import tcp_send
+.import tcp_close
+.import io_read_catalogue
+.import native_to_ascii
+.import io_read_file_with_callback
+.import io_filename
+.import io_callback
+.import timer_seconds
+.import  __HTTP_VARS_LOAD__
+.import  __HTTP_VARS_RUN__
+.import  __HTTP_VARS_SIZE__
+
+temp_ptr =copy_src
+
+.bss
+found_eol: .byte 0
+connection_closed: .byte 0
+output_buffer_length: .res 2
+sent_header: .res 1
+connection_timeout_seconds: .byte 0
+tcp_buffer_ptr: .res 2
+buffer_size: .res 1
+skip_mode: .res 1
+temp_x: .res 1
+
+.segment "HTTP_VARS"
+
+httpd_io_buffer: .word $2000 ;by default, use a 3k buffer at $2000 for storing inbound requests.
+httpd_scratch_buffer: .word $2B00 ;by default, use a 1k buffer at $2b00 as a scratchpad
+httpd_port_number: .word 80
+
+
+jump_to_callback:
+  jmp $ffff
+
+jump_to_embedded_routine:
+  jmp $ffff
+
+get_next_byte:
+  lda $ffff
+  inc get_next_byte+1
+  bne @skip
+  inc get_next_byte+2
+@skip:
+  rts
+
+  
+emit_a:
+  stx temp_x
+  ldx skip_mode
+  bne skip_emit_a
+emit_a_ptr:
+  sta $ffff
+  inc emit_a_ptr+1
+  bne :+
+  inc emit_a_ptr+2
+:
+  inc output_buffer_length
+  bne :+
+  inc output_buffer_length+1
+  lda output_buffer_length+1
+  cmp #2
+  bne :+
+  jsr send_buffer    
+:  
+skip_emit_a:
+  ldx temp_x
+  rts
+
+.code
+
+;start a HTTP server
+;this routine will stay in an endless loop that is broken only if user press the ABORT key (runstop on a c64)
+;inputs: 
+;httpd_port_number=port number to listen on
+;AX = pointer to routine to callback for each inbound HTTP request=
+;set this to $0000 to use the default handler (which will look for requested files on the local disk).
+;outputs:
+; none
+httpd_start:
+  pha ;save AX while we relocate self modifying code
+  txa
+  pha
+  
+  ;relocate the self-modifying code 
+  ldax #__HTTP_VARS_LOAD__
+  stax copy_src
+  ldax #__HTTP_VARS_RUN__
+  stax copy_dest
+  ldax #__HTTP_VARS_SIZE__
+  jsr copymem
+  pla
+  tax
+  pla
+  
+  stx jump_to_callback+2
+  bne @not_default_handler
+  ldax #default_httpd_handler
+  jmp httpd_start
+@not_default_handler:
+  sta jump_to_callback+1
+
+
+
+@listen:
+  jsr tcp_close
+  ldax httpd_io_buffer
+  stax tcp_buffer_ptr 
+  ldax #http_callback
+  stax tcp_callback
+  ldax httpd_port_number
+  
+  jsr tcp_listen
+  bcc @connect_ok
+  rts 
+@connect_ok: 
+
+  lda #0
+  sta connection_closed
+  sta found_eol
+  clc
+  jsr timer_seconds  ;time of day clock: seconds (in BCD)
+  sed
+  adc #HTTPD_TIMEOUT_SECONDS
+  cmp #$60
+  bcc @timeout_set
+  sec
+  sbc #$60
+@timeout_set:  
+  cld
+  sta connection_timeout_seconds
+
+@main_polling_loop:
+  jsr ip65_process
+  jsr check_for_abort_key
+  bcc @no_abort
+  lda #KPR_ERROR_ABORTED_BY_USER
+  sta ip65_error
+  rts
+@no_abort:  
+  lda found_eol
+  bne @got_eol  
+
+  jsr timer_seconds  ;time of day clock: seconds
+  
+  cmp connection_timeout_seconds  
+  beq @connection_timed_out
+  lda connection_closed
+  beq  @main_polling_loop  
+@connection_timed_out:
+  jmp @listen
+  
+@got_eol:
+  ldax httpd_io_buffer  
+  jsr http_parse_request
+  jsr jump_to_callback  ;call the handler to generate the response for this request.
+  ;AX should now point at data to be sent
+  ;Y should contain the content type/status code
+  bcs :+    ;carry is set if the callback routine already sent the response
+  jsr send_response
+  :
+
+
+  jmp @listen ;go listen for the next request
+  
+
+http_callback:  
+  lda tcp_inbound_data_length+1
+  cmp #$ff
+  bne @not_eof
+  inc connection_closed
+@done:  
+  rts
+@not_eof:
+  lda found_eol
+  bne @done
+  
+;copy this chunk to our input buffer
+  ldax tcp_buffer_ptr  
+  stax copy_dest
+  ldax tcp_inbound_data_ptr
+  stax copy_src
+  ldax tcp_inbound_data_length
+  jsr copymem
+
+;increment the pointer into the input buffer  
+  clc
+  lda tcp_buffer_ptr
+  adc tcp_inbound_data_length
+  sta tcp_buffer_ptr
+  sta temp_ptr
+  lda tcp_buffer_ptr+1
+  adc tcp_inbound_data_length+1
+  sta tcp_buffer_ptr+1  
+  sta temp_ptr+1
+  
+;put a null byte at the end (assumes we have set temp_ptr already)
+  lda #0
+  tay
+  sta (temp_ptr),y
+    
+;look for CR or LF in input
+  sta found_eol
+  ldax httpd_io_buffer
+  stax get_next_byte+1
+
+@look_for_eol:
+  jsr get_next_byte
+  cmp #$0a
+  beq @found_eol    
+  cmp #$0d
+  bne @not_eol
+@found_eol:
+  inc found_eol
+  rts
+@not_eol:
+  cmp #0
+  bne @look_for_eol 
+  rts
+  
+  
+default_httpd_handler:
+  lda #$02
+  jsr http_get_value
+  bcc @not_error
+  ldy #5
+  clc
+  rts
+@not_error:  
+  stax temp_ptr
+  ldy #1
+  lda (temp_ptr),y
+  bne @not_index
+  ldax #default_html
+  ldy #2
+  clc
+  rts
+@not_index:
+  ;assume that 'path' is a filename  
+  inc temp_ptr
+  bne :+
+  inc temp_ptr
+:
+  lda #0
+  sta sent_header
+  ldax temp_ptr
+  stax io_filename
+  ldax #send_file_callback
+  stax io_callback
+  ldax httpd_scratch_buffer
+  jsr io_read_file_with_callback
+  bcc @sent_ok
+  ldy #4
+  clc  
+  rts
+@sent_ok:
+  sec
+  rts
+
+send_file_callback:
+  sty buffer_size             ;only 1 (the last) sector can ever be !=$100 bytes
+  lda  sent_header
+  bne @sent_header
+  ldy #3  ;"application/octet-stream"
+  jsr send_header
+  inc sent_header
+@sent_header:
+  ldax  httpd_scratch_buffer
+  stax  temp_ptr
+  ldy #0
+@loop:
+  tya
+  pha
+  lda (temp_ptr),y
+  jsr emit_a
+  pla
+  tay
+  iny
+  cpy buffer_size
+  bne @loop
+  rts
+
+reset_output_buffer:
+  ldax httpd_io_buffer  
+  sta emit_a_ptr+1
+  stx emit_a_ptr+2
+  lda #0
+  sta output_buffer_length
+  sta output_buffer_length+1
+  sta skip_mode
+  rts
+
+
+emit_disk_catalogue:
+
+  ldax httpd_scratch_buffer
+  jsr  io_read_catalogue
+  lda get_next_byte+1
+  pha
+  lda get_next_byte+2
+  pha
+  ldax  httpd_scratch_buffer
+  stax  get_next_byte+1
+
+@next_filename:
+
+  jsr get_next_byte
+  cmp #0
+  beq  @done
+  pha
+  
+  
+  ldax #file_li_preamble
+  jsr  emit_string
+  pla
+  pha
+  jsr  emit_a  
+  ldax get_next_byte+1  
+  jsr  emit_string
+
+  ldax #file_li_middle
+  jsr  emit_string
+  pla
+  bne @convert_byte
+@next_byte:
+  jsr get_next_byte
+  cmp #0
+  beq  @done_this_filename
+@convert_byte:  
+  jsr native_to_ascii
+  jsr  emit_a
+  jmp @next_byte
+@done_this_filename:
+
+
+  
+
+  ldax #file_li_postamble
+  jsr  emit_string
+
+
+  jmp  @next_filename
+@done:  
+  
+  pla
+  sta get_next_byte+2
+  pla
+  sta get_next_byte+1
+  rts
+  
+send_response:
+  stax get_next_byte+1
+  jsr reset_output_buffer
+  jsr send_header  
+  
+@response_loop:
+  jsr get_next_byte
+  cmp #0
+  bne @not_last_byte
+@send_buffer:  
+  jmp send_buffer
+@not_last_byte:  
+  cmp #'%'
+  beq @escape
+@back_from_escape:
+  jsr emit_a
+  jmp @response_loop
+
+@escape:
+  jsr get_next_byte
+  cmp #0
+  beq @send_buffer
+  cmp #'%'
+  beq @back_from_escape
+  cmp #'$'
+  bne :+
+  jsr get_next_byte
+  jsr http_get_value
+  bcs @response_loop
+  jsr emit_string
+  jmp @response_loop
+:  
+  cmp #'.'
+  bne :+
+  lda #0
+  sta skip_mode
+  jmp @response_loop
+:
+  cmp #'?'
+  bne :+
+  lda #0
+  sta skip_mode
+  jsr get_next_byte
+  jsr http_get_value
+  bcc @response_loop
+  inc skip_mode
+  jmp @response_loop
+:  
+  cmp #'!'
+  bne :+
+  lda #0
+  sta skip_mode
+  jsr get_next_byte
+  jsr http_get_value
+  bcs @response_loop
+  inc skip_mode
+  jmp @response_loop
+:  
+ 
+  cmp #';'
+  bne :+
+  jsr get_next_byte
+  sta jump_to_embedded_routine+1
+  jsr get_next_byte
+  sta jump_to_embedded_routine+2
+  jmp @call_embedded_routine
+  :
+  cmp #':'
+  bne :+
+  jsr @get_next_hex_value
+  sta jump_to_embedded_routine+2    
+  jsr @get_next_hex_value
+  sta jump_to_embedded_routine+1
+@call_embedded_routine:  
+  lda skip_mode
+  bne @dont_call_embedded_routine
+  ldax #emit_a
+  jsr jump_to_embedded_routine
+@dont_call_embedded_routine:  
+  jmp @response_loop
+  :
+;if we got here, it's an invalid escape code  
+  jmp @response_loop
+  
+@get_next_hex_value:
+  jsr get_next_byte
+  tax
+  jsr get_next_byte
+  jmp parse_hex_digits
+    
+send_buffer:    
+  ldax output_buffer_length
+  stax tcp_send_data_len
+  ldax httpd_io_buffer  
+  jsr tcp_send
+  jmp reset_output_buffer
+ 
+ send_header:
+;inputs: Y = header type
+;$00 = no header (assume header sent already)
+;$01 = 200 OK, 'text/text'
+;$02 = 200 OK, 'text/html'
+;$03 = 200 OK, 'application/octet-stream' 
+;$04 = 404 Not Found
+;$05..$FF = 500 System Error
+
+  cpy #00
+  bne :+
+  rts
+:
+  cpy #1
+  bne  @not_text
+  jsr  emit_ok_status_line_and_content_type
+  ldax  #text_text
+  jsr  emit_string
+  jmp  @done
+
+@not_text:
+  cpy #2
+  
+  bne  @not_html
+  jsr  emit_ok_status_line_and_content_type
+  ldax  #text_html
+  jsr  emit_string
+  jmp  @done
+
+@not_html:
+  cpy #3
+  bne  @not_binary
+  jsr  emit_ok_status_line_and_content_type
+  ldax  #application_octet_stream
+  jsr  emit_string
+  jmp  @done
+
+@not_binary:
+  cpy #4
+  bne  @not_404
+  ldax  #http_version
+  jsr  emit_string
+  ldax  #status_not_found
+  jsr  emit_string
+
+  jsr @done
+  ldax #status_not_found
+  jmp emit_string
+  
+@not_404:
+  ldax  #http_version
+  jsr  emit_string
+  ldax  #status_system_error
+  jsr  emit_string
+  jsr @done
+  ldax #status_system_error
+  jmp emit_string
+@done:
+  ldax   #end_of_header
+  jmp  emit_string      
+
+
+emit_ok_status_line_and_content_type:
+  ldax  #http_version
+  jsr  emit_string
+  ldax  #status_ok
+  jsr  emit_string
+  ldax  #content_type
+  jmp  emit_string    
+
+emit_string:
+  stax  temp_ptr
+  ldy  #0
+@next_byte:
+  lda  (temp_ptr),y
+  beq  @done
+  jsr  emit_a
+  iny
+  bne  @next_byte
+@done:
+  rts
+  
+ 
+.rodata
+default_html:
+.byte "

Index of /


kipper - the 100%% 6502 m/l web server " +.byte 0 + + +CR=$0D +LF=$0A + +http_version: + .byte "HTTP/1.0 ",0 + +status_ok: + .byte "200 OK",CR,LF,0 + +status_not_found: + .byte "404 Not Found",CR,LF,0 + +status_system_error: + .byte "500 System Error",CR,LF,0 +content_type: + .byte "Content-Type: ",0 + +text_text: + .byte "text/text",CR,LF,0 + +text_html: + .byte "text/html",CR,LF,0 + +application_octet_stream: + .byte "application/octet-stream",CR,LF,0 + +end_of_header: + .byte "Connection: Close",CR,LF + .byte "Server: Kipper_httpd/0.c64",CR,LF + .byte CR,LF,0 + +file_li_preamble: + .byte "
  • ",0 +file_li_postamble: + .byte "
  • ",0 + + + +;-- LICENSE FOR httpd.s -- +; The contents of this file are subject to the Mozilla Public License +; Version 1.1 (the "License"); you may not use this file except in +; compliance with the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +; License for the specific language governing rights and limitations +; under the License. +; +; The Original Code is ip65. +; +; The Initial Developer of the Original Code is Jonno Downes, +; jonno@jamtronix.com. +; Portions created by the Initial Developer are Copyright (C) 2009 +; Jonno Downes. All Rights Reserved. +; -- LICENSE END -- +
    \ No newline at end of file diff --git a/docs/ip65_icmp_s.html b/docs/ip65_icmp_s.html new file mode 100644 index 0000000..a1f121a --- /dev/null +++ b/docs/ip65_icmp_s.html @@ -0,0 +1,500 @@ +

    ip65 technical reference

    File : ip65/icmp.s

    ICMP implementation
    +
    +

    functions

    functiondescription
    icmp_add_listener
    add an icmp listener
    +inputs:
    + A = icmp type
    + icmp_callback: vector to call when an icmp packet of specified type arrives
    +outputs:
    + carry flag - set if error, clear if no error
    
    icmp_init
     initialize icmp
    + inputs: none
    + outputs: none
    
    icmp_ping
    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_process
    process incoming icmp packet
    +inputs:
    +   eth_inp points to an ethernet frame containing an icmp packet
    +outputs:
    +   carry flag - set on any error, clear if OK
    +   if inbound packet is a request (e.g. 'echo request') and an icmp listener
    +   has been installed, then an appropriate response message will  be 
    +   generated and sent out (overwriting the eth_outp buffer)
    
    icmp_remove_listener
    remove an icmp listener
    +inputs:
    + A = icmp type
    +outputs:
    + carry flag - set if error (i.e. no listner for this type exists), 
    +    clear if no error
    
    icmp_send_echo
     icmp_send_echo was contributed by Glenn 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
    

    variables

    variabledescriptionsize (bytes)
    icmp_callback argument for icmp_add_listener 2
    icmp_echo_ip destination IP address for echo request ("ping") 4

    constants

    constantsdescriptionvalue
    icmp_cksumoffset of 'checksum' field in icmp packet 2
    icmp_codeoffset of 'code' field in icmp packet 1
    icmp_dataoffset of 'data' field in icmp packet 4
    icmp_inppointer to inbound icmp packet ip_inp + ip_data
    icmp_outppointer to outbound icmp packet ip_outp + ip_data
    icmp_typeoffset of 'type' field in icmp packet 0

    implementation

    ;ICMP implementation
    +;
    +
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +  .export icmp_init
    +  .export icmp_process
    +  .export icmp_add_listener
    +  .export icmp_remove_listener
    +
    +  .export icmp_callback
    +
    +  .export icmp_inp
    +  .export icmp_outp
    +  .exportzp icmp_type
    +  .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_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 timer_read
    +  .import timer_timeout
    +
    +.data
    +  icmp_cbtmp:  jmp $0000      ; temporary vector - address filled in later
    +
    +
    +  .bss
    +
    +; argument for icmp_add_listener
    +icmp_callback:  .res 2
    +
    +
    +; icmp callbacks
    +icmp_cbmax  = 2
    +icmp_cbveclo:  .res icmp_cbmax    ; table of listener vectors (lsb)
    +icmp_cbvechi:  .res icmp_cbmax    ; table of listener vectors (msb)
    +icmp_cbtype:  .res icmp_cbmax    ; table of listener types
    +icmp_cbcount:  .res 1      ; number of active listeners
    +
    +; icmp packet offsets
    +icmp_inp  = ip_inp + ip_data  ;pointer to inbound icmp packet
    +icmp_outp  = ip_outp + ip_data ;pointer to outbound icmp packet
    +icmp_type  = 0 ;offset of 'type' field in icmp packet
    +icmp_code  = 1 ;offset of 'code' field in icmp packet
    +icmp_cksum  = 2 ;offset of 'checksum' field in icmp packet
    +icmp_data  = 4;offset of 'data' field in icmp packet
    +
    +; icmp echo packet offsets
    +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
    +
    +; initialize icmp
    +; inputs: none
    +; outputs: none
    +icmp_init:
    +  lda #0
    +  sta icmp_cbcount
    +  rts
    +
    +;process incoming icmp packet
    +;inputs:
    +;   eth_inp points to an ethernet frame containing an icmp packet
    +;outputs:
    +;   carry flag - set on any error, clear if OK
    +;   if inbound packet is a request (e.g. 'echo request') and an icmp listener
    +;   has been installed, then an appropriate response message will  be 
    +;   generated and sent out (overwriting the eth_outp buffer)
    +icmp_process:
    +  lda icmp_inp + icmp_type
    +  cmp #icmp_msg_type_echo_request        ; ping
    +  beq @echo
    +
    +  lda icmp_cbcount    ; any installed icmp listeners?
    +  beq @drop
    +
    +  ldx icmp_cbcount    ; check listened types
    +  dex
    +:  lda icmp_cbtype,x
    +  cmp icmp_inp + icmp_type
    +  beq @handle      ; found a match
    +  dex
    +  bpl :-
    +
    +@drop:
    +  sec
    +  rts
    +
    +@handle:
    +  lda icmp_cbveclo,x    ; copy vector
    +  sta icmp_cbtmp + 1
    +  lda icmp_cbvechi,x
    +  sta icmp_cbtmp + 2
    +  jsr icmp_cbtmp      ; call listener
    +  clc
    +  rts
    +
    +@echo:
    +  lda ip_broadcast    ; check if packet is broadcast
    +  beq @notbc
    +  sec        ; don't reply to broadcast pings
    +  rts
    +@notbc:
    +  ldx #5
    +:  lda eth_inp,x      ; swap dest and src mac
    +  sta eth_outp + 6,x
    +  lda eth_inp + 6,x
    +  sta eth_outp,x
    +  dex
    +  bpl :-
    +
    +  ldx #12        ; copy the packet
    +:  lda eth_inp,x
    +  sta eth_outp,x
    +  inx
    +  cpx eth_inp_len
    +  bne :-
    +
    +  ldx #3
    +:  lda ip_inp + ip_src,x    ; swap dest and src ip
    +  sta ip_outp + ip_dest,x
    +  lda ip_inp + ip_dest,x
    +  sta ip_outp + ip_src,x
    +  dex
    +  bpl :-
    +
    +  lda #0        ; change type to reply
    +  sta icmp_outp + icmp_type
    +
    +  lda icmp_inp + icmp_cksum  ; recalc checksum
    +  clc
    +  adc #8
    +  sta icmp_outp + icmp_cksum
    +  bcc :+
    +  inc icmp_outp + icmp_cksum + 1
    +:
    +  lda eth_inp_len      ; copy length
    +  sta eth_outp_len
    +  lda eth_inp_len + 1
    +  sta eth_outp_len + 1
    +
    +  lda #0        ; clear checksum
    +  sta ip_outp + ip_header_cksum
    +  sta ip_outp + ip_header_cksum + 1
    +  ldax #ip_outp      ; calculate ip header checksum
    +  stax ip_cksum_ptr
    +  ldax #20
    +  jsr ip_calc_cksum
    +  stax ip_outp + ip_header_cksum
    +
    +  jsr eth_tx      ; send packet
    +
    +  clc
    +  rts
    +
    +
    +;add an icmp listener
    +;inputs:
    +; A = icmp type
    +; icmp_callback: vector to call when an icmp packet of specified type arrives
    +;outputs:
    +; carry flag - set if error, clear if no error
    +icmp_add_listener:
    +  ldx icmp_cbcount    ; any listeners at all?
    +  beq @add
    +  cpx #icmp_cbmax      ; max?
    +  beq @full
    +  ldx #0
    +:  cmp icmp_cbtype,x    ; check if type is already listened
    +  beq @busy
    +  inx
    +  cpx icmp_cbcount
    +  bne :-
    +@add:
    +  inc icmp_cbcount    ; increase counter
    +  sta icmp_cbtype,x    ; add type
    +  lda icmp_callback    ; and vector
    +  sta icmp_cbveclo,x
    +  lda icmp_callback + 1
    +  sta icmp_cbvechi,x
    +
    +  clc
    +  rts
    +@full:
    +@busy:
    +  sec
    +  rts
    +
    +
    +;remove an icmp listener
    +;inputs:
    +; A = icmp type
    +;outputs:
    +; carry flag - set if error (i.e. no listner for this type exists), 
    +;    clear if no error
    +icmp_remove_listener:
    +  ldx icmp_cbcount    ; any listeners installed?
    +  beq @notfound
    +  dex
    +:  cmp icmp_cbtype,x    ; check if type is listened
    +  beq @remove
    +  dex
    +  bpl :-
    +@notfound:
    +  sec
    +  rts
    +@remove:
    +  txa        ; number of listeners below
    +  eor #$ff
    +  clc
    +  adc icmp_cbcount
    +  beq @done
    +@move:
    +  tay        ; number of items to move
    +:  lda icmp_cbtype + 1,x    ; move type
    +  sta icmp_cbtype,x
    +  lda icmp_cbveclo + 1,x    ; move vector lsb
    +  sta icmp_cbveclo,x
    +  lda icmp_cbvechi + 1,x    ; move vector msb
    +  sta icmp_cbvechi,x
    +  inx
    +  dey
    +  bne :-
    +@done:
    +  dec icmp_cbcount    ; decrement counter
    +  clc
    +  rts
    +
    +.ifdef TCP
    +
    +; icmp_send_echo was contributed by Glenn 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 #KPR_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 #KPR_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
    +
    +
    +;-- LICENSE FOR icmp.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Per Olofsson,
    +; MagerValp@gmail.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Per Olofsson. All Rights Reserved.  
    +;
    +;Contributor(s): Jonno Downes, Glenn Holmer
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_ip65_s.html b/docs/ip65_ip65_s.html new file mode 100644 index 0000000..e8893bd --- /dev/null +++ b/docs/ip65_ip65_s.html @@ -0,0 +1,166 @@ +

    ip65 technical reference

    File : ip65/ip65.s

     ip65 main routines
    +

    functions

    functiondescription
    ip65_init
     initialise the IP stack
    + this calls the individual protocol & driver initialisations, so this is
    + the only *_init routine that must be called by a user application,
    + except for dhcp_init which must also be called if the application
    + is using dhcp rather than hardcoded ip configuration
    + inputs: none
    + outputs: none
    
    ip65_process
    main ip polling loop
    +this routine should be periodically called by an application at any time
    +that an inbound packet needs to be handled.
    +it is 'non-blocking', i.e. it will return if there is no packet waiting to be
    +handled. any inbound packet will be handed off to the appropriate handler.
    +inputs: none
    +outputs: carry flag set if no packet was waiting, or packet handling caused error.
    +  since the inbound packet may trigger generation of an outbound, eth_outp 
    +  and eth_outp_len may be overwriiten. 
    
    ip65_random_word
    generate a 'random' 16 bit word
    +entropy comes from the last ethernet frame, counters, and timer
    +inputs: none
    +outputs: AX set to a pseudo-random 16 bit number
    

    variables

    variabledescriptionsize (bytes)
    ip65_ctr incremented for every incoming packet 1
    ip65_ctr_arp incremented for every incoming arp packet 1
    ip65_ctr_ip incremented for every incoming ip packet 1
    ip65_errorlast error code 1

    implementation

    ; ip65 main routines
    +
    +.include "../inc/common.i"
    +
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +  .export ip65_init
    +  .export ip65_process
    +  .export ip65_random_word
    +  .export ip65_ctr
    +  .export ip65_ctr_arp
    +  .export ip65_ctr_ip
    +  
    +  .export ip65_error
    +   
    +  .import cfg_init
    +  
    +  .import eth_init
    +  .import timer_init
    +  .import arp_init
    +  .import ip_init
    +  .import timer_read
    +
    +  .import eth_inp
    +  .import eth_outp
    +  .import eth_rx
    +
    +  .import ip_process
    +  .import arp_process
    +
    +  .importzp eth_proto_arp
    +
    +  .export ip65_random_word
    +  
    +  .bss
    +
    +
    +ip65_ctr:  .res 1    ; incremented for every incoming packet
    +ip65_ctr_arp:  .res 1    ; incremented for every incoming arp packet
    +ip65_ctr_ip:  .res 1    ; incremented for every incoming ip packet
    +
    +ip65_error: .res 1  ;last error code
    +
    +  .code
    +
    +;generate a 'random' 16 bit word
    +;entropy comes from the last ethernet frame, counters, and timer
    +;inputs: none
    +;outputs: AX set to a pseudo-random 16 bit number
    +ip65_random_word:
    +  jsr timer_read ;sets AX
    +  adc $9004 ;on a VIC 20, this is the raster register
    +  adc $d41b; on a c64, this is a 'random' number from the SID
    +  pha
    +  adc ip65_ctr_arp
    +  ora #$08    ;make sure we grab at least 8 bytes from eth_inp
    +  tax   
    +:  
    +  adc eth_inp,x
    +  adc eth_outp,x
    +  dex
    +  bne :-
    +  tax
    +  pla
    +  adc ip65_ctr
    +  eor ip65_ctr_ip
    +  rts
    +
    +; initialise the IP stack
    +; this calls the individual protocol & driver initialisations, so this is
    +; the only *_init routine that must be called by a user application,
    +; except for dhcp_init which must also be called if the application
    +; is using dhcp rather than hardcoded ip configuration
    +; inputs: none
    +; outputs: none
    +ip65_init:
    +  jsr cfg_init    ;copy default values (including MAC address) to RAM
    +  jsr eth_init    ; initialize ethernet driver
    +  
    +  bcc @ok
    +  lda #KPR_ERROR_DEVICE_FAILURE
    +  sta ip65_error
    +  rts
    +@ok:  
    +  jsr timer_init    ; initialize timer
    +  jsr arp_init    ; initialize arp
    +  jsr ip_init    ; initialize ip, icmp, udp, and tcp
    +  clc
    +  rts
    +
    +
    +;main ip polling loop
    +;this routine should be periodically called by an application at any time
    +;that an inbound packet needs to be handled.
    +;it is 'non-blocking', i.e. it will return if there is no packet waiting to be
    +;handled. any inbound packet will be handed off to the appropriate handler.
    +;inputs: none
    +;outputs: carry flag set if no packet was waiting, or packet handling caused error.
    +;  since the inbound packet may trigger generation of an outbound, eth_outp 
    +;  and eth_outp_len may be overwriiten. 
    +ip65_process:
    +  jsr eth_rx    ; check for incoming packets
    +  bcs @done
    +
    +  lda eth_inp + 12  ; type should be 08xx
    +  cmp #8
    +  bne @done
    +
    +  lda eth_inp + 13
    +;  cmp #eth_proto_ip  ; ip = 00
    +  beq @ip
    +  cmp #eth_proto_arp  ; arp = 06
    +  beq @arp
    +@done:
    +  rts
    +
    +@arp:
    +  inc ip65_ctr_arp
    +  jmp arp_process
    +
    +@ip:
    +  inc ip65_ctr_ip
    +  jmp ip_process
    +
    +
    +
    +;-- LICENSE FOR ip65.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Per Olofsson,
    +; MagerValp@gmail.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Per Olofsson. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_ip_s.html b/docs/ip65_ip_s.html new file mode 100644 index 0000000..1eba810 --- /dev/null +++ b/docs/ip65_ip_s.html @@ -0,0 +1,533 @@ +

    ip65 technical reference

    File : ip65/ip.s

    functions

    functiondescription
    ip_calc_cksum
     calculate checksum for a buffer according to the standard IP checksum algorithm
    + David Schmidt discovered errors in the original ip65 implementation, and he replaced
    + this with an implementation from the contiki project (http://www.sics.se/contiki/)
    + when incorporating ip65 into ADTPro (http://adtpro.sourceforge.net/)
    + So I have cribbed that version from 
    + http://adtpro.cvs.sourceforge.net/viewvc/adtpro/adtpro/client/src/ip65/ip.s
    +inputs:
    + ip_cksum_ptr: points at buffer to be checksummed
    + AX: length of buffer to be checksumed
    +outputs:
    + AX: checkum of buffer
    
    ip_create_packet
     create an IP header (with all the appropriate flags and common fields set) inside an
    + ethernet frame
    +inputs:
    + eth_outp: should point to a buffer in which the ethernet frame is being built
    +outputs:
    + eth_outp: contains an IP header with version, TTL, flags, src address & IP header 
    + checksum fields set.
    
    ip_init
     initialize ip routines
    + inputs: none
    + outputs: none
    
    ip_process
    process an incoming packet & call the appropriate protocol handler 
    +inputs:
    + eth_inp: should point to the received ethernet packet 
    +outputs:
    + carry flag - set on any error, clear if OK
    + depending on the packet contents and the protocol handler, a response
    + message may be generated and sent out (overwriting eth_outp buffer)
    
    ip_send
     send an IP packet
    +inputs
    + eth_outp: should point to an ethernet frame that has an IP header created (by 
    + calling ip_create_packet)
    + ip_len: should contain length of IP packet (header + data)
    + ip_id: should contain an ID that is unique for each packet
    + ip_protocol: should contain protocol ID
    + ip_dest: should contain the destination IP address
    +outputs:
    + eth_outp: ethernet frame updated with correct IP header, then sent out over 
    + the wire
    + carry flag - set on any error, clear if OK
    

    variables

    variabledescriptionsize (bytes)
    ip_broadcastflag set when an incoming IP packet was sent to a broadcast address 1
    ip_cksum_ptr pointer to data to be checksummed 2

    constants

    constantsdescriptionvalue
    ip_dataoffset of data payload in an IP packet 20
    ip_destoffset of "destination address" field in an IP packet header 16
    ip_fragoffset of "fragmentation offset" field in an IP packet header 6
    ip_header_cksumoffset of "ip header checksum" field in an IP packet header 10
    ip_idoffset of "identification" field in an IP packet header 4
    ip_inppointer to start of IP packet in input ethernet frame eth_inp + eth_data
    ip_lenoffset of "length" field in an IP packet header 2
    ip_outppointer to start of IP packet in output ethernet frame eth_outp + eth_data
    ip_protooffset of "protocol number" field in an IP packet header 9
    ip_proto_icmp ip protocols 1 +
    ip_proto_tcp6 +
    ip_proto_udp17 +
    ip_srcoffset of "source address" field in an IP packet header 12
    ip_tosoffset of "type of service" field in an IP packet header 1
    ip_ttloffset of "time to live" field in an IP packet header 8
    ip_ver_ihloffset of 4 bit "version" field and 4 bit "header length" field in an IP packet header 0

    implementation

    .include "../inc/common.i"
    +
    +  .export ip_init
    +  .export ip_process
    +  .export ip_calc_cksum
    +  .export ip_create_packet
    +  .export ip_send
    +  .export ip_inp
    +  .export ip_outp
    +  .export ip_broadcast
    +  .exportzp ip_cksum_ptr
    +  .exportzp ip_ver_ihl
    +  .exportzp ip_tos
    +  .exportzp ip_len
    +  .exportzp ip_id
    +  .exportzp ip_frag
    +  .exportzp ip_ttl
    +  .exportzp ip_proto
    +  .exportzp ip_header_cksum
    +  .exportzp ip_src
    +  .exportzp ip_dest
    +  .exportzp ip_data
    +
    +  .exportzp ip_proto_icmp
    +  .exportzp ip_proto_tcp
    +  .exportzp ip_proto_udp
    +
    +  
    +  .import cfg_mac
    +  .import cfg_ip
    +
    +  .import eth_tx
    +  .import eth_set_proto
    +  .import eth_inp
    +  .import eth_inp_len
    +  .import eth_outp
    +  .import eth_outp_len
    +
    +  .importzp eth_dest
    +  .importzp eth_src
    +  .importzp eth_type
    +  .importzp eth_data
    +  .importzp eth_proto_ip
    +  .importzp eth_proto_arp
    +
    +  .import arp_lookup
    +  .import arp_mac
    +  .import arp_ip
    +
    +  .import icmp_init
    +  .import icmp_process
    +
    +  .import udp_init
    +  .import udp_process
    +
    +.ifdef TCP
    +  .import tcp_init
    +  .import tcp_process
    +.endif
    +  .importzp copy_src
    +
    +
    +  .segment "IP65ZP" : zeropage
    +
    +; checksum
    +ip_cksum_ptr:  .res 2    ; pointer to data to be checksummed
    +
    +
    +.bss
    +
    +ip_cksum_len:  .res 2    ; length of data to be checksummed
    +
    +; ip packets start at ethernet packet + 14
    +ip_inp    = eth_inp + eth_data    ;pointer to start of IP packet in input ethernet frame
    +ip_outp    = eth_outp + eth_data ;pointer to start of IP packet in output ethernet frame
    +
    +; temp storage for size calculation
    +len:    .res 2
    +
    +; flag for incoming broadcast packets
    +ip_broadcast:  .res 1  ;flag set when an incoming IP packet was sent to a broadcast address
    +
    +; ip packet offsets
    +ip_ver_ihl  = 0 ;offset of 4 bit "version" field and 4 bit "header length" field in an IP packet header
    +ip_tos    = 1 ;offset of "type of service" field in an IP packet header
    +ip_len    = 2 ;offset of "length" field in an IP packet header
    +ip_id    = 4 ;offset of "identification" field in an IP packet header
    +ip_frag    = 6 ;offset of "fragmentation offset" field in an IP packet header
    +ip_ttl    = 8 ;offset of "time to live" field in an IP packet header
    +ip_proto  = 9 ;offset of "protocol number" field in an IP packet header
    +ip_header_cksum  = 10 ;offset of "ip header checksum" field in an IP packet header
    +ip_src    = 12 ;offset of "source address" field in an IP packet header
    +ip_dest    = 16 ;offset of "destination address" field in an IP packet header
    +ip_data    = 20 ;offset of data payload in an IP packet
    +
    +; ip protocols
    +
    +ip_proto_icmp  = 1
    +ip_proto_tcp  = 6
    +ip_proto_udp  = 17
    +
    +
    +; temp for calculating checksum
    +cksum:    .res 3
    +
    +; bad packet counters
    +bad_header:  .res 2
    +bad_addr:  .res 2
    +
    +
    +  .code
    +
    +; initialize ip routines
    +; inputs: none
    +; outputs: none
    +ip_init:
    +  lda #0
    +  sta bad_header
    +  sta bad_header + 1
    +  sta bad_addr
    +  sta bad_addr + 1
    +
    +  jsr icmp_init
    +.ifdef TCP
    +  jsr tcp_init
    +.endif
    +  jsr udp_init
    +
    +  rts
    +
    +
    +;process an incoming packet & call the appropriate protocol handler 
    +;inputs:
    +; eth_inp: should point to the received ethernet packet 
    +;outputs:
    +; carry flag - set on any error, clear if OK
    +; depending on the packet contents and the protocol handler, a response
    +; message may be generated and sent out (overwriting eth_outp buffer)
    +ip_process:
    +  jsr verifyheader    ; ver, ihl, len, frag, checksum
    +  bcc @ok
    +@badpacket:
    +  sec
    +  rts
    +@ok:
    +  jsr checkaddr      ; make sure it's meant for us
    +  bcs @badpacket
    +
    +  lda ip_inp + ip_proto
    +  cmp #ip_proto_icmp
    +  bne :+
    +  jmp icmp_process    ; jump to icmp handler
    +.ifdef TCP  
    +:  cmp #ip_proto_tcp
    +  bne :+
    +  jmp tcp_process      ; jump to tcp handler
    +.endif  
    +:  cmp #ip_proto_udp
    +  bne :+
    +  jmp udp_process      ; jump to udp handler
    +:
    +unknown_protocol:
    +  sec        ; unknown protocol
    +  rts
    +
    +
    +; verify that header contains what we expect
    +verifyheader:
    +  lda ip_inp + ip_ver_ihl    ; IPv4 and no IP options
    +  cmp #$45
    +  bne @badpacket
    +
    +;  lda ip_inp + ip_tos    ; ignore ToS
    +
    +  lda ip_inp + ip_len + 1    ; ip + 14 bytes ethernet header
    +  clc
    +  adc #14
    +  sta len
    +  lda ip_inp + ip_len
    +  adc #0
    +  sta len + 1
    +
    +  lda eth_inp_len      ; check if advertised length is shorter
    +  sec        ; than actual length
    +  sbc len
    +  lda eth_inp_len + 1
    +  sbc len + 1
    +  bmi @badpacket
    +
    +  lda ip_inp + ip_frag    ; check for fragmentation
    +  beq :+
    +  cmp #$40
    +  bne @badpacket
    +:  lda ip_inp + ip_frag + 1
    +  bne @badpacket
    +
    +  ldax #ip_inp      ; verify checksum
    +  stax ip_cksum_ptr
    +  ldax #20
    +  jsr ip_calc_cksum
    +  cmp #0
    +  bne @badpacket
    +  cpx #0
    +  bne @badpacket
    +
    +  clc
    +  rts
    +@badpacket:
    +  inc bad_header
    +  bne :+
    +  inc bad_header + 1
    +:  sec
    +  rts
    +
    +
    +; check that this packet was addressed to us
    +checkaddr:
    +  lda #0
    +  sta ip_broadcast
    +  lda ip_inp + ip_dest    ; compare ip address
    +  cmp cfg_ip
    +  bne @broadcast
    +  lda ip_inp + ip_dest + 1
    +  cmp cfg_ip + 1
    +  bne @broadcast
    +  lda ip_inp + ip_dest + 2
    +  cmp cfg_ip + 2
    +  bne @broadcast
    +  lda ip_inp + ip_dest + 3
    +  cmp cfg_ip + 3
    +  bne @broadcast
    +@ok:  clc
    +  rts
    +@broadcast:
    +;jonno 2011-01-2
    +;previously this was just checking for 255.255.255.255
    +;however it is also possible to do a broadcast to a specific subnet, e.g. 10.5.1.255
    +;this is particularly common with NETBIOS over TCP 
    +;we really should use the netmask, but as a kludge, just see if last octet is 255.
    +;this will work on a /24 network
    +;
    +  inc ip_broadcast
    +;  lda ip_inp + ip_dest    ; check for broadcast
    +;  and ip_inp + ip_dest + 1
    +;  and ip_inp + ip_dest + 2
    +;  and ip_inp + ip_dest + 3
    +  lda ip_inp + ip_dest +3    ; check for broadcast
    +  cmp #$ff
    +  beq @ok
    +  inc bad_addr
    +  
    +  bne :+
    +  inc bad_addr + 1
    +:  sec
    +  rts
    +
    +
    +; create an IP header (with all the appropriate flags and common fields set) inside an
    +; ethernet frame
    +;inputs:
    +; eth_outp: should point to a buffer in which the ethernet frame is being built
    +;outputs:
    +; eth_outp: contains an IP header with version, TTL, flags, src address & IP header 
    +; checksum fields set.
    +ip_create_packet:
    +  lda #$45      ; set IP version and header length
    +  sta ip_outp + ip_ver_ihl
    +
    +  lda #0        ; set type of service
    +  sta ip_outp + ip_tos
    +
    +  ; skip length
    +
    +  ; skip ID
    +
    +  lda #$40      ; don't fragment - or should we not care?
    +  sta ip_outp + ip_frag
    +  lda #0
    +  sta ip_outp + ip_frag + 1
    +
    +  lda #$40      ; set time to live
    +  sta ip_outp + ip_ttl
    +
    +  ; skip protocol
    +
    +  lda #0        ; clear checksum
    +  sta ip_outp + ip_header_cksum
    +  sta ip_outp + ip_header_cksum + 1
    +
    +  ldx #3        ; copy source address
    +:  lda cfg_ip,x
    +  sta ip_outp + ip_src,x
    +  dex
    +  bpl :-
    +
    +  ; skip destination address
    +
    +  rts
    +
    +
    +; send an IP packet
    +;inputs
    +; eth_outp: should point to an ethernet frame that has an IP header created (by 
    +; calling ip_create_packet)
    +; ip_len: should contain length of IP packet (header + data)
    +; ip_id: should contain an ID that is unique for each packet
    +; ip_protocol: should contain protocol ID
    +; ip_dest: should contain the destination IP address
    +;outputs:
    +; eth_outp: ethernet frame updated with correct IP header, then sent out over 
    +; the wire
    +; carry flag - set on any error, clear if OK
    +ip_send:
    +  ldx #3        ; get mac addr from ip
    +:  lda ip_outp + ip_dest,x
    +  sta arp_ip,x
    +  dex
    +  bpl :-
    +
    +  jsr arp_lookup
    +  bcc :+
    +  rts        ; packet buffer nuked, fail
    +:
    +  ldax #ip_outp      ; calculate ip header checksum
    +  stax ip_cksum_ptr
    +  ldax #20
    +  jsr ip_calc_cksum
    +  stax ip_outp + ip_header_cksum
    +
    +  ldx #5
    +:  lda arp_mac,x      ; copy destination mac address
    +  sta eth_outp + eth_dest,x
    +  lda cfg_mac,x      ; copy my mac address
    +  sta eth_outp + eth_src,x
    +  dex
    +  bpl :-
    +
    +  lda #eth_proto_ip    ; set type to IP
    +  jsr eth_set_proto
    +
    +  lda ip_outp + ip_len + 1  ; set packet length
    +  lsr
    +  bcc @dontpad
    +
    +  rol        ; pad with 0
    +  ;clc
    +  adc #ip_outp
    +  sta copy_src + 1
    +  ldy #0
    +  tya
    +  sta (copy_src),y
    +
    +  sec        ; round up to even number
    +@dontpad:
    +  lda ip_outp + ip_len + 1
    +  adc #eth_data
    +  sta eth_outp_len
    +  lda ip_outp + ip_len
    +  adc #0
    +  sta eth_outp_len + 1
    +
    +  ;jsr dbg_dump_ip_header
    +
    +  jmp eth_tx      ; send packet and return status
    +
    +
    +; calculate checksum for a buffer according to the standard IP checksum algorithm
    +; David Schmidt discovered errors in the original ip65 implementation, and he replaced
    +; this with an implementation from the contiki project (http://www.sics.se/contiki/)
    +; when incorporating ip65 into ADTPro (http://adtpro.sourceforge.net/)
    +; So I have cribbed that version from 
    +; http://adtpro.cvs.sourceforge.net/viewvc/adtpro/adtpro/client/src/ip65/ip.s
    +;inputs:
    +; ip_cksum_ptr: points at buffer to be checksummed
    +; AX: length of buffer to be checksumed
    +;outputs:
    +; AX: checkum of buffer
    +ip_calc_cksum:
    +  sta ip_cksum_len    ; save length
    +  stx ip_cksum_len + 1
    +
    +  lda #0
    +  sta cksum
    +  sta cksum+1
    +
    +  lda ip_cksum_len+1
    +  beq chksumlast
    +
    +; If checksum is > 256, do the first runs.
    +  ldy #0
    +  clc
    +chksumloop_256:
    +  lda (ip_cksum_ptr),y
    +  adc cksum
    +  sta cksum
    +  iny
    +  lda (ip_cksum_ptr),y
    +  adc cksum+1
    +  sta cksum+1
    +  iny
    +  bne chksumloop_256
    +  inc ip_cksum_ptr+1
    +  dec ip_cksum_len+1
    +  bne chksumloop_256
    +
    +chksum_endloop_256:
    +  lda cksum
    +  adc #0
    +  sta cksum
    +  lda cksum+1
    +  adc #0
    +  sta cksum+1
    +  bcs chksum_endloop_256
    +  
    +chksumlast:
    +  lda ip_cksum_len
    +  lsr
    +  bcc chksum_noodd
    +  ldy ip_cksum_len
    +  dey
    +  lda (ip_cksum_ptr),y
    +  clc
    +  adc cksum
    +  sta cksum
    +  bcc noinc1
    +  inc cksum+1
    +  bne noinc1
    +  inc cksum
    +noinc1:
    +  dec ip_cksum_len
    +
    +chksum_noodd:
    +  clc
    +  php
    +  ldy ip_cksum_len
    +chksum_loop1:
    +  cpy #0
    +  beq chksum_loop1_end
    +  plp
    +  dey
    +  dey
    +  lda (ip_cksum_ptr),y
    +  adc cksum
    +  sta cksum
    +  iny
    +  lda (ip_cksum_ptr),y
    +  adc cksum+1
    +  sta cksum+1
    +  dey
    +  php
    +  jmp chksum_loop1
    +chksum_loop1_end:
    +  plp
    +  
    +chksum_endloop:
    +  lda cksum
    +  adc #0
    +  sta cksum
    +  lda cksum+1
    +  adc #0
    +  sta cksum+1
    +  bcs chksum_endloop
    +  
    +  lda cksum+1
    +  eor #$ff
    +  tax
    +  lda cksum
    +  eor #$ff
    +
    +  rts
    +
    +
    +
    +;-- LICENSE FOR ip.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Per Olofsson,
    +; MagerValp@gmail.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Per Olofsson. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_output_buffer_s.html b/docs/ip65_output_buffer_s.html new file mode 100644 index 0000000..93d2ce8 --- /dev/null +++ b/docs/ip65_output_buffer_s.html @@ -0,0 +1,30 @@ +

    ip65 technical reference

    File : ip65/output_buffer.s

    variables

    variabledescriptionsize (bytes)
    output_bufferglobal scratch buffer that DHCP/DNS/TFTP and others can use while building outbound packets. +you need to be careful if using this that you don't call a function that also uses it. +if this is reserved for higher level protocols, the likelyhood of collision is low. 520

    implementation

    .bss
    +
    +;global scratch buffer that DHCP/DNS/TFTP and others can use while building outbound packets.
    +;you need to be careful if using this that you don't call a function that also uses it.
    +;if this is reserved for higher level protocols, the likelyhood of collision is low.
    +.export output_buffer
    +output_buffer: .res 520
    +
    +
    +;-- LICENSE FOR output_buffer.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_parser_s.html b/docs/ip65_parser_s.html new file mode 100644 index 0000000..48de1fd --- /dev/null +++ b/docs/ip65_parser_s.html @@ -0,0 +1,114 @@ +

    ip65 technical reference

    File : ip65/parser.s

    text file parsing routines
    + first call parser_init
    + then call parser_skip_next
    +

    functions

    functiondescription
    parser_init
    set up a string for parsing
    +inputs: AX = pointer to (null terminated) string to be parsed
    +outputs: none
    
    parser_skip_next
    advance pointer along till just past the next occurance of specified string
    +inputs: AX= pointer to (null terminated) string to search for 
    +outputs: sec if search string not found
    + if clc, AX = pointer to first byte after string specified 
    + if sec (i.e. no match found), pointer stays in same place
    

    implementation

    ;text file parsing routines
    +; first call parser_init
    +; then call parser_skip_next
    +
    +.export parser_init
    +.export parser_skip_next
    +.importzp copy_src
    +.importzp copy_dest
    +
    +
    +target_string=copy_src
    +search_string=copy_dest
    +
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +.bss
    +temp_ptr: .res 2
    +
    +.segment "SELF_MODIFIED_CODE"
    +get_next_byte:
    +current_string_ptr=get_next_byte+1
    +  lda $ffff
    +  inc current_string_ptr
    +  bne :+
    +  inc current_string_ptr+1
    +:  
    +  pha
    +  pla ;reload A so flags are set correctly
    +  rts
    +
    +.code
    +
    +;set up a string for parsing
    +;inputs: AX = pointer to (null terminated) string to be parsed
    +;outputs: none
    +parser_init:
    +  stax current_string_ptr
    +  clc
    +  rts
    + 
    +
    +;advance pointer along till just past the next occurance of specified string
    +;inputs: AX= pointer to (null terminated) string to search for 
    +;outputs: sec if search string not found
    +; if clc, AX = pointer to first byte after string specified 
    +; if sec (i.e. no match found), pointer stays in same place
    +parser_skip_next:
    +  stax  search_string
    +  ldax  current_string_ptr
    +  stax temp_ptr
    +@check_string:
    +  ldy #0
    +  ldax  current_string_ptr
    +  stax target_string
    +@check_next_char:
    +  lda (search_string),y
    +  beq @matched  
    +  cmp (target_string),y
    +  bne @not_matched
    +  iny
    +  bne @check_next_char
    +@matched:
    +  ;now skip 'y' bytes
    +
    +@skip_byte:
    +  jsr get_next_byte
    +  dey 
    +  bne @skip_byte  
    +
    +  ldax current_string_ptr
    +  clc
    +  rts
    + @not_matched:
    +  jsr get_next_byte
    +  bne @check_string
    +  ldax  temp_ptr
    +  stax current_string_ptr
    +  sec
    +  rts
    +
    +
    +
    +;-- LICENSE FOR parser.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_printf_s.html b/docs/ip65_printf_s.html new file mode 100644 index 0000000..10bd6bc --- /dev/null +++ b/docs/ip65_printf_s.html @@ -0,0 +1,441 @@ +

    ip65 technical reference

    File : ip65/printf.s

    functions

    functiondescription
    console_printf
    print a string to console, with (some) standard printf % codes converted
    +inputs: AX = pointer to 'argument list' 
    + first word in argument list is the string to be printed
    + subsequent words in argument list are interpolated in to the string as
    + it is displayed. (argument list is automagically created by the 'printf' macro
    + defined in inc/printf.i)
    +outputs: none
    +supported % codes:
    +   %s: string (argument interpreted as pointer to null terminated string)
    +   %d: decimal number (arguement interpreted as 16 bit number)
    +   %x: hex number (arguement interpreted as 16 bit number)
    +   %c: char (arguement interpreted as pointer to single ASCII char)
    +"field width" modifiers are also supported, e.g "%02x" to print 2 hex digits
    

    implementation

    .include "../inc/common.i"
    +
    +  .export console_printf
    +
    +
    +  .import console_out
    +  .import console_strout
    +
    +
    +  .segment "IP65ZP" : zeropage
    +
    +strptr:    .res 2
    +argptr:    .res 2
    +valptr:    .res 2
    +
    +
    +  .bss
    +
    +ysave:    .res 1
    +arg:    .res 1
    +fieldwidth:  .res 1
    +fieldwcnt:  .res 1
    +leadzero:  .res 1
    +argtemp:  .res 1
    +int:    .res 2
    +num:    .res 5
    +ext:    .res 2
    +
    +
    +  .code
    +
    +;print a string to console, with (some) standard printf % codes converted
    +;inputs: AX = pointer to 'argument list' 
    +; first word in argument list is the string to be printed
    +; subsequent words in argument list are interpolated in to the string as
    +; it is displayed. (argument list is automagically created by the 'printf' macro
    +; defined in inc/printf.i)
    +;outputs: none
    +;supported % codes:
    +;   %s: string (argument interpreted as pointer to null terminated string)
    +;   %d: decimal number (arguement interpreted as 16 bit number)
    +;   %x: hex number (arguement interpreted as 16 bit number)
    +;   %c: char (arguement interpreted as pointer to single ASCII char)
    +;"field width" modifiers are also supported, e.g "%02x" to print 2 hex digits
    +console_printf:
    +  stax argptr
    +  ldy #0
    +  lda (argptr),y
    +  sta strptr
    +  iny
    +  lda (argptr),y
    +  sta strptr + 1
    +  iny
    +  sty arg
    +
    +  ldy #0
    +@nextchar:
    +  lda (strptr),y
    +  bne :+
    +  rts
    +:
    +  cmp #'%'
    +  beq @printarg
    +
    +  cmp #'\'
    +  beq @printescape
    +
    +  jsr console_out
    +
    +@next:
    +  iny
    +  bne @nextchar
    +
    +  inc strptr + 1
    +  jmp @nextchar
    +
    +@printescape:
    +  iny
    +  bne :+
    +  inc strptr + 1
    +:  lda (strptr),y
    +  ldx #esc_count - 1
    +:  cmp esc_code,x
    +  beq @escmatch
    +  dex
    +  bpl :-
    +  bmi @next
    +@escmatch:
    +  lda esc_char,x
    +  jsr console_out
    +  jmp @next
    +
    +@printarg:
    +  lda #0
    +  sta fieldwidth
    +  sta leadzero
    +  lda #$ff
    +  sta fieldwcnt
    +@argnext:
    +  iny
    +  bne :+
    +  inc strptr + 1
    +:
    +  tya
    +  pha
    +
    +  lda (strptr),y
    +
    +  cmp #'0'    ; check for field width
    +  bcc @notdigit
    +  cmp #'9'+1
    +  bcs @notdigit
    +  and #$0f
    +  bne :+      ; check for leading 0
    +  inc fieldwcnt
    +  bne :+
    +  lda #$80
    +  sta leadzero
    +  pla
    +  tay
    +  jmp @argnext
    +:
    +  pha      ; multiply old value by 10
    +  asl fieldwidth
    +  lda fieldwidth
    +  asl
    +  asl
    +  clc
    +  adc fieldwidth
    +  sta fieldwidth
    +  pla
    +  clc      ; add new value
    +  adc fieldwidth
    +  sta fieldwidth
    +  pla
    +  tay
    +  jmp @argnext
    +
    +@notdigit:
    +  cmp #'s'
    +  beq @argstr
    +
    +  cmp #'d'
    +  beq @argint
    +
    +  cmp #'x'
    +  beq @arghex
    +
    +  cmp #'c'
    +  beq @argchar
    +
    +@argdone:
    +  pla
    +  tay
    +  jmp @next
    +
    +@argstr:
    +  jsr @argax
    +  jsr console_strout
    +
    +  jmp @argdone
    +
    +@argint:
    +  jsr @argax
    +  stax valptr
    +  jsr @valax
    +  jsr printint
    +
    +  jmp @argdone
    +
    +@arghex:
    +  jsr @argax
    +  stax valptr
    +  jsr @valax
    +  jsr printhex
    +
    +  jmp @argdone
    +
    +@argchar:
    +  jsr @argax
    +  stax valptr
    +  ldy #0
    +  lda (valptr),y
    +  jsr console_out
    +
    +  jmp @argdone
    +
    +@argax:
    +  ldy arg
    +  lda (argptr),y
    +  pha
    +  iny
    +  lda (argptr),y
    +  tax
    +  iny
    +  sty arg
    +  pla
    +  rts
    +
    +@valax:
    +  ldy #0
    +  lda (valptr),y
    +  pha
    +  iny
    +  lda (valptr),y
    +  tax
    +  pla
    +  rts
    +
    +@printx:
    +  txa
    +  lsr
    +  lsr
    +  lsr
    +  lsr
    +  tay
    +  lda hex2asc,y
    +  jsr console_out
    +  txa
    +  and #$0f
    +  tay
    +  lda hex2asc,y
    +  jmp console_out
    +
    +
    +; print 16-bit hexadecimal number
    +printhex:
    +  tay
    +  and #$0f
    +  sta num + 3
    +  tya
    +  lsr
    +  lsr
    +  lsr
    +  lsr
    +  sta num + 2
    +
    +  txa
    +  and #$0f
    +  sta num + 1
    +  txa
    +  lsr
    +  lsr
    +  lsr
    +  lsr
    +  sta num
    +
    +  lda #4
    +  sec
    +  sbc fieldwidth
    +  tax
    +  bpl :+
    +  jsr printlong
    +:
    +  cpx #4
    +  beq @nowidth
    +
    +@printlead:
    +  lda num,x
    +  bne @printrest
    +  lda #' '
    +  bit leadzero
    +  bpl :+
    +  lda #'0'
    +:  jsr console_out
    +  inx
    +  cpx #3
    +  bne @printlead
    +
    +@nowidth:
    +  ldx #0
    +:  lda num,x
    +  bne @printrest
    +  inx
    +  cpx #4
    +  bne :-
    +  lda #'0'
    +  jsr console_out
    +  rts
    +
    +@printrest:
    +  lda num,x
    +  tay
    +  lda hex2asc,y
    +  jsr console_out
    +  inx
    +  cpx #4
    +  bne @printrest
    +  rts
    +
    +
    +printlong:
    +  lda #' '
    +  bit leadzero
    +  bpl :+
    +  lda #'0'
    +:  jsr console_out
    +  inx
    +  bne :-
    +  rts
    +
    +
    +; print a 16-bit integer
    +printint:
    +  stax int
    +
    +  ldx #4
    +@next:
    +  lda #0
    +  sta num,x
    +  jsr div10
    +  lda ext
    +  sta num,x
    +  dex
    +  bpl @next
    +
    +  lda fieldwidth
    +  beq @nowidth
    +  lda #5
    +  sec
    +  sbc fieldwidth
    +  tax
    +  bpl :+
    +  jsr printlong
    +:
    +@printlead:
    +  lda num,x
    +  bne @print
    +
    +  lda #' '
    +  bit leadzero
    +  bpl :+
    +  lda #'0'
    +:  jsr console_out
    +  inx
    +  cpx #5
    +  bne @printlead
    +  beq @printzero
    +
    +@nowidth:
    +  inx
    +  cpx #5
    +  beq @printzero
    +  lda num,x
    +  beq @nowidth
    +
    +@print:
    +  clc
    +  adc #'0'
    +  jsr console_out
    +  inx
    +  cpx #5
    +  beq @done
    +@printall:
    +  lda num,x
    +  jmp @print
    +
    +@done:
    +  rts
    +
    +@printzero:
    +  lda #'0'
    +  jmp console_out
    +
    +
    +; 16/16-bit division, from the fridge
    +; int/aux -> int, remainder in ext
    +div10:
    +  lda #0
    +  sta ext+1
    +  ldy #$10
    +@dloop:
    +  asl int
    +  rol int+1
    +  rol
    +  rol ext+1
    +  pha
    +  cmp #10
    +  lda ext+1
    +  sbc #0    ; is this a nop?
    +  bcc @div2
    +  sta ext+1
    +  pla
    +  sbc #10
    +  pha
    +  inc int
    +@div2:
    +  pla
    +  dey
    +  bne @dloop
    +  sta ext
    +  rts
    +
    +
    +  .rodata
    +
    +msg_unimplemented:
    +  .byte "",0
    +
    +hex2asc:
    +  .byte "0123456789abcdef"
    +
    +esc_code:
    +  .byte "eabfnrt", '\'
    +esc_count  = * - esc_code
    +esc_char:
    +  .byte 27, 7, 8, 12, 10, 13, 9, '\'
    +
    +
    +
    +;-- LICENSE FOR printf.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Per Olofsson,
    +; MagerValp@gmail.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Per Olofsson. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_sntp_s.html b/docs/ip65_sntp_s.html new file mode 100644 index 0000000..b39bcec --- /dev/null +++ b/docs/ip65_sntp_s.html @@ -0,0 +1,219 @@ +

    ip65 technical reference

    File : ip65/sntp.s

     Simple Network Time Protocol implementation - per RFC 2030
    +

    functions

    functiondescription
    sntp_get_time
     query an sntp server for current UTC time
    + inputs:
    +   sntp_ip must point to an SNTP server
    + outputs: 
    +   carry flag is set if there was an error, clear otherwise 
    +   sntp_utc_timestamp: set to the number of seconds (seconds since 00:00 on Jan 1, 1900) - timezone is UTC
    

    variables

    variabledescriptionsize (bytes)
    sntp_utc_timestamp will be set to seconds (only) part of utc timestamp (seconds since 00:00 on Jan 1, 1900) 4

    constants

    constantsdescriptionvalue
    sntp_ipcan be set to ip address of server that will be queried via sntp (default is a local LAN broadcast) $ff,$ff,$ff,$ff

    implementation

    ; Simple Network Time Protocol implementation - per RFC 2030
    +
    +MAX_SNTP_MESSAGES_SENT=8
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +.export sntp_ip
    +.export sntp_utc_timestamp
    +.export sntp_get_time
    +  
    +.import ip65_process
    +.import ip65_error
    +
    +.import udp_add_listener
    +.import udp_remove_listener
    +
    +.import udp_callback
    +.import udp_send
    +
    +.import udp_inp
    +.import output_buffer
    +.importzp udp_data
    +
    +.import udp_send_dest
    +.import udp_send_src_port
    +.import udp_send_dest_port
    +.import udp_send_len
    +.import check_for_abort_key
    +.import timer_read
    +  
    +.segment "IP65ZP" : zeropage
    +
    +  
    +.data
    +sntp_ip: .byte $ff,$ff,$ff,$ff  ;can be set to ip address of server that will be queried via sntp (default is a local LAN broadcast)
    +
    +.bss
    +
    +; sntp packet offsets
    +sntp_inp    = udp_inp + udp_data
    +
    +sntp_server_port=123
    +sntp_client_port=123
    +
    +sntp_utc_timestamp: .res 4  ; will be set to seconds (only) part of utc timestamp (seconds since 00:00 on Jan 1, 1900)
    +
    +; sntp state machine
    +sntp_initializing  = 1        ; initial state
    +sntp_query_sent  = 2      ; sent a query, waiting for a response
    +sntp_completed = 3        ; got a good response
    +
    +sntp_timer:  .res 1
    +sntp_loop_count: .res 1
    +sntp_break_polling_loop: .res 1
    +
    +sntp_state:  .res 1
    +sntp_message_sent_count:  .res 1
    + 
    +
    +.code
    +
    +; query an sntp server for current UTC time
    +; inputs:
    +;   sntp_ip must point to an SNTP server
    +; outputs: 
    +;   carry flag is set if there was an error, clear otherwise 
    +;   sntp_utc_timestamp: set to the number of seconds (seconds since 00:00 on Jan 1, 1900) - timezone is UTC
    +sntp_get_time:  
    +  ldax #sntp_in
    +  stax udp_callback 
    +  ldax #sntp_client_port
    +  jsr udp_add_listener  
    +  bcc :+
    +  rts
    +:
    +
    +  lda #sntp_initializing
    +  sta sntp_state
    +  lda #0  ;reset the "message sent" counter
    +  sta sntp_message_sent_count
    +  jsr send_sntp_query
    +  
    +@sntp_polling_loop:
    +  lda sntp_message_sent_count
    +  adc  #10
    +  sta sntp_loop_count  
    +@outer_delay_loop: 
    +  lda #0
    +  sta sntp_break_polling_loop
    +  jsr timer_read
    +  stx sntp_timer            ;we only care about the high byte  
    +  
    +@inner_delay_loop:  
    +  jsr ip65_process
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  rts
    +@no_abort:  
    +  
    +  lda sntp_state
    +  cmp #sntp_completed
    +  beq @complete
    +   
    +  lda sntp_break_polling_loop
    +  bne @break_polling_loop
    +  jsr timer_read
    +  cpx sntp_timer            ;this will tick over after about 1/4 of a second
    +  beq @inner_delay_loop
    +  
    +  dec sntp_loop_count
    +  bne @outer_delay_loop  
    +
    +@break_polling_loop:
    +  jsr send_sntp_query  
    +  inc sntp_message_sent_count
    +  lda sntp_message_sent_count
    +  cmp #MAX_SNTP_MESSAGES_SENT-1
    +  bpl @too_many_messages_sent
    +  jmp @sntp_polling_loop
    +  
    +@complete:
    +
    +  ldax #sntp_client_port  
    +  jsr udp_remove_listener  
    +  rts
    +
    +@too_many_messages_sent:
    +@failed:
    +  ldax #sntp_client_port
    +  jsr udp_remove_listener
    +  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
    +  sta ip65_error  
    +  sec             ;signal an error
    +  rts
    +
    +
    +
    +send_sntp_query:  
    +
    +  ;make a zero filled buffer
    +  lda #$0
    +  ldx #$30
    +  stx udp_send_len
    +  sta udp_send_len+1
    +:
    +  sta output_buffer,x
    +  dex
    +  bpl :-
    +
    +  ;set the flags field
    +  lda #$E3  ;  flags -  LI=11 (unknown), VN=100 (4), MODE=011 (client)
    +  sta output_buffer
    +    
    +  ldax #sntp_client_port
    +  stax udp_send_src_port
    +  ldax #sntp_server_port
    +  stax udp_send_dest_port
    +  ldx #3        ; set destination address
    +: lda sntp_ip,x
    +  sta udp_send_dest,x
    +  dex
    +  bpl :-
    +
    +  ldax #output_buffer
    +  jsr udp_send  
    +  bcs @error_on_send
    +  lda #sntp_query_sent
    +  sta sntp_state
    +@error_on_send:  
    +  rts
    +
    +
    +sntp_in:
    +  
    +  ldx #3
    +  ldy #0
    +:
    +  lda sntp_inp+$28,x  ;the 'transmit' timestamp (in big end order)
    +  sta sntp_utc_timestamp,y
    +  iny
    +  dex
    +  bpl :-
    +  
    +  inc sntp_break_polling_loop
    +  lda #sntp_completed
    +  sta sntp_state 
    +  rts
    +
    +
    +;-- LICENSE FOR sntp.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009,2011
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_string_utils_s.html b/docs/ip65_string_utils_s.html new file mode 100644 index 0000000..d7e40bc --- /dev/null +++ b/docs/ip65_string_utils_s.html @@ -0,0 +1,118 @@ +

    ip65 technical reference

    File : ip65/string_utils.s

    text file parsing routines
    +

    functions

    functiondescription
    parse_hex_digits
    parse_integer
    parses a string, returns integer (up to 16 bits)
    +inputs: AX points to a string containing an integer
    +outputs: AX contains integer
    

    implementation

    ;text file parsing routines
    +
    +.export parse_integer
    +.export parse_hex_digits
    +
    +.importzp copy_dest
    +
    +.import mul_8_16
    +.importzp acc16
    +  
    +
    +target_string=copy_dest
    +
    +.include "../inc/common.i"
    +
    +.bss
    +temp_value: .res 2
    +
    +
    +.code
    +;parses a string, returns integer (up to 16 bits)
    +;inputs: AX points to a string containing an integer
    +;outputs: AX contains integer
    +parse_integer:
    +      
    +  stax  target_string
    +  lda #0
    +  sta temp_value
    +  sta temp_value+1
    +  tay
    +@parse_int:
    +  lda (target_string),y
    +  cmp #$30
    +  bcc @end_of_int  ;any non-decimal char should be treated as end of integer   
    +  cmp #$3A
    +  bcs @end_of_int  ;any non-decimal char should be treated as end of integer 
    +   
    +  ldax  temp_value
    +  stax  acc16
    +  lda #10
    +  jsr mul_8_16
    +  ldax  acc16
    +  stax  temp_value
    +  lda (target_string),y
    +  sec
    +  sbc #'0'
    +  clc
    +  adc temp_value
    +  sta temp_value
    +  bcc @no_rollover  
    +  inc temp_value+1
    +@no_rollover:
    +  iny
    +  bne @parse_int
    +@end_of_int:
    +  ldax temp_value
    +  clc
    +  rts
    +
    +
    +parse_hex_digits:
    +;parses 2 hex digits, returns a byte
    +;inputs: X contains high nibble char, A contains low nibble char
    +;outputs: A contains byte
    +  pha
    +  txa
    +  jsr parse_1_digit
    +  asl
    +  asl
    +  asl
    +  asl
    +  sta temp_value
    +  
    +  pla
    +  jsr parse_1_digit
    +  clc
    +  adc temp_value
    +  rts
    +  
    +parse_1_digit:
    +  cmp #$3A
    +  
    +  bcs @not_digit
    +  sec
    +  sbc #$30
    +  rts
    +@not_digit:
    +  ora #$20  ;make lower case
    +  sec
    +  sbc #'a'-10
    +  rts
    +
    +
    +
    +
    +
    +;-- LICENSE FOR string_utils.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_tcp_s.html b/docs/ip65_tcp_s.html new file mode 100644 index 0000000..4b1bed0 --- /dev/null +++ b/docs/ip65_tcp_s.html @@ -0,0 +1,1140 @@ +

    ip65 technical reference

    File : ip65/tcp.s

    TCP (transmission control protocol) functions
    +NB to use these functions, you must pass "-DTCP" to ca65 when assembling "ip.s"
    +otherwise inbound tcp packets won't get passed in to tcp_process 
    +currently only a single outbound (client) connection is supported
    +to use, first call "tcp_connect" to create a connection. to send data on that connection, call "tcp_send". 
    +whenever data arrives, a call will be made to the routine pointed at by tcp_callback.
    +

    functions

    functiondescription
    tcp_close
    tcp_connect
    make outbound tcp connection
    +inputs:
    + tcp_connect_ip:  destination ip address (4 bytes)
    + AX: destination port (2 bytes)
    + tcp_callback: vector to call when data arrives on this connection
    +outputs:
    +   carry flag is set if an error occured, clear otherwise
    
    tcp_init
     initialize tcp
    +called automatically by ip_init if "ip.s" was compiled with -DTCP
    + inputs: none
    + outputs: none
    
    tcp_listen
    listen for an inbound tcp connection
    +this is a 'blocking' call, i.e. it will not return until a connection has been made
    +inputs:
    + AX: destination port (2 bytes)
    + tcp_callback: vector to call when data arrives on this connection
    +outputs:
    +   carry flag is set if an error occured, clear otherwise
    
    tcp_process
    process incoming tcp packet
    +called automatically by ip_process if "ip.s" was compiled with -DTCP
    +inputs:
    + eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet
    +outputs:
    + none but if connection was found, an outbound message may be created, overwriting eth_outp
    + also tcp_state and other tcp variables may be modified
    
    tcp_send
    send tcp data
    +inputs:
    +   tcp connection should already be opened
    +   tcp_send_data_len: length of data to send (exclusive of any headers)
    +   AX: pointer to buffer containing data to be sent
    +outputs:
    +   carry flag is set if an error occured, clear otherwise  
    
    tcp_send_keep_alive
    send an empty ACK packet on the current connection
    +inputs:
    +   none
    +outputs:
    +   carry flag is set if an error occured, clear otherwise
    
    tcp_send_string
    send a string over the current tcp connection
    +inputs:
    +   tcp connection should already be opened
    +   AX: pointer to buffer - data up to (but not including)
    + the first nul byte will be sent. max of 255 bytes will be sent.
    +outputs:
    +   carry flag is set if an error occured, clear otherwise
    

    variables

    variabledescriptionsize (bytes)
    tcp_callbackvector to routine to be called when data is received over tcp connection 2
    tcp_connect_ipip address of remote server to connect to 4
    tcp_connect_remote_port2
    tcp_inbound_data_lengthlength of data just received over tcp connection 2
    tcp_inbound_data_ptrpointer to data just recieved over tcp connection 2
    tcp_send_data_lenlength (in bytes) of data to be sent over tcp connection 2
    tcp_state1

    constants

    constantsdescriptionvalue
    tcp_remote_ip

    implementation

    ;TCP (transmission control protocol) functions
    +;NB to use these functions, you must pass "-DTCP" to ca65 when assembling "ip.s"
    +;otherwise inbound tcp packets won't get passed in to tcp_process 
    +;currently only a single outbound (client) connection is supported
    +;to use, first call "tcp_connect" to create a connection. to send data on that connection, call "tcp_send". 
    +;whenever data arrives, a call will be made to the routine pointed at by tcp_callback.
    +
    +
    +MAX_TCP_PACKETS_SENT=8     ;timeout after sending 8 messages will be about 7 seconds (1+2+3+4+5+6+7+8)/4
    +
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +.import ip65_error
    +
    +.export tcp_init
    +.export tcp_process
    +.export tcp_connect
    +.export tcp_callback
    +.export tcp_connect_ip
    +.export tcp_send_data_len
    +.export tcp_send
    +.export tcp_send_string
    +.export tcp_close
    +.export tcp_listen
    +.export tcp_send_keep_alive
    +.export tcp_connect_remote_port
    +.export tcp_remote_ip
    +.export tcp_state
    +.export tcp_inbound_data_ptr
    +.export tcp_inbound_data_length
    +
    +
    +.import ip_calc_cksum
    +.import ip_send
    +.import ip_create_packet
    +.import ip_inp
    +.import ip_outp
    +.import ip65_process
    +
    +.import check_for_abort_key
    +.import timer_read
    +.import ip65_random_word
    +
    +.importzp acc32
    +.importzp op32
    +.importzp acc16
    +
    +.import add_32_32
    +.import add_16_32
    +.import cmp_32_32
    +.import cmp_16_16
    +.import sub_16_16
    +
    +
    +
    +.importzp ip_cksum_ptr
    +.importzp ip_header_cksum
    +.importzp ip_src
    +.importzp ip_dest
    +.importzp ip_data
    +.importzp ip_proto
    +.importzp ip_proto_tcp
    +.importzp ip_id
    +.importzp ip_len
    +
    +.import copymem
    +.importzp copy_src
    +.importzp copy_dest
    +
    +.import cfg_ip
    +
    +tcp_cxn_state_closed      = 0 
    +tcp_cxn_state_listening   = 1  ;(waiting for an inbound SYN)
    +tcp_cxn_state_syn_sent    = 2  ;(waiting for an inbound SYN/ACK)
    +tcp_cxn_state_established = 3  ;  
    +
    +; tcp packet offsets
    +tcp_inp    = ip_inp + ip_data  ;pointer to tcp packet inside inbound ethernet frame
    +tcp_outp  = ip_outp + ip_data ;pointer to tcp packet inside outbound ethernet frame
    +tcp_src_port  = 0 ;offset of source port field in tcp packet
    +tcp_dest_port  = 2 ;offset of destination port field in tcp packet
    +tcp_seq    = 4 ;offset of sequence number field in tcp packet
    +tcp_ack  = 8 ;offset of acknowledgement field in tcp packet
    +tcp_header_length  = 12 ;offset of header length field in tcp packet
    +tcp_flags_field  = 13 ;offset of flags field in tcp packet
    +tcp_window_size = 14 ; offset of window size field in tcp packet
    +tcp_checksum = 16 ; offset of checksum field in tcp packet
    +tcp_urgent_pointer = 18 ; offset of urgent pointer field in tcp packet
    +tcp_data=20   ;offset of data in tcp packet 
    +
    +; virtual header
    +tcp_vh    = tcp_outp - 12
    +tcp_vh_src  = 0
    +tcp_vh_dest  = 4
    +tcp_vh_zero  = 8
    +tcp_vh_proto  = 9
    +tcp_vh_len  = 10
    +
    +;
    +tcp_flag_FIN  =1
    +tcp_flag_SYN  =2
    +tcp_flag_RST  =4
    +tcp_flag_PSH  =8
    +tcp_flag_ACK  =16
    +tcp_flag_URG  =32
    +
    +
    +
    +
    +.segment "TCP_VARS"
    +tcp_state:  .res 1
    +tcp_local_port: .res 2
    +tcp_remote_port: .res 2
    +tcp_remote_ip: .res 4
    +tcp_sequence_number: .res 4
    +tcp_ack_number: .res 4
    +tcp_data_ptr: .res 2
    +tcp_data_len: .res 2
    +tcp_send_data_ptr: .res 2
    +tcp_send_data_len: .res 2 ;length (in bytes) of data to be sent over tcp connection
    +tcp_callback: .res 2 ;vector to routine to be called when data is received over tcp connection
    +tcp_flags: .res 1
    +tcp_fin_sent: .res 1
    +
    +tcp_listen_port: .res 2
    +
    +tcp_inbound_data_ptr: .res 2 ;pointer to data just recieved over tcp connection
    +tcp_inbound_data_length: .res 2 ;length of data just received over tcp connection
    +;(if this is $ffff, that means "end of file", i.e. remote end has closed connection)
    +tcp_connect_sequence_number: .res 4   ;the seq number we will next send out
    +tcp_connect_expected_ack_number: .res 4 ;what we expect to see in the next inbound ack
    +tcp_connect_ack_number: .res 4 ;what we will next ack
    +tcp_connect_last_received_seq_number: .res 4 ;the seq field in the last inbound packet for this connection
    +tcp_connect_last_ack: .res 4 ;ack field in the last inbound packet for this connection
    +tcp_connect_local_port: .res 2 ;
    +tcp_connect_remote_port: .res 2
    +tcp_connect_ip: .res 4 ;ip address of remote server to connect to
    +
    +
    +tcp_timer:  .res 1
    +tcp_loop_count: .res 1
    +tcp_packet_sent_count: .res 1
    +
    +
    +.code
    +
    +; initialize tcp
    +;called automatically by ip_init if "ip.s" was compiled with -DTCP
    +; inputs: none
    +; outputs: none
    +tcp_init:
    +  
    +  rts
    +
    +
    +jmp_to_callback:
    +  jmp (tcp_callback)
    +
    +;listen for an inbound tcp connection
    +;this is a 'blocking' call, i.e. it will not return until a connection has been made
    +;inputs:
    +; AX: destination port (2 bytes)
    +; tcp_callback: vector to call when data arrives on this connection
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +tcp_listen:
    +  stax  tcp_listen_port
    +  lda #tcp_cxn_state_listening
    +  sta tcp_state
    +  lda #0  ;reset the "packet sent" counter
    +  sta tcp_packet_sent_count
    +  sta tcp_fin_sent
    +  
    +  ;set the low word of seq number to $0000, high word to something random
    +  sta tcp_connect_sequence_number
    +  sta tcp_connect_sequence_number+1
    +  jsr ip65_random_word
    +  stax  tcp_connect_sequence_number+2
    +  jsr set_expected_ack;       ;due to various ugly hacks, the 'expected ack' value is now what is put into the 'SEQ' field in outbound packets 
    +@listen_loop:
    +  jsr ip65_process
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  rts
    +@no_abort:  
    +  lda #tcp_cxn_state_listening  
    +  cmp tcp_state
    +  beq @listen_loop
    +    
    +  jmp tcp_connection_established
    +  rts
    +
    +;make outbound tcp connection
    +;inputs:
    +; tcp_connect_ip:  destination ip address (4 bytes)
    +; AX: destination port (2 bytes)
    +; tcp_callback: vector to call when data arrives on this connection
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +tcp_connect:
    +  stax  tcp_connect_remote_port
    +  jsr ip65_random_word
    +  stax  tcp_connect_local_port
    +  lda #tcp_cxn_state_syn_sent
    +  sta tcp_state
    +  lda #0  ;reset the "packet sent" counter
    +  sta tcp_packet_sent_count
    +  sta tcp_fin_sent
    +  
    +  ;set the low word of seq number to $0000, high word to something random
    +  sta tcp_connect_sequence_number
    +  sta tcp_connect_sequence_number+1
    +  jsr ip65_random_word
    +  stax  tcp_connect_sequence_number+2
    +  
    +  
    +@tcp_polling_loop:
    +
    +  ;create a SYN packet
    +  lda #tcp_flag_SYN
    +  sta tcp_flags
    +  lda  #0
    +  sta  tcp_data_len
    +  sta  tcp_data_len+1
    +  
    +  ldx #3        ; 
    +:  lda tcp_connect_ip,x
    +  sta tcp_remote_ip,x
    +  lda tcp_connect_sequence_number,x
    +  sta tcp_sequence_number,x
    +  dex
    +  bpl :-
    +  ldax  tcp_connect_local_port
    +  stax  tcp_local_port  
    +  ldax  tcp_connect_remote_port
    +  stax  tcp_remote_port
    +  
    +  jsr tcp_send_packet
    +  lda tcp_packet_sent_count
    +  adc #1
    +  sta tcp_loop_count       ;we wait a bit longer between each resend  
    +@outer_delay_loop: 
    +  jsr timer_read
    +  stx tcp_timer            ;we only care about the high byte  
    +@inner_delay_loop:  
    +  jsr ip65_process
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  rts
    +@no_abort:  
    +  lda tcp_state  
    +  cmp #tcp_cxn_state_syn_sent
    +  bne @got_a_response
    +
    +  jsr timer_read
    +  cpx tcp_timer            ;this will tick over after about 1/4 of a second
    +  beq @inner_delay_loop
    +  
    +  dec tcp_loop_count
    +  bne @outer_delay_loop  
    +
    +  
    +  inc tcp_packet_sent_count
    +  lda tcp_packet_sent_count
    +  cmp #MAX_TCP_PACKETS_SENT-1
    +  bpl @too_many_messages_sent
    +  jmp @tcp_polling_loop
    +
    +@too_many_messages_sent:
    +@failed:
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
    +  sta ip65_error  
    +  sec             ;signal an error
    +  rts
    +@got_a_response:
    +  lda tcp_state  
    +  cmp #tcp_cxn_state_closed
    +  bne @was_accepted
    +  sec     ;if we got here, then the other side sent a RST or FIN, so signal an error to the caller
    +  rts
    +@was_accepted:
    +tcp_connection_established:
    +;inc the sequence number to cover the SYN we have sent
    +  ldax  #tcp_connect_sequence_number
    +  stax  acc32
    +  ldax  #$01
    +  jsr add_16_32
    +
    +set_expected_ack:
    +;set the expected ack number with current seq number
    +  ldx #3        ; 
    +:  lda tcp_connect_sequence_number,x
    +  sta tcp_connect_expected_ack_number,x
    +  dex
    +  bpl :-
    +
    +  clc
    +  rts
    +
    +tcp_close:
    +;close the current connection
    +;inputs:
    +;   none
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +
    +
    +  lda tcp_state
    +  cmp #tcp_cxn_state_established
    +  beq :+
    +@connection_closed:  
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  clc
    +  rts
    +:  
    +  ;increment the expected sequence number for the SYN we are about to send
    +  ldax #tcp_connect_expected_ack_number
    +  stax acc32
    +  ldax #1
    +  sta tcp_fin_sent
    +  jsr add_16_32
    +
    +
    +@send_fin_loop:
    +  lda #tcp_flag_FIN+tcp_flag_ACK
    +  sta tcp_flags
    +  ldax  #0
    +  stax  tcp_data_len
    +  ldx #3        ; 
    +:  lda tcp_connect_ip,x
    +  sta tcp_remote_ip,x
    +  lda tcp_connect_ack_number,x
    +  sta tcp_ack_number,x
    +  lda tcp_connect_sequence_number,x
    +  sta tcp_sequence_number,x
    +  dex
    +  bpl :-
    +  ldax  tcp_connect_local_port
    +  stax  tcp_local_port  
    +  ldax  tcp_connect_remote_port
    +  stax  tcp_remote_port  
    +  
    +  jsr tcp_send_packet
    +
    +  lda tcp_packet_sent_count
    +  adc #1
    +  sta tcp_loop_count       ;we wait a bit longer between each resend  
    +@outer_delay_loop: 
    +  jsr timer_read
    +  stx tcp_timer            ;we only care about the high byte  
    +@inner_delay_loop:  
    +  jsr ip65_process
    +  lda tcp_state
    +  cmp #tcp_cxn_state_established
    +  bne @connection_closed
    +
    +  jsr timer_read
    +  cpx tcp_timer            ;this will tick over after about 1/4 of a second
    +  beq @inner_delay_loop
    +  
    +  dec tcp_loop_count
    +  bne @outer_delay_loop  
    +  
    +  inc tcp_packet_sent_count
    +  lda tcp_packet_sent_count
    +  cmp #MAX_TCP_PACKETS_SENT-1
    +  bpl @too_many_messages_sent
    +  jmp @send_fin_loop
    +@too_many_messages_sent:
    +@failed:
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
    +  sta ip65_error  
    +  sec             ;signal an error
    +  rts
    +
    +
    +
    +;send a string over the current tcp connection
    +;inputs:
    +;   tcp connection should already be opened
    +;   AX: pointer to buffer - data up to (but not including)
    +; the first nul byte will be sent. max of 255 bytes will be sent.
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +tcp_send_string:
    +  stax tcp_send_data_ptr
    +  stax copy_src
    +  lda #0
    +  tay
    +  sta tcp_send_data_len
    +  sta tcp_send_data_len+1
    +  lda (copy_src),y
    +  bne @find_end_of_string
    +  rts ; if the string is empty, don't send anything!
    +@find_end_of_string:  
    +  lda (copy_src),y
    +  beq @done  
    +  inc tcp_send_data_len
    +  iny
    +  bne @find_end_of_string
    +@done:  
    +  ldax tcp_send_data_ptr
    +  ;now we can fall through into tcp_send
    +  
    +
    +;send tcp data
    +;inputs:
    +;   tcp connection should already be opened
    +;   tcp_send_data_len: length of data to send (exclusive of any headers)
    +;   AX: pointer to buffer containing data to be sent
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise  
    +tcp_send:
    +
    +  stax tcp_send_data_ptr
    +  
    +  lda tcp_state
    +  cmp #tcp_cxn_state_established
    +  beq @connection_established
    +  lda #KPR_ERROR_CONNECTION_CLOSED
    +  sta ip65_error
    +  sec
    +  rts
    +  lda #0  ;reset the "packet sent" counter
    +  sta tcp_packet_sent_count
    +
    +@connection_established:
    +  ;increment the expected sequence number
    +  ldax #tcp_connect_expected_ack_number
    +  stax acc32
    +  ldax tcp_send_data_len
    +  jsr add_16_32
    +  
    +
    +@tcp_polling_loop:
    +
    +  ;create a data packet
    +  lda #tcp_flag_ACK+tcp_flag_PSH
    +  sta tcp_flags
    +  ldax tcp_send_data_len
    +  stax tcp_data_len
    +  
    +  ldax tcp_send_data_ptr
    +  stax tcp_data_ptr
    +  
    +  ldx #3        ; 
    +:  lda tcp_connect_ip,x
    +  sta tcp_remote_ip,x
    +  lda tcp_connect_sequence_number,x
    +  sta tcp_sequence_number,x
    +
    +  dex
    +  bpl :-
    +  ldax  tcp_connect_local_port
    +  stax  tcp_local_port  
    +  ldax  tcp_connect_remote_port
    +  stax  tcp_remote_port
    +  
    +  
    +  jsr tcp_send_packet
    +  lda tcp_packet_sent_count
    +  adc #1
    +  sta tcp_loop_count       ;we wait a bit longer between each resend  
    +@outer_delay_loop: 
    +  jsr timer_read
    +  stx tcp_timer            ;we only care about the high byte  
    +@inner_delay_loop:  
    +  jsr ip65_process
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  
    +  rts
    +@no_abort:  
    +  ldax #tcp_connect_last_ack
    +  stax acc32
    +  ldax #tcp_connect_expected_ack_number
    +  stax op32
    +  jsr cmp_32_32
    +  beq @got_ack
    +
    +  jsr timer_read
    +  cpx tcp_timer            ;this will tick over after about 1/4 of a second
    +  beq @inner_delay_loop
    +  
    +  dec tcp_loop_count
    +  bne @outer_delay_loop  
    +
    +  
    +  inc tcp_packet_sent_count
    +  lda tcp_packet_sent_count
    +  cmp #MAX_TCP_PACKETS_SENT-1
    +  bpl @too_many_messages_sent
    +  jmp @tcp_polling_loop
    +
    +@too_many_messages_sent:
    +@failed:
    +
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
    +  sta ip65_error  
    +  sec             ;signal an error
    +  rts
    +@got_ack: 
    +  ;finished - now we need to advance the sequence number for the data we just sent
    +  ldax #tcp_connect_sequence_number
    +  stax acc32
    +  ldax tcp_send_data_len
    +  jsr add_16_32
    +
    +  clc
    +  rts
    +
    +
    +;send a single tcp packet 
    +;inputs:
    +; tcp_remote_ip: IP address of destination server
    +; tcp_remote_port: destination tcp port 
    +; tcp_local_port: source tcp port
    +; tcp_flags: 6 bit flags
    +; tcp_data_ptr: pointer to data to include in this packet
    +; tcp_data_len: length of data pointed at by tcp_data_ptr
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +tcp_send_packet:
    +  ldax  tcp_data_ptr
    +  stax copy_src      ; copy data to output buffer
    +  ldax #tcp_outp + tcp_data
    +  stax copy_dest
    +  ldax tcp_data_len
    +  jsr copymem
    +
    +  ldx #3        ; copy virtual header addresses
    +:  lda tcp_remote_ip,x
    +  sta tcp_vh + tcp_vh_dest,x  ; set virtual header destination
    +  lda cfg_ip,x
    +  sta tcp_vh + tcp_vh_src,x  ; set virtual header source
    +  dex
    +  bpl :-
    +
    +  lda tcp_local_port    ; copy source port
    +  sta tcp_outp + tcp_src_port + 1
    +  lda tcp_local_port + 1
    +  sta tcp_outp + tcp_src_port
    +
    +  lda tcp_remote_port    ; copy destination port
    +  sta tcp_outp + tcp_dest_port + 1
    +  lda tcp_remote_port + 1
    +  sta tcp_outp + tcp_dest_port
    +
    +  ldx #3        ; copy sequence and ack (if ACK flag set) numbers (in reverse order)
    +  ldy #0
    +:  lda tcp_sequence_number,x
    +  sta tcp_outp + tcp_seq,y
    +  lda #tcp_flag_ACK
    +  bit tcp_flags
    +  bne @ack_set 
    +  lda #0
    +  beq @sta_ack
    +  @ack_set:
    +  lda tcp_ack_number,x
    +  @sta_ack:
    +  sta tcp_outp + tcp_ack,y
    +  iny
    +  dex
    +  bpl :-
    +
    +  lda #$50    ;4 bit header length in 32bit words + 4 bits of zero
    +  sta tcp_outp+tcp_header_length
    +  lda tcp_flags
    +  sta tcp_outp+tcp_flags_field
    +  
    +  lda #ip_proto_tcp
    +  sta tcp_vh + tcp_vh_proto
    +
    +  ldax  #$0010  ;$1000 in network byte order
    +  stax  tcp_outp+tcp_window_size
    +
    +  lda #0        ; clear checksum
    +  sta tcp_outp + tcp_checksum
    +  sta tcp_outp + tcp_checksum + 1
    +  sta tcp_vh + tcp_vh_zero  ; clear virtual header zero byte
    +
    +  ldax #tcp_vh      ; checksum pointer to virtual header
    +  stax ip_cksum_ptr
    +
    +  lda tcp_data_len    ; copy length + 20
    +  clc
    +  adc #20
    +  sta tcp_vh + tcp_vh_len + 1  ; lsb for virtual header
    +  tay
    +  lda tcp_data_len + 1
    +  adc #0
    +  sta tcp_vh + tcp_vh_len    ; msb for virtual header
    +
    +  tax        ; length to A/X
    +  tya
    +
    +  clc        ; add 12 bytes for virtual header
    +  adc #12
    +  bcc :+
    +  inx
    +:
    +  jsr ip_calc_cksum    ; calculate checksum
    +  stax tcp_outp + tcp_checksum
    +
    +  ldx #3        ; copy addresses
    +:  lda tcp_remote_ip,x
    +  sta ip_outp + ip_dest,x    ; set ip destination address
    +  dex
    +  bpl :-
    +
    +  jsr ip_create_packet    ; create ip packet template
    +
    +  lda tcp_data_len   ; ip len = tcp data length +20 byte ip header + 20 byte tcp header
    +  ldx tcp_data_len +1
    +  clc
    +  adc #40 
    +  bcc :+
    +  inx
    +:  sta ip_outp + ip_len + 1  ; set length
    +  stx ip_outp + ip_len
    +
    +  ldax #$1234          ; set ID
    +  stax ip_outp + ip_id
    +
    +  lda #ip_proto_tcp    ; set protocol
    +  sta ip_outp + ip_proto
    +
    +  jmp ip_send      ; send packet, sec on error
    +
    +
    +
    +check_current_connection:
    +;see if the ip packet we just got is for a valid (non-closed) tcp connection
    +;inputs:
    +; eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet
    +;outputs:
    +; carry flag clear if inbound tcp packet part of existing connection
    +
    +  
    +  lda tcp_state
    +  cmp #tcp_cxn_state_closed
    +  bne @connection_not_closed
    +  sec
    +  rts
    +@connection_not_closed:  
    +  ldax  #ip_inp+ip_src
    +  stax  acc32
    +  ldax  #tcp_connect_ip
    +  stax  op32
    +  jsr   cmp_32_32
    +  beq @remote_ip_matches
    +  
    +  sec
    +  rts
    +@remote_ip_matches:
    +  ldax  tcp_inp+tcp_src_port
    +  stax  acc16
    +  lda   tcp_connect_remote_port+1 ;this value in reverse byte order to how it is presented in the TCP header
    +  ldx   tcp_connect_remote_port 
    +  jsr   cmp_16_16
    +  beq @remote_port_matches
    +  sec
    +  rts
    +@remote_port_matches:
    +  ldax  tcp_inp+tcp_dest_port
    +  stax  acc16
    +  lda   tcp_connect_local_port+1 ;this value in reverse byte order to how it is presented in the TCP header
    +  ldx   tcp_connect_local_port 
    +  jsr   cmp_16_16
    +  beq   @local_port_matches
    +  sec
    +  rts
    +@local_port_matches:
    +  clc
    +  rts
    +  
    +;process incoming tcp packet
    +;called automatically by ip_process if "ip.s" was compiled with -DTCP
    +;inputs:
    +; eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet
    +;outputs:
    +; none but if connection was found, an outbound message may be created, overwriting eth_outp
    +; also tcp_state and other tcp variables may be modified
    +tcp_process:
    +  
    +  lda #tcp_flag_RST
    +  bit tcp_inp+tcp_flags_field
    +  beq @not_reset
    +  jsr check_current_connection
    +  bcs @not_current_connection_on_rst  
    +  ;for some reason, search.twitter.com is sending RSTs with ID=$1234 (i.e. echoing the inbound ID)
    +  ;but then keeps the connection open and ends up sending the file.
    +  ;so lets ignore a reset with ID=$1234
    +  lda ip_inp+ip_id
    +  cmp #$34
    +  bne @not_invalid_reset
    +  lda ip_inp+ip_id+1
    +  cmp #$12  
    +  bne @not_invalid_reset
    +  jmp @send_ack
    +@not_invalid_reset:
    +  ;connection has been reset so mark it as closed    
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +  lda #KPR_ERROR_CONNECTION_RESET_BY_PEER
    +  sta ip65_error
    +  
    +  lda #$ff
    +  sta tcp_inbound_data_length
    +  sta tcp_inbound_data_length+1
    +  jsr jmp_to_callback   ;let the caller see the connection has closed
    +  
    +@not_current_connection_on_rst:
    +  ;if we get a reset for a closed or nonexistent connection, then ignore it  
    +  rts
    +@not_reset:
    +  lda tcp_inp+tcp_flags_field
    +  cmp #tcp_flag_SYN+tcp_flag_ACK
    +  bne @not_syn_ack
    +  
    +  ;it's a SYN/ACK
    +  jsr check_current_connection
    +  bcc @current_connection_on_syn_ack
    +  ;if we get a SYN/ACK for something that aint the connection we're expecting, 
    +  ;terminate with extreme prejudice
    +  jmp @send_rst 
    +@current_connection_on_syn_ack:  
    +  lda tcp_state
    +  cmp #tcp_cxn_state_syn_sent
    +  bne @not_expecting_syn_ack
    +  ;this IS the syn/ack we are waiting for :-)
    +  ldx #3        ; copy sequence number to ack (in reverse order)
    +  ldy #0
    +:  lda tcp_inp + tcp_seq,y
    +  sta tcp_connect_ack_number,x
    +  iny
    +  dex
    +  bpl :-
    +
    +  ldax #tcp_connect_ack_number
    +  stax acc32
    +  ldax  #$0001  ;
    +  jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received
    +
    +
    +  lda #tcp_cxn_state_established
    +  sta tcp_state
    +  
    +@not_expecting_syn_ack:   
    +;we get a SYN/ACK for the current connection,
    +;but we're not expecting it, it's probably
    +;a retransmist - just ACK it
    +  jmp @send_ack
    +  
    +  
    +@not_syn_ack:  
    +
    +;is it an ACK - alone or with PSH/URGENT but not a SYN/ACK?
    +  lda #tcp_flag_ACK
    +  bit tcp_inp+tcp_flags_field
    +  bne @ack
    +  jmp @not_ack
    +@ack:  
    +  ;is this the current connection?
    +  jsr check_current_connection
    +  bcc @current_connection_on_ack
    +  ;if we get an ACK for something that is not the current connection
    +  ;we should send a RST
    +  jmp @send_rst
    +@current_connection_on_ack:
    +  ;if it's an ACK, then record the last ACK (reorder the bytes in the process)
    +  ldx #3        ; copy seq & ack fields (in reverse order)
    +  ldy #0
    +:  lda tcp_inp + tcp_ack,y
    +  sta tcp_connect_last_ack,x
    +  lda tcp_inp + tcp_seq,y
    +  sta tcp_connect_last_received_seq_number,x
    +  iny
    +  dex
    +  bpl :-
    +  
    +  ;was this the next sequence number we're waiting for?
    +  ldax  #tcp_connect_ack_number
    +  stax  acc32
    +  ldax  #tcp_connect_last_received_seq_number
    +  stax  op32
    +  jsr   cmp_32_32
    +  
    +  bne   @not_expected_seq_number
    +
    +
    +  
    +  ;what is the size of data in this packet?
    +  lda ip_inp+ip_len+1 ;payload length (lo byte)
    +  sta acc16
    +  lda ip_inp+ip_len ;payload length (hi byte)
    +  sta acc16+1
    +  lda tcp_inp+tcp_header_length   ;high 4 bits is header length in 32 bit words
    +  lsr ; A=A/2
    +  lsr ; A=A/2
    +  clc ; A now equal to tcp header length in bytes
    +  adc #20 ;add 20 bytes for IP header. this gives length of IP +TCP headers
    +  ldx #0
    +  sta tcp_header_length
    +  jsr sub_16_16
    +  
    +  ;acc16 now contains the length of data in this TCP packet
    +  
    +  lda acc16
    +  sta tcp_inbound_data_length
    +  lda acc16+1
    +  sta tcp_inbound_data_length+1
    +  bne @not_empty_packet
    +  lda acc16
    +  bne @not_empty_packet
    +  jmp @empty_packet  
    +@not_empty_packet:
    +  
    +  
    +  ;calculate ptr to tcp data
    +  clc
    +  lda tcp_header_length
    +  adc #ip_inp
    +  adc #0
    +  sta tcp_inbound_data_ptr+1
    +  
    +  ;  do a callback
    +  jsr jmp_to_callback
    +  
    +    
    +  ; move ack ptr along
    +  ldax #tcp_connect_ack_number
    +  stax acc32
    +  ldax tcp_inbound_data_length
    +  jsr add_16_32 
    +  
    +  
    +@not_expected_seq_number: ;send an ACK with the sequence number we expect  
    +
    +  ;send the ACK for any data in this packet, then return to check for FIN flag
    +  jsr @send_ack 
    +
    +@not_ack: 
    +@empty_packet:  
    +
    +;is it a FIN?  
    +  lda #tcp_flag_FIN
    +  bit tcp_inp+tcp_flags_field
    +  bne @fin
    +  jmp @not_fin
    +@fin:  
    +  ;is this the current connection?
    +  jsr check_current_connection
    +  bcc :+
    +  jmp @send_rst ;reset if not current connection
    +:  
    +  ldx #3        ; copy seq field (in reverse order)
    +  ldy #0
    +:  lda tcp_inp + tcp_seq,y
    +  sta tcp_connect_last_received_seq_number,x
    +  iny
    +  dex
    +  bpl :-
    +  
    +  ;was this the next sequence number we're waiting for?
    +  ldax  #tcp_connect_ack_number
    +  stax  acc32
    +  ldax  #tcp_connect_last_received_seq_number
    +  stax  op32
    +  jsr   cmp_32_32
    +  
    +  beq :+
    +  rts ;bail if not expected sequence number
    +:  
    +
    +  ;set the length to $ffff
    +  lda #$ff
    +  sta tcp_inbound_data_length
    +  sta tcp_inbound_data_length+1
    +  jsr jmp_to_callback   ;let the caller see the connection has closed   
    +    
    +
    +  lda #tcp_cxn_state_closed
    +  sta tcp_state
    +
    +  ;send a FIN/ACK
    +  ; move ack ptr along for the inbound FIN
    +  ldax #tcp_connect_ack_number
    +  stax acc32
    +  ldax #$01
    +  sta  tcp_fin_sent
    +  jsr add_16_32
    +
    +  ;if we've already sent a FIN then just send back an ACK 
    +  lda tcp_fin_sent
    +  beq @send_fin_ack
    +;if we get here, we've sent a FIN, and just received an inbound FIN.
    +;when we sent the fin, we didn't update the sequence number, since
    +;we want to use the old sequence on every resend of that FIN
    +;now that our fin has been ACKed, we need to inc the sequence number
    +;and then send another ACK.
    +
    +  ldax #tcp_connect_sequence_number
    +  stax acc32
    +  ldax  #$0001  ;
    +  jsr add_16_32 ;increment the SEQ counter by 1, for the FIN we have been sending
    +
    +  lda #tcp_flag_ACK  
    +  jmp @send_packet
    +  
    +@send_fin_ack:  
    + 
    +  lda #tcp_flag_FIN+tcp_flag_ACK
    +  
    +  jmp @send_packet
    +
    +  
    +@not_fin:
    +
    +  lda tcp_inp+tcp_flags_field
    +  cmp #tcp_flag_SYN
    +  beq @syn 
    +  jmp @not_syn
    +@syn:  
    +  
    +  ;is this the port we are listening on?
    +  lda tcp_inp+tcp_dest_port+1
    +  cmp tcp_listen_port
    +  bne @decline_syn_with_reset
    +  lda tcp_inp+tcp_dest_port
    +  cmp tcp_listen_port+1
    +  bne @decline_syn_with_reset
    +  
    +  ;it's the right port - are we actually waiting for a connecting?
    +  lda #tcp_cxn_state_listening  
    +  cmp tcp_state  
    +  beq @this_is_connection_we_are_waiting_for
    +  ;is this the current connection? that would mean our ACK got lost, so resend
    +  jsr check_current_connection
    +  bcc @this_is_connection_we_are_waiting_for
    +  
    +  rts ;if we've currently got a connection open, then ignore any new requests
    +      ;the sender will timeout and resend the SYN, by which time we may be
    +      ;ready to accept it again.
    +  
    +@this_is_connection_we_are_waiting_for:
    +
    +  ; copy sequence number to ack (in reverse order) and remote IP
    +  ldx #3        
    +  ldy #0
    +:  lda tcp_inp + tcp_seq,y
    +  sta tcp_connect_ack_number,x
    +  lda ip_inp+ip_src,x
    +  sta tcp_connect_ip,x
    +  iny
    +  dex
    +  bpl :-
    +
    +  ;copy ports
    +  ldax tcp_listen_port
    +  stax tcp_connect_local_port
    +  
    +  lda tcp_inp+tcp_src_port+1
    +  sta tcp_connect_remote_port
    +  lda tcp_inp+tcp_src_port
    +  sta tcp_connect_remote_port+1
    +
    +  lda #tcp_cxn_state_established
    +  sta tcp_state
    +
    +  ldax #tcp_connect_ack_number 
    +  stax acc32
    +  ldax  #$0001  ;
    +  jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received
    +  lda #tcp_flag_SYN+tcp_flag_ACK
    +  jmp @send_packet
    +  
    +@decline_syn_with_reset:
    +;create a RST packet
    +  ldx #3        ; copy sequence number to ack (in reverse order)
    +  ldy #0
    +:  lda tcp_inp + tcp_seq,y
    +  sta tcp_ack_number,x
    +  iny
    +  dex
    +  bpl :-
    +
    +  ldax #tcp_ack_number 
    +  stax acc32
    +  ldax  #$0001  ;
    +  jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received
    +  
    +@send_rst:
    +  
    +  lda #tcp_flag_RST+tcp_flag_ACK
    +  sta tcp_flags
    +  ldax  #0
    +  stax  tcp_data_len
    +  ldx #3        ; 
    +:  lda ip_inp+ip_src,x
    +  sta tcp_remote_ip,x
    +  dex
    +  bpl :-
    +  
    +  ;copy src/dest ports in inverted byte order
    +  lda tcp_inp+tcp_src_port
    +  sta tcp_remote_port+1
    +  lda tcp_inp+tcp_src_port+1
    +  sta tcp_remote_port
    +  
    +  lda tcp_inp+tcp_dest_port
    +  sta tcp_local_port+1
    +  lda tcp_inp+tcp_dest_port+1
    +  sta tcp_local_port
    +  
    +  jsr tcp_send_packet
    +  rts
    +
    +@not_syn:
    +  rts
    +
    +@send_ack:
    +
    +;create an ACK packet
    +  lda #tcp_flag_ACK
    +  
    +@send_packet:  
    +  sta tcp_flags
    +  ldax  #0
    +  stax  tcp_data_len
    +  ldx #3        ; 
    +:  lda tcp_connect_ip,x
    +  sta tcp_remote_ip,x
    +  lda tcp_connect_ack_number,x
    +  sta tcp_ack_number,x
    +;if we have just sent a packet out, we may not yet have updated tcp_connect_sequence_number yet
    +;so use current value of tcp_connect_expected_ack_number as outbound sequence number instead
    +  lda tcp_connect_expected_ack_number,x   
    +  sta tcp_sequence_number,x
    +  dex
    +  bpl :-
    +  ldax  tcp_connect_local_port
    +  stax  tcp_local_port  
    +  ldax  tcp_connect_remote_port
    +  stax  tcp_remote_port
    +  
    +  
    +  jmp tcp_send_packet
    +
    +
    +;send an empty ACK packet on the current connection
    +;inputs:
    +;   none
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +tcp_send_keep_alive=@send_ack
    +
    +;-- LICENSE FOR tcp.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_telnet_s.html b/docs/ip65_telnet_s.html new file mode 100644 index 0000000..5562291 --- /dev/null +++ b/docs/ip65_telnet_s.html @@ -0,0 +1,511 @@ +

    ip65 technical reference

    File : ip65/telnet.s

    minimal telnet implementation (dumb terminal emulation only)
    +to use:
    +set the following variables - telnet_use_native_charset,telnet_port,telnet_ip
    +then call telnet_connect
    +you must also define (and export) these function
    + telnet_menu - called whenever the F1 key is pressed.
    + telnet_on_connection - called after succesful connection
    +

    functions

    functiondescription
    telnet_connect
    connect to a remote telnet server
    +inputs:
    +telnet_use_native_charset: set to 0 if remote server uses standard ASCII, 1 if remote server uses the 'native' charset (i.e. PETSCII)
    +telnet_port: port number to connect to
    +telnet_ip: ip address of remote server
    

    variables

    variabledescriptionsize (bytes)
    telnet_ipip address of remote server 4
    telnet_portport number to connect to 2
    telnet_use_native_charset 0 means all data is translated to/from NVT ASCII 1

    implementation

    ;minimal telnet implementation (dumb terminal emulation only)
    +;to use:
    +;set the following variables - telnet_use_native_charset,telnet_port,telnet_ip
    +;then call telnet_connect
    +;you must also define (and export) these function
    +; telnet_menu - called whenever the F1 key is pressed.
    +; telnet_on_connection - called after succesful connection
    +
    +.include "../inc/common.i"
    +
    +  .import tcp_connect
    +  .import tcp_callback
    +  .import tcp_connect_ip
    +  .import tcp_listen
    +  .importzp KEYCODE_F1
    +  .import tcp_inbound_data_ptr
    +  .import tcp_inbound_data_length
    +  .import tcp_send
    +  .import tcp_send_data_len
    +  .import tcp_close
    +  .import print_a
    +  .import print_cr
    +  .import vt100_init_terminal
    +  .import vt100_process_inbound_char
    +  .import vt100_transform_outbound_char
    +  .import tcp_send_keep_alive
    +  .import timer_read
    +
    +  .import ip65_process
    +  .import get_key_if_available
    +  .import get_filtered_input
    +  .import check_for_abort_key
    +  .import ok_msg
    +  .import failed_msg
    +  .import print
    +  .import print_errorcode
    +  .import native_to_ascii
    +  .import ascii_to_native
    +
    +.export telnet_connect
    +.export telnet_use_native_charset
    +.export telnet_port
    +.export telnet_ip
    +
    +.import telnet_menu
    +.import telnet_on_connection
    +
    +.segment "IP65ZP" : zeropage
    +
    +; pointer for moving through buffers
    +buffer_ptr:  .res 2      ; source pointer
    +
    +.code
    +
    +;connect to a remote telnet server
    +;inputs:
    +;telnet_use_native_charset: set to 0 if remote server uses standard ASCII, 1 if remote server uses the 'native' charset (i.e. PETSCII)
    +;telnet_port: port number to connect to
    +;telnet_ip: ip address of remote server
    +telnet_connect:
    +  lda telnet_use_native_charset
    +  bne :+
    +  jsr vt100_init_terminal
    +:  
    +  ldax #telnet_callback
    +  stax tcp_callback
    +  ldx #3
    +@copy_dest_ip:
    +  lda telnet_ip,x
    +  sta tcp_connect_ip,x
    +  dex  
    +  bpl @copy_dest_ip
    +  
    +  ldax telnet_port
    +  jsr tcp_connect
    +
    +  bcc @connect_ok 
    +  jsr print_cr
    +  ldax #failed_msg
    +  jsr print
    +  jsr print_cr
    +  jsr print_errorcode
    +  rts
    +@connect_ok:
    +  
    +  jsr telnet_on_connection
    +  
    +  ldax #ok_msg
    +  jsr print
    +  jsr print_cr
    +  lda #0
    +  sta connection_closed
    +  sta iac_response_buffer_length      
    +    
    +@main_polling_loop:
    +
    +  jsr check_for_abort_key
    +  bcc  @no_abort
    +  jsr  tcp_close
    +  jmp   @disconnected
    +  
    +@no_abort:
    +  jsr timer_read
    +  txa
    +  adc #$20  ;32 x 1/4 = ~ 8seconds
    +  sta telnet_timeout
    +@wait_for_keypress:  
    +  jsr timer_read
    +  cpx telnet_timeout
    +  bne @no_timeout
    +  jsr tcp_send_keep_alive
    +  jmp @main_polling_loop
    +@no_timeout:  
    +  jsr ip65_process
    +  lda connection_closed
    +  beq @not_disconnected
    +@disconnected:  
    +  ldax #disconnected
    +  jsr print
    +  rts
    +@not_disconnected:
    +  lda iac_response_buffer_length  
    +  beq @no_iac_response
    +  ldx #0
    +  stax tcp_send_data_len
    +  stx iac_response_buffer_length  
    +  ldax  #iac_response_buffer
    +  jsr tcp_send
    +@no_iac_response:
    +  
    +  
    +  
    +  jsr get_key_if_available
    +  beq @wait_for_keypress
    +
    +  cmp #KEYCODE_F1
    +  bne @not_telnet_menu
    +  jsr telnet_menu
    +  jmp @main_polling_loop
    +@not_telnet_menu:
    +
    +  ldx #0
    +  stx tcp_send_data_len
    +  stx tcp_send_data_len+1
    +
    +  ldx telnet_use_native_charset
    +  bne @no_conversion_required
    +  
    +  
    +  jsr vt100_transform_outbound_char
    +
    +  sta temp_a
    +  tya
    +  bne :+ 
    +  jmp @main_polling_loop  ;Y=0 means nothing to send
    +:  
    +  
    +  cmp #2
    +  beq :+
    +  lda temp_a
    +  jmp @no_conversion_required
    +:  
    +
    +
    +  lda temp_a
    +  stax buffer_ptr
    +  ldy #0  
    +:
    +  lda (buffer_ptr),y
    +  beq @send_char
    +  sta scratch_buffer,y
    +  inc tcp_send_data_len
    +  iny
    +  bne :-  
    +  jmp @send_char
    +  
    +@no_conversion_required:
    +  ldy tcp_send_data_len
    +  sta scratch_buffer,y
    +  inc tcp_send_data_len
    +  
    +@send_char:
    +
    +  ldax  #scratch_buffer
    +  jsr tcp_send
    +  bcs @error_on_send
    +  jmp @main_polling_loop
    +
    +@error_on_send:
    +  ldax #transmission_error
    +  jsr print
    +  jmp print_errorcode
    + 
    +
    +;tcp callback - will be executed whenever data arrives on the TCP connection
    +telnet_callback:
    +  
    +  lda tcp_inbound_data_length+1
    +  cmp #$ff
    +  bne @not_eof
    +  lda #1
    +  sta connection_closed
    +  rts
    +@not_eof:
    +  
    +  ldax tcp_inbound_data_ptr
    +  stax buffer_ptr
    +  lda tcp_inbound_data_length
    +  sta buffer_length
    +  lda tcp_inbound_data_length+1
    +  sta buffer_length+1
    +  
    +@next_byte:
    +  ldy #0
    +  lda (buffer_ptr),y
    +  tax
    +  lda telnet_use_native_charset
    +  beq :+
    +  jmp  @no_conversion_req
    +:
    +
    +;if we get here, we are in ASCII 'char at a time' mode,  so look for (and process) Telnet style IAC bytes
    +  lda telnet_state
    +  cmp #telnet_state_got_command
    +  bne :+
    +  jmp @waiting_for_option
    +:  
    +  cmp #telnet_state_got_iac
    +  beq @waiting_for_command
    +  cmp #telnet_state_got_suboption
    +  beq @waiting_for_suboption_end
    +; we must be in 'normal' mode
    +  txa
    +  cmp #255
    +  beq :+
    +  jmp @not_iac
    +:  
    +  lda #telnet_state_got_iac
    +  sta telnet_state
    +  jmp @byte_processed
    +
    +@waiting_for_suboption_end:
    +  txa 
    +  
    +  ldx iac_suboption_buffer_length  
    +  sta iac_suboption_buffer,x
    +  inc iac_suboption_buffer_length
    +  cmp #$f0  ;SE - suboption end
    +  bne @exit_suboption
    +
    +  lda #telnet_state_normal  
    +  sta telnet_state
    +  lda iac_suboption_buffer
    +  cmp #$18
    +  bne @not_terminal_type
    +
    +  ldx #0
    +:  
    +  lda terminal_type_response,x
    +  ldy iac_response_buffer_length
    +  inc iac_response_buffer_length
    +  sta iac_response_buffer,y
    +  inx 
    +  txa
    +  cmp #terminal_type_response_length
    +  bne :-
    +  
    +@not_terminal_type:
    +
    +@exit_suboption:
    +  jmp @byte_processed
    +@waiting_for_command:
    +  txa
    +  sta telnet_command
    +  cmp #$fa ; SB - suboption begin
    +  beq @suboption
    +  cmp #$fb ;WILL 
    +  beq @option
    +  cmp #$fc ;WONT
    +  beq @option
    +  cmp #$fd ; DO
    +  beq @option
    +  cmp #$fe ;DONT
    +  beq @option
    +;we got a command we don't understand - just ignore it  
    +  lda #telnet_state_normal  
    +  sta telnet_state
    +  jmp @byte_processed
    +@suboption:
    +  
    +  lda #telnet_state_got_suboption
    +  sta telnet_state
    +  lda #0
    +  sta iac_suboption_buffer_length
    +  jmp @byte_processed
    +  
    +@option:
    +  lda #telnet_state_got_command
    +  sta telnet_state
    +  jmp @byte_processed
    +
    +@waiting_for_option:
    +;we have now got IAC, , 
    \ No newline at end of file diff --git a/docs/ip65_tftp_s.html b/docs/ip65_tftp_s.html new file mode 100644 index 0000000..88d6817 --- /dev/null +++ b/docs/ip65_tftp_s.html @@ -0,0 +1,651 @@ +

    ip65 technical reference

    File : ip65/tftp.s

    minimal tftp implementation (client only)
    +supports file upload and download
    +

    functions

    functiondescription
    tftp_callback_vector
    tftp_clear_callbacks
    clear callback vectors, i.e. all future transfers read from/write to RAM
    +inputs: none
    +outputs: none
    
    tftp_download
    download a file from a tftp server
    + inputs:
    +   tftp_ip: ip address of host to download from (set to 255.255.255.255 for broadcast)
    +   tftp_filename: pointer to null terminated name of file to download
    +   tftp_load_address: memory location that dir will be stored in, or $0000 to
    +     treat first 2 bytes received from tftp server as memory address that rest
    +     of file should be loaded into (e.g. if downloading a C64 'prg' file)
    + outputs: carry flag is set if there was an error
    +   if a callback vector has been set with tftp_set_callback_vector
    +   then the specified routine will be called once for each 512 byte packet
    +   sent from the tftp server (each time AX will point at data block just arrived,
    +   and tftp_data_block_length will contain number of bytes in that data block)
    +   otherwise, the buffer at tftp_load_address will be filled
    +   with file downloaded.
    +   tftp_load_address: will be set to the actual address loaded into (NB - this field is
    +       ignored if a callback vector has been set with tftp_set_callback_vector)
    
    tftp_set_callback_vector
    set up vector of routine to be called when each 512 packet arrives from tftp server 
    +when downloading OR for routine to be called when ready to send new block
    +when uploading.
    +when vector is called when downloading, AX will point to data that was downloaded,
    +tftp_data_block_length will be set to length of downloaded data block. This will be 
    +equal to $200 (512) for each block EXCEPT the final block. THe final block will
    +always be less than $200 bytes - if the file is an exact multiple if $200 bytes
    +long, then a final block will be received with length $00.
    +when vector is called when uploading, AX will point to a 512 byte buffer that
    +should be filled with the next block. the user supplied routine should set AX
    +to be equal to the actual number of bytes inserted into the buffer, which should
    +equal to $200 (512) for each block EXCEPT the final block. The final block must
    +always be less than $200 bytes - if the file is an exact multiple if $200 bytes
    +long, then a final block must be created with length $00.
    + inputs:
    + AX - address of routine to call for each packet.
    + outputs: none
    
    tftp_upload
    uploads a file to a tftp server with data retrieved from user supplied routine
    + inputs:
    +  tftp_ip: ip address of host to send file to (set to 255.255.255.255 for broadcast)
    +  tftp_filename: pointer to null terminated name of file to upload
    +   a callback vector should have been set with tftp_set_callback_vector
    + outputs: carry flag is set if there was an error
    +   the specified routine will be called once for each 512 byte packet
    +   to be sent from the tftp server.
    
    tftp_upload_from_memory
    uploads a file to a tftp server with data retrieved from specified memory location
    + inputs:
    +  tftp_ip: ip address of host to send file to (set to 255.255.255.255 for broadcast)
    +  tftp_filename: pointer to null terminated name of file to upload
    +  tftp_load_address: starting address of data to be sent
    +  tftp_filesize: length of data to send
    + outputs: carry flag is set if there was an error
    +   if a callback vector has been set with tftp_set_callback_vector
    +   then the specified routine will be called once for each 512 byte packet
    +   to be sent to the tftp server 
    

    variables

    variabledescriptionsize (bytes)
    tftp_data_block_length2
    tftp_filenamename of file to d/l or filemask to get directory listing for 2
    tftp_filesizewill be set by tftp_download, needs to be set before calling tftp_upload_from_memory 2
    tftp_ipip address of tftp server - set to 255.255.255.255 (broadcast) to send request to all tftp servers on local lan 4
    tftp_load_addressaddress file will be (or was) downloaded to 2

    implementation

    ;minimal tftp implementation (client only)
    +;supports file upload and download
    +
    +
    +  TFTP_MAX_RESENDS=10
    +  TFTP_TIMER_MASK=$F8 ;mask lower two bits, means we wait for 8 x1/4 seconds
    +
    +  .include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +  .exportzp tftp_filename
    +  .export tftp_load_address
    +  .export tftp_ip
    +  .export tftp_download
    +  .export tftp_upload
    +  .export tftp_data_block_length
    +  .export tftp_set_callback_vector
    +  .export tftp_callback_vector
    +  .export tftp_clear_callbacks
    +  .export tftp_filesize
    +  .export tftp_upload_from_memory
    +  .import ip65_process
    +  .import ip65_error
    +  
    +
    +  .import udp_add_listener
    +  .import udp_remove_listener
    +  .import output_buffer
    +  .import udp_callback
    +  .import udp_send
    +  .import check_for_abort_key
    +  .import udp_inp
    +  .import ip_inp
    +  .importzp ip_src
    +  .importzp udp_src_port
    +
    +
    +  .importzp udp_data
    +
    +  .import udp_send_dest
    +  .import udp_send_src_port
    +  .import udp_send_dest_port
    +  .import udp_send_len
    +
    +  .import copymem
    +  .importzp copy_src
    +  .importzp copy_dest
    +
    +  .import timer_read
    +  
    +  .segment "IP65ZP" : zeropage
    +
    +tftp_filename: .res 2 ;name of file to d/l or filemask to get directory listing for
    +  
    +  .bss
    +
    +;packet offsets
    +tftp_inp    = udp_inp + udp_data 
    +tftp_outp = output_buffer
    +
    +;everything after filename in a request at a relative address, not fixed, so don't bother defining offset constants
    +
    +tftp_server_port=69
    +tftp_client_port_low_byte: .res 1
    +
    +tftp_load_address: .res 2 ;address file will be (or was) downloaded to
    +tftp_ip: .res 4 ;ip address of tftp server - set to 255.255.255.255 (broadcast) to send request to all tftp servers on local lan
    +
    +tftp_data_block_length: .res 2
    +tftp_send_len: .res 2
    +tftp_current_memloc: .res 2
    +
    +; tftp state machine
    +tftp_initializing  = 1        ; initial state
    +tftp_initial_request_sent=2 ; sent the RRQ or WRQ, waiting for some data
    +tftp_transmission_in_progress=3       ; we have sent/received the first packet of file data
    +tftp_complete=4             ; we have sent/received the final packet of file data
    +tftp_error=5                ; we got an error
    +
    +tftp_state:  .res 1    ; current activity
    +tftp_timer:  .res 1
    +tftp_resend_counter: .res 1
    +tftp_break_inner_loop: .res 1
    +tftp_current_block_number: .res 2
    +tftp_actual_server_port: .res 2   ;this is read from the reply  - it is not (usually) the port # we send the RRQ or WRQ to
    +tftp_actual_server_ip: .res 4     ;this is read from the reply - it may not be the IP we sent to (e.g. if we send to broadcast)
    +
    +tftp_just_set_new_load_address: .res 1
    +
    +tftp_opcode: .res 2 ; will be set to 4 if we are doing a RRQ, or 7 if we are doing a DIR
    +tftp_filesize: .res 2 ;will be set by tftp_download, needs to be set before calling tftp_upload_from_memory
    +tftp_bytes_remaining: .res 2 
    +
    +.code
    +
    +;uploads a file to a tftp server with data retrieved from specified memory location
    +; inputs:
    +;  tftp_ip: ip address of host to send file to (set to 255.255.255.255 for broadcast)
    +;  tftp_filename: pointer to null terminated name of file to upload
    +;  tftp_load_address: starting address of data to be sent
    +;  tftp_filesize: length of data to send
    +; outputs: carry flag is set if there was an error
    +;   if a callback vector has been set with tftp_set_callback_vector
    +;   then the specified routine will be called once for each 512 byte packet
    +;   to be sent to the tftp server 
    +tftp_upload_from_memory:
    +  ldax #copy_ram_to_tftp_block
    +  jsr tftp_set_callback_vector
    +  ldax tftp_filesize
    +  stax  tftp_bytes_remaining
    +  lda #00
    +  sta tftp_filesize
    +  sta tftp_filesize+1
    +  
    +;uploads a file to a tftp server with data retrieved from user supplied routine
    +; inputs:
    +;  tftp_ip: ip address of host to send file to (set to 255.255.255.255 for broadcast)
    +;  tftp_filename: pointer to null terminated name of file to upload
    +;   a callback vector should have been set with tftp_set_callback_vector
    +; outputs: carry flag is set if there was an error
    +;   the specified routine will be called once for each 512 byte packet
    +;   to be sent from the tftp server.
    +tftp_upload:
    +  ldax  #$0200      ;opcode 02 = WRQ
    +  jmp set_tftp_opcode
    +
    +;download a file from a tftp server
    +; inputs:
    +;   tftp_ip: ip address of host to download from (set to 255.255.255.255 for broadcast)
    +;   tftp_filename: pointer to null terminated name of file to download
    +;   tftp_load_address: memory location that dir will be stored in, or $0000 to
    +;     treat first 2 bytes received from tftp server as memory address that rest
    +;     of file should be loaded into (e.g. if downloading a C64 'prg' file)
    +; outputs: carry flag is set if there was an error
    +;   if a callback vector has been set with tftp_set_callback_vector
    +;   then the specified routine will be called once for each 512 byte packet
    +;   sent from the tftp server (each time AX will point at data block just arrived,
    +;   and tftp_data_block_length will contain number of bytes in that data block)
    +;   otherwise, the buffer at tftp_load_address will be filled
    +;   with file downloaded.
    +;   tftp_load_address: will be set to the actual address loaded into (NB - this field is
    +;       ignored if a callback vector has been set with tftp_set_callback_vector)
    +tftp_download:
    +  lda #00
    +  sta tftp_filesize
    +  sta tftp_filesize+1
    +  ldx #$01                      ;opcode 01 = RRQ (A should already be zero from having just reset file length)
    +set_tftp_opcode:  
    +  stax  tftp_opcode
    +  lda #tftp_initializing
    +  sta tftp_state  
    +  ldax #0000
    +  stax tftp_current_block_number
    +  ldax tftp_load_address
    +  stax tftp_current_memloc
    +  ldax #tftp_in
    +  stax udp_callback 
    +  ldx #$69
    +  inc tftp_client_port_low_byte    ;each transfer uses a different client port
    +  lda tftp_client_port_low_byte    ;so we don't get confused by late replies to a previous call
    +  
    +  jsr udp_add_listener
    +  
    +  bcc :+      ;bail if we couldn't listen on the port we want
    +  lda #KPR_ERROR_PORT_IN_USE
    +  sta ip65_error
    +  rts
    +:
    +
    +  lda #TFTP_MAX_RESENDS
    +  sta tftp_resend_counter
    +@outer_delay_loop:
    +  jsr timer_read 
    +  txa
    +  and #TFTP_TIMER_MASK
    +  sta tftp_timer            ;we only care about the high byte  
    +  lda #0
    +  sta tftp_break_inner_loop
    +  lda tftp_state
    +  cmp #tftp_initializing
    +  bne @not_initializing
    +  jsr send_request_packet
    +
    +  jmp @inner_delay_loop
    +  
    +@not_initializing:
    +
    +  cmp #tftp_error
    +  bne @not_error
    +
    +@exit_with_error:
    +  ldx #$69
    +  lda tftp_client_port_low_byte    
    +  jsr udp_remove_listener  
    +  sec
    +  rts
    +  
    +@not_error:
    +  
    +  cmp #tftp_complete
    +  bne @not_complete
    +  jsr send_ack    ;send the ack for the last block
    +  bcs @not_complete ;if we couldn't send the ACK (e.g. coz we need to do an ARP request) then keep looping
    +  ldx #$69
    +  lda tftp_client_port_low_byte    
    +  jsr udp_remove_listener
    +  rts
    +  
    +@not_complete:  
    +  cmp #tftp_transmission_in_progress
    +  bne @not_transmitting
    +  jsr send_tftp_packet
    +  jmp @inner_delay_loop
    +@not_transmitting:
    +  jsr send_request_packet  
    +
    +@inner_delay_loop:  
    +  jsr ip65_process
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  jmp @exit_with_error
    +@no_abort:    
    +  lda tftp_break_inner_loop
    +  bne @outer_delay_loop
    +  jsr timer_read
    +  txa
    +  and #TFTP_TIMER_MASK
    +  cmp tftp_timer            
    +  beq @inner_delay_loop
    +  
    +  dec tftp_resend_counter
    +  bne @outer_delay_loop
    +  lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
    +  sta ip65_error
    +  jmp @exit_with_error  
    +
    +send_request_packet:
    +  lda #tftp_initializing
    +  sta tftp_state
    +  ldax  tftp_opcode  
    +  stax tftp_outp
    +  
    +  ldx #$01          ;we inc x/y at start of loop, so
    +  ldy #$ff          ;set them to be 1 below where we want the copy to begin
    +@copy_filename_loop:
    +  inx
    +  iny
    +  bmi @error_in_send  ;if we get to 0x80 bytes, we've gone too far
    +  lda (tftp_filename),y
    +  sta tftp_outp,x
    +  bne @copy_filename_loop
    +
    +  ldy #$ff
    +@copy_mode_loop:
    +  inx
    +  iny
    +  lda tftp_octet_mode,y
    +  sta tftp_outp,x
    +  bne @copy_mode_loop
    +  
    +  inx 
    +  txa
    +  ldx #0
    +  stax udp_send_len
    +  
    +  ldx #$69
    +  lda tftp_client_port_low_byte    
    +  stax udp_send_src_port
    +
    +  ldx #3        ; set destination address
    +: lda tftp_ip,x
    +  sta udp_send_dest,x
    +  dex
    +  bpl :-
    +
    +  ldax #tftp_server_port      ; set destination port
    +  stax udp_send_dest_port
    +  ldax #tftp_outp
    +  jsr udp_send
    +  bcs @error_in_send
    +  lda #tftp_initial_request_sent
    +  sta tftp_state
    +  rts
    +@error_in_send:  
    +  lda #KPR_ERROR_TRANSMIT_FAILED
    +  sta ip65_error
    +  sec
    +  rts
    +
    +send_ack:
    +  ldax  #$0400      ;opcode 04 = ACK
    +  stax tftp_outp
    +  ldax #04
    +  stax tftp_send_len
    +send_tftp_packet: ;TFTP block should be created in tftp_outp, we just add the UDP&IP stuff and send
    +
    +  ldx tftp_current_block_number
    +  lda tftp_current_block_number+1
    +  stax  tftp_outp+2 
    +
    +  ldx #$69
    +  lda tftp_client_port_low_byte    
    +  stax udp_send_src_port
    +  
    +  lda tftp_actual_server_ip
    +  sta udp_send_dest
    +  lda tftp_actual_server_ip+1
    +  sta udp_send_dest+1
    +  lda tftp_actual_server_ip+2
    +  sta udp_send_dest+2
    +  lda tftp_actual_server_ip+3
    +  sta udp_send_dest+3
    +  ldx tftp_actual_server_port
    +  lda tftp_actual_server_port+1
    +  stax udp_send_dest_port
    +  ldax tftp_send_len
    +  stax udp_send_len
    +
    +  ldax #tftp_outp
    +  jsr udp_send
    +  rts
    +  
    +got_expected_block:
    +  lda tftp_current_block_number
    +  inc tftp_current_block_number
    +  bne :+
    +  inc tftp_current_block_number+1
    +: 
    +  lda #tftp_transmission_in_progress
    +  sta tftp_state
    +  lda #TFTP_MAX_RESENDS
    +  sta tftp_resend_counter
    +  lda #1
    +  sta tftp_break_inner_loop
    +
    +  ldax  udp_inp+udp_src_port
    +  stax  tftp_actual_server_port
    +  ldax  ip_inp+ip_src
    +  stax  tftp_actual_server_ip
    +  ldax  ip_inp+ip_src+2
    +  stax  tftp_actual_server_ip+2
    +  rts
    +  
    +tftp_in:
    +    
    +  lda tftp_inp+1  ;get the opcode
    +  cmp #5
    +  bne @not_an_error
    +@recv_error:
    +  lda #tftp_error
    +  sta tftp_state
    +  lda #KPR_ERROR_TRANSMISSION_REJECTED_BY_PEER
    +  sta ip65_error  
    +  rts
    +@not_an_error:
    +
    +  cmp #3  
    +  beq :+
    +  jmp @not_data_block
    +:  
    +
    +  
    +  lda #0
    +  sta tftp_just_set_new_load_address ;clear the flag
    +  clc 
    +  lda tftp_load_address       
    +  adc tftp_load_address+1     ;is load address currently $0000?
    +  bne @dont_set_load_address
    +  
    +  lda tftp_callback_address_set ;have we overridden the default handler?
    +  bne @dont_set_load_address  ;if so, don't skip the first two bytes in the file
    +  
    +  ldax udp_inp+$0c            ;get first two bytes of data
    +  stax tftp_load_address      ;make them the new load adress
    +  stax tftp_current_memloc    ;also the current memory destination
    +  lda #1                      ;set the flag
    +  sta tftp_just_set_new_load_address 
    +
    +@dont_set_load_address:
    +  ldx tftp_inp+3                  ;get the (low byte) of the data block
    +  dex
    +  cpx tftp_current_block_number
    +  beq :+
    +  jmp @not_expected_block_number
    +:  
    +  ;this is the block we wanted  
    +  jsr got_expected_block
    +  
    +  lda tftp_just_set_new_load_address 
    +  bne @skip_first_2_bytes_in_calculating_header_length
    +  lda udp_inp+5        ;get the low byte of udp packet length
    +  sec
    +  sbc #$0c              ;take off the length of the UDP header+OPCODE + BLOCK 
    +
    +  jmp @adjusted_header_length
    +@skip_first_2_bytes_in_calculating_header_length:  
    +  lda udp_inp+5        ;get the low byte of udp packet length
    +  sec
    +  sbc #$0e              ;take off the length of the UDP header+OPCODE + BLOCK + first 2 bytes (memory location)
    +@adjusted_header_length:
    +
    +  sta tftp_data_block_length
    +  lda udp_inp+4        ;get high byte of the length of the UDP packet
    +  sbc #0
    +  sta tftp_data_block_length+1
    +
    +  lda tftp_just_set_new_load_address 
    +  bne @skip_first_2_bytes_in_calculating_copy_src
    +  ldax #udp_inp+$0c
    +  jmp @got_pointer_to_tftp_data
    +@skip_first_2_bytes_in_calculating_copy_src:
    +  ldax #udp_inp+$0e
    +@got_pointer_to_tftp_data:
    +
    +  stax copy_src
    +  ldax #output_buffer+2
    +  stax copy_dest
    +  ldax  tftp_data_block_length
    +  stax  output_buffer
    +  jsr copymem
    +  ldax #output_buffer
    +  
    +  jsr tftp_callback_vector
    +  jsr send_ack
    +  
    +  lda udp_inp+4         ;check the length of the UDP packet
    +  cmp #02
    +  bne @last_block
    +  
    +  lda udp_inp+5
    +  cmp #$0c
    +  bne @last_block
    +  beq @not_last_block
    +@not_data_block:
    +  cmp #4 ;ACK is opcode 4
    +  beq :+
    +  jmp @not_ack
    +:  
    +;it's an ACK, so we must be sending a file
    +
    +  ldx tftp_inp+3                  ;get the (low byte) of the data block
    +  cpx tftp_current_block_number  
    +  beq :+
    +  jmp @not_expected_block_number
    +: 
    +;the last block we sent was acked so now we need to send the next one
    +;
    +  ldax #output_buffer+4
    +  jsr tftp_callback_vector  ;this (caller supplied) routine should fill the buffer with up to 512 bytes
    +  stax tftp_data_block_length
    +  clc
    +  adc #4
    +  bcc :+
    +  inx
    +:
    +  stax tftp_send_len
    +  ldax  #$0300      ;opcode 03 = DATA
    +  stax tftp_outp
    +  jsr got_expected_block
    +  jsr send_tftp_packet
    +  
    +  
    +  lda tftp_data_block_length+1 ;get length of data we just sent (high byte)
    +  cmp #2
    +  bne @last_block
    +@not_last_block:  
    +  inc tftp_filesize+1 ;add $200 to file size
    +  inc tftp_filesize+1 ;add $200 to file size
    +  
    +@not_ack:
    +@not_expected_block_number:
    +  rts
    +  
    +@last_block:
    +  lda tftp_data_block_length
    +  sta tftp_filesize; this must be the first block that is not a multiple of 512, hence till now the low byte in tftp_filesize is still $00
    +  lda tftp_data_block_length+1 ;this can only be 0 or 1
    +  beq :+
    +  inc tftp_filesize+1
    +:
    +  lda #tftp_complete
    +  sta tftp_state
    +  rts
    +  
    +  
    +;default handler when block arrives:
    +;copy to RAM
    +;assumes tftp_data_block_length has been set, and AX should point to start of data
    +copy_tftp_block_to_ram:
    +  clc       
    +  adc #02       ;skip the 2 byte length at start of buffer
    +  bcc :+
    +  inx
    +:  
    +  stax copy_src
    +  ldax tftp_current_memloc
    +  stax  copy_dest
    +  ldax  tftp_data_block_length
    +  jsr copymem
    +  clc
    +  lda tftp_data_block_length  ;update the location where the next data will go
    +  adc tftp_current_memloc
    +  sta tftp_current_memloc
    +  lda tftp_data_block_length+1
    +  adc tftp_current_memloc+1
    +  sta tftp_current_memloc+1
    +  rts
    +
    +;default handler for uploading a file
    +copy_ram_to_tftp_block:
    +  
    +  stax copy_dest
    +  ldax tftp_current_memloc
    +  stax  copy_src
    +  clc
    +  lda   tftp_bytes_remaining+1  
    +  beq @last_block
    +  cmp #01
    +  beq @last_block  
    +  dec   tftp_bytes_remaining+1 
    +  dec   tftp_bytes_remaining+1
    +  ldax  #$0200
    +@length_is_set:
    +  stax  tftp_data_block_length
    +  jsr copymem
    +  inc tftp_current_memloc+1
    +  inc tftp_current_memloc+1
    +  ldax  tftp_data_block_length
    +  clc  
    +  rts
    +@last_block:
    +  ldax tftp_bytes_remaining
    +  jmp @length_is_set
    +
    +;set up vector of routine to be called when each 512 packet arrives from tftp server 
    +;when downloading OR for routine to be called when ready to send new block
    +;when uploading.
    +;when vector is called when downloading, AX will point to data that was downloaded,
    +;tftp_data_block_length will be set to length of downloaded data block. This will be 
    +;equal to $200 (512) for each block EXCEPT the final block. THe final block will
    +;always be less than $200 bytes - if the file is an exact multiple if $200 bytes
    +;long, then a final block will be received with length $00.
    +;when vector is called when uploading, AX will point to a 512 byte buffer that
    +;should be filled with the next block. the user supplied routine should set AX
    +;to be equal to the actual number of bytes inserted into the buffer, which should
    +;equal to $200 (512) for each block EXCEPT the final block. The final block must
    +;always be less than $200 bytes - if the file is an exact multiple if $200 bytes
    +;long, then a final block must be created with length $00.
    +
    +; inputs:
    +; AX - address of routine to call for each packet.
    +; outputs: none
    +tftp_set_callback_vector:
    +  stax  tftp_callback_vector+1
    +  inc tftp_callback_address_set
    +  rts
    +  
    +;clear callback vectors, i.e. all future transfers read from/write to RAM
    +;inputs: none
    +;outputs: none
    +tftp_clear_callbacks:
    +  lda #0
    +  sta tftp_callback_address_set
    +  ldax #copy_tftp_block_to_ram
    +  jmp tftp_set_callback_vector
    +
    +.rodata
    +  tftp_octet_mode: .asciiz "OCTET"
    +  
    +.data
    +tftp_callback_vector:
    +    jmp copy_tftp_block_to_ram  ;vector for action to take when a data block received (default is to store block in RAM)
    +
    +tftp_callback_address_set:  .byte 0
    +
    +
    +;-- LICENSE FOR tftp.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_timer_s.html b/docs/ip65_timer_s.html new file mode 100644 index 0000000..0918426 --- /dev/null +++ b/docs/ip65_timer_s.html @@ -0,0 +1,70 @@ +

    ip65 technical reference

    File : ip65/timer.s

     timer routines
    +
    + the timer should be a 16-bit counter that's incremented by about
    + 1000 units per second. it doesn't have to be particularly accurate,
    + if you're working with e.g. a 60 Hz VBLANK IRQ, adding 17 to the
    + counter every frame would be just fine.
    +
    + this is generic timer routines, machine specific code goes in drivers/<machinename>timer.s
    +

    functions

    functiondescription
    timer_timeout
    check if specified period of time has passed yet
    +inputs: AX - maximum number of milliseconds we are willing to wait for
    +outputs: carry flag set if timeout occured, clear otherwise
    

    implementation

    ; timer routines
    +;
    +; the timer should be a 16-bit counter that's incremented by about
    +; 1000 units per second. it doesn't have to be particularly accurate,
    +; if you're working with e.g. a 60 Hz VBLANK IRQ, adding 17 to the
    +; counter every frame would be just fine.
    +;
    +; this is generic timer routines, machine specific code goes in drivers/timer.s
    +  .include "../inc/common.i"
    +
    +
    +  .export timer_timeout
    +  .import timer_read
    +
    +  .bss
    +
    +time:    .res 2
    +
    +
    +  .code
    +
    +;check if specified period of time has passed yet
    +;inputs: AX - maximum number of milliseconds we are willing to wait for
    +;outputs: carry flag set if timeout occured, clear otherwise
    +timer_timeout:
    +  pha
    +  txa
    +  pha
    +  jsr timer_read
    +  stax time
    +  pla
    +  tax
    +  pla
    +  sec      ; subtract current value
    +  sbc time
    +  txa
    +  sbc time + 1
    +  rts      ; clc = timeout, sec = no timeout
    +
    +
    +
    +;-- LICENSE FOR timer.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_udp_s.html b/docs/ip65_udp_s.html new file mode 100644 index 0000000..d6c89d0 --- /dev/null +++ b/docs/ip65_udp_s.html @@ -0,0 +1,389 @@ +

    ip65 technical reference

    File : ip65/udp.s

    UDP (user datagram protocol) functions
    +

    functions

    functiondescription
    udp_add_listener
    add a udp listener
    +inputs:
    + udp_callback: vector to call when udp packet arrives on specified port
    + AX: set to udp port to listen on
    +outputs:
    + carry flag set if too may listeners already installed, clear otherwise
    
    udp_init
     initialize udp
    + inputs: none
    + outputs: none
    
    udp_process
    process incoming udp packet
    +inputs:
    + eth_inp: should contain an ethernet frame encapsulating an inbound udp packet
    +outputs:
    + carry flag set if any error occured (including if no handler for specified port
    +  was found)
    + carry flag clear if no error
    + if handler was found, an outbound message may be created, overwriting eth_outp
    
    udp_remove_listener
     remove an udp listener
    + inputs:
    + AX = port to stop listening on
    + outputs:
    + carry flag clear of handler found and removed
    + carry flag set if handler for specified port not found
    
    udp_send
    send udp packet
    +inputs:
    +   udp_send_dest:  destination ip address (4 bytes)
    +   udp_send_dest_port: destination port (2 bytes)
    +   udp_send_src_port: source port (2 bytes)
    +   udp_send_len: length of data to send (exclusive of any headers)
    +   AX: pointer to buffer containing data to be sent
    +outputs:
    +   carry flag is set if an error occured, clear otherwise
    

    variables

    variabledescriptionsize (bytes)
    udp_callbackvector to routine to be called when a udp packet arrives 2
    udp_send_destset to ip address that udp packet will be sent to 4
    udp_send_dest_portset to port that udp packet will be sent to 2
    udp_send_lenset to length of data to be sent in udp packet (excluding ethernet,ip & udp headers) 2
    udp_send_src_portset to port that udp packet will be sent from 2

    constants

    constantsdescriptionvalue
    udp_cksumoffset of checksum field in udp packet 6
    udp_dataoffset of data in udp packet 8
    udp_dest_portoffset of destination port field in udp packet 2
    udp_inppointer to udp packet inside inbound ethernet frame ip_inp + ip_data
    udp_lenoffset of length field in udp packet 4
    udp_outppointer to udp packet inside outbound ethernet frame ip_outp + ip_data
    udp_src_portoffset of source port field in udp packet 0

    implementation

    ;UDP (user datagram protocol) functions
    +
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +  ;.import dbg_dump_udp_header
    +
    +  .import ip65_error
    +
    +  .export udp_init
    +  .export udp_process
    +  .export udp_add_listener
    +  .export udp_remove_listener
    +  .export udp_send
    +
    +  .export udp_callback
    +
    +  .export udp_inp
    +  .export udp_outp
    +
    +  .exportzp udp_src_port
    +  .exportzp udp_dest_port
    +  .exportzp udp_len
    +  .exportzp udp_cksum
    +  .exportzp udp_data
    +
    +  .export udp_send_dest
    +  .export udp_send_src_port
    +  .export udp_send_dest_port
    +  .export udp_send_len
    +
    +  .import ip_calc_cksum
    +  .import ip_send
    +  .import ip_create_packet
    +  .import ip_inp
    +  .import ip_outp
    +  .importzp ip_cksum_ptr
    +  .importzp ip_header_cksum
    +  .importzp ip_src
    +  .importzp ip_dest
    +  .importzp ip_data
    +  .importzp ip_proto
    +  .importzp ip_proto_udp
    +  .importzp ip_id
    +  .importzp ip_len
    +
    +  .import copymem
    +  .importzp copy_src
    +  .importzp copy_dest
    +
    +  .import cfg_ip
    +  
    +  .data
    +  udp_cbtmp:  jmp $ffff      ; temporary vector - gets filled in later
    +
    +
    +  .bss
    +
    +; argument for udp_add_listener
    +udp_callback:  .res 2  ;vector to routine to be called when a udp packet arrives
    +
    +; arguments for udp_send
    +udp_send_dest:    .res 4  ;set to ip address that udp packet will be sent to
    +udp_send_src_port:  .res 2 ;set to port that udp packet will be sent from
    +udp_send_dest_port:  .res 2 ;set to port that udp packet will be sent to
    +udp_send_len:    .res 2 ;set to length of data to be sent in udp packet (excluding ethernet,ip & udp headers)
    +
    +; udp listener callbacks
    +udp_cbmax  = 4
    +udp_cbveclo:  .res udp_cbmax    ; table of listener vectors (lsb)
    +udp_cbvechi:  .res udp_cbmax    ; table of listener vectors (msb)
    +udp_cbportlo:  .res udp_cbmax    ; table of ports (lsb)
    +udp_cbporthi:  .res udp_cbmax    ; table of ports (msb)
    +udp_cbcount:  .res 1      ; number of active listeners
    +
    +; udp packet offsets
    +udp_inp    = ip_inp + ip_data  ;pointer to udp packet inside inbound ethernet frame
    +udp_outp  = ip_outp + ip_data ;pointer to udp packet inside outbound ethernet frame
    +udp_src_port  = 0 ;offset of source port field in udp packet
    +udp_dest_port  = 2 ;offset of destination port field in udp packet
    +udp_len    = 4 ;offset of length field in udp packet
    +udp_cksum  = 6 ;offset of checksum field in udp packet
    +udp_data  = 8 ;offset of data in udp packet
    +
    +; virtual header
    +udp_vh    = udp_outp - 12
    +udp_vh_src  = 0
    +udp_vh_dest  = 4
    +udp_vh_zero  = 8
    +udp_vh_proto  = 9
    +udp_vh_len  = 10
    +
    +
    +; temp for port comparison
    +port:     .res 2
    +
    +
    +  .code
    +
    +; initialize udp
    +; inputs: none
    +; outputs: none
    +udp_init:
    +  lda #0
    +  sta udp_cbcount
    +  rts
    +
    +
    +;process incoming udp packet
    +;inputs:
    +; eth_inp: should contain an ethernet frame encapsulating an inbound udp packet
    +;outputs:
    +; carry flag set if any error occured (including if no handler for specified port
    +;  was found)
    +; carry flag clear if no error
    +; if handler was found, an outbound message may be created, overwriting eth_outp
    +
    +udp_process:
    +  lda udp_cbcount      ; any installed udp listeners?
    +  beq @drop
    +
    +  tax        ; check ports
    +  dex
    +@checkport:
    +  lda udp_cbportlo,x
    +  cmp udp_inp + udp_dest_port + 1
    +  bne :+
    +  lda udp_cbporthi,x
    +  cmp udp_inp + udp_dest_port
    +  beq @handle
    +:  dex
    +  bpl @checkport
    +
    +@drop:
    +    sec
    +  rts
    +
    +@handle:
    +  lda udp_cbveclo,x    ; copy vector
    +  sta udp_cbtmp + 1
    +  lda udp_cbvechi,x
    +  sta udp_cbtmp + 2
    +  jsr udp_cbtmp      ; call listener
    +  clc
    +  rts
    +
    +
    +;add a udp listener
    +;inputs:
    +; udp_callback: vector to call when udp packet arrives on specified port
    +; AX: set to udp port to listen on
    +;outputs:
    +; carry flag set if too may listeners already installed, clear otherwise
    +udp_add_listener:
    +  sta port
    +  stx port + 1
    +
    +  ldy udp_cbcount      ; any listeners at all?
    +  beq @add
    +  cpy #udp_cbmax      ; max?
    +  beq @full
    +  ldy #0
    +@check:
    +  lda udp_cbportlo,y    ; check if port is already handled
    +  cmp port
    +  bne :+
    +  lda udp_cbporthi,y
    +  cmp port + 1
    +  beq @busy
    +:  iny
    +  cpy udp_cbcount
    +  bne @check
    +@add:
    +  inc udp_cbcount      ; increase counter
    +  sta udp_cbportlo,y    ; add port
    +  txa
    +  sta udp_cbporthi,y    ; add port
    +  lda udp_callback    ; and vector
    +  sta udp_cbveclo,y
    +  lda udp_callback + 1
    +  sta udp_cbvechi,y
    +
    +  clc
    +  rts
    +@full:
    +@busy:
    +  lda #KPR_ERROR_LISTENER_NOT_AVAILABLE
    +  sta  ip65_error
    +  sec
    +  sec
    +  rts
    +
    +
    +; remove an udp listener
    +; inputs:
    +; AX = port to stop listening on
    +; outputs:
    +; carry flag clear of handler found and removed
    +; carry flag set if handler for specified port not found
    +udp_remove_listener:
    +  sta port
    +  stx port + 1
    +
    +  ldy udp_cbcount      ; any listeners installed?
    +  beq @notfound
    +  dey
    +@check:
    +  lda udp_cbportlo,y    ; check if port is handled
    +  cmp port
    +  bne :+  
    +  lda udp_cbporthi,y
    +  cmp port + 1
    +  beq @remove
    +:  dey
    +  bpl @check
    +@notfound:
    +  sec
    +  rts
    +@remove:
    +  tya        ; number of listeners below
    +  eor #$ff
    +  clc
    +  adc udp_cbcount
    +  beq @done
    +@move:
    +  tax        ; number of items to move
    +:  lda udp_cbportlo + 1,y    ; move ports
    +  sta udp_cbportlo,y
    +  lda udp_cbporthi + 1,y
    +  sta udp_cbporthi,y
    +  lda udp_cbveclo + 1,y    ; move vectors
    +  sta udp_cbveclo,y
    +  lda udp_cbvechi + 1,y
    +  sta udp_cbvechi,y
    +  iny
    +  dex
    +  bne :-
    +@done:
    +  dec udp_cbcount    ; decrement counter
    +  clc
    +  rts
    +
    +
    +;send udp packet
    +;inputs:
    +;   udp_send_dest:  destination ip address (4 bytes)
    +;   udp_send_dest_port: destination port (2 bytes)
    +;   udp_send_src_port: source port (2 bytes)
    +;   udp_send_len: length of data to send (exclusive of any headers)
    +;   AX: pointer to buffer containing data to be sent
    +;outputs:
    +;   carry flag is set if an error occured, clear otherwise
    +udp_send:
    +  stax copy_src      ; copy data to output buffer
    +  ldax #udp_outp + udp_data
    +  stax copy_dest
    +  ldax udp_send_len
    +  jsr copymem
    +
    +  ldx #3        ; copy virtual header addresses
    +:  lda udp_send_dest,x
    +  sta udp_vh + udp_vh_dest,x  ; set virtual header destination
    +  lda cfg_ip,x
    +  sta udp_vh + udp_vh_src,x  ; set virtual header source
    +  dex
    +  bpl :-
    +
    +  lda udp_send_src_port    ; copy source port
    +  sta udp_outp + udp_src_port + 1
    +  lda udp_send_src_port + 1
    +  sta udp_outp + udp_src_port
    +
    +  lda udp_send_dest_port    ; copy destination port
    +  sta udp_outp + udp_dest_port + 1
    +  lda udp_send_dest_port + 1
    +  sta udp_outp + udp_dest_port
    +
    +  lda #ip_proto_udp
    +  sta udp_vh + udp_vh_proto
    +
    +  lda #0        ; clear checksum
    +  sta udp_outp + udp_cksum
    +  sta udp_outp + udp_cksum + 1
    +  sta udp_vh + udp_vh_zero  ; clear virtual header zero byte
    +
    +  ldax #udp_vh      ; checksum pointer to virtual header
    +  stax ip_cksum_ptr
    +
    +  lda udp_send_len    ; copy length + 8
    +  clc
    +  adc #8
    +  sta udp_outp + udp_len + 1  ; lsb for udp header
    +  sta udp_vh + udp_vh_len + 1  ; lsb for virtual header
    +  tay
    +  lda udp_send_len + 1
    +  adc #0
    +  sta udp_outp + udp_len    ; msb for udp header
    +  sta udp_vh + udp_vh_len    ; msb for virtual header
    +
    +  tax        ; length to A/X
    +  tya
    +
    +  clc        ; add 12 bytes for virtual header
    +  adc #12
    +  bcc :+
    +  inx
    +:
    +  jsr ip_calc_cksum    ; calculate checksum
    +  stax udp_outp + udp_cksum
    +
    +  ldx #3        ; copy addresses
    +:  lda udp_send_dest,x
    +  sta ip_outp + ip_dest,x    ; set ip destination address
    +  dex
    +  bpl :-
    +
    +  jsr ip_create_packet    ; create ip packet template
    +
    +  lda udp_outp + udp_len + 1  ; ip len = udp len + 20
    +  ldx udp_outp + udp_len
    +  clc
    +  adc #20
    +  bcc :+
    +  inx
    +:  sta ip_outp + ip_len + 1  ; set length
    +  stx ip_outp + ip_len
    +
    +  ldax #$1234          ; set ID
    +  stax ip_outp + ip_id
    +
    +  lda #ip_proto_udp    ; set protocol
    +  sta ip_outp + ip_proto
    +
    +  ;jsr dbg_dump_udp_header
    +
    +  jmp ip_send      ; send packet, sec on error
    +
    +
    +
    +;-- LICENSE FOR udp.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Per Olofsson,
    +; MagerValp@gmail.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Per Olofsson. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_url_s.html b/docs/ip65_url_s.html new file mode 100644 index 0000000..f8b8201 --- /dev/null +++ b/docs/ip65_url_s.html @@ -0,0 +1,489 @@ +

    ip65 technical reference

    File : ip65/url.s

    routines for parsing a URL, and downloading an URL
    +

    functions

    functiondescription
    resource_download
    download a resource specified by ip,port & selector
    +inputs: 
    + url_ip = ip address of host to connect to
    + url_port = port number of to connect to
    + url_selector= address of selector to send to host after connecting
    + url_download_buffer - points to a buffer that url will be downloaded into
    + url_download_buffer_length - length of buffer
    +outputs:
    + sec if an error occured, else buffer pointed at by url_download_buffer is filled with contents 
    + of specified resource (with an extra 2 null bytes at the end),
    + AX = length of resource downloaded.
    
    url_download
    download a resource specified by an URL
    +inputs: 
    +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
    +outputs:
    + sec if an error occured, else buffer pointed at by url_download_buffer is filled with contents 
    + of specified resource (with an extra 2 null bytes at the end),
    + AX = length of resource downloaded.
    
    url_parse
    parses a URL into a form that makes it easy to retrieve the specified resource
    +inputs: 
    +AX = address of URL string
    +any control character (i.e. <$20) is treated as 'end of string', e.g. a CR or LF, as well as $00
    +outputs:
    + sec if a malformed url, otherwise:
    + url_ip = ip address of host in url
    + url_port = port number of url
    + url_selector= address of selector part of URL
    

    variables

    variabledescriptionsize (bytes)
    url_download_buffer points to a buffer that url will be downloaded into 2
    url_download_buffer_lengthlength of buffer that url will be downloaded into 2
    url_resource_type1

    constants

    constantsdescriptionvalue
    url_ip url_ip = ip address of host to connect to ip address of host to connect to +
    url_port url_port = port number of to connect to port number of to connect to +
    url_selector url_selector= address of selector to send to host after connecting address of selector to send to host after connecting +

    implementation

    ;routines for parsing a URL, and downloading an URL
    +
    +
    +.include "../inc/common.i"
    +
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +TIMEOUT_SECONDS=15
    +
    +.import output_buffer
    +.importzp copy_src
    +.importzp copy_dest
    +.import copymem
    +.import timer_read
    +.import ip65_error
    +.import ip65_process
    +.import parser_init
    +.import parser_skip_next
    +.import dns_set_hostname
    +.import dns_resolve
    +.import parse_integer
    +.import dns_ip
    +.import tcp_connect
    +.import tcp_send_string
    +.import tcp_send_data_len
    +.import tcp_callback
    +.import tcp_close
    +.import tcp_connect_ip
    +.import tcp_inbound_data_length
    +.import tcp_inbound_data_ptr
    +
    +
    +.export  url_ip
    +.export  url_port
    +.export  url_selector
    +.export url_resource_type
    +.export url_parse
    +.export url_download
    +.export url_download_buffer
    +.export url_download_buffer_length
    +.export resource_download
    +
    +target_string=copy_src
    +search_string=copy_dest
    +selector_buffer=output_buffer
    +
    +.segment "TCP_VARS"
    +
    +  url_string: .res 2 
    +  url_ip: .res 4    ;will be set with ip address of host in url
    +  url_port: .res 2 ;will be set with port number of url
    +  url_selector: .res 2 ;will be set with address of selector part of URL
    +  url_type: .res 1
    +  url_resource_type: .res 1
    +  url_type_unknown=0
    +  url_type_gopher=1
    +  url_type_http=2
    +  
    +  src_ptr: .res 1
    +  dest_ptr: .res 1
    +  timeout_counter: .res 1
    +  url_download_buffer: .res 2 ; points to a buffer that url will be downloaded into
    +  url_download_buffer_length: .res 2  ;length of buffer that url will be downloaded into
    +
    +  temp_buffer: .res 2  
    +  temp_buffer_length: .res 2  
    +
    +  download_flag: .res 1
    +
    +
    +.code
    +
    +
    +;parses a URL into a form that makes it easy to retrieve the specified resource
    +;inputs: 
    +;AX = address of URL string
    +;any control character (i.e. <$20) is treated as 'end of string', e.g. a CR or LF, as well as $00
    +;outputs:
    +; sec if a malformed url, otherwise:
    +; url_ip = ip address of host in url
    +; url_port = port number of url
    +; url_selector= address of selector part of URL
    +url_parse:
    +  stax url_string
    +  ldy #0
    +  sty url_type
    +  sty url_port
    +  sty url_port+1
    +  sty url_resource_type
    +
    +  jsr skip_to_hostname
    +  bcc :+
    +  ldax url_string
    +  jmp @no_protocol_specifier
    +:  
    +  ldax url_string
    +  stax  search_string
    +
    +  lda (search_string),y
    +  cmp  #'g'
    +  beq @gopher
    +  cmp  #'G'
    +  beq @gopher
    +  cmp  #'h'
    +  beq @http
    +  cmp  #'H'
    +  beq @http
    +@exit_with_error:  
    +  lda #KPR_ERROR_MALFORMED_URL 
    +  sta ip65_error
    +@exit_with_sec:  
    +  sec
    +  rts
    +@http:
    +  lda #url_type_http
    +  sta url_type
    +  lda #80
    +  sta url_port
    +  jmp @protocol_set
    +@gopher:
    +lda #url_type_gopher
    +  sta url_type
    +  lda #70
    +  sta url_port
    +@protocol_set:
    +  jsr skip_to_hostname
    +  ;now pointing at hostname
    +  bcs @exit_with_error
    +@no_protocol_specifier:  
    +  jsr dns_set_hostname
    +  bcs @exit_with_sec
    +  jsr dns_resolve
    +  bcc :+
    +  lda #KPR_ERROR_DNS_LOOKUP_FAILED
    +  sta ip65_error
    +  jmp @exit_with_sec
    +  :
    +  ;copy IP address
    +  ldx #3
    +:
    +  lda dns_ip,x
    +  sta url_ip,x
    +  dex
    +  bpl :-
    +
    +  jsr skip_to_hostname
    +  
    +  ;skip over next colon
    +  ldax #colon
    +  jsr parser_skip_next
    +  bcs @no_port_in_url
    +  ;AX now point at first thing past a colon - should be a number:
    +  jsr  parse_integer
    +  stax url_port
    +@no_port_in_url:  
    +  ;skip over next slash
    +  ldax #slash
    +  jsr parser_skip_next
    +  ;AX now pointing at selector
    +  stax copy_src
    +  ldax #selector_buffer
    +  stax copy_dest
    +  lda #0
    +  sta src_ptr
    +  sta dest_ptr
    +  lda url_type
    +  
    +  cmp #url_type_gopher
    +  bne @not_gopher  
    +  ;first byte after / in a gopher url is the resource type  
    +  ldy src_ptr  
    +  lda (copy_src),y
    +  beq @start_of_selector  
    +  sta url_resource_type
    +  inc src_ptr  
    +  jmp @start_of_selector
    +@not_gopher:  
    +  cmp #url_type_http
    +  beq @build_http_request
    +  jmp @done ; if it's not gopher or http, we don't know how to build a selector
    +@build_http_request:  
    +  ldy #get_length-1
    +  sty dest_ptr
    +:
    +  lda get,y
    +  sta (copy_dest),y
    +  dey
    +  bpl :-  
    +  
    +@start_of_selector: 
    +  lda #'/'
    +  inc dest_ptr  
    +  jmp @save_first_byte_of_selector
    +@copy_one_byte:
    +  ldy src_ptr  
    +  lda (copy_src),y
    +  cmp #$20
    +  bcc @end_of_selector  ;any control char (including CR,LF, and $00) should be treated as end of URL
    +  inc src_ptr  
    +@save_first_byte_of_selector:  
    +  ldy dest_ptr  
    +  sta (copy_dest),y  
    +  inc dest_ptr
    +  bne @copy_one_byte
    +@end_of_selector:
    +
    +
    +  ldx #1 ;number of CRLF at end of gopher request
    +  lda url_type
    +  
    +  cmp #url_type_http
    +  bne @final_crlf
    +    
    +  ;now the HTTP version number & Host: field
    +  ldx #0
    +:
    +  lda http_preamble,x
    +  beq :+
    +  ldy dest_ptr
    +  inc dest_ptr
    +  sta (copy_dest),y  
    +  inx
    +  bne :-  
    +:
    +
    +  
    +  ;now copy the host field
    +  jsr skip_to_hostname
    +  ;AX now pointing at hostname
    +  stax copy_src
    +  ldax #selector_buffer
    +  stax copy_dest
    +
    +  lda #0
    +  sta src_ptr
    +  
    +@copy_one_byte_of_hostname:
    +  ldy src_ptr  
    +  lda (copy_src),y
    +  beq @end_of_hostname
    +  cmp #':'
    +  beq @end_of_hostname
    +  cmp #'/'
    +  beq @end_of_hostname
    +  inc src_ptr  
    +  ldy dest_ptr  
    +  sta (copy_dest),y  
    +  inc dest_ptr  
    +  bne @copy_one_byte_of_hostname
    +@end_of_hostname:
    +   
    +  ldx #2 ;number of CRLF at end of HTTP request
    +  
    +@final_crlf:
    +  ldy dest_ptr
    +  lda #$0d
    +  sta (copy_dest),y
    +  iny
    +  lda #$0a
    +  sta (copy_dest),y
    +  iny
    +  sty dest_ptr
    +  dex
    +  bne @final_crlf
    +
    +@done:  
    +  lda #$00
    +  sta (copy_dest),y
    +  ldax #selector_buffer
    +  stax url_selector
    +  clc
    +  
    +  rts
    +  
    +skip_to_hostname:
    +  ldax url_string
    +  jsr parser_init
    +  ldax #colon_slash_slash
    +  jmp parser_skip_next
    +  
    +
    +
    +;download a resource specified by an URL
    +;inputs: 
    +;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
    +;outputs:
    +; sec if an error occured, else buffer pointed at by url_download_buffer is filled with contents 
    +; of specified resource (with an extra 2 null bytes at the end),
    +; AX = length of resource downloaded.
    +url_download:
    +  jsr url_parse  
    +  bcc resource_download
    +  rts
    +
    +;download a resource specified by ip,port & selector
    +;inputs: 
    +; url_ip = ip address of host to connect to
    +; url_port = port number of to connect to
    +; url_selector= address of selector to send to host after connecting
    +; url_download_buffer - points to a buffer that url will be downloaded into
    +; url_download_buffer_length - length of buffer
    +;outputs:
    +; sec if an error occured, else buffer pointed at by url_download_buffer is filled with contents 
    +; of specified resource (with an extra 2 null bytes at the end),
    +; AX = length of resource downloaded.
    +resource_download:
    + 
    +  ldax url_download_buffer
    +  stax temp_buffer
    +  ldax url_download_buffer_length
    +  stax temp_buffer_length
    +  jsr put_zero_at_end_of_dl_buffer
    +  
    +  ldx #3        ; save IP address just retrieved
    +: lda url_ip,x
    +  sta tcp_connect_ip,x
    +  dex
    +  bpl :-
    +  ldax #url_download_callback
    +  stax tcp_callback
    +
    +  ldax url_port  
    +  jsr tcp_connect
    +  bcs @error
    +
    +  ;connected, now send the selector
    +  ldx #0
    +  stx download_flag
    +  ldax url_selector
    +  
    +  jsr tcp_send_string
    +  jsr timer_read
    +  txa
    +  adc #TIMEOUT_SECONDS*4 ;what value should trigger the timeout?
    +  sta timeout_counter
    +  ;now loop until we're done
    +@download_loop:
    +  jsr ip65_process
    +  jsr timer_read
    +  cpx timeout_counter
    +  beq @timeout
    +  lda download_flag
    +  beq @download_loop
    +@timeout:  
    +  jsr tcp_close
    +  clc
    +@error:
    +  rts
    +
    +
    +  lda #KPR_ERROR_FILE_ACCESS_FAILURE
    +  sta ip65_error
    +  sec  
    +  rts
    +  
    +  
    +  url_download_callback:
    +  
    +  lda tcp_inbound_data_length+1
    +  cmp #$ff
    +  bne not_end_of_file
    +@end_of_file:  
    +  lda #1
    +  sta download_flag
    +
    +put_zero_at_end_of_dl_buffer:
    +  ;put a zero byte at the end of the file 
    +  ldax temp_buffer  
    +  stax copy_dest
    +  lda #0  
    +  tay
    +  sta (copy_dest),y  
    +  rts
    +  
    +not_end_of_file:
    +;copy this chunk to our input buffer
    +  ldax temp_buffer
    +  stax copy_dest
    +  ldax tcp_inbound_data_ptr
    +  stax copy_src
    +  sec
    +  lda temp_buffer_length
    +  sbc tcp_inbound_data_length
    +  pha
    +  lda temp_buffer_length+1
    +  sbc tcp_inbound_data_length+1
    +  bcc @would_overflow_buffer
    +  sta temp_buffer_length+1
    +  pla 
    +  sta temp_buffer_length
    +  ldax tcp_inbound_data_length
    +  jsr  copymem  
    +;increment the pointer into the input buffer  
    +  clc
    +  lda temp_buffer
    +  adc tcp_inbound_data_length
    +  sta temp_buffer
    +  lda temp_buffer+1
    +  adc tcp_inbound_data_length+1
    +  sta temp_buffer+1  
    +  jmp put_zero_at_end_of_dl_buffer
    +
    +@would_overflow_buffer:
    +  pla ;clean up the stack
    +  ldax temp_buffer_length
    +  jsr  copymem 
    +  lda temp_buffer
    +  adc temp_buffer_length
    +  sta temp_buffer
    +  lda temp_buffer+1
    +  adc temp_buffer_length+1
    +  sta temp_buffer+1  
    +  lda #0
    +  sta temp_buffer_length
    +  sta temp_buffer_length+1
    +  jmp put_zero_at_end_of_dl_buffer
    +  
    +  .rodata
    +  get: .byte "GET "
    +  get_length=4
    +  http_preamble: 
    +    .byte " HTTP/1.0",$0d,$0a
    +    .byte "User-Agent: IP65/"
    +    .include "../inc/version.i"
    +    .byte $0d,$0a
    +    .byte "Connection: close",$0d,$0a
    +    .byte  "Host: ",0
    +  
    +  colon_slash_slash: .byte ":/"
    +  slash: .byte "/",0
    +  colon: .byte ":",0
    +  
    +  
    +
    +
    +;-- LICENSE FOR url.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ip65_xmodem_s.html b/docs/ip65_xmodem_s.html new file mode 100644 index 0000000..35626b1 --- /dev/null +++ b/docs/ip65_xmodem_s.html @@ -0,0 +1,609 @@ +

    ip65 technical reference

    File : ip65/xmodem.s

     XMODEM file transfer
    +

    functions

    functiondescription
    xmodem_receive
    recieve a file via XMODEM (checksum mode only, not CRC)
    +assumes that a tcp connection has already been set up, and that the other end is waiting to start sending
    +inputs: AX points to routine to call once for each byte in downloaded file (e.g. save to disk, print to screen, whatever) - byte will be in A
    + xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
    +outputs: none
    
    xmodem_send
    send a file via XMODEM (checksum mode only, not CRC)
    +assumes that a tcp connection has already been set up, and that the other end is waiting to start receiving
    +inputs: AX points to routine to call once for each byte in file to send (e.g. save to disk, print to screen, whatever) - byte will be in A, carry flag set means EOF
    + xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
    +outputs: none
    

    variables

    variabledescriptionsize (bytes)
    xmodem_iac_escapeare IAC bytes ($FF) escaped? 1

    implementation

    ; XMODEM file transfer
    +
    +.include "../inc/common.i"
    +.ifndef KPR_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +  
    +
    +XMODEM_BLOCK_SIZE=$80  ;how many bytes (excluding header & checksum) in each block?
    +XMODEM_TIMEOUT_SECONDS=5
    +XMODEM_MAX_ERRORS=10
    +SOH = $01
    +EOT = $04
    +ACK = $06
    +NAK = $15
    +CAN = $18
    +PAD = $1A ;padding added to end of file
    +
    +.export xmodem_receive
    +.export xmodem_send
    +
    +.export xmodem_iac_escape ;are IAC bytes ($FF) escaped?
    +
    +.import ip65_process
    +.import ip65_error
    +.import tcp_callback
    +.import copymem
    +.importzp copy_src
    +.importzp copy_dest
    +.import tcp_send
    +.import tcp_send_data_len
    +.import tcp_inbound_data_ptr
    +.import tcp_inbound_data_length
    +.import check_for_abort_key
    +.import print_a
    +.import print_cr
    +.import print_ascii_as_native
    +.import print_hex
    +.import timer_seconds
    +
    +.segment "SELF_MODIFIED_CODE"
    +got_byte:
    +  jmp $ffff
    +
    +get_byte:
    +  jmp $ffff
    +
    +next_char:
    +  lda buffer_length
    +  bne @not_eof
    +  lda buffer_length+1
    +  bne @not_eof
    +  sec
    +  rts
    +@not_eof:  
    +  next_char_ptr=*+1
    +  lda $ffff
    +  pha
    +  inc next_char_ptr
    +  bne :+
    +  inc next_char_ptr+1
    +:  
    +  sec
    +  lda   buffer_length
    +  sbc #1
    +  sta   buffer_length
    +  lda   buffer_length+1
    +  sbc #0
    +  sta   buffer_length+1
    +  pla
    +  clc  
    +  rts
    +  
    +
    +emit_a:
    +;put a byte to the output buffer
    +;if the byte is $FF, and xmodem_iac_escape is not zero, it is doubled
    +  jsr @real_emit_a
    +  cmp #$ff
    +  bne exit_emit_a
    +  ldx xmodem_iac_escape
    +  beq exit_emit_a
    +@real_emit_a:
    +emit_a_ptr=*+1
    +  sta $ffff
    +  inc emit_a_ptr
    +  bne :+
    +  inc emit_a_ptr+1
    +:
    +  inc xmodem_block_buffer_length
    +  bne :+
    +  inc xmodem_block_buffer_length+1
    +:
    +exit_emit_a:
    +  rts
    +  
    +  .bss
    +
    +original_tcp_callback: .res 2
    +getc_timeout_end: .res 1
    +getc_timeout_seconds: .res 1
    +buffer_length: .res 2
    +
    +  .code
    +  
    +;send a file via XMODEM (checksum mode only, not CRC)
    +;assumes that a tcp connection has already been set up, and that the other end is waiting to start receiving
    +;inputs: AX points to routine to call once for each byte in file to send (e.g. save to disk, print to screen, whatever) - byte will be in A, carry flag set means EOF
    +; xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
    +;outputs: none
    +xmodem_send:
    +  stax get_byte+1
    +  jsr xmodem_transfer_setup
    +  lda #0
    +  sta at_eof
    +
    +@send_block:
    +  ldax #sending
    +  jsr print_ascii_as_native
    +
    +  ldax #block_number_msg
    +  jsr print_ascii_as_native
    +  lda expected_block_number
    +  jsr print_hex
    +  jsr print_cr
    +
    +
    +@wait_for_ack_or_nak:
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcs @synch_error
    +  cmp   #ACK
    +  beq @got_ack
    +  cmp   #NAK
    +  beq @got_nak
    +
    +@synch_error:
    +  pha
    +  lda user_abort
    +  beq @no_user_abort
    +  pla
    +  jmp xmodem_transfer_exit
    +@no_user_abort:  
    +
    +  
    +;flush the input buffer
    +  lda   #0
    +  sta   buffer_length
    +  lda   buffer_length+1
    +
    +  lda #'('
    +  jsr print_a
    +  pla 
    +  jsr print_hex
    +  lda #')'
    +  jsr print_a
    +  
    +  inc error_number
    +  ldax #sync_error_msg
    +  jsr print_ascii_as_native
    +  ldax #error_count_msg
    +  jsr print_ascii_as_native
    +  lda error_number
    +  jsr print_hex
    +  jsr print_cr
    +  lda error_number
    +  cmp #XMODEM_MAX_ERRORS
    +  bcc @wait_for_ack_or_nak
    +  lda #KPR_ERROR_TOO_MANY_ERRORS
    +  sta ip65_error
    +  
    +
    +  jmp xmodem_transfer_exit
    +
    +@got_ack:
    +  inc expected_block_number
    +  lda at_eof
    +  bne @send_eot
    +@got_nak:
    +  lda #0
    +  sta checksum
    +  sta xmodem_block_buffer_length
    +  sta xmodem_block_buffer_length+1
    +  ldax #xmodem_block_buffer
    +  stax emit_a_ptr
    +  
    +  lda #SOH
    +  jsr emit_a
    +  lda expected_block_number
    +  jsr emit_a
    +  eor #$ff
    +  jsr emit_a
    +  lda #$80
    +  sta block_ptr
    +@copy_one_byte:
    +  lda at_eof
    +  bne @add_pad_byte
    +
    +  jsr get_byte
    +  bcc @got_byte
    +  ;sec indicates EOF 
    +  lda block_ptr
    +  cmp #$80  ;have we sent any data at all?
    +  bne @add_pad_byte
    +
    +@send_eot:
    +  ;if we get here, we should send an EOT, then read an ACK
    +  ldax #1
    +  stax tcp_send_data_len
    +  ldax #eot_packet
    +  jsr tcp_send
    +
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc      ;should be an ACK coming back, doesn't really matter if we don't see it though
    +
    +  jmp xmodem_transfer_exit
    +  
    +@add_pad_byte:
    +  lda #PAD
    +@got_byte:  
    +  pha
    +  clc
    +  adc checksum
    +  sta checksum
    +  pla
    +  jsr emit_a
    +  dec block_ptr
    +  bne @copy_one_byte
    +  lda checksum
    +  jsr emit_a
    +      
    +  ldax xmodem_block_buffer_length
    +  stax tcp_send_data_len
    +  ldax #xmodem_block_buffer
    +  jsr tcp_send
    +  bcc @send_ok
    +  ldax #send_error
    +  jsr print_ascii_as_native
    +  lda ip65_error
    +  jsr print_hex
    +  jsr print_cr
    +@send_ok:  
    +  jmp @send_block
    +
    +  rts
    +
    +
    +  
    +xmodem_transfer_setup:
    +  lda #0
    +  sta buffer_length
    +  sta buffer_length+1
    +  sta error_number
    +  sta user_abort
    +  lda #1
    +  sta expected_block_number
    +  
    +
    +  ldax tcp_callback
    +  stax original_tcp_callback
    +  ldax #xmodem_tcp_callback
    +  stax tcp_callback 
    +  rts
    +  
    +
    +;recieve a file via XMODEM (checksum mode only, not CRC)
    +;assumes that a tcp connection has already been set up, and that the other end is waiting to start sending
    +;inputs: AX points to routine to call once for each byte in downloaded file (e.g. save to disk, print to screen, whatever) - byte will be in A
    +; xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
    +;outputs: none
    +xmodem_receive:
    +  
    +  stax got_byte+1
    +  jsr xmodem_transfer_setup
    +  jsr send_nak  
    +
    +
    +@next_block:  
    +  lda #0
    +  sta block_ptr
    +  sta checksum
    +  ldax #expecting
    +  jsr print_ascii_as_native
    +
    +  ldax #block_number_msg
    +  jsr print_ascii_as_native
    +  lda expected_block_number
    +  jsr print_hex
    +  jsr print_cr
    +
    +@wait_for_block_start:
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcc @got_block_start
    +  lda user_abort
    +  beq @no_user_abort
    +  sec
    +  jmp xmodem_transfer_exit
    +@no_user_abort:  
    +  jsr send_nak
    +  inc error_number
    +  ldax #timeout_msg
    +  jsr print_ascii_as_native
    +  ldax #error_count_msg
    +  jsr print_ascii_as_native
    +  lda error_number
    +  jsr print_hex
    +  jsr print_cr
    +  lda error_number
    +  cmp #XMODEM_MAX_ERRORS
    +  bcc @wait_for_block_start
    +  lda #KPR_ERROR_TOO_MANY_ERRORS
    +  sta ip65_error
    +  jmp xmodem_transfer_exit
    +@got_block_start:
    +  cmp #EOT
    +  bne :+
    +  ldax #got_eot
    +  jsr print_ascii_as_native
    +
    +  jsr send_ack
    +  clc
    +  jmp xmodem_transfer_exit
    +:  
    +    
    +  cmp #$81 ;jamming signal BBS seems to use $81 not $01 as SOH
    +  beq @got_soh
    +  cmp #SOH
    +  beq @got_soh
    +  lda #'!'  ;we got an unexpected character
    +  jsr print_a
    +  jsr print_hex
    +  ;we need to clear the input buffer
    +@clear_input_buffer:  
    +  lda #'!'  ;we got an unexpected character
    +  jsr print_a
    +  lda #1
    +  jsr getc
    +  bcc @clear_input_buffer  
    +  
    +
    +  jmp @wait_for_block_start
    +@got_soh:
    +  ;now get block number
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcc :+
    +  jsr send_nak
    +  lda #'.'
    +  jmp print_a
    +  jmp @wait_for_block_start
    +:
    +  sta actual_block_number
    +  
    +  ;now get block number check
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcc :+
    +  lda #'.'
    +  jmp print_a
    +  jsr send_nak
    +  jmp @wait_for_block_start
    +:
    +  adc actual_block_number
    +  cmp #$ff
    +  beq :+
    +  lda #'?'
    +  jsr print_a  
    +  jmp @wait_for_block_start
    +:  
    +  ldax #receiving
    +  jsr print_ascii_as_native
    +
    +  ldax #block_number_msg
    +  jsr print_ascii_as_native
    +  lda actual_block_number
    +  jsr print_hex
    +  jsr print_cr
    +  
    +@next_byte:
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcc :+
    +  jmp xmodem_transfer_exit
    +:  
    +  ldx block_ptr
    +  sta xmodem_block_buffer,x
    +  adc checksum
    +  sta checksum
    +  
    +  inc block_ptr
    +  lda block_ptr
    +  bpl @next_byte
    +  
    +  ldax #checksum_msg
    +  jsr print_ascii_as_native
    +  
    +  lda checksum
    +  jsr print_hex
    +  
    +  lda #'/'
    +  jsr print_a
    +  
    +  lda #XMODEM_TIMEOUT_SECONDS
    +  jsr getc
    +  bcs xmodem_transfer_exit
    +  sta received_checksum
    +  jsr print_hex
    +  jsr print_cr
    +  
    +  lda received_checksum
    +  cmp checksum
    +  beq @checksum_ok
    +  ;checksum error :-(
    +  inc error_number
    +  ldax #checksum_error_msg
    +  jsr print_ascii_as_native
    +  ldax #error_count_msg
    +  jsr print_ascii_as_native
    +  lda error_number
    +  jsr print_hex
    +  jsr print_cr
    +  lda error_number
    +  cmp #XMODEM_MAX_ERRORS
    +  bcs :+
    +  jmp @wait_for_block_start
    +:  
    +  lda #KPR_ERROR_TOO_MANY_ERRORS
    +  sta ip65_error
    +  jmp xmodem_transfer_exit
    +
    +  jsr send_nak
    +  jmp @next_block
    +
    +@checksum_ok:
    +  lda expected_block_number
    +  cmp actual_block_number
    +  bne @skip_block_output
    +
    +  lda #0
    +  sta block_ptr
    +
    +@output_byte:
    +  ldx block_ptr
    +  lda xmodem_block_buffer,x
    +  jsr got_byte
    +  inc block_ptr
    +  lda block_ptr
    +  bpl @output_byte
    +
    +  inc expected_block_number
    +
    +@skip_block_output:
    +  jsr send_ack
    +  jmp @next_block
    +
    +  clc
    +
    +xmodem_transfer_exit:
    +  ldax original_tcp_callback
    +  stax tcp_callback
    +  
    +  rts
    +
    +xmodem_tcp_callback:
    +  lda tcp_inbound_data_length+1
    +  cmp #$ff
    +  bne @not_eof
    +  rts
    +@not_eof:
    +  
    +  ldax tcp_inbound_data_ptr
    +  stax copy_src
    +  ldax #xmodem_stream_buffer
    +  stax copy_dest
    +  stax next_char_ptr
    +
    +  ldax tcp_inbound_data_length
    +  stax buffer_length
    +  jsr copymem
    +  rts
    +  
    +send_nak:
    +  ldax #1
    +  stax tcp_send_data_len
    +  ldax #nak_packet
    +  jmp tcp_send
    +
    +send_ack:
    +  ldax #1
    +  stax tcp_send_data_len
    +  ldax #ack_packet
    +  jmp tcp_send
    +
    +
    +getc:
    +  jsr @real_getc
    +  bcc :+  ;of we got an error, then bail
    +  rts
    +:  
    +  cmp #$ff
    +  beq @got_ff
    +  clc
    +  rts
    +@got_ff:    
    +  lda xmodem_iac_escape
    +  bne @real_getc  ;need to skip over the $FF and go read another byte
    +  lda #$ff
    +  clc
    +  rts
    +  
    +@real_getc:
    +  sta getc_timeout_seconds
    +
    +  clc
    +  jsr timer_seconds  ;time of day clock: seconds (in BCD)
    +  sed
    +  adc getc_timeout_seconds
    +  cmp #$60
    +  bcc @timeout_set
    +  sec
    +  sbc #$60
    +@timeout_set:  
    +  cld
    +  sta getc_timeout_end  
    +
    +@poll_loop: 
    +  jsr next_char
    +  bcs @no_char
    +  rts ;done!
    +@no_char:  
    +  jsr check_for_abort_key
    +  bcc @no_abort
    +  lda #KPR_ERROR_ABORTED_BY_USER
    +  sta ip65_error
    +  inc user_abort
    +  rts
    +@no_abort:  
    +  jsr ip65_process
    +  jsr timer_seconds  ;time of day clock: seconds
    +  cmp getc_timeout_end  
    +  bne @poll_loop
    +  lda #00
    +  sec
    +  rts
    +  
    +  
    +
    +.rodata
    +  ack_packet: .byte ACK
    +  nak_packet: .byte NAK
    +  eot_packet: .byte EOT
    +  
    +block_number_msg: .byte " block $",0
    +expecting: .byte "expecting",0
    +receiving: .byte "receiving",0
    +sending: .byte "sending",0
    +got_eot: .byte "end of transmission",10,0
    +
    +bad_block_number: .byte "bad block number",0
    +checksum_msg: .byte "checksum $",0
    +checksum_error_msg : .byte "checksum",0 
    +timeout_msg: .byte "timeout error",0
    +sync_error_msg: .byte "sync",0
    +error_count_msg: .byte " error - error count $",0
    +send_error: .byte " send error - $",0
    +
    +.segment "APP_SCRATCH"
    +xmodem_stream_buffer: .res 1600
    +xmodem_block_buffer: .res 300
    +xmodem_block_buffer_length: .res 2
    +expected_block_number: .res 1
    +actual_block_number: .res 1
    +checksum: .res 1
    +received_checksum: .res 1
    +block_ptr: .res 1
    +error_number: .res 1
    +user_abort: .res 1
    +xmodem_iac_escape: .res 1
    +at_eof: .res 1
    +;-- LICENSE FOR xmodem.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/ref_frames.html b/docs/ref_frames.html new file mode 100644 index 0000000..2d78406 --- /dev/null +++ b/docs/ref_frames.html @@ -0,0 +1,18 @@ + + + + + + ip65 technical reference + + + + + + + + + + diff --git a/docs/ref_index.html b/docs/ref_index.html new file mode 100644 index 0000000..5cd8383 --- /dev/null +++ b/docs/ref_index.html @@ -0,0 +1 @@ +

    ip65 technical reference

    files

    filesymbols
    drivers/a2charconv.sascii_to_native, native_to_ascii
    drivers/a2input.scheck_for_abort_key, filter_dns, filter_ip, filter_number, filter_text, get_filtered_input, get_key, get_key_if_available, get_key_ip65
    drivers/a2kernal.sexit_to_basic
    drivers/a2print.sbeep, cls, print_a, print_a_inverse, print_cr, screen_current_col, screen_current_row
    drivers/a2timer.stimer_init, timer_read
    drivers/c64_vt100.svt100_init_terminal, vt100_process_inbound_char, vt100_transform_outbound_char
    drivers/c64inputs.scheck_for_abort_key, filter_dns, filter_ip, filter_number, filter_text, filter_url, get_filtered_input, get_key, get_key_if_available, get_key_ip65
    drivers/c64kernal.sexit_to_basic
    drivers/c64print.sbeep, cls, print_a, print_a_inverse, print_cr, screen_current_col, screen_current_row
    drivers/c64timer.stimer_init, timer_read, timer_seconds
    drivers/cbm_disk_access.sio_callback, io_device_no, io_error_buffer, io_filename, io_filesize, io_load_address, io_read_catalogue, io_read_catalogue_ex, io_read_file, io_read_file_with_callback, io_read_sector, io_sector_no, io_track_no, io_write_sector
    drivers/cs8900a.seth_init, eth_rx, eth_tx
    drivers/lan91c96.seth_driver_io_base, eth_driver_name, eth_init, eth_rx, eth_tx
    drivers/petscii_charconv.sascii_to_native, native_to_ascii
    drivers/rr-net.scs_init, cs_packet_data, cs_packet_page, cs_rxtx_data, cs_tx_cmd, cs_tx_len, eth_driver_io_base, eth_driver_name
    drivers/uthernet.scs_init, cs_packet_data, cs_packet_page, cs_rxtx_data, cs_tx_cmd, cs_tx_len, eth_driver_io_base, eth_driver_name
    drivers/vic20-rr-net.scs_init, cs_packet_data, cs_packet_page, cs_rxtx_data, cs_tx_cmd, cs_tx_len, eth_driver_io_base, eth_driver_name
    drivers/vic20input.scheck_for_abort_key, filter_dns, filter_ip, filter_number, filter_text, filter_url, get_filtered_input, get_key, get_key_if_available, get_key_ip65
    drivers/vic20print.sbeep, cls, print_a, print_a_inverse, print_cr, screen_current_col, screen_current_row
    drivers/vic20timer.stimer_init, timer_read, timer_seconds
    drivers/w5100.seth_driver_io_base, eth_driver_name, eth_init, eth_rx, eth_tx, tcp_callback, tcp_close, tcp_connect, tcp_connect_ip, tcp_connect_remote_port, tcp_inbound_data_length, tcp_inbound_data_ptr, tcp_listen, tcp_remote_ip, tcp_send, tcp_send_data_len, tcp_send_keep_alive, tcp_send_string, tcp_state, w5100_ip65_init, w5100_read_register, w5100_select_register, w5100_set_ip_config, w5100_write_register
    ip65/arithmetic.sacc16, acc32, add_16_32, add_32_32, cmp_16_16, cmp_32_32, mul_8_16, op32, sub_16_16
    ip65/arp.sac_size, arp_add, arp_cache, arp_calculate_gateway_mask, arp_init, arp_ip, arp_lookup, arp_mac, arp_process
    ip65/cifs.scifs_l1_decode, cifs_l1_encode, cifs_start
    ip65/config.scfg_default_drive, cfg_dns, cfg_gateway, cfg_get_configuration_ptr, cfg_init, cfg_ip, cfg_mac, cfg_mac_default, cfg_netmask, cfg_size, cfg_tftp_server, dhcp_server
    ip65/copymem.scopy_dest, copy_src, copymem
    ip65/debug.sconsole_out, console_strout, dbg_dump_eth_header, dbg_dump_ip_header, dbg_dump_udp_header, dbgout16
    ip65/dhcp.sdhcp_init, dhcp_state
    ip65/dns.sdns_ip, dns_resolve, dns_set_hostname, dns_status
    ip65/dottedquad.sdotted_quad_value, parse_dotted_quad
    ip65/eth.seth_data, eth_dest, eth_inp, eth_inp_len, eth_outp, eth_outp_len, eth_proto_arp, eth_proto_ip, eth_set_broadcast_dest, eth_set_my_mac_src, eth_set_proto, eth_src, eth_type
    ip65/function_dispatcher.skipper_dispatcher
    ip65/http.shttp_get_value, http_parse_request, http_variables_buffer
    ip65/httpd.shttpd_start
    ip65/icmp.sicmp_add_listener, icmp_callback, icmp_cksum, icmp_code, icmp_data, icmp_echo_ip, icmp_init, icmp_inp, icmp_outp, icmp_ping, icmp_process, icmp_remove_listener, icmp_send_echo, icmp_type
    ip65/ip.sip_broadcast, ip_calc_cksum, ip_cksum_ptr, ip_create_packet, ip_data, ip_dest, ip_frag, ip_header_cksum, ip_id, ip_init, ip_inp, ip_len, ip_outp, ip_process, ip_proto, ip_proto_icmp, ip_proto_tcp, ip_proto_udp, ip_send, ip_src, ip_tos, ip_ttl, ip_ver_ihl
    ip65/ip65.sip65_ctr, ip65_ctr_arp, ip65_ctr_ip, ip65_error, ip65_init, ip65_process, ip65_random_word
    ip65/output_buffer.soutput_buffer
    ip65/parser.sparser_init, parser_skip_next
    ip65/printf.sconsole_printf
    ip65/sntp.ssntp_get_time, sntp_ip, sntp_utc_timestamp
    ip65/string_utils.sparse_hex_digits, parse_integer
    ip65/tcp.stcp_callback, tcp_close, tcp_connect, tcp_connect_ip, tcp_connect_remote_port, tcp_inbound_data_length, tcp_inbound_data_ptr, tcp_init, tcp_listen, tcp_process, tcp_remote_ip, tcp_send, tcp_send_data_len, tcp_send_keep_alive, tcp_send_string, tcp_state
    ip65/telnet \ No newline at end of file diff --git a/docs/test_test_disk_io_s.html b/docs/test_test_disk_io_s.html new file mode 100644 index 0000000..317a76b --- /dev/null +++ b/docs/test_test_disk_io_s.html @@ -0,0 +1,472 @@ +

    ip65 technical reference

    File : test/test_disk_io.s

    variables

    variabledescriptionsize (bytes)
    output_buffer520

    implementation

    +.ifndef KIPPER_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +.include "../inc/common.i"
    +.include "../inc/commonprint.i"
    +.import print_a
    +.import cfg_get_configuration_ptr
    +.import  io_device_no
    +.import  io_sector_no
    +.import  io_track_no
    +.import  io_read_sector
    +.import  io_write_sector
    +.import io_read_file_with_callback
    +.import io_read_file
    +.import io_filename
    +.import io_filesize
    +.import io_load_address
    +.import io_callback
    +.import get_key
    +.import ip65_error
    +.import io_read_catalogue_ex
    +
    +.macro cout arg
    +  lda arg
    +  jsr print_a
    +.endmacro   
    +
    +
    +
    +.bss
    + sector_buffer: .res 256
    + output_buffer: .res 520
    + .export output_buffer
    +current_byte_in_row: .res 1
    +current_byte_in_sector: .res 1
    +start_of_current_row: .res 1
    +
    +directory_buffer: .res 4096
    +
    +.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
    +
    +init:
    +
    +  ;switch to lower case charset
    +  lda #23
    +  sta $d018
    +
    +  ;test we can read catalogue the hard way
    +  ldax #loading
    +  jsr print
    +  ldax #dir_fname
    +  stax io_filename
    +  jsr print
    +
    +
    +  jsr print_cr
    +  lda #01
    +  sta io_device_no
    +
    +  ldax #readfile_callback
    +  stax  io_callback
    +  ldax  #$3000
    +  jsr io_read_file
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:  
    +
    +
    +
    +  ;test we can write sector to default desk
    +  
    +  ldx #$00
    +@fill_sector_loop:  
    +  txa
    +  sta sector_buffer,x
    +  inx  
    +  bne @fill_sector_loop
    +  
    +  lda #$01
    +  sta io_track_no
    +  lda #$01
    +  sta io_sector_no
    +  ldax #write_sector
    +  jsr print
    +  lda io_sector_no
    +  jsr print_hex
    +  jsr print_cr
    +  ldax #sector_buffer  
    +  jsr io_write_sector
    +  
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:
    +
    +
    +
    +  inc io_sector_no
    +  ldax #write_sector
    +  jsr print
    +  lda io_sector_no
    +  jsr print_hex
    +  jsr print_cr
    +  ldax #sector_buffer
    +  jsr io_write_sector
    +  
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:
    +
    +  inc io_sector_no
    +  ldax #write_sector
    +  jsr print
    +  lda io_sector_no
    +  jsr print_hex
    +  jsr print_cr
    +  ldax #sector_buffer
    +  jsr io_write_sector
    +  
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:
    +
    +
    +
    +  ;test we can read a sector from default desk
    +  ldax #read_sector
    +  jsr print
    +
    +  lda #$01
    +  sta io_track_no
    +  lda #$03
    +  sta io_sector_no
    +  ldax #sector_buffer
    +  jsr io_read_sector
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:
    +  
    +  jsr dump_sector
    +  
    +  ;test we can read the catalogue
    +  ldax #read_catalogue
    +  jsr print  
    +
    +  lda #01
    +  sta io_device_no
    +
    +  ldax #directory_buffer
    +  jsr io_read_catalogue_ex
    +  
    +  bcc @no_error_on_catalogue
    +  jsr print_error_code
    +  rts
    +@no_error_on_catalogue:  
    +  ldax #directory_buffer
    +  jsr print_catalogue
    +
    +  ;test we can read without callbacks to fixed buffer
    +   ldax #loading
    +  jsr print
    +  ldax #fname
    +  stax io_filename
    +  jsr print
    +
    +
    +  jsr print_cr
    +  lda #01
    +  sta io_device_no
    +
    +  ldax #readfile_callback
    +  stax  io_callback
    +  ldax  #$3000
    +  jsr io_read_file
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:  
    +
    +  ldax io_filesize
    +  jsr print_integer
    +  ldax #bytes_to
    +  jsr print
    +  lda io_load_address+1
    +  jsr print_hex
    +  lda io_load_address
    +  jsr print_hex
    +  jsr print_cr
    +
    +
    +;test we can read without callbacks to address in file
    +   ldax #loading
    +  jsr print
    +  ldax #fname2
    +  stax io_filename
    +  jsr print
    +
    +
    +  jsr print_cr
    +  lda #01
    +  sta io_device_no
    +
    +  ldax #readfile_callback
    +  stax  io_callback
    +  ldax  #$0000
    +  jsr io_read_file
    +  bcc :+
    +  jsr print_error_code
    +  rts
    +:  
    +
    +  ldax io_filesize
    +  jsr print_integer
    +  ldax #bytes_to
    +  jsr print
    +  lda io_load_address+1
    +  jsr print_hex
    +  lda io_load_address
    +  jsr print_hex
    +  jsr print_cr
    +
    +  jsr wait_for_keypress
    +  
    +  ;test we can read via callbacks
    +
    +  ldax #loading
    +  jsr print
    +  ldax #fname
    +  stax io_filename
    +  jsr print
    +
    +  jsr print_cr
    +  lda #01
    +  sta io_device_no
    +
    +  ldax #readfile_callback
    +  stax  io_callback
    +  ldax  #sector_buffer
    +    
    +  jsr io_read_file_with_callback
    +  bcc :+
    +  jsr print_error_code
    +:  
    +
    +
    +  
    +  rts
    +  
    + 
    +@error:
    +  jsr print_cr
    +  lda ip65_error
    +  jsr print_hex
    +  rts
    +
    +
    +;print catalogue pointed at by AX
    +print_catalogue:
    +  stax tmp_buffer_ptr
    +
    + @print_one_filename:
    +  jsr read_byte_from_buffer
    +  beq @catalogue_done
    +@print_one_char:
    +  jsr print_a
    +  jsr read_byte_from_buffer
    +  beq @end_of_filename
    +  jmp @print_one_char
    +@end_of_filename:
    +    jsr print_cr
    +  ldax #filetype
    +  jsr print
    +  jsr read_byte_from_buffer
    +  jsr print_hex
    +  ldax #sectors
    +  jsr print
    +  jsr read_byte_from_buffer
    +  pha
    +  jsr read_byte_from_buffer
    +  jsr print_hex
    +  pla
    +  jsr print_hex
    +  jsr print_cr
    +  jmp @print_one_filename
    +@catalogue_done:
    +  rts
    +
    +read_byte_from_buffer:
    +tmp_buffer_ptr=read_byte_from_buffer+1
    +  lda $ffff
    +  inc tmp_buffer_ptr
    +  bne :+
    +  inc tmp_buffer_ptr+1
    +:  
    +  pha
    +  pla ;reload A so flags are set correctly
    +  rts
    +
    +
    +
    +readfile_callback:
    +  tya
    +  jsr print_hex
    +  ldax #bytes
    +  jsr print
    +  jsr dump_sector
    +  rts
    +
    +print_error_code:
    +  jsr print_cr
    +  ldax  #error
    +  jsr print  
    +  lda ip65_error
    +  jsr print_hex
    +  jsr print_cr
    +  rts
    +
    +wait_for_keypress:
    +  lda #0
    +  sta $c6 ;set the keyboard buffer to be empty
    +  ldax  #press_a_key_to_continue
    +  jsr print
    +  jsr get_key  
    +  rts
    +
    +dump_sector:
    +;hex dump sector
    +  lda #0
    +  sta current_byte_in_sector
    +  sta start_of_current_row
    +  
    +@one_row:
    +  lda #$80
    +  cmp current_byte_in_sector
    +  bne @dont_wait_for_key
    +  jsr wait_for_keypress
    +@dont_wait_for_key:  
    +  lda current_byte_in_sector
    +  
    +  sta start_of_current_row
    +  jsr print_hex
    +  lda #':'
    +  jsr print_a
    +  lda #' '
    +  jsr print_a
    +
    +  lda #0
    +  sta current_byte_in_row
    +  
    +;first the hex values  
    +@dump_byte:
    +  ldy current_byte_in_sector
    +  lda sector_buffer,y
    +  jsr print_hex
    +  lda #' '
    +  jsr print_a
    +  inc current_byte_in_sector
    +  inc current_byte_in_row
    +  lda current_byte_in_row
    +  cmp #08
    +  bne @dump_byte
    +  
    +;now the ascii values
    +  lda start_of_current_row
    +  sta current_byte_in_sector
    +@print_byte:
    +  ldy current_byte_in_sector
    +  lda sector_buffer,y
    +  cmp #32
    +  bmi @not_printable
    +  cmp #94
    +  bmi @printable
    +@not_printable:
    +  lda #'.'
    +@printable:
    +  jsr print_a
    +  inc current_byte_in_sector
    +  beq @last_byte
    +  dec current_byte_in_row
    +  bne @print_byte
    +  jsr print_cr
    +  jmp @one_row
    +@last_byte:
    +  jsr print_cr
    +  jsr wait_for_keypress
    +  rts
    +
    +
    +
    +write_sector:
    +  .byte "WRITING SECTOR",0
    +
    +read_sector:
    +  .byte "READING SECTOR",13,0
    +
    +
    +dir_fname: .byte "$",0
    +
    +read_catalogue:
    +  .byte "READING CATALOGUE",13,0
    +fname:  
    +  .byte "TEST_DISK_IO.PRG",0
    +
    +fname2:
    +  .byte "SCREEN.PRG",0
    +
    +loading: .byte "LOADING ",0
    +.rodata
    +
    +filetype:
    +  .byte "TYPE: $",0
    +
    +sectors:
    +  .byte " SECTORS: $",0
    +error:
    +  .byte "ERROR - $", 0
    +
    +failed:
    +  .byte "FAILED ", 0
    +
    +ok:
    +  .byte "OK ", 0
    +
    +bytes:
    +  .byte " BYTES.", 0
    +
    +bytes_to:
    +  .byte " BYTES TO $", 0
    +
    +
    +
    +
    +;-- LICENSE FOR test_disk_io.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; compliance with the License. You may obtain a copy of the License at
    +; http://www.mozilla.org/MPL/
    +; 
    +; Software distributed under the License is distributed on an "AS IS"
    +; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    +; License for the specific language governing rights and limitations
    +; under the License.
    +; 
    +; The Original Code is ip65.
    +; 
    +; The Initial Developer of the Original Code is Jonno Downes,
    +; jonno@jamtronix.com.
    +; Portions created by the Initial Developer are Copyright (C) 2009
    +; Jonno Downes. All Rights Reserved.  
    +; -- LICENSE END --
    +
    \ No newline at end of file diff --git a/docs/test_test_getc_s.html b/docs/test_test_getc_s.html new file mode 100644 index 0000000..e1b840c --- /dev/null +++ b/docs/test_test_getc_s.html @@ -0,0 +1,182 @@ +

    ip65 technical reference

    File : test/test_getc.s

    variables

    variabledescriptionsize (bytes)
    output_buffer

    implementation

    +.ifndef KIPPER_API_VERSION_NUMBER
    +  .define EQU     =
    +  .include "../inc/kipper_constants.i"
    +.endif
    +
    +.include "../inc/common.i"
    +.include "../inc/commonprint.i"
    +.import print_a
    +.import cfg_get_configuration_ptr
    +.import  io_device_no
    +.import  io_sector_no
    +.import  io_track_no
    +.import  io_read_sector
    +.import  io_write_sector
    +
    +.import io_read_file_with_callback
    +.import io_read_file
    +.import io_filename
    +.import io_filesize
    +.import io_load_address
    +.import io_callback
    +.import get_key
    +.import ip65_error
    +.import ip65_process
    +.import io_read_catalogue_ex
    +
    +.macro cout arg
    +  lda arg
    +  jsr print_a
    +.endmacro   
    +
    +
    +
    +.bss
    + sector_buffer: .res 256
    + output_buffer: .res 520
    + .export output_buffer
    +current_byte_in_row: .res 1
    +current_byte_in_sector: .res 1
    +start_of_current_row: .res 1
    +
    +directory_buffer: .res 4096
    +
    +.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
    +
    +init:
    +  lda #14
    +  jsr print_a ;switch to lower case 
    +  lda     $dc08 ;read deci-seconds - start clock ticking
    +  sta  $dc08
    +  jsr load_buffer
    +@loop:
    +  lda #5  ;timeout period
    +  jsr getc
    +  bcs @done
    +  jsr print_a
    +  jmp @loop
    +@done:
    +  rts
    +
    +load_buffer:
    +  ldax #buffer
    +  stax next_char_ptr
    +  ldax #buffer_length
    +  stax buff_length
    +  rts
    +
    +
    +getc:
    +  sta getc_timeout_seconds
    +
    +  clc
    +  lda $dc09  ;time of day clock: seconds (in BCD)
    +  sed
    +  adc getc_timeout_seconds
    +  cmp #$60
    +  bcc @timeout_set
    +  sec
    +  sbc #$60
    +@timeout_set:  
    +  cld
    +  sta getc_timeout_end  
    +
    +@poll_loop: 
    +  jsr ip65_process
    +  jsr next_char
    +  bcs @no_char
    +  rts ;done!
    +@no_char:
    +  lda $dc09  ;time of day clock: seconds
    +  cmp getc_timeout_end  
    +  bne @poll_loop
    +  sec
    +  rts
    +  
    +next_char:
    +  lda buff_length
    +  bne @not_eof
    +  lda buff_length+1
    +  bne @not_eof
    +  sec
    +  rts
    +@not_eof:  
    +  next_char_ptr=*+1
    +  lda $ffff
    +  pha
    +  inc next_char_ptr
    +  bne :+
    +  inc next_char_ptr+1
    +:  
    +  sec
    +  lda   buff_length
    +  sbc #1
    +  sta   buff_length
    +  lda   buff_length+1
    +  sbc #0
    +  sta   buff_length+1
    +  pla
    +  clc
    +  
    +  rts
    +  
    +.rodata
    +buffer:
    +  .byte "this is a test1!",13
    +  .byte "this is a test2!",13
    +  .byte "this is a test3!",13
    +  .byte "this is a test4!",13
    +  .byte "this is a test5!",13
    +  .byte "this is a test6!",13
    +  .byte "this is a test7!",13
    +  .byte "this is a test8!",13
    +  .byte "this is a test9!",13
    +  .byte "this is a test10!",13
    +  .byte "this is a test1@",13
    +  .byte "this is a test2@",13
    +  .byte "this is a test3@",13
    +  .byte "this is a test4@",13
    +  .byte "this is a test5@",13
    +  .byte "this is a test6@",13
    +  .byte "this is a test7@",13
    +  .byte "this is a test8@",13
    +  .byte "this is a test9@",13
    +  .byte "this is a test10@",13
    +  .byte "this is a test1*",13
    +  .byte "this is a test2*",13
    +  .byte "this is a test3*",13
    +  .byte "this is a test4*",13
    +  .byte "this is a test5*",13
    +  .byte "this is a test6*",13
    +  .byte "this is a test7*",13
    +  .byte "this is a test8*",13
    +  .byte "this is a test9*",13
    +  .byte "this is a test10*",13
    +
    +buffer_length=*-buffer
    +
    +.bss
    +getc_timeout_end: .res 1
    +getc_timeout_seconds: .res 1
    +buff_length: .res 2
    +
    +
    +;-- LICENSE FOR test_getc.s --
    +; The contents of this file are subject to the Mozilla Public License
    +; Version 1.1 (the "License"); you may not use this file except in
    +; complianc
    \ No newline at end of file
    diff --git a/docs/variable_index.html b/docs/variable_index.html
    new file mode 100644
    index 0000000..163bbcc
    --- /dev/null
    +++ b/docs/variable_index.html
    @@ -0,0 +1 @@
    +

    variables

    variabledefined in
    arp_cacheip65/arp.s
    arp_ipip65/arp.s
    arp_macip65/arp.s
    cfg_default_driveip65/config.s
    cfg_dnsip65/config.s
    cfg_gatewayip65/config.s
    cfg_ipip65/config.s
    cfg_macip65/config.s
    cfg_netmaskip65/config.s
    cfg_tftp_serverip65/config.s
    copy_destip65/copymem.s
    copy_srcip65/copymem.s
    dhcp_serverip65/config.s
    dhcp_stateip65/dhcp.s
    dns_ipip65/dns.s
    dns_statusip65/dns.s
    dotted_quad_valueip65/dottedquad.s
    eth_inpip65/eth.s
    eth_inp_lenip65/eth.s
    eth_outpip65/eth.s
    eth_outp_lenip65/eth.s
    icmp_callbackip65/icmp.s
    icmp_echo_ipip65/icmp.s
    io_filenamedrivers/cbm_disk_access.s
    io_filesizedrivers/cbm_disk_access.s
    io_load_addressdrivers/cbm_disk_access.s
    io_sector_nodrivers/cbm_disk_access.s
    io_track_nodrivers/cbm_disk_access.s
    ip65_ctrip65/ip65.s
    ip65_ctr_arpip65/ip65.s
    ip65_ctr_ipip65/ip65.s
    ip65_errorip65/ip65.s
    ip_broadcastip65/ip.s
    ip_cksum_ptrip65/ip.s
    op32ip65/arithmetic.s
    output_buffertest/test_getc.s, test/test_disk_io.s, ip65/output_buffer.s
    sntp_utc_timestampip65/sntp.s
    tcp_callbackip65/tcp.s, drivers/w5100.s
    tcp_connect_ipip65/tcp.s, drivers/w5100.s
    tcp_connect_remote_portip65/tcp.s, drivers/w5100.s
    tcp_inbound_data_lengthip65/tcp.s, drivers/w5100.s
    tcp_inbound_data_ptrip65/tcp.s, drivers/w5100.s
    tcp_send_data_lenip65/tcp.s, drivers/w5100.s
    tcp_stateip65/tcp.s, drivers/w5100.s
    telnet_ipip65/telnet.s
    telnet_portip65/telnet.s
    telnet_use_native_charsetip65/telnet.s
    tftp_data_block_lengthip65/tftp.s
    tftp_filenameip65/tftp.s
    tftp_filesizeip65/tftp.s
    tftp_ipip65/tftp.s
    tftp_load_addressip65/tftp.s
    udp_callbackip65/udp.s
    udp_send_destip65/udp.s
    udp_send_dest_portip65/udp.s
    udp_send_lenip65/udp.s
    udp_send_src_portip65/udp.s
    url_download_bufferip65/url.s
    url_download_buffer_lengthip65/url.s
    url_resource_typeip65/url.s
    xmodem_iac_escapeip65/xmodem.s
    \ No newline at end of file