* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
#include "../common/a.out.h"
/* Number of stack words. */
#define STACK_LENGTH 1048576
/* Number of bytes of RAM. */
#define RAM_LENGTH 1073741824
/* Number of hardware threads. */
/* Number of syllables per word. */
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. */
uint32_t stack
[STACK_LENGTH
];
struct DBGthread
* debug
;
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
;
print_usage(char ** argv
)
printf( "NED Simulator v%d (www.subgeniuskitty.com)\n"
"Usage: %s -i <file> [-s <hz>} [-t <file>] [-a <address> -p <port>]\n"
" -h Help (prints this message)\n"
" -i <file> Specify a binary image file to load in RAM.\n"
" -s <int hz, optional> Frequency of simulated system clock.\n"
" Allowable values are 1 <= clock <= 1,000,000,000.\n"
" -t <file, optional> Saves a trace of JMP and BRZ syllables to file.\n"
" -a <ip-addr, optional> IP address of nedfp instance. Enables front panel output.\n"
" -p <port, optional> Port of nedfp instance. Required when '-i' specified.\n"
thread_init(struct NEDthread
* thread
)
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;
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. */
tcsetattr(STDIN_FILENO
, TCSANOW
, &options
);
unset_terminal_mode(void)
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
);
FD_SET(STDIN_FILENO
, &read_fds
);
int retval
= select(1, &read_fds
, NULL
, NULL
, &timeout
);
/* TODO: How do I want to handle this error? */
generate_binary_psw(struct NEDstate
* state
)
if (state
->active_thread
->psw
->zero
) psw
|= 0b1;
if (state
->active_thread
->psw
->negative
) psw
|= 0b10;
ram_w_byte(struct NEDstate
* state
, uint32_t address
, uint8_t data
)
state
->ram
[address
] = data
;
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. */
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 */
} 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
);
ram_r_word(struct NEDstate
* state
, uint32_t address
)
if (address
== 0x0) { /* Zero register */
} else if (address
== 0x4) { /* 0x80000000 register */
} 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. */
} else if (address
== 0x8000008) { /* SLU: RBUF */
if (is_stdin_nonempty()) {
} else if (address
== 0x800000C) { /* SLU: RCSR */
if (is_stdin_nonempty()) {
} else if (address
>= 0x20000000) { /* RAM */
state
->active_thread
->debug
->ram_reads
+= 1;
for (int i
=0; i
<4; i
++) word
|= (ram_r_byte(state
,address
+i
)) << (8*(3-i
));
fetch_instruction_word(struct NEDstate
* state
)
uint32_t word
= ram_r_word(state
, state
->active_thread
->pc
);
state
->active_thread
->pc
+= BPW
;
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("########## 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)) {
if (i
== (state
->active_thread
->sp
- 1)) {
printf(" TOS> %ld: 0x%X\n", i
, state
->active_thread
->stack
[i
]);
printf(" %ld: 0x%X\n", i
, state
->active_thread
->stack
[i
]);
stack_w(struct NEDthread
* thread
, uint32_t value
, uint8_t offset
)
thread
->stack
[thread
->sp
- (offset
+ 1)] = value
;
stack_r(struct NEDthread
* thread
, uint8_t offset
)
return thread
->stack
[thread
->sp
- (offset
+ 1)];
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
;
stack_pop(struct NEDthread
* thread
)
return thread
->stack
[--thread
->sp
];
set_psw_flags(uint32_t word
, struct NEDstate
* state
)
state
->active_thread
->psw
->zero
= true;
state
->active_thread
->psw
->zero
= false;
state
->active_thread
->psw
->negative
= true;
state
->active_thread
->psw
->negative
= false;
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
);
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
);
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_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
);
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
);
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
];
printf("ERROR: Attempted MVSTCK to ID higher than THREAD_COUNT.\n");
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)));
stack_push(state
->active_thread
, (word
>> shift_by
));
set_psw_flags(stack_r(state
->active_thread
,0), state
);
ned_instruction_test(struct NEDstate
* state
)
uint32_t word
= stack_pop(state
->active_thread
);
set_psw_flags(word
, state
);
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;
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
);
ned_instruction_brz(struct NEDstate
* state
)
uint32_t new_pc
= stack_pop(state
->active_thread
);
uint32_t test_word
= stack_pop(state
->active_thread
);
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;
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
);
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
);
ned_instruction_halt(struct NEDstate
* state
)
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
);
stack_w(state
->active_thread
,stack_pop(state
->active_thread
),(syllable
& 0b111));
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;
printf("ERROR: Attempted to execute illegal syllable: 0o%o\n", syllable
);
extract_syllable_from_word(uint32_t word
, uint8_t index
)
uint32_t mask
= 0b111111 << 6*(4-index
);
return (word
& mask
) >> 6*(4-index
);
ned_sigint_handler(int sig
)
print_snapshot(NEDdebug
);
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;
struct timespec current_time
;
if(clock_gettime(CLOCK_MONOTONIC
, ¤t_time
)) {
fprintf(stderr
, "WARNING: Unable to sync to requested CPU speed.\n");
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;
parse_aout_file(FILE * input
, struct exec
* aout_exec
, uint8_t * text_segment
,
struct nlist
** symbol_table
, uint32_t * symbol_count
)
/* Read in and check the a.out header. */
for (uint32_t i
=0; i
<8; 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;
fprintf(stderr
, "ERROR: Invalid a.out header.\n");
if (N_BADMAG(*aout_exec
)) {
fprintf(stderr
, "ERROR: Invalid magic number in a.out header.\n");
} else if (N_GETMID(*aout_exec
) != MID_NED
) {
fprintf(stderr
, "ERROR: Executable not intended for NED Machine ID.\n");
/* 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");
/* Correct the byte order. */
for (uint32_t i
=0; i
< (text_segment_size
/ 4); i
++) {
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
++) {
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;
fprintf(stderr
, "ERROR: Unable to read entire symbol table.\n");
/* 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
);
fprintf(stderr
, "ERROR: Failed to read string table size.\n");
for (uint32_t i
=0; i
< *symbol_count
; i
++) {
if (i
< ((*symbol_count
)-1)) {
len
= ((*symbol_table
)[i
+1].n_un
.n_strx
- (*symbol_table
)[i
].n_un
.n_strx
);
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
);
fprintf(stderr
, "ERROR: Failed to read a string from the string table.\n");
send_front_panel_update(struct NEDstate
* state
, int sockfd
, uint32_t ram_address
,
const struct sockaddr
*to
, socklen_t tolen
)
* Datagram format (all entries are 4 bytes):
* Sequence number (See RFC1982)
* Currently executing word
* Top 16 RAM words from start address
seq_num
== 0xffffffff ? seq_num
= 0 : seq_num
++;
snapshot
[1] = ram_r_word(state
, (state
->active_thread
->pc
- 4));
snapshot
[2] = state
->active_thread
->pc
;
snapshot
[3] = (uint32_t) state
->active_thread
->sc
;
snapshot
[4] = generate_binary_psw(state
);
snapshot
[5] = state
->active_thread
->sp
;
int64_t sp
= state
->active_thread
->sp
;
for (int i
=0; i
<16; i
++) {
snapshot
[i
+6] = state
->active_thread
->stack
[sp
-i
];
snapshot
[22] = ram_address
;
for (int i
=0; i
<16; i
++) {
snapshot
[i
+23] = ram_r_word(state
,(ram_address
+ (i
* 4)));
if ((numbytes
= sendto(sockfd
, snapshot
, 156, 0, to
, tolen
)) == -1) {
fprintf(stderr
, "WARNING: Unable to send machine snapshot to front panel socket.\n");
main(int argc
, char ** argv
)
* Process command line arguments
uint32_t clock_period
= 1;
uint32_t fp_ram
= 0x40000000;
while ((c
= getopt(argc
,argv
,"hi:p:t:s:a:")) != -1) {
if ((input
= fopen(optarg
, "r")) == NULL
) {
fprintf(stderr
, "ERROR: %s: %s\n", optarg
, strerror(errno
));
if ((trace
= fopen(optarg
, "wx")) == NULL
) {
fprintf(stderr
, "ERROR: %s: %s\n", optarg
, strerror(errno
));
// TODO: What do I want to consider valid input?
// TODO: What do I want to consider valid input?
intmax_t freq
= strtoimax(optarg
, NULL
, 0);
if (1 <= freq
&& freq
<= 1000000000) {
clock_period
= ((1.0 / freq
) * 1000000000);
fprintf(stderr
, "WARNING: Clock period out of range.\n");
fprintf(stderr
, "ERROR: Must specify a binary image file with -i flag.\n");
if (fp_ip
!= NULL
&& fp_port
== NULL
) asprintf(&fp_port
, "4999");
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. */
signal(SIGINT
, ned_sigint_handler
);
/* Load an initial image into memory. */
uint32_t address
= 0x20000000;
struct nlist
* symbol_table
;
parse_aout_file(input
, &aout_exec
, &(state
->ram
[address
]), &symbol_table
, &symbol_count
);
/* Start up the front panel connections, if specified. */
struct addrinfo
* servinfo
;
memset(&hints
, 0, sizeof hints
);
hints
.ai_family
= AF_UNSPEC
;
hints
.ai_socktype
= SOCK_DGRAM
;
if ((rv
= getaddrinfo(fp_ip
, fp_port
, &hints
, &servinfo
)) != 0) {
fprintf(stderr
, "ERROR: getaddrinfo: %s\n", gai_strerror(rv
));
for(p
= servinfo
; p
!= NULL
; p
= p
->ai_next
) {
if ((sockfd
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
)) == -1) {
fprintf(stderr
, "ERROR: Failed to create socket\n");
/* Check for interrupt requests. */
/* Fetch new instruction word. */
iw
= fetch_instruction_word(state
);
/* Tracing, if enabled. */
if (next_pc
== 0) next_pc
= state
->active_thread
->pc
;
if (next_pc
!= (state
->active_thread
->pc
- BPW
)) {
/* A jump has occurred. */
asprintf(&trace_msg
, "Jump from 0x%08x to 0x%08x ",
(next_pc
- BPW
), (state
->active_thread
->pc
- BPW
)
fwrite(trace_msg
, strlen(trace_msg
), 1, trace
);
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
;
asprintf(&trace_msg
, "(%s).\n", symbol_string
);
fwrite(trace_msg
, strlen(trace_msg
), 1, trace
);
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. */
if (fp_ip
) send_front_panel_update(state
, sockfd
, fp_ram
, p
->ai_addr
, p
->ai_addrlen
);
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
++
if (fp_ip
) send_front_panel_update(state
, sockfd
, fp_ram
, p
->ai_addr
, p
->ai_addrlen
);
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;
fprintf(stderr
, "ERROR: Attempted to execute illegal instruction.\n");