1 /* Example synacor simulator. 2 3 Copyright (C) 2005-2024 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 #include "example-synacor-sim.h" 31 32 /* Get the register number from the number. */ 33 static uint16_t 34 register_num (SIM_CPU *cpu, uint16_t num) 35 { 36 SIM_DESC sd = CPU_STATE (cpu); 37 38 if (num < 0x8000 || num >= 0x8008) 39 sim_engine_halt (sd, cpu, NULL, sim_pc_get (cpu), sim_signalled, SIM_SIGILL); 40 41 return num & 0xf; 42 } 43 44 /* Helper to process immediates according to the ISA. */ 45 static uint16_t 46 interp_num (SIM_CPU *cpu, uint16_t num) 47 { 48 SIM_DESC sd = CPU_STATE (cpu); 49 struct example_sim_cpu *example_cpu = EXAMPLE_SIM_CPU (cpu); 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); 61 return example_cpu->regs[num & 0xf]; 62 } 63 else 64 { 65 /* Numbers 32776..65535 are invalid. */ 66 TRACE_DECODE (cpu, "%#x is an invalid number", num); 67 sim_engine_halt (sd, cpu, NULL, example_cpu->pc, sim_signalled, SIM_SIGILL); 68 } 69 } 70 71 /* Decode & execute a single instruction. */ 72 void step_once (SIM_CPU *cpu) 73 { 74 SIM_DESC sd = CPU_STATE (cpu); 75 struct example_sim_cpu *example_cpu = EXAMPLE_SIM_CPU (cpu); 76 uint16_t iw1, num1; 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>. */ 93 uint16_t iw2, iw3, num2, num3; 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); 103 example_cpu->regs[num2] = num3; 104 105 pc += 6; 106 } 107 else if (num1 == 2) 108 { 109 /* push: 2 a: Push <a> onto the stack. */ 110 uint16_t iw2, num2; 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 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); 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. */ 127 uint16_t iw2, num2, result; 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); 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); 136 137 TRACE_REGISTER (cpu, "R%i = %#x", num2, result); 138 example_cpu->regs[num2] = result; 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. */ 146 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 160 example_cpu->regs[num2] = result; 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. */ 168 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 182 example_cpu->regs[num2] = result; 183 184 pc += 8; 185 } 186 else if (num1 == 6) 187 { 188 /* jmp: 6 a: Jump to <a>. */ 189 uint16_t iw2, num2; 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>. */ 204 uint16_t iw2, iw3, num2, num3; 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>. */ 227 uint16_t iw2, iw3, num2, num3; 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). */ 250 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 265 example_cpu->regs[num2] = result; 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). */ 273 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 288 example_cpu->regs[num2] = result; 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>. */ 295 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 309 example_cpu->regs[num2] = result; 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>. */ 316 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 330 example_cpu->regs[num2] = result; 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>. */ 337 uint16_t iw2, iw3, iw4, num2, num3, num4, result; 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); 351 example_cpu->regs[num2] = result; 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>. */ 358 uint16_t iw2, iw3, num2, num3, result; 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); 370 example_cpu->regs[num2] = result; 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>. */ 377 uint16_t iw2, iw3, num2, num3, result; 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); 392 example_cpu->regs[num2] = result; 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>. */ 399 uint16_t iw2, iw3, num2, num3; 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>. */ 419 uint16_t iw2, num2; 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); 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); 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. */ 440 uint16_t result; 441 442 TRACE_INSN (cpu, "RET"); 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); 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. */ 454 uint16_t iw2, num2; 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. */ 472 uint16_t iw2, num2; 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); 492 example_cpu->regs[iw2 & 0xf] = c; 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 510 /* Return the program counter for this cpu. */ 511 static sim_cia 512 pc_get (sim_cpu *cpu) 513 { 514 struct example_sim_cpu *example_cpu = EXAMPLE_SIM_CPU (cpu); 515 516 return example_cpu->pc; 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 { 523 struct example_sim_cpu *example_cpu = EXAMPLE_SIM_CPU (cpu); 524 525 example_cpu->pc = pc; 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 { 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; 537 /* Make sure it's initialized outside of the 16-bit address space. */ 538 example_cpu->sp = 0x80000; 539 540 CPU_PC_FETCH (cpu) = pc_get; 541 CPU_PC_STORE (cpu) = pc_set; 542 } 543