Showing the changes to src/exec/debugger.cpp (1 of 3 files). Show all
Unified · Split
| @@ -73,8 +73,26 @@ namespace · 72 unmodified lines | |||
| 73 | << "\n"; | 73 | << "\n"; |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // Debug command context | ||
| 77 | struct dbg_cmd_cxt_t | ||
| 78 | { | ||
| 79 | dbg_cmd_cxt_t(const vm_t &v, const vm_registers_t &r) | ||
| 80 | : initial_register{ r } | ||
| 81 | , exit_break{ false } | ||
| 82 | , vm{ v } | ||
| 83 | { | ||
| 84 | current_register = &initial_register; | ||
| 85 | } | ||
| 86 | |||
| 87 | bool exit_break; | ||
| 88 | |||
| 89 | const vm_t &vm; | ||
| 90 | const vm_registers_t *current_register; | ||
| 91 | const vm_registers_t &initial_register; | ||
| 92 | }; | ||
| 93 | |||
| 76 | // Debug command exec operation | 94 | // Debug command exec operation |
| 77 | using dbg_cmd_exec_t = bool(*)(const std::vector<std::string> &, const vm_t &, const vm_registers_t &); | 95 | using dbg_cmd_exec_t = void(*)(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt); |
| 78 | 96 | ||
| 79 | // Debug command | 97 | // Debug command |
| 80 | struct dbg_cmd_t | 98 | struct dbg_cmd_t |
| @@ -118,9 +136,49 @@ namespace · 37 unmodified lines | |||
| 118 | 136 | ||
| 119 | std::ostream& operator<<(std::ostream &ss, const vm_string_t *s) | 137 | std::ostream& operator<<(std::ostream &ss, const vm_string_t *s) |
| 120 | { | 138 | { |
| 139 | auto escaped_string = std::stringstream{}; | ||
| 140 | auto str = s->str(); | ||
| 141 | if (str != nullptr) | ||
| 142 | { | ||
| 143 | while (*str != '\0') | ||
| 144 | { | ||
| 145 | switch (*str) | ||
| 146 | { | ||
| 147 | case '\\': | ||
| 148 | escaped_string << "\\\\"; | ||
| 149 | break; | ||
| 150 | case '\a': | ||
| 151 | escaped_string << "\\a"; | ||
| 152 | break; | ||
| 153 | case '\b': | ||
| 154 | escaped_string << "\\b"; | ||
| 155 | break; | ||
| 156 | case '\f': | ||
| 157 | escaped_string << "\\f"; | ||
| 158 | break; | ||
| 159 | case '\n': | ||
| 160 | escaped_string << "\\n"; | ||
| 161 | break; | ||
| 162 | case '\r': | ||
| 163 | escaped_string << "\\r"; | ||
| 164 | break; | ||
| 165 | case '\t': | ||
| 166 | escaped_string << "\\t"; | ||
| 167 | break; | ||
| 168 | case '\v': | ||
| 169 | escaped_string << "\\v"; | ||
| 170 | break; | ||
| 171 | default: | ||
| 172 | escaped_string << *str; | ||
| 173 | } | ||
| 174 | |||
| 175 | str++; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 121 | ss << "string\n" | 179 | ss << "string\n" |
| 122 | << "\tlength: " << s->get_length() << "\n" | 180 | << "\tlength: " << s->get_length() << "\n" |
| 123 | << "\tvalue: " << s->str() << "\n"; | 181 | << "\tvalue: \"" << escaped_string.str() << "\"\n"; |
| 124 | 182 | ||
| 125 | return ss; | 183 | return ss; |
| 126 | } | 184 | } |
| @@ -149,10 +207,33 @@ namespace · 22 unmodified lines | |||
| 149 | return ss; | 207 | return ss; |
| 150 | } | 208 | } |
| 151 | 209 | ||
| 152 | std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *a) | 210 | std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *alloc) |
| 153 | { | 211 | { |
| 154 | ss << "alloc\n" | 212 | if (alloc == nullptr) |
| 155 | << "\tsize: " << a->alloc_type->size_in_bytes << "\n"; | 213 | { |
| 214 | ss << "<nil>"; | ||
| 215 | } | ||
| 216 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>()) | ||
| 217 | { | ||
| 218 | ss << vm_alloc_t::from_allocation<vm_string_t>(alloc->get_allocation()); | ||
| 219 | } | ||
| 220 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>()) | ||
| 221 | { | ||
| 222 | ss << vm_alloc_t::from_allocation<vm_array_t>(alloc->get_allocation()); | ||
| 223 | } | ||
| 224 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>()) | ||
| 225 | { | ||
| 226 | ss << vm_alloc_t::from_allocation<vm_list_t>(alloc->get_allocation()); | ||
| 227 | } | ||
| 228 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>()) | ||
| 229 | { | ||
| 230 | ss << vm_alloc_t::from_allocation<vm_channel_t>(alloc->get_allocation()); | ||
| 231 | } | ||
| 232 | else | ||
| 233 | { | ||
| 234 | ss << "alloc\n" | ||
| 235 | << "\tsize: " << alloc->alloc_type->size_in_bytes << "\n"; | ||
| 236 | } | ||
| 156 | 237 | ||
| 157 | return ss; | 238 | return ss; |
| 158 | } | 239 | } |
| @@ -210,72 +291,54 @@ namespace · 51 unmodified lines | |||
| 210 | auto frame_string = std::stringstream{}; | 291 | auto frame_string = std::stringstream{}; |
| 211 | enum_pointer_fields(*frame->frame_type, frame->base(), [&frame_string](pointer_t p) | 292 | enum_pointer_fields(*frame->frame_type, frame->base(), [&frame_string](pointer_t p) |
| 212 | { | 293 | { |
| 213 | frame_string << std::showbase << std::hex << reinterpret_cast<uintptr_t>(p) << " : " << std::dec; | 294 | frame_string << std::showbase << std::hex << reinterpret_cast<uintptr_t>(p) << " : " << std::dec << vm_alloc_t::from_allocation(p); |
| 214 | |||
| 215 | auto alloc = vm_alloc_t::from_allocation(p); | ||
| 216 | if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>()) | ||
| 217 | frame_string << vm_alloc_t::from_allocation<vm_string_t>(p); | ||
| 218 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>()) | ||
| 219 | frame_string << vm_alloc_t::from_allocation<vm_array_t>(p); | ||
| 220 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>()) | ||
| 221 | frame_string << vm_alloc_t::from_allocation<vm_list_t>(p); | ||
| 222 | else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>()) | ||
| 223 | frame_string << vm_alloc_t::from_allocation<vm_channel_t>(p); | ||
| 224 | else | ||
| 225 | frame_string << alloc; | ||
| 226 | }); | 295 | }); |
| 227 | 296 | ||
| 228 | return frame_string.str(); | 297 | return frame_string.str(); |
| 229 | } | 298 | } |
| 230 | 299 | ||
| 231 | bool cmd_continue(const std::vector<std::string> &, const vm_t &, const vm_registers_t &) | 300 | void cmd_continue(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 232 | { | 301 | { |
| 233 | return false; | 302 | cxt.exit_break = true; |
| 234 | } | 303 | } |
| 235 | 304 | ||
| 236 | bool cmd_print_registers(const std::vector<std::string> &, const vm_t &, const vm_registers_t &r) | 305 | void cmd_print_registers(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 237 | { | 306 | { |
| 238 | auto s = registers_to_string(r); | 307 | auto s = registers_to_string(*cxt.current_register); |
| 239 | debug_print_info(s); | 308 | debug_print_info(s); |
| 240 | |||
| 241 | return true; | ||
| 242 | } | 309 | } |
| 243 | 310 | ||
| 244 | bool cmd_print_threads(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &) | 311 | void cmd_print_threads(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 245 | { | 312 | { |
| 246 | auto msg = std::stringstream{}; | 313 | auto msg = std::stringstream{}; |
| 247 | msg << "Threads:\n"; | 314 | msg << "Threads:\n"; |
| 248 | for (auto t : vm.get_scheduler_control().get_all_threads()) | 315 | for (auto t : cxt.vm.get_scheduler_control().get_all_threads()) |
| 249 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; | 316 | msg << " " << std::setw(5) << t->get_thread_id() << " - " << t->get_state() << "\n"; |
| 250 | 317 | ||
| 251 | debug_print_info(msg.str()); | 318 | debug_print_info(msg.str()); |
| 252 | |||
| 253 | return true; | ||
| 254 | } | 319 | } |
| 255 | 320 | ||
| 256 | bool cmd_print_modules(const std::vector<std::string> &, const vm_t &vm, const vm_registers_t &) | 321 | void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt) |
| 257 | { | 322 | { |
| 258 | auto msg = std::stringstream{}; | 323 | auto msg = std::stringstream{}; |
| 259 | msg << "Modules:\n"; | 324 | msg << "Modules:\n"; |
| 260 | for (auto m : vm.get_loaded_modules()) | 325 | for (auto m : cxt.vm.get_loaded_modules()) |
| 261 | msg << " " << m->module_name->str() << "\n"; | 326 | msg << " " << m->module_name->str() << "\n"; |
| 262 | 327 | ||
| 263 | debug_print_info(msg.str()); | 328 | debug_print_info(msg.str()); |
| 264 | |||
| 265 | return true; | ||
| 266 | } | 329 | } |
| 267 | 330 | ||
| 268 | bool cmd_stacktrace(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r) | 331 | void cmd_stacktrace(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 269 | { | 332 | { |
| 270 | auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; | 333 | auto thread_registers = std::map<uint32_t, const vm_registers_t*>{}; |
| 271 | if (a.size() == 1) | 334 | if (a.size() == 1) |
| 272 | { | 335 | { |
| 273 | thread_registers[r.thread.get_thread_id()] = &r; | 336 | thread_registers[cxt.current_register->thread.get_thread_id()] = cxt.current_register; |
| 274 | } | 337 | } |
| 275 | else | 338 | else |
| 276 | { | 339 | { |
| 277 | uint32_t thread_id; | 340 | uint32_t thread_id; |
| 278 | auto all_threads = vm.get_scheduler_control().get_all_threads(); | 341 | auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); |
| 279 | if (a[1].compare("*") == 0) | 342 | if (a[1].compare("*") == 0) |
| 280 | { | 343 | { |
| 281 | thread_id = 0; | 344 | thread_id = 0; |
| @@ -289,7 +352,7 @@ namespace · 7 unmodified lines | |||
| 289 | catch (...) | 352 | catch (...) |
| 290 | { | 353 | { |
| 291 | debug_print_error("Invalid thread ID format"); | 354 | debug_print_error("Invalid thread ID format"); |
| 292 | return true; | 355 | return; |
| 293 | } | 356 | } |
| 294 | } | 357 | } |
| 295 | 358 | ||
| @@ -311,7 +374,7 @@ namespace · 15 unmodified lines | |||
| 311 | if (thread_registers.empty()) | 374 | if (thread_registers.empty()) |
| 312 | { | 375 | { |
| 313 | debug_print_error("Unknown thread ID"); | 376 | debug_print_error("Unknown thread ID"); |
| 314 | return true; | 377 | return; |
| 315 | } | 378 | } |
| 316 | 379 | ||
| 317 | auto msg = std::stringstream{}; | 380 | auto msg = std::stringstream{}; |
| @@ -323,19 +386,14 @@ namespace · 5 unmodified lines | |||
| 323 | } | 386 | } |
| 324 | 387 | ||
| 325 | debug_print_info(msg.str()); | 388 | debug_print_info(msg.str()); |
| 326 | |||
| 327 | return true; | ||
| 328 | } | 389 | } |
| 329 | 390 | ||
| 330 | // Forward declaration | 391 | void cmd_switchthread(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 331 | void prompt(const vm_t &, const vm_registers_t &); | ||
| 332 | |||
| 333 | bool cmd_switchthread(const std::vector<std::string> &a, const vm_t &vm, const vm_registers_t &r) | ||
| 334 | { | 392 | { |
| 335 | if (a.size() == 1) | 393 | if (a.size() == 1) |
| 336 | { | 394 | { |
| 337 | debug_print_error("Must supply a target thread ID"); | 395 | debug_print_error("Must supply a target thread ID"); |
| 338 | return true; | 396 | return; |
| 339 | } | 397 | } |
| 340 | 398 | ||
| 341 | uint32_t thread_id; | 399 | uint32_t thread_id; |
| @@ -346,10 +404,10 @@ namespace · 4 unmodified lines | |||
| 346 | catch (...) | 404 | catch (...) |
| 347 | { | 405 | { |
| 348 | debug_print_error("Invalid thread ID format"); | 406 | debug_print_error("Invalid thread ID format"); |
| 349 | return true; | 407 | return; |
| 350 | } | 408 | } |
| 351 | 409 | ||
| 352 | auto all_threads = vm.get_scheduler_control().get_all_threads(); | 410 | auto all_threads = cxt.vm.get_scheduler_control().get_all_threads(); |
| 353 | 411 | ||
| 354 | // Find the thread | 412 | // Find the thread |
| 355 | for (auto t : all_threads) | 413 | for (auto t : all_threads) |
| @@ -357,18 +415,15 @@ namespace · 1 unmodified line | |||
| 357 | const auto tid = t->get_thread_id(); | 415 | const auto tid = t->get_thread_id(); |
| 358 | if (tid == thread_id) | 416 | if (tid == thread_id) |
| 359 | { | 417 | { |
| 360 | prompt(vm, t->get_registers()); | 418 | cxt.current_register = &t->get_registers(); |
| 361 | 419 | return; | |
| 362 | // If we are returning from a thread switch we are not continuing, so return false to indicate as such. | ||
| 363 | return false; | ||
| 364 | } | 420 | } |
| 365 | } | 421 | } |
| 366 | 422 | ||
| 367 | debug_print_error("Unknown thread ID"); | 423 | debug_print_error("Unknown thread ID"); |
| 368 | return true; | ||
| 369 | } | 424 | } |
| 370 | 425 | ||
| 371 | bool cmd_disassemble(const std::vector<std::string> &a, const vm_t &, const vm_registers_t &r) | 426 | void cmd_disassemble(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) |
| 372 | { | 427 | { |
| 373 | auto disassemble_length = 1; | 428 | auto disassemble_length = 1; |
| 374 | if (a.size() > 1) | 429 | if (a.size() > 1) |
| @@ -380,10 +435,11 @@ namespace · 5 unmodified lines | |||
| 380 | catch (...) | 435 | catch (...) |
| 381 | { | 436 | { |
| 382 | debug_print_error("Invalid disassemble count format"); | 437 | debug_print_error("Invalid disassemble count format"); |
| 383 | return true; | 438 | return; |
| 384 | } | 439 | } |
| 385 | } | 440 | } |
| 386 | 441 | ||
| 442 | const auto &r = *cxt.current_register; | ||
| 387 | const auto &code_section = r.module_ref->code_section; | 443 | const auto &code_section = r.module_ref->code_section; |
| 388 | 444 | ||
| 389 | auto begin_index = static_cast<std::size_t>(r.pc); | 445 | auto begin_index = static_cast<std::size_t>(r.pc); |
| @@ -404,21 +460,122 @@ namespace · 14 unmodified lines | |||
| 404 | msg << " " << std::setw(5) << i << ": " << code_section[i].op << "\n"; | 460 | msg << " " << std::setw(5) << i << ": " << code_section[i].op << "\n"; |
| 405 | 461 | ||
| 406 | debug_print_info(msg.str()); | 462 | debug_print_info(msg.str()); |
| 463 | } | ||
| 464 | |||
| 465 | void cmd_examine(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt) | ||
| 466 | { | ||
| 467 | if (a.size() < 3) | ||
| 468 | { | ||
| 469 | debug_print_error("Invalid number of arguments"); | ||
| 470 | return; | ||
| 471 | } | ||
| 407 | 472 | ||
| 408 | return true; | 473 | std::shared_ptr<const type_descriptor_t> base_type; |
| 474 | word_t *base_pointer; | ||
| 475 | auto &base_ptr_id = a[1]; | ||
| 476 | if (base_ptr_id.compare("mp") == 0) | ||
| 477 | { | ||
| 478 | base_type = cxt.current_register->mp_base->alloc_type; | ||
| 479 | base_pointer = reinterpret_cast<word_t *>(cxt.current_register->mp_base->get_allocation()); | ||
| 480 | } | ||
| 481 | else if (base_ptr_id.compare("fp") == 0) | ||
| 482 | { | ||
| 483 | auto frame = cxt.current_register->stack.peek_frame(); | ||
| 484 | if (frame == nullptr) | ||
| 485 | { | ||
| 486 | debug_print_error("Invalid frame pointer "); | ||
| 487 | return; | ||
| 488 | } | ||
| 489 | |||
| 490 | base_type = frame->frame_type; | ||
| 491 | base_pointer = reinterpret_cast<word_t *>(frame->base()); | ||
| 492 | } | ||
| 493 | else | ||
| 494 | { | ||
| 495 | debug_print_error("Invalid memory base pointer"); | ||
| 496 | return; | ||
| 497 | } | ||
| 498 | |||
| 499 | assert(base_type != nullptr && base_pointer != nullptr); | ||
| 500 | |||
| 501 | const auto pointer_size = static_cast<int>(sizeof(pointer_t)); | ||
| 502 | auto byte_offset1 = int{ 0 }; | ||
| 503 | auto byte_offset2 = int{ -1 }; | ||
| 504 | try | ||
| 505 | { | ||
| 506 | byte_offset1 = std::stoi(a[2]); | ||
| 507 | |||
| 508 | // The 4th argument is optional | ||
| 509 | if (a.size() >= 4) | ||
| 510 | byte_offset2 = std::stoi(a[3]); | ||
| 511 | } | ||
| 512 | catch (...) | ||
| 513 | { | ||
| 514 | debug_print_error("Invalid memory offset format"); | ||
| 515 | return; | ||
| 516 | } | ||
| 517 | |||
| 518 | if (0 > byte_offset1 || byte_offset1 >= base_type->size_in_bytes) | ||
| 519 | { | ||
| 520 | debug_print_error("First offset out of range"); | ||
| 521 | return; | ||
| 522 | } | ||
| 523 | |||
| 524 | auto msg = std::stringstream{}; | ||
| 525 | if (byte_offset2 != -1) | ||
| 526 | msg << " " << byte_offset2 << "(" << byte_offset1 << "(" << a[1] << ")):\n"; | ||
| 527 | else | ||
| 528 | msg << " " << byte_offset1 << "(" << a[1] << "):\n"; | ||
| 529 | |||
| 530 | auto value_is_pointer = bool{ false }; | ||
| 531 | const auto pointer_offset1 = byte_offset1 / pointer_size; | ||
| 532 | auto value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset1]); | ||
| 533 | if (runtime::is_offset_pointer(*base_type, pointer_offset1)) | ||
| 534 | { | ||
| 535 | value_is_pointer = true; | ||
| 536 | auto alloc_to_examine = vm_alloc_t::from_allocation(value_to_examine); | ||
| 537 | if (byte_offset2 != -1) | ||
| 538 | { | ||
| 539 | if (alloc_to_examine == nullptr) | ||
| 540 | { | ||
| 541 | debug_print_error("First offset is nil"); | ||
| 542 | return; | ||
| 543 | } | ||
| 544 | |||
| 545 | if (0 > byte_offset2 || byte_offset2 >= alloc_to_examine->alloc_type->size_in_bytes) | ||
| 546 | { | ||
| 547 | debug_print_error("Second offset out of range"); | ||
| 548 | return; | ||
| 549 | } | ||
| 550 | |||
| 551 | // Update data pointer for 2nd indirection | ||
| 552 | base_pointer = reinterpret_cast<word_t *>(alloc_to_examine->get_allocation()); | ||
| 553 | base_type = alloc_to_examine->alloc_type; | ||
| 554 | |||
| 555 | const auto pointer_offset2 = byte_offset2 / pointer_size; | ||
| 556 | value_to_examine = reinterpret_cast<pointer_t>(base_pointer[pointer_offset2]); | ||
| 557 | value_is_pointer = runtime::is_offset_pointer(*base_type, pointer_offset2); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | if (value_is_pointer) | ||
| 562 | msg << " " << vm_alloc_t::from_allocation(value_to_examine); | ||
| 563 | else | ||
| 564 | msg << " " << std::setw(8) << std::showbase << std::hex << value_to_examine << std::dec; | ||
| 565 | |||
| 566 | debug_print_info(msg.str()); | ||
| 409 | } | 567 | } |
| 410 | 568 | ||
| 411 | // Forward declaration | 569 | // Forward declaration |
| 412 | void print_help(); | 570 | void print_help(); |
| 413 | 571 | ||
| 414 | bool cmd_help(const std::vector<std::string> &, const vm_t &, const vm_registers_t &) | 572 | void cmd_help(const std::vector<std::string> &, dbg_cmd_cxt_t &) |
| 415 | { | 573 | { |
| 416 | print_help(); | 574 | print_help(); |
| 417 | return true; | ||
| 418 | } | 575 | } |
| 419 | 576 | ||
| 420 | // Easter egg | 577 | // ee |
| 421 | bool cmd_tlk_tilde_star(const std::vector<std::string> &, const vm_t &, const vm_registers_t &) | 578 | void cmd_tlk_tilde_star(const std::vector<std::string> &, dbg_cmd_cxt_t &) |
| 422 | { | 579 | { |
| 423 | std::cout | 580 | std::cout |
| 424 | << console_modifiers::black_font | 581 | << console_modifiers::black_font |
| @@ -460,8 +617,6 @@ namespace · 35 unmodified lines | |||
| 460 | << console_modifiers::bold | 617 | << console_modifiers::bold |
| 461 | << "!\n" | 618 | << "!\n" |
| 462 | << console_modifiers::reset_all; | 619 | << console_modifiers::reset_all; |
| 463 | |||
| 464 | return true; | ||
| 465 | } | 620 | } |
| 466 | 621 | ||
| 467 | const dbg_cmd_t cmds[] = | 622 | const dbg_cmd_t cmds[] = |
| @@ -473,7 +628,10 @@ namespace · 5 unmodified lines | |||
| 473 | { "sw", "Stack walk/back trace", "bt ([0-9]+|\\*)?", "bt, bt 34, bt *", cmd_stacktrace }, | 628 | { "sw", "Stack walk/back trace", "bt ([0-9]+|\\*)?", "bt, bt 34, bt *", cmd_stacktrace }, |
| 474 | { "st", "Switch to thread", "st [0-9]+", "st 42", cmd_switchthread }, | 629 | { "st", "Switch to thread", "st [0-9]+", "st 42", cmd_switchthread }, |
| 475 | { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, | 630 | { "d", "Disassemble the next/previous N instructions", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, |
| 631 | { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine }, | ||
| 476 | { "?", "Print help", nullptr, nullptr, cmd_help }, | 632 | { "?", "Print help", nullptr, nullptr, cmd_help }, |
| 633 | |||
| 634 | // Undocumented commands | ||
| 477 | { "tlk~*", nullptr, nullptr, nullptr, cmd_tlk_tilde_star } | 635 | { "tlk~*", nullptr, nullptr, nullptr, cmd_tlk_tilde_star } |
| 478 | }; | 636 | }; |
| 479 | 637 | ||
| @@ -548,12 +706,14 @@ namespace · 68 unmodified lines | |||
| 548 | 706 | ||
| 549 | std::locale loc; | 707 | std::locale loc; |
| 550 | std::string cmd; | 708 | std::string cmd; |
| 709 | |||
| 710 | dbg_cmd_cxt_t cxt{ vm, r }; | ||
| 551 | for (;;) | 711 | for (;;) |
| 552 | { | 712 | { |
| 553 | std::cout | 713 | std::cout |
| 554 | << console_modifiers::green_font | 714 | << console_modifiers::green_font |
| 555 | << console_modifiers::bold | 715 | << console_modifiers::bold |
| 556 | << r.thread.get_thread_id() | 716 | << cxt.current_register->thread.get_thread_id() |
| 557 | << " >>> " | 717 | << " >>> " |
| 558 | << console_modifiers::reset_all; | 718 | << console_modifiers::reset_all; |
| 559 | 719 | ||
| @@ -566,16 +726,15 @@ namespace · 6 unmodified lines | |||
| 566 | continue; | 726 | continue; |
| 567 | 727 | ||
| 568 | auto cmd_exec = find_cmd(cmd_tokens[0]); | 728 | auto cmd_exec = find_cmd(cmd_tokens[0]); |
| 569 | if (cmd_exec != nullptr) | 729 | if (cmd_exec == nullptr) |
| 570 | { | ||
| 571 | if (!cmd_exec(cmd_tokens, vm, r)) | ||
| 572 | break; | ||
| 573 | } | ||
| 574 | else | ||
| 575 | { | 730 | { |
| 576 | debug_print_error("Unknown command"); | 731 | debug_print_error("Unknown command. '?' for help."); |
| 577 | print_help(); | 732 | continue; |
| 578 | } | 733 | } |
| 734 | |||
| 735 | cmd_exec(cmd_tokens, cxt); | ||
| 736 | if (cxt.exit_break) | ||
| 737 | break; | ||
| 579 | } | 738 | } |
| 580 | } | 739 | } |
| 581 | } | 740 | } |