45
45
# is as follows, in a Verilog-like syntax:
46
46
# assign V = {S, {{7{~S}}, M, 7'b0000000}[E+:15]};
47
47
#
48
- # Compatibility modes
49
- # -------------------
50
- #
51
- # Yamaha chips that have compatibility features implement them in a somewhat broken way. When
52
- # the compatibility feature is disabled (e.g. bit 5 of 0x01 TEST for YM3812), its registers are
53
- # masked off. However, the actual feature is still (partially) enabled and it will result in
54
- # broken playback if this is not accounted for. Therefore, for example, the reset sequence has to
55
- # enable all available advanced features, zero out the registers, and then disable them back for
56
- # compatibility with OPL clients that expect the compatibility mode to be on.
57
- #
58
48
# Bus cycles
59
49
# ----------
60
50
#
63
53
# wait states. Reads are referenced to ~RD falling edge, and always read exactly one register,
64
54
# although there might be undocumented registers elsewhere.
65
55
#
56
+ # On some chips (e.g OPL4) the register that can be read has a busy bit, which seems to be always
57
+ # the LSB. On many others (e.g. OPL3) there is no busy bit. Whether the busy bit is available
58
+ # on any given silicon seems pretty arbitrary.
59
+ #
60
+ # Register compatibility
61
+ # ----------------------
62
+ #
63
+ # Yamaha chips that have compatibility features implement them in a somewhat broken way. When
64
+ # the compatibility feature is disabled (e.g. bit 5 of 0x01 TEST for YM3812), its registers are
65
+ # masked off. However, the actual feature is still (partially) enabled and it will result in
66
+ # broken playback if this is not accounted for. Therefore, for example, the reset sequence has to
67
+ # enable all available advanced features, zero out the registers, and then disable them back for
68
+ # compatibility with OPL clients that expect the compatibility mode to be on.
69
+ #
66
70
# Register latency
67
71
# ----------------
68
72
#
74
78
# On YM3812, the latch latency is 12 cycles and the sample takes 72 clocks, therefore each
75
79
# address/data write cycle takes 12+12+72 clocks.
76
80
#
77
- # On some chips (e.g OPL4) the register that can be read has a busy bit, which seems to be always
78
- # the LSB. On many others (e.g. OPL3) there is no busy bit. Whether the busy bit is available
79
- # on any given silicon seems pretty arbitrary.
81
+ # Timing compatibility
82
+ # --------------------
83
+ #
84
+ # When OPL3, functions in the OPL3 mode (NEW=1), the address and data latency are the declared
85
+ # values, i.e. 32 and 32 master clock cycles. However, in OPL/OPL2 mode, OPL3 uses completely
86
+ # different timings. It is not clear what they are, but 32*4/32*4 is not enough (and lead to missed
87
+ # writes), whereas 36*4/36*4 seems to work fine. This is never mentioned in any documentation.
88
+ #
89
+ # Although it is not mentioned anywhere, it is generally understood that OPL3 in compatibility
90
+ # mode (NEW=0) is attempting to emulate two independent OPL2's present on the first release
91
+ # of Sound Blaster PRO, which could be the cause of the bizarre timings. See the following link:
92
+ # https://www.msx.org/forum/msx-talk/software/vgmplay-msx?page=29
80
93
#
81
94
# VGM timeline
82
95
# ------------
93
106
# synchronous digital logic, that doesn't generally affect the output until it breaks.
94
107
#
95
108
# * YM3812 stops working between 15 MHz (good) and 30 MHz (bad).
109
+ #
110
+ # Test cases
111
+ # ----------
112
+ #
113
+ # Good test cases that stress the various timings and interfaces are:
114
+ # * (YM3526) https://vgmrips.net/packs/pack/chelnov-atomic-runner-karnov track 03
115
+ # Good general-purpose OPL test.
116
+ # * (YM3812) https://vgmrips.net/packs/pack/ultima-vi-the-false-prohpet-ibm-pc-xt-at track 01
117
+ # This track makes missing notes (due to timing mismatches) extremely noticeable.
118
+ # * (YM3812) https://vgmrips.net/packs/pack/lemmings-dos
119
+ # This pack does very few commands at a time and doesn't have software vibrato, so if commands
120
+ # go missing, notes go out of tune.
121
+ # * (YM3812) https://vgmrips.net/packs/pack/zero-wing-toaplan-1 track 02
122
+ # Good general-purpose OPL2 test, exhibits serious glitches if the OPL2 isn't reset correctly
123
+ # or if the LSI TEST register handling is broken.
124
+ # * (YM3812) https://vgmrips.net/packs/pack/vimana-toaplan-1 track 02
125
+ # This is an OPL2 track but the music is written for OPL and in fact the VGM file disables
126
+ # WAVE SELECT as one of the first commands. Implementation bugs tend to silence drums,
127
+ # which is easily noticeable but only if you listen to the reference first.
128
+ # * (YMF262) https://vgmrips.net/packs/pack/touhou-eiyashou-imperishable-night-ibm-pc-at track 18
129
+ # Good general-purpose OPL3 test.
96
130
97
131
from abc import ABCMeta , abstractmethod
98
132
import os .path
@@ -167,7 +201,7 @@ def __init__(self, pads):
167
201
clk_sy_r = Signal ()
168
202
self .sync += [
169
203
clk_sy_r .eq (clk_sy_s ),
170
- self .stb_sy .eq (~ clk_sy_r & clk_sy_s )
204
+ self .stb_sy .eq (clk_sy_r & ~ clk_sy_s )
171
205
]
172
206
173
207
sh_r = Signal ()
@@ -285,18 +319,24 @@ def __init__(self, pads, in_fifo, out_fifo,
285
319
xfer_o = Signal (16 )
286
320
self .comb += [
287
321
# FIXME: this is uglier than necessary because of Migen bugs. Rewrite nicer in nMigen.
288
- xfer_o .eq (Cat ((Cat (xfer_i .m , Replicate (~ xfer_i .s , 7 )) << xfer_i .e )[1 :16 ], xfer_i .s ))
322
+ # xfer_o.eq(Cat((Cat(xfer_i.m, Replicate(~xfer_i.s, 7)) << xfer_i.e)[1:16], xfer_i.s))
323
+ xfer_o .eq (xfer_i .raw_bits ())
289
324
]
290
325
291
- data_r = Signal (16 )
292
- data_l = Signal (16 )
326
+ data_r = Signal (17 )
327
+ data_l = Signal (17 )
293
328
self .sync += If (dac_bus .stb_sy , data_r .eq (Cat (data_r [1 :], dac_bus .mo )))
294
- self .comb += xfer_i .raw_bits ().eq (data_l )
329
+ self .comb += xfer_i .raw_bits ().eq (data_l [ 0 :] )
295
330
296
331
self .submodules .data_fsm = FSM ()
297
332
self .data_fsm .act ("WAIT-SH" ,
298
333
NextValue (in_fifo .flush , ~ enabled ),
299
334
If (dac_bus .stb_sh & enabled ,
335
+ NextState ("WAIT-SY" )
336
+ )
337
+ )
338
+ self .data_fsm .act ("WAIT-SY" ,
339
+ If (dac_bus .stb_sy ,
300
340
NextState ("SAMPLE" )
301
341
)
302
342
)
@@ -406,8 +446,8 @@ def _check_level(self, feature, feature_level):
406
446
407
447
async def _check_enable_features (self , address , data ):
408
448
if address not in self ._registers :
409
- self ._log ("client uses undefined feature [%#04x]" ,
410
- address ,
449
+ self ._log ("client uses undefined feature [%#04x]=%#04x " ,
450
+ address , data ,
411
451
level = logging .WARN )
412
452
413
453
async def write_register (self , address , data , check_feature = True ):
@@ -451,10 +491,10 @@ async def read_samples(self, count, hint=0):
451
491
452
492
453
493
class YamahaOPLInterface (YamahaOPxInterface ):
454
- chips = ["YM3526 ( OPL) " ]
494
+ chips = ["YM3526/ OPL" ]
455
495
456
496
def get_vgm_clock_rate (self , vgm_reader ):
457
- return vgm_reader .ym3526_clk
497
+ return vgm_reader .ym3526_clk , 1
458
498
459
499
address_clocks = 12
460
500
data_clocks = 84
@@ -473,10 +513,13 @@ async def _check_enable_features(self, address, data):
473
513
474
514
475
515
class YamahaOPL2Interface (YamahaOPLInterface ):
476
- chips = YamahaOPLInterface .chips + ["YM3812 ( OPL2) " ]
516
+ chips = YamahaOPLInterface .chips + ["YM3812/ OPL2" ]
477
517
478
518
def get_vgm_clock_rate (self , vgm_reader ):
479
- return vgm_reader .ym3812_clk or YamahaOPLInterface .get_vgm_clock_rate (self , vgm_reader )
519
+ if vgm_reader .ym3812_clk :
520
+ return vgm_reader .ym3812_clk , 1
521
+ else :
522
+ return YamahaOPLInterface .get_vgm_clock_rate (self , vgm_reader )
480
523
481
524
_registers = YamahaOPLInterface ._registers + [
482
525
* range (0xE0 , 0xF6 )
@@ -489,15 +532,61 @@ async def _use_lowest_level(self):
489
532
await self .write_register (0x01 , 0x00 , check_feature = False )
490
533
491
534
async def _check_enable_features (self , address , data ):
492
- if address == 0x01 and data & 0x20 :
493
- self ._enable_level (2 )
535
+ if address == 0x01 and data in (0x00 , 0x20 ):
536
+ if data & 0x20 :
537
+ self ._enable_level (2 )
494
538
elif address in range (0xE0 , 0xF6 ):
495
539
if self ._check_level (address , 2 ):
496
540
await self .write_register (0x01 , 0x20 )
497
541
else :
498
542
await super ()._check_enable_features (address , data )
499
543
500
544
545
+ class YamahaOPL3Interface (YamahaOPL2Interface ):
546
+ chips = [chip + " (no CSM)" for chip in YamahaOPL2Interface .chips ] + ["YMF262/OPL3" ]
547
+
548
+ def get_vgm_clock_rate (self , vgm_reader ):
549
+ if vgm_reader .ymf262_clk :
550
+ return vgm_reader .ymf262_clk , 1
551
+ else :
552
+ ym3812_clk , _ = YamahaOPL2Interface .get_vgm_clock_rate (self , vgm_reader )
553
+ return ym3812_clk , 4
554
+
555
+ # The datasheet says use 32 master clock cycle latency. That's a lie, there's a /4 pre-divisor.
556
+ # So you'd think 32 * 4 master clock cycles would work. But 32 is also a lie, that doesn't
557
+ # result in robust playback. It appears that 36 is the real latency number.
558
+ address_clocks = 36 * 4
559
+ data_clocks = 36 * 4
560
+ sample_clocks = 72 * 4
561
+
562
+ _registers = YamahaOPL2Interface ._registers + [
563
+ 0x104 , * range (0x120 , 0x136 ), * range (0x140 , 0x156 ), * range (0x160 , 0x176 ),
564
+ * range (0x180 , 0x196 ), * range (0x1A0 , 0x1A9 ), * range (0x1B0 , 0x1B9 ), * range (0x1C0 , 0x1C9 ),
565
+ * range (0x1E0 , 0x1F6 )
566
+ ]
567
+
568
+ async def _use_highest_level (self ):
569
+ await super ()._use_highest_level ()
570
+ await self .write_register (0x105 , 0x01 , check_feature = False )
571
+
572
+ async def _use_lowest_level (self ):
573
+ await self .write_register (0x105 , 0x00 , check_feature = False )
574
+ await super ()._use_lowest_level ()
575
+
576
+ async def _check_enable_features (self , address , data ):
577
+ if address == 0x08 and data & 0x80 :
578
+ self ._log ("client uses deprecated and removed feature [0x08]|0x80" ,
579
+ level = logging .WARN )
580
+ elif address == 0x105 and data in (0x00 , 0x01 ):
581
+ if data & 0x01 :
582
+ self ._enable_level (3 )
583
+ elif address in range (0x100 , 0x200 ) and address in self ._registers :
584
+ if self ._check_level (address , 3 ):
585
+ await self .write_register (0x105 , 0x01 )
586
+ else :
587
+ await super ()._check_enable_features (address , data )
588
+
589
+
501
590
class YamahaVGMStreamPlayer (VGMStreamPlayer ):
502
591
def __init__ (self , reader , opx_iface , clock_rate ):
503
592
self ._reader = reader
@@ -542,6 +631,9 @@ async def ym3526_write(self, address, data):
542
631
async def ym3812_write (self , address , data ):
543
632
await self ._opx_iface .write_register (address , data )
544
633
634
+ async def ymf262_write (self , address , data ):
635
+ await self ._opx_iface .write_register (address , data )
636
+
545
637
async def wait_seconds (self , delay ):
546
638
await self ._opx_iface .wait_clocks (int (delay * self .clock_rate ))
547
639
@@ -555,7 +647,8 @@ def __init__(self, logger, opx_iface):
555
647
async def serve_index (self , request ):
556
648
with open (os .path .join (os .path .dirname (__file__ ), "index.html" )) as f :
557
649
index_html = f .read ()
558
- index_html = index_html .replace ("{{chips}}" , ", " .join (self ._opx_iface .chips ))
650
+ index_html = index_html .replace ("{{chip}}" , self ._opx_iface .chips [- 1 ])
651
+ index_html = index_html .replace ("{{compat}}" , ", " .join (self ._opx_iface .chips ))
559
652
return web .Response (text = index_html , content_type = "text/html" )
560
653
561
654
def _make_resampler (self , actual , preferred ):
@@ -613,14 +706,18 @@ async def serve_vgm(self, request):
613
706
614
707
self ._logger .info ("web: %s: VGM has commands for %s" ,
615
708
digest , ", " .join (vgm_reader .chips ()))
616
- if len (vgm_reader .chips ()) != 1 :
617
- raise ValueError ("VGM file does not contain commands for exactly one chip" )
618
709
619
- clock_rate = self ._opx_iface .get_vgm_clock_rate (vgm_reader )
710
+ clock_rate , clock_prescaler = self ._opx_iface .get_vgm_clock_rate (vgm_reader )
620
711
if clock_rate == 0 :
621
- raise ValueError ("VGM file does not contain commands for any supported chip" )
712
+ raise ValueError ("VGM file contains commands for {}, which is not a supported chip"
713
+ .format (", " .join (vgm_reader .chips ())))
622
714
if clock_rate & 0xc0000000 :
623
715
raise ValueError ("VGM file uses unsupported chip configuration" )
716
+ if len (vgm_reader .chips ()) != 1 :
717
+ raise ValueError ("VGM file contains commands for {}, but only playback of exactly "
718
+ "one chip is supported"
719
+ .format (", " .join (vgm_reader .chips ())))
720
+ clock_rate *= clock_prescaler
624
721
625
722
self ._logger .info ("web: %s: VGM is looped for %.2f/%.2f s" ,
626
723
digest , vgm_reader .loop_seconds , vgm_reader .total_seconds )
@@ -768,15 +865,17 @@ def add_build_arguments(cls, parser, access):
768
865
access .add_pin_argument (parser , "mo" , default = True )
769
866
770
867
parser .add_argument (
771
- "-d" , "--device" , metavar = "DEVICE" , choices = ["OPL" , "OPL2" ], required = True ,
772
- help = "Synthesizer family" )
868
+ "-d" , "--device" , metavar = "DEVICE" , choices = ["OPL" , "OPL2" , "OPL3" ], required = True ,
869
+ help = "synthesizer family" )
773
870
774
871
@staticmethod
775
872
def _device_iface_cls (args ):
776
873
if args .device == "OPL" :
777
874
return YamahaOPLInterface
778
875
if args .device == "OPL2" :
779
876
return YamahaOPL2Interface
877
+ if args .device == "OPL3" :
878
+ return YamahaOPL3Interface
780
879
781
880
def build (self , target , args ):
782
881
device_iface_cls = self ._device_iface_cls (args )
@@ -830,12 +929,13 @@ async def interact(self, device, args, opx_iface):
830
929
if len (vgm_reader .chips ()) != 1 :
831
930
raise GlasgowAppletError ("VGM file does not contain commands for exactly one chip" )
832
931
833
- clock_rate = opx_iface .get_vgm_clock_rate (vgm_reader )
932
+ clock_rate , clock_prescaler = opx_iface .get_vgm_clock_rate (vgm_reader )
834
933
if clock_rate == 0 :
835
934
raise GlasgowAppletError ("VGM file does not contain commands for any "
836
935
"supported chip" )
837
936
if clock_rate & 0xc0000000 :
838
937
raise GlasgowAppletError ("VGM file uses unsupported chip configuration" )
938
+ clock_rate *= clock_prescaler
839
939
840
940
vgm_player = YamahaVGMStreamPlayer (vgm_reader , opx_iface , clock_rate )
841
941
self .logger .info ("recording at sample rate %d Hz" , 1 / vgm_player .sample_time )
@@ -853,7 +953,6 @@ async def write_pcm(input_queue):
853
953
write_fut = asyncio .ensure_future (write_pcm (input_queue ))
854
954
done , pending = await asyncio .wait ([play_fut , record_fut , write_fut ],
855
955
return_when = asyncio .FIRST_EXCEPTION )
856
- print (done , pending )
857
956
for fut in done :
858
957
await fut
859
958
0 commit comments