]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/example-synacor/sim-main.c
sim: switch config.h usage to defs.h
[thirdparty/binutils-gdb.git] / sim / example-synacor / sim-main.c
1 /* Example synacor simulator.
2
3 Copyright (C) 2005-2021 Free Software Foundation, Inc.
4 Contributed by Mike Frysinger.
5
6 This file is part of simulators.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 /* This file contains the main simulator decoding logic. i.e. everything that
22 is architecture specific. */
23
24 /* This must come before any other includes. */
25 #include "defs.h"
26
27 #include "sim-main.h"
28 \f
29 /* Get the register number from the number. */
30 static unsigned16
31 register_num (SIM_CPU *cpu, unsigned16 num)
32 {
33 SIM_DESC sd = CPU_STATE (cpu);
34
35 if (num < 0x8000 || num >= 0x8008)
36 sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
37
38 return num & 0xf;
39 }
40
41 /* Helper to process immediates according to the ISA. */
42 static unsigned16
43 interp_num (SIM_CPU *cpu, unsigned16 num)
44 {
45 SIM_DESC sd = CPU_STATE (cpu);
46
47 if (num < 0x8000)
48 {
49 /* Numbers 0..32767 mean a literal value. */
50 TRACE_DECODE (cpu, "%#x is a literal", num);
51 return num;
52 }
53 else if (num < 0x8008)
54 {
55 /* Numbers 32768..32775 instead mean registers 0..7. */
56 TRACE_DECODE (cpu, "%#x is register R%i", num, num & 0xf);
57 return cpu->regs[num & 0xf];
58 }
59 else
60 {
61 /* Numbers 32776..65535 are invalid. */
62 TRACE_DECODE (cpu, "%#x is an invalid number", num);
63 sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
64 }
65 }
66 \f
67 /* Decode & execute a single instruction. */
68 void step_once (SIM_CPU *cpu)
69 {
70 SIM_DESC sd = CPU_STATE (cpu);
71 unsigned16 iw1, num1;
72 sim_cia pc = sim_pc_get (cpu);
73
74 iw1 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
75 TRACE_EXTRACT (cpu, "%04x: iw1: %#x", pc, iw1);
76 /* This never happens, but technically is possible in the ISA. */
77 num1 = interp_num (cpu, iw1);
78
79 if (num1 == 0)
80 {
81 /* halt: 0: Stop execution and terminate the program. */
82 TRACE_INSN (cpu, "HALT");
83 sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
84 }
85 else if (num1 == 1)
86 {
87 /* set: 1 a b: Set register <a> to the value of <b>. */
88 unsigned16 iw2, iw3, num2, num3;
89
90 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
91 num2 = register_num (cpu, iw2);
92 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
93 num3 = interp_num (cpu, iw3);
94 TRACE_EXTRACT (cpu, "SET %#x %#x", iw2, iw3);
95 TRACE_INSN (cpu, "SET R%i %#x", num2, num3);
96
97 TRACE_REGISTER (cpu, "R%i = %#x", num2, num3);
98 cpu->regs[num2] = num3;
99
100 pc += 6;
101 }
102 else if (num1 == 2)
103 {
104 /* push: 2 a: Push <a> onto the stack. */
105 unsigned16 iw2, num2;
106
107 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
108 num2 = interp_num (cpu, iw2);
109 TRACE_EXTRACT (cpu, "PUSH %#x", iw2);
110 TRACE_INSN (cpu, "PUSH %#x", num2);
111
112 sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, num2);
113 cpu->sp -= 2;
114 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
115
116 pc += 4;
117 }
118 else if (num1 == 3)
119 {
120 /* pop: 3 a: Remove the top element from the stack and write it into <a>.
121 Empty stack = error. */
122 unsigned16 iw2, num2, result;
123
124 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
125 num2 = register_num (cpu, iw2);
126 TRACE_EXTRACT (cpu, "POP %#x", iw2);
127 TRACE_INSN (cpu, "POP R%i", num2);
128 cpu->sp += 2;
129 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
130 result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
131
132 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
133 cpu->regs[num2] = result;
134
135 pc += 4;
136 }
137 else if (num1 == 4)
138 {
139 /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
140 otherwise. */
141 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
142
143 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
144 num2 = register_num (cpu, iw2);
145 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
146 num3 = interp_num (cpu, iw3);
147 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
148 num4 = interp_num (cpu, iw4);
149 result = (num3 == num4);
150 TRACE_EXTRACT (cpu, "EQ %#x %#x %#x", iw2, iw3, iw4);
151 TRACE_INSN (cpu, "EQ R%i %#x %#x", num2, num3, num4);
152 TRACE_DECODE (cpu, "R%i = (%#x == %#x) = %i", num2, num3, num4, result);
153
154 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
155 cpu->regs[num2] = result;
156
157 pc += 8;
158 }
159 else if (num1 == 5)
160 {
161 /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
162 otherwise. */
163 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
164
165 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
166 num2 = register_num (cpu, iw2);
167 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
168 num3 = interp_num (cpu, iw3);
169 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
170 num4 = interp_num (cpu, iw4);
171 result = (num3 > num4);
172 TRACE_EXTRACT (cpu, "GT %#x %#x %#x", iw2, iw3, iw4);
173 TRACE_INSN (cpu, "GT R%i %#x %#x", num2, num3, num4);
174 TRACE_DECODE (cpu, "R%i = (%#x > %#x) = %i", num2, num3, num4, result);
175
176 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
177 cpu->regs[num2] = result;
178
179 pc += 8;
180 }
181 else if (num1 == 6)
182 {
183 /* jmp: 6 a: Jump to <a>. */
184 unsigned16 iw2, num2;
185
186 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
187 num2 = interp_num (cpu, iw2);
188 /* Addresses are 16-bit aligned. */
189 num2 <<= 1;
190 TRACE_EXTRACT (cpu, "JMP %#x", iw2);
191 TRACE_INSN (cpu, "JMP %#x", num2);
192
193 pc = num2;
194 TRACE_BRANCH (cpu, "JMP %#x", pc);
195 }
196 else if (num1 == 7)
197 {
198 /* jt: 7 a b: If <a> is nonzero, jump to <b>. */
199 unsigned16 iw2, iw3, num2, num3;
200
201 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
202 num2 = interp_num (cpu, iw2);
203 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
204 num3 = interp_num (cpu, iw3);
205 /* Addresses are 16-bit aligned. */
206 num3 <<= 1;
207 TRACE_EXTRACT (cpu, "JT %#x %#x", iw2, iw3);
208 TRACE_INSN (cpu, "JT %#x %#x", num2, num3);
209 TRACE_DECODE (cpu, "JT %#x != 0 -> %s", num2, num2 ? "taken" : "nop");
210
211 if (num2)
212 {
213 pc = num3;
214 TRACE_BRANCH (cpu, "JT %#x", pc);
215 }
216 else
217 pc += 6;
218 }
219 else if (num1 == 8)
220 {
221 /* jf: 8 a b: If <a> is zero, jump to <b>. */
222 unsigned16 iw2, iw3, num2, num3;
223
224 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
225 num2 = interp_num (cpu, iw2);
226 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
227 num3 = interp_num (cpu, iw3);
228 /* Addresses are 16-bit aligned. */
229 num3 <<= 1;
230 TRACE_EXTRACT (cpu, "JF %#x %#x", iw2, iw3);
231 TRACE_INSN (cpu, "JF %#x %#x", num2, num3);
232 TRACE_DECODE (cpu, "JF %#x == 0 -> %s", num2, num2 ? "nop" : "taken");
233
234 if (!num2)
235 {
236 pc = num3;
237 TRACE_BRANCH (cpu, "JF %#x", pc);
238 }
239 else
240 pc += 6;
241 }
242 else if (num1 == 9)
243 {
244 /* add: 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768). */
245 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
246
247 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
248 num2 = register_num (cpu, iw2);
249 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
250 num3 = interp_num (cpu, iw3);
251 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
252 num4 = interp_num (cpu, iw4);
253 result = (num3 + num4) % 32768;
254 TRACE_EXTRACT (cpu, "ADD %#x %#x %#x", iw2, iw3, iw4);
255 TRACE_INSN (cpu, "ADD R%i %#x %#x", num2, num3, num4);
256 TRACE_DECODE (cpu, "R%i = (%#x + %#x) %% %i = %#x", num2, num3, num4,
257 32768, result);
258
259 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
260 cpu->regs[num2] = result;
261
262 pc += 8;
263 }
264 else if (num1 == 10)
265 {
266 /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
267 32768). */
268 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
269
270 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
271 num2 = register_num (cpu, iw2);
272 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
273 num3 = interp_num (cpu, iw3);
274 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
275 num4 = interp_num (cpu, iw4);
276 result = (num3 * num4) % 32768;
277 TRACE_EXTRACT (cpu, "MULT %#x %#x %#x", iw2, iw3, iw4);
278 TRACE_INSN (cpu, "MULT R%i %#x %#x", num2, num3, num4);
279 TRACE_DECODE (cpu, "R%i = (%#x * %#x) %% %i = %#x", num2, num3, num4,
280 32768, result);
281
282 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
283 cpu->regs[num2] = result;
284
285 pc += 8;
286 }
287 else if (num1 == 11)
288 {
289 /* mod: 11 a b c: Store into <a> the remainder of <b> divided by <c>. */
290 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
291
292 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
293 num2 = register_num (cpu, iw2);
294 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
295 num3 = interp_num (cpu, iw3);
296 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
297 num4 = interp_num (cpu, iw4);
298 result = num3 % num4;
299 TRACE_EXTRACT (cpu, "MOD %#x %#x %#x", iw2, iw3, iw4);
300 TRACE_INSN (cpu, "MOD R%i %#x %#x", num2, num3, num4);
301 TRACE_DECODE (cpu, "R%i = %#x %% %#x = %#x", num2, num3, num4, result);
302
303 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
304 cpu->regs[num2] = result;
305
306 pc += 8;
307 }
308 else if (num1 == 12)
309 {
310 /* and: 12 a b c: Stores into <a> the bitwise and of <b> and <c>. */
311 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
312
313 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
314 num2 = register_num (cpu, iw2);
315 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
316 num3 = interp_num (cpu, iw3);
317 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
318 num4 = interp_num (cpu, iw4);
319 result = (num3 & num4);
320 TRACE_EXTRACT (cpu, "AND %#x %#x %#x", iw2, iw3, iw4);
321 TRACE_INSN (cpu, "AND R%i %#x %#x", num2, num3, num4);
322 TRACE_DECODE (cpu, "R%i = %#x & %#x = %#x", num2, num3, num4, result);
323
324 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
325 cpu->regs[num2] = result;
326
327 pc += 8;
328 }
329 else if (num1 == 13)
330 {
331 /* or: 13 a b c: Stores into <a> the bitwise or of <b> and <c>. */
332 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
333
334 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
335 num2 = register_num (cpu, iw2);
336 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
337 num3 = interp_num (cpu, iw3);
338 iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
339 num4 = interp_num (cpu, iw4);
340 result = (num3 | num4);
341 TRACE_EXTRACT (cpu, "OR %#x %#x %#x", iw2, iw3, iw4);
342 TRACE_INSN (cpu, "OR R%i %#x %#x", num2, num3, num4);
343 TRACE_DECODE (cpu, "R%i = %#x | %#x = %#x", num2, num3, num4, result);
344
345 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
346 cpu->regs[num2] = result;
347
348 pc += 8;
349 }
350 else if (num1 == 14)
351 {
352 /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>. */
353 unsigned16 iw2, iw3, num2, num3, result;
354
355 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
356 num2 = register_num (cpu, iw2);
357 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
358 num3 = interp_num (cpu, iw3);
359 result = (~num3) & 0x7fff;
360 TRACE_EXTRACT (cpu, "NOT %#x %#x", iw2, iw3);
361 TRACE_INSN (cpu, "NOT R%i %#x", num2, num3);
362 TRACE_DECODE (cpu, "R%i = (~%#x) & 0x7fff = %#x", num2, num3, result);
363
364 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
365 cpu->regs[num2] = result;
366
367 pc += 6;
368 }
369 else if (num1 == 15)
370 {
371 /* rmem: 15 a b: Read memory at address <b> and write it to <a>. */
372 unsigned16 iw2, iw3, num2, num3, result;
373
374 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
375 num2 = register_num (cpu, iw2);
376 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
377 num3 = interp_num (cpu, iw3);
378 /* Addresses are 16-bit aligned. */
379 num3 <<= 1;
380 TRACE_EXTRACT (cpu, "RMEM %#x %#x", iw2, iw3);
381 TRACE_INSN (cpu, "RMEM R%i %#x", num2, num3);
382
383 TRACE_MEMORY (cpu, "reading %#x", num3);
384 result = sim_core_read_aligned_2 (cpu, pc, read_map, num3);
385
386 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
387 cpu->regs[num2] = result;
388
389 pc += 6;
390 }
391 else if (num1 == 16)
392 {
393 /* wmem: 16 a b: Write the value from <b> into memory at address <a>. */
394 unsigned16 iw2, iw3, num2, num3;
395
396 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
397 num2 = interp_num (cpu, iw2);
398 iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
399 num3 = interp_num (cpu, iw3);
400 /* Addresses are 16-bit aligned. */
401 num2 <<= 1;
402 TRACE_EXTRACT (cpu, "WMEM %#x %#x", iw2, iw3);
403 TRACE_INSN (cpu, "WMEM %#x %#x", num2, num3);
404
405 TRACE_MEMORY (cpu, "writing %#x to %#x", num3, num2);
406 sim_core_write_aligned_2 (cpu, pc, write_map, num2, num3);
407
408 pc += 6;
409 }
410 else if (num1 == 17)
411 {
412 /* call: 17 a: Write the address of the next instruction to the stack and
413 jump to <a>. */
414 unsigned16 iw2, num2;
415
416 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
417 num2 = interp_num (cpu, iw2);
418 /* Addresses are 16-bit aligned. */
419 num2 <<= 1;
420 TRACE_EXTRACT (cpu, "CALL %#x", iw2);
421 TRACE_INSN (cpu, "CALL %#x", num2);
422
423 TRACE_MEMORY (cpu, "pushing %#x onto stack", (pc + 4) >> 1);
424 sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, (pc + 4) >> 1);
425 cpu->sp -= 2;
426 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
427
428 pc = num2;
429 TRACE_BRANCH (cpu, "CALL %#x", pc);
430 }
431 else if (num1 == 18)
432 {
433 /* ret: 18: Remove the top element from the stack and jump to it; empty
434 stack = halt. */
435 unsigned16 result;
436
437 TRACE_INSN (cpu, "RET");
438 cpu->sp += 2;
439 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
440 result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
441 TRACE_MEMORY (cpu, "popping %#x off of stack", result << 1);
442
443 pc = result << 1;
444 TRACE_BRANCH (cpu, "RET -> %#x", pc);
445 }
446 else if (num1 == 19)
447 {
448 /* out: 19 a: Write the character <a> to the terminal. */
449 unsigned16 iw2, num2;
450
451 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
452 num2 = interp_num (cpu, iw2);
453 TRACE_EXTRACT (cpu, "OUT %#x", iw2);
454 TRACE_INSN (cpu, "OUT %#x", num2);
455 TRACE_EVENTS (cpu, "write to stdout: %#x (%c)", num2, num2);
456
457 sim_io_printf (sd, "%c", num2);
458
459 pc += 4;
460 }
461 else if (num1 == 20)
462 {
463 /* in: 20 a: read a character from the terminal and write its ascii code
464 to <a>. It can be assumed that once input starts, it will continue
465 until a newline is encountered. This means that you can safely read
466 lines from the keyboard and trust that they will be fully read. */
467 unsigned16 iw2, num2;
468 char c;
469
470 iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
471 num2 = register_num (cpu, iw2);
472 TRACE_EXTRACT (cpu, "IN %#x", iw2);
473 TRACE_INSN (cpu, "IN %#x", num2);
474 sim_io_read_stdin (sd, &c, 1);
475 TRACE_EVENTS (cpu, "read from stdin: %#x (%c)", c, c);
476
477 /* The challenge uses lowercase for all inputs, so insert some low level
478 helpers of our own to make it a bit nicer. */
479 switch (c)
480 {
481 case 'Q':
482 sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
483 break;
484 }
485
486 TRACE_REGISTER (cpu, "R%i = %#x", iw2 & 0xf, c);
487 cpu->regs[iw2 & 0xf] = c;
488
489 pc += 4;
490 }
491 else if (num1 == 21)
492 {
493 /* noop: 21: no operation */
494 TRACE_INSN (cpu, "NOOP");
495
496 pc += 2;
497 }
498 else
499 sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
500
501 TRACE_REGISTER (cpu, "PC = %#x", pc);
502 sim_pc_set (cpu, pc);
503 }
504 \f
505 /* Return the program counter for this cpu. */
506 static sim_cia
507 pc_get (sim_cpu *cpu)
508 {
509 return cpu->pc;
510 }
511
512 /* Set the program counter for this cpu to the new pc value. */
513 static void
514 pc_set (sim_cpu *cpu, sim_cia pc)
515 {
516 cpu->pc = pc;
517 }
518
519 /* Initialize the state for a single cpu. Usuaully this involves clearing all
520 registers back to their reset state. Should also hook up the fetch/store
521 helper functions too. */
522 void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu)
523 {
524 memset (cpu->regs, 0, sizeof (cpu->regs));
525 cpu->pc = 0;
526 /* Make sure it's initialized outside of the 16-bit address space. */
527 cpu->sp = 0x80000;
528
529 CPU_PC_FETCH (cpu) = pc_get;
530 CPU_PC_STORE (cpu) = pc_set;
531 }