shithub: choc

Download patch

ref: dce2c95f05b8f5ed734d1a1b75ccd7bfb2260557
parent: 808b78153c3d3bfe1fb692106323731d17e49489
author: Simon Howard <fraggle@gmail.com>
date: Sat Sep 26 19:52:41 EDT 2009

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

--- a/configure.in
+++ b/configure.in
@@ -70,7 +70,7 @@
     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.
 
--- a/opl/examples/droplay.c
+++ b/opl/examples/droplay.c
@@ -73,30 +73,6 @@
     }
 }
 
-// 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 @@
         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 @@
     }
 
     Init();
-    ClearAllRegs();
 
     PlayFile(argv[1]);
 
--- 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 *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.
 
-    // Try drivers until we find a working one:
+    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;
+    }
+
+    // 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)
@@ -114,6 +206,146 @@
         return 0;
     }
 }
+
+//
+// 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)
 {
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -58,8 +58,12 @@
 #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);
 
 // Shut down the OPL subsystem.
@@ -73,6 +77,31 @@
 // Read from one of the OPL I/O ports:
 
 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.
--- a/opl/opl_obsd.c
+++ b/opl/opl_obsd.c
@@ -101,5 +101,5 @@
     OPL_Timer_SetPaused
 };
 
-#endif /* #ifdef HAVE_IOPERM */
+#endif /* #ifdef HAVE_LIBI386 */
 
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -326,149 +326,11 @@
 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 @@
         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 @@
     // 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 @@
     {
         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 @@
 
         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 @@
 {
     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 @@
         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 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 @@
 
     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;
     }