[Scummvm-git-logs] scummvm master -> 45c8c90bdd129d51ac5360ba064f8e27528f9367

dreammaster noreply at scummvm.org
Tue May 5 10:44:37 UTC 2026


This automated email contains information about 18 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
17e6e98711 MADS: PHANTOM: Move file access code from ASound to SoundDriver base class
cdb6a04607 MADS: PHANTOM: New ASound class implementation
af3b883391 MADS: PHANTOM: Renaming ASound generic arrays/fields to proper names
bc5484c8d5 MADS: PHANTOM: Fixes to new ASound class
52305a16a2 MADS: PHANTOM: Change ASound types to use ScummVM types
e4bddf2cb0 MADS: PHANTOM: Further sound fixes
645c8b7844 MADS: PHANTOM: Load sound samples for each driver
105fe44e3a MADS: PHANTOM: Sound mutex and init fixes
bf952bd1eb MADS: PHANTOM: Further sound fixes
061fb12acc MADS: PHANTOM: New ASound class finally starting to produce sound
a990769ec6 MADS: PHANTOM: Various ASound fixes
64a96d2fe8 MADS: PHANTOM: Cleanup of ASound comments and debugging code
4afbf280c1 MADS: NEBULAR: Shift ASound data caching to Nebular ASound
b250841d0b MADS: PHANTOM: Fix loader_read in anim_load
006059de52 MADS: PHANTOM: Beginnings of animview
9474e9da69 MADS: PHANTOM: Loop for reading res file lines
8553563b20 MADS: PHANTOM: Add resource flag parsing
45c8c90bdd MADS: PHANTOM: ConfMan option to quick start animview


Commit: 17e6e987110403e4b4633a70928f30f7e6aa39da
    https://github.com/scummvm/scummvm/commit/17e6e987110403e4b4633a70928f30f7e6aa39da
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:44+10:00

Commit Message:
MADS: PHANTOM: Move file access code from ASound to SoundDriver base class

Changed paths:
  A engines/mads/madsv2/core/asound.cpp
  A engines/mads/madsv2/core/asound.h
    engines/mads/core/sound_manager.cpp
    engines/mads/core/sound_manager.h
    engines/mads/madsv2/phantom/sound_phantom.h
    engines/mads/module.mk
    engines/mads/nebular/core/asound.cpp
    engines/mads/nebular/core/asound.h


diff --git a/engines/mads/core/sound_manager.cpp b/engines/mads/core/sound_manager.cpp
index a613a484303..658b9e832a9 100644
--- a/engines/mads/core/sound_manager.cpp
+++ b/engines/mads/core/sound_manager.cpp
@@ -121,4 +121,44 @@ void SoundManager::noise() {
 		_driver->noise();
 }
 
+//====================================================================
+
+SoundDriver::SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset) :
+	_mixer(mixer), _opl(opl), _dataOffset(dataOffset) {
+	// Open up the appropriate sound file
+	if (!_soundFile.open(filename))
+		error("Could not open file - %s", filename.toString().c_str());
+}
+
+SoundDriver::~SoundDriver() {
+	_opl->stop();
+
+	Common::List<CachedDataEntry>::iterator i;
+	for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
+		delete[](*i)._data;
+}
+
+byte *SoundDriver::loadData(int offset, int size) {
+	// First scan for an existing copy
+	Common::List<CachedDataEntry>::iterator i;
+	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
+		CachedDataEntry &e = *i;
+		if (e._offset == offset)
+			return e._data;
+	}
+
+	// No existing entry found, so load up data and store as a new entry
+	CachedDataEntry rec;
+	rec._offset = offset;
+	rec._data = new byte[size];
+	rec._dataEnd = rec._data + size - 1;
+	_soundFile.seek(_dataOffset + offset);
+	_soundFile.read(rec._data, size);
+	_dataCache.push_back(rec);
+
+	// Return the data
+	return rec._data;
+}
+
+
 } // namespace MADS
diff --git a/engines/mads/core/sound_manager.h b/engines/mads/core/sound_manager.h
index 9a767222591..7b86e6c294f 100644
--- a/engines/mads/core/sound_manager.h
+++ b/engines/mads/core/sound_manager.h
@@ -37,15 +37,29 @@ class OPL;
 
 namespace MADS {
 
+struct CachedDataEntry {
+	int _offset;
+	byte *_data;
+	byte *_dataEnd;
+};
+
 class SoundDriver {
 protected:
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
+	Common::File _soundFile;
+	Common::List<CachedDataEntry> _dataCache;
+	int _dataOffset;
+
+	/**
+	 * Loads a data block from the sound file, caching the result for any future
+	 * calls for the same data
+	 */
+	byte *loadData(int offset, int size);
 
 public:
-	SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl) : _mixer(mixer), _opl(opl) {}
-	virtual ~SoundDriver() {
-	}
+	SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
+	virtual ~SoundDriver();
 
 	/**
 	 * Execute a player command. Most commands represent sounds to play, but some
diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
new file mode 100644
index 00000000000..8a1fa4339e8
--- /dev/null
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -0,0 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/asound.h"
+
+namespace MADS {
+namespace MADSV2 {
+
+
+
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
new file mode 100644
index 00000000000..07b93147494
--- /dev/null
+++ b/engines/mads/madsv2/core/asound.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_CORE_ASOUND_H
+#define MADS_CORE_ASOUND_H
+
+#include "mads/core/sound_manager.h"
+
+namespace MADS {
+namespace MADSV2 {
+
+class ASound : public SoundDriver {
+public:
+	/**
+	 * Constructor
+	 * @param mixer			Mixer
+	 * @param opl			OPL
+	 * @param filename		Specifies the adlib sound player file to use
+	 * @param dataOffset	Offset in the file of the data segment
+	 */
+	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
+
+	/**
+	 * Destructor
+	 */
+	~ASound() override {}
+};
+
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/phantom/sound_phantom.h b/engines/mads/madsv2/phantom/sound_phantom.h
index 38bd91b9f31..20eb373c53a 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.h
+++ b/engines/mads/madsv2/phantom/sound_phantom.h
@@ -22,15 +22,12 @@
 #ifndef MADS_PHANTOM_SOUND_H
 #define MADS_PHANTOM_SOUND_H
 
-#include "mads/nebular/core/asound.h"
+#include "mads/madsv2/core/asound.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Phantom {
 
-using Nebular::AdlibChannel;
-using Nebular::AdlibSample;
-
 class PhantomSoundManager : public SoundManager {
 protected:
 	void loadDriver(int sectionNum) override;
@@ -44,29 +41,6 @@ public:
 	void validate() override;
 };
 
-class PhantomASound : public Nebular::ASound {
-protected:
-	// Per-driver scripting register file (256 byte-sized registers)
-	byte _scratchArr[256];
-
-	// Music synchronisation state variables (mirrors word_11F32 etc.)
-	int _w11F32 = 0, _w11F42 = 0, _w11F44 = 0;
-	int _w11F46 = 0, _w11F48 = 0, _w11F4A = 0;
-	int _w11F4C = 0, _w11F4E = 0, _w11F50 = 0;
-
-	void channelCommand(byte *&pSrc, bool &updateFlag) override;
-
-public:
-	/**
-	 * Constructor
-	 * @param mixer			Mixer
-	 * @param opl			OPL
-	 * @param filename		Specifies the adlib sound player file to use
-	 * @param dataOffset	Offset in the file of the data segment
-	 */
-	PhantomASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
-};
-
 /**
  * ASound1  (asound.ph1, _dataOffset = 0x21e0)
  *
@@ -85,7 +59,7 @@ public:
  * randomly picks from four music loaders and plays it, storing the choice
  * in _musicIndex (mirrors word_11F5E in the original).
  */
-class ASound1 : public PhantomASound {
+class ASound1 : public ASound {
 private:
 	typedef int (ASound1::*CommandPtr)();
 	static const CommandPtr _commandList[40];
@@ -141,7 +115,7 @@ public:
  *   asound_commands4: commands 32–35  (max=0x23, base=0x20, 4 entries)
  *   asound_commands5: commands 64–72  (max=0x48, base=0x40, 9 entries)
  */
-class ASound2 : public PhantomASound {
+class ASound2 : public ASound {
 private:
 	typedef int (ASound2::*CommandPtr)();
 	static const CommandPtr _commandList[73];
@@ -195,7 +169,7 @@ public:
  *   asound_commands5: commands 64–75  (max=0x4B, base=0x40, 12 entries)
  *     (command 76 = nullsub_8, silently ignored by bounds check)
  */
-class ASound3 : public PhantomASound {
+class ASound3 : public ASound {
 private:
 	typedef int (ASound3::*CommandPtr)();
 	static const CommandPtr _commandList[77];
@@ -259,7 +233,7 @@ public:
  *
  * commands 24 and 25 share the same handler (sub_11D0A).
  */
-class ASound4 : public PhantomASound {
+class ASound4 : public ASound {
 private:
 	typedef int (ASound4::*CommandPtr)();
 	static const CommandPtr _commandList[71];
@@ -309,7 +283,7 @@ public:
  * commands 36/35/34 load channels in non-sequential data order.
  * commands 70, 77, and 78 all play the same 0x40BA sound block.
  */
-class ASound5 : public PhantomASound {
+class ASound5 : public ASound {
 private:
 	typedef int (ASound5::*CommandPtr)();
 	static const CommandPtr _commandList[79];
@@ -362,7 +336,7 @@ public:
 	int command(int commandId, int param) override;
 };
 
-class ASound9 : public PhantomASound {
+class ASound9 : public ASound {
 private:
 	typedef int (ASound9:: *CommandPtr)();
 	int command0() override;
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 2cf1ef87422..b8ee8c05a95 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -58,6 +58,7 @@ MODULE_OBJS += \
 	madsv2/console.o \
 	madsv2/engine.o \
 	madsv2/core/anim.o \
+	madsv2/core/asound.o \
 	madsv2/core/attr.o \
 	madsv2/core/buffer.o \
 	madsv2/core/camera.o \
diff --git a/engines/mads/nebular/core/asound.cpp b/engines/mads/nebular/core/asound.cpp
index b99c19038b5..2ebd0b1b3fc 100644
--- a/engines/mads/nebular/core/asound.cpp
+++ b/engines/mads/nebular/core/asound.cpp
@@ -155,11 +155,7 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
 /*-----------------------------------------------------------------------*/
 
 ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset) :
-	SoundDriver(mixer, opl) {
-	// Open up the appropriate sound file
-	if (!_soundFile.open(filename))
-		error("Could not open file - %s", filename.toString().c_str());
-
+	SoundDriver(mixer, opl, filename, dataOffset) {
 	// Initialize fields
 	_commandParam = 0;
 	_activeChannelPtr = nullptr;
@@ -199,11 +195,6 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 
 	AdlibChannel::_channelsEnabled = false;
 
-	// Store passed parameters, and setup OPL
-	_dataOffset = dataOffset;
-	_mixer = mixer;
-	_opl = opl;
-
 	// Initialize the Adlib
 	adlibInit();
 
@@ -213,15 +204,6 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
 }
 
-ASound::~ASound() {
-	_opl->stop();
-
-	Common::List<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
-		delete[](*i)._data;
-}
-
-
 void ASound::adlibInit() {
 	write(4, 0x60);
 	write(4, 0x80);
@@ -316,28 +298,6 @@ void ASound::resultCheck() {
 	}
 }
 
-byte *ASound::loadData(int offset, int size) {
-	// First scan for an existing copy
-	Common::List<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
-		CachedDataEntry &e = *i;
-		if (e._offset == offset)
-			return e._data;
-	}
-
-	// No existing entry found, so load up data and store as a new entry
-	CachedDataEntry rec;
-	rec._offset = offset;
-	rec._data = new byte[size];
-	rec._dataEnd = rec._data + size - 1;
-	_soundFile.seek(_dataOffset + offset);
-	_soundFile.read(rec._data, size);
-	_dataCache.push_back(rec);
-
-	// Return the data
-	return rec._data;
-}
-
 void ASound::playSound(int offset, int size) {
 	// Load the specified data block
 	playSoundData(loadData(offset, size));
diff --git a/engines/mads/nebular/core/asound.h b/engines/mads/nebular/core/asound.h
index e3c5acf2f3e..c819d75c2c1 100644
--- a/engines/mads/nebular/core/asound.h
+++ b/engines/mads/nebular/core/asound.h
@@ -131,18 +131,11 @@ struct RegisterValue {
 #define ADLIB_CHANNEL_MIDWAY 5
 #define CALLBACKS_PER_SECOND 60
 
-struct CachedDataEntry {
-	int _offset;
-	byte *_data;
-	byte *_dataEnd;
-};
-
 /**
  * Base class for the sound player resource files
  */
 class ASound : public SoundDriver {
 private:
-	Common::List<CachedDataEntry> _dataCache;
 	uint16 _randomSeed;
 	int _masterVolume;
 
@@ -232,12 +225,6 @@ protected:
 	 */
 	void resultCheck();
 
-	/**
-	 * Loads a data block from the sound file, caching the result for any future
-	 * calls for the same data
-	 */
-	byte *loadData(int offset, int size);
-
 	/**
 	 * Play the specified sound
 	 * @param offset	Offset of sound data within sound player data segment
@@ -292,10 +279,8 @@ public:
 	AdlibChannelData _channelData[11];
 	Common::Array<AdlibSample> _samples;
 	AdlibSample *_samplePtr;
-	Common::File _soundFile;
 	Common::Queue<RegisterValue> _queue;
 	Common::Mutex _driverMutex;
-	int _dataOffset;
 	int _frameCounter;
 	bool _isDisabled;
 	int _v1;
@@ -331,7 +316,8 @@ public:
 	/**
 	 * Destructor
 	 */
-	~ASound() override;
+	~ASound() override {
+	}
 
 	/**
 	 * Validates the Adlib sound files


Commit: cdb6a04607a995afe46d2baca1deb3cdaeb4b733
    https://github.com/scummvm/scummvm/commit/cdb6a04607a995afe46d2baca1deb3cdaeb4b733
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:44+10:00

Commit Message:
MADS: PHANTOM: New ASound class implementation

The Phantom sound driver is somewhat different than Rex. So this
is a fresh reimplementation, with the assistance of Claude
converting the assembly to C. Though it still needs further
debugging work, as it doesn't produce sound yet.

Changed paths:
    engines/mads/core/sound_manager.cpp
    engines/mads/core/sound_manager.h
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h
    engines/mads/madsv2/phantom/sound_phantom.cpp
    engines/mads/madsv2/phantom/sound_phantom.h
    engines/mads/nebular/core/asound.cpp
    engines/mads/nebular/core/asound.h
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h


diff --git a/engines/mads/core/sound_manager.cpp b/engines/mads/core/sound_manager.cpp
index 658b9e832a9..97cd10df4f4 100644
--- a/engines/mads/core/sound_manager.cpp
+++ b/engines/mads/core/sound_manager.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "audio/fmopl.h"
+#include "common/file.h"
 #include "common/memstream.h"
 #include "mads/core/sound_manager.h"
 
@@ -123,42 +124,46 @@ void SoundManager::noise() {
 
 //====================================================================
 
-SoundDriver::SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset) :
-	_mixer(mixer), _opl(opl), _dataOffset(dataOffset) {
+SoundDriver::SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize) : _mixer(mixer), _opl(opl) {
 	// Open up the appropriate sound file
-	if (!_soundFile.open(filename))
+	Common::File soundFile;
+	if (!soundFile.open(filename))
 		error("Could not open file - %s", filename.toString().c_str());
+
+	_soundData.resize(dataSize);
+	soundFile.seek(dataOffset);
+	soundFile.read(&_soundData[0], dataSize);
 }
 
 SoundDriver::~SoundDriver() {
 	_opl->stop();
-
-	Common::List<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
-		delete[](*i)._data;
 }
 
 byte *SoundDriver::loadData(int offset, int size) {
-	// First scan for an existing copy
-	Common::List<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
-		CachedDataEntry &e = *i;
-		if (e._offset == offset)
-			return e._data;
+	byte *ptr = &_soundData[offset];
+
+	// Check for an existing cache entry
+	uint idx;
+	for (idx = 0; idx < _dataCache.size() && _dataCache[idx]._dataStart != ptr; ++idx) {
 	}
 
-	// No existing entry found, so load up data and store as a new entry
-	CachedDataEntry rec;
-	rec._offset = offset;
-	rec._data = new byte[size];
-	rec._dataEnd = rec._data + size - 1;
-	_soundFile.seek(_dataOffset + offset);
-	_soundFile.read(rec._data, size);
-	_dataCache.push_back(rec);
+	if (idx == _dataCache.size())
+		_dataCache.push_back(CachedDataEntry(ptr, size));
 
-	// Return the data
-	return rec._data;
+	// Return the data pointer
+	return ptr;
 }
 
+SoundDriver::CachedDataEntry &SoundDriver::getCachedData(byte *pData) {
+	Common::Array<CachedDataEntry>::iterator i;
+	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
+		CachedDataEntry &e = *i;
+		if (e._dataStart == pData)
+			return e;
+	}
+
+	error("Could not find previously loaded data");
+}
 
 } // namespace MADS
diff --git a/engines/mads/core/sound_manager.h b/engines/mads/core/sound_manager.h
index 7b86e6c294f..6ea9d5588c5 100644
--- a/engines/mads/core/sound_manager.h
+++ b/engines/mads/core/sound_manager.h
@@ -23,7 +23,7 @@
 #define MADS_CORE_SOUND_MANAGER_H
 
 #include "common/array.h"
-#include "common/file.h"
+#include "common/memstream.h"
 #include "common/mutex.h"
 #include "common/queue.h"
 
@@ -37,30 +37,53 @@ class OPL;
 
 namespace MADS {
 
-struct CachedDataEntry {
-	int _offset;
-	byte *_data;
-	byte *_dataEnd;
-};
+#define CALLBACKS_PER_SECOND 60
 
 class SoundDriver {
+protected:
+	struct CachedDataEntry {
+		byte *_dataStart = nullptr;
+		byte *_dataEnd = nullptr;
+		CachedDataEntry(byte *dataStart, size_t size) : _dataStart(dataStart),
+			_dataEnd(dataStart + size - 1) {
+		}
+	};
+
+private:
+	Common::Array<CachedDataEntry> _dataCache;
+
 protected:
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
-	Common::File _soundFile;
-	Common::List<CachedDataEntry> _dataCache;
-	int _dataOffset;
+	Common::Array<byte> _soundData;
 
 	/**
-	 * Loads a data block from the sound file, caching the result for any future
-	 * calls for the same data
+	 * Returns data for the specified offset. It also caches the data size for that
+	 * offset, for any future references that need it.
 	 */
 	byte *loadData(int offset, int size);
 
+	/**
+	 * Gets a stream starting at a given offset in the loaded sound data
+	 */
+	Common::MemoryReadStream getDataStream(int offset) const {
+		return Common::MemoryReadStream(&_soundData[offset], _soundData.size() - offset);
+	}
+
+	int getDataOffset(byte *ptr) const {
+		return ptr - &_soundData[0];
+	}
+
 public:
-	SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
+	SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize);
 	virtual ~SoundDriver();
 
+	/**
+	 * Return the cached data block record for previously loaded sound data
+	 */
+	CachedDataEntry &getCachedData(byte *pData);
+
 	/**
 	 * Execute a player command. Most commands represent sounds to play, but some
 	 * low number commands also provide control operations.
@@ -77,9 +100,7 @@ public:
 	/**
 	 * Main poll method to allow sounds to progress
 	 */
-	virtual int poll() {
-		return 0;
-	}
+	virtual int poll() = 0;
 
 	/**
 	 * General noise/note output
@@ -177,7 +198,6 @@ public:
 	//@}
 };
 
-
 } // namespace MADS
 
 #endif
diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 8a1fa4339e8..20982cc73fe 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -19,12 +19,1827 @@
  *
  */
 
+#include "audio/fmopl.h"
 #include "mads/madsv2/core/asound.h"
 
 namespace MADS {
 namespace MADSV2 {
 
+bool AdlibChannel::_isDisabled;
 
+/* =========================================================================
+ * Lookup tables
+ * ========================================================================= */
+
+ /*
+  * byte_12072 - Patch-attenuation to OPL total-level conversion table.
+  * Index is the patchAttenuation value (0-127); output is the 6-bit TL field.
+  */
+static const uint8 byte_12072[128] = {
+	0x3F,0x36,0x31,0x2D,0x2A,0x28,0x26,0x24,0x22,0x21,0x20,
+	0x1F,0x1E,0x1D,0x1C,0x1B,0x1A,0x19,0x19,0x18,0x17,0x17,
+	0x16,0x16,0x15,0x15,0x14,0x14,0x13,0x13,0x12,0x12,0x12,
+	0x11,0x11,0x10,0x10,0x10,0x0F,0x0F,0x0F,0x0E,0x0E,0x0E,
+	0x0E,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C,0x0B,0x0B,0x0B,
+	0x0B,0x0B,0x0A,0x0A,0x0A,0x0A,0x0A,0x09,0x09,0x09,0x09,
+	0x09,0x08,0x08,0x08,0x08,0x08,0x07,0x07,0x07,0x07,0x07,
+	0x07,0x06,0x06,0x06,0x06,0x06,0x06,0x05,0x05,0x05,0x05,
+	0x05,0x05,0x05,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x03,
+	0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x02,0x02,0x02,0x02,
+	0x02,0x02,0x02,0x02,0x02,0x01,0x01,0x01,0x01,0x01,0x01,
+	0x01,0x01,0x01,0x01,0x00,0x00,0x00
+};
+
+/*
+ * byte_120F2 - Volume/velocity to OPL attenuation step table.
+ * 128 entries; each group of 4 maps to the same step value (1..32).
+ */
+static const uint8 byte_120F2[128] = {
+	0x01,0x01,0x01,0x01, 0x02,0x02,0x02,0x02, 0x03,0x03,0x03,0x03,
+	0x04,0x04,0x04,0x04, 0x05,0x05,0x05,0x05, 0x06,0x06,0x06,0x06,
+	0x07,0x07,0x07,0x07, 0x08,0x08,0x08,0x08, 0x09,0x09,0x09,0x09,
+	0x0A,0x0A,0x0A,0x0A, 0x0B,0x0B,0x0B,0x0B, 0x0C,0x0C,0x0C,0x0C,
+	0x0D,0x0D,0x0D,0x0D, 0x0E,0x0E,0x0E,0x0E, 0x0F,0x0F,0x0F,0x0F,
+	0x10,0x10,0x10,0x10, 0x11,0x11,0x11,0x11, 0x12,0x12,0x12,0x12,
+	0x13,0x13,0x13,0x13, 0x14,0x14,0x14,0x14, 0x15,0x15,0x15,0x15,
+	0x16,0x16,0x16,0x16, 0x17,0x17,0x17,0x17, 0x18,0x18,0x18,0x18,
+	0x19,0x19,0x19,0x19, 0x1A,0x1A,0x1A,0x1A, 0x1B,0x1B,0x1B,0x1B,
+	0x1C,0x1C,0x1C,0x1C, 0x1D,0x1D,0x1D,0x1D, 0x1E,0x1E,0x1E,0x1E,
+	0x1F,0x1F,0x1F,0x1F, 0x20,0x20,0x20,0x20
+};
+
+/* Carrier operator slot offset per voice (byte_12190, index 0 = unused pad) */
+static const uint8 byte_12190[18] = {
+	0,3,1,4,2,5,6,9,10,8,11,12,15,13,16,14,17,0
+};
+
+/*
+ * byte_12191 - Modulator operator slot offset per voice.
+ * 17 entries used (voices 0-8 -> modulator slots, voices 0-8 -> carrier slots).
+ */
+static const uint8 byte_12191[17] = {
+	3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17
+};
+
+/*
+ * byte_121A2 - Operator slot index to OPL register offset.
+ * Maps an operator slot (0-17) to its base register offset within a group.
+ */
+static const uint8 byte_121A2[18] = {
+	0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21
+};
+
+/*
+ * word_12174 - F-number table: one entry per semitone within an octave.
+ * 12 entries (C through B).
+ */
+static const uint16 word_12174[12] = {
+	0x0200,0x021E,0x023F,0x0261,0x0285,0x02AB,
+	0x02D4,0x02FF,0x032D,0x035D,0x0390,0x03C7
+};
+
+/* Null / silence sound data pointer placeholder */
+static uint8 ADLIB_NULLDATA[] = {
+	0x00, 0x01, 0xF3, 0x00, 0x00, 0xEF, 0x00, 0x00,
+	0xF5, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF8, 0x1D,
+	0xFF, 0x00, 0xF4, 0x6E, 0x2A, 0x1C, 0xF4, 0x5F,
+	0x2A, 0x1C, 0x2A, 0x1C, 0x2A, 0x1C, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00
+};
+
+// =========================================================================
+
+void AdlibChannel::reset() {
+	_activeCount = 0;
+	_pitchBend = 0;
+	_volumeFadeStep = 0;
+	_vibratoDepth = 0;
+	_freqSweepCounter = 0;
+	_noiseFreqMask = 0;
+	_freqAccum = 0;
+	_freqStep = 0;
+	_loopStartPtr = 0;
+	_pSrc = 0;
+}
+
+void AdlibChannel::load(byte *soundData) {
+	_isDisabled = true;
+
+	memset(this, 0, sizeof(AdlibChannel));
+
+	_loopStartPtr = soundData;
+	_pSrc = soundData;
+	_innerLoopPtr = soundData;
+	_outerLoopPtr = soundData;
+	_soundData = soundData;
+	_patchAttenuation = 0x40;
+	_activeCount = 1;
+
+	_isDisabled = false;
+}
+
+void AdlibChannel::setPtr2(byte *ptr) {
+	_loopStartPtr = ptr;
+	_pSrc = ptr;
+	_volumeFadeStep = 0xFF;   /* -1 signed: fade down */
+	_fadePeriodReload = 1;
+	_fadePeriodCounter = 1;
+}
+
+void AdlibChannel::enable() {
+	if (_activeCount == 0)
+		return;
+	_pendingStop = 0xFF;
+	_soundData = ADLIB_NULLDATA;
+}
+
+void AdlibChannel::processChannelFade() {
+	if (_activeCount == 0)
+		return;
+	if (_pendingStop == 0)
+		return;
+
+	/* If both velocity and volume are zero the sound has fully faded */
+	if (_velocity == 0 && _volume == 0) {
+		_loopStartPtr = ADLIB_NULLDATA;
+		_pSrc = ADLIB_NULLDATA;
+		_pendingStop = 0;
+		return;
+	}
+
+	/* Arm a fast fade-down */
+	_volumeFadeStep = 0xFF;   /* -1: decrement each period */
+	_fadePeriodReload = 2;
+	if (_fadePeriodCounter == 0)
+		_fadePeriodCounter = 1;
+}
+
+// =========================================================================
+
+ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize) :
+		SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
+	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
+}
+
+int ASound::stop() {
+	command0();
+	int result = _pollResult;
+	_pollResult = 0;
+	return result;
+}
+
+int ASound::poll() {
+	// Update any playing sounds
+	update();
+
+	// Return result
+	int result = _pollResult;
+	_pollResult = 0;
+	return result;
+}
+
+void ASound::noise() {
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; i++)
+		noise_inner(i);
+}
+
+void ASound::playSound(int offset, int size) {
+	findFreeChannelFull(loadData(offset, size));
+}
+
+/* =========================================================================
+ * Shared Commands
+ * ========================================================================= */
+
+int ASound::command0() {
+	int i;
+
+	/* 1. Reset all 9 channels */
+	for (i = 0; i < ADLIB_CHANNEL_COUNT; i++)
+		_channels[i]->reset();
+
+	/* 2. Registers 0x40-0x4F: set total-level to maximum (0x3F) combined
+	 *    with whatever flags were already there (asm used OR with 0x3F then
+	 *    writes 0x3F-remaining).  The reset state is simply full attenuation. */
+	for (int reg = 0x4F; reg >= 0x40; reg--)
+		write(reg, 0x3F);
+
+	/* 3. Registers 0x60-0xFF: clear (silence envelope / frequency / etc.) */
+	for (int reg = 0xFF; reg >= 0x60; reg--)
+		write(reg, 0x00);
+
+	/* 4. Registers 0x01-0x3F: clear */
+	for (int reg = 0x3F; reg >= 0x01; reg--)
+		write(reg, 0x00);
+
+	/* 5. Waveform Select Enable */
+	write(0x01, 0x20);
+
+	/* 6. Reset callback counters */
+	resetCallback();
+
+	return 0;
+}
+
+int ASound::command1() {
+	command3();
+	command5();
+
+	return 0;
+}
+
+int ASound::command2() {
+	_channel0.setPtr2(ADLIB_NULLDATA);
+	_channel1.setPtr2(ADLIB_NULLDATA);
+	_channel2.setPtr2(ADLIB_NULLDATA);
+	_channel3.setPtr2(ADLIB_NULLDATA);
+	_channel4.setPtr2(ADLIB_NULLDATA);
+	_channel5.setPtr2(ADLIB_NULLDATA);
+
+	return 0;
+}
+
+int ASound::command3() {
+	_channel0.enable();
+	_channel1.enable();
+	_channel2.enable();
+	_channel3.enable();
+	_channel4.enable();
+	_channel5.enable();
+
+	return 0;
+}
+
+int ASound::command4() {
+	_channel6.setPtr2(ADLIB_NULLDATA);
+	_channel7.setPtr2(ADLIB_NULLDATA);
+	_channel8.setPtr2(ADLIB_NULLDATA);
+
+	return 0;
+}
+
+int ASound::command5() {
+	_channel6.enable();
+	_channel7.enable();
+	_channel8.enable();
+
+	return 0;
+}
+
+int ASound::command6() {
+	_isDisabled = true;
+
+	/* Save sweep counters */
+	_channel0._savedFreqSweep = _channel0._freqSweepCounter;
+	_channel1._savedFreqSweep = _channel1._freqSweepCounter;
+	_channel2._savedFreqSweep = _channel2._freqSweepCounter;
+	_channel3._savedFreqSweep = _channel3._freqSweepCounter;
+	_channel4._savedFreqSweep = _channel4._freqSweepCounter;
+	byte_16B5D = byte_16B5C;   /* channel5 shadow */
+	_channel6._savedFreqSweep = _channel6._freqSweepCounter;
+	_channel7._savedFreqSweep = _channel7._freqSweepCounter;
+	_channel8._savedFreqSweep = _channel8._freqSweepCounter;
+
+	/* Zero sweep counters */
+	_channel0._freqSweepCounter = 0;
+	_channel1._freqSweepCounter = 0;
+	_channel2._freqSweepCounter = 0;
+	_channel3._freqSweepCounter = 0;
+	_channel4._freqSweepCounter = 0;
+	byte_16B5C = 0;
+	_channel6._freqSweepCounter = 0;
+	_channel7._freqSweepCounter = 0;
+	_channel8._freqSweepCounter = 0;
+
+	/*
+	 * Mute output operators for voices 3-5 (operator registers 0x43-0x45,
+	 * 0x4B-0x4D, 0x53-0x55 in the OPL2 register map).
+	 * These correspond to the modulator operators for channels 3, 4, 5 and
+	 * their carrier operators.
+	 */
+	adlib_channelOff(0x43);
+	adlib_channelOff(0x44);
+	adlib_channelOff(0x45);
+	adlib_channelOff(0x4B);
+	adlib_channelOff(0x4C);
+	adlib_channelOff(0x4D);
+	adlib_channelOff(0x53);
+	adlib_channelOff(0x54);
+	adlib_channelOff(0x55);
+
+	return 0;
+}
+
+int ASound::command7() {
+	/* Restore operator volumes */
+	adlib_channelOn(67, _channel0._volume);
+	adlib_channelOn(68, _channel1._volume);
+	adlib_channelOn(69, _channel2._volume);
+	adlib_channelOn(75, _channel3._volume);
+	adlib_channelOn(76, _channel4._volume);
+	adlib_channelOn(77, _channel5._volume);
+
+	/* Check if any sweep counter was saved (OR all savedFreqSweep fields) */
+	uint8 anySweep =
+		_channel0._savedFreqSweep |
+		_channel1._savedFreqSweep |
+		_channel2._savedFreqSweep |
+		_channel3._savedFreqSweep |
+		_channel4._savedFreqSweep |
+		byte_16B5D |
+		_channel6._savedFreqSweep |
+		_channel7._savedFreqSweep |
+		_channel8._savedFreqSweep;
+
+	if (anySweep != 0) {
+		/* Re-save live sweep counters (which are currently 0 from command6) */
+		_channel0._savedFreqSweep = _channel0._freqSweepCounter;
+		_channel1._savedFreqSweep = _channel1._freqSweepCounter;
+		_channel2._savedFreqSweep = _channel2._freqSweepCounter;
+		_channel3._savedFreqSweep = _channel3._freqSweepCounter;
+		_channel4._savedFreqSweep = _channel4._freqSweepCounter;
+		byte_16B5D = byte_16B5C;
+		_channel6._savedFreqSweep = _channel6._freqSweepCounter;
+		_channel7._savedFreqSweep = _channel7._freqSweepCounter;
+		_channel8._savedFreqSweep = _channel8._freqSweepCounter;
+		signalSoundPlaying();
+	}
+
+	_isDisabled = false;
+	return 0;
+}
+
+int ASound::command8() {
+	uint8 result = 0;
+	result |= _channel0._activeCount;
+	result |= _channel1._activeCount;
+	result |= _channel2._activeCount;
+	result |= _channel3._activeCount;
+	result |= _channel4._activeCount;
+	result |= (uint8)(_channel5._activeCount); /* byte_16B63 area */
+	result |= _channel6._activeCount;
+	result |= _channel7._activeCount;
+	result |= _channel8._activeCount;
+
+	return result;
+}
+
+void ASound::callFunction(uint16 offset) {
+	error("Unsupported call to sound driver function at offset %.4x", offset);
+}
+
+/* =========================================================================
+ * Support functions
+ * ========================================================================= */
+
+
+void ASound::write(uint8 reg, uint8 value) {
+	_queue.push(Common::Pair<byte, byte>(reg, value));
+}
+
+void ASound::onTimer() {
+	Common::StackLock slock(_driverMutex);
+	poll();
+	flush();
+}
+
+void ASound::flush() {
+	Common::StackLock slock(_driverMutex);
+
+	while (!_queue.empty()) {
+		auto v = _queue.pop();
+		_opl->writeReg(v.first, v.second);
+	}
+}
+
+uint16 ASound::getRandomNumber() {
+	uint16 ax = 0x9248 + _randomSeed;
+	/* ror ax, 3  (three right-rotates of a 16-bit value) */
+	ax = (ax >> 3) | (ax << 13);
+	_randomSeed = ax;
+	return ax;
+}
+
+void ASound::adlib_channelOff(uint8 portIndex) {
+	uint8 val = _adlibPorts[portIndex] | 0x3F;
+	write(portIndex, val);
+}
+
+void ASound::adlib_channelOn(uint8 portIndex, uint8 vol) {
+	uint8 val = (_adlibPorts[portIndex] & 0xC0) | (vol ^ 0x3F);
+	write(portIndex, val);
+}
+
+void ASound::signalSoundPlaying() {
+	if (_resultFlag == 1)
+		return;
+	_resultFlag = 1;
+	_pollResult = 1;
+}
+
+void ASound::clearCallback() {
+	word_12C06 = 0;
+	word_12C08 = 0;
+	word_12C0A = 0;
+}
+
+/* =========================================================================
+ * writeVolume
+ * Computes and writes the total-level (volume) registers for the current
+ * active channel to the OPL chip.
+ *
+ * Two paths depending on _adlib_v5660_2:
+ *   < 0x18  -> simple linear mapping of (volume + velocity) -> TL
+ *   >= 0x18 -> patch-attenuation-aware mapping using byte_12072 table
+ * ========================================================================= */
+
+void ASound::writeVolume() {
+	AdlibChannel *ch = _activeChannelPtr;
+	int16_t var4, var6 = 0;
+
+	/* Step 1: map volume through byte_120F2 */
+	int16_t volStep = (int16_t)byte_120F2[(uint8)ch->_volume];
+	/* Step 2: map velocity through byte_120F2 */
+	int16_t velStep = (int16_t)byte_120F2[(int8_t)ch->_velocity];
+	int16_t var8 = volStep + velStep - 1;   /* combined attenuation step */
+
+	/* Determine carrier operator register for this voice */
+	uint8 chanNum = _activeChannelNumber;
+	uint8 slot = byte_12191[chanNum * 2];          /* modulator slot index */
+	uint8 regOff = byte_121A2[slot];
+	uint16 siReg = (uint16)regOff + 0x40;         /* TL register base */
+
+	/* KSL flags from port shadow */
+	uint8 kslBits = _adlibPorts[siReg] & 0xC0;
+
+	if (_adlib_v5660_2 < 0x18) {
+		/* ---- OPL2 simple path ---- */
+		if (var8 < 0)  var8 = 0;
+		if (var8 > 63) var8 = 63;
+		var4 = (0x3F - var8) | kslBits;
+		write((uint8)siReg, (uint8)var4);
+	} else {
+		/* ---- OPL3 patch-attenuation path ---- */
+		/* Modulator TL */
+		int16_t patchAtt = (int16_t)ch->_patchAttenuation;
+		int16_t tl1 = byte_12072[patchAtt];
+		var4 = -(tl1 - var8);           /* negate of (table[patchAtt] - combined) */
+		if (var4 < 0)  var4 = 0;
+		if (var4 > 63) var4 = 63;
+		var4 = (0x3F - var4) | kslBits;
+		write((uint8)siReg, (uint8)var4);
+
+		/* Carrier TL (uses complementary lookup at unk_120F1 - patchAtt) */
+		int16_t tl2 = byte_12072[127 - patchAtt];   /* unk_120F1 offset */
+		var6 = -(tl2 - var8);
+		if (var6 < 0)  var6 = 0;
+		if (var6 > 63) var6 = 63;
+		/* carrier register */
+		uint8 cslot = byte_12190[chanNum * 2];
+		uint8 cregOff = byte_121A2[cslot];
+		uint16 cReg = (uint16)cregOff + 0x40;
+		uint8  cksl = _adlibPorts[cReg] & 0xC0;
+		var6 = (0x3F - var6) | cksl;
+		write((uint8)(siReg + 2), (uint8)var4);   /* mod in OPL3 second pair */
+		write((uint8)cReg, (uint8)var6);
+	}
+
+	/* Cache the written TL bytes in the channel struct */
+	ch->_opVolBytes = (uint8)(var4 & 0x3F);
+	ch->_opVolBytes2 = (uint8)(var6 & 0x3F);
+
+	/* ---- Second operator (alg == 0 check) ---- */
+	int sampleIdx = (int)ch->_sampleIndex;
+	AdlibSample *smp = &_samples[sampleIdx];
+	if (smp->_alg != 0)
+		return;   /* FM: only one operator carries volume */
+
+	/* Second operator TL register */
+	uint8 slot2 = byte_12190[chanNum * 2];
+	uint8 roff2 = byte_121A2[slot2];
+	uint16 siReg2 = (uint16)roff2 + 0x40;
+	uint8  ksl2 = _adlibPorts[siReg2] & 0xC0;
+
+	int16_t tl_s = (int16_t)smp->_totalLevel - 63;
+	tl_s = -tl_s;   /* neg */
+	int16_t var8b = (int16_t)var8 - tl_s;   /* subtract from combined */
+
+	if (_adlib_v5660_2 < 0x18) {
+		if (var8b < 0)  var8b = 0;
+		if (var8b > 63) var8b = 63;
+		int16_t val2 = (0x3F - var8b) | ksl2;
+		write((uint8)siReg2, (uint8)val2);
+	} else {
+		int16_t patchAtt2 = (int16_t)ch->_patchAttenuation;
+		int16_t tl1b = byte_12072[patchAtt2];
+		var4 = -(tl1b - var8b);
+		if (var4 < 0)  var4 = 0;
+		if (var4 > 63) var4 = 63;
+		var4 = (0x3F - var4) | ksl2;
+		write((uint8)siReg2, (uint8)var4);
+
+		int16_t tl2b = byte_12072[127 - patchAtt2];
+		var6 = -(tl2b - var8b);
+		if (var6 < 0)  var6 = 0;
+		if (var6 > 63) var6 = 63;
+		var6 = (0x3F - var6) | ksl2;
+		write((uint8)(siReg2 + 2), (uint8)var4);
+		write((uint8)(siReg2 + 0), (uint8)var6);
+	}
+}
+
+void ASound::writeFrequency() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8  chanNum = _activeChannelNumber;
+	uint16 aReg = (uint16)chanNum + 0xA0;   /* freq-low register */
+	uint16 bReg = aReg + 0x10;                /* freq-high / keyon register */
+
+	/* Note number (1-based), adjusted by octaveTranspose, clamped */
+	int note = (int)ch->_note + (int)ch->_octaveTranspose - 1;
+
+	/* Octave = note / 12 */
+	uint8 octave = (uint8)(note / 12);
+
+	/* Semitone within octave */
+	int semi = note % 12;
+	if (semi < 0) {
+		semi += 12; octave--;
+	}
+
+	/* F-number from table, offset by transpose */
+	int16_t fnumLow = (int16_t)word_12174[semi] + (int16_t)(int8_t)ch->_transpose;
+
+	/* Write F-number low byte to 0xA0+ch */
+	write((uint8)aReg, (uint8)fnumLow);
+
+	/* Build high register: preserve key-on bit, add F-num high bits and octave */
+	uint8 bVal = _adlibPorts[bReg] & 0x20;        /* keep key-on */
+	bVal |= (uint8)((fnumLow >> 8) & 0x03);       /* F-num bits 8-9 */
+	bVal |= (uint8)(octave << 2);                 /* octave in bits 2-4 */
+	write((uint8)bReg, bVal);
+}
+
+void ASound::writePitchBend() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8  chanNum = _activeChannelNumber;
+	uint16 aReg = (uint16)chanNum + 0xA0;
+	uint16 bReg = aReg + 0x10;
+
+	/* Reconstruct 10-bit F-number from port shadow */
+	uint16 fnum = (uint16)_adlibPorts[aReg]
+		| ((uint16)(_adlibPorts[bReg] & 0x1F) << 8);
+
+	/* Add signed pitch bend */
+	int16_t bent = (int16_t)fnum + (int16_t)(int8_t)ch->_pitchBend;
+
+	/* Write low byte */
+	write((uint8)aReg, (uint8)bent);
+
+	/* Write high byte: keep key-on and octave bits, update F-num[9:8] */
+	uint8 bVal = _adlibPorts[bReg] & 0x20;
+	bVal |= (uint8)((bent >> 8) & 0x03);
+	write((uint8)bReg, bVal);
+}
+
+void ASound::updateOctave() {
+	uint8  chanNum = _activeChannelNumber;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+	uint8  val = _adlibPorts[bReg] & 0xDF;   /* clear bit 5 */
+	write((uint8)bReg, val);
+}
+
+void ASound::noteOn() {
+	AdlibChannel *ch = _activeChannelPtr;
+
+	writeVolume();
+
+	int sampleIdx = (int)ch->_sampleIndex;
+	AdlibSample *smp = &_samples[sampleIdx];
+	ch->_freqSweepCounter = smp->_freqSweepInit;
+
+	if (ch->_freqSweepCounter != 0) {
+		/* Sweep active: signal playing but don't key on yet */
+		signalSoundPlaying();
+		return;
+	}
+
+	/* No sweep: write frequency and key on */
+	writeFrequency();
+
+	uint8  chanNum = _activeChannelNumber;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+	uint8  bVal = _adlibPorts[bReg] | 0x20;   /* set key-on bit */
+	write((uint8)bReg, bVal);
+}
+
+void ASound::writeSampleRegs() {
+	AdlibSample *smp = _samplePtr;
+	uint16 base = word_16CF6;
+
+	/* --- Register 0xBD: Percussion/tremolo/vibrato depth --- */
+	uint8 bdVal = 0;
+	if (byte_1218C != 1) bdVal |= 0x80;
+	if (byte_1218D != 1) bdVal |= 0x40;
+	bdVal |= _adlibPorts[0xBD] & 0x3F;
+	write(0xBD, bdVal);
+
+	/* --- Register 0x08: Note-select (CSM/keyboardsplit) --- */
+	uint8 ns = (byte_1218E != 1) ? 0x40 : 0x00;
+	write(0x08, ns);
+
+	/* --- Register 0xC0+voice: Feedback / algorithm --- */
+	uint8 fb = (uint8)(smp->_feedback << 1);
+	if (smp->_vib) fb |= 0x01;   /* _alg field in struct */
+	write((uint8)(_activeChannelReg + 0xC0), fb);
+
+	/* --- Register 0x60+base: Attack/Decay --- */
+	uint8 ad = (uint8)((smp->_attackRate << 4) | (smp->_decayRate & 0x0F));
+	write((uint8)(base + 0x60), ad);
+
+	/* --- Register 0x80+base: Sustain/Release --- */
+	uint8 sr = (uint8)((smp->_sustainLevel << 4) | (smp->_releaseRate & 0x0F));
+	write((uint8)(base + 0x80), sr);
+
+	/* --- Register 0x20+base: Tremolo/vibrato/sustain/KSR/multiplier --- */
+	uint8 am = 0;
+	if (smp->_egTyp != 1) am |= 0x20;
+	if (smp->_ksr != 1) am |= 0x10;
+	if (smp->_ampMod != 1) am |= 0x80;
+	if (smp->_vib != 1) am |= 0x40;   /* NB: vib doubles as "AM" flag here */
+	am |= smp->_freqMultiple & 0x0F;
+	write((uint8)(base + 0x20), am);
+
+	/* --- Register 0xE0+base: Waveform select --- */
+	write((uint8)(base + 0xE0), (uint8)(smp->_waveformSelect & 0x03));
+}
+
+void ASound::loadSample() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+
+	/* --- Silence modulator operator first --- */
+	uint8 mSlot = byte_12191[chanNum * 2];
+	uint8 mReg = byte_121A2[mSlot];
+	word_16CF6 = mReg;
+
+	write((uint8)(word_16CF6 + 0x80), 0xFF);  /* max release */
+	write((uint8)(word_16CF6 + 0x40), 63);    /* full attenuation */
+
+	/* --- Silence carrier operator --- */
+	uint8 cSlot = byte_12190[chanNum * 2];
+	uint8 cReg = byte_121A2[cSlot];
+	word_16CF6 = (uint16)cReg;
+
+	write((uint8)(word_16CF6 + 0x80), 0xFF);
+	write((uint8)(word_16CF6 + 0x40), 63);
+
+	/* --- Set active register for C0 writes --- */
+	_activeChannelReg = (uint16)chanNum;
+
+	/* --- Point samplePtr at operator 1 data and write registers --- */
+	int sampleIdx = (int)ch->_sampleIndex;
+	_samplePtr = &_samples[sampleIdx];
+
+	/* Modulator operator regs (operator 1) */
+	uint8 ms1 = byte_12191[chanNum * 2];
+	uint8 mr1 = byte_121A2[ms1];
+	word_16CF6 = (uint16)mr1;
+	writeSampleRegs();
+
+	/* Carrier operator regs (operator 2, offset by 490 bytes = next pair) */
+	_samplePtr = (AdlibSample *)((uint8 *)&_samples[sampleIdx] + 490);
+
+	uint8 ms2 = byte_12190[chanNum * 2];
+	uint8 mr2 = byte_121A2[ms2];
+	word_16CF6 = (uint16)mr2;
+	writeSampleRegs();
+
+	/* Write carrier TL: noteOffset shifts the level */
+	AdlibSample *smp2 = _samplePtr;
+	uint8 noteOffBits = (uint8)((smp2->_attackRate << 6) | 0x3F);
+	write((uint8)(word_16CF6 + 0x40), noteOffBits);
+
+	/* --- Third operator (if present, offset 0x2C * sampleIndex[5]) --- */
+	int smp3idx = (int)ch->_noteOffset;
+	_samplePtr = &_samples[smp3idx * 2];
+
+	uint8 s3slot = byte_12190[chanNum * 2];
+	uint8 s3reg = byte_121A2[s3slot];
+	uint16 s3Reg = (uint16)s3reg + 0x40;
+
+	AdlibSample *smp3 = _samplePtr;
+	if (smp3->_alg == 0) {
+		/* FM: write scaling level + max TL */
+		uint8 scl3 = (uint8)((smp3->_scalingLevel << 6) | 0x3F);
+		write((uint8)s3Reg, scl3);
+	} else {
+		/* Additive: write actual total level */
+		int16_t tl3 = (int16_t)smp3->_totalLevel - 63;
+		tl3 = -tl3;
+		uint8 scl3 = (uint8)((smp3->_scalingLevel << 6) | (tl3 & 0x3F));
+		write((uint8)s3Reg, scl3);
+	}
+
+	/* Copy sweep / freq parameters from sample into channel */
+	ch->_freqSweepCounter = smp3->_freqSweepInit;
+	ch->_noiseFreqMask = smp3->_freqMask;
+	ch->_freqAccum = smp3->_freqBase;
+	ch->_freqStep = smp3->_outerLoopPtr;
+}
+
+void ASound::update() {
+	if (!_isDisabled) {
+		(void)getRandomNumber();
+		++_frameNumber2;
+	}
+}
+
+void ASound::update1(int channelIndex) {
+	AdlibChannel *ch = _channels[channelIndex];
+
+	if (ch->_freqSweepCounter == 0)
+		return;
+
+	byte_12036 = 1;
+	ch->_freqAccum += ch->_freqStep;
+	ch->_freqSweepCounter--;
+
+	if (ch->_freqSweepCounter != 0)
+		return;
+
+	/* Sweep finished: write zeroes to the frequency registers */
+	uint8 voice = (uint8)channelIndex;
+	write((uint8)(voice + 0xA0), 0x00);
+	write((uint8)(voice + 0xB0), 0x00);
+}
+
+void ASound::setFrequency(uint8 voice, uint16 freq) {
+	write((uint8)(voice + 0xA0), (uint8)(freq & 0xFF));
+	uint8 bVal = (_adlibPorts[voice + 0xB0] & 0x20)
+		| (uint8)((freq >> 8) & 0x03)
+		| 0x20;   /* key-on */
+	write((uint8)(voice + 0xB0), bVal);
+}
+
+void ASound::noise_inner(int channelIndex) {
+	AdlibChannel *ch = _channels[channelIndex];
+
+	if (ch->_freqSweepCounter == 0)
+		return;
+
+	uint16 rnd = getRandomNumber();
+	uint16 freq = (rnd & ch->_noiseFreqMask) + ch->_freqAccum;
+	setFrequency((uint8)channelIndex, freq);
+}
+
+void ASound::updateAllChannels() {
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		_channels[i]->processChannelFade();
+}
+
+void ASound::findFreeChannel(byte *soundData) {
+	byte_12050 = 1;
+
+	for (int chan = 0; chan < 6; ++chan) {
+		if (_channels[chan]->_activeCount == 0) {
+			_channels[chan]->load(soundData);
+			return;
+		}
+	}
+
+	/* Fall through to full search (channels 6-8 + pending-stop channels) */
+	findFreeChannelFull(soundData);
+}
+
+void ASound::findFreeChannelFull(byte *soundData) {
+	int chan;
+	byte_12050 = 2;
+
+	// Check channels 6-8 for empty slots
+	for (chan = 6; chan < ADLIB_CHANNEL_COUNT; ++chan) {
+		if (_channels[chan]->_activeCount == 0) {
+			_channels[chan]->load(soundData);
+			return;
+		}
+	}
+
+	// Check channels 6-8 for pending-stop (can be pre-empted)
+	for (chan = ADLIB_CHANNEL_COUNT - 1; chan >= 6; --chan) {
+		if (_channels[chan]->_pendingStop == 0xff) {
+			_channels[chan]->load(soundData);
+			return;
+		}
+	}
+
+	// Fall through to check channels 0-5 pending-stop
+	if (byte_16B63 != 0xFF) {
+		for (chan = 4; chan >= 0; --chan) {
+			if (_channels[chan]->_pendingStop == 0xff) {
+				_channels[chan]->load(soundData);
+				return;
+			}
+		}
+	}
+
+	// No free channel found - sound is dropped
+}
+
+void ASound::pollAllChannels() {
+	_activeChannelNumber = 0;
+	for (int chan = 0; chan < ADLIB_CHANNEL_COUNT; ++chan) {
+		_activeChannelPtr = _channels[chan];
+		pollActiveChannel();
+	}
+}
+
+void ASound::pollActiveChannel() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8_t var_8 = 0;		// volume-dirty flag
+	uint16 var_6;			// temp word
+	uint16_t var_4 = 0;		// temp word 2
+	uint16_t var_A = 0;		// temp word 3
+	uint16_t var_C = 0;		// temp word 4
+
+	/* Channel inactive - nothing to do */
+	if (ch->_activeCount == 0) goto done;
+
+	/* Key-on delay countdown */
+	if (ch->_keyOnDelay != 0) {
+		ch->_keyOnDelay--;
+		if (ch->_keyOnDelay == 0)
+			updateOctave();
+	}
+
+	/* Decrement the duration counter; when it hits zero, fetch next command */
+	ch->_activeCount--;
+	if (ch->_activeCount != 0)
+		goto post_keyon;    /* still counting down - skip to pitch-bend / fade */
+
+	/* ---- top of command dispatch (def_11A34) ---- */
+dispatch:
+	{
+		/* Load pSrc from channel and read the first byte */
+		pSrc = ch->_pSrc;
+		uint8_t b = *pSrc;
+
+		if (!(b & 0x80)) {
+			/*
+			 * Note byte (bit 7 clear): two-byte format [note][duration].
+			 * Read note into _note, duration into _activeCount, advance pSrc.
+			 */
+			ch->_note = *pSrc;   /* b already */
+			pSrc++;
+			ch->_activeCount = *pSrc;
+			pSrc++;
+			ch->_pSrc += 2;
+
+			if (ch->_note == 0 || ch->_activeCount == 0) {
+				updateOctave();
+				goto post_keyon;
+			}
+			goto key_on;
+		}
+
+		/* High bit set - it is a command.  Commands above 0xBD go to switch. */
+		if (b > 0xBD) {
+			/* Decode the command opcode: ax = b - (-66) = b + 66          */
+			/* Switch range is 0..65 (opcodes -66 .. -1 mapped to 0..65)   */
+			int16_t ax = (int16_t)(int8_t)b;   /* sign-extend */
+			var_8 = 0;
+			ax = (int16_t)(ax - (-66));
+			if ((uint16_t)ax > 65) goto dispatch;  /* unknown - skip */
+
+			switch (ax) {
+
+				/* ---- opcode -1  (0xFF): inner loop ---- */
+			case 65:
+			{
+				if (ch->_innerLoopCount == 0) {
+					pSrc++;
+					uint8_t cnt = *pSrc;
+					if (cnt == 0) {
+						ch->_pSrc += 2;
+						ch->_innerLoopPtr = ch->_pSrc;
+						ch->_innerLoopCount = 0;
+						goto dispatch;
+					}
+					ch->_innerLoopCount = (uint16_t)cnt;
+				}
+				ch->_innerLoopCount--;
+				if (ch->_innerLoopCount == 0) {
+					ch->_pSrc += 2;
+					ch->_innerLoopPtr = ch->_pSrc;
+					goto dispatch;
+				}
+				ch->_pSrc = ch->_innerLoopPtr;
+				goto dispatch;
+			}
+
+			/* ---- opcode -2  (0xFE): outer loop ---- */
+			case 64:
+			{
+				if (ch->_outerLoopCount == 0) {
+					pSrc++;
+					uint8_t cnt = *pSrc;
+					if (cnt == 0) {
+						ch->_pSrc += 2;
+						ch->_outerLoopPtr = ch->_pSrc;
+						ch->_innerLoopPtr = ch->_pSrc;
+						ch->_innerLoopCount = 0;
+						ch->_outerLoopCount = 0;
+						goto dispatch;
+					}
+					ch->_outerLoopCount = (uint16_t)cnt;
+				}
+				ch->_outerLoopCount--;
+				if (ch->_outerLoopCount == 0) {
+					ch->_pSrc += 2;
+					ch->_outerLoopPtr = ch->_pSrc;
+					ch->_innerLoopPtr = ch->_pSrc;
+					goto dispatch;
+				}
+				ch->_pSrc = ch->_outerLoopPtr;
+				ch->_innerLoopPtr = ch->_outerLoopPtr;
+				goto dispatch;
+			}
+
+			/* ---- opcode -3  (0xFD): restart / reset all channel state ---- */
+			case 63:
+			{
+				ch->_loopStartPtr = ch->_soundData;
+				ch->_pSrc = ch->_soundData;
+				ch->_innerLoopPtr = ch->_soundData;
+				ch->_outerLoopPtr = ch->_soundData;
+				ch->_pitchBend = 0;
+				ch->_volumeFadeStep = 0;
+				ch->_vibratoDepth = 0;
+				ch->_transpose = 0;
+				ch->_velocity = 0;
+				ch->_opVolBytes = 0;
+				ch->_opVolBytes2 = 0;
+				ch->_volume = 0;
+				ch->_fadePeriodCounter = 0;
+				ch->_vibPeriodCounter = 0;
+				ch->_innerLoopCount = 0;
+				ch->_outerLoopCount = 0;
+				ch->_noteOffset = 0;
+				goto dispatch;
+			}
+
+			/* ---- opcode -4  (0xFC): set sound-data pointer (abs) ---- */
+			case 62:
+			{
+				byte *ptr = &_soundData[readWord_impl()];
+				ch->_loopStartPtr = ptr;
+				ch->_pSrc = ptr;
+				ch->_innerLoopPtr = ptr;
+				ch->_outerLoopPtr = ptr;
+				ch->_soundData = ptr;
+				goto dispatch;
+			}
+
+			/* ---- opcode -5  (0xFB): branch (unconditional) ---- */
+			case 61:
+			{
+				byte *ptr = &_soundData[readWord_impl()];
+				ch->_pSrc = ptr;
+				goto dispatch;
+			}
+
+			/* ---- opcode -6  (0xFA): branch-with-save ---- */
+			case 60:
+			{
+				byte *ptr = &_soundData[readWord_impl()];
+				ch->_branchTargetPtr = ch->_pSrc + 3;
+				ch->_pSrc = ptr;
+				goto dispatch;
+			}
+
+			/* ---- opcode -7  (0xF9): return-from-branch ---- */
+			case 59:
+			{
+				if (ch->_branchTargetPtr != 0) {
+					ch->_pSrc = ch->_branchTargetPtr;
+					ch->_branchTargetPtr = 0;
+				} else {
+					ch->_pSrc++;
+				}
+				goto dispatch;
+			}
+
+			/* ---- opcode -8  (0xF8): set sample / patch ---- */
+			case 58:
+			{
+				pSrc++;
+				ch->_sampleIndex = *pSrc;
+				ch->_pSrc += 2;
+				loadSample();
+				goto dispatch;
+			}
+
+			/* ---- opcode -9  (0xF7): set note (with 0 duration override) ---- */
+			case 57:
+			{
+				pSrc++;
+				ch->_noteOffset = *pSrc;
+				ch->_durationOverride = 0;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -10 (0xF6): set duration override ---- */
+			case 56:
+			{
+				pSrc++;
+				ch->_durationOverride = *pSrc;
+				ch->_noteOffset = 0;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -11 (0xF5): set pitch bend ---- */
+			case 55:
+			{
+				pSrc++;
+				ch->_pitchBend = *pSrc;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -12 (0xF4): set volume ---- */
+			case 52:
+			{
+				pSrc++;
+				uint8_t vol = *pSrc;
+				var_6 = (uint16_t)vol;
+				if (ch->_pendingStop != 0) {
+					/* Clamp: don't raise volume above current when fading out */
+					if ((uint16_t)ch->_volume <= var_6)
+						goto set_vol_patch;
+				}
+				ch->_volume = (uint8_t)var_6;
+				goto vol_advance;
+set_vol_patch:
+				ch->_volume = vol;
+vol_advance:
+				ch->_pSrc += 2;
+				var_8 = 1;
+				goto dispatch;
+			}
+
+			/* ---- opcode -13 (0xF3): set fade ---- */
+			case 51:
+			{
+				if (ch->_pendingStop != 0) {
+					ch->_pSrc += 3;
+					goto dispatch;
+				}
+				pSrc++;
+				ch->_fadePeriodReload = *pSrc;
+				pSrc++;
+				ch->_volumeFadeStep = *pSrc;
+				ch->_fadePeriodCounter = 1;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -14 (0xF2): set transpose ---- */
+			case 50:
+			{
+				pSrc++;
+				ch->_transpose = *pSrc;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -15 (0xF1): set velocity ---- */
+			case 49:
+			{
+				pSrc++;
+				uint8_t vel = *pSrc;
+				var_6 = (uint16_t)vel;
+				if (ch->_pendingStop != 0) {
+					if ((uint16_t)ch->_velocity > var_6)
+						ch->_velocity = vel;
+				} else {
+					ch->_velocity = vel;
+				}
+				var_8 = 1;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -16 (0xF0): set patch attenuation ---- */
+			case 48:
+			{
+				pSrc++;
+				ch->_patchAttenuation = *pSrc;
+				ch->_pSrc += 2;
+				var_8 = 1;
+				goto dispatch;
+			}
+
+			/* ---- opcode -17 (0xEF): set vibrato ---- */
+			case 47:
+			{
+				pSrc++;
+				ch->_vibPeriodReload = *pSrc;
+				pSrc++;
+				ch->_vibratoDepth = *pSrc;
+				ch->_vibPeriodCounter = 1;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -18 (0xEE): set octave transpose ---- */
+			case 46:
+			{
+				pSrc++;
+				ch->_octaveTranspose = *pSrc;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -19 (0xED): skip forward by N+3 bytes ---- */
+			case 45:
+			{
+				pSrc++;
+				var_C = *pSrc;
+				uint16_t skip = var_C + 3;
+				ch->_pSrc += skip;
+				goto dispatch;
+			}
+
+			/* ---- opcode -20 (0xEC): random pick from table ---- */
+			case 44:
+			{
+				pSrc++;
+				uint8_t tblSize = *pSrc;
+				var_C = (uint16_t)tblSize;
+				pSrc++;
+				byte *base = pSrc;
+
+				(void)getRandomNumber();
+				uint16_t rnd = _randomSeed & 0x7FFF;
+				uint16_t idx = (uint16_t)((int16_t)rnd % (int16_t)var_C);
+				var_6 = idx;
+
+				uint8_t  chosen = *(base + idx);
+				uint8_t  target = *(base + var_C);
+				/* Write chosen into table[target+1] */
+				*(base + target + 1) = chosen;
+
+				uint16_t advance = var_C + 3;
+				ch->_pSrc += advance;
+				goto dispatch;
+			}
+
+			/* ---- opcode -21 (0xEB): random value in range [lo..hi] ---- */
+			case 43:
+			{
+				pSrc++;
+				var_A = *pSrc;
+				pSrc++;
+				var_4 = *pSrc;
+				pSrc++;
+				var_C = var_4 - var_A + 1;
+
+				(void)getRandomNumber();
+				uint16_t rnd = _randomSeed & 0x7FFF;
+				var_6 = (uint16_t)((int16_t)rnd % (int16_t)var_C);
+
+				byte *base = pSrc;
+				uint8_t  targetSlot = *base;
+				uint8_t  result = var_6 + var_A;
+				*(base + targetSlot + 1) = result;
+
+				ch->_pSrc += 4;
+				goto dispatch;
+			}
+
+			/* ---- opcode -62 (0xC2): advance pSrc by 4 ---- */
+			case 4:
+			{
+				ch->_pSrc += 4;
+				goto dispatch;
+			}
+
+			/* ---- opcode -22 (0xEA): indirect random pick from var table ---- */
+			case 42:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				pSrc++;
+				byte *base = pSrc;
+				uint8_t  indIdx = byte_16A30[var_6];
+				uint8_t  chosen = *(base + (uintptr_t)indIdx);
+				uint8_t  target = *(base + var_C);
+				*(base + target + 1) = chosen;
+				uint16_t advance = (uint16_t)(var_C + 4);
+				ch->_pSrc += advance;
+				goto dispatch;
+			}
+
+			/* ---- opcode -23 (0xE9): store immediate into script var ---- */
+			case 41:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t val = *pSrc;
+				byte_16A30[var_6] = val;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -24 (0xE8): var-to-var copy ---- */
+			case 40:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] = byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -25 (0xE7): indirect copy to stream ---- */
+			case 39:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte *base = pSrc;
+				uint8_t  src = byte_16A30[var_6];
+				*(base + var_C + 1) = src;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -26 (0xE6): increment script var ---- */
+			case 38:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				byte_16A30[var_6]++;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -27 (0xE5): decrement script var ---- */
+			case 37:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				byte_16A30[var_6]--;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -28 (0xE4): add immediate to script var ---- */
+			case 36:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] += imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -29 (0xE3): add var to script var ---- */
+			case 35:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] += byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -30 (0xE2): subtract immediate from script var ---- */
+			case 34:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] -= imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -31 (0xE1): subtract var from script var ---- */
+			case 33:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] -= byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -32 (0xE0): multiply script var by var ---- */
+			case 32:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] *= byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -33 (0xDF): multiply script var by immediate ---- */
+			case 31:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] *= imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -34 (0xDE): divide script var by immediate -> quotient ---- */
+			case 30:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] = (uint8_t)((int8_t)byte_16A30[var_6] / (int8_t)var_C);
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -35 (0xDD): divide var by var -> quotient to dst ---- */
+			case 29:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] = (uint8_t)((uint8_t)byte_16A30[var_6] / (uint8_t)byte_16A30[var_C]);
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -36 (0xDC): divide script var by immediate -> remainder ---- */
+			case 28:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] = (uint8_t)((int8_t)byte_16A30[var_6] % (int8_t)var_C);
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -37 (0xDB): divide var by var -> remainder to dst ---- */
+			case 27:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] = (uint8_t)((uint8_t)byte_16A30[var_6] % (uint8_t)byte_16A30[var_C]);
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -38 (0xDA): AND script var by immediate ---- */
+			case 26:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] &= imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -39 (0xD9): AND var by var ---- */
+			case 25:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] &= byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -40 (0xD8): OR script var by immediate ---- */
+			case 24:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] |= imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -41 (0xD7): OR var by var ---- */
+			case 23:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] |= byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -42 (0xD6): XOR script var by immediate ---- */
+			case 22:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				uint8_t imm = *pSrc;
+				byte_16A30[var_6] ^= imm;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -43 (0xD5): XOR var by var ---- */
+			case 21:
+			{
+				pSrc++;
+				var_6 = *pSrc;
+				pSrc++;
+				var_C = *pSrc;
+				byte_16A30[var_6] ^= byte_16A30[var_C];
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- Comparison / conditional-branch opcodes -44 through -59 ----
+			 * Each reads two operands, compares them, and either branches
+			 * (taken: read next word as target -> pSrc) or skips 5 bytes ahead.
+			 * For opcodes -44..-51 the two operands are: imm vs var[idx].
+			 * For opcodes -52..-59 they are: var[idx] vs var[idx2].
+			 */
+
+			 /* -44 (0xD4): branch if imm == var */
+			case 20:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				uint8_t v = byte_16A30[var_6];
+				if ((uint16_t)var_C == (uint16_t)v) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -45 (0xD3): branch if imm != var */
+			case 19:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C != (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -46 (0xD2): branch if imm > var */
+			case 18:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C > (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -47 (0xD1): branch if imm >= var */
+			case 17:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C >= (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -48 (0xD0): branch if var[idx1] == var[idx2] */
+			case 16:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] == byte_16A30[var_C]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -49 (0xCF): branch if var[idx1] != var[idx2] */
+			case 15:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] != byte_16A30[var_C]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -50 (0xCE): branch if var[idx1] > var[idx2] */
+			case 14:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] > byte_16A30[var_C]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -51 (0xCD): branch if var[idx1] < var[idx2] */
+			case 13:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] < byte_16A30[var_C]) goto branch_taken;
+				goto branch_skip5;
+			}
+
+			/* -52 (0xCC): branch if imm == var (extended) - same as -44 logic */
+			case 12:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C == (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -53 (0xCB): branch if imm != var (extended) */
+			case 11:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C != (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -54 (0xCA): branch if imm > var (extended) */
+			case 10:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C > (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -55 (0xC9): branch if imm < var (extended) */
+			case 9:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if ((uint16_t)var_C < (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -56 (0xC8): branch if var[idx1] == var[idx2] (extended) */
+			case 8:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] == byte_16A30[var_C]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -57 (0xC7): branch if var[idx1] != var[idx2] (extended) */
+			case 7:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] != byte_16A30[var_C]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -58 (0xC6): branch if var[idx1] > var[idx2] (extended) */
+			case 6:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] > byte_16A30[var_C]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* -59 (0xC5): branch if var[idx1] < var[idx2] (extended) */
+			case 5:
+			{
+				pSrc++; var_6 = *pSrc;
+				pSrc++; var_C = *pSrc;
+				if (byte_16A30[var_6] < byte_16A30[var_C]) goto branch_taken2;
+				goto branch_skip5;
+			}
+
+			/* Shared branch-taken logic for -44..-51: save +5 as return target */
+branch_taken:
+			{
+				ch->_branchTargetPtr = ch->_pSrc + 5;
+				byte *dest = &_soundData[readWord_impl()];
+				ch->_pSrc = dest;
+				goto dispatch;
+			}
+
+			/* Shared branch-taken logic for -52..-59: read immediate target word */
+branch_taken2:
+			{
+				ch->_branchTargetPtr = ch->_pSrc + 5;
+				/* fall through to reading the target word */
+				byte *dest = &_soundData[readWord_impl()];
+				ch->_pSrc = dest;
+				goto dispatch;
+			}
+
+			/* Shared branch-skip: advance 5 bytes */
+branch_skip5:
+			{
+				ch->_pSrc += 5;
+				goto dispatch;
+			}
+
+			/* ---- opcode -60 (0xC4): call a function pointer ---- */
+			case 2: {
+				uint16_t fptr = readWord_impl();
+
+				// Call the function
+				callFunction(fptr);
+
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			/* ---- opcode -61 (0xC3): call nullsub_1 with byte arg ---- */
+			case 3:
+			{
+				pSrc++;
+				uint8_t arg = *pSrc;
+				(void)arg;   /* nullsub_1 is a no-op */
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -63 (0xC1): set word_12070 ---- */
+			case 1:
+			{
+				pSrc++;
+				word_12070 = *pSrc;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			/* ---- opcode -64 (0xC0): set tempo / frame sync ---- */
+			case 0:
+			{
+				pSrc++;
+				word_12066 = *pSrc;
+				ch->_pSrc += 2;
+				if (_frameNumber2 != 0)
+					goto dispatch;
+				word_1206E = word_12066;
+				goto dispatch;
+			}
+
+			default:
+				break;
+			} /* end switch */
+
+			goto dispatch;
+		}
+
+		/* --- 0xBE or 0xBF range (> 0xBD but <0xC0): treated as commands -65/-64 ---
+		 * 0xBF -> ax = 0xBF + 66 - 66 = 0xBF; not in [0,65], falls to default in dispatch.
+		 * The asm handles 0xBD and below as notes, >0xBD as the big switch.
+		 * 0xBE / 0xBF would land in the switch default (no-op) which falls through
+		 * to the re-dispatch. Handled by the goto dispatch above.
+		 */
+	} /* end command decode block */
+
+	/* Should not reach here in normal execution */
+	goto post_keyon;
+
+key_on:
+	{
+		ch = _activeChannelPtr;
+		if (ch->_durationOverride != 0)
+			ch->_keyOnDelay = ch->_durationOverride;
+		else
+			ch->_keyOnDelay = ch->_activeCount - ch->_noteOffset;
+		noteOn();
+	}
+
+post_keyon:
+	{
+		ch = _activeChannelPtr;
+		if (ch->_pitchBend != 0)
+			writePitchBend();
+
+		var_8 = 0;
+
+		/* ---- Fade / vibrato processing ---- */
+		if (ch->_fadePeriodCounter == 0 && ch->_vibPeriodCounter == 0)
+			goto done;
+
+		ch->_fadePeriodCounter--;
+		ch = _activeChannelPtr;
+
+		if (ch->_fadePeriodCounter == 0) {
+			ch->_fadePeriodCounter = ch->_fadePeriodReload;
+			ch = _activeChannelPtr;
+
+			if (ch->_volumeFadeStep != 0) {
+				if (ch->_pendingStop != 0) {
+					/* Fading a channel that is pending-stop: step velocity down */
+					if (ch->_velocity > 0) {
+						ch->_velocity += ch->_volumeFadeStep; /* step is signed */
+					}
+					ch = _activeChannelPtr;
+					if (ch->_volume != 0) {
+						ch->_volume += ch->_volumeFadeStep;
+					}
+				} else {
+					/* Normal fade: clamp velocity at 0..127 */
+					if ((int8_t)ch->_volumeFadeStep > 0) {
+						ch->_velocity += ch->_volumeFadeStep;
+						ch = _activeChannelPtr;
+						if ((int16_t)ch->_velocity > 0x7F)
+							ch->_velocity = 0x7F;
+					} else {
+						ch->_velocity += ch->_volumeFadeStep;
+						ch = _activeChannelPtr;
+						if ((int8_t)ch->_velocity < 0)
+							ch->_velocity = 0;
+					}
+				}
+				var_8 = 1;
+			}
+		}
+
+		/* ---- Vibrato ---- */
+		ch = _activeChannelPtr;
+		ch->_vibPeriodCounter--;
+		ch = _activeChannelPtr;
+
+		if (ch->_vibPeriodCounter == 0) {
+			ch->_vibPeriodCounter = ch->_vibPeriodReload;
+			ch = _activeChannelPtr;
+
+			if ((int8_t)ch->_vibratoDepth != 0) {
+				int16_t pa = (int16_t)(int8_t)ch->_patchAttenuation;
+				int16_t vib = (int16_t)(int8_t)ch->_vibratoDepth;
+				int16_t sum = pa + vib;
+
+				if ((int8_t)ch->_vibratoDepth > 0) {
+					/* Positive vibrato: bounce if above 0x7F */
+					if (sum > 0x7F)
+						ch->_vibratoDepth = (uint8_t)(-ch->_vibratoDepth);
+				} else {
+					/* Negative vibrato: bounce if below zero */
+					if (sum < 0)
+						ch->_vibratoDepth = (uint8_t)(-ch->_vibratoDepth);
+				}
+
+				ch = _activeChannelPtr;
+				ch->_patchAttenuation += ch->_vibratoDepth;
+				var_8 = 1;
+			}
+		}
+
+		if (var_8 != 0)
+			writeVolume();
+	}
+
+done:
+	_activeChannelNumber++;
+}
+
+bool ASound::isSoundActive(byte *ptr) const {
+	for (int chan = 0; chan < ADLIB_CHANNEL_COUNT; ++chan) {
+		if (_channels[chan]->_activeCount && _channels[chan]->_soundData == ptr)
+			return true;
+	}
+
+	return false;
+}
+
+uint16 ASound::readWord_impl() {
+	uint16 lo = *++pSrc;
+	uint16 hi = *++pSrc;
+	return lo | (hi << 8);
+}
 
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 07b93147494..9eb337d38e1 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -22,12 +22,413 @@
 #ifndef MADS_CORE_ASOUND_H
 #define MADS_CORE_ASOUND_H
 
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/util.h"
 #include "mads/core/sound_manager.h"
 
 namespace MADS {
 namespace MADSV2 {
 
+#define ADLIB_CHANNEL_COUNT 9
+
+struct AdlibChannel {
+	static bool _isDisabled;
+	uint8  _activeCount;
+	uint8  _pitchBend;
+	uint8  _volumeFadeStep;
+	uint8  _vibratoDepth;
+	uint8  _note;
+	uint8  _sampleIndex;
+	uint8  _volume;
+	uint8  _noteOffset;
+	uint8  _keyOnDelay;
+	uint8  _fadePeriodCounter;
+	uint8  _fadePeriodReload;
+	uint8  _vibPeriodCounter;
+	uint8  _vibPeriodReload;
+	uint8  _patchAttenuation;
+	byte *_loopStartPtr;
+	byte *_pSrc;
+	byte *_innerLoopPtr;
+	byte *_outerLoopPtr;
+	byte *_soundData;
+	byte *_branchTargetPtr;
+	uint16 _innerLoopCount;
+	uint16 _outerLoopCount;
+	uint16 _noiseFreqMask;
+	uint16 _freqAccum;
+	uint16 _freqStep;
+	uint8  _freqSweepCounter;
+	uint8  _savedFreqSweep;
+	uint8  _transpose;
+	uint8  _velocity;
+	uint8  _opVolBytes;
+	uint8  _opVolBytes2;
+	uint8  _octaveTranspose;
+	uint8  _pendingStop;
+	uint8  _durationOverride;
+
+	/**
+	 * Zeroes the "live" fields of one channel without touching loop/sound-data
+	 * pointers (those are handled separately by AdlibChannel_load).
+	 */
+	void reset();
+
+	/**
+	 * Clears the entire channel struct, then initialises the loop/source
+	 * pointers to the supplied sound-data address and marks the channel active.
+	 */
+	void load(byte *soundData);
+
+	/**
+	 * Point a channel at the null/silence data and
+	 * arm a one-step volume fade so it fades out gracefully.
+	 */
+	void setPtr2(byte *ptr);
+
+	/**
+	 * Mark a channel as pending-stop and redirect its
+	 * sound-data pointer to the null/silence stream.
+	 */
+	void enable();
+
+	/**
+	 * Called every frame for each channel.  When _pendingStop is set,
+	 * waits until both velocity and volume are zero before marking the channel
+	 * idle; otherwise arms a fade-down of velocity/volume each period.
+	 */
+	void processChannelFade();
+};
+
+struct AdlibSample {
+	uint8  _attackRate;
+	uint8  _decayRate;
+	uint8  _sustainLevel;
+	uint8  _releaseRate;
+	uint8  _egTyp;
+	uint8  _ksr;
+	uint8  _totalLevel;
+	uint8  _scalingLevel;
+	uint8  _waveformSelect;
+	uint8  _freqMultiple;
+	uint8  _feedback;
+	uint8  _ampMod;
+	uint8  _vib;
+	uint8  _alg;
+	uint8  _freqSweepInit;
+	uint8  _reserved;
+	uint16 _freqMask;
+	uint16 _freqBase;
+	uint16 _outerLoopPtr;
+};
+
 class ASound : public SoundDriver {
+private:
+	Common::Mutex _driverMutex;
+	Common::Queue< Common::Pair<byte, byte> > _queue;
+	uint16 word_12C06 = 0;  // period counter
+	uint16 word_12C08 = 0;  // period reload
+	uint16 word_12C0A = 0;  // callback function pointer (not used in C)
+	AdlibChannel *_activeChannelPtr = NULL;
+	uint8 _activeChannelNumber = 0;
+	uint16 _activeChannelReg = 0;
+	uint16 word_16CF6 = 0;				// Current operator base
+	AdlibSample *_samplePtr = NULL;
+	byte *pSrc = nullptr;				// current read pointer
+	uint8 _adlib_v5660_2 = 0;			// OPL version flag
+	uint16 _pollResult = 0;
+	uint16 _resultFlag = 0;
+	uint16 _randomSeed = 0x4D2;
+	uint8  _isDisabled = 0;
+	uint8  byte_12050 = 0;				// findFreeChannel mode
+	uint8  byte_16B5C = 0;				// channel5 savedFreqSweep shadow
+	uint8  byte_16B5D = 0;				// channel5 savedFreqSweep shadow 2
+	uint8  byte_16B63 = 0;				// channel5 pendingStop shadow
+	/* Percussion flags (byte_1218C/D/E) */
+	uint8 byte_1218C;					// rhythm mode: hi-hat
+	uint8 byte_1218D;					// rhythm mode: cymbal
+	uint8 byte_1218E;					// rhythm mode: enable
+
+	uint8 byte_12036 = 0;				// any-sweep-active flag
+	int _frameNumber2 = 0;
+	uint8 byte_16A30[32];				// General-purpose script registers
+	uint16 word_12052 = 1;
+	uint16 word_12062 = 0;
+	uint16 word_12066 = 0;
+	uint16 word_12068 = 0;
+	uint16 word_1206A = 0;
+	uint16 word_1206C = 0xA0;
+	uint16 word_1206E = 0x28;
+	uint16 word_12070 = 0x0A;
+
+	/**
+	 * Timer function for OPL
+	 */
+	void onTimer();
+
+	/**
+	 * Clears the periodic tick-callback state.
+	 */
+	void clearCallback();
+	void resetCallback() {
+		clearCallback();
+	}
+
+	/**
+	 * Mutes one output operator by setting its total-level to maximum (0x3F)
+	 * while preserving the upper KSL bits.
+	 * 'portIndex' is the base port index for that operator (from _adlibPorts).
+	 */
+	void adlib_channelOff(uint8 portIndex);
+
+
+	/**
+	 * Computes and writes the total-level (volume) registers for the current
+	 * active channel to the OPL chip.
+	 *
+	 * Two paths depending on _adlib_v5660_2:
+	 *   < 0x18  -> simple linear mapping of (volume + velocity) -> TL
+	 *   >= 0x18 -> patch-attenuation-aware mapping using byte_12072 table
+	 */
+	void writeVolume();
+
+	/**
+	 * Computes the OPL F-number / block (octave) from the channel's note,
+	 * octaveTranspose and transpose, then writes registers 0xA0+ch and 0xB0+ch.
+	 */
+	void writeFrequency();
+
+	/**
+	 * Applies the channel's pitchBend offset to the current frequency registers
+	 * without recalculating from the note table.
+	 */
+	void writePitchBend();
+
+	/**
+	 * Clears the key-on bit (bit 5) of the 0xB0+ch register to silence the
+	 * note without changing pitch (used during note-delay countdown).
+	 */
+	void updateOctave();
+
+	/**
+	 * Writes volume, loads the freq-sweep counter from the sample definition,
+	 * and - if the sweep counter is zero - triggers the note (writes frequency
+	 * and sets the key-on bit in the 0xB0+ch register).
+	 */
+	void noteOn();
+
+	/**
+	 * Writes a full set of OPL operator registers for the sample pointed to
+	 * by the global asound_samplePtr, using word_16CF6 as the operator base.
+	 */
+	void writeSampleRegs();
+
+	/**
+	 * Loads all OPL registers for the patch assigned to the current channel,
+	 * covering both the modulator and carrier operators.
+	 */
+	void loadSample();
+
+	/**
+	 * Main sound update function
+	 */
+	void update();
+
+	/**
+	 * Per-channel frequency-sweep tick.  Called once per frame per channel.
+	 * When freqSweepCounter > 0: accumulates freqStep into freqAccum and
+	 * decrements the counter.  When it reaches zero, zeroes the frequency
+	 * registers for that voice.
+	 */
+	void update1(int channelIndex);
+
+	/**
+	 * Sets voice frequency
+	 */
+	void setFrequency(uint8 voice, uint16 freq);
+
+	/**
+	 * Noise channel : each tick picks a random frequency offset masked by
+	 * noiseFreqMask, adds freqAccum, and writes the result.
+	 */
+	void noise_inner(int channelIndex);
+
+	/**
+	 * Calls processChannelFade for all 9 channels each frame.
+	 */
+	void updateAllChannels();
+
+	/**
+	 * Advances pSrc by one, reads two bytes little-endian, returns the word.
+	 */
+	uint16 readWord_impl();
+
+protected:
+	AdlibChannel _channel0, _channel1, _channel2;
+	AdlibChannel _channel3, _channel4, _channel5;
+	AdlibChannel _channel6, _channel7, _channel8;
+	AdlibChannel *_channels[ADLIB_CHANNEL_COUNT] = {
+		&_channel0, &_channel1, &_channel2, &_channel3, &_channel4,
+		&_channel5, &_channel6, &_channel7, &_channel8
+	};
+
+	uint8  _adlibPorts[0x100] = { 0 };
+	Common::Array<AdlibSample> _samples;
+
+protected:
+	/**
+	  * write(register, value)
+	  *
+	  * Updates the local port-shadow array so that subsequent reads from
+	  * _adlibPorts[reg] return the last-written value, then sends the byte
+	  * pair to the real hardware (or whatever back-end is wired in).
+	  */
+	void write(uint8 reg, uint8 value);
+
+	/**
+	 * Flush any pending Adlib register values to the OPL driver
+	 */
+	void flush();
+
+	/**
+	 * Updates and returns _asound_randomSeed.
+	 */
+	uint16 getRandomNumber();
+
+	/**
+	 * Restores one output operator: clears the forced-max bits (0x3F) and
+	 * writes the caller-supplied volume nibble (bh in the asm).
+	 * 'portIndex' is the operator register index; 'vol' is the volume nibble.
+	 */
+	void adlib_channelOn(uint8 portIndex, uint8 vol);
+
+	/**
+	 * Sets the poll-result flags to indicate that sound is playing.
+	 */
+	void signalSoundPlaying();
+
+	/**
+	 * Polls all the channels
+	 */
+	void pollAllChannels();
+
+	/**
+	 * Per-channel update, called once per frame by pollAllChannels.
+	 * Implements a bytecode interpreter : the sound data stream is a sequence of
+	 * (note, duration) pairs plus command bytes(0x80-0xFF). Commands with high
+	 * bit clear are notes; high bit set triggers the switch below.
+	 *
+	 * The interpreter loop re-enters the top of the dispatch after
+	 * each command that does not consume a duration tick. Note bytes consume
+	 * one tick per call (activeCount counts down).
+	  */
+	void pollActiveChannel();
+
+	/**
+	 * Returns true if a given sound data is already active.
+	 */
+	bool isSoundActive(byte *ptr) const;
+
+	/**
+	 * Channel finder helpers
+	 * Scan channels in priority order and call AdlibChannel_load on the first
+	 * inactive (or pending-stop) one found.
+	 *
+	 * Searches channels 0-5 for an empty slot, falling through to
+	 * asound_findFreeChannelFull (channels 6-8) if none found.
+	 * byte_12050 == 1 means "only search 0-5".
+	  */
+	void findFreeChannel(byte *soundData);
+
+	/**
+	 * Extends the search to channels 6-8 and then checks for pending-stop
+	 * channels (which can be pre-empted), in reverse priority order.
+	 */
+	void findFreeChannelFull(byte *soundData);
+
+protected:
+	/**
+	 * Silences every voice and mutes the hardware:
+	 *   1. Reset all 9 channels.
+	 *   2. Mute all operator total-level registers (0x40-0x55) to max attenuation.
+	 *   3. Zero remaining operator registers (0x60-0xFF and 0x01-0x3F).
+	 *   4. Write Waveform Select Enable (register 0x01 = 0x20).
+	 *   5. Reset the tick callback.
+	 */
+	int command0();
+
+	 /*
+	  * Fade out all channels (calls command2 + command4 via
+	  * the command3/command5 helpers that enable pending-stop on each channel).
+	  */
+	int command1();
+
+	/**
+	 * Fade out music channels 0-5.
+	 */
+	int command2();
+
+	/**
+	 * Fade out low channels
+	 */
+	int command3();
+
+	/**
+	 * Fade out SFX channels 6-8.
+	 */
+	int command4();
+
+	/*
+	 * Stop SFX channels (6-8) with pending-stop flag.
+	 *
+	 * Marks each of the three SFX channels (6, 7, 8) as pending-stop by setting
+	 * their _pendingStop field to 0xFF and redirecting their sound-data pointer
+	 * to the null/silence stream.  Channels that are already inactive
+	 * (_activeCount == 0) are skipped.
+	 *
+	 * Unlike command4 (which forces an immediate fade-out via AdlibChannel_setPtr2),
+	 * this command lets each channel finish its current envelope naturally:
+	 * asound_processChannelFade checks _pendingStop each frame and only silences
+	 * the channel once both _velocity and _volume have reached zero.
+	 */
+	int command5();
+
+	/**
+	 * Saves each channel's current freqSweepCounter into savedFreqSweep,
+	 * zeroes freqSweepCounter on all channels, then mutes the output operators
+	 * for voices 3-5 (the secondary operator set at 0x43-0x55).
+	 */
+	int command6();
+
+	/**
+	 * Resume playback after a pause.
+	 * Restores operator volumes for channels 0-5, re-saves sweep counters from
+	 * the live values (which were frozen to 0 by command6), then re-enables
+	 * the audio engine.
+	 *
+	 * The OPL2 voice-to-operator mapping used here:
+	 *   voice 0 -> operator index 67  (0x43)
+	 *   voice 1 -> operator index 68  (0x44)
+	 *   voice 2 -> operator index 69  (0x45)
+	 *   voice 3 -> operator index 75  (0x4B)
+	 *   voice 4 -> operator index 76  (0x4C)
+	 *   voice 5 -> operator index 77  (0x4D)
+	 */
+	int command7();
+
+	/**
+	 * Returns non-zero if any of the 9 Adlib channels has a non-zero
+	 * _activeCount (i.e. sound is currently playing).
+	 */
+	int command8();
+
+	/**
+	 * Calls a function at a fixed offset within the sound driver.
+	 * @param offset		Offset of the function
+	 */
+	virtual void callFunction(uint16 offset);
+
 public:
 	/**
 	 * Constructor
@@ -36,12 +437,38 @@ public:
 	 * @param filename		Specifies the adlib sound player file to use
 	 * @param dataOffset	Offset in the file of the data segment
 	 */
-	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
+	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize);
 
 	/**
 	 * Destructor
 	 */
 	~ASound() override {}
+
+	/**
+	 * Stop all currently playing sounds
+	 */
+	virtual int stop();
+
+	/**
+	 * Main poll method to allow sounds to progress
+	 */
+	int poll() override;
+
+	/**
+	 * Noise channel : each tick picks a random frequency offset masked by
+	 * noiseFreqMask, adds freqAccum, and writes the result.
+	 */
+	void noise() override;
+
+	void setVolume(int volume) override {
+		// TODO: Does this need implementation?
+	}
+
+	/**
+	 * Plays a sound
+	*/
+	void playSound(int offset, int size);
 };
 
 } // namespace MADSV2
diff --git a/engines/mads/madsv2/phantom/sound_phantom.cpp b/engines/mads/madsv2/phantom/sound_phantom.cpp
index c3f744154e1..b60706ecbdf 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.cpp
+++ b/engines/mads/madsv2/phantom/sound_phantom.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/endian.h"
+#include "common/file.h"
 #include "common/md5.h"
 #include "common/textconsole.h"
 #include "mads/madsv2/phantom/sound_phantom.h"
@@ -82,559 +83,6 @@ void PhantomSoundManager::loadDriver(int sectionNumber) {
 	}
 }
 
-/*-----------------------------------------------------------------------*/
-
-PhantomASound::PhantomASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset) :
-		ASound(mixer, opl, filename, dataOffset) {
-	_chanCommandCount = 66;
-	memset(_scratchArr, 0, sizeof(_scratchArr));
-
-	// Load sound samples - all the asound drivers have an identical set of 120
-	// samples starting at offset 1d4h in their data segment
-	_soundFile.seek(_dataOffset + 0x1d4);
-	for (int i = 0; i < 120; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
-}
-
-void PhantomASound::channelCommand(byte *&pSrc, bool &updateFlag) {
-	AdlibChannel *chan = _activeChannelPtr;
-	int cmdNum = (int8)*pSrc;   // -1 .. -66
-
-	switch (cmdNum) {
-
-	case -1:
-		// Single-level repeat loop (uses _field17 / _ptr3)
-		if (!chan->_field17) {
-			if (pSrc[1] == 0) {
-				chan->_pSrc += 2;
-				chan->_ptr3 = chan->_pSrc;
-				chan->_field17 = 0;
-			} else {
-				chan->_field17 = (int8)pSrc[1];
-				chan->_pSrc = chan->_ptr3;
-			}
-		} else {
-			--chan->_field17;
-			if (!chan->_field17) {
-				chan->_pSrc += 2;
-				chan->_ptr3 = chan->_pSrc;
-			} else {
-				chan->_pSrc = chan->_ptr3;
-			}
-		}
-		break;
-
-	case -2:
-		// Double-level repeat loop (uses _field19/_ptr4 outer, _field17/_ptr3 inner)
-		if (!chan->_field19) {
-			if (pSrc[1] == 0) {
-				chan->_pSrc += 2;
-				chan->_ptr4 = chan->_pSrc;
-				chan->_ptr3 = chan->_pSrc;
-				chan->_field17 = 0;
-				chan->_field19 = 0;
-			} else {
-				chan->_field19 = (int8)pSrc[1];
-				chan->_pSrc = chan->_ptr4;
-				chan->_ptr3 = chan->_ptr4;
-			}
-		} else {
-			--chan->_field19;
-			if (!chan->_field19) {
-				chan->_pSrc += 2;
-				chan->_ptr4 = chan->_pSrc;
-				chan->_ptr3 = chan->_pSrc;
-			} else {
-				chan->_pSrc = chan->_ptr4;
-				chan->_ptr3 = chan->_ptr4;
-			}
-		}
-		break;
-
-	case -3:
-		// Reset channel to soundData start
-		chan->_fieldE = 0;      // original stored soundData near ptr here
-		chan->_pSrc     = chan->_soundData;
-		chan->_ptr3     = chan->_soundData;
-		chan->_ptr4     = chan->_soundData;
-		chan->_field1   = 0;
-		chan->_field2   = 0;
-		chan->_field3   = 0;
-		chan->_field26  = 0;
-		chan->_volumeOffset = 0;
-		chan->_field28  = 0;
-		chan->_volume   = 0;
-		chan->_field9   = 0;
-		chan->_fieldB   = 0;
-		chan->_field17  = 0;
-		chan->_field19  = 0;
-		chan->_field7   = 0;
-		break;
-
-	case -4: {
-		// Jump: set all four data pointers from a near-pointer word argument
-		++pSrc;
-		int nearPtr = (int16)READ_LE_UINT16(pSrc);
-		byte *p = getDataPtr(nearPtr);
-		chan->_fieldE    = nearPtr;
-		chan->_pSrc      = p;
-		chan->_ptr3      = p;
-		chan->_ptr4      = p;
-		chan->_soundData = p;
-		break;
-	}
-
-	case -5: {
-		// Unconditional jump to near-pointer target (no return address saved)
-		++pSrc;
-		int nearPtr = (int16)READ_LE_UINT16(pSrc);
-		chan->_pSrc = getDataPtr(nearPtr);
-		break;
-	}
-
-	case -6: {
-		// Unconditional call to near-pointer target (saves return address in _ptrEnd)
-		++pSrc;
-		int nearPtr = (int16)READ_LE_UINT16(pSrc);
-		chan->_ptrEnd = chan->_pSrc + 3;
-		chan->_pSrc   = getDataPtr(nearPtr);
-		break;
-	}
-
-	case -7:
-		// Return from call (restores _pSrc from _ptrEnd)
-		if (chan->_ptrEnd) {
-			chan->_pSrc   = chan->_ptrEnd;
-			chan->_ptrEnd = nullptr;
-		} else {
-			chan->_pSrc += 1;
-		}
-		break;
-
-	case -8:
-		// Load sample: byte argument is sample index
-		chan->_sampleIndex = pSrc[1];
-		chan->_pSrc += 2;
-		loadSample(chan->_sampleIndex);
-		break;
-
-	case -9:
-		// Set _field7 (pitch source), clear _field2C (frequency counter)
-		chan->_field7  = pSrc[1];
-		chan->_field2C = 0;
-		chan->_pSrc += 2;
-		break;
-
-	case -10:
-		// Set _field2C (frequency counter), clear _field7
-		chan->_field2C = pSrc[1];
-		chan->_field7  = 0;
-		chan->_pSrc += 2;
-		break;
-
-	case -11:
-		// Set _field1 (envelope / modulation)
-		chan->_field1 = pSrc[1];
-		chan->_pSrc += 2;
-		break;
-
-	case -12:
-		// Set volume, capped downward if _field2B (mute flag) is active
-		{
-			int val = (int8)pSrc[1];
-			if (chan->_field2B) {
-				if (chan->_volume > val)
-					chan->_volume = val;
-			} else {
-				chan->_volume = val;
-			}
-			updateFlag = true;
-			chan->_pSrc += 2;
-		}
-		break;
-
-	case -13:
-		// Set _fieldA and _field2 (vibrato params), or skip if muted
-		if (chan->_field2B) {
-			chan->_pSrc += 3;
-		} else {
-			chan->_fieldA = pSrc[1];
-			chan->_field2 = pSrc[2];
-			chan->_field9 = 1;
-			chan->_pSrc += 3;
-		}
-		break;
-
-	case -14:
-		// Set _field26 (pitch delta)
-		chan->_field26 = pSrc[1];
-		chan->_pSrc += 2;
-		break;
-
-	case -15:
-		// Set _volumeOffset, capped downward if _field2B (mute flag) is active
-		{
-			int val = (int8)pSrc[1];
-			if (chan->_field2B) {
-				if ((int8)chan->_volumeOffset > val)
-					chan->_volumeOffset = val;
-			} else {
-				chan->_volumeOffset = val;
-			}
-			updateFlag = true;
-			chan->_pSrc += 2;
-		}
-		break;
-
-	case -16:
-		// Set _fieldD (volume increment step)
-		chan->_fieldD = pSrc[1];
-		updateFlag = true;
-		chan->_pSrc += 2;
-		break;
-
-	case -17:
-		// Set _fieldC (period) and _field3 (volume slide), enable slide (_fieldB=1)
-		chan->_fieldC = pSrc[1];
-		chan->_field3 = pSrc[2];
-		chan->_fieldB = 1;
-		chan->_pSrc += 3;
-		break;
-
-	case -18:
-		// Set _field2A
-		chan->_field2A = pSrc[1];
-		chan->_pSrc += 2;
-		break;
-
-	case -19:
-		// Relative skip: advance by (signed byte arg) + 3
-		chan->_pSrc += (int8)pSrc[1] + 3;
-		break;
-
-	case -20: {
-		// Pick random element from array[0..N-1], write chosen value to a
-		// self-modifying offset within the sound data
-		int n    = (byte)pSrc[1];
-		byte *base = pSrc + 2;
-		int rnd  = getRandomNumber() & 0x7FFF;
-		int idx  = rnd % n;
-		byte chosen = base[idx];
-		int destIdx = (int8)base[n];
-		base[destIdx + n + 1] = chosen;
-		chan->_pSrc += n + 3;
-		break;
-	}
-
-	case -21: {
-		// Pick random value in [lower..upper], write to a self-modifying offset
-		int lower = (int8)pSrc[1];
-		int upper = (int8)pSrc[2];
-		int range = upper - lower + 1;
-		int rnd   = getRandomNumber() & 0x7FFF;
-		byte *base = pSrc + 3;
-		int destIdx = (int8)base[0];
-		base[destIdx + 1] = (byte)(rnd % range + lower);
-		chan->_pSrc += 4;
-		break;
-	}
-
-	case -22: {
-		// Like -20 but array index is read from _scratchArr[regIdx]
-		int regIdx = (byte)pSrc[1];
-		int n      = (byte)pSrc[2];
-		byte *base = pSrc + 3;
-		int arrVal = _scratchArr[regIdx];
-		byte chosen = base[arrVal];
-		int destOffset = (int8)base[n];
-		base[destOffset + n + 1] = chosen;
-		chan->_pSrc += n + 4;
-		break;
-	}
-
-	case -23:
-		// arr[dest] = literal
-		_scratchArr[(byte)pSrc[1]] = (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -24:
-		// arr[dest] = arr[src]
-		_scratchArr[(byte)pSrc[1]] = _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	case -25:
-		// Write arr[regIdx] into sound data at pSrc[offset+3]
-		pSrc[(int8)pSrc[2] + 3] = _scratchArr[(byte)pSrc[1]];
-		chan->_pSrc += 3;
-		break;
-
-	case -26:
-		// arr[idx]++
-		++_scratchArr[(byte)pSrc[1]];
-		chan->_pSrc += 2;
-		break;
-
-	case -27:
-		// arr[idx]--
-		--_scratchArr[(byte)pSrc[1]];
-		chan->_pSrc += 2;
-		break;
-
-	case -28:
-		// arr[dest] += literal
-		_scratchArr[(byte)pSrc[1]] += (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -29:
-		// arr[dest] += arr[src]
-		_scratchArr[(byte)pSrc[1]] += _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	case -30:
-		// arr[dest] -= literal
-		_scratchArr[(byte)pSrc[1]] -= (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -31:
-		// arr[dest] -= arr[src]
-		_scratchArr[(byte)pSrc[1]] -= _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	case -32:
-		// arr[dest] = literal * arr[dest]  (byte multiply, low byte)
-		_scratchArr[(byte)pSrc[1]] = (byte)((byte)pSrc[2] * _scratchArr[(byte)pSrc[1]]);
-		chan->_pSrc += 3;
-		break;
-
-	case -33:
-		// arr[dest] = arr[src] * arr[dest]  (byte multiply, low byte)
-		_scratchArr[(byte)pSrc[1]] = (byte)(_scratchArr[(byte)pSrc[2]] * _scratchArr[(byte)pSrc[1]]);
-		chan->_pSrc += 3;
-		break;
-
-	case -34:
-		// arr[dest] /= literal  (quotient)
-		_scratchArr[(byte)pSrc[1]] = (byte)(_scratchArr[(byte)pSrc[1]] / (int8)pSrc[2]);
-		chan->_pSrc += 3;
-		break;
-
-	case -35:
-		// arr[dest] /= arr[src]  (quotient, unsigned)
-		_scratchArr[(byte)pSrc[1]] = (byte)(_scratchArr[(byte)pSrc[1]] / _scratchArr[(byte)pSrc[2]]);
-		chan->_pSrc += 3;
-		break;
-
-	case -36:
-		// arr[dest] %= literal  (remainder)
-		_scratchArr[(byte)pSrc[1]] = (byte)(_scratchArr[(byte)pSrc[1]] % (int8)pSrc[2]);
-		chan->_pSrc += 3;
-		break;
-
-	case -37:
-		// arr[dest] %= arr[src]  (remainder, unsigned)
-		_scratchArr[(byte)pSrc[1]] = (byte)(_scratchArr[(byte)pSrc[1]] % _scratchArr[(byte)pSrc[2]]);
-		chan->_pSrc += 3;
-		break;
-
-	case -38:
-		// arr[dest] &= literal
-		_scratchArr[(byte)pSrc[1]] &= (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -39:
-		// arr[dest] &= arr[src]
-		_scratchArr[(byte)pSrc[1]] &= _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	case -40:
-		// arr[dest] |= literal
-		_scratchArr[(byte)pSrc[1]] |= (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -41:
-		// arr[dest] |= arr[src]
-		_scratchArr[(byte)pSrc[1]] |= _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	case -42:
-		// arr[dest] ^= literal
-		_scratchArr[(byte)pSrc[1]] ^= (byte)pSrc[2];
-		chan->_pSrc += 3;
-		break;
-
-	case -43:
-		// arr[dest] ^= arr[src]
-		_scratchArr[(byte)pSrc[1]] ^= _scratchArr[(byte)pSrc[2]];
-		chan->_pSrc += 3;
-		break;
-
-	// Cases -44 to -51: conditional jumps (5-byte: cmd, idx, literal/idx2, ptr_lo, ptr_hi)
-	// Branch taken  → jump to near-pointer target (bytes 3-4)
-	// Branch not taken → skip 5 bytes
-
-#define PHANTOM_COND_JUMP(cond) \
-	do { \
-		if (cond) { \
-			int nearPtr = (int16)READ_LE_UINT16(pSrc + 3); \
-			chan->_pSrc = getDataPtr(nearPtr); \
-		} else { \
-			chan->_pSrc += 5; \
-		} \
-	} while (0)
-
-	case -44:
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[1]] == (byte)pSrc[2]);
-		break;
-
-	case -45:
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[1]] != (byte)pSrc[2]);
-		break;
-
-	case -46:
-		// arr[idx] < literal  (jmp if below the cap)
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[1]] < (byte)pSrc[2]);
-		break;
-
-	case -47:
-		// arr[idx] > literal
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[1]] > (byte)pSrc[2]);
-		break;
-
-	case -48:
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[2]] == _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -49:
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[2]] != _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -50:
-		// arr[idx2] > arr[idx1]  (unsigned)
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[2]] > _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -51:
-		// arr[idx2] < arr[idx1]  (unsigned)
-		PHANTOM_COND_JUMP(_scratchArr[(byte)pSrc[2]] < _scratchArr[(byte)pSrc[1]]);
-		break;
-
-#undef PHANTOM_COND_JUMP
-
-	// Cases -52 to -59: conditional calls — same as -44..-51 but save return address
-	// Branch taken  → save (chan->_pSrc + 5) in _ptrEnd, then jump to target
-	// Branch not taken → skip 5 bytes
-
-#define PHANTOM_COND_CALL(cond) \
-	do { \
-		if (cond) { \
-			int nearPtr = (int16)READ_LE_UINT16(pSrc + 3); \
-			chan->_ptrEnd = chan->_pSrc + 5; \
-			chan->_pSrc   = getDataPtr(nearPtr); \
-		} else { \
-			chan->_pSrc += 5; \
-		} \
-	} while (0)
-
-	case -52:
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[1]] == (byte)pSrc[2]);
-		break;
-
-	case -53:
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[1]] != (byte)pSrc[2]);
-		break;
-
-	case -54:
-		// arr[idx] < literal
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[1]] < (byte)pSrc[2]);
-		break;
-
-	case -55:
-		// arr[idx] > literal
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[1]] > (byte)pSrc[2]);
-		break;
-
-	case -56:
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[2]] == _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -57:
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[2]] != _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -58:
-		// arr[idx2] > arr[idx1]  (unsigned)
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[2]] > _scratchArr[(byte)pSrc[1]]);
-		break;
-
-	case -59:
-		// arr[idx2] < arr[idx1]  (unsigned)
-		PHANTOM_COND_CALL(_scratchArr[(byte)pSrc[2]] < _scratchArr[(byte)pSrc[1]]);
-		break;
-
-#undef PHANTOM_COND_CALL
-
-	case -60:
-		// Call near function pointer — not translatable to C++; skip 3-byte command
-		chan->_pSrc += 3;
-		break;
-
-	case -61:
-		// No-op with 2-byte command (original called a null stub)
-		chan->_pSrc += 2;
-		break;
-
-	case -62:
-		// Skip 4 bytes (advance past this 4-byte command)
-		chan->_pSrc += 4;
-		break;
-
-	case -63:
-		// Store signed byte into _w11F50 music state variable
-		_w11F50 = (int8)pSrc[1];
-		chan->_pSrc += 2;
-		break;
-
-	case -64:
-		// Store signed byte into _w11F46; if _w11F44 == 0, also copy to _w11F4E
-		_w11F46 = (int8)pSrc[1];
-		chan->_pSrc += 2;
-		if (!_w11F44)
-			_w11F4E = _w11F46;
-		break;
-
-	case -65: {
-		// Store 16-bit word into _w11F48; signal music sync (_w11F32/_w11F42 = 1)
-		++pSrc;
-		_w11F48 = (int16)READ_LE_UINT16(pSrc);
-		chan->_pSrc = pSrc + 2;     // advance 3 bytes total from command
-		if (!_w11F44)
-			_w11F4C = _w11F48;
-		_w11F32 = 1;
-		_w11F42 = 1;
-		break;
-	}
-
-	case -66:
-		// Store signed byte into _w11F4A music state variable
-		_w11F4A = (int8)pSrc[1];
-		chan->_pSrc += 2;
-		break;
-
-	default:
-		break;
-	}
-}
-
 /*-----------------------------------------------------------------------*/
 /* ASound1  (asound.ph1)                                                  *
  *                                                                        *
@@ -643,9 +91,9 @@ void PhantomASound::channelCommand(byte *&pSrc, bool &updateFlag) {
  *   sub_1040E=ch3, sub_10413=ch4, sub_10418=ch5                         *
  *   loc_1041D=ch6, loc_10422=ch7, loc_10427=ch8                         *
  *                                                                        *
- * sub_106DF = isSoundActive guard (non-zero BX → already playing)       *
- * sub_1039C = playSoundData(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
- * sub_10352 = playSoundData(pData, 0)                     (lower pool)  *
+ * sub_106DF = isSoundActive guard (non-zero BX -> already playing)       *
+ * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
+ * sub_10352 = findFreeChannel(pData, 0)                     (lower pool)  *
  *-----------------------------------------------------------------------*/
 
 const ASound1::CommandPtr ASound1::_commandList[40] = {
@@ -662,7 +110,7 @@ const ASound1::CommandPtr ASound1::_commandList[40] = {
 };
 
 ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl)
-		: PhantomASound(mixer, opl, "asound.ph1", 0x21e0) {
+		: ASound(mixer, opl, "asound.ph1", 0x21e0, 0x4d20) {
 }
 
 int ASound1::command(int commandId, int param) {
@@ -670,9 +118,7 @@ int ASound1::command(int commandId, int param) {
 	// near-pointer table (unk_13C3E) that cannot be safely reconstructed.
 	if (commandId > 39 || !_commandList[commandId])
 		return 0;
-
-	_commandParam = param;
-	_frameCounter = 0;
+	
 	return (this->*_commandList[commandId])();
 }
 
@@ -690,7 +136,7 @@ int ASound1::command8() { return ASound::command8(); }
 // ---------------------------------------------------------------------------
 // Background-music loaders (sub_11D84, sub_11EE6, sub_11F0E, sub_11F36)
 // Each calls command1() then loads six channels.
-// The five cx values that command16 checks against _channels[0]._field17
+// The five cx values that command16 checks against _channels[0]->_field17
 // are the starting offsets of each piece: 0x1ECA, 0x21C4, 0x3418, 0x3688,
 // 0x3D52.  (0x21C4 is the start of dead code following commandMusic0 that
 // was never reached by any dispatch table entry.)
@@ -698,45 +144,45 @@ int ASound1::command8() { return ASound::command8(); }
 
 int ASound1::commandMusic0() {
 	ASound::command1();
-	_channels[0].load(loadData(0x1ECA, 245));
-	_channels[1].load(loadData(0x1FBF, 120));
-	_channels[2].load(loadData(0x2037, 183));
-	_channels[3].load(loadData(0x20EE, 173));
-	_channels[4].load(loadData(0x219B,  20));
-	_channels[5].load(loadData(0x21AF,  21));
+	_channels[0]->load(loadData(0x1ECA, 245));
+	_channels[1]->load(loadData(0x1FBF, 120));
+	_channels[2]->load(loadData(0x2037, 183));
+	_channels[3]->load(loadData(0x20EE, 173));
+	_channels[4]->load(loadData(0x219B,  20));
+	_channels[5]->load(loadData(0x21AF,  21));
 	return 0;
 }
 
 int ASound1::commandMusic1() {
 	ASound::command1();
-	_channels[0].load(loadData(0x3418, 211));
-	_channels[1].load(loadData(0x34EB, 176));
-	_channels[2].load(loadData(0x359B, 189));
-	_channels[3].load(loadData(0x3658,  15));
-	_channels[4].load(loadData(0x3667,  16));
-	_channels[5].load(loadData(0x3677,  17));
+	_channels[0]->load(loadData(0x3418, 211));
+	_channels[1]->load(loadData(0x34EB, 176));
+	_channels[2]->load(loadData(0x359B, 189));
+	_channels[3]->load(loadData(0x3658,  15));
+	_channels[4]->load(loadData(0x3667,  16));
+	_channels[5]->load(loadData(0x3677,  17));
 	return 0;
 }
 
 int ASound1::commandMusic2() {
 	ASound::command1();
-	_channels[0].load(loadData(0x3688, 499));
-	_channels[1].load(loadData(0x387B, 390));
-	_channels[2].load(loadData(0x3A01, 453));
-	_channels[3].load(loadData(0x3BC6, 363));
-	_channels[4].load(loadData(0x3D31,  16));
-	_channels[5].load(loadData(0x3D41,  17));
+	_channels[0]->load(loadData(0x3688, 499));
+	_channels[1]->load(loadData(0x387B, 390));
+	_channels[2]->load(loadData(0x3A01, 453));
+	_channels[3]->load(loadData(0x3BC6, 363));
+	_channels[4]->load(loadData(0x3D31,  16));
+	_channels[5]->load(loadData(0x3D41,  17));
 	return 0;
 }
 
 int ASound1::commandMusic3() {
 	ASound::command1();
-	_channels[0].load(loadData(0x3D52, 641));
-	_channels[1].load(loadData(0x3FD3, 556));
-	_channels[2].load(loadData(0x41FF,  13));
-	_channels[3].load(loadData(0x420C,  13));
-	_channels[4].load(loadData(0x4219,  16));
-	_channels[5].load(loadData(0x4229,  17));
+	_channels[0]->load(loadData(0x3D52, 641));
+	_channels[1]->load(loadData(0x3FD3, 556));
+	_channels[2]->load(loadData(0x41FF,  13));
+	_channels[3]->load(loadData(0x420C,  13));
+	_channels[4]->load(loadData(0x4219,  16));
+	_channels[5]->load(loadData(0x4229,  17));
 	return 0;
 }
 
@@ -747,13 +193,13 @@ int ASound1::commandMusic3() {
 // pieces (identified by their starting offset in field_17), leave it alone.
 // Otherwise pick a piece at random: the original uses getRandomNumber() & 7,
 // discarding 0 and indexing a four-entry table repeated twice (entries 1–3
-// → pieces 1–3, entries 4–7 → same pieces again with entry 4 wrapping to
+// -> pieces 1–3, entries 4–7 -> same pieces again with entry 4 wrapping to
 // piece 0).  We reproduce this with a modulo-4 on a non-zero value.
 // ---------------------------------------------------------------------------
 int ASound1::command16() {
-	if (_channels[0]._activeCount) {
-		int f = _channels[0]._field17;
-		if (f == 0x1ECA || f == 0x21C4 ||
+	if (_channels[0]->_activeCount) {
+		int f = getDataOffset(_channels[0]->_loopStartPtr);
+		if (f == 0 || f == 0x1ECA || f == 0x21C4 ||
 		    f == 0x3418 || f == 0x3688 || f == 0x3D52)
 			return 0;
 	}
@@ -806,12 +252,12 @@ int ASound1::command27() {
 
 // command32 (sub_11DD4) – no guard, no fade, load ch0–5
 int ASound1::command32() {
-	_channels[0].load(loadData(0x2522, 59));
-	_channels[1].load(loadData(0x255D, 52));
-	_channels[2].load(loadData(0x2591, 42));
-	_channels[3].load(loadData(0x25BB, 44));
-	_channels[4].load(loadData(0x25E7, 44));
-	_channels[5].load(loadData(0x2613, 89));
+	_channels[0]->load(loadData(0x2522, 59));
+	_channels[1]->load(loadData(0x255D, 52));
+	_channels[2]->load(loadData(0x2591, 42));
+	_channels[3]->load(loadData(0x25BB, 44));
+	_channels[4]->load(loadData(0x25E7, 44));
+	_channels[5]->load(loadData(0x2613, 89));
 	return 0;
 }
 
@@ -820,12 +266,12 @@ int ASound1::command33() {
 	byte *pData = loadData(0x266C, 701);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x2929, 500));
-		_channels[2].load(loadData(0x2B1D, 538));
-		_channels[3].load(loadData(0x2D37, 396));
-		_channels[4].load(loadData(0x2EC3, 368));
-		_channels[5].load(loadData(0x3033, 493));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x2929, 500));
+		_channels[2]->load(loadData(0x2B1D, 538));
+		_channels[3]->load(loadData(0x2D37, 396));
+		_channels[4]->load(loadData(0x2EC3, 368));
+		_channels[5]->load(loadData(0x3033, 493));
 	}
 	return 0;
 }
@@ -835,12 +281,12 @@ int ASound1::command34() {
 	byte *pData = loadData(0x1852, 599);
 	if (!isSoundActive(pData)) {
 		stop();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x1AA9, 283));
-		_channels[2].load(loadData(0x1BC4, 301));
-		_channels[3].load(loadData(0x1CF1, 257));
-		_channels[4].load(loadData(0x1DF2, 204));
-		_channels[5].load(loadData(0x1EBE,  12));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x1AA9, 283));
+		_channels[2]->load(loadData(0x1BC4, 301));
+		_channels[3]->load(loadData(0x1CF1, 257));
+		_channels[4]->load(loadData(0x1DF2, 204));
+		_channels[5]->load(loadData(0x1EBE,  12));
 	}
 	return 0;
 }
@@ -851,12 +297,12 @@ int ASound1::command35() {
 	byte *pData = loadData(0x0C36, 329);
 	if (!isSoundActive(pData)) {
 		ASound::command2();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0D7F, 201));
-		_channels[2].load(loadData(0x0E48, 200));
-		_channels[3].load(loadData(0x0F10, 162));
-		_channels[4].load(loadData(0x0FB2, 228));
-		_channels[5].load(loadData(0x1096, 250));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0D7F, 201));
+		_channels[2]->load(loadData(0x0E48, 200));
+		_channels[3]->load(loadData(0x0F10, 162));
+		_channels[4]->load(loadData(0x0FB2, 228));
+		_channels[5]->load(loadData(0x1096, 250));
 	}
 	return 0;
 }
@@ -867,26 +313,26 @@ int ASound1::command36() {
 	byte *pData = loadData(0x1190, 327);
 	if (!isSoundActive(pData)) {
 		ASound::command2();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x12D7, 211));
-		_channels[2].load(loadData(0x13AA, 204));
-		_channels[3].load(loadData(0x1476, 178));
-		_channels[4].load(loadData(0x1528, 236));
-		_channels[5].load(loadData(0x1614, 294));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x12D7, 211));
+		_channels[2]->load(loadData(0x13AA, 204));
+		_channels[3]->load(loadData(0x1476, 178));
+		_channels[4]->load(loadData(0x1528, 236));
+		_channels[5]->load(loadData(0x1614, 294));
 	}
 	return 0;
 }
 
 // command37 (sub_11E32) – isSoundActive guard, command1, four loadAny
-// calls starting from channel 0 (sub_10352 = playSoundData(pData, 0))
+// calls starting from channel 0 (sub_10352 = findFreeChannel(pData, 0))
 int ASound1::command37() {
 	byte *pData = loadData(0x3220, 74);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		playSoundData(pData, 0);
-		playSoundData(loadData(0x326A, 41), 0);
-		playSoundData(loadData(0x3293, 25), 0);
-		playSoundData(loadData(0x32AC, 14), 0);
+		findFreeChannel(pData);
+		findFreeChannel(loadData(0x326A, 41));
+		findFreeChannel(loadData(0x3293, 25));
+		findFreeChannel(loadData(0x32AC, 14));
 	}
 	return 0;
 }
@@ -902,12 +348,12 @@ int ASound1::command39() {
 	byte *pData = loadData(0x423A, 421);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x43DF, 280));
-		_channels[2].load(loadData(0x44F7, 246));
-		_channels[3].load(loadData(0x45ED, 268));
-		_channels[4].load(loadData(0x46F9, 438));
-		_channels[5].load(loadData(0x48AF,   0));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x43DF, 280));
+		_channels[2]->load(loadData(0x44F7, 246));
+		_channels[3]->load(loadData(0x45ED, 268));
+		_channels[4]->load(loadData(0x46F9, 438));
+		_channels[5]->load(loadData(0x48AF,   0));
 	}
 	return 0;
 }
@@ -937,8 +383,8 @@ int ASound1::command39() {
  * loc_10548 = command7                                                   *
  * unk_106B2 = command8 (OR of all channel activeCount fields)            *
  * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = playSoundData(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
- * sub_10352 = playSoundData(pData, 0)                     (lower pool)  *
+ * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
+ * sub_10352 = findFreeChannel(pData, 0)                     (lower pool)  *
  *                                                                        *
  * commands5 entry 4 (command 68) = nullsub_3 = no-op                    *
  *-----------------------------------------------------------------------*/
@@ -977,15 +423,13 @@ const ASound2::CommandPtr ASound2::_commandList[73] = {
 };
 
 ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl)
-		: PhantomASound(mixer, opl, "asound.ph2", 0x2040) {
+		: ASound(mixer, opl, "asound.ph2", 0x2040, 0x2300) {
 }
 
 int ASound2::command(int commandId, int param) {
 	if (commandId > 72 || !_commandList[commandId])
 		return 0;
 
-	_commandParam = param;
-	_frameCounter = 0;
 	return (this->*_commandList[commandId])();
 }
 
@@ -1007,12 +451,12 @@ int ASound2::command16() {
 	byte *pData = loadData(0x0C36, 88);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0C8E, 102));
-		_channels[2].load(loadData(0x0CF4,  90));
-		_channels[3].load(loadData(0x0D4E,  85));
-		_channels[4].load(loadData(0x0DA3,  14));
-		_channels[5].load(loadData(0x0DB1,  15));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0C8E, 102));
+		_channels[2]->load(loadData(0x0CF4,  90));
+		_channels[3]->load(loadData(0x0D4E,  85));
+		_channels[4]->load(loadData(0x0DA3,  14));
+		_channels[5]->load(loadData(0x0DB1,  15));
 	}
 	return 0;
 }
@@ -1052,15 +496,15 @@ int ASound2::command27() {
 // ---------------------------------------------------------------------------
 
 // command32 (sub_11DDC) – command1, six loadAny calls from channel 0
-// (sub_10352 = playSoundData(pData, 0))
+// (sub_10352 = findFreeChannel(pData, 0))
 int ASound2::command32() {
 	ASound::command1();
-	playSoundData(loadData(0x1BE4, 211), 0);
-	playSoundData(loadData(0x1CB7, 359), 0);
-	playSoundData(loadData(0x1E1E, 170), 0);
-	playSoundData(loadData(0x1EC8,  16), 0);
-	playSoundData(loadData(0x1ED8,  23), 0);
-	playSoundData(loadData(0x1EEF,  19), 0);
+	findFreeChannel(loadData(0x1BE4, 211));
+	findFreeChannel(loadData(0x1CB7, 359));
+	findFreeChannel(loadData(0x1E1E, 170));
+	findFreeChannel(loadData(0x1EC8,  16));
+	findFreeChannel(loadData(0x1ED8,  23));
+	findFreeChannel(loadData(0x1EEF,  19));
 	return 0;
 }
 
@@ -1069,14 +513,14 @@ int ASound2::command33() {
 	byte *pData = loadData(0x1B62, 53);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x1B97, 14));
-		_channels[2].load(loadData(0x1BA5, 14));
-		_channels[3].load(loadData(0x1BB3, 14));
-		_channels[4].load(loadData(0x1BC1,  4));
-		_channels[5].load(loadData(0x1BC5,  4));
-		_channels[6].load(loadData(0x1BC9, 12));
-		_channels[7].load(loadData(0x1BD5, 15));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x1B97, 14));
+		_channels[2]->load(loadData(0x1BA5, 14));
+		_channels[3]->load(loadData(0x1BB3, 14));
+		_channels[4]->load(loadData(0x1BC1,  4));
+		_channels[5]->load(loadData(0x1BC5,  4));
+		_channels[6]->load(loadData(0x1BC9, 12));
+		_channels[7]->load(loadData(0x1BD5, 15));
 	}
 	return 0;
 }
@@ -1086,13 +530,13 @@ int ASound2::command34() {
 	byte *pData = loadData(0x0DC0, 495);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0FAF, 599));
-		_channels[2].load(loadData(0x1206, 404));
-		_channels[3].load(loadData(0x139A, 459));
-		_channels[4].load(loadData(0x1565, 718));
-		_channels[5].load(loadData(0x1833, 154));
-		_channels[6].load(loadData(0x18CD,  91));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0FAF, 599));
+		_channels[2]->load(loadData(0x1206, 404));
+		_channels[3]->load(loadData(0x139A, 459));
+		_channels[4]->load(loadData(0x1565, 718));
+		_channels[5]->load(loadData(0x1833, 154));
+		_channels[6]->load(loadData(0x18CD,  91));
 	}
 	return 0;
 }
@@ -1102,13 +546,13 @@ int ASound2::command35() {
 	byte *pData = loadData(0x1F02, 100);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x1F66,  10));
-		_channels[2].load(loadData(0x1F70,  29));
-		_channels[3].load(loadData(0x1F8D,  65));
-		_channels[4].load(loadData(0x1FCE,  41));
-		_channels[5].load(loadData(0x1FF7,  55));
-		_channels[6].load(loadData(0x202E,  34));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x1F66,  10));
+		_channels[2]->load(loadData(0x1F70,  29));
+		_channels[3]->load(loadData(0x1F8D,  65));
+		_channels[4]->load(loadData(0x1FCE,  41));
+		_channels[5]->load(loadData(0x1FF7,  55));
+		_channels[6]->load(loadData(0x202E,  34));
 	}
 	return 0;
 }
@@ -1194,7 +638,7 @@ int ASound2::command72() {
  *   loc_10427 =ch8                                                       *
  *                                                                        *
  * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = playSoundData(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
+ * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
  *                                                                        *
  * sub_11CC6 (helper) – isSoundActive guard on 0xC36, command1,          *
  *   loads ch0–7; called by command34 which then adds ch8 at 0x298E.     *
@@ -1237,15 +681,13 @@ const ASound3::CommandPtr ASound3::_commandList[77] = {
 };
 
 ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl)
-		: PhantomASound(mixer, opl, "asound.ph3", 0x20c0) {
+		: ASound(mixer, opl, "asound.ph3", 0x20c0, 0x31a0) {
 }
 
 int ASound3::command(int commandId, int param) {
 	if (commandId > 76 || !_commandList[commandId])
 		return 0;
-
-	_commandParam = param;
-	_frameCounter = 0;
+	
 	return (this->*_commandList[commandId])();
 }
 
@@ -1270,14 +712,14 @@ void ASound3::sub11CC6() {
 	byte *pData = loadData(0x0C36, 53);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0C6B, 14));
-		_channels[2].load(loadData(0x0C79, 14));
-		_channels[3].load(loadData(0x0C87, 14));
-		_channels[4].load(loadData(0x0C95,  4));
-		_channels[5].load(loadData(0x0C99,  4));
-		_channels[6].load(loadData(0x0C9D, 12));
-		_channels[7].load(loadData(0x0CA9, 15));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0C6B, 14));
+		_channels[2]->load(loadData(0x0C79, 14));
+		_channels[3]->load(loadData(0x0C87, 14));
+		_channels[4]->load(loadData(0x0C95,  4));
+		_channels[5]->load(loadData(0x0C99,  4));
+		_channels[6]->load(loadData(0x0C9D, 12));
+		_channels[7]->load(loadData(0x0CA9, 15));
 	}
 }
 
@@ -1288,12 +730,12 @@ int ASound3::command16() {
 	byte *pData = loadData(0x24F2, 172);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x259E, 137));
-		_channels[2].load(loadData(0x2627, 135));
-		_channels[3].load(loadData(0x26AE, 179));
-		_channels[4].load(loadData(0x2761, 175));
-		_channels[5].load(loadData(0x2810, 186));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x259E, 137));
+		_channels[2]->load(loadData(0x2627, 135));
+		_channels[3]->load(loadData(0x26AE, 179));
+		_channels[4]->load(loadData(0x2761, 175));
+		_channels[5]->load(loadData(0x2810, 186));
 	}
 	return 0;
 }
@@ -1337,14 +779,14 @@ int ASound3::command32() {
 	byte *pData = loadData(0x2B94, 108);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x2C00,  73));
-		_channels[2].load(loadData(0x2C49,  67));
-		_channels[3].load(loadData(0x2C8C, 151));
-		_channels[4].load(loadData(0x2D23, 171));
-		_channels[5].load(loadData(0x2DCE,  87));
-		_channels[6].load(loadData(0x2E25,  95));
-		_channels[7].load(loadData(0x2E84, 110));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x2C00,  73));
+		_channels[2]->load(loadData(0x2C49,  67));
+		_channels[3]->load(loadData(0x2C8C, 151));
+		_channels[4]->load(loadData(0x2D23, 171));
+		_channels[5]->load(loadData(0x2DCE,  87));
+		_channels[6]->load(loadData(0x2E25,  95));
+		_channels[7]->load(loadData(0x2E84, 110));
 	}
 	return 0;
 }
@@ -1354,13 +796,13 @@ int ASound3::command33() {
 	byte *pData = loadData(0x149E, 525);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x16AB, 648));
-		_channels[2].load(loadData(0x1933, 252));
-		_channels[3].load(loadData(0x1A2F, 502));
-		_channels[4].load(loadData(0x1C25, 680));
-		_channels[5].load(loadData(0x1ECD, 418));
-		_channels[6].load(loadData(0x206F,   3));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x16AB, 648));
+		_channels[2]->load(loadData(0x1933, 252));
+		_channels[3]->load(loadData(0x1A2F, 502));
+		_channels[4]->load(loadData(0x1C25, 680));
+		_channels[5]->load(loadData(0x1ECD, 418));
+		_channels[6]->load(loadData(0x206F,   3));
 	}
 	return 0;
 }
@@ -1369,7 +811,7 @@ int ASound3::command33() {
 // then unconditionally loads ch8 at 0x298E
 int ASound3::command34() {
 	sub11CC6();
-	_channels[8].load(loadData(0x298E, 10));
+	_channels[8]->load(loadData(0x298E, 10));
 	return 0;
 }
 
@@ -1378,12 +820,12 @@ int ASound3::command35() {
 	byte *pData = loadData(0x0CB8, 413);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0E55, 270));
-		_channels[2].load(loadData(0x0F63, 238));
-		_channels[3].load(loadData(0x1051, 264));
-		_channels[4].load(loadData(0x1159, 434));
-		_channels[5].load(loadData(0x130B, 403));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0E55, 270));
+		_channels[2]->load(loadData(0x0F63, 238));
+		_channels[3]->load(loadData(0x1051, 264));
+		_channels[4]->load(loadData(0x1159, 434));
+		_channels[5]->load(loadData(0x130B, 403));
 	}
 	return 0;
 }
@@ -1393,12 +835,12 @@ int ASound3::command36() {
 	byte *pData = loadData(0x2072, 196);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x2136, 692));
-		_channels[2].load(loadData(0x23EA,  83));
-		_channels[3].load(loadData(0x243D,  24));
-		_channels[4].load(loadData(0x2455,  78));
-		_channels[5].load(loadData(0x24A3,  79));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x2136, 692));
+		_channels[2]->load(loadData(0x23EA,  83));
+		_channels[3]->load(loadData(0x243D,  24));
+		_channels[4]->load(loadData(0x2455,  78));
+		_channels[5]->load(loadData(0x24A3,  79));
 	}
 	return 0;
 }
@@ -1420,23 +862,23 @@ int ASound3::command37() {
 
 // sub_11DC8 – ch6 + ch8
 int ASound3::command64() {
-	_channels[6].load(loadData(0x28CA, 50));
-	_channels[8].load(loadData(0x28FC, 29));
+	_channels[6]->load(loadData(0x28CA, 50));
+	_channels[8]->load(loadData(0x28FC, 29));
 	return 0;
 }
 
 // sub_11DD4 – ch6 + ch8
 int ASound3::command65() {
-	_channels[6].load(loadData(0x2919, 17));
-	_channels[8].load(loadData(0x292A, 13));
+	_channels[6]->load(loadData(0x2919, 17));
+	_channels[8]->load(loadData(0x292A, 13));
 	return 0;
 }
 
 // sub_11DE0 – ch6 + ch7 + ch8
 int ASound3::command66() {
-	_channels[6].load(loadData(0x2937, 31));
-	_channels[7].load(loadData(0x2956, 15));
-	_channels[8].load(loadData(0x2965, 31));
+	_channels[6]->load(loadData(0x2937, 31));
+	_channels[7]->load(loadData(0x2956, 15));
+	_channels[8]->load(loadData(0x2965, 31));
 	return 0;
 }
 
@@ -1474,8 +916,8 @@ int ASound3::command71() {
 
 // sub_11E22 – ch7 + ch8
 int ASound3::command72() {
-	_channels[7].load(loadData(0x29EA, 17));
-	_channels[8].load(loadData(0x2A18, 17));
+	_channels[7]->load(loadData(0x29EA, 17));
+	_channels[8]->load(loadData(0x2A18, 17));
 	return 0;
 }
 
@@ -1493,8 +935,8 @@ int ASound3::command74() {
 
 // sub_11E47 – ch7 + ch8
 int ASound3::command75() {
-	_channels[7].load(loadData(0x29FB, 29));
-	_channels[8].load(loadData(0x2A29, 27));
+	_channels[7]->load(loadData(0x29FB, 29));
+	_channels[8]->load(loadData(0x2A29, 27));
 	return 0;
 }
 
@@ -1516,7 +958,7 @@ int ASound3::command75() {
  *   sub_10413=ch4  sub_10418=ch5  sub_1041D=ch6                         *
  *                                                                        *
  * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = playSoundData(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
+ * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
  *                                                                        *
  * commands 24 and 25 share the same handler (sub_11D0A).                *
  * Two unreferenced subs (sub_11D5E, sub_11D6B) contain dead sound data  *
@@ -1556,15 +998,13 @@ const ASound4::CommandPtr ASound4::_commandList[71] = {
 };
 
 ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl)
-		: PhantomASound(mixer, opl, "asound.ph4", 0x1f90) {
+		: ASound(mixer, opl, "asound.ph4", 0x1f90, 0x14d0) {
 }
 
 int ASound4::command(int commandId, int param) {
 	if (commandId > 70 || !_commandList[commandId])
 		return 0;
-
-	_commandParam = param;
-	_frameCounter = 0;
+	
 	return (this->*_commandList[commandId])();
 }
 
@@ -1586,13 +1026,13 @@ int ASound4::command16() {
 	byte *pData = loadData(0x0C36, 63);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0C75, 636));
-		_channels[2].load(loadData(0x0EF1,  40));
-		_channels[3].load(loadData(0x0F19,  40));
-		_channels[4].load(loadData(0x0F41,  38));
-		_channels[5].load(loadData(0x0F67,  41));
-		_channels[6].load(loadData(0x0F90, 106));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0C75, 636));
+		_channels[2]->load(loadData(0x0EF1,  40));
+		_channels[3]->load(loadData(0x0F19,  40));
+		_channels[4]->load(loadData(0x0F41,  38));
+		_channels[5]->load(loadData(0x0F67,  41));
+		_channels[6]->load(loadData(0x0F90, 106));
 	}
 	return 0;
 }
@@ -1699,8 +1139,8 @@ int ASound4::command70() {
  *                                                                        *
  * In this driver IDA named the lower-pool loadAny as AdlibChannel_loadAny*
  * (starts from ch0) and sub_1039C as the upper-pool loader (ch6-8).     *
- *   AdlibChannel_loadAny → playSoundData(pData, 0)                      *
- *   sub_1039C            → playSound() / playSoundData(pData, MIDWAY)   *
+ *   AdlibChannel_loadAny -> findFreeChannel(pData, 0)                      *
+ *   sub_1039C            -> playSound() / findFreeChannel(pData, MIDWAY)   *
  *                                                                        *
  * loc_11D42 (cmd37) and loc_11D72 (cmd36) load channels in non-         *
  * sequential order — the sound data for each channel is not stored       *
@@ -1745,15 +1185,13 @@ const ASound5::CommandPtr ASound5::_commandList[79] = {
 };
 
 ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl)
-		: PhantomASound(mixer, opl, "asound.ph5", 0x2140) {
+		: ASound(mixer, opl, "asound.ph5", 0x2140, 0x5cd0) {
 }
 
 int ASound5::command(int commandId, int param) {
 	if (commandId > 78 || !_commandList[commandId])
 		return 0;
-
-	_commandParam = param;
-	_frameCounter = 0;
+	
 	return (this->*_commandList[commandId])();
 }
 
@@ -1775,12 +1213,12 @@ int ASound5::command16() {
 	byte *pData = loadData(0x4142, 120);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x41BA, 146));
-		_channels[2].load(loadData(0x424C, 133));
-		_channels[3].load(loadData(0x42D1,  69));
-		_channels[4].load(loadData(0x4316, 152));
-		_channels[5].load(loadData(0x43AE,  14));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x41BA, 146));
+		_channels[2]->load(loadData(0x424C, 133));
+		_channels[3]->load(loadData(0x42D1,  69));
+		_channels[4]->load(loadData(0x4316, 152));
+		_channels[5]->load(loadData(0x43AE,  14));
 	}
 	return 0;
 }
@@ -1824,14 +1262,14 @@ int ASound5::command27() {
 // sub_11EB4 – command1, eight loadAny (lower pool, AdlibChannel_loadAny)
 int ASound5::command32() {
 	ASound::command1();
-	playSoundData(loadData(0x43BC, 689), 0);
-	playSoundData(loadData(0x466D, 262), 0);
-	playSoundData(loadData(0x4773, 480), 0);
-	playSoundData(loadData(0x4953, 504), 0);
-	playSoundData(loadData(0x4B4B, 584), 0);
-	playSoundData(loadData(0x4D93, 308), 0);
-	playSoundData(loadData(0x4EC7, 426), 0);
-	playSoundData(loadData(0x5071, 357), 0);
+	findFreeChannel(loadData(0x43BC, 689));
+	findFreeChannel(loadData(0x466D, 262));
+	findFreeChannel(loadData(0x4773, 480));
+	findFreeChannel(loadData(0x4953, 504));
+	findFreeChannel(loadData(0x4B4B, 584));
+	findFreeChannel(loadData(0x4D93, 308));
+	findFreeChannel(loadData(0x4EC7, 426));
+	findFreeChannel(loadData(0x5071, 357));
 	return 0;
 }
 
@@ -1840,13 +1278,13 @@ int ASound5::command33() {
 	byte *pData = loadData(0x21C6, 609);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x2427, 652));
-		_channels[2].load(loadData(0x26B3, 272));
-		_channels[3].load(loadData(0x27C3, 558));
-		_channels[4].load(loadData(0x29F1, 712));
-		_channels[5].load(loadData(0x2CB9, 464));
-		_channels[6].load(loadData(0x2E89,  21));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x2427, 652));
+		_channels[2]->load(loadData(0x26B3, 272));
+		_channels[3]->load(loadData(0x27C3, 558));
+		_channels[4]->load(loadData(0x29F1, 712));
+		_channels[5]->load(loadData(0x2CB9, 464));
+		_channels[6]->load(loadData(0x2E89,  21));
 	}
 	return 0;
 }
@@ -1857,12 +1295,12 @@ int ASound5::command34() {
 	byte *pData = loadData(0x2E9E, 1521);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x4003,    7));
-		_channels[2].load(loadData(0x348F, 1424));
-		_channels[3].load(loadData(0x400A,    7));
-		_channels[4].load(loadData(0x3A1F, 1508));
-		_channels[5].load(loadData(0x4011,    9));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x4003,    7));
+		_channels[2]->load(loadData(0x348F, 1424));
+		_channels[3]->load(loadData(0x400A,    7));
+		_channels[4]->load(loadData(0x3A1F, 1508));
+		_channels[5]->load(loadData(0x4011,    9));
 	}
 	return 0;
 }
@@ -1873,12 +1311,12 @@ int ASound5::command35() {
 	byte *pData = loadData(0x1D0A, 320);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x2196,  18));
-		_channels[2].load(loadData(0x1E4A, 304));
-		_channels[3].load(loadData(0x21A8,  18));
-		_channels[4].load(loadData(0x1F7A, 540));
-		_channels[5].load(loadData(0x21BA,  12));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x2196,  18));
+		_channels[2]->load(loadData(0x1E4A, 304));
+		_channels[3]->load(loadData(0x21A8,  18));
+		_channels[4]->load(loadData(0x1F7A, 540));
+		_channels[5]->load(loadData(0x21BA,  12));
 	}
 	return 0;
 }
@@ -1889,12 +1327,12 @@ int ASound5::command36() {
 	byte *pData = loadData(0x15F5, 740);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x18E2, 326));
-		_channels[2].load(loadData(0x1A28, 561));
-		_channels[3].load(loadData(0x1C59, 177));
-		_channels[4].load(loadData(0x15EC,   9));
-		_channels[5].load(loadData(0x18D9,   9));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x18E2, 326));
+		_channels[2]->load(loadData(0x1A28, 561));
+		_channels[3]->load(loadData(0x1C59, 177));
+		_channels[4]->load(loadData(0x15EC,   9));
+		_channels[5]->load(loadData(0x18D9,   9));
 	}
 	return 0;
 }
@@ -1904,15 +1342,15 @@ int ASound5::command37() {
 	byte *pData = loadData(0x1190, 397);
 	if (!isSoundActive(pData)) {
 		ASound::command1();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x131D, 378));
-		_channels[2].load(loadData(0x1497,  60));
-		_channels[3].load(loadData(0x14D3,  64));
-		_channels[4].load(loadData(0x1513,  44));
-		_channels[5].load(loadData(0x153F,  44));
-		_channels[6].load(loadData(0x156B,  50));
-		_channels[7].load(loadData(0x159D,  52));
-		_channels[8].load(loadData(0x15D1,  27));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x131D, 378));
+		_channels[2]->load(loadData(0x1497,  60));
+		_channels[3]->load(loadData(0x14D3,  64));
+		_channels[4]->load(loadData(0x1513,  44));
+		_channels[5]->load(loadData(0x153F,  44));
+		_channels[6]->load(loadData(0x156B,  50));
+		_channels[7]->load(loadData(0x159D,  52));
+		_channels[8]->load(loadData(0x15D1,  27));
 	}
 	return 0;
 }
@@ -1922,12 +1360,12 @@ int ASound5::command38() {
 	byte *pData = loadData(0x0C36, 329);
 	if (!isSoundActive(pData)) {
 		ASound::command2();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x0D7F, 201));
-		_channels[2].load(loadData(0x0E48, 200));
-		_channels[3].load(loadData(0x0F10, 162));
-		_channels[4].load(loadData(0x0FB2, 228));
-		_channels[5].load(loadData(0x1096, 250));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x0D7F, 201));
+		_channels[2]->load(loadData(0x0E48, 200));
+		_channels[3]->load(loadData(0x0F10, 162));
+		_channels[4]->load(loadData(0x0FB2, 228));
+		_channels[5]->load(loadData(0x1096, 250));
 	}
 	return 0;
 }
@@ -1937,12 +1375,12 @@ int ASound5::command39() {
 	byte *pData = loadData(0x5312, 599);
 	if (!isSoundActive(pData)) {
 		ASound::command3();
-		_channels[0].load(pData);
-		_channels[1].load(loadData(0x5569, 275));
-		_channels[2].load(loadData(0x567C, 289));
-		_channels[3].load(loadData(0x579D, 243));
-		_channels[4].load(loadData(0x5890, 196));
-		_channels[5].load(loadData(0x5954, 206));
+		_channels[0]->load(pData);
+		_channels[1]->load(loadData(0x5569, 275));
+		_channels[2]->load(loadData(0x567C, 289));
+		_channels[3]->load(loadData(0x579D, 243));
+		_channels[4]->load(loadData(0x5890, 196));
+		_channels[5]->load(loadData(0x5954, 206));
 	}
 	return 0;
 }
@@ -2064,15 +1502,14 @@ const ASound9::CommandPtr ASound9::_commandList[72] = {
 	&ASound9::command68, &ASound9::command69, &ASound9::command70, &ASound9::command71
 };
 
-ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) : PhantomASound(mixer, opl, "asound.ph9", 0x20c0) {
+ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) :
+		ASound(mixer, opl, "asound.ph9", 0x20c0, 0x3690) {
 }
 
 int ASound9::command(int commandId, int param) {
 	if (commandId > 71 || !_commandList[commandId])
 		return 0;
-
-	_commandParam = param;
-	_frameCounter = 0;
+	
 	return (this->*_commandList[commandId])();
 }
 
diff --git a/engines/mads/madsv2/phantom/sound_phantom.h b/engines/mads/madsv2/phantom/sound_phantom.h
index 20eb373c53a..b95542a8dcd 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.h
+++ b/engines/mads/madsv2/phantom/sound_phantom.h
@@ -73,7 +73,7 @@ private:
 	int commandMusic2();   // sub_11F0E  – starts at 0x3688
 	int commandMusic3();   // sub_11F36  – starts at 0x3D52
 
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
@@ -120,7 +120,7 @@ private:
 	typedef int (ASound2::*CommandPtr)();
 	static const CommandPtr _commandList[73];
 
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
@@ -178,7 +178,7 @@ private:
 	// Called by command34 which then adds ch8.
 	void sub11CC6();
 
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
@@ -238,7 +238,7 @@ private:
 	typedef int (ASound4::*CommandPtr)();
 	static const CommandPtr _commandList[71];
 
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
@@ -288,7 +288,7 @@ private:
 	typedef int (ASound5::*CommandPtr)();
 	static const CommandPtr _commandList[79];
 
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
@@ -339,7 +339,7 @@ public:
 class ASound9 : public ASound {
 private:
 	typedef int (ASound9:: *CommandPtr)();
-	int command0() override;
+	int command0();
 	int command1();
 	int command2();
 	int command3();
diff --git a/engines/mads/nebular/core/asound.cpp b/engines/mads/nebular/core/asound.cpp
index 2ebd0b1b3fc..8e5f6844c7a 100644
--- a/engines/mads/nebular/core/asound.cpp
+++ b/engines/mads/nebular/core/asound.cpp
@@ -110,7 +110,7 @@ void AdlibChannel::load(byte *pData) {
 	_field17 = 0;
 	_field19 = 0;
 
-	CachedDataEntry &cacheEntry = _owner->getCachedData(pData);
+	auto &cacheEntry = _owner->getCachedData(pData);
 	_ptrEnd = cacheEntry._dataEnd;
 }
 
@@ -154,8 +154,8 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
 
 /*-----------------------------------------------------------------------*/
 
-ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset) :
-	SoundDriver(mixer, opl, filename, dataOffset) {
+ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize) : SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
 	// Initialize fields
 	_commandParam = 0;
 	_activeChannelPtr = nullptr;
@@ -242,27 +242,6 @@ void ASound::noise() {
 	}
 }
 
-byte *ASound::getDataPtr(int nearPtr) {
-	for (auto i = _dataCache.begin(); i != _dataCache.end(); ++i) {
-		CachedDataEntry &e = *i;
-		int entrySize = (int)(e._dataEnd - e._data) + 1;
-		if (nearPtr >= e._offset && nearPtr < e._offset + entrySize)
-			return e._data + (nearPtr - e._offset);
-	}
-	return nullptr;
-}
-
-CachedDataEntry &ASound::getCachedData(byte *pData) {
-	Common::List<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
-		CachedDataEntry &e = *i;
-		if (e._data == pData)
-			return e;
-	}
-
-	error("Could not find previously loaded data");
-}
-
 void ASound::write(int reg, int val) {
 	_queue.push(RegisterValue(reg, val));
 }
diff --git a/engines/mads/nebular/core/asound.h b/engines/mads/nebular/core/asound.h
index c819d75c2c1..ad7cbb529a0 100644
--- a/engines/mads/nebular/core/asound.h
+++ b/engines/mads/nebular/core/asound.h
@@ -129,7 +129,6 @@ struct RegisterValue {
 
 #define ADLIB_CHANNEL_COUNT 9
 #define ADLIB_CHANNEL_MIDWAY 5
-#define CALLBACKS_PER_SECOND 60
 
 /**
  * Base class for the sound player resource files
@@ -310,8 +309,10 @@ public:
 	 * @param opl			OPL
 	 * @param filename		Specifies the adlib sound player file to use
 	 * @param dataOffset	Offset in the file of the data segment
+	 * @param dataSize		Size of the data segment
 	 */
-	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename, int dataOffset);
+	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize);
 
 	/**
 	 * Destructor
@@ -346,11 +347,6 @@ public:
 		return _frameCounter;
 	}
 
-	/**
-	 * Return the cached data block record for previously loaded sound data
-	 */
-	CachedDataEntry &getCachedData(byte *pData);
-
 	/**
 	 * Set the volume
 	 */
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index 3d6d7c54b44..20cb8a79b7d 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -21,6 +21,7 @@
 
 #include "audio/fmopl.h"
 #include "common/algorithm.h"
+#include "common/file.h"
 #include "common/md5.h"
 #include "mads/nebular/sound_nebular.h"
 
@@ -96,8 +97,8 @@ void RexSoundManager::loadDriver(int sectionNumber) {
 /*-----------------------------------------------------------------------*/
 
 RexASound::RexASound(Audio::Mixer *mixer, OPL::OPL *opl,
-		const Common::Path &filename, int dataOffset) :
-		ASound(mixer, opl, filename, dataOffset) {
+		const Common::Path &filename, int dataOffset, int dataSize) :
+		ASound(mixer, opl, filename, dataOffset, dataSize) {
 	_chanCommandCount = 15;
 }
 
@@ -279,13 +280,13 @@ const ASound1::CommandPtr ASound1::_commandList[42] = {
 };
 
 ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl)
-	: RexASound(mixer, opl, "asound.001", 0x1520) {
+	: RexASound(mixer, opl, "asound.001", 0x1520, 0x17b0) {
 	_cmd23Toggle = false;
 
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x12C);
+	auto samplesStream = getDataStream(0x12C);
 	for (int i = 0; i < 98; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound1::command(int commandId, int param) {
@@ -579,13 +580,14 @@ const ASound2::CommandPtr ASound2::_commandList[44] = {
 	&ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43
 };
 
-ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.002", 0x15E0) {
+ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.002", 0x15E0, 0x4b70) {
 	_command12Param = 0xFD;
 
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x144);
+	auto samplesStream = getDataStream(0x144);
 	for (int i = 0; i < 164; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound2::command(int commandId, int param) {
@@ -950,13 +952,14 @@ const ASound3::CommandPtr ASound3::_commandList[61] = {
 	&ASound3::command60
 };
 
-ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.003", 0x15B0) {
+ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.003", 0x15B0, 0x5020) {
 	_command39Flag = false;
 
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x122);
+	auto samplesStream = getDataStream(0x122);
 	for (int i = 0; i < 192; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound3::command(int commandId, int param) {
@@ -1354,11 +1357,12 @@ const ASound4::CommandPtr ASound4::_commandList[61] = {
 	&ASound4::command60
 };
 
-ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.004", 0x14F0) {
+ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.004", 0x14F0, 0x2930) {
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x122);
+	auto samplesStream = getDataStream(0x122);
 	for (int i = 0; i < 210; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound4::command(int commandId, int param) {
@@ -1610,11 +1614,12 @@ const ASound5::CommandPtr ASound5::_commandList[42] = {
 	&ASound5::command40, &ASound5::command41
 };
 
-ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.002", 0x15E0) {
+ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.005", 0x15E0, 0x2200) {
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x144);
+	auto samplesStream = getDataStream(0x144);
 	for (int i = 0; i < 164; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound5::command(int commandId, int param) {
@@ -1851,11 +1856,12 @@ const ASound6::CommandPtr ASound6::_commandList[30] = {
 	&ASound6::nullCommand, &ASound6::command29
 };
 
-ASound6::ASound6(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.006", 0x1390) {
+ASound6::ASound6(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.006", 0x1390, 0x22d0) {
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x122);
+	auto samplesStream = getDataStream(0x122);
 	for (int i = 0; i < 200; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound6::command(int commandId, int param) {
@@ -2007,11 +2013,12 @@ const ASound7::CommandPtr ASound7::_commandList[38] = {
 	&ASound7::command36, &ASound7::command37
 };
 
-ASound7::ASound7(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.007", 0x1460) {
+ASound7::ASound7(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.007", 0x1460, 0x2cf0) {
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x122);
+	auto samplesStream = getDataStream(0x122);
 	for (int i = 0; i < 214; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound7::command(int commandId, int param) {
@@ -2213,11 +2220,12 @@ const ASound8::CommandPtr ASound8::_commandList[38] = {
 	&ASound8::command36, &ASound8::command37
 };
 
-ASound8::ASound8(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.008", 0x1490) {
+ASound8::ASound8(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.008", 0x1490, 0x1810) {
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x122);
+	auto samplesStream = getDataStream(0x122);
 	for (int i = 0; i < 174; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound8::command(int commandId, int param) {
@@ -2469,14 +2477,15 @@ const ASound9::CommandPtr ASound9::_commandList[52] = {
 	&ASound9::command48, &ASound9::command49, &ASound9::command50, &ASound9::command51
 };
 
-ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) : RexASound(mixer, opl, "asound.009", 0x16F0) {
+ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) :
+		RexASound(mixer, opl, "asound.009", 0x16F0, 0x85a0) {
 	_v1 = _v2 = 0;
 	_soundPtr = nullptr;
 
 	// Load sound samples
-	_soundFile.seek(_dataOffset + 0x50);
+	auto samplesStream = getDataStream(0x50);
 	for (int i = 0; i < 94; ++i)
-		_samples.push_back(AdlibSample(_soundFile));
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound9::command(int commandId, int param) {
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index 3a25a4bee2f..4a5d87c1a9d 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -46,7 +46,7 @@ protected:
 
 public:
 	RexASound(Audio::Mixer *mixer, OPL::OPL *opl,
-		const Common::Path &filename, int dataOffset);
+		const Common::Path &filename, int dataOffset, int dataSize);
 };
 
 class ASound1 : public RexASound {


Commit: af3b88339148dfda044bc97aa9941f22bbfdf58c
    https://github.com/scummvm/scummvm/commit/af3b88339148dfda044bc97aa9941f22bbfdf58c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:44+10:00

Commit Message:
MADS: PHANTOM: Renaming ASound generic arrays/fields to proper names

Changed paths:
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 20982cc73fe..bd007624b69 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -32,10 +32,10 @@ bool AdlibChannel::_isDisabled;
  * ========================================================================= */
 
  /*
-  * byte_12072 - Patch-attenuation to OPL total-level conversion table.
+  * PATCH_ATTEN_TO_TL - Patch-attenuation to OPL total-level conversion table.
   * Index is the patchAttenuation value (0-127); output is the 6-bit TL field.
   */
-static const uint8 byte_12072[128] = {
+static const uint8 PATCH_ATTEN_TO_TL[128] = {
 	0x3F,0x36,0x31,0x2D,0x2A,0x28,0x26,0x24,0x22,0x21,0x20,
 	0x1F,0x1E,0x1D,0x1C,0x1B,0x1A,0x19,0x19,0x18,0x17,0x17,
 	0x16,0x16,0x15,0x15,0x14,0x14,0x13,0x13,0x12,0x12,0x12,
@@ -51,10 +51,10 @@ static const uint8 byte_12072[128] = {
 };
 
 /*
- * byte_120F2 - Volume/velocity to OPL attenuation step table.
+ * VOL_VEL_TO_ATTEN_STEP - Volume/velocity to OPL attenuation step table.
  * 128 entries; each group of 4 maps to the same step value (1..32).
  */
-static const uint8 byte_120F2[128] = {
+static const uint8 VOL_VEL_TO_ATTEN_STEP[128] = {
 	0x01,0x01,0x01,0x01, 0x02,0x02,0x02,0x02, 0x03,0x03,0x03,0x03,
 	0x04,0x04,0x04,0x04, 0x05,0x05,0x05,0x05, 0x06,0x06,0x06,0x06,
 	0x07,0x07,0x07,0x07, 0x08,0x08,0x08,0x08, 0x09,0x09,0x09,0x09,
@@ -68,32 +68,32 @@ static const uint8 byte_120F2[128] = {
 	0x1F,0x1F,0x1F,0x1F, 0x20,0x20,0x20,0x20
 };
 
-/* Carrier operator slot offset per voice (byte_12190, index 0 = unused pad) */
-static const uint8 byte_12190[18] = {
+/* Carrier operator slot offset per voice (CARRIER_SLOT_FOR_VOICE, index 0 = unused pad) */
+static const uint8 CARRIER_SLOT_FOR_VOICE[18] = {
 	0,3,1,4,2,5,6,9,10,8,11,12,15,13,16,14,17,0
 };
 
 /*
- * byte_12191 - Modulator operator slot offset per voice.
+ * MODULATOR_SLOT_FOR_VOICE - Modulator operator slot offset per voice.
  * 17 entries used (voices 0-8 -> modulator slots, voices 0-8 -> carrier slots).
  */
-static const uint8 byte_12191[17] = {
+static const uint8 MODULATOR_SLOT_FOR_VOICE[17] = {
 	3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17
 };
 
 /*
- * byte_121A2 - Operator slot index to OPL register offset.
+ * SLOT_TO_REG_OFFSET - Operator slot index to OPL register offset.
  * Maps an operator slot (0-17) to its base register offset within a group.
  */
-static const uint8 byte_121A2[18] = {
+static const uint8 SLOT_TO_REG_OFFSET[18] = {
 	0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21
 };
 
 /*
- * word_12174 - F-number table: one entry per semitone within an octave.
+ * SEMITONE_FREQ_TABLE - F-number table: one entry per semitone within an octave.
  * 12 entries (C through B).
  */
-static const uint16 word_12174[12] = {
+static const uint16 SEMITONE_FREQ_TABLE[12] = {
 	0x0200,0x021E,0x023F,0x0261,0x0285,0x02AB,
 	0x02D4,0x02FF,0x032D,0x035D,0x0390,0x03C7
 };
@@ -296,7 +296,7 @@ int ASound::command6() {
 	_channel2._savedFreqSweep = _channel2._freqSweepCounter;
 	_channel3._savedFreqSweep = _channel3._freqSweepCounter;
 	_channel4._savedFreqSweep = _channel4._freqSweepCounter;
-	byte_16B5D = byte_16B5C;   /* channel5 shadow */
+	_ch5SweepSaved = _ch5SweepLive;   /* channel5 shadow */
 	_channel6._savedFreqSweep = _channel6._freqSweepCounter;
 	_channel7._savedFreqSweep = _channel7._freqSweepCounter;
 	_channel8._savedFreqSweep = _channel8._freqSweepCounter;
@@ -307,7 +307,7 @@ int ASound::command6() {
 	_channel2._freqSweepCounter = 0;
 	_channel3._freqSweepCounter = 0;
 	_channel4._freqSweepCounter = 0;
-	byte_16B5C = 0;
+	_ch5SweepLive = 0;
 	_channel6._freqSweepCounter = 0;
 	_channel7._freqSweepCounter = 0;
 	_channel8._freqSweepCounter = 0;
@@ -347,7 +347,7 @@ int ASound::command7() {
 		_channel2._savedFreqSweep |
 		_channel3._savedFreqSweep |
 		_channel4._savedFreqSweep |
-		byte_16B5D |
+		_ch5SweepSaved |
 		_channel6._savedFreqSweep |
 		_channel7._savedFreqSweep |
 		_channel8._savedFreqSweep;
@@ -359,7 +359,7 @@ int ASound::command7() {
 		_channel2._savedFreqSweep = _channel2._freqSweepCounter;
 		_channel3._savedFreqSweep = _channel3._freqSweepCounter;
 		_channel4._savedFreqSweep = _channel4._freqSweepCounter;
-		byte_16B5D = byte_16B5C;
+		_ch5SweepSaved = _ch5SweepLive;
 		_channel6._savedFreqSweep = _channel6._freqSweepCounter;
 		_channel7._savedFreqSweep = _channel7._freqSweepCounter;
 		_channel8._savedFreqSweep = _channel8._freqSweepCounter;
@@ -377,7 +377,7 @@ int ASound::command8() {
 	result |= _channel2._activeCount;
 	result |= _channel3._activeCount;
 	result |= _channel4._activeCount;
-	result |= (uint8)(_channel5._activeCount); /* byte_16B63 area */
+	result |= (uint8)(_channel5._activeCount); /* _ch5PendingStop area */
 	result |= _channel6._activeCount;
 	result |= _channel7._activeCount;
 	result |= _channel8._activeCount;
@@ -439,9 +439,9 @@ void ASound::signalSoundPlaying() {
 }
 
 void ASound::clearCallback() {
-	word_12C06 = 0;
-	word_12C08 = 0;
-	word_12C0A = 0;
+	_callbackCounter = 0;
+	_callbackPeriod = 0;
+	_callbackFnPtr = 0;
 }
 
 /* =========================================================================
@@ -451,23 +451,23 @@ void ASound::clearCallback() {
  *
  * Two paths depending on _adlib_v5660_2:
  *   < 0x18  -> simple linear mapping of (volume + velocity) -> TL
- *   >= 0x18 -> patch-attenuation-aware mapping using byte_12072 table
+ *   >= 0x18 -> patch-attenuation-aware mapping using PATCH_ATTEN_TO_TL table
  * ========================================================================= */
 
 void ASound::writeVolume() {
 	AdlibChannel *ch = _activeChannelPtr;
 	int16_t var4, var6 = 0;
 
-	/* Step 1: map volume through byte_120F2 */
-	int16_t volStep = (int16_t)byte_120F2[(uint8)ch->_volume];
-	/* Step 2: map velocity through byte_120F2 */
-	int16_t velStep = (int16_t)byte_120F2[(int8_t)ch->_velocity];
+	/* Step 1: map volume through VOL_VEL_TO_ATTEN_STEP */
+	int16_t volStep = (int16_t)VOL_VEL_TO_ATTEN_STEP[(uint8)ch->_volume];
+	/* Step 2: map velocity through VOL_VEL_TO_ATTEN_STEP */
+	int16_t velStep = (int16_t)VOL_VEL_TO_ATTEN_STEP[(int8_t)ch->_velocity];
 	int16_t var8 = volStep + velStep - 1;   /* combined attenuation step */
 
 	/* Determine carrier operator register for this voice */
 	uint8 chanNum = _activeChannelNumber;
-	uint8 slot = byte_12191[chanNum * 2];          /* modulator slot index */
-	uint8 regOff = byte_121A2[slot];
+	uint8 slot = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];          /* modulator slot index */
+	uint8 regOff = SLOT_TO_REG_OFFSET[slot];
 	uint16 siReg = (uint16)regOff + 0x40;         /* TL register base */
 
 	/* KSL flags from port shadow */
@@ -483,21 +483,21 @@ void ASound::writeVolume() {
 		/* ---- OPL3 patch-attenuation path ---- */
 		/* Modulator TL */
 		int16_t patchAtt = (int16_t)ch->_patchAttenuation;
-		int16_t tl1 = byte_12072[patchAtt];
+		int16_t tl1 = PATCH_ATTEN_TO_TL[patchAtt];
 		var4 = -(tl1 - var8);           /* negate of (table[patchAtt] - combined) */
 		if (var4 < 0)  var4 = 0;
 		if (var4 > 63) var4 = 63;
 		var4 = (0x3F - var4) | kslBits;
 		write((uint8)siReg, (uint8)var4);
 
-		/* Carrier TL (uses complementary lookup at unk_120F1 - patchAtt) */
-		int16_t tl2 = byte_12072[127 - patchAtt];   /* unk_120F1 offset */
+		/* Carrier TL (uses complementary lookup at PATCH_ATTEN_TO_TL - patchAtt) */
+		int16_t tl2 = PATCH_ATTEN_TO_TL[127 - patchAtt];   /* PATCH_ATTEN_TO_TL offset */
 		var6 = -(tl2 - var8);
 		if (var6 < 0)  var6 = 0;
 		if (var6 > 63) var6 = 63;
 		/* carrier register */
-		uint8 cslot = byte_12190[chanNum * 2];
-		uint8 cregOff = byte_121A2[cslot];
+		uint8 cslot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+		uint8 cregOff = SLOT_TO_REG_OFFSET[cslot];
 		uint16 cReg = (uint16)cregOff + 0x40;
 		uint8  cksl = _adlibPorts[cReg] & 0xC0;
 		var6 = (0x3F - var6) | cksl;
@@ -516,8 +516,8 @@ void ASound::writeVolume() {
 		return;   /* FM: only one operator carries volume */
 
 	/* Second operator TL register */
-	uint8 slot2 = byte_12190[chanNum * 2];
-	uint8 roff2 = byte_121A2[slot2];
+	uint8 slot2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 roff2 = SLOT_TO_REG_OFFSET[slot2];
 	uint16 siReg2 = (uint16)roff2 + 0x40;
 	uint8  ksl2 = _adlibPorts[siReg2] & 0xC0;
 
@@ -532,14 +532,14 @@ void ASound::writeVolume() {
 		write((uint8)siReg2, (uint8)val2);
 	} else {
 		int16_t patchAtt2 = (int16_t)ch->_patchAttenuation;
-		int16_t tl1b = byte_12072[patchAtt2];
+		int16_t tl1b = PATCH_ATTEN_TO_TL[patchAtt2];
 		var4 = -(tl1b - var8b);
 		if (var4 < 0)  var4 = 0;
 		if (var4 > 63) var4 = 63;
 		var4 = (0x3F - var4) | ksl2;
 		write((uint8)siReg2, (uint8)var4);
 
-		int16_t tl2b = byte_12072[127 - patchAtt2];
+		int16_t tl2b = PATCH_ATTEN_TO_TL[127 - patchAtt2];
 		var6 = -(tl2b - var8b);
 		if (var6 < 0)  var6 = 0;
 		if (var6 > 63) var6 = 63;
@@ -568,7 +568,7 @@ void ASound::writeFrequency() {
 	}
 
 	/* F-number from table, offset by transpose */
-	int16_t fnumLow = (int16_t)word_12174[semi] + (int16_t)(int8_t)ch->_transpose;
+	int16_t fnumLow = (int16_t)SEMITONE_FREQ_TABLE[semi] + (int16_t)(int8_t)ch->_transpose;
 
 	/* Write F-number low byte to 0xA0+ch */
 	write((uint8)aReg, (uint8)fnumLow);
@@ -635,17 +635,17 @@ void ASound::noteOn() {
 
 void ASound::writeSampleRegs() {
 	AdlibSample *smp = _samplePtr;
-	uint16 base = word_16CF6;
+	uint16 base = _currentOpBase;
 
 	/* --- Register 0xBD: Percussion/tremolo/vibrato depth --- */
 	uint8 bdVal = 0;
-	if (byte_1218C != 1) bdVal |= 0x80;
-	if (byte_1218D != 1) bdVal |= 0x40;
+	if (_rhythmHiHat != 1) bdVal |= 0x80;
+	if (_rhythmCymbal != 1) bdVal |= 0x40;
 	bdVal |= _adlibPorts[0xBD] & 0x3F;
 	write(0xBD, bdVal);
 
 	/* --- Register 0x08: Note-select (CSM/keyboardsplit) --- */
-	uint8 ns = (byte_1218E != 1) ? 0x40 : 0x00;
+	uint8 ns = (_rhythmEnable != 1) ? 0x40 : 0x00;
 	write(0x08, ns);
 
 	/* --- Register 0xC0+voice: Feedback / algorithm --- */
@@ -679,20 +679,20 @@ void ASound::loadSample() {
 	uint8 chanNum = _activeChannelNumber;
 
 	/* --- Silence modulator operator first --- */
-	uint8 mSlot = byte_12191[chanNum * 2];
-	uint8 mReg = byte_121A2[mSlot];
-	word_16CF6 = mReg;
+	uint8 mSlot = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 mReg = SLOT_TO_REG_OFFSET[mSlot];
+	_currentOpBase = mReg;
 
-	write((uint8)(word_16CF6 + 0x80), 0xFF);  /* max release */
-	write((uint8)(word_16CF6 + 0x40), 63);    /* full attenuation */
+	write((uint8)(_currentOpBase + 0x80), 0xFF);  /* max release */
+	write((uint8)(_currentOpBase + 0x40), 63);    /* full attenuation */
 
 	/* --- Silence carrier operator --- */
-	uint8 cSlot = byte_12190[chanNum * 2];
-	uint8 cReg = byte_121A2[cSlot];
-	word_16CF6 = (uint16)cReg;
+	uint8 cSlot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 cReg = SLOT_TO_REG_OFFSET[cSlot];
+	_currentOpBase = (uint16)cReg;
 
-	write((uint8)(word_16CF6 + 0x80), 0xFF);
-	write((uint8)(word_16CF6 + 0x40), 63);
+	write((uint8)(_currentOpBase + 0x80), 0xFF);
+	write((uint8)(_currentOpBase + 0x40), 63);
 
 	/* --- Set active register for C0 writes --- */
 	_activeChannelReg = (uint16)chanNum;
@@ -702,30 +702,30 @@ void ASound::loadSample() {
 	_samplePtr = &_samples[sampleIdx];
 
 	/* Modulator operator regs (operator 1) */
-	uint8 ms1 = byte_12191[chanNum * 2];
-	uint8 mr1 = byte_121A2[ms1];
-	word_16CF6 = (uint16)mr1;
+	uint8 ms1 = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 mr1 = SLOT_TO_REG_OFFSET[ms1];
+	_currentOpBase = (uint16)mr1;
 	writeSampleRegs();
 
 	/* Carrier operator regs (operator 2, offset by 490 bytes = next pair) */
 	_samplePtr = (AdlibSample *)((uint8 *)&_samples[sampleIdx] + 490);
 
-	uint8 ms2 = byte_12190[chanNum * 2];
-	uint8 mr2 = byte_121A2[ms2];
-	word_16CF6 = (uint16)mr2;
+	uint8 ms2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 mr2 = SLOT_TO_REG_OFFSET[ms2];
+	_currentOpBase = (uint16)mr2;
 	writeSampleRegs();
 
 	/* Write carrier TL: noteOffset shifts the level */
 	AdlibSample *smp2 = _samplePtr;
 	uint8 noteOffBits = (uint8)((smp2->_attackRate << 6) | 0x3F);
-	write((uint8)(word_16CF6 + 0x40), noteOffBits);
+	write((uint8)(_currentOpBase + 0x40), noteOffBits);
 
 	/* --- Third operator (if present, offset 0x2C * sampleIndex[5]) --- */
 	int smp3idx = (int)ch->_noteOffset;
 	_samplePtr = &_samples[smp3idx * 2];
 
-	uint8 s3slot = byte_12190[chanNum * 2];
-	uint8 s3reg = byte_121A2[s3slot];
+	uint8 s3slot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 s3reg = SLOT_TO_REG_OFFSET[s3slot];
 	uint16 s3Reg = (uint16)s3reg + 0x40;
 
 	AdlibSample *smp3 = _samplePtr;
@@ -761,7 +761,7 @@ void ASound::update1(int channelIndex) {
 	if (ch->_freqSweepCounter == 0)
 		return;
 
-	byte_12036 = 1;
+	_anySweepActive = 1;
 	ch->_freqAccum += ch->_freqStep;
 	ch->_freqSweepCounter--;
 
@@ -799,7 +799,7 @@ void ASound::updateAllChannels() {
 }
 
 void ASound::findFreeChannel(byte *soundData) {
-	byte_12050 = 1;
+	_findChannelMode = 1;
 
 	for (int chan = 0; chan < 6; ++chan) {
 		if (_channels[chan]->_activeCount == 0) {
@@ -814,7 +814,7 @@ void ASound::findFreeChannel(byte *soundData) {
 
 void ASound::findFreeChannelFull(byte *soundData) {
 	int chan;
-	byte_12050 = 2;
+	_findChannelMode = 2;
 
 	// Check channels 6-8 for empty slots
 	for (chan = 6; chan < ADLIB_CHANNEL_COUNT; ++chan) {
@@ -833,7 +833,7 @@ void ASound::findFreeChannelFull(byte *soundData) {
 	}
 
 	// Fall through to check channels 0-5 pending-stop
-	if (byte_16B63 != 0xFF) {
+	if (_ch5PendingStop != 0xFF) {
 		for (chan = 4; chan >= 0; --chan) {
 			if (_channels[chan]->_pendingStop == 0xff) {
 				_channels[chan]->load(soundData);
@@ -1234,7 +1234,7 @@ vol_advance:
 				var_C = *pSrc;
 				pSrc++;
 				byte *base = pSrc;
-				uint8_t  indIdx = byte_16A30[var_6];
+				uint8_t  indIdx = _scriptVars[var_6];
 				uint8_t  chosen = *(base + (uintptr_t)indIdx);
 				uint8_t  target = *(base + var_C);
 				*(base + target + 1) = chosen;
@@ -1250,7 +1250,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t val = *pSrc;
-				byte_16A30[var_6] = val;
+				_scriptVars[var_6] = val;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1262,7 +1262,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] = byte_16A30[var_C];
+				_scriptVars[var_6] = _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1275,7 +1275,7 @@ vol_advance:
 				pSrc++;
 				var_C = *pSrc;
 				byte *base = pSrc;
-				uint8_t  src = byte_16A30[var_6];
+				uint8_t  src = _scriptVars[var_6];
 				*(base + var_C + 1) = src;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1286,7 +1286,7 @@ vol_advance:
 			{
 				pSrc++;
 				var_6 = *pSrc;
-				byte_16A30[var_6]++;
+				_scriptVars[var_6]++;
 				ch->_pSrc += 2;
 				goto dispatch;
 			}
@@ -1296,7 +1296,7 @@ vol_advance:
 			{
 				pSrc++;
 				var_6 = *pSrc;
-				byte_16A30[var_6]--;
+				_scriptVars[var_6]--;
 				ch->_pSrc += 2;
 				goto dispatch;
 			}
@@ -1308,7 +1308,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] += imm;
+				_scriptVars[var_6] += imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1320,7 +1320,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] += byte_16A30[var_C];
+				_scriptVars[var_6] += _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1332,7 +1332,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] -= imm;
+				_scriptVars[var_6] -= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1344,7 +1344,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] -= byte_16A30[var_C];
+				_scriptVars[var_6] -= _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1356,7 +1356,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] *= byte_16A30[var_C];
+				_scriptVars[var_6] *= _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1368,7 +1368,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] *= imm;
+				_scriptVars[var_6] *= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1380,7 +1380,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] = (uint8_t)((int8_t)byte_16A30[var_6] / (int8_t)var_C);
+				_scriptVars[var_6] = (uint8_t)((int8_t)_scriptVars[var_6] / (int8_t)var_C);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1392,7 +1392,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] = (uint8_t)((uint8_t)byte_16A30[var_6] / (uint8_t)byte_16A30[var_C]);
+				_scriptVars[var_6] = (uint8_t)((uint8_t)_scriptVars[var_6] / (uint8_t)_scriptVars[var_C]);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1404,7 +1404,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] = (uint8_t)((int8_t)byte_16A30[var_6] % (int8_t)var_C);
+				_scriptVars[var_6] = (uint8_t)((int8_t)_scriptVars[var_6] % (int8_t)var_C);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1416,7 +1416,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] = (uint8_t)((uint8_t)byte_16A30[var_6] % (uint8_t)byte_16A30[var_C]);
+				_scriptVars[var_6] = (uint8_t)((uint8_t)_scriptVars[var_6] % (uint8_t)_scriptVars[var_C]);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1428,7 +1428,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] &= imm;
+				_scriptVars[var_6] &= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1440,7 +1440,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] &= byte_16A30[var_C];
+				_scriptVars[var_6] &= _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1452,7 +1452,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] |= imm;
+				_scriptVars[var_6] |= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1464,7 +1464,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] |= byte_16A30[var_C];
+				_scriptVars[var_6] |= _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1476,7 +1476,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				uint8_t imm = *pSrc;
-				byte_16A30[var_6] ^= imm;
+				_scriptVars[var_6] ^= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1488,7 +1488,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				byte_16A30[var_6] ^= byte_16A30[var_C];
+				_scriptVars[var_6] ^= _scriptVars[var_C];
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1505,7 +1505,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				uint8_t v = byte_16A30[var_6];
+				uint8_t v = _scriptVars[var_6];
 				if ((uint16_t)var_C == (uint16_t)v) goto branch_taken;
 				goto branch_skip5;
 			}
@@ -1515,7 +1515,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C != (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				if ((uint16_t)var_C != (uint16_t)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1524,7 +1524,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C > (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				if ((uint16_t)var_C > (uint16_t)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1533,7 +1533,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C >= (uint16_t)byte_16A30[var_6]) goto branch_taken;
+				if ((uint16_t)var_C >= (uint16_t)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1542,7 +1542,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] == byte_16A30[var_C]) goto branch_taken;
+				if (_scriptVars[var_6] == _scriptVars[var_C]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1551,7 +1551,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] != byte_16A30[var_C]) goto branch_taken;
+				if (_scriptVars[var_6] != _scriptVars[var_C]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1560,7 +1560,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] > byte_16A30[var_C]) goto branch_taken;
+				if (_scriptVars[var_6] > _scriptVars[var_C]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1569,7 +1569,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] < byte_16A30[var_C]) goto branch_taken;
+				if (_scriptVars[var_6] < _scriptVars[var_C]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1578,7 +1578,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C == (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				if ((uint16_t)var_C == (uint16_t)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1587,7 +1587,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C != (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				if ((uint16_t)var_C != (uint16_t)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1596,7 +1596,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C > (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				if ((uint16_t)var_C > (uint16_t)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1605,7 +1605,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C < (uint16_t)byte_16A30[var_6]) goto branch_taken2;
+				if ((uint16_t)var_C < (uint16_t)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1614,7 +1614,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] == byte_16A30[var_C]) goto branch_taken2;
+				if (_scriptVars[var_6] == _scriptVars[var_C]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1623,7 +1623,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] != byte_16A30[var_C]) goto branch_taken2;
+				if (_scriptVars[var_6] != _scriptVars[var_C]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1632,7 +1632,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] > byte_16A30[var_C]) goto branch_taken2;
+				if (_scriptVars[var_6] > _scriptVars[var_C]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1641,7 +1641,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if (byte_16A30[var_6] < byte_16A30[var_C]) goto branch_taken2;
+				if (_scriptVars[var_6] < _scriptVars[var_C]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1692,11 +1692,11 @@ branch_skip5:
 				goto dispatch;
 			}
 
-			/* ---- opcode -63 (0xC1): set word_12070 ---- */
+			/* ---- opcode -63 (0xC1): set _tempoScale ---- */
 			case 1:
 			{
 				pSrc++;
-				word_12070 = *pSrc;
+				_tempoScale = *pSrc;
 				ch->_pSrc += 2;
 				goto dispatch;
 			}
@@ -1705,11 +1705,11 @@ branch_skip5:
 			case 0:
 			{
 				pSrc++;
-				word_12066 = *pSrc;
+				_tempoReload = *pSrc;
 				ch->_pSrc += 2;
 				if (_frameNumber2 != 0)
 					goto dispatch;
-				word_1206E = word_12066;
+				_tempoCurrent = _tempoReload;
 				goto dispatch;
 			}
 
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 9eb337d38e1..225e4d222d5 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -127,13 +127,13 @@ class ASound : public SoundDriver {
 private:
 	Common::Mutex _driverMutex;
 	Common::Queue< Common::Pair<byte, byte> > _queue;
-	uint16 word_12C06 = 0;  // period counter
-	uint16 word_12C08 = 0;  // period reload
-	uint16 word_12C0A = 0;  // callback function pointer (not used in C)
+	uint16 _callbackCounter = 0;  // period counter
+	uint16 _callbackPeriod = 0;  // period reload
+	uint16 _callbackFnPtr = 0;  // callback function pointer (not used in C)
 	AdlibChannel *_activeChannelPtr = NULL;
 	uint8 _activeChannelNumber = 0;
 	uint16 _activeChannelReg = 0;
-	uint16 word_16CF6 = 0;				// Current operator base
+	uint16 _currentOpBase = 0;				// Current operator base
 	AdlibSample *_samplePtr = NULL;
 	byte *pSrc = nullptr;				// current read pointer
 	uint8 _adlib_v5660_2 = 0;			// OPL version flag
@@ -141,26 +141,26 @@ private:
 	uint16 _resultFlag = 0;
 	uint16 _randomSeed = 0x4D2;
 	uint8  _isDisabled = 0;
-	uint8  byte_12050 = 0;				// findFreeChannel mode
-	uint8  byte_16B5C = 0;				// channel5 savedFreqSweep shadow
-	uint8  byte_16B5D = 0;				// channel5 savedFreqSweep shadow 2
-	uint8  byte_16B63 = 0;				// channel5 pendingStop shadow
-	/* Percussion flags (byte_1218C/D/E) */
-	uint8 byte_1218C;					// rhythm mode: hi-hat
-	uint8 byte_1218D;					// rhythm mode: cymbal
-	uint8 byte_1218E;					// rhythm mode: enable
-
-	uint8 byte_12036 = 0;				// any-sweep-active flag
+	uint8  _findChannelMode = 0;				// findFreeChannel mode
+	uint8  _ch5SweepLive = 0;				// channel5 savedFreqSweep shadow
+	uint8  _ch5SweepSaved = 0;				// channel5 savedFreqSweep shadow 2
+	uint8  _ch5PendingStop = 0;				// channel5 pendingStop shadow
+	/* Percussion flags (_rhythmHiHat/D/E) */
+	uint8 _rhythmHiHat;					// rhythm mode: hi-hat
+	uint8 _rhythmCymbal;					// rhythm mode: cymbal
+	uint8 _rhythmEnable;					// rhythm mode: enable
+
+	uint8 _anySweepActive = 0;				// any-sweep-active flag
 	int _frameNumber2 = 0;
-	uint8 byte_16A30[32];				// General-purpose script registers
-	uint16 word_12052 = 1;
-	uint16 word_12062 = 0;
-	uint16 word_12066 = 0;
-	uint16 word_12068 = 0;
-	uint16 word_1206A = 0;
-	uint16 word_1206C = 0xA0;
-	uint16 word_1206E = 0x28;
-	uint16 word_12070 = 0x0A;
+	uint8 _scriptVars[32];				// General-purpose script registers
+	uint16 _tickEnabled = 1;
+	uint16 _tickCounter = 0;
+	uint16 _tempoReload = 0;
+	uint16 _tempoTarget = 0;
+	uint16 _tempoShift = 0;
+	uint16 _tempoBase = 0xA0;
+	uint16 _tempoCurrent = 0x28;
+	uint16 _tempoScale = 0x0A;
 
 	/**
 	 * Timer function for OPL
@@ -189,7 +189,7 @@ private:
 	 *
 	 * Two paths depending on _adlib_v5660_2:
 	 *   < 0x18  -> simple linear mapping of (volume + velocity) -> TL
-	 *   >= 0x18 -> patch-attenuation-aware mapping using byte_12072 table
+	 *   >= 0x18 -> patch-attenuation-aware mapping using _patchAttenToTL table
 	 */
 	void writeVolume();
 
@@ -220,7 +220,7 @@ private:
 
 	/**
 	 * Writes a full set of OPL operator registers for the sample pointed to
-	 * by the global asound_samplePtr, using word_16CF6 as the operator base.
+	 * by the global asound_samplePtr, using _currentOpBase as the operator base.
 	 */
 	void writeSampleRegs();
 
@@ -337,7 +337,7 @@ protected:
 	 *
 	 * Searches channels 0-5 for an empty slot, falling through to
 	 * asound_findFreeChannelFull (channels 6-8) if none found.
-	 * byte_12050 == 1 means "only search 0-5".
+	 * _findChannelMode == 1 means "only search 0-5".
 	  */
 	void findFreeChannel(byte *soundData);
 


Commit: bc5484c8d53744d93523193c7d12c278b285ef2d
    https://github.com/scummvm/scummvm/commit/bc5484c8d53744d93523193c7d12c278b285ef2d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:44+10:00

Commit Message:
MADS: PHANTOM: Fixes to new ASound class

Changed paths:
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index bd007624b69..8b96b5b6b93 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -179,6 +179,7 @@ void AdlibChannel::processChannelFade() {
 ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 		int dataOffset, int dataSize) :
 		SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
+	AdlibChannel::_isDisabled = false;
 	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
 }
 
@@ -409,6 +410,7 @@ void ASound::flush() {
 
 	while (!_queue.empty()) {
 		auto v = _queue.pop();
+		warning("%.2x %.2x", v.first, v.second);
 		_opl->writeReg(v.first, v.second);
 	}
 }
@@ -441,7 +443,6 @@ void ASound::signalSoundPlaying() {
 void ASound::clearCallback() {
 	_callbackCounter = 0;
 	_callbackPeriod = 0;
-	_callbackFnPtr = 0;
 }
 
 /* =========================================================================
@@ -752,6 +753,20 @@ void ASound::update() {
 	if (!_isDisabled) {
 		(void)getRandomNumber();
 		++_frameNumber2;
+
+		pollAllChannels();
+		updateAllChannels();
+		_anySweepActive = false;
+
+		for (int chan = 0; chan < ADLIB_CHANNEL_COUNT; ++chan)
+			update1(chan);
+
+		if (!_anySweepActive) {
+			if (_resultFlag != -1) {
+				_resultFlag = -1;
+				_pollResult = -1;
+			}
+		}
 	}
 }
 
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 225e4d222d5..cd8188f0464 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -34,40 +34,40 @@ namespace MADSV2 {
 
 struct AdlibChannel {
 	static bool _isDisabled;
-	uint8  _activeCount;
-	uint8  _pitchBend;
-	uint8  _volumeFadeStep;
-	uint8  _vibratoDepth;
-	uint8  _note;
-	uint8  _sampleIndex;
-	uint8  _volume;
-	uint8  _noteOffset;
-	uint8  _keyOnDelay;
-	uint8  _fadePeriodCounter;
-	uint8  _fadePeriodReload;
-	uint8  _vibPeriodCounter;
-	uint8  _vibPeriodReload;
-	uint8  _patchAttenuation;
-	byte *_loopStartPtr;
-	byte *_pSrc;
-	byte *_innerLoopPtr;
-	byte *_outerLoopPtr;
-	byte *_soundData;
-	byte *_branchTargetPtr;
-	uint16 _innerLoopCount;
-	uint16 _outerLoopCount;
-	uint16 _noiseFreqMask;
-	uint16 _freqAccum;
-	uint16 _freqStep;
-	uint8  _freqSweepCounter;
-	uint8  _savedFreqSweep;
-	uint8  _transpose;
-	uint8  _velocity;
-	uint8  _opVolBytes;
-	uint8  _opVolBytes2;
-	uint8  _octaveTranspose;
-	uint8  _pendingStop;
-	uint8  _durationOverride;
+	uint8  _activeCount = 0;
+	uint8  _pitchBend = 0;
+	uint8  _volumeFadeStep = 0;
+	uint8  _vibratoDepth = 0;
+	uint8  _note = 0;
+	uint8  _sampleIndex = 0;
+	uint8  _volume = 0;
+	uint8  _noteOffset = 0;
+	uint8  _keyOnDelay = 0;
+	uint8  _fadePeriodCounter = 0;
+	uint8  _fadePeriodReload = 0;
+	uint8  _vibPeriodCounter = 0;
+	uint8  _vibPeriodReload = 0;
+	uint8  _patchAttenuation = 0;
+	byte *_loopStartPtr = nullptr;
+	byte *_pSrc = nullptr;
+	byte *_innerLoopPtr = nullptr;
+	byte *_outerLoopPtr = nullptr;
+	byte *_soundData = nullptr;
+	byte *_branchTargetPtr = nullptr;
+	uint16 _innerLoopCount = 0;
+	uint16 _outerLoopCount = 0;
+	uint16 _noiseFreqMask = 0;
+	uint16 _freqAccum = 0;
+	uint16 _freqStep = 0;
+	uint8  _freqSweepCounter = 0;
+	uint8  _savedFreqSweep = 0;
+	uint8  _transpose = 0;
+	uint8  _velocity = 0;
+	uint8  _opVolBytes = 0;
+	uint8  _opVolBytes2 = 0;
+	uint8  _octaveTranspose = 0;
+	uint8  _pendingStop = 0;
+	uint8  _durationOverride = 0;
 
 	/**
 	 * Zeroes the "live" fields of one channel without touching loop/sound-data
@@ -102,53 +102,52 @@ struct AdlibChannel {
 };
 
 struct AdlibSample {
-	uint8  _attackRate;
-	uint8  _decayRate;
-	uint8  _sustainLevel;
-	uint8  _releaseRate;
-	uint8  _egTyp;
-	uint8  _ksr;
-	uint8  _totalLevel;
-	uint8  _scalingLevel;
-	uint8  _waveformSelect;
-	uint8  _freqMultiple;
-	uint8  _feedback;
-	uint8  _ampMod;
-	uint8  _vib;
-	uint8  _alg;
-	uint8  _freqSweepInit;
-	uint8  _reserved;
-	uint16 _freqMask;
-	uint16 _freqBase;
-	uint16 _outerLoopPtr;
+	uint8  _attackRate = 0;
+	uint8  _decayRate = 0;
+	uint8  _sustainLevel = 0;
+	uint8  _releaseRate = 0;
+	uint8  _egTyp = 0;
+	uint8  _ksr = 0;
+	uint8  _totalLevel = 0;
+	uint8  _scalingLevel = 0;
+	uint8  _waveformSelect = 0;
+	uint8  _freqMultiple = 0;
+	uint8  _feedback = 0;
+	uint8  _ampMod = 0;
+	uint8  _vib = 0;
+	uint8  _alg = 0;
+	uint8  _freqSweepInit = 0;
+	uint8  _reserved = 0;
+	uint16 _freqMask = 0;
+	uint16 _freqBase = 0;
+	uint16 _outerLoopPtr = 0;
 };
 
 class ASound : public SoundDriver {
 private:
 	Common::Mutex _driverMutex;
 	Common::Queue< Common::Pair<byte, byte> > _queue;
-	uint16 _callbackCounter = 0;  // period counter
-	uint16 _callbackPeriod = 0;  // period reload
-	uint16 _callbackFnPtr = 0;  // callback function pointer (not used in C)
+	uint16 _callbackCounter = 0;		// Period counter
+	uint16 _callbackPeriod = 0;			// Period reload
 	AdlibChannel *_activeChannelPtr = NULL;
 	uint8 _activeChannelNumber = 0;
 	uint16 _activeChannelReg = 0;
-	uint16 _currentOpBase = 0;				// Current operator base
+	uint16 _currentOpBase = 0;			// Current operator base
 	AdlibSample *_samplePtr = NULL;
-	byte *pSrc = nullptr;				// current read pointer
+	byte *pSrc = nullptr;				// Current read pointer
 	uint8 _adlib_v5660_2 = 0;			// OPL version flag
-	uint16 _pollResult = 0;
-	uint16 _resultFlag = 0;
+	int16 _pollResult = 0;
+	int16 _resultFlag = 0;
 	uint16 _randomSeed = 0x4D2;
 	uint8  _isDisabled = 0;
-	uint8  _findChannelMode = 0;				// findFreeChannel mode
-	uint8  _ch5SweepLive = 0;				// channel5 savedFreqSweep shadow
-	uint8  _ch5SweepSaved = 0;				// channel5 savedFreqSweep shadow 2
-	uint8  _ch5PendingStop = 0;				// channel5 pendingStop shadow
+	uint8  _findChannelMode = 0;		// findFreeChannel mode
+	uint8  _ch5SweepLive = 0;			// Channel5 savedFreqSweep shadow
+	uint8  _ch5SweepSaved = 0;			// Channel5 savedFreqSweep shadow 2
+	uint8  _ch5PendingStop = 0;			// Channel5 pendingStop shadow
 	/* Percussion flags (_rhythmHiHat/D/E) */
-	uint8 _rhythmHiHat;					// rhythm mode: hi-hat
-	uint8 _rhythmCymbal;					// rhythm mode: cymbal
-	uint8 _rhythmEnable;					// rhythm mode: enable
+	uint8 _rhythmHiHat = 0;				// Rhythm mode: hi-hat
+	uint8 _rhythmCymbal = 0;			// Rhythm mode: cymbal
+	uint8 _rhythmEnable = 0;			// Rhythm mode: enable
 
 	uint8 _anySweepActive = 0;				// any-sweep-active flag
 	int _frameNumber2 = 0;


Commit: 52305a16a2976ce1b809bf98098845ee374337d0
    https://github.com/scummvm/scummvm/commit/52305a16a2976ce1b809bf98098845ee374337d0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:45+10:00

Commit Message:
MADS: PHANTOM: Change ASound types to use ScummVM types

Changed paths:
    engines/mads/madsv2/core/asound.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 8b96b5b6b93..5551554e9ff 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -457,13 +457,13 @@ void ASound::clearCallback() {
 
 void ASound::writeVolume() {
 	AdlibChannel *ch = _activeChannelPtr;
-	int16_t var4, var6 = 0;
+	int16 var4, var6 = 0;
 
 	/* Step 1: map volume through VOL_VEL_TO_ATTEN_STEP */
-	int16_t volStep = (int16_t)VOL_VEL_TO_ATTEN_STEP[(uint8)ch->_volume];
+	int16 volStep = (int16)VOL_VEL_TO_ATTEN_STEP[(uint8)ch->_volume];
 	/* Step 2: map velocity through VOL_VEL_TO_ATTEN_STEP */
-	int16_t velStep = (int16_t)VOL_VEL_TO_ATTEN_STEP[(int8_t)ch->_velocity];
-	int16_t var8 = volStep + velStep - 1;   /* combined attenuation step */
+	int16 velStep = (int16)VOL_VEL_TO_ATTEN_STEP[(int8)ch->_velocity];
+	int16 var8 = volStep + velStep - 1;   /* combined attenuation step */
 
 	/* Determine carrier operator register for this voice */
 	uint8 chanNum = _activeChannelNumber;
@@ -483,8 +483,8 @@ void ASound::writeVolume() {
 	} else {
 		/* ---- OPL3 patch-attenuation path ---- */
 		/* Modulator TL */
-		int16_t patchAtt = (int16_t)ch->_patchAttenuation;
-		int16_t tl1 = PATCH_ATTEN_TO_TL[patchAtt];
+		int16 patchAtt = (int16)ch->_patchAttenuation;
+		int16 tl1 = PATCH_ATTEN_TO_TL[patchAtt];
 		var4 = -(tl1 - var8);           /* negate of (table[patchAtt] - combined) */
 		if (var4 < 0)  var4 = 0;
 		if (var4 > 63) var4 = 63;
@@ -492,7 +492,7 @@ void ASound::writeVolume() {
 		write((uint8)siReg, (uint8)var4);
 
 		/* Carrier TL (uses complementary lookup at PATCH_ATTEN_TO_TL - patchAtt) */
-		int16_t tl2 = PATCH_ATTEN_TO_TL[127 - patchAtt];   /* PATCH_ATTEN_TO_TL offset */
+		int16 tl2 = PATCH_ATTEN_TO_TL[127 - patchAtt];   /* PATCH_ATTEN_TO_TL offset */
 		var6 = -(tl2 - var8);
 		if (var6 < 0)  var6 = 0;
 		if (var6 > 63) var6 = 63;
@@ -500,7 +500,7 @@ void ASound::writeVolume() {
 		uint8 cslot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
 		uint8 cregOff = SLOT_TO_REG_OFFSET[cslot];
 		uint16 cReg = (uint16)cregOff + 0x40;
-		uint8  cksl = _adlibPorts[cReg] & 0xC0;
+		uint8 cksl = _adlibPorts[cReg] & 0xC0;
 		var6 = (0x3F - var6) | cksl;
 		write((uint8)(siReg + 2), (uint8)var4);   /* mod in OPL3 second pair */
 		write((uint8)cReg, (uint8)var6);
@@ -520,27 +520,27 @@ void ASound::writeVolume() {
 	uint8 slot2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
 	uint8 roff2 = SLOT_TO_REG_OFFSET[slot2];
 	uint16 siReg2 = (uint16)roff2 + 0x40;
-	uint8  ksl2 = _adlibPorts[siReg2] & 0xC0;
+	uint8 ksl2 = _adlibPorts[siReg2] & 0xC0;
 
-	int16_t tl_s = (int16_t)smp->_totalLevel - 63;
+	int16 tl_s = (int16)smp->_totalLevel - 63;
 	tl_s = -tl_s;   /* neg */
-	int16_t var8b = (int16_t)var8 - tl_s;   /* subtract from combined */
+	int16 var8b = (int16)var8 - tl_s;   /* subtract from combined */
 
 	if (_adlib_v5660_2 < 0x18) {
 		if (var8b < 0)  var8b = 0;
 		if (var8b > 63) var8b = 63;
-		int16_t val2 = (0x3F - var8b) | ksl2;
+		int16 val2 = (0x3F - var8b) | ksl2;
 		write((uint8)siReg2, (uint8)val2);
 	} else {
-		int16_t patchAtt2 = (int16_t)ch->_patchAttenuation;
-		int16_t tl1b = PATCH_ATTEN_TO_TL[patchAtt2];
+		int16 patchAtt2 = (int16)ch->_patchAttenuation;
+		int16 tl1b = PATCH_ATTEN_TO_TL[patchAtt2];
 		var4 = -(tl1b - var8b);
 		if (var4 < 0)  var4 = 0;
 		if (var4 > 63) var4 = 63;
 		var4 = (0x3F - var4) | ksl2;
 		write((uint8)siReg2, (uint8)var4);
 
-		int16_t tl2b = PATCH_ATTEN_TO_TL[127 - patchAtt2];
+		int16 tl2b = PATCH_ATTEN_TO_TL[127 - patchAtt2];
 		var6 = -(tl2b - var8b);
 		if (var6 < 0)  var6 = 0;
 		if (var6 > 63) var6 = 63;
@@ -552,7 +552,7 @@ void ASound::writeVolume() {
 
 void ASound::writeFrequency() {
 	AdlibChannel *ch = _activeChannelPtr;
-	uint8  chanNum = _activeChannelNumber;
+	uint8 chanNum = _activeChannelNumber;
 	uint16 aReg = (uint16)chanNum + 0xA0;   /* freq-low register */
 	uint16 bReg = aReg + 0x10;                /* freq-high / keyon register */
 
@@ -569,7 +569,7 @@ void ASound::writeFrequency() {
 	}
 
 	/* F-number from table, offset by transpose */
-	int16_t fnumLow = (int16_t)SEMITONE_FREQ_TABLE[semi] + (int16_t)(int8_t)ch->_transpose;
+	int16 fnumLow = (int16)SEMITONE_FREQ_TABLE[semi] + (int16)(int8)ch->_transpose;
 
 	/* Write F-number low byte to 0xA0+ch */
 	write((uint8)aReg, (uint8)fnumLow);
@@ -583,7 +583,7 @@ void ASound::writeFrequency() {
 
 void ASound::writePitchBend() {
 	AdlibChannel *ch = _activeChannelPtr;
-	uint8  chanNum = _activeChannelNumber;
+	uint8 chanNum = _activeChannelNumber;
 	uint16 aReg = (uint16)chanNum + 0xA0;
 	uint16 bReg = aReg + 0x10;
 
@@ -592,7 +592,7 @@ void ASound::writePitchBend() {
 		| ((uint16)(_adlibPorts[bReg] & 0x1F) << 8);
 
 	/* Add signed pitch bend */
-	int16_t bent = (int16_t)fnum + (int16_t)(int8_t)ch->_pitchBend;
+	int16 bent = (int16)fnum + (int16)(int8)ch->_pitchBend;
 
 	/* Write low byte */
 	write((uint8)aReg, (uint8)bent);
@@ -604,9 +604,9 @@ void ASound::writePitchBend() {
 }
 
 void ASound::updateOctave() {
-	uint8  chanNum = _activeChannelNumber;
+	uint8 chanNum = _activeChannelNumber;
 	uint16 bReg = (uint16)chanNum + 0xB0;
-	uint8  val = _adlibPorts[bReg] & 0xDF;   /* clear bit 5 */
+	uint8 val = _adlibPorts[bReg] & 0xDF;   /* clear bit 5 */
 	write((uint8)bReg, val);
 }
 
@@ -628,9 +628,9 @@ void ASound::noteOn() {
 	/* No sweep: write frequency and key on */
 	writeFrequency();
 
-	uint8  chanNum = _activeChannelNumber;
+	uint8 chanNum = _activeChannelNumber;
 	uint16 bReg = (uint16)chanNum + 0xB0;
-	uint8  bVal = _adlibPorts[bReg] | 0x20;   /* set key-on bit */
+	uint8 bVal = _adlibPorts[bReg] | 0x20;   /* set key-on bit */
 	write((uint8)bReg, bVal);
 }
 
@@ -736,7 +736,7 @@ void ASound::loadSample() {
 		write((uint8)s3Reg, scl3);
 	} else {
 		/* Additive: write actual total level */
-		int16_t tl3 = (int16_t)smp3->_totalLevel - 63;
+		int16 tl3 = (int16)smp3->_totalLevel - 63;
 		tl3 = -tl3;
 		uint8 scl3 = (uint8)((smp3->_scalingLevel << 6) | (tl3 & 0x3F));
 		write((uint8)s3Reg, scl3);
@@ -870,11 +870,11 @@ void ASound::pollAllChannels() {
 
 void ASound::pollActiveChannel() {
 	AdlibChannel *ch = _activeChannelPtr;
-	uint8_t var_8 = 0;		// volume-dirty flag
+	uint8 var_8 = 0;		// volume-dirty flag
 	uint16 var_6;			// temp word
-	uint16_t var_4 = 0;		// temp word 2
-	uint16_t var_A = 0;		// temp word 3
-	uint16_t var_C = 0;		// temp word 4
+	uint16 var_4 = 0;		// temp word 2
+	uint16 var_A = 0;		// temp word 3
+	uint16 var_C = 0;		// temp word 4
 
 	/* Channel inactive - nothing to do */
 	if (ch->_activeCount == 0) goto done;
@@ -896,7 +896,7 @@ dispatch:
 	{
 		/* Load pSrc from channel and read the first byte */
 		pSrc = ch->_pSrc;
-		uint8_t b = *pSrc;
+		uint8 b = *pSrc;
 
 		if (!(b & 0x80)) {
 			/*
@@ -920,10 +920,10 @@ dispatch:
 		if (b > 0xBD) {
 			/* Decode the command opcode: ax = b - (-66) = b + 66          */
 			/* Switch range is 0..65 (opcodes -66 .. -1 mapped to 0..65)   */
-			int16_t ax = (int16_t)(int8_t)b;   /* sign-extend */
+			int16 ax = (int16)(int8)b;   /* sign-extend */
 			var_8 = 0;
-			ax = (int16_t)(ax - (-66));
-			if ((uint16_t)ax > 65) goto dispatch;  /* unknown - skip */
+			ax = (int16)(ax - (-66));
+			if ((uint16)ax > 65) goto dispatch;  /* unknown - skip */
 
 			switch (ax) {
 
@@ -932,14 +932,14 @@ dispatch:
 			{
 				if (ch->_innerLoopCount == 0) {
 					pSrc++;
-					uint8_t cnt = *pSrc;
+					uint8 cnt = *pSrc;
 					if (cnt == 0) {
 						ch->_pSrc += 2;
 						ch->_innerLoopPtr = ch->_pSrc;
 						ch->_innerLoopCount = 0;
 						goto dispatch;
 					}
-					ch->_innerLoopCount = (uint16_t)cnt;
+					ch->_innerLoopCount = (uint16)cnt;
 				}
 				ch->_innerLoopCount--;
 				if (ch->_innerLoopCount == 0) {
@@ -956,7 +956,7 @@ dispatch:
 			{
 				if (ch->_outerLoopCount == 0) {
 					pSrc++;
-					uint8_t cnt = *pSrc;
+					uint8 cnt = *pSrc;
 					if (cnt == 0) {
 						ch->_pSrc += 2;
 						ch->_outerLoopPtr = ch->_pSrc;
@@ -965,7 +965,7 @@ dispatch:
 						ch->_outerLoopCount = 0;
 						goto dispatch;
 					}
-					ch->_outerLoopCount = (uint16_t)cnt;
+					ch->_outerLoopCount = (uint16)cnt;
 				}
 				ch->_outerLoopCount--;
 				if (ch->_outerLoopCount == 0) {
@@ -1086,14 +1086,14 @@ dispatch:
 			case 52:
 			{
 				pSrc++;
-				uint8_t vol = *pSrc;
-				var_6 = (uint16_t)vol;
+				uint8 vol = *pSrc;
+				var_6 = (uint16)vol;
 				if (ch->_pendingStop != 0) {
 					/* Clamp: don't raise volume above current when fading out */
-					if ((uint16_t)ch->_volume <= var_6)
+					if ((uint16)ch->_volume <= var_6)
 						goto set_vol_patch;
 				}
-				ch->_volume = (uint8_t)var_6;
+				ch->_volume = (uint8)var_6;
 				goto vol_advance;
 set_vol_patch:
 				ch->_volume = vol;
@@ -1132,10 +1132,10 @@ vol_advance:
 			case 49:
 			{
 				pSrc++;
-				uint8_t vel = *pSrc;
-				var_6 = (uint16_t)vel;
+				uint8 vel = *pSrc;
+				var_6 = (uint16)vel;
 				if (ch->_pendingStop != 0) {
-					if ((uint16_t)ch->_velocity > var_6)
+					if ((uint16)ch->_velocity > var_6)
 						ch->_velocity = vel;
 				} else {
 					ch->_velocity = vel;
@@ -1181,7 +1181,7 @@ vol_advance:
 			{
 				pSrc++;
 				var_C = *pSrc;
-				uint16_t skip = var_C + 3;
+				uint16 skip = var_C + 3;
 				ch->_pSrc += skip;
 				goto dispatch;
 			}
@@ -1190,22 +1190,22 @@ vol_advance:
 			case 44:
 			{
 				pSrc++;
-				uint8_t tblSize = *pSrc;
-				var_C = (uint16_t)tblSize;
+				uint8 tblSize = *pSrc;
+				var_C = (uint16)tblSize;
 				pSrc++;
 				byte *base = pSrc;
 
 				(void)getRandomNumber();
-				uint16_t rnd = _randomSeed & 0x7FFF;
-				uint16_t idx = (uint16_t)((int16_t)rnd % (int16_t)var_C);
+				uint16 rnd = _randomSeed & 0x7FFF;
+				uint16 idx = (uint16)((int16)rnd % (int16)var_C);
 				var_6 = idx;
 
-				uint8_t  chosen = *(base + idx);
-				uint8_t  target = *(base + var_C);
+				uint8 chosen = *(base + idx);
+				uint8 target = *(base + var_C);
 				/* Write chosen into table[target+1] */
 				*(base + target + 1) = chosen;
 
-				uint16_t advance = var_C + 3;
+				uint16 advance = var_C + 3;
 				ch->_pSrc += advance;
 				goto dispatch;
 			}
@@ -1221,12 +1221,12 @@ vol_advance:
 				var_C = var_4 - var_A + 1;
 
 				(void)getRandomNumber();
-				uint16_t rnd = _randomSeed & 0x7FFF;
-				var_6 = (uint16_t)((int16_t)rnd % (int16_t)var_C);
+				uint16 rnd = _randomSeed & 0x7FFF;
+				var_6 = (uint16)((int16)rnd % (int16)var_C);
 
 				byte *base = pSrc;
-				uint8_t  targetSlot = *base;
-				uint8_t  result = var_6 + var_A;
+				uint8 targetSlot = *base;
+				uint8 result = var_6 + var_A;
 				*(base + targetSlot + 1) = result;
 
 				ch->_pSrc += 4;
@@ -1249,11 +1249,11 @@ vol_advance:
 				var_C = *pSrc;
 				pSrc++;
 				byte *base = pSrc;
-				uint8_t  indIdx = _scriptVars[var_6];
-				uint8_t  chosen = *(base + (uintptr_t)indIdx);
-				uint8_t  target = *(base + var_C);
+				uint8 indIdx = _scriptVars[var_6];
+				uint8 chosen = *(base + (uintptr_t)indIdx);
+				uint8 target = *(base + var_C);
 				*(base + target + 1) = chosen;
-				uint16_t advance = (uint16_t)(var_C + 4);
+				uint16 advance = (uint16)(var_C + 4);
 				ch->_pSrc += advance;
 				goto dispatch;
 			}
@@ -1264,7 +1264,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t val = *pSrc;
+				uint8 val = *pSrc;
 				_scriptVars[var_6] = val;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1290,7 +1290,7 @@ vol_advance:
 				pSrc++;
 				var_C = *pSrc;
 				byte *base = pSrc;
-				uint8_t  src = _scriptVars[var_6];
+				uint8 src = _scriptVars[var_6];
 				*(base + var_C + 1) = src;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1322,7 +1322,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] += imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1346,7 +1346,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] -= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1382,7 +1382,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] *= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1395,7 +1395,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				_scriptVars[var_6] = (uint8_t)((int8_t)_scriptVars[var_6] / (int8_t)var_C);
+				_scriptVars[var_6] = (uint8)((int8)_scriptVars[var_6] / (int8)var_C);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1407,7 +1407,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				_scriptVars[var_6] = (uint8_t)((uint8_t)_scriptVars[var_6] / (uint8_t)_scriptVars[var_C]);
+				_scriptVars[var_6] = (uint8)((uint8)_scriptVars[var_6] / (uint8)_scriptVars[var_C]);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1419,7 +1419,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				_scriptVars[var_6] = (uint8_t)((int8_t)_scriptVars[var_6] % (int8_t)var_C);
+				_scriptVars[var_6] = (uint8)((int8)_scriptVars[var_6] % (int8)var_C);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1431,7 +1431,7 @@ vol_advance:
 				var_6 = *pSrc;
 				pSrc++;
 				var_C = *pSrc;
-				_scriptVars[var_6] = (uint8_t)((uint8_t)_scriptVars[var_6] % (uint8_t)_scriptVars[var_C]);
+				_scriptVars[var_6] = (uint8)((uint8)_scriptVars[var_6] % (uint8)_scriptVars[var_C]);
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
@@ -1442,7 +1442,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] &= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1466,7 +1466,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] |= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1490,7 +1490,7 @@ vol_advance:
 				pSrc++;
 				var_6 = *pSrc;
 				pSrc++;
-				uint8_t imm = *pSrc;
+				uint8 imm = *pSrc;
 				_scriptVars[var_6] ^= imm;
 				ch->_pSrc += 3;
 				goto dispatch;
@@ -1520,8 +1520,8 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				uint8_t v = _scriptVars[var_6];
-				if ((uint16_t)var_C == (uint16_t)v) goto branch_taken;
+				uint8 v = _scriptVars[var_6];
+				if ((uint16)var_C == (uint16)v) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1530,7 +1530,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C != (uint16_t)_scriptVars[var_6]) goto branch_taken;
+				if ((uint16)var_C != (uint16)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1539,7 +1539,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C > (uint16_t)_scriptVars[var_6]) goto branch_taken;
+				if ((uint16)var_C > (uint16)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1548,7 +1548,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C >= (uint16_t)_scriptVars[var_6]) goto branch_taken;
+				if ((uint16)var_C >= (uint16)_scriptVars[var_6]) goto branch_taken;
 				goto branch_skip5;
 			}
 
@@ -1593,7 +1593,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C == (uint16_t)_scriptVars[var_6]) goto branch_taken2;
+				if ((uint16)var_C == (uint16)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1602,7 +1602,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C != (uint16_t)_scriptVars[var_6]) goto branch_taken2;
+				if ((uint16)var_C != (uint16)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1611,7 +1611,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C > (uint16_t)_scriptVars[var_6]) goto branch_taken2;
+				if ((uint16)var_C > (uint16)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1620,7 +1620,7 @@ vol_advance:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
-				if ((uint16_t)var_C < (uint16_t)_scriptVars[var_6]) goto branch_taken2;
+				if ((uint16)var_C < (uint16)_scriptVars[var_6]) goto branch_taken2;
 				goto branch_skip5;
 			}
 
@@ -1688,7 +1688,7 @@ branch_skip5:
 
 			/* ---- opcode -60 (0xC4): call a function pointer ---- */
 			case 2: {
-				uint16_t fptr = readWord_impl();
+				uint16 fptr = readWord_impl();
 
 				// Call the function
 				callFunction(fptr);
@@ -1701,7 +1701,7 @@ branch_skip5:
 			case 3:
 			{
 				pSrc++;
-				uint8_t arg = *pSrc;
+				uint8 arg = *pSrc;
 				(void)arg;   /* nullsub_1 is a no-op */
 				ch->_pSrc += 2;
 				goto dispatch;
@@ -1787,15 +1787,15 @@ post_keyon:
 					}
 				} else {
 					/* Normal fade: clamp velocity at 0..127 */
-					if ((int8_t)ch->_volumeFadeStep > 0) {
+					if ((int8)ch->_volumeFadeStep > 0) {
 						ch->_velocity += ch->_volumeFadeStep;
 						ch = _activeChannelPtr;
-						if ((int16_t)ch->_velocity > 0x7F)
+						if ((int16)ch->_velocity > 0x7F)
 							ch->_velocity = 0x7F;
 					} else {
 						ch->_velocity += ch->_volumeFadeStep;
 						ch = _activeChannelPtr;
-						if ((int8_t)ch->_velocity < 0)
+						if ((int8)ch->_velocity < 0)
 							ch->_velocity = 0;
 					}
 				}
@@ -1812,19 +1812,19 @@ post_keyon:
 			ch->_vibPeriodCounter = ch->_vibPeriodReload;
 			ch = _activeChannelPtr;
 
-			if ((int8_t)ch->_vibratoDepth != 0) {
-				int16_t pa = (int16_t)(int8_t)ch->_patchAttenuation;
-				int16_t vib = (int16_t)(int8_t)ch->_vibratoDepth;
-				int16_t sum = pa + vib;
+			if ((int8)ch->_vibratoDepth != 0) {
+				int16 pa = (int16)(int8)ch->_patchAttenuation;
+				int16 vib = (int16)(int8)ch->_vibratoDepth;
+				int16 sum = pa + vib;
 
-				if ((int8_t)ch->_vibratoDepth > 0) {
+				if ((int8)ch->_vibratoDepth > 0) {
 					/* Positive vibrato: bounce if above 0x7F */
 					if (sum > 0x7F)
-						ch->_vibratoDepth = (uint8_t)(-ch->_vibratoDepth);
+						ch->_vibratoDepth = (uint8)(-ch->_vibratoDepth);
 				} else {
 					/* Negative vibrato: bounce if below zero */
 					if (sum < 0)
-						ch->_vibratoDepth = (uint8_t)(-ch->_vibratoDepth);
+						ch->_vibratoDepth = (uint8)(-ch->_vibratoDepth);
 				}
 
 				ch = _activeChannelPtr;


Commit: e4bddf2cb0f81f56188b5fd6c337fa5ddc52899b
    https://github.com/scummvm/scummvm/commit/e4bddf2cb0f81f56188b5fd6c337fa5ddc52899b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:45+10:00

Commit Message:
MADS: PHANTOM: Further sound fixes

Changed paths:
    engines/mads/madsv2/core/asound.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 5551554e9ff..b4c1f192e12 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -918,8 +918,35 @@ dispatch:
 
 		/* High bit set - it is a command.  Commands above 0xBD go to switch. */
 		if (b > 0xBD) {
-			/* Decode the command opcode: ax = b - (-66) = b + 66          */
-			/* Switch range is 0..65 (opcodes -66 .. -1 mapped to 0..65)   */
+			/*
+			 * Decode the command opcode.
+			 * The byte is sign-extended to int8 (0xFF..-0x80 -> -1..-128),
+			 * then ax = int8(b) - (-66).
+			 *
+			 * Opcode -> ax mapping (selected):
+			 *   0xFF (-1)  -> 65    0xFE (-2)  -> 64    0xFD (-3)  -> 63
+			 *   0xFC (-4)  -> 62    0xFB (-5)  -> 61    0xFA (-6)  -> 60
+			 *   0xF9 (-7)  -> 59    0xF8 (-8)  -> 58    0xF7 (-9)  -> 57
+			 *   0xF6 (-10) -> 56    0xF5 (-11) -> 55    0xF4 (-12) -> 54
+			 *   0xF3 (-13) -> 53    0xF2 (-14) -> 52    0xF1 (-15) -> 51
+			 *   0xF0 (-16) -> 50    0xEF (-17) -> 49    0xEE (-18) -> 48
+			 *   0xED (-19) -> 47    0xEC (-20) -> 46    0xEB (-21) -> 45
+			 *   0xEA (-22) -> 44    0xE9 (-23) -> 43    0xE8 (-24) -> 42
+			 *   0xE7 (-25) -> 41    0xE6 (-26) -> 40    0xE5 (-27) -> 39
+			 *   0xE4 (-28) -> 38    0xE3 (-29) -> 37    0xE2 (-30) -> 36
+			 *   0xE1 (-31) -> 35    0xE0 (-32) -> 34    0xDF (-33) -> 33
+			 *   0xDE (-34) -> 32    0xDD (-35) -> 31    0xDC (-36) -> 30
+			 *   0xDB (-37) -> 29    0xDA (-38) -> 28    0xD9 (-39) -> 27
+			 *   0xD8 (-40) -> 26    0xD7 (-41) -> 25    0xD6 (-42) -> 24
+			 *   0xD5 (-43) -> 23    0xD4 (-44) -> 22    0xD3 (-45) -> 21
+			 *   0xD2 (-46) -> 20    0xD1 (-47) -> 19    0xD0 (-48) -> 18
+			 *   0xCF (-49) -> 17    0xCE (-50) -> 16    0xCD (-51) -> 15
+			 *   0xCC (-52) -> 14    0xCB (-53) -> 13    0xCA (-54) -> 12
+			 *   0xC9 (-55) -> 11    0xC8 (-56) -> 10    0xC7 (-57) ->  9
+			 *   0xC6 (-58) ->  8    0xC5 (-59) ->  7    0xC4 (-60) ->  6
+			 *   0xC3 (-61) ->  5    0xC2 (-62) ->  4    0xC1 (-63) ->  3
+			 *   0xC0 (-64) ->  2
+			 */
 			int16 ax = (int16)(int8)b;   /* sign-extend */
 			var_8 = 0;
 			ax = (int16)(ax - (-66));
@@ -1053,7 +1080,7 @@ dispatch:
 				goto dispatch;
 			}
 
-			/* ---- opcode -9  (0xF7): set note (with 0 duration override) ---- */
+			/* ---- opcode -9  (0xF7): set note offset (with 0 duration override) ---- */
 			case 57:
 			{
 				pSrc++;
@@ -1083,7 +1110,7 @@ dispatch:
 			}
 
 			/* ---- opcode -12 (0xF4): set volume ---- */
-			case 52:
+			case 54:
 			{
 				pSrc++;
 				uint8 vol = *pSrc;
@@ -1104,7 +1131,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -13 (0xF3): set fade ---- */
-			case 51:
+			case 53:
 			{
 				if (ch->_pendingStop != 0) {
 					ch->_pSrc += 3;
@@ -1120,7 +1147,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -14 (0xF2): set transpose ---- */
-			case 50:
+			case 52:
 			{
 				pSrc++;
 				ch->_transpose = *pSrc;
@@ -1129,7 +1156,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -15 (0xF1): set velocity ---- */
-			case 49:
+			case 51:
 			{
 				pSrc++;
 				uint8 vel = *pSrc;
@@ -1146,7 +1173,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -16 (0xF0): set patch attenuation ---- */
-			case 48:
+			case 50:
 			{
 				pSrc++;
 				ch->_patchAttenuation = *pSrc;
@@ -1156,7 +1183,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -17 (0xEF): set vibrato ---- */
-			case 47:
+			case 49:
 			{
 				pSrc++;
 				ch->_vibPeriodReload = *pSrc;
@@ -1168,7 +1195,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -18 (0xEE): set octave transpose ---- */
-			case 46:
+			case 48:
 			{
 				pSrc++;
 				ch->_octaveTranspose = *pSrc;
@@ -1177,7 +1204,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -19 (0xED): skip forward by N+3 bytes ---- */
-			case 45:
+			case 47:
 			{
 				pSrc++;
 				var_C = *pSrc;
@@ -1187,7 +1214,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -20 (0xEC): random pick from table ---- */
-			case 44:
+			case 46:
 			{
 				pSrc++;
 				uint8 tblSize = *pSrc;
@@ -1211,7 +1238,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -21 (0xEB): random value in range [lo..hi] ---- */
-			case 43:
+			case 45:
 			{
 				pSrc++;
 				var_A = *pSrc;
@@ -1233,15 +1260,8 @@ vol_advance:
 				goto dispatch;
 			}
 
-			/* ---- opcode -62 (0xC2): advance pSrc by 4 ---- */
-			case 4:
-			{
-				ch->_pSrc += 4;
-				goto dispatch;
-			}
-
 			/* ---- opcode -22 (0xEA): indirect random pick from var table ---- */
-			case 42:
+			case 44:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1259,7 +1279,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -23 (0xE9): store immediate into script var ---- */
-			case 41:
+			case 43:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1271,7 +1291,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -24 (0xE8): var-to-var copy ---- */
-			case 40:
+			case 42:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1283,7 +1303,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -25 (0xE7): indirect copy to stream ---- */
-			case 39:
+			case 41:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1297,7 +1317,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -26 (0xE6): increment script var ---- */
-			case 38:
+			case 40:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1307,7 +1327,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -27 (0xE5): decrement script var ---- */
-			case 37:
+			case 39:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1317,7 +1337,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -28 (0xE4): add immediate to script var ---- */
-			case 36:
+			case 38:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1329,7 +1349,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -29 (0xE3): add var to script var ---- */
-			case 35:
+			case 37:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1341,7 +1361,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -30 (0xE2): subtract immediate from script var ---- */
-			case 34:
+			case 36:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1353,7 +1373,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -31 (0xE1): subtract var from script var ---- */
-			case 33:
+			case 35:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1365,7 +1385,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -32 (0xE0): multiply script var by var ---- */
-			case 32:
+			case 34:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1377,7 +1397,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -33 (0xDF): multiply script var by immediate ---- */
-			case 31:
+			case 33:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1389,7 +1409,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -34 (0xDE): divide script var by immediate -> quotient ---- */
-			case 30:
+			case 32:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1401,7 +1421,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -35 (0xDD): divide var by var -> quotient to dst ---- */
-			case 29:
+			case 31:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1413,7 +1433,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -36 (0xDC): divide script var by immediate -> remainder ---- */
-			case 28:
+			case 30:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1425,7 +1445,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -37 (0xDB): divide var by var -> remainder to dst ---- */
-			case 27:
+			case 29:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1437,7 +1457,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -38 (0xDA): AND script var by immediate ---- */
-			case 26:
+			case 28:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1449,7 +1469,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -39 (0xD9): AND var by var ---- */
-			case 25:
+			case 27:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1461,7 +1481,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -40 (0xD8): OR script var by immediate ---- */
-			case 24:
+			case 26:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1473,7 +1493,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -41 (0xD7): OR var by var ---- */
-			case 23:
+			case 25:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1485,7 +1505,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -42 (0xD6): XOR script var by immediate ---- */
-			case 22:
+			case 24:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1497,7 +1517,7 @@ vol_advance:
 			}
 
 			/* ---- opcode -43 (0xD5): XOR var by var ---- */
-			case 21:
+			case 23:
 			{
 				pSrc++;
 				var_6 = *pSrc;
@@ -1515,8 +1535,8 @@ vol_advance:
 			 * For opcodes -52..-59 they are: var[idx] vs var[idx2].
 			 */
 
-			 /* -44 (0xD4): branch if imm == var */
-			case 20:
+			 /* ---- opcode -44 (0xD4): branch if imm == var ---- */
+			case 22:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1525,8 +1545,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -45 (0xD3): branch if imm != var */
-			case 19:
+			/* ---- opcode -45 (0xD3): branch if imm != var ---- */
+			case 21:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1534,8 +1554,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -46 (0xD2): branch if imm > var */
-			case 18:
+			/* ---- opcode -46 (0xD2): branch if imm > var ---- */
+			case 20:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1543,8 +1563,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -47 (0xD1): branch if imm >= var */
-			case 17:
+			/* ---- opcode -47 (0xD1): branch if imm >= var ---- */
+			case 19:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1552,8 +1572,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -48 (0xD0): branch if var[idx1] == var[idx2] */
-			case 16:
+			/* ---- opcode -48 (0xD0): branch if var[idx1] == var[idx2] ---- */
+			case 18:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1561,8 +1581,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -49 (0xCF): branch if var[idx1] != var[idx2] */
-			case 15:
+			/* ---- opcode -49 (0xCF): branch if var[idx1] != var[idx2] ---- */
+			case 17:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1570,8 +1590,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -50 (0xCE): branch if var[idx1] > var[idx2] */
-			case 14:
+			/* ---- opcode -50 (0xCE): branch if var[idx1] > var[idx2] ---- */
+			case 16:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1579,8 +1599,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -51 (0xCD): branch if var[idx1] < var[idx2] */
-			case 13:
+			/* ---- opcode -51 (0xCD): branch if var[idx1] < var[idx2] ---- */
+			case 15:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1588,8 +1608,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -52 (0xCC): branch if imm == var (extended) - same as -44 logic */
-			case 12:
+			/* ---- opcode -52 (0xCC): branch if imm == var (extended) ---- */
+			case 14:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1597,8 +1617,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -53 (0xCB): branch if imm != var (extended) */
-			case 11:
+			/* ---- opcode -53 (0xCB): branch if imm != var (extended) ---- */
+			case 13:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1606,8 +1626,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -54 (0xCA): branch if imm > var (extended) */
-			case 10:
+			/* ---- opcode -54 (0xCA): branch if imm > var (extended) ---- */
+			case 12:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1615,8 +1635,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -55 (0xC9): branch if imm < var (extended) */
-			case 9:
+			/* ---- opcode -55 (0xC9): branch if imm < var (extended) ---- */
+			case 11:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1624,8 +1644,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -56 (0xC8): branch if var[idx1] == var[idx2] (extended) */
-			case 8:
+			/* ---- opcode -56 (0xC8): branch if var[idx1] == var[idx2] (extended) ---- */
+			case 10:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1633,8 +1653,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -57 (0xC7): branch if var[idx1] != var[idx2] (extended) */
-			case 7:
+			/* ---- opcode -57 (0xC7): branch if var[idx1] != var[idx2] (extended) ---- */
+			case 9:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1642,8 +1662,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -58 (0xC6): branch if var[idx1] > var[idx2] (extended) */
-			case 6:
+			/* ---- opcode -58 (0xC6): branch if var[idx1] > var[idx2] (extended) ---- */
+			case 8:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1651,8 +1671,8 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* -59 (0xC5): branch if var[idx1] < var[idx2] (extended) */
-			case 5:
+			/* ---- opcode -59 (0xC5): branch if var[idx1] < var[idx2] (extended) ---- */
+			case 7:
 			{
 				pSrc++; var_6 = *pSrc;
 				pSrc++; var_C = *pSrc;
@@ -1660,7 +1680,7 @@ vol_advance:
 				goto branch_skip5;
 			}
 
-			/* Shared branch-taken logic for -44..-51: save +5 as return target */
+			/* Shared branch-taken logic for -44..-51: save pSrc+5 as return target */
 branch_taken:
 			{
 				ch->_branchTargetPtr = ch->_pSrc + 5;
@@ -1669,11 +1689,10 @@ branch_taken:
 				goto dispatch;
 			}
 
-			/* Shared branch-taken logic for -52..-59: read immediate target word */
+			/* Shared branch-taken logic for -52..-59: same, different label for clarity */
 branch_taken2:
 			{
 				ch->_branchTargetPtr = ch->_pSrc + 5;
-				/* fall through to reading the target word */
 				byte *dest = &_soundData[readWord_impl()];
 				ch->_pSrc = dest;
 				goto dispatch;
@@ -1687,18 +1706,16 @@ branch_skip5:
 			}
 
 			/* ---- opcode -60 (0xC4): call a function pointer ---- */
-			case 2: {
+			case 6:
+			{
 				uint16 fptr = readWord_impl();
-
-				// Call the function
 				callFunction(fptr);
-
 				ch->_pSrc += 3;
 				goto dispatch;
 			}
 
 			/* ---- opcode -61 (0xC3): call nullsub_1 with byte arg ---- */
-			case 3:
+			case 5:
 			{
 				pSrc++;
 				uint8 arg = *pSrc;
@@ -1707,8 +1724,15 @@ branch_skip5:
 				goto dispatch;
 			}
 
+			/* ---- opcode -62 (0xC2): advance pSrc by 4 ---- */
+			case 4:
+			{
+				ch->_pSrc += 4;
+				goto dispatch;
+			}
+
 			/* ---- opcode -63 (0xC1): set _tempoScale ---- */
-			case 1:
+			case 3:
 			{
 				pSrc++;
 				_tempoScale = *pSrc;
@@ -1717,7 +1741,7 @@ branch_skip5:
 			}
 
 			/* ---- opcode -64 (0xC0): set tempo / frame sync ---- */
-			case 0:
+			case 2:
 			{
 				pSrc++;
 				_tempoReload = *pSrc;
@@ -1728,19 +1752,32 @@ branch_skip5:
 				goto dispatch;
 			}
 
+			/* ---- opcode -65 (0xBF): set _tempoTarget / _tempoBase, enable tick ---- */
+			case 1:
+			{
+				uint16 w = readWord_impl();
+				_tempoTarget = w;
+				ch->_pSrc += 3;
+				if (_frameNumber2 == 0)
+					_tempoBase = _tempoTarget;
+				_tickEnabled = 1;
+				_tickCounter = 1;
+				goto dispatch;
+			}
+
+			/* ---- opcode -66 (0xBE): set _tempoShift ---- */
+			case 0:
+			{
+				pSrc++;
+				_tempoShift = (int16)(int8)*pSrc;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
 			default:
+				error("Unhandled sound opcode");
 				break;
 			} /* end switch */
-
-			goto dispatch;
 		}
-
-		/* --- 0xBE or 0xBF range (> 0xBD but <0xC0): treated as commands -65/-64 ---
-		 * 0xBF -> ax = 0xBF + 66 - 66 = 0xBF; not in [0,65], falls to default in dispatch.
-		 * The asm handles 0xBD and below as notes, >0xBD as the big switch.
-		 * 0xBE / 0xBF would land in the switch default (no-op) which falls through
-		 * to the re-dispatch. Handled by the goto dispatch above.
-		 */
 	} /* end command decode block */
 
 	/* Should not reach here in normal execution */


Commit: 645c8b78448c746d361b6345f9aec6af32499ff6
    https://github.com/scummvm/scummvm/commit/645c8b78448c746d361b6345f9aec6af32499ff6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:45+10:00

Commit Message:
MADS: PHANTOM: Load sound samples for each driver

Changed paths:
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h
    engines/mads/madsv2/phantom/sound_phantom.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index b4c1f192e12..fbc34bc6477 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -109,6 +109,30 @@ static uint8 ADLIB_NULLDATA[] = {
 
 // =========================================================================
 
+AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
+	_attackRate = s.readByte();
+	_decayRate = s.readByte();
+	_sustainLevel = s.readByte();
+	_releaseRate = s.readByte();
+	_egTyp = s.readByte();
+	_ksr = s.readByte();
+	_totalLevel = s.readByte();
+	_scalingLevel = s.readByte();
+	_waveformSelect = s.readByte();
+	_freqMultiple = s.readByte();
+	_feedback = s.readByte();
+	_ampMod = s.readByte();
+	_vib = s.readByte();
+	_alg = s.readByte();
+	_freqSweepInit = s.readByte();
+	_reserved = s.readByte();
+	_freqMask = s.readUint16LE();
+	_freqBase = s.readUint16LE();
+	_outerLoopPtr = s.readUint16LE();
+}
+
+// =========================================================================
+
 void AdlibChannel::reset() {
 	_activeCount = 0;
 	_pitchBend = 0;
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index cd8188f0464..8127a553ec0 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -121,6 +121,9 @@ struct AdlibSample {
 	uint16 _freqMask = 0;
 	uint16 _freqBase = 0;
 	uint16 _outerLoopPtr = 0;
+
+	AdlibSample() {}
+	AdlibSample(Common::SeekableReadStream &s);
 };
 
 class ASound : public SoundDriver {
diff --git a/engines/mads/madsv2/phantom/sound_phantom.cpp b/engines/mads/madsv2/phantom/sound_phantom.cpp
index b60706ecbdf..5288e78f98e 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.cpp
+++ b/engines/mads/madsv2/phantom/sound_phantom.cpp
@@ -111,6 +111,10 @@ const ASound1::CommandPtr ASound1::_commandList[40] = {
 
 ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl)
 		: ASound(mixer, opl, "asound.ph1", 0x21e0, 0x4d20) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound1::command(int commandId, int param) {
@@ -424,6 +428,10 @@ const ASound2::CommandPtr ASound2::_commandList[73] = {
 
 ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl)
 		: ASound(mixer, opl, "asound.ph2", 0x2040, 0x2300) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound2::command(int commandId, int param) {
@@ -682,6 +690,10 @@ const ASound3::CommandPtr ASound3::_commandList[77] = {
 
 ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl)
 		: ASound(mixer, opl, "asound.ph3", 0x20c0, 0x31a0) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound3::command(int commandId, int param) {
@@ -999,6 +1011,10 @@ const ASound4::CommandPtr ASound4::_commandList[71] = {
 
 ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl)
 		: ASound(mixer, opl, "asound.ph4", 0x1f90, 0x14d0) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound4::command(int commandId, int param) {
@@ -1186,6 +1202,10 @@ const ASound5::CommandPtr ASound5::_commandList[79] = {
 
 ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl)
 		: ASound(mixer, opl, "asound.ph5", 0x2140, 0x5cd0) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound5::command(int commandId, int param) {
@@ -1504,6 +1524,10 @@ const ASound9::CommandPtr ASound9::_commandList[72] = {
 
 ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) :
 		ASound(mixer, opl, "asound.ph9", 0x20c0, 0x3690) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x1d4);
+	for (int i = 0; i < 120; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
 }
 
 int ASound9::command(int commandId, int param) {


Commit: 105fe44e3af39d6f39e6107096f28ac8b7e6c6f3
    https://github.com/scummvm/scummvm/commit/105fe44e3af39d6f39e6107096f28ac8b7e6c6f3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:45+10:00

Commit Message:
MADS: PHANTOM: Sound mutex and init fixes

Changed paths:
    engines/mads/core/sound_manager.cpp
    engines/mads/core/sound_manager.h
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h
    engines/mads/nebular/core/asound.cpp
    engines/mads/nebular/core/asound.h


diff --git a/engines/mads/core/sound_manager.cpp b/engines/mads/core/sound_manager.cpp
index 97cd10df4f4..8028b932cbb 100644
--- a/engines/mads/core/sound_manager.cpp
+++ b/engines/mads/core/sound_manager.cpp
@@ -137,6 +137,7 @@ SoundDriver::SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path
 }
 
 SoundDriver::~SoundDriver() {
+	Common::StackLock slock(_driverMutex);
 	_opl->stop();
 }
 
diff --git a/engines/mads/core/sound_manager.h b/engines/mads/core/sound_manager.h
index 6ea9d5588c5..25bc5471118 100644
--- a/engines/mads/core/sound_manager.h
+++ b/engines/mads/core/sound_manager.h
@@ -56,6 +56,7 @@ protected:
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
 	Common::Array<byte> _soundData;
+	Common::Mutex _driverMutex;
 
 	/**
 	 * Returns data for the specified offset. It also caches the data size for that
diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index fbc34bc6477..45cf5f35860 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "audio/fmopl.h"
+#include "common/debug.h"
 #include "mads/madsv2/core/asound.h"
 
 namespace MADS {
@@ -204,6 +205,16 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 		int dataOffset, int dataSize) :
 		SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
 	AdlibChannel::_isDisabled = false;
+
+	write(4, 0x60);		// Mask off both adlib timers
+	write(4, 0x80);		// IRQ reset timer flags
+	write(2, 0xff);		// Timer 1 rate 80us
+	write(4, 0x21);		// Activate timer 1
+	write(4, 0x60);
+	write(4, 0x80);
+
+	command0();
+
 	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
 }
 
@@ -430,11 +441,9 @@ void ASound::onTimer() {
 }
 
 void ASound::flush() {
-	Common::StackLock slock(_driverMutex);
-
 	while (!_queue.empty()) {
 		auto v = _queue.pop();
-		warning("%.2x %.2x", v.first, v.second);
+		debug("%.2x %.2x", v.first, v.second);
 		_opl->writeReg(v.first, v.second);
 	}
 }
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 8127a553ec0..730ace26456 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -128,7 +128,6 @@ struct AdlibSample {
 
 class ASound : public SoundDriver {
 private:
-	Common::Mutex _driverMutex;
 	Common::Queue< Common::Pair<byte, byte> > _queue;
 	uint16 _callbackCounter = 0;		// Period counter
 	uint16 _callbackPeriod = 0;			// Period reload
diff --git a/engines/mads/nebular/core/asound.cpp b/engines/mads/nebular/core/asound.cpp
index 8e5f6844c7a..441ee296bf5 100644
--- a/engines/mads/nebular/core/asound.cpp
+++ b/engines/mads/nebular/core/asound.cpp
@@ -254,8 +254,6 @@ int ASound::write2(int state, int reg, int val) {
 }
 
 void ASound::flush() {
-	Common::StackLock slock(_driverMutex);
-
 	while (!_queue.empty()) {
 		RegisterValue v = _queue.pop();
 		_opl->writeReg(v._regNum, v._value);
diff --git a/engines/mads/nebular/core/asound.h b/engines/mads/nebular/core/asound.h
index ad7cbb529a0..ec0b1b58d4b 100644
--- a/engines/mads/nebular/core/asound.h
+++ b/engines/mads/nebular/core/asound.h
@@ -279,7 +279,6 @@ public:
 	Common::Array<AdlibSample> _samples;
 	AdlibSample *_samplePtr;
 	Common::Queue<RegisterValue> _queue;
-	Common::Mutex _driverMutex;
 	int _frameCounter;
 	bool _isDisabled;
 	int _v1;


Commit: bf952bd1eb6c07583fb702a00ac07daf8418e941
    https://github.com/scummvm/scummvm/commit/bf952bd1eb6c07583fb702a00ac07daf8418e941
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:46+10:00

Commit Message:
MADS: PHANTOM: Further sound fixes

Changed paths:
    engines/mads/madsv2/core/asound.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 45cf5f35860..a62945a4c40 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -431,6 +431,8 @@ void ASound::callFunction(uint16 offset) {
 
 
 void ASound::write(uint8 reg, uint8 value) {
+	if (reg != 2 && reg != 3 && reg != 4)
+		debug("%.2x %.2x", reg, value);
 	_queue.push(Common::Pair<byte, byte>(reg, value));
 }
 
@@ -733,7 +735,7 @@ void ASound::loadSample() {
 
 	/* --- Point samplePtr at operator 1 data and write registers --- */
 	int sampleIdx = (int)ch->_sampleIndex;
-	_samplePtr = &_samples[sampleIdx];
+	_samplePtr = &_samples[sampleIdx * 2];
 
 	/* Modulator operator regs (operator 1) */
 	uint8 ms1 = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
@@ -742,7 +744,7 @@ void ASound::loadSample() {
 	writeSampleRegs();
 
 	/* Carrier operator regs (operator 2, offset by 490 bytes = next pair) */
-	_samplePtr = (AdlibSample *)((uint8 *)&_samples[sampleIdx] + 490);
+	_samplePtr = &_samples[sampleIdx * 2 + 1];
 
 	uint8 ms2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
 	uint8 mr2 = SLOT_TO_REG_OFFSET[ms2];


Commit: 061fb12acc9fb0b1f5f9fd2da9acc059c883643f
    https://github.com/scummvm/scummvm/commit/061fb12acc9fb0b1f5f9fd2da9acc059c883643f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:46+10:00

Commit Message:
MADS: PHANTOM: New ASound class finally starting to produce sound

Changed paths:
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h
    engines/mads/madsv2/phantom/sound_phantom.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index a62945a4c40..577d4fcc7f5 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -110,6 +110,13 @@ static uint8 ADLIB_NULLDATA[] = {
 
 // =========================================================================
 
+constexpr uint8 OPL_VERSION_FLAG = 18;	// OPL version flag
+constexpr bool RHYTHM_HI_HAT = true;	// Rhythm mode: hi-hat
+constexpr bool RHYTHM_CYMBAL = true;	// Rhythm mode: cymbal
+constexpr bool RHYTHM_ENABLE = true;	// Rhythm mode: enable
+
+// =========================================================================
+
 AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
 	_attackRate = s.readByte();
 	_decayRate = s.readByte();
@@ -213,6 +220,7 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 	write(4, 0x60);
 	write(4, 0x80);
 
+	Common::fill(_adlibPorts, _adlibPorts + 256, 0);
 	command0();
 
 	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
@@ -429,23 +437,27 @@ void ASound::callFunction(uint16 offset) {
  * Support functions
  * ========================================================================= */
 
-
 void ASound::write(uint8 reg, uint8 value) {
+	_adlibPorts[reg] = value;
+
 	if (reg != 2 && reg != 3 && reg != 4)
 		debug("%.2x %.2x", reg, value);
 	_queue.push(Common::Pair<byte, byte>(reg, value));
 }
+bool timerFlag = false;
 
 void ASound::onTimer() {
+	if (!timerFlag) return; //***DEBUG****
 	Common::StackLock slock(_driverMutex);
+debug("---TIMER START");
 	poll();
 	flush();
+debug("---TIMER END");
 }
 
 void ASound::flush() {
 	while (!_queue.empty()) {
 		auto v = _queue.pop();
-		debug("%.2x %.2x", v.first, v.second);
 		_opl->writeReg(v.first, v.second);
 	}
 }
@@ -509,7 +521,7 @@ void ASound::writeVolume() {
 	/* KSL flags from port shadow */
 	uint8 kslBits = _adlibPorts[siReg] & 0xC0;
 
-	if (_adlib_v5660_2 < 0x18) {
+	if (OPL_VERSION_FLAG < 0x18) {
 		/* ---- OPL2 simple path ---- */
 		if (var8 < 0)  var8 = 0;
 		if (var8 > 63) var8 = 63;
@@ -547,7 +559,7 @@ void ASound::writeVolume() {
 
 	/* ---- Second operator (alg == 0 check) ---- */
 	int sampleIdx = (int)ch->_sampleIndex;
-	AdlibSample *smp = &_samples[sampleIdx];
+	AdlibSample *smp = &_samples[sampleIdx * 2];
 	if (smp->_alg != 0)
 		return;   /* FM: only one operator carries volume */
 
@@ -561,7 +573,7 @@ void ASound::writeVolume() {
 	tl_s = -tl_s;   /* neg */
 	int16 var8b = (int16)var8 - tl_s;   /* subtract from combined */
 
-	if (_adlib_v5660_2 < 0x18) {
+	if (OPL_VERSION_FLAG < 0x18) {
 		if (var8b < 0)  var8b = 0;
 		if (var8b > 63) var8b = 63;
 		int16 val2 = (0x3F - var8b) | ksl2;
@@ -673,36 +685,36 @@ void ASound::writeSampleRegs() {
 	AdlibSample *smp = _samplePtr;
 	uint16 base = _currentOpBase;
 
-	/* --- Register 0xBD: Percussion/tremolo/vibrato depth --- */
+	/* --- Register 0xBD: Hi-hat / Cymbal / percussion bits --- */
 	uint8 bdVal = 0;
-	if (_rhythmHiHat != 1) bdVal |= 0x80;
-	if (_rhythmCymbal != 1) bdVal |= 0x40;
+	if (RHYTHM_HI_HAT) bdVal |= 0x80;
+	if (RHYTHM_CYMBAL) bdVal |= 0x40;
 	bdVal |= _adlibPorts[0xBD] & 0x3F;
 	write(0xBD, bdVal);
 
-	/* --- Register 0x08: Note-select (CSM/keyboardsplit) --- */
-	uint8 ns = (_rhythmEnable != 1) ? 0x40 : 0x00;
+	/* --- Register 0x08: Note-select --- */
+	uint8 ns = RHYTHM_ENABLE ? 0x40 : 0x00;  // was !=, should be ==
 	write(0x08, ns);
 
 	/* --- Register 0xC0+voice: Feedback / algorithm --- */
-	uint8 fb = (uint8)(smp->_feedback << 1);
-	if (smp->_vib) fb |= 0x01;   /* _alg field in struct */
-	write((uint8)(_activeChannelReg + 0xC0), fb);
+	uint8 value = ((uint16)smp->_feedback << 1) | (smp->_alg == 1 ? 0 : 1);
+	write(_activeChannelReg + 0xC0, value);
 
 	/* --- Register 0x60+base: Attack/Decay --- */
-	uint8 ad = (uint8)((smp->_attackRate << 4) | (smp->_decayRate & 0x0F));
+	uint8 ad = (uint8)(((smp->_attackRate & 0x0F) << 4) | (smp->_decayRate & 0x0F));
 	write((uint8)(base + 0x60), ad);
 
 	/* --- Register 0x80+base: Sustain/Release --- */
-	uint8 sr = (uint8)((smp->_sustainLevel << 4) | (smp->_releaseRate & 0x0F));
+	uint8 sr = (uint8)(((smp->_sustainLevel & 0x0F) << 4) | (smp->_releaseRate & 0x0F));
 	write((uint8)(base + 0x80), sr);
 
-	/* --- Register 0x20+base: Tremolo/vibrato/sustain/KSR/multiplier --- */
+	/* --- Register 0x20+base: Tremolo/Vibrato/Sustain/KSR/Multiplier --- */
+	// Each flag: bit set when field == 1 (sbb/neg pattern with cmc)
 	uint8 am = 0;
-	if (smp->_egTyp != 1) am |= 0x20;
-	if (smp->_ksr != 1) am |= 0x10;
-	if (smp->_ampMod != 1) am |= 0x80;
-	if (smp->_vib != 1) am |= 0x40;   /* NB: vib doubles as "AM" flag here */
+	if (smp->_egTyp == 1) am |= 0x20;
+	if (smp->_vib == 1) am |= 0x40;
+	if (smp->_ksr == 1) am |= 0x10;
+	if (smp->_ampMod == 1) am |= 0x80;
 	am |= smp->_freqMultiple & 0x0F;
 	write((uint8)(base + 0x20), am);
 
@@ -714,70 +726,45 @@ void ASound::loadSample() {
 	AdlibChannel *ch = _activeChannelPtr;
 	uint8 chanNum = _activeChannelNumber;
 
-	/* --- Silence modulator operator first --- */
+	/* --- Phase 1: Silence modulator operator --- */
 	uint8 mSlot = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
-	uint8 mReg = SLOT_TO_REG_OFFSET[mSlot];
-	_currentOpBase = mReg;
+	_currentOpBase = SLOT_TO_REG_OFFSET[mSlot];
+	write(_currentOpBase + 0x80, 0xFF);
+	write(_currentOpBase + 0x40, 63);
 
-	write((uint8)(_currentOpBase + 0x80), 0xFF);  /* max release */
-	write((uint8)(_currentOpBase + 0x40), 63);    /* full attenuation */
-
-	/* --- Silence carrier operator --- */
+	/* --- Phase 2: Silence carrier operator --- */
 	uint8 cSlot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
-	uint8 cReg = SLOT_TO_REG_OFFSET[cSlot];
-	_currentOpBase = (uint16)cReg;
-
-	write((uint8)(_currentOpBase + 0x80), 0xFF);
-	write((uint8)(_currentOpBase + 0x40), 63);
+	_currentOpBase = SLOT_TO_REG_OFFSET[cSlot];
+	write(_currentOpBase + 0x80, 0xFF);
+	write(_currentOpBase + 0x40, 63);
 
-	/* --- Set active register for C0 writes --- */
+	/* --- Phase 3: First writeSampleRegs --- */
 	_activeChannelReg = (uint16)chanNum;
-
-	/* --- Point samplePtr at operator 1 data and write registers --- */
-	int sampleIdx = (int)ch->_sampleIndex;
-	_samplePtr = &_samples[sampleIdx * 2];
-
-	/* Modulator operator regs (operator 1) */
-	uint8 ms1 = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
-	uint8 mr1 = SLOT_TO_REG_OFFSET[ms1];
-	_currentOpBase = (uint16)mr1;
+	_samplePtr = &_samples[ch->_sampleIndex * 2];      // operator 1
 	writeSampleRegs();
 
-	/* Carrier operator regs (operator 2, offset by 490 bytes = next pair) */
-	_samplePtr = &_samples[sampleIdx * 2 + 1];
-
-	uint8 ms2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
-	uint8 mr2 = SLOT_TO_REG_OFFSET[ms2];
-	_currentOpBase = (uint16)mr2;
+	/* --- Phase 4: Second writeSampleRegs --- */
+	_samplePtr = &_samples[ch->_sampleIndex * 2 + 1];  // operator 2
+	_currentOpBase = SLOT_TO_REG_OFFSET[MODULATOR_SLOT_FOR_VOICE[chanNum * 2]];
 	writeSampleRegs();
 
-	/* Write carrier TL: noteOffset shifts the level */
-	AdlibSample *smp2 = _samplePtr;
-	uint8 noteOffBits = (uint8)((smp2->_attackRate << 6) | 0x3F);
-	write((uint8)(_currentOpBase + 0x40), noteOffBits);
-
-	/* --- Third operator (if present, offset 0x2C * sampleIndex[5]) --- */
-	int smp3idx = (int)ch->_noteOffset;
-	_samplePtr = &_samples[smp3idx * 2];
-
-	uint8 s3slot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
-	uint8 s3reg = SLOT_TO_REG_OFFSET[s3slot];
-	uint16 s3Reg = (uint16)s3reg + 0x40;
+	/* --- Phase 5: Carrier TL write --- */
+	write(_currentOpBase + 0x40, (uint8)((_samplePtr->_scalingLevel << 6) | 0x3F));
 
+	/* --- Phase 6: Third sample --- */
+	uint8 thirdIdx = *((uint8 *)ch + 5);
+	_samplePtr = &_samples[thirdIdx * 2];   // operator 1 of third sample
 	AdlibSample *smp3 = _samplePtr;
+	uint16 cRegPlus40 = SLOT_TO_REG_OFFSET[CARRIER_SLOT_FOR_VOICE[chanNum * 2]] + 0x40;
+
 	if (smp3->_alg == 0) {
-		/* FM: write scaling level + max TL */
-		uint8 scl3 = (uint8)((smp3->_scalingLevel << 6) | 0x3F);
-		write((uint8)s3Reg, scl3);
+		write(cRegPlus40, (uint8)((smp3->_scalingLevel << 6) | 0x3F));
 	} else {
-		/* Additive: write actual total level */
-		int16 tl3 = (int16)smp3->_totalLevel - 63;
-		tl3 = -tl3;
-		uint8 scl3 = (uint8)((smp3->_scalingLevel << 6) | (tl3 & 0x3F));
-		write((uint8)s3Reg, scl3);
+		uint8 tl = (uint8)(-(smp3->_totalLevel - 63) & 0xFF);
+		write(cRegPlus40, (uint8)((smp3->_scalingLevel << 6) | (tl & 0x3F)));
 	}
 
-	/* Copy sweep / freq parameters from sample into channel */
+	/* --- Copy sweep/freq parameters from sample into channel --- */
 	ch->_freqSweepCounter = smp3->_freqSweepInit;
 	ch->_noiseFreqMask = smp3->_freqMask;
 	ch->_freqAccum = smp3->_freqBase;
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 730ace26456..27cf346814e 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -29,7 +29,7 @@
 
 namespace MADS {
 namespace MADSV2 {
-
+extern bool timerFlag; //***DEBUG***
 #define ADLIB_CHANNEL_COUNT 9
 
 struct AdlibChannel {
@@ -137,7 +137,6 @@ private:
 	uint16 _currentOpBase = 0;			// Current operator base
 	AdlibSample *_samplePtr = NULL;
 	byte *pSrc = nullptr;				// Current read pointer
-	uint8 _adlib_v5660_2 = 0;			// OPL version flag
 	int16 _pollResult = 0;
 	int16 _resultFlag = 0;
 	uint16 _randomSeed = 0x4D2;
@@ -146,12 +145,7 @@ private:
 	uint8  _ch5SweepLive = 0;			// Channel5 savedFreqSweep shadow
 	uint8  _ch5SweepSaved = 0;			// Channel5 savedFreqSweep shadow 2
 	uint8  _ch5PendingStop = 0;			// Channel5 pendingStop shadow
-	/* Percussion flags (_rhythmHiHat/D/E) */
-	uint8 _rhythmHiHat = 0;				// Rhythm mode: hi-hat
-	uint8 _rhythmCymbal = 0;			// Rhythm mode: cymbal
-	uint8 _rhythmEnable = 0;			// Rhythm mode: enable
-
-	uint8 _anySweepActive = 0;				// any-sweep-active flag
+	uint8 _anySweepActive = 0;			// any-sweep-active flag
 	int _frameNumber2 = 0;
 	uint8 _scriptVars[32];				// General-purpose script registers
 	uint16 _tickEnabled = 1;
diff --git a/engines/mads/madsv2/phantom/sound_phantom.cpp b/engines/mads/madsv2/phantom/sound_phantom.cpp
index 5288e78f98e..39d30e28728 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.cpp
+++ b/engines/mads/madsv2/phantom/sound_phantom.cpp
@@ -344,7 +344,9 @@ int ASound1::command37() {
 // command38 (sub_11D84) – alias for commandMusic0; also the direct dispatch
 // target for command 38.
 int ASound1::command38() {
-	return commandMusic0();
+	commandMusic0();
+	timerFlag = true; //****DEBUG****
+	return 0;
 }
 
 // command39 (sub_11FAE) – isSoundActive guard, command1, load ch0–5


Commit: a990769ec6689268d3132c98a2679bc0fbe23806
    https://github.com/scummvm/scummvm/commit/a990769ec6689268d3132c98a2679bc0fbe23806
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:46+10:00

Commit Message:
MADS: PHANTOM: Various ASound fixes

Changed paths:
    engines/mads/madsv2/core/asound.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 577d4fcc7f5..82ba9951881 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -37,18 +37,14 @@ bool AdlibChannel::_isDisabled;
   * Index is the patchAttenuation value (0-127); output is the 6-bit TL field.
   */
 static const uint8 PATCH_ATTEN_TO_TL[128] = {
-	0x3F,0x36,0x31,0x2D,0x2A,0x28,0x26,0x24,0x22,0x21,0x20,
-	0x1F,0x1E,0x1D,0x1C,0x1B,0x1A,0x19,0x19,0x18,0x17,0x17,
-	0x16,0x16,0x15,0x15,0x14,0x14,0x13,0x13,0x12,0x12,0x12,
-	0x11,0x11,0x10,0x10,0x10,0x0F,0x0F,0x0F,0x0E,0x0E,0x0E,
-	0x0E,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C,0x0B,0x0B,0x0B,
-	0x0B,0x0B,0x0A,0x0A,0x0A,0x0A,0x0A,0x09,0x09,0x09,0x09,
-	0x09,0x08,0x08,0x08,0x08,0x08,0x07,0x07,0x07,0x07,0x07,
-	0x07,0x06,0x06,0x06,0x06,0x06,0x06,0x05,0x05,0x05,0x05,
-	0x05,0x05,0x05,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x03,
-	0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x02,0x02,0x02,0x02,
-	0x02,0x02,0x02,0x02,0x02,0x01,0x01,0x01,0x01,0x01,0x01,
-	0x01,0x01,0x01,0x01,0x00,0x00,0x00
+	63, 54, 49, 45, 42, 40, 38, 36, 34, 33, 32, 31, 30, 29, 28, 27,
+	26, 25, 25, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18,
+	18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13,
+	12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9,
+	9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+	6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4,
+	4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
 };
 
 /*
@@ -69,17 +65,10 @@ static const uint8 VOL_VEL_TO_ATTEN_STEP[128] = {
 	0x1F,0x1F,0x1F,0x1F, 0x20,0x20,0x20,0x20
 };
 
-/* Carrier operator slot offset per voice (CARRIER_SLOT_FOR_VOICE, index 0 = unused pad) */
-static const uint8 CARRIER_SLOT_FOR_VOICE[18] = {
-	0,3,1,4,2,5,6,9,10,8,11,12,15,13,16,14,17,0
-};
-
-/*
- * MODULATOR_SLOT_FOR_VOICE - Modulator operator slot offset per voice.
- * 17 entries used (voices 0-8 -> modulator slots, voices 0-8 -> carrier slots).
- */
-static const uint8 MODULATOR_SLOT_FOR_VOICE[17] = {
-	3,1,4,2,5,6,9,7,10,8,11,12,15,13,16,14,17
+/* Carrier and operator slot offset per voice */
+static const uint8 VOICE_SLOTS[ADLIB_CHANNEL_COUNT][2] = {
+	{ 0, 3 }, { 1, 4 }, { 2, 5 }, { 6, 9 }, { 7, 10 },
+	{ 8, 11 }, { 12, 15 }, { 13, 16 }, { 14, 17 }
 };
 
 /*
@@ -449,10 +438,9 @@ bool timerFlag = false;
 void ASound::onTimer() {
 	if (!timerFlag) return; //***DEBUG****
 	Common::StackLock slock(_driverMutex);
-debug("---TIMER START");
+
 	poll();
 	flush();
-debug("---TIMER END");
 }
 
 void ASound::flush() {
@@ -514,7 +502,7 @@ void ASound::writeVolume() {
 
 	/* Determine carrier operator register for this voice */
 	uint8 chanNum = _activeChannelNumber;
-	uint8 slot = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];          /* modulator slot index */
+	uint8 slot = VOICE_SLOTS[chanNum][1];          /* modulator slot index */
 	uint8 regOff = SLOT_TO_REG_OFFSET[slot];
 	uint16 siReg = (uint16)regOff + 0x40;         /* TL register base */
 
@@ -544,7 +532,7 @@ void ASound::writeVolume() {
 		if (var6 < 0)  var6 = 0;
 		if (var6 > 63) var6 = 63;
 		/* carrier register */
-		uint8 cslot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+		uint8 cslot = VOICE_SLOTS[chanNum][1];
 		uint8 cregOff = SLOT_TO_REG_OFFSET[cslot];
 		uint16 cReg = (uint16)cregOff + 0x40;
 		uint8 cksl = _adlibPorts[cReg] & 0xC0;
@@ -564,7 +552,7 @@ void ASound::writeVolume() {
 		return;   /* FM: only one operator carries volume */
 
 	/* Second operator TL register */
-	uint8 slot2 = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 slot2 = VOICE_SLOTS[chanNum][0];
 	uint8 roff2 = SLOT_TO_REG_OFFSET[slot2];
 	uint16 siReg2 = (uint16)roff2 + 0x40;
 	uint8 ksl2 = _adlibPorts[siReg2] & 0xC0;
@@ -727,13 +715,13 @@ void ASound::loadSample() {
 	uint8 chanNum = _activeChannelNumber;
 
 	/* --- Phase 1: Silence modulator operator --- */
-	uint8 mSlot = MODULATOR_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 mSlot = VOICE_SLOTS[chanNum][1];
 	_currentOpBase = SLOT_TO_REG_OFFSET[mSlot];
 	write(_currentOpBase + 0x80, 0xFF);
 	write(_currentOpBase + 0x40, 63);
 
 	/* --- Phase 2: Silence carrier operator --- */
-	uint8 cSlot = CARRIER_SLOT_FOR_VOICE[chanNum * 2];
+	uint8 cSlot = VOICE_SLOTS[chanNum][0];
 	_currentOpBase = SLOT_TO_REG_OFFSET[cSlot];
 	write(_currentOpBase + 0x80, 0xFF);
 	write(_currentOpBase + 0x40, 63);
@@ -745,7 +733,7 @@ void ASound::loadSample() {
 
 	/* --- Phase 4: Second writeSampleRegs --- */
 	_samplePtr = &_samples[ch->_sampleIndex * 2 + 1];  // operator 2
-	_currentOpBase = SLOT_TO_REG_OFFSET[MODULATOR_SLOT_FOR_VOICE[chanNum * 2]];
+	_currentOpBase = SLOT_TO_REG_OFFSET[VOICE_SLOTS[chanNum][1]];
 	writeSampleRegs();
 
 	/* --- Phase 5: Carrier TL write --- */
@@ -755,7 +743,7 @@ void ASound::loadSample() {
 	uint8 thirdIdx = *((uint8 *)ch + 5);
 	_samplePtr = &_samples[thirdIdx * 2];   // operator 1 of third sample
 	AdlibSample *smp3 = _samplePtr;
-	uint16 cRegPlus40 = SLOT_TO_REG_OFFSET[CARRIER_SLOT_FOR_VOICE[chanNum * 2]] + 0x40;
+	uint16 cRegPlus40 = SLOT_TO_REG_OFFSET[VOICE_SLOTS[chanNum][0]] + 0x40;
 
 	if (smp3->_alg == 0) {
 		write(cRegPlus40, (uint8)((smp3->_scalingLevel << 6) | 0x3F));


Commit: 64a96d2fe8bce8822a30ae997fa38527ec7e5d07
    https://github.com/scummvm/scummvm/commit/64a96d2fe8bce8822a30ae997fa38527ec7e5d07
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:46+10:00

Commit Message:
MADS: PHANTOM: Cleanup of ASound comments and debugging code

Changed paths:
    engines/mads/madsv2/core/asound.cpp
    engines/mads/madsv2/core/asound.h
    engines/mads/madsv2/phantom/sound_phantom.cpp


diff --git a/engines/mads/madsv2/core/asound.cpp b/engines/mads/madsv2/core/asound.cpp
index 82ba9951881..2a8d46376b4 100644
--- a/engines/mads/madsv2/core/asound.cpp
+++ b/engines/mads/madsv2/core/asound.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "audio/fmopl.h"
-#include "common/debug.h"
 #include "mads/madsv2/core/asound.h"
 
 namespace MADS {
@@ -410,7 +409,7 @@ int ASound::command8() {
 	result |= _channel2._activeCount;
 	result |= _channel3._activeCount;
 	result |= _channel4._activeCount;
-	result |= (uint8)(_channel5._activeCount); /* _ch5PendingStop area */
+	result |= _channel5._activeCount;
 	result |= _channel6._activeCount;
 	result |= _channel7._activeCount;
 	result |= _channel8._activeCount;
@@ -428,15 +427,10 @@ void ASound::callFunction(uint16 offset) {
 
 void ASound::write(uint8 reg, uint8 value) {
 	_adlibPorts[reg] = value;
-
-	if (reg != 2 && reg != 3 && reg != 4)
-		debug("%.2x %.2x", reg, value);
 	_queue.push(Common::Pair<byte, byte>(reg, value));
 }
-bool timerFlag = false;
 
 void ASound::onTimer() {
-	if (!timerFlag) return; //***DEBUG****
 	Common::StackLock slock(_driverMutex);
 
 	poll();
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 27cf346814e..719829f280d 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -29,7 +29,7 @@
 
 namespace MADS {
 namespace MADSV2 {
-extern bool timerFlag; //***DEBUG***
+
 #define ADLIB_CHANNEL_COUNT 9
 
 struct AdlibChannel {
diff --git a/engines/mads/madsv2/phantom/sound_phantom.cpp b/engines/mads/madsv2/phantom/sound_phantom.cpp
index 39d30e28728..231c252316b 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.cpp
+++ b/engines/mads/madsv2/phantom/sound_phantom.cpp
@@ -85,15 +85,6 @@ void PhantomSoundManager::loadDriver(int sectionNumber) {
 
 /*-----------------------------------------------------------------------*/
 /* ASound1  (asound.ph1)                                                  *
- *                                                                        *
- * Channel-load helpers in the original:                                  *
- *   sub_103FF=ch0, sub_10404=ch1, sub_10409=ch2                         *
- *   sub_1040E=ch3, sub_10413=ch4, sub_10418=ch5                         *
- *   loc_1041D=ch6, loc_10422=ch7, loc_10427=ch8                         *
- *                                                                        *
- * sub_106DF = isSoundActive guard (non-zero BX -> already playing)       *
- * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
- * sub_10352 = findFreeChannel(pData, 0)                     (lower pool)  *
  *-----------------------------------------------------------------------*/
 
 const ASound1::CommandPtr ASound1::_commandList[40] = {
@@ -118,8 +109,6 @@ ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl)
 }
 
 int ASound1::command(int commandId, int param) {
-	// The original dispatcher also handles commands 64–76 via a raw-data
-	// near-pointer table (unk_13C3E) that cannot be safely reconstructed.
 	if (commandId > 39 || !_commandList[commandId])
 		return 0;
 	
@@ -137,15 +126,6 @@ int ASound1::command6() { return ASound::command6(); }
 int ASound1::command7() { return ASound::command7(); }
 int ASound1::command8() { return ASound::command8(); }
 
-// ---------------------------------------------------------------------------
-// Background-music loaders (sub_11D84, sub_11EE6, sub_11F0E, sub_11F36)
-// Each calls command1() then loads six channels.
-// The five cx values that command16 checks against _channels[0]->_field17
-// are the starting offsets of each piece: 0x1ECA, 0x21C4, 0x3418, 0x3688,
-// 0x3D52.  (0x21C4 is the start of dead code following commandMusic0 that
-// was never reached by any dispatch table entry.)
-// ---------------------------------------------------------------------------
-
 int ASound1::commandMusic0() {
 	ASound::command1();
 	_channels[0]->load(loadData(0x1ECA, 245));
@@ -191,7 +171,7 @@ int ASound1::commandMusic3() {
 }
 
 // ---------------------------------------------------------------------------
-// command16 (sub_11F70) – random background music
+// command16 – random background music
 //
 // If channel 0 is active and already playing one of the five known music
 // pieces (identified by their starting offset in field_17), leave it alone.
@@ -225,7 +205,7 @@ int ASound1::command16() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 24–27 (off_11C46) – upper channel pool (sub_1039C per entry)
+// commands 24–27 – upper channel pool
 // ---------------------------------------------------------------------------
 
 int ASound1::command24() {
@@ -251,10 +231,10 @@ int ASound1::command27() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 32–39 (off_11C4E, entries 0–7)
+// commands 32–39
 // ---------------------------------------------------------------------------
 
-// command32 (sub_11DD4) – no guard, no fade, load ch0–5
+// command32 – no guard, no fade, load ch0–5
 int ASound1::command32() {
 	_channels[0]->load(loadData(0x2522, 59));
 	_channels[1]->load(loadData(0x255D, 52));
@@ -265,7 +245,7 @@ int ASound1::command32() {
 	return 0;
 }
 
-// command33 (sub_11E02) – isSoundActive guard, command1, load ch0–5
+// command33 – isSoundActive guard, command1, load ch0–5
 int ASound1::command33() {
 	byte *pData = loadData(0x266C, 701);
 	if (!isSoundActive(pData)) {
@@ -280,7 +260,7 @@ int ASound1::command33() {
 	return 0;
 }
 
-// command34 (sub_11D54) – isSoundActive guard, stop(), load ch0–5
+// command34 – isSoundActive guard, stop(), load ch0–5
 int ASound1::command34() {
 	byte *pData = loadData(0x1852, 599);
 	if (!isSoundActive(pData)) {
@@ -295,7 +275,7 @@ int ASound1::command34() {
 	return 0;
 }
 
-// command35 (sub_11CCC) – isSoundActive guard, command2 (lower-bank fade),
+// command35 – isSoundActive guard, command2 (lower-bank fade),
 // load ch0–5
 int ASound1::command35() {
 	byte *pData = loadData(0x0C36, 329);
@@ -311,7 +291,7 @@ int ASound1::command35() {
 	return 0;
 }
 
-// command36 (sub_11CFC) – isSoundActive guard, command2 (lower-bank fade),
+// command36 – isSoundActive guard, command2 (lower-bank fade),
 // load ch0–5
 int ASound1::command36() {
 	byte *pData = loadData(0x1190, 327);
@@ -327,8 +307,8 @@ int ASound1::command36() {
 	return 0;
 }
 
-// command37 (sub_11E32) – isSoundActive guard, command1, four loadAny
-// calls starting from channel 0 (sub_10352 = findFreeChannel(pData, 0))
+// command37 – isSoundActive guard, command1, four loadAny
+// calls starting from channel 0
 int ASound1::command37() {
 	byte *pData = loadData(0x3220, 74);
 	if (!isSoundActive(pData)) {
@@ -341,15 +321,13 @@ int ASound1::command37() {
 	return 0;
 }
 
-// command38 (sub_11D84) – alias for commandMusic0; also the direct dispatch
+// command38 – alias for commandMusic0; also the direct dispatch
 // target for command 38.
 int ASound1::command38() {
-	commandMusic0();
-	timerFlag = true; //****DEBUG****
-	return 0;
+	return commandMusic0();
 }
 
-// command39 (sub_11FAE) – isSoundActive guard, command1, load ch0–5
+// command39 – isSoundActive guard, command1, load ch0–5
 int ASound1::command39() {
 	byte *pData = loadData(0x423A, 421);
 	if (!isSoundActive(pData)) {
@@ -368,31 +346,6 @@ int ASound1::command39() {
 
 /*-----------------------------------------------------------------------*/
 /* ASound2  (asound.ph2)                                                  *
- *                                                                        *
- * Dispatch table layout:                                                 *
- *   asound_commands1: commands  0– 8  (max=8,    base=0)                *
- *   asound_commands2: command     16  (max=0x10, base=0x10, 1 entry)    *
- *   asound_commands3: commands 24–27  (max=0x1B, base=0x18, 4 entries)  *
- *   asound_commands4: commands 32–35  (max=0x23, base=0x20, 4 entries)  *
- *   asound_commands5: commands 64–72  (max=0x48, base=0x40, 9 entries)  *
- *                                                                        *
- * Channel-load helpers:                                                  *
- *   sub_103FF=ch0  sub_10404=ch1  sub_10409=ch2  sub_1040E=ch3          *
- *   sub_10413=ch4  sub_10418=ch5  loc_1041D=ch6  loc_10422=ch7          *
- *   (ch8 not used in any sound command)                                  *
- *                                                                        *
- * sub_105F3 = command1 (fade both banks)                                 *
- * sub_10481 = command2 (lower setPtr2, cx=0x1A26)                       *
- * sub_105FA = command3 (lower bank fade helper)                          *
- * sub_104A9 = command4 (upper setPtr2, cx=0x1A26)                       *
- * sub_104BF = command6                                                   *
- * loc_10548 = command7                                                   *
- * unk_106B2 = command8 (OR of all channel activeCount fields)            *
- * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY)  (upper pool)  *
- * sub_10352 = findFreeChannel(pData, 0)                     (lower pool)  *
- *                                                                        *
- * commands5 entry 4 (command 68) = nullsub_3 = no-op                    *
  *-----------------------------------------------------------------------*/
 
 const ASound2::CommandPtr ASound2::_commandList[73] = {
@@ -455,7 +408,7 @@ int ASound2::command7() { return ASound::command7(); }
 int ASound2::command8() { return ASound::command8(); }
 
 // ---------------------------------------------------------------------------
-// command16 (sub_11CBC) – isSoundActive guard, command1, load ch0–5
+// command16 – isSoundActive guard, command1, load ch0–5
 // ---------------------------------------------------------------------------
 int ASound2::command16() {
 	byte *pData = loadData(0x0C36, 88);
@@ -472,30 +425,26 @@ int ASound2::command16() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 24–27 (asound_commands3) – upper channel pool via sub_1039C
+// commands 24–27 (asound_commands3) – upper channel pool
 // ---------------------------------------------------------------------------
 
-// sub_11D78
 int ASound2::command24() {
 	playSound(0x1A4A, 51);
 	playSound(0x1A7D, 46);
 	return 0;
 }
 
-// sub_11D85
 int ASound2::command25() {
 	playSound(0x1AAB, 44);
 	playSound(0x1AD7, 46);
 	return 0;
 }
 
-// sub_11D92
 int ASound2::command26() {
 	playSound(0x1B05, 12);
 	return 0;
 }
 
-// sub_11D99
 int ASound2::command27() {
 	playSound(0x1B11, 81);
 	return 0;
@@ -505,8 +454,7 @@ int ASound2::command27() {
 // commands 32–35 (asound_commands4)
 // ---------------------------------------------------------------------------
 
-// command32 (sub_11DDC) – command1, six loadAny calls from channel 0
-// (sub_10352 = findFreeChannel(pData, 0))
+// command32 – command1, six loadAny calls from channel 0
 int ASound2::command32() {
 	ASound::command1();
 	findFreeChannel(loadData(0x1BE4, 211));
@@ -518,7 +466,7 @@ int ASound2::command32() {
 	return 0;
 }
 
-// command33 (sub_11DA0) – isSoundActive guard, command1, load ch0–7
+// command33 – isSoundActive guard, command1, load ch0–7
 int ASound2::command33() {
 	byte *pData = loadData(0x1B62, 53);
 	if (!isSoundActive(pData)) {
@@ -535,7 +483,7 @@ int ASound2::command33() {
 	return 0;
 }
 
-// command34 (sub_11CEC) – isSoundActive guard, command1, load ch0–6
+// command34 – isSoundActive guard, command1, load ch0–6
 int ASound2::command34() {
 	byte *pData = loadData(0x0DC0, 495);
 	if (!isSoundActive(pData)) {
@@ -551,7 +499,7 @@ int ASound2::command34() {
 	return 0;
 }
 
-// command35 (sub_11E04) – isSoundActive guard, command1, load ch0–6
+// command35 – isSoundActive guard, command1, load ch0–6
 int ASound2::command35() {
 	byte *pData = loadData(0x1F02, 100);
 	if (!isSoundActive(pData)) {
@@ -568,40 +516,35 @@ int ASound2::command35() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 64–72 (asound_commands5) – upper channel pool via sub_1039C
+// commands 64–72 (asound_commands5) – upper channel pool
 // ---------------------------------------------------------------------------
 
-// sub_11D22
 int ASound2::command64() {
 	playSound(0x1928, 20);
 	return 0;
 }
 
-// sub_11D29
 int ASound2::command65() {
 	playSound(0x193C, 10);
 	return 0;
 }
 
-// sub_11D30
 int ASound2::command66() {
 	playSound(0x1946, 22);
 	playSound(0x195C, 17);
 	return 0;
 }
 
-// sub_11D3D
 int ASound2::command67() {
 	playSound(0x196D, 18);
 	return 0;
 }
 
-// nullsub_3 – no-op
+// no-op
 int ASound2::command68() {
 	return 0;
 }
 
-// sub_11D45
 int ASound2::command69() {
 	playSound(0x197F, 38);
 	playSound(0x19A5, 38);
@@ -609,20 +552,17 @@ int ASound2::command69() {
 	return 0;
 }
 
-// sub_11D58
 int ASound2::command70() {
 	playSound(0x19E5, 12);
 	playSound(0x19F1, 14);
 	return 0;
 }
 
-// sub_11D65
 int ASound2::command71() {
 	playSound(0x19FF, 14);
 	return 0;
 }
 
-// sub_11D6C
 int ASound2::command72() {
 	playSound(0x1A0D,  3);
 	playSound(0x1A10, 22);
@@ -633,25 +573,6 @@ int ASound2::command72() {
 
 /*-----------------------------------------------------------------------*/
 /* ASound3  (asound.ph3)                                                  *
- *                                                                        *
- * Dispatch table layout:                                                 *
- *   asound_commands1: commands  0–8   (max=8,    base=0)                *
- *   asound_commands2: command   16    (max=0x10, base=0x10, 1 entry)    *
- *   asound_commands3: commands 24–27  (max=0x1B, base=0x18, 4 entries)  *
- *   asound_commands4: commands 32–37  (max=0x25, base=0x20, 6 entries)  *
- *   asound_commands5: commands 64–76  (max=0x4B, base=0x40, 12 entries) *
- *     (entry 12, command 76 = nullsub_8, is a no-op)                    *
- *                                                                        *
- * Channel-load helpers:                                                  *
- *   sub_103FF=ch0  sub_10404=ch1  sub_10409=ch2  sub_1040E=ch3          *
- *   sub_10413=ch4  sub_10418=ch5  sub_1041D=ch6  sub_10422=ch7          *
- *   loc_10427 =ch8                                                       *
- *                                                                        *
- * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
- *                                                                        *
- * sub_11CC6 (helper) – isSoundActive guard on 0xC36, command1,          *
- *   loads ch0–7; called by command34 which then adds ch8 at 0x298E.     *
  *-----------------------------------------------------------------------*/
 
 const ASound3::CommandPtr ASound3::_commandList[77] = {
@@ -738,7 +659,7 @@ void ASound3::sub11CC6() {
 }
 
 // ---------------------------------------------------------------------------
-// command16 (sub_11D98) – isSoundActive guard, command1, load ch0–5
+// command16 – isSoundActive guard, command1, load ch0–5
 // ---------------------------------------------------------------------------
 int ASound3::command16() {
 	byte *pData = loadData(0x24F2, 172);
@@ -755,30 +676,26 @@ int ASound3::command16() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 24–27 (asound_commands3) – upper channel pool (sub_1039C)
+// commands 24–27 (asound_commands3) – upper channel pool
 // ---------------------------------------------------------------------------
 
-// sub_11E54
 int ASound3::command24() {
 	playSound(0x2A7C, 51);
 	playSound(0x2AAF, 46);
 	return 0;
 }
 
-// sub_11E61
 int ASound3::command25() {
 	playSound(0x2ADD, 44);
 	playSound(0x2B09, 46);
 	return 0;
 }
 
-// sub_11E6E
 int ASound3::command26() {
 	playSound(0x2B37, 12);
 	return 0;
 }
 
-// sub_11E75
 int ASound3::command27() {
 	playSound(0x2B43, 12);
 	return 0;
@@ -788,7 +705,7 @@ int ASound3::command27() {
 // commands 32–37 (asound_commands4)
 // ---------------------------------------------------------------------------
 
-// command32 (sub_11E7C) – isSoundActive guard, command1, load ch0–7
+// command32 – isSoundActive guard, command1, load ch0–7
 int ASound3::command32() {
 	byte *pData = loadData(0x2B94, 108);
 	if (!isSoundActive(pData)) {
@@ -805,7 +722,7 @@ int ASound3::command32() {
 	return 0;
 }
 
-// command33 (sub_11D32) – isSoundActive guard, command1, load ch0–6
+// command33 – isSoundActive guard, command1, load ch0–6
 int ASound3::command33() {
 	byte *pData = loadData(0x149E, 525);
 	if (!isSoundActive(pData)) {
@@ -821,7 +738,7 @@ int ASound3::command33() {
 	return 0;
 }
 
-// command34 (sub_11E2F) – calls sub11CC6 (loads ch0–7 if not active),
+// command34 – calls sub11CC6 (loads ch0–7 if not active),
 // then unconditionally loads ch8 at 0x298E
 int ASound3::command34() {
 	sub11CC6();
@@ -829,7 +746,7 @@ int ASound3::command34() {
 	return 0;
 }
 
-// command35 (sub_11D02) – isSoundActive guard, command1, load ch0–5
+// command35 – isSoundActive guard, command1, load ch0–5
 int ASound3::command35() {
 	byte *pData = loadData(0x0CB8, 413);
 	if (!isSoundActive(pData)) {
@@ -844,7 +761,7 @@ int ASound3::command35() {
 	return 0;
 }
 
-// command36 (sub_11D68) – isSoundActive guard, command1, load ch0–5
+// command36 – isSoundActive guard, command1, load ch0–5
 int ASound3::command36() {
 	byte *pData = loadData(0x2072, 196);
 	if (!isSoundActive(pData)) {
@@ -859,7 +776,7 @@ int ASound3::command36() {
 	return 0;
 }
 
-// command37 (sub_11DF8) – single upper-pool voice
+// command37 – single upper-pool voice
 int ASound3::command37() {
 	playSound(0x298E, 10);
 	return 0;
@@ -867,28 +784,20 @@ int ASound3::command37() {
 
 // ---------------------------------------------------------------------------
 // commands 64–75 (asound_commands5)
-//
-// sub_11DC8/sub_11DD4: load ch6 and ch8 directly (skipping ch7).
-// sub_11DE0: loads ch6, ch7, ch8.
-// sub_11E22/sub_11E47: load ch7 and ch8 directly.
-// Remaining entries use the upper pool (sub_1039C).
 // ---------------------------------------------------------------------------
 
-// sub_11DC8 – ch6 + ch8
 int ASound3::command64() {
 	_channels[6]->load(loadData(0x28CA, 50));
 	_channels[8]->load(loadData(0x28FC, 29));
 	return 0;
 }
 
-// sub_11DD4 – ch6 + ch8
 int ASound3::command65() {
 	_channels[6]->load(loadData(0x2919, 17));
 	_channels[8]->load(loadData(0x292A, 13));
 	return 0;
 }
 
-// sub_11DE0 – ch6 + ch7 + ch8
 int ASound3::command66() {
 	_channels[6]->load(loadData(0x2937, 31));
 	_channels[7]->load(loadData(0x2956, 15));
@@ -896,13 +805,11 @@ int ASound3::command66() {
 	return 0;
 }
 
-// sub_11DF2 – upper pool x1
 int ASound3::command67() {
 	playSound(0x2984, 10);
 	return 0;
 }
 
-// sub_11DFE – upper pool x3
 int ASound3::command68() {
 	playSound(0x2998, 22);
 	playSound(0x29AE, 20);
@@ -910,44 +817,37 @@ int ASound3::command68() {
 	return 0;
 }
 
-// sub_11E10 – upper pool x1
 int ASound3::command69() {
 	playSound(0x29D8, 18);
 	return 0;
 }
 
-// sub_11E16 – upper pool x1
 int ASound3::command70() {
 	playSound(0x2B4F, 15);
 	return 0;
 }
 
-// sub_11E1C – upper pool x1
 int ASound3::command71() {
 	playSound(0x2B5E, 54);
 	return 0;
 }
 
-// sub_11E22 – ch7 + ch8
 int ASound3::command72() {
 	_channels[7]->load(loadData(0x29EA, 17));
 	_channels[8]->load(loadData(0x2A18, 17));
 	return 0;
 }
 
-// sub_11E39 – upper pool x1
 int ASound3::command73() {
 	playSound(0x2A44, 10);
 	return 0;
 }
 
-// sub_11E40 – upper pool x1
 int ASound3::command74() {
 	playSound(0x2A4E, 46);
 	return 0;
 }
 
-// sub_11E47 – ch7 + ch8
 int ASound3::command75() {
 	_channels[7]->load(loadData(0x29FB, 29));
 	_channels[8]->load(loadData(0x2A29, 27));
@@ -958,27 +858,6 @@ int ASound3::command75() {
 
 /*-----------------------------------------------------------------------*/
 /* ASound4  (asound.ph4)                                                  *
- *                                                                        *
- * Dispatch table layout:                                                 *
- *   asound_commands1: commands  0–8   (max=8,    base=0)                *
- *   asound_commands2: command   16    (max=0x10, base=0x10, 1 entry)    *
- *   asound_commands3: commands 24–27  (max=0x1B, base=0x18, 4 entries)  *
- *   asound_commands4: commands 64–70  (max=0x46, base=0x40, 7 entries)  *
- *     (The 0x20-range table also points at asound_commands4 but has     *
- *      max=0, making commands 32–63 unreachable.)                       *
- *                                                                        *
- * Channel-load helpers:                                                  *
- *   sub_103FF=ch0  sub_10404=ch1  sub_10409=ch2  sub_1040E=ch3          *
- *   sub_10413=ch4  sub_10418=ch5  sub_1041D=ch6                         *
- *                                                                        *
- * sub_106DF = isSoundActive guard                                        *
- * sub_1039C = findFreeChannel(pData, ADLIB_CHANNEL_MIDWAY) (upper pool)   *
- *                                                                        *
- * commands 24 and 25 share the same handler (sub_11D0A).                *
- * Two unreferenced subs (sub_11D5E, sub_11D6B) contain dead sound data  *
- * that is never played; they are omitted.                                *
- * An unreferenced random-pitch stub exists between sub_11CAB and        *
- * sub_11CD3; it was never wired into any command.                        *
  *-----------------------------------------------------------------------*/
 
 const ASound4::CommandPtr ASound4::_commandList[71] = {
@@ -1038,7 +917,7 @@ int ASound4::command7() { return ASound::command7(); }
 int ASound4::command8() { return ASound::command8(); }
 
 // ---------------------------------------------------------------------------
-// command16 (sub_11CD3) – isSoundActive guard, command1, load ch0–6
+// command16 – isSoundActive guard, command1, load ch0–6
 // ---------------------------------------------------------------------------
 int ASound4::command16() {
 	byte *pData = loadData(0x0C36, 63);
@@ -1056,13 +935,12 @@ int ASound4::command16() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 24–27 (asound_commands3) – upper pool (sub_1039C)
+// commands 24–27 (asound_commands3) – upper pool
 //
-// commands 24 and 25 are both wired to the same handler (sub_11D0A),
+// commands 24 and 25 are both wired to the same handler,
 // which loads two upper-pool sounds.
 // ---------------------------------------------------------------------------
 
-// sub_11D0A  (shared by both command24 and command25)
 int ASound4::command24() {
 	playSound(0x0FFA, 18);
 	playSound(0x100C, 11);
@@ -1073,13 +951,11 @@ int ASound4::command25() {
 	return command24();
 }
 
-// sub_11D78
 int ASound4::command26() {
 	playSound(0x119D, 12);
 	return 0;
 }
 
-// sub_11D7F
 int ASound4::command27() {
 	playSound(0x11A9, 121);
 	return 0;
@@ -1087,50 +963,43 @@ int ASound4::command27() {
 
 // ---------------------------------------------------------------------------
 // commands 64–70 (asound_commands4, base 0x40)
-// All entries use the upper pool (sub_1039C).
+// All entries use the upper pool
 // ---------------------------------------------------------------------------
 
-// sub_11D16
 int ASound4::command64() {
 	playSound(0x1017, 26);
 	playSound(0x1031, 17);
 	return 0;
 }
 
-// sub_11D22
 int ASound4::command65() {
 	playSound(0x1042,  9);
 	playSound(0x104B, 20);
 	return 0;
 }
 
-// sub_11D2E
 int ASound4::command66() {
 	playSound(0x105F,  9);
 	playSound(0x1068, 16);
 	return 0;
 }
 
-// sub_11D3A
 int ASound4::command67() {
 	playSound(0x1078,  9);
 	playSound(0x1081, 14);
 	return 0;
 }
 
-// sub_11D46
 int ASound4::command68() {
 	playSound(0x108F, 12);
 	return 0;
 }
 
-// sub_11D4C
 int ASound4::command69() {
 	playSound(0x109B, 10);
 	return 0;
 }
 
-// sub_11D52
 int ASound4::command70() {
 	playSound(0x10A5,  3);
 	playSound(0x10A8, 58);
@@ -1141,31 +1010,6 @@ int ASound4::command70() {
 
 /*-----------------------------------------------------------------------*/
 /* ASound5  (asound.ph5)                                                  *
- *                                                                        *
- * Dispatch table layout:                                                 *
- *   asound_commands1: commands  0–8   (max=8,    base=0)                *
- *   asound_commands2: command   16    (max=0x10, base=0x10, 1 entry)    *
- *   asound_commands3: commands 24–27  (max=0x1B, base=0x18, 4 entries)  *
- *   asound_commands4: commands 32–39  (max=0x27, base=0x20, 8 entries)  *
- *   asound_commands5: commands 64–78  (max=0x4E, base=0x40, 15 entries) *
- *     (entry 15, command 79 = nullsub_8, silently ignored)              *
- *                                                                        *
- * Channel-load helpers:                                                  *
- *   sub_103FF=ch0  sub_10404=ch1  sub_10409=ch2  sub_1040E=ch3          *
- *   sub_10413=ch4  sub_10418=ch5  loc_1041D=ch6  loc_10422=ch7          *
- *   loc_10427=ch8                                                        *
- *                                                                        *
- * In this driver IDA named the lower-pool loadAny as AdlibChannel_loadAny*
- * (starts from ch0) and sub_1039C as the upper-pool loader (ch6-8).     *
- *   AdlibChannel_loadAny -> findFreeChannel(pData, 0)                      *
- *   sub_1039C            -> playSound() / findFreeChannel(pData, MIDWAY)   *
- *                                                                        *
- * loc_11D42 (cmd37) and loc_11D72 (cmd36) load channels in non-         *
- * sequential order — the sound data for each channel is not stored       *
- * contiguously. Block sizes are derived from the full sorted data map.   *
- *                                                                        *
- * commands 70/77/78 all reference the same data block at 0x40BA.        *
- * A dead sub (sub_11F09) and a dead random-pitch stub are ignored.       *
  *-----------------------------------------------------------------------*/
 
 const ASound5::CommandPtr ASound5::_commandList[79] = {
@@ -1229,7 +1073,7 @@ int ASound5::command7() { return ASound::command7(); }
 int ASound5::command8() { return ASound::command8(); }
 
 // ---------------------------------------------------------------------------
-// command16 (sub_11E84) – isSoundActive guard, command1, load ch0–5
+// command16 – isSoundActive guard, command1, load ch0–5
 // ---------------------------------------------------------------------------
 int ASound5::command16() {
 	byte *pData = loadData(0x4142, 120);
@@ -1246,30 +1090,27 @@ int ASound5::command16() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 24–27 (asound_commands3) – upper pool (sub_1039C)
+// commands 24–27 (asound_commands3) – upper pool
 // ---------------------------------------------------------------------------
 
-// sub_11EE8
 int ASound5::command24() {
 	playSound(0x51FA, 51);
 	playSound(0x522D, 46);
 	return 0;
 }
 
-// sub_11EF5
 int ASound5::command25() {
 	playSound(0x525B, 44);
 	playSound(0x5287, 46);
 	return 0;
 }
 
-// sub_11F02
 int ASound5::command26() {
 	playSound(0x52B5, 12);
 	return 0;
 }
 
-// sub_11E71 – three upper-pool voices
+// three upper-pool voices
 int ASound5::command27() {
 	playSound(0x4040, 10);
 	playSound(0x404A, 23);
@@ -1281,7 +1122,7 @@ int ASound5::command27() {
 // commands 32–39 (asound_commands4)
 // ---------------------------------------------------------------------------
 
-// sub_11EB4 – command1, eight loadAny (lower pool, AdlibChannel_loadAny)
+// command1, eight loadAny (lower pool, AdlibChannel_loadAny)
 int ASound5::command32() {
 	ASound::command1();
 	findFreeChannel(loadData(0x43BC, 689));
@@ -1295,7 +1136,7 @@ int ASound5::command32() {
 	return 0;
 }
 
-// sub_11DA2 – isSoundActive guard, command1, load ch0–6
+// isSoundActive guard, command1, load ch0–6
 int ASound5::command33() {
 	byte *pData = loadData(0x21C6, 609);
 	if (!isSoundActive(pData)) {
@@ -1311,7 +1152,7 @@ int ASound5::command33() {
 	return 0;
 }
 
-// sub_11DD8 – isSoundActive guard, command1, load ch0–5 (non-sequential)
+// isSoundActive guard, command1, load ch0–5 (non-sequential)
 // ch1 and ch3 use data from the 0x4000 region; ch0/ch2/ch4 from 0x2E9E region
 int ASound5::command34() {
 	byte *pData = loadData(0x2E9E, 1521);
@@ -1327,7 +1168,7 @@ int ASound5::command34() {
 	return 0;
 }
 
-// loc_11D72 – isSoundActive guard, command1, load ch0–5 (non-sequential)
+// isSoundActive guard, command1, load ch0–5 (non-sequential)
 // ch1/ch3/ch5 use data from the 0x2196 region interleaved with ch0/ch2/ch4
 int ASound5::command35() {
 	byte *pData = loadData(0x1D0A, 320);
@@ -1343,7 +1184,7 @@ int ASound5::command35() {
 	return 0;
 }
 
-// loc_11D42 – isSoundActive guard, command1, load ch0–5 (non-sequential)
+// isSoundActive guard, command1, load ch0–5 (non-sequential)
 // ch4 and ch5 reuse blocks at 0x15EC and 0x18D9 that precede ch0's data
 int ASound5::command36() {
 	byte *pData = loadData(0x15F5, 740);
@@ -1359,7 +1200,7 @@ int ASound5::command36() {
 	return 0;
 }
 
-// loc_11D00 – isSoundActive guard, command1, load ch0–8
+// isSoundActive guard, command1, load ch0–8
 int ASound5::command37() {
 	byte *pData = loadData(0x1190, 397);
 	if (!isSoundActive(pData)) {
@@ -1377,7 +1218,7 @@ int ASound5::command37() {
 	return 0;
 }
 
-// loc_11CD0 – isSoundActive guard, command2 (lower-bank fade), load ch0–5
+// isSoundActive guard, command2 (lower-bank fade), load ch0–5
 int ASound5::command38() {
 	byte *pData = loadData(0x0C36, 329);
 	if (!isSoundActive(pData)) {
@@ -1392,7 +1233,7 @@ int ASound5::command38() {
 	return 0;
 }
 
-// sub_11F10 – isSoundActive guard, command3 (lower-bank fade only), load ch0–5
+// isSoundActive guard, command3 (lower-bank fade only), load ch0–5
 int ASound5::command39() {
 	byte *pData = loadData(0x5312, 599);
 	if (!isSoundActive(pData)) {
@@ -1408,94 +1249,82 @@ int ASound5::command39() {
 }
 
 // ---------------------------------------------------------------------------
-// commands 64–78 (asound_commands5) – upper pool (sub_1039C) unless noted
+// commands 64–78 (asound_commands5) – upper pool unless noted
 // ---------------------------------------------------------------------------
 
-// sub_11E08
 int ASound5::command64() {
 	playSound(0x4101, 10);
 	return 0;
 }
 
-// sub_11E0E
 int ASound5::command65() {
 	playSound(0x401A, 18);
 	return 0;
 }
 
-// sub_11E14
 int ASound5::command66() {
 	playSound(0x402C, 10);
 	return 0;
 }
 
-// sub_11E1A
 int ASound5::command67() {
 	playSound(0x4036, 10);
 	return 0;
 }
 
-// sub_11E21
 int ASound5::command68() {
 	playSound(0x407A, 18);
 	return 0;
 }
 
-// sub_11E27
 int ASound5::command69() {
 	playSound(0x408C, 46);
 	return 0;
 }
 
-// sub_11E2D – also shared by command77 and command78
+// also shared by command77 and command78
 int ASound5::command70() {
 	playSound(0x40BA, 14);
 	return 0;
 }
 
-// sub_11E33
 int ASound5::command71() {
 	playSound(0x40C8, 10);
 	return 0;
 }
 
-// sub_11E39
 int ASound5::command72() {
 	playSound(0x40D2, 10);
 	return 0;
 }
 
-// sub_11E3F
 int ASound5::command73() {
 	playSound(0x40DC, 11);
 	playSound(0x40E7, 26);
 	return 0;
 }
 
-// sub_11E4C
 int ASound5::command74() {
 	playSound(0x410B, 20);
 	return 0;
 }
 
-// sub_11E52
 int ASound5::command75() {
 	playSound(0x4129, 11);
 	playSound(0x4134, 14);
 	return 0;
 }
 
-// sub_11E5F – same block as command70
+// same block as command70
 int ASound5::command76() {
 	return command70();
 }
 
-// sub_11E65 – same block as command70
+// same block as command70
 int ASound5::command77() {
 	return command70();
 }
 
-// sub_11E6B
 int ASound5::command78() {
 	playSound(0x411F, 10);
 	return 0;


Commit: 4afbf280c111bc07d20c4549f504b8041873484b
    https://github.com/scummvm/scummvm/commit/4afbf280c111bc07d20c4549f504b8041873484b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:46+10:00

Commit Message:
MADS: NEBULAR: Shift ASound data caching to Nebular ASound

This was a cache of sound data offsets/sizes loaded for playing,
and is needed by the Nebular version of ASound since it sets up
data block end pointers for validation. Since the Phantom
version doesn't need this, I've shifted the relevant structure
and code to be Nebular specific

Changed paths:
    engines/mads/core/sound_manager.cpp
    engines/mads/core/sound_manager.h
    engines/mads/madsv2/core/asound.h
    engines/mads/madsv2/phantom/sound_phantom.cpp
    engines/mads/nebular/core/asound.cpp
    engines/mads/nebular/core/asound.h


diff --git a/engines/mads/core/sound_manager.cpp b/engines/mads/core/sound_manager.cpp
index 8028b932cbb..55ca90b6b06 100644
--- a/engines/mads/core/sound_manager.cpp
+++ b/engines/mads/core/sound_manager.cpp
@@ -141,30 +141,4 @@ SoundDriver::~SoundDriver() {
 	_opl->stop();
 }
 
-byte *SoundDriver::loadData(int offset, int size) {
-	byte *ptr = &_soundData[offset];
-
-	// Check for an existing cache entry
-	uint idx;
-	for (idx = 0; idx < _dataCache.size() && _dataCache[idx]._dataStart != ptr; ++idx) {
-	}
-
-	if (idx == _dataCache.size())
-		_dataCache.push_back(CachedDataEntry(ptr, size));
-
-	// Return the data pointer
-	return ptr;
-}
-
-SoundDriver::CachedDataEntry &SoundDriver::getCachedData(byte *pData) {
-	Common::Array<CachedDataEntry>::iterator i;
-	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
-		CachedDataEntry &e = *i;
-		if (e._dataStart == pData)
-			return e;
-	}
-
-	error("Could not find previously loaded data");
-}
-
 } // namespace MADS
diff --git a/engines/mads/core/sound_manager.h b/engines/mads/core/sound_manager.h
index 25bc5471118..9de6a45c456 100644
--- a/engines/mads/core/sound_manager.h
+++ b/engines/mads/core/sound_manager.h
@@ -40,30 +40,12 @@ namespace MADS {
 #define CALLBACKS_PER_SECOND 60
 
 class SoundDriver {
-protected:
-	struct CachedDataEntry {
-		byte *_dataStart = nullptr;
-		byte *_dataEnd = nullptr;
-		CachedDataEntry(byte *dataStart, size_t size) : _dataStart(dataStart),
-			_dataEnd(dataStart + size - 1) {
-		}
-	};
-
-private:
-	Common::Array<CachedDataEntry> _dataCache;
-
 protected:
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
 	Common::Array<byte> _soundData;
 	Common::Mutex _driverMutex;
 
-	/**
-	 * Returns data for the specified offset. It also caches the data size for that
-	 * offset, for any future references that need it.
-	 */
-	byte *loadData(int offset, int size);
-
 	/**
 	 * Gets a stream starting at a given offset in the loaded sound data
 	 */
@@ -71,20 +53,11 @@ protected:
 		return Common::MemoryReadStream(&_soundData[offset], _soundData.size() - offset);
 	}
 
-	int getDataOffset(byte *ptr) const {
-		return ptr - &_soundData[0];
-	}
-
 public:
 	SoundDriver(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
 		int dataOffset, int dataSize);
 	virtual ~SoundDriver();
 
-	/**
-	 * Return the cached data block record for previously loaded sound data
-	 */
-	CachedDataEntry &getCachedData(byte *pData);
-
 	/**
 	 * Execute a player command. Most commands represent sounds to play, but some
 	 * low number commands also provide control operations.
diff --git a/engines/mads/madsv2/core/asound.h b/engines/mads/madsv2/core/asound.h
index 719829f280d..23194d6fb88 100644
--- a/engines/mads/madsv2/core/asound.h
+++ b/engines/mads/madsv2/core/asound.h
@@ -342,6 +342,13 @@ protected:
 	 */
 	void findFreeChannelFull(byte *soundData);
 
+	/**
+	 * Returns data for the specified offset.
+	 */
+	byte *loadData(int offset, int /*size*/) {
+		return &_soundData[offset];
+	}
+
 protected:
 	/**
 	 * Silences every voice and mutes the hardware:
diff --git a/engines/mads/madsv2/phantom/sound_phantom.cpp b/engines/mads/madsv2/phantom/sound_phantom.cpp
index 231c252316b..f08bcc548d4 100644
--- a/engines/mads/madsv2/phantom/sound_phantom.cpp
+++ b/engines/mads/madsv2/phantom/sound_phantom.cpp
@@ -182,7 +182,9 @@ int ASound1::commandMusic3() {
 // ---------------------------------------------------------------------------
 int ASound1::command16() {
 	if (_channels[0]->_activeCount) {
-		int f = getDataOffset(_channels[0]->_loopStartPtr);
+		// Special offset checks
+		int f = _channels[0]->_loopStartPtr - &_soundData[0];
+
 		if (f == 0 || f == 0x1ECA || f == 0x21C4 ||
 		    f == 0x3418 || f == 0x3688 || f == 0x3D52)
 			return 0;
diff --git a/engines/mads/nebular/core/asound.cpp b/engines/mads/nebular/core/asound.cpp
index 441ee296bf5..d3eb0704b44 100644
--- a/engines/mads/nebular/core/asound.cpp
+++ b/engines/mads/nebular/core/asound.cpp
@@ -213,6 +213,33 @@ void ASound::adlibInit() {
 	write(4, 0x80);
 }
 
+
+byte *ASound::loadData(int offset, int size) {
+	byte *ptr = &_soundData[offset];
+
+	// Check for an existing cache entry
+	uint idx;
+	for (idx = 0; idx < _dataCache.size() && _dataCache[idx]._dataStart != ptr; ++idx) {
+	}
+
+	if (idx == _dataCache.size())
+		_dataCache.push_back(CachedDataEntry(ptr, size));
+
+	// Return the data pointer
+	return ptr;
+}
+
+ASound::CachedDataEntry &ASound::getCachedData(byte *pData) {
+	Common::Array<CachedDataEntry>::iterator i;
+	for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
+		CachedDataEntry &e = *i;
+		if (e._dataStart == pData)
+			return e;
+	}
+
+	error("Could not find previously loaded data");
+}
+
 int ASound::stop() {
 	command0();
 	int result = _pollResult;
diff --git a/engines/mads/nebular/core/asound.h b/engines/mads/nebular/core/asound.h
index ec0b1b58d4b..1ad9a5aed26 100644
--- a/engines/mads/nebular/core/asound.h
+++ b/engines/mads/nebular/core/asound.h
@@ -134,6 +134,18 @@ struct RegisterValue {
  * Base class for the sound player resource files
  */
 class ASound : public SoundDriver {
+protected:
+	struct CachedDataEntry {
+		byte *_dataStart = nullptr;
+		byte *_dataEnd = nullptr;
+		CachedDataEntry(byte *dataStart, size_t size) : _dataStart(dataStart),
+			_dataEnd(dataStart + size - 1) {
+		}
+	};
+
+private:
+	Common::Array<CachedDataEntry> _dataCache;
+
 private:
 	uint16 _randomSeed;
 	int _masterVolume;
@@ -189,6 +201,16 @@ protected:
 
 	virtual void channelCommand(byte *&pSrc, bool &updateFlag) = 0;
 
+	/**
+	 * Returns data for the specified offset. It also caches the data size for that
+	 * offset, for any future references that need it.
+	 */
+	byte *loadData(int offset, int size);
+
+	int getDataOffset(byte *ptr) const {
+		return ptr - &_soundData[0];
+	}
+
 	/**
 	 * Loads up the specified sample
 	 */
@@ -324,6 +346,11 @@ public:
 	 */
 	static void validate();
 
+	/**
+	 * Return the cached data block record for previously loaded sound data
+	 */
+	CachedDataEntry &getCachedData(byte *pData);
+
 	/**
 	 * Stop all currently playing sounds
 	 */


Commit: b250841d0b88d710dab45c45aac63e3e3b8e49d9
    https://github.com/scummvm/scummvm/commit/b250841d0b88d710dab45c45aac63e3e3b8e49d9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:47+10:00

Commit Message:
MADS: PHANTOM: Fix loader_read in anim_load

Changed paths:
    engines/mads/madsv2/core/anim.cpp


diff --git a/engines/mads/madsv2/core/anim.cpp b/engines/mads/madsv2/core/anim.cpp
index 9b82127c2f7..5d21d50f301 100644
--- a/engines/mads/madsv2/core/anim.cpp
+++ b/engines/mads/madsv2/core/anim.cpp
@@ -241,7 +241,7 @@ AnimPtr anim_load(const char *file_name, Buffer *orig, Buffer *depth,
 	{
 		anim_error = 2;
 		byte buffer[AnimFile::SIZE];
-		if (!loader_read(buffer, sizeof(AnimFile), 1, &load_handle))
+		if (!loader_read(buffer, AnimFile::SIZE, 1, &load_handle))
 			goto done;
 
 		Common::MemoryReadStream src(buffer, AnimFile::SIZE);


Commit: 006059de5273fbfdb9e4fcc1b3bfb54fa3796b15
    https://github.com/scummvm/scummvm/commit/006059de5273fbfdb9e4fcc1b3bfb54fa3796b15
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:47+10:00

Commit Message:
MADS: PHANTOM: Beginnings of animview

Changed paths:
  A engines/mads/madsv2/animview/animview.cpp
  A engines/mads/madsv2/animview/animview.h
    engines/mads/madsv2/core/mouse.cpp
    engines/mads/madsv2/core/mouse.h
    engines/mads/madsv2/core/tile.cpp
    engines/mads/madsv2/core/tile.h
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/animview/animview.cpp b/engines/mads/madsv2/animview/animview.cpp
new file mode 100644
index 00000000000..6f817eb634f
--- /dev/null
+++ b/engines/mads/madsv2/animview/animview.cpp
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/file.h"
+#include "mads/madsv2/core/cycle.h"
+#include "mads/madsv2/core/env.h"
+#include "mads/madsv2/core/himem.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mcga.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/pack.h"
+#include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/tile.h"
+#include "mads/madsv2/core/timer.h"
+#include "mads/madsv2/animview/animview.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace AnimView {
+
+struct AnimEntry {
+	char name[16];
+	uint8 bg_load_status;
+	uint8 sound_mode;
+	uint8 show_bars;
+};
+constexpr int MAX_ANIM = 40;
+
+static bool has_anim;
+static int anim_count;
+static AnimEntry anim_list[MAX_ANIM];
+static uint8 background_load_status;
+static int16 sound_interrupts_mode;
+static bool show_white_bars;
+constexpr bool in_mads_mode = true;
+static int concat_mode;
+
+/**
+ * Adds an animation to the list of .aa files to show in sequence
+ * @param name		Animation resource name
+ */
+static void add_anim(const char *name) {
+	static char buf[16];
+
+	if (strlen(name) > 0 && anim_count < MAX_ANIM) {
+		has_anim = true;
+
+		Common::strcpy_s(buf, name);
+		if (!strchr(buf, '.'))
+			Common::strcat_s(buf, ".aa");
+
+		Common::strcpy_s(anim_list[anim_count].name, buf);
+		anim_list[anim_count].bg_load_status = background_load_status;
+		anim_list[anim_count].sound_mode = sound_interrupts_mode;
+		anim_list[anim_count].show_bars = show_white_bars;
+		++anim_count;
+	}
+}
+
+static void init_globals() {
+	has_anim = false;
+	anim_count = 0;
+	background_load_status = 0xff;
+	sound_interrupts_mode = -1;
+	show_white_bars = true;
+	concat_mode = 0;
+}
+
+static void perform() {
+	char buf[80], speech_name[80];
+	AnimFile anim_in;
+
+	himem_startup();
+	(void)tile_setup();
+
+	mcga_compute_retrace_parameters();
+	memset(cycling_palette, 0, sizeof(Palette));
+	pal_init(1, 8);
+	mouse_hard_cursor_mode(2, master_palette);
+
+	timer_install();
+	matte_init(-1);
+
+	for (int count = 0; count < anim_count; ++count) {
+		*buf = '\0';
+		if (in_mads_mode)
+			Common::strcpy_s(buf, "*");
+		Common::strcat_s(buf, anim_list[count].name);
+
+		himem_preload_series(buf, 0);
+
+		if (anim_get_header_info(buf, &anim_in))
+			continue;
+		
+		// TODO: More stuff
+	}
+}
+
+void animview_main(const char *resName) {
+	char name[16];
+
+	init_globals();
+
+	pack_enable_pfab_explode();
+	(void)env_verify();
+
+	Common::strcpy_s(name, resName);
+	if (!Common::File::exists(name)) {
+		Common::strcpy_s(name, "*");
+		Common::strcat_s(name, resName);
+	}
+
+	Common::SeekableReadStream *file = env_open(name);
+	if (!file)
+		error("Could not open animview resource - %s", name);
+
+	perform();
+}
+
+} // namespace AnimView
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/animview/animview.h b/engines/mads/madsv2/animview/animview.h
new file mode 100644
index 00000000000..c845c148bc8
--- /dev/null
+++ b/engines/mads/madsv2/animview/animview.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADSV2_ANIMVIEW_H
+#define MADSV2_ANIMVIEW_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace AnimView {
+
+
+extern void animview_main(const char *resName);
+
+} // namespace AnimView
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/core/mouse.cpp b/engines/mads/madsv2/core/mouse.cpp
index e8d4db27068..e8cb0d6bf0f 100644
--- a/engines/mads/madsv2/core/mouse.cpp
+++ b/engines/mads/madsv2/core/mouse.cpp
@@ -240,6 +240,9 @@ const byte *mouse_get_stack() {
 	error("TODO: mouse_get_stack");
 }
 
+void mouse_hard_cursor_mode(int mode, Palette mypal) {
+}
+
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/core/mouse.h b/engines/mads/madsv2/core/mouse.h
index e44f01dfcde..59a3a97e298 100644
--- a/engines/mads/madsv2/core/mouse.h
+++ b/engines/mads/madsv2/core/mouse.h
@@ -110,6 +110,7 @@ extern void mouse_cursor_sprite(SeriesPtr series, int id);
 extern void mouse_video_init();
 extern void mouse_video_update(int from_x, int from_y,
 	int unto_x, int unto_y, int size_x, int size_y);
+extern void mouse_hard_cursor_mode(int mode, Palette mypal);
 
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/tile.cpp b/engines/mads/madsv2/core/tile.cpp
index 7eba68d1ea4..fee1a049521 100644
--- a/engines/mads/madsv2/core/tile.cpp
+++ b/engines/mads/madsv2/core/tile.cpp
@@ -28,6 +28,7 @@
 #include "mads/madsv2/core/mem.h"
 #include "mads/madsv2/core/pack.h"
 #include "mads/madsv2/core/color.h"
+#include "mads/madsv2/core/ems.h"
 #include "mads/madsv2/core/env.h"
 #include "mads/madsv2/core/room.h"
 #include "mads/madsv2/core/pal.h"
@@ -43,7 +44,11 @@ namespace MADSV2 {
 
 int tile_load_error;
 ShadowList tile_shadow;
-
+static bool tile_ems_available = false;
+#if 0
+static int tile_picture_handle;
+static int tile_attribute_handle;
+#endif
 
 void TileMapHeader::load(Common::SeekableReadStream *src) {
 	src->readMultipleLE(tile_type, one_to_one, num_x_tiles, num_y_tiles,
@@ -63,6 +68,28 @@ void TileResource::load(Common::SeekableReadStream *src) {
 		num_pages, tiles_per_page, chunk_size, color_handle);
 }
 
+int tile_setup() {
+	int error_flag = true;
+
+	picture_map.map = NULL;
+	depth_map.map = NULL;
+#if 0
+	tile_picture_handle = ems_get_page_handle(TILE_MAX_PAGES);
+	if (tile_picture_handle < 0) goto done;
+
+	tile_attribute_handle = ems_get_page_handle(TILE_MAX_PAGES >> 1);
+	if (tile_attribute_handle < 0) goto done;
+
+	tile_ems_available = true;
+	error_flag = false;
+
+done:
+	if (error_flag) {
+		if (tile_picture_handle >= 0) ems_free_page_handle(tile_picture_handle);
+	}
+#endif
+	return error_flag;
+}
 
 int tile_load(const char *base, int tile_type, TileResource *tile_resource,
 		TileMapHeader *map, Buffer *picture, ColorListPtr color_list,
diff --git a/engines/mads/madsv2/core/tile.h b/engines/mads/madsv2/core/tile.h
index d9d2730777b..01ae99e4bf0 100644
--- a/engines/mads/madsv2/core/tile.h
+++ b/engines/mads/madsv2/core/tile.h
@@ -98,7 +98,7 @@ struct TileMapHeader {
 extern ShadowList tile_shadow;
 extern int tile_load_error;
 
-
+extern int tile_setup();
 extern int tile_load(const char *base, int tile_type, TileResource *tile_resource,
 	TileMapHeader *map, Buffer *picture, ColorListPtr color_list,
 	CycleListPtr cycle_list, int ems_handle, int load_flags);
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index b8ee8c05a95..91611e6af6a 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -57,6 +57,7 @@ ifdef ENABLE_MADSV2
 MODULE_OBJS += \
 	madsv2/console.o \
 	madsv2/engine.o \
+	madsv2/animview/animview.o \
 	madsv2/core/anim.o \
 	madsv2/core/asound.o \
 	madsv2/core/attr.o \


Commit: 9474e9da6929084aee4cc836bf8a546e615eac6f
    https://github.com/scummvm/scummvm/commit/9474e9da6929084aee4cc836bf8a546e615eac6f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:47+10:00

Commit Message:
MADS: PHANTOM: Loop for reading res file lines

Changed paths:
    engines/mads/madsv2/animview/animview.cpp
    engines/mads/madsv2/phantom/main.cpp


diff --git a/engines/mads/madsv2/animview/animview.cpp b/engines/mads/madsv2/animview/animview.cpp
index 6f817eb634f..487b44e842b 100644
--- a/engines/mads/madsv2/animview/animview.cpp
+++ b/engines/mads/madsv2/animview/animview.cpp
@@ -44,7 +44,6 @@ struct AnimEntry {
 };
 constexpr int MAX_ANIM = 40;
 
-static bool has_anim;
 static int anim_count;
 static AnimEntry anim_list[MAX_ANIM];
 static uint8 background_load_status;
@@ -61,8 +60,6 @@ static void add_anim(const char *name) {
 	static char buf[16];
 
 	if (strlen(name) > 0 && anim_count < MAX_ANIM) {
-		has_anim = true;
-
 		Common::strcpy_s(buf, name);
 		if (!strchr(buf, '.'))
 			Common::strcat_s(buf, ".aa");
@@ -75,8 +72,10 @@ static void add_anim(const char *name) {
 	}
 }
 
+/**
+ * Initializes animview global variables
+ */
 static void init_globals() {
-	has_anim = false;
 	anim_count = 0;
 	background_load_status = 0xff;
 	sound_interrupts_mode = -1;
@@ -84,8 +83,54 @@ static void init_globals() {
 	concat_mode = 0;
 }
 
+/**
+ * Parses a flag from an animation line in the resource file
+ */
+static void flag_parse(const char *param) {
+	// TODO
+}
+
+/**
+ * Reads the contents of the resource file stream, and adds
+ * entries to the anim_list for what to display
+ */
+static void read_resource(Common::SeekableReadStream *src) {
+	while (!src->eos()) {
+		Common::String line = src->readLine();
+		line.trim();
+		if (line.empty())
+			continue;
+
+		// Handle any flags at the start of the line
+		const char *lineP = line.c_str();
+		while (strchr("/-", *lineP)) {
+			// It's a flag
+			const char *switchEnd = strchr(lineP, ' ');
+			Common::String param;
+
+			if (switchEnd) {
+				// There's more line after the flag
+				param = Common::String(lineP, switchEnd);
+				for (lineP = switchEnd; *lineP == ' '; ++lineP) {
+				}
+			} else {
+				// This is the last flag of the line
+				param = Common::String(lineP);
+				lineP = lineP + strlen(lineP);
+			}
+
+			flag_parse(param.c_str());
+		}
+
+		// As long as we're not at the end of the line, any remainder
+		// should be the name of the animation resource to play
+		if (*lineP)
+			add_anim(lineP);
+	}
+}
+
 static void perform() {
-	char buf[80], speech_name[80];
+	char buf[80];// , speech_name[80];
 	AnimFile anim_in;
 
 	himem_startup();
@@ -132,6 +177,10 @@ void animview_main(const char *resName) {
 	if (!file)
 		error("Could not open animview resource - %s", name);
 
+	// Read in the resource lines
+	read_resource(file);
+	delete file;
+
 	perform();
 }
 
diff --git a/engines/mads/madsv2/phantom/main.cpp b/engines/mads/madsv2/phantom/main.cpp
index 62ea6463add..df2eb90be4b 100644
--- a/engines/mads/madsv2/phantom/main.cpp
+++ b/engines/mads/madsv2/phantom/main.cpp
@@ -21,6 +21,7 @@
 
 #include "common/config-manager.h"
 #include "mads/madsv2/phantom/main.h"
+#include "mads/madsv2/animview/animview.h"
 #include "mads/madsv2/core/env.h"
 #include "mads/madsv2/core/error.h"
 #include "mads/madsv2/core/fileio.h"
@@ -274,6 +275,12 @@ void phantom_main() {
 				game_main(2, CMD_LINE);
 				return;
 
+			case 3:
+				AnimView::animview_main("@phantom");
+				firstTime = true;
+				selected_item = 0;
+				break;
+
 			case 4:
 				// Exit
 				return;


Commit: 8553563b20f17d03412a7c6e85d2f73975c9024c
    https://github.com/scummvm/scummvm/commit/8553563b20f17d03412a7c6e85d2f73975c9024c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:47+10:00

Commit Message:
MADS: PHANTOM: Add resource flag parsing

Changed paths:
    engines/mads/madsv2/animview/animview.cpp


diff --git a/engines/mads/madsv2/animview/animview.cpp b/engines/mads/madsv2/animview/animview.cpp
index 487b44e842b..16e37d1769f 100644
--- a/engines/mads/madsv2/animview/animview.cpp
+++ b/engines/mads/madsv2/animview/animview.cpp
@@ -41,16 +41,33 @@ struct AnimEntry {
 	uint8 bg_load_status;
 	uint8 sound_mode;
 	uint8 show_bars;
+	uint8 fx;
 };
 constexpr int MAX_ANIM = 40;
+constexpr bool in_mads_mode = true;
 
 static int anim_count;
 static AnimEntry anim_list[MAX_ANIM];
 static uint8 background_load_status;
 static int16 sound_interrupts_mode;
 static bool show_white_bars;
-constexpr bool in_mads_mode = true;
 static int concat_mode;
+static bool resync_timer1, resync_timer2;
+static bool exit_immediately_at_end;
+
+/**
+ * Initializes animview global variables
+ */
+static void init_globals() {
+	anim_count = 0;
+	background_load_status = 0xff;
+	sound_interrupts_mode = -1;
+	show_white_bars = true;
+	concat_mode = 0;
+	resync_timer1 = true;
+	resync_timer2 = false;
+	exit_immediately_at_end = false;
+}
 
 /**
  * Adds an animation to the list of .aa files to show in sequence
@@ -72,22 +89,53 @@ static void add_anim(const char *name) {
 	}
 }
 
-/**
- * Initializes animview global variables
- */
-static void init_globals() {
-	anim_count = 0;
-	background_load_status = 0xff;
-	sound_interrupts_mode = -1;
-	show_white_bars = true;
-	concat_mode = 0;
-}
-
 /**
  * Parses a flag from an animation line in the resource file
  */
 static void flag_parse(const char *param) {
-	// TODO
+	switch (tolower(*param++)) {
+	case 'o':
+		// Specify opening special effect
+		assert(anim_count < MAX_ANIM);
+		if (*param == ':')
+			anim_list[anim_count].fx = atoi(param + 1);
+		break;
+
+	case 'r':
+		// -r[:abn] Resynch timer (always, beginning, never)
+		if (*param == ':') {
+			switch (tolower(*++param)) {
+			case 'n':
+				resync_timer1 = true;
+				resync_timer2 = false;
+				break;
+			case 'a':
+				resync_timer1 = false;
+				break;
+			case 'b':
+				resync_timer1 = true;
+				resync_timer2 = true;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+
+	case 'w':
+		// Toggle white bars on or off
+		show_white_bars = !show_white_bars;
+		break;
+
+	case 'x':
+		// Exit immediately after last frame
+		exit_immediately_at_end = true;
+		break;
+
+	default:
+		error("Unsupported animview flag - %c", *param);
+		break;
+	}
 }
 
 /**
@@ -105,6 +153,7 @@ static void read_resource(Common::SeekableReadStream *src) {
 		const char *lineP = line.c_str();
 		while (strchr("/-", *lineP)) {
 			// It's a flag
+			++lineP;
 			const char *switchEnd = strchr(lineP, ' ');
 			Common::String param;
 


Commit: 45c8c90bdd129d51ac5360ba064f8e27528f9367
    https://github.com/scummvm/scummvm/commit/45c8c90bdd129d51ac5360ba064f8e27528f9367
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-05T20:40:48+10:00

Commit Message:
MADS: PHANTOM: ConfMan option to quick start animview

Changed paths:
    engines/mads/madsv2/animview/animview.cpp
    engines/mads/madsv2/phantom/main.cpp


diff --git a/engines/mads/madsv2/animview/animview.cpp b/engines/mads/madsv2/animview/animview.cpp
index 16e37d1769f..02ea3e2120d 100644
--- a/engines/mads/madsv2/animview/animview.cpp
+++ b/engines/mads/madsv2/animview/animview.cpp
@@ -217,6 +217,11 @@ void animview_main(const char *resName) {
 	(void)env_verify();
 
 	Common::strcpy_s(name, resName);
+	if (*name == '@') {
+		Common::strcpy_s(name, resName + 1);
+		Common::strcat_s(name, ".res");
+	}
+
 	if (!Common::File::exists(name)) {
 		Common::strcpy_s(name, "*");
 		Common::strcat_s(name, resName);
diff --git a/engines/mads/madsv2/phantom/main.cpp b/engines/mads/madsv2/phantom/main.cpp
index df2eb90be4b..55de9ef4128 100644
--- a/engines/mads/madsv2/phantom/main.cpp
+++ b/engines/mads/madsv2/phantom/main.cpp
@@ -260,34 +260,34 @@ void phantom_main() {
 	if (!env_verify())
 		env_search_mode = ENV_SEARCH_CONCAT_FILES;
 
-	bool firstTime = !ConfMan.getBool("start_game") && !ConfMan.hasKey("save_slot");
-	selected_item = 0;
+	if (ConfMan.getBool("start_game") || ConfMan.hasKey("save_slot"))
+		selected_item = 0;
+	else if (ConfMan.getBool("start_intro"))
+		selected_item = 3;
+	else
+		selected_item = -1;
 
 	while (!g_engine->shouldQuit()) {
-		if (firstTime) {
+		switch (selected_item) {
+		case -1:
 			main_menu_main();
-			firstTime = false;
-		}
+			break;
 
-		if (!g_engine->shouldQuit()) {
-			switch (selected_item) {
-			case 0:
-				game_main(2, CMD_LINE);
-				return;
+		case 0:
+			game_main(2, CMD_LINE);
+			return;
 
-			case 3:
-				AnimView::animview_main("@phantom");
-				firstTime = true;
-				selected_item = 0;
-				break;
+		case 3:
+			AnimView::animview_main("@phantom");
+			selected_item = -1;
+			break;
 
-			case 4:
-				// Exit
-				return;
+		case 4:
+			// Exit
+			return;
 
-			default:
-				break;
-			}
+		default:
+			break;
 		}
 	}
 }




More information about the Scummvm-git-logs mailing list