Add OPL hardware playback support for Windows NT-based systems.

Subversion-branch: /branches/opl-branch
Subversion-revision: 1871
This commit is contained in:
Simon Howard 2010-02-26 21:07:59 +00:00
parent cdacf59ace
commit 06b97d2d11
7 changed files with 358 additions and 24 deletions

View file

@ -15,5 +15,6 @@ Bad MIDIs:
Other tasks:
* Get a better software OPL emulator
* DMXOPTIONS opl3/phase option support.

View file

@ -64,11 +64,13 @@ stdout.txt:
If you're running an NT-based system, it is not possible to directly
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 ===

View file

@ -13,6 +13,7 @@ libopl_a_SOURCES = \
opl_queue.c opl_queue.h \
opl_sdl.c \
opl_timer.c opl_timer.h \
opl_win9x.c \
opl_win32.c \
ioperm_sys.c ioperm_sys.h \
fmopl.c fmopl.h

255
opl/ioperm_sys.c Normal file
View 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
View 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 */

View file

@ -46,7 +46,7 @@ extern opl_driver_t opl_linux_driver;
extern opl_driver_t opl_openbsd_driver;
#endif
#ifdef _WIN32
extern opl_driver_t opl_win9x_driver;
extern opl_driver_t opl_win32_driver;
#endif
extern opl_driver_t opl_sdl_driver;
@ -59,7 +59,7 @@ static opl_driver_t *drivers[] =
&opl_openbsd_driver,
#endif
#ifdef _WIN32
&opl_win9x_driver,
&opl_win32_driver,
#endif
&opl_sdl_driver,
NULL
@ -197,6 +197,7 @@ void OPL_WritePort(opl_port_t port, unsigned int value)
{
#ifdef OPL_DEBUG_TRACE
printf("OPL_write: %i, %x\n", port, value);
fflush(stdout);
#endif
driver->write_port_func(port, value);
}
@ -208,10 +209,16 @@ unsigned int OPL_ReadPort(opl_port_t port)
{
unsigned int result;
#ifdef OPL_DEBUG_TRACE
printf("OPL_read: %i...\n", port);
fflush(stdout);
#endif
result = driver->read_port_func(port);
#ifdef OPL_DEBUG_TRACE
printf("OPL_read: %i -> %x\n", port, result);
fflush(stdout);
#endif
return result;

View file

@ -19,7 +19,7 @@
// 02111-1307, USA.
//
// DESCRIPTION:
// OPL Win9x native interface.
// OPL Win32 native interface.
//
//-----------------------------------------------------------------------------
@ -27,6 +27,8 @@
#ifdef _WIN32
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@ -34,13 +36,15 @@
#include "opl_internal.h"
#include "opl_timer.h"
#include "ioperm_sys.h"
static unsigned int opl_port_base;
// MingW?
#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;
@ -56,7 +60,7 @@ static unsigned int OPL_Win9x_PortRead(opl_port_t port)
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 (
"movl %0, %%edx\n"
@ -77,58 +81,86 @@ static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
#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;
}
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
static int OPL_Win9x_Init(unsigned int port_base)
static int OPL_Win32_Init(unsigned int port_base)
{
#ifndef NO_PORT_RW
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));
version_info.dwOSVersionInfoSize = sizeof(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
return 0;
}
static void OPL_Win9x_Shutdown(void)
static void OPL_Win32_Shutdown(void)
{
// Stop callback thread
OPL_Timer_StopThread();
// Unload IOperm library.
IOperm_UninstallDriver();
}
opl_driver_t opl_win9x_driver =
opl_driver_t opl_win32_driver =
{
"Win9x",
OPL_Win9x_Init,
OPL_Win9x_Shutdown,
OPL_Win9x_PortRead,
OPL_Win9x_PortWrite,
"Win32",
OPL_Win32_Init,
OPL_Win32_Shutdown,
OPL_Win32_PortRead,
OPL_Win32_PortWrite,
OPL_Timer_SetCallback,
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,