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