1 /* MIX simulator, copyright 1994 by Darius Bacon */
2 #include "mix.h"
3 #include "charset.h"
4 #include "io.h"
5 #include "run.h"
6
7 #include <errno.h>
8 #include <string.h>
9 #include <stdio.h>
10
11 /* --- Device tables --- */
12
13 /* Device types: */
14 enum DeviceType { tape, disk, card_in, card_out, printer, console };
15
16 /* The device table: */
17 static struct {
18 const enum DeviceType type;
19 FILE *file;
20 long position; /* used by random-access devices */
21 /* const char *filename; */
22 } devices[] = {
23 {tape}, {tape}, {tape}, {tape}, {tape}, {tape}, {tape}, {tape},
24 {disk}, {disk}, {disk}, {disk}, {disk}, {disk}, {disk}, {disk},
25 {card_in},
26 {card_out},
27 {printer},
28 {console}
29 };
30
31 #define num_devices ( sizeof devices / sizeof devices[0] )
32
33 /* add an assign_file(device, filename) operation, too */
34 /* and unassign? */
35
36 typedef void IOHandler(unsigned, Cell, Address);
37 typedef void IOCHandler(unsigned, Cell);
38
39 static IOHandler tape_in, disk_in, text_in, console_in, no_in;
40 static IOHandler tape_out, disk_out, text_out, console_out, no_out;
41 static IOCHandler tape_ioc, disk_ioc, no_ioc, printer_ioc;
42
43 /* The device-class table: */
44 /*** need to distinguish read/write permission... */
45 static const struct Device_attributes {
46 const char *base_filename;
47 unsigned block_size;
48 IOHandler *in_handler;
49 IOHandler *out_handler;
50 IOCHandler *ioc_handler;
51 } methods[] = {
52
53 /* tape */ { "tape", 100, tape_in, tape_out, tape_ioc },
54 /* disk */ { "disk", 100, disk_in, disk_out, disk_ioc },
55 /* card_in */ { NULL, 16, text_in, no_out, no_ioc },
56 /* card_out */ { NULL, 16, no_in, text_out, no_ioc },
57 /* printer */ { NULL, 24, no_in, text_out, printer_ioc },
58 /* console */ { NULL, 14, console_in, console_out, no_ioc }
59
60 };
61
attributes(unsigned device)62 static const struct Device_attributes *attributes (unsigned device)
63 {
64 return &methods[devices[device].type];
65 }
66
block_size(unsigned device)67 static unsigned block_size(unsigned device)
68 {
69 return attributes(device)->block_size;
70 }
71
assigned_file(unsigned device)72 static FILE *assigned_file(unsigned device)
73 {
74 /* glibc2.1 fix -ajk */
75 switch (devices[device].type) {
76 case card_in:
77 return stdin; break;
78 case card_out: case printer:
79 return stdout; break;
80 default:
81 return devices[device].file;
82 }
83 }
84
device_filename(Byte device)85 static char *device_filename(Byte device)
86 {
87 static char filename[FILENAME_MAX];
88 sprintf(filename, "%s%02d",
89 attributes(device)->base_filename, device);
90 return filename;
91 }
92
ensure_open(Byte device)93 static void ensure_open(Byte device)
94 {
95 if (num_devices <= device)
96 error("Unknown device - %02o", device);
97 if (!assigned_file(device)){
98 if (attributes(device)->base_filename) {
99 const char *filename = device_filename(device);
100 if (!(devices[device].file = fopen(filename, "r+b"))
101 && !(devices[device].file = fopen(filename, "w+b")))
102 error("%s: %s", filename, strerror(errno));
103 devices[device].position = 0;
104 } else
105 error("No file assigned to device %02o (type %d)", device, devices[device].type);
106 }
107 }
108
io_control(Byte device,Cell argument)109 void io_control(Byte device, Cell argument)
110 {
111 ensure_open(device);
112 attributes(device)->ioc_handler(device, argument);
113 }
114
do_input(Byte device,Cell argument,Address buffer)115 void do_input(Byte device, Cell argument, Address buffer)
116 {
117 ensure_open(device);
118 attributes(device)->in_handler(device, argument, buffer);
119 }
120
do_output(Byte device,Cell argument,Address buffer)121 void do_output(Byte device, Cell argument, Address buffer)
122 {
123 ensure_open(device);
124 attributes(device)->out_handler(device, argument, buffer);
125 }
126
127 /* --- Unsupported input or output --- */
128
no_ioc(unsigned device,Cell argument)129 static void no_ioc(unsigned device, Cell argument)
130 {
131 error("IOC undefined for device %02o", device);
132 }
133
no_in(unsigned device,Cell argument,Address buffer)134 static void no_in(unsigned device, Cell argument, Address buffer)
135 {
136 error("Input not allowed for device %02o", device);
137 }
138
no_out(unsigned device,Cell argument,Address buffer)139 static void no_out(unsigned device, Cell argument, Address buffer)
140 {
141 error("Output not allowed for device %02o", device);
142 }
143
144 /* --- Text devices --- */
145
146 /* Read a line from -file- into memory[buffer..buffer+size).
147 (Big-endian byte order, padded with 0 bytes if the line is less
148 than -size- cells long. The signs of the cells are set to '+'.
149 If the line is longer than 5*size bytes, only the first 5*size
150 bytes get read. */
read_line(FILE * file,Address buffer,unsigned size)151 static void read_line(FILE* file, Address buffer, unsigned size)
152 {
153 unsigned i, b;
154 Flag past_end = false;
155 for (i = 0; i < size; ++i) {
156 Cell cell = zero;
157 if (memory_size <= buffer + i)
158 /*** I think we need memory_fetch() and memory_store() functions... */
159 error("Address out of range");
160 for (b = 1; b <= 5; ++b) {
161 Byte mix_char;
162 if (past_end)
163 mix_char = (Byte) 0;
164 else {
165 int c = fgetc(file);
166 if (c == '\n' || c == EOF)
167 past_end = true, mix_char = (Byte) 0;
168 else
169 mix_char = C_char_to_mix((char) c);
170 }
171 cell = set_byte(mix_char, b, cell);
172 }
173 memory[buffer + i] = cell;
174 }
175 }
176
write_cell(Cell cell,FILE * outfile,Flag text)177 static void write_cell(Cell cell, FILE *outfile, Flag text)
178 {
179 unsigned i;
180 if (!text)
181 fputc(is_negative(cell) ? '-' : ' ', outfile);
182 for (i = 1; i <= 5; ++i)
183 fputc(mix_to_C_char(get_byte(i, cell)), outfile);
184 }
185
write_line(FILE * file,Address buffer,unsigned size,Flag text)186 static void write_line(FILE *file, Address buffer, unsigned size, Flag text)
187 {
188 unsigned i;
189 for (i = 0; i < size; ++i) {
190 if (memory_size <= buffer + i)
191 error("Address out of range");
192 write_cell(memory[buffer + i], file, text);
193 }
194 fputc('\n', file);
195 }
196
printer_ioc(Byte device,Cell argument)197 static void printer_ioc(Byte device, Cell argument)
198 {
199 if (magnitude(argument) != 0)
200 error("IOC argument undefined for printer device %02o", device);
201 fputc('\f', assigned_file(device));
202 }
203
text_in(Byte device,Cell argument,Address buffer)204 static void text_in(Byte device, Cell argument, Address buffer)
205 {
206 read_line(assigned_file(device), buffer, block_size(device));
207 }
208
text_out(Byte device,Cell argument,Address buffer)209 static void text_out(Byte device, Cell argument, Address buffer)
210 {
211 write_line(assigned_file(device), buffer, block_size(device), true);
212 }
213
214 /* --- Block devices --- */
215 /*** Make this section more robust. */
216 /*** And either these errors should be fatal errors or we need to think
217 about recovery. */
218
set_file_position(Byte device,unsigned block,Flag writing)219 static void set_file_position(Byte device, unsigned block, Flag writing)
220 {
221 if (fseek(assigned_file(device),
222 (long) block * (6 * block_size(device) + 1),
223 SEEK_SET))
224 error("Device %02o: %s", device, strerror(errno));
225 }
226
227 /* Read a block from -device- into memory[buffer..buffer+block_size(device)).
228 (Big-endian byte order, with words represented by 6 native C characters:
229 the sign ('-' or ' '), followed by 5 characters whose MIX equivalents
230 code for the corresponding bytes.) The block should end with a '\n'. */
read_block(Byte device,Address buffer)231 static void read_block(Byte device, Address buffer)
232 {
233 FILE *file = assigned_file(device);
234 unsigned size = block_size(device);
235 unsigned i, b;
236 for (i = 0; i < size; ++i) {
237 int c;
238 Cell cell = zero;
239 if (memory_size <= buffer + i)
240 error("Address out of range -- read_block");
241
242 c = fgetc(file);
243 if (c == EOF)
244 error("Unexpected EOF reading from device %02o", device);
245 else if (c == '-')
246 cell = negative(cell);
247
248 for (b = 1; b <= 5; ++b) {
249 c = fgetc(file);
250 if (c == EOF)
251 error("Unexpected EOF reading from device %02o", device);
252 cell = set_byte(C_char_to_mix((char) c), b, cell);
253 }
254 memory[buffer + i] = cell;
255 }
256 fgetc(file); /* should be '\n' */
257 }
258
259 /* The inverse of read_block. */
write_block(Byte device,Address buffer)260 static void write_block(Byte device, Address buffer)
261 {
262 write_line(assigned_file(device), buffer, block_size(device), false);
263 }
264
265 /* --- Tapes --- */
266
tape_ioc(unsigned device,Cell offset)267 static void tape_ioc(unsigned device, Cell offset)
268 {
269 error("Unimplemented");
270 }
271
tape_in(unsigned device,Cell argument,Address buffer)272 static void tape_in(unsigned device, Cell argument, Address buffer)
273 {
274 error("Unimplemented");
275 }
276
tape_out(unsigned device,Cell argument,Address buffer)277 static void tape_out(unsigned device, Cell argument, Address buffer)
278 {
279 error("Unimplemented");
280 }
281
282 /* --- Disks --- */
283
disk_ioc(Byte device,Cell offset)284 static void disk_ioc(Byte device, Cell offset)
285 {
286 if (magnitude(offset) != 0)
287 error("IOC argument undefined for disk device %02o", device);
288 }
289
disk_in(Byte device,Cell argument,Address buffer)290 static void disk_in(Byte device, Cell argument, Address buffer)
291 {
292 unsigned block_num = (unsigned) field(make_field_spec(4, 5), argument);
293 set_file_position(device, block_num, false);
294 read_block(device, buffer);
295 }
296
disk_out(Byte device,Cell argument,Address buffer)297 static void disk_out(Byte device, Cell argument, Address buffer)
298 {
299 unsigned block_num = (unsigned) field(make_field_spec(4, 5), argument);
300 set_file_position(device, block_num, true);
301 write_block(device, buffer);
302 }
303
304 /* --- The console (typewriter/paper tape) --- */
305 /* Always connected to stdin/stdout, for simplicity. */
306
console_in(Byte device,Cell argument,Address buffer)307 static void console_in(Byte device, Cell argument, Address buffer)
308 {
309 read_line(stdin, buffer, block_size(device));
310 }
311
console_out(Byte device,Cell argument,Address buffer)312 static void console_out(Byte device, Cell argument, Address buffer)
313 {
314 write_line(stdout, buffer, block_size(device), true);
315 }
316
317