Changed BRZ behavior to chomp a test word from stack instead of checking PSW_Z bit.
[ned1] / nedsim / nedsim.c
/*
* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <termios.h>
#include <signal.h>
#include "../common/a.out.h"
#define VERSION 4
/* Bytes per word. */
#define BPW 4
/* Number of stack words. */
#define STACK_LENGTH 1048576
/* Number of bytes of RAM. */
#define RAM_LENGTH 1073741824
/* Number of hardware threads. */
#define THREAD_COUNT 8
/* Number of syllables per word. */
#define SPW 5
struct DBGthread {
uint32_t max_stack; /* Highest stack entry reached during execution. */
uint32_t max_stack_address; /* Address of PC when highest stack entry was reached. */
uint64_t ram_reads; /* Number of words of RAM read during execution. */
uint64_t ram_writes; /* Number of words of RAM written during execution. */
uint64_t cycle_count; /* Total number of CPU cycles. One cycle corresponds */
/* to a single CPU action, either a WORD_x word */
/* or a single syllable from a five-syllable word. */
};
struct NEDpsw {
bool zero;
bool negative;
};
struct NEDthread {
uint32_t stack[STACK_LENGTH];
uint32_t sp;
uint32_t pc;
uint8_t sc;
struct NEDpsw * psw;
struct DBGthread * debug;
};
struct NEDstate {
uint8_t ram[RAM_LENGTH];
struct NEDthread * thread[THREAD_COUNT];
struct NEDthread * active_thread;
struct timespec * prev_instruction_clock;
uint32_t clock_period_ns;
};
/* This variable allows printing the processor's state from a signal handler. */
/* It should not be used for anything else. */
struct NEDstate * NEDdebug = NULL;
enum syllables {
MVSTCK = 0b00001111,
JMP = 0b00001110,
SWAP = 0b00001101,
ADD = 0b00001100,
XOR = 0b00001011,
NOT = 0b00001010,
OR = 0b00001001,
AND = 0b00001000,
BRZ = 0b00000111,
TEST = 0b00000110,
CMPSWP = 0b00000101,
SHIFT = 0b00000100,
STORE = 0b00000011,
LOAD = 0b00000010,
NOP = 0b00000001,
HALT = 0b00000000
};
void
print_usage(char ** argv)
{
printf( "NED Simulator v%d (www.subgeniuskitty.com)\n"
"Usage: %s -i <file>\n"
" -h Help (prints this message)\n"
" -i <file> Specify a binary image file to load in RAM.\n"
" -p <int ns, optional> Period in nanoseconds of simulated system clock.\n"
" Allowable values are 1 <= clock <= 1,000,000,000.\n"
" -t <file> Saves a trace of JMP and BRZ syllables to file.\n"
, VERSION, argv[0]
);
}
void
thread_init(struct NEDthread * thread)
{
thread->pc = 0;
thread->sc = 0;
thread->sp = 0;
thread->psw->zero = false;
thread->psw->negative = false;
thread->debug->max_stack = 0;
thread->debug->max_stack_address = 0;
thread->debug->ram_reads = 0;
thread->debug->ram_writes = 0;
thread->debug->cycle_count = 0;
}
void
set_terminal_mode(void)
{
struct termios options;
tcgetattr(STDIN_FILENO, &options);
/* Create a cbreak-like environment through the following options. */
options.c_lflag &= ~ECHO; /* Disable echoing of input characters. */
options.c_lflag &= ~ICANON; /* Disable cooked/line-oriented mode. */
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &options);
}
void
unset_terminal_mode(void)
{
struct termios options;
tcgetattr(STDIN_FILENO, &options);
/* Undo the changes made in set_terminal_mode(). */
options.c_lflag |= ECHO; /* Enable echoing of input characters. */
options.c_lflag |= ICANON; /* Enable cooked/line-oriented mode. */
options.c_cc[VMIN] = 1; /* Default value from /usr/src/sys/sys/ttydefaults.h */
options.c_cc[VTIME] = 0; /* Default value from /usr/src/sys/sys/ttydefaults.h */
tcsetattr(STDIN_FILENO, TCSANOW, &options);
}
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
generate_binary_psw(struct NEDstate * state)
{
uint32_t psw = 0;
if (state->active_thread->psw->zero) psw |= 0b1;
if (state->active_thread->psw->negative) psw |= 0b10;
return psw;
}
void
ram_w_byte(struct NEDstate * state, uint32_t address, uint8_t data)
{
state->ram[address] = data;
}
uint8_t
ram_r_byte(struct NEDstate * state, uint32_t address)
{
return state->ram[address];
}
/* 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
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) {
state->active_thread->debug->ram_writes += 1;
for (int i=3; i>=0; i--) {
uint8_t tmp_byte = ((data >> (8*(3-i))) & 0xff);
ram_w_byte(state,address+i,tmp_byte);
}
}
}
uint32_t
ram_r_word(struct NEDstate * state, uint32_t address)
{
if (address == 0x0) { /* Zero register */
return 0b0;
} else if (address == 0x4) { /* 0x80000000 register */
return 0x80000000;
} else if (address == 0x8) { /* PC register */
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 */
state->active_thread->debug->ram_reads += 1;
uint32_t word = 0;
for (int i=0; i<4; i++) word |= (ram_r_byte(state,address+i)) << (8*(3-i));
return word;
}
return 0b0;
}
uint32_t
fetch_instruction_word(struct NEDstate * state)
{
uint32_t word = ram_r_word(state, state->active_thread->pc);
state->active_thread->pc += BPW;
return word;
}
void
print_snapshot(struct NEDstate * state)
{
/*
* This function is sometimes called from a signal handler that depends
* on a global variable. That variable is set to NULL in the declaration.
* For that reason, check for a NULL pointer here.
*/
if (state == NULL) return;
printf("\n");
printf("########## Processor Statistics ##########\n");
printf("Stack Max: %u @ 0x%08x\n", state->active_thread->debug->max_stack,
state->active_thread->debug->max_stack_address
);
printf("Cycle Count: %lu\n", state->active_thread->debug->cycle_count);
printf("RAM Reads: %lu\n", state->active_thread->debug->ram_reads);
printf("RAM Writes: %lu\n", state->active_thread->debug->ram_writes);
printf("########## Processor Snapshot ##########\n");
printf("PC: 0x%X\n", state->active_thread->pc);
printf("PSW:\n %d Zero\n %d Negative\n",
state->active_thread->psw->zero,
state->active_thread->psw->negative
);
printf("Stack:\n ...\n");
int64_t i = state->active_thread->sp;
if ((i + 5) > (STACK_LENGTH - 1)) {
i = STACK_LENGTH - 1;
} else {
i += 5;
}
for ( ; i >= 0; i--) {
if (i == (state->active_thread->sp - 1)) {
printf(" TOS> %ld: 0x%X\n", i, state->active_thread->stack[i]);
} else {
printf(" %ld: 0x%X\n", i, state->active_thread->stack[i]);
}
}
}
void
stack_w(struct NEDthread * thread, uint32_t value, uint8_t offset)
{
thread->stack[thread->sp - (offset + 1)] = value;
}
uint32_t
stack_r(struct NEDthread * thread, uint8_t offset)
{
return thread->stack[thread->sp - (offset + 1)];
}
void
stack_push(struct NEDthread * thread, uint32_t value)
{
if (thread->sp > thread->debug->max_stack) {
thread->debug->max_stack = thread->sp;
thread->debug->max_stack_address = thread->pc;
}
thread->stack[thread->sp++] = value;
}
uint32_t
stack_pop(struct NEDthread * thread)
{
return thread->stack[--thread->sp];
}
void
set_psw_flags(uint32_t word, struct NEDstate * state)
{
if (word == 0) {
state->active_thread->psw->zero = true;
} else {
state->active_thread->psw->zero = false;
}
if (word & 0x80000000) {
state->active_thread->psw->negative = true;
} else {
state->active_thread->psw->negative = false;
}
}
void
ned_instruction_and(struct NEDstate * state)
{
uint32_t operand1 = stack_pop(state->active_thread);
uint32_t operand2 = stack_pop(state->active_thread);
stack_push(state->active_thread, (operand1 & operand2));
set_psw_flags(stack_r(state->active_thread,0), state);
}
void
ned_instruction_or(struct NEDstate * state)
{
uint32_t operand1 = stack_pop(state->active_thread);
uint32_t operand2 = stack_pop(state->active_thread);
stack_push(state->active_thread, (operand1 | operand2));
set_psw_flags(stack_r(state->active_thread,0), state);
}
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);
}
void
ned_instruction_xor(struct NEDstate * state)
{
uint32_t operand1 = stack_pop(state->active_thread);
uint32_t operand2 = stack_pop(state->active_thread);
stack_push(state->active_thread, (operand1 ^ operand2));
set_psw_flags(stack_r(state->active_thread,0), state);
}
void
ned_instruction_add(struct NEDstate * state)
{
uint32_t operand1 = stack_pop(state->active_thread);
uint32_t operand2 = stack_pop(state->active_thread);
stack_push(state->active_thread, (operand1 + operand2));
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");
print_snapshot(state);
unset_terminal_mode();
exit(EXIT_FAILURE);
}
}
void
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) {
stack_push(state->active_thread, (word << (shift_by & 0x7fffffff)));
} else {
stack_push(state->active_thread, (word >> shift_by));
}
set_psw_flags(stack_r(state->active_thread,0), state);
}
void
ned_instruction_test(struct NEDstate * state)
{
uint32_t word = stack_pop(state->active_thread);
set_psw_flags(word, state);
}
void
ned_instruction_jmp(struct NEDstate * state)
{
state->active_thread->pc = stack_pop(state->active_thread);
/* TODO: Find better way to communicate we're skipping ahead to the next word. */
state->active_thread->sc = 4;
}
void
ned_instruction_swap(struct NEDstate * state)
{
uint32_t temp1 = stack_pop(state->active_thread);
uint32_t temp2 = stack_pop(state->active_thread);
stack_push(state->active_thread, temp1);
stack_push(state->active_thread, temp2);
set_psw_flags(stack_r(state->active_thread,0), state);
}
void
ned_instruction_brz(struct NEDstate * state)
{
uint32_t new_pc = stack_pop(state->active_thread);
uint32_t test_word = stack_pop(state->active_thread);
if (test_word == 0) {
state->active_thread->pc = new_pc;
/* TODO: Find better way to communicate we're skipping ahead to the next word. */
state->active_thread->sc = 4;
}
}
void
ned_instruction_load(struct NEDstate * state)
{
uint32_t address = stack_pop(state->active_thread);
stack_push(state->active_thread, ram_r_word(state, address));
set_psw_flags(stack_r(state->active_thread,0), state);
}
void
ned_instruction_store(struct NEDstate * state)
{
uint32_t address = stack_pop(state->active_thread);
uint32_t data = stack_pop(state->active_thread);
ram_w_word(state, address, data);
}
void
ned_instruction_halt(struct NEDstate * state)
{
printf("Halting.\n");
print_snapshot(state);
unset_terminal_mode();
exit(EXIT_SUCCESS);
}
void
execute_syllable(struct NEDstate * state, enum syllables syllable)
{
if (syllable & 0b100000) { /* Check the first bit of the syllable. 1 means IM_x. */
stack_push(state->active_thread, (uint32_t)(syllable & 0b11111));
} else if (syllable & 0b10000) { /* 1 in 2nd bit means LDSP+x or STSP+x instruction. */
if (syllable & 0b1000) { /* LDSP+x */
stack_push(state->active_thread,stack_r(state->active_thread,(syllable & 0b111)));
set_psw_flags(stack_r(state->active_thread,0), state);
} else { /* STSP+x */
stack_w(state->active_thread,stack_pop(state->active_thread),(syllable & 0b111));
}
} else {
switch (syllable) {
case AND: ned_instruction_and(state); break;
case OR: ned_instruction_or(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 SHIFT: ned_instruction_shift(state); break;
case CMPSWP: /* TODO */ break;
case TEST: ned_instruction_test(state); break;
case JMP: ned_instruction_jmp(state); break;
case SWAP: ned_instruction_swap(state); break;
case BRZ: ned_instruction_brz(state); break;
case LOAD: ned_instruction_load(state); break;
case STORE: ned_instruction_store(state); break;
case NOP: /* Intentionally blank */ break;
case HALT: ned_instruction_halt(state); break;
default:
printf("ERROR: Attempted to execute illegal syllable: 0o%o\n", syllable);
break;
}
}
}
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);
}
void
ned_sigint_handler(int sig)
{
print_snapshot(NEDdebug);
unset_terminal_mode();
exit(EXIT_SUCCESS);
}
void
wait_for_next_clock_cycle(struct NEDstate * state)
{
state->prev_instruction_clock->tv_nsec += state->clock_period_ns;
while (state->prev_instruction_clock->tv_nsec > 1000000000) {
state->prev_instruction_clock->tv_nsec -= 1000000000;
state->prev_instruction_clock->tv_sec += 1;
}
while(1) {
struct timespec current_time;
if(clock_gettime(CLOCK_MONOTONIC, &current_time)) {
fprintf(stderr, "WARNING: Unable to sync to requested CPU speed.\n");
break;
} else {
bool sec_gt = current_time.tv_sec > state->prev_instruction_clock->tv_sec;
bool sec_eq = current_time.tv_sec == state->prev_instruction_clock->tv_sec;
bool nsec_gt = current_time.tv_nsec > state->prev_instruction_clock->tv_nsec;
if (sec_gt || (sec_eq && nsec_gt)) break;
}
}
}
void
parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
struct nlist ** symbol_table, uint32_t * symbol_count)
{
uint32_t read_count = 0;
/* Read in and check the a.out header. */
for (uint32_t i=0; i<8; i++) {
switch (i) {
case 0: read_count = fread(&(aout_exec->a_midmag), 4, 1, input); break;
case 1: read_count = fread(&(aout_exec->a_text), 4, 1, input); break;
case 2: read_count = fread(&(aout_exec->a_data), 4, 1, input); break;
case 3: read_count = fread(&(aout_exec->a_bss), 4, 1, input); break;
case 4: read_count = fread(&(aout_exec->a_syms), 4, 1, input); break;
case 5: read_count = fread(&(aout_exec->a_entry), 4, 1, input); break;
case 6: read_count = fread(&(aout_exec->a_trsize), 4, 1, input); break;
case 7: read_count = fread(&(aout_exec->a_drsize), 4, 1, input); break;
}
if (read_count != 1) {
fprintf(stderr, "ERROR: Invalid a.out header.\n");
exit(EXIT_FAILURE);
}
}
if (N_BADMAG(*aout_exec)) {
fprintf(stderr, "ERROR: Invalid magic number in a.out header.\n");
exit(EXIT_FAILURE);
} else if (N_GETMID(*aout_exec) != MID_NED) {
fprintf(stderr, "ERROR: Executable not intended for NED Machine ID.\n");
exit(EXIT_FAILURE);
}
/* Read in the text segment. */
uint32_t text_segment_size = (N_DATOFF(*aout_exec) - N_TXTOFF(*aout_exec));
read_count = fread(text_segment, 1, text_segment_size, input);
if (read_count != text_segment_size) {
fprintf(stderr, "ERROR: Failed to read entire text segment.\n");
exit(EXIT_FAILURE);
}
/* Correct the byte order. */
for (uint32_t i=0; i < (text_segment_size / 4); i++) {
uint8_t temp_word[4];
for (uint8_t j=0; j<4; j++) temp_word[j] = text_segment[((i*4)+j)];
for (uint8_t j=0; j<4; j++) text_segment[((i*4)+j)] = temp_word[(3-j)];
}
/* Read in the symbol table. */
*symbol_count = ((N_STROFF(*aout_exec) - N_SYMOFF(*aout_exec)) / 20); /* 20 bytes per symbol. */
*symbol_table = malloc((*symbol_count) * sizeof(struct nlist));
for (uint32_t i=0; i < *symbol_count; i++) {
for (uint32_t j=0; j<5; j++) {
switch (j) {
case 0: read_count = fread(&((*symbol_table)[i].n_un.n_strx), 4, 1, input); break;
case 1: read_count = fread(&((*symbol_table)[i].n_type), 4, 1, input); break;
case 2: read_count = fread(&((*symbol_table)[i].n_other), 4, 1, input); break;
case 3: read_count = fread(&((*symbol_table)[i].n_desc), 4, 1, input); break;
case 4: read_count = fread(&((*symbol_table)[i].n_value), 4, 1, input); break;
}
if (read_count != 1) {
fprintf(stderr, "ERROR: Unable to read entire symbol table.\n");
exit(EXIT_FAILURE);
}
}
}
/* Read in the string table and update the symbol table entries with pointers to new strings. */
uint32_t string_table_size;
read_count = fread(&string_table_size, 4, 1, input);
if (read_count != 1) {
fprintf(stderr, "ERROR: Failed to read string table size.\n");
exit(EXIT_FAILURE);
}
for (uint32_t i=0; i < *symbol_count; i++) {
uint32_t len = 0;
if (i < ((*symbol_count)-1)) {
len = ((*symbol_table)[i+1].n_un.n_strx - (*symbol_table)[i].n_un.n_strx);
} else {
len = (string_table_size - (*symbol_table)[i].n_un.n_strx);
}
(*symbol_table)[i].n_un.n_name = malloc(len);
read_count = fread((*symbol_table)[i].n_un.n_name, 1, len, input);
if (read_count != len) {
fprintf(stderr, "ERROR: Failed to read a string from the string table.\n");
exit(EXIT_FAILURE);
}
}
}
int
main(int argc, char ** argv)
{
/*
* Process command line arguments
*/
int c;
FILE * input = NULL;
FILE * trace = NULL;
uint32_t clock_period = 1;
while ((c = getopt(argc,argv,"i:p:t:h")) != -1) {
switch (c) {
case 't':
if ((trace = fopen(optarg, "wx")) == NULL) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
exit(EXIT_FAILURE);
}
break;
case 'i':
if ((input = fopen(optarg, "r")) == NULL) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
exit(EXIT_FAILURE);
}
break;
case 'p':
{
intmax_t temp_p = strtoimax(optarg, NULL, 0);
if (1 <= temp_p && temp_p <= 1000000000) {
clock_period = temp_p;
} else {
fprintf(stderr, "WARN: Clock period out of range.\n");
}
break;
}
case 'h':
print_usage(argv);
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
if (input == NULL) {
fprintf(stderr, "ERROR: Must specify a binary image file with -i flag.\n");
print_usage(argv);
exit(EXIT_FAILURE);
}
/*
* Initialization
*/
set_terminal_mode();
struct NEDstate * state = malloc(sizeof(struct NEDstate));
state->prev_instruction_clock = malloc(sizeof(struct timespec));
clock_gettime(CLOCK_MONOTONIC, state->prev_instruction_clock);
state->clock_period_ns = clock_period;
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[i]->debug = malloc(sizeof(struct DBGthread));
}
thread_init(state->thread[0]);
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. */
/* NEDdebug should only be used in ned_sigint_handler() to print state while exiting. */
NEDdebug = state;
signal(SIGINT, ned_sigint_handler);
/* Load an initial image into memory. */
uint32_t address = 0x20000000;
struct exec aout_exec;
struct nlist * symbol_table;
uint32_t symbol_count;
parse_aout_file(input, &aout_exec, &(state->ram[address]), &symbol_table, &symbol_count);
fclose(input);
/*
* Main Loop
*/
uint32_t iw = 0;
uint32_t next_pc = 0;
while (1) {
/* Check for interrupt requests. */
/* TODO */
/* Fetch new instruction word. */
iw = fetch_instruction_word(state);
/* Tracing, if enabled. */
if (trace) {
if (next_pc == 0) next_pc = state->active_thread->pc;
if (next_pc != (state->active_thread->pc - BPW)) {
/* A jump has occurred. */
char * trace_msg = NULL;
asprintf(&trace_msg, "Jump from 0x%08x to 0x%08x ",
(next_pc - BPW), (state->active_thread->pc - BPW)
);
if (trace_msg != NULL) {
fwrite(trace_msg, strlen(trace_msg), 1, trace);
free(trace_msg);
}
trace_msg = NULL;
char * symbol_string = NULL;
for (uint32_t i=0; i < symbol_count; i++) {
if ((state->active_thread->pc - BPW) == symbol_table[i].n_value) {
symbol_string = symbol_table[i].n_un.n_name;
break;
}
}
if (symbol_string) {
asprintf(&trace_msg, "(%s).\n", symbol_string);
if (trace_msg != NULL) {
fwrite(trace_msg, strlen(trace_msg), 1, trace);
free(trace_msg);
}
} else {
fwrite("\n", 1, 1, trace);
}
}
next_pc = state->active_thread->pc;
}
/* Decode instruction word format and execute. */
if (iw & (0b1 << 31)) { /* Instruction word is type A. */
wait_for_next_clock_cycle(state);
stack_push(state->active_thread, (iw << 1));
state->active_thread->debug->cycle_count += 1;
} else if ((iw & (0b11 << 30)) == 0) { /* Instruction word is type C. */
for (state->active_thread->sc = 0;
state->active_thread->sc < SPW;
state->active_thread->sc++
) {
wait_for_next_clock_cycle(state);
uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
execute_syllable(state, syllable);
state->active_thread->debug->cycle_count += 1;
}
} else {
fprintf(stderr, "ERROR: Attempted to execute illegal instruction.\n");
print_snapshot(state);
unset_terminal_mode();
exit(EXIT_FAILURE);
}
}
unset_terminal_mode();
exit(EXIT_SUCCESS);
}