GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
tape.c
Go to the documentation of this file.
1/*
2 * tape.c — functional MTC/MTH magnetic tape on a standard connector (3/4).
3 *
4 * A length-prefixed record image (see tape.h). A read TPER transfers the record
5 * at the current position into CPU memory; like the disk, each logical byte is
6 * nibble-split for the connector's binary read mode (the channel packs the low
7 * nibbles of two presented bytes back into one memory byte).
8 *
9 * Scope (functional MVP): READ and the motion control orders (rewind / forward
10 * space / backspace) work on the record stream. WRITE / erase / write-tape-mark
11 * are placeholders pending the real MTC order codes and the connector output
12 * micro-flow (descriptive volume not yet extracted) — see the TODOs.
13 *
14 * Multi-unit controller semantics (a read/write busies all the controller's
15 * units while a rewind frees the others) are documented in the plan but not yet
16 * modelled: this is a single reel on one unit. TODO.
17 */
18
19#include "tape.h"
20#include "ge.h"
21#include "connector34.h"
22#include "log.h"
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27
28#define TAPE_IMAGE_MAX (1u << 20) /* 1 MiB reel image cap */
29
30/* TODO: real CPER order codes from the MTC 163-173 descriptive volume. */
40
41struct tape_ctx {
42 uint8_t connector;
43 uint8_t unit;
44 uint8_t *image;
45 size_t nbytes;
46 size_t pos; /* byte offset of the next record */
47 size_t prev_pos; /* for a single-level backspace */
49};
50
51static int tape_claims(void *opaque, struct std_unitname un)
52{
53 struct tape_ctx *t = (struct tape_ctx *)opaque;
54 return un.connector == t->connector && un.unit == t->unit;
55}
56
57/* Length of the record at `pos` (0 = tape mark), or -1 at end of medium. */
58static long record_len(struct tape_ctx *t, size_t pos)
59{
60 if (pos + 2 > t->nbytes)
61 return -1;
62 return ((long)t->image[pos] << 8) | t->image[pos + 1];
63}
64
65static std_reaction tape_command(struct ge *ge, void *opaque,
66 struct std_unitname un, uint8_t order)
67{
68 struct tape_ctx *t = (struct tape_ctx *)opaque;
69 (void)ge; (void)un;
70
71 switch (order) {
72 case TAPE_ORD_REWIND:
73 t->prev_pos = t->pos;
74 t->pos = 0;
75 return STD_ACCEPTED_END;
76 case TAPE_ORD_FORWARD: {
77 long len = record_len(t, t->pos);
78 if (len < 0)
79 return STD_NOT_POSSIBLE; /* at end of medium */
80 t->prev_pos = t->pos;
81 t->pos += 2 + (size_t)len;
82 return STD_ACCEPTED_END;
83 }
85 t->pos = t->prev_pos; /* single-level backspace (MVP) */
86 return STD_ACCEPTED_END;
87 case TAPE_ORD_WRITE:
88 case TAPE_ORD_ERASE:
89 case TAPE_ORD_WTM:
90 return STD_ACCEPTED_NO_END; /* TODO: output path not yet wired */
91 case TAPE_ORD_READ:
92 return STD_ACCEPTED_NO_END; /* the read transfer does the work */
93 default:
95 }
96}
97
98static std_reaction tape_transfer(struct ge *ge, void *opaque,
99 struct std_unitname un, int dir,
100 uint8_t *buf, uint16_t *len, uint16_t cap)
101{
102 struct tape_ctx *t = (struct tape_ctx *)opaque;
103 (void)ge; (void)un;
104
105 if (dir != 0) {
106 /* WRITE: connector output path not yet wired in the core. TODO. */
107 *len = 0;
108 return STD_NOT_POSSIBLE;
109 }
110
111 long rl = record_len(t, t->pos);
112 if (rl < 0) {
113 *len = 0;
114 return STD_NOT_POSSIBLE; /* end of medium */
115 }
116 if (rl == 0) {
117 /* Tape mark: a zero-length read. Step over it. */
118 t->prev_pos = t->pos;
119 t->pos += 2;
120 *len = 0;
121 return STD_ACCEPTED_END;
122 }
123
124 const uint8_t *rec = t->image + t->pos + 2;
125 uint16_t n = 0;
126 for (long i = 0; i < rl && (size_t)(t->pos + 2 + i) < t->nbytes
127 && n + 2 <= cap; i++) {
128 buf[n++] = (uint8_t)(rec[i] >> 4);
129 buf[n++] = (uint8_t)(rec[i] & 0x0F);
130 }
131 *len = n;
132
133 t->prev_pos = t->pos;
134 t->pos += 2 + (size_t)rl;
135 return STD_ACCEPTED_END;
136}
137
138int tape_register(struct ge *ge, const char *image_path,
139 uint8_t connector, uint8_t unit)
140{
141 struct tape_ctx *t = calloc(1, sizeof(*t));
142 if (!t)
143 return -1;
144
145 t->connector = connector;
146 t->unit = unit;
147 t->image = calloc(1, TAPE_IMAGE_MAX);
148 if (!t->image) {
149 free(t);
150 return -1;
151 }
152 t->nbytes = 0;
153 t->pos = 0;
154 t->prev_pos = 0;
155
156 if (image_path) {
157 FILE *f = fopen(image_path, "rb");
158 if (f) {
159 t->nbytes = fread(t->image, 1, TAPE_IMAGE_MAX, f);
160 fclose(f);
161 ge_log(LOG_PERI, "tape: loaded %zu bytes from %s\n",
162 t->nbytes, image_path);
163 } else {
164 ge_log(LOG_ERR, "tape: cannot open %s (blank reel)\n", image_path);
165 }
166 }
167
168 t->dev.name = "MTC tape";
169 t->dev.ctx = t;
173 t->dev.tick = NULL;
174
175 if (connector34_init(ge) != 0)
176 return -1;
177 return connector34_attach(ge, &t->dev, connector);
178}
int connector34_init(struct ge *ge)
int connector34_attach(struct ge *ge, struct ge_std_device *dev, uint8_t connector)
std_reaction
Definition connector34.h:32
@ STD_ACCEPTED_END
Definition connector34.h:34
@ STD_ACCEPTED_NO_END
Definition connector34.h:33
@ STD_NOT_POSSIBLE
Definition connector34.h:36
void ge_log(ge_log_type type, const char *format,...)
Log message.
Definition log.c:122
@ LOG_ERR
Emulator unrecoverable condition.
Definition log.h:18
@ LOG_PERI
Peripherals IO.
Definition log.h:27
void(* tick)(struct ge *, void *ctx)
Definition connector34.h:72
int(* claims)(void *ctx, struct std_unitname un)
Definition connector34.h:56
const char * name
Definition connector34.h:51
std_reaction(* command)(struct ge *, void *ctx, struct std_unitname un, uint8_t order)
Definition connector34.h:60
std_reaction(* transfer)(struct ge *, void *ctx, struct std_unitname un, int dir, uint8_t *buf, uint16_t *len, uint16_t cap)
Definition connector34.h:68
The entire state of the emulated system, including registers, memory, peripherals and timings.
Definition ge.h:96
uint8_t unit
Definition connector34.h:43
uint8_t connector
Definition connector34.h:42
uint8_t unit
Definition tape.c:43
size_t pos
Definition tape.c:46
uint8_t connector
Definition tape.c:42
size_t nbytes
Definition tape.c:45
size_t prev_pos
Definition tape.c:47
struct ge_std_device dev
Definition tape.c:48
uint8_t * image
Definition tape.c:44
static std_reaction tape_command(struct ge *ge, void *opaque, struct std_unitname un, uint8_t order)
Definition tape.c:65
static int tape_claims(void *opaque, struct std_unitname un)
Definition tape.c:51
static long record_len(struct tape_ctx *t, size_t pos)
Definition tape.c:58
int tape_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)
Definition tape.c:138
tape_order
Definition tape.c:31
@ TAPE_ORD_REWIND
Definition tape.c:34
@ TAPE_ORD_ERASE
Definition tape.c:37
@ TAPE_ORD_READ
Definition tape.c:32
@ TAPE_ORD_WTM
Definition tape.c:38
@ TAPE_ORD_WRITE
Definition tape.c:33
@ TAPE_ORD_BACKSPACE
Definition tape.c:35
@ TAPE_ORD_FORWARD
Definition tape.c:36
static std_reaction tape_transfer(struct ge *ge, void *opaque, struct std_unitname un, int dir, uint8_t *buf, uint16_t *len, uint16_t cap)
Definition tape.c:98
#define TAPE_IMAGE_MAX
Definition tape.c:28