├── .gitignore ├── README.md ├── demo ├── Makefile ├── peer.c ├── stream.c ├── stream.h ├── stream_main.c ├── tcp.s ├── tcp_main.c ├── udp.s └── udp_main.c └── test ├── Makefile ├── README.txt ├── stream3.c ├── stream3.h ├── test.c └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.lib 3 | *.map 4 | *.bin 5 | *.sys 6 | *.dsk 7 | *.po 8 | 9 | *.obj 10 | *.exe 11 | 12 | demo/peer 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uthernet II 2 | 3 | * The a2RetroSystems [Uthernet II](http://a2retrosystems.com/products.htm) product page 4 | * The Uthernet II [User’s and Programmer’s Manual](http://dserver.macgui.com/Uthernet%20II%20manual%20draft.pdf) 5 | * An Uthernet II [Applications Overview](http://www.a2retrosystems.com/downloads.htm) 6 | * Some Uthernet II [Programming Infos](https://github.com/a2retrosystems/uthernet2/wiki) 7 | -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | all: demo.dsk 2 | 3 | %.bin: %.s %_main.c 4 | cl65 -o $@ -t apple2enh -m $(basename $@).map $^ 5 | 6 | stream.bin: stream.c stream_main.c 7 | cl65 -o $@ -t apple2enh -m $(basename $@).map $^ 8 | 9 | stream.irq.bin: stream.c stream_main.c 10 | cl65 -o $@ -t apple2enh -m $(basename $@).map -D SOCKET_IRQ $^ 11 | 12 | demo.dsk: udp.bin tcp.bin stream.bin stream.irq.bin 13 | cp prodos.dsk $@ 14 | java -jar $(AC) -as $@ udp bin < udp.bin 15 | java -jar $(AC) -as $@ tcp bin < tcp.bin 16 | java -jar $(AC) -as $@ stream bin < stream.bin 17 | java -jar $(AC) -as $@ stream.irq bin < stream.irq.bin 18 | 19 | %.exe: %.c 20 | cl /Fe:$@ $^ 21 | 22 | peer: peer.c 23 | gcc -o $@ $^ 24 | 25 | clean: 26 | -rm -f *.o *.map *.bin demo.dsk *.obj *.exe peer 27 | -------------------------------------------------------------------------------- /demo/peer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef _MSC_VER 5 | 6 | #include 7 | 8 | #define WIN32_LEAN_AND_MEAN 9 | #include 10 | #pragma comment(lib, "ws2_32.lib") 11 | 12 | #define SOCKET_ERRNO WSAGetLastError() 13 | #define SOCKET_WOULDBLOCK WSAEWOULDBLOCK 14 | 15 | static int set_non_blocking_flag(SOCKET s) 16 | { 17 | u_long arg = 1; 18 | return ioctlsocket(s, FIONBIO, &arg); 19 | } 20 | 21 | static int init() 22 | { 23 | WSADATA wsa; 24 | return WSAStartup(MAKEWORD(2, 2), &wsa) == 0; 25 | } 26 | 27 | #else // _MSC_VER 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define INVALID_SOCKET (-1) 37 | #define SOCKET_ERROR (-1) 38 | 39 | #define SOCKET int 40 | #define SOCKADDR_IN struct sockaddr_in 41 | #define SOCKADDR struct sockaddr 42 | 43 | #define SOCKET_ERRNO errno 44 | #define SOCKET_WOULDBLOCK EWOULDBLOCK 45 | 46 | #define closesocket close 47 | #define Sleep(x) usleep(1000 * x) 48 | #define kbhit() 1 49 | 50 | static int set_non_blocking_flag(SOCKET s) 51 | { 52 | return fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK); 53 | } 54 | 55 | static int init() 56 | { 57 | return set_non_blocking_flag(STDIN_FILENO) != SOCKET_ERROR; 58 | } 59 | 60 | // one must press ENTER for it to work 61 | // and all characters will be returned (inclusing ENTER) 62 | static char getch() 63 | { 64 | int c = getchar(); 65 | // it return EOF = -1 on error, or a unsigned char 66 | return c >= 0 ? c : 0; 67 | } 68 | 69 | #endif // _MSC_VER 70 | 71 | static void dump(unsigned char *buf, unsigned len) 72 | { 73 | unsigned i; 74 | 75 | for (i = 0; i < len; ++i) 76 | { 77 | if ((i % 24) == 0) 78 | { 79 | printf("\n$%04X:", i); 80 | } 81 | printf(" %02X", buf[i]); 82 | } 83 | printf(".\n"); 84 | } 85 | 86 | void main(void) 87 | { 88 | printf("Init\n"); 89 | 90 | if (!init()) 91 | { 92 | return; 93 | } 94 | 95 | SOCKET udp = socket(AF_INET, SOCK_DGRAM , IPPROTO_UDP); 96 | if (udp == INVALID_SOCKET) 97 | { 98 | return; 99 | } 100 | SOCKET srv = socket(AF_INET, SOCK_STREAM , IPPROTO_TCP); 101 | if (srv == INVALID_SOCKET) 102 | { 103 | return; 104 | } 105 | 106 | if (set_non_blocking_flag(udp) == SOCKET_ERROR) 107 | { 108 | return; 109 | } 110 | if (set_non_blocking_flag(srv) == SOCKET_ERROR) 111 | { 112 | return; 113 | } 114 | 115 | SOCKADDR_IN local; 116 | local.sin_family = AF_INET; 117 | local.sin_addr.s_addr = INADDR_ANY; 118 | local.sin_port = htons(6502); 119 | if (bind(udp, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR) 120 | { 121 | return; 122 | } 123 | if (bind(srv, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR) 124 | { 125 | return; 126 | } 127 | 128 | if (listen(srv, 1) == SOCKET_ERROR) 129 | { 130 | return; 131 | } 132 | 133 | SOCKADDR_IN remote; 134 | remote.sin_addr.s_addr = INADDR_NONE; 135 | 136 | SOCKET tcp = INVALID_SOCKET; 137 | 138 | printf("(U)DP, (T)CP or e(X)it\n"); 139 | char key; 140 | do 141 | { 142 | int len; 143 | unsigned char buf[1500]; 144 | 145 | if (kbhit()) 146 | { 147 | key = tolower(getch()); 148 | } 149 | else 150 | { 151 | key = '\0'; 152 | } 153 | 154 | if (key == 'u') 155 | { 156 | if (remote.sin_addr.s_addr == INADDR_NONE) 157 | { 158 | printf("Peer Unknown As Yet\n"); 159 | } 160 | else 161 | { 162 | unsigned i; 163 | 164 | len = 500; 165 | for (i = 0; i < len; ++i) 166 | { 167 | buf[i] = i; 168 | } 169 | printf("Send Len %d To %s", len, inet_ntoa(remote.sin_addr)); 170 | if (sendto(udp, buf, len, 0, (SOCKADDR *)&remote, sizeof(remote)) == SOCKET_ERROR) 171 | { 172 | return; 173 | } 174 | printf(".\n"); 175 | } 176 | } 177 | 178 | if (key == 't') 179 | { 180 | if (tcp == INVALID_SOCKET) 181 | { 182 | printf("No Connection\n"); 183 | } 184 | else 185 | { 186 | unsigned i; 187 | 188 | len = 500; 189 | for (i = 0; i < len; ++i) 190 | { 191 | buf[i] = i; 192 | } 193 | printf("Send Len %d", len); 194 | if (send(tcp, buf, len, 0) == SOCKET_ERROR) 195 | { 196 | return; 197 | } 198 | printf(".\n"); 199 | } 200 | } 201 | 202 | unsigned remote_size = sizeof(remote); 203 | len = recvfrom(udp, buf, sizeof(buf), 0, (SOCKADDR *)&remote, &remote_size); 204 | if (len == SOCKET_ERROR) 205 | { 206 | if (SOCKET_ERRNO != SOCKET_WOULDBLOCK) 207 | { 208 | return; 209 | } 210 | } 211 | else if (len) 212 | { 213 | printf("Recv Len %d From %s", len, inet_ntoa(remote.sin_addr)); 214 | dump(buf, len); 215 | } 216 | 217 | if (tcp == INVALID_SOCKET) 218 | { 219 | SOCKADDR_IN conn; 220 | unsigned conn_size = sizeof(conn); 221 | tcp = accept(srv, (SOCKADDR *)&conn, &conn_size); 222 | if (tcp == INVALID_SOCKET) 223 | { 224 | if (SOCKET_ERRNO != SOCKET_WOULDBLOCK) 225 | { 226 | return; 227 | } 228 | } 229 | else 230 | { 231 | printf("Connect From %s\n", inet_ntoa(conn.sin_addr)); 232 | 233 | if (set_non_blocking_flag(tcp) == SOCKET_ERROR) 234 | { 235 | return; 236 | } 237 | } 238 | } 239 | else 240 | { 241 | len = recv(tcp, buf, sizeof(buf), 0); 242 | if (len == SOCKET_ERROR) 243 | { 244 | if (SOCKET_ERRNO != SOCKET_WOULDBLOCK) 245 | { 246 | return; 247 | } 248 | } 249 | else if (len) 250 | { 251 | printf("Recv Len %d", len); 252 | dump(buf, len); 253 | } 254 | else 255 | { 256 | printf("Disconnect\n"); 257 | closesocket(tcp); 258 | tcp = INVALID_SOCKET; 259 | } 260 | } 261 | 262 | Sleep(10); 263 | } 264 | while (key != 'x'); 265 | 266 | closesocket(udp); 267 | closesocket(tcp); 268 | closesocket(srv); 269 | 270 | printf("Done\n"); 271 | } 272 | -------------------------------------------------------------------------------- /demo/stream.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Oliver Schmidt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ******************************************************************************/ 29 | 30 | // Both pragmas are obligatory to have cc65 generate code 31 | // suitable to access the W5100 auto-increment registers. 32 | #pragma optimize (on) 33 | #pragma static-locals (on) 34 | 35 | #include <6502.h> 36 | 37 | #include "stream.h" 38 | 39 | #define MIN(a,b) (((a)<(b))?(a):(b)) 40 | 41 | static volatile byte* stream_mode; 42 | static volatile byte* stream_addr_hi; 43 | static volatile byte* stream_addr_lo; 44 | volatile byte* stream_data; 45 | #ifdef SOCKET_IRQ 46 | volatile byte stream_connected; 47 | 48 | static byte irq_stack[0x100]; 49 | #endif // SOCKET_IRQ 50 | 51 | static void set_addr(word addr) 52 | { 53 | *w5100_addr_hi = addr >> 8; 54 | *w5100_addr_lo = addr; 55 | } 56 | 57 | static byte get_byte(word addr) 58 | { 59 | set_addr(addr); 60 | 61 | return *stream_data; 62 | } 63 | 64 | static void set_byte(word addr, byte data) 65 | { 66 | set_addr(addr); 67 | 68 | *stream_data = data; 69 | } 70 | 71 | static word get_word(word addr) 72 | { 73 | set_addr(addr); 74 | 75 | return *w5100_data << 8 | *w5100_data; 76 | } 77 | 78 | static void set_word(word addr, word data) 79 | { 80 | set_addr(addr); 81 | 82 | *w5100_data = data >> 8; 83 | *w5100_data = data; 84 | } 85 | 86 | static void set_bytes(word addr, byte data[], word size) 87 | { 88 | set_addr(addr); 89 | 90 | { 91 | word i; 92 | for (i = 0; i < size; ++i) 93 | *stream_data = data[i]; 94 | } 95 | } 96 | 97 | #ifdef SOCKET_IRQ 98 | static byte socket_irq(void) 99 | { 100 | // Interrupt Register: S0_INT ? 101 | if (get_byte(0x0015) & 0x01) 102 | { 103 | // Socket 0 Interrupt Register: Get interrupt bits 104 | byte intr = get_byte(0x0402); 105 | 106 | // DISCON interrupt bit ? 107 | if (intr & 0x02) 108 | stream_connected = 0; 109 | 110 | // Socket 0 Interrupt Register: Clear interrupt bits 111 | set_byte(0x0402, intr); 112 | 113 | // Provide a bit feedback to the user 114 | *(byte*)0xC030 = 0; 115 | 116 | return IRQ_HANDLED; 117 | } 118 | return IRQ_NOT_HANDLED; 119 | } 120 | #endif // SOCKET_IRQ 121 | 122 | byte stream_init(word base_addr, byte *ip_addr, 123 | byte *submask, 124 | byte *gateway) 125 | { 126 | stream_mode = (byte*)base_addr; 127 | stream_addr_hi = (byte*)base_addr + 1; 128 | stream_addr_lo = (byte*)base_addr + 2; 129 | stream_data = (byte*)base_addr + 3; 130 | 131 | // Assert Indirect Bus I/F mode & Address Auto-Increment 132 | *stream_mode |= 0x03; 133 | 134 | // Retry Time-value Register: Default ? 135 | if (get_word(0x0017) != 2000) 136 | return 0; 137 | 138 | // Check for W5100 shared access 139 | // RX Memory Size Register: Assign 4+2+1+1KB to Socket 0 to 3 ? 140 | if (get_byte(0x001A) != 0x06) 141 | { 142 | // S/W Reset 143 | *stream_mode = 0x80; 144 | while (*stream_mode & 0x80) 145 | ; 146 | 147 | // Indirect Bus I/F mode & Address Auto-Increment 148 | *stream_mode = 0x03; 149 | 150 | // RX Memory Size Register: Assign 4KB each to Socket 0 and 1 151 | set_byte(0x001A, 0x0A); 152 | 153 | // TX Memory Size Register: Assign 4KB each to Socket 0 and 1 154 | set_byte(0x001B, 0x0A); 155 | 156 | // Source Hardware Address Register 157 | { 158 | static byte mac_addr[6] = {0x00, 0x08, 0xDC, // OUI of WIZnet 159 | 0x11, 0x11, 0x11}; 160 | set_bytes(0x0009, mac_addr, sizeof(mac_addr)); 161 | } 162 | } 163 | 164 | // Source IP Address Register 165 | set_bytes(0x000F, ip_addr, 4); 166 | 167 | // Subnet Mask Register 168 | set_bytes(0x0005, submask, 4); 169 | 170 | // Gateway IP Address Register 171 | set_bytes(0x0001, gateway, 4); 172 | 173 | #ifdef SOCKET_IRQ 174 | set_irq(socket_irq, irq_stack, sizeof(irq_stack)); 175 | 176 | // Interrupt Mask Register: IM_IR0 177 | set_byte(0x0016, 0x01); 178 | #endif // SOCKET_IRQ 179 | 180 | return 1; 181 | } 182 | 183 | byte stream_connect(byte *server_addr, word server_port) 184 | { 185 | #ifdef SOCKET_IRQ 186 | SEI(); 187 | #endif 188 | 189 | // Socket 0 Mode Register: TCP 190 | set_byte(0x0400, 0x01); 191 | 192 | // Socket 0 Source Port Register 193 | set_word(0x0404, 6502); 194 | 195 | // Socket 0 Command Register: OPEN 196 | set_byte(0x0401, 0x01); 197 | 198 | // Socket 0 Status Register: SOCK_INIT ? 199 | while (get_byte(0x0403) != 0x13) 200 | ; 201 | 202 | // Socket 0 Destination IP Address Register 203 | set_bytes(0x040C, server_addr, 4); 204 | 205 | // Socket 0 Destination Port Register 206 | set_word(0x0410, server_port); 207 | 208 | // Socket 0 Command Register: CONNECT 209 | set_byte(0x0401, 0x04); 210 | 211 | while (1) 212 | { 213 | // Socket 0 Status Register 214 | switch (get_byte(0x0403)) 215 | { 216 | case 0x00: 217 | #ifdef SOCKET_IRQ 218 | CLI(); 219 | stream_connected = 0; 220 | #endif 221 | return 0; // Socket Status: SOCK_CLOSED 222 | 223 | case 0x17: 224 | #ifdef SOCKET_IRQ 225 | CLI(); 226 | stream_connected = 1; 227 | #endif 228 | return 1; // Socket Status: SOCK_ESTABLISHED 229 | } 230 | } 231 | } 232 | 233 | void stream_disconnect(void) 234 | { 235 | #ifdef SOCKET_IRQ 236 | SEI(); 237 | #endif 238 | 239 | // Socket 0 Command Register: Command Pending ? 240 | while (get_byte(0x0401)) 241 | ; 242 | 243 | // Socket 0 Command Register: DISCON 244 | set_byte(0x0401, 0x08); 245 | 246 | // Socket 0 Status Register: SOCK_CLOSED ? 247 | while (get_byte(0x0403)) 248 | // Wait for disconnect to allow for reconnect 249 | ; 250 | 251 | #ifdef SOCKET_IRQ 252 | CLI(); 253 | stream_connected = 0; 254 | #endif 255 | } 256 | 257 | #ifndef SOCKET_IRQ 258 | byte stream_connected(void) 259 | { 260 | // Socket 0 Status Register: SOCK_ESTABLISHED ? 261 | return get_byte(0x0403) == 0x17; 262 | } 263 | #endif // !SOCKET_IRQ 264 | 265 | word stream_data_request(byte do_send) 266 | { 267 | // Socket 0 Command Register: Command Pending ? 268 | if (get_byte(0x0401)) 269 | return 0; 270 | 271 | { 272 | word size = 0; 273 | word prev_size; 274 | 275 | // Reread of nonzero RX Received Size Register / TX Free Size Register 276 | // until its value settles ... 277 | // - is present in the WIZnet driver - getSn_RX_RSR() / getSn_TX_FSR() 278 | // - was additionally tested on 6502 machines to be actually necessary 279 | do 280 | { 281 | prev_size = size; 282 | { 283 | static word reg[2] = {0x0426, // Socket 0 RX Received Size Register 284 | 0x0420}; // Socket 0 TX Free Size Register 285 | size = get_word(reg[do_send]); 286 | } 287 | } 288 | while (size != prev_size); 289 | 290 | if (!size) 291 | return 0; 292 | 293 | { 294 | static word reg[2] = {0x0428, // Socket 0 RX Read Pointer Register 295 | 0x0424}; // Socket 0 TX Write Pointer Register 296 | 297 | static word bas[2] = {0x6000, // Socket 0 RX Memory Base 298 | 0x4000}; // Socket 0 TX Memory Base 299 | 300 | static word lim[2] = {0x7000, // Socket 0 RX Memory Limit 301 | 0x5000}; // Socket 0 TX Memory Limit 302 | 303 | // Calculate and set physical address 304 | word addr = get_word(reg[do_send]) & 0x0FFF | bas[do_send]; 305 | set_addr(addr); 306 | 307 | // Access to *stream_data is limited both by ... 308 | // - size of received / free space 309 | // - end of physical address space 310 | return MIN(size, lim[do_send] - addr); 311 | } 312 | } 313 | } 314 | 315 | void stream_data_commit(byte do_send, word size) 316 | { 317 | { 318 | static word reg[2] = {0x0428, // Socket 0 RX Read Pointer Register 319 | 0x0424}; // Socket 0 TX Write Pointer Register 320 | set_word(reg[do_send], get_word(reg[do_send]) + size); 321 | } 322 | 323 | { 324 | static byte cmd[2] = {0x40, // Socket Command: RECV 325 | 0x20}; // Socket Command: SEND 326 | // Socket 0 Command Register 327 | set_byte(0x0401, cmd[do_send]); 328 | } 329 | 330 | // Do NOT wait for command completion here, rather 331 | // let W5100 operation overlap with 6502 operation 332 | } 333 | -------------------------------------------------------------------------------- /demo/stream.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2014, Oliver Schmidt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ******************************************************************************/ 29 | 30 | #ifndef _STREAM_H_ 31 | #define _STREAM_H_ 32 | 33 | #ifndef __APPLE2ENH__ 34 | #error W5100 auto-increment register access requires 65C02. 35 | #endif 36 | 37 | typedef unsigned char byte; 38 | typedef unsigned short word; 39 | 40 | word stream_data_request(byte do_send); 41 | void stream_data_commit(byte do_send, word size); 42 | 43 | // After stream_receive_request() every read operation returns the next byte 44 | // from the server. 45 | // After stream_send_request() every write operation prepares the next byte 46 | // to be sent to the server. 47 | extern volatile byte* stream_data; 48 | 49 | // Initialize W5100 Ethernet controller with indirect bus interface located 50 | // at . Use , and to configure the 51 | // TCP/IP stack. 52 | // Return <1> if a W5100 was found at , return <0> otherwise. 53 | byte stream_init(word base_addr, byte *ip_addr, 54 | byte *submask, 55 | byte *gateway); 56 | 57 | // Connect to server with IP address on TCP port . 58 | // Use <6502> as fixed local port. 59 | // Return <1> if the connection is established, return <0> otherwise. 60 | byte stream_connect(byte *server_addr, word server_port); 61 | 62 | // Disconnect from server. 63 | void stream_disconnect(void); 64 | 65 | #ifdef SOCKET_IRQ 66 | 67 | // Check if still connected to server. 68 | // <1> if the connection is established, <0> otherwise. 69 | extern volatile byte stream_connected; 70 | 71 | #else // SOCKET_IRQ 72 | 73 | // Check if still connected to server. 74 | // Return <1> if the connection is established, return <0> otherwise. 75 | byte stream_connected(void); 76 | 77 | #endif // SOCKET_IRQ 78 | 79 | // Request to receive data from the server. 80 | // Return maximum number of bytes to be received by reading from *stream_data. 81 | #define stream_receive_request() stream_data_request(0) 82 | 83 | // Commit receiving of bytes from server. may be smaller than 84 | // the return value of stream_receive_request(). Not commiting at all just 85 | // makes the next request receive the same data again. 86 | #define stream_receive_commit(size) stream_data_commit(0, (size)) 87 | 88 | // Request to send data to the server. 89 | // Return maximum number of bytes to be send by writing to *stream_data. 90 | #define stream_send_request() stream_data_request(1) 91 | 92 | // Commit sending of bytes to server. is usually smaller than 93 | // the return value of stream_send_request(). Not commiting at all just turns 94 | // the stream_send_request() - and the writes to *stream_data - into NOPs. 95 | #define stream_send_commit(size) stream_data_commit(1, (size)) 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /demo/stream_main.c: -------------------------------------------------------------------------------- 1 | // Both pragmas are obligatory to have cc65 generate code 2 | // suitable to access the W5100 auto-increment registers. 3 | #pragma optimize (on) 4 | #pragma static-locals (on) 5 | 6 | #include 7 | #include 8 | #include 9 | #include <6502.h> 10 | 11 | #include "stream.h" 12 | 13 | #define MIN(a,b) (((a)<(b))?(a):(b)) 14 | 15 | byte ip_addr[4] = {192, 168, 0, 123}; 16 | byte submask[4] = {255, 255, 255, 0}; 17 | byte gateway[4] = {192, 168, 0, 1}; 18 | 19 | byte server[4] = {192, 168, 0, 2}; // IP addr of machine running peer.c 20 | 21 | void main(void) 22 | { 23 | char key; 24 | 25 | videomode(VIDEOMODE_80COL); 26 | printf("Init\n"); 27 | if (!stream_init(0xC0B4, ip_addr, 28 | submask, 29 | gateway)) 30 | { 31 | printf("No Hardware Found\n"); 32 | return; 33 | } 34 | printf("Connect\n"); 35 | if (!stream_connect(server, 6502)) 36 | { 37 | printf("Faild To Connect To %d.%d.%d.%d\n", server[0], 38 | server[1], 39 | server[2], 40 | server[3]); 41 | return; 42 | } 43 | printf("Connected To %d.%d.%d.%d\n", server[0], 44 | server[1], 45 | server[2], 46 | server[3]); 47 | 48 | printf("(T)CP or e(X)it\n"); 49 | do 50 | { 51 | word len, all; 52 | 53 | if (kbhit()) 54 | { 55 | key = tolower(cgetc()); 56 | } 57 | else 58 | { 59 | key = '\0'; 60 | } 61 | 62 | if (key == 't') 63 | { 64 | all = 500; 65 | printf("Send Len %d", all); 66 | do 67 | { 68 | word i; 69 | 70 | #ifdef SOCKET_IRQ 71 | SEI(); 72 | #endif 73 | while (!(len = stream_send_request())) 74 | { 75 | printf("!"); 76 | } 77 | len = MIN(all, len); 78 | for (i = 0; i < len; ++i) 79 | { 80 | *stream_data = 500 - all + i; 81 | } 82 | stream_send_commit(len); 83 | #ifdef SOCKET_IRQ 84 | CLI(); 85 | #endif 86 | all -= len; 87 | } 88 | while (all); 89 | printf(".\n"); 90 | } 91 | 92 | #ifdef SOCKET_IRQ 93 | SEI(); 94 | #endif 95 | len = stream_receive_request(); 96 | if (len) 97 | { 98 | word i; 99 | 100 | printf("Recv Len %d", len); 101 | for (i = 0; i < len; ++i) 102 | { 103 | if ((i % 24) == 0) 104 | { 105 | printf("\n$%04X:", i); 106 | } 107 | printf(" %02X", *stream_data); 108 | } 109 | stream_receive_commit(len); 110 | printf(".\n"); 111 | } 112 | #ifdef SOCKET_IRQ 113 | CLI(); 114 | #endif 115 | 116 | #ifdef SOCKET_IRQ 117 | if (!stream_connected) 118 | #else 119 | if (!stream_connected()) 120 | #endif 121 | { 122 | printf("Disconnect\n"); 123 | return; 124 | } 125 | } 126 | while (key != 'x'); 127 | 128 | stream_disconnect(); 129 | printf("Done\n"); 130 | } 131 | -------------------------------------------------------------------------------- /demo/tcp.s: -------------------------------------------------------------------------------- 1 | .feature c_comments 2 | 3 | /****************************************************************************** 4 | 5 | Copyright (c) 2014, Oliver Schmidt 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of the nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | ******************************************************************************/ 31 | 32 | .export _tcp_init, _tcp_done 33 | .export _tcp_recv_init, _tcp_recv_byte, _tcp_recv_done 34 | .export _tcp_send_init, _tcp_send_byte, _tcp_send_done 35 | 36 | ptr := $06 ; 2 byte pointer value 37 | sha := $08 ; 2 byte physical addr shadow ($F000-$FFFF) 38 | adv := $EB ; 2 byte pointer register advancement 39 | tmp := $ED ; 1 byte temporary value 40 | bas := $EE ; 1 byte socket 3 Base Address (hibyte) 41 | 42 | mode := $C0B4 43 | addr := $C0B5 44 | data := $C0B7 45 | 46 | ;------------------------------------------------------------------------------ 47 | 48 | _tcp_init: 49 | ; Input 50 | ; AX: Address of ip_parms (serverip, cfg_ip, cfg_netmask ,cfg_gateway) 51 | ; Output 52 | ; A: Nonzero if connected to server 53 | ; Remark 54 | ; The ip_parms are only accessed during this function. 55 | 56 | ; Set ip_parms pointer 57 | sta ptr 58 | stx ptr+1 59 | 60 | ; S/W Reset 61 | lda #$80 62 | sta mode 63 | : lda mode 64 | bmi :- 65 | 66 | ; Indirect Bus I/F mode, Address Auto-Increment 67 | lda #$03 68 | sta mode 69 | 70 | ; Gateway IP Address Register: IP address of router on local network 71 | ldx #$00 ; Hibyte 72 | ldy #$01 ; Lobyte 73 | jsr set_addr 74 | ldy #3*4 ; ip_parms::cfg_gateway 75 | jsr set_ipv4value 76 | 77 | ; Subnet Mask Register: Netmask of local network 78 | ; -> addr is already set 79 | ldy #2*4 ; ip_parms::cfg_netmask 80 | jsr set_ipv4value 81 | 82 | ; Source Hardware Address Register: MAC Address 83 | ; -> addr is already set 84 | ldx #$00 85 | : lda mac,x 86 | sta data 87 | inx 88 | cpx #$06 89 | bcc :- 90 | 91 | ; Source IP Address Register: IP address of local machine 92 | ; -> addr is already set 93 | ldy #1*4 ; ip_parms::cfg_ip 94 | jsr set_ipv4value 95 | 96 | ; RX Memory Size Register: Assign 4+2+1+1KB to socket 0 to 3 97 | ldx #$00 ; Hibyte 98 | ldy #$1A ; Lobyte 99 | jsr set_addr 100 | lda #$06 101 | sta data 102 | 103 | ; TX Memory Size Register: Assign 4+2+1+1KB to socket 0 to 3 104 | ; -> addr is already set 105 | ; -> A is still $06 106 | sta data 107 | 108 | ; Socket 3 Mode Register: TCP 109 | ldy #$00 110 | jsr set_addrsocket3 111 | lda #$01 112 | sta data 113 | 114 | ; Socket 3 Source Port Register: 6502 115 | ldy #$04 116 | jsr set_addrsocket3 117 | jsr set_data6502 118 | 119 | ; Socket 3 Destination IP Address Register: Destination IP address 120 | ldy #$0C 121 | jsr set_addrsocket3 122 | ldy #0*4 ; ip_parms::serverip 123 | jsr set_ipv4value 124 | 125 | ; Socket 3 Destination Port Register: 6502 126 | ; -> addr is already set 127 | jsr set_data6502 128 | 129 | ; Socket 3 Command Register: OPEN 130 | lda #$01 131 | jsr set_cmdsocket3 132 | 133 | ; Socket 3 Command Register: CONNECT 134 | lda #$04 135 | jsr set_cmdsocket3 136 | 137 | ; Socket 3 Status Register: SOCK_CLOSED or SOCK_ESTABLISHED ? 138 | : ldy #$03 139 | jsr set_addrsocket3 140 | lda data 141 | beq error ; SOCK_CLOSED (:= 0) 142 | cmp #$17 ; SOCK_ESTABLISHED 143 | bne :- ; Intermediate status -> retry 144 | 145 | ; Return success 146 | beq success ; Always 147 | 148 | ;------------------------------------------------------------------------------ 149 | 150 | set_ipv4value: 151 | ldx #$03 152 | : lda (ptr),y 153 | iny 154 | sta data 155 | dex 156 | bpl :- 157 | rts 158 | 159 | ;------------------------------------------------------------------------------ 160 | 161 | set_data6502: 162 | lda #<6502 163 | ldx #>6502 164 | stx data ; Hibyte 165 | sta data ; Lobyte 166 | rts 167 | 168 | ;------------------------------------------------------------------------------ 169 | 170 | _tcp_done: 171 | ; Input 172 | ; None 173 | ; Output 174 | ; None 175 | ; Remark 176 | ; Disconnect from the server. 177 | 178 | ; Check for completion of previous command 179 | jsr get_cmdsocket3 180 | 181 | ; Socket 3 Command Register: DISCON 182 | lda #$08 183 | 184 | set_cmdsocket3: 185 | ; Socket 3 Command Register: command 186 | jsr set_addrcmdreg3 187 | sta data 188 | 189 | get_cmdsocket3: 190 | ; Check for completion of command 191 | ; Socket 3 Command Register: 0 ? 192 | : jsr set_addrcmdreg3 193 | lda data 194 | bne :- ; Not completed -> retry 195 | rts 196 | 197 | ;------------------------------------------------------------------------------ 198 | 199 | _tcp_recv_init: 200 | ; Input 201 | ; None 202 | ; Output 203 | ; AX: Number of bytes to receive or -1 if not connected anymore 204 | ; Remark 205 | ; To be called before recv_byte. 206 | 207 | ; Socket 3 Status Register: SOCK_ESTABLISHED ? 208 | ldy #$03 209 | jsr set_addrsocket3 210 | lda data 211 | cmp #$17 212 | beq :+ 213 | 214 | ; Return -1 215 | lda #<$FFFF 216 | tax 217 | rts 218 | 219 | ; Socket 3 RX Received Size Register: 0 or volatile ? 220 | : lda #$26 ; Socket RX Received Size Register 221 | jsr prolog 222 | bne error 223 | 224 | ; Save pointer advancement 225 | stx adv ; Lobyte 226 | sta adv+1 ; Hibyte 227 | 228 | ; Socket 3 RX Read Pointer Register 229 | ; -> addr already set 230 | 231 | ; Calculate and set pyhsical address 232 | ldx #>$7C00 ; Socket 3 RX Base Address 233 | jsr set_addrphysical 234 | 235 | ; Return pointer advancement 236 | lda adv 237 | ldx adv+1 238 | rts 239 | 240 | ;------------------------------------------------------------------------------ 241 | 242 | _tcp_send_init: 243 | ; Input 244 | ; AX: Number of bytes to send 245 | ; Output 246 | ; A: Nonzero if ready to send 247 | ; Remark 248 | ; To be called before send_byte. 249 | 250 | ; Set pointer advancement 251 | sta adv 252 | stx adv+1 253 | 254 | ; Socket 3 TX Free Size Register: 0 or volatile ? 255 | lda #$20 ; Socket TX Free Size Register 256 | jsr prolog 257 | bne error 258 | 259 | ; Socket 3 TX Free Size Register: < advancement ? 260 | cpx adv ; Lobyte 261 | sbc adv+1 ; Hibyte 262 | bcc error ; Not enough free size 263 | 264 | ; Socket 3 TX Write Pointer Register 265 | ldy #$24 266 | jsr set_addrsocket3 267 | 268 | ; Calculate and set pyhsical address 269 | ldx #>$5C00 ; Socket 3 TX Base Address 270 | jsr set_addrphysical 271 | 272 | success: 273 | ; Return success 274 | lda #$01 275 | ldx #>$0000 ; Required by cc65 C callers 276 | rts 277 | 278 | ;------------------------------------------------------------------------------ 279 | 280 | error: 281 | lda #<$0000 282 | tax 283 | rts 284 | 285 | ;------------------------------------------------------------------------------ 286 | 287 | prolog: 288 | ; Check for completion of previous command 289 | ; Socket 3 Command Register: 0 ? 290 | jsr set_addrcmdreg3 291 | ldx data 292 | bne :++ ; Not completed -> Z = 0 293 | 294 | ; Socket Size Register: not 0 ? 295 | tay ; Select Size Register 296 | jsr get_wordsocket3 297 | stx ptr ; Lobyte 298 | sta ptr+1 ; Hibyte 299 | ora ptr 300 | bne :+ 301 | inx ; -> Z = 0 302 | rts 303 | 304 | ; Socket Size Register: volatile ? 305 | : jsr get_wordsocket3 306 | cpx ptr ; Lobyte 307 | bne :+ ; Volatile size -> Z = 0 308 | cmp ptr+1 ; Hibyte 309 | ; bne :+ ; Volatile size -> Z = 0 310 | : rts 311 | 312 | ;------------------------------------------------------------------------------ 313 | 314 | _tcp_recv_byte: 315 | ; Input 316 | ; None 317 | ; Output 318 | ; A: Byte received 319 | ; Remark 320 | ; May be called as often as indicated by recv_init. 321 | 322 | ; Read byte 323 | lda data 324 | 325 | ; Increment physical addr shadow lobyte 326 | inc sha 327 | beq incsha 328 | ldx #>$0000 ; Required by cc65 C callers 329 | rts 330 | 331 | ;------------------------------------------------------------------------------ 332 | 333 | _tcp_send_byte: 334 | ; Input 335 | ; A: Byte to send 336 | ; Output 337 | ; None 338 | ; Remark 339 | ; Should be called as often as indicated to send_init. 340 | 341 | ; Write byte 342 | sta data 343 | 344 | ; Increment physical addr shadow lobyte 345 | inc sha 346 | beq incsha 347 | rts 348 | 349 | incsha: 350 | ; Increment physical addr shadow hibyte 351 | inc sha+1 352 | beq set_addrbase 353 | ldx #>$0000 ; Required by cc65 C callers (_tcp_recv_byte) 354 | rts 355 | 356 | ;------------------------------------------------------------------------------ 357 | 358 | _tcp_recv_done: 359 | ; Input 360 | ; None 361 | ; Output 362 | ; None 363 | ; Remark 364 | ; Mark data indicated by recv_init as processed (independently from how 365 | ; often recv_byte was called), if not called then next call of recv_init 366 | ; will just indicate the very same data again. 367 | 368 | ; Set parameters for commit code 369 | lda #$40 ; RECV 370 | ldy #$28 ; Socket RX Read Pointer Register 371 | bne epilog ; Always 372 | 373 | ;------------------------------------------------------------------------------ 374 | 375 | _tcp_send_done: 376 | ; Input 377 | ; None 378 | ; Output 379 | ; None 380 | ; Remark 381 | ; Actually send data indicated to send_init (independently from how often 382 | ; send_byte was called), if not called then send_init (and send_byte) are 383 | ; just NOPs. 384 | 385 | ; Set parameters for commit code 386 | lda #$20 ; SEND 387 | ldy #$24 ; Socket TX Write Pointer Register 388 | 389 | epilog: 390 | ; Advance pointer register 391 | jsr set_addrsocket3 392 | tay ; Save command 393 | clc 394 | lda ptr 395 | adc adv 396 | tax 397 | lda ptr+1 398 | adc adv+1 399 | sta data ; Hibyte 400 | stx data ; Lobyte 401 | 402 | ; Set command register 403 | tya ; Restore command 404 | jsr set_addrcmdreg3 405 | sta data 406 | rts 407 | 408 | ;------------------------------------------------------------------------------ 409 | 410 | set_addrphysical: 411 | lda data ; Hibyte 412 | ldy data ; Lobyte 413 | sty ptr 414 | sta ptr+1 415 | and #>$03FF ; Socket Mask Address (hibyte) 416 | stx bas ; Socket Base Address (hibyte) 417 | ora bas 418 | tax 419 | ora #>$FC00 ; Move sha/sha+1 to $FC00-$FFFF 420 | sty sha 421 | sta sha+1 422 | 423 | set_addr: 424 | stx addr ; Hibyte 425 | sty addr+1 ; Lobyte 426 | ldx #>$0000 ; Required by cc65 C callers (_tcp_recv_byte) 427 | rts 428 | 429 | ;------------------------------------------------------------------------------ 430 | 431 | set_addrcmdreg3: 432 | ldy #$01 ; Socket Command Register 433 | 434 | set_addrsocket3: 435 | ldx #>$0700 ; Socket 3 register base address 436 | bne set_addr ; Always 437 | 438 | ;------------------------------------------------------------------------------ 439 | 440 | set_addrbase: 441 | ldx bas ; Socket Base Address (hibyte) 442 | ldy #<$0000 ; Socket Base Address (lobyte) 443 | beq set_addr ; Always 444 | 445 | ;------------------------------------------------------------------------------ 446 | 447 | get_wordsocket3: 448 | jsr set_addrsocket3 449 | lda data ; Hibyte 450 | ldx data ; Lobyte 451 | rts 452 | 453 | ;------------------------------------------------------------------------------ 454 | 455 | .rodata 456 | 457 | mac: .byte $00, $08, $DC ; OUI of WIZnet 458 | .byte $11, $11, $11 459 | -------------------------------------------------------------------------------- /demo/tcp_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | unsigned char __fastcall__ tcp_init(void *parms); 6 | void tcp_done(void); 7 | 8 | int tcp_recv_init(void); 9 | unsigned char tcp_recv_byte(void); 10 | void tcp_recv_done(void); 11 | 12 | unsigned char __fastcall__ tcp_send_init(unsigned int len); 13 | void __fastcall__ tcp_send_byte(unsigned char val); 14 | void tcp_send_done(void); 15 | 16 | struct 17 | { 18 | unsigned char serverip [4]; 19 | unsigned char cfg_ip [4]; 20 | unsigned char cfg_netmask[4]; 21 | unsigned char cfg_gateway[4]; 22 | } 23 | parms = 24 | { 25 | {192, 168, 0, 2}, // IP addr of machine running peer.c 26 | {192, 168, 0, 123}, 27 | {255, 255, 255, 0}, 28 | {192, 168, 0, 1} 29 | }; 30 | 31 | void main(void) 32 | { 33 | char key; 34 | 35 | videomode(VIDEOMODE_80COL); 36 | printf("Init\n"); 37 | if (!tcp_init(&parms)) 38 | { 39 | printf("Faild To Connect To %d.%d.%d.%d\n", parms.serverip[0], 40 | parms.serverip[1], 41 | parms.serverip[2], 42 | parms.serverip[3]); 43 | return; 44 | } 45 | printf("Connected To %d.%d.%d.%d\n", parms.serverip[0], 46 | parms.serverip[1], 47 | parms.serverip[2], 48 | parms.serverip[3]); 49 | 50 | printf("(T)CP or e(X)it\n"); 51 | do 52 | { 53 | unsigned len; 54 | 55 | if (kbhit()) 56 | { 57 | key = tolower(cgetc()); 58 | } 59 | else 60 | { 61 | key = '\0'; 62 | } 63 | 64 | if (key == 't') 65 | { 66 | unsigned i; 67 | 68 | len = 500; 69 | printf("Send Len %d", len); 70 | while (!tcp_send_init(len)) 71 | { 72 | printf("!"); 73 | } 74 | for (i = 0; i < len; ++i) 75 | { 76 | tcp_send_byte(i); 77 | } 78 | tcp_send_done(); 79 | printf(".\n"); 80 | } 81 | 82 | len = tcp_recv_init(); 83 | if (len == -1) 84 | { 85 | printf("Disconnect\n"); 86 | return; 87 | } 88 | else if (len) 89 | { 90 | unsigned i; 91 | 92 | printf("Recv Len %d", len); 93 | for (i = 0; i < len; ++i) 94 | { 95 | if ((i % 24) == 0) 96 | { 97 | printf("\n$%04X:", i); 98 | } 99 | printf(" %02X", tcp_recv_byte()); 100 | } 101 | tcp_recv_done(); 102 | printf(".\n"); 103 | } 104 | } 105 | while (key != 'x'); 106 | 107 | tcp_done(); 108 | printf("Done\n"); 109 | } 110 | -------------------------------------------------------------------------------- /demo/udp.s: -------------------------------------------------------------------------------- 1 | .feature c_comments 2 | 3 | /****************************************************************************** 4 | 5 | Copyright (c) 2014, Oliver Schmidt 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of the nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | ******************************************************************************/ 31 | 32 | .export _udp_init 33 | .export _udp_recv_init, _udp_recv_byte, _udp_recv_done 34 | .export _udp_send_init, _udp_send_byte, _udp_send_done 35 | 36 | ptr := $06 ; 2 byte pointer value 37 | sha := $08 ; 2 byte physical addr shadow ($F000-$FFFF) 38 | adv := $EB ; 2 byte pointer register advancement 39 | len := $ED ; 2 byte frame length 40 | tmp := $FA ; 1 byte temporary value 41 | bas := $FB ; 1 byte socket 3 Base Address (hibyte) 42 | 43 | mode := $C0B4 44 | addr := $C0B5 45 | data := $C0B7 46 | 47 | ;------------------------------------------------------------------------------ 48 | 49 | _udp_init: 50 | ; Input 51 | ; AX: Address of ip_parms (serverip, cfg_ip, cfg_netmask, cfg_gateway) 52 | ; Output 53 | ; None 54 | ; Remark 55 | ; The ip_parms are only accessed during this function. 56 | 57 | ; Set ip_parms pointer 58 | sta ptr 59 | stx ptr+1 60 | 61 | ; S/W Reset 62 | lda #$80 63 | sta mode 64 | : lda mode 65 | bmi :- 66 | 67 | ; Indirect Bus I/F mode, Address Auto-Increment 68 | lda #$03 69 | sta mode 70 | 71 | ; Gateway IP Address Register: IP address of router on local network 72 | ldx #$00 ; Hibyte 73 | ldy #$01 ; Lobyte 74 | jsr set_addr 75 | ldy #3*4 ; ip_parms::cfg_gateway 76 | jsr set_ipv4value 77 | 78 | ; Subnet Mask Register: Netmask of local network 79 | ; -> addr is already set 80 | ldy #2*4 ; ip_parms::cfg_netmask 81 | jsr set_ipv4value 82 | 83 | ; Source Hardware Address Register: MAC Address 84 | ; -> addr is already set 85 | ldx #$00 86 | : lda mac,x 87 | sta data 88 | inx 89 | cpx #$06 90 | bcc :- 91 | 92 | ; Source IP Address Register: IP address of local machine 93 | ; -> addr is already set 94 | ldy #1*4 ; ip_parms::cfg_ip 95 | jsr set_ipv4value 96 | 97 | ; RX Memory Size Register: Assign 4+2+1+1KB to socket 0 to 3 98 | ldx #$00 ; Hibyte 99 | ldy #$1A ; Lobyte 100 | jsr set_addr 101 | lda #$06 102 | sta data 103 | 104 | ; TX Memory Size Register: Assign 4+2+1+1KB to socket 0 to 3 105 | ; -> addr is already set 106 | ; -> A is still $06 107 | sta data 108 | 109 | ; Socket 3 Source Port Register: 6502 110 | ldy #$04 111 | jsr set_addrsocket3 112 | jsr set_data6502 113 | 114 | ; Socket 3 Destination IP Address Register: Destination IP address 115 | ; This has to be the last call to set_ipv4value because it writes 116 | ; as a side effect to 'hdr' and it is the destination IP address 117 | ; that has to be present in 'hdr' after initialization 118 | ldy #$0C 119 | jsr set_addrsocket3 120 | ldy #0*4 ; ip_parms::serverip 121 | jsr set_ipv4value 122 | 123 | ; Socket 3 Destination Port Register: 6502 124 | ; -> addr is already set 125 | jsr set_data6502 126 | 127 | ; Socket 3 Mode Register: UDP 128 | ldy #$00 129 | jsr set_addrsocket3 130 | lda #$02 131 | sta data 132 | 133 | ; Socket 3 Command Register: OPEN 134 | ; -> addr is already set 135 | lda #$01 136 | sta data 137 | rts 138 | 139 | ;------------------------------------------------------------------------------ 140 | 141 | set_ipv4value: 142 | ldx #$03 143 | : lda (ptr),y 144 | iny 145 | sta data 146 | sta hdr+2,x 147 | dex 148 | bpl :- 149 | rts 150 | 151 | ;------------------------------------------------------------------------------ 152 | 153 | set_data6502: 154 | lda #<6502 155 | ldx #>6502 156 | stx data ; Hibyte 157 | sta data ; Lobyte 158 | rts 159 | 160 | ;------------------------------------------------------------------------------ 161 | 162 | _udp_recv_init: 163 | ; Input 164 | ; None 165 | ; Output 166 | ; AX: Number of bytes to receive 167 | ; Remark 168 | ; To be called before recv_byte. 169 | 170 | ; Socket 3 RX Received Size Register: 0 or volatile ? 171 | lda #$26 ; Socket RX Received Size Register 172 | jsr prolog 173 | bne error 174 | 175 | ; Socket 3 RX Read Pointer Register 176 | ; -> addr already set 177 | 178 | ; Calculate and set pyhsical address 179 | ldx #>$7C00 ; Socket 3 RX Base Address 180 | jsr set_addrphysical 181 | 182 | ; Compare peer IP addr and peer port with expected values 183 | ; in 'hdr' and set C(arry flag) if there's a mismatch 184 | clc 185 | ldx #$05 186 | stx tmp 187 | : jsr _udp_recv_byte ; Doesn't trash C 188 | ldx tmp 189 | eor hdr,x ; Doesn't trash C 190 | beq :+ 191 | sec 192 | : dec tmp 193 | bpl :-- 194 | php ; Save C 195 | 196 | ; Read data length 197 | jsr _udp_recv_byte ; Hibyte 198 | sta len+1 199 | jsr _udp_recv_byte ; Lobyte 200 | sta len 201 | 202 | ; Add 8 byte header to set pointer advancement 203 | clc 204 | adc #<$0008 205 | sta adv 206 | lda len+1 207 | adc #>$0008 208 | sta adv+1 209 | 210 | ; Skip frame if it doesn't originate from our 211 | ; expected communicaion peer 212 | plp ; Restore C 213 | bcs _udp_recv_done 214 | 215 | ; Return data length 216 | lda len 217 | ldx len+1 218 | rts 219 | 220 | ;------------------------------------------------------------------------------ 221 | 222 | _udp_send_init: 223 | ; Input 224 | ; AX: Number of bytes to send 225 | ; Output 226 | ; A: Nonzero if ready to send 227 | ; Remark 228 | ; To be called before send_byte. 229 | 230 | ; Set pointer advancement 231 | sta adv 232 | stx adv+1 233 | 234 | ; Socket 3 TX Free Size Register: 0 or volatile ? 235 | lda #$20 ; Socket TX Free Size Register 236 | jsr prolog 237 | bne error 238 | 239 | ; Socket 3 TX Free Size Register: < advancement ? 240 | cpx adv ; Lobyte 241 | sbc adv+1 ; Hibyte 242 | bcc error ; Not enough free size 243 | 244 | ; Socket 3 TX Write Pointer Register 245 | ldy #$24 246 | jsr set_addrsocket3 247 | 248 | ; Calculate and set pyhsical address 249 | ldx #>$5C00 ; Socket 3 TX Base Address 250 | jsr set_addrphysical 251 | 252 | ; Return success 253 | lda #$01 254 | ldx #>$0000 ; Required by cc65 C callers 255 | rts 256 | 257 | ;------------------------------------------------------------------------------ 258 | 259 | error: 260 | lda #<$0000 261 | tax 262 | rts 263 | 264 | ;------------------------------------------------------------------------------ 265 | 266 | prolog: 267 | ; Check for completion of previous command 268 | ; Socket 3 Command Register: 0 ? 269 | jsr set_addrcmdreg3 270 | ldx data 271 | bne :++ ; Not completed -> Z = 0 272 | 273 | ; Socket Size Register: not 0 ? 274 | tay ; Select Size Register 275 | jsr get_wordsocket3 276 | stx ptr ; Lobyte 277 | sta ptr+1 ; Hibyte 278 | ora ptr 279 | bne :+ 280 | inx ; -> Z = 0 281 | rts 282 | 283 | ; Socket Size Register: volatile ? 284 | : jsr get_wordsocket3 285 | cpx ptr ; Lobyte 286 | bne :+ ; Volatile size -> Z = 0 287 | cmp ptr+1 ; Hibyte 288 | ; bne :+ ; Volatile size -> Z = 0 289 | : rts 290 | 291 | ;------------------------------------------------------------------------------ 292 | 293 | _udp_recv_byte: 294 | ; Input 295 | ; None 296 | ; Output 297 | ; A: Byte received 298 | ; Remark 299 | ; May be called as often as indicated by recv_init. 300 | 301 | ; Read byte 302 | lda data 303 | 304 | ; Increment physical addr shadow lobyte 305 | inc sha 306 | beq incsha 307 | ldx #>$0000 ; Required by cc65 C callers 308 | rts 309 | 310 | ;------------------------------------------------------------------------------ 311 | 312 | _udp_send_byte: 313 | ; Input 314 | ; A: Byte to send 315 | ; Output 316 | ; None 317 | ; Remark 318 | ; Should be called as often as indicated to send_init. 319 | 320 | ; Write byte 321 | sta data 322 | 323 | ; Increment physical addr shadow lobyte 324 | inc sha 325 | beq incsha 326 | rts 327 | 328 | incsha: 329 | ; Increment physical addr shadow hibyte 330 | inc sha+1 331 | beq set_addrbase 332 | ldx #>$0000 ; Required by cc65 C callers (_udp_recv_byte) 333 | rts 334 | 335 | ;------------------------------------------------------------------------------ 336 | 337 | _udp_recv_done: 338 | ; Input 339 | ; None 340 | ; Output 341 | ; None 342 | ; Remark 343 | ; Mark data indicated by recv_init as processed (independently from how 344 | ; often recv_byte was called), if not called then next call of recv_init 345 | ; will just indicate the very same data again. 346 | 347 | ; Set parameters for commit code 348 | lda #$40 ; RECV 349 | ldy #$28 ; Socket RX Read Pointer Register 350 | bne epilog ; Always 351 | 352 | ;------------------------------------------------------------------------------ 353 | 354 | _udp_send_done: 355 | ; Input 356 | ; None 357 | ; Output 358 | ; None 359 | ; Remark 360 | ; Actually send data indicated to send_init (independently from how often 361 | ; send_byte was called), if not called then send_init (and send_byte) are 362 | ; just NOPs. 363 | 364 | ; Set parameters for commit code 365 | lda #$20 ; SEND 366 | ldy #$24 ; Socket TX Write Pointer Register 367 | 368 | epilog: 369 | ; Advance pointer register 370 | jsr set_addrsocket3 371 | tay ; Save command 372 | clc 373 | lda ptr 374 | adc adv 375 | tax 376 | lda ptr+1 377 | adc adv+1 378 | sta data ; Hibyte 379 | stx data ; Lobyte 380 | 381 | ; Set command register 382 | tya ; Restore command 383 | jsr set_addrcmdreg3 384 | sta data 385 | 386 | ; Return error (_udp_recv_init) 387 | bne error ; Always 388 | 389 | ;------------------------------------------------------------------------------ 390 | 391 | set_addrphysical: 392 | lda data ; Hibyte 393 | ldy data ; Lobyte 394 | sty ptr 395 | sta ptr+1 396 | and #>$03FF ; Socket Mask Address (hibyte) 397 | stx bas ; Socket Base Address (hibyte) 398 | ora bas 399 | tax 400 | ora #>$FC00 ; Move sha/sha+1 to $FC00-$FFFF 401 | sty sha 402 | sta sha+1 403 | 404 | set_addr: 405 | stx addr ; Hibyte 406 | sty addr+1 ; Lobyte 407 | ldx #>$0000 ; Required by cc65 C callers (_udp_recv_byte) 408 | rts 409 | 410 | ;------------------------------------------------------------------------------ 411 | 412 | set_addrcmdreg3: 413 | ldy #$01 ; Socket Command Register 414 | 415 | set_addrsocket3: 416 | ldx #>$0700 ; Socket 3 register base address 417 | bne set_addr ; Always 418 | 419 | ;------------------------------------------------------------------------------ 420 | 421 | set_addrbase: 422 | ldx bas ; Socket Base Address (hibyte) 423 | ldy #<$0000 ; Socket Base Address (lobyte) 424 | beq set_addr ; Always 425 | 426 | ;------------------------------------------------------------------------------ 427 | 428 | get_wordsocket3: 429 | jsr set_addrsocket3 430 | lda data ; Hibyte 431 | ldx data ; Lobyte 432 | rts 433 | 434 | ;------------------------------------------------------------------------------ 435 | 436 | .rodata 437 | 438 | mac: .byte $00, $08, $DC ; OUI of WIZnet 439 | .byte $11, $11, $11 440 | 441 | ;------------------------------------------------------------------------------ 442 | 443 | .data 444 | 445 | hdr: .word 6502 ; Destination Port 446 | .res 4 ; Destination IP Address 447 | -------------------------------------------------------------------------------- /demo/udp_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void __fastcall__ udp_init(void *parms); 6 | 7 | unsigned int udp_recv_init(void); 8 | unsigned char udp_recv_byte(void); 9 | void udp_recv_done(void); 10 | 11 | unsigned char __fastcall__ udp_send_init(unsigned int len); 12 | void __fastcall__ udp_send_byte(unsigned char val); 13 | void udp_send_done(void); 14 | 15 | struct 16 | { 17 | unsigned char serverip [4]; 18 | unsigned char cfg_ip [4]; 19 | unsigned char cfg_netmask[4]; 20 | unsigned char cfg_gateway[4]; 21 | } 22 | parms = 23 | { 24 | {192, 168, 0, 2}, // IP addr of machine running peer.c 25 | {192, 168, 0, 123}, 26 | {255, 255, 255, 0}, 27 | {192, 168, 0, 1} 28 | }; 29 | 30 | void main(void) 31 | { 32 | char key; 33 | 34 | videomode(VIDEOMODE_80COL); 35 | printf("Init\n"); 36 | udp_init(&parms); 37 | 38 | printf("(U)DP or e(X)it\n"); 39 | do 40 | { 41 | unsigned len; 42 | 43 | if (kbhit()) 44 | { 45 | key = tolower(cgetc()); 46 | } 47 | else 48 | { 49 | key = '\0'; 50 | } 51 | 52 | if (key == 'u') 53 | { 54 | unsigned i; 55 | 56 | len = 500; 57 | printf("Send Len %d To %d.%d.%d.%d", len, parms.serverip[0], 58 | parms.serverip[1], 59 | parms.serverip[2], 60 | parms.serverip[3]); 61 | while (!udp_send_init(len)) 62 | { 63 | printf("!"); 64 | } 65 | for (i = 0; i < len; ++i) 66 | { 67 | udp_send_byte(i); 68 | } 69 | udp_send_done(); 70 | printf(".\n"); 71 | } 72 | 73 | len = udp_recv_init(); 74 | if (len) 75 | { 76 | unsigned i; 77 | 78 | printf("Recv Len %d From %d.%d.%d.%d", len, parms.serverip[0], 79 | parms.serverip[1], 80 | parms.serverip[2], 81 | parms.serverip[3]); 82 | for (i = 0; i < len; ++i) 83 | { 84 | if ((i % 24) == 0) 85 | { 86 | printf("\n$%04X:", i); 87 | } 88 | printf(" %02X", udp_recv_byte()); 89 | } 90 | udp_recv_done(); 91 | printf(".\n"); 92 | } 93 | } 94 | while (key != 'x'); 95 | 96 | printf("Done\n"); 97 | } 98 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | all: test.dsk 2 | 3 | test.bin: test.c ../demo/stream.c 4 | cl65 -o $@ -t apple2enh -m $(basename $@).map $^ 5 | 6 | test3.bin: test.c stream3.c 7 | cl65 -o $@ -t apple2 -m $(basename $@).map $^ 8 | 9 | test.dsk: test.bin test3.bin 10 | cp prodos.dsk $@ 11 | java -jar $(AC) -p $@ test.system sys < $(shell cl65 --print-target-path)/apple2/util/loader.system 12 | java -jar $(AC) -as $@ test bin < test.bin 13 | java -jar $(AC) -p $@ test3.system sys < $(shell cl65 --print-target-path)/apple2/util/loader.system 14 | java -jar $(AC) -as $@ test3 bin < test3.bin 15 | 16 | clean: 17 | -rm -f *.o *.map *.bin test.dsk 18 | -------------------------------------------------------------------------------- /test/README.txt: -------------------------------------------------------------------------------- 1 | Production Test 2 | =============== 3 | 4 | test.c 5 | ------ 6 | A TCP client to be run on an Apple II containing the Uthernet II card under 7 | test in slot 3. If the program is run automatically after booting the Apple 8 | II neither needs a keyboard nor a monitor. Therefore the program beeps both 9 | on startup and shutdown. The program echos all data back to the server. 10 | 11 | test.py 12 | ------- 13 | A TCP server to be run on a peer machine with the IPv4 address 192.168.0.1. 14 | After connected by the client the program sends 100kB of random data to the 15 | client waits until the client echos the data. If the received data differs 16 | from the sent data or if no data is transmitted for more than 1 second the 17 | test fails. For production usage run the program with stderr redirected to 18 | the null device. 19 | -------------------------------------------------------------------------------- /test/stream3.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Oliver Schmidt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ******************************************************************************/ 29 | 30 | // Both pragmas are obligatory to have cc65 generate code 31 | // suitable to access the W5100 auto-increment registers. 32 | #pragma optimize (on) 33 | #pragma static-locals (on) 34 | 35 | #include "stream3.h" 36 | 37 | #define MIN(a,b) (((a)<(b))?(a):(b)) 38 | 39 | #define stream_mode ((byte*)(BASE_ADDR)) 40 | #define stream_addr_hi ((byte*)(BASE_ADDR + 1)) 41 | #define stream_addr_lo ((byte*)(BASE_ADDR + 2)) 42 | 43 | static void set_addr(word addr) 44 | { 45 | // The variables are necessary to have cc65 generate code 46 | // suitable to access the W5100 auto-increment registers. 47 | byte addr_hi = addr >> 8; 48 | byte addr_lo = addr; 49 | *stream_addr_hi = addr_hi; 50 | *stream_addr_lo = addr_lo; 51 | } 52 | 53 | static byte get_byte(word addr) 54 | { 55 | set_addr(addr); 56 | 57 | return *stream_data; 58 | } 59 | 60 | static void set_byte(word addr, byte data) 61 | { 62 | set_addr(addr); 63 | 64 | *stream_data = data; 65 | } 66 | 67 | static word get_word(word addr) 68 | { 69 | set_addr(addr); 70 | 71 | { 72 | // The variables are necessary to have cc65 generate code 73 | // suitable to access the W5100 auto-increment registers. 74 | byte data_hi = *stream_data; 75 | byte data_lo = *stream_data; 76 | return data_hi << 8 | data_lo; 77 | } 78 | } 79 | 80 | static void set_word(word addr, word data) 81 | { 82 | set_addr(addr); 83 | 84 | { 85 | // The variables are necessary to have cc65 generate code 86 | // suitable to access the W5100 auto-increment registers. 87 | byte data_hi = data >> 8; 88 | byte data_lo = data; 89 | *stream_data = data_hi; 90 | *stream_data = data_lo; 91 | } 92 | } 93 | 94 | static void set_bytes(word addr, byte data[], word size) 95 | { 96 | set_addr(addr); 97 | 98 | { 99 | word i; 100 | for (i = 0; i < size; ++i) 101 | *stream_data = data[i]; 102 | } 103 | } 104 | 105 | byte stream_init(word base_addr, byte *ip_addr, 106 | byte *submask, 107 | byte *gateway) 108 | { 109 | if (base_addr != BASE_ADDR) 110 | return 0; 111 | 112 | // Assert Indirect Bus I/F mode & Address Auto-Increment 113 | *stream_mode |= 0x03; 114 | 115 | // Retry Time-value Register: Default ? 116 | if (get_word(0x0017) != 2000) 117 | return 0; 118 | 119 | // Check for W5100 shared access 120 | // RX Memory Size Register: Assign 4+2+1+1KB to Socket 0 to 3 ? 121 | if (get_byte(0x001A) != 0x06) 122 | { 123 | // S/W Reset 124 | *stream_mode = 0x80; 125 | while (*stream_mode & 0x80) 126 | ; 127 | 128 | // Indirect Bus I/F mode & Address Auto-Increment 129 | *stream_mode = 0x03; 130 | 131 | // RX Memory Size Register: Assign 4KB each to Socket 0 and 1 132 | set_byte(0x001A, 0x0A); 133 | 134 | // TX Memory Size Register: Assign 4KB each to Socket 0 and 1 135 | set_byte(0x001B, 0x0A); 136 | 137 | // Source Hardware Address Register 138 | { 139 | static byte mac_addr[6] = {0x00, 0x08, 0xDC, // OUI of WIZnet 140 | 0x11, 0x11, 0x11}; 141 | set_bytes(0x0009, mac_addr, sizeof(mac_addr)); 142 | } 143 | } 144 | 145 | // Source IP Address Register 146 | set_bytes(0x000F, ip_addr, 4); 147 | 148 | // Subnet Mask Register 149 | set_bytes(0x0005, submask, 4); 150 | 151 | // Gateway IP Address Register 152 | set_bytes(0x0001, gateway, 4); 153 | 154 | return 1; 155 | } 156 | 157 | byte stream_connect(byte *server_addr, word server_port) 158 | { 159 | // Socket 0 Mode Register: TCP 160 | set_byte(0x0400, 0x01); 161 | 162 | // Socket 0 Source Port Register 163 | set_word(0x0404, 6502); 164 | 165 | // Socket 0 Command Register: OPEN 166 | set_byte(0x0401, 0x01); 167 | 168 | // Socket 0 Status Register: SOCK_INIT ? 169 | while (get_byte(0x0403) != 0x13) 170 | ; 171 | 172 | // Socket 0 Destination IP Address Register 173 | set_bytes(0x040C, server_addr, 4); 174 | 175 | // Socket 0 Destination Port Register 176 | set_word(0x0410, server_port); 177 | 178 | // Socket 0 Command Register: CONNECT 179 | set_byte(0x0401, 0x04); 180 | 181 | while (1) 182 | { 183 | // Socket 0 Status Register 184 | switch (get_byte(0x0403)) 185 | { 186 | case 0x00: return 0; // Socket Status: SOCK_CLOSED 187 | case 0x17: return 1; // Socket Status: SOCK_ESTABLISHED 188 | } 189 | } 190 | } 191 | 192 | byte stream_connected(void) 193 | { 194 | // Socket 0 Status Register: SOCK_ESTABLISHED ? 195 | return get_byte(0x0403) == 0x17; 196 | } 197 | 198 | void stream_disconnect(void) 199 | { 200 | // Socket 0 Command Register: Command Pending ? 201 | while (get_byte(0x0401)) 202 | ; 203 | 204 | // Socket 0 Command Register: DISCON 205 | set_byte(0x0401, 0x08); 206 | 207 | // Socket 0 Status Register: SOCK_CLOSED ? 208 | while (get_byte(0x0403)) 209 | // Wait for disconnect to allow for reconnect 210 | ; 211 | } 212 | 213 | word stream_data_request(byte do_send) 214 | { 215 | // Socket 0 Command Register: Command Pending ? 216 | if (get_byte(0x0401)) 217 | return 0; 218 | 219 | { 220 | word size = 0; 221 | word prev_size; 222 | 223 | // Reread of nonzero RX Received Size Register / TX Free Size Register 224 | // until its value settles ... 225 | // - is present in the WIZnet driver - getSn_RX_RSR() / getSn_TX_FSR() 226 | // - was additionally tested on 6502 machines to be actually necessary 227 | do 228 | { 229 | prev_size = size; 230 | { 231 | static word reg[2] = {0x0426, // Socket 0 RX Received Size Register 232 | 0x0420}; // Socket 0 TX Free Size Register 233 | size = get_word(reg[do_send]); 234 | } 235 | } 236 | while (size != prev_size); 237 | 238 | if (!size) 239 | return 0; 240 | 241 | { 242 | static word reg[2] = {0x0428, // Socket 0 RX Read Pointer Register 243 | 0x0424}; // Socket 0 TX Write Pointer Register 244 | 245 | static word bas[2] = {0x6000, // Socket 0 RX Memory Base 246 | 0x4000}; // Socket 0 TX Memory Base 247 | 248 | static word lim[2] = {0x7000, // Socket 0 RX Memory Limit 249 | 0x5000}; // Socket 0 TX Memory Limit 250 | 251 | // Calculate and set physical address 252 | word addr = get_word(reg[do_send]) & 0x0FFF | bas[do_send]; 253 | set_addr(addr); 254 | 255 | // Access to *stream_data is limited both by ... 256 | // - size of received / free space 257 | // - end of physical address space 258 | return MIN(size, lim[do_send] - addr); 259 | } 260 | } 261 | } 262 | 263 | void stream_data_commit(byte do_send, word size) 264 | { 265 | { 266 | static word reg[2] = {0x0428, // Socket 0 RX Read Pointer Register 267 | 0x0424}; // Socket 0 TX Write Pointer Register 268 | set_word(reg[do_send], get_word(reg[do_send]) + size); 269 | } 270 | 271 | { 272 | static byte cmd[2] = {0x40, // Socket Command: RECV 273 | 0x20}; // Socket Command: SEND 274 | // Socket 0 Command Register 275 | set_byte(0x0401, cmd[do_send]); 276 | } 277 | 278 | // Do NOT wait for command completion here, rather 279 | // let W5100 operation overlap with 6502 operation 280 | } 281 | -------------------------------------------------------------------------------- /test/stream3.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2014, Oliver Schmidt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL OLIVER SCHMIDT BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ******************************************************************************/ 29 | 30 | #ifndef _STREAM_H_ 31 | #define _STREAM_H_ 32 | 33 | #define BASE_ADDR 0xC0B4 34 | 35 | typedef unsigned char byte; 36 | typedef unsigned short word; 37 | 38 | word stream_data_request(byte do_send); 39 | void stream_data_commit(byte do_send, word size); 40 | 41 | // After stream_receive_request() every read operation returns the next byte 42 | // from the server. 43 | // After stream_send_request() every write operation prepares the next byte 44 | // to be sent to the server. 45 | #define stream_data ((byte*)(BASE_ADDR + 3)) 46 | 47 | // Initialize W5100 Ethernet controller with indirect bus interface located 48 | // at . Use , and to configure the 49 | // TCP/IP stack. 50 | // Return <1> if a W5100 was found at , return <0> otherwise. 51 | byte stream_init(word base_addr, byte *ip_addr, 52 | byte *submask, 53 | byte *gateway); 54 | 55 | // Connect to server with IP address on TCP port . 56 | // Use <6502> as fixed local port. 57 | // Return <1> if the connection is established, return <0> otherwise. 58 | byte stream_connect(byte *server_addr, word server_port); 59 | 60 | // Check if still connected to server. 61 | // Return <1> if the connection is established, return <0> otherwise. 62 | byte stream_connected(void); 63 | 64 | // Disconnect from server. 65 | void stream_disconnect(void); 66 | 67 | // Request to receive data from the server. 68 | // Return maximum number of bytes to be received by reading from *stream_data. 69 | #define stream_receive_request() stream_data_request(0) 70 | 71 | // Commit receiving of bytes from server. may be smaller than 72 | // the return value of stream_receive_request(). Not commiting at all just 73 | // makes the next request receive the same data again. 74 | #define stream_receive_commit(size) stream_data_commit(0, (size)) 75 | 76 | // Request to send data to the server. 77 | // Return maximum number of bytes to be send by writing to *stream_data. 78 | #define stream_send_request() stream_data_request(1) 79 | 80 | // Commit sending of bytes to server. is usually smaller than 81 | // the return value of stream_send_request(). Not commiting at all just turns 82 | // the stream_send_request() - and the writes to *stream_data - into NOPs. 83 | #define stream_send_commit(size) stream_data_commit(1, (size)) 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | // Both pragmas are obligatory to have cc65 generate code 2 | // suitable to access the W5100 auto-increment registers. 3 | #pragma optimize (on) 4 | #pragma static-locals (on) 5 | 6 | #include 7 | 8 | #ifdef __APPLE2ENH__ 9 | #include "../demo/stream.h" 10 | #else 11 | #include "stream3.h" 12 | #endif 13 | 14 | 15 | #define MIN(a,b) (((a)<(b))?(a):(b)) 16 | 17 | byte ip_addr[4] = {192, 168, 0, 2}; 18 | byte submask[4] = {255, 255, 255, 0}; 19 | byte gateway[4] = {192, 168, 0, 1}; 20 | 21 | byte server[4] = {192, 168, 0, 1}; 22 | 23 | byte buffer[0x1000]; 24 | 25 | void main(void) 26 | { 27 | printf("\aInit\n"); 28 | if (!stream_init(0xC0B4, ip_addr, 29 | submask, 30 | gateway)) 31 | { 32 | printf("No Hardware Found\n"); 33 | return; 34 | } 35 | printf("Connect\n"); 36 | if (!stream_connect(server, 6502)) 37 | { 38 | printf("Faild To Connect To %d.%d.%d.%d\n", server[0], 39 | server[1], 40 | server[2], 41 | server[3]); 42 | return; 43 | } 44 | printf("Connected To %d.%d.%d.%d\n", server[0], 45 | server[1], 46 | server[2], 47 | server[3]); 48 | 49 | for (;;) 50 | { 51 | word rcv, snd, pos, i; 52 | 53 | if (!stream_connected()) 54 | { 55 | printf("Disconnected\n\a"); 56 | return; 57 | } 58 | 59 | if (!(rcv = stream_receive_request())) 60 | { 61 | continue; 62 | } 63 | rcv = MIN(rcv, sizeof(buffer)); 64 | printf("Recv Length: %d\n", rcv); 65 | for (i = 0; i < rcv; ++i) 66 | { 67 | buffer[i] = *stream_data; 68 | } 69 | stream_receive_commit(rcv); 70 | 71 | pos = 0; 72 | while (rcv) 73 | { 74 | while (!(snd = stream_send_request())) 75 | { 76 | printf("Send Window Empty\n"); 77 | } 78 | snd = MIN(snd, rcv); 79 | printf("Send Length: %d\n", snd); 80 | for (i = 0; i < snd; ++i) 81 | { 82 | *stream_data = buffer[pos + i]; 83 | } 84 | stream_send_commit(snd); 85 | pos += snd; 86 | rcv -= snd; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import os, socket, sys, time 2 | 3 | data_len = 100 * 1024 4 | data = os.urandom(data_len) 5 | 6 | serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | serv.bind(('', 6502)) 8 | serv.listen(1) 9 | 10 | while True: 11 | try: 12 | print 'Test Ready' 13 | print >>sys.stderr, 'Listen On Port 6502' 14 | conn, addr = serv.accept() 15 | 16 | print 'Test Started' 17 | print >>sys.stderr, 'Connected To', addr 18 | conn.settimeout(0.001) 19 | 20 | send_pos = recv_pos = 0 21 | watchdog = int(time.time()) 22 | while recv_pos < data_len: 23 | if send_pos < data_len: 24 | try: 25 | sent = conn.send(data[send_pos:]) 26 | except: 27 | pass 28 | else: 29 | print >>sys.stderr, 'Send Length:', sent 30 | send_pos += sent; 31 | watchdog = int(time.time()) 32 | 33 | try: 34 | temp = conn.recv(0x1000) 35 | except: 36 | pass 37 | else: 38 | sys.stdout.write('.') 39 | sys.stdout.flush() 40 | received = len(temp) 41 | print >>sys.stderr, 'Recv Length:', received 42 | assert temp == data[recv_pos:recv_pos+received] 43 | recv_pos += received; 44 | watchdog = int(time.time()) 45 | 46 | assert int(time.time()) < watchdog+2 47 | 48 | except: 49 | print 50 | print 'Test Failure !!!' 51 | else: 52 | print 53 | print 'Test Success' 54 | finally: 55 | conn.close() 56 | print >>sys.stderr, 'Disconnected' 57 | --------------------------------------------------------------------------------