_example_tools.tcl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. # Return the content of the MSX screen as a string (text-modes only)
  2. set_help_text get_screen \
  3. {Returns the content of the MSX screen as a string (only works for text-modes).
  4. }
  5. proc get_screen {} {
  6. set mode [get_screen_mode_number]
  7. switch -- $mode {
  8. 0 {
  9. set addr 0
  10. set width [expr {([debug read "VDP regs" 0] & 0x04) ? 80 : 40}]
  11. }
  12. 1 {
  13. set addr 6144
  14. set width 32
  15. }
  16. default {
  17. error "Screen mode $mode not supported"
  18. }
  19. }
  20. # scrape screen and build string
  21. set screen ""
  22. for {set y 0} {$y < 24} {incr y} {
  23. set line ""
  24. for {set x 0} {$x < $width} {incr x} {
  25. set char [vpeek $addr]
  26. if {$char == 255 && [machine_info type] eq "MSX"} {
  27. set char [peek 0xFBCC]
  28. } elseif {$char == 191 && [machine_info type] eq "SVI"} {
  29. set char [peek 0xFD67]
  30. }
  31. append line [format %c $char]
  32. incr addr
  33. }
  34. append screen [string trim $line] "\n"
  35. }
  36. return [string trim $screen]
  37. }
  38. #***********************************************
  39. #* Basic Reader
  40. #*
  41. #* A special thanks to Vincent van Dam for
  42. #* giving me permission to translate his
  43. #* script into Tcl
  44. #***********************************************
  45. set_help_text listing \
  46. {Interpret the content of the memory as a BASIC program and return the
  47. equivalent output of the BASIC LIST command. (May not be terribly useful, but
  48. it does show the power of openMSX scripts ;-) You can add an optional start
  49. address for the decoding; by default it's what the system variable TXTTAB says.
  50. }
  51. proc listing {{startaddr "default"}} {
  52. # TODO: reduce duplication, difference seems only in last couple of
  53. # lines
  54. set tokens1MSX [list \
  55. "" "END" "FOR" "NEXT" "DATA" "INPUT" "DIM" "READ" "LET" \
  56. "GOTO" "RUN" "IF" "RESTORE" "GOSUB" "RETURN" "REM" "STOP" \
  57. "PRINT" "CLEAR" "LIST" "NEW" "ON" "WAIT" "DEF" "POKE" "CONT" \
  58. "CSAVE" "CLOAD" "OUT" "LPRINT" "LLIST" "CLS" "WIDTH" "ELSE" \
  59. "TRON" "TROFF" "SWAP" "ERASE" "ERROR" "RESUME" "DELETE" \
  60. "AUTO" "RENUM" "DEFSTR" "DEFINT" "DEFSNG" "DEFDBL" "LINE" \
  61. "OPEN" "FIELD" "GET" "PUT" "CLOSE" "LOAD" "MERGE" "FILES" \
  62. "LSET" "RSET" "SAVE" "LFILES" "CIRCLE" "COLOR" "DRAW" "PAINT" \
  63. "BEEP" "PLAY" "PSET" "PRESET" "SOUND" "SCREEN" "VPOKE" \
  64. "SPRITE" "VDP" "BASE" "CALL" "TIME" "KEY" "MAX" "MOTOR" \
  65. "BLOAD" "BSAVE" "DSKO$" "SET" "NAME" "KILL" "IPL" "COPY" "CMD" \
  66. "LOCATE" "TO" "THEN" "TAB(" "STEP" "USR" "FN" "SPC(" "NOT" \
  67. "ERL" "ERR" "STRING$" "USING" "INSTR" "'" "VARPTR" "CSRLIN" \
  68. "ATTR$" "DSKI$" "OFF" "INKEY$" "POINT" ">" "=" "<" "+" "-" "*" \
  69. "/" "^" "AND" "OR" "XOR" "EQV" "IMP" "MOD" "\\" "\n" "" \
  70. "\0x127"]
  71. set tokens1SVI [list \
  72. "" "END" "FOR" "NEXT" "DATA" "INPUT" "DIM" "READ" "LET" \
  73. "GOTO" "RUN" "IF" "RESTORE" "GOSUB" "RETURN" "REM" "STOP" \
  74. "PRINT" "CLEAR" "LIST" "NEW" "ON" "WAIT" "DEF" "POKE" "CONT" \
  75. "CSAVE" "CLOAD" "OUT" "LPRINT" "LLIST" "CLS" "WIDTH" "ELSE" \
  76. "TRON" "TROFF" "SWAP" "ERASE" "ERROR" "RESUME" "DELETE" \
  77. "AUTO" "RENUM" "DEFSTR" "DEFINT" "DEFSNG" "DEFDBL" "LINE" \
  78. "OPEN" "FIELD" "GET" "PUT" "CLOSE" "LOAD" "MERGE" "FILES" \
  79. "LSET" "RSET" "SAVE" "LFILES" "CIRCLE" "COLOR" "DRAW" "PAINT" \
  80. "BEEP" "PLAY" "PSET" "PRESET" "SOUND" "SCREEN" "VPOKE" \
  81. "KEY" "CLICK" "SWITCH" "MAX" "MON" "MOTOR" "BLOAD" "BSAVE" \
  82. "MDM" "DIAL" "DSKO$" "SET" "NAME" "KILL" "IPL" "COPY" "CMD" \
  83. "LOCATE" "TO" "THEN" "TAB(" "STEP" "USR" "FN" "SPC(" "NOT" \
  84. "ERL" "ERR" "STRING$" "USING" "INSTR" "'" "VARPTR" "CSRLIN" \
  85. "ATTR$" "DSKI$" "OFF" "INKEY$" "POINT" "SPRITE" "TIME" ">" \
  86. "=" "<" "+" "-" "*" "/" "^" "AND" "OR" "XOR" "EQV" "IMP" \
  87. "MOD" "\\" "\0x127"]
  88. set tokens2 [list \
  89. "" "LEFT$" "RIGHT$" "MID$" "SGN" "INT" "ABS" "SQR" "RND" "SIN" \
  90. "LOG" "EXP" "COS" "TAN" "ATN" "FRE" "INP" "POS" "LEN" "STR$" \
  91. "VAL" "ASC" "CHR$" "PEEK" "VPEEK" "SPACE$" "OCT$" "HEX$" \
  92. "LPOS" "BIN$" "CINT" "CSNG" "CDBL" "FIX" "STICK" "STRIG" "PDL" \
  93. "PAD" "DSKF" "FPOS" "CVI" "CVS" "CVD" "EOF" "LOC" "LOF" "MKI$" \
  94. "MKS$" "MKD$" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
  95. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
  96. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
  97. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""]
  98. if {[machine_info type] eq "SVI"} {
  99. set TXTTAB 0xF54A
  100. set tokens1 $tokens1SVI
  101. } else {
  102. set TXTTAB 0xF676
  103. set tokens1 $tokens1MSX
  104. }
  105. if {$startaddr eq "default"} {
  106. set startaddr [peek16 $TXTTAB]
  107. }
  108. # Loop over all lines
  109. set listing ""
  110. for {set addr $startaddr} {[peek16 $addr] != 0} {} {
  111. append listing [format "0x%x > " $addr]
  112. incr addr 2
  113. append listing "[peek16 $addr] "
  114. incr addr 2
  115. set inquotes false
  116. set inrem false
  117. # Loop over tokens in one line
  118. while {true} {
  119. set token [peek $addr]; incr addr
  120. set t [format "%c" $token]
  121. if {$token == 0x0} {
  122. set t ""
  123. break
  124. } elseif {$token == 0x22} {
  125. set inquotes [expr !$inquotes]
  126. }
  127. if {!$inquotes && !$inrem} {
  128. if {0x80 < $token && $token < 0xFF} {
  129. if {$token == 0x8F} {
  130. if {[peek $addr] == 0xE6} {
  131. set t "'"
  132. incr addr
  133. } else {
  134. set t "REM"
  135. }
  136. set inrem true
  137. } else {
  138. set t [lindex $tokens1 [expr {$token - 0x80}]]
  139. }
  140. } elseif {$token == 0xFF} {
  141. set t [lindex $tokens2 [expr {[peek $addr] - 0x80}]]
  142. incr addr
  143. } elseif {$token == 0x3a} {
  144. if {[peek $addr] == 0xA1 || ([peek $addr] == 0x8F && [peek [expr {$addr + 1}]] == 0xE6)} {
  145. set t ""
  146. }
  147. } elseif {$token == 0x0B} {
  148. set t [format "&O%o" [peek16 $addr]]
  149. incr addr 2
  150. } elseif {$token == 0x0C} {
  151. set t [format "&H%X" [peek16 $addr]]
  152. incr addr 2
  153. } elseif {$token == 0x0D} {
  154. # line number (stored as address)
  155. set t [format "%d" [peek16 [expr {[peek16 $addr] + 3}]]]
  156. incr addr 2
  157. } elseif {$token == 0x0E} {
  158. set t [format "%d" [peek16 $addr]]
  159. incr addr 2
  160. } elseif {$token == 0x0F} {
  161. set t [format "%d" [peek $addr]]
  162. incr addr
  163. } elseif {$token == 0x1C} {
  164. set t [format "%d" [peek16 $addr]]
  165. incr addr 2
  166. } elseif {$token == 0x1D} {
  167. set exp [peek $addr]
  168. incr addr
  169. set t [format "%02x" [peek $addr]]
  170. incr addr
  171. append t [format "%02x" [peek $addr]]
  172. incr addr
  173. append t [format "%02x" [peek $addr]]
  174. incr addr
  175. set exp "E[expr {($exp & 0x7F) - 70}]"
  176. set t [make_msx_single $t$exp]
  177. } elseif {$token == 0x1F} {
  178. set exp [peek $addr]
  179. incr addr
  180. set t [format "%02x" [peek $addr]]
  181. incr addr
  182. append t [format "%02x" [peek $addr]]
  183. incr addr
  184. append t [format "%02x" [peek $addr]]
  185. incr addr
  186. append t [format "%02x" [peek $addr]]
  187. incr addr
  188. append t [format "%02x" [peek $addr]]
  189. incr addr
  190. append t [format "%02x" [peek $addr]]
  191. incr addr
  192. append t [format "%02x" [peek $addr]]
  193. incr addr
  194. set exp "E[expr {($exp & 0x7F) - 78}]"
  195. set t [make_msx_double $t$exp]
  196. } elseif {0x11 <= $token && $token <= 0x1A} {
  197. set t [expr {$token - 0x11}]
  198. }
  199. }
  200. append listing $t
  201. }
  202. append listing "\n"
  203. }
  204. return $listing
  205. }
  206. proc make_msx_double {num} {
  207. if {[string toupper $num] ne $num} {
  208. error "Invalid BASIC program"
  209. }
  210. set n [format "% .14E" $num]
  211. set ee [string range $n 18 20]
  212. set e [scan $ee %d]
  213. if {$e <-2 && $e >-5} {
  214. set n [format "%.14G" [string range $n 0 16]]E$ee
  215. } else {
  216. set n [format "%.14G" $num]
  217. }
  218. if {[string range $n 0 1] eq "0."} {\
  219. set n [string range $n 1 100]
  220. }
  221. if {![string match *E* $n]} {
  222. append n "#"
  223. }
  224. return $n
  225. }
  226. proc make_msx_single {num} {
  227. if {[string toupper $num] ne $num} {
  228. error "Invalid BASIC program"
  229. }
  230. set n [format "% .6E" $num]
  231. set ee [string range $n 10 12]
  232. set e [scan $ee %d ]
  233. if {$e <-2 && $e >-5} {
  234. set n [format "%.6G" [string range $n 0 8]]E$ee
  235. } else {
  236. set n [format "%.6G" $num]
  237. }
  238. if {[string range $n 0 1] eq "0."} {
  239. set n [string range $n 1 100]
  240. }
  241. if {$n >32767 && ![string match *.* $n]} {
  242. append n "!"
  243. }
  244. return $n
  245. }
  246. set_help_text get_color_count \
  247. "Gives some statistics on the used colors of the currently visible screen. Does not take into account sprites, screen splits and other trickery.
  248. Options:
  249. -sort <field>, where <field> is either 'color' (default) or 'amount'
  250. -reverse, to reverse the sorting order
  251. -all, to also include colors that have a count of zero
  252. "
  253. proc get_color_count {args} {
  254. set result ""
  255. set sortindex 0
  256. set sortorder "-increasing"
  257. set showall false
  258. # parse options
  259. while {1} {
  260. switch -- [lindex $args 0] {
  261. "" break
  262. "-sort" {
  263. set sortfield [lindex $args 1]
  264. if {$sortfield eq "color"} {
  265. set sortindex 0
  266. } elseif {$sortfield eq "amount"} {
  267. set sortindex 1
  268. } else {
  269. error "Unsupported sort field: $sortfield"
  270. }
  271. set args [lrange $args 2 end]
  272. }
  273. "-reverse" {
  274. set sortorder "-decreasing"
  275. set args [lrange $args 1 end]
  276. }
  277. "-all" {
  278. set showall true
  279. set args [lrange $args 1 end]
  280. }
  281. "default" {
  282. error "Invalid option: [lindex $args 0]"
  283. }
  284. }
  285. }
  286. set mode [get_screen_mode_number]
  287. switch -- $mode {
  288. 5 {
  289. set nofbytes_per_line 128
  290. set nofpixels_per_byte 2
  291. set page_size [expr {32 * 1024}]
  292. }
  293. 6 {
  294. set nofbytes_per_line 128
  295. set nofpixels_per_byte 4
  296. set page_size [expr {32 * 1024}]
  297. }
  298. 7 {
  299. set nofbytes_per_line 256
  300. set nofpixels_per_byte 2
  301. set page_size [expr {64 * 1024}]
  302. }
  303. 8 {
  304. set nofbytes_per_line 256
  305. set nofpixels_per_byte 1
  306. set page_size [expr {64 * 1024}]
  307. }
  308. default {
  309. error "Screen mode $mode not supported (yet)"
  310. }
  311. }
  312. set page [expr {([debug read "VDP regs" 2] & 96) >> 5}]
  313. set noflines [expr {([debug read "VDP regs" 9] & 128) ? 212 : 192}]
  314. set bpp [expr {8 / $nofpixels_per_byte}]
  315. set nrcolors [expr {1 << $bpp}]
  316. append result "Counting pixels of screen $mode, page $page with $noflines lines...\n"
  317. # get bytes into large list
  318. set offset [expr {$page_size * $page}]
  319. set nrbytes [expr {$noflines * $nofbytes_per_line}]
  320. binary scan [debug read_block VRAM $offset $nrbytes] c* myvram
  321. # analyze pixels
  322. set pixelstats [dict create]
  323. for {set p 0} {$p < $nrcolors} {incr p} {
  324. dict set pixelstats $p 0
  325. }
  326. set mask [expr {$nrcolors - 1}]
  327. foreach byte $myvram {
  328. for {set pixel 0} {$pixel < $nofpixels_per_byte} {incr pixel} {
  329. set color [expr {($byte >> ($pixel * $bpp)) & $mask}]
  330. dict incr pixelstats $color
  331. }
  332. }
  333. # convert to list
  334. set pixelstatlist [list]
  335. dict for {key val} $pixelstats {
  336. if {$showall || ($val != 0)} {
  337. lappend pixelstatlist [list $key $val]
  338. }
  339. }
  340. set pixelstatlistsorted [lsort -integer $sortorder -index $sortindex $pixelstatlist]
  341. # print results
  342. set number 0
  343. set colorwidth [expr {($mode == 8) ? 3 : 2}]
  344. set palette ""
  345. foreach pixelinfo $pixelstatlistsorted {
  346. incr number
  347. lassign $pixelinfo color amount
  348. if {$mode != 8} {
  349. set palette " ([getcolor $color])"
  350. }
  351. append result [format "%${colorwidth}d: color %${colorwidth}d$palette: amount: %6d\n" $number $color $amount]
  352. }
  353. return $result
  354. }
  355. variable tron_bp
  356. proc toggle_tron {} {
  357. variable tron_bp
  358. if {[osd exists tron]} {
  359. osd destroy tron
  360. debug remove_bp $tron_bp
  361. return "Off"
  362. }
  363. set tron_bp [debug set_bp 0xFF43 {} {update_tron}]
  364. osd create rectangle tron \
  365. -x 269 -y 220 -h 7 -w 42 -scaled true \
  366. -borderrgba 0x00000040 -bordersize 0.5
  367. osd create text tron.text -x 3 -y 1 -size 4 -rgba 0xffffffff
  368. return "On"
  369. }
  370. proc update_tron {} {
  371. if {![osd exists tron]} return
  372. set tron ""
  373. if {[peek16 0xF41C] != 0xFFFF} {
  374. set tron "Line: [peek16 0xF41C]"
  375. }
  376. osd configure tron.text -text ${tron}
  377. }