GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
console_socket.c
Go to the documentation of this file.
1#include <sys/socket.h>
2#include <sys/un.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <string.h>
6#include <stdint.h>
7#include <stdlib.h>
8
9#include "console_socket.h"
10#include "ge.h"
11#include "console.h"
12#include "log.h"
13
14static const char socket_path[] = "/tmp/gemu.console";
15static int console_socket_fd = -1;
16
17/* Momentary keys are edge-triggered; this remembers the last frame's word. */
18static uint16_t console_prev_buttons;
19
20static int console_socket_init(struct ge *ge, void *ctx)
21{
22 int sd;
23 struct sockaddr_un sock;
24 (void)ge;
25 (void)ctx;
26 unlink(socket_path);
27 memset(&sock, 0, sizeof(sock));
28 sock.sun_family = AF_UNIX;
29 strcpy(sock.sun_path, socket_path);
30 sd = socket(AF_UNIX, SOCK_DGRAM, 0);
31 if (sd < 0)
32 return sd;
33 fcntl(sd, F_SETFL, O_NONBLOCK);
34 if (bind(sd, (struct sockaddr *)&sock, sizeof(sock)) != 0) {
35 close(sd);
36 return -1;
37 }
38
40 console_prev_buttons = 0; /* a fresh session starts with no key down */
41 return 0;
42}
43
44static int console_socket_deinit(struct ge *ge, void *ctx)
45{
46 (void)ge;
47 (void)ctx;
48 if (console_socket_fd >= 0) {
49 close(console_socket_fd);
51 }
52 unlink(socket_path);
53 return 0;
54}
55
56
57/*
58 * The wire format of an inbound console frame is `struct ge_console` as far as
59 * the client fills it in: the lamp block is write-only from the machine's side
60 * and arrives zeroed, then the switches, the momentary buttons, and the rotary
61 * position. Only the last three are read here.
62 *
63 * bytes 0..11 lamps (ignored on input)
64 * bytes 12..13 switch flags (PAPA, PATE, RICI, ACOV, ACON, INAR, STOC,
65 * INCE, SITE, lamps_on)
66 * bytes 14..15 AM forcing register
67 * bytes 16..17 momentary buttons (see console.h ge_console_buttons)
68 * byte 18 rotary register selector
69 *
70 * The client packs the rotary as a single byte, not as the C enum's four, so
71 * the frame is 19 bytes and the rotary is read as a byte.
72 */
73#define CONSOLE_FRAME_BYTES 19
74#define CONSOLE_OFF_SWITCHES 12
75#define CONSOLE_OFF_BUTTONS 16
76#define CONSOLE_OFF_ROTARY 18
77
78/* Bit positions within the buttons word, in ge_console_buttons field order. */
79enum {
84};
85
86/*
87 * Momentary keys are edge-triggered: a key does something when it goes down,
88 * not for as long as a client keeps reporting it held. (A client that clears
89 * its button word after each frame gets the same behaviour either way.)
90 */
91static void console_press_buttons(struct ge *ge, uint16_t buttons)
92{
93 uint16_t edge = (uint16_t)(buttons & ~console_prev_buttons);
94
95 console_prev_buttons = buttons;
96
97 /* LAMPS CHECK shares the MAINT ON button and is the one control here that
98 * is not edge-triggered: it is a bulb test that lasts exactly as long as
99 * the key is held, so it follows the level. */
100 ge->lamps_test = (buttons >> BTN_MAINTENANCE_ON) & 1u;
101
102 if (!edge)
103 return;
104
105 /* CLEAR: stop the subsystem and preset it. Note this does NOT clear core --
106 * see ge_clear. */
107 if (edge & (1u << BTN_CLEAR)) {
108 ge_log(LOG_CONSOLE, "console: CLEAR\n");
109 ge_clear(ge);
110 }
111
112 /* LOAD1/LOAD2: pick which of the two install-time load units the bootstrap
113 * will read. One key on the panel, alternating between the two lamps. */
114 if (edge & (1u << BTN_LOAD_1_2)) {
115 if (ge->ALOI)
116 ge_load_2(ge);
117 else
118 ge_load_1(ge);
119 ge_log(LOG_CONSOLE, "console: LOAD%d selected\n", ge->ALOI ? 1 : 2);
120 }
121
122 /* LOAD: arms AINI and NOTHING else. No card moves until START. */
123 if (edge & (1u << BTN_LOAD)) {
124 ge_log(LOG_CONSOLE, "console: LOAD (AINI armed; START will read one card)\n");
125 ge_load(ge);
126 }
127
128 /* START (HALT): the same key starts a stopped machine and stops a running
129 * one. The white HALT lamp says which state you are in. */
130 if (edge & (1u << BTN_HALT_START)) {
131 if (ge->ALTO) {
132 ge_log(LOG_CONSOLE, "console: START\n");
133 /* Off NORM this performs the one maintenance cycle the key is worth
134 * and leaves the machine stopped; the caller's run loop must not
135 * turn it further (console.c). */
137 } else {
138 ge_log(LOG_CONSOLE, "console: HALT\n");
139 ge->ALTO = 1;
140 }
141 }
142
143 /* The two program-readable switches (JS1 / JS2). */
144 if (edge & (1u << BTN_SWITCH_1))
145 ge->JS1 = !ge->JS1;
146 if (edge & (1u << BTN_SWITCH_2))
147 ge->JS2 = !ge->JS2;
148
149 /* STEP-BY-STEP is the operator panel's own switch (ASIN), a separate circuit
150 * from the maintenance PAPA and with its own lamp -- so it is a key here,
151 * and it owns its own state. It stops the machine at each instruction and
152 * can be inhibited by the program (INS/ENS), which PAPA cannot. */
153 if (edge & (1u << BTN_STEP_BY_STEP))
154 ge->ASIN = !ge->ASIN;
155
156 if (edge & (1u << BTN_POWER_ON))
157 ge->powered = 1;
158 if (edge & (1u << BTN_EMERGEN_OFF))
159 ge->powered = 0;
160}
161
162static int console_socket_check(struct ge *ge, void *ctx)
163{
164 unsigned char buf[1024];
165 struct sockaddr_un dst;
166 ssize_t ret;
167 socklen_t socket_size = sizeof(struct sockaddr_un);
168 struct ge_console console;
169
170 (void)ctx;
171
172 if (console_socket_fd < 0) {
173 return -1;
174 }
175
176 ret = recvfrom(console_socket_fd, buf, sizeof(buf), 0,
177 (struct sockaddr *)&dst, &socket_size);
178 if (ret > 0) {
179 if (ret >= CONSOLE_FRAME_BYTES) {
180 struct ge_console_switches sw;
181 uint16_t buttons;
182 unsigned char rotary;
183
184 memcpy(&sw, buf + CONSOLE_OFF_SWITCHES, sizeof(sw));
185 memcpy(&buttons, buf + CONSOLE_OFF_BUTTONS, sizeof(buttons));
186 rotary = buf[CONSOLE_OFF_ROTARY];
187
189 if (rotary <= RS_FO)
191 console_press_buttons(ge, buttons);
192 } else {
194 "console: short frame (%zd bytes, want %d); ignoring input\n",
196 }
197
198 /* Answer with the lamps as they stand AFTER the keys were acted on, so
199 * a CLEAR or a START is visible in the very frame that requested it. */
200 ge_fill_console_data(ge, &console);
201 sendto(console_socket_fd, (unsigned char *)(&console),
202 sizeof(struct ge_console), 0, (struct sockaddr *)&dst,
203 socket_size);
204 }
205
206 return 0;
207}
208
209static struct ge_peri console_socket = {
211 .on_pulse = console_socket_check,
212 .deinit = console_socket_deinit,
213};
214
216{
218}
void ge_fill_console_data(struct ge *ge, struct ge_console *console)
Definition console.c:9
void ge_set_console_switches(struct ge *ge, struct ge_console_switches *switches)
Definition console.c:99
int ge_console_start(struct ge *ge)
Definition console.c:138
void ge_set_console_rotary(struct ge *ge, enum ge_console_rotary rs)
Definition console.c:112
ge_console_rotary
Definition console.h:8
@ RS_FO
Definition console.h:22
@ BTN_STEP_BY_STEP
@ BTN_DC_ALERT
@ BTN_AC_ON
@ BTN_HALT_START
@ BTN_MEM_CHECK
@ BTN_POWER_ON
@ BTN_SWITCH_2
@ BTN_EMERGEN_OFF
@ BTN_STANDBY
@ BTN_CLEAR
@ BTN_PAD
@ BTN_MAINTENANCE_ON
@ BTN_SWITCH_1
@ BTN_OPER_CALL
@ BTN_LOAD_1_2
@ BTN_LOAD
static void console_press_buttons(struct ge *ge, uint16_t buttons)
static int console_socket_fd
#define CONSOLE_OFF_BUTTONS
static int console_socket_deinit(struct ge *ge, void *ctx)
#define CONSOLE_OFF_SWITCHES
static int console_socket_check(struct ge *ge, void *ctx)
#define CONSOLE_FRAME_BYTES
static const char socket_path[]
static uint16_t console_prev_buttons
static int console_socket_init(struct ge *ge, void *ctx)
#define CONSOLE_OFF_ROTARY
static struct ge_peri console_socket
int console_socket_register(struct ge *ge)
void ge_load_1(struct ge *ge)
Emulate the press of the "load 1" button in the console.
Definition ge.c:372
void ge_clear(struct ge *ge)
Emulate the press of the "clear" button in the console.
Definition ge.c:137
void ge_load_2(struct ge *ge)
Emulate the press of the "load 2" button in the console.
Definition ge.c:389
void ge_load(struct ge *ge)
Emulate the press of the "load" button in the console.
Definition ge.c:362
int ge_register_peri(struct ge *ge, struct ge_peri *p)
void ge_log(ge_log_type type, const char *format,...)
Log message.
Definition log.c:122
@ LOG_CONSOLE
Console interactions.
Definition log.h:26
Console switches.
Definition console.h:71
Definition ge.h:882
int(* init)(struct ge *, void *)
Definition ge.h:884
The entire state of the emulated system, including registers, memory, peripherals and timings.
Definition ge.h:172
uint8_t ALOI
Load connector selection.
Definition ge.h:384
uint8_t JS2
Console jump condition 2.
Definition ge.h:496
uint8_t ALTO
Stops internal cycles.
Definition ge.h:392
uint8_t lamps_test
LAMPS CHECK held (CPU[4] ยง3.2).
Definition ge.h:491
uint8_t powered
Definition ge.h:175
uint8_t ASIN
STEP-BY-STEP, the OPERATOR panel switch (signal ASIN).
Definition ge.h:477
uint8_t JS1
Console jump condition 1.
Definition ge.h:495