]>
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. */
29 /* Get the register number from the number. */
31 register_num (SIM_CPU
*cpu
, unsigned16 num
)
33 SIM_DESC sd
= CPU_STATE (cpu
);
35 if (num
< 0x8000 || num
>= 0x8008)
36 sim_engine_halt (sd
, cpu
, NULL
, cpu
->pc
, sim_signalled
, SIM_SIGILL
);
41 /* Helper to process immediates according to the ISA. */
43 interp_num (SIM_CPU
*cpu
, unsigned16 num
)
45 SIM_DESC sd
= CPU_STATE (cpu
);
49 /* Numbers 0..32767 mean a literal value. */
50 TRACE_DECODE (cpu
, "%#x is a literal", num
);
53 else if (num
< 0x8008)
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];
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
);
67 /* Decode & execute a single instruction. */
68 void step_once (SIM_CPU
*cpu
)
70 SIM_DESC sd
= CPU_STATE (cpu
);
72 sim_cia pc
= sim_pc_get (cpu
);
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
);
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);
87 /* set: 1 a b: Set register <a> to the value of <b>. */
88 unsigned16 iw2
, iw3
, num2
, num3
;
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
);
97 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, num3
);
98 cpu
->regs
[num2
] = num3
;
104 /* push: 2 a: Push <a> onto the stack. */
105 unsigned16 iw2
, num2
;
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
);
112 sim_core_write_aligned_2 (cpu
, pc
, write_map
, cpu
->sp
, num2
);
114 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
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
;
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
);
129 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
130 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, cpu
->sp
);
132 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
133 cpu
->regs
[num2
] = result
;
139 /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
141 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
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
);
154 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
155 cpu
->regs
[num2
] = result
;
161 /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
163 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
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
);
176 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
177 cpu
->regs
[num2
] = result
;
183 /* jmp: 6 a: Jump to <a>. */
184 unsigned16 iw2
, num2
;
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. */
190 TRACE_EXTRACT (cpu
, "JMP %#x", iw2
);
191 TRACE_INSN (cpu
, "JMP %#x", num2
);
194 TRACE_BRANCH (cpu
, "JMP %#x", pc
);
198 /* jt: 7 a b: If <a> is nonzero, jump to <b>. */
199 unsigned16 iw2
, iw3
, num2
, num3
;
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. */
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");
214 TRACE_BRANCH (cpu
, "JT %#x", pc
);
221 /* jf: 8 a b: If <a> is zero, jump to <b>. */
222 unsigned16 iw2
, iw3
, num2
, num3
;
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. */
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");
237 TRACE_BRANCH (cpu
, "JF %#x", pc
);
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
;
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
,
259 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
260 cpu
->regs
[num2
] = result
;
266 /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
268 unsigned16 iw2
, iw3
, iw4
, num2
, num3
, num4
, result
;
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
,
282 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
283 cpu
->regs
[num2
] = result
;
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
;
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
);
303 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
304 cpu
->regs
[num2
] = result
;
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
;
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
);
324 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
325 cpu
->regs
[num2
] = result
;
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
;
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
);
345 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
346 cpu
->regs
[num2
] = result
;
352 /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>. */
353 unsigned16 iw2
, iw3
, num2
, num3
, result
;
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
);
364 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
365 cpu
->regs
[num2
] = result
;
371 /* rmem: 15 a b: Read memory at address <b> and write it to <a>. */
372 unsigned16 iw2
, iw3
, num2
, num3
, result
;
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. */
380 TRACE_EXTRACT (cpu
, "RMEM %#x %#x", iw2
, iw3
);
381 TRACE_INSN (cpu
, "RMEM R%i %#x", num2
, num3
);
383 TRACE_MEMORY (cpu
, "reading %#x", num3
);
384 result
= sim_core_read_aligned_2 (cpu
, pc
, read_map
, num3
);
386 TRACE_REGISTER (cpu
, "R%i = %#x", num2
, result
);
387 cpu
->regs
[num2
] = result
;
393 /* wmem: 16 a b: Write the value from <b> into memory at address <a>. */
394 unsigned16 iw2
, iw3
, num2
, num3
;
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. */
402 TRACE_EXTRACT (cpu
, "WMEM %#x %#x", iw2
, iw3
);
403 TRACE_INSN (cpu
, "WMEM %#x %#x", num2
, num3
);
405 TRACE_MEMORY (cpu
, "writing %#x to %#x", num3
, num2
);
406 sim_core_write_aligned_2 (cpu
, pc
, write_map
, num2
, num3
);
412 /* call: 17 a: Write the address of the next instruction to the stack and
414 unsigned16 iw2
, num2
;
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. */
420 TRACE_EXTRACT (cpu
, "CALL %#x", iw2
);
421 TRACE_INSN (cpu
, "CALL %#x", num2
);
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);
426 TRACE_REGISTER (cpu
, "SP = %#x", cpu
->sp
);
429 TRACE_BRANCH (cpu
, "CALL %#x", pc
);
433 /* ret: 18: Remove the top element from the stack and jump to it; empty
437 TRACE_INSN (cpu
, "RET");
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);
444 TRACE_BRANCH (cpu
, "RET -> %#x", pc
);
448 /* out: 19 a: Write the character <a> to the terminal. */
449 unsigned16 iw2
, num2
;
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
);
457 sim_io_printf (sd
, "%c", num2
);
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
;
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
);
477 /* The challenge uses lowercase for all inputs, so insert some low level
478 helpers of our own to make it a bit nicer. */
482 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_exited
, 0);
486 TRACE_REGISTER (cpu
, "R%i = %#x", iw2
& 0xf, c
);
487 cpu
->regs
[iw2
& 0xf] = c
;
493 /* noop: 21: no operation */
494 TRACE_INSN (cpu
, "NOOP");
499 sim_engine_halt (sd
, cpu
, NULL
, pc
, sim_signalled
, SIM_SIGILL
);
501 TRACE_REGISTER (cpu
, "PC = %#x", pc
);
502 sim_pc_set (cpu
, pc
);
505 /* Return the program counter for this cpu. */
507 pc_get (sim_cpu
*cpu
)
512 /* Set the program counter for this cpu to the new pc value. */
514 pc_set (sim_cpu
*cpu
, sim_cia pc
)
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
)
524 memset (cpu
->regs
, 0, sizeof (cpu
->regs
));
526 /* Make sure it's initialized outside of the 16-bit address space. */
529 CPU_PC_FETCH (cpu
) = pc_get
;
530 CPU_PC_STORE (cpu
) = pc_set
;