]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/example-synacor/sim-main.c
sim: split sim-signal.h include out
[thirdparty/binutils-gdb.git] / sim / example-synacor / sim-main.c
CommitLineData
26da232c
MF
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
6df01ab8
MF
24/* This must come before any other includes. */
25#include "defs.h"
26da232c
MF
26
27#include "sim-main.h"
1fef66b0 28#include "sim-signal.h"
26da232c
MF
29\f
30/* Get the register number from the number. */
31static unsigned16
32register_num (SIM_CPU *cpu, unsigned16 num)
33{
34 SIM_DESC sd = CPU_STATE (cpu);
35
36 if (num < 0x8000 || num >= 0x8008)
37 sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
38
39 return num & 0xf;
40}
41
42/* Helper to process immediates according to the ISA. */
43static unsigned16
44interp_num (SIM_CPU *cpu, unsigned16 num)
45{
46 SIM_DESC sd = CPU_STATE (cpu);
47
48 if (num < 0x8000)
49 {
50 /* Numbers 0..32767 mean a literal value. */
51 TRACE_DECODE (cpu, "%#x is a literal", num);
52 return num;
53 }
54 else if (num < 0x8008)
55 {
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];
59 }
60 else
61 {
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);
65 }
66}
67\f
68/* Decode & execute a single instruction. */
69void step_once (SIM_CPU *cpu)
70{
71 SIM_DESC sd = CPU_STATE (cpu);
72 unsigned16 iw1, num1;
73 sim_cia pc = sim_pc_get (cpu);
74
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);
79
80 if (num1 == 0)
81 {
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);
85 }
86 else if (num1 == 1)
87 {
88 /* set: 1 a b: Set register <a> to the value of <b>. */
89 unsigned16 iw2, iw3, num2, num3;
90
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);
97
98 TRACE_REGISTER (cpu, "R%i = %#x", num2, num3);
99 cpu->regs[num2] = num3;
100
101 pc += 6;
102 }
103 else if (num1 == 2)
104 {
105 /* push: 2 a: Push <a> onto the stack. */
106 unsigned16 iw2, num2;
107
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);
112
113 sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, num2);
114 cpu->sp -= 2;
115 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
116
117 pc += 4;
118 }
119 else if (num1 == 3)
120 {
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;
124
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);
129 cpu->sp += 2;
130 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
131 result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
132
133 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
134 cpu->regs[num2] = result;
135
136 pc += 4;
137 }
138 else if (num1 == 4)
139 {
140 /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
141 otherwise. */
142 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
143
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);
154
155 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
156 cpu->regs[num2] = result;
157
158 pc += 8;
159 }
160 else if (num1 == 5)
161 {
162 /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
163 otherwise. */
164 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
165
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);
176
177 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
178 cpu->regs[num2] = result;
179
180 pc += 8;
181 }
182 else if (num1 == 6)
183 {
184 /* jmp: 6 a: Jump to <a>. */
185 unsigned16 iw2, num2;
186
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. */
190 num2 <<= 1;
191 TRACE_EXTRACT (cpu, "JMP %#x", iw2);
192 TRACE_INSN (cpu, "JMP %#x", num2);
193
194 pc = num2;
195 TRACE_BRANCH (cpu, "JMP %#x", pc);
196 }
197 else if (num1 == 7)
198 {
199 /* jt: 7 a b: If <a> is nonzero, jump to <b>. */
200 unsigned16 iw2, iw3, num2, num3;
201
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. */
207 num3 <<= 1;
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");
211
212 if (num2)
213 {
214 pc = num3;
215 TRACE_BRANCH (cpu, "JT %#x", pc);
216 }
217 else
218 pc += 6;
219 }
220 else if (num1 == 8)
221 {
222 /* jf: 8 a b: If <a> is zero, jump to <b>. */
223 unsigned16 iw2, iw3, num2, num3;
224
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. */
230 num3 <<= 1;
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");
234
235 if (!num2)
236 {
237 pc = num3;
238 TRACE_BRANCH (cpu, "JF %#x", pc);
239 }
240 else
241 pc += 6;
242 }
243 else if (num1 == 9)
244 {
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;
247
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,
258 32768, result);
259
260 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
261 cpu->regs[num2] = result;
262
263 pc += 8;
264 }
265 else if (num1 == 10)
266 {
267 /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
268 32768). */
269 unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
270
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,
281 32768, result);
282
283 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
284 cpu->regs[num2] = result;
285
286 pc += 8;
287 }
288 else if (num1 == 11)
289 {
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;
292
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);
303
304 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
305 cpu->regs[num2] = result;
306
307 pc += 8;
308 }
309 else if (num1 == 12)
310 {
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;
313
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);
324
325 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
326 cpu->regs[num2] = result;
327
328 pc += 8;
329 }
330 else if (num1 == 13)
331 {
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;
334
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);
345
346 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
347 cpu->regs[num2] = result;
348
349 pc += 8;
350 }
351 else if (num1 == 14)
352 {
353 /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>. */
354 unsigned16 iw2, iw3, num2, num3, result;
355
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);
364
365 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
366 cpu->regs[num2] = result;
367
368 pc += 6;
369 }
370 else if (num1 == 15)
371 {
372 /* rmem: 15 a b: Read memory at address <b> and write it to <a>. */
373 unsigned16 iw2, iw3, num2, num3, result;
374
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. */
380 num3 <<= 1;
381 TRACE_EXTRACT (cpu, "RMEM %#x %#x", iw2, iw3);
382 TRACE_INSN (cpu, "RMEM R%i %#x", num2, num3);
383
384 TRACE_MEMORY (cpu, "reading %#x", num3);
385 result = sim_core_read_aligned_2 (cpu, pc, read_map, num3);
386
387 TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
388 cpu->regs[num2] = result;
389
390 pc += 6;
391 }
392 else if (num1 == 16)
393 {
394 /* wmem: 16 a b: Write the value from <b> into memory at address <a>. */
395 unsigned16 iw2, iw3, num2, num3;
396
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. */
402 num2 <<= 1;
403 TRACE_EXTRACT (cpu, "WMEM %#x %#x", iw2, iw3);
404 TRACE_INSN (cpu, "WMEM %#x %#x", num2, num3);
405
406 TRACE_MEMORY (cpu, "writing %#x to %#x", num3, num2);
407 sim_core_write_aligned_2 (cpu, pc, write_map, num2, num3);
408
409 pc += 6;
410 }
411 else if (num1 == 17)
412 {
413 /* call: 17 a: Write the address of the next instruction to the stack and
414 jump to <a>. */
415 unsigned16 iw2, num2;
416
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. */
420 num2 <<= 1;
421 TRACE_EXTRACT (cpu, "CALL %#x", iw2);
422 TRACE_INSN (cpu, "CALL %#x", num2);
423
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);
426 cpu->sp -= 2;
427 TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
428
429 pc = num2;
430 TRACE_BRANCH (cpu, "CALL %#x", pc);
431 }
432 else if (num1 == 18)
433 {
434 /* ret: 18: Remove the top element from the stack and jump to it; empty
435 stack = halt. */
436 unsigned16 result;
437
438 TRACE_INSN (cpu, "RET");
439 cpu->sp += 2;
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);
443
444 pc = result << 1;
445 TRACE_BRANCH (cpu, "RET -> %#x", pc);
446 }
447 else if (num1 == 19)
448 {
449 /* out: 19 a: Write the character <a> to the terminal. */
450 unsigned16 iw2, num2;
451
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);
457
458 sim_io_printf (sd, "%c", num2);
459
460 pc += 4;
461 }
462 else if (num1 == 20)
463 {
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;
469 char c;
470
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);
477
478 /* The challenge uses lowercase for all inputs, so insert some low level
479 helpers of our own to make it a bit nicer. */
480 switch (c)
481 {
482 case 'Q':
483 sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
484 break;
485 }
486
487 TRACE_REGISTER (cpu, "R%i = %#x", iw2 & 0xf, c);
488 cpu->regs[iw2 & 0xf] = c;
489
490 pc += 4;
491 }
492 else if (num1 == 21)
493 {
494 /* noop: 21: no operation */
495 TRACE_INSN (cpu, "NOOP");
496
497 pc += 2;
498 }
499 else
500 sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
501
502 TRACE_REGISTER (cpu, "PC = %#x", pc);
503 sim_pc_set (cpu, pc);
504}
505\f
506/* Return the program counter for this cpu. */
507static sim_cia
508pc_get (sim_cpu *cpu)
509{
510 return cpu->pc;
511}
512
513/* Set the program counter for this cpu to the new pc value. */
514static void
515pc_set (sim_cpu *cpu, sim_cia pc)
516{
517 cpu->pc = pc;
518}
519
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. */
523void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu)
524{
525 memset (cpu->regs, 0, sizeof (cpu->regs));
526 cpu->pc = 0;
527 /* Make sure it's initialized outside of the 16-bit address space. */
528 cpu->sp = 0x80000;
529
530 CPU_PC_FETCH (cpu) = pc_get;
531 CPU_PC_STORE (cpu) = pc_set;
532}