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