File: _example_tools.tcl

package info (click to toggle)
openmsx 21.0%2Bdfsg-3
  • links: PTS
  • area: main
  • in suites: sid
  • size: 28,132 kB
  • sloc: cpp: 244,928; xml: 54,344; tcl: 15,603; python: 5,335; perl: 281; sh: 78; makefile: 57
file content (401 lines) | stat: -rw-r--r-- 11,635 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# Return the content of the MSX screen as a string (text-modes only)

set_help_text get_screen \
{Returns the content of the MSX screen as a string (only works for text-modes).
}

set ::is_svi [expr {[machine_info type] eq "SVI"}]
set ::cursor_chr  [expr {$::is_svi ? 191    : 255}]
set ::cursor_addr [expr {$::is_svi ? 0xFD67 : 0xFBCC}]
proc unknown_chr {c} {
	if {$c != $::cursor_chr} { return 32 }
	scan [msxcode2unicode [format %c [peek $::cursor_addr]]] %c u
	return $u
}
proc get_screen {} {
	set mode [get_screen_mode]
	switch -- $mode {
		TEXT40 { set addr    0 ; set width 40 }
		TEXT80 { set addr    0 ; set width 80 }
		1      { set addr 6144 ; set width 32 }
		default { error "Screen mode $mode not supported" }
	}

	# scrape screen and build string
	set screen ""
	for {set y 0} {$y < 24} {incr y} {
		set vram [debug read_block VRAM $addr $width]
		append screen [msxcode2unicode $vram unknown_chr] "\n"
		incr addr $width
	}
	return $screen
}

proc copy_screen_to_clipboard {} {
	set_clipboard_text [get_screen]
}

#***********************************************
#* Basic Reader
#*
#* A special thanks to Vincent van Dam for
#* giving me permission to translate his
#* script into Tcl
#***********************************************

set_help_text listing \
{Interpret the content of the memory as a BASIC program and return the
equivalent output of the BASIC LIST command. (May not be terribly useful, but
it does show the power of openMSX scripts ;-) You can add an optional start
address for the decoding; by default it's what the system variable TXTTAB says.
}

proc listing {{startaddr "default"}} {

	# TODO: reduce duplication, difference seems only in last couple of
	# lines

	set tokens1MSX [list \
		"" "END" "FOR" "NEXT" "DATA" "INPUT" "DIM" "READ" "LET" \
		"GOTO" "RUN" "IF" "RESTORE" "GOSUB" "RETURN" "REM" "STOP" \
		"PRINT" "CLEAR" "LIST" "NEW" "ON" "WAIT" "DEF" "POKE" "CONT" \
		"CSAVE" "CLOAD" "OUT" "LPRINT" "LLIST" "CLS" "WIDTH" "ELSE" \
		"TRON" "TROFF" "SWAP" "ERASE" "ERROR" "RESUME" "DELETE" \
		"AUTO" "RENUM" "DEFSTR" "DEFINT" "DEFSNG" "DEFDBL" "LINE" \
		"OPEN" "FIELD" "GET" "PUT" "CLOSE" "LOAD" "MERGE" "FILES" \
		"LSET" "RSET" "SAVE" "LFILES" "CIRCLE" "COLOR" "DRAW" "PAINT" \
		"BEEP" "PLAY" "PSET" "PRESET" "SOUND" "SCREEN" "VPOKE" \
		"SPRITE" "VDP" "BASE" "CALL" "TIME" "KEY" "MAX" "MOTOR" \
		"BLOAD" "BSAVE" "DSKO$" "SET" "NAME" "KILL" "IPL" "COPY" "CMD" \
		"LOCATE" "TO" "THEN" "TAB(" "STEP" "USR" "FN" "SPC(" "NOT" \
		"ERL" "ERR" "STRING$" "USING" "INSTR" "'" "VARPTR" "CSRLIN" \
		"ATTR$" "DSKI$" "OFF" "INKEY$" "POINT" ">" "=" "<" "+" "-" "*" \
		"/" "^" "AND" "OR" "XOR" "EQV" "IMP" "MOD" "\\" "\n" "" \
		"\0x127"]

	set tokens1SVI [list \
		"" "END" "FOR" "NEXT" "DATA" "INPUT" "DIM" "READ" "LET" \
		"GOTO" "RUN" "IF" "RESTORE" "GOSUB" "RETURN" "REM" "STOP" \
		"PRINT" "CLEAR" "LIST" "NEW" "ON" "WAIT" "DEF" "POKE" "CONT" \
		"CSAVE" "CLOAD" "OUT" "LPRINT" "LLIST" "CLS" "WIDTH" "ELSE" \
		"TRON" "TROFF" "SWAP" "ERASE" "ERROR" "RESUME" "DELETE" \
		"AUTO" "RENUM" "DEFSTR" "DEFINT" "DEFSNG" "DEFDBL" "LINE" \
		"OPEN" "FIELD" "GET" "PUT" "CLOSE" "LOAD" "MERGE" "FILES" \
		"LSET" "RSET" "SAVE" "LFILES" "CIRCLE" "COLOR" "DRAW" "PAINT" \
		"BEEP" "PLAY" "PSET" "PRESET" "SOUND" "SCREEN" "VPOKE" \
		"KEY" "CLICK" "SWITCH" "MAX" "MON" "MOTOR" "BLOAD" "BSAVE" \
		"MDM" "DIAL" "DSKO$" "SET" "NAME" "KILL" "IPL" "COPY" "CMD" \
		"LOCATE" "TO" "THEN" "TAB(" "STEP" "USR" "FN" "SPC(" "NOT" \
		"ERL" "ERR" "STRING$" "USING" "INSTR" "'" "VARPTR" "CSRLIN" \
		"ATTR$" "DSKI$" "OFF" "INKEY$" "POINT" "SPRITE" "TIME" ">" \
		"=" "<" "+" "-" "*" "/" "^" "AND" "OR" "XOR" "EQV" "IMP" \
		"MOD" "\\" "\0x127"]

	set tokens2 [list \
		"" "LEFT$" "RIGHT$" "MID$" "SGN" "INT" "ABS" "SQR" "RND" "SIN" \
		"LOG" "EXP" "COS" "TAN" "ATN" "FRE" "INP" "POS" "LEN" "STR$" \
		"VAL" "ASC" "CHR$" "PEEK" "VPEEK" "SPACE$" "OCT$" "HEX$" \
		"LPOS" "BIN$" "CINT" "CSNG" "CDBL" "FIX" "STICK" "STRIG" "PDL" \
		"PAD" "DSKF" "FPOS" "CVI" "CVS" "CVD" "EOF" "LOC" "LOF" "MKI$" \
		"MKS$" "MKD$" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
		"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
		"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" \
		"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""]

	if {[machine_info type] eq "SVI"} {
		set TXTTAB 0xF54A
		set tokens1 $tokens1SVI
	} else {
		set TXTTAB 0xF676
		set tokens1 $tokens1MSX
	}

	if {$startaddr eq "default"} {
		set startaddr [peek16 $TXTTAB]
	}

	# Loop over all lines
	set listing ""
	for {set addr $startaddr} {[peek16 $addr] != 0} {} {
		append listing [format "0x%x > " $addr]
		incr addr 2
		append listing "[peek16 $addr] "
		incr addr 2
		set inquotes false
		set inrem false
		# Loop over tokens in one line
		while {true} {
			set token [peek $addr]; incr addr
			set t [format "%c" $token]
			if {$token == 0x0} {
				set t ""
				break
			} elseif {$token == 0x22} {
				set inquotes [expr !$inquotes]
			}
			if {!$inquotes && !$inrem} {
				if {0x80 < $token && $token < 0xFF} {
					if {$token == 0x8F} {
						if {[peek $addr] == 0xE6} {
							set t "'"
							incr addr
						} else {
							set t "REM"
						}
						set inrem true
					} else {
						set t [lindex $tokens1 [expr {$token - 0x80}]]
					}
				} elseif {$token == 0xFF} {
					set t [lindex $tokens2 [expr {[peek $addr] - 0x80}]]
					incr addr
				} elseif {$token == 0x3a} {
					if {[peek $addr] == 0xA1 || ([peek $addr] == 0x8F && [peek [expr {$addr + 1}]] == 0xE6)} {
						set t ""
					}
				} elseif {$token == 0x0B} {
					set t [format "&O%o" [peek16 $addr]]
					incr addr 2
				} elseif {$token == 0x0C} {
					set t [format "&H%X" [peek16 $addr]]
					incr addr 2
				} elseif {$token == 0x0D} {
					# line number (stored as address)
					set t [format "%d" [peek16 [expr {[peek16 $addr] + 3}]]]
					incr addr 2
				} elseif {$token == 0x0E} {
					set t [format "%d" [peek16 $addr]]
					incr addr 2
				} elseif {$token == 0x0F} {
					set t [format "%d" [peek $addr]]
					incr addr
				} elseif {$token == 0x1C} {
					set t [format "%d" [peek16 $addr]]
					incr addr 2
				} elseif {$token == 0x1D} {
					set exp [peek $addr]
					incr addr
					set t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					set exp "E[expr {($exp & 0x7F) - 70}]"
					set t [make_msx_single $t$exp]
				} elseif {$token == 0x1F} {
					set exp [peek $addr]
					incr addr
					set t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					append t [format "%02x" [peek $addr]]
					incr addr
					set exp "E[expr {($exp & 0x7F) - 78}]"
					set t [make_msx_double $t$exp]
				} elseif {0x11 <= $token && $token <= 0x1A} {
					set t [expr {$token - 0x11}]
				}
			}
			append listing $t
		}
		append listing "\n"
	}
	return $listing
}

proc make_msx_double {num} {
	if {[string toupper $num] ne $num} {
		error "Invalid BASIC program"
	}
	set n [format "% .14E" $num]
	set ee [string range $n 18 20]
	set e [scan $ee %d]
	if {$e <-2 && $e >-5} {
		set n [format "%.14G" [string range $n 0 16]]E$ee
	} else {
		set n [format "%.14G" $num]
	}
	if {[string range $n 0 1] eq "0."} {\
		set n [string range $n 1 100]
	}
	if {![string match *E* $n]} {
		append n "#"
	}
	return $n
}

proc make_msx_single {num} {
	if {[string toupper $num] ne $num} {
		error "Invalid BASIC program"
	}
	set n [format "% .6E" $num]
	set ee [string range $n 10 12]
	set e [scan $ee %d ]
	if {$e <-2 && $e >-5} {
		set n [format "%.6G" [string range $n 0 8]]E$ee
	} else {
		set n [format "%.6G" $num]
	}
	if {[string range $n 0 1] eq "0."} {
		set n [string range $n 1 100]
	}
	if {$n >32767 && ![string match *.* $n]} {
		append n "!"
	}
	return $n
}

set_help_text get_color_count \
"Gives some statistics on the used colors of the currently visible screen. Does not take into account sprites, screen splits and other trickery.

Options:
    -sort <field>, where <field> is either 'color' (default) or 'amount'
    -reverse, to reverse the sorting order
    -all, to also include colors that have a count of zero
"
proc get_color_count {args} {
	set result ""

	set sortindex 0
	set sortorder "-increasing"
	set showall false

	# parse options
	while {1} {
		switch -- [lindex $args 0] {
		"" break
		"-sort" {
			set sortfield [lindex $args 1]
			if {$sortfield eq "color"} {
				set sortindex 0
			} elseif {$sortfield eq "amount"} {
				set sortindex 1
			} else {
				error "Unsupported sort field: $sortfield"
			}
			set args [lrange $args 2 end]
		}
		"-reverse" {
			set sortorder "-decreasing"
			set args [lrange $args 1 end]
		}
		"-all" {
			set showall true
			set args [lrange $args 1 end]
		}
		"default" {
			error "Invalid option: [lindex $args 0]"
		}
		}
	}

	set mode [get_screen_mode_number]
	switch -- $mode {
		5 {
			set nofbytes_per_line 128
			set nofpixels_per_byte 2
			set page_size [expr {32 * 1024}]
		}
		6 {
			set nofbytes_per_line 128
			set nofpixels_per_byte 4
			set page_size [expr {32 * 1024}]
		}
		7 {
			set nofbytes_per_line 256
			set nofpixels_per_byte 2
			set page_size [expr {64 * 1024}]
		}
		8 {
			set nofbytes_per_line 256
			set nofpixels_per_byte 1
			set page_size [expr {64 * 1024}]
		}
		default {
			error "Screen mode $mode not supported (yet)"
		}
	}
	set page [expr {([debug read "VDP regs" 2] & 96) >> 5}]
	set noflines [expr {([debug read "VDP regs" 9] & 128) ? 212 : 192}]
	set bpp [expr {8 / $nofpixels_per_byte}]
	set nrcolors [expr {1 << $bpp}]
	append result "Counting pixels of screen $mode, page $page with $noflines lines...\n"

	# get bytes into large list
	set offset [expr {$page_size * $page}]
	set nrbytes [expr {$noflines * $nofbytes_per_line}]
	binary scan [debug read_block VRAM $offset $nrbytes] c* myvram

	# analyze pixels
	set pixelstats [dict create]
	for {set p 0} {$p < $nrcolors} {incr p} {
		dict set pixelstats $p 0
	}
	set mask [expr {$nrcolors - 1}]
	foreach byte $myvram {
		for {set pixel 0} {$pixel < $nofpixels_per_byte} {incr pixel} {
			set color [expr {($byte >> ($pixel * $bpp)) & $mask}]
			dict incr pixelstats $color
		}
	}
	# convert to list
	set pixelstatlist [list]
	dict for {key val} $pixelstats {
		if {$showall || ($val != 0)} {
			lappend pixelstatlist [list $key $val]
		}
	}
	set pixelstatlistsorted [lsort -integer $sortorder -index $sortindex $pixelstatlist]
	# print results
	set number 0
	set colorwidth [expr {($mode == 8) ? 3 : 2}]
	set palette ""
	foreach pixelinfo $pixelstatlistsorted {
		incr number
		lassign $pixelinfo color amount
		if {$mode != 8} {
			set palette " ([getcolor $color])"
		}
		append result [format "%${colorwidth}d: color %${colorwidth}d$palette: amount: %6d\n" $number $color $amount]
	}
	return $result
}

variable tron_bp

proc toggle_tron {} {
	variable tron_bp

	if {[osd exists tron]} {
		osd destroy tron
		debug remove_bp $tron_bp
		return "Off"
	}

	set tron_bp [debug set_bp 0xFF43 {} {update_tron}]

	osd create rectangle tron \
		-x 269 -y 220 -h 7 -w 42 -scaled true \
		-borderrgba 0x00000040 -bordersize 0.5
	osd create text tron.text -x 3 -y 1 -size 4 -rgba 0xffffffff
	return "On"
}

proc update_tron {} {
	if {![osd exists tron]} return
	set tron ""
	if {[peek16 0xF41C] != 0xFFFF} {
		set tron "Line: [peek16 0xF41C]"
	}
	osd configure tron.text -text ${tron}
}