Added missing newline in NEDsim error message.
[screensavers] / hacks / NEDsim / simulator.c
index 4857946..b433e0d 100644 (file)
@@ -5,49 +5,17 @@
 /* NED1 Simulator                                                             */
 /* -------------------------------------------------------------------------- */
 
 /* NED1 Simulator                                                             */
 /* -------------------------------------------------------------------------- */
 
-// TODO: Make a bunch of functions private in this file.
 #include <stdio.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdint.h>
-#include <inttypes.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdbool.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <string.h>
 #include <errno.h>
 #include <string.h>
 #include <errno.h>
-#include <time.h>
-#include <termios.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
 
 #include "a.out.h"
 #include "simulator.h"
 
 
 #include "a.out.h"
 #include "simulator.h"
 
-int
-is_stdin_nonempty(void)
-{
-    fd_set read_fds;
-    FD_ZERO(&read_fds);
-    FD_SET(STDIN_FILENO, &read_fds);
-
-    struct timeval timeout;
-    timeout.tv_sec = 0;
-    timeout.tv_usec = 0;
-
-    int retval = select(1, &read_fds, NULL, NULL, &timeout);
-
-    if (retval == -1) {
-        /* TODO: How do I want to handle this error? */
-    }
-
-    return retval;
-}
-
-uint32_t
+static uint32_t
 generate_binary_psw(struct NEDstate * state)
 {
     uint32_t psw = 0;
 generate_binary_psw(struct NEDstate * state)
 {
     uint32_t psw = 0;
@@ -56,32 +24,24 @@ generate_binary_psw(struct NEDstate * state)
     return psw;
 }
 
     return psw;
 }
 
-void
+static void
 ram_w_byte(struct NEDstate * state, uint32_t address, uint8_t data)
 {
 ram_w_byte(struct NEDstate * state, uint32_t address, uint8_t data)
 {
-    state->ram[address] = data;
+    state->ram[address-RAM_BASE_ADDRESS] = data;
 }
 
 }
 
-uint8_t
+static uint8_t
 ram_r_byte(struct NEDstate * state, uint32_t address)
 {
 ram_r_byte(struct NEDstate * state, uint32_t address)
 {
-    return state->ram[address];
+    return state->ram[address-RAM_BASE_ADDRESS];
 }
 
 /* For now, with only a terminal for IO, we pick off IO requests when accessing RAM. */
 }
 
 /* For now, with only a terminal for IO, we pick off IO requests when accessing RAM. */
-/* TODO: Improve this before adding any other IO devices like disks. */
 
 
-void
+static void
 ram_w_word(struct NEDstate * state, uint32_t address, uint32_t data)
 {
 ram_w_word(struct NEDstate * state, uint32_t address, uint32_t data)
 {
-    /* TODO: Since PC and PSW are memory mapped, they should accept writes. */
-    /*       Should writes to the PC automatically reset the syllable counter? */
-    if (address == 0x8000000) { /* SLU: XBUF */
-        printf("%c", data);
-        fflush(stdout);
-    } else if (address == 0x0 || address == 0x4) {
-        /* Intentionally empty */
-    } else if (address >= 0x20000000) {
+    if (address >= RAM_BASE_ADDRESS) {
         for (int i=3; i>=0; i--) {
             uint8_t tmp_byte = ((data >> (8*(3-i))) & 0xff);
             ram_w_byte(state,address+i,tmp_byte);
         for (int i=3; i>=0; i--) {
             uint8_t tmp_byte = ((data >> (8*(3-i))) & 0xff);
             ram_w_byte(state,address+i,tmp_byte);
@@ -100,23 +60,7 @@ ram_r_word(struct NEDstate * state, uint32_t address)
         return state->active_thread->pc;
     } else if (address == 0xC) {        /* PSW register */
         return generate_binary_psw(state);
         return state->active_thread->pc;
     } else if (address == 0xC) {        /* PSW register */
         return generate_binary_psw(state);
-    } else if (address == 0x8000004) {  /* SLU: XCSR */
-        /* TODO: Should I artificially restrict printing in the simulator? */
-        /*       It might help catch bugs like the GCC bug that slipped past SIMH. */
-        return 0b1;
-    } else if (address == 0x8000008) {  /* SLU: RBUF */
-        if (is_stdin_nonempty()) {
-            return getchar();
-        } else {
-            return (uint8_t)rand();
-        }
-    } else if (address == 0x800000C) {  /* SLU: RCSR */
-        if (is_stdin_nonempty()) {
-            return 0b1;
-        } else {
-            return 0b0;
-        }
-    } else if (address >= 0x20000000) { /* RAM */
+    } else if (address >= RAM_BASE_ADDRESS) { /* RAM */
         uint32_t word = 0;
         for (int i=0; i<4; i++) word |= (ram_r_byte(state,address+i)) << (8*(3-i));
         return word;
         uint32_t word = 0;
         for (int i=0; i<4; i++) word |= (ram_r_byte(state,address+i)) << (8*(3-i));
         return word;
@@ -124,7 +68,7 @@ ram_r_word(struct NEDstate * state, uint32_t address)
     return 0b0;
 }
 
     return 0b0;
 }
 
-uint32_t
+static uint32_t
 fetch_instruction_word(struct NEDstate * state)
 {
     uint32_t word = ram_r_word(state, state->active_thread->pc);
 fetch_instruction_word(struct NEDstate * state)
 {
     uint32_t word = ram_r_word(state, state->active_thread->pc);
@@ -132,31 +76,31 @@ fetch_instruction_word(struct NEDstate * state)
     return word;
 }
 
     return word;
 }
 
-void
+static void
 stack_w(struct NEDthread * thread, uint32_t value, uint8_t offset)
 {
     thread->stack[thread->sp - (offset + 1)] = value;
 }
 
 stack_w(struct NEDthread * thread, uint32_t value, uint8_t offset)
 {
     thread->stack[thread->sp - (offset + 1)] = value;
 }
 
-uint32_t
+static uint32_t
 stack_r(struct NEDthread * thread, uint8_t offset)
 {
     return thread->stack[thread->sp - (offset + 1)];
 }
 
 stack_r(struct NEDthread * thread, uint8_t offset)
 {
     return thread->stack[thread->sp - (offset + 1)];
 }
 
-void
+static void
 stack_push(struct NEDthread * thread, uint32_t value)
 {
     thread->stack[thread->sp++] = value;
 }
 
 stack_push(struct NEDthread * thread, uint32_t value)
 {
     thread->stack[thread->sp++] = value;
 }
 
-uint32_t
+static uint32_t
 stack_pop(struct NEDthread * thread)
 {
     return thread->stack[--thread->sp];
 }
 
 stack_pop(struct NEDthread * thread)
 {
     return thread->stack[--thread->sp];
 }
 
-void
+static void
 set_psw_flags(uint32_t word, struct NEDstate * state)
 {
     if (word == 0) {
 set_psw_flags(uint32_t word, struct NEDstate * state)
 {
     if (word == 0) {
@@ -171,7 +115,7 @@ set_psw_flags(uint32_t word, struct NEDstate * state)
     }
 }
 
     }
 }
 
-void
+static void
 ned_instruction_and(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
 ned_instruction_and(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
@@ -180,7 +124,7 @@ ned_instruction_and(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_or(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
 ned_instruction_or(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
@@ -189,14 +133,14 @@ ned_instruction_or(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_not(struct NEDstate * state)
 {
     stack_push(state->active_thread, ~stack_pop(state->active_thread));
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
 ned_instruction_not(struct NEDstate * state)
 {
     stack_push(state->active_thread, ~stack_pop(state->active_thread));
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_xor(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
 ned_instruction_xor(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
@@ -205,7 +149,7 @@ ned_instruction_xor(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_add(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
 ned_instruction_add(struct NEDstate * state)
 {
     uint32_t operand1 = stack_pop(state->active_thread);
@@ -214,24 +158,9 @@ ned_instruction_add(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
-ned_instruction_mvstck(struct NEDstate * state)
-{
-    uint32_t new_id = stack_pop(state->active_thread);
-    if (new_id < THREAD_COUNT) {
-        state->active_thread = state->thread[new_id];
-    } else {
-        printf("ERROR: Attempted MVSTCK to ID higher than THREAD_COUNT.\n");
-        state->halted = true;
-    }
-}
-
-void
+static void
 ned_instruction_shift(struct NEDstate * state)
 {
 ned_instruction_shift(struct NEDstate * state)
 {
-    /* TODO: Bounds check: Either all inputs are valid OR shift_by < 32. */
-    /* I guess this also depends if I'm shifting-and-dropping, or barrel-shifting. */
-    /* How should I pad for a right shift if I shift-and-drop? Sign extend? */
     uint32_t shift_by = stack_pop(state->active_thread);
     uint32_t word = stack_pop(state->active_thread);
     if (shift_by & 0x80000000) {
     uint32_t shift_by = stack_pop(state->active_thread);
     uint32_t word = stack_pop(state->active_thread);
     if (shift_by & 0x80000000) {
@@ -242,21 +171,21 @@ ned_instruction_shift(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_test(struct NEDstate * state)
 {
     uint32_t word = stack_pop(state->active_thread);
     set_psw_flags(word, state);
 }
 
 ned_instruction_test(struct NEDstate * state)
 {
     uint32_t word = stack_pop(state->active_thread);
     set_psw_flags(word, state);
 }
 
-void
+static void
 ned_instruction_jmp(struct NEDstate * state)
 {
     state->active_thread->pc = stack_pop(state->active_thread);
     // The SC is caught and reset by the main loop since the PC changed.
 }
 
 ned_instruction_jmp(struct NEDstate * state)
 {
     state->active_thread->pc = stack_pop(state->active_thread);
     // The SC is caught and reset by the main loop since the PC changed.
 }
 
-void
+static void
 ned_instruction_swap(struct NEDstate * state)
 {
     uint32_t temp1 = stack_pop(state->active_thread);
 ned_instruction_swap(struct NEDstate * state)
 {
     uint32_t temp1 = stack_pop(state->active_thread);
@@ -266,7 +195,7 @@ ned_instruction_swap(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_brz(struct NEDstate * state)
 {
     uint32_t new_pc = stack_pop(state->active_thread);
 ned_instruction_brz(struct NEDstate * state)
 {
     uint32_t new_pc = stack_pop(state->active_thread);
@@ -277,7 +206,7 @@ ned_instruction_brz(struct NEDstate * state)
     }
 }
 
     }
 }
 
-void
+static void
 ned_instruction_load(struct NEDstate * state)
 {
     uint32_t address = stack_pop(state->active_thread);
 ned_instruction_load(struct NEDstate * state)
 {
     uint32_t address = stack_pop(state->active_thread);
@@ -285,7 +214,7 @@ ned_instruction_load(struct NEDstate * state)
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
     set_psw_flags(stack_r(state->active_thread,0), state);
 }
 
-void
+static void
 ned_instruction_store(struct NEDstate * state)
 {
     uint32_t address = stack_pop(state->active_thread);
 ned_instruction_store(struct NEDstate * state)
 {
     uint32_t address = stack_pop(state->active_thread);
@@ -293,14 +222,14 @@ ned_instruction_store(struct NEDstate * state)
     ram_w_word(state, address, data);
 }
 
     ram_w_word(state, address, data);
 }
 
-void
+static void
 ned_instruction_halt(struct NEDstate * state)
 {
     printf("Halting.\n");
     state->halted = true;
 }
 
 ned_instruction_halt(struct NEDstate * state)
 {
     printf("Halting.\n");
     state->halted = true;
 }
 
-void
+static void
 execute_syllable(struct NEDstate * state, enum syllables syllable)
 {
     if (syllable & 0b100000) { /* Check the first bit of the syllable. 1 means IM_x. */
 execute_syllable(struct NEDstate * state, enum syllables syllable)
 {
     if (syllable & 0b100000) { /* Check the first bit of the syllable. 1 means IM_x. */
@@ -319,9 +248,9 @@ execute_syllable(struct NEDstate * state, enum syllables syllable)
             case NOT:       ned_instruction_not(state);     break;
             case XOR:       ned_instruction_xor(state);     break;
             case ADD:       ned_instruction_add(state);     break;
             case NOT:       ned_instruction_not(state);     break;
             case XOR:       ned_instruction_xor(state);     break;
             case ADD:       ned_instruction_add(state);     break;
-            case MVSTCK:    ned_instruction_mvstck(state);  break;
+            case MVSTCK:    /* Intentionally blank */       break;
             case SHIFT:     ned_instruction_shift(state);   break;
             case SHIFT:     ned_instruction_shift(state);   break;
-            case CMPSWP:    /* TODO */                      break;
+            case CMPSWP:    /* Intentionally blank */       break;
             case TEST:      ned_instruction_test(state);    break;
             case JMP:       ned_instruction_jmp(state);     break;
             case SWAP:      ned_instruction_swap(state);    break;
             case TEST:      ned_instruction_test(state);    break;
             case JMP:       ned_instruction_jmp(state);     break;
             case SWAP:      ned_instruction_swap(state);    break;
@@ -338,14 +267,14 @@ execute_syllable(struct NEDstate * state, enum syllables syllable)
     }
 }
 
     }
 }
 
-uint8_t
+static uint8_t
 extract_syllable_from_word(uint32_t word, uint8_t index)
 {
     uint32_t mask = 0b111111 << 6*(4-index);
     return (word & mask) >> 6*(4-index);
 }
 
 extract_syllable_from_word(uint32_t word, uint8_t index)
 {
     uint32_t mask = 0b111111 << 6*(4-index);
     return (word & mask) >> 6*(4-index);
 }
 
-void
+static void
 parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
                 struct nlist ** symbol_table, uint32_t * symbol_count)
 {
 parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
                 struct nlist ** symbol_table, uint32_t * symbol_count)
 {
@@ -435,39 +364,56 @@ parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
 }
 
 struct NEDstate *
 }
 
 struct NEDstate *
-init_simulator(void)
+init_simulator(char * input_file, const uint8_t * blob, const size_t * blob_size)
 {
     struct NEDstate * state = malloc(sizeof(struct NEDstate));
     state->hack = malloc(sizeof(struct NEDhack));
 {
     struct NEDstate * state = malloc(sizeof(struct NEDstate));
     state->hack = malloc(sizeof(struct NEDhack));
-    for (size_t i=0; i < THREAD_COUNT; i++) {
-        state->thread[i] = malloc(sizeof(struct NEDthread));
-        state->thread[i]->psw = malloc(sizeof(struct NEDpsw));
-    }
+    state->thread[0] = malloc(sizeof(struct NEDthread));
+    state->thread[0]->psw = malloc(sizeof(struct NEDpsw));
     state->thread[0]->pc = 0;
     state->thread[0]->sc = 0;
     state->thread[0]->sp = 0;
     state->thread[0]->psw->zero = false;
     state->thread[0]->psw->negative = false;
     state->thread[0]->pc = 0;
     state->thread[0]->sc = 0;
     state->thread[0]->sp = 0;
     state->thread[0]->psw->zero = false;
     state->thread[0]->psw->negative = false;
-    state->thread[0]->pc = 0x20000000;  /* Data region starts 512 MB into address space. */
-    state->active_thread = state->thread[0];  /* By convention, use thread 0 for init. */
+    state->thread[0]->pc = RAM_BASE_ADDRESS;
+    state->active_thread = state->thread[0];
     state->halted = false;
     state->hack->resume_word = false;
 
     state->halted = false;
     state->hack->resume_word = false;
 
-// TODO: This needs to be passed in as a CLI option.
-#define AOUT_PATH "./test.out"
-
     /* Load an initial image into memory. */
     /* Load an initial image into memory. */
-    uint32_t address = 0x20000000;
-    struct exec aout_exec;
-    struct nlist * symbol_table;
-    uint32_t symbol_count;
-    FILE * input = NULL;
-    if ((input = fopen(AOUT_PATH, "r")) == NULL) {
-        fprintf(stderr, "ERROR: %s: %s\n", AOUT_PATH, strerror(errno));
+    if (input_file) {
+        struct exec aout_exec;
+        struct nlist * symbol_table;
+        uint32_t symbol_count;
+        FILE * input = NULL;
+        if ((input = fopen(input_file, "r")) == NULL) {
+            fprintf(stderr, "ERROR: %s: %s\n", input_file, strerror(errno));
+            state->halted = true;
+        }
+        parse_aout_file(input, &aout_exec, state->ram, &symbol_table, &symbol_count);
+        fclose(input);
+        for (size_t i=0; i < symbol_count; i++) {
+            free(symbol_table[i].n_un.n_name);
+        }
+        free(symbol_table);
+    } else if (blob && blob_size) {
+        if (*blob_size <= RAM_LENGTH) {
+            size_t index = *blob_size;
+            while (index) {
+                index--;
+                state->ram[index] = blob[index];
+            }
+        } else {
+            fprintf(stderr,
+                    "ERROR: Built-in NED1 program is larger than simulated RAM (%zu vs %d bytes)\n",
+                    *blob_size, RAM_LENGTH
+            );
+            state->halted = true;
+        }
+    } else {
+        fprintf(stderr, "ERROR: No suitable binary image passed when initializing simulator.\n");
         state->halted = true;
     }
         state->halted = true;
     }
-    parse_aout_file(input, &aout_exec, &(state->ram[address]), &symbol_table, &symbol_count);
-    fclose(input);
 
     return state;
 }
 
     return state;
 }
@@ -481,8 +427,10 @@ run_simulator(struct NEDstate * state)
     uint32_t iw;
     if (state->hack->resume_word) {
         iw = state->hack->iw;
     uint32_t iw;
     if (state->hack->resume_word) {
         iw = state->hack->iw;
+        state->active_thread->sc++;
     } else {
         iw = fetch_instruction_word(state);
     } else {
         iw = fetch_instruction_word(state);
+        state->active_thread->sc = 0;
     }
 
     /* Decode instruction word format and execute. */
     }
 
     /* Decode instruction word format and execute. */
@@ -490,19 +438,18 @@ run_simulator(struct NEDstate * state)
         stack_push(state->active_thread, (iw << 1));
     } else if ((iw & (0b11 << 30)) == 0) { /* Instruction word is type C. */
         uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
         stack_push(state->active_thread, (iw << 1));
     } else if ((iw & (0b11 << 30)) == 0) { /* Instruction word is type C. */
         uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
-        state->active_thread->sc++; // TODO: Should this be part of extract_syllable_from_word()? After all, incrementing the PC is done in fetch_instruction_word().
-        uint32_t pre_execution_pc = state->active_thread->pc; // TODO: This is so we can catch JMP/JSR/etc subroutines that need the SC to be reset to zero.
+        /* The following variable allows us to catch JMP/BRZ instructions that */
+        /* jump to a new PC and need the SC reset.                             */
+        uint32_t pre_execution_pc = state->active_thread->pc;
         execute_syllable(state, syllable);
         if (state->active_thread->pc != pre_execution_pc) {
         execute_syllable(state, syllable);
         if (state->active_thread->pc != pre_execution_pc) {
-            // Jumped to a new address, so prepare to execute a new instruction word.
-            state->active_thread->sc = 0;
+            /* Jumped to a new address, so prepare to execute a new instruction word. */
             state->hack->resume_word = false;
             state->hack->resume_word = false;
-        } else if (state->active_thread->sc >= SPW) {
-            // Just executed the last syllable in this word, time to follow the PC to the next word.
-            state->active_thread->sc = 0;
+        } else if (state->active_thread->sc >= SPW-1) {
+            /* Just executed the last syllable in this word, follow the PC to the next word. */
             state->hack->resume_word = false;
         } else {
             state->hack->resume_word = false;
         } else {
-            // More syllables remain to be executed in this instruction word.
+            /* More syllables remain to be executed in this instruction word. */
             state->hack->resume_word = true;
             state->hack->iw = iw;
         }
             state->hack->resume_word = true;
             state->hack->iw = iw;
         }