Added DMA and SoundBlaster emulation.
This commit is contained in:
parent
a60d05b405
commit
53268a54d1
|
@ -7,8 +7,9 @@ IMPORTS = user32 kernel32 ntdll
|
|||
LDIMPORTS = user32.dll kernel32.dll ntdll.dll
|
||||
|
||||
C_SRCS = \
|
||||
dosaspi.c \
|
||||
devices.c \
|
||||
dma.c \
|
||||
dosaspi.c \
|
||||
dosvm.c \
|
||||
int09.c \
|
||||
int10.c \
|
||||
|
@ -23,6 +24,7 @@ C_SRCS = \
|
|||
int67.c \
|
||||
ioports.c \
|
||||
module.c \
|
||||
soundblaster.c \
|
||||
vga.c \
|
||||
xms.c
|
||||
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* DMA Emulation
|
||||
*
|
||||
* Copyright 2002 Christian Costa
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "windef.h"
|
||||
#include "dosexe.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(dma);
|
||||
|
||||
/* Internal registers of the 2 DMA chips wich control 8 DMA channels */
|
||||
static DWORD DMA_BaseAddress[8];
|
||||
static WORD DMA_ByteCount[8];
|
||||
static DWORD DMA_CurrentBaseAddress[8];
|
||||
static WORD DMA_CurrentByteCount[8];
|
||||
static BYTE DMA_Command[8];
|
||||
static BYTE DMA_Mask[2]={0x0F,0x0F};
|
||||
static BYTE DMA_Status[2]={0x00,0x00};
|
||||
static BOOL DMA_Toggle[2]={FALSE,FALSE};
|
||||
|
||||
/*
|
||||
* DMA_Transfer : Try to perform a transfer of reqlen elements (8 or 16 bits)
|
||||
* on the specified channel and return the elements transferred
|
||||
*/
|
||||
int DMA_Transfer(int channel,int reqlen,void* buffer)
|
||||
{
|
||||
int i,size,ret=0;
|
||||
int opmode,increment,autoinit,trmode,dmachip;
|
||||
int regmode = DMA_Command[channel];
|
||||
char *p,*dmabuf;
|
||||
|
||||
dmabuf = buffer;
|
||||
dmachip = (channel<4) ? 0 : 1;
|
||||
|
||||
TRACE("DMA_Command = %x reqlen=%d\n",regmode,reqlen);
|
||||
|
||||
/* Exit if channel is masked */
|
||||
if (DMA_Mask[dmachip]&(1<<(channel&3)))
|
||||
return 0;
|
||||
|
||||
opmode = (regmode & 0xC0) >> 6;
|
||||
increment = !(regmode & 0x20);
|
||||
autoinit = regmode & 0x10;
|
||||
trmode = (regmode & 0x0C) >> 2;
|
||||
|
||||
/* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
|
||||
size = (channel<4) ? 1 : 2;
|
||||
|
||||
/* Process operating mode */
|
||||
switch(opmode)
|
||||
{
|
||||
case 0:
|
||||
/* Request mode */
|
||||
FIXME("Request Mode - Not Implemented\n");
|
||||
return 0;
|
||||
case 1:
|
||||
/* Single Mode */
|
||||
break;
|
||||
case 2:
|
||||
/* Request mode */
|
||||
FIXME("Block Mode - Not Implemented\n");
|
||||
return 0;
|
||||
case 3:
|
||||
/* Cascade Mode */
|
||||
ERR("Cascade Mode should not be used by regular apps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform one the 4 transfer modes */
|
||||
if (trmode == 4) {
|
||||
/* Illegal */
|
||||
ERR("DMA Transfer Type Illegal\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = min(DMA_CurrentByteCount[channel],reqlen);
|
||||
|
||||
/* Update DMA registers */
|
||||
DMA_CurrentByteCount[channel]-=ret;
|
||||
if (increment)
|
||||
DMA_CurrentBaseAddress[channel] += ret * size;
|
||||
else
|
||||
DMA_CurrentBaseAddress[channel] -= ret * size;
|
||||
|
||||
switch(trmode)
|
||||
{
|
||||
case 0:
|
||||
/* Verification (no real transfer)*/
|
||||
TRACE("Verification DMA operation\n");
|
||||
break;
|
||||
case 1:
|
||||
/* Write */
|
||||
TRACE("Perform Write transfer of %d bytes at %lx with count %x\n",ret,
|
||||
DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
|
||||
if (increment)
|
||||
memcpy((void*)DMA_CurrentBaseAddress[channel],dmabuf,ret*size);
|
||||
else
|
||||
for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
|
||||
/* FIXME: possible endianness issue for 16 bits DMA */
|
||||
*(p-i) = dmabuf[i];
|
||||
break;
|
||||
case 2:
|
||||
/* Read */
|
||||
TRACE("Perform Read transfer of %d bytes at %lx with count %x\n",ret,
|
||||
DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
|
||||
if (increment)
|
||||
memcpy(dmabuf,(void*)DMA_CurrentBaseAddress[channel],ret*size);
|
||||
else
|
||||
for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
|
||||
/* FIXME: possible endianness issue for 16 bits DMA */
|
||||
dmabuf[i] = *(p-i);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for end of transfer */
|
||||
if (DMA_CurrentByteCount[channel]==0) {
|
||||
TRACE("DMA buffer empty\n");
|
||||
|
||||
/* Update status register of the DMA chip corresponding to the channel */
|
||||
DMA_Status[dmachip] |= 1 << (channel & 0x3); /* Mark transfer as finished */
|
||||
DMA_Status[dmachip] &= ~(1 << ((channel & 0x3) + 4)); /* Reset soft request if any */
|
||||
|
||||
if (autoinit) {
|
||||
/* Reload Current* register to their initial values */
|
||||
DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
|
||||
DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void DMA_ioport_out( WORD port, BYTE val )
|
||||
{
|
||||
int channel,dmachip;
|
||||
|
||||
switch(port)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0xC0:
|
||||
case 0xC4:
|
||||
case 0xC8:
|
||||
case 0xCC:
|
||||
/* Base Address*/
|
||||
channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
|
||||
dmachip = (channel<4) ? 0 : 1;
|
||||
if (!DMA_Toggle[dmachip])
|
||||
DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & ~0xFF)|(val & 0xFF);
|
||||
else {
|
||||
DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & (~(0xFF << 8)))|((val & 0xFF) << 8);
|
||||
DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
|
||||
TRACE("Write Base Address = %lx\n",DMA_BaseAddress[channel]);
|
||||
}
|
||||
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0xC2:
|
||||
case 0xC6:
|
||||
case 0xCA:
|
||||
case 0xCE:
|
||||
/* Count*/
|
||||
channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
|
||||
dmachip = (channel<4) ? 0 : 1;
|
||||
if (!DMA_Toggle[dmachip])
|
||||
DMA_ByteCount[channel]=(DMA_ByteCount[channel] & ~0xFF)|((val+1) & 0xFF);
|
||||
else {
|
||||
DMA_ByteCount[channel]=(DMA_ByteCount[channel] & (~(0xFF << 8)))|(((val+1) & 0xFF) << 8);
|
||||
DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
|
||||
TRACE("Write Count = %x.\n",DMA_ByteCount[channel]);
|
||||
}
|
||||
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
||||
break;
|
||||
|
||||
/* Low Page Base Address */
|
||||
case 0x87: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x83: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x81: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x82: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x8B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x89: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
case 0x8A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
||||
|
||||
/* Low Page Base Address (only 4 lower bits are significant) */
|
||||
case 0x487: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x483: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x481: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x482: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x48B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x489: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
case 0x48A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
||||
|
||||
case 0x08:
|
||||
case 0xD0:
|
||||
/* Command */
|
||||
FIXME("Write Command (%x) - Not Implemented\n",val);
|
||||
break;
|
||||
|
||||
case 0x0B:
|
||||
case 0xD6:
|
||||
/* Mode */
|
||||
TRACE("Write Mode (%x)\n",val);
|
||||
DMA_Command[((port==0xD6)?4:0)+(val&0x3)]=val;
|
||||
switch(val>>6)
|
||||
{
|
||||
case 0:
|
||||
/* Request mode */
|
||||
FIXME("Request Mode - Not Implemented\n");
|
||||
break;
|
||||
case 1:
|
||||
/* Single Mode */
|
||||
break;
|
||||
case 2:
|
||||
/* Block mode */
|
||||
FIXME("Block Mode - Not Implemented\n");
|
||||
break;
|
||||
case 3:
|
||||
/* Cascade Mode */
|
||||
ERR("Cascade Mode should not be used by regular apps\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0A:
|
||||
case 0xD4:
|
||||
/* Write Single Mask Bit */
|
||||
TRACE("Write Single Mask Bit (%x)\n",val);
|
||||
dmachip = (port==0x0A) ? 0 : 1;
|
||||
if (val&4)
|
||||
DMA_Mask[dmachip] |= 1<<(val&3);
|
||||
else
|
||||
DMA_Mask[dmachip] &= ~(1<<(val&3));
|
||||
break;
|
||||
|
||||
case 0x0F:
|
||||
case 0xDE:
|
||||
/* Write All Mask Bits (only 4 lower bits are significant */
|
||||
FIXME("Write All Mask Bits (%x)\n",val);
|
||||
dmachip = (port==0x0F) ? 0 : 1;
|
||||
DMA_Mask[dmachip] = val & 0x0F;
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
case 0xD2:
|
||||
/* Software DRQx Request */
|
||||
FIXME("Software DRQx Request (%x) - Not Implemented\n",val);
|
||||
break;
|
||||
|
||||
case 0x0C:
|
||||
case 0xD8:
|
||||
/* Reset DMA Pointer Flip-Flop */
|
||||
TRACE("Reset Flip-Flop\n");
|
||||
DMA_Toggle[port==0xD8]=FALSE;
|
||||
break;
|
||||
|
||||
case 0x0D:
|
||||
case 0xDA:
|
||||
/* Master Reset */
|
||||
TRACE("Master Reset\n");
|
||||
dmachip = (port==0x0D) ? 0 : 1;
|
||||
/* Reset DMA Pointer Flip-Flop */
|
||||
DMA_Toggle[dmachip]=FALSE;
|
||||
/* Mask all channels */
|
||||
DMA_Mask[dmachip] = 0x0F;
|
||||
break;
|
||||
|
||||
case 0x0E:
|
||||
case 0xDC:
|
||||
/* Reset Mask Register */
|
||||
FIXME("Reset Mask Register\n");
|
||||
dmachip = (port==0x0E) ? 0 : 1;
|
||||
/* Unmask all channels */
|
||||
DMA_Mask[dmachip] = 0x00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE DMA_ioport_in( WORD port )
|
||||
{
|
||||
int channel,dmachip;
|
||||
BYTE res = 0;
|
||||
|
||||
switch(port)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0xC0:
|
||||
case 0xC4:
|
||||
case 0xC8:
|
||||
case 0xCC:
|
||||
/* Base Address*/
|
||||
channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
|
||||
dmachip = (channel<4) ? 0 : 1;
|
||||
if (!DMA_Toggle[dmachip])
|
||||
res = DMA_CurrentBaseAddress[channel] & 0xFF;
|
||||
else {
|
||||
res = (DMA_CurrentBaseAddress[channel] & (0xFF << 8))>>8;
|
||||
TRACE("Read Current Base Address = %lx\n",DMA_CurrentBaseAddress[channel]);
|
||||
}
|
||||
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0xC2:
|
||||
case 0xC6:
|
||||
case 0xCA:
|
||||
case 0xCE:
|
||||
/* Count*/
|
||||
channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
|
||||
dmachip = (channel<4) ? 0 : 1;
|
||||
if (!DMA_Toggle[dmachip])
|
||||
res = DMA_CurrentByteCount[channel] & 0xFF;
|
||||
else {
|
||||
res = (DMA_CurrentByteCount[channel] & (0xFF << 8))>>8;
|
||||
TRACE("Read Current Count = %x.\n",DMA_CurrentByteCount[channel]);
|
||||
}
|
||||
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
||||
break;
|
||||
|
||||
/* Low Page Base Address */
|
||||
case 0x87: res = (DMA_BaseAddress[0]&(0xFF<<16))>>16; break;
|
||||
case 0x83: res = (DMA_BaseAddress[1]&(0xFF<<16))>>16; break;
|
||||
case 0x81: res = (DMA_BaseAddress[2]&(0xFF<<16))>>16; break;
|
||||
case 0x82: res = (DMA_BaseAddress[3]&(0xFF<<16))>>16; break;
|
||||
case 0x8B: res = (DMA_BaseAddress[5]&(0xFF<<16))>>16; break;
|
||||
case 0x89: res = (DMA_BaseAddress[6]&(0xFF<<16))>>16; break;
|
||||
case 0x8A: res = (DMA_BaseAddress[7]&(0xFF<<16))>>16; break;
|
||||
|
||||
/* High Page Base Address */
|
||||
case 0x487: res = (DMA_BaseAddress[0]&(0xFF<<24))>>24; break;
|
||||
case 0x483: res = (DMA_BaseAddress[1]&(0xFF<<24))>>24; break;
|
||||
case 0x481: res = (DMA_BaseAddress[2]&(0xFF<<24))>>24; break;
|
||||
case 0x482: res = (DMA_BaseAddress[3]&(0xFF<<24))>>24; break;
|
||||
case 0x48B: res = (DMA_BaseAddress[5]&(0xFF<<24))>>24; break;
|
||||
case 0x489: res = (DMA_BaseAddress[6]&(0xFF<<24))>>24; break;
|
||||
case 0x48A: res = (DMA_BaseAddress[7]&(0xFF<<24))>>24; break;
|
||||
|
||||
case 0x08:
|
||||
case 0xD0:
|
||||
/* Status */
|
||||
TRACE("Status Register Read\n");
|
||||
res = DMA_Status[(port==0x08)?0:1];
|
||||
|
||||
case 0x0D:
|
||||
case 0xDA:
|
||||
/* Temporary */
|
||||
FIXME("Temporary Register Read- Not Implemented\n");
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
|
@ -72,6 +72,11 @@ extern int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify);
|
|||
extern int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen);
|
||||
extern int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen);
|
||||
|
||||
/* dma.c */
|
||||
extern int DMA_Transfer(int channel,int reqlength,void* buffer);
|
||||
extern void DMA_ioport_out( WORD port, BYTE val );
|
||||
extern BYTE DMA_ioport_in( WORD port );
|
||||
|
||||
/* int09.c */
|
||||
extern void WINAPI DOSVM_Int09Handler(CONTEXT86*);
|
||||
extern void WINAPI DOSVM_Int09SendScan(BYTE scan,BYTE ascii);
|
||||
|
@ -113,6 +118,10 @@ extern void WINAPI DOSVM_Int33Console(MOUSE_EVENT_RECORD*);
|
|||
extern void WINAPI DOSVM_Int67Handler(CONTEXT86*);
|
||||
extern void WINAPI EMS_Ioctl_Handler(CONTEXT86*);
|
||||
|
||||
/* soundblaster.c */
|
||||
extern void SB_ioport_out( WORD port, BYTE val );
|
||||
extern BYTE SB_ioport_in( WORD port );
|
||||
|
||||
/* xms.c */
|
||||
extern void WINAPI XMS_Handler(CONTEXT86*);
|
||||
|
||||
|
|
|
@ -36,10 +36,51 @@ BOOL WINAPI DOSVM_inport( int port, int size, DWORD *res )
|
|||
case 0x60:
|
||||
*res = DOSVM_Int09ReadScan(NULL);
|
||||
break;
|
||||
case 0x22a:
|
||||
case 0x22c:
|
||||
case 0x22e:
|
||||
*res = (DWORD)SB_ioport_in( port );
|
||||
break;
|
||||
case 0x3ba:
|
||||
case 0x3da:
|
||||
*res = (DWORD)VGA_ioport_in( port );
|
||||
break;
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0xC0:
|
||||
case 0xC2:
|
||||
case 0xC4:
|
||||
case 0xC6:
|
||||
case 0xC8:
|
||||
case 0xCA:
|
||||
case 0xCC:
|
||||
case 0xCE:
|
||||
case 0x87:
|
||||
case 0x83:
|
||||
case 0x81:
|
||||
case 0x82:
|
||||
case 0x8B:
|
||||
case 0x89:
|
||||
case 0x8A:
|
||||
case 0x487:
|
||||
case 0x483:
|
||||
case 0x481:
|
||||
case 0x482:
|
||||
case 0x48B:
|
||||
case 0x489:
|
||||
case 0x48A:
|
||||
case 0x08:
|
||||
case 0xD0:
|
||||
case 0x0D:
|
||||
case 0xDA:
|
||||
*res = (DWORD)DMA_ioport_in( port );
|
||||
break;
|
||||
default:
|
||||
return FALSE; /* not handled */
|
||||
}
|
||||
|
@ -57,10 +98,62 @@ BOOL WINAPI DOSVM_outport( int port, int size, DWORD value )
|
|||
case 0x20:
|
||||
DOSVM_PIC_ioport_out( port, (BYTE)value );
|
||||
break;
|
||||
case 0x226:
|
||||
case 0x22c:
|
||||
SB_ioport_out( port, (BYTE)value );
|
||||
break;
|
||||
case 0x3c8:
|
||||
case 0x3c9:
|
||||
VGA_ioport_out( port, (BYTE)value );
|
||||
break;
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0xC0:
|
||||
case 0xC2:
|
||||
case 0xC4:
|
||||
case 0xC6:
|
||||
case 0xC8:
|
||||
case 0xCA:
|
||||
case 0xCC:
|
||||
case 0xCE:
|
||||
case 0x87:
|
||||
case 0x83:
|
||||
case 0x81:
|
||||
case 0x82:
|
||||
case 0x8B:
|
||||
case 0x89:
|
||||
case 0x8A:
|
||||
case 0x487:
|
||||
case 0x483:
|
||||
case 0x481:
|
||||
case 0x482:
|
||||
case 0x48B:
|
||||
case 0x489:
|
||||
case 0x48A:
|
||||
case 0x08:
|
||||
case 0xD0:
|
||||
case 0x0B:
|
||||
case 0xD6:
|
||||
case 0x0A:
|
||||
case 0xD4:
|
||||
case 0x0F:
|
||||
case 0xDE:
|
||||
case 0x09:
|
||||
case 0xD2:
|
||||
case 0x0C:
|
||||
case 0xD8:
|
||||
case 0x0D:
|
||||
case 0xDA:
|
||||
case 0x0E:
|
||||
case 0xDC:
|
||||
DMA_ioport_out( port, (BYTE)value );
|
||||
break;
|
||||
default:
|
||||
return FALSE; /* not handled */
|
||||
}
|
||||
|
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* Soundblaster Emulation
|
||||
*
|
||||
* Copyright 2002 Christian Costa
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "windef.h"
|
||||
#include "dosexe.h"
|
||||
#include "wine/debug.h"
|
||||
#include "dsound.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(sblaster);
|
||||
|
||||
/* Board Configuration */
|
||||
/* FIXME: Should be in a config file */
|
||||
#define SB_IRQ 5
|
||||
#define SB_IRQ_PRI 11
|
||||
#define SB_DMA 1
|
||||
|
||||
/* Soundblaster state */
|
||||
static int SampleMode; /* Mono / Stereo */
|
||||
static int SampleRate;
|
||||
static int SamplesCount;
|
||||
static BYTE DSP_Command[256]; /* Store param numbers in bytes for each command */
|
||||
static BYTE DSP_InBuffer[10]; /* Store DSP command bytes parameters from host */
|
||||
static int InSize; /* Nb of bytes in InBuffer */
|
||||
static BYTE DSP_OutBuffer[10]; /* Store DSP information bytes to host */
|
||||
static int OutSize; /* Nb of bytes in InBuffer */
|
||||
static int command; /* Current command */
|
||||
static int end_sound_loop = 0;
|
||||
static int dma_enable = 0;
|
||||
|
||||
/* The maximum size of a dma transfer can be 65536 */
|
||||
#define DMATRFSIZE 1024
|
||||
|
||||
/* DMA can perform 8 or 16-bit transfer */
|
||||
static BYTE dma_buffer[DMATRFSIZE*2];
|
||||
|
||||
/* Direct Sound buffer config */
|
||||
#define DSBUFLEN 4096 /* FIXME: Only this value seems to work */
|
||||
|
||||
/* Direct Sound playback stuff */
|
||||
static HMODULE hmodule;
|
||||
typedef HRESULT (WINAPI* fnDirectSoundCreate) (LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
|
||||
fnDirectSoundCreate lpDirectSoundCreate;
|
||||
static LPDIRECTSOUND lpdsound;
|
||||
static LPDIRECTSOUNDBUFFER lpdsbuf;
|
||||
static DSBUFFERDESC buf_desc;
|
||||
static WAVEFORMATEX wav_fmt;
|
||||
static HANDLE SB_Thread;
|
||||
static UINT buf_off;
|
||||
extern HWND vga_hwnd;
|
||||
|
||||
/* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
|
||||
static DWORD CALLBACK SB_Poll( void *dummy )
|
||||
{
|
||||
HRESULT result;
|
||||
LPBYTE lpbuf1 = NULL;
|
||||
LPBYTE lpbuf2 = NULL;
|
||||
DWORD dwsize1 = 0;
|
||||
DWORD dwsize2 = 0;
|
||||
DWORD dwbyteswritten1 = 0;
|
||||
DWORD dwbyteswritten2 = 0;
|
||||
int size;
|
||||
|
||||
/* FIXME: this loop must be improved */
|
||||
while(!end_sound_loop)
|
||||
{
|
||||
Sleep(10);
|
||||
|
||||
if (dma_enable) {
|
||||
size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
|
||||
} else
|
||||
continue;
|
||||
|
||||
result = IDirectSoundBuffer_Lock(lpdsbuf,buf_off,size,&lpbuf1,&dwsize1,&lpbuf2,&dwsize2,0);
|
||||
if (result != DS_OK) {
|
||||
ERR("Unable to lock sound buffer !\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dwbyteswritten1 = min(size,dwsize1);
|
||||
memcpy(lpbuf1,dma_buffer,dwbyteswritten1);
|
||||
if (size>dwsize1) {
|
||||
dwbyteswritten2 = min(size - dwbyteswritten1,dwsize2);
|
||||
memcpy(lpbuf2,dma_buffer+dwbyteswritten1,dwbyteswritten2);
|
||||
}
|
||||
buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;
|
||||
|
||||
result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
|
||||
if (result!=DS_OK)
|
||||
ERR("Unable to unlock sound buffer !\n");
|
||||
|
||||
SamplesCount -= size;
|
||||
if (!SamplesCount) {
|
||||
DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
|
||||
dma_enable = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL SB_Init()
|
||||
{
|
||||
HRESULT result;
|
||||
|
||||
if (!lpdsound) {
|
||||
hmodule = LoadLibraryA("dsound.dll");
|
||||
if (!hmodule) {
|
||||
ERR("Can't load dsound.dll !\n");
|
||||
return 0;
|
||||
}
|
||||
lpDirectSoundCreate = (fnDirectSoundCreate)GetProcAddress(hmodule,"DirectSoundCreate");
|
||||
if (!lpDirectSoundCreate) {
|
||||
/* CloseHandle(hmodule); */
|
||||
ERR("Can't find DirectSoundCreate function !\n");
|
||||
return 0;
|
||||
}
|
||||
result = (*lpDirectSoundCreate)(NULL,&lpdsound,NULL);
|
||||
if (result != DS_OK) {
|
||||
ERR("Unable to initialize Sound Subsystem err = %lx !\n",result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: To uncomment when :
|
||||
- SetCooperative level is correctly implemented
|
||||
- an always valid and non changing handle to a windows (vga_hwnd) is available
|
||||
(this surely needs some work in vga.c)
|
||||
result = IDirectSound_SetCooperativeLevel(lpdsound,vga_hwnd,DSSCL_EXCLUSIVE|DSSCL_PRIORITY);
|
||||
if (result != DS_OK) {
|
||||
ERR("Can't set cooperative level !\n");
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Default format */
|
||||
wav_fmt.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wav_fmt.nChannels = 1;
|
||||
wav_fmt.nSamplesPerSec = 22050;
|
||||
wav_fmt.nAvgBytesPerSec = 22050;
|
||||
wav_fmt.nBlockAlign = 1;
|
||||
wav_fmt.wBitsPerSample = 8;
|
||||
wav_fmt.cbSize = 0;
|
||||
|
||||
memset(&buf_desc,0,sizeof(DSBUFFERDESC));
|
||||
buf_desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
buf_desc.dwBufferBytes = DSBUFLEN;
|
||||
buf_desc.lpwfxFormat = &wav_fmt;
|
||||
result = IDirectSound_CreateSoundBuffer(lpdsound,&buf_desc,&lpdsbuf,NULL);
|
||||
if (result != DS_OK) {
|
||||
ERR("Can't create sound buffer !\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
|
||||
if (result != DS_OK) {
|
||||
ERR("Can't start playing !\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf_off = 0;
|
||||
end_sound_loop = 0;
|
||||
SB_Thread = CreateThread(NULL, 0, SB_Poll, NULL, 0, NULL);
|
||||
TRACE("thread\n");
|
||||
if (!SB_Thread) {
|
||||
ERR("Can't create thread !\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void SB_Reset()
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<256;i++)
|
||||
DSP_Command[i]=0;
|
||||
|
||||
/* Set Time Constant */
|
||||
DSP_Command[0x40]=1;
|
||||
/* Generate IRQ */
|
||||
DSP_Command[0xF2]=0;
|
||||
/* DMA DAC 8-bits */
|
||||
DSP_Command[0x14]=2;
|
||||
/* Generic DAC/ADC DMA (16-bit, 8-bit) */
|
||||
for(i=0xB0;i<=0xCF;i++)
|
||||
DSP_Command[i]=3;
|
||||
/* DSP Indentification */
|
||||
DSP_Command[0xE0]=1;
|
||||
|
||||
/* Clear command and input buffer */
|
||||
command = -1;
|
||||
InSize = 0;
|
||||
|
||||
/* Put a garbage value in the output buffer */
|
||||
OutSize = 1;
|
||||
if (SB_Init())
|
||||
/* All right, let's put the magic value for autodetection */
|
||||
DSP_OutBuffer[0] = 0xaa;
|
||||
else
|
||||
/* Something is wrong, put 0 to failed audetection */
|
||||
DSP_OutBuffer[0] = 0x00;
|
||||
}
|
||||
|
||||
/* Find a standard sampling rate for DirectSound */
|
||||
int SB_StdSampleRate(int SampleRate)
|
||||
{
|
||||
if (SampleRate>((44100+48000)/2)) return 48000;
|
||||
if (SampleRate>((32000+44100)/2)) return 44100;
|
||||
if (SampleRate>((24000+32000)/2)) return 32000;
|
||||
if (SampleRate>((22050+24000)/2)) return 24000;
|
||||
if (SampleRate>((16000+22050)/2)) return 22050;
|
||||
if (SampleRate>((12000+16000)/2)) return 16000;
|
||||
if (SampleRate>((11025+12000)/2)) return 12000;
|
||||
if (SampleRate>((8000+11025)/2)) return 11025;
|
||||
return 8000;
|
||||
}
|
||||
|
||||
void SB_ioport_out( WORD port, BYTE val )
|
||||
{
|
||||
switch(port)
|
||||
{
|
||||
/* DSP - Reset */
|
||||
case 0x226:
|
||||
TRACE("Resetting DSP.\n");
|
||||
SB_Reset();
|
||||
break;
|
||||
/* DSP - Write Data or Command */
|
||||
case 0x22c:
|
||||
TRACE("val=%x\n",val);
|
||||
if (command == -1) {
|
||||
/* Clear input buffer and set the current command */
|
||||
command = val;
|
||||
InSize = 0;
|
||||
}
|
||||
if (InSize!=DSP_Command[command])
|
||||
/* Fill the input buffer the command parameters if any */
|
||||
DSP_InBuffer[InSize++]=val;
|
||||
else {
|
||||
/* Process command */
|
||||
switch(command)
|
||||
{
|
||||
case 0x10: /* SB */
|
||||
FIXME("Direct DAC (8-bit) - Not Implemented\n");
|
||||
break;
|
||||
case 0x14: /* SB */
|
||||
SamplesCount = DSP_InBuffer[1]+(val<<8)+1;
|
||||
TRACE("DMA DAC (8-bit) for %x samples\n",SamplesCount);
|
||||
dma_enable = 1;
|
||||
break;
|
||||
case 0x20:
|
||||
FIXME("Direct ADC (8-bit) - Not Implemented\n");
|
||||
break;
|
||||
case 0x24: /* SB */
|
||||
FIXME("DMA ADC (8-bit) - Not Implemented\n");
|
||||
break;
|
||||
case 0x40: /* SB */
|
||||
SampleRate = 1000000/(256-val);
|
||||
TRACE("Set Time Constant (%d <-> %d Hz => %d Hz)\n",DSP_InBuffer[0],
|
||||
SampleRate,SB_StdSampleRate(SampleRate));
|
||||
SampleRate = SB_StdSampleRate(SampleRate);
|
||||
wav_fmt.nSamplesPerSec = SampleRate;
|
||||
wav_fmt.nAvgBytesPerSec = SampleRate;
|
||||
IDirectSoundBuffer_SetFormat(lpdsbuf,&wav_fmt);
|
||||
break;
|
||||
/* case 0xBX/0xCX -> See below */
|
||||
case 0xD0: /* SB */
|
||||
TRACE("Halt DMA operation (8-bit)\n");
|
||||
dma_enable = 0;
|
||||
break;
|
||||
case 0xD1: /* SB */
|
||||
FIXME("Enable Speaker - Not Implemented\n");
|
||||
break;
|
||||
case 0xD3: /* SB */
|
||||
FIXME("Disable Speaker - Not Implemented\n");
|
||||
break;
|
||||
case 0xD4: /* SB */
|
||||
FIXME("Continue DMA operation (8-bit) - Not Implemented\n");
|
||||
break;
|
||||
case 0xD8: /* SB */
|
||||
FIXME("Speaker Status - Not Implemented\n");
|
||||
break;
|
||||
case 0xE0: /* SB 2.0 */
|
||||
TRACE("DSP Identification\n");
|
||||
DSP_OutBuffer[OutSize++] = ~val;
|
||||
break;
|
||||
case 0xE1: /* SB */
|
||||
FIXME("DSP Version - Not Implemented\n");
|
||||
break;
|
||||
case 0xF2: /* SB */
|
||||
TRACE("IRQ Request (8-bit)\n");
|
||||
DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
|
||||
break;
|
||||
default:
|
||||
if (((command&0xF0)==0xB0)||((DSP_InBuffer[0]&0xF0)==0xC0)) {
|
||||
/* SB16 */
|
||||
FIXME("Generic DAC/ADC DMA (16-bit, 8-bit) - %d % d\n",command,DSP_InBuffer[1]);
|
||||
if (command&0x02)
|
||||
FIXME("Generic DAC/ADC fifo mode not supported\n");
|
||||
if (command&0x04)
|
||||
FIXME("Generic DAC/ADC autoinit dma mode not supported\n");
|
||||
if (command&0x08)
|
||||
FIXME("Generic DAC/ADC adc mode not supported\n");
|
||||
switch(command>>4) {
|
||||
case 0xB:
|
||||
FIXME("Generic DAC/ADC 8-bit not supported\n");
|
||||
SampleMode = 0;
|
||||
break;
|
||||
case 0xC:
|
||||
FIXME("Generic DAC/ADC 16-bit not supported\n");
|
||||
SampleMode = 1;
|
||||
break;
|
||||
default:
|
||||
ERR("Generic DAC/ADC resolution unknown\n");
|
||||
break;
|
||||
}
|
||||
if (DSP_InBuffer[1]&0x010)
|
||||
FIXME("Generic DAC/ADC signed sample mode not supported\n");
|
||||
if (DSP_InBuffer[1]&0x020)
|
||||
FIXME("Generic DAC/ADC stereo mode not supported\n");
|
||||
SamplesCount = DSP_InBuffer[2]+(val<<8)+1;
|
||||
TRACE("Generic DMA for %x samples\n",SamplesCount);
|
||||
dma_enable = 1;
|
||||
}
|
||||
else
|
||||
FIXME("DSP command %x not supported\n",val);
|
||||
}
|
||||
/* Empty the input buffer and end the command */
|
||||
InSize = 0;
|
||||
command = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BYTE SB_ioport_in( WORD port )
|
||||
{
|
||||
BYTE res = 0;
|
||||
|
||||
switch(port)
|
||||
{
|
||||
/* DSP Read Data */
|
||||
case 0x22a:
|
||||
/* Value in the read buffer */
|
||||
if (OutSize)
|
||||
res = DSP_OutBuffer[--OutSize];
|
||||
else
|
||||
/* return the last byte */
|
||||
res = DSP_OutBuffer[0];
|
||||
break;
|
||||
/* DSP - Write Buffer Status */
|
||||
case 0x22c:
|
||||
/* DSP always ready for writing */
|
||||
res = 0x00;
|
||||
break;
|
||||
/* DSP - Data Available Status */
|
||||
/* DSP - IRQ Acknowledge, 8-bit */
|
||||
case 0x22e:
|
||||
/* DSP data availability check */
|
||||
if (OutSize)
|
||||
res = 0x80;
|
||||
else
|
||||
res = 0x00;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
Loading…
Reference in New Issue