]>
git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/example-synacor/sim-main.c
1 /* Example synacor simulator.
3 Copyright (C) 2005-2021 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 /* Get the register number from the number. */
32 register_num (SIM_CPU
*cpu
, unsigned16 num
)
34 SIM_DESC sd
= CPU_STATE (cpu
);
36 if (num
< 0x8000 || num
>= 0x8008)
37 sim_engine_halt (sd
, cpu
, NULL
, cpu
->pc
, sim_signalled
, SIM_SIGILL
);
42 /* Helper to process immediates according to the ISA. */
44 interp_num (SIM_CPU
*cpu
, unsigned16 num
)
46 SIM_DESC sd
= CPU_STATE (cpu
);
50 /* Numbers 0..32767 mean a literal value. */
51 TRACE_DECODE (cpu
, "%#x is a literal", num
);
54 else if (num
< 0x8008)
56 /* Numbers 32768..32775 instead mean registers 0..7. */
57 TRACE_DECODE (cpu
, "%#x is register R%i", num
, num
& 0xf);
58 return cpu
->regs
[num
& 0xf];
62 /* Numbers 32776..65535 are invalid. */
63 TRACE_DECODE (cpu
, "%#x is an invalid number", num
);
64 sim_engine_halt (sd
, cpu
, NULL
, cpu
->pc
, sim_signalled
, SIM_SIGILL
);
68 /* Decode & execute a single instruction. */
69 void step_once (SIM_CPU
*cpu
)
71 SIM_DESC sd
= CPU_STATE (cpu
);
73 sim_cia pc
= sim_pc_get (cpu
);
75 iw1
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
);
76 TRACE_EXTRACT (cpu
, "%04x: iw1: %#x", pc
, iw1
);
77 /* This never happens, but technically is possible in the ISA. */
78 num1
= interp_num (cpu
, iw1
);
82 /* halt: 0: Stop execution and terminate the program. */
83 TRACE_INSN (cpu
, "HALT");
84 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_exited
, 0);
88 /* set: 1 a b: Set register <a> to the value of <b>. */
89 unsigned16 iw2
, iw3
, num2
, num3
;
91 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
92 num2
= register_num (cpu
, iw2
);
93 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
94 num3
= interp_num (cpu
, iw3
);
95 TRACE_EXTRACT (cpu
, "SET %#x %#x", iw2
, iw3
);
96 TRACE_INSN (cpu
, "SET R%i %#x", num2
, num3
);
98 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, num3
);
99 cpu
->regs
[num2
] = num3
;
105 /* push: 2 a: Push <a> onto the stack. */
106 unsigned16 iw2
, num2
;
108 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
109 num2
= interp_num (cpu
, iw2
);
110 TRACE_EXTRACT (cpu
, "PUSH %#x", iw2
);
111 TRACE_INSN (cpu
, "PUSH %#x", num2
);
113 sim_core_write_aligned_2 (cpu
, pc
, write_map
, cpu
->sp
, num2
);
115 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
121 /* pop: 3 a: Remove the top element from the stack and write it into <a>.
122 Empty stack = error. */
123 unsigned16 iw2
, num2
, result
;
125 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
126 num2
= register_num (cpu
, iw2
);
127 TRACE_EXTRACT (cpu
, "POP %#x", iw2
);
128 TRACE_INSN (cpu
, "POP R%i", num2
);
130 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
131 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, cpu
->sp
);
133 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
134 cpu
->regs
[num2
] = result
;
140 /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
142 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
144 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
145 num2
= register_num (cpu
, iw2
);
146 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
147 num3
= interp_num (cpu
, iw3
);
148 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
149 num4
= interp_num (cpu
, iw4
);
150 result
= (num3
== num4
);
151 TRACE_EXTRACT (cpu
, "EQ %#x %#x %#x", iw2
, iw3
, iw4
);
152 TRACE_INSN (cpu
, "EQ R%i %#x %#x", num2
, num3
, num4
);
153 TRACE_DECODE (cpu
, "R%i = (%#x == %#x) = %i", num2
, num3
, num4
, result
);
155 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
156 cpu
->regs
[num2
] = result
;
162 /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
164 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
166 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
167 num2
= register_num (cpu
, iw2
);
168 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
169 num3
= interp_num (cpu
, iw3
);
170 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
171 num4
= interp_num (cpu
, iw4
);
172 result
= (num3
> num4
);
173 TRACE_EXTRACT (cpu
, "GT %#x %#x %#x", iw2
, iw3
, iw4
);
174 TRACE_INSN (cpu
, "GT R%i %#x %#x", num2
, num3
, num4
);
175 TRACE_DECODE (cpu
, "R%i = (%#x > %#x) = %i", num2
, num3
, num4
, result
);
177 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
178 cpu
->regs
[num2
] = result
;
184 /* jmp: 6 a: Jump to <a>. */
185 unsigned16 iw2
, num2
;
187 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
188 num2
= interp_num (cpu
, iw2
);
189 /* Addresses are 16-bit aligned. */
191 TRACE_EXTRACT (cpu
, "JMP %#x", iw2
);
192 TRACE_INSN (cpu
, "JMP %#x", num2
);
195 TRACE_BRANCH (cpu
, "JMP %#x", pc
);
199 /* jt: 7 a b: If <a> is nonzero, jump to <b>. */
200 unsigned16 iw2
, iw3
, num2
, num3
;
202 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
203 num2
= interp_num (cpu
, iw2
);
204 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
205 num3
= interp_num (cpu
, iw3
);
206 /* Addresses are 16-bit aligned. */
208 TRACE_EXTRACT (cpu
, "JT %#x %#x", iw2
, iw3
);
209 TRACE_INSN (cpu
, "JT %#x %#x", num2
, num3
);
210 TRACE_DECODE (cpu
, "JT %#x != 0 -> %s", num2
, num2
? "taken" : "nop");
215 TRACE_BRANCH (cpu
, "JT %#x", pc
);
222 /* jf: 8 a b: If <a> is zero, jump to <b>. */
223 unsigned16 iw2
, iw3
, num2
, num3
;
225 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
226 num2
= interp_num (cpu
, iw2
);
227 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
228 num3
= interp_num (cpu
, iw3
);
229 /* Addresses are 16-bit aligned. */
231 TRACE_EXTRACT (cpu
, "JF %#x %#x", iw2
, iw3
);
232 TRACE_INSN (cpu
, "JF %#x %#x", num2
, num3
);
233 TRACE_DECODE (cpu
, "JF %#x == 0 -> %s", num2
, num2
? "nop" : "taken");
238 TRACE_BRANCH (cpu
, "JF %#x", pc
);
245 /* add: 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768). */
246 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
248 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
249 num2
= register_num (cpu
, iw2
);
250 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
251 num3
= interp_num (cpu
, iw3
);
252 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
253 num4
= interp_num (cpu
, iw4
);
254 result
= (num3
+ num4
) % 32768;
255 TRACE_EXTRACT (cpu
, "ADD %#x %#x %#x", iw2
, iw3
, iw4
);
256 TRACE_INSN (cpu
, "ADD R%i %#x %#x", num2
, num3
, num4
);
257 TRACE_DECODE (cpu
, "R%i = (%#x + %#x) %% %i = %#x", num2
, num3
, num4
,
260 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
261 cpu
->regs
[num2
] = result
;
267 /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
269 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
271 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
272 num2
= register_num (cpu
, iw2
);
273 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
274 num3
= interp_num (cpu
, iw3
);
275 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
276 num4
= interp_num (cpu
, iw4
);
277 result
= (num3
* num4
) % 32768;
278 TRACE_EXTRACT (cpu
, "MULT %#x %#x %#x", iw2
, iw3
, iw4
);
279 TRACE_INSN (cpu
, "MULT R%i %#x %#x", num2
, num3
, num4
);
280 TRACE_DECODE (cpu
, "R%i = (%#x * %#x) %% %i = %#x", num2
, num3
, num4
,
283 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
284 cpu
->regs
[num2
] = result
;
290 /* mod: 11 a b c: Store into <a> the remainder of <b> divided by <c>. */
291 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
293 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
294 num2
= register_num (cpu
, iw2
);
295 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
296 num3
= interp_num (cpu
, iw3
);
297 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
298 num4
= interp_num (cpu
, iw4
);
299 result
= num3
% num4
;
300 TRACE_EXTRACT (cpu
, "MOD %#x %#x %#x", iw2
, iw3
, iw4
);
301 TRACE_INSN (cpu
, "MOD R%i %#x %#x", num2
, num3
, num4
);
302 TRACE_DECODE (cpu
, "R%i = %#x %% %#x = %#x", num2
, num3
, num4
, result
);
304 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
305 cpu
->regs
[num2
] = result
;
311 /* and: 12 a b c: Stores into <a> the bitwise and of <b> and <c>. */
312 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
314 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
315 num2
= register_num (cpu
, iw2
);
316 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
317 num3
= interp_num (cpu
, iw3
);
318 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
319 num4
= interp_num (cpu
, iw4
);
320 result
= (num3
& num4
);
321 TRACE_EXTRACT (cpu
, "AND %#x %#x %#x", iw2
, iw3
, iw4
);
322 TRACE_INSN (cpu
, "AND R%i %#x %#x", num2
, num3
, num4
);
323 TRACE_DECODE (cpu
, "R%i = %#x & %#x = %#x", num2
, num3
, num4
, result
);
325 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
326 cpu
->regs
[num2
] = result
;
332 /* or: 13 a b c: Stores into <a> the bitwise or of <b> and <c>. */
333 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
335 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
336 num2
= register_num (cpu
, iw2
);
337 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
338 num3
= interp_num (cpu
, iw3
);
339 iw4
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 6);
340 num4
= interp_num (cpu
, iw4
);
341 result
= (num3
| num4
);
342 TRACE_EXTRACT (cpu
, "OR %#x %#x %#x", iw2
, iw3
, iw4
);
343 TRACE_INSN (cpu
, "OR R%i %#x %#x", num2
, num3
, num4
);
344 TRACE_DECODE (cpu
, "R%i = %#x | %#x = %#x", num2
, num3
, num4
, result
);
346 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
347 cpu
->regs
[num2
] = result
;
353 /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>. */
354 unsigned16 iw2
, iw3
, num2
, num3
, result
;
356 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
357 num2
= register_num (cpu
, iw2
);
358 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
359 num3
= interp_num (cpu
, iw3
);
360 result
= (~num3
) & 0x7fff;
361 TRACE_EXTRACT (cpu
, "NOT %#x %#x", iw2
, iw3
);
362 TRACE_INSN (cpu
, "NOT R%i %#x", num2
, num3
);
363 TRACE_DECODE (cpu
, "R%i = (~%#x) & 0x7fff = %#x", num2
, num3
, result
);
365 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
366 cpu
->regs
[num2
] = result
;
372 /* rmem: 15 a b: Read memory at address <b> and write it to <a>. */
373 unsigned16 iw2
, iw3
, num2
, num3
, result
;
375 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
376 num2
= register_num (cpu
, iw2
);
377 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
378 num3
= interp_num (cpu
, iw3
);
379 /* Addresses are 16-bit aligned. */
381 TRACE_EXTRACT (cpu
, "RMEM %#x %#x", iw2
, iw3
);
382 TRACE_INSN (cpu
, "RMEM R%i %#x", num2
, num3
);
384 TRACE_MEMORY (cpu
, "reading %#x", num3
);
385 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, num3
);
387 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
388 cpu
->regs
[num2
] = result
;
394 /* wmem: 16 a b: Write the value from <b> into memory at address <a>. */
395 unsigned16 iw2
, iw3
, num2
, num3
;
397 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
398 num2
= interp_num (cpu
, iw2
);
399 iw3
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 4);
400 num3
= interp_num (cpu
, iw3
);
401 /* Addresses are 16-bit aligned. */
403 TRACE_EXTRACT (cpu
, "WMEM %#x %#x", iw2
, iw3
);
404 TRACE_INSN (cpu
, "WMEM %#x %#x", num2
, num3
);
406 TRACE_MEMORY (cpu
, "writing %#x to %#x", num3
, num2
);
407 sim_core_write_aligned_2 (cpu
, pc
, write_map
, num2
, num3
);
413 /* call: 17 a: Write the address of the next instruction to the stack and
415 unsigned16 iw2
, num2
;
417 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
418 num2
= interp_num (cpu
, iw2
);
419 /* Addresses are 16-bit aligned. */
421 TRACE_EXTRACT (cpu
, "CALL %#x", iw2
);
422 TRACE_INSN (cpu
, "CALL %#x", num2
);
424 TRACE_MEMORY (cpu
, "pushing %#x onto stack", (pc
+ 4) >> 1);
425 sim_core_write_aligned_2 (cpu
, pc
, write_map
, cpu
->sp
, (pc
+ 4) >> 1);
427 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
430 TRACE_BRANCH (cpu
, "CALL %#x", pc
);
434 /* ret: 18: Remove the top element from the stack and jump to it; empty
438 TRACE_INSN (cpu
, "RET");
440 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
441 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, cpu
->sp
);
442 TRACE_MEMORY (cpu
, "popping %#x off of stack", result
<< 1);
445 TRACE_BRANCH (cpu
, "RET -> %#x", pc
);
449 /* out: 19 a: Write the character <a> to the terminal. */
450 unsigned16 iw2
, num2
;
452 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
453 num2
= interp_num (cpu
, iw2
);
454 TRACE_EXTRACT (cpu
, "OUT %#x", iw2
);
455 TRACE_INSN (cpu
, "OUT %#x", num2
);
456 TRACE_EVENTS (cpu
, "write to stdout: %#x (%c)", num2
, num2
);
458 sim_io_printf (sd
, "%c", num2
);
464 /* in: 20 a: read a character from the terminal and write its ascii code
465 to <a>. It can be assumed that once input starts, it will continue
466 until a newline is encountered. This means that you can safely read
467 lines from the keyboard and trust that they will be fully read. */
468 unsigned16 iw2
, num2
;
471 iw2
= sim_core_read_aligned_2 (cpu
, pc
, exec_map
, pc
+ 2);
472 num2
= register_num (cpu
, iw2
);
473 TRACE_EXTRACT (cpu
, "IN %#x", iw2
);
474 TRACE_INSN (cpu
, "IN %#x", num2
);
475 sim_io_read_stdin (sd
, &c
, 1);
476 TRACE_EVENTS (cpu
, "read from stdin: %#x (%c)", c
, c
);
478 /* The challenge uses lowercase for all inputs, so insert some low level
479 helpers of our own to make it a bit nicer. */
483 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_exited
, 0);
487 TRACE_REGISTER (cpu
, "R%i = %#x", iw2
& 0xf, c
);
488 cpu
->regs
[iw2
& 0xf] = c
;
494 /* noop: 21: no operation */
495 TRACE_INSN (cpu
, "NOOP");
500 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_signalled
, SIM_SIGILL
);
502 TRACE_REGISTER (cpu
, "PC = %#x", pc
);
503 sim_pc_set (cpu
, pc
);
506 /* Return the program counter for this cpu. */
508 pc_get (sim_cpu
*cpu
)
513 /* Set the program counter for this cpu to the new pc value. */
515 pc_set (sim_cpu
*cpu
, sim_cia pc
)
520 /* Initialize the state for a single cpu. Usuaully this involves clearing all
521 registers back to their reset state. Should also hook up the fetch/store
522 helper functions too. */
523 void initialize_cpu (SIM_DESC sd
, SIM_CPU
*cpu
)
525 memset (cpu
->regs
, 0, sizeof (cpu
->regs
));
527 /* Make sure it's initialized outside of the 16-bit address space. */
530 CPU_PC_FETCH (cpu
) = pc_get
;
531 CPU_PC_STORE (cpu
) = pc_set
;