_disasm.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. namespace eval disasm {
  2. # very common debug functions
  3. proc peek {addr {m memory}} {
  4. debug read $m $addr
  5. }
  6. proc peek8 {addr {m memory}} {
  7. peek $addr $m
  8. }
  9. proc peek_u8 {addr {m memory}} {
  10. peek $addr $m
  11. }
  12. proc peek_s8 {addr {m memory}} {
  13. set b [peek $addr $m]
  14. expr {($b < 128) ? $b : ($b - 256)}
  15. }
  16. proc peek16 {addr {m memory}} {
  17. expr {[peek $addr $m] + 256 * [peek [expr {$addr + 1}] $m]}
  18. }
  19. proc peek16_LE {addr {m memory}} {
  20. peek16 $addr $m
  21. }
  22. proc peek16_BE {addr {m memory}} {
  23. expr {256 * [peek $addr $m] + [peek [expr {$addr + 1}] $m]}
  24. }
  25. proc peek_u16 {addr {m memory}} {
  26. peek16 $addr $m
  27. }
  28. proc peek_u16LE {addr {m memory}} {
  29. peek16 $addr $m
  30. }
  31. proc peek_u16BE {addr {m memory}} {
  32. peek16_BE $addr $m
  33. }
  34. proc peek_s16 {addr {m memory}} {
  35. set w [peek16 $addr $m]
  36. expr {($w < 32768) ? $w : ($w - 65536)}
  37. }
  38. proc peek_s16LE {addr {m memory}} {
  39. peek_s16 $addr $m
  40. }
  41. proc peek_s16BE {addr {m memory}} {
  42. set w [peek16_BE $addr $m]
  43. expr {($w < 32768) ? $w : ($w - 65536)}
  44. }
  45. set help_text_peek \
  46. {Read a byte or word from the given memory location.
  47. Optionally allows to specify a different 'debuggable' (default is 'memory').
  48. usage:
  49. peek <addr> [<mem>] Read unsigned 8-bit value from address
  50. peek8 <addr> [<mem>] unsigned 8-bit
  51. peek_u8 <addr> [<mem>] unsigned 8-bit
  52. peek_s8 <addr> [<mem>] signed 8-bit
  53. peek16 <addr> [<mem>] unsigned 16-bit little endian
  54. peek16_LE <addr> [<mem>] unsigned 16-bit little endian
  55. peek16_BE <addr> [<mem>] unsigned 16-bit big endian
  56. peek_u16 <addr> [<mem>] unsigned 16-bit little endian
  57. peek_u16_LE <addr> [<mem>] unsigned 16-bit little endian
  58. peek_u16_BE <addr> [<mem>] unsigned 16-bit big endian
  59. peek_s16 <addr> [<mem>] signed 16-bit little endian
  60. peek_s16_LE <addr> [<mem>] signed 16-bit little endian
  61. peek_s16_BE <addr> [<mem>] signed 16-bit big endian
  62. }
  63. set_help_text peek $help_text_peek
  64. set_help_text peek8 $help_text_peek
  65. set_help_text peek_u8 $help_text_peek
  66. set_help_text peek_s8 $help_text_peek
  67. set_help_text peek16 $help_text_peek
  68. set_help_text peek16_LE $help_text_peek
  69. set_help_text peek16_BE $help_text_peek
  70. set_help_text peek_u16 $help_text_peek
  71. set_help_text peek_u16_LE $help_text_peek
  72. set_help_text peek_u16_BE $help_text_peek
  73. set_help_text peek_s16 $help_text_peek
  74. set_help_text peek_s16_LE $help_text_peek
  75. set_help_text peek_s16_BE $help_text_peek
  76. proc poke {addr val {m memory}} {
  77. debug write $m $addr $val
  78. }
  79. proc poke8 {addr val {m memory}} {
  80. poke $addr $val $m
  81. }
  82. proc poke16 {addr val {m memory}} {
  83. poke $addr [expr { $val & 255}] $m
  84. poke [expr {$addr + 1}] [expr {($val >> 8) & 255}] $m
  85. }
  86. proc poke16_LE {addr val {m memory}} {
  87. poke16 $addr $val $m
  88. }
  89. proc poke16_BE {addr val {m memory}} {
  90. poke $addr [expr {($val >> 8) & 255}] $m
  91. poke [expr {$addr + 1}] [expr { $val & 255}] $m
  92. }
  93. set help_text_poke \
  94. {Write a byte or word to the given memory location.
  95. Optionally allows to specify a different 'debuggable' (default is 'memory').
  96. usage:
  97. poke <addr> <val> [<mem>] Write 8-bit value
  98. poke8 <addr> <val> [<mem>] 8-bit
  99. poke16 <addr> <val> [<mem>] 16-bit little endian
  100. poke16_LE <addr> <val> [<mem>] 16-bit little endian
  101. poke16_BE <addr> <val> [<mem>] 16-bit big endian
  102. }
  103. set_help_text poke $help_text_poke
  104. set_help_text poke8 $help_text_poke
  105. set_help_text poke16 $help_text_poke
  106. set_help_text poke16_LE $help_text_poke
  107. set_help_text poke16_BE $help_text_poke
  108. # because of reverse we can now save replays to a file,
  109. # poke-ing adds an entry into the replay file and therefore
  110. # the file size can grow significantly. Therefor dpoke (poke
  111. # if different or diffpoke) is introduced.
  112. proc dpoke {addr val {m memory}} {
  113. if {[peek $addr $m] != $val} {poke $addr $val $m}
  114. }
  115. #
  116. # disasm
  117. #
  118. set_help_text disasm \
  119. {Disassemble z80 instructions
  120. Usage:
  121. disasm Disassemble 8 instr starting at the currect PC
  122. disasm <addr> Disassemble 8 instr starting at address <adr>
  123. disasm <addr> <num> Disassemble <num> instr starting at address <addr>
  124. }
  125. proc disasm {{address -1} {num 8}} {
  126. if {$address == -1} {set address [reg PC]}
  127. for {set i 0} {$i < int($num)} {incr i} {
  128. set l [debug disasm $address]
  129. append result [format "%04X %s\n" $address [join $l]]
  130. set address [expr {($address + [llength $l] - 1) & 0xFFFF}]
  131. }
  132. return $result
  133. }
  134. #
  135. # run_to
  136. #
  137. set_help_text run_to \
  138. {Run to the specified address, if a breakpoint is reached earlier we stop
  139. at that breakpoint.}
  140. proc run_to {address} {
  141. set bp [debug set_bp $address]
  142. after break "debug remove_bp $bp"
  143. debug cont
  144. }
  145. #
  146. # step_in
  147. #
  148. set_help_text step_in \
  149. {Step in. Execute the next instruction, also go into subroutines.}
  150. proc step_in {} {
  151. debug step
  152. }
  153. set_help_text step \
  154. {Same as step_in.}
  155. proc step {} {
  156. debug step
  157. }
  158. #
  159. # step_out
  160. #
  161. set_help_text step_out \
  162. {Step out of the current subroutine. In other words, execute till right after
  163. the next 'ret' instruction (more if there were also extra 'call' instructions).
  164. Note: simulation can be slow during execution of 'step_out', though for not
  165. extremely large subroutines this is not a problem.}
  166. variable step_out_bp1
  167. variable step_out_bp2
  168. proc step_out_is_ret {} {
  169. # ret 0xC9
  170. # ret <cc> 0xC0,0xC8,0xD0,..,0xF8
  171. # reti retn 0xED + 0x45,0x4D,0x55,..,0x7D
  172. set instr [peek16 [reg pc]]
  173. expr {(($instr & 0x00FF) == 0x00C9) ||
  174. (($instr & 0x00C7) == 0x00C0) ||
  175. (($instr & 0xC7FF) == 0x45ED)}
  176. }
  177. proc step_out_after_break {} {
  178. variable step_out_bp1
  179. variable step_out_bp2
  180. # also clean up when breaked, but not because of step_out
  181. catch {debug remove_condition $step_out_bp1}
  182. catch {debug remove_condition $step_out_bp2}
  183. }
  184. proc step_out_after_next {} {
  185. variable step_out_bp1
  186. variable step_out_bp2
  187. variable step_out_sp
  188. catch {debug remove_condition $step_out_bp2}
  189. if {[reg sp] > $step_out_sp} {
  190. catch {debug remove_condition $step_out_bp1}
  191. debug break
  192. }
  193. }
  194. proc step_out_after_ret {} {
  195. variable step_out_bp2
  196. catch {debug remove_condition $step_out_bp2}
  197. set step_out_bp2 [debug set_condition 1 [namespace code step_out_after_next]]
  198. }
  199. proc step_out {} {
  200. variable step_out_bp1
  201. variable step_out_bp2
  202. variable step_out_sp
  203. catch {debug remove_condition $step_out_bp1}
  204. catch {debug remove_condition $step_out_bp2}
  205. set step_out_sp [reg sp]
  206. set step_out_bp1 [debug set_condition {[disasm::step_out_is_ret]} [namespace code step_out_after_ret]]
  207. after break [namespace code step_out_after_break]
  208. debug cont
  209. }
  210. #
  211. # step_over
  212. #
  213. set_help_text step_over \
  214. {Step over. Execute the next instruction but don't step into subroutines.
  215. Only 'call' or 'rst' instructions are stepped over. Note: 'push xx / jp nn'
  216. sequences can in theory also be used as calls but these are not skipped
  217. by this command.}
  218. proc step_over {} {
  219. set address [reg PC]
  220. set l [debug disasm $address]
  221. set instr [lindex $l 0]
  222. if {[string match "call*" $instr] ||
  223. [string match "rst*" $instr] ||
  224. [string match "ldir*" $instr] ||
  225. [string match "cpir*" $instr] ||
  226. [string match "inir*" $instr] ||
  227. [string match "otir*" $instr] ||
  228. [string match "lddr*" $instr] ||
  229. [string match "cpdr*" $instr] ||
  230. [string match "indr*" $instr] ||
  231. [string match "otdr*" $instr] ||
  232. [string match "halt*" $instr]} {
  233. run_to [expr {$address + [llength $l] - 1}]
  234. } else {
  235. debug step
  236. }
  237. }
  238. #
  239. # step_back
  240. #
  241. set_help_text step_back \
  242. {Step back. Go back in time till right before the last instruction was
  243. executed. Note that this operation is relatively slow (compared to the other
  244. step functions). Also the reverse feature must be enabled for this to work
  245. (normally it's enabled by default).}
  246. proc step_back {} {
  247. # In the past this proc was implemented totally different. It's worth
  248. # mentioning this old algorithm and explain why it wasn't good enough.
  249. # The old algorithm went like this:
  250. # - take small steps back till we're not at the start instruction
  251. # anymore (this works because 'reverse goto' only stops after
  252. # emulating a full instruction)
  253. # The problem was that on R800 it could take _many_ (more than 80)
  254. # steps till the destination was reached.
  255. #
  256. # The current algorithm goes like this:
  257. # - take a large step back
  258. # - take small steps forward till we're back at the start
  259. # - we now know where the previous instruction started, so go there
  260. # (= take a small step back again)
  261. #
  262. # So the old algorithm takes (potentially) many backwards steps. While
  263. # the new algorithm takes exactly 2 backwards steps and (potentially)
  264. # many forward steps. In the current openMSX implementation, (small)
  265. # forward steps are orders of magnitude faster than backwards steps (an
  266. # optimization I added specifically for this use case). So the worst
  267. # execution time should now be much better.
  268. # 'z80' or 'r800'
  269. set cpu [get_active_cpu]
  270. # Get duration of one CPU cycle.
  271. set cycle_period [expr {1.0 / [machine_info ${cpu}_freq]}]
  272. # (Overestimation) for the maximum instruction length.
  273. # On Z80 the slowest instruction is probably 'EX (SP),IX' (25 cycles).
  274. # On R800 it's probably some I/O instruction to the VDP, followed by
  275. # a memory refresh (up to 87(!) cycles). I added some extra cycles as
  276. # a safety margin in case I forgot some extra penalty cycles (e.g.
  277. # access to a device that inserts extra wait cycles).
  278. set max_instr_len [expr {(($cpu eq "z80") ? 35 : 100) * $cycle_period}]
  279. # Get time of the start instruction.
  280. set start [dict get [reverse status] "current"]
  281. # Go back till a moment that's certainly before the start instruction.
  282. reverse goback -novideo $max_instr_len
  283. set curr [dict get [reverse status] "current"]
  284. if {$curr >= $start} {
  285. error "Internal error: initial step-back was not big enough"
  286. }
  287. # Take small steps (forward) till we again reach the start instruction.
  288. while {1} {
  289. # Note that 'reverse goto' for a small forward step is
  290. # orders of magnitudes faster than a backwards 'reverse goto'.
  291. # The '-novideo' flag is required to not (temporarily
  292. # internally) step back a few video frames (so that immediately
  293. # after 'reverse goto' we have the correct video output).
  294. # Also note that this may take a bigger step forward than
  295. # requested: it will only stop after a complete instruction is
  296. # emulated.
  297. reverse goto -novideo [expr {$curr + $cycle_period}]
  298. set next [dict get [reverse status] "current"]
  299. if {$next > $start} {
  300. error "Internal error: overshot destination"
  301. }
  302. if {$next == $start} break
  303. set curr $next
  304. }
  305. # The previous step was the correct one, so go back there.
  306. # Note that (only here) we don't pass the '-novideo' flag
  307. reverse goto $curr
  308. }
  309. #
  310. # skip one instruction
  311. #
  312. set_help_text skip_instruction \
  313. {Skip the current instruction. In other words increase the program counter with the length of the current instruction.}
  314. proc skip_instruction {} {
  315. set pc [reg pc]
  316. reg pc [expr {$pc + [llength [debug disasm $pc]] - 1}]
  317. }
  318. namespace export peek
  319. namespace export peek8
  320. namespace export peek_u8
  321. namespace export peek_s8
  322. namespace export peek16
  323. namespace export peek16_LE
  324. namespace export peek16_BE
  325. namespace export peek_u16
  326. namespace export peek_u16_LE
  327. namespace export peek_u16_BE
  328. namespace export peek_s16
  329. namespace export peek_s16_LE
  330. namespace export peek_s16_BE
  331. namespace export poke
  332. namespace export poke8
  333. namespace export poke16
  334. namespace export poke16_LE
  335. namespace export poke16_BE
  336. namespace export dpoke
  337. namespace export disasm
  338. namespace export run_to
  339. namespace export step_over
  340. namespace export step_back
  341. namespace export step_out
  342. namespace export step_in
  343. namespace export step
  344. namespace export skip_instruction
  345. } ;# namespace disasm
  346. namespace import disasm::*