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