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