\ 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 @@
+
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 @@
+
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
constants
description
value
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 @@
+
.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 @@
+
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
print_a
+use Apple 2 monitor ROM function to display 1 char
+inputs: A should be set to ASCII char to display
+outputs: none
print_a_inverse
print_cr
use Apple 2 monitor ROM function to move to new line
+inputs: none
+outputs: none
constants
constants
description
value
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 @@
+
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
function
description
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 @@
+
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
function
description
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 @@
+
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
constants
description
value
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 @@
+
.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 @@
+
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
print_a
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_inverse
print a single char in inverse text:
print_cr
use C64 Kernel ROM function to move to a new line
+inputs: none
+outputs: none
constants
constants
description
value
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 @@
+
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
function
description
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 @@
+
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
variable
description
size (bytes)
io_filename
2
io_filesize
although a file on disk can be >64K, io_filesize is only used when loading into RAM hence file must be <64K
2
io_load_address
2
io_sector_no
1
io_track_no
2
constants
constants
description
value
io_callback
jmp_to_callback+1
+
io_device_no
0
+
io_error_buffer
error_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 @@
+
Ethernet driver for CS8900A chip (as used in RR-NET and Uthernet adapters)
+
+ Based on Doc Bacardi's tftp source
+
functions
function
description
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 @@
+
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
constants
description
value
eth_driver_io_base
fixlan01+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 @@
+
ASCII/PETSCII conversion tables
+cribbed from http://www.ffd2.com/fridge/misc/petcom.c
+
functions
function
description
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 @@
+
initialise Retro Replay so we can access the network adapter
+inputs: none
+outputs: none
constants
constants
description
value
cs_packet_data
address of 'packet data' port on RR-Net
IO_BASE+4
cs_packet_page
address of 'packet page' port on RR-Net
IO_BASE+2
cs_rxtx_data
address of 'recieve/transmit data' port on RR-Net
IO_BASE+8
cs_tx_cmd
address of 'transmit command' port on RR-Net
IO_BASE+$0c
cs_tx_len
address 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 @@
+
uthernet driver
+currently hardcoded to use slot 3 addresses only
+
functions
function
description
cs_init
uthernet driver
+currently hardcoded to use slot 3 addresses only
constants
constants
description
value
cs_packet_data
address of 'packet data' port on Uthernet
$c0bc
cs_packet_page
address of 'packet page' port on Uthernet
$c0ba
cs_rxtx_data
address of 'recieve/transmit data' port on Uthernet
$c0b0
cs_tx_cmd
address of 'transmit command' port on Uthernet
$c0b4
cs_tx_len
address 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 @@
+
RR-Net driver, as seen on a VIC-20 (i.e. using a MasC=uerade adapter)
+
functions
function
description
cs_init
initialise Retro Replay so we can access the network adapter
+inputs: none
+outputs: none
constants
constants
description
value
cs_packet_data
address of 'packet data' port on RR-Net
$9804
cs_packet_page
address of 'packet page' port on RR-Net
$9802
cs_rxtx_data
address of 'recieve/transmit data' port on RR-Net
$9808
cs_tx_cmd
address of 'transmit command' port on RR-Net
$980c
cs_tx_len
address 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 @@
+
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
constants
description
value
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 @@
+
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
print_a
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_inverse
print a single char in inverse text:
print_cr
use VIC 20 Kernel ROM function to move to a new line
+inputs: none
+outputs: none
constants
constants
description
value
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 @@
+
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
function
description
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 @@
+
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
variable
description
size (bytes)
tcp_callback
vector to routine to be called when data is received over tcp connection
2
tcp_connect_ip
ip address of remote server to connect to
4
tcp_connect_remote_port
2
tcp_inbound_data_length
2
tcp_inbound_data_ptr
2
tcp_send_data_len
2
tcp_state
1
constants
constants
description
value
eth_driver_io_base
eth_driver_name
"RR-NET MK3 (WIZNET 5100)"
+
tcp_remote_ip
tcp_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 @@
+
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
variable
description
size (bytes)
op32
constants
constants
description
value
acc16
acc16=acc16*a
acc16*a
+
acc32
acc32=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 @@
+
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
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
variable
description
size (bytes)
arp_cache
cache 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
constants
description
value
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 @@
+
a simple NETBIOS over TCP server
+aka "Common Internet File System"
+
+ refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/
+
functions
function
description
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 @@
+
IP configuration defaults
+most of these will be overwritten if dhcp is used for configuration
+
functions
function
description
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
variable
description
size (bytes)
cfg_default_drive
1
cfg_dns
ip address of dns server to use (will be overwritten if dhcp_init is called)
4;
cfg_gateway
ip address of router on local network (will be overwritten if dhcp_init is called)
4
cfg_ip
ip address of local machine (will be overwritten if dhcp_init is called)
4
cfg_mac
mac 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_server
will be set address of dhcp server that configuration was obtained from
4
constants
constants
description
value
cfg_mac_default
mac address to be assigned to local machine
$00, $80, $10, $00, $51, $00
cfg_size
cfg_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 @@
+
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
variable
description
size (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 @@
+
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
constants
description
value
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 @@
+
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
function
description
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
variable
description
size (bytes)
dhcp_state
flag 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 @@
+
minimal dns implementation - requires a DNS server that supports recursion
+
functions
function
description
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
variable
description
size (bytes)
dns_ip
will 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 @@
+
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
variable
description
size (bytes)
dotted_quad_value
set 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 @@
+
Common ethernet driver code (independant of host computer or ethernet chipset)
+
functions
function
description
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
variable
description
size (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
constants
description
value
eth_data
offset of packet data in ethernet packet
14
eth_dest
offset of destination address in ethernet packet
0
eth_proto_arp
6
+
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 @@
+
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
function
description
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 @@
+
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
function
description
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 @@
+
a simple HTTP server
+to use - call httpd_start with AX pointing at routine to call for each inbound page
+
functions
function
description
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
",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 @@
+
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
variable
description
size (bytes)
icmp_callback
argument for icmp_add_listener
2
icmp_echo_ip
destination IP address for echo request ("ping")
4
constants
constants
description
value
icmp_cksum
offset of 'checksum' field in icmp packet
2
icmp_code
offset of 'code' field in icmp packet
1
icmp_data
offset of 'data' field in icmp packet
4
icmp_inp
pointer to inbound icmp packet
ip_inp + ip_data
icmp_outp
pointer to outbound icmp packet
ip_outp + ip_data
icmp_type
offset 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 @@
+
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
variable
description
size (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_error
last 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 @@
+
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
variable
description
size (bytes)
ip_broadcast
flag set when an incoming IP packet was sent to a broadcast address
1
ip_cksum_ptr
pointer to data to be checksummed
2
constants
constants
description
value
ip_data
offset of data payload in an IP packet
20
ip_dest
offset of "destination address" field in an IP packet header
16
ip_frag
offset of "fragmentation offset" field in an IP packet header
6
ip_header_cksum
offset of "ip header checksum" field in an IP packet header
10
ip_id
offset of "identification" field in an IP packet header
4
ip_inp
pointer to start of IP packet in input ethernet frame
eth_inp + eth_data
ip_len
offset of "length" field in an IP packet header
2
ip_outp
pointer to start of IP packet in output ethernet frame
eth_outp + eth_data
ip_proto
offset of "protocol number" field in an IP packet header
9
ip_proto_icmp
ip protocols
1
+
ip_proto_tcp
6
+
ip_proto_udp
17
+
ip_src
offset of "source address" field in an IP packet header
12
ip_tos
offset of "type of service" field in an IP packet header
1
ip_ttl
offset of "time to live" field in an IP packet header
8
ip_ver_ihl
offset 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 @@
+
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.
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 @@
+
text file parsing routines
+ first call parser_init
+ then call parser_skip_next
+
functions
function
description
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 @@
+
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 @@
+
Simple Network Time Protocol implementation - per RFC 2030
+
functions
function
description
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
variable
description
size (bytes)
sntp_utc_timestamp
will be set to seconds (only) part of utc timestamp (seconds since 00:00 on Jan 1, 1900)
4
constants
constants
description
value
sntp_ip
can 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 @@
+
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 @@
+
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
function
description
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
variable
description
size (bytes)
tcp_callback
vector to routine to be called when data is received over tcp connection
2
tcp_connect_ip
ip address of remote server to connect to
4
tcp_connect_remote_port
2
tcp_inbound_data_length
length of data just received over tcp connection
2
tcp_inbound_data_ptr
pointer to data just recieved over tcp connection
2
tcp_send_data_len
length (in bytes) of data to be sent over tcp connection
2
tcp_state
1
constants
constants
description
value
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 @@
+
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
function
description
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
\ 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 @@
+
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
variable
description
size (bytes)
tftp_data_block_length
2
tftp_filename
name of file to d/l or filemask to get directory listing for
2
tftp_filesize
will be set by tftp_download, needs to be set before calling tftp_upload_from_memory
2
tftp_ip
ip address of tftp server - set to 255.255.255.255 (broadcast) to send request to all tftp servers on local lan
4
tftp_load_address
address 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 @@
+
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
function
description
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 @@
+
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
variable
description
size (bytes)
udp_callback
vector to routine to be called when a udp packet arrives
2
udp_send_dest
set to ip address that udp packet will be sent to
4
udp_send_dest_port
set to port that udp packet will be sent to
2
udp_send_len
set to length of data to be sent in udp packet (excluding ethernet,ip & udp headers)
2
udp_send_src_port
set to port that udp packet will be sent from
2
constants
constants
description
value
udp_cksum
offset of checksum field in udp packet
6
udp_data
offset of data in udp packet
8
udp_dest_port
offset of destination port field in udp packet
2
udp_inp
pointer to udp packet inside inbound ethernet frame
ip_inp + ip_data
udp_len
offset of length field in udp packet
4
udp_outp
pointer to udp packet inside outbound ethernet frame
ip_outp + ip_data
udp_src_port
offset 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 @@
+
routines for parsing a URL, and downloading an URL
+
functions
function
description
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
variable
description
size (bytes)
url_download_buffer
points to a buffer that url will be downloaded into
2
url_download_buffer_length
length of buffer that url will be downloaded into
2
url_resource_type
1
constants
constants
description
value
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 @@
+
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
variable
description
size (bytes)
xmodem_iac_escape
are 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 @@
+
\ 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 @@
+