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