]> 4ch.mooo.com Git - 16.git/commitdiff
vgm sound driver added!!
authorsparky4 <sparky4@cock.li>
Fri, 31 Jul 2015 17:42:10 +0000 (12:42 -0500)
committersparky4 <sparky4@cock.li>
Fri, 31 Jul 2015 17:42:10 +0000 (12:42 -0500)
new file:   16/vgmsnd/3812intf.c
new file:   16/vgmsnd/3812intf.h
new file:   16/vgmsnd/FotF_Title.vgm
new file:   16/vgmsnd/GingaNinkyouDen_Round3.vgm
new file:   16/vgmsnd/Lemmings3D_SweetLand1.vgm
new file:   16/vgmsnd/Lemmings_LetsGo.vgm
renamed:    VgmSndDrv.7z -> 16/vgmsnd/VgmSndDrv.7z
new file:   16/vgmsnd/VgmSndDrv.exe
new file:   16/vgmsnd/VgmSndDrv.sln
new file:   16/vgmsnd/VgmSndDrv.vcxproj
new file:   16/vgmsnd/VgmSndDrv.vcxproj.filters
new file:   16/vgmsnd/fmopl.c
new file:   16/vgmsnd/fmopl.h
new file:   16/vgmsnd/main.c
new file:   16/vgmsnd/mamedef.h
new file:   16/vgmsnd/stdbool.h
new file:   16/vgmsnd/stdtype.h
new file:   16/vgmsnd/vgmSndDrv.c
new file:   16/vgmsnd/vgmSndDrv.h

19 files changed:
16/vgmsnd/3812intf.c [new file with mode: 0644]
16/vgmsnd/3812intf.h [new file with mode: 0644]
16/vgmsnd/FotF_Title.vgm [new file with mode: 0644]
16/vgmsnd/GingaNinkyouDen_Round3.vgm [new file with mode: 0644]
16/vgmsnd/Lemmings3D_SweetLand1.vgm [new file with mode: 0644]
16/vgmsnd/Lemmings_LetsGo.vgm [new file with mode: 0644]
16/vgmsnd/VgmSndDrv.7z [moved from VgmSndDrv.7z with 100% similarity]
16/vgmsnd/VgmSndDrv.exe [new file with mode: 0644]
16/vgmsnd/VgmSndDrv.sln [new file with mode: 0644]
16/vgmsnd/VgmSndDrv.vcxproj [new file with mode: 0644]
16/vgmsnd/VgmSndDrv.vcxproj.filters [new file with mode: 0644]
16/vgmsnd/fmopl.c [new file with mode: 0644]
16/vgmsnd/fmopl.h [new file with mode: 0644]
16/vgmsnd/main.c [new file with mode: 0644]
16/vgmsnd/mamedef.h [new file with mode: 0644]
16/vgmsnd/stdbool.h [new file with mode: 0644]
16/vgmsnd/stdtype.h [new file with mode: 0644]
16/vgmsnd/vgmSndDrv.c [new file with mode: 0644]
16/vgmsnd/vgmSndDrv.h [new file with mode: 0644]

diff --git a/16/vgmsnd/3812intf.c b/16/vgmsnd/3812intf.c
new file mode 100644 (file)
index 0000000..876bb57
--- /dev/null
@@ -0,0 +1,97 @@
+/******************************************************************************\r
+* FILE\r
+*   Yamaha 3812 emulator interface - MAME VERSION\r
+*\r
+* CREATED BY\r
+*   Ernesto Corvi\r
+*\r
+* UPDATE LOG\r
+*   JB  28-04-2002  Fixed simultaneous usage of all three different chip types.\r
+*                       Used real sample rate when resample filter is active.\r
+*       AAT 12-28-2001  Protected Y8950 from accessing unmapped port and keyboard handlers.\r
+*   CHS 1999-01-09  Fixes new ym3812 emulation interface.\r
+*   CHS 1998-10-23  Mame streaming sound chip update\r
+*   EC  1998        Created Interface\r
+*\r
+* NOTES\r
+*\r
+******************************************************************************/\r
+#include <stddef.h>\r
+#include "mamedef.h"\r
+#include "3812intf.h"\r
+#include "fmopl.h"\r
+\r
+\r
+typedef struct _ym3812_state ym3812_state;\r
+struct _ym3812_state\r
+{\r
+       void *                  chip;\r
+};\r
+\r
+\r
+extern INT32 CHIP_SAMPLE_RATE;\r
+\r
+#define MAX_CHIPS      0x02\r
+static ym3812_state YM3812Data[MAX_CHIPS];\r
+static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL};\r
+\r
+void ym3812_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples)\r
+{\r
+       ym3812_state *info = &YM3812Data[ChipID];\r
+       ym3812_update_one(info->chip, outputs, samples);\r
+}\r
+\r
+static void _stream_update(void * param/*, int interval*/)\r
+{\r
+       ym3812_state *info = (ym3812_state *)param;\r
+       ym3812_update_one(info->chip, DUMMYBUF, 0);\r
+}\r
+\r
+\r
+//static DEVICE_START( ym3812 )\r
+int device_start_ym3812(UINT8 ChipID, int clock)\r
+{\r
+       ym3812_state *info;\r
+       int rate;\r
+       \r
+       if (ChipID >= MAX_CHIPS)\r
+               return 0;\r
+       \r
+       info = &YM3812Data[ChipID];\r
+       //rate = clock /72;\r
+       rate = CHIP_SAMPLE_RATE;\r
+\r
+       info->chip = ym3812_init(clock, rate);\r
+\r
+       /* YM3812 setup */\r
+       ym3812_set_timer_handler (info->chip, NULL, info);\r
+       ym3812_set_irq_handler   (info->chip, NULL, info);\r
+       ym3812_set_update_handler(info->chip, _stream_update, info);\r
+\r
+       return rate;\r
+}\r
+\r
+void device_stop_ym3812(UINT8 ChipID)\r
+{\r
+       ym3812_state *info = &YM3812Data[ChipID];\r
+       ym3812_shutdown(info->chip);\r
+}\r
+\r
+void device_reset_ym3812(UINT8 ChipID)\r
+{\r
+       ym3812_state *info = &YM3812Data[ChipID];\r
+       ym3812_reset_chip(info->chip);\r
+}\r
+\r
+\r
+UINT8 ym3812_r(UINT8 ChipID, offs_t offset)\r
+{\r
+       ym3812_state *info = &YM3812Data[ChipID];\r
+       return ym3812_read(info->chip, offset & 1);\r
+}\r
+\r
+void ym3812_w(UINT8 ChipID, offs_t offset, UINT8 data)\r
+{\r
+       ym3812_state *info = &YM3812Data[ChipID];\r
+       ym3812_write(info->chip, offset & 1, data);\r
+}\r
diff --git a/16/vgmsnd/3812intf.h b/16/vgmsnd/3812intf.h
new file mode 100644 (file)
index 0000000..bb8c69f
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once\r
+\r
+void ym3812_stream_update(UINT8 ChipID, INT32 **outputs, int samples);\r
+int device_start_ym3812(UINT8 ChipID, int clock);\r
+void device_stop_ym3812(UINT8 ChipID);\r
+void device_reset_ym3812(UINT8 ChipID);\r
+\r
+UINT8 ym3812_r(UINT8 ChipID, UINT32 offset);\r
+void ym3812_w(UINT8 ChipID, UINT32 offset, UINT8 data);\r
diff --git a/16/vgmsnd/FotF_Title.vgm b/16/vgmsnd/FotF_Title.vgm
new file mode 100644 (file)
index 0000000..652c8b4
Binary files /dev/null and b/16/vgmsnd/FotF_Title.vgm differ
diff --git a/16/vgmsnd/GingaNinkyouDen_Round3.vgm b/16/vgmsnd/GingaNinkyouDen_Round3.vgm
new file mode 100644 (file)
index 0000000..5ac3233
Binary files /dev/null and b/16/vgmsnd/GingaNinkyouDen_Round3.vgm differ
diff --git a/16/vgmsnd/Lemmings3D_SweetLand1.vgm b/16/vgmsnd/Lemmings3D_SweetLand1.vgm
new file mode 100644 (file)
index 0000000..fb4ee0c
Binary files /dev/null and b/16/vgmsnd/Lemmings3D_SweetLand1.vgm differ
diff --git a/16/vgmsnd/Lemmings_LetsGo.vgm b/16/vgmsnd/Lemmings_LetsGo.vgm
new file mode 100644 (file)
index 0000000..8cd74ca
Binary files /dev/null and b/16/vgmsnd/Lemmings_LetsGo.vgm differ
similarity index 100%
rename from VgmSndDrv.7z
rename to 16/vgmsnd/VgmSndDrv.7z
diff --git a/16/vgmsnd/VgmSndDrv.exe b/16/vgmsnd/VgmSndDrv.exe
new file mode 100644 (file)
index 0000000..e001617
Binary files /dev/null and b/16/vgmsnd/VgmSndDrv.exe differ
diff --git a/16/vgmsnd/VgmSndDrv.sln b/16/vgmsnd/VgmSndDrv.sln
new file mode 100644 (file)
index 0000000..8072ac5
--- /dev/null
@@ -0,0 +1,20 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 11.00\r
+# Visual Studio 2010\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VgmSndDrv", "VgmSndDrv.vcxproj", "{85B88412-1CEF-42C9-B991-C17D41E5A0AE}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {85B88412-1CEF-42C9-B991-C17D41E5A0AE}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {85B88412-1CEF-42C9-B991-C17D41E5A0AE}.Debug|Win32.Build.0 = Debug|Win32\r
+               {85B88412-1CEF-42C9-B991-C17D41E5A0AE}.Release|Win32.ActiveCfg = Release|Win32\r
+               {85B88412-1CEF-42C9-B991-C17D41E5A0AE}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/16/vgmsnd/VgmSndDrv.vcxproj b/16/vgmsnd/VgmSndDrv.vcxproj
new file mode 100644 (file)
index 0000000..a374502
--- /dev/null
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{85B88412-1CEF-42C9-B991-C17D41E5A0AE}</ProjectGuid>\r
+    <Keyword>Win32Proj</Keyword>\r
+    <RootNamespace>VgmSndDrv</RootNamespace>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>true</UseDebugLibraries>\r
+    <CharacterSet>Unicode</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>false</UseDebugLibraries>\r
+    <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <CharacterSet>Unicode</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <LinkIncremental>true</LinkIncremental>\r
+    <IncludePath>D:\VStudio-Programme\VC++\libvgm;$(IncludePath)</IncludePath>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <LinkIncremental>false</LinkIncremental>\r
+    <IncludePath>D:\VStudio-Programme\VC++\libvgm;$(IncludePath)</IncludePath>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+      <WarningLevel>Level3</WarningLevel>\r
+      <Optimization>Disabled</Optimization>\r
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;AUDDRV_WINMM;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+    </ClCompile>\r
+    <Link>\r
+      <SubSystem>Console</SubSystem>\r
+      <GenerateDebugInformation>true</GenerateDebugInformation>\r
+      <AdditionalDependencies>kernel32.lib;user32.lib;winmm.lib</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <ClCompile>\r
+      <WarningLevel>Level3</WarningLevel>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+      <Optimization>MaxSpeed</Optimization>\r
+      <FunctionLevelLinking>true</FunctionLevelLinking>\r
+      <IntrinsicFunctions>true</IntrinsicFunctions>\r
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;AUDDRV_WINMM;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+    </ClCompile>\r
+    <Link>\r
+      <SubSystem>Console</SubSystem>\r
+      <GenerateDebugInformation>true</GenerateDebugInformation>\r
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+      <OptimizeReferences>true</OptimizeReferences>\r
+      <AdditionalDependencies>kernel32.lib;user32.lib;winmm.lib</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\..\libvgm\audio\AudDrv_WinMM.c" />\r
+    <ClCompile Include="..\..\libvgm\audio\AudioStream.c" />\r
+    <ClCompile Include="3812intf.c" />\r
+    <ClCompile Include="fmopl.c" />\r
+    <ClCompile Include="main.c" />\r
+    <ClCompile Include="vgmSndDrv.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClInclude Include="3812intf.h" />\r
+    <ClInclude Include="fmopl.h" />\r
+    <ClInclude Include="mamedef.h" />\r
+    <ClInclude Include="vgmSndDrv.h" />\r
+    <ClInclude Include="stdbool.h" />\r
+    <ClInclude Include="stdtype.h" />\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>
\ No newline at end of file
diff --git a/16/vgmsnd/VgmSndDrv.vcxproj.filters b/16/vgmsnd/VgmSndDrv.vcxproj.filters
new file mode 100644 (file)
index 0000000..7d99f21
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup>\r
+    <Filter Include="Quelldateien">\r
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r
+    </Filter>\r
+    <Filter Include="Headerdateien">\r
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>\r
+    </Filter>\r
+    <Filter Include="Ressourcendateien">\r
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r
+    </Filter>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\..\libvgm\audio\AudDrv_WinMM.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\libvgm\audio\AudioStream.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="main.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="vgmSndDrv.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="3812intf.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="fmopl.c">\r
+      <Filter>Quelldateien</Filter>\r
+    </ClCompile>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClInclude Include="3812intf.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="fmopl.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="mamedef.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="stdbool.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="stdtype.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="vgmSndDrv.h">\r
+      <Filter>Headerdateien</Filter>\r
+    </ClInclude>\r
+  </ItemGroup>\r
+</Project>
\ No newline at end of file
diff --git a/16/vgmsnd/fmopl.c b/16/vgmsnd/fmopl.c
new file mode 100644 (file)
index 0000000..f8545fc
--- /dev/null
@@ -0,0 +1,2755 @@
+/*\r
+**\r
+** File: fmopl.c - software implementation of FM sound generator\r
+**                                            types OPL and OPL2\r
+**\r
+** Copyright Jarek Burczynski (bujar at mame dot net)\r
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development\r
+**\r
+** Version 0.72\r
+**\r
+\r
+Revision History:\r
+\r
+04-08-2003 Jarek Burczynski:\r
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip\r
+   handles memory read/write or during the adpcm synthesis when the chip\r
+   requests another byte of ADPCM data.\r
+\r
+24-07-2003 Jarek Burczynski:\r
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after\r
+   some (unknown) delay). Right now it's always set.\r
+\r
+14-06-2003 Jarek Burczynski:\r
+ - implemented all of the status register flags in Y8950 emulation\r
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since\r
+   they can be either RAM or ROM\r
+\r
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)\r
+ - corrected ym3526_read() to always set bit 2 and bit 1\r
+   to HIGH state - identical to ym3812_read (verified on real YM3526)\r
+\r
+04-28-2002 Jarek Burczynski:\r
+ - binary exact Envelope Generator (verified on real YM3812);\r
+   compared to YM2151: the EG clock is equal to internal_clock,\r
+   rates are 2 times slower and volume resolution is one bit less\r
+ - modified interface functions (they no longer return pointer -\r
+   that's internal to the emulator now):\r
+    - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()\r
+ - corrected 'off by one' error in feedback calculations (when feedback is off)\r
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)\r
+ - speeded up noise generator calculations (Nicola Salmoria)\r
+\r
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)\r
+ Complete rewrite (all verified on real YM3812):\r
+ - corrected sin_tab and tl_tab data\r
+ - corrected operator output calculations\r
+ - corrected waveform_select_enable register;\r
+   simply: ignore all writes to waveform_select register when\r
+   waveform_select_enable == 0 and do not change the waveform previously selected.\r
+ - corrected KSR handling\r
+ - corrected Envelope Generator: attack shape, Sustain mode and\r
+   Percussive/Non-percussive modes handling\r
+ - Envelope Generator rates are two times slower now\r
+ - LFO amplitude (tremolo) and phase modulation (vibrato)\r
+ - rhythm sounds phase generation\r
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)\r
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)\r
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)\r
+\r
+12-28-2001 Acho A. Tang\r
+ - reflected Delta-T EOS status on Y8950 status port.\r
+ - fixed subscription range of attack/decay tables\r
+\r
+\r
+    To do:\r
+        add delay before key off in CSM mode (see CSMKeyControll)\r
+        verify volume of the FM part on the Y8950\r
+*/\r
+\r
+#include <math.h>\r
+#include "mamedef.h"\r
+#ifdef _DEBUG\r
+#include <stdio.h>\r
+#endif\r
+#include <malloc.h>\r
+#include <memory.h>\r
+//#include "sndintrf.h"\r
+#include "fmopl.h"\r
+#if BUILD_Y8950\r
+#include "ymdeltat.h"\r
+#endif\r
+\r
+\r
+#define NULL   ((void *)0)\r
+\r
+\r
+/* output final shift */\r
+#if (OPL_SAMPLE_BITS==16)\r
+       #define FINAL_SH        (0)\r
+       #define MAXOUT          (+32767)\r
+       #define MINOUT          (-32768)\r
+#else\r
+       #define FINAL_SH        (8)\r
+       #define MAXOUT          (+127)\r
+       #define MINOUT          (-128)\r
+#endif\r
+\r
+\r
+#define FREQ_SH                        16  /* 16.16 fixed point (frequency calculations) */\r
+#define EG_SH                  16  /* 16.16 fixed point (EG timing)              */\r
+#define LFO_SH                 24  /*  8.24 fixed point (LFO calculations)       */\r
+#define TIMER_SH               16  /* 16.16 fixed point (timers calculations)    */\r
+\r
+#define FREQ_MASK              ((1<<FREQ_SH)-1)\r
+\r
+/* envelope output entries */\r
+#define ENV_BITS               10\r
+#define ENV_LEN                        (1<<ENV_BITS)\r
+#define ENV_STEP               (128.0/ENV_LEN)\r
+\r
+#define MAX_ATT_INDEX  ((1<<(ENV_BITS-1))-1) /*511*/\r
+#define MIN_ATT_INDEX  (0)\r
+\r
+/* sinwave entries */\r
+#define SIN_BITS               10\r
+#define SIN_LEN                        (1<<SIN_BITS)\r
+#define SIN_MASK               (SIN_LEN-1)\r
+\r
+#define TL_RES_LEN             (256)   /* 8 bits addressing (real chip) */\r
+\r
+\r
+\r
+/* register number to channel number , slot offset */\r
+#define SLOT1 0\r
+#define SLOT2 1\r
+\r
+/* Envelope Generator phases */\r
+\r
+#define EG_ATT                 4\r
+#define EG_DEC                 3\r
+#define EG_SUS                 2\r
+#define EG_REL                 1\r
+#define EG_OFF                 0\r
+\r
+\r
+/* save output as raw 16-bit sample */\r
+\r
+/*#define SAVE_SAMPLE*/\r
+\r
+#ifdef SAVE_SAMPLE\r
+INLINE signed int acc_calc(signed int value)\r
+{\r
+       if (value>=0)\r
+       {\r
+               if (value < 0x0200)\r
+                       return (value & ~0);\r
+               if (value < 0x0400)\r
+                       return (value & ~1);\r
+               if (value < 0x0800)\r
+                       return (value & ~3);\r
+               if (value < 0x1000)\r
+                       return (value & ~7);\r
+               if (value < 0x2000)\r
+                       return (value & ~15);\r
+               if (value < 0x4000)\r
+                       return (value & ~31);\r
+               return (value & ~63);\r
+       }\r
+       /*else value < 0*/\r
+       if (value > -0x0200)\r
+               return (~abs(value) & ~0);\r
+       if (value > -0x0400)\r
+               return (~abs(value) & ~1);\r
+       if (value > -0x0800)\r
+               return (~abs(value) & ~3);\r
+       if (value > -0x1000)\r
+               return (~abs(value) & ~7);\r
+       if (value > -0x2000)\r
+               return (~abs(value) & ~15);\r
+       if (value > -0x4000)\r
+               return (~abs(value) & ~31);\r
+       return (~abs(value) & ~63);\r
+}\r
+\r
+\r
+static FILE *sample[1];\r
+       #if 1   /*save to MONO file */\r
+               #define SAVE_ALL_CHANNELS \\r
+               {       signed int pom = acc_calc(lt); \\r
+                       fputc((unsigned short)pom&0xff,sample[0]); \\r
+                       fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+               }\r
+       #else   /*save to STEREO file */\r
+               #define SAVE_ALL_CHANNELS \\r
+               {       signed int pom = lt; \\r
+                       fputc((unsigned short)pom&0xff,sample[0]); \\r
+                       fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+                       pom = rt; \\r
+                       fputc((unsigned short)pom&0xff,sample[0]); \\r
+                       fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+               }\r
+       #endif\r
+#endif\r
+\r
+//#define LOG_CYM_FILE 0\r
+//static FILE * cymfile = NULL;\r
+\r
+\r
+\r
+#define OPL_TYPE_WAVESEL   0x01  /* waveform select     */\r
+#define OPL_TYPE_ADPCM     0x02  /* DELTA-T ADPCM unit  */\r
+#define OPL_TYPE_KEYBOARD  0x04  /* keyboard interface  */\r
+#define OPL_TYPE_IO        0x08  /* I/O port            */\r
+\r
+/* ---------- Generic interface section ---------- */\r
+#define OPL_TYPE_YM3526 (0)\r
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)\r
+#define OPL_TYPE_Y8950  (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)\r
+\r
+\r
+\r
+typedef struct\r
+{\r
+       UINT32  ar;                     /* attack rate: AR<<2           */\r
+       UINT32  dr;                     /* decay rate:  DR<<2           */\r
+       UINT32  rr;                     /* release rate:RR<<2           */\r
+       UINT8   KSR;            /* key scale rate               */\r
+       UINT8   ksl;            /* keyscale level               */\r
+       UINT8   ksr;            /* key scale rate: kcode>>KSR   */\r
+       UINT8   mul;            /* multiple: mul_tab[ML]        */\r
+\r
+       /* Phase Generator */\r
+       UINT32  Cnt;            /* frequency counter            */\r
+       UINT32  Incr;           /* frequency counter step       */\r
+       UINT8   FB;                     /* feedback shift value         */\r
+       INT32   *connect1;      /* slot1 output pointer         */\r
+       INT32   op1_out[2];     /* slot1 output for feedback    */\r
+       UINT8   CON;            /* connection (algorithm) type  */\r
+\r
+       /* Envelope Generator */\r
+       UINT8   eg_type;        /* percussive/non-percussive mode */\r
+       UINT8   state;          /* phase type                   */\r
+       UINT32  TL;                     /* total level: TL << 2         */\r
+       INT32   TLL;            /* adjusted now TL              */\r
+       INT32   volume;         /* envelope counter             */\r
+       UINT32  sl;                     /* sustain level: sl_tab[SL]    */\r
+       UINT8   eg_sh_ar;       /* (attack state)               */\r
+       UINT8   eg_sel_ar;      /* (attack state)               */\r
+       UINT8   eg_sh_dr;       /* (decay state)                */\r
+       UINT8   eg_sel_dr;      /* (decay state)                */\r
+       UINT8   eg_sh_rr;       /* (release state)              */\r
+       UINT8   eg_sel_rr;      /* (release state)              */\r
+       UINT32  key;            /* 0 = KEY OFF, >0 = KEY ON     */\r
+\r
+       /* LFO */\r
+       UINT32  AMmask;         /* LFO Amplitude Modulation enable mask */\r
+       UINT8   vib;            /* LFO Phase Modulation enable flag (active high)*/\r
+\r
+       /* waveform select */\r
+       UINT16  wavetable;\r
+} OPL_SLOT;\r
+\r
+typedef struct\r
+{\r
+       OPL_SLOT SLOT[2];\r
+       /* phase generator state */\r
+       UINT32  block_fnum;     /* block+fnum                   */\r
+       UINT32  fc;                     /* Freq. Increment base         */\r
+       UINT32  ksl_base;       /* KeyScaleLevel Base step      */\r
+       UINT8   kcode;          /* key code (for key scaling)   */\r
+       UINT8   Muted;\r
+} OPL_CH;\r
+\r
+/* OPL state */\r
+typedef struct fm_opl_f\r
+{\r
+       /* FM channel slots */\r
+       OPL_CH  P_CH[9];                                /* OPL/OPL2 chips have 9 channels*/\r
+       UINT8   MuteSpc[6];                             /* Mute Special: 5 Rhythm + 1 DELTA-T Channel */\r
+\r
+       UINT32  eg_cnt;                                 /* global envelope generator counter    */\r
+       UINT32  eg_timer;                               /* global envelope generator counter works at frequency = chipclock/72 */\r
+       UINT32  eg_timer_add;                   /* step of eg_timer                     */\r
+       UINT32  eg_timer_overflow;              /* envelope generator timer overlfows every 1 sample (on real chip) */\r
+\r
+       UINT8   rhythm;                                 /* Rhythm mode                  */\r
+\r
+       UINT32  fn_tab[1024];                   /* fnumber->increment counter   */\r
+\r
+       /* LFO */\r
+       UINT32  LFO_AM;\r
+       INT32   LFO_PM;\r
+\r
+       UINT8   lfo_am_depth;\r
+       UINT8   lfo_pm_depth_range;\r
+       UINT32  lfo_am_cnt;\r
+       UINT32  lfo_am_inc;\r
+       UINT32  lfo_pm_cnt;\r
+       UINT32  lfo_pm_inc;\r
+\r
+       UINT32  noise_rng;                              /* 23 bit noise shift register  */\r
+       UINT32  noise_p;                                /* current noise 'phase'        */\r
+       UINT32  noise_f;                                /* current noise period         */\r
+\r
+       UINT8   wavesel;                                /* waveform select enable flag  */\r
+\r
+       UINT32  T[2];                                   /* timer counters               */\r
+       UINT32  Trem[2];\r
+       UINT8   st[2];                                  /* timer enable                 */\r
+\r
+#if BUILD_Y8950\r
+       /* Delta-T ADPCM unit (Y8950) */\r
+\r
+       YM_DELTAT *deltat;\r
+\r
+       /* Keyboard and I/O ports interface */\r
+       UINT8   portDirection;\r
+       UINT8   portLatch;\r
+       OPL_PORTHANDLER_R porthandler_r;\r
+       OPL_PORTHANDLER_W porthandler_w;\r
+       void *  port_param;\r
+       OPL_PORTHANDLER_R keyboardhandler_r;\r
+       OPL_PORTHANDLER_W keyboardhandler_w;\r
+       void *  keyboard_param;\r
+#endif\r
+\r
+       /* external event callback handlers */\r
+       OPL_TIMERHANDLER  timer_handler;        /* TIMER handler                */\r
+       void *TimerParam;                                       /* TIMER parameter              */\r
+       OPL_IRQHANDLER    IRQHandler;   /* IRQ handler                  */\r
+       void *IRQParam;                                 /* IRQ parameter                */\r
+       OPL_UPDATEHANDLER UpdateHandler;/* stream update handler        */\r
+       void *UpdateParam;                              /* stream update parameter      */\r
+\r
+       UINT8 type;                                             /* chip type                    */\r
+       UINT8 address;                                  /* address register             */\r
+       UINT8 status;                                   /* status flag                  */\r
+       UINT8 statusmask;                               /* status mask                  */\r
+       UINT8 mode;                                             /* Reg.08 : CSM,notesel,etc.    */\r
+\r
+       UINT32 clock;                                   /* master clock  (Hz)           */\r
+       UINT32 rate;                                    /* sampling rate (Hz)           */\r
+       double freqbase;                                /* frequency base               */\r
+       //attotime TimerBase;                   /* Timer base time (==sampling time)*/\r
+\r
+       signed int phase_modulation;    /* phase modulation input (SLOT 2) */\r
+       signed int output[1];\r
+#if BUILD_Y8950\r
+       INT32 output_deltat[4];         /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */\r
+#endif\r
+} FM_OPL;\r
+\r
+\r
+\r
+/* mapping of register number (offset) to slot number used by the emulator */\r
+static const int slot_array[32]=\r
+{\r
+        0, 2, 4, 1, 3, 5,-1,-1,\r
+        6, 8,10, 7, 9,11,-1,-1,\r
+       12,14,16,13,15,17,-1,-1,\r
+       -1,-1,-1,-1,-1,-1,-1,-1\r
+};\r
+\r
+/* key scale level */\r
+/* table is 3dB/octave , DV converts this into 6dB/octave */\r
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */\r
+#define DV (0.1875/2.0)\r
+static const UINT32 ksl_tab[8*16]=\r
+{\r
+       /* OCT 0 */\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+       /* OCT 1 */\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,\r
+        1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,\r
+       /* OCT 2 */\r
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+        0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,\r
+        3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,\r
+        4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,\r
+       /* OCT 3 */\r
+        0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,\r
+        3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,\r
+        6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,\r
+        7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,\r
+       /* OCT 4 */\r
+        0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,\r
+        6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,\r
+        9.000/DV, 9.750/DV,10.125/DV,10.500/DV,\r
+       10.875/DV,11.250/DV,11.625/DV,12.000/DV,\r
+       /* OCT 5 */\r
+        0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,\r
+        9.000/DV,10.125/DV,10.875/DV,11.625/DV,\r
+       12.000/DV,12.750/DV,13.125/DV,13.500/DV,\r
+       13.875/DV,14.250/DV,14.625/DV,15.000/DV,\r
+       /* OCT 6 */\r
+        0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,\r
+       12.000/DV,13.125/DV,13.875/DV,14.625/DV,\r
+       15.000/DV,15.750/DV,16.125/DV,16.500/DV,\r
+       16.875/DV,17.250/DV,17.625/DV,18.000/DV,\r
+       /* OCT 7 */\r
+        0.000/DV, 9.000/DV,12.000/DV,13.875/DV,\r
+       15.000/DV,16.125/DV,16.875/DV,17.625/DV,\r
+       18.000/DV,18.750/DV,19.125/DV,19.500/DV,\r
+       19.875/DV,20.250/DV,20.625/DV,21.000/DV\r
+};\r
+#undef DV\r
+\r
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */\r
+static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 };\r
+\r
+\r
+/* sustain level table (3dB per step) */\r
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/\r
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )\r
+static const UINT32 sl_tab[16]={\r
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),\r
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)\r
+};\r
+#undef SC\r
+\r
+\r
+#define RATE_STEPS (8)\r
+static const unsigned char eg_inc[15*RATE_STEPS]={\r
+\r
+/*cycle:0 1  2 3  4 5  6 7*/\r
+\r
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */\r
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */\r
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */\r
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */\r
+\r
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */\r
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */\r
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */\r
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */\r
+\r
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */\r
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */\r
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */\r
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */\r
+\r
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */\r
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */\r
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */\r
+};\r
+\r
+\r
+#define O(a) (a*RATE_STEPS)\r
+\r
+/*note that there is no O(13) in this table - it's directly in the code */\r
+static const unsigned char eg_rate_select[16+64+16]={  /* Envelope Generator rates (16 + 64 rates + 16 RKS) */\r
+/* 16 infinite time rates */\r
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),\r
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),\r
+\r
+/* rates 00-12 */\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+\r
+/* rate 13 */\r
+O( 4),O( 5),O( 6),O( 7),\r
+\r
+/* rate 14 */\r
+O( 8),O( 9),O(10),O(11),\r
+\r
+/* rate 15 */\r
+O(12),O(12),O(12),O(12),\r
+\r
+/* 16 dummy rates (same as 15 3) */\r
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),\r
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),\r
+\r
+};\r
+#undef O\r
+\r
+/*rate  0,    1,    2,    3,   4,   5,   6,  7,  8,  9,  10, 11, 12, 13, 14, 15 */\r
+/*shift 12,   11,   10,   9,   8,   7,   6,  5,  4,  3,  2,  1,  0,  0,  0,  0  */\r
+/*mask  4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7,  3,  1,  0,  0,  0,  0  */\r
+\r
+#define O(a) (a*1)\r
+static const unsigned char eg_rate_shift[16+64+16]={   /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */\r
+/* 16 infinite time rates */\r
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),\r
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),\r
+\r
+/* rates 00-12 */\r
+O(12),O(12),O(12),O(12),\r
+O(11),O(11),O(11),O(11),\r
+O(10),O(10),O(10),O(10),\r
+O( 9),O( 9),O( 9),O( 9),\r
+O( 8),O( 8),O( 8),O( 8),\r
+O( 7),O( 7),O( 7),O( 7),\r
+O( 6),O( 6),O( 6),O( 6),\r
+O( 5),O( 5),O( 5),O( 5),\r
+O( 4),O( 4),O( 4),O( 4),\r
+O( 3),O( 3),O( 3),O( 3),\r
+O( 2),O( 2),O( 2),O( 2),\r
+O( 1),O( 1),O( 1),O( 1),\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 13 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 14 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 15 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* 16 dummy rates (same as 15 3) */\r
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),\r
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),\r
+\r
+};\r
+#undef O\r
+\r
+\r
+/* multiple table */\r
+#define ML 2\r
+static const UINT8 mul_tab[16]= {\r
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */\r
+   0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,\r
+   8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML\r
+};\r
+#undef ML\r
+\r
+/*  TL_TAB_LEN is calculated as:\r
+*   12 - sinus amplitude bits     (Y axis)\r
+*   2  - sinus sign bit           (Y axis)\r
+*   TL_RES_LEN - sinus resolution (X axis)\r
+*/\r
+#define TL_TAB_LEN (12*2*TL_RES_LEN)\r
+static signed int tl_tab[TL_TAB_LEN];\r
+\r
+#define ENV_QUIET              (TL_TAB_LEN>>4)\r
+\r
+/* sin waveform table in 'decibel' scale */\r
+/* four waveforms on OPL2 type chips */\r
+static unsigned int sin_tab[SIN_LEN * 4];\r
+\r
+\r
+/* LFO Amplitude Modulation table (verified on real YM3812)\r
+   27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples\r
+\r
+   Length: 210 elements.\r
+\r
+    Each of the elements has to be repeated\r
+    exactly 64 times (on 64 consecutive samples).\r
+    The whole table takes: 64 * 210 = 13440 samples.\r
+\r
+    When AM = 1 data is used directly\r
+    When AM = 0 data is divided by 4 before being used (losing precision is important)\r
+*/\r
+\r
+#define LFO_AM_TAB_ELEMENTS 210\r
+\r
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {\r
+0,0,0,0,0,0,0,\r
+1,1,1,1,\r
+2,2,2,2,\r
+3,3,3,3,\r
+4,4,4,4,\r
+5,5,5,5,\r
+6,6,6,6,\r
+7,7,7,7,\r
+8,8,8,8,\r
+9,9,9,9,\r
+10,10,10,10,\r
+11,11,11,11,\r
+12,12,12,12,\r
+13,13,13,13,\r
+14,14,14,14,\r
+15,15,15,15,\r
+16,16,16,16,\r
+17,17,17,17,\r
+18,18,18,18,\r
+19,19,19,19,\r
+20,20,20,20,\r
+21,21,21,21,\r
+22,22,22,22,\r
+23,23,23,23,\r
+24,24,24,24,\r
+25,25,25,25,\r
+26,26,26,\r
+25,25,25,25,\r
+24,24,24,24,\r
+23,23,23,23,\r
+22,22,22,22,\r
+21,21,21,21,\r
+20,20,20,20,\r
+19,19,19,19,\r
+18,18,18,18,\r
+17,17,17,17,\r
+16,16,16,16,\r
+15,15,15,15,\r
+14,14,14,14,\r
+13,13,13,13,\r
+12,12,12,12,\r
+11,11,11,11,\r
+10,10,10,10,\r
+9,9,9,9,\r
+8,8,8,8,\r
+7,7,7,7,\r
+6,6,6,6,\r
+5,5,5,5,\r
+4,4,4,4,\r
+3,3,3,3,\r
+2,2,2,2,\r
+1,1,1,1\r
+};\r
+\r
+/* LFO Phase Modulation table (verified on real YM3812) */\r
+static const INT8 lfo_pm_table[8*8*2] = {\r
+\r
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */\r
+0, 0, 0, 0, 0, 0, 0, 0,        /*LFO PM depth = 0*/\r
+0, 0, 0, 0, 0, 0, 0, 0,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */\r
+0, 0, 0, 0, 0, 0, 0, 0,        /*LFO PM depth = 0*/\r
+1, 0, 0, 0,-1, 0, 0, 0,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */\r
+1, 0, 0, 0,-1, 0, 0, 0,        /*LFO PM depth = 0*/\r
+2, 1, 0,-1,-2,-1, 0, 1,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */\r
+1, 0, 0, 0,-1, 0, 0, 0,        /*LFO PM depth = 0*/\r
+3, 1, 0,-1,-3,-1, 0, 1,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */\r
+2, 1, 0,-1,-2,-1, 0, 1,        /*LFO PM depth = 0*/\r
+4, 2, 0,-2,-4,-2, 0, 2,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */\r
+2, 1, 0,-1,-2,-1, 0, 1,        /*LFO PM depth = 0*/\r
+5, 2, 0,-2,-5,-2, 0, 2,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */\r
+3, 1, 0,-1,-3,-1, 0, 1,        /*LFO PM depth = 0*/\r
+6, 3, 0,-3,-6,-3, 0, 3,        /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */\r
+3, 1, 0,-1,-3,-1, 0, 1,        /*LFO PM depth = 0*/\r
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/\r
+};\r
+\r
+\r
+/* lock level of common table */\r
+static int num_lock = 0;\r
+\r
+\r
+#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1])\r
+#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2])\r
+#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1])\r
+#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2])\r
+\r
+\r
+\r
+\r
+/*INLINE int limit( int val, int max, int min ) {\r
+       if ( val > max )\r
+               val = max;\r
+       else if ( val < min )\r
+               val = min;\r
+\r
+       return val;\r
+}*/\r
+\r
+\r
+/* status set and IRQ handling */\r
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)\r
+{\r
+       /* set status flag */\r
+       OPL->status |= flag;\r
+       if(!(OPL->status & 0x80))\r
+       {\r
+               if(OPL->status & OPL->statusmask)\r
+               {       /* IRQ on */\r
+                       OPL->status |= 0x80;\r
+                       /* callback user interrupt handler (IRQ is OFF to ON) */\r
+                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);\r
+               }\r
+       }\r
+}\r
+\r
+/* status reset and IRQ handling */\r
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)\r
+{\r
+       /* reset status flag */\r
+       OPL->status &=~flag;\r
+       if((OPL->status & 0x80))\r
+       {\r
+               if (!(OPL->status & OPL->statusmask) )\r
+               {\r
+                       OPL->status &= 0x7f;\r
+                       /* callback user interrupt handler (IRQ is ON to OFF) */\r
+                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);\r
+               }\r
+       }\r
+}\r
+\r
+/* IRQ mask set */\r
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)\r
+{\r
+       OPL->statusmask = flag;\r
+       /* IRQ handling check */\r
+       OPL_STATUS_SET(OPL,0);\r
+       OPL_STATUS_RESET(OPL,0);\r
+}\r
+\r
+\r
+/* advance LFO to next sample */\r
+INLINE void advance_lfo(FM_OPL *OPL)\r
+{\r
+       UINT8 tmp;\r
+\r
+       /* LFO */\r
+       OPL->lfo_am_cnt += OPL->lfo_am_inc;\r
+       if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) )  /* lfo_am_table is 210 elements long */\r
+               OPL->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);\r
+\r
+       tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];\r
+\r
+       if (OPL->lfo_am_depth)\r
+               OPL->LFO_AM = tmp;\r
+       else\r
+               OPL->LFO_AM = tmp>>2;\r
+\r
+       OPL->lfo_pm_cnt += OPL->lfo_pm_inc;\r
+       OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;\r
+}\r
+\r
+INLINE void refresh_eg(FM_OPL* OPL)\r
+{\r
+       OPL_CH *CH;\r
+       OPL_SLOT *op;\r
+       int i;\r
+       int new_vol;\r
+\r
+       for (i=0; i<9*2; i++)\r
+       {\r
+               CH  = &OPL->P_CH[i/2];\r
+               op  = &CH->SLOT[i&1];\r
+\r
+               // Envelope Generator\r
+               switch(op->state)\r
+               {\r
+               case EG_ATT:            // attack phase\r
+                       if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )\r
+                       {\r
+                               new_vol = op->volume + ((~op->volume *\r
+                                                          (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])\r
+                                                         ) >> 3);\r
+                               if (new_vol <= MIN_ATT_INDEX)\r
+                               {\r
+                                       op->volume = MIN_ATT_INDEX;\r
+                                       op->state = EG_DEC;\r
+                               }\r
+                       }\r
+                       break;\r
+               /*case EG_DEC:  // decay phase\r
+                       if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )\r
+                       {\r
+                               new_vol = op->volume + eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];\r
+\r
+                               if ( new_vol >= op->sl )\r
+                                       op->state = EG_SUS;\r
+                       }\r
+                       break;\r
+               case EG_SUS:    // sustain phase\r
+                       if ( !op->eg_type)      percussive mode\r
+                       {\r
+                               new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+                               if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+                               {\r
+                                       if ( new_vol >= MAX_ATT_INDEX )\r
+                                               op->volume = MAX_ATT_INDEX;\r
+                               }\r
+                       }\r
+                       break;\r
+               case EG_REL:    // release phase\r
+                       if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+                       {\r
+                               new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+                               if ( new_vol >= MAX_ATT_INDEX )\r
+                               {\r
+                                       op->volume = MAX_ATT_INDEX;\r
+                                       op->state = EG_OFF;\r
+                               }\r
+\r
+                       }\r
+                       break;\r
+               default:\r
+                       break;*/\r
+               }\r
+       }\r
+       \r
+       return;\r
+}\r
+\r
+/* advance to next sample */\r
+INLINE void advance(FM_OPL *OPL)\r
+{\r
+       OPL_CH *CH;\r
+       OPL_SLOT *op;\r
+       int i;\r
+\r
+       OPL->eg_timer += OPL->eg_timer_add;\r
+\r
+       while (OPL->eg_timer >= OPL->eg_timer_overflow)\r
+       {\r
+               OPL->eg_timer -= OPL->eg_timer_overflow;\r
+\r
+               OPL->eg_cnt++;\r
+\r
+               for (i = 0; i < 2; i ++)\r
+               {\r
+                       if (OPL->st[i])\r
+                       {\r
+                               if (! OPL->Trem[i])\r
+                                       OPLTimerOver(OPL, i);\r
+                               OPL->Trem[i] --;\r
+                       }\r
+               }\r
+\r
+               for (i=0; i<9*2; i++)\r
+               {\r
+                       CH  = &OPL->P_CH[i/2];\r
+                       op  = &CH->SLOT[i&1];\r
+\r
+                       /* Envelope Generator */\r
+                       switch(op->state)\r
+                       {\r
+                       case EG_ATT:            /* attack phase */\r
+                               if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )\r
+                               {\r
+                                       op->volume += (~op->volume *\r
+                                                          (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])\r
+                                                         ) >>3;\r
+\r
+                                       if (op->volume <= MIN_ATT_INDEX)\r
+                                       {\r
+                                               op->volume = MIN_ATT_INDEX;\r
+                                               op->state = EG_DEC;\r
+                                       }\r
+\r
+                               }\r
+                       break;\r
+\r
+                       case EG_DEC:    /* decay phase */\r
+                               if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )\r
+                               {\r
+                                       op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];\r
+\r
+                                       if ( op->volume >= op->sl )\r
+                                               op->state = EG_SUS;\r
+\r
+                               }\r
+                       break;\r
+\r
+                       case EG_SUS:    /* sustain phase */\r
+\r
+                               /* this is important behaviour:\r
+                one can change percusive/non-percussive modes on the fly and\r
+                the chip will remain in sustain phase - verified on real YM3812 */\r
+\r
+                               if(op->eg_type)         /* non-percussive mode */\r
+                               {\r
+                                                                       /* do nothing */\r
+                               }\r
+                               else                            /* percussive mode */\r
+                               {\r
+                                       /* during sustain phase chip adds Release Rate (in percussive mode) */\r
+                                       if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+                                       {\r
+                                               op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+                                               if ( op->volume >= MAX_ATT_INDEX )\r
+                                                       op->volume = MAX_ATT_INDEX;\r
+                                       }\r
+                                       /* else do nothing in sustain phase */\r
+                               }\r
+                       break;\r
+\r
+                       case EG_REL:    /* release phase */\r
+                               if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+                               {\r
+                                       op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+                                       if ( op->volume >= MAX_ATT_INDEX )\r
+                                       {\r
+                                               op->volume = MAX_ATT_INDEX;\r
+                                               op->state = EG_OFF;\r
+                                       }\r
+\r
+                               }\r
+                       break;\r
+\r
+                       default:\r
+                       break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       for (i=0; i<9*2; i++)\r
+       {\r
+               CH  = &OPL->P_CH[i/2];\r
+               op  = &CH->SLOT[i&1];\r
+\r
+               /* Phase Generator */\r
+               if(op->vib)\r
+               {\r
+                       UINT8 block;\r
+                       unsigned int block_fnum = CH->block_fnum;\r
+\r
+                       unsigned int fnum_lfo   = (block_fnum&0x0380) >> 7;\r
+\r
+                       signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];\r
+\r
+                       if (lfo_fn_table_index_offset)  /* LFO phase modulation active */\r
+                       {\r
+                               block_fnum += lfo_fn_table_index_offset;\r
+                               block = (block_fnum&0x1c00) >> 10;\r
+                               op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;\r
+                       }\r
+                       else    /* LFO phase modulation  = zero */\r
+                       {\r
+                               op->Cnt += op->Incr;\r
+                       }\r
+               }\r
+               else    /* LFO phase modulation disabled for this operator */\r
+               {\r
+                       op->Cnt += op->Incr;\r
+               }\r
+       }\r
+\r
+       /*  The Noise Generator of the YM3812 is 23-bit shift register.\r
+    *   Period is equal to 2^23-2 samples.\r
+    *   Register works at sampling frequency of the chip, so output\r
+    *   can change on every sample.\r
+    *\r
+    *   Output of the register and input to the bit 22 is:\r
+    *   bit0 XOR bit14 XOR bit15 XOR bit22\r
+    *\r
+    *   Simply use bit 22 as the noise output.\r
+    */\r
+\r
+       OPL->noise_p += OPL->noise_f;\r
+       i = OPL->noise_p >> FREQ_SH;            /* number of events (shifts of the shift register) */\r
+       OPL->noise_p &= FREQ_MASK;\r
+       while (i)\r
+       {\r
+               /*\r
+        UINT32 j;\r
+        j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;\r
+        OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);\r
+        */\r
+\r
+               /*\r
+            Instead of doing all the logic operations above, we\r
+            use a trick here (and use bit 0 as the noise output).\r
+            The difference is only that the noise bit changes one\r
+            step ahead. This doesn't matter since we don't know\r
+            what is real state of the noise_rng after the reset.\r
+        */\r
+\r
+               if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;\r
+               OPL->noise_rng >>= 1;\r
+\r
+               i--;\r
+       }\r
+}\r
+\r
+\r
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)\r
+{\r
+       UINT32 p;\r
+\r
+       p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];\r
+\r
+       if (p >= TL_TAB_LEN)\r
+               return 0;\r
+       return tl_tab[p];\r
+}\r
+\r
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)\r
+{\r
+       UINT32 p;\r
+\r
+       p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm      )) >> FREQ_SH ) & SIN_MASK) ];\r
+\r
+       if (p >= TL_TAB_LEN)\r
+               return 0;\r
+       return tl_tab[p];\r
+}\r
+\r
+\r
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))\r
+\r
+/* calculate output */\r
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )\r
+{\r
+       OPL_SLOT *SLOT;\r
+       unsigned int env;\r
+       signed int out;\r
+\r
+       if (CH->Muted)\r
+               return;\r
+\r
+       OPL->phase_modulation = 0;\r
+\r
+       /* SLOT 1 */\r
+       SLOT = &CH->SLOT[SLOT1];\r
+       env  = volume_calc(SLOT);\r
+       out  = SLOT->op1_out[0] + SLOT->op1_out[1];\r
+       SLOT->op1_out[0] = SLOT->op1_out[1];\r
+       *SLOT->connect1 += SLOT->op1_out[0];\r
+       SLOT->op1_out[1] = 0;\r
+       if( env < ENV_QUIET )\r
+       {\r
+               if (!SLOT->FB)\r
+                       out = 0;\r
+               SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );\r
+       }\r
+\r
+       /* SLOT 2 */\r
+       SLOT++;\r
+       env = volume_calc(SLOT);\r
+       if( env < ENV_QUIET )\r
+               OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);\r
+}\r
+\r
+/*\r
+    operators used in the rhythm sounds generation process:\r
+\r
+    Envelope Generator:\r
+\r
+channel  operator  register number   Bass  High  Snare Tom  Top\r
+/ slot   number    TL ARDR SLRR Wave Drum  Hat   Drum  Tom  Cymbal\r
+ 6 / 0   12        50  70   90   f0  +\r
+ 6 / 1   15        53  73   93   f3  +\r
+ 7 / 0   13        51  71   91   f1        +\r
+ 7 / 1   16        54  74   94   f4              +\r
+ 8 / 0   14        52  72   92   f2                    +\r
+ 8 / 1   17        55  75   95   f5                          +\r
+\r
+    Phase Generator:\r
+\r
+channel  operator  register number   Bass  High  Snare Tom  Top\r
+/ slot   number    MULTIPLE          Drum  Hat   Drum  Tom  Cymbal\r
+ 6 / 0   12        30                +\r
+ 6 / 1   15        33                +\r
+ 7 / 0   13        31                      +     +           +\r
+ 7 / 1   16        34                -----  n o t  u s e d -----\r
+ 8 / 0   14        32                                  +\r
+ 8 / 1   17        35                      +                 +\r
+\r
+channel  operator  register number   Bass  High  Snare Tom  Top\r
+number   number    BLK/FNUM2 FNUM    Drum  Hat   Drum  Tom  Cymbal\r
+   6     12,15     B6        A6      +\r
+\r
+   7     13,16     B7        A7            +     +           +\r
+\r
+   8     14,17     B8        A8            +           +     +\r
+\r
+*/\r
+\r
+/* calculate rhythm */\r
+\r
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )\r
+{\r
+       OPL_SLOT *SLOT;\r
+       signed int out;\r
+       unsigned int env;\r
+\r
+\r
+       /* Bass Drum (verified on real YM3812):\r
+      - depends on the channel 6 'connect' register:\r
+          when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)\r
+          when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored\r
+      - output sample always is multiplied by 2\r
+    */\r
+\r
+       OPL->phase_modulation = 0;\r
+       /* SLOT 1 */\r
+       SLOT = &CH[6].SLOT[SLOT1];\r
+       env = volume_calc(SLOT);\r
+\r
+       out = SLOT->op1_out[0] + SLOT->op1_out[1];\r
+       SLOT->op1_out[0] = SLOT->op1_out[1];\r
+\r
+       if (!SLOT->CON)\r
+               OPL->phase_modulation = SLOT->op1_out[0];\r
+       /* else ignore output of operator 1 */\r
+\r
+       SLOT->op1_out[1] = 0;\r
+       if( env < ENV_QUIET )\r
+       {\r
+               if (!SLOT->FB)\r
+                       out = 0;\r
+               SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );\r
+       }\r
+\r
+       /* SLOT 2 */\r
+       SLOT++;\r
+       env = volume_calc(SLOT);\r
+       if( env < ENV_QUIET && ! OPL->MuteSpc[0] )\r
+               OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;\r
+\r
+\r
+       /* Phase generation is based on: */\r
+       /* HH  (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */\r
+       /* SD  (16) channel 7->slot 1 */\r
+       /* TOM (14) channel 8->slot 1 */\r
+       /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */\r
+\r
+       /* Envelope generation based on: */\r
+       /* HH  channel 7->slot1 */\r
+       /* SD  channel 7->slot2 */\r
+       /* TOM channel 8->slot1 */\r
+       /* TOP channel 8->slot2 */\r
+\r
+\r
+       /* The following formulas can be well optimized.\r
+       I leave them in direct form for now (in case I've missed something).\r
+    */\r
+\r
+       /* High Hat (verified on real YM3812) */\r
+       env = volume_calc(SLOT7_1);\r
+       if( env < ENV_QUIET && ! OPL->MuteSpc[4] )\r
+       {\r
+\r
+               /* high hat phase generation:\r
+            phase = d0 or 234 (based on frequency only)\r
+            phase = 34 or 2d0 (based on noise)\r
+        */\r
+\r
+               /* base frequency derived from operator 1 in channel 7 */\r
+               unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;\r
+               unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;\r
+               unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;\r
+\r
+               unsigned char res1 = (bit2 ^ bit7) | bit3;\r
+\r
+               /* when res1 = 0 phase = 0x000 | 0xd0; */\r
+               /* when res1 = 1 phase = 0x200 | (0xd0>>2); */\r
+               UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;\r
+\r
+               /* enable gate based on frequency of operator 2 in channel 8 */\r
+               unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;\r
+               unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;\r
+\r
+               unsigned char res2 = (bit3e ^ bit5e);\r
+\r
+               /* when res2 = 0 pass the phase from calculation above (res1); */\r
+               /* when res2 = 1 phase = 0x200 | (0xd0>>2); */\r
+               if (res2)\r
+                       phase = (0x200|(0xd0>>2));\r
+\r
+\r
+               /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */\r
+               /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */\r
+               if (phase&0x200)\r
+               {\r
+                       if (noise)\r
+                               phase = 0x200|0xd0;\r
+               }\r
+               else\r
+               /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */\r
+               /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */\r
+               {\r
+                       if (noise)\r
+                               phase = 0xd0>>2;\r
+               }\r
+\r
+               OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;\r
+       }\r
+\r
+       /* Snare Drum (verified on real YM3812) */\r
+       env = volume_calc(SLOT7_2);\r
+       if( env < ENV_QUIET && ! OPL->MuteSpc[1] )\r
+       {\r
+               /* base frequency derived from operator 1 in channel 7 */\r
+               unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;\r
+\r
+               /* when bit8 = 0 phase = 0x100; */\r
+               /* when bit8 = 1 phase = 0x200; */\r
+               UINT32 phase = bit8 ? 0x200 : 0x100;\r
+\r
+               /* Noise bit XOR'es phase by 0x100 */\r
+               /* when noisebit = 0 pass the phase from calculation above */\r
+               /* when noisebit = 1 phase ^= 0x100; */\r
+               /* in other words: phase ^= (noisebit<<8); */\r
+               if (noise)\r
+                       phase ^= 0x100;\r
+\r
+               OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;\r
+       }\r
+\r
+       /* Tom Tom (verified on real YM3812) */\r
+       env = volume_calc(SLOT8_1);\r
+       if( env < ENV_QUIET && ! OPL->MuteSpc[2] )\r
+               OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;\r
+\r
+       /* Top Cymbal (verified on real YM3812) */\r
+       env = volume_calc(SLOT8_2);\r
+       if( env < ENV_QUIET && ! OPL->MuteSpc[3] )\r
+       {\r
+               /* base frequency derived from operator 1 in channel 7 */\r
+               unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;\r
+               unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;\r
+               unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;\r
+\r
+               unsigned char res1 = (bit2 ^ bit7) | bit3;\r
+\r
+               /* when res1 = 0 phase = 0x000 | 0x100; */\r
+               /* when res1 = 1 phase = 0x200 | 0x100; */\r
+               UINT32 phase = res1 ? 0x300 : 0x100;\r
+\r
+               /* enable gate based on frequency of operator 2 in channel 8 */\r
+               unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;\r
+               unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;\r
+\r
+               unsigned char res2 = (bit3e ^ bit5e);\r
+               /* when res2 = 0 pass the phase from calculation above (res1); */\r
+               /* when res2 = 1 phase = 0x200 | 0x100; */\r
+               if (res2)\r
+                       phase = 0x300;\r
+\r
+               OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;\r
+       }\r
+}\r
+\r
+\r
+/* generic table initialize */\r
+static int init_tables(void)\r
+{\r
+       signed int i,x;\r
+       signed int n;\r
+       double o,m;\r
+\r
+\r
+       for (x=0; x<TL_RES_LEN; x++)\r
+       {\r
+               m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);\r
+               m = floor(m);\r
+\r
+               /* we never reach (1<<16) here due to the (x+1) */\r
+               /* result fits within 16 bits at maximum */\r
+\r
+               n = (int)m;             /* 16 bits here */\r
+               n >>= 4;                /* 12 bits here */\r
+               if (n&1)                /* round to nearest */\r
+                       n = (n>>1)+1;\r
+               else\r
+                       n = n>>1;\r
+                                               /* 11 bits here (rounded) */\r
+               n <<= 1;                /* 12 bits here (as in real chip) */\r
+               tl_tab[ x*2 + 0 ] = n;\r
+               tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];\r
+\r
+               for (i=1; i<12; i++)\r
+               {\r
+                       tl_tab[ x*2+0 + i*2*TL_RES_LEN ] =  tl_tab[ x*2+0 ]>>i;\r
+                       tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];\r
+               }\r
+       #if 0\r
+                       logerror("tl %04i", x*2);\r
+                       for (i=0; i<12; i++)\r
+                               logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );\r
+                       logerror("\n");\r
+       #endif\r
+       }\r
+       /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/\r
+\r
+\r
+       for (i=0; i<SIN_LEN; i++)\r
+       {\r
+               /* non-standard sinus */\r
+               m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */\r
+\r
+               /* we never reach zero here due to ((i*2)+1) */\r
+\r
+               if (m>0.0)\r
+                       o = 8*log(1.0/m)/log(2.0);      /* convert to 'decibels' */\r
+               else\r
+                       o = 8*log(-1.0/m)/log(2.0);     /* convert to 'decibels' */\r
+\r
+               o = o / (ENV_STEP/4);\r
+\r
+               n = (int)(2.0*o);\r
+               if (n&1)                                                /* round to nearest */\r
+                       n = (n>>1)+1;\r
+               else\r
+                       n = n>>1;\r
+\r
+               sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );\r
+\r
+               /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/\r
+       }\r
+\r
+       for (i=0; i<SIN_LEN; i++)\r
+       {\r
+               /* waveform 1:  __      __     */\r
+               /*             /  \____/  \____*/\r
+               /* output only first half of the sinus waveform (positive one) */\r
+\r
+               if (i & (1<<(SIN_BITS-1)) )\r
+                       sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;\r
+               else\r
+                       sin_tab[1*SIN_LEN+i] = sin_tab[i];\r
+\r
+               /* waveform 2:  __  __  __  __ */\r
+               /*             /  \/  \/  \/  \*/\r
+               /* abs(sin) */\r
+\r
+               sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];\r
+\r
+               /* waveform 3:  _   _   _   _  */\r
+               /*             / |_/ |_/ |_/ |_*/\r
+               /* abs(output only first quarter of the sinus waveform) */\r
+\r
+               if (i & (1<<(SIN_BITS-2)) )\r
+                       sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;\r
+               else\r
+                       sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];\r
+\r
+               /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );\r
+        logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );\r
+        logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/\r
+       }\r
+       /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/\r
+\r
+\r
+#ifdef SAVE_SAMPLE\r
+       sample[0]=fopen("sampsum.pcm","wb");\r
+#endif\r
+\r
+       return 1;\r
+}\r
+\r
+static void OPLCloseTable( void )\r
+{\r
+#ifdef SAVE_SAMPLE\r
+       fclose(sample[0]);\r
+#endif\r
+}\r
+\r
+\r
+\r
+static void OPL_initalize(FM_OPL *OPL)\r
+{\r
+       int i;\r
+\r
+       /* frequency base */\r
+       OPL->freqbase  = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate  : 0;\r
+#if 0\r
+       OPL->rate = (double)OPL->clock / 72.0;\r
+       OPL->freqbase  = 1.0;\r
+#endif\r
+\r
+       /*logerror("freqbase=%f\n", OPL->freqbase);*/\r
+\r
+       /* Timer base time */\r
+       //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72);\r
+\r
+       /* make fnumber -> increment counter table */\r
+       for( i=0 ; i < 1024 ; i++ )\r
+       {\r
+               /* opn phase increment counter = 20bit */\r
+               OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */\r
+#if 0\r
+               logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",\r
+                                i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );\r
+#endif\r
+       }\r
+\r
+#if 0\r
+       for( i=0 ; i < 16 ; i++ )\r
+       {\r
+               logerror("FMOPL.C: sl_tab[%i] = %08x\n",\r
+                       i, sl_tab[i] );\r
+       }\r
+       for( i=0 ; i < 8 ; i++ )\r
+       {\r
+               int j;\r
+               logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);\r
+               for (j=0; j<16; j++)\r
+               {\r
+                       logerror("%08x ", ksl_tab[i*16+j] );\r
+               }\r
+               logerror("\n");\r
+       }\r
+#endif\r
+\r
+       for(i = 0; i < 9; i ++)\r
+               OPL->P_CH[i].Muted = 0x00;\r
+       for(i = 0; i < 6; i ++)\r
+               OPL->MuteSpc[i] = 0x00;\r
+\r
+\r
+       /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */\r
+       /* One entry from LFO_AM_TABLE lasts for 64 samples */\r
+       OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase;\r
+\r
+       /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */\r
+       OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase;\r
+\r
+       /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/\r
+\r
+       /* Noise generator: a step takes 1 sample */\r
+       OPL->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase;\r
+\r
+       OPL->eg_timer_add  = (1<<EG_SH)  * OPL->freqbase;\r
+       OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);\r
+       /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/\r
+\r
+}\r
+\r
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)\r
+{\r
+       if( !SLOT->key )\r
+       {\r
+               /* restart Phase Generator */\r
+               SLOT->Cnt = 0;\r
+               /* phase -> Attack */\r
+               SLOT->state = EG_ATT;\r
+       }\r
+       SLOT->key |= key_set;\r
+}\r
+\r
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)\r
+{\r
+       if( SLOT->key )\r
+       {\r
+               SLOT->key &= key_clr;\r
+\r
+               if( !SLOT->key )\r
+               {\r
+                       /* phase -> Release */\r
+                       if (SLOT->state>EG_REL)\r
+                               SLOT->state = EG_REL;\r
+               }\r
+       }\r
+}\r
+\r
+/* update phase increment counter of operator (also update the EG rates if necessary) */\r
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)\r
+{\r
+       int ksr;\r
+\r
+       /* (frequency) phase increment counter */\r
+       SLOT->Incr = CH->fc * SLOT->mul;\r
+       ksr = CH->kcode >> SLOT->KSR;\r
+\r
+       if( SLOT->ksr != ksr )\r
+       {\r
+               SLOT->ksr = ksr;\r
+\r
+               /* calculate envelope generator rates */\r
+               if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+               {\r
+                       SLOT->eg_sh_ar  = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+                       SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+               }\r
+               else\r
+               {\r
+                       SLOT->eg_sh_ar  = 0;\r
+                       SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+               }\r
+               SLOT->eg_sh_dr  = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+               SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+               SLOT->eg_sh_rr  = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+               SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+       }\r
+}\r
+\r
+/* set multi,am,vib,EG-TYP,KSR,mul */\r
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)\r
+{\r
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];\r
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+       SLOT->mul     = mul_tab[v&0x0f];\r
+       SLOT->KSR     = (v&0x10) ? 0 : 2;\r
+       SLOT->eg_type = (v&0x20);\r
+       SLOT->vib     = (v&0x40);\r
+       SLOT->AMmask  = (v&0x80) ? ~0 : 0;\r
+       CALC_FCSLOT(CH,SLOT);\r
+}\r
+\r
+/* set ksl & tl */\r
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)\r
+{\r
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];\r
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+       SLOT->ksl = ksl_shift[v >> 6];\r
+       SLOT->TL  = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */\r
+\r
+       SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);\r
+}\r
+\r
+/* set attack rate & decay rate  */\r
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)\r
+{\r
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];\r
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+       SLOT->ar = (v>>4)  ? 16 + ((v>>4)  <<2) : 0;\r
+\r
+       if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+       {\r
+               SLOT->eg_sh_ar  = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+               SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+       }\r
+       else\r
+       {\r
+               SLOT->eg_sh_ar  = 0;\r
+               SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+       }\r
+\r
+       SLOT->dr    = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;\r
+       SLOT->eg_sh_dr  = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+       SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+}\r
+\r
+/* set sustain level & release rate */\r
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)\r
+{\r
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];\r
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+       SLOT->sl  = sl_tab[ v>>4 ];\r
+\r
+       SLOT->rr  = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;\r
+       SLOT->eg_sh_rr  = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+       SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+}\r
+\r
+\r
+/* write a value v to register r on OPL chip */\r
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)\r
+{\r
+       OPL_CH *CH;\r
+       int slot;\r
+       int block_fnum;\r
+\r
+\r
+       /* adjust bus to 8 bits */\r
+       r &= 0xff;\r
+       v &= 0xff;\r
+\r
+       /*if (LOG_CYM_FILE && (cymfile) && (r!=0) )\r
+       {\r
+               fputc( (unsigned char)r, cymfile );\r
+               fputc( (unsigned char)v, cymfile );\r
+       }*/\r
+\r
+\r
+       switch(r&0xe0)\r
+       {\r
+       case 0x00:      /* 00-1f:control */\r
+               switch(r&0x1f)\r
+               {\r
+               case 0x01:      /* waveform select enable */\r
+                       if(OPL->type&OPL_TYPE_WAVESEL)\r
+                       {\r
+                               OPL->wavesel = v&0x20;\r
+                               /* do not change the waveform previously selected */\r
+                       }\r
+                       break;\r
+               case 0x02:      /* Timer 1 */\r
+                       OPL->T[0] = (256-v)*4;\r
+                       break;\r
+               case 0x03:      /* Timer 2 */\r
+                       OPL->T[1] = (256-v)*16;\r
+                       break;\r
+               case 0x04:      /* IRQ clear / mask and Timer enable */\r
+                       if(v&0x80)\r
+                       {       /* IRQ flag clear */\r
+                               OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */\r
+                       }\r
+                       else\r
+                       {       /* set IRQ mask ,timer enable*/\r
+                               UINT8 st1 = v&1;\r
+                               UINT8 st2 = (v>>1)&1;\r
+\r
+                               /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */\r
+                               OPL_STATUS_RESET(OPL, v & (0x78-0x08) );\r
+                               OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );\r
+\r
+                               /* timer 2 */\r
+                               if(OPL->st[1] != st2)\r
+                               {\r
+                                       //attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero;\r
+                                       OPL->st[1] = st2;\r
+                                       //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);\r
+                               }\r
+                               /* timer 1 */\r
+                               if(OPL->st[0] != st1)\r
+                               {\r
+                                       //attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero;\r
+                                       OPL->st[0] = st1;\r
+                                       //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);\r
+                               }\r
+                       }\r
+                       break;\r
+#if BUILD_Y8950\r
+               case 0x06:              /* Key Board OUT */\r
+                       if(OPL->type&OPL_TYPE_KEYBOARD)\r
+                       {\r
+                               if(OPL->keyboardhandler_w)\r
+                                       OPL->keyboardhandler_w(OPL->keyboard_param,v);\r
+#ifdef _DEBUG\r
+                               else\r
+                                       logerror("Y8950: write unmapped KEYBOARD port\n");\r
+#endif\r
+                       }\r
+                       break;\r
+               case 0x07:      /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */\r
+                       if(OPL->type&OPL_TYPE_ADPCM)\r
+                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);\r
+                       break;\r
+#endif\r
+               case 0x08:      /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */\r
+                       OPL->mode = v;\r
+#if BUILD_Y8950\r
+                       if(OPL->type&OPL_TYPE_ADPCM)\r
+                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */\r
+#endif\r
+                       break;\r
+\r
+#if BUILD_Y8950\r
+               case 0x09:              /* START ADD */\r
+               case 0x0a:\r
+               case 0x0b:              /* STOP ADD  */\r
+               case 0x0c:\r
+               case 0x0d:              /* PRESCALE   */\r
+               case 0x0e:\r
+               case 0x0f:              /* ADPCM data write */\r
+               case 0x10:              /* DELTA-N    */\r
+               case 0x11:              /* DELTA-N    */\r
+               case 0x12:              /* ADPCM volume */\r
+                       if(OPL->type&OPL_TYPE_ADPCM)\r
+                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);\r
+                       break;\r
+\r
+               case 0x15:              /* DAC data high 8 bits (F7,F6...F2) */\r
+               case 0x16:              /* DAC data low 2 bits (F1, F0 in bits 7,6) */\r
+               case 0x17:              /* DAC data shift (S2,S1,S0 in bits 2,1,0) */\r
+#ifdef _DEBUG\r
+                       logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);\r
+#endif\r
+                       break;\r
+\r
+               case 0x18:              /* I/O CTRL (Direction) */\r
+                       if(OPL->type&OPL_TYPE_IO)\r
+                               OPL->portDirection = v&0x0f;\r
+                       break;\r
+               case 0x19:              /* I/O DATA */\r
+                       if(OPL->type&OPL_TYPE_IO)\r
+                       {\r
+                               OPL->portLatch = v;\r
+                               if(OPL->porthandler_w)\r
+                                       OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);\r
+                       }\r
+                       break;\r
+#endif\r
+               default:\r
+#ifdef _DEBUG\r
+                       logerror("FMOPL.C: write to unknown register: %02x\n",r);\r
+#endif\r
+                       break;\r
+               }\r
+               break;\r
+       case 0x20:      /* am ON, vib ON, ksr, eg_type, mul */\r
+               slot = slot_array[r&0x1f];\r
+               if(slot < 0) return;\r
+               set_mul(OPL,slot,v);\r
+               break;\r
+       case 0x40:\r
+               slot = slot_array[r&0x1f];\r
+               if(slot < 0) return;\r
+               set_ksl_tl(OPL,slot,v);\r
+               break;\r
+       case 0x60:\r
+               slot = slot_array[r&0x1f];\r
+               if(slot < 0) return;\r
+               set_ar_dr(OPL,slot,v);\r
+               break;\r
+       case 0x80:\r
+               slot = slot_array[r&0x1f];\r
+               if(slot < 0) return;\r
+               set_sl_rr(OPL,slot,v);\r
+               break;\r
+       case 0xa0:\r
+               if (r == 0xbd)                  /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */\r
+               {\r
+                       OPL->lfo_am_depth = v & 0x80;\r
+                       OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;\r
+\r
+                       OPL->rhythm  = v&0x3f;\r
+\r
+                       if(OPL->rhythm&0x20)\r
+                       {\r
+                               /* BD key on/off */\r
+                               if(v&0x10)\r
+                               {\r
+                                       FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);\r
+                                       FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);\r
+                               }\r
+                               else\r
+                               {\r
+                                       FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);\r
+                                       FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);\r
+                               }\r
+                               /* HH key on/off */\r
+                               if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);\r
+                               else       FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);\r
+                               /* SD key on/off */\r
+                               if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);\r
+                               else       FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);\r
+                               /* TOM key on/off */\r
+                               if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);\r
+                               else       FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);\r
+                               /* TOP-CY key on/off */\r
+                               if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);\r
+                               else       FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);\r
+                       }\r
+                       else\r
+                       {\r
+                               /* BD key off */\r
+                               FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);\r
+                               FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);\r
+                               /* HH key off */\r
+                               FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);\r
+                               /* SD key off */\r
+                               FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);\r
+                               /* TOM key off */\r
+                               FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);\r
+                               /* TOP-CY off */\r
+                               FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);\r
+                       }\r
+                       return;\r
+               }\r
+               /* keyon,block,fnum */\r
+               if( (r&0x0f) > 8) return;\r
+               CH = &OPL->P_CH[r&0x0f];\r
+               if(!(r&0x10))\r
+               {       /* a0-a8 */\r
+                       block_fnum  = (CH->block_fnum&0x1f00) | v;\r
+               }\r
+               else\r
+               {       /* b0-b8 */\r
+                       block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);\r
+\r
+                       if(v&0x20)\r
+                       {\r
+                               FM_KEYON (&CH->SLOT[SLOT1], 1);\r
+                               FM_KEYON (&CH->SLOT[SLOT2], 1);\r
+                       }\r
+                       else\r
+                       {\r
+                               FM_KEYOFF(&CH->SLOT[SLOT1],~1);\r
+                               FM_KEYOFF(&CH->SLOT[SLOT2],~1);\r
+                       }\r
+               }\r
+               /* update */\r
+               if(CH->block_fnum != block_fnum)\r
+               {\r
+                       UINT8 block  = block_fnum >> 10;\r
+\r
+                       CH->block_fnum = block_fnum;\r
+\r
+                       CH->ksl_base = ksl_tab[block_fnum>>6];\r
+                       CH->fc       = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);\r
+\r
+                       /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */\r
+                       CH->kcode    = (CH->block_fnum&0x1c00)>>9;\r
+\r
+                        /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */\r
+                       /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum  */\r
+                       /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */\r
+                       if (OPL->mode&0x40)\r
+                               CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */\r
+                       else\r
+                               CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */\r
+\r
+                       /* refresh Total Level in both SLOTs of this channel */\r
+                       CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);\r
+                       CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);\r
+\r
+                       /* refresh frequency counter in both SLOTs of this channel */\r
+                       CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);\r
+                       CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);\r
+               }\r
+               break;\r
+       case 0xc0:\r
+               /* FB,C */\r
+               if( (r&0x0f) > 8) return;\r
+               CH = &OPL->P_CH[r&0x0f];\r
+               CH->SLOT[SLOT1].FB  = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;\r
+               CH->SLOT[SLOT1].CON = v&1;\r
+               CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;\r
+               break;\r
+       case 0xe0: /* waveform select */\r
+               /* simply ignore write to the waveform select register if selecting not enabled in test register */\r
+               if(OPL->wavesel)\r
+               {\r
+                       slot = slot_array[r&0x1f];\r
+                       if(slot < 0) return;\r
+                       CH = &OPL->P_CH[slot/2];\r
+\r
+                       CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;\r
+               }\r
+               break;\r
+       }\r
+}\r
+\r
+/*static TIMER_CALLBACK( cymfile_callback )\r
+{\r
+       if (cymfile)\r
+       {\r
+               fputc( (unsigned char)0, cymfile );\r
+       }\r
+}*/\r
+\r
+/* lock/unlock for common table */\r
+static int OPL_LockTable(void)\r
+{\r
+       num_lock++;\r
+       if(num_lock>1) return 0;\r
+\r
+       /* first time */\r
+\r
+       /* allocate total level table (128kb space) */\r
+       if( !init_tables() )\r
+       {\r
+               num_lock--;\r
+               return -1;\r
+       }\r
+\r
+       /*if (LOG_CYM_FILE)\r
+       {\r
+               cymfile = fopen("3812_.cym","wb");\r
+               if (cymfile)\r
+                       timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer\r
+               else\r
+                       logerror("Could not create file 3812_.cym\n");\r
+       }*/\r
+\r
+       return 0;\r
+}\r
+\r
+static void OPL_UnLockTable(void)\r
+{\r
+       if(num_lock) num_lock--;\r
+       if(num_lock) return;\r
+\r
+       /* last time */\r
+\r
+       OPLCloseTable();\r
+\r
+       /*if (cymfile)\r
+               fclose (cymfile);\r
+       cymfile = NULL;*/\r
+}\r
+\r
+static void OPLResetChip(FM_OPL *OPL)\r
+{\r
+       int c,s;\r
+       int i;\r
+\r
+       OPL->eg_timer = 0;\r
+       OPL->eg_cnt   = 0;\r
+\r
+       OPL->noise_rng = 1;     /* noise shift register */\r
+       OPL->mode   = 0;        /* normal mode */\r
+       OPL_STATUS_RESET(OPL,0x7f);\r
+\r
+       /* reset with register write */\r
+       OPLWriteReg(OPL,0x01,0); /* wavesel disable */\r
+       OPLWriteReg(OPL,0x02,0); /* Timer1 */\r
+       OPLWriteReg(OPL,0x03,0); /* Timer2 */\r
+       OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */\r
+       OPL->Trem[0] = OPL->Trem[1] = 0;\r
+       for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);\r
+\r
+       /* reset operator parameters */\r
+       for( c = 0 ; c < 9 ; c++ )\r
+       {\r
+               OPL_CH *CH = &OPL->P_CH[c];\r
+               for(s = 0 ; s < 2 ; s++ )\r
+               {\r
+                       /* wave table */\r
+                       CH->SLOT[s].wavetable = 0;\r
+                       CH->SLOT[s].state     = EG_OFF;\r
+                       CH->SLOT[s].volume    = MAX_ATT_INDEX;\r
+               }\r
+       }\r
+#if BUILD_Y8950\r
+       if(OPL->type&OPL_TYPE_ADPCM)\r
+       {\r
+               YM_DELTAT *DELTAT = OPL->deltat;\r
+\r
+               DELTAT->freqbase = OPL->freqbase;\r
+               DELTAT->output_pointer = &OPL->output_deltat[0];\r
+               DELTAT->portshift = 5;\r
+               DELTAT->output_range = 1<<23;\r
+               YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);\r
+       }\r
+#endif\r
+}\r
+\r
+\r
+#if 0\r
+//static STATE_POSTLOAD( OPL_postload )\r
+static void OPL_postload(void* param)\r
+{\r
+       FM_OPL *OPL = (FM_OPL *)param;\r
+       int slot, ch;\r
+\r
+       for( ch=0 ; ch < 9 ; ch++ )\r
+       {\r
+               OPL_CH *CH = &OPL->P_CH[ch];\r
+\r
+               /* Look up key scale level */\r
+               UINT32 block_fnum = CH->block_fnum;\r
+               CH->ksl_base = ksl_tab[block_fnum >> 6];\r
+               CH->fc       = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));\r
+\r
+               for( slot=0 ; slot < 2 ; slot++ )\r
+               {\r
+                       OPL_SLOT *SLOT = &CH->SLOT[slot];\r
+\r
+                       /* Calculate key scale rate */\r
+                       SLOT->ksr = CH->kcode >> SLOT->KSR;\r
+\r
+                       /* Calculate attack, decay and release rates */\r
+                       if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+                       {\r
+                               SLOT->eg_sh_ar  = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+                               SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+                       }\r
+                       else\r
+                       {\r
+                               SLOT->eg_sh_ar  = 0;\r
+                               SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+                       }\r
+                       SLOT->eg_sh_dr  = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+                       SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+                       SLOT->eg_sh_rr  = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+                       SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+\r
+                       /* Calculate phase increment */\r
+                       SLOT->Incr = CH->fc * SLOT->mul;\r
+\r
+                       /* Total level */\r
+                       SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);\r
+\r
+                       /* Connect output */\r
+                       SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;\r
+               }\r
+       }\r
+#if BUILD_Y8950\r
+       if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )\r
+       {\r
+               // We really should call the postlod function for the YM_DELTAT, but it's hard without registers\r
+               // (see the way the YM2610 does it)\r
+               //YM_DELTAT_postload(OPL->deltat, REGS);\r
+       }\r
+#endif\r
+}\r
+\r
+\r
+static void OPLsave_state_channel(OPL_CH *CH)\r
+{\r
+       int slot, ch;\r
+\r
+       for( ch=0 ; ch < 9 ; ch++, CH++ )\r
+       {\r
+               // channel \r
+               state_save_register_device_item(device, ch, CH->block_fnum);\r
+               state_save_register_device_item(device, ch, CH->kcode);\r
+               // slots \r
+               for( slot=0 ; slot < 2 ; slot++ )\r
+               {\r
+                       OPL_SLOT *SLOT = &CH->SLOT[slot];\r
+\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->ar);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->dr);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->rr);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->mul);\r
+\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->FB);\r
+                       state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->CON);\r
+\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->state);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->TL);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->volume);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->sl);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->key);\r
+\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask);\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->vib);\r
+\r
+                       state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable);\r
+               }\r
+       }\r
+}\r
+#endif\r
+\r
+\r
+/* Register savestate for a virtual YM3812/YM3526/Y8950 */\r
+\r
+/*static void OPL_save_state(FM_OPL *OPL)\r
+{\r
+       OPLsave_state_channel(device, OPL->P_CH);\r
+\r
+       state_save_register_device_item(device, 0, OPL->eg_cnt);\r
+       state_save_register_device_item(device, 0, OPL->eg_timer);\r
+\r
+       state_save_register_device_item(device, 0, OPL->rhythm);\r
+\r
+       state_save_register_device_item(device, 0, OPL->lfo_am_depth);\r
+       state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range);\r
+       state_save_register_device_item(device, 0, OPL->lfo_am_cnt);\r
+       state_save_register_device_item(device, 0, OPL->lfo_pm_cnt);\r
+\r
+       state_save_register_device_item(device, 0, OPL->noise_rng);\r
+       state_save_register_device_item(device, 0, OPL->noise_p);\r
+\r
+       if( OPL->type & OPL_TYPE_WAVESEL )\r
+       {\r
+               state_save_register_device_item(device, 0, OPL->wavesel);\r
+       }\r
+\r
+       state_save_register_device_item_array(device, 0, OPL->T);\r
+       state_save_register_device_item_array(device, 0, OPL->st);\r
+\r
+#if BUILD_Y8950\r
+       if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )\r
+       {\r
+               YM_DELTAT_savestate(device, OPL->deltat);\r
+       }\r
+\r
+       if ( OPL->type & OPL_TYPE_IO )\r
+       {\r
+               state_save_register_device_item(device, 0, OPL->portDirection);\r
+               state_save_register_device_item(device, 0, OPL->portLatch);\r
+       }\r
+#endif\r
+\r
+       state_save_register_device_item(device, 0, OPL->address);\r
+       state_save_register_device_item(device, 0, OPL->status);\r
+       state_save_register_device_item(device, 0, OPL->statusmask);\r
+       state_save_register_device_item(device, 0, OPL->mode);\r
+\r
+       state_save_register_postload(device->machine, OPL_postload, OPL);\r
+}*/\r
+\r
+\r
+/* Create one of virtual YM3812/YM3526/Y8950 */\r
+/* 'clock' is chip clock in Hz  */\r
+/* 'rate'  is sampling rate  */\r
+static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type)\r
+{\r
+       char *ptr;\r
+       FM_OPL *OPL;\r
+       int state_size;\r
+\r
+       if (OPL_LockTable() == -1) return NULL;\r
+\r
+       /* calculate OPL state size */\r
+       state_size  = sizeof(FM_OPL);\r
+\r
+#if BUILD_Y8950\r
+       if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);\r
+#endif\r
+\r
+       /* allocate memory block */\r
+       ptr = (char *)malloc(state_size);\r
+\r
+       if (ptr==NULL)\r
+               return NULL;\r
+\r
+       /* clear */\r
+       memset(ptr,0,state_size);\r
+\r
+       OPL  = (FM_OPL *)ptr;\r
+\r
+       ptr += sizeof(FM_OPL);\r
+\r
+#if BUILD_Y8950\r
+       if (type&OPL_TYPE_ADPCM)\r
+       {\r
+               OPL->deltat = (YM_DELTAT *)ptr;\r
+       }\r
+       ptr += sizeof(YM_DELTAT);\r
+#endif\r
+\r
+       OPL->type  = type;\r
+       OPL->clock = clock;\r
+       OPL->rate  = rate;\r
+\r
+       /* init global tables */\r
+       OPL_initalize(OPL);\r
+\r
+       return OPL;\r
+}\r
+\r
+/* Destroy one of virtual YM3812 */\r
+static void OPLDestroy(FM_OPL *OPL)\r
+{\r
+       OPL_UnLockTable();\r
+       free(OPL);\r
+}\r
+\r
+/* Optional handlers */\r
+\r
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)\r
+{\r
+       OPL->timer_handler   = timer_handler;\r
+       OPL->TimerParam = param;\r
+}\r
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+       OPL->IRQHandler     = IRQHandler;\r
+       OPL->IRQParam = param;\r
+}\r
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+       OPL->UpdateHandler = UpdateHandler;\r
+       OPL->UpdateParam = param;\r
+}\r
+\r
+static int OPLWrite(FM_OPL *OPL,int a,int v)\r
+{\r
+       if( !(a&1) )\r
+       {       /* address port */\r
+               OPL->address = v & 0xff;\r
+       }\r
+       else\r
+       {       /* data port */\r
+               if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/);\r
+               OPLWriteReg(OPL,OPL->address,v);\r
+       }\r
+       return OPL->status>>7;\r
+}\r
+\r
+static unsigned char OPLRead(FM_OPL *OPL,int a)\r
+{\r
+       if( !(a&1) )\r
+       {\r
+               /* status port */\r
+\r
+               #if BUILD_Y8950\r
+\r
+               if(OPL->type&OPL_TYPE_ADPCM)    /* Y8950 */\r
+               {\r
+                       return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);\r
+               }\r
+\r
+               #endif\r
+\r
+               /* OPL and OPL2 */\r
+               return OPL->status & (OPL->statusmask|0x80);\r
+       }\r
+\r
+#if BUILD_Y8950\r
+       /* data port */\r
+       switch(OPL->address)\r
+       {\r
+       case 0x05: /* KeyBoard IN */\r
+               if(OPL->type&OPL_TYPE_KEYBOARD)\r
+               {\r
+                       if(OPL->keyboardhandler_r)\r
+                               return OPL->keyboardhandler_r(OPL->keyboard_param);\r
+#ifdef _DEBUG\r
+                       else\r
+                               logerror("Y8950: read unmapped KEYBOARD port\n");\r
+#endif\r
+               }\r
+               return 0;\r
+\r
+       case 0x0f: /* ADPCM-DATA  */\r
+               if(OPL->type&OPL_TYPE_ADPCM)\r
+               {\r
+                       UINT8 val;\r
+\r
+                       val = YM_DELTAT_ADPCM_Read(OPL->deltat);\r
+                       /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/\r
+                       return val;\r
+               }\r
+               return 0;\r
+\r
+       case 0x19: /* I/O DATA    */\r
+               if(OPL->type&OPL_TYPE_IO)\r
+               {\r
+                       if(OPL->porthandler_r)\r
+                               return OPL->porthandler_r(OPL->port_param);\r
+#ifdef _DEBUG\r
+                       else\r
+                               logerror("Y8950:read unmapped I/O port\n");\r
+#endif\r
+               }\r
+               return 0;\r
+       case 0x1a: /* PCM-DATA    */\r
+               if(OPL->type&OPL_TYPE_ADPCM)\r
+               {\r
+#ifdef _DEBUG\r
+                       logerror("Y8950 A/D convertion is accessed but not implemented !\n");\r
+#endif\r
+                       return 0x80; /* 2's complement PCM data - result from A/D convertion */\r
+               }\r
+               return 0;\r
+       }\r
+#endif\r
+\r
+       return 0xff;\r
+}\r
+\r
+/* CSM Key Controll */\r
+INLINE void CSMKeyControll(OPL_CH *CH)\r
+{\r
+       FM_KEYON (&CH->SLOT[SLOT1], 4);\r
+       FM_KEYON (&CH->SLOT[SLOT2], 4);\r
+\r
+       /* The key off should happen exactly one sample later - not implemented correctly yet */\r
+\r
+       FM_KEYOFF(&CH->SLOT[SLOT1], ~4);\r
+       FM_KEYOFF(&CH->SLOT[SLOT2], ~4);\r
+}\r
+\r
+\r
+static int OPLTimerOver(FM_OPL *OPL,int c)\r
+{\r
+       if( c )\r
+       {       /* Timer B */\r
+               OPL_STATUS_SET(OPL,0x20);\r
+       }\r
+       else\r
+       {       /* Timer A */\r
+               OPL_STATUS_SET(OPL,0x40);\r
+               /* CSM mode key,TL controll */\r
+               if( OPL->mode & 0x80 )\r
+               {       /* CSM mode total level latch and auto key on */\r
+                       int ch;\r
+                       if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/);\r
+                       for(ch=0; ch<9; ch++)\r
+                               CSMKeyControll( &OPL->P_CH[ch] );\r
+               }\r
+       }\r
+       /* reload timer */\r
+       OPL->Trem[c] = OPL->T[c];\r
+       //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c]));\r
+       return OPL->status>>7;\r
+}\r
+\r
+\r
+#define MAX_OPL_CHIPS 2\r
+\r
+\r
+#if (BUILD_YM3812)\r
+\r
+void * ym3812_init(UINT32 clock, UINT32 rate)\r
+{\r
+       /* emulator create */\r
+       FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812);\r
+       if (YM3812)\r
+       {\r
+               //OPL_save_state(YM3812);\r
+               ym3812_reset_chip(YM3812);\r
+       }\r
+       return YM3812;\r
+}\r
+\r
+void ym3812_shutdown(void *chip)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       /* emulator shutdown */\r
+       OPLDestroy(YM3812);\r
+}\r
+void ym3812_reset_chip(void *chip)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       OPLResetChip(YM3812);\r
+}\r
+\r
+int ym3812_write(void *chip, int a, int v)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       return OPLWrite(YM3812, a, v);\r
+}\r
+\r
+unsigned char ym3812_read(void *chip, int a)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       /* YM3812 always returns bit2 and bit1 in HIGH state */\r
+       return OPLRead(YM3812, a) | 0x06 ;\r
+}\r
+int ym3812_timer_over(void *chip, int c)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       return OPLTimerOver(YM3812, c);\r
+}\r
+\r
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       OPLSetTimerHandler(YM3812, timer_handler, param);\r
+}\r
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       OPLSetIRQHandler(YM3812, IRQHandler, param);\r
+}\r
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+       FM_OPL *YM3812 = (FM_OPL *)chip;\r
+       OPLSetUpdateHandler(YM3812, UpdateHandler, param);\r
+}\r
+\r
+\r
+/*\r
+** Generate samples for one of the YM3812's\r
+**\r
+** 'which' is the virtual YM3812 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       UINT8           rhythm = OPL->rhythm&0x20;\r
+       OPLSAMPLE       *bufL = buffer[0];\r
+       OPLSAMPLE       *bufR = buffer[1];\r
+       int i;\r
+\r
+       if (! length)\r
+       {\r
+               refresh_eg(OPL);\r
+               return;\r
+       }\r
+       \r
+       for( i=0; i < length ; i++ )\r
+       {\r
+               int lt;\r
+\r
+               OPL->output[0] = 0;\r
+\r
+               advance_lfo(OPL);\r
+\r
+               /* FM part */\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+               if(!rhythm)\r
+               {\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+               }\r
+               else            /* Rhythm part */\r
+               {\r
+                       OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+               }\r
+\r
+               lt = OPL->output[0];\r
+\r
+               lt >>= FINAL_SH;\r
+\r
+               /* limit check */\r
+               //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+               #ifdef SAVE_SAMPLE\r
+               if (which==0)\r
+               {\r
+                       SAVE_ALL_CHANNELS\r
+               }\r
+               #endif\r
+\r
+               /* store to sound buffer */\r
+               bufL[i] = lt;\r
+               bufR[i] = lt;\r
+\r
+               advance(OPL);\r
+       }\r
+\r
+}\r
+#endif /* BUILD_YM3812 */\r
+\r
+\r
+\r
+#if (BUILD_YM3526)\r
+\r
+void *ym3526_init(UINT32 clock, UINT32 rate)\r
+{\r
+       /* emulator create */\r
+       FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526);\r
+       if (YM3526)\r
+       {\r
+               //OPL_save_state(YM3526);\r
+               ym3526_reset_chip(YM3526);\r
+       }\r
+       return YM3526;\r
+}\r
+\r
+void ym3526_shutdown(void *chip)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       /* emulator shutdown */\r
+       OPLDestroy(YM3526);\r
+}\r
+void ym3526_reset_chip(void *chip)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       OPLResetChip(YM3526);\r
+}\r
+\r
+int ym3526_write(void *chip, int a, int v)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       return OPLWrite(YM3526, a, v);\r
+}\r
+\r
+unsigned char ym3526_read(void *chip, int a)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       /* YM3526 always returns bit2 and bit1 in HIGH state */\r
+       return OPLRead(YM3526, a) | 0x06 ;\r
+}\r
+int ym3526_timer_over(void *chip, int c)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       return OPLTimerOver(YM3526, c);\r
+}\r
+\r
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       OPLSetTimerHandler(YM3526, timer_handler, param);\r
+}\r
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       OPLSetIRQHandler(YM3526, IRQHandler, param);\r
+}\r
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+       FM_OPL *YM3526 = (FM_OPL *)chip;\r
+       OPLSetUpdateHandler(YM3526, UpdateHandler, param);\r
+}\r
+\r
+\r
+/*\r
+** Generate samples for one of the YM3526's\r
+**\r
+** 'which' is the virtual YM3526 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       UINT8           rhythm = OPL->rhythm&0x20;\r
+       OPLSAMPLE       *bufL = buffer[0];\r
+       OPLSAMPLE       *bufR = buffer[1];\r
+       int i;\r
+\r
+       for( i=0; i < length ; i++ )\r
+       {\r
+               int lt;\r
+\r
+               OPL->output[0] = 0;\r
+\r
+               advance_lfo(OPL);\r
+\r
+               /* FM part */\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+               if(!rhythm)\r
+               {\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+               }\r
+               else            /* Rhythm part */\r
+               {\r
+                       OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+               }\r
+\r
+               lt = OPL->output[0];\r
+\r
+               lt >>= FINAL_SH;\r
+\r
+               /* limit check */\r
+               //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+               #ifdef SAVE_SAMPLE\r
+               if (which==0)\r
+               {\r
+                       SAVE_ALL_CHANNELS\r
+               }\r
+               #endif\r
+\r
+               /* store to sound buffer */\r
+               bufL[i] = lt;\r
+               bufR[i] = lt;\r
+\r
+               advance(OPL);\r
+       }\r
+\r
+}\r
+#endif /* BUILD_YM3526 */\r
+\r
+\r
+\r
+\r
+#if BUILD_Y8950\r
+\r
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPL_STATUS_SET(Y8950, changebits);\r
+}\r
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPL_STATUS_RESET(Y8950, changebits);\r
+}\r
+\r
+void *y8950_init(UINT32 clock, UINT32 rate)\r
+{\r
+       /* emulator create */\r
+       FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950);\r
+       if (Y8950)\r
+       {\r
+               Y8950->deltat->status_set_handler = Y8950_deltat_status_set;\r
+               Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;\r
+               Y8950->deltat->status_change_which_chip = Y8950;\r
+               Y8950->deltat->status_change_EOS_bit = 0x10;            /* status flag: set bit4 on End Of Sample */\r
+               Y8950->deltat->status_change_BRDY_bit = 0x08;   /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */\r
+\r
+               /*Y8950->deltat->write_time = 10.0 / clock;*/           /* a single byte write takes 10 cycles of main clock */\r
+               /*Y8950->deltat->read_time  = 8.0 / clock;*/            /* a single byte read takes 8 cycles of main clock */\r
+               /* reset */\r
+               //OPL_save_state(Y8950);\r
+               y8950_reset_chip(Y8950);\r
+       }\r
+\r
+       return Y8950;\r
+}\r
+\r
+void y8950_shutdown(void *chip)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       \r
+       free(Y8950->deltat->memory);    Y8950->deltat->memory = NULL;\r
+       \r
+       /* emulator shutdown */\r
+       OPLDestroy(Y8950);\r
+}\r
+void y8950_reset_chip(void *chip)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPLResetChip(Y8950);\r
+}\r
+\r
+int y8950_write(void *chip, int a, int v)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       return OPLWrite(Y8950, a, v);\r
+}\r
+\r
+unsigned char y8950_read(void *chip, int a)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       return OPLRead(Y8950, a);\r
+}\r
+int y8950_timer_over(void *chip, int c)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       return OPLTimerOver(Y8950, c);\r
+}\r
+\r
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPLSetTimerHandler(Y8950, timer_handler, param);\r
+}\r
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPLSetIRQHandler(Y8950, IRQHandler, param);\r
+}\r
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       OPLSetUpdateHandler(Y8950, UpdateHandler, param);\r
+}\r
+\r
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )\r
+{\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);\r
+       OPL->deltat->memory_size = deltat_mem_size;\r
+}\r
+\r
+void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart,\r
+                                                offs_t DataLength, const UINT8* ROMData)\r
+{\r
+       FM_OPL *Y8950 = (FM_OPL *)chip;\r
+       \r
+       if (Y8950->deltat->memory_size != ROMSize)\r
+       {\r
+               Y8950->deltat->memory = (UINT8*)realloc(Y8950->deltat->memory, ROMSize);\r
+               Y8950->deltat->memory_size = ROMSize;\r
+               memset(Y8950->deltat->memory, 0xFF, ROMSize);\r
+               YM_DELTAT_calc_mem_mask(Y8950->deltat);\r
+       }\r
+       if (DataStart > ROMSize)\r
+               return;\r
+       if (DataStart + DataLength > ROMSize)\r
+               DataLength = ROMSize - DataStart;\r
+       \r
+       memcpy(Y8950->deltat->memory + DataStart, ROMData, DataLength);\r
+       \r
+       return;\r
+}\r
+\r
+/*\r
+** Generate samples for one of the Y8950's\r
+**\r
+** 'which' is the virtual Y8950 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+       int i;\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       UINT8           rhythm  = OPL->rhythm&0x20;\r
+       YM_DELTAT       *DELTAT = OPL->deltat;\r
+       OPLSAMPLE       *bufL = buffer[0];\r
+       OPLSAMPLE       *bufR = buffer[1];\r
+\r
+       for( i=0; i < length ; i++ )\r
+       {\r
+               int lt;\r
+\r
+               OPL->output[0] = 0;\r
+               OPL->output_deltat[0] = 0;\r
+\r
+               advance_lfo(OPL);\r
+\r
+               /* deltaT ADPCM */\r
+               if( DELTAT->portstate&0x80 && ! OPL->MuteSpc[5] )\r
+                       YM_DELTAT_ADPCM_CALC(DELTAT);\r
+\r
+               /* FM part */\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+               OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+               if(!rhythm)\r
+               {\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+                       OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+               }\r
+               else            /* Rhythm part */\r
+               {\r
+                       OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+               }\r
+\r
+               lt = OPL->output[0] + (OPL->output_deltat[0]>>11);\r
+\r
+               lt >>= FINAL_SH;\r
+\r
+               /* limit check */\r
+               //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+               #ifdef SAVE_SAMPLE\r
+               if (which==0)\r
+               {\r
+                       SAVE_ALL_CHANNELS\r
+               }\r
+               #endif\r
+\r
+               /* store to sound buffer */\r
+               bufL[i] = lt;\r
+               bufR[i] = lt;\r
+\r
+               advance(OPL);\r
+       }\r
+\r
+}\r
+\r
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)\r
+{\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       OPL->porthandler_w = PortHandler_w;\r
+       OPL->porthandler_r = PortHandler_r;\r
+       OPL->port_param = param;\r
+}\r
+\r
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)\r
+{\r
+       FM_OPL          *OPL = (FM_OPL *)chip;\r
+       OPL->keyboardhandler_w = KeyboardHandler_w;\r
+       OPL->keyboardhandler_r = KeyboardHandler_r;\r
+       OPL->keyboard_param = param;\r
+}\r
+\r
+#endif\r
+\r
+void opl_set_mute_mask(void *chip, UINT32 MuteMask)\r
+{\r
+       FM_OPL *opl = (FM_OPL *)chip;\r
+       UINT8 CurChn;\r
+       \r
+       for (CurChn = 0; CurChn < 9; CurChn ++)\r
+               opl->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;\r
+       for (CurChn = 0; CurChn < 6; CurChn ++)\r
+               opl->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01;\r
+       \r
+       return;\r
+}\r
diff --git a/16/vgmsnd/fmopl.h b/16/vgmsnd/fmopl.h
new file mode 100644 (file)
index 0000000..7a2db3a
--- /dev/null
@@ -0,0 +1,118 @@
+#pragma once\r
+\r
+//#include "attotime.h"\r
+\r
+/* --- select emulation chips --- */\r
+//#define BUILD_YM3812 (HAS_YM3812)\r
+//#define BUILD_YM3526 (HAS_YM3526)\r
+//#define BUILD_Y8950  (HAS_Y8950)\r
+#define BUILD_YM3812 1\r
+#define BUILD_YM3526 0\r
+#define BUILD_Y8950  0\r
+\r
+/* select output bits size of output : 8 or 16 */\r
+#define OPL_SAMPLE_BITS 16\r
+\r
+/* compiler dependence */\r
+/*#ifndef __OSDCOMM_H__\r
+#define __OSDCOMM_H__\r
+typedef unsigned char  UINT8;   // unsigned  8bit\r
+typedef unsigned short UINT16;  // unsigned 16bit\r
+typedef unsigned int   UINT32;  // unsigned 32bit\r
+typedef signed char            INT8;    // signed  8bit\r
+typedef signed short   INT16;   // signed 16bit\r
+typedef signed int             INT32;   // signed 32bit\r
+#endif*/ /* __OSDCOMM_H__ */\r
+\r
+typedef stream_sample_t OPLSAMPLE;\r
+/*\r
+#if (OPL_SAMPLE_BITS==16)\r
+typedef INT16 OPLSAMPLE;\r
+#endif\r
+#if (OPL_SAMPLE_BITS==8)\r
+typedef INT8 OPLSAMPLE;\r
+#endif\r
+*/\r
+\r
+//typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period);\r
+typedef void (*OPL_TIMERHANDLER)(void *param,int timer,int period);\r
+typedef void (*OPL_IRQHANDLER)(void *param,int irq);\r
+typedef void (*OPL_UPDATEHANDLER)(void *param/*,int min_interval_us*/);\r
+typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data);\r
+typedef unsigned char (*OPL_PORTHANDLER_R)(void *param);\r
+\r
+\r
+#if BUILD_YM3812\r
+\r
+void *ym3812_init(UINT32 clock, UINT32 rate);\r
+void ym3812_shutdown(void *chip);\r
+void ym3812_reset_chip(void *chip);\r
+int  ym3812_write(void *chip, int a, int v);\r
+unsigned char ym3812_read(void *chip, int a);\r
+int  ym3812_timer_over(void *chip, int c);\r
+void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length);\r
+\r
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);\r
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);\r
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);\r
+\r
+#endif /* BUILD_YM3812 */\r
+\r
+\r
+#if BUILD_YM3526\r
+\r
+/*\r
+** Initialize YM3526 emulator(s).\r
+**\r
+** 'num' is the number of virtual YM3526's to allocate\r
+** 'clock' is the chip clock in Hz\r
+** 'rate' is sampling rate\r
+*/\r
+void *ym3526_init(UINT32 clock, UINT32 rate);\r
+/* shutdown the YM3526 emulators*/\r
+void ym3526_shutdown(void *chip);\r
+void ym3526_reset_chip(void *chip);\r
+int  ym3526_write(void *chip, int a, int v);\r
+unsigned char ym3526_read(void *chip, int a);\r
+int  ym3526_timer_over(void *chip, int c);\r
+/*\r
+** Generate samples for one of the YM3526's\r
+**\r
+** 'which' is the virtual YM3526 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length);\r
+\r
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);\r
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);\r
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);\r
+\r
+#endif /* BUILD_YM3526 */\r
+\r
+\r
+#if BUILD_Y8950\r
+\r
+/* Y8950 port handlers */\r
+void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param);\r
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param);\r
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );\r
+void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart,\r
+                                                offs_t DataLength, const UINT8* ROMData);\r
+\r
+void * y8950_init(UINT32 clock, UINT32 rate);\r
+void y8950_shutdown(void *chip);\r
+void y8950_reset_chip(void *chip);\r
+int  y8950_write(void *chip, int a, int v);\r
+unsigned char y8950_read (void *chip, int a);\r
+int  y8950_timer_over(void *chip, int c);\r
+void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length);\r
+\r
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);\r
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);\r
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);\r
+\r
+#endif /* BUILD_Y8950 */\r
+\r
+void opl_set_mute_mask(void *chip, UINT32 MuteMask);\r
+\r
diff --git a/16/vgmsnd/main.c b/16/vgmsnd/main.c
new file mode 100644 (file)
index 0000000..78bddc2
--- /dev/null
@@ -0,0 +1,277 @@
+#ifdef _WIN32\r
+#ifdef _DEBUG\r
+#include <crtdbg.h>\r
+#endif\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <ctype.h>     // for toupper()\r
+\r
+\r
+#ifdef _WIN32\r
+int __cdecl _getch(void);      // from conio.h\r
+#else\r
+#define _getch getchar\r
+#endif\r
+\r
+#include <common.h>\r
+#include <audio/AudioStream.h>\r
+#include <audio/AudioStream_SpcDrvFuns.h>\r
+#include "vgmSndDrv.h"\r
+#include "3812intf.h"\r
+\r
+\r
+int main(int argc, char* argv[]);\r
+static UINT32 FillBuffer(void* Params, UINT32 bufSize, void* Data);\r
+void OPL2_Write(UINT8 reg, UINT8 data);\r
+UINT8 OPL2_ReadStatus(void);\r
+void EngineControlCUI(void);\r
+\r
+\r
+#define VGM_SLOTS      4\r
+\r
+static void* audDrv;\r
+static void* audDrvLog;\r
+INT32 CHIP_SAMPLE_RATE;\r
+VGM_FILE vgmFiles[VGM_SLOTS];\r
+\r
+int main(int argc, char* argv[])\r
+{\r
+       UINT8 retVal;\r
+       UINT32 drvCount;\r
+       UINT32 idWavOut;\r
+       UINT32 idWavOutDev;\r
+       AUDDRV_INFO* drvInfo;\r
+       AUDIO_OPTS* opts;\r
+       \r
+       Audio_Init();\r
+       drvCount = Audio_GetDriverCount();\r
+       if (! drvCount)\r
+               goto Exit_Deinit;\r
+       \r
+       idWavOut = 0;\r
+       idWavOutDev = 0;\r
+       Audio_GetDriverInfo(idWavOut, &drvInfo);\r
+       printf("Using driver %s.\n", drvInfo->drvName);\r
+       retVal = AudioDrv_Init(idWavOut, &audDrv);\r
+       if (retVal)\r
+       {\r
+               printf("WaveOut: Drv Init Error: %02X\n", retVal);\r
+               goto Exit_Deinit;\r
+       }\r
+       \r
+       opts = AudioDrv_GetOptions(audDrv);\r
+       opts->numChannels = 1;\r
+       opts->numBitsPerSmpl = 16;\r
+       CHIP_SAMPLE_RATE = opts->sampleRate;\r
+       \r
+       device_start_ym3812(0, 3579545);\r
+       device_reset_ym3812(0);\r
+       InitEngine();\r
+       \r
+       AudioDrv_SetCallback(audDrv, FillBuffer);\r
+       printf("Opening Device %u ...\n", idWavOutDev);\r
+       retVal = AudioDrv_Start(audDrv, idWavOutDev);\r
+       if (retVal)\r
+       {\r
+               printf("Dev Init Error: %02X\n", retVal);\r
+               goto Exit_DrvDeinit;\r
+       }\r
+       \r
+       /*getchar();\r
+       printf("Current Latency: %u ms\n", AudioDrv_GetLatency(audDrv));*/\r
+       EngineControlCUI();\r
+       \r
+       retVal = AudioDrv_Stop(audDrv);\r
+       \r
+Exit_DrvDeinit:\r
+       DeinitEngine();\r
+       device_stop_ym3812(0);\r
+       AudioDrv_Deinit(&audDrv);\r
+Exit_Deinit:\r
+       Audio_Deinit();\r
+       printf("Done.\n");\r
+       \r
+#if _DEBUG\r
+       if (_CrtDumpMemoryLeaks())\r
+               _getch();\r
+#endif\r
+       \r
+       return 0;\r
+}\r
+\r
+INT32 chipSmplL[1] = {0};\r
+INT32 chipSmplR[1] = {0};\r
+INT32* chipSmpls[2] = {chipSmplL, chipSmplR};\r
+static UINT32 smplLastIrq = 0;\r
+static UINT32 FillBuffer(void* Params, UINT32 bufSize, void* data)\r
+{\r
+       UINT32 smplCount;\r
+       INT16* SmplPtr16;\r
+       UINT32 curSmpl;\r
+       INT32 chipSmplsFin;\r
+       \r
+       smplCount = bufSize / 2;\r
+       SmplPtr16 = (INT16*)data;\r
+       for (curSmpl = 0; curSmpl < smplCount; curSmpl ++)\r
+       {\r
+               ym3812_stream_update(0, chipSmpls, 1);\r
+               UpdateSoundEngine();\r
+               smplLastIrq ++;\r
+               \r
+               chipSmplsFin = chipSmplL[0];\r
+               \r
+#if 0\r
+               if ((curSmpl / (smplCount / 16)) < 15)\r
+                       chipSmplsFin += +0x0100;\r
+               else\r
+                       chipSmplsFin += -0x0100;\r
+#endif\r
+               \r
+               if (chipSmplsFin < -0x7FFF)\r
+                       chipSmplsFin = -0x7FFF;\r
+               else if (chipSmplsFin > 0x7FFF)\r
+                       chipSmplsFin = 0x7FFF;\r
+               SmplPtr16[curSmpl] = (INT16)chipSmplsFin;\r
+       }\r
+       return curSmpl * 2;\r
+}\r
+\r
+\r
+void OPL2_Write(UINT8 reg, UINT8 data)\r
+{\r
+       ym3812_w(0, 0, reg);\r
+       ym3812_w(0, 1, data);\r
+       return;\r
+}\r
+\r
+UINT8 OPL2_ReadStatus(void)\r
+{\r
+       return ym3812_r(0, 0);\r
+}\r
+\r
+\r
+\r
+INLINE UINT8 getVgmID(char vgmIdChar)\r
+{\r
+       vgmIdChar = toupper(vgmIdChar);\r
+       if (vgmIdChar == 'M')\r
+               return 0x00;\r
+       else if (vgmIdChar >= '0' && vgmIdChar < '0'+VGM_SLOTS)\r
+               return (UINT8)(vgmIdChar - '0');\r
+       else\r
+               return 0xFF;\r
+}\r
+\r
+INLINE UINT8 getVgmChn(char vgmChnChar)\r
+{\r
+       vgmChnChar = toupper(vgmChnChar);\r
+       if (vgmChnChar == 'M')\r
+               return 0x7F;\r
+       else if (vgmChnChar >= '0' && vgmChnChar <= '9')\r
+               return vgmChnChar - '0';\r
+       else\r
+               return 0xFF;\r
+}\r
+\r
+void EngineControlCUI(void)\r
+{\r
+       VGM_FILE* tempVgmFile;\r
+       UINT8 curSFX;\r
+       char inLine[0x100];\r
+       char* tempStr;\r
+       int tempPos;\r
+       UINT8 vgmId;\r
+       UINT8 vgmChn;\r
+       UINT8 retVal;\r
+       \r
+       printf("Commands:\n");\r
+       printf("Ls File.vgm - Load File.vgm into Slot s\n");\r
+       printf("Pcs - Play Slot s on Channel c\n");\r
+       printf("Sc - Stop Channel c\n");\r
+       printf("S - Stop all SFX\n");\r
+       printf("E - Pause Music\n");\r
+       printf("R - Resume Music\n");\r
+       printf("Channels: M = music, 0-5 = SFX\n");\r
+       printf("Slots: M, 0-3 (M equals slot 0)\n");\r
+       \r
+       for (curSFX = 0; curSFX < VGM_SLOTS; curSFX ++)\r
+               memset(&vgmFiles[curSFX], 0x00, sizeof(VGM_FILE));\r
+       \r
+       while(1)\r
+       {\r
+               tempStr = fgets(inLine, 0x100, stdin);\r
+               if (tempStr == NULL)\r
+                       break;\r
+               tempPos = strlen(inLine);\r
+               if (tempPos <= 1)\r
+                       break;\r
+               inLine[tempPos-1] = '\0';\r
+               \r
+               switch(toupper(inLine[0]))\r
+               {\r
+               case 'L':       // load\r
+                       vgmId = getVgmID(inLine[1]);\r
+                       if (vgmId == 0xFF)\r
+                       {\r
+                               printf("Wrong File Slot!\n");\r
+                               break;\r
+                       }\r
+                       tempVgmFile = &vgmFiles[vgmId];\r
+                       FreeVGMFile(tempVgmFile);\r
+                       retVal = OpenVGMFile(inLine + 3, tempVgmFile);\r
+                       if (retVal > 0)\r
+                               printf("Load Error 0x%02X\n", retVal);\r
+                       else\r
+                               printf("VGM loaded into slot %d.\n", vgmId);\r
+                       break;\r
+               case 'P':       // play\r
+                       vgmChn = getVgmChn(inLine[1]);\r
+                       vgmId = getVgmID(inLine[2]);\r
+                       if (vgmChn == 0xFF)\r
+                       {\r
+                               printf("Wrong Channel!\n");\r
+                               break;\r
+                       }\r
+                       if (vgmId == 0xFF)\r
+                       {\r
+                               printf("Wrong File Slot!\n");\r
+                               break;\r
+                       }\r
+                       tempVgmFile = &vgmFiles[vgmId];\r
+                       if (vgmChn == 0x7F)\r
+                               PlayMusic(tempVgmFile);\r
+                       else\r
+                               PlaySFX(tempVgmFile, vgmChn);\r
+                       break;\r
+               case 'S':       // stop\r
+                       vgmChn = getVgmChn(inLine[1]);\r
+                       if (vgmChn == 0x7F)\r
+                               StopMusic();\r
+                       else\r
+                               StopSFX(vgmChn);\r
+                       break;\r
+               case 'E':       // pause\r
+                       PauseMusic();\r
+                       break;\r
+               case 'R':       // resume\r
+                       ResumeMusic();\r
+                       break;\r
+               default:\r
+                       printf("Unknown Command!\n");\r
+                       break;\r
+               }\r
+       }\r
+       \r
+       StopMusic();\r
+       StopSFX(0xFF);\r
+       for (curSFX = 0; curSFX < VGM_SLOTS; curSFX ++)\r
+               FreeVGMFile(&vgmFiles[curSFX]);\r
+       \r
+       printf("Quit.\n");\r
+       _getch();\r
+       \r
+       return;\r
+}\r
diff --git a/16/vgmsnd/mamedef.h b/16/vgmsnd/mamedef.h
new file mode 100644 (file)
index 0000000..5b8eeaf
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __MAMEDEF_H__\r
+#define __MAMEDEF_H__\r
+\r
+// typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h)\r
+/* 8-bit values */\r
+typedef unsigned char                                          UINT8;\r
+typedef signed char                                            INT8;\r
+\r
+/* 16-bit values */\r
+typedef unsigned short                                         UINT16;\r
+typedef signed short                                           INT16;\r
+\r
+/* 32-bit values */\r
+#ifndef _WINDOWS_H\r
+typedef unsigned int                                           UINT32;\r
+typedef signed int                                                     INT32;\r
+#endif\r
+\r
+/* 64-bit values */\r
+#ifndef _WINDOWS_H\r
+#ifdef _MSC_VER\r
+typedef signed __int64                                         INT64;\r
+typedef unsigned __int64                                       UINT64;\r
+#else\r
+__extension__ typedef unsigned long long       UINT64;\r
+__extension__ typedef signed long long         INT64;\r
+#endif\r
+#endif\r
+\r
+/* offsets and addresses are 32-bit (for now...) */\r
+typedef UINT32 offs_t;\r
+\r
+/* stream_sample_t is used to represent a single sample in a sound stream */\r
+typedef INT32 stream_sample_t;\r
+\r
+#ifdef VGM_BIG_ENDIAN\r
+#define BYTE_XOR_BE(x)  (x)\r
+#else\r
+#define BYTE_XOR_BE(x) ((x) ^ 0x01)\r
+#endif\r
+\r
+#if defined(_MSC_VER)\r
+//#define INLINE       static __forceinline\r
+#define INLINE static __inline\r
+#elif defined(__GNUC__)\r
+#define INLINE static __inline__\r
+#else\r
+#define INLINE static inline\r
+#endif\r
+#define M_PI   3.14159265358979323846\r
+\r
+#ifdef _DEBUG\r
+#define logerror       printf\r
+#else\r
+#define logerror\r
+#endif\r
+\r
+extern stream_sample_t* DUMMYBUF[];\r
+\r
+typedef void (*SRATE_CALLBACK)(void*, UINT32);\r
+\r
+#endif // __MAMEDEF_H__\r
diff --git a/16/vgmsnd/stdbool.h b/16/vgmsnd/stdbool.h
new file mode 100644 (file)
index 0000000..924c031
--- /dev/null
@@ -0,0 +1,15 @@
+// custom stdbool.h to for 1-byte bool types\r
+#ifndef __CST_STDBOOL_H__\r
+#define __CST_STDBOOL_H__\r
+\r
+#ifndef __cplusplus    // C++ already has the bool-type\r
+\r
+// the MS VC++ 6 compiler uses a one-byte-type (unsigned char, to be exact), so I'll reproduce this here\r
+typedef unsigned char  bool;\r
+\r
+#define false  0x00\r
+#define true   0x01\r
+\r
+#endif // ! __cplusplus\r
+\r
+#endif // ! __CST_STDBOOL_H__\r
diff --git a/16/vgmsnd/stdtype.h b/16/vgmsnd/stdtype.h
new file mode 100644 (file)
index 0000000..42dc92e
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef __CST_STDTYPE_H__\r
+#define __CST_STDTYPE_H__\r
+\r
+#ifdef HAVE_STDINT\r
+\r
+#include <stdint.h>\r
+\r
+typedef uint8_t        UINT8;\r
+typedef  int8_t         INT8;\r
+typedef uint16_t       UINT16;\r
+typedef  int16_t        INT16;\r
+typedef uint32_t       UINT32;\r
+typedef  int32_t        INT32;\r
+typedef uint64_t       UINT64;\r
+typedef  int64_t        INT64;\r
+\r
+#else  // ! HAVE_STDINT\r
+\r
+// typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h)\r
+// 8-bit values\r
+typedef unsigned char          UINT8;\r
+typedef   signed char           INT8;\r
+\r
+// 16-bit values\r
+typedef unsigned short         UINT16;\r
+typedef   signed short          INT16;\r
+\r
+// 32-bit values\r
+#ifndef _WINDOWS_H\r
+typedef unsigned int           UINT32;\r
+typedef   signed int            INT32;\r
+\r
+// 64-bit values\r
+#ifdef _MSC_VER\r
+typedef unsigned __int64       UINT64;\r
+typedef   signed __int64        INT64;\r
+#else\r
+__extension__ typedef unsigned long long       UINT64;\r
+__extension__ typedef   signed long long        INT64;\r
+#endif\r
+#endif // _WINDOWS_H\r
+\r
+#endif // HAVE_STDINT\r
+\r
+#endif // __CST_STDTYPE_H__\r
diff --git a/16/vgmsnd/vgmSndDrv.c b/16/vgmsnd/vgmSndDrv.c
new file mode 100644 (file)
index 0000000..dcdb174
--- /dev/null
@@ -0,0 +1,756 @@
+// vgmSndDrv.c - VGM Sound Driver for OPL2\r
+// Valley Bell, 2015-07-27\r
+\r
+// Note: This uses quite a few optimizations that assume that your\r
+//       machine is Little Endian.\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include <common.h>\r
+#include "vgmSndDrv.h"\r
+\r
+\r
+#define QUICK_READ\r
+\r
+\r
+\r
+#define FCC_VGM        0x206D6756      // 'Vgm '\r
+#define FCC_GD3        0x20336447      // 'Gd3 '\r
+\r
+typedef struct _vgm_file_header_base\r
+{\r
+       UINT32 fccVGM;                  // 00\r
+       UINT32 lngEOFOffset;    // 04\r
+       UINT32 lngVersion;              // 08\r
+       UINT32 lngSkip1[2];             // 0C\r
+       UINT32 lngGD3Offset;    // 14\r
+       UINT32 lngTotalSamples; // 18\r
+       UINT32 lngLoopOffset;   // 1C\r
+       UINT32 lngLoopSamples;  // 20\r
+       UINT32 lngRate;                 // 24\r
+       UINT32 lngSkip2[3];             // 28\r
+       UINT32 lngDataOffset;   // 34\r
+       UINT32 lngSkip3[2];             // 38\r
+} VGM_BASE_HDR;\r
+\r
+#define PBMODE_MUSIC   0x00\r
+#define PBMODE_SFX             0x01\r
+typedef struct _vgm_playback\r
+{\r
+       UINT8 pbMode;\r
+       UINT8 vgmEnd;   // 00 - running, 01 - finished, FF - not loaded\r
+       UINT16 curLoopCnt;\r
+       UINT32 vgmPos;\r
+       UINT32 vgmSmplPos;\r
+       UINT32 pbSmplPos;\r
+       VGM_FILE* file;\r
+       \r
+       // oplChnMask:\r
+       //      Music: mask of channels used/overridden by SFX\r
+       //      SFX:   ID of channel used by SFX (all commands are forces to it)\r
+       UINT16 oplChnMask;\r
+       UINT8* oplRegCache;\r
+       UINT8 workRAM[0x04];\r
+} VGM_PBK;\r
+\r
+\r
+\r
+INLINE UINT16 ReadLE16(const UINT8* buffer)\r
+{\r
+#ifdef QUICK_READ\r
+       return *(UINT16*)buffer;\r
+#else\r
+       return (buffer[0x00] << 0) | (buffer[0x01] << 8);\r
+#endif\r
+}\r
+\r
+INLINE UINT32 ReadLE32(const UINT8* buffer)\r
+{\r
+#ifdef QUICK_READ\r
+       return *(UINT32*)buffer;\r
+#else\r
+       return  (buffer[0x00] <<  0) | (buffer[0x01] <<  8) |\r
+                       (buffer[0x02] << 16) | (buffer[0x03] << 24);\r
+#endif\r
+}\r
+\r
+\r
+// Function Prototypes\r
+//UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile);\r
+//void FreeVGMFile(VGM_FILE* vgmFile);\r
+\r
+static bool DoVgmLoop(VGM_PBK* vgmPlay);\r
+static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples);\r
+\r
+//void InitEngine(void);\r
+//void DeinitEngine(void);\r
+\r
+//UINT8 PlayMusic(VGM_FILE* vgmFile);\r
+//UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID);\r
+//UINT8 StopMusic(void);\r
+//UINT8 StopSFX(UINT8 sfxChnID);       // Note: sfxChnID == 0xFF -> stop all SFX\r
+//UINT8 PauseMusic(void);\r
+//UINT8 ResumeMusic(void);\r
+static void StartPlayback(VGM_PBK* vgmPb);\r
+static void StopPlayback(VGM_PBK* vgmPb);\r
+\r
+static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data);\r
+\r
+//void UpdateSoundEngine(void);\r
+\r
+\r
+// Functions that must be supplied by external library\r
+extern void OPL2_Write(UINT8 reg, UINT8 data);\r
+extern UINT8 OPL2_ReadStatus(void);\r
+\r
+\r
+\r
+\r
+#define SFX_CHN_COUNT  6\r
+\r
+#define TIMER1_RATE            7       // 256-7 = 248; (3579545/72/4) / 248 = 50.12 Hz ~ 50 Hz\r
+#define VGM_UPD_RATE   880     // 880 samples (44.1 KHz) = 50.11 Hz\r
+\r
+static VGM_PBK vgmPbMusic;\r
+static VGM_PBK vgmPbSFX[SFX_CHN_COUNT];\r
+\r
+static UINT8 oplRegs_Music[0x100];\r
+static UINT8 oplRegs_SFX[SFX_CHN_COUNT][0x0D]; // 20 23 40 43 60 63 80 83 E0 E3 C0 A0 B0\r
+\r
+static const UINT8 SFX_REGS[0x0D] =\r
+{      0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83,\r
+       0xE0, 0xE3, 0xC0, 0xA0, 0xB0};\r
+static const UINT8 SFX_REGS_REV[0x10] =        // 20/30 -> 0, 40/50 -> 2, ...\r
+{      0xFF, 0xFF, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04,\r
+       0x06, 0x06, 0x0B, 0x0C, 0x0A, 0xFF, 0x08, 0x08};\r
+static const UINT8 CHN_OPMASK[0x09] =\r
+{      0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};\r
+static const UINT8 CHN_OPMASK_REV[0x20] =\r
+{      0x00, 0x01, 0x02, 0x80, 0x81, 0x82, 0xFF, 0xFF,\r
+       0x03, 0x04, 0x05, 0x83, 0x84, 0x85, 0xFF, 0xFF,\r
+       0x06, 0x07, 0x08, 0x86, 0x87, 0x88, 0xFF, 0xFF,\r
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\r
+\r
+\r
+UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile)\r
+{\r
+       size_t hdrSize;\r
+       size_t readEl;  // 'elements' read from file\r
+       size_t bytesToRead;\r
+       VGM_BASE_HDR vgmBaseHdr;\r
+       FILE* hFile;\r
+       UINT32 CurPos;\r
+       \r
+       memset(vgmFile, 0x00, sizeof(VGM_FILE));\r
+       \r
+       hFile = fopen(FileName, "rb");\r
+       if (hFile == NULL)\r
+               return 0xFF;\r
+       \r
+       hdrSize = sizeof(VGM_BASE_HDR);\r
+       readEl = fread(&vgmBaseHdr, hdrSize, 0x01, hFile);\r
+       if (readEl <= 0)\r
+       {\r
+               fclose(hFile);\r
+               return 0xFE;    // read error\r
+       }\r
+       if (vgmBaseHdr.fccVGM != FCC_VGM)\r
+       {\r
+               fclose(hFile);\r
+               return 0x80;    // bad signature\r
+       }\r
+       if (vgmBaseHdr.lngVersion < 0x0150)\r
+       {\r
+               fclose(hFile);\r
+               return 0x81;    // We don't support VGM v1.10 and earlier\r
+       }\r
+       \r
+       vgmFile->dataLen = vgmBaseHdr.lngEOFOffset + 0x04;\r
+       vgmFile->data = (UINT8*)malloc(vgmFile->dataLen);\r
+       if (vgmFile->data == NULL)\r
+       {\r
+               fclose(hFile);\r
+               return 0xF0;    // malloc error\r
+       }\r
+       memcpy(vgmFile->data, &vgmBaseHdr, hdrSize);\r
+       bytesToRead = vgmFile->dataLen - hdrSize;\r
+       readEl = fread(vgmFile->data + hdrSize, 0x01, bytesToRead, hFile);\r
+       if (readEl < bytesToRead)\r
+       {\r
+               //fclose(hFile);\r
+               //return 0xFE;  // read error\r
+               vgmFile->dataLen = hdrSize + readEl;\r
+       }\r
+       \r
+       fclose(hFile);\r
+       \r
+       memcpy(&vgmFile->header, vgmFile->data, sizeof(VGM_HEADER));\r
+       \r
+       // relative -> absolute addresses\r
+       vgmFile->header.lngEOFOffset += 0x04;\r
+       if (vgmFile->header.lngGD3Offset)\r
+               vgmFile->header.lngGD3Offset += 0x14;\r
+       if (vgmFile->header.lngLoopOffset)\r
+               vgmFile->header.lngLoopOffset += 0x1C;\r
+       if (! vgmFile->header.lngDataOffset)\r
+               vgmFile->header.lngDataOffset = 0x0C;\r
+       vgmFile->header.lngDataOffset += 0x34;\r
+       \r
+       CurPos = vgmFile->header.lngDataOffset;\r
+       if (vgmFile->header.lngVersion < 0x0150)\r
+               CurPos = 0x40;\r
+       hdrSize = sizeof(VGM_HEADER);\r
+       if (hdrSize > CurPos)\r
+               memset((UINT8*)&vgmFile->header + CurPos, 0x00, hdrSize - CurPos);\r
+       \r
+       fclose(hFile);\r
+       return 0x00;\r
+}\r
+\r
+void FreeVGMFile(VGM_FILE* vgmFile)\r
+{\r
+       free(vgmFile->data);    vgmFile->data = NULL;\r
+       vgmFile->dataLen = 0;\r
+       \r
+       return;\r
+}\r
+\r
+\r
+static bool DoVgmLoop(VGM_PBK* vgmPlay)\r
+{\r
+       const VGM_HEADER* vgmHdr = &vgmPlay->file->header;\r
+       \r
+       if (! vgmHdr->lngLoopOffset)\r
+               return false;\r
+       \r
+       vgmPlay->curLoopCnt ++;\r
+       \r
+       vgmPlay->vgmPos = vgmHdr->lngLoopOffset;\r
+       vgmPlay->vgmSmplPos -= vgmHdr->lngLoopSamples;\r
+       vgmPlay->pbSmplPos -= vgmHdr->lngLoopSamples;\r
+       \r
+       return true;\r
+}\r
+\r
+static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples)\r
+{\r
+       const UINT32 vgmLen = vgmPlay->file->dataLen;\r
+       const UINT8* vgmData = vgmPlay->file->data;\r
+       const UINT8* VGMPnt;\r
+       UINT32 VGMPos;\r
+       UINT32 VGMSmplPos;\r
+       UINT8 Command;\r
+       UINT8 blockType;\r
+       UINT32 blockLen;\r
+       \r
+       vgmPlay->pbSmplPos += Samples;\r
+       VGMPos = vgmPlay->vgmPos;\r
+       VGMSmplPos = vgmPlay->vgmSmplPos;\r
+       while(VGMSmplPos < vgmPlay->pbSmplPos && ! vgmPlay->vgmEnd)\r
+       {\r
+               VGMPnt = &vgmData[VGMPos];\r
+               Command = VGMPnt[0x00];\r
+               switch(Command & 0xF0)\r
+               {\r
+               case 0x70:      // small delay (1-16 samples)\r
+                       VGMSmplPos += (Command & 0x0F) + 0x01;\r
+                       VGMPos += 0x01;\r
+                       break;\r
+               case 0x80:      // DAC write + small delay (0-15 samples)\r
+                       VGMSmplPos += (Command & 0x0F);\r
+                       VGMPos += 0x01;\r
+                       break;\r
+               case 0x60:\r
+                       switch(Command)\r
+                       {\r
+                       case 0x66:      // End Of File\r
+                               vgmPlay->vgmPos = VGMPos;\r
+                               vgmPlay->vgmSmplPos = VGMSmplPos;\r
+                               if (! DoVgmLoop(vgmPlay))\r
+                                       vgmPlay->vgmEnd = 0x01;\r
+                               VGMPos = vgmPlay->vgmPos;\r
+                               VGMSmplPos = vgmPlay->vgmSmplPos;\r
+                               break;\r
+                       case 0x62:      // 1/60s delay\r
+                               VGMSmplPos += 735;\r
+                               VGMPos += 0x01;\r
+                               break;\r
+                       case 0x63:      // 1/50s delay\r
+                               VGMSmplPos += 882;\r
+                               VGMPos += 0x01;\r
+                               break;\r
+                       case 0x61:      // xx Sample Delay\r
+                               VGMSmplPos += ReadLE16(&VGMPnt[0x01]);\r
+                               VGMPos += 0x03;\r
+                               break;\r
+                       case 0x67:      // Data Block (PCM Data Stream)\r
+                               blockType = VGMPnt[0x02];\r
+                               blockLen = ReadLE32(&VGMPnt[0x03]);\r
+                               blockLen &= 0x7FFFFFFF;\r
+                               VGMPos += 0x07 + blockLen;\r
+                               break;\r
+                       case 0x68:      // PCM RAM write\r
+                               VGMPos += 0x0C;\r
+                               break;\r
+                       default:\r
+                               vgmPlay->vgmEnd = 0x01;\r
+                               break;\r
+                       }\r
+                       break;\r
+               case 0x50:\r
+                       if (Command == 0x50)\r
+                       {\r
+                               VGMPos += 0x02; // SN76496 write\r
+                               break;\r
+                       }\r
+                       switch(Command)\r
+                       {\r
+                       case 0x51:      // YM2413 write\r
+                               ym2413_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+                               break;\r
+                       case 0x5A:      // YM3812 write\r
+                               ym3812_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+                               break;\r
+                       case 0x5B:      // YM3526 write\r
+                       case 0x5C:      // Y8950 write\r
+                               ym3512_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+                               break;\r
+                       case 0x5E:      // YMF262 write, port 0\r
+                       case 0x5F:      // YMF262 write, port 1\r
+                               ymf262_write(vgmPlay, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]);\r
+                               break;\r
+                       }\r
+                       VGMPos += 0x03;\r
+                       break;\r
+               case 0x30:\r
+                       VGMPos += 0x02;\r
+                       break;\r
+               case 0x40:\r
+               case 0xA0:\r
+               case 0xB0:\r
+                       VGMPos += 0x03;\r
+                       break;\r
+               case 0xC0:\r
+               case 0xD0:\r
+                       VGMPos += 0x04;\r
+                       break;\r
+               case 0xE0:\r
+               case 0xF0:\r
+                       VGMPos += 0x05;\r
+                       break;\r
+               case 0x90:\r
+                       switch(Command)\r
+                       {\r
+                       case 0x90:      // DAC Ctrl: Setup Chip\r
+                               VGMPos += 0x05;\r
+                               break;\r
+                       case 0x91:      // DAC Ctrl: Set Data\r
+                               VGMPos += 0x05;\r
+                               break;\r
+                       case 0x92:      // DAC Ctrl: Set Freq\r
+                               VGMPos += 0x06;\r
+                               break;\r
+                       case 0x93:      // DAC Ctrl: Play from Start Pos\r
+                               VGMPos += 0x0B;\r
+                               break;\r
+                       case 0x94:      // DAC Ctrl: Stop immediately\r
+                               VGMPos += 0x02;\r
+                               break;\r
+                       case 0x95:      // DAC Ctrl: Play Block (small)\r
+                               VGMPos += 0x05;\r
+                               break;\r
+                       default:\r
+                               vgmPlay->vgmEnd = 0x01;\r
+                               break;\r
+                       }\r
+                       break;\r
+               default:\r
+                       vgmPlay->vgmEnd = 0x01;\r
+                       return;\r
+               }\r
+               \r
+               if (VGMPos >= vgmLen)\r
+                       vgmPlay->vgmEnd = 0x01;\r
+       }\r
+       vgmPlay->vgmPos = VGMPos;\r
+       vgmPlay->vgmSmplPos = VGMSmplPos;\r
+       if (vgmPlay->vgmEnd)\r
+               StopPlayback(vgmPlay);\r
+       \r
+       return;\r
+}\r
+\r
+\r
+\r
+\r
+void InitEngine(void)\r
+{\r
+       UINT8 curSFX;\r
+       UINT8 curReg;\r
+       \r
+       memset(oplRegs_Music, 0x00, 0x100);\r
+       memset(&vgmPbMusic, 0x00, sizeof(VGM_PBK));\r
+       vgmPbMusic.pbMode = PBMODE_MUSIC;\r
+       vgmPbMusic.vgmEnd = 0xFF;\r
+       vgmPbMusic.oplChnMask = 0x0000;\r
+       vgmPbMusic.oplRegCache = oplRegs_Music;\r
+       \r
+       for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+       {\r
+               memset(&oplRegs_SFX[curSFX], 0x00, sizeof(VGM_PBK));\r
+               memset(&vgmPbSFX[curSFX], 0x00, sizeof(VGM_PBK));\r
+               vgmPbSFX[curSFX].pbMode = PBMODE_SFX;\r
+               vgmPbSFX[curSFX].vgmEnd = 0xFF;\r
+               vgmPbSFX[curSFX].oplChnMask = curSFX;\r
+               vgmPbSFX[curSFX].oplRegCache = oplRegs_SFX[curSFX];\r
+       }\r
+       \r
+       // reset OPL2 chip\r
+       curReg = 0x00;\r
+       do\r
+       {\r
+               curReg --;\r
+               OPL2_Write(curReg, 0x00);\r
+       } while(curReg > 0x20);\r
+       \r
+       OPL2_Write(0x02, TIMER1_RATE);  // set Timer 1 Period\r
+       OPL2_Write(0x04, 0x01); // Timer 1 on/unmasked, Timer 2 off\r
+       OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags\r
+       \r
+       OPL2_Write(0x01, 0x20); // Waveform Select: Enable\r
+       \r
+       return;\r
+}\r
+\r
+void DeinitEngine(void)\r
+{\r
+       UINT8 curSFX;\r
+       \r
+       StopPlayback(&vgmPbMusic);\r
+       for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+               StopPlayback(&vgmPbSFX[curSFX]);\r
+       \r
+       OPL2_Write(0x04, 0x00); // disable all timers\r
+       \r
+       return;\r
+}\r
+\r
+\r
+UINT8 PlayMusic(VGM_FILE* vgmFile)\r
+{\r
+       VGM_PBK* vgmPb = &vgmPbMusic;\r
+       \r
+       if (! vgmPb->vgmEnd)\r
+               StopPlayback(vgmPb);\r
+       \r
+       vgmPb->file = vgmFile;\r
+       \r
+       StartPlayback(vgmPb);\r
+       \r
+       return 0x00;\r
+}\r
+\r
+UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID)\r
+{\r
+       VGM_PBK* vgmPb;\r
+       \r
+       if (sfxChnID >= SFX_CHN_COUNT)\r
+               return 0xFF;\r
+       \r
+       vgmPb = &vgmPbSFX[sfxChnID];\r
+       \r
+       if (! vgmPb->vgmEnd)\r
+               StopPlayback(vgmPb);\r
+       \r
+       vgmPb->file = vgmFile;\r
+       \r
+       StartPlayback(vgmPb);\r
+       \r
+       return 0x00;\r
+}\r
+\r
+UINT8 StopMusic(void)\r
+{\r
+       StopPlayback(&vgmPbMusic);\r
+       return 0x00;\r
+}\r
+\r
+UINT8 StopSFX(UINT8 sfxChnID)\r
+{\r
+       if (sfxChnID == 0xFF)\r
+       {\r
+               for (sfxChnID = 0; sfxChnID < SFX_CHN_COUNT; sfxChnID ++)\r
+                       StopPlayback(&vgmPbSFX[sfxChnID]);\r
+               return 0x00;\r
+       }\r
+       \r
+       if (sfxChnID >= SFX_CHN_COUNT)\r
+               return 0xFF;\r
+       \r
+       StopPlayback(&vgmPbSFX[sfxChnID]);\r
+       return 0x00;\r
+}\r
+\r
+UINT8 PauseMusic(void)\r
+{\r
+       if (vgmPbMusic.vgmEnd == 0xFF)\r
+               return 0xFF;    // not playing\r
+       if (vgmPbMusic.vgmEnd == 0x01)\r
+               return 0x80;    // finished playing already\r
+       if (vgmPbMusic.vgmEnd == 0x02)\r
+               return 0x01;    // is already paused\r
+       \r
+       StopPlayback(&vgmPbMusic);\r
+       vgmPbMusic.vgmEnd = 0x02;\r
+       \r
+       return 0x00;\r
+}\r
+\r
+UINT8 ResumeMusic(void)\r
+{\r
+       if (vgmPbMusic.vgmEnd == 0xFF)\r
+               return 0xFF;    // not playing\r
+       if (vgmPbMusic.vgmEnd == 0x01)\r
+               return 0x80;    // finished playing already\r
+       if (! (vgmPbMusic.vgmEnd & 0x02))\r
+               return 0x01;    // is not paused\r
+       \r
+       vgmPbMusic.vgmEnd &= ~0x02;\r
+       \r
+       return 0x00;\r
+}\r
+\r
+static void StartPlayback(VGM_PBK* vgmPb)\r
+{\r
+       if (vgmPb->file == NULL || vgmPb->file->data == NULL ||\r
+               vgmPb->file->header.fccVGM != FCC_VGM)\r
+       {\r
+               vgmPb->vgmEnd = 0xFF;\r
+               return;\r
+       }\r
+       \r
+       vgmPb->vgmEnd = 0x00;   // set to 'running'\r
+       vgmPb->vgmPos = vgmPb->file->header.lngDataOffset;\r
+       vgmPb->vgmSmplPos = 0;\r
+       vgmPb->pbSmplPos = 0;\r
+       vgmPb->curLoopCnt = 0;\r
+       memset(vgmPb->workRAM, 0x00, 0x04);\r
+       \r
+       if (vgmPb->pbMode == PBMODE_SFX)\r
+       {\r
+               UINT8 curReg;\r
+               \r
+               curReg = 0xB0 | vgmPb->oplChnMask;\r
+               if (oplRegs_Music[curReg] & 0x20)\r
+                       OPL2_Write(curReg, oplRegs_Music[curReg] & ~0x20);      // send Key Off\r
+               \r
+               vgmPbMusic.oplChnMask |= (1 << vgmPb->oplChnMask);      // mask out music channel\r
+       }\r
+       \r
+       return;\r
+}\r
+\r
+static void StopPlayback(VGM_PBK* vgmPb)\r
+{\r
+       if (vgmPb->vgmEnd & 0x80)\r
+               return;\r
+       \r
+       if (vgmPb->pbMode == PBMODE_MUSIC)\r
+       {\r
+               UINT8 curReg;\r
+               UINT16 chnMask;\r
+               \r
+               chnMask = 0x0001;\r
+               for (curReg = 0xB0; curReg < 0xB9; curReg ++, chnMask <<= 1)\r
+               {\r
+                       if (vgmPb->oplChnMask & chnMask)\r
+                               continue;       // keep channels used by SFX untouched\r
+                       if (vgmPb->oplRegCache[curReg] & 0x20)\r
+                       {\r
+                               vgmPb->oplRegCache[curReg] &= ~0x20;\r
+                               OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off\r
+                       }\r
+               }\r
+               curReg = 0xBD;  // rhythm register\r
+               if (vgmPb->oplRegCache[curReg] & 0x1F)\r
+               {\r
+                       vgmPb->oplRegCache[curReg] &= ~0x1F;\r
+                       OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off\r
+               }\r
+               \r
+               vgmPb->vgmEnd = 0x01;\r
+       }\r
+       else //if (vgmPb->pbMode == PBMODE_SFX)\r
+       {\r
+               UINT8 regID;\r
+               UINT8 curReg;\r
+               UINT8 opMask;\r
+               \r
+               curReg = 0xB0 | vgmPb->oplChnMask;\r
+               if (vgmPb->oplRegCache[0x0C] & 0x20)\r
+               {\r
+                       vgmPb->oplRegCache[0x0C] &= ~0x20;\r
+                       OPL2_Write(curReg, vgmPb->oplRegCache[0x0C]);   // send Key Off\r
+               }\r
+               \r
+               vgmPb->vgmEnd = 0x01;\r
+               \r
+               if (! vgmPbMusic.vgmEnd)        // if (music is playing)\r
+               {\r
+                       opMask = CHN_OPMASK[vgmPb->oplChnMask];\r
+                       for (regID = 0x00; regID < 0x0A; regID ++)\r
+                       {\r
+                               curReg = SFX_REGS[regID] + opMask;\r
+                               OPL2_Write(curReg, oplRegs_Music[curReg]);      // restore Music register\r
+                       }\r
+                       for (; regID < 0x0D; regID ++)\r
+                       {\r
+                               curReg = SFX_REGS[regID] | vgmPb->oplChnMask;\r
+                               OPL2_Write(curReg, oplRegs_Music[curReg]);      // restore Music register\r
+                       }\r
+                       \r
+                       vgmPbMusic.oplChnMask &= ~(1 << vgmPb->oplChnMask);\r
+               }\r
+       }\r
+       \r
+       return;\r
+}\r
+\r
+\r
+\r
+static void OPL_CachedWrite(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+       UINT8 regChn;\r
+       UINT8 ramOfs;\r
+       \r
+       if (vgmPb->pbMode == PBMODE_MUSIC)\r
+       {\r
+               if (reg == 0x01)\r
+                       data |= 0x20;   // enforce "Waveform Select Enable" bit\r
+               vgmPb->oplRegCache[reg] = data;\r
+               \r
+               ramOfs = SFX_REGS_REV[reg >> 4];\r
+               if (ramOfs < 0x0A)      // Operator 20/40/60/80/E0\r
+               {\r
+                       regChn = CHN_OPMASK_REV[reg & 0x1F] & 0x7F;\r
+                       if (vgmPb->oplChnMask & (1 << regChn))\r
+                               return; // channel overridden by SFX - return\r
+               }\r
+               else if (ramOfs < 0x0D) // Operator C0/A0/B0\r
+               {\r
+                       regChn = reg & 0x0F;\r
+                       if (vgmPb->oplChnMask & (1 << regChn))\r
+                               return; // channel overridden by SFX - return\r
+               }\r
+       }\r
+       else //if (vgmPb->pbMode == PBMODE_SFX)\r
+       {\r
+               if (reg == 0xBD)\r
+                       return; // no rhythm register for SFX\r
+               \r
+               ramOfs = SFX_REGS_REV[reg >> 4];\r
+               if (ramOfs == 0xFF)\r
+                       return;\r
+               \r
+               if (ramOfs < 0x0A)      // Operator 20/40/60/80/E0\r
+               {\r
+                       regChn = CHN_OPMASK_REV[reg & 0x1F];\r
+                       if (regChn == 0xFF)\r
+                               return; // ignore writes to invalid channels/operators\r
+                       ramOfs += (regChn & 0x80) >> 7;\r
+                       regChn &= 0x7F;\r
+                       vgmPb->oplRegCache[ramOfs] = data;\r
+                       \r
+                       if (regChn != vgmPb->oplChnMask)\r
+                       {\r
+                               // force command to current channel\r
+                               reg = SFX_REGS[ramOfs] + CHN_OPMASK[vgmPb->oplChnMask];\r
+                       }\r
+               }\r
+               else    // Operator C0/A0/B0\r
+               {\r
+                       regChn = CHN_OPMASK_REV[reg & 0x0F];\r
+                       if (regChn >= 0x09)\r
+                               return; // ignore writes to invalid channels\r
+                       vgmPb->oplRegCache[ramOfs] = data;\r
+                       \r
+                       reg = (reg & 0xF0) | vgmPb->oplChnMask;\r
+               }\r
+       }\r
+       \r
+       OPL2_Write(reg, data);\r
+       return;\r
+}\r
+\r
+\r
+static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+       return; // unsupported for now\r
+}\r
+\r
+static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+       if (reg == 0x01)\r
+       {\r
+               vgmPb->workRAM[0x00] = data & 0x20;     // store "Wave Select Enable" bit\r
+       }\r
+       else if ((reg & 0xE0) == 0xE0)\r
+       {\r
+               if (! vgmPb->workRAM[0x00])     // "Wave Select Enable" off?\r
+                       data = 0x00;    // disable waveforms\r
+       }\r
+       \r
+       OPL_CachedWrite(vgmPb, reg, data);\r
+       return;\r
+}\r
+\r
+static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+       if ((reg & 0xE0) == 0xE0)\r
+       {\r
+               data = 0x00;    // OPL1 has no waveforms\r
+       }\r
+       if (reg >= 0x07 && reg < 0x20)\r
+       {\r
+               if (reg == 0x08)\r
+                       data &= ~0x0F;  // mask out Y8950 DeltaT data\r
+               else\r
+                       return; // ignore Y8950 DeltaT writes\r
+       }\r
+       \r
+       OPL_CachedWrite(vgmPb, reg, data);\r
+       return;\r
+}\r
+\r
+static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data)\r
+{\r
+       return; // unsupported for now\r
+}\r
+\r
+\r
+\r
+void UpdateSoundEngine(void)\r
+{\r
+       UINT8 tmrMask;\r
+       UINT8 curSFX;\r
+       \r
+       tmrMask = OPL2_ReadStatus();\r
+       if (! (tmrMask & 0x40))\r
+               return; // wait for overflow\r
+       OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags\r
+       \r
+       if (! vgmPbMusic.vgmEnd)\r
+               UpdateVGM(&vgmPbMusic, VGM_UPD_RATE);\r
+       for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+       {\r
+               if (! vgmPbSFX[curSFX].vgmEnd)\r
+                       UpdateVGM(&vgmPbSFX[curSFX], VGM_UPD_RATE);\r
+       }\r
+       \r
+       return;\r
+}\r
diff --git a/16/vgmsnd/vgmSndDrv.h b/16/vgmsnd/vgmSndDrv.h
new file mode 100644 (file)
index 0000000..b3a38f0
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __VGMSNDDRV_H__\r
+#define __VGMSNDDRV_H__\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif\r
+\r
+#include <stdtype.h>\r
+\r
+typedef struct _vgm_file_header\r
+{\r
+       UINT32 fccVGM;\r
+       UINT32 lngEOFOffset;\r
+       UINT32 lngVersion;\r
+       UINT32 lngHzPSG;\r
+       UINT32 lngHzYM2413;\r
+       UINT32 lngGD3Offset;\r
+       UINT32 lngTotalSamples;\r
+       UINT32 lngLoopOffset;\r
+       UINT32 lngLoopSamples;\r
+       UINT32 lngRate;\r
+       UINT8 bytPSGCfg[4];\r
+       UINT32 lngHzYM2612;\r
+       UINT32 lngHzYM2151;\r
+       UINT32 lngDataOffset;\r
+       UINT32 lngHzSPCM;\r
+       UINT32 lngSPCMIntf;\r
+       UINT32 lngHzRF5C68;\r
+       UINT32 lngHzYM2203;\r
+       UINT32 lngHzYM2608;\r
+       UINT32 lngHzYM2610;\r
+       UINT32 lngHzYM3812;\r
+       UINT32 lngHzYM3526;\r
+       UINT32 lngHzY8950;\r
+       UINT32 lngHzYMF262;\r
+       UINT32 lngHzYMF278B;\r
+       UINT32 lngHzYMF271;\r
+       UINT32 lngHzYMZ280B;\r
+       UINT32 lngHzRF5C164;\r
+       UINT32 lngHzPWM;\r
+       UINT32 lngHzAY8910;\r
+       UINT8 bytAYCfg[4];\r
+       UINT8 bytVolumeModifier;\r
+       UINT8 bytReserved2;\r
+       INT8 bytLoopBase;\r
+       UINT8 bytLoopModifier;\r
+} VGM_HEADER;\r
+\r
+typedef struct _vgm_file\r
+{\r
+       UINT32 dataLen;\r
+       UINT8* data;\r
+       VGM_HEADER header;\r
+} VGM_FILE;\r
+\r
+\r
+UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile);\r
+void FreeVGMFile(VGM_FILE* vgmFile);\r
+\r
+void InitEngine(void);\r
+void DeinitEngine(void);\r
+\r
+UINT8 PlayMusic(VGM_FILE* vgmFile);\r
+UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID);\r
+UINT8 StopMusic(void);\r
+UINT8 StopSFX(UINT8 sfxChnID);\r
+UINT8 PauseMusic(void);\r
+UINT8 ResumeMusic(void);\r
+\r
+void UpdateSoundEngine(void);\r
+\r
+\r
+// Functions that must be supplied by external library:\r
+//extern void OPL2_Write(UINT8 reg, UINT8 data);\r
+//extern UINT8 OPL2_ReadStatus(void);\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif // ! __VGMSNDDRV_H__\r