xref: /netbsd-src/external/gpl3/gdb/dist/sim/example-synacor/sim-main.c (revision 2dd295436a0082eb4f8d294f4aa73c223413d0f2)
1 /* Example synacor simulator.
2 
3    Copyright (C) 2005-2023 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 /* This must come before any other includes.  */
25 #include "defs.h"
26 
27 #include "sim-main.h"
28 #include "sim-signal.h"
29 
30 /* Get the register number from the number.  */
31 static uint16_t
32 register_num (SIM_CPU *cpu, uint16_t 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.  */
43 static uint16_t
44 interp_num (SIM_CPU *cpu, uint16_t 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 
68 /* Decode & execute a single instruction.  */
69 void step_once (SIM_CPU *cpu)
70 {
71   SIM_DESC sd = CPU_STATE (cpu);
72   uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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       uint16_t 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 
506 /* Return the program counter for this cpu. */
507 static sim_cia
508 pc_get (sim_cpu *cpu)
509 {
510   return cpu->pc;
511 }
512 
513 /* Set the program counter for this cpu to the new pc value. */
514 static void
515 pc_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.  */
523 void 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 }
533