summaryrefslogtreecommitdiff
path: root/opl
diff options
context:
space:
mode:
Diffstat (limited to 'opl')
-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
4 files changed, 269 insertions, 39 deletions
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 */