First Commit of my working state
[simh.git] / AltairZ80 / altairz80_net.c
CommitLineData
196ba1fc
PH
1/* altairz80_net.c: networking capability\r
2\r
3 Copyright (c) 2002-2008, Peter Schorn\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Peter Schorn shall not\r
23 be used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Peter Schorn.\r
25*/\r
26\r
27#include "altairz80_defs.h"\r
28#include "sim_sock.h"\r
29extern uint32 PCX;\r
30extern char messageBuffer[];\r
31extern void printMessage(void);\r
32\r
33#define UNIT_V_SERVER (UNIT_V_UF + 0) /* define machine as a server */\r
34#define UNIT_SERVER (1 << UNIT_V_SERVER)\r
35#define NET_INIT_POLL_SERVER 16000\r
36#define NET_INIT_POLL_CLIENT 15000\r
37\r
38static t_stat net_attach (UNIT *uptr, char *cptr);\r
39static t_stat net_detach (UNIT *uptr);\r
40static t_stat net_reset (DEVICE *dptr);\r
41static t_stat net_svc (UNIT *uptr);\r
42static t_stat set_net (UNIT *uptr, int32 value, char *cptr, void *desc);\r
43int32 netStatus (const int32 port, const int32 io, const int32 data);\r
44int32 netData (const int32 port, const int32 io, const int32 data);\r
45\r
46#define MAX_CONNECTIONS 2 /* maximal number of server connections */\r
47#define BUFFER_LENGTH 512 /* length of input and output buffer */\r
48#define NET_ACCEPT 1 /* bit masks for trace_level */\r
49#define NET_DROP 2\r
50#define NET_IN 4\r
51#define NET_OUT 8\r
52static int32 trace_level = 0;\r
53\r
54static struct {\r
55 int32 Z80StatusPort; /* Z80 status port associated with this ioSocket, read only */\r
56 int32 Z80DataPort; /* Z80 data port associated with this ioSocket, read only */\r
57 SOCKET masterSocket; /* server master socket, only defined at [1] */\r
58 SOCKET ioSocket; /* accepted server socket or connected client socket, 0 iff free */\r
59 char inputBuffer[BUFFER_LENGTH]; /* buffer for input characters read from ioSocket */\r
60 int32 inputPosRead; /* position of next character to read from buffer */\r
61 int32 inputPosWrite; /* position of next character to append to input buffer from ioSocket */\r
62 int32 inputSize; /* number of characters in circular input buffer */\r
63 char outputBuffer[BUFFER_LENGTH];/* buffer for output characters to be written to ioSocket */\r
64 int32 outputPosRead; /* position of next character to write to ioSocket */\r
65 int32 outputPosWrite; /* position of next character to append to output buffer */\r
66 int32 outputSize; /* number of characters in circular output buffer */\r
67} serviceDescriptor[MAX_CONNECTIONS+1] = { /* serviceDescriptor[0] holds the information for a client */\r
68/* stat dat ms ios in inPR inPW inS out outPR outPW outS */\r
69 {50, 51, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* client Z80 port 50 and 51 */\r
70 {40, 41, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* server Z80 port 40 and 41 */\r
71 {42, 43, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0} /* server Z80 port 42 and 43 */\r
72};\r
73\r
74static UNIT net_unit = {\r
75 UDATA (&net_svc, UNIT_ATTABLE, 0),\r
76 0, /* wait, set in attach */\r
77 0, /* u3 = Port */\r
78 0, /* u4 = IP of host */\r
79 0, /* u5, unused */\r
80 0, /* u6, unused */\r
81};\r
82\r
83static REG net_reg[] = {\r
84 { DRDATA (POLL, net_unit.wait, 32) },\r
85 { HRDATA (IPHOST, net_unit.u4, 32), REG_RO },\r
86 { DRDATA (PORT, net_unit.u3, 32), REG_RO },\r
87 { HRDATA (TRACELEVEL, trace_level, 32) },\r
88 { NULL }\r
89};\r
90\r
91static MTAB net_mod[] = {\r
92 { UNIT_SERVER, 0, "CLIENT", "CLIENT", &set_net}, /* machine is a client */\r
93 { UNIT_SERVER, UNIT_SERVER, "SERVER", "SERVER", &set_net}, /* machine is a server */\r
94 { 0 }\r
95};\r
96\r
97DEVICE net_dev = {\r
98 "NET", &net_unit, net_reg, net_mod,\r
99 1, 10, 31, 1, 8, 8,\r
100 NULL, NULL, &net_reset,\r
101 NULL, &net_attach, &net_detach,\r
102 NULL, 0, 0,\r
103 NULL, NULL, NULL\r
104};\r
105\r
106static t_stat set_net(UNIT *uptr, int32 value, char *cptr, void *desc) {\r
107 char temp[CBUFSIZE];\r
108 if ((net_unit.flags & UNIT_ATT) && ((net_unit.flags & UNIT_SERVER) != value)) {\r
109 strncpy(temp, net_unit.filename, CBUFSIZE); /* save name for later attach */\r
110 net_detach(&net_unit);\r
111 net_unit.flags ^= UNIT_SERVER; /* now switch from client to server and vice versa */\r
112 net_attach(uptr, temp);\r
113 return SCPE_OK;\r
114 }\r
115 return SCPE_OK;\r
116}\r
117\r
118static void serviceDescriptor_reset(const uint32 i) {\r
119 serviceDescriptor[i].inputPosRead = 0;\r
120 serviceDescriptor[i].inputPosWrite = 0;\r
121 serviceDescriptor[i].inputSize = 0;\r
122 serviceDescriptor[i].outputPosRead = 0;\r
123 serviceDescriptor[i].outputPosWrite = 0;\r
124 serviceDescriptor[i].outputSize = 0;\r
125}\r
126\r
127static t_stat net_reset(DEVICE *dptr) {\r
128 uint32 i;\r
129 if (net_unit.flags & UNIT_ATT)\r
130 sim_activate(&net_unit, net_unit.wait); /* start poll */\r
131 for (i = 0; i <= MAX_CONNECTIONS; i++)\r
132 serviceDescriptor_reset(i);\r
133 return SCPE_OK;\r
134}\r
135\r
136static t_stat net_attach(UNIT *uptr, char *cptr) {\r
137 uint32 i, ipa, ipp;\r
138 t_stat r = get_ipaddr(cptr, &ipa, &ipp);\r
139 if (r != SCPE_OK) return SCPE_ARG;\r
140 if (ipa == 0) ipa = 0x7F000001; /* localhost = 127.0.0.1 */\r
141 if (ipp == 0) ipp = 3000;\r
142 net_unit.u3 = ipp;\r
143 net_unit.u4 = ipa;\r
144 net_reset(&net_dev);\r
145 for (i = 0; i <= MAX_CONNECTIONS; i++) serviceDescriptor[i].ioSocket = 0;\r
146 if (net_unit.flags & UNIT_SERVER) {\r
147 net_unit.wait = NET_INIT_POLL_SERVER;\r
148 serviceDescriptor[1].masterSocket = sim_master_sock(ipp);\r
149 if (serviceDescriptor[1].masterSocket == INVALID_SOCKET) return SCPE_IOERR;\r
150 }\r
151 else {\r
152 net_unit.wait = NET_INIT_POLL_CLIENT;\r
153 serviceDescriptor[0].ioSocket = sim_connect_sock(ipa, ipp);\r
154 if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR;\r
155 }\r
156 net_unit.flags |= UNIT_ATT;\r
157 net_unit.filename = (char *) calloc(CBUFSIZE, sizeof (char)); /* alloc name buf */\r
158 if (net_unit.filename == NULL) return SCPE_MEM;\r
159 strncpy(net_unit.filename, cptr, CBUFSIZE); /* save name */\r
160 return SCPE_OK;\r
161}\r
162\r
163static t_stat net_detach(UNIT *uptr) {\r
164 uint32 i;\r
165 if (!(net_unit.flags & UNIT_ATT)) return SCPE_OK; /* if not attached simply return */\r
166 if (net_unit.flags & UNIT_SERVER)\r
167 sim_close_sock(serviceDescriptor[1].masterSocket, TRUE);\r
168 for (i = 0; i <= MAX_CONNECTIONS; i++)\r
169 if (serviceDescriptor[i].ioSocket)\r
170 sim_close_sock(serviceDescriptor[i].ioSocket, FALSE);\r
171 free(net_unit.filename); /* free port string */\r
172 net_unit.filename = NULL;\r
173 net_unit.flags &= ~UNIT_ATT; /* not attached */\r
174 return SCPE_OK;\r
175}\r
176\r
177/* cannot use sim_check_conn to check whether read will return an error */\r
178static t_stat net_svc(UNIT *uptr) {\r
179 int32 i, j, k, r;\r
180 SOCKET s;\r
181 static char svcBuffer[BUFFER_LENGTH];\r
182 if (net_unit.flags & UNIT_ATT) { /* cannot remove due to following else */\r
183 sim_activate(&net_unit, net_unit.wait); /* continue poll */\r
184 if (net_unit.flags & UNIT_SERVER) {\r
185 for (i = 1; i <= MAX_CONNECTIONS; i++)\r
186 if (serviceDescriptor[i].ioSocket == 0) {\r
187 s = sim_accept_conn(serviceDescriptor[1].masterSocket, NULL);\r
188 if (s != INVALID_SOCKET) {\r
189 serviceDescriptor[i].ioSocket = s;\r
190 if (trace_level & NET_ACCEPT) {\r
191 MESSAGE_3("Accepted connection %i with socket %i.", i, s);\r
192 }\r
193 }\r
194 }\r
195 }\r
196 else if (serviceDescriptor[0].ioSocket == 0) {\r
197 serviceDescriptor[0].ioSocket = sim_connect_sock(net_unit.u4, net_unit.u3);\r
198 if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR;\r
199 printf("\rWaiting for server ... Type g<return> (possibly twice) when ready" NLP);\r
200 return SCPE_STOP;\r
201 }\r
202 for (i = 0; i <= MAX_CONNECTIONS; i++)\r
203 if (serviceDescriptor[i].ioSocket) {\r
204 if (serviceDescriptor[i].inputSize < BUFFER_LENGTH) { /* there is space left in inputBuffer */\r
205 r = sim_read_sock(serviceDescriptor[i].ioSocket, svcBuffer,\r
206 BUFFER_LENGTH - serviceDescriptor[i].inputSize);\r
207 if (r == -1) {\r
208 if (trace_level & NET_DROP) {\r
209 MESSAGE_3("Drop connection %i with socket %i.", i, serviceDescriptor[i].ioSocket);\r
210 }\r
211 sim_close_sock(serviceDescriptor[i].ioSocket, FALSE);\r
212 serviceDescriptor[i].ioSocket = 0;\r
213 serviceDescriptor_reset(i);\r
214 continue;\r
215 }\r
216 else {\r
217 for (j = 0; j < r; j++) {\r
218 serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosWrite++] = svcBuffer[j];\r
219 if (serviceDescriptor[i].inputPosWrite == BUFFER_LENGTH)\r
220 serviceDescriptor[i].inputPosWrite = 0;\r
221 }\r
222 serviceDescriptor[i].inputSize += r;\r
223 }\r
224 }\r
225 if (serviceDescriptor[i].outputSize > 0) { /* there is something to write in outputBuffer */\r
226 k = serviceDescriptor[i].outputPosRead;\r
227 for (j = 0; j < serviceDescriptor[i].outputSize; j++) {\r
228 svcBuffer[j] = serviceDescriptor[i].outputBuffer[k++];\r
229 if (k == BUFFER_LENGTH) k = 0;\r
230 }\r
231 r = sim_write_sock(serviceDescriptor[i].ioSocket, svcBuffer, serviceDescriptor[i].outputSize);\r
232 if (r >= 0) {\r
233 serviceDescriptor[i].outputSize -= r;\r
234 serviceDescriptor[i].outputPosRead += r;\r
235 if (serviceDescriptor[i].outputPosRead >= BUFFER_LENGTH)\r
236 serviceDescriptor[i].outputPosRead -= BUFFER_LENGTH;\r
237 }\r
238 else printf("write %i" NLP, r);\r
239 }\r
240 }\r
241 }\r
242 return SCPE_OK;\r
243}\r
244\r
245int32 netStatus(const int32 port, const int32 io, const int32 data) {\r
246 uint32 i;\r
247 if ((net_unit.flags & UNIT_ATT) == 0) return 0;\r
248 net_svc(&net_unit);\r
249 if (io == 0) /* IN */\r
250 for (i = 0; i <= MAX_CONNECTIONS; i++)\r
251 if (serviceDescriptor[i].Z80StatusPort == port)\r
252 return (serviceDescriptor[i].inputSize > 0 ? 1 : 0) |\r
253 (serviceDescriptor[i].outputSize < BUFFER_LENGTH ? 2 : 0);\r
254 return 0;\r
255}\r
256\r
257int32 netData(const int32 port, const int32 io, const int32 data) {\r
258 uint32 i;\r
259 char result;\r
260 if ((net_unit.flags & UNIT_ATT) == 0) return 0;\r
261 net_svc(&net_unit);\r
262 for (i = 0; i <= MAX_CONNECTIONS; i++)\r
263 if (serviceDescriptor[i].Z80DataPort == port)\r
264 if (io == 0) { /* IN */\r
265 if (serviceDescriptor[i].inputSize == 0) {\r
266 printf("re-read from %i" NLP, port);\r
267 result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead > 0 ?\r
268 serviceDescriptor[i].inputPosRead - 1 : BUFFER_LENGTH - 1];\r
269 }\r
270 else {\r
271 result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead++];\r
272 if (serviceDescriptor[i].inputPosRead == BUFFER_LENGTH)\r
273 serviceDescriptor[i].inputPosRead = 0;\r
274 serviceDescriptor[i].inputSize--;\r
275 }\r
276 if (trace_level & NET_IN) {\r
277 MESSAGE_4(" IN(%i)=%03xh (%c)", port, (result & 0xff),\r
278 (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?');\r
279 }\r
280 return result;\r
281 }\r
282 else { /* OUT */\r
283 if (serviceDescriptor[i].outputSize == BUFFER_LENGTH) {\r
284 printf("over-write %i to %i" NLP, data, port);\r
285 serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite > 0 ?\r
286 serviceDescriptor[i].outputPosWrite - 1 : BUFFER_LENGTH - 1] = data;\r
287 }\r
288 else {\r
289 serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite++] = data;\r
290 if (serviceDescriptor[i].outputPosWrite== BUFFER_LENGTH)\r
291 serviceDescriptor[i].outputPosWrite = 0;\r
292 serviceDescriptor[i].outputSize++;\r
293 }\r
294 if (trace_level & NET_OUT) {\r
295 MESSAGE_4("OUT(%i)=%03xh (%c)", port, data,\r
296 (32 <= data) && (data <= 127) ? data : '?');\r
297 }\r
298 return 0;\r
299 }\r
300 return 0;\r
301}\r