/* MIX simulator, copyright 1994 by Darius Bacon */
#include "mix.h"
#include "asm.h" /* for entry_point */
#include "charset.h"
#include "io.h"
#include "run.h"
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static void stop(const char *message, va_list args)
{
fprintf(stderr, "RUNTIME ERROR: ");
vfprintf(stderr, message, args);
fprintf(stderr, "\n");
print_CPU_state();
exit(1);
}
/* --- Execution statistics --- */
static unsigned long elapsed_time = 0; /* in Tyme units */
/* --- The CPU state --- */
Cell memory[memory_size];
#define A 0
#define X 7
#define J 8
static Cell r[10]; /* the registers; except that r[9] == zero. */
static int comparison_indicator; /* the overflow toggle is defined in cell.c */
static Address pc; /* the program counter */
void set_initial_state(void)
{
overflow = false;
comparison_indicator = 0;
{
unsigned i;
for (i = 0; i < 10; ++i)
r[i] = zero;
}
pc = entry_point; /*** need to check for no entry point */
}
void print_CPU_state(void)
{
printf ("A:");
print_cell (r[A]);
printf ("\t");
{ /* Print the index registers: */
unsigned i;
for (i = 1; i <= 6; ++i)
printf ("I%u:%s%04lo ",
i, is_negative (r[i]) ? "-" : " ", magnitude (r[i]));
}
printf ("\nX:");
print_cell (r[X]);
printf ("\t J: %04lo", magnitude (r[J])); /* (it's always nonnegative) */
printf (" PC: %04o", pc);
printf (" Flags: %-7s %-8s",
comparison_indicator < 0 ? "less" :
comparison_indicator == 0 ? "equal" : "greater",
overflow ? "overflow" : "");
printf (" %11lu elapsed\n", elapsed_time);
}
/* --- The interpreter --- */
/* --- I've followed Knuth's MIX interpreter quite closely. */
static jmp_buf escape_k; /* continuation to escape from interpreter */
/* C, F, M, and V as defined in Knuth: */
static Byte C;
static Byte F;
static Cell M;
static Cell get_V(void)
{
return field(F, memory[cell_to_address(M)]);
}
/* do_foo performs the action of instruction type foo. */
static void do_nop(void) { }
static void do_add(void) { r[A] = add(r[A], get_V()); }
static void do_sub(void) { r[A] = sub(r[A], get_V()); }
static void do_mul(void) { multiply(r[A], get_V(), &r[A], &r[X]); }
static void do_div(void) { divide(r[A], r[X], get_V(), &r[A], &r[X]); }
static void do_special(void)
{
switch (F) {
case 0: { /* NUM */
unsigned i;
Cell num = zero;
Cell ten = ulong_to_cell(10);
for (i = 1; i <= 5; ++i)
num = add(mul(ten, num), (Cell)(get_byte(i, r[A]) % 10));
for (i = 1; i <= 5; ++i)
num = add(mul(ten, num), (Cell)(get_byte(i, r[X]) % 10));
r[A] = is_negative(r[A]) ? negative(num) : num;
break;
}
case 1: { /* CHAR */
unsigned long num = magnitude(r[A]);
unsigned z = (unsigned) C_char_to_mix('0');
unsigned i;
for (i = 5; 0 < i; --i, num /= 10)
r[X] = set_byte((Byte) (z + num % 10), i, r[X]);
for (i = 5; 0 < i; --i, num /= 10)
r[A] = set_byte((Byte) (z + num % 10), i, r[A]);
break;
}
case 2: /* HLT */
longjmp(escape_k, 1);
default: error("Unknown extended opcode");
}
}
static void do_shift(void)
{
Cell ignore;
unsigned long count = magnitude(M);
if (is_negative(M) && count != 0)
error("Negative shift count");
switch (F) {
case 0: /* SLA */
shift_left(zero, r[A], count, &ignore, &r[A]);
break;
case 1: /* SRA */
shift_right(r[A], zero, count, &r[A], &ignore);
break;
case 2: /* SLAX */
shift_left(r[A], r[X], count, &r[A], &r[X]);
break;
case 3: /* SRAX */
shift_right(r[A], r[X], count, &r[A], &r[X]);
break;
case 4: /* SLC */
shift_left_circular(r[A], r[X], (unsigned)(count % 10), &r[A], &r[X]);
break;
case 5: { /* SRC */
unsigned c = (10 - count % 10) % 10; /* -count modulo 10 */
shift_left_circular(r[A], r[X], c, &r[A], &r[X]);
break;
}
default: error("Unknown extended opcode");
}
}
static void do_move(void)
{
Address from = cell_to_address(M);
Address to = cell_to_address(r[1]);
unsigned count = F;
for (; count != 0; --count) {
if (memory_size <= from + count || memory_size <= to + count)
error("Address out of range");
memory[to + count] = memory[from + count];
elapsed_time += 2;
}
r[1] = address_to_cell(to + count);
}
static void do_lda(void) { r[A] = get_V(); }
static void do_ldx(void) { r[X] = get_V(); }
static void do_ldi(void) {
Cell cell = get_V();
if (INDEX_MAX < magnitude(cell))
error("Magnitude too large for index register: %10o", magnitude(cell));
r[C & 7] = cell;
}
static void do_ldan(void) { r[A] = negative(get_V()); }
static void do_ldxn(void) { r[X] = negative(get_V()); }
static void do_ldin(void) {
Cell cell = get_V();
if (INDEX_MAX < magnitude(cell))
error("Magnitude too large for index register: %10o", magnitude(cell));
r[C & 7] = negative(cell);
}
static void do_store(void)
{
Address a = cell_to_address(M);
memory[a] = set_field(r[C-24], F, memory[a]);
}
static void jump(void)
{
r[J] = address_to_cell(pc);
pc = cell_to_address(M);
}
static void branch(unsigned condition, int sign)
{
switch (condition) {
case 0: jump(); break;
case 1: pc = cell_to_address(M); break;
case 2: if (overflow) jump(); overflow = false; break;
case 3: if (!overflow) jump(); overflow = false; break;
case 4: if (sign < 0) jump(); break;
case 5: if (sign == 0) jump(); break;
case 6: if (sign > 0) jump(); break;
case 7: if (sign >= 0) jump(); break;
case 8: if (sign != 0) jump(); break;
case 9: if (sign <= 0) jump(); break;
default: error("Bad branch condition");
}
}
static void do_jump(void)
{
branch(F, comparison_indicator);
}
static int sign_of_difference(Cell difference)
{
return magnitude(difference) == 0 ? 0 : is_negative(difference) ? -1 : 1;
}
static void do_reg_branch(void)
{
branch(F + 4, sign_of_difference(r[C & 7]));
}
static void do_jbus(void)
{
/* no channel is ever busy, because we're using C's blocking I/O */
}
static void do_jred(void)
{
jump(); /* conversely, all channels are always ready */
}
static void do_ioc(void) { io_control(F, M); }
static void do_in(void) { do_input(F, r[X], cell_to_address(M)); }
static void do_out(void) { do_output(F, r[X], cell_to_address(M)); }
static void do_addr_op(void)
{
Cell cell;
unsigned reg = C & 7;
switch (F) {
case 0: cell = add(r[reg], M); break;
case 1: cell = sub(r[reg], M); break;
case 2: cell = M; break;
case 3: cell = negative(M); break;
default: error("Unknown extended opcode"); cell = zero;
}
if (reg - 1 < 6) /* same as: 1 <= reg && reg <= 6 */
if (INDEX_MAX < magnitude(cell))
error("Magnitude too large for index register: %10o",
magnitude(cell));
r[reg] = cell;
}
static void do_compare(void)
{
Flag saved = overflow;
Cell difference = sub(field(F, r[C & 7]),
field(F, memory[cell_to_address(M)]));
comparison_indicator = sign_of_difference(difference);
overflow = saved;
}
static const struct {
void (*action)(void);
unsigned clocks;
} op_table[64] = {
{ do_nop, 1 },
{ do_add, 2 },
{ do_sub, 2 },
{ do_mul, 10 },
{ do_div, 12 },
{ do_special, 1 },
{ do_shift, 2 },
{ do_move, 1 },
{ do_lda, 2 },
{ do_ldi, 2 },
{ do_ldi, 2 },
{ do_ldi, 2 },
{ do_ldi, 2 },
{ do_ldi, 2 },
{ do_ldi, 2 },
{ do_ldx, 2 },
{ do_ldan, 2 },
{ do_ldin, 2 },
{ do_ldin, 2 },
{ do_ldin, 2 },
{ do_ldin, 2 },
{ do_ldin, 2 },
{ do_ldin, 2 },
{ do_ldxn, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_store, 2 },
{ do_jbus, 1 },
{ do_ioc, 1 },
{ do_in, 1 },
{ do_out, 1 },
{ do_jred, 1 },
{ do_jump, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_reg_branch, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_addr_op, 1 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
{ do_compare, 2 },
};
void run(void)
{
install_error_handler(stop);
if (setjmp(escape_k) != 0)
return;
for (;;) {
/* print_CPU_state(); */
if (memory_size <= pc)
error("Program counter out of range: %4o", pc);
{
Byte I;
destructure_cell(memory[pc++], M, I, F, C);
if (6 < I)
error("Invalid I-field: %u", I);
if (I != 0)
M = add(M, r[I]); /* (the add can't overflow because the numbers are too small) */
op_table[C].action();
elapsed_time += op_table[C].clocks;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1