1 /* Example synacor simulator.
3 Copyright (C) 2005-2024 Free Software Foundation, Inc.
4 Contributed by Mike Frysinger.
6 This file is part of simulators.
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.
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.
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/>. */
21 /* This file contains the main simulator decoding logic. i.e. everything that
22 is architecture specific. */
24 /* This must come before any other includes. */
28 #include "sim-signal.h"
30 #include "example-synacor-sim.h"
32 /* Get the register number from the number. */
34 register_num (SIM_CPU
*cpu
, uint16_t num
)
36 SIM_DESC sd
= CPU_STATE (cpu
);
38 if (num
< 0x8000 || num
>= 0x8008)
39 sim_engine_halt (sd
, cpu
, NULL
, sim_pc_get (cpu
), sim_signalled
, SIM_SIGILL
);
44 /* Helper to process immediates according to the ISA. */
46 interp_num (SIM_CPU
*cpu
, uint16_t num
)
48 SIM_DESC sd
= CPU_STATE (cpu
);
49 struct example_sim_cpu
*example_cpu
= EXAMPLE_SIM_CPU (cpu
);
53 /* Numbers 0..32767 mean a literal value. */
54 TRACE_DECODE (cpu
, "%#x is a literal", num
);
57 else if (num
< 0x8008)
59 /* Numbers 32768..32775 instead mean registers 0..7. */
60 TRACE_DECODE (cpu
, "%#x is register R%i", num
, num
& 0xf);
61 return example_cpu
->regs
[num
& 0xf];
65 /* Numbers 32776..65535 are invalid. */
66 TRACE_DECODE (cpu
, "%#x is an invalid number", num
);
67 sim_engine_halt (sd
, cpu
, NULL
, example_cpu
->pc
, sim_signalled
, SIM_SIGILL
);
71 /* Decode & execute a single instruction. */
72 void step_once (SIM_CPU
*cpu
)
74 SIM_DESC sd
= CPU_STATE (cpu
);
75 struct example_sim_cpu
*example_cpu
= EXAMPLE_SIM_CPU (cpu
);
77 sim_cia pc
= sim_pc_get (cpu
);
79 iw1
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
);
80 TRACE_EXTRACT (cpu
, "%04x: iw1: %#x", pc
, iw1
);
81 /* This never happens, but technically is possible in the ISA. */
82 num1
= interp_num (cpu
, iw1
);
86 /* halt: 0: Stop execution and terminate the program. */
87 TRACE_INSN (cpu
, "HALT");
88 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_exited
, 0);
92 /* set: 1 a b: Set register <a> to the value of <b>. */
93 uint16_t iw2
, iw3
, num2
, num3
;
95 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
96 num2
= register_num (cpu
, iw2
);
97 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
98 num3
= interp_num (cpu
, iw3
);
99 TRACE_EXTRACT (cpu
, "SET %#x %#x", iw2
, iw3
);
100 TRACE_INSN (cpu
, "SET R%i %#x", num2
, num3
);
102 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, num3
);
103 example_cpu
->regs
[num2
] = num3
;
109 /* push: 2 a: Push <a> onto the stack. */
112 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
113 num2
= interp_num (cpu
, iw2
);
114 TRACE_EXTRACT (cpu
, "PUSH %#x", iw2
);
115 TRACE_INSN (cpu
, "PUSH %#x", num2
);
117 sim_core_write_aligned_2 (cpu
, pc
, write_map
, example_cpu
->sp
, num2
);
118 example_cpu
->sp
-= 2;
119 TRACE_REGISTER (cpu
, "SP = %#x", example_cpu
->sp
);
125 /* pop: 3 a: Remove the top element from the stack and write it into <a>.
126 Empty stack = error. */
127 uint16_t iw2
, num2
, result
;
129 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
130 num2
= register_num (cpu
, iw2
);
131 TRACE_EXTRACT (cpu
, "POP %#x", iw2
);
132 TRACE_INSN (cpu
, "POP R%i", num2
);
133 example_cpu
->sp
+= 2;
134 TRACE_REGISTER (cpu
, "SP = %#x", example_cpu
->sp
);
135 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, example_cpu
->sp
);
137 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
138 example_cpu
->regs
[num2
] = result
;
144 /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
146 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
148 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
149 num2
= register_num (cpu
, iw2
);
150 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
151 num3
= interp_num (cpu
, iw3
);
152 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
153 num4
= interp_num (cpu
, iw4
);
154 result
= (num3
== num4
);
155 TRACE_EXTRACT (cpu
, "EQ %#x %#x %#x", iw2
, iw3
, iw4
);
156 TRACE_INSN (cpu
, "EQ R%i %#x %#x", num2
, num3
, num4
);
157 TRACE_DECODE (cpu
, "R%i = (%#x == %#x) = %i", num2
, num3
, num4
, result
);
159 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
160 example_cpu
->regs
[num2
] = result
;
166 /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
168 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
170 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
171 num2
= register_num (cpu
, iw2
);
172 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
173 num3
= interp_num (cpu
, iw3
);
174 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
175 num4
= interp_num (cpu
, iw4
);
176 result
= (num3
> num4
);
177 TRACE_EXTRACT (cpu
, "GT %#x %#x %#x", iw2
, iw3
, iw4
);
178 TRACE_INSN (cpu
, "GT R%i %#x %#x", num2
, num3
, num4
);
179 TRACE_DECODE (cpu
, "R%i = (%#x > %#x) = %i", num2
, num3
, num4
, result
);
181 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
182 example_cpu
->regs
[num2
] = result
;
188 /* jmp: 6 a: Jump to <a>. */
191 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
192 num2
= interp_num (cpu
, iw2
);
193 /* Addresses are 16-bit aligned. */
195 TRACE_EXTRACT (cpu
, "JMP %#x", iw2
);
196 TRACE_INSN (cpu
, "JMP %#x", num2
);
199 TRACE_BRANCH (cpu
, "JMP %#x", pc
);
203 /* jt: 7 a b: If <a> is nonzero, jump to <b>. */
204 uint16_t iw2
, iw3
, num2
, num3
;
206 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
207 num2
= interp_num (cpu
, iw2
);
208 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
209 num3
= interp_num (cpu
, iw3
);
210 /* Addresses are 16-bit aligned. */
212 TRACE_EXTRACT (cpu
, "JT %#x %#x", iw2
, iw3
);
213 TRACE_INSN (cpu
, "JT %#x %#x", num2
, num3
);
214 TRACE_DECODE (cpu
, "JT %#x != 0 -> %s", num2
, num2
? "taken" : "nop");
219 TRACE_BRANCH (cpu
, "JT %#x", pc
);
226 /* jf: 8 a b: If <a> is zero, jump to <b>. */
227 uint16_t iw2
, iw3
, num2
, num3
;
229 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
230 num2
= interp_num (cpu
, iw2
);
231 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
232 num3
= interp_num (cpu
, iw3
);
233 /* Addresses are 16-bit aligned. */
235 TRACE_EXTRACT (cpu
, "JF %#x %#x", iw2
, iw3
);
236 TRACE_INSN (cpu
, "JF %#x %#x", num2
, num3
);
237 TRACE_DECODE (cpu
, "JF %#x == 0 -> %s", num2
, num2
? "nop" : "taken");
242 TRACE_BRANCH (cpu
, "JF %#x", pc
);
249 /* add: 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768). */
250 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
252 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
253 num2
= register_num (cpu
, iw2
);
254 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
255 num3
= interp_num (cpu
, iw3
);
256 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
257 num4
= interp_num (cpu
, iw4
);
258 result
= (num3
+ num4
) % 32768;
259 TRACE_EXTRACT (cpu
, "ADD %#x %#x %#x", iw2
, iw3
, iw4
);
260 TRACE_INSN (cpu
, "ADD R%i %#x %#x", num2
, num3
, num4
);
261 TRACE_DECODE (cpu
, "R%i = (%#x + %#x) %% %i = %#x", num2
, num3
, num4
,
264 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
265 example_cpu
->regs
[num2
] = result
;
271 /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
273 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
275 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
276 num2
= register_num (cpu
, iw2
);
277 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
278 num3
= interp_num (cpu
, iw3
);
279 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
280 num4
= interp_num (cpu
, iw4
);
281 result
= (num3
* num4
) % 32768;
282 TRACE_EXTRACT (cpu
, "MULT %#x %#x %#x", iw2
, iw3
, iw4
);
283 TRACE_INSN (cpu
, "MULT R%i %#x %#x", num2
, num3
, num4
);
284 TRACE_DECODE (cpu
, "R%i = (%#x * %#x) %% %i = %#x", num2
, num3
, num4
,
287 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
288 example_cpu
->regs
[num2
] = result
;
294 /* mod: 11 a b c: Store into <a> the remainder of <b> divided by <c>. */
295 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
297 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
298 num2
= register_num (cpu
, iw2
);
299 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
300 num3
= interp_num (cpu
, iw3
);
301 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
302 num4
= interp_num (cpu
, iw4
);
303 result
= num3
% num4
;
304 TRACE_EXTRACT (cpu
, "MOD %#x %#x %#x", iw2
, iw3
, iw4
);
305 TRACE_INSN (cpu
, "MOD R%i %#x %#x", num2
, num3
, num4
);
306 TRACE_DECODE (cpu
, "R%i = %#x %% %#x = %#x", num2
, num3
, num4
, result
);
308 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
309 example_cpu
->regs
[num2
] = result
;
315 /* and: 12 a b c: Stores into <a> the bitwise and of <b> and <c>. */
316 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
318 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
319 num2
= register_num (cpu
, iw2
);
320 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
321 num3
= interp_num (cpu
, iw3
);
322 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
323 num4
= interp_num (cpu
, iw4
);
324 result
= (num3
& num4
);
325 TRACE_EXTRACT (cpu
, "AND %#x %#x %#x", iw2
, iw3
, iw4
);
326 TRACE_INSN (cpu
, "AND R%i %#x %#x", num2
, num3
, num4
);
327 TRACE_DECODE (cpu
, "R%i = %#x & %#x = %#x", num2
, num3
, num4
, result
);
329 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
330 example_cpu
->regs
[num2
] = result
;
336 /* or: 13 a b c: Stores into <a> the bitwise or of <b> and <c>. */
337 uint16_t iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
339 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
340 num2
= register_num (cpu
, iw2
);
341 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
342 num3
= interp_num (cpu
, iw3
);
343 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
344 num4
= interp_num (cpu
, iw4
);
345 result
= (num3
| num4
);
346 TRACE_EXTRACT (cpu
, "OR %#x %#x %#x", iw2
, iw3
, iw4
);
347 TRACE_INSN (cpu
, "OR R%i %#x %#x", num2
, num3
, num4
);
348 TRACE_DECODE (cpu
, "R%i = %#x | %#x = %#x", num2
, num3
, num4
, result
);
350 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
351 example_cpu
->regs
[num2
] = result
;
357 /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>. */
358 uint16_t iw2
, iw3
, num2
, num3
, result
;
360 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
361 num2
= register_num (cpu
, iw2
);
362 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
363 num3
= interp_num (cpu
, iw3
);
364 result
= (~num3
) & 0x7fff;
365 TRACE_EXTRACT (cpu
, "NOT %#x %#x", iw2
, iw3
);
366 TRACE_INSN (cpu
, "NOT R%i %#x", num2
, num3
);
367 TRACE_DECODE (cpu
, "R%i = (~%#x) & 0x7fff = %#x", num2
, num3
, result
);
369 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
370 example_cpu
->regs
[num2
] = result
;
376 /* rmem: 15 a b: Read memory at address <b> and write it to <a>. */
377 uint16_t iw2
, iw3
, num2
, num3
, result
;
379 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
380 num2
= register_num (cpu
, iw2
);
381 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
382 num3
= interp_num (cpu
, iw3
);
383 /* Addresses are 16-bit aligned. */
385 TRACE_EXTRACT (cpu
, "RMEM %#x %#x", iw2
, iw3
);
386 TRACE_INSN (cpu
, "RMEM R%i %#x", num2
, num3
);
388 TRACE_MEMORY (cpu
, "reading %#x", num3
);
389 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, num3
);
391 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
392 example_cpu
->regs
[num2
] = result
;
398 /* wmem: 16 a b: Write the value from <b> into memory at address <a>. */
399 uint16_t iw2
, iw3
, num2
, num3
;
401 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
402 num2
= interp_num (cpu
, iw2
);
403 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
404 num3
= interp_num (cpu
, iw3
);
405 /* Addresses are 16-bit aligned. */
407 TRACE_EXTRACT (cpu
, "WMEM %#x %#x", iw2
, iw3
);
408 TRACE_INSN (cpu
, "WMEM %#x %#x", num2
, num3
);
410 TRACE_MEMORY (cpu
, "writing %#x to %#x", num3
, num2
);
411 sim_core_write_aligned_2 (cpu
, pc
, write_map
, num2
, num3
);
417 /* call: 17 a: Write the address of the next instruction to the stack and
421 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
422 num2
= interp_num (cpu
, iw2
);
423 /* Addresses are 16-bit aligned. */
425 TRACE_EXTRACT (cpu
, "CALL %#x", iw2
);
426 TRACE_INSN (cpu
, "CALL %#x", num2
);
428 TRACE_MEMORY (cpu
, "pushing %#x onto stack", (pc
+ 4) >> 1);
429 sim_core_write_aligned_2 (cpu
, pc
, write_map
, example_cpu
->sp
, (pc
+ 4) >> 1);
430 example_cpu
->sp
-= 2;
431 TRACE_REGISTER (cpu
, "SP = %#x", example_cpu
->sp
);
434 TRACE_BRANCH (cpu
, "CALL %#x", pc
);
438 /* ret: 18: Remove the top element from the stack and jump to it; empty
442 TRACE_INSN (cpu
, "RET");
443 example_cpu
->sp
+= 2;
444 TRACE_REGISTER (cpu
, "SP = %#x", example_cpu
->sp
);
445 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, example_cpu
->sp
);
446 TRACE_MEMORY (cpu
, "popping %#x off of stack", result
<< 1);
449 TRACE_BRANCH (cpu
, "RET -> %#x", pc
);
453 /* out: 19 a: Write the character <a> to the terminal. */
456 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
457 num2
= interp_num (cpu
, iw2
);
458 TRACE_EXTRACT (cpu
, "OUT %#x", iw2
);
459 TRACE_INSN (cpu
, "OUT %#x", num2
);
460 TRACE_EVENTS (cpu
, "write to stdout: %#x (%c)", num2
, num2
);
462 sim_io_printf (sd
, "%c", num2
);
468 /* in: 20 a: read a character from the terminal and write its ascii code
469 to <a>. It can be assumed that once input starts, it will continue
470 until a newline is encountered. This means that you can safely read
471 lines from the keyboard and trust that they will be fully read. */
475 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
476 num2
= register_num (cpu
, iw2
);
477 TRACE_EXTRACT (cpu
, "IN %#x", iw2
);
478 TRACE_INSN (cpu
, "IN %#x", num2
);
479 sim_io_read_stdin (sd
, &c
, 1);
480 TRACE_EVENTS (cpu
, "read from stdin: %#x (%c)", c
, c
);
482 /* The challenge uses lowercase for all inputs, so insert some low level
483 helpers of our own to make it a bit nicer. */
487 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_exited
, 0);
491 TRACE_REGISTER (cpu
, "R%i = %#x", iw2
& 0xf, c
);
492 example_cpu
->regs
[iw2
& 0xf] = c
;
498 /* noop: 21: no operation */
499 TRACE_INSN (cpu
, "NOOP");
504 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_signalled
, SIM_SIGILL
);
506 TRACE_REGISTER (cpu
, "PC = %#x", pc
);
507 sim_pc_set (cpu
, pc
);
510 /* Return the program counter for this cpu. */
512 pc_get (sim_cpu
*cpu
)
514 struct example_sim_cpu
*example_cpu
= EXAMPLE_SIM_CPU (cpu
);
516 return example_cpu
->pc
;
519 /* Set the program counter for this cpu to the new pc value. */
521 pc_set (sim_cpu
*cpu
, sim_cia pc
)
523 struct example_sim_cpu
*example_cpu
= EXAMPLE_SIM_CPU (cpu
);
525 example_cpu
->pc
= pc
;
528 /* Initialize the state for a single cpu. Usuaully this involves clearing all
529 registers back to their reset state. Should also hook up the fetch/store
530 helper functions too. */
531 void initialize_cpu (SIM_DESC sd
, SIM_CPU
*cpu
)
533 struct example_sim_cpu
*example_cpu
= EXAMPLE_SIM_CPU (cpu
);
535 memset (example_cpu
->regs
, 0, sizeof (example_cpu
->regs
));
537 /* Make sure it's initialized outside of the 16-bit address space. */
538 example_cpu
->sp
= 0x80000;
540 CPU_PC_FETCH (cpu
) = pc_get
;
541 CPU_PC_STORE (cpu
) = pc_set
;