summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.in2
-rw-r--r--opl/examples/droplay.c31
-rw-r--r--opl/opl.c244
-rw-r--r--opl/opl.h31
-rw-r--r--opl/opl_obsd.c2
-rw-r--r--src/i_oplmusic.c178
6 files changed, 282 insertions, 206 deletions
diff --git a/configure.in b/configure.in
index 17ec9dc4..d8bad56e 100644
--- a/configure.in
+++ b/configure.in
@@ -70,7 +70,7 @@ AC_SDL_MAIN_WORKAROUND([
AC_CHECK_LIB(samplerate, src_new)
AC_CHECK_HEADERS([linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h])
- AC_CHECK_FUNCS(mmap sched_setaffinity)
+ AC_CHECK_FUNCS(mmap sched_setaffinity ioperm)
# OpenBSD I/O i386 library for I/O port access.
diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c
index d53a427b..36f5c3c0 100644
--- a/opl/examples/droplay.c
+++ b/opl/examples/droplay.c
@@ -73,30 +73,6 @@ void ClearAllRegs(void)
}
}
-// Detect an OPL chip.
-
-int DetectOPL(void)
-{
- int val1, val2;
-
- WriteReg(OPL_REG_TIMER_CTRL, 0x60);
- WriteReg(OPL_REG_TIMER_CTRL, 0x80);
-
- val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0;
-
- WriteReg(OPL_REG_TIMER1, 0xff);
- WriteReg(OPL_REG_TIMER_CTRL, 0x21);
-
- OPL_Delay(1);
-
- val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0;
-
- WriteReg(OPL_REG_TIMER_CTRL, 0x60);
- WriteReg(OPL_REG_TIMER_CTRL, 0x80);
-
- return val1 == 0 && val2 == 0xc0;
-}
-
void Init(void)
{
if (SDL_Init(SDL_INIT_TIMER) < 0)
@@ -110,12 +86,6 @@ void Init(void)
fprintf(stderr, "Unable to initialise OPL layer\n");
exit(-1);
}
-
- if (!DetectOPL())
- {
- fprintf(stderr, "Adlib not detected\n");
- exit(-1);
- }
}
void Shutdown(void)
@@ -236,7 +206,6 @@ int main(int argc, char *argv[])
}
Init();
- ClearAllRegs();
PlayFile(argv[1]);
diff --git a/opl/opl.c b/opl/opl.c
index f28cd63f..00c9b04a 100644
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -1,4 +1,4 @@
-// Emacs style mode select -*- C++ -*-
+// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 2009 Simon Howard
@@ -28,6 +28,10 @@
#include <stdio.h>
#include <stdlib.h>
+#ifdef _WIN32_WCE
+#include "libc_wince.h"
+#endif
+
#include "SDL.h"
#include "opl.h"
@@ -56,25 +60,113 @@ static opl_driver_t *drivers[] =
};
static opl_driver_t *driver = NULL;
+static int init_stage_reg_writes = 1;
-int OPL_Init(unsigned int port_base)
+//
+// Init/shutdown code.
+//
+
+// Initialize the specified driver and detect an OPL chip. Returns
+// true if an OPL is detected.
+
+static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
{
- int i;
+ // Initialize the driver.
+
+ if (!_driver->init_func(port_base))
+ {
+ return 0;
+ }
+
+ // The driver was initialized okay, so we now have somewhere
+ // to write to. It doesn't mean there's an OPL chip there,
+ // though. Perform the detection sequence to make sure.
+ // (it's done twice, like how Doom does it).
+
+ driver = _driver;
+ init_stage_reg_writes = 1;
+
+ if (!OPL_Detect() || !OPL_Detect())
+ {
+ printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
+ _driver->shutdown_func();
+ driver = NULL;
+ return 0;
+ }
- // Try drivers until we find a working one:
+ // Initialize all registers.
+
+ OPL_InitRegisters();
+
+ init_stage_reg_writes = 0;
+
+ printf("OPL_Init: Using driver '%s'.\n", driver->name);
+
+ return 1;
+}
+
+// Find a driver automatically by trying each in the list.
+
+static int AutoSelectDriver(unsigned int port_base)
+{
+ int i;
for (i=0; drivers[i] != NULL; ++i)
{
- if (drivers[i]->init_func(port_base))
+ if (InitDriver(drivers[i], port_base))
{
- driver = drivers[i];
return 1;
}
}
+ printf("OPL_Init: Failed to find a working driver.\n");
+
return 0;
}
+// Initialize the OPL library. Returns true if initialized
+// successfully.
+
+int OPL_Init(unsigned int port_base)
+{
+ char *driver_name;
+ int i;
+
+ driver_name = getenv("OPL_DRIVER");
+
+ if (driver_name != NULL)
+ {
+ // Search the list until we find the driver with this name.
+
+ for (i=0; drivers[i] != NULL; ++i)
+ {
+ if (!strcmp(driver_name, drivers[i]->name))
+ {
+ if (InitDriver(drivers[i], port_base))
+ {
+ return 1;
+ }
+ else
+ {
+ printf("OPL_Init: Failed to initialize "
+ "driver: '%s'.\n", driver_name);
+ return 0;
+ }
+ }
+ }
+
+ printf("OPL_Init: unknown driver: '%s'.\n", driver_name);
+
+ return 0;
+ }
+ else
+ {
+ return AutoSelectDriver(port_base);
+ }
+}
+
+// Shut down the OPL library.
+
void OPL_Shutdown(void)
{
if (driver != NULL)
@@ -115,6 +207,146 @@ unsigned int OPL_ReadPort(opl_port_t port)
}
}
+//
+// Higher-level functions, based on the lower-level functions above
+// (register write, etc).
+//
+
+unsigned int OPL_ReadStatus(void)
+{
+ return OPL_ReadPort(OPL_REGISTER_PORT);
+}
+
+// Write an OPL register value
+
+void OPL_WriteRegister(int reg, int value)
+{
+ int i;
+
+ OPL_WritePort(OPL_REGISTER_PORT, reg);
+
+ // For timing, read the register port six times after writing the
+ // register number to cause the appropriate delay
+
+ for (i=0; i<6; ++i)
+ {
+ // An oddity of the Doom OPL code: at startup initialisation,
+ // the spacing here is performed by reading from the register
+ // port; after initialisation, the data port is read, instead.
+
+ if (init_stage_reg_writes)
+ {
+ OPL_ReadPort(OPL_REGISTER_PORT);
+ }
+ else
+ {
+ OPL_ReadPort(OPL_DATA_PORT);
+ }
+ }
+
+ OPL_WritePort(OPL_DATA_PORT, value);
+
+ // Read the register port 24 times after writing the value to
+ // cause the appropriate delay
+
+ for (i=0; i<24; ++i)
+ {
+ OPL_ReadStatus();
+ }
+}
+
+// Detect the presence of an OPL chip
+
+int OPL_Detect(void)
+{
+ int result1, result2;
+ int i;
+
+ // Reset both timers:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+
+ // Enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ // Read status
+ result1 = OPL_ReadStatus();
+
+ // Set timer:
+ OPL_WriteRegister(OPL_REG_TIMER1, 0xff);
+
+ // Start timer 1:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21);
+
+ // Wait for 80 microseconds
+ // This is how Doom does it:
+
+ for (i=0; i<200; ++i)
+ {
+ OPL_ReadStatus();
+ }
+
+ OPL_Delay(1);
+
+ // Read status
+ result2 = OPL_ReadStatus();
+
+ // Reset both timers:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+
+ // Enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ return (result1 & 0xe0) == 0x00
+ && (result2 & 0xe0) == 0xc0;
+}
+
+// Initialize registers on startup
+
+void OPL_InitRegisters(void)
+{
+ int r;
+
+ // Initialize level registers
+
+ for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r, 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, 0x00);
+ }
+
+ // More registers ...
+
+ for (r=1; r < OPL_REGS_LEVEL; ++r)
+ {
+ OPL_WriteRegister(r, 0x00);
+ }
+
+ // Re-initialize the low registers:
+
+ // Reset both timers and enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ // "Allow FM chips to control the waveform of each operator":
+ OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
+
+ // Keyboard split point on (?)
+ OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
+}
+
+//
+// Timer functions.
+//
+
void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data)
{
if (driver != NULL)
diff --git a/opl/opl.h b/opl/opl.h
index f5b93a64..9f5d0a9f 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -58,7 +58,11 @@ typedef enum
#define OPL_REGS_FREQ_2 0xB0
#define OPL_REGS_FEEDBACK 0xC0
-// Initialise the OPL subsystem.
+//
+// Low-level functions.
+//
+
+// Initialize the OPL subsystem.
int OPL_Init(unsigned int port_base);
@@ -74,6 +78,31 @@ void OPL_WritePort(opl_port_t port, unsigned int value);
unsigned int OPL_ReadPort(opl_port_t port);
+//
+// Higher-level functions.
+//
+
+// Read the cuurrent status byte of the OPL chip.
+
+unsigned int OPL_ReadStatus(void);
+
+// Write to an OPL register.
+
+void OPL_WriteRegister(int reg, int value);
+
+// Perform a detection sequence to determine that an
+// OPL chip is present.
+
+int OPL_Detect(void);
+
+// Initialize all registers, performed on startup.
+
+void OPL_InitRegisters(void);
+
+//
+// Timer callback functions.
+//
+
// Set a timer callback. After the specified number of milliseconds
// have elapsed, the callback will be invoked.
diff --git a/opl/opl_obsd.c b/opl/opl_obsd.c
index a3a22ab8..82d9186d 100644
--- a/opl/opl_obsd.c
+++ b/opl/opl_obsd.c
@@ -101,5 +101,5 @@ opl_driver_t opl_openbsd_driver =
OPL_Timer_SetPaused
};
-#endif /* #ifdef HAVE_IOPERM */
+#endif /* #ifdef HAVE_LIBI386 */
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index dd62076d..e9e722e4 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -326,149 +326,11 @@ static unsigned int num_tracks;
static unsigned int running_tracks = 0;
static boolean song_looping;
-// In the initialisation stage, register writes are spaced by reading
-// from the register port (0). After initialisation, spacing is
-// peformed by reading from the data port instead. I have no idea
-// why.
-
-static boolean init_stage_reg_writes = false;
-
// Configuration file variable, containing the port number for the
// adlib chip.
int opl_io_port = 0x388;
-static unsigned int GetStatus(void)
-{
- return OPL_ReadPort(OPL_REGISTER_PORT);
-}
-
-// Write an OPL register value
-
-static void WriteRegister(int reg, int value)
-{
- int i;
-
- OPL_WritePort(OPL_REGISTER_PORT, reg);
-
- // For timing, read the register port six times after writing the
- // register number to cause the appropriate delay
-
- for (i=0; i<6; ++i)
- {
- // An oddity of the Doom OPL code: at startup initialisation,
- // the spacing here is performed by reading from the register
- // port; after initialisation, the data port is read, instead.
-
- if (init_stage_reg_writes)
- {
- OPL_ReadPort(OPL_REGISTER_PORT);
- }
- else
- {
- OPL_ReadPort(OPL_DATA_PORT);
- }
- }
-
- OPL_WritePort(OPL_DATA_PORT, value);
-
- // Read the register port 25 times after writing the value to
- // cause the appropriate delay
-
- for (i=0; i<24; ++i)
- {
- GetStatus();
- }
-}
-
-// Detect the presence of an OPL chip
-
-static boolean DetectOPL(void)
-{
- int result1, result2;
- int i;
-
- // Reset both timers:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
-
- // Enable interrupts:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
-
- // Read status
- result1 = GetStatus();
-
- // Set timer:
- WriteRegister(OPL_REG_TIMER1, 0xff);
-
- // Start timer 1:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x21);
-
- // Wait for 80 microseconds
- // This is how Doom does it:
-
- for (i=0; i<200; ++i)
- {
- GetStatus();
- }
-
- OPL_Delay(1);
-
- // Read status
- result2 = GetStatus();
-
- // Reset both timers:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
-
- // Enable interrupts:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
-
- return (result1 & 0xe0) == 0x00
- && (result2 & 0xe0) == 0xc0;
-}
-
-// Initialise registers on startup
-
-static void InitRegisters(void)
-{
- int r;
-
- // Initialise level registers
-
- for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
- {
- WriteRegister(r, 0x3f);
- }
-
- // Initialise 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)
- {
- WriteRegister(r, 0x00);
- }
-
- // More registers ...
-
- for (r=1; r < OPL_REGS_LEVEL; ++r)
- {
- WriteRegister(r, 0x00);
- }
-
- // Re-initialise the low registers:
-
- // Reset both timers and enable interrupts:
- WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
- WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
-
- // "Allow FM chips to control the waveform of each operator":
- WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
-
- // Keyboard split point on (?)
- WriteRegister(OPL_REG_FM_MODE, 0x40);
-}
-
// Load instrument table from GENMIDI lump:
static boolean LoadInstrumentTable(void)
@@ -584,11 +446,11 @@ static void LoadOperatorData(int operator, genmidi_op_t *data,
level |= 0x3f;
}
- WriteRegister(OPL_REGS_LEVEL + operator, level);
- WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
- WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
- WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain);
- WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform);
+ OPL_WriteRegister(OPL_REGS_LEVEL + operator, level);
+ OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
+ OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
+ OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain);
+ OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform);
}
// Set the instrument for a particular voice.
@@ -629,8 +491,8 @@ static void SetVoiceInstrument(opl_voice_t *voice,
// two operators. Turn on bits in the upper nybble; I think this
// is for OPL3, where it turns on channel A/B.
- WriteRegister(OPL_REGS_FEEDBACK + voice->index,
- data->feedback | 0x30);
+ OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index,
+ data->feedback | 0x30);
// Hack to force a volume update.
@@ -669,7 +531,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
{
voice->reg_volume = reg_volume;
- WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume);
+ OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume);
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
@@ -679,7 +541,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
if ((opl_voice->feedback & 0x01) != 0)
{
- WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume);
+ OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume);
}
}
}
@@ -731,17 +593,7 @@ static boolean I_OPL_InitMusic(void)
{
if (!OPL_Init(opl_io_port))
{
- return false;
- }
-
- init_stage_reg_writes = true;
-
- // Doom does the detection sequence twice, for some reason:
-
- if (!DetectOPL() || !DetectOPL())
- {
printf("Dude. The Adlib isn't responding.\n");
- OPL_Shutdown();
return false;
}
@@ -753,14 +605,8 @@ static boolean I_OPL_InitMusic(void)
return false;
}
- InitRegisters();
InitVoices();
- // Now that initialisation has finished, switch the
- // register writing mode:
-
- init_stage_reg_writes = false;
-
music_initialised = true;
return true;
@@ -789,7 +635,7 @@ static void I_OPL_SetMusicVolume(int volume)
static void VoiceKeyOff(opl_voice_t *voice)
{
- WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);
+ OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);
}
// Get the frequency that we should be using for a voice.
@@ -947,8 +793,8 @@ static void UpdateVoiceFrequency(opl_voice_t *voice)
if (voice->freq != freq)
{
- WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff);
- WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20);
+ OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff);
+ OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20);
voice->freq = freq;
}