Migrated GitHub pages from gh-pages branch to /docs folder.
This commit is contained in:
parent
e6a7ce4f45
commit
9c2e55ce3b
|
@ -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; }
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -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 #<gotpacket
|
||||
ldx #>gotpacket
|
||||
sta udp_callback
|
||||
stx udp_callback + 1
|
||||
lda #<gangedport
|
||||
ldx #>gangedport
|
||||
jsr udp_add_listener
|
||||
|
||||
main:
|
||||
jsr ip65_process
|
||||
jmp main
|
||||
|
||||
gotpacket:
|
||||
sei
|
||||
lda $01
|
||||
pha
|
||||
lda udp_inp
|
||||
sta $01
|
||||
|
||||
lda udp_inp + 1
|
||||
ldx udp_inp + 2
|
||||
sta zp_data
|
||||
stx zp_data + 2
|
||||
ldy udp_inp + 3
|
||||
copy:
|
||||
lda udp_inp + 3,y
|
||||
sta (zp_data),y
|
||||
dey
|
||||
bne copy
|
||||
|
||||
pla
|
||||
sta $01
|
||||
cli
|
||||
rts
|
||||
</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>
|
|
@ -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>
|
|
@ -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 & 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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 & 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' & '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
</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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 & 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>
|
|
@ -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 & 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 & 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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
|
@ -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>
|
|
@ -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>
|
|
@ -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/<machinename>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>
|
|
@ -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>
|
|
@ -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 & 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. <$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>
|
|
@ -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>
|
|
@ -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>
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -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
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue