dosbox-wii/src/hardware/adlib.cpp

375 lines
10 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-03 00:28:34 +02:00
* Copyright (C) 2002-2007 The DOSBox Team
2009-05-02 23:03:37 +02:00
*
* 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
2009-05-03 00:02:15 +02:00
* GNU General Public License for more details.
2009-05-02 23:03:37 +02:00
*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
2009-05-03 00:02:15 +02:00
#include <sys/types.h>
#include <dirent.h>
2009-05-02 23:03:37 +02:00
#include "dosbox.h"
#include "inout.h"
#include "mixer.h"
2009-05-02 23:27:47 +02:00
#include "pic.h"
2009-05-02 23:03:37 +02:00
#include "hardware.h"
2009-05-02 23:20:05 +02:00
#include "setup.h"
2009-05-03 00:02:15 +02:00
#include "mapper.h"
#include "mem.h"
2009-05-02 23:03:37 +02:00
/*
Thanks to vdmsound for nice simple way to implement this
*/
2009-05-03 00:02:15 +02:00
#define logerror
2009-05-02 23:27:47 +02:00
2009-05-02 23:43:00 +02:00
#ifdef _MSC_VER
2009-05-02 23:03:37 +02:00
/* Disable recurring warnings */
2009-05-02 23:27:47 +02:00
# pragma warning ( disable : 4018 )
# pragma warning ( disable : 4244 )
2009-05-02 23:43:00 +02:00
#endif
2009-05-02 23:27:47 +02:00
2009-05-03 00:02:15 +02:00
struct __MALLOCPTR {
void* m_ptr;
2009-05-02 23:27:47 +02:00
2009-05-03 00:02:15 +02:00
__MALLOCPTR(void) : m_ptr(NULL) { }
__MALLOCPTR(void* src) : m_ptr(src) { }
void* operator=(void* rhs) { return (m_ptr = rhs); }
operator int*() const { return (int*)m_ptr; }
operator int**() const { return (int**)m_ptr; }
operator char*() const { return (char*)m_ptr; }
2009-05-02 23:03:37 +02:00
};
2009-05-03 00:02:15 +02:00
namespace OPL2 {
#define HAS_YM3812 1
#include "fmopl.c"
void TimerOver(Bitu val){
YM3812TimerOver(val>>8,val & 0xff);
}
void TimerHandler(int channel,double interval_Sec) {
if (interval_Sec==0.0) return;
PIC_AddEvent(TimerOver,1000.0f*interval_Sec,channel);
}
}
#undef OSD_CPU_H
#undef TL_TAB_LEN
namespace THEOPL3 {
#define HAS_YMF262 1
#include "ymf262.c"
void TimerOver(Bitu val){
YMF262TimerOver(val>>8,val & 0xff);
}
void TimerHandler(int channel,double interval_Sec) {
if (interval_Sec==0.0) return;
PIC_AddEvent(TimerOver,1000.0f*interval_Sec,channel);
}
}
2009-05-02 23:27:47 +02:00
2009-05-03 00:02:15 +02:00
#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz
#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz
2009-05-02 23:27:47 +02:00
2009-05-03 00:02:15 +02:00
#define RAW_SIZE 1024
static struct {
bool active;
OPL_Mode mode;
MixerChannel * chan;
Bit32u last_used;
Bit16s mixbuf[2][128];
struct {
FILE * handle;
bool capturing;
Bit32u start;
Bit32u last;
Bit8u index;
Bit8u buffer[RAW_SIZE+8];
Bit8u regs[2][256];
Bit32u used;
Bit32u done;
Bit8u cmd[2];
2009-05-03 00:08:43 +02:00
bool opl3;
bool dualopl2;
2009-05-03 00:02:15 +02:00
} raw;
} opl;
2009-05-02 23:03:37 +02:00
2009-05-03 00:02:15 +02:00
static void OPL_CallBack(Bitu len) {
2009-05-02 23:03:37 +02:00
/* Check for size to update and check for 1 ms updates to the opl registers */
2009-05-03 00:02:15 +02:00
Bitu i;
switch(opl.mode) {
case OPL_opl2:
OPL2::YM3812UpdateOne(0,(OPL2::INT16 *)MixTemp,len);
opl.chan->AddSamples_m16(len,(Bit16s*)MixTemp);
break;
case OPL_opl3:
THEOPL3::YMF262UpdateOne(0,(OPL2::INT16 *)MixTemp,len);
opl.chan->AddSamples_s16(len,(Bit16s*)MixTemp);
break;
case OPL_dualopl2:
OPL2::YM3812UpdateOne(0,(OPL2::INT16 *)opl.mixbuf[0],len);
OPL2::YM3812UpdateOne(1,(OPL2::INT16 *)opl.mixbuf[1],len);
for (i=0;i<len;i++) {
((Bit16s*)MixTemp)[i*2+0]=opl.mixbuf[0][i];
((Bit16s*)MixTemp)[i*2+1]=opl.mixbuf[1][i];
}
opl.chan->AddSamples_s16(len,(Bit16s*)MixTemp);
break;
}
if ((PIC_Ticks-opl.last_used)>30000) {
opl.chan->Enable(false);
opl.active=false;
}
}
2009-05-02 23:03:37 +02:00
2009-05-03 00:02:15 +02:00
static Bitu OPL_Read(Bitu port,Bitu iolen) {
Bitu addr=port & 3;
switch (opl.mode) {
case OPL_opl2:
return OPL2::YM3812Read(0,addr);
case OPL_dualopl2:
return OPL2::YM3812Read(addr>>1,addr);
case OPL_opl3:
return THEOPL3::YMF262Read(0,addr);
}
return 0xff;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
static void OPL_RawAdd(Bitu index,Bitu val);
void OPL_Write(Bitu port,Bitu val,Bitu iolen) {
opl.last_used=PIC_Ticks;
if (!opl.active) {
opl.active=true;
opl.chan->Enable(true);
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
port&=3;
if (port&1) {
Bitu index=port>>1;
opl.raw.regs[index][opl.raw.cmd[index]]=val;
if (opl.raw.capturing) OPL_RawAdd(index,val);
} else opl.raw.cmd[port>>1]=val;
if (!port) adlib_commandreg=val;
switch (opl.mode) {
case OPL_opl2:
OPL2::YM3812Write(0,port,val);
break;
case OPL_opl3:
THEOPL3::YMF262Write(0,port,val);
break;
case OPL_dualopl2:
OPL2::YM3812Write(port>>1,port,val);
break;
2009-05-02 23:03:37 +02:00
}
}
2009-05-03 00:02:15 +02:00
static Bit8u dro_header[]={
2009-05-03 00:08:43 +02:00
'D','B','R','A', /* 0x00, Bit32u ID */
'W','O','P','L', /* 0x04, Bit32u ID */
0x0,0x00, /* 0x08, Bit16u version low */
0x1,0x00, /* 0x09, Bit16u version high */
0x0,0x0,0x0,0x0, /* 0x0c, Bit32u total milliseconds */
0x0,0x0,0x0,0x0, /* 0x10, Bit32u total data */
0x0,0x0,0x0,0x0 /* 0x14, Bit32u Type 0=opl2,1=opl3,2=dual-opl2 */
2009-05-03 00:02:15 +02:00
};
/* Commands
0x00 Bit8u, millisecond delay+1
0x02 none, Use the low index/data pair
0x03 none, Use the high index/data pair
2009-05-03 00:08:43 +02:00
0x10 Bit16u, millisecond delay+1
2009-05-03 00:02:15 +02:00
0xxx Bit8u, send command and data to current index/data pair
*/
2009-05-02 23:53:27 +02:00
2009-05-03 00:02:15 +02:00
static void OPL_RawEmptyBuffer(void) {
fwrite(opl.raw.buffer,1,opl.raw.used,opl.raw.handle);
opl.raw.done+=opl.raw.used;
opl.raw.used=0;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
#define ADDBUF(_VAL_) opl.raw.buffer[opl.raw.used++]=_VAL_;
static void OPL_RawAdd(Bitu index,Bitu val) {
Bit8u cmd=opl.raw.cmd[index];
2009-05-03 00:08:43 +02:00
/* check for cmd's we use for special meaning
These only control timers or are unused
*/
if (cmd == 2 || cmd == 3 || cmd == 0x10) return;
if (cmd == 4 && !index) return;
/* Check if we have yet to start */
2009-05-03 00:02:15 +02:00
if (!opl.raw.handle) {
if (cmd<0xb0 || cmd>0xb8) return;
if (!(val&0x20)) return;
Bitu i;
opl.raw.last=PIC_Ticks;
opl.raw.start=PIC_Ticks;
opl.raw.handle=OpenCaptureFile("Raw Opl",".dro");
if (!opl.raw.handle) {
opl.raw.capturing=false;
2009-05-02 23:03:37 +02:00
return;
2009-05-03 00:02:15 +02:00
}
memset(opl.raw.buffer,0,sizeof(opl.raw.buffer));
fwrite(dro_header,1,sizeof(dro_header),opl.raw.handle);
/* Check the registers to add */
2009-05-03 00:08:43 +02:00
for (i=0;i<256;i++) {
2009-05-03 00:02:15 +02:00
if (!opl.raw.regs[0][i]) continue;
if (i>=0xb0 && i<=0xb8) continue;
ADDBUF((Bit8u)i);
ADDBUF(opl.raw.regs[0][i]);
}
bool donesecond=false;
2009-05-03 00:08:43 +02:00
/* Check if we already have an opl3 enable bit logged */
if (opl.raw.regs[1][5] & 1)
opl.raw.opl3 = true;
for (i=0;i<256;i++) {
if (!opl.raw.regs[1][i]) continue;
2009-05-03 00:02:15 +02:00
if (i>=0xb0 && i<=0xb8) continue;
if (!donesecond) {
2009-05-03 00:08:43 +02:00
/* Or already have dual opl2 */
opl.raw.dualopl2 = true;
2009-05-03 00:02:15 +02:00
donesecond=true;
ADDBUF(0x3);
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
ADDBUF((Bit8u)i);
2009-05-03 00:08:43 +02:00
ADDBUF(opl.raw.regs[1][i]);
2009-05-03 00:02:15 +02:00
}
if (donesecond) ADDBUF(0x2);
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:08:43 +02:00
/* Check if we enable opl3 or access dual opl2 mode */
if (cmd == 5 && index && (val & 1)) {
opl.raw.opl3 = true;
}
if (index && val && cmd>=0xb0 && cmd<=0xb8) {
opl.raw.dualopl2 = true;
}
2009-05-03 00:02:15 +02:00
/* Check how much time has passed, Allow an extra 5 milliseconds? */
if (PIC_Ticks>(opl.raw.last+5)) {
Bitu passed=PIC_Ticks-opl.raw.last;
opl.raw.last=PIC_Ticks;
while (passed) {
passed-=1;
if (passed<256) {
ADDBUF(0x00); //8bit delay
ADDBUF((Bit8u)passed); //8bit delay
passed=0;
} else if (passed<0x10000) {
ADDBUF(0x01); //16bit delay
ADDBUF((Bit8u)(passed & 0xff)); //lo-byte
ADDBUF((Bit8u)(passed >> 8)); //hi-byte
passed=0;
} else {
ADDBUF(0x01); //16bit delay
ADDBUF(0xff); //lo-byte
ADDBUF(0xff); //hi-byte
passed-=0xffff;
}
}
}
/* Check if we're still sending to the correct index */
if (opl.raw.index != index) {
opl.raw.index=index;
ADDBUF(opl.raw.index ? 0x3 : 0x2);
}
ADDBUF(cmd);
ADDBUF(val);
if (opl.raw.used>=RAW_SIZE) OPL_RawEmptyBuffer();
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:18:08 +02:00
static void OPL_SaveRawEvent(bool pressed) {
if (!pressed)
return;
2009-05-03 00:02:15 +02:00
/* Check for previously opened wave file */
if (opl.raw.handle) {
OPL_RawEmptyBuffer();
/* Fill in the header with useful information */
2009-05-03 00:08:43 +02:00
host_writed(&dro_header[0x0c],opl.raw.last-opl.raw.start);
host_writed(&dro_header[0x10],opl.raw.done);
if (opl.raw.opl3 && opl.raw.dualopl2) host_writed(&dro_header[0x14],0x1);
else if (opl.raw.dualopl2) host_writed(&dro_header[0x14],0x2);
else host_writed(&dro_header[0x14],0x0);
2009-05-03 00:02:15 +02:00
fseek(opl.raw.handle,0,0);
fwrite(dro_header,1,sizeof(dro_header),opl.raw.handle);
fclose(opl.raw.handle);
opl.raw.handle=0;
}
if (opl.raw.capturing) {
opl.raw.capturing=false;
LOG_MSG("Stopping Raw OPL capturing.");
2009-05-02 23:03:37 +02:00
} else {
2009-05-03 00:02:15 +02:00
LOG_MSG("Preparing to capture Raw OPL, will start with first note played.");
opl.raw.capturing=true;
opl.raw.index=0;
opl.raw.used=0;
opl.raw.done=0;
opl.raw.start=0;
opl.raw.last=0;
2009-05-02 23:03:37 +02:00
}
}
2009-05-03 00:18:08 +02:00
class OPL: public Module_base {
private:
IO_ReadHandleObject ReadHandler[3];
IO_WriteHandleObject WriteHandler[3];
MixerObject MixerChan;
public:
static OPL_Mode oplmode;
2009-05-02 23:03:37 +02:00
2009-05-03 00:18:08 +02:00
OPL(Section* configuration):Module_base(configuration) {
Section_prop * section=static_cast<Section_prop *>(configuration);
Bitu base = section->Get_hex("sbbase");
Bitu rate = section->Get_int("oplrate");
if (OPL2::YM3812Init(2,OPL2_INTERNAL_FREQ,rate)) {
E_Exit("Can't create OPL2 Emulator");
};
OPL2::YM3812SetTimerHandler(0,OPL2::TimerHandler,0);
OPL2::YM3812SetTimerHandler(1,OPL2::TimerHandler,256);
if (THEOPL3::YMF262Init(1,OPL3_INTERNAL_FREQ,rate)) {
E_Exit("Can't create OPL3 Emulator");
};
THEOPL3::YMF262SetTimerHandler(0,THEOPL3::TimerHandler,0);
WriteHandler[0].Install(0x388,OPL_Write,IO_MB,4);
ReadHandler[0].Install(0x388,OPL_Read,IO_MB,4);
if (oplmode>=OPL_dualopl2) {
WriteHandler[1].Install(base,OPL_Write,IO_MB,4);
ReadHandler[1].Install(base,OPL_Read,IO_MB,4);
}
2009-05-02 23:27:47 +02:00
2009-05-03 00:18:08 +02:00
WriteHandler[2].Install(base+8,OPL_Write,IO_MB,2);
ReadHandler[2].Install(base+8,OPL_Read,IO_MB,2);
2009-05-02 23:03:37 +02:00
2009-05-03 00:18:08 +02:00
opl.active=false;
opl.last_used=0;
opl.mode=oplmode;
memset(&opl.raw,0,sizeof(opl.raw));
opl.chan=MixerChan.Install(OPL_CallBack,rate,"FM");
MAPPER_AddHandler(OPL_SaveRawEvent,MK_f7,MMOD1|MMOD2,"caprawopl","Cap OPL");
}
~OPL() {
if (opl.raw.handle) OPL_SaveRawEvent(true);
OPL2::YM3812Shutdown();
THEOPL3::YMF262Shutdown();
}
2009-05-02 23:03:37 +02:00
};
2009-05-03 00:18:08 +02:00
static OPL* test;
//Initialize static members
OPL_Mode OPL::oplmode=OPL_none;
void OPL_Init(Section* sec,OPL_Mode oplmode) {
OPL::oplmode = oplmode;
test = new OPL(sec);
}
void OPL_ShutDown(Section* sec){
delete test;
}