/* MIX simulator, copyright 1994 by Darius Bacon */
#include "mix.h"
#include "charset.h"
#include "io.h"
#include "run.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
/* --- Device tables --- */
/* Device types: */
enum DeviceType { tape, disk, card_in, card_out, printer, console };
/* The device table: */
static struct {
const enum DeviceType type;
FILE *file;
long position; /* used by random-access devices */
/* const char *filename; */
} devices[] = {
{tape}, {tape}, {tape}, {tape}, {tape}, {tape}, {tape}, {tape},
{disk}, {disk}, {disk}, {disk}, {disk}, {disk}, {disk}, {disk},
{card_in},
{card_out},
{printer},
{console}
};
#define num_devices ( sizeof devices / sizeof devices[0] )
/* add an assign_file(device, filename) operation, too */
/* and unassign? */
typedef void IOHandler(unsigned, Cell, Address);
typedef void IOCHandler(unsigned, Cell);
static IOHandler tape_in, disk_in, text_in, console_in, no_in;
static IOHandler tape_out, disk_out, text_out, console_out, no_out;
static IOCHandler tape_ioc, disk_ioc, no_ioc, printer_ioc;
/* The device-class table: */
/*** need to distinguish read/write permission... */
static const struct Device_attributes {
const char *base_filename;
unsigned block_size;
IOHandler *in_handler;
IOHandler *out_handler;
IOCHandler *ioc_handler;
} methods[] = {
/* tape */ { "tape", 100, tape_in, tape_out, tape_ioc },
/* disk */ { "disk", 100, disk_in, disk_out, disk_ioc },
/* card_in */ { NULL, 16, text_in, no_out, no_ioc },
/* card_out */ { NULL, 16, no_in, text_out, no_ioc },
/* printer */ { NULL, 24, no_in, text_out, printer_ioc },
/* console */ { NULL, 14, console_in, console_out, no_ioc }
};
static const struct Device_attributes *attributes (unsigned device)
{
return &methods[devices[device].type];
}
static unsigned block_size(unsigned device)
{
return attributes(device)->block_size;
}
static FILE *assigned_file(unsigned device)
{
/* glibc2.1 fix -ajk */
switch (devices[device].type) {
case card_in:
return stdin; break;
case card_out: case printer:
return stdout; break;
default:
return devices[device].file;
}
}
static char *device_filename(Byte device)
{
static char filename[FILENAME_MAX];
sprintf(filename, "%s%02d",
attributes(device)->base_filename, device);
return filename;
}
static void ensure_open(Byte device)
{
if (num_devices <= device)
error("Unknown device - %02o", device);
if (!assigned_file(device)){
if (attributes(device)->base_filename) {
const char *filename = device_filename(device);
if (!(devices[device].file = fopen(filename, "r+b"))
&& !(devices[device].file = fopen(filename, "w+b")))
error("%s: %s", filename, strerror(errno));
devices[device].position = 0;
} else
error("No file assigned to device %02o (type %d)", device, devices[device].type);
}
}
void io_control(Byte device, Cell argument)
{
ensure_open(device);
attributes(device)->ioc_handler(device, argument);
}
void do_input(Byte device, Cell argument, Address buffer)
{
ensure_open(device);
attributes(device)->in_handler(device, argument, buffer);
}
void do_output(Byte device, Cell argument, Address buffer)
{
ensure_open(device);
attributes(device)->out_handler(device, argument, buffer);
}
/* --- Unsupported input or output --- */
static void no_ioc(unsigned device, Cell argument)
{
error("IOC undefined for device %02o", device);
}
static void no_in(unsigned device, Cell argument, Address buffer)
{
error("Input not allowed for device %02o", device);
}
static void no_out(unsigned device, Cell argument, Address buffer)
{
error("Output not allowed for device %02o", device);
}
/* --- Text devices --- */
/* Read a line from -file- into memory[buffer..buffer+size).
(Big-endian byte order, padded with 0 bytes if the line is less
than -size- cells long. The signs of the cells are set to '+'.
If the line is longer than 5*size bytes, only the first 5*size
bytes get read. */
static void read_line(FILE* file, Address buffer, unsigned size)
{
unsigned i, b;
Flag past_end = false;
for (i = 0; i < size; ++i) {
Cell cell = zero;
if (memory_size <= buffer + i)
/*** I think we need memory_fetch() and memory_store() functions... */
error("Address out of range");
for (b = 1; b <= 5; ++b) {
Byte mix_char;
if (past_end)
mix_char = (Byte) 0;
else {
int c = fgetc(file);
if (c == '\n' || c == EOF)
past_end = true, mix_char = (Byte) 0;
else
mix_char = C_char_to_mix((char) c);
}
cell = set_byte(mix_char, b, cell);
}
memory[buffer + i] = cell;
}
}
static void write_cell(Cell cell, FILE *outfile, Flag text)
{
unsigned i;
if (!text)
fputc(is_negative(cell) ? '-' : ' ', outfile);
for (i = 1; i <= 5; ++i)
fputc(mix_to_C_char(get_byte(i, cell)), outfile);
}
static void write_line(FILE *file, Address buffer, unsigned size, Flag text)
{
unsigned i;
for (i = 0; i < size; ++i) {
if (memory_size <= buffer + i)
error("Address out of range");
write_cell(memory[buffer + i], file, text);
}
fputc('\n', file);
}
static void printer_ioc(Byte device, Cell argument)
{
if (magnitude(argument) != 0)
error("IOC argument undefined for printer device %02o", device);
fputc('\f', assigned_file(device));
}
static void text_in(Byte device, Cell argument, Address buffer)
{
read_line(assigned_file(device), buffer, block_size(device));
}
static void text_out(Byte device, Cell argument, Address buffer)
{
write_line(assigned_file(device), buffer, block_size(device), true);
}
/* --- Block devices --- */
/*** Make this section more robust. */
/*** And either these errors should be fatal errors or we need to think
about recovery. */
static void set_file_position(Byte device, unsigned block, Flag writing)
{
if (fseek(assigned_file(device),
(long) block * (6 * block_size(device) + 1),
SEEK_SET))
error("Device %02o: %s", device, strerror(errno));
}
/* Read a block from -device- into memory[buffer..buffer+block_size(device)).
(Big-endian byte order, with words represented by 6 native C characters:
the sign ('-' or ' '), followed by 5 characters whose MIX equivalents
code for the corresponding bytes.) The block should end with a '\n'. */
static void read_block(Byte device, Address buffer)
{
FILE *file = assigned_file(device);
unsigned size = block_size(device);
unsigned i, b;
for (i = 0; i < size; ++i) {
int c;
Cell cell = zero;
if (memory_size <= buffer + i)
error("Address out of range -- read_block");
c = fgetc(file);
if (c == EOF)
error("Unexpected EOF reading from device %02o", device);
else if (c == '-')
cell = negative(cell);
for (b = 1; b <= 5; ++b) {
c = fgetc(file);
if (c == EOF)
error("Unexpected EOF reading from device %02o", device);
cell = set_byte(C_char_to_mix((char) c), b, cell);
}
memory[buffer + i] = cell;
}
fgetc(file); /* should be '\n' */
}
/* The inverse of read_block. */
static void write_block(Byte device, Address buffer)
{
write_line(assigned_file(device), buffer, block_size(device), false);
}
/* --- Tapes --- */
static void tape_ioc(unsigned device, Cell offset)
{
error("Unimplemented");
}
static void tape_in(unsigned device, Cell argument, Address buffer)
{
error("Unimplemented");
}
static void tape_out(unsigned device, Cell argument, Address buffer)
{
error("Unimplemented");
}
/* --- Disks --- */
static void disk_ioc(Byte device, Cell offset)
{
if (magnitude(offset) != 0)
error("IOC argument undefined for disk device %02o", device);
}
static void disk_in(Byte device, Cell argument, Address buffer)
{
unsigned block_num = (unsigned) field(make_field_spec(4, 5), argument);
set_file_position(device, block_num, false);
read_block(device, buffer);
}
static void disk_out(Byte device, Cell argument, Address buffer)
{
unsigned block_num = (unsigned) field(make_field_spec(4, 5), argument);
set_file_position(device, block_num, true);
write_block(device, buffer);
}
/* --- The console (typewriter/paper tape) --- */
/* Always connected to stdin/stdout, for simplicity. */
static void console_in(Byte device, Cell argument, Address buffer)
{
read_line(stdin, buffer, block_size(device));
}
static void console_out(Byte device, Cell argument, Address buffer)
{
write_line(stdout, buffer, block_size(device), true);
}
syntax highlighted by Code2HTML, v. 0.9.1