62a568c131
been ported from QEMU and originally have this license - TODO: The QEMU people should verify the changes and tell us if something is missing. Bochs 2.6 will be released after everything has been confirmed to be okay.
819 lines
26 KiB
C++
819 lines
26 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ES1370 soundcard support (ported from QEMU)
|
|
//
|
|
// Copyright (c) 2005 Vassili Karpov (malc)
|
|
// Copyright (C) 2011 The Bochs Project
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
#define BX_PLUGGABLE
|
|
|
|
#include "iodev.h"
|
|
#if BX_SUPPORT_PCI && BX_SUPPORT_ES1370
|
|
|
|
#include "pci.h"
|
|
#include "es1370.h"
|
|
#include "soundmod.h"
|
|
#include "soundlnx.h"
|
|
#include "soundwin.h"
|
|
#include "soundosx.h"
|
|
#include "soundsdl.h"
|
|
|
|
#define LOG_THIS theES1370Device->
|
|
|
|
bx_es1370_c* theES1370Device = NULL;
|
|
|
|
const Bit8u es1370_iomask[64] = {7, 1, 3, 1, 7, 1, 3, 1, 1, 3, 1, 0, 7, 0, 0, 0,
|
|
6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
|
7, 1, 3, 1, 6, 0, 2, 0, 6, 0, 2, 0, 6, 0, 2, 0,
|
|
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0};
|
|
|
|
#define ES1370_CTL 0x00
|
|
#define ES1370_STATUS 0x04
|
|
#define ES1370_UART_DATA 0x08
|
|
#define ES1370_UART_STATUS 0x09
|
|
#define ES1370_UART_CTL 0x09
|
|
#define ES1370_UART_TEST 0x0a
|
|
#define ES1370_MEMPAGE 0x0c
|
|
#define ES1370_CODEC 0x10
|
|
#define ES1370_SCTL 0x20
|
|
#define ES1370_DAC1_SCOUNT 0x24
|
|
#define ES1370_DAC2_SCOUNT 0x28
|
|
#define ES1370_ADC_SCOUNT 0x2c
|
|
|
|
#define ES1370_DAC1_FRAMEADR 0xc30
|
|
#define ES1370_DAC1_FRAMECNT 0xc34
|
|
#define ES1370_DAC2_FRAMEADR 0xc38
|
|
#define ES1370_DAC2_FRAMECNT 0xc3c
|
|
#define ES1370_ADC_FRAMEADR 0xd30
|
|
#define ES1370_ADC_FRAMECNT 0xd34
|
|
#define ES1370_PHA_FRAMEADR 0xd38
|
|
#define ES1370_PHA_FRAMECNT 0xd3c
|
|
|
|
#define STAT_INTR 0x80000000
|
|
#define STAT_DAC1 0x00000004
|
|
#define STAT_DAC2 0x00000002
|
|
#define STAT_ADC 0x00000001
|
|
|
|
#define SCTRL_R1INTEN 0x00000400
|
|
#define SCTRL_P2INTEN 0x00000200
|
|
#define SCTRL_P1INTEN 0x00000100
|
|
|
|
#define DAC1_CHANNEL 0
|
|
#define DAC2_CHANNEL 1
|
|
#define ADC_CHANNEL 2
|
|
|
|
const char chan_name[3][5] = {"DAC1", "DAC2", "ADC"};
|
|
const Bit16u dac1_freq[4] = {5512, 11025, 22050, 44100};
|
|
const Bit16u ctl_ch_en[3] = {0x0040, 0x0020, 0x0010};
|
|
const Bit16u sctl_ch_pause[3] = {0x0800, 0x1000, 0x0000};
|
|
const Bit16u sctl_loop_sel[3] = {0x2000, 0x4000, 0x8000};
|
|
|
|
// builtin configuration handling functions
|
|
|
|
void es1370_init_options(void)
|
|
{
|
|
bx_param_c *sound = SIM->get_param("sound");
|
|
bx_list_c *menu = new bx_list_c(sound, "es1370", "ES1370 Configuration");
|
|
menu->set_options(menu->SHOW_PARENT);
|
|
menu->set_enabled(BX_SUPPORT_ES1370);
|
|
|
|
bx_param_bool_c *enabled = new bx_param_bool_c(menu,
|
|
"enabled",
|
|
"Enable ES1370 emulation",
|
|
"Enables the ES1370 emulation",
|
|
0);
|
|
enabled->set_enabled(BX_SUPPORT_ES1370);
|
|
|
|
bx_param_filename_c *wavedev = new bx_param_filename_c(menu,
|
|
"wavedev",
|
|
"Wave device",
|
|
"This is the device where the wave output is sent to",
|
|
"", BX_PATHNAME_LEN);
|
|
bx_list_c *deplist = new bx_list_c(NULL);
|
|
deplist->add(wavedev);
|
|
enabled->set_dependent_list(deplist);
|
|
}
|
|
|
|
Bit32s es1370_options_parser(const char *context, int num_params, char *params[])
|
|
{
|
|
if (!strcmp(params[0], "es1370")) {
|
|
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_SOUND_ES1370);
|
|
for (int i = 1; i < num_params; i++) {
|
|
if (!strncmp(params[i], "enabled=", 8)) {
|
|
SIM->get_param_bool("enabled", base)->set(atol(¶ms[i][8]));
|
|
} else if (!strncmp(params[i], "wavedev=", 8)) {
|
|
SIM->get_param_string("wavedev", base)->set(¶ms[i][8]);
|
|
} else {
|
|
BX_ERROR(("%s: unknown parameter for es1370 ignored.", context));
|
|
}
|
|
}
|
|
} else {
|
|
BX_PANIC(("%s: unknown directive '%s'", context, params[0]));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Bit32s es1370_options_save(FILE *fp)
|
|
{
|
|
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_SOUND_ES1370);
|
|
fprintf(fp, "es1370: enabled=%d", SIM->get_param_bool("enabled", base)->get());
|
|
if (SIM->get_param_bool("enabled", base)->get()) {
|
|
fprintf(fp, ", wavedev=%s", SIM->get_param_string("wavedev", base)->getptr());
|
|
}
|
|
fprintf(fp, "\n");
|
|
return 0;
|
|
}
|
|
|
|
// device plugin entry points
|
|
|
|
int libes1370_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
|
{
|
|
theES1370Device = new bx_es1370_c();
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theES1370Device, BX_PLUGIN_ES1370);
|
|
// add new configuration parameter for the config interface
|
|
es1370_init_options();
|
|
// register add-on option for bochsrc and command line
|
|
SIM->register_addon_option("es1370", es1370_options_parser, es1370_options_save);
|
|
return 0; // Success
|
|
}
|
|
|
|
void libes1370_LTX_plugin_fini(void)
|
|
{
|
|
SIM->unregister_addon_option("es1370");
|
|
bx_list_c *menu = (bx_list_c*)SIM->get_param("sound");
|
|
menu->remove("es1370");
|
|
delete theES1370Device;
|
|
}
|
|
|
|
// the device object
|
|
|
|
bx_es1370_c::bx_es1370_c()
|
|
{
|
|
put("es1370", "E1370");
|
|
memset(&s, 0, sizeof(bx_es1370_t));
|
|
s.dac1_timer_index = BX_NULL_TIMER_HANDLE;
|
|
s.dac2_timer_index = BX_NULL_TIMER_HANDLE;
|
|
soundmod = NULL;
|
|
}
|
|
|
|
bx_es1370_c::~bx_es1370_c()
|
|
{
|
|
if (s.dac_outputinit) {
|
|
soundmod->closewaveoutput();
|
|
}
|
|
if (s.adc_inputinit) {
|
|
soundmod->closewaveinput();
|
|
}
|
|
delete soundmod;
|
|
|
|
SIM->get_bochs_root()->remove("es1370");
|
|
BX_DEBUG(("Exit"));
|
|
}
|
|
|
|
void bx_es1370_c::init(void)
|
|
{
|
|
// Read in values from config interface
|
|
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_SOUND_ES1370);
|
|
// Check if the device is disabled or not configured
|
|
if (!SIM->get_param_bool("enabled", base)->get()) {
|
|
BX_INFO(("ES1370 disabled"));
|
|
// mark unused plugin for removal
|
|
((bx_param_bool_c*)((bx_list_c*)SIM->get_param(BXPN_PLUGIN_CTRL))->get_by_name("es1370"))->set(0);
|
|
return;
|
|
}
|
|
BX_ES1370_THIS s.devfunc = 0x00;
|
|
DEV_register_pci_handlers(this, &BX_ES1370_THIS s.devfunc, BX_PLUGIN_ES1370,
|
|
"Experimental ES1370 soundcard");
|
|
|
|
for (unsigned i=0; i<256; i++) {
|
|
BX_ES1370_THIS pci_conf[i] = 0x0;
|
|
}
|
|
BX_ES1370_THIS pci_base_address[0] = 0;
|
|
|
|
char *wavedev = SIM->get_param_string(BXPN_ES1370_WAVEDEV)->getptr();
|
|
if (!strcmp(wavedev, "sdl")) {
|
|
BX_ES1370_THIS soundmod = DEV_sound_init_module("sdl", BX_ES1370_THIS_PTR);
|
|
} else {
|
|
BX_ES1370_THIS soundmod = DEV_sound_init_module("default", BX_ES1370_THIS_PTR);
|
|
}
|
|
int ret = BX_ES1370_THIS soundmod->openwaveoutput(wavedev);
|
|
if (ret != BX_SOUNDLOW_OK) {
|
|
BX_ERROR(("could not open wave output device"));
|
|
BX_ES1370_THIS s.dac_outputinit = 0;
|
|
} else {
|
|
BX_ES1370_THIS s.dac_outputinit = 1;
|
|
}
|
|
BX_ES1370_THIS s.adc_inputinit = 0;
|
|
BX_ES1370_THIS s.dac_nr_active = -1;
|
|
|
|
if (BX_ES1370_THIS s.dac1_timer_index == BX_NULL_TIMER_HANDLE) {
|
|
BX_ES1370_THIS s.dac1_timer_index = bx_pc_system.register_timer
|
|
(BX_ES1370_THIS_PTR, es1370_timer_handler, 1, 1, 0, "es1370.dac1");
|
|
// DAC1 timer: inactive, continuous, frequency variable
|
|
}
|
|
if (BX_ES1370_THIS s.dac2_timer_index == BX_NULL_TIMER_HANDLE) {
|
|
BX_ES1370_THIS s.dac2_timer_index = bx_pc_system.register_timer
|
|
(BX_ES1370_THIS_PTR, es1370_timer_handler, 1, 1, 0, "es1370.dac2");
|
|
// DAC2 timer: inactive, continuous, frequency variable
|
|
}
|
|
|
|
BX_INFO(("ES1370 initialized"));
|
|
}
|
|
|
|
void bx_es1370_c::reset(unsigned type)
|
|
{
|
|
unsigned i;
|
|
|
|
static const struct reset_vals_t {
|
|
unsigned addr;
|
|
unsigned char val;
|
|
} reset_vals[] = {
|
|
{ 0x00, 0x74 }, { 0x01, 0x12 },
|
|
{ 0x02, 0x00 }, { 0x03, 0x50 },
|
|
{ 0x04, 0x05 }, { 0x05, 0x00 }, // command_io
|
|
{ 0x06, 0x00 }, { 0x07, 0x04 }, // status
|
|
{ 0x08, 0x00 }, // revision number
|
|
{ 0x09, 0x00 }, // interface
|
|
{ 0x0a, 0x01 }, // class_sub
|
|
{ 0x0b, 0x04 }, // class_base Multimedia Audio Device
|
|
{ 0x0e, 0x00 }, // header type generic
|
|
// address space 0x10 - 0x13
|
|
{ 0x10, 0x01 }, { 0x11, 0x00 },
|
|
{ 0x12, 0x00 }, { 0x13, 0x00 },
|
|
{ 0x2c, 0x42 }, { 0x2d, 0x49 }, // subsystem vendor
|
|
{ 0x2e, 0x4c }, { 0x2f, 0x4c }, // subsystem id
|
|
{ 0x3c, 0x00 }, // IRQ
|
|
{ 0x3d, BX_PCI_INTA }, // INT
|
|
{ 0x3e, 0x0c }, // min_gnt
|
|
{ 0x3f, 0x80 }, // max_lat
|
|
|
|
};
|
|
for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
|
|
BX_ES1370_THIS pci_conf[reset_vals[i].addr] = reset_vals[i].val;
|
|
}
|
|
|
|
BX_ES1370_THIS s.ctl = 1;
|
|
BX_ES1370_THIS s.status = 0x60;
|
|
BX_ES1370_THIS s.mempage = 0;
|
|
BX_ES1370_THIS s.codec = 0;
|
|
BX_ES1370_THIS s.sctl = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
BX_ES1370_THIS s.chan[i].scount = 0;
|
|
BX_ES1370_THIS s.chan[i].leftover = 0;
|
|
}
|
|
|
|
DEV_gameport_set_enabled(0);
|
|
|
|
// Deassert IRQ
|
|
set_irq_level(0);
|
|
}
|
|
|
|
void bx_es1370_c::register_state(void)
|
|
{
|
|
unsigned i;
|
|
char chname[6];
|
|
|
|
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "es1370", "ES1370 State");
|
|
for (i = 0; i < 3; i++) {
|
|
sprintf(chname, "chan%d", i);
|
|
bx_list_c *chan = new bx_list_c(list, chname, "");
|
|
BXRS_HEX_PARAM_FIELD(chan, shift, BX_ES1370_THIS s.chan[i].shift);
|
|
BXRS_HEX_PARAM_FIELD(chan, leftover, BX_ES1370_THIS s.chan[i].leftover);
|
|
BXRS_HEX_PARAM_FIELD(chan, scount, BX_ES1370_THIS s.chan[i].scount);
|
|
BXRS_HEX_PARAM_FIELD(chan, frame_addr, BX_ES1370_THIS s.chan[i].frame_addr);
|
|
BXRS_HEX_PARAM_FIELD(chan, frame_cnt, BX_ES1370_THIS s.chan[i].frame_cnt);
|
|
}
|
|
BXRS_HEX_PARAM_FIELD(list, ctl, BX_ES1370_THIS s.ctl);
|
|
BXRS_HEX_PARAM_FIELD(list, status, BX_ES1370_THIS s.status);
|
|
BXRS_HEX_PARAM_FIELD(list, mempage, BX_ES1370_THIS s.mempage);
|
|
BXRS_HEX_PARAM_FIELD(list, codec, BX_ES1370_THIS s.codec);
|
|
BXRS_HEX_PARAM_FIELD(list, sctl, BX_ES1370_THIS s.sctl);
|
|
|
|
register_pci_state(list);
|
|
}
|
|
|
|
void bx_es1370_c::after_restore_state(void)
|
|
{
|
|
if (DEV_pci_set_base_io(BX_ES1370_THIS_PTR, read_handler, write_handler,
|
|
&BX_ES1370_THIS pci_base_address[0],
|
|
&BX_ES1370_THIS pci_conf[0x10],
|
|
64, &es1370_iomask[0], "ES1370")) {
|
|
BX_INFO(("new base address: 0x%04x", BX_ES1370_THIS pci_base_address[0]));
|
|
}
|
|
BX_ES1370_THIS check_lower_irq(BX_ES1370_THIS s.sctl);
|
|
BX_ES1370_THIS s.adc_inputinit = 0;
|
|
BX_ES1370_THIS s.dac_nr_active = -1;
|
|
BX_ES1370_THIS update_voices(BX_ES1370_THIS s.ctl, BX_ES1370_THIS s.sctl, 1);
|
|
}
|
|
|
|
// static IO port read callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
Bit32u bx_es1370_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
|
|
{
|
|
#if !BX_USE_ES1370_SMF
|
|
bx_es1370_c *class_ptr = (bx_es1370_c *) this_ptr;
|
|
return class_ptr->read(address, io_len);
|
|
}
|
|
|
|
Bit32u bx_es1370_c::read(Bit32u address, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_ES1370_SMF
|
|
Bit32u val = 0x0, shift;
|
|
Bit16u offset;
|
|
unsigned i;
|
|
|
|
BX_DEBUG(("register read from address 0x%04x - ", address));
|
|
|
|
offset = address - BX_ES1370_THIS pci_base_address[0];
|
|
if (offset >= 0x30) {
|
|
offset |= (BX_ES1370_THIS s.mempage << 8);
|
|
}
|
|
shift = (offset & 3) << 3;
|
|
|
|
switch (offset & ~3) {
|
|
case ES1370_CTL:
|
|
val = BX_ES1370_THIS s.ctl >> shift;
|
|
break;
|
|
case ES1370_STATUS:
|
|
val = BX_ES1370_THIS s.status >> shift;
|
|
break;
|
|
case ES1370_UART_DATA:
|
|
case ES1370_UART_STATUS:
|
|
case ES1370_UART_TEST:
|
|
BX_ERROR(("reading from UART not supported yet"));
|
|
break;
|
|
case ES1370_MEMPAGE:
|
|
val = BX_ES1370_THIS s.mempage;
|
|
break;
|
|
case ES1370_CODEC:
|
|
val = BX_ES1370_THIS s.codec;
|
|
break;
|
|
case ES1370_SCTL:
|
|
val = BX_ES1370_THIS s.sctl >> shift;
|
|
break;
|
|
case ES1370_DAC1_SCOUNT:
|
|
case ES1370_DAC2_SCOUNT:
|
|
case ES1370_ADC_SCOUNT:
|
|
i = (offset - ES1370_DAC1_SCOUNT) / 4;
|
|
val = BX_ES1370_THIS s.chan[i].scount >> shift;
|
|
break;
|
|
case ES1370_DAC1_FRAMEADR:
|
|
val = BX_ES1370_THIS s.chan[0].frame_addr;
|
|
break;
|
|
case ES1370_DAC2_FRAMEADR:
|
|
val = BX_ES1370_THIS s.chan[1].frame_addr;
|
|
break;
|
|
case ES1370_ADC_FRAMEADR:
|
|
val = BX_ES1370_THIS s.chan[2].frame_addr;
|
|
break;
|
|
case ES1370_DAC1_FRAMECNT:
|
|
val = BX_ES1370_THIS s.chan[0].frame_cnt;
|
|
break;
|
|
case ES1370_DAC2_FRAMECNT:
|
|
val = BX_ES1370_THIS s.chan[1].frame_cnt;
|
|
break;
|
|
case ES1370_ADC_FRAMECNT:
|
|
val = BX_ES1370_THIS s.chan[2].frame_cnt;
|
|
break;
|
|
case ES1370_PHA_FRAMEADR:
|
|
BX_ERROR(("reading from phantom frame address"));
|
|
val = ~0U;
|
|
break;
|
|
case ES1370_PHA_FRAMECNT:
|
|
BX_ERROR(("reading from phantom frame count"));
|
|
val = ~0U;
|
|
break;
|
|
default:
|
|
val = ~0U; // keep compiler happy
|
|
BX_ERROR(("unsupported io read from offset=0x%04x!", offset));
|
|
break;
|
|
}
|
|
|
|
BX_DEBUG(("val = 0x%08x", val));
|
|
|
|
return(val);
|
|
}
|
|
|
|
// static IO port write callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
void bx_es1370_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#if !BX_USE_ES1370_SMF
|
|
bx_es1370_c *class_ptr = (bx_es1370_c *) this_ptr;
|
|
|
|
class_ptr->write(address, value, io_len);
|
|
}
|
|
|
|
void bx_es1370_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_ES1370_SMF
|
|
Bit16u offset;
|
|
Bit32u shift, mask;
|
|
unsigned i;
|
|
|
|
BX_DEBUG(("register write to address 0x%04x - value = 0x%08x", address, value));
|
|
|
|
offset = address - BX_ES1370_THIS pci_base_address[0];
|
|
if (offset >= 0x30) {
|
|
offset |= (BX_ES1370_THIS s.mempage << 8);
|
|
}
|
|
shift = (offset & 3) << 3;
|
|
|
|
switch (offset & ~3) {
|
|
case ES1370_CTL:
|
|
mask = (0xffffffff >> ((4 - io_len) << 3)) << shift;
|
|
value = (BX_ES1370_THIS s.ctl & ~mask) | ((value << shift) & mask);
|
|
if ((value ^ BX_ES1370_THIS s.ctl) & 0x04) {
|
|
DEV_gameport_set_enabled((value & 0x04) != 0);
|
|
}
|
|
BX_ES1370_THIS update_voices(value, BX_ES1370_THIS s.sctl, 0);
|
|
break;
|
|
case ES1370_UART_DATA:
|
|
case ES1370_UART_CTL:
|
|
case ES1370_UART_TEST:
|
|
BX_ERROR(("writing to UART not supported yet"));
|
|
break;
|
|
case ES1370_MEMPAGE:
|
|
BX_ES1370_THIS s.mempage = value & 0x0f;
|
|
break;
|
|
case ES1370_CODEC:
|
|
BX_ES1370_THIS s.codec = value & 0xffff;
|
|
BX_DEBUG(("writing to CODEC register 0x%02x, value = 0x%02x", (value >> 8) & 0xff, value & 0xff));
|
|
break;
|
|
case ES1370_SCTL:
|
|
mask = (0xffffffff >> ((4 - io_len) << 3)) << shift;
|
|
value = (BX_ES1370_THIS s.sctl & ~mask) | ((value << shift) & mask);
|
|
BX_ES1370_THIS check_lower_irq(value);
|
|
BX_ES1370_THIS update_voices(BX_ES1370_THIS s.ctl, value, 0);
|
|
break;
|
|
case ES1370_DAC1_SCOUNT:
|
|
case ES1370_DAC2_SCOUNT:
|
|
case ES1370_ADC_SCOUNT:
|
|
i = (offset - ES1370_DAC1_SCOUNT) / 4;
|
|
value &= 0xffff;
|
|
BX_ES1370_THIS s.chan[i].scount = value | (value << 16);
|
|
break;
|
|
case ES1370_DAC1_FRAMEADR:
|
|
BX_ES1370_THIS s.chan[0].frame_addr = value;
|
|
break;
|
|
case ES1370_DAC2_FRAMEADR:
|
|
BX_ES1370_THIS s.chan[1].frame_addr = value;
|
|
break;
|
|
case ES1370_ADC_FRAMEADR:
|
|
BX_ES1370_THIS s.chan[2].frame_addr = value;
|
|
break;
|
|
case ES1370_DAC1_FRAMECNT:
|
|
BX_ES1370_THIS s.chan[0].frame_cnt = value;
|
|
break;
|
|
case ES1370_DAC2_FRAMECNT:
|
|
BX_ES1370_THIS s.chan[1].frame_cnt = value;
|
|
break;
|
|
case ES1370_ADC_FRAMECNT:
|
|
BX_ES1370_THIS s.chan[2].frame_cnt = value;
|
|
break;
|
|
case ES1370_PHA_FRAMEADR:
|
|
BX_ERROR(("writing to phantom frame address"));
|
|
break;
|
|
case ES1370_PHA_FRAMECNT:
|
|
BX_ERROR(("writing to phantom frame count"));
|
|
break;
|
|
default:
|
|
BX_ERROR(("unsupported io write to offset=0x%04x!", offset));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void bx_es1370_c::es1370_timer_handler(void *this_ptr)
|
|
{
|
|
bx_es1370_c *class_ptr = (bx_es1370_c *) this_ptr;
|
|
class_ptr->es1370_timer();
|
|
}
|
|
|
|
void bx_es1370_c::es1370_timer(void)
|
|
{
|
|
int timer_id;
|
|
unsigned i;
|
|
|
|
timer_id = bx_pc_system.triggeredTimerID();
|
|
i = (timer_id == BX_ES1370_THIS s.dac1_timer_index) ? 0 : 1;
|
|
run_channel(i, timer_id, BX_ES1370_THIS s.dac_packet_size[i]);
|
|
}
|
|
|
|
void bx_es1370_c::run_channel(unsigned chan, int timer_id, Bit32u buflen)
|
|
{
|
|
Bit32u new_status = BX_ES1370_THIS s.status;
|
|
Bit32u addr, sc, csc_bytes, cnt, size, left, transfered, temp;
|
|
Bit8u tmpbuf[BX_SOUNDLOW_WAVEPACKETSIZE];
|
|
bx_bool irq = 0;
|
|
|
|
chan_t *d = &BX_ES1370_THIS s.chan[chan];
|
|
|
|
if (!(BX_ES1370_THIS s.ctl & ctl_ch_en[chan]) || (BX_ES1370_THIS s.sctl & sctl_ch_pause[chan])) {
|
|
if (chan == ADC_CHANNEL) {
|
|
BX_ES1370_THIS soundmod->stopwaverecord();
|
|
} else {
|
|
bx_pc_system.deactivate_timer(timer_id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
addr = d->frame_addr;
|
|
sc = d->scount & 0xffff;
|
|
csc_bytes = ((d->scount >> 16) + 1) << d->shift;
|
|
cnt = d->frame_cnt >> 16;
|
|
size = d->frame_cnt & 0xffff;
|
|
left = ((size - cnt + 1) << 2) + d->leftover;
|
|
transfered = 0;
|
|
temp = BX_MIN(buflen, BX_MIN(left, csc_bytes));
|
|
addr += (cnt << 2) + d->leftover;
|
|
|
|
if (chan == ADC_CHANNEL) {
|
|
BX_ES1370_THIS soundmod->getwavepacket(temp, tmpbuf);
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(addr, temp, tmpbuf);
|
|
transfered = temp;
|
|
} else {
|
|
DEV_MEM_READ_PHYSICAL_DMA(addr, temp, tmpbuf);
|
|
if (((int)chan == BX_ES1370_THIS s.dac_nr_active) && BX_ES1370_THIS s.dac_outputinit) {
|
|
BX_ES1370_THIS soundmod->sendwavepacket(temp, tmpbuf);
|
|
}
|
|
transfered = temp;
|
|
}
|
|
|
|
if (csc_bytes == transfered) {
|
|
irq = 1;
|
|
d->scount = sc | (sc << 16);
|
|
BX_DEBUG(("%s: all samples played/recorded - signalling IRQ (if enabled)", chan_name[chan]));
|
|
} else {
|
|
irq = 0;
|
|
d->scount = sc | (((csc_bytes - transfered - 1) >> d->shift) << 16);
|
|
}
|
|
|
|
cnt += (transfered + d->leftover) >> 2;
|
|
|
|
if (BX_ES1370_THIS s.sctl & sctl_loop_sel[chan]) {
|
|
BX_ERROR(("%s: non looping mode not supported", chan_name[chan]));
|
|
} else {
|
|
d->frame_cnt = size;
|
|
if (cnt <= d->frame_cnt) {
|
|
d->frame_cnt |= cnt << 16;
|
|
}
|
|
}
|
|
|
|
d->leftover = (transfered + d->leftover) & 3;
|
|
|
|
if (irq) {
|
|
if (BX_ES1370_THIS s.sctl & (1 << (8 + chan))) {
|
|
new_status |= (4 >> chan);
|
|
}
|
|
}
|
|
if (new_status != BX_ES1370_THIS s.status) {
|
|
update_status(new_status);
|
|
}
|
|
}
|
|
|
|
Bit32u bx_es1370_c::es1370_adc_handler(void *this_ptr, Bit32u buflen)
|
|
{
|
|
bx_es1370_c *class_ptr = (bx_es1370_c *) this_ptr;
|
|
class_ptr->run_channel(ADC_CHANNEL, 0, buflen);
|
|
return 0;
|
|
}
|
|
|
|
void bx_es1370_c::set_irq_level(bx_bool level)
|
|
{
|
|
DEV_pci_set_irq(BX_ES1370_THIS s.devfunc, BX_ES1370_THIS pci_conf[0x3d], level);
|
|
}
|
|
|
|
void bx_es1370_c::update_status(Bit32u new_status)
|
|
{
|
|
Bit32u level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
|
|
|
|
if (level) {
|
|
BX_ES1370_THIS s.status = new_status | STAT_INTR;
|
|
} else {
|
|
BX_ES1370_THIS s.status = new_status & ~STAT_INTR;
|
|
}
|
|
set_irq_level(level != 0);
|
|
}
|
|
|
|
void bx_es1370_c::check_lower_irq(Bit32u sctl)
|
|
{
|
|
Bit32u new_status = BX_ES1370_THIS s.status;
|
|
|
|
if (!(sctl & SCTRL_P1INTEN) && (BX_ES1370_THIS s.sctl & SCTRL_P1INTEN)) {
|
|
new_status &= ~STAT_DAC1;
|
|
}
|
|
if (!(sctl & SCTRL_P2INTEN) && (BX_ES1370_THIS s.sctl & SCTRL_P2INTEN)) {
|
|
new_status &= ~STAT_DAC2;
|
|
}
|
|
if (!(sctl & SCTRL_R1INTEN) && (BX_ES1370_THIS s.sctl & SCTRL_R1INTEN)) {
|
|
new_status &= ~STAT_ADC;
|
|
}
|
|
if (new_status != BX_ES1370_THIS s.status) {
|
|
update_status(new_status);
|
|
}
|
|
}
|
|
|
|
void bx_es1370_c::update_voices(Bit32u ctl, Bit32u sctl, bx_bool force)
|
|
{
|
|
unsigned i;
|
|
Bit32u old_freq, new_freq, old_fmt, new_fmt;
|
|
int ret, timer_id;
|
|
Bit64u timer_val;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
chan_t *d = &BX_ES1370_THIS s.chan[i];
|
|
|
|
old_fmt = (BX_ES1370_THIS s.sctl >> (i << 1)) & 3;
|
|
new_fmt = (sctl >> (i << 1)) & 3;
|
|
|
|
if (i == DAC1_CHANNEL) {
|
|
old_freq = dac1_freq[(BX_ES1370_THIS s.ctl >> 12) & 3];
|
|
new_freq = dac1_freq[(ctl >> 12) & 3];
|
|
} else {
|
|
old_freq = 1411200 / (((BX_ES1370_THIS s.ctl >> 16) & 0x1fff) + 2);
|
|
new_freq = 1411200 / (((ctl >> 16) & 0x1fff) + 2);
|
|
}
|
|
|
|
if ((old_fmt != new_fmt) || (old_freq != new_freq) || force) {
|
|
d->shift = (new_fmt & 1) + (new_fmt >> 1);
|
|
if (new_freq) {
|
|
if (i == ADC_CHANNEL) {
|
|
if (!BX_ES1370_THIS s.adc_inputinit) {
|
|
ret = BX_ES1370_THIS soundmod->openwaveinput(SIM->get_param_string(BXPN_ES1370_WAVEDEV)->getptr(),
|
|
es1370_adc_handler);
|
|
if (ret != BX_SOUNDLOW_OK) {
|
|
BX_ERROR(("could not open wave input device"));
|
|
} else {
|
|
BX_ES1370_THIS s.adc_inputinit = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (((ctl ^ BX_ES1370_THIS s.ctl) & ctl_ch_en[i]) ||
|
|
((sctl ^ BX_ES1370_THIS s.sctl) & sctl_ch_pause[i]) || force) {
|
|
bx_bool on = ((ctl & ctl_ch_en[i]) && !(sctl & sctl_ch_pause[i]));
|
|
|
|
if (i == DAC1_CHANNEL) {
|
|
timer_id = BX_ES1370_THIS s.dac1_timer_index;
|
|
} else {
|
|
timer_id = BX_ES1370_THIS s.dac2_timer_index;
|
|
}
|
|
if (on) {
|
|
BX_INFO(("%s: freq = %d, nchannels %d, fmt %d, shift %d",
|
|
chan_name[i], new_freq, 1 << (new_fmt & 1), (new_fmt & 2) ? 16 : 8, d->shift));
|
|
if (i == ADC_CHANNEL) {
|
|
if (BX_ES1370_THIS s.adc_inputinit) {
|
|
ret = BX_ES1370_THIS soundmod->startwaverecord(new_freq, (new_fmt >> 1) ? 16 : 8, (new_fmt & 1), (new_fmt >> 1));
|
|
if (ret != BX_SOUNDLOW_OK) {
|
|
BX_ES1370_THIS soundmod->closewaveinput();
|
|
BX_ES1370_THIS s.adc_inputinit = 0;
|
|
BX_ERROR(("could not start wave record"));
|
|
}
|
|
}
|
|
} else {
|
|
if ((BX_ES1370_THIS s.dac_nr_active == -1) && BX_ES1370_THIS s.dac_outputinit) {
|
|
ret = BX_ES1370_THIS soundmod->startwaveplayback(new_freq, (new_fmt >> 1) ? 16 : 8, (new_fmt & 1), (new_fmt >> 1));
|
|
if (ret != BX_SOUNDLOW_OK) {
|
|
BX_ES1370_THIS soundmod->closewaveoutput();
|
|
BX_ES1370_THIS s.dac_outputinit = 0;
|
|
BX_ERROR(("could not start wave playback"));
|
|
} else {
|
|
BX_ES1370_THIS s.dac_nr_active = i;
|
|
}
|
|
}
|
|
BX_ES1370_THIS s.dac_packet_size[i] = (new_freq / 10) << d->shift; // 0.1 sec
|
|
if (BX_ES1370_THIS s.dac_packet_size[i] > BX_SOUNDLOW_WAVEPACKETSIZE) {
|
|
BX_ES1370_THIS s.dac_packet_size[i] = BX_SOUNDLOW_WAVEPACKETSIZE;
|
|
}
|
|
timer_val = (Bit64u)BX_ES1370_THIS s.dac_packet_size[i] * 1000000 / (new_freq << d->shift);
|
|
bx_pc_system.activate_timer(timer_id, (Bit32u)timer_val, 1);
|
|
}
|
|
} else {
|
|
if (i == ADC_CHANNEL) {
|
|
if (BX_ES1370_THIS s.adc_inputinit) {
|
|
BX_ES1370_THIS soundmod->stopwaverecord();
|
|
}
|
|
} else {
|
|
if (((int)i == BX_ES1370_THIS s.dac_nr_active) && BX_ES1370_THIS s.dac_outputinit) {
|
|
BX_ES1370_THIS soundmod->stopwaveplayback();
|
|
BX_ES1370_THIS s.dac_nr_active = -1;
|
|
}
|
|
bx_pc_system.deactivate_timer(timer_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BX_ES1370_THIS s.ctl = ctl;
|
|
BX_ES1370_THIS s.sctl = sctl;
|
|
}
|
|
|
|
// pci configuration space read callback handler
|
|
Bit32u bx_es1370_c::pci_read_handler(Bit8u address, unsigned io_len)
|
|
{
|
|
Bit32u value = 0;
|
|
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
value |= (BX_ES1370_THIS pci_conf[address+i] << (i*8));
|
|
}
|
|
|
|
if (io_len == 1)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%02x", address, value));
|
|
else if (io_len == 2)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%04x", address, value));
|
|
else if (io_len == 4)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%08x", address, value));
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
// pci configuration space write callback handler
|
|
void bx_es1370_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
|
|
{
|
|
Bit8u value8, oldval;
|
|
bx_bool baseaddr_change = 0;
|
|
|
|
if ((address >= 0x14) && (address < 0x34))
|
|
return;
|
|
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
value8 = (value >> (i*8)) & 0xFF;
|
|
oldval = BX_ES1370_THIS pci_conf[address+i];
|
|
switch (address+i) {
|
|
case 0x04:
|
|
value8 &= 0x05;
|
|
BX_ES1370_THIS pci_conf[address+i] = value8;
|
|
break;
|
|
case 0x05:
|
|
value8 &= 0x01;
|
|
BX_ES1370_THIS pci_conf[address+i] = value8;
|
|
break;
|
|
case 0x3d: //
|
|
case 0x06: // disallowing write to status lo-byte (is that expected?)
|
|
break;
|
|
case 0x3c:
|
|
if (value8 != oldval) {
|
|
BX_INFO(("new irq line = %d", value8));
|
|
BX_ES1370_THIS pci_conf[address+i] = value8;
|
|
}
|
|
break;
|
|
case 0x10:
|
|
value8 = (value8 & 0xfc) | 0x01;
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
baseaddr_change |= (value8 != oldval);
|
|
default:
|
|
BX_ES1370_THIS pci_conf[address+i] = value8;
|
|
}
|
|
}
|
|
if (baseaddr_change) {
|
|
if (DEV_pci_set_base_io(BX_ES1370_THIS_PTR, read_handler, write_handler,
|
|
&BX_ES1370_THIS pci_base_address[0],
|
|
&BX_ES1370_THIS pci_conf[0x10],
|
|
64, &es1370_iomask[0], "ES1370")) {
|
|
BX_INFO(("new base address: 0x%04x", BX_ES1370_THIS pci_base_address[0]));
|
|
}
|
|
}
|
|
|
|
if (io_len == 1)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%02x", address, value));
|
|
else if (io_len == 2)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%04x", address, value));
|
|
else if (io_len == 4)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%08x", address, value));
|
|
}
|
|
|
|
#endif // BX_SUPPORT_PCI && BX_SUPPORT_ES1370
|