debug.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. "use strict";
  2. CPU.prototype.debug_init = function()
  3. {
  4. var cpu = this;
  5. var debug = {};
  6. this.debug = debug;
  7. debug.init = function()
  8. {
  9. if(!DEBUG) return;
  10. if(cpu.io)
  11. {
  12. // write seabios debug output to console
  13. var seabios_debug = "";
  14. cpu.io.register_write(0x402, this, handle); // seabios
  15. cpu.io.register_write(0x500, this, handle); // vgabios
  16. }
  17. function handle(out_byte)
  18. {
  19. if(out_byte === 10)
  20. {
  21. dbg_log(seabios_debug, LOG_BIOS);
  22. seabios_debug = "";
  23. }
  24. else
  25. {
  26. seabios_debug += String.fromCharCode(out_byte);
  27. }
  28. }
  29. };
  30. debug.get_regs_short = get_regs_short;
  31. debug.dump_regs = dump_regs_short;
  32. debug.get_state = get_state;
  33. debug.dump_state = dump_state;
  34. debug.dump_stack = dump_stack;
  35. debug.dump_page_structures = dump_page_structures;
  36. debug.dump_gdt_ldt = dump_gdt_ldt;
  37. debug.dump_idt = dump_idt;
  38. debug.get_memory_dump = get_memory_dump;
  39. debug.memory_hex_dump = memory_hex_dump;
  40. debug.used_memory_dump = used_memory_dump;
  41. function dump_stack(start, end)
  42. {
  43. if(!DEBUG) return;
  44. var esp = cpu.reg32[REG_ESP];
  45. dbg_log("========= STACK ==========");
  46. if(end >= start || end === undefined)
  47. {
  48. start = 5;
  49. end = -5;
  50. }
  51. for(var i = start; i > end; i--)
  52. {
  53. var line = " ";
  54. if(!i) line = "=> ";
  55. line += h(i, 2) + " | ";
  56. dbg_log(line + h(esp + 4 * i, 8) + " | " + h(cpu.read32s(esp + 4 * i) >>> 0));
  57. }
  58. }
  59. function get_state(where)
  60. {
  61. if(!DEBUG) return;
  62. var mode = cpu.protected_mode[0] ? "prot" : "real";
  63. var vm = (cpu.flags[0] & FLAG_VM) ? 1 : 0;
  64. var flags = cpu.get_eflags();
  65. var iopl = cpu.getiopl();
  66. var cpl = cpu.cpl[0];
  67. var cs_eip = h(cpu.sreg[REG_CS], 4) + ":" + h(cpu.get_real_eip() >>> 0, 8);
  68. var ss_esp = h(cpu.sreg[REG_SS], 4) + ":" + h(cpu.reg32[REG_ES] >>> 0, 8);
  69. var op_size = cpu.is_32[0] ? "32" : "16";
  70. var if_ = (cpu.flags[0] & FLAG_INTERRUPT) ? 1 : 0;
  71. var flag_names = {
  72. [FLAG_CARRY]: "c",
  73. [FLAG_PARITY]: "p",
  74. [FLAG_ADJUST]: "a",
  75. [FLAG_ZERO]: "z",
  76. [FLAG_SIGN]: "s",
  77. [FLAG_TRAP]: "t",
  78. [FLAG_INTERRUPT]: "i",
  79. [FLAG_DIRECTION]: "d",
  80. [FLAG_OVERFLOW]: "o",
  81. };
  82. var flag_string = "";
  83. for(var i = 0; i < 16; i++)
  84. {
  85. if(flag_names[1 << i])
  86. {
  87. if(flags & 1 << i)
  88. {
  89. flag_string += flag_names[1 << i];
  90. }
  91. else
  92. {
  93. flag_string += " ";
  94. }
  95. }
  96. }
  97. return ("mode=" + mode + "/" + op_size + " paging=" + (+((cpu.cr[0] & CR0_PG) !== 0)) +
  98. " pae=" + (+((cpu.cr[4] & CR4_PAE) !== 0)) +
  99. " iopl=" + iopl + " cpl=" + cpl + " if=" + if_ + " cs:eip=" + cs_eip +
  100. " cs_off=" + h(cpu.get_seg_cs() >>> 0, 8) +
  101. " flgs=" + h(cpu.get_eflags() >>> 0, 6) + " (" + flag_string + ")" +
  102. " ss:esp=" + ss_esp +
  103. " ssize=" + (+cpu.stack_size_32[0]) +
  104. (where ? " in " + where : ""));
  105. }
  106. function dump_state(where)
  107. {
  108. if(!DEBUG) return;
  109. dbg_log(get_state(where), LOG_CPU);
  110. }
  111. function get_regs_short()
  112. {
  113. if(!DEBUG) return;
  114. var
  115. r32 = { "eax": REG_EAX, "ecx": REG_ECX, "edx": REG_EDX, "ebx": REG_EBX,
  116. "esp": REG_ESP, "ebp": REG_EBP, "esi": REG_ESI, "edi": REG_EDI },
  117. r32_names = ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"],
  118. s = { "cs": REG_CS, "ds": REG_DS, "es": REG_ES, "fs": REG_FS, "gs": REG_GS, "ss": REG_SS },
  119. line1 = "",
  120. line2 = "";
  121. for(var i = 0; i < 4; i++)
  122. {
  123. line1 += r32_names[i] + "=" + h(cpu.reg32[r32[r32_names[i]]] >>> 0, 8) + " ";
  124. line2 += r32_names[i+4] + "=" + h(cpu.reg32[r32[r32_names[i+4]]] >>> 0, 8) + " ";
  125. }
  126. //line1 += " eip=" + h(cpu.get_real_eip() >>> 0, 8);
  127. //line2 += " flg=" + h(cpu.get_eflags(), 8);
  128. line1 += " ds=" + h(cpu.sreg[REG_DS], 4) + " es=" + h(cpu.sreg[REG_ES], 4) + " fs=" + h(cpu.sreg[REG_FS], 4);
  129. line2 += " gs=" + h(cpu.sreg[REG_GS], 4) + " cs=" + h(cpu.sreg[REG_CS], 4) + " ss=" + h(cpu.sreg[REG_SS], 4);
  130. return [line1, line2];
  131. }
  132. function dump_regs_short()
  133. {
  134. if(!DEBUG) return;
  135. var lines = get_regs_short();
  136. dbg_log(lines[0], LOG_CPU);
  137. dbg_log(lines[1], LOG_CPU);
  138. }
  139. function dump_gdt_ldt()
  140. {
  141. if(!DEBUG) return;
  142. dbg_log("gdt: (len = " + h(cpu.gdtr_size[0]) + ")");
  143. dump_table(cpu.translate_address_system_read(cpu.gdtr_offset[0]), cpu.gdtr_size[0]);
  144. dbg_log("\nldt: (len = " + h(cpu.segment_limits[REG_LDTR]) + ")");
  145. dump_table(cpu.translate_address_system_read(cpu.segment_offsets[REG_LDTR]), cpu.segment_limits[REG_LDTR]);
  146. function dump_table(addr, size)
  147. {
  148. for(var i = 0; i < size; i += 8, addr += 8)
  149. {
  150. var base = cpu.read16(addr + 2) |
  151. cpu.read8(addr + 4) << 16 |
  152. cpu.read8(addr + 7) << 24,
  153. limit = cpu.read16(addr) | (cpu.read8(addr + 6) & 0xF) << 16,
  154. access = cpu.read8(addr + 5),
  155. flags = cpu.read8(addr + 6) >> 4,
  156. flags_str = "",
  157. dpl = access >> 5 & 3;
  158. if(!(access & 128))
  159. {
  160. // present bit not set
  161. //continue;
  162. flags_str += "NP ";
  163. }
  164. else
  165. {
  166. flags_str += " P ";
  167. }
  168. if(access & 16)
  169. {
  170. if(flags & 4)
  171. {
  172. flags_str += "32b ";
  173. }
  174. else
  175. {
  176. flags_str += "16b ";
  177. }
  178. if(access & 8)
  179. {
  180. // executable
  181. flags_str += "X ";
  182. if(access & 4)
  183. {
  184. flags_str += "C ";
  185. }
  186. }
  187. else
  188. {
  189. // data
  190. flags_str += "R ";
  191. }
  192. flags_str += "RW ";
  193. }
  194. else
  195. {
  196. // system
  197. flags_str += "sys: " + h(access & 15);
  198. }
  199. if(flags & 8)
  200. {
  201. limit = limit << 12 | 0xFFF;
  202. }
  203. dbg_log(h(i & ~7, 4) + " " + h(base >>> 0, 8) + " (" + h(limit >>> 0, 8) + " bytes) " +
  204. flags_str + "; dpl = " + dpl + ", a = " + access.toString(2) +
  205. ", f = " + flags.toString(2));
  206. }
  207. }
  208. }
  209. function dump_idt()
  210. {
  211. if(!DEBUG) return;
  212. for(var i = 0; i < cpu.idtr_size[0]; i += 8)
  213. {
  214. var addr = cpu.translate_address_system_read(cpu.idtr_offset[0] + i),
  215. base = cpu.read16(addr) | cpu.read16(addr + 6) << 16,
  216. selector = cpu.read16(addr + 2),
  217. type = cpu.read8(addr + 5),
  218. line,
  219. dpl = type >> 5 & 3;
  220. if((type & 31) === 5)
  221. {
  222. line = "task gate ";
  223. }
  224. else if((type & 31) === 14)
  225. {
  226. line = "intr gate ";
  227. }
  228. else if((type & 31) === 15)
  229. {
  230. line = "trap gate ";
  231. }
  232. else
  233. {
  234. line = "invalid ";
  235. }
  236. if(type & 128)
  237. {
  238. line += " P";
  239. }
  240. else
  241. {
  242. // present bit not set
  243. //continue;
  244. line += "NP";
  245. }
  246. dbg_log(h(i >> 3, 4) + " " + h(base >>> 0, 8) + ", " +
  247. h(selector, 4) + "; " + line + "; dpl = " + dpl + ", t = " + type.toString(2));
  248. }
  249. }
  250. function load_page_entry(dword_entry, pae, is_directory)
  251. {
  252. if(!DEBUG) return;
  253. if(!(dword_entry & 1))
  254. {
  255. // present bit not set
  256. return false;
  257. }
  258. var size = (dword_entry & 128) === 128,
  259. address;
  260. if(size && !is_directory)
  261. {
  262. address = dword_entry & (pae ? 0xFFE00000 : 0xFFC00000);
  263. }
  264. else
  265. {
  266. address = dword_entry & 0xFFFFF000;
  267. }
  268. return {
  269. size: size,
  270. global: (dword_entry & 256) === 256,
  271. accessed: (dword_entry & 0x20) === 0x20,
  272. dirty: (dword_entry & 0x40) === 0x40,
  273. cache_disable : (dword_entry & 16) === 16,
  274. user : (dword_entry & 4) === 4,
  275. read_write : (dword_entry & 2) === 2,
  276. address : address >>> 0
  277. };
  278. }
  279. function dump_page_structures() {
  280. var pae = !!(cpu.cr[4] & CR4_PAE);
  281. if (pae)
  282. {
  283. dbg_log("PAE enabled");
  284. for (var i = 0; i < 4; i++) {
  285. var addr = cpu.cr[3] + 8 * i;
  286. var dword = cpu.read32s(addr);
  287. if (dword & 1)
  288. {
  289. dump_page_directory(dword & 0xFFFFF000, true, i << 30);
  290. }
  291. }
  292. }
  293. else
  294. {
  295. dbg_log("PAE disabled");
  296. dump_page_directory(cpu.cr[3], false, 0);
  297. }
  298. }
  299. /* NOTE: PAE entries are 64-bits, we ignore the high half here. */
  300. function dump_page_directory(pd_addr, pae, start)
  301. {
  302. if(!DEBUG) return;
  303. var n = pae ? 512 : 1024;
  304. var entry_size = pae ? 8 : 4;
  305. var pd_shift = pae ? 21 : 22;
  306. for(var i = 0; i < n; i++)
  307. {
  308. var addr = pd_addr + i * entry_size,
  309. dword = cpu.read32s(addr),
  310. entry = load_page_entry(dword, pae, true);
  311. if(!entry)
  312. {
  313. continue;
  314. }
  315. var flags = "";
  316. flags += entry.size ? "S " : " ";
  317. flags += entry.accessed ? "A " : " ";
  318. flags += entry.cache_disable ? "Cd " : " ";
  319. flags += entry.user ? "U " : " ";
  320. flags += entry.read_write ? "Rw " : " ";
  321. if(entry.size)
  322. {
  323. dbg_log("=== " + h(start + (i << pd_shift) >>> 0, 8) + " -> " +
  324. h(entry.address >>> 0, 8) + " | " + flags);
  325. continue;
  326. }
  327. else
  328. {
  329. dbg_log("=== " + h(start + (i << pd_shift) >>> 0, 8) + " | " + flags);
  330. }
  331. for(var j = 0; j < n; j++)
  332. {
  333. var sub_addr = entry.address + j * entry_size;
  334. dword = cpu.read32s(sub_addr);
  335. var subentry = load_page_entry(dword, pae, false);
  336. if(subentry)
  337. {
  338. flags = "";
  339. flags += subentry.cache_disable ? "Cd " : " ";
  340. flags += subentry.user ? "U " : " ";
  341. flags += subentry.read_write ? "Rw " : " ";
  342. flags += subentry.global ? "G " : " ";
  343. flags += subentry.accessed ? "A " : " ";
  344. flags += subentry.dirty ? "Di " : " ";
  345. dbg_log("# " + h(start + (i << pd_shift | j << 12) >>> 0, 8) + " -> " +
  346. h(subentry.address, 8) + " | " + flags + " (at " + h(sub_addr, 8) + ")");
  347. }
  348. }
  349. }
  350. }
  351. function get_memory_dump(start, count)
  352. {
  353. if(!DEBUG) return;
  354. if(start === undefined)
  355. {
  356. start = 0;
  357. count = cpu.memory_size[0];
  358. }
  359. else if(count === undefined)
  360. {
  361. count = start;
  362. start = 0;
  363. }
  364. return cpu.mem8.slice(start, start + count).buffer;
  365. }
  366. function memory_hex_dump(addr, length)
  367. {
  368. if(!DEBUG) return;
  369. length = length || 4 * 0x10;
  370. var line, byt;
  371. for(var i = 0; i < length >> 4; i++)
  372. {
  373. line = h(addr + (i << 4), 5) + " ";
  374. for(var j = 0; j < 0x10; j++)
  375. {
  376. byt = cpu.read8(addr + (i << 4) + j);
  377. line += h(byt, 2) + " ";
  378. }
  379. line += " ";
  380. for(j = 0; j < 0x10; j++)
  381. {
  382. byt = cpu.read8(addr + (i << 4) + j);
  383. line += (byt < 33 || byt > 126) ? "." : String.fromCharCode(byt);
  384. }
  385. dbg_log(line);
  386. }
  387. }
  388. function used_memory_dump()
  389. {
  390. if(!DEBUG) return;
  391. var width = 0x80,
  392. height = 0x10,
  393. block_size = cpu.memory_size[0] / width / height | 0,
  394. row;
  395. for(var i = 0; i < height; i++)
  396. {
  397. row = h(i * width * block_size, 8) + " | ";
  398. for(var j = 0; j < width; j++)
  399. {
  400. var used = cpu.mem32s[(i * width + j) * block_size] > 0;
  401. row += used ? "X" : " ";
  402. }
  403. dbg_log(row);
  404. }
  405. }
  406. debug.debug_interrupt = function(interrupt_nr)
  407. {
  408. //if(interrupt_nr === 0x20)
  409. //{
  410. // //var vxd_device = cpu.safe_read16(cpu.instruction_pointer + 2);
  411. // //var vxd_sub = cpu.safe_read16(cpu.instruction_pointer + 0);
  412. // //var service = "";
  413. // //if(vxd_device === 1)
  414. // //{
  415. // // service = vxd_table1[vxd_sub];
  416. // //}
  417. // //dbg_log("vxd: " + h(vxd_device, 4) + " " + h(vxd_sub, 4) + " " + service);
  418. //}
  419. //if(interrupt_nr >= 0x21 && interrupt_nr < 0x30)
  420. //{
  421. // dbg_log("dos: " + h(interrupt_nr, 2) + " ah=" + h(this.reg8[reg_ah], 2) + " ax=" + h(this.reg16[reg_ax], 4));
  422. //}
  423. //if(interrupt_nr === 0x13 && (this.reg8[reg_ah] | 1) === 0x43)
  424. //{
  425. // this.debug.memory_hex_dump(this.get_seg(reg_ds) + this.reg16[reg_si], 0x18);
  426. //}
  427. //if(interrupt_nr == 0x10)
  428. //{
  429. // dbg_log("int10 ax=" + h(this.reg16[reg_ax], 4) + " '" + String.fromCharCode(this.reg8[reg_al]) + "'");
  430. // this.debug.dump_regs_short();
  431. // if(this.reg8[reg_ah] == 0xe) vga.tt_write(this.reg8[reg_al]);
  432. //}
  433. //if(interrupt_nr === 0x13)
  434. //{
  435. // this.debug.dump_regs_short();
  436. //}
  437. //if(interrupt_nr === 6)
  438. //{
  439. // this.instruction_pointer += 2;
  440. // dbg_log("BUG()", LOG_CPU);
  441. // dbg_log("line=" + this.read_imm16() + " " +
  442. // "file=" + this.read_string(this.translate_address_read(this.read_imm32s())), LOG_CPU);
  443. // this.instruction_pointer -= 8;
  444. // this.debug.dump_regs_short();
  445. //}
  446. //if(interrupt_nr === 0x80)
  447. //{
  448. // dbg_log("linux syscall");
  449. // this.debug.dump_regs_short();
  450. //}
  451. //if(interrupt_nr === 0x40)
  452. //{
  453. // dbg_log("kolibri syscall");
  454. // this.debug.dump_regs_short();
  455. //}
  456. };
  457. let cs;
  458. let capstone_decoder;
  459. debug.dump_code = function(is_32, buffer, start)
  460. {
  461. if(!capstone_decoder)
  462. {
  463. if(cs === undefined)
  464. {
  465. if(typeof require === "function")
  466. {
  467. cs = require("./capstone-x86.min.js");
  468. }
  469. else
  470. {
  471. cs = window.cs;
  472. }
  473. if(cs === undefined)
  474. {
  475. dbg_log("Warning: Missing capstone library, disassembly not available");
  476. return;
  477. }
  478. }
  479. capstone_decoder = [
  480. new cs.Capstone(cs.ARCH_X86, cs.MODE_16),
  481. new cs.Capstone(cs.ARCH_X86, cs.MODE_32),
  482. ];
  483. }
  484. try
  485. {
  486. const instructions = capstone_decoder[is_32].disasm(buffer, start);
  487. instructions.forEach(function (instr) {
  488. dbg_log(h(instr.address >>> 0) + ": " +
  489. v86util.pads(instr.bytes.map(x => h(x, 2).slice(-2)).join(" "), 20) + " " +
  490. instr.mnemonic + " " + instr.op_str);
  491. });
  492. dbg_log("");
  493. }
  494. catch(e)
  495. {
  496. dbg_log("Could not disassemble: " + Array.from(buffer).map(x => h(x, 2)).join(" "));
  497. }
  498. };
  499. function dump_file(ab, name)
  500. {
  501. var blob = new Blob([ab]);
  502. var a = document.createElement("a");
  503. a["download"] = name;
  504. a.href = window.URL.createObjectURL(blob);
  505. a.dataset["downloadurl"] = ["application/octet-stream", a["download"], a.href].join(":");
  506. a.click();
  507. window.URL.revokeObjectURL(a.src);
  508. }
  509. let wabt;
  510. debug.dump_wasm = function(buffer)
  511. {
  512. if(wabt === undefined)
  513. {
  514. if(typeof require === "function")
  515. {
  516. wabt = require("./libwabt.js");
  517. }
  518. else
  519. {
  520. wabt = new window.WabtModule;
  521. }
  522. if(wabt === undefined)
  523. {
  524. dbg_log("Warning: Missing libwabt, wasm dump not available");
  525. return;
  526. }
  527. }
  528. // Need to make a small copy otherwise libwabt goes nuts trying to copy
  529. // the whole underlying buffer
  530. buffer = buffer.slice();
  531. try
  532. {
  533. var module = wabt.readWasm(buffer, { readDebugNames: false });
  534. module.generateNames();
  535. module.applyNames();
  536. const result = module.toText({ foldExprs: true, inlineExport: true });
  537. dbg_log(result);
  538. }
  539. catch(e)
  540. {
  541. dump_file(buffer, "failed.wasm");
  542. console.log(e.toString());
  543. }
  544. finally
  545. {
  546. if(module)
  547. {
  548. module.destroy();
  549. }
  550. }
  551. };
  552. };