GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
printer.c
Go to the documentation of this file.
1/*
2 * printer.c - Integrated printer / console-typewriter peripheral (channel 2).
3 *
4 * See printer.h for the model rationale. Summary of the completion mechanism,
5 * derived empirically against the funktionalcpu report_and_end PER (0x19D0,
6 * `PER 0x00,0x19EE`) and the channel-2 / TPER flow charts:
7 *
8 * A channel-2 print/typewriter PER runs the CPU through the external-operation
9 * organisation phase (c8 d8 d9 .. a8 a9 aa ab) and parks it at state b8, the
10 * org-phase external request-wait. There the CPU suspends: RC00 drops, so the
11 * NA knot yields 0 (idle) and rSA stays 0 while rSO holds b8 pending. gemu has
12 * no channel-2 timing model to satisfy the wait, so it hangs.
13 *
14 * The faithful exit is gated on DU97 = PUC2 ^ L2.3 (signals.h). With PUC2
15 * asserted (and RC00 raised so NA_knot routes rSO=b8 into rSA), state_b8's own
16 * CU01/CU13/CU14/CU06 commands build the PER-completion future_state and the
17 * sequencer returns to alpha at the post-PER instruction with PO/registers
18 * intact (verified: it then runs the real final-verify CMC at 0x19d4 cleanly).
19 *
20 * Discriminating the channel-2 print-wait from the channel-1 reader input-wait:
21 * State b8 is SHARED with the card reader (its inter-byte "waiting for next
22 * column" gap looks identical: rSO=b8, rSA=0, RASI=1, lu08=0, RC00=0). The
23 * reader peripheral presents a byte within a couple of cycles, breaking the
24 * wait; a genuine channel-2 print-wait persists. We therefore debounce with a
25 * stall counter: only after the wait has held unbroken for STALL_THRESHOLD
26 * on_clock calls do we treat it as a printer op and complete it. The reader's
27 * inter-byte gap never approaches that, so a concurrently-registered reader is
28 * never disturbed.
29 */
30
31#include "printer.h"
32
33#include <stdlib.h>
34#include <string.h>
35
36#include "log.h"
37#include "gecode.h"
38
39/* Consecutive idle b8-wait cycles before we conclude it is a channel-2 print
40 * op and not a reader inter-byte gap. The reader presents within a few cycles;
41 * 256 is comfortably above any inter-byte gap yet imperceptible to the user. */
42#define STALL_THRESHOLD 256
43
45 int stall; /* consecutive idle channel-2 wait cycles */
46 int per_pending; /* a channel-2 PER org-phase is in progress */
47 uint16_t per_base; /* its order-block base (rV1 at state c8) */
48 struct ge_peri peri;
49};
50
51/* A channel-2 PER order block is {z, cmd, len_hi, len_lo, buf_hi, buf_lo}
52 * (perperi-validated). A put/print command has bit 7 set; a length beyond a
53 * print line is taken as a control PER (e.g. funktionalcpu's banner, whose
54 * 2-byte order overlaps the following code) and ignored. */
55#define PRINT_CMD_IS_PUT(cmd) ((cmd) & 0x80)
56#define PRINT_LEN_MAX 256
57#define KBD_CMD_LINE 0x40
58#define KBD_CMD_CHAR 0x41
59/* Line printer (LP/PRT, connector 2): the WRITE/print order is the same 6-byte
60 * block {z, cmd, len_hi, len_lo, buf_hi, buf_lo} as the console typewriter, but
61 * with command byte 0x42 (WRITE) instead of the typewriter's PUT (bit 7 set) —
62 * verified from printermechanicaltest.cap (order {51 42 00 9F 02 02} prints 159
63 * bytes from 0x0202) and the PRT mechanic-test listing ("X'C0',WRITE"). Unlike
64 * the typewriter output it parks the CPU at the b8 external request-wait with
65 * RC00 still set, so it is released there (like an input order) and the actual
66 * transfer is armed when the PER returns to alpha. */
67#define LP_CMD_WRITE 0x42
68#define IS_OUTPUT_CMD(cmd) (PRINT_CMD_IS_PUT(cmd) || (cmd) == LP_CMD_WRITE)
69
70#define STDIO_STATUS_ADDR 0x0030
71#define STDIO_COUNT_ADDR 0x0032
72#define LP_CMD_SINGLE_SPACE 0x2E
73#define LP_CMD_TRIPLE_SPACE 0x5A
74
75static void store16(struct ge *ge, uint16_t addr, uint16_t value)
76{
77 ge_mem_store8(ge, addr, (uint8_t)(value >> 8));
78 ge_mem_store8(ge, (uint16_t)(addr + 1), (uint8_t)value);
79}
80
81static void printer_capture_char(struct ge_integrated_printer *p, char c)
82{
83 if (p->out_len >= (int)sizeof(p->out) - 1)
84 return;
85 p->out[p->out_len++] = c;
86 p->out[p->out_len] = '\0';
87}
88
89static void printer_capture_breaks(struct ge_integrated_printer *p, int count)
90{
91 for (int i = 0; i < count; i++)
92 printer_capture_char(p, '\n');
93}
94
95static void printer_capture_control(struct ge_integrated_printer *p, uint8_t cmd)
96{
97 switch (cmd) {
100 break;
103 break;
104 default:
105 break;
106 }
107}
108
109static int kbd_ready(const struct ge_integrated_printer *p)
110{
111 return p->kbd_head != p->kbd_tail;
112}
113
114static int kbd_has_complete_line(const struct ge_integrated_printer *p)
115{
116 for (int i = p->kbd_head; i != p->kbd_tail; i = (i + 1) % (int)sizeof(p->kbd)) {
117 if (p->kbd[i] == '\n' || p->kbd[i] == '\r')
118 return 1;
119 }
120 return 0;
121}
122
123static uint8_t kbd_pop(struct ge_integrated_printer *p)
124{
125 uint8_t c = p->kbd[p->kbd_head];
126 p->kbd_head = (p->kbd_head + 1) % (int)sizeof(p->kbd);
127 return c;
128}
129
130static int service_input_line(struct ge *ge, uint16_t buf, int len)
131{
132 struct ge_integrated_printer *p = &ge->integrated_printer;
133 int max = (len > 0) ? len - 1 : 0;
134 int n = 0;
135
136 while (kbd_ready(p)) {
137 uint8_t c = kbd_pop(p);
138 if (c == '\r')
139 continue;
140 if (c == '\n')
141 break;
142 if (n < max)
143 ge_mem_store8(ge, (uint16_t)(buf + n++), ge_code(c));
144 }
145 if (len > 0)
146 ge_mem_store8(ge, (uint16_t)(buf + n), 0x00);
147 store16(ge, STDIO_COUNT_ADDR, (uint16_t)n);
149 return n;
150}
151
152static int service_input_char(struct ge *ge, uint16_t buf, int len)
153{
154 struct ge_integrated_printer *p = &ge->integrated_printer;
155 uint8_t c;
156
157 if (!kbd_ready(p))
158 return 0;
159 c = kbd_pop(p);
160 if (c == '\r')
161 c = '\n';
162 if (len > 0)
163 ge_mem_store8(ge, buf, ge_code(c));
164 store16(ge, STDIO_COUNT_ADDR, len > 0 ? 1 : 0);
165 store16(ge, STDIO_STATUS_ADDR, len > 0 ? 1 : 0);
166 return len > 0 ? 1 : 0;
167}
168
169static int input_order_ready(struct ge *ge, uint8_t cmd)
170{
171 if (cmd == KBD_CMD_CHAR)
173 if (cmd == KBD_CMD_LINE)
175 return 0;
176}
177
178/* Is the machine parked in the channel-2 org-phase external request-wait?
179 * rSO=b8 pending, rSA=0 (CPU suspended/idle), CPU-active request not raised,
180 * and the integrated reader is not presenting a byte (so this is not a reader
181 * inter-byte gap that the reader is about to break). */
182static int in_channel2_print_wait(struct ge *ge)
183{
184 return ge->rSO == 0xb8 &&
185 ge->rSA == 0x00 &&
186 !ge->RC00 &&
188}
189
190/*
191 * on_clock: called at TO00, the first clock of every machine cycle, before the
192 * MSL chart for the cycle runs. We watch for the channel-2 print-wait; once it
193 * has stalled past the debounce threshold we capture the line and assert the
194 * unit-ready / CPU-active conditions so the machine's own state_b8 microcode
195 * completes the PER (B8 -> alpha) on the next cycle.
196 */
197static int printer_on_clock(struct ge *ge, void *opaque)
198{
199 struct printer_ctx *ctx = (struct printer_ctx *)opaque;
200 struct ge_integrated_printer *p = &ge->integrated_printer;
201
202 if (!p->present)
203 return 0;
204
205 /* Channel-2 OUTPUT transfer engine: drive one character per cycle. While
206 * bytes remain, hold the channel-2 request (RC02) and the rSI output state
207 * so NA_knot routes this cycle to state_02 (mem[V4] -> RO -> CE16 -> sink).
208 * RC02 is asserted at TO00, before pulse() latches RIA2 = RC02. When the
209 * count is exhausted, drop the request and end the line. */
210 if (p->out_active) {
211 if (p->out_remaining > 0) {
212 ge->RC02 = 1;
213 ge->rSI = 0x02;
214 p->out_remaining--;
215 } else {
216 /* Transfer done: drop the request (so this cycle is no longer a
217 * channel-2 cycle and the CPU resumes), restore the CPU sequencer
218 * the stolen cycles clobbered, and end the line. */
219 ge->RC02 = 0;
220 ge->rSO = p->out_saved_so;
221 if (p->out_line_mode)
223 store16(ge, STDIO_COUNT_ADDR, (uint16_t)p->out_total);
225 p->out_active = 0;
226 p->out_line_mode = 0;
227 }
228 return 0;
229 }
230
231 /* Arm an output transfer from a channel-2 print PER. At the org-phase entry
232 * (SO=0xc8) rV1 is the order-block base; when the PER returns to alpha (e2),
233 * decode the order and, for a put command with a plausible length, start the
234 * transfer engine (which then drains the buffer over channel 2). */
235 if (ge->rSO == 0xc8) {
236 ctx->per_pending = 1;
237 ctx->per_base = ge->rV1;
238 /* Start each PER's org phase with a clean channel-2 handshake. PUC2 is
239 * a flip-flop the real machine clears via the L2.3 toggle; the model
240 * never toggles L2.3, so an input completion that asserted PUC2 would
241 * otherwise leave it stuck and make DU97 fire prematurely in the next
242 * PER (jamming its org phase). Clear it here. */
243 ge->PUC2 = 0;
244 } else if (ctx->per_pending && ge->rSO == 0xe2) {
245 uint16_t base = ctx->per_base;
246 uint8_t cmd = ge->mem[(uint16_t)(base + 1)];
247 int len = (ge->mem[(uint16_t)(base + 2)] << 8) |
248 ge->mem[(uint16_t)(base + 3)];
249 uint16_t buf = (ge->mem[(uint16_t)(base + 4)] << 8) |
250 ge->mem[(uint16_t)(base + 5)];
251 ctx->per_pending = 0;
252 if (IS_OUTPUT_CMD(cmd) && len >= 1 && len <= PRINT_LEN_MAX)
253 printer_begin_output(ge, buf, len, cmd == LP_CMD_WRITE);
254 else if (cmd == KBD_CMD_LINE && len >= 1 && len <= PRINT_LEN_MAX)
255 service_input_line(ge, buf, len);
256 else if (cmd == KBD_CMD_CHAR && len >= 1 && len <= PRINT_LEN_MAX)
257 service_input_char(ge, buf, len);
258 }
259
260 /* Input orders (read line / read char) park the CPU at the channel-2
261 * external request-wait (rSO=b8) with RC00 still asserted: unlike an
262 * output/control PER, no transfer cycles ran to reset it via CE18, so the
263 * print-wait detector below (which requires !RC00) never fires. Complete
264 * the read here, independent of RC00, as soon as the requested input is
265 * available (a full line for KBD_CMD_LINE, any key for KBD_CMD_CHAR). */
266 if (ctx->per_pending && ge->rSO == 0xb8) {
267 uint8_t cmd = ge->mem[(uint16_t)(ctx->per_base + 1)];
268 int len = (ge->mem[(uint16_t)(ctx->per_base + 2)] << 8) |
269 ge->mem[(uint16_t)(ctx->per_base + 3)];
270 /* A line-printer WRITE parks here with RC00 set; release it so the PER
271 * returns to alpha, where the e2 arm starts the buffer transfer. */
272 int write_ready = (cmd == LP_CMD_WRITE && len >= 1 && len <= PRINT_LEN_MAX);
273 int input_ready = (cmd == KBD_CMD_LINE || cmd == KBD_CMD_CHAR) &&
274 input_order_ready(ge, cmd);
275 /* Any OTHER channel-2 order parked here with RC00 still set is a printer
276 * control op (paper spacing, jump-to-channel, drum/ribbon select) that
277 * transfers no data: the device accepts it immediately. Complete it here
278 * — the RC00=0 path below (stall debounce vs the card reader's b8 gap)
279 * cannot, because RC00 never dropped. Gated on RC00 so a reader inter-byte
280 * gap (RC00=0) and funktionalcpu's RC00=0 control PER are left to it. */
281 int control_ready = ge->RC00 &&
282 cmd != KBD_CMD_LINE &&
283 cmd != KBD_CMD_CHAR &&
284 !write_ready;
285 if (write_ready || input_ready || control_ready) {
286 if (control_ready)
288 ge->PUC2 = 1; /* channel-2 unit ready -> DU97 completes the PER */
289 ge->RC00 = 1; /* CPU-active request -> rSO=b8 routed into rSA */
290 ctx->stall = 0;
291 return 0;
292 }
293 }
294
296 ctx->stall = 0;
297 return 0;
298 }
299
300 if (ctx->per_pending) {
301 uint8_t cmd = ge->mem[(uint16_t)(ctx->per_base + 1)];
302 if ((cmd == KBD_CMD_LINE || cmd == KBD_CMD_CHAR) &&
303 input_order_ready(ge, cmd)) {
304 ge->PUC2 = 1;
305 ge->RC00 = 1;
306 ctx->stall = 0;
307 return 0;
308 }
309 }
310
311 if (++ctx->stall < STALL_THRESHOLD)
312 return 0;
313
314 /* A channel-2 PER parked at the b8 external request-wait that is NOT a data
315 * print (no order-block transfer was armed above) — e.g. a control/order PER
316 * such as funktionalcpu's banner/report. Complete it via the native path so
317 * the CPU resumes, but emit nothing: real printed text comes only from an
318 * armed output transfer, not from heuristically scraping the order block. */
319 if (ctx->per_pending) {
320 uint8_t cmd = ge->mem[(uint16_t)(ctx->per_base + 1)];
322 }
323 ge->PUC2 = 1; /* channel-2 unit ready -> DU97 = 1 (PUC2 ^ L2.3) */
324 ge->RC00 = 1; /* CPU-active request -> NA_knot routes rSO=b8 into rSA */
325 ctx->stall = 0; /* one-shot; rSO leaves b8 next cycle */
326 return 0;
327}
328
329static int printer_deinit(struct ge *ge, void *opaque)
330{
331 (void)ge;
332 free(opaque);
333 return 0;
334}
335
336/* Channel-2 OUTPUT sink: the CPU's channel-2 transfer microcode hands one
337 * character to the printer here (command CE16 "Load Printer Buffer", rSI state
338 * 02/03). The byte is the machine's internal GE graphic code; render it through
339 * the GE 100-series glyph table into the paper-feed buffer (same buffer the
340 * front-ends drain). See docs/peripherals.md "CAN2 data-transfer phase". */
341static void printer_sink(struct ge *ge, struct ge_channel *ch, uint8_t c)
342{
343 struct ge_integrated_printer *p = &ge->integrated_printer;
344 (void)ch;
345
347}
348
350{
351 struct printer_ctx *ctx = calloc(1, sizeof(*ctx));
352 if (!ctx)
353 return -1;
354
358 ge->integrated_printer.out[0] = '\0';
361
362 ctx->stall = 0;
363 ctx->peri.next = NULL;
364 ctx->peri.init = NULL;
365 ctx->peri.on_pulse = NULL;
368 ctx->peri.ctx = ctx;
369
370 return ge_register_peri(ge, &ctx->peri);
371}
372
373void printer_begin_output(struct ge *ge, uint16_t buffer, int length, int line_mode)
374{
375 ge->rV4 = buffer;
380 ge->integrated_printer.out_saved_so = ge->rSO; /* resume here when done */
381}
382
383void printer_feed_key(struct ge *ge, uint8_t c)
384{
385 struct ge_integrated_printer *p = &ge->integrated_printer;
386 int next = (p->kbd_tail + 1) % (int)sizeof(p->kbd);
387 if (next == p->kbd_head)
388 return; /* queue full; drop */
389 p->kbd[p->kbd_tail] = c;
390 p->kbd_tail = next;
391}
392
394{
396}
397
398const char *printer_output(struct ge *ge)
399{
400 return ge->integrated_printer.out;
401}
402
404{
406 ge->integrated_printer.out[0] = '\0';
407}
void ge_mem_store8(struct ge *ge, uint16_t addr, uint8_t val)
Store a byte with generated odd parity + mark-written (for the hybrid ALU/SS write paths that write g...
Definition ge.c:119
int ge_register_peri(struct ge *ge, struct ge_peri *p)
char ge_glyph(uint8_t b)
Definition gecode.c:37
uint8_t ge_code(uint8_t c)
Definition gecode.c:47
static void printer_capture_control(struct ge_integrated_printer *p, uint8_t cmd)
Definition printer.c:95
#define STDIO_STATUS_ADDR
Definition printer.c:70
#define LP_CMD_WRITE
Definition printer.c:67
static int service_input_char(struct ge *ge, uint16_t buf, int len)
Definition printer.c:152
#define KBD_CMD_CHAR
Definition printer.c:58
int printer_output_len(struct ge *ge)
Definition printer.c:393
static int printer_on_clock(struct ge *ge, void *opaque)
Definition printer.c:197
static void printer_capture_breaks(struct ge_integrated_printer *p, int count)
Definition printer.c:89
#define KBD_CMD_LINE
Definition printer.c:57
static void store16(struct ge *ge, uint16_t addr, uint16_t value)
Definition printer.c:75
#define LP_CMD_SINGLE_SPACE
Definition printer.c:72
static void printer_capture_char(struct ge_integrated_printer *p, char c)
Definition printer.c:81
static int in_channel2_print_wait(struct ge *ge)
Definition printer.c:182
void printer_begin_output(struct ge *ge, uint16_t buffer, int length, int line_mode)
Definition printer.c:373
int printer_register(struct ge *ge)
Definition printer.c:349
#define PRINT_LEN_MAX
Definition printer.c:56
const char * printer_output(struct ge *ge)
Definition printer.c:398
static int input_order_ready(struct ge *ge, uint8_t cmd)
Definition printer.c:169
static int printer_deinit(struct ge *ge, void *opaque)
Definition printer.c:329
void printer_feed_key(struct ge *ge, uint8_t c)
Definition printer.c:383
static uint8_t kbd_pop(struct ge_integrated_printer *p)
Definition printer.c:123
#define IS_OUTPUT_CMD(cmd)
Definition printer.c:68
static int kbd_has_complete_line(const struct ge_integrated_printer *p)
Definition printer.c:114
void printer_output_clear(struct ge *ge)
Definition printer.c:403
#define STALL_THRESHOLD
Definition printer.c:42
static void printer_sink(struct ge *ge, struct ge_channel *ch, uint8_t c)
Definition printer.c:341
#define LP_CMD_TRIPLE_SPACE
Definition printer.c:73
static int kbd_ready(const struct ge_integrated_printer *p)
Definition printer.c:109
static int service_input_line(struct ge *ge, uint16_t buf, int len)
Definition printer.c:130
#define STDIO_COUNT_ADDR
Definition printer.c:71
uint8_t kbd[256]
Definition ge.h:613
char out[65536]
Definition ge.h:607
ge_channel_sink sink
Definition channel.h:54
Definition ge.h:732
int(* on_pulse)(struct ge *, void *)
Definition ge.h:735
void * ctx
Definition ge.h:738
struct ge_peri * next
Definition ge.h:733
int(* init)(struct ge *, void *)
Definition ge.h:734
int(* deinit)(struct ge *, void *)
Definition ge.h:737
int(* on_clock)(struct ge *, void *)
Definition ge.h:736
The entire state of the emulated system, including registers, memory, peripherals and timings.
Definition ge.h:96
struct ge_integrated_reader integrated_reader
The I/O interface for the integrated reader (RI)
Definition ge.h:595
uint16_t rV1
Addresser for the first operand.
Definition ge.h:123
uint8_t rSO
Main sequencer.
Definition ge.h:220
uint8_t PUC2
Channel 2 in transfer.
Definition ge.h:411
uint8_t RC00
Asynchronous CPU Cycle Request.
Definition ge.h:445
uint8_t mem[MEM_SIZE]
The memory of the emulated system.
Definition ge.h:566
struct ge_channel channel2
Integrated channel 2 (CAN2) line bundle — shared by the integrated reader (input),...
Definition ge.h:645
uint16_t rV4
Addresser for external instructions using channel 2.
Definition ge.h:126
uint8_t rSA
Future state configuration.
Definition ge.h:242
uint8_t RC02
Asynchronous Channel 2 Cycle Request.
Definition ge.h:473
uint8_t rSI
Peripheral unit sequencer.
Definition ge.h:234
struct ge::ge_integrated_printer integrated_printer
int stall
Definition printer.c:45
struct ge_peri peri
Definition printer.c:48
uint16_t per_base
Definition printer.c:47
int per_pending
Definition printer.c:46