-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSocketAPI.c
500 lines (478 loc) · 15.5 KB
/
SocketAPI.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
/*
Copyright 2003-2005,2009 Ronald S. Burkey <[email protected]>
This file is part of yaAGC.
yaAGC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
yaAGC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with yaAGC; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
In addition, as a special exception, Ronald S. Burkey gives permission to
link the code of this program with the Orbiter SDK library (or with
modified versions of the Orbiter SDK library that use the same license as
the Orbiter SDK library), and distribute linked combinations including
the two. You must obey the GNU General Public License in all respects for
all of the code used other than the Orbiter SDK library. If you modify
this file, you may extend this exception to your version of the file,
but you are not obligated to do so. If you do not wish to do so, delete
this exception statement from your version.
Filename: SocketAPI.c
Purpose: This is an implementation of yaAGC-to-peripheral
communications by means of a socket interface. If some
other means of communications is desired, such as a
memory-mapped i/o-channel interface, just replace the
functions in this file with alternate functions.
Compiler: GNU gcc.
Contact: Ron Burkey <[email protected]>
Reference: http://www.ibiblio.org/apollo/index.html
Mods: 08/18/04 RSB. Split off from agc_engine.c.
02/27/05 RSB Added the license exception, as required by
the GPL, for linking to Orbiter SDK libraries.
05/14/05 RSB Corrected website references.
05/15/05 RSB Fixed bug in which counter increments
would endlessly recur. SOCKET_API_C added
for agc_engine.h.
05/31/05 RSB --debug-deda mode.
06/05/05 RSB Corrected polarity of discretes in
--debug-deda mode. Corrected positioning
of bits in the "input discretes".
06/07/05 RSB Fixed bit positioning in DEDA shift
register.
06/26/05 RSB Accounted for uplinked data.
07/09/05 RSB Accounted for rotational hand controller (RHC)
in LM. In retrospect, this is a bad place
to handle it, since it won't automatically
be handled in NullAPI.c. I'll worry about
that later.
03/11/09 RSB Added casts of various char types (unsigned
char, char, const char) to other char types
for function parameters. Without these,
there were fatal compilation errors with
some versions of g++.
03/18/09 RSB Removed printed messages about adding/removing
sockets when the DebugMode flag is set.
Added some robustness checking to the
data stream on the tcp port.
03/19/09 RSB Added DedaQuiet.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
typedef unsigned short uint16_t;
#endif
#include "yaAGC.h"
#define SOCKET_API_C
#include "agc_engine.h"
//-----------------------------------------------------------------------------
// Function for broadcasting "output channel" data to all connected clients of
// yaAGC.
void
ChannelOutput (agc_t * State, int Channel, int Value)
{
int i, j;
//int n;
Client_t *Client;
unsigned char Packet[4];
extern int DebugMode;
// Some output channels have purposes within the CPU, so we have to
// account for those separately.
if (Channel == 7)
{
State->InputChannel[7] = State->OutputChannel7 = (Value & 0160);
return;
}
// Stick data into the RHCCTR registers, if bits 8,9 of channel 013 are set.
if (Channel == 013 && 0600 == (0600 & Value) && !CmOrLm)
{
State->Erasable[0][042] = LastRhcPitch;
State->Erasable[0][043] = LastRhcYaw;
State->Erasable[0][044] = LastRhcRoll;
}
// Most output channels are simply transmitted to clients representing
// hardware simulations.
if (FormIoPacket (Channel, Value, Packet))
return;
for (i = 0, Client = Clients; i < MAX_CLIENTS; i++, Client++)
if (Clients[i].Socket != -1)
{
j = send (Client->Socket, (const char *) Packet, 4, MSG_NOSIGNAL);
if (j == SOCKET_ERROR && SOCKET_BROKEN)
{
if (!DebugMode)
printf ("Removing socket %d\n", Clients[i].Socket);
#ifdef unix
close (Clients[i].Socket);
#else
closesocket (Clients[i].Socket);
#endif
Clients[i].Socket = -1;
}
}
}
//----------------------------------------------------------------------
// A function for handling reception of "input channel" data from
// any connected client. At most one channel of input is handled per
// call. The function returns 0 if there is no input, or non-zero if
// there is input. If the input causes an interrupt (as, for example,
// a keystroke from the DSKY), then the function should set the
// appropriate request in State->InterruptRequests[].
//
// The function also handles unprogrammed counter increments caused
// by inputs to the CPU. The function returns 1 if a counter-increment
// was performed, and returns zero otherwise.
static int CurrentChannelValues[256] = { 0 };
static const unsigned char Signatures[4] = { 0x00, 0x40, 0x80, 0xC0 };
static const unsigned char SignaturesAgs[4] = { 0x00, 0xC0, 0x80, 0x40 };
int
ChannelInput (agc_t *State)
{
static int SocketInterlace = 0;
int i, j, k;
unsigned char c;
Client_t *Client;
int Channel, Value;
//We use SocketInterlace to slow down the number
// of polls of the sockets.
if (SocketInterlace > 0)
SocketInterlace--;
else
{
SocketInterlace = SocketInterlaceReload;
for (i = 0, Client = Clients; i < MAX_CLIENTS; i++, Client++)
if (Client->Socket != -1)
{
// We arbitrarily adopt the rule that we'll process at most one
// packet per client per instruction cycle.
for (j = Client->Size; j < 4; j++)
{
k = recv (Client->Socket, (char *) &c, 1, 0);
if (k == 0 || k == -1)
break;
// 20090318 RSB. Added this filter for a little robustness,
// but it shouldn't be needed.
if (!(
(Signatures[j] == (c & 0xC0)) ||
(DebugDeda && (SignaturesAgs[j] == (c & 0xC0)))
)
)
{
Client->Size = 0;
if (0 != (c & 0xC0))
{
j = -1;
continue;
}
j = 0;
}
Client->Packet[Client->Size++] = c;
}
// Process a received packet.
if (Client->Size >= 4)
{
int uBit, Type, Data;
//printf ("Received from %d: %02X %02X %02X %02X\n",
// i, Client->Packet[0], Client->Packet[1],
// Client->Packet[2], Client->Packet[3]);
if (!ParseIoPacket (Client->Packet, &Channel, &Value, &uBit))
{
// Convert to AGC format (upper 15 bits).
Value &= 077777;
if (uBit)
{
Client->ChannelMasks[Channel] = Value;
}
else if (Channel & 0x80)
{
// In this case we're dealing with a counter increment.
// So increment the counter.
//printf ("Channel=%02o Int=%o\n", Channel, Value);
UnprogrammedIncrement (State, Channel, Value);
Client->Size = 0;
return (1);
}
else
{
Value &= Client->ChannelMasks[Channel];
Value |=
ReadIO (State,
Channel) & ~Client->ChannelMasks[Channel];
WriteIO (State, Channel, Value);
// If this is a keystroke from the DSKY, generate an interrupt req.
if (Channel == 015)
State->InterruptRequests[5] = 1;
// If this is on fictitious input channel 0173, then the data
// should be placed in the INLINK counter register, and an
// UPRUPT interrupt request should be set.
else if (Channel == 0173)
{
State->Erasable[0][RegINLINK] = (Value & 077777);
State->InterruptRequests[7] = 1;
}
// Fictitious registers for rotational hand controller (RHC).
// Note that the RHC angles are not immediately used, but
// merely squirreled away for later. They won't actually
// go into the counter registers until the RHC counters are
// enabled and the data requested (bits 8,9 of channel 13).
else if (Channel == 0166)
{
LastRhcPitch = Value;
ChannelOutput (State, Channel, Value); // echo
}
else if (Channel == 0167)
{
LastRhcYaw = Value;
ChannelOutput (State, Channel, Value); // echo
}
else if (Channel == 0170)
{
LastRhcRoll = Value;
ChannelOutput (State, Channel, Value); // echo
}
else if (Channel == 031)
{
static int LastInDetent = 040000;
int InDetent;
ChannelOutput (State, Channel, Value);
// If the RHC stick has moved out of detent,
// generate a RUPT10 interrupt.
InDetent = (040000 & Value);
if (LastInDetent && !InDetent)
State->InterruptRequests[10] = 1;
LastInDetent = InDetent;
}
//---------------------------------------------------------------
// For --debug-dsky mode.
if (DebugDsky)
{
if (Channel == 032)
{
// For DebugDsky purposes only, the PRO key is translated
// to appear as the otherwise-fictitious KeyCode 0.
if (0 != (Value & 020000))
{
Channel = 015;
Value = 0;
}
}
if (Channel == 015)
{
int i, CurrentValue;
Value &= 077777;
for (i = 0; i < NumDebugRules; i++)
if (Value == DebugRules[i].KeyCode)
{
CurrentValue =
CurrentChannelValues[DebugRules[i].
Channel];
switch (DebugRules[i].Logic)
{
case '=':
CurrentValue = DebugRules[i].Value;
break;
case '|':
CurrentValue |= DebugRules[i].Value;
break;
case '&':
CurrentValue &= DebugRules[i].Value;
break;
case '^':
CurrentValue ^= DebugRules[i].Value;
break;
default:
break;
}
CurrentChannelValues[DebugRules[i].
Channel] =
CurrentValue;
ChannelOutput (State,
DebugRules[i].Channel,
CurrentValue);
}
}
//if (Channel != 032 && Channel != 015)
// WriteIO (State, Channel, Value);
}
//---------------------------------------------------------------
}
}
else if (DebugDeda && !ParseIoPacketAGS (Client->Packet, &Type, &Data))
{
// The following code is present only for debugging yaDEDA
// communications, and has no interesting purpose yaAGC-wise.
static unsigned char Buffer[9];
static int NumInBuffer = 0, NumWanted = 0, Collecting = 0;
static unsigned char Packet[4];
if (Type == 05 && (Data == 0777002 || Data == 0777004 ||
Data == 0777010 || Data == 0777020))
printf ("DEDA key release.\n");
else if (Collecting && Type == 07)
{
Buffer[NumInBuffer++] = Data >> 13;
if (NumInBuffer < NumWanted)
send (Client->Socket, (const char *) Packet, 4, 0);
else
{
int i;
Collecting = 0;
printf ("Received %d DEDA nibbles:", NumWanted);
for (i = 0; i < NumWanted; i++)
printf (" %1X", Buffer[i]);
printf ("\n");
if (NumWanted == 3)
{
if (!DedaQuiet)
DedaMonitor = 1;
DedaAddress = Buffer[0] * 0100 + Buffer[1] * 010 + Buffer[2];
DedaWhen = State->CycleCounter;
}
}
}
else if (Type == 05 && (Data == 0775002 || Data == 0773004))
{
NumInBuffer = 0;
Collecting = 1;
if (Data == 0775002)
{
printf ("Received DEDA READOUT.\n");
NumWanted = 3;
}
else
{
printf ("Received DEDA ENTR.\n");
NumWanted = 9;
}
FormIoPacketAGS (040, ~010, Packet);
send (Client->Socket, (const char *) Packet, 4, 0);
}
else if (Type == 05 && Data == 0767010)
{
printf ("Received DEDA HOLD.\n");
DedaMonitor = 0;
}
else if (Type == 05 && Data == 0757020)
{
printf ("Received DEDA CLR.\n");
DedaMonitor = 0;
}
else
printf ("Unknown AGS packet %02X %02X %02X %02X\n",
Client->Packet[0], Client->Packet[1],
Client->Packet[2], Client->Packet[3]);
}
Client->Size = 0;
}
}
}
return (0);
}
//----------------------------------------------------------------------
// A generic function for handling client connects/disconnects.
// The input parameters are the CPU-dependent state (may be an agc_t
// or an aea_t, for example) and a function which will update a
// newly-connected peripheral with up-to-date CPU output signals.
void
ChannelRoutineGeneric (void *State, void (*UpdatePeripherals) (void *, Client_t *))
{
static int TimeoutCount = 0;
int i, j;
Client_t *Client;
extern int DebugMode;
// Initialize the server sockets, if needed.
if (NumServers == 0)
{
for (; NumServers < MAX_CLIENTS; NumServers++)
{
Clients[NumServers].Socket = -1;
ServerSockets[NumServers] =
EstablishSocket (Portnum + NumServers, 3);
}
}
for (i = 0, Client = Clients; i < MAX_CLIENTS; i++, Client++)
if (ServerSockets[i] != -1 && Client->Socket == -1)
{
// Get new clients.
Client->Socket = accept (ServerSockets[i], NULL, NULL);
if (Client->Socket != -1)
{
int ii;
extern int DebugMode;
UnblockSocket (Client->Socket);
if (!DebugMode)
printf ("Adding socket %d on port %d\n", Client->Socket,
Portnum + i);
Client->Size = 0;
for (ii = 0; ii < 256; ii++)
Client->ChannelMasks[ii] = 077777;
// Now that a new connection has been made, update the
// client with all current output-port values. Values of
// zero are treated as the default, and are not transmitted.
(*UpdatePeripherals) (State, Client);
}
else if (errno == EMFILE)
printf ("File-descriptor limit reached.\n");
}
// Do a sort of timeout check for missing clients.
TimeoutCount++;
if (0 == (017 & TimeoutCount))
{
unsigned char c = 0377;
for (i = 0, Client = Clients; i < MAX_CLIENTS; i++, Client++)
if (Clients[i].Socket != -1)
{
j = send (Client->Socket, (const char *) &c, 1, MSG_NOSIGNAL);
if (j == SOCKET_ERROR && SOCKET_BROKEN)
{
if (!DebugMode)
printf ("Removing socket %d\n", Clients[i].Socket);
#ifdef unix
close (Clients[i].Socket);
#else
closesocket (Clients[i].Socket);
#endif
Client->Socket = -1;
}
}
}
}
static void
UpdateAgcPeripheralConnect (void *AgcState, Client_t *Client)
{
#define State ((agc_t *) AgcState)
int i;
unsigned char Packet[4];
for (i = 0; i < 16; i++)
if (State->OutputChannel10[i] != 0) {
FormIoPacket (010, State->OutputChannel10[i], Packet);
send (Client->Socket, (const char *) Packet, 4, MSG_NOSIGNAL);
}
for (i = 011; i < 0200; i++)
if (State->InputChannel[i] != 0) {
FormIoPacket (i, State->InputChannel[i], Packet);
send (Client->Socket, (const char *) Packet, 4, MSG_NOSIGNAL);
}
#undef State
}
void
ChannelRoutine (agc_t *State)
{
ChannelRoutineGeneric (State, UpdateAgcPeripheralConnect);
}
//----------------------------------------------------------------------
// This function is useful for debugging the yaDEDA socket interface. It
// forms a packet for the DEDA shift register.
void
ShiftToDeda (agc_t *State, int Data)
{
unsigned char Packet[4];
int i;
Client_t *Client;
FormIoPacketAGS (027, Data << 13, Packet);
for (i = 0, Client = Clients; i < MAX_CLIENTS; i++, Client++)
send (Client->Socket, (const char *) Packet, 4, MSG_NOSIGNAL);
}