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.  */
31static uint16_t
32register_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.  */
43static uint16_t
44interp_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.  */
69void 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. */
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}
533