diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 19804b7352..546658b4e9 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -554,7 +554,7 @@ namespace BizHawk.Client.Common switch (game.System) { case "GEN": - var genesis = new GPGX(nextComm, null, disc, GetCoreSettings(), GetCoreSyncSettings()); + var genesis = new GPGX(nextComm, null, new[] { disc }, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = genesis; break; case "SAT": diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs index ed54dfcee5..5e3cefb72b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs @@ -7,20 +7,42 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { public IEmulatorServiceProvider ServiceProvider { get; private set; } - public ControllerDefinition ControllerDefinition { get; private set; } - - // TODO: use render and rendersound + public ControllerDefinition ControllerDefinition { get; private set; } + + // TODO: use render and rendersound public void FrameAdvance(IController controller, bool render, bool rendersound = true) { if (controller.IsPressed("Reset")) Core.gpgx_reset(false); if (controller.IsPressed("Power")) - Core.gpgx_reset(true); - - // this shouldn't be needed, as nothing has changed - // if (!Core.gpgx_get_control(input, inputsize)) - // throw new Exception("gpgx_get_control() failed!"); - + Core.gpgx_reset(true); + if (_cds != null) + { + var prev = controller.IsPressed("Previous Disk"); + var next = controller.IsPressed("Next Disk"); + int newDisk = _discIndex; + if (prev && !_prevDiskPressed) + newDisk--; + if (next && !_nextDiskPressed) + newDisk++; + + if (newDisk < -1) + newDisk = -1; + if (newDisk >= _cds.Length) + newDisk = _cds.Length - 1; + + if (newDisk != _discIndex) + { + _discIndex = newDisk; + Core.gpgx_swap_disc(_discIndex == -1 ? null : GetCDDataStruct(_cds[_discIndex])); + Console.WriteLine("IMMA CHANGING MAH DISKS"); + } + } + + // this shouldn't be needed, as nothing has changed + // if (!Core.gpgx_get_control(input, inputsize)) + // throw new Exception("gpgx_get_control() failed!"); + ControlConverter.ScreenWidth = vwidth; ControlConverter.ScreenHeight = vheight; ControlConverter.Convert(controller, input); @@ -39,7 +61,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx if (IsLagFrame) LagCount++; - if (CD != null) + if (_cds != null) DriveLightOn = _drivelight; } @@ -70,8 +92,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { if (_elf != null) _elf.Dispose(); - if (CD != null) - CD.Dispose(); + if (_cds != null) + foreach (var cd in _cds) + cd.Dispose(); _disposed = true; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs index a31c1233dc..312bc3e69c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs @@ -36,6 +36,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx Frame = reader.ReadInt32(); LagCount = reader.ReadInt32(); IsLagFrame = reader.ReadBoolean(); + _discIndex = reader.ReadInt32(); + _prevDiskPressed = reader.ReadBoolean(); + _nextDiskPressed = reader.ReadBoolean(); // any managed pointers that we sent to the core need to be resent now! Core.gpgx_set_input_callback(InputCallback); RefreshMemCallbacks(); @@ -51,6 +54,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx writer.Write(Frame); writer.Write(LagCount); writer.Write(IsLagFrame); + writer.Write(_discIndex); + writer.Write(_prevDiskPressed); + writer.Write(_nextDiskPressed); } public byte[] SaveStateBinary() diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index b94e50ab42..88ac2a5d75 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -7,6 +7,8 @@ using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Waterbox; using BizHawk.Common; using BizHawk.Emulation.DiscSystem; +using System.Collections.Generic; +using System.Linq; namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { @@ -27,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { } - public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object settings, object syncSettings) + public GPGX(CoreComm comm, byte[] rom, IEnumerable cds, object settings, object syncSettings) { ServiceProvider = new BasicServiceProvider(this); // this can influence some things internally (autodetect romtype, etc) @@ -64,12 +66,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx LoadCallback = new LibGPGX.load_archive_cb(load_archive); this.romfile = rom; - this.CD = CD; - if (CD != null) + + if (cds != null) { - this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD); + _cds = cds.ToArray(); + _cdReaders = cds.Select(c => new DiscSectorReader(c)).ToArray(); cd_callback_handle = new LibGPGX.cd_read_cb(CDRead); Core.gpgx_set_cdd_callback(cd_callback_handle); + DriveLightEnabled = true; } LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE; @@ -139,9 +143,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx InputCallback = new LibGPGX.input_cb(input_callback); Core.gpgx_set_input_callback(InputCallback); - if (CD != null) - DriveLightEnabled = true; - // process the non-init settings now PutSettings(_settings); @@ -159,8 +160,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx private LibGPGX Core; private PeRunner _elf; - DiscSystem.Disc CD; - DiscSystem.DiscSectorReader DiscSectorReader; + private Disc[] _cds; + private int _discIndex; + private DiscSectorReader[] _cdReaders; + private bool _prevDiskPressed; + private bool _nextDiskPressed; + byte[] romfile; private bool _disposed = false; @@ -217,12 +222,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx } else { - if (CD == null) + if (_cds == null) { Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename); return 0; } - srcdata = GetCDData(CD); + srcdata = GetCDData(_cds[0]); if (srcdata.Length != maxsize) { Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename); @@ -283,36 +288,38 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx void CDRead(int lba, IntPtr dest, bool audio) { - if (audio) + if ((uint)_discIndex < _cds.Length) { - byte[] data = new byte[2352]; - if (lba < CD.Session1.LeadoutLBA) + if (audio) { - DiscSectorReader.ReadLBA_2352(lba, data, 0); + byte[] data = new byte[2352]; + if (lba < _cds[_discIndex].Session1.LeadoutLBA) + { + _cdReaders[_discIndex].ReadLBA_2352(lba, data, 0); + } + else + { + // audio seems to read slightly past the end of disks; probably innoculous + // just send back 0s. + // Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount); + } + Marshal.Copy(data, 0, dest, 2352); } else { - // audio seems to read slightly past the end of disks; probably innoculous - // just send back 0s. - // Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount); + byte[] data = new byte[2048]; + _cdReaders[_discIndex].ReadLBA_2048(lba, data, 0); + Marshal.Copy(data, 0, dest, 2048); + _drivelight = true; } - Marshal.Copy(data, 0, dest, 2352); - } - else - { - byte[] data = new byte[2048]; - DiscSectorReader.ReadLBA_2048(lba, data, 0); - Marshal.Copy(data, 0, dest, 2048); - _drivelight = true; } } LibGPGX.cd_read_cb cd_callback_handle; - public static unsafe byte[] GetCDData(Disc cd) + public static LibGPGX.CDData GetCDDataStruct(Disc cd) { - LibGPGX.CDData ret = new LibGPGX.CDData(); - int size = Marshal.SizeOf(ret); + var ret = new LibGPGX.CDData(); var ses = cd.Session1; int ntrack = ses.InformationTrackCount; @@ -338,6 +345,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx } } + return ret; + } + + public static unsafe byte[] GetCDData(Disc cd) + { + var ret = GetCDDataStruct(cd); + int size = Marshal.SizeOf(ret); byte[] retdata = new byte[size]; fixed (byte* p = &retdata[0]) @@ -360,7 +374,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx if (!Core.gpgx_get_control(input, inputsize)) throw new Exception("gpgx_get_control() failed"); - ControlConverter = new GPGXControlConverter(input); + ControlConverter = new GPGXControlConverter(input, _cds != null); ControllerDefinition = ControlConverter.ControllerDef; } @@ -369,7 +383,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx return (LibGPGX.INPUT_DEVICE[])input.dev.Clone(); } - public bool IsMegaCD { get { return CD != null; } } + public bool IsMegaCD { get { return _cds != null; } } public class VDPView : IMonitor { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs index f1af5af62f..99a7a7745b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs @@ -175,7 +175,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx }); } - public GPGXControlConverter(LibGPGX.InputData input) + public GPGXControlConverter(LibGPGX.InputData input, bool cdButtons) { Console.WriteLine("Genesis Controller report:"); foreach (var e in input.system) @@ -189,6 +189,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx ControllerDef.BoolButtons.Add("Power"); ControllerDef.BoolButtons.Add("Reset"); + if (cdButtons) + { + ControllerDef.BoolButtons.Add("Previous Disk"); + ControllerDef.BoolButtons.Add("Next Disk"); + } for (int i = 0; i < LibGPGX.MAX_DEVICES; i++) { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs index aaea926d69..5892f53513 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs @@ -49,7 +49,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx public uint BackdropColor; } - [BizImport(CallingConvention.Cdecl, Compatibility=true)] + [BizImport(CallingConvention.Cdecl, Compatibility = true)] public abstract bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings); [BizImport(CallingConvention.Cdecl)] @@ -84,34 +84,34 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx public enum INPUT_SYSTEM : byte { - SYSTEM_NONE = 0, // unconnected port - SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad - SYSTEM_MOUSE = 2, // Sega Mouse - SYSTEM_MENACER = 3, // Sega Menacer -- port B only - SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only - SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only - SYSTEM_ACTIVATOR = 6, // Sega Activator - SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System - SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System - SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System - SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System - SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer - SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports + SYSTEM_NONE = 0, // unconnected port + SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad + SYSTEM_MOUSE = 2, // Sega Mouse + SYSTEM_MENACER = 3, // Sega Menacer -- port B only + SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only + SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only + SYSTEM_ACTIVATOR = 6, // Sega Activator + SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System + SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System + SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System + SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System + SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer + SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports }; public enum INPUT_DEVICE : byte { - DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player) - DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player) - DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player) - DEVICE_PAD2B = 0x02, // 2-buttons Control Pad - DEVICE_MOUSE = 0x03, // Sega Mouse + DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player) + DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player) + DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player) + DEVICE_PAD2B = 0x02, // 2-buttons Control Pad + DEVICE_MOUSE = 0x03, // Sega Mouse DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers - DEVICE_PADDLE = 0x05, // Sega Paddle Control + DEVICE_PADDLE = 0x05, // Sega Paddle Control DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad - DEVICE_PICO = 0x07, // PICO tablet - DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet - DEVICE_XE_A1P = 0x09, // XE-A1P analog controller + DEVICE_PICO = 0x07, // PICO tablet + DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet + DEVICE_XE_A1P = 0x09, // XE-A1P analog controller DEVICE_ACTIVATOR = 0x0a,// Activator }; @@ -151,8 +151,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [BizImport(CallingConvention.Cdecl)] public abstract void gpgx_set_cd_callback(CDCallback cd); - - /// /// not every flag is valid for every device! /// @@ -277,6 +275,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [BizImport(CallingConvention.Cdecl)] public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb); + [BizImport(CallingConvention.Cdecl, Compatibility = true)] + public abstract void gpgx_swap_disc(CDData toc); + [StructLayout(LayoutKind.Sequential)] public struct VDPNameTable { diff --git a/output/dll/gpgx.wbx.gz b/output/dll/gpgx.wbx.gz index fc7827b49e..3a0c836d14 100644 Binary files a/output/dll/gpgx.wbx.gz and b/output/dll/gpgx.wbx.gz differ diff --git a/waterbox/gpgx/cinterface/cinterface.c b/waterbox/gpgx/cinterface/cinterface.c index 9f2c53217c..84efe5fa24 100644 --- a/waterbox/gpgx/cinterface/cinterface.c +++ b/waterbox/gpgx/cinterface/cinterface.c @@ -192,6 +192,14 @@ GPGX_EX void gpgx_advance(void) nsamples = audio_update(soundbuffer); } +GPGX_EX void gpgx_swap_disc(const toc_t* toc) +{ + if (system_hw == SYSTEM_MCD) + { + cdd_hotswap(toc); + } +} + typedef struct { uint32 width; // in cells diff --git a/waterbox/gpgx/core/cd_hw/cdd.c b/waterbox/gpgx/core/cd_hw/cdd.c index 0eb4890317..cfebb81a45 100644 --- a/waterbox/gpgx/core/cd_hw/cdd.c +++ b/waterbox/gpgx/core/cd_hw/cdd.c @@ -1,826 +1,841 @@ -/*************************************************************************************** - * Genesis Plus - * CD drive processor & CD-DA fader - * - * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) - * - * Redistribution and use of this code or any derivative works are permitted - * provided that the following conditions are met: - * - * - Redistributions may not be sold, nor may they be used in a commercial - * product or activity. - * - * - Redistributions that are modified from the original source must include the - * complete source code, including the source code for all components used by a - * binary built from the modified sources. However, as a special exception, the - * source code distributed need not include anything that is normally distributed - * (in either source or binary form) with the major components (compiler, kernel, - * and so on) of the operating system on which the executable runs, unless that - * component itself accompanies the executable. - * - * - Redistributions must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************************/ -#include "shared.h" -#include "../cinterface/callbacks.h" - -/* BCD conversion lookup tables */ -static const uint8 lut_BCD_8[100] = -{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, -}; - -static const uint16 lut_BCD_16[100] = -{ - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, - 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, - 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, - 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, - 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609, - 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, 0x0708, 0x0709, - 0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, - 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909, -}; - -/* pre-build TOC */ -static const uint16 toc_snatcher[21] = -{ - 56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681, - 8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485, - 31380 -}; - -static const uint16 toc_lunar[52] = -{ - 5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870, - 6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263, - 6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205, - 1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549, - 1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132, - 685, 3167 -}; - -static const uint32 toc_shadow[15] = -{ - 10226, 70054, 11100, 12532, 12444, 11923, 10059, 10167, 10138, 13792, - 11637, 2547, 2521, 3856, 900 -}; - -static const uint32 toc_dungeon[13] = -{ - 2250, 22950, 16350, 24900, 13875, 19950, 13800, 15375, 17400, 17100, - 3325, 6825, 25275 -}; - -static const uint32 toc_ffight[26] = -{ - 11994, 9742, 10136, 9685, 9553, 14588, 9430, 8721, 9975, 9764, - 9704, 12796, 585, 754, 951, 624, 9047, 1068, 817, 9191, 1024, - 14562, 10320, 8627, 3795, 3047 -}; - -static const uint32 toc_ffightj[29] = -{ - 11994, 9752, 10119, 9690, 9567, 14575, 9431, 8731, 9965, 9763, - 9716, 12791, 579, 751, 958, 630, 9050, 1052, 825, 9193, 1026, - 14553, 9834, 10542, 1699, 1792, 1781, 3783, 3052 -}; - -/* supported WAVE file header (16-bit stereo samples @44.1kHz) */ -static const unsigned char waveHeader[32] = -{ - 0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00, - 0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61 -}; - -static blip_t* blip[2]; - -typedef struct -{ - toc_t toc; -} frontendcd_t; - -int cdd_load(const char *key, char *header) -{ - frontendcd_t fecd; - char data[2048]; - int startoffs; - - - int bytes = sizeof(frontendcd_t); - if (load_archive(key, (unsigned char *)&fecd, bytes, NULL) != bytes) - return 0; - - // look for valid header - cdd_readcallback(0, data, 0); - if (memcmp("SEGADISCSYSTEM", data, 14) == 0) - startoffs = 0; - else if (memcmp("SEGADISCSYSTEM", data + 16, 14) == 0) - startoffs = 16; - else - return 0; - // copy security block - memcpy(header, data + startoffs, 0x210); - - // copy disk information - memcpy(&cdd.toc, &fecd.toc, sizeof(toc_t)); - - cdd.loaded = 1; - return 1; -} - -void cdd_init(blip_t* left, blip_t* right) -{ - /* CD-DA is running by default at 44100 Hz */ - /* Audio stream is resampled to desired rate using Blip Buffer */ - blip[0] = left; - blip[1] = right; - blip_set_rates(left, 44100, snd.sample_rate); - blip_set_rates(right, 44100, snd.sample_rate); -} - -void cdd_reset(void) -{ - /* reset cycle counter */ - cdd.cycles = 0; - - /* reset drive access latency */ - cdd.latency = 0; - - /* reset track index */ - cdd.index = 0; - - /* reset logical block address */ - cdd.lba = 0; - - // reset audio subblock position - cdd.sampleOffset = 0; - - // reset audio read position - cdd.sampleLba = 0; - - /* reset status */ - cdd.status = cdd.loaded ? CD_STOP : NO_DISC; - - /* reset CD-DA fader (full volume) */ - cdd.volume = 0x400; - - /* clear CD-DA output */ - cdd.audio[0] = cdd.audio[1] = 0; -} - -void cdd_unload(void) -{ - cdd.loaded = 0; - cdd_readcallback = NULL; - - /* reset TOC */ - memset(&cdd.toc, 0x00, sizeof(cdd.toc)); -} - -void cdd_read_data(uint8 *dst) -{ - /* only read DATA track sectors */ - if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end)) - { - cdd_readcallback(cdd.lba, dst, 0); - } -} - -void cdd_read_audio(unsigned int samples) -{ - // previous audio outputs // - int16 l = cdd.audio[0]; - int16 r = cdd.audio[1]; - - // get number of internal clocks (samples) needed // - samples = blip_clocks_needed(blip[0], samples); - - // audio track playing ? // - if (!scd.regs[0x36>>1].byte.h) - { - int i, mul, delta; - - // current CD-DA fader volume // - int curVol = cdd.volume; - - // CD-DA fader volume setup (0-1024) // - int endVol = scd.regs[0x34>>1].w >> 4; - - // read samples from current block // - { -#ifdef LSB_FIRST - int16 *ptr = (int16 *) (cdc.ram); -#else - uint8 *ptr = cdc.ram; -#endif - { - char scratch[2352]; - // copy the end of current sector - int nsampreq = samples; - unsigned char *dest = cdc.ram; - cdd_readcallback(cdd.sampleLba, scratch, 1); - memcpy(cdc.ram, scratch + cdd.sampleOffset * 4, 2352 - cdd.sampleOffset * 4); - cdd.sampleLba++; - nsampreq -= 588 - cdd.sampleOffset; - dest += 2352 - cdd.sampleOffset * 4; - cdd.sampleOffset = 0; - // fill full sectors - while (nsampreq >= 588) - { - cdd_readcallback(cdd.sampleLba, scratch, 1); - memcpy(dest, scratch, 2352); - cdd.sampleLba++; - nsampreq -= 588; - dest += 2352; - } - // do last partial sector - if (nsampreq > 0) - { - cdd_readcallback(cdd.sampleLba, scratch, 1); - memcpy(dest, scratch, nsampreq * 4); - cdd.sampleOffset = nsampreq; - dest += nsampreq * 4; - nsampreq = 0; - } - //printf("samples: %i\n", samples); - //memset(cdc.ram, 0, samples * 4); - //fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd); - } - - // process 16-bit (little-endian) stereo samples // - for (i=0; i endVol) - { - // fade-out // - curVol--; - } - else if (!curVol) - { - // audio will remain muted until next setup // - break; - } - } - } - - // save current CD-DA fader volume // - cdd.volume = curVol; - - // save last audio output for next frame // - cdd.audio[0] = l; - cdd.audio[1] = r; - } - else - { - // no audio output // - if (l) blip_add_delta_fast(blip[0], 0, -l); - if (r) blip_add_delta_fast(blip[1], 0, -r); - - // save audio output for next frame // - cdd.audio[0] = 0; - cdd.audio[1] = 0; - } - - // end of Blip Buffer timeframe // - blip_end_frame(blip[0], samples); - blip_end_frame(blip[1], samples); -} - - -void cdd_update(void) -{ -#ifdef LOG_CDD - error("LBA = %d (track n°%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency); -#endif - - /* seeking disc */ - if (cdd.status == CD_SEEK) - { - /* drive latency */ - if (cdd.latency > 0) - { - cdd.latency--; - return; - } - - /* drive is ready */ - cdd.status = CD_READY; - } - - /* reading disc */ - else if (cdd.status == CD_PLAY) - { - /* drive latency */ - if (cdd.latency > 0) - { - cdd.latency--; - return; - } - - /* track type */ - if (!cdd.index) - { - /* DATA sector header (CD-ROM Mode 1) */ - uint8 header[4]; - uint32 msf = cdd.lba + 150; - header[0] = lut_BCD_8[(msf / 75) / 60]; - header[1] = lut_BCD_8[(msf / 75) % 60]; - header[2] = lut_BCD_8[(msf % 75)]; - header[3] = 0x01; - - /* data track sector read is controlled by CDC */ - cdd.lba += cdc_decoder_update(*(uint32 *)(header)); - } - else if (cdd.index < cdd.toc.last) - { - /* check against audio track start index */ - if (cdd.lba >= cdd.toc.tracks[cdd.index].start) - { - /* audio track playing */ - // if it wasn't before, set the audio start position - if (scd.regs[0x36>>1].byte.h) - { - cdd.sampleLba = cdd.lba + 1; - cdd.sampleOffset = 0; - } - scd.regs[0x36>>1].byte.h = 0x00; - } - - /* audio blocks are still sent to CDC as well as CD DAC/Fader */ - cdc_decoder_update(0); - - /* next audio block is automatically read */ - cdd.lba++; - } - else - { - /* end of disc */ - cdd.status = CD_END; - return; - } - - /* check end of current track */ - if (cdd.lba >= cdd.toc.tracks[cdd.index].end) - { - /* play next track */ - cdd.index++; - - /* PAUSE between tracks */ - scd.regs[0x36>>1].byte.h = 0x01; - } - } - - /* scanning disc */ - else if (cdd.status == CD_SCAN) - { - /* fast-forward or fast-rewind */ - cdd.lba += cdd.scanOffset; - cdd.sampleLba += cdd.scanOffset; - - /* check current track limits */ - if (cdd.lba >= cdd.toc.tracks[cdd.index].end) - { - /* next track */ - cdd.index++; - - /* skip directly to track start position */ - cdd.lba = cdd.toc.tracks[cdd.index].start; - - /* AUDIO track playing ? */ - if (cdd.status == CD_PLAY) - { - scd.regs[0x36>>1].byte.h = 0x00; - // set audio start point - cdd.sampleLba = cdd.lba; - cdd.sampleOffset = 0; - } - } - else if (cdd.lba < cdd.toc.tracks[cdd.index].start) - { - /* previous track */ - cdd.index--; - - /* skip directly to track end position */ - cdd.lba = cdd.toc.tracks[cdd.index].end; - } - - /* check disc limits */ - if (cdd.index < 0) - { - cdd.index = 0; - cdd.lba = 0; - } - else if (cdd.index >= cdd.toc.last) - { - /* no AUDIO track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* end of disc */ - cdd.index = cdd.toc.last; - cdd.lba = cdd.toc.end; - cdd.status = CD_END; - return; - } - } -} - -void cdd_process(void) -{ - /* Process CDD command */ - switch (scd.regs[0x42>>1].byte.h & 0x0f) - { - case 0x00: /* Drive Status */ - { - /* RS1-RS8 normally unchanged */ - scd.regs[0x38>>1].byte.h = cdd.status; - - /* unless RS1 indicated invalid track infos */ - if (scd.regs[0x38>>1].byte.l == 0x0f) - { - /* and SEEK has ended */ - if (cdd.status != CD_SEEK) - { - /* then return valid track infos, e.g current track number in RS2-RS3 (fixes Lunar - The Silver Star) */ - scd.regs[0x38>>1].byte.l = 0x02; - scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A; - } - } - break; - } - - case 0x01: /* Stop Drive */ - { - /* update status */ - cdd.status = cdd.loaded ? CD_STOP : NO_DISC; - - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ - scd.regs[0x38>>1].w = 0x0000; - scd.regs[0x3a>>1].w = 0x0000; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; - scd.regs[0x40>>1].w = 0x000f; - return; - } - - case 0x02: /* Read TOC */ - { - /* Infos automatically retrieved by CDD processor from Q-Channel */ - /* commands 0x00-0x02 (current block) and 0x03-0x05 (Lead-In) */ - switch (scd.regs[0x44>>1].byte.l) - { - case 0x00: /* Current Absolute Time (MM:SS:FF) */ - { - int lba = cdd.lba + 150; - scd.regs[0x38>>1].w = cdd.status << 8; - scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; - scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; - scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; - scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ - break; - } - - case 0x01: /* Current Track Relative Time (MM:SS:FF) */ - { - int lba = cdd.lba - cdd.toc.tracks[cdd.index].start; - scd.regs[0x38>>1].w = (cdd.status << 8) | 0x01; - scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; - scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; - scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; - scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ - break; - } - - case 0x02: /* Current Track Number */ - { - scd.regs[0x38>>1].w = (cdd.status << 8) | 0x02; - scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; /* Disk Control Code (?) in RS6 */ - scd.regs[0x40>>1].byte.h = 0x00; - break; - } - - case 0x03: /* Total length (MM:SS:FF) */ - { - int lba = cdd.toc.end + 150; - scd.regs[0x38>>1].w = (cdd.status << 8) | 0x03; - scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; - scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; - scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; - scd.regs[0x40>>1].byte.h = 0x00; - break; - } - - case 0x04: /* First & Last Track Numbers */ - { - scd.regs[0x38>>1].w = (cdd.status << 8) | 0x04; - scd.regs[0x3a>>1].w = 0x0001; - scd.regs[0x3c>>1].w = lut_BCD_16[cdd.toc.last]; - scd.regs[0x3e>>1].w = 0x0000; /* Drive Version (?) in RS6-RS7 */ - scd.regs[0x40>>1].byte.h = 0x00; /* Lead-In flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ - break; - } - - case 0x05: /* Track Start Time (MM:SS:FF) */ - { - int track = scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l; - int lba = cdd.toc.tracks[track-1].start + 150; - scd.regs[0x38>>1].w = (cdd.status << 8) | 0x05; - scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; - scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; - scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; - scd.regs[0x40>>1].byte.h = track % 10; /* Track Number (low digit) */ - if (track == 1) - { - /* RS6 bit 3 is set for the first (DATA) track */ - scd.regs[0x3e>>1].byte.h |= 0x08; - } - break; - } - - default: - { -#ifdef LOG_ERROR - error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc); -#endif - return; - } - } - break; - } - - case 0x03: /* Play */ - { - /* reset track index */ - int index = 0; - - /* new LBA position */ - int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 + - (scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 + - (scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150; - - /* CD drive latency */ - if (!cdd.latency) - { - /* Fixes a few games hanging during intro because they expect data to be read with some delay */ - /* Radical Rex needs at least one interrupt delay */ - /* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal) need at least 6 interrupts delay */ - /* Space Adventure Cobra (2nd morgue scene) needs at least 13 interrupts delay (incl. seek time, so 6 is OK) */ - /* Jeopardy & ESPN Sunday Night NFL are picky about this as well: 10 interrupts delay (+ seek time) seems OK */ - cdd.latency = 10; - } - - /* CD drive seek time */ - /* max. seek time = 1.5 s = 1.5 x 75 = 112.5 CDD interrupts (rounded to 120) for 270000 sectors max on disc. */ - /* Note: This is only a rough approximation since, on real hardware, seek time is much likely not linear and */ - /* latency much larger than above value, but this model works fine for Sonic CD (track 26 playback needs to */ - /* be enough delayed to start in sync with intro sequence, as compared with real hardware recording). */ - if (lba > cdd.lba) - { - cdd.latency += (((lba - cdd.lba) * 120) / 270000); - } - else - { - cdd.latency += (((cdd.lba - lba) * 120) / 270000); - } - - /* update current LBA */ - cdd.lba = lba; - - /* get track index */ - while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; - - /* update current track index */ - cdd.index = index; - - /* no audio track playing (yet) */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status */ - cdd.status = CD_PLAY; - - /* return track index in RS2-RS3 */ - scd.regs[0x38>>1].w = (CD_PLAY << 8) | 0x02; - scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[index + 1] : 0x0A0A; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; - scd.regs[0x40>>1].byte.h = 0x00; - break; - } - - case 0x04: /* Seek */ - { - /* reset track index */ - int index = 0; - - /* new LBA position */ - int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 + - (scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 + - (scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150; - - /* CD drive seek time */ - /* We are using similar linear model as above, although still not exactly accurate, */ - /* it works fine for Switch/Panic! intro (Switch needs at least 30 interrupts while */ - /* seeking from 00:05:63 to 24:03:19, Panic! when seeking from 00:05:60 to 24:06:07) */ - if (lba > cdd.lba) - { - cdd.latency = ((lba - cdd.lba) * 120) / 270000; - } - else - { - cdd.latency = ((cdd.lba - lba) * 120) / 270000; - } - - /* update current LBA */ - cdd.lba = lba; - - /* get current track index */ - while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; - - /* update current track index */ - cdd.index = index; - - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status */ - cdd.status = CD_SEEK; - - /* unknown RS1-RS8 values (returning 0xF in RS1 invalidates track infos in RS2-RS8 and fixes Final Fight CD intro when seek time is emulated) */ - scd.regs[0x38>>1].w = (CD_SEEK << 8) | 0x0f; - scd.regs[0x3a>>1].w = 0x0000; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; - scd.regs[0x40>>1].w = ~(CD_SEEK + 0xf) & 0x0f; - return; - } - - case 0x06: /* Pause */ - { - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status (RS1-RS8 unchanged) */ - cdd.status = scd.regs[0x38>>1].byte.h = CD_READY; - break; - } - - case 0x07: /* Resume */ - { - /* update status (RS1-RS8 unchanged) */ - cdd.status = scd.regs[0x38>>1].byte.h = CD_PLAY; - break; - } - - case 0x08: /* Forward Scan */ - { - /* reset scanning direction / speed */ - cdd.scanOffset = CD_SCAN_SPEED; - - /* update status (RS1-RS8 unchanged) */ - cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN; - break; - } - - case 0x09: /* Rewind Scan */ - { - /* reset scanning direction / speed */ - cdd.scanOffset = -CD_SCAN_SPEED; - - /* update status (RS1-RS8 unchanged) */ - cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN; - break; - } - - - case 0x0a: /* N-Track Jump Control ? (usually sent before CD_SEEK or CD_PLAY commands) */ - { - /* TC3 corresponds to seek direction (00=forward, FF=reverse) */ - /* TC4-TC7 are related to seek length (4x4 bits i.e parameter values are between -65535 and +65535) */ - /* Maybe related to number of auto-sequenced track jumps/moves for CD DSP (cf. CXD2500BQ datasheet) */ - /* also see US Patent nr. 5222054 for a detailled description of seeking operation using Track Jump */ - - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status (RS1-RS8 unchanged) */ - cdd.status = scd.regs[0x38>>1].byte.h = CD_READY; - break; - } - - case 0x0c: /* Close Tray */ - { - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status */ - cdd.status = cdd.loaded ? CD_STOP : NO_DISC; - - /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ - scd.regs[0x38>>1].w = 0x0000; - scd.regs[0x3a>>1].w = 0x0000; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; - scd.regs[0x40>>1].w = 0x000f; - -#ifdef CD_TRAY_CALLBACK - CD_TRAY_CALLBACK -#endif - return; - } - - case 0x0d: /* Open Tray */ - { - /* no audio track playing */ - scd.regs[0x36>>1].byte.h = 0x01; - - /* update status (RS1-RS8 ignored) */ - cdd.status = CD_OPEN; - scd.regs[0x38>>1].w = CD_OPEN << 8; - scd.regs[0x3a>>1].w = 0x0000; - scd.regs[0x3c>>1].w = 0x0000; - scd.regs[0x3e>>1].w = 0x0000; - scd.regs[0x40>>1].w = ~CD_OPEN & 0x0f; - -#ifdef CD_TRAY_CALLBACK - CD_TRAY_CALLBACK -#endif - return; - } - - default: /* Unknown command */ -#ifdef LOG_CDD - error("Unknown CDD Command !!!\n"); -#endif - scd.regs[0x38>>1].byte.h = cdd.status; - break; - } - - /* only compute checksum when necessary */ - scd.regs[0x40>>1].byte.l = ~(scd.regs[0x38>>1].byte.h + scd.regs[0x38>>1].byte.l + - scd.regs[0x3a>>1].byte.h + scd.regs[0x3a>>1].byte.l + - scd.regs[0x3c>>1].byte.h + scd.regs[0x3c>>1].byte.l + - scd.regs[0x3e>>1].byte.h + scd.regs[0x3e>>1].byte.l + - scd.regs[0x40>>1].byte.h) & 0x0f; -} +/*************************************************************************************** + * Genesis Plus + * CD drive processor & CD-DA fader + * + * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ +#include "shared.h" +#include "../cinterface/callbacks.h" + +/* BCD conversion lookup tables */ +static const uint8 lut_BCD_8[100] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, +}; + +static const uint16 lut_BCD_16[100] = +{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, + 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, + 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, + 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609, + 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, 0x0708, 0x0709, + 0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, + 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909, +}; + +/* pre-build TOC */ +static const uint16 toc_snatcher[21] = +{ + 56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681, + 8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485, + 31380 +}; + +static const uint16 toc_lunar[52] = +{ + 5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870, + 6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263, + 6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205, + 1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549, + 1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132, + 685, 3167 +}; + +static const uint32 toc_shadow[15] = +{ + 10226, 70054, 11100, 12532, 12444, 11923, 10059, 10167, 10138, 13792, + 11637, 2547, 2521, 3856, 900 +}; + +static const uint32 toc_dungeon[13] = +{ + 2250, 22950, 16350, 24900, 13875, 19950, 13800, 15375, 17400, 17100, + 3325, 6825, 25275 +}; + +static const uint32 toc_ffight[26] = +{ + 11994, 9742, 10136, 9685, 9553, 14588, 9430, 8721, 9975, 9764, + 9704, 12796, 585, 754, 951, 624, 9047, 1068, 817, 9191, 1024, + 14562, 10320, 8627, 3795, 3047 +}; + +static const uint32 toc_ffightj[29] = +{ + 11994, 9752, 10119, 9690, 9567, 14575, 9431, 8731, 9965, 9763, + 9716, 12791, 579, 751, 958, 630, 9050, 1052, 825, 9193, 1026, + 14553, 9834, 10542, 1699, 1792, 1781, 3783, 3052 +}; + +/* supported WAVE file header (16-bit stereo samples @44.1kHz) */ +static const unsigned char waveHeader[32] = +{ + 0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00, + 0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61 +}; + +static blip_t* blip[2]; + +typedef struct +{ + toc_t toc; +} frontendcd_t; + +int cdd_load(const char *key, char *header) +{ + frontendcd_t fecd; + char data[2048]; + int startoffs; + + + int bytes = sizeof(frontendcd_t); + if (load_archive(key, (unsigned char *)&fecd, bytes, NULL) != bytes) + return 0; + + // look for valid header + cdd_readcallback(0, data, 0); + if (memcmp("SEGADISCSYSTEM", data, 14) == 0) + startoffs = 0; + else if (memcmp("SEGADISCSYSTEM", data + 16, 14) == 0) + startoffs = 16; + else + return 0; + // copy security block + memcpy(header, data + startoffs, 0x210); + + // copy disk information + memcpy(&cdd.toc, &fecd.toc, sizeof(toc_t)); + + cdd.loaded = 1; + return 1; +} + +void cdd_init(blip_t* left, blip_t* right) +{ + /* CD-DA is running by default at 44100 Hz */ + /* Audio stream is resampled to desired rate using Blip Buffer */ + blip[0] = left; + blip[1] = right; + blip_set_rates(left, 44100, snd.sample_rate); + blip_set_rates(right, 44100, snd.sample_rate); +} + +void cdd_reset(void) +{ + /* reset cycle counter */ + cdd.cycles = 0; + + /* reset drive access latency */ + cdd.latency = 0; + + /* reset track index */ + cdd.index = 0; + + /* reset logical block address */ + cdd.lba = 0; + + // reset audio subblock position + cdd.sampleOffset = 0; + + // reset audio read position + cdd.sampleLba = 0; + + /* reset status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* reset CD-DA fader (full volume) */ + cdd.volume = 0x400; + + /* clear CD-DA output */ + cdd.audio[0] = cdd.audio[1] = 0; +} + +void cdd_unload(void) +{ + cdd.loaded = 0; + cdd_readcallback = NULL; + + /* reset TOC */ + memset(&cdd.toc, 0x00, sizeof(cdd.toc)); +} + +void cdd_hotswap(const toc_t *toc) +{ + if (toc) + { + cdd.loaded = 1; + memcpy(&cdd.toc, &toc, sizeof(cdd.toc)); + } + else + { + cdd.loaded = 0; + memset(&cdd.toc, 0x00, sizeof(cdd.toc)); + } + cdd_reset(); +} + +void cdd_read_data(uint8 *dst) +{ + /* only read DATA track sectors */ + if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end)) + { + cdd_readcallback(cdd.lba, dst, 0); + } +} + +void cdd_read_audio(unsigned int samples) +{ + // previous audio outputs // + int16 l = cdd.audio[0]; + int16 r = cdd.audio[1]; + + // get number of internal clocks (samples) needed // + samples = blip_clocks_needed(blip[0], samples); + + // audio track playing ? // + if (!scd.regs[0x36>>1].byte.h) + { + int i, mul, delta; + + // current CD-DA fader volume // + int curVol = cdd.volume; + + // CD-DA fader volume setup (0-1024) // + int endVol = scd.regs[0x34>>1].w >> 4; + + // read samples from current block // + { +#ifdef LSB_FIRST + int16 *ptr = (int16 *) (cdc.ram); +#else + uint8 *ptr = cdc.ram; +#endif + { + char scratch[2352]; + // copy the end of current sector + int nsampreq = samples; + unsigned char *dest = cdc.ram; + cdd_readcallback(cdd.sampleLba, scratch, 1); + memcpy(cdc.ram, scratch + cdd.sampleOffset * 4, 2352 - cdd.sampleOffset * 4); + cdd.sampleLba++; + nsampreq -= 588 - cdd.sampleOffset; + dest += 2352 - cdd.sampleOffset * 4; + cdd.sampleOffset = 0; + // fill full sectors + while (nsampreq >= 588) + { + cdd_readcallback(cdd.sampleLba, scratch, 1); + memcpy(dest, scratch, 2352); + cdd.sampleLba++; + nsampreq -= 588; + dest += 2352; + } + // do last partial sector + if (nsampreq > 0) + { + cdd_readcallback(cdd.sampleLba, scratch, 1); + memcpy(dest, scratch, nsampreq * 4); + cdd.sampleOffset = nsampreq; + dest += nsampreq * 4; + nsampreq = 0; + } + //printf("samples: %i\n", samples); + //memset(cdc.ram, 0, samples * 4); + //fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd); + } + + // process 16-bit (little-endian) stereo samples // + for (i=0; i endVol) + { + // fade-out // + curVol--; + } + else if (!curVol) + { + // audio will remain muted until next setup // + break; + } + } + } + + // save current CD-DA fader volume // + cdd.volume = curVol; + + // save last audio output for next frame // + cdd.audio[0] = l; + cdd.audio[1] = r; + } + else + { + // no audio output // + if (l) blip_add_delta_fast(blip[0], 0, -l); + if (r) blip_add_delta_fast(blip[1], 0, -r); + + // save audio output for next frame // + cdd.audio[0] = 0; + cdd.audio[1] = 0; + } + + // end of Blip Buffer timeframe // + blip_end_frame(blip[0], samples); + blip_end_frame(blip[1], samples); +} + + +void cdd_update(void) +{ +#ifdef LOG_CDD + error("LBA = %d (track n�%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency); +#endif + + /* seeking disc */ + if (cdd.status == CD_SEEK) + { + /* drive latency */ + if (cdd.latency > 0) + { + cdd.latency--; + return; + } + + /* drive is ready */ + cdd.status = CD_READY; + } + + /* reading disc */ + else if (cdd.status == CD_PLAY) + { + /* drive latency */ + if (cdd.latency > 0) + { + cdd.latency--; + return; + } + + /* track type */ + if (!cdd.index) + { + /* DATA sector header (CD-ROM Mode 1) */ + uint8 header[4]; + uint32 msf = cdd.lba + 150; + header[0] = lut_BCD_8[(msf / 75) / 60]; + header[1] = lut_BCD_8[(msf / 75) % 60]; + header[2] = lut_BCD_8[(msf % 75)]; + header[3] = 0x01; + + /* data track sector read is controlled by CDC */ + cdd.lba += cdc_decoder_update(*(uint32 *)(header)); + } + else if (cdd.index < cdd.toc.last) + { + /* check against audio track start index */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].start) + { + /* audio track playing */ + // if it wasn't before, set the audio start position + if (scd.regs[0x36>>1].byte.h) + { + cdd.sampleLba = cdd.lba + 1; + cdd.sampleOffset = 0; + } + scd.regs[0x36>>1].byte.h = 0x00; + } + + /* audio blocks are still sent to CDC as well as CD DAC/Fader */ + cdc_decoder_update(0); + + /* next audio block is automatically read */ + cdd.lba++; + } + else + { + /* end of disc */ + cdd.status = CD_END; + return; + } + + /* check end of current track */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + { + /* play next track */ + cdd.index++; + + /* PAUSE between tracks */ + scd.regs[0x36>>1].byte.h = 0x01; + } + } + + /* scanning disc */ + else if (cdd.status == CD_SCAN) + { + /* fast-forward or fast-rewind */ + cdd.lba += cdd.scanOffset; + cdd.sampleLba += cdd.scanOffset; + + /* check current track limits */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + { + /* next track */ + cdd.index++; + + /* skip directly to track start position */ + cdd.lba = cdd.toc.tracks[cdd.index].start; + + /* AUDIO track playing ? */ + if (cdd.status == CD_PLAY) + { + scd.regs[0x36>>1].byte.h = 0x00; + // set audio start point + cdd.sampleLba = cdd.lba; + cdd.sampleOffset = 0; + } + } + else if (cdd.lba < cdd.toc.tracks[cdd.index].start) + { + /* previous track */ + cdd.index--; + + /* skip directly to track end position */ + cdd.lba = cdd.toc.tracks[cdd.index].end; + } + + /* check disc limits */ + if (cdd.index < 0) + { + cdd.index = 0; + cdd.lba = 0; + } + else if (cdd.index >= cdd.toc.last) + { + /* no AUDIO track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* end of disc */ + cdd.index = cdd.toc.last; + cdd.lba = cdd.toc.end; + cdd.status = CD_END; + return; + } + } +} + +void cdd_process(void) +{ + /* Process CDD command */ + switch (scd.regs[0x42>>1].byte.h & 0x0f) + { + case 0x00: /* Drive Status */ + { + /* RS1-RS8 normally unchanged */ + scd.regs[0x38>>1].byte.h = cdd.status; + + /* unless RS1 indicated invalid track infos */ + if (scd.regs[0x38>>1].byte.l == 0x0f) + { + /* and SEEK has ended */ + if (cdd.status != CD_SEEK) + { + /* then return valid track infos, e.g current track number in RS2-RS3 (fixes Lunar - The Silver Star) */ + scd.regs[0x38>>1].byte.l = 0x02; + scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A; + } + } + break; + } + + case 0x01: /* Stop Drive */ + { + /* update status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ + scd.regs[0x38>>1].w = 0x0000; + scd.regs[0x3a>>1].w = 0x0000; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; + scd.regs[0x40>>1].w = 0x000f; + return; + } + + case 0x02: /* Read TOC */ + { + /* Infos automatically retrieved by CDD processor from Q-Channel */ + /* commands 0x00-0x02 (current block) and 0x03-0x05 (Lead-In) */ + switch (scd.regs[0x44>>1].byte.l) + { + case 0x00: /* Current Absolute Time (MM:SS:FF) */ + { + int lba = cdd.lba + 150; + scd.regs[0x38>>1].w = cdd.status << 8; + scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; + scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; + scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; + scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x01: /* Current Track Relative Time (MM:SS:FF) */ + { + int lba = cdd.lba - cdd.toc.tracks[cdd.index].start; + scd.regs[0x38>>1].w = (cdd.status << 8) | 0x01; + scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; + scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; + scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; + scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x02: /* Current Track Number */ + { + scd.regs[0x38>>1].w = (cdd.status << 8) | 0x02; + scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; /* Disk Control Code (?) in RS6 */ + scd.regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x03: /* Total length (MM:SS:FF) */ + { + int lba = cdd.toc.end + 150; + scd.regs[0x38>>1].w = (cdd.status << 8) | 0x03; + scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; + scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; + scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; + scd.regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x04: /* First & Last Track Numbers */ + { + scd.regs[0x38>>1].w = (cdd.status << 8) | 0x04; + scd.regs[0x3a>>1].w = 0x0001; + scd.regs[0x3c>>1].w = lut_BCD_16[cdd.toc.last]; + scd.regs[0x3e>>1].w = 0x0000; /* Drive Version (?) in RS6-RS7 */ + scd.regs[0x40>>1].byte.h = 0x00; /* Lead-In flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x05: /* Track Start Time (MM:SS:FF) */ + { + int track = scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l; + int lba = cdd.toc.tracks[track-1].start + 150; + scd.regs[0x38>>1].w = (cdd.status << 8) | 0x05; + scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60]; + scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60]; + scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)]; + scd.regs[0x40>>1].byte.h = track % 10; /* Track Number (low digit) */ + if (track == 1) + { + /* RS6 bit 3 is set for the first (DATA) track */ + scd.regs[0x3e>>1].byte.h |= 0x08; + } + break; + } + + default: + { +#ifdef LOG_ERROR + error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc); +#endif + return; + } + } + break; + } + + case 0x03: /* Play */ + { + /* reset track index */ + int index = 0; + + /* new LBA position */ + int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 + + (scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 + + (scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150; + + /* CD drive latency */ + if (!cdd.latency) + { + /* Fixes a few games hanging during intro because they expect data to be read with some delay */ + /* Radical Rex needs at least one interrupt delay */ + /* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal) need at least 6 interrupts delay */ + /* Space Adventure Cobra (2nd morgue scene) needs at least 13 interrupts delay (incl. seek time, so 6 is OK) */ + /* Jeopardy & ESPN Sunday Night NFL are picky about this as well: 10 interrupts delay (+ seek time) seems OK */ + cdd.latency = 10; + } + + /* CD drive seek time */ + /* max. seek time = 1.5 s = 1.5 x 75 = 112.5 CDD interrupts (rounded to 120) for 270000 sectors max on disc. */ + /* Note: This is only a rough approximation since, on real hardware, seek time is much likely not linear and */ + /* latency much larger than above value, but this model works fine for Sonic CD (track 26 playback needs to */ + /* be enough delayed to start in sync with intro sequence, as compared with real hardware recording). */ + if (lba > cdd.lba) + { + cdd.latency += (((lba - cdd.lba) * 120) / 270000); + } + else + { + cdd.latency += (((cdd.lba - lba) * 120) / 270000); + } + + /* update current LBA */ + cdd.lba = lba; + + /* get track index */ + while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; + + /* update current track index */ + cdd.index = index; + + /* no audio track playing (yet) */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = CD_PLAY; + + /* return track index in RS2-RS3 */ + scd.regs[0x38>>1].w = (CD_PLAY << 8) | 0x02; + scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[index + 1] : 0x0A0A; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; + scd.regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x04: /* Seek */ + { + /* reset track index */ + int index = 0; + + /* new LBA position */ + int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 + + (scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 + + (scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150; + + /* CD drive seek time */ + /* We are using similar linear model as above, although still not exactly accurate, */ + /* it works fine for Switch/Panic! intro (Switch needs at least 30 interrupts while */ + /* seeking from 00:05:63 to 24:03:19, Panic! when seeking from 00:05:60 to 24:06:07) */ + if (lba > cdd.lba) + { + cdd.latency = ((lba - cdd.lba) * 120) / 270000; + } + else + { + cdd.latency = ((cdd.lba - lba) * 120) / 270000; + } + + /* update current LBA */ + cdd.lba = lba; + + /* get current track index */ + while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; + + /* update current track index */ + cdd.index = index; + + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = CD_SEEK; + + /* unknown RS1-RS8 values (returning 0xF in RS1 invalidates track infos in RS2-RS8 and fixes Final Fight CD intro when seek time is emulated) */ + scd.regs[0x38>>1].w = (CD_SEEK << 8) | 0x0f; + scd.regs[0x3a>>1].w = 0x0000; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; + scd.regs[0x40>>1].w = ~(CD_SEEK + 0xf) & 0x0f; + return; + } + + case 0x06: /* Pause */ + { + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = scd.regs[0x38>>1].byte.h = CD_READY; + break; + } + + case 0x07: /* Resume */ + { + /* update status (RS1-RS8 unchanged) */ + cdd.status = scd.regs[0x38>>1].byte.h = CD_PLAY; + break; + } + + case 0x08: /* Forward Scan */ + { + /* reset scanning direction / speed */ + cdd.scanOffset = CD_SCAN_SPEED; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN; + break; + } + + case 0x09: /* Rewind Scan */ + { + /* reset scanning direction / speed */ + cdd.scanOffset = -CD_SCAN_SPEED; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN; + break; + } + + + case 0x0a: /* N-Track Jump Control ? (usually sent before CD_SEEK or CD_PLAY commands) */ + { + /* TC3 corresponds to seek direction (00=forward, FF=reverse) */ + /* TC4-TC7 are related to seek length (4x4 bits i.e parameter values are between -65535 and +65535) */ + /* Maybe related to number of auto-sequenced track jumps/moves for CD DSP (cf. CXD2500BQ datasheet) */ + /* also see US Patent nr. 5222054 for a detailled description of seeking operation using Track Jump */ + + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = scd.regs[0x38>>1].byte.h = CD_READY; + break; + } + + case 0x0c: /* Close Tray */ + { + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ + scd.regs[0x38>>1].w = 0x0000; + scd.regs[0x3a>>1].w = 0x0000; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; + scd.regs[0x40>>1].w = 0x000f; + +#ifdef CD_TRAY_CALLBACK + CD_TRAY_CALLBACK +#endif + return; + } + + case 0x0d: /* Open Tray */ + { + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 ignored) */ + cdd.status = CD_OPEN; + scd.regs[0x38>>1].w = CD_OPEN << 8; + scd.regs[0x3a>>1].w = 0x0000; + scd.regs[0x3c>>1].w = 0x0000; + scd.regs[0x3e>>1].w = 0x0000; + scd.regs[0x40>>1].w = ~CD_OPEN & 0x0f; + +#ifdef CD_TRAY_CALLBACK + CD_TRAY_CALLBACK +#endif + return; + } + + default: /* Unknown command */ +#ifdef LOG_CDD + error("Unknown CDD Command !!!\n"); +#endif + scd.regs[0x38>>1].byte.h = cdd.status; + break; + } + + /* only compute checksum when necessary */ + scd.regs[0x40>>1].byte.l = ~(scd.regs[0x38>>1].byte.h + scd.regs[0x38>>1].byte.l + + scd.regs[0x3a>>1].byte.h + scd.regs[0x3a>>1].byte.l + + scd.regs[0x3c>>1].byte.h + scd.regs[0x3c>>1].byte.l + + scd.regs[0x3e>>1].byte.h + scd.regs[0x3e>>1].byte.l + + scd.regs[0x40>>1].byte.h) & 0x0f; +} diff --git a/waterbox/gpgx/core/cd_hw/cdd.h b/waterbox/gpgx/core/cd_hw/cdd.h index e1300b4eac..fdfb62b127 100644 --- a/waterbox/gpgx/core/cd_hw/cdd.h +++ b/waterbox/gpgx/core/cd_hw/cdd.h @@ -1,106 +1,110 @@ -/*************************************************************************************** - * Genesis Plus - * CD drive processor & CD-DA fader - * - * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) - * - * Redistribution and use of this code or any derivative works are permitted - * provided that the following conditions are met: - * - * - Redistributions may not be sold, nor may they be used in a commercial - * product or activity. - * - * - Redistributions that are modified from the original source must include the - * complete source code, including the source code for all components used by a - * binary built from the modified sources. However, as a special exception, the - * source code distributed need not include anything that is normally distributed - * (in either source or binary form) with the major components (compiler, kernel, - * and so on) of the operating system on which the executable runs, unless that - * component itself accompanies the executable. - * - * - Redistributions must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************************/ -#ifndef _HW_CDD_ -#define _HW_CDD_ - -#include "blip_buf.h" - -#ifdef USE_LIBTREMOR -#include "tremor/ivorbisfile.h" -#endif - -#define cdd scd.cdd_hw - -/* CDD status */ -#define NO_DISC 0x00 -#define CD_PLAY 0x01 -#define CD_SEEK 0x02 -#define CD_SCAN 0x03 -#define CD_READY 0x04 -#define CD_OPEN 0x05 /* similar to 0x0E ? */ -#define CD_STOP 0x09 -#define CD_END 0x0C - -/* CD blocks scanning speed */ -#define CD_SCAN_SPEED 30 - -#define CD_MAX_TRACKS 100 - -/* CD track */ -typedef struct -{ - int start; - int end; -} track_t; - -/* CD TOC */ -typedef struct -{ - int end; - int last; - track_t tracks[CD_MAX_TRACKS]; -} toc_t; - -/* CDD hardware */ -typedef struct -{ - uint32 cycles; - uint32 latency; - int loaded; - int index; - int lba; - int scanOffset; - int volume; - int sampleOffset; - int sampleLba; - uint8 status; - toc_t toc; - int16 audio[2]; -} cdd_t; - -/* Function prototypes */ -extern void cdd_init(blip_t* left, blip_t* right); -extern void cdd_reset(void); -extern int cdd_load(const char *key, char *header); -extern void cdd_unload(void); -extern void cdd_read_data(uint8 *dst); -extern void cdd_read_audio(unsigned int samples); -extern void cdd_update(void); -extern void cdd_process(void); - -#endif +/*************************************************************************************** + * Genesis Plus + * CD drive processor & CD-DA fader + * + * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ +#ifndef _HW_CDD_ +#define _HW_CDD_ + +#include "blip_buf.h" + +#ifdef USE_LIBTREMOR +#include "tremor/ivorbisfile.h" +#endif + +#define cdd scd.cdd_hw + +/* CDD status */ +#define NO_DISC 0x00 +#define CD_PLAY 0x01 +#define CD_SEEK 0x02 +#define CD_SCAN 0x03 +#define CD_READY 0x04 +#define CD_OPEN 0x05 /* similar to 0x0E ? */ +#define CD_STOP 0x09 +#define CD_END 0x0C + +/* CD blocks scanning speed */ +#define CD_SCAN_SPEED 30 + +#define CD_MAX_TRACKS 100 + +/* CD track */ +typedef struct +{ + int start; + int end; +} track_t; + +/* CD TOC */ +typedef struct +{ + int end; + int last; + track_t tracks[CD_MAX_TRACKS]; +} toc_t; + +/* CDD hardware */ +typedef struct +{ + uint32 cycles; + uint32 latency; + int loaded; + int index; + int lba; + int scanOffset; + int volume; + int sampleOffset; + int sampleLba; + uint8 status; + toc_t toc; + int16 audio[2]; +} cdd_t; + +/* Function prototypes */ +extern void cdd_init(blip_t* left, blip_t* right); +extern void cdd_reset(void); +extern int cdd_load(const char *key, char *header); +extern void cdd_unload(void); +extern void cdd_read_data(uint8 *dst); +extern void cdd_read_audio(unsigned int samples); +extern void cdd_update(void); +extern void cdd_process(void); + +// switch disks after emulation was started +// pass NULL to open tray +void cdd_hotswap(const toc_t *toc); + +#endif