Made most functions in NEDsim simulator static.
[screensavers] / hacks / NEDsim / simulator.c
CommitLineData
84b74595
AT
1/* (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com> */
2/* See LICENSE.txt file for copyright and license details. */
3
4/* -------------------------------------------------------------------------- */
5/* NED1 Simulator */
6/* -------------------------------------------------------------------------- */
7
84b74595
AT
8#include <stdio.h>
9#include <stdint.h>
10#include <inttypes.h>
11#include <stdlib.h>
12#include <stdbool.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <string.h>
16#include <errno.h>
17#include <time.h>
18#include <termios.h>
19#include <signal.h>
20#include <sys/socket.h>
21#include <sys/types.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <netdb.h>
25
26#include "a.out.h"
27#include "simulator.h"
28
51228e25 29static uint32_t
84b74595
AT
30generate_binary_psw(struct NEDstate * state)
31{
32 uint32_t psw = 0;
33 if (state->active_thread->psw->zero) psw |= 0b1;
34 if (state->active_thread->psw->negative) psw |= 0b10;
35 return psw;
36}
37
51228e25 38static void
84b74595
AT
39ram_w_byte(struct NEDstate * state, uint32_t address, uint8_t data)
40{
d87b1e06 41 state->ram[address-RAM_BASE_ADDRESS] = data;
84b74595
AT
42}
43
51228e25 44static uint8_t
84b74595
AT
45ram_r_byte(struct NEDstate * state, uint32_t address)
46{
d87b1e06 47 return state->ram[address-RAM_BASE_ADDRESS];
84b74595
AT
48}
49
50/* For now, with only a terminal for IO, we pick off IO requests when accessing RAM. */
84b74595 51
51228e25 52static void
84b74595
AT
53ram_w_word(struct NEDstate * state, uint32_t address, uint32_t data)
54{
d87b1e06 55 if (address >= RAM_BASE_ADDRESS) {
84b74595
AT
56 for (int i=3; i>=0; i--) {
57 uint8_t tmp_byte = ((data >> (8*(3-i))) & 0xff);
58 ram_w_byte(state,address+i,tmp_byte);
59 }
60 }
61}
62
63uint32_t
64ram_r_word(struct NEDstate * state, uint32_t address)
65{
66 if (address == 0x0) { /* Zero register */
67 return 0b0;
68 } else if (address == 0x4) { /* 0x80000000 register */
69 return 0x80000000;
70 } else if (address == 0x8) { /* PC register */
71 return state->active_thread->pc;
72 } else if (address == 0xC) { /* PSW register */
73 return generate_binary_psw(state);
d87b1e06 74 } else if (address >= RAM_BASE_ADDRESS) { /* RAM */
84b74595
AT
75 uint32_t word = 0;
76 for (int i=0; i<4; i++) word |= (ram_r_byte(state,address+i)) << (8*(3-i));
77 return word;
78 }
79 return 0b0;
80}
81
51228e25 82static uint32_t
84b74595
AT
83fetch_instruction_word(struct NEDstate * state)
84{
85 uint32_t word = ram_r_word(state, state->active_thread->pc);
86 state->active_thread->pc += BPW;
87 return word;
88}
89
51228e25 90static void
84b74595
AT
91stack_w(struct NEDthread * thread, uint32_t value, uint8_t offset)
92{
93 thread->stack[thread->sp - (offset + 1)] = value;
94}
95
51228e25 96static uint32_t
84b74595
AT
97stack_r(struct NEDthread * thread, uint8_t offset)
98{
99 return thread->stack[thread->sp - (offset + 1)];
100}
101
51228e25 102static void
84b74595
AT
103stack_push(struct NEDthread * thread, uint32_t value)
104{
105 thread->stack[thread->sp++] = value;
106}
107
51228e25 108static uint32_t
84b74595
AT
109stack_pop(struct NEDthread * thread)
110{
111 return thread->stack[--thread->sp];
112}
113
51228e25 114static void
84b74595
AT
115set_psw_flags(uint32_t word, struct NEDstate * state)
116{
117 if (word == 0) {
118 state->active_thread->psw->zero = true;
119 } else {
120 state->active_thread->psw->zero = false;
121 }
122 if (word & 0x80000000) {
123 state->active_thread->psw->negative = true;
124 } else {
125 state->active_thread->psw->negative = false;
126 }
127}
128
51228e25 129static void
84b74595
AT
130ned_instruction_and(struct NEDstate * state)
131{
132 uint32_t operand1 = stack_pop(state->active_thread);
133 uint32_t operand2 = stack_pop(state->active_thread);
134 stack_push(state->active_thread, (operand1 & operand2));
135 set_psw_flags(stack_r(state->active_thread,0), state);
136}
137
51228e25 138static void
84b74595
AT
139ned_instruction_or(struct NEDstate * state)
140{
141 uint32_t operand1 = stack_pop(state->active_thread);
142 uint32_t operand2 = stack_pop(state->active_thread);
143 stack_push(state->active_thread, (operand1 | operand2));
144 set_psw_flags(stack_r(state->active_thread,0), state);
145}
146
51228e25 147static void
84b74595
AT
148ned_instruction_not(struct NEDstate * state)
149{
150 stack_push(state->active_thread, ~stack_pop(state->active_thread));
151 set_psw_flags(stack_r(state->active_thread,0), state);
152}
153
51228e25 154static void
84b74595
AT
155ned_instruction_xor(struct NEDstate * state)
156{
157 uint32_t operand1 = stack_pop(state->active_thread);
158 uint32_t operand2 = stack_pop(state->active_thread);
159 stack_push(state->active_thread, (operand1 ^ operand2));
160 set_psw_flags(stack_r(state->active_thread,0), state);
161}
162
51228e25 163static void
84b74595
AT
164ned_instruction_add(struct NEDstate * state)
165{
166 uint32_t operand1 = stack_pop(state->active_thread);
167 uint32_t operand2 = stack_pop(state->active_thread);
168 stack_push(state->active_thread, (operand1 + operand2));
169 set_psw_flags(stack_r(state->active_thread,0), state);
170}
171
51228e25 172static void
84b74595
AT
173ned_instruction_shift(struct NEDstate * state)
174{
175 /* TODO: Bounds check: Either all inputs are valid OR shift_by < 32. */
176 /* I guess this also depends if I'm shifting-and-dropping, or barrel-shifting. */
177 /* How should I pad for a right shift if I shift-and-drop? Sign extend? */
178 uint32_t shift_by = stack_pop(state->active_thread);
179 uint32_t word = stack_pop(state->active_thread);
180 if (shift_by & 0x80000000) {
181 stack_push(state->active_thread, (word << (shift_by & 0x7fffffff)));
182 } else {
183 stack_push(state->active_thread, (word >> shift_by));
184 }
185 set_psw_flags(stack_r(state->active_thread,0), state);
186}
187
51228e25 188static void
84b74595
AT
189ned_instruction_test(struct NEDstate * state)
190{
191 uint32_t word = stack_pop(state->active_thread);
192 set_psw_flags(word, state);
193}
194
51228e25 195static void
84b74595
AT
196ned_instruction_jmp(struct NEDstate * state)
197{
198 state->active_thread->pc = stack_pop(state->active_thread);
199 // The SC is caught and reset by the main loop since the PC changed.
200}
201
51228e25 202static void
84b74595
AT
203ned_instruction_swap(struct NEDstate * state)
204{
205 uint32_t temp1 = stack_pop(state->active_thread);
206 uint32_t temp2 = stack_pop(state->active_thread);
207 stack_push(state->active_thread, temp1);
208 stack_push(state->active_thread, temp2);
209 set_psw_flags(stack_r(state->active_thread,0), state);
210}
211
51228e25 212static void
84b74595
AT
213ned_instruction_brz(struct NEDstate * state)
214{
215 uint32_t new_pc = stack_pop(state->active_thread);
216 uint32_t test_word = stack_pop(state->active_thread);
217 if (test_word == 0) {
218 state->active_thread->pc = new_pc;
219 // The SC is caught and reset by the main loop since the PC changed.
220 }
221}
222
51228e25 223static void
84b74595
AT
224ned_instruction_load(struct NEDstate * state)
225{
226 uint32_t address = stack_pop(state->active_thread);
227 stack_push(state->active_thread, ram_r_word(state, address));
228 set_psw_flags(stack_r(state->active_thread,0), state);
229}
230
51228e25 231static void
84b74595
AT
232ned_instruction_store(struct NEDstate * state)
233{
234 uint32_t address = stack_pop(state->active_thread);
235 uint32_t data = stack_pop(state->active_thread);
236 ram_w_word(state, address, data);
237}
238
51228e25 239static void
84b74595
AT
240ned_instruction_halt(struct NEDstate * state)
241{
242 printf("Halting.\n");
243 state->halted = true;
244}
245
51228e25 246static void
84b74595
AT
247execute_syllable(struct NEDstate * state, enum syllables syllable)
248{
249 if (syllable & 0b100000) { /* Check the first bit of the syllable. 1 means IM_x. */
250 stack_push(state->active_thread, (uint32_t)(syllable & 0b11111));
251 } else if (syllable & 0b10000) { /* 1 in 2nd bit means LDSP+x or STSP+x instruction. */
252 if (syllable & 0b1000) { /* LDSP+x */
253 stack_push(state->active_thread,stack_r(state->active_thread,(syllable & 0b111)));
254 set_psw_flags(stack_r(state->active_thread,0), state);
255 } else { /* STSP+x */
256 stack_w(state->active_thread,stack_pop(state->active_thread),(syllable & 0b111));
257 }
258 } else {
259 switch (syllable) {
260 case AND: ned_instruction_and(state); break;
261 case OR: ned_instruction_or(state); break;
262 case NOT: ned_instruction_not(state); break;
263 case XOR: ned_instruction_xor(state); break;
264 case ADD: ned_instruction_add(state); break;
d87b1e06 265 case MVSTCK: /* Intentionally blank */ break;
84b74595 266 case SHIFT: ned_instruction_shift(state); break;
d87b1e06 267 case CMPSWP: /* Intentionally blank */ break;
84b74595
AT
268 case TEST: ned_instruction_test(state); break;
269 case JMP: ned_instruction_jmp(state); break;
270 case SWAP: ned_instruction_swap(state); break;
271 case BRZ: ned_instruction_brz(state); break;
272 case LOAD: ned_instruction_load(state); break;
273 case STORE: ned_instruction_store(state); break;
274 case NOP: /* Intentionally blank */ break;
275 case HALT: ned_instruction_halt(state); break;
276 default:
277 printf("ERROR: Attempted to execute illegal syllable: 0o%o\n", syllable);
278 state->halted = true;
279 break;
280 }
281 }
282}
283
51228e25 284static uint8_t
84b74595
AT
285extract_syllable_from_word(uint32_t word, uint8_t index)
286{
287 uint32_t mask = 0b111111 << 6*(4-index);
288 return (word & mask) >> 6*(4-index);
289}
290
51228e25 291static void
84b74595
AT
292parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
293 struct nlist ** symbol_table, uint32_t * symbol_count)
294{
295 uint32_t read_count = 0;
296
297 /* Read in and check the a.out header. */
298 for (uint32_t i=0; i<8; i++) {
299 switch (i) {
300 case 0: read_count = fread(&(aout_exec->a_midmag), 4, 1, input); break;
301 case 1: read_count = fread(&(aout_exec->a_text), 4, 1, input); break;
302 case 2: read_count = fread(&(aout_exec->a_data), 4, 1, input); break;
303 case 3: read_count = fread(&(aout_exec->a_bss), 4, 1, input); break;
304 case 4: read_count = fread(&(aout_exec->a_syms), 4, 1, input); break;
305 case 5: read_count = fread(&(aout_exec->a_entry), 4, 1, input); break;
306 case 6: read_count = fread(&(aout_exec->a_trsize), 4, 1, input); break;
307 case 7: read_count = fread(&(aout_exec->a_drsize), 4, 1, input); break;
308 }
309 if (read_count != 1) {
310 fprintf(stderr, "ERROR: Invalid a.out header.\n");
311 exit(EXIT_FAILURE);
312 }
313 }
314 if (N_BADMAG(*aout_exec)) {
315 fprintf(stderr, "ERROR: Invalid magic number in a.out header.\n");
316 exit(EXIT_FAILURE);
317 } else if (N_GETMID(*aout_exec) != MID_NED) {
318 fprintf(stderr, "ERROR: Executable not intended for NED Machine ID.\n");
319 exit(EXIT_FAILURE);
320 }
321
322 /* Read in the text segment. */
323 uint32_t text_segment_size = (N_DATOFF(*aout_exec) - N_TXTOFF(*aout_exec));
324 read_count = fread(text_segment, 1, text_segment_size, input);
325 if (read_count != text_segment_size) {
326 fprintf(stderr, "ERROR: Failed to read entire text segment.\n");
327 exit(EXIT_FAILURE);
328 }
329
330 /* Correct the byte order. */
331 for (uint32_t i=0; i < (text_segment_size / 4); i++) {
332 uint8_t temp_word[4];
333 for (uint8_t j=0; j<4; j++) temp_word[j] = text_segment[((i*4)+j)];
334 for (uint8_t j=0; j<4; j++) text_segment[((i*4)+j)] = temp_word[(3-j)];
335 }
336
337 /* Read in the symbol table. */
338 *symbol_count = ((N_STROFF(*aout_exec) - N_SYMOFF(*aout_exec)) / 20); /* 20 bytes per symbol. */
339 *symbol_table = malloc((*symbol_count) * sizeof(struct nlist));
340 for (uint32_t i=0; i < *symbol_count; i++) {
341 for (uint32_t j=0; j<5; j++) {
342 switch (j) {
343 case 0: read_count = fread(&((*symbol_table)[i].n_un.n_strx), 4, 1, input); break;
344 case 1: read_count = fread(&((*symbol_table)[i].n_type), 4, 1, input); break;
345 case 2: read_count = fread(&((*symbol_table)[i].n_other), 4, 1, input); break;
346 case 3: read_count = fread(&((*symbol_table)[i].n_desc), 4, 1, input); break;
347 case 4: read_count = fread(&((*symbol_table)[i].n_value), 4, 1, input); break;
348 }
349 if (read_count != 1) {
350 fprintf(stderr, "ERROR: Unable to read entire symbol table.\n");
351 exit(EXIT_FAILURE);
352 }
353 }
354 }
355
356 /* Read in the string table and update the symbol table entries with pointers to new strings. */
357 uint32_t string_table_size;
358 read_count = fread(&string_table_size, 4, 1, input);
359 if (read_count != 1) {
360 fprintf(stderr, "ERROR: Failed to read string table size.\n");
361 exit(EXIT_FAILURE);
362 }
363 for (uint32_t i=0; i < *symbol_count; i++) {
364 uint32_t len = 0;
365 if (i < ((*symbol_count)-1)) {
366 len = ((*symbol_table)[i+1].n_un.n_strx - (*symbol_table)[i].n_un.n_strx);
367 } else {
368 len = (string_table_size - (*symbol_table)[i].n_un.n_strx);
369 }
370 (*symbol_table)[i].n_un.n_name = malloc(len);
371 read_count = fread((*symbol_table)[i].n_un.n_name, 1, len, input);
372 if (read_count != len) {
373 fprintf(stderr, "ERROR: Failed to read a string from the string table.\n");
374 exit(EXIT_FAILURE);
375 }
376 }
377
378}
379
380struct NEDstate *
5923644e 381init_simulator(char * input_file)
84b74595
AT
382{
383 struct NEDstate * state = malloc(sizeof(struct NEDstate));
384 state->hack = malloc(sizeof(struct NEDhack));
d87b1e06
AT
385 state->thread[0] = malloc(sizeof(struct NEDthread));
386 state->thread[0]->psw = malloc(sizeof(struct NEDpsw));
84b74595
AT
387 state->thread[0]->pc = 0;
388 state->thread[0]->sc = 0;
389 state->thread[0]->sp = 0;
390 state->thread[0]->psw->zero = false;
391 state->thread[0]->psw->negative = false;
d87b1e06
AT
392 state->thread[0]->pc = RAM_BASE_ADDRESS;
393 state->active_thread = state->thread[0];
84b74595
AT
394 state->halted = false;
395 state->hack->resume_word = false;
396
84b74595 397 /* Load an initial image into memory. */
84b74595
AT
398 struct exec aout_exec;
399 struct nlist * symbol_table;
400 uint32_t symbol_count;
401 FILE * input = NULL;
5923644e
AT
402 if ((input = fopen(input_file, "r")) == NULL) {
403 fprintf(stderr, "ERROR: %s: %s\n", input_file, strerror(errno));
84b74595
AT
404 state->halted = true;
405 }
d87b1e06 406 parse_aout_file(input, &aout_exec, state->ram, &symbol_table, &symbol_count);
84b74595 407 fclose(input);
d87b1e06
AT
408 for (size_t i=0; i < symbol_count; i++) {
409 free(symbol_table[i].n_un.n_name);
410 }
411 free(symbol_table);
84b74595
AT
412
413 return state;
414}
415
416struct NEDstate *
417run_simulator(struct NEDstate * state)
418{
419 if (state->halted) return state;
420
421 /* Fetch instruction word. */
422 uint32_t iw;
423 if (state->hack->resume_word) {
424 iw = state->hack->iw;
425 } else {
426 iw = fetch_instruction_word(state);
427 }
428
429 /* Decode instruction word format and execute. */
430 if (iw & (0b1 << 31)) { /* Instruction word is type A. */
431 stack_push(state->active_thread, (iw << 1));
432 } else if ((iw & (0b11 << 30)) == 0) { /* Instruction word is type C. */
433 uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
434 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().
435 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.
436 execute_syllable(state, syllable);
437 if (state->active_thread->pc != pre_execution_pc) {
438 // Jumped to a new address, so prepare to execute a new instruction word.
439 state->active_thread->sc = 0;
440 state->hack->resume_word = false;
441 } else if (state->active_thread->sc >= SPW) {
442 // Just executed the last syllable in this word, time to follow the PC to the next word.
443 state->active_thread->sc = 0;
444 state->hack->resume_word = false;
445 } else {
446 // More syllables remain to be executed in this instruction word.
447 state->hack->resume_word = true;
448 state->hack->iw = iw;
449 }
450 } else {
451 state->halted = true;
452 fprintf(stderr, "WARNING: Halting due to attempted execution of illegal instruction.\n");
453 }
454
455 return state;
456}