-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtape.c
More file actions
178 lines (159 loc) · 5.39 KB
/
tape.c
File metadata and controls
178 lines (159 loc) · 5.39 KB
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
/*
* tape.c — functional MTC/MTH magnetic tape on a standard connector (3/4).
*
* A length-prefixed record image (see tape.h). A read TPER transfers the record
* at the current position into CPU memory; like the disk, each logical byte is
* nibble-split for the connector's binary read mode (the channel packs the low
* nibbles of two presented bytes back into one memory byte).
*
* Scope (functional MVP): READ and the motion control orders (rewind / forward
* space / backspace) work on the record stream. WRITE / erase / write-tape-mark
* are placeholders pending the real MTC order codes and the connector output
* micro-flow (descriptive volume not yet extracted) — see the TODOs.
*
* Multi-unit controller semantics (a read/write busies all the controller's
* units while a rewind frees the others) are documented in the plan but not yet
* modelled: this is a single reel on one unit. TODO.
*/
#include "tape.h"
#include "ge.h"
#include "connector34.h"
#include "log.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define TAPE_IMAGE_MAX (1u << 20) /* 1 MiB reel image cap */
/* TODO: real CPER order codes from the MTC 163-173 descriptive volume. */
enum tape_order {
TAPE_ORD_READ = 0x40,
TAPE_ORD_WRITE = 0x42,
TAPE_ORD_REWIND = 0x20,
TAPE_ORD_BACKSPACE = 0x24,
TAPE_ORD_FORWARD = 0x28,
TAPE_ORD_ERASE = 0x2C,
TAPE_ORD_WTM = 0x30,
};
struct tape_ctx {
uint8_t connector;
uint8_t unit;
uint8_t *image;
size_t nbytes;
size_t pos; /* byte offset of the next record */
size_t prev_pos; /* for a single-level backspace */
struct ge_std_device dev;
};
static int tape_claims(void *opaque, struct std_unitname un)
{
struct tape_ctx *t = (struct tape_ctx *)opaque;
return un.connector == t->connector && un.unit == t->unit;
}
/* Length of the record at `pos` (0 = tape mark), or -1 at end of medium. */
static long record_len(struct tape_ctx *t, size_t pos)
{
if (pos + 2 > t->nbytes)
return -1;
return ((long)t->image[pos] << 8) | t->image[pos + 1];
}
static std_reaction tape_command(struct ge *ge, void *opaque,
struct std_unitname un, uint8_t order)
{
struct tape_ctx *t = (struct tape_ctx *)opaque;
(void)ge; (void)un;
switch (order) {
case TAPE_ORD_REWIND:
t->prev_pos = t->pos;
t->pos = 0;
return STD_ACCEPTED_END;
case TAPE_ORD_FORWARD: {
long len = record_len(t, t->pos);
if (len < 0)
return STD_NOT_POSSIBLE; /* at end of medium */
t->prev_pos = t->pos;
t->pos += 2 + (size_t)len;
return STD_ACCEPTED_END;
}
case TAPE_ORD_BACKSPACE:
t->pos = t->prev_pos; /* single-level backspace (MVP) */
return STD_ACCEPTED_END;
case TAPE_ORD_WRITE:
case TAPE_ORD_ERASE:
case TAPE_ORD_WTM:
return STD_ACCEPTED_NO_END; /* TODO: output path not yet wired */
case TAPE_ORD_READ:
return STD_ACCEPTED_NO_END; /* the read transfer does the work */
default:
return STD_ACCEPTED_NO_END;
}
}
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)
{
struct tape_ctx *t = (struct tape_ctx *)opaque;
(void)ge; (void)un;
if (dir != 0) {
/* WRITE: connector output path not yet wired in the core. TODO. */
*len = 0;
return STD_NOT_POSSIBLE;
}
long rl = record_len(t, t->pos);
if (rl < 0) {
*len = 0;
return STD_NOT_POSSIBLE; /* end of medium */
}
if (rl == 0) {
/* Tape mark: a zero-length read. Step over it. */
t->prev_pos = t->pos;
t->pos += 2;
*len = 0;
return STD_ACCEPTED_END;
}
const uint8_t *rec = t->image + t->pos + 2;
uint16_t n = 0;
for (long i = 0; i < rl && (size_t)(t->pos + 2 + i) < t->nbytes
&& n + 2 <= cap; i++) {
buf[n++] = (uint8_t)(rec[i] >> 4);
buf[n++] = (uint8_t)(rec[i] & 0x0F);
}
*len = n;
t->prev_pos = t->pos;
t->pos += 2 + (size_t)rl;
return STD_ACCEPTED_END;
}
int tape_register(struct ge *ge, const char *image_path,
uint8_t connector, uint8_t unit)
{
struct tape_ctx *t = calloc(1, sizeof(*t));
if (!t)
return -1;
t->connector = connector;
t->unit = unit;
t->image = calloc(1, TAPE_IMAGE_MAX);
if (!t->image) {
free(t);
return -1;
}
t->nbytes = 0;
t->pos = 0;
t->prev_pos = 0;
if (image_path) {
FILE *f = fopen(image_path, "rb");
if (f) {
t->nbytes = fread(t->image, 1, TAPE_IMAGE_MAX, f);
fclose(f);
ge_log(LOG_PERI, "tape: loaded %zu bytes from %s\n",
t->nbytes, image_path);
} else {
ge_log(LOG_ERR, "tape: cannot open %s (blank reel)\n", image_path);
}
}
t->dev.name = "MTC tape";
t->dev.ctx = t;
t->dev.claims = tape_claims;
t->dev.command = tape_command;
t->dev.transfer = tape_transfer;
t->dev.tick = NULL;
if (connector34_init(ge) != 0)
return -1;
return connector34_attach(ge, &t->dev, connector);
}