Migrated GitHub pages from gh-pages branch to /docs folder.

This commit is contained in:
Oliver Schmidt 2018-09-18 22:01:19 +02:00
parent e6a7ce4f45
commit 9c2e55ce3b
58 changed files with 18159 additions and 0 deletions

55
docs/ca65-doc-style.css Normal file
View File

@ -0,0 +1,55 @@
body { background-color: #cde; color: #333; }
body, p, ol, ul, td, th {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 9pt;
/* line-height: 12pt;*/
}
pre {
background-color: #eee;
padding: 10px;
font-size: 16px;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
td,th {
background-color: #eee;
vertical-align: text-top;
/* padding-left: 3px;
padding-right: 3px;
*/
}
tr.header {
background-color: #aaf;
}
p.footer {
font-size: 12px;
line-height: 16px;
}
h2 {
background-color: #bcd;
}
h3 {
background-color: #bcd;
}
img {
border: 0px;
}
a { color: #000; }
a:visited { color: #666; }
a:hover { color: #fff; background-color:#000; }

1
docs/constant_index.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/a2charconv.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="ascii_to_native">ascii_to_native</td><td><pre>given an ASCII char in A, return equivalent A2 Screen Code </pre></td></tr><tr><td id="native_to_ascii">native_to_ascii</td><td><pre>given an A2 Screen Code char in A, return equivalent ASCII </pre></td></tr></table><h2>implementation</h2><pre id="code">
.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 --
</pre></body></html>

230
docs/drivers_a2input_s.html Normal file
View File

@ -0,0 +1,230 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/a2input.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="check_for_abort_key">check_for_abort_key</td><td><pre>check whether the escape key is being pressed
inputs: none
outputs: sec if escape pressed, clear otherwise </pre></td></tr><tr><td id="get_filtered_input">get_filtered_input</td><td><pre>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 </pre></td></tr><tr><td id="get_key">get_key</td><td><pre>use Apple 2 monitor ROM function to read from keyboard
inputs: none
outputs: A contains ASCII code of key pressed </pre></td></tr><tr><td id="get_key_if_available">get_key_if_available</td><td><pre>inputs: none
outputs: A contains ASCII value of key just pressed (0 if no key pressed) </pre></td></tr><tr><td id="get_key_ip65">get_key_ip65</td><td><pre>process inbound ip packets while waiting for a keypress </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="filter_dns">filter_dns</td><td></td><td>"-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
</td></tr><tr><td id="filter_ip">filter_ip</td><td></td><td>"."
</td></tr><tr><td id="filter_number">filter_number</td><td></td><td>"1234567890",0
</td></tr><tr><td id="filter_text">filter_text</td><td>=================================================
Some example filters
================================================= </td><td>",+!#$%&'()* "
</td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

View File

@ -0,0 +1,26 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/a2kernal.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="exit_to_basic">exit_to_basic</td><td><pre/></td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

View File

@ -0,0 +1,86 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/a2print.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="beep">beep</td><td><pre>use Apple 2 monitor ROM function to move to make a 'beep' noise
inputs: none
outputs: none </pre></td></tr><tr><td id="cls">cls</td><td><pre>use Apple 2 monitor ROM function to move to clear the screen
inputs: none
outputs: none </pre></td></tr><tr><td id="print_a">print_a</td><td><pre>
use Apple 2 monitor ROM function to display 1 char
inputs: A should be set to ASCII char to display
outputs: none </pre></td></tr><tr><td id="print_a_inverse">print_a_inverse</td><td><pre/></td></tr><tr><td id="print_cr">print_cr</td><td><pre>use Apple 2 monitor ROM function to move to new line
inputs: none
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="screen_current_col">screen_current_col</td><td> CH - Horizontal cursor-position (0-39) </td><td>$24 </td></tr><tr><td id="screen_current_row">screen_current_row</td><td> CV - Vertical cursor-position (0-23) </td><td>$25 </td></tr></table><h2>implementation</h2><pre id="code">
.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 --
</pre></body></html>

View File

@ -0,0 +1,80 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/a2timer.s</h1><pre> 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
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="timer_init">timer_init</td><td><pre>reset timer to 0
inputs: none
outputs: none </pre></td></tr><tr><td id="timer_read">timer_read</td><td><pre>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') </pre></td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,223 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/c64inputs.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="check_for_abort_key">check_for_abort_key</td><td><pre>check whether the RUN/STOP key is being pressed
inputs: none
outputs: sec if RUN/STOP pressed, clear otherwise </pre></td></tr><tr><td id="get_filtered_input">get_filtered_input</td><td><pre>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 </pre></td></tr><tr><td id="get_key">get_key</td><td><pre>use C64 Kernel ROM function to read a key
inputs: none
outputs: A contains ASCII value of key just pressed </pre></td></tr><tr><td id="get_key_if_available">get_key_if_available</td><td><pre>not officially documented - where F13E (GETIN) falls through to if device # is 0 (KEYBD) </pre></td></tr><tr><td id="get_key_ip65">get_key_ip65</td><td><pre>process inbound ip packets while waiting for a keypress </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="filter_dns">filter_dns</td><td></td><td>"-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
</td></tr><tr><td id="filter_ip">filter_ip</td><td></td><td>"."
</td></tr><tr><td id="filter_number">filter_number</td><td></td><td>"1234567890",0
</td></tr><tr><td id="filter_text">filter_text</td><td>=================================================
Some example filters
================================================= </td><td>",!#'()* "
</td></tr><tr><td id="filter_url">filter_url</td><td></td><td>":/%&?+$"
</td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

View File

@ -0,0 +1,28 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/c64kernal.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="exit_to_basic">exit_to_basic</td><td><pre> jump to BASIC interpreter loop </pre></td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

View File

@ -0,0 +1,123 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/c64print.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="beep">beep</td><td><pre>currently does nothing (should make a 'beep noise')
inputs: none
outputs: none </pre></td></tr><tr><td id="cls">cls</td><td><pre>use C64 Kernel ROM function to clear the screen
inputs: none
outputs: none </pre></td></tr><tr><td id="print_a">print_a</td><td><pre>use C64 Kernel ROM function to print a character to the screen
inputs: A contains petscii value of character to print
outputs: none </pre></td></tr><tr><td id="print_a_inverse">print_a_inverse</td><td><pre>print a single char in inverse text: </pre></td></tr><tr><td id="print_cr">print_cr</td><td><pre>use C64 Kernel ROM function to move to a new line
inputs: none
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="screen_current_col">screen_current_col</td><td></td><td>$d3
</td></tr><tr><td id="screen_current_row">screen_current_row</td><td></td><td>$d6
</td></tr></table><h2>implementation</h2><pre id="code">
.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 --
</pre></body></html>

View File

@ -0,0 +1,75 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/c64timer.s</h1><pre> 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.
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="timer_init">timer_init</td><td><pre> initialize timers </pre></td></tr><tr><td id="timer_read">timer_read</td><td><pre> return the current value </pre></td></tr><tr><td id="timer_seconds">timer_seconds</td><td><pre> this should return a single BCD byte (00..59) which is a count of seconds </pre></td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

View File

@ -0,0 +1,689 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/cbm_disk_access.s</h1><pre>C64 disk access routines
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="io_read_catalogue">io_read_catalogue</td><td><pre>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) </pre></td></tr><tr><td id="io_read_catalogue_ex">io_read_catalogue_ex</td><td><pre>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. </pre></td></tr><tr><td id="io_read_file">io_read_file</td><td><pre>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.
</pre></td></tr><tr><td id="io_read_file_with_callback">io_read_file_with_callback</td><td><pre>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 </pre></td></tr><tr><td id="io_read_sector">io_read_sector</td><td><pre>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 </pre></td></tr><tr><td id="io_write_sector">io_write_sector</td><td><pre>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. </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="io_filename">io_filename</td><td></td><td>2</td></tr><tr><td id="io_filesize">io_filesize</td><td>although a file on disk can be >64K, io_filesize is only used when loading into RAM hence file must be <64K </td><td>2</td></tr><tr><td id="io_load_address">io_load_address</td><td></td><td>2</td></tr><tr><td id="io_sector_no">io_sector_no</td><td></td><td>1</td></tr><tr><td id="io_track_no">io_track_no</td><td></td><td>2</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="io_callback">io_callback</td><td></td><td>jmp_to_callback+1
</td></tr><tr><td id="io_device_no">io_device_no</td><td></td><td>0
</td></tr><tr><td id="io_error_buffer">io_error_buffer</td><td></td><td>error_buffer
</td></tr></table><h2>implementation</h2><pre id="code">;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
ldy #>cname
jsr SETNAM
lda #02
ldx drive_id
ldy #02
jsr SETLFS
jsr OPEN
bcs @error
lda #7
ldx #<bpname
ldy #>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
ldy #>cname
jsr SETNAM
lda #02
ldx drive_id
ldy #02
jsr SETLFS
jsr OPEN
bcs @error
ldx #<command_buffer
ldy #>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 <x>" 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 --
</pre></body></html>

312
docs/drivers_cs8900a_s.html Normal file
View File

@ -0,0 +1,312 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/cs8900a.s</h1><pre> Ethernet driver for CS8900A chip (as used in RR-NET and Uthernet adapters)
Based on Doc Bacardi's tftp source
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="eth_init">eth_init</td><td><pre>initialize the ethernet adaptor
inputs: none
outputs: carry flag is set if there was an error, clear otherwise </pre></td></tr><tr><td id="eth_rx">eth_rx</td><td><pre>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 </pre></td></tr><tr><td id="eth_tx">eth_tx</td><td><pre> 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 </pre></td></tr></table><h2>implementation</h2><pre id="code">; 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
ldy #>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 ; set packet pointer
sta eth_packet
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 ; select bus status register
sta cs_packet_page
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 --
</pre></body></html>

View File

@ -0,0 +1,483 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/lan91c96.s</h1><pre> Ethernet driver for SMC LAN91C96 chip
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="eth_init">eth_init</td><td><pre>initialize the ethernet adaptor
inputs: none
outputs: carry flag is set if there was an error, clear otherwise </pre></td></tr><tr><td id="eth_rx">eth_rx</td><td><pre>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 </pre></td></tr><tr><td id="eth_tx">eth_tx</td><td><pre> 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 </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="eth_driver_io_base">eth_driver_io_base</td><td></td><td>fixlan01+1
</td></tr><tr><td id="eth_driver_name">eth_driver_name</td><td></td><td>"LANceGS (91C96)"
</td></tr></table><h2>implementation</h2><pre id="code">
; 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
ldx #>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 ; Send the packet
ldx #>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 --
</pre></body></html>

View File

@ -0,0 +1,79 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/petscii_charconv.s</h1><pre>ASCII/PETSCII conversion tables
cribbed from http://www.ffd2.com/fridge/misc/petcom.c
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="ascii_to_native">ascii_to_native</td><td><pre>given an ASCII char in A, return equivalent PETSCII </pre></td></tr><tr><td id="native_to_ascii">native_to_ascii</td><td><pre>given a PETSCII char in A, return equivalent ASCII </pre></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

View File

@ -0,0 +1,67 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/rr-net.s</h1><pre> RR-Net driver
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="cs_init">cs_init</td><td><pre>initialise Retro Replay so we can access the network adapter
inputs: none
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="cs_packet_data">cs_packet_data</td><td>address of 'packet data' port on RR-Net </td><td>IO_BASE+4</td></tr><tr><td id="cs_packet_page">cs_packet_page</td><td>address of 'packet page' port on RR-Net </td><td>IO_BASE+2 </td></tr><tr><td id="cs_rxtx_data">cs_rxtx_data</td><td>address of 'recieve/transmit data' port on RR-Net </td><td>IO_BASE+8 </td></tr><tr><td id="cs_tx_cmd">cs_tx_cmd</td><td>address of 'transmit command' port on RR-Net </td><td>IO_BASE+$0c</td></tr><tr><td id="cs_tx_len">cs_tx_len</td><td>address of 'transmission length' port on RR-Net </td><td>IO_BASE+$0e</td></tr><tr><td id="eth_driver_io_base">eth_driver_io_base</td><td></td><td></td></tr><tr><td id="eth_driver_name">eth_driver_name</td><td></td><td></td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

View File

@ -0,0 +1,57 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/uthernet.s</h1><pre>uthernet driver
currently hardcoded to use slot 3 addresses only
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="cs_init">cs_init</td><td><pre>uthernet driver
currently hardcoded to use slot 3 addresses only </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="cs_packet_data">cs_packet_data</td><td>address of 'packet data' port on Uthernet </td><td>$c0bc</td></tr><tr><td id="cs_packet_page">cs_packet_page</td><td>address of 'packet page' port on Uthernet </td><td>$c0ba</td></tr><tr><td id="cs_rxtx_data">cs_rxtx_data</td><td>address of 'recieve/transmit data' port on Uthernet </td><td>$c0b0 </td></tr><tr><td id="cs_tx_cmd">cs_tx_cmd</td><td>address of 'transmit command' port on Uthernet </td><td>$c0b4</td></tr><tr><td id="cs_tx_len">cs_tx_len</td><td>address of 'transmission length' port on Uthernet </td><td>$c0b6</td></tr><tr><td id="eth_driver_io_base">eth_driver_io_base</td><td></td><td></td></tr><tr><td id="eth_driver_name">eth_driver_name</td><td></td><td>"UTHERNET",0
</td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

View File

@ -0,0 +1,62 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/vic20-rr-net.s</h1><pre> RR-Net driver, as seen on a VIC-20 (i.e. using a MasC=uerade adapter)
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="cs_init">cs_init</td><td><pre>initialise Retro Replay so we can access the network adapter
inputs: none
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="cs_packet_data">cs_packet_data</td><td>address of 'packet data' port on RR-Net </td><td>$9804</td></tr><tr><td id="cs_packet_page">cs_packet_page</td><td>address of 'packet page' port on RR-Net </td><td>$9802 </td></tr><tr><td id="cs_rxtx_data">cs_rxtx_data</td><td>address of 'recieve/transmit data' port on RR-Net </td><td>$9808 </td></tr><tr><td id="cs_tx_cmd">cs_tx_cmd</td><td>address of 'transmit command' port on RR-Net </td><td>$980c</td></tr><tr><td id="cs_tx_len">cs_tx_len</td><td>address of 'transmission length' port on RR-Net </td><td>$980e</td></tr><tr><td id="eth_driver_io_base">eth_driver_io_base</td><td></td><td></td></tr><tr><td id="eth_driver_name">eth_driver_name</td><td></td><td>"VIC20 RR-NET"
</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

View File

@ -0,0 +1,222 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/vic20input.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="check_for_abort_key">check_for_abort_key</td><td><pre>check whether the RUN/STOP key is being pressed
inputs: none
outputs: sec if RUN/STOP pressed, clear otherwise </pre></td></tr><tr><td id="get_filtered_input">get_filtered_input</td><td><pre>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 </pre></td></tr><tr><td id="get_key">get_key</td><td><pre>use Vic 20 Kernel ROM function to read a key
inputs: none
outputs: A contains ASCII value of key just pressed </pre></td></tr><tr><td id="get_key_if_available">get_key_if_available</td><td><pre>not officially documented - where F1f5 (GETIN) falls through to if device # is 0 (KEYBD) </pre></td></tr><tr><td id="get_key_ip65">get_key_ip65</td><td><pre>process inbound ip packets while waiting for a keypress </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="filter_dns">filter_dns</td><td></td><td>"-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
</td></tr><tr><td id="filter_ip">filter_ip</td><td></td><td>"."
</td></tr><tr><td id="filter_number">filter_number</td><td></td><td>"1234567890",0
</td></tr><tr><td id="filter_text">filter_text</td><td>=================================================
Some example filters
================================================= </td><td>",!#'()* "
</td></tr><tr><td id="filter_url">filter_url</td><td></td><td>":/%&?+$"
</td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

View File

@ -0,0 +1,106 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/vic20print.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="beep">beep</td><td><pre>currently does nothing (should make a 'beep noise')
inputs: none
outputs: none </pre></td></tr><tr><td id="cls">cls</td><td><pre>use VIC 20 Kernel ROM function to clear the screen
inputs: none
outputs: none </pre></td></tr><tr><td id="print_a">print_a</td><td><pre>use VIC 20 Kernel ROM function to print a character to the screen
inputs: A contains petscii value of character to print
outputs: none </pre></td></tr><tr><td id="print_a_inverse">print_a_inverse</td><td><pre>print a single char in inverse text: </pre></td></tr><tr><td id="print_cr">print_cr</td><td><pre>use VIC 20 Kernel ROM function to move to a new line
inputs: none
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="screen_current_col">screen_current_col</td><td></td><td>$d3
</td></tr><tr><td id="screen_current_row">screen_current_row</td><td></td><td>$d6
</td></tr></table><h2>implementation</h2><pre id="code">
.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 --
</pre></body></html>

View File

@ -0,0 +1,134 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : drivers/vic20timer.s</h1><pre> 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
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="timer_init">timer_init</td><td><pre>reset timer to 0
inputs: none
outputs: none </pre></td></tr><tr><td id="timer_read">timer_read</td><td><pre>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') </pre></td></tr><tr><td id="timer_seconds">timer_seconds</td><td><pre/></td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

1123
docs/drivers_w5100_s.html Normal file

File diff suppressed because it is too large Load Diff

1
docs/function_index.html Normal file

File diff suppressed because one or more lines are too long

203
docs/index.html Normal file
View File

@ -0,0 +1,203 @@
<html>
<head>
<title>IP65 - a TCP/IP stack for 6502 computers</title>
</head>
<style type="text/css">
body {
background-color: white;
color: black;
}
table {
background-color: #000000;
padding: 0px;
margin: 10px 20px;
}
th {
width: 10em;
background-color: #ccccff;
padding: 4px;
text-align: center;
padding-right: 4px;
# border: solid 2px black;
}
td {
background-color: #ffff99;
font-family: sans-serif;
font-weight: bold;
width: 6em;
padding: 4px;
text-align: center;
white-space: nowrap;
# border: solid 2px black;
}
</style>
<body>
<h1>IP65</h1>
<p>
IP65 is a TCP/IP stack for 6502 based computers.
</p>
<h2>Status</h2>
<table>
<tr>
<th>Applications</th>
<td colspan="1">HTTP Client</td>
<td colspan="1">HTTP Server</td>
<td colspan="1">Telnet Client</td>
<td colspan="1">Gopher Client</td>
<td colspan="2">TFTP</td>
<td colspan="1">Ping</td>
</tr>
<tr>
<th>Services</th>
<td colspan="4" rowspan="2">TCP</td>
<td colspan="1">DHCP</td>
<td colspan="1">DNS</td>
<td colspan="1">Echo</td>
</tr>
<tr>
<th>Transport</th>
<td colspan="2">UDP</td>
<td colspan="1">ICMP</td>
</tr>
<tr>
<th>Network</th>
<td colspan="7">IP</td>
</tr>
<tr>
<th>Addressing</th>
<td colspan="7">ARP</td>
</tr>
<tr>
<th>Ethernet controller</th>
<td colspan="7">CS8900A / LAN91C96 / W5100</td>
</tr>
<tr>
<th>Ethernet driver</th>
<td colspan="1">RR-Net</td>
<td colspan="1">ETH64</td>
<td colspan="1">Uthernet</td>
<td colspan="1">LANceGS</td>
<td colspan="1">Uthernet II</td>
<td colspan="1">Dragon Cart</td>
<td colspan="1">RR-Net</td>
</tr>
<tr>
<th>Host computer</th>
<td colspan="2">C64 / C128</td>
<td colspan="3">Apple ][</td>
<td colspan="1">ATARI 8-bit</td>
<td colspan="1">VIC20</td>
</tr>
</table>
<h2>Documentation</h2>
<a href="ref_frames.html">IP65 technical reference</a>
<h2>Download</h2>
<a href="http://github.com/cc65/ip65/archive/master.zip">Latest code</a> (github.com)<p>
<a href="http://sourceforge.net/projects/ip65/files/ip65-2012-11-21.zip/download">ip65-2012-11-21.zip</a> (sourceforge.net)<p>
<a href="http://web.archive.org/web/20121128054236if_/http://www.paradroid.net/ip65/ip65-2009-01-22.zip">ip65-2009-01-22.zip</a> (paradroid.net)
<h2>History</h2>
<pre>
Release Maintainer Changes
------- ---------- -------
2011-01-15 Jonno Downes Drivers for Wiznet W5100 ethernet, VIC-20 host
2009-12-23 Jonno Downes TCP and telnet bugfixes, vt100 emulation, XMODEM support
2009-10-31 Jonno Downes Added Web Application Server functions
2009-08-02 Jonno Downes More TCP functionality, includes telnet
2009-07-12 Jonno Downes Initial TCP implementation (use -DTCP to include)
2009-03-21 Jonno Downes Added technical reference documentation
2009-03-15 Jonno Downes Added DHCP, DNS & TFTP
2009-01-22 Per Olofsson Added copymem fix from Jonno Downes. Added MPL license.
2008-09-27 Per Olofsson Added timeout fix for ineth_tx from David Schmidt.
2006-09-20 Per Olofsson Fixed checksum calculation for odd packet sizes.
2006-02-22 Per Olofsson Added fix for sending of packets larger than 256 bytes
from Ewen Wannop and Glenn Jones.
</pre>
<h2>Sample UDP listener source</h2>
<pre>
gangedport = 60064
jsr ip65_init
lda #&lt;gotpacket
ldx #&gt;gotpacket
sta udp_callback
stx udp_callback + 1
lda #&lt;gangedport
ldx #&gt;gangedport
jsr udp_add_listener
main:
jsr ip65_process
jmp main
gotpacket:
sei
lda $01
pha
lda udp_inp
sta $01
lda udp_inp + 1
ldx udp_inp + 2
sta zp_data
stx zp_data + 2
ldy udp_inp + 3
copy:
lda udp_inp + 3,y
sta (zp_data),y
dey
bne copy
pla
sta $01
cli
rts
</pre>
<h2>License</h2>
This project is released under the Mozilla Public License Version 1.1.
For details, please visit <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a>.
<h2>Source Code</h2>
Browse online at <a href="http://github.com/cc65/ip65">http://github.com/cc65/ip65</a> or else download the whole tree through Git with the following instruction:
<pre>git clone https://github.com/cc65/ip65.git</pre>
<p>Published with <a href="http://pages.github.com">GitHub Pages</a></p>
</body>
</html>

179
docs/ip65_arithmetic_s.html Normal file
View File

@ -0,0 +1,179 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/arithmetic.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="add_16_32">add_16_32</td><td><pre>add a 16bit operand to the 32 bit accumulater
acc32=acc32+AX </pre></td></tr><tr><td id="add_32_32">add_32_32</td><td><pre>add a 32bit operand to the 32 bit accumulater
acc32=acc32+op32 </pre></td></tr><tr><td id="cmp_16_16">cmp_16_16</td><td><pre>compare 2 16bit numbers
on exit, zero flag clear iff acc16==AX </pre></td></tr><tr><td id="cmp_32_32">cmp_32_32</td><td><pre>compare 2 32bit numbers
on exit, zero flag clear iff acc32==op32 </pre></td></tr><tr><td id="mul_8_16">mul_8_16</td><td><pre>multiply a 16 bit number by an 8 bit number
acc16=acc16*a </pre></td></tr><tr><td id="sub_16_16">sub_16_16</td><td><pre>subtract 2 16 bit numbers
acc16=acc16-AX </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="op32">op32</td><td></td><td></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="acc16">acc16</td><td>acc16=acc16*a </td><td>acc16*a
</td></tr><tr><td id="acc32">acc32</td><td>acc32=acc32+AX </td><td>acc32+AX
</td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

465
docs/ip65_arp_s.html Normal file
View File

@ -0,0 +1,465 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/arp.s</h1><pre> ARP address resolution
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="arp_add">arp_add</td><td><pre> 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 </pre></td></tr><tr><td id="arp_calculate_gateway_mask">arp_calculate_gateway_mask</td><td></td></tr><tr><td id="arp_init">arp_init</td><td><pre>initialize arp (including clearing the arp cache)
inputs: none
outputs: none </pre></td></tr><tr><td id="arp_lookup">arp_lookup</td><td><pre>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. </pre></td></tr><tr><td id="arp_process">arp_process</td><td><pre>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 &amp; 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="arp_cache">arp_cache</td><td>cache of IP addresses and corresponding MAC addresses </td><td>(6+4)*ac_size</td></tr><tr><td id="arp_ip">arp_ip</td><td> set arp_ip before calling arp_lookup </td><td>4</td></tr><tr><td id="arp_mac">arp_mac</td><td> result is delivered here when arp_lookup returns with carry flag clear </td><td>6</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="ac_size">ac_size</td><td> lookup cache </td><td>8 </td></tr></table><h2>implementation</h2><pre id="code">; 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 ; set packet length
sta eth_outp_len
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 ; set timeout to now + ARP_TIMEOUT_MS
sta arptimeout
txa
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 ; set packet length
sta eth_outp_len
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 --
</pre></body></html>

940
docs/ip65_cifs_s.html Normal file
View File

@ -0,0 +1,940 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/cifs.s</h1><pre>a simple NETBIOS over TCP server
aka "Common Internet File System"
refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="cifs_l1_decode">cifs_l1_decode</td><td><pre>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) </pre></td></tr><tr><td id="cifs_l1_encode">cifs_l1_encode</td><td><pre>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 </pre></td></tr><tr><td id="cifs_start">cifs_start</td><td><pre>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 </pre></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

105
docs/ip65_config_s.html Normal file
View File

@ -0,0 +1,105 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/config.s</h1><pre>IP configuration defaults
most of these will be overwritten if dhcp is used for configuration
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="cfg_get_configuration_ptr">cfg_get_configuration_ptr</td><td><pre>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. </pre></td></tr><tr><td id="cfg_init">cfg_init</td><td><pre>copy the IP stack defaults (probably stored in ROM) to the running values in RAM
inputs: none
outputs: AX = pointer to IP configuration. </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="cfg_default_drive">cfg_default_drive</td><td></td><td>1</td></tr><tr><td id="cfg_dns">cfg_dns</td><td> ip address of dns server to use (will be overwritten if dhcp_init is called) </td><td>4;</td></tr><tr><td id="cfg_gateway">cfg_gateway</td><td>ip address of router on local network (will be overwritten if dhcp_init is called) </td><td>4</td></tr><tr><td id="cfg_ip">cfg_ip</td><td>ip address of local machine (will be overwritten if dhcp_init is called) </td><td>4</td></tr><tr><td id="cfg_mac">cfg_mac</td><td>mac address to be assigned to local machine </td><td>6</td></tr><tr><td id="cfg_netmask">cfg_netmask</td><td> netmask of local network (will be overwritten if dhcp_init is called) </td><td>4;</td></tr><tr><td id="cfg_tftp_server">cfg_tftp_server</td><td> ip address of server to send tftp requests to (can be a broadcast address) </td><td>4</td></tr><tr><td id="dhcp_server">dhcp_server</td><td>will be set address of dhcp server that configuration was obtained from </td><td>4</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="cfg_mac_default">cfg_mac_default</td><td>mac address to be assigned to local machine </td><td>$00, $80, $10, $00, $51, $00 </td></tr><tr><td id="cfg_size">cfg_size</td><td></td><td>cfg_end_defaults-cfg_mac_default+1
</td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

84
docs/ip65_copymem_s.html Normal file
View File

@ -0,0 +1,84 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/copymem.s</h1><pre> utility routine to copy memory
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="copymem">copymem</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="copy_dest">copy_dest</td><td> destination pointer </td><td>2</td></tr><tr><td id="copy_src">copy_src</td><td> source pointer </td><td>2</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

188
docs/ip65_debug_s.html Normal file
View File

@ -0,0 +1,188 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/debug.s</h1><pre>routines for dumping debug information
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="console_strout">console_strout</td><td><pre>print a string to the console
inputs: AX = address of (null terminated) string to print
outputs: none </pre></td></tr><tr><td id="dbg_dump_eth_header">dbg_dump_eth_header</td><td><pre>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 </pre></td></tr><tr><td id="dbg_dump_ip_header">dbg_dump_ip_header</td><td><pre>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 </pre></td></tr><tr><td id="dbg_dump_udp_header">dbg_dump_udp_header</td><td><pre>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 </pre></td></tr><tr><td id="dbgout16">dbgout16</td><td><pre>print a 32 bit number as 4 hex digits
inputs: AX = 32 bit number to print
outputs: none </pre></td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="console_out">console_out</td><td></td><td>$ffd2
</td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

530
docs/ip65_dhcp_s.html Normal file
View File

@ -0,0 +1,530 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/dhcp.s</h1><pre> 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
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="dhcp_init">dhcp_init</td><td><pre>
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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="dhcp_state">dhcp_state</td><td>flag indicating current state of dhcp initialization. </td><td>1</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

499
docs/ip65_dns_s.html Normal file
View File

@ -0,0 +1,499 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/dns.s</h1><pre> minimal dns implementation - requires a DNS server that supports recursion
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="dns_resolve">dns_resolve</td><td><pre> 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) </pre></td></tr><tr><td id="dns_set_hostname">dns_set_hostname</td><td><pre> 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="dns_ip">dns_ip</td><td>will be contain ip address of hostname after succesful exection of dns_resolve </td><td>4</td></tr><tr><td id="dns_status">dns_status</td><td> for debugging purposes only (behaviour not garuanteed) </td><td>2</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

113
docs/ip65_dottedquad_s.html Normal file
View File

@ -0,0 +1,113 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/dottedquad.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="parse_dotted_quad">parse_dotted_quad</td><td><pre> 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="dotted_quad_value">dotted_quad_value</td><td>set to 32 bit ip address on a succesful call to parse_dotted_quad </td><td>4</td></tr></table><h2>implementation</h2><pre id="code">
.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 --
</pre></body></html>

114
docs/ip65_eth_s.html Normal file
View File

@ -0,0 +1,114 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/eth.s</h1><pre> Common ethernet driver code (independant of host computer or ethernet chipset)
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="eth_set_broadcast_dest">eth_set_broadcast_dest</td><td><pre>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 </pre></td></tr><tr><td id="eth_set_my_mac_src">eth_set_my_mac_src</td><td><pre>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 </pre></td></tr><tr><td id="eth_set_proto">eth_set_proto</td><td><pre>set the 'protocol' field in the packet under construction
inputs:
A = protocol number (per 'eth_proto_*' constants)
outputs: none </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="eth_inp">eth_inp</td><td> space for input packet </td><td>1518</td></tr><tr><td id="eth_inp_len">eth_inp_len</td><td> input packet length </td><td>2</td></tr><tr><td id="eth_outp">eth_outp</td><td> space for output packet </td><td>1518</td></tr><tr><td id="eth_outp_len">eth_outp_len</td><td> output packet length </td><td>2</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="eth_data">eth_data</td><td> offset of packet data in ethernet packet </td><td>14 </td></tr><tr><td id="eth_dest">eth_dest</td><td> offset of destination address in ethernet packet </td><td>0 </td></tr><tr><td id="eth_proto_arp">eth_proto_arp</td><td></td><td>6
</td></tr><tr><td id="eth_proto_ip">eth_proto_ip</td><td> protocols </td><td>0
</td></tr><tr><td id="eth_src">eth_src</td><td> offset of source address in ethernet packet </td><td>6 </td></tr><tr><td id="eth_type">eth_type</td><td> offset of packet type in ethernet packet </td><td>12 </td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

View File

@ -0,0 +1,704 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/function_dispatcher.s</h1><pre>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.
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="kipper_dispatcher">kipper_dispatcher</td><td></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

280
docs/ip65_http_s.html Normal file
View File

@ -0,0 +1,280 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/http.s</h1><pre>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 &amp; http_get_value else odd things will happen.
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="http_get_value">http_get_value</td><td><pre>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. </pre></td></tr><tr><td id="http_parse_request">http_parse_request</td><td><pre>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' &amp; '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&amp;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 </pre></td></tr><tr><td id="http_variables_buffer">http_variables_buffer</td><td><pre>work area for storing variables extracted from query string </pre></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

636
docs/ip65_httpd_s.html Normal file
View File

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

500
docs/ip65_icmp_s.html Normal file
View File

@ -0,0 +1,500 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/icmp.s</h1><pre>ICMP implementation
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="icmp_add_listener">icmp_add_listener</td><td><pre>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 </pre></td></tr><tr><td id="icmp_init">icmp_init</td><td><pre> initialize icmp
inputs: none
outputs: none </pre></td></tr><tr><td id="icmp_ping">icmp_ping</td><td><pre>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 </pre></td></tr><tr><td id="icmp_process">icmp_process</td><td><pre>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) </pre></td></tr><tr><td id="icmp_remove_listener">icmp_remove_listener</td><td><pre>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 </pre></td></tr><tr><td id="icmp_send_echo">icmp_send_echo</td><td><pre> 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="icmp_callback">icmp_callback</td><td> argument for icmp_add_listener </td><td>2</td></tr><tr><td id="icmp_echo_ip">icmp_echo_ip</td><td> destination IP address for echo request ("ping") </td><td>4</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="icmp_cksum">icmp_cksum</td><td>offset of 'checksum' field in icmp packet </td><td>2 </td></tr><tr><td id="icmp_code">icmp_code</td><td>offset of 'code' field in icmp packet </td><td>1 </td></tr><tr><td id="icmp_data">icmp_data</td><td>offset of 'data' field in icmp packet </td><td>4</td></tr><tr><td id="icmp_inp">icmp_inp</td><td>pointer to inbound icmp packet </td><td>ip_inp + ip_data </td></tr><tr><td id="icmp_outp">icmp_outp</td><td>pointer to outbound icmp packet </td><td>ip_outp + ip_data </td></tr><tr><td id="icmp_type">icmp_type</td><td>offset of 'type' field in icmp packet </td><td>0 </td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

166
docs/ip65_ip65_s.html Normal file
View File

@ -0,0 +1,166 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/ip65.s</h1><pre> ip65 main routines
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="ip65_init">ip65_init</td><td><pre> initialise the IP stack
this calls the individual protocol &amp; 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 </pre></td></tr><tr><td id="ip65_process">ip65_process</td><td><pre>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. </pre></td></tr><tr><td id="ip65_random_word">ip65_random_word</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="ip65_ctr">ip65_ctr</td><td> incremented for every incoming packet </td><td>1</td></tr><tr><td id="ip65_ctr_arp">ip65_ctr_arp</td><td> incremented for every incoming arp packet </td><td>1</td></tr><tr><td id="ip65_ctr_ip">ip65_ctr_ip</td><td> incremented for every incoming ip packet </td><td>1</td></tr><tr><td id="ip65_error">ip65_error</td><td>last error code </td><td>1</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

533
docs/ip65_ip_s.html Normal file
View File

@ -0,0 +1,533 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/ip.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="ip_calc_cksum">ip_calc_cksum</td><td><pre> 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 </pre></td></tr><tr><td id="ip_create_packet">ip_create_packet</td><td><pre> 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 &amp; IP header
checksum fields set. </pre></td></tr><tr><td id="ip_init">ip_init</td><td><pre> initialize ip routines
inputs: none
outputs: none </pre></td></tr><tr><td id="ip_process">ip_process</td><td><pre>process an incoming packet &amp; 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) </pre></td></tr><tr><td id="ip_send">ip_send</td><td><pre> 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="ip_broadcast">ip_broadcast</td><td>flag set when an incoming IP packet was sent to a broadcast address </td><td>1</td></tr><tr><td id="ip_cksum_ptr">ip_cksum_ptr</td><td> pointer to data to be checksummed </td><td>2</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="ip_data">ip_data</td><td>offset of data payload in an IP packet </td><td>20 </td></tr><tr><td id="ip_dest">ip_dest</td><td>offset of "destination address" field in an IP packet header </td><td>16 </td></tr><tr><td id="ip_frag">ip_frag</td><td>offset of "fragmentation offset" field in an IP packet header </td><td>6 </td></tr><tr><td id="ip_header_cksum">ip_header_cksum</td><td>offset of "ip header checksum" field in an IP packet header </td><td>10 </td></tr><tr><td id="ip_id">ip_id</td><td>offset of "identification" field in an IP packet header </td><td>4 </td></tr><tr><td id="ip_inp">ip_inp</td><td>pointer to start of IP packet in input ethernet frame </td><td>eth_inp + eth_data </td></tr><tr><td id="ip_len">ip_len</td><td>offset of "length" field in an IP packet header </td><td>2 </td></tr><tr><td id="ip_outp">ip_outp</td><td>pointer to start of IP packet in output ethernet frame </td><td>eth_outp + eth_data </td></tr><tr><td id="ip_proto">ip_proto</td><td>offset of "protocol number" field in an IP packet header </td><td>9 </td></tr><tr><td id="ip_proto_icmp">ip_proto_icmp</td><td> ip protocols </td><td>1
</td></tr><tr><td id="ip_proto_tcp">ip_proto_tcp</td><td></td><td>6
</td></tr><tr><td id="ip_proto_udp">ip_proto_udp</td><td></td><td>17
</td></tr><tr><td id="ip_src">ip_src</td><td>offset of "source address" field in an IP packet header </td><td>12 </td></tr><tr><td id="ip_tos">ip_tos</td><td>offset of "type of service" field in an IP packet header </td><td>1 </td></tr><tr><td id="ip_ttl">ip_ttl</td><td>offset of "time to live" field in an IP packet header </td><td>8 </td></tr><tr><td id="ip_ver_ihl">ip_ver_ihl</td><td>offset of 4 bit "version" field and 4 bit "header length" field in an IP packet header </td><td>0 </td></tr></table><h2>implementation</h2><pre id="code">.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 ; borrow copymem zp...
lda ip_outp + ip_len
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 --
</pre></body></html>

View File

@ -0,0 +1,30 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/output_buffer.s</h1><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="output_buffer">output_buffer</td><td>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. </td><td>520</td></tr></table><h2>implementation</h2><pre id="code">.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 --
</pre></body></html>

114
docs/ip65_parser_s.html Normal file
View File

@ -0,0 +1,114 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/parser.s</h1><pre>text file parsing routines
first call parser_init
then call parser_skip_next
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="parser_init">parser_init</td><td><pre>set up a string for parsing
inputs: AX = pointer to (null terminated) string to be parsed
outputs: none </pre></td></tr><tr><td id="parser_skip_next">parser_skip_next</td><td><pre>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 </pre></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

441
docs/ip65_printf_s.html Normal file
View File

@ -0,0 +1,441 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/printf.s</h1><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="console_printf">console_printf</td><td><pre>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 </pre></td></tr></table><h2>implementation</h2><pre id="code">.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 "<unimplemented>",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 --
</pre></body></html>

219
docs/ip65_sntp_s.html Normal file
View File

@ -0,0 +1,219 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/sntp.s</h1><pre> Simple Network Time Protocol implementation - per RFC 2030
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="sntp_get_time">sntp_get_time</td><td><pre> 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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="sntp_utc_timestamp">sntp_utc_timestamp</td><td> will be set to seconds (only) part of utc timestamp (seconds since 00:00 on Jan 1, 1900) </td><td>4</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="sntp_ip">sntp_ip</td><td>can be set to ip address of server that will be queried via sntp (default is a local LAN broadcast) </td><td>$ff,$ff,$ff,$ff </td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

View File

@ -0,0 +1,118 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/string_utils.s</h1><pre>text file parsing routines
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="parse_hex_digits">parse_hex_digits</td><td></td></tr><tr><td id="parse_integer">parse_integer</td><td><pre>parses a string, returns integer (up to 16 bits)
inputs: AX points to a string containing an integer
outputs: AX contains integer </pre></td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

1140
docs/ip65_tcp_s.html Normal file

File diff suppressed because it is too large Load Diff

511
docs/ip65_telnet_s.html Normal file
View File

@ -0,0 +1,511 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/telnet.s</h1><pre>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
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="telnet_connect">telnet_connect</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="telnet_ip">telnet_ip</td><td>ip address of remote server </td><td>4</td></tr><tr><td id="telnet_port">telnet_port</td><td>port number to connect to </td><td>2</td></tr><tr><td id="telnet_use_native_charset">telnet_use_native_charset</td><td> 0 means all data is translated to/from NVT ASCII </td><td>1</td></tr></table><h2>implementation</h2><pre id="code">;minimal telnet implementation (dumb terminal emulation only)
;to use:
;set the following variables - telnet_use_native_charset,telnet_port,telnet_ip
;then call telnet_connect
;you must also define (and export) these function
; telnet_menu - called whenever the F1 key is pressed.
; telnet_on_connection - called after succesful connection
.include "../inc/common.i"
.import tcp_connect
.import tcp_callback
.import tcp_connect_ip
.import tcp_listen
.importzp KEYCODE_F1
.import tcp_inbound_data_ptr
.import tcp_inbound_data_length
.import tcp_send
.import tcp_send_data_len
.import tcp_close
.import print_a
.import print_cr
.import vt100_init_terminal
.import vt100_process_inbound_char
.import vt100_transform_outbound_char
.import tcp_send_keep_alive
.import timer_read
.import ip65_process
.import get_key_if_available
.import get_filtered_input
.import check_for_abort_key
.import ok_msg
.import failed_msg
.import print
.import print_errorcode
.import native_to_ascii
.import ascii_to_native
.export telnet_connect
.export telnet_use_native_charset
.export telnet_port
.export telnet_ip
.import telnet_menu
.import telnet_on_connection
.segment "IP65ZP" : zeropage
; pointer for moving through buffers
buffer_ptr: .res 2 ; source pointer
.code
;connect to a remote telnet server
;inputs:
;telnet_use_native_charset: set to 0 if remote server uses standard ASCII, 1 if remote server uses the 'native' charset (i.e. PETSCII)
;telnet_port: port number to connect to
;telnet_ip: ip address of remote server
telnet_connect:
lda telnet_use_native_charset
bne :+
jsr vt100_init_terminal
:
ldax #telnet_callback
stax tcp_callback
ldx #3
@copy_dest_ip:
lda telnet_ip,x
sta tcp_connect_ip,x
dex
bpl @copy_dest_ip
ldax telnet_port
jsr tcp_connect
bcc @connect_ok
jsr print_cr
ldax #failed_msg
jsr print
jsr print_cr
jsr print_errorcode
rts
@connect_ok:
jsr telnet_on_connection
ldax #ok_msg
jsr print
jsr print_cr
lda #0
sta connection_closed
sta iac_response_buffer_length
@main_polling_loop:
jsr check_for_abort_key
bcc @no_abort
jsr tcp_close
jmp @disconnected
@no_abort:
jsr timer_read
txa
adc #$20 ;32 x 1/4 = ~ 8seconds
sta telnet_timeout
@wait_for_keypress:
jsr timer_read
cpx telnet_timeout
bne @no_timeout
jsr tcp_send_keep_alive
jmp @main_polling_loop
@no_timeout:
jsr ip65_process
lda connection_closed
beq @not_disconnected
@disconnected:
ldax #disconnected
jsr print
rts
@not_disconnected:
lda iac_response_buffer_length
beq @no_iac_response
ldx #0
stax tcp_send_data_len
stx iac_response_buffer_length
ldax #iac_response_buffer
jsr tcp_send
@no_iac_response:
jsr get_key_if_available
beq @wait_for_keypress
cmp #KEYCODE_F1
bne @not_telnet_menu
jsr telnet_menu
jmp @main_polling_loop
@not_telnet_menu:
ldx #0
stx tcp_send_data_len
stx tcp_send_data_len+1
ldx telnet_use_native_charset
bne @no_conversion_required
jsr vt100_transform_outbound_char
sta temp_a
tya
bne :+
jmp @main_polling_loop ;Y=0 means nothing to send
:
cmp #2
beq :+
lda temp_a
jmp @no_conversion_required
:
lda temp_a
stax buffer_ptr
ldy #0
:
lda (buffer_ptr),y
beq @send_char
sta scratch_buffer,y
inc tcp_send_data_len
iny
bne :-
jmp @send_char
@no_conversion_required:
ldy tcp_send_data_len
sta scratch_buffer,y
inc tcp_send_data_len
@send_char:
ldax #scratch_buffer
jsr tcp_send
bcs @error_on_send
jmp @main_polling_loop
@error_on_send:
ldax #transmission_error
jsr print
jmp print_errorcode
;tcp callback - will be executed whenever data arrives on the TCP connection
telnet_callback:
lda tcp_inbound_data_length+1
cmp #$ff
bne @not_eof
lda #1
sta connection_closed
rts
@not_eof:
ldax tcp_inbound_data_ptr
stax buffer_ptr
lda tcp_inbound_data_length
sta buffer_length
lda tcp_inbound_data_length+1
sta buffer_length+1
@next_byte:
ldy #0
lda (buffer_ptr),y
tax
lda telnet_use_native_charset
beq :+
jmp @no_conversion_req
:
;if we get here, we are in ASCII 'char at a time' mode, so look for (and process) Telnet style IAC bytes
lda telnet_state
cmp #telnet_state_got_command
bne :+
jmp @waiting_for_option
:
cmp #telnet_state_got_iac
beq @waiting_for_command
cmp #telnet_state_got_suboption
beq @waiting_for_suboption_end
; we must be in 'normal' mode
txa
cmp #255
beq :+
jmp @not_iac
:
lda #telnet_state_got_iac
sta telnet_state
jmp @byte_processed
@waiting_for_suboption_end:
txa
ldx iac_suboption_buffer_length
sta iac_suboption_buffer,x
inc iac_suboption_buffer_length
cmp #$f0 ;SE - suboption end
bne @exit_suboption
lda #telnet_state_normal
sta telnet_state
lda iac_suboption_buffer
cmp #$18
bne @not_terminal_type
ldx #0
:
lda terminal_type_response,x
ldy iac_response_buffer_length
inc iac_response_buffer_length
sta iac_response_buffer,y
inx
txa
cmp #terminal_type_response_length
bne :-
@not_terminal_type:
@exit_suboption:
jmp @byte_processed
@waiting_for_command:
txa
sta telnet_command
cmp #$fa ; SB - suboption begin
beq @suboption
cmp #$fb ;WILL
beq @option
cmp #$fc ;WONT
beq @option
cmp #$fd ; DO
beq @option
cmp #$fe ;DONT
beq @option
;we got a command we don't understand - just ignore it
lda #telnet_state_normal
sta telnet_state
jmp @byte_processed
@suboption:
lda #telnet_state_got_suboption
sta telnet_state
lda #0
sta iac_suboption_buffer_length
jmp @byte_processed
@option:
lda #telnet_state_got_command
sta telnet_state
jmp @byte_processed
@waiting_for_option:
;we have now got IAC, <command>, <option>
txa
sta telnet_option
lda telnet_command
cmp #$fb
beq @iac_will
cmp #$fc
beq @iac_wont
cmp #$fe
beq @iac_dont
;if we get here, then it's a "do"
lda telnet_option
cmp #$18 ;terminal type
beq @do_terminaltype
cmp #$1f
beq @do_naws
;if we get here, then it's a "do" command we don't honour
@iac_dont:
lda #$fc ;wont
@add_iac_response:
ldx iac_response_buffer_length
sta iac_response_buffer+1,x
lda #255
sta iac_response_buffer,x
lda telnet_option
sta iac_response_buffer+2,x
inc iac_response_buffer_length
inc iac_response_buffer_length
inc iac_response_buffer_length
@after_set_iac_response:
lda #telnet_state_normal
sta telnet_state
jmp @byte_processed
@iac_will:
lda telnet_option
cmp #$01 ;ECHO
beq @will_echo
cmp #$03 ;DO SUPPRESS GA
beq @will_suppress_ga
@iac_wont:
lda #$fe ;dont
jmp @add_iac_response
@will_echo:
lda #$fd ;DO
jmp @add_iac_response
@will_suppress_ga:
lda #$fd ;DO
jmp @add_iac_response
@do_naws:
ldx #0
:
lda naws_response,x
ldy iac_response_buffer_length
inc iac_response_buffer_length
sta iac_response_buffer,y
inx
txa
cmp #naws_response_length
bne :-
jmp @after_set_iac_response
@do_terminaltype:
lda #$fb ;WILL
jmp @add_iac_response
@not_iac:
@convert_to_native:
txa
jsr vt100_process_inbound_char
jmp @byte_processed
@no_conversion_req:
txa
jsr print_a
@byte_processed:
inc buffer_ptr
bne :+
inc buffer_ptr+1
:
lda buffer_length+1
beq @last_page
lda buffer_length
bne @not_end_of_page
dec buffer_length+1
@not_end_of_page:
dec buffer_length
jmp @next_byte
@last_page:
dec buffer_length
beq @finished
jmp @next_byte
@finished:
rts
;constants
closing_connection: .byte "CLOSING CONNECTION",13,0
disconnected: .byte 13,"CONNECTION CLOSED",13,0
transmission_error: .byte "ERROR WHILE SENDING ",0
;initial_telnet_options:
; .byte $ff,$fb,$1F ;IAC WILL NAWS
; .byte $ff,$fb,$18 ;IAC WILL TERMINAL TYPE
;initial_telnet_options_length=*-initial_telnet_options
terminal_type_response:
.byte $ff ; IAC
.byte $fa; SB
.byte $18 ; TERMINAL TYPE
.byte $0 ; IS
.byte "vt100" ;what we pretend to be
.byte $ff ; IAC
.byte $f0 ; SE
terminal_type_response_length=*-terminal_type_response
naws_response:
.byte $ff,$fb,$1F ;IAC WILL NAWS
.byte $ff ; IAC
.byte $fa; SB
.byte $1F ; NAWS
.byte $00 ; width (high byte)
.byte 40 ; width (low byte)
.byte $00 ; height (high byte)
.byte 25 ; height (low byte)
.byte $ff ; IAC
.byte $f0 ; SE
naws_response_length=*-naws_response
;variables
.segment "APP_SCRATCH"
telnet_ip: .res 4 ;ip address of remote server
telnet_port: .res 2 ;port number to connect to
telnet_timeout: .res 1
connection_closed: .res 1
telnet_use_native_charset: .res 1 ; 0 means all data is translated to/from NVT ASCII
buffer_offset: .res 1
telnet_command: .res 1
telnet_option: .res 1
telnet_state_normal = 0
telnet_state_got_iac = 1
telnet_state_got_command = 2
telnet_state_got_suboption=3
buffer_length: .res 2
telnet_state: .res 1
temp_a: .res 1
iac_response_buffer: .res 64
iac_response_buffer_length: .res 1
scratch_buffer : .res 40
iac_suboption_buffer: .res 64
iac_suboption_buffer_length: .res 1
;-- LICENSE FOR telnet.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 --
</pre></body></html>

651
docs/ip65_tftp_s.html Normal file
View File

@ -0,0 +1,651 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/tftp.s</h1><pre>minimal tftp implementation (client only)
supports file upload and download
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="tftp_callback_vector">tftp_callback_vector</td><td></td></tr><tr><td id="tftp_clear_callbacks">tftp_clear_callbacks</td><td><pre>clear callback vectors, i.e. all future transfers read from/write to RAM
inputs: none
outputs: none </pre></td></tr><tr><td id="tftp_download">tftp_download</td><td><pre>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) </pre></td></tr><tr><td id="tftp_set_callback_vector">tftp_set_callback_vector</td><td><pre>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 </pre></td></tr><tr><td id="tftp_upload">tftp_upload</td><td><pre>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. </pre></td></tr><tr><td id="tftp_upload_from_memory">tftp_upload_from_memory</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="tftp_data_block_length">tftp_data_block_length</td><td></td><td>2</td></tr><tr><td id="tftp_filename">tftp_filename</td><td>name of file to d/l or filemask to get directory listing for </td><td>2</td></tr><tr><td id="tftp_filesize">tftp_filesize</td><td>will be set by tftp_download, needs to be set before calling tftp_upload_from_memory </td><td>2</td></tr><tr><td id="tftp_ip">tftp_ip</td><td>ip address of tftp server - set to 255.255.255.255 (broadcast) to send request to all tftp servers on local lan </td><td>4</td></tr><tr><td id="tftp_load_address">tftp_load_address</td><td>address file will be (or was) downloaded to </td><td>2</td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

70
docs/ip65_timer_s.html Normal file
View File

@ -0,0 +1,70 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/timer.s</h1><pre> 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/&lt;machinename&gt;timer.s
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="timer_timeout">timer_timeout</td><td><pre>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 </pre></td></tr></table><h2>implementation</h2><pre id="code">; 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
.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 --
</pre></body></html>

389
docs/ip65_udp_s.html Normal file
View File

@ -0,0 +1,389 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/udp.s</h1><pre>UDP (user datagram protocol) functions
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="udp_add_listener">udp_add_listener</td><td><pre>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 </pre></td></tr><tr><td id="udp_init">udp_init</td><td><pre> initialize udp
inputs: none
outputs: none </pre></td></tr><tr><td id="udp_process">udp_process</td><td><pre>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 </pre></td></tr><tr><td id="udp_remove_listener">udp_remove_listener</td><td><pre> 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 </pre></td></tr><tr><td id="udp_send">udp_send</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="udp_callback">udp_callback</td><td>vector to routine to be called when a udp packet arrives </td><td>2</td></tr><tr><td id="udp_send_dest">udp_send_dest</td><td>set to ip address that udp packet will be sent to </td><td>4</td></tr><tr><td id="udp_send_dest_port">udp_send_dest_port</td><td>set to port that udp packet will be sent to </td><td>2</td></tr><tr><td id="udp_send_len">udp_send_len</td><td>set to length of data to be sent in udp packet (excluding ethernet,ip & udp headers) </td><td>2</td></tr><tr><td id="udp_send_src_port">udp_send_src_port</td><td>set to port that udp packet will be sent from </td><td>2</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="udp_cksum">udp_cksum</td><td>offset of checksum field in udp packet </td><td>6 </td></tr><tr><td id="udp_data">udp_data</td><td>offset of data in udp packet </td><td>8 </td></tr><tr><td id="udp_dest_port">udp_dest_port</td><td>offset of destination port field in udp packet </td><td>2 </td></tr><tr><td id="udp_inp">udp_inp</td><td>pointer to udp packet inside inbound ethernet frame </td><td>ip_inp + ip_data </td></tr><tr><td id="udp_len">udp_len</td><td>offset of length field in udp packet </td><td>4 </td></tr><tr><td id="udp_outp">udp_outp</td><td>pointer to udp packet inside outbound ethernet frame </td><td>ip_outp + ip_data </td></tr><tr><td id="udp_src_port">udp_src_port</td><td>offset of source port field in udp packet </td><td>0 </td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

489
docs/ip65_url_s.html Normal file
View File

@ -0,0 +1,489 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/url.s</h1><pre>routines for parsing a URL, and downloading an URL
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="resource_download">resource_download</td><td><pre>download a resource specified by ip,port &amp; 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. </pre></td></tr><tr><td id="url_download">url_download</td><td><pre>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. </pre></td></tr><tr><td id="url_parse">url_parse</td><td><pre>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. &lt;$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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="url_download_buffer">url_download_buffer</td><td> points to a buffer that url will be downloaded into </td><td>2</td></tr><tr><td id="url_download_buffer_length">url_download_buffer_length</td><td>length of buffer that url will be downloaded into </td><td>2</td></tr><tr><td id="url_resource_type">url_resource_type</td><td></td><td>1</td></tr></table><h2 id="constants">constants</h2><table><tr><th>constants</th><th>description</th><th>value</th></tr><tr><td id="url_ip">url_ip</td><td> url_ip = ip address of host to connect to </td><td>ip address of host to connect to
</td></tr><tr><td id="url_port">url_port</td><td> url_port = port number of to connect to </td><td>port number of to connect to
</td></tr><tr><td id="url_selector">url_selector</td><td> url_selector= address of selector to send to host after connecting </td><td>address of selector to send to host after connecting
</td></tr></table><h2>implementation</h2><pre id="code">;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 --
</pre></body></html>

609
docs/ip65_xmodem_s.html Normal file
View File

@ -0,0 +1,609 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : ip65/xmodem.s</h1><pre> XMODEM file transfer
</pre><h2 id="functions">functions</h2><table><tr><th>function</th><th>description</th></tr><tr><td id="xmodem_receive">xmodem_receive</td><td><pre>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 </pre></td></tr><tr><td id="xmodem_send">xmodem_send</td><td><pre>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 </pre></td></tr></table><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="xmodem_iac_escape">xmodem_iac_escape</td><td>are IAC bytes ($FF) escaped? </td><td>1</td></tr></table><h2>implementation</h2><pre id="code">; 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 --
</pre></body></html>

18
docs/ref_frames.html Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>ip65 technical reference</title>
</head>
<frameset rows="20%, 80%" border=1>
<frameset cols="5,5,5" border=1>
<frame src="function_index.html" title="functions" name="functions" />
<frame src="variable_index.html" title="variables" name="variables"/>
<frame src="constant_index.html" title="constants" name="constants"/>
</frameset>
<frame name="docwin" src="ref_index.html" />
</frameset>

1
docs/ref_index.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,472 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : test/test_disk_io.s</h1><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="output_buffer">output_buffer</td><td></td><td>520</td></tr></table><h2>implementation</h2><pre id="code">
.ifndef KIPPER_API_VERSION_NUMBER
.define EQU =
.include "../inc/kipper_constants.i"
.endif
.include "../inc/common.i"
.include "../inc/commonprint.i"
.import print_a
.import cfg_get_configuration_ptr
.import io_device_no
.import io_sector_no
.import io_track_no
.import io_read_sector
.import io_write_sector
.import io_read_file_with_callback
.import io_read_file
.import io_filename
.import io_filesize
.import io_load_address
.import io_callback
.import get_key
.import ip65_error
.import io_read_catalogue_ex
.macro cout arg
lda arg
jsr print_a
.endmacro
.bss
sector_buffer: .res 256
output_buffer: .res 520
.export output_buffer
current_byte_in_row: .res 1
current_byte_in_sector: .res 1
start_of_current_row: .res 1
directory_buffer: .res 4096
.segment "STARTUP" ;this is what gets put at the start of the file on the C64
.word basicstub ; load address
basicstub:
.word @nextline
.word 2003
.byte $9e
.byte <(((init / 1000) .mod 10) + $30)
.byte <(((init / 100 ) .mod 10) + $30)
.byte <(((init / 10 ) .mod 10) + $30)
.byte <(((init ) .mod 10) + $30)
.byte 0
@nextline:
.word 0
init:
;switch to lower case charset
lda #23
sta $d018
;test we can read catalogue the hard way
ldax #loading
jsr print
ldax #dir_fname
stax io_filename
jsr print
jsr print_cr
lda #01
sta io_device_no
ldax #readfile_callback
stax io_callback
ldax #$3000
jsr io_read_file
bcc :+
jsr print_error_code
rts
:
;test we can write sector to default desk
ldx #$00
@fill_sector_loop:
txa
sta sector_buffer,x
inx
bne @fill_sector_loop
lda #$01
sta io_track_no
lda #$01
sta io_sector_no
ldax #write_sector
jsr print
lda io_sector_no
jsr print_hex
jsr print_cr
ldax #sector_buffer
jsr io_write_sector
bcc :+
jsr print_error_code
rts
:
inc io_sector_no
ldax #write_sector
jsr print
lda io_sector_no
jsr print_hex
jsr print_cr
ldax #sector_buffer
jsr io_write_sector
bcc :+
jsr print_error_code
rts
:
inc io_sector_no
ldax #write_sector
jsr print
lda io_sector_no
jsr print_hex
jsr print_cr
ldax #sector_buffer
jsr io_write_sector
bcc :+
jsr print_error_code
rts
:
;test we can read a sector from default desk
ldax #read_sector
jsr print
lda #$01
sta io_track_no
lda #$03
sta io_sector_no
ldax #sector_buffer
jsr io_read_sector
bcc :+
jsr print_error_code
rts
:
jsr dump_sector
;test we can read the catalogue
ldax #read_catalogue
jsr print
lda #01
sta io_device_no
ldax #directory_buffer
jsr io_read_catalogue_ex
bcc @no_error_on_catalogue
jsr print_error_code
rts
@no_error_on_catalogue:
ldax #directory_buffer
jsr print_catalogue
;test we can read without callbacks to fixed buffer
ldax #loading
jsr print
ldax #fname
stax io_filename
jsr print
jsr print_cr
lda #01
sta io_device_no
ldax #readfile_callback
stax io_callback
ldax #$3000
jsr io_read_file
bcc :+
jsr print_error_code
rts
:
ldax io_filesize
jsr print_integer
ldax #bytes_to
jsr print
lda io_load_address+1
jsr print_hex
lda io_load_address
jsr print_hex
jsr print_cr
;test we can read without callbacks to address in file
ldax #loading
jsr print
ldax #fname2
stax io_filename
jsr print
jsr print_cr
lda #01
sta io_device_no
ldax #readfile_callback
stax io_callback
ldax #$0000
jsr io_read_file
bcc :+
jsr print_error_code
rts
:
ldax io_filesize
jsr print_integer
ldax #bytes_to
jsr print
lda io_load_address+1
jsr print_hex
lda io_load_address
jsr print_hex
jsr print_cr
jsr wait_for_keypress
;test we can read via callbacks
ldax #loading
jsr print
ldax #fname
stax io_filename
jsr print
jsr print_cr
lda #01
sta io_device_no
ldax #readfile_callback
stax io_callback
ldax #sector_buffer
jsr io_read_file_with_callback
bcc :+
jsr print_error_code
:
rts
@error:
jsr print_cr
lda ip65_error
jsr print_hex
rts
;print catalogue pointed at by AX
print_catalogue:
stax tmp_buffer_ptr
@print_one_filename:
jsr read_byte_from_buffer
beq @catalogue_done
@print_one_char:
jsr print_a
jsr read_byte_from_buffer
beq @end_of_filename
jmp @print_one_char
@end_of_filename:
jsr print_cr
ldax #filetype
jsr print
jsr read_byte_from_buffer
jsr print_hex
ldax #sectors
jsr print
jsr read_byte_from_buffer
pha
jsr read_byte_from_buffer
jsr print_hex
pla
jsr print_hex
jsr print_cr
jmp @print_one_filename
@catalogue_done:
rts
read_byte_from_buffer:
tmp_buffer_ptr=read_byte_from_buffer+1
lda $ffff
inc tmp_buffer_ptr
bne :+
inc tmp_buffer_ptr+1
:
pha
pla ;reload A so flags are set correctly
rts
readfile_callback:
tya
jsr print_hex
ldax #bytes
jsr print
jsr dump_sector
rts
print_error_code:
jsr print_cr
ldax #error
jsr print
lda ip65_error
jsr print_hex
jsr print_cr
rts
wait_for_keypress:
lda #0
sta $c6 ;set the keyboard buffer to be empty
ldax #press_a_key_to_continue
jsr print
jsr get_key
rts
dump_sector:
;hex dump sector
lda #0
sta current_byte_in_sector
sta start_of_current_row
@one_row:
lda #$80
cmp current_byte_in_sector
bne @dont_wait_for_key
jsr wait_for_keypress
@dont_wait_for_key:
lda current_byte_in_sector
sta start_of_current_row
jsr print_hex
lda #':'
jsr print_a
lda #' '
jsr print_a
lda #0
sta current_byte_in_row
;first the hex values
@dump_byte:
ldy current_byte_in_sector
lda sector_buffer,y
jsr print_hex
lda #' '
jsr print_a
inc current_byte_in_sector
inc current_byte_in_row
lda current_byte_in_row
cmp #08
bne @dump_byte
;now the ascii values
lda start_of_current_row
sta current_byte_in_sector
@print_byte:
ldy current_byte_in_sector
lda sector_buffer,y
cmp #32
bmi @not_printable
cmp #94
bmi @printable
@not_printable:
lda #'.'
@printable:
jsr print_a
inc current_byte_in_sector
beq @last_byte
dec current_byte_in_row
bne @print_byte
jsr print_cr
jmp @one_row
@last_byte:
jsr print_cr
jsr wait_for_keypress
rts
write_sector:
.byte "WRITING SECTOR",0
read_sector:
.byte "READING SECTOR",13,0
dir_fname: .byte "$",0
read_catalogue:
.byte "READING CATALOGUE",13,0
fname:
.byte "TEST_DISK_IO.PRG",0
fname2:
.byte "SCREEN.PRG",0
loading: .byte "LOADING ",0
.rodata
filetype:
.byte "TYPE: $",0
sectors:
.byte " SECTORS: $",0
error:
.byte "ERROR - $", 0
failed:
.byte "FAILED ", 0
ok:
.byte "OK ", 0
bytes:
.byte " BYTES.", 0
bytes_to:
.byte " BYTES TO $", 0
;-- LICENSE FOR test_disk_io.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --
</pre></body></html>

182
docs/test_test_getc_s.html Normal file
View File

@ -0,0 +1,182 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><link rel="stylesheet" type="text/css" href="ca65-doc-style.css"/></head><body><a href="ref_index.html"><h1>ip65 technical reference</h1></a><h1>File : test/test_getc.s</h1><h2 id="variables">variables</h2><table><tr><th>variable</th><th>description</th><th>size (bytes)</th></tr><tr><td id="output_buffer">output_buffer</td><td></td><td></td></tr></table><h2>implementation</h2><pre id="code">
.ifndef KIPPER_API_VERSION_NUMBER
.define EQU =
.include "../inc/kipper_constants.i"
.endif
.include "../inc/common.i"
.include "../inc/commonprint.i"
.import print_a
.import cfg_get_configuration_ptr
.import io_device_no
.import io_sector_no
.import io_track_no
.import io_read_sector
.import io_write_sector
.import io_read_file_with_callback
.import io_read_file
.import io_filename
.import io_filesize
.import io_load_address
.import io_callback
.import get_key
.import ip65_error
.import ip65_process
.import io_read_catalogue_ex
.macro cout arg
lda arg
jsr print_a
.endmacro
.bss
sector_buffer: .res 256
output_buffer: .res 520
.export output_buffer
current_byte_in_row: .res 1
current_byte_in_sector: .res 1
start_of_current_row: .res 1
directory_buffer: .res 4096
.segment "STARTUP" ;this is what gets put at the start of the file on the C64
.word basicstub ; load address
basicstub:
.word @nextline
.word 2003
.byte $9e
.byte <(((init / 1000) .mod 10) + $30)
.byte <(((init / 100 ) .mod 10) + $30)
.byte <(((init / 10 ) .mod 10) + $30)
.byte <(((init ) .mod 10) + $30)
.byte 0
@nextline:
.word 0
init:
lda #14
jsr print_a ;switch to lower case
lda $dc08 ;read deci-seconds - start clock ticking
sta $dc08
jsr load_buffer
@loop:
lda #5 ;timeout period
jsr getc
bcs @done
jsr print_a
jmp @loop
@done:
rts
load_buffer:
ldax #buffer
stax next_char_ptr
ldax #buffer_length
stax buff_length
rts
getc:
sta getc_timeout_seconds
clc
lda $dc09 ;time of day clock: seconds (in BCD)
sed
adc getc_timeout_seconds
cmp #$60
bcc @timeout_set
sec
sbc #$60
@timeout_set:
cld
sta getc_timeout_end
@poll_loop:
jsr ip65_process
jsr next_char
bcs @no_char
rts ;done!
@no_char:
lda $dc09 ;time of day clock: seconds
cmp getc_timeout_end
bne @poll_loop
sec
rts
next_char:
lda buff_length
bne @not_eof
lda buff_length+1
bne @not_eof
sec
rts
@not_eof:
next_char_ptr=*+1
lda $ffff
pha
inc next_char_ptr
bne :+
inc next_char_ptr+1
:
sec
lda buff_length
sbc #1
sta buff_length
lda buff_length+1
sbc #0
sta buff_length+1
pla
clc
rts
.rodata
buffer:
.byte "this is a test1!",13
.byte "this is a test2!",13
.byte "this is a test3!",13
.byte "this is a test4!",13
.byte "this is a test5!",13
.byte "this is a test6!",13
.byte "this is a test7!",13
.byte "this is a test8!",13
.byte "this is a test9!",13
.byte "this is a test10!",13
.byte "this is a test1@",13
.byte "this is a test2@",13
.byte "this is a test3@",13
.byte "this is a test4@",13
.byte "this is a test5@",13
.byte "this is a test6@",13
.byte "this is a test7@",13
.byte "this is a test8@",13
.byte "this is a test9@",13
.byte "this is a test10@",13
.byte "this is a test1*",13
.byte "this is a test2*",13
.byte "this is a test3*",13
.byte "this is a test4*",13
.byte "this is a test5*",13
.byte "this is a test6*",13
.byte "this is a test7*",13
.byte "this is a test8*",13
.byte "this is a test9*",13
.byte "this is a test10*",13
buffer_length=*-buffer
.bss
getc_timeout_end: .res 1
getc_timeout_seconds: .res 1
buff_length: .res 2
;-- LICENSE FOR test_getc.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; complianc

1
docs/variable_index.html Normal file

File diff suppressed because one or more lines are too long