_scc_toys.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. # thanks to bifimsx for his help and his technical documentation @
  2. # http://bifi.msxnet.org/msxnet/tech/scc.html
  3. #
  4. # TODO:
  5. # - optimize! (A LOT!)
  6. # - support SCC-I
  7. set_help_text toggle_scc_viewer\
  8. {Toggles display of the SCC viewer in which you can follow the wave forms and
  9. volume per SCC channel in real time. Note: it doesn't explicitly support SCC-I
  10. yet and it can take up quite some CPU load...}
  11. namespace eval scc_toys {
  12. #scc viewer
  13. variable scc_viewer_active false
  14. variable scc_devices [list]
  15. variable num_samples 32
  16. variable num_channels 5
  17. variable vertical_downscale_factor 4
  18. variable channel_height [expr {256 / $vertical_downscale_factor}]
  19. variable machine_switch_trigger_id 0
  20. variable frame_trigger_id 0
  21. variable volume_address [expr {$num_samples * $num_channels + 2 * $num_channels}]
  22. #scc editor / PSG2SCC
  23. variable active false
  24. variable cur_wp1
  25. variable cur_wp2
  26. variable latch -1
  27. variable regs [list 0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 -1 0xaf 0xaa 0xab 0xac -1 -1 -1 -1 -1]
  28. variable select_device
  29. variable select_device_chan 0
  30. proc update_device_list {} {
  31. variable scc_devices
  32. variable select_device
  33. set scc_devices [list]
  34. foreach soundchip [machine_info sounddevice] {
  35. switch [machine_info sounddevice $soundchip] {
  36. "Konami SCC" -
  37. "Konami SCC+" {
  38. lappend scc_devices $soundchip
  39. }
  40. }
  41. }
  42. if {[llength $scc_devices] == 0} {
  43. #if no SCC is present try to plug in SCC
  44. if {![catch {ext scc} errorText]} {
  45. update_device_list
  46. } else {
  47. error "No SCC devices present and failed to insert one: $errorText"
  48. }
  49. }
  50. # for now, always select the first device
  51. set select_device [lindex $scc_devices 0]
  52. }
  53. proc scc_viewer_init {} {
  54. variable machine_switch_trigger_id
  55. variable scc_viewer_active
  56. variable scc_devices
  57. variable num_channels
  58. variable num_samples
  59. variable vertical_downscale_factor
  60. variable channel_height
  61. update_device_list
  62. #set base element
  63. osd create rectangle scc_viewer \
  64. -x 2 \
  65. -y 2 \
  66. -alpha 0 \
  67. -z 100
  68. set textheight 15
  69. set border_width 2
  70. set inter_channel_spacing 8
  71. set device_width [expr {$num_channels * ($num_samples + $inter_channel_spacing) \
  72. - $inter_channel_spacing + 2 * $border_width}]
  73. #create channels
  74. set number 0
  75. set offset 0
  76. foreach device $scc_devices {
  77. osd create rectangle scc_viewer.$device \
  78. -x [expr {$offset + $number * $device_width}] \
  79. -h [expr {$channel_height + 2 * $border_width + $textheight}] \
  80. -w $device_width \
  81. -rgba 0xffffff20 \
  82. -clip true
  83. osd create text scc_viewer.$device.title \
  84. -rgba 0xffffffff \
  85. -text $device \
  86. -size $textheight
  87. for {set chan 0} {$chan < $num_channels} {incr chan} {
  88. osd create rectangle scc_viewer.$device.$chan \
  89. -x [expr {($chan * ($num_samples + $inter_channel_spacing)) + $border_width}] \
  90. -y [expr {$border_width + $textheight}] \
  91. -h $channel_height \
  92. -w $num_samples \
  93. -rgba "0x0044aa80 0x2266dd80 0x0055cc80 0x44aaff80" \
  94. -borderrgba 0xffffff80 -bordersize 1 \
  95. -clip true
  96. osd create rectangle scc_viewer.$device.$chan.volume \
  97. -relw 1 \
  98. -z 1 \
  99. -rgba 0x0077ff80 \
  100. -borderrgba 0x0077ffc0 -bordersize 1
  101. osd create rectangle scc_viewer.$device.$chan.mid \
  102. -y [expr {$channel_height / 2}] \
  103. -h 1 \
  104. -relw 1 \
  105. -z 3 \
  106. -rgba 0xdd0000ff
  107. osd create rectangle scc_viewer.$device.$chan.mid.2 \
  108. -y -1 \
  109. -h 3 \
  110. -relw 1 \
  111. -rgba 0xff000060
  112. for {set pos 0} {$pos < $num_samples} {incr pos} {
  113. osd create rectangle scc_viewer.$device.$chan.$pos \
  114. -x $pos \
  115. -y [expr {$channel_height / 2}] \
  116. -w 2 \
  117. -z 2 \
  118. -rgba 0xffffffb0
  119. }
  120. }
  121. incr number
  122. set offset 10
  123. }
  124. set machine_switch_trigger_id [after machine_switch [namespace code scc_viewer_reset]]
  125. }
  126. proc update_scc_viewer {} {
  127. variable scc_viewer_active
  128. variable scc_devices
  129. variable num_channels
  130. variable num_samples
  131. variable vertical_downscale_factor
  132. variable channel_height
  133. variable frame_trigger_id
  134. variable volume_address
  135. if {!$scc_viewer_active} return
  136. foreach device $scc_devices {
  137. binary scan [debug read_block "$device SCC" 0 224] c* scc_regs
  138. for {set chan 0} {$chan < $num_channels} {incr chan} {
  139. for {set pos 0} {$pos < $num_samples} {incr pos} {
  140. set scc_wave_new [expr {[get_scc_wave [lindex $scc_regs [expr {($chan * $num_samples) + $pos}]]] / $vertical_downscale_factor}]
  141. osd configure scc_viewer.$device.$chan.$pos \
  142. -h $scc_wave_new
  143. }
  144. set volume [expr {[lindex $scc_regs [expr {$volume_address + $chan}]] * 4}]
  145. osd configure scc_viewer.$device.$chan.volume \
  146. -h $volume \
  147. -y [expr {($channel_height - $volume) / 2}]
  148. }
  149. }
  150. # set frame_trigger_id [after frame [namespace code {puts [time update_scc_viewer]}]];# for profiling
  151. set frame_trigger_id [after time 0.05 [namespace code update_scc_viewer]]
  152. }
  153. proc get_scc_wave {sccval} {
  154. expr {-($sccval < 128 ? $sccval : $sccval - 256)}
  155. }
  156. proc scc_viewer_reset {} {
  157. variable scc_viewer_active
  158. if {!$scc_viewer_active} {
  159. error "Please fix a bug in this script!"
  160. }
  161. toggle_scc_viewer
  162. toggle_scc_viewer
  163. }
  164. proc toggle_scc_viewer {} {
  165. variable scc_viewer_active
  166. variable machine_switch_trigger_id
  167. variable frame_trigger_id
  168. if {$scc_viewer_active} {
  169. after cancel $machine_switch_trigger_id
  170. after cancel $frame_trigger_id
  171. set scc_viewer_active false
  172. osd destroy scc_viewer
  173. } else {
  174. scc_viewer_init
  175. set scc_viewer_active true
  176. update_scc_viewer
  177. }
  178. return ""
  179. }
  180. proc init {} {
  181. variable select_device
  182. update_device_list
  183. set_scc_wave $select_device 0 3
  184. set_scc_wave $select_device 1 2
  185. set_scc_wave $select_device 2 3
  186. }
  187. proc update1 {} {
  188. variable latch
  189. set latch $::wp_last_value
  190. }
  191. proc update2 {} {
  192. variable latch
  193. variable regs
  194. variable select_device
  195. set reg [expr {($latch == -1) ? $latch : [lindex $regs $latch]}]
  196. set val $::wp_last_value
  197. if {$latch == 7} {set val [expr {($val ^ 0x07) & 0x07}]}
  198. if {$reg != -1} {
  199. if {[catch {debug write "$select_device SCC" $reg $val}]} {
  200. # device gone? Let's deactivate
  201. toggle_psg2scc
  202. }
  203. }
  204. }
  205. proc toggle_psg2scc {} {
  206. variable active
  207. variable cur_wp1
  208. variable cur_wp2
  209. variable select_device
  210. if {!$active} {
  211. init
  212. set active true
  213. set cur_wp1 [debug set_watchpoint write_io 0xa0 1 {scc_toys::update1}]
  214. set cur_wp2 [debug set_watchpoint write_io 0xa1 1 {scc_toys::update2}]
  215. return "Activated."
  216. } else {
  217. debug remove_watchpoint $cur_wp1
  218. debug remove_watchpoint $cur_wp2
  219. catch { ;# may fail if device is gone
  220. debug write "$select_device SCC" 0xaf 0
  221. }
  222. set active false
  223. return "Deactivated."
  224. }
  225. }
  226. proc set_scc_form {device channel wave} {
  227. set base [expr {$channel * 32}]
  228. for {set i 0} {$i < 32} {incr i} {
  229. debug write "$device SCC" [expr {$base + $i}] "0x[string range $wave [expr {$i * 2}] [expr {$i * 2 + 1}]]"
  230. }
  231. }
  232. proc set_scc_wave {device channel form} {
  233. switch $form {
  234. 0 { #Saw Tooth
  235. set_scc_form $device $channel "fff7efe7dfd7cfc7bfb7afa79f978f877f776f675f574f473f372f271f170f07"
  236. }
  237. 1 { #Square
  238. set_scc_form $device $channel "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f80808080808080808080808080808080"
  239. }
  240. 2 { #Triangle
  241. set_scc_form $device $channel "7f7060504030201000f0e0d0c0b0a0908090a0b0c0d0e0f00010203040506070"
  242. }
  243. 3 { #Sin Wave
  244. set_scc_form $device $channel "001931475A6A757D7F7D756A5A47311900E7CFB9A6968B8380838B96A6B9CFE7"
  245. }
  246. 4 { #Organ
  247. set_scc_form $device $channel "0070502050703000507F6010304000B0106000E0F000B090C010E0A0C0F0C0A0"
  248. }
  249. 5 { #SAWWY001
  250. set_scc_form $device $channel "636E707070705F2198858080808086AB40706F8C879552707052988080808EC1"
  251. }
  252. 6 { #SQROOT01
  253. set_scc_form $device $channel "00407F401001EAD6C3B9AFA49C958F8A86838183868A8F959CA4AFB9C3D6EAFF"
  254. }
  255. 7 { #SQROOT01
  256. set_scc_form $device $channel "636E707070705F2198858080808086AB40706F8C879552707052988080808EC1"
  257. }
  258. 8 { #DYERVC01
  259. set_scc_form $device $channel "00407F4001C081C001407F4001C0014001E0012001F0011001FFFFFFFF404040"
  260. }
  261. 9 { #SPACY
  262. set_scc_form $device $channel "808ea0c0e000203f3e3c3a373129201c1000e6c0d000203f10e080c000200090"
  263. }
  264. }
  265. }
  266. #SCC editor/copier
  267. proc toggle_scc_editor {} {
  268. variable select_device
  269. if {![osd exists scc_viewer]} {toggle_scc_viewer}
  270. # If exists destory/reset and exit
  271. if {[osd exists scc]} {
  272. osd destroy scc
  273. osd destroy selected
  274. # Let's assume the user doesn't have the SCC Viewer active
  275. toggle_scc_viewer
  276. deactivate_input_layer scc_editor
  277. return ""
  278. }
  279. bind -layer scc_editor "mouse button1 down" {scc_toys::checkclick}
  280. activate_input_layer scc_editor
  281. osd create rectangle scc \
  282. -x 200 -y 100 -h 256 -w 256 \
  283. -rgba "0x0044aa80 0x2266dd80 0x0055cc80 0x44aaff80" \
  284. -borderrgba 0xffffffff -bordersize 1
  285. for {set i 0} {$i < 32} {incr i} {
  286. osd create rectangle scc.slider$i -x [expr {$i * 8}] -y 0 -h 255 -w 8 -rgba 0x0000ff80
  287. osd create rectangle scc.slider$i.val -x 0 -y 127 -h 1 -w 8 -rgba 0xffffff90
  288. }
  289. for {set i 0} {$i < 32} {incr i} {
  290. osd create rectangle "scc.hline$i" -x [expr {$i * 8 - 1}] -y 0 -h 255 -w 1 -rgba 0xffffff60
  291. osd create rectangle "scc.vline$i" -x 0 -y [expr {$i * 8 - 1}] -h 1 -w 255 -rgba 0xffffff60
  292. }
  293. osd create rectangle "scc.hmid1" -x 63 -y 0 -h 255 -w 1 -rgba 0xff000080
  294. osd create text "scc.hmid1.text" -x -2 -y -12 -text "7" -size 8 -rgba 0xffffffff
  295. osd create rectangle "scc.hmid2" -x 127 -y 0 -h 255 -w 1 -rgba 0xffffffff
  296. osd create text "scc.hmid2.text" -x -5 -y -12 -text "15" -size 8 -rgba 0xffffffff
  297. osd create rectangle "scc.hmid3" -x 191 -y 0 -h 255 -w 1 -rgba 0xff000080
  298. osd create text "scc.hmid3.text" -x -5 -y -12 -text "23" -size 8 -rgba 0xffffffff
  299. osd create text "scc.hline0.text" -x 0 -y -12 -text "0" -size 8 -rgba 0xffffffff
  300. osd create text "scc.hline31.text" -x 0 -y -12 -text "31" -size 8 -rgba 0xffffffff
  301. osd create rectangle "scc.vmid1" -x 0 -y 63 -h 1 -w 255 -rgba 0xff000080
  302. osd create text "scc.vmid1.text" -x -20 -y -4 -text "+64" -size 8 -rgba 0xffffffff
  303. osd create rectangle "scc.vmid2" -x 0 -y 127 -h 1 -w 255 -rgba 0xffffffff
  304. osd create text "scc.vmid2.text" -x -10 -y -4 -text "0" -size 8 -rgba 0xffffffff
  305. osd create rectangle "scc.vmid3" -x 0 -y 191 -h 1 -w 255 -rgba 0xff000080
  306. osd create text "scc.vmid3.text" -x -18 -y -4 -text "-64" -size 8 -rgba 0xffffffff
  307. osd create text "scc.vline0.text" -x -25 -y 0 -text "+128" -size 8 -rgba 0xffffffff
  308. osd create text "scc.vline31.text" -x -22 -y 0 -text "-128" -size 8 -rgba 0xffffffff
  309. osd create rectangle selected
  310. return ""
  311. }
  312. proc checkclick {} {
  313. variable scc_devices
  314. variable select_device
  315. variable select_device_chan
  316. #check editor matrix
  317. for {set i 0} {$i < 32} {incr i} {
  318. lassign [osd info "scc.slider$i" -mousecoord] x y
  319. if {($x >= 0 && $x <= 1) && ($y >= 0 && $y <= 1)} {
  320. debug write "$select_device SCC" [expr {$select_device_chan * 32 + $i}] [expr {int(255 * $y - 128) & 0xff}]
  321. osd configure scc.slider$i.val \
  322. -y [expr {$y * 255}] \
  323. -h [expr {128 - ($y * 255)}]
  324. }
  325. }
  326. #check scc viewer channels
  327. foreach device $scc_devices {
  328. for {set i 0} {$i < 5} {incr i} {
  329. lassign [osd info "scc_viewer.$device.$i" -mousecoord] x y
  330. if {($x >= 0 && $x <= 1) && ($y >= 0 && $y <= 1)} {
  331. #store device and channel picked from the SCC_viewer in memory
  332. set select_device $device
  333. set select_device_chan $i
  334. set abs_x [osd info "scc_viewer.$device" -x]
  335. set sel_h [osd info "scc_viewer.$device.$i" -h]
  336. set sel_w [osd info "scc_viewer.$device.$i" -w]
  337. set sel_x [osd info "scc_viewer.$device.$i" -x]
  338. set sel_y [osd info "scc_viewer.$device.$i" -y]
  339. osd configure selected \
  340. -x [expr {int($sel_x) + $abs_x}] \
  341. -y [expr {int($sel_y)}] \
  342. -w [expr {$sel_w + 4}] \
  343. -h [expr {$sel_h + 4}] \
  344. -z 1 \
  345. -rgba 0xff0000ff
  346. set base [expr {$i * 32}]
  347. for {set q 0} {$q < 32} {incr q} {
  348. set sccwave_new [get_scc_wave [debug read "$device SCC" [expr {$base + $q}]]]
  349. set sccwave_old [osd info scc.slider$q.val -h]
  350. osd configure scc.slider$q.val \
  351. -y [expr {128 + $y}] \
  352. -h $sccwave_new
  353. }
  354. }
  355. }
  356. }
  357. }
  358. proc get_val_matrix_column {sccval} {
  359. expr {$sccval < 0 ? $sccval + 256 : $sccval}
  360. }
  361. proc get_scc_string_from_matrix {name} {
  362. set sccstring ""
  363. set outputfile "$name"
  364. set output [open $outputfile "w"]
  365. for {set i 0} {$i < 32} {incr i} {
  366. set a [format %02x [get_val_matrix_column [expr {int([osd info scc.slider$i.val -h])}]]]
  367. set sccstring [concat $sccstring$a]
  368. }
  369. close $output
  370. puts "$outputfile writen to $name"
  371. return $sccstring
  372. }
  373. namespace export toggle_scc_editor
  374. namespace export toggle_psg2scc
  375. namespace export set_scc_wave
  376. namespace export toggle_scc_viewer
  377. } ;# namespace scc_toys
  378. namespace import scc_toys::*