| 1 | /* |
| 2 | * This file is part of SIS. |
| 3 | * |
| 4 | * SIS, SPARC instruction simulator V1.6 Copyright (C) 1995 Jiri Gaisler, |
| 5 | * European Space Agency |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify it under |
| 8 | * the terms of the GNU General Public License as published by the Free |
| 9 | * Software Foundation; either version 2 of the License, or (at your option) |
| 10 | * any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT |
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 15 | * more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License along with |
| 18 | * this program; if not, write to the Free Software Foundation, Inc., 675 |
| 19 | * Mass Ave, Cambridge, MA 02139, USA. |
| 20 | * |
| 21 | */ |
| 22 | |
| 23 | #include <signal.h> |
| 24 | #include <string.h> |
| 25 | #include <stdio.h> |
| 26 | #include <stdlib.h> |
| 27 | #include <time.h> |
| 28 | #include <sys/fcntl.h> |
| 29 | #include "sis.h" |
| 30 | #include "bfd.h" |
| 31 | #include <dis-asm.h> |
| 32 | #include "sim-config.h" |
| 33 | |
| 34 | #include "gdb/remote-sim.h" |
| 35 | |
| 36 | #ifndef fprintf |
| 37 | extern fprintf(); |
| 38 | #endif |
| 39 | |
| 40 | #define PSR_CWP 0x7 |
| 41 | |
| 42 | #define VAL(x) strtol(x,(char **)NULL,0) |
| 43 | |
| 44 | extern char **buildargv(char *input); |
| 45 | |
| 46 | extern struct disassemble_info dinfo; |
| 47 | extern struct pstate sregs; |
| 48 | extern struct estate ebase; |
| 49 | |
| 50 | extern int current_target_byte_order; |
| 51 | extern int ctrl_c; |
| 52 | extern int nfp; |
| 53 | extern int ift; |
| 54 | extern int rom8; |
| 55 | extern int wrp; |
| 56 | extern int uben; |
| 57 | extern int sis_verbose; |
| 58 | extern char *sis_version; |
| 59 | extern struct estate ebase; |
| 60 | extern struct evcell evbuf[]; |
| 61 | extern struct irqcell irqarr[]; |
| 62 | extern int irqpend, ext_irl; |
| 63 | extern int sparclite; |
| 64 | extern int dumbio; |
| 65 | extern int sparclite_board; |
| 66 | extern int termsave; |
| 67 | extern char uart_dev1[], uart_dev2[]; |
| 68 | |
| 69 | int sis_gdb_break = 1; |
| 70 | |
| 71 | host_callback *sim_callback; |
| 72 | |
| 73 | int |
| 74 | run_sim(sregs, icount, dis) |
| 75 | struct pstate *sregs; |
| 76 | unsigned int icount; |
| 77 | int dis; |
| 78 | { |
| 79 | int mexc, irq; |
| 80 | |
| 81 | if (sis_verbose) |
| 82 | (*sim_callback->printf_filtered) (sim_callback, "resuming at %x\n", |
| 83 | sregs->pc); |
| 84 | init_stdio(); |
| 85 | sregs->starttime = time(NULL); |
| 86 | irq = 0; |
| 87 | while (!sregs->err_mode & (icount > 0)) { |
| 88 | |
| 89 | sregs->fhold = 0; |
| 90 | sregs->hold = 0; |
| 91 | sregs->icnt = 1; |
| 92 | |
| 93 | if (sregs->psr & 0x080) |
| 94 | sregs->asi = 8; |
| 95 | else |
| 96 | sregs->asi = 9; |
| 97 | |
| 98 | #if 0 /* DELETE ME! for debugging purposes only */ |
| 99 | if (sis_verbose > 1) |
| 100 | if (sregs->pc == 0 || sregs->npc == 0) |
| 101 | printf ("bogus pc or npc\n"); |
| 102 | #endif |
| 103 | mexc = memory_read(sregs->asi, sregs->pc, &sregs->inst, |
| 104 | 2, &sregs->hold); |
| 105 | #if 1 /* DELETE ME! for debugging purposes only */ |
| 106 | if (sis_verbose > 2) |
| 107 | printf("pc %x, np %x, sp %x, fp %x, wm %x, cw %x, i %08x\n", |
| 108 | sregs->pc, sregs->npc, |
| 109 | sregs->r[(((sregs->psr & 7) << 4) + 14) & 0x7f], |
| 110 | sregs->r[(((sregs->psr & 7) << 4) + 30) & 0x7f], |
| 111 | sregs->wim, |
| 112 | sregs->psr & 7, |
| 113 | sregs->inst); |
| 114 | #endif |
| 115 | if (sregs->annul) { |
| 116 | sregs->annul = 0; |
| 117 | sregs->icnt = 1; |
| 118 | sregs->pc = sregs->npc; |
| 119 | sregs->npc = sregs->npc + 4; |
| 120 | } else { |
| 121 | if (ext_irl) irq = check_interrupts(sregs); |
| 122 | if (!irq) { |
| 123 | if (mexc) { |
| 124 | sregs->trap = I_ACC_EXC; |
| 125 | } else { |
| 126 | if ((sis_gdb_break) && (sregs->inst == 0x91d02001)) { |
| 127 | if (sis_verbose) |
| 128 | (*sim_callback->printf_filtered) (sim_callback, |
| 129 | "SW BP hit at %x\n", sregs->pc); |
| 130 | sim_halt(); |
| 131 | restore_stdio(); |
| 132 | clearerr(stdin); |
| 133 | return (BPT_HIT); |
| 134 | } else |
| 135 | dispatch_instruction(sregs); |
| 136 | } |
| 137 | icount--; |
| 138 | } |
| 139 | if (sregs->trap) { |
| 140 | irq = 0; |
| 141 | sregs->err_mode = execute_trap(sregs); |
| 142 | } |
| 143 | } |
| 144 | advance_time(sregs); |
| 145 | if (ctrl_c) { |
| 146 | icount = 0; |
| 147 | } |
| 148 | } |
| 149 | sim_halt(); |
| 150 | sregs->tottime += time(NULL) - sregs->starttime; |
| 151 | restore_stdio(); |
| 152 | clearerr(stdin); |
| 153 | if (sregs->err_mode) |
| 154 | error_mode(sregs->pc); |
| 155 | if (sregs->err_mode) |
| 156 | return (ERROR); |
| 157 | if (sregs->bphit) { |
| 158 | if (sis_verbose) |
| 159 | (*sim_callback->printf_filtered) (sim_callback, |
| 160 | "HW BP hit at %x\n", sregs->pc); |
| 161 | return (BPT_HIT); |
| 162 | } |
| 163 | if (ctrl_c) { |
| 164 | ctrl_c = 0; |
| 165 | return (CTRL_C); |
| 166 | } |
| 167 | return (TIME_OUT); |
| 168 | } |
| 169 | |
| 170 | void |
| 171 | sim_set_callbacks (ptr) |
| 172 | host_callback *ptr; |
| 173 | { |
| 174 | sim_callback = ptr; |
| 175 | } |
| 176 | |
| 177 | void |
| 178 | sim_size (memsize) |
| 179 | int memsize; |
| 180 | { |
| 181 | } |
| 182 | |
| 183 | SIM_DESC |
| 184 | sim_open (kind, callback, abfd, argv) |
| 185 | SIM_OPEN_KIND kind; |
| 186 | struct host_callback_struct *callback; |
| 187 | struct bfd *abfd; |
| 188 | char **argv; |
| 189 | { |
| 190 | |
| 191 | int argc = 0; |
| 192 | int stat = 1; |
| 193 | int freq = 0; |
| 194 | |
| 195 | sim_callback = callback; |
| 196 | |
| 197 | while (argv[argc]) |
| 198 | argc++; |
| 199 | while (stat < argc) { |
| 200 | if (argv[stat][0] == '-') { |
| 201 | if (strcmp(argv[stat], "-v") == 0) { |
| 202 | sis_verbose++; |
| 203 | } else |
| 204 | if (strcmp(argv[stat], "-nfp") == 0) { |
| 205 | nfp = 1; |
| 206 | } else |
| 207 | if (strcmp(argv[stat], "-ift") == 0) { |
| 208 | ift = 1; |
| 209 | } else |
| 210 | if (strcmp(argv[stat], "-sparclite") == 0) { |
| 211 | sparclite = 1; |
| 212 | } else |
| 213 | if (strcmp(argv[stat], "-sparclite-board") == 0) { |
| 214 | sparclite_board = 1; |
| 215 | } else |
| 216 | if (strcmp(argv[stat], "-dumbio") == 0) { |
| 217 | dumbio = 1; |
| 218 | } else |
| 219 | if (strcmp(argv[stat], "-wrp") == 0) { |
| 220 | wrp = 1; |
| 221 | } else |
| 222 | if (strcmp(argv[stat], "-rom8") == 0) { |
| 223 | rom8 = 1; |
| 224 | } else |
| 225 | if (strcmp(argv[stat], "-uben") == 0) { |
| 226 | uben = 1; |
| 227 | } else |
| 228 | if (strcmp(argv[stat], "-uart1") == 0) { |
| 229 | if ((stat + 1) < argc) |
| 230 | strcpy(uart_dev1, argv[++stat]); |
| 231 | } else |
| 232 | if (strcmp(argv[stat], "-uart2") == 0) { |
| 233 | if ((stat + 1) < argc) |
| 234 | strcpy(uart_dev2, argv[++stat]); |
| 235 | } else |
| 236 | if (strcmp(argv[stat], "-nogdb") == 0) { |
| 237 | sis_gdb_break = 0; |
| 238 | } else |
| 239 | if (strcmp(argv[stat], "-freq") == 0) { |
| 240 | if ((stat + 1) < argc) { |
| 241 | freq = VAL(argv[++stat]); |
| 242 | } |
| 243 | } else { |
| 244 | (*sim_callback->printf_filtered) (sim_callback, |
| 245 | "unknown option %s\n", |
| 246 | argv[stat]); |
| 247 | } |
| 248 | } else |
| 249 | bfd_load(argv[stat]); |
| 250 | stat++; |
| 251 | } |
| 252 | |
| 253 | if (sis_verbose) { |
| 254 | (*sim_callback->printf_filtered) (sim_callback, "\n SIS - SPARC instruction simulator %s\n", sis_version); |
| 255 | (*sim_callback->printf_filtered) (sim_callback, " Bug-reports to Jiri Gaisler ESA/ESTEC (jgais@wd.estec.esa.nl)\n"); |
| 256 | if (nfp) |
| 257 | (*sim_callback->printf_filtered) (sim_callback, "no FPU\n"); |
| 258 | if (sparclite) |
| 259 | (*sim_callback->printf_filtered) (sim_callback, "simulating Sparclite\n"); |
| 260 | if (dumbio) |
| 261 | (*sim_callback->printf_filtered) (sim_callback, "dumb IO (no input, dumb output)\n"); |
| 262 | if (sis_gdb_break == 0) |
| 263 | (*sim_callback->printf_filtered) (sim_callback, "disabling GDB trap handling for breakpoints\n"); |
| 264 | if (freq) |
| 265 | (*sim_callback->printf_filtered) (sim_callback, " ERC32 freq %d Mhz\n", freq); |
| 266 | } |
| 267 | |
| 268 | sregs.freq = freq ? freq : 15; |
| 269 | termsave = fcntl(0, F_GETFL, 0); |
| 270 | INIT_DISASSEMBLE_INFO(dinfo, stdout,(fprintf_ftype)fprintf); |
| 271 | dinfo.endian = BFD_ENDIAN_BIG; |
| 272 | reset_all(); |
| 273 | ebase.simtime = 0; |
| 274 | init_sim(); |
| 275 | init_bpt(&sregs); |
| 276 | reset_stat(&sregs); |
| 277 | |
| 278 | /* Fudge our descriptor for now. */ |
| 279 | return (SIM_DESC) 1; |
| 280 | } |
| 281 | |
| 282 | void |
| 283 | sim_close(sd, quitting) |
| 284 | SIM_DESC sd; |
| 285 | int quitting; |
| 286 | { |
| 287 | |
| 288 | exit_sim(); |
| 289 | fcntl(0, F_SETFL, termsave); |
| 290 | |
| 291 | }; |
| 292 | |
| 293 | SIM_RC |
| 294 | sim_load(sd, prog, abfd, from_tty) |
| 295 | SIM_DESC sd; |
| 296 | char *prog; |
| 297 | bfd *abfd; |
| 298 | int from_tty; |
| 299 | { |
| 300 | bfd_load (prog); |
| 301 | return SIM_RC_OK; |
| 302 | } |
| 303 | |
| 304 | SIM_RC |
| 305 | sim_create_inferior(sd, abfd, argv, env) |
| 306 | SIM_DESC sd; |
| 307 | struct bfd *abfd; |
| 308 | char **argv; |
| 309 | char **env; |
| 310 | { |
| 311 | bfd_vma start_address = 0; |
| 312 | if (abfd != NULL) |
| 313 | start_address = bfd_get_start_address (abfd); |
| 314 | |
| 315 | ebase.simtime = 0; |
| 316 | reset_all(); |
| 317 | reset_stat(&sregs); |
| 318 | sregs.pc = start_address & ~3; |
| 319 | sregs.npc = sregs.pc + 4; |
| 320 | return SIM_RC_OK; |
| 321 | } |
| 322 | |
| 323 | int |
| 324 | sim_store_register(sd, regno, value, length) |
| 325 | SIM_DESC sd; |
| 326 | int regno; |
| 327 | unsigned char *value; |
| 328 | int length; |
| 329 | { |
| 330 | /* FIXME: Review the computation of regval. */ |
| 331 | int regval; |
| 332 | if (current_target_byte_order == BIG_ENDIAN) |
| 333 | regval = (value[0] << 24) | (value[1] << 16) |
| 334 | | (value[2] << 8) | value[3]; |
| 335 | else |
| 336 | regval = (value[3] << 24) | (value[2] << 16) |
| 337 | | (value[1] << 8) | value[0]; |
| 338 | set_regi(&sregs, regno, regval); |
| 339 | return -1; |
| 340 | } |
| 341 | |
| 342 | |
| 343 | int |
| 344 | sim_fetch_register(sd, regno, buf, length) |
| 345 | SIM_DESC sd; |
| 346 | int regno; |
| 347 | unsigned char *buf; |
| 348 | int length; |
| 349 | { |
| 350 | get_regi(&sregs, regno, buf); |
| 351 | return -1; |
| 352 | } |
| 353 | |
| 354 | int |
| 355 | sim_write(sd, mem, buf, length) |
| 356 | SIM_DESC sd; |
| 357 | SIM_ADDR mem; |
| 358 | unsigned char *buf; |
| 359 | int length; |
| 360 | { |
| 361 | return (sis_memory_write(mem, buf, length)); |
| 362 | } |
| 363 | |
| 364 | int |
| 365 | sim_read(sd, mem, buf, length) |
| 366 | SIM_DESC sd; |
| 367 | SIM_ADDR mem; |
| 368 | unsigned char *buf; |
| 369 | int length; |
| 370 | { |
| 371 | return (sis_memory_read(mem, buf, length)); |
| 372 | } |
| 373 | |
| 374 | void |
| 375 | sim_info(sd, verbose) |
| 376 | SIM_DESC sd; |
| 377 | int verbose; |
| 378 | { |
| 379 | show_stat(&sregs); |
| 380 | } |
| 381 | |
| 382 | int simstat = OK; |
| 383 | |
| 384 | void |
| 385 | sim_stop_reason(sd, reason, sigrc) |
| 386 | SIM_DESC sd; |
| 387 | enum sim_stop * reason; |
| 388 | int *sigrc; |
| 389 | { |
| 390 | |
| 391 | switch (simstat) { |
| 392 | case CTRL_C: |
| 393 | *reason = sim_stopped; |
| 394 | *sigrc = SIGINT; |
| 395 | break; |
| 396 | case OK: |
| 397 | case TIME_OUT: |
| 398 | case BPT_HIT: |
| 399 | *reason = sim_stopped; |
| 400 | #ifdef _WIN32 |
| 401 | #define SIGTRAP 5 |
| 402 | #endif |
| 403 | *sigrc = SIGTRAP; |
| 404 | break; |
| 405 | case ERROR: |
| 406 | *sigrc = 0; |
| 407 | *reason = sim_exited; |
| 408 | } |
| 409 | ctrl_c = 0; |
| 410 | simstat = OK; |
| 411 | } |
| 412 | |
| 413 | /* Flush all register windows out to the stack. Starting after the invalid |
| 414 | window, flush all windows up to, and including the current window. This |
| 415 | allows GDB to do backtraces and look at local variables for frames that |
| 416 | are still in the register windows. Note that strictly speaking, this |
| 417 | behavior is *wrong* for several reasons. First, it doesn't use the window |
| 418 | overflow handlers. It therefore assumes standard frame layouts and window |
| 419 | handling policies. Second, it changes system state behind the back of the |
| 420 | target program. I expect this to mainly pose problems when debugging trap |
| 421 | handlers. |
| 422 | */ |
| 423 | |
| 424 | static void |
| 425 | flush_windows () |
| 426 | { |
| 427 | int invwin; |
| 428 | int cwp; |
| 429 | int win; |
| 430 | int ws; |
| 431 | |
| 432 | /* Keep current window handy */ |
| 433 | |
| 434 | cwp = sregs.psr & PSR_CWP; |
| 435 | |
| 436 | /* Calculate the invalid window from the wim. */ |
| 437 | |
| 438 | for (invwin = 0; invwin <= PSR_CWP; invwin++) |
| 439 | if ((sregs.wim >> invwin) & 1) |
| 440 | break; |
| 441 | |
| 442 | /* Start saving with the window after the invalid window. */ |
| 443 | |
| 444 | invwin = (invwin - 1) & PSR_CWP; |
| 445 | |
| 446 | for (win = invwin; ; win = (win - 1) & PSR_CWP) |
| 447 | { |
| 448 | uint32 sp; |
| 449 | int i; |
| 450 | |
| 451 | sp = sregs.r[(win * 16 + 14) & 0x7f]; |
| 452 | #if 1 |
| 453 | if (sis_verbose > 2) { |
| 454 | uint32 fp = sregs.r[(win * 16 + 30) & 0x7f]; |
| 455 | printf("flush_window: win %d, sp %x, fp %x\n", win, sp, fp); |
| 456 | } |
| 457 | #endif |
| 458 | |
| 459 | for (i = 0; i < 16; i++) |
| 460 | memory_write (11, sp + 4 * i, &sregs.r[(win * 16 + 16 + i) & 0x7f], 2, |
| 461 | &ws); |
| 462 | |
| 463 | if (win == cwp) |
| 464 | break; |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | void |
| 469 | sim_resume(SIM_DESC sd, int step, int siggnal) |
| 470 | { |
| 471 | simstat = run_sim(&sregs, -1, 0); |
| 472 | |
| 473 | if (sis_gdb_break) flush_windows (); |
| 474 | } |
| 475 | |
| 476 | int |
| 477 | sim_trace (sd) |
| 478 | SIM_DESC sd; |
| 479 | { |
| 480 | /* FIXME: unfinished */ |
| 481 | sim_resume (sd, 0, 0); |
| 482 | return 1; |
| 483 | } |
| 484 | |
| 485 | void |
| 486 | sim_do_command(sd, cmd) |
| 487 | SIM_DESC sd; |
| 488 | char *cmd; |
| 489 | { |
| 490 | exec_cmd(&sregs, cmd); |
| 491 | } |
| 492 | |
| 493 | #if 0 /* FIXME: These shouldn't exist. */ |
| 494 | |
| 495 | int |
| 496 | sim_insert_breakpoint(int addr) |
| 497 | { |
| 498 | if (sregs.bptnum < BPT_MAX) { |
| 499 | sregs.bpts[sregs.bptnum] = addr & ~0x3; |
| 500 | sregs.bptnum++; |
| 501 | if (sis_verbose) |
| 502 | (*sim_callback->printf_filtered) (sim_callback, "inserted HW BP at %x\n", addr); |
| 503 | return 0; |
| 504 | } else |
| 505 | return 1; |
| 506 | } |
| 507 | |
| 508 | int |
| 509 | sim_remove_breakpoint(int addr) |
| 510 | { |
| 511 | int i = 0; |
| 512 | |
| 513 | while ((i < sregs.bptnum) && (sregs.bpts[i] != addr)) |
| 514 | i++; |
| 515 | if (addr == sregs.bpts[i]) { |
| 516 | for (; i < sregs.bptnum - 1; i++) |
| 517 | sregs.bpts[i] = sregs.bpts[i + 1]; |
| 518 | sregs.bptnum -= 1; |
| 519 | if (sis_verbose) |
| 520 | (*sim_callback->printf_filtered) (sim_callback, "removed HW BP at %x\n", addr); |
| 521 | return 0; |
| 522 | } |
| 523 | return 1; |
| 524 | } |
| 525 | |
| 526 | #endif |