Add OPL hardware playback support for Windows NT-based systems.
Subversion-branch: /branches/opl-branch Subversion-revision: 1871
This commit is contained in:
parent
cdacf59ace
commit
06b97d2d11
7 changed files with 358 additions and 24 deletions
1
OPL-TODO
1
OPL-TODO
|
|
@ -15,5 +15,6 @@ Bad MIDIs:
|
||||||
|
|
||||||
Other tasks:
|
Other tasks:
|
||||||
|
|
||||||
|
* Get a better software OPL emulator
|
||||||
* DMXOPTIONS opl3/phase option support.
|
* DMXOPTIONS opl3/phase option support.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,13 @@ stdout.txt:
|
||||||
|
|
||||||
If you're running an NT-based system, it is not possible to directly
|
If you're running an NT-based system, it is not possible to directly
|
||||||
access the OPL chip, even when running as Administrator. Fortunately,
|
access the OPL chip, even when running as Administrator. Fortunately,
|
||||||
it is possible to use the third-party "PortTalk" driver:
|
it is possible to use the "ioperm.sys" driver developed for Cygwin:
|
||||||
|
|
||||||
http://www.beyondlogic.org/porttalk/porttalk.htm
|
http://openwince.sourceforge.net/ioperm/
|
||||||
|
|
||||||
TODO - the NT driver hasn't actually been written yet..
|
It is not necessary to have Cygwin installed to use this. Copy the
|
||||||
|
ioperm.sys file into the same directory as the Chocolate Doom
|
||||||
|
executable and it should be automatically loaded.
|
||||||
|
|
||||||
=== Linux ===
|
=== Linux ===
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ libopl_a_SOURCES = \
|
||||||
opl_queue.c opl_queue.h \
|
opl_queue.c opl_queue.h \
|
||||||
opl_sdl.c \
|
opl_sdl.c \
|
||||||
opl_timer.c opl_timer.h \
|
opl_timer.c opl_timer.h \
|
||||||
opl_win9x.c \
|
opl_win32.c \
|
||||||
|
ioperm_sys.c ioperm_sys.h \
|
||||||
fmopl.c fmopl.h
|
fmopl.c fmopl.h
|
||||||
|
|
||||||
|
|
|
||||||
255
opl/ioperm_sys.c
Normal file
255
opl/ioperm_sys.c
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
// Emacs style mode select -*- C++ -*-
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2002, 2003 Marcel Telka
|
||||||
|
// Copyright(C) 2009 Simon Howard
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
// 02111-1307, USA.
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// Interface to the ioperm.sys driver, based on code from the
|
||||||
|
// Cygwin ioperm library.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winioctl.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define IOPERM_FILE "\\\\.\\ioperm"
|
||||||
|
|
||||||
|
#define IOCTL_IOPERM \
|
||||||
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
struct ioperm_data
|
||||||
|
{
|
||||||
|
unsigned long from;
|
||||||
|
unsigned long num;
|
||||||
|
int turn_on;
|
||||||
|
};
|
||||||
|
|
||||||
|
static SC_HANDLE scm = NULL;
|
||||||
|
static SC_HANDLE svc = NULL;
|
||||||
|
static int service_was_created = 0;
|
||||||
|
static int service_was_started = 0;
|
||||||
|
|
||||||
|
int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on)
|
||||||
|
{
|
||||||
|
HANDLE h;
|
||||||
|
struct ioperm_data ioperm_data;
|
||||||
|
DWORD BytesReturned;
|
||||||
|
BOOL r;
|
||||||
|
|
||||||
|
h = CreateFile(IOPERM_FILE, GENERIC_READ, 0, NULL,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioperm_data.from = from;
|
||||||
|
ioperm_data.num = num;
|
||||||
|
ioperm_data.turn_on = turn_on;
|
||||||
|
|
||||||
|
r = DeviceIoControl(h, IOCTL_IOPERM,
|
||||||
|
&ioperm_data, sizeof ioperm_data,
|
||||||
|
NULL, 0,
|
||||||
|
&BytesReturned, NULL);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
{
|
||||||
|
errno = EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(h);
|
||||||
|
|
||||||
|
return r != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load ioperm.sys driver.
|
||||||
|
// Returns 1 for success, 0 for failure.
|
||||||
|
// Remember to call IOperm_UninstallDriver to uninstall the driver later.
|
||||||
|
|
||||||
|
int IOperm_InstallDriver(void)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
int result = 1;
|
||||||
|
|
||||||
|
scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||||
|
|
||||||
|
if (scm == NULL)
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i)\n",
|
||||||
|
error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc = CreateService(scm,
|
||||||
|
TEXT("ioperm"),
|
||||||
|
TEXT("I/O port access driver"),
|
||||||
|
SERVICE_ALL_ACCESS,
|
||||||
|
SERVICE_KERNEL_DRIVER,
|
||||||
|
SERVICE_AUTO_START,
|
||||||
|
SERVICE_ERROR_NORMAL,
|
||||||
|
"ioperm.sys",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (svc == NULL)
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
|
||||||
|
if (error != ERROR_SERVICE_EXISTS)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"IOperm_InstallDriver: Failed to create service (%i)\n",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
svc = OpenService(scm, TEXT("ioperm"), SERVICE_ALL_ACCESS);
|
||||||
|
|
||||||
|
if (svc == NULL)
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"IOperm_InstallDriver: Failed to open service (%i)\n",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc == NULL)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(scm);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
service_was_created = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StartService(svc, 0, NULL))
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
|
||||||
|
if (error != ERROR_SERVICE_ALREADY_RUNNING)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i)\n",
|
||||||
|
error);
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("IOperm_InstallDriver: ioperm driver already running\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("IOperm_InstallDriver: ioperm driver installed\n");
|
||||||
|
service_was_started = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(svc);
|
||||||
|
CloseServiceHandle(scm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IOperm_UninstallDriver(void)
|
||||||
|
{
|
||||||
|
SERVICE_STATUS stat;
|
||||||
|
int result = 1;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
// If we started the service, stop it.
|
||||||
|
|
||||||
|
if (service_was_started)
|
||||||
|
{
|
||||||
|
if (!ControlService(svc, SERVICE_CONTROL_STOP, &stat))
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
|
||||||
|
if (error == ERROR_SERVICE_NOT_ACTIVE)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"IOperm_UninstallDriver: Service not active? (%i)\n",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"IOperm_UninstallDriver: Failed to stop service (%i)\n",
|
||||||
|
error);
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we created the service, delete it.
|
||||||
|
|
||||||
|
if (service_was_created)
|
||||||
|
{
|
||||||
|
if (!DeleteService(svc))
|
||||||
|
{
|
||||||
|
error = GetLastError();
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"IOperm_UninstallDriver: DeleteService failed (%i)\n",
|
||||||
|
error);
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close handles.
|
||||||
|
|
||||||
|
if (svc != NULL)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(svc);
|
||||||
|
svc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scm != NULL)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(scm);
|
||||||
|
scm = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_was_created = 0;
|
||||||
|
service_was_started = 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #ifndef _WIN32 */
|
||||||
|
|
||||||
36
opl/ioperm_sys.h
Normal file
36
opl/ioperm_sys.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Emacs style mode select -*- C++ -*-
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2002, 2003 Marcel Telka
|
||||||
|
// Copyright(C) 2009 Simon Howard
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
// 02111-1307, USA.
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// Interface to the ioperm.sys driver, based on code from the
|
||||||
|
// Cygwin ioperm library.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef IOPERM_SYS_H
|
||||||
|
#define IOPERM_SYS_H
|
||||||
|
|
||||||
|
int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on);
|
||||||
|
int IOperm_InstallDriver(void);
|
||||||
|
int IOperm_UninstallDriver(void);
|
||||||
|
|
||||||
|
#endif /* #ifndef IOPERM_SYS_H */
|
||||||
|
|
||||||
11
opl/opl.c
11
opl/opl.c
|
|
@ -46,7 +46,7 @@ extern opl_driver_t opl_linux_driver;
|
||||||
extern opl_driver_t opl_openbsd_driver;
|
extern opl_driver_t opl_openbsd_driver;
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
extern opl_driver_t opl_win9x_driver;
|
extern opl_driver_t opl_win32_driver;
|
||||||
#endif
|
#endif
|
||||||
extern opl_driver_t opl_sdl_driver;
|
extern opl_driver_t opl_sdl_driver;
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ static opl_driver_t *drivers[] =
|
||||||
&opl_openbsd_driver,
|
&opl_openbsd_driver,
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
&opl_win9x_driver,
|
&opl_win32_driver,
|
||||||
#endif
|
#endif
|
||||||
&opl_sdl_driver,
|
&opl_sdl_driver,
|
||||||
NULL
|
NULL
|
||||||
|
|
@ -197,6 +197,7 @@ void OPL_WritePort(opl_port_t port, unsigned int value)
|
||||||
{
|
{
|
||||||
#ifdef OPL_DEBUG_TRACE
|
#ifdef OPL_DEBUG_TRACE
|
||||||
printf("OPL_write: %i, %x\n", port, value);
|
printf("OPL_write: %i, %x\n", port, value);
|
||||||
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
driver->write_port_func(port, value);
|
driver->write_port_func(port, value);
|
||||||
}
|
}
|
||||||
|
|
@ -208,10 +209,16 @@ unsigned int OPL_ReadPort(opl_port_t port)
|
||||||
{
|
{
|
||||||
unsigned int result;
|
unsigned int result;
|
||||||
|
|
||||||
|
#ifdef OPL_DEBUG_TRACE
|
||||||
|
printf("OPL_read: %i...\n", port);
|
||||||
|
fflush(stdout);
|
||||||
|
#endif
|
||||||
|
|
||||||
result = driver->read_port_func(port);
|
result = driver->read_port_func(port);
|
||||||
|
|
||||||
#ifdef OPL_DEBUG_TRACE
|
#ifdef OPL_DEBUG_TRACE
|
||||||
printf("OPL_read: %i -> %x\n", port, result);
|
printf("OPL_read: %i -> %x\n", port, result);
|
||||||
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
// 02111-1307, USA.
|
// 02111-1307, USA.
|
||||||
//
|
//
|
||||||
// DESCRIPTION:
|
// DESCRIPTION:
|
||||||
// OPL Win9x native interface.
|
// OPL Win32 native interface.
|
||||||
//
|
//
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
|
@ -34,13 +36,15 @@
|
||||||
#include "opl_internal.h"
|
#include "opl_internal.h"
|
||||||
#include "opl_timer.h"
|
#include "opl_timer.h"
|
||||||
|
|
||||||
|
#include "ioperm_sys.h"
|
||||||
|
|
||||||
static unsigned int opl_port_base;
|
static unsigned int opl_port_base;
|
||||||
|
|
||||||
// MingW?
|
// MingW?
|
||||||
|
|
||||||
#if defined(__GNUC__) && defined(__i386__)
|
#if defined(__GNUC__) && defined(__i386__)
|
||||||
|
|
||||||
static unsigned int OPL_Win9x_PortRead(opl_port_t port)
|
static unsigned int OPL_Win32_PortRead(opl_port_t port)
|
||||||
{
|
{
|
||||||
unsigned char result;
|
unsigned char result;
|
||||||
|
|
||||||
|
|
@ -56,7 +60,7 @@ static unsigned int OPL_Win9x_PortRead(opl_port_t port)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
|
static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
|
||||||
{
|
{
|
||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
"movl %0, %%edx\n"
|
"movl %0, %%edx\n"
|
||||||
|
|
@ -77,58 +81,86 @@ static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
|
||||||
|
|
||||||
#define NO_PORT_RW
|
#define NO_PORT_RW
|
||||||
|
|
||||||
static unsigned int OPL_Win9x_PortRead(opl_port_t port)
|
static unsigned int OPL_Win32_PortRead(opl_port_t port)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
|
static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int OPL_Win9x_Init(unsigned int port_base)
|
static int OPL_Win32_Init(unsigned int port_base)
|
||||||
{
|
{
|
||||||
#ifndef NO_PORT_RW
|
#ifndef NO_PORT_RW
|
||||||
|
|
||||||
OSVERSIONINFO version_info;
|
OSVERSIONINFO version_info;
|
||||||
|
|
||||||
// Check that this is a Windows 9x series OS:
|
opl_port_base = port_base;
|
||||||
|
|
||||||
|
// Check the OS version.
|
||||||
|
|
||||||
memset(&version_info, 0, sizeof(version_info));
|
memset(&version_info, 0, sizeof(version_info));
|
||||||
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
||||||
|
|
||||||
GetVersionEx(&version_info);
|
GetVersionEx(&version_info);
|
||||||
|
|
||||||
if (version_info.dwPlatformId == 1)
|
// On NT-based systems, we must acquire I/O port permissions
|
||||||
|
// using the ioperm.sys driver.
|
||||||
|
|
||||||
|
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||||||
{
|
{
|
||||||
opl_port_base = port_base;
|
// Install driver.
|
||||||
|
|
||||||
// Start callback thread
|
if (!IOperm_InstallDriver())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return OPL_Timer_StartThread();
|
// Open port range.
|
||||||
|
|
||||||
|
if (!IOperm_EnablePortRange(opl_port_base, 2, 1))
|
||||||
|
{
|
||||||
|
IOperm_UninstallDriver();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start callback thread
|
||||||
|
|
||||||
|
if (!OPL_Timer_StartThread())
|
||||||
|
{
|
||||||
|
IOperm_UninstallDriver();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OPL_Win9x_Shutdown(void)
|
static void OPL_Win32_Shutdown(void)
|
||||||
{
|
{
|
||||||
// Stop callback thread
|
// Stop callback thread
|
||||||
|
|
||||||
OPL_Timer_StopThread();
|
OPL_Timer_StopThread();
|
||||||
|
|
||||||
|
// Unload IOperm library.
|
||||||
|
|
||||||
|
IOperm_UninstallDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
opl_driver_t opl_win9x_driver =
|
opl_driver_t opl_win32_driver =
|
||||||
{
|
{
|
||||||
"Win9x",
|
"Win32",
|
||||||
OPL_Win9x_Init,
|
OPL_Win32_Init,
|
||||||
OPL_Win9x_Shutdown,
|
OPL_Win32_Shutdown,
|
||||||
OPL_Win9x_PortRead,
|
OPL_Win32_PortRead,
|
||||||
OPL_Win9x_PortWrite,
|
OPL_Win32_PortWrite,
|
||||||
OPL_Timer_SetCallback,
|
OPL_Timer_SetCallback,
|
||||||
OPL_Timer_ClearCallbacks,
|
OPL_Timer_ClearCallbacks,
|
||||||
OPL_Timer_Lock,
|
OPL_Timer_Lock,
|
||||||
Loading…
Reference in a new issue