summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Howard2009-09-26 23:52:41 +0000
committerSimon Howard2009-09-26 23:52:41 +0000
commitdce2c95f05b8f5ed734d1a1b75ccd7bfb2260557 (patch)
treec691b8315051dbd9ee59158d6d7d88886ab46c97
parent808b78153c3d3bfe1fb692106323731d17e49489 (diff)
downloadchocolate-doom-dce2c95f05b8f5ed734d1a1b75ccd7bfb2260557.tar.gz
chocolate-doom-dce2c95f05b8f5ed734d1a1b75ccd7bfb2260557.tar.bz2
chocolate-doom-dce2c95f05b8f5ed734d1a1b75ccd7bfb2260557.zip
Move register read/write code into OPL library. Detect OPL in the
library code, so that we fall back to software emulation if we have port access but an OPL is not detected. Fix detection of ioperm in configure. Subversion-branch: /branches/opl-branch Subversion-revision: 1692
-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;
}