summaryrefslogtreecommitdiff
path: root/opl
diff options
context:
space:
mode:
authorSimon Howard2015-05-30 13:04:56 -0400
committerSimon Howard2015-05-30 13:04:56 -0400
commit0e90c19ca717298f4f3b83dfd075cdf0c458de32 (patch)
tree09fae977fd34c03759d5c90e7daf2a087e6e7756 /opl
parent5082f14944442344030d66f6fbdf86a75a1c1c70 (diff)
parent1e516e34911d3a95479c303fe26b59f20e618252 (diff)
downloadchocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.tar.gz
chocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.tar.bz2
chocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.zip
Merge pull request #545 from khokh2001/opl3_mode
opl: Add OPL3 mode. The DMX library had limited support for the features of the OPL3 chip, enabled by setting the DMXOPTIONS variable. This reproduces the OPL3 support in Chocolate Doom's OPL playback and emulation layer. Huge thanks to Alexey Khokholov for researching and developing this. This fixes #470.
Diffstat (limited to 'opl')
-rw-r--r--opl/opl.c82
-rw-r--r--opl/opl.h6
-rw-r--r--opl/opl_sdl.c43
3 files changed, 108 insertions, 23 deletions
diff --git a/opl/opl.c b/opl/opl.c
index 2800f434..a6ad4a9d 100644
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -67,6 +67,8 @@ unsigned int opl_sample_rate = 22050;
static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
{
+ int result1, result2;
+
// Initialize the driver.
if (!_driver->init_func(port_base))
@@ -82,7 +84,9 @@ static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
driver = _driver;
init_stage_reg_writes = 1;
- if (!OPL_Detect() || !OPL_Detect())
+ result1 = OPL_Detect();
+ result2 = OPL_Detect();
+ if (!result1 || !result2)
{
printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
_driver->shutdown_func();
@@ -90,15 +94,11 @@ static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
return 0;
}
- // Initialize all registers.
-
- OPL_InitRegisters();
-
init_stage_reg_writes = 0;
printf("OPL_Init: Using driver '%s'.\n", driver->name);
- return 1;
+ return result2;
}
// Find a driver automatically by trying each in the list.
@@ -106,12 +106,14 @@ static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
static int AutoSelectDriver(unsigned int port_base)
{
int i;
+ int result;
for (i=0; drivers[i] != NULL; ++i)
{
- if (InitDriver(drivers[i], port_base))
+ result = InitDriver(drivers[i], port_base);
+ if (result)
{
- return 1;
+ return result;
}
}
@@ -127,6 +129,7 @@ int OPL_Init(unsigned int port_base)
{
char *driver_name;
int i;
+ int result;
driver_name = getenv("OPL_DRIVER");
@@ -138,9 +141,10 @@ int OPL_Init(unsigned int port_base)
{
if (!strcmp(driver_name, drivers[i]->name))
{
- if (InitDriver(drivers[i], port_base))
+ result = InitDriver(drivers[i], port_base);
+ if (result)
{
- return 1;
+ return result;
}
else
{
@@ -233,7 +237,14 @@ void OPL_WriteRegister(int reg, int value)
{
int i;
- OPL_WritePort(OPL_REGISTER_PORT, reg);
+ if (reg & 0x100)
+ {
+ OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg);
+ }
+ else
+ {
+ OPL_WritePort(OPL_REGISTER_PORT, reg);
+ }
// For timing, read the register port six times after writing the
// register number to cause the appropriate delay
@@ -306,13 +317,22 @@ int OPL_Detect(void)
// Enable interrupts:
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
- return (result1 & 0xe0) == 0x00
- && (result2 & 0xe0) == 0xc0;
+ if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0)
+ {
+ result1 = OPL_ReadPort(OPL_REGISTER_PORT);
+ result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3);
+ if (result1 == 0x00)
+ {
+ return 2;
+ }
+ return 1;
+ }
+ return 0;
}
// Initialize registers on startup
-void OPL_InitRegisters(void)
+void OPL_InitRegisters(int opl3)
{
int r;
@@ -349,8 +369,42 @@ void OPL_InitRegisters(void)
// "Allow FM chips to control the waveform of each operator":
OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
+ if (opl3)
+ {
+ OPL_WriteRegister(OPL_REG_NEW, 0x01);
+
+ // Initialize level registers
+
+ for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x3f);
+ }
+
+ // Initialize other registers
+ // These two loops write to registers that actually don't exist,
+ // but this is what Doom does ...
+ // Similarly, the <= is also intenational.
+
+ for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x00);
+ }
+
+ // More registers ...
+
+ for (r=1; r < OPL_REGS_LEVEL; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x00);
+ }
+ }
+
// Keyboard split point on (?)
OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
+
+ if (opl3)
+ {
+ OPL_WriteRegister(OPL_REG_NEW, 0x01);
+ }
}
//
diff --git a/opl/opl.h b/opl/opl.h
index c4b91b25..c4bc2c80 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -26,7 +26,8 @@ typedef void (*opl_callback_t)(void *data);
typedef enum
{
OPL_REGISTER_PORT = 0,
- OPL_DATA_PORT = 1
+ OPL_DATA_PORT = 1,
+ OPL_REGISTER_PORT_OPL3 = 2
} opl_port_t;
#define OPL_NUM_OPERATORS 21
@@ -37,6 +38,7 @@ typedef enum
#define OPL_REG_TIMER2 0x03
#define OPL_REG_TIMER_CTRL 0x04
#define OPL_REG_FM_MODE 0x08
+#define OPL_REG_NEW 0x105
// Operator registers (21 of each):
@@ -101,7 +103,7 @@ int OPL_Detect(void);
// Initialize all registers, performed on startup.
-void OPL_InitRegisters(void);
+void OPL_InitRegisters(int opl3);
//
// Timer callback functions.
diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c
index 8834ee07..1334ac07 100644
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -71,6 +71,7 @@ static uint64_t pause_offset;
// OPL software emulator structure.
static Chip opl_chip;
+static int opl_opl3mode;
// Temporary mixing buffer used by the mixing callback.
@@ -164,15 +165,30 @@ static void FillBuffer(int16_t *buffer, unsigned int nsamples)
assert(nsamples < mixing_freq);
- Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer);
+ if (opl_opl3mode)
+ {
+ Chip__GenerateBlock3(&opl_chip, nsamples, mix_buffer);
- // Mix into the destination buffer, doubling up into stereo.
+ // Mix into the destination buffer, doubling up into stereo.
- for (i=0; i<nsamples; ++i)
- {
- buffer[i * 2] = (int16_t) mix_buffer[i];
- buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
+ for (i=0; i<nsamples; ++i)
+ {
+ buffer[i * 2] = (int16_t) mix_buffer[i * 2];
+ buffer[i * 2 + 1] = (int16_t) mix_buffer[i * 2 + 1];
+ }
}
+ else
+ {
+ Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer);
+
+ // Mix into the destination buffer, doubling up into stereo.
+
+ for (i=0; i<nsamples; ++i)
+ {
+ buffer[i * 2] = (int16_t) mix_buffer[i];
+ buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
+ }
+ }
}
// Callback function to fill a new sound buffer:
@@ -351,13 +367,14 @@ static int OPL_SDL_Init(unsigned int port_base)
// Mix buffer:
- mix_buffer = malloc(mixing_freq * sizeof(uint32_t));
+ mix_buffer = malloc(mixing_freq * sizeof(uint32_t) * 2);
// Create the emulator structure:
DBOPL_InitTables();
Chip__Chip(&opl_chip);
Chip__Setup(&opl_chip, mixing_freq);
+ opl_opl3mode = 0;
callback_mutex = SDL_CreateMutex();
callback_queue_mutex = SDL_CreateMutex();
@@ -372,6 +389,11 @@ static unsigned int OPL_SDL_PortRead(opl_port_t port)
{
unsigned int result = 0;
+ if (port == OPL_REGISTER_PORT_OPL3)
+ {
+ return 0xff;
+ }
+
if (timer1.enabled && current_time > timer1.expire_time)
{
result |= 0x80; // Either have expired
@@ -439,6 +461,9 @@ static void WriteRegister(unsigned int reg_num, unsigned int value)
break;
+ case OPL_REG_NEW:
+ opl_opl3mode = value & 0x01;
+
default:
Chip__WriteReg(&opl_chip, reg_num, value);
break;
@@ -451,6 +476,10 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value)
{
register_num = value;
}
+ else if (port == OPL_REGISTER_PORT_OPL3)
+ {
+ register_num = value | 0x100;
+ }
else if (port == OPL_DATA_PORT)
{
WriteRegister(register_num, value);