From c9af38790a52f090eab041860f3787affa599b54 Mon Sep 17 00:00:00 2001
From: Oliver Schmidt
Date: Sat, 1 Aug 2015 18:57:58 +0200
Subject: [PATCH] Added context to W5100 UDP driver.
In order to ease exploring the W5100 UDP driver a simple test program using it was added. Additionally a communication peer program was introduced. The latter builds as-is for Windows using i.e. the VC++ command line 'cl w5100_peer.c'.
---
supplement/w5100_udp.s | 114 +++++++++++++++++++++++++++++++----------
test/Makefile | 12 ++++-
test/w5100_peer.c | 112 ++++++++++++++++++++++++++++++++++++++++
test/w5100_udp_c.c | 96 ++++++++++++++++++++++++++++++++++
test/w5100_udp_s.s | 41 +++++++++++++++
5 files changed, 346 insertions(+), 29 deletions(-)
create mode 100644 test/w5100_peer.c
create mode 100644 test/w5100_udp_c.c
create mode 100644 test/w5100_udp_s.s
diff --git a/supplement/w5100_udp.s b/supplement/w5100_udp.s
index e7917de..fea59ec 100644
--- a/supplement/w5100_udp.s
+++ b/supplement/w5100_udp.s
@@ -29,24 +29,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
-ptr := $06 ; 2 byte pointer value
-tmp := $08 ; 1 byte temporary value
-bas := $09 ; 1 byte socket 1 Base Address (hibyte)
-sha := $19 ; 2 byte physical addr shadow ($F000-$FFFF)
-len := $1B ; 2 byte frame length
-adv := $1D ; 2 byte pointer register advancement
-
-mode := $C0C4
-addr := $C0C5
-data := $C0C7
-
.export init
.export recv_init, recv_byte, recv_done
.export send_init, send_byte, send_done
+ptr := $06 ; 2 byte pointer value
+sha := $08 ; 2 byte physical addr shadow ($F000-$FFFF)
+adv := $EB ; 2 byte pointer register advancement
+len := $ED ; 2 byte frame length
+tmp := $FA ; 1 byte temporary value
+bas := $FB ; 1 byte socket 1 Base Address (hibyte)
+
+mode := $C0B4
+addr := $C0B5
+data := $C0B7
+
;------------------------------------------------------------------------------
init:
+; Input
+; AX: Address of ip_parms (serverip, cfg_ip, cfg_netmask, cfg_gateway)
+; Output
+; None
+; Remark
+; The ip_parms are only accessed during this function.
+
; Set ip_parms pointer
sta ptr
stx ptr+1
@@ -99,6 +106,11 @@ init:
; -> A is still $0A
sta data
+ ; Socket 1 Source Port Register: 6502
+ ldy #$04
+ jsr set_addrsocket1
+ jsr set_data6502
+
; Socket 1 Destination IP Address Register: Destination IP address
; This has to be the last call to set_ipv4value because it writes
; as a side effect to 'hdr' and it is the destination IP address
@@ -112,11 +124,6 @@ init:
; -> addr is already set
jsr set_data6502
- ; Socket 1 Source Port Register: 6502
- ldy #$04
- jsr set_addrsocket1
- jsr set_data6502
-
; Socket 1 Mode Register: UDP
ldy #$00
jsr set_addrsocket1
@@ -124,7 +131,7 @@ init:
sta data
; Socket 1 Command Register: OPEN
- ; addr is already set
+ ; -> addr is already set
lda #$01
sta data
rts
@@ -153,12 +160,20 @@ set_data6502:
;------------------------------------------------------------------------------
recv_init:
+; Input
+; None
+; Output
+; C: Clear if ready to receive
+; AX: If C is clear then number of bytes to receive
+; Remark
+; To be called before recv_byte.
+
; Socket 1 RX Received Size Register: 0 or volatile ?
lda #$26 ; Socket RX Received Size Register
jsr prolog
bcs :+++
- ; Socket 0 RX Read Pointer Register
+ ; Socket 1 RX Read Pointer Register
; -> addr already set
; Calculate and set pyhsical address
@@ -207,6 +222,13 @@ recv_init:
;------------------------------------------------------------------------------
send_init:
+; Input
+; AX: Number of bytes to send
+; Output
+; C: Clear if ready to send
+; Remark
+; To be called before send_byte.
+
; Set pointer advancement
sta adv
stx adv+1
@@ -219,7 +241,7 @@ send_init:
; Socket 1 TX Free Size Register: < advancement ?
cpx adv ; Lobyte
sbc adv+1 ; Hibyte
- bcc rts_cs
+ bcc sec_rts ; Not enough free size -> error
; Socket 1 TX Write Pointer Register
ldy #$24
@@ -240,7 +262,7 @@ prolog:
; Socket 1 Command Register: 0 ?
jsr set_addrcmdreg1
ldx data
- bne rts_cs ; Not completed -> error
+ bne sec_rts ; Not completed -> error
; Socket Size Register: not 0 ?
tay ; Select Size Register
@@ -249,21 +271,30 @@ prolog:
sta ptr+1 ; Hibyte
ora ptr
bne :+
-rts_cs: sec ; Error (size == 0)
+
+sec_rts:
+ sec ; Error (size == 0)
rts
; Socket Size Register: volatile ?
: jsr get_wordsocket1
cpx ptr ; Lobyte
- bne rts_cs ; Volatile size -> error
+ bne sec_rts ; Volatile size -> error
cmp ptr+1 ; Hibyte
- bne rts_cs ; Volatile size -> error
- clc ; Sucess (size != 0)
+ bne sec_rts ; Volatile size -> error
+ clc ; Success (size != 0)
rts
;------------------------------------------------------------------------------
recv_byte:
+; Input
+; None
+; Output
+; A: Byte received
+; Remark
+; May be called as often as indicated by recv_init.
+
; Read byte
lda data
@@ -275,6 +306,13 @@ recv_byte:
;------------------------------------------------------------------------------
send_byte:
+; Input
+; A: Byte to send
+; Output
+; None
+; Remark
+; Should be called as often as indicated to send_init.
+
; Write byte
sta data
@@ -283,14 +321,24 @@ send_byte:
beq incsha
rts
+incsha:
; Increment physical addr shadow hibyte
-incsha: inc sha+1
+ inc sha+1
beq set_addrbase
rts
;------------------------------------------------------------------------------
recv_done:
+; Input
+; None
+; Output
+; None
+; Remark
+; Mark data indicated by recv_init as processed (independently from how
+; often recv_byte was called), if not called then next call of recv_init
+; will just indicate the very same data again.
+
; Set parameters for commit code
lda #$40 ; RECV
ldy #$28 ; Socket RX Read Pointer Register
@@ -299,12 +347,22 @@ recv_done:
;------------------------------------------------------------------------------
send_done:
+; Input
+; None
+; Output
+; None
+; Remark
+; Actually send data indicated to send_init (independently from how often
+; send_byte was called), if not called then send_init (and send_byte) are
+; just NOPs.
+
; Set parameters for commit code
lda #$20 ; SEND
ldy #$24 ; Socket TX Write Pointer Register
+epilog:
; Advance pointer register
-epilog: jsr set_addrsocket1
+ jsr set_addrsocket1
tay ; Save command
clc
lda ptr
@@ -319,7 +377,7 @@ epilog: jsr set_addrsocket1
tya ; Restore command
jsr set_addrcmdreg1
sta data
- sec ; When coming from _recv_init -> error
+ sec ; When coming from recv_init -> error
rts
;------------------------------------------------------------------------------
@@ -336,6 +394,7 @@ set_addrphysical:
ora #>$F000 ; Move sha/sha+1 to $F000-$FFFF
sty sha
sta sha+1
+
set_addr:
stx addr ; Hibyte
sty addr+1 ; Lobyte
@@ -345,6 +404,7 @@ set_addr:
set_addrcmdreg1:
ldy #$01 ; Socket Command Register
+
set_addrsocket1:
ldx #>$0500 ; Socket 1 register base address
bne set_addr ; Always
diff --git a/test/Makefile b/test/Makefile
index 96f59a0..584cd3e 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -118,8 +118,16 @@ ip65demo.dsk: httpd.bin httpd-slotscan.bin
java -jar $(AC) -cc65 $@ webserver-slot3 B < httpd.bin
java -jar $(AC) -cc65 $@ webserver-slotscan B < httpd-slotscan.bin
+w5100_%.bin: ../supplement/w5100_%.s w5100_%_s.s w5100_%_c.c
+ cl65 -o $@ -t apple2enh -m $(basename $@).map $^
+
+w5100.dsk: w5100_udp.bin
+ cp prodos.dsk $@
+ java -jar $(AC) -cc65 $@ udp bin < w5100_udp.bin
+
clean:
make -C ../ip65 clean
make -C ../drivers clean
- -rm -f *.prg *.bin *.vicprg *.map
- -rm -f ip65.d64 ip65.dsk ip65demo.dsk
+ -rm -f ../supplement/*.o
+ -rm -f *.o *.prg *.bin *.vicprg *.map
+ -rm -f ip65.d64 ip65.dsk ip65demo.dsk w5100.dsk
diff --git a/test/w5100_peer.c b/test/w5100_peer.c
new file mode 100644
index 0000000..6e9f3e5
--- /dev/null
+++ b/test/w5100_peer.c
@@ -0,0 +1,112 @@
+#include
+#include
+
+#define WIN32_LEAN_AND_MEAN
+#include
+#pragma comment(lib, "ws2_32.lib")
+
+void main(void)
+{
+ printf("Init\n");
+ WSADATA wsa;
+ if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
+ {
+ return;
+ }
+
+ SOCKET s = socket(AF_INET, SOCK_DGRAM , IPPROTO_UDP);
+ if (s == INVALID_SOCKET)
+ {
+ return;
+ }
+
+ u_long arg = 1;
+ if (ioctlsocket(s, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ return;
+ }
+
+ SOCKADDR_IN local;
+ local.sin_family = AF_INET;
+ local.sin_addr.s_addr = INADDR_ANY;
+ local.sin_port = htons(6502);
+ if (bind(s, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)
+ {
+ return;
+ }
+
+ SOCKADDR_IN remote;
+ remote.sin_addr.s_addr = INADDR_NONE;
+
+ printf("(S)end or e(X)it\n");
+ char key;
+ do
+ {
+ int len;
+ unsigned char buf[1500];
+
+ if (kbhit())
+ {
+ key = getch();
+ }
+ else
+ {
+ key = '\0';
+ }
+
+ if (key == 's')
+ {
+ if (remote.sin_addr.s_addr == INADDR_NONE)
+ {
+ printf("Peer Addr Unknown As Yet\n", len);
+ }
+ else
+ {
+ unsigned i;
+
+ len = 512;
+ for (i = 0; i < len; ++i)
+ {
+ buf[i] = i;
+ }
+ printf("Send Len $%04X To %s", len, inet_ntoa(remote.sin_addr));
+ if (sendto(s, buf, len, 0, (SOCKADDR *)&remote, sizeof(remote)) == SOCKET_ERROR)
+ {
+ return;
+ }
+ printf(".\n");
+ }
+ }
+
+ unsigned remote_size = sizeof(remote);
+ len = recvfrom(s, buf, sizeof(buf), 0, (SOCKADDR *)&remote, &remote_size);
+ if (len == SOCKET_ERROR)
+ {
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ return;
+ }
+ len = 0;
+ }
+ if (len)
+ {
+ unsigned i;
+
+ printf("Recv Len $%04X From %s", len, inet_ntoa(remote.sin_addr));
+ for (i = 0; i < len; ++i)
+ {
+ if ((i % 24) == 0)
+ {
+ printf("\n$%04X:", i);
+ }
+ printf(" %02X", buf[i]);
+ }
+ printf(".\n");
+ }
+ }
+ while (key != 'x');
+
+ closesocket(s);
+ WSACleanup();
+ printf("Done\n");
+}
diff --git a/test/w5100_udp_c.c b/test/w5100_udp_c.c
new file mode 100644
index 0000000..c7cfec7
--- /dev/null
+++ b/test/w5100_udp_c.c
@@ -0,0 +1,96 @@
+#include
+#include
+
+void __fastcall__ init(void *parms);
+
+unsigned recv_init(void);
+unsigned char recv_byte(void);
+void recv_done(void);
+
+unsigned __fastcall__ send_init(unsigned len);
+void __fastcall__ send_byte(unsigned char val);
+void send_done(void);
+
+struct
+{
+ unsigned char serverip [4];
+ unsigned char cfg_ip [4];
+ unsigned char cfg_netmask[4];
+ unsigned char cfg_gateway[4];
+}
+parms =
+{
+ {192, 168, 0, 2},
+ {192, 168, 0, 123},
+ {255, 255, 255, 0},
+ {192, 168, 0, 1}
+};
+
+void main(void)
+{
+ char key;
+
+ videomode(VIDEOMODE_80COL);
+ printf("Init\n");
+ init(&parms);
+
+ printf("(S)end or e(X)it\n");
+ do
+ {
+ unsigned len;
+
+ if (kbhit())
+ {
+ key = cgetc();
+ }
+ else
+ {
+ key = '\0';
+ }
+
+ if (key == 's')
+ {
+ unsigned i;
+
+ len = 512;
+ printf("Send Len $%04X To %d.%d.%d.%d", len, parms.serverip[0],
+ parms.serverip[1],
+ parms.serverip[2],
+ parms.serverip[3]);
+ while (!send_init(len))
+ {
+ printf("!");
+ }
+ for (i = 0; i < len; ++i)
+ {
+ send_byte(i);
+ }
+ send_done();
+ printf(".\n");
+ }
+
+ len = recv_init();
+ if (len)
+ {
+ unsigned i;
+
+ printf("Recv Len $%04X From %d.%d.%d.%d", len, parms.serverip[0],
+ parms.serverip[1],
+ parms.serverip[2],
+ parms.serverip[3]);
+ for (i = 0; i < len; ++i)
+ {
+ if ((i % 24) == 0)
+ {
+ printf("\n$%04X:", i);
+ }
+ printf(" %02X", recv_byte());
+ }
+ recv_done();
+ printf(".\n");
+ }
+ }
+ while (key != 'x');
+
+ printf("Done\n");
+}
diff --git a/test/w5100_udp_s.s b/test/w5100_udp_s.s
new file mode 100644
index 0000000..9753f6c
--- /dev/null
+++ b/test/w5100_udp_s.s
@@ -0,0 +1,41 @@
+.export _init
+.export _recv_init, _recv_byte, _recv_done
+.export _send_init, _send_byte, _send_done
+
+.import init
+.import recv_init, recv_byte, recv_done
+.import send_init, send_byte, send_done
+
+_init:
+ jmp init
+
+_recv_init:
+ jsr recv_init
+ bcc :+
+ lda #<$0000
+ ldx #>$0000
+: rts
+
+_recv_byte:
+ jsr recv_byte
+ ldx #>$0000
+ rts
+
+_recv_done:
+ jmp recv_done
+
+_send_init:
+ jsr send_init
+ bcc :+
+ lda #<$0000
+ ldx #>$0000
+ rts
+: lda #<$0001
+ ldx #>$0001
+ rts
+
+_send_byte:
+ jmp send_byte
+
+_send_done:
+ jmp send_done